/* * Phase 1: multibyte characters are mapped to source characters and * trigraphs are translated. * No-op for us, since we support neither multibyte source * characters nor trigraphs. * * Phase 2: backslash-newline is deleted (backslash-newline-EOF is an * error). * Temporally, we handle this at the same time as phase 3. But in * code structure terms, phase2 and phase3 are separate. * * Phase 3: break source into preprocessing tokens and whitespace. A * file shall not end in partial pptoken or partial comment. Comments * are conceptually replaced by single spaces. * * Phase 4: preprocessing directives (including #include) are executed, * including expanding macros. * * Phase 5: character constants and strings are converted from the * source character set to the execution character set. This is a * no-op for us. * * Phase 6: adjacent string literals are concatenated. * * Phase 7: preprocessing tokens are converted to tokens, including * discarding whitespace. Parsing and compilation proper happen. * We split phase 7 into phase 7a, the first sentence, and 7b, the * second. (This appears mostly in routine names.) * * Phase 8: linking. */ /* * Turn this on to get detailed tracking of SRCFILE references. This * can help greatly with tracking down certain kinds of leaks. * * With this not defined, they are refcounted, but individual * references are not tracked. */ //#define SFLOGGING #include #include #include #include #include #include #include #include "cclass.h" #include "verbose.h" #include "verbtypes.h" #include "stdio-util.h" extern const char *__progname; #define MAXP3UNGET 4 // never want to unget more than 4 chars #define MAXP4PUSH 4 // never need to push more than 4 pp-tokens typedef enum { PPT_EOF = 1, PPT_NEWLINE, PPT_WHITESPACE, PPT_HEADERNAME, PPT_IDENT, PPT_NUMBER, PPT_CHARCONST, PPT_STRING, PPT_PUNCT, PPT_OTHER, // other single character PPT_MARKER, // internal only - see 6.10 PPT_ENDMACRO, // internal only - not C99-specified PPT_SYNTH, // internal only - synthesized by ## PPT_MACROARG, // internal only - macro argument PPT_LINE, // internal only - rest of line } PPTOKTYPE; typedef enum { PPP_LBRACK = 1, // [ PPP_RBRACK, // ] PPP_LPAREN, // ( PPP_RPAREN, // ) PPP_LBRACE, // { PPP_RBRACE, // } PPP_DOT, // . PPP_ARROW, // -> PPP_PLUSPLUS, // ++ PPP_MINUSMINUS, // -- PPP_AND, // & PPP_STAR, // * PPP_PLUS, // + PPP_MINUS, // - PPP_TILDE, // ~ PPP_BANG, // ! PPP_SLASH, // / PPP_PERCENT, // % PPP_LSH, // << PPP_RSH, // >> PPP_LT, // < PPP_GT, // > PPP_LEQ, // <= PPP_GEQ, // >= PPP_EQ, // == PPP_NEQ, // != PPP_HAT, // ^ PPP_OR, // | PPP_ANDAND, // && PPP_OROR, // || PPP_QUESTION, // ? PPP_COLON, // : PPP_SEMI, // ; PPP_ELLIPSIS, // ... PPP_ASSIGN, // = PPP_MULASGN, // *= PPP_DIVASGN, // /= PPP_MODASGN, // %= PPP_ADDASGN, // += PPP_SUBASGN, // -= PPP_LSHASGN, // <<= PPP_RSHASGN, // >>= PPP_ANDASGN, // &= PPP_XORASGN, // ^= PPP_ORASGN, // |= PPP_COMMA, // , PPP_SHARP, // # PPP_SSHARP, // ## } PPPUNCTTYPE; typedef enum { P2S_NORMAL = 1, P2S_BACKSLASH, P2S_CONT, P2S_PUSHED, } P2STATE; typedef enum { IFS_FALSE = 1, IFS_TRUE, IFS_OVER, IFS_ELSE_F, IFS_ELSE_T, IFS_NEST, } IFSTATE; typedef long long int PPVAL; typedef struct srcfile SRCFILE; typedef struct pptoken PPTOKEN; typedef struct str STR; typedef struct ebuf EBUF; typedef struct istack ISTACK; typedef struct ifstack IFSTACK; typedef struct pptlist PPTLIST; typedef struct macro MACRO; typedef struct macroexpander MACROEXPANDER; typedef struct stringizer STRINGIZER; typedef struct pptconc PPTCONC; typedef struct concatter CONCATTER; typedef struct expreval EXPREVAL; typedef struct eflag_state EFLAG_STATE; typedef struct cl CL; typedef struct p2p3 P2P3; #ifdef SFLOGGING typedef struct sfref SFREF; struct sfref { SRCFILE *sf; SFREF *flink; SFREF *blink; char *text; } ; #else typedef SRCFILE *SFREF; #endif struct cl { int c; int l; } ; struct eflag_state { int line; SFREF file; int atnl; int atws; PPTOKTYPE lasttt; } ; struct pptconc { PPTLIST *head; PPTLIST **tail; } ; struct concatter { PPTCONC list; PPTCONC last[2]; } ; struct stringizer { void (*put)(unsigned char, void *); void *putarg; int started; } ; struct str { char *data; int len; int needsfree; } ; struct macro { MACRO *link; STR name; unsigned int flags; #define MF_HASARGS 0x00000001 #define MF_VARARGS 0x00000002 #define MF_DISABLED 0x00000004 int nargs; PPTOKEN **args; PPTLIST *replacement; } ; struct macroexpander { PPTCONC done; MACRO *curmac; PPTOKEN *name; PPTCONC arglist; int argnest; PPTLIST *pending; } ; struct ifstack { IFSTACK *link; SFREF start_sf; int start_line; IFSTATE state; } ; struct pptlist { PPTLIST *link; PPTOKEN *token; } ; struct istack { ISTACK *link; SFREF sf; } ; struct ebuf { unsigned char *b; int a; int l; } ; struct srcfile { SFREF link; char *name; FILE *f; int lno; #ifdef SFLOGGING SFREF *reflist; #endif int nrefs; } ; struct pptoken { PPTOKTYPE type; STR str; SFREF file; int startline; union { PPPUNCTTYPE punct; // PPT_PUNCT unsigned char other; // PPT_OTHER MACRO *macro; // PPT_ENDMACRO struct { // PPT_MACROARG int argno; unsigned int flags; #define MAF_NOEXPAND 0x00000001 } macroarg; } detail; } ; struct expreval { PPTLIST *list; void (*fail)(void); } ; struct p2p3 { P2STATE s2; CL backslash; CL push2; int n3unget; CL unget3[MAXP3UNGET]; EBUF buf3; PPTOKEN *push3; } ; static SFREF srcfiles; static int errs; static SFREF cursf_; static int startlno; static P2P3 p2p3; static MACROEXPANDER p4mx; static ISTACK *istack; static int atbol; static IFSTACK *ifstack; static MACRO *macros; static int p4npush; static PPTOKEN *p4pushed[MAXP4PUSH]; static unsigned int verbose; extern const VERBTYPE verbtypes[]; static int Eflag = 0; static EFLAG_STATE Estate; #define Cisdigit(x) isdigit((unsigned char)(x)) static void panic(void) __attribute__((__noreturn__)); static void panic(void) { (void)*(volatile char *)0; abort(); } #ifdef SFLOGGING static FILE *sflog(void) { static FILE *f = 0; if (f) return(f); f = fopen("sf.log","w"); if (! f) { fprintf(stderr,"can't open sf.log: %s\n",strerror(errno)); exit(1); } setlinebuf(f); return(f); } static void sfref_drop(SFREF *); // forward static void sfref_init(SFREF *, const char *, ...) __attribute__((__format__(__printf__,2,3))); static void sfref_init(SFREF *r, const char *fmt, ...) { va_list ap; char *s; va_start(ap,fmt); vasprintf(&s,fmt,ap); va_end(ap); r->sf = 0; r->flink = 0; r->blink = 0; r->text = s; } static void sfref_settext(SFREF *, const char *, ...) __attribute__((__format__(__printf__,2,3))); static void sfref_settext(SFREF *r, const char *fmt, ...) { va_list ap; char *s; va_start(ap,fmt); vasprintf(&s,fmt,ap); va_end(ap); free(r->text); r->text = s; } static void checknrefs(SRCFILE *sf) { SFREF *r; int n; n = 0; for (r=sf->reflist;r;r=r->flink) n ++; if (n != sf->nrefs) abort(); } static void sfref_addref(SFREF *ref, SRCFILE *to) { (void)*(volatile char *)ref->text; if (ref->sf) panic(); if (! to) return; to->nrefs ++; fprintf(sflog(),"add ref (%d) to %p from %s (%p)\n",to->nrefs,(void *)to,ref->text,(void *)ref); ref->sf = to; ref->flink = to->reflist; ref->blink = 0; if (to->reflist) to->reflist->blink = ref; to->reflist = ref; checknrefs(to); } static void sfref_delref(SFREF *ref, SRCFILE *to) { (void)*(volatile char *)ref->text; if (ref->sf != to) panic(); if (! to) return; to->nrefs --; fprintf(sflog(),"del ref (%d) to %p from %s (%p)\n",to->nrefs,(void *)to,ref->text,(void *)ref); ref->sf = 0; if (ref->flink) ref->flink->blink = ref->blink; if (ref->blink) ref->blink->flink = ref->flink; else to->reflist = ref->flink; ref->flink = 0; ref->blink = 0; checknrefs(to); if (! to->nrefs) { fprintf(sflog(),"freeing %p\n",(void *)to); sfref_drop(&to->link); free(to->name); free(to); } } static void sfref_drop(SFREF *ref) { (void)*(volatile char *)ref->text; if (! ref->sf) return; sfref_delref(ref,ref->sf); } static void sfref_done(SFREF *ref) { (void)*(volatile char *)ref->text; sfref_drop(ref); free(ref->text); } static SRCFILE *sfref_follow(SFREF *ref) { (void)*(volatile char *)ref->text; return(ref->sf); } #else // ie, ifndef SFLOGGING static void sfref_drop(SFREF *); // forward #define sfref_init(refp, ...) (*(refp) = 0) #define sfref_follow(refp) (*(refp)) #define sfref_settext(refp, ...) do { } while (0) static void sfref_addref(SFREF *ref, SRCFILE *to) { *ref = to; if (! to) return; to->nrefs ++; } static void sfref_delref(SFREF *ref, SRCFILE *to) { *ref = 0; to->nrefs --; if (to->nrefs > 0) return; if (to->nrefs < 0) panic(); sfref_drop(&to->link); free(to->name); free(to); } static void sfref_drop(SFREF *ref) { if (! *ref) return; sfref_delref(ref,*ref); } static void sfref_done(SFREF *ref) { sfref_drop(ref); } #endif static void error(const char *, ...) __attribute__((__format__(__printf__,1,2))); static void error(const char *fmt, ...) { va_list ap; fprintf(stderr,"%s: \"%s\", line %d: ",__progname,sfref_follow(&cursf_)->name,sfref_follow(&cursf_)->lno); va_start(ap,fmt); vfprintf(stderr,fmt,ap); va_end(ap); fprintf(stderr,"\n"); errs ++; } static void error_at(const char *, int, const char *, ...) __attribute__((__format__(__printf__,3,4))); static void error_at(const char *fn, int lno, const char *fmt, ...) { va_list ap; fprintf(stderr,"%s: \"%s\", line %d: ",__progname,fn,lno); va_start(ap,fmt); vfprintf(stderr,fmt,ap); va_end(ap); fprintf(stderr,"\n"); errs ++; } static void verbprintf(unsigned int, const char *, ...) __attribute__((__format__(__printf__,2,3))); static void verbprintf(unsigned int level, const char *fmt, ...) { va_list ap; if (! (verbose & level)) return; va_start(ap,fmt); vprintf(fmt,ap); va_end(ap); } static void ebuf_init(EBUF *eb) { eb->b = 0; eb->a = 0; eb->l = 0; } static int ebuf_len(EBUF *eb) { return(eb->l); } static void *ebuf_data(EBUF *eb) { return(eb->b); } static void ebuf_clear(EBUF *eb) { eb->l = 0; } static void ebuf_append1(EBUF *eb, unsigned char c) { if (eb->l >= eb->a) eb->b = realloc(eb->b,eb->a=eb->l+8); eb->b[eb->l++] = c; } static void ebuf_done(EBUF *eb) { free(eb->b); eb->b = 0; eb->a = 0; } static int set_verbosity(const char *s) { int i; if (!strcmp(s,"all")) { for (i=NVERB-1;i>=0;i--) verbose |= verbtypes[i].bit; return(0); } else { for (i=NVERB-1;i>=0;i--) { if (!strcmp(s,verbtypes[i].name)) { verbose |= verbtypes[i].bit; return(0); } } fprintf(stderr,"%s: unknown verbosity type `%s'\n",__progname,s); return(1); } } /* * #ifdef SFLOGGING because this is used only in an sfref_settext() * call, which doesn't actually use its arguments unless SFLOGGING is * turned on. */ #ifdef SFLOGGING static const char *pp_toktype_str(PPTOKTYPE tt) { switch (tt) { case PPT_EOF: return("PPT_EOF"); break; case PPT_NEWLINE: return("PPT_NEWLINE"); break; case PPT_WHITESPACE: return("PPT_WHITESPACE"); break; case PPT_HEADERNAME: return("PPT_HEADERNAME"); break; case PPT_IDENT: return("PPT_IDENT"); break; case PPT_NUMBER: return("PPT_NUMBER"); break; case PPT_CHARCONST: return("PPT_CHARCONST"); break; case PPT_STRING: return("PPT_STRING"); break; case PPT_PUNCT: return("PPT_PUNCT"); break; case PPT_OTHER: return("PPT_OTHER"); break; case PPT_MARKER: return("PPT_MARKER"); break; case PPT_ENDMACRO: return("PPT_ENDMACRO"); break; case PPT_SYNTH: return("PPT_SYNTH"); break; case PPT_MACROARG: return("PPT_MACROARG"); break; case PPT_LINE: return("PPT_LINE"); break; } panic(); } #endif static const char *pp_punct_str(PPPUNCTTYPE t) { switch (t) { case PPP_LBRACK: return("["); break; case PPP_RBRACK: return("]"); break; case PPP_LPAREN: return("("); break; case PPP_RPAREN: return(")"); break; case PPP_LBRACE: return("{"/*}*/); break; case PPP_RBRACE: return(/*{*/"}"); break; case PPP_DOT: return("."); break; case PPP_ARROW: return("->"); break; case PPP_PLUSPLUS: return("++"); break; case PPP_MINUSMINUS: return("--"); break; case PPP_AND: return("&"); break; case PPP_STAR: return("*"); break; case PPP_PLUS: return("+"); break; case PPP_MINUS: return("-"); break; case PPP_TILDE: return("~"); break; case PPP_BANG: return("!"); break; case PPP_SLASH: return("/"); break; case PPP_PERCENT: return("%"); break; case PPP_LSH: return("<<"); break; case PPP_RSH: return(">>"); break; case PPP_LT: return("<"); break; case PPP_GT: return(">"); break; case PPP_LEQ: return("<="); break; case PPP_GEQ: return(">="); break; case PPP_EQ: return("=="); break; case PPP_NEQ: return("!="); break; case PPP_HAT: return("^"); break; case PPP_OR: return("|"); break; case PPP_ANDAND: return("&&"); break; case PPP_OROR: return("||"); break; case PPP_QUESTION: return("?"); break; case PPP_COLON: return(":"); break; case PPP_SEMI: return(";"); break; case PPP_ELLIPSIS: return("..."); break; case PPP_ASSIGN: return("="); break; case PPP_MULASGN: return("*="); break; case PPP_DIVASGN: return("/="); break; case PPP_MODASGN: return("%="); break; case PPP_ADDASGN: return("+="); break; case PPP_SUBASGN: return("-="); break; case PPP_LSHASGN: return("<<="); break; case PPP_RSHASGN: return(">>="); break; case PPP_ANDASGN: return("&="); break; case PPP_XORASGN: return("^="); break; case PPP_ORASGN: return("|="); break; case PPP_COMMA: return(","); break; case PPP_SHARP: return("#"); break; case PPP_SSHARP: return("##"); break; } panic(); } static void Eflag_set(void) { Eflag = 1; Estate.line = -1; sfref_init(&Estate.file,"Eflag state"); Estate.atnl = 1; Estate.atws = 1; Estate.lasttt = PPT_EOF; } static SRCFILE *new_srcfile(const char *, ...) __attribute__((__format__(__printf__,1,2))); static SRCFILE *new_srcfile(const char *fmt, ...) { va_list ap; char *s; SRCFILE *sf; va_start(ap,fmt); vasprintf(&s,fmt,ap); va_end(ap); sf = malloc(sizeof(SRCFILE)); sfref_init(&sf->link,"%p (%s)",(void *)sf,s); sf->name = 0; sf->f = 0; sf->lno = -1; #ifdef SFLOGGING sf->reflist = 0; #endif sf->nrefs = 0; free(s); return(sf); } static void handleargs(int ac, char **av) { int skip; int errs; SRCFILE *last; SRCFILE *sf; last = 0; skip = 0; errs = 0; sfref_init(&srcfiles,"srcfiles root"); for (ac--,av++;ac;ac--,av++) { if (skip > 0) { skip --; continue; } if (**av != '-') { sf = new_srcfile("command-line file %s",*av); sf->name = *av; if (last) sfref_addref(&last->link,sf); else sfref_addref(&srcfiles,sf); continue; } if (0) { needarg:; fprintf(stderr,"%s: %s needs a following argument\n",__progname,*av); errs ++; continue; } #define WANTARG() do { if (++skip >= ac) goto needarg; } while (0) if (!strcmp(*av,"-V")) { WANTARG(); errs += set_verbosity(av[skip]); continue; } if (!strcmp(*av,"-E")) { Eflag_set(); continue; } #undef WANTARG fprintf(stderr,"%s: unrecognized option `%s'\n",__progname,*av); errs ++; } if (errs) exit(1); } static void open_predefines(void) { SRCFILE *sf; SRCFILE *n; sf = new_srcfile("predefines srcfile"); sf->name = strdup(""); sf->f = open_string( "\n" "#define __STDC__ 1\n" "#define __STDC_HOSTED__ 1\n" "#define __MCC__\n" #ifdef __amd64t__ "#define __amd64__\n" #endif #ifdef __x86_64__ "#define __x86_64__\n" #endif #ifdef __i386__ "#define __i386__\n" #endif #ifdef __NetBSD__ "#define __NetBSD__\n" #endif #ifdef __unix__ "#define __unix__\n" #endif #ifdef __ELF__ "#define __ELF__\n" #endif ,-1); sf->lno = 1; n = sfref_follow(&srcfiles); sfref_addref(&sf->link,n); sfref_delref(&srcfiles,n); sfref_addref(&srcfiles,sf); } static int open_sourcefile(SRCFILE *sf) { int fd; FILE *f; fd = open(sf->name,O_RDONLY,0); if (fd < 0) { fprintf(stderr,"%s: can't open %s: %s\n",__progname,sf->name,strerror(errno)); return(1); } f = fdopen(fd,"r"); sf->f = f; sf->lno = 1; return(0); } static void open_sourcefiles(void) { SRCFILE *sf; int errs; errs = 0; for (sf=sfref_follow(&srcfiles);sf;sf=sfref_follow(&sf->link)) errs |= open_sourcefile(sf); if (errs) exit(1); } static void str_free(STR *s) { if (s->needsfree) free(s->data); } static STR str_copy(STR s) { STR n; n.len = s.len; n.data = malloc(n.len); bcopy(s.data,n.data,n.len); n.needsfree = 1; return(n); } static int str_equal_c(const STR *s, const char *c) { int i; for (i=0;ilen;i++) { if (! c[i]) return(0); if (s->data[i] != c[i]) return(0); } return(!c[i]); } static int str_equal_str(const STR *s1, const STR *s2) { return( (s1->len == s2->len) ? !bcmp(s1->data,s2->data,s1->len) : 0 ); } static void str_print_escaped(const STR *s, FILE *to) { int i; for (i=0;ilen;i++) { switch ((unsigned char)s->data[i]) { case 7: fprintf(to,"\\a"); break; case 8: fprintf(to,"\\b"); break; case 27: fprintf(to,"\\e"); break; case 12: fprintf(to,"\\f"); break; case 10: fprintf(to,"\\n"); break; case 13: fprintf(to,"\\r"); break; case 9: fprintf(to,"\\t"); break; case 11: fprintf(to,"\\v"); break; case ' ' ... '~': case 160 ... 255: if (s->data[i] == '\\') fprintf(to,"\\\\"); else putc(s->data[i],to); break; default: if ((i+1 < s->len) && Cisdigit(s->data[i+1])) { fprintf(to,"\\%03o",(unsigned char)s->data[i]); } else { fprintf(to,"\\%o",(unsigned char)s->data[i]); } break; } } } static void stringizer_start(STRINGIZER *s) { if (! s->started) { (*s->put)('"',s->putarg); s->started = 1; } } static void *wrap_stringize_init(void (*put)(unsigned char, void *), void *putarg) { STRINGIZER *s; s = malloc(sizeof(STRINGIZER)); s->put = put; s->putarg = putarg; s->started = 0; return(s); } static void wrap_stringize_put(void *sv, unsigned char ch) { STRINGIZER *s; s = sv; stringizer_start(s); switch (ch) { case '"': case '\\': (*s->put)('\\',s->putarg); /* fall through */ default: (*s->put)(ch,s->putarg); break; } } static void wrap_stringize_done(void *sv) { STRINGIZER *s; s = sv; stringizer_start(s); (*s->put)('"',s->putarg); free(s); } static WRAP_W wrap_stringize = WRAP_W_INIT(stringize); static void pptoken_dump(FILE *f, PPTOKEN *pt) { int pstr; fprintf(f,"file=%s startline=%d ",sfref_follow(&pt->file)->name,pt->startline); pstr = 0; switch (pt->type) { case PPT_EOF: fprintf(f,"EOF"); break; case PPT_NEWLINE: fprintf(f,"NEWLINE"); break; case PPT_WHITESPACE: fprintf(f,"WHITESPACE"); pstr = 1; break; case PPT_HEADERNAME: fprintf(f,"HEADERNAME"); pstr = 1; break; case PPT_IDENT: fprintf(f,"IDENT"); pstr = 1; break; case PPT_NUMBER: fprintf(f,"NUMBER"); pstr = 1; break; case PPT_CHARCONST: fprintf(f,"CHARCONST"); pstr = 1; break; case PPT_STRING: fprintf(f,"STRING"); pstr = 1; break; case PPT_MARKER: fprintf(f,"MARKER"); break; case PPT_SYNTH: fprintf(f,"SYNTH"); pstr = 1; break; case PPT_PUNCT: fprintf(f,"PUNCT "); switch (pt->detail.punct) { case PPP_LBRACK: fprintf(f,"["); break; case PPP_RBRACK: fprintf(f,"]"); break; case PPP_LPAREN: fprintf(f,"("); break; case PPP_RPAREN: fprintf(f,")"); break; case PPP_LBRACE: fprintf(f,"{"/*}*/); break; case PPP_RBRACE: fprintf(f,/*{*/"}"); break; case PPP_DOT: fprintf(f,"."); break; case PPP_ARROW: fprintf(f,"->"); break; case PPP_PLUSPLUS: fprintf(f,"++"); break; case PPP_MINUSMINUS: fprintf(f,"--"); break; case PPP_AND: fprintf(f,"&"); break; case PPP_STAR: fprintf(f,"*"); break; case PPP_PLUS: fprintf(f,"+"); break; case PPP_MINUS: fprintf(f,"-"); break; case PPP_TILDE: fprintf(f,"~"); break; case PPP_BANG: fprintf(f,"!"); break; case PPP_SLASH: fprintf(f,"/"); break; case PPP_PERCENT: fprintf(f,"%%"); break; case PPP_LSH: fprintf(f,"<<"); break; case PPP_RSH: fprintf(f,">>"); break; case PPP_LT: fprintf(f,"<"); break; case PPP_GT: fprintf(f,">"); break; case PPP_LEQ: fprintf(f,"<="); break; case PPP_GEQ: fprintf(f,">="); break; case PPP_EQ: fprintf(f,"=="); break; case PPP_NEQ: fprintf(f,"!="); break; case PPP_HAT: fprintf(f,"^"); break; case PPP_OR: fprintf(f,"|"); break; case PPP_ANDAND: fprintf(f,"&&"); break; case PPP_OROR: fprintf(f,"||"); break; case PPP_QUESTION: fprintf(f,"?"); break; case PPP_COLON: fprintf(f,":"); break; case PPP_SEMI: fprintf(f,";"); break; case PPP_ELLIPSIS: fprintf(f,"..."); break; case PPP_ASSIGN: fprintf(f,"="); break; case PPP_MULASGN: fprintf(f,"*="); break; case PPP_DIVASGN: fprintf(f,"/="); break; case PPP_MODASGN: fprintf(f,"%%="); break; case PPP_ADDASGN: fprintf(f,"+="); break; case PPP_SUBASGN: fprintf(f,"-="); break; case PPP_LSHASGN: fprintf(f,"<<="); break; case PPP_RSHASGN: fprintf(f,">>="); break; case PPP_ANDASGN: fprintf(f,"&="); break; case PPP_XORASGN: fprintf(f,"^="); break; case PPP_ORASGN: fprintf(f,"|="); break; case PPP_COMMA: fprintf(f,","); break; case PPP_SHARP: fprintf(f,"#"); break; case PPP_SSHARP: fprintf(f,"##"); break; default: fprintf(f,"?%d",(int)pt->detail.punct); break; } break; case PPT_OTHER: fprintf(f,"OTHER %c",pt->detail.other); break; case PPT_ENDMACRO: fprintf(f,"ENDMACRO %p (%.*s)",(void *)pt->detail.macro,pt->detail.macro->name.len,pt->detail.macro->name.data); break; case PPT_MACROARG: fprintf(f,"MACROARG %d flags=%x<",pt->detail.macroarg.argno,pt->detail.macroarg.flags); if (pt->detail.macroarg.flags & MAF_NOEXPAND) fprintf(f,"NOEXPAND"); fprintf(f,">"); break; default: fprintf(f,"?%d",(int)pt->type); break; } if (pstr) { fprintf(f," str="); str_print_escaped(&pt->str,f); } } static void pptoken_text(FILE *f, PPTOKEN *pt) { switch (pt->type) { case PPT_NEWLINE: fprintf(f,"\n"); break; case PPT_HEADERNAME: case PPT_WHITESPACE: case PPT_IDENT: case PPT_NUMBER: case PPT_CHARCONST: case PPT_STRING: case PPT_SYNTH: fwrite(pt->str.data,1,pt->str.len,f); break; case PPT_MARKER: break; case PPT_PUNCT: switch (pt->detail.punct) { case PPP_LBRACK: fprintf(f,"["); break; case PPP_RBRACK: fprintf(f,"]"); break; case PPP_LPAREN: fprintf(f,"("); break; case PPP_RPAREN: fprintf(f,")"); break; case PPP_LBRACE: fprintf(f,"{"/*}*/); break; case PPP_RBRACE: fprintf(f,/*{*/"}"); break; case PPP_DOT: fprintf(f,"."); break; case PPP_ARROW: fprintf(f,"->"); break; case PPP_PLUSPLUS: fprintf(f,"++"); break; case PPP_MINUSMINUS: fprintf(f,"--"); break; case PPP_AND: fprintf(f,"&"); break; case PPP_STAR: fprintf(f,"*"); break; case PPP_PLUS: fprintf(f,"+"); break; case PPP_MINUS: fprintf(f,"-"); break; case PPP_TILDE: fprintf(f,"~"); break; case PPP_BANG: fprintf(f,"!"); break; case PPP_SLASH: fprintf(f,"/"); break; case PPP_PERCENT: fprintf(f,"%%"); break; case PPP_LSH: fprintf(f,"<<"); break; case PPP_RSH: fprintf(f,">>"); break; case PPP_LT: fprintf(f,"<"); break; case PPP_GT: fprintf(f,">"); break; case PPP_LEQ: fprintf(f,"<="); break; case PPP_GEQ: fprintf(f,">="); break; case PPP_EQ: fprintf(f,"=="); break; case PPP_NEQ: fprintf(f,"!="); break; case PPP_HAT: fprintf(f,"^"); break; case PPP_OR: fprintf(f,"|"); break; case PPP_ANDAND: fprintf(f,"&&"); break; case PPP_OROR: fprintf(f,"||"); break; case PPP_QUESTION: fprintf(f,"?"); break; case PPP_COLON: fprintf(f,":"); break; case PPP_SEMI: fprintf(f,";"); break; case PPP_ELLIPSIS: fprintf(f,"..."); break; case PPP_ASSIGN: fprintf(f,"="); break; case PPP_MULASGN: fprintf(f,"*="); break; case PPP_DIVASGN: fprintf(f,"/="); break; case PPP_MODASGN: fprintf(f,"%%="); break; case PPP_ADDASGN: fprintf(f,"+="); break; case PPP_SUBASGN: fprintf(f,"-="); break; case PPP_LSHASGN: fprintf(f,"<<="); break; case PPP_RSHASGN: fprintf(f,">>="); break; case PPP_ANDASGN: fprintf(f,"&="); break; case PPP_XORASGN: fprintf(f,"^="); break; case PPP_ORASGN: fprintf(f,"|="); break; case PPP_COMMA: fprintf(f,","); break; case PPP_SHARP: fprintf(f,"#"); break; case PPP_SSHARP: fprintf(f,"##"); break; default: panic(); break; } break; case PPT_OTHER: putc(pt->detail.other,f); break; default: panic(); break; } } static void pptoken_free(PPTOKEN *pt) { if (! pt) return; str_free(&pt->str); sfref_done(&pt->file); free(pt); } static PPTOKEN *pptoken_new(void) { PPTOKEN *t; t = malloc(sizeof(PPTOKEN)); t->type = PPT_EOF; sfref_init(&t->file,"pptoken %p",(void *)t); t->startline = -1; return(t); } static PPTOKEN *pptoken_copy(PPTOKEN *t) { PPTOKEN *n; n = pptoken_new(); n->type = t->type; switch (t->type) { default: break; case PPT_WHITESPACE: case PPT_HEADERNAME: case PPT_IDENT: case PPT_NUMBER: case PPT_CHARCONST: case PPT_STRING: case PPT_SYNTH: case PPT_LINE: n->str = str_copy(t->str); break; } sfref_addref(&n->file,sfref_follow(&t->file)); switch (t->type) { default: break; case PPT_PUNCT: n->detail.punct = t->detail.punct; break; case PPT_OTHER: n->detail.other = t->detail.other; break; case PPT_ENDMACRO: n->detail.macro = t->detail.macro; break; case PPT_MACROARG: n->detail.macroarg = t->detail.macroarg; break; } return(n); } static PPTOKEN *pptoken_of_type(PPTOKTYPE type) { PPTOKEN *t; t = pptoken_new(); t->type = type; t->str.data = 0; t->str.len = 0; t->str.needsfree = 0; sfref_settext(&t->file,"pptoken %p (%s)",(void *)t,pp_toktype_str(type)); sfref_addref(&t->file,sfref_follow(&cursf_)); t->startline = startlno; return(t); } static PPTOKEN *pptoken_with_str(P2P3 *p, PPTOKTYPE type) { PPTOKEN *t; t = pptoken_of_type(type); t->str.len = ebuf_len(&p->buf3); t->str.data = malloc(t->str.len); bcopy(ebuf_data(&p->buf3),t->str.data,t->str.len); t->str.needsfree = 1; return(t); } static PPTOKEN *pptoken_punct(PPPUNCTTYPE type) { PPTOKEN *t; t = pptoken_of_type(PPT_PUNCT); t->detail.punct = type; return(t); } static PPTOKEN *pptoken_other(unsigned char c) { PPTOKEN *t; t = pptoken_of_type(PPT_OTHER); t->detail.other = c; return(t); } static PPTOKEN *pptoken_endmacro(MACRO *m) { PPTOKEN *t; t = pptoken_of_type(PPT_ENDMACRO); t->detail.macro = m; return(t); } static PPTOKEN *pptoken_macroarg(int argno) { PPTOKEN *t; t = pptoken_of_type(PPT_MACROARG); t->detail.macroarg.argno = argno; t->detail.macroarg.flags = 0; return(t); } static PPTOKEN *pptoken_stringize(PPTLIST *list) { int ws; FILE *f; FILE *g; PPTOKEN *t; t = pptoken_of_type(PPT_STRING); g = open_accum(&t->str.data,&t->str.len); f = open_wrap_w(g,&wrap_stringize); while (list && (list->token->type == PPT_WHITESPACE)) list = list->link; ws = 0; for (;list;list=list->link) { if ((list->token->type == PPT_WHITESPACE) || (list->token->type == PPT_NEWLINE)) { ws = 1; } else { if (ws) { putc(' ',f); ws = 0; } pptoken_text(f,list->token); } } fclose(f); fclose(g); return(t); } static void pptlist_free(PPTLIST *list) { PPTLIST *e; while ((e = list)) { list = e->link; pptoken_free(e->token); free(e); } } static PPTLIST *pptlist_holding(PPTOKEN *t) { PPTLIST *l; l = malloc(sizeof(PPTLIST)); l->token = t; return(l); } static PPTLIST *pptlist_clone_append(PPTLIST *toclone, PPTLIST *tail) { PPTLIST *n; PPTLIST **nt; PPTLIST *e; PPTLIST *ne; nt = &n; for (e=toclone;e;e=e->link) { ne = pptlist_holding(pptoken_copy(e->token)); *nt = ne; nt = &ne->link; } *nt = tail; return(n); } static void pptconc_init(PPTCONC *tc) { tc->head = 0; tc->tail = &tc->head; } static void pptconc_append(PPTCONC *tc, PPTLIST *el) { *tc->tail = el; tc->tail = &el->link; el->link = 0; } static void pptconc_nwstoken(PPTCONC *tc, PPTOKEN *t) { if (! t) return; if ((t->type == PPT_WHITESPACE) || (t->type == PPT_NEWLINE)) { pptoken_free(t); } else { pptconc_append(tc,pptlist_holding(t)); } } static void pptconc_append_tconc(PPTCONC *tc, PPTCONC *end) { if (end->head) { *tc->tail = end->head; tc->tail = end->tail; } } static PPTLIST *pptconc_head(PPTCONC *tc) { return(tc->head); } static PPTLIST *pptconc_pophead(PPTCONC *tc) { PPTLIST *l; l = tc->head; if (l) { tc->head = l->link; if (! tc->head) tc->tail = &tc->head; l->link = 0; } return(l); } static PPTLIST *pptconc_done(PPTCONC *tc, PPTLIST *tail) { PPTLIST *l; *tc->tail = tail; l = tc->head; tc->head = 0; tc->tail = &tc->head; return(l); } static void macro_free(MACRO *m) { int i; str_free(&m->name); for (i=m->nargs-1;i>=0;i--) pptoken_free(m->args[i]); free(m->args); pptlist_free(m->replacement); } static void macro_define(MACRO *new) { MACRO *m; PPTLIST *r1; PPTLIST *r2; for (m=macros;m;m=m->link) { if (str_equal_str(&m->name,&new->name)) { r1 = m->replacement; r2 = new->replacement; do <"differ"> { while (r1 || r2) { if (!r1 || !r2) { break <"differ">; } else if ((r1->token->type == PPT_WHITESPACE) && (r2->token->type == PPT_WHITESPACE)) { while (r1 && (r1->token->type == PPT_WHITESPACE)) r1 = r1->link; while (r2 && (r2->token->type == PPT_WHITESPACE)) r2 = r2->link; } else if (r1->token->type != r2->token->type) { break <"differ">; } else { switch (r1->token->type) { case PPT_IDENT: case PPT_NUMBER: case PPT_CHARCONST: case PPT_STRING: if (! str_equal_str(&r1->token->str,&r2->token->str)) break <"differ">; break; case PPT_PUNCT: if (r1->token->detail.punct != r2->token->detail.punct) break <"differ">; break; case PPT_OTHER: if (r1->token->detail.other != r2->token->detail.other) break <"differ">; break; case PPT_MACROARG: if ( (r1->token->detail.macroarg.argno != r2->token->detail.macroarg.argno) || (r1->token->detail.macroarg.flags != r2->token->detail.macroarg.flags) ) break <"differ">; break; default: panic(); break; } } r1 = r1->link; r2 = r2->link; } macro_free(new); return; } while (0); error("%.*s incorrectly redefined",new->name.len,new->name.data); macro_free(new); return; } } new->link = macros; macros = new; } static void macro_undef(STR *name) { MACRO *m; MACRO **mp; mp = ¯os; while ((m = *mp)) { if (str_equal_str(&m->name,name)) { *mp = m->link; macro_free(m); return; } mp = &m->link; } } static MACRO *macro_find(PPTOKEN *name) { MACRO *m; for (m=macros;m;m=m->link) if (str_equal_str(&m->name,&name->str)) return(m); return(0); } static int macro_arg_no(MACRO *m, PPTOKEN *name) { int i; if (name->type != PPT_IDENT) return(-1); if ((m->flags & MF_VARARGS) && (name->str.len == 11) && !bcmp(name->str.data,"__VA_ARGS__",11)) return(m->nargs); for (i=m->nargs-1;i>=0;i--) if (str_equal_str(&m->args[i]->str,&name->str)) return(i); return(-1); } static PPTOKEN *macroexpander_input(MACROEXPANDER *, PPTOKEN *); // forward static PPTOKEN *macroexpander_flush(MACROEXPANDER *); // forward static void macroexpander_init(MACROEXPANDER *mx) { VERB(MACROEXPANDER,"macroexpander_init %p\n",(void *)mx); pptconc_init(&mx->done); mx->curmac = 0; mx->name = 0; pptconc_init(&mx->arglist); mx->pending = 0; } static void macroexpander_done(MACROEXPANDER *mx) { VERB(MACROEXPANDER,"macroexpander_done %p\n",(void *)mx); pptlist_free(pptconc_done(&mx->done,0)); if (mx->name) pptoken_free(mx->name); pptlist_free(pptconc_done(&mx->arglist,0)); pptlist_free(mx->pending); } static PPTOKEN *macroexpander_get(MACROEXPANDER *mx) { PPTLIST *l; PPTOKEN *t; VERB(MACROEXPANDER,"macroexpander_get ENTRY %p\n",(void *)mx); l = pptconc_pophead(&mx->done); if (! l) { VERB(MACROEXPANDER,"macroexpander_get EXIT nil\n"); return(0); } t = l->token; free(l); if (verbose & VERB_MACROEXPANDER) { printf("macroexpander_get EXIT %p ",(void *)t); pptoken_dump(stdout,t); printf("\n"); } return(t); } static void macroexpander_addarg(MACROEXPANDER *mx, PPTOKEN *t) { PPTLIST *l; if (verbose & VERB_MACROEXPANDER) { printf("macroexpander_addarg ENTRY %p %p ",(void *)mx,(void *)t); pptoken_dump(stdout,t); printf("\n"); } l = pptlist_holding(t); pptconc_append(&mx->arglist,l); VERB(MACROEXPANDER,"macroexpander_addarg EXIT listel %p\n",(void *)l); } static void macroexpander_append_done(MACROEXPANDER *mx, PPTOKEN *t) { PPTLIST *l; if (verbose & VERB_MACROEXPANDER) { printf("macroexpander_append_done ENTRY %p %p ",(void *)mx,(void *)t); pptoken_dump(stdout,t); printf("\n"); } if (t->type == PPT_MARKER) { VERB(MACROEXPANDER,"macroexpander_append_done EXIT dropping placemarker\n"); pptoken_free(t); } else { l = pptlist_holding(t); pptconc_append(&mx->done,l); VERB(MACROEXPANDER,"macroexpander_append_done EXIT listel %p\n",(void *)l); } } static void macroexpander_pending_push(MACROEXPANDER *mx, PPTOKEN *t) { PPTLIST *l; if (verbose & VERB_MACROEXPANDER) { printf("macroexpander_pending_push ENTRY %p %p ",(void *)mx,(void *)t); pptoken_dump(stdout,t); printf("\n"); } l = pptlist_holding(t); l->link = mx->pending; mx->pending = l; VERB(MACROEXPANDER,"macroexpander_pending_push EXIT listel %p\n",(void *)l); } static PPTOKEN *macroexpander_pending_pop(MACROEXPANDER *mx) { PPTLIST *l; PPTOKEN *t; l = mx->pending; VERB(MACROEXPANDER,"macroexpander_pending_pop ENTRY %p listel %p\n",(void *)mx,(void *)l); mx->pending = l->link; t = l->token; free(l); if (verbose & VERB_MACROEXPANDER) { printf("macroexpander_pending_pop EXIT %p ",(void *)t); pptoken_dump(stdout,t); printf("\n"); } return(t); } static void macroexpander_abort(MACROEXPANDER *mx) { VERB(MACROEXPANDER,"macroexpander_abort %p\n",(void *)mx); pptlist_free(pptconc_done(&mx->arglist,0)); mx->curmac = 0; } static PPTOKEN *macroexpander_flush(MACROEXPANDER *mx) { PPTOKEN *t; VERB(MACROEXPANDER,"macroexpander_flush ENTRY %p\n",(void *)mx); t = macroexpander_get(mx); if (t) { if (verbose & VERB_MACROEXPANDER) { printf("macroexpander_flush EXIT %p ",(void *)t); pptoken_dump(stdout,t); printf("\n"); } return(t); } if (mx->curmac) { error("unclosed call to macro %.*s",mx->curmac->name.len,mx->curmac->name.data); macroexpander_abort(mx); } VERB(MACROEXPANDER,"macroexpander_flush EXIT nil\n"); return(0); } static void concatter_init(CONCATTER *c) { VERB(CONCATTER,"concatter_init %p\n",(void *)c); pptconc_init(&c->list); pptconc_init(&c->last[0]); pptconc_init(&c->last[1]); } /* * Note: knowledge of what constitutes ppnumbers is also wired into * phase3_ppnumber (and a little in phase3_get); knowledge of what * constitutes identifiers is also wired into phase3_get. */ static void classify_synth(PPTOKEN *t) { unsigned char c; int i; int cc; if (t->str.len < 1) { // Can this happen? return; } c = t->str.data[0]; cc = chclass(c); if (cc & CC_IDENT) { do <"notident"> { for (i=t->str.len-1;i>=1;i--) { if (! (chclass(t->str.data[i]) & (CC_IDENT|CC_DIGIT))) break <"notident">; } t->type = PPT_IDENT; return; } while (0); } if ( ((cc & CC_DIGIT) && (i=1)) || ((c == '.') && (t->str.len > 1) && (chclass(t->str.data[1]) & CC_DIGIT) && (i=2)) ) { do <"notnum"> { while (i < t->str.len) { c = t->str.data[i]; switch (c) { case '.': break; case 'e': case 'E': case 'p': case 'P': if (i+1 < t->str.len) { switch (t->str.data[i+1]) { case '+': case '-': i += 2; continue; break; } } break; default: if (! (chclass(c) & (CC_IDENT|CC_DIGIT))) break <"notnum">; } i ++; } t->type = PPT_NUMBER; return; } while (0); } } static void concatter_append(CONCATTER *c, PPTLIST *el) { PPTLIST *h1; PPTLIST *h0; PPTOKEN *t; PPTOKEN *new; FILE *f; if (verbose & VERB_CONCATTER) { printf("concatter_append ENTRY %p %p/%p ",(void *)c,(void *)el,(void *)el->token); pptoken_dump(stdout,el->token); printf("\n"); } t = el->token; if ((t->type == PPT_WHITESPACE) || (t->type == PPT_NEWLINE)) { VERB(CONCATTER,"concatter_append EXIT simple append (whitespace/newline)\n"); pptconc_append(&c->last[0],el); return; } h1 = pptconc_head(&c->last[1]); if (h1) { h0 = pptconc_head(&c->last[0]); t = h0->token; if ((t->type == PPT_PUNCT) && (t->detail.punct == PPP_SSHARP)) { if (h1->token->type == PPT_MARKER) { new = pptoken_copy(el->token); } else if (el->token->type == PPT_MARKER) { new = pptoken_copy(h1->token); } else { new = pptoken_of_type(PPT_SYNTH); f = open_accum(&new->str.data,&new->str.len); pptoken_text(f,h1->token); pptoken_text(f,el->token); fclose(f); classify_synth(new); } pptlist_free(pptconc_done(&c->last[1],0)); pptlist_free(pptconc_done(&c->last[0],0)); el->link = 0; pptlist_free(el); pptconc_init(&c->last[1]); pptconc_init(&c->last[0]); pptconc_append(&c->last[0],pptlist_holding(new)); return; } } pptconc_append_tconc(&c->list,&c->last[1]); c->last[1] = c->last[0]; pptconc_init(&c->last[0]); pptconc_append(&c->last[0],el); } static PPTLIST *concatter_term(CONCATTER *c, PPTLIST *tail) { pptconc_append_tconc(&c->list,&c->last[1]); pptconc_append_tconc(&c->list,&c->last[0]); return(pptconc_done(&c->list,tail)); } static void macroexpander_expand_args(MACROEXPANDER *mx) { PPTLIST *a; PPTLIST **curtail; int nest; int nargs; int margs; int nonws; MACRO *m; PPTLIST **argv; int setcur; int i; PPTLIST *x; PPTLIST *x2; CONCATTER expansion; PPTOKEN *t; VERB(MACROEXPANDER,"macroexpander_expand_args ENTRY %p\n",(void *)mx); m = mx->curmac; nest = 0; nargs = 1; nonws = 0; if (verbose & VERB_MACROEXPANDER) { printf("macroexpander_expand_args arglist =\n"); for (a=pptconc_head(&mx->arglist);a;a=a->link) { printf("\t%p/%p ",(void *)a,(void *)a->token); pptoken_dump(stdout,a->token); printf("\n"); } } for (a=pptconc_head(&mx->arglist);a;a=a->link) { switch (a->token->type) { case PPT_PUNCT: nonws = 1; switch (a->token->detail.punct) { case PPP_LPAREN: nest ++; break; case PPP_RPAREN: nest --; if (nest < 0) panic(); break; case PPP_COMMA: if (nest == 0) nargs ++; break; default: break; } break; case PPT_WHITESPACE: case PPT_NEWLINE: break; default: nonws = 1; break; } } if (nest) panic(); if ((nargs == 1) && !nonws && (m->nargs == 0) && !(m->flags & MF_VARARGS)) nargs = 0; VERB(MACROEXPANDER,"macroexpander_expand_args nargs = %d\n",nargs); argv = 0; do <"done"> { if (m->flags & MF_VARARGS) { if (nargs <= m->nargs) { error("too few arguments to macro %.*s",m->name.len,m->name.data); break <"done">; } margs = m->nargs + 1; } else { if (nargs < m->nargs) { error("too few arguments to macro %.*s",m->name.len,m->name.data); break <"done">; } if (nargs > m->nargs) { error("too many arguments to macro %.*s",m->name.len,m->name.data); break <"done">; } margs = nargs; } VERB(MACROEXPANDER,"macroexpander_expand_args margs = %d\n",margs); if (nargs) { argv = malloc(margs*sizeof(*argv)); i = 0; setcur = 1; nest = 0; while (1) { a = pptconc_pophead(&mx->arglist); if (! a) break; if (verbose & VERB_MACROEXPANDER) { printf("macroexpander_expand_args: taking arg %p/%p (",(void *)a,(void *)a->token); pptoken_dump(stdout,a->token); printf("\n"); } if (setcur) { if (i >= margs) panic(); VERB(MACROEXPANDER,"macroexpander_expand_args: building argv[%d] list\n",i); curtail = &argv[i++]; setcur = 0; } if (a->token->type == PPT_PUNCT) { switch (a->token->detail.punct) { case PPP_LPAREN: nest ++; VERB(MACROEXPANDER,"macroexpander_expand_args: ( nest now %d\n",nest); break; case PPP_RPAREN: nest --; VERB(MACROEXPANDER,"macroexpander_expand_args: ) nest now %d\n",nest); if (nest < 0) panic(); break; case PPP_COMMA: VERB(MACROEXPANDER,"macroexpander_expand_args: , nest %d i %d margs %d\n",nest,i,margs); if ((nest == 0) && (i < margs)) { *curtail = 0; VERB(MACROEXPANDER,"macroexpander_expand_args: freeing delimiter comma %p/%p\n",(void *)a,(void *)a->token); a->link = 0; pptlist_free(a); setcur = 1; continue; } break; default: break; } } if (verbose & VERB_MACROEXPANDER) { printf("macroexpander_expand_args: saving argument token %p/%p ",(void *)a,(void *)a->token); pptoken_dump(stdout,a->token); printf("\n"); } *curtail = a; curtail = &a->link; } *curtail = 0; if (i == margs-1) argv[i++] = 0; if (i != margs) panic(); } else { if (margs) panic(); argv = 0; pptlist_free(pptconc_done(&mx->arglist,0)); } if (verbose & VERB_MACROEXPANDER) { for (i=0;ilink) { printf("\t%p/%p ",(void *)x,(void *)x->token); pptoken_dump(stdout,x->token); printf("\n"); } } } concatter_init(&expansion); if (mx->curmac->flags & MF_DISABLED) panic(); mx->curmac->flags |= MF_DISABLED; if (verbose & VERB_MACROEXPANDER) { printf("macroexpander_expand_args replacement =\n"); for (x=mx->curmac->replacement;x;x=x->link) { printf("\t%p/%p ",(void *)x,(void *)x->token); pptoken_dump(stdout,x->token); printf("\n"); } } x = mx->curmac->replacement; while (x) { if (verbose & VERB_MACROEXPANDER) { printf("macroexpander_expand_args replacement el %p token %p ",(void *)x,(void *)x->token); pptoken_dump(stdout,x->token); printf("\n"); } if ((x->token->type == PPT_PUNCT) && (x->token->detail.punct == PPP_SHARP)) { do x = x->link; while (x && (x->token->type == PPT_WHITESPACE)); if (!x || (x->token->type != PPT_MACROARG)) panic(); if ((x->token->detail.macroarg.argno < 0) || (x->token->detail.macroarg.argno >= margs)) panic(); x2 = pptlist_holding(pptoken_stringize(argv[x->token->detail.macroarg.argno])); if (verbose & VERB_MACROEXPANDER) { printf("macroexpander_expand_args stringized result is el %p token %p ",(void *)x2,(void *)x2->token); pptoken_dump(stdout,x2->token); printf("\n"); } x = x->link; concatter_append(&expansion,x2); } else if (x->token->type == PPT_MACROARG) { if ((x->token->detail.macroarg.argno < 0) || (x->token->detail.macroarg.argno >= margs)) panic(); a = pptlist_clone_append(argv[x->token->detail.macroarg.argno],0); if (x->token->detail.macroarg.flags & MAF_NOEXPAND) { if (a) { VERB(MACROEXPANDER,"macroexpander_expand_args copying arg %d\n",x->token->detail.macroarg.argno); while (a) { x2 = a; a = a->link; concatter_append(&expansion,x2); } } else { VERB(MACROEXPANDER,"macroexpander_expand_args inserting placemarker for arg %d\n",x->token->detail.macroarg.argno); concatter_append(&expansion,pptlist_holding(pptoken_of_type(PPT_MARKER))); } } else { MACROEXPANDER mx; VERB(MACROEXPANDER,"macroexpander_expand_args expanding arg %d\n",x->token->detail.macroarg.argno); macroexpander_init(&mx); while (1) { t = macroexpander_get(&mx); if (! t) { if (! a) break; t = a->token; x2 = a->link; free(a); a = x2; t = macroexpander_input(&mx,t); } if (t) concatter_append(&expansion,pptlist_holding(t)); } while ((t = macroexpander_flush(&mx))) concatter_append(&expansion,pptlist_holding(t)); macroexpander_done(&mx); } x = x->link; } else { concatter_append(&expansion,pptlist_holding(pptoken_copy(x->token))); x = x->link; } } VERB(MACROEXPANDER,"macroexpander_expand_args pushing endmacro\n"); x = pptlist_holding(pptoken_endmacro(mx->curmac)); x->link = mx->pending; mx->pending = concatter_term(&expansion,x); } while (0); free(argv); macroexpander_abort(mx); VERB(MACROEXPANDER,"macroexpander_expand_args EXIT\n"); } static PPTOKEN *macroexpander_input(MACROEXPANDER *mx, PPTOKEN *t) { MACRO *m; if (verbose & VERB_MACROEXPANDER) { printf("macroexpander_input ENTRY %p %p ",(void *)mx,(void *)t); pptoken_dump(stdout,t); printf("\n"); } if (pptconc_head(&mx->done)) panic(); if (!mx->curmac && ((t->type != PPT_IDENT) || !macro_find(t))) { VERB(MACROEXPANDER,"macroexpander_input EXIT simple case\n"); return(t); } mx->pending = 0; macroexpander_pending_push(mx,t); while (mx->pending) { t = macroexpander_pending_pop(mx); if (verbose & VERB_MACROEXPANDER) { printf("macroexpander_input taking pending token %p ",(void *)t); pptoken_dump(stdout,t); printf("\n"); } if (mx->curmac) { if (mx->name) { if ((t->type == PPT_WHITESPACE) || (t->type == PPT_NEWLINE)) { pptoken_free(t); } else if ((t->type == PPT_PUNCT) && (t->detail.punct == PPP_LPAREN)) { pptoken_free(t); mx->argnest = 1; pptoken_free(mx->name); mx->name = 0; } else { macroexpander_append_done(mx,mx->name); macroexpander_pending_push(mx,t); mx->name = 0; mx->curmac = 0; } } else { switch (t->type) { case PPT_EOF: error("EOF inside macro invocation arglist"); pptoken_free(t); pptlist_free(pptconc_done(&mx->arglist,0)); mx->curmac = 0; break; case PPT_PUNCT: switch (t->detail.punct) { case PPP_LPAREN: mx->argnest ++; break; case PPP_RPAREN: mx->argnest --; break; default: break; } if (mx->argnest < 1) { pptoken_free(t); macroexpander_expand_args(mx); break; } /* fall through */ default: macroexpander_addarg(mx,t); break; } } continue; } if (t->type == PPT_ENDMACRO) { if (! (t->detail.macro->flags & MF_DISABLED)) panic(); t->detail.macro->flags &= ~MF_DISABLED; pptoken_free(t); continue; } m = (t->type == PPT_IDENT) ? macro_find(t) : 0; if (!m || (m->flags & MF_DISABLED)) { macroexpander_append_done(mx,t); continue; } if (m->flags & MF_HASARGS) { mx->curmac = m; mx->name = t; } else { pptoken_free(t); m->flags |= MF_DISABLED; macroexpander_pending_push(mx,pptoken_endmacro(m)); mx->pending = pptlist_clone_append(m->replacement,mx->pending); } } return(macroexpander_get(mx)); } static void phase2_init(P2P3 *p) { p->s2 = P2S_NORMAL; } static void phase2_done(P2P3 *p) { (void)p; } static int sfget(void) { SRCFILE *sf; int c; sf = sfref_follow(&cursf_); c = getc(sf->f); if (c == '\n') sf->lno ++; return(c); } static void include_file(PPTOKEN *fnt) { SRCFILE *oldcursf; SRCFILE *sf; char *fn; int fd; FILE *f; char *slash; char *name; ISTACK *i; int try(const char *path) { fd = open(path,O_RDONLY,0); return(fd>=0); } oldcursf = sfref_follow(&cursf_); if (fnt->type != PPT_HEADERNAME) panic(); do <"open"> { fn = malloc(fnt->str.len-1); bcopy(fnt->str.data+1,fn,fnt->str.len-2); fn[fnt->str.len-2] = '\0'; if (fn[0] == '/') { if (try(fn)) { name = strdup(fn); break <"open">; } } else { if (fnt->str.data[0] == '"') { slash = rindex(oldcursf->name,'/'); if (slash) { asprintf(&name,"%.*s/%s",(int)(slash-oldcursf->name),oldcursf->name,fn); } else { name = strdup(fn); } if (try(name)) break <"open">; free(name); } asprintf(&name,"/usr/include/%s",fn); if (try(name)) break <"open">; free(name); } error("include file %s not found",fn); free(fn); return; } while (0); free(fn); i = malloc(sizeof(ISTACK)); sfref_init(&i->sf,"#include %.*s istack",fnt->str.len,fnt->str.data); sfref_addref(&i->sf,sfref_follow(&cursf_)); i->link = istack; istack = i; f = fdopen(fd,"r"); sf = new_srcfile("#include %.*s",fnt->str.len,fnt->str.data); sf->name = name; sf->f = f; sf->lno = 1; sfref_addref(&sf->link,oldcursf); sfref_delref(&cursf_,oldcursf); sfref_addref(&cursf_,sf); } static CL phase2_get(P2P3 *p) { CL c; c.l = sfref_follow(&cursf_)->lno; while (1) { switch (p->s2) { case P2S_NORMAL: c.c = sfget(); switch (c.c) { case EOF: return(c); break; case '\\': p->s2 = P2S_BACKSLASH; p->backslash = c; break; default: return(c); break; } break; case P2S_BACKSLASH: c.c = sfget(); switch (c.c) { case EOF: error("EOF after backslash"); return(c); break; case '\n': p->s2 = P2S_CONT; break; default: p->s2 = P2S_PUSHED; p->push2 = c; return(p->backslash); break; } break; case P2S_PUSHED: c = p->push2; p->s2 = P2S_NORMAL; return(c); break; case P2S_CONT: c.c = sfget(); switch (c.c) { case EOF: error("EOF after backslash-newline"); return(c); break; default: p->s2 = P2S_NORMAL; return(c); break; } break; default: panic(); break; } } } static void phase3_init(P2P3 *p) { p->n3unget = 0; p->push3 = 0; ebuf_init(&p->buf3); } static void phase3_done(P2P3 *p) { ebuf_done(&p->buf3); if (p->push3) pptoken_free(p->push3); } static CL p3iget(P2P3 *p) { return((p->n3unget > 0) ? p->unget3[--p->n3unget] : phase2_get(p)); } static void p3iunget(P2P3 *p, CL c) { if (p->n3unget >= MAXP3UNGET) panic(); p->unget3[p->n3unget++] = c; } /* * Note: knowledge of what constitutes ppnumbers is also wired into * classify_synth. */ static PPTOKEN *phase3_ppnumber(P2P3 *p, int dig, int haddot) { CL c; if (haddot) ebuf_append1(&p->buf3,'.'); ebuf_append1(&p->buf3,dig); while <"num"> (1) { c = p3iget(p); switch (c.c) { case 'e': case 'E': case 'p': case 'P': ebuf_append1(&p->buf3,c.c); c = p3iget(p); switch (c.c) { case '+': case '-': ebuf_append1(&p->buf3,c.c); break; default: p3iunget(p,c); break; } break; case '.': ebuf_append1(&p->buf3,c.c); break; case EOF: p3iunget(p,c); break <"num">; default: if (! (chclass(c.c) & (CC_IDENT|CC_DIGIT))) break <"num">; ebuf_append1(&p->buf3,c.c); break; } } p3iunget(p,c); return(pptoken_with_str(p,PPT_NUMBER)); } static PPTOKEN *phase3_stringish(P2P3 *p, char odelim, char cdelim, PPTOKTYPE type) { CL c; int bq; int ending; bq = 0; ebuf_append1(&p->buf3,odelim); ending = 0; while (1) { c = p3iget(p); if (c.c == EOF) { error("unclosed %c at EOF\n",odelim); return(pptoken_of_type(PPT_EOF)); } if (bq) { bq = 0; } else if (c.c == '\\') { bq = 1; } else if (c.c == cdelim) { ending = 1; } ebuf_append1(&p->buf3,c.c); if (ending) break; } return(pptoken_with_str(p,type)); } static PPTOKEN *pptoken_comment_slashslash(P2P3 *p) { CL c; ebuf_append1(&p->buf3,'/'); ebuf_append1(&p->buf3,'/'); while (1) { c = p3iget(p); switch (c.c) { case EOF: error("EOF inside a // comment"); return(pptoken_of_type(PPT_EOF)); break; case '\n': p3iunget(p,c); return(pptoken_with_str(p,PPT_WHITESPACE)); break; } ebuf_append1(&p->buf3,c.c); } } static PPTOKEN *pptoken_comment_slashstar(P2P3 *p) { int pc; int c; ebuf_append1(&p->buf3,'/'); ebuf_append1(&p->buf3,'*'); c = 0; do { pc = c; c = p3iget(p).c; switch (c) { case EOF: error("EOF inside a /* comment */"); return(pptoken_of_type(PPT_EOF)); break; } ebuf_append1(&p->buf3,c); } while (! ((pc == '*') && (c == '/'))); return(pptoken_with_str(p,PPT_WHITESPACE)); } /* * Note: knowledge of what constitutes identifiers is also wired into * classify_synth. */ typedef enum { P3GK_NORMAL = 1, P3GK_HEADER, P3GK_LINE, } P3GKIND; static PPTOKEN *phase3_get_(P2P3 *p, P3GKIND kind) { CL c; CL c2; int cc; ebuf_clear(&p->buf3); c = p3iget(p); p3iunget(p,c); startlno = c.l; if (kind == P3GK_LINE) { while (1) { c = p3iget(p); if ((c.c == EOF) || (chclass(c.c) & CC_NL)) break; ebuf_append1(&p->buf3,c.c); } p3iunget(p,c); return(pptoken_with_str(p,PPT_LINE)); } c = p3iget(p); if (c.c == EOF) return(pptoken_of_type(PPT_EOF)); cc = chclass(c.c); if (cc & CC_NL) return(pptoken_of_type(PPT_NEWLINE)); if (kind == P3GK_HEADER) { switch (c.c) { case '<': return(phase3_stringish(p,'<','>',PPT_HEADERNAME)); break; case '"': return(phase3_stringish(p,'"','"',PPT_HEADERNAME)); break; } } if (cc & CC_WHITE) { do { ebuf_append1(&p->buf3,c.c); c = p3iget(p); } while ((c.c != EOF) && (chclass(c.c) & CC_WHITE)); p3iunget(p,c); return(pptoken_with_str(p,PPT_WHITESPACE)); } if (cc & CC_IDENT) { do { ebuf_append1(&p->buf3,c.c); c = p3iget(p); } while ((c.c != EOF) && (chclass(c.c) & (CC_IDENT|CC_DIGIT))); p3iunget(p,c); return(pptoken_with_str(p,PPT_IDENT)); } if (cc & CC_DIGIT) return(phase3_ppnumber(p,c.c,0)); switch (c.c) { case '\'': return(phase3_stringish(p,'\'','\'',PPT_CHARCONST)); break; case '"': return(phase3_stringish(p,'"','"',PPT_STRING)); break; case '.': c = p3iget(p); if (chclass(c.c) & CC_DIGIT) return(phase3_ppnumber(p,c.c,1)); if (c.c == '.') { c2 = p3iget(p); if (c2.c == '.') return(pptoken_punct(PPP_ELLIPSIS)); p3iunget(p,c2); } p3iunget(p,c); return(pptoken_punct(PPP_DOT)); break; case '!': { PPPUNCTTYPE witheq; PPPUNCTTYPE noeq; witheq = PPP_NEQ; noeq = PPP_BANG; if (0) { case '%': witheq = PPP_MODASGN; noeq = PPP_PERCENT; } if (0) { case '*': witheq = PPP_MULASGN; noeq = PPP_STAR; } if (0) { case '=': witheq = PPP_EQ; noeq = PPP_ASSIGN; } if (0) { case '^': witheq = PPP_XORASGN; noeq = PPP_HAT; } c = p3iget(p); if (c.c == '=') return(pptoken_punct(witheq)); p3iunget(p,c); return(pptoken_punct(noeq)); } break; case '/': c = p3iget(p); switch (c.c) { case '/': return(pptoken_comment_slashslash(p)); break; case '*': return(pptoken_comment_slashstar(p)); break; case '=': return(pptoken_punct(PPP_DIVASGN)); break; default: p3iunget(p,c); return(pptoken_punct(PPP_SLASH)); break; } break; case '&': { PPPUNCTTYPE witheq; PPPUNCTTYPE doubled; PPPUNCTTYPE bare; witheq = PPP_ANDASGN; doubled = PPP_ANDAND; bare = PPP_AND; if (0) { case '+': witheq = PPP_ADDASGN; doubled = PPP_PLUSPLUS; bare = PPP_PLUS; } if (0) { case '|': witheq = PPP_ORASGN; doubled = PPP_OROR; bare = PPP_OR; } c2 = p3iget(p); if (c2.c == '=') return(pptoken_punct(witheq)); if (c2.c == c.c) return(pptoken_punct(doubled)); p3iunget(p,c2); return(pptoken_punct(bare)); } break; case '-': c = p3iget(p); switch (c.c) { case '>': return(pptoken_punct(PPP_ARROW)); break; case '-': return(pptoken_punct(PPP_MINUSMINUS)); break; case '=': return(pptoken_punct(PPP_SUBASGN)); break; default: p3iunget(p,c); return(pptoken_punct(PPP_MINUS)); break; } break; case '<': { PPPUNCTTYPE witheq; PPPUNCTTYPE doubled; PPPUNCTTYPE doubledeq; PPPUNCTTYPE bare; witheq = PPP_LEQ; doubled = PPP_LSH; doubledeq = PPP_LSHASGN; bare = PPP_LT; if (0) { case '>': witheq = PPP_GEQ; doubled = PPP_RSH; doubledeq = PPP_RSHASGN; bare = PPP_GT; } c2 = p3iget(p); if (c2.c == '=') return(pptoken_punct(witheq)); if (c2.c == c.c) { c2 = p3iget(p); if (c2.c == '=') return(pptoken_punct(doubledeq)); p3iunget(p,c2); return(pptoken_punct(doubled)); } p3iunget(p,c2); return(pptoken_punct(bare)); } break; case '#': c = p3iget(p); if (c.c == '#') return(pptoken_punct(PPP_SSHARP)); p3iunget(p,c); return(pptoken_punct(PPP_SHARP)); break; case '[': return(pptoken_punct(PPP_LBRACK)); break; case ']': return(pptoken_punct(PPP_RBRACK)); break; case '(': return(pptoken_punct(PPP_LPAREN)); break; case ')': return(pptoken_punct(PPP_RPAREN)); break; case '{':/*}*/ return(pptoken_punct(PPP_LBRACE)); break; case /*{*/'}': return(pptoken_punct(PPP_RBRACE)); break; case '~': return(pptoken_punct(PPP_TILDE)); break; case '?': return(pptoken_punct(PPP_QUESTION)); break; case ':': return(pptoken_punct(PPP_COLON)); break; case ';': return(pptoken_punct(PPP_SEMI)); break; case ',': return(pptoken_punct(PPP_COMMA)); break; default: return(pptoken_other(c.c)); break; } } static const char *p3gkind_str(P3GKIND k) { switch (k) { case P3GK_NORMAL: return("P3GK_NORMAL"); break; case P3GK_HEADER: return("P3GK_HEADER"); break; case P3GK_LINE: return("P3GK_LINE"); break; } panic(); } static PPTOKEN *phase3_get(P2P3 *p, P3GKIND kind) { PPTOKEN *t; const char *how; if (p->push3) { t = p->push3; p->push3 = 0; how = "pushed"; } else { t = phase3_get_(p,kind); how = "new"; } if (verbose & VERB_PHASE3) { printf("phase3_get(%s) returning %s ",p3gkind_str(kind),how); pptoken_dump(stdout,t); printf("\n"); } return(t); } static PPTOKEN *phase3_get_skipws(P2P3 *p) { PPTOKEN *t; while (1) { t = phase3_get(p,P3GK_NORMAL); if (t->type != PPT_WHITESPACE) break; pptoken_free(t); } return(t); } static void phase3_push(P2P3 *p, PPTOKEN *t) { if (p->push3) panic(); p->push3 = t; } static void phase4_init(void) { istack = 0; atbol = 1; ifstack = 0; macros = 0; macroexpander_init(&p4mx); p4npush = 0; } static void phase4_done(void) { MACRO *m; if (ifstack) abort(); // see process_sourcefile() if (istack) abort(); // see process_sourcefile() while ((m = macros)) { macros = m->link; macro_free(m); } } static void expreval_fail(EXPREVAL *e, const char *errmsg) { error("invalid preprocessor expression: %s",errmsg); (*e->fail)(); } static void expreval_consume(EXPREVAL *e) { if (! e->list) panic(); e->list = e->list->link; } static PPVAL expreval_expression(EXPREVAL *); // forward static PPVAL pptoken_number_value(PPTOKEN *t) { static char *tbuf = 0; static int tlen = 0; if (t->type != PPT_NUMBER) panic(); if (tlen < t->str.len+1) { free(tbuf); tlen = t->str.len + 1; tbuf = malloc(tlen); } bcopy(t->str.data,tbuf,t->str.len); tbuf[t->str.len] = '\0'; return(strtoll(tbuf,0,0)); } static PPVAL pptoken_charconst_value(PPTOKEN *t) { PPVAL v; unsigned int cv; int i; int bq; int end; const unsigned char *dp; if (t->type != PPT_CHARCONST) panic(); v = 0; bq = 0; i = 1; end = t->str.len - 1; dp = (const void *) t->str.data; if (end < 2) panic(); if ((dp[0] != '\'') || (dp[end] != '\'')) panic(); while (i < end) { if (dp[i] == '\\') { if (i+1 >= end) panic(); switch (dp[i+1]) { default: cv = dp[i+1]; i += 2; break; case 'a': cv = 7; break; case 'b': cv = 8; break; case 'e': cv = 27; break; case 'f': cv = 12; break; case 'n': cv = 10; break; case 'r': cv = 13; break; case 't': cv = 9; break; case 'v': cv = 11; break; case '0': cv = 0; if (0) { case '1': cv = 1; } if (0) { case '2': cv = 2; } if (0) { case '3': cv = 3; } if (0) { case '4': cv = 4; } if (0) { case '5': cv = 5; } if (0) { case '6': cv = 6; } if (0) { case '7': cv = 7; } if (i+2 < end) { switch (dp[i+2]) { default: i += 2; break; case '0': cv = (cv << 3) | 0; if (0) { case '1': cv = (cv << 3) | 1; } if (0) { case '2': cv = (cv << 3) | 2; } if (0) { case '3': cv = (cv << 3) | 3; } if (0) { case '4': cv = (cv << 3) | 4; } if (0) { case '5': cv = (cv << 3) | 5; } if (0) { case '6': cv = (cv << 3) | 6; } if (0) { case '7': cv = (cv << 3) | 7; } if (i+3 < end) { switch (dp[i+2]) { default: i += 3; break; case '0': cv = (cv << 3) | 0; if (0) { case '1': cv = (cv << 3) | 1; } if (0) { case '2': cv = (cv << 3) | 2; } if (0) { case '3': cv = (cv << 3) | 3; } if (0) { case '4': cv = (cv << 3) | 4; } if (0) { case '5': cv = (cv << 3) | 5; } if (0) { case '6': cv = (cv << 3) | 6; } if (0) { case '7': cv = (cv << 3) | 7; } i += 4; break; } } else { i += 3; } break; } } else { i += 2; } break; } } else { cv = dp[i++]; } v = (v << 8) | (unsigned char) cv; } return(v); } static PPVAL expreval_primary_expr(EXPREVAL *e) { PPVAL v; if (e->list && (e->list->token->type == PPT_IDENT)) { v = 0; expreval_consume(e); } else if (e->list && (e->list->token->type == PPT_NUMBER)) { v = pptoken_number_value(e->list->token); expreval_consume(e); } else if (e->list && (e->list->token->type == PPT_CHARCONST)) { v = pptoken_charconst_value(e->list->token); expreval_consume(e); } else if (e->list && (e->list->token->type == PPT_PUNCT) && (e->list->token->detail.punct == PPP_LPAREN)) { expreval_consume(e); v = expreval_expression(e); if (! (e->list && (e->list->token->type == PPT_PUNCT) && (e->list->token->detail.punct == PPP_RPAREN))) expreval_fail(e,"improperly nested parentheses"); expreval_consume(e); } return(v); } static PPVAL expreval_postfix_expr(EXPREVAL *e) { return(expreval_primary_expr(e)); } static PPVAL expreval_unary_expr(EXPREVAL *e) { if (e->list && (e->list->token->type == PPT_PUNCT) && (e->list->token->detail.punct == PPP_PLUS)) { expreval_consume(e); return(expreval_unary_expr(e)); } else if (e->list && (e->list->token->type == PPT_PUNCT) && (e->list->token->detail.punct == PPP_MINUS)) { expreval_consume(e); return(-expreval_unary_expr(e)); } else if (e->list && (e->list->token->type == PPT_PUNCT) && (e->list->token->detail.punct == PPP_TILDE)) { expreval_consume(e); return(~expreval_unary_expr(e)); } else if (e->list && (e->list->token->type == PPT_PUNCT) && (e->list->token->detail.punct == PPP_BANG)) { expreval_consume(e); return(!expreval_unary_expr(e)); } else { return(expreval_postfix_expr(e)); } } static PPVAL expreval_cast_expr(EXPREVAL *e) { return(expreval_unary_expr(e)); } static PPVAL expreval_multiplicative_expr(EXPREVAL *e) { PPVAL lhs; PPVAL rhs; lhs = expreval_cast_expr(e); while (1) { if (e->list && (e->list->token->type == PPT_PUNCT) && (e->list->token->detail.punct == PPP_STAR)) { expreval_consume(e); rhs = expreval_cast_expr(e); lhs *= rhs; } else if (e->list && (e->list->token->type == PPT_PUNCT) && (e->list->token->detail.punct == PPP_SLASH)) { expreval_consume(e); rhs = expreval_cast_expr(e); if (rhs == 0) expreval_fail(e,"division by zero"); lhs /= rhs; } else if (e->list && (e->list->token->type == PPT_PUNCT) && (e->list->token->detail.punct == PPP_PERCENT)) { expreval_consume(e); rhs = expreval_cast_expr(e); if (rhs == 0) expreval_fail(e,"mod by zero"); lhs %= rhs; } else { break; } } return(lhs); } static PPVAL expreval_additive_expr(EXPREVAL *e) { PPVAL lhs; PPVAL rhs; lhs = expreval_multiplicative_expr(e); while (1) { if (e->list && (e->list->token->type == PPT_PUNCT) && (e->list->token->detail.punct == PPP_PLUS)) { expreval_consume(e); rhs = expreval_multiplicative_expr(e); lhs += rhs; } else if (e->list && (e->list->token->type == PPT_PUNCT) && (e->list->token->detail.punct == PPP_MINUS)) { expreval_consume(e); rhs = expreval_multiplicative_expr(e); lhs -= rhs; } else { break; } } return(lhs); } static PPVAL expreval_shift_expr(EXPREVAL *e) { PPVAL lhs; PPVAL rhs; lhs = expreval_additive_expr(e); while (1) { if (e->list && (e->list->token->type == PPT_PUNCT) && (e->list->token->detail.punct == PPP_LSH)) { expreval_consume(e); rhs = expreval_additive_expr(e); lhs <<= rhs; } else if (e->list && (e->list->token->type == PPT_PUNCT) && (e->list->token->detail.punct == PPP_RSH)) { expreval_consume(e); rhs = expreval_additive_expr(e); lhs >>= rhs; } else { break; } } return(lhs); } static PPVAL expreval_relational_expr(EXPREVAL *e) { PPVAL lhs; PPVAL rhs; lhs = expreval_shift_expr(e); while (1) { if (e->list && (e->list->token->type == PPT_PUNCT) && (e->list->token->detail.punct == PPP_LT)) { expreval_consume(e); rhs = expreval_shift_expr(e); lhs = (lhs < rhs); } else if (e->list && (e->list->token->type == PPT_PUNCT) && (e->list->token->detail.punct == PPP_GT)) { expreval_consume(e); rhs = expreval_shift_expr(e); lhs = (lhs > rhs); } else if (e->list && (e->list->token->type == PPT_PUNCT) && (e->list->token->detail.punct == PPP_LEQ)) { expreval_consume(e); rhs = expreval_shift_expr(e); lhs = (lhs <= rhs); } else if (e->list && (e->list->token->type == PPT_PUNCT) && (e->list->token->detail.punct == PPP_GEQ)) { expreval_consume(e); rhs = expreval_shift_expr(e); lhs = (lhs >= rhs); } else { break; } } return(lhs); } static PPVAL expreval_equality_expr(EXPREVAL *e) { PPVAL lhs; PPVAL rhs; lhs = expreval_relational_expr(e); while (1) { if (e->list && (e->list->token->type == PPT_PUNCT) && (e->list->token->detail.punct == PPP_EQ)) { expreval_consume(e); rhs = expreval_relational_expr(e); lhs = (lhs == rhs); } else if (e->list && (e->list->token->type == PPT_PUNCT) && (e->list->token->detail.punct == PPP_NEQ)) { expreval_consume(e); rhs = expreval_relational_expr(e); lhs = (lhs != rhs); } else { break; } } return(lhs); } static PPVAL expreval_AND_expr(EXPREVAL *e) { PPVAL lhs; PPVAL rhs; lhs = expreval_equality_expr(e); while (e->list && (e->list->token->type == PPT_PUNCT) && (e->list->token->detail.punct == PPP_AND)) { expreval_consume(e); rhs = expreval_equality_expr(e); lhs &= rhs; } return(lhs); } static PPVAL expreval_exclusive_OR_expr(EXPREVAL *e) { PPVAL lhs; PPVAL rhs; lhs = expreval_AND_expr(e); while (e->list && (e->list->token->type == PPT_PUNCT) && (e->list->token->detail.punct == PPP_HAT)) { expreval_consume(e); rhs = expreval_AND_expr(e); lhs ^= rhs; } return(lhs); } static PPVAL expreval_inclusive_OR_expr(EXPREVAL *e) { PPVAL lhs; PPVAL rhs; lhs = expreval_exclusive_OR_expr(e); while (e->list && (e->list->token->type == PPT_PUNCT) && (e->list->token->detail.punct == PPP_OR)) { expreval_consume(e); rhs = expreval_exclusive_OR_expr(e); lhs |= rhs; } return(lhs); } static PPVAL expreval_logical_AND_expr(EXPREVAL *e) { PPVAL lhs; PPVAL rhs; lhs = expreval_inclusive_OR_expr(e); while (e->list && (e->list->token->type == PPT_PUNCT) && (e->list->token->detail.punct == PPP_ANDAND)) { expreval_consume(e); rhs = expreval_inclusive_OR_expr(e); lhs = (lhs && rhs); } return(lhs); } static PPVAL expreval_logical_OR_expr(EXPREVAL *e) { PPVAL lhs; PPVAL rhs; lhs = expreval_logical_AND_expr(e); while (e->list && (e->list->token->type == PPT_PUNCT) && (e->list->token->detail.punct == PPP_OROR)) { expreval_consume(e); rhs = expreval_logical_AND_expr(e); lhs = (lhs || rhs); } return(lhs); } static PPVAL expreval_conditional_expr(EXPREVAL *e) { PPVAL lhs; PPVAL mhs; PPVAL rhs; lhs = expreval_logical_OR_expr(e); if (e->list && (e->list->token->type == PPT_PUNCT) && (e->list->token->detail.punct == PPP_QUESTION)) { expreval_consume(e); mhs = expreval_expression(e); if (e->list && (e->list->token->type == PPT_PUNCT) && (e->list->token->detail.punct == PPP_COLON)) { expreval_consume(e); rhs = expreval_conditional_expr(e); return(lhs?mhs:rhs); } expreval_fail(e,"improper ? : operator"); } return(lhs); } static PPVAL expreval_constant_expr(EXPREVAL *e) { return(expreval_conditional_expr(e)); } static PPVAL expreval_assignment_expr(EXPREVAL *e) { return(expreval_conditional_expr(e)); } static PPVAL expreval_expression(EXPREVAL *e) { return(expreval_assignment_expr(e)); } static PPVAL expreval_eval(EXPREVAL *e) { PPTLIST *l; PPVAL v; if (verbose & VERB_EVAL) { printf("expreval_eval: token list\n"); for (l=e->list;l;l=l->link) { printf("\t%p/%p ",(void *)l,(void *)l->token); pptoken_dump(stdout,l->token); printf("\n"); } } v = expreval_constant_expr(e); if (e->list) error("junk after expression"); return(v); } static PPVAL eval_pp_expr(PPTLIST *list) { __label__ failto; PPTCONC explist; MACROEXPANDER mx; PPTOKEN *t; PPVAL val; EXPREVAL ee; PPTOKEN *listpop(void) { PPTLIST *l; PPTOKEN *t; l = list; if (! l) return(0); list = l->link; t = l->token; free(l); return(t); } PPTOKEN *popnws(void) { PPTOKEN *t; do { t = listpop(); } while (t && ((t->type == PPT_WHITESPACE) || (t->type == PPT_NEWLINE))); return(t); } void fail(void) { val = 0; VERB(EVAL,"eval_pp_expr failure exit\n"); goto failto; } if (verbose & VERB_EVAL) { PPTLIST *l; printf("eval_pp_expr ENTRY, list\n"); for (l=list;l;l=l->link) { printf("\t%p/%p ",(void *)l,(void *)l->token); pptoken_dump(stdout,l->token); printf("\n"); } } macroexpander_init(&mx); pptconc_init(&explist); while <"expand"> (1) { t = macroexpander_get(&mx); if (! t) { t = listpop(); VERB(EVAL,"eval_pp_expr expander loop popped %p, list now %p\n",(void *)t,(void *)list); if (! t) break; if ((t->type == PPT_IDENT) && (t->str.len == 7) && !bcmp(t->str.data,"defined",7)) { pptoken_free(t); t = popnws(); VERB(EVAL,"eval_pp_expr defined popped %p, list now %p\n",(void *)t,(void *)list); if (t && (t->type == PPT_PUNCT) && (t->detail.punct == PPP_LPAREN)) { pptoken_free(t); t = popnws(); VERB(EVAL,"eval_pp_expr defined() popped %p, list now %p\n",(void *)t,(void *)list); { PPTOKEN *t2; t2 = popnws(); VERB(EVAL,"eval_pp_expr defined() flushed %p, list now %p\n",(void *)t2,(void *)list); pptoken_free(t2); } } if (verbose & VERB_EVAL) { printf("eval_pp_expr defined arg is "); pptoken_dump(stdout,t); printf("\n"); } val = t && (t->type == PPT_IDENT) && macro_find(t); pptoken_free(t); t = pptoken_of_type(PPT_NUMBER); t->str.data = malloc(1); t->str.data[0] = val ? '1' : '0'; t->str.len = 1; t->str.needsfree = 1; } t = macroexpander_input(&mx,t); } pptconc_nwstoken(&explist,t); } while (1) { t = macroexpander_flush(&mx); if (! t) break; pptconc_nwstoken(&explist,t); } list = pptconc_done(&explist,0); if (verbose & VERB_EVAL) { PPTLIST *l; printf("eval_pp_expr after expanding\n"); for (l=list;l;l=l->link) { printf("\t%p/%p ",(void *)l,(void *)l->token); pptoken_dump(stdout,l->token); printf("\n"); } } ee.list = list; ee.fail = &fail; val = expreval_eval(&ee); VERB(EVAL,"eval_pp_expr normal exit, val = %lld\n",val); failto:; pptlist_free(list); return(val); } static PPTOKEN *phase4_flush_line(void) { PPTOKEN *t; while (1) { t = phase3_get(&p2p3,P3GK_NORMAL); if (t->type == PPT_NEWLINE) return(t); pptoken_free(t); } } static int phase4_iftrue(void) { if (! ifstack) return(1); switch (ifstack->state) { case IFS_FALSE: case IFS_OVER: case IFS_ELSE_F: case IFS_NEST: break; case IFS_TRUE: case IFS_ELSE_T: return(1); break; default: panic(); break; } return(0); } static const char *ifstate_string(IFSTATE state) { switch (state) { case IFS_FALSE: return("FALSE"); break; case IFS_TRUE: return("TRUE"); break; case IFS_OVER: return("OVER"); break; case IFS_ELSE_F: return("ELSE_F"); break; case IFS_ELSE_T: return("ELSE_T"); break; case IFS_NEST: return("NEST"); break; } panic(); return("(invalid)"); } static void ifstack_dump(void) { IFSTACK *s; if (verbose & VERB_IF) { printf("ifstack now:"); for (s=ifstack;s;s=s->link) printf(" %s",ifstate_string(s->state)); printf("\n"); } } static void phase4_start_if(IFSTATE state) { IFSTACK *s; VERB(IF,"phase4_start_if %s\n",ifstate_string(state)); s = malloc(sizeof(IFSTACK)); s->state = state; sfref_init(&s->start_sf,"start of #if"); sfref_addref(&s->start_sf,sfref_follow(&cursf_)); s->start_line = sfref_follow(&cursf_)->lno; s->link = ifstack; ifstack = s; ifstack_dump(); } static void phase4_end_if(void) { IFSTACK *s; VERB(IF,"phase4_end_if\n"); s = ifstack; ifstack = s->link; sfref_done(&s->start_sf); free(s); ifstack_dump(); } static PPTOKEN *phase4_macro_arglist(int (*arg)(PPTOKEN *)) { PPTOKEN *t; PPTOKEN *t2; t = phase3_get_skipws(&p2p3); if (t->type == PPT_EOF) { error("EOF reading macro argument formals"); return(t); } if ((t->type == PPT_PUNCT) && (t->detail.punct == PPP_RPAREN)) { pptoken_free(t); return(0); } if ((t->type == PPT_PUNCT) && (t->detail.punct == PPP_ELLIPSIS)) { t2 = phase3_get_skipws(&p2p3); if (t2->type == PPT_EOF) { error("EOF reading macro argument formals"); pptoken_free(t); return(t2); } if ((t2->type == PPT_PUNCT) && (t2->detail.punct == PPP_RPAREN)) { (*arg)(t); pptoken_free(t2); return(0); } error("... in macro formal list isn't followed by )"); pptoken_free(t); phase3_push(&p2p3,t2); return(phase4_flush_line()); } if ((t->type == PPT_PUNCT) && (t->detail.punct == PPP_COMMA)) { error("missing macro formal argument"); phase3_push(&p2p3,t); return(phase4_flush_line()); } if (t->type != PPT_IDENT) { error("macro formal argument isn't an identifier"); phase3_push(&p2p3,t); return(phase4_flush_line()); } while (1) { if ((*arg)(t)) return(phase4_flush_line()); t = phase3_get_skipws(&p2p3); if (t->type == PPT_EOF) { error("EOF reading macro argument formals"); return(t); } if ((t->type == PPT_PUNCT) && (t->detail.punct == PPP_RPAREN)) { pptoken_free(t); return(0); } if (t->type == PPT_EOF) { error("EOF reading macro argument formals"); return(t); } if (! ((t->type == PPT_PUNCT) && (t->detail.punct == PPP_COMMA))) { error("invalid macro argument list delimiter"); phase3_push(&p2p3,t); return(phase4_flush_line()); } pptoken_free(t); t = phase3_get_skipws(&p2p3); if (t->type == PPT_IDENT) continue; if ((t->type == PPT_PUNCT) && (t->detail.punct == PPP_ELLIPSIS)) { t2 = phase3_get_skipws(&p2p3); if (t2->type == PPT_EOF) { error("EOF reading macro argument formals"); pptoken_free(t); return(t2); } if ((t2->type == PPT_PUNCT) && (t2->detail.punct == PPP_RPAREN)) { (*arg)(t); pptoken_free(t2); return(0); } error("... in macro formal list isn't followed by )"); pptoken_free(t); phase3_push(&p2p3,t2); return(phase4_flush_line()); } error("macro formal argument isn't an identifier"); phase3_push(&p2p3,t); return(phase4_flush_line()); } } static PPTOKEN *phase4_rest_of_line(PPTLIST **listp) { PPTOKEN *t; PPTLIST **rtail; PPTLIST **wstail; PPTLIST *l; rtail = listp; wstail = 0; t = phase3_get_skipws(&p2p3); phase3_push(&p2p3,t); while (1) { t = phase3_get(&p2p3,P3GK_NORMAL); switch (t->type) { case PPT_EOF: case PPT_NEWLINE: *rtail = 0; if (wstail) { pptlist_free(*wstail); *wstail = 0; } return(t); break; case PPT_WHITESPACE: if (! wstail) wstail = rtail; break; default: wstail = 0; break; } l = pptlist_holding(t); *rtail = l; rtail = &l->link; } } static PPTOKEN *phase4_do_define(void) { PPTOKEN *name; PPTOKEN *next; PPTOKEN *t; MACRO *m; PPTLIST *l; int i; PPTOKEN *pnt; PPTOKEN *r; int add_formal(PPTOKEN *t) { int i; if ((t->type == PPT_PUNCT) && (t->detail.punct == PPP_ELLIPSIS)) { pptoken_free(t); m->flags |= MF_VARARGS; return(0); } if (t->type != PPT_IDENT) panic(); for (i=m->nargs-1;i>=0;i--) { if (str_equal_str(&t->str,&m->args[i]->str)) { error("duplicate macro formal (args %d and %d)",m->nargs+1,i+1); pptoken_free(t); return(1); } } m->nargs ++; m->args = realloc(m->args,m->nargs*sizeof(*m->args)); m->args[m->nargs-1] = t; return(0); } VERB(DEFINE,"phase4_do_define ENTRY\n"); name = phase3_get_skipws(&p2p3); if (verbose & VERB_DEFINE) { printf("phase4_do_define name is %p ",(void *)name); pptoken_dump(stdout,name); printf("\n"); } if (name->type != PPT_IDENT) { error("#define with invalid name"); phase3_push(&p2p3,name); return(phase4_flush_line()); } m = malloc(sizeof(MACRO)); m->name = str_copy(name->str); m->flags = 0; m->nargs = 0; m->args = 0; m->replacement = 0; pptoken_free(name); next = phase3_get(&p2p3,P3GK_NORMAL); if ((next->type == PPT_PUNCT) && (next->detail.punct == PPP_LPAREN)) { VERB(DEFINE,"phase4_do_define: function-like\n"); pptoken_free(next); t = phase4_macro_arglist(&add_formal); if (t) { macro_free(m); if (verbose & VERB_DEFINE) { printf("phase4_do_define: arglist parse failed, returning %p ",(void *)t); pptoken_dump(stdout,t); printf("\n"); } return(t); } if (verbose & VERB_DEFINE) { printf("phase4_do_define: arg count %d:\n",m->nargs); for (i=0;inargs;i++) { printf("\t%p ",(void *)m->args[i]); pptoken_dump(stdout,m->args[i]); printf("\n"); } } m->flags |= MF_HASARGS; } else if (next->type == PPT_WHITESPACE) { VERB(DEFINE,"phase4_do_define: object-like\n"); pptoken_free(next); } else if (next->type == PPT_NEWLINE) { VERB(DEFINE,"phase4_do_define: object-like and empty\n"); phase3_push(&p2p3,next); } else if (next->type != PPT_WHITESPACE) { error("#define is neither object-like nor function-like"); macro_free(m); phase3_push(&p2p3,next); return(phase4_flush_line()); } t = phase4_rest_of_line(&m->replacement); if (verbose & VERB_DEFINE) { printf("phase4_do_define: initial replacement:\n"); for (l=m->replacement;l;l=l->link) { printf("\t%p/%p ",(void *)l,(void *)l->token); pptoken_dump(stdout,l->token); printf("\n"); } } switch <"done"> (t->type) { case PPT_EOF: error("EOF reading #define replacement text"); macro_free(m); break; case PPT_NEWLINE: do <"fail"> { if (! (m->flags & MF_VARARGS)) { for (l=m->replacement;l;l=l->link) { if ( (l->token->type == PPT_IDENT) && (l->token->str.len == 11) && !bcmp(l->token->str.data,"__VA_ARGS__",11) ) break; } if (l) { error("__VA_ARGS__ used in a non-varargs macro"); break <"fail">; } } if (m->replacement) { r = m->replacement->token; if ((r->type == PPT_PUNCT) && (r->detail.punct == PPP_SSHARP)) { error("#define replacement text begins with ##"); break <"fail">; } for (l=m->replacement;l->link;l=l->link) ; r = l->token; if ((r->type == PPT_PUNCT) && (r->detail.punct == PPP_SSHARP)) { error("#define replacement text ends with ##"); break <"fail">; } if (m->flags & MF_HASARGS) { for (l=m->replacement;l;l=l->link) { i = macro_arg_no(m,l->token); if (i >= 0) { pptoken_free(l->token); l->token = pptoken_macroarg(i); } } } for (l=m->replacement;l;l=l->link) { r = l->token; if ((r->type == PPT_PUNCT) && (r->detail.punct == PPP_SHARP)) { do l = l->link; while (l && (l->token->type == PPT_WHITESPACE)); if (!l || (l->token->type != PPT_MACROARG)) { error("#define replacement text includes a # not followed by an argument"); break <"fail">; } l->token->detail.macroarg.flags |= MAF_NOEXPAND; } } pnt = 0; for (l=m->replacement;l;l=l->link) { if (l->token->type == PPT_WHITESPACE) continue; if (pnt) { if ((pnt->type == PPT_MACROARG) && (l->token->type == PPT_PUNCT) && (l->token->detail.punct == PPP_SSHARP)) { pnt->detail.macroarg.flags |= MAF_NOEXPAND; } if ((l->token->type == PPT_MACROARG) && (pnt->type == PPT_PUNCT) && (pnt->detail.punct == PPP_SSHARP)) { l->token->detail.macroarg.flags |= MAF_NOEXPAND; } } pnt = l->token; } } if (verbose & VERB_DEFINE) { printf("phase4_do_define: massaged replacement:\n"); for (l=m->replacement;l;l=l->link) { printf("\t%p/%p ",(void *)l,(void *)l->token); pptoken_dump(stdout,l->token); printf("\n"); } } macro_define(m); break <"done">; } while (0); macro_free(m); break; default: panic(); break; } return(t); } static PPTOKEN *phase4_one_name_line(const char *what) { PPTOKEN *name; PPTOKEN *t; name = phase3_get_skipws(&p2p3); switch (name->type) { case PPT_IDENT: break; case PPT_EOF: error("EOF partway through #%s line",what); phase3_push(&p2p3,name); return(0); break; case PPT_NEWLINE: error("#%s line with no name",what); phase3_push(&p2p3,name); return(0); break; default: error("#%s with invalid name",what); phase3_push(&p2p3,name); return(0); break; } t = phase3_get_skipws(&p2p3); if (t->type == PPT_EOF) { error("EOF partway through #%s line",what); pptoken_free(name); phase3_push(&p2p3,t); return(0); } if (t->type != PPT_NEWLINE) { error("#%s with garbage after name",what); pptoken_free(name); pptoken_free(t); return(0); } phase3_push(&p2p3,t); return(name); } static int phase4_bare_line(const char *what) { PPTOKEN *t; t = phase3_get_skipws(&p2p3); if (t->type == PPT_EOF) { error("EOF partway through #%s line",what); phase3_push(&p2p3,t); return(0); } if (t->type != PPT_NEWLINE) { error("garbage on #%s line",what); pptoken_free(t); return(0); } phase3_push(&p2p3,t); return(1); } static PPTOKEN *phase4_do_undef(void) { PPTOKEN *name; VERB(DEFINE,"phase4_do_undef ENTRY\n"); name = phase4_one_name_line("undef"); if (verbose & VERB_DEFINE) { printf("phase4_do_undef name "); pptoken_dump(stdout,name); printf("\n"); } if (name) { macro_undef(&name->str); pptoken_free(name); } return(phase4_flush_line()); } static PPTOKEN *phase4_do_ifdef(void) { PPTOKEN *name; VERB(IF,"phase4_do_ifdef ENTRY\n"); if (! phase4_iftrue()) { VERB(IF,"phase4_do_ifdef already in false if\n"); phase4_start_if(IFS_NEST); } else { name = phase4_one_name_line("ifdef"); if (verbose & VERB_IF) { printf("phase4_do_ifdef name "); pptoken_dump(stdout,name); printf("\n"); } if (name) { phase4_start_if(macro_find(name)?IFS_TRUE:IFS_FALSE); pptoken_free(name); } } return(phase4_flush_line()); } static PPTOKEN *phase4_do_ifndef(void) { PPTOKEN *name; VERB(IF,"phase4_do_ifndef ENTRY\n"); if (! phase4_iftrue()) { VERB(IF,"phase4_do_ifndef already in false if\n"); phase4_start_if(IFS_NEST); } else { name = phase4_one_name_line("ifndef"); if (verbose & VERB_IF) { printf("phase4_do_ifndef name "); pptoken_dump(stdout,name); printf("\n"); } if (name) { phase4_start_if(macro_find(name)?IFS_FALSE:IFS_TRUE); pptoken_free(name); } } return(phase4_flush_line()); } static PPTOKEN *phase4_do_else(void) { if (! ifstack) { error("#else with no matching conditional"); } else { VERB(IF,"phase4_do_else currently %s\n",ifstate_string(ifstack->state)); switch (ifstack->state) { case IFS_FALSE: if (phase4_bare_line("else")) ifstack->state = IFS_ELSE_T; break; case IFS_TRUE: if (phase4_bare_line("else")) ifstack->state = IFS_ELSE_F; break; case IFS_OVER: ifstack->state = IFS_ELSE_F; break; case IFS_ELSE_F: case IFS_ELSE_T: error("multiple #else blocks in a conditional"); ifstack->state = IFS_ELSE_F; break; case IFS_NEST: break; default: panic(); break; } ifstack_dump(); } return(phase4_flush_line()); } static PPTOKEN *phase4_do_endif(void) { VERB(IF,"phase4_do_endif\n"); if (ifstack && (ifstack->state == IFS_NEST)) { phase4_end_if(); } else if (phase4_bare_line("endif")) { if (ifstack) { phase4_end_if(); } else { error("#endif with no matching conditional"); } } return(phase4_flush_line()); } static PPTOKEN *phase4_do_if(void) { PPTLIST *expr; PPTOKEN *t; VERB(IF,"phase4_do_if\n"); if (phase4_iftrue()) { t = phase4_rest_of_line(&expr); switch (t->type) { case PPT_EOF: error("EOF reading #if expression"); pptlist_free(expr); break; case PPT_NEWLINE: phase4_start_if(eval_pp_expr(expr)?IFS_TRUE:IFS_FALSE); break; default: panic(); break; } return(t); } else { phase4_start_if(IFS_NEST); return(phase4_flush_line()); } } static PPTOKEN *phase4_do_elif(void) { PPTLIST *expr; PPTOKEN *t; VERB(IF,"phase4_do_elif\n"); if (! ifstack) { error("#elif with no matching conditional"); return(phase4_flush_line()); } t = phase4_rest_of_line(&expr); switch (t->type) { case PPT_EOF: error("EOF reading #elif expression"); pptlist_free(expr); break; case PPT_NEWLINE: switch (ifstack->state) { case IFS_FALSE: if (eval_pp_expr(expr)) ifstack->state = IFS_TRUE; break; case IFS_TRUE: ifstack->state = IFS_OVER; break; case IFS_OVER: break; case IFS_ELSE_F: case IFS_ELSE_T: error("#elif after #else"); ifstack->state = IFS_ELSE_F; break; case IFS_NEST: break; default: panic(); break; } ifstack_dump(); break; default: panic(); break; } return(t); } static PPTOKEN *phase4_do_include(void) { PPTOKEN *hn; PPTOKEN *t; VERB(INCLUDE,"phase4_do_include\n"); while (1) { hn = phase3_get(&p2p3,P3GK_HEADER); if (hn->type != PPT_WHITESPACE) break; pptoken_free(hn); } if (hn->type == PPT_HEADERNAME) { VERB(INCLUDE,"phase4_do_include: header name = %.*s\n",hn->str.len,hn->str.data); t = phase3_get_skipws(&p2p3); switch (t->type) { case PPT_EOF: error("EOF inside a #include line"); pptoken_free(hn); break; case PPT_NEWLINE: include_file(hn); break; default: error("#include with garbage after filename"); pptoken_free(hn); t = phase4_flush_line(); break; } } else { error("macro-expanded #include not yet supported"); t = phase4_flush_line(); } return(t); } static PPTOKEN *phase4_do_line(void) { printf("phase4_do_line\n"); return(phase4_flush_line()); } static PPTOKEN *phase4_do_error(void) { PPTOKEN *t; t = phase3_get(&p2p3,P3GK_LINE); error_at(sfref_follow(&t->file)->name,t->startline,"#error: %.*s",t->str.len,t->str.data); pptoken_free(t); return(phase4_flush_line()); } static PPTOKEN *pragma_body(P2P3 *p) { PPTOKEN *t; while (1) { t = phase3_get(p,P3GK_NORMAL); if ((t->type == PPT_NEWLINE) || (t->type == PPT_EOF)) return(t); printf("\t"); pptoken_dump(stdout,t); printf("\n"); pptoken_free(t); } } static PPTOKEN *phase4_do_pragma(P2P3 *p) { PPTOKEN *t; while (1) { t = phase3_get(p,P3GK_NORMAL); if (t->type != PPT_WHITESPACE) { phase3_push(p,t); break; } pptoken_free(t); } printf("#pragma:\n"); return(pragma_body(p)); } static PPTOKEN *phase4_do_eval(void) { PPTLIST *expr; PPTOKEN *t; char *s; int l; printf("phase4_do_eval\n"); t = phase4_rest_of_line(&expr); switch (t->type) { case PPT_EOF: error("EOF reading #eval expression"); pptlist_free(expr); break; case PPT_NEWLINE: phase3_push(&p2p3,t); l = asprintf(&s,"%lld",(long long int)eval_pp_expr(expr)); t = pptoken_of_type(PPT_NUMBER); t->str.data = s; t->str.len = l; t->str.needsfree = 1; break; default: panic(); break; } return(t); } static PPTOKEN *phase4_get_(void) { PPTOKEN *t; PPTOKEN *t2; int bol; t = macroexpander_get(&p4mx); if (t) return(t); while (1) { bol = atbol; t2 = 0; while <"eatwhite"> (1) { t = phase3_get(&p2p3,P3GK_NORMAL); switch (t->type) { case PPT_WHITESPACE: bol = 0; break; case PPT_NEWLINE: bol = 1; break; default: break <"eatwhite">; } if (! t2) t2 = t; else pptoken_free(t); } if (t2) { phase3_push(&p2p3,t); atbol = bol; t = t2; } if (bol && (t->type == PPT_PUNCT) && (t->detail.punct == PPP_SHARP)) { pptoken_free(t); t = phase3_get(&p2p3,P3GK_NORMAL); if (t->type == PPT_IDENT) { if (str_equal_c(&t->str,"if")) { pptoken_free(t); return(phase4_do_if()); } else if (str_equal_c(&t->str,"ifdef")) { pptoken_free(t); return(phase4_do_ifdef()); } else if (str_equal_c(&t->str,"ifndef")) { pptoken_free(t); return(phase4_do_ifndef()); } else if (str_equal_c(&t->str,"elif")) { pptoken_free(t); return(phase4_do_elif()); } else if (str_equal_c(&t->str,"else")) { pptoken_free(t); return(phase4_do_else()); } else if (str_equal_c(&t->str,"endif")) { pptoken_free(t); return(phase4_do_endif()); } else if (phase4_iftrue()) { if (str_equal_c(&t->str,"include")) { pptoken_free(t); return(phase4_do_include()); } else if (str_equal_c(&t->str,"define")) { pptoken_free(t); return(phase4_do_define()); } else if (str_equal_c(&t->str,"undef")) { pptoken_free(t); return(phase4_do_undef()); } else if (str_equal_c(&t->str,"line")) { pptoken_free(t); return(phase4_do_line()); } else if (str_equal_c(&t->str,"error")) { pptoken_free(t); return(phase4_do_error()); } else if (str_equal_c(&t->str,"pragma")) { pptoken_free(t); return(phase4_do_pragma(&p2p3)); } else if (str_equal_c(&t->str,"eval")) { pptoken_free(t); return(phase4_do_eval()); } else { error("unrecognizable preprocessor #%.*s line",t->str.len,t->str.data); pptoken_free(t); return(phase4_flush_line()); } } } else { error("unrecognizable preprocessor #%.*s line",t->str.len,t->str.data); pptoken_free(t); return(phase4_flush_line()); } } if (phase4_iftrue()) { t = macroexpander_input(&p4mx,t); if (t) return(t); } } } static void do__Pragma(PPTOKEN *arg) { P2P3 p; SRCFILE *sf; char *str; int ax; int l; int sx; SFREF savesf; char *fakename; if (arg->type != PPT_STRING) panic(); str = malloc(arg->str.len); ax = 0; l = arg->str.len; if (arg->str.data[ax] == 'L') { ax ++; l --; } if (arg->str.data[ax] != '"') panic(); if (arg->str.data[l-1] != '"') panic(); ax ++; l -= 2; sx = 0; for (;l>0;ax++,l--) { if ((l > 1) && (arg->str.data[ax] == '\\')) { if ((arg->str.data[ax+1] == '"') || (arg->str.data[ax+1] == '\\')) { str[sx++] = arg->str.data[ax+1]; ax ++; l --; continue; } } str[sx++] = arg->str.data[ax]; } phase2_init(&p); phase3_init(&p); sf = new_srcfile("_Pragma temp"); asprintf(&fakename,"%s _Pragma",sfref_follow(&arg->file)->name); sf->name = fakename; sf->f = open_string(str,sx); sf->lno = arg->startline; sfref_init(&savesf,"_Pragma cursave"); sfref_addref(&savesf,sfref_follow(&cursf_)); sfref_drop(&cursf_); sfref_addref(&cursf_,sf); printf("_Pragma:\n"); pptoken_free(pragma_body(&p)); fclose(sf->f); sfref_drop(&cursf_); sfref_addref(&cursf_,sfref_follow(&savesf)); sfref_done(&savesf); phase2_done(&p); phase3_done(&p); free(str); } static PPTOKEN *phase4_get(void) { PPTOKEN *t; PPTOKEN *prag[3]; int np; np = 0; while (1) { if (p4npush > 0) { t = p4pushed[--p4npush]; } else { t = phase4_get_(); } switch (np) { case 0: if ((t->type == PPT_IDENT) && (t->str.len == 7) && !bcmp(t->str.data,"_Pragma",7)) { prag[np++] = t; continue; } break; case 1: if ((t->type == PPT_PUNCT) && (t->detail.punct == PPP_LPAREN)) { prag[np++] = t; continue; } break; case 2: if (t->type == PPT_STRING) { prag[np++] = t; continue; } break; case 3: if ((t->type == PPT_PUNCT) && (t->detail.punct == PPP_RPAREN)) { pptoken_free(prag[0]); pptoken_free(prag[1]); pptoken_free(t); do__Pragma(prag[2]); pptoken_free(prag[2]); np = 0; continue; } break; } if (np > 0) { p4pushed[p4npush++] = t; while (np > 1) p4pushed[p4npush++] = prag[--np]; t = prag[0]; } if (verbose & VERB_PHASE4) { printf("phase4_get: got %p ",(void *)t); pptoken_dump(stdout,t); printf("\n"); } return(t); } } static void istack_free(ISTACK *s) { sfref_done(&s->sf); free(s); } static void pop_istack(void) { ISTACK *s; SRCFILE *c; SRCFILE *n; s = istack; istack = s->link; c = sfref_follow(&cursf_); if (sfref_follow(&s->sf) != c) panic(); n = sfref_follow(&istack->sf); if (n != sfref_follow(&c->link)) panic(); fclose(c->f); sfref_drop(&cursf_); sfref_addref(&cursf_,n); sfref_done(&s->sf); istack_free(s); } static void check_if_nesting(void) { SRCFILE *c; c = sfref_follow(&cursf_); while (ifstack && (sfref_follow(&ifstack->start_sf) == c)) { error_at(c->name,ifstack->start_line,"conditional still open at EOF"); phase4_end_if(); } } #if 0 static TOKEN *phase7a_get(void) { PPTOKEN *t; do t = phase4_get(); while ((t->type == PPT_NEWLINE) || (t->type == PPT_WHITESPACE)) ; return(0); } #endif static void Eflag_output(EFLAG_STATE *s, PPTOKEN *t) { int wasatws; if (t->type == PPT_EOF) { pptoken_free(t); return; } if ((s->line < 0) || (sfref_follow(&s->file) != sfref_follow(&t->file)) || (t->startline < s->line) || (t->startline > s->line+4)) { if (! s->atnl) printf("\n"); printf("# %d %s\n",t->startline,sfref_follow(&t->file)->name); sfref_drop(&s->file); sfref_addref(&s->file,sfref_follow(&t->file)); s->line = t->startline; s->atnl = 1; s->atws = 1; } if (t->type == PPT_NEWLINE) { // let startline processing take care of it pptoken_free(t); return; } if (t->type == PPT_WHITESPACE) { if (! s->atws) { printf(" "); s->atws = 1; s->atnl = 0; } pptoken_free(t); return; } while (t->startline > s->line) { printf("\n"); s->line ++; s->atnl = 1; s->atws = 1; } wasatws = s->atws; s->atws = 0; switch (t->type) { default: panic(); break; case PPT_IDENT: switch (s->lasttt) { case PPT_IDENT: case PPT_NUMBER: case PPT_SYNTH: printf(" "); break; default: break; } fwrite(t->str.data,1,t->str.len,stdout); s->atnl = 0; break; case PPT_NUMBER: switch (s->lasttt) { case PPT_IDENT: case PPT_NUMBER: case PPT_SYNTH: case PPT_PUNCT: // don't attach . to a number printf(" "); break; default: break; } fwrite(t->str.data,1,t->str.len,stdout); s->atnl = 0; break; case PPT_CHARCONST: case PPT_STRING: case PPT_SYNTH: fwrite(t->str.data,1,t->str.len,stdout); s->atnl = 0; break; case PPT_PUNCT: switch (s->lasttt) { case PPT_PUNCT: case PPT_NUMBER: // don't attach . to a number printf(" "); break; default: break; } printf("%s",pp_punct_str(t->detail.punct)); s->atnl = 0; break; case PPT_OTHER: if (! wasatws) printf(" "); fwrite(t->str.data,1,t->str.len,stdout); s->atnl = 0; break; } s->lasttt = t->type; pptoken_free(t); } static void Eflag_done(EFLAG_STATE *s) { if (! s->atnl) printf("\n"); sfref_drop(&s->file); s->line = -1; s->lasttt = PPT_EOF; } static void process_sourcefile(SRCFILE *sf) { PPTOKEN *t; int iseof; sfref_addref(&cursf_,sf); phase2_init(&p2p3); phase3_init(&p2p3); if (Eflag) { while (1) { t = phase4_get(); iseof = (t->type == PPT_EOF); Eflag_output(&Estate,t); if (iseof) { if (! istack) break; check_if_nesting(); pop_istack(); continue; } } } else { error("real compilation not yet implemented"); } while (ifstack) { error("conditional unclosed at end of file (opened at \"%s\", line %d)",sfref_follow(&ifstack->start_sf)->name,ifstack->start_line); phase4_end_if(); } phase3_done(&p2p3); phase2_done(&p2p3); sfref_drop(&cursf_); } static void process_sourcefiles(void) { SRCFILE *sf; sfref_init(&cursf_,"current sf"); phase4_init(); for (sf=sfref_follow(&srcfiles);sf;sf=sfref_follow(&sf->link)) process_sourcefile(sf); if (Eflag) Eflag_done(&Estate); phase4_done(); } int main(int, char **); int main(int ac, char **av) { handleargs(ac,av); if (! sfref_follow(&srcfiles)) { fprintf(stderr,"%s: no source files\n",__progname); exit(1); } errs = 0; open_sourcefiles(); open_predefines(); if (errs) exit(1); process_sourcefiles(); if (errs) exit(1); return(0); } /* preprocessing-token header-name identifier pp-number character-constant string-literal punctuator each non-white-space character that cannot be one of the above header-name < h-char+ > " q-char+ " h-char = any char except newline and > q-char = any char except newline and " identifier [A-Za-z_][A-Za-z_0-9]* pp-number [.]?[0-9]([0-9a-zA-Z_]|[eEpP][-+]|[.])* character-constant ' c-char+ ' L' c-char+ ' c-char any member of the source character set except ', \, or newline escape-sequence escape-sequence simple-escape-sequence octal-escape-sequence hexadecimal-escape-sequence universal-character-name (unicode guff: \uxxxx or \Uxxxxxxxx) simple-escape-sequence \' \" \? \\ \a \b \f \n \r \t \v octal-escape-sequence \ [0-7]{1,3} hexadecimal-escape-sequence \x [0-9a-fA-F]+ string-literal " s-char* " L" s-char* " s-char any member of the source character set except ", \, or newline escape-sequence punctuator [ ] ( ) { } . -> ++ -- & * + - ~ ! / % << >> < > <= >= == != ^ | && || ? : ; ... = *= /= %= += -= <<= >>= &= ^= |= , # ## <: :> <% %> %: %:%: preprocessing-file group? group group-part+ group-part if-section control-line text-line # non-directive if-section if-group elif-groups? else-group? endif-line if-group # if constant-expr new-line group? # ifdef identifier new-line group? # ifndef identifier new-line group? elif-groups elif-group+ elif-group # elif constant-expr new-line group? else-group # else new-line group? endif-line # endif new-line control-line # include pp-tokens new-line # define identifier replacement-list new-line # define identifier lparen identifier-list? ) replacement-list new-line # define identifier lparen ... ) replacement-list new-line # define identifier lparen identifier-list , ... ) replacement-list new-line # undef identifier new-line # line pp-tokens new-line # error pp-tokens? new-line # pragma pp-tokens? new-line # new-line text-line pp-tokens? new-line non-directive pp-tokens new-line lparen a ( character not immediately preceded by white-space replacement-list pp-tokens? pp-tokens preprocessing-token+ new-line the new-line character [#2] A preprocessing directive consists of a sequence of preprocessing tokens that begins with a # preprocessing token that (at the start of translation phase 4) is either the first character in the source file (optionally after white space containing no new-line characters) or that follows white space containing at least one new-line character, and is ended by the next new-line character.139) A new-line character ends the preprocessing directive even if it occurs within what would otherwise be an invocation of a function-like macro. [#3] A text line shall not begin with a # preprocessing token. A non-directive shall not begin with any of the directive names appearing in the syntax. [#4] When in a group that is skipped (6.10.1), the directive syntax is relaxed to allow any sequence of preprocessing tokens to occur between the directive name and the following new-line character. [#5] The only white-space characters that shall appear between preprocessing tokens within a preprocessing directive (from just after the introducing # preprocessing token through just before the terminating new-line character) are space and horizontal-tab (including spaces that have replaced comments or possibly other white-space characters in translation phase 3). constant-expr conditional-expr conditional-expr logical-OR-expr logical-OR-expr ? expression : conditional-expr logical-OR-expr logical-AND-expr logical-OR-expr || logical-AND-expr logical-AND-expr inclusive-OR-expr logical-AND-expr && inclusive-OR-expr inclusive-OR-expr exclusive-OR-expr inclusive-OR-expr | exclusive-OR-expr exclusive-OR-expr AND-expr exclusive-OR-expr ^ AND-expr AND-expr equality-expr AND-expr & equality-expr equality-expr relational-expr equality-expr == relational-expr equality-expr != relational-expr relational-expr shift-expr relational-expr < shift-expr relational-expr > shift-expr relational-expr <= shift-expr relational-expr >= shift-expr shift-expr additive-expr shift-expr << additive-expr shift-expr >> additive-expr additive-expr multiplicative-expr additive-expr + multiplicative-expr additive-expr - multiplicative-expr multiplicative-expr cast-expr multiplicative-expr * cast-expr multiplicative-expr / cast-expr multiplicative-expr % cast-expr cast-expr unary-expr #### ( type-name ) cast-expr unary-expr postfix-expr #### ++ unary-expr #### -- unary-expr unary-operator cast-expr #### sizeof unary-expr #### sizeof ( type-name ) unary-operator & * + - ~ ! defined postfix-expr primary-expr %%%% postfix-expr [ expression ] #### postfix-expr ( argument-expr-list-opt ) %%%% postfix-expr . identifier %%%% postfix-expr -> identifier #### postfix-expr ++ #### postfix-expr -- %%%% ( type-name ) { initializer-list } %%%% ( type-name ) { initializer-list , } %%%%argument-expr-list %%%% assignment-expr %%%% argument-expr-list , assignment-expr assignment-expr conditional-expr #### unary-expr assignment-operator assignment-expr assignment-operator = *= /= %= += -= <<= >>= &= ^= |= primary-expr identifier constant %%%% string-literal ( expression ) expression assignment-expr #### expression , assignment-expr [#3] Constant expressions shall not contain assignment, increment, decrement, function-call, or comma operators, except when they are contained within a subexpression that is not evaluated. [#6] An integer constant expression96) shall have integer type and shall only have operands that are integer constants, enumeration constants, character constants, sizeof expressions whose results are integer constants, and floating constants that are the immediate operands of casts. Cast operators in an integer constant expression shall only convert arithmetic types to integer types, except as part of an operand to the sizeof operator. [#1] The expression that controls conditional inclusion shall be an integer constant expression except that: it shall not contain a cast; identifiers (including those lexically identical to keywords) are interpreted as described below;140) and it may contain unary operator expressions of the form defined identifier or defined ( identifier ) which evaluate to 1 if the identifier is currently defined as a macro name (that is, if it is predefined or if it has been the subject of a #define preprocessing directive without an intervening #undef directive with the same subject identifier), 0 if it is not. 140Because the controlling constant expression is evaluated during translation phase 4, all identifiers either are or are not macro names -- there simply are no keywords, enumeration constants, etc. */