blob: 4248437643890bedbd6defc2d0584f2da0d64677 [file] [log] [blame]
// 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"
#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'];
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->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;
}
}