Commit 74cb9632 authored by Robert Griesemer's avatar Robert Griesemer

go/parser: Remove unused Parse* functions. Simplified ParseExpr signature.

Only ParseFile, ParseDir, and ParseExpr are used in the tree.
If partial parsing of code is required, it is fairly simple
to wrap the relevant piece of code into a dummy package for
parsing (see parser.ParseExpr).

Also: minor cleanups.

R=rsc
CC=golang-dev
https://golang.org/cl/5535055
parent 06479f76
......@@ -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
}
}
......
......@@ -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())
}
......
......@@ -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)
......
......@@ -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.
//
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.
// 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 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
}
......@@ -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 {
func (p *parser) init(fset *token.FileSet, filename string, src []byte, mode uint) {
p.file = fset.AddFile(filename, fset.Base(), len(src))
var m uint
if mode&ParseComments != 0 {
m |= scanner.ScanComments
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))
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
......
......@@ -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 {
......
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