Commit ee161e85 authored by Matthew Dempsky's avatar Matthew Dempsky

cmd/compile: handle pragmas immediately with -newparser=1

Instead of saving all pragmas and processing them after parsing is
finished, process them immediately during scanning like the current
lexer does.

This is a bit unfortunate because it means we can't use
syntax.ParseFile to concurrently parse files yet, but it fixes how we
report syntax errors in the presence of //line pragmas.

While here, add a bunch more gcCompat entries to syntax/parser.go to
get "go build -toolexec='toolstash -cmp' std cmd" passing. There are
still a few remaining cases only triggered building unit tests, but
this seems like a nice checkpoint.

Change-Id: Iaf3bbcf2849857a460496f31eea228e0c585ce13
Reviewed-on: https://go-review.googlesource.com/28226
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarRobert Griesemer <gri@golang.org>
parent 69e7e8a6
...@@ -7,6 +7,7 @@ package gc ...@@ -7,6 +7,7 @@ package gc
import ( import (
"bufio" "bufio"
"bytes" "bytes"
"cmd/compile/internal/syntax"
"cmd/internal/obj" "cmd/internal/obj"
"fmt" "fmt"
"io" "io"
...@@ -60,7 +61,7 @@ func plan9quote(s string) string { ...@@ -60,7 +61,7 @@ func plan9quote(s string) string {
return s return s
} }
type Pragma uint16 type Pragma syntax.Pragma
const ( const (
Nointerface Pragma = 1 << iota Nointerface Pragma = 1 << iota
......
...@@ -6,7 +6,6 @@ package gc ...@@ -6,7 +6,6 @@ package gc
import ( import (
"fmt" "fmt"
"sort"
"strconv" "strconv"
"strings" "strings"
"unicode/utf8" "unicode/utf8"
...@@ -15,23 +14,19 @@ import ( ...@@ -15,23 +14,19 @@ import (
) )
func parseFile(filename string) { func parseFile(filename string) {
errh := func(_, line int, msg string) { p := noder{baseline: lexlineno}
yyerrorl(lexlineno+int32(line)-1, "%s", msg) file, err := syntax.ReadFile(filename, p.error, p.pragma, 0)
}
file, err := syntax.ReadFile(filename, errh, 0)
if err != nil { if err != nil {
Fatalf("syntax.ReadFile %s: %v", filename, err) Fatalf("syntax.ReadFile %s: %v", filename, err)
} }
p := noder{pragmas: file.Pragmas} p.file(file)
p.lineno(file.PkgName)
mkpackage(file.PkgName.Value)
xtop = append(xtop, p.decls(file.DeclList, true)...) if !imported_unsafe {
p.globalPragmas() for _, x := range p.linknames {
lexlineno += p.maxline p.error(0, x, "//go:linkname only allowed in Go files that import \"unsafe\"")
}
}
if nsyntaxerrors == 0 { if nsyntaxerrors == 0 {
testdclstack() testdclstack()
...@@ -40,13 +35,21 @@ func parseFile(filename string) { ...@@ -40,13 +35,21 @@ func parseFile(filename string) {
// noder transforms package syntax's AST into a Nod tree. // noder transforms package syntax's AST into a Nod tree.
type noder struct { type noder struct {
indent []byte baseline int32
pragmas []syntax.Pragma linknames []int // tracks //go:linkname lines
pline int32
maxline int32
} }
func (p *noder) decls(decls []syntax.Decl, top bool) (l []*Node) { func (p *noder) file(file *syntax.File) {
p.lineno(file.PkgName)
mkpackage(file.PkgName.Value)
xtop = append(xtop, p.decls(file.DeclList)...)
lexlineno = p.baseline + int32(file.Lines) - 1
lineno = lexlineno
}
func (p *noder) decls(decls []syntax.Decl) (l []*Node) {
var lastConstGroup *syntax.Group var lastConstGroup *syntax.Group
var lastConstRHS []*Node var lastConstRHS []*Node
var iotaVal int32 var iotaVal int32
...@@ -59,6 +62,7 @@ func (p *noder) decls(decls []syntax.Decl, top bool) (l []*Node) { ...@@ -59,6 +62,7 @@ func (p *noder) decls(decls []syntax.Decl, top bool) (l []*Node) {
case *syntax.VarDecl: case *syntax.VarDecl:
l = append(l, p.varDecl(decl)...) l = append(l, p.varDecl(decl)...)
case *syntax.ConstDecl: case *syntax.ConstDecl:
// Tricky to handle golang.org/issue/15550 correctly. // Tricky to handle golang.org/issue/15550 correctly.
...@@ -91,10 +95,6 @@ func (p *noder) decls(decls []syntax.Decl, top bool) (l []*Node) { ...@@ -91,10 +95,6 @@ func (p *noder) decls(decls []syntax.Decl, top bool) (l []*Node) {
default: default:
panic("unhandled Decl") panic("unhandled Decl")
} }
if top {
p.pline = p.maxline
}
} }
return return
...@@ -102,7 +102,7 @@ func (p *noder) decls(decls []syntax.Decl, top bool) (l []*Node) { ...@@ -102,7 +102,7 @@ func (p *noder) decls(decls []syntax.Decl, top bool) (l []*Node) {
func (p *noder) importDecl(imp *syntax.ImportDecl) { func (p *noder) importDecl(imp *syntax.ImportDecl) {
val := p.basicLit(imp.Path) val := p.basicLit(imp.Path)
importfile(&val, p.indent) importfile(&val, nil)
ipkg := importpkg ipkg := importpkg
importpkg = nil importpkg = nil
...@@ -159,6 +159,7 @@ func (p *noder) varDecl(decl *syntax.VarDecl) []*Node { ...@@ -159,6 +159,7 @@ func (p *noder) varDecl(decl *syntax.VarDecl) []*Node {
exprs = p.exprList(decl.Values) exprs = p.exprList(decl.Values)
} }
p.lineno(decl)
return variter(names, typ, exprs) return variter(names, typ, exprs)
} }
...@@ -216,7 +217,7 @@ func (p *noder) funcDecl(fun *syntax.FuncDecl) *Node { ...@@ -216,7 +217,7 @@ func (p *noder) funcDecl(fun *syntax.FuncDecl) *Node {
} }
} }
pragma := p.pragma() pragma := Pragma(fun.Pragma)
f.Nbody.Set(body) f.Nbody.Set(body)
f.Noescape = pragma&Noescape != 0 f.Noescape = pragma&Noescape != 0
...@@ -224,7 +225,7 @@ func (p *noder) funcDecl(fun *syntax.FuncDecl) *Node { ...@@ -224,7 +225,7 @@ func (p *noder) funcDecl(fun *syntax.FuncDecl) *Node {
Yyerror("can only use //go:noescape with external func implementations") Yyerror("can only use //go:noescape with external func implementations")
} }
f.Func.Pragma = pragma f.Func.Pragma = pragma
lineno = lexlineno + int32(fun.EndLine) - 1 lineno = p.baseline + int32(fun.EndLine) - 1
f.Func.Endlineno = lineno f.Func.Endlineno = lineno
funcbody(f) funcbody(f)
...@@ -345,15 +346,19 @@ func (p *noder) expr(expr syntax.Expr) *Node { ...@@ -345,15 +346,19 @@ func (p *noder) expr(expr syntax.Expr) *Node {
if expr.Type != nil { if expr.Type != nil {
n.Right = p.expr(expr.Type) n.Right = p.expr(expr.Type)
} }
// TODO(mdempsky): Should apply wrapname to n.List nodes. l := p.exprs(expr.ElemList)
n.List.Set(p.exprs(expr.ElemList)) for i, e := range l {
l[i] = p.wrapname(expr.ElemList[i], e)
}
n.List.Set(l)
lineno = p.baseline + int32(expr.EndLine) - 1
return n return n
case *syntax.KeyValueExpr: case *syntax.KeyValueExpr:
return p.nod(expr, OKEY, p.expr(expr.Key), p.wrapname(expr.Value, p.expr(expr.Value))) return p.nod(expr, OKEY, p.expr(expr.Key), p.wrapname(expr.Value, p.expr(expr.Value)))
case *syntax.FuncLit: case *syntax.FuncLit:
closurehdr(p.typeExpr(expr.Type)) closurehdr(p.typeExpr(expr.Type))
body := p.stmts(expr.Body) body := p.stmts(expr.Body)
lineno = lexlineno + int32(expr.EndLine) - 1 lineno = p.baseline + int32(expr.EndLine) - 1
return p.setlineno(expr, closurebody(body)) return p.setlineno(expr, closurebody(body))
case *syntax.ParenExpr: case *syntax.ParenExpr:
return p.nod(expr, OPAREN, p.expr(expr.X), nil) return p.nod(expr, OPAREN, p.expr(expr.X), nil)
...@@ -398,7 +403,9 @@ func (p *noder) expr(expr syntax.Expr) *Node { ...@@ -398,7 +403,9 @@ func (p *noder) expr(expr syntax.Expr) *Node {
x = unparen(x) // TODO(mdempsky): Needed? x = unparen(x) // TODO(mdempsky): Needed?
if x.Op == OCOMPLIT { if x.Op == OCOMPLIT {
// Special case for &T{...}: turn into (*T){...}. // Special case for &T{...}: turn into (*T){...}.
x.Right = p.nod(expr, OIND, x.Right, nil) // TODO(mdempsky): Switch back to p.nod after we
// get rid of gcCompat.
x.Right = Nod(OIND, x.Right, nil)
x.Right.Implicit = true x.Right.Implicit = true
return x return x
} }
...@@ -577,7 +584,7 @@ func (p *noder) stmt(stmt syntax.Stmt) *Node { ...@@ -577,7 +584,7 @@ func (p *noder) stmt(stmt syntax.Stmt) *Node {
case *syntax.SendStmt: case *syntax.SendStmt:
return p.nod(stmt, OSEND, p.expr(stmt.Chan), p.expr(stmt.Value)) return p.nod(stmt, OSEND, p.expr(stmt.Chan), p.expr(stmt.Value))
case *syntax.DeclStmt: case *syntax.DeclStmt:
return liststmt(p.decls(stmt.DeclList, false)) return liststmt(p.decls(stmt.DeclList))
case *syntax.AssignStmt: case *syntax.AssignStmt:
if stmt.Op != 0 && stmt.Op != syntax.Def { if stmt.Op != 0 && stmt.Op != syntax.Def {
n := p.nod(stmt, OASOP, p.expr(stmt.Lhs), p.expr(stmt.Rhs)) n := p.nod(stmt, OASOP, p.expr(stmt.Lhs), p.expr(stmt.Rhs))
...@@ -975,10 +982,7 @@ func (p *noder) setlineno(src syntax.Node, dst *Node) *Node { ...@@ -975,10 +982,7 @@ func (p *noder) setlineno(src syntax.Node, dst *Node) *Node {
// TODO(mdempsky): Shouldn't happen. Fix package syntax. // TODO(mdempsky): Shouldn't happen. Fix package syntax.
return dst return dst
} }
if l > p.maxline { dst.Lineno = p.baseline + l - 1
p.maxline = l
}
dst.Lineno = lexlineno + l - 1
return dst return dst
} }
...@@ -991,78 +995,57 @@ func (p *noder) lineno(n syntax.Node) { ...@@ -991,78 +995,57 @@ func (p *noder) lineno(n syntax.Node) {
// TODO(mdempsky): Shouldn't happen. Fix package syntax. // TODO(mdempsky): Shouldn't happen. Fix package syntax.
return return
} }
if l > p.maxline { lineno = p.baseline + l - 1
p.maxline = l
}
lineno = lexlineno + l - 1
} }
func (p *noder) pragma() Pragma { func (p *noder) error(_, line int, msg string) {
lo := sort.Search(len(p.pragmas), func(i int) bool { return int32(p.pragmas[i].Line) >= p.pline }) yyerrorl(p.baseline+int32(line)-1, "%s", msg)
hi := sort.Search(len(p.pragmas), func(i int) bool { return int32(p.pragmas[i].Line) > p.maxline }) }
var res Pragma func (p *noder) pragma(pos, line int, text string) syntax.Pragma {
for _, prag := range p.pragmas[lo:hi] { switch {
text := prag.Text case strings.HasPrefix(text, "line "):
if !strings.HasPrefix(text, "go:") { i := strings.IndexByte(text, ':')
continue if i < 0 {
break
}
n, err := strconv.Atoi(text[i+1:])
if err != nil {
// todo: make this an error instead? it is almost certainly a bug.
break
} }
if n > 1e8 {
p.error(pos, line, "line number out of range")
errorexit()
}
if n <= 0 {
break
}
lexlineno = p.baseline + int32(line)
linehistupdate(text[5:i], n)
case strings.HasPrefix(text, "go:linkname "):
// Record line number so we can emit an error later if
// the file doesn't import package unsafe.
p.linknames = append(p.linknames, line)
f := strings.Fields(text)
if len(f) != 3 {
p.error(pos, line, "usage: //go:linkname localname linkname")
break
}
Lookup(f[1]).Linkname = f[2]
case strings.HasPrefix(text, "go:cgo_"):
pragcgobuf += pragcgo(text)
fallthrough // because of //go:cgo_unsafe_args
default:
verb := text verb := text
if i := strings.Index(text, " "); i >= 0 { if i := strings.Index(text, " "); i >= 0 {
verb = verb[:i] verb = verb[:i]
} }
return syntax.Pragma(PragmaValue(verb))
res |= PragmaValue(verb)
} }
return res
}
func (p *noder) globalPragmas() {
origlexlineno := lexlineno
defer func() {
lexlineno = origlexlineno
}()
for _, prag := range p.pragmas {
text := prag.Text
if strings.HasPrefix(text, "go:cgo_") {
pragcgobuf += pragcgo(text)
}
if strings.HasPrefix(text, "go:linkname ") { return 0
if !imported_unsafe {
Yyerror("//go:linkname only allowed in Go files that import \"unsafe\"")
}
f := strings.Fields(text)
if len(f) != 3 {
Yyerror("usage: //go:linkname localname linkname")
break
}
Lookup(f[1]).Linkname = f[2]
}
// TODO(mdempsky): Move into package syntax.
if strings.HasPrefix(text, "line ") {
i := strings.IndexByte(text, ':')
if i < 0 {
continue
}
n, err := strconv.Atoi(text[i+1:])
if err != nil {
// todo: make this an error instead? it is almost certainly a bug.
continue
}
if n > 1e8 {
Yyerror("line number out of range")
errorexit()
}
if n <= 0 {
continue
}
lexlineno = origlexlineno + int32(prag.Line)
linehistupdate(text[5:i], n)
}
}
} }
...@@ -14,7 +14,7 @@ func TestDump(t *testing.T) { ...@@ -14,7 +14,7 @@ func TestDump(t *testing.T) {
t.Skip("skipping test in short mode") t.Skip("skipping test in short mode")
} }
ast, err := ReadFile(*src, nil, 0) ast, err := ReadFile(*src, nil, nil, 0)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
......
...@@ -10,6 +10,7 @@ package syntax ...@@ -10,6 +10,7 @@ package syntax
type Node interface { type Node interface {
Line() uint32 Line() uint32
aNode() aNode()
init(p *parser)
} }
type node struct { type node struct {
...@@ -35,16 +36,10 @@ func (n *node) init(p *parser) { ...@@ -35,16 +36,10 @@ func (n *node) init(p *parser) {
type File struct { type File struct {
PkgName *Name PkgName *Name
DeclList []Decl DeclList []Decl
Pragmas []Pragma
Lines int Lines int
node node
} }
type Pragma struct {
Line int
Text string
}
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Declarations // Declarations
...@@ -90,6 +85,7 @@ type ( ...@@ -90,6 +85,7 @@ type (
Name *Name Name *Name
Type *FuncType Type *FuncType
Body []Stmt // nil means no body (forward declaration) Body []Stmt // nil means no body (forward declaration)
Pragma Pragma // TODO(mdempsky): Cleaner solution.
EndLine uint32 // TODO(mdempsky): Cleaner solution. EndLine uint32 // TODO(mdempsky): Cleaner solution.
decl decl
} }
...@@ -130,7 +126,8 @@ type ( ...@@ -130,7 +126,8 @@ type (
CompositeLit struct { CompositeLit struct {
Type Expr // nil means no literal type Type Expr // nil means no literal type
ElemList []Expr ElemList []Expr
NKeys int // number of elements with keys NKeys int // number of elements with keys
EndLine uint32 // TODO(mdempsky): Cleaner solution.
expr expr
} }
......
...@@ -28,7 +28,7 @@ type parser struct { ...@@ -28,7 +28,7 @@ type parser struct {
nerrors int // error count nerrors int // error count
} }
func (p *parser) init(src io.Reader, errh ErrorHandler) { func (p *parser) init(src io.Reader, errh ErrorHandler, pragh PragmaHandler) {
p.scanner.init(src, func(pos, line int, msg string) { p.scanner.init(src, func(pos, line int, msg string) {
p.nerrors++ p.nerrors++
if !debug && errh != nil { if !debug && errh != nil {
...@@ -36,7 +36,7 @@ func (p *parser) init(src io.Reader, errh ErrorHandler) { ...@@ -36,7 +36,7 @@ func (p *parser) init(src io.Reader, errh ErrorHandler) {
return return
} }
panic(fmt.Sprintf("%d: %s\n", line, msg)) panic(fmt.Sprintf("%d: %s\n", line, msg))
}) }, pragh)
p.fnest = 0 p.fnest = 0
p.xnest = 0 p.xnest = 0
...@@ -245,6 +245,10 @@ func (p *parser) file() *File { ...@@ -245,6 +245,10 @@ func (p *parser) file() *File {
continue continue
} }
// Reset p.pragma BEFORE advancing to the next token (consuming ';')
// since comments before may set pragmas for the next function decl.
p.pragma = 0
if p.tok != _EOF && !p.got(_Semi) { if p.tok != _EOF && !p.got(_Semi) {
p.syntax_error("after top level declaration") p.syntax_error("after top level declaration")
p.advance(_Const, _Type, _Var, _Func) p.advance(_Const, _Type, _Var, _Func)
...@@ -253,7 +257,6 @@ func (p *parser) file() *File { ...@@ -253,7 +257,6 @@ func (p *parser) file() *File {
// p.tok == _EOF // p.tok == _EOF
f.Lines = p.source.line f.Lines = p.source.line
f.Pragmas = p.pragmas
return f return f
} }
...@@ -372,6 +375,9 @@ func (p *parser) varDecl(group *Group) Decl { ...@@ -372,6 +375,9 @@ func (p *parser) varDecl(group *Group) Decl {
} }
} }
d.Group = group d.Group = group
if gcCompat {
d.init(p)
}
return d return d
} }
...@@ -426,8 +432,12 @@ func (p *parser) funcDecl() *FuncDecl { ...@@ -426,8 +432,12 @@ func (p *parser) funcDecl() *FuncDecl {
f.Name = p.name() f.Name = p.name()
f.Type = p.funcType() f.Type = p.funcType()
if gcCompat {
f.node = f.Type.node
}
f.Body = p.funcBody() f.Body = p.funcBody()
f.Pragma = p.pragma
f.EndLine = uint32(p.line) f.EndLine = uint32(p.line)
// TODO(gri) deal with function properties // TODO(gri) deal with function properties
...@@ -465,6 +475,9 @@ func (p *parser) binaryExpr(prec int) Expr { ...@@ -465,6 +475,9 @@ func (p *parser) binaryExpr(prec int) Expr {
tprec := p.prec tprec := p.prec
p.next() p.next()
t.Y = p.binaryExpr(tprec) t.Y = p.binaryExpr(tprec)
if gcCompat {
t.init(p)
}
x = t x = t
} }
return x return x
...@@ -485,6 +498,9 @@ func (p *parser) unaryExpr() Expr { ...@@ -485,6 +498,9 @@ func (p *parser) unaryExpr() Expr {
x.Op = p.op x.Op = p.op
p.next() p.next()
x.X = p.unaryExpr() x.X = p.unaryExpr()
if gcCompat {
x.init(p)
}
return x return x
case And: case And:
...@@ -730,6 +746,9 @@ loop: ...@@ -730,6 +746,9 @@ loop:
p.syntax_error("expecting name or (") p.syntax_error("expecting name or (")
p.advance(_Semi, _Rparen) p.advance(_Semi, _Rparen)
} }
if gcCompat {
x.init(p)
}
case _Lbrack: case _Lbrack:
p.next() p.next()
...@@ -851,6 +870,9 @@ func (p *parser) complitexpr() *CompositeLit { ...@@ -851,6 +870,9 @@ func (p *parser) complitexpr() *CompositeLit {
l.init(p) l.init(p)
l.Key = e l.Key = e
l.Value = p.bare_complitexpr() l.Value = p.bare_complitexpr()
if gcCompat {
l.init(p)
}
e = l e = l
x.NKeys++ x.NKeys++
} }
...@@ -860,6 +882,7 @@ func (p *parser) complitexpr() *CompositeLit { ...@@ -860,6 +882,7 @@ func (p *parser) complitexpr() *CompositeLit {
} }
} }
x.EndLine = uint32(p.line)
p.xnest-- p.xnest--
p.want(_Rbrace) p.want(_Rbrace)
...@@ -996,6 +1019,9 @@ func (p *parser) funcType() *FuncType { ...@@ -996,6 +1019,9 @@ func (p *parser) funcType() *FuncType {
typ.init(p) typ.init(p)
typ.ParamList = p.paramList() typ.ParamList = p.paramList()
typ.ResultList = p.funcResult() typ.ResultList = p.funcResult()
if gcCompat {
typ.init(p)
}
return typ return typ
} }
...@@ -1134,6 +1160,10 @@ func (p *parser) addField(styp *StructType, name *Name, typ Expr, tag *BasicLit) ...@@ -1134,6 +1160,10 @@ func (p *parser) addField(styp *StructType, name *Name, typ Expr, tag *BasicLit)
f.Type = typ f.Type = typ
styp.FieldList = append(styp.FieldList, f) styp.FieldList = append(styp.FieldList, f)
if gcCompat && name != nil {
f.node = name.node
}
if debug && tag != nil && len(styp.FieldList) != len(styp.TagList) { if debug && tag != nil && len(styp.FieldList) != len(styp.TagList) {
panic("inconsistent struct field list") panic("inconsistent struct field list")
} }
...@@ -1443,6 +1473,9 @@ func (p *parser) simpleStmt(lhs Expr, rangeOk bool) SimpleStmt { ...@@ -1443,6 +1473,9 @@ func (p *parser) simpleStmt(lhs Expr, rangeOk bool) SimpleStmt {
s.init(p) s.init(p)
s.Chan = lhs s.Chan = lhs
s.Value = p.expr() s.Value = p.expr()
if gcCompat {
s.init(p)
}
return s return s
default: default:
...@@ -1509,6 +1542,9 @@ func (p *parser) rangeClause(lhs Expr, def bool) *RangeClause { ...@@ -1509,6 +1542,9 @@ func (p *parser) rangeClause(lhs Expr, def bool) *RangeClause {
r.Lhs = lhs r.Lhs = lhs
r.Def = def r.Def = def
r.X = p.expr() r.X = p.expr()
if gcCompat {
r.init(p)
}
return r return r
} }
...@@ -1583,6 +1619,9 @@ func (p *parser) forStmt() Stmt { ...@@ -1583,6 +1619,9 @@ func (p *parser) forStmt() Stmt {
p.want(_For) p.want(_For)
s.Init, s.Cond, s.Post = p.header(true) s.Init, s.Cond, s.Post = p.header(true)
if gcCompat {
s.init(p)
}
s.Body = p.stmtBody("for clause") s.Body = p.stmtBody("for clause")
return s return s
...@@ -1672,6 +1711,10 @@ func (p *parser) ifStmt() *IfStmt { ...@@ -1672,6 +1711,10 @@ func (p *parser) ifStmt() *IfStmt {
p.error("missing condition in if statement") p.error("missing condition in if statement")
} }
if gcCompat {
s.init(p)
}
s.Then = p.stmtBody("if clause") s.Then = p.stmtBody("if clause")
if p.got(_Else) { if p.got(_Else) {
...@@ -1914,6 +1957,9 @@ func (p *parser) stmt() Stmt { ...@@ -1914,6 +1957,9 @@ func (p *parser) stmt() Stmt {
if p.tok != _Semi && p.tok != _Rbrace { if p.tok != _Semi && p.tok != _Rbrace {
s.Results = p.exprList() s.Results = p.exprList()
} }
if gcCompat {
s.init(p)
}
return s return s
case _Semi: case _Semi:
......
...@@ -22,7 +22,7 @@ var src = flag.String("src", "parser.go", "source file to parse") ...@@ -22,7 +22,7 @@ var src = flag.String("src", "parser.go", "source file to parse")
var verify = flag.Bool("verify", false, "verify idempotent printing") var verify = flag.Bool("verify", false, "verify idempotent printing")
func TestParse(t *testing.T) { func TestParse(t *testing.T) {
_, err := ReadFile(*src, nil, 0) _, err := ReadFile(*src, nil, nil, 0)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
...@@ -52,7 +52,7 @@ func TestStdLib(t *testing.T) { ...@@ -52,7 +52,7 @@ func TestStdLib(t *testing.T) {
if debug { if debug {
fmt.Printf("parsing %s\n", filename) fmt.Printf("parsing %s\n", filename)
} }
ast, err := ReadFile(filename, nil, 0) ast, err := ReadFile(filename, nil, nil, 0)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
...@@ -133,7 +133,7 @@ func verifyPrint(filename string, ast1 *File) { ...@@ -133,7 +133,7 @@ func verifyPrint(filename string, ast1 *File) {
panic(err) panic(err)
} }
ast2, err := ReadBytes(buf1.Bytes(), nil, 0) ast2, err := ReadBytes(buf1.Bytes(), nil, nil, 0)
if err != nil { if err != nil {
panic(err) panic(err)
} }
......
...@@ -15,7 +15,7 @@ func TestPrint(t *testing.T) { ...@@ -15,7 +15,7 @@ func TestPrint(t *testing.T) {
t.Skip("skipping test in short mode") t.Skip("skipping test in short mode")
} }
ast, err := ReadFile(*src, nil, 0) ast, err := ReadFile(*src, nil, nil, 0)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
......
...@@ -15,6 +15,7 @@ import ( ...@@ -15,6 +15,7 @@ import (
type scanner struct { type scanner struct {
source source
nlsemi bool // if set '\n' and EOF translate to ';' nlsemi bool // if set '\n' and EOF translate to ';'
pragma Pragma
// current token, valid after calling next() // current token, valid after calling next()
pos, line int pos, line int
...@@ -24,12 +25,13 @@ type scanner struct { ...@@ -24,12 +25,13 @@ type scanner struct {
op Operator // valid if tok is _Operator, _AssignOp, or _IncOp op Operator // valid if tok is _Operator, _AssignOp, or _IncOp
prec int // valid if tok is _Operator, _AssignOp, or _IncOp prec int // valid if tok is _Operator, _AssignOp, or _IncOp
pragmas []Pragma pragh PragmaHandler
} }
func (s *scanner) init(src io.Reader, errh ErrorHandler) { func (s *scanner) init(src io.Reader, errh ErrorHandler, pragh PragmaHandler) {
s.source.init(src, errh) s.source.init(src, errh)
s.nlsemi = false s.nlsemi = false
s.pragh = pragh
} }
func (s *scanner) next() { func (s *scanner) next() {
...@@ -540,6 +542,10 @@ func (s *scanner) lineComment() { ...@@ -540,6 +542,10 @@ func (s *scanner) lineComment() {
// recognize pragmas // recognize pragmas
var prefix string var prefix string
r := s.getr() r := s.getr()
if s.pragh == nil {
goto skip
}
switch r { switch r {
case 'g': case 'g':
prefix = "go:" prefix = "go:"
...@@ -565,10 +571,7 @@ func (s *scanner) lineComment() { ...@@ -565,10 +571,7 @@ func (s *scanner) lineComment() {
} }
r = s.getr() r = s.getr()
} }
s.pragmas = append(s.pragmas, Pragma{ s.pragma |= s.pragh(0, s.line, strings.TrimSuffix(string(s.stopLit()), "\r"))
Line: s.line,
Text: strings.TrimSuffix(string(s.stopLit()), "\r"),
})
return return
skip: skip:
......
...@@ -22,7 +22,7 @@ func TestScanner(t *testing.T) { ...@@ -22,7 +22,7 @@ func TestScanner(t *testing.T) {
defer src.Close() defer src.Close()
var s scanner var s scanner
s.init(src, nil) s.init(src, nil, nil)
for { for {
s.next() s.next()
if s.tok == _EOF { if s.tok == _EOF {
...@@ -51,7 +51,7 @@ func TestTokens(t *testing.T) { ...@@ -51,7 +51,7 @@ func TestTokens(t *testing.T) {
// scan source // scan source
var got scanner var got scanner
got.init(&bytesReader{buf}, nil) got.init(&bytesReader{buf}, nil, nil)
got.next() got.next()
for i, want := range sampleTokens { for i, want := range sampleTokens {
nlsemi := false nlsemi := false
...@@ -338,7 +338,7 @@ func TestScanErrors(t *testing.T) { ...@@ -338,7 +338,7 @@ func TestScanErrors(t *testing.T) {
} else if nerrors > 1 { } else if nerrors > 1 {
t.Errorf("%q: got unexpected %q at pos = %d, line = %d", test.src, msg, pos, line) t.Errorf("%q: got unexpected %q at pos = %d, line = %d", test.src, msg, pos, line)
} }
}) }, nil)
for { for {
s.next() s.next()
......
...@@ -12,17 +12,27 @@ import ( ...@@ -12,17 +12,27 @@ import (
type Mode uint type Mode uint
// A Pragma value is a set of flags that augment a function
// declaration. Callers may assign meaning to the flags as
// appropriate.
type Pragma uint16
type ErrorHandler func(pos, line int, msg string) 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. // TODO(gri) These need a lot more work.
func ReadFile(filename string, errh ErrorHandler, mode Mode) (*File, error) { func ReadFile(filename string, errh ErrorHandler, pragh PragmaHandler, mode Mode) (*File, error) {
src, err := os.Open(filename) src, err := os.Open(filename)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer src.Close() defer src.Close()
return Read(src, errh, mode) return Read(src, errh, pragh, mode)
} }
type bytesReader struct { type bytesReader struct {
...@@ -38,13 +48,13 @@ func (r *bytesReader) Read(p []byte) (int, error) { ...@@ -38,13 +48,13 @@ func (r *bytesReader) Read(p []byte) (int, error) {
return 0, io.EOF return 0, io.EOF
} }
func ReadBytes(src []byte, errh ErrorHandler, mode Mode) (*File, error) { func ReadBytes(src []byte, errh ErrorHandler, pragh PragmaHandler, mode Mode) (*File, error) {
return Read(&bytesReader{src}, errh, mode) return Read(&bytesReader{src}, errh, pragh, mode)
} }
func Read(src io.Reader, errh ErrorHandler, mode Mode) (*File, error) { func Read(src io.Reader, errh ErrorHandler, pragh PragmaHandler, mode Mode) (*File, error) {
var p parser var p parser
p.init(src, errh) p.init(src, errh, pragh)
p.next() p.next()
ast := p.file() ast := p.file()
......
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