defer func() {
f.Close()
if err != nil {
- // Add file name to error.
+ // add file name to error
err = fmt.Errorf("reading export data: %s: %v", filename, err)
}
}()
p.next()
p.id = id
p.imports = imports
+ // leave for debugging
+ if false {
+ // check consistency of imports map
+ for _, pkg := range imports {
+ if pkg.Name == "" {
+ fmt.Printf("no package name for %s\n", pkg.Path)
+ }
+ }
+ }
}
func (p *gcParser) next() {
}
// ----------------------------------------------------------------------------
-// Import declarations
+// Qualified and unqualified names
-// ImportPath = string_lit .
+// PackageId = string_lit .
//
-func (p *gcParser) parsePkgId() *Package {
+func (p *gcParser) parsePackageId() string {
id, err := strconv.Unquote(p.expect(scanner.String))
if err != nil {
p.error(err)
}
-
- switch id {
- case "":
- // id == "" stands for the imported package id
- // (only known at time of package installation)
+ // id == "" stands for the imported package id
+ // (only known at time of package installation)
+ if id == "" {
id = p.id
- case "unsafe":
- // package unsafe is not in the imports map - handle explicitly
- return Unsafe
- }
-
- pkg := p.imports[id]
- if pkg == nil {
- pkg = &Package{Scope: new(Scope)}
- p.imports[id] = pkg
}
+ return id
+}
- return pkg
+// PackageName = ident .
+//
+func (p *gcParser) parsePackageName() string {
+ return p.expect(scanner.Ident)
}
// dotIdentifier = ( ident | '·' ) { ident | int | '·' } .
return ident
}
-// ExportedName = "@" ImportPath "." dotIdentifier .
+// QualifiedName = "@" PackageId "." dotIdentifier .
//
-func (p *gcParser) parseExportedName() (*Package, string) {
+func (p *gcParser) parseQualifiedName() (id, name string) {
p.expect('@')
- pkg := p.parsePkgId()
+ id = p.parsePackageId()
p.expect('.')
- name := p.parseDotIdent()
- return pkg, name
+ name = p.parseDotIdent()
+ return
+}
+
+// getPkg returns the package for a given id. 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 *gcParser) getPkg(id, name string) *Package {
+ // package unsafe is not in the imports map - handle explicitly
+ if id == "unsafe" {
+ return Unsafe
+ }
+ pkg := p.imports[id]
+ if pkg == nil && name != "" {
+ pkg = &Package{Name: name, Path: id, Scope: new(Scope)}
+ p.imports[id] = pkg
+ }
+ return pkg
+}
+
+// parseExportedName is like parseQualifiedName, but
+// the package id is resolved to an imported *Package.
+//
+func (p *gcParser) parseExportedName() (pkg *Package, name string) {
+ id, name := p.parseQualifiedName()
+ pkg = p.getPkg(id, "")
+ if pkg == nil {
+ p.errorf("%s package not found", id)
+ }
+ return
}
// ----------------------------------------------------------------------------
return &Map{Key: key, Elt: elt}
}
-// Name = identifier | "?" | ExportedName .
+// Name = identifier | "?" | QualifiedName .
+//
+// If materializePkg is set, a package is returned for fully qualified names.
+// That package may be a fake package (without name, scope, and not in the
+// p.imports map), created for the sole purpose of providing a package path
+// for QualifiedNames. Fake packages are created when the package id is not
+// found in the p.imports map; we cannot create a real package in that case
+// because we don't have a package name.
+//
+// TODO(gri): consider changing QualifiedIdents to (path, name) pairs to
+// simplify this code.
//
-func (p *gcParser) parseName() (pkg *Package, name string) {
+func (p *gcParser) parseName(materializePkg bool) (pkg *Package, name string) {
switch p.tok {
case scanner.Ident:
name = p.lit
p.next()
case '@':
// exported name prefixed with package path
- pkg, name = p.parseExportedName()
+ var id string
+ id, name = p.parseQualifiedName()
+ if materializePkg {
+ // we don't have a package name - if the package
+ // doesn't exist yet, create a fake package instead
+ pkg = p.getPkg(id, "")
+ if pkg == nil {
+ pkg = &Package{Path: id}
+ }
+ }
default:
p.error("name expected")
}
//
func (p *gcParser) parseField() *Field {
var f Field
- f.Pkg, f.Name = p.parseName()
+ f.Pkg, f.Name = p.parseName(true)
f.Type = p.parseType()
if p.tok == scanner.String {
f.Tag = p.expect(scanner.String)
// Parameter = ( identifier | "?" ) [ "..." ] Type [ string_lit ] .
//
func (p *gcParser) parseParameter() (par *Var, isVariadic bool) {
- _, name := p.parseName()
+ _, name := p.parseName(false)
if name == "" {
name = "_" // cannot access unnamed identifiers
}
var results []*Var
switch p.tok {
case scanner.Ident, '[', '*', '<', '@':
+ // TODO(gri) does this ever happen?
// single, unnamed result
results = []*Var{{Type: p.parseType()}}
case '(':
if len(methods) > 0 {
p.expect(';')
}
- pkg, name := p.parseName()
+ pkg, name := p.parseName(true)
typ := p.parseSignature()
methods = append(methods, &Method{QualifiedName{pkg, name}, typ})
}
// ----------------------------------------------------------------------------
// Declarations
-// ImportDecl = "import" identifier string_lit .
+// ImportDecl = "import" PackageName PackageId .
//
func (p *gcParser) parseImportDecl() {
p.expectKeyword("import")
- // The identifier has no semantic meaning in the import data.
- // It exists so that error messages can print the real package
- // name: binary.ByteOrder instead of "encoding/binary".ByteOrder.
- name := p.expect(scanner.Ident)
- pkg := p.parsePkgId()
- assert(pkg.Name == "" || pkg.Name == name)
- pkg.Name = name
+ name := p.parsePackageName()
+ p.getPkg(p.parsePackageId(), name)
}
// int_lit = [ "+" | "-" ] { "0" ... "9" } .
base := typ.(*NamedType)
// parse method name, signature, and possibly inlined body
- pkg, name := p.parseName() // unexported method names in imports are qualified with their package.
+ pkg, name := p.parseName(true) // unexported method names in imports are qualified with their package.
sig := p.parseFunc()
sig.Recv = recv
// Export
// Export = "PackageClause { Decl } "$$" .
-// PackageClause = "package" identifier [ "safe" ] "\n" .
+// PackageClause = "package" PackageName [ "safe" ] "\n" .
//
func (p *gcParser) parseExport() *Package {
p.expectKeyword("package")
- name := p.expect(scanner.Ident)
+ name := p.parsePackageName()
if p.tok != '\n' {
// A package is safe if it was compiled with the -u flag,
// which disables the unsafe package.
}
p.expect('\n')
- pkg := p.imports[p.id]
- if pkg == nil {
- pkg = &Package{Name: name, Scope: new(Scope)}
- p.imports[p.id] = pkg
- }
+ pkg := p.getPkg(p.id, name)
for p.tok != '$' && p.tok != scanner.EOF {
p.parseDecl()