home *** CD-ROM | disk | FTP | other *** search
/ The C Users' Group Library 1994 August / wc-cdrom-cusersgrouplibrary-1994-08.iso / vol_300 / 357_01 / cstar1.exe / DEF.C < prev    next >
C/C++ Source or Header  |  1991-06-18  |  14KB  |  727 lines

  1. /*
  2.     C* -- Macro definition and expansion routines.
  3.  
  4.     source:  def.c
  5.     started: October 22, 1985
  6.     version:
  7.         January 6, 1987
  8.         March 7, 1989
  9.  
  10.     PUBLIC DOMAIN SOFTWARE
  11.  
  12.     The CSTAR program was placed in    the public domain on June 15, 1991,
  13.     by its author and sole owner,
  14.  
  15.         Edward K. Ream
  16.         1617 Monroe Street
  17.         Madison, WI 53711
  18.         (608) 257-0802
  19.  
  20.     CSTAR may be used for any commercial or non-commercial purpose.
  21.  
  22.     See cstar.h or cstar.c for a DISCLAIMER OF WARRANTIES.
  23. */
  24. #include "cstar.h"
  25.  
  26. /*
  27.     Visible routines defined in this file:
  28. */
  29. void    pp_def        (void);
  30. void    pp_expand    (int nargs, unsigned char * rtext);
  31.  
  32. /*
  33.     Internal routines:
  34. */
  35. static void t_aalist    (int nargs);
  36. static void t_1aarg    (void);
  37.  
  38. /*
  39.     These routines do NOT use dynamic storage allocation to keep track of
  40.     actual or formal parameters.  They are kept in the following array.
  41. */
  42. #define MAX_PARAM 4000
  43. static unsigned char param [MAX_PARAM];
  44. static unsigned char * param_p;
  45. static int param_c;
  46.  
  47. #define MAX_ARG 100
  48. static unsigned char * arg [MAX_ARG];
  49. static int arg_c;
  50.  
  51. /*
  52.     Space for replacement text.
  53.     Must be separate from param [].
  54.  
  55.     WARNING:  MAX_RTEXT1 + MAX_SYMBOL must be <= MAX_RTEXT
  56. */
  57. #define MAX_RTEXT 2000
  58. #define MAX_RTEXT1 1000
  59. static unsigned char rtext [MAX_RTEXT];
  60.  
  61. /*
  62.     The pp_def() routine replaces formal param n by n+ARG_OFFSET, ARG_FLAG.
  63.     The pp_expand() routine replaces these two characters by
  64.     the n'th actual parameter.
  65.  
  66.     The ARG_FLAG character should be a character that can never appear in
  67.     normal text, but it MUST NOT BE NEGATIVE, so as to fit in a char.
  68. */
  69. #define ARG_FLAG 1
  70. #define ARG_OFFSET '0'
  71.  
  72. /*
  73.     Handle the #define directive by parsing the statement, and entering
  74.     the macro's name, number of arguments and replacement text into the
  75.     macro table.
  76.  
  77.     Formal arguments are found and replaced by a flag byte followed by the
  78.     number of the formal argument.
  79. */
  80. void
  81. pp_def(void)
  82. {
  83.     register int i;
  84.     register unsigned char * textp;
  85.     register int textc;
  86.  
  87.     TICK("pp_def");
  88.  
  89.     /* Initialize. */
  90.     textp = ¶m[0];
  91.     textc = 0;
  92.     arg_c = 0;
  93.  
  94.     /* Make sure the name is present. */
  95.     if (!isid1(ch)) {
  96.         t_error("#define ignored -- no symbol name given.");
  97.         skip_1line();
  98.         return;
  99.     }
  100.  
  101.     /* Put the name at the start of param[].*/
  102.     while (isid2(ch)) {
  103.         *textp++ = ch;
  104.         textc++;
  105.         sysnext();
  106.     }
  107.     *textp++ = '\0';
  108.     textc++;
  109.     
  110.     if (ch != '(') {
  111.         /* Indicate no argument list and continue. */
  112.         arg_c = -1;
  113.         goto gettext;
  114.     }
  115.     else {
  116.         sysnext();
  117.     }
  118.  
  119.     if (ch == ')') {
  120.         sysnext();
  121.         arg_c = 0;
  122.         goto gettext;
  123.     }
  124.  
  125.     /* 
  126.         Put the formal arguments into param[].
  127.         Set pointers to the arguments in arg[].
  128.     */
  129.     for (arg_c = 0; arg_c < MAX_ARG; ) {
  130.  
  131.         TICK("pp_def2");
  132.  
  133.         skip_bl();
  134.         if (!isid1(ch)) {
  135.             t_error(
  136.             "#define ignored -- formal arg must be an identifier"
  137.             );
  138.             skip_1line();
  139.             return;
  140.         }
  141.  
  142.         /* Point arg[] at the start of the parameter. */
  143.         arg [arg_c++] = textp;
  144.  
  145.         /* Copy one formal arg to param[]. */
  146.         while (isid2(ch)) {
  147.             *textp++ = ch;
  148.             textc++;
  149.             sysnext();
  150.         }
  151.         *textp++ = '\0';
  152.         textc++;
  153.         if (textc >= MAX_PARAM) {
  154.             t_error("formal parameter list too long");
  155.             skip_1line();
  156.             return;
  157.         }
  158.             
  159.         if (ch == ')') {
  160.             sysnext();
  161.             goto gettext;
  162.         }
  163.         else if (ch == ',') {
  164.             sysnext();
  165.         }
  166.     }
  167.     t_error("#define ignored -- too many arguments.");
  168.     skip_1line();
  169.     return;
  170.  
  171.  
  172.     /*
  173.     At this point,  arg_c contains the number of formal arguments, or
  174.     -1 if no argument list was given.  0 is allowed and means that the
  175.     argument list was ().
  176.     */
  177.  
  178. gettext:
  179.  
  180.     TICK("pp_def3");
  181.  
  182.     skip_bl();
  183.  
  184.     /*
  185.         Put the replacement text into rtext[].
  186.         Replace formal arg n by n FLAG on the fly.
  187.     */
  188.  
  189.     textp = &rtext[0];
  190.     textc = 0;
  191.     for(;;) {
  192.  
  193.         TICK("pp_def4");
  194.  
  195.         switch (ch) {
  196.  
  197.         case END_FILE:
  198.             goto done;
  199.  
  200.         case '\n':
  201.             /* Let the main parsing loop handle the newline. */
  202.             goto done;
  203.  
  204.         case '\r':
  205.             sysnext();
  206.             continue;
  207.  
  208.         case '\\':
  209.             sysnext();
  210.             if (skip_crlf()) {
  211.                 /* Allow continuation of definitions. */
  212.  
  213.                 /* Do NOT put newline into replacement text. */
  214.                 /* Thus, PP direcives may NOT be recognized  */
  215.                 /* from within macro expansions (4/8/86).    */    
  216.  
  217.                 *textp++ = ' ';
  218.                 textc++;
  219.  
  220.                 do_nl();
  221.                 begin_line(FALSE);
  222.             }
  223.             else {
  224.                 *textp++ = '\\';
  225.                 textc++;
  226.             }
  227.             continue;
  228.  
  229.         case '/':
  230.             /* Eliminate comments */
  231.             sysnext();
  232.             if (ch != '*') {
  233.                 *textp++ = '/';
  234.                 textc++;
  235.                 continue;
  236.             }
  237.             
  238.             for (;;) {
  239.                 if (ch == END_FILE ||
  240.                     ch == '\n' ||
  241.                     ch == '\r') {
  242.                     t_error
  243.                     ("bad comment in macro definition");
  244.                     break;
  245.                 }
  246.                 else if (ch == '*') {
  247.                     sysnext();
  248.                     if (ch == '/') {
  249.                         sysnext();
  250.                         break;
  251.                     }
  252.                 }
  253.                 else {
  254.                     sysnext();
  255.                 }
  256.             }
  257.             continue;
  258.             
  259.  
  260.         default:
  261.  
  262.             TICK("pp_def5");
  263.  
  264.             if (!isid1(ch)) {
  265.                 *textp++ = ch;
  266.                 textc++;
  267.                 sysnext();
  268.                 continue;
  269.             }
  270.  
  271.             /* Copy the id into the buffer and set t_length. */
  272.             t_id(textp);
  273.  
  274.             /* See if the id is a formal arg. */
  275.             for (i = 0; i < arg_c ; i++) {
  276.  
  277.                 TICK("pp_def6");
  278.  
  279.                 if (str_eq(textp, arg[i])) {
  280.                     *textp++ = (unsigned char)i + ARG_OFFSET;
  281.                     *textp++ = ARG_FLAG;
  282.                     textc += 2;
  283.                     break;
  284.                 }
  285.             }
  286.             if (i == arg_c || arg_c == -1) {
  287.                 /* Not found.  Move past the identifier. */
  288.                 textp += t_length;
  289.                 textc += t_length;
  290.             }
  291.             continue;
  292.         }
  293.     }
  294.  
  295. done:
  296.     TICK("pp_def7");
  297.  
  298.     /* This check is made only here to save a little time. */
  299.     if (textc >= MAX_RTEXT) {
  300.         fatal("#define too long...");
  301.     }
  302.  
  303.     /* Strip white space off end of definition. */
  304.     do {
  305.         textc--;
  306.         textp--;
  307.     }
  308.     while (textc > 0 && (*textp == ' ' || *textp == '\t'));
  309.     textp++;
  310.     textc++;
  311.     *textp = '\0';
  312.             
  313.     /*
  314.         Enter the symbol, replacement text and number of arguments
  315.         into the macro table.
  316.     */
  317.     (void) mst_enter(param, rtext, arg_c);
  318. }
  319.  
  320. /*
  321.     Expand a macro.
  322.  
  323.     Step one:    put the actual parameters into param[].
  324.     Step two:    push the replacement text, replacing
  325.             formal parameter flags by actual params on the fly.
  326. */
  327. void
  328. pp_expand (register int nargs, register unsigned char * rtext)
  329. {
  330.     register int i;
  331.     register unsigned char c, * textp;
  332.     register int textc;
  333.  
  334.     TICK("pp_expand");
  335.     TRACE("pp_expand",
  336.     printf("pp_expand(nargs: %d, rtext: %s)\n", nargs, rtext));
  337.     TRACE("pp_expand", printf("ch = %c\n",ch));
  338.  
  339.     /* Make no argument substitutions if none possible. */
  340.     if (nargs < 0) {
  341.  
  342.         TICK("pp_expand1");
  343.  
  344.         /* No arguments in replacement text. */
  345.         syspush(ch);
  346.         sysspush(rtext);
  347.         sysnext();
  348.         return;
  349.     }
  350.  
  351.     TICK("pp_expand1");
  352.  
  353.     /*
  354.         Step 1:
  355.  
  356.         Put the actual parameters in param[].
  357.         Put pointers to parameters in arg[].
  358.     */
  359.     t_aalist(nargs);
  360.  
  361.     /* Save the current character. */
  362.     syspush(ch);
  363.     ch = ' ';
  364.  
  365.     /*
  366.         Step 2:
  367.  
  368.         Push back the replacement stack.
  369.         Replace n ARG_FLAG by actual argument n.
  370.     */
  371.  
  372.     textc = str_len(rtext);
  373.     textp = rtext;
  374.     textp += textc;
  375.     while (textc > 0) {
  376.  
  377.         c = *--textp;
  378.         textc--;
  379.  
  380.         TICK("pp_expand1");
  381.  
  382.         if (c == ARG_FLAG) {
  383.             c = *--textp;
  384.             c -= ARG_OFFSET;
  385.             textc--;
  386.  
  387.             if ((int)c >= nargs) {
  388.                 fatal("pp_expand:  can't happen");
  389.             }
  390.  
  391.             TICK("pp_expand_arg");
  392.  
  393.             sysspush(arg [c]);
  394.             continue;
  395.         }
  396.         else {
  397.             TICK("pp_expand_char");
  398.  
  399.             syspush(c);
  400.         }
  401.     }
  402.  
  403.     /* Get the next character into ch. */
  404.     sysnext();
  405. }
  406.  
  407.  
  408. /*
  409.     Parse a list of nargs actual arguments.
  410.     Put the arguments into param[].
  411.     Put pointers to each argument in arg[].
  412. */
  413. static int call_start;
  414.  
  415. static void
  416. t_aalist(register int nargs)
  417. {
  418.     char msg [100];
  419.     char line [10];
  420.     
  421.     TICK("t_aalist");
  422.  
  423.     /* Initialize. */
  424.     arg_c   = 0;
  425.     param_p = ¶m[0];
  426.     param_c = 0;
  427.  
  428.     /* Save starting line number of the macro call. */
  429.     call_start = t_line;
  430.     
  431.     /* Look for opening parenthesis on the same line. */
  432.     skip_bl();
  433.     if (ch != '(') {
  434.         t_error("Missing arguments to macro call--nulls assumed.");
  435.         goto check1;
  436.     }
  437.     else {
  438.         sysnext();
  439.     }
  440.  
  441.     if (ch == ')') {
  442.         sysnext();
  443.         arg_c = 0;
  444.         goto check;
  445.     }
  446.  
  447.     for(;;) {
  448.  
  449.         TICK("t_aalist1");
  450.  
  451.         t_1aarg();
  452.         if (ch == END_FILE) {
  453.             goto check1;
  454.         }
  455.         else if (ch == ')') {
  456.             sysnext();
  457.             break;
  458.         }
  459.         else if (ch == ',') {
  460.             sysnext();
  461.         }
  462.         else {
  463.             /* Error detected in t_1aalist(). */
  464.             break;
  465.         }
  466.     }
  467.  
  468. check:
  469.     if (arg_c != nargs) {
  470.         if (call_start != t_line) {
  471.             strcpy(msg, "Macro call starting at line ");
  472.             conv2s(call_start, line);
  473.             strcat(msg, line);
  474.             strcat(msg, " requires ");
  475.         }
  476.         else {
  477.             strcpy(msg, "Macro call requires ");
  478.         }
  479.         conv2s(nargs, line);
  480.         strcat(msg, line);
  481.         strcat(msg, " arguments.");
  482.         t_error(msg);
  483.     }
  484.  
  485.     /* 4/8/86 */
  486. check1:
  487.     while (arg_c < nargs) {
  488.         /* Clear out the missing arguments. */
  489.         arg [arg_c++] = NULL;
  490.     }
  491. }
  492.  
  493. /*
  494.     Copy the next actual argument into param[].
  495.     On entry, param_p and param_c describe the state of param[].
  496.     Point arg [arg_c] at the the parameter.
  497.  
  498.     Formal arguments are simply identifiers, which are handled by t_id(),
  499.     but actual arguments are LISTS of tokens separated by commas.  As an
  500.     added twist, commas inside single or double quotes, or commas which are
  501.     "protected" by additional parentheses do NOT separate actual args.
  502.     Thus, each of the following calls have to M have ONE actual argument:
  503.  
  504.         M(a)
  505.         M(a * N(c,b))
  506.         M(',')
  507.         M("a,b")
  508.         M((a,b))
  509.         M((a,")",b))
  510.  
  511.     There is one place where the exact details about how whitespace is
  512.     treated DOES make a difference, namely involving whitespace in actual
  513.     parameters to macros.  The reason is that actual arguments are replaced
  514.     EVEN INSIDE STRINGS.  If the string appears in a printf() statement,
  515.     for instance, what gets printed depends on just exactly how whitespace
  516.     is treated.  The C Refence Guide is silent on this point.
  517.  
  518.     This routine changes comments to one blank and retains all other white
  519.     space as is.  Thus, tokens will always be properly surrounded by white
  520.     space when they need to be.
  521. */
  522. static void
  523. t_1aarg(void)
  524. {
  525.     register unsigned char * textp;
  526.     register int textc;
  527.  
  528.     register int plevel;
  529.     register unsigned char delim;
  530.  
  531.     char buffer [100];
  532.     char linebuf [10];
  533.  
  534.     TICK("t_1aarg");
  535.  
  536.     /* Allow raw newlines only at the beginning or end of arguments. */
  537.     /* comment out -----
  538.     skip_ws();
  539.     if (ch == '\r') {
  540.         sysnext();
  541.     }
  542.     if (ch == '\n') {
  543.         do_nl();
  544.         sysnext();
  545.     }
  546.     ----- end comment out */
  547.  
  548.     /* No parens have been seen yet. */
  549.     plevel = 0;
  550.  
  551.     /* Initialize.*/
  552.     textp = param_p;
  553.     textc = param_c;
  554.  
  555.     arg [arg_c++] = textp;
  556.  
  557.     for(;;) {
  558.  
  559.         TICK("t_1aarg1");
  560.  
  561.         /* Make sure there is room for one more. */
  562.         if (textc >= MAX_RTEXT - 2) {
  563.             goto toolong;
  564.         }
  565.  
  566.         switch (ch) {
  567.  
  568.         case END_FILE:
  569.             goto runon;
  570.  
  571.         case '\n':
  572.             /* Convert to one blank. */
  573.             sysnext();
  574.             do_nl();
  575.             *textp++ = ' ';
  576.             textc++;
  577.             continue;
  578.  
  579.         case '\r':
  580.             sysnext();
  581.             continue;
  582.  
  583.         case '\\':
  584.             sysnext();
  585.             if (skip_crlf()) {
  586.                 *textp++ = ' ';
  587.                 textc++;
  588.                 do_nl();
  589.             }
  590.             else {
  591.                 *textp++ = '\\';
  592.                 textc++;
  593.             }
  594.             continue;
  595.  
  596.         case ',':
  597.             if (plevel == 0) {
  598.                 goto end_arg;
  599.             }
  600.             else {
  601.                 *textp++ = ch;
  602.                 textc++;
  603.                 sysnext();
  604.                 /*
  605.                     Allow raw newlines only at the start
  606.                     or end of arguments.
  607.                 */
  608.                 /* comment out -----
  609.                 skip_ws();
  610.                 if (ch == '\r') {
  611.                     sysnext();
  612.                 }
  613.                 if (ch == '\n') {
  614.                     do_nl();
  615.                     sysnext();
  616.                 }
  617.                 ----- end comment out */
  618.             }
  619.             continue;
  620.                 
  621.         case ')':
  622.  
  623.             if (plevel == 0) {
  624.                 goto end_arg;
  625.             }
  626.             else {
  627.                 plevel--;
  628.                 *textp++ = ch;
  629.                 textc++;
  630.                 sysnext();
  631.                 continue;
  632.             }
  633.  
  634.         case '(':
  635.             plevel++;
  636.             *textp++ = ch;
  637.             textc++;
  638.             sysnext();
  639.             continue;
  640.  
  641.         case '"':
  642.         case '\'':
  643.             delim = ch;
  644.             *textp++ = delim;
  645.             textc++;
  646.  
  647.             t_length = 0;
  648.             t_string(textp);
  649.             if (textc + t_length + 1 >= MAX_RTEXT1) {
  650.                 goto toolong;
  651.             }
  652.             textp += t_length;
  653.             textc += t_length;
  654.  
  655.             *textp++ = delim;
  656.             textc++;
  657.             continue;
  658.  
  659.         case '/':
  660.             sysnext();
  661.             if (ch == '*') {
  662.                 sysnext();
  663.                 t_comment();
  664.  
  665.                 /* Change a comment into one blank. */
  666.                 *textp++ = ' ';
  667.                 textc++;
  668.             }
  669.             else {
  670.                 *textp++ = '/';
  671.                 textc++;
  672.             }
  673.             continue;
  674.  
  675.         default:
  676.             if (isid1(ch)) {
  677.                 t_id(textp);
  678.                 if (textc + t_length >= MAX_RTEXT) {
  679.                     goto toolong;
  680.                 }
  681.                 textp += t_length;
  682.                 textc++;
  683.             }
  684.             else {
  685.                 *textp++ = ch;
  686.                 textc++;
  687.                 sysnext();
  688.             }
  689.         }
  690.     }
  691.  
  692. end_arg:
  693.     /* Finish off the argument. */
  694.     *textp++ = '\0';
  695.     textc++;
  696.  
  697.     /* Update the globals. */
  698.     param_p = textp;
  699.     param_c = textc;
  700.  
  701.     TRACE("t_1aarg",
  702.     printf("t_1aarg returns arg[%d] = <%s>\n", arg_c - 1, arg[arg_c-1]));
  703.     TRACE("t_1aarg", printf("t_1aarg: ch = %c\n", ch));
  704.     return;
  705.  
  706. runon:
  707.     if (call_start != t_line) {
  708.         conv2s(call_start, linebuf);
  709.         str_cpy(buffer, "Runon macro call at line ");
  710.         str_cat(buffer, linebuf);
  711.     }
  712.     else {
  713.         str_cpy(buffer, "Runon macro call");
  714.     }
  715.     str_cat(buffer, "--last arg set to null.");
  716.     t_error(buffer);
  717.     arg [arg_c - 1] = NULL;
  718.     return;
  719.  
  720. toolong:
  721.     conv2s(call_start, linebuf);
  722.     str_cpy(buffer, "Macro arg starting at line ");
  723.     str_cat(buffer, linebuf);
  724.     str_cat(buffer, " is too long!");
  725.     fatal(buffer);
  726. }
  727.