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 ast = require("anselme.ast")
|
||||||
local Overloadable = ast.abstract.Overloadable
|
local Overloadable = ast.abstract.Overloadable
|
||||||
local Closure, ReturnBoundary
|
local ReturnBoundary
|
||||||
|
|
||||||
local operator_priority = require("anselme.common").operator_priority
|
local operator_priority = require("anselme.common").operator_priority
|
||||||
|
|
||||||
|
local resume_manager, calling_environment_manager
|
||||||
|
|
||||||
local Function
|
local Function
|
||||||
Function = Overloadable {
|
Function = Overloadable {
|
||||||
type = "function",
|
type = "function",
|
||||||
|
|
||||||
parameters = nil, -- ParameterTuple
|
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.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,
|
end,
|
||||||
|
|
||||||
_format = function(self, ...)
|
_format = function(self, ...)
|
||||||
|
|
@ -32,6 +39,19 @@ Function = Overloadable {
|
||||||
traverse = function(self, fn, ...)
|
traverse = function(self, fn, ...)
|
||||||
fn(self.parameters, ...)
|
fn(self.parameters, ...)
|
||||||
fn(self.expression, ...)
|
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,
|
end,
|
||||||
|
|
||||||
compatible_with_arguments = function(self, state, args)
|
compatible_with_arguments = function(self, state, args)
|
||||||
|
|
@ -44,26 +64,51 @@ Function = Overloadable {
|
||||||
return self.parameters:hash()
|
return self.parameters:hash()
|
||||||
end,
|
end,
|
||||||
call_dispatched = function(self, state, args)
|
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()
|
state.scope:push()
|
||||||
args:bind_parameter_tuple(state, self.parameters)
|
args:bind_parameter_tuple(state, self.parameters)
|
||||||
|
|
||||||
local exp = self.expression:eval(state)
|
local exp = self.expression:eval(state)
|
||||||
|
|
||||||
state.scope:pop()
|
state.scope:pop()
|
||||||
|
|
||||||
-- reminder: don't do any additionnal processing here as that won't be executed when resuming self.expression directly
|
calling_environment_manager:pop(state)
|
||||||
-- which is done in a few places, notably to predefine exports in Closure
|
state.scope:pop()
|
||||||
-- instead wrap it in some additional node, like our friend ReturnBoundary
|
|
||||||
|
|
||||||
return exp
|
return exp
|
||||||
end,
|
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)
|
-- push captured closure scope
|
||||||
return Closure:new(Function:new(self.parameters:eval(state), self.expression), state)
|
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,
|
end,
|
||||||
}
|
}
|
||||||
|
|
||||||
package.loaded[...] = Function
|
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
|
return Function
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@ local Overloadable = ast.abstract.Overloadable
|
||||||
local operator_priority = require("anselme.common").operator_priority
|
local operator_priority = require("anselme.common").operator_priority
|
||||||
local unpack = table.unpack or unpack
|
local unpack = table.unpack or unpack
|
||||||
|
|
||||||
|
local calling_environment_manager
|
||||||
|
|
||||||
local LuaFunction
|
local LuaFunction
|
||||||
LuaFunction = ast.abstract.Runtime(Overloadable) {
|
LuaFunction = ast.abstract.Runtime(Overloadable) {
|
||||||
type = "lua function",
|
type = "lua function",
|
||||||
|
|
@ -45,6 +47,9 @@ LuaFunction = ast.abstract.Runtime(Overloadable) {
|
||||||
return self.parameters:hash()
|
return self.parameters:hash()
|
||||||
end,
|
end,
|
||||||
call_dispatched = function(self, state, args)
|
call_dispatched = function(self, state, args)
|
||||||
|
local calling_environment = state.scope:capture()
|
||||||
|
calling_environment_manager:push(state, calling_environment)
|
||||||
|
|
||||||
local lua_args = { state }
|
local lua_args = { state }
|
||||||
|
|
||||||
state.scope:push()
|
state.scope:push()
|
||||||
|
|
@ -58,6 +63,8 @@ LuaFunction = ast.abstract.Runtime(Overloadable) {
|
||||||
|
|
||||||
local r = self.func(unpack(lua_args))
|
local r = self.func(unpack(lua_args))
|
||||||
assert(r, "lua function returned no value")
|
assert(r, "lua function returned no value")
|
||||||
|
|
||||||
|
calling_environment_manager:pop(state)
|
||||||
return r
|
return r
|
||||||
end,
|
end,
|
||||||
|
|
||||||
|
|
@ -70,4 +77,7 @@ LuaFunction = ast.abstract.Runtime(Overloadable) {
|
||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
package.loaded[...] = LuaFunction
|
||||||
|
calling_environment_manager = require("anselme.state.calling_environment_manager")
|
||||||
|
|
||||||
return LuaFunction
|
return LuaFunction
|
||||||
|
|
|
||||||
|
|
@ -197,7 +197,7 @@ return primary {
|
||||||
if not s then error(("invalid expression in function definition: %s"):format(right), 0) end
|
if not s then error(("invalid expression in function definition: %s"):format(right), 0) end
|
||||||
|
|
||||||
-- return function
|
-- 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
|
return Definition:new(symbol, fn):set_source(source_start), rem
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,6 @@ return prefix {
|
||||||
s, right, rem = pcall(expression_to_ast, source, rem, limit_pattern, self.priority)
|
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
|
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
|
end
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,16 +2,17 @@ local ast = require("anselme.ast")
|
||||||
local ArgumentTuple, Boolean, Nil = ast.ArgumentTuple, ast.Boolean, ast.Nil
|
local ArgumentTuple, Boolean, Nil = ast.ArgumentTuple, ast.Boolean, ast.Nil
|
||||||
|
|
||||||
local resume_manager = require("anselme.state.resume_manager")
|
local resume_manager = require("anselme.state.resume_manager")
|
||||||
|
local calling_environment_manager = require("anselme.state.calling_environment_manager")
|
||||||
|
|
||||||
return {
|
return {
|
||||||
{
|
{
|
||||||
"resume", "(function::closure, anchor::anchor)",
|
"resume", "(function::function, anchor::anchor)",
|
||||||
function(state, func, anchor)
|
function(state, func, anchor)
|
||||||
return func:resume(state, anchor)
|
return func:resume(state, anchor)
|
||||||
end
|
end
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"resume", "(function::closure, anchor::nil)",
|
"resume", "(function::function, anchor::nil)",
|
||||||
function(state, func)
|
function(state, func)
|
||||||
return func:call(state, ArgumentTuple:new())
|
return func:call(state, ArgumentTuple:new())
|
||||||
end
|
end
|
||||||
|
|
@ -23,9 +24,9 @@ return {
|
||||||
end
|
end
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"resuming", "(level::number)",
|
"resuming", "(level::number=0)",
|
||||||
function(state, level)
|
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)
|
state.scope:push(env)
|
||||||
local r = Boolean:new(resume_manager:resuming(state))
|
local r = Boolean:new(resume_manager:resuming(state))
|
||||||
state.scope:pop()
|
state.scope:pop()
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,24 @@
|
||||||
local ast = require("anselme.ast")
|
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 assert0 = require("anselme.common").assert0
|
||||||
|
|
||||||
|
local calling_environment_manager = require("anselme.state.calling_environment_manager")
|
||||||
|
|
||||||
return {
|
return {
|
||||||
{
|
{
|
||||||
"defined", "(c::closure, s::string)",
|
"defined", "(c::function, s::string)",
|
||||||
function(state, c, s)
|
function(state, c, s)
|
||||||
return Boolean:new(c.scope:defined_in_current_strict(state, s:to_identifier()))
|
return Boolean:new(c.scope:defined_in_current_strict(state, s:to_identifier()))
|
||||||
end
|
end
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"has upvalue", "(c::closure, s::string)",
|
"has upvalue", "(c::function, s::string)",
|
||||||
function(state, c, s)
|
function(state, c, s)
|
||||||
return Boolean:new(c.scope:defined(state, s:to_identifier()))
|
return Boolean:new(c.scope:defined(state, s:to_identifier()))
|
||||||
end
|
end
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"_._", "(c::closure, s::string)",
|
"_._", "(c::function, s::string)",
|
||||||
function(state, c, s)
|
function(state, c, s)
|
||||||
local identifier = s:to_identifier()
|
local identifier = s:to_identifier()
|
||||||
assert0(c.scope:defined(state, identifier), ("no variable %q defined in closure"):format(s.string))
|
assert0(c.scope:defined(state, identifier), ("no variable %q defined in closure"):format(s.string))
|
||||||
|
|
@ -24,7 +26,7 @@ return {
|
||||||
end
|
end
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"_._", "(c::closure, s::string) = v",
|
"_._", "(c::function, s::string) = v",
|
||||||
function(state, c, s, v)
|
function(state, c, s, v)
|
||||||
local identifier = s:to_identifier()
|
local identifier = s:to_identifier()
|
||||||
assert0(c.scope:defined(state, identifier), ("no variable %q defined in closure"):format(s.string))
|
assert0(c.scope:defined(state, identifier), ("no variable %q defined in closure"):format(s.string))
|
||||||
|
|
@ -33,7 +35,7 @@ return {
|
||||||
end
|
end
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"_._", "(c::closure, s::symbol) = v",
|
"_._", "(c::function, s::symbol) = v",
|
||||||
function(state, c, s, v)
|
function(state, c, s, v)
|
||||||
state.scope:push(c.scope)
|
state.scope:push(c.scope)
|
||||||
local r = Definition:new(s, v):eval(state)
|
local r = Definition:new(s, v):eval(state)
|
||||||
|
|
@ -45,7 +47,7 @@ return {
|
||||||
">_", "(q::is(\"quote\"))",
|
">_", "(q::is(\"quote\"))",
|
||||||
function(state, q)
|
function(state, q)
|
||||||
local exp = q.expression
|
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
|
local set_exp
|
||||||
if Call:is(exp) then
|
if Call:is(exp) then
|
||||||
|
|
@ -57,11 +59,29 @@ return {
|
||||||
if set_exp then
|
if set_exp then
|
||||||
local set_param = ParameterTuple:new()
|
local set_param = ParameterTuple:new()
|
||||||
set_param:insert_assignment(FunctionParameter:new(Identifier:new("value")))
|
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)
|
return Overload:new(get, set)
|
||||||
else
|
else
|
||||||
return get
|
return get
|
||||||
end
|
end
|
||||||
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 },
|
{ "struct", "(x)", function(state, x) return Boolean:new(x.type == "struct") end },
|
||||||
{ "table", "(x)", function(state, x) return Boolean:new(x.type == "table") 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 },
|
{ "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