From: ceriel Date: Tue, 6 Jan 1987 15:16:53 +0000 (+0000) Subject: Initial revision X-Git-Tag: release-5-5~5060 X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=143b2531bb692596115e809d6fa9ef32f2022829;p=ack.git Initial revision --- diff --git a/util/cpp/LLlex.c b/util/cpp/LLlex.c new file mode 100644 index 000000000..744fd7287 --- /dev/null +++ b/util/cpp/LLlex.c @@ -0,0 +1,372 @@ +/* $Header$ */ +/* L E X I C A L A N A L Y Z E R */ + +#include "idfsize.h" +#include "numsize.h" +#include "strsize.h" + +#include +#include "input.h" +#include "idf.h" +#include "LLlex.h" +#include "Lpars.h" +#include "class.h" +#include "bits.h" + +/* Data about the token yielded */ +struct token dot; + +int ReplaceMacros = 1; /* replacing macros */ +int AccFileSpecifier = 0; /* return filespecifier <...> */ +int AccDefined = 0; /* accept "defined(...)" */ +int UnknownIdIsZero = 0; /* interpret unknown id as integer 0 */ + +char *string_token(); + +PushLex() +{ + DOT = 0; +} + +PopLex() +{} + +int +LLlex() +{ + return (DOT != EOF) ? GetToken(&dot) : EOF; +} + +#define BUFSIZ 1024 + +int +GetToken(ptok) + register struct token *ptok; +{ + char buf[BUFSIZ]; + register int c, nch; + +again: /* rescan the input after an error or replacement */ + LoadChar(c); +go_on: + if ((c & 0200) && c != EOI) + fatal("non-ascii '\\%03o' read", c & 0377); + switch (class(c)) { /* detect character class */ + case STNL: + LineNumber++; + return ptok->tk_symb = EOF; + case STSKIP: + goto again; + case STGARB: /* garbage character */ + if (c == '\\') { + /* a '\\' is allowed in #if/#elif expression */ + LoadChar(c); + if (class(c) == STNL) { /* vt , ff ? */ + ++LineNumber; + goto again; + } + PushBack(); + c = '\\'; + } + if (040 < c && c < 0177) + error("garbage char %c", c); + else + error("garbage char \\%03o", c); + goto again; + case STSIMP: /* a simple character, no part of compound token*/ + if (c == '/') { /* probably the start of comment */ + LoadChar(c); + if (c == '*') { /* start of comment */ + skipcomment(); + goto again; + } + else { + PushBack(); + c = '/'; /* restore c */ + } + } + return ptok->tk_symb = c; + case STCOMP: /* maybe the start of a compound token */ + LoadChar(nch); /* character lookahead */ + switch (c) { + case '!': + if (nch == '=') + return ptok->tk_symb = NOTEQUAL; + PushBack(); + return ptok->tk_symb = c; + case '&': + if (nch == '&') + return ptok->tk_symb = AND; + PushBack(); + return ptok->tk_symb = c; + case '<': + if (AccFileSpecifier) { + PushBack(); /* pushback nch */ + ptok->tk_str = + string_token("file specifier", '>'); + return ptok->tk_symb = FILESPECIFIER; + } + if (nch == '<') + return ptok->tk_symb = LEFT; + if (nch == '=') + return ptok->tk_symb = LESSEQ; + PushBack(); + return ptok->tk_symb = c; + case '=': + if (nch == '=') + return ptok->tk_symb = EQUAL; + /* The following piece of code tries to recognise + old-fashioned assignment operators `=op' + */ + error("illegal character"); + goto go_on; + case '>': + if (nch == '=') + return ptok->tk_symb = GREATEREQ; + if (nch == '>') + return ptok->tk_symb = RIGHT; + PushBack(); + return ptok->tk_symb = c; + case '|': + if (nch == '|') + return ptok->tk_symb = OR; + PushBack(); + return ptok->tk_symb = c; + } + case STIDF: + { + extern int idfsize; /* ??? */ + register char *tg = &buf[0]; + register char *maxpos = &buf[idfsize]; + register struct idf *idef; + +#define tstmac(bx) if (!(bits[c] & bx)) goto nomac +#define cpy if (Unstacked) EnableMacros(); *tg++ = c +#define load LoadChar(c); if (!in_idf(c)) goto endidf + +#ifdef DOBITS + cpy; tstmac(bit0); load; + cpy; tstmac(bit1); load; + cpy; tstmac(bit2); load; + cpy; tstmac(bit3); load; + cpy; tstmac(bit4); load; + cpy; tstmac(bit5); load; + cpy; tstmac(bit6); load; + cpy; tstmac(bit7); load; +#endif + + for(;;) { + if (tg < maxpos) { + cpy; + } + load; + } + endidf: + PushBack(); + *tg = '\0'; /* mark the end of the identifier */ + if (ReplaceMacros) { + idef = findidf(buf); + if ((idef && idef->id_macro && replace(idef))) { + goto again; + } + } + nomac: + LoadChar(c); + while (in_idf(c)) { + if (tg < maxpos) *tg++ = c; + LoadChar(c); + } + PushBack(); + *tg++ = '\0'; /* mark the end of the identifier */ + if (UnknownIdIsZero) { + ptok->tk_val = 0; + return ptok->tk_symb = INTEGER; + } + ptok->tk_str = Malloc(idfsize + 1); + strcpy(ptok->tk_str, buf); + return ptok->tk_symb = IDENTIFIER; + } + case STCHAR: /* character constant */ + { + register int val = 0, size = 0; + + LoadChar(c); + if (c == '\'') + error("character constant too short"); + else + while (c != '\'') { + if (c == '\n') { + error("newline in character constant"); + LineNumber++; + break; + } + if (c == '\\') { + LoadChar(c); + if (c == '\n') { + LoadChar(c); + LineNumber++; + continue; + } + c = quoted(c); + } + val = val*256 + c; + size++; + LoadChar(c); + } + if (size > sizeof(int)) + error("character constant too long"); + ptok->tk_val = val; + return ptok->tk_symb = INTEGER; + } + case STNUM: + { + register char *np = &buf[1]; + register int base = 10; + register int vch; + register int val = 0; + + if (c == '0') { + *np++ = c; + LoadChar(c); + if (c == 'x' || c == 'X') { + base = 16; + LoadChar(c); + } + else + base = 8; + } + while (vch = val_in_base(c, base), vch >= 0) { + val = val*base + vch; + if (np < &buf[NUMSIZE]) + *np++ = c; + LoadChar(c); + } + if (c == 'l' || c == 'L') + LoadChar(c); + PushBack(); + ptok->tk_val = val; + return ptok->tk_symb = INTEGER; + } + case STSTR: + ptok->tk_str = string_token("string", '"'); + return ptok->tk_symb = STRING; + case STEOI: /* end of text on source file */ + return ptok->tk_symb = EOF; + default: + crash("Impossible character class"); + } + /*NOTREACHED*/ +} + +skipcomment() +{ + register int c; + + NoUnstack++; + LoadChar(c); + do { + while (c != '*') { + if (class(c) == STNL) + ++LineNumber; + else + if (c == EOI) { + NoUnstack--; + return; + } + LoadChar(c); + } + /* Last Character seen was '*' */ + LoadChar(c); + } while (c != '/'); + NoUnstack--; +} + +char * +string_token(nm, stop_char) + char *nm; +{ + register int c; + register int str_size; + register char *str = Malloc(str_size = ISTRSIZE); + register int pos = 0; + + LoadChar(c); + while (c != stop_char) { + if (c == '\n') { + error("newline in %s", nm); + LineNumber++; + break; + } + if (c == EOI) { + error("end-of-file inside %s", nm); + break; + } + if (c == '\\') { + LoadChar(c); + if (c == '\n') { + LineNumber++; + LoadChar(c); + continue; + } + c = quoted(c); + } + str[pos++] = c; + if (pos == str_size) + str = Srealloc(str, str_size += RSTRSIZE); + LoadChar(c); + } + str[pos++] = '\0'; /* for filenames etc. */ + return str; +} + +int +quoted(c) + register int c; +{ + /* quoted() replaces an escaped character sequence by the + character meant. + */ + /* first char after backslash already in c */ + if (!is_oct(c)) { /* a quoted char */ + switch (c) { + case 'n': + c = '\n'; + break; + case 't': + c = '\t'; + break; + case 'b': + c = '\b'; + break; + case 'r': + c = '\r'; + break; + case 'f': + c = '\f'; + break; + } + } + else { /* a quoted octal */ + register int oct = 0, cnt = 0; + + do { + oct = oct*8 + (c-'0'); + LoadChar(c); + } while (is_oct(c) && ++cnt < 3); + PushBack(); + c = oct; + } + return c&0377; +} + +/* provisional */ +int +val_in_base(c, base) + register int c; +{ + return + is_dig(c) ? c - '0' : + base != 16 ? -1 : + is_hex(c) ? (c - 'a' + 10) & 017 : + -1; +} diff --git a/util/cpp/LLlex.h b/util/cpp/LLlex.h new file mode 100644 index 000000000..f85444aef --- /dev/null +++ b/util/cpp/LLlex.h @@ -0,0 +1,37 @@ +/* D E F I N I T I O N S F O R T H E L E X I C A L A N A L Y Z E R */ + +/* A token from the input stream is represented by an integer, + called a "symbol", but it may have other information associated + to it. +*/ + +/* the structure of a token: */ +struct token { + int tok_symb; /* the token itself */ + union { + int tok_val; /* numeric values */ + char *tok_str; /* string/filespecifier */ + } tok_data; +}; + +#include "file_info.h" + +#define tk_symb tok_symb +#define tk_val tok_data.tok_val +#define tk_str tok_data.tok_str + +extern struct token dot; + +extern int ReplaceMacros; /* "LLlex.c" */ +extern int AccFileSpecifier; /* "LLlex.c" */ +extern int AccDefined; /* "LLlex.c" */ +extern int UnknownIdIsZero; /* "LLlex.c" */ + +extern int NoUnstack; /* "input.c" */ +extern int Unstacked; /* "input.c" */ + +extern int err_occurred; /* "error.c" */ + +#define DOT dot.tk_symb + +#define EOF (-1) diff --git a/util/cpp/LLmessage.c b/util/cpp/LLmessage.c new file mode 100644 index 000000000..81b11333f --- /dev/null +++ b/util/cpp/LLmessage.c @@ -0,0 +1,19 @@ +/* PARSER ERROR ADMINISTRATION */ + +#include "LLlex.h" +#include "Lpars.h" + +extern char *symbol2str(); + +LLmessage(tk) { + err_occurred = 1; + if (tk < 0) + error("garbage at end of line"); + else if (tk) { + error("%s missing", symbol2str(tk)); + skipline(); + DOT = EOF; + } + else + error("%s deleted", symbol2str(DOT)); +} diff --git a/util/cpp/Makefile b/util/cpp/Makefile new file mode 100644 index 000000000..d02b11553 --- /dev/null +++ b/util/cpp/Makefile @@ -0,0 +1,164 @@ +# MAKEFILE FOR (STAND_ALONE) CEM PREPROCESSOR + +EMHOME=../.. +MODULES=$(EMHOME)/modules +MODULESLIB=$(MODULES)/lib +BIN=$(EMHOME)/lib + +# Some paths + +# Libraries +SYSLIB = $(MODULESLIB)/libsystem.a +STRLIB = $(MODULESLIB)/libstring.a +PRTLIB = $(MODULESLIB)/libprint.a +ALLOCLIB = $(MODULESLIB)/liballoc.a +ASSERTLIB = $(MODULESLIB)/libassert.a +MALLOC = $(MODULESLIB)/malloc.o +LIBS = $(PRTLIB) $(STRLIB) $(ALLOCLIB) $(MALLOC) $(ASSERTLIB) $(SYSLIB) +LIB_INCLUDES = -I$(MODULES)/h -I$(MODULES)/pkg + +# Where to install the preprocessor +CEMPP = $(BIN)/cpp + +# What C compiler to use and how +CC = cc +COPTIONS = +LFLAGS = -n + +# What parser generator to use and how +GEN = $(EMHOME)/bin/LLgen +GENOPTIONS = + +# Special #defines during compilation +CDEFS = $(LIB_INCLUDES) +CFLAGS = $(CDEFS) $(COPTIONS) -O# # we cannot pass the COPTIONS to lint! + +# Grammar files and their objects +LSRC = tokenfile.g expression.g +LCSRC = tokenfile.c expression.c Lpars.c +LOBJ = tokenfile.o expression.o Lpars.o + +# Objects of hand-written C files +CSRC = LLlex.c LLmessage.c ch7bin.c ch7mon.c domacro.c \ + error.c idf.c init.c input.c main.c options.c \ + preprocess.c replace.c scan.c skip.c tokenname.c next.c expr.c +COBJ = LLlex.o LLmessage.o ch7bin.o ch7mon.o domacro.o \ + error.o idf.o init.o input.o main.o options.o \ + preprocess.o replace.o scan.o skip.o tokenname.o next.o expr.o + +PRFILES = Makefile Parameters \ + make.hfiles make.tokcase make.tokfile LLlex.h bits.h file_info.h \ + idf.h input.h interface.h macro.h \ + class.h chtab.c char.tab expression.g $(CSRC) + +# Objects of other generated C files +GOBJ = char.o symbol2str.o + +# generated source files +GSRC = char.c symbol2str.c + +# .h files generated by `make hfiles'; PLEASE KEEP THIS UP-TO-DATE! +GHSRC = errout.h idfsize.h ifdepth.h lapbuf.h \ + maxincl.h nparams.h numsize.h obufsize.h \ + parbufsize.h pathlength.h strsize.h textsize.h \ + botch_free.h debug.h inputtype.h dobits.h + +# Other generated files, for 'make clean' only +GENERATED = tokenfile.g Lpars.h LLfiles LL.output lint.out \ + Xref hfiles cfiles charoffset.h + +all: cc + +cc: hfiles LLfiles + make "EMHOME="$(EMHOME) cpp + +hfiles: Parameters + ./make.hfiles Parameters + @touch hfiles + +LLfiles: $(LSRC) + $(GEN) $(GENOPTIONS) $(LSRC) + @touch LLfiles + +tokenfile.g: tokenname.c make.tokfile + tokenfile.g + +symbol2str.c: tokenname.c make.tokcase + symbol2str.c + +char.c: char.tab chtab + chtab -fchar.tab > char.c + +charoffset.h: chtab char.tab + chtab -fchar.tab > /dev/null + +chtab: chtab.o + $(CC) -o chtab chtab.o + +# Objects needed for 'cpp' +OBJ = $(COBJ) $(LOBJ) $(GOBJ) +SRC = $(CSRC) $(LCSRC) $(GSRC) + +cpp: $(OBJ) Makefile + $(CC) $(COPTIONS) $(LFLAGS) $(OBJ) $(LIBS) -o cpp + size cpp + +cfiles: hfiles LLfiles $(GSRC) + @touch cfiles + +install: all + cp cpp $(CEMPP) + +cmp: all + cmp cpp $(CEMPP) + +pr: + @pr $(PRFILES) + +opr: + make pr | opr + +tags: cfiles + ctags $(SRC) + +depend: cfiles + sed '/^#AUTOAUTO/,$$d' Makefile >Makefile.new + echo '#AUTOAUTOAUTOAUTOAUTOAUTOAUTOAUTOAUTOAUTO' >>Makefile.new + $(EMHOME)/bin/mkdep $(SRC) | \ + sed 's/\.c:/.o:/' >>Makefile.new + mv Makefile Makefile.old + mv Makefile.new Makefile + +xref: + ctags -x `grep "\.[ch]" Files`|sed "s/).*/)/">Xref + +lint: cfiles + lint -bx $(CDEFS) $(SRC) >lint.out + +clean: + rm -f $(LCSRC) $(OBJ) $(GENERATED) $(GSRC) $(GHSRC) chtab.o chtab + +#AUTOAUTOAUTOAUTOAUTOAUTOAUTOAUTOAUTOAUTO +LLlex.o: LLlex.h Lpars.h bits.h charoffset.h class.h dobits.h file_info.h idf.h idfsize.h input.h inputtype.h numsize.h strsize.h +LLmessage.o: LLlex.h Lpars.h file_info.h +ch7bin.o: Lpars.h +ch7mon.o: Lpars.h +domacro.o: LLlex.h Lpars.h bits.h botch_free.h charoffset.h class.h debug.h dobits.h file_info.h idf.h idfsize.h ifdepth.h input.h inputtype.h interface.h macro.h nparams.h parbufsize.h textsize.h +error.o: LLlex.h errout.h file_info.h +idf.o: idf.h +init.o: charoffset.h class.h idf.h interface.h macro.h +input.o: file_info.h input.h inputtype.h +main.o: file_info.h idfsize.h +options.o: charoffset.h class.h idf.h idfsize.h macro.h maxincl.h +preprocess.o: LLlex.h bits.h charoffset.h class.h dobits.h file_info.h idf.h idfsize.h input.h inputtype.h maxincl.h obufsize.h +replace.o: LLlex.h charoffset.h class.h debug.h file_info.h idf.h input.h inputtype.h interface.h macro.h pathlength.h textsize.h +scan.o: charoffset.h class.h idf.h input.h inputtype.h interface.h lapbuf.h macro.h nparams.h +skip.o: LLlex.h charoffset.h class.h file_info.h input.h inputtype.h +tokenname.o: LLlex.h Lpars.h file_info.h idf.h +next.o: debug.h +expr.o: Lpars.h +tokenfile.o: Lpars.h +expression.o: LLlex.h Lpars.h file_info.h +Lpars.o: Lpars.h +char.o: charoffset.h class.h +symbol2str.o: Lpars.h diff --git a/util/cpp/Parameters b/util/cpp/Parameters new file mode 100644 index 000000000..21bd8a667 --- /dev/null +++ b/util/cpp/Parameters @@ -0,0 +1,71 @@ +!File: pathlength.h +#define PATHLENGTH 1024 /* max. length of path to file */ + + +!File: errout.h +#define ERROUT STDERR /* file pointer for writing messages */ +#define MAXERR_LINE 5 /* maximum number of error messages given + on the same input line. */ + + +!File: idfsize.h +#define IDFSIZE 64 /* maximum significant length of an identifier */ + + +!File: numsize.h +#define NUMSIZE 256 /* maximum length of a numeric constant */ + + +!File: nparams.h +#define NPARAMS 32 /* maximum number of parameters of macros */ + + +!File: ifdepth.h +#define IFDEPTH 256 /* maximum number of nested if-constructions */ + + +!File: maxincl.h +#define MAXINCL 16 /* maximum number of #include directories */ + + +!File: lapbuf.h +#define LAPBUF 4096 /* size of macro actual parameter buffer */ + + +!File: strsize.h +#define ISTRSIZE 32 /* minimum number of bytes allocated for + storing a string */ +#define RSTRSIZE 8 /* step size in enlarging the memory for + the storage of a string */ + + +!File: botch_free.h +#undef BOTCH_FREE 1 /* botch freed memory, as a check */ + + +!File: debug.h +#undef DEBUG 1 /* perform various self-tests */ + + +!File: parbufsize.h +#define PARBUFSIZE 1024 + + +!File: textsize.h +#define ITEXTSIZE 32 /* 1st piece of memory for repl. text */ +#define RTEXTSIZE 32 /* stepsize for enlarging repl.text */ + + +!File: inputtype.h +#undef INP_READ_IN_ONE 1 /* read input file in one. */ + /* If defined, we cannot read from a pipe */ + + +!File: obufsize.h +#define OBUFSIZE 8192 /* output buffer size */ + + +!File: dobits.h +#define DOBITS 1 /* use trick to reduce symboltable accesses */ + + diff --git a/util/cpp/bits.h b/util/cpp/bits.h new file mode 100644 index 000000000..97195218c --- /dev/null +++ b/util/cpp/bits.h @@ -0,0 +1,13 @@ +#include "dobits.h" +#ifdef DOBITS +#define bit0 0x01 +#define bit1 0x02 +#define bit2 0x04 +#define bit3 0x08 +#define bit4 0x10 +#define bit5 0x20 +#define bit6 0x40 +#define bit7 0x80 + +extern char bits[]; +#endif diff --git a/util/cpp/ch7bin.c b/util/cpp/ch7bin.c new file mode 100644 index 000000000..120f8becb --- /dev/null +++ b/util/cpp/ch7bin.c @@ -0,0 +1,74 @@ +/* EVALUATION OF BINARY OPERATORS */ + +#include "Lpars.h" + +ch7bin(pval, oper, val) + register int *pval, val; + int oper; +{ + switch (oper) { + case '%': + if (val == 0) + error("% by 0"); + else + *pval = *pval % val; + break; + case '/': + if (val == 0) + error("/ by 0"); + else + *pval = *pval / val; + break; + case '*': + *pval = *pval * val; + break; + case '+': + *pval = *pval + val; + break; + case '-': + *pval = *pval - val; + break; + case LEFT: + *pval = *pval << val; + break; + case RIGHT: + *pval = *pval >> val; + break; + case '<': + *pval = (*pval < val); + break; + case '>': + *pval = (*pval > val); + break; + case LESSEQ: + *pval = (*pval <= val); + break; + case GREATEREQ: + *pval = (*pval >= val); + break; + case EQUAL: + *pval = (*pval == val); + break; + case NOTEQUAL: + *pval = (*pval != val); + break; + case '&': + *pval = *pval & val; + break; + case '^': + *pval = *pval ^ val; + break; + case '|': + *pval = *pval | val; + break; + case AND: + *pval = (*pval && val); + break; + case OR: + *pval = (*pval || val); + break; + case ',': + *pval = val; + break; + } +} diff --git a/util/cpp/ch7mon.c b/util/cpp/ch7mon.c new file mode 100644 index 000000000..5856dbabe --- /dev/null +++ b/util/cpp/ch7mon.c @@ -0,0 +1,19 @@ +/* EVALUATION OF MONADIC OPERATORS */ + +#include "Lpars.h" + +ch7mon(oper, pval) + register int *pval; +{ + switch (oper) { + case '~': + *pval = ~(*pval); + break; + case '-': + *pval = -(*pval); + break; + case '!': + *pval = !(*pval); + break; + } +} diff --git a/util/cpp/char.tab b/util/cpp/char.tab new file mode 100644 index 000000000..d5e8d9a7e --- /dev/null +++ b/util/cpp/char.tab @@ -0,0 +1,36 @@ +% +% CHARACTER CLASSES +% +% some general settings: +%F %s, +% +% START OF TOKEN +% +%C +STGARB:\000-\377 +STSKIP:\r \t +STNL:\n\f\013 +STCOMP:!&<=>| +STSIMP:-%()+*,/:?^~ +STCHAR:' +STIDF:a-zA-Z_ +STNUM:0-9 +STSTR:" +STEOI:\200 +%T/* character classes */ +%T#include "class.h" +%Tchar tkclass[] = { +%p +%T}; +% +% other kinds of classes +% +%C +_D_|_H_|_I_|_O_:0-7 +_D_|_H_|_I_:89 +_H_|_I_:a-fA-F +_I_:g-zG-Z_ +%Tchar tk2class[] = { +%F %s, +%p +%T}; diff --git a/util/cpp/chtab.c b/util/cpp/chtab.c new file mode 100644 index 000000000..8b369f388 --- /dev/null +++ b/util/cpp/chtab.c @@ -0,0 +1,316 @@ +/* + chtab - character table generator + + Author: Erik Baalbergen (..tjalk!erikb) + Modified by Ceriel Jacobs +*/ + +#include + +static char *RcsId = "$Header$"; + +#define MAXBUF 256 +#define TABSIZE 257 +#define COMCOM '-' +#define FILECOM '%' + +int InputForm = 'c'; +char OutputForm[MAXBUF] = "%s,\n"; +char *Table[TABSIZE]; +char *Name; +char *ProgCall; +int signedch = 0; +char *OutputName; +char *chroffsetfile = "charoffset.h"; + +main(argc, argv) + char *argv[]; +{ + char c = 0200; + int i = c; + + if (i < 0) signedch = 1; + + ProgCall = *argv++; + argc--; + while (argc-- > 0) { + if (**argv == COMCOM) { + option(*argv++); + } + else { + process(*argv++, InputForm); + } + } + MkCharIndex(); +} + +MkCharIndex() +{ + FILE *fp; + + if ((fp = fopen(chroffsetfile, "w")) == NULL) { + fprintf(stderr, "%s: cannot write file %s\n", ProgCall, chroffsetfile); + exit(1); + } + if (signedch) { + fputs("#define CharOffset 128\n"); + } + else fputs("#define CharOffset 0\n"); + fclose(fp); +} + +char * +Salloc(s) + char *s; +{ + char *malloc(); + char *ns = malloc(strlen(s) + 1); + + if (ns) { + strcpy(ns, s); + } + return ns; +} + +option(str) + char *str; +{ + /* note that *str indicates the source of the option: + either COMCOM (from command line) or FILECOM (from a file). + */ + switch (*++str) { + + case ' ': /* command */ + case '\t': + case '\0': + break; + case 'I': + InputForm = *++str; + break; + case 'f': + if (*++str == '\0') { + fprintf(stderr, "%s: -f: name expected\n", ProgCall); + exit(1); + } + DoFile(str); + break; + case 'F': + sprintf(OutputForm, "%s\n", ++str); + break; + case 'T': + printf("%s\n", ++str); + break; + case 'p': + PrintTable(); + break; + case 'C': + ClearTable(); + break; + case 'H': + if (*++str == '\0') { + fprintf(stderr, "%s: -H: name expected\n", ProgCall); + } + else chroffsetfile = ++str; + break; + default: + fprintf(stderr, "%s: bad option -%s\n", ProgCall, str); + } +} + +ClearTable() +{ + register i; + + for (i = 0; i < TABSIZE; i++) { + Table[i] = 0; + } +} + +PrintTable() +{ + register i; + + for (i = 0; i < TABSIZE; i++) { + if (Table[i]) { + printf(OutputForm, Table[i]); + } + else { + printf(OutputForm, "0"); + } + } +} + +process(str, format) + char *str; +{ + char *cstr = str; + char *Name = cstr; /* overwrite original string! */ + + /* strip of the entry name + */ + while (*str && *str != ':') { + if (*str == '\\') { + ++str; + } + *cstr++ = *str++; + } + + if (*str != ':') { + fprintf(stderr, "%s: bad specification: \"%s\", ignored\n", + ProgCall, Name); + return 0; + } + *cstr = '\0'; + str++; + + switch (format) { + + case 'c': + return c_proc(str, Name); + default: + fprintf(stderr, "%s: bad input format\n", ProgCall); + } + return 0; +} + +c_proc(str, Name) + char *str; + char *Name; +{ + int ch, ch2; + int quoted(); + char *name = Salloc(Name); + + while (*str) { + if (*str == '\\') { + ch = quoted(&str); + } + else { + ch = *str++; + } + if (*str == '-') { + if (*++str == '\\') { + ch2 = quoted(&str); + } + else { + if (ch2 = *str++); + else str--; + } + if (ch > ch2) { + fprintf(stderr, "%s: bad range\n", ProgCall); + return 0; + } +#define ind(X) (signedch?(X>=128?X-128:X+128):X) + while (ch <= ch2) { + Table[ind(ch)] = name; + ch++; + } + } + else { + if (ch >= 0 && ch <= 255) + Table[ind(ch)] = name; + } + } + Table[256] = Table[0]; + return 1; +} + +int +quoted(pstr) + char **pstr; +{ + register int ch; + register int i; + register char *str = *pstr; + + if ((*++str >= '0') && (*str <= '9')) { + ch = 0; + for (i = 0; i < 3; i++) { + ch = 8 * ch + (*str - '0'); + if (*++str < '0' || *str > '9') + break; + } + } + else { + switch (*str++) { + + case 'n': + ch = '\n'; + break; + case 't': + ch = '\t'; + break; + case 'b': + ch = '\b'; + break; + case 'r': + ch = '\r'; + break; + case 'f': + ch = '\f'; + break; + default : + ch = *str; + } + } + *pstr = str; + return ch & 0377; +} + +int +stoi(str) + char *str; +{ + register i = 0; + + while (*str >= '0' && *str <= '9') { + i = i * 10 + *str++ - '0'; + } + return i; +} + +char * +getline(s, n, fp) + char *s; + FILE *fp; +{ + register c = getc(fp); + char *str = s; + + while (n--) { + if (c == EOF) { + return NULL; + } + else + if (c == '\n') { + *str++ = '\0'; + return s; + } + *str++ = c; + c = getc(fp); + } + s[n - 1] = '\0'; + return s; +} + +#define BUFSIZE 1024 + +DoFile(name) + char *name; +{ + char text[BUFSIZE]; + FILE *fp; + + if ((fp = fopen(name, "r")) == NULL) { + fprintf(stderr, "%s: cannot read file %s\n", ProgCall, name); + exit(1); + } + while (getline(text, BUFSIZE, fp) != NULL) { + if (text[0] == FILECOM) { + option(text); + } + else { + process(text, InputForm); + } + } +} diff --git a/util/cpp/class.h b/util/cpp/class.h new file mode 100644 index 000000000..3697fd572 --- /dev/null +++ b/util/cpp/class.h @@ -0,0 +1,41 @@ +/* U S E O F C H A R A C T E R C L A S S E S */ + +/* As a starter, chars are divided into classes, according to which + token they can be the start of. + At present such a class number is supposed to fit in 4 bits. +*/ + +#include "charoffset.h" + +#define class(ch) ((tkclass+CharOffset)[ch]) + +/* Being the start of a token is, fortunately, a mutual exclusive + property, so, as there are less than 16 classes they can be + packed in 4 bits. +*/ + +#define STSKIP 0 /* spaces and so on: skipped characters */ +#define STNL 1 /* newline character(s): update linenumber etc. */ +#define STGARB 2 /* garbage ascii character: not allowed */ +#define STSIMP 3 /* this character can occur as token */ +#define STCOMP 4 /* this one can start a compound token */ +#define STIDF 5 /* being the initial character of an identifier */ +#define STCHAR 6 /* the starter of a character constant */ +#define STSTR 7 /* the starter of a string */ +#define STNUM 8 /* the starter of a numeric constant */ +#define STEOI 9 /* End-Of-Information mark */ + +/* But occurring inside a token is not, so we need 1 bit for each + class. +*/ +#define _I_ 01 +#define _O_ 02 +#define _D_ 04 +#define _H_ 010 + +#define in_idf(ch) ((tk2class+CharOffset)[ch] & _I_) +#define is_oct(ch) ((tk2class+CharOffset)[ch] & _O_) +#define is_dig(ch) ((tk2class+CharOffset)[ch] & _D_) +#define is_hex(ch) ((tk2class+CharOffset)[ch] & _H_) + +extern char tkclass[], tk2class[]; diff --git a/util/cpp/domacro.c b/util/cpp/domacro.c new file mode 100644 index 000000000..2b908448b --- /dev/null +++ b/util/cpp/domacro.c @@ -0,0 +1,693 @@ +/* $Header$ */ +/* PREPROCESSOR: CONTROLLINE INTERPRETER */ + +#include "interface.h" +#include +#include "LLlex.h" +#include "Lpars.h" +#include "debug.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 +#include +#include "class.h" +#include "macro.h" +#include "bits.h" + +IMPORT char *inctable[]; /* list of include directories */ +IMPORT char *getwdir(); +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 char * +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_str : (char *)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 */ + register struct idf *id; + + switch(GetToken(&tk)) { /* 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); + PushBack(); + skipline(); + 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("#line without linenumber"); + PushBack(); + skipline(); + } + else + do_line((unsigned int)tk.tk_val); + break; + case K_UNDEF: /* "undef" */ + do_undef(); + break; + default: + /* invalid word seen after the '#' */ + error("%s: unknown control", id->id_text); + PushBack(); + skipline(); + } + break; + case INTEGER: /* # []? */ + do_line((unsigned int)tk.tk_val); + break; + case EOI: /* only `#' on this line: do nothing, ignore */ + break; + default: /* invalid token following '#' */ + error("illegal # line"); + PushBack(); + skipline(); + } +} + +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 int skiplevel = nestlevel; /* current nesting level */ + struct token tk; + struct idf *id; + + NoUnstack++; + for (;;) { + LoadChar(ch); /* read first character after newline */ + if (ch != '#') { + if (ch == EOI) { + NoUnstack--; + return; + } + PushBack(); + skipline(); + continue; + } + if (GetToken(&tk) != IDENTIFIER) { + PushBack(); + skipline(); + 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); + free(tk.tk_str); + switch(id->id_resmac) { + case K_IF: + case K_IFDEF: + case K_IFNDEF: + push_if(); + continue; + case K_ELIF: + if (nestlevel == skiplevel) { + nestlevel--; + push_if(); + if (ifexpr()) { + NoUnstack--; + return; + } + } + break; + case K_ELSE: + ++(ifstack[nestlevel]); + if (nestlevel == skiplevel) { + PushBack(); + skipline(); + NoUnstack--; + return; + } + break; + case K_ENDIF: + assert(nestlevel >= 0); + if (nestlevel == skiplevel) { + PushBack(); + skipline(); + 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; + 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; + PushBack(); + skipline(); + inctable[0] = WorkingDir; + if (filenm) { + if (!InsertFile(filenm, &inctable[tok==FILESPECIFIER],&result)){ + error("cannot find include file \"%s\"", filenm); + } + else { + WorkingDir = getwdir(result); + FileName = result; + LineNumber = 1; + } + } +} + +PRIVATE +do_define() +{ + /* do_define() interprets a #define control line. + */ + char *str; + 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())) { + error("#define: illegal macro name"); + PushBack(); + skipline(); + 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) { + PushBack(); + skipline(); + 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(str2idf(str, 0), 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])) { + error("#elif without corresponding #if"); + PushBack(); + skipline(); + } + else { /* restart at this level as if a #if is detected. */ + nestlevel--; + push_if(); + skip_block(); + } +} + +PRIVATE +do_else() +{ + PushBack(); + skipline(); + if (nestlevel < 0 || (ifstack[nestlevel])) + error("#else without corresponding #if"); + else { /* mark this level as else-d */ + ++(ifstack[nestlevel]); + skip_block(); + } +} + +PRIVATE +do_endif() +{ + PushBack(); + skipline(); + if (nestlevel-- < 0) + error("#endif without corresponding #if"); +} + +PRIVATE +do_if() +{ + push_if(); + if (!ifexpr()) /* a false #if/#elif expression */ + skip_block(); +} + +PRIVATE +do_ifdef(how) +{ + register struct idf *id; + char *str; + + /* how == 1 : ifdef; how == 0 : ifndef + */ + push_if(); + str = GetIdentifier(); + if (!str) { + error("illegal #ifdef construction"); + id = 0; + } + else { + id = findidf(str); + free(str); + } + + /* The next test is a shorthand for: + (how && !id->id_macro) || (!how && id->id_macro) + */ + if (how ^ (id && id->id_macro != 0)) + skip_block(); + else { + PushBack(); + skipline(); + } +} + +PRIVATE +do_undef() +{ + register struct idf *id; + register char *str = GetIdentifier(); + + /* Forget a macro definition. */ + if (str) { + if (id = findidf(str)) { + if (id->id_macro) { /* forget the macro */ + free_macro(id->id_macro); + id->id_macro = (struct macro *) 0; + } /* else: don't complain */ + } + free(str); + } + else + error("illegal #undef construction"); + PushBack(); + skipline(); +} + +PRIVATE +do_line(l) + unsigned int l; +{ + struct token tk; + + LineNumber = l - 1; /* the number of the next input line */ + if (GetToken(&tk) == STRING) /* is there a filespecifier? */ + FileName = tk.tk_str; + PushBack(); + skipline(); +} + +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 char **pbuf = &buf[0]; + register int c; + register char *ptr = &parbuf[0]; + register char **pbuf2; + + LoadChar(c); + c = skipspaces(c); + if (c == ')') { /* no parameters: #define name() */ + *pbuf = (char *) 0; + return 0; + } + for (;;) { /* eat the formal parameter list */ + if (class(c) != STIDF) { /* not an identifier */ + 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 */ + 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 */ + + /* 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); + if (c == ')') { /* end of the formal parameter list */ + *pbuf = (char *) 0; + return pbuf - buf; + } + if (c != ',') { + error("#define: bad formal parameter list"); + return -1; + } + LoadChar(c); + c = skipspaces(c); + } + /*NOTREACHED*/ +} + +EXPORT +macro_def(id, text, nformals, length, flags) + register struct idf *id; + char *text; +{ + /* 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. + */ + register struct macro *newdef = id->id_macro; + + if (newdef) { /* is there a redefinition? */ + if (macroeq(newdef->mc_text, text)) + return; + warning("redefine \"%s\"", id->id_text); + } + 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 */ + newdef->mc_count = 0; +} + +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 int c; + register int text_size; + char *text = Malloc(text_size = ITEXTSIZE); + register int 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'); + } +} diff --git a/util/cpp/error.c b/util/cpp/error.c new file mode 100644 index 000000000..f19ce53d8 --- /dev/null +++ b/util/cpp/error.c @@ -0,0 +1,59 @@ +/* E R R O R A N D D I A G N O S T I C R O U T I N E S */ + +#include + +#include "errout.h" +#include "LLlex.h" + +/* This file contains the (non-portable) error-message and diagnostic + functions. Beware, they are called with a variable number of + arguments! +*/ + +int err_occurred; + +err_hdr(s) + char *s; +{ + fprint(ERROUT, "\"%s\", line %d: %s", FileName, LineNumber, s); +} + +/*VARARGS1*/ +error(fmt, args) + char *fmt; +{ + err_hdr(""); + doprnt(ERROUT, fmt, &args); + fprint(ERROUT, "\n"); +} + +/*VARARGS1*/ +warning(fmt, args) + char *fmt; +{ + err_hdr("warning "); + doprnt(ERROUT, fmt, &args); + fprint(ERROUT, "\n"); +} + +/*VARARGS1*/ +crash(fmt, args) + char *fmt; + int args; +{ + err_hdr("crash "); + doprnt(ERROUT, fmt, &args); + fprint(ERROUT, "\n"); + sys_stop(S_ABORT); +} + +/*VARARGS1*/ +fatal(fmt, args) + char *fmt; + int args; +{ + err_hdr("fatal "); + doprnt(ERROUT, fmt, &args); + fprint(ERROUT, "\n"); + sys_stop(S_EXIT); +} diff --git a/util/cpp/expr.c b/util/cpp/expr.c new file mode 100644 index 000000000..45ce1a054 --- /dev/null +++ b/util/cpp/expr.c @@ -0,0 +1,53 @@ +/* OPERATOR HANDLING */ + +#include "Lpars.h" + +int +rank_of(oper) + int oper; +{ + /* The rank of the operator oper is returned. + */ + switch (oper) { + default: + return 0; + case '(': + return 1; + case '!': + return 2; + case '*': + case '/': + case '%': + return 3; + case '+': + case '-': + return 4; + case LEFT: + case RIGHT: + return 5; + case '<': + case '>': + case LESSEQ: + case GREATEREQ: + return 6; + case EQUAL: + case NOTEQUAL: + return 7; + case '&': + return 8; + case '^': + return 9; + case '|': + return 10; + case AND: + return 11; + case OR: + return 12; + case '?': + case ':': + return 13; + case ',': + return 15; + } + /*NOTREACHED*/ +} diff --git a/util/cpp/expression.g b/util/cpp/expression.g new file mode 100644 index 000000000..109b56441 --- /dev/null +++ b/util/cpp/expression.g @@ -0,0 +1,124 @@ +/* EXPRESSION SYNTAX PARSER */ + +%lexical LLlex; +%start If_expr, if_expression; + +{ +#include "LLlex.h" + +extern int ifval; +} + +if_expression +: + constant_expression(&ifval) +; + +/* 7.1 */ +primary(int *pval;) +: + constant(pval) +| + '(' expression(pval) ')' +; + +unary(int *pval;) + {int oper;} +: + unop(&oper) + unary(pval) + { ch7mon(oper, pval); } +| + primary(pval) +; + +binary_expression(int maxrank; int *pval;) + {int oper; int val1;} +: + unary(pval) + [%while (rank_of(DOT) <= maxrank) + binop(&oper) + binary_expression(rank_of(oper)-1, &val1) + { + ch7bin(pval, oper, val1); + } + ]* +; + +/* 7.13 */ +conditional_expression(int *pval;) + {int val1 = 0, val2 = 0;} +: + /* allow all binary operators */ + binary_expression(rank_of('?') - 1, pval) + [ '?' + expression(&val1) + ':' + assignment_expression(&val2) + { *pval = (*pval ? val1 : val2); } + ]? +; + +/* 7.14 */ +assignment_expression(int *pval;) +: + conditional_expression(pval) +; + +/* 7.15 */ +expression(int *pval;) + {int val1;} +: + assignment_expression(pval) + [ ',' + assignment_expression(&val1) + { + ch7bin(pval, ',', val1); + } + ]* +; + +unop(int *oper;) : + [ '-' | '!' | '~' ] + {*oper = DOT;} +; + +multop: + '*' | '/' | '%' +; + +addop: + '+' | '-' +; + +shiftop: + LEFT | RIGHT +; + +relop: + '<' | '>' | LESSEQ | GREATEREQ +; + +eqop: + EQUAL | NOTEQUAL +; + +arithop: + multop | addop | shiftop +| + '&' | '^' | '|' +; + +binop(int *oper;) : + [ arithop | relop | eqop | AND | OR ] + {*oper = DOT;} +; + +constant(int *pval;) : + INTEGER + {*pval = dot.tk_val;} +; + +constant_expression (int *pval;) : + assignment_expression(pval) +; diff --git a/util/cpp/file_info.h b/util/cpp/file_info.h new file mode 100644 index 000000000..8a3c8a899 --- /dev/null +++ b/util/cpp/file_info.h @@ -0,0 +1,13 @@ +/* F I L E I N F O R M A T I O N S T R U C T U R E */ + +struct file_info { + unsigned int fil_lino; + char *fil_name; + char *fil_wdir; +}; + +#define LineNumber finfo.fil_lino +#define FileName finfo.fil_name +#define WorkingDir finfo.fil_wdir + +extern struct file_info finfo; /* input.c */ diff --git a/util/cpp/idf.c b/util/cpp/idf.c new file mode 100644 index 000000000..4bbd88a80 --- /dev/null +++ b/util/cpp/idf.c @@ -0,0 +1,2 @@ +#include "idf.h" +#include diff --git a/util/cpp/idf.h b/util/cpp/idf.h new file mode 100644 index 000000000..a017b9cfb --- /dev/null +++ b/util/cpp/idf.h @@ -0,0 +1,11 @@ +struct id_usr { + struct macro *idu_macro; + int idu_resmac; +}; + +#define IDF_TYPE struct id_usr +#define IDF_HSIZE 6 +#define id_macro id_user.idu_macro +#define id_resmac id_user.idu_resmac + +#include diff --git a/util/cpp/init.c b/util/cpp/init.c new file mode 100644 index 000000000..64ec4cbab --- /dev/null +++ b/util/cpp/init.c @@ -0,0 +1,72 @@ +/* PREPROCESSOR: INITIALIZATION ROUTINES */ + +#include +#include +#include "class.h" +#include "macro.h" +#include "idf.h" +#include "interface.h" + +PRIVATE struct mkey { + char *mk_reserved; + int mk_key; +} mkey[] = { + {"define", K_DEFINE}, + {"elif", K_ELIF}, + {"else", K_ELSE}, + {"endif", K_ENDIF}, + {"if", K_IF}, + {"ifdef", K_IFDEF}, + {"ifndef", K_IFNDEF}, + {"include", K_INCLUDE}, + {"line", K_LINE}, + {"undef", K_UNDEF}, + {0, K_UNKNOWN} +}; + +char *strcpy(); + +EXPORT +init_pp() +{ + long clock, sys_time(); + static char date[30]; + char *ctime(); + + /* Initialise the control line keywords (if, include, define, etc) + Although the lexical analyzer treats them as identifiers, the + control line handler can recognize them as keywords by the + id_resmac field of the identifier. + */ + { + register struct mkey *mk = &mkey[0]; + + while (mk->mk_reserved) { + struct idf *idf = str2idf(mk->mk_reserved, 0); + + if (idf->id_resmac) + fatal("maximum identifier length insufficient"); + idf->id_resmac = mk->mk_key; + mk++; + } + } + + /* Initialize __DATE__, __FILE__ and __LINE__ macro + definitions. + */ + /* __DATE__ */ + clock = sys_time(); + strcpy(&date[1], ctime(&clock)); + date[26] = '\0'; /* zap nl */ + date[0] = date[25] = '"'; + macro_def(str2idf("__DATE__", 0), date, -1, 26, NOFLAG); + + /* __LINE__ */ + macro_def(str2idf("__LINE__", 0), "0", -1, 1, FUNC); + + /* __FILE__ */ + macro_def(str2idf("__FILE__", 0), "", -1, 1, FUNC); + + /* defined(??) */ + macro_def(str2idf("defined", 0), "", 1, 1, FUNC); +} diff --git a/util/cpp/input.c b/util/cpp/input.c new file mode 100644 index 000000000..b26bfc038 --- /dev/null +++ b/util/cpp/input.c @@ -0,0 +1,47 @@ +#include "file_info.h" +#include "input.h" +#define INP_TYPE struct file_info +#define INP_VAR finfo +struct file_info finfo; +#include + +char * +getwdir(fn) + char *fn; +{ + register char *p; + char *strrindex(); + + p = strrindex(fn, '/'); + while (p && *(p + 1) == '\0') { /* remove trailing /'s */ + *p = '\0'; + p = strrindex(fn, '/'); + } + + if (fn[0] == '\0' || (fn[0] == '/' && p == &fn[0])) /* absolute path */ + return ""; + else + if (p) { + *p = '\0'; + fn = Salloc(fn, p - &fn[0] + 1); + *p = '/'; + return fn; + } + else return "."; +} + +int NoUnstack; +int Unstacked; + +AtEoIT() +{ + if (NoUnstack) error("unexpected EOF"); + DoUnstack(); + return 0; +} + +AtEoIF() +{ + if (NoUnstack) error("unexpected EOF"); + return 0; +} diff --git a/util/cpp/input.h b/util/cpp/input.h new file mode 100644 index 000000000..b1221d583 --- /dev/null +++ b/util/cpp/input.h @@ -0,0 +1,2 @@ +#include "inputtype.h" +#include diff --git a/util/cpp/interface.h b/util/cpp/interface.h new file mode 100644 index 000000000..d4a8c6518 --- /dev/null +++ b/util/cpp/interface.h @@ -0,0 +1,3 @@ +#define PRIVATE +#define IMPORT extern +#define EXPORT diff --git a/util/cpp/macro.h b/util/cpp/macro.h new file mode 100644 index 000000000..b04953a7f --- /dev/null +++ b/util/cpp/macro.h @@ -0,0 +1,52 @@ +/* PREPROCESSOR: DEFINITION OF MACRO DESCRIPTOR */ + +/* The flags of the mc_flag field of the macro structure. Note that + these flags can be set simultaneously. +*/ +#define NOFLAG 0 /* no special flags */ +#define FUNC 01 /* function attached */ +#define NOREPLACE 02 /* don't replace */ + +#define FORMALP 0200 /* mask for creating macro formal parameter */ + +/* The macro descriptor is very simple, except the fact that the + mc_text, which points to the replacement text, contains the + non-ascii characters \201, \202, etc, indicating the position of a + formal parameter in this text. +*/ +struct macro { + struct macro *next; + char * mc_text; /* the replacement text */ + int mc_nps; /* number of formal parameters */ + int mc_length; /* length of replacement text */ + int mc_count; /* # of "concurrent" invocations*/ + char mc_flag; /* marking this macro */ +}; + + +/* allocation definitions of struct macro */ +extern char *st_alloc(); +extern struct macro *h_macro; +#ifdef DEBUG +extern int cnt_macro; +extern char *std_alloc(); +#define new_macro() ((struct macro *) std_alloc((char **)&h_macro, sizeof(struct macro), 20, &cnt_macro)) +#else +#define new_macro() ((struct macro *) st_alloc((char **)&h_macro, sizeof(struct macro), 20)) +#endif +#define free_macro(p) st_free(p, &h_macro, sizeof(struct macro)) + + +/* `token' numbers of keywords of command-line processor +*/ +#define K_UNKNOWN 0 +#define K_DEFINE 1 +#define K_ELIF 2 +#define K_ELSE 3 +#define K_ENDIF 4 +#define K_IF 5 +#define K_IFDEF 6 +#define K_IFNDEF 7 +#define K_INCLUDE 8 +#define K_LINE 9 +#define K_UNDEF 10 diff --git a/util/cpp/main.c b/util/cpp/main.c new file mode 100644 index 000000000..fc581c3b1 --- /dev/null +++ b/util/cpp/main.c @@ -0,0 +1,65 @@ +/* MAIN PROGRAM */ + +#include "file_info.h" +#include "idfsize.h" + +extern struct tokenname tkidf[], tkkey[]; +extern char *symbol2str(); +extern char *inctable[]; +extern int err_occurred; +int idfsize = IDFSIZE; + +int ifval; + +char *prog_name; + +main(argc, argv) + char *argv[]; +{ + /* parse and interpret the command line options */ + prog_name = argv[0]; + + init_idf(); + init_pp(); /* initialise the preprocessor macros */ + + /* Note: source file "-" indicates that the source is supplied + as standard input. This is only allowed if INP_READ_IN_ONE is + not defined! + */ + while (argc > 1 && *argv[1] == '-' && argv[1][1] != '\0') { + char *par = &argv[1][1]; + + if (*par == '-') + par++; + do_option(par); + argc--, argv++; + } + compile(argc - 1, &argv[1]); + return err_occurred; +} + +compile(argc, argv) + char *argv[]; +{ + register char *source = 0; + char *dummy; + + switch (argc) { + case 1: + source = argv[0]; + FileName = source; + break; + case 0: + FileName = ""; + break; + default: + fatal("use: %s [options] [source]", prog_name); + break; + } + WorkingDir = inctable[0]; + + if (!InsertFile(source, (char **) 0, &dummy)) /* read the source file */ + fatal("%s: no source file %s\n", prog_name, + source ? source : "stdin"); + preprocess(source); +} diff --git a/util/cpp/make.hfiles b/util/cpp/make.hfiles new file mode 100755 index 000000000..2132dd618 --- /dev/null +++ b/util/cpp/make.hfiles @@ -0,0 +1,35 @@ +: Update Files from database + +PATH=/bin:/usr/bin + +case $# in +1) ;; +*) echo use: $0 file >&2 + exit 1 +esac + +( +IFCOMMAND="if (<\$FN) 2>/dev/null;\ + then if cmp -s \$FN \$TMP;\ + then rm \$TMP;\ + else mv \$TMP \$FN;\ + echo update \$FN;\ + fi;\ + else mv \$TMP \$FN;\ + echo create \$FN;\ + fi" +echo 'TMP=.uf$$' +echo 'FN=$TMP' +echo 'cat >$TMP <<\!EOF!' +sed -n '/^!File:/,${ +/^$/d +/^!File:[ ]*\(.*\)$/s@@!EOF!\ +'"$IFCOMMAND"'\ +FN=\1\ +cat >$TMP <<\\!EOF!@ +p +}' $1 +echo '!EOF!' +echo $IFCOMMAND +) | +sh diff --git a/util/cpp/make.tokcase b/util/cpp/make.tokcase new file mode 100755 index 000000000..ef32292f9 --- /dev/null +++ b/util/cpp/make.tokcase @@ -0,0 +1,34 @@ +cat <<'--EOT--' +#include "Lpars.h" + +char * +symbol2str(tok) + int tok; +{ + static char buf[2] = { '\0', '\0' }; + + if (040 <= tok && tok < 0177) { + buf[0] = tok; + buf[1] = '\0'; + return buf; + } + switch (tok) { +--EOT-- +sed ' +/{[A-Z]/!d +s/.*{\(.*\),.*\(".*"\).*$/ case \1 :\ + return \2;/ +' +cat <<'--EOT--' + case '\n': + case '\f': + case '\v': + case '\r': + case '\t': + buf[0] = tok; + return buf; + default: + return "bad token"; + } +} +--EOT-- diff --git a/util/cpp/make.tokfile b/util/cpp/make.tokfile new file mode 100755 index 000000000..494b7e3cc --- /dev/null +++ b/util/cpp/make.tokfile @@ -0,0 +1,6 @@ +sed ' +/{[A-Z]/!d +s/.*{// +s/,.*// +s/.*/%token &;/ +' diff --git a/util/cpp/next.c b/util/cpp/next.c new file mode 100644 index 000000000..64ff5ff28 --- /dev/null +++ b/util/cpp/next.c @@ -0,0 +1,5 @@ +#include "debug.h" +struct macro *h_macro = 0; +#ifdef DEBUG +int cnt_macro = 0; +#endif diff --git a/util/cpp/options.c b/util/cpp/options.c new file mode 100644 index 000000000..c1ef969c7 --- /dev/null +++ b/util/cpp/options.c @@ -0,0 +1,119 @@ +/* USER-OPTION HANDLING */ + +#include +#include "idfsize.h" +#include "maxincl.h" +#include "class.h" +#include "macro.h" +#include "idf.h" + +char options[128]; /* one for every char */ +int inc_pos = 1; /* place where next -I goes */ +char *inctable[MAXINCL] = { /* list for includes */ + ".", + "/usr/include", + 0 +}; + +extern int idfsize; +int txt2int(); + +do_option(text) + char *text; +{ + switch(*text++) { + case '-': + options[*text] = 1; + break; + default: + error("illegal option: %c", text[-1]); + break; + case 'C' : /* comment output */ + options['C'] = 1; + break; + case 'D' : /* -Dname : predefine name */ + { + register char *cp = text, *name, *mactext; + + if (class(*cp) != STIDF) { + error("identifier missing in -D%s", text); + break; + } + name = cp; + while (*cp && in_idf(*cp)) + ++cp; + if (!*cp) /* -Dname */ + mactext = "1"; + else + if (*cp == '=') { /* -Dname=text */ + *cp++ = '\0'; /* end of name */ + mactext = cp; + } + else { /* -Dname?? */ + error("malformed option -D%s", text); + break; + } + macro_def(str2idf(name, 0), mactext, -1, strlen(mactext), NOFLAG); + break; + } + case 'I' : /* -Ipath : insert "path" into include list */ + if (*text) { + register int i = inc_pos++; + register char *new = text; + + while (new) { + register char *tmp = inctable[i]; + + inctable[i++] = new; + if (i == MAXINCL) + fatal("too many -I options"); + new = tmp; + } + } + else inctable[inc_pos] = 0; + break; + case 'M': /* maximum identifier length */ + idfsize = txt2int(&text); + if (*text) + error("malformed -M option"); + if (idfsize > IDFSIZE) { + warning("maximum identifier length is %d", IDFSIZE); + idfsize = IDFSIZE; + } + if (idfsize < 8) { + warning("minimum identifier length is 8"); + idfsize = 8; + } + break; + case 'P' : /* run preprocessor stand-alone, without #'s */ + options['P'] = 1; + break; + case 'U' : /* -Uname : undefine predefined */ + if (*text) { + register struct idf *idef = findidf(text); + + if (idef && idef->id_macro) { + free_macro(idef->id_macro); + idef->id_macro = (struct macro *) 0; + } + } + break; + } +} + +int +txt2int(tp) + char **tp; +{ + /* the integer pointed to by *tp is read, while increasing + *tp; the resulting value is yielded. + */ + register int val = 0; + register int ch; + + while (ch = **tp, ch >= '0' && ch <= '9') { + val = val * 10 + ch - '0'; + (*tp)++; + } + return val; +} diff --git a/util/cpp/preprocess.c b/util/cpp/preprocess.c new file mode 100644 index 000000000..935d178c7 --- /dev/null +++ b/util/cpp/preprocess.c @@ -0,0 +1,209 @@ +/* PREPROCESSOR DRIVER */ + +#include +#include "input.h" +#include "maxincl.h" +#include "obufsize.h" +#include "LLlex.h" +#include "class.h" +#include "idf.h" +#include "idfsize.h" +#include "bits.h" + +char _obuf[OBUFSIZE]; + +#ifdef DOBITS +char bits[128]; +#endif + +preprocess(fn) + char *fn; +{ + register int c; + register char *op = _obuf; + register char *ob = &_obuf[OBUFSIZE]; + int lineno = 0; + extern char options[]; + +#define flush(X) (sys_write(STDOUT,_obuf,X), op = _obuf) +#define echo(ch) (op != ob || flush(OBUFSIZE), *op++ = (ch)) +#define newline() echo('\n') + + for (;;) { + LineNumber++; + lineno++; + LoadChar(c); + while (c == '#') { + domacro(); + lineno++; + newline(); + LoadChar(c); + } + if (! options['P'] && + (lineno != LineNumber || fn != FileName)) { + char Xbuf[256]; + register char *p = Xbuf; + + fn = FileName; + lineno = LineNumber; + sprint(p, "#line %d \"%s\"\n", LineNumber, + FileName); + while (*p) echo(*p++); + } + for (;;) { + if (c & 0200) { + if (c == EOI) { + flush(op-_obuf); + return; + } + fatal("non-ascii character read"); + } + if (c == '/') { + LoadChar(c); + if (c == '*') { + NoUnstack++; + if (options['C']) { + echo('/'); + echo('*'); + } + for (;;) { + LoadChar(c); + if (class(c) == STNL) { + ++LineNumber; + ++lineno; + echo(c); + } + else if (c == EOI) { + flush(op - _obuf); + return; + } + else if (c == '*') { + if (options['C']) echo(c); + LoadChar(c); + if (c == '/') { + if (options['C']) echo(c); + break; + } + else { + PushBack(); + } + } + else if (options['C']) echo(c); + } + NoUnstack--; + LoadChar(c); + continue; + } + echo('/'); + continue; + } + switch(class(c)) { + case STNL: + echo(c); + break; + case STSTR: + case STCHAR: { + register int stopc = c; + + do { + echo(c); + LoadChar(c); + if (c == '\\') { + echo(c); + LoadChar(c); + } + if (c == '\n') { + ++LineNumber; + lineno++; + } + else if (c == EOI) { + flush(op-_obuf); + return; + } + } + while (c != stopc); + echo(c); + LoadChar(c); + continue; + } + case STNUM: +#define getdec(c) do { echo(c); LoadChar(c);} while (is_dig(c)) +#define gethex(c) do { echo(c); LoadChar(c);} while (is_hex(c)) + + if (c != '0') { + getdec(c); + if (c == '.') getdec(c); + if (c == 'e') { + echo(c); + LoadChar(c); + if (c == '+' || c == '-') { + echo(c); + LoadChar(c); + } + if (is_dig(c)) getdec(c); + } + } + else { + echo(c); + LoadChar(c); + if (c == 'x' || c == 'X') gethex(c); + else if (is_dig(c)) getdec(c); + } + continue; + case STIDF: { + extern int idfsize; /* ??? */ + char buf[IDFSIZE + 1]; + register char *tg = &buf[0]; + register char *maxpos = &buf[idfsize]; + register struct idf *idef; + +#define tstmac(bx) if (!(bits[c] & bx)) goto nomac +#define cpy if (Unstacked) EnableMacros(); *tg++ = c +#define load LoadChar(c); if (!in_idf(c)) goto endidf + +#ifdef DOBITS + cpy; tstmac(bit0); load; + cpy; tstmac(bit1); load; + cpy; tstmac(bit2); load; + cpy; tstmac(bit3); load; + cpy; tstmac(bit4); load; + cpy; tstmac(bit5); load; + cpy; tstmac(bit6); load; + cpy; tstmac(bit7); load; +#endif + + for(;;) { + if (tg < maxpos) { + cpy; + } + load; + } + endidf: + PushBack(); + *tg = '\0'; /* mark the end of the identifier */ + idef = findidf(buf); + if ((idef && idef->id_macro && replace(idef))) { + LoadChar(c); + continue; + } + nomac: + *tg = 0; + tg = buf; + while (*tg) echo(*tg++); + LoadChar(c); + while (in_idf(c)) { + echo(c); + LoadChar(c); + } + continue; + } + default: + echo(c); + LoadChar(c); + continue; + } + break; + } + } + /*NOTREACHED*/ +} diff --git a/util/cpp/replace.c b/util/cpp/replace.c new file mode 100644 index 000000000..e8e78d301 --- /dev/null +++ b/util/cpp/replace.c @@ -0,0 +1,196 @@ +/* PREPROCESSOR: MACRO-TEXT REPLACEMENT ROUTINES */ + +#include "debug.h" /* UF */ +#include "pathlength.h" /* UF */ +#include "textsize.h" /* UF */ + +#include +#include +#include +#include "idf.h" +#include "input.h" +#include "macro.h" +#include "LLlex.h" +#include "class.h" +#include "interface.h" + +char *strcpy(), *strcat(); +char *long2str(); + +PRIVATE struct macro *ReplaceList; /* list of currently active macros */ + +EXPORT int +replace(idef) + register struct idf *idef; +{ + /* replace() is called by the lexical analyzer to perform + macro replacement. "idef" is the description of the + identifier which leads to the replacement. If the + optional actual parameters of the macro are OK, the text + of the macro is prepared to serve as an input buffer, + which is pushed onto the input stack. + replace() returns 1 if the replacement succeeded and 0 if + some error has occurred. + */ + register struct macro *mac = idef->id_macro; + register char c; + char **actpars, **getactuals(); + char *reptext, *macro2buffer(); + int size; + + if (mac->mc_flag & NOREPLACE) { + warning("macro %s is recursive", idef->id_text); + return 0; + } + if (mac->mc_nps != -1) { /* with parameter list */ + if (mac->mc_flag & FUNC) { + /* must be "defined". + Unfortunately, the next assertion + will not compile ... + assert( ! strcmp("defined", idef->id_text)); + */ + if (! AccDefined) + return 0; + } + if (++mac->mc_count > 100) { + /* 100 must be some number in Parameters */ + warning("macro %s is assumed recursive", + idef->id_text); + return 0; + } + LoadChar(c); + c = skipspaces(c); + if (c != '(') { /* no replacement if no () */ + error("macro %s needs arguments", + idef->id_text); + PushBack(); + return 0; + } + actpars = getactuals(idef); /* get act.param. list */ + if (mac->mc_flag & FUNC) { + struct idf *param = findidf(*actpars); + + if (param && param->id_macro) + reptext = "1"; + else + reptext = "0"; + InsertText(reptext, 1); + mac->next = ReplaceList; + ReplaceList = mac; + return 1; + } + } + if (mac->mc_flag & FUNC) /* this macro leads to special action */ + macro_func(idef); + if (mac->mc_nps <= 0) { + mac->mc_flag |= NOREPLACE; + reptext = mac->mc_text; + size = mac->mc_length; + } + else reptext = macro2buffer(idef, actpars, &size); /* create input buffer */ + InsertText(reptext, size); + mac->next = ReplaceList; + ReplaceList = mac; + return 1; +} + +char FilNamBuf[PATHLENGTH]; + +PRIVATE +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; + + switch (idef->id_text[2]) { /* This switch is very blunt... */ + case 'F' : /* __FILE__ */ + mac->mc_length = strlen(FileName) + 2; + mac->mc_text = FilNamBuf; + mac->mc_text[0] = '"'; + strcpy(&(mac->mc_text[1]), FileName); + strcat(mac->mc_text, "\""); + break; + case 'L' : /* __LINE__ */ + { + mac->mc_text = long2str((long) LineNumber, 10); + mac->mc_length = strlen(mac->mc_text); + break; + } + default : + crash("(macro_func)"); + } +} + +PRIVATE char * +macro2buffer(idef, actpars, siztext) + struct idf *idef; + char **actpars; + int *siztext; +{ + /* Macro2buffer() turns the macro replacement text, as it is + stored, into an input buffer, while each occurrence of the + non-ascii formal parameter mark is replaced by its + corresponding actual parameter specified in the actual + parameter list actpars. A pointer to the beginning of the + constructed text is returned, while *siztext is filled + with its length. + If there are no parameters, this function behaves + the same as strcpy(). + */ + register int size = idef->id_macro->mc_length + ITEXTSIZE; + register char *text = Malloc(size); + register int pos = 0; + register char *ptr = idef->id_macro->mc_text; + + while (*ptr) { + if (*ptr & FORMALP) { /* non-asc formal param. mark */ + register int n = *ptr++ & 0177; + register char *p; + + assert(n != 0); + /* copy the text of the actual parameter + into the replacement text + */ + for (p = actpars[n - 1]; p && *p; p++) { + text[pos++] = *p; + if (pos == size) + text = Srealloc(text, size += RTEXTSIZE); + } + } + else { + text[pos++] = *ptr++; + if (pos == size) + text = Srealloc(text, size += RTEXTSIZE); + } + } + text[pos] = '\0'; + *siztext = pos; + return text; +} + +EXPORT +DoUnstack() +{ + Unstacked++; +} + +EXPORT +EnableMacros() +{ + register struct macro *p = ReplaceList; + + assert(Unstacked > 0); + while (Unstacked > 0) { + assert(p != 0); + p->mc_flag &= ~NOREPLACE; + p->mc_count = 0; + p = p->next; + Unstacked--; + } + ReplaceList = p; +} diff --git a/util/cpp/scan.c b/util/cpp/scan.c new file mode 100644 index 000000000..8be12de24 --- /dev/null +++ b/util/cpp/scan.c @@ -0,0 +1,224 @@ +/* PREPROCESSOR: SCANNER FOR THE ACTUAL PARAMETERS OF MACROS */ + +/* This file contains the function getactuals() which scans an actual + parameter list and splits it up into a list of strings, each one + representing an actual parameter. +*/ + +#include "lapbuf.h" /* UF */ +#include "nparams.h" /* UF */ + +#include "input.h" +#include "class.h" +#include "idf.h" +#include "macro.h" +#include "interface.h" + +#define EOS '\0' +#define overflow() (fatal("actual parameter buffer overflow")) + +PRIVATE char apbuf[LAPBUF]; /* temporary storage for actual parameters */ +PRIVATE char *actparams[NPARAMS]; /* pointers to the text of the actuals */ +PRIVATE char *aptr; /* pointer to last inserted character in apbuf */ + +#define copy(ch) ((aptr < &apbuf[LAPBUF]) ? (*aptr++ = ch) : overflow()) + +PRIVATE int nr_of_params; /* number of actuals read until now */ + +PRIVATE char ** +getactuals(idef) + struct idf *idef; +{ + /* getactuals() collects the actual parameters and turns them + into a list of strings, a pointer to which is returned. + */ + register acnt = idef->id_macro->mc_nps; + + nr_of_params = 0; + actparams[0] = aptr = &apbuf[0]; + copyact('(', ')', 0); /* read the actual parameters */ + copy(EOS); /* mark the end of it all */ + + if (!nr_of_params++) { /* 0 or 1 parameter */ + /* there could be a ( ) + */ + register char *p = actparams[0]; + + while ((class(*p) == STSKIP) || (*p == '\n')) { + ++p; + } + + if (!*p) { /* the case () : 0 parameters */ + nr_of_params--; + } + } + + if (nr_of_params != acnt) { + /* argument mismatch: too many or too few + actual parameters. + */ + error("argument mismatch, %s", idef->id_text); + + while (++nr_of_params < acnt) { + /* too few paraeters: remaining actuals are "" + */ + actparams[nr_of_params] = (char *) 0; + } + } + + return actparams; +} + +PRIVATE +copyact(ch1, ch2, level) + char ch1, ch2; + int level; +{ + /* copyact() is taken from Ceriel Jacobs' LLgen, with + permission. Its task is to build a list of actuals + parameters, which list is surrounded by '(' and ')' and in + which the parameters are separated by ',' if there are + more than 1. The balancing of '(',')' and '[',']' and + '{','}' is taken care of by calling this function + recursively. At each level, copyact() reads the input, + upto the corresponding closing bracket. + + Opening bracket is ch1, closing bracket is ch2. If + level != 0, copy opening and closing parameters too. + */ + register int ch; /* Current char */ + register int match; /* used to read strings */ + + if (level) { + copy(ch1); + } + + for (;;) { + LoadChar(ch); + + if (ch == ch2) { + if (level) { + copy(ch); + } + return; + } + + switch(ch) { + + case ')': + case '}': + case ']': + error("unbalanced parenthesis"); + break; + + case '(': + copyact('(', ')', level+1); + break; + + case '{': + /* example: + #define declare(v, t) t v + declare(v, union{int i, j; float r;}); + */ + copyact('{', '}', level+1); + break; + + case '[': + copyact('[', ']', level+1); + break; + + case '\n': + LoadChar(ch); + while (ch == '#') { + /* This piece of code needs some + explanation: consider the call of + the macro defined as: + #define sum(b,c) (b + c) + in the following form: + sum( + #include my_phone_number + ,2) + in which case the include must be + interpreted as such. + */ + domacro(); /* has read nl, vt or ff */ + LoadChar(ch); + /* Loop, for another control line */ + } + + PushBack(); + copy('\n'); + break; + + case '/': + LoadChar(ch); + + if (ch == '*') { /* skip comment */ + skipcomment(); + continue; + } + + PushBack(); + copy('/'); + break; + + case ',': + if (!level) { + /* next parameter encountered */ + copy(EOS); + + if (++nr_of_params >= NPARAMS) { + fatal("(getact) too many actuals"); + } + + actparams[nr_of_params] = aptr; + } + else { + copy(ch); + } + break; + + case '\'': + case '"' : + /* watch out for brackets in strings, they do + not count ! + */ + match = ch; + copy(ch); + LoadChar(ch); + while (ch != EOI) { + if (ch == match) { + break; + } + + if (ch == '\\') { + copy(ch); + LoadChar(ch); + } + else + if (ch == '\n') { + error("newline in string"); + copy(match); + break; + } + + copy(ch); + LoadChar(ch); + } + + if (ch == match) { + copy(ch); + break; + } + /* Fall through */ + + case EOI : + error("unterminated macro call"); + return; + + default: + copy(ch); + break; + } + } +} diff --git a/util/cpp/skip.c b/util/cpp/skip.c new file mode 100644 index 000000000..636e79b31 --- /dev/null +++ b/util/cpp/skip.c @@ -0,0 +1,64 @@ +/* $Header$ */ +/* PREPROCESSOR: INPUT SKIP FUNCTIONS */ + +#include "LLlex.h" +#include "class.h" +#include "input.h" + +int +skipspaces(ch) + register int ch; +{ + /* skipspaces() skips any white space and returns the first + non-space character. + */ + for (;;) { + while (class(ch) == STSKIP) + LoadChar(ch); + /* How about "\\\n"????????? */ + if (ch == '/') { + LoadChar(ch); + if (ch == '*') { + skipcomment(); + LoadChar(ch); + } + else { + PushBack(); + return '/'; + } + } + else + return ch; + } +} + +skipline() +{ + /* skipline() skips all characters until a newline character + is seen, not escaped by a '\\'. + Any comment is skipped. + */ + register int c; + + LoadChar(c); + while (class(c) != STNL && c != EOI) { + if (c == '\\') { + LoadChar(c); + if (class(c) == STNL) + ++LineNumber; + } + if (c == '/') { + LoadChar(c); + if (c == '*') + skipcomment(); + else + continue; + } + LoadChar(c); + } + ++LineNumber; + if (c == EOI) { /* garbage input... */ + error("unexpected EOF while skipping text"); + PushBack(); + } +} diff --git a/util/cpp/tokenname.c b/util/cpp/tokenname.c new file mode 100644 index 000000000..82fc1f551 --- /dev/null +++ b/util/cpp/tokenname.c @@ -0,0 +1,51 @@ +/* $Header$ */ +/* TOKEN NAME DEFINITIONS */ + +#include "idf.h" +#include "LLlex.h" +#include "Lpars.h" + +struct tokenname { /* Used for defining the name of a + token as identified by its symbol + */ + int tn_symbol; + char *tn_name; +}; + +/* To centralize the declaration of %tokens, their presence in this + file is taken as their declaration. The Makefile will produce + a grammar file (tokenfile.g) from this file. + Moreover, rather than looking up a symbol in all these lists + to find its printable name, a fast version of symbol2str() is + generated from these tables. + Consequenty some of these tables are not referenced explicitly + in the C text any more. To save space and to avoid lint confusion, + these have been made pseudo-invisible by #ifdefs. +*/ + +#ifdef ____ +struct tokenname tkspec[] = { /* the names of the special tokens */ + {IDENTIFIER, "identifier"}, + {STRING, "string"}, + {FILESPECIFIER, "filespecifier"}, + {INTEGER, "integer"}, + {0, ""} +}; + +struct tokenname tkcomp[] = { /* names of the composite tokens */ + {NOTEQUAL, "!="}, + {AND, "&&"}, + {LEFT, "<<"}, + {LESSEQ, "<="}, + {EQUAL, "=="}, + {GREATEREQ, ">="}, + {RIGHT, ">>"}, + {OR, "||"}, + {0, ""} +}; + +struct tokenname tkfunny[] = { /* internal keywords */ + {ERRONEOUS, "erroneous"}, + {0, ""} +}; +#endif ____