Skip to content

Commit

Permalink
resolve: add aliases for Binding and Scope (google#173)
Browse files Browse the repository at this point in the history
This restores the nice syntax prior to the refactoring.
  • Loading branch information
adonovan authored Mar 11, 2019
1 parent 3d5a061 commit 6db5e16
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 50 deletions.
37 changes: 19 additions & 18 deletions internal/compile/compile.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import (
"path/filepath"
"strconv"

"go.starlark.net/resolve"
"go.starlark.net/syntax"
)

Expand Down Expand Up @@ -411,8 +412,8 @@ func (fn *Funcode) Position(pc uint32) syntax.Position {
return pos
}

// bindings converts syntax.Bindings to compiled form.
func bindings(bindings []*syntax.Binding) []Binding {
// bindings converts resolve.Bindings to compiled form.
func bindings(bindings []*resolve.Binding) []Binding {
res := make([]Binding, len(bindings))
for i, bind := range bindings {
res[i].Name = bind.First.Name
Expand All @@ -422,14 +423,14 @@ func bindings(bindings []*syntax.Binding) []Binding {
}

// Expr compiles an expression to a program consisting of a single toplevel function.
func Expr(expr syntax.Expr, name string, locals []*syntax.Binding) *Funcode {
func Expr(expr syntax.Expr, name string, locals []*resolve.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.Binding) *Program {
func File(stmts []syntax.Stmt, pos syntax.Position, name string, locals, globals []*resolve.Binding) *Program {
pcomp := &pcomp{
prog: &Program{
Globals: bindings(globals),
Expand All @@ -443,7 +444,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.Binding) *Funcode {
func (pcomp *pcomp) function(name string, pos syntax.Position, stmts []syntax.Stmt, locals, freevars []*resolve.Binding) *Funcode {
fcomp := &fcomp{
pcomp: pcomp,
pos: pos,
Expand All @@ -459,7 +460,7 @@ func (pcomp *pcomp) function(name string, pos syntax.Position, stmts []syntax.St

// Record indices of locals that require cells.
for i, local := range locals {
if local.Scope == syntax.CellScope {
if local.Scope == resolve.Cell {
fcomp.fn.Cells = append(fcomp.fn.Cells, i)
}
}
Expand Down Expand Up @@ -917,13 +918,13 @@ func (fcomp *fcomp) setPos(pos syntax.Position) {
func (fcomp *fcomp) set(id *syntax.Ident) {
bind := id.Binding
switch bind.Scope {
case syntax.LocalScope:
case resolve.Local:
fcomp.emit1(SETLOCAL, uint32(bind.Index))
case syntax.CellScope:
case resolve.Cell:
// TODO(adonovan): opt: make a single op for LOCAL<n>, SETCELL.
fcomp.emit1(LOCAL, uint32(bind.Index))
fcomp.emit(SETCELL)
case syntax.GlobalScope:
case resolve.Global:
fcomp.emit1(SETGLOBAL, uint32(bind.Index))
default:
log.Fatalf("%s: set(%s): not global/local/cell (%d)", id.NamePos, id.Name, bind.Scope)
Expand All @@ -933,25 +934,25 @@ func (fcomp *fcomp) set(id *syntax.Ident) {
// lookup emits code to push the value of the specified variable.
func (fcomp *fcomp) lookup(id *syntax.Ident) {
bind := id.Binding
if bind.Scope != syntax.UniversalScope { // (universal lookup can't fail)
if bind.Scope != resolve.Universal { // (universal lookup can't fail)
fcomp.setPos(id.NamePos)
}
switch bind.Scope {
case syntax.LocalScope:
case resolve.Local:
fcomp.emit1(LOCAL, uint32(bind.Index))
case syntax.FreeScope:
case resolve.Free:
// TODO(adonovan): opt: make a single op for FREE<n>, CELL.
fcomp.emit1(FREE, uint32(bind.Index))
fcomp.emit(CELL)
case syntax.CellScope:
case resolve.Cell:
// TODO(adonovan): opt: make a single op for LOCAL<n>, CELL.
fcomp.emit1(LOCAL, uint32(bind.Index))
fcomp.emit(CELL)
case syntax.GlobalScope:
case resolve.Global:
fcomp.emit1(GLOBAL, uint32(bind.Index))
case syntax.PredeclaredScope:
case resolve.Predeclared:
fcomp.emit1(PREDECLARED, fcomp.pcomp.nameIndex(id.Name))
case syntax.UniversalScope:
case resolve.Universal:
fcomp.emit1(UNIVERSAL, fcomp.pcomp.nameIndex(id.Name))
default:
log.Fatalf("%s: compiler.lookup(%s): scope = %d", id.NamePos, id.Name, bind.Scope)
Expand Down Expand Up @@ -1740,9 +1741,9 @@ func (fcomp *fcomp) function(pos syntax.Position, name string, f *syntax.Functio
// Don't call fcomp.lookup because we want
// the cell itself, not its content.
switch freevar.Scope {
case syntax.FreeScope:
case resolve.Free:
fcomp.emit1(FREE, uint32(freevar.Index))
case syntax.CellScope:
case resolve.Cell:
fcomp.emit1(LOCAL, uint32(freevar.Index))
}
}
Expand Down
82 changes: 50 additions & 32 deletions resolve/resolve.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.Binding, error) {
func Expr(expr syntax.Expr, isPredeclared, isUniversal func(name string) bool) ([]*Binding, error) {
r := newResolver(isPredeclared, isUniversal)
r.expr(expr)
r.env.resolveLocalUses()
Expand Down Expand Up @@ -166,11 +166,25 @@ func newResolver(isPredeclared, isUniversal func(name string) bool) *resolver {
env: new(block), // module block
isPredeclared: isPredeclared,
isUniversal: isUniversal,
globals: make(map[string]*syntax.Binding),
predeclared: make(map[string]*syntax.Binding),
globals: make(map[string]*Binding),
predeclared: make(map[string]*Binding),
}
}

// Declare aliases for types and constants that logically belong here.

type Binding = syntax.Binding

const (
Undefined = syntax.UndefinedScope
Local = syntax.LocalScope
Cell = syntax.CellScope
Free = syntax.FreeScope
Global = syntax.GlobalScope
Predeclared = syntax.PredeclaredScope
Universal = syntax.UniversalScope
)

type resolver struct {
// env is the current local environment:
// a linked list of blocks, innermost first.
Expand All @@ -180,13 +194,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.Binding
moduleGlobals []*syntax.Binding
moduleLocals []*Binding
moduleGlobals []*Binding

// 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
globals map[string]*Binding
predeclared map[string]*Binding

// These predicates report whether a name is
// pre-declared, either in this module or universally.
Expand Down Expand Up @@ -226,7 +240,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]*syntax.Binding
bindings map[string]*Binding

// children records the child blocks of the current one.
children []*block
Expand All @@ -241,9 +255,9 @@ type block struct {

func (b *block) isModule() bool { return b.parent == nil }

func (b *block) bind(name string, bind *syntax.Binding) {
func (b *block) bind(name string, bind *Binding) {
if b.bindings == nil {
b.bindings = make(map[string]*syntax.Binding)
b.bindings = make(map[string]*Binding)
}
b.bindings[name] = bind
}
Expand Down Expand Up @@ -278,9 +292,9 @@ func (r *resolver) bind(id *syntax.Ident) bool {
bind, ok := r.globals[id.Name]
if !ok {
// first global binding of this name
bind = &syntax.Binding{
bind = &Binding{
First: id,
Scope: syntax.GlobalScope,
Scope: Global,
Index: len(r.moduleGlobals),
}
r.globals[id.Name] = bind
Expand All @@ -296,15 +310,15 @@ 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.Binding
var locals *[]*Binding
if fn := r.container().function; fn != nil {
locals = &fn.Locals
} else {
locals = &r.moduleLocals
}
bind := &syntax.Binding{
bind := &Binding{
First: id,
Scope: syntax.LocalScope,
Scope: Local,
Index: len(*locals),
}
r.env.bind(id.Name, bind)
Expand Down Expand Up @@ -359,26 +373,30 @@ 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) (bind *syntax.Binding) {
func (r *resolver) useGlobal(use use) (bind *Binding) {
id := use.id
if prev, ok := r.globals[id.Name]; ok {
bind = prev // use of global declared by module
// use of global declared by module
bind = prev
} else if prev, ok := r.predeclared[id.Name]; ok {
bind = prev // repeated use of predeclared or universal
// repeated use of predeclared or universal
bind = prev
} else if r.isPredeclared(id.Name) {
bind = &syntax.Binding{Scope: syntax.PredeclaredScope} // use of pre-declared name
r.predeclared[id.Name] = bind // save it
// use of pre-declared name
bind = &Binding{Scope: Predeclared}
r.predeclared[id.Name] = bind // save it
} else if r.isUniversal(id.Name) {
// 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
bind = &Binding{Scope: Universal}
r.predeclared[id.Name] = bind // save it
} else {
bind = &syntax.Binding{Scope: syntax.UndefinedScope}
bind = &Binding{Scope: Undefined}
var hint string
if n := r.spellcheck(use); n != "" {
hint = fmt.Sprintf(" (did you mean %s?)", n)
Expand Down Expand Up @@ -418,7 +436,7 @@ 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 != nil && (bind.Scope == syntax.LocalScope || bind.Scope == syntax.CellScope) {
if bind := lookupLocal(use); bind != nil && (bind.Scope == Local || bind.Scope == Cell) {
use.id.Binding = bind
} else {
unresolved = append(unresolved, use)
Expand Down Expand Up @@ -843,10 +861,10 @@ func (r *resolver) resolveNonLocalUses(b *block) {
}

// lookupLocal looks up an identifier within its immediately enclosing function.
func lookupLocal(use use) *syntax.Binding {
func lookupLocal(use use) *Binding {
for env := use.env; env != nil; env = env.parent {
if bind, ok := env.bindings[use.id.Name]; ok {
if bind.Scope == syntax.FreeScope {
if bind.Scope == Free {
// shouldn't exist till later
log.Fatalf("%s: internal error: %s, %v", use.id.NamePos, use.id.Name, bind)
}
Expand All @@ -861,7 +879,7 @@ func lookupLocal(use use) *syntax.Binding {

// 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 *syntax.Binding) {
func (r *resolver) lookupLexical(use use, env *block) (bind *Binding) {
if debug {
fmt.Printf("lookupLexical %s in %s = ...\n", use.id.Name, env)
defer func() { fmt.Printf("= %v\n", bind) }()
Expand All @@ -877,19 +895,19 @@ func (r *resolver) lookupLexical(use use, env *block) (bind *syntax.Binding) {
if !ok {
// Defined in parent block?
bind = r.lookupLexical(use, env.parent)
if env.function != nil && (bind.Scope == syntax.LocalScope || bind.Scope == syntax.FreeScope || bind.Scope == syntax.CellScope) {
if env.function != nil && (bind.Scope == Local || bind.Scope == Free || bind.Scope == Cell) {
// Found in parent block, which belongs to enclosing function.
// Add the parent's binding to the function's freevars,
// and add a new 'free' binding to the inner function's block,
// and turn the parent's local into cell.
if bind.Scope == syntax.LocalScope {
bind.Scope = syntax.CellScope
if bind.Scope == Local {
bind.Scope = Cell
}
index := len(env.function.FreeVars)
env.function.FreeVars = append(env.function.FreeVars, bind)
bind = &syntax.Binding{
bind = &Binding{
First: bind.First,
Scope: syntax.FreeScope,
Scope: Free,
Index: index,
}
if debug {
Expand Down
3 changes: 3 additions & 0 deletions syntax/binding.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ package syntax

// A Binding ties together all identifiers that denote the same variable.
// The resolver computes a binding for every Ident.
//
// Where possible, refer to this type using the alias resolve.Binding.
type Binding struct {
Scope Scope

Expand All @@ -20,6 +22,7 @@ type Binding struct {
}

// The Scope of Binding indicates what kind of scope it has.
// Where possible, refer to these constants using the aliases resolve.Local, etc.
type Scope uint8

const (
Expand Down

0 comments on commit 6db5e16

Please sign in to comment.