/* * Conceptually, operation proceeds in 8 phases, though for us, like * many other implementations, some phases are no-ops and the * conceptual boundaries don't always match the practical boundaries. * * 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; our source and execution character sets * are the same. * * Phase 6: adjacent string literals are concatenated. * We actually do conversion of string literals from source text * form to contents at this point, because otherwise concatenating * them can be tricky; consider concatenating "\02" with "0" - you * can't just strip out the intermediate " " and concatenate the * source text forms. * * 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. */ #include #include #include #include #include #include #include #include #include "config.h" #include "mcc.h" #include "ebuf.h" #include "cclass.h" #include "ieeefp.h" #include "target.h" #include "verbose.h" #include "verbtypes.h" #include "stdio-util.h" extern const char *__progname; extern TARGET target_sparc64; 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; static VERBDEST *verbdests = 0; static unsigned long long int verbline = 0; static unsigned int verbmask; extern VERBTYPE verbtypes[]; static int Eflag = 0; static EFLAG_STATE Estate; static PPTOKEN *p6push; static const TARGET *target; static const char *outfile; static STRTCONC srcnames; static VFPRIV *allvfp = 0; #define Cisspace(x) isspace((unsigned char)(x)) #define Cisdigit(x) isdigit((unsigned char)(x)) void panic(void) { static int panicking = 0; if (! panicking) { panicking = 1; fflush(0); fflush(0); fflush(0); } (void)*(volatile char *)0; abort(); } static void verbprintf(unsigned int, const char *, ...) __attribute__((__format__(__printf__,2,3))); static void verbprintf(unsigned int level, const char *fmt, ...) { va_list ap; char *s; int l; VERBDEST *d; if (! (verbose & level)) return; va_start(ap,fmt); l = vasprintf(&s,fmt,ap); va_end(ap); verbmask = level; for (d=verbdests;d;d=d->link) { if (d->mask & level) { fwrite(s,1,l,d->f); fflush(d->f); } } verbmask = 0; free(s); } static int verb_f_w(void *pv, const char *data, int len) { VFPRIV *p; int o; void *nl; const char *part; int partlen; int suflen; VERBDEST *d; p = pv; verbmask = p->mask; o = 0; while (o < len) { nl = memchr(data+o,'\n',len-o); if (nl) { suflen = (((const char *)nl) - (data+o)) + 1; partlen = ebuf_len(&p->eb); if (partlen > 0) { part = ebuf_data(&p->eb); for (d=verbdests;d;d=d->link) { if (d->mask & p->mask) { fwrite(part,1,partlen,d->f); fwrite(data+o,1,suflen,d->f); } } ebuf_clear(&p->eb); } else { for (d=verbdests;d;d=d->link) { if (d->mask & p->mask) { fwrite(data+o,1,suflen,d->f); } } } o += suflen; } else { ebuf_appendn(&p->eb,data+o,len-o); o = len; } } for (d=verbdests;d;d=d->link) if (d->mask & p->mask) fflush(d->f); verbmask = 0; return(len); } static int verb_f_c(void *pv) { VFPRIV *p; int l; const char *s; VERBDEST *d; p = pv; l = ebuf_len(&p->eb); if (l > 0) { verbmask = p->mask; s = ebuf_data(&p->eb); for (d=verbdests;d;d=d->link) { if (d->mask & p->mask) { fwrite(s,1,l,d->f); putc('\n',d->f); fflush(d->f); } } verbmask = 0; } ebuf_done(&p->eb); if (p->flink) p->flink->blink = p->blink; if (p->blink) p->blink->flink = p->flink; else allvfp = p->flink; free(p); return(0); } static FILE *verb_f_(unsigned int mask, int line) #define verb_f(m) verb_f_((m),__LINE__) { VFPRIV *p; FILE *f; if (! (verbose & mask)) return(0); p = malloc(sizeof(VFPRIV)); if (p == 0) return(0); f = funopen(p,0,&verb_f_w,0,&verb_f_c); if (! f) { free(p); return(0); } p->mask = mask; ebuf_init(&p->eb); p->line = line; p->flink = allvfp; p->blink = 0; if (allvfp) allvfp->blink = p; allvfp = p; return(f); } static void check_vfps(void) { VFPRIV *p; for (p=allvfp;p;p=p->flink) { fprintf(stderr,"Leaked VFPRIV from line %d\n",p->line); } } #ifdef SFLOGGING 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 ++; VERB(SFREF,"add ref (%d) to %p (%s) from %s (%p)\n",to->nrefs,(void *)to,to->name,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 --; VERB(SFREF,"del ref (%d) to %p (%s) from %s (%p)\n",to->nrefs,(void *)to,to->name,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) { VERB(SFREF,"freeing %p (%s)\n",(void *)to,to->name); 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 #define sfref_copy(a,b) sfref_addref((a),sfref_follow((b))) static void error_cursf(const char *, ...) __attribute__((__format__(__printf__,1,2))); static void error_cursf(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 error_pt(PPTOKEN *, const char *, ...) __attribute__((__format__(__printf__,2,3))); static void error_pt(PPTOKEN *pt, const char *fmt, ...) { va_list ap; fprintf(stderr,"%s: \"%s\", line %d: ",__progname,sfref_follow(&pt->file)->name,pt->startline); va_start(ap,fmt); vfprintf(stderr,fmt,ap); va_end(ap); fprintf(stderr,"\n"); errs ++; } /* * The argument to -V is * * [-]KIND[,[-]KIND[,[-]KIND...]][=PLACE] * * Each distinct PLACE has its own separate list of what KINDs of * verbosity get sent there. The default PLACE is stdout, which can * be explicitly specified as a single dash. Arbitrary file * descriptors can be specified as -N, where N is the fd number, but * note that "-1" is distinct from "-". */ static int set_verbosity(const char *s) { unsigned int mset; unsigned int mclr; unsigned int m; int i; int l; int j; int dash; int x; int eqx; const void *p; const char *pstr; int plen; VERBDEST *d; VERBDEST **dp; if (verbtypes[0].namelen == 0) for (i=NVERB-1;i>=0;i--) verbtypes[i].namelen = strlen(verbtypes[i].name); l = strlen(s); // wish there were an rmemchr().... eqx = -1; while (1) { p = memchr(s+eqx+1,'=',l-(eqx+1)); if (! p) break; eqx = ((const char *)p) - s; } if (eqx < 0) eqx = l; mset = 0; mclr = 0; x = 0; while (1) { while ((x < eqx) && (Cisspace(s[x]) || (s[x] == ','))) x ++; if (x >= eqx) break; if (s[x] == '-') { dash = 1; x ++; } else { dash = 0; } for (i=x;(i=0;j--) { if ( (verbtypes[j].namelen == i-x) && !bcmp(verbtypes[j].name,s+x,i-x) ) break; } if (j >= 0) { m = verbtypes[j].bitmask; } else if ((i-x == 3) && !bcmp(s+x,"all",3)) { m = (1U << NVERB) - 1; } else { fprintf(stderr,"Unrecognized verbosity type `%.*s'\n",i-x,s+x); return(1); } if (dash) { mset &= ~m; mclr |= m; } else { mset |= m; mclr &= ~m; } x = i; } if (eqx < l) { plen = l - (eqx + 1); if (plen < 1) { fprintf(stderr,"Missing destination name in -V argument `%s'\n",s); exit(1); } pstr = s + eqx + 1; } else { pstr = "-"; plen = 1; } dp = &verbdests; while ((d = *dp)) { if ((plen == d->plen) && !bcmp(pstr,d->place,plen)) break; dp = &d->link; } if (! d) { d = malloc(sizeof(VERBDEST)); d->link = 0; d->plen = plen; d->place = malloc(plen+1); bcopy(pstr,d->place,plen); d->place[plen] = '\0'; // for setup_verbosity()'s convenience d->f = 0; d->mask = 0; *dp = d; } #ifndef SFLOGGING VERB(SFREF,"This build doesn't include SFLOGGING - sfref verbosity unavailable\n"); #endif d->mask = (d->mask | mset) & ~mclr; if (! d->mask) { free(d->place); *dp = d->link; free(d); } verbose = 0; for (d=verbdests;d;d=d->link) verbose |= d->mask; return(0); } /* * #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(PUNCTTYPE t) { switch (t) { case PUNCT_LBRACK: return("["); break; case PUNCT_RBRACK: return("]"); break; case PUNCT_LPAREN: return("("); break; case PUNCT_RPAREN: return(")"); break; case PUNCT_LBRACE: return("{"/*}*/); break; case PUNCT_RBRACE: return(/*{*/"}"); break; case PUNCT_DOT: return("."); break; case PUNCT_ARROW: return("->"); break; case PUNCT_PLUSPLUS: return("++"); break; case PUNCT_MINUSMINUS: return("--"); break; case PUNCT_AND: return("&"); break; case PUNCT_STAR: return("*"); break; case PUNCT_PLUS: return("+"); break; case PUNCT_MINUS: return("-"); break; case PUNCT_TILDE: return("~"); break; case PUNCT_BANG: return("!"); break; case PUNCT_SLASH: return("/"); break; case PUNCT_PERCENT: return("%"); break; case PUNCT_LSH: return("<<"); break; case PUNCT_RSH: return(">>"); break; case PUNCT_LT: return("<"); break; case PUNCT_GT: return(">"); break; case PUNCT_LEQ: return("<="); break; case PUNCT_GEQ: return(">="); break; case PUNCT_EQ: return("=="); break; case PUNCT_NEQ: return("!="); break; case PUNCT_HAT: return("^"); break; case PUNCT_OR: return("|"); break; case PUNCT_ANDAND: return("&&"); break; case PUNCT_OROR: return("||"); break; case PUNCT_QUESTION: return("?"); break; case PUNCT_COLON: return(":"); break; case PUNCT_SEMI: return(";"); break; case PUNCT_ELLIPSIS: return("..."); break; case PUNCT_ASSIGN: return("="); break; case PUNCT_MULASGN: return("*="); break; case PUNCT_DIVASGN: return("/="); break; case PUNCT_MODASGN: return("%="); break; case PUNCT_ADDASGN: return("+="); break; case PUNCT_SUBASGN: return("-="); break; case PUNCT_LSHASGN: return("<<="); break; case PUNCT_RSHASGN: return(">>="); break; case PUNCT_ANDASGN: return("&="); break; case PUNCT_XORASGN: return("^="); break; case PUNCT_ORASGN: return("|="); break; case PUNCT_COMMA: return(","); break; case PUNCT_SHARP: return("#"); break; case PUNCT_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; Estate.to = 0; } 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 strtconc_init(STRTCONC *tc) { tc->head = 0; tc->tail = &tc->head; } static void strtconc_append(STRTCONC *tc, const char *s) { STRLIST *l; l = malloc(sizeof(STRLIST)); l->s = s; *tc->tail = l; tc->tail = &l->link; } static void strtconc_term(STRTCONC *tc) { *tc->tail = 0; } static void handleargs(int ac, char **av) { int skip; int errs; skip = 0; errs = 0; strtconc_init(&srcnames); for (ac--,av++;ac;ac--,av++) { if (skip > 0) { skip --; continue; } if (**av != '-') { strtconc_append(&srcnames,*av); 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; } if (!strcmp(*av,"-o")) { WANTARG(); outfile = av[skip]; continue; } #undef WANTARG fprintf(stderr,"%s: unrecognized option `%s'\n",__progname,*av); errs ++; } strtconc_term(&srcnames); if (errs) exit(1); } static void verb_text(VERBDEST *d, const char *txt, int len) { int i; const char *pref; if (d->atnl) { fprintf(d->g,"%llu [",verbline++); pref = ""; for (i=0;ig,"%s%s",pref,verbtypes[i].name); pref = "+"; } } fprintf(d->g,"] "); } fwrite(txt,1,len,d->g); } static int verb_w(void *dv, const char *data, int len) { VERBDEST *d; int o; void *nl; int pl; d = dv; o = 0; while (o < len) { nl = memchr(data+o,'\n',len-o); if (nl) { pl = ((const char *)nl) - (data+o) + 1; verb_text(d,data+o,pl); o += pl; d->atnl = 1; } else { verb_text(d,data+o,len-o); o = len; d->atnl = 0; } } fflush(d->g); return(len); } static void setup_verbosity(void) { VERBDEST *d; int fd; for (d=verbdests;d;d=d->link) { if ((d->plen >= 1) && (d->place[0] == '-')) { if (d->plen == 1) { fd = dup(1); if (fd < 0) { fprintf(stderr,"%s: can't dup stdout: %s\n",__progname,strerror(errno)); exit(1); } } else { long int v; char *ep; v = strtol(d->place+1,&ep,0); if (ep == d->place+1) { fprintf(stderr,"%s: missing number after - in verbosity destination `%s'\n",__progname,d->place); exit(1); } if (*ep) { fprintf(stderr,"%s: junk after number in verbosity destination `%s'\n",__progname,d->place); exit(1); } fd = v; if ((fd != v) || (fd < 0)) { fprintf(stderr,"%s: invalid fd %d in verbosity destination `%s'\n",__progname,fd,d->place); exit(1); } if (fcntl(fd,F_GETFL,0) == -1) { fprintf(stderr,"%s: bad fd %d in verbosity destination `%s': %s\n",__progname,fd,d->place,strerror(errno)); exit(1); } } } else { fd = open(d->place,O_WRONLY|O_CREAT|O_TRUNC|O_APPEND,0666); if (fd < 0) { fprintf(stderr,"%s: can't open verbosity destination `%s': %s\n",__progname,d->place,strerror(errno)); exit(1); } } d->g = fdopen(fd,"w"); if (! d->g) { fprintf(stderr,"%s: can't fdopen fd %d for verbosity destination `%s': %s\n",__progname,fd,d->place,strerror(errno)); exit(1); } d->f = funopen(d,0,&verb_w,0,0); setlinebuf(d->f); d->atnl = 1; } } static void convert_sourcefiles(void) { STRLIST *l; SRCFILE *sf; SRCFILE *last; char *s; if (! srcnames.head) { fprintf(stderr,"%s: no source files\n",__progname); exit(1); } last = 0; sfref_init(&srcfiles,"srcfiles root"); while ((l = srcnames.head)) { srcnames.head = l->link; s = strdup(l->s); sf = new_srcfile("command-line file %s",s); sf->name = s; if (last) sfref_addref(&last->link,sf); else sfref_addref(&srcfiles,sf); free(l); last = sf; } } static void open_predefines(void) { SRCFILE *sf; SRCFILE *t; 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; t = sfref_follow(&srcfiles); sfref_addref(&sf->link,t); sfref_delref(&srcfiles,t); 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 PUNCT_LBRACK: fprintf(f,"["); break; case PUNCT_RBRACK: fprintf(f,"]"); break; case PUNCT_LPAREN: fprintf(f,"("); break; case PUNCT_RPAREN: fprintf(f,")"); break; case PUNCT_LBRACE: fprintf(f,"{"/*}*/); break; case PUNCT_RBRACE: fprintf(f,/*{*/"}"); break; case PUNCT_DOT: fprintf(f,"."); break; case PUNCT_ARROW: fprintf(f,"->"); break; case PUNCT_PLUSPLUS: fprintf(f,"++"); break; case PUNCT_MINUSMINUS: fprintf(f,"--"); break; case PUNCT_AND: fprintf(f,"&"); break; case PUNCT_STAR: fprintf(f,"*"); break; case PUNCT_PLUS: fprintf(f,"+"); break; case PUNCT_MINUS: fprintf(f,"-"); break; case PUNCT_TILDE: fprintf(f,"~"); break; case PUNCT_BANG: fprintf(f,"!"); break; case PUNCT_SLASH: fprintf(f,"/"); break; case PUNCT_PERCENT: fprintf(f,"%%"); break; case PUNCT_LSH: fprintf(f,"<<"); break; case PUNCT_RSH: fprintf(f,">>"); break; case PUNCT_LT: fprintf(f,"<"); break; case PUNCT_GT: fprintf(f,">"); break; case PUNCT_LEQ: fprintf(f,"<="); break; case PUNCT_GEQ: fprintf(f,">="); break; case PUNCT_EQ: fprintf(f,"=="); break; case PUNCT_NEQ: fprintf(f,"!="); break; case PUNCT_HAT: fprintf(f,"^"); break; case PUNCT_OR: fprintf(f,"|"); break; case PUNCT_ANDAND: fprintf(f,"&&"); break; case PUNCT_OROR: fprintf(f,"||"); break; case PUNCT_QUESTION: fprintf(f,"?"); break; case PUNCT_COLON: fprintf(f,":"); break; case PUNCT_SEMI: fprintf(f,";"); break; case PUNCT_ELLIPSIS: fprintf(f,"..."); break; case PUNCT_ASSIGN: fprintf(f,"="); break; case PUNCT_MULASGN: fprintf(f,"*="); break; case PUNCT_DIVASGN: fprintf(f,"/="); break; case PUNCT_MODASGN: fprintf(f,"%%="); break; case PUNCT_ADDASGN: fprintf(f,"+="); break; case PUNCT_SUBASGN: fprintf(f,"-="); break; case PUNCT_LSHASGN: fprintf(f,"<<="); break; case PUNCT_RSHASGN: fprintf(f,">>="); break; case PUNCT_ANDASGN: fprintf(f,"&="); break; case PUNCT_XORASGN: fprintf(f,"^="); break; case PUNCT_ORASGN: fprintf(f,"|="); break; case PUNCT_COMMA: fprintf(f,","); break; case PUNCT_SHARP: fprintf(f,"#"); break; case PUNCT_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 PUNCT_LBRACK: fprintf(f,"["); break; case PUNCT_RBRACK: fprintf(f,"]"); break; case PUNCT_LPAREN: fprintf(f,"("); break; case PUNCT_RPAREN: fprintf(f,")"); break; case PUNCT_LBRACE: fprintf(f,"{"/*}*/); break; case PUNCT_RBRACE: fprintf(f,/*{*/"}"); break; case PUNCT_DOT: fprintf(f,"."); break; case PUNCT_ARROW: fprintf(f,"->"); break; case PUNCT_PLUSPLUS: fprintf(f,"++"); break; case PUNCT_MINUSMINUS: fprintf(f,"--"); break; case PUNCT_AND: fprintf(f,"&"); break; case PUNCT_STAR: fprintf(f,"*"); break; case PUNCT_PLUS: fprintf(f,"+"); break; case PUNCT_MINUS: fprintf(f,"-"); break; case PUNCT_TILDE: fprintf(f,"~"); break; case PUNCT_BANG: fprintf(f,"!"); break; case PUNCT_SLASH: fprintf(f,"/"); break; case PUNCT_PERCENT: fprintf(f,"%%"); break; case PUNCT_LSH: fprintf(f,"<<"); break; case PUNCT_RSH: fprintf(f,">>"); break; case PUNCT_LT: fprintf(f,"<"); break; case PUNCT_GT: fprintf(f,">"); break; case PUNCT_LEQ: fprintf(f,"<="); break; case PUNCT_GEQ: fprintf(f,">="); break; case PUNCT_EQ: fprintf(f,"=="); break; case PUNCT_NEQ: fprintf(f,"!="); break; case PUNCT_HAT: fprintf(f,"^"); break; case PUNCT_OR: fprintf(f,"|"); break; case PUNCT_ANDAND: fprintf(f,"&&"); break; case PUNCT_OROR: fprintf(f,"||"); break; case PUNCT_QUESTION: fprintf(f,"?"); break; case PUNCT_COLON: fprintf(f,":"); break; case PUNCT_SEMI: fprintf(f,";"); break; case PUNCT_ELLIPSIS: fprintf(f,"..."); break; case PUNCT_ASSIGN: fprintf(f,"="); break; case PUNCT_MULASGN: fprintf(f,"*="); break; case PUNCT_DIVASGN: fprintf(f,"/="); break; case PUNCT_MODASGN: fprintf(f,"%%="); break; case PUNCT_ADDASGN: fprintf(f,"+="); break; case PUNCT_SUBASGN: fprintf(f,"-="); break; case PUNCT_LSHASGN: fprintf(f,"<<="); break; case PUNCT_RSHASGN: fprintf(f,">>="); break; case PUNCT_ANDASGN: fprintf(f,"&="); break; case PUNCT_XORASGN: fprintf(f,"^="); break; case PUNCT_ORASGN: fprintf(f,"|="); break; case PUNCT_COMMA: fprintf(f,","); break; case PUNCT_SHARP: fprintf(f,"#"); break; case PUNCT_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_copy(&n->file,&t->file); n->startline = t->startline; 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_copy(&t->file,&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(PUNCTTYPE 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_cursf("%.*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; FILE *f; 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); f = verb_f(VERB_MACROEXPANDER); if (f) { fprintf(f,"macroexpander_get EXIT %p ",(void *)t); pptoken_dump(f,t); fprintf(f,"\n"); fclose(f); } return(t); } static void macroexpander_addarg(MACROEXPANDER *mx, PPTOKEN *t) { PPTLIST *l; FILE *f; f = verb_f(VERB_MACROEXPANDER); if (f) { fprintf(f,"macroexpander_addarg ENTRY %p %p ",(void *)mx,(void *)t); pptoken_dump(f,t); fprintf(f,"\n"); fclose(f); } 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; FILE *f; f = verb_f(VERB_MACROEXPANDER); if (f) { fprintf(f,"macroexpander_append_done ENTRY %p %p ",(void *)mx,(void *)t); pptoken_dump(f,t); fprintf(f,"\n"); fclose(f); } 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; FILE *f; f = verb_f(VERB_MACROEXPANDER); if (f) { fprintf(f,"macroexpander_pending_push ENTRY %p %p ",(void *)mx,(void *)t); pptoken_dump(f,t); fprintf(f,"\n"); fclose(f); } 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; FILE *f; 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); f = verb_f(VERB_MACROEXPANDER); if (f) { fprintf(f,"macroexpander_pending_pop EXIT %p ",(void *)t); pptoken_dump(f,t); fprintf(f,"\n"); fclose(f); } 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; FILE *f; VERB(MACROEXPANDER,"macroexpander_flush ENTRY %p\n",(void *)mx); t = macroexpander_get(mx); if (t) { f = verb_f(VERB_MACROEXPANDER); if (f) { fprintf(f,"macroexpander_flush EXIT %p ",(void *)t); pptoken_dump(f,t); fprintf(f,"\n"); fclose(f); } return(t); } if (mx->curmac) { error_cursf("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; f = verb_f(VERB_CONCATTER); if (f) { fprintf(f,"concatter_append ENTRY %p %p/%p ",(void *)c,(void *)el,(void *)el->token); pptoken_dump(f,el->token); fprintf(f,"\n"); fclose(f); } 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 == PUNCT_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; FILE *f; VERB(MACROEXPANDER,"macroexpander_expand_args ENTRY %p\n",(void *)mx); m = mx->curmac; nest = 0; nargs = 1; nonws = 0; f = verb_f(VERB_MACROEXPANDER); if (f) { fprintf(f,"macroexpander_expand_args arglist =\n"); for (a=pptconc_head(&mx->arglist);a;a=a->link) { fprintf(f,"\t%p/%p ",(void *)a,(void *)a->token); pptoken_dump(f,a->token); fprintf(f,"\n"); } fclose(f); } 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 PUNCT_LPAREN: nest ++; break; case PUNCT_RPAREN: nest --; if (nest < 0) panic(); break; case PUNCT_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_cursf("too few arguments to macro %.*s",m->name.len,m->name.data); break <"done">; } margs = m->nargs + 1; } else { if (nargs < m->nargs) { error_cursf("too few arguments to macro %.*s",m->name.len,m->name.data); break <"done">; } if (nargs > m->nargs) { error_cursf("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; f = verb_f(VERB_MACROEXPANDER); if (f) { fprintf(f,"macroexpander_expand_args: taking arg %p/%p (",(void *)a,(void *)a->token); pptoken_dump(f,a->token); fprintf(f,"\n"); fclose(f); } 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 PUNCT_LPAREN: nest ++; VERB(MACROEXPANDER,"macroexpander_expand_args: ( nest now %d\n",nest); break; case PUNCT_RPAREN: nest --; VERB(MACROEXPANDER,"macroexpander_expand_args: ) nest now %d\n",nest); if (nest < 0) panic(); break; case PUNCT_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; } } f = verb_f(VERB_MACROEXPANDER); if (f) { fprintf(f,"macroexpander_expand_args: saving argument token %p/%p ",(void *)a,(void *)a->token); pptoken_dump(f,a->token); fprintf(f,"\n"); fclose(f); } *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)); } f = verb_f(VERB_MACROEXPANDER); if (f) { for (i=0;ilink) { fprintf(f,"\t%p/%p ",(void *)x,(void *)x->token); pptoken_dump(f,x->token); fprintf(f,"\n"); } } fclose(f); } concatter_init(&expansion); if (mx->curmac->flags & MF_DISABLED) panic(); mx->curmac->flags |= MF_DISABLED; f = verb_f(VERB_MACROEXPANDER); if (f) { fprintf(f,"macroexpander_expand_args replacement =\n"); for (x=mx->curmac->replacement;x;x=x->link) { fprintf(f,"\t%p/%p ",(void *)x,(void *)x->token); pptoken_dump(f,x->token); fprintf(f,"\n"); } fclose(f); } x = mx->curmac->replacement; while (x) { f = verb_f(VERB_MACROEXPANDER); if (f) { fprintf(f,"macroexpander_expand_args replacement el %p token %p ",(void *)x,(void *)x->token); pptoken_dump(f,x->token); fprintf(f,"\n"); fclose(f); } if ((x->token->type == PPT_PUNCT) && (x->token->detail.punct == PUNCT_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])); f = verb_f(VERB_MACROEXPANDER); if (f) { fprintf(f,"macroexpander_expand_args stringized result is el %p token %p ",(void *)x2,(void *)x2->token); pptoken_dump(f,x2->token); fprintf(f,"\n"); fclose(f); } 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; FILE *f; f = verb_f(VERB_MACROEXPANDER); if (f) { fprintf(f,"macroexpander_input ENTRY %p %p ",(void *)mx,(void *)t); pptoken_dump(f,t); fprintf(f,"\n"); fclose(f); } 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); f = verb_f(VERB_MACROEXPANDER); if (f) { fprintf(f,"macroexpander_input taking pending token %p ",(void *)t); pptoken_dump(f,t); fprintf(f,"\n"); fclose(f); } 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 == PUNCT_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_cursf("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 PUNCT_LPAREN: mx->argnest ++; break; case PUNCT_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; SRCFILE *tsf; SFREF tref; 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_cursf("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_copy(&i->sf,&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_delref(&cursf,oldcursf); sfref_addref(&cursf,sf); sfref_init(&tref,"#include temp ref"); tsf = sfref_follow(&srcfiles); sfref_addref(&sf->link,tsf); sfref_delref(&srcfiles,tsf); sfref_addref(&srcfiles,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_cursf("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_cursf("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_cursf("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_cursf("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_cursf("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(PUNCT_ELLIPSIS)); p3iunget(p,c2); } p3iunget(p,c); return(pptoken_punct(PUNCT_DOT)); break; case '!': { PUNCTTYPE witheq; PUNCTTYPE noeq; witheq = PUNCT_NEQ; noeq = PUNCT_BANG; if (0) { case '%': witheq = PUNCT_MODASGN; noeq = PUNCT_PERCENT; } if (0) { case '*': witheq = PUNCT_MULASGN; noeq = PUNCT_STAR; } if (0) { case '=': witheq = PUNCT_EQ; noeq = PUNCT_ASSIGN; } if (0) { case '^': witheq = PUNCT_XORASGN; noeq = PUNCT_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(PUNCT_DIVASGN)); break; default: p3iunget(p,c); return(pptoken_punct(PUNCT_SLASH)); break; } break; case '&': { PUNCTTYPE witheq; PUNCTTYPE doubled; PUNCTTYPE bare; witheq = PUNCT_ANDASGN; doubled = PUNCT_ANDAND; bare = PUNCT_AND; if (0) { case '+': witheq = PUNCT_ADDASGN; doubled = PUNCT_PLUSPLUS; bare = PUNCT_PLUS; } if (0) { case '|': witheq = PUNCT_ORASGN; doubled = PUNCT_OROR; bare = PUNCT_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(PUNCT_ARROW)); break; case '-': return(pptoken_punct(PUNCT_MINUSMINUS)); break; case '=': return(pptoken_punct(PUNCT_SUBASGN)); break; default: p3iunget(p,c); return(pptoken_punct(PUNCT_MINUS)); break; } break; case '<': { PUNCTTYPE witheq; PUNCTTYPE doubled; PUNCTTYPE doubledeq; PUNCTTYPE bare; witheq = PUNCT_LEQ; doubled = PUNCT_LSH; doubledeq = PUNCT_LSHASGN; bare = PUNCT_LT; if (0) { case '>': witheq = PUNCT_GEQ; doubled = PUNCT_RSH; doubledeq = PUNCT_RSHASGN; bare = PUNCT_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(PUNCT_SSHARP)); p3iunget(p,c); return(pptoken_punct(PUNCT_SHARP)); break; case '[': return(pptoken_punct(PUNCT_LBRACK)); break; case ']': return(pptoken_punct(PUNCT_RBRACK)); break; case '(': return(pptoken_punct(PUNCT_LPAREN)); break; case ')': return(pptoken_punct(PUNCT_RPAREN)); break; case '{':/*}*/ return(pptoken_punct(PUNCT_LBRACE)); break; case /*{*/'}': return(pptoken_punct(PUNCT_RBRACE)); break; case '~': return(pptoken_punct(PUNCT_TILDE)); break; case '?': return(pptoken_punct(PUNCT_QUESTION)); break; case ':': return(pptoken_punct(PUNCT_COLON)); break; case ';': return(pptoken_punct(PUNCT_SEMI)); break; case ',': return(pptoken_punct(PUNCT_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; FILE *f; if (p->push3) { t = p->push3; p->push3 = 0; how = "pushed"; } else { t = phase3_get_(p,kind); how = "new"; } f = verb_f(VERB_PHASE3); if (f) { fprintf(f,"phase3_get(%s) returning %s ",p3gkind_str(kind),how); pptoken_dump(f,t); fprintf(f,"\n"); fclose(f); } 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_cursf("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 == PUNCT_LPAREN)) { expreval_consume(e); v = expreval_expression(e); if (! (e->list && (e->list->token->type == PPT_PUNCT) && (e->list->token->detail.punct == PUNCT_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 == PUNCT_PLUS)) { expreval_consume(e); return(expreval_unary_expr(e)); } else if (e->list && (e->list->token->type == PPT_PUNCT) && (e->list->token->detail.punct == PUNCT_MINUS)) { expreval_consume(e); return(-expreval_unary_expr(e)); } else if (e->list && (e->list->token->type == PPT_PUNCT) && (e->list->token->detail.punct == PUNCT_TILDE)) { expreval_consume(e); return(~expreval_unary_expr(e)); } else if (e->list && (e->list->token->type == PPT_PUNCT) && (e->list->token->detail.punct == PUNCT_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 == PUNCT_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 == PUNCT_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 == PUNCT_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 == PUNCT_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 == PUNCT_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 == PUNCT_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 == PUNCT_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 == PUNCT_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 == PUNCT_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 == PUNCT_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 == PUNCT_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 == PUNCT_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 == PUNCT_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 == PUNCT_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 == PUNCT_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 == PUNCT_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 == PUNCT_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 == PUNCT_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 == PUNCT_QUESTION)) { expreval_consume(e); mhs = expreval_expression(e); if (e->list && (e->list->token->type == PPT_PUNCT) && (e->list->token->detail.punct == PUNCT_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; FILE *f; f = verb_f(VERB_EVAL); if (f) { fprintf(f,"expreval_eval: token list\n"); for (l=e->list;l;l=l->link) { fprintf(f,"\t%p/%p ",(void *)l,(void *)l->token); pptoken_dump(f,l->token); fprintf(f,"\n"); } fclose(f); } v = expreval_constant_expr(e); if (e->list) error_cursf("junk after expression"); return(v); } static PPVAL eval_pp_expr(PPTLIST *list) { __label__ failto; PPTCONC explist; MACROEXPANDER mx; PPTOKEN *t; PPVAL val; EXPREVAL ee; FILE *f; 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; } f = verb_f(VERB_EVAL); if (f) { PPTLIST *l; fprintf(f,"eval_pp_expr ENTRY, list\n"); for (l=list;l;l=l->link) { fprintf(f,"\t%p/%p ",(void *)l,(void *)l->token); pptoken_dump(f,l->token); fprintf(f,"\n"); } fclose(f); } 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 == PUNCT_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); } } f = verb_f(VERB_EVAL); if (f) { fprintf(f,"eval_pp_expr defined arg is "); pptoken_dump(f,t); fprintf(f,"\n"); fclose(f); } 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); f = verb_f(VERB_EVAL); if (f) { PPTLIST *l; fprintf(f,"eval_pp_expr after expanding\n"); for (l=list;l;l=l->link) { fprintf(f,"\t%p/%p ",(void *)l,(void *)l->token); pptoken_dump(f,l->token); fprintf(f,"\n"); } fclose(f); } 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; FILE *f; f = verb_f(VERB_IF); if (f) { fprintf(f,"ifstack now:"); for (s=ifstack;s;s=s->link) fprintf(f," %s",ifstate_string(s->state)); fprintf(f,"\n"); fclose(f); } } 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_copy(&s->start_sf,&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_cursf("EOF reading macro argument formals"); return(t); } if ((t->type == PPT_PUNCT) && (t->detail.punct == PUNCT_RPAREN)) { pptoken_free(t); return(0); } if ((t->type == PPT_PUNCT) && (t->detail.punct == PUNCT_ELLIPSIS)) { t2 = phase3_get_skipws(&p2p3); if (t2->type == PPT_EOF) { error_cursf("EOF reading macro argument formals"); pptoken_free(t); return(t2); } if ((t2->type == PPT_PUNCT) && (t2->detail.punct == PUNCT_RPAREN)) { (*arg)(t); pptoken_free(t2); return(0); } error_cursf("... 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 == PUNCT_COMMA)) { error_cursf("missing macro formal argument"); phase3_push(&p2p3,t); return(phase4_flush_line()); } if (t->type != PPT_IDENT) { error_cursf("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_cursf("EOF reading macro argument formals"); return(t); } if ((t->type == PPT_PUNCT) && (t->detail.punct == PUNCT_RPAREN)) { pptoken_free(t); return(0); } if (t->type == PPT_EOF) { error_cursf("EOF reading macro argument formals"); return(t); } if (! ((t->type == PPT_PUNCT) && (t->detail.punct == PUNCT_COMMA))) { error_cursf("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 == PUNCT_ELLIPSIS)) { t2 = phase3_get_skipws(&p2p3); if (t2->type == PPT_EOF) { error_cursf("EOF reading macro argument formals"); pptoken_free(t); return(t2); } if ((t2->type == PPT_PUNCT) && (t2->detail.punct == PUNCT_RPAREN)) { (*arg)(t); pptoken_free(t2); return(0); } error_cursf("... in macro formal list isn't followed by )"); pptoken_free(t); phase3_push(&p2p3,t2); return(phase4_flush_line()); } error_cursf("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; FILE *f; int add_formal(PPTOKEN *t) { int i; if ((t->type == PPT_PUNCT) && (t->detail.punct == PUNCT_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_cursf("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); f = verb_f(VERB_DEFINE); if (f) { fprintf(f,"phase4_do_define name is %p ",(void *)name); pptoken_dump(f,name); fprintf(f,"\n"); fclose(f); } if (name->type != PPT_IDENT) { error_cursf("#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 == PUNCT_LPAREN)) { VERB(DEFINE,"phase4_do_define: function-like\n"); pptoken_free(next); t = phase4_macro_arglist(&add_formal); if (t) { macro_free(m); f = verb_f(VERB_DEFINE); if (f) { fprintf(f,"phase4_do_define: arglist parse failed, returning %p ",(void *)t); pptoken_dump(f,t); fprintf(f,"\n"); fclose(f); } return(t); } f = verb_f(VERB_DEFINE); if (f) { fprintf(f,"phase4_do_define: arg count %d:\n",m->nargs); for (i=0;inargs;i++) { fprintf(f,"\t%p ",(void *)m->args[i]); pptoken_dump(f,m->args[i]); fprintf(f,"\n"); } fclose(f); } 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_cursf("#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); f = verb_f(VERB_DEFINE); if (f) { fprintf(f,"phase4_do_define: initial replacement:\n"); for (l=m->replacement;l;l=l->link) { fprintf(f,"\t%p/%p ",(void *)l,(void *)l->token); pptoken_dump(f,l->token); fprintf(f,"\n"); } fclose(f); } switch <"done"> (t->type) { case PPT_EOF: error_cursf("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_cursf("__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 == PUNCT_SSHARP)) { error_cursf("#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 == PUNCT_SSHARP)) { error_cursf("#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 == PUNCT_SHARP)) { do l = l->link; while (l && (l->token->type == PPT_WHITESPACE)); if (!l || (l->token->type != PPT_MACROARG)) { error_cursf("#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 == PUNCT_SSHARP)) { pnt->detail.macroarg.flags |= MAF_NOEXPAND; } if ((l->token->type == PPT_MACROARG) && (pnt->type == PPT_PUNCT) && (pnt->detail.punct == PUNCT_SSHARP)) { l->token->detail.macroarg.flags |= MAF_NOEXPAND; } } pnt = l->token; } } f = verb_f(VERB_DEFINE); if (f) { fprintf(f,"phase4_do_define: massaged replacement:\n"); for (l=m->replacement;l;l=l->link) { fprintf(f,"\t%p/%p ",(void *)l,(void *)l->token); pptoken_dump(f,l->token); fprintf(f,"\n"); } fclose(f); } 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_cursf("EOF partway through #%s line",what); phase3_push(&p2p3,name); return(0); break; case PPT_NEWLINE: error_cursf("#%s line with no name",what); phase3_push(&p2p3,name); return(0); break; default: error_cursf("#%s with invalid name",what); phase3_push(&p2p3,name); return(0); break; } t = phase3_get_skipws(&p2p3); if (t->type == PPT_EOF) { error_cursf("EOF partway through #%s line",what); pptoken_free(name); phase3_push(&p2p3,t); return(0); } if (t->type != PPT_NEWLINE) { error_cursf("#%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_cursf("EOF partway through #%s line",what); phase3_push(&p2p3,t); return(0); } if (t->type != PPT_NEWLINE) { error_cursf("garbage on #%s line",what); pptoken_free(t); return(0); } phase3_push(&p2p3,t); return(1); } static PPTOKEN *phase4_do_undef(void) { PPTOKEN *name; FILE *f; VERB(DEFINE,"phase4_do_undef ENTRY\n"); name = phase4_one_name_line("undef"); f = verb_f(VERB_DEFINE); if (f) { fprintf(f,"phase4_do_undef name "); pptoken_dump(f,name); fprintf(f,"\n"); fclose(f); } if (name) { macro_undef(&name->str); pptoken_free(name); } return(phase4_flush_line()); } static PPTOKEN *phase4_do_ifdef(void) { PPTOKEN *name; FILE *f; 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"); f = verb_f(VERB_IF); if (f) { fprintf(f,"phase4_do_ifdef name "); pptoken_dump(f,name); fprintf(f,"\n"); fclose(f); } 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; FILE *f; 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"); f = verb_f(VERB_IF); if (f) { fprintf(f,"phase4_do_ifndef name "); pptoken_dump(f,name); fprintf(f,"\n"); fclose(f); } 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_cursf("#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_cursf("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_cursf("#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_cursf("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_cursf("#elif with no matching conditional"); return(phase4_flush_line()); } t = phase4_rest_of_line(&expr); switch (t->type) { case PPT_EOF: error_cursf("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_cursf("#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_cursf("EOF inside a #include line"); break; case PPT_NEWLINE: include_file(hn); break; default: error_cursf("#include with garbage after filename"); t = phase4_flush_line(); break; } pptoken_free(hn); } else { error_cursf("macro-expanded #include not yet supported"); t = phase4_flush_line(); } return(t); } static PPTOKEN *phase4_do_line(void) { VERB(PHASE4,"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; FILE *f; while (1) { t = phase3_get(p,P3GK_NORMAL); if ((t->type == PPT_NEWLINE) || (t->type == PPT_EOF)) return(t); f = verb_f(VERB_PRAGMA); if (f) { fprintf(f,"\t"); pptoken_dump(f,t); fprintf(f,"\n"); fclose(f); } 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); } VERB(PRAGMA,"#pragma:\n"); return(pragma_body(p)); } static PPTOKEN *phase4_do_eval(void) { PPTLIST *expr; PPTOKEN *t; char *s; int l; VERB(PHASE4,"phase4_do_eval\n"); t = phase4_rest_of_line(&expr); switch (t->type) { case PPT_EOF: error_cursf("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 == PUNCT_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_cursf("unrecognizable preprocessor #%.*s line",t->str.len,t->str.data); pptoken_free(t); return(phase4_flush_line()); } } } else { error_cursf("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_copy(&savesf,&cursf); sfref_drop(&cursf); sfref_addref(&cursf,sf); VERB(PRAGMA,"_Pragma:\n"); pptoken_free(pragma_body(&p)); fclose(sf->f); sfref_drop(&cursf); sfref_copy(&cursf,&savesf); sfref_done(&savesf); phase2_done(&p); phase3_done(&p); free(str); } static PPTOKEN *phase4_get(void) { PPTOKEN *t; PPTOKEN *prag[3]; int np; FILE *f; 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 == PUNCT_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 == PUNCT_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]; } f = verb_f(VERB_PHASE4); if (f) { fprintf(f,"phase4_get: got %p ",(void *)t); pptoken_dump(f,t); fprintf(f,"\n"); fclose(f); } return(t); } } static void istack_free(ISTACK *s) { sfref_done(&s->sf); free(s); } static void pop_istack(void) { ISTACK *s; SRCFILE *c; s = istack; istack = s->link; c = sfref_follow(&cursf); fclose(c->f); sfref_drop(&cursf); sfref_copy(&cursf,&s->sf); 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 (s->to == 0) { if (outfile) { s->to = fopen(outfile,"w"); if (! s->to) { fprintf(stderr,"%s: can't open -o file %s: %s\n",__progname,outfile,strerror(errno)); exit(1); } } else { s->to = stdout; } } 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) fprintf(s->to,"\n"); fprintf(s->to,"# %d %s\n",t->startline,sfref_follow(&t->file)->name); sfref_drop(&s->file); sfref_copy(&s->file,&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) { putc(' ',s->to); s->atws = 1; s->atnl = 0; } pptoken_free(t); return; } while (t->startline > s->line) { putc('\n',s->to); s->line ++; s->atnl = 1; s->atws = 1; } wasatws = s->atws; s->atws = 0; switch (t->type) { default: panic(); break; case PPT_IDENT: if (! wasatws) { switch (s->lasttt) { case PPT_IDENT: case PPT_NUMBER: case PPT_SYNTH: putc(' ',s->to); break; default: break; } } fwrite(t->str.data,1,t->str.len,s->to); s->atnl = 0; break; case PPT_NUMBER: if (! wasatws) { switch (s->lasttt) { case PPT_IDENT: case PPT_NUMBER: case PPT_SYNTH: case PPT_PUNCT: // don't attach . to a number putc(' ',s->to); break; default: break; } } fwrite(t->str.data,1,t->str.len,s->to); s->atnl = 0; break; case PPT_CHARCONST: case PPT_STRING: case PPT_SYNTH: fwrite(t->str.data,1,t->str.len,s->to); s->atnl = 0; break; case PPT_PUNCT: if (! wasatws) { switch (s->lasttt) { case PPT_PUNCT: case PPT_NUMBER: // don't attach . to a number putc(' ',s->to); break; default: break; } } fprintf(s->to,"%s",pp_punct_str(t->detail.punct)); s->atnl = 0; break; case PPT_OTHER: if (! wasatws) putc(' ',s->to); fwrite(t->str.data,1,t->str.len,s->to); s->atnl = 0; break; } s->lasttt = t->type; pptoken_free(t); } static void Eflag_done(EFLAG_STATE *s) { if (! s->atnl) putc('\n',s->to); sfref_drop(&s->file); s->line = -1; s->lasttt = PPT_EOF; } static void phase6_init(void) { p6push = 0; } static TOKEN *token_new(void) { TOKEN *t; t = malloc(sizeof(TOKEN)); t->type = TT_ERROR; sfref_init(&t->file,"token %p",(void *)t); t->startline = -1; t->u.err = "unset token"; return(t); } static KEYWORD lookup_keyword(const STR s) { switch (s.len) { case 2: switch (s.data[0]) { case 'd': if (!bcmp(s.data,"do",2)) return(KEY_DO); break; case 'i': if (!bcmp(s.data,"if",2)) return(KEY_IF); break; } break; case 3: switch (s.data[0]) { case 'f': if (!bcmp(s.data,"for",3)) return(KEY_FOR); break; case 'i': if (!bcmp(s.data,"int",3)) return(KEY_INT); break; } break; case 4: switch (s.data[0] + s.data[3]) { case 'a' + 'o': if (!bcmp(s.data,"auto",4)) return(KEY_AUTO); break; case 'c' + 'e': if (!bcmp(s.data,"case",4)) return(KEY_CASE); break; case 'c' + 'r': if (!bcmp(s.data,"char",4)) return(KEY_CHAR); break; case 'e' + 'e': if (!bcmp(s.data,"else",4)) return(KEY_ELSE); break; case 'e' + 'm': if (!bcmp(s.data,"enum",4)) return(KEY_ENUM); break; case 'g' + 'o': if (!bcmp(s.data,"goto",4)) return(KEY_GOTO); break; case 'l' + 'g': if (!bcmp(s.data,"long",4)) return(KEY_LONG); break; case 'v' + 'd': if (!bcmp(s.data,"void",4)) return(KEY_VOID); break; } break; case 5: switch (s.data[0]) { case 'b': if (!bcmp(s.data,"break",5)) return(KEY_BREAK); break; case 'c': if (!bcmp(s.data,"const",5)) return(KEY_CONST); break; case 'f': if (!bcmp(s.data,"float",5)) return(KEY_FLOAT); break; case 's': if (!bcmp(s.data,"short",5)) return(KEY_SHORT); break; case 'u': if (!bcmp(s.data,"union",5)) return(KEY_UNION); break; case 'w': if (!bcmp(s.data,"while",5)) return(KEY_WHILE); break; } break; case 6: switch (s.data[0] + s.data[5]) { case 'd' + 'e': if (!bcmp(s.data,"double",6)) return(KEY_DOUBLE); break; case 'e' + 'n': if (!bcmp(s.data,"extern",6)) return(KEY_EXTERN); break; case 'i' + 'e': if (!bcmp(s.data,"inline",6)) return(KEY_INLINE); break; case 'r' + 'n': if (!bcmp(s.data,"return",6)) return(KEY_RETURN); break; case 's' + 'd': if (!bcmp(s.data,"signed",6)) return(KEY_SIGNED); break; case 's' + 'f': if (!bcmp(s.data,"sizeof",6)) return(KEY_SIZEOF); break; case 's' + 'c': if (!bcmp(s.data,"static",6)) return(KEY_STATIC); break; case 's' + 't': if (!bcmp(s.data,"struct",6)) return(KEY_STRUCT); break; case 's' + 'h': if (!bcmp(s.data,"switch",6)) return(KEY_SWITCH); break; } break; case 7: switch (s.data[0]) { case 'd': if (!bcmp(s.data,"default",7)) return(KEY_DEFAULT); break; case 't': if (!bcmp(s.data,"typedef",7)) return(KEY_TYPEDEF); break; } break; case 8: switch (s.data[4]) { case 'i': if (!bcmp(s.data,"continue",8)) return(KEY_CONTINUE); break; case 's': if (!bcmp(s.data,"register",8)) return(KEY_REGISTER); break; case 'r': if (!bcmp(s.data,"restrict",8)) return(KEY_RESTRICT); break; case 'g': if (!bcmp(s.data,"unsigned",8)) return(KEY_UNSIGNED); break; case 't': if (!bcmp(s.data,"volatile",8)) return(KEY_VOLATILE); break; } break; } return(KEY__NOKEY); } static int digitval(char d) { switch (d) { case '0': return(0); break; case '1': return(1); break; case '2': return(2); break; case '3': return(3); break; case '4': return(4); break; case '5': return(5); break; case '6': return(6); break; case '7': return(7); break; case '8': return(8); break; case '9': return(9); break; case 'a': case 'A': return(10); break; case 'b': case 'B': return(11); break; case 'c': case 'C': return(12); break; case 'd': case 'D': return(13); break; case 'e': case 'E': return(14); break; case 'f': case 'F': return(15); break; } return(16); } static int charconst_value(PPTOKEN *pt) { STR s; int v; int dv; int n; s = pt->str; if ((s.len > 0) && (s.data[0] == 'L')) { s.data ++; s.len --; } if ((s.len > 0) && (s.data[0] == '\'')) { s.data ++; s.len --; } if ((s.len > 0) && (s.data[s.len-1] == '\'')) s.len --; if (s.len < 1) { error_pt(pt,"empty character constant"); return(0); } if (s.data[0] == '\\') { if (s.len < 2) panic(); n = 2; switch (s.data[1]) { default: v = s.data[1]; break; case 'a': v = '\a'; break; case 'b': v = '\b'; break; case 'f': v = '\f'; break; case 'n': v = '\n'; break; case 'r': v = '\r'; break; case 't': v = '\t'; break; case 'v': v = '\v'; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': v = digitval(s.data[1]); while (s.len > n) { if (n >= 4) break; dv = digitval(s.data[n]); if (dv > 7) break; v = (v << 3) | dv; n ++; } break; case 'x': v = 0; while (s.len > n) { dv = digitval(s.data[n]); if (dv > 15) break; v = (v << 4) | dv; n ++; } break; } } else { v = s.data[0]; n = 1; } if (s.len > n) { error_pt(pt,"multi-character character constant"); } return(v); } static void const_token_int(TOKEN *t, int v) { int i; t->type = TT_CONST; t->u.cst.type = CST_INT; for (i=0;iu.cst.val[i] = v & 0xff; v >>= 8; } if (v) panic(); } static int integer_constant_token(TOKEN *t, PPTOKEN *pt, const char **whynot) { int base; int dv; unsigned char val[MAXVAL+1]; int vw; const char *s; int l; int x; int i; int j; int a; int ovf; const CSTTYPE *tv; int tvl; int w; int sgn; static const CSTTYPE t_none_dec[] = { CST_INT, CST_LONG, CST_LLONG }; static const CSTTYPE t_none_bin[] = { CST_INT, CST_UINT, CST_LONG, CST_ULONG, CST_LLONG, CST_ULLONG }; static const CSTTYPE t_u[] = { CST_UINT, CST_ULONG, CST_ULLONG }; static const CSTTYPE t_l_dec[] = { CST_LONG, CST_LLONG }; static const CSTTYPE t_l_bin[] = { CST_LONG, CST_ULONG, CST_LLONG, CST_ULLONG }; static const CSTTYPE t_ul[] = { CST_ULONG, CST_ULLONG }; static const CSTTYPE t_ll_dec[] = { CST_LLONG }; static const CSTTYPE t_ll_bin[] = { CST_LLONG, CST_ULLONG }; static const CSTTYPE t_ull[] = { CST_ULLONG }; l = pt->str.len; s = pt->str.data; if ((l > 0) && (s[0] == '0')) { base = 8; x = 0; } else if ((l > 1) && (s[0] == '0') && ((s[1] == 'x') || (s[1] == 'X'))) { base = 16; x = 2; } else { base = 10; x = 0; } vw = 0; ovf = 0; for (i=0;x+i= base) break; a = dv; for (j=0;j>= 8; } if (a > 0) { val[vw++] = a; if (vw > MAXVAL) { ovf = 1; vw = MAXVAL; } } } if (i == 0) { *whynot = "no digits"; return(0); } x += i; #define SETTV(vec) do { tv = &(vec)[0]; tvl = sizeof((vec))/sizeof((vec)[0]); } while (0) do <"suffix"> { switch (l - x) { case 0: if (base == 10) SETTV(t_none_dec); else SETTV(t_none_bin); break <"suffix">; case 1: switch (s[x]) { case 'u': case 'U': SETTV(t_u); break <"suffix">; case 'l': case 'L': if (base == 10) SETTV(t_l_dec); else SETTV(t_l_bin); break <"suffix">; } break; case 2: if (!strncasecmp(s+x,"ul",2) || !strncasecmp(s+x,"lu",2)) { SETTV(t_ul); break <"suffix">; } else if (!strncasecmp(s+x,"ll",2)) { if (base == 10) SETTV(t_ll_dec); else SETTV(t_ll_bin); break <"suffix">; } break; case 3: if (!strncasecmp(s+x,"ull",2) || !strncasecmp(s+x,"llu",2)) { SETTV(t_ull); break <"suffix">; } break; } *whynot = "invalid suffix"; return(0); } while (0); #undef SETTV // val[] element can be zero if ovf do vw --; while ((vw >= 0) && (val[vw] == 0)); if (vw < 0) { j = 0; } else { j = vw * 8; if (val[vw] & 0x80) j += 8; else if (val[vw] & 0x40) j += 7; else if (val[vw] & 0x20) j += 6; else if (val[vw] & 0x10) j += 5; else if (val[vw] & 0x08) j += 4; else if (val[vw] & 0x04) j += 3; else if (val[vw] & 0x02) j += 2; else if (val[vw] & 0x01) j += 1; else panic(); } for (i=0;ibits_int; sgn = 1; break; case CST_UINT: w = target->bits_int; sgn = 0; break; case CST_LONG: w = target->bits_long; sgn = 1; break; case CST_ULONG: w = target->bits_long; sgn = 0; break; case CST_LLONG: w = target->bits_llong; sgn = 1; break; case CST_ULLONG: w = target->bits_llong; sgn = 0; break; default: panic(); break; } if (j+sgn <= w) { sfref_copy(&t->file,&pt->file); t->startline = pt->startline; t->type = TT_CONST; t->u.cst.type = tv[i]; vw ++; if (vw > 0) bcopy(&val[0],&t->u.cst.val[0],vw*sizeof(val[0])); if (vw < MAXVAL) bzero(&t->u.cst.val[vw],(MAXVAL-vw)*sizeof(val[0])); if (ovf) error_pt(pt,"overflow in integer constant"); return(1); } } *whynot = "value too large for target"; return(0); } static int floating_constant_token(TOKEN *t, PPTOKEN *pt, const char **whynot) { const char *s; int l; int base; int inexp; unsigned char mant[MAXFDIGS]; int mantlen; int dotloc; int i; int dv; int exp; int expsgn; int lead0; CSTTYPE type; l = pt->str.len; s = pt->str.data; if ( (pt->str.len >= 2) && (pt->str.data[0] == '0') && ( (pt->str.data[1] == 'x') || (pt->str.data[1] == 'X') ) ) { base = 16; i = 2; } else { base = 10; i = 0; } mantlen = 0; lead0 = 0; dotloc = -1; inexp = 0; type = CST_DOUBLE; for (;i target->maxfexp) { *whynot = "floating exponent too large"; return(0); } } else if ((inexp == 1) && ((s[i] == '+') || (s[i] == '-'))) { expsgn = (s[i] == '+') ? 1 : -1; } else { *whynot = "invalid char in exponent"; return(0); } inexp ++; } else if (dv < base) { if ((dv == 0) && (mantlen == 0)) { if (dotloc >= 0) lead0 ++; } else { if (mantlen < MAXFDIGS) mant[mantlen] = dv; mantlen ++; } } else if (s[i] == '.') { if (dotloc < 0) { dotloc = mantlen; } else { *whynot = "multiple decimal points present"; return(0); } } else if ( (base == 10) ? ((s[i] == 'e') || (s[i] == 'E')) : ((s[i] == 'p') || (s[i] == 'P')) ) { inexp = 1; exp = 0; expsgn = 0; } } if (mantlen < 1) { *whynot = "no digits"; return(0); } if ((inexp == 1) || ((inexp == 2) && (expsgn != 0))) { *whynot = "no digits in exponent"; return(0); } if ((base == 16) && !inexp) { *whynot = "hex but no exponent"; return(0); } if ((dotloc < 0) && !inexp) { *whynot = "no decimal point and no exponent"; return(0); } t->type = TT_CONST; t->u.cst.type = type; if (! (*target->float_cvt)( t, base, &mant[0], (mantlen>MAXFDIGS)?MAXFDIGS:mantlen, (dotloc<0)?mantlen:(dotloc==0)?-lead0:dotloc, (expsgn<0)?-exp:exp, whynot ) ) return(0); sfref_copy(&t->file,&pt->file); t->startline = pt->startline; return(1); } static void const_token(TOKEN *t, PPTOKEN *pt) { const char *iwhy; const char *fwhy; if (integer_constant_token(t,pt,&iwhy)) return; if (floating_constant_token(t,pt,&fwhy)) return; } /* * "[#2] Each preprocessing token that is converted to a token * shall have the lexical form of a keyword, an identifier, a * constant, a string literal, or a punctuator." * * Thus, besides overhead types TT_ERROR and TT_EOF, the return token * type must be TT_KEY, TT_IDENT, TT_CONST, TT_STRING, or TT_PUNCT. */ static TOKEN *phase67a_convert(PPTOKEN *pt) { TOKEN *t; KEYWORD k; t = token_new(); sfref_copy(&t->file,&pt->file); t->startline = pt->startline; switch (pt->type) { default: panic(); break; case PPT_EOF: t->type = TT_EOF; break; case PPT_IDENT: k = lookup_keyword(pt->str); if (k != KEY__NOKEY) { t->type = TT_KEY; t->u.key = k; } else { t->type = TT_IDENT; t->u.ident = str_copy(pt->str); } break; case PPT_NUMBER: const_token(t,pt); break; case PPT_CHARCONST: const_token_int(t,charconst_value(pt)); break; case PPT_PUNCT: t->type = TT_PUNCT; t->u.punct = pt->detail.punct; break; } return(t); } static TOKEN *phase67a_build_string(PPTCONC *list) { (void)list; return(0); } static TOKEN *phase7a_get(void) { PPTOKEN *t; PPTCONC tc; if (p6push) { t = p6push; p6push = 0; } else { t = phase4_get(); } if (t->type != PPT_STRING) return(phase67a_convert(t)); pptconc_init(&tc); pptconc_append(&tc,pptlist_holding(t)); while (1) { t = phase4_get(); if (t->type != PPT_STRING) { p6push = t; return(phase67a_build_string(&tc)); } pptconc_append(&tc,pptlist_holding(t)); } } static void process_sourcefile(SRCFILE *sf) { int iseof; phase2_init(&p2p3); phase3_init(&p2p3); if (istack) panic(); if (sfref_follow(&cursf)) panic(); sfref_addref(&cursf,sf); if (Eflag) { PPTOKEN *t; 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 { TOKEN *t; t = phase7a_get(); } while (ifstack) { error_cursf("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(); if (! Eflag) phase6_init(); for (sf=sfref_follow(&srcfiles);sf;sf=sfref_follow(&sf->link)) process_sourcefile(sf); if (Eflag) Eflag_done(&Estate); phase4_done(); check_vfps(); sfref_drop(&srcfiles); } int main(int, char **); int main(int ac, char **av) { handleargs(ac,av); setup_verbosity(); convert_sourcefiles(); errs = 0; target = &target_sparc64; 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. */