// Scopes
pkgScope *ast.Scope
fileScope *ast.Scope
- topScope *ast.Scope
+ funcScope *ast.Scope
}
p.mode = mode
p.trace = mode&Trace != 0 // for convenience (p.trace is used frequently)
p.check = mode&CheckSemantics != 0 // for convenience (p.check is used frequently)
+ p.pkgScope = ast.NewScope(nil) // TODO(gri) should probably provide the pkgScope from outside
+ p.fileScope = ast.NewScope(p.pkgScope)
p.next()
}
// ----------------------------------------------------------------------------
// Scope support
-// Usage pattern: defer closeScope(openScope(p));
-func openScope(p *parser) *parser {
- p.topScope = ast.NewScope(p.topScope)
- return p
+func (p *parser) openScope() *ast.Scope {
+ p.funcScope = ast.NewScope(p.funcScope)
+ return p.funcScope
}
-func closeScope(p *parser) { p.topScope = p.topScope.Outer }
+func (p *parser) closeScope() { p.funcScope = p.funcScope.Outer }
func (p *parser) parseIdent(kind ast.ObjKind) *ast.Ident {
- obj := ast.NewObj(ast.Err, p.pos, "")
- if p.tok == token.IDENT {
- obj.Name = string(p.lit)
- p.next()
- } else {
- p.expect(token.IDENT) // use expect() error handling
- }
- return &ast.Ident{obj.Pos, obj}
-}
-
-
-// TODO(gri) Separate parsing from declaration since an identifier's
-// scope often starts only after the type has been seen.
-func (p *parser) declIdent(kind ast.ObjKind) *ast.Ident {
- obj := ast.NewObj(kind, p.pos, "")
+ obj := ast.NewObj(kind, p.pos, "_")
if p.tok == token.IDENT {
obj.Name = string(p.lit)
- // TODO(gri) Consider reversing the conditionals below:
- // always do the declaration but only report
- // error if enabled (may be necessary to get
- // search functionality in the presence of
- // incorrect files).
- if p.check && !p.topScope.Declare(obj) {
- // TODO(gri) Declare could return already-declared
- // object for a very good error message.
- p.Error(obj.Pos, "'"+obj.Name+"' declared already")
- }
p.next()
} else {
p.expect(token.IDENT) // use expect() error handling
}
-// TODO(gri) Separate parsing from declaration since an identifier's
-// scope often starts only after the type has been seen.
-func (p *parser) declIdentList(kind ast.ObjKind) []*ast.Ident {
+func (p *parser) parseIdentList(kind ast.ObjKind) []*ast.Ident {
if p.trace {
defer un(trace(p, "IdentList"))
}
var list vector.Vector
- list.Push(p.declIdent(kind))
+ list.Push(p.parseIdent(kind))
for p.tok == token.COMMA {
p.next()
- list.Push(p.declIdent(kind))
+ list.Push(p.parseIdent(kind))
}
// convert vector
}
+func (p *parser) declIdent(scope *ast.Scope, id *ast.Ident) {
+ ok := scope.Declare(id.Obj)
+ if p.check && !ok {
+ p.Error(id.Pos(), "'"+id.Name()+"' declared already")
+ }
+}
+
+
+func (p *parser) declIdentList(scope *ast.Scope, list []*ast.Ident) {
+ for _, id := range list {
+ p.declIdent(scope, id)
+ }
+}
+
+
+func (p *parser) declFieldList(scope *ast.Scope, list []*ast.Field) {
+ for _, f := range list {
+ p.declIdentList(scope, f.Names)
+ }
+}
+
+
func (p *parser) findIdent() *ast.Ident {
pos := p.pos
name := ""
var obj *ast.Object
if p.tok == token.IDENT {
name = string(p.lit)
- obj = p.topScope.Lookup(name)
+ obj = p.funcScope.Lookup(name)
p.next()
} else {
p.expect(token.IDENT) // use expect() error handling
}
if obj == nil {
+ // TODO(gri) These identifiers need to be tracked as
+ // unresolved identifiers in the package
+ // scope so that they can be resolved later.
obj = ast.NewObj(ast.Err, pos, name)
}
return &ast.Ident{pos, obj}
fields[i] = x.(*ast.Field)
}
+ // TODO(gri) The struct scope shouldn't get lost.
+ p.declFieldList(ast.NewScope(nil), fields)
+
return &ast.StructType{pos, lbrace, fields, rbrace, false}
}
}
for p.tok != token.RPAREN && p.tok != token.EOF {
- idents := p.declIdentList(ast.Var)
+ idents := p.parseIdentList(ast.Var)
typ := p.parseParameterType(ellipsisOk)
list.Push(&ast.Field{nil, idents, typ, nil, nil})
if p.tok != token.COMMA {
}
-func (p *parser) parseParameters(ellipsisOk bool) []*ast.Field {
+func (p *parser) parseParameters(scope *ast.Scope, ellipsisOk bool) []*ast.Field {
if p.trace {
defer un(trace(p, "Parameters"))
}
var params []*ast.Field
p.expect(token.LPAREN)
- openScope(p)
if p.tok != token.RPAREN {
params = p.parseParameterList(ellipsisOk)
+ p.declFieldList(scope, params)
}
- closeScope(p)
p.expect(token.RPAREN)
return params
}
-func (p *parser) parseResult() []*ast.Field {
+func (p *parser) parseResult(scope *ast.Scope) []*ast.Field {
if p.trace {
defer un(trace(p, "Result"))
}
var results []*ast.Field
if p.tok == token.LPAREN {
- results = p.parseParameters(false)
+ results = p.parseParameters(scope, false)
} else if p.tok != token.FUNC {
typ := p.tryType()
if typ != nil {
}
-func (p *parser) parseSignature() (params []*ast.Field, results []*ast.Field) {
+func (p *parser) parseSignature(scope *ast.Scope) (params []*ast.Field, results []*ast.Field) {
if p.trace {
defer un(trace(p, "Signature"))
}
- params = p.parseParameters(true)
- results = p.parseResult()
+ params = p.parseParameters(scope, true)
+ results = p.parseResult(scope)
return
}
-func (p *parser) parseFuncType() *ast.FuncType {
+func (p *parser) parseFuncType() (*ast.Scope, *ast.FuncType) {
if p.trace {
defer un(trace(p, "FuncType"))
}
pos := p.expect(token.FUNC)
- params, results := p.parseSignature()
+ scope := ast.NewScope(p.funcScope)
+ params, results := p.parseSignature(scope)
- return &ast.FuncType{pos, params, results}
+ return scope, &ast.FuncType{pos, params, results}
}
if ident, isIdent := x.(*ast.Ident); isIdent && p.tok == token.LPAREN {
// method
idents = []*ast.Ident{ident}
- params, results := p.parseSignature()
+ params, results := p.parseSignature(ast.NewScope(p.funcScope))
typ = &ast.FuncType{noPos, params, results}
} else {
// embedded interface
methods[i] = x.(*ast.Field)
}
+ // TODO(gri) The interface scope shouldn't get lost.
+ p.declFieldList(ast.NewScope(nil), methods)
+
return &ast.InterfaceType{pos, lbrace, methods, rbrace, false}
}
case token.MUL:
return p.parsePointerType()
case token.FUNC:
- return p.parseFuncType()
+ _, typ := p.parseFuncType()
+ return typ
case token.INTERFACE:
return p.parseInterfaceType()
case token.MAP:
}
+func (p *parser) parseBody(scope *ast.Scope) *ast.BlockStmt {
+ if p.trace {
+ defer un(trace(p, "Body"))
+ }
+
+ savedScope := p.funcScope
+ p.funcScope = scope
+
+ lbrace := p.expect(token.LBRACE)
+ list := p.parseStmtList()
+ rbrace := p.expect(token.RBRACE)
+
+ p.funcScope = savedScope
+
+ return &ast.BlockStmt{lbrace, list, rbrace}
+}
+
+
func (p *parser) parseBlockStmt() *ast.BlockStmt {
if p.trace {
defer un(trace(p, "BlockStmt"))
}
- defer closeScope(openScope(p))
+ p.openScope()
+ defer p.closeScope()
lbrace := p.expect(token.LBRACE)
list := p.parseStmtList()
defer un(trace(p, "FuncTypeOrLit"))
}
- typ := p.parseFuncType()
+ scope, typ := p.parseFuncType()
if p.tok != token.LBRACE {
// function type only
return typ
}
p.exprLev++
- body := p.parseBlockStmt()
+ body := p.parseBody(scope)
p.exprLev--
return &ast.FuncLit{typ, body}
}
// IfStmt block
- defer closeScope(openScope(p))
+ p.openScope()
+ defer p.closeScope()
pos := p.expect(token.IF)
s1, s2, _ := p.parseControlClause(false)
}
// CaseClause block
- defer closeScope(openScope(p))
+ p.openScope()
+ defer p.closeScope()
// SwitchCase
pos := p.pos
}
// TypeCaseClause block
- defer closeScope(openScope(p))
+ p.openScope()
+ defer p.closeScope()
// TypeSwitchCase
pos := p.pos
}
// SwitchStmt block
- defer closeScope(openScope(p))
+ p.openScope()
+ defer p.closeScope()
pos := p.expect(token.SWITCH)
s1, s2, _ := p.parseControlClause(false)
}
// CommClause block
- defer closeScope(openScope(p))
+ p.openScope()
+ defer p.closeScope()
// CommCase
pos := p.pos
}
// ForStmt block
- defer closeScope(openScope(p))
+ p.openScope()
+ defer p.closeScope()
pos := p.expect(token.FOR)
s1, s2, s3 := p.parseControlClause(true)
ident = &ast.Ident{p.pos, ast.NewObj(ast.Pkg, p.pos, ".")}
p.next()
} else if p.tok == token.IDENT {
- ident = p.declIdent(ast.Pkg)
+ ident = p.parseIdent(ast.Pkg)
+ p.declIdent(p.fileScope, ident)
}
var path []*ast.BasicLit
defer un(trace(p, "ConstSpec"))
}
- idents := p.declIdentList(ast.Con)
+ idents := p.parseIdentList(ast.Con)
+ if p.funcScope == nil {
+ // the scope of a constant outside any function
+ // is the package scope
+ p.declIdentList(p.pkgScope, idents)
+ }
typ := p.tryType()
var values []ast.Expr
if typ != nil || p.tok == token.ASSIGN {
p.expect(token.ASSIGN)
values = p.parseExprList()
}
+ if p.funcScope != nil {
+ // the scope of a constant inside a function
+ // begins after the the ConstSpec
+ p.declIdentList(p.funcScope, idents)
+ }
p.expectSemi()
return &ast.ValueSpec{doc, idents, typ, values, p.lineComment}
defer un(trace(p, "TypeSpec"))
}
- ident := p.declIdent(ast.Typ)
+ ident := p.parseIdent(ast.Typ)
+ // the scope of a type outside any function is
+ // the package scope; the scope of a type inside
+ // a function starts at the type identifier
+ scope := p.funcScope
+ if scope == nil {
+ scope = p.pkgScope
+ }
+ p.declIdent(scope, ident)
typ := p.parseType()
p.expectSemi()
defer un(trace(p, "VarSpec"))
}
- idents := p.declIdentList(ast.Var)
+ idents := p.parseIdentList(ast.Var)
+ if p.funcScope == nil {
+ // the scope of a variable outside any function
+ // is the pkgScope
+ p.declIdentList(p.pkgScope, idents)
+ }
typ := p.tryType()
var values []ast.Expr
if typ == nil || p.tok == token.ASSIGN {
p.expect(token.ASSIGN)
values = p.parseExprList()
}
+ if p.funcScope != nil {
+ // the scope of a variable inside a function
+ // begins after the the VarSpec
+ p.declIdentList(p.funcScope, idents)
+ }
p.expectSemi()
return &ast.ValueSpec{doc, idents, typ, values, p.lineComment}
}
-func (p *parser) parseReceiver() *ast.Field {
+func (p *parser) parseReceiver(scope *ast.Scope) *ast.Field {
if p.trace {
defer un(trace(p, "Receiver"))
}
pos := p.pos
- par := p.parseParameters(false)
+ par := p.parseParameters(scope, false)
// must have exactly one receiver
if len(par) != 1 || len(par) == 1 && len(par[0].Names) > 1 {
doc := p.leadComment
pos := p.expect(token.FUNC)
+ scope := ast.NewScope(p.funcScope)
var recv *ast.Field
if p.tok == token.LPAREN {
- recv = p.parseReceiver()
+ recv = p.parseReceiver(scope)
}
- ident := p.declIdent(ast.Fun)
- params, results := p.parseSignature()
+ ident := p.parseIdent(ast.Fun)
+ p.declIdent(p.pkgScope, ident) // there are no local function declarations
+ params, results := p.parseSignature(scope)
var body *ast.BlockStmt
if p.tok == token.LBRACE {
- body = p.parseBlockStmt()
+ body = p.parseBody(scope)
}
p.expectSemi()
ident := p.parseIdent(ast.Pkg) // package name is in no scope
p.expectSemi()
- // file block
- defer closeScope(openScope(p))
-
var decls []ast.Decl
// Don't bother parsing the rest if we had errors already.