123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673 |
- /* $Header$ */
- /* PREPROCESSOR: CONTROLLINE INTERPRETER */
- #include "interface.h"
- #include "arith.h"
- #include "LLlex.h"
- #include "Lpars.h"
- #include "debug.h"
- #include "idf.h"
- #include "input.h"
- #include "nopp.h"
- #ifndef NOPP
- #include "ifdepth.h"
- #include "botch_free.h"
- #include "nparams.h"
- #include "parbufsize.h"
- #include "textsize.h"
- #include "idfsize.h"
- #include "assert.h"
- #include "alloc.h"
- #include "class.h"
- #include "macro.h"
- #include "storage.h"
- IMPORT char *inctable[]; /* list of include directories */
- PRIVATE char ifstack[IFDEPTH]; /* if-stack: the content of an entry is */
- /* 1 if a corresponding ELSE has been */
- /* encountered. */
- PRIVATE int nestlevel = -1; /* initially no nesting level. */
- PRIVATE struct idf *
- GetIdentifier()
- {
- /* returns a pointer to the descriptor of the identifier that is
- read from the input stream. A null-pointer is returned if
- the input does not contain an identifier.
- The substitution of macros is disabled.
- */
- int tok;
- struct token tk;
- ReplaceMacros = 0;
- tok = GetToken(&tk);
- ReplaceMacros = 1;
- return tok == IDENTIFIER ? tk.tk_idf : (struct idf *)0;
- }
- /* 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,
- i.e. it is not one of "define" .. "undef" , integer or newline.
- */
- EXPORT
- domacro()
- {
- struct token tk; /* the token itself */
- EoiForNewline = 1;
- SkipEscNewline = 1;
- switch(GetToken(&tk)) { /* select control line action */
- case IDENTIFIER: /* is it a macro keyword? */
- switch (tk.tk_idf->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) {
- lexerror("#line without linenumber");
- SkipRestOfLine();
- }
- else
- do_line((unsigned int)tk.tk_ival);
- break;
- case K_UNDEF: /* "undef" */
- do_undef();
- break;
- default:
- /* invalid word seen after the '#' */
- lexerror("%s: unknown control", tk.tk_idf->id_text);
- SkipRestOfLine();
- }
- break;
- case INTEGER: /* # <integer> [<filespecifier>]? */
- do_line((unsigned int)tk.tk_ival);
- break;
- case EOI: /* only `#' on this line: do nothing, ignore */
- break;
- default: /* invalid token following '#' */
- lexerror("illegal # line");
- SkipRestOfLine();
- }
- EoiForNewline = 0;
- SkipEscNewline = 0;
- }
- PRIVATE
- skip_block()
- {
- /* 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 skiplevel = nestlevel; /* current nesting level */
- struct token tk;
- NoUnstack++;
- for (;;) {
- LoadChar(ch); /* read first character after newline */
- if (ch != '#') {
- if (ch == EOI) {
- NoUnstack--;
- return;
- }
- SkipRestOfLine();
- continue;
- }
- if (GetToken(&tk) != IDENTIFIER) {
- SkipRestOfLine();
- continue;
- }
- /* an IDENTIFIER: look for #if, #ifdef and #ifndef
- without interpreting them.
- Interpret #else, #elif and #endif if they occur
- on the same level.
- */
- switch(tk.tk_idf->id_resmac) {
- case K_IF:
- case K_IFDEF:
- case K_IFNDEF:
- push_if();
- break;
- case K_ELIF:
- if (nestlevel == skiplevel) {
- nestlevel--;
- push_if();
- if (ifexpr()) {
- NoUnstack--;
- return;
- }
- }
- break;
- case K_ELSE:
- ++(ifstack[nestlevel]);
- if (nestlevel == skiplevel) {
- SkipRestOfLine();
- NoUnstack--;
- return;
- }
- break;
- case K_ENDIF:
- ASSERT(nestlevel >= 0);
- if (nestlevel == skiplevel) {
- SkipRestOfLine();
- nestlevel--;
- NoUnstack--;
- return;
- }
- nestlevel--;
- break;
- }
- }
- }
- PRIVATE
- 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".
- */
- IMPORT arith ifval;
- int errors = err_occurred;
- ifval = (arith)0;
- AccDefined = 1;
- UnknownIdIsZero = 1;
- PushLex(); /* NEW parser */
- If_expr(); /* invoke constant expression parser */
- PopLex(); /* OLD parser */
- AccDefined = 0;
- UnknownIdIsZero = 0;
- return (errors == err_occurred) && (ifval != (arith)0);
- }
- PRIVATE
- do_include()
- {
- /* do_include() performs the inclusion of a file.
- */
- char *filenm;
- int tok;
- struct token tk;
- AccFileSpecifier = 1;
- if (((tok = GetToken(&tk)) == FILESPECIFIER) || tok == STRING)
- filenm = tk.tk_str;
- else {
- lexerror("bad include syntax");
- filenm = (char *)0;
- }
- AccFileSpecifier = 0;
- SkipRestOfLine();
- if (filenm && !InsertFile(filenm, &inctable[tok == FILESPECIFIER]))
- lexerror("cannot find include file \"%s\"", filenm);
- }
- PRIVATE
- do_define()
- {
- /* do_define() interprets a #define control line.
- */
- struct idf *id; /* 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 (!(id = GetIdentifier())) {
- lexerror("#define: illegal macro name");
- SkipRestOfLine();
- return;
- }
- /* there is a formal parameter list if the identifier is
- followed immediately by a '('.
- */
- LoadChar(ch);
- if (ch == '(') {
- if ((nformals = getparams(formals, parbuf)) == -1) {
- SkipRestOfLine();
- return; /* an error occurred */
- }
- LoadChar(ch);
- }
- /* read the replacement text if there is any */
- ch = skipspaces(ch); /* find first character of the text */
- ASSERT(ch != EOI);
- if (class(ch) == STNL) {
- /* Treat `#define something' as `#define something ""'
- */
- repl_text = "";
- length = 0;
- }
- else {
- PushBack();
- repl_text = get_text((nformals > 0) ? formals : 0, &length);
- }
- macro_def(id, repl_text, nformals, length, NOFLAG);
- LineNumber++;
- }
- PRIVATE
- push_if()
- {
- if (nestlevel >= IFDEPTH)
- fatal("too many nested #if/#ifdef/#ifndef");
- else
- ifstack[++nestlevel] = 0;
- }
- PRIVATE
- do_elif()
- {
- if (nestlevel < 0 || (ifstack[nestlevel])) {
- /* invalid elif encountered.. */
- lexerror("#elif without corresponding #if");
- SkipRestOfLine();
- }
- else {
- /* restart at this level as if a #if
- is detected.
- */
- nestlevel--;
- push_if();
- skip_block();
- }
- }
- PRIVATE
- do_else()
- {
- SkipRestOfLine();
- if (nestlevel < 0 || (ifstack[nestlevel]))
- lexerror("#else without corresponding #if");
- else { /* mark this level as else-d */
- ++(ifstack[nestlevel]);
- skip_block();
- }
- }
- PRIVATE
- do_endif()
- {
- SkipRestOfLine();
- if (nestlevel-- < 0)
- lexerror("#endif without corresponding #if");
- }
- PRIVATE
- do_if()
- {
- push_if();
- if (!ifexpr()) /* a false #if/#elif expression */
- skip_block();
- }
- PRIVATE
- do_ifdef(how)
- {
- struct idf *id;
- /* how == 1 : ifdef; how == 0 : ifndef
- */
- push_if();
- if (id = GetIdentifier()) {
- if ((how && !(id && id->id_macro)) ||
- (!how && id && id->id_macro))
- { /* this id is not defined */
- skip_block();
- }
- else
- SkipRestOfLine();
- }
- else {
- lexerror("illegal #ifdef construction");
- SkipRestOfLine();
- }
- }
- PRIVATE
- do_undef()
- {
- struct idf *id;
- /* Forget a macro definition. */
- if (id = GetIdentifier()) {
- if (id && id->id_macro) { /* forget the macro */
- free_macro(id->id_macro);
- id->id_macro = (struct macro *) 0;
- }
- /* else: don't complain */
- }
- else
- lexerror("illegal #undef construction");
- SkipRestOfLine();
- }
- PRIVATE
- do_line(l)
- unsigned int l;
- {
- struct token tk;
- LineNumber = l;
- /* is there a filespecifier? */
- if (GetToken(&tk) == STRING)
- FileName = tk.tk_str;
- SkipRestOfLine();
- }
- PRIVATE 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 count = 0;
- register c;
- register char *ptr = &parbuf[0];
- LoadChar(c);
- c = skipspaces(c);
- if (c == ')') { /* no parameters: #define name() */
- buf[0] = (char *) 0;
- return 0;
- }
- for (;;) { /* eat the formal parameter list */
- if (class(c) != STIDF) { /* not an identifier */
- lexerror("#define: bad formal parameter");
- return -1;
- }
- buf[count++] = ptr; /* name of the formal */
- *ptr++ = c;
- if (ptr >= &parbuf[PARBUFSIZE])
- fatal("formal parameter buffer overflow");
- do { /* eat the identifier name */
- LoadChar(c);
- *ptr++ = c;
- if (ptr >= &parbuf[PARBUFSIZE])
- fatal("formal parameter buffer overflow");
- } while (in_idf(c));
- *(ptr - 1) = '\0'; /* mark end of the name */
- c = skipspaces(c);
- if (c == ')') { /* end of the formal parameter list */
- buf[count] = (char *) 0;
- return count;
- }
- if (c != ',') {
- lexerror("#define: bad formal parameter list");
- return -1;
- }
- LoadChar(c);
- c = skipspaces(c);
- }
- }
- EXPORT
- macro_def(id, text, nformals, length, flags)
- 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.
- A warning is given if the definition overwrites another
- (unless predefined!)
- */
- if (newdef) { /* is there a redefinition? */
- if ((newdef->mc_flag & PREDEF) == 0) {
- if (macroeq(newdef->mc_text, text))
- return;
- lexwarning("redefine \"%s\"", id->id_text);
- }
- /* else: overwrite pre-definition */
- }
- else
- 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 */
- }
- PRIVATE 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;
- }
- PRIVATE 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 only interested in
- identifiers, because they might be replaced by some actual
- parameter. Other tokens will not be seen as such.
- */
- register c;
- register text_size;
- char *text = Malloc(text_size = ITEXTSIZE);
- register pos = 0;
- LoadChar(c);
- while ((c != EOI) && (class(c) != STNL)) {
- if (c == '\\') { /* check for "\\\n" */
- LoadChar(c);
- if (c == '\n') {
- /* more than one line is used for the
- replacement text. Replace "\\\n" by " ".
- */
- text[pos++] = ' ';
- ++LineNumber;
- LoadChar(c);
- }
- else
- text[pos++] = '\\';
- if (pos == text_size)
- text = Srealloc(text, text_size += RTEXTSIZE);
- }
- else
- if ( c == '/') {
- LoadChar(c);
- if (c == '*') {
- skipcomment();
- text[pos++] = ' ';
- LoadChar(c);
- }
- else
- text[pos++] = '/';
- if (pos == text_size)
- text = Srealloc(text, text_size += RTEXTSIZE);
- }
- else
- if (formals && class(c) == STIDF) {
- char id_buf[IDFSIZE + 1];
- register id_size = 0;
- register n;
- /* read identifier: it may be a formal parameter */
- id_buf[id_size++] = c;
- do {
- LoadChar(c);
- if (id_size <= IDFSIZE)
- id_buf[id_size++] = c;
- } while (in_idf(c));
- id_buf[--id_size] = '\0';
- if (n = find_name(id_buf, formals)) {
- /* construct the formal parameter mark */
- text[pos++] = FORMALP | (char) n;
- if (pos == text_size)
- text = Srealloc(text,
- text_size += RTEXTSIZE);
- }
- else {
- register char *ptr = &id_buf[0];
- while (pos + id_size >= text_size)
- text = Srealloc(text,
- text_size += RTEXTSIZE);
- while (text[pos++] = *ptr++) ;
- pos--;
- }
- }
- else {
- text[pos++] = c;
- if (pos == text_size)
- text = Srealloc(text, text_size += RTEXTSIZE);
- LoadChar(c);
- }
- }
- text[pos++] = '\0';
- *length = pos - 1;
- return text;
- }
- #define BLANK(ch) ((ch == ' ') || (ch == '\t'))
- /* 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).
- */
- PRIVATE
- 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');
- }
- }
- #else NOPP
- EXPORT
- domacro()
- {
- int tok;
- struct token tk;
- EoiForNewline = 1;
- SkipEscNewline = 1;
- if ((tok = GetToken(&tk)) == IDENTIFIER) {
- if (strcmp(tk.tk_idf->id_text, "line") != 0) {
- error("illegal # line");
- SkipRestOfLine();
- return;
- }
- tok = GetToken(&tk);
- }
- if (tok != INTEGER) {
- error("illegal # line");
- SkipRestOfLine();
- return;
- }
- LineNumber = tk.tk_ival;
- if ((tok = GetToken(&tk)) == STRING)
- FileName = tk.tk_str;
- else
- if (tok != EOI) {
- error("illegal # line");
- SkipRestOfLine();
- }
- EoiForNewline = 0;
- SkipEscNewline = 0;
- }
- #endif NOPP
- PRIVATE
- SkipRestOfLine()
- {
- /* we do a PushBack because we don't want to skip the next line
- if the last character was a newline
- */
- PushBack();
- skipline();
- }
|