diff options
-rw-r--r-- | internal/compile/compile.go | 84 | ||||
-rw-r--r-- | internal/compile/serial.go | 50 | ||||
-rw-r--r-- | resolve/resolve.go | 148 | ||||
-rw-r--r-- | starlark/eval.go | 2 | ||||
-rw-r--r-- | syntax/binding.go | 43 | ||||
-rw-r--r-- | syntax/syntax.go | 20 |
6 files changed, 187 insertions, 160 deletions
diff --git a/internal/compile/compile.go b/internal/compile/compile.go index 1aee10c..6d98fc1 100644 --- a/internal/compile/compile.go +++ b/internal/compile/compile.go @@ -30,7 +30,6 @@ import ( "path/filepath" "strconv" - "go.starlark.net/resolve" "go.starlark.net/syntax" ) @@ -289,12 +288,12 @@ func (op Opcode) String() string { // Programs are serialized by the gobProgram function, // which must be updated whenever this declaration is changed. type Program struct { - Loads []Ident // name (really, string) and position of each load stmt + Loads []Binding // name (really, string) and position of each load stmt Names []string // names of attributes and predeclared variables Constants []interface{} // = string | int64 | float64 | *big.Int Functions []*Funcode - Globals []Ident // for error messages and tracing - Toplevel *Funcode // module initialization function + Globals []Binding // for error messages and tracing + Toplevel *Funcode // module initialization function } // A Funcode is the code of a compiled Starlark function. @@ -308,16 +307,16 @@ type Funcode struct { Doc string // docstring of this function Code []byte // the byte code pclinetab []uint16 // mapping from pc to linenum - Locals []Ident // locals, parameters first - Freevars []Ident // for tracing + Locals []Binding // locals, parameters first + Freevars []Binding // for tracing MaxStack int NumParams int NumKwonlyParams int HasVarargs, HasKwargs bool } -// An Ident is the name and position of an identifier. -type Ident struct { +// A Binding is the name and position of a binding identifier. +type Binding struct { Name string Pos syntax.Position } @@ -401,28 +400,28 @@ func (fn *Funcode) Position(pc uint32) syntax.Position { return pos } -// idents convert syntactic identifiers to compiled form. -func idents(ids []*syntax.Ident) []Ident { - res := make([]Ident, len(ids)) - for i, id := range ids { - res[i].Name = id.Name - res[i].Pos = id.NamePos +// bindings converts syntax.Bindings to compiled form. +func bindings(bindings []*syntax.Binding) []Binding { + res := make([]Binding, len(bindings)) + for i, bind := range bindings { + res[i].Name = bind.First.Name + res[i].Pos = bind.First.NamePos } return res } // Expr compiles an expression to a program consisting of a single toplevel function. -func Expr(expr syntax.Expr, name string, locals []*syntax.Ident) *Funcode { +func Expr(expr syntax.Expr, name string, locals []*syntax.Binding) *Funcode { pos := syntax.Start(expr) stmts := []syntax.Stmt{&syntax.ReturnStmt{Result: expr}} return File(stmts, pos, name, locals, nil).Toplevel } // File compiles the statements of a file into a program. -func File(stmts []syntax.Stmt, pos syntax.Position, name string, locals, globals []*syntax.Ident) *Program { +func File(stmts []syntax.Stmt, pos syntax.Position, name string, locals, globals []*syntax.Binding) *Program { pcomp := &pcomp{ prog: &Program{ - Globals: idents(globals), + Globals: bindings(globals), }, names: make(map[string]uint32), constants: make(map[interface{}]uint32), @@ -433,7 +432,7 @@ func File(stmts []syntax.Stmt, pos syntax.Position, name string, locals, globals return pcomp.prog } -func (pcomp *pcomp) function(name string, pos syntax.Position, stmts []syntax.Stmt, locals, freevars []*syntax.Ident) *Funcode { +func (pcomp *pcomp) function(name string, pos syntax.Position, stmts []syntax.Stmt, locals, freevars []*syntax.Binding) *Funcode { fcomp := &fcomp{ pcomp: pcomp, pos: pos, @@ -442,8 +441,8 @@ func (pcomp *pcomp) function(name string, pos syntax.Position, stmts []syntax.St Pos: pos, Name: name, Doc: docStringFromBody(stmts), - Locals: idents(locals), - Freevars: idents(freevars), + Locals: bindings(locals), + Freevars: bindings(freevars), }, } @@ -898,34 +897,36 @@ func (fcomp *fcomp) setPos(pos syntax.Position) { // set emits code to store the top-of-stack value // to the specified local or global variable. func (fcomp *fcomp) set(id *syntax.Ident) { - switch resolve.Scope(id.Scope) { - case resolve.Local: - fcomp.emit1(SETLOCAL, uint32(id.Index)) - case resolve.Global: - fcomp.emit1(SETGLOBAL, uint32(id.Index)) + bind := id.Binding + switch bind.Scope { + case syntax.LocalScope: + fcomp.emit1(SETLOCAL, uint32(bind.Index)) + case syntax.GlobalScope: + fcomp.emit1(SETGLOBAL, uint32(bind.Index)) default: - log.Fatalf("%s: set(%s): neither global nor local (%d)", id.NamePos, id.Name, id.Scope) + log.Fatalf("%s: set(%s): neither global nor local (%d)", id.NamePos, id.Name, bind.Scope) } } // lookup emits code to push the value of the specified variable. func (fcomp *fcomp) lookup(id *syntax.Ident) { - switch resolve.Scope(id.Scope) { - case resolve.Local: + bind := id.Binding + switch bind.Scope { + case syntax.LocalScope: fcomp.setPos(id.NamePos) - fcomp.emit1(LOCAL, uint32(id.Index)) - case resolve.Free: - fcomp.emit1(FREE, uint32(id.Index)) - case resolve.Global: + fcomp.emit1(LOCAL, uint32(bind.Index)) + case syntax.FreeScope: + fcomp.emit1(FREE, uint32(bind.Index)) + case syntax.GlobalScope: fcomp.setPos(id.NamePos) - fcomp.emit1(GLOBAL, uint32(id.Index)) - case resolve.Predeclared: + fcomp.emit1(GLOBAL, uint32(bind.Index)) + case syntax.PredeclaredScope: fcomp.setPos(id.NamePos) fcomp.emit1(PREDECLARED, fcomp.pcomp.nameIndex(id.Name)) - case resolve.Universal: + case syntax.UniversalScope: fcomp.emit1(UNIVERSAL, fcomp.pcomp.nameIndex(id.Name)) default: - log.Fatalf("%s: compiler.lookup(%s): scope = %d", id.NamePos, id.Name, id.Scope) + log.Fatalf("%s: compiler.lookup(%s): scope = %d", id.NamePos, id.Name, bind.Scope) } } @@ -1108,7 +1109,7 @@ func (fcomp *fcomp) stmt(stmt syntax.Stmt) { fcomp.string(stmt.From[i].Name) } module := stmt.Module.Value.(string) - fcomp.pcomp.prog.Loads = append(fcomp.pcomp.prog.Loads, Ident{ + fcomp.pcomp.prog.Loads = append(fcomp.pcomp.prog.Loads, Binding{ Name: module, Pos: stmt.Module.TokenPos, }) @@ -1116,7 +1117,7 @@ func (fcomp *fcomp) stmt(stmt syntax.Stmt) { fcomp.setPos(stmt.Load) fcomp.emit1(LOAD, uint32(len(stmt.From))) for i := range stmt.To { - fcomp.emit1(SETGLOBAL, uint32(stmt.To[len(stmt.To)-1-i].Index)) + fcomp.emit1(SETGLOBAL, uint32(stmt.To[len(stmt.To)-1-i].Binding.Index)) } default: @@ -1708,7 +1709,12 @@ func (fcomp *fcomp) function(pos syntax.Position, name string, f *syntax.Functio // Capture the values of the function's // free variables from the lexical environment. for _, freevar := range f.FreeVars { - fcomp.lookup(freevar) + switch freevar.Scope { + case syntax.LocalScope: + fcomp.emit1(LOCAL, uint32(freevar.Index)) + case syntax.FreeScope: + fcomp.emit1(FREE, uint32(freevar.Index)) + } } fcomp.emit1(MAKETUPLE, uint32(len(f.FreeVars))) diff --git a/internal/compile/serial.go b/internal/compile/serial.go index 6056ea7..b3f986f 100644 --- a/internal/compile/serial.go +++ b/internal/compile/serial.go @@ -96,7 +96,7 @@ func (prog *Program) Encode() []byte { e.p = append(e.p, "????"...) // string data offset; filled in later e.int(Version) e.string(prog.Toplevel.Pos.Filename()) - e.idents(prog.Loads) + e.bindings(prog.Loads) e.int(len(prog.Names)) for _, name := range prog.Names { e.string(name) @@ -118,7 +118,7 @@ func (prog *Program) Encode() []byte { e.string(c.Text(10)) } } - e.idents(prog.Globals) + e.bindings(prog.Globals) e.function(prog.Toplevel) e.int(len(prog.Functions)) for _, fn := range prog.Functions { @@ -161,29 +161,29 @@ func (e *encoder) bytes(b []byte) { e.s = append(e.s, b...) } -func (e *encoder) ident(id Ident) { - e.string(id.Name) - e.int(int(id.Pos.Line)) - e.int(int(id.Pos.Col)) +func (e *encoder) binding(bind Binding) { + e.string(bind.Name) + e.int(int(bind.Pos.Line)) + e.int(int(bind.Pos.Col)) } -func (e *encoder) idents(ids []Ident) { - e.int(len(ids)) - for _, id := range ids { - e.ident(id) +func (e *encoder) bindings(binds []Binding) { + e.int(len(binds)) + for _, bind := range binds { + e.binding(bind) } } func (e *encoder) function(fn *Funcode) { - e.ident(Ident{fn.Name, fn.Pos}) + e.binding(Binding{fn.Name, fn.Pos}) e.string(fn.Doc) e.bytes(fn.Code) e.int(len(fn.pclinetab)) for _, x := range fn.pclinetab { e.int64(int64(x)) } - e.idents(fn.Locals) - e.idents(fn.Freevars) + e.bindings(fn.Locals) + e.bindings(fn.Freevars) e.int(fn.MaxStack) e.int(fn.NumParams) e.int(fn.NumKwonlyParams) @@ -228,7 +228,7 @@ func DecodeProgram(data []byte) (_ *Program, err error) { filename := d.string() d.filename = &filename - loads := d.idents() + loads := d.bindings() names := make([]string, d.int()) for i := range names { @@ -252,7 +252,7 @@ func DecodeProgram(data []byte) (_ *Program, err error) { constants[i] = c } - globals := d.idents() + globals := d.bindings() toplevel := d.function() funcs := make([]*Funcode, d.int()) for i := range funcs { @@ -323,33 +323,33 @@ func (d *decoder) bytes() []byte { return r } -func (d *decoder) ident() Ident { +func (d *decoder) binding() Binding { name := d.string() line := int32(d.int()) col := int32(d.int()) - return Ident{Name: name, Pos: syntax.MakePosition(d.filename, line, col)} + return Binding{Name: name, Pos: syntax.MakePosition(d.filename, line, col)} } -func (d *decoder) idents() []Ident { - idents := make([]Ident, d.int()) - for i := range idents { - idents[i] = d.ident() +func (d *decoder) bindings() []Binding { + bindings := make([]Binding, d.int()) + for i := range bindings { + bindings[i] = d.binding() } - return idents + return bindings } func (d *decoder) bool() bool { return d.int() != 0 } func (d *decoder) function() *Funcode { - id := d.ident() + id := d.binding() doc := d.string() code := d.bytes() pclinetab := make([]uint16, d.int()) for i := range pclinetab { pclinetab[i] = uint16(d.int()) } - locals := d.idents() - freevars := d.idents() + locals := d.bindings() + freevars := d.bindings() maxStack := d.int() numParams := d.int() numKwonlyParams := d.int() diff --git a/resolve/resolve.go b/resolve/resolve.go index a519006..9f73008 100644 --- a/resolve/resolve.go +++ b/resolve/resolve.go @@ -137,7 +137,7 @@ func File(file *syntax.File, isPredeclared, isUniversal func(name string) bool) // It returns the local variables bound within the expression. // // The isPredeclared and isUniversal predicates behave as for the File function. -func Expr(expr syntax.Expr, isPredeclared, isUniversal func(name string) bool) ([]*syntax.Ident, error) { +func Expr(expr syntax.Expr, isPredeclared, isUniversal func(name string) bool) ([]*syntax.Binding, error) { r := newResolver(isPredeclared, isUniversal) r.expr(expr) r.env.resolveLocalUses() @@ -161,35 +161,13 @@ type Error struct { func (e Error) Error() string { return e.Pos.String() + ": " + e.Msg } -// The Scope of a syntax.Ident indicates what kind of scope it has. -type Scope uint8 - -const ( - Undefined Scope = iota // name is not defined - Local // name is local to its function - Free // name is local to some enclosing function - Global // name is global to module - Predeclared // name is predeclared for this module (e.g. glob) - Universal // name is universal (e.g. len) -) - -var scopeNames = [...]string{ - Undefined: "undefined", - Local: "local", - Free: "free", - Global: "global", - Predeclared: "predeclared", - Universal: "universal", -} - -func (scope Scope) String() string { return scopeNames[scope] } - func newResolver(isPredeclared, isUniversal func(name string) bool) *resolver { return &resolver{ env: new(block), // module block isPredeclared: isPredeclared, isUniversal: isUniversal, - globals: make(map[string]*syntax.Ident), + globals: make(map[string]*syntax.Binding), + predeclared: make(map[string]*syntax.Binding), } } @@ -202,12 +180,13 @@ type resolver struct { // moduleLocals contains the local variables of the module // (due to comprehensions outside any function). // moduleGlobals contains the global variables of the module. - moduleLocals []*syntax.Ident - moduleGlobals []*syntax.Ident + moduleLocals []*syntax.Binding + moduleGlobals []*syntax.Binding - // globals maps each global name in the module - // to its first binding occurrence. - globals map[string]*syntax.Ident + // globals maps each global name in the module to its binding. + // predeclared does the same for predeclared and universal names. + globals map[string]*syntax.Binding + predeclared map[string]*syntax.Binding // These predicates report whether a name is // pre-declared, either in this module or universally. @@ -247,7 +226,7 @@ type block struct { // bindings maps a name to its binding. // A local binding has an index into its innermost enclosing container's locals array. // A free binding has an index into its innermost enclosing function's freevars array. - bindings map[string]binding + bindings map[string]*syntax.Binding // children records the child blocks of the current one. children []*block @@ -260,16 +239,11 @@ type block struct { uses []use } -type binding struct { - scope Scope - index int -} - func (b *block) isModule() bool { return b.parent == nil } -func (b *block) bind(name string, bind binding) { +func (b *block) bind(name string, bind *syntax.Binding) { if b.bindings == nil { - b.bindings = make(map[string]binding) + b.bindings = make(map[string]*syntax.Binding) } b.bindings[name] = bind } @@ -301,19 +275,20 @@ type use struct { func (r *resolver) bind(id *syntax.Ident) bool { // Binding outside any local (comprehension/function) block? if r.env.isModule() { - id.Scope = uint8(Global) - prev, ok := r.globals[id.Name] - if ok { - if !AllowGlobalReassign { - r.errorf(id.NamePos, "cannot reassign global %s declared at %s", id.Name, prev.NamePos) - } - id.Index = prev.Index - } else { + bind, ok := r.globals[id.Name] + if !ok { // first global binding of this name - r.globals[id.Name] = id - id.Index = len(r.moduleGlobals) - r.moduleGlobals = append(r.moduleGlobals, id) + bind = &syntax.Binding{ + First: id, + Scope: syntax.GlobalScope, + Index: len(r.moduleGlobals), + } + r.globals[id.Name] = bind + r.moduleGlobals = append(r.moduleGlobals, bind) + } else if !AllowGlobalReassign { + r.errorf(id.NamePos, "cannot reassign global %s declared at %s", id.Name, bind.First.NamePos) } + id.Binding = bind return ok } @@ -321,14 +296,19 @@ func (r *resolver) bind(id *syntax.Ident) bool { // Assign it a new local (positive) index in the current container. _, ok := r.env.bindings[id.Name] if !ok { - var locals *[]*syntax.Ident + var locals *[]*syntax.Binding if fn := r.container().function; fn != nil { locals = &fn.Locals } else { locals = &r.moduleLocals } - r.env.bind(id.Name, binding{Local, len(*locals)}) - *locals = append(*locals, id) + bind := &syntax.Binding{ + First: id, + Scope: syntax.LocalScope, + Index: len(*locals), + } + r.env.bind(id.Name, bind) + *locals = append(*locals, bind) } r.use(id) @@ -379,32 +359,34 @@ func (r *resolver) use(id *syntax.Ident) { // useGlobals resolves use.id as a reference to a global. // The use.env field captures the original environment for error reporting. -func (r *resolver) useGlobal(use use) binding { +func (r *resolver) useGlobal(use use) (bind *syntax.Binding) { id := use.id - var scope Scope if prev, ok := r.globals[id.Name]; ok { - scope = Global // use of global declared by module - id.Index = prev.Index + bind = prev // use of global declared by module + } else if prev, ok := r.predeclared[id.Name]; ok { + bind = prev // repeated use of predeclared or universal } else if r.isPredeclared(id.Name) { - scope = Predeclared // use of pre-declared + bind = &syntax.Binding{Scope: syntax.PredeclaredScope} // use of pre-declared name + r.predeclared[id.Name] = bind // save it } else if r.isUniversal(id.Name) { - scope = Universal // use of universal name if !AllowFloat && id.Name == "float" { r.errorf(id.NamePos, doesnt+"support floating point") } if !AllowSet && id.Name == "set" { r.errorf(id.NamePos, doesnt+"support sets") } + bind = &syntax.Binding{Scope: syntax.UniversalScope} // use of universal name + r.predeclared[id.Name] = bind // save it } else { - scope = Undefined + bind = &syntax.Binding{Scope: syntax.UndefinedScope} var hint string if n := r.spellcheck(use); n != "" { hint = fmt.Sprintf(" (did you mean %s?)", n) } r.errorf(id.NamePos, "undefined: %s%s", id.Name, hint) } - id.Scope = uint8(scope) - return binding{scope, id.Index} + id.Binding = bind + return bind } // spellcheck returns the most likely misspelling of @@ -423,8 +405,8 @@ func (r *resolver) spellcheck(use use) string { // // We have no way to enumerate predeclared/universe, // which includes prior names in the REPL session. - for _, id := range r.moduleGlobals { - names = append(names, id.Name) + for _, bind := range r.moduleGlobals { + names = append(names, bind.First.Name) } sort.Strings(names) @@ -436,9 +418,8 @@ func (r *resolver) spellcheck(use use) string { func (b *block) resolveLocalUses() { unresolved := b.uses[:0] for _, use := range b.uses { - if bind := lookupLocal(use); bind.scope == Local { - use.id.Scope = uint8(bind.scope) - use.id.Index = bind.index + if bind := lookupLocal(use); bind != nil && bind.Scope == syntax.LocalScope { + use.id.Binding = bind } else { unresolved = append(unresolved, use) } @@ -857,19 +838,17 @@ func (r *resolver) resolveNonLocalUses(b *block) { r.resolveNonLocalUses(child) } for _, use := range b.uses { - bind := r.lookupLexical(use, use.env) - use.id.Scope = uint8(bind.scope) - use.id.Index = bind.index + use.id.Binding = r.lookupLexical(use, use.env) } } // lookupLocal looks up an identifier within its immediately enclosing function. -func lookupLocal(use use) binding { +func lookupLocal(use use) *syntax.Binding { for env := use.env; env != nil; env = env.parent { if bind, ok := env.bindings[use.id.Name]; ok { - if bind.scope == Free { + if bind.Scope == syntax.FreeScope { // shouldn't exist till later - log.Fatalf("%s: internal error: %s, %d", use.id.NamePos, use.id.Name, bind) + log.Fatalf("%s: internal error: %s, %v", use.id.NamePos, use.id.Name, bind) } return bind // found } @@ -877,15 +856,15 @@ func lookupLocal(use use) binding { break } } - return binding{} // not found in this function + return nil // not found in this function } // lookupLexical looks up an identifier use.id within its lexically enclosing environment. // The use.env field captures the original environment for error reporting. -func (r *resolver) lookupLexical(use use, env *block) (bind binding) { +func (r *resolver) lookupLexical(use use, env *block) (bind *syntax.Binding) { if debug { fmt.Printf("lookupLexical %s in %s = ...\n", use.id.Name, env) - defer func() { fmt.Printf("= %d\n", bind) }() + defer func() { fmt.Printf("= %v\n", bind) }() } // Is this the module block? @@ -898,19 +877,20 @@ func (r *resolver) lookupLexical(use use, env *block) (bind binding) { if !ok { // Defined in parent block? bind = r.lookupLexical(use, env.parent) - if env.function != nil && (bind.scope == Local || bind.scope == Free) { + if env.function != nil && (bind.Scope == syntax.LocalScope || bind.Scope == syntax.FreeScope) { // Found in parent block, which belongs to enclosing function. - id := &syntax.Ident{ - Name: use.id.Name, - Scope: uint8(bind.scope), - Index: bind.index, + // Add the parent's binding to the function's freevars, + // and add a new 'free' binding to the inner function's block. + index := len(env.function.FreeVars) + env.function.FreeVars = append(env.function.FreeVars, bind) + bind = &syntax.Binding{ + First: bind.First, + Scope: syntax.FreeScope, + Index: index, } - bind.scope = Free - bind.index = len(env.function.FreeVars) - env.function.FreeVars = append(env.function.FreeVars, id) if debug { fmt.Printf("creating freevar %v in function at %s: %s\n", - len(env.function.FreeVars), fmt.Sprint(env.function.Span()), id.Name) + len(env.function.FreeVars), fmt.Sprint(env.function.Span()), use.id.Name) } } diff --git a/starlark/eval.go b/starlark/eval.go index 11ebbe1..045469d 100644 --- a/starlark/eval.go +++ b/starlark/eval.go @@ -1247,7 +1247,7 @@ func setArgs(locals []Value, fn *Function, args Tuple, kwargs []Tuple) error { return nil } -func findParam(params []compile.Ident, name string) int { +func findParam(params []compile.Binding, name string) int { for i, param := range params { if param.Name == name { return i diff --git a/syntax/binding.go b/syntax/binding.go new file mode 100644 index 0000000..2ed10b0 --- /dev/null +++ b/syntax/binding.go @@ -0,0 +1,43 @@ +package syntax + +// This file defines resolver data types referenced by the syntax tree. +// We cannot guarantee API stability for these types +// as they are closely tied to the implementation. + +// A Binding ties together all identifiers that denote the same variable. +// The resolver computes a binding for every Ident. +type Binding struct { + Scope Scope + + // Index records the index into the enclosing + // - {DefStmt,File}.Locals, if Scope==Local + // - DefStmt.FreeVars, if Scope==Free + // - File.Globals, if Scope==Global. + // It is zero if Scope is Predeclared, Universal, or Undefined. + Index int + + First *Ident // first binding use (iff Scope==Local/Free/Global) +} + +// The Scope of Binding indicates what kind of scope it has. +type Scope uint8 + +const ( + UndefinedScope Scope = iota // name is not defined + LocalScope // name is local to its function + FreeScope // name is local to some enclosing function + GlobalScope // name is global to module + PredeclaredScope // name is predeclared for this module (e.g. glob) + UniversalScope // name is universal (e.g. len) +) + +var scopeNames = [...]string{ + UndefinedScope: "undefined", + LocalScope: "local", + FreeScope: "free", + GlobalScope: "global", + PredeclaredScope: "predeclared", + UniversalScope: "universal", +} + +func (scope Scope) String() string { return scopeNames[scope] } diff --git a/syntax/syntax.go b/syntax/syntax.go index 4ed4066..d805540 100644 --- a/syntax/syntax.go +++ b/syntax/syntax.go @@ -71,8 +71,8 @@ type File struct { Stmts []Stmt // set by resolver: - Locals []*Ident // this file's (comprehension-)local variables - Globals []*Ident // this file's global variables + Locals []*Binding // this file's (comprehension-)local variables + Globals []*Binding // this file's global variables } func (x *File) Span() (start, end Position) { @@ -126,11 +126,11 @@ type Function struct { Body []Stmt // set by resolver: - HasVarargs bool // whether params includes *args (convenience) - HasKwargs bool // whether params includes **kwargs (convenience) - NumKwonlyParams int // number of keyword-only optional parameters - Locals []*Ident // this function's local variables, parameters first - FreeVars []*Ident // enclosing local variables to capture in closure + HasVarargs bool // whether params includes *args (convenience) + HasKwargs bool // whether params includes **kwargs (convenience) + NumKwonlyParams int // number of keyword-only optional parameters + Locals []*Binding // this function's local variables, parameters first + FreeVars []*Binding // enclosing local variables to capture in closure } func (x *Function) Span() (start, end Position) { @@ -260,10 +260,8 @@ type Ident struct { NamePos Position Name string - // set by resolver: - - Scope uint8 // see type resolve.Scope - Index int // index into enclosing {DefStmt,File}.Locals (if scope==Local) or DefStmt.FreeVars (if scope==Free) or File.Globals (if scope==Global) + // set by resolver + Binding *Binding } func (x *Ident) Span() (start, end Position) { |