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 == Free {
				log.Fatalf("%s: internal error: %s, %d", use.id.NamePos, use.id.Name, bind)
			}
			return bind  
		}
		if env.function != nil {
			break
		}
	}
	return binding{}  
}
lookupLocal looks up an identifier within its immediately enclosing function.
package resolve
import (
	"fmt"
	"log"
	"strings"
	"github.com/google/skylark/syntax"
)
const debug = false
const doesnt = "this Skylark dialect does not "
var (
	AllowNestedDef      = false  
	AllowLambda         = false  
	AllowFloat          = false  
	AllowSet            = false  
	AllowGlobalReassign = false  
	AllowBitwise        = false  
)
func File(file *syntax.File, isPredeclared, isUniversal func(name string) bool) error {
	r := newResolver(isPredeclared, isUniversal)
	r.stmts(file.Stmts)
	r.env.resolveLocalUses()
	r.resolveNonLocalUses(r.env)
	file.Locals = r.moduleLocals
	file.Globals = r.moduleGlobals
	if len(r.errors) > 0 {
		return r.errors
	}
	return nil
}
func Expr(expr syntax.Expr, isPredeclared, isUniversal func(name string) bool) ([]*syntax.Ident, error) {
	r := newResolver(isPredeclared, isUniversal)
	r.expr(expr)
	r.env.resolveLocalUses()
	r.resolveNonLocalUses(r.env)  
	if len(r.errors) > 0 {
		return nil, r.errors
	}
	return r.moduleLocals, nil
}
type ErrorList []Error  
func (e ErrorList) Error() string { return e[0].Error() }
type Error struct {
	Pos syntax.Position
	Msg string
}
func (e Error) Error() string { return e.Pos.String() + ": " + e.Msg }
type Scope uint8
const (
	Undefined   Scope = iota  
	Local                     
	Free                      
	Global                    
	Predeclared               
	Universal                 
)
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),  
		isPredeclared: isPredeclared,
		isUniversal:   isUniversal,
		globals:       make(map[string]*syntax.Ident),
	}
}
type resolver struct {
	env *block
	moduleLocals  []*syntax.Ident
	moduleGlobals []*syntax.Ident
	globals map[string]*syntax.Ident
	isPredeclared, isUniversal func(name string) bool
	loops int  
	errors ErrorList
}
func (r *resolver) container() *block {
	for b := r.env; ; b = b.parent {
		if b.function != nil || b.isModule() {
			return b
		}
	}
}
func (r *resolver) push(b *block) {
	r.env.children = append(r.env.children, b)
	b.parent = r.env
	r.env = b
}
func (r *resolver) pop() { r.env = r.env.parent }
type block struct {
	parent *block  
	function *syntax.Function       
	comp     *syntax.Comprehension  
	bindings map[string]binding
	children []*block
	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) {
	if b.bindings == nil {
		b.bindings = make(map[string]binding)
	}
	b.bindings[name] = bind
}
func (b *block) String() string {
	if b.function != nil {
		return "function block at " + fmt.Sprint(b.function.Span())
	}
	if b.comp != nil {
		return "comprehension block at " + fmt.Sprint(b.comp.Span())
	}
	return "module block"
}
func (r *resolver) errorf(posn syntax.Position, format string, args ...interface{}) {
	r.errors = append(r.errors, Error{posn, fmt.Sprintf(format, args...)})
}
type use struct {
	id  *syntax.Ident
	env *block
}
func (r *resolver) bind(id *syntax.Ident, allowRebind bool) bool {
	if r.env.isModule() {
		id.Scope = uint8(Global)
		prev, ok := r.globals[id.Name]
		if ok {
			if !allowRebind && !AllowGlobalReassign {
				r.errorf(id.NamePos, "cannot reassign global %s declared at %s", id.Name, prev.NamePos)
			}
			id.Index = prev.Index
		} else {
			r.globals[id.Name] = id
			id.Index = len(r.moduleGlobals)
			r.moduleGlobals = append(r.moduleGlobals, id)
		}
		return ok
	}
	_, ok := r.env.bindings[id.Name]
	if !ok {
		var locals *[]*syntax.Ident
		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)
	}
	r.use(id)
	return ok
}
func (r *resolver) use(id *syntax.Ident) {
	b := r.container()
	b.uses = append(b.uses, use{id, r.env})
}
func (r *resolver) useGlobal(id *syntax.Ident) binding {
	var scope Scope
	if prev, ok := r.globals[id.Name]; ok {
		scope = Global  
		id.Index = prev.Index
	} else if r.isPredeclared(id.Name) {
		scope = Predeclared  
	} else if r.isUniversal(id.Name) {
		scope = Universal  
		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")
		}
	} else {
		scope = Undefined
		r.errorf(id.NamePos, "undefined: %s", id.Name)
	}
	id.Scope = uint8(scope)
	return binding{scope, id.Index}
}
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
		} else {
			unresolved = append(unresolved, use)
		}
	}
	b.uses = unresolved
}
func (r *resolver) stmts(stmts []syntax.Stmt) {
	for _, stmt := range stmts {
		r.stmt(stmt)
	}
}
func (r *resolver) stmt(stmt syntax.Stmt) {
	switch stmt := stmt.(type) {
	case *syntax.ExprStmt:
		r.expr(stmt.X)
	case *syntax.BranchStmt:
		if r.loops == 0 && (stmt.Token == syntax.BREAK || stmt.Token == syntax.CONTINUE) {
			r.errorf(stmt.TokenPos, "%s not in a loop", stmt.Token)
		}
	case *syntax.IfStmt:
		if r.container().function == nil {
			r.errorf(stmt.If, "if statement not within a function")
		}
		r.expr(stmt.Cond)
		r.stmts(stmt.True)
		r.stmts(stmt.False)
	case *syntax.AssignStmt:
		if !AllowBitwise {
			switch stmt.Op {
			case syntax.AMP_EQ, syntax.PIPE_EQ, syntax.CIRCUMFLEX_EQ, syntax.LTLT_EQ, syntax.GTGT_EQ:
				r.errorf(stmt.OpPos, doesnt+"support bitwise operations")
			}
		}
		r.expr(stmt.RHS)
		isAugmented := stmt.Op != syntax.EQ
		r.assign(stmt.LHS, isAugmented)
	case *syntax.DefStmt:
		if !AllowNestedDef && r.container().function != nil {
			r.errorf(stmt.Def, doesnt+"support nested def")
		}
		const allowRebind = false
		r.bind(stmt.Name, allowRebind)
		r.function(stmt.Def, stmt.Name.Name, &stmt.Function)
	case *syntax.ForStmt:
		if r.container().function == nil {
			r.errorf(stmt.For, "for loop not within a function")
		}
		r.expr(stmt.X)
		const allowRebind = false
		r.assign(stmt.Vars, allowRebind)
		r.loops++
		r.stmts(stmt.Body)
		r.loops--
	case *syntax.ReturnStmt:
		if r.container().function == nil {
			r.errorf(stmt.Return, "return statement not within a function")
		}
		if stmt.Result != nil {
			r.expr(stmt.Result)
		}
	case *syntax.LoadStmt:
		if r.container().function != nil {
			r.errorf(stmt.Load, "load statement within a function")
		}
		const allowRebind = false
		for i, from := range stmt.From {
			if from.Name == "" {
				r.errorf(from.NamePos, "load: empty identifier")
				continue
			}
			if from.Name[0] == '_' {
				r.errorf(from.NamePos, "load: names with leading underscores are not exported: %s", from.Name)
			}
			r.bind(stmt.To[i], allowRebind)
		}
	default:
		log.Fatalf("unexpected stmt %T", stmt)
	}
}
func (r *resolver) assign(lhs syntax.Expr, isAugmented bool) {
	switch lhs := lhs.(type) {
	case *syntax.Ident:
		allowRebind := isAugmented
		r.bind(lhs, allowRebind)
	case *syntax.IndexExpr:
		r.expr(lhs.X)
		r.expr(lhs.Y)
	case *syntax.DotExpr:
		r.expr(lhs.X)
	case *syntax.TupleExpr:
		if len(lhs.List) == 0 {
			r.errorf(syntax.Start(lhs), "can't assign to ()")
		}
		if isAugmented {
			r.errorf(syntax.Start(lhs), "can't use tuple expression in augmented assignment")
		}
		for _, elem := range lhs.List {
			r.assign(elem, isAugmented)
		}
	case *syntax.ListExpr:
		if len(lhs.List) == 0 {
			r.errorf(syntax.Start(lhs), "can't assign to []")
		}
		if isAugmented {
			r.errorf(syntax.Start(lhs), "can't use list expression in augmented assignment")
		}
		for _, elem := range lhs.List {
			r.assign(elem, isAugmented)
		}
	case *syntax.ParenExpr:
		r.assign(lhs.X, isAugmented)
	default:
		name := strings.ToLower(strings.TrimPrefix(fmt.Sprintf("%T", lhs), "*syntax."))
		r.errorf(syntax.Start(lhs), "can't assign to %s", name)
	}
}
func (r *resolver) expr(e syntax.Expr) {
	switch e := e.(type) {
	case *syntax.Ident:
		r.use(e)
	case *syntax.Literal:
		if !AllowFloat && e.Token == syntax.FLOAT {
			r.errorf(e.TokenPos, doesnt+"support floating point")
		}
	case *syntax.ListExpr:
		for _, x := range e.List {
			r.expr(x)
		}
	case *syntax.CondExpr:
		r.expr(e.Cond)
		r.expr(e.True)
		r.expr(e.False)
	case *syntax.IndexExpr:
		r.expr(e.X)
		r.expr(e.Y)
	case *syntax.DictEntry:
		r.expr(e.Key)
		r.expr(e.Value)
	case *syntax.SliceExpr:
		r.expr(e.X)
		if e.Lo != nil {
			r.expr(e.Lo)
		}
		if e.Hi != nil {
			r.expr(e.Hi)
		}
		if e.Step != nil {
			r.expr(e.Step)
		}
	case *syntax.Comprehension:
		clause := e.Clauses[0].(*syntax.ForClause)
		r.expr(clause.X)
		r.push(&block{comp: e})
		const allowRebind = false
		r.assign(clause.Vars, allowRebind)
		for _, clause := range e.Clauses[1:] {
			switch clause := clause.(type) {
			case *syntax.IfClause:
				r.expr(clause.Cond)
			case *syntax.ForClause:
				r.assign(clause.Vars, allowRebind)
				r.expr(clause.X)
			}
		}
		r.expr(e.Body)  
		r.pop()
	case *syntax.TupleExpr:
		for _, x := range e.List {
			r.expr(x)
		}
	case *syntax.DictExpr:
		for _, entry := range e.List {
			entry := entry.(*syntax.DictEntry)
			r.expr(entry.Key)
			r.expr(entry.Value)
		}
	case *syntax.UnaryExpr:
		if !AllowBitwise && e.Op == syntax.TILDE {
			r.errorf(e.OpPos, doesnt+"support bitwise operations")
		}
		r.expr(e.X)
	case *syntax.BinaryExpr:
		if !AllowFloat && e.Op == syntax.SLASH {
			r.errorf(e.OpPos, doesnt+"support floating point (use //)")
		}
		if !AllowBitwise {
			switch e.Op {
			case syntax.AMP, syntax.PIPE, syntax.CIRCUMFLEX, syntax.LTLT, syntax.GTGT:
				r.errorf(e.OpPos, doesnt+"support bitwise operations")
			}
		}
		r.expr(e.X)
		r.expr(e.Y)
	case *syntax.DotExpr:
		r.expr(e.X)
	case *syntax.CallExpr:
		r.expr(e.Fn)
		var seenVarargs, seenKwargs, seenNamed bool
		for _, arg := range e.Args {
			pos, _ := arg.Span()
			if unop, ok := arg.(*syntax.UnaryExpr); ok && unop.Op == syntax.STARSTAR {
				if seenKwargs {
					r.errorf(pos, "multiple **kwargs not allowed")
				}
				seenKwargs = true
				r.expr(arg)
			} else if ok && unop.Op == syntax.STAR {
				if seenKwargs {
					r.errorf(pos, "*args may not follow **kwargs")
				} else if seenVarargs {
					r.errorf(pos, "multiple *args not allowed")
				}
				seenVarargs = true
				r.expr(arg)
			} else if binop, ok := arg.(*syntax.BinaryExpr); ok && binop.Op == syntax.EQ {
				if seenKwargs {
					r.errorf(pos, "argument may not follow **kwargs")
				}
				r.expr(binop.Y)
				seenNamed = true
			} else {
				if seenVarargs {
					r.errorf(pos, "argument may not follow *args")
				} else if seenKwargs {
					r.errorf(pos, "argument may not follow **kwargs")
				} else if seenNamed {
					r.errorf(pos, "positional argument may not follow named")
				}
				r.expr(arg)
			}
		}
	case *syntax.LambdaExpr:
		if !AllowLambda {
			r.errorf(e.Lambda, doesnt+"support lambda")
		}
		r.function(e.Lambda, "lambda", &e.Function)
	case *syntax.ParenExpr:
		r.expr(e.X)
	default:
		log.Fatalf("unexpected expr %T", e)
	}
}
func (r *resolver) function(pos syntax.Position, name string, function *syntax.Function) {
	for _, param := range function.Params {
		if binary, ok := param.(*syntax.BinaryExpr); ok {
			r.expr(binary.Y)
		}
	}
	b := &block{function: function}
	r.push(b)
	const allowRebind = false
	var seenVarargs, seenKwargs, seenOptional bool
	for _, param := range function.Params {
		switch param := param.(type) {
		case *syntax.Ident:
			if seenKwargs {
				r.errorf(pos, "parameter may not follow **kwargs")
			} else if seenVarargs {
				r.errorf(pos, "parameter may not follow *args")
			} else if seenOptional {
				r.errorf(pos, "required parameter may not follow optional")
			}
			if r.bind(param, allowRebind) {
				r.errorf(pos, "duplicate parameter: %s", param.Name)
			}
		case *syntax.BinaryExpr:
			if seenKwargs {
				r.errorf(pos, "parameter may not follow **kwargs")
			} else if seenVarargs {
				r.errorf(pos, "parameter may not follow *args")
			}
			if id := param.X.(*syntax.Ident); r.bind(id, allowRebind) {
				r.errorf(pos, "duplicate parameter: %s", id.Name)
			}
			seenOptional = true
		case *syntax.UnaryExpr:
			if param.Op == syntax.STAR {
				if seenKwargs {
					r.errorf(pos, "*args may not follow **kwargs")
				} else if seenVarargs {
					r.errorf(pos, "multiple *args not allowed")
				}
				seenVarargs = true
			} else {
				if seenKwargs {
					r.errorf(pos, "multiple **kwargs not allowed")
				}
				seenKwargs = true
			}
			if id := param.X.(*syntax.Ident); r.bind(id, allowRebind) {
				r.errorf(pos, "duplicate parameter: %s", id.Name)
			}
		}
	}
	function.HasVarargs = seenVarargs
	function.HasKwargs = seenKwargs
	r.stmts(function.Body)
	b.resolveLocalUses()
	r.pop()
}
func (r *resolver) resolveNonLocalUses(b *block) {
	for _, child := range b.children {
		r.resolveNonLocalUses(child)
	}
	for _, use := range b.uses {
		bind := r.lookupLexical(use.id, use.env)
		use.id.Scope = uint8(bind.scope)
		use.id.Index = bind.index
	}
}