// Inferno utils/cc/macbody // http://code.google.com/p/inferno-os/source/browse/utils/cc/macbody // // Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. // Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) // Portions Copyright © 1997-1999 Vita Nuova Limited // Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) // Portions Copyright © 2004,2006 Bruce Ellis // Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) // Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others // Portions Copyright © 2009 The Go Authors. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #define VARMAC 0x80 int32 getnsn(void) { int32 n; int c; c = getnsc(); if(c < '0' || c > '9') return -1; n = 0; while(c >= '0' && c <= '9') { n = n*10 + c-'0'; c = getc(); } unget(c); return n; } Sym* getsym(void) { int c; char *cp; c = getnsc(); if(!isalpha(c) && c != '_' && c < 0x80) { unget(c); return S; } for(cp = symb;;) { if(cp <= symb+NSYMB-4) *cp++ = c; c = getc(); if(isalnum(c) || c == '_' || c >= 0x80) continue; unget(c); break; } *cp = 0; if(cp > symb+NSYMB-4) yyerror("symbol too large: %s", symb); return lookup(); } Sym* getsymdots(int *dots) { int c; Sym *s; s = getsym(); if(s != S) return s; c = getnsc(); if(c != '.'){ unget(c); return S; } if(getc() != '.' || getc() != '.') yyerror("bad dots in macro"); *dots = 1; return slookup("__VA_ARGS__"); } int getcom(void) { int c; for(;;) { c = getnsc(); if(c != '/') break; c = getc(); if(c == '/') { while(c != '\n') c = getc(); break; } if(c != '*') break; c = getc(); for(;;) { if(c == '*') { c = getc(); if(c != '/') continue; c = getc(); break; } if(c == '\n') { yyerror("comment across newline"); break; } c = getc(); } if(c == '\n') break; } return c; } void dodefine(char *cp) { Sym *s; char *p; int32 l; strcpy(symb, cp); p = strchr(symb, '='); if(p) { *p++ = 0; s = lookup(); l = strlen(p) + 2; /* +1 null, +1 nargs */ s->macro = alloc(l); strcpy(s->macro+1, p); } else { s = lookup(); s->macro = "\0001"; /* \000 is nargs */ } if(debug['m']) print("#define (-D) %s %s\n", s->name, s->macro+1); } struct { char *macname; void (*macf)(void); } mactab[] = { "ifdef", 0, /* macif(0) */ "ifndef", 0, /* macif(1) */ "else", 0, /* macif(2) */ "line", maclin, "define", macdef, "include", macinc, "undef", macund, "pragma", macprag, "endif", macend, 0 }; void domacro(void) { int i; Sym *s; s = getsym(); if(s == S) s = slookup("endif"); for(i=0; mactab[i].macname; i++) if(strcmp(s->name, mactab[i].macname) == 0) { if(mactab[i].macf) (*mactab[i].macf)(); else macif(i); return; } yyerror("unknown #: %s", s->name); macend(); } void macund(void) { Sym *s; s = getsym(); macend(); if(s == S) { yyerror("syntax in #undef"); return; } s->macro = 0; } #define NARG 25 void macdef(void) { Sym *s, *a; char *args[NARG], *np, *base; int n, i, c, len, dots; int ischr; s = getsym(); if(s == S) goto bad; if(s->macro) yyerror("macro redefined: %s", s->name); c = getc(); n = -1; dots = 0; if(c == '(') { n++; c = getnsc(); if(c != ')') { unget(c); for(;;) { a = getsymdots(&dots); if(a == S) goto bad; if(n >= NARG) { yyerror("too many arguments in #define: %s", s->name); goto bad; } args[n++] = a->name; c = getnsc(); if(c == ')') break; if(c != ',' || dots) goto bad; } } c = getc(); } if(isspace(c)) if(c != '\n') c = getnsc(); base = hunk; len = 1; ischr = 0; for(;;) { if(isalpha(c) || c == '_') { np = symb; *np++ = c; c = getc(); while(isalnum(c) || c == '_') { *np++ = c; c = getc(); } *np = 0; for(i=0; i= n) { i = strlen(symb); base = allocn(base, len, i); memcpy(base+len, symb, i); len += i; continue; } base = allocn(base, len, 2); base[len++] = '#'; base[len++] = 'a' + i; continue; } if(ischr){ if(c == '\\'){ base = allocn(base, len, 1); base[len++] = c; c = getc(); }else if(c == ischr) ischr = 0; }else{ if(c == '"' || c == '\''){ base = allocn(base, len, 1); base[len++] = c; ischr = c; c = getc(); continue; } if(c == '/') { c = getc(); if(c == '/'){ c = getc(); for(;;) { if(c == '\n') break; c = getc(); } continue; } if(c == '*'){ c = getc(); for(;;) { if(c == '*') { c = getc(); if(c != '/') continue; c = getc(); break; } if(c == '\n') { yyerror("comment and newline in define: %s", s->name); break; } c = getc(); } continue; } base = allocn(base, len, 1); base[len++] = '/'; continue; } } if(c == '\\') { c = getc(); if(c == '\n') { c = getc(); continue; } else if(c == '\r') { c = getc(); if(c == '\n') { c = getc(); continue; } } base = allocn(base, len, 1); base[len++] = '\\'; continue; } if(c == '\n') break; if(c == '#') if(n > 0) { base = allocn(base, len, 1); base[len++] = c; } base = allocn(base, len, 1); base[len++] = c; c = ((--fi.c < 0)? filbuf(): (*fi.p++ & 0xff)); if(c == '\n') lineno++; if(c == -1) { yyerror("eof in a macro: %s", s->name); break; } } do { base = allocn(base, len, 1); base[len++] = 0; } while(len & 3); *base = n+1; if(dots) *base |= VARMAC; s->macro = base; if(debug['m']) print("#define %s %s\n", s->name, s->macro+1); return; bad: if(s == S) yyerror("syntax in #define"); else yyerror("syntax in #define: %s", s->name); macend(); } void macexpand(Sym *s, char *b) { char buf[2000]; int n, l, c, nargs; char *arg[NARG], *cp, *ob, *ecp, dots; ob = b; if(*s->macro == 0) { strcpy(b, s->macro+1); if(debug['m']) print("#expand %s %s\n", s->name, ob); return; } nargs = (char)(*s->macro & ~VARMAC) - 1; dots = *s->macro & VARMAC; c = getnsc(); if(c != '(') goto bad; n = 0; c = getc(); if(c != ')') { unget(c); l = 0; cp = buf; ecp = cp + sizeof(buf)-4; arg[n++] = cp; for(;;) { if(cp >= ecp) goto toobig; c = getc(); if(c == '"') for(;;) { if(cp >= ecp) goto toobig; *cp++ = c; c = getc(); if(c == '\\') { *cp++ = c; c = getc(); continue; } if(c == '\n') goto bad; if(c == '"') break; } if(c == '\'') for(;;) { if(cp >= ecp) goto toobig; *cp++ = c; c = getc(); if(c == '\\') { *cp++ = c; c = getc(); continue; } if(c == '\n') goto bad; if(c == '\'') break; } if(c == '/') { c = getc(); switch(c) { case '*': for(;;) { c = getc(); if(c == '*') { c = getc(); if(c == '/') break; } } *cp++ = ' '; continue; case '/': while((c = getc()) != '\n') ; break; default: unget(c); c = '/'; } } if(l == 0) { if(c == ',') { if(n == nargs && dots) { *cp++ = ','; continue; } *cp++ = 0; arg[n++] = cp; if(n > nargs) break; continue; } if(c == ')') break; } if(c == '\n') c = ' '; *cp++ = c; if(c == '(') l++; if(c == ')') l--; } *cp = 0; } if(n != nargs) { yyerror("argument mismatch expanding: %s", s->name); *b = 0; return; } cp = s->macro+1; for(;;) { c = *cp++; if(c == '\n') c = ' '; if(c != '#') { *b++ = c; if(c == 0) break; continue; } c = *cp++; if(c == 0) goto bad; if(c == '#') { *b++ = c; continue; } c -= 'a'; if(c < 0 || c >= n) continue; strcpy(b, arg[c]); b += strlen(arg[c]); } *b = 0; if(debug['m']) print("#expand %s %s\n", s->name, ob); return; bad: yyerror("syntax in macro expansion: %s", s->name); *b = 0; return; toobig: yyerror("too much text in macro expansion: %s", s->name); *b = 0; } void macinc(void) { int c0, c, i, f; char str[STRINGSZ], *hp; c0 = getnsc(); if(c0 != '"') { c = c0; if(c0 != '<') goto bad; c0 = '>'; } for(hp = str;;) { c = getc(); if(c == c0) break; if(c == '\n') goto bad; *hp++ = c; } *hp = 0; c = getcom(); if(c != '\n') goto bad; f = -1; for(i=0; i') continue; strcpy(symb, include[i]); strcat(symb, "/"); if(strcmp(symb, "./") == 0) symb[0] = 0; strcat(symb, str); f = open(symb, OREAD); if(f >= 0) break; } if(f < 0) strcpy(symb, str); c = strlen(symb) + 1; hp = alloc(c); memcpy(hp, symb, c); newio(); pushio(); newfile(hp, f); return; bad: unget(c); yyerror("syntax in #include"); macend(); } void maclin(void) { char *cp; int c; int32 n; n = getnsn(); c = getc(); if(n < 0) goto bad; for(;;) { if(c == ' ' || c == '\t') { c = getc(); continue; } if(c == '"') break; if(c == '\n') { strcpy(symb, ""); goto nn; } goto bad; } cp = symb; for(;;) { c = getc(); if(c == '"') break; *cp++ = c; } *cp = 0; c = getcom(); if(c != '\n') goto bad; nn: c = strlen(symb) + 1; cp = alloc(c); memcpy(cp, symb, c); linehist(cp, n); return; bad: unget(c); yyerror("syntax in #line"); macend(); } void macif(int f) { int c, l, bol; Sym *s; if(f == 2) goto skip; s = getsym(); if(s == S) goto bad; if(getcom() != '\n') goto bad; if((s->macro != 0) ^ f) return; skip: bol = 1; l = 0; for(;;) { c = getc(); if(c != '#') { if(!isspace(c)) bol = 0; if(c == '\n') bol = 1; continue; } if(!bol) continue; s = getsym(); if(s == S) continue; if(strcmp(s->name, "endif") == 0) { if(l) { l--; continue; } macend(); return; } if(strcmp(s->name, "ifdef") == 0 || strcmp(s->name, "ifndef") == 0) { l++; continue; } if(l == 0 && f != 2 && strcmp(s->name, "else") == 0) { macend(); return; } } bad: yyerror("syntax in #if(n)def"); macend(); } void macprag(void) { Sym *s; int c0, c; char *hp; Hist *h; s = getsym(); if(s && strcmp(s->name, "lib") == 0) goto praglib; if(s && strcmp(s->name, "pack") == 0) { pragpack(); return; } if(s && strcmp(s->name, "fpround") == 0) { pragfpround(); return; } if(s && strcmp(s->name, "textflag") == 0) { pragtextflag(); return; } if(s && strcmp(s->name, "varargck") == 0) { pragvararg(); return; } if(s && strcmp(s->name, "incomplete") == 0) { pragincomplete(); return; } if(s && strcmp(s->name, "dynld") == 0) { pragdynld(); return; } while(getnsc() != '\n') ; return; praglib: c0 = getnsc(); if(c0 != '"') { c = c0; if(c0 != '<') goto bad; c0 = '>'; } for(hp = symb;;) { c = getc(); if(c == c0) break; if(c == '\n') goto bad; *hp++ = c; } *hp = 0; c = getcom(); if(c != '\n') goto bad; /* * put pragma-line in as a funny history */ c = strlen(symb) + 1; hp = alloc(c); memcpy(hp, symb, c); h = alloc(sizeof(Hist)); h->name = hp; h->line = lineno; h->offset = -1; h->link = H; if(ehist == H) { hist = h; ehist = h; return; } ehist->link = h; ehist = h; return; bad: unget(c); yyerror("syntax in #pragma lib"); macend(); } void macend(void) { int c; for(;;) { c = getnsc(); if(c < 0 || c == '\n') return; } } void linehist(char *f, int offset) { Hist *h; /* * overwrite the last #line directive if * no alloc has happened since the last one */ if(newflag == 0 && ehist != H && offset != 0 && ehist->offset != 0) if(f && ehist->name && strcmp(f, ehist->name) == 0) { ehist->line = lineno; ehist->offset = offset; return; } if(debug['f']) if(f) { if(offset) print("%4ld: %s (#line %d)\n", lineno, f, offset); else print("%4ld: %s\n", lineno, f); } else print("%4ld: \n", lineno); newflag = 0; h = alloc(sizeof(Hist)); h->name = f; h->line = lineno; h->offset = offset; h->link = H; if(ehist == H) { hist = h; ehist = h; return; } ehist->link = h; ehist = h; }