| // Inferno utils/cc/lexbody |
| // http://code.google.com/p/inferno-os/source/browse/utils/cc/lexbody |
| // |
| // 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. |
| |
| /* |
| * common code for all the assemblers |
| */ |
| |
| void |
| pragpack(void) |
| { |
| while(getnsc() != '\n') |
| ; |
| } |
| |
| void |
| pragvararg(void) |
| { |
| while(getnsc() != '\n') |
| ; |
| } |
| |
| void |
| pragdynimport(void) |
| { |
| while(getnsc() != '\n') |
| ; |
| } |
| |
| void |
| pragdynexport(void) |
| { |
| while(getnsc() != '\n') |
| ; |
| } |
| |
| void |
| pragfpround(void) |
| { |
| while(getnsc() != '\n') |
| ; |
| } |
| |
| void |
| pragtextflag(void) |
| { |
| while(getnsc() != '\n') |
| ; |
| } |
| |
| void |
| pragprofile(void) |
| { |
| while(getnsc() != '\n') |
| ; |
| } |
| |
| void |
| pragincomplete(void) |
| { |
| while(getnsc() != '\n') |
| ; |
| } |
| |
| 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; |
| } |
| } |
| |
| void |
| setinclude(char *p) |
| { |
| int i; |
| |
| if(p == 0) |
| return; |
| 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 |
| errorexit(void) |
| { |
| |
| 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; |
| |
| i = ionext; |
| i->link = iostack; |
| iostack = i; |
| i->f = f; |
| if(f < 0) |
| i->f = open(s, 0); |
| if(i->f < 0) { |
| yyerror("%ca: %r: %s", thechar, s); |
| errorexit(); |
| } |
| fi.c = 0; |
| linehist(s, 0); |
| } |
| |
| Sym* |
| slookup(char *s) |
| { |
| ensuresymb(strlen(s)); |
| strcpy(symb, s); |
| return lookup(); |
| } |
| |
| Sym* |
| lookup(void) |
| { |
| Sym *s; |
| int32 h; |
| char *p; |
| int c, l; |
| 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] = '"'; |
| } |
| |
| // turn · into . |
| for(r=w=symb; *r; r++) { |
| if((uchar)*r == 0xc2 && (uchar)*(r+1) == 0xb7) { |
| *w++ = '.'; |
| r++; |
| }else |
| *w++ = *r; |
| } |
| *w = '\0'; |
| |
| h = 0; |
| for(p=symb; c = *p; p++) |
| h = h+h+h + c; |
| l = (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(memcmp(s->name, symb, l) == 0) |
| return s; |
| } |
| s = alloc(sizeof(*s)); |
| s->name = alloc(l); |
| memmove(s->name, symb, l); |
| |
| s->link = hash[h]; |
| hash[h] = s; |
| syminit(s); |
| return s; |
| } |
| |
| int |
| ISALPHA(int c) |
| { |
| if(isalpha(c)) |
| return 1; |
| if(c >= Runeself) |
| return 1; |
| return 0; |
| } |
| |
| int32 |
| yylex(void) |
| { |
| int c, c1; |
| char *cp; |
| Sym *s; |
| |
| c = peekc; |
| if(c != IGN) { |
| peekc = IGN; |
| goto l1; |
| } |
| l0: |
| c = GETC(); |
| |
| l1: |
| if(c == EOF) { |
| peekc = EOF; |
| return -1; |
| } |
| if(isspace(c)) { |
| if(c == '\n') { |
| lineno++; |
| return ';'; |
| } |
| goto l0; |
| } |
| if(ISALPHA(c)) |
| goto talph; |
| if(isdigit(c)) |
| goto tnum; |
| switch(c) |
| { |
| case '\n': |
| lineno++; |
| return ';'; |
| |
| case '#': |
| domacro(); |
| goto l0; |
| |
| case '.': |
| c = GETC(); |
| if(ISALPHA(c)) { |
| cp = symb; |
| *cp++ = '.'; |
| goto aloop; |
| } |
| if(isdigit(c)) { |
| cp = symb; |
| *cp++ = '.'; |
| goto casedot; |
| } |
| peekc = c; |
| return '.'; |
| |
| talph: |
| case '_': |
| case '@': |
| cp = symb; |
| |
| aloop: |
| *cp++ = c; |
| c = GETC(); |
| if(ISALPHA(c) || isdigit(c) || c == '_' || c == '$') |
| goto aloop; |
| *cp = 0; |
| 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; |
| } |
| if(s->type == 0) |
| s->type = LNAME; |
| if(s->type == LNAME || |
| s->type == LVAR || |
| s->type == LLAB) { |
| yylval.sym = s; |
| return s->type; |
| } |
| yylval.lval = s->value; |
| return s->type; |
| |
| tnum: |
| cp = symb; |
| if(c != '0') |
| goto dc; |
| *cp++ = c; |
| c = GETC(); |
| c1 = 3; |
| if(c == 'x' || c == 'X') { |
| c1 = 4; |
| c = GETC(); |
| } else |
| if(c < '0' || c > '7') |
| goto dc; |
| yylval.lval = 0; |
| for(;;) { |
| if(c >= '0' && c <= '9') { |
| if(c > '7' && c1 == 3) |
| break; |
| yylval.lval <<= c1; |
| yylval.lval += c - '0'; |
| c = GETC(); |
| continue; |
| } |
| if(c1 == 3) |
| break; |
| if(c >= 'A' && c <= 'F') |
| c += 'a' - 'A'; |
| if(c >= 'a' && c <= 'f') { |
| yylval.lval <<= c1; |
| yylval.lval += c - 'a' + 10; |
| c = GETC(); |
| continue; |
| } |
| break; |
| } |
| goto ncu; |
| |
| dc: |
| for(;;) { |
| if(!isdigit(c)) |
| break; |
| *cp++ = c; |
| c = GETC(); |
| } |
| if(c == '.') |
| goto casedot; |
| if(c == 'e' || c == 'E') |
| goto casee; |
| *cp = 0; |
| if(sizeof(yylval.lval) == sizeof(vlong)) |
| yylval.lval = strtoll(symb, nil, 10); |
| else |
| yylval.lval = strtol(symb, nil, 10); |
| |
| ncu: |
| while(c == 'U' || c == 'u' || c == 'l' || c == 'L') |
| c = GETC(); |
| peekc = c; |
| return LCONST; |
| |
| casedot: |
| for(;;) { |
| *cp++ = c; |
| c = GETC(); |
| if(!isdigit(c)) |
| break; |
| } |
| if(c == 'e' || c == 'E') |
| goto casee; |
| goto caseout; |
| |
| casee: |
| *cp++ = 'e'; |
| c = GETC(); |
| if(c == '+' || c == '-') { |
| *cp++ = c; |
| c = GETC(); |
| } |
| while(isdigit(c)) { |
| *cp++ = c; |
| c = GETC(); |
| } |
| |
| caseout: |
| *cp = 0; |
| peekc = c; |
| if(FPCHIP) { |
| yylval.dval = atof(symb); |
| return LFCONST; |
| } |
| yyerror("assembler cannot interpret fp constants"); |
| yylval.lval = 1L; |
| return LCONST; |
| |
| case '"': |
| memcpy(yylval.sval, nullgen.sval, sizeof(yylval.sval)); |
| cp = yylval.sval; |
| c1 = 0; |
| for(;;) { |
| c = escchar('"'); |
| if(c == EOF) |
| break; |
| if(c1 < sizeof(yylval.sval)) |
| *cp++ = c; |
| c1++; |
| } |
| if(c1 > sizeof(yylval.sval)) |
| yyerror("string constant too long"); |
| return LSCONST; |
| |
| case '\'': |
| c = escchar('\''); |
| if(c == EOF) |
| c = '\''; |
| if(escchar('\'') != EOF) |
| yyerror("missing '"); |
| yylval.lval = c; |
| return LCONST; |
| |
| case '/': |
| c1 = GETC(); |
| if(c1 == '/') { |
| for(;;) { |
| c = GETC(); |
| if(c == '\n') |
| goto l1; |
| if(c == EOF) { |
| yyerror("eof in comment"); |
| errorexit(); |
| } |
| } |
| } |
| if(c1 == '*') { |
| for(;;) { |
| c = GETC(); |
| while(c == '*') { |
| c = GETC(); |
| if(c == '/') |
| goto l0; |
| } |
| if(c == EOF) { |
| yyerror("eof in comment"); |
| errorexit(); |
| } |
| if(c == '\n') |
| lineno++; |
| } |
| } |
| break; |
| |
| default: |
| return c; |
| } |
| peekc = c1; |
| return c; |
| } |
| |
| int |
| getc(void) |
| { |
| int c; |
| |
| c = peekc; |
| if(c != IGN) { |
| peekc = IGN; |
| return c; |
| } |
| c = GETC(); |
| if(c == '\n') |
| lineno++; |
| if(c == EOF) { |
| yyerror("End of file"); |
| errorexit(); |
| } |
| return c; |
| } |
| |
| int |
| getnsc(void) |
| { |
| int c; |
| |
| for(;;) { |
| c = getc(); |
| if(!isspace(c) || c == '\n') |
| return c; |
| } |
| } |
| |
| void |
| unget(int c) |
| { |
| |
| peekc = c; |
| if(c == '\n') |
| lineno--; |
| } |
| |
| int |
| escchar(int e) |
| { |
| int c, l; |
| |
| loop: |
| c = getc(); |
| if(c == '\n') { |
| yyerror("newline in string"); |
| return EOF; |
| } |
| if(c != '\\') { |
| if(c == e) |
| return EOF; |
| return c; |
| } |
| c = getc(); |
| if(c >= '0' && c <= '7') { |
| l = c - '0'; |
| c = getc(); |
| if(c >= '0' && c <= '7') { |
| l = l*8 + c-'0'; |
| c = getc(); |
| if(c >= '0' && c <= '7') { |
| l = l*8 + c-'0'; |
| return l; |
| } |
| } |
| peekc = c; |
| 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 0x07; |
| case 'v': return 0x0b; |
| case 'z': return 0x00; |
| } |
| return c; |
| } |
| |
| void |
| pinit(char *f) |
| { |
| int i; |
| Sym *s; |
| |
| lineno = 1; |
| newio(); |
| newfile(f, -1); |
| pc = 0; |
| peekc = IGN; |
| sym = 1; |
| for(i=0; i<NSYM; i++) { |
| h[i].type = 0; |
| h[i].sym = S; |
| } |
| for(i=0; i<NHASH; i++) |
| for(s = hash[i]; s != S; s = s->link) |
| s->macro = 0; |
| } |
| |
| 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); |
| linehist(0, 0); |
| goto pop; |
| } |
| fi.p = i->b + 1; |
| return i->b[0]; |
| |
| 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++; |
| } |
| |
| void |
| yyerror(char *a, ...) |
| { |
| char buf[200]; |
| va_list arg; |
| |
| /* |
| * hack to intercept message from yaccpar |
| */ |
| if(strcmp(a, "syntax error") == 0) { |
| yyerror("syntax error, last name: %s", symb); |
| return; |
| } |
| prfile(lineno); |
| va_start(arg, a); |
| vseprint(buf, buf+sizeof(buf), a, arg); |
| va_end(arg); |
| print("%s\n", buf); |
| nerrors++; |
| if(nerrors > 10) { |
| print("too many errors\n"); |
| errorexit(); |
| } |
| } |
| |
| void |
| prfile(int32 l) |
| { |
| int i, n; |
| Hist a[HISTSZ], *h; |
| int32 d; |
| |
| n = 0; |
| for(h = hist; h != H; h = h->link) { |
| if(l < h->line) |
| break; |
| if(h->name) { |
| if(h->offset == 0) { |
| if(n >= 0 && n < HISTSZ) |
| a[n] = *h; |
| n++; |
| continue; |
| } |
| if(n > 0 && n < HISTSZ) |
| if(a[n-1].offset == 0) { |
| a[n] = *h; |
| n++; |
| } else |
| a[n-1] = *h; |
| continue; |
| } |
| n--; |
| if(n >= 0 && n < HISTSZ) { |
| d = h->line - a[n].line; |
| for(i=0; i<n; i++) |
| a[i].line += d; |
| } |
| } |
| if(n > HISTSZ) |
| n = HISTSZ; |
| for(i=0; i<n; i++) |
| print("%s:%ld ", a[i].name, (long)(l-a[i].line+a[i].offset+1)); |
| } |
| |
| void |
| ieeedtod(Ieee *ieee, double native) |
| { |
| double fr, ho, f; |
| int exp; |
| |
| if(native < 0) { |
| ieeedtod(ieee, -native); |
| ieee->h |= 0x80000000L; |
| return; |
| } |
| if(native == 0) { |
| ieee->l = 0; |
| ieee->h = 0; |
| return; |
| } |
| fr = frexp(native, &exp); |
| f = 2097152L; /* shouldnt use fp constants here */ |
| fr = modf(fr*f, &ho); |
| ieee->h = ho; |
| ieee->h &= 0xfffffL; |
| ieee->h |= (exp+1022L) << 20; |
| f = 65536L; |
| fr = modf(fr*f, &ho); |
| ieee->l = ho; |
| ieee->l <<= 16; |
| ieee->l |= (int32)(fr*f); |
| } |