home *** CD-ROM | disk | FTP | other *** search
- /*
- C* -- Macro definition and expansion routines.
-
- source: def.c
- started: October 22, 1985
- version:
- January 6, 1987
- March 7, 1989
-
- PUBLIC DOMAIN SOFTWARE
-
- The CSTAR program was placed in the public domain on June 15, 1991,
- by its author and sole owner,
-
- Edward K. Ream
- 1617 Monroe Street
- Madison, WI 53711
- (608) 257-0802
-
- CSTAR may be used for any commercial or non-commercial purpose.
-
- See cstar.h or cstar.c for a DISCLAIMER OF WARRANTIES.
- */
- #include "cstar.h"
-
- /*
- Visible routines defined in this file:
- */
- void pp_def (void);
- void pp_expand (int nargs, unsigned char * rtext);
-
- /*
- Internal routines:
- */
- static void t_aalist (int nargs);
- static void t_1aarg (void);
-
- /*
- These routines do NOT use dynamic storage allocation to keep track of
- actual or formal parameters. They are kept in the following array.
- */
- #define MAX_PARAM 4000
- static unsigned char param [MAX_PARAM];
- static unsigned char * param_p;
- static int param_c;
-
- #define MAX_ARG 100
- static unsigned char * arg [MAX_ARG];
- static int arg_c;
-
- /*
- Space for replacement text.
- Must be separate from param [].
-
- WARNING: MAX_RTEXT1 + MAX_SYMBOL must be <= MAX_RTEXT
- */
- #define MAX_RTEXT 2000
- #define MAX_RTEXT1 1000
- static unsigned char rtext [MAX_RTEXT];
-
- /*
- The pp_def() routine replaces formal param n by n+ARG_OFFSET, ARG_FLAG.
- The pp_expand() routine replaces these two characters by
- the n'th actual parameter.
-
- The ARG_FLAG character should be a character that can never appear in
- normal text, but it MUST NOT BE NEGATIVE, so as to fit in a char.
- */
- #define ARG_FLAG 1
- #define ARG_OFFSET '0'
-
- /*
- Handle the #define directive by parsing the statement, and entering
- the macro's name, number of arguments and replacement text into the
- macro table.
-
- Formal arguments are found and replaced by a flag byte followed by the
- number of the formal argument.
- */
- void
- pp_def(void)
- {
- register int i;
- register unsigned char * textp;
- register int textc;
-
- TICK("pp_def");
-
- /* Initialize. */
- textp = ¶m[0];
- textc = 0;
- arg_c = 0;
-
- /* Make sure the name is present. */
- if (!isid1(ch)) {
- t_error("#define ignored -- no symbol name given.");
- skip_1line();
- return;
- }
-
- /* Put the name at the start of param[].*/
- while (isid2(ch)) {
- *textp++ = ch;
- textc++;
- sysnext();
- }
- *textp++ = '\0';
- textc++;
-
- if (ch != '(') {
- /* Indicate no argument list and continue. */
- arg_c = -1;
- goto gettext;
- }
- else {
- sysnext();
- }
-
- if (ch == ')') {
- sysnext();
- arg_c = 0;
- goto gettext;
- }
-
- /*
- Put the formal arguments into param[].
- Set pointers to the arguments in arg[].
- */
- for (arg_c = 0; arg_c < MAX_ARG; ) {
-
- TICK("pp_def2");
-
- skip_bl();
- if (!isid1(ch)) {
- t_error(
- "#define ignored -- formal arg must be an identifier"
- );
- skip_1line();
- return;
- }
-
- /* Point arg[] at the start of the parameter. */
- arg [arg_c++] = textp;
-
- /* Copy one formal arg to param[]. */
- while (isid2(ch)) {
- *textp++ = ch;
- textc++;
- sysnext();
- }
- *textp++ = '\0';
- textc++;
- if (textc >= MAX_PARAM) {
- t_error("formal parameter list too long");
- skip_1line();
- return;
- }
-
- if (ch == ')') {
- sysnext();
- goto gettext;
- }
- else if (ch == ',') {
- sysnext();
- }
- }
- t_error("#define ignored -- too many arguments.");
- skip_1line();
- return;
-
-
- /*
- At this point, arg_c contains the number of formal arguments, or
- -1 if no argument list was given. 0 is allowed and means that the
- argument list was ().
- */
-
- gettext:
-
- TICK("pp_def3");
-
- skip_bl();
-
- /*
- Put the replacement text into rtext[].
- Replace formal arg n by n FLAG on the fly.
- */
-
- textp = &rtext[0];
- textc = 0;
- for(;;) {
-
- TICK("pp_def4");
-
- switch (ch) {
-
- case END_FILE:
- goto done;
-
- case '\n':
- /* Let the main parsing loop handle the newline. */
- goto done;
-
- case '\r':
- sysnext();
- continue;
-
- case '\\':
- sysnext();
- if (skip_crlf()) {
- /* Allow continuation of definitions. */
-
- /* Do NOT put newline into replacement text. */
- /* Thus, PP direcives may NOT be recognized */
- /* from within macro expansions (4/8/86). */
-
- *textp++ = ' ';
- textc++;
-
- do_nl();
- begin_line(FALSE);
- }
- else {
- *textp++ = '\\';
- textc++;
- }
- continue;
-
- case '/':
- /* Eliminate comments */
- sysnext();
- if (ch != '*') {
- *textp++ = '/';
- textc++;
- continue;
- }
-
- for (;;) {
- if (ch == END_FILE ||
- ch == '\n' ||
- ch == '\r') {
- t_error
- ("bad comment in macro definition");
- break;
- }
- else if (ch == '*') {
- sysnext();
- if (ch == '/') {
- sysnext();
- break;
- }
- }
- else {
- sysnext();
- }
- }
- continue;
-
-
- default:
-
- TICK("pp_def5");
-
- if (!isid1(ch)) {
- *textp++ = ch;
- textc++;
- sysnext();
- continue;
- }
-
- /* Copy the id into the buffer and set t_length. */
- t_id(textp);
-
- /* See if the id is a formal arg. */
- for (i = 0; i < arg_c ; i++) {
-
- TICK("pp_def6");
-
- if (str_eq(textp, arg[i])) {
- *textp++ = (unsigned char)i + ARG_OFFSET;
- *textp++ = ARG_FLAG;
- textc += 2;
- break;
- }
- }
- if (i == arg_c || arg_c == -1) {
- /* Not found. Move past the identifier. */
- textp += t_length;
- textc += t_length;
- }
- continue;
- }
- }
-
- done:
- TICK("pp_def7");
-
- /* This check is made only here to save a little time. */
- if (textc >= MAX_RTEXT) {
- fatal("#define too long...");
- }
-
- /* Strip white space off end of definition. */
- do {
- textc--;
- textp--;
- }
- while (textc > 0 && (*textp == ' ' || *textp == '\t'));
- textp++;
- textc++;
- *textp = '\0';
-
- /*
- Enter the symbol, replacement text and number of arguments
- into the macro table.
- */
- (void) mst_enter(param, rtext, arg_c);
- }
-
- /*
- Expand a macro.
-
- Step one: put the actual parameters into param[].
- Step two: push the replacement text, replacing
- formal parameter flags by actual params on the fly.
- */
- void
- pp_expand (register int nargs, register unsigned char * rtext)
- {
- register int i;
- register unsigned char c, * textp;
- register int textc;
-
- TICK("pp_expand");
- TRACE("pp_expand",
- printf("pp_expand(nargs: %d, rtext: %s)\n", nargs, rtext));
- TRACE("pp_expand", printf("ch = %c\n",ch));
-
- /* Make no argument substitutions if none possible. */
- if (nargs < 0) {
-
- TICK("pp_expand1");
-
- /* No arguments in replacement text. */
- syspush(ch);
- sysspush(rtext);
- sysnext();
- return;
- }
-
- TICK("pp_expand1");
-
- /*
- Step 1:
-
- Put the actual parameters in param[].
- Put pointers to parameters in arg[].
- */
- t_aalist(nargs);
-
- /* Save the current character. */
- syspush(ch);
- ch = ' ';
-
- /*
- Step 2:
-
- Push back the replacement stack.
- Replace n ARG_FLAG by actual argument n.
- */
-
- textc = str_len(rtext);
- textp = rtext;
- textp += textc;
- while (textc > 0) {
-
- c = *--textp;
- textc--;
-
- TICK("pp_expand1");
-
- if (c == ARG_FLAG) {
- c = *--textp;
- c -= ARG_OFFSET;
- textc--;
-
- if ((int)c >= nargs) {
- fatal("pp_expand: can't happen");
- }
-
- TICK("pp_expand_arg");
-
- sysspush(arg [c]);
- continue;
- }
- else {
- TICK("pp_expand_char");
-
- syspush(c);
- }
- }
-
- /* Get the next character into ch. */
- sysnext();
- }
-
-
- /*
- Parse a list of nargs actual arguments.
- Put the arguments into param[].
- Put pointers to each argument in arg[].
- */
- static int call_start;
-
- static void
- t_aalist(register int nargs)
- {
- char msg [100];
- char line [10];
-
- TICK("t_aalist");
-
- /* Initialize. */
- arg_c = 0;
- param_p = ¶m[0];
- param_c = 0;
-
- /* Save starting line number of the macro call. */
- call_start = t_line;
-
- /* Look for opening parenthesis on the same line. */
- skip_bl();
- if (ch != '(') {
- t_error("Missing arguments to macro call--nulls assumed.");
- goto check1;
- }
- else {
- sysnext();
- }
-
- if (ch == ')') {
- sysnext();
- arg_c = 0;
- goto check;
- }
-
- for(;;) {
-
- TICK("t_aalist1");
-
- t_1aarg();
- if (ch == END_FILE) {
- goto check1;
- }
- else if (ch == ')') {
- sysnext();
- break;
- }
- else if (ch == ',') {
- sysnext();
- }
- else {
- /* Error detected in t_1aalist(). */
- break;
- }
- }
-
- check:
- if (arg_c != nargs) {
- if (call_start != t_line) {
- strcpy(msg, "Macro call starting at line ");
- conv2s(call_start, line);
- strcat(msg, line);
- strcat(msg, " requires ");
- }
- else {
- strcpy(msg, "Macro call requires ");
- }
- conv2s(nargs, line);
- strcat(msg, line);
- strcat(msg, " arguments.");
- t_error(msg);
- }
-
- /* 4/8/86 */
- check1:
- while (arg_c < nargs) {
- /* Clear out the missing arguments. */
- arg [arg_c++] = NULL;
- }
- }
-
- /*
- Copy the next actual argument into param[].
- On entry, param_p and param_c describe the state of param[].
- Point arg [arg_c] at the the parameter.
-
- Formal arguments are simply identifiers, which are handled by t_id(),
- but actual arguments are LISTS of tokens separated by commas. As an
- added twist, commas inside single or double quotes, or commas which are
- "protected" by additional parentheses do NOT separate actual args.
- Thus, each of the following calls have to M have ONE actual argument:
-
- M(a)
- M(a * N(c,b))
- M(',')
- M("a,b")
- M((a,b))
- M((a,")",b))
-
- There is one place where the exact details about how whitespace is
- treated DOES make a difference, namely involving whitespace in actual
- parameters to macros. The reason is that actual arguments are replaced
- EVEN INSIDE STRINGS. If the string appears in a printf() statement,
- for instance, what gets printed depends on just exactly how whitespace
- is treated. The C Refence Guide is silent on this point.
-
- This routine changes comments to one blank and retains all other white
- space as is. Thus, tokens will always be properly surrounded by white
- space when they need to be.
- */
- static void
- t_1aarg(void)
- {
- register unsigned char * textp;
- register int textc;
-
- register int plevel;
- register unsigned char delim;
-
- char buffer [100];
- char linebuf [10];
-
- TICK("t_1aarg");
-
- /* Allow raw newlines only at the beginning or end of arguments. */
- /* comment out -----
- skip_ws();
- if (ch == '\r') {
- sysnext();
- }
- if (ch == '\n') {
- do_nl();
- sysnext();
- }
- ----- end comment out */
-
- /* No parens have been seen yet. */
- plevel = 0;
-
- /* Initialize.*/
- textp = param_p;
- textc = param_c;
-
- arg [arg_c++] = textp;
-
- for(;;) {
-
- TICK("t_1aarg1");
-
- /* Make sure there is room for one more. */
- if (textc >= MAX_RTEXT - 2) {
- goto toolong;
- }
-
- switch (ch) {
-
- case END_FILE:
- goto runon;
-
- case '\n':
- /* Convert to one blank. */
- sysnext();
- do_nl();
- *textp++ = ' ';
- textc++;
- continue;
-
- case '\r':
- sysnext();
- continue;
-
- case '\\':
- sysnext();
- if (skip_crlf()) {
- *textp++ = ' ';
- textc++;
- do_nl();
- }
- else {
- *textp++ = '\\';
- textc++;
- }
- continue;
-
- case ',':
- if (plevel == 0) {
- goto end_arg;
- }
- else {
- *textp++ = ch;
- textc++;
- sysnext();
- /*
- Allow raw newlines only at the start
- or end of arguments.
- */
- /* comment out -----
- skip_ws();
- if (ch == '\r') {
- sysnext();
- }
- if (ch == '\n') {
- do_nl();
- sysnext();
- }
- ----- end comment out */
- }
- continue;
-
- case ')':
-
- if (plevel == 0) {
- goto end_arg;
- }
- else {
- plevel--;
- *textp++ = ch;
- textc++;
- sysnext();
- continue;
- }
-
- case '(':
- plevel++;
- *textp++ = ch;
- textc++;
- sysnext();
- continue;
-
- case '"':
- case '\'':
- delim = ch;
- *textp++ = delim;
- textc++;
-
- t_length = 0;
- t_string(textp);
- if (textc + t_length + 1 >= MAX_RTEXT1) {
- goto toolong;
- }
- textp += t_length;
- textc += t_length;
-
- *textp++ = delim;
- textc++;
- continue;
-
- case '/':
- sysnext();
- if (ch == '*') {
- sysnext();
- t_comment();
-
- /* Change a comment into one blank. */
- *textp++ = ' ';
- textc++;
- }
- else {
- *textp++ = '/';
- textc++;
- }
- continue;
-
- default:
- if (isid1(ch)) {
- t_id(textp);
- if (textc + t_length >= MAX_RTEXT) {
- goto toolong;
- }
- textp += t_length;
- textc++;
- }
- else {
- *textp++ = ch;
- textc++;
- sysnext();
- }
- }
- }
-
- end_arg:
- /* Finish off the argument. */
- *textp++ = '\0';
- textc++;
-
- /* Update the globals. */
- param_p = textp;
- param_c = textc;
-
- TRACE("t_1aarg",
- printf("t_1aarg returns arg[%d] = <%s>\n", arg_c - 1, arg[arg_c-1]));
- TRACE("t_1aarg", printf("t_1aarg: ch = %c\n", ch));
- return;
-
- runon:
- if (call_start != t_line) {
- conv2s(call_start, linebuf);
- str_cpy(buffer, "Runon macro call at line ");
- str_cat(buffer, linebuf);
- }
- else {
- str_cpy(buffer, "Runon macro call");
- }
- str_cat(buffer, "--last arg set to null.");
- t_error(buffer);
- arg [arg_c - 1] = NULL;
- return;
-
- toolong:
- conv2s(call_start, linebuf);
- str_cpy(buffer, "Macro arg starting at line ");
- str_cat(buffer, linebuf);
- str_cat(buffer, " is too long!");
- fatal(buffer);
- }
-