mirror of
https://github.com/Reuh/anselme.git
synced 2025-10-27 16:49:31 +00:00
Closures are not affected by following redefinitions of upvalues
This commit is contained in:
parent
3d10b1b15c
commit
d1818d10b1
2 changed files with 33 additions and 2 deletions
|
|
@ -85,6 +85,14 @@ local Environment = ast.abstract.Runtime {
|
||||||
self._lookup_cache = {}
|
self._lookup_cache = {}
|
||||||
self._lookup_cache_current = {}
|
self._lookup_cache_current = {}
|
||||||
end,
|
end,
|
||||||
|
-- precache variable
|
||||||
|
-- when cached, if a variable is defined in a parent scope after it has been cached here from a higher parent, it will not be used in this env
|
||||||
|
-- most of the time scopes are discarded after a pop so there's no possibility for this anyway, except for closures as they restore an old environment
|
||||||
|
-- in which case we may want to precache variables that appear in the function so future definitions don't affect the closure
|
||||||
|
precache = function(self, state, identifier)
|
||||||
|
self:_lookup(state, identifier)
|
||||||
|
self:_lookup_in_current(state, identifier:to_symbol())
|
||||||
|
end,
|
||||||
|
|
||||||
traverse = function(self, fn, ...)
|
traverse = function(self, fn, ...)
|
||||||
if self.parent then
|
if self.parent then
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,15 @@ local operator_priority = require("anselme.common").operator_priority
|
||||||
|
|
||||||
local resume_manager, calling_environment_manager
|
local resume_manager, calling_environment_manager
|
||||||
|
|
||||||
|
local function list_upvalues(v, l)
|
||||||
|
if ast.Identifier:is(v) then
|
||||||
|
table.insert(l, v)
|
||||||
|
elseif ast.Symbol:is(v) then
|
||||||
|
table.insert(l, v:to_identifier())
|
||||||
|
end
|
||||||
|
v:traverse(list_upvalues, l)
|
||||||
|
end
|
||||||
|
|
||||||
local Function
|
local Function
|
||||||
Function = Overloadable {
|
Function = Overloadable {
|
||||||
type = "function",
|
type = "function",
|
||||||
|
|
@ -15,11 +24,13 @@ Function = Overloadable {
|
||||||
parameters = nil, -- ParameterTuple
|
parameters = nil, -- ParameterTuple
|
||||||
expression = nil, -- function content
|
expression = nil, -- function content
|
||||||
scope = nil, -- Environment; captured scope for closure (evaluated functions); not set when not evaluated
|
scope = nil, -- Environment; captured scope for closure (evaluated functions); not set when not evaluated
|
||||||
|
upvalues = nil, -- list of identifiers; not set when not evaluated. Contain _at least_ all the upvalues explicitely defined in the function code.
|
||||||
|
|
||||||
init = function(self, parameters, expression, scope)
|
init = function(self, parameters, expression, scope, upvalues)
|
||||||
self.parameters = parameters
|
self.parameters = parameters
|
||||||
self.expression = expression
|
self.expression = expression
|
||||||
self.scope = scope
|
self.scope = scope
|
||||||
|
self.upvalues = upvalues
|
||||||
end,
|
end,
|
||||||
with_return_boundary = function(self, parameters, expression)
|
with_return_boundary = function(self, parameters, expression)
|
||||||
return Function:new(parameters, ReturnBoundary:new(expression))
|
return Function:new(parameters, ReturnBoundary:new(expression))
|
||||||
|
|
@ -51,7 +62,19 @@ Function = Overloadable {
|
||||||
local scope = state.scope:capture() -- capture current scope to build closure
|
local scope = state.scope:capture() -- capture current scope to build closure
|
||||||
state.scope:pop()
|
state.scope:pop()
|
||||||
|
|
||||||
return Function:new(self.parameters:eval(state), self.expression, scope)
|
-- get upvalues
|
||||||
|
local upvalues = {}
|
||||||
|
self.expression:traverse(list_upvalues, upvalues)
|
||||||
|
if scope:defined(state, ast.Identifier:new("_")) then
|
||||||
|
scope:get(state, ast.Identifier:new("_")):traverse(list_upvalues)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- cache upvalues so they aren't affected by future redefinition in a parent scope
|
||||||
|
for _, ident in ipairs(upvalues) do
|
||||||
|
scope:precache(state, ident)
|
||||||
|
end
|
||||||
|
|
||||||
|
return Function:new(self.parameters:eval(state), self.expression, scope, upvalues)
|
||||||
end,
|
end,
|
||||||
|
|
||||||
compatible_with_arguments = function(self, state, args)
|
compatible_with_arguments = function(self, state, args)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue