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

  1.  
  2. /*---------------------------------------------------------------------*\
  3. |                                    |
  4. | CPP -- a stand-alone C preprocessor                    |
  5. | Copyright (c) 1993 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.   "__INCLUDE_LEVEL__",
  38. };
  39. int N_MWORDS = nelems(magic_words);
  40. int N_M2WORDS = 3;        /* number of "special" magic words */
  41.  
  42. /* mk_Macro() -- allocate and initialize a macro structure */
  43. Macro *mk_Macro()
  44. {
  45.   register Macro *M = alloc_Macro();
  46.  
  47.   M->flags = M->nargs = 0;
  48.   M->m_text = M->argnames = NULL;
  49.   M->next = NULL;
  50.   return M;
  51. }
  52.  
  53. /* free_Macro() -- clean up and deallocate a macro structure */
  54. void free_Macro(M)
  55.   register Macro *M;
  56. {
  57.   if (!M)
  58.     return;
  59.   if (M->m_text)
  60.     free_tlist(M->m_text);
  61.   if (M->argnames)
  62.     free_tlist(M->argnames);
  63.   dealloc_Macro(M);
  64. }
  65.  
  66. /*
  67.    magic_token() -- create a Token to be returned by expand_magic() below
  68. */
  69. static TokenP magic_token(type, text, l, T0)
  70.   int type;
  71.   const char *text;
  72.   long l;
  73.   TokenP T0;
  74. {
  75.   register TokenP T = mk_Token();
  76.  
  77.   set_ws(T, token_ws(T0));
  78.   if (type != STR_CON)
  79.     set_txt(T, text);
  80.   else {
  81.     char *u = mallok(strlen(text) + 3);
  82.  
  83.     (void)sprintf(u, "\"%s\"", text);
  84.     set_txt(T, u);
  85.     free(u);
  86.   }
  87.   T->type = type;
  88.   if (type == NUMBER)
  89.     T->val = l;
  90.   if (type == ID)
  91.     T->flags |= BLUEPAINT;
  92.   return T;
  93. }
  94.  
  95. /* expand_magic() -- expand a magic preprocessor constant */
  96. static TokenP expand_magic(T)
  97.   register TokenP T;
  98. {
  99.   static char buf[20];
  100.   int i;
  101.   register TokenP T1, tL;
  102.   Token tLH;
  103.  
  104.   for (i = 0; i < N_MWORDS; i++)
  105.     if (streq(token_txt(T), magic_words[i]))
  106.       break;
  107.   switch (i) {
  108.   case 0:            /* __STDC__ */
  109.     return magic_token(NUMBER, "1", 1L, T);
  110.   case 1:            /* defined */
  111.     tL = &tLH;
  112.     tL->next = NULL;
  113.     buf[0] = '0';
  114.     buf[1] = '\0';
  115.     T1 = token();
  116.     if (T1->type == STOP) {
  117.       push_tlist(T1);
  118.       error("defined() has no parameter");
  119.       goto nope;
  120.     }
  121. #if 0
  122.     tL = tL->next = T1;
  123. #endif
  124.     if (T1->type == ID) {
  125.       if (lookup(token_txt(T1), T1->hashval))
  126.     buf[0] = '1';
  127.       free_token(T1);
  128.     } else if (T1->type == LPAREN) {
  129.       free_token(T1);
  130.       T1 = token();
  131. #if 0
  132.       tL = tL->next = T1;
  133. #endif
  134.       if (T1->type == STOP) {
  135.     push_tlist(T1);
  136.     error("unterminated defined() macro");
  137.     goto nope;
  138.       } else if (T1->type == ID) {
  139.     if (lookup(token_txt(T1), T1->hashval))
  140.       buf[0] = '1';
  141.     free_token(T1);
  142.     T1 = token();
  143. #if 0
  144.     tL = tL->next = T1;
  145. #endif
  146.     if (T1->type != RPAREN) {
  147.       push_tlist(T1);
  148.       error("missing `)' in defined()");
  149.       goto nope;
  150.     }
  151.     free_token(T1);
  152.       } else {
  153.     error(SC_not_id, token_txt(T1));
  154.     free_token(T1);
  155.     goto nope;
  156.       }
  157.     } else {
  158.       error(SC_not_id, token_txt(T1));
  159.       free_token(T1);
  160.       goto nope;
  161.     }
  162. #if 0
  163.     free_tlist(tLH.next);
  164. #endif
  165.     return magic_token(NUMBER, buf, (long)(*buf == '1'), T);
  166.   nope:
  167. #if 0
  168.     push_tlist(tLH.next);
  169. #endif
  170.     return magic_token(NUMBER, "0", 0L, T);
  171.   case 2:            /* __FLUFF__ */
  172.     return magic_token(NUMBER, "1", 1L, T);
  173.   case 3:            /* __DATE__ */
  174.     return magic_token(STR_CON, date_string, 0L, T);
  175.   case 4:            /* __TIME__ */
  176.     return magic_token(STR_CON, time_string, 0L, T);
  177.   case 5:            /* __FILE__ */
  178.     return magic_token(STR_CON, cur_file, 0L, T);
  179.   case 6:            /* __LINE__ */
  180.     sprintf(buf, "%lu", this_line);
  181.     return magic_token(NUMBER, buf, this_line, T);
  182.   case 7:            /* __INCLUDE_LEVEL__ */
  183.     sprintf(buf, "%lu", include_level);
  184.     return magic_token(NUMBER, buf, include_level, T);
  185.   default:
  186.     bugchk("unknown magic word %s", token_txt(T));
  187.   }
  188. }
  189.  
  190. /*
  191.    get_args() -- read in the actual arguments of a macro expansion. |mname|
  192.    is the name of the macro being expanded; |nargs| is the number of
  193.    arguments to read.
  194. */
  195. static TokenP *get_args(mname, nargs)
  196.   char *mname;
  197.   int nargs;
  198. {
  199.   int cur_arg, par_level = 0;
  200.   register TokenP T, L;
  201.   register TokenP *args;
  202.   Token head;
  203.  
  204.   args = (TokenP *) mallok((nargs ? nargs : 1) * sizeof (TokenP));
  205.   for (cur_arg = 0; cur_arg < nargs || cur_arg == 0; cur_arg++)
  206.     args[cur_arg] = NULL;
  207.   cur_arg = 0;
  208.   L = &head;
  209.   L->next = NULL;
  210.   change_mode(SLURP, 0);
  211.   for (;;) {
  212.     T = token();
  213.     switch (T->type) {
  214.     case STOP:
  215.       push_tlist(T);
  216.       error("unterminated macro \"%s\"", mname);
  217.       goto out_loop;
  218.     case EOL:
  219.       free_token(T);
  220.       continue;
  221.     case LPAREN:
  222.       par_level++;
  223.       break;
  224.     case RPAREN:
  225.       if (--par_level < 0) {
  226.     free_token(T);
  227.     goto out_loop;
  228.       }
  229.       break;
  230.     case COMMA:
  231.       if (par_level == 0) {
  232.     free_token(T);
  233.     args[cur_arg++] = head.next;
  234.     L = &head;
  235.     L->next = NULL;
  236.     continue;
  237.       }
  238.       break;
  239.     }
  240.     if (cur_arg < nargs)
  241.       L = L->next = T;
  242.   }
  243. out_loop:
  244.   change_mode(0, SLURP);
  245.   if (head.next || cur_arg > 0)
  246.     args[cur_arg++] = head.next;
  247.   if (cur_arg != nargs)
  248.     error("macro \"%s\" declared with %d arg%s, used with %d",
  249.       mname, nargs, (nargs == 1 ? "" : "s"), cur_arg);
  250.   return args;
  251. }
  252.  
  253. /*
  254.    stringize() -- create a string literal representing the token list |T|
  255. */
  256. static TokenP stringize(T)
  257.   TokenP T;
  258. {
  259.   char *buf;
  260.   register char *s, *t;
  261.   size_t buflen, i;
  262.   ptrdiff_t dp;
  263.   register TokenP tt;
  264.   TokenP t0;
  265.  
  266.   s = buf = mallok(buflen = 80);
  267.   *s++ = '"';
  268.   for (tt = T; tt; tt = tt->next) {
  269.     i = 2 * strlen(token_txt(tt)) + 2;
  270.     if (tt != T)
  271.       i += strlen(token_ws(tt));
  272.     s = grow(&buf, &buflen, s, i);
  273.     if (tt != T)
  274.       for (t = token_ws(tt); *t; t++)
  275.     *s++ = *t;
  276.     for (t = token_txt(tt); *t; t++) {
  277.       if (*t == '\\' || *t == '"')
  278.     *s++ = '\\';
  279.       *s++ = *t;
  280.     }
  281.   }
  282.   *s++ = '"';
  283.   *s = '\0';
  284.   t0 = mk_Token();
  285.   t0->type = STR_CON;
  286.   set_txt(t0, buf);
  287.   free(buf);
  288.   set_ws(t0, token_ws(T));
  289.   return t0;
  290. }
  291.  
  292. /*
  293.    expand() -- expand the macro definition |M| of the Token |T|.  Push the
  294.    resulting token list onto the token stream.
  295.  
  296. WARNING:  _Always_ check the BLUEPAINT flag before calling expand(), or
  297.    you'll end up with either the wrong answer or an infinite loop, or both.
  298.    At the moment this is not a concern, since only exp_token() and
  299.    expand_tlist() call expand().
  300.  
  301. NOTE:  |T| should be free_token()'d after the call to expand().
  302. */
  303. void expand(T, M)
  304.   TokenP T;
  305.   register Macro *M;
  306. {
  307.   Token head1, head2, mhead;
  308.   register TokenP t1, pt1, t2 = &head2;
  309.   TokenP pt2 = &head1, *args, *exp_args, *str_args;
  310.   int n;
  311.  
  312.   head1.flags = head2.flags = mhead.flags = 0;
  313.   head1.next = &head2;
  314.   head2.next = NULL;
  315.   mhead.next = M->m_text;
  316.   if (M->flags & MARKED) {
  317.     t1 = copy_token(T);
  318.     t1->flags |= BLUEPAINT;
  319.     push_tlist(t1);
  320.     return;
  321.   }
  322.   if (M->flags & MAGIC) {
  323.     push_tlist(expand_magic(T));
  324.     return;
  325.   }
  326.   if (M->flags & HASARGS) {
  327.     t1 = token();
  328.     if (t1->type == STOP) {
  329.  
  330.       /*
  331.          Special case:  we can't expand this token now, but if it is placed
  332.          before a left paren, we can expand it later.  Mark it to be
  333.          unpainted after expansion.
  334.       */
  335.       push_tlist(t1);
  336.       t1 = copy_token(T);
  337.       t1->flags |= BLUEPAINT | UNPAINT_ME;
  338.       push_tlist(t1);
  339.       return;
  340.     }
  341.     if (t1->type != LPAREN) {
  342.       push_tlist(t1);
  343.       t1 = copy_token(T);
  344.       t1->flags |= BLUEPAINT;
  345.       push_tlist(t1);
  346.       return;
  347.     }
  348.     free_token(t1);
  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. #if 0
  374.       if (t1->pre_ws) {
  375.     if (t3->pre_ws)
  376.       free(t3->pre_ws);
  377.     t3->pre_ws = strdup(t1->pre_ws);
  378.       }
  379. #else
  380.       set_ws(t3, token_ws(t1));
  381. #endif
  382.     } else {
  383.       t3 = copy_token(t1);
  384.       t3->flags &= ~(STRINGIZE_ME | CONCAT_NEXT | TRAIL_SPC);
  385.     }
  386.     if (pt1->flags & CONCAT_NEXT) {
  387.       TokenP t4;
  388.  
  389.       t4 = merge_tokens(t2, t3);
  390.       pt2->next = t4;
  391.       t4->next = t3->next;
  392.       t2->next = t3->next = NULL;
  393.       free_token(t2);
  394.       free_token(t3);
  395.       t2 = t4;
  396.     } else
  397.       t2->next = t3;
  398.     while (t2->next) {
  399.       t2 = t2->next;
  400.       pt2 = pt2->next;
  401.     }
  402.   }
  403.  
  404.   /*
  405.      prepend the leading whitespace from T to the first token of the expanded
  406.      text, if any
  407.   */
  408.   if (head2.next) {
  409. #if 0
  410.     if (head2.next->pre_ws)
  411.       free(head2.next->pre_ws);
  412.     head2.next->pre_ws = strdup(T->pre_ws);
  413. #else
  414.     set_ws(head2.next, token_ws(T));
  415. #endif
  416.   }
  417.   /* add a trailing space */
  418.   t2->flags |= TRAIL_SPC;
  419.   push_tlist(mk_unmarker(T));
  420.   push_tlist(head2.next);
  421.  
  422.   /* clear out the tables of arguments we created on entry */
  423.   if (args) {
  424.     for (n = 0; n < M->nargs; n++) {
  425.       if (args[n])
  426.     free_tlist(args[n]);
  427.     }
  428.     free(args);
  429.   }
  430.   if (exp_args) {
  431.     for (n = 0; n < M->nargs; n++) {
  432.       if (exp_args[n])
  433.     free_tlist(exp_args[n]);
  434.     }
  435.     free(exp_args);
  436.   }
  437.   if (str_args) {
  438.     for (n = 0; n < M->nargs; n++) {
  439.       if (str_args[n])
  440.     free_tlist(str_args[n]);
  441.     }
  442.     free(str_args);
  443.   }
  444. }
  445.  
  446. /*
  447.    expand_tlist() -- call expand() on each of a list of Token's, returning
  448.    the resulting list of Token's
  449. */
  450. TokenP expand_tlist(TL)
  451.   TokenP TL;
  452. {
  453.   Token head;
  454.   register TokenP t1, t2 = &head;
  455.   Macro *M;
  456.  
  457.   head.next = NULL;
  458.   push_tlist(mk_stopper());
  459.   push_tlist(TL);
  460.   for (t1 = token(); t1->type != STOP; t1 = token()) {
  461.     if (t1->type == ID && !(t1->flags & BLUEPAINT) &&
  462.     (M = lookup(token_txt(t1), t1->hashval))) {
  463.       expand(t1, M);
  464.       free_token(t1);
  465.     } else {
  466.       t2->next = t1;
  467.       t2 = t2->next;
  468.     }
  469.   }
  470.   /* t1 should now hold a STOP token */
  471.   if (t1->type != STOP)
  472.     bugchk("%d isn't STOP...", (int)t1->type);
  473.  
  474.   /*
  475.      Special case:  the last token in the list may need to be unpainted. See
  476.      expand() for details.
  477.   */
  478.   if (t2->flags & UNPAINT_ME)
  479.     t2->flags &= ~(BLUEPAINT | UNPAINT_ME);
  480.   free_token(t1);
  481.   return head.next;
  482. }
  483.