diff options
Diffstat (limited to 'bcc/preproc.c')
-rw-r--r-- | bcc/preproc.c | 944 |
1 files changed, 944 insertions, 0 deletions
diff --git a/bcc/preproc.c b/bcc/preproc.c new file mode 100644 index 0000000..90f5a8c --- /dev/null +++ b/bcc/preproc.c @@ -0,0 +1,944 @@ +/* preprocessor routines for bcc */ + +/* Copyright (C) 1992 Bruce Evans */ + +#include "const.h" +#include "types.h" +#include "input.h" +#include "os.h" +#include "output.h" +#include "parse.h" +#include "sc.h" +#include "scan.h" +#include "table.h" +#include "type.h" + +#define MAX_IF 32 +#define MAX__LINE__ 10 /* enough for 32-bit source unsigneds */ +#define MAX_MACRO 32 +#define MAX_PARAM 127 /* max char with no sign on all machines */ + +/* Definition types. These are kept in the 'storage' member of struct + * symstruct and must be distinct from 'LOCAL' because dumplocs() doesn't + * check. + */ +enum +{ + DEF_LINE, /* __LINE__ keyword */ + DEF_NONE /* nothing special */ +}; + +struct ifstruct +{ + bool_t elseflag; + bool_t ifflag; +}; + +struct macroposition +{ + char *maclineptr; + char **paramlist; + char *paramspot; + bool_t inparam; + indn_t nparam; +}; + +PRIVATE char dummyparam[] = { EOL, 0 }; +PRIVATE fastin_t iflevel; /* depends on zero init */ +PRIVATE struct ifstruct ifstate; + /* elseflag depends on zero init */ +PRIVATE struct ifstruct ifstack[MAX_IF]; + +PRIVATE struct macroposition macrostack[MAX_MACRO]; + +FORWARD void asmcontrol P((void)); +FORWARD void control P((void)); +FORWARD void defineorundefinestring P((char *str, bool_pt defineflag)); +FORWARD void elsecontrol P((void)); +FORWARD void endif P((void)); +FORWARD fastin_pt getparnames P((void)); +FORWARD void ifcontrol P((sym_pt ifcase)); +FORWARD void undef P((void)); + +/* asmcontrol() - process #asm */ + +PRIVATE void asmcontrol() +{ + char treasure; /* to save at least one leading blank */ + + asmmode = TRUE; + if (orig_cppmode) + outstr("#asm\n"); + else + dumplocs(); + while (TRUE) + { + skipline(); + skipeol(); + if (eof) + { + eofin("#asm"); + break; + } + if (SYMOFCHAR(ch) == SPECIALCHAR) + specialchar(); + treasure = 0; + if (SYMOFCHAR(ch) == WHITESPACE) + treasure = ch; + blanks(); + if (ch == '#') + { + if (ctext) + { + register char *lptr; + + comment(); + if (treasure != 0) + outbyte(treasure); + lptr = lineptr; + while (*lptr++ != EOL) /* XXX - handle COEOL too */ + outbyte(ch); + outnl(); + } + gch1(); + docontrol(); + if (!asmmode) + break; + } + else + { + if (treasure != 0) + outbyte(treasure); + while (ch != EOL) /* XXX - handle COEOL too */ + { + outbyte(ch); + gch1(); + } + outnl(); + } + } + if (orig_cppmode) + outstr("#endasm"); /* nl is done by skipeol */ +} + +/* blanksident() - return nonzero if at blanks followed by an identifier */ + +PUBLIC bool_pt blanksident() +{ + blanks(); + return isident(); +} + +PUBLIC void checknotinif() +{ + while (iflevel != 0) + { + if (ifstate.ifflag) + eofin("true #conditional"); + else + eofin("false #conditional"); + endif(); + } +} + +/* control() - select and switch to control statement */ + +PRIVATE void control() +{ + char sname[NAMESIZE + 1]; + sym_t ctlcase; + struct symstruct *symptr; + + sname[0] = '#'; /* prepare for bad control */ + sname[1] = 0; + while (blanksident()) + { + if ((gsymptr = findlorg(gsname)) == NULL || + gsymptr->flags != DEFINITION) + { + strcat(sname, gsname); + break; + } + entermac(); + } + if (sname[1] == 0 && ch == EOL) + return; + if (SYMOFCHAR(ch) == INTCONST) + { linecontol(); return; } + if ((symptr = findlorg(sname)) == NULL) + { + if (ifstate.ifflag) + error(" bad control"); + return; + } + ctlcase = symptr->offset.offsym; + if (ifstate.ifflag == FALSE && + (ctlcase < ELSECNTL || ctlcase > IFNDEFCNTL)) + return; + switch (ctlcase) + { + case ASMCNTL: + asmcontrol(); + break; + case DEFINECNTL: + define(); + break; + case ELSECNTL: + elsecontrol(); + break; + case ENDASMCNTL: + asmmode = FALSE; + break; + case ENDIFCNTL: + endif(); + break; + case IFCNTL: + case IFDEFCNTL: + case IFNDEFCNTL: + ifcontrol(symptr->offset.offsym); + break; + case INCLUDECNTL: + include(); + break; + case LINECNTL: + { linecontol(); break; } + case UNDEFCNTL: + undef(); + break; + } +} + +/* define() - process #define */ + +/* + MACRO storage. + A symbol recording the macro name is added to the symbol table. + This overrides all current scopes of the name (put at head of hash chain). + The flags are set to DEFINITION. + The indcount is 0 if no parameters, else 1 + number of parameters. + The offset field points to the macro string. + The macro string is null-terminated but EOL-sentineled. + It consists of EOL-terminated substrings followed by parameter numbers, + e.g., junk(x,y)=-x+y is stored as '-', EOL, 1, '+', EOL, 2, EOL, 0. + Here 1 is for the 1st parameter (start at 1 so 0 can terminate). + EOL acts as a sentinel for the scanner. + This choice works well because EOL cannot occur in a macro string. +*/ + +PUBLIC void define() +{ + char sname[NAMESIZE]; + char quote; + struct symstruct **hashptr; + struct symstruct *locmark = NULL; /* for -Wall */ + char *macstring; + fastin_t nparnames; + char *oldstring; + struct symstruct *symptr; + + if (!blanksident()) + { + error("illegal macro name"); + return; + } + strcpy(sname, gsname); /* will overwrite gsname if parameters */ + if (ch == '(') + { + locmark = locptr; + newlevel(); /* temp storage for parameter names */ + nparnames = getparnames() + 1; + } + else + nparnames = 0; + blanks(); + macstring = charptr; + quote = 0; + while (ch != EOL) + { + if (charptr >= char1top) /* check room for char and end of string */ + macstring = growobject(macstring, 2); + if (nparnames && isident()) + { + if ((symptr = findlorg(gsname)) != NULL && + symptr->level == level) + { +#ifdef TS +++ts_n_macstring_param; +ts_s_macstring += 2; +#endif + *charptr++ = EOL; /* end current string */ + *charptr++ = symptr->indcount; /* param to insert */ + } + else + { + if (charptr + strlen(gsname) >= chartop) /* null too */ + macstring = growobject(macstring, strlen(gsname) + 1); +#ifdef TS +++ts_n_macstring_ident; +ts_s_macstring += strlen(gsname);; +#endif + strcpy(charptr, gsname); + charptr += strlen(gsname); /* discard null */ + } + continue; + } + if (ch == '\\') + { + gch1(); + *charptr = '\\'; + *(charptr + 1) = ch; +#ifdef TS +++ts_n_macstring_quoted; +ts_s_macstring += 2; +#endif + charptr += 2; + gch1(); + continue; + } + if (quote) + { + if (ch == quote) + quote = 0; + } + else if (ch == '"' || ch == '\'') + quote = ch; + else if (ch == '/') + { + if (SYMOFCHAR(*(lineptr + 1)) == SPECIALCHAR) + { + gch1(); + ch = *--lineptr = '/'; /* pushback */ + } + if (*(lineptr + 1) == '*') + { + gch1(); + skipcomment(); + ch = *--lineptr = ' '; /* comment is space in modern cpp's */ + } + } +#ifdef TS +++ts_n_macstring_ordinary; +++ts_s_macstring; +#endif + *charptr++ = ch; + gch1(); + } + { + register char *rcp; + + /* strip trailing blanks, but watch out for parameters */ + for (rcp = charptr; + rcp > macstring && SYMOFCHAR(*(rcp - 1)) == WHITESPACE + && (--rcp == macstring || *(rcp - 1) != EOL); ) + charptr = rcp; + } + if (charptr >= char1top) + macstring = growobject(macstring, 2); +#ifdef TS +++ts_n_macstring_term; +ts_s_macstring += 2; +#endif + *charptr++ = EOL; + *charptr++ = 0; + if (nparnames) + { + oldlevel(); + locptr = locmark; + } + if (asmmode) + equ(sname, macstring); + + if ((symptr = findlorg(sname)) != NULL && symptr->flags == DEFINITION) + { + if (strcmp(macstring, oldstring = symptr->offset.offp) != 0) + error("%wredefined macro"); + if (strlen(macstring) > strlen(oldstring = symptr->offset.offp)) + symptr->offset.offp = macstring; + else + { + strcpy(oldstring, macstring); /* copy if == to avoid test */ + charptr = macstring; + } + return; + } + symptr = qmalloc(sizeof (struct symstruct) + strlen(sname)); +#ifdef TS +++ts_n_defines; +ts_s_defines += sizeof (struct symstruct) + strlen(sname); +#endif + addsym(sname, vtype, symptr); + symptr->storage = DEF_NONE; + symptr->indcount = nparnames; + symptr->flags = DEFINITION; + symptr->level = GLBLEVEL; + symptr->offset.offp = macstring; + if (*(hashptr = gethashptr(sname)) != NULL) + { + symptr->next = *hashptr; + symptr->next->prev = &symptr->next; + } + *hashptr = symptr; + symptr->prev = hashptr; +} + +PRIVATE void defineorundefinestring(str, defineflag) +char *str; /* "name[=def]" or "name def" */ +bool_pt defineflag; +{ + char *fakeline; + unsigned len; + bool_t old_eof; + + len = strlen(str); + strcpy(fakeline = (char *) ourmalloc(3 + len + 2 + 2) + 3, str); + /* 3 pushback, 2 + 2 guards */ +#ifdef TS +ts_s_fakeline += 3 + len + 2 + 2; +ts_s_fakeline_tot += 3 + len + 2 + 2; +#endif + { + register char *endfakeline; + + endfakeline = fakeline + len; + endfakeline[0] = EOL; /* guards any trailing backslash */ + endfakeline[1] = EOL; /* line ends here or before */ + } + old_eof = eof; + eof = TRUE; /* valid after first EOL */ + ch = *(lineptr = fakeline); + if (defineflag) + { + if (blanksident()) /* if not, let define() produce error */ + { + blanks(); + if (ch == '=') + *lineptr = ' '; + else if (ch == EOL) + { + register char *lptr; + + lptr = lineptr; + lptr[0] = ' '; + lptr[1] = '1'; /* 2 extra were allocated for this & EOL */ + lptr[2] = EOL; + } + } + ch = *(lineptr = fakeline); + define(); + } + else + undef(); + eof = old_eof; +#ifdef TS +ts_s_fakeline_tot -= len + 2 + 2; +#endif + ourfree(fakeline - 3); +} + +PUBLIC void definestring(str) +char *str; /* "name[=def]" or "name def" */ +{ + defineorundefinestring(str, TRUE); +} + +/* docontrol() - process control statement, loop till "#if" is true */ + +PUBLIC void docontrol() +{ + while (TRUE) + { + control(); + skipline(); + if (ifstate.ifflag) + return; + while (TRUE) + { + skipeol(); + if (eof) + return; + blanks(); + if (ch == '#') + { + gch1(); + break; + } + skipline(); + } + } +} + +/* elsecontrol() - process #else */ + +PRIVATE void elsecontrol() +{ + if (iflevel == 0) + { + error("else without if"); + return; + } + ifstate.ifflag = ifstate.elseflag; + ifstate.elseflag = FALSE; +} + +/* endif() - process #endif */ + +PRIVATE void endif() +{ + if (iflevel == 0) + { + error("endif without if"); + return; + } + { + register struct ifstruct *ifptr; + + ifptr = &ifstack[(int)--iflevel]; + ifstate.elseflag = ifptr->elseflag; + ifstate.ifflag = ifptr->ifflag; + } +} + +/* entermac() - switch line ptr to macro string */ + +PUBLIC void entermac() +{ + char quote; + struct symstruct *symptr; + char **paramhead; + char **paramlist; + int ngoodparams; + int nparleft; + int lpcount; + + if (maclevel >= MAX_MACRO) + { + limiterror("macros nested too deeply (33 levels)"); + return; + } + symptr = gsymptr; + ngoodparams = 0; + paramhead = NULL; + if (symptr->indcount != 0) + { + nparleft = symptr->indcount - 1; + if (nparleft == 0) + paramhead = NULL; + else + paramhead = ourmalloc(sizeof *paramlist * nparleft); + paramlist = paramhead; +#ifdef TS +++ts_n_macparam; +ts_s_macparam += sizeof *paramlist * nparleft; +ts_s_macparam_tot += sizeof *paramlist * nparleft; +#endif + blanks(); + while (ch == EOL && !eof) + { + skipeol(); + blanks(); + } + if (ch != '(') + error("missing '('"); + else + { + gch1(); + while (nparleft) + { + --nparleft; + ++ngoodparams; + *(paramlist++) = charptr; + quote = 0; + lpcount = 1; + while (TRUE) + { + if (ch == '\\') + { + gch1(); + if (charptr >= char1top) + *(paramlist - 1) = growobject(*(paramlist - 1), 2); +#ifdef TS +++ts_n_macparam_string_quoted; +++ts_s_macparam_string; +++ts_s_macparam_string_tot; +#endif + *charptr++ = '\\'; + } + else if (quote) + { + if (ch == quote) + quote = 0; + } + else if (ch == '"' || ch == '\'') + quote = ch; + else if (ch == '/') + { + if (SYMOFCHAR(*(lineptr + 1)) == SPECIALCHAR) + { + gch1(); + ch = *--lineptr = '/'; /* pushback */ + } + if (*(lineptr + 1) == '*') + { + gch1(); + skipcomment(); + ch = *--lineptr = ' '; /* pushback */ + } + } + else if (ch == '(') + ++lpcount; + else if ((ch == ')' && --lpcount == 0) || + (ch == ',' && lpcount == 1)) + break; + if (ch == EOL) + ch = ' '; + if (charptr >= char1top) + *(paramlist - 1) = growobject(*(paramlist - 1), 2); +#ifdef TS +++ts_n_macparam_string_ordinary; +++ts_s_macparam_string; +++ts_s_macparam_string_tot; +#endif + *charptr++ = ch; + if (*lineptr == EOL) + { + skipeol(); /* macro case disposed of already */ + if (SYMOFCHAR(ch) == SPECIALCHAR) + specialchar(); + if (eof) + break; + } + else + gch1(); + } +#ifdef TS +++ts_n_macparam_string_term; +++ts_s_macparam_string; +++ts_s_macparam_string_tot; +#endif + *charptr++ = EOL; + { + register char *newparam; + register char *oldparam; + unsigned size; + + oldparam = *(paramlist - 1); + size = (/* size_t */ unsigned) (charptr - oldparam); + newparam = ourmalloc(size); +#ifdef TS +ts_s_macparam_string_alloced += size; +ts_s_macparam_string_alloced_tot += size; +#endif + memcpy(newparam, oldparam, size); + *(paramlist - 1) = newparam; +#ifdef TS +ts_s_macparam_string_tot -= charptr - oldparam; +#endif + charptr = oldparam; + } + if (ch == ',') + gch1(); + else + break; + } + } + blanks(); + while (ch == EOL && !eof) + { + skipeol(); + blanks(); + } + if (eof) + eofin("macro parameter expansion"); + if (nparleft) + { + error("too few macro parameters"); + do + *(paramlist++) = dummyparam; + while (--nparleft); + } + if (ch == ')') + gch1(); + else if (ch == ',') + { + error("too many macro parameters"); + + /* XXX - should read and discard extra parameters. Also check + * for eof at end. + */ + while (ch != ')') + { + if (ch == EOL) + { + skipeol(); + if (eof) + break; + continue; + } + gch1(); + } + } + else + error("missing ')'"); + } + + if (symptr->storage == DEF_LINE) + { + char *str; + + str = pushudec(symptr->offset.offp + MAX__LINE__, input.linenumber); + memcpy(symptr->offset.offp, str, /* size_t */ + (unsigned) (symptr->offset.offp + MAX__LINE__ + 1 + 1 - str)); + } + + { + register struct macroposition *mpptr; + + mpptr = ¯ostack[maclevel]; + mpptr->paramlist = paramhead; + mpptr->maclineptr = lineptr; + ch = *(lineptr = symptr->offset.offp); + mpptr->inparam = FALSE; + mpptr->nparam = ngoodparams; + ++maclevel; + } +/* + comment(); + outstr("MACRO (level "); + outudec((unsigned) maclevel); + outstr(") "); + outline(lineptr); +*/ +} + +/* getparnames() - get parameter names during macro definition, return count */ + +PRIVATE fastin_pt getparnames() +{ + fastin_t nparnames; + struct symstruct *symptr; + + nparnames = 0; + gch1(); + while (blanksident()) + { + if ((symptr = findlorg(gsname)) != NULL && + symptr->level == level) + error("repeated parameter"); + symptr = addloc(gsname, itype); + if (nparnames >= MAX_PARAM) + limiterror("too many macro parameters (128)"); + else + ++nparnames; /* number params from 1 */ + symptr->indcount = nparnames; /* param number */ + blanks(); + if (ch == ',') + gch1(); + } + if (ch != ')') + error("missing ')'"); + else + gch1(); + return nparnames; +} + +/* ifcontrol - process #if, #ifdef, #ifndef */ + +PRIVATE void ifcontrol(ifcase) +sym_pt ifcase; +{ + bool_t iftrue; + struct symstruct *symptr; + + if (iflevel >= MAX_IF) + { + limiterror("#if's nested too deeply (33 levels)"); + return; + } + { + register struct ifstruct *ifptr; + + ifptr = &ifstack[(int)iflevel++]; + ifptr->elseflag = ifstate.elseflag; + ifptr->ifflag = ifstate.ifflag; + ifstate.elseflag = FALSE; /* prepare for !(if now)||(if future)*/ + } + if (ifstate.ifflag) + { + if ((sym_t) ifcase != IFCNTL) + { + iftrue = FALSE; + if (blanksident() && (symptr = findlorg(gsname)) != NULL && + symptr->flags == DEFINITION) + iftrue = TRUE; + } + else + { + incppexpr = TRUE; + nextsym(); + iftrue = constexpression() != 0; + incppexpr = FALSE; + } + if ((!iftrue && (sym_t) ifcase != IFNDEFCNTL) || + (iftrue && (sym_t) ifcase == IFNDEFCNTL)) + { + ifstate.elseflag = TRUE; + ifstate.ifflag = FALSE; + } + } +} + +/* ifinit() - initialise if state */ + +PUBLIC void ifinit() +{ + ifstate.ifflag = TRUE; +} + +/* leavemac() - leave current and further macro substrings till not at end */ + +PUBLIC void leavemac() +{ + register struct macroposition *mpptr; + + do + { + mpptr = ¯ostack[maclevel - 1]; + if (mpptr->inparam) + { + lineptr = ++mpptr->paramspot; + mpptr->inparam = FALSE; + } + else + { + ch = *++lineptr; /* gch1() would mess up next param == EOL-1 */ + if (ch != 0) + { + mpptr->paramspot = lineptr; + lineptr = mpptr->paramlist[ch - 1]; + mpptr->inparam = TRUE; + } + else + { + lineptr = mpptr->maclineptr; + if (mpptr->nparam != 0) + { + register char **paramlist; + +#ifdef TS +ts_s_macparam_tot -= sizeof *paramlist * mpptr->nparam; +#endif + paramlist = mpptr->paramlist; + do +{ +#ifdef TS +ts_s_macparam_string_alloced_tot -= strchr(*paramlist, EOL) - *paramlist + 1; +#endif + ourfree(*paramlist++); +} + while (--mpptr->nparam != 0); + ourfree(mpptr->paramlist); + } + --maclevel; + } + } + } + while ((ch = *lineptr) == EOL && maclevel != 0); +} + +PUBLIC void predefine() +{ + definestring("__BCC__ 1"); + definestring("__LINE__ 0123456789"); /* MAX__LINE__ digits */ + findlorg("__LINE__")->storage = DEF_LINE; +} + +PUBLIC char *savedlineptr() +{ + return macrostack[0].maclineptr; +} + +PUBLIC void skipcomment() +{ +/* Skip current char, then everything up to '*' '/' or eof. */ + + gch1(); + do + { + while (TRUE) + { + { + register char *reglineptr; + + reglineptr = lineptr; + symofchar['*'] = SPECIALCHAR; + while (SYMOFCHAR(*reglineptr) != SPECIALCHAR) + ++reglineptr; + symofchar['*'] = STAR; + lineptr = reglineptr; + if (*reglineptr == '*') + break; + ch = *reglineptr; + } + specialchar(); + if (ch == EOL) + { + skipeol(); + if (eof) + break; + } + else if (ch != '*') + gch1(); + } + gch1(); + if (eof) + { + eofin("comment"); + return; + } + } + while (ch != '/'); + gch1(); +} + +/* skipline() - skip rest of line */ + +PUBLIC void skipline() +{ + while (TRUE) + { + blanks(); + if (ch == EOL) + return; + if (ch == '\\') + { + gch1(); + if (ch == EOL) /* XXX - I think blanks() eats \EOL */ + return; + gch1(); /* XXX - escape() better? */ + } + else if (ch == '"' || ch == '\'') + { + stringorcharconst(); + charptr = constant.value.s; + } + else + gch1(); + } +} + +/* undef() - process #undef */ + +PRIVATE void undef() +{ + struct symstruct *symptr; + + if (blanksident() && (symptr = findlorg(gsname)) != NULL && + symptr->flags == DEFINITION) + delsym(symptr); +} + +PUBLIC void undefinestring(str) +char *str; +{ + defineorundefinestring(str, FALSE); +} |