Commit 7d3a4097 authored by Matthew Dempsky's avatar Matthew Dempsky

cmd/compile: refactor export data parsing

Merge push_parser and pop_parser into a single parse_import function
and inline unimportfile. Shake out function boundaries a little bit so
that the symmetry is readily visible.

Move the import_package call into parse_import (and inline
import_there into import_package).  This means importfile no longer
needs to provide fake import data to be needlessly lexed/parsed every
time it's called.

Also, instead of indicating import success/failure by whether the next
token is "package", import_spec can just check whether importpkg is
non-nil.

Tangentially, this somehow alters the diagnostics produced for
test/fixedbugs/issue11610.go.  However, the new diagnostics are more
consistent with those produced when the empty import statement is
absent, which seems more desirable than maintaining the previous
errors.

Change-Id: I5cd1c22aa14da8a743ef569ff084711d137279d5
Reviewed-on: https://go-review.googlesource.com/19650Reviewed-by: default avatarRobert Griesemer <gri@golang.org>
parent 0d5e6a3f
...@@ -671,38 +671,30 @@ func loadsys() { ...@@ -671,38 +671,30 @@ func loadsys() {
importpkg = Runtimepkg importpkg = Runtimepkg
cannedimports("runtime.Builtin", runtimeimport) cannedimports("runtime.Builtin", runtimeimport)
thenewparser.import_package()
thenewparser.import_there()
importpkg = unsafepkg importpkg = unsafepkg
cannedimports("unsafe.o", unsafeimport) cannedimports("unsafe.o", unsafeimport)
thenewparser.import_package()
thenewparser.import_there()
importpkg = nil importpkg = nil
} }
func fakeimport() {
importpkg = mkpkg("fake")
cannedimports("fake.o", "$$\n")
}
func importfile(f *Val) { func importfile(f *Val) {
if importpkg != nil {
Fatalf("importpkg not nil")
}
path_, ok := f.U.(string) path_, ok := f.U.(string)
if !ok { if !ok {
Yyerror("import statement not a string") Yyerror("import statement not a string")
fakeimport()
return return
} }
if len(path_) == 0 { if len(path_) == 0 {
Yyerror("import path is empty") Yyerror("import path is empty")
fakeimport()
return return
} }
if isbadimport(path_) { if isbadimport(path_) {
fakeimport()
return return
} }
...@@ -731,7 +723,6 @@ func importfile(f *Val) { ...@@ -731,7 +723,6 @@ func importfile(f *Val) {
} }
importpkg = unsafepkg importpkg = unsafepkg
cannedimports("unsafe.o", "package unsafe\n\n$$\n\n")
imported_unsafe = true imported_unsafe = true
return return
} }
...@@ -739,7 +730,6 @@ func importfile(f *Val) { ...@@ -739,7 +730,6 @@ func importfile(f *Val) {
if islocalname(path_) { if islocalname(path_) {
if path_[0] == '/' { if path_[0] == '/' {
Yyerror("import path cannot be absolute path") Yyerror("import path cannot be absolute path")
fakeimport()
return return
} }
...@@ -754,7 +744,6 @@ func importfile(f *Val) { ...@@ -754,7 +744,6 @@ func importfile(f *Val) {
path_ = cleanbuf path_ = cleanbuf
if isbadimport(path_) { if isbadimport(path_) {
fakeimport()
return return
} }
} }
...@@ -767,28 +756,18 @@ func importfile(f *Val) { ...@@ -767,28 +756,18 @@ func importfile(f *Val) {
importpkg = mkpkg(path_) 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 { if importpkg.Imported {
tag := ""
if importpkg.Safe {
tag = "safe"
}
p := fmt.Sprintf("package %s %s\n$$\n", importpkg.Name, tag)
cannedimports(file, p)
return return
} }
importpkg.Imported = true importpkg.Imported = true
var err error imp, err := obj.Bopenr(file)
var imp *obj.Biobuf
imp, err = obj.Bopenr(file)
if err != nil { if err != nil {
Yyerror("can't open import: %q: %v", path_, err) Yyerror("can't open import: %q: %v", path_, err)
errorexit() errorexit()
} }
defer obj.Bterm(imp)
if strings.HasSuffix(file, ".a") { if strings.HasSuffix(file, ".a") {
if !skiptopkgdef(imp) { if !skiptopkgdef(imp) {
...@@ -845,74 +824,44 @@ func importfile(f *Val) { ...@@ -845,74 +824,44 @@ func importfile(f *Val) {
case '\n': case '\n':
// old export format // old export format
pushedio = curio pushedio = curio
curio = Io{bin: imp, infile: file}
curio.bin = imp
curio.peekc = 0
curio.peekc1 = 0
curio.infile = file
curio.nlsemi = false
typecheckok = true typecheckok = true
push_parser() parse_import()
typecheckok = false
curio = pushedio
pushedio.bin = nil
case 'B': case 'B':
// new export format // new export format
obj.Bgetc(imp) // skip \n after $$B obj.Bgetc(imp) // skip \n after $$B
Import(imp) 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: default:
Yyerror("no import in %q", path_) Yyerror("no import in %q", path_)
errorexit()
} }
}
func unimportfile() {
pop_parser()
if curio.bin != nil { if safemode != 0 && !importpkg.Safe {
obj.Bterm(curio.bin) Yyerror("cannot import unsafe package %q", importpkg.Path)
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) { func cannedimports(file string, cp string) {
lexlineno++ // if sys.6 is included on line 1, lexlineno++ // if sys.6 is included on line 1,
pushedio = curio pushedio = curio
curio = Io{infile: file, cp: cp}
curio.bin = nil
curio.peekc = 0
curio.peekc1 = 0
curio.infile = file
curio.cp = cp
curio.nlsemi = false
typecheckok = true typecheckok = true
incannedimport = 1 incannedimport = 1
push_parser() parse_import()
typecheckok = false
incannedimport = 0
curio = pushedio
pushedio.bin = nil
lexlineno-- // re correct sys.6 line number
} }
func isSpace(c int) bool { func isSpace(c int) bool {
......
...@@ -20,13 +20,11 @@ import ( ...@@ -20,13 +20,11 @@ import (
const trace = false // if set, parse tracing can be enabled with -x const trace = false // if set, parse tracing can be enabled with -x
// TODO(gri) Once we handle imports w/o redirecting the underlying // TODO(gri) Once we stop supporting the legacy export data format
// source of the lexer we can get rid of these. They are here for // we can get rid of this (issue 13242).
// compatibility with the existing yacc-based parser setup (issue 13242). var fileparser parser // the Go source file parser in use
var thenewparser parser // the parser in use
var savedstate []parser // saved parser state, used during import
func push_parser() { func parse_import() {
// Indentation (for tracing) must be preserved across parsers // Indentation (for tracing) must be preserved across parsers
// since we are changing the lexer source (and parser state) // since we are changing the lexer source (and parser state)
// under foot, in the middle of productions. This won't be // under foot, in the middle of productions. This won't be
...@@ -34,25 +32,16 @@ func push_parser() { ...@@ -34,25 +32,16 @@ func push_parser() {
// be the push/pop_parser functionality. // be the push/pop_parser functionality.
// (Instead we could just use a global variable indent, but // (Instead we could just use a global variable indent, but
// but eventually indent should be parser-specific anyway.) // but eventually indent should be parser-specific anyway.)
indent := thenewparser.indent importparser := parser{indent: fileparser.indent} // preserve indentation
savedstate = append(savedstate, thenewparser) importparser.next()
thenewparser = parser{indent: indent} // preserve indentation importparser.import_package()
thenewparser.next()
}
func pop_parser() {
indent := thenewparser.indent
n := len(savedstate) - 1
thenewparser = savedstate[n]
thenewparser.indent = indent // preserve indentation
savedstate = savedstate[:n]
} }
// parse_file sets up a new parser and parses a single Go source file. // parse_file sets up a new parser and parses a single Go source file.
func parse_file() { func parse_file() {
thenewparser = parser{} fileparser = parser{}
thenewparser.next() fileparser.next()
thenewparser.file() fileparser.file()
} }
type parser struct { type parser struct {
...@@ -364,23 +353,18 @@ func (p *parser) importdcl() { ...@@ -364,23 +353,18 @@ func (p *parser) importdcl() {
p.next() p.next()
importfile(&path) importfile(&path)
if p.tok != LPACKAGE { if importpkg == nil {
// When an invalid import path is passed to importfile,
// it calls Yyerror and then sets up a fake import with
// no package statement. This allows us to test more
// than one invalid import statement in a single file.
p.import_there()
if nerrors == 0 { if nerrors == 0 {
Fatalf("phase error in import") Fatalf("phase error in import")
} }
return return
} }
p.import_package()
p.import_there()
ipkg := importpkg ipkg := importpkg
importpkg = nil importpkg = nil
ipkg.Direct = true
if my == nil { if my == nil {
my = Lookup(ipkg.Name) my = Lookup(ipkg.Name)
} }
...@@ -442,23 +426,8 @@ func (p *parser) import_package() { ...@@ -442,23 +426,8 @@ func (p *parser) import_package() {
} else if importpkg.Name != name { } else if importpkg.Name != name {
Yyerror("conflicting names %s and %s for package %q", importpkg.Name, name, importpkg.Path) Yyerror("conflicting names %s and %s for package %q", importpkg.Name, name, importpkg.Path)
} }
if incannedimport == 0 {
importpkg.Direct = true
}
importpkg.Safe = importsafe importpkg.Safe = importsafe
if safemode != 0 && !importsafe {
Yyerror("cannot import unsafe package %q", importpkg.Path)
}
}
// import_there parses the imported package definitions and then switches
// the underlying lexed source back to the importing package.
func (p *parser) import_there() {
if trace && Debug['x'] != 0 {
defer p.trace("import_there")()
}
defercheckwidth() defercheckwidth()
p.hidden_import_list() p.hidden_import_list()
...@@ -469,7 +438,6 @@ func (p *parser) import_there() { ...@@ -469,7 +438,6 @@ func (p *parser) import_there() {
} }
resumecheckwidth() resumecheckwidth()
unimportfile()
} }
// Declaration = ConstDecl | TypeDecl | VarDecl . // Declaration = ConstDecl | TypeDecl | VarDecl .
......
...@@ -9,9 +9,9 @@ ...@@ -9,9 +9,9 @@
package a package a
import"" // ERROR "import path is empty" import"" // ERROR "import path is empty"
var? // ERROR "invalid declaration" var? // ERROR "unexpected \?"
var x int // ERROR "unexpected var" var x int // ERROR "unexpected var" "cannot declare name"
func main() { func main() {
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment