Creater a binder.Context structure
This change introduces a binder.Context structure, with a *core.Context embedded, that carries additional semantic information forward to future passes in the compiler. In particular, this is how the evaluation/interpretation phase will gain access to types, scopes, and symbols.
This commit is contained in:
parent
f3c69b671a
commit
c5aea187c2
7 changed files with 191 additions and 167 deletions
|
@ -22,47 +22,32 @@ import (
|
||||||
type Binder interface {
|
type Binder interface {
|
||||||
core.Phase
|
core.Phase
|
||||||
|
|
||||||
|
// BindCtx represents the contextual information resulting from binding.
|
||||||
|
BindCtx() *Context
|
||||||
|
|
||||||
// BindPackages takes a package AST, resolves all dependencies and tokens inside of it, and returns a fully bound
|
// BindPackages takes a package AST, resolves all dependencies and tokens inside of it, and returns a fully bound
|
||||||
// package symbol that can be used for semantic operations (like interpretation and evaluation).
|
// package symbol that can be used for semantic operations (like interpretation and evaluation).
|
||||||
BindPackage(pkg *pack.Package) *symbols.Package
|
BindPackage(pkg *pack.Package) *symbols.Package
|
||||||
}
|
}
|
||||||
|
|
||||||
// TypeMap maps AST nodes to their corresponding type. The semantics of this differ based on the kind of node. For
|
|
||||||
// example, an ast.Expression's type is the type of its evaluation; an ast.LocalVariable's type is the bound type of its
|
|
||||||
// value. And so on. This is used during binding, type checking, and evaluation, to perform type-sensitive operations.
|
|
||||||
// This avoids needing to recreate scopes and/or storing type information on every single node in the AST.
|
|
||||||
type TypeMap map[ast.Node]symbols.Type
|
|
||||||
|
|
||||||
// New allocates a fresh binder object with the given workspace, context, and metadata reader.
|
// New allocates a fresh binder object with the given workspace, context, and metadata reader.
|
||||||
func New(w workspace.W, ctx *core.Context, reader metadata.Reader) Binder {
|
func New(w workspace.W, ctx *core.Context, reader metadata.Reader) Binder {
|
||||||
// Create a new binder and a new scope with an empty symbol table.
|
// Create a new binder with a fresh binding context.
|
||||||
b := &binder{
|
return &binder{
|
||||||
w: w,
|
w: w,
|
||||||
ctx: ctx,
|
ctx: NewContextFrom(ctx),
|
||||||
reader: reader,
|
reader: reader,
|
||||||
types: make(TypeMap),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a global scope and populate it with all of the predefined type names. This one's never popped.
|
|
||||||
NewScope(ctx, &b.scope)
|
|
||||||
for _, prim := range types.Primitives {
|
|
||||||
b.scope.MustRegister(prim)
|
|
||||||
}
|
|
||||||
|
|
||||||
return b
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type binder struct {
|
type binder struct {
|
||||||
w workspace.W // a workspace in which this compilation is happening.
|
w workspace.W // a workspace in which this compilation is happening.
|
||||||
ctx *core.Context // a context shared across all phases of compilation.
|
ctx *Context // a binding context shared with future phases of compilation.
|
||||||
reader metadata.Reader // a metadata reader (in case we encounter package references).
|
reader metadata.Reader // a metadata reader (in case we encounter package references).
|
||||||
scope *Scope // the current (mutable) scope.
|
|
||||||
types TypeMap // the typechecked types for expressions (see TypeMap's comments above).
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *binder) Diag() diag.Sink {
|
func (b *binder) BindCtx() *Context { return b.ctx }
|
||||||
return b.ctx.Diag
|
func (b *binder) Diag() diag.Sink { return b.ctx.Diag }
|
||||||
}
|
|
||||||
|
|
||||||
// bindType binds a type token AST node to a symbol.
|
// bindType binds a type token AST node to a symbol.
|
||||||
func (b *binder) bindType(node *ast.TypeToken) symbols.Type {
|
func (b *binder) bindType(node *ast.TypeToken) symbols.Type {
|
||||||
|
@ -261,7 +246,7 @@ func (b *binder) requireToken(node ast.Node, tok tokens.Token) symbols.Symbol {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// A simple token has no package, module, or class part. It refers to the symbol table.
|
// A simple token has no package, module, or class part. It refers to the symbol table.
|
||||||
if sym := b.scope.Lookup(tok); sym != nil {
|
if sym := b.ctx.Scope.Lookup(tok); sym != nil {
|
||||||
return sym
|
return sym
|
||||||
} else {
|
} else {
|
||||||
b.Diag().Errorf(errors.ErrorSymbolNotFound.At(node), tok, "simple name not found")
|
b.Diag().Errorf(errors.ErrorSymbolNotFound.At(node), tok, "simple name not found")
|
||||||
|
@ -283,101 +268,3 @@ func (b *binder) requireClassMember(node ast.Node, class symbols.Type, tok token
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// requireType requires that a type exists for the given AST node.
|
|
||||||
func (b *binder) requireType(node ast.Node) symbols.Type {
|
|
||||||
ty := b.types[node]
|
|
||||||
contract.Assertf(ty != nil, "Expected a typemap entry for %v node", node.GetKind())
|
|
||||||
return ty
|
|
||||||
}
|
|
||||||
|
|
||||||
// requireExprType fetches an expression's non-nil type.
|
|
||||||
func (b *binder) requireExprType(node ast.Expression) symbols.Type {
|
|
||||||
return b.requireType(node)
|
|
||||||
}
|
|
||||||
|
|
||||||
// registerExprType registers an expression's type.
|
|
||||||
func (b *binder) registerExprType(node ast.Expression, tysym symbols.Type) {
|
|
||||||
contract.Require(tysym != nil, "tysym")
|
|
||||||
contract.Assert(b.types[node] == nil)
|
|
||||||
if glog.V(7) {
|
|
||||||
glog.V(7).Infof("Registered expression type: '%v' => %v", node.GetKind(), tysym.Name())
|
|
||||||
}
|
|
||||||
b.types[node] = tysym
|
|
||||||
}
|
|
||||||
|
|
||||||
// requireFunctionType fetches the non-nil registered type for a given function.
|
|
||||||
func (b *binder) requireFunctionType(node ast.Function) *symbols.FunctionType {
|
|
||||||
ty := b.requireType(node)
|
|
||||||
fty, ok := ty.(*symbols.FunctionType)
|
|
||||||
contract.Assertf(ok, "Expected function type for %v; got %v", node.GetKind(), fty.Token())
|
|
||||||
return fty
|
|
||||||
}
|
|
||||||
|
|
||||||
// registerFunctionType understands how to turn any function node into a type, and adds it to the type table. This
|
|
||||||
// works for any kind of function-like AST node: module property, class property, or lambda.
|
|
||||||
func (b *binder) registerFunctionType(node ast.Function) *symbols.FunctionType {
|
|
||||||
// Make a function type and inject it into the type table.
|
|
||||||
var params []symbols.Type
|
|
||||||
np := node.GetParameters()
|
|
||||||
if np != nil {
|
|
||||||
for _, param := range *np {
|
|
||||||
var ptysym symbols.Type
|
|
||||||
|
|
||||||
// If there was an explicit type, look it up.
|
|
||||||
if param.Type != nil {
|
|
||||||
ptysym = b.scope.LookupType(param.Type.Tok)
|
|
||||||
}
|
|
||||||
|
|
||||||
// If either the parameter's type was unknown, or the lookup failed (leaving an error), use the any type.
|
|
||||||
if ptysym == nil {
|
|
||||||
ptysym = types.Any
|
|
||||||
}
|
|
||||||
|
|
||||||
params = append(params, ptysym)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var ret symbols.Type
|
|
||||||
nr := node.GetReturnType()
|
|
||||||
if nr != nil {
|
|
||||||
ret = b.scope.LookupType(nr.Tok)
|
|
||||||
}
|
|
||||||
|
|
||||||
tysym := symbols.NewFunctionType(params, ret)
|
|
||||||
if glog.V(7) {
|
|
||||||
glog.V(7).Infof("Registered function type: '%v' => %v", node.GetName().Ident, tysym.Name())
|
|
||||||
}
|
|
||||||
b.types[node] = tysym
|
|
||||||
|
|
||||||
return tysym
|
|
||||||
}
|
|
||||||
|
|
||||||
// requireVariableType fetches the non-nil registered type for a given variable.
|
|
||||||
func (b *binder) requireVariableType(node ast.Variable) symbols.Type {
|
|
||||||
return b.requireType(node)
|
|
||||||
}
|
|
||||||
|
|
||||||
// registerVariableType understands how to turn any variable node into a type, and adds it to the type table. This
|
|
||||||
// works for any kind of variable-like AST node: module property, class property, parameter, or local variable.
|
|
||||||
func (b *binder) registerVariableType(node ast.Variable) symbols.Type {
|
|
||||||
var tysym symbols.Type
|
|
||||||
|
|
||||||
// If there is an explicit node type, use it.
|
|
||||||
nt := node.GetType()
|
|
||||||
if nt != nil {
|
|
||||||
tysym = b.scope.LookupType(nt.Tok)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise, either there was no type, or the lookup failed (leaving behind an error); use the any type.
|
|
||||||
if tysym == nil {
|
|
||||||
tysym = types.Any
|
|
||||||
}
|
|
||||||
|
|
||||||
if glog.V(7) {
|
|
||||||
glog.V(7).Infof("Registered variable type: '%v' => %v", node.GetName().Ident, tysym.Name())
|
|
||||||
}
|
|
||||||
b.types[node] = tysym
|
|
||||||
|
|
||||||
return tysym
|
|
||||||
}
|
|
||||||
|
|
|
@ -16,12 +16,12 @@ func (b *binder) bindClass(node *ast.Class, parent *symbols.Module) *symbols.Cla
|
||||||
// Bind base type tokens to actual symbols.
|
// Bind base type tokens to actual symbols.
|
||||||
var extends symbols.Type
|
var extends symbols.Type
|
||||||
if node.Extends != nil {
|
if node.Extends != nil {
|
||||||
extends = b.scope.LookupType(node.Extends.Tok)
|
extends = b.ctx.Scope.LookupType(node.Extends.Tok)
|
||||||
}
|
}
|
||||||
var implements symbols.Types
|
var implements symbols.Types
|
||||||
if node.Implements != nil {
|
if node.Implements != nil {
|
||||||
for _, impltok := range *node.Implements {
|
for _, impltok := range *node.Implements {
|
||||||
if impl := b.scope.LookupType(impltok.Tok); impl != nil {
|
if impl := b.ctx.Scope.LookupType(impltok.Tok); impl != nil {
|
||||||
implements = append(implements, impl)
|
implements = append(implements, impl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,7 +61,7 @@ func (b *binder) bindClassProperty(node *ast.ClassProperty, parent *symbols.Clas
|
||||||
glog.V(3).Infof("Binding class '%v' property '%v'", parent.Name(), node.Name.Ident)
|
glog.V(3).Infof("Binding class '%v' property '%v'", parent.Name(), node.Name.Ident)
|
||||||
|
|
||||||
// Look up this node's type and inject it into the type table.
|
// Look up this node's type and inject it into the type table.
|
||||||
ty := b.registerVariableType(node)
|
ty := b.ctx.RegisterVariableType(node)
|
||||||
return symbols.NewClassPropertySym(node, parent, ty)
|
return symbols.NewClassPropertySym(node, parent, ty)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,7 +69,7 @@ func (b *binder) bindClassMethod(node *ast.ClassMethod, parent *symbols.Class) *
|
||||||
glog.V(3).Infof("Binding class '%v' method '%v'", parent.Name(), node.Name.Ident)
|
glog.V(3).Infof("Binding class '%v' method '%v'", parent.Name(), node.Name.Ident)
|
||||||
|
|
||||||
// Make a function type out of this method and inject it into the type table.
|
// Make a function type out of this method and inject it into the type table.
|
||||||
ty := b.registerFunctionType(node)
|
ty := b.ctx.RegisterFunctionType(node)
|
||||||
|
|
||||||
// Note that we don't actually bind the body of this method yet. Until we have gone ahead and injected *all*
|
// Note that we don't actually bind the body of this method yet. Until we have gone ahead and injected *all*
|
||||||
// top-level symbols into the type table, we would potentially encounter missing intra-module symbols.
|
// top-level symbols into the type table, we would potentially encounter missing intra-module symbols.
|
||||||
|
|
139
pkg/compiler/binder/context.go
Normal file
139
pkg/compiler/binder/context.go
Normal file
|
@ -0,0 +1,139 @@
|
||||||
|
// Copyright 2016 Marapongo, Inc. All rights reserved.
|
||||||
|
|
||||||
|
package binder
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/golang/glog"
|
||||||
|
|
||||||
|
"github.com/marapongo/mu/pkg/compiler/ast"
|
||||||
|
"github.com/marapongo/mu/pkg/compiler/core"
|
||||||
|
"github.com/marapongo/mu/pkg/compiler/symbols"
|
||||||
|
"github.com/marapongo/mu/pkg/compiler/types"
|
||||||
|
"github.com/marapongo/mu/pkg/util/contract"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Context holds binder-specific context information, like symbol and type binding information.
|
||||||
|
type Context struct {
|
||||||
|
*core.Context // inherits all of the other context info.
|
||||||
|
Scope *Scope // the current (mutable) scope.
|
||||||
|
Types TypeMap // the type-checked type symbols for expressions.
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewContextFrom(ctx *core.Context) *Context {
|
||||||
|
bctx := &Context{
|
||||||
|
Context: ctx,
|
||||||
|
Types: make(TypeMap),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a global scope and populate it with all of the predefined type names. This one's never popped.
|
||||||
|
NewScope(ctx, &bctx.Scope)
|
||||||
|
for _, prim := range types.Primitives {
|
||||||
|
bctx.Scope.MustRegister(prim)
|
||||||
|
}
|
||||||
|
|
||||||
|
return bctx
|
||||||
|
}
|
||||||
|
|
||||||
|
// TypeMap maps AST nodes to their corresponding type. The semantics of this differ based on the kind of node. For
|
||||||
|
// example, an ast.Expression's type is the type of its evaluation; an ast.LocalVariable's type is the bound type of its
|
||||||
|
// value. And so on. This is used during binding, type checking, and evaluation, to perform type-sensitive operations.
|
||||||
|
// This avoids needing to recreate scopes and/or storing type information on every single node in the AST.
|
||||||
|
type TypeMap map[ast.Node]symbols.Type
|
||||||
|
|
||||||
|
// RequireType requires that a type exists for the given AST node.
|
||||||
|
func (ctx *Context) RequireType(node ast.Node) symbols.Type {
|
||||||
|
ty := ctx.Types[node]
|
||||||
|
contract.Assertf(ty != nil, "Expected a typemap entry for %v node", node.GetKind())
|
||||||
|
return ty
|
||||||
|
}
|
||||||
|
|
||||||
|
// RequireExprType fetches an expression's non-nil type.
|
||||||
|
func (ctx *Context) RequireExprType(node ast.Expression) symbols.Type {
|
||||||
|
return ctx.RequireType(node)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterExprType registers an expression's type.
|
||||||
|
func (ctx *Context) RegisterExprType(node ast.Expression, tysym symbols.Type) {
|
||||||
|
contract.Require(tysym != nil, "tysym")
|
||||||
|
contract.Assert(ctx.Types[node] == nil)
|
||||||
|
if glog.V(7) {
|
||||||
|
glog.V(7).Infof("Registered expression type: '%v' => %v", node.GetKind(), tysym.Name())
|
||||||
|
}
|
||||||
|
ctx.Types[node] = tysym
|
||||||
|
}
|
||||||
|
|
||||||
|
// RequireFunctionType fetches the non-nil registered type for a given function.
|
||||||
|
func (ctx *Context) RequireFunctionType(node ast.Function) *symbols.FunctionType {
|
||||||
|
ty := ctx.RequireType(node)
|
||||||
|
fty, ok := ty.(*symbols.FunctionType)
|
||||||
|
contract.Assertf(ok, "Expected function type for %v; got %v", node.GetKind(), fty.Token())
|
||||||
|
return fty
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterFunctionType understands how to turn any function node into a type, and adds it to the type table. This
|
||||||
|
// works for any kind of function-like AST node: module property, class property, or lambda.
|
||||||
|
func (ctx *Context) RegisterFunctionType(node ast.Function) *symbols.FunctionType {
|
||||||
|
// Make a function type and inject it into the type table.
|
||||||
|
var params []symbols.Type
|
||||||
|
np := node.GetParameters()
|
||||||
|
if np != nil {
|
||||||
|
for _, param := range *np {
|
||||||
|
var ptysym symbols.Type
|
||||||
|
|
||||||
|
// If there was an explicit type, look it up.
|
||||||
|
if param.Type != nil {
|
||||||
|
ptysym = ctx.Scope.LookupType(param.Type.Tok)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If either the parameter's type was unknown, or the lookup failed (leaving an error), use the any type.
|
||||||
|
if ptysym == nil {
|
||||||
|
ptysym = types.Any
|
||||||
|
}
|
||||||
|
|
||||||
|
params = append(params, ptysym)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var ret symbols.Type
|
||||||
|
nr := node.GetReturnType()
|
||||||
|
if nr != nil {
|
||||||
|
ret = ctx.Scope.LookupType(nr.Tok)
|
||||||
|
}
|
||||||
|
|
||||||
|
tysym := symbols.NewFunctionType(params, ret)
|
||||||
|
if glog.V(7) {
|
||||||
|
glog.V(7).Infof("Registered function type: '%v' => %v", node.GetName().Ident, tysym.Name())
|
||||||
|
}
|
||||||
|
ctx.Types[node] = tysym
|
||||||
|
|
||||||
|
return tysym
|
||||||
|
}
|
||||||
|
|
||||||
|
// RequireVariableType fetches the non-nil registered type for a given variable.
|
||||||
|
func (ctx *Context) RequireVariableType(node ast.Variable) symbols.Type {
|
||||||
|
return ctx.RequireType(node)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterVariableType understands how to turn any variable node into a type, and adds it to the type table. This
|
||||||
|
// works for any kind of variable-like AST node: module property, class property, parameter, or local variable.
|
||||||
|
func (ctx *Context) RegisterVariableType(node ast.Variable) symbols.Type {
|
||||||
|
var tysym symbols.Type
|
||||||
|
|
||||||
|
// If there is an explicit node type, use it.
|
||||||
|
nt := node.GetType()
|
||||||
|
if nt != nil {
|
||||||
|
tysym = ctx.Scope.LookupType(nt.Tok)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, either there was no type, or the lookup failed (leaving behind an error); use the any type.
|
||||||
|
if tysym == nil {
|
||||||
|
tysym = types.Any
|
||||||
|
}
|
||||||
|
|
||||||
|
if glog.V(7) {
|
||||||
|
glog.V(7).Infof("Registered variable type: '%v' => %v", node.GetName().Ident, tysym.Name())
|
||||||
|
}
|
||||||
|
ctx.Types[node] = tysym
|
||||||
|
|
||||||
|
return tysym
|
||||||
|
}
|
|
@ -10,14 +10,14 @@ import (
|
||||||
// bindFunctionBody binds a function body, including a scope, its parameters, and its expressions and statements.
|
// bindFunctionBody binds a function body, including a scope, its parameters, and its expressions and statements.
|
||||||
func (b *binder) bindFunctionBody(node ast.Function) {
|
func (b *binder) bindFunctionBody(node ast.Function) {
|
||||||
// Enter a new scope, bind the parameters, and then bind the body using a visitor.
|
// Enter a new scope, bind the parameters, and then bind the body using a visitor.
|
||||||
scope := b.scope.Push()
|
scope := b.ctx.Scope.Push()
|
||||||
defer scope.Pop()
|
defer scope.Pop()
|
||||||
params := node.GetParameters()
|
params := node.GetParameters()
|
||||||
if params != nil {
|
if params != nil {
|
||||||
for _, param := range *params {
|
for _, param := range *params {
|
||||||
// Register this variable's type and associate its name with the identifier.
|
// Register this variable's type and associate its name with the identifier.
|
||||||
b.registerVariableType(param)
|
b.ctx.RegisterVariableType(param)
|
||||||
b.scope.TryRegister(param, symbols.NewLocalVariableSym(param))
|
b.ctx.Scope.TryRegister(param, symbols.NewLocalVariableSym(param))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -71,37 +71,37 @@ func (b *binder) ValidateStack(stack *ast.Stack) {
|
||||||
|
|
||||||
// LookupService binds a name to a Service type.
|
// LookupService binds a name to a Service type.
|
||||||
func (b *binder) LookupService(nm tokens.Name) (*ast.Service, bool) {
|
func (b *binder) LookupService(nm tokens.Name) (*ast.Service, bool) {
|
||||||
contract.Assertf(b.scope != nil, "Unexpected empty binding scope during LookupService")
|
contract.Assertf(b.ctx.Scope != nil, "Unexpected empty binding scope during LookupService")
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// LookupStack binds a name to a Stack type.
|
// LookupStack binds a name to a Stack type.
|
||||||
func (b *binder) LookupStack(nm tokens.PackageName) (*ast.Stack, bool) {
|
func (b *binder) LookupStack(nm tokens.PackageName) (*ast.Stack, bool) {
|
||||||
contract.Assertf(b.scope != nil, "Unexpected empty binding scope during LookupStack")
|
contract.Assertf(b.ctx.Scope != nil, "Unexpected empty binding scope during LookupStack")
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// LookupUninstStack binds a name to a UninstStack type.
|
// LookupUninstStack binds a name to a UninstStack type.
|
||||||
func (b *binder) LookupUninstStack(nm tokens.PackageName) (*ast.UninstStack, bool) {
|
func (b *binder) LookupUninstStack(nm tokens.PackageName) (*ast.UninstStack, bool) {
|
||||||
contract.Assertf(b.scope != nil, "Unexpected empty binding scope during LookupUninstStack")
|
contract.Assertf(b.ctx.Scope != nil, "Unexpected empty binding scope during LookupUninstStack")
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// LookupSchema binds a name to a Schema type.
|
// LookupSchema binds a name to a Schema type.
|
||||||
func (b *binder) LookupSchema(nm tokens.PackageName) (*ast.Schema, bool) {
|
func (b *binder) LookupSchema(nm tokens.PackageName) (*ast.Schema, bool) {
|
||||||
contract.Assertf(b.scope != nil, "Unexpected empty binding scope during LookupSchema")
|
contract.Assertf(b.ctx.Scope != nil, "Unexpected empty binding scope during LookupSchema")
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// LookupSymbol binds a name to any kind of Symbol.
|
// LookupSymbol binds a name to any kind of Symbol.
|
||||||
func (b *binder) LookupSymbol(nm tokens.Name) (*legacy.Symbol, bool) {
|
func (b *binder) LookupSymbol(nm tokens.Name) (*legacy.Symbol, bool) {
|
||||||
contract.Assertf(b.scope != nil, "Unexpected empty binding scope during LookupSymbol")
|
contract.Assertf(b.ctx.Scope != nil, "Unexpected empty binding scope during LookupSymbol")
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegisterSymbol registers a symbol with the given name; if it already exists, the function returns false.
|
// RegisterSymbol registers a symbol with the given name; if it already exists, the function returns false.
|
||||||
func (b *binder) RegisterSymbol(sym *legacy.Symbol) bool {
|
func (b *binder) RegisterSymbol(sym *legacy.Symbol) bool {
|
||||||
contract.Assertf(b.scope != nil, "Unexpected empty binding scope during RegisterSymbol")
|
contract.Assertf(b.ctx.Scope != nil, "Unexpected empty binding scope during RegisterSymbol")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,7 +150,6 @@ func (p *binderPreparePhase) VisitStack(stack *ast.Stack) {
|
||||||
|
|
||||||
// Stack names are required.
|
// Stack names are required.
|
||||||
if stack.Name == "" {
|
if stack.Name == "" {
|
||||||
p.Diag().Errorf(errors.ErrorMissingStackName.At(stack))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stack versions must be valid semantic versions (and specifically, not ranges). In other words, we need
|
// Stack versions must be valid semantic versions (and specifically, not ranges). In other words, we need
|
||||||
|
@ -348,7 +347,6 @@ func (p *binderBindPhase) VisitService(pstack *ast.Stack, parent *ast.Services,
|
||||||
|
|
||||||
// A service cannot instantiate an abstract stack.
|
// A service cannot instantiate an abstract stack.
|
||||||
if svc.BoundType != nil && svc.BoundType.Abstract {
|
if svc.BoundType != nil && svc.BoundType.Abstract {
|
||||||
p.Diag().Errorf(errors.ErrorCannotCreateAbstractStack.At(pstack), svc.Name, svc.BoundType.Name)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ func (b *binder) bindModule(node *ast.Module, parent *symbols.Package) *symbols.
|
||||||
// First bind all imports to concrete symbols. These will be used to perform initialization later on.
|
// First bind all imports to concrete symbols. These will be used to perform initialization later on.
|
||||||
if node.Imports != nil {
|
if node.Imports != nil {
|
||||||
for _, imptok := range *node.Imports {
|
for _, imptok := range *node.Imports {
|
||||||
if imp := b.scope.LookupModule(imptok.Tok); imp != nil {
|
if imp := b.ctx.Scope.LookupModule(imptok.Tok); imp != nil {
|
||||||
module.Imports = append(module.Imports, imp)
|
module.Imports = append(module.Imports, imp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -62,7 +62,7 @@ func (b *binder) bindExport(node *ast.Export, parent *symbols.Module) *symbols.E
|
||||||
glog.V(3).Infof("Binding module '%v' export '%v'", parent.Name(), node.Name.Ident)
|
glog.V(3).Infof("Binding module '%v' export '%v'", parent.Name(), node.Name.Ident)
|
||||||
|
|
||||||
// To bind an export, simply look up the referent symbol and associate this name with it.
|
// To bind an export, simply look up the referent symbol and associate this name with it.
|
||||||
refsym := b.scope.Lookup(node.Referent.Tok)
|
refsym := b.ctx.Scope.Lookup(node.Referent.Tok)
|
||||||
if refsym == nil {
|
if refsym == nil {
|
||||||
// TODO: issue a verification error; name not found! Also sub in a "bad" symbol.
|
// TODO: issue a verification error; name not found! Also sub in a "bad" symbol.
|
||||||
contract.Failf("Export name not found: %v", node.Referent)
|
contract.Failf("Export name not found: %v", node.Referent)
|
||||||
|
@ -74,7 +74,7 @@ func (b *binder) bindModuleProperty(node *ast.ModuleProperty, parent *symbols.Mo
|
||||||
glog.V(3).Infof("Binding module '%v' property '%v'", parent.Name(), node.Name.Ident)
|
glog.V(3).Infof("Binding module '%v' property '%v'", parent.Name(), node.Name.Ident)
|
||||||
|
|
||||||
// Look up this node's type and inject it into the type table.
|
// Look up this node's type and inject it into the type table.
|
||||||
ty := b.registerVariableType(node)
|
ty := b.ctx.RegisterVariableType(node)
|
||||||
return symbols.NewModulePropertySym(node, parent, ty)
|
return symbols.NewModulePropertySym(node, parent, ty)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,7 +82,7 @@ func (b *binder) bindModuleMethod(node *ast.ModuleMethod, parent *symbols.Module
|
||||||
glog.V(3).Infof("Binding module '%v' method '%v'", parent.Name(), node.Name.Ident)
|
glog.V(3).Infof("Binding module '%v' method '%v'", parent.Name(), node.Name.Ident)
|
||||||
|
|
||||||
// Make a function type out of this method and inject it into the type table.
|
// Make a function type out of this method and inject it into the type table.
|
||||||
ty := b.registerFunctionType(node)
|
ty := b.ctx.RegisterFunctionType(node)
|
||||||
|
|
||||||
// Note that we don't actually bind the body of this method yet. Until we have gone ahead and injected *all*
|
// Note that we don't actually bind the body of this method yet. Until we have gone ahead and injected *all*
|
||||||
// top-level symbols into the type table, we would potentially encounter missing intra-module symbols.
|
// top-level symbols into the type table, we would potentially encounter missing intra-module symbols.
|
||||||
|
|
|
@ -65,13 +65,13 @@ func (a *astBinder) After(node ast.Node) {
|
||||||
|
|
||||||
// Expressions
|
// Expressions
|
||||||
case *ast.NullLiteral:
|
case *ast.NullLiteral:
|
||||||
a.b.registerExprType(n, types.Null) // register a null type.
|
a.b.ctx.RegisterExprType(n, types.Null) // register a null type.
|
||||||
case *ast.BoolLiteral:
|
case *ast.BoolLiteral:
|
||||||
a.b.registerExprType(n, types.Bool) // register a bool type.
|
a.b.ctx.RegisterExprType(n, types.Bool) // register a bool type.
|
||||||
case *ast.NumberLiteral:
|
case *ast.NumberLiteral:
|
||||||
a.b.registerExprType(n, types.Number) // register as a number type.
|
a.b.ctx.RegisterExprType(n, types.Number) // register as a number type.
|
||||||
case *ast.StringLiteral:
|
case *ast.StringLiteral:
|
||||||
a.b.registerExprType(n, types.String) // register as a string type.
|
a.b.ctx.RegisterExprType(n, types.String) // register as a string type.
|
||||||
case *ast.ArrayLiteral:
|
case *ast.ArrayLiteral:
|
||||||
a.checkArrayLiteral(n)
|
a.checkArrayLiteral(n)
|
||||||
case *ast.ObjectLiteral:
|
case *ast.ObjectLiteral:
|
||||||
|
@ -79,7 +79,7 @@ func (a *astBinder) After(node ast.Node) {
|
||||||
case *ast.LoadLocationExpression:
|
case *ast.LoadLocationExpression:
|
||||||
a.checkLoadLocationExpression(n)
|
a.checkLoadLocationExpression(n)
|
||||||
case *ast.LoadDynamicExpression:
|
case *ast.LoadDynamicExpression:
|
||||||
a.b.registerExprType(n, types.Any) // register as an any type.
|
a.b.ctx.RegisterExprType(n, types.Any) // register as an any type.
|
||||||
case *ast.NewExpression:
|
case *ast.NewExpression:
|
||||||
a.checkNewExpression(n)
|
a.checkNewExpression(n)
|
||||||
case *ast.InvokeFunctionExpression:
|
case *ast.InvokeFunctionExpression:
|
||||||
|
@ -102,19 +102,19 @@ func (a *astBinder) After(node ast.Node) {
|
||||||
|
|
||||||
// Ensure that all expression types resulted in a type registration.
|
// Ensure that all expression types resulted in a type registration.
|
||||||
expr, isExpr := node.(ast.Expression)
|
expr, isExpr := node.(ast.Expression)
|
||||||
contract.Assert(!isExpr || a.b.requireExprType(expr) != nil)
|
contract.Assert(!isExpr || a.b.ctx.RequireExprType(expr) != nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Statements
|
// Statements
|
||||||
|
|
||||||
func (a *astBinder) visitBlock(node *ast.Block) {
|
func (a *astBinder) visitBlock(node *ast.Block) {
|
||||||
// Entering a new block requires a fresh lexical scope.
|
// Entering a new block requires a fresh lexical scope.
|
||||||
a.b.scope.Push()
|
a.b.ctx.Scope.Push()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *astBinder) checkBlock(node *ast.Block) {
|
func (a *astBinder) checkBlock(node *ast.Block) {
|
||||||
// Exiting a block restores the prior lexical context.
|
// Exiting a block restores the prior lexical context.
|
||||||
a.b.scope.Pop()
|
a.b.ctx.Scope.Pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *astBinder) checkBreakStatement(node *ast.BreakStatement) {
|
func (a *astBinder) checkBreakStatement(node *ast.BreakStatement) {
|
||||||
|
@ -144,8 +144,8 @@ func (a *astBinder) checkIfStatement(node *ast.IfStatement) {
|
||||||
|
|
||||||
func (a *astBinder) visitLocalVariable(node *ast.LocalVariable) {
|
func (a *astBinder) visitLocalVariable(node *ast.LocalVariable) {
|
||||||
// Encountering a new local variable results in registering it; both to the type and symbol table.
|
// Encountering a new local variable results in registering it; both to the type and symbol table.
|
||||||
a.b.registerVariableType(node)
|
a.b.ctx.RegisterVariableType(node)
|
||||||
a.b.scope.TryRegister(node, symbols.NewLocalVariableSym(node))
|
a.b.ctx.Scope.TryRegister(node, symbols.NewLocalVariableSym(node))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *astBinder) visitLabeledStatement(node *ast.LabeledStatement) {
|
func (a *astBinder) visitLabeledStatement(node *ast.LabeledStatement) {
|
||||||
|
@ -159,7 +159,7 @@ func (a *astBinder) visitLabeledStatement(node *ast.LabeledStatement) {
|
||||||
|
|
||||||
func (a *astBinder) checkReturnStatement(node *ast.ReturnStatement) {
|
func (a *astBinder) checkReturnStatement(node *ast.ReturnStatement) {
|
||||||
// Ensure that the return expression is correct (present or missing; and its type).
|
// Ensure that the return expression is correct (present or missing; and its type).
|
||||||
fncty := a.b.requireFunctionType(a.fnc)
|
fncty := a.b.ctx.RequireFunctionType(a.fnc)
|
||||||
if fncty.Return == nil {
|
if fncty.Return == nil {
|
||||||
if node.Expression != nil {
|
if node.Expression != nil {
|
||||||
// The function has no return type ("void"), and yet the return had an expression.
|
// The function has no return type ("void"), and yet the return had an expression.
|
||||||
|
@ -188,7 +188,7 @@ func (a *astBinder) checkWhileStatement(node *ast.WhileStatement) {
|
||||||
// Expressions
|
// Expressions
|
||||||
|
|
||||||
func (a *astBinder) checkExprType(expr ast.Expression, expect symbols.Type) bool {
|
func (a *astBinder) checkExprType(expr ast.Expression, expect symbols.Type) bool {
|
||||||
actual := a.b.requireExprType(expr)
|
actual := a.b.ctx.RequireExprType(expr)
|
||||||
if !types.CanConvert(actual, expect) {
|
if !types.CanConvert(actual, expect) {
|
||||||
a.b.Diag().Errorf(errors.ErrorIncorrectExprType.At(expr), expect, actual)
|
a.b.Diag().Errorf(errors.ErrorIncorrectExprType.At(expr), expect, actual)
|
||||||
return false
|
return false
|
||||||
|
@ -203,10 +203,10 @@ func (a *astBinder) checkArrayLiteral(node *ast.ArrayLiteral) {
|
||||||
}
|
}
|
||||||
// Now mark the resulting expression as an array of the right type.
|
// Now mark the resulting expression as an array of the right type.
|
||||||
if node.ElemType == nil {
|
if node.ElemType == nil {
|
||||||
a.b.registerExprType(node, types.AnyArray)
|
a.b.ctx.RegisterExprType(node, types.AnyArray)
|
||||||
} else {
|
} else {
|
||||||
elemType := a.b.bindType(node.ElemType)
|
elemType := a.b.bindType(node.ElemType)
|
||||||
a.b.registerExprType(node, symbols.NewArrayType(elemType))
|
a.b.ctx.RegisterExprType(node, symbols.NewArrayType(elemType))
|
||||||
|
|
||||||
// Ensure the elements, if any, are of the right type.
|
// Ensure the elements, if any, are of the right type.
|
||||||
if node.Elements != nil {
|
if node.Elements != nil {
|
||||||
|
@ -220,10 +220,10 @@ func (a *astBinder) checkArrayLiteral(node *ast.ArrayLiteral) {
|
||||||
func (a *astBinder) checkObjectLiteral(node *ast.ObjectLiteral) {
|
func (a *astBinder) checkObjectLiteral(node *ast.ObjectLiteral) {
|
||||||
// Mark the resulting object literal with the correct type.
|
// Mark the resulting object literal with the correct type.
|
||||||
if node.Type == nil {
|
if node.Type == nil {
|
||||||
a.b.registerExprType(node, types.Any)
|
a.b.ctx.RegisterExprType(node, types.Any)
|
||||||
} else {
|
} else {
|
||||||
ty := a.b.bindType(node.Type)
|
ty := a.b.bindType(node.Type)
|
||||||
a.b.registerExprType(node, ty)
|
a.b.ctx.RegisterExprType(node, ty)
|
||||||
|
|
||||||
// Only permit object literals for records and interfaces. Classes have constructors.
|
// Only permit object literals for records and interfaces. Classes have constructors.
|
||||||
if !ty.Record() && !ty.Interface() {
|
if !ty.Record() && !ty.Interface() {
|
||||||
|
@ -268,7 +268,7 @@ func (a *astBinder) checkLoadLocationExpression(node *ast.LoadLocationExpression
|
||||||
sym = a.b.requireToken(node.Name, node.Name.Tok)
|
sym = a.b.requireToken(node.Name, node.Name.Tok)
|
||||||
} else {
|
} else {
|
||||||
// If there's an object, we are accessing a class member property or function.
|
// If there's an object, we are accessing a class member property or function.
|
||||||
typ := a.b.requireExprType(*node.Object)
|
typ := a.b.ctx.RequireExprType(*node.Object)
|
||||||
sym = a.b.requireClassMember(node.Name, typ, tokens.ClassMember(node.Name.Tok))
|
sym = a.b.requireClassMember(node.Name, typ, tokens.ClassMember(node.Name.Tok))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -279,16 +279,16 @@ func (a *astBinder) checkLoadLocationExpression(node *ast.LoadLocationExpression
|
||||||
} else {
|
} else {
|
||||||
switch s := sym.(type) {
|
switch s := sym.(type) {
|
||||||
case ast.Function:
|
case ast.Function:
|
||||||
ty = a.b.requireFunctionType(s)
|
ty = a.b.ctx.RequireFunctionType(s)
|
||||||
case ast.Variable:
|
case ast.Variable:
|
||||||
ty = a.b.requireVariableType(s)
|
ty = a.b.ctx.RequireVariableType(s)
|
||||||
default:
|
default:
|
||||||
contract.Failf("Unrecognized load location symbol type: %v", sym.Token())
|
contract.Failf("Unrecognized load location symbol type: %v", sym.Token())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register a pointer type so that this expression is a valid l-expr.
|
// Register a pointer type so that this expression is a valid l-expr.
|
||||||
a.b.registerExprType(node, symbols.NewPointerType(ty))
|
a.b.ctx.RegisterExprType(node, symbols.NewPointerType(ty))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *astBinder) checkNewExpression(node *ast.NewExpression) {
|
func (a *astBinder) checkNewExpression(node *ast.NewExpression) {
|
||||||
|
@ -306,7 +306,7 @@ func (a *astBinder) checkNewExpression(node *ast.NewExpression) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
a.b.registerExprType(node, ty)
|
a.b.ctx.RegisterExprType(node, ty)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *astBinder) checkInvokeFunctionExpression(node *ast.InvokeFunctionExpression) {
|
func (a *astBinder) checkInvokeFunctionExpression(node *ast.InvokeFunctionExpression) {
|
||||||
|
@ -319,14 +319,14 @@ func (a *astBinder) checkLambdaExpression(node *ast.LambdaExpression) {
|
||||||
var params []symbols.Type
|
var params []symbols.Type
|
||||||
if pparams := node.GetParameters(); pparams != nil {
|
if pparams := node.GetParameters(); pparams != nil {
|
||||||
for _, param := range *pparams {
|
for _, param := range *pparams {
|
||||||
params = append(params, a.b.requireVariableType(param))
|
params = append(params, a.b.ctx.RequireVariableType(param))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var ret symbols.Type
|
var ret symbols.Type
|
||||||
if pret := node.GetReturnType(); pret != nil {
|
if pret := node.GetReturnType(); pret != nil {
|
||||||
ret = a.b.bindType(pret)
|
ret = a.b.bindType(pret)
|
||||||
}
|
}
|
||||||
a.b.registerExprType(node, symbols.NewFunctionType(params, ret))
|
a.b.ctx.RegisterExprType(node, symbols.NewFunctionType(params, ret))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *astBinder) checkUnaryOperatorExpression(node *ast.UnaryOperatorExpression) {
|
func (a *astBinder) checkUnaryOperatorExpression(node *ast.UnaryOperatorExpression) {
|
||||||
|
@ -341,7 +341,7 @@ func (a *astBinder) checkBinaryOperatorExpression(node *ast.BinaryOperatorExpres
|
||||||
|
|
||||||
func (a *astBinder) checkCastExpression(node *ast.CastExpression) {
|
func (a *astBinder) checkCastExpression(node *ast.CastExpression) {
|
||||||
// TODO: validate that this is legal.
|
// TODO: validate that this is legal.
|
||||||
a.b.registerExprType(node, a.b.bindType(node.Type))
|
a.b.ctx.RegisterExprType(node, a.b.bindType(node.Type))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *astBinder) checkTypeOfExpression(node *ast.TypeOfExpression) {
|
func (a *astBinder) checkTypeOfExpression(node *ast.TypeOfExpression) {
|
||||||
|
@ -355,5 +355,5 @@ func (a *astBinder) checkConditionalExpression(node *ast.ConditionalExpression)
|
||||||
func (a *astBinder) checkSequenceExpression(node *ast.SequenceExpression) {
|
func (a *astBinder) checkSequenceExpression(node *ast.SequenceExpression) {
|
||||||
// The type of a sequence expression is just the type of the last expression in the sequence.
|
// The type of a sequence expression is just the type of the last expression in the sequence.
|
||||||
// TODO: check that there's at least one!
|
// TODO: check that there's at least one!
|
||||||
a.b.registerExprType(node, a.b.requireExprType(node.Expressions[len(node.Expressions)-1]))
|
a.b.ctx.RegisterExprType(node, a.b.ctx.RequireExprType(node.Expressions[len(node.Expressions)-1]))
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue