The interpreter uses a chain of scopes: global (module) plus pushed frames for functions and blocks.
Lookup: identifiers resolve from innermost frame outward to global.
Assignment (x.setVar(v)):
if x exists in any frame, that
binding is updated (including globals from inside a function—how
shared module state works). Otherwise x
is created in the innermost frame.
Namespaces like
math.add are not variables; they
live on the interpreter’s library table, not the scope chain.
function.name(...) — parameters + localstest.ifTrue / ifFalse /
elseIf* / else bodies (when run)
test.while and test.forLooprandom.run bodyerror.try and each error.except handlerconvert* iteration bindingsconstructorDefine and
methodDefine bodies — parameters,
locals, and this bound to the instance
(OOP guide)
Full detail: someProgrammingLanguage/SCOPING.md
in the repo.