diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go index 486090e90e..155eb0440f 100644 --- a/src/cmd/cgo/gcc.go +++ b/src/cmd/cgo/gcc.go @@ -344,8 +344,7 @@ func (p *Package) guessKinds(f *File) []*Name { if _, err := strconv.Atoi(n.Define); err == nil { ok = true } else if n.Define[0] == '"' || n.Define[0] == '\'' { - _, err := parser.ParseExpr(fset, "", n.Define) - if err == nil { + if _, err := parser.ParseExpr(n.Define); err == nil { ok = true } } diff --git a/src/cmd/gofix/fix.go b/src/cmd/gofix/fix.go index 5d70e9cf9c..d1a7bc8749 100644 --- a/src/cmd/gofix/fix.go +++ b/src/cmd/gofix/fix.go @@ -746,7 +746,7 @@ func usesImport(f *ast.File, path string) (used bool) { } func expr(s string) ast.Expr { - x, err := parser.ParseExpr(fset, "", s) + x, err := parser.ParseExpr(s) if err != nil { panic("parsing " + s + ": " + err.Error()) } diff --git a/src/cmd/gofmt/rewrite.go b/src/cmd/gofmt/rewrite.go index 60a4a7b49f..3c7861f0d1 100644 --- a/src/cmd/gofmt/rewrite.go +++ b/src/cmd/gofmt/rewrite.go @@ -36,7 +36,7 @@ func initRewrite() { // but there are problems with preserving formatting and also // with what a wildcard for a statement looks like. func parseExpr(s string, what string) ast.Expr { - x, err := parser.ParseExpr(fset, "input", s) + x, err := parser.ParseExpr(s) if err != nil { fmt.Fprintf(os.Stderr, "parsing %s %s: %s\n", what, s, err) os.Exit(2) diff --git a/src/pkg/go/parser/interface.go b/src/pkg/go/parser/interface.go index be11f461c3..2ce3df8df7 100644 --- a/src/pkg/go/parser/interface.go +++ b/src/pkg/go/parser/interface.go @@ -10,7 +10,6 @@ import ( "bytes" "errors" "go/ast" - "go/scanner" "go/token" "io" "io/ioutil" @@ -36,86 +35,28 @@ func readSource(filename string, src interface{}) ([]byte, error) { } case io.Reader: var buf bytes.Buffer - _, err := io.Copy(&buf, s) - if err != nil { + if _, err := io.Copy(&buf, s); err != nil { return nil, err } return buf.Bytes(), nil - default: - return nil, errors.New("invalid source") } + return nil, errors.New("invalid source") } - return ioutil.ReadFile(filename) } -func (p *parser) errors() error { - mode := scanner.Sorted - if p.mode&SpuriousErrors == 0 { - mode = scanner.NoMultiples - } - return p.GetError(mode) -} - -// ParseExpr parses a Go expression and returns the corresponding -// AST node. The fset, filename, and src arguments have the same interpretation -// as for ParseFile. If there is an error, the result expression -// may be nil or contain a partial AST. +// The mode parameter to the Parse* functions is a set of flags (or 0). +// They control the amount of source code parsed and other optional +// parser functionality. // -func ParseExpr(fset *token.FileSet, filename string, src interface{}) (ast.Expr, error) { - data, err := readSource(filename, src) - if err != nil { - return nil, err - } - - var p parser - p.init(fset, filename, data, 0) - x := p.parseRhs() - if p.tok == token.SEMICOLON { - p.next() // consume automatically inserted semicolon, if any - } - p.expect(token.EOF) - - return x, p.errors() -} - -// ParseStmtList parses a list of Go statements and returns the list -// of corresponding AST nodes. The fset, filename, and src arguments have the same -// interpretation as for ParseFile. If there is an error, the node -// list may be nil or contain partial ASTs. -// -func ParseStmtList(fset *token.FileSet, filename string, src interface{}) ([]ast.Stmt, error) { - data, err := readSource(filename, src) - if err != nil { - return nil, err - } - - var p parser - p.init(fset, filename, data, 0) - list := p.parseStmtList() - p.expect(token.EOF) - - return list, p.errors() -} - -// ParseDeclList parses a list of Go declarations and returns the list -// of corresponding AST nodes. The fset, filename, and src arguments have the same -// interpretation as for ParseFile. If there is an error, the node -// list may be nil or contain partial ASTs. -// -func ParseDeclList(fset *token.FileSet, filename string, src interface{}) ([]ast.Decl, error) { - data, err := readSource(filename, src) - if err != nil { - return nil, err - } - - var p parser - p.init(fset, filename, data, 0) - list := p.parseDeclList() - p.expect(token.EOF) - - return list, p.errors() -} +const ( + PackageClauseOnly uint = 1 << iota // parsing stops after package clause + ImportsOnly // parsing stops after import declarations + ParseComments // parse comments and add them to AST + Trace // print a trace of parsed productions + DeclarationErrors // report declaration errors + SpuriousErrors // report all (not just the first) errors per line +) // ParseFile parses the source code of a single Go source file and returns // the corresponding ast.File node. The source code may be provided via @@ -124,7 +65,6 @@ func ParseDeclList(fset *token.FileSet, filename string, src interface{}) ([]ast // If src != nil, ParseFile parses the source from src and the filename is // only used when recording position information. The type of the argument // for the src parameter must be string, []byte, or io.Reader. -// // If src == nil, ParseFile parses the file specified by filename. // // The mode parameter controls the amount of source text parsed and other @@ -133,49 +73,18 @@ func ParseDeclList(fset *token.FileSet, filename string, src interface{}) ([]ast // // If the source couldn't be read, the returned AST is nil and the error // indicates the specific failure. If the source was read but syntax -// errors were found, the result is a partial AST (with ast.BadX nodes +// errors were found, the result is a partial AST (with ast.Bad* nodes // representing the fragments of erroneous source code). Multiple errors // are returned via a scanner.ErrorList which is sorted by file position. // func ParseFile(fset *token.FileSet, filename string, src interface{}, mode uint) (*ast.File, error) { - data, err := readSource(filename, src) + text, err := readSource(filename, src) if err != nil { return nil, err } - var p parser - p.init(fset, filename, data, mode) - file := p.parseFile() // parseFile reads to EOF - - return file, p.errors() -} - -// ParseFiles calls ParseFile for each file in the filenames list and returns -// a map of package name -> package AST with all the packages found. The mode -// bits are passed to ParseFile unchanged. Position information is recorded -// in the file set fset. -// -// Files with parse errors are ignored. In this case the map of packages may -// be incomplete (missing packages and/or incomplete packages) and the first -// error encountered is returned. -// -func ParseFiles(fset *token.FileSet, filenames []string, mode uint) (pkgs map[string]*ast.Package, first error) { - pkgs = make(map[string]*ast.Package) - for _, filename := range filenames { - if src, err := ParseFile(fset, filename, nil, mode); err == nil { - name := src.Name.Name - pkg, found := pkgs[name] - if !found { - // TODO(gri) Use NewPackage here; reconsider ParseFiles API. - pkg = &ast.Package{name, nil, nil, make(map[string]*ast.File)} - pkgs[name] = pkg - } - pkg.Files[filename] = src - } else if first == nil { - first = err - } - } - return + p.init(fset, filename, text, mode) + return p.parseFile(), p.errors() } // ParseDir calls ParseFile for the files in the directory specified by path and @@ -186,9 +95,9 @@ func ParseFiles(fset *token.FileSet, filenames []string, mode uint) (pkgs map[st // // If the directory couldn't be read, a nil map and the respective error are // returned. If a parse error occurred, a non-nil but incomplete map and the -// error are returned. +// first error encountered are returned. // -func ParseDir(fset *token.FileSet, path string, filter func(os.FileInfo) bool, mode uint) (map[string]*ast.Package, error) { +func ParseDir(fset *token.FileSet, path string, filter func(os.FileInfo) bool, mode uint) (pkgs map[string]*ast.Package, first error) { fd, err := os.Open(path) if err != nil { return nil, err @@ -200,15 +109,36 @@ func ParseDir(fset *token.FileSet, path string, filter func(os.FileInfo) bool, m return nil, err } - filenames := make([]string, len(list)) - n := 0 + pkgs = make(map[string]*ast.Package) for _, d := range list { if filter == nil || filter(d) { - filenames[n] = filepath.Join(path, d.Name()) - n++ + filename := filepath.Join(path, d.Name()) + if src, err := ParseFile(fset, filename, nil, mode); err == nil { + name := src.Name.Name + pkg, found := pkgs[name] + if !found { + pkg = &ast.Package{name, nil, nil, make(map[string]*ast.File)} + pkgs[name] = pkg + } + pkg.Files[filename] = src + } else if first == nil { + first = err + } } } - filenames = filenames[0:n] - return ParseFiles(fset, filenames, mode) + return +} + +// ParseExpr is a convenience function for obtaining the AST of an expression x. +// The position information recorded in the AST is undefined. +// +func ParseExpr(x string) (ast.Expr, error) { + // parse x within the context of a complete package for correct scopes; + // use //line directive for correct positions in error messages + file, err := ParseFile(token.NewFileSet(), "", "package p;func _(){_=\n//line :1\n"+x+";}", 0) + if err != nil { + return nil, err + } + return file.Decls[0].(*ast.FuncDecl).Body.List[0].(*ast.AssignStmt).Rhs[0], nil } diff --git a/src/pkg/go/parser/parser.go b/src/pkg/go/parser/parser.go index 8467b0e4e4..d90f5775df 100644 --- a/src/pkg/go/parser/parser.go +++ b/src/pkg/go/parser/parser.go @@ -16,19 +16,6 @@ import ( "go/token" ) -// The mode parameter to the Parse* functions is a set of flags (or 0). -// They control the amount of source code parsed and other optional -// parser functionality. -// -const ( - PackageClauseOnly uint = 1 << iota // parsing stops after package clause - ImportsOnly // parsing stops after import declarations - ParseComments // parse comments and add them to AST - Trace // print a trace of parsed productions - DeclarationErrors // report declaration errors - SpuriousErrors // report all (not just the first) errors per line -) - // The parser structure holds the parser's internal state. type parser struct { file *token.File @@ -65,18 +52,13 @@ type parser struct { targetStack [][]*ast.Ident // stack of unresolved labels } -// scannerMode returns the scanner mode bits given the parser's mode bits. -func scannerMode(mode uint) uint { - var m uint - if mode&ParseComments != 0 { - m |= scanner.ScanComments - } - return m -} - func (p *parser) init(fset *token.FileSet, filename string, src []byte, mode uint) { p.file = fset.AddFile(filename, fset.Base(), len(src)) - p.scanner.Init(p.file, src, p, scannerMode(mode)) + var m uint + if mode&ParseComments != 0 { + m = scanner.ScanComments + } + p.scanner.Init(p.file, src, p, m) p.mode = mode p.trace = mode&Trace != 0 // for convenience (p.trace is used frequently) @@ -92,6 +74,14 @@ func (p *parser) init(fset *token.FileSet, filename string, src []byte, mode uin p.openLabelScope() } +func (p *parser) errors() error { + m := scanner.Sorted + if p.mode&SpuriousErrors == 0 { + m = scanner.NoMultiples + } + return p.GetError(m) +} + // ---------------------------------------------------------------------------- // Scoping support @@ -2109,18 +2099,6 @@ func (p *parser) parseDecl() ast.Decl { return p.parseGenDecl(p.tok, f) } -func (p *parser) parseDeclList() (list []ast.Decl) { - if p.trace { - defer un(trace(p, "DeclList")) - } - - for p.tok != token.EOF { - list = append(list, p.parseDecl()) - } - - return -} - // ---------------------------------------------------------------------------- // Source files diff --git a/src/pkg/go/parser/parser_test.go b/src/pkg/go/parser/parser_test.go index f602db8896..a3ee8525de 100644 --- a/src/pkg/go/parser/parser_test.go +++ b/src/pkg/go/parser/parser_test.go @@ -54,7 +54,7 @@ func TestParseIllegalInputs(t *testing.T) { } } -var validPrograms = []interface{}{ +var validPrograms = []string{ "package p\n", `package p;`, `package p; import "fmt"; func f() { fmt.Println("Hello, World!") };`, @@ -136,6 +136,32 @@ func TestParse4(t *testing.T) { } } +func TestParseExpr(t *testing.T) { + // just kicking the tires: + // a valid expression + src := "a + b" + x, err := ParseExpr(src) + if err != nil { + t.Errorf("ParseExpr(%s): %v", src, err) + } + // sanity check + if _, ok := x.(*ast.BinaryExpr); !ok { + t.Errorf("ParseExpr(%s): got %T, expected *ast.BinaryExpr", src, x) + } + + // an invalid expression + src = "a + *" + _, err = ParseExpr(src) + if err == nil { + t.Errorf("ParseExpr(%s): %v", src, err) + } + + // it must not crash + for _, src := range validPrograms { + ParseExpr(src) + } +} + func TestColonEqualsScope(t *testing.T) { f, err := ParseFile(fset, "", `package p; func f() { x, y, z := x, y, z }`, 0) if err != nil {