home *** CD-ROM | disk | FTP | other *** search
/ The Atari Compendium / The Atari Compendium (Toad Computers) (1994).iso / files / prgtools / programm.ing / cpp114.zoo / src / macro.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-01-20  |  11.2 KB  |  469 lines

  1.  
  2. /*---------------------------------------------------------------------*\
  3. |                                    |
  4. | CPP -- a stand-alone C preprocessor                    |
  5. | Copyright (c) 1993-95 Hacker Ltd.        Author: Scott Bigham    |
  6. |                                    |
  7. | Permission is granted to anyone to use this software for any purpose    |
  8. | on any computer system, and to redistribute it freely, with the    |
  9. | following restrictions:                        |
  10. | - No charge may be made other than reasonable charges for repro-    |
  11. |     duction.                                |
  12. | - Modified versions must be clearly marked as such.            |
  13. | - The author is not responsible for any harmful consequences of    |
  14. |     using this software, even if they result from defects therein.    |
  15. |                                    |
  16. | macro.c -- do macro expansion                        |
  17. \*---------------------------------------------------------------------*/
  18.  
  19. #include <stddef.h>
  20. #include <string.h>
  21. #include <time.h>
  22. #include <stdlib.h>
  23. #include "global.h"
  24. #include "alloc.h"
  25.  
  26. static char SC_not_id[] = "parameter \"%s\" to defined() is not an identifier";
  27.  
  28. char *magic_words[] =
  29. {
  30.   "__STDC__",            /* the first three are particuarly magic -- */
  31.   "defined",            /* leave them where they are */
  32.   "__FLUFF__",
  33.   "__DATE__",
  34.   "__TIME__",
  35.   "__FILE__",
  36.   "__LINE__",
  37. };
  38. int N_MWORDS = nelems(magic_words);
  39. int N_M2WORDS = 3;        /* number of "special" magic words */
  40.  
  41. /* mk_Macro() -- allocate and initialize a macro structure */
  42. Macro *mk_Macro()
  43. {
  44.   register Macro *M = alloc_Macro();
  45.  
  46.   M->flags = M->nargs = 0;
  47.   M->m_text = M->argnames = NULL;
  48.   M->next = NULL;
  49.   return M;
  50. }
  51.  
  52. /* free_Macro() -- clean up and deallocate a macro structure */
  53. void free_Macro(M)
  54.   register Macro *M;
  55. {
  56.   if (!M)
  57.     return;
  58.   if (M->m_text)
  59.     free_tlist(M->m_text);
  60.   if (M->argnames)
  61.     free_tlist(M->argnames);
  62.   dealloc_Macro(M);
  63. }
  64.  
  65. /*
  66.    magic_token() -- create a Token to be returned by expand_magic() below
  67. */
  68. static TokenP magic_token(type, text, l, T0)
  69.   int type;
  70.   const char *text;
  71.   long l;
  72.   TokenP T0;
  73. {
  74.   register TokenP T = mk_Token();
  75.  
  76.   set_ws(T, token_ws(T0));
  77.   if (type != STR_CON)
  78.     set_txt(T, text);
  79.   else {
  80.     char *u = mallok(strlen(text) + 3);
  81.  
  82.     (void)sprintf(u, "\"%s\"", text);
  83.     set_txt(T, u);
  84.     free(u);
  85.   }
  86.   T->type = type;
  87.   if (type == NUMBER)
  88.     T->val = l;
  89.   if (type == ID)
  90.     T->flags |= BLUEPAINT;
  91.   return T;
  92. }
  93.  
  94. /* expand_magic() -- expand a magic preprocessor constant */
  95. static TokenP expand_magic(T)
  96.   register TokenP T;
  97. {
  98.   static char buf[20];
  99.   int i;
  100.   register TokenP T1, tL;
  101.   Token tLH;
  102.  
  103.   for (i = 0; i < N_MWORDS; i++)
  104.     if (streq(token_txt(T), magic_words[i]))
  105.       break;
  106.   switch (i) {
  107.   case 0:            /* __STDC__ */
  108.     return magic_token(NUMBER, "1", 1L, T);
  109.   case 1:            /* defined */
  110.     tL = &tLH;
  111.     tL->next = NULL;
  112.     buf[0] = '0';
  113.     buf[1] = '\0';
  114.     T1 = token();
  115.     if (T1->type == STOP) {
  116.       push_tlist(T1);
  117.       error("defined() has no parameter");
  118.       goto nope;
  119.     }
  120.     if (T1->type == ID) {
  121.       if (lookup(token_txt(T1), T1->hashval))
  122.     buf[0] = '1';
  123.       free_token(T1);
  124.     } else if (T1->type == LPAREN) {
  125.       free_token(T1);
  126.       T1 = token();
  127.       if (T1->type == STOP) {
  128.     push_tlist(T1);
  129.     error("unterminated defined() macro");
  130.     goto nope;
  131.       } else if (T1->type == ID) {
  132.     if (lookup(token_txt(T1), T1->hashval))
  133.       buf[0] = '1';
  134.     free_token(T1);
  135.     T1 = token();
  136.     if (T1->type != RPAREN) {
  137.       push_tlist(T1);
  138.       error("missing `)' in defined()");
  139.       goto nope;
  140.     }
  141.     free_token(T1);
  142.       } else {
  143.     error(SC_not_id, token_txt(T1));
  144.     free_token(T1);
  145.     goto nope;
  146.       }
  147.     } else {
  148.       error(SC_not_id, token_txt(T1));
  149.       free_token(T1);
  150.       goto nope;
  151.     }
  152.     return magic_token(NUMBER, buf, (long)(*buf == '1'), T);
  153.   nope:
  154.     return magic_token(NUMBER, "0", 0L, T);
  155.   case 2:            /* __FLUFF__ */
  156.     return magic_token(NUMBER, "1", 1L, T);
  157.   case 3:            /* __DATE__ */
  158.     return magic_token(STR_CON, date_string, 0L, T);
  159.   case 4:            /* __TIME__ */
  160.     return magic_token(STR_CON, time_string, 0L, T);
  161.   case 5:            /* __FILE__ */
  162.     return magic_token(STR_CON, cur_file, 0L, T);
  163.   case 6:            /* __LINE__ */
  164.     sprintf(buf, "%lu", this_line);
  165.     return magic_token(NUMBER, buf, this_line, T);
  166.   default:
  167.     bugchk("unknown magic word %s", token_txt(T));
  168.     return 0;        /* just to shut up the compiler... */
  169.   }
  170. }
  171.  
  172. /*
  173.    get_args() -- read in the actual arguments of a macro expansion. |mname|
  174.    is the name of the macro being expanded; |nargs| is the number of
  175.    arguments to read.
  176. */
  177. static TokenP *get_args(mname, nargs)
  178.   char *mname;
  179.   int nargs;
  180. {
  181.   int cur_arg, par_level = 0;
  182.   register TokenP T, L;
  183.   register TokenP *args;
  184.   Token head;
  185.  
  186.   args = (TokenP *) mallok((nargs ? nargs : 1) * sizeof (TokenP));
  187.   for (cur_arg = 0; cur_arg < nargs || cur_arg == 0; cur_arg++)
  188.     args[cur_arg] = NULL;
  189.   cur_arg = 0;
  190.   L = &head;
  191.   L->next = NULL;
  192.   change_mode(SLURP, 0);
  193.   for (;;) {
  194.     T = token();
  195.     switch (T->type) {
  196.     case STOP:
  197.       push_tlist(T);
  198.       error("unterminated macro \"%s\"", mname);
  199.       goto out_loop;
  200.     case EOL:
  201.       free_token(T);
  202.       continue;
  203.     case LPAREN:
  204.       par_level++;
  205.       break;
  206.     case RPAREN:
  207.       if (--par_level < 0) {
  208.     free_token(T);
  209.     goto out_loop;
  210.       }
  211.       break;
  212.     case COMMA:
  213.       if (par_level == 0) {
  214.     free_token(T);
  215.     args[cur_arg++] = head.next;
  216.     L = &head;
  217.     L->next = NULL;
  218.     continue;
  219.       }
  220.       break;
  221.     }
  222.     if (cur_arg < nargs)
  223.       L = L->next = T;
  224.   }
  225. out_loop:
  226.   change_mode(0, SLURP);
  227.   if (head.next || cur_arg > 0)
  228.     args[cur_arg++] = head.next;
  229.   if (cur_arg != nargs)
  230.     error("macro \"%s\" declared with %d arg%s, used with %d",
  231.       mname, nargs, (nargs == 1 ? "" : "s"), cur_arg);
  232.   return args;
  233. }
  234.  
  235. /*
  236.    stringize() -- create a string literal representing the token list |T|
  237. */
  238. static TokenP stringize(T)
  239.   TokenP T;
  240. {
  241.   char *buf;
  242.   register char *s, *t;
  243.   size_t buflen, i;
  244.   register TokenP tt;
  245.   TokenP t0;
  246.  
  247.   s = buf = mallok(buflen = 80);
  248.   *s++ = '"';
  249.   for (tt = T; tt; tt = tt->next) {
  250.     i = 2 * strlen(token_txt(tt)) + 2;
  251.     if (tt != T)
  252.       i += strlen(token_ws(tt));
  253.     s = grow(&buf, &buflen, s, i);
  254.     if (tt != T)
  255.       for (t = token_ws(tt); *t; t++)
  256.     *s++ = *t;
  257.     for (t = token_txt(tt); *t; t++) {
  258.       if (*t == '\\' || *t == '"')
  259.     *s++ = '\\';
  260.       *s++ = *t;
  261.     }
  262.   }
  263.   *s++ = '"';
  264.   *s = '\0';
  265.   t0 = mk_Token();
  266.   t0->type = STR_CON;
  267.   set_txt(t0, buf);
  268.   free(buf);
  269.   set_ws(t0, token_ws(T));
  270.   return t0;
  271. }
  272.  
  273. /*
  274.    expand() -- expand the macro definition |M| of the Token |T|.  Push the
  275.    resulting token list onto the token stream.
  276.  
  277. WARNING:  _Always_ check the BLUEPAINT flag before calling expand(), or
  278.    you'll end up with either the wrong answer or an infinite loop, or both.
  279.    At the moment this is not a concern, since only exp_token() and
  280.    expand_tlist() call expand().
  281.  
  282. NOTE:  |T| should be free_token()'d after the call to expand().
  283. */
  284. void expand(T, M)
  285.   TokenP T;
  286.   register Macro *M;
  287. {
  288.   Token head1, head2, mhead;
  289.   register TokenP t1, pt1, t2 = &head2;
  290.   TokenP pt2 = &head1, *args, *exp_args, *str_args;
  291.   int n;
  292.   TokenP saved_eols = 0;
  293.  
  294.   head1.flags = head2.flags = mhead.flags = 0;
  295.   head1.next = &head2;
  296.   head2.next = NULL;
  297.   mhead.next = M->m_text;
  298.   if (M->flags & MARKED) {
  299.     t1 = copy_token(T);
  300.     t1->flags |= BLUEPAINT;
  301.     push_tlist(t1);
  302.     return;
  303.   }
  304.   if (M->flags & MAGIC) {
  305.     push_tlist(expand_magic(T));
  306.     return;
  307.   }
  308.   if (M->flags & HASARGS) {
  309.   jump_back:
  310.     t1 = token();
  311.     if (t1->type == STOP) {
  312.       /*
  313.          Special case:  we can't expand this token now, but if it is placed
  314.          before a left paren, we can expand it later.  Mark it to be
  315.          unpainted after expansion.
  316.       */
  317.       push_tlist(saved_eols);
  318.       push_tlist(t1);
  319.       t1 = copy_token(T);
  320.       t1->flags |= BLUEPAINT | UNPAINT_ME;
  321.       push_tlist(t1);
  322.       return;
  323.     }
  324.     if (t1->type == EOL) {
  325.       /*
  326.      If the first token on the next non-empty line is an LPAREN, it's
  327.      a valid parametrized macro invocation.  Grmbl...  Collect the
  328.      EOL's in |saved_eols|, to be pushed back if this turns out to be
  329.      a wild goose chase.  If -C is active, this will reverse the order
  330.      of any comments between the identifier and the LPAREN, but anyone
  331.      who does something that evil deserves anything he gets.  (I just
  332.      hope synchronize() will compensate for all the EOL's we'll be
  333.      effectively discarding...)
  334.       */
  335.       t1->next = saved_eols;
  336.       saved_eols = t1;
  337.       goto jump_back;
  338.     }
  339.     if (t1->type != LPAREN) {
  340.       push_tlist(saved_eols);
  341.       push_tlist(t1);
  342.       t1 = copy_token(T);
  343.       t1->flags |= BLUEPAINT;
  344.       push_tlist(t1);
  345.       return;
  346.     }
  347.     free_token(t1);
  348.     free_tlist(saved_eols);
  349.     args = get_args(token_txt(T), M->nargs);
  350.     exp_args = mallok(M->nargs * sizeof (TokenP));
  351.     str_args = mallok(M->nargs * sizeof (TokenP));
  352.     for (n = 0; n < M->nargs; n++)
  353.       if (args[n]) {
  354.     exp_args[n] = expand_tlist(copy_tlist(args[n]));
  355.     str_args[n] = stringize(args[n]);
  356.       } else
  357.     exp_args[n] = str_args[n] = NULL;
  358.   } else
  359.     args = exp_args = str_args = NULL;
  360.   M->flags |= MARKED;
  361.   t2->next = NULL;
  362.   for (t1 = M->m_text, pt1 = &mhead; t1; t1 = t1->next, pt1 = pt1->next) {
  363.     TokenP t3;
  364.  
  365.     if (t1->type == MACRO_ARG) {
  366.       t3 = copy_tlist(
  367.                (t1->flags & STRINGIZE_ME ? str_args
  368.         : t1->flags & CONCAT_NEXT || pt1->flags & CONCAT_NEXT ? args
  369.             : exp_args
  370.                )[t1->val]
  371.       );
  372.       /* copy over the leading whitespace from t1 */
  373.       set_ws(t3, token_ws(t1));
  374.     } else {
  375.       t3 = copy_token(t1);
  376.       t3->flags &= ~(STRINGIZE_ME | CONCAT_NEXT | TRAIL_SPC);
  377.     }
  378.     if (pt1->flags & CONCAT_NEXT) {
  379.       TokenP t4;
  380.  
  381.       t4 = merge_tokens(t2, t3);
  382.       pt2->next = t4;
  383.       t4->next = t3->next;
  384.       t2->next = t3->next = NULL;
  385.       free_token(t2);
  386.       free_token(t3);
  387.       t2 = t4;
  388.     } else
  389.       t2->next = t3;
  390.     while (t2->next) {
  391.       t2 = t2->next;
  392.       pt2 = pt2->next;
  393.     }
  394.   }
  395.  
  396.   /*
  397.      prepend the leading whitespace from T to the first token of the expanded
  398.      text, if any
  399.   */
  400.   if (head2.next) {
  401.     set_ws(head2.next, token_ws(T));
  402.   }
  403.   /* add a trailing space */
  404.   t2->flags |= TRAIL_SPC;
  405.   push_tlist(mk_unmarker(T));
  406.   push_tlist(head2.next);
  407.  
  408.   /* clear out the tables of arguments we created on entry */
  409.   if (args) {
  410.     for (n = 0; n < M->nargs; n++) {
  411.       if (args[n])
  412.     free_tlist(args[n]);
  413.     }
  414.     free(args);
  415.   }
  416.   if (exp_args) {
  417.     for (n = 0; n < M->nargs; n++) {
  418.       if (exp_args[n])
  419.     free_tlist(exp_args[n]);
  420.     }
  421.     free(exp_args);
  422.   }
  423.   if (str_args) {
  424.     for (n = 0; n < M->nargs; n++) {
  425.       if (str_args[n])
  426.     free_tlist(str_args[n]);
  427.     }
  428.     free(str_args);
  429.   }
  430. }
  431.  
  432. /*
  433.    expand_tlist() -- call expand() on each of a list of Token's, returning
  434.    the resulting list of Token's
  435. */
  436. TokenP expand_tlist(TL)
  437.   TokenP TL;
  438. {
  439.   Token head;
  440.   register TokenP t1, t2 = &head;
  441.   Macro *M;
  442.  
  443.   head.next = NULL;
  444.   push_tlist(mk_stopper());
  445.   push_tlist(TL);
  446.   for (t1 = token(); t1->type != STOP; t1 = token()) {
  447.     if (t1->type == ID && !(t1->flags & BLUEPAINT) &&
  448.     (M = lookup(token_txt(t1), t1->hashval))) {
  449.       expand(t1, M);
  450.       free_token(t1);
  451.     } else {
  452.       t2->next = t1;
  453.       t2 = t2->next;
  454.     }
  455.   }
  456.   /* t1 should now hold a STOP token */
  457.   if (t1->type != STOP)
  458.     bugchk("%d isn't STOP...", (int)t1->type);
  459.  
  460.   /*
  461.      Special case:  the last token in the list may need to be unpainted. See
  462.      expand() for details.
  463.   */
  464.   if (t2->flags & UNPAINT_ME)
  465.     t2->flags &= ~(BLUEPAINT | UNPAINT_ME);
  466.   free_token(t1);
  467.   return head.next;
  468. }
  469.