exprLev int // < 0: in control clause, >= 0: in expression
// Ordinary identifer scopes
- pkgScope *ast.Scope // pkgScope.Outer == nil
- topScope *ast.Scope // top-most scope; may be pkgScope
- unresolved []*ast.Ident // unresolved global identifiers
+ pkgScope *ast.Scope // pkgScope.Outer == nil
+ topScope *ast.Scope // top-most scope; may be pkgScope
+ unresolved []*ast.Ident // unresolved identifiers
+ imports []*ast.ImportSpec // list of imports
// Label scope
// (maintained by open/close LabelScope)
func (p *parser) declare(decl interface{}, scope *ast.Scope, kind ast.ObjKind, idents ...*ast.Ident) {
for _, ident := range idents {
+ assert(ident.Obj == nil, "identifier already declared or resolved")
if ident.Name != "_" {
obj := ast.NewObj(kind, ident.Name)
// remember the corresponding declaration for redeclaration
// the same type, and at least one of the non-blank variables is new.
n := 0 // number of new variables
for _, ident := range idents {
+ assert(ident.Obj == nil, "identifier already declared or resolved")
if ident.Name != "_" {
obj := ast.NewObj(ast.Var, ident.Name)
// short var declarations cannot have redeclaration errors
}
-func (p *parser) resolve(ident *ast.Ident) {
+// The unresolved object is a sentinel to mark identifiers that have been added
+// to the list of unresolved identifiers. The sentinel is only used for verifying
+// internal consistency.
+var unresolved = new(ast.Object)
+
+
+func (p *parser) resolve(x ast.Expr) {
+ // nothing to do if x is not an identifier or the blank identifier
+ ident, _ := x.(*ast.Ident)
+ if ident == nil {
+ return
+ }
+ assert(ident.Obj == nil, "identifier already declared or resolved")
if ident.Name == "_" {
return
}
return
}
}
- // collect unresolved global identifiers; ignore the others
- if p.topScope == p.pkgScope {
- p.unresolved = append(p.unresolved, ident)
- }
+ // all local scopes are known, so any unresolved identifier
+ // must be found either in the file scope, package scope
+ // (perhaps in another file), or universe scope --- collect
+ // them so that they can be resolved later
+ ident.Obj = unresolved
+ p.unresolved = append(p.unresolved, ident)
}
}
+func assert(cond bool, msg string) {
+ if !cond {
+ panic("go/parser internal error: " + msg)
+ }
+}
+
+
// ----------------------------------------------------------------------------
// Identifiers
// ----------------------------------------------------------------------------
// Common productions
-func (p *parser) parseExprList() (list []ast.Expr) {
+// If lhs is set, result list elements which are identifiers are not resolved.
+func (p *parser) parseExprList(lhs bool) (list []ast.Expr) {
if p.trace {
defer un(trace(p, "ExpressionList"))
}
- list = append(list, p.parseExpr())
+ list = append(list, p.parseExpr(lhs))
for p.tok == token.COMMA {
p.next()
- list = append(list, p.parseExpr())
+ list = append(list, p.parseExpr(lhs))
}
return
}
+func (p *parser) parseLhsList() []ast.Expr {
+ list := p.parseExprList(true)
+ switch p.tok {
+ case token.DEFINE:
+ // lhs of a short variable declaration
+ p.shortVarDecl(p.makeIdentList(list))
+ case token.COLON:
+ // lhs of a label declaration or a communication clause of a select
+ // statement (parseLhsList is not called when parsing the case clause
+ // of a switch statement):
+ // - labels are declared by the caller of parseLhsList
+ // - for communication clauses, if there is a stand-alone identifier
+ // followed by a colon, we have a syntax error; there is no need
+ // to resolve the identifier in that case
+ default:
+ // identifiers must be declared elsewhere
+ for _, x := range list {
+ p.resolve(x)
+ }
+ }
+ return list
+}
+
+
+func (p *parser) parseRhsList() []ast.Expr {
+ return p.parseExprList(false)
+}
+
+
// ----------------------------------------------------------------------------
// Types
}
-func (p *parser) parseQualifiedIdent() ast.Expr {
+// If the result is an identifier, it is not resolved.
+func (p *parser) parseTypeName() ast.Expr {
if p.trace {
- defer un(trace(p, "QualifiedIdent"))
+ defer un(trace(p, "TypeName"))
}
ident := p.parseIdent()
- p.resolve(ident)
- var x ast.Expr = ident
+ // don't resolve ident yet - it may be a parameter or field name
+
if p.tok == token.PERIOD {
- // first identifier is a package identifier
+ // ident is a package name
p.next()
+ p.resolve(ident)
sel := p.parseIdent()
- x = &ast.SelectorExpr{x, sel}
+ return &ast.SelectorExpr{ident, sel}
}
- return x
-}
-
-
-func (p *parser) parseTypeName() ast.Expr {
- if p.trace {
- defer un(trace(p, "TypeName"))
- }
-
- return p.parseQualifiedIdent()
+ return ident
}
len = &ast.Ellipsis{p.pos, nil}
p.next()
} else if p.tok != token.RBRACK {
- len = p.parseExpr()
+ len = p.parseRhs()
}
p.expect(token.RBRACK)
elt := p.parseType()
}
-func (p *parser) parseFieldDecl() *ast.Field {
+func (p *parser) parseFieldDecl(scope *ast.Scope) *ast.Field {
if p.trace {
defer un(trace(p, "FieldDecl"))
}
} else {
// ["*"] TypeName (AnonymousField)
typ = list[0] // we always have at least one element
+ p.resolve(typ)
if n := len(list); n > 1 || !isTypeName(deref(typ)) {
pos := typ.Pos()
p.errorExpected(pos, "anonymous field")
p.expectSemi() // call before accessing p.linecomment
- return &ast.Field{doc, idents, typ, tag, p.lineComment}
+ field := &ast.Field{doc, idents, typ, tag, p.lineComment}
+ p.declare(field, scope, ast.Var, idents...)
+
+ return field
}
pos := p.expect(token.STRUCT)
lbrace := p.expect(token.LBRACE)
+ scope := ast.NewScope(nil) // struct scope
var list []*ast.Field
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 = append(list, p.parseFieldDecl())
+ list = append(list, p.parseFieldDecl(scope))
}
rbrace := p.expect(token.RBRACE)
+ // TODO(gri): store struct scope in AST
return &ast.StructType{pos, &ast.FieldList{lbrace, list, rbrace}, false}
}
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
+ typ := p.tryIdentOrType(isParam) // don't use parseType so we can provide better error message
if typ == nil {
p.error(pos, "'...' parameter is missing type")
typ = &ast.BadExpr{pos, p.pos}
}
return &ast.Ellipsis{pos, typ}
}
- return p.tryType()
+ return p.tryIdentOrType(false)
}
// if we had a list of identifiers, it must be followed by a type
typ = p.tryVarType(isParam)
+ if typ != nil {
+ p.resolve(typ)
+ }
return
}
// Type { "," Type } (anonymous parameters)
params = make([]*ast.Field, len(list))
for i, x := range list {
+ p.resolve(x)
params[i] = &ast.Field{Type: x}
}
}
}
-func (p *parser) parseMethodSpec() *ast.Field {
+func (p *parser) parseMethodSpec(scope *ast.Scope) *ast.Field {
if p.trace {
defer un(trace(p, "MethodSpec"))
}
doc := p.leadComment
var idents []*ast.Ident
var typ ast.Expr
- x := p.parseQualifiedIdent()
+ x := p.parseTypeName()
if ident, isIdent := x.(*ast.Ident); isIdent && p.tok == token.LPAREN {
// method
idents = []*ast.Ident{ident}
}
p.expectSemi() // call before accessing p.linecomment
- return &ast.Field{doc, idents, typ, nil, p.lineComment}
+ spec := &ast.Field{doc, idents, typ, nil, p.lineComment}
+ p.declare(spec, scope, ast.Fun, idents...)
+
+ return spec
}
pos := p.expect(token.INTERFACE)
lbrace := p.expect(token.LBRACE)
+ scope := ast.NewScope(nil) // interface scope
var list []*ast.Field
for p.tok == token.IDENT {
- list = append(list, p.parseMethodSpec())
+ list = append(list, p.parseMethodSpec(scope))
}
rbrace := p.expect(token.RBRACE)
+ // TODO(gri): store interface scope in AST
return &ast.InterfaceType{pos, &ast.FieldList{lbrace, list, rbrace}, false}
}
}
-func (p *parser) tryRawType(ellipsisOk bool) ast.Expr {
+// If the result is an identifier, it is not resolved.
+func (p *parser) tryIdentOrType(ellipsisOk bool) ast.Expr {
switch p.tok {
case token.IDENT:
return p.parseTypeName()
}
-func (p *parser) tryType() ast.Expr { return p.tryRawType(false) }
+func (p *parser) tryType() ast.Expr {
+ typ := p.tryIdentOrType(false)
+ if typ != nil {
+ p.resolve(typ)
+ }
+ return typ
+}
// ----------------------------------------------------------------------------
// parseOperand may return an expression or a raw type (incl. array
// types of the form [...]T. Callers must verify the result.
+// If lhs is set and the result is an identifier, it is not resolved.
//
-func (p *parser) parseOperand() ast.Expr {
+func (p *parser) parseOperand(lhs bool) ast.Expr {
if p.trace {
defer un(trace(p, "Operand"))
}
switch p.tok {
case token.IDENT:
- ident := p.parseIdent()
- p.resolve(ident)
- return ident
+ x := p.parseIdent()
+ if !lhs {
+ p.resolve(x)
+ }
+ return x
case token.INT, token.FLOAT, token.IMAG, token.CHAR, token.STRING:
x := &ast.BasicLit{p.pos, p.tok, p.lit()}
lparen := p.pos
p.next()
p.exprLev++
- x := p.parseExpr()
+ x := p.parseRhs()
p.exprLev--
rparen := p.expect(token.RPAREN)
return &ast.ParenExpr{lparen, x, rparen}
return p.parseFuncTypeOrLit()
default:
- t := p.tryRawType(true) // could be type for composite literal or conversion
- if t != nil {
- return t
+ if typ := p.tryIdentOrType(true); typ != nil {
+ // could be type for composite literal or conversion
+ _, isIdent := typ.(*ast.Ident)
+ assert(!isIdent, "type cannot be identifier")
+ return typ
}
}
}
-func (p *parser) parseSelectorOrTypeAssertion(x ast.Expr) ast.Expr {
+func (p *parser) parseSelector(x ast.Expr) ast.Expr {
if p.trace {
- defer un(trace(p, "SelectorOrTypeAssertion"))
+ defer un(trace(p, "Selector"))
}
- p.expect(token.PERIOD)
- if p.tok == token.IDENT {
- // selector
- sel := p.parseIdent()
- return &ast.SelectorExpr{x, sel}
+ sel := p.parseIdent()
+
+ return &ast.SelectorExpr{x, sel}
+}
+
+
+func (p *parser) parseTypeAssertion(x ast.Expr) ast.Expr {
+ if p.trace {
+ defer un(trace(p, "TypeAssertion"))
}
- // type assertion
p.expect(token.LPAREN)
var typ ast.Expr
if p.tok == token.TYPE {
var low, high ast.Expr
isSlice := false
if p.tok != token.COLON {
- low = p.parseExpr()
+ low = p.parseRhs()
}
if p.tok == token.COLON {
isSlice = true
p.next()
if p.tok != token.RBRACK {
- high = p.parseExpr()
+ high = p.parseRhs()
}
}
p.exprLev--
var list []ast.Expr
var ellipsis token.Pos
for p.tok != token.RPAREN && p.tok != token.EOF && !ellipsis.IsValid() {
- list = append(list, p.parseExpr())
+ list = append(list, p.parseRhs())
if p.tok == token.ELLIPSIS {
ellipsis = p.pos
p.next()
return p.parseLiteralValue(nil)
}
- x := p.parseExpr()
+ x := p.parseRhs()
if keyOk && p.tok == token.COLON {
colon := p.pos
p.next()
}
-func (p *parser) parsePrimaryExpr() ast.Expr {
+// If lhs is set and the result is an identifier, it is not resolved.
+func (p *parser) parsePrimaryExpr(lhs bool) ast.Expr {
if p.trace {
defer un(trace(p, "PrimaryExpr"))
}
- x := p.parseOperand()
+ x := p.parseOperand(lhs)
L:
for {
switch p.tok {
case token.PERIOD:
- x = p.parseSelectorOrTypeAssertion(p.checkExpr(x))
+ p.next()
+ if lhs {
+ p.resolve(x)
+ }
+ switch p.tok {
+ case token.IDENT:
+ x = p.parseSelector(p.checkExpr(x))
+ case token.LPAREN:
+ x = p.parseTypeAssertion(p.checkExpr(x))
+ default:
+ pos := p.pos
+ p.next() // make progress
+ p.errorExpected(pos, "selector or type assertion")
+ x = &ast.BadExpr{pos, p.pos}
+ }
case token.LBRACK:
+ if lhs {
+ p.resolve(x)
+ }
x = p.parseIndexOrSlice(p.checkExpr(x))
case token.LPAREN:
+ if lhs {
+ p.resolve(x)
+ }
x = p.parseCallOrConversion(p.checkExprOrType(x))
case token.LBRACE:
if isLiteralType(x) && (p.exprLev >= 0 || !isTypeName(x)) {
+ if lhs {
+ p.resolve(x)
+ }
x = p.parseLiteralValue(x)
} else {
break L
default:
break L
}
+ lhs = false // no need to try to resolve again
}
return x
}
-func (p *parser) parseUnaryExpr() ast.Expr {
+// If lhs is set and the result is an identifier, it is not resolved.
+func (p *parser) parseUnaryExpr(lhs bool) ast.Expr {
if p.trace {
defer un(trace(p, "UnaryExpr"))
}
case token.ADD, token.SUB, token.NOT, token.XOR, token.AND, token.RANGE:
pos, op := p.pos, p.tok
p.next()
- x := p.parseUnaryExpr()
+ x := p.parseUnaryExpr(false)
return &ast.UnaryExpr{pos, op, p.checkExpr(x)}
case token.ARROW:
return &ast.ChanType{pos, ast.RECV, value}
}
- x := p.parseUnaryExpr()
+ x := p.parseUnaryExpr(false)
return &ast.UnaryExpr{pos, token.ARROW, p.checkExpr(x)}
case token.MUL:
// pointer type or unary "*" expression
pos := p.pos
p.next()
- x := p.parseUnaryExpr()
+ x := p.parseUnaryExpr(false)
return &ast.StarExpr{pos, p.checkExprOrType(x)}
}
- return p.parsePrimaryExpr()
+ return p.parsePrimaryExpr(lhs)
}
-func (p *parser) parseBinaryExpr(prec1 int) ast.Expr {
+// If lhs is set and the result is an identifier, it is not resolved.
+func (p *parser) parseBinaryExpr(lhs bool, prec1 int) ast.Expr {
if p.trace {
defer un(trace(p, "BinaryExpr"))
}
- x := p.parseUnaryExpr()
+ x := p.parseUnaryExpr(lhs)
for prec := p.tok.Precedence(); prec >= prec1; prec-- {
for p.tok.Precedence() == prec {
pos, op := p.pos, p.tok
p.next()
- y := p.parseBinaryExpr(prec + 1)
+ if lhs {
+ p.resolve(x)
+ lhs = false
+ }
+ y := p.parseBinaryExpr(false, prec+1)
x = &ast.BinaryExpr{p.checkExpr(x), pos, op, p.checkExpr(y)}
}
}
}
+// If lhs is set and the result is an identifier, it is not resolved.
// TODO(gri): parseExpr may return a type or even a raw type ([..]int) -
// should reject when a type/raw type is obviously not allowed
-func (p *parser) parseExpr() ast.Expr {
+func (p *parser) parseExpr(lhs bool) ast.Expr {
if p.trace {
defer un(trace(p, "Expression"))
}
- return p.parseBinaryExpr(token.LowestPrec + 1)
+ return p.parseBinaryExpr(lhs, token.LowestPrec+1)
+}
+
+
+func (p *parser) parseRhs() ast.Expr {
+ return p.parseExpr(false)
}
defer un(trace(p, "SimpleStmt"))
}
- x := p.parseExprList()
+ x := p.parseLhsList()
switch p.tok {
case
// assignment statement
pos, tok := p.pos, p.tok
p.next()
- y := p.parseExprList()
- if tok == token.DEFINE {
- p.shortVarDecl(p.makeIdentList(x))
- }
+ y := p.parseRhsList()
return &ast.AssignStmt{x, pos, tok, y}
}
// send statement
arrow := p.pos
p.next() // consume "<-"
- y := p.parseExpr()
+ y := p.parseRhs()
return &ast.SendStmt{x[0], arrow, y}
case token.INC, token.DEC:
func (p *parser) parseCallExpr() *ast.CallExpr {
- x := p.parseExpr()
+ x := p.parseRhs()
if call, isCall := x.(*ast.CallExpr); isCall {
return call
}
p.expect(token.RETURN)
var x []ast.Expr
if p.tok != token.SEMICOLON && p.tok != token.RBRACE {
- x = p.parseExprList()
+ x = p.parseRhsList()
}
p.expectSemi()
p.exprLev = -1
if p.tok == token.SEMICOLON {
p.next()
- x = p.parseExpr()
+ x = p.parseRhs()
} else {
s = p.parseSimpleStmt(false)
if p.tok == token.SEMICOLON {
p.next()
- x = p.parseExpr()
+ x = p.parseRhs()
} else {
x = p.makeExpr(s)
s = nil
if p.tok == token.CASE {
p.next()
if exprSwitch {
- list = p.parseExprList()
+ list = p.parseRhsList()
} else {
list = p.parseTypeList()
}
var comm ast.Stmt
if p.tok == token.CASE {
p.next()
- lhs := p.parseExprList()
+ lhs := p.parseLhsList()
if p.tok == token.ARROW {
// SendStmt
if len(lhs) > 1 {
}
arrow := p.pos
p.next()
- rhs := p.parseExpr()
+ rhs := p.parseRhs()
comm = &ast.SendStmt{lhs[0], arrow, rhs}
} else {
// RecvStmt
lhs = lhs[0:2]
}
p.next()
- rhs = p.parseExpr()
- if tok == token.DEFINE {
- p.shortVarDecl(p.makeIdentList(lhs))
- }
+ rhs = p.parseRhs()
} else {
// rhs must be single receive operation
if len(lhs) > 1 {
}
p.expectSemi() // call before accessing p.linecomment
- return &ast.ImportSpec{doc, ident, path, p.lineComment}
+ // collect imports
+ spec := &ast.ImportSpec{doc, ident, path, p.lineComment}
+ p.imports = append(p.imports, spec)
+
+ return spec
}
var values []ast.Expr
if typ != nil || p.tok == token.ASSIGN || iota == 0 {
p.expect(token.ASSIGN)
- values = p.parseExprList()
+ values = p.parseRhsList()
}
p.expectSemi() // call before accessing p.linecomment
var values []ast.Expr
if typ == nil || p.tok == token.ASSIGN {
p.expect(token.ASSIGN)
- values = p.parseExprList()
+ values = p.parseRhsList()
}
p.expectSemi() // call before accessing p.linecomment
}
}
- if p.topScope != p.pkgScope {
- panic("internal error: imbalanced scopes")
- }
+ assert(p.topScope == p.pkgScope, "imbalanced scopes")
// resolve global identifiers within the same file
i := 0
for _, ident := range p.unresolved {
// i <= index for current ident
- ident.Obj = p.pkgScope.Lookup(ident.Name)
+ assert(ident.Obj == unresolved, "object already resolved")
+ ident.Obj = p.pkgScope.Lookup(ident.Name) // also removes unresolved sentinel
if ident.Obj == nil {
p.unresolved[i] = ident
i++
}
}
+ // TODO(gri): store p.imports in AST
return &ast.File{doc, pos, ident, decls, p.pkgScope, p.unresolved[0:i], p.comments}
}