| // Inferno utils/cc/lex.c |
| // http://code.google.com/p/inferno-os/source/browse/utils/cc/lex.c |
| // |
| // 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. |
| |
| #include <u.h> |
| #include "cc.h" |
| #include "y.tab.h" |
| #include "../ld/textflag.h" |
| |
| #ifndef CPP |
| #define CPP "cpp" |
| #endif |
| |
| int |
| systemtype(int sys) |
| { |
| #ifdef _WIN32 |
| return sys&Windows; |
| #else |
| return sys&Plan9; |
| #endif |
| } |
| |
| int |
| pathchar(void) |
| { |
| return '/'; |
| } |
| |
| /* |
| * known debug flags |
| * -a acid declaration output |
| * -A !B |
| * -B non ANSI |
| * -d print declarations |
| * -D name define |
| * -F format specification check |
| * -G print pgen stuff |
| * -g print cgen trees |
| * -i print initialization |
| * -I path include |
| * -l generate little-endian code |
| * -L print every NAME symbol |
| * -M constant multiplication |
| * -m print add/sub/mul trees |
| * -n print acid or godefs to file (%.c=%.acid) (with -a or -aa) |
| * -o file output file |
| * -p use standard cpp ANSI preprocessor (not on windows) |
| * -p something with peepholes |
| * -q print equivalent Go code for variables and types (lower-case identifiers) |
| * -Q print equivalent Go code for variables and types (upper-case identifiers) |
| * -r print registerization |
| * -s print structure offsets (with -a or -aa) |
| * -S print assembly |
| * -t print type trees |
| * -V enable void* conversion warnings |
| * -v verbose printing |
| * -w print warnings |
| * -X abort on error |
| * -. Inhibit search for includes in source directory |
| */ |
| |
| void |
| usage(void) |
| { |
| print("usage: %cc [options] file.c...\n", thechar); |
| flagprint(1); |
| errorexit(); |
| } |
| |
| void |
| dospim(void) |
| { |
| thechar = '0'; |
| thestring = "spim"; |
| } |
| |
| char **defs; |
| int ndef; |
| |
| void |
| dodef(char *p) |
| { |
| if(ndef%8 == 0) |
| defs = allocn(defs, ndef*sizeof(char *), |
| 8*sizeof(char *)); |
| defs[ndef++] = p; |
| dodefine(p); |
| } |
| |
| void |
| main(int argc, char *argv[]) |
| { |
| int c; |
| char *p; |
| |
| // Allow GOARCH=thestring or GOARCH=thestringsuffix, |
| // but not other values. |
| p = getgoarch(); |
| if(strncmp(p, thestring, strlen(thestring)) != 0) |
| sysfatal("cannot use %cc with GOARCH=%s", thechar, p); |
| if(strcmp(p, "amd64p32") == 0) // must be before cinit |
| ewidth[TIND] = 4; |
| |
| nacl = strcmp(getgoos(), "nacl") == 0; |
| if(nacl) |
| flag_largemodel = 1; |
| |
| quotefmtinstall(); // before cinit, which overrides %Q |
| |
| linkarchinit(); |
| ctxt = linknew(thelinkarch); |
| ctxt->diag = yyerror; |
| ctxt->bso = &bstdout; |
| Binit(&bstdout, 1, OWRITE); |
| |
| ensuresymb(NSYMB); |
| memset(debug, 0, sizeof(debug)); |
| tinit(); |
| cinit(); |
| ginit(); |
| arginit(); |
| |
| fmtstrinit(&pragcgobuf); |
| |
| tufield = simplet((1L<<tfield->etype) | BUNSIGNED); |
| ndef = 0; |
| defs = nil; |
| outfile = 0; |
| setinclude("."); |
| |
| flagcount("+", "pass -+ to preprocessor", &debug['+']); |
| flagcount(".", "pass -. to preprocessor", &debug['.']); |
| flagcount("<", "debug shift", &debug['<']); |
| flagcount("A", "debug alignment", &debug['A']); |
| flagcount("B", "allow pre-ANSI code", &debug['B']); |
| if(thechar == '5') |
| flagcount("C", "debug constant propagation", &debug['C']); |
| flagfn1("D", "name[=value]: add #define", dodef); |
| flagcount("F", "enable print format checks", &debug['F']); |
| if(thechar == '5') |
| flagcount("H", "debug shift propagation", &debug['H']); |
| flagfn1("I", "dir: add dir to include path", setinclude); |
| flagcount("L", "debug lexer", &debug['L']); |
| flagcount("M", "debug move generation", &debug['M']); |
| flagcount("N", "disable optimizations", &debug['N']); |
| flagcount("P", "debug peephole optimizer", &debug['P']); |
| flagcount("Q", "print exported Go definitions", &debug['Q']); |
| flagcount("R", "debug register optimizer", &debug['R']); |
| flagcount("S", "print assembly", &debug['S']); |
| flagcount("T", "enable type signatures", &debug['T']); |
| flagcount("V", "enable pointer type checks", &debug['V']); |
| flagcount("W", "debug switch generation", &debug['W']); |
| flagcount("X", "abort on error", &debug['X']); |
| flagcount("Y", "debug index generation", &debug['Y']); |
| flagcount("Z", "skip code generation", &debug['Z']); |
| flagcount("a", "print acid definitions", &debug['a']); |
| flagcount("c", "debug constant evaluation", &debug['c']); |
| flagcount("d", "debug declarations", &debug['d']); |
| flagcount("e", "debug macro expansion", &debug['e']); |
| flagcount("f", "debug pragmas", &debug['f']); |
| flagcount("g", "debug code generation", &debug['g']); |
| flagcount("i", "debug initialization", &debug['i']); |
| if(thechar == 'v') |
| flagfn0("l", "little-endian mips mode", dospim); |
| flagcount("m", "debug multiplication", &debug['m']); |
| flagcount("n", "print acid/Go to file, not stdout", &debug['n']); |
| flagstr("o", "file: set output file", &outfile); |
| flagcount("p", "invoke C preprocessor", &debug['p']); |
| flagcount("q", "print Go definitions", &debug['q']); |
| flagcount("s", "print #define assembly offsets", &debug['s']); |
| flagcount("t", "debug code generation", &debug['t']); |
| flagstr("trimpath", "prefix: remove prefix from recorded source file paths", &ctxt->trimpath); |
| flagcount("w", "enable warnings", &debug['w']); |
| flagcount("v", "increase debug verbosity", &debug['v']); |
| if(thechar == '6') |
| flagcount("largemodel", "generate code that assumes a large memory model", &flag_largemodel); |
| |
| flagparse(&argc, &argv, usage); |
| ctxt->debugasm = debug['S']; |
| ctxt->debugvlog = debug['v']; |
| |
| if(argc < 1 && outfile == 0) |
| usage(); |
| |
| if(argc > 1){ |
| print("can't compile multiple files\n"); |
| errorexit(); |
| } |
| |
| if(argc == 0) |
| c = compile("stdin", defs, ndef); |
| else |
| c = compile(argv[0], defs, ndef); |
| |
| Bflush(&bstdout); |
| if(c) |
| errorexit(); |
| exits(0); |
| } |
| |
| int |
| compile(char *file, char **defs, int ndef) |
| { |
| char *ofile; |
| char *p, **av, opt[256]; |
| int i, c, fd[2]; |
| static int first = 1; |
| |
| ofile = alloc(strlen(file)+10); |
| strcpy(ofile, file); |
| p = utfrrune(ofile, pathchar()); |
| if(p) { |
| *p++ = 0; |
| if(!debug['.']) |
| include[0] = strdup(ofile); |
| } else |
| p = ofile; |
| |
| if(outfile == 0) { |
| outfile = p; |
| if(outfile) { |
| if(p = utfrrune(outfile, '.')) |
| if(p[1] == 'c' && p[2] == 0) |
| p[0] = 0; |
| p = utfrune(outfile, 0); |
| if(debug['a'] && debug['n']) |
| strcat(p, ".acid"); |
| else if((debug['q'] || debug['Q']) && debug['n']) |
| strcat(p, ".go"); |
| else { |
| p[0] = '.'; |
| p[1] = thechar; |
| p[2] = 0; |
| } |
| } else |
| outfile = "/dev/null"; |
| } |
| |
| if (first) |
| Binit(&diagbuf, 1, OWRITE); |
| /* |
| * if we're writing acid to standard output, don't keep scratching |
| * outbuf. |
| */ |
| if((debug['a'] || debug['q'] || debug['Q']) && !debug['n']) { |
| if (first) { |
| outfile = 0; |
| Binit(&outbuf, dup(1, -1), OWRITE); |
| dup(2, 1); |
| } |
| } else { |
| c = create(outfile, OWRITE, 0664); |
| if(c < 0) { |
| diag(Z, "cannot open %s - %r", outfile); |
| outfile = 0; |
| errorexit(); |
| } |
| Binit(&outbuf, c, OWRITE); |
| outfile = strdup(outfile); |
| } |
| newio(); |
| first = 0; |
| |
| /* Use an ANSI preprocessor */ |
| if(debug['p']) { |
| if(systemtype(Windows)) { |
| diag(Z, "-p option not supported on windows"); |
| errorexit(); |
| } |
| if(access(file, AREAD) < 0) { |
| diag(Z, "%s does not exist", file); |
| errorexit(); |
| } |
| if(pipe(fd) < 0) { |
| diag(Z, "pipe failed"); |
| errorexit(); |
| } |
| switch(fork()) { |
| case -1: |
| diag(Z, "fork failed"); |
| errorexit(); |
| case 0: |
| close(fd[0]); |
| dup(fd[1], 1); |
| close(fd[1]); |
| av = alloc((ndef+ninclude+5)*sizeof(char *)); |
| av[0] = CPP; |
| i = 1; |
| if(debug['.']){ |
| sprint(opt, "-."); |
| av[i++] = strdup(opt); |
| } |
| if(debug['+']) { |
| sprint(opt, "-+"); |
| av[i++] = strdup(opt); |
| } |
| for(c = 0; c < ndef; c++) |
| av[i++] = smprint("-D%s", defs[c]); |
| for(c = 0; c < ninclude; c++) |
| av[i++] = smprint("-I%s", include[c]); |
| if(strcmp(file, "stdin") != 0) |
| av[i++] = file; |
| av[i] = 0; |
| if(debug['p'] > 1) { |
| for(c = 0; c < i; c++) |
| fprint(2, "%s ", av[c]); |
| fprint(2, "\n"); |
| } |
| exec(av[0], av); |
| fprint(2, "can't exec C preprocessor %s: %r\n", CPP); |
| errorexit(); |
| default: |
| close(fd[1]); |
| newfile(file, fd[0]); |
| break; |
| } |
| } else { |
| if(strcmp(file, "stdin") == 0) |
| newfile(file, 0); |
| else |
| newfile(file, -1); |
| } |
| yyparse(); |
| if(!debug['a'] && !debug['q'] && !debug['Q']) |
| gclean(); |
| return nerrors; |
| } |
| |
| void |
| errorexit(void) |
| { |
| Bflush(&bstdout); |
| if(outfile) |
| remove(outfile); |
| exits("error"); |
| } |
| |
| void |
| pushio(void) |
| { |
| Io *i; |
| |
| i = iostack; |
| if(i == I) { |
| yyerror("botch in pushio"); |
| errorexit(); |
| } |
| i->p = fi.p; |
| i->c = fi.c; |
| } |
| |
| void |
| newio(void) |
| { |
| Io *i; |
| static int pushdepth = 0; |
| |
| i = iofree; |
| if(i == I) { |
| pushdepth++; |
| if(pushdepth > 1000) { |
| yyerror("macro/io expansion too deep"); |
| errorexit(); |
| } |
| i = alloc(sizeof(*i)); |
| } else |
| iofree = i->link; |
| i->c = 0; |
| i->f = -1; |
| ionext = i; |
| } |
| |
| void |
| newfile(char *s, int f) |
| { |
| Io *i; |
| |
| if(debug['e']) |
| print("%L: %s\n", lineno, s); |
| |
| i = ionext; |
| i->link = iostack; |
| iostack = i; |
| i->f = f; |
| if(f < 0) |
| i->f = open(s, 0); |
| if(i->f < 0) { |
| yyerror("%cc: %r: %s", thechar, s); |
| errorexit(); |
| } |
| fi.c = 0; |
| linklinehist(ctxt, lineno, s, 0); |
| } |
| |
| Sym* |
| slookup(char *s) |
| { |
| ensuresymb(strlen(s)); |
| strcpy(symb, s); |
| return lookup(); |
| } |
| |
| Sym* |
| lookup(void) |
| { |
| Sym *s; |
| uint32 h; |
| char *p; |
| int c, n; |
| char *r, *w; |
| |
| if((uchar)symb[0] == 0xc2 && (uchar)symb[1] == 0xb7) { |
| // turn leading · into ""· |
| h = strlen(symb); |
| ensuresymb(h+2); |
| memmove(symb+2, symb, h+1); |
| symb[0] = '"'; |
| symb[1] = '"'; |
| } |
| |
| for(r=w=symb; *r; r++) { |
| // turn · (U+00B7) into . |
| // turn ∕ (U+2215) into / |
| if((uchar)*r == 0xc2 && (uchar)*(r+1) == 0xb7) { |
| *w++ = '.'; |
| r++; |
| }else if((uchar)*r == 0xe2 && (uchar)*(r+1) == 0x88 && (uchar)*(r+2) == 0x95) { |
| *w++ = '/'; |
| r++; |
| r++; |
| }else |
| *w++ = *r; |
| } |
| *w = '\0'; |
| |
| h = 0; |
| for(p=symb; *p;) { |
| h = h * 3; |
| h += *p++; |
| } |
| n = (p - symb) + 1; |
| h &= 0xffffff; |
| h %= NHASH; |
| c = symb[0]; |
| for(s = hash[h]; s != S; s = s->link) { |
| if(s->name[0] != c) |
| continue; |
| if(strcmp(s->name, symb) == 0) |
| return s; |
| } |
| s = alloc(sizeof(*s)); |
| s->name = alloc(n); |
| memmove(s->name, symb, n); |
| s->link = hash[h]; |
| hash[h] = s; |
| syminit(s); |
| |
| return s; |
| } |
| |
| void |
| syminit(Sym *s) |
| { |
| s->lexical = LNAME; |
| s->block = 0; |
| s->offset = 0; |
| s->type = T; |
| s->suetag = T; |
| s->class = CXXX; |
| s->aused = 0; |
| s->sig = SIGNONE; |
| } |
| |
| #define EOF (-1) |
| #define IGN (-2) |
| #define ESC (1<<20) |
| #define GETC() ((--fi.c < 0)? filbuf(): (*fi.p++ & 0xff)) |
| |
| enum |
| { |
| Numdec = 1<<0, |
| Numlong = 1<<1, |
| Numuns = 1<<2, |
| Numvlong = 1<<3, |
| Numflt = 1<<4, |
| }; |
| |
| int32 |
| yylex(void) |
| { |
| vlong vv; |
| int32 c, c1, t; |
| char *cp; |
| Rune rune; |
| Sym *s; |
| |
| if(peekc != IGN) { |
| c = peekc; |
| peekc = IGN; |
| goto l1; |
| } |
| l0: |
| c = GETC(); |
| |
| l1: |
| if(c >= Runeself) { |
| /* |
| * extension -- |
| * all multibyte runes are alpha |
| */ |
| cp = symb; |
| goto talph; |
| } |
| if(isspace(c)) { |
| if(c == '\n') |
| lineno++; |
| goto l0; |
| } |
| if(isalpha(c)) { |
| cp = symb; |
| if(c != 'L') |
| goto talph; |
| *cp++ = c; |
| c = GETC(); |
| if(c == '\'') { |
| /* L'x' */ |
| c = escchar('\'', 1, 0); |
| if(c == EOF) |
| c = '\''; |
| c1 = escchar('\'', 1, 0); |
| if(c1 != EOF) { |
| yyerror("missing '"); |
| peekc = c1; |
| } |
| yylval.vval = convvtox(c, TRUNE); |
| return LUCONST; |
| } |
| if(c == '"') { |
| goto caselq; |
| } |
| goto talph; |
| } |
| if(isdigit(c)) |
| goto tnum; |
| switch(c) |
| { |
| |
| case EOF: |
| peekc = EOF; |
| return -1; |
| |
| case '_': |
| cp = symb; |
| goto talph; |
| |
| case '#': |
| domacro(); |
| goto l0; |
| |
| case '.': |
| c1 = GETC(); |
| if(isdigit(c1)) { |
| cp = symb; |
| *cp++ = c; |
| c = c1; |
| c1 = 0; |
| goto casedot; |
| } |
| break; |
| |
| case '"': |
| strcpy(symb, "\"<string>\""); |
| cp = alloc(0); |
| c1 = 0; |
| |
| /* "..." */ |
| for(;;) { |
| c = escchar('"', 0, 1); |
| if(c == EOF) |
| break; |
| if(c & ESC) { |
| cp = allocn(cp, c1, 1); |
| cp[c1++] = c; |
| } else { |
| rune = c; |
| c = runelen(rune); |
| cp = allocn(cp, c1, c); |
| runetochar(cp+c1, &rune); |
| c1 += c; |
| } |
| } |
| yylval.sval.l = c1; |
| do { |
| cp = allocn(cp, c1, 1); |
| cp[c1++] = 0; |
| } while(c1 & MAXALIGN); |
| yylval.sval.s = cp; |
| return LSTRING; |
| |
| caselq: |
| /* L"..." */ |
| strcpy(symb, "\"L<string>\""); |
| cp = alloc(0); |
| c1 = 0; |
| for(;;) { |
| c = escchar('"', 1, 0); |
| if(c == EOF) |
| break; |
| cp = allocn(cp, c1, sizeof(TRune)); |
| *(TRune*)(cp + c1) = c; |
| c1 += sizeof(TRune); |
| } |
| yylval.sval.l = c1; |
| do { |
| cp = allocn(cp, c1, sizeof(TRune)); |
| *(TRune*)(cp + c1) = 0; |
| c1 += sizeof(TRune); |
| } while(c1 & MAXALIGN); |
| yylval.sval.s = cp; |
| return LLSTRING; |
| |
| case '\'': |
| /* '.' */ |
| c = escchar('\'', 0, 0); |
| if(c == EOF) |
| c = '\''; |
| c1 = escchar('\'', 0, 0); |
| if(c1 != EOF) { |
| yyerror("missing '"); |
| peekc = c1; |
| } |
| vv = c; |
| yylval.vval = convvtox(vv, TUCHAR); |
| if(yylval.vval != vv) |
| yyerror("overflow in character constant: 0x%x", c); |
| else |
| if(c & 0x80){ |
| nearln = lineno; |
| warn(Z, "sign-extended character constant"); |
| } |
| yylval.vval = convvtox(vv, TCHAR); |
| return LCONST; |
| |
| case '/': |
| c1 = GETC(); |
| if(c1 == '*') { |
| for(;;) { |
| c = getr(); |
| while(c == '*') { |
| c = getr(); |
| if(c == '/') |
| goto l0; |
| } |
| if(c == EOF) { |
| yyerror("eof in comment"); |
| errorexit(); |
| } |
| } |
| } |
| if(c1 == '/') { |
| for(;;) { |
| c = getr(); |
| if(c == '\n') |
| goto l0; |
| if(c == EOF) { |
| yyerror("eof in comment"); |
| errorexit(); |
| } |
| } |
| } |
| if(c1 == '=') |
| return LDVE; |
| break; |
| |
| case '*': |
| c1 = GETC(); |
| if(c1 == '=') |
| return LMLE; |
| break; |
| |
| case '%': |
| c1 = GETC(); |
| if(c1 == '=') |
| return LMDE; |
| break; |
| |
| case '+': |
| c1 = GETC(); |
| if(c1 == '+') |
| return LPP; |
| if(c1 == '=') |
| return LPE; |
| break; |
| |
| case '-': |
| c1 = GETC(); |
| if(c1 == '-') |
| return LMM; |
| if(c1 == '=') |
| return LME; |
| if(c1 == '>') |
| return LMG; |
| break; |
| |
| case '>': |
| c1 = GETC(); |
| if(c1 == '>') { |
| c = LRSH; |
| c1 = GETC(); |
| if(c1 == '=') |
| return LRSHE; |
| break; |
| } |
| if(c1 == '=') |
| return LGE; |
| break; |
| |
| case '<': |
| c1 = GETC(); |
| if(c1 == '<') { |
| c = LLSH; |
| c1 = GETC(); |
| if(c1 == '=') |
| return LLSHE; |
| break; |
| } |
| if(c1 == '=') |
| return LLE; |
| break; |
| |
| case '=': |
| c1 = GETC(); |
| if(c1 == '=') |
| return LEQ; |
| break; |
| |
| case '!': |
| c1 = GETC(); |
| if(c1 == '=') |
| return LNE; |
| break; |
| |
| case '&': |
| c1 = GETC(); |
| if(c1 == '&') |
| return LANDAND; |
| if(c1 == '=') |
| return LANDE; |
| break; |
| |
| case '|': |
| c1 = GETC(); |
| if(c1 == '|') |
| return LOROR; |
| if(c1 == '=') |
| return LORE; |
| break; |
| |
| case '^': |
| c1 = GETC(); |
| if(c1 == '=') |
| return LXORE; |
| break; |
| |
| default: |
| return c; |
| } |
| peekc = c1; |
| return c; |
| |
| talph: |
| /* |
| * cp is set to symb and some |
| * prefix has been stored |
| */ |
| for(;;) { |
| if(c >= Runeself) { |
| for(c1=0;;) { |
| cp[c1++] = c; |
| if(fullrune(cp, c1)) |
| break; |
| c = GETC(); |
| } |
| cp += c1; |
| c = GETC(); |
| continue; |
| } |
| if(!isalnum(c) && c != '_') |
| break; |
| *cp++ = c; |
| c = GETC(); |
| } |
| *cp = 0; |
| if(debug['L']) |
| print("%L: %s\n", lineno, symb); |
| peekc = c; |
| s = lookup(); |
| if(s->macro) { |
| newio(); |
| cp = ionext->b; |
| macexpand(s, cp); |
| pushio(); |
| ionext->link = iostack; |
| iostack = ionext; |
| fi.p = cp; |
| fi.c = strlen(cp); |
| if(peekc != IGN) { |
| cp[fi.c++] = peekc; |
| cp[fi.c] = 0; |
| peekc = IGN; |
| } |
| goto l0; |
| } |
| yylval.sym = s; |
| if(s->class == CTYPEDEF || s->class == CTYPESTR) |
| return LTYPE; |
| return s->lexical; |
| |
| tnum: |
| c1 = 0; |
| cp = symb; |
| if(c != '0') { |
| c1 |= Numdec; |
| for(;;) { |
| *cp++ = c; |
| c = GETC(); |
| if(isdigit(c)) |
| continue; |
| goto dc; |
| } |
| } |
| *cp++ = c; |
| c = GETC(); |
| if(c == 'x' || c == 'X') |
| for(;;) { |
| *cp++ = c; |
| c = GETC(); |
| if(isdigit(c)) |
| continue; |
| if(c >= 'a' && c <= 'f') |
| continue; |
| if(c >= 'A' && c <= 'F') |
| continue; |
| if(cp == symb+2) |
| yyerror("malformed hex constant"); |
| goto ncu; |
| } |
| if(c < '0' || c > '7') |
| goto dc; |
| for(;;) { |
| if(c >= '0' && c <= '7') { |
| *cp++ = c; |
| c = GETC(); |
| continue; |
| } |
| goto ncu; |
| } |
| |
| dc: |
| if(c == '.') |
| goto casedot; |
| if(c == 'e' || c == 'E') |
| goto casee; |
| |
| ncu: |
| if((c == 'U' || c == 'u') && !(c1 & Numuns)) { |
| c = GETC(); |
| c1 |= Numuns; |
| goto ncu; |
| } |
| if((c == 'L' || c == 'l') && !(c1 & Numvlong)) { |
| c = GETC(); |
| if(c1 & Numlong) |
| c1 |= Numvlong; |
| c1 |= Numlong; |
| goto ncu; |
| } |
| *cp = 0; |
| peekc = c; |
| if(mpatov(symb, &yylval.vval)) |
| yyerror("overflow in constant"); |
| |
| vv = yylval.vval; |
| if(c1 & Numvlong) { |
| if((c1 & Numuns) || convvtox(vv, TVLONG) < 0) { |
| c = LUVLCONST; |
| t = TUVLONG; |
| goto nret; |
| } |
| c = LVLCONST; |
| t = TVLONG; |
| goto nret; |
| } |
| if(c1 & Numlong) { |
| if((c1 & Numuns) || convvtox(vv, TLONG) < 0) { |
| c = LULCONST; |
| t = TULONG; |
| goto nret; |
| } |
| c = LLCONST; |
| t = TLONG; |
| goto nret; |
| } |
| if((c1 & Numuns) || convvtox(vv, TINT) < 0) { |
| c = LUCONST; |
| t = TUINT; |
| goto nret; |
| } |
| c = LCONST; |
| t = TINT; |
| goto nret; |
| |
| nret: |
| yylval.vval = convvtox(vv, t); |
| if(yylval.vval != vv){ |
| nearln = lineno; |
| warn(Z, "truncated constant: %T %s", types[t], symb); |
| } |
| return c; |
| |
| casedot: |
| for(;;) { |
| *cp++ = c; |
| c = GETC(); |
| if(!isdigit(c)) |
| break; |
| } |
| if(c != 'e' && c != 'E') |
| goto caseout; |
| |
| casee: |
| *cp++ = 'e'; |
| c = GETC(); |
| if(c == '+' || c == '-') { |
| *cp++ = c; |
| c = GETC(); |
| } |
| if(!isdigit(c)) |
| yyerror("malformed fp constant exponent"); |
| while(isdigit(c)) { |
| *cp++ = c; |
| c = GETC(); |
| } |
| |
| caseout: |
| if(c == 'L' || c == 'l') { |
| c = GETC(); |
| c1 |= Numlong; |
| } else |
| if(c == 'F' || c == 'f') { |
| c = GETC(); |
| c1 |= Numflt; |
| } |
| *cp = 0; |
| peekc = c; |
| yylval.dval = strtod(symb, nil); |
| if(isInf(yylval.dval, 1) || isInf(yylval.dval, -1)) { |
| yyerror("overflow in float constant"); |
| yylval.dval = 0; |
| } |
| if(c1 & Numflt) |
| return LFCONST; |
| return LDCONST; |
| } |
| |
| /* |
| * convert a string, s, to vlong in *v |
| * return conversion overflow. |
| * required syntax is [0[x]]d* |
| */ |
| int |
| mpatov(char *s, vlong *v) |
| { |
| vlong n, nn; |
| int c; |
| |
| n = 0; |
| c = *s; |
| if(c == '0') |
| goto oct; |
| while(c = *s++) { |
| if(c >= '0' && c <= '9') |
| nn = n*10 + c-'0'; |
| else |
| goto bad; |
| if(n < 0 && nn >= 0) |
| goto bad; |
| n = nn; |
| } |
| goto out; |
| |
| oct: |
| s++; |
| c = *s; |
| if(c == 'x' || c == 'X') |
| goto hex; |
| while(c = *s++) { |
| if(c >= '0' || c <= '7') |
| nn = n*8 + c-'0'; |
| else |
| goto bad; |
| if(n < 0 && nn >= 0) |
| goto bad; |
| n = nn; |
| } |
| goto out; |
| |
| hex: |
| s++; |
| while(c = *s++) { |
| if(c >= '0' && c <= '9') |
| c += 0-'0'; |
| else |
| if(c >= 'a' && c <= 'f') |
| c += 10-'a'; |
| else |
| if(c >= 'A' && c <= 'F') |
| c += 10-'A'; |
| else |
| goto bad; |
| nn = (uvlong)n*16 + c; |
| if(n < 0 && nn >= 0) |
| goto bad; |
| n = nn; |
| } |
| out: |
| *v = n; |
| return 0; |
| |
| bad: |
| *v = ~0; |
| return 1; |
| } |
| |
| int |
| getc(void) |
| { |
| int c; |
| |
| if(peekc != IGN) { |
| c = peekc; |
| peekc = IGN; |
| } else |
| c = GETC(); |
| if(c == '\n') |
| lineno++; |
| if(c == EOF) { |
| yyerror("End of file"); |
| errorexit(); |
| } |
| return c; |
| } |
| |
| int32 |
| getr(void) |
| { |
| int c, i; |
| char str[UTFmax+1]; |
| Rune rune; |
| |
| |
| c = getc(); |
| if(c < Runeself) |
| return c; |
| i = 0; |
| str[i++] = c; |
| |
| loop: |
| c = getc(); |
| str[i++] = c; |
| if(!fullrune(str, i)) |
| goto loop; |
| c = chartorune(&rune, str); |
| if(rune == Runeerror && c == 1) { |
| nearln = lineno; |
| diag(Z, "illegal rune in string"); |
| for(c=0; c<i; c++) |
| print(" %.2x", *(uchar*)(str+c)); |
| print("\n"); |
| } |
| return rune; |
| } |
| |
| int |
| getnsc(void) |
| { |
| int c; |
| |
| if(peekc != IGN) { |
| c = peekc; |
| peekc = IGN; |
| } else |
| c = GETC(); |
| for(;;) { |
| if(c >= Runeself || !isspace(c)) |
| return c; |
| if(c == '\n') { |
| lineno++; |
| return c; |
| } |
| c = GETC(); |
| } |
| } |
| |
| void |
| unget(int c) |
| { |
| |
| peekc = c; |
| if(c == '\n') |
| lineno--; |
| } |
| |
| int32 |
| escchar(int32 e, int longflg, int escflg) |
| { |
| int32 c, l; |
| int i; |
| |
| loop: |
| c = getr(); |
| if(c == '\n') { |
| yyerror("newline in string"); |
| return EOF; |
| } |
| if(c != '\\') { |
| if(c == e) |
| c = EOF; |
| return c; |
| } |
| c = getr(); |
| if(c == 'x') { |
| /* |
| * note this is not ansi, |
| * supposed to only accept 2 hex |
| */ |
| i = 2; |
| if(longflg) |
| i = 6; |
| l = 0; |
| for(; i>0; i--) { |
| c = getc(); |
| if(c >= '0' && c <= '9') { |
| l = l*16 + c-'0'; |
| continue; |
| } |
| if(c >= 'a' && c <= 'f') { |
| l = l*16 + c-'a' + 10; |
| continue; |
| } |
| if(c >= 'A' && c <= 'F') { |
| l = l*16 + c-'A' + 10; |
| continue; |
| } |
| unget(c); |
| break; |
| } |
| if(escflg) |
| l |= ESC; |
| return l; |
| } |
| if(c >= '0' && c <= '7') { |
| /* |
| * note this is not ansi, |
| * supposed to only accept 3 oct |
| */ |
| i = 2; |
| if(longflg) |
| i = 8; |
| l = c - '0'; |
| for(; i>0; i--) { |
| c = getc(); |
| if(c >= '0' && c <= '7') { |
| l = l*8 + c-'0'; |
| continue; |
| } |
| unget(c); |
| } |
| if(escflg) |
| l |= ESC; |
| return l; |
| } |
| switch(c) |
| { |
| case '\n': goto loop; |
| case 'n': return '\n'; |
| case 't': return '\t'; |
| case 'b': return '\b'; |
| case 'r': return '\r'; |
| case 'f': return '\f'; |
| case 'a': return '\a'; |
| case 'v': return '\v'; |
| } |
| return c; |
| } |
| |
| struct |
| { |
| char *name; |
| ushort lexical; |
| ushort type; |
| } itab[] = |
| { |
| "auto", LAUTO, 0, |
| "break", LBREAK, 0, |
| "case", LCASE, 0, |
| "char", LCHAR, TCHAR, |
| "const", LCONSTNT, 0, |
| "continue", LCONTINUE, 0, |
| "default", LDEFAULT, 0, |
| "do", LDO, 0, |
| "double", LDOUBLE, TDOUBLE, |
| "else", LELSE, 0, |
| "enum", LENUM, 0, |
| "extern", LEXTERN, 0, |
| "float", LFLOAT, TFLOAT, |
| "for", LFOR, 0, |
| "goto", LGOTO, 0, |
| "if", LIF, 0, |
| "inline", LINLINE, 0, |
| "int", LINT, TINT, |
| "long", LLONG, TLONG, |
| "PREFETCH", LPREFETCH, 0, |
| "register", LREGISTER, 0, |
| "restrict", LRESTRICT, 0, |
| "return", LRETURN, 0, |
| "SET", LSET, 0, |
| "short", LSHORT, TSHORT, |
| "signed", LSIGNED, 0, |
| "signof", LSIGNOF, 0, |
| "sizeof", LSIZEOF, 0, |
| "static", LSTATIC, 0, |
| "struct", LSTRUCT, 0, |
| "switch", LSWITCH, 0, |
| "typedef", LTYPEDEF, 0, |
| "typestr", LTYPESTR, 0, |
| "union", LUNION, 0, |
| "unsigned", LUNSIGNED, 0, |
| "USED", LUSED, 0, |
| "void", LVOID, TVOID, |
| "volatile", LVOLATILE, 0, |
| "while", LWHILE, 0, |
| 0 |
| }; |
| |
| void |
| cinit(void) |
| { |
| Sym *s; |
| int i; |
| Type *t; |
| |
| nerrors = 0; |
| lineno = 1; |
| iostack = I; |
| iofree = I; |
| peekc = IGN; |
| nhunk = 0; |
| |
| types[TXXX] = T; |
| types[TCHAR] = typ(TCHAR, T); |
| types[TUCHAR] = typ(TUCHAR, T); |
| types[TSHORT] = typ(TSHORT, T); |
| types[TUSHORT] = typ(TUSHORT, T); |
| types[TINT] = typ(TINT, T); |
| types[TUINT] = typ(TUINT, T); |
| types[TLONG] = typ(TLONG, T); |
| types[TULONG] = typ(TULONG, T); |
| types[TVLONG] = typ(TVLONG, T); |
| types[TUVLONG] = typ(TUVLONG, T); |
| types[TFLOAT] = typ(TFLOAT, T); |
| types[TDOUBLE] = typ(TDOUBLE, T); |
| types[TVOID] = typ(TVOID, T); |
| types[TENUM] = typ(TENUM, T); |
| types[TFUNC] = typ(TFUNC, types[TINT]); |
| types[TIND] = typ(TIND, types[TVOID]); |
| |
| for(i=0; i<NHASH; i++) |
| hash[i] = S; |
| for(i=0; itab[i].name; i++) { |
| s = slookup(itab[i].name); |
| s->lexical = itab[i].lexical; |
| if(itab[i].type != 0) |
| s->type = types[itab[i].type]; |
| } |
| blockno = 0; |
| autobn = 0; |
| autoffset = 0; |
| |
| t = typ(TARRAY, types[TCHAR]); |
| t->width = 0; |
| symstring = slookup(".string"); |
| symstring->class = CSTATIC; |
| symstring->dataflag = NOPTR; |
| symstring->type = t; |
| |
| t = typ(TARRAY, types[TCHAR]); |
| t->width = 0; |
| |
| nodproto = new(OPROTO, Z, Z); |
| dclstack = D; |
| |
| fmtinstall('O', Oconv); |
| fmtinstall('T', Tconv); |
| fmtinstall('F', FNconv); |
| fmtinstall('L', Lconv); |
| fmtinstall('Q', Qconv); |
| fmtinstall('|', VBconv); |
| fmtinstall('U', Uconv); |
| fmtinstall('B', Bconv); |
| } |
| |
| int |
| filbuf(void) |
| { |
| Io *i; |
| |
| loop: |
| i = iostack; |
| if(i == I) |
| return EOF; |
| if(i->f < 0) |
| goto pop; |
| fi.c = read(i->f, i->b, BUFSIZ) - 1; |
| if(fi.c < 0) { |
| close(i->f); |
| linklinehist(ctxt, lineno, nil, 0); |
| goto pop; |
| } |
| fi.p = i->b + 1; |
| return i->b[0] & 0xff; |
| |
| pop: |
| iostack = i->link; |
| i->link = iofree; |
| iofree = i; |
| i = iostack; |
| if(i == I) |
| return EOF; |
| fi.p = i->p; |
| fi.c = i->c; |
| if(--fi.c < 0) |
| goto loop; |
| return *fi.p++ & 0xff; |
| } |
| |
| int |
| Oconv(Fmt *fp) |
| { |
| int a; |
| |
| a = va_arg(fp->args, int); |
| if(a < OXXX || a > OEND) |
| return fmtprint(fp, "***badO %d***", a); |
| |
| return fmtstrcpy(fp, onames[a]); |
| } |
| |
| int |
| Lconv(Fmt *fp) |
| { |
| return linklinefmt(ctxt, fp); |
| } |
| |
| int |
| Tconv(Fmt *fp) |
| { |
| char str[STRINGSZ+20], s[STRINGSZ+20]; |
| Type *t, *t1; |
| int et; |
| int32 n; |
| |
| str[0] = 0; |
| for(t = va_arg(fp->args, Type*); t != T; t = t->link) { |
| et = t->etype; |
| if(str[0]) |
| strcat(str, " "); |
| if(t->garb&~GINCOMPLETE) { |
| sprint(s, "%s ", gnames[t->garb&~GINCOMPLETE]); |
| if(strlen(str) + strlen(s) < STRINGSZ) |
| strcat(str, s); |
| } |
| sprint(s, "%s", tnames[et]); |
| if(strlen(str) + strlen(s) < STRINGSZ) |
| strcat(str, s); |
| if(et == TFUNC && (t1 = t->down)) { |
| sprint(s, "(%T", t1); |
| if(strlen(str) + strlen(s) < STRINGSZ) |
| strcat(str, s); |
| while(t1 = t1->down) { |
| sprint(s, ", %T", t1); |
| if(strlen(str) + strlen(s) < STRINGSZ) |
| strcat(str, s); |
| } |
| if(strlen(str) + strlen(s) < STRINGSZ) |
| strcat(str, ")"); |
| } |
| if(et == TARRAY) { |
| n = t->width; |
| if(t->link && t->link->width) |
| n /= t->link->width; |
| sprint(s, "[%d]", n); |
| if(strlen(str) + strlen(s) < STRINGSZ) |
| strcat(str, s); |
| } |
| if(t->nbits) { |
| sprint(s, " %d:%d", t->shift, t->nbits); |
| if(strlen(str) + strlen(s) < STRINGSZ) |
| strcat(str, s); |
| } |
| if(typesu[et]) { |
| if(t->tag) { |
| strcat(str, " "); |
| if(strlen(str) + strlen(t->tag->name) < STRINGSZ) |
| strcat(str, t->tag->name); |
| } else |
| strcat(str, " {}"); |
| break; |
| } |
| } |
| return fmtstrcpy(fp, str); |
| } |
| |
| int |
| FNconv(Fmt *fp) |
| { |
| char *str; |
| Node *n; |
| |
| n = va_arg(fp->args, Node*); |
| str = "<indirect>"; |
| if(n != Z && (n->op == ONAME || n->op == ODOT || n->op == OELEM)) |
| str = n->sym->name; |
| return fmtstrcpy(fp, str); |
| } |
| |
| int |
| Qconv(Fmt *fp) |
| { |
| char str[STRINGSZ+20], *s; |
| int32 b; |
| int i; |
| |
| str[0] = 0; |
| for(b = va_arg(fp->args, int32); b;) { |
| i = bitno(b); |
| if(str[0]) |
| strcat(str, " "); |
| s = qnames[i]; |
| if(strlen(str) + strlen(s) >= STRINGSZ) |
| break; |
| strcat(str, s); |
| b &= ~(1L << i); |
| } |
| return fmtstrcpy(fp, str); |
| } |
| |
| int |
| VBconv(Fmt *fp) |
| { |
| char str[STRINGSZ]; |
| int i, n, t, pc; |
| |
| n = va_arg(fp->args, int); |
| pc = 0; /* BUG: was printcol */ |
| i = 0; |
| while(pc < n) { |
| t = (pc+4) & ~3; |
| if(t <= n) { |
| str[i++] = '\t'; |
| pc = t; |
| continue; |
| } |
| str[i++] = ' '; |
| pc++; |
| } |
| str[i] = 0; |
| |
| return fmtstrcpy(fp, str); |
| } |
| |
| int |
| Bconv(Fmt *fp) |
| { |
| char str[STRINGSZ], ss[STRINGSZ], *s; |
| Bits bits; |
| int i; |
| |
| str[0] = 0; |
| bits = va_arg(fp->args, Bits); |
| while(bany(&bits)) { |
| i = bnum(bits); |
| if(str[0]) |
| strcat(str, " "); |
| if(var[i].sym == nil) { |
| sprint(ss, "$%lld", var[i].offset); |
| s = ss; |
| } else |
| s = var[i].sym->name; |
| if(strlen(str) + strlen(s) + 1 >= STRINGSZ) |
| break; |
| strcat(str, s); |
| bits.b[i/32] &= ~(1L << (i%32)); |
| } |
| return fmtstrcpy(fp, str); |
| } |
| |
| void |
| setinclude(char *p) |
| { |
| int i; |
| |
| if(*p != 0) { |
| for(i=1; i < ninclude; i++) |
| if(strcmp(p, include[i]) == 0) |
| return; |
| |
| if(ninclude%8 == 0) |
| include = allocn(include, ninclude*sizeof(char *), |
| 8*sizeof(char *)); |
| include[ninclude++] = p; |
| } |
| } |
| |
| void* |
| alloc(int32 n) |
| { |
| void *p; |
| |
| p = malloc(n); |
| if(p == nil) { |
| print("alloc out of mem\n"); |
| exits("alloc: out of mem"); |
| } |
| memset(p, 0, n); |
| return p; |
| } |
| |
| void* |
| allocn(void *p, int32 n, int32 d) |
| { |
| if(p == nil) |
| return alloc(n+d); |
| p = realloc(p, n+d); |
| if(p == nil) { |
| print("allocn out of mem\n"); |
| exits("allocn: out of mem"); |
| } |
| if(d > 0) |
| memset((char*)p+n, 0, d); |
| return p; |
| } |
| |
| void |
| ensuresymb(int32 n) |
| { |
| if(symb == nil) { |
| symb = alloc(NSYMB+1); |
| nsymb = NSYMB; |
| } |
| |
| if(n > nsymb) { |
| symb = allocn(symb, nsymb, n+1-nsymb); |
| nsymb = n; |
| } |
| } |