home *** CD-ROM | disk | FTP | other *** search
/ Dream 48 / Amiga_Dream_48.iso / Atari / c / cpp.zoo / src / macro.c < prev    next >
C/C++ Source or Header  |  1993-06-03  |  8KB  |  351 lines

  1.  
  2. #include <stddef.h>
  3. #include <string.h>
  4. #include <time.h>
  5. #include "global.h"
  6.  
  7. char *magic_words[] =
  8. {
  9.   "__STDC__",            /* these two are particuarly magic -- */
  10.   "defined",            /* leave them where they are */
  11.   "__DATE__",
  12.   "__TIME__",
  13.   "__FILE__",
  14.   "__LINE__",
  15.   "__INCLUDE_LEVEL__",
  16. };
  17. int N_MWORDS = nelems(magic_words);
  18.  
  19. /*
  20.    magic_token() -- create a Token to be returned by expand_magic() below
  21. */
  22. static TokenP magic_token(type, text, l, T0)
  23.   int type;
  24.   char *text;
  25.   long l;
  26.   TokenP T0;
  27. {
  28.   TokenP T = alloc_token();
  29.   int len;
  30.   register char *s, *t;
  31.  
  32.   T->pre_ws = strdup(T0->pre_ws);
  33.   if (type != STR_CON)
  34.     T->txt = strdup(T0->txt);
  35.   else {
  36.     len = strlen(T0->txt);
  37.     T->txt = mallok(len + 3);
  38.     strcpy(T->txt + 1, T0->txt);
  39.     T->txt[0] = T->txt[len + 1] = '"';
  40.     T->txt[len + 2] = '\0';
  41.   }
  42.   T->type = type;
  43.   if (type == NUMBER)
  44.     T->val = l;
  45.   if (type == ID)
  46.     T->flags |= BLUEPAINT;
  47.   return T;
  48. }
  49.  
  50. /* expand_magic() -- expand a magic preprocessor constant */
  51. static TokenP expand_magic(T)
  52.   TokenP T;
  53. {
  54.   static char buf[20];
  55.   int i;
  56.   TokenP T1, tL;
  57.   Token tLH;
  58.  
  59.   for (i = 0; i < N_MWORDS; i++)
  60.     if (streq(T->txt, magic_words[i]))
  61.       break;
  62.   switch (i) {
  63.   case 0:            /* __STDC__ */
  64.     return magic_token(NUMBER, "1", 1L, T);
  65.   case 1:            /* defined */
  66.     tL = &tLH;
  67.     tL->next = NULL;
  68.     buf[0] = '0';
  69.     buf[1] = '\0';
  70.     T1 = token();
  71.     if (T1->type == STOP) {
  72.       push_tlist(T1);
  73.       error("defined() has no parameter");
  74.       goto nope;
  75.     }
  76.     tL = tL->next = T1;
  77.     if (T1->type == ID) {
  78.       if (lookup(T1->txt, T1->hashval))
  79.     buf[0] = '1';
  80.     } else if (T1->type == LPAREN) {
  81.       T1 = token();
  82.       tL = tL->next = T1;
  83.       if (T1->type == STOP) {
  84.     error("unterminated defined() macro");
  85.     goto nope;
  86.       } else if (T1->type == ID) {
  87.     if (lookup(T1->txt, T1->hashval))
  88.       buf[0] = '1';
  89.     T1 = token();
  90.     tL = tL->next = T1;
  91.     if (T1->type != RPAREN) {
  92.       error("missing `)' in defined()");
  93.       goto nope;
  94.     }
  95.       } else {
  96.     error("parameter \"%s\" to defined() is not an identifier", T1->txt);
  97.     goto nope;
  98.       }
  99.     } else {
  100.       error("parameter \"%s\" to defined() is not an identifier", T1->txt);
  101.       goto nope;
  102.     }
  103.     free_tlist(tLH.next);
  104.     return magic_token(NUMBER, buf, (long)(*buf == '1'), T);
  105.   nope:
  106.     push_tlist(tLH.next);
  107.     return magic_token(NUMBER, "0", 0L, T);
  108.   case 2:            /* __DATE__ */
  109.     return magic_token(STR_CON, date_string, 0L, T);
  110.   case 3:            /* __TIME__ */
  111.     return magic_token(STR_CON, time_string, 0L, T);
  112.   case 4:            /* __FILE__ */
  113.     return magic_token(STR_CON, cur_file, 0L, T);
  114.   case 5:            /* __LINE__ */
  115.     sprintf(buf, "%lu", this_line);
  116.     return magic_token(NUMBER, buf, this_line, T);
  117.   case 6:            /* __INCLUDE_LEVEL__ */
  118.     sprintf(buf, "%lu", include_level);
  119.     return magic_token(NUMBER, buf, include_level, T);
  120.   default:
  121.     bugchk("unknown magic word %s", T->txt);
  122.   }
  123. }
  124.  
  125. /*
  126.    get_args() -- read in the actual arguments of a macro expansion. |mname|
  127.    is the name of the macro being expanded; |nargs| is the number of
  128.    arguments to read.
  129. */
  130. static TokenP *get_args(mname, nargs)
  131.   char *mname;
  132.   int nargs;
  133. {
  134.   TokenP *args;
  135.   int cur_arg, par_level = 0;
  136.   TokenP T, L;
  137.   Token head;
  138.  
  139.   args = (TokenP *) mallok((nargs ? nargs : 1) * sizeof (TokenP));
  140.   for (cur_arg = 0; cur_arg < nargs || cur_arg == 0; cur_arg++)
  141.     args[cur_arg] = NULL;
  142.   cur_arg = 0;
  143.   L = &head;
  144.   L->next = NULL;
  145.   change_mode(SLURP, 0);
  146.   for (;;) {
  147.     T = token();
  148.     switch (T->type) {
  149.     case STOP:
  150.       push_tlist(T);
  151.       error("unterminated macro \"%s\"", mname);
  152.       goto out_loop;
  153.     case EOL:
  154.       free_token(T);
  155.       continue;
  156.     case LPAREN:
  157.       par_level++;
  158.       break;
  159.     case RPAREN:
  160.       if (--par_level < 0) {
  161.     free_token(T);
  162.     goto out_loop;
  163.       }
  164.       break;
  165.     case COMMA:
  166.       if (par_level == 0) {
  167.     free_token(T);
  168.     args[cur_arg++] = head.next;
  169.     L = &head;
  170.     L->next = NULL;
  171.     continue;
  172.       }
  173.       break;
  174.     }
  175.     if (cur_arg < nargs)
  176.       L = L->next = T;
  177.   }
  178. out_loop:
  179.   change_mode(0, SLURP);
  180.   if (head.next || cur_arg > 0)
  181.     args[cur_arg++] = head.next;
  182.   if (cur_arg != nargs)
  183.     error("macro \"%s\" declared with %d arg%s, used with %d",
  184.       mname, nargs, (nargs == 1 ? "" : "s"), cur_arg);
  185.   return args;
  186. }
  187.  
  188. /*
  189.    stringize() -- create a string literal representing the token list |T|
  190. */
  191. static TokenP stringize(T)
  192.   TokenP T;
  193. {
  194.   char *buf;
  195.   register char *s, *t;
  196.   size_t buflen, i;
  197.   ptrdiff_t dp;
  198.   TokenP tt, t0;
  199.  
  200.   s = buf = mallok(buflen = 80);
  201.   *s++ = '"';
  202.   for (tt = T; tt; tt = tt->next) {
  203.     i = 2 * strlen(tt->txt) + 2;
  204.     if (tt != T) ;
  205.     i += strlen(tt->pre_ws);
  206.     s = grow(&buf, &buflen, s, i);
  207.     if (tt != T)
  208.       for (t = tt->pre_ws; *t; t++)
  209.     *s++ = *t;
  210.     for (t = tt->txt; *t; t++) {
  211.       if (*t == '\\' || *t == '"')
  212.     *s++ = '\\';
  213.       *s++ = *t;
  214.     }
  215.   }
  216.   *s++ = '"';
  217.   *s = '\0';
  218.   t0 = alloc_token();
  219.   t0->type = STR_CON;
  220.   t0->txt = buf;
  221.   t0->pre_ws = strdup(T->pre_ws);
  222.   return t0;
  223. }
  224.  
  225. /*
  226.    expand() -- expand the macro definition |M| of the Token |T|. Returns a
  227.    list of Token's representing the expanded text.
  228.  
  229. WARNING:  _Always_ check the BLUEPAINT flag before calling expand(), or
  230.    you'll end up with either the wrong answer or an infinite loop, or both.
  231.    At the moment this is not a concern, since only exp_token() calls
  232.    expand().
  233. */
  234. void expand(T, M)
  235.   TokenP T;
  236.   Macro *M;
  237. {
  238.   Token head1, head2, mhead;
  239.   TokenP t1, pt1, t2 = &head2, pt2 = &head1, *args, *exp_args, *str_args;
  240.   int n;
  241.  
  242.   head1.flags = head2.flags = mhead.flags = 0;
  243.   head1.next = &head2;
  244.   head2.next = NULL;
  245.   mhead.next = M->m_text;
  246.   if (M->flags & MARKED) {
  247.     T->flags |= BLUEPAINT;
  248.     push_tlist(T);
  249.     return;
  250.   }
  251.   if (M->flags & MAGIC) {
  252.     push_tlist(expand_magic(T));
  253.     return;
  254.   }
  255.   if (M->flags & HASARGS) {
  256.     t1 = token();
  257.     if (t1->type != LPAREN) {
  258.       push_tlist(t1);
  259.       T->flags |= BLUEPAINT;
  260.       push_tlist(T);
  261.       return;
  262.     }
  263.     args = get_args(T->txt, M->nargs);
  264.     exp_args = mallok(M->nargs * sizeof (TokenP));
  265.     str_args = mallok(M->nargs * sizeof (TokenP));
  266.     for (n = 0; n < M->nargs; n++)
  267.       if (args[n]) {
  268.     exp_args[n] = expand_tlist(copy_tlist(args[n]));
  269.     str_args[n] = stringize(args[n]);
  270.       } else
  271.     exp_args[n] = str_args[n] = NULL;
  272.   } else
  273.     args = exp_args = str_args = NULL;
  274.   M->flags |= MARKED;
  275.   t2->next = NULL;
  276.   for (t1 = M->m_text, pt1 = &mhead; t1; t1 = t1->next, pt1 = pt1->next) {
  277.     TokenP t3;
  278.  
  279.     if (t1->type == MACRO_ARG) {
  280.       t3 = copy_tlist(
  281.                (t1->flags & STRINGIZE_ME ? str_args
  282.         : t1->flags & CONCAT_NEXT || pt1->flags & CONCAT_NEXT ? args
  283.             : exp_args
  284.                )[t1->val]
  285.       );
  286.     } else {
  287.       t3 = copy_token(t1);
  288.       t3->flags &= ~(STRINGIZE_ME | CONCAT_NEXT | TRAIL_SPC);
  289.     }
  290.     if (pt1->flags & CONCAT_NEXT) {
  291.       TokenP t4;
  292.  
  293.       t4 = merge_tokens(t2, t3);
  294.       pt2->next = t4;
  295.       t4->next = t3->next;
  296.       t2->next = t3->next = NULL;
  297.       free_token(t2);
  298.       free_token(t3);
  299.       t2 = t4;
  300.     } else
  301.       t2->next = t3;
  302.     while (t2->next) {
  303.       t2 = t2->next;
  304.       pt2 = pt2->next;
  305.     }
  306.   }
  307.  
  308.   /*
  309.      prepend the leading whitespace from T to the first token of the expanded
  310.      text, if any
  311.   */
  312.   if (head2.next) {
  313.     if (head2.next->pre_ws)
  314.       free(head2.next->pre_ws);
  315.     head2.next->pre_ws = T->pre_ws;
  316.   }
  317.   /* add a trailing space */
  318.   t2->flags |= TRAIL_SPC;
  319.   push_tlist(mk_unmarker(T));
  320.   push_tlist(head2.next);
  321. }
  322.  
  323. /*
  324.    expand_tlist() -- call expand() on each of a list of Token's, returning
  325.    the resulting list of Token's
  326. */
  327. TokenP expand_tlist(TL)
  328.   TokenP TL;
  329. {
  330.   Token head;
  331.   TokenP t1, t2 = &head;
  332.   Macro *M;
  333.  
  334.   head.next = NULL;
  335.   push_tlist(mk_stopper());
  336.   push_tlist(TL);
  337.   for (t1 = token(); t1->type != STOP; t1 = token()) {
  338.     if (t1->type == ID && !(t1->flags & BLUEPAINT) &&
  339.     (M = lookup(t1->txt, t1->hashval))) {
  340.       expand(t1, M, NULL);
  341.       free_token(t1);
  342.     } else {
  343.       t2->next = t1;
  344.       t2 = t2->next;
  345.     }
  346.   }
  347.   /* t1 should now hold a STOP token */
  348.   free_token(t1);
  349.   return head.next;
  350. }
  351.