cmd/compile/internal/syntax: factor out list parsing

Instead of repeating the same list parsing pattern for parenthesized
of braced comma or semicolon-separated lists, introduce a single list
parsing function that can be parametrized and which takes a closure
to parse list elements.

This ensures the same error handling and recovery logic is used across
all lists and simplifies the code.

No semantic change.

Change-Id: Ia738d354d6c2e0c3d84a5f1c7269a6eb95685edc
Reviewed-on: https://go-review.googlesource.com/70492
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
This commit is contained in:
griesemer 2017-10-11 16:57:39 -07:00 committed by Robert Griesemer
parent f8f0d6c4de
commit 645c661a54

View file

@ -175,7 +175,7 @@ func tokstring(tok token) string {
case _Comma: case _Comma:
return "comma" return "comma"
case _Semi: case _Semi:
return "semicolon" return "semicolon or newline"
} }
return tok.String() return tok.String()
} }
@ -351,17 +351,51 @@ func isEmptyFuncDecl(dcl Decl) bool {
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Declarations // Declarations
// appendGroup(f) = f | "(" { f ";" } ")" . // list parses a possibly empty, sep-separated list, optionally
func (p *parser) appendGroup(list []Decl, f func(*Group) Decl) []Decl { // followed by sep and enclosed by ( and ) or { and }. open is
if p.got(_Lparen) { // one of _Lparen, or _Lbrace, sep is one of _Comma or _Semi,
g := new(Group) // and close is expected to be the (closing) opposite of open.
for p.tok != _EOF && p.tok != _Rparen { // For each list element, f is called. After f returns true, no
list = append(list, f(g)) // more list elements are accepted. list returns the position
if !p.osemi(_Rparen) { // of the closing token.
break //
} // list = "(" { f sep } ")" |
// "{" { f sep } "}" . // sep is optional before ")" or "}"
//
func (p *parser) list(open, sep, close token, f func() bool) src.Pos {
p.want(open)
var done bool
for p.tok != _EOF && p.tok != close && !done {
done = f()
switch p.tok {
case sep:
p.next()
case _Rparen, _Rbrace:
// comma is optional before ) or } - nothing to do
// TODO(gri): consider restricting this case
// to the expected close token only
default:
p.syntax_error(fmt.Sprintf("expecting %s or %s", tokstring(sep), tokstring(close)))
p.advance(close)
done = true
} }
p.want(_Rparen) }
pos := p.pos()
p.want(close)
return pos
}
// appendGroup(f) = f | "(" { f ";" } ")" . // ";" is optional before ")"
func (p *parser) appendGroup(list []Decl, f func(*Group) Decl) []Decl {
if p.tok == _Lparen {
g := new(Group)
p.list(_Lparen, _Semi, _Rparen, func() bool {
list = append(list, f(g))
return false
})
} else { } else {
list = append(list, f(nil)) list = append(list, f(nil))
} }
@ -939,10 +973,8 @@ func (p *parser) complitexpr() *CompositeLit {
x := new(CompositeLit) x := new(CompositeLit)
x.pos = p.pos() x.pos = p.pos()
p.want(_Lbrace)
p.xnest++ p.xnest++
x.Rbrace = p.list(_Lbrace, _Comma, _Rbrace, func() bool {
for p.tok != _EOF && p.tok != _Rbrace {
// value // value
e := p.bare_complitexpr() e := p.bare_complitexpr()
if p.tok == _Colon { if p.tok == _Colon {
@ -956,14 +988,9 @@ func (p *parser) complitexpr() *CompositeLit {
x.NKeys++ x.NKeys++
} }
x.ElemList = append(x.ElemList, e) x.ElemList = append(x.ElemList, e)
if !p.ocomma(_Rbrace) { return false
break })
}
}
x.Rbrace = p.pos()
p.xnest-- p.xnest--
p.want(_Rbrace)
return x return x
} }
@ -1149,14 +1176,10 @@ func (p *parser) structType() *StructType {
typ.pos = p.pos() typ.pos = p.pos()
p.want(_Struct) p.want(_Struct)
p.want(_Lbrace) p.list(_Lbrace, _Semi, _Rbrace, func() bool {
for p.tok != _EOF && p.tok != _Rbrace {
p.fieldDecl(typ) p.fieldDecl(typ)
if !p.osemi(_Rbrace) { return false
break })
}
}
p.want(_Rbrace)
return typ return typ
} }
@ -1171,16 +1194,12 @@ func (p *parser) interfaceType() *InterfaceType {
typ.pos = p.pos() typ.pos = p.pos()
p.want(_Interface) p.want(_Interface)
p.want(_Lbrace) p.list(_Lbrace, _Semi, _Rbrace, func() bool {
for p.tok != _EOF && p.tok != _Rbrace {
if m := p.methodDecl(); m != nil { if m := p.methodDecl(); m != nil {
typ.MethodList = append(typ.MethodList, m) typ.MethodList = append(typ.MethodList, m)
} }
if !p.osemi(_Rbrace) { return false
break })
}
}
p.want(_Rbrace)
return typ return typ
} }
@ -1433,10 +1452,9 @@ func (p *parser) paramList() (list []*Field) {
} }
pos := p.pos() pos := p.pos()
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 { p.list(_Lparen, _Comma, _Rparen, func() bool {
if par := p.paramDeclOrNil(); 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")
@ -1446,10 +1464,8 @@ func (p *parser) paramList() (list []*Field) {
} }
list = append(list, par) list = append(list, par)
} }
if !p.ocomma(_Rparen) { return false
break })
}
}
// distribute parameter types // distribute parameter types
if named == 0 { if named == 0 {
@ -1488,7 +1504,6 @@ func (p *parser) paramList() (list []*Field) {
} }
} }
p.want(_Rparen)
return return
} }
@ -2071,19 +2086,13 @@ func (p *parser) argList() (list []Expr, hasDots bool) {
defer p.trace("argList")() defer p.trace("argList")()
} }
p.want(_Lparen)
p.xnest++ p.xnest++
p.list(_Lparen, _Comma, _Rparen, func() bool {
for p.tok != _EOF && p.tok != _Rparen {
list = append(list, p.expr()) list = append(list, p.expr())
hasDots = p.got(_DotDotDot) hasDots = p.got(_DotDotDot)
if !p.ocomma(_Rparen) || hasDots { return hasDots
break })
}
}
p.xnest-- p.xnest--
p.want(_Rparen)
return return
} }
@ -2172,40 +2181,6 @@ func (p *parser) exprList() Expr {
return x return x
} }
// osemi parses an optional semicolon.
func (p *parser) osemi(follow token) bool {
switch p.tok {
case _Semi:
p.next()
return true
case _Rparen, _Rbrace:
// semicolon is optional before ) or }
return true
}
p.syntax_error("expecting semicolon, newline, or " + tokstring(follow))
p.advance(follow)
return false
}
// ocomma parses an optional comma.
func (p *parser) ocomma(follow token) bool {
switch p.tok {
case _Comma:
p.next()
return true
case _Rparen, _Rbrace:
// comma is optional before ) or }
return true
}
p.syntax_error("expecting comma or " + tokstring(follow))
p.advance(follow)
return false
}
// unparen removes all parentheses around an expression. // unparen removes all parentheses around an expression.
func unparen(x Expr) Expr { func unparen(x Expr) Expr {
for { for {