// 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 run mkbuiltin.go runtime unsafe

package gc

import (
	"bytes"
	"cmd/internal/obj"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"path"
	"strconv"
	"strings"
	"unicode"
	"unicode/utf8"
)

var imported_unsafe bool

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
	{"export", &Debug_export},         // print export data
}

const (
	EOF = -1
)

func usage() {
	fmt.Printf("usage: compile [options] file.go...\n")
	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("compile version %s%s%s\n", 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.DiagFunc = 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("msan", "build code compatible with C/C++ memory sanitizer", &flag_msan)
	obj.Flagcount("newexport", "use new export format", &newexport) // TODO(gri) remove eventually (issue 13241)
	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
	switch Thearch.Thechar {
	case '5', '6', '7', '8', '9':
		obj.Flagcount("shared", "generate code that can be linked into a shared library", &flag_shared)
	}
	if Thearch.Thechar == '6' {
		obj.Flagcount("largemodel", "generate code that assumes a large memory model", &flag_largemodel)
	}
	switch Thearch.Thechar {
	case '5', '6', '7', '8', '9':
		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"
	}
	if flag_msan != 0 {
		msanpkg = mkpkg("runtime/msan")
		msanpkg.Name = "msan"
	}
	if flag_race != 0 && flag_msan != 0 {
		log.Fatal("can not use both -race and -msan")
	} else if flag_race != 0 || flag_msan != 0 {
		instrumenting = true
	}

	// 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 {
		Fatalf("betypeinit failed")
	}

	lexinit()
	typeinit()
	lexinit1()

	blockgen = 1
	dclcontext = PEXTERN
	nerrors = 0
	lexlineno = 1
	const BOM = 0xFEFF

	for _, infile = range flag.Args() {
		if trace && Debug['x'] != 0 {
			fmt.Printf("--- %s ---\n", infile)
		}

		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 = false
		curio.eofnl = false
		curio.last = 0

		// Skip initial BOM if present.
		if obj.Bgetrune(curio.bin) != BOM {
			obj.Bungetrune(curio.bin)
		}

		block = 1
		iota_ = -1000000

		imported_unsafe = false

		parse_file()
		if nsyntaxerrors != 0 {
			errorexit()
		}

		linehistpop()
		if curio.bin != nil {
			obj.Bterm(curio.bin)
		}
	}

	testdclstack()
	mkpackage(localpkg.Name) // final import not used checks
	lexfini()

	typecheckok = true
	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 _, n := range importlist {
			if n.Func.Inl != nil {
				saveerrors()
				typecheckinl(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 []*Node, recursive bool) {
			// TODO: use a range statement here if the order does not matter
			for i := len(list) - 1; i >= 0; i-- {
				n := list[i]
				if n.Op == ODCLFUNC {
					caninl(n)
					inlcalls(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)
	}

	if compiling_runtime != 0 {
		checknowritebarrierrec()
	}

	// Phase 9: Check external declarations.
	for i, n := range externdcl {
		if n.Op == ONAME {
			typecheck(&externdcl[i], 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 && 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 _, err := os.Stat(file); err == nil {
			return file, true
		}
		file = fmt.Sprintf("%s.o", name)
		if _, err := os.Stat(file); err == nil {
			return file, true
		}
		return "", false
	}

	// local imports should be canonicalized already.
	// don't want to see "encoding/../encoding/base64"
	// as different from "encoding/base64".
	if q := path.Clean(name); q != 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 _, err := os.Stat(file); err == nil {
			return file, true
		}
		file = fmt.Sprintf("%s/%s.o", p.dir, name)
		if _, err := os.Stat(file); err == nil {
			return file, true
		}
	}

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

		file = fmt.Sprintf("%s/pkg/%s_%s%s%s/%s.a", goroot, goos, goarch, suffixsep, suffix, name)
		if _, err := os.Stat(file); err == nil {
			return file, true
		}
		file = fmt.Sprintf("%s/pkg/%s_%s%s%s/%s.o", goroot, goos, goarch, suffixsep, suffix, name)
		if _, err := os.Stat(file); err == nil {
			return file, true
		}
	}

	return "", false
}

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

// TODO(gri) line argument doesn't appear to be used
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()
	}

	path_ := f.U.(string)

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

	if path_ == "unsafe" {
		if safemode != 0 {
			Yyerror("cannot import package unsafe")
			errorexit()
		}

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

	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 {
		tag := ""
		if importpkg.Safe {
			tag = "safe"
		}

		p := fmt.Sprintf("package %s %s\n$$\n", importpkg.Name, tag)
		cannedimports(file, p)
		return
	}

	importpkg.Imported = true

	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

	// In the importfile, if we find:
	// $$\n  (old format): position the input right after $$\n and return
	// $$B\n (new format): import directly, then feed the lexer a dummy statement

	// look for $$
	var c int
	for {
		c = obj.Bgetc(imp)
		if c < 0 {
			break
		}
		if c == '$' {
			c = obj.Bgetc(imp)
			if c == '$' || c < 0 {
				break
			}
		}
	}

	// get character after $$
	if c >= 0 {
		c = obj.Bgetc(imp)
	}

	switch c {
	case '\n':
		// old export format
		pushedio = curio

		curio.bin = imp
		curio.peekc = 0
		curio.peekc1 = 0
		curio.infile = file
		curio.nlsemi = false
		typecheckok = true

		push_parser()

	case 'B':
		// new export format
		obj.Bgetc(imp) // skip \n after $$B
		Import(imp)

		// continue as if the package was imported before (see above)
		tag := ""
		if importpkg.Safe {
			tag = "safe"
		}
		p := fmt.Sprintf("package %s %s\n$$\n", importpkg.Name, tag)
		cannedimports(file, p)
		// Reset incannedimport flag (we are not truly in a
		// canned import) - this will cause importpkg.Direct to
		// be set via parser.import_package (was issue #13977).
		//
		// TODO(gri) Remove this global variable and convoluted
		// code in the process of streamlining the import code.
		incannedimport = 0

	default:
		Yyerror("no import in %q", f.U.(string))
	}
}

func unimportfile() {
	pop_parser()

	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 = false
}

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 = false
	curio.importsafe = false

	typecheckok = true
	incannedimport = 1

	push_parser()
}

func isSpace(c int) bool {
	return c == ' ' || c == '\t' || c == '\n' || c == '\r'
}

func isAlpha(c int) bool {
	return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z'
}

func isDigit(c int) bool {
	return '0' <= c && c <= '9'
}
func isAlnum(c int) bool {
	return isAlpha(c) || isDigit(c)
}

func plan9quote(s string) string {
	if s == "" {
		return "''"
	}
	for _, c := range s {
		if c <= ' ' || c == '\'' {
			return "'" + strings.Replace(s, "'", "''", -1) + "'"
		}
	}
	return s
}

func isfrog(c int) bool {
	// complain about possibly invisible control characters
	if c < ' ' {
		return !isSpace(c) // exclude good white space
	}

	if 0x7f <= c && c <= 0xa0 { // DEL, unicode block including unbreakable space.
		return true
	}
	return false
}

type yySymType struct {
	sym *Sym
	val Val
	op  Op
}

const (
	LLITERAL = 57346 + iota
	LASOP
	LCOLAS
	LBREAK
	LCASE
	LCHAN
	LCONST
	LCONTINUE
	LDDD
	LDEFAULT
	LDEFER
	LELSE
	LFALL
	LFOR
	LFUNC
	LGO
	LGOTO
	LIF
	LIMPORT
	LINTERFACE
	LMAP
	LNAME
	LPACKAGE
	LRANGE
	LRETURN
	LSELECT
	LSTRUCT
	LSWITCH
	LTYPE
	LVAR
	LANDAND
	LANDNOT
	LCOMM
	LDEC
	LEQ
	LGE
	LGT
	LIGNORE
	LINC
	LLE
	LLSH
	LLT
	LNE
	LOROR
	LRSH
)

func _yylex(yylval *yySymType) int32 {
	var c1 int
	var op Op
	var escflag int
	var v int64
	var cp *bytes.Buffer
	var s *Sym
	var str string

	prevlineno = lineno

l0:
	c := getc()
	if isSpace(c) {
		if c == '\n' && curio.nlsemi {
			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 isAlpha(c) {
		cp = &lexbuf
		cp.Reset()
		goto talph
	}

	if isDigit(c) {
		cp = &lexbuf
		cp.Reset()
		if c != '0' {
			for {
				cp.WriteByte(byte(c))
				c = getc()
				if 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 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 !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 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 {
				cp.WriteRune(rune(v))
			}
		}

		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 := false
			for {
				c = int(getr())
				if c == '\n' {
					nl = true
				}
				for c == '*' {
					c = int(getr())
					if c == '/' {
						if nl {
							ungetc('\n')
						}
						goto l0
					}

					if c == '\n' {
						nl = true
					}
				}

				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 == '=' {
			op = ODIV
			goto asop
		}

	case ':':
		c1 = getc()
		if c1 == '=' {
			c = int(LCOLAS)
			goto lx
		}

	case '*':
		c1 = getc()
		if c1 == '=' {
			op = OMUL
			goto asop
		}

	case '%':
		c1 = getc()
		if c1 == '=' {
			op = OMOD
			goto asop
		}

	case '+':
		c1 = getc()
		if c1 == '+' {
			c = int(LINC)
			goto lx
		}

		if c1 == '=' {
			op = OADD
			goto asop
		}

	case '-':
		c1 = getc()
		if c1 == '-' {
			c = int(LDEC)
			goto lx
		}

		if c1 == '=' {
			op = OSUB
			goto asop
		}

	case '>':
		c1 = getc()
		if c1 == '>' {
			c = int(LRSH)
			c1 = getc()
			if c1 == '=' {
				op = ORSH
				goto asop
			}

			break
		}

		if c1 == '=' {
			c = int(LGE)
			goto lx
		}

		c = int(LGT)

	case '<':
		c1 = getc()
		if c1 == '<' {
			c = int(LLSH)
			c1 = getc()
			if c1 == '=' {
				op = OLSH
				goto asop
			}

			break
		}

		if c1 == '=' {
			c = int(LLE)
			goto lx
		}

		if c1 == '-' {
			c = int(LCOMM)
			goto lx
		}

		c = int(LLT)

	case '=':
		c1 = getc()
		if c1 == '=' {
			c = int(LEQ)
			goto lx
		}

	case '!':
		c1 = getc()
		if c1 == '=' {
			c = int(LNE)
			goto lx
		}

	case '&':
		c1 = getc()
		if c1 == '&' {
			c = int(LANDAND)
			goto lx
		}

		if c1 == '^' {
			c = int(LANDNOT)
			c1 = getc()
			if c1 == '=' {
				op = OANDNOT
				goto asop
			}

			break
		}

		if c1 == '=' {
			op = OAND
			goto asop
		}

	case '|':
		c1 = getc()
		if c1 == '|' {
			c = int(LOROR)
			goto lx
		}

		if c1 == '=' {
			op = OOR
			goto asop
		}

	case '^':
		c1 = getc()
		if c1 == '=' {
			op = OXOR
			goto asop
		}

	default:
		goto lx
	}

	ungetc(c1)

lx:
	if Debug['x'] != 0 {
		if c > 0xff {
			fmt.Printf("%v lex: TOKEN %s\n", Ctxt.Line(int(lexlineno)), lexname(c))
		} else {
			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.op = op
	if Debug['x'] != 0 {
		fmt.Printf("lex: TOKEN ASOP %s=\n", goopnames[op])
	}
	return LASOP

	// cp is set to lexbuf and some
	// prefix has been stored
talph:
	for {
		if c >= utf8.RuneSelf {
			ungetc(c)
			r := rune(getr())

			// 0xb7 · is used for internal names
			if !unicode.IsLetter(r) && !unicode.IsDigit(r) && (importpkg == nil || r != 0xb7) {
				Yyerror("invalid identifier character U+%04x", r)
			}
			if cp.Len() == 0 && unicode.IsDigit(r) {
				Yyerror("identifier cannot begin with digit U+%04x", r)
			}
			cp.WriteRune(r)
		} else if !isAlnum(c) && c != '_' {
			break
		} else {
			cp.WriteByte(byte(c))
		}
		c = getc()
	}

	cp = nil
	ungetc(c)

	s = LookupBytes(lexbuf.Bytes())
	if s.Lexical == LIGNORE {
		goto l0
	}

	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 !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 !isDigit(c) {
		Yyerror("malformed floating point constant exponent")
	}
	for 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).Imag, 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 != "" && 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 {
				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:noinline" {
			noinline = true
			return c
		}

		if verb == "go:systemstack" {
			if compiling_runtime == 0 {
				Yyerror("//go:systemstack only allowed in runtime")
			}
			systemstack = true
			return c
		}

		if verb == "go:nowritebarrier" {
			if compiling_runtime == 0 {
				Yyerror("//go:nowritebarrier only allowed in runtime")
			}
			nowritebarrier = true
			return c
		}

		if verb == "go:nowritebarrierrec" {
			if compiling_runtime == 0 {
				Yyerror("//go:nowritebarrierrec only allowed in runtime")
			}
			nowritebarrierrec = true
			nowritebarrier = true // Implies nowritebarrier
			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) && !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" {
		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" {
		p, ok := getquoted(&q)
		if !ok {
			Yyerror("usage: //go:cgo_ldflag \"arg\"")
			return
		}
		pragcgobuf += fmt.Sprintf("cgo_ldflag %v\n", plan9quote(p))
		return

	}
}

func yylex(yylval *yySymType) int32 {
	lx := _yylex(yylval)

	if curio.nlsemi && 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 = true

	default:
		curio.nlsemi = false
	}

	return 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)
		// recognize BOM (U+FEFF): UTF-8 encoding is 0xef 0xbb 0xbf
		if c == 0xef {
			buf, err := curio.bin.Peek(2)
			if err != nil {
				yyerrorl(int(lexlineno), "illegal UTF-8 sequence ef % x followed by read error (%v)", string(buf), err)
				errorexit()
			}
			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 || curio.last == '\n' {
			return EOF
		}
		curio.eofnl = true
		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   EType
	op      Op
}{
	// basic types
	{"int8", LNAME, TINT8, OXXX},
	{"int16", LNAME, TINT16, OXXX},
	{"int32", LNAME, TINT32, OXXX},
	{"int64", LNAME, TINT64, OXXX},
	{"uint8", LNAME, TUINT8, OXXX},
	{"uint16", LNAME, TUINT16, OXXX},
	{"uint32", LNAME, TUINT32, OXXX},
	{"uint64", LNAME, TUINT64, OXXX},
	{"float32", LNAME, TFLOAT32, OXXX},
	{"float64", LNAME, TFLOAT64, OXXX},
	{"complex64", LNAME, TCOMPLEX64, OXXX},
	{"complex128", LNAME, TCOMPLEX128, OXXX},
	{"bool", LNAME, TBOOL, OXXX},
	{"string", LNAME, TSTRING, OXXX},
	{"any", LNAME, TANY, OXXX},
	{"break", LBREAK, Txxx, OXXX},
	{"case", LCASE, Txxx, OXXX},
	{"chan", LCHAN, Txxx, OXXX},
	{"const", LCONST, Txxx, OXXX},
	{"continue", LCONTINUE, Txxx, OXXX},
	{"default", LDEFAULT, Txxx, OXXX},
	{"else", LELSE, Txxx, OXXX},
	{"defer", LDEFER, Txxx, OXXX},
	{"fallthrough", LFALL, Txxx, OXXX},
	{"for", LFOR, Txxx, OXXX},
	{"func", LFUNC, Txxx, OXXX},
	{"go", LGO, Txxx, OXXX},
	{"goto", LGOTO, Txxx, OXXX},
	{"if", LIF, Txxx, OXXX},
	{"import", LIMPORT, Txxx, OXXX},
	{"interface", LINTERFACE, Txxx, OXXX},
	{"map", LMAP, Txxx, OXXX},
	{"package", LPACKAGE, Txxx, OXXX},
	{"range", LRANGE, Txxx, OXXX},
	{"return", LRETURN, Txxx, OXXX},
	{"select", LSELECT, Txxx, OXXX},
	{"struct", LSTRUCT, Txxx, OXXX},
	{"switch", LSWITCH, Txxx, OXXX},
	{"type", LTYPE, Txxx, OXXX},
	{"var", LVAR, Txxx, OXXX},
	{"append", LNAME, Txxx, OAPPEND},
	{"cap", LNAME, Txxx, OCAP},
	{"close", LNAME, Txxx, OCLOSE},
	{"complex", LNAME, Txxx, OCOMPLEX},
	{"copy", LNAME, Txxx, OCOPY},
	{"delete", LNAME, Txxx, ODELETE},
	{"imag", LNAME, Txxx, OIMAG},
	{"len", LNAME, Txxx, OLEN},
	{"make", LNAME, Txxx, OMAKE},
	{"new", LNAME, Txxx, ONEW},
	{"panic", LNAME, Txxx, OPANIC},
	{"print", LNAME, Txxx, OPRINT},
	{"println", LNAME, Txxx, OPRINTN},
	{"real", LNAME, Txxx, OREAL},
	{"recover", LNAME, Txxx, ORECOVER},
	{"notwithstanding", LIGNORE, Txxx, OXXX},
	{"thetruthofthematter", LIGNORE, Txxx, OXXX},
	{"despiteallobjections", LIGNORE, Txxx, OXXX},
	{"whereas", LIGNORE, Txxx, OXXX},
	{"insofaras", LIGNORE, Txxx, OXXX},
}

// lexinit initializes known symbols and the basic types.
func lexinit() {
	for _, s := range syms {
		lex := s.lexical
		s1 := Lookup(s.name)
		s1.Lexical = uint16(lex)

		if etype := s.etype; etype != Txxx {
			if int(etype) >= len(Types) {
				Fatalf("lexinit: %s bad etype", s.name)
			}
			s2 := Pkglookup(s.name, builtinpkg)
			t := Types[etype]
			if t == nil {
				t = typ(etype)
				t.Sym = s2

				if etype != TANY && etype != TSTRING {
					dowidth(t)
				}
				Types[etype] = t
			}

			s2.Lexical = LNAME
			s2.Def = typenod(t)
			s2.Def.Name = new(Name)
			continue
		}

		// TODO(marvin): Fix Node.EType type union.
		if etype := s.op; etype != OXXX {
			s2 := Pkglookup(s.name, builtinpkg)
			s2.Lexical = LNAME
			s2.Def = Nod(ONAME, nil, nil)
			s2.Def.Sym = s2
			s2.Def.Etype = EType(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 = true
	in := typ(TSTRUCT)
	in.Funarg = true
	out := typ(TSTRUCT)
	out.Type = typ(TFIELD)
	out.Type.Type = Types[TSTRING]
	out.Funarg = true
	f := typ(TFUNC)
	*getthis(f) = rcvr
	*Getoutarg(f) = out
	*getinarg(f) = in
	f.Thistuple = 1
	f.Intuple = 0
	f.Outnamed = false
	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() {
	for i := range syms {
		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
		}

		// TODO(marvin): Fix Node.EType type union.
		etype = EType(syms[i].op)
		if etype != EType(OXXX) && s.Def == nil {
			s.Def = Nod(ONAME, nil, nil)
			s.Def.Sym = s
			s.Def.Etype = 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.
	if s := Lookup("byte"); s.Def == nil {
		s.Def = typenod(bytetype)
		s.Def.Name = new(Name)
		s.Origpkg = builtinpkg
	}

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

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

	if s := Lookup("nil"); 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
	}

	if s := Lookup("iota"); s.Def == nil {
		s.Def = Nod(OIOTA, nil, nil)
		s.Def.Sym = s
		s.Origpkg = builtinpkg
	}

	if s := Lookup("true"); s.Def == nil {
		s.Def = Nodbool(true)
		s.Def.Sym = s
		s.Def.Name = new(Name)
		s.Origpkg = builtinpkg
	}

	if s := Lookup("false"); 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 = map[int]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 {
	if s, ok := lexn[lex]; ok {
		return s
	}
	return fmt.Sprintf("LEX-%d", lex)
}

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