Alan Donovan | 312d1a5 | 2017-10-02 10:10:28 -0400 | [diff] [blame] | 1 | // Copyright 2017 The Bazel Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style |
| 3 | // license that can be found in the LICENSE file. |
| 4 | |
Alan Donovan | e3deafe | 2018-10-23 11:05:09 -0400 | [diff] [blame] | 5 | // Package resolve defines a name-resolution pass for Starlark abstract |
Alan Donovan | 312d1a5 | 2017-10-02 10:10:28 -0400 | [diff] [blame] | 6 | // syntax trees. |
| 7 | // |
| 8 | // The resolver sets the Locals and FreeVars arrays of each DefStmt and |
| 9 | // the LocalIndex field of each syntax.Ident that refers to a local or |
| 10 | // free variable. It also sets the Locals array of a File for locals |
| 11 | // bound by comprehensions outside any function. Identifiers for global |
| 12 | // variables do not get an index. |
Alan Donovan | 551f300 | 2018-11-01 09:44:00 -0400 | [diff] [blame] | 13 | package resolve // import "go.starlark.net/resolve" |
Alan Donovan | 312d1a5 | 2017-10-02 10:10:28 -0400 | [diff] [blame] | 14 | |
| 15 | // All references to names are statically resolved. Names may be |
alandonovan | a1b28d8 | 2018-03-13 10:59:24 -0400 | [diff] [blame] | 16 | // predeclared, global, or local to a function or module-level comprehension. |
| 17 | // The resolver maps each global name to a small integer and each local |
| 18 | // name to a small integer; these integers enable a fast and compact |
| 19 | // representation of globals and locals in the evaluator. |
| 20 | // |
| 21 | // As an optimization, the resolver classifies each predeclared name as |
| 22 | // either universal (e.g. None, len) or per-module (e.g. glob in Bazel's |
| 23 | // build language), enabling the evaluator to share the representation |
| 24 | // of the universal environment across all modules. |
Alan Donovan | 312d1a5 | 2017-10-02 10:10:28 -0400 | [diff] [blame] | 25 | // |
| 26 | // The lexical environment is a tree of blocks with the module block at |
| 27 | // its root. The module's child blocks may be of two kinds: functions |
| 28 | // and comprehensions, and these may have further children of either |
| 29 | // kind. |
| 30 | // |
| 31 | // Python-style resolution requires multiple passes because a name is |
| 32 | // determined to be local to a function only if the function contains a |
alandonovan | 4956ce9 | 2018-08-22 17:44:47 -0400 | [diff] [blame] | 33 | // "binding" use of it; similarly, a name is determined be global (as |
| 34 | // opposed to predeclared) if the module contains a binding use at the |
| 35 | // top level. For both locals and globals, a non-binding use may |
| 36 | // lexically precede the binding to which it is resolved. |
| 37 | // In the first pass, we inspect each function, recording in |
Alan Donovan | 312d1a5 | 2017-10-02 10:10:28 -0400 | [diff] [blame] | 38 | // 'uses' each identifier and the environment block in which it occurs. |
| 39 | // If a use of a name is binding, such as a function parameter or |
| 40 | // assignment, we add the name to the block's bindings mapping and add a |
| 41 | // local variable to the enclosing function. |
| 42 | // |
| 43 | // As we finish resolving each function, we inspect all the uses within |
alandonovan | a1b28d8 | 2018-03-13 10:59:24 -0400 | [diff] [blame] | 44 | // that function and discard ones that were found to be local. The |
Alan Donovan | 312d1a5 | 2017-10-02 10:10:28 -0400 | [diff] [blame] | 45 | // remaining ones must be either free (local to some lexically enclosing |
alandonovan | a1b28d8 | 2018-03-13 10:59:24 -0400 | [diff] [blame] | 46 | // function), or nonlocal (global or predeclared), but we cannot tell |
| 47 | // which until we have finished inspecting the outermost enclosing |
| 48 | // function. At that point, we can distinguish local from global names |
| 49 | // (and this is when Python would compute free variables). |
Alan Donovan | 312d1a5 | 2017-10-02 10:10:28 -0400 | [diff] [blame] | 50 | // |
Alan Donovan | e3deafe | 2018-10-23 11:05:09 -0400 | [diff] [blame] | 51 | // However, Starlark additionally requires that all references to global |
Alan Donovan | 312d1a5 | 2017-10-02 10:10:28 -0400 | [diff] [blame] | 52 | // names are satisfied by some declaration in the current module; |
Alan Donovan | e3deafe | 2018-10-23 11:05:09 -0400 | [diff] [blame] | 53 | // Starlark permits a function to forward-reference a global that has not |
Alan Donovan | 312d1a5 | 2017-10-02 10:10:28 -0400 | [diff] [blame] | 54 | // been declared yet so long as it is declared before the end of the |
| 55 | // module. So, instead of re-resolving the unresolved references after |
| 56 | // each top-level function, we defer this until the end of the module |
| 57 | // and ensure that all such references are satisfied by some definition. |
| 58 | // |
| 59 | // At the end of the module, we visit each of the nested function blocks |
| 60 | // in bottom-up order, doing a recursive lexical lookup for each |
| 61 | // unresolved name. If the name is found to be local to some enclosing |
| 62 | // function, we must create a DefStmt.FreeVar (capture) parameter for |
| 63 | // each intervening function. We enter these synthetic bindings into |
| 64 | // the bindings map so that we create at most one freevar per name. If |
| 65 | // the name was not local, we check that it was defined at module level. |
| 66 | // |
| 67 | // We resolve all uses of locals in the module (due to comprehensions) |
| 68 | // in a similar way and compute the set of its local variables. |
| 69 | // |
Alan Donovan | e3deafe | 2018-10-23 11:05:09 -0400 | [diff] [blame] | 70 | // Starlark enforces that all global names are assigned at most once on |
Alan Donovan | 312d1a5 | 2017-10-02 10:10:28 -0400 | [diff] [blame] | 71 | // all control flow paths by forbidding if/else statements and loops at |
alandonovan | 4956ce9 | 2018-08-22 17:44:47 -0400 | [diff] [blame] | 72 | // top level. A global may be used before it is defined, leading to a |
| 73 | // dynamic error. |
Alan Donovan | 312d1a5 | 2017-10-02 10:10:28 -0400 | [diff] [blame] | 74 | // |
| 75 | // TODO(adonovan): opt: reuse local slots once locals go out of scope. |
| 76 | |
| 77 | import ( |
| 78 | "fmt" |
| 79 | "log" |
| 80 | "strings" |
| 81 | |
Alan Donovan | 6beab7e | 2018-10-31 17:53:09 -0400 | [diff] [blame] | 82 | "go.starlark.net/syntax" |
Alan Donovan | 312d1a5 | 2017-10-02 10:10:28 -0400 | [diff] [blame] | 83 | ) |
| 84 | |
| 85 | const debug = false |
Alan Donovan | e3deafe | 2018-10-23 11:05:09 -0400 | [diff] [blame] | 86 | const doesnt = "this Starlark dialect does not " |
Alan Donovan | 312d1a5 | 2017-10-02 10:10:28 -0400 | [diff] [blame] | 87 | |
| 88 | // global options |
Alan Donovan | e3deafe | 2018-10-23 11:05:09 -0400 | [diff] [blame] | 89 | // These features are either not standard Starlark (yet), or deprecated |
Alan Donovan | 312d1a5 | 2017-10-02 10:10:28 -0400 | [diff] [blame] | 90 | // features of the BUILD language, so we put them behind flags. |
| 91 | var ( |
| 92 | AllowNestedDef = false // allow def statements within function bodies |
| 93 | AllowLambda = false // allow lambda expressions |
| 94 | AllowFloat = false // allow floating point literals, the 'float' built-in, and x / y |
Alan Donovan | 312d1a5 | 2017-10-02 10:10:28 -0400 | [diff] [blame] | 95 | AllowSet = false // allow the 'set' built-in |
| 96 | AllowGlobalReassign = false // allow reassignment to globals declared in same file (deprecated) |
Hittorp | 0a5e39a | 2018-08-09 15:02:30 +0300 | [diff] [blame] | 97 | AllowBitwise = false // allow bitwise operations (&, |, ^, ~, <<, and >>) |
Alessandro Arzilli | 678bafe | 2018-12-07 17:28:35 +0100 | [diff] [blame] | 98 | AllowRecursion = false // allow while statements and recursive functions |
Alan Donovan | 312d1a5 | 2017-10-02 10:10:28 -0400 | [diff] [blame] | 99 | ) |
| 100 | |
| 101 | // File resolves the specified file. |
alandonovan | 631f7b5 | 2017-10-11 13:04:47 -0400 | [diff] [blame] | 102 | // |
alandonovan | a1b28d8 | 2018-03-13 10:59:24 -0400 | [diff] [blame] | 103 | // The isPredeclared and isUniversal predicates report whether a name is |
| 104 | // a pre-declared identifier (visible in the current module) or a |
| 105 | // universal identifier (visible in every module). |
| 106 | // Clients should typically pass predeclared.Has for the first and |
Alan Donovan | e3deafe | 2018-10-23 11:05:09 -0400 | [diff] [blame] | 107 | // starlark.Universe.Has for the second, where predeclared is the |
| 108 | // module's StringDict of predeclared names and starlark.Universe is the |
alandonovan | a1b28d8 | 2018-03-13 10:59:24 -0400 | [diff] [blame] | 109 | // standard set of built-ins. |
| 110 | // The isUniverse predicate is supplied a parameter to avoid a cyclic |
Alan Donovan | e3deafe | 2018-10-23 11:05:09 -0400 | [diff] [blame] | 111 | // dependency upon starlark.Universe, not because users should ever need |
alandonovan | a1b28d8 | 2018-03-13 10:59:24 -0400 | [diff] [blame] | 112 | // to redefine it. |
| 113 | func File(file *syntax.File, isPredeclared, isUniversal func(name string) bool) error { |
| 114 | r := newResolver(isPredeclared, isUniversal) |
Alan Donovan | 312d1a5 | 2017-10-02 10:10:28 -0400 | [diff] [blame] | 115 | r.stmts(file.Stmts) |
| 116 | |
| 117 | r.env.resolveLocalUses() |
| 118 | |
| 119 | // At the end of the module, resolve all non-local variable references, |
| 120 | // computing closures. |
| 121 | // Function bodies may contain forward references to later global declarations. |
| 122 | r.resolveNonLocalUses(r.env) |
| 123 | |
| 124 | file.Locals = r.moduleLocals |
alandonovan | a1b28d8 | 2018-03-13 10:59:24 -0400 | [diff] [blame] | 125 | file.Globals = r.moduleGlobals |
Alan Donovan | 312d1a5 | 2017-10-02 10:10:28 -0400 | [diff] [blame] | 126 | |
| 127 | if len(r.errors) > 0 { |
| 128 | return r.errors |
| 129 | } |
| 130 | return nil |
| 131 | } |
| 132 | |
| 133 | // Expr resolves the specified expression. |
| 134 | // It returns the local variables bound within the expression. |
alandonovan | 631f7b5 | 2017-10-11 13:04:47 -0400 | [diff] [blame] | 135 | // |
alandonovan | a1b28d8 | 2018-03-13 10:59:24 -0400 | [diff] [blame] | 136 | // The isPredeclared and isUniversal predicates behave as for the File function. |
| 137 | func Expr(expr syntax.Expr, isPredeclared, isUniversal func(name string) bool) ([]*syntax.Ident, error) { |
| 138 | r := newResolver(isPredeclared, isUniversal) |
Alan Donovan | 312d1a5 | 2017-10-02 10:10:28 -0400 | [diff] [blame] | 139 | r.expr(expr) |
| 140 | r.env.resolveLocalUses() |
alandonovan | 631f7b5 | 2017-10-11 13:04:47 -0400 | [diff] [blame] | 141 | r.resolveNonLocalUses(r.env) // globals & universals |
Alan Donovan | 312d1a5 | 2017-10-02 10:10:28 -0400 | [diff] [blame] | 142 | if len(r.errors) > 0 { |
| 143 | return nil, r.errors |
| 144 | } |
| 145 | return r.moduleLocals, nil |
| 146 | } |
| 147 | |
| 148 | // An ErrorList is a non-empty list of resolver error messages. |
| 149 | type ErrorList []Error // len > 0 |
| 150 | |
| 151 | func (e ErrorList) Error() string { return e[0].Error() } |
| 152 | |
| 153 | // An Error describes the nature and position of a resolver error. |
| 154 | type Error struct { |
| 155 | Pos syntax.Position |
| 156 | Msg string |
| 157 | } |
| 158 | |
| 159 | func (e Error) Error() string { return e.Pos.String() + ": " + e.Msg } |
| 160 | |
| 161 | // The Scope of a syntax.Ident indicates what kind of scope it has. |
| 162 | type Scope uint8 |
| 163 | |
| 164 | const ( |
alandonovan | a1b28d8 | 2018-03-13 10:59:24 -0400 | [diff] [blame] | 165 | Undefined Scope = iota // name is not defined |
| 166 | Local // name is local to its function |
| 167 | Free // name is local to some enclosing function |
| 168 | Global // name is global to module |
| 169 | Predeclared // name is predeclared for this module (e.g. glob) |
| 170 | Universal // name is universal (e.g. len) |
Alan Donovan | 312d1a5 | 2017-10-02 10:10:28 -0400 | [diff] [blame] | 171 | ) |
| 172 | |
| 173 | var scopeNames = [...]string{ |
alandonovan | a1b28d8 | 2018-03-13 10:59:24 -0400 | [diff] [blame] | 174 | Undefined: "undefined", |
| 175 | Local: "local", |
| 176 | Free: "free", |
| 177 | Global: "global", |
| 178 | Predeclared: "predeclared", |
| 179 | Universal: "universal", |
Alan Donovan | 312d1a5 | 2017-10-02 10:10:28 -0400 | [diff] [blame] | 180 | } |
| 181 | |
| 182 | func (scope Scope) String() string { return scopeNames[scope] } |
| 183 | |
alandonovan | a1b28d8 | 2018-03-13 10:59:24 -0400 | [diff] [blame] | 184 | func newResolver(isPredeclared, isUniversal func(name string) bool) *resolver { |
Alan Donovan | 312d1a5 | 2017-10-02 10:10:28 -0400 | [diff] [blame] | 185 | return &resolver{ |
alandonovan | a1b28d8 | 2018-03-13 10:59:24 -0400 | [diff] [blame] | 186 | env: new(block), // module block |
| 187 | isPredeclared: isPredeclared, |
| 188 | isUniversal: isUniversal, |
| 189 | globals: make(map[string]*syntax.Ident), |
Alan Donovan | 312d1a5 | 2017-10-02 10:10:28 -0400 | [diff] [blame] | 190 | } |
| 191 | } |
| 192 | |
| 193 | type resolver struct { |
| 194 | // env is the current local environment: |
| 195 | // a linked list of blocks, innermost first. |
| 196 | // The tail of the list is the module block. |
| 197 | env *block |
| 198 | |
| 199 | // moduleLocals contains the local variables of the module |
| 200 | // (due to comprehensions outside any function). |
alandonovan | a1b28d8 | 2018-03-13 10:59:24 -0400 | [diff] [blame] | 201 | // moduleGlobals contains the global variables of the module. |
| 202 | moduleLocals []*syntax.Ident |
| 203 | moduleGlobals []*syntax.Ident |
Alan Donovan | 312d1a5 | 2017-10-02 10:10:28 -0400 | [diff] [blame] | 204 | |
alandonovan | a1b28d8 | 2018-03-13 10:59:24 -0400 | [diff] [blame] | 205 | // globals maps each global name in the module |
| 206 | // to its first binding occurrence. |
| 207 | globals map[string]*syntax.Ident |
Alan Donovan | 312d1a5 | 2017-10-02 10:10:28 -0400 | [diff] [blame] | 208 | |
| 209 | // These predicates report whether a name is |
alandonovan | a1b28d8 | 2018-03-13 10:59:24 -0400 | [diff] [blame] | 210 | // pre-declared, either in this module or universally. |
| 211 | isPredeclared, isUniversal func(name string) bool |
Alan Donovan | 312d1a5 | 2017-10-02 10:10:28 -0400 | [diff] [blame] | 212 | |
| 213 | loops int // number of enclosing for loops |
| 214 | |
| 215 | errors ErrorList |
| 216 | } |
| 217 | |
| 218 | // container returns the innermost enclosing "container" block: |
| 219 | // a function (function != nil) or module (function == nil). |
| 220 | // Container blocks accumulate local variable bindings. |
| 221 | func (r *resolver) container() *block { |
| 222 | for b := r.env; ; b = b.parent { |
| 223 | if b.function != nil || b.isModule() { |
| 224 | return b |
| 225 | } |
| 226 | } |
| 227 | } |
| 228 | |
| 229 | func (r *resolver) push(b *block) { |
| 230 | r.env.children = append(r.env.children, b) |
| 231 | b.parent = r.env |
| 232 | r.env = b |
| 233 | } |
| 234 | |
| 235 | func (r *resolver) pop() { r.env = r.env.parent } |
| 236 | |
| 237 | type block struct { |
| 238 | parent *block // nil for module block |
| 239 | |
| 240 | // In the module (root) block, both these fields are nil. |
| 241 | function *syntax.Function // only for function blocks |
| 242 | comp *syntax.Comprehension // only for comprehension blocks |
| 243 | |
| 244 | // bindings maps a name to its binding. |
| 245 | // A local binding has an index into its innermost enclosing container's locals array. |
| 246 | // A free binding has an index into its innermost enclosing function's freevars array. |
| 247 | bindings map[string]binding |
| 248 | |
| 249 | // children records the child blocks of the current one. |
| 250 | children []*block |
| 251 | |
| 252 | // uses records all identifiers seen in this container (function or module), |
| 253 | // and a reference to the environment in which they appear. |
| 254 | // As we leave each container block, we resolve them, |
| 255 | // so that only free and global ones remain. |
| 256 | // At the end of each top-level function we compute closures. |
| 257 | uses []use |
| 258 | } |
| 259 | |
| 260 | type binding struct { |
| 261 | scope Scope |
| 262 | index int |
| 263 | } |
| 264 | |
| 265 | func (b *block) isModule() bool { return b.parent == nil } |
| 266 | |
| 267 | func (b *block) bind(name string, bind binding) { |
| 268 | if b.bindings == nil { |
| 269 | b.bindings = make(map[string]binding) |
| 270 | } |
| 271 | b.bindings[name] = bind |
| 272 | } |
| 273 | |
| 274 | func (b *block) String() string { |
| 275 | if b.function != nil { |
| 276 | return "function block at " + fmt.Sprint(b.function.Span()) |
| 277 | } |
| 278 | if b.comp != nil { |
alandonovan | 5b0e788 | 2018-08-10 13:44:17 -0400 | [diff] [blame] | 279 | return "comprehension block at " + fmt.Sprint(b.comp.Span()) |
Alan Donovan | 312d1a5 | 2017-10-02 10:10:28 -0400 | [diff] [blame] | 280 | } |
| 281 | return "module block" |
| 282 | } |
| 283 | |
| 284 | func (r *resolver) errorf(posn syntax.Position, format string, args ...interface{}) { |
| 285 | r.errors = append(r.errors, Error{posn, fmt.Sprintf(format, args...)}) |
| 286 | } |
| 287 | |
| 288 | // A use records an identifier and the environment in which it appears. |
| 289 | type use struct { |
| 290 | id *syntax.Ident |
| 291 | env *block |
| 292 | } |
| 293 | |
| 294 | // bind creates a binding for id in the current block, |
| 295 | // if there is not one already, and reports an error if |
| 296 | // a global was re-bound and allowRebind is false. |
| 297 | // It returns whether a binding already existed. |
| 298 | func (r *resolver) bind(id *syntax.Ident, allowRebind bool) bool { |
| 299 | // Binding outside any local (comprehension/function) block? |
| 300 | if r.env.isModule() { |
alandonovan | a1b28d8 | 2018-03-13 10:59:24 -0400 | [diff] [blame] | 301 | id.Scope = uint8(Global) |
| 302 | prev, ok := r.globals[id.Name] |
Alan Donovan | 312d1a5 | 2017-10-02 10:10:28 -0400 | [diff] [blame] | 303 | if ok { |
Alan Donovan | 312d1a5 | 2017-10-02 10:10:28 -0400 | [diff] [blame] | 304 | // Global reassignments are permitted only if |
| 305 | // they are of the form x += y. We can't tell |
| 306 | // statically whether it's a reassignment |
| 307 | // (e.g. int += int) or a mutation (list += list). |
| 308 | if !allowRebind && !AllowGlobalReassign { |
alandonovan | a1b28d8 | 2018-03-13 10:59:24 -0400 | [diff] [blame] | 309 | r.errorf(id.NamePos, "cannot reassign global %s declared at %s", id.Name, prev.NamePos) |
Alan Donovan | 312d1a5 | 2017-10-02 10:10:28 -0400 | [diff] [blame] | 310 | } |
alandonovan | a1b28d8 | 2018-03-13 10:59:24 -0400 | [diff] [blame] | 311 | id.Index = prev.Index |
Alan Donovan | 312d1a5 | 2017-10-02 10:10:28 -0400 | [diff] [blame] | 312 | } else { |
alandonovan | a1b28d8 | 2018-03-13 10:59:24 -0400 | [diff] [blame] | 313 | // first global binding of this name |
| 314 | r.globals[id.Name] = id |
| 315 | id.Index = len(r.moduleGlobals) |
| 316 | r.moduleGlobals = append(r.moduleGlobals, id) |
Alan Donovan | 312d1a5 | 2017-10-02 10:10:28 -0400 | [diff] [blame] | 317 | } |
| 318 | return ok |
| 319 | } |
| 320 | |
| 321 | // Mark this name as local to current block. |
| 322 | // Assign it a new local (positive) index in the current container. |
| 323 | _, ok := r.env.bindings[id.Name] |
| 324 | if !ok { |
| 325 | var locals *[]*syntax.Ident |
| 326 | if fn := r.container().function; fn != nil { |
| 327 | locals = &fn.Locals |
| 328 | } else { |
| 329 | locals = &r.moduleLocals |
| 330 | } |
| 331 | r.env.bind(id.Name, binding{Local, len(*locals)}) |
| 332 | *locals = append(*locals, id) |
| 333 | } |
| 334 | |
| 335 | r.use(id) |
| 336 | return ok |
| 337 | } |
| 338 | |
| 339 | func (r *resolver) use(id *syntax.Ident) { |
alandonovan | 84a935b | 2018-11-26 14:42:21 -0500 | [diff] [blame] | 340 | // The spec says that if there is a global binding of a name |
| 341 | // then all references to that name in that block refer to the |
| 342 | // global, even if the use precedes the def---just as for locals. |
| 343 | // For example, in this code, |
| 344 | // |
| 345 | // print(len); len=1; print(len) |
| 346 | // |
| 347 | // both occurrences of len refer to the len=1 binding, which |
| 348 | // completely shadows the predeclared len function. |
| 349 | // |
| 350 | // The rationale for these semantics, which differ from Python, |
| 351 | // is that the static meaning of len (a reference to a global) |
| 352 | // does not change depending on where it appears in the file. |
| 353 | // Of course, its dynamic meaning does change, from an error |
| 354 | // into a valid reference, so it's not clear these semantics |
| 355 | // have any practical advantage. |
| 356 | // |
| 357 | // In any case, the Bazel implementation lags behind the spec |
| 358 | // and follows Python behavior, so the first use of len refers |
| 359 | // to the predeclared function. This typically used in a BUILD |
| 360 | // file that redefines a predeclared name half way through, |
| 361 | // for example: |
| 362 | // |
| 363 | // proto_library(...) # built-in rule |
| 364 | // load("myproto.bzl", "proto_library") |
| 365 | // proto_library(...) # user-defined rule |
| 366 | // |
| 367 | // We will piggyback support for the legacy semantics on the |
| 368 | // AllowGlobalReassign flag, which is loosely related and also |
| 369 | // required for Bazel. |
| 370 | if AllowGlobalReassign && r.env.isModule() { |
| 371 | r.useGlobal(id) |
| 372 | return |
| 373 | } |
| 374 | |
Alan Donovan | 312d1a5 | 2017-10-02 10:10:28 -0400 | [diff] [blame] | 375 | b := r.container() |
| 376 | b.uses = append(b.uses, use{id, r.env}) |
| 377 | } |
| 378 | |
alandonovan | a1b28d8 | 2018-03-13 10:59:24 -0400 | [diff] [blame] | 379 | func (r *resolver) useGlobal(id *syntax.Ident) binding { |
| 380 | var scope Scope |
| 381 | if prev, ok := r.globals[id.Name]; ok { |
Alan Donovan | 312d1a5 | 2017-10-02 10:10:28 -0400 | [diff] [blame] | 382 | scope = Global // use of global declared by module |
alandonovan | a1b28d8 | 2018-03-13 10:59:24 -0400 | [diff] [blame] | 383 | id.Index = prev.Index |
| 384 | } else if r.isPredeclared(id.Name) { |
| 385 | scope = Predeclared // use of pre-declared |
alandonovan | 631f7b5 | 2017-10-11 13:04:47 -0400 | [diff] [blame] | 386 | } else if r.isUniversal(id.Name) { |
| 387 | scope = Universal // use of universal name |
Alan Donovan | 312d1a5 | 2017-10-02 10:10:28 -0400 | [diff] [blame] | 388 | if !AllowFloat && id.Name == "float" { |
| 389 | r.errorf(id.NamePos, doesnt+"support floating point") |
| 390 | } |
| 391 | if !AllowSet && id.Name == "set" { |
| 392 | r.errorf(id.NamePos, doesnt+"support sets") |
| 393 | } |
Alan Donovan | 312d1a5 | 2017-10-02 10:10:28 -0400 | [diff] [blame] | 394 | } else { |
| 395 | scope = Undefined |
| 396 | r.errorf(id.NamePos, "undefined: %s", id.Name) |
| 397 | } |
| 398 | id.Scope = uint8(scope) |
alandonovan | a1b28d8 | 2018-03-13 10:59:24 -0400 | [diff] [blame] | 399 | return binding{scope, id.Index} |
Alan Donovan | 312d1a5 | 2017-10-02 10:10:28 -0400 | [diff] [blame] | 400 | } |
| 401 | |
| 402 | // resolveLocalUses is called when leaving a container (function/module) |
| 403 | // block. It resolves all uses of locals within that block. |
| 404 | func (b *block) resolveLocalUses() { |
| 405 | unresolved := b.uses[:0] |
| 406 | for _, use := range b.uses { |
| 407 | if bind := lookupLocal(use); bind.scope == Local { |
| 408 | use.id.Scope = uint8(bind.scope) |
| 409 | use.id.Index = bind.index |
| 410 | } else { |
| 411 | unresolved = append(unresolved, use) |
| 412 | } |
| 413 | } |
| 414 | b.uses = unresolved |
| 415 | } |
| 416 | |
| 417 | func (r *resolver) stmts(stmts []syntax.Stmt) { |
| 418 | for _, stmt := range stmts { |
| 419 | r.stmt(stmt) |
| 420 | } |
| 421 | } |
| 422 | |
| 423 | func (r *resolver) stmt(stmt syntax.Stmt) { |
| 424 | switch stmt := stmt.(type) { |
| 425 | case *syntax.ExprStmt: |
| 426 | r.expr(stmt.X) |
| 427 | |
| 428 | case *syntax.BranchStmt: |
| 429 | if r.loops == 0 && (stmt.Token == syntax.BREAK || stmt.Token == syntax.CONTINUE) { |
| 430 | r.errorf(stmt.TokenPos, "%s not in a loop", stmt.Token) |
| 431 | } |
| 432 | |
| 433 | case *syntax.IfStmt: |
| 434 | if r.container().function == nil { |
| 435 | r.errorf(stmt.If, "if statement not within a function") |
| 436 | } |
| 437 | r.expr(stmt.Cond) |
| 438 | r.stmts(stmt.True) |
| 439 | r.stmts(stmt.False) |
| 440 | |
| 441 | case *syntax.AssignStmt: |
Hittorp | 0a5e39a | 2018-08-09 15:02:30 +0300 | [diff] [blame] | 442 | if !AllowBitwise { |
| 443 | switch stmt.Op { |
| 444 | case syntax.AMP_EQ, syntax.PIPE_EQ, syntax.CIRCUMFLEX_EQ, syntax.LTLT_EQ, syntax.GTGT_EQ: |
| 445 | r.errorf(stmt.OpPos, doesnt+"support bitwise operations") |
| 446 | } |
| 447 | } |
Alan Donovan | 312d1a5 | 2017-10-02 10:10:28 -0400 | [diff] [blame] | 448 | r.expr(stmt.RHS) |
| 449 | // x += y may be a re-binding of a global variable, |
| 450 | // but we cannot tell without knowing the type of x. |
| 451 | // (If x is a list it's equivalent to x.extend(y).) |
| 452 | // The use is conservatively treated as binding, |
| 453 | // but we suppress the error if it's an already-bound global. |
| 454 | isAugmented := stmt.Op != syntax.EQ |
| 455 | r.assign(stmt.LHS, isAugmented) |
| 456 | |
| 457 | case *syntax.DefStmt: |
| 458 | if !AllowNestedDef && r.container().function != nil { |
| 459 | r.errorf(stmt.Def, doesnt+"support nested def") |
| 460 | } |
| 461 | const allowRebind = false |
| 462 | r.bind(stmt.Name, allowRebind) |
| 463 | r.function(stmt.Def, stmt.Name.Name, &stmt.Function) |
| 464 | |
| 465 | case *syntax.ForStmt: |
| 466 | if r.container().function == nil { |
| 467 | r.errorf(stmt.For, "for loop not within a function") |
| 468 | } |
| 469 | r.expr(stmt.X) |
| 470 | const allowRebind = false |
| 471 | r.assign(stmt.Vars, allowRebind) |
| 472 | r.loops++ |
| 473 | r.stmts(stmt.Body) |
| 474 | r.loops-- |
| 475 | |
Alessandro Arzilli | 678bafe | 2018-12-07 17:28:35 +0100 | [diff] [blame] | 476 | case *syntax.WhileStmt: |
| 477 | if !AllowRecursion { |
| 478 | r.errorf(stmt.While, "while loop not allowed") |
| 479 | } |
| 480 | if r.container().function == nil { |
| 481 | r.errorf(stmt.While, "while loop not within a function") |
| 482 | } |
| 483 | r.expr(stmt.Cond) |
| 484 | r.loops++ |
| 485 | r.stmts(stmt.Body) |
| 486 | r.loops-- |
| 487 | |
Alan Donovan | 312d1a5 | 2017-10-02 10:10:28 -0400 | [diff] [blame] | 488 | case *syntax.ReturnStmt: |
| 489 | if r.container().function == nil { |
| 490 | r.errorf(stmt.Return, "return statement not within a function") |
| 491 | } |
| 492 | if stmt.Result != nil { |
| 493 | r.expr(stmt.Result) |
| 494 | } |
| 495 | |
| 496 | case *syntax.LoadStmt: |
| 497 | if r.container().function != nil { |
| 498 | r.errorf(stmt.Load, "load statement within a function") |
| 499 | } |
| 500 | |
| 501 | const allowRebind = false |
| 502 | for i, from := range stmt.From { |
| 503 | if from.Name == "" { |
| 504 | r.errorf(from.NamePos, "load: empty identifier") |
| 505 | continue |
| 506 | } |
| 507 | if from.Name[0] == '_' { |
| 508 | r.errorf(from.NamePos, "load: names with leading underscores are not exported: %s", from.Name) |
| 509 | } |
| 510 | r.bind(stmt.To[i], allowRebind) |
| 511 | } |
| 512 | |
| 513 | default: |
| 514 | log.Fatalf("unexpected stmt %T", stmt) |
| 515 | } |
| 516 | } |
| 517 | |
| 518 | func (r *resolver) assign(lhs syntax.Expr, isAugmented bool) { |
| 519 | switch lhs := lhs.(type) { |
| 520 | case *syntax.Ident: |
| 521 | // x = ... |
| 522 | allowRebind := isAugmented |
| 523 | r.bind(lhs, allowRebind) |
| 524 | |
| 525 | case *syntax.IndexExpr: |
| 526 | // x[i] = ... |
| 527 | r.expr(lhs.X) |
| 528 | r.expr(lhs.Y) |
| 529 | |
| 530 | case *syntax.DotExpr: |
| 531 | // x.f = ... |
| 532 | r.expr(lhs.X) |
| 533 | |
| 534 | case *syntax.TupleExpr: |
| 535 | // (x, y) = ... |
| 536 | if len(lhs.List) == 0 { |
| 537 | r.errorf(syntax.Start(lhs), "can't assign to ()") |
| 538 | } |
| 539 | if isAugmented { |
| 540 | r.errorf(syntax.Start(lhs), "can't use tuple expression in augmented assignment") |
| 541 | } |
| 542 | for _, elem := range lhs.List { |
| 543 | r.assign(elem, isAugmented) |
| 544 | } |
| 545 | |
| 546 | case *syntax.ListExpr: |
| 547 | // [x, y, z] = ... |
| 548 | if len(lhs.List) == 0 { |
| 549 | r.errorf(syntax.Start(lhs), "can't assign to []") |
| 550 | } |
| 551 | if isAugmented { |
| 552 | r.errorf(syntax.Start(lhs), "can't use list expression in augmented assignment") |
| 553 | } |
| 554 | for _, elem := range lhs.List { |
| 555 | r.assign(elem, isAugmented) |
| 556 | } |
| 557 | |
Laurent Le Brun | 28ceca7 | 2018-02-26 15:01:53 +0100 | [diff] [blame] | 558 | case *syntax.ParenExpr: |
| 559 | r.assign(lhs.X, isAugmented) |
| 560 | |
Alan Donovan | 312d1a5 | 2017-10-02 10:10:28 -0400 | [diff] [blame] | 561 | default: |
| 562 | name := strings.ToLower(strings.TrimPrefix(fmt.Sprintf("%T", lhs), "*syntax.")) |
| 563 | r.errorf(syntax.Start(lhs), "can't assign to %s", name) |
| 564 | } |
| 565 | } |
| 566 | |
| 567 | func (r *resolver) expr(e syntax.Expr) { |
| 568 | switch e := e.(type) { |
| 569 | case *syntax.Ident: |
| 570 | r.use(e) |
| 571 | |
| 572 | case *syntax.Literal: |
| 573 | if !AllowFloat && e.Token == syntax.FLOAT { |
| 574 | r.errorf(e.TokenPos, doesnt+"support floating point") |
| 575 | } |
| 576 | |
| 577 | case *syntax.ListExpr: |
| 578 | for _, x := range e.List { |
| 579 | r.expr(x) |
| 580 | } |
| 581 | |
| 582 | case *syntax.CondExpr: |
| 583 | r.expr(e.Cond) |
| 584 | r.expr(e.True) |
| 585 | r.expr(e.False) |
| 586 | |
| 587 | case *syntax.IndexExpr: |
| 588 | r.expr(e.X) |
| 589 | r.expr(e.Y) |
| 590 | |
| 591 | case *syntax.DictEntry: |
| 592 | r.expr(e.Key) |
| 593 | r.expr(e.Value) |
| 594 | |
| 595 | case *syntax.SliceExpr: |
| 596 | r.expr(e.X) |
| 597 | if e.Lo != nil { |
| 598 | r.expr(e.Lo) |
| 599 | } |
| 600 | if e.Hi != nil { |
| 601 | r.expr(e.Hi) |
| 602 | } |
| 603 | if e.Step != nil { |
| 604 | r.expr(e.Step) |
| 605 | } |
| 606 | |
| 607 | case *syntax.Comprehension: |
alandonovan | 0d5491b | 2018-03-30 10:42:13 -0400 | [diff] [blame] | 608 | // The 'in' operand of the first clause (always a ForClause) |
| 609 | // is resolved in the outer block; consider: [x for x in x]. |
| 610 | clause := e.Clauses[0].(*syntax.ForClause) |
| 611 | r.expr(clause.X) |
| 612 | |
Alan Donovan | 312d1a5 | 2017-10-02 10:10:28 -0400 | [diff] [blame] | 613 | // A list/dict comprehension defines a new lexical block. |
| 614 | // Locals defined within the block will be allotted |
| 615 | // distinct slots in the locals array of the innermost |
| 616 | // enclosing container (function/module) block. |
| 617 | r.push(&block{comp: e}) |
alandonovan | 0d5491b | 2018-03-30 10:42:13 -0400 | [diff] [blame] | 618 | |
Alan Donovan | 312d1a5 | 2017-10-02 10:10:28 -0400 | [diff] [blame] | 619 | const allowRebind = false |
alandonovan | 0d5491b | 2018-03-30 10:42:13 -0400 | [diff] [blame] | 620 | r.assign(clause.Vars, allowRebind) |
| 621 | |
| 622 | for _, clause := range e.Clauses[1:] { |
Alan Donovan | 312d1a5 | 2017-10-02 10:10:28 -0400 | [diff] [blame] | 623 | switch clause := clause.(type) { |
| 624 | case *syntax.IfClause: |
| 625 | r.expr(clause.Cond) |
| 626 | case *syntax.ForClause: |
| 627 | r.assign(clause.Vars, allowRebind) |
| 628 | r.expr(clause.X) |
| 629 | } |
| 630 | } |
| 631 | r.expr(e.Body) // body may be *DictEntry |
| 632 | r.pop() |
| 633 | |
| 634 | case *syntax.TupleExpr: |
| 635 | for _, x := range e.List { |
| 636 | r.expr(x) |
| 637 | } |
| 638 | |
| 639 | case *syntax.DictExpr: |
| 640 | for _, entry := range e.List { |
| 641 | entry := entry.(*syntax.DictEntry) |
| 642 | r.expr(entry.Key) |
| 643 | r.expr(entry.Value) |
| 644 | } |
| 645 | |
| 646 | case *syntax.UnaryExpr: |
Hittorp | 0a5e39a | 2018-08-09 15:02:30 +0300 | [diff] [blame] | 647 | if !AllowBitwise && e.Op == syntax.TILDE { |
| 648 | r.errorf(e.OpPos, doesnt+"support bitwise operations") |
| 649 | } |
Alan Donovan | 312d1a5 | 2017-10-02 10:10:28 -0400 | [diff] [blame] | 650 | r.expr(e.X) |
| 651 | |
| 652 | case *syntax.BinaryExpr: |
| 653 | if !AllowFloat && e.Op == syntax.SLASH { |
| 654 | r.errorf(e.OpPos, doesnt+"support floating point (use //)") |
| 655 | } |
Hittorp | 0a5e39a | 2018-08-09 15:02:30 +0300 | [diff] [blame] | 656 | if !AllowBitwise { |
| 657 | switch e.Op { |
| 658 | case syntax.AMP, syntax.PIPE, syntax.CIRCUMFLEX, syntax.LTLT, syntax.GTGT: |
| 659 | r.errorf(e.OpPos, doesnt+"support bitwise operations") |
| 660 | } |
| 661 | } |
Alan Donovan | 312d1a5 | 2017-10-02 10:10:28 -0400 | [diff] [blame] | 662 | r.expr(e.X) |
| 663 | r.expr(e.Y) |
| 664 | |
| 665 | case *syntax.DotExpr: |
| 666 | r.expr(e.X) |
| 667 | // ignore e.Name |
| 668 | |
| 669 | case *syntax.CallExpr: |
| 670 | r.expr(e.Fn) |
alandonovan | 2c65f9e | 2018-12-14 13:46:48 -0500 | [diff] [blame^] | 671 | var seenVarargs, seenKwargs bool |
| 672 | var seenName map[string]bool |
Alan Donovan | 312d1a5 | 2017-10-02 10:10:28 -0400 | [diff] [blame] | 673 | for _, arg := range e.Args { |
| 674 | pos, _ := arg.Span() |
| 675 | if unop, ok := arg.(*syntax.UnaryExpr); ok && unop.Op == syntax.STARSTAR { |
| 676 | // **kwargs |
| 677 | if seenKwargs { |
| 678 | r.errorf(pos, "multiple **kwargs not allowed") |
| 679 | } |
| 680 | seenKwargs = true |
| 681 | r.expr(arg) |
| 682 | } else if ok && unop.Op == syntax.STAR { |
| 683 | // *args |
| 684 | if seenKwargs { |
| 685 | r.errorf(pos, "*args may not follow **kwargs") |
| 686 | } else if seenVarargs { |
| 687 | r.errorf(pos, "multiple *args not allowed") |
| 688 | } |
| 689 | seenVarargs = true |
| 690 | r.expr(arg) |
| 691 | } else if binop, ok := arg.(*syntax.BinaryExpr); ok && binop.Op == syntax.EQ { |
| 692 | // k=v |
| 693 | if seenKwargs { |
| 694 | r.errorf(pos, "argument may not follow **kwargs") |
| 695 | } |
alandonovan | 2c65f9e | 2018-12-14 13:46:48 -0500 | [diff] [blame^] | 696 | x := binop.X.(*syntax.Ident) |
| 697 | if seenName[x.Name] { |
| 698 | r.errorf(x.NamePos, "keyword argument %s repeated", x.Name) |
| 699 | } else { |
| 700 | if seenName == nil { |
| 701 | seenName = make(map[string]bool) |
| 702 | } |
| 703 | seenName[x.Name] = true |
| 704 | } |
Alan Donovan | 312d1a5 | 2017-10-02 10:10:28 -0400 | [diff] [blame] | 705 | r.expr(binop.Y) |
| 706 | } else { |
| 707 | // positional argument |
| 708 | if seenVarargs { |
| 709 | r.errorf(pos, "argument may not follow *args") |
| 710 | } else if seenKwargs { |
| 711 | r.errorf(pos, "argument may not follow **kwargs") |
alandonovan | 2c65f9e | 2018-12-14 13:46:48 -0500 | [diff] [blame^] | 712 | } else if len(seenName) > 0 { |
Alan Donovan | 1b9d0e7 | 2017-11-28 14:19:10 -0500 | [diff] [blame] | 713 | r.errorf(pos, "positional argument may not follow named") |
Alan Donovan | 312d1a5 | 2017-10-02 10:10:28 -0400 | [diff] [blame] | 714 | } |
| 715 | r.expr(arg) |
| 716 | } |
| 717 | } |
| 718 | |
| 719 | case *syntax.LambdaExpr: |
| 720 | if !AllowLambda { |
| 721 | r.errorf(e.Lambda, doesnt+"support lambda") |
| 722 | } |
| 723 | r.function(e.Lambda, "lambda", &e.Function) |
| 724 | |
Laurent Le Brun | 28ceca7 | 2018-02-26 15:01:53 +0100 | [diff] [blame] | 725 | case *syntax.ParenExpr: |
| 726 | r.expr(e.X) |
| 727 | |
Alan Donovan | 312d1a5 | 2017-10-02 10:10:28 -0400 | [diff] [blame] | 728 | default: |
| 729 | log.Fatalf("unexpected expr %T", e) |
| 730 | } |
| 731 | } |
| 732 | |
| 733 | func (r *resolver) function(pos syntax.Position, name string, function *syntax.Function) { |
| 734 | // Resolve defaults in enclosing environment. |
| 735 | for _, param := range function.Params { |
| 736 | if binary, ok := param.(*syntax.BinaryExpr); ok { |
| 737 | r.expr(binary.Y) |
| 738 | } |
| 739 | } |
| 740 | |
| 741 | // Enter function block. |
| 742 | b := &block{function: function} |
| 743 | r.push(b) |
| 744 | |
| 745 | const allowRebind = false |
alandonovan | b7e3b1f | 2018-12-12 17:14:58 -0500 | [diff] [blame] | 746 | var seenOptional bool |
| 747 | var star, starStar *syntax.Ident // *args, **kwargs |
Alan Donovan | 312d1a5 | 2017-10-02 10:10:28 -0400 | [diff] [blame] | 748 | for _, param := range function.Params { |
| 749 | switch param := param.(type) { |
| 750 | case *syntax.Ident: |
| 751 | // e.g. x |
alandonovan | b7e3b1f | 2018-12-12 17:14:58 -0500 | [diff] [blame] | 752 | if starStar != nil { |
| 753 | r.errorf(param.NamePos, "required parameter may not follow **%s", starStar.Name) |
| 754 | } else if star != nil { |
| 755 | r.errorf(param.NamePos, "required parameter may not follow * parameter") |
Alan Donovan | 1b9d0e7 | 2017-11-28 14:19:10 -0500 | [diff] [blame] | 756 | } else if seenOptional { |
alandonovan | b7e3b1f | 2018-12-12 17:14:58 -0500 | [diff] [blame] | 757 | r.errorf(param.NamePos, "required parameter may not follow optional") |
Alan Donovan | 312d1a5 | 2017-10-02 10:10:28 -0400 | [diff] [blame] | 758 | } |
| 759 | if r.bind(param, allowRebind) { |
alandonovan | b7e3b1f | 2018-12-12 17:14:58 -0500 | [diff] [blame] | 760 | r.errorf(param.NamePos, "duplicate parameter: %s", param.Name) |
Alan Donovan | 312d1a5 | 2017-10-02 10:10:28 -0400 | [diff] [blame] | 761 | } |
| 762 | |
| 763 | case *syntax.BinaryExpr: |
| 764 | // e.g. y=dflt |
alandonovan | b7e3b1f | 2018-12-12 17:14:58 -0500 | [diff] [blame] | 765 | if starStar != nil { |
| 766 | r.errorf(param.OpPos, "optional parameter may not follow **%s", starStar.Name) |
| 767 | } else if star != nil { |
| 768 | r.errorf(param.OpPos, "optional parameter may not follow *%s", star.Name) |
Alan Donovan | 312d1a5 | 2017-10-02 10:10:28 -0400 | [diff] [blame] | 769 | } |
| 770 | if id := param.X.(*syntax.Ident); r.bind(id, allowRebind) { |
alandonovan | b7e3b1f | 2018-12-12 17:14:58 -0500 | [diff] [blame] | 771 | r.errorf(param.OpPos, "duplicate parameter: %s", id.Name) |
Alan Donovan | 312d1a5 | 2017-10-02 10:10:28 -0400 | [diff] [blame] | 772 | } |
Alan Donovan | 1b9d0e7 | 2017-11-28 14:19:10 -0500 | [diff] [blame] | 773 | seenOptional = true |
Alan Donovan | 312d1a5 | 2017-10-02 10:10:28 -0400 | [diff] [blame] | 774 | |
| 775 | case *syntax.UnaryExpr: |
| 776 | // *args or **kwargs |
alandonovan | b7e3b1f | 2018-12-12 17:14:58 -0500 | [diff] [blame] | 777 | id := param.X.(*syntax.Ident) |
Alan Donovan | 312d1a5 | 2017-10-02 10:10:28 -0400 | [diff] [blame] | 778 | if param.Op == syntax.STAR { |
alandonovan | b7e3b1f | 2018-12-12 17:14:58 -0500 | [diff] [blame] | 779 | if starStar != nil { |
| 780 | r.errorf(param.OpPos, "*%s parameter may not follow **%s", id.Name, starStar.Name) |
| 781 | } else if star != nil { |
| 782 | r.errorf(param.OpPos, "multiple * parameters not allowed") |
| 783 | } else { |
| 784 | star = id |
Alan Donovan | 312d1a5 | 2017-10-02 10:10:28 -0400 | [diff] [blame] | 785 | } |
Alan Donovan | 312d1a5 | 2017-10-02 10:10:28 -0400 | [diff] [blame] | 786 | } else { |
alandonovan | b7e3b1f | 2018-12-12 17:14:58 -0500 | [diff] [blame] | 787 | if starStar != nil { |
| 788 | r.errorf(param.OpPos, "multiple ** parameters not allowed") |
| 789 | } else { |
| 790 | starStar = id |
Alan Donovan | 312d1a5 | 2017-10-02 10:10:28 -0400 | [diff] [blame] | 791 | } |
Alan Donovan | 312d1a5 | 2017-10-02 10:10:28 -0400 | [diff] [blame] | 792 | } |
alandonovan | b7e3b1f | 2018-12-12 17:14:58 -0500 | [diff] [blame] | 793 | if r.bind(id, allowRebind) { |
| 794 | r.errorf(id.NamePos, "duplicate parameter: %s", id.Name) |
Alan Donovan | 312d1a5 | 2017-10-02 10:10:28 -0400 | [diff] [blame] | 795 | } |
| 796 | } |
| 797 | } |
alandonovan | b7e3b1f | 2018-12-12 17:14:58 -0500 | [diff] [blame] | 798 | function.HasVarargs = star != nil |
| 799 | function.HasKwargs = starStar != nil |
Alan Donovan | 312d1a5 | 2017-10-02 10:10:28 -0400 | [diff] [blame] | 800 | r.stmts(function.Body) |
| 801 | |
| 802 | // Resolve all uses of this function's local vars, |
| 803 | // and keep just the remaining uses of free/global vars. |
| 804 | b.resolveLocalUses() |
| 805 | |
| 806 | // Leave function block. |
| 807 | r.pop() |
| 808 | |
| 809 | // References within the function body to globals are not |
| 810 | // resolved until the end of the module. |
| 811 | } |
| 812 | |
| 813 | func (r *resolver) resolveNonLocalUses(b *block) { |
| 814 | // First resolve inner blocks. |
| 815 | for _, child := range b.children { |
| 816 | r.resolveNonLocalUses(child) |
| 817 | } |
| 818 | for _, use := range b.uses { |
| 819 | bind := r.lookupLexical(use.id, use.env) |
| 820 | use.id.Scope = uint8(bind.scope) |
| 821 | use.id.Index = bind.index |
| 822 | } |
| 823 | } |
| 824 | |
| 825 | // lookupLocal looks up an identifier within its immediately enclosing function. |
| 826 | func lookupLocal(use use) binding { |
| 827 | for env := use.env; env != nil; env = env.parent { |
| 828 | if bind, ok := env.bindings[use.id.Name]; ok { |
| 829 | if bind.scope == Free { |
| 830 | // shouldn't exist till later |
| 831 | log.Fatalf("%s: internal error: %s, %d", use.id.NamePos, use.id.Name, bind) |
| 832 | } |
| 833 | return bind // found |
| 834 | } |
| 835 | if env.function != nil { |
| 836 | break |
| 837 | } |
| 838 | } |
| 839 | return binding{} // not found in this function |
| 840 | } |
| 841 | |
| 842 | // lookupLexical looks up an identifier within its lexically enclosing environment. |
| 843 | func (r *resolver) lookupLexical(id *syntax.Ident, env *block) (bind binding) { |
| 844 | if debug { |
| 845 | fmt.Printf("lookupLexical %s in %s = ...\n", id.Name, env) |
| 846 | defer func() { fmt.Printf("= %d\n", bind) }() |
| 847 | } |
| 848 | |
| 849 | // Is this the module block? |
| 850 | if env.isModule() { |
alandonovan | a1b28d8 | 2018-03-13 10:59:24 -0400 | [diff] [blame] | 851 | return r.useGlobal(id) // global, predeclared, or not found |
Alan Donovan | 312d1a5 | 2017-10-02 10:10:28 -0400 | [diff] [blame] | 852 | } |
| 853 | |
| 854 | // Defined in this block? |
| 855 | bind, ok := env.bindings[id.Name] |
| 856 | if !ok { |
| 857 | // Defined in parent block? |
| 858 | bind = r.lookupLexical(id, env.parent) |
| 859 | if env.function != nil && (bind.scope == Local || bind.scope == Free) { |
| 860 | // Found in parent block, which belongs to enclosing function. |
| 861 | id := &syntax.Ident{ |
| 862 | Name: id.Name, |
| 863 | Scope: uint8(bind.scope), |
| 864 | Index: bind.index, |
| 865 | } |
| 866 | bind.scope = Free |
| 867 | bind.index = len(env.function.FreeVars) |
| 868 | env.function.FreeVars = append(env.function.FreeVars, id) |
| 869 | if debug { |
| 870 | fmt.Printf("creating freevar %v in function at %s: %s\n", |
| 871 | len(env.function.FreeVars), fmt.Sprint(env.function.Span()), id.Name) |
| 872 | } |
| 873 | } |
| 874 | |
| 875 | // Memoize, to avoid duplicate free vars |
| 876 | // and redundant global (failing) lookups. |
| 877 | env.bind(id.Name, bind) |
| 878 | } |
| 879 | return bind |
| 880 | } |