home *** CD-ROM | disk | FTP | other *** search
- /*
-
-
- Copyright (C) 1990 Texas Instruments Incorporated.
-
- Permission is granted to any individual or institution to use, copy, modify,
- and distribute this software, provided that this complete copyright and
- permission notice is maintained, intact, in all copies and supporting
- documentation.
-
- Texas Instruments Incorporated provides this software "as is" without
- express or implied warranty.
-
-
- *
- * Edit history
- * Created: LGO 30-Mar-89 -- Initial design and implementation.
- *
- * MACRO defmacro
- *
- * CPP defmacro for building macros which can include cpp directives.
- * MACRO works like #define, except the macro body is NOT limited to a
- * single line, and may contain arbitrary cpp directives. Optional,
- * keyword and body parameters are accepted.
- *
- * To define a macro, the following must have been encountered in the input
- * stream:
- * #pragma defmacro MACRO "macro" delimiter=}
- *
- * A macro definition has the form:
- * MACRO name(arglist) { body }
- *
- * where name is the name of the macro, arglist are argument
- * specifiers seperated by comma's and body is what is substituted on a
- * call to the macro. arglist may specify positional, optional,
- * optional keyword, required keyword, rest and body arguments.
- * The syntax is:
- * arglist := [KEY: | REST: | BODY] identifier [= identifier] [, arglist]
- *
- * The equals sign indicates a default argument value.
- * When KEY: is encountered, the rest of the arguments are all keyword
- * arguments. A rest parameter will be set to all remaining parameter
- * values during macro expansion. REST: parm=name indicates that "parm"
- * will hold the rest of the parameters, and "name" will hold the number
- * of parameters in "parm". BODY: parm indicates that "parm" will expand to
- * the text within braces {} after the macro call. The braces are removed.
- *
- * Example 1:
- * MACRO set_val (size, value=NULL, KEY: low = 0, high = 100)
- * { set_val_internal(size, val, low, high-low) }
- *
- * set_val is the macro name
- * size is a required positional parameter
- * val is an optional positional parameter
- * low is an optional keyword parameter
- * high is a required keyword parameter
- *
- * Example 2:
- * // Build a table of char*'s
- * MACRO build_table(name, REST: rest)
- * { char* name[] = { build_table_internal(rest) NULL} }
- *
- * MACRO build_table_internal(first, REST: rest=count)
- * {#first,
- * #if count // recurse on rest args
- * build_table_internal(rest)
- * #endif
- * }
- *
- * build_table(table, 1,2,3,4,5,6,7); // expands to
- * char* table[] = {"1", "2", "3", "4", "5", "6", "7"};
- *
- * Example 3:
- *
- * MACRO LOOP (type, variable, sequence, BODY: body) {
- * { type varible;
- * for(sequence.reset; sequence.next;) {
- * variable = sequence.value();
- * body
- * }
- * }}
- *
- * LOOP(char, elt, list) { print(elt) } // Expands to:
- *
- * { char varible;
- * for(list.reset; list.next;) {
- * elt = list.value();
- * print(elt)
- * }
- * }
- *
- * Error messages: (first %s is macro name)
- * %s: Can't find argument list
- * %s: REST and BODY args must be last
- * %s: Unknown parameter keyword: %s:
- * %s: Syntax error, '%c' found after %s=%s
- * %s: Syntax error, '%c' found after %s
- * %s: The required argument %s is missing
- * %s: Keyword missing for %s
- * %s: The required keyword argument %s is missing
- * %s: body argument not found
- * %s: Unknown argument: %s
- * %s: Missing ; after macro body
- */
-
- #include "defmacio.h"
- #include "macro.h"
-
- typedef struct mac_def {
- char* name;
- Arg* args;
- char* body;
- } Mac_Def;
-
- #define BSIZE 512
- #define MAXPARM 32
-
- Arg arg_error; /* Global error flag */
- /*
- * Parse macro args
- */
- Arg*
- macro_args(macname)
- char* macname;
- {
- char c;
- char* name;
- char* value;
- Type type = p_positional;
- Arg* result = NULL;
- Arg* next = NULL;
- Arg* new;
- Boolean rest_or_body = FALSE;
-
- c = skip_blanks();
- if(c != '(') {
- fprintf(stderr, "%s: Can't find argument list", macname);
- return &arg_error;
- }
- do {
- if(type==p_body || type==p_rest)
- rest_or_body = TRUE;
- else if (rest_or_body) {
- fprintf(stderr, "%s: REST and BODY args must be last", macname);
- return &arg_error;
- }
- value="";
- name=scan_token(",):=");
- c = getchar();
- if (c==':') {
- if(!strcmp(name, "KEY"))
- type=p_key;
- else if (!strcmp(name, "REST"))
- type=p_rest;
- else if (!strcmp(name, "BODY"))
- type=p_body;
- else {
- fprintf(stderr, "%s: Unknown parameter keyword: %s", macname, name);
- return &arg_error;
- }
- name=scan_token(",):=");
- c = getchar();
- }
- switch (c) {
- case ')': break;
- case ',': break;
- case '=':
- value=scan_token(",)");
- c=getchar();
- if(c == ',' || c == ')') break;
- fprintf(stderr, "%s: Syntax error, '%c' found after %s=%s",
- macname, c, name, value);
- return &arg_error;
- default:
- fprintf(stderr, "%s: Syntax error, '%c' found after %s", macname, c, name);
- return &arg_error;
- }
- new = (Arg*) getmem(sizeof(Arg)); /* Create new Arg structure */
- if (result == NULL)
- result = new;
- else
- next->next = new;
- next = new;
- new->next = NULL;
- new->name = name;
- new->value = value;
- new->type = type;
- } while (c != ')');
- return result;
- }
- /*
- * Write body returning when a token is found
- */
- static char*
- get_token(body, tokenbuf, modifier)
- char* body;
- char* tokenbuf;
- Modifier* modifier;
- {
- char c;
- char p = EOS;
- char* bodyp = body;
- char* tokenp = tokenbuf;
- *modifier = mod_none;
- while((c = *bodyp++) != EOS) {
- if(isalnum(c) || c=='_' || c=='$') {
- *tokenp++ = c;
- while((c = *bodyp++) != EOS) {
- if((isalnum(c) || c=='_' || c=='$'))
- *tokenp++ = c;
- else { /* End of token */
- *tokenp = EOS;
- return bodyp-1;
- }
- } /* End of body with token */
- *tokenp = EOS;
- return bodyp-1;
- }
- else if(c == '#') {
- if (*bodyp == '#') { /* token catenation */
- *modifier = mod_concat;
- bodyp++;
- while(isspace(*bodyp)) bodyp++;
- } else {
- if (p != EOS) putchar(p);
- if (isalnum(*bodyp)) { /* token quoting */
- *modifier = mod_quote;
- } else if(*bodyp == '~') { /* Special hack #~ for unquote */
- bodyp++;
- *modifier = mod_unquote;
- } else {
- putchar(c);
- }
- }
- } else {
- if (*bodyp == '#')
- p = c;
- else {
- putchar(c);
- p = EOS;
- }
- }
- }
- *tokenp = EOS;
- return bodyp-1; /* End of body no token */
- }
- /*
- * Write body substituting args for values
- */
- void
- macro_substitute(body, nparm, parnames, parvalues)
- char* body;
- int nparm;
- char** parnames;
- char** parvalues;
- {
- Modifier modifier;
- char tokenbuf[BSIZE];
- int i;
- Boolean prev_match = FALSE;
- do {
- body = get_token(body, tokenbuf, &modifier);
- for(i=nparm; i--;) {
- if(!strcmp(tokenbuf, parnames[i])) {
- char* val = parvalues[i];
- prev_match = TRUE;
- switch (modifier) {
- case mod_quote:
- put_string(val);
- break;
- case mod_unquote:
- { char c;
- val++;
- while((c=*val++) != EOS)
- if(*val == EOS) break;
- else if(!(c == '\\' && *val == '"'))
- putchar(c);
- }
- break;
- default:
- puts(val);
- break;
- }
- goto next;
- }
- }
- switch (modifier) {
- case mod_concat:
- if (prev_match) break;
- putchar('#');
- case mod_quote: putchar('#'); break;
- case mod_unquote: puts("#~"); break;
- }
- puts(tokenbuf);
- prev_match = FALSE;
- next:
- ;
- }
- while (*body != EOS);
- }
- /*
- * Expand a macro defined by define_macro
- */
- expand_macro(argc, argv)
- int argc;
- char* argv[];
- {
- Mac_Def* mac = (Mac_Def*) argv[1];
- char c;
- Arg* call_argp;
- Arg* def_argp = mac->args;
- Arg* args = NULL;
- Arg* argp;
- char* bodyarg = NULL;
- int nparm = 0;
- char* parnames[MAXPARM];
- char* parvalues[MAXPARM];
- char junk[BSIZE];
- int n;
- char nstr[4];
-
- if(copytoken(junk) == NULL) /* Skip macro name */
- return(1);
-
- if(def_argp && def_argp->type != p_body) {
- if((args = macro_args(mac->name)) == &arg_error) { /* Gather arguments */
- fprintf(stderr, " in %s\n", junk);
- return(1);
- }
- }
- call_argp = args; /* Match calling and defining args */
- for (; def_argp != NULL; def_argp = def_argp->next) {
- switch (def_argp->type) {
- case p_positional:
- parnames[nparm] = def_argp->name;
- if(call_argp != NULL) {
- parvalues[nparm] = (*call_argp->value != EOS) ?
- call_argp->value : call_argp->name;
- call_argp = call_argp ->next;
- } else if (*def_argp->value != EOS)
- parvalues[nparm] = def_argp->value;
- else {
- fprintf(stderr, "%s: The required argument %s is missing\n",
- mac->name, def_argp->name);
- return 1;
- }
- break;
- case p_rest:
- parnames[nparm] = def_argp->name;
- *junk = EOS;
- n = 0;
- for(;call_argp != NULL; call_argp = call_argp->next) {
- strcat(junk, call_argp->name);
- if (*call_argp->value != EOS) {
- strcat(junk, "=");
- strcat(junk, call_argp->value);
- }
- if (call_argp->next)
- strcat(junk, ", ");
- n++;
- }
- parvalues[nparm] = junk;
- /* If rest arg has a default value */
- if (*def_argp->value != EOS) { /* Use it as the name of a parameter */
- sprintf(nstr, "%d", n); /* which holds the number of rest args */
- nparm++;
- parnames[nparm] = def_argp->value;
- parvalues[nparm] = nstr;
- }
- break;
- case p_key:
- parnames[nparm] = def_argp->name;
- parvalues[nparm] = def_argp->value;
- { Arg* argp = call_argp;
- for(;argp != NULL; argp = argp->next) {
- if(*argp->name == EOS) {
- fprintf(stderr, "%s: Keyword missing for %s\n",
- mac->name, argp->value);
- return 1;
- }
- if(!strcmp(argp->name, def_argp->name)) {
- parvalues[nparm] = argp->value;
- argp->type = p_key; /* Munge type to indicate used */
- break;
- }
- }
- }
- if (*parvalues[nparm] == EOS) {
- fprintf(stderr, "%s: The required keyword argument %s is missing\n",
- mac->name, def_argp->name);
- return 1;
- }
- break;
- case p_body:
- c = skip_blanks();
- if (c != '{') {
- fprintf(stderr, "%s: body argument not found\n", mac->name);
- do { putchar(c); } /* error recovery */
- while ((c=getchar()) != EOF);
- return 1;
- }
- unget();
- bodyarg = scan_next('}'); /* Grab the macro body */
- {char* bp = bodyarg + strlen(bodyarg) - 1;
- *bp = EOS;} /* Strip {} from body */
- parnames[nparm] = def_argp->name;
- parvalues[nparm] = bodyarg+1;
- break;
- }
- nparm++;
- }
- if (call_argp != NULL) { /* Check for unused keyargs */
- for(argp = call_argp; argp != NULL; argp = argp->next)
- if (argp->type != p_key) {
- fprintf(stderr, "%s: Unknown argument: %s\n",
- mac->name, argp->name);
- return 1;
- }
- }
- /* Substitute args for values */
- macro_substitute(mac->body, nparm, parnames, parvalues);
- for(argp = args; argp != NULL;) {
- Arg* old = argp;
- argp = argp->next;
- free(old->name);
- if (*old->value) free(old->value);
- free(old);
- }
- return 0;
- }
- /*
- * Define a new macro
- */
- int
- define_macro(argc, argv)
- int argc;
- char* argv[];
- {
- char* name;
- Arg* args;
- char* body;
- Mac_Def* mac;
- char expanding = 0;
-
- { char macname[BSIZE];
- if(copytoken(macname) == NULL) /* Skip macro name */
- return(1);
- if(copytoken(macname) == NULL) /* Copy the macro name */
- return(1);
- if(!strcmp(macname, "EXPANDING")) {
- expanding = 1;
- if(copytoken(macname) == NULL) /* Copy the macro name */
- return(1);
- }
- name = savestring(macname);
- }
- if((args = macro_args("MACRO")) == &arg_error) { /* Gather arguments */
- fprintf(stderr, " in %s\n", name);
- return(1);
- }
- skip_blanks();
- unget();
- body = savestring(scan_next('}')); /* Grab the macro body */
- { char* bp;
- while(*body++ != '{'); /* strip start of body */
- bp = body + strlen(body); /* Strip end of body */
- *--bp = EOS;
- }
- mac = (Mac_Def*) getmem(sizeof(Mac_Def)); /* Create new macro structure */
- mac->name = name;
- mac->args = args;
- mac->body = body;
- { char delim = ')';
- Boolean recursive = FALSE;
- char* arglist[3];
- arglist[0] = name;
- arglist[1] = (char*) mac;
- arglist[2] = NULL;
- if (args != NULL) {
- Arg* a = args;
- while(a->next!=NULL) a = a->next; /* Find last parameter */
- if(a->type == p_body) delim = '}';
- if(a->type == p_rest || a->type == p_body) recursive = TRUE;
- }
- new_defmacro(name, expanding, recursive, delim, EOS,
- expand_macro, name, arglist);
- }
- return 0;
- }
-