// Copyright 2013 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. package gccgoimporter import ( "bytes" "errors" "fmt" "go/token" "io" "strconv" "strings" "text/scanner" "llvm.org/llgo/third_party/gotools/go/exact" "llvm.org/llgo/third_party/gotools/go/types" ) type parser struct { scanner scanner.Scanner tok rune // current token lit string // literal string; only valid for Ident, Int, String tokens pkgpath string // package path of imported package pkgname string // name of imported package pkg *types.Package // reference to imported package imports map[string]*types.Package // package path -> package object typeMap map[int]types.Type // type number -> type initdata InitData // package init priority data } func (p *parser) init(filename string, src io.Reader, imports map[string]*types.Package) { p.scanner.Init(src) p.scanner.Error = func(_ *scanner.Scanner, msg string) { p.error(msg) } p.scanner.Mode = scanner.ScanIdents | scanner.ScanInts | scanner.ScanFloats | scanner.ScanStrings | scanner.ScanComments | scanner.SkipComments p.scanner.Whitespace = 1<<'\t' | 1<<'\n' | 1<<' ' p.scanner.Filename = filename // for good error messages p.next() p.imports = imports p.typeMap = make(map[int]types.Type) } type importError struct { pos scanner.Position err error } func (e importError) Error() string { return fmt.Sprintf("import error %s (byte offset = %d): %s", e.pos, e.pos.Offset, e.err) } func (p *parser) error(err interface{}) { if s, ok := err.(string); ok { err = errors.New(s) } // panic with a runtime.Error if err is not an error panic(importError{p.scanner.Pos(), err.(error)}) } func (p *parser) errorf(format string, args ...interface{}) { p.error(fmt.Errorf(format, args...)) } func (p *parser) expect(tok rune) string { lit := p.lit if p.tok != tok { p.errorf("expected %s, got %s (%s)", scanner.TokenString(tok), scanner.TokenString(p.tok), lit) } p.next() return lit } func (p *parser) expectKeyword(keyword string) { lit := p.expect(scanner.Ident) if lit != keyword { p.errorf("expected keyword %s, got %q", keyword, lit) } } func (p *parser) parseString() string { str, err := strconv.Unquote(p.expect(scanner.String)) if err != nil { p.error(err) } return str } // unquotedString = { unquotedStringChar } . // unquotedStringChar = . func (p *parser) parseUnquotedString() string { if p.tok == scanner.EOF { p.error("unexpected EOF") } var buf bytes.Buffer buf.WriteString(p.scanner.TokenText()) // This loop needs to examine each character before deciding whether to consume it. If we see a semicolon, // we need to let it be consumed by p.next(). for ch := p.scanner.Peek(); ch != ';' && ch != scanner.EOF && p.scanner.Whitespace&(1< 0 { p.expect(',') } par, variadic := p.parseParam(pkg) list = append(list, par) if variadic { if isVariadic { p.error("... not on final argument") } isVariadic = true } } p.expect(')') return types.NewTuple(list...), isVariadic } // ResultList = Type | ParamList . func (p *parser) parseResultList(pkg *types.Package) *types.Tuple { switch p.tok { case '<': return types.NewTuple(types.NewParam(token.NoPos, pkg, "", p.parseType(pkg))) case '(': params, _ := p.parseParamList(pkg) return params default: return nil } } // FunctionType = ParamList ResultList . func (p *parser) parseFunctionType(pkg *types.Package) *types.Signature { params, isVariadic := p.parseParamList(pkg) results := p.parseResultList(pkg) return types.NewSignature(pkg.Scope(), nil, params, results, isVariadic) } // Func = Name FunctionType . func (p *parser) parseFunc(pkg *types.Package) *types.Func { name := p.parseName() if strings.ContainsRune(name, '$') { // This is a Type$equal or Type$hash function, which we don't want to parse, // except for the types. p.discardDirectiveWhileParsingTypes(pkg) return nil } return types.NewFunc(token.NoPos, pkg, name, p.parseFunctionType(pkg)) } // InterfaceType = "interface" "{" { ("?" Type | Func) ";" } "}" . func (p *parser) parseInterfaceType(pkg *types.Package) types.Type { p.expectKeyword("interface") var methods []*types.Func var typs []*types.Named p.expect('{') for p.tok != '}' && p.tok != scanner.EOF { if p.tok == '?' { p.next() typs = append(typs, p.parseType(pkg).(*types.Named)) } else { method := p.parseFunc(pkg) methods = append(methods, method) } p.expect(';') } p.expect('}') return types.NewInterface(methods, typs) } // PointerType = "*" ("any" | Type) . func (p *parser) parsePointerType(pkg *types.Package) types.Type { p.expect('*') if p.tok == scanner.Ident { p.expectKeyword("any") return types.Typ[types.UnsafePointer] } return types.NewPointer(p.parseType(pkg)) } // TypeDefinition = NamedType | MapType | ChanType | StructType | InterfaceType | PointerType | ArrayOrSliceType | FunctionType . func (p *parser) parseTypeDefinition(pkg *types.Package, n int) types.Type { var t types.Type switch p.tok { case scanner.String: t = p.parseNamedType(n) case scanner.Ident: switch p.lit { case "map": t = p.parseMapType(pkg) case "chan": t = p.parseChanType(pkg) case "struct": t = p.parseStructType(pkg) case "interface": t = p.parseInterfaceType(pkg) } case '*': t = p.parsePointerType(pkg) case '[': t = p.parseArrayOrSliceType(pkg) case '(': t = p.parseFunctionType(pkg) } p.typeMap[n] = t return t } const ( // From gofrontend/go/export.h // Note that these values are negative in the gofrontend and have been made positive // in the gccgoimporter. gccgoBuiltinINT8 = 1 gccgoBuiltinINT16 = 2 gccgoBuiltinINT32 = 3 gccgoBuiltinINT64 = 4 gccgoBuiltinUINT8 = 5 gccgoBuiltinUINT16 = 6 gccgoBuiltinUINT32 = 7 gccgoBuiltinUINT64 = 8 gccgoBuiltinFLOAT32 = 9 gccgoBuiltinFLOAT64 = 10 gccgoBuiltinINT = 11 gccgoBuiltinUINT = 12 gccgoBuiltinUINTPTR = 13 gccgoBuiltinBOOL = 15 gccgoBuiltinSTRING = 16 gccgoBuiltinCOMPLEX64 = 17 gccgoBuiltinCOMPLEX128 = 18 gccgoBuiltinERROR = 19 gccgoBuiltinBYTE = 20 gccgoBuiltinRUNE = 21 ) func lookupBuiltinType(typ int) types.Type { return [...]types.Type{ gccgoBuiltinINT8: types.Typ[types.Int8], gccgoBuiltinINT16: types.Typ[types.Int16], gccgoBuiltinINT32: types.Typ[types.Int32], gccgoBuiltinINT64: types.Typ[types.Int64], gccgoBuiltinUINT8: types.Typ[types.Uint8], gccgoBuiltinUINT16: types.Typ[types.Uint16], gccgoBuiltinUINT32: types.Typ[types.Uint32], gccgoBuiltinUINT64: types.Typ[types.Uint64], gccgoBuiltinFLOAT32: types.Typ[types.Float32], gccgoBuiltinFLOAT64: types.Typ[types.Float64], gccgoBuiltinINT: types.Typ[types.Int], gccgoBuiltinUINT: types.Typ[types.Uint], gccgoBuiltinUINTPTR: types.Typ[types.Uintptr], gccgoBuiltinBOOL: types.Typ[types.Bool], gccgoBuiltinSTRING: types.Typ[types.String], gccgoBuiltinCOMPLEX64: types.Typ[types.Complex64], gccgoBuiltinCOMPLEX128: types.Typ[types.Complex128], gccgoBuiltinERROR: types.Universe.Lookup("error").Type(), gccgoBuiltinBYTE: types.Typ[types.Byte], gccgoBuiltinRUNE: types.Typ[types.Rune], }[typ] } // Type = "<" "type" ( "-" int | int [ TypeDefinition ] ) ">" . func (p *parser) parseType(pkg *types.Package) (t types.Type) { p.expect('<') p.expectKeyword("type") switch p.tok { case scanner.Int: n := p.parseInt() if p.tok == '>' { t = p.typeMap[int(n)] } else { t = p.parseTypeDefinition(pkg, int(n)) } case '-': p.next() n := p.parseInt() t = lookupBuiltinType(int(n)) default: p.errorf("expected type number, got %s (%q)", scanner.TokenString(p.tok), p.lit) return nil } p.expect('>') return } // PackageInit = unquotedString unquotedString int . func (p *parser) parsePackageInit() PackageInit { name := p.parseUnquotedString() initfunc := p.parseUnquotedString() priority := int(p.parseInt()) return PackageInit{Name: name, InitFunc: initfunc, Priority: priority} } // Throw away tokens until we see a ';'. If we see a '<', attempt to parse as a type. func (p *parser) discardDirectiveWhileParsingTypes(pkg *types.Package) { for { switch p.tok { case ';': return case '<': p.parseType(p.pkg) case scanner.EOF: p.error("unexpected EOF") default: p.next() } } } // Create the package if we have parsed both the package path and package name. func (p *parser) maybeCreatePackage() { if p.pkgname != "" && p.pkgpath != "" { p.pkg = p.getPkg(p.pkgpath, p.pkgname) } } // InitDataDirective = "v1" ";" | // "priority" int ";" | // "init" { PackageInit } ";" | // "checksum" unquotedString ";" . func (p *parser) parseInitDataDirective() { if p.tok != scanner.Ident { // unexpected token kind; panic p.expect(scanner.Ident) } switch p.lit { case "v1": p.next() p.expect(';') case "priority": p.next() p.initdata.Priority = int(p.parseInt()) p.expect(';') case "init": p.next() for p.tok != ';' && p.tok != scanner.EOF { p.initdata.Inits = append(p.initdata.Inits, p.parsePackageInit()) } p.expect(';') case "checksum": // Don't let the scanner try to parse the checksum as a number. defer func(mode uint) { p.scanner.Mode = mode }(p.scanner.Mode) p.scanner.Mode &^= scanner.ScanInts | scanner.ScanFloats p.next() p.parseUnquotedString() p.expect(';') default: p.errorf("unexpected identifier: %q", p.lit) } } // Directive = InitDataDirective | // "package" unquotedString ";" | // "pkgpath" unquotedString ";" | // "import" unquotedString unquotedString string ";" | // "func" Func ";" | // "type" Type ";" | // "var" Var ";" | // "const" Const ";" . func (p *parser) parseDirective() { if p.tok != scanner.Ident { // unexpected token kind; panic p.expect(scanner.Ident) } switch p.lit { case "v1", "priority", "init", "checksum": p.parseInitDataDirective() case "package": p.next() p.pkgname = p.parseUnquotedString() p.maybeCreatePackage() p.expect(';') case "pkgpath": p.next() p.pkgpath = p.parseUnquotedString() p.maybeCreatePackage() p.expect(';') case "import": p.next() pkgname := p.parseUnquotedString() pkgpath := p.parseUnquotedString() p.getPkg(pkgpath, pkgname) p.parseString() p.expect(';') case "func": p.next() fun := p.parseFunc(p.pkg) if fun != nil { p.pkg.Scope().Insert(fun) } p.expect(';') case "type": p.next() p.parseType(p.pkg) p.expect(';') case "var": p.next() v := p.parseVar(p.pkg) p.pkg.Scope().Insert(v) p.expect(';') case "const": p.next() c := p.parseConst(p.pkg) p.pkg.Scope().Insert(c) p.expect(';') default: p.errorf("unexpected identifier: %q", p.lit) } } // Package = { Directive } . func (p *parser) parsePackage() *types.Package { for p.tok != scanner.EOF { p.parseDirective() } for _, typ := range p.typeMap { if it, ok := typ.(*types.Interface); ok { it.Complete() } } p.pkg.MarkComplete() return p.pkg } // InitData = { InitDataDirective } . func (p *parser) parseInitData() { for p.tok != scanner.EOF { p.parseInitDataDirective() } }