mirror of
https://github.com/Reuh/anselme.git
synced 2025-10-27 16:49:31 +00:00
Merge Closure into Function and add calling_environment_manager
This commit is contained in:
parent
18dd3ad6bd
commit
c2bec6f5d2
8 changed files with 105 additions and 106 deletions
|
|
@ -1,77 +0,0 @@
|
|||
-- note: functions only appear in non-evaluated nodes! once evaluated, they always become closures
|
||||
|
||||
local ast = require("anselme.ast")
|
||||
local Overloadable, Runtime = ast.abstract.Overloadable, ast.abstract.Runtime
|
||||
|
||||
local resume_manager
|
||||
|
||||
local Closure
|
||||
Closure = Runtime(Overloadable) {
|
||||
type = "closure",
|
||||
|
||||
func = nil, -- Function
|
||||
scope = nil, -- Environment
|
||||
|
||||
init = function(self, func, state)
|
||||
self.func = func
|
||||
|
||||
-- layer a new scope layer on top of captured/current scope
|
||||
-- to allow future define in the function (fn.:var = "foo")
|
||||
state.scope:push()
|
||||
self.scope = state.scope:capture()
|
||||
state.scope:define(ast.Symbol:new("_calling_environment"), self.scope)
|
||||
state.scope:pop()
|
||||
end,
|
||||
|
||||
_format = function(self, ...)
|
||||
return self.func:format(...)
|
||||
end,
|
||||
|
||||
traverse = function(self, fn, ...)
|
||||
fn(self.func, ...)
|
||||
fn(self.scope, ...)
|
||||
end,
|
||||
|
||||
compatible_with_arguments = function(self, state, args)
|
||||
return args:match_parameter_tuple(state, self.func.parameters)
|
||||
end,
|
||||
format_parameters = function(self, state)
|
||||
return self.func.parameters:format(state)
|
||||
end,
|
||||
hash_parameters = function(self)
|
||||
return self.func.parameters:hash()
|
||||
end,
|
||||
call_dispatched = function(self, state, args)
|
||||
local calling_environment = state.scope:capture()
|
||||
state.scope:push(self.scope)
|
||||
state.scope:set(ast.Identifier:new("_calling_environment"), calling_environment)
|
||||
local exp = self.func:call_dispatched(state, args)
|
||||
state.scope:pop()
|
||||
return exp
|
||||
end,
|
||||
resume = function(self, state, target)
|
||||
if self.func.parameters.min_arity > 0 then error("can't resume function with parameters") end
|
||||
local calling_environment = state.scope:capture()
|
||||
state.scope:push(self.scope)
|
||||
state.scope:set(ast.Identifier:new("_calling_environment"), calling_environment)
|
||||
resume_manager:push(state, target)
|
||||
local exp = self.func:call(state, ast.ArgumentTuple:new())
|
||||
resume_manager:pop(state)
|
||||
state.scope:pop()
|
||||
return exp
|
||||
end,
|
||||
get_level = function(self, state, level) -- TODO make generic to all calls? only closures?
|
||||
local env = state.scope:capture()
|
||||
while level > 0 do
|
||||
assert(env:defined(state, ast.Identifier:new("_calling_environment")))
|
||||
env = env:get(state, ast.Identifier:new("_calling_environment"))
|
||||
level = level - 1
|
||||
end
|
||||
return env
|
||||
end
|
||||
}
|
||||
|
||||
package.loaded[...] = Closure
|
||||
resume_manager = require("anselme.state.resume_manager")
|
||||
|
||||
return Closure
|
||||
|
|
@ -2,20 +2,27 @@
|
|||
|
||||
local ast = require("anselme.ast")
|
||||
local Overloadable = ast.abstract.Overloadable
|
||||
local Closure, ReturnBoundary
|
||||
local ReturnBoundary
|
||||
|
||||
local operator_priority = require("anselme.common").operator_priority
|
||||
|
||||
local resume_manager, calling_environment_manager
|
||||
|
||||
local Function
|
||||
Function = Overloadable {
|
||||
type = "function",
|
||||
|
||||
parameters = nil, -- ParameterTuple
|
||||
expression = nil,
|
||||
expression = nil, -- function content
|
||||
scope = nil, -- Environment; captured scope for closure (evaluated functions); not set when not evaluated
|
||||
|
||||
init = function(self, parameters, expression)
|
||||
init = function(self, parameters, expression, scope)
|
||||
self.parameters = parameters
|
||||
self.expression = ReturnBoundary:new(expression)
|
||||
self.expression = expression
|
||||
self.scope = scope
|
||||
end,
|
||||
with_return_boundary = function(self, parameters, expression)
|
||||
return Function:new(parameters, ReturnBoundary:new(expression))
|
||||
end,
|
||||
|
||||
_format = function(self, ...)
|
||||
|
|
@ -32,6 +39,19 @@ Function = Overloadable {
|
|||
traverse = function(self, fn, ...)
|
||||
fn(self.parameters, ...)
|
||||
fn(self.expression, ...)
|
||||
if self.scope then
|
||||
fn(self.scope, ...)
|
||||
end
|
||||
end,
|
||||
|
||||
_eval = function(self, state)
|
||||
-- layer a new scope layer on top of captured/current scope
|
||||
-- to allow future define in the function (fn.:var = "foo")
|
||||
state.scope:push()
|
||||
local scope = state.scope:capture() -- capture current scope to build closure
|
||||
state.scope:pop()
|
||||
|
||||
return Function:new(self.parameters:eval(state), self.expression, scope)
|
||||
end,
|
||||
|
||||
compatible_with_arguments = function(self, state, args)
|
||||
|
|
@ -44,26 +64,51 @@ Function = Overloadable {
|
|||
return self.parameters:hash()
|
||||
end,
|
||||
call_dispatched = function(self, state, args)
|
||||
assert(self.scope, "can't call unevaluated function")
|
||||
|
||||
-- push captured closure scope
|
||||
local calling_environment = state.scope:capture()
|
||||
state.scope:push(self.scope)
|
||||
calling_environment_manager:push(state, calling_environment)
|
||||
|
||||
-- push function scope
|
||||
state.scope:push()
|
||||
args:bind_parameter_tuple(state, self.parameters)
|
||||
|
||||
local exp = self.expression:eval(state)
|
||||
|
||||
state.scope:pop()
|
||||
|
||||
-- reminder: don't do any additionnal processing here as that won't be executed when resuming self.expression directly
|
||||
-- which is done in a few places, notably to predefine exports in Closure
|
||||
-- instead wrap it in some additional node, like our friend ReturnBoundary
|
||||
|
||||
calling_environment_manager:pop(state)
|
||||
state.scope:pop()
|
||||
return exp
|
||||
end,
|
||||
resume = function(self, state, target)
|
||||
if self.parameters.min_arity > 0 then error("can't resume function with parameters") end
|
||||
assert(self.scope, "can't resume unevaluated function")
|
||||
|
||||
_eval = function(self, state)
|
||||
return Closure:new(Function:new(self.parameters:eval(state), self.expression), state)
|
||||
-- push captured closure scope
|
||||
local calling_environment = state.scope:capture()
|
||||
state.scope:push(self.scope)
|
||||
calling_environment_manager:push(state, calling_environment)
|
||||
|
||||
resume_manager:push(state, target)
|
||||
|
||||
-- push function scope
|
||||
state.scope:push()
|
||||
local exp = self.expression:eval(state)
|
||||
state.scope:pop()
|
||||
|
||||
resume_manager:pop(state)
|
||||
|
||||
calling_environment_manager:pop(state)
|
||||
state.scope:pop()
|
||||
return exp
|
||||
end,
|
||||
}
|
||||
|
||||
package.loaded[...] = Function
|
||||
Closure, ReturnBoundary = ast.Closure, ast.ReturnBoundary
|
||||
ReturnBoundary = ast.ReturnBoundary
|
||||
|
||||
resume_manager = require("anselme.state.resume_manager")
|
||||
calling_environment_manager = require("anselme.state.calling_environment_manager")
|
||||
|
||||
return Function
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ local Overloadable = ast.abstract.Overloadable
|
|||
local operator_priority = require("anselme.common").operator_priority
|
||||
local unpack = table.unpack or unpack
|
||||
|
||||
local calling_environment_manager
|
||||
|
||||
local LuaFunction
|
||||
LuaFunction = ast.abstract.Runtime(Overloadable) {
|
||||
type = "lua function",
|
||||
|
|
@ -45,6 +47,9 @@ LuaFunction = ast.abstract.Runtime(Overloadable) {
|
|||
return self.parameters:hash()
|
||||
end,
|
||||
call_dispatched = function(self, state, args)
|
||||
local calling_environment = state.scope:capture()
|
||||
calling_environment_manager:push(state, calling_environment)
|
||||
|
||||
local lua_args = { state }
|
||||
|
||||
state.scope:push()
|
||||
|
|
@ -58,6 +63,8 @@ LuaFunction = ast.abstract.Runtime(Overloadable) {
|
|||
|
||||
local r = self.func(unpack(lua_args))
|
||||
assert(r, "lua function returned no value")
|
||||
|
||||
calling_environment_manager:pop(state)
|
||||
return r
|
||||
end,
|
||||
|
||||
|
|
@ -70,4 +77,7 @@ LuaFunction = ast.abstract.Runtime(Overloadable) {
|
|||
end,
|
||||
}
|
||||
|
||||
package.loaded[...] = LuaFunction
|
||||
calling_environment_manager = require("anselme.state.calling_environment_manager")
|
||||
|
||||
return LuaFunction
|
||||
|
|
|
|||
|
|
@ -197,7 +197,7 @@ return primary {
|
|||
if not s then error(("invalid expression in function definition: %s"):format(right), 0) end
|
||||
|
||||
-- return function
|
||||
local fn = Function:new(parameters, right):set_source(source_start)
|
||||
local fn = Function:with_return_boundary(parameters, right):set_source(source_start)
|
||||
return Definition:new(symbol, fn):set_source(source_start), rem
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -30,6 +30,6 @@ return prefix {
|
|||
s, right, rem = pcall(expression_to_ast, source, rem, limit_pattern, self.priority)
|
||||
if not s then error(("invalid expression after unop %q: %s"):format(self.operator, right), 0) end
|
||||
|
||||
return Function:new(parameters, right):set_source(source_start), rem
|
||||
return Function:with_return_boundary(parameters, right):set_source(source_start), rem
|
||||
end
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,16 +2,17 @@ local ast = require("anselme.ast")
|
|||
local ArgumentTuple, Boolean, Nil = ast.ArgumentTuple, ast.Boolean, ast.Nil
|
||||
|
||||
local resume_manager = require("anselme.state.resume_manager")
|
||||
local calling_environment_manager = require("anselme.state.calling_environment_manager")
|
||||
|
||||
return {
|
||||
{
|
||||
"resume", "(function::closure, anchor::anchor)",
|
||||
"resume", "(function::function, anchor::anchor)",
|
||||
function(state, func, anchor)
|
||||
return func:resume(state, anchor)
|
||||
end
|
||||
},
|
||||
{
|
||||
"resume", "(function::closure, anchor::nil)",
|
||||
"resume", "(function::function, anchor::nil)",
|
||||
function(state, func)
|
||||
return func:call(state, ArgumentTuple:new())
|
||||
end
|
||||
|
|
@ -23,9 +24,9 @@ return {
|
|||
end
|
||||
},
|
||||
{
|
||||
"resuming", "(level::number)",
|
||||
"resuming", "(level::number=0)",
|
||||
function(state, level)
|
||||
local env = ast.Closure:get_level(state, level:to_lua(state))
|
||||
local env = calling_environment_manager:get_level(state, level:to_lua(state)+1)
|
||||
state.scope:push(env)
|
||||
local r = Boolean:new(resume_manager:resuming(state))
|
||||
state.scope:pop()
|
||||
|
|
|
|||
|
|
@ -1,22 +1,24 @@
|
|||
local ast = require("anselme.ast")
|
||||
local Nil, Boolean, Definition, Call, Function, ParameterTuple, FunctionParameter, Identifier, Overload, Assignment = ast.Nil, ast.Boolean, ast.Definition, ast.Call, ast.Function, ast.ParameterTuple, ast.FunctionParameter, ast.Identifier, ast.Overload, ast.Assignment
|
||||
local Nil, Boolean, Definition, Call, Function, ParameterTuple, FunctionParameter, Identifier, Overload, Assignment, Return = ast.Nil, ast.Boolean, ast.Definition, ast.Call, ast.Function, ast.ParameterTuple, ast.FunctionParameter, ast.Identifier, ast.Overload, ast.Assignment, ast.Return
|
||||
local assert0 = require("anselme.common").assert0
|
||||
|
||||
local calling_environment_manager = require("anselme.state.calling_environment_manager")
|
||||
|
||||
return {
|
||||
{
|
||||
"defined", "(c::closure, s::string)",
|
||||
"defined", "(c::function, s::string)",
|
||||
function(state, c, s)
|
||||
return Boolean:new(c.scope:defined_in_current_strict(state, s:to_identifier()))
|
||||
end
|
||||
},
|
||||
{
|
||||
"has upvalue", "(c::closure, s::string)",
|
||||
"has upvalue", "(c::function, s::string)",
|
||||
function(state, c, s)
|
||||
return Boolean:new(c.scope:defined(state, s:to_identifier()))
|
||||
end
|
||||
},
|
||||
{
|
||||
"_._", "(c::closure, s::string)",
|
||||
"_._", "(c::function, s::string)",
|
||||
function(state, c, s)
|
||||
local identifier = s:to_identifier()
|
||||
assert0(c.scope:defined(state, identifier), ("no variable %q defined in closure"):format(s.string))
|
||||
|
|
@ -24,7 +26,7 @@ return {
|
|||
end
|
||||
},
|
||||
{
|
||||
"_._", "(c::closure, s::string) = v",
|
||||
"_._", "(c::function, s::string) = v",
|
||||
function(state, c, s, v)
|
||||
local identifier = s:to_identifier()
|
||||
assert0(c.scope:defined(state, identifier), ("no variable %q defined in closure"):format(s.string))
|
||||
|
|
@ -33,7 +35,7 @@ return {
|
|||
end
|
||||
},
|
||||
{
|
||||
"_._", "(c::closure, s::symbol) = v",
|
||||
"_._", "(c::function, s::symbol) = v",
|
||||
function(state, c, s, v)
|
||||
state.scope:push(c.scope)
|
||||
local r = Definition:new(s, v):eval(state)
|
||||
|
|
@ -45,7 +47,7 @@ return {
|
|||
">_", "(q::is(\"quote\"))",
|
||||
function(state, q)
|
||||
local exp = q.expression
|
||||
local get = Function:new(ParameterTuple:new(), exp):eval(state)
|
||||
local get = Function:with_return_boundary(ParameterTuple:new(), exp):eval(state)
|
||||
|
||||
local set_exp
|
||||
if Call:is(exp) then
|
||||
|
|
@ -57,11 +59,29 @@ return {
|
|||
if set_exp then
|
||||
local set_param = ParameterTuple:new()
|
||||
set_param:insert_assignment(FunctionParameter:new(Identifier:new("value")))
|
||||
local set = Function:new(set_param, set_exp):eval(state)
|
||||
local set = Function:with_return_boundary(set_param, set_exp):eval(state)
|
||||
return Overload:new(get, set)
|
||||
else
|
||||
return get
|
||||
end
|
||||
end
|
||||
}
|
||||
{
|
||||
"attached block", "(level::number=1)",
|
||||
function(state, level)
|
||||
-- level 2: env of the function that called the function that called attached block
|
||||
local env = calling_environment_manager:get_level(state, level:to_lua(state)+1)
|
||||
local r = env:get(state, Identifier:new("_"))
|
||||
return Function:with_return_boundary(ParameterTuple:new(), r.expression):eval(state)
|
||||
end
|
||||
},
|
||||
{
|
||||
"attached block keep return", "(level::number=1)",
|
||||
function(state, level)
|
||||
-- level 2: env of the function that called the function that called attached block
|
||||
local env = calling_environment_manager:get_level(state, level:to_lua(state)+1)
|
||||
local r = env:get(state, Identifier:new("_"))
|
||||
return Function:new(ParameterTuple:new(), r.expression):eval(state)
|
||||
end
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ return {
|
|||
{ "struct", "(x)", function(state, x) return Boolean:new(x.type == "struct") end },
|
||||
{ "table", "(x)", function(state, x) return Boolean:new(x.type == "table") end },
|
||||
|
||||
{ "closure", "(x)", function(state, x) return Boolean:new(x.type == "closure") end },
|
||||
{ "function", "(x)", function(state, x) return Boolean:new(x.type == "function") end },
|
||||
{ "overload", "(x)", function(state, x) return Boolean:new(x.type == "overload") end },
|
||||
{ "function", "(x)", function(state, x) return Boolean:new(x.type == "overload" or x.type == "closure" or x.type == "funciton" or x.type == "lua function") end },
|
||||
{ "callable", "(x)", function(state, x) return Boolean:new(x.type == "overload" or x.type == "function" or x.type == "lua function" or x.type == "quote") end },
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue