// 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. //go:generate go tool yacc go.y //go:generate go run mkbuiltin.go runtime unsafe package gc import ( "bytes" "cmd/internal/obj" "flag" "fmt" "io" "log" "os" "path" "strconv" "strings" "unicode" "unicode/utf8" ) var yychar_lex int var yyprev int var yylast int var imported_unsafe int var goos string var goarch string var goroot string // 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. var debugtab = []struct { name string val *int }{struct { name string val *int }{"nil", &Debug_checknil}} // Our own isdigit, isspace, isalpha, isalnum that take care // of EOF and other out of range arguments. func yy_isdigit(c int) bool { return c >= 0 && c <= 0xFF && isdigit(c) } func yy_isspace(c int) bool { return c == ' ' || c == '\t' || c == '\n' || c == '\r' } func yy_isalpha(c int) bool { return 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' } func yy_isalnum(c int) bool { return c >= 0 && c <= 0xFF && isalnum(c) } // Disallow use of isdigit etc. const ( EOF = -1 ) func usage() { fmt.Printf("usage: %cg [options] file.go...\n", Thearch.Thechar) obj.Flagprint(1) Exit(2) } func hidePanic() { if nsavederrors+nerrors > 0 { // If we've already complained about things // in the program, don't bother complaining // about a panic too; let the user clean up // the code and try again. if err := recover(); err != nil { errorexit() } } } func doversion() { p := obj.Expstring() if p == "X:none" { p = "" } sep := "" if p != "" { sep = " " } fmt.Printf("%cg version %s%s%s\n", Thearch.Thechar, obj.Getgoversion(), sep, p) os.Exit(0) } func Main() { defer hidePanic() // Allow GOARCH=thearch.thestring or GOARCH=thearch.thestringsuffix, // but not other values. p := obj.Getgoarch() if !strings.HasPrefix(p, Thearch.Thestring) { log.Fatalf("cannot use %cg with GOARCH=%s", Thearch.Thechar, p) } goarch = p Thearch.Linkarchinit() Ctxt = obj.Linknew(Thearch.Thelinkarch) Ctxt.Diag = Yyerror Ctxt.Bso = &bstdout bstdout = *obj.Binitw(os.Stdout) 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 = obj.Getgoroot() goos = obj.Getgoos() Nacl = goos == "nacl" if Nacl { flag_largemodel = 1 } outfile = "" obj.Flagcount("+", "compiling runtime", &compiling_runtime) obj.Flagcount("%", "debug non-static initializers", &Debug['%']) obj.Flagcount("A", "for bootstrapping, allow 'any' type", &Debug['A']) obj.Flagcount("B", "disable bounds checking", &Debug['B']) obj.Flagstr("D", "path: set relative path for local imports", &localimport) obj.Flagcount("E", "debug symbol export", &Debug['E']) obj.Flagfn1("I", "dir: add dir to import search path", addidir) obj.Flagcount("K", "debug missing line numbers", &Debug['K']) obj.Flagcount("L", "use full (long) path in error messages", &Debug['L']) obj.Flagcount("M", "debug move generation", &Debug['M']) obj.Flagcount("N", "disable optimizations", &Debug['N']) obj.Flagcount("P", "debug peephole optimizer", &Debug['P']) obj.Flagcount("R", "debug register optimizer", &Debug['R']) obj.Flagcount("S", "print assembly listing", &Debug['S']) obj.Flagfn0("V", "print compiler version", doversion) obj.Flagcount("W", "debug parse tree after type checking", &Debug['W']) obj.Flagstr("asmhdr", "file: write assembly header to named file", &asmhdr) obj.Flagcount("complete", "compiling complete package (no C or assembly)", &pure_go) obj.Flagstr("d", "list: print debug information about items in list", &debugstr) obj.Flagcount("e", "no limit on number of errors reported", &Debug['e']) obj.Flagcount("f", "debug stack frames", &Debug['f']) obj.Flagcount("g", "debug code generation", &Debug['g']) obj.Flagcount("h", "halt on error", &Debug['h']) obj.Flagcount("i", "debug line number stack", &Debug['i']) obj.Flagstr("installsuffix", "pkg directory suffix", &flag_installsuffix) obj.Flagcount("j", "debug runtime-initialized variables", &Debug['j']) obj.Flagcount("l", "disable inlining", &Debug['l']) obj.Flagcount("live", "debug liveness analysis", &debuglive) obj.Flagcount("m", "print optimization decisions", &Debug['m']) obj.Flagcount("nolocalimports", "reject local (relative) imports", &nolocalimports) obj.Flagstr("o", "obj: set output file", &outfile) obj.Flagstr("p", "path: set expected package import path", &myimportpath) obj.Flagcount("pack", "write package file instead of object file", &writearchive) obj.Flagcount("r", "debug generated wrappers", &Debug['r']) obj.Flagcount("race", "enable race detector", &flag_race) obj.Flagcount("s", "warn about composite literals that can be simplified", &Debug['s']) obj.Flagstr("trimpath", "prefix: remove prefix from recorded source file paths", &Ctxt.Trimpath) obj.Flagcount("u", "reject unsafe code", &safemode) obj.Flagcount("v", "increase debug verbosity", &Debug['v']) obj.Flagcount("w", "debug type checking", &Debug['w']) use_writebarrier = 1 obj.Flagcount("wb", "enable write barrier", &use_writebarrier) obj.Flagcount("x", "debug lexer", &Debug['x']) obj.Flagcount("y", "debug declarations in canned imports (with -d)", &Debug['y']) if Thearch.Thechar == '6' { obj.Flagcount("largemodel", "generate code that assumes a large memory model", &flag_largemodel) } obj.Flagstr("cpuprofile", "file: write cpu profile to file", &cpuprofile) obj.Flagstr("memprofile", "file: write memory profile to file", &memprofile) obj.Flagparse(usage) Ctxt.Debugasm = int32(Debug['S']) Ctxt.Debugvlog = int32(Debug['v']) if flag.NArg() < 1 { usage() } startProfile() if flag_race != 0 { racepkg = mkpkg(newstrlit("runtime/race")) racepkg.Name = "race" } // parse -d argument if debugstr != "" { var j int f := strings.Split(debugstr, ",") for i := range f { if f[i] == "" { continue } for j = 0; j < len(debugtab); j++ { if debugtab[j].name == f[i] { if debugtab[j].val != nil { *debugtab[j].val = 1 } break } } if j >= len(debugtab) { log.Fatalf("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 := obj.Getgo386() if p == "387" { Use_sse = 0 } else if p == "sse2" { Use_sse = 1 } else { log.Fatalf("unsupported setting GO386=%s", p) } } Thearch.Betypeinit() if Widthptr == 0 { Fatal("betypeinit failed") } lexinit() typeinit() lexinit1() // TODO(rsc): Restore yytinit? blockgen = 1 dclcontext = PEXTERN nerrors = 0 lexlineno = 1 for _, infile = range flag.Args() { linehist(infile, 0, 0) curio.infile = infile var err error curio.bin, err = obj.Bopenr(infile) if err != nil { fmt.Printf("open %s: %v\n", infile, err) errorexit() } curio.peekc = 0 curio.peekc1 = 0 curio.nlsemi = 0 curio.eofnl = 0 curio.last = 0 // Skip initial BOM if present. if obj.Bgetrune(curio.bin) != obj.BOM { obj.Bungetrune(curio.bin) } block = 1 iota_ = -1000000 imported_unsafe = 0 yyparse() if nsyntaxerrors != 0 { errorexit() } linehist("", 0, 0) if curio.bin != nil { obj.Bterm(curio.bin) } } testdclstack() mkpackage(localpkg.Name) // final import not used checks lexfini() typecheckok = 1 if Debug['f'] != 0 { 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 != nil; 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 != nil; 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 != nil; 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 != nil; l = l.Next { if l.N.Op == ODCLFUNC && l.N.Closure != nil { Curfn = l.N capturevars(l.N) } } Curfn = nil if nsavederrors+nerrors != 0 { 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 != nil; l = l.Next { if l.N.Inl != nil { saveerrors() typecheckinl(l.N) } } if nsavederrors+nerrors != 0 { errorexit() } } if Debug['l'] != 0 { // Find functions that can be inlined and clone them before walk expands them. visitBottomUp(xtop, func(list *NodeList, recursive bool) { for l := list; l != nil; l = l.Next { if l.N.Op == ODCLFUNC { caninl(l.N) 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 != nil; l = l.Next { if l.N.Op == ODCLFUNC && l.N.Closure != nil { Curfn = l.N transformclosure(l.N) } } Curfn = nil // Phase 8: Compile top level functions. for l := xtop; l != nil; 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 != nil; l = l.Next { if l.N.Op == ONAME { typecheck(&l.N, Erv) } } if nerrors+nsavederrors != 0 { errorexit() } dumpobj() if asmhdr != "" { dumpasmhdr() } if nerrors+nsavederrors != 0 { errorexit() } Flusherrors() } func saveerrors() { nsavederrors += nerrors nerrors = 0 } func arsize(b *obj.Biobuf, name string) int { var buf [ArhdrSize]byte if _, err := io.ReadFull(b, buf[:]); err != nil { return -1 } aname := strings.Trim(string(buf[0:16]), " ") if !strings.HasPrefix(aname, name) { return -1 } asize := strings.Trim(string(buf[48:58]), " ") i, _ := strconv.Atoi(asize) return i } func skiptopkgdef(b *obj.Biobuf) bool { /* archive header */ p := obj.Brdline(b, '\n') if p == "" { return false } if obj.Blinelen(b) != 8 { return false } if p != "!\n" { return false } /* symbol table may be first; skip it */ sz := arsize(b, "__.GOSYMDEF") if sz >= 0 { obj.Bseek(b, int64(sz), 1) } else { obj.Bseek(b, 8, 0) } /* package export block is next */ sz = arsize(b, "__.PKGDEF") if sz <= 0 { return false } return true } func addidir(dir string) { if dir == "" { return } var pp **Idir for pp = &idirs; *pp != nil; pp = &(*pp).link { } *pp = new(Idir) (*pp).link = nil (*pp).dir = dir } // is this path a local name? begins with ./ or ../ or / func islocalname(name *Strlit) bool { return strings.HasPrefix(name.S, "/") || Ctxt.Windows != 0 && len(name.S) >= 3 && yy_isalpha(int(name.S[0])) && name.S[1] == ':' && name.S[2] == '/' || strings.HasPrefix(name.S, "./") || name.S == "." || strings.HasPrefix(name.S, "../") || name.S == ".." } func findpkg(name *Strlit) bool { if islocalname(name) { if safemode != 0 || nolocalimports != 0 { return false } // 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. namebuf = fmt.Sprintf("%v.a", Zconv(name, 0)) if obj.Access(namebuf, 0) >= 0 { return true } namebuf = fmt.Sprintf("%v.%c", Zconv(name, 0), Thearch.Thechar) if obj.Access(namebuf, 0) >= 0 { return true } return false } // local imports should be canonicalized already. // don't want to see "encoding/../encoding/base64" // as different from "encoding/base64". var q string _ = q if path.Clean(name.S) != name.S { Yyerror("non-canonical import path %v (should be %s)", Zconv(name, 0), q) return false } for p := idirs; p != nil; p = p.link { namebuf = fmt.Sprintf("%s/%v.a", p.dir, Zconv(name, 0)) if obj.Access(namebuf, 0) >= 0 { return true } namebuf = fmt.Sprintf("%s/%v.%c", p.dir, Zconv(name, 0), Thearch.Thechar) if obj.Access(namebuf, 0) >= 0 { return true } } if goroot != "" { suffix := "" suffixsep := "" if flag_installsuffix != "" { suffixsep = "_" suffix = flag_installsuffix } else if flag_race != 0 { suffixsep = "_" suffix = "race" } namebuf = fmt.Sprintf("%s/pkg/%s_%s%s%s/%v.a", goroot, goos, goarch, suffixsep, suffix, Zconv(name, 0)) if obj.Access(namebuf, 0) >= 0 { return true } namebuf = fmt.Sprintf("%s/pkg/%s_%s%s%s/%v.%c", goroot, goos, goarch, suffixsep, suffix, Zconv(name, 0), Thearch.Thechar) if obj.Access(namebuf, 0) >= 0 { return true } } return false } func fakeimport() { importpkg = mkpkg(newstrlit("fake")) cannedimports("fake.6", "$$\n") } func importfile(f *Val, line int) { if f.Ctype != CTSTR { Yyerror("import statement not a string") fakeimport() return } if len(f.U.Sval.S) == 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 f.U.Sval.S == "main" { Yyerror("cannot import \"main\"") errorexit() } if myimportpath != "" && f.U.Sval.S == myimportpath { Yyerror("import \"%v\" while compiling that package (import cycle)", Zconv(f.U.Sval, 0)) errorexit() } if f.U.Sval.S == "unsafe" { if safemode != 0 { 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 != "" { prefix = localimport } cleanbuf := prefix cleanbuf += "/" cleanbuf += path_.S cleanbuf = path.Clean(cleanbuf) path_ = newstrlit(cleanbuf) if isbadimport(path_) { fakeimport() return } } if !findpkg(path_) { Yyerror("can't find import: \"%v\"", Zconv(f.U.Sval, 0)) 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 != 0 { file := namebuf tag := "" if importpkg.Safe { tag = "safe" } p := fmt.Sprintf("package %s %s\n$$\n", importpkg.Name, tag) cannedimports(file, p) return } importpkg.Imported = 1 var err error var imp *obj.Biobuf imp, err = obj.Bopenr(namebuf) if err != nil { Yyerror("can't open import: \"%v\": %v", Zconv(f.U.Sval, 0), err) errorexit() } file := namebuf n := len(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 := obj.Brdstr(imp, '\n', 1) if p != "empty archive" { if !strings.HasPrefix(p, "go object ") { Yyerror("import %s: not a go object file", file) errorexit() } q := fmt.Sprintf("%s %s %s %s", obj.Getgoos(), obj.Getgoarch(), obj.Getgoversion(), obj.Expstring()) if p[10:] != q { Yyerror("import %s: object is [%s] expected [%s]", file, p[10:], q) errorexit() } } // assume files move (get installed) // so don't record the full path. linehist(file[n-len(path_.S)-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 var c int32 for { c = int32(getc()) if c == EOF { break } if c != '$' { continue } c = int32(getc()) if c == EOF { break } if c != '$' { continue } return } Yyerror("no import in \"%v\"", Zconv(f.U.Sval, 0)) unimportfile() } func unimportfile() { if curio.bin != nil { obj.Bterm(curio.bin) curio.bin = nil } else { lexlineno-- // re correct sys.6 line number } curio = pushedio pushedio.bin = nil incannedimport = 0 typecheckok = 0 } func cannedimports(file string, cp string) { 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 = false typecheckok = 1 incannedimport = 1 } func isfrog(c int) bool { // 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 true } return false } type Loophack struct { v int next *Loophack } var _yylex_lstk *Loophack func _yylex(yylval *yySymType) int32 { var c1 int var escflag int var v int64 var cp *bytes.Buffer var rune_ uint var s *Sym var h *Loophack var str string prevlineno = lineno l0: c := getc() if yy_isspace(c) { if c == '\n' && curio.nlsemi != 0 { ungetc(c) if Debug['x'] != 0 { fmt.Printf("lex: implicit semi\n") } return ';' } goto l0 } lineno = lexlineno /* start of token */ if c >= utf8.RuneSelf { /* all multibyte runes are alpha */ cp = &lexbuf cp.Reset() goto talph } if yy_isalpha(c) { cp = &lexbuf cp.Reset() goto talph } if yy_isdigit(c) { cp = &lexbuf cp.Reset() if c != '0' { for { cp.WriteByte(byte(c)) c = getc() if yy_isdigit(c) { continue } if c == '.' { goto casedot } if c == 'e' || c == 'E' || c == 'p' || c == 'P' { goto caseep } if c == 'i' { goto casei } goto ncu } } cp.WriteByte(byte(c)) c = getc() if c == 'x' || c == 'X' { for { cp.WriteByte(byte(c)) c = getc() if yy_isdigit(c) { continue } if c >= 'a' && c <= 'f' { continue } if c >= 'A' && c <= 'F' { continue } if lexbuf.Len() == 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 !yy_isdigit(c) { break } if c < '0' || c > '7' { c1 = 1 // not octal } cp.WriteByte(byte(c)) c = getc() } if c == '.' { goto casedot } if c == 'e' || c == 'E' { goto caseep } if c == 'i' { goto casei } if c1 != 0 { Yyerror("malformed octal constant") } goto ncu } switch c { case EOF: lineno = prevlineno ungetc(EOF) return -1 case '_': cp = &lexbuf cp.Reset() goto talph case '.': c1 = getc() if yy_isdigit(c1) { cp = &lexbuf cp.Reset() cp.WriteByte(byte(c)) c = c1 goto casedot } if c1 == '.' { c1 = getc() if c1 == '.' { c = LDDD goto lx } ungetc(c1) c1 = '.' } /* "..." */ case '"': lexbuf.Reset() lexbuf.WriteString(`""`) cp = &strbuf cp.Reset() for { if escchar('"', &escflag, &v) { break } if v < utf8.RuneSelf || escflag != 0 { cp.WriteByte(byte(v)) } else { rune_ = uint(v) cp.WriteRune(rune(rune_)) } } goto strlit /* `...` */ case '`': lexbuf.Reset() lexbuf.WriteString("``") cp = &strbuf cp.Reset() for { c = int(getr()) if c == '\r' { continue } if c == EOF { Yyerror("eof in string") break } if c == '`' { break } cp.WriteRune(rune(c)) } goto strlit /* '.' */ case '\'': if escchar('\'', &escflag, &v) { Yyerror("empty character literal or unescaped ' in character literal") v = '\'' } if !escchar('\'', &escflag, &v) { Yyerror("missing '") ungetc(int(v)) } yylval.val.U.Xval = new(Mpint) Mpmovecfix(yylval.val.U.Xval, v) yylval.val.Ctype = CTRUNE if Debug['x'] != 0 { fmt.Printf("lex: codepoint literal\n") } litbuf = "string literal" return LLITERAL case '/': c1 = getc() if c1 == '*' { nl := 0 for { c = int(getr()) if c == '\n' { nl = 1 } for c == '*' { c = int(getr()) if c == '/' { if nl != 0 { 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 = int(getr()) } } if c1 == '=' { c = ODIV goto asop } case ':': c1 = getc() if c1 == '=' { c = LCOLAS yylval.i = int(lexlineno) goto lx } case '*': c1 = getc() if c1 == '=' { c = OMUL goto asop } case '%': c1 = getc() if c1 == '=' { c = OMOD goto asop } case '+': c1 = getc() if c1 == '+' { c = LINC goto lx } if c1 == '=' { c = OADD goto asop } case '-': c1 = getc() if c1 == '-' { c = LDEC goto lx } if c1 == '=' { c = OSUB goto asop } case '>': c1 = getc() if c1 == '>' { c = LRSH c1 = getc() if c1 == '=' { c = ORSH goto asop } break } if c1 == '=' { c = LGE goto lx } c = LGT 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 case '=': c1 = getc() if c1 == '=' { c = LEQ goto lx } case '!': c1 = getc() if c1 == '=' { c = LNE goto lx } 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 } case '|': c1 = getc() if c1 == '|' { c = LOROR goto lx } if c1 == '=' { c = OOR goto asop } case '^': c1 = getc() if c1 == '=' { c = OXOR goto asop } /* * 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 '(', '[': if loophack != 0 || _yylex_lstk != nil { h = new(Loophack) if h == nil { Flusherrors() Yyerror("out of memory") errorexit() } h.v = loophack h.next = _yylex_lstk _yylex_lstk = h loophack = 0 } goto lx case ')', ']': if _yylex_lstk != nil { h = _yylex_lstk loophack = h.v _yylex_lstk = h.next } goto lx case '{': if loophack == 1 { if Debug['x'] != 0 { fmt.Printf("%v lex: LBODY\n", Ctxt.Line(int(lexlineno))) } loophack = 0 return LBODY } goto lx default: goto lx } ungetc(c1) lx: if c > 0xff { if Debug['x'] != 0 { fmt.Printf("%v lex: TOKEN %s\n", Ctxt.Line(int(lexlineno)), lexname(c)) } } else { if Debug['x'] != 0 { fmt.Printf("%v lex: TOKEN '%c'\n", Ctxt.Line(int(lexlineno)), c) } } if isfrog(c) { Yyerror("illegal character 0x%x", uint(c)) goto l0 } if importpkg == nil && (c == '#' || c == '$' || c == '?' || c == '@' || c == '\\') { Yyerror("%s: unexpected %c", "syntax error", c) goto l0 } return int32(c) asop: yylval.i = c // rathole to hold which asop if Debug['x'] != 0 { fmt.Printf("lex: TOKEN ASOP %c\n", c) } return LASOP /* * cp is set to lexbuf and some * prefix has been stored */ talph: for { if c >= utf8.RuneSelf { ungetc(c) rune_ = uint(getr()) // 0xb7 ยท is used for internal names if !unicode.IsLetter(rune(rune_)) && !unicode.IsDigit(rune(rune_)) && (importpkg == nil || rune_ != 0xb7) { Yyerror("invalid identifier character U+%04x", rune_) } cp.WriteRune(rune(rune_)) } else if !yy_isalnum(c) && c != '_' { break } else { cp.WriteByte(byte(c)) } c = getc() } cp = nil ungetc(c) s = Lookup(lexbuf.String()) switch s.Lexical { case LIGNORE: goto l0 case LFOR, LIF, LSWITCH, LSELECT: loophack = 1 // see comment about loophack above } if Debug['x'] != 0 { fmt.Printf("lex: %s %s\n", Sconv(s, 0), lexname(int(s.Lexical))) } yylval.sym = s return int32(s.Lexical) ncu: cp = nil ungetc(c) str = lexbuf.String() yylval.val.U.Xval = new(Mpint) mpatofix(yylval.val.U.Xval, str) if yylval.val.U.Xval.Ovf != 0 { Yyerror("overflow in constant") Mpmovecfix(yylval.val.U.Xval, 0) } yylval.val.Ctype = CTINT if Debug['x'] != 0 { fmt.Printf("lex: integer literal\n") } litbuf = "literal " + str return LLITERAL casedot: for { cp.WriteByte(byte(c)) c = getc() if !yy_isdigit(c) { break } } if c == 'i' { goto casei } if c != 'e' && c != 'E' { goto caseout } caseep: cp.WriteByte(byte(c)) c = getc() if c == '+' || c == '-' { cp.WriteByte(byte(c)) c = getc() } if !yy_isdigit(c) { Yyerror("malformed fp constant exponent") } for yy_isdigit(c) { cp.WriteByte(byte(c)) c = getc() } if c == 'i' { goto casei } goto caseout // imaginary constant casei: cp = nil str = lexbuf.String() yylval.val.U.Cval = new(Mpcplx) Mpmovecflt(&yylval.val.U.Cval.Real, 0.0) mpatoflt(&yylval.val.U.Cval.Imag, str) if yylval.val.U.Cval.Imag.Val.Ovf != 0 { Yyerror("overflow in imaginary constant") Mpmovecflt(&yylval.val.U.Cval.Real, 0.0) } yylval.val.Ctype = CTCPLX if Debug['x'] != 0 { fmt.Printf("lex: imaginary literal\n") } litbuf = "literal " + str return LLITERAL caseout: cp = nil ungetc(c) str = lexbuf.String() yylval.val.U.Fval = new(Mpflt) mpatoflt(yylval.val.U.Fval, str) if yylval.val.U.Fval.Val.Ovf != 0 { Yyerror("overflow in float constant") Mpmovecflt(yylval.val.U.Fval, 0.0) } yylval.val.Ctype = CTFLT if Debug['x'] != 0 { fmt.Printf("lex: floating literal\n") } litbuf = "literal " + str return LLITERAL strlit: yylval.val.U.Sval = &Strlit{S: cp.String()} yylval.val.Ctype = CTSTR if Debug['x'] != 0 { fmt.Printf("lex: string literal\n") } litbuf = "string literal" return LLITERAL } func more(pp *string) bool { p := *pp for p != "" && yy_isspace(int(p[0])) { p = p[1:] } *pp = p return p != "" } /* * 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 */ func getlinepragma() int { var cmd, verb, name string c := int(getr()) if c == 'g' { cp := &lexbuf cp.Reset() cp.WriteByte('g') // already read for { c = int(getr()) if c == EOF || c >= utf8.RuneSelf { return c } if c == '\n' { break } cp.WriteByte(byte(c)) } cp = nil if strings.HasPrefix(lexbuf.String(), "go:cgo_") { pragcgo(lexbuf.String()) } cmd = lexbuf.String() verb = cmd if i := strings.Index(verb, " "); i >= 0 { verb = verb[:i] } if verb == "go:linkname" { if imported_unsafe == 0 { Yyerror("//go:linkname only allowed in Go files that import \"unsafe\"") } f := strings.Fields(cmd) if len(f) != 3 { Yyerror("usage: //go:linkname localname linkname") return c } Lookup(f[1]).Linkname = f[2] return c } if verb == "go:nointerface" && obj.Fieldtrack_enabled != 0 { nointerface = true return c } if verb == "go:noescape" { noescape = true return c } if verb == "go:nosplit" { nosplit = true return c } if verb == "go:nowritebarrier" { if compiling_runtime == 0 { Yyerror("//go:nowritebarrier only allowed in runtime") } nowritebarrier = true return c } return c } if c != 'l' { return c } for i := 1; i < 5; i++ { c = int(getr()) if c != int("line "[i]) { return c } } cp := &lexbuf cp.Reset() linep := 0 for { c = int(getr()) if c == EOF { return c } if c == '\n' { break } if c == ' ' { continue } if c == ':' { linep = cp.Len() + 1 } cp.WriteByte(byte(c)) } cp = nil if linep == 0 { return c } n := 0 for _, c := range lexbuf.String()[linep:] { if c < '0' || c > '9' { goto out } n = n*10 + int(c) - '0' if n > 1e8 { Yyerror("line number out of range") errorexit() } } if n <= 0 { return c } // try to avoid allocating file name over and over name = lexbuf.String()[:linep-1] for h := Ctxt.Hist; h != nil; h = h.Link { if h.Name != "" && h.Name == name { linehist(h.Name, int32(n), 0) return c } } linehist(name, int32(n), 0) return c out: return c } func getimpsym(pp *string) string { more(pp) // skip spaces p := *pp if p == "" || p[0] == '"' { return "" } i := 0 for i < len(p) && !yy_isspace(int(p[i])) && p[i] != '"' { i++ } sym := p[:i] *pp = p[i:] return sym } func getquoted(pp *string) (string, bool) { more(pp) // skip spaces p := *pp if p == "" || p[0] != '"' { return "", false } p = p[1:] i := strings.Index(p, `"`) if i < 0 { return "", false } *pp = p[i+1:] return p[:i], true } // Copied nearly verbatim from the C compiler's #pragma parser. // TODO: Rewrite more cleanly once the compiler is written in Go. func pragcgo(text string) { var q string if i := strings.Index(text, " "); i >= 0 { text, q = text[:i], text[i:] } verb := text[3:] // skip "go:" if verb == "cgo_dynamic_linker" || verb == "dynlinker" { var ok bool var p string p, ok = getquoted(&q) if !ok { Yyerror("usage: //go:cgo_dynamic_linker \"path\"") return } pragcgobuf += fmt.Sprintf("cgo_dynamic_linker %v\n", plan9quote(p)) return } if verb == "dynexport" { verb = "cgo_export_dynamic" } if verb == "cgo_export_static" || verb == "cgo_export_dynamic" { local := getimpsym(&q) var remote string if local == "" { goto err2 } if !more(&q) { pragcgobuf += fmt.Sprintf("%s %v\n", verb, plan9quote(local)) return } remote = getimpsym(&q) if remote == "" { goto err2 } pragcgobuf += fmt.Sprintf("%s %v %v\n", verb, plan9quote(local), plan9quote(remote)) return err2: Yyerror("usage: //go:%s local [remote]", verb) return } if verb == "cgo_import_dynamic" || verb == "dynimport" { var ok bool local := getimpsym(&q) var p string var remote string if local == "" { goto err3 } if !more(&q) { pragcgobuf += fmt.Sprintf("cgo_import_dynamic %v\n", plan9quote(local)) return } remote = getimpsym(&q) if remote == "" { goto err3 } if !more(&q) { pragcgobuf += fmt.Sprintf("cgo_import_dynamic %v %v\n", plan9quote(local), plan9quote(remote)) return } p, ok = getquoted(&q) if !ok { goto err3 } pragcgobuf += fmt.Sprintf("cgo_import_dynamic %v %v %v\n", plan9quote(local), plan9quote(remote), plan9quote(p)) return err3: Yyerror("usage: //go:cgo_import_dynamic local [remote [\"library\"]]") return } if verb == "cgo_import_static" { local := getimpsym(&q) if local == "" || more(&q) { Yyerror("usage: //go:cgo_import_static local") return } pragcgobuf += fmt.Sprintf("cgo_import_static %v\n", plan9quote(local)) return } if verb == "cgo_ldflag" { var ok bool var p string p, ok = getquoted(&q) if !ok { Yyerror("usage: //go:cgo_ldflag \"arg\"") return } pragcgobuf += fmt.Sprintf("cgo_ldflag %v\n", plan9quote(p)) return } } type yy struct{} var yymsg []struct { yystate, yychar int msg string } func (yy) Lex(v *yySymType) int { return int(yylex(v)) } func (yy) Error(msg string) { Yyerror("%s", msg) } var theparser yyParser var parsing bool func yyparse() { theparser = yyNewParser() parsing = true theparser.Parse(yy{}) parsing = false } func yylex(yylval *yySymType) int32 { lx := int(_yylex(yylval)) if curio.nlsemi != 0 && lx == EOF { // Treat EOF as "end of line" for the purposes // of inserting a semicolon. lx = ';' } switch lx { case LNAME, LLITERAL, LBREAK, LCONTINUE, LFALL, LRETURN, LINC, LDEC, ')', '}', ']': curio.nlsemi = 1 default: curio.nlsemi = 0 } // Track last two tokens returned by yylex. yyprev = yylast yylast = lx return int32(lx) } func getc() int { c := curio.peekc if c != 0 { curio.peekc = curio.peekc1 curio.peekc1 = 0 goto check } if curio.bin == nil { if len(curio.cp) == 0 { c = 0 } else { c = int(curio.cp[0]) curio.cp = curio.cp[1:] } } else { var c1 int var c2 int loop: c = obj.Bgetc(curio.bin) if c == 0xef { c1 = obj.Bgetc(curio.bin) c2 = obj.Bgetc(curio.bin) if c1 == 0xbb && c2 == 0xbf { yyerrorl(int(lexlineno), "Unicode (UTF-8) BOM in middle of file") goto loop } obj.Bungetc(curio.bin) obj.Bungetc(curio.bin) } } check: switch c { case 0: if curio.bin != nil { Yyerror("illegal NUL byte") break } fallthrough // insert \n at EOF case EOF: if curio.eofnl != 0 || curio.last == '\n' { return EOF } curio.eofnl = 1 c = '\n' fallthrough case '\n': if pushedio.bin == nil { lexlineno++ } } curio.last = c return c } func ungetc(c int) { curio.peekc1 = curio.peekc curio.peekc = c if c == '\n' && pushedio.bin == nil { lexlineno-- } } func getr() int32 { var buf [utf8.UTFMax]byte for i := 0; ; i++ { c := getc() if i == 0 && c < utf8.RuneSelf { return int32(c) } buf[i] = byte(c) if i+1 == len(buf) || utf8.FullRune(buf[:i+1]) { r, w := utf8.DecodeRune(buf[:i+1]) if r == utf8.RuneError && w == 1 { lineno = lexlineno Yyerror("illegal UTF-8 sequence % x", buf[:i+1]) } return int32(r) } } } func escchar(e int, escflg *int, val *int64) bool { *escflg = 0 c := int(getr()) switch c { case EOF: Yyerror("eof in string") return true case '\n': Yyerror("newline in string") return true case '\\': break default: if c == e { return true } *val = int64(c) return false } u := 0 c = int(getr()) var i int 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', '1', '2', '3', '4', '5', '6', '7': *escflg = 1 // it's a byte l := int64(c) - '0' for i := 2; i > 0; i-- { c = getc() if c >= '0' && c <= '7' { l = l*8 + int64(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 false case 'a': c = '\a' case 'b': c = '\b' case 'f': c = '\f' case 'n': c = '\n' case 'r': c = '\r' case 't': c = '\t' case 'v': c = '\v' case '\\': c = '\\' default: if c != e { Yyerror("unknown escape sequence: %c", c) } } *val = int64(c) return false hex: l := int64(0) for ; i > 0; i-- { c = getc() if c >= '0' && c <= '9' { l = l*16 + int64(c) - '0' continue } if c >= 'a' && c <= 'f' { l = l*16 + int64(c) - 'a' + 10 continue } if c >= 'A' && c <= 'F' { l = l*16 + int64(c) - 'A' + 10 continue } Yyerror("non-hex character in escape sequence: %c", c) ungetc(c) break } if u != 0 && (l > utf8.MaxRune || (0xd800 <= l && l < 0xe000)) { Yyerror("invalid Unicode code point in escape sequence: %#x", l) l = utf8.RuneError } *val = l return false } var syms = []struct { name string lexical int etype int op int }{ /* name lexical etype op */ /* basic types */ struct { name string lexical int etype int op int }{"int8", LNAME, TINT8, OXXX}, struct { name string lexical int etype int op int }{"int16", LNAME, TINT16, OXXX}, struct { name string lexical int etype int op int }{"int32", LNAME, TINT32, OXXX}, struct { name string lexical int etype int op int }{"int64", LNAME, TINT64, OXXX}, struct { name string lexical int etype int op int }{"uint8", LNAME, TUINT8, OXXX}, struct { name string lexical int etype int op int }{"uint16", LNAME, TUINT16, OXXX}, struct { name string lexical int etype int op int }{"uint32", LNAME, TUINT32, OXXX}, struct { name string lexical int etype int op int }{"uint64", LNAME, TUINT64, OXXX}, struct { name string lexical int etype int op int }{"float32", LNAME, TFLOAT32, OXXX}, struct { name string lexical int etype int op int }{"float64", LNAME, TFLOAT64, OXXX}, struct { name string lexical int etype int op int }{"complex64", LNAME, TCOMPLEX64, OXXX}, struct { name string lexical int etype int op int }{"complex128", LNAME, TCOMPLEX128, OXXX}, struct { name string lexical int etype int op int }{"bool", LNAME, TBOOL, OXXX}, struct { name string lexical int etype int op int }{"string", LNAME, TSTRING, OXXX}, struct { name string lexical int etype int op int }{"any", LNAME, TANY, OXXX}, struct { name string lexical int etype int op int }{"break", LBREAK, Txxx, OXXX}, struct { name string lexical int etype int op int }{"case", LCASE, Txxx, OXXX}, struct { name string lexical int etype int op int }{"chan", LCHAN, Txxx, OXXX}, struct { name string lexical int etype int op int }{"const", LCONST, Txxx, OXXX}, struct { name string lexical int etype int op int }{"continue", LCONTINUE, Txxx, OXXX}, struct { name string lexical int etype int op int }{"default", LDEFAULT, Txxx, OXXX}, struct { name string lexical int etype int op int }{"else", LELSE, Txxx, OXXX}, struct { name string lexical int etype int op int }{"defer", LDEFER, Txxx, OXXX}, struct { name string lexical int etype int op int }{"fallthrough", LFALL, Txxx, OXXX}, struct { name string lexical int etype int op int }{"for", LFOR, Txxx, OXXX}, struct { name string lexical int etype int op int }{"func", LFUNC, Txxx, OXXX}, struct { name string lexical int etype int op int }{"go", LGO, Txxx, OXXX}, struct { name string lexical int etype int op int }{"goto", LGOTO, Txxx, OXXX}, struct { name string lexical int etype int op int }{"if", LIF, Txxx, OXXX}, struct { name string lexical int etype int op int }{"import", LIMPORT, Txxx, OXXX}, struct { name string lexical int etype int op int }{"interface", LINTERFACE, Txxx, OXXX}, struct { name string lexical int etype int op int }{"map", LMAP, Txxx, OXXX}, struct { name string lexical int etype int op int }{"package", LPACKAGE, Txxx, OXXX}, struct { name string lexical int etype int op int }{"range", LRANGE, Txxx, OXXX}, struct { name string lexical int etype int op int }{"return", LRETURN, Txxx, OXXX}, struct { name string lexical int etype int op int }{"select", LSELECT, Txxx, OXXX}, struct { name string lexical int etype int op int }{"struct", LSTRUCT, Txxx, OXXX}, struct { name string lexical int etype int op int }{"switch", LSWITCH, Txxx, OXXX}, struct { name string lexical int etype int op int }{"type", LTYPE, Txxx, OXXX}, struct { name string lexical int etype int op int }{"var", LVAR, Txxx, OXXX}, struct { name string lexical int etype int op int }{"append", LNAME, Txxx, OAPPEND}, struct { name string lexical int etype int op int }{"cap", LNAME, Txxx, OCAP}, struct { name string lexical int etype int op int }{"close", LNAME, Txxx, OCLOSE}, struct { name string lexical int etype int op int }{"complex", LNAME, Txxx, OCOMPLEX}, struct { name string lexical int etype int op int }{"copy", LNAME, Txxx, OCOPY}, struct { name string lexical int etype int op int }{"delete", LNAME, Txxx, ODELETE}, struct { name string lexical int etype int op int }{"imag", LNAME, Txxx, OIMAG}, struct { name string lexical int etype int op int }{"len", LNAME, Txxx, OLEN}, struct { name string lexical int etype int op int }{"make", LNAME, Txxx, OMAKE}, struct { name string lexical int etype int op int }{"new", LNAME, Txxx, ONEW}, struct { name string lexical int etype int op int }{"panic", LNAME, Txxx, OPANIC}, struct { name string lexical int etype int op int }{"print", LNAME, Txxx, OPRINT}, struct { name string lexical int etype int op int }{"println", LNAME, Txxx, OPRINTN}, struct { name string lexical int etype int op int }{"real", LNAME, Txxx, OREAL}, struct { name string lexical int etype int op int }{"recover", LNAME, Txxx, ORECOVER}, struct { name string lexical int etype int op int }{"notwithstanding", LIGNORE, Txxx, OXXX}, struct { name string lexical int etype int op int }{"thetruthofthematter", LIGNORE, Txxx, OXXX}, struct { name string lexical int etype int op int }{"despiteallobjections", LIGNORE, Txxx, OXXX}, struct { name string lexical int etype int op int }{"whereas", LIGNORE, Txxx, OXXX}, struct { name string lexical int etype int op int }{"insofaras", LIGNORE, Txxx, OXXX}, } func lexinit() { var lex int var s *Sym var s1 *Sym var t *Type var etype int /* * initialize basic types array * initialize known symbols */ for i := 0; i < len(syms); i++ { lex = syms[i].lexical s = Lookup(syms[i].name) s.Lexical = uint16(lex) etype = syms[i].etype if etype != Txxx { if etype < 0 || etype >= len(Types) { Fatal("lexinit: %s bad etype", s.Name) } s1 = Pkglookup(syms[i].name, builtinpkg) t = Types[etype] if t == nil { 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, nil, nil) s1.Def.Sym = s1 s1.Def.Etype = uint8(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(true) s.Def.Sym = Lookup("true") s.Def.Type = idealbool s = Pkglookup("false", builtinpkg) s.Def = Nodbool(false) s.Def.Sym = Lookup("false") s.Def.Type = idealbool s = Lookup("_") s.Block = -100 s.Def = Nod(ONAME, nil, nil) 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, nil, nil) s.Def.Sym = s Types[TBLANK] = typ(TBLANK) s.Def.Type = Types[TBLANK] Types[TNIL] = typ(TNIL) s = Pkglookup("nil", builtinpkg) var v Val v.Ctype = CTNIL s.Def = nodlit(v) s.Def.Sym = s } func lexinit1() { // 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) } func lexfini() { var s *Sym var lex int var etype int var i int for i = 0; i < len(syms); i++ { lex = syms[i].lexical if lex != LNAME { continue } s = Lookup(syms[i].name) s.Lexical = uint16(lex) etype = syms[i].etype if etype != Txxx && (etype != TANY || Debug['A'] != 0) && s.Def == nil { s.Def = typenod(Types[etype]) s.Origpkg = builtinpkg } etype = syms[i].op if etype != OXXX && s.Def == nil { s.Def = Nod(ONAME, nil, nil) s.Def.Sym = s s.Def.Etype = uint8(etype) s.Def.Builtin = 1 s.Origpkg = builtinpkg } } // backend-specific builtin types (e.g. int). for i = range Thearch.Typedefs { s = Lookup(Thearch.Typedefs[i].Name) if s.Def == nil { 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 == nil { s.Def = typenod(bytetype) s.Origpkg = builtinpkg } s = Lookup("error") if s.Def == nil { s.Def = typenod(errortype) s.Origpkg = builtinpkg } s = Lookup("rune") if s.Def == nil { s.Def = typenod(runetype) s.Origpkg = builtinpkg } s = Lookup("nil") if s.Def == nil { var v Val v.Ctype = CTNIL s.Def = nodlit(v) s.Def.Sym = s s.Origpkg = builtinpkg } s = Lookup("iota") if s.Def == nil { s.Def = Nod(OIOTA, nil, nil) s.Def.Sym = s s.Origpkg = builtinpkg } s = Lookup("true") if s.Def == nil { s.Def = Nodbool(true) s.Def.Sym = s s.Origpkg = builtinpkg } s = Lookup("false") if s.Def == nil { s.Def = Nodbool(false) s.Def.Sym = s s.Origpkg = builtinpkg } nodfp = Nod(ONAME, nil, nil) nodfp.Type = Types[TINT32] nodfp.Xoffset = 0 nodfp.Class = PPARAM nodfp.Sym = Lookup(".fp") } var lexn = []struct { lex int name string }{ struct { lex int name string }{LANDAND, "ANDAND"}, struct { lex int name string }{LANDNOT, "ANDNOT"}, struct { lex int name string }{LASOP, "ASOP"}, struct { lex int name string }{LBREAK, "BREAK"}, struct { lex int name string }{LCASE, "CASE"}, struct { lex int name string }{LCHAN, "CHAN"}, struct { lex int name string }{LCOLAS, "COLAS"}, struct { lex int name string }{LCOMM, "<-"}, struct { lex int name string }{LCONST, "CONST"}, struct { lex int name string }{LCONTINUE, "CONTINUE"}, struct { lex int name string }{LDDD, "..."}, struct { lex int name string }{LDEC, "DEC"}, struct { lex int name string }{LDEFAULT, "DEFAULT"}, struct { lex int name string }{LDEFER, "DEFER"}, struct { lex int name string }{LELSE, "ELSE"}, struct { lex int name string }{LEQ, "EQ"}, struct { lex int name string }{LFALL, "FALL"}, struct { lex int name string }{LFOR, "FOR"}, struct { lex int name string }{LFUNC, "FUNC"}, struct { lex int name string }{LGE, "GE"}, struct { lex int name string }{LGO, "GO"}, struct { lex int name string }{LGOTO, "GOTO"}, struct { lex int name string }{LGT, "GT"}, struct { lex int name string }{LIF, "IF"}, struct { lex int name string }{LIMPORT, "IMPORT"}, struct { lex int name string }{LINC, "INC"}, struct { lex int name string }{LINTERFACE, "INTERFACE"}, struct { lex int name string }{LLE, "LE"}, struct { lex int name string }{LLITERAL, "LITERAL"}, struct { lex int name string }{LLSH, "LSH"}, struct { lex int name string }{LLT, "LT"}, struct { lex int name string }{LMAP, "MAP"}, struct { lex int name string }{LNAME, "NAME"}, struct { lex int name string }{LNE, "NE"}, struct { lex int name string }{LOROR, "OROR"}, struct { lex int name string }{LPACKAGE, "PACKAGE"}, struct { lex int name string }{LRANGE, "RANGE"}, struct { lex int name string }{LRETURN, "RETURN"}, struct { lex int name string }{LRSH, "RSH"}, struct { lex int name string }{LSELECT, "SELECT"}, struct { lex int name string }{LSTRUCT, "STRUCT"}, struct { lex int name string }{LSWITCH, "SWITCH"}, struct { lex int name string }{LTYPE, "TYPE"}, struct { lex int name string }{LVAR, "VAR"}, } var lexname_buf string func lexname(lex int) string { for i := 0; i < len(lexn); i++ { if lexn[i].lex == lex { return lexn[i].name } } lexname_buf = fmt.Sprintf("LEX-%d", lex) return lexname_buf } var yytfix = []struct { have string want string }{ struct { have string want string }{"$end", "EOF"}, struct { have string want string }{"LLITERAL", "literal"}, struct { have string want string }{"LASOP", "op="}, struct { have string want string }{"LBREAK", "break"}, struct { have string want string }{"LCASE", "case"}, struct { have string want string }{"LCHAN", "chan"}, struct { have string want string }{"LCOLAS", ":="}, struct { have string want string }{"LCONST", "const"}, struct { have string want string }{"LCONTINUE", "continue"}, struct { have string want string }{"LDDD", "..."}, struct { have string want string }{"LDEFAULT", "default"}, struct { have string want string }{"LDEFER", "defer"}, struct { have string want string }{"LELSE", "else"}, struct { have string want string }{"LFALL", "fallthrough"}, struct { have string want string }{"LFOR", "for"}, struct { have string want string }{"LFUNC", "func"}, struct { have string want string }{"LGO", "go"}, struct { have string want string }{"LGOTO", "goto"}, struct { have string want string }{"LIF", "if"}, struct { have string want string }{"LIMPORT", "import"}, struct { have string want string }{"LINTERFACE", "interface"}, struct { have string want string }{"LMAP", "map"}, struct { have string want string }{"LNAME", "name"}, struct { have string want string }{"LPACKAGE", "package"}, struct { have string want string }{"LRANGE", "range"}, struct { have string want string }{"LRETURN", "return"}, struct { have string want string }{"LSELECT", "select"}, struct { have string want string }{"LSTRUCT", "struct"}, struct { have string want string }{"LSWITCH", "switch"}, struct { have string want string }{"LTYPE", "type"}, struct { have string want string }{"LVAR", "var"}, struct { have string want string }{"LANDAND", "&&"}, struct { have string want string }{"LANDNOT", "&^"}, struct { have string want string }{"LBODY", "{"}, struct { have string want string }{"LCOMM", "<-"}, struct { have string want string }{"LDEC", "--"}, struct { have string want string }{"LINC", "++"}, struct { have string want string }{"LEQ", "=="}, struct { have string want string }{"LGE", ">="}, struct { have string want string }{"LGT", ">"}, struct { have string want string }{"LLE", "<="}, struct { have string want string }{"LLT", "<"}, struct { have string want string }{"LLSH", "<<"}, struct { have string want string }{"LRSH", ">>"}, struct { have string want string }{"LOROR", "||"}, struct { have string want string }{"LNE", "!="}, // spell out to avoid confusion with punctuation in error messages struct { have string want string }{"';'", "semicolon or newline"}, struct { have string want string }{"','", "comma"}, } func pkgnotused(lineno int, path_ *Strlit, name string) { // 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 := path_.S if i := strings.LastIndex(elem, "/"); i >= 0 { elem = elem[i+1:] } if name == "" || elem == name { yyerrorl(int(lineno), "imported and not used: \"%v\"", Zconv(path_, 0)) } else { yyerrorl(int(lineno), "imported and not used: \"%v\" as %s", Zconv(path_, 0), name) } } func mkpackage(pkgname string) { if localpkg.Name == "" { if pkgname == "_" { Yyerror("invalid package name _") } localpkg.Name = pkgname } else { if pkgname != localpkg.Name { Yyerror("package %s; expected %s", pkgname, localpkg.Name) } var s *Sym for h := int32(0); h < NHASH; h++ { for s = hash[h]; s != nil; s = s.Link { if s.Def == nil || 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 == 0 && nsyntaxerrors == 0 { pkgnotused(int(s.Def.Lineno), s.Def.Pkg.Path, s.Name) } s.Def = nil continue } if s.Def.Sym != s { // throw away top-level name left over // from previous import . "x" if s.Def.Pack != nil && s.Def.Pack.Used == 0 && nsyntaxerrors == 0 { pkgnotused(int(s.Def.Pack.Lineno), s.Def.Pack.Pkg.Path, "") s.Def.Pack.Used = 1 } s.Def = nil continue } } } } if outfile == "" { p := infile if i := strings.LastIndex(p, "/"); i >= 0 { p = p[i+1:] } if Ctxt.Windows != 0 { if i := strings.LastIndex(p, `\`); i >= 0 { p = p[i+1:] } } namebuf = p if i := strings.LastIndex(namebuf, "."); i >= 0 { namebuf = namebuf[:i] } outfile = fmt.Sprintf("%s.%c", namebuf, Thearch.Thechar) } }