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:
parent
a85e7ab0af
commit
15f29e3bce
31 changed files with 70 additions and 163 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue