Golang程序  |  2654行  |  50.28 KB

// 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 yyprev int

var yylast int

var imported_unsafe int

var (
	goos    string
	goarch  string
	goroot  string
	buildid string
)

var (
	Debug_append int
	Debug_panic  int
	Debug_slice  int
	Debug_wb     int
)

// 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
}{
	{"append", &Debug_append},         // print information about append compilation
	{"disablenil", &Disable_checknil}, // disable nil checks
	{"gcprog", &Debug_gcprog},         // print dump of GC programs
	{"nil", &Debug_checknil},          // print information about nil checks
	{"panic", &Debug_panic},           // do not hide any compiler panic
	{"slice", &Debug_slice},           // print information about slice compilation
	{"typeassert", &Debug_typeassert}, // print information about type assertion inlining
	{"wb", &Debug_wb},                 // print information about write barriers
}

// 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 Debug_panic == 0 && 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("")
	localpkg.Prefix = "\"\""

	// pseudo-package, for scoping
	builtinpkg = mkpkg("go.builtin")

	builtinpkg.Prefix = "go.builtin" // not go%2ebuiltin

	// pseudo-package, accessed by import "unsafe"
	unsafepkg = mkpkg("unsafe")

	unsafepkg.Name = "unsafe"

	// real package, referred to by generated runtime calls
	Runtimepkg = mkpkg("runtime")

	Runtimepkg.Name = "runtime"

	// pseudo-packages used in symbol tables
	gostringpkg = mkpkg("go.string")

	gostringpkg.Name = "go.string"
	gostringpkg.Prefix = "go.string" // not go%2estring

	itabpkg = mkpkg("go.itab")

	itabpkg.Name = "go.itab"
	itabpkg.Prefix = "go.itab" // not go%2eitab

	weaktypepkg = mkpkg("go.weak.type")

	weaktypepkg.Name = "go.weak.type"
	weaktypepkg.Prefix = "go.weak.type" // not go%2eweak%2etype

	typelinkpkg = mkpkg("go.typelink")
	typelinkpkg.Name = "go.typelink"
	typelinkpkg.Prefix = "go.typelink" // not go%2etypelink

	trackpkg = mkpkg("go.track")

	trackpkg.Name = "go.track"
	trackpkg.Prefix = "go.track" // not go%2etrack

	typepkg = mkpkg("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", "set relative `path` for local imports", &localimport)
	obj.Flagcount("E", "debug symbol export", &Debug['E'])
	obj.Flagfn1("I", "add `directory` 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", "write assembly header to `file`", &asmhdr)
	obj.Flagstr("buildid", "record `id` as the build id in the export metadata", &buildid)
	obj.Flagcount("complete", "compiling complete package (no C or assembly)", &pure_go)
	obj.Flagstr("d", "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.Flagfn1("importmap", "add `definition` of the form source=actual to import map", addImportMap)
	obj.Flagstr("installsuffix", "set 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", "write output to `file`", &outfile)
	obj.Flagstr("p", "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", "remove `prefix` from recorded source file paths", &Ctxt.LineHist.TrimPathPrefix)
	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'])
	var flag_shared int
	var flag_dynlink bool
	if Thearch.Thechar == '6' {
		obj.Flagcount("largemodel", "generate code that assumes a large memory model", &flag_largemodel)
		obj.Flagcount("shared", "generate code that can be linked into a shared library", &flag_shared)
		flag.BoolVar(&flag_dynlink, "dynlink", false, "support references to Go symbols defined in other shared libraries")
	}
	obj.Flagstr("cpuprofile", "write cpu profile to `file`", &cpuprofile)
	obj.Flagstr("memprofile", "write memory profile to `file`", &memprofile)
	obj.Flagint64("memprofilerate", "set runtime.MemProfileRate to `rate`", &memprofilerate)
	obj.Flagparse(usage)

	if flag_dynlink {
		flag_shared = 1
	}
	Ctxt.Flag_shared = int32(flag_shared)
	Ctxt.Flag_dynlink = flag_dynlink

	Ctxt.Debugasm = int32(Debug['S'])
	Ctxt.Debugvlog = int32(Debug['v'])

	if flag.NArg() < 1 {
		usage()
	}

	startProfile()

	if flag_race != 0 {
		racepkg = mkpkg("runtime/race")
		racepkg.Name = "race"
	}

	// parse -d argument
	if debugstr != "" {
	Split:
		for _, name := range strings.Split(debugstr, ",") {
			if name == "" {
				continue
			}
			val := 1
			if i := strings.Index(name, "="); i >= 0 {
				var err error
				val, err = strconv.Atoi(name[i+1:])
				if err != nil {
					log.Fatalf("invalid debug value %v", name)
				}
				name = name[:i]
			}
			for _, t := range debugtab {
				if t.name == name {
					if t.val != nil {
						*t.val = val
						continue Split
					}
				}
			}
			log.Fatalf("unknown debug key -d %s\n", name)
		}
	}

	// 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']
	}

	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() {
		linehistpush(infile)

		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()
		}

		linehistpop()
		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 && l.N.Op != OAS2 {
			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 || l.N.Op == OAS2 {
			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.Func.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.Func.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.
	// Large values are also moved off stack in escape analysis;
	// because large values may contain pointers, it must happen early.
	escapes(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.Func.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()
}

var importMap = map[string]string{}

func addImportMap(s string) {
	if strings.Count(s, "=") != 1 {
		log.Fatal("-importmap argument must be of the form source=actual")
	}
	i := strings.Index(s, "=")
	source, actual := s[:i], s[i+1:]
	if source == "" || actual == "" {
		log.Fatal("-importmap argument must be of the form source=actual; source and actual must be non-empty")
	}
	importMap[source] = actual
}

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 != "!<arch>\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 string) bool {
	return strings.HasPrefix(name, "/") ||
		Ctxt.Windows != 0 && len(name) >= 3 && yy_isalpha(int(name[0])) && name[1] == ':' && name[2] == '/' ||
		strings.HasPrefix(name, "./") || name == "." ||
		strings.HasPrefix(name, "../") || name == ".."
}

func findpkg(name string) (file string, ok 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.
		file = fmt.Sprintf("%s.a", name)
		if obj.Access(file, 0) >= 0 {
			return file, true
		}
		file = fmt.Sprintf("%s.o", name)
		if obj.Access(file, 0) >= 0 {
			return file, 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) != name {
		Yyerror("non-canonical import path %q (should be %q)", name, q)
		return "", false
	}

	for p := idirs; p != nil; p = p.link {
		file = fmt.Sprintf("%s/%s.a", p.dir, name)
		if obj.Access(file, 0) >= 0 {
			return file, true
		}
		file = fmt.Sprintf("%s/%s.o", p.dir, name)
		if obj.Access(file, 0) >= 0 {
			return file, true
		}
	}

	if goroot != "" {
		suffix := ""
		suffixsep := ""
		if flag_installsuffix != "" {
			suffixsep = "_"
			suffix = flag_installsuffix
		} else if flag_race != 0 {
			suffixsep = "_"
			suffix = "race"
		}

		file = fmt.Sprintf("%s/pkg/%s_%s%s%s/%s.a", goroot, goos, goarch, suffixsep, suffix, name)
		if obj.Access(file, 0) >= 0 {
			return file, true
		}
		file = fmt.Sprintf("%s/pkg/%s_%s%s%s/%s.o", goroot, goos, goarch, suffixsep, suffix, name)
		if obj.Access(file, 0) >= 0 {
			return file, true
		}
	}

	return "", false
}

func fakeimport() {
	importpkg = mkpkg("fake")
	cannedimports("fake.o", "$$\n")
}

func importfile(f *Val, line int) {
	if _, ok := f.U.(string); !ok {
		Yyerror("import statement not a string")
		fakeimport()
		return
	}

	if len(f.U.(string)) == 0 {
		Yyerror("import path is empty")
		fakeimport()
		return
	}

	if isbadimport(f.U.(string)) {
		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.(string) == "main" {
		Yyerror("cannot import \"main\"")
		errorexit()
	}

	if myimportpath != "" && f.U.(string) == myimportpath {
		Yyerror("import %q while compiling that package (import cycle)", f.U.(string))
		errorexit()
	}

	if f.U.(string) == "unsafe" {
		if safemode != 0 {
			Yyerror("cannot import package unsafe")
			errorexit()
		}

		importpkg = mkpkg(f.U.(string))
		cannedimports("unsafe.o", unsafeimport)
		imported_unsafe = 1
		return
	}

	path_ := f.U.(string)

	if mapped, ok := importMap[path_]; ok {
		path_ = mapped
	}

	if islocalname(path_) {
		if path_[0] == '/' {
			Yyerror("import path cannot be absolute path")
			fakeimport()
			return
		}

		prefix := Ctxt.Pathname
		if localimport != "" {
			prefix = localimport
		}
		cleanbuf := prefix
		cleanbuf += "/"
		cleanbuf += path_
		cleanbuf = path.Clean(cleanbuf)
		path_ = cleanbuf

		if isbadimport(path_) {
			fakeimport()
			return
		}
	}

	file, found := findpkg(path_)
	if !found {
		Yyerror("can't find import: %q", f.U.(string))
		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 {
		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(file)
	if err != nil {
		Yyerror("can't open import: %q: %v", f.U.(string), err)
		errorexit()
	}

	if strings.HasSuffix(file, ".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.
	linehistpragma(file[len(file)-len(path_)-2:]) // 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 %q", f.U.(string))
	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(`"<string>"`)

		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("`<string>`")

		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))
		}

		x := new(Mpint)
		yylval.val.U = x
		Mpmovecfix(x, v)
		x.Rune = true
		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 = LookupBytes(lexbuf.Bytes())
	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", s, lexname(int(s.Lexical)))
	}
	yylval.sym = s
	return int32(s.Lexical)

ncu:
	cp = nil
	ungetc(c)

	str = lexbuf.String()
	yylval.val.U = new(Mpint)
	mpatofix(yylval.val.U.(*Mpint), str)
	if yylval.val.U.(*Mpint).Ovf {
		Yyerror("overflow in constant")
		Mpmovecfix(yylval.val.U.(*Mpint), 0)
	}

	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:
	if importpkg == nil && (c == 'p' || c == 'P') {
		// <mantissa>p<base-2-exponent> is allowed in .a/.o imports,
		// but not in .go sources.  See #9036.
		Yyerror("malformed floating point constant")
	}
	cp.WriteByte(byte(c))
	c = getc()
	if c == '+' || c == '-' {
		cp.WriteByte(byte(c))
		c = getc()
	}

	if !yy_isdigit(c) {
		Yyerror("malformed floating point 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 = new(Mpcplx)
	Mpmovecflt(&yylval.val.U.(*Mpcplx).Real, 0.0)
	mpatoflt(&yylval.val.U.(*Mpcplx).Imag, str)
	if yylval.val.U.(*Mpcplx).Imag.Val.IsInf() {
		Yyerror("overflow in imaginary constant")
		Mpmovecflt(&yylval.val.U.(*Mpcplx).Real, 0.0)
	}

	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 = newMpflt()
	mpatoflt(yylval.val.U.(*Mpflt), str)
	if yylval.val.U.(*Mpflt).Val.IsInf() {
		Yyerror("overflow in float constant")
		Mpmovecflt(yylval.val.U.(*Mpflt), 0.0)
	}

	if Debug['x'] != 0 {
		fmt.Printf("lex: floating literal\n")
	}
	litbuf = "literal " + str
	return LLITERAL

strlit:
	yylval.val.U = internString(cp.Bytes())
	if Debug['x'] != 0 {
		fmt.Printf("lex: string literal\n")
	}
	litbuf = "string literal"
	return LLITERAL
}

var internedStrings = map[string]string{}

func internString(b []byte) string {
	s, ok := internedStrings[string(b)] // string(b) here doesn't allocate
	if ok {
		return s
	}
	s = string(b)
	internedStrings[s] = s
	return s
}

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

		text := strings.TrimSuffix(lexbuf.String(), "\r")

		if strings.HasPrefix(text, "go:cgo_") {
			pragcgo(text)
		}

		cmd = text
		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:norace" {
			norace = true
			return c
		}

		if verb == "go:nosplit" {
			nosplit = true
			return c
		}

		if verb == "go:systemstack" {
			systemstack = 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
	}
	text := strings.TrimSuffix(lexbuf.String(), "\r")
	n := 0
	for _, c := range text[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
	}

	name = text[:linep-1]
	linehistupdate(name, n)
	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{}

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 {
	loop:
		c = obj.Bgetc(curio.bin)
		if c == 0xef {
			buf, err := curio.bin.Peek(2)
			if err != nil {
				log.Fatalf("getc: peeking: %v", err)
			}
			if buf[0] == 0xbb && buf[1] == 0xbf {
				yyerrorl(int(lexlineno), "Unicode (UTF-8) BOM in middle of file")

				// consume BOM bytes
				obj.Bgetc(curio.bin)
				obj.Bgetc(curio.bin)
				goto loop
			}
		}
	}

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
				// The string conversion here makes a copy for passing
				// to fmt.Printf, so that buf itself does not escape and can
				// be allocated on the stack.
				Yyerror("illegal UTF-8 sequence % x", string(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
}{
	/* 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},
}

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)
			s1.Def.Name = new(Name)
			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)
		}
	}

	// 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.Name = new(Name)
	s.Def.Type = idealbool

	s = Pkglookup("false", builtinpkg)
	s.Def = Nodbool(false)
	s.Def.Sym = Lookup("false")
	s.Def.Name = new(Name)
	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.U = new(NilVal)
	s.Def = nodlit(v)
	s.Def.Sym = s
	s.Def.Name = new(Name)
}

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)
	s1.Def.Name = new(Name)

	// 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)
	s1.Def.Name = new(Name)
}

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.Def.Name = new(Name)
			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.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.Def.Name = new(Name)
			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.Def.Name = new(Name)
		s.Origpkg = builtinpkg
	}

	s = Lookup("error")
	if s.Def == nil {
		s.Def = typenod(errortype)
		s.Def.Name = new(Name)
		s.Origpkg = builtinpkg
	}

	s = Lookup("rune")
	if s.Def == nil {
		s.Def = typenod(runetype)
		s.Def.Name = new(Name)
		s.Origpkg = builtinpkg
	}

	s = Lookup("nil")
	if s.Def == nil {
		var v Val
		v.U = new(NilVal)
		s.Def = nodlit(v)
		s.Def.Sym = s
		s.Def.Name = new(Name)
		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.Def.Name = new(Name)
		s.Origpkg = builtinpkg
	}

	s = Lookup("false")
	if s.Def == nil {
		s.Def = Nodbool(false)
		s.Def.Sym = s
		s.Def.Name = new(Name)
		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
}{
	{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"},
}

func lexname(lex int) string {
	for i := 0; i < len(lexn); i++ {
		if lexn[i].lex == lex {
			return lexn[i].name
		}
	}
	return fmt.Sprintf("LEX-%d", lex)
}

var yytfix = []struct {
	have string
	want string
}{
	{"$end", "EOF"},
	{"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"},
}

func init() {
	yyErrorVerbose = true

Outer:
	for i, s := range yyToknames {
		// Apply yytfix if possible.
		for _, fix := range yytfix {
			if s == fix.have {
				yyToknames[i] = fix.want
				continue Outer
			}
		}

		// Turn 'x' into x.
		if len(s) == 3 && s[0] == '\'' && s[2] == '\'' {
			yyToknames[i] = s[1:2]
			continue
		}
	}
}

func pkgnotused(lineno int, path string, 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
	if i := strings.LastIndex(elem, "/"); i >= 0 {
		elem = elem[i+1:]
	}
	if name == "" || elem == name {
		yyerrorl(int(lineno), "imported and not used: %q", path)
	} else {
		yyerrorl(int(lineno), "imported and not used: %q as %s", path, 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)
		}
		for _, s := range localpkg.Syms {
			if s.Def == nil {
				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 == 0 {
					pkgnotused(int(s.Def.Lineno), s.Def.Name.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.Name != nil && s.Def.Name.Pack != nil && !s.Def.Name.Pack.Used && nsyntaxerrors == 0 {
					pkgnotused(int(s.Def.Name.Pack.Lineno), s.Def.Name.Pack.Name.Pkg.Path, "")
					s.Def.Name.Pack.Used = true
				}

				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:]
			}
		}
		if i := strings.LastIndex(p, "."); i >= 0 {
			p = p[:i]
		}
		suffix := ".o"
		if writearchive > 0 {
			suffix = ".a"
		}
		outfile = p + suffix
	}
}