/* * Preprocessor. Handles compilation phases 1-6. */ #include #include #include #include #include #include #include #include "cclass.h" extern const char *__progname; #define MAXP3UNGET 4 // never want to unget more than 4 chars typedef enum { PPT_NONE = 1, PPT_EOF, PPT_NEWLINE, PPT_WHITESPACE, PPT_HEADERNAME, PPT_IDENT, PPT_NUMBER, PPT_CHARCONST, PPT_STRING, PPT_PUNCT, PPT_OTHER, // other single character PPT_MARKER, // internal only - see 6.10 PPT_ENDMACRO, // internal only - not C99-specified } PPTOKTYPE; typedef enum { PUNCT_LBRACK = 1, // [ PUNCT_RBRACK, // ] PUNCT_LPAREN, // ( PUNCT_RPAREN, // ) PUNCT_LBRACE, // { PUNCT_RBRACE, // } PUNCT_DOT, // . PUNCT_ARROW, // -> PUNCT_PLUSPLUS, // ++ PUNCT_MINUSMINUS, // -- PUNCT_AND, // & PUNCT_STAR, // * PUNCT_PLUS, // + PUNCT_MINUS, // - PUNCT_TILDE, // ~ PUNCT_BANG, // ! PUNCT_SLASH, // / PUNCT_PCT, // % PUNCT_LSH, // << PUNCT_RSH, // >> PUNCT_LT, // < PUNCT_GT, // > PUNCT_LEQ, // <= PUNCT_GEQ, // >= PUNCT_EQ, // == PUNCT_NEQ, // != PUNCT_HAT, // ^ PUNCT_OR, // | PUNCT_ANDAND, // && PUNCT_OROR, // || PUNCT_QUESTION, // ? PUNCT_COLON, // : PUNCT_SEMI, // ; PUNCT_ELLIPSIS, // ... PUNCT_ASSIGN, // = PUNCT_MULEQ, // *= PUNCT_DIVEQ, // /= PUNCT_MODEQ, // %= PUNCT_ADDEQ, // += PUNCT_SUBEQ, // -= PUNCT_LSHEQ, // <<= PUNCT_RSHEQ, // >>= PUNCT_ANDEQ, // &= PUNCT_XOREQ, // ^= PUNCT_OREQ, // |= PUNCT_COMMA, // , PUNCT_SHARP, // # PUNCT_SSHARP, // ## } PUNCTTYPE; typedef enum { P2S_NORMAL = 1, P2S_BACKSLASH, P2S_CONT, P2S_PUSHED, } P2STATE; typedef enum { IFS_FALSE = 1, IFS_TRUE, IFS_OVER, IFS_ELSE_F, IFS_ELSE_T, IFS_NEST, } IFSTATE; typedef struct srcfile SRCFILE; typedef struct pptoken PPTOKEN; typedef struct str STR; typedef struct ebuf EBUF; typedef struct istack ISTACK; typedef struct ifstack IFSTACK; typedef struct pptlist PPTLIST; typedef struct macro MACRO; typedef struct macroexpander MACROEXPANDER; struct str { char *data; int len; int needsfree; } ; struct macro { MACRO *link; STR name; unsigned int flags; #define MF_HASARGS 0x00000001 #define MF_VARARGS 0x00000002 #define MF_DISABLED 0x00000004 int nargs; PPTOKEN **args; PPTLIST *replacement; } ; struct macroexpander { PPTLIST *done; PPTLIST **donetail; MACRO *curmac; PPTOKEN *name; PPTLIST *arglist; PPTLIST **argtail; int argnest; } ; struct ifstack { IFSTACK *link; IFSTATE state; } ; struct pptlist { PPTLIST *link; PPTOKEN *token; } ; struct istack { ISTACK *link; SRCFILE *sf; } ; struct ebuf { unsigned char *b; int a; int l; } ; #define EBUF_INIT { 0, 0 } struct srcfile { SRCFILE *link; char *name; FILE *f; int lno; } ; struct pptoken { PPTOKTYPE type; STR str; SRCFILE *file; int startline; union { PUNCTTYPE punct; // PPT_PUNCT unsigned char other; // PPT_OTHER MACRO *macro; // PPT_ENDMACRO } detail; } ; static SRCFILE *srcfiles; static int errs; static SRCFILE *cursf; static int startlno; static P2STATE p2state; static int p2push; static int np3unget; static int p3unget[MAXP3UNGET]; static EBUF p3buf = EBUF_INIT; static PPTOKEN *p3pushed; static MACROEXPANDER p4mx; static ISTACK *istack; static int atbol; static IFSTACK *ifstack; static MACRO *macros; #define Cisdigit(x) isdigit((unsigned char)(x)) static void panic(void) { (void)*(volatile char *)0; abort(); } static void error(const char *, ...) __attribute__((__format__(__printf__,1,2))); static void error(const char *fmt, ...) { va_list ap; fprintf(stderr,"%s: \"%s\", line %d: ",__progname,cursf->name,cursf->lno); va_start(ap,fmt); vfprintf(stderr,fmt,ap); va_end(ap); fprintf(stderr,"\n"); errs ++; } static int ebuf_len(EBUF *eb) { return(eb->l); } static void *ebuf_data(EBUF *eb) { return(eb->b); } static void ebuf_clear(EBUF *eb) { eb->l = 0; } static void ebuf_append1(EBUF *eb, unsigned char c) { if (eb->l >= eb->a) eb->b = realloc(eb->b,eb->a=eb->l+8); eb->b[eb->l++] = c; } static void handleargs(int ac, char **av) { int skip; int errs; SRCFILE **sftail; SRCFILE *sf; sftail = &srcfiles; skip = 0; errs = 0; for (ac--,av++;ac;ac--,av++) { if (skip > 0) { skip --; continue; } if (**av != '-') { sf = malloc(sizeof(SRCFILE)); sf->name = *av; *sftail = sf; sftail = &sf->link; 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,"-timestdin")) { WANTARG(); continue; } #undef WANTARG fprintf(stderr,"%s: unrecognized option `%s'\n",__progname,*av); errs ++; } if (errs) exit(1); *sftail = 0; } 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=srcfiles;sf;sf=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 pptoken_dump(PPTOKEN *pt) { int pstr; printf("file=%s startline=%d ",pt->file->name,pt->startline); pstr = 0; switch (pt->type) { case PPT_NONE: printf("NONE"); break; case PPT_EOF: printf("EOF"); break; case PPT_NEWLINE: printf("NEWLINE"); break; case PPT_WHITESPACE: printf("WHITESPACE"); pstr = 1; break; case PPT_HEADERNAME: printf("HEADERNAME"); break; case PPT_IDENT: printf("IDENT"); pstr = 1; break; case PPT_NUMBER: printf("NUMBER"); pstr = 1; break; case PPT_CHARCONST: printf("CHARCONST"); pstr = 1; break; case PPT_STRING: printf("STRING"); pstr = 1; break; case PPT_MARKER: printf("MARKER"); break; case PPT_PUNCT: printf("PUNCT "); switch (pt->detail.punct) { case PUNCT_LBRACK: printf("["); break; case PUNCT_RBRACK: printf("]"); break; case PUNCT_LPAREN: printf("("); break; case PUNCT_RPAREN: printf(")"); break; case PUNCT_LBRACE: printf("{"/*}*/); break; case PUNCT_RBRACE: printf(/*{*/"}"); break; case PUNCT_DOT: printf("."); break; case PUNCT_ARROW: printf("->"); break; case PUNCT_PLUSPLUS: printf("++"); break; case PUNCT_MINUSMINUS: printf("--"); break; case PUNCT_AND: printf("&"); break; case PUNCT_STAR: printf("*"); break; case PUNCT_PLUS: printf("+"); break; case PUNCT_MINUS: printf("-"); break; case PUNCT_TILDE: printf("~"); break; case PUNCT_BANG: printf("!"); break; case PUNCT_SLASH: printf("/"); break; case PUNCT_PCT: printf("%%"); break; case PUNCT_LSH: printf("<<"); break; case PUNCT_RSH: printf(">>"); break; case PUNCT_LT: printf("<"); break; case PUNCT_GT: printf(">"); break; case PUNCT_LEQ: printf("<="); break; case PUNCT_GEQ: printf(">="); break; case PUNCT_EQ: printf("=="); break; case PUNCT_NEQ: printf("!="); break; case PUNCT_HAT: printf("^"); break; case PUNCT_OR: printf("|"); break; case PUNCT_ANDAND: printf("&&"); break; case PUNCT_OROR: printf("||"); break; case PUNCT_QUESTION: printf("?"); break; case PUNCT_COLON: printf(":"); break; case PUNCT_SEMI: printf(";"); break; case PUNCT_ELLIPSIS: printf("..."); break; case PUNCT_ASSIGN: printf("="); break; case PUNCT_MULEQ: printf("*="); break; case PUNCT_DIVEQ: printf("/="); break; case PUNCT_MODEQ: printf("%%="); break; case PUNCT_ADDEQ: printf("+="); break; case PUNCT_SUBEQ: printf("-="); break; case PUNCT_LSHEQ: printf("<<="); break; case PUNCT_RSHEQ: printf(">>="); break; case PUNCT_ANDEQ: printf("&="); break; case PUNCT_XOREQ: printf("^="); break; case PUNCT_OREQ: printf("|="); break; case PUNCT_COMMA: printf(","); break; case PUNCT_SHARP: printf("#"); break; case PUNCT_SSHARP: printf("##"); break; default: printf("?%d",(int)pt->detail.punct); break; } printf(""); break; case PPT_OTHER: printf("OTHER %c",pt->detail.other); break; case PPT_ENDMACRO: printf("ENDMACRO %p (%.*s)",(void *)pt->detail.macro,pt->detail.macro->name.len,pt->detail.macro->name.data); break; default: printf("?%d",(int)pt->type); break; } if (pstr) { printf(" str="); str_print_escaped(&pt->str,stdout); } printf("\n"); } static void pptoken_free(PPTOKEN *pt) { str_free(&pt->str); free(pt); } static PPTOKEN *pptoken_copy(PPTOKEN *t) { PPTOKEN *n; n = malloc(sizeof(PPTOKEN)); *n = *t; switch (t->type) { case PPT_WHITESPACE: case PPT_IDENT: case PPT_NUMBER: case PPT_CHARCONST: case PPT_STRING: n->str = str_copy(t->str); break; default: n->str.len = 0; n->str.data = 0; n->str.needsfree = 0; break; } return(n); } static PPTOKEN *pptoken_of_type(PPTOKTYPE type) { PPTOKEN *t; t = malloc(sizeof(PPTOKEN)); t->type = type; t->str.data = 0; t->str.len = 0; t->str.needsfree = 0; t->file = cursf; t->startline = startlno; return(t); } static PPTOKEN *pptoken_with_str(PPTOKTYPE type) { PPTOKEN *t; t = pptoken_of_type(type); t->str.len = ebuf_len(&p3buf); t->str.data = malloc(t->str.len); bcopy(ebuf_data(&p3buf),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 void pptlist_free(PPTLIST *list) { PPTLIST *e; while ((e = list)) { 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 *nl; PPTLIST **nt; PPTLIST *e; PPTLIST *n; nt = &nl; for (e=toclone;e;e=e->link) { n = pptlist_holding(pptoken_copy(e->token)); *nt = n; nt = &n->link; } *nt = tail; return(nl); } 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; default: panic(); break; } } r1 = r1->link; r2 = r2->link; } macro_free(new); return; } while (0); error("%.*s incorrectly redefined",new->name.len,new->name.data); macro_free(new); return; } } new->link = macros; macros = new; } static void macro_undef(PPTOKEN *name) { MACRO *m; MACRO **mp; mp = ¯os; while ((m = *mp)) { if (str_equal_str(&m->name,&name->str)) { *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 void macroexpander_init(MACROEXPANDER *mx) { mx->done = 0; mx->donetail = &mx->done; mx->curmac = 0; } static PPTOKEN *macroexpander_get(MACROEXPANDER *mx) { PPTLIST *l; PPTOKEN *t; l = mx->done; if (! l) return(0); mx->done = l->link; if (! mx->done) mx->donetail = &mx->done; t = l->token; free(l); return(t); } static void macroexpander_addarg(MACROEXPANDER *mx, PPTOKEN *t) { PPTLIST *l; l = pptlist_holding(t); *mx->argtail = l; mx->argtail = &l->link; } static void macroexpander_append_done(MACROEXPANDER *mx, PPTOKEN *t) { PPTLIST *l; l = pptlist_holding(t); *mx->donetail = l; mx->donetail = &l->link; } static void macroexpander_expand_args(MACROEXPANDER *mx) { PPTLIST *a; int nest; int nargs; nest = 0; nargs = 0; for (a=mx->arglist;a;a=a->link) { if (a->token->type != PPT_PUNCT) continue; 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; } } XXX XXX XXX;;; } static PPTOKEN *macroexpander_input(MACROEXPANDER *mx, PPTOKEN *t) { MACRO *m; PPTLIST *pending; void pending_push(PPTOKEN *t) { PPTLIST *l; l = pptlist_holding(t); l->link = pending; pending = l; } PPTOKEN *pending_pop(void) { PPTLIST *l; PPTOKEN *t; l = pending; pending = l->link; t = l->token; free(l); return(t); } if (mx->done) panic(); if (!mx->curmac && ((t->type != PPT_IDENT) || !macro_find(t))) return(t); pending = 0; pending_push(t); while (pending) { t = pending_pop(); 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)) { mx->argtail = &mx->arglist; pptoken_free(t); mx->argnest = 1; pptoken_free(mx->name); mx->name = 0; } else { macroexpander_append_done(mx,mx->name); pending_push(t); mx->name = 0; mx->curmac = 0; } } else { switch (t->type) { case PPT_EOF: error("EOF inside macro invocation arglist"); pptoken_free(t); *mx->argtail = 0; pptlist_free(mx->arglist); 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); *mx->argtail = 0; 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; pending_push(pptoken_endmacro(m)); pending = pptlist_clone_append(m->replacement,pending); } } *mx->donetail = 0; return(macroexpander_get(mx)); } static void phase2_init(void) { p2state = P2S_NORMAL; } static int sfget(void) { int c; c = getc(cursf->f); if (c == '\n') cursf->lno ++; return(c); } static void include_file(PPTOKEN *fnt) { char *fn; int fd; FILE *f; char *slash; char *name; SRCFILE *newsf; ISTACK *i; int try(const char *path) { fd = open(path,O_RDONLY,0); return(fd>=0); } 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(cursf->name,'/'); if (slash) { asprintf(&name,"%.*s/%s",(int)(slash-cursf->name),cursf->name,fn); } else { name = strdup(fn); } if (try(name)) break <"open">; free(name); } asprintf(&name,"/usr/include/%s",fn); if (try(name)) break <"open">; free(name); } error("include file %s not found",fn); free(fn); return; } while (0); f = fdopen(fd,"r"); newsf = malloc(sizeof(SRCFILE)); newsf->name = name; newsf->f = f; newsf->lno = 1; newsf->link = 0; cursf = newsf; i = malloc(sizeof(ISTACK)); i->sf = newsf; i->link = istack; istack = i; } static int phase2_get(void) { int c; while (1) { switch (p2state) { case P2S_NORMAL: c = sfget(); switch (c) { case EOF: return(EOF); break; case '\\': p2state = P2S_BACKSLASH; break; default: return(c); break; } break; case P2S_BACKSLASH: c = sfget(); switch (c) { case EOF: error("EOF after backslash"); return(EOF); break; case '\n': p2state = P2S_CONT; break; default: p2state = P2S_PUSHED; p2push = c; return('\\'); break; } break; case P2S_PUSHED: c = p2push; p2state = P2S_NORMAL; p2push = 0; return(c); break; case P2S_CONT: c = sfget(); switch (c) { case EOF: error("EOF after backslash-newline"); return(EOF); break; default: p2state = P2S_NORMAL; return(c); break; } break; default: panic(); break; } } } static void phase3_init(void) { np3unget = 0; p3pushed = 0; } static int p3iget(void) { if (np3unget > 0) return(p3unget[--np3unget]); return(phase2_get()); } static void p3iunget(int c) { if (np3unget >= MAXP3UNGET) panic(); p3unget[np3unget++] = c; } static PPTOKEN *phase3_ppnumber(int dig, int haddot) { int c; if (haddot) ebuf_append1(&p3buf,'.'); ebuf_append1(&p3buf,dig); while <"num"> (1) { c = p3iget(); switch (c) { case 'e': case 'E': case 'p': case 'P': ebuf_append1(&p3buf,c); c = p3iget(); switch (c) { case '+': case '-': ebuf_append1(&p3buf,c); break; default: p3iunget(c); break; } break; case '.': ebuf_append1(&p3buf,c); break; case EOF: p3iunget(EOF); break <"num">; default: if (! (chclass(c) & (CC_IDENT|CC_DIGIT))) break <"num">; ebuf_append1(&p3buf,c); break; } } p3iunget(c); return(pptoken_with_str(PPT_NUMBER)); } static PPTOKEN *phase3_stringish(char odelim, char cdelim, PPTOKTYPE type) { int c; int bq; bq = 0; ebuf_append1(&p3buf,odelim); while (1) { c = p3iget(); if (c == EOF) { error("unclosed %c at EOF\n",odelim); return(pptoken_of_type(PPT_EOF)); } if (bq) { bq = 0; } else if (c == '\\') { bq = 1; } ebuf_append1(&p3buf,c); if (c == cdelim) break; } return(pptoken_with_str(type)); } static PPTOKEN *pptoken_comment_slashslash(void) { int c; ebuf_append1(&p3buf,'/'); ebuf_append1(&p3buf,'/'); while (1) { c = p3iget(); switch (c) { case EOF: error("EOF inside a // comment"); return(pptoken_of_type(PPT_EOF)); break; case '\n': p3iunget('\n'); return(pptoken_with_str(PPT_WHITESPACE)); break; } ebuf_append1(&p3buf,c); } } static PPTOKEN *pptoken_comment_slashstar(void) { int pc; int c; ebuf_append1(&p3buf,'/'); ebuf_append1(&p3buf,'*'); c = 0; do { pc = c; c = p3iget(); switch (c) { case EOF: error("EOF inside a /* comment */"); return(pptoken_of_type(PPT_EOF)); break; } ebuf_append1(&p3buf,c); } while (! ((pc == '*') && (c == '/'))); return(pptoken_with_str(PPT_WHITESPACE)); } static PPTOKEN *phase3_get(int headername) { int c; int c2; int cc; PPTOKEN *t; if (p3pushed) { t = p3pushed; p3pushed = 0; return(t); } ebuf_clear(&p3buf); startlno = cursf->lno; c = p3iget(); if (c == EOF) return(pptoken_of_type(PPT_EOF)); cc = chclass(c); if (cc & CC_NL) return(pptoken_of_type(PPT_NEWLINE)); if (headername) { switch (c) { case '<': return(phase3_stringish('<','>',PPT_HEADERNAME)); break; case '"': return(phase3_stringish('"','"',PPT_HEADERNAME)); break; } } if (cc & CC_WHITE) { do { ebuf_append1(&p3buf,c); c = p3iget(); } while ((c != EOF) && (chclass(c) & CC_WHITE)); p3iunget(c); return(pptoken_with_str(PPT_WHITESPACE)); } if (cc & CC_IDENT) { do { ebuf_append1(&p3buf,c); c = p3iget(); } while ((c != EOF) && (chclass(c) & (CC_IDENT|CC_DIGIT))); p3iunget(c); return(pptoken_with_str(PPT_IDENT)); } if (cc & CC_DIGIT) return(phase3_ppnumber(c,0)); switch (c) { case '\'': return(phase3_stringish('\'','\'',PPT_CHARCONST)); break; case '"': return(phase3_stringish('"','"',PPT_STRING)); break; case '.': c = p3iget(); if (chclass(c) & CC_DIGIT) return(phase3_ppnumber(c,1)); if (c == '.') { c = p3iget(); if (c == '.') return(pptoken_punct(PUNCT_ELLIPSIS)); p3iunget(c); c = '.'; } p3iunget(c); return(pptoken_punct(PUNCT_DOT)); break; case '!': { PUNCTTYPE witheq; PUNCTTYPE noeq; witheq = PUNCT_NEQ; noeq = PUNCT_BANG; if (0) { case '%': witheq = PUNCT_MODEQ; noeq = PUNCT_PCT; } if (0) { case '*': witheq = PUNCT_MULEQ; noeq = PUNCT_STAR; } if (0) { case '=': witheq = PUNCT_EQ; noeq = PUNCT_ASSIGN; } if (0) { case '^': witheq = PUNCT_XOREQ; noeq = PUNCT_HAT; } c = p3iget(); if (c == '=') return(pptoken_punct(witheq)); p3iunget(c); return(pptoken_punct(noeq)); } break; case '/': c = p3iget(); switch (c) { case '/': return(pptoken_comment_slashslash()); break; case '*': return(pptoken_comment_slashstar()); break; case '=': return(pptoken_punct(PUNCT_DIVEQ)); break; default: p3iunget(c); return(pptoken_punct(PUNCT_SLASH)); break; } break; case '&': { PUNCTTYPE witheq; PUNCTTYPE doubled; PUNCTTYPE bare; witheq = PUNCT_ANDEQ; doubled = PUNCT_ANDAND; bare = PUNCT_AND; if (0) { case '+': witheq = PUNCT_ADDEQ; doubled = PUNCT_PLUSPLUS; bare = PUNCT_PLUS; } if (0) { case '|': witheq = PUNCT_OREQ; doubled = PUNCT_OROR; bare = PUNCT_OR; } c2 = p3iget(); if (c2 == '=') return(pptoken_punct(witheq)); if (c2 == c) return(pptoken_punct(doubled)); p3iunget(c2); return(pptoken_punct(bare)); } break; case '-': c = p3iget(); switch (c) { case '>': return(pptoken_punct(PUNCT_ARROW)); break; case '-': return(pptoken_punct(PUNCT_MINUSMINUS)); break; case '=': return(pptoken_punct(PUNCT_SUBEQ)); break; default: p3iunget(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_LSHEQ; bare = PUNCT_LT; if (0) { case '>': witheq = PUNCT_GEQ; doubled = PUNCT_RSH; doubledeq = PUNCT_RSHEQ; bare = PUNCT_GT; } c2 = p3iget(); if (c2 == '=') return(pptoken_punct(witheq)); if (c2 == c) { c2 = p3iget(); if (c2 == '=') return(pptoken_punct(doubledeq)); p3iunget(c2); return(pptoken_punct(doubled)); } p3iunget(c2); return(pptoken_punct(bare)); } break; case '#': c = p3iget(); if (c == '#') return(pptoken_punct(PUNCT_SSHARP)); p3iunget(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)); break; } } static PPTOKEN *phase3_get_skipws(void) { PPTOKEN *t; do t = phase3_get(0); while (t->type == PPT_WHITESPACE); return(t); } static void phase3_push(PPTOKEN *t) { if (p3pushed) panic(); p3pushed = t; } static void phase4_init(void) { istack = 0; atbol = 1; ifstack = 0; macros = 0; macroexpander_init(&p4mx); } static int eval_if_expr(PPTLIST *list) { PPTLIST *push; PPTLIST *ltmp; PPTOKEN *t; push = 0; while (1) { if (push) { ltmp = push; push = ltmp->link; } else { ltmp = list; list = ltmp->link; } t = ltmp->token; free(ltmp); } } static PPTOKEN *phase4_flush_line(void) { PPTOKEN *t; while (1) { t = phase3_get(0); 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 void phase4_start_if(IFSTATE state) { IFSTACK *s; s = malloc(sizeof(IFSTACK)); s->state = state; s->link = ifstack; ifstack = s; } static void phase4_end_if(void) { IFSTACK *s; s = ifstack; ifstack = s->link; free(s); } static PPTOKEN *phase4_macro_arglist(int (*arg)(PPTOKEN *)) { PPTOKEN *t; PPTOKEN *t2; t = phase3_get_skipws(); if (t->type == PPT_EOF) { error("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(); if (t2->type == PPT_EOF) { error("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("... in macro formal list isn't followed by )"); pptoken_free(t); phase3_push(t2); return(phase4_flush_line()); } if ((t->type == PPT_PUNCT) && (t->detail.punct == PUNCT_COMMA)) { error("missing macro formal argument"); phase3_push(t); return(phase4_flush_line()); } if (t->type != PPT_IDENT) { error("macro formal argument isn't an identifier"); phase3_push(t); return(phase4_flush_line()); } while (1) { if ((*arg)(t)) return(phase4_flush_line()); t = phase3_get_skipws(); if (t->type == PPT_EOF) { error("EOF reading macro argument formals"); return(t); } if ((t->type == PPT_PUNCT) && (t->detail.punct == PUNCT_RPAREN)) { pptoken_free(t); return(0); } if (t->type == PPT_EOF) { error("EOF reading macro argument formals"); return(t); } if (! ((t->type == PPT_PUNCT) && (t->detail.punct == PUNCT_COMMA))) { error("invalid macro argument list delimiter"); phase3_push(t); return(phase4_flush_line()); } pptoken_free(t); t = phase3_get_skipws(); if (t->type == PPT_IDENT) continue; if ((t->type == PPT_PUNCT) && (t->detail.punct == PUNCT_ELLIPSIS)) { t2 = phase3_get_skipws(); if (t2->type == PPT_EOF) { error("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("... in macro formal list isn't followed by )"); pptoken_free(t); phase3_push(t2); return(phase4_flush_line()); } error("macro formal argument isn't an identifier"); phase3_push(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(); phase3_push(t); while (1) { t = phase3_get(0); 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 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("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); } name = phase3_get_skipws(); if (name->type != PPT_IDENT) { error("#define with invalid name"); phase3_push(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(0); if ((next->type == PPT_PUNCT) && (next->detail.punct == PUNCT_LPAREN)) { pptoken_free(next); t = phase4_macro_arglist(&add_formal); if (t) { macro_free(m); return(t); } } else if (next->type != PPT_WHITESPACE) { error("#define is neither object-like nor function-like"); macro_free(m); phase3_push(next); return(phase4_flush_line()); } t = phase4_rest_of_line(&m->replacement); switch <"done"> (t->type) { case PPT_EOF: error("EOF reading replacement text for #define"); macro_free(m); break; case PPT_NEWLINE: if (! (m->flags & MF_VARARGS)) { for (l=m->replacement;l;l=l->link) { if ( (l->token->type == PPT_IDENT) && (l->token->str.len == 11) && !bcmp(l->token->str.data,"__VA_ARGS__",11) ) break; } if (l) { error("__VA_ARGS__ used in a non-varargs macro"); macro_free(m); break; } } macro_define(m); break; default: panic(); break; } return(t); } static PPTOKEN *phase4_one_name_line(const char *what) { PPTOKEN *name; PPTOKEN *t; name = phase3_get_skipws(); switch (name->type) { case PPT_IDENT: break; case PPT_EOF: error("EOF partway through #%s line",what); phase3_push(name); return(0); break; case PPT_NEWLINE: error("#%s line with no name",what); phase3_push(name); return(0); break; default: error("#%s with invalid name",what); phase3_push(name); return(0); break; } t = phase3_get_skipws(); if (t->type == PPT_EOF) { error("EOF partway through #%s line",what); pptoken_free(name); phase3_push(t); return(0); } if (t->type != PPT_NEWLINE) { error("#%s with garbage after name",what); pptoken_free(name); pptoken_free(t); return(0); } phase3_push(t); return(name); } static int phase4_bare_line(const char *what) { PPTOKEN *t; t = phase3_get_skipws(); if (t->type == PPT_EOF) { error("EOF partway through #%s line",what); phase3_push(t); return(0); } if (t->type != PPT_NEWLINE) { error("garbage on #%s line",what); pptoken_free(t); return(0); } phase3_push(t); return(1); } static PPTOKEN *phase4_do_undef(void) { PPTOKEN *name; name = phase4_one_name_line("undef"); if (name) { macro_undef(name); pptoken_free(name); } return(phase4_flush_line()); } static PPTOKEN *phase4_do_ifdef(void) { PPTOKEN *name; if (! phase4_iftrue()) { phase4_start_if(IFS_NEST); } else { name = phase4_one_name_line("ifdef"); 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; if (! phase4_iftrue()) { phase4_start_if(IFS_NEST); } else { name = phase4_one_name_line("ifdef"); if (name) { phase4_start_if(macro_find(name)?IFS_FALSE:IFS_TRUE); pptoken_free(name); } } return(phase4_flush_line()); } static PPTOKEN *phase4_do_else(void) { if (! ifstack) { error("#else with no matching conditional"); } else { switch (ifstack->state) { case IFS_FALSE: if (phase4_bare_line("else")) ifstack->state = IFS_ELSE_T; break; case IFS_TRUE: if (phase4_bare_line("else")) ifstack->state = IFS_ELSE_F; break; case IFS_OVER: ifstack->state = IFS_ELSE_F; break; case IFS_ELSE_F: case IFS_ELSE_T: error("multiple #else blocks in a conditional"); ifstack->state = IFS_ELSE_F; break; case IFS_NEST: break; default: panic(); break; } } return(phase4_flush_line()); } static PPTOKEN *phase4_do_endif(void) { if (ifstack && (ifstack->state == IFS_NEST)) { phase4_end_if(); } else if (phase4_bare_line("endif")) { if (ifstack) { phase4_end_if(); } else { error("#endif with no matching conditional"); } } return(phase4_flush_line()); } static PPTOKEN *phase4_do_if(void) { PPTLIST *expr; PPTOKEN *t; if (phase4_iftrue()) { t = phase4_rest_of_line(&expr); switch (t->type) { case PPT_EOF: error("EOF reading #if expression"); pptlist_free(expr); break; case PPT_NEWLINE: phase4_start_if(eval_if_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; if (! ifstack) { error("#elif with no matching conditional"); return(phase4_flush_line()); } t = phase4_rest_of_line(&expr); switch (t->type) { case PPT_EOF: error("EOF reading #elif expression"); pptlist_free(expr); break; case PPT_NEWLINE: switch (ifstack->state) { case IFS_FALSE: if (eval_if_expr(expr)) ifstack->state = IFS_TRUE; break; case IFS_TRUE: ifstack->state = IFS_OVER; break; case IFS_OVER: break; case IFS_ELSE_F: case IFS_ELSE_T: error("#elif after #else"); ifstack->state = IFS_ELSE_F; break; case IFS_NEST: break; default: panic(); break; } break; default: panic(); break; } return(t); } static PPTOKEN *phase4_do_include(void) { PPTOKEN *hn; PPTOKEN *t; while (1) { hn = phase3_get(1); if (hn->type != PPT_WHITESPACE) break; pptoken_free(hn); } if (hn->type == PPT_HEADERNAME) { t = phase3_get_skipws(); switch (t->type) { case PPT_EOF: error("EOF inside a #include line"); pptoken_free(hn); break; case PPT_NEWLINE: include_file(hn); break; default: error("#include with garbage after filename"); pptoken_free(hn); t = phase4_flush_line(); break; } } else { error("macro-expanded #include not yet supported"); t = phase4_flush_line(); } return(t); } static PPTOKEN *phase4_do_line(void) { return(phase4_flush_line()); } static PPTOKEN *phase4_do_error(void) { return(phase4_flush_line()); } static PPTOKEN *phase4_do_pragma(void) { return(phase4_flush_line()); } 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(0); 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(t); atbol = bol; return(t2); } if (bol && (t->type == PPT_PUNCT) && (t->detail.punct == PUNCT_SHARP)) { pptoken_free(t); t = phase3_get(0); 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()); } else { error("unrecognizable preprocessor #%.*s line",t->str.len,t->str.data); pptoken_free(t); return(phase4_flush_line()); } } } else { error("unrecognizable preprocessor #%.*s line",t->str.len,t->str.data); pptoken_free(t); return(phase4_flush_line()); } } t = macroexpander_input(&p4mx,t); if (t) return(t); } } static void process_sourcefile(SRCFILE *sf) { PPTOKEN *t; cursf = sf; phase2_init(); phase3_init(); phase4_init(); while (1) { t = phase4_get(); pptoken_dump(t); if (t->type == PPT_EOF) { if (! istack) break; fclose(cursf->f); free(cursf->name); free(cursf); continue; } pptoken_free(t); } } static void process_sourcefiles(void) { SRCFILE *sf; for (sf=srcfiles;sf;sf=sf->link) process_sourcefile(sf); } int main(int, char **); int main(int ac, char **av) { handleargs(ac,av); if (! srcfiles) { fprintf(stderr,"%s: no source files\n",__progname); exit(1); } errs = 0; open_sourcefiles(); if (errs) exit(1); process_sourcefiles(); if (errs) exit(1); return(0); } /* preprocessing-token header-name identifier pp-number character-constant string-literal punctuator each non-white-space character that cannot be one of the above header-name < h-char+ > " q-char+ " h-char = any char except newline and > q-char = any char except newline and " identifier [A-Za-z_][A-Za-z_0-9]* pp-number [.]?[0-9]([0-9a-zA-Z_]|[eEpP][-+]|[.])* character-constant ' c-char+ ' L' c-char+ ' c-char any member of the source character set except ', \, or newline escape-sequence escape-sequence simple-escape-sequence octal-escape-sequence hexadecimal-escape-sequence universal-character-name (unicode guff: \uxxxx or \Uxxxxxxxx) simple-escape-sequence \' \" \? \\ \a \b \f \n \r \t \v octal-escape-sequence \ [0-7]{1,3} hexadecimal-escape-sequence \x [0-9a-fA-F]+ string-literal " s-char* " L" s-char* " s-char any member of the source character set except ", \, or newline escape-sequence punctuator [ ] ( ) { } . -> ++ -- & * + - ~ ! / % << >> < > <= >= == != ^ | && || ? : ; ... = *= /= %= += -= <<= >>= &= ^= |= , # ## <: :> <% %> %: %:%: preprocessing-file group? group group-part+ group-part if-section control-line text-line # non-directive if-section if-group elif-groups? else-group? endif-line if-group # if constant-expr new-line group? # ifdef identifier new-line group? # ifndef identifier new-line group? elif-groups elif-group+ elif-group # elif constant-expr new-line group? else-group # else new-line group? endif-line # endif new-line control-line # include pp-tokens new-line # define identifier replacement-list new-line # define identifier lparen identifier-list? ) replacement-list new-line # define identifier lparen ... ) replacement-list new-line # define identifier lparen identifier-list , ... ) replacement-list new-line # undef identifier new-line # line pp-tokens new-line # error pp-tokens? new-line # pragma pp-tokens? new-line # new-line text-line pp-tokens? new-line non-directive pp-tokens new-line lparen a ( character not immediately preceded by white-space replacement-list pp-tokens? pp-tokens preprocessing-token+ new-line the new-line character [#2] A preprocessing directive consists of a sequence of preprocessing tokens that begins with a # preprocessing token that (at the start of translation phase 4) is either the first character in the source file (optionally after white space containing no new-line characters) or that follows white space containing at least one new-line character, and is ended by the next new-line character.139) A new-line character ends the preprocessing directive even if it occurs within what would otherwise be an invocation of a function-like macro. [#3] A text line shall not begin with a # preprocessing token. A non-directive shall not begin with any of the directive names appearing in the syntax. [#4] When in a group that is skipped (6.10.1), the directive syntax is relaxed to allow any sequence of preprocessing tokens to occur between the directive name and the following new-line character. [#5] The only white-space characters that shall appear between preprocessing tokens within a preprocessing directive (from just after the introducing # preprocessing token through just before the terminating new-line character) are space and horizontal-tab (including spaces that have replaced comments or possibly other white-space characters in translation phase 3). constant-expr conditional-expr conditional-expr logical-OR-expr logical-OR-expr ? expression : conditional-expr logical-OR-expr logical-AND-expr logical-OR-expr || logical-AND-expr logical-AND-expr inclusive-OR-expr logical-AND-expr && inclusive-OR-expr inclusive-OR-expr exclusive-OR-expr inclusive-OR-expr | exclusive-OR-expr exclusive-OR-expr AND-expr exclusive-OR-expr ^ AND-expr AND-expr equality-expr AND-expr & equality-expr equality-expr relational-expr equality-expr == relational-expr equality-expr != relational-expr relational-expr shift-expr relational-expr < shift-expr relational-expr > shift-expr relational-expr <= shift-expr relational-expr >= shift-expr shift-expr additive-expr shift-expr << additive-expr shift-expr >> additive-expr additive-expr multiplicative-expr additive-expr + multiplicative-expr additive-expr - multiplicative-expr multiplicative-expr cast-expr multiplicative-expr * cast-expr multiplicative-expr / cast-expr multiplicative-expr % cast-expr cast-expr unary-expr #### ( type-name ) cast-expr unary-expr postfix-expr #### ++ unary-expr #### -- unary-expr unary-operator cast-expr #### sizeof unary-expr #### sizeof ( type-name ) unary-operator & * + - ~ ! defined postfix-expr primary-expr %%%% postfix-expr [ expression ] #### postfix-expr ( argument-expr-list-opt ) %%%% postfix-expr . identifier %%%% postfix-expr -> identifier #### postfix-expr ++ #### postfix-expr -- %%%% ( type-name ) { initializer-list } %%%% ( type-name ) { initializer-list , } %%%%argument-expr-list %%%% assignment-expr %%%% argument-expr-list , assignment-expr assignment-expr conditional-expr #### unary-expr assignment-operator assignment-expr assignment-operator = *= /= %= += -= <<= >>= &= ^= |= primary-expr identifier constant %%%% string-literal ( expression ) expression assignment-expr #### expression , assignment-expr [#3] Constant expressions shall not contain assignment, increment, decrement, function-call, or comma operators, except when they are contained within a subexpression that is not evaluated. [#6] An integer constant expression96) shall have integer type and shall only have operands that are integer constants, enumeration constants, character constants, sizeof expressions whose results are integer constants, and floating constants that are the immediate operands of casts. Cast operators in an integer constant expression shall only convert arithmetic types to integer types, except as part of an operand to the sizeof operator. [#1] The expression that controls conditional inclusion shall be an integer constant expression except that: it shall not contain a cast; identifiers (including those lexically identical to keywords) are interpreted as described below;140) and it may contain unary operator expressions of the form defined identifier or defined ( identifier ) which evaluate to 1 if the identifier is currently defined as a macro name (that is, if it is predefined or if it has been the subject of a #define preprocessing directive without an intervening #undef directive with the same subject identifier), 0 if it is not. 140Because the controlling constant expression is evaluated during translation phase 4, all identifiers either are or are not macro names -- there simply are no keywords, enumeration constants, etc. */