123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811 |
- /*
- * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
- * See the copyright notice in the ACK home directory, in the file "Copyright".
- */
- /* $Id$ */
- /* PREPROCESSOR: CONTROLLINE INTERPRETER */
- #include "arith.h"
- #include "LLlex.h"
- #include "Lpars.h"
- #include "idf.h"
- #include "input.h"
- #include "ifdepth.h"
- #include "botch_free.h"
- #include "nparams.h"
- #include "parbufsize.h"
- #include "textsize.h"
- #include "idfsize.h"
- #include "debug.h"
- #include <assert.h>
- #include <alloc.h>
- #include "class.h"
- #include "macro.h"
- #include "bits.h"
- #include "macbuf.h"
- #include "replace.h"
- extern char options[];
- extern char **inctable; /* list of include directories */
- extern char *getwdir();
- char ifstack[IFDEPTH]; /* if-stack: the content of an entry is */
- /* 1 if a corresponding ELSE has been */
- /* encountered. */
- int nestlevel = -1;
- int svnestlevel[30] = {-1};
- int nestcount;
- extern int do_preprocess;
- char *
- GetIdentifier(skiponerr)
- int skiponerr; /* skip the rest of the line on error */
- {
- /* Returns a pointer to the identifier that is read from the
- input stream. When the input does not contain an
- identifier, the rest of the line is skipped when skiponerr
- is on, and a null-pointer is returned.
- The substitution of macros is disabled.
- Remember that on end-of-line EOF is returned.
- */
- int tmp = UnknownIdIsZero;
- int tok;
- struct token tk;
- UnknownIdIsZero = ReplaceMacros = 0;
- tok = GetToken(&tk);
- ReplaceMacros = 1;
- UnknownIdIsZero = tmp;
- if (tok != IDENTIFIER) {
- if (skiponerr && tok != EOF) SkipToNewLine();
- return (char *)0;
- }
- return tk.tk_str;
- }
- /* domacro() is the control line interpreter. The '#' has already
- been read by the lexical analyzer by which domacro() is called.
- The token appearing directly after the '#' is obtained by calling
- the basic lexical analyzing function GetToken() and is interpreted
- to perform the action belonging to that token.
- An error message is produced when the token is not recognized.
- Pragma's are handled by do_pragma(). They are passed on to the
- compiler.
- */
- domacro()
- {
- struct token tk; /* the token itself */
- register struct idf *id;
- int toknum;
- ReplaceMacros = 0;
- toknum = GetToken(&tk);
- ReplaceMacros = 1;
- switch(toknum) { /* select control line action */
- case IDENTIFIER: /* is it a macro keyword? */
- id = findidf(tk.tk_str);
- if (!id) {
- error("%s: unknown control", tk.tk_str);
- SkipToNewLine();
- free(tk.tk_str);
- break;
- }
- free(tk.tk_str);
- switch (id->id_resmac) {
- case K_DEFINE: /* "define" */
- do_define();
- break;
- case K_ELIF: /* "elif" */
- do_elif();
- break;
- case K_ELSE: /* "else" */
- do_else();
- break;
- case K_ENDIF: /* "endif" */
- do_endif();
- break;
- case K_IF: /* "if" */
- do_if();
- break;
- case K_IFDEF: /* "ifdef" */
- do_ifdef(1);
- break;
- case K_IFNDEF: /* "ifndef" */
- do_ifdef(0);
- break;
- case K_INCLUDE: /* "include" */
- do_include();
- break;
- case K_LINE: /* "line" */
- /* set LineNumber and FileName according to
- the arguments.
- */
- if (GetToken(&tk) != INTEGER) {
- error("bad #line syntax");
- SkipToNewLine();
- }
- else
- do_line((unsigned int)tk.tk_val);
- break;
- case K_ERROR: /* "error" */
- do_error();
- break;
- case K_PRAGMA: /* "pragma" */
- do_pragma();
- break;
- case K_UNDEF: /* "undef" */
- do_undef((char *)0);
- break;
- default:
- /* invalid word seen after the '#' */
- error("%s: unknown control", id->id_text);
- SkipToNewLine();
- }
- break;
- case INTEGER: /* # <integer> [<filespecifier>]? */
- do_line((unsigned int)tk.tk_val);
- break;
- case EOF: /* only `#' on this line: do nothing, ignore */
- break;
- default: /* invalid token following '#' */
- error("illegal # line");
- SkipToNewLine();
- }
- }
- skip_block(to_endif)
- int to_endif;
- {
- /* skip_block() skips the input from
- 1) a false #if, #ifdef, #ifndef or #elif until the
- corresponding #elif (resulting in true), #else or
- #endif is read.
- 2) a #else corresponding to a true #if, #ifdef,
- #ifndef or #elif until the corresponding #endif is
- seen.
- */
- register int ch;
- register int skiplevel = nestlevel; /* current nesting level */
- struct token tk;
- int toknum;
- struct idf *id;
- NoUnstack++;
- for (;;) {
- ch = GetChar(); /* read first character after newline */
- while (class(ch) == STSKIP)
- ch = GetChar();
- if (ch != '#') {
- if (ch == EOI) {
- NoUnstack--;
- return;
- }
- if (ch == '/') {
- if (ch != '*') UnGetChar();
- else {
- skipcomment();
- continue;
- }
- } else UnGetChar();
- SkipToNewLine();
- continue;
- }
- ReplaceMacros = 0;
- toknum = GetToken(&tk);
- ReplaceMacros = 1;
- if (toknum != IDENTIFIER) {
- if (toknum != INTEGER) {
- error("illegal # line");
- }
- SkipToNewLine();
- continue;
- }
- /* an IDENTIFIER: look for #if, #ifdef and #ifndef
- without interpreting them.
- Interpret #else, #elif and #endif if they occur
- on the same level.
- */
- id = findidf(tk.tk_str);
- if (id == (struct idf *)0) {
- /* invalid word seen after the '#' */
- warning("%s: unknown control", tk.tk_str);
- }
- free(tk.tk_str);
- if (id == (struct idf *)0) continue;
- switch(id->id_resmac) {
- case K_DEFINE:
- case K_ERROR:
- case K_INCLUDE:
- case K_LINE:
- case K_PRAGMA:
- case K_UNDEF:
- case K_FILE:
- SkipToNewLine();
- break;
- case K_IF:
- case K_IFDEF:
- case K_IFNDEF:
- push_if();
- SkipToNewLine();
- break;
- case K_ELIF:
- if (ifstack[nestlevel])
- error("#elif after #else");
- if (!to_endif && nestlevel == skiplevel) {
- nestlevel--;
- push_if();
- if (ifexpr()) {
- NoUnstack--;
- return;
- }
- }
- else SkipToNewLine(); /* otherwise done in ifexpr() */
- break;
- case K_ELSE:
- if (ifstack[nestlevel])
- error("#else after #else");
- ++(ifstack[nestlevel]);
- if (!to_endif && nestlevel == skiplevel) {
- if (SkipToNewLine()) {
- if (!options['o'])
- strict("garbage following #else");
- }
- NoUnstack--;
- return;
- }
- else SkipToNewLine();
- break;
- case K_ENDIF:
- assert(nestlevel > svnestlevel[nestcount]);
- if (nestlevel == skiplevel) {
- if (SkipToNewLine()) {
- if (!options['o'])
- strict("garbage following #endif");
- }
- nestlevel--;
- NoUnstack--;
- return;
- }
- else SkipToNewLine();
- nestlevel--;
- break;
- }
- }
- }
- ifexpr()
- {
- /* ifexpr() returns whether the restricted constant
- expression following #if or #elif evaluates to true. This
- is done by calling the LLgen generated subparser for
- constant expressions. The result of this expression will
- be given in the extern long variable "ifval".
- */
- extern arith ifval;
- int errors = err_occurred;
- ifval = (arith)0;
- AccDefined = 1;
- UnknownIdIsZero = 1;
- DOT = 0; /* tricky */
- If_expr(); /* invoke constant expression parser */
- AccDefined = 0;
- UnknownIdIsZero = 0;
- return (errors == err_occurred) && (ifval != (arith)0);
- }
- do_include()
- {
- /* do_include() performs the inclusion of a file.
- */
- char *filenm;
- char *result;
- int tok;
- struct token tk;
- AccFileSpecifier = 1;
- if (((tok = GetToken(&tk)) == FILESPECIFIER) || tok == STRING)
- filenm = tk.tk_str;
- else {
- error("bad include syntax");
- filenm = (char *)0;
- }
- AccFileSpecifier = 0;
- if (SkipToNewLine()) {
- error("bad include syntax");
- }
- inctable[0] = WorkingDir;
- if (filenm) {
- if (!InsertFile(filenm, &inctable[tok==FILESPECIFIER],&result)){
- if (do_preprocess) error("cannot open include file \"%s\"", filenm);
- else warning("cannot open include file \"%s\"", filenm);
- add_dependency(filenm);
- }
- else {
- add_dependency(result);
- WorkingDir = getwdir(result);
- svnestlevel[++nestcount] = nestlevel;
- FileName = result;
- LineNumber = 1;
- }
- }
- }
- do_define()
- {
- /* do_define() interprets a #define control line.
- */
- register char *str; /* the #defined identifier's descriptor */
- int nformals = -1; /* keep track of the number of formals */
- char *formals[NPARAMS]; /* pointers to the names of the formals */
- char parbuf[PARBUFSIZE]; /* names of formals */
- char *repl_text; /* start of the replacement text */
- int length; /* length of the replacement text */
- register ch;
- char *get_text();
- /* read the #defined macro's name */
- if (!(str = GetIdentifier(1))) {
- error("#define: illegal macro name");
- return;
- }
- /* there is a formal parameter list if the identifier is
- followed immediately by a '('.
- */
- ch = GetChar();
- if (ch == '(') {
- if ((nformals = getparams(formals, parbuf)) == -1) {
- SkipToNewLine();
- free(str);
- return; /* an error occurred */
- }
- ch = GetChar();
- }
- /* read the replacement text if there is any */
- ch = skipspaces(ch,0); /* find first character of the text */
- assert(ch != EOI);
- /* UnGetChar() is not right when replacement starts with a '/' */
- ChPushBack(ch);
- repl_text = get_text((nformals > 0) ? formals : 0, &length);
- macro_def(str2idf(str, 0), repl_text, nformals, length, NOFLAG);
- LineNumber++;
- }
- push_if()
- {
- if (nestlevel >= IFDEPTH)
- fatal("too many nested #if/#ifdef/#ifndef");
- else
- ifstack[++nestlevel] = 0;
- }
- do_elif()
- {
- if (nestlevel <= svnestlevel[nestcount]) {
- error("#elif without corresponding #if");
- SkipToNewLine();
- }
- else { /* restart at this level as if a #if is detected. */
- if (ifstack[nestlevel]) {
- error("#elif after #else");
- SkipToNewLine();
- }
- nestlevel--;
- push_if();
- skip_block(1);
- }
- }
- do_else()
- {
- if (SkipToNewLine()) {
- if (!options['o'])
- strict("garbage following #else");
- }
- if (nestlevel <= svnestlevel[nestcount])
- error("#else without corresponding #if");
- else { /* mark this level as else-d */
- if (ifstack[nestlevel]) {
- error("#else after #else");
- }
- ++(ifstack[nestlevel]);
- skip_block(1);
- }
- }
- do_endif()
- {
- if (SkipToNewLine()) {
- if (!options['o'])
- strict("garbage following #endif");
- }
- if (nestlevel <= svnestlevel[nestcount]) {
- error("#endif without corresponding #if");
- }
- else nestlevel--;
- }
- do_if()
- {
- push_if();
- if (!ifexpr()) /* a false #if/#elif expression */
- skip_block(0);
- }
- do_ifdef(how)
- {
- register struct idf *id;
- register char *str;
- /* how == 1 : ifdef; how == 0 : ifndef
- */
- push_if();
- if (!(str = GetIdentifier(1))) {
- error("illegal #ifdef construction");
- id = (struct idf *)0;
- } else {
- id = findidf(str);
- free(str);
- }
- if (SkipToNewLine()) {
- if (str && !options['o'])
- strict("garbage following #%s <identifier>",
- how ? "ifdef" : "ifndef");
- }
- /* The next test is a shorthand for:
- (how && !id->id_macro) || (!how && id->id_macro)
- */
- if (how ^ (id && id->id_macro != 0))
- skip_block(0);
- }
- /* argstr != NULL when the undef came from a -U option */
- do_undef(argstr)
- char *argstr;
- {
- register struct idf *id;
- register char *str = argstr;
- /* Forget a macro definition. */
- if (str || (str = GetIdentifier(1))) {
- if ((id = findidf(str)) && id->id_macro) {
- if (id->id_macro->mc_flag & NOUNDEF) {
- error("it is not allowed to #undef %s", str);
- } else {
- free(id->id_macro->mc_text);
- free_macro(id->id_macro);
- id->id_macro = (struct macro *) 0;
- }
- } /* else: don't complain */
- if (!argstr){
- free(str);
- if (SkipToNewLine()) {
- if (!options['o'])
- strict("garbage following #undef");
- }
- }
- }
- else
- error("illegal #undef construction");
- }
- do_error()
- {
- int len;
- char *get_text();
- char *bp = get_text((char **) 0, &len);
- error("user error: %s", bp);
- free(bp);
- LineNumber++;
- }
- int
- getparams(buf, parbuf)
- char *buf[];
- char parbuf[];
- {
- /* getparams() reads the formal parameter list of a macro
- definition.
- The number of parameters is returned.
- As a formal parameter list is expected when calling this
- routine, -1 is returned if an error is detected, for
- example:
- #define one(1), where 1 is not an identifier.
- Note that the '(' has already been eaten.
- The names of the formal parameters are stored into parbuf.
- */
- register char **pbuf = &buf[0];
- register int c;
- register char *ptr = &parbuf[0];
- register char **pbuf2;
- c = GetChar();
- c = skipspaces(c,0);
- if (c == ')') { /* no parameters: #define name() */
- *pbuf = (char *) 0;
- return 0;
- }
- for (;;) { /* eat the formal parameter list */
- if (class(c) != STIDF && class(c) != STELL) {
- error("#define: bad formal parameter");
- return -1;
- }
- *pbuf = ptr; /* name of the formal */
- *ptr++ = c;
- if (ptr >= &parbuf[PARBUFSIZE])
- fatal("formal parameter buffer overflow");
- do { /* eat the identifier name */
- c = GetChar();
- *ptr++ = c;
- if (ptr >= &parbuf[PARBUFSIZE])
- fatal("formal parameter buffer overflow");
- } while (in_idf(c));
- *(ptr - 1) = '\0'; /* mark end of the name */
- /* Check if this formal parameter is already used.
- Usually, macros do not have many parameters, so ...
- */
- for (pbuf2 = pbuf - 1; pbuf2 >= &buf[0]; pbuf2--) {
- if (!strcmp(*pbuf2, *pbuf)) {
- warning("formal parameter \"%s\" already used",
- *pbuf);
- }
- }
- pbuf++;
- c = skipspaces(c,0);
- if (c == ')') { /* end of the formal parameter list */
- *pbuf = (char *) 0;
- return pbuf - buf;
- }
- if (c != ',') {
- error("#define: bad formal parameter list");
- return -1;
- }
- c = GetChar();
- c = skipspaces(c,0);
- }
- /*NOTREACHED*/
- }
- macro_def(id, text, nformals, length, flags)
- register struct idf *id;
- char *text;
- {
- register struct macro *newdef = id->id_macro;
- /* macro_def() puts the contents and information of a macro
- definition into a structure and stores it into the symbol
- table entry belonging to the name of the macro.
- An error is given if there was already a definition
- */
- if (newdef) { /* is there a redefinition? */
- if (newdef->mc_flag & NOUNDEF) {
- error("it is not allowed to redefine %s", id->id_text);
- } else if (!macroeq(newdef->mc_text, text))
- error("illegal redefine of \"%s\"", id->id_text);
- free(text);
- return;
- } else {
- #ifdef DOBITS
- register char *p = id->id_text;
- #define setbit(bx) if (!*p) goto go_on; bits[*p++] |= (bx)
- setbit(bit0);
- setbit(bit1);
- setbit(bit2);
- setbit(bit3);
- setbit(bit4);
- setbit(bit5);
- setbit(bit6);
- setbit(bit7);
- go_on:
- #endif
- id->id_macro = newdef = new_macro();
- }
- newdef->mc_text = text; /* replacement text */
- newdef->mc_nps = nformals; /* nr of formals */
- newdef->mc_length = length; /* length of repl. text */
- newdef->mc_flag = flags; /* special flags */
- }
- int
- find_name(nm, index)
- char *nm, *index[];
- {
- /* find_name() returns the index of "nm" in the namelist
- "index" if it can be found there. 0 is returned if it is
- not there.
- */
- register char **ip = &index[0];
- while (*ip)
- if (strcmp(nm, *ip++) == 0)
- return ip - &index[0];
- /* arrived here, nm is not in the name list. */
- return 0;
- }
- #define BLANK(ch) ((ch == ' ') || (ch == '\t'))
- char *
- get_text(formals, length)
- char *formals[];
- int *length;
- {
- /* get_text() copies the replacement text of a macro
- definition with zero, one or more parameters, thereby
- substituting each formal parameter by a special character
- (non-ascii: 0200 & (order-number in the formal parameter
- list)) in order to substitute this character later by the
- actual parameter. The replacement text is copied into
- itself because the copied text will contain fewer or the
- same amount of characters. The length of the replacement
- text is returned.
- Implementation:
- finite automaton : we are interested in
- 1- white space, sequences must be mapped onto 1 single
- blank.
- 2- identifiers, since they might be replaced by some
- actual parameter.
- 3- strings and character constants, since replacing
- variables within them is illegal, and white-space is
- significant.
- 4- comment, same as for 1
- Other tokens will not be seen as such.
- */
- register int c;
- struct repl repls;
- register struct repl *repl = &repls;
- int blank = 0;
- c = GetChar();
- repl->r_ptr = repl->r_text = Malloc((unsigned)(repl->r_size = ITEXTSIZE));
- *repl->r_ptr = '\0';
- while ((c != EOI) && (class(c) != STNL)) {
- if (BLANK(c)) {
- blank++;
- c = GetChar();
- continue;
- }
- if (c == '\'' || c == '"') {
- register int delim = c;
- if (blank) {
- blank = 0;
- add2repl(repl, ' ');
- }
- do {
- add2repl(repl, c);
- if (c == '\\') add2repl(repl, GetChar());
- c = GetChar();
- } while (c != delim && c != EOI && class(c) != STNL);
- if (c != delim) {
- strict("unclosed opening %c", delim);
- break;
- }
- add2repl(repl, c);
- c = GetChar();
- } else if (c == '/') {
- c = GetChar();
- if (c == '*') {
- skipcomment();
- blank++;
- c = GetChar();
- continue;
- }
- if (blank) {
- blank = 0;
- add2repl(repl, ' ');
- }
- add2repl(repl, '/');
- } else if (formals
- && (class(c) == STIDF || class(c) == STELL)) {
- char id_buf[IDFSIZE + 1];
- register char *idp = id_buf;
- int n;
- /* read identifier: it may be a formal parameter */
- *idp++ = c;
- do {
- c = GetChar();
- if (idp <= &id_buf[IDFSIZE])
- *idp++ = c;
- } while (in_idf(c));
- *--idp = '\0';
- if (blank) {
- blank = 0;
- add2repl(repl, ' ');
- }
- /* construct the formal parameter mark or identifier */
- if (n = find_name(id_buf, formals))
- add2repl(repl, FORMALP | (char) n);
- else {
- idp = id_buf;
- while (*idp) add2repl(repl, *idp++);
- }
- } else if (class(c) == STNUM) {
- if (blank) {
- blank = 0;
- add2repl(repl, ' ');
- }
- add2repl(repl, c);
- if (c == '.') {
- c = GetChar();
- if (class(c) != STNUM) {
- continue;
- }
- add2repl(repl, c);
- }
- c = GetChar();
- while(in_idf(c) || c == '.') {
- add2repl(repl, c);
- if((c = GetChar()) == 'e' || c == 'E') {
- add2repl(repl, c);
- c = GetChar();
- if (c == '+' || c == '-') {
- add2repl(repl, c);
- c = GetChar();
- }
- }
- }
- } else {
- if (blank) {
- blank = 0;
- add2repl(repl, ' ');
- }
- add2repl(repl, c);
- c = GetChar();
- }
- }
- *length = repl->r_ptr - repl->r_text;
- return Realloc(repl->r_text, (unsigned)(repl->r_ptr - repl->r_text +1));
- }
- /* macroeq() decides whether two macro replacement texts are
- identical. This version compares the texts, which occur
- as strings, without taking care of the leading and trailing
- blanks (spaces and tabs).
- */
- macroeq(s, t)
- register char *s, *t;
- {
-
- /* skip leading spaces */
- while (BLANK(*s)) s++;
- while (BLANK(*t)) t++;
- /* first non-blank encountered in both strings */
- /* The actual comparison loop: */
- while (*s && *s == *t)
- s++, t++;
- /* two cases are possible when arrived here: */
- if (*s == '\0') { /* *s == '\0' */
- while (BLANK(*t)) t++;
- return *t == '\0';
- }
- else { /* *s != *t */
- while (BLANK(*s)) s++;
- while (BLANK(*t)) t++;
- return (*s == '\0') && (*t == '\0');
- }
- }
- do_line(l)
- unsigned int l;
- {
- struct token tk;
- int t = GetToken(&tk);
- if (t != EOF) SkipToNewLine();
- LineNumber = l; /* the number of the next input line */
- if (t == STRING) /* is there a filespecifier? */
- FileName = tk.tk_str;
- }
|