| // 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 |
| |
| // This is a verbatim copy of $GOROOT/src/go/internal/gccgoimporter/parser.go. |
| |
| import ( |
| "bytes" |
| "errors" |
| "fmt" |
| "go/constant" |
| "go/token" |
| "go/types" |
| "io" |
| "strconv" |
| "strings" |
| "text/scanner" |
| ) |
| |
| type parser struct { |
| scanner scanner.Scanner |
| version string // format version |
| 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 = <neither a whitespace nor a ';' char> . |
| 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<<uint(ch)) == 0; ch = p.scanner.Peek() { |
| buf.WriteRune(ch) |
| p.scanner.Next() |
| } |
| p.next() |
| return buf.String() |
| } |
| |
| func (p *parser) next() { |
| p.tok = p.scanner.Scan() |
| switch p.tok { |
| case scanner.Ident, scanner.Int, scanner.Float, scanner.String, 'ยท': |
| p.lit = p.scanner.TokenText() |
| default: |
| p.lit = "" |
| } |
| } |
| |
| func (p *parser) parseQualifiedName() (path, name string) { |
| return p.parseQualifiedNameStr(p.parseString()) |
| } |
| |
| func (p *parser) parseUnquotedQualifiedName() (path, name string) { |
| return p.parseQualifiedNameStr(p.parseUnquotedString()) |
| } |
| |
| // qualifiedName = [ ["."] unquotedString "." ] unquotedString . |
| // |
| // The above production uses greedy matching. |
| func (p *parser) parseQualifiedNameStr(unquotedName string) (pkgpath, name string) { |
| parts := strings.Split(unquotedName, ".") |
| if parts[0] == "" { |
| parts = parts[1:] |
| } |
| |
| switch len(parts) { |
| case 0: |
| p.errorf("malformed qualified name: %q", unquotedName) |
| case 1: |
| // unqualified name |
| pkgpath = p.pkgpath |
| name = parts[0] |
| default: |
| // qualified name, which may contain periods |
| pkgpath = strings.Join(parts[0:len(parts)-1], ".") |
| name = parts[len(parts)-1] |
| } |
| |
| return |
| } |
| |
| // getPkg returns the package for a given path. If the package is |
| // not found but we have a package name, create the package and |
| // add it to the p.imports map. |
| // |
| func (p *parser) getPkg(pkgpath, name string) *types.Package { |
| // package unsafe is not in the imports map - handle explicitly |
| if pkgpath == "unsafe" { |
| return types.Unsafe |
| } |
| pkg := p.imports[pkgpath] |
| if pkg == nil && name != "" { |
| pkg = types.NewPackage(pkgpath, name) |
| p.imports[pkgpath] = pkg |
| } |
| return pkg |
| } |
| |
| // parseExportedName is like parseQualifiedName, but |
| // the package path is resolved to an imported *types.Package. |
| // |
| // ExportedName = string [string] . |
| func (p *parser) parseExportedName() (pkg *types.Package, name string) { |
| path, name := p.parseQualifiedName() |
| var pkgname string |
| if p.tok == scanner.String { |
| pkgname = p.parseString() |
| } |
| pkg = p.getPkg(path, pkgname) |
| if pkg == nil { |
| p.errorf("package %s (path = %q) not found", name, path) |
| } |
| return |
| } |
| |
| // Name = QualifiedName | "?" . |
| func (p *parser) parseName() string { |
| if p.tok == '?' { |
| // Anonymous. |
| p.next() |
| return "" |
| } |
| // The package path is redundant for us. Don't try to parse it. |
| _, name := p.parseUnquotedQualifiedName() |
| return name |
| } |
| |
| func deref(typ types.Type) types.Type { |
| if p, _ := typ.(*types.Pointer); p != nil { |
| typ = p.Elem() |
| } |
| return typ |
| } |
| |
| // Field = Name Type [string] . |
| func (p *parser) parseField(pkg *types.Package) (field *types.Var, tag string) { |
| name := p.parseName() |
| typ := p.parseType(pkg) |
| anon := false |
| if name == "" { |
| anon = true |
| switch typ := deref(typ).(type) { |
| case *types.Basic: |
| name = typ.Name() |
| case *types.Named: |
| name = typ.Obj().Name() |
| default: |
| p.error("anonymous field expected") |
| } |
| } |
| field = types.NewField(token.NoPos, pkg, name, typ, anon) |
| if p.tok == scanner.String { |
| tag = p.parseString() |
| } |
| return |
| } |
| |
| // Param = Name ["..."] Type . |
| func (p *parser) parseParam(pkg *types.Package) (param *types.Var, isVariadic bool) { |
| name := p.parseName() |
| if p.tok == '.' { |
| p.next() |
| p.expect('.') |
| p.expect('.') |
| isVariadic = true |
| } |
| typ := p.parseType(pkg) |
| if isVariadic { |
| typ = types.NewSlice(typ) |
| } |
| param = types.NewParam(token.NoPos, pkg, name, typ) |
| return |
| } |
| |
| // Var = Name Type . |
| func (p *parser) parseVar(pkg *types.Package) *types.Var { |
| name := p.parseName() |
| return types.NewVar(token.NoPos, pkg, name, p.parseType(pkg)) |
| } |
| |
| // Conversion = "convert" "(" Type "," ConstValue ")" . |
| func (p *parser) parseConversion(pkg *types.Package) (val constant.Value, typ types.Type) { |
| p.expectKeyword("convert") |
| p.expect('(') |
| typ = p.parseType(pkg) |
| p.expect(',') |
| val, _ = p.parseConstValue(pkg) |
| p.expect(')') |
| return |
| } |
| |
| // ConstValue = string | "false" | "true" | ["-"] (int ["'"] | FloatOrComplex) | Conversion . |
| // FloatOrComplex = float ["i" | ("+"|"-") float "i"] . |
| func (p *parser) parseConstValue(pkg *types.Package) (val constant.Value, typ types.Type) { |
| switch p.tok { |
| case scanner.String: |
| str := p.parseString() |
| val = constant.MakeString(str) |
| typ = types.Typ[types.UntypedString] |
| return |
| |
| case scanner.Ident: |
| b := false |
| switch p.lit { |
| case "false": |
| case "true": |
| b = true |
| |
| case "convert": |
| return p.parseConversion(pkg) |
| |
| default: |
| p.errorf("expected const value, got %s (%q)", scanner.TokenString(p.tok), p.lit) |
| } |
| |
| p.next() |
| val = constant.MakeBool(b) |
| typ = types.Typ[types.UntypedBool] |
| return |
| } |
| |
| sign := "" |
| if p.tok == '-' { |
| p.next() |
| sign = "-" |
| } |
| |
| switch p.tok { |
| case scanner.Int: |
| val = constant.MakeFromLiteral(sign+p.lit, token.INT, 0) |
| if val == nil { |
| p.error("could not parse integer literal") |
| } |
| |
| p.next() |
| if p.tok == '\'' { |
| p.next() |
| typ = types.Typ[types.UntypedRune] |
| } else { |
| typ = types.Typ[types.UntypedInt] |
| } |
| |
| case scanner.Float: |
| re := sign + p.lit |
| p.next() |
| |
| var im string |
| switch p.tok { |
| case '+': |
| p.next() |
| im = p.expect(scanner.Float) |
| |
| case '-': |
| p.next() |
| im = "-" + p.expect(scanner.Float) |
| |
| case scanner.Ident: |
| // re is in fact the imaginary component. Expect "i" below. |
| im = re |
| re = "0" |
| |
| default: |
| val = constant.MakeFromLiteral(re, token.FLOAT, 0) |
| if val == nil { |
| p.error("could not parse float literal") |
| } |
| typ = types.Typ[types.UntypedFloat] |
| return |
| } |
| |
| p.expectKeyword("i") |
| reval := constant.MakeFromLiteral(re, token.FLOAT, 0) |
| if reval == nil { |
| p.error("could not parse real component of complex literal") |
| } |
| imval := constant.MakeFromLiteral(im+"i", token.IMAG, 0) |
| if imval == nil { |
| p.error("could not parse imag component of complex literal") |
| } |
| val = constant.BinaryOp(reval, token.ADD, imval) |
| typ = types.Typ[types.UntypedComplex] |
| |
| default: |
| p.errorf("expected const value, got %s (%q)", scanner.TokenString(p.tok), p.lit) |
| } |
| |
| return |
| } |
| |
| // Const = Name [Type] "=" ConstValue . |
| func (p *parser) parseConst(pkg *types.Package) *types.Const { |
| name := p.parseName() |
| var typ types.Type |
| if p.tok == '<' { |
| typ = p.parseType(pkg) |
| } |
| p.expect('=') |
| val, vtyp := p.parseConstValue(pkg) |
| if typ == nil { |
| typ = vtyp |
| } |
| return types.NewConst(token.NoPos, pkg, name, typ, val) |
| } |
| |
| // NamedType = TypeName [ "=" ] Type { Method } . |
| // TypeName = ExportedName . |
| // Method = "func" "(" Param ")" Name ParamList ResultList ";" . |
| func (p *parser) parseNamedType(n int) types.Type { |
| pkg, name := p.parseExportedName() |
| scope := pkg.Scope() |
| |
| if p.tok == '=' { |
| // type alias |
| p.next() |
| typ := p.parseType(pkg) |
| if obj := scope.Lookup(name); obj != nil { |
| typ = obj.Type() // use previously imported type |
| if typ == nil { |
| p.errorf("%v (type alias) used in cycle", obj) |
| } |
| } else { |
| obj = types.NewTypeName(token.NoPos, pkg, name, typ) |
| scope.Insert(obj) |
| } |
| p.typeMap[n] = typ |
| return typ |
| } |
| |
| // named type |
| obj := scope.Lookup(name) |
| if obj == nil { |
| // a named type may be referred to before the underlying type |
| // is known - set it up |
| tname := types.NewTypeName(token.NoPos, pkg, name, nil) |
| types.NewNamed(tname, nil, nil) |
| scope.Insert(tname) |
| obj = tname |
| } |
| |
| typ := obj.Type() |
| p.typeMap[n] = typ |
| |
| nt, ok := typ.(*types.Named) |
| if !ok { |
| // This can happen for unsafe.Pointer, which is a TypeName holding a Basic type. |
| pt := p.parseType(pkg) |
| if pt != typ { |
| p.error("unexpected underlying type for non-named TypeName") |
| } |
| return typ |
| } |
| |
| underlying := p.parseType(pkg) |
| if nt.Underlying() == nil { |
| nt.SetUnderlying(underlying.Underlying()) |
| } |
| |
| // collect associated methods |
| for p.tok == scanner.Ident { |
| p.expectKeyword("func") |
| p.expect('(') |
| receiver, _ := p.parseParam(pkg) |
| p.expect(')') |
| name := p.parseName() |
| params, isVariadic := p.parseParamList(pkg) |
| results := p.parseResultList(pkg) |
| p.expect(';') |
| |
| sig := types.NewSignature(receiver, params, results, isVariadic) |
| nt.AddMethod(types.NewFunc(token.NoPos, pkg, name, sig)) |
| } |
| |
| return nt |
| } |
| |
| func (p *parser) parseInt() int64 { |
| lit := p.expect(scanner.Int) |
| n, err := strconv.ParseInt(lit, 10, 0) |
| if err != nil { |
| p.error(err) |
| } |
| return n |
| } |
| |
| // ArrayOrSliceType = "[" [ int ] "]" Type . |
| func (p *parser) parseArrayOrSliceType(pkg *types.Package) types.Type { |
| p.expect('[') |
| if p.tok == ']' { |
| p.next() |
| return types.NewSlice(p.parseType(pkg)) |
| } |
| |
| n := p.parseInt() |
| p.expect(']') |
| return types.NewArray(p.parseType(pkg), n) |
| } |
| |
| // MapType = "map" "[" Type "]" Type . |
| func (p *parser) parseMapType(pkg *types.Package) types.Type { |
| p.expectKeyword("map") |
| p.expect('[') |
| key := p.parseType(pkg) |
| p.expect(']') |
| elem := p.parseType(pkg) |
| return types.NewMap(key, elem) |
| } |
| |
| // ChanType = "chan" ["<-" | "-<"] Type . |
| func (p *parser) parseChanType(pkg *types.Package) types.Type { |
| p.expectKeyword("chan") |
| dir := types.SendRecv |
| switch p.tok { |
| case '-': |
| p.next() |
| p.expect('<') |
| dir = types.SendOnly |
| |
| case '<': |
| // don't consume '<' if it belongs to Type |
| if p.scanner.Peek() == '-' { |
| p.next() |
| p.expect('-') |
| dir = types.RecvOnly |
| } |
| } |
| |
| return types.NewChan(dir, p.parseType(pkg)) |
| } |
| |
| // StructType = "struct" "{" { Field } "}" . |
| func (p *parser) parseStructType(pkg *types.Package) types.Type { |
| p.expectKeyword("struct") |
| |
| var fields []*types.Var |
| var tags []string |
| |
| p.expect('{') |
| for p.tok != '}' && p.tok != scanner.EOF { |
| field, tag := p.parseField(pkg) |
| p.expect(';') |
| fields = append(fields, field) |
| tags = append(tags, tag) |
| } |
| p.expect('}') |
| |
| return types.NewStruct(fields, tags) |
| } |
| |
| // ParamList = "(" [ { Parameter "," } Parameter ] ")" . |
| func (p *parser) parseParamList(pkg *types.Package) (*types.Tuple, bool) { |
| var list []*types.Var |
| isVariadic := false |
| |
| p.expect('(') |
| for p.tok != ')' && p.tok != scanner.EOF { |
| if len(list) > 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(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.Universe.Lookup("byte").Type(), |
| gccgoBuiltinRUNE: types.Universe.Lookup("rune").Type(), |
| }[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 := -1 |
| if p.version == "v1" { |
| 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" | "v2" ) ";" | |
| // "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", "v2": |
| p.version = p.lit |
| 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 "init_graph": |
| p.next() |
| // The graph data is thrown away for now. |
| for p.tok != ';' && p.tok != scanner.EOF { |
| p.parseInt() |
| p.parseInt() |
| } |
| 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 [ unquotedString ] [ unquotedString ] ";" | |
| // "pkgpath" unquotedString ";" | |
| // "prefix" 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", "v2", "priority", "init", "init_graph", "checksum": |
| p.parseInitDataDirective() |
| |
| case "package": |
| p.next() |
| p.pkgname = p.parseUnquotedString() |
| p.maybeCreatePackage() |
| if p.version == "v2" && p.tok != ';' { |
| p.parseUnquotedString() |
| p.parseUnquotedString() |
| } |
| p.expect(';') |
| |
| case "pkgpath": |
| p.next() |
| p.pkgpath = p.parseUnquotedString() |
| p.maybeCreatePackage() |
| p.expect(';') |
| |
| case "prefix": |
| p.next() |
| p.pkgpath = p.parseUnquotedString() |
| 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 |
| } |