--- /dev/null
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package syntax
+
+import (
+ "fmt"
+ "strings"
+ "testing"
+)
+
+// A test is a source code snippet of a particular node type.
+// In the snippet, a '@' indicates the position recorded by
+// the parser when creating the respective node.
+type test struct {
+ nodetyp string
+ snippet string
+}
+
+var decls = []test{
+ // The position of declarations is always the
+ // position of the first token of an individual
+ // declaration, independent of grouping.
+ {"ImportDecl", `import @"math"`},
+ {"ImportDecl", `import @mymath "math"`},
+ {"ImportDecl", `import @. "math"`},
+ {"ImportDecl", `import (@"math")`},
+ {"ImportDecl", `import (@mymath "math")`},
+ {"ImportDecl", `import (@. "math")`},
+
+ {"ConstDecl", `const @x`},
+ {"ConstDecl", `const @x = 0`},
+ {"ConstDecl", `const @x, y, z = 0, 1, 2`},
+ {"ConstDecl", `const (@x)`},
+ {"ConstDecl", `const (@x = 0)`},
+ {"ConstDecl", `const (@x, y, z = 0, 1, 2)`},
+
+ {"TypeDecl", `type @T int`},
+ {"TypeDecl", `type @T = int`},
+ {"TypeDecl", `type (@T int)`},
+ {"TypeDecl", `type (@T = int)`},
+
+ {"VarDecl", `var @x int`},
+ {"VarDecl", `var @x, y, z int`},
+ {"VarDecl", `var @x int = 0`},
+ {"VarDecl", `var @x, y, z int = 1, 2, 3`},
+ {"VarDecl", `var @x = 0`},
+ {"VarDecl", `var @x, y, z = 1, 2, 3`},
+ {"VarDecl", `var (@x int)`},
+ {"VarDecl", `var (@x, y, z int)`},
+ {"VarDecl", `var (@x int = 0)`},
+ {"VarDecl", `var (@x, y, z int = 1, 2, 3)`},
+ {"VarDecl", `var (@x = 0)`},
+ {"VarDecl", `var (@x, y, z = 1, 2, 3)`},
+
+ {"FuncDecl", `func @f() {}`},
+ {"FuncDecl", `func @(T) f() {}`},
+ {"FuncDecl", `func @(x T) f() {}`},
+}
+
+var exprs = []test{
+ // The position of an expression is the position
+ // of the left-most token that identifies the
+ // kind of expression.
+ {"Name", `@x`},
+
+ {"BasicLit", `@0`},
+ {"BasicLit", `@0x123`},
+ {"BasicLit", `@3.1415`},
+ {"BasicLit", `@.2718`},
+ {"BasicLit", `@1i`},
+ {"BasicLit", `@'a'`},
+ {"BasicLit", `@"abc"`},
+ {"BasicLit", "@`abc`"},
+
+ {"CompositeLit", `@{}`},
+ {"CompositeLit", `T@{}`},
+ {"CompositeLit", `struct{x, y int}@{}`},
+
+ {"KeyValueExpr", `"foo"@: true`},
+ {"KeyValueExpr", `"a"@: b`},
+
+ {"FuncLit", `@func (){}`},
+ {"ParenExpr", `@(x)`},
+ {"SelectorExpr", `a@.b`},
+ {"IndexExpr", `a@[i]`},
+
+ {"SliceExpr", `a@[:]`},
+ {"SliceExpr", `a@[i:]`},
+ {"SliceExpr", `a@[:j]`},
+ {"SliceExpr", `a@[i:j]`},
+ {"SliceExpr", `a@[i:j:k]`},
+
+ {"AssertExpr", `x@.(T)`},
+
+ {"Operation", `@*b`},
+ {"Operation", `@+b`},
+ {"Operation", `@-b`},
+ {"Operation", `@!b`},
+ {"Operation", `@^b`},
+ {"Operation", `@&b`},
+ {"Operation", `@<-b`},
+
+ {"Operation", `a @|| b`},
+ {"Operation", `a @&& b`},
+ {"Operation", `a @== b`},
+ {"Operation", `a @+ b`},
+ {"Operation", `a @* b`},
+
+ {"CallExpr", `f@()`},
+ {"CallExpr", `f@(x, y, z)`},
+ {"CallExpr", `obj.f@(1, 2, 3)`},
+ {"CallExpr", `func(x int) int { return x + 1 }@(y)`},
+
+ // ListExpr: tested via multi-value const/var declarations
+}
+
+var types = []test{
+ {"Operation", `@*T`},
+ {"Operation", `@*struct{}`},
+
+ {"ArrayType", `@[10]T`},
+ {"ArrayType", `@[...]T`},
+
+ {"SliceType", `@[]T`},
+ {"DotsType", `@...T`},
+ {"StructType", `@struct{}`},
+ {"InterfaceType", `@interface{}`},
+ {"FuncType", `func@()`},
+ {"MapType", `@map[T]T`},
+
+ {"ChanType", `@chan T`},
+ {"ChanType", `@chan<- T`},
+ {"ChanType", `@<-chan T`},
+}
+
+var fields = []test{
+ {"Field", `@T`},
+ {"Field", `@(T)`},
+ {"Field", `@x T`},
+ {"Field", `@x *(T)`},
+ {"Field", `@x, y, z T`},
+ {"Field", `@x, y, z (*T)`},
+}
+
+var stmts = []test{
+ {"EmptyStmt", `@;`},
+
+ {"LabeledStmt", `L@:`},
+ {"LabeledStmt", `L@: ;`},
+ {"LabeledStmt", `L@: f()`},
+
+ {"BlockStmt", `@{}`},
+
+ // The position of an ExprStmt is the position of the expression.
+ {"ExprStmt", `@<-ch`},
+ {"ExprStmt", `f@()`},
+ {"ExprStmt", `append@(s, 1, 2, 3)`},
+
+ {"SendStmt", `ch @<- x`},
+
+ {"DeclStmt", `@const x = 0`},
+ {"DeclStmt", `@const (x = 0)`},
+ {"DeclStmt", `@type T int`},
+ {"DeclStmt", `@type T = int`},
+ {"DeclStmt", `@type (T1 = int; T2 = float32)`},
+ {"DeclStmt", `@var x = 0`},
+ {"DeclStmt", `@var x, y, z int`},
+ {"DeclStmt", `@var (a, b = 1, 2)`},
+
+ {"AssignStmt", `x @= y`},
+ {"AssignStmt", `a, b, x @= 1, 2, 3`},
+ {"AssignStmt", `x @+= y`},
+ {"AssignStmt", `x @:= y`},
+ {"AssignStmt", `x, ok @:= f()`},
+ {"AssignStmt", `x@++`},
+ {"AssignStmt", `a[i]@--`},
+
+ {"BranchStmt", `@break`},
+ {"BranchStmt", `@break L`},
+ {"BranchStmt", `@continue`},
+ {"BranchStmt", `@continue L`},
+ {"BranchStmt", `@fallthrough`},
+ {"BranchStmt", `@goto L`},
+
+ {"CallStmt", `@defer f()`},
+ {"CallStmt", `@go f()`},
+
+ {"ReturnStmt", `@return`},
+ {"ReturnStmt", `@return x`},
+ {"ReturnStmt", `@return a, b, c`},
+
+ {"IfStmt", `@if cond {}`},
+ {"ForStmt", `@for {}`},
+ {"SwitchStmt", `@switch {}`},
+ {"SelectStmt", `@select {}`},
+}
+
+var ranges = []test{
+ {"RangeClause", `for @range s {}`},
+ {"RangeClause", `for _, i = @range s {}`},
+ {"RangeClause", `for x, i = @range s {}`},
+ {"RangeClause", `for _, i := @range s {}`},
+ {"RangeClause", `for x, i := @range s {}`},
+}
+
+var guards = []test{
+ {"TypeSwitchGuard", `switch x@.(type) {}`},
+ {"TypeSwitchGuard", `switch x := x@.(type) {}`},
+ {"TypeSwitchGuard", `switch a = b; x@.(type) {}`},
+ {"TypeSwitchGuard", `switch a := b; x := x@.(type) {}`},
+}
+
+var cases = []test{
+ {"CaseClause", ` switch { @case x: }`},
+ {"CaseClause", ` switch { @case x, y, z: }`},
+ {"CaseClause", ` switch { @case x == 1, y == 2: }`},
+ {"CaseClause", ` switch { @default: }`},
+}
+
+var comms = []test{
+ {"CommClause", `select { @case <-ch: }`},
+ {"CommClause", `select { @case x <- ch: }`},
+ {"CommClause", `select { @case x = <-ch: }`},
+ {"CommClause", `select { @case x := <-ch: }`},
+ {"CommClause", `select { @case x, ok = <-ch: }`},
+ {"CommClause", `select { @case x, ok := <-ch: }`},
+ {"CommClause", `select { @default: }`},
+}
+
+func TestPos(t *testing.T) {
+ // TODO(gri) Once we have a general tree walker, we can use that to find
+ // the first occurence of the respective node and we don't need to hand-
+ // extract the node for each specific kind of construct.
+
+ testPos(t, decls, "package p; ", "",
+ func(f *File) Node { return f.DeclList[0] },
+ )
+
+ // embed expressions in a composite literal so we can test key:value and naked composite literals
+ testPos(t, exprs, "package p; var _ = T{ ", " }",
+ func(f *File) Node { return f.DeclList[0].(*VarDecl).Values.(*CompositeLit).ElemList[0] },
+ )
+
+ // embed types in a function signature so we can test ... types
+ testPos(t, types, "package p; func f(", ")",
+ func(f *File) Node { return f.DeclList[0].(*FuncDecl).Type.ParamList[0].Type },
+ )
+
+ testPos(t, fields, "package p; func f(", ")",
+ func(f *File) Node { return f.DeclList[0].(*FuncDecl).Type.ParamList[0] },
+ )
+
+ testPos(t, stmts, "package p; func _() { ", " } ",
+ func(f *File) Node { return f.DeclList[0].(*FuncDecl).Body[0] },
+ )
+
+ testPos(t, ranges, "package p; func _() { ", " } ",
+ func(f *File) Node { return f.DeclList[0].(*FuncDecl).Body[0].(*ForStmt).Init.(*RangeClause) },
+ )
+
+ testPos(t, guards, "package p; func _() { ", " } ",
+ func(f *File) Node { return f.DeclList[0].(*FuncDecl).Body[0].(*SwitchStmt).Tag.(*TypeSwitchGuard) },
+ )
+
+ testPos(t, cases, "package p; func _() { ", " } ",
+ func(f *File) Node { return f.DeclList[0].(*FuncDecl).Body[0].(*SwitchStmt).Body[0] },
+ )
+
+ testPos(t, comms, "package p; func _() { ", " } ",
+ func(f *File) Node { return f.DeclList[0].(*FuncDecl).Body[0].(*SelectStmt).Body[0] },
+ )
+}
+
+func testPos(t *testing.T, list []test, prefix, suffix string, extract func(*File) Node) {
+ for _, test := range list {
+ // complete source, compute @ position, and strip @ from source
+ src, index := stripAt(prefix + test.snippet + suffix)
+ if index < 0 {
+ t.Errorf("missing @: %s", src)
+ continue
+ }
+
+ // build syntaxt tree
+ file, err := ParseBytes(nil, []byte(src), nil, nil, 0)
+ if err != nil {
+ t.Errorf("parse error: %s: %v", src, err)
+ continue
+ }
+
+ // extract desired node
+ node := extract(file)
+ if typ := typeOf(node); typ != test.nodetyp {
+ t.Errorf("type error: %s: type = %s, want %s", src, typ, test.nodetyp)
+ continue
+ }
+
+ // verify node position with expected position as indicated by @
+ if col := int(node.Pos().Col()); col != index {
+ t.Errorf("pos error: %s: col = %d, want %d", src, col, index)
+ continue
+ }
+ }
+}
+
+func stripAt(s string) (string, int) {
+ if i := strings.Index(s, "@"); i >= 0 {
+ return s[:i] + s[i+1:], i
+ }
+ return s, -1
+}
+
+func typeOf(n Node) string {
+ const prefix = "*syntax."
+ k := fmt.Sprintf("%T", n)
+ if strings.HasPrefix(k, prefix) {
+ return k[len(prefix):]
+ }
+ return k
+}
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 {
base *src.PosBase
errh ErrorHandler
}
f := new(File)
- f.init(p)
+ f.pos = p.pos()
// PackageClause
if !p.got(_Package) {
}
d := new(ImportDecl)
- d.init(p)
+ d.pos = p.pos()
switch p.tok {
case _Name:
d.LocalPkgName = p.name()
case _Dot:
n := new(Name)
- n.init(p)
+ n.pos = p.pos()
n.Value = "."
d.LocalPkgName = n
p.next()
}
d := new(ConstDecl)
- d.init(p)
+ d.pos = p.pos()
d.NameList = p.nameList(p.name())
if p.tok != _EOF && p.tok != _Semi && p.tok != _Rparen {
}
d := new(TypeDecl)
- d.init(p)
+ d.pos = p.pos()
d.Name = p.name()
d.Alias = p.got(_Assign)
}
d := new(VarDecl)
- d.init(p)
+ d.pos = p.pos()
d.NameList = p.nameList(p.name())
if p.got(_Assign) {
}
}
d.Group = group
- if gcCompat {
- d.init(p)
- }
return d
}
}
f := new(FuncDecl)
- f.init(p)
+ f.pos = p.pos()
badRecv := false
if p.tok == _Lparen {
f.Name = p.name()
f.Type = p.funcType()
- if gcCompat {
- f.node = f.Type.node
- }
f.Body = p.funcBody()
f.Pragma = p.pragma
x := p.unaryExpr()
for (p.tok == _Operator || p.tok == _Star) && p.prec > prec {
t := new(Operation)
- t.init(p)
+ t.pos = p.pos()
t.Op = p.op
t.X = x
tprec := p.prec
p.next()
t.Y = p.binaryExpr(tprec)
- if gcCompat {
- t.init(p)
- }
x = t
}
return x
switch p.op {
case Mul, Add, Sub, Not, Xor:
x := new(Operation)
- x.init(p)
+ x.pos = p.pos()
x.Op = p.op
p.next()
x.X = p.unaryExpr()
- if gcCompat {
- x.init(p)
- }
return x
case And:
- p.next()
x := new(Operation)
- x.init(p)
+ x.pos = p.pos()
x.Op = And
+ p.next()
// unaryExpr may have returned a parenthesized composite literal
// (see comment in operand) - remove parentheses if any
x.X = unparen(p.unaryExpr())
case _Arrow:
// receive op (<-x) or receive-only channel (<-chan E)
+ pos := p.pos()
p.next()
// If the next token is _Chan we still don't know if it is
}
// x is not a channel type => we have a receive op
- return &Operation{Op: Recv, X: x}
+ o := new(Operation)
+ o.pos = pos
+ o.Op = Recv
+ o.X = x
+ return o
}
// TODO(mdempsky): We need parens here so we can report an
}
s := new(CallStmt)
- s.init(p)
+ s.pos = p.pos()
s.Tok = p.tok // _Defer or _Go
p.next()
return p.oliteral()
case _Lparen:
+ pos := p.pos()
p.next()
p.xnest++
x := p.expr() // expr_or_type
// in a go/defer statement. In that case, operand is called
// with keep_parens set.
if keep_parens {
- x = &ParenExpr{X: x}
+ px := new(ParenExpr)
+ px.pos = pos
+ px.X = x
+ x = px
}
return x
case _Func:
+ pos := p.pos()
p.next()
t := p.funcType()
if p.tok == _Lbrace {
p.fnest++
p.xnest++
+
f := new(FuncLit)
- f.init(p)
+ f.pos = pos
f.Type = t
f.Body = p.funcBody()
f.EndLine = p.line
+
p.xnest--
p.fnest--
return f
loop:
for {
+ pos := p.pos()
switch p.tok {
case _Dot:
p.next()
case _Name:
// pexpr '.' sym
t := new(SelectorExpr)
- t.init(p)
+ t.pos = pos
t.X = x
t.Sel = p.name()
x = t
p.next()
if p.got(_Type) {
t := new(TypeSwitchGuard)
- t.init(p)
+ t.pos = pos
t.X = x
x = t
} else {
t := new(AssertExpr)
- t.init(p)
+ t.pos = pos
t.X = x
t.Type = p.expr()
x = t
p.syntax_error("expecting name or (")
p.advance(_Semi, _Rparen)
}
- if gcCompat && x != nil {
- x.init(p)
- }
case _Lbrack:
p.next()
if p.got(_Rbrack) {
// x[i]
t := new(IndexExpr)
- t.init(p)
+ t.pos = pos
t.X = x
t.Index = i
x = t
// x[i:...
t := new(SliceExpr)
- t.init(p)
+ t.pos = pos
t.X = x
t.Index[0] = i
p.want(_Colon)
}
x := new(CompositeLit)
- x.init(p)
+ x.pos = p.pos()
p.want(_Lbrace)
p.xnest++
for p.tok != _EOF && p.tok != _Rbrace {
// value
e := p.bare_complitexpr()
- if p.got(_Colon) {
+ if p.tok == _Colon {
// key ':' value
l := new(KeyValueExpr)
- l.init(p)
+ l.pos = p.pos()
+ p.next()
l.Key = e
l.Value = p.bare_complitexpr()
- if gcCompat {
- l.init(p)
- }
e = l
x.NKeys++
}
return typ
}
- p.syntax_error("")
+ p.syntax_error("expecting type")
p.advance()
return nil
}
-func indirect(typ Expr) Expr {
- return &Operation{Op: Mul, X: typ}
+func indirect(pos src.Pos, typ Expr) Expr {
+ o := new(Operation)
+ o.pos = pos
+ o.Op = Mul
+ o.X = typ
+ return o
}
// tryType is like type_ but it returns nil if there was no type
defer p.trace("tryType")()
}
+ pos := p.pos()
switch p.tok {
case _Star:
// ptrtype
p.next()
- return indirect(p.type_())
+ return indirect(pos, p.type_())
case _Arrow:
// recvchantype
p.next()
p.want(_Chan)
t := new(ChanType)
- t.init(p)
+ t.pos = pos
t.Dir = RecvOnly
t.Elem = p.chanElem()
return t
// []T
p.xnest--
t := new(SliceType)
- t.init(p)
+ t.pos = pos
t.Elem = p.type_()
return t
}
// [n]T
t := new(ArrayType)
- t.init(p)
+ t.pos = pos
if !p.got(_DotDotDot) {
t.Len = p.expr()
}
// _Chan _Comm ntype
p.next()
t := new(ChanType)
- t.init(p)
+ t.pos = pos
if p.got(_Arrow) {
t.Dir = SendOnly
}
p.next()
p.want(_Lbrack)
t := new(MapType)
- t.init(p)
+ t.pos = pos
t.Key = p.type_()
p.want(_Rbrack)
t.Value = p.type_()
}
typ := new(FuncType)
- typ.init(p)
+ typ.pos = p.pos()
typ.ParamList = p.paramList()
typ.ResultList = p.funcResult()
- if gcCompat {
- typ.init(p)
- }
+
return typ
}
defer p.trace("dotname")()
}
- if p.got(_Dot) {
+ if p.tok == _Dot {
s := new(SelectorExpr)
- s.init(p)
+ s.pos = p.pos()
+ p.next()
s.X = name
s.Sel = p.name()
return s
}
typ := new(StructType)
- typ.init(p)
+ typ.pos = p.pos()
p.want(_Struct)
p.want(_Lbrace)
}
typ := new(InterfaceType)
- typ.init(p)
+ typ.pos = p.pos()
p.want(_Interface)
p.want(_Lbrace)
return p.paramList()
}
+ pos := p.pos()
if result := p.tryType(); result != nil {
f := new(Field)
- f.init(p)
+ f.pos = pos
f.Type = result
return []*Field{f}
}
return nil
}
-func (p *parser) addField(styp *StructType, name *Name, typ Expr, tag *BasicLit) {
+func (p *parser) addField(styp *StructType, pos src.Pos, name *Name, typ Expr, tag *BasicLit) {
if tag != nil {
for i := len(styp.FieldList) - len(styp.TagList); i > 0; i-- {
styp.TagList = append(styp.TagList, nil)
}
f := new(Field)
- f.init(p)
+ f.pos = pos
f.Name = name
f.Type = typ
styp.FieldList = append(styp.FieldList, f)
- if gcCompat && name != nil {
- f.node = name.node
- }
-
if debug && tag != nil && len(styp.FieldList) != len(styp.TagList) {
panic("inconsistent struct field list")
}
defer p.trace("fieldDecl")()
}
- var name *Name
+ pos := p.pos()
switch p.tok {
case _Name:
- name = p.name()
+ name := p.name()
if p.tok == _Dot || p.tok == _Literal || p.tok == _Semi || p.tok == _Rbrace {
// embed oliteral
typ := p.qualifiedName(name)
tag := p.oliteral()
- p.addField(styp, nil, typ, tag)
+ p.addField(styp, pos, nil, typ, tag)
return
}
tag := p.oliteral()
for _, name := range names {
- p.addField(styp, name, typ, tag)
+ p.addField(styp, name.Pos(), name, typ, tag)
}
case _Lparen:
p.next()
if p.tok == _Star {
// '(' '*' embed ')' oliteral
+ pos := p.pos()
p.next()
- typ := indirect(p.qualifiedName(nil))
+ typ := indirect(pos, p.qualifiedName(nil))
p.want(_Rparen)
tag := p.oliteral()
- p.addField(styp, nil, typ, tag)
+ p.addField(styp, pos, nil, typ, tag)
p.syntax_error("cannot parenthesize embedded type")
} else {
typ := p.qualifiedName(nil)
p.want(_Rparen)
tag := p.oliteral()
- p.addField(styp, nil, typ, tag)
+ p.addField(styp, pos, nil, typ, tag)
p.syntax_error("cannot parenthesize embedded type")
}
p.next()
if p.got(_Lparen) {
// '*' '(' embed ')' oliteral
- typ := indirect(p.qualifiedName(nil))
+ typ := indirect(pos, p.qualifiedName(nil))
p.want(_Rparen)
tag := p.oliteral()
- p.addField(styp, nil, typ, tag)
+ p.addField(styp, pos, nil, typ, tag)
p.syntax_error("cannot parenthesize embedded type")
} else {
// '*' embed oliteral
- typ := indirect(p.qualifiedName(nil))
+ typ := indirect(pos, p.qualifiedName(nil))
tag := p.oliteral()
- p.addField(styp, nil, typ, tag)
+ p.addField(styp, pos, nil, typ, tag)
}
default:
func (p *parser) oliteral() *BasicLit {
if p.tok == _Literal {
b := new(BasicLit)
- b.init(p)
+ b.pos = p.pos()
b.Value = p.lit
b.Kind = p.kind
p.next()
}
f := new(Field)
- f.init(p)
+ f.pos = name.Pos()
if p.tok != _Lparen {
// packname
f.Type = p.qualifiedName(name)
return f
case _Lparen:
- p.next()
+ p.syntax_error("cannot parenthesize embedded type")
f := new(Field)
- f.init(p)
+ f.pos = p.pos()
+ p.next()
f.Type = p.qualifiedName(nil)
p.want(_Rparen)
- p.syntax_error("cannot parenthesize embedded type")
return f
default:
- p.syntax_error("")
+ p.syntax_error("expecting method or interface name")
p.advance(_Semi, _Rbrace)
return nil
}
}
f := new(Field)
- f.init(p)
+ f.pos = p.pos()
switch p.tok {
case _Name:
}
t := new(DotsType)
- t.init(p)
+ t.pos = p.pos()
p.want(_DotDotDot)
t.Elem = p.tryType()
defer p.trace("simpleStmt")()
}
- if rangeOk && p.got(_Range) {
+ if rangeOk && p.tok == _Range {
// _Range expr
if debug && lhs != nil {
panic("invalid call of simpleStmt")
if _, ok := lhs.(*ListExpr); !ok && p.tok != _Assign && p.tok != _Define {
// expr
+ pos := p.pos()
switch p.tok {
case _AssignOp:
// lhs op= rhs
op := p.op
p.next()
- return p.newAssignStmt(op, lhs, p.expr())
+ return p.newAssignStmt(pos, op, lhs, p.expr())
case _IncOp:
// lhs++ or lhs--
op := p.op
p.next()
- return p.newAssignStmt(op, lhs, ImplicitOne)
+ return p.newAssignStmt(pos, op, lhs, ImplicitOne)
case _Arrow:
// lhs <- rhs
- p.next()
s := new(SendStmt)
- s.init(p)
+ s.pos = pos
+ p.next()
s.Chan = lhs
s.Value = p.expr()
- if gcCompat {
- s.init(p)
- }
return s
default:
// expr
- return &ExprStmt{X: lhs}
+ s := new(ExprStmt)
+ if lhs != nil { // be cautious (test/syntax/semi4.go)
+ s.pos = lhs.Pos()
+ } else {
+ s.pos = p.pos()
+ }
+ s.X = lhs
+ return s
}
}
// expr_list
+ pos := p.pos()
switch p.tok {
case _Assign:
p.next()
- if rangeOk && p.got(_Range) {
+ if rangeOk && p.tok == _Range {
// expr_list '=' _Range expr
return p.rangeClause(lhs, false)
}
// expr_list '=' expr_list
- return p.newAssignStmt(0, lhs, p.exprList())
+ return p.newAssignStmt(pos, 0, lhs, p.exprList())
case _Define:
- pos := p.pos()
p.next()
- if rangeOk && p.got(_Range) {
+ if rangeOk && p.tok == _Range {
// expr_list ':=' range expr
return p.rangeClause(lhs, true)
}
// TODO(mdempsky): Have Expr types implement Stringer?
p.error(fmt.Sprintf("invalid variable name %s in type switch", lhs))
}
- return &ExprStmt{X: x}
+ s := new(ExprStmt)
+ s.pos = x.Pos()
+ s.X = x
+ return s
}
- as := p.newAssignStmt(Def, lhs, rhs)
+ as := p.newAssignStmt(pos, Def, lhs, rhs)
as.pos = pos // TODO(gri) pass this into newAssignStmt
return as
func (p *parser) rangeClause(lhs Expr, def bool) *RangeClause {
r := new(RangeClause)
- r.init(p)
+ r.pos = p.pos()
+ p.next() // consume _Range
r.Lhs = lhs
r.Def = def
r.X = p.expr()
- if gcCompat {
- r.init(p)
- }
return r
}
-func (p *parser) newAssignStmt(op Operator, lhs, rhs Expr) *AssignStmt {
+func (p *parser) newAssignStmt(pos src.Pos, op Operator, lhs, rhs Expr) *AssignStmt {
a := new(AssignStmt)
- a.init(p)
+ a.pos = pos
a.Op = op
a.Lhs = lhs
a.Rhs = rhs
}
s := new(LabeledStmt)
- s.init(p)
+ s.pos = p.pos()
s.Label = label
p.want(_Colon)
}
s := new(BlockStmt)
- s.init(p)
+ s.pos = p.pos()
p.want(_Lbrace)
s.Body = p.stmtList()
p.want(_Rbrace)
}
s := new(DeclStmt)
- s.init(p)
+ s.pos = p.pos()
p.next() // _Const, _Type, or _Var
s.DeclList = p.appendGroup(nil, f)
}
s := new(ForStmt)
- s.init(p)
+ s.pos = p.pos()
- p.want(_For)
s.Init, s.Cond, s.Post = p.header(_For)
- if gcCompat {
- s.init(p)
- }
s.Body = p.stmtBody("for clause")
return s
}
func (p *parser) header(keyword token) (init SimpleStmt, cond Expr, post SimpleStmt) {
- // TODO(gri) move caller's p.want(keyword) here, once we removed gcCompat
+ p.want(keyword)
if p.tok == _Lbrace {
if keyword == _If {
}
s := new(IfStmt)
- s.init(p)
+ s.pos = p.pos()
- p.want(_If)
s.Init, s.Cond, _ = p.header(_If)
-
- if gcCompat {
- s.init(p)
- }
-
s.Then = p.stmtBody("if clause")
if p.got(_Else) {
defer p.trace("switchStmt")()
}
- p.want(_Switch)
s := new(SwitchStmt)
- s.init(p)
+ s.pos = p.pos()
s.Init, s.Tag, _ = p.header(_Switch)
defer p.trace("selectStmt")()
}
- p.want(_Select)
s := new(SelectStmt)
- s.init(p)
+ s.pos = p.pos()
+ p.want(_Select)
if !p.got(_Lbrace) {
p.syntax_error("missing { after select clause")
p.advance(_Case, _Default, _Rbrace)
}
c := new(CaseClause)
- c.init(p)
+ c.pos = p.pos()
switch p.tok {
case _Case:
}
c := new(CommClause)
- c.init(p)
+ c.pos = p.pos()
switch p.tok {
case _Case:
p.advance(_Case, _Default, _Rbrace)
}
- if gcCompat {
- c.init(p)
- }
p.want(_Colon)
c.Body = p.stmtList()
return p.ifStmt()
case _Fallthrough:
- p.next()
s := new(BranchStmt)
- s.init(p)
+ s.pos = p.pos()
+ p.next()
s.Tok = _Fallthrough
return s
- // // will be converted to OFALL
- // stmt := nod(OXFALL, nil, nil)
- // stmt.Xoffset = int64(block)
- // return stmt
case _Break, _Continue:
- tok := p.tok
- p.next()
s := new(BranchStmt)
- s.init(p)
- s.Tok = tok
+ s.pos = p.pos()
+ s.Tok = p.tok
+ p.next()
if p.tok == _Name {
s.Label = p.name()
}
return p.callStmt()
case _Goto:
- p.next()
s := new(BranchStmt)
- s.init(p)
+ s.pos = p.pos()
s.Tok = _Goto
+ p.next()
s.Label = p.name()
return s
- // stmt := nod(OGOTO, p.new_name(p.name()), nil)
- // stmt.Sym = dclstack // context, for goto restrictions
- // return stmt
case _Return:
- p.next()
s := new(ReturnStmt)
- s.init(p)
+ s.pos = p.pos()
+ p.next()
if p.tok != _Semi && p.tok != _Rbrace {
s.Results = p.exprList()
}
- if gcCompat {
- s.init(p)
- }
return s
case _Semi:
s := new(EmptyStmt)
- s.init(p)
+ s.pos = p.pos()
return s
}
// call or conversion
// convtype '(' expr ocomma ')'
c := new(CallExpr)
- c.init(p)
+ c.pos = p.pos()
c.Fun = fun
p.want(_Lparen)
}
p.xnest--
- if gcCompat {
- c.init(p)
- }
p.want(_Rparen)
return c
// no tracing to avoid overly verbose output
n := new(Name)
- n.init(p)
+ n.pos = p.pos()
if p.tok == _Name {
n.Value = p.lit
name = p.name()
default:
name = new(Name)
- name.init(p)
+ name.pos = p.pos()
p.syntax_error("expecting name")
p.advance(_Dot, _Semi, _Rbrace)
}