cmd/compile/internal/syntax: fix error handling for Read/Parse calls

- define syntax.Error for cleaner error reporting
- abort parsing after first error if no error handler is installed
- make sure to always report the first error, if any
- document behavior of API calls
- while at it: rename ReadXXX -> ParseXXX (clearer)
- adjust cmd/compile noder.go accordingly

Fixes #17774.

Change-Id: I7893eedea454a64acd753e32f7a8bf811ddbb03c
Reviewed-on: https://go-review.googlesource.com/32950
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
This commit is contained in:
Robert Griesemer 2016-11-08 16:01:56 -08:00
parent ad020477f4
commit 60a9bf9f95
8 changed files with 126 additions and 85 deletions

View file

@ -5,35 +5,72 @@
package syntax
import (
"errors"
"fmt"
"io"
"os"
)
// Mode describes the parser mode.
type Mode uint
// Error describes a syntax error. Error implements the error interface.
type Error struct {
// TODO(gri) decide what we really need here
Pos int // byte offset from file start
Line int // line (starting with 1)
Msg string
}
func (err Error) Error() string {
return fmt.Sprintf("%d: %s", err.Line, err.Msg)
}
var _ error = Error{} // verify that Error implements error
// An ErrorHandler is called for each error encountered reading a .go file.
type ErrorHandler func(err error)
// A Pragma value is a set of flags that augment a function or
// type declaration. Callers may assign meaning to the flags as
// appropriate.
type Pragma uint16
type ErrorHandler func(pos, line int, msg string)
// A PragmaHandler is used to process //line and //go: directives as
// they're scanned. The returned Pragma value will be unioned into the
// next FuncDecl node.
type PragmaHandler func(pos, line int, text string) Pragma
// TODO(gri) These need a lot more work.
// Parse parses a single Go source file from src and returns the corresponding
// syntax tree. If there are syntax errors, Parse will return the first error
// encountered.
//
// If errh != nil, it is called with each error encountered, and Parse will
// process as much source as possible. If errh is nil, Parse will terminate
// immediately upon encountering an error.
//
// If a PragmaHandler is provided, it is called with each pragma encountered.
//
// The Mode argument is currently ignored.
func Parse(src io.Reader, errh ErrorHandler, pragh PragmaHandler, mode Mode) (_ *File, err error) {
defer func() {
if p := recover(); p != nil {
var ok bool
if err, ok = p.(Error); ok {
return
}
panic(p)
}
}()
func ReadFile(filename string, errh ErrorHandler, pragh PragmaHandler, mode Mode) (*File, error) {
src, err := os.Open(filename)
if err != nil {
return nil, err
}
defer src.Close()
return Read(src, errh, pragh, mode)
var p parser
p.init(src, errh, pragh)
p.next()
return p.file(), p.first
}
// ParseBytes behaves like Parse but it reads the source from the []byte slice provided.
func ParseBytes(src []byte, errh ErrorHandler, pragh PragmaHandler, mode Mode) (*File, error) {
return Parse(&bytesReader{src}, errh, pragh, mode)
}
type bytesReader struct {
@ -49,37 +86,15 @@ func (r *bytesReader) Read(p []byte) (int, error) {
return 0, io.EOF
}
func ReadBytes(src []byte, errh ErrorHandler, pragh PragmaHandler, mode Mode) (*File, error) {
return Read(&bytesReader{src}, errh, pragh, mode)
}
func Read(src io.Reader, errh ErrorHandler, pragh PragmaHandler, mode Mode) (ast *File, err error) {
defer func() {
if p := recover(); p != nil {
if msg, ok := p.(parserError); ok {
err = errors.New(string(msg))
return
}
panic(p)
// ParseFile behaves like Parse but it reads the source from the named file.
func ParseFile(filename string, errh ErrorHandler, pragh PragmaHandler, mode Mode) (*File, error) {
src, err := os.Open(filename)
if err != nil {
if errh != nil {
errh(err)
}
}()
var p parser
p.init(src, errh, pragh)
p.next()
ast = p.file()
// TODO(gri) This isn't quite right: Even if there's an error handler installed
// we should report an error if parsing found syntax errors. This also
// requires updating the noder's ReadFile call.
if errh == nil && p.nerrors > 0 {
ast = nil
err = fmt.Errorf("%d syntax errors", p.nerrors)
return nil, err
}
return
}
func Write(w io.Writer, n *File) error {
panic("unimplemented")
defer src.Close()
return Parse(src, errh, pragh, mode)
}