/* * 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 char pp_punct_str_panic_; #define PP_PUNCT_STR_PANIC (&pp_punct_str_panic_) static const char *pp_punct_str_(PUNCTTYPE t, const char *def) #define pp_punct_str(t) pp_punct_str_(t,PP_PUNCT_STR_PANIC) { 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; } if (def != PP_PUNCT_STR_PANIC) return(def); 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_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; 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]; } 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(); } } static PPTOKEN *phase4_get(void) { PPTOKEN *t; FILE *f; while (1) { t = phase4_get_(); if (t->type != PPT_EOF) { 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); } if (! istack) { pptoken_free(t); VERB(PHASE4,"phase4_get: got nil\n"); return(0); } check_if_nesting(); pop_istack(); } } 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 const char *keyword_name(KEYWORD k) { switch (k) { case KEY_AUTO: return("auto"); break; case KEY_BREAK: return("break"); break; case KEY_CASE: return("case"); break; case KEY_CHAR: return("char"); break; case KEY_CONST: return("const"); break; case KEY_CONTINUE: return("continue"); break; case KEY_DEFAULT: return("default"); break; case KEY_DO: return("do"); break; case KEY_DOUBLE: return("double"); break; case KEY_ELSE: return("else"); break; case KEY_ENUM: return("enum"); break; case KEY_EXTERN: return("extern"); break; case KEY_FLOAT: return("float"); break; case KEY_FOR: return("for"); break; case KEY_GOTO: return("goto"); break; case KEY_IF: return("if"); break; case KEY_INLINE: return("inline"); break; case KEY_INT: return("int"); break; case KEY_LONG: return("long"); break; case KEY_REGISTER: return("register"); break; case KEY_RESTRICT: return("restrict"); break; case KEY_RETURN: return("return"); break; case KEY_SHORT: return("short"); break; case KEY_SIGNED: return("signed"); break; case KEY_SIZEOF: return("sizeof"); break; case KEY_STATIC: return("static"); break; case KEY_STRUCT: return("struct"); break; case KEY_SWITCH: return("switch"); break; case KEY_TYPEDEF: return("typedef"); break; case KEY_UNION: return("union"); break; case KEY_UNSIGNED: return("unsigned"); break; case KEY_VOID: return("void"); break; case KEY_VOLATILE: return("volatile"); break; case KEY_WHILE: return("while"); break; default: return(0); break; } } 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 { while <"skipws"> (1) { t = phase4_get(); if (! t) return(0); switch (t->type) { default: break <"skipws">; case PPT_WHITESPACE: case PPT_NEWLINE: break; } } } 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)); } } /* * val points to MAXVAL unsigned chars */ static void dump_integer_u(FILE *f, const unsigned char *val, int bits) { unsigned char work[MAXVAL]; int i; int a; void dec(void) { int i; int a; int nz; a = 0; nz = 0; for (i=MAXVAL-1;i>=0;i--) { a = (a * 0x100) + work[i]; if (a >= 10) nz = 1; work[i] = a / 10; a %= 10; } if (nz) dec(); fprintf(f,"%d",a); } if (bits < 1) panic(); do <"dumpall"> { if ((bits & 7) && (val[bits>>3] >> (bits&7))) break <"dumpall">; for (i=(bits>>3)+1;i; bcopy(val,&work[0],MAXVAL); dec(); return; } while (0); fprintf(f,""); } /* * val points to MAXVAL unsigned chars */ static void dump_integer_s(FILE *f, const unsigned char *val, int bits) { int i; int b; int a; if (bits < 2) panic(); do <"dumpall"> { if ((bits & 7) && (val[bits>>3] >> (bits&7))) break <"dumpall">; for (i=(bits>>3)+1;i; b = bits - 1; if (val[b>>3] >> (b&7)) { unsigned char copy[MAXVAL]; b = (bits >> 3) - 1; a = 0; for (i=0;i"); } static void token_dump(FILE *f, TOKEN *t) { fprintf(f,"file=%s startline=%d ",sfref_follow(&t->file)->name,t->startline); switch (t->type) { default: panic(); break; case TT_ERROR: fprintf(f,"ERROR: %s\n",t->u.err); break; case TT_EOF: fprintf(f,"EOF\n"); break; case TT_KEY: { const char *s; s = keyword_name(t->u.key); if (! s) panic(); fprintf(f,"KEYWORD: %s\n",keyword_name(t->u.key)); } break; case TT_IDENT: fprintf(f,"IDENT: "); fwrite(t->u.ident.data,1,t->u.ident.len,f); break; case TT_CONST: switch (t->u.cst.type) { default: panic(); break; case CST_INT: fprintf(f,"CONST INT "); dump_integer_s(f,&t->u.cst.val[0],target->bits_int); fprintf(f,"\n"); break; case CST_UINT: fprintf(f,"CONST UINT "); dump_integer_u(f,&t->u.cst.val[0],target->bits_int); fprintf(f,"\n"); break; case CST_LONG: fprintf(f,"CONST LONG "); dump_integer_s(f,&t->u.cst.val[0],target->bits_long); fprintf(f,"\n"); break; case CST_ULONG: fprintf(f,"CONST ULONG "); dump_integer_u(f,&t->u.cst.val[0],target->bits_long); fprintf(f,"\n"); break; case CST_LLONG: fprintf(f,"CONST LLONG "); dump_integer_s(f,&t->u.cst.val[0],target->bits_llong); fprintf(f,"\n"); break; case CST_ULLONG: fprintf(f,"CONST ULLONG "); dump_integer_u(f,&t->u.cst.val[0],target->bits_llong); fprintf(f,"\n"); break; case CST_FLOAT: fprintf(f,"CONST FLOAT "); (*target->float_dump)(f,CST_FLOAT,&t->u.cst.val[0]); fprintf(f,"\n"); break; case CST_DOUBLE: fprintf(f,"CONST DOUBLE "); (*target->float_dump)(f,CST_DOUBLE,&t->u.cst.val[0]); fprintf(f,"\n"); break; case CST_LDOUBLE: fprintf(f,"CONST LDOUBLE "); (*target->float_dump)(f,CST_LDOUBLE,&t->u.cst.val[0]); fprintf(f,"\n"); break; } break; case TT_STRING: fprintf(f,"STRING "); str_print_escaped(&t->u.string,f); fprintf(f,"\n"); break; case TT_PUNCT: fprintf(f,"PUNCT %s\n",pp_punct_str(t->u.punct)); break; } } static void token_free(TOKEN *t) { if (! t) return; switch (t->type) { case TT_IDENT: str_free(&t->u.ident); break; case TT_STRING: str_free(&t->u.string); break; default: break; } sfref_done(&t->file); free(t); } static void process_sourcefile(SRCFILE *sf) { 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(); if (! t) break; Eflag_output(&Estate,t); } } else { TOKEN *t; while (1) { t = phase7a_get(); if (! t) break; token_dump(stdout,t); token_free(t); } } 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); }