aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/compile/internal/syntax/parser.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/compile/internal/syntax/parser.go')
-rw-r--r--src/cmd/compile/internal/syntax/parser.go152
1 files changed, 100 insertions, 52 deletions
diff --git a/src/cmd/compile/internal/syntax/parser.go b/src/cmd/compile/internal/syntax/parser.go
index f267d4b2c9..9544001a2e 100644
--- a/src/cmd/compile/internal/syntax/parser.go
+++ b/src/cmd/compile/internal/syntax/parser.go
@@ -13,6 +13,11 @@ import (
const debug = false
const trace = false
+// The old gc parser assigned line numbers very inconsistently depending
+// on when it happened to construct AST nodes. To make transitioning to the
+// new AST easier, we try to mimick the behavior as much as possible.
+const gcCompat = true
+
type parser struct {
scanner
@@ -60,6 +65,11 @@ func (p *parser) want(tok token) {
// syntax_error reports a syntax error at the current line.
func (p *parser) syntax_error(msg string) {
+ p.syntax_error_at(p.pos, p.line, msg)
+}
+
+// Like syntax_error, but reports error at given line rather than current lexer line.
+func (p *parser) syntax_error_at(pos, line int, msg string) {
if trace {
defer p.trace("syntax_error (" + msg + ")")()
}
@@ -78,15 +88,17 @@ func (p *parser) syntax_error(msg string) {
msg = ", " + msg
default:
// plain error - we don't care about current token
- p.error("syntax error: " + msg)
+ p.error_at(pos, line, "syntax error: "+msg)
return
}
// determine token string
var tok string
switch p.tok {
- case _Name, _Literal:
+ case _Name:
tok = p.lit
+ case _Literal:
+ tok = "literal " + p.lit
case _Operator:
tok = p.op.String()
case _AssignOp:
@@ -98,17 +110,7 @@ func (p *parser) syntax_error(msg string) {
tok = tokstring(p.tok)
}
- p.error("syntax error: unexpected " + tok + msg)
-}
-
-// Like syntax_error, but reports error at given line rather than current lexer line.
-func (p *parser) syntax_error_at(lineno uint32, msg string) {
- // TODO(gri) fix this
- // defer func(lineno int32) {
- // lexlineno = lineno
- // }(lexlineno)
- // lexlineno = lineno
- p.syntax_error(msg)
+ p.error_at(pos, line, "syntax error: unexpected "+tok+msg)
}
// The stopset contains keywords that start a statement.
@@ -195,7 +197,10 @@ func (p *parser) file() *File {
f.init(p)
// PackageClause
- p.want(_Package)
+ if !p.got(_Package) {
+ p.syntax_error("package statement must be first")
+ return nil
+ }
f.PkgName = p.name()
p.want(_Semi)
@@ -296,7 +301,7 @@ func (p *parser) importDecl(group *Group) Decl {
d.LocalPkgName = n
p.next()
}
- if p.tok == _Literal && p.kind == StringLit {
+ if p.tok == _Literal && (gcCompat || p.kind == StringLit) {
d.Path = p.oliteral()
} else {
p.syntax_error("missing import path; require quoted string")
@@ -384,17 +389,18 @@ func (p *parser) funcDecl() *FuncDecl {
f := new(FuncDecl)
f.init(p)
+ badRecv := false
if p.tok == _Lparen {
rcvr := p.paramList()
switch len(rcvr) {
case 0:
p.error("method has no receiver")
- return nil // TODO(gri) better solution
+ badRecv = true
case 1:
f.Recv = rcvr[0]
default:
p.error("method has multiple receivers")
- return nil // TODO(gri) better solution
+ badRecv = true
}
}
@@ -422,11 +428,16 @@ func (p *parser) funcDecl() *FuncDecl {
f.Type = p.funcType()
f.Body = p.funcBody()
+ f.EndLine = uint32(p.line)
+
// TODO(gri) deal with function properties
// if noescape && body != nil {
// p.error("can only use //go:noescape with external func implementations")
// }
+ if badRecv {
+ return nil // TODO(gri) better solution
+ }
return f
}
@@ -508,25 +519,29 @@ func (p *parser) unaryExpr() Expr {
// <-(chan E) => (<-chan E)
// <-(chan<-E) => (<-chan (<-E))
- if x, ok := x.(*ChanType); ok {
+ if _, ok := x.(*ChanType); ok {
// x is a channel type => re-associate <-
dir := SendOnly
t := x
- for ok && dir == SendOnly {
- dir = t.Dir
+ for dir == SendOnly {
+ c, ok := t.(*ChanType)
+ if !ok {
+ break
+ }
+ dir = c.Dir
if dir == RecvOnly {
// t is type <-chan E but <-<-chan E is not permitted
// (report same error as for "type _ <-<-chan E")
p.syntax_error("unexpected <-, expecting chan")
// already progressed, no need to advance
}
- t.Dir = RecvOnly
- t, ok = t.Elem.(*ChanType)
+ c.Dir = RecvOnly
+ t = c.Elem
}
if dir == SendOnly {
// channel dir is <- but channel element E is not a channel
// (report same error as for "type _ <-chan<-E")
- p.syntax_error(fmt.Sprintf("unexpected %v, expecting chan", t))
+ p.syntax_error(fmt.Sprintf("unexpected %s, expecting chan", String(t)))
// already progressed, no need to advance
}
return x
@@ -536,7 +551,10 @@ func (p *parser) unaryExpr() Expr {
return &Operation{Op: Recv, X: x}
}
- return p.pexpr(false)
+ // TODO(mdempsky): We need parens here so we can report an
+ // error for "(x) := true". It should be possible to detect
+ // and reject that more efficiently though.
+ return p.pexpr(true)
}
// callStmt parses call-like statements that can be preceded by 'defer' and 'go'.
@@ -554,6 +572,9 @@ func (p *parser) callStmt() *CallStmt {
switch x := x.(type) {
case *CallExpr:
s.Call = x
+ if gcCompat {
+ s.node = x.node
+ }
case *ParenExpr:
p.error(fmt.Sprintf("expression in %s must not be parenthesized", s.Tok))
// already progressed, no need to advance
@@ -624,6 +645,7 @@ func (p *parser) operand(keep_parens bool) Expr {
f.init(p)
f.Type = t
f.Body = p.funcBody()
+ f.EndLine = uint32(p.line)
p.xnest--
p.fnest--
return f
@@ -739,6 +761,7 @@ loop:
t.Index[1] = p.expr()
}
if p.got(_Colon) {
+ t.Full = true
// x[i:j:...]
if t.Index[1] == nil {
p.error("middle index required in 3-index slice")
@@ -756,13 +779,7 @@ loop:
p.xnest--
case _Lparen:
- // call or conversion
- // convtype '(' expr ocomma ')'
- c := new(CallExpr)
- c.init(p)
- c.Fun = x
- c.ArgList, c.HasDots = p.argList()
- x = c
+ x = p.call(x)
case _Lbrace:
// operand may have returned a parenthesized complit
@@ -1028,6 +1045,9 @@ func (p *parser) structType() *StructType {
break
}
}
+ if gcCompat {
+ typ.init(p)
+ }
p.want(_Rbrace)
return typ
@@ -1052,6 +1072,9 @@ func (p *parser) interfaceType() *InterfaceType {
break
}
}
+ if gcCompat {
+ typ.init(p)
+ }
p.want(_Rbrace)
return typ
@@ -1442,7 +1465,8 @@ func (p *parser) simpleStmt(lhs Expr, rangeOk bool) SimpleStmt {
return p.newAssignStmt(0, lhs, p.exprList())
case _Define:
- //lno := lineno
+ var n node
+ n.init(p)
p.next()
if rangeOk && p.got(_Range) {
@@ -1466,7 +1490,11 @@ func (p *parser) simpleStmt(lhs Expr, rangeOk bool) SimpleStmt {
return &ExprStmt{X: x}
}
- return p.newAssignStmt(Def, lhs, rhs)
+ as := p.newAssignStmt(Def, lhs, rhs)
+ if gcCompat {
+ as.node = n
+ }
+ return as
default:
p.syntax_error("expecting := or = or comma")
@@ -1498,21 +1526,22 @@ func (p *parser) labeledStmt(label *Name) Stmt {
defer p.trace("labeledStmt")()
}
- var ls Stmt // labeled statement
+ s := new(LabeledStmt)
+ s.init(p)
+ s.Label = label
+
+ p.want(_Colon)
+
if p.tok != _Rbrace && p.tok != _EOF {
- ls = p.stmt()
- if ls == missing_stmt {
+ s.Stmt = p.stmt()
+ if s.Stmt == missing_stmt {
// report error at line of ':' token
- p.syntax_error_at(label.line, "missing statement after label")
+ p.syntax_error_at(int(label.pos), int(label.line), "missing statement after label")
// we are already at the end of the labeled statement - no need to advance
return missing_stmt
}
}
- s := new(LabeledStmt)
- s.init(p)
- s.Label = label
- s.Stmt = ls
return s
}
@@ -1586,8 +1615,8 @@ func (p *parser) header(forStmt bool) (init SimpleStmt, cond Expr, post SimpleSt
if p.tok != _Semi {
// accept potential varDecl but complain
- if p.got(_Var) {
- p.error("var declaration not allowed in initializer")
+ if forStmt && p.got(_Var) {
+ p.error("var declaration not allowed in for initializer")
}
init = p.simpleStmt(nil, forStmt)
// If we have a range clause, we are done.
@@ -1646,10 +1675,14 @@ func (p *parser) ifStmt() *IfStmt {
s.Then = p.stmtBody("if clause")
if p.got(_Else) {
- if p.tok == _If {
+ switch p.tok {
+ case _If:
s.Else = p.ifStmt()
- } else {
+ case _Lbrace:
s.Else = p.blockStmt()
+ default:
+ p.error("else must be followed by if or statement block")
+ p.advance(_Name, _Rbrace)
}
}
@@ -1721,6 +1754,9 @@ func (p *parser) caseClause() *CaseClause {
p.advance(_Case, _Default, _Rbrace)
}
+ if gcCompat {
+ c.init(p)
+ }
p.want(_Colon)
c.Body = p.stmtList()
@@ -1765,6 +1801,9 @@ func (p *parser) commClause() *CommClause {
p.advance(_Case, _Default, _Rbrace)
}
+ if gcCompat {
+ c.init(p)
+ }
p.want(_Colon)
c.Body = p.stmtList()
@@ -1790,7 +1829,7 @@ func (p *parser) stmt() Stmt {
// look for it first before doing anything more expensive.
if p.tok == _Name {
lhs := p.exprList()
- if label, ok := lhs.(*Name); ok && p.got(_Colon) {
+ if label, ok := lhs.(*Name); ok && p.tok == _Colon {
return p.labeledStmt(label)
}
return p.simpleStmt(lhs, false)
@@ -1912,26 +1951,35 @@ func (p *parser) stmtList() (l []Stmt) {
}
// Arguments = "(" [ ( ExpressionList | Type [ "," ExpressionList ] ) [ "..." ] [ "," ] ] ")" .
-func (p *parser) argList() (list []Expr, hasDots bool) {
+func (p *parser) call(fun Expr) *CallExpr {
if trace {
- defer p.trace("argList")()
+ defer p.trace("call")()
}
+ // call or conversion
+ // convtype '(' expr ocomma ')'
+ c := new(CallExpr)
+ c.init(p)
+ c.Fun = fun
+
p.want(_Lparen)
p.xnest++
for p.tok != _EOF && p.tok != _Rparen {
- list = append(list, p.expr()) // expr_or_type
- hasDots = p.got(_DotDotDot)
- if !p.ocomma(_Rparen) || hasDots {
+ c.ArgList = append(c.ArgList, p.expr()) // expr_or_type
+ c.HasDots = p.got(_DotDotDot)
+ if !p.ocomma(_Rparen) || c.HasDots {
break
}
}
p.xnest--
+ if gcCompat {
+ c.init(p)
+ }
p.want(_Rparen)
- return
+ return c
}
// ----------------------------------------------------------------------------