123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800 |
- /*
- * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
- * See the copyright notice in the ACK home directory, in the file "Copyright".
- */
- /* $Id$ */
- /* M A C R O R E P L A C E M E N T */
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
- #include "pathlength.h"
- #include "strsize.h"
- #include "nparams.h"
- #include "idfsize.h"
- #include "numsize.h"
- #include "alloc.h"
- #include "idf.h"
- #include "input.h"
- #include "macro.h"
- #include "arith.h"
- #include "LLlex.h"
- #include "class.h"
- #include "debug.h"
- #include "assert.h"
- #include "macbuf.h"
- #include "replace.h"
- extern char *GetIdentifier();
- extern char *strcpy();
- extern char *strcat();
- extern int InputLevel;
- struct repl *ReplaceList; /* list of currently active macros */
- int
- replace(idf)
- register struct idf *idf;
- {
- /* replace is called by the lexical analyzer to perform
- macro replacement. The routine actualy functions as a
- higher interface to the real thing: expand_macro().
- */
- struct repl *repl;
- if (!(idf->id_macro)) return 0;
- if (idf->id_macro->mc_flag & NOREPLACE)
- return 0;
- repl = new_repl();
- repl->r_ptr = repl->r_text = Malloc((unsigned)(repl->r_size = LAPBUF));
- repl->r_args = new_args();
- repl->r_idf = idf;
- if (!expand_macro(repl, idf))
- return 0;
- InputLevel++;
- InsertText(repl->r_text, (int)(repl->r_ptr - repl->r_text));
- idf->id_macro->mc_flag |= NOREPLACE;
- repl->r_level = InputLevel;
- repl->next = ReplaceList;
- ReplaceList = repl;
- return 1;
- }
- unstackrepl()
- {
- Unstacked++;
- }
- freeargs(args)
- struct args *args;
- {
- register int i;
- /* We must don't know how many parameters were specified, so be
- * prepared to free all NPARAMS parameters.
- * When an expvec is !NULL, the rawvec will also be !NULL.
- * When an expvec is NULL, all remaining vectors will also be NULL.
- */
- for (i = 0; i < NPARAMS; i++) {
- if (args->a_expvec[i]) {
- free(args->a_expvec[i]);
- free(args->a_rawvec[i]);
- } else break;
- }
- free_args(args);
- }
- EnableMacros()
- {
- register struct repl *r = ReplaceList, *prev = 0;
- assert(Unstacked > 0);
- while(r) {
- struct repl *nxt = r->next;
- if (r->r_level > InputLevel) {
- r->r_idf->id_macro->mc_flag &= ~NOREPLACE;
- if (!prev) ReplaceList = nxt;
- else prev->next = nxt;
- free(r->r_text);
- freeargs(r->r_args);
- free_repl(r);
- }
- else prev = r;
- r = nxt;
- }
- Unstacked = 0;
- }
- expand_macro(repl, idf)
- register struct repl *repl;
- register struct idf *idf;
- {
- /* expand_macro() does the actual macro replacement.
- "idf" is a description of the identifier which
- caused the replacement.
- If the identifier represents a function-like macro
- call, the number of actual parameters is checked
- against the number of formal parameters. Note that
- in ANSI C the parameters are expanded first;
- this is done by calling getactuals().
- When the possible parameters are expanded, the replace-
- ment list associated with "idf" is expanded.
- expand_macro() returns 1 if the replacement succeeded
- and 0 if some error occurred.
- A special case is "defined". This acts as a unary operator
- on a single, unexpanded identifier, which may be surrounded
- by parenthesis. The function expand_defined() handles this.
- */
- register struct macro *mac = idf->id_macro;
- struct args *args = repl->r_args;
- register int ch;
- if (mac->mc_nps != -1) { /* with parameter list */
- if (mac->mc_flag & FUNC) {
- /* the following assertion won't compile:
- assert(!strcmp("defined", idf->id_text));
- */
- if (!AccDefined) return 0;
- expand_defined(repl);
- return 1;
- }
- ch = GetChar();
- ch = skipspaces(ch,1);
- if (ch != '(') { /* no replacement if no () */
- ChPushBack(ch);
- return 0;
- } else
- getactuals(repl, idf);
- }
- if (mac->mc_flag & FUNC) /* this macro leads to special action */
- macro_func(idf);
- macro2buffer(repl, idf, args);
- /* According to the ANSI definition:
- #define a +
- a+b; --> + + b ;
- 'a' must be substituded, but the result should be
- three tokens: + + ID. Therefore a token separator is
- inserted after the replacement.
- */
- if (repl->r_text == repl->r_ptr || *(repl->r_ptr -1) != TOKSEP) {
- add2repl(repl, TOKSEP);
- }
- return 1;
- }
- expand_defined(repl)
- register struct repl *repl;
- {
- register int ch = GetChar();
- struct idf *id;
- char *str;
- int parens = 0;
- ch = skipspaces(ch, 0);
- if (ch == '(') {
- parens++;
- ch = GetChar();
- ch = skipspaces(ch, 0);
- }
- if ((class(ch) != STIDF) && (class(ch) != STELL)) {
- error("identifier missing");
- if (parens && ch != ')') error(") missing");
- if (!parens || ch != ')') ChPushBack(ch);
- add2repl(repl,'0');
- return;
- }
- ChPushBack(ch);
- str = GetIdentifier(0);
- if (str) {
- id = findidf(str);
- free(str);
- } else id = 0;
- ch = GetChar();
- ch = skipspaces(ch, 0);
- if (parens && ch != ')') error(") missing");
- if (!parens || ch != ')') ChPushBack(ch);
- add2repl(repl, (id && id->id_macro) ? '1' : '0');
- add2repl(repl, ' ');
- }
- newarg(args)
- struct args *args;
- {
- args->a_expptr = args->a_expbuf = Malloc((unsigned)(args->a_expsize = ARGBUF));
- args->a_rawptr = args->a_rawbuf = Malloc((unsigned)(args->a_rawsize = ARGBUF));
- }
- getactuals(repl, idf)
- struct repl *repl;
- register struct idf *idf;
- {
- /* Get the actual parameters from the input stream.
- The hard part is done by actual(), only comma's and
- other syntactic trivialities are checked here.
- */
- register struct args *args = repl->r_args;
- register int nps = idf->id_macro->mc_nps;
- register int argcnt;
- register int ch;
- argcnt = 0;
- newarg(args);
- if ((ch = GetChar()) != ')') {
- UnGetChar();
- while ((ch = actual(repl)) != ')' ) {
- if (ch != ',') {
- error("illegal macro call");
- return;
- }
- stash(repl, '\0', 1);
- args->a_expvec[argcnt] = args->a_expbuf;
- args->a_rawvec[argcnt] = args->a_rawbuf;
- ++argcnt;
- if (argcnt == STDC_NPARAMS)
- strict("number of parameters exceeds ANSI standard");
- if (argcnt >= NPARAMS)
- fatal("argument vector overflow");
- newarg(args);
- }
- stash(repl, '\0', 1);
- args->a_expvec[argcnt] = args->a_expbuf;
- args->a_rawvec[argcnt] = args->a_rawbuf;
- ++argcnt;
- }
- if (argcnt < nps)
- error("too few macro arguments");
- else if (argcnt > nps)
- error("too many macro arguments");
- }
- saveraw(repl)
- struct repl *repl;
- {
- register struct repl *nrepl = ReplaceList;
- register struct args *ap = nrepl->r_args;
- register char *p;
- /* stash identifier name */
- for (p = nrepl->r_idf->id_text; *p != '\0'; p++)
- stash(repl, *p, -1);
- /* The following code deals with expanded function
- like macro calls. It makes the following code
- work:
- #define def(a,b) x(a,b)
- #define glue(a,b) a ## b
- glue(abc,def(a,b))
- Results in:
- abcdef(a,b);
- */
- if (ap->a_rawvec[0]) {
- /* stash arguments */
- register int i;
- for (i = 0; ap->a_rawvec[i] != (char *)0; i++) {
- if (i == 0) stash(repl, '(', -1);
- else stash(repl, ',', -1);
- for (p = ap->a_rawvec[i]; *p != '\0'; p++)
- stash(repl, *p, -1);
- }
- stash(repl, ')', -1);
- }
- }
- int
- actual(repl)
- struct repl *repl;
- {
- /* This routine deals with the scanning of an actual parameter.
- It keeps in account the opening and closing brackets,
- preprocessor numbers, strings and character constants.
- */
- register int ch = 0;
- register int level = 0, nostashraw = 0;
- int lastch;
- static int Unstacked_missed;
- while (1) {
- lastch = ch;
- ch = GetChar();
- if (nostashraw
- && nostashraw >= Unstacked_missed) {
- nostashraw -= Unstacked_missed;
- Unstacked_missed = 0;
- }
- if (Unstacked) {
- nostashraw -= Unstacked;
- if (nostashraw < 0) {
- Unstacked_missed = -nostashraw;
- nostashraw = 0;
- }
- EnableMacros();
- }
- if (class(ch) == STIDF || class(ch) == STELL) {
- /* Scan a preprocessor identifier token. If the
- token is a macro, it is expanded first.
- */
- char buf[(IDFSIZE > NUMSIZE ? IDFSIZE : NUMSIZE) + 1];
- register char *p = buf;
- register struct idf *idef;
- register int pos = -1;
- extern int idfsize;
- int NoExpandMacro;
- if (ch == NOEXPM) {
- NoExpandMacro= 1;
- ch = GetChar();
- } else NoExpandMacro = 0;
- do {
- if (++pos < idfsize) {
- *p++ = ch;
- }
- ch = GetChar();
- } while (in_idf(ch));
- *p++ = '\0';
- ch = '\0'; /* Could be a non-stashed TOKSEP */
- UnGetChar();
- /* When the identifier has an associated macro
- replacement list, it's expanded.
- */
- idef = findidf(buf);
- if (!idef || NoExpandMacro || !replace(idef)) {
- if (NoExpandMacro
- || (idef && idef->id_macro
- && (idef->id_macro->mc_flag & NOREPLACE)))
- stash(repl, NOEXPM, !nostashraw);
- for (p = buf; *p != '\0'; p++)
- stash(repl, *p, !nostashraw);
- } else {
- if (!nostashraw) saveraw(repl);
- nostashraw++;
- }
- } else if (class(ch) == STNUM) {
- /* a preprocessing number has the following
- regular expression:
- [0-9|"."[0-9]]{[0-9"."a-zA-Z_]|{[Ee][+-]}}*
- */
- stash(repl, ch, !nostashraw);
- if (ch == '.') {
- ch = GetChar();
- if (class(ch) != STNUM) {
- ch = '\0'; /* Could be a non-stashed TOKSEP */
- UnGetChar();
- continue;
- }
- else stash(repl, ch, !nostashraw);
- }
- ch = GetChar();
- while (in_idf(ch) || ch == '.') {
- stash(repl, ch, !nostashraw);
- if ((ch = GetChar()) == 'e' || ch == 'E') {
- stash(repl, ch, !nostashraw);
- ch = GetChar();
- if (ch == '+' || ch == '-') {
- stash(repl, ch, !nostashraw);
- ch = GetChar();
- }
- }
- }
- ch = '\0'; /* Could be a non-stashed TOKSEP */
- UnGetChar();
- } else if (ch == '(') {
- /* a comma may occur within parentheses */
- level++;
- stash(repl, ch, !nostashraw);
- } else if (ch == ')') {
- level--;
- /* test on closing parenthesis of macro call */
- if (level < 0) return ')';
- stash(repl, ch, !nostashraw);
- } else if (ch == ',') {
- if (level <= 0) { /* comma separator for next argument */
- if (level)
- error("unbalanced parenthesis");
- if (!nostashraw)
- return ','; /* ??? */
- }
- stash(repl, ch, !nostashraw);
- } else if (ch == '\n') {
- /* newlines are accepted as white spaces */
- LineNumber++;
- /* This piece of code needs some explanation:
- consider the call of a macro defined as:
- #define sum(a,b) (a+b)
- in the following form:
- sum(
- /_* comment *_/ #include phone_number
- ,2);
- in which case the include must be handled
- interpreted as such.
- */
- a_new_line: ch = GetChar();
- while (class(ch) == STSKIP || ch == '/') {
- if (ch == '/') {
- if ((ch = GetChar()) == '*' && !InputLevel) {
- skipcomment();
- stash(repl, ' ', !nostashraw);
- ch = GetChar();
- continue;
- } else {
- UnGetChar();
- ch = '/';
- }
- stash(repl, '/', !nostashraw);
- break;
- } else ch = GetChar();
- }
- if (ch == '#') {
- domacro();
- goto a_new_line;
- } else if (ch == EOI) {
- error("unterminated macro call");
- return ')';
- }
- if (ch != '/') {
- UnGetChar();
- ch = ' ';
- stash(repl, ' ', !nostashraw);
- }
- } else if (ch == '/') {
- /* comments are treated as one white space token */
- if ((ch = GetChar()) == '*' && !InputLevel) {
- skipcomment();
- stash(repl, ' ', !nostashraw);
- } else {
- UnGetChar();
- ch = '/';
- stash(repl, '/', !nostashraw);
- }
- } else if (ch == '\'' || ch == '"') {
- /* Strings are considered as ONE token, thus no
- replacement within strings.
- */
- register int match = ch;
- stash(repl, ch, !nostashraw);
- while ((ch = GetChar()) != EOI) {
- if (ch == match)
- break;
- if (ch == '\\') {
- stash(repl, ch, !nostashraw);
- ch = GetChar();
- } else if (ch == '\n') {
- error("newline in string");
- LineNumber++;
- stash(repl, match, !nostashraw);
- break;
- }
- stash(repl, ch, !nostashraw);
- }
- if (ch != match) {
- error("unterminated macro call");
- return ')';
- }
- stash(repl, ch, !nostashraw);
- } else {
- if (lastch == TOKSEP && ch == TOKSEP) continue;
- stash(repl, ch, !nostashraw);
- }
- }
- }
- macro_func(idef)
- register struct idf *idef;
- {
- /* macro_func() performs the special actions needed with some
- macros. These macros are __FILE__ and __LINE__ which
- replacement texts must be evaluated at the time they are
- used.
- */
- register struct macro *mac = idef->id_macro;
- static char FilNamBuf[PATHLENGTH];
- char *long2str();
- switch (idef->id_text[2]) {
- case 'F': /* __FILE__ */
- FilNamBuf[0] = '"';
- strcpy(&FilNamBuf[1], FileName);
- strcat(FilNamBuf, "\"");
- mac->mc_text = FilNamBuf;
- mac->mc_length = strlen(FilNamBuf);
- break;
- case 'L': /* __LINE__ */
- mac->mc_text = long2str((long)LineNumber, 10);
- mac->mc_length = strlen(mac->mc_text);
- break;
- default:
- crash("(macro_func)");
- /*NOTREACHED*/
- }
- }
- macro2buffer(repl, idf, args)
- register struct repl *repl;
- register struct idf *idf;
- register struct args *args;
- {
- /* macro2buffer expands the replacement list and places the
- result onto the replacement buffer. It deals with the #
- and ## operators, and inserts the actual parameters.
- The argument buffer contains the raw argument (needed
- for the # and ## operators), and the expanded argument
- (for all other parameter substitutions).
- The grammar of the replacement list is:
- repl_list: TOKEN repl_list
- | PARAMETER repl_list
- | '#' PARAMETER
- | TOKEN '##' TOKEN
- | PARAMETER '##' TOKEN
- | TOKEN '##' PARAMETER
- | PARAMETER '##' PARAMETER
- ;
- As the grammar indicates, we could make a DFA and
- use this finite state machine for the replacement
- list parsing (inserting the arguments, etc.).
- Currently we go through the replacement list in a
- linear fashion. This is VERY expensive, something
- smarter should be done (but even a DFA is O(|s|)).
- */
- register char *ptr = idf->id_macro->mc_text;
- int err = 0;
- int func = idf->id_macro->mc_nps != -1;
- char *stringify();
- assert(ptr[idf->id_macro->mc_length] == '\0');
- while (*ptr) {
- if (*ptr == '\'' || *ptr == '"') {
- register int delim = *ptr;
- do {
- add2repl(repl, *ptr);
- if (*ptr == '\\')
- add2repl(repl, *++ptr);
- if (*ptr == '\0') {
- error("unterminated string");
- return;
- }
- ptr++;
- } while (*ptr != delim || *ptr == '\0');
- add2repl(repl, *ptr++);
- } else if (*ptr == '#' && (func || *(ptr+1) == '#')) {
- if (*++ptr == '#') {
- register int tmpindex;
- /* ## - paste operator */
- ptr++;
- /* trim the actual replacement list */
- --repl->r_ptr;
- while (repl->r_ptr >= repl->r_text
- && is_wsp(*repl->r_ptr))
- --repl->r_ptr;
- /* ## occurred at the beginning of the replacement list.
- */
- if (repl->r_ptr < repl->r_text) {
- err = 1;
- break;
- }
- if (repl->r_ptr >= repl->r_text
- && *repl->r_ptr == TOKSEP)
- --repl->r_ptr;
- ++repl->r_ptr;
- tmpindex = repl->r_ptr - repl->r_text;
- /* tmpindex can be 0 */
- /* skip space in macro replacement list */
- while ((*ptr & FORMALP) == 0 && is_wsp(*ptr))
- ptr++;
- /* ## occurred at the end of the replacement list.
- */
- if (*ptr & FORMALP) {
- register int n = *ptr++ & 0177;
- register char *p;
- assert(n > 0);
- p = args->a_rawvec[n-1];
- if (p) { /* else macro argument missing */
- while (is_wsp(*p)) p++;
- if (*p == NOEXPM) p++;
- while (*p)
- add2repl(repl, *p++);
- }
- while (tmpindex > 0
- && in_idf(repl->r_text[tmpindex]))
- tmpindex--;
- if (tmpindex >= 0
- && repl->r_text[tmpindex] == NOEXPM)
- repl->r_text[tmpindex] = TOKSEP;
- } else if (*ptr == '\0') {
- err = 1;
- break;
- } else {
- if (in_idf(*ptr)) {
- tmpindex--;
- while (tmpindex > 0
- && in_idf(repl->r_text[tmpindex]))
- tmpindex--;
- if (tmpindex >= 0
- && repl->r_text[tmpindex] == NOEXPM)
- repl->r_text[tmpindex] = TOKSEP;
- }
- }
- } else { /* # operator */
- ptr = stringify(repl, ptr, args);
- }
- } else if (*ptr & FORMALP) {
- /* insert actual parameter */
- register int n = *ptr++ & 0177;
- register char *p, *q;
- assert(n > 0);
- /* This is VERY dirty, we look ahead for the
- ## operator. If it's found we use the raw
- argument buffer instead of the expanded
- one.
- */
- for (p = ptr; (*p & FORMALP) == 0 && is_wsp(*p); p++)
- /* EMPTY */;
- if (*p == '#' && p[1] == '#')
- q = args->a_rawvec[n-1];
- else
- q = args->a_expvec[n-1];
- if (q) /* else macro argument missing */
- while (*q)
- add2repl(repl, *q++);
- if (repl->r_text == repl->r_ptr || *(repl->r_ptr-1) != TOKSEP)
- add2repl(repl, TOKSEP);
- } else {
- add2repl(repl, *ptr++);
- }
- }
- if (err)
- error("illegal use of ## operator");
- }
- char *
- stringify(repl, ptr, args)
- register struct repl *repl;
- register char *ptr;
- register struct args *args;
- {
- /* If a parameter is immediately preceded by a # token
- both are replaced by a single string literal that
- contains the spelling of the token sequence for the
- corresponding argument.
- Each occurrence of white space between the argument's
- tokens become a single space character in the string
- literal. White spaces before the first token and after
- the last token comprising the argument are deleted.
- To retain the original spelling we insert backslashes
- as appropriate. We only escape backslashes if they
- occure within string tokens.
- */
- register int space = 1; /* skip leading spaces */
- register int delim = 0; /* string or character constant delim */
- register int backslash = 0; /* last character was a \ */
- /* skip spaces macro replacement list */
- while ((*ptr & FORMALP) == 0 && is_wsp(*ptr))
- ptr++;
- if (*ptr & FORMALP) {
- register int n = *ptr++ & 0177;
- register char *p;
- assert(n != 0);
- p = args->a_rawvec[n-1];
- add2repl(repl, '"');
- while (*p) {
- if (is_wsp(*p)) {
- if (!space) {
- space = 1;
- add2repl(repl, ' ');
- }
- p++;
- continue;
- }
- space = 0;
- if (!delim && (*p == '"' || *p == '\''))
- delim = *p;
- else if (*p == delim && !backslash)
- delim = 0;
- backslash = *p == '\\';
- if (*p == '"' || (delim && *p == '\\'))
- add2repl(repl, '\\');
- if (*p == TOKSEP || *p == NOEXPM) p++;
- else add2repl(repl, *p++);
- }
- /* trim spaces in the replacement list */
- for (--repl->r_ptr; is_wsp(*repl->r_ptr); repl->r_ptr--)
- /* EMPTY */;
- ++repl->r_ptr; /* oops, one to far */
- add2repl(repl, '"');
- } else
- error("illegal use of # operator");
- return ptr;
- }
- /* The following routine is also called from domacro.c.
- */
- add2repl(repl, ch)
- register struct repl *repl;
- int ch;
- {
- register int index = repl->r_ptr - repl->r_text;
-
- assert(index < repl->r_size);
- if (index + 2 >= repl->r_size) {
- repl->r_text = Realloc(repl->r_text, (unsigned)(repl->r_size <<= 1));
- repl->r_ptr = repl->r_text + index;
- }
- *repl->r_ptr++ = ch;
- *repl->r_ptr = '\0';
- }
- /* If the variable stashraw is negative, we must only stash into the raw
- * buffer. If the variable is zero, we must only stash into the expanded
- * buffer. Otherwise, we must use both buffers.
- */
- stash(repl, ch, stashraw)
- struct repl *repl;
- register int ch;
- int stashraw;
- {
- /* Stash characters into the macro expansion buffer.
- */
- register struct args *args = repl->r_args;
- register int index = args->a_expptr - args->a_expbuf;
-
- if (stashraw >= 0) {
- assert(index < args->a_expsize);
- if (index + 1 >= args->a_expsize) {
- args->a_expbuf = Realloc(args->a_expbuf,
- (unsigned)(args->a_expsize <<= 1));
- args->a_expptr = args->a_expbuf + index;
- }
- *args->a_expptr++ = ch;
- }
-
- if (stashraw) {
- index = args->a_rawptr - args->a_rawbuf;
- assert(index < args->a_rawsize);
- if (index + 1 >= args->a_rawsize) {
- args->a_rawbuf = Realloc(args->a_rawbuf,
- (unsigned)(args->a_rawsize <<= 1));
- args->a_rawptr = args->a_rawbuf + index;
- }
- *args->a_rawptr++ = ch;
- }
- }
|