[dev.inline] cmd/compile/internal/syntax: clean up error and pragma handling

Reviewed in and cherry-picked from https://go-review.googlesource.com/#/c/33873/.

- simplify error handling in source.go
  (move handling of first error into parser, where it belongs)

- clean up error handling in scanner.go

- move pragma and position base handling from scanner
  to parser where it belongs

- have separate error methods in parser to avoid confusion
  with handlers from scanner.go and source.go

- (source.go) and (scanner.go, source.go, tokens.go)
  may be stand-alone packages if so desired, which means
  these files are now less entangled and easier to maintain

Change-Id: I81510fc7ef943b78eaa49092c0eab2075a05878c
Reviewed-on: https://go-review.googlesource.com/34235
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Robert Griesemer <gri@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
This commit is contained in:
Robert Griesemer 2016-12-01 22:04:49 -08:00
parent e97c8a592f
commit 54ef0447fe
4 changed files with 128 additions and 102 deletions

View file

@ -7,6 +7,7 @@ package syntax
import (
"fmt"
"io"
"strconv"
"strings"
)
@ -19,21 +20,53 @@ const trace = false
const gcCompat = true
type parser struct {
base *PosBase
errh ErrorHandler
scanner
first error // first error encountered
pragma Pragma // pragma flags
fnest int // function nesting level (for error handling)
xnest int // expression nesting level (for complit ambiguity resolution)
indent []byte // tracing support
}
func (p *parser) init(filename string, src io.Reader, errh ErrorHandler, pragh PragmaHandler) {
p.scanner.init(filename, src, errh, pragh)
p.base = NewFileBase(filename)
p.errh = errh
p.scanner.init(src, p.error_at, func(line, col uint, text string) {
if strings.HasPrefix(text, "line ") {
p.updateBase(line, col, text[5:])
}
if pragh != nil {
p.pragma |= pragh(line, text)
}
}, gcCompat)
p.first = nil
p.pragma = 0
p.fnest = 0
p.xnest = 0
p.indent = nil
}
func (p *parser) updateBase(line, col uint, text string) {
// Want to use LastIndexByte below but it's not defined in Go1.4 and bootstrap fails.
i := strings.LastIndex(text, ":") // look from right (Windows filenames may contain ':')
if i < 0 {
return
}
nstr := text[i+1:]
n, err := strconv.Atoi(nstr)
if err != nil || n <= 0 || n > lineMax {
p.error_at(line, col+uint(i+1), "invalid line number: "+nstr)
return
}
p.base = NewLinePragmaBase(MakePos(p.base.Pos().Base(), line, col), text[:i], uint(n))
}
func (p *parser) got(tok token) bool {
if p.tok == tok {
p.next()
@ -52,12 +85,24 @@ func (p *parser) want(tok token) {
// ----------------------------------------------------------------------------
// Error handling
// syntax_error reports a syntax error at the current line.
func (p *parser) syntax_error(msg string) {
p.syntax_error_at(p.line, p.col, msg)
// error reports an error at the given position.
func (p *parser) error_at(line, col uint, msg string) {
err := Error{line, col, msg}
if p.first == nil {
p.first = err
}
if p.errh == nil {
panic(p.first)
}
p.errh(err)
}
// Like syntax_error, but reports error at given line rather than current lexer line.
// error reports a (non-syntax) error at the current token position.
func (p *parser) error(msg string) {
p.error_at(p.line, p.col, msg)
}
// syntax_error_at reports a syntax error at the given position.
func (p *parser) syntax_error_at(line, col uint, msg string) {
if trace {
defer p.trace("syntax_error (" + msg + ")")()
@ -102,6 +147,11 @@ func (p *parser) syntax_error_at(line, col uint, msg string) {
p.error_at(line, col, "syntax error: unexpected "+tok+msg)
}
// syntax_error reports a syntax error at the current token position.
func (p *parser) syntax_error(msg string) {
p.syntax_error_at(p.line, p.col, msg)
}
// The stopset contains keywords that start a statement.
// They are good synchronization points in case of syntax
// errors and (usually) shouldn't be skipped over.