1
0
Fork 0
mirror of https://github.com/Reuh/anselme.git synced 2025-10-28 00:59:31 +00:00

Undefine _ in attached block

This commit is contained in:
Étienne Fildadut 2024-01-02 14:35:18 +01:00
parent a85e7ab0af
commit 15f29e3bce
31 changed files with 70 additions and 163 deletions

View file

@ -68,6 +68,7 @@ local Environment = ast.abstract.Runtime {
variables = nil, -- Table of { {identifier} = variable metadata, ... }
partial = nil, -- { [name string] = true, ... }
undefine = nil, -- { [name string] = true, ... }
export = nil, -- bool
init = function(self, state, parent, partial_names, is_export)
@ -75,6 +76,7 @@ local Environment = ast.abstract.Runtime {
self.parent = parent
self.partial = partial_names
self.export = is_export
self.undefine = {}
end,
traverse = function(self, fn, ...)
@ -97,7 +99,11 @@ local Environment = ast.abstract.Runtime {
or (self.export ~= symbol.exported) then
return self.parent:define(state, symbol, exp)
end
self.variables:set(state, symbol:to_identifier(), VariableMetadata:new(state, symbol, exp))
if symbol.undefine then
self.undefine[name] = true
else
self.variables:set(state, symbol:to_identifier(), VariableMetadata:new(state, symbol, exp))
end
end,
-- define or redefine new overloadable variable in current environment, inheriting existing overload variants from (parent) scopes
define_overloadable = function(self, state, symbol, exp)
@ -132,7 +138,7 @@ local Environment = ast.abstract.Runtime {
defined = function(self, state, identifier)
if self.variables:has(state, identifier) then
return true
elseif self.parent then
elseif self.parent and not self.undefine[identifier.name] then
return self.parent:defined(state, identifier)
end
return false
@ -143,9 +149,10 @@ local Environment = ast.abstract.Runtime {
local name = symbol.string
if self.variables:has(state, symbol:to_identifier()) then
return true
elseif (self.partial and not self.partial[name])
or (self.export ~= symbol.exported) then
return self.parent:defined_in_current(state, symbol)
elseif self.parent and not self.undefine[name] then
if (self.partial and not self.partial[name]) or (self.export ~= symbol.exported) then
return self.parent:defined_in_current(state, symbol)
end
end
return false
end,
@ -159,7 +166,7 @@ local Environment = ast.abstract.Runtime {
if self:defined(state, identifier) then
if self.variables:has(state, identifier) then
return self.variables:get(state, identifier)
elseif self.parent then
elseif self.parent and not self.undefine[identifier.name] then
return self.parent:_get_variable(state, identifier)
end
else

View file

@ -1,7 +1,7 @@
-- create a partial layer to define temporary variables
local ast = require("anselme.ast")
local Identifier, Quote
local Identifier, Quote, Nil
local attached_block_identifier, attached_block_symbol
local unpack = table.unpack or unpack
@ -11,7 +11,7 @@ PartialScope = ast.abstract.Node {
type = "partial scope",
expression = nil,
definitions = nil, -- {[sym]=value,...} where values are already evaluated!
definitions = nil, -- {[sym]=value,...}
_identifiers = nil, -- {identifier,...} - just a cache so we don't rebuild it on every eval
init = function(self, expression)
@ -49,7 +49,7 @@ PartialScope = ast.abstract.Node {
_eval = function(self, state)
state.scope:push_partial(unpack(self._identifiers))
for sym, val in pairs(self.definitions) do state.scope:define(sym, val) end
for sym, val in pairs(self.definitions) do state.scope:define(sym:eval(state), val:eval(state)) end
local exp = self.expression:eval(state)
state.scope:pop()
@ -71,14 +71,16 @@ PartialScope = ast.abstract.Node {
end,
-- class method: return a PartialScope that define the block identifier _ to a Quote of `block`
attach_block = function(self, expression, block)
local partial = ast.PartialScope:new(expression)
partial:define(attached_block_symbol, Quote:new(block))
local partial = PartialScope:new(expression)
local unpartial = PartialScope:new(block)
unpartial:define(attached_block_symbol:with{undefine=true}, Nil:new())
partial:define(attached_block_symbol, Quote:new(unpartial))
return partial
end
}
package.loaded[...] = PartialScope
Identifier, Quote = ast.Identifier, ast.Quote
Identifier, Quote, Nil = ast.Identifier, ast.Quote, ast.Nil
attached_block_identifier = Identifier:new("_")
attached_block_symbol = attached_block_identifier:to_symbol()

View file

@ -15,6 +15,7 @@ Symbol = ast.abstract.Node {
exported = nil, -- bool
confined_to_branch = nil, -- bool
undefine = nil, -- bool
init = function(self, str, modifiers)
modifiers = modifiers or {}
@ -24,6 +25,7 @@ Symbol = ast.abstract.Node {
self.alias = modifiers.alias
self.confined_to_branch = modifiers.confined_to_branch
self.exported = modifiers.exported
self.undefine = modifiers.undefine
end,
_eval = function(self, state)
@ -34,7 +36,7 @@ Symbol = ast.abstract.Node {
with = function(self, modifiers)
modifiers = modifiers or {}
for _, k in ipairs{"constant", "type_check", "alias", "exported", "confined_to_branch"} do
for _, k in ipairs{"constant", "type_check", "alias", "exported", "confined_to_branch", "undefine"} do
if modifiers[k] == nil then
modifiers[k] = self[k]
end

View file

@ -4,6 +4,8 @@ local assert0 = require("anselme.common").assert0
local calling_environment_manager = require("anselme.state.calling_environment_manager")
local block_identifier = Identifier:new("_")
return {
{
"defined", "(c::function, s::string)",
@ -88,21 +90,33 @@ return {
end
},
{
"attached block", "(level::number=1)",
function(state, level)
"attached block", "(level::number=1, keep return=false)",
function(state, level, keep_return)
-- 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)
local r = env:get(state, block_identifier)
if keep_return:truthy() then
return Function:new(ParameterTuple:new(), r.expression):eval(state)
else
return Function:with_return_boundary(ParameterTuple:new(), r.expression):eval(state)
end
end
},
{
"attached block keep return", "(level::number=1)",
function(state, level)
"attached block", "(level::number=1, keep return=false, default)",
function(state, level, keep_return, default)
-- 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)
if env:defined(state, block_identifier) then
local r = env:get(state, block_identifier)
if keep_return:truthy() then
return Function:new(ParameterTuple:new(), r.expression):eval(state)
else
return Function:with_return_boundary(ParameterTuple:new(), r.expression):eval(state)
end
else
return default
end
end
},
}

View file

@ -18,7 +18,7 @@ end
return {
{
"if", "(condition, expression=attached block keep return!)", function(state, condition, expression)
"if", "(condition, expression=attached block(keep return=true))", function(state, condition, expression)
ensure_if_variable(state)
if condition:truthy() or expression:contains_current_resume_target(state) then
set_if_variable(state, true)
@ -42,7 +42,7 @@ return {
end
},
{
"else if", "(condition, expression=attached block keep return!)",
"else if", "(condition, expression=attached block(keep return=true))",
function(state, condition, expression)
ensure_if_variable(state)
if (not last_if_success(state) and condition:truthy()) or expression:contains_current_resume_target(state) then
@ -55,7 +55,7 @@ return {
end
},
{
"else", "(expression=attached block keep return!)",
"else", "(expression=attached block(keep return=true))",
function(state, expression)
ensure_if_variable(state)
if not last_if_success(state) or expression:contains_current_resume_target(state) then
@ -67,7 +67,7 @@ return {
end
},
{
"while", "(condition, expression=attached block keep return!)",
"while", "(condition, expression=attached block(keep return=true))",
function(state, condition, expression)
ensure_if_variable(state)
local cond = condition:call(state, ArgumentTuple:new())

View file

@ -8,18 +8,19 @@ return [[
fn.:check = $(anchor::anchor)
fn.reached(anchor) = (fn.reached(anchor) | 0) + 1
fn.:checkpoint = $(anchor::anchor)
fn.current checkpoint = anchor
if(resumed from != anchor)
fn.reached(anchor) = (fn.reached(anchor) | 0) + 1
merge branch!
fn.:checkpoint = $(anchor::anchor, on resume::function)
fn.current checkpoint = anchor
if(resumed from == anchor | resuming(2))
on resume!
fn.:checkpoint = $(anchor::anchor, on resume=attached block(default=()))
if(on resume)
fn.current checkpoint = anchor
if(resumed from == anchor | resuming(4))
on resume!
else!
fn.reached(anchor) = (fn.reached(anchor) | 0) + 1
merge branch!
else!
fn.reached(anchor) = (fn.reached(anchor) | 0) + 1
merge branch!
fn.current checkpoint = anchor
if(resumed from != anchor)
fn.reached(anchor) = (fn.reached(anchor) | 0) + 1
merge branch!
:f = $
if(fn.current checkpoint)