doc := p.leadComment
- // a list of identifiers looks like a list of type names
- var list vector.Vector
- for {
- // TODO(gri): do not allow ()'s here
- list.Push(p.parseType())
- if p.tok != token.COMMA {
- break
- }
- p.next()
- }
-
- // if we had a list of identifiers, it must be followed by a type
- typ := p.tryType()
+ // fields
+ list, typ := p.parseVarList(false)
// optional tag
var tag *ast.BasicLit
var idents []*ast.Ident
if typ != nil {
// IdentifierList Type
- idents = p.makeIdentList(&list)
+ idents = p.makeIdentList(list)
} else {
- // Type (anonymous field)
- if len(list) == 1 {
- // TODO(gri): check that this looks like a type
- typ = list.At(0).(ast.Expr)
- } else {
- p.errorExpected(p.pos, "anonymous field")
- typ = &ast.BadExpr{p.pos}
+ // ["*"] TypeName (AnonymousField)
+ typ = (*list)[0].(ast.Expr) // we always have at least one element
+ if len(*list) > 1 || !isTypeName(deref(typ)) {
+ pos := typ.Pos()
+ p.errorExpected(pos, "anonymous field")
+ typ = &ast.BadExpr{pos}
}
}
pos := p.expect(token.STRUCT)
lbrace := p.expect(token.LBRACE)
var list vector.Vector
- for p.tok == token.IDENT || p.tok == token.MUL {
+ for p.tok == token.IDENT || p.tok == token.MUL || p.tok == token.LPAREN {
+ // a field declaration cannot start with a '(' but we accept
+ // it here for more robust parsing and better error messages
+ // (parseFieldDecl will check and complain if necessary)
list.Push(p.parseFieldDecl())
}
rbrace := p.expect(token.RBRACE)
}
-func (p *parser) tryParameterType(ellipsisOk bool) ast.Expr {
- if ellipsisOk && p.tok == token.ELLIPSIS {
+func (p *parser) tryVarType(isParam bool) ast.Expr {
+ if isParam && p.tok == token.ELLIPSIS {
pos := p.pos
p.next()
typ := p.tryType() // don't use parseType so we can provide better error message
}
-func (p *parser) parseParameterType(ellipsisOk bool) ast.Expr {
- typ := p.tryParameterType(ellipsisOk)
+func (p *parser) parseVarType(isParam bool) ast.Expr {
+ typ := p.tryVarType(isParam)
if typ == nil {
p.errorExpected(p.pos, "type")
p.next() // make progress
}
-func (p *parser) parseParameterDecl(ellipsisOk bool) (*vector.Vector, ast.Expr) {
+func (p *parser) parseVarList(isParam bool) (*vector.Vector, ast.Expr) {
if p.trace {
- defer un(trace(p, "ParameterDecl"))
+ defer un(trace(p, "VarList"))
}
// a list of identifiers looks like a list of type names
var list vector.Vector
for {
- // TODO(gri): do not allow ()'s here
- list.Push(p.parseParameterType(ellipsisOk))
+ // parseVarType accepts any type (including parenthesized ones)
+ // even though the syntax does not permit them here: we
+ // accept them all for more robust parsing and complain
+ // afterwards
+ list.Push(p.parseVarType(isParam))
if p.tok != token.COMMA {
break
}
}
// if we had a list of identifiers, it must be followed by a type
- typ := p.tryParameterType(ellipsisOk)
+ typ := p.tryVarType(isParam)
return &list, typ
}
defer un(trace(p, "ParameterList"))
}
- list, typ := p.parseParameterDecl(ellipsisOk)
+ list, typ := p.parseVarList(ellipsisOk)
if typ != nil {
// IdentifierList Type
idents := p.makeIdentList(list)
for p.tok != token.RPAREN && p.tok != token.EOF {
idents := p.parseIdentList(ast.Var)
- typ := p.parseParameterType(ellipsisOk)
+ typ := p.parseVarType(ellipsisOk)
list.Push(&ast.Field{nil, idents, typ, nil, nil})
if p.tok != token.COMMA {
break
}
-// TODO(gri): Consider different approach to checking syntax after parsing:
-// Provide a arguments (set of flags) to parsing functions
-// restricting what they are supposed to accept depending
-// on context.
-
// checkExpr checks that x is an expression (and not a type).
func (p *parser) checkExpr(x ast.Expr) ast.Expr {
- // TODO(gri): should provide predicate in AST nodes
- switch t := x.(type) {
+ switch t := unparen(x).(type) {
case *ast.BadExpr:
case *ast.Ident:
case *ast.BasicLit:
case *ast.FuncLit:
case *ast.CompositeLit:
case *ast.ParenExpr:
+ panic("unreachable")
case *ast.SelectorExpr:
case *ast.IndexExpr:
case *ast.SliceExpr:
}
-// isTypeName returns true iff x is type name.
+// isTypeName returns true iff x is a (qualified) TypeName.
func isTypeName(x ast.Expr) bool {
- // TODO(gri): should provide predicate in AST nodes
switch t := x.(type) {
case *ast.BadExpr:
case *ast.Ident:
- case *ast.ParenExpr:
- return isTypeName(t.X) // TODO(gri): should (TypeName) be illegal?
case *ast.SelectorExpr:
- return isTypeName(t.X)
+ _, isIdent := t.X.(*ast.Ident)
+ return isIdent
default:
return false // all other nodes are not type names
}
}
-// isCompositeLitType returns true iff x is a legal composite literal type.
-func isCompositeLitType(x ast.Expr) bool {
- // TODO(gri): should provide predicate in AST nodes
+// isLiteralType returns true iff x is a legal composite literal type.
+func isLiteralType(x ast.Expr) bool {
switch t := x.(type) {
case *ast.BadExpr:
case *ast.Ident:
- case *ast.ParenExpr:
- return isCompositeLitType(t.X)
case *ast.SelectorExpr:
- return isTypeName(t.X)
+ _, isIdent := t.X.(*ast.Ident)
+ return isIdent
case *ast.ArrayType:
case *ast.StructType:
case *ast.MapType:
}
+// If x is of the form *T, deref returns T, otherwise it returns x.
+func deref(x ast.Expr) ast.Expr {
+ if p, isPtr := x.(*ast.StarExpr); isPtr {
+ x = p.X
+ }
+ return x
+}
+
+
+// If x is of the form (T), unparen returns unparen(T), otherwise it returns x.
+func unparen(x ast.Expr) ast.Expr {
+ if p, isParen := x.(*ast.ParenExpr); isParen {
+ x = unparen(p.X)
+ }
+ return x
+}
+
+
// checkExprOrType checks that x is an expression or a type
// (and not a raw type such as [...]T).
//
func (p *parser) checkExprOrType(x ast.Expr) ast.Expr {
- // TODO(gri): should provide predicate in AST nodes
- switch t := x.(type) {
+ switch t := unparen(x).(type) {
+ case *ast.ParenExpr:
+ panic("unreachable")
case *ast.UnaryExpr:
if t.Op == token.RANGE {
// the range operator is only allowed at the top of a for statement
case token.LPAREN:
x = p.parseCallOrConversion(p.checkExprOrType(x))
case token.LBRACE:
- if isCompositeLitType(x) && (p.exprLev >= 0 || !isTypeName(x)) {
+ if isLiteralType(x) && (p.exprLev >= 0 || !isTypeName(x)) {
x = p.parseCompositeLit(x)
} else {
break L
func (p *parser) parseGenDecl(keyword token.Token, f parseSpecFunction) *ast.GenDecl {
if p.trace {
- defer un(trace(p, keyword.String()+"Decl"))
+ defer un(trace(p, "GenDecl("+keyword.String()+")"))
}
doc := p.leadComment
if par.NumFields() != 1 {
p.errorExpected(pos, "exactly one receiver")
par.List = []*ast.Field{&ast.Field{Type: &ast.BadExpr{noPos}}}
+ return par
}
+ // recv type must be of the form ["*"] identifier
recv := par.List[0]
-
- // recv type must be TypeName or *TypeName
- base := recv.Type
- if ptr, isPtr := base.(*ast.StarExpr); isPtr {
- base = ptr.X
- }
- if !isTypeName(base) {
- p.errorExpected(base.Pos(), "type name")
+ base := deref(recv.Type)
+ if _, isIdent := base.(*ast.Ident); !isIdent {
+ p.errorExpected(base.Pos(), "(unqualified) identifier")
+ par.List = []*ast.Field{&ast.Field{Type: &ast.BadExpr{recv.Pos()}}}
}
return par