diff options
Diffstat (limited to 'src/cmd/gc/lex.c')
-rw-r--r-- | src/cmd/gc/lex.c | 2590 |
1 files changed, 0 insertions, 2590 deletions
diff --git a/src/cmd/gc/lex.c b/src/cmd/gc/lex.c deleted file mode 100644 index a4b832aa0b..0000000000 --- a/src/cmd/gc/lex.c +++ /dev/null @@ -1,2590 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include <u.h> -#include <libc.h> -#include "go.h" -#include "y.tab.h" -#include <ar.h> - -#ifndef PLAN9 -#include <signal.h> -#endif - -#undef getc -#undef ungetc -#define getc ccgetc -#define ungetc ccungetc - -extern int yychar; -int yyprev; -int yylast; - -static int imported_unsafe; - -static void lexinit(void); -static void lexinit1(void); -static void lexfini(void); -static void yytinit(void); -static int getc(void); -static void ungetc(int); -static int32 getr(void); -static int escchar(int, int*, vlong*); -static void addidir(char*); -static int getlinepragma(void); -static char *goos, *goarch, *goroot; - -#define BOM 0xFEFF - -// Debug arguments. -// These can be specified with the -d flag, as in "-d nil" -// to set the debug_checknil variable. In general the list passed -// to -d can be comma-separated. -static struct { - char *name; - int *val; -} debugtab[] = { - {"nil", &debug_checknil}, -}; - -// Our own isdigit, isspace, isalpha, isalnum that take care -// of EOF and other out of range arguments. -static int -yy_isdigit(int c) -{ - return c >= 0 && c <= 0xFF && isdigit(c); -} - -static int -yy_isspace(int c) -{ - return c == ' ' || c == '\t' || c == '\n' || c == '\r'; -} - -static int -yy_isalpha(int c) -{ - return c >= 0 && c <= 0xFF && isalpha(c); -} - -static int -yy_isalnum(int c) -{ - return c >= 0 && c <= 0xFF && isalnum(c); -} - -// Disallow use of isdigit etc. -#undef isdigit -#undef isspace -#undef isalpha -#undef isalnum -#define isdigit use_yy_isdigit_instead_of_isdigit -#define isspace use_yy_isspace_instead_of_isspace -#define isalpha use_yy_isalpha_instead_of_isalpha -#define isalnum use_yy_isalnum_instead_of_isalnum - -#define DBG if(!debug['x']){}else print -/*c2go void DBG(char*, ...); */ - -enum -{ - EOF = -1, -}; - -void -usage(void) -{ - print("usage: %cg [options] file.go...\n", thearch.thechar); - flagprint(1); - exits("usage"); -} - -void -fault(int s) -{ - USED(s); - - // If we've already complained about things - // in the program, don't bother complaining - // about the seg fault too; let the user clean up - // the code and try again. - if(nsavederrors + nerrors > 0) - errorexit(); - fatal("fault"); -} - -#ifdef PLAN9 -void -catcher(void *v, char *s) -{ - USED(v); - - if(strncmp(s, "sys: trap: fault read", 21) == 0) { - if(nsavederrors + nerrors > 0) - errorexit(); - fatal("fault"); - } - noted(NDFLT); -} -#endif - -void -doversion(void) -{ - char *p, *sep; - - p = expstring(); - if(strcmp(p, "X:none") == 0) - p = ""; - sep = ""; - if(*p) - sep = " "; - print("%cg version %s%s%s\n", thearch.thechar, getgoversion(), sep, p); - exits(0); -} - -int -gcmain(int argc, char *argv[]) -{ - int i; - NodeList *l; - char *p; - -#ifdef SIGBUS - signal(SIGBUS, fault); - signal(SIGSEGV, fault); -#endif - -#ifdef PLAN9 - notify(catcher); - // Tell the FPU to handle all exceptions. - setfcr(FPPDBL|FPRNR); -#endif - // Allow GOARCH=thearch.thestring or GOARCH=thearch.thestringsuffix, - // but not other values. - p = getgoarch(); - if(strncmp(p, thearch.thestring, strlen(thearch.thestring)) != 0) - sysfatal("cannot use %cg with GOARCH=%s", thearch.thechar, p); - goarch = p; - - thearch.linkarchinit(); - ctxt = linknew(thearch.thelinkarch); - ctxt->diag = yyerror; - ctxt->bso = &bstdout; - Binit(&bstdout, 1, OWRITE); - - localpkg = mkpkg(newstrlit("")); - localpkg->prefix = "\"\""; - - // pseudo-package, for scoping - builtinpkg = mkpkg(newstrlit("go.builtin")); - builtinpkg->prefix = "go.builtin"; // not go%2ebuiltin - - // pseudo-package, accessed by import "unsafe" - unsafepkg = mkpkg(newstrlit("unsafe")); - unsafepkg->name = "unsafe"; - - // real package, referred to by generated runtime calls - runtimepkg = mkpkg(newstrlit("runtime")); - runtimepkg->name = "runtime"; - - // pseudo-packages used in symbol tables - gostringpkg = mkpkg(newstrlit("go.string")); - gostringpkg->name = "go.string"; - gostringpkg->prefix = "go.string"; // not go%2estring - - itabpkg = mkpkg(newstrlit("go.itab")); - itabpkg->name = "go.itab"; - itabpkg->prefix = "go.itab"; // not go%2eitab - - weaktypepkg = mkpkg(newstrlit("go.weak.type")); - weaktypepkg->name = "go.weak.type"; - weaktypepkg->prefix = "go.weak.type"; // not go%2eweak%2etype - - typelinkpkg = mkpkg(newstrlit("go.typelink")); - typelinkpkg->name = "go.typelink"; - typelinkpkg->prefix = "go.typelink"; // not go%2etypelink - - trackpkg = mkpkg(newstrlit("go.track")); - trackpkg->name = "go.track"; - trackpkg->prefix = "go.track"; // not go%2etrack - - typepkg = mkpkg(newstrlit("type")); - typepkg->name = "type"; - - goroot = getgoroot(); - goos = getgoos(); - - nacl = strcmp(goos, "nacl") == 0; - if(nacl) - flag_largemodel = 1; - - fmtstrinit(&pragcgobuf); - quotefmtinstall(); - - outfile = nil; - flagcount("+", "compiling runtime", &compiling_runtime); - flagcount("%", "debug non-static initializers", &debug['%']); - flagcount("A", "for bootstrapping, allow 'any' type", &debug['A']); - flagcount("B", "disable bounds checking", &debug['B']); - flagstr("D", "path: set relative path for local imports", &localimport); - flagcount("E", "debug symbol export", &debug['E']); - flagfn1("I", "dir: add dir to import search path", addidir); - flagcount("K", "debug missing line numbers", &debug['K']); - flagcount("L", "use full (long) path in error messages", &debug['L']); - flagcount("M", "debug move generation", &debug['M']); - flagcount("N", "disable optimizations", &debug['N']); - flagcount("P", "debug peephole optimizer", &debug['P']); - flagcount("R", "debug register optimizer", &debug['R']); - flagcount("S", "print assembly listing", &debug['S']); - flagfn0("V", "print compiler version", doversion); - flagcount("W", "debug parse tree after type checking", &debug['W']); - flagstr("asmhdr", "file: write assembly header to named file", &asmhdr); - flagcount("complete", "compiling complete package (no C or assembly)", &pure_go); - flagstr("d", "list: print debug information about items in list", &debugstr); - flagcount("e", "no limit on number of errors reported", &debug['e']); - flagcount("f", "debug stack frames", &debug['f']); - flagcount("g", "debug code generation", &debug['g']); - flagcount("h", "halt on error", &debug['h']); - flagcount("i", "debug line number stack", &debug['i']); - flagstr("installsuffix", "pkg directory suffix", &flag_installsuffix); - flagcount("j", "debug runtime-initialized variables", &debug['j']); - flagcount("l", "disable inlining", &debug['l']); - flagcount("live", "debug liveness analysis", &debuglive); - flagcount("m", "print optimization decisions", &debug['m']); - flagcount("nolocalimports", "reject local (relative) imports", &nolocalimports); - flagstr("o", "obj: set output file", &outfile); - flagstr("p", "path: set expected package import path", &myimportpath); - flagcount("pack", "write package file instead of object file", &writearchive); - flagcount("r", "debug generated wrappers", &debug['r']); - flagcount("race", "enable race detector", &flag_race); - flagcount("s", "warn about composite literals that can be simplified", &debug['s']); - flagstr("trimpath", "prefix: remove prefix from recorded source file paths", &ctxt->trimpath); - flagcount("u", "reject unsafe code", &safemode); - flagcount("v", "increase debug verbosity", &debug['v']); - flagcount("w", "debug type checking", &debug['w']); - use_writebarrier = 1; - flagcount("wb", "enable write barrier", &use_writebarrier); - flagcount("x", "debug lexer", &debug['x']); - flagcount("y", "debug declarations in canned imports (with -d)", &debug['y']); - if(thearch.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) - usage(); - - if(flag_race) { - racepkg = mkpkg(newstrlit("runtime/race")); - racepkg->name = "race"; - } - - // parse -d argument - if(debugstr) { - char *f[100]; - int i, j, nf; - - nf = getfields(debugstr, f, nelem(f), 1, ","); - for(i=0; i<nf; i++) { - for(j=0; j<nelem(debugtab); j++) { - if(strcmp(debugtab[j].name, f[i]) == 0) { - if(debugtab[j].val != nil) - *debugtab[j].val = 1; - break; - } - } - if(j >= nelem(debugtab)) - sysfatal("unknown debug information -d '%s'\n", f[i]); - } - } - - // enable inlining. for now: - // default: inlining on. (debug['l'] == 1) - // -l: inlining off (debug['l'] == 0) - // -ll, -lll: inlining on again, with extra debugging (debug['l'] > 1) - if(debug['l'] <= 1) - debug['l'] = 1 - debug['l']; - - if(thearch.thechar == '8') { - p = getgo386(); - if(strcmp(p, "387") == 0) - use_sse = 0; - else if(strcmp(p, "sse2") == 0) - use_sse = 1; - else - sysfatal("unsupported setting GO386=%s", p); - } - - fmtinstallgo(); - thearch.betypeinit(); - if(widthptr == 0) - fatal("betypeinit failed"); - - lexinit(); - typeinit(); - lexinit1(); - yytinit(); - - blockgen = 1; - dclcontext = PEXTERN; - nerrors = 0; - lexlineno = 1; - - for(i=0; i<argc; i++) { - infile = argv[i]; - linehist(infile, 0, 0); - - curio.infile = infile; - curio.bin = Bopen(infile, OREAD); - if(curio.bin == nil) { - print("open %s: %r\n", infile); - errorexit(); - } - curio.peekc = 0; - curio.peekc1 = 0; - curio.nlsemi = 0; - curio.eofnl = 0; - curio.last = 0; - - // Skip initial BOM if present. - if(Bgetrune(curio.bin) != BOM) - Bungetrune(curio.bin); - - block = 1; - iota = -1000000; - - imported_unsafe = 0; - - yyparse(); - if(nsyntaxerrors != 0) - errorexit(); - - linehist(nil, 0, 0); - if(curio.bin != nil) - Bterm(curio.bin); - } - testdclstack(); - mkpackage(localpkg->name); // final import not used checks - lexfini(); - - typecheckok = 1; - if(debug['f']) - frame(1); - - // Process top-level declarations in phases. - - // Phase 1: const, type, and names and types of funcs. - // This will gather all the information about types - // and methods but doesn't depend on any of it. - defercheckwidth(); - for(l=xtop; l; l=l->next) - if(l->n->op != ODCL && l->n->op != OAS) - typecheck(&l->n, Etop); - - // Phase 2: Variable assignments. - // To check interface assignments, depends on phase 1. - for(l=xtop; l; l=l->next) - if(l->n->op == ODCL || l->n->op == OAS) - typecheck(&l->n, Etop); - resumecheckwidth(); - - // Phase 3: Type check function bodies. - for(l=xtop; l; l=l->next) { - if(l->n->op == ODCLFUNC || l->n->op == OCLOSURE) { - curfn = l->n; - decldepth = 1; - saveerrors(); - typechecklist(l->n->nbody, Etop); - checkreturn(l->n); - if(nerrors != 0) - l->n->nbody = nil; // type errors; do not compile - } - } - - // Phase 4: Decide how to capture closed variables. - // This needs to run before escape analysis, - // because variables captured by value do not escape. - for(l=xtop; l; l=l->next) { - if(l->n->op == ODCLFUNC && l->n->closure) { - curfn = l->n; - capturevars(l->n); - } - } - - curfn = nil; - - if(nsavederrors+nerrors) - errorexit(); - - // Phase 5: Inlining - if(debug['l'] > 1) { - // Typecheck imported function bodies if debug['l'] > 1, - // otherwise lazily when used or re-exported. - for(l=importlist; l; l=l->next) - if (l->n->inl) { - saveerrors(); - typecheckinl(l->n); - } - - if(nsavederrors+nerrors) - errorexit(); - } - - if(debug['l']) { - // Find functions that can be inlined and clone them before walk expands them. - for(l=xtop; l; l=l->next) - if(l->n->op == ODCLFUNC) - caninl(l->n); - - // Expand inlineable calls in all functions - for(l=xtop; l; l=l->next) - if(l->n->op == ODCLFUNC) - inlcalls(l->n); - } - - // Phase 6: Escape analysis. - // Required for moving heap allocations onto stack, - // which in turn is required by the closure implementation, - // which stores the addresses of stack variables into the closure. - // If the closure does not escape, it needs to be on the stack - // or else the stack copier will not update it. - escapes(xtop); - - // Escape analysis moved escaped values off stack. - // Move large values off stack too. - movelarge(xtop); - - // Phase 7: Transform closure bodies to properly reference captured variables. - // This needs to happen before walk, because closures must be transformed - // before walk reaches a call of a closure. - for(l=xtop; l; l=l->next) { - if(l->n->op == ODCLFUNC && l->n->closure) { - curfn = l->n; - transformclosure(l->n); - } - } - curfn = N; - - // Phase 8: Compile top level functions. - for(l=xtop; l; l=l->next) - if(l->n->op == ODCLFUNC) - funccompile(l->n); - - if(nsavederrors+nerrors == 0) - fninit(xtop); - - // Phase 9: Check external declarations. - for(l=externdcl; l; l=l->next) - if(l->n->op == ONAME) - typecheck(&l->n, Erv); - - if(nerrors+nsavederrors) - errorexit(); - - dumpobj(); - - if(asmhdr) - dumpasmhdr(); - - if(nerrors+nsavederrors) - errorexit(); - - flusherrors(); - exits(0); - return 0; -} - -void -saveerrors(void) -{ - nsavederrors += nerrors; - nerrors = 0; -} - -static int -arsize(Biobuf *b, char *name) -{ - struct ar_hdr a; - - if(Bread(b, a.name, sizeof(a.name)) != sizeof(a.name) || - Bread(b, a.date, sizeof(a.date)) != sizeof(a.date) || - Bread(b, a.uid, sizeof(a.uid)) != sizeof(a.uid) || - Bread(b, a.gid, sizeof(a.gid)) != sizeof(a.gid) || - Bread(b, a.mode, sizeof(a.mode)) != sizeof(a.mode) || - Bread(b, a.size, sizeof(a.size)) != sizeof(a.size) || - Bread(b, a.fmag, sizeof(a.fmag)) != sizeof(a.fmag)) - return -1; - - if(strncmp(a.name, name, strlen(name)) != 0) - return -1; - - return atoi(a.size); -} - -static int -skiptopkgdef(Biobuf *b) -{ - char *p; - int sz; - - /* archive header */ - if((p = Brdline(b, '\n')) == nil) - return 0; - if(Blinelen(b) != 8) - return 0; - if(memcmp(p, "!<arch>\n", 8) != 0) - return 0; - /* symbol table may be first; skip it */ - sz = arsize(b, "__.GOSYMDEF"); - if(sz >= 0) - Bseek(b, sz, 1); - else - Bseek(b, 8, 0); - /* package export block is next */ - sz = arsize(b, "__.PKGDEF"); - if(sz <= 0) - return 0; - return 1; -} - -static void -addidir(char* dir) -{ - Idir** pp; - - if(dir == nil) - return; - - for(pp = &idirs; *pp != nil; pp = &(*pp)->link) - ; - *pp = mal(sizeof(Idir)); - (*pp)->link = nil; - (*pp)->dir = dir; -} - -// is this path a local name? begins with ./ or ../ or / -static int -islocalname(Strlit *name) -{ - if(name->len >= 1 && name->s[0] == '/') - return 1; - if(ctxt->windows && name->len >= 3 && - yy_isalpha(name->s[0]) && name->s[1] == ':' && name->s[2] == '/') - return 1; - if(name->len >= 2 && strncmp(name->s, "./", 2) == 0) - return 1; - if(name->len == 1 && strncmp(name->s, ".", 1) == 0) - return 1; - if(name->len >= 3 && strncmp(name->s, "../", 3) == 0) - return 1; - if(name->len == 2 && strncmp(name->s, "..", 2) == 0) - return 1; - return 0; -} - -static int -findpkg(Strlit *name) -{ - Idir *p; - char *q, *suffix, *suffixsep; - - if(islocalname(name)) { - if(safemode || nolocalimports) - return 0; - // try .a before .6. important for building libraries: - // if there is an array.6 in the array.a library, - // want to find all of array.a, not just array.6. - snprint(namebuf, sizeof(namebuf), "%Z.a", name); - if(access(namebuf, 0) >= 0) - return 1; - snprint(namebuf, sizeof(namebuf), "%Z.%c", name, thearch.thechar); - if(access(namebuf, 0) >= 0) - return 1; - return 0; - } - - // local imports should be canonicalized already. - // don't want to see "encoding/../encoding/base64" - // as different from "encoding/base64". - q = mal(name->len+1); - memmove(q, name->s, name->len); - q[name->len] = '\0'; - cleanname(q); - if(strlen(q) != name->len || memcmp(q, name->s, name->len) != 0) { - yyerror("non-canonical import path %Z (should be %s)", name, q); - return 0; - } - - for(p = idirs; p != nil; p = p->link) { - snprint(namebuf, sizeof(namebuf), "%s/%Z.a", p->dir, name); - if(access(namebuf, 0) >= 0) - return 1; - snprint(namebuf, sizeof(namebuf), "%s/%Z.%c", p->dir, name, thearch.thechar); - if(access(namebuf, 0) >= 0) - return 1; - } - if(goroot != nil) { - suffix = ""; - suffixsep = ""; - if(flag_installsuffix != nil) { - suffixsep = "_"; - suffix = flag_installsuffix; - } else if(flag_race) { - suffixsep = "_"; - suffix = "race"; - } - snprint(namebuf, sizeof(namebuf), "%s/pkg/%s_%s%s%s/%Z.a", goroot, goos, goarch, suffixsep, suffix, name); - if(access(namebuf, 0) >= 0) - return 1; - snprint(namebuf, sizeof(namebuf), "%s/pkg/%s_%s%s%s/%Z.%c", goroot, goos, goarch, suffixsep, suffix, name, thearch.thechar); - if(access(namebuf, 0) >= 0) - return 1; - } - return 0; -} - -static void -fakeimport(void) -{ - importpkg = mkpkg(newstrlit("fake")); - cannedimports("fake.6", "$$\n"); -} - -void -importfile(Val *f, int line) -{ - Biobuf *imp; - char *file, *p, *q, *tag; - int32 c; - int n; - Strlit *path; - char *cleanbuf, *prefix; - - USED(line); - - if(f->ctype != CTSTR) { - yyerror("import statement not a string"); - fakeimport(); - return; - } - - if(f->u.sval->len == 0) { - yyerror("import path is empty"); - fakeimport(); - return; - } - - if(isbadimport(f->u.sval)) { - fakeimport(); - return; - } - - // The package name main is no longer reserved, - // but we reserve the import path "main" to identify - // the main package, just as we reserve the import - // path "math" to identify the standard math package. - if(strcmp(f->u.sval->s, "main") == 0) { - yyerror("cannot import \"main\""); - errorexit(); - } - - if(myimportpath != nil && strcmp(f->u.sval->s, myimportpath) == 0) { - yyerror("import \"%Z\" while compiling that package (import cycle)", f->u.sval); - errorexit(); - } - - if(strcmp(f->u.sval->s, "unsafe") == 0) { - if(safemode) { - yyerror("cannot import package unsafe"); - errorexit(); - } - importpkg = mkpkg(f->u.sval); - cannedimports("unsafe.6", unsafeimport); - imported_unsafe = 1; - return; - } - - path = f->u.sval; - if(islocalname(path)) { - if(path->s[0] == '/') { - yyerror("import path cannot be absolute path"); - fakeimport(); - return; - } - prefix = ctxt->pathname; - if(localimport != nil) - prefix = localimport; - cleanbuf = mal(strlen(prefix) + strlen(path->s) + 2); - strcpy(cleanbuf, prefix); - strcat(cleanbuf, "/"); - strcat(cleanbuf, path->s); - cleanname(cleanbuf); - path = newstrlit(cleanbuf); - - if(isbadimport(path)) { - fakeimport(); - return; - } - } - - if(!findpkg(path)) { - yyerror("can't find import: \"%Z\"", f->u.sval); - errorexit(); - } - importpkg = mkpkg(path); - - // If we already saw that package, feed a dummy statement - // to the lexer to avoid parsing export data twice. - if(importpkg->imported) { - file = strdup(namebuf); - tag = ""; - if(importpkg->safe) { - tag = "safe"; - } - p = smprint("package %s %s\n$$\n", importpkg->name, tag); - cannedimports(file, p); - return; - } - importpkg->imported = 1; - - imp = Bopen(namebuf, OREAD); - if(imp == nil) { - yyerror("can't open import: \"%Z\": %r", f->u.sval); - errorexit(); - } - file = strdup(namebuf); - - n = strlen(namebuf); - if(n > 2 && namebuf[n-2] == '.' && namebuf[n-1] == 'a') { - if(!skiptopkgdef(imp)) { - yyerror("import %s: not a package file", file); - errorexit(); - } - } - - // check object header - p = Brdstr(imp, '\n', 1); - if(strcmp(p, "empty archive") != 0) { - if(strncmp(p, "go object ", 10) != 0) { - yyerror("import %s: not a go object file", file); - errorexit(); - } - q = smprint("%s %s %s %s", getgoos(), getgoarch(), getgoversion(), expstring()); - if(strcmp(p+10, q) != 0) { - yyerror("import %s: object is [%s] expected [%s]", file, p+10, q); - errorexit(); - } - free(q); - } - - // assume files move (get installed) - // so don't record the full path. - linehist(file + n - path->len - 2, -1, 1); // acts as #pragma lib - - /* - * position the input right - * after $$ and return - */ - pushedio = curio; - curio.bin = imp; - curio.peekc = 0; - curio.peekc1 = 0; - curio.infile = file; - curio.nlsemi = 0; - typecheckok = 1; - - for(;;) { - c = getc(); - if(c == EOF) - break; - if(c != '$') - continue; - c = getc(); - if(c == EOF) - break; - if(c != '$') - continue; - return; - } - yyerror("no import in \"%Z\"", f->u.sval); - unimportfile(); -} - -void -unimportfile(void) -{ - if(curio.bin != nil) { - Bterm(curio.bin); - curio.bin = nil; - } else - lexlineno--; // re correct sys.6 line number - - curio = pushedio; - pushedio.bin = nil; - incannedimport = 0; - typecheckok = 0; -} - -void -cannedimports(char *file, char *cp) -{ - lexlineno++; // if sys.6 is included on line 1, - - pushedio = curio; - curio.bin = nil; - curio.peekc = 0; - curio.peekc1 = 0; - curio.infile = file; - curio.cp = cp; - curio.nlsemi = 0; - curio.importsafe = 0; - - typecheckok = 1; - incannedimport = 1; -} - -static int -isfrog(int c) -{ - // complain about possibly invisible control characters - if(c < ' ') { - return !yy_isspace(c); // exclude good white space - } - if(0x7f <= c && c <= 0xa0) // DEL, unicode block including unbreakable space. - return 1; - return 0; -} - -typedef struct Loophack Loophack; -struct Loophack { - int v; - Loophack *next; -}; - -static int32 -_yylex(void) -{ - int c, c1, clen, escflag, ncp; - vlong v; - char *cp, *ep; - Rune rune; - Sym *s; - static Loophack *lstk; - Loophack *h; - - prevlineno = lineno; - -l0: - c = getc(); - if(yy_isspace(c)) { - if(c == '\n' && curio.nlsemi) { - ungetc(c); - DBG("lex: implicit semi\n"); - return ';'; - } - goto l0; - } - - lineno = lexlineno; /* start of token */ - - if(c >= Runeself) { - /* all multibyte runes are alpha */ - cp = lexbuf; - ep = lexbuf+sizeof lexbuf; - goto talph; - } - - if(yy_isalpha(c)) { - cp = lexbuf; - ep = lexbuf+sizeof lexbuf; - goto talph; - } - - if(yy_isdigit(c)) - goto tnum; - - switch(c) { - case EOF: - lineno = prevlineno; - ungetc(EOF); - return -1; - - case '_': - cp = lexbuf; - ep = lexbuf+sizeof lexbuf; - goto talph; - - case '.': - c1 = getc(); - if(yy_isdigit(c1)) { - cp = lexbuf; - ep = lexbuf+sizeof lexbuf; - *cp++ = c; - c = c1; - goto casedot; - } - if(c1 == '.') { - c1 = getc(); - if(c1 == '.') { - c = LDDD; - goto lx; - } - ungetc(c1); - c1 = '.'; - } - break; - - case '"': - /* "..." */ - strcpy(lexbuf, "\"<string>\""); - cp = mal(8); - clen = sizeof(int32); - ncp = 8; - - for(;;) { - if(clen+UTFmax > ncp) { - cp = remal(cp, ncp, ncp); - ncp += ncp; - } - if(escchar('"', &escflag, &v)) - break; - if(v < Runeself || escflag) { - cp[clen++] = v; - } else { - rune = v; - c = runelen(rune); - runetochar(cp+clen, &rune); - clen += c; - } - } - goto strlit; - - case '`': - /* `...` */ - strcpy(lexbuf, "`<string>`"); - cp = mal(8); - clen = sizeof(int32); - ncp = 8; - - for(;;) { - if(clen+UTFmax > ncp) { - cp = remal(cp, ncp, ncp); - ncp += ncp; - } - c = getr(); - if(c == '\r') - continue; - if(c == EOF) { - yyerror("eof in string"); - break; - } - if(c == '`') - break; - rune = c; - clen += runetochar(cp+clen, &rune); - } - goto strlit; - - case '\'': - /* '.' */ - if(escchar('\'', &escflag, &v)) { - yyerror("empty character literal or unescaped ' in character literal"); - v = '\''; - } - if(!escchar('\'', &escflag, &v)) { - yyerror("missing '"); - ungetc(v); - } - yylval.val.u.xval = mal(sizeof(*yylval.val.u.xval)); - mpmovecfix(yylval.val.u.xval, v); - yylval.val.ctype = CTRUNE; - DBG("lex: codepoint literal\n"); - strcpy(litbuf, "string literal"); - return LLITERAL; - - case '/': - c1 = getc(); - if(c1 == '*') { - int nl; - - nl = 0; - for(;;) { - c = getr(); - if(c == '\n') - nl = 1; - while(c == '*') { - c = getr(); - if(c == '/') { - if(nl) - ungetc('\n'); - goto l0; - } - if(c == '\n') - nl = 1; - } - if(c == EOF) { - yyerror("eof in comment"); - errorexit(); - } - } - } - if(c1 == '/') { - c = getlinepragma(); - for(;;) { - if(c == '\n' || c == EOF) { - ungetc(c); - goto l0; - } - c = getr(); - } - } - if(c1 == '=') { - c = ODIV; - goto asop; - } - break; - - case ':': - c1 = getc(); - if(c1 == '=') { - c = LCOLAS; - yylval.i = lexlineno; - goto lx; - } - break; - - case '*': - c1 = getc(); - if(c1 == '=') { - c = OMUL; - goto asop; - } - break; - - case '%': - c1 = getc(); - if(c1 == '=') { - c = OMOD; - goto asop; - } - break; - - case '+': - c1 = getc(); - if(c1 == '+') { - c = LINC; - goto lx; - } - if(c1 == '=') { - c = OADD; - goto asop; - } - break; - - case '-': - c1 = getc(); - if(c1 == '-') { - c = LDEC; - goto lx; - } - if(c1 == '=') { - c = OSUB; - goto asop; - } - break; - - case '>': - c1 = getc(); - if(c1 == '>') { - c = LRSH; - c1 = getc(); - if(c1 == '=') { - c = ORSH; - goto asop; - } - break; - } - if(c1 == '=') { - c = LGE; - goto lx; - } - c = LGT; - break; - - case '<': - c1 = getc(); - if(c1 == '<') { - c = LLSH; - c1 = getc(); - if(c1 == '=') { - c = OLSH; - goto asop; - } - break; - } - if(c1 == '=') { - c = LLE; - goto lx; - } - if(c1 == '-') { - c = LCOMM; - goto lx; - } - c = LLT; - break; - - case '=': - c1 = getc(); - if(c1 == '=') { - c = LEQ; - goto lx; - } - break; - - case '!': - c1 = getc(); - if(c1 == '=') { - c = LNE; - goto lx; - } - break; - - case '&': - c1 = getc(); - if(c1 == '&') { - c = LANDAND; - goto lx; - } - if(c1 == '^') { - c = LANDNOT; - c1 = getc(); - if(c1 == '=') { - c = OANDNOT; - goto asop; - } - break; - } - if(c1 == '=') { - c = OAND; - goto asop; - } - break; - - case '|': - c1 = getc(); - if(c1 == '|') { - c = LOROR; - goto lx; - } - if(c1 == '=') { - c = OOR; - goto asop; - } - break; - - case '^': - c1 = getc(); - if(c1 == '=') { - c = OXOR; - goto asop; - } - break; - - /* - * clumsy dance: - * to implement rule that disallows - * if T{1}[0] { ... } - * but allows - * if (T{1}[0]) { ... } - * the block bodies for if/for/switch/select - * begin with an LBODY token, not '{'. - * - * when we see the keyword, the next - * non-parenthesized '{' becomes an LBODY. - * loophack is normally 0. - * a keyword makes it go up to 1. - * parens push loophack onto a stack and go back to 0. - * a '{' with loophack == 1 becomes LBODY and disables loophack. - * - * i said it was clumsy. - */ - case '(': - case '[': - if(loophack || lstk != nil) { - h = malloc(sizeof *h); - if(h == nil) { - flusherrors(); - yyerror("out of memory"); - errorexit(); - } - h->v = loophack; - h->next = lstk; - lstk = h; - loophack = 0; - } - goto lx; - case ')': - case ']': - if(lstk != nil) { - h = lstk; - loophack = h->v; - lstk = h->next; - free(h); - } - goto lx; - case '{': - if(loophack == 1) { - DBG("%L lex: LBODY\n", lexlineno); - loophack = 0; - return LBODY; - } - goto lx; - - default: - goto lx; - } - ungetc(c1); - -lx: - if(c > 0xff) - DBG("%L lex: TOKEN %s\n", lexlineno, lexname(c)); - else - DBG("%L lex: TOKEN '%c'\n", lexlineno, c); - if(isfrog(c)) { - yyerror("illegal character 0x%ux", c); - goto l0; - } - if(importpkg == nil && (c == '#' || c == '$' || c == '?' || c == '@' || c == '\\')) { - yyerror("%s: unexpected %c", "syntax error", c); - goto l0; - } - return c; - -asop: - yylval.i = c; // rathole to hold which asop - DBG("lex: TOKEN ASOP %c\n", c); - return LASOP; - -talph: - /* - * cp is set to lexbuf and some - * prefix has been stored - */ - for(;;) { - if(cp+10 >= ep) { - yyerror("identifier too long"); - errorexit(); - } - if(c >= Runeself) { - ungetc(c); - rune = getr(); - // 0xb7 ยท is used for internal names - if(!isalpharune(rune) && !isdigitrune(rune) && (importpkg == nil || rune != 0xb7)) - yyerror("invalid identifier character U+%04x", rune); - cp += runetochar(cp, &rune); - } else if(!yy_isalnum(c) && c != '_') - break; - else - *cp++ = c; - c = getc(); - } - *cp = 0; - ungetc(c); - - s = lookup(lexbuf); - switch(s->lexical) { - case LIGNORE: - goto l0; - - case LFOR: - case LIF: - case LSWITCH: - case LSELECT: - loophack = 1; // see comment about loophack above - break; - } - - DBG("lex: %S %s\n", s, lexname(s->lexical)); - yylval.sym = s; - return s->lexical; - -tnum: - cp = lexbuf; - ep = lexbuf+sizeof lexbuf; - if(c != '0') { - for(;;) { - if(cp+10 >= ep) { - yyerror("identifier too long"); - errorexit(); - } - *cp++ = c; - c = getc(); - if(yy_isdigit(c)) - continue; - goto dc; - } - } - *cp++ = c; - c = getc(); - if(c == 'x' || c == 'X') { - for(;;) { - if(cp+10 >= ep) { - yyerror("identifier too long"); - errorexit(); - } - *cp++ = c; - c = getc(); - if(yy_isdigit(c)) - continue; - if(c >= 'a' && c <= 'f') - continue; - if(c >= 'A' && c <= 'F') - continue; - if(cp == lexbuf+2) - yyerror("malformed hex constant"); - if(c == 'p') - goto caseep; - goto ncu; - } - } - - if(c == 'p') // 0p begins floating point zero - goto caseep; - - c1 = 0; - for(;;) { - if(cp+10 >= ep) { - yyerror("identifier too long"); - errorexit(); - } - if(!yy_isdigit(c)) - break; - if(c < '0' || c > '7') - c1 = 1; // not octal - *cp++ = c; - c = getc(); - } - if(c == '.') - goto casedot; - if(c == 'e' || c == 'E') - goto caseep; - if(c == 'i') - goto casei; - if(c1) - yyerror("malformed octal constant"); - goto ncu; - -dc: - if(c == '.') - goto casedot; - if(c == 'e' || c == 'E' || c == 'p' || c == 'P') - goto caseep; - if(c == 'i') - goto casei; - -ncu: - *cp = 0; - ungetc(c); - - yylval.val.u.xval = mal(sizeof(*yylval.val.u.xval)); - mpatofix(yylval.val.u.xval, lexbuf); - if(yylval.val.u.xval->ovf) { - yyerror("overflow in constant"); - mpmovecfix(yylval.val.u.xval, 0); - } - yylval.val.ctype = CTINT; - DBG("lex: integer literal\n"); - strcpy(litbuf, "literal "); - strcat(litbuf, lexbuf); - return LLITERAL; - -casedot: - for(;;) { - if(cp+10 >= ep) { - yyerror("identifier too long"); - errorexit(); - } - *cp++ = c; - c = getc(); - if(!yy_isdigit(c)) - break; - } - if(c == 'i') - goto casei; - if(c != 'e' && c != 'E') - goto caseout; - -caseep: - *cp++ = c; - c = getc(); - if(c == '+' || c == '-') { - *cp++ = c; - c = getc(); - } - if(!yy_isdigit(c)) - yyerror("malformed fp constant exponent"); - while(yy_isdigit(c)) { - if(cp+10 >= ep) { - yyerror("identifier too long"); - errorexit(); - } - *cp++ = c; - c = getc(); - } - if(c == 'i') - goto casei; - goto caseout; - -casei: - // imaginary constant - *cp = 0; - yylval.val.u.cval = mal(sizeof(*yylval.val.u.cval)); - mpmovecflt(&yylval.val.u.cval->real, 0.0); - mpatoflt(&yylval.val.u.cval->imag, lexbuf); - if(yylval.val.u.cval->imag.val.ovf) { - yyerror("overflow in imaginary constant"); - mpmovecflt(&yylval.val.u.cval->real, 0.0); - } - yylval.val.ctype = CTCPLX; - DBG("lex: imaginary literal\n"); - strcpy(litbuf, "literal "); - strcat(litbuf, lexbuf); - return LLITERAL; - -caseout: - *cp = 0; - ungetc(c); - - yylval.val.u.fval = mal(sizeof(*yylval.val.u.fval)); - mpatoflt(yylval.val.u.fval, lexbuf); - if(yylval.val.u.fval->val.ovf) { - yyerror("overflow in float constant"); - mpmovecflt(yylval.val.u.fval, 0.0); - } - yylval.val.ctype = CTFLT; - DBG("lex: floating literal\n"); - strcpy(litbuf, "literal "); - strcat(litbuf, lexbuf); - return LLITERAL; - -strlit: - *(int32*)cp = clen-sizeof(int32); // length - do { - cp[clen++] = 0; - } while(clen & MAXALIGN); - yylval.val.u.sval = (Strlit*)cp; - yylval.val.ctype = CTSTR; - DBG("lex: string literal\n"); - strcpy(litbuf, "string literal"); - return LLITERAL; -} - -static void pragcgo(char*); - -static int -more(char **pp) -{ - char *p; - - p = *pp; - while(yy_isspace(*p)) - p++; - *pp = p; - return *p != '\0'; -} - -/* - * read and interpret syntax that looks like - * //line parse.y:15 - * as a discontinuity in sequential line numbers. - * the next line of input comes from parse.y:15 - */ -static int -getlinepragma(void) -{ - int i, c, n; - char *cp, *ep, *linep; - Hist *h; - - c = getr(); - if(c == 'g') - goto go; - if(c != 'l') - goto out; - for(i=1; i<5; i++) { - c = getr(); - if(c != "line "[i]) - goto out; - } - - cp = lexbuf; - ep = lexbuf+sizeof(lexbuf)-5; - linep = nil; - for(;;) { - c = getr(); - if(c == EOF) - goto out; - if(c == '\n') - break; - if(c == ' ') - continue; - if(c == ':') - linep = cp; - if(cp < ep) - *cp++ = c; - } - *cp = 0; - - if(linep == nil || linep >= ep) - goto out; - *linep++ = '\0'; - n = 0; - for(cp=linep; *cp; cp++) { - if(*cp < '0' || *cp > '9') - goto out; - n = n*10 + *cp - '0'; - if(n > 1e8) { - yyerror("line number out of range"); - errorexit(); - } - } - if(n <= 0) - goto out; - - // try to avoid allocating file name over and over - for(h=ctxt->hist; h!=nil; h=h->link) { - if(h->name != nil && strcmp(h->name, lexbuf) == 0) { - linehist(h->name, n, 0); - goto out; - } - } - linehist(strdup(lexbuf), n, 0); - goto out; - -go: - cp = lexbuf; - ep = lexbuf+sizeof(lexbuf)-5; - *cp++ = 'g'; // already read - for(;;) { - c = getr(); - if(c == EOF || c >= Runeself) - goto out; - if(c == '\n') - break; - if(cp < ep) - *cp++ = c; - } - *cp = 0; - - if(strncmp(lexbuf, "go:cgo_", 7) == 0) - pragcgo(lexbuf); - - ep = strchr(lexbuf, ' '); - if(ep != nil) - *ep = 0; - - if(strcmp(lexbuf, "go:linkname") == 0) { - if(!imported_unsafe) - yyerror("//go:linkname only allowed in Go files that import \"unsafe\""); - if(ep == nil) { - yyerror("usage: //go:linkname localname linkname"); - goto out; - } - cp = ep+1; - while(yy_isspace(*cp)) - cp++; - ep = strchr(cp, ' '); - if(ep == nil) { - yyerror("usage: //go:linkname localname linkname"); - goto out; - } - *ep++ = 0; - while(yy_isspace(*ep)) - ep++; - if(*ep == 0) { - yyerror("usage: //go:linkname localname linkname"); - goto out; - } - lookup(cp)->linkname = strdup(ep); - goto out; - } - - if(strcmp(lexbuf, "go:nointerface") == 0 && fieldtrack_enabled) { - nointerface = 1; - goto out; - } - if(strcmp(lexbuf, "go:noescape") == 0) { - noescape = 1; - goto out; - } - if(strcmp(lexbuf, "go:nosplit") == 0) { - nosplit = 1; - goto out; - } - if(strcmp(lexbuf, "go:nowritebarrier") == 0) { - if(!compiling_runtime) - yyerror("//go:nowritebarrier only allowed in runtime"); - nowritebarrier = 1; - goto out; - } - -out: - return c; -} - -static char* -getimpsym(char **pp) -{ - char *p, *start; - - more(pp); // skip spaces - - p = *pp; - if(*p == '\0' || *p == '"') - return nil; - - start = p; - while(*p != '\0' && !yy_isspace(*p) && *p != '"') - p++; - if(*p != '\0') - *p++ = '\0'; - - *pp = p; - return start; -} - -static char* -getquoted(char **pp) -{ - char *p, *start; - - more(pp); // skip spaces - - p = *pp; - if(*p != '"') - return nil; - p++; - - start = p; - while(*p != '"') { - if(*p == '\0') - return nil; - p++; - } - *p++ = '\0'; - *pp = p; - return start; -} - -// Copied nearly verbatim from the C compiler's #pragma parser. -// TODO: Rewrite more cleanly once the compiler is written in Go. -static void -pragcgo(char *text) -{ - char *local, *remote, *p, *q, *verb; - - for(q=text; *q != '\0' && *q != ' '; q++) - ; - if(*q == ' ') - *q++ = '\0'; - - verb = text+3; // skip "go:" - - if(strcmp(verb, "cgo_dynamic_linker") == 0 || strcmp(verb, "dynlinker") == 0) { - p = getquoted(&q); - if(p == nil) - goto err1; - fmtprint(&pragcgobuf, "cgo_dynamic_linker %q\n", p); - goto out; - - err1: - yyerror("usage: //go:cgo_dynamic_linker \"path\""); - goto out; - } - - if(strcmp(verb, "dynexport") == 0) - verb = "cgo_export_dynamic"; - if(strcmp(verb, "cgo_export_static") == 0 || strcmp(verb, "cgo_export_dynamic") == 0) { - local = getimpsym(&q); - if(local == nil) - goto err2; - if(!more(&q)) { - fmtprint(&pragcgobuf, "%s %q\n", verb, local); - goto out; - } - remote = getimpsym(&q); - if(remote == nil) - goto err2; - fmtprint(&pragcgobuf, "%s %q %q\n", verb, local, remote); - goto out; - - err2: - yyerror("usage: //go:%s local [remote]", verb); - goto out; - } - - if(strcmp(verb, "cgo_import_dynamic") == 0 || strcmp(verb, "dynimport") == 0) { - local = getimpsym(&q); - if(local == nil) - goto err3; - if(!more(&q)) { - fmtprint(&pragcgobuf, "cgo_import_dynamic %q\n", local); - goto out; - } - remote = getimpsym(&q); - if(remote == nil) - goto err3; - if(!more(&q)) { - fmtprint(&pragcgobuf, "cgo_import_dynamic %q %q\n", local, remote); - goto out; - } - p = getquoted(&q); - if(p == nil) - goto err3; - fmtprint(&pragcgobuf, "cgo_import_dynamic %q %q %q\n", local, remote, p); - goto out; - - err3: - yyerror("usage: //go:cgo_import_dynamic local [remote [\"library\"]]"); - goto out; - } - - if(strcmp(verb, "cgo_import_static") == 0) { - local = getimpsym(&q); - if(local == nil || more(&q)) - goto err4; - fmtprint(&pragcgobuf, "cgo_import_static %q\n", local); - goto out; - - err4: - yyerror("usage: //go:cgo_import_static local"); - goto out; - } - - if(strcmp(verb, "cgo_ldflag") == 0) { - p = getquoted(&q); - if(p == nil) - goto err5; - fmtprint(&pragcgobuf, "cgo_ldflag %q\n", p); - goto out; - - err5: - yyerror("usage: //go:cgo_ldflag \"arg\""); - goto out; - } - -out:; -} - -int32 -yylex(void) -{ - int lx; - - lx = _yylex(); - - if(curio.nlsemi && lx == EOF) { - // Treat EOF as "end of line" for the purposes - // of inserting a semicolon. - lx = ';'; - } - - switch(lx) { - case LNAME: - case LLITERAL: - case LBREAK: - case LCONTINUE: - case LFALL: - case LRETURN: - case LINC: - case LDEC: - case ')': - case '}': - case ']': - curio.nlsemi = 1; - break; - default: - curio.nlsemi = 0; - break; - } - - // Track last two tokens returned by yylex. - yyprev = yylast; - yylast = lx; - return lx; -} - -static int -getc(void) -{ - int c, c1, c2; - - c = curio.peekc; - if(c != 0) { - curio.peekc = curio.peekc1; - curio.peekc1 = 0; - goto check; - } - - if(curio.bin == nil) { - c = *curio.cp & 0xff; - if(c != 0) - curio.cp++; - } else { - loop: - c = BGETC(curio.bin); - if(c == 0xef) { - c1 = BGETC(curio.bin); - c2 = BGETC(curio.bin); - if(c1 == 0xbb && c2 == 0xbf) { - yyerrorl(lexlineno, "Unicode (UTF-8) BOM in middle of file"); - goto loop; - } - Bungetc(curio.bin); - Bungetc(curio.bin); - } - } - -check: - switch(c) { - case 0: - if(curio.bin != nil) { - yyerror("illegal NUL byte"); - break; - } - case EOF: - // insert \n at EOF - if(curio.eofnl || curio.last == '\n') - return EOF; - curio.eofnl = 1; - c = '\n'; - case '\n': - if(pushedio.bin == nil) - lexlineno++; - break; - } - curio.last = c; - return c; -} - -static void -ungetc(int c) -{ - curio.peekc1 = curio.peekc; - curio.peekc = c; - if(c == '\n' && pushedio.bin == nil) - lexlineno--; -} - -static 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) { - lineno = lexlineno; - yyerror("illegal UTF-8 sequence"); - flusherrors(); - print("\t"); - for(c=0; c<i; c++) - print("%s%.2x", c > 0 ? " " : "", *(uchar*)(str+c)); - print("\n"); - } - return rune; -} - -static int -escchar(int e, int *escflg, vlong *val) -{ - int i, u, c; - vlong l; - - *escflg = 0; - - c = getr(); - switch(c) { - case EOF: - yyerror("eof in string"); - return 1; - case '\n': - yyerror("newline in string"); - return 1; - case '\\': - break; - default: - if(c == e) - return 1; - *val = c; - return 0; - } - - u = 0; - c = getr(); - switch(c) { - case 'x': - *escflg = 1; // it's a byte - i = 2; - goto hex; - - case 'u': - i = 4; - u = 1; - goto hex; - - case 'U': - i = 8; - u = 1; - goto hex; - - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - *escflg = 1; // it's a byte - goto oct; - - case 'a': c = '\a'; break; - case 'b': c = '\b'; break; - case 'f': c = '\f'; break; - case 'n': c = '\n'; break; - case 'r': c = '\r'; break; - case 't': c = '\t'; break; - case 'v': c = '\v'; break; - case '\\': c = '\\'; break; - - default: - if(c != e) - yyerror("unknown escape sequence: %c", c); - } - *val = c; - return 0; - -hex: - 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; - } - yyerror("non-hex character in escape sequence: %c", c); - ungetc(c); - break; - } - if(u && (l > Runemax || (0xd800 <= l && l < 0xe000))) { - yyerror("invalid Unicode code point in escape sequence: %#llx", l); - l = Runeerror; - } - *val = l; - return 0; - -oct: - l = c - '0'; - for(i=2; i>0; i--) { - c = getc(); - if(c >= '0' && c <= '7') { - l = l*8 + c-'0'; - continue; - } - yyerror("non-octal character in escape sequence: %c", c); - ungetc(c); - } - if(l > 255) - yyerror("octal escape value > 255: %d", l); - - *val = l; - return 0; -} - -static struct -{ - char* name; - int lexical; - int etype; - int op; -} syms[] = -{ -/* name lexical etype op - */ -/* basic types */ - {"int8", LNAME, TINT8, OXXX}, - {"int16", LNAME, TINT16, OXXX}, - {"int32", LNAME, TINT32, OXXX}, - {"int64", LNAME, TINT64, OXXX}, - - {"uint8", LNAME, TUINT8, OXXX}, - {"uint16", LNAME, TUINT16, OXXX}, - {"uint32", LNAME, TUINT32, OXXX}, - {"uint64", LNAME, TUINT64, OXXX}, - - {"float32", LNAME, TFLOAT32, OXXX}, - {"float64", LNAME, TFLOAT64, OXXX}, - - {"complex64", LNAME, TCOMPLEX64, OXXX}, - {"complex128", LNAME, TCOMPLEX128, OXXX}, - - {"bool", LNAME, TBOOL, OXXX}, - {"string", LNAME, TSTRING, OXXX}, - - {"any", LNAME, TANY, OXXX}, - - {"break", LBREAK, Txxx, OXXX}, - {"case", LCASE, Txxx, OXXX}, - {"chan", LCHAN, Txxx, OXXX}, - {"const", LCONST, Txxx, OXXX}, - {"continue", LCONTINUE, Txxx, OXXX}, - {"default", LDEFAULT, Txxx, OXXX}, - {"else", LELSE, Txxx, OXXX}, - {"defer", LDEFER, Txxx, OXXX}, - {"fallthrough", LFALL, Txxx, OXXX}, - {"for", LFOR, Txxx, OXXX}, - {"func", LFUNC, Txxx, OXXX}, - {"go", LGO, Txxx, OXXX}, - {"goto", LGOTO, Txxx, OXXX}, - {"if", LIF, Txxx, OXXX}, - {"import", LIMPORT, Txxx, OXXX}, - {"interface", LINTERFACE, Txxx, OXXX}, - {"map", LMAP, Txxx, OXXX}, - {"package", LPACKAGE, Txxx, OXXX}, - {"range", LRANGE, Txxx, OXXX}, - {"return", LRETURN, Txxx, OXXX}, - {"select", LSELECT, Txxx, OXXX}, - {"struct", LSTRUCT, Txxx, OXXX}, - {"switch", LSWITCH, Txxx, OXXX}, - {"type", LTYPE, Txxx, OXXX}, - {"var", LVAR, Txxx, OXXX}, - - {"append", LNAME, Txxx, OAPPEND}, - {"cap", LNAME, Txxx, OCAP}, - {"close", LNAME, Txxx, OCLOSE}, - {"complex", LNAME, Txxx, OCOMPLEX}, - {"copy", LNAME, Txxx, OCOPY}, - {"delete", LNAME, Txxx, ODELETE}, - {"imag", LNAME, Txxx, OIMAG}, - {"len", LNAME, Txxx, OLEN}, - {"make", LNAME, Txxx, OMAKE}, - {"new", LNAME, Txxx, ONEW}, - {"panic", LNAME, Txxx, OPANIC}, - {"print", LNAME, Txxx, OPRINT}, - {"println", LNAME, Txxx, OPRINTN}, - {"real", LNAME, Txxx, OREAL}, - {"recover", LNAME, Txxx, ORECOVER}, - - {"notwithstanding", LIGNORE, Txxx, OXXX}, - {"thetruthofthematter", LIGNORE, Txxx, OXXX}, - {"despiteallobjections", LIGNORE, Txxx, OXXX}, - {"whereas", LIGNORE, Txxx, OXXX}, - {"insofaras", LIGNORE, Txxx, OXXX}, -}; - -static void -lexinit(void) -{ - int i, lex; - Sym *s, *s1; - Type *t; - int etype; - Val v; - - /* - * initialize basic types array - * initialize known symbols - */ - for(i=0; i<nelem(syms); i++) { - lex = syms[i].lexical; - s = lookup(syms[i].name); - s->lexical = lex; - - etype = syms[i].etype; - if(etype != Txxx) { - if(etype < 0 || etype >= nelem(types)) - fatal("lexinit: %s bad etype", s->name); - s1 = pkglookup(syms[i].name, builtinpkg); - t = types[etype]; - if(t == T) { - t = typ(etype); - t->sym = s1; - - if(etype != TANY && etype != TSTRING) - dowidth(t); - types[etype] = t; - } - s1->lexical = LNAME; - s1->def = typenod(t); - continue; - } - - etype = syms[i].op; - if(etype != OXXX) { - s1 = pkglookup(syms[i].name, builtinpkg); - s1->lexical = LNAME; - s1->def = nod(ONAME, N, N); - s1->def->sym = s1; - s1->def->etype = etype; - s1->def->builtin = 1; - } - } - - // logically, the type of a string literal. - // types[TSTRING] is the named type string - // (the type of x in var x string or var x = "hello"). - // this is the ideal form - // (the type of x in const x = "hello"). - idealstring = typ(TSTRING); - idealbool = typ(TBOOL); - - s = pkglookup("true", builtinpkg); - s->def = nodbool(1); - s->def->sym = lookup("true"); - s->def->type = idealbool; - - s = pkglookup("false", builtinpkg); - s->def = nodbool(0); - s->def->sym = lookup("false"); - s->def->type = idealbool; - - s = lookup("_"); - s->block = -100; - s->def = nod(ONAME, N, N); - s->def->sym = s; - types[TBLANK] = typ(TBLANK); - s->def->type = types[TBLANK]; - nblank = s->def; - - s = pkglookup("_", builtinpkg); - s->block = -100; - s->def = nod(ONAME, N, N); - s->def->sym = s; - types[TBLANK] = typ(TBLANK); - s->def->type = types[TBLANK]; - - types[TNIL] = typ(TNIL); - s = pkglookup("nil", builtinpkg); - v.ctype = CTNIL; - s->def = nodlit(v); - s->def->sym = s; -} - -static void -lexinit1(void) -{ - Sym *s, *s1; - Type *t, *f, *rcvr, *in, *out; - - // t = interface { Error() string } - rcvr = typ(TSTRUCT); - rcvr->type = typ(TFIELD); - rcvr->type->type = ptrto(typ(TSTRUCT)); - rcvr->funarg = 1; - in = typ(TSTRUCT); - in->funarg = 1; - out = typ(TSTRUCT); - out->type = typ(TFIELD); - out->type->type = types[TSTRING]; - out->funarg = 1; - f = typ(TFUNC); - *getthis(f) = rcvr; - *getoutarg(f) = out; - *getinarg(f) = in; - f->thistuple = 1; - f->intuple = 0; - f->outnamed = 0; - f->outtuple = 1; - t = typ(TINTER); - t->type = typ(TFIELD); - t->type->sym = lookup("Error"); - t->type->type = f; - - // error type - s = lookup("error"); - s->lexical = LNAME; - s1 = pkglookup("error", builtinpkg); - errortype = t; - errortype->sym = s1; - s1->lexical = LNAME; - s1->def = typenod(errortype); - - // byte alias - s = lookup("byte"); - s->lexical = LNAME; - s1 = pkglookup("byte", builtinpkg); - bytetype = typ(TUINT8); - bytetype->sym = s1; - s1->lexical = LNAME; - s1->def = typenod(bytetype); - - // rune alias - s = lookup("rune"); - s->lexical = LNAME; - s1 = pkglookup("rune", builtinpkg); - runetype = typ(TINT32); - runetype->sym = s1; - s1->lexical = LNAME; - s1->def = typenod(runetype); -} - -static void -lexfini(void) -{ - Sym *s; - int lex, etype, i; - Val v; - - for(i=0; i<nelem(syms); i++) { - lex = syms[i].lexical; - if(lex != LNAME) - continue; - s = lookup(syms[i].name); - s->lexical = lex; - - etype = syms[i].etype; - if(etype != Txxx && (etype != TANY || debug['A']) && s->def == N) { - s->def = typenod(types[etype]); - s->origpkg = builtinpkg; - } - - etype = syms[i].op; - if(etype != OXXX && s->def == N) { - s->def = nod(ONAME, N, N); - s->def->sym = s; - s->def->etype = etype; - s->def->builtin = 1; - s->origpkg = builtinpkg; - } - } - - // backend-specific builtin types (e.g. int). - for(i=0; thearch.typedefs[i].name; i++) { - s = lookup(thearch.typedefs[i].name); - if(s->def == N) { - s->def = typenod(types[thearch.typedefs[i].etype]); - s->origpkg = builtinpkg; - } - } - - // there's only so much table-driven we can handle. - // these are special cases. - s = lookup("byte"); - if(s->def == N) { - s->def = typenod(bytetype); - s->origpkg = builtinpkg; - } - - s = lookup("error"); - if(s->def == N) { - s->def = typenod(errortype); - s->origpkg = builtinpkg; - } - - s = lookup("rune"); - if(s->def == N) { - s->def = typenod(runetype); - s->origpkg = builtinpkg; - } - - s = lookup("nil"); - if(s->def == N) { - v.ctype = CTNIL; - s->def = nodlit(v); - s->def->sym = s; - s->origpkg = builtinpkg; - } - - s = lookup("iota"); - if(s->def == N) { - s->def = nod(OIOTA, N, N); - s->def->sym = s; - s->origpkg = builtinpkg; - } - - s = lookup("true"); - if(s->def == N) { - s->def = nodbool(1); - s->def->sym = s; - s->origpkg = builtinpkg; - } - - s = lookup("false"); - if(s->def == N) { - s->def = nodbool(0); - s->def->sym = s; - s->origpkg = builtinpkg; - } - - nodfp = nod(ONAME, N, N); - nodfp->type = types[TINT32]; - nodfp->xoffset = 0; - nodfp->class = PPARAM; - nodfp->sym = lookup(".fp"); -} - -struct -{ - int lex; - char* name; -} lexn[] = -{ - {LANDAND, "ANDAND"}, - {LANDNOT, "ANDNOT"}, - {LASOP, "ASOP"}, - {LBREAK, "BREAK"}, - {LCASE, "CASE"}, - {LCHAN, "CHAN"}, - {LCOLAS, "COLAS"}, - {LCOMM, "<-"}, - {LCONST, "CONST"}, - {LCONTINUE, "CONTINUE"}, - {LDDD, "..."}, - {LDEC, "DEC"}, - {LDEFAULT, "DEFAULT"}, - {LDEFER, "DEFER"}, - {LELSE, "ELSE"}, - {LEQ, "EQ"}, - {LFALL, "FALL"}, - {LFOR, "FOR"}, - {LFUNC, "FUNC"}, - {LGE, "GE"}, - {LGO, "GO"}, - {LGOTO, "GOTO"}, - {LGT, "GT"}, - {LIF, "IF"}, - {LIMPORT, "IMPORT"}, - {LINC, "INC"}, - {LINTERFACE, "INTERFACE"}, - {LLE, "LE"}, - {LLITERAL, "LITERAL"}, - {LLSH, "LSH"}, - {LLT, "LT"}, - {LMAP, "MAP"}, - {LNAME, "NAME"}, - {LNE, "NE"}, - {LOROR, "OROR"}, - {LPACKAGE, "PACKAGE"}, - {LRANGE, "RANGE"}, - {LRETURN, "RETURN"}, - {LRSH, "RSH"}, - {LSELECT, "SELECT"}, - {LSTRUCT, "STRUCT"}, - {LSWITCH, "SWITCH"}, - {LTYPE, "TYPE"}, - {LVAR, "VAR"}, -}; - -char* -lexname(int lex) -{ - int i; - static char buf[100]; - - for(i=0; i<nelem(lexn); i++) - if(lexn[i].lex == lex) - return lexn[i].name; - snprint(buf, sizeof(buf), "LEX-%d", lex); - return buf; -} - -struct -{ - char *have; - char *want; -} yytfix[] = -{ - {"$end", "EOF"}, - {"LLITERAL", "literal"}, - {"LASOP", "op="}, - {"LBREAK", "break"}, - {"LCASE", "case"}, - {"LCHAN", "chan"}, - {"LCOLAS", ":="}, - {"LCONST", "const"}, - {"LCONTINUE", "continue"}, - {"LDDD", "..."}, - {"LDEFAULT", "default"}, - {"LDEFER", "defer"}, - {"LELSE", "else"}, - {"LFALL", "fallthrough"}, - {"LFOR", "for"}, - {"LFUNC", "func"}, - {"LGO", "go"}, - {"LGOTO", "goto"}, - {"LIF", "if"}, - {"LIMPORT", "import"}, - {"LINTERFACE", "interface"}, - {"LMAP", "map"}, - {"LNAME", "name"}, - {"LPACKAGE", "package"}, - {"LRANGE", "range"}, - {"LRETURN", "return"}, - {"LSELECT", "select"}, - {"LSTRUCT", "struct"}, - {"LSWITCH", "switch"}, - {"LTYPE", "type"}, - {"LVAR", "var"}, - {"LANDAND", "&&"}, - {"LANDNOT", "&^"}, - {"LBODY", "{"}, - {"LCOMM", "<-"}, - {"LDEC", "--"}, - {"LINC", "++"}, - {"LEQ", "=="}, - {"LGE", ">="}, - {"LGT", ">"}, - {"LLE", "<="}, - {"LLT", "<"}, - {"LLSH", "<<"}, - {"LRSH", ">>"}, - {"LOROR", "||"}, - {"LNE", "!="}, - - // spell out to avoid confusion with punctuation in error messages - {"';'", "semicolon or newline"}, - {"','", "comma"}, -}; - -static void -yytinit(void) -{ - int i, j; - extern char *yytname[]; - char *s, *t; - - for(i=0; yytname[i] != nil; i++) { - s = yytname[i]; - - if(strcmp(s, "LLITERAL") == 0) { - strcpy(litbuf, "literal"); - yytname[i] = litbuf; - goto loop; - } - - // apply yytfix if possible - for(j=0; j<nelem(yytfix); j++) { - if(strcmp(s, yytfix[j].have) == 0) { - yytname[i] = yytfix[j].want; - goto loop; - } - } - - // turn 'x' into x. - if(s[0] == '\'') { - t = strdup(s+1); - t[strlen(t)-1] = '\0'; - yytname[i] = t; - } - loop:; - } -} - -static void -pkgnotused(int lineno, Strlit *path, char *name) -{ - char *elem; - - // If the package was imported with a name other than the final - // import path element, show it explicitly in the error message. - // Note that this handles both renamed imports and imports of - // packages containing unconventional package declarations. - // Note that this uses / always, even on Windows, because Go import - // paths always use forward slashes. - elem = strrchr(path->s, '/'); - if(elem != nil) - elem++; - else - elem = path->s; - if(name == nil || strcmp(elem, name) == 0) - yyerrorl(lineno, "imported and not used: \"%Z\"", path); - else - yyerrorl(lineno, "imported and not used: \"%Z\" as %s", path, name); -} - -void -mkpackage(char* pkgname) -{ - Sym *s; - int32 h; - char *p, *q; - - if(localpkg->name == nil) { - if(strcmp(pkgname, "_") == 0) - yyerror("invalid package name _"); - localpkg->name = pkgname; - } else { - if(strcmp(pkgname, localpkg->name) != 0) - yyerror("package %s; expected %s", pkgname, localpkg->name); - for(h=0; h<NHASH; h++) { - for(s = hash[h]; s != S; s = s->link) { - if(s->def == N || s->pkg != localpkg) - continue; - if(s->def->op == OPACK) { - // throw away top-level package name leftover - // from previous file. - // leave s->block set to cause redeclaration - // errors if a conflicting top-level name is - // introduced by a different file. - if(!s->def->used && !nsyntaxerrors) - pkgnotused(s->def->lineno, s->def->pkg->path, s->name); - s->def = N; - continue; - } - if(s->def->sym != s) { - // throw away top-level name left over - // from previous import . "x" - if(s->def->pack != N && !s->def->pack->used && !nsyntaxerrors) { - pkgnotused(s->def->pack->lineno, s->def->pack->pkg->path, nil); - s->def->pack->used = 1; - } - s->def = N; - continue; - } - } - } - } - - if(outfile == nil) { - p = strrchr(infile, '/'); - if(ctxt->windows) { - q = strrchr(infile, '\\'); - if(q > p) - p = q; - } - if(p == nil) - p = infile; - else - p = p+1; - snprint(namebuf, sizeof(namebuf), "%s", p); - p = strrchr(namebuf, '.'); - if(p != nil) - *p = 0; - outfile = smprint("%s.%c", namebuf, thearch.thechar); - } -} |