mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
cmd/compile/internal/syntax: always construct a correct syntax tree
- parser creates sensible nodes in case of syntax errors instead of nil - a new BadExpr node is used in places where we can't do better - fixed error message for incorrect type switch guard - minor cleanups Fixes #19663. Change-Id: I028394c6db9cba7371f0e417ebf93f594659786a Reviewed-on: https://go-review.googlesource.com/38653 Run-TryBot: Robert Griesemer <gri@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Matthew Dempsky <mdempsky@google.com>
This commit is contained in:
parent
ecc6a81617
commit
d1f5e5f482
6 changed files with 132 additions and 85 deletions
|
|
@ -646,7 +646,6 @@ var knownFormats = map[string]string{
|
||||||
"cmd/compile/internal/ssa.regMask %d": "",
|
"cmd/compile/internal/ssa.regMask %d": "",
|
||||||
"cmd/compile/internal/ssa.register %d": "",
|
"cmd/compile/internal/ssa.register %d": "",
|
||||||
"cmd/compile/internal/syntax.Expr %#v": "",
|
"cmd/compile/internal/syntax.Expr %#v": "",
|
||||||
"cmd/compile/internal/syntax.Expr %s": "",
|
|
||||||
"cmd/compile/internal/syntax.Node %T": "",
|
"cmd/compile/internal/syntax.Node %T": "",
|
||||||
"cmd/compile/internal/syntax.Operator %d": "",
|
"cmd/compile/internal/syntax.Operator %d": "",
|
||||||
"cmd/compile/internal/syntax.Operator %s": "",
|
"cmd/compile/internal/syntax.Operator %s": "",
|
||||||
|
|
|
||||||
|
|
@ -416,7 +416,7 @@ func (p *noder) exprs(exprs []syntax.Expr) []*Node {
|
||||||
func (p *noder) expr(expr syntax.Expr) *Node {
|
func (p *noder) expr(expr syntax.Expr) *Node {
|
||||||
p.lineno(expr)
|
p.lineno(expr)
|
||||||
switch expr := expr.(type) {
|
switch expr := expr.(type) {
|
||||||
case nil:
|
case nil, *syntax.BadExpr:
|
||||||
return nil
|
return nil
|
||||||
case *syntax.Name:
|
case *syntax.Name:
|
||||||
return p.mkname(expr)
|
return p.mkname(expr)
|
||||||
|
|
|
||||||
|
|
@ -125,6 +125,12 @@ type (
|
||||||
aExpr()
|
aExpr()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Placeholder for an expression that failed to parse
|
||||||
|
// correctly and where we can't provide a better node.
|
||||||
|
BadExpr struct {
|
||||||
|
expr
|
||||||
|
}
|
||||||
|
|
||||||
// Value
|
// Value
|
||||||
Name struct {
|
Name struct {
|
||||||
Value string
|
Value string
|
||||||
|
|
|
||||||
|
|
@ -235,9 +235,12 @@ func (p *parser) trace(msg string) func() {
|
||||||
// Parse methods are annotated with matching Go productions as appropriate.
|
// Parse methods are annotated with matching Go productions as appropriate.
|
||||||
// The annotations are intended as guidelines only since a single Go grammar
|
// The annotations are intended as guidelines only since a single Go grammar
|
||||||
// rule may be covered by multiple parse methods and vice versa.
|
// rule may be covered by multiple parse methods and vice versa.
|
||||||
|
//
|
||||||
|
// Excluding methods returning slices, parse methods named xOrNil may return
|
||||||
|
// nil; all others are expected to return a valid non-nil node.
|
||||||
|
|
||||||
// SourceFile = PackageClause ";" { ImportDecl ";" } { TopLevelDecl ";" } .
|
// SourceFile = PackageClause ";" { ImportDecl ";" } { TopLevelDecl ";" } .
|
||||||
func (p *parser) file() *File {
|
func (p *parser) fileOrNil() *File {
|
||||||
if trace {
|
if trace {
|
||||||
defer p.trace("file")()
|
defer p.trace("file")()
|
||||||
}
|
}
|
||||||
|
|
@ -281,10 +284,12 @@ func (p *parser) file() *File {
|
||||||
|
|
||||||
case _Func:
|
case _Func:
|
||||||
p.next()
|
p.next()
|
||||||
f.DeclList = appendDecl(f.DeclList, p.funcDecl())
|
if d := p.funcDeclOrNil(); d != nil {
|
||||||
|
f.DeclList = append(f.DeclList, d)
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if p.tok == _Lbrace && len(f.DeclList) > 0 && emptyFuncDecl(f.DeclList[len(f.DeclList)-1]) {
|
if p.tok == _Lbrace && len(f.DeclList) > 0 && isEmptyFuncDecl(f.DeclList[len(f.DeclList)-1]) {
|
||||||
// opening { of function declaration on next line
|
// opening { of function declaration on next line
|
||||||
p.syntax_error("unexpected semicolon or newline before {")
|
p.syntax_error("unexpected semicolon or newline before {")
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -310,7 +315,7 @@ func (p *parser) file() *File {
|
||||||
return f
|
return f
|
||||||
}
|
}
|
||||||
|
|
||||||
func emptyFuncDecl(dcl Decl) bool {
|
func isEmptyFuncDecl(dcl Decl) bool {
|
||||||
f, ok := dcl.(*FuncDecl)
|
f, ok := dcl.(*FuncDecl)
|
||||||
return ok && f.Body == nil
|
return ok && f.Body == nil
|
||||||
}
|
}
|
||||||
|
|
@ -323,25 +328,29 @@ func (p *parser) appendGroup(list []Decl, f func(*Group) Decl) []Decl {
|
||||||
if p.got(_Lparen) {
|
if p.got(_Lparen) {
|
||||||
g := new(Group)
|
g := new(Group)
|
||||||
for p.tok != _EOF && p.tok != _Rparen {
|
for p.tok != _EOF && p.tok != _Rparen {
|
||||||
list = appendDecl(list, f(g))
|
list = append(list, f(g))
|
||||||
if !p.osemi(_Rparen) {
|
if !p.osemi(_Rparen) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p.want(_Rparen)
|
p.want(_Rparen)
|
||||||
return list
|
} else {
|
||||||
|
list = append(list, f(nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
return appendDecl(list, f(nil))
|
if debug {
|
||||||
}
|
for _, d := range list {
|
||||||
|
if d == nil {
|
||||||
func appendDecl(list []Decl, d Decl) []Decl {
|
panic("nil list entry")
|
||||||
if d != nil {
|
|
||||||
return append(list, d)
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return list
|
return list
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ImportSpec = [ "." | PackageName ] ImportPath .
|
||||||
|
// ImportPath = string_lit .
|
||||||
func (p *parser) importDecl(group *Group) Decl {
|
func (p *parser) importDecl(group *Group) Decl {
|
||||||
if trace {
|
if trace {
|
||||||
defer p.trace("importDecl")()
|
defer p.trace("importDecl")()
|
||||||
|
|
@ -354,10 +363,7 @@ func (p *parser) importDecl(group *Group) Decl {
|
||||||
case _Name:
|
case _Name:
|
||||||
d.LocalPkgName = p.name()
|
d.LocalPkgName = p.name()
|
||||||
case _Dot:
|
case _Dot:
|
||||||
n := new(Name)
|
d.LocalPkgName = p.newName(".")
|
||||||
n.pos = p.pos()
|
|
||||||
n.Value = "."
|
|
||||||
d.LocalPkgName = n
|
|
||||||
p.next()
|
p.next()
|
||||||
}
|
}
|
||||||
d.Path = p.oliteral()
|
d.Path = p.oliteral()
|
||||||
|
|
@ -382,7 +388,7 @@ func (p *parser) constDecl(group *Group) Decl {
|
||||||
|
|
||||||
d.NameList = p.nameList(p.name())
|
d.NameList = p.nameList(p.name())
|
||||||
if p.tok != _EOF && p.tok != _Semi && p.tok != _Rparen {
|
if p.tok != _EOF && p.tok != _Semi && p.tok != _Rparen {
|
||||||
d.Type = p.tryType()
|
d.Type = p.typeOrNil()
|
||||||
if p.got(_Assign) {
|
if p.got(_Assign) {
|
||||||
d.Values = p.exprList()
|
d.Values = p.exprList()
|
||||||
}
|
}
|
||||||
|
|
@ -403,11 +409,11 @@ func (p *parser) typeDecl(group *Group) Decl {
|
||||||
|
|
||||||
d.Name = p.name()
|
d.Name = p.name()
|
||||||
d.Alias = p.got(_Assign)
|
d.Alias = p.got(_Assign)
|
||||||
d.Type = p.tryType()
|
d.Type = p.typeOrNil()
|
||||||
if d.Type == nil {
|
if d.Type == nil {
|
||||||
|
d.Type = p.bad()
|
||||||
p.syntax_error("in type declaration")
|
p.syntax_error("in type declaration")
|
||||||
p.advance(_Semi, _Rparen)
|
p.advance(_Semi, _Rparen)
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
d.Group = group
|
d.Group = group
|
||||||
d.Pragma = p.pragma
|
d.Pragma = p.pragma
|
||||||
|
|
@ -443,7 +449,7 @@ func (p *parser) varDecl(group *Group) Decl {
|
||||||
// Function = Signature FunctionBody .
|
// Function = Signature FunctionBody .
|
||||||
// MethodDecl = "func" Receiver MethodName ( Function | Signature ) .
|
// MethodDecl = "func" Receiver MethodName ( Function | Signature ) .
|
||||||
// Receiver = Parameters .
|
// Receiver = Parameters .
|
||||||
func (p *parser) funcDecl() *FuncDecl {
|
func (p *parser) funcDeclOrNil() *FuncDecl {
|
||||||
if trace {
|
if trace {
|
||||||
defer p.trace("funcDecl")()
|
defer p.trace("funcDecl")()
|
||||||
}
|
}
|
||||||
|
|
@ -633,18 +639,23 @@ func (p *parser) callStmt() *CallStmt {
|
||||||
p.next()
|
p.next()
|
||||||
|
|
||||||
x := p.pexpr(p.tok == _Lparen) // keep_parens so we can report error below
|
x := p.pexpr(p.tok == _Lparen) // keep_parens so we can report error below
|
||||||
switch x := x.(type) {
|
if t := unparen(x); t != x {
|
||||||
case *CallExpr:
|
|
||||||
s.Call = x
|
|
||||||
case *ParenExpr:
|
|
||||||
p.error(fmt.Sprintf("expression in %s must not be parenthesized", s.Tok))
|
p.error(fmt.Sprintf("expression in %s must not be parenthesized", s.Tok))
|
||||||
// already progressed, no need to advance
|
// already progressed, no need to advance
|
||||||
default:
|
x = t
|
||||||
p.error(fmt.Sprintf("expression in %s must be function call", s.Tok))
|
|
||||||
// already progressed, no need to advance
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return s // TODO(gri) should we return nil in case of failure?
|
cx, ok := x.(*CallExpr)
|
||||||
|
if !ok {
|
||||||
|
p.error(fmt.Sprintf("expression in %s must be function call", s.Tok))
|
||||||
|
// already progressed, no need to advance
|
||||||
|
cx := new(CallExpr)
|
||||||
|
cx.pos = x.Pos()
|
||||||
|
cx.Fun = p.bad()
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Call = cx
|
||||||
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
// Operand = Literal | OperandName | MethodExpr | "(" Expression ")" .
|
// Operand = Literal | OperandName | MethodExpr | "(" Expression ")" .
|
||||||
|
|
@ -721,9 +732,10 @@ func (p *parser) operand(keep_parens bool) Expr {
|
||||||
return p.type_() // othertype
|
return p.type_() // othertype
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
x := p.bad()
|
||||||
p.syntax_error("expecting expression")
|
p.syntax_error("expecting expression")
|
||||||
p.advance()
|
p.advance()
|
||||||
return nil
|
return x
|
||||||
}
|
}
|
||||||
|
|
||||||
// Syntactically, composite literals are operands. Because a complit
|
// Syntactically, composite literals are operands. Because a complit
|
||||||
|
|
@ -936,16 +948,17 @@ func (p *parser) type_() Expr {
|
||||||
defer p.trace("type_")()
|
defer p.trace("type_")()
|
||||||
}
|
}
|
||||||
|
|
||||||
if typ := p.tryType(); typ != nil {
|
typ := p.typeOrNil()
|
||||||
return typ
|
if typ == nil {
|
||||||
}
|
typ = p.bad()
|
||||||
|
|
||||||
p.syntax_error("expecting type")
|
p.syntax_error("expecting type")
|
||||||
p.advance()
|
p.advance()
|
||||||
return nil
|
}
|
||||||
|
|
||||||
|
return typ
|
||||||
}
|
}
|
||||||
|
|
||||||
func indirect(pos src.Pos, typ Expr) Expr {
|
func newIndirect(pos src.Pos, typ Expr) Expr {
|
||||||
o := new(Operation)
|
o := new(Operation)
|
||||||
o.pos = pos
|
o.pos = pos
|
||||||
o.Op = Mul
|
o.Op = Mul
|
||||||
|
|
@ -953,16 +966,16 @@ func indirect(pos src.Pos, typ Expr) Expr {
|
||||||
return o
|
return o
|
||||||
}
|
}
|
||||||
|
|
||||||
// tryType is like type_ but it returns nil if there was no type
|
// typeOrNil is like type_ but it returns nil if there was no type
|
||||||
// instead of reporting an error.
|
// instead of reporting an error.
|
||||||
//
|
//
|
||||||
// Type = TypeName | TypeLit | "(" Type ")" .
|
// Type = TypeName | TypeLit | "(" Type ")" .
|
||||||
// TypeName = identifier | QualifiedIdent .
|
// TypeName = identifier | QualifiedIdent .
|
||||||
// TypeLit = ArrayType | StructType | PointerType | FunctionType | InterfaceType |
|
// TypeLit = ArrayType | StructType | PointerType | FunctionType | InterfaceType |
|
||||||
// SliceType | MapType | Channel_Type .
|
// SliceType | MapType | Channel_Type .
|
||||||
func (p *parser) tryType() Expr {
|
func (p *parser) typeOrNil() Expr {
|
||||||
if trace {
|
if trace {
|
||||||
defer p.trace("tryType")()
|
defer p.trace("typeOrNil")()
|
||||||
}
|
}
|
||||||
|
|
||||||
pos := p.pos()
|
pos := p.pos()
|
||||||
|
|
@ -970,7 +983,7 @@ func (p *parser) tryType() Expr {
|
||||||
case _Star:
|
case _Star:
|
||||||
// ptrtype
|
// ptrtype
|
||||||
p.next()
|
p.next()
|
||||||
return indirect(pos, p.type_())
|
return newIndirect(pos, p.type_())
|
||||||
|
|
||||||
case _Arrow:
|
case _Arrow:
|
||||||
// recvchantype
|
// recvchantype
|
||||||
|
|
@ -1072,13 +1085,14 @@ func (p *parser) chanElem() Expr {
|
||||||
defer p.trace("chanElem")()
|
defer p.trace("chanElem")()
|
||||||
}
|
}
|
||||||
|
|
||||||
if typ := p.tryType(); typ != nil {
|
typ := p.typeOrNil()
|
||||||
return typ
|
if typ == nil {
|
||||||
}
|
typ = p.bad()
|
||||||
|
|
||||||
p.syntax_error("missing channel element type")
|
p.syntax_error("missing channel element type")
|
||||||
// assume element type is simply absent - don't advance
|
// assume element type is simply absent - don't advance
|
||||||
return nil
|
}
|
||||||
|
|
||||||
|
return typ
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *parser) dotname(name *Name) Expr {
|
func (p *parser) dotname(name *Name) Expr {
|
||||||
|
|
@ -1170,10 +1184,10 @@ func (p *parser) funcResult() []*Field {
|
||||||
}
|
}
|
||||||
|
|
||||||
pos := p.pos()
|
pos := p.pos()
|
||||||
if result := p.tryType(); result != nil {
|
if typ := p.typeOrNil(); typ != nil {
|
||||||
f := new(Field)
|
f := new(Field)
|
||||||
f.pos = pos
|
f.pos = pos
|
||||||
f.Type = result
|
f.Type = typ
|
||||||
return []*Field{f}
|
return []*Field{f}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1234,7 +1248,7 @@ func (p *parser) fieldDecl(styp *StructType) {
|
||||||
// '(' '*' embed ')' oliteral
|
// '(' '*' embed ')' oliteral
|
||||||
pos := p.pos()
|
pos := p.pos()
|
||||||
p.next()
|
p.next()
|
||||||
typ := indirect(pos, p.qualifiedName(nil))
|
typ := newIndirect(pos, p.qualifiedName(nil))
|
||||||
p.want(_Rparen)
|
p.want(_Rparen)
|
||||||
tag := p.oliteral()
|
tag := p.oliteral()
|
||||||
p.addField(styp, pos, nil, typ, tag)
|
p.addField(styp, pos, nil, typ, tag)
|
||||||
|
|
@ -1253,7 +1267,7 @@ func (p *parser) fieldDecl(styp *StructType) {
|
||||||
p.next()
|
p.next()
|
||||||
if p.got(_Lparen) {
|
if p.got(_Lparen) {
|
||||||
// '*' '(' embed ')' oliteral
|
// '*' '(' embed ')' oliteral
|
||||||
typ := indirect(pos, p.qualifiedName(nil))
|
typ := newIndirect(pos, p.qualifiedName(nil))
|
||||||
p.want(_Rparen)
|
p.want(_Rparen)
|
||||||
tag := p.oliteral()
|
tag := p.oliteral()
|
||||||
p.addField(styp, pos, nil, typ, tag)
|
p.addField(styp, pos, nil, typ, tag)
|
||||||
|
|
@ -1261,7 +1275,7 @@ func (p *parser) fieldDecl(styp *StructType) {
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// '*' embed oliteral
|
// '*' embed oliteral
|
||||||
typ := indirect(pos, p.qualifiedName(nil))
|
typ := newIndirect(pos, p.qualifiedName(nil))
|
||||||
tag := p.oliteral()
|
tag := p.oliteral()
|
||||||
p.addField(styp, pos, nil, typ, tag)
|
p.addField(styp, pos, nil, typ, tag)
|
||||||
}
|
}
|
||||||
|
|
@ -1336,7 +1350,7 @@ func (p *parser) methodDecl() *Field {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParameterDecl = [ IdentifierList ] [ "..." ] Type .
|
// ParameterDecl = [ IdentifierList ] [ "..." ] Type .
|
||||||
func (p *parser) paramDecl() *Field {
|
func (p *parser) paramDeclOrNil() *Field {
|
||||||
if trace {
|
if trace {
|
||||||
defer p.trace("paramDecl")()
|
defer p.trace("paramDecl")()
|
||||||
}
|
}
|
||||||
|
|
@ -1390,8 +1404,9 @@ func (p *parser) dotsType() *DotsType {
|
||||||
t.pos = p.pos()
|
t.pos = p.pos()
|
||||||
|
|
||||||
p.want(_DotDotDot)
|
p.want(_DotDotDot)
|
||||||
t.Elem = p.tryType()
|
t.Elem = p.typeOrNil()
|
||||||
if t.Elem == nil {
|
if t.Elem == nil {
|
||||||
|
t.Elem = p.bad()
|
||||||
p.syntax_error("final argument in variadic function missing type")
|
p.syntax_error("final argument in variadic function missing type")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1405,11 +1420,12 @@ func (p *parser) paramList() (list []*Field) {
|
||||||
defer p.trace("paramList")()
|
defer p.trace("paramList")()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pos := p.pos()
|
||||||
p.want(_Lparen)
|
p.want(_Lparen)
|
||||||
|
|
||||||
var named int // number of parameters that have an explicit name and type
|
var named int // number of parameters that have an explicit name and type
|
||||||
for p.tok != _EOF && p.tok != _Rparen {
|
for p.tok != _EOF && p.tok != _Rparen {
|
||||||
if par := p.paramDecl(); par != nil {
|
if par := p.paramDeclOrNil(); par != nil {
|
||||||
if debug && par.Name == nil && par.Type == nil {
|
if debug && par.Name == nil && par.Type == nil {
|
||||||
panic("parameter without name or type")
|
panic("parameter without name or type")
|
||||||
}
|
}
|
||||||
|
|
@ -1434,20 +1450,29 @@ func (p *parser) paramList() (list []*Field) {
|
||||||
}
|
}
|
||||||
} else if named != len(list) {
|
} else if named != len(list) {
|
||||||
// some named => all must be named
|
// some named => all must be named
|
||||||
|
ok := true
|
||||||
var typ Expr
|
var typ Expr
|
||||||
for i := len(list) - 1; i >= 0; i-- {
|
for i := len(list) - 1; i >= 0; i-- {
|
||||||
if par := list[i]; par.Type != nil {
|
if par := list[i]; par.Type != nil {
|
||||||
typ = par.Type
|
typ = par.Type
|
||||||
if par.Name == nil {
|
if par.Name == nil {
|
||||||
typ = nil // error
|
ok = false
|
||||||
|
n := p.newName("_")
|
||||||
|
n.pos = typ.Pos() // correct position
|
||||||
|
par.Name = n
|
||||||
}
|
}
|
||||||
} else {
|
} else if typ != nil {
|
||||||
par.Type = typ
|
par.Type = typ
|
||||||
|
} else {
|
||||||
|
// par.Type == nil && typ == nil => we only have a par.Name
|
||||||
|
ok = false
|
||||||
|
t := p.bad()
|
||||||
|
t.pos = par.Name.Pos() // correct position
|
||||||
|
par.Type = t
|
||||||
}
|
}
|
||||||
if typ == nil {
|
|
||||||
p.syntax_error("mixed named and unnamed function parameters")
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
if !ok {
|
||||||
|
p.syntax_error_at(pos, "mixed named and unnamed function parameters")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1455,6 +1480,12 @@ func (p *parser) paramList() (list []*Field) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *parser) bad() *BadExpr {
|
||||||
|
b := new(BadExpr)
|
||||||
|
b.pos = p.pos()
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// Statements
|
// Statements
|
||||||
|
|
||||||
|
|
@ -1475,7 +1506,7 @@ func (p *parser) simpleStmt(lhs Expr, rangeOk bool) SimpleStmt {
|
||||||
if debug && lhs != nil {
|
if debug && lhs != nil {
|
||||||
panic("invalid call of simpleStmt")
|
panic("invalid call of simpleStmt")
|
||||||
}
|
}
|
||||||
return p.rangeClause(nil, false)
|
return p.newRangeClause(nil, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
if lhs == nil {
|
if lhs == nil {
|
||||||
|
|
@ -1510,11 +1541,7 @@ func (p *parser) simpleStmt(lhs Expr, rangeOk bool) SimpleStmt {
|
||||||
default:
|
default:
|
||||||
// expr
|
// expr
|
||||||
s := new(ExprStmt)
|
s := new(ExprStmt)
|
||||||
if lhs != nil { // be cautious (test/syntax/semi4.go)
|
|
||||||
s.pos = lhs.Pos()
|
s.pos = lhs.Pos()
|
||||||
} else {
|
|
||||||
s.pos = p.pos()
|
|
||||||
}
|
|
||||||
s.X = lhs
|
s.X = lhs
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
@ -1528,7 +1555,7 @@ func (p *parser) simpleStmt(lhs Expr, rangeOk bool) SimpleStmt {
|
||||||
|
|
||||||
if rangeOk && p.tok == _Range {
|
if rangeOk && p.tok == _Range {
|
||||||
// expr_list '=' _Range expr
|
// expr_list '=' _Range expr
|
||||||
return p.rangeClause(lhs, false)
|
return p.newRangeClause(lhs, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// expr_list '=' expr_list
|
// expr_list '=' expr_list
|
||||||
|
|
@ -1539,7 +1566,7 @@ func (p *parser) simpleStmt(lhs Expr, rangeOk bool) SimpleStmt {
|
||||||
|
|
||||||
if rangeOk && p.tok == _Range {
|
if rangeOk && p.tok == _Range {
|
||||||
// expr_list ':=' range expr
|
// expr_list ':=' range expr
|
||||||
return p.rangeClause(lhs, true)
|
return p.newRangeClause(lhs, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// expr_list ':=' expr_list
|
// expr_list ':=' expr_list
|
||||||
|
|
@ -1550,10 +1577,13 @@ func (p *parser) simpleStmt(lhs Expr, rangeOk bool) SimpleStmt {
|
||||||
case *Name:
|
case *Name:
|
||||||
x.Lhs = lhs
|
x.Lhs = lhs
|
||||||
case *ListExpr:
|
case *ListExpr:
|
||||||
p.error(fmt.Sprintf("argument count mismatch: %d = %d", len(lhs.ElemList), 1))
|
p.error_at(lhs.Pos(), fmt.Sprintf("cannot assign 1 value to %d variables", len(lhs.ElemList)))
|
||||||
|
// make the best of what we have
|
||||||
|
if lhs, ok := lhs.ElemList[0].(*Name); ok {
|
||||||
|
x.Lhs = lhs
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
// TODO(mdempsky): Have Expr types implement Stringer?
|
p.error_at(lhs.Pos(), fmt.Sprintf("invalid variable name %s in type switch", String(lhs)))
|
||||||
p.error(fmt.Sprintf("invalid variable name %s in type switch", lhs))
|
|
||||||
}
|
}
|
||||||
s := new(ExprStmt)
|
s := new(ExprStmt)
|
||||||
s.pos = x.Pos()
|
s.pos = x.Pos()
|
||||||
|
|
@ -1562,17 +1592,23 @@ func (p *parser) simpleStmt(lhs Expr, rangeOk bool) SimpleStmt {
|
||||||
}
|
}
|
||||||
|
|
||||||
as := p.newAssignStmt(pos, Def, lhs, rhs)
|
as := p.newAssignStmt(pos, Def, lhs, rhs)
|
||||||
as.pos = pos // TODO(gri) pass this into newAssignStmt
|
|
||||||
return as
|
return as
|
||||||
|
|
||||||
default:
|
default:
|
||||||
p.syntax_error("expecting := or = or comma")
|
p.syntax_error("expecting := or = or comma")
|
||||||
p.advance(_Semi, _Rbrace)
|
p.advance(_Semi, _Rbrace)
|
||||||
return nil
|
// make the best of what we have
|
||||||
|
if x, ok := lhs.(*ListExpr); ok {
|
||||||
|
lhs = x.ElemList[0]
|
||||||
|
}
|
||||||
|
s := new(ExprStmt)
|
||||||
|
s.pos = lhs.Pos()
|
||||||
|
s.X = lhs
|
||||||
|
return s
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *parser) rangeClause(lhs Expr, def bool) *RangeClause {
|
func (p *parser) newRangeClause(lhs Expr, def bool) *RangeClause {
|
||||||
r := new(RangeClause)
|
r := new(RangeClause)
|
||||||
r.pos = p.pos()
|
r.pos = p.pos()
|
||||||
p.next() // consume _Range
|
p.next() // consume _Range
|
||||||
|
|
@ -2050,21 +2086,25 @@ func (p *parser) call(fun Expr) *CallExpr {
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// Common productions
|
// Common productions
|
||||||
|
|
||||||
|
func (p *parser) newName(value string) *Name {
|
||||||
|
n := new(Name)
|
||||||
|
n.pos = p.pos()
|
||||||
|
n.Value = value
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
func (p *parser) name() *Name {
|
func (p *parser) name() *Name {
|
||||||
// no tracing to avoid overly verbose output
|
// no tracing to avoid overly verbose output
|
||||||
|
|
||||||
n := new(Name)
|
|
||||||
n.pos = p.pos()
|
|
||||||
|
|
||||||
if p.tok == _Name {
|
if p.tok == _Name {
|
||||||
n.Value = p.lit
|
n := p.newName(p.lit)
|
||||||
p.next()
|
p.next()
|
||||||
} else {
|
return n
|
||||||
n.Value = "_"
|
|
||||||
p.syntax_error("expecting name")
|
|
||||||
p.advance()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
n := p.newName("_")
|
||||||
|
p.syntax_error("expecting name")
|
||||||
|
p.advance()
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2099,8 +2139,7 @@ func (p *parser) qualifiedName(name *Name) Expr {
|
||||||
case p.tok == _Name:
|
case p.tok == _Name:
|
||||||
name = p.name()
|
name = p.name()
|
||||||
default:
|
default:
|
||||||
name = new(Name)
|
name = p.newName("_")
|
||||||
name.pos = p.pos()
|
|
||||||
p.syntax_error("expecting name")
|
p.syntax_error("expecting name")
|
||||||
p.advance(_Dot, _Semi, _Rbrace)
|
p.advance(_Dot, _Semi, _Rbrace)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -345,6 +345,9 @@ func (p *printer) printRawNode(n Node) {
|
||||||
// we should not reach here but don't crash
|
// we should not reach here but don't crash
|
||||||
|
|
||||||
// expressions and types
|
// expressions and types
|
||||||
|
case *BadExpr:
|
||||||
|
p.print(_Name, "<bad expr>")
|
||||||
|
|
||||||
case *Name:
|
case *Name:
|
||||||
p.print(_Name, n.Value) // _Name requires actual value following immediately
|
p.print(_Name, n.Value) // _Name requires actual value following immediately
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ func Parse(base *src.PosBase, src io.Reader, errh ErrorHandler, pragh PragmaHand
|
||||||
var p parser
|
var p parser
|
||||||
p.init(base, src, errh, pragh)
|
p.init(base, src, errh, pragh)
|
||||||
p.next()
|
p.next()
|
||||||
return p.file(), p.first
|
return p.fileOrNil(), p.first
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseBytes behaves like Parse but it reads the source from the []byte slice provided.
|
// ParseBytes behaves like Parse but it reads the source from the []byte slice provided.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue