aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--internal/compile/compile.go84
-rw-r--r--internal/compile/serial.go50
-rw-r--r--resolve/resolve.go148
-rw-r--r--starlark/eval.go2
-rw-r--r--syntax/binding.go43
-rw-r--r--syntax/syntax.go20
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) {