1
0
Fork 0
mirror of https://github.com/Reuh/anselme.git synced 2025-10-27 16:49: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)

View file

@ -34,9 +34,8 @@ Standard library.
* Text manipulation would make sense, but that would require a full UTF-8/Unicode support library like https://github.com/starwing/luautf8.
* Something to load other files. Maybe not load it by default to let the calling game sandbox Anselme. Probably create an export scope per file to perform some nice module loading.
* Implement the useful functions from Anselme v1.
* Checkpoint management.
* Overloadable :format for custom types.
* Text manipulation: concatenation, retag/add tags
* And in general, clean up everything.
---

View file

@ -9,8 +9,6 @@
| {}"1,2,3,4,5: " {}"*[1, 2, 3, 4, 5]" {}"" |
--- error ---
cancel merge
↳ from test/tests/checkpoint merging mutable value.ans:24:6 in call: error("cancel merge")
↳ from ? in block: :l = *[1, 2]…
--# post run check #--
--- text ---
| {}"1,2,3,4: " {}"*[1, 2, 3, 4]" {}"" |

View file

@ -9,8 +9,6 @@
| {}"4: " {}"4" {}"" |
--- error ---
cancel merge
↳ from test/tests/checkpoint merging variable.ans:24:6 in call: error("cancel merge")
↳ from ? in block: :l = 1…
--# post run check #--
--- text ---
| {}"3: " {}"3" {}"" |

View file

@ -3,9 +3,5 @@
| {}"" {}"42" {}"" |
--- error ---
no variable "y" defined in closure
↳ from test/tests/closure define nested.ans:12:4 in call: f . "y"
↳ from test/tests/closure define nested.ans:12:1 in text interpolation: | {f . "y"} |
↳ from test/tests/closure define nested.ans:12:1 in translatable: | {f . "y"} |
↳ from ? in block: :f = ($() _)…
--# saved #--
{}

View file

@ -1,7 +1,5 @@
--# run #--
--- error ---
trying to change the value of constant a
↳ from test/tests/constant variable.ans:5:3 in assignment: a = 52
↳ from ? in block: ::a = 3…
--# saved #--
{}

View file

@ -5,7 +5,5 @@
| {}"" {}"type(\"kg\", 12)" {}"" |
--- error ---
type check failure for weigh; 32 does not satisfy $(x) type(x) == t
↳ from test/tests/constrained variable assignement.ans:9:7 in assignment: weigh = 32
↳ from ? in block: :weigh::is("kg") = type(5, "kg")…
--# saved #--
{}

View file

@ -1,7 +1,5 @@
--# run #--
--- error ---
a is already defined in the current scope
↳ from test/tests/define override function.ans:4:4 in definition: :a = 2
↳ from ? in block: :a = ($() _)…
--# saved #--
{}

View file

@ -1,8 +1,5 @@
--# run #--
--- error ---
can't add an overload variant to non-overloadable variable a defined in the same scope
↳ from test/tests/define override variable.ans:3:1 in definition: :a = ($() _)
↳ from test/tests/define override variable.ans:3:1 in partial scope: :a = ($() _)…
↳ from ? in block: :a = 2…
--# saved #--
{}

View file

@ -1,7 +1,5 @@
--# run #--
--- error ---
a is already defined in the current scope
↳ from test/tests/define override.ans:3:4 in definition: :a = 2
↳ from ? in block: :a = 5…
--# saved #--
{}

View file

@ -6,9 +6,5 @@
| {}"" {}"42" {}"" |
--- error ---
identifier "z" is undefined in branch cf017f8a-7c86-4871-109af-6658231331e6
↳ from test/tests/exported variable nested.ans:12:3 in identifier: z
↳ from test/tests/exported variable nested.ans:12:1 in text interpolation: | {z} |
↳ from test/tests/exported variable nested.ans:12:1 in translatable: | {z} |
↳ from ? in block: :f = ($() _)…
--# saved #--
{}

View file

@ -1,7 +1,5 @@
--# run #--
--- error ---
can't call function $(a, b) _: expected 2 arguments, received 1
↳ from test/tests/function args arity check fail.ans:4:2 in call: f("ok")
↳ from ? in block: :f = ($(a, b) _)…
--# saved #--
{}

View file

@ -1,7 +1,5 @@
--# run #--
--- error ---
a function with parameters (a, b) is already defined in the overload
↳ from test/tests/function conflict.ans:5:1 in definition: :f = ($(a, b) 0)
↳ from ? in block: :f = ($(a, b) 0)…
--# saved #--
{}

View file

@ -7,7 +7,5 @@
can't call overload overload<($(name::($(x) type(x) == t)) _), ($(name::($(x) type(x) == t)) _)>: no function match (type("nope", 5)), possible functions were:
• (name::($(x) type(x) == t)): type check failure for parameter name in function (name::($(x) type(x) == t))
• (name::($(x) type(x) == t)): type check failure for parameter name in function (name::($(x) type(x) == t))
↳ from test/tests/function custom type dispatch error.ans:14:2 in call: a(type(5, "nope"))
↳ from ? in block: :french name = "french name"…
--# saved #--
{}

View file

@ -1,9 +1,5 @@
--# run #--
--- error ---
identifier "b" is undefined in branch cf017f8a-7c86-4871-109af-6658231331e6
↳ from test/tests/function scope wrong.ans:4:7 in identifier: b
↳ from test/tests/function scope wrong.ans:4:1 in text interpolation: | a: {b} |
↳ from test/tests/function scope wrong.ans:4:1 in translatable: | a: {b} |
↳ from ? in block: :a = ($() _)…
--# saved #--
{}

View file

@ -7,9 +7,5 @@
• (c::($(x) <lua function>), s::($(x) <lua function>)) = v: expected 3 arguments, received 2
• (c::($(x) <lua function>), s::($(x) <lua function>)) = v: expected 3 arguments, received 2
• (c::($(x) <lua function>), s::($(x) <lua function>)): type check failure for parameter c in function (c::($(x) <lua function>), s::($(x) <lua function>))
↳ from test/tests/function separate variable from variants.ans:10:4 in call: f . "a"
↳ from test/tests/function separate variable from variants.ans:10:1 in text interpolation: | {f . "a"} = 2 |
↳ from test/tests/function separate variable from variants.ans:10:1 in translatable: | {f . "a"} = 2 |
↳ from ? in block: :f = ($() _)…
--# saved #--
{}

View file

@ -3,7 +3,5 @@
can't call overload overload<($(a::($(x) <lua function>)) _), ($(x::($(x) <lua function>)) _)>: more than one function match (5), matching functions were at least (specificity 1.3):
• (x::($(x) <lua function>))
• (a::($(x) <lua function>))
↳ from test/tests/function type dispatch ambigous.ans:7:3 in call: fn(5)
↳ from ? in block: :fn = ($(x::number) _)…
--# saved #--
{}

View file

@ -11,7 +11,5 @@
| {}"" {}"*[3, 12, 99]" {}"" |
--- error ---
list index out of bounds
↳ from test/tests/list assignement.ans:21:6 in call: x(5) = 0
↳ from ? in block: :x = *[1, 2]…
--# saved #--
{}

View file

@ -9,9 +9,5 @@
| {}"" {}"3" {}" == " {}"3" {}"" |
--- error ---
tuple index out of bounds
↳ from test/tests/list index.ans:11:4 in call: x(-4)
↳ from test/tests/list index.ans:11:1 in text interpolation: | {x(-4)} |
↳ from test/tests/list index.ans:11:1 in translatable: | {x(-4)} |
↳ from ? in block: :x = [1, 2, 3]…
--# saved #--
{}

View file

@ -1,23 +1,6 @@
--# run #--
--- error ---
abort
↳ from test/tests/merge nested mutable error bis.ans:14:7 in call: error("abort")
↳ from test/tests/merge nested mutable error bis.ans:3:1 in block: insert(a, b)…
↳ from test/tests/merge nested mutable error bis.ans:3:18 in call: _
↳ from test/tests/merge nested mutable error bis.ans:3:17 in return boundary: _
↳ from script.ans:29:6 in call: fn!
↳ from script.ans:27:3 in block: resumed from = ()…
↳ from script.ans:27:7 in call: else!
↳ from script.ans:27:3 in partial scope: else!…
↳ from script.ans:23:2 in block: if(fn . "current checkpoint")…
↳ from script.ans:23:9 in call: _
↳ from script.ans:23:7 in return boundary: _
↳ from script.ans:37:9 in call: value(s)!
↳ from script.ans:36:1 in block: value(s)!
↳ from script.ans:36:20 in call: _
↳ from script.ans:36:1 in return boundary: _
↳ from test/tests/merge nested mutable error bis.ans:19:2 in call: f!
↳ from ? in block: :a = *[1]…
abort
--# post run check #--
--- text ---
| {}"[1,[2,3]]: " {}"*[1, *[2, 3]]" {}"" |

View file

@ -1,23 +1,6 @@
--# run #--
--- error ---
abort
↳ from test/tests/merge nested mutable error.ans:14:7 in call: error("abort")
↳ from test/tests/merge nested mutable error.ans:3:1 in block: insert(a, b)…
↳ from test/tests/merge nested mutable error.ans:3:18 in call: _
↳ from test/tests/merge nested mutable error.ans:3:17 in return boundary: _
↳ from script.ans:29:6 in call: fn!
↳ from script.ans:27:3 in block: resumed from = ()…
↳ from script.ans:27:7 in call: else!
↳ from script.ans:27:3 in partial scope: else!…
↳ from script.ans:23:2 in block: if(fn . "current checkpoint")…
↳ from script.ans:23:9 in call: _
↳ from script.ans:23:7 in return boundary: _
↳ from script.ans:37:9 in call: value(s)!
↳ from script.ans:36:1 in block: value(s)!
↳ from script.ans:36:20 in call: _
↳ from script.ans:36:1 in return boundary: _
↳ from test/tests/merge nested mutable error.ans:19:2 in call: f!
↳ from ? in block: :a = *[1]…
abort
--# post run check #--
--- text ---
| {}"[1,[2,3]]: " {}"*[1, *[2, 3]]" {}"" |

View file

@ -14,20 +14,7 @@
--- text ---
| {}"CHECK 2" |
--- error ---
t
↳ from test/tests/scope checkpoint mutable bis error.ans:32:7 in call: error("t")
↳ from test/tests/scope checkpoint mutable bis error.ans:7:1 in block: insert(t, len(l) + 1)…
↳ from test/tests/scope checkpoint mutable bis error.ans:7:8 in call: _
↳ from test/tests/scope checkpoint mutable bis error.ans:7:1 in return boundary: _
↳ from test/tests/scope checkpoint mutable bis error.ans:19:4 in call: f(t)
↳ from test/tests/scope checkpoint mutable bis error.ans:15:2 in block: | REC |…
↳ from test/tests/scope checkpoint mutable bis error.ans:15:4 in call: if(n < 1)
↳ from test/tests/scope checkpoint mutable bis error.ans:15:2 in partial scope: if(n < 1)…
↳ from test/tests/scope checkpoint mutable bis error.ans:7:1 in block: insert(t, len(l) + 1)…
↳ from test/tests/scope checkpoint mutable bis error.ans:7:8 in call: _
↳ from test/tests/scope checkpoint mutable bis error.ans:7:1 in return boundary: _
↳ from test/tests/scope checkpoint mutable bis error.ans:41:2 in call: f(l)
↳ from ? in block: :x = *[99]…
t
--# post run check #--
--- text ---
| {}"AFTER ERROR" |

View file

@ -10,20 +10,7 @@
--- text ---
| {}"CHECK" |
--- error ---
t
↳ from test/tests/scope checkpoint mutable error.ans:23:7 in call: error("t")
↳ from test/tests/scope checkpoint mutable error.ans:5:1 in block: insert(t, len(l) + 1)…
↳ from test/tests/scope checkpoint mutable error.ans:5:8 in call: _
↳ from test/tests/scope checkpoint mutable error.ans:5:1 in return boundary: _
↳ from test/tests/scope checkpoint mutable error.ans:17:4 in call: f(t)
↳ from test/tests/scope checkpoint mutable error.ans:13:2 in block: | REC |…
↳ from test/tests/scope checkpoint mutable error.ans:13:4 in call: if(n < 1)
↳ from test/tests/scope checkpoint mutable error.ans:13:2 in partial scope: if(n < 1)…
↳ from test/tests/scope checkpoint mutable error.ans:5:1 in block: insert(t, len(l) + 1)…
↳ from test/tests/scope checkpoint mutable error.ans:5:8 in call: _
↳ from test/tests/scope checkpoint mutable error.ans:5:1 in return boundary: _
↳ from test/tests/scope checkpoint mutable error.ans:32:2 in call: f(l)
↳ from ? in block: :l = *[1]…
t
--# post run check #--
--- text ---
| {}"AFTER ERROR" |

View file

@ -14,20 +14,7 @@
--- text ---
| {}"CHECK 2" |
--- error ---
t
↳ from test/tests/scope checkpoint mutable ter error.ans:34:7 in call: error("t")
↳ from test/tests/scope checkpoint mutable ter error.ans:7:1 in block: insert(t, len(l) + 1)…
↳ from test/tests/scope checkpoint mutable ter error.ans:7:8 in call: _
↳ from test/tests/scope checkpoint mutable ter error.ans:7:1 in return boundary: _
↳ from test/tests/scope checkpoint mutable ter error.ans:19:4 in call: f(t)
↳ from test/tests/scope checkpoint mutable ter error.ans:15:2 in block: | REC |…
↳ from test/tests/scope checkpoint mutable ter error.ans:15:4 in call: if(n < 1)
↳ from test/tests/scope checkpoint mutable ter error.ans:15:2 in partial scope: if(n < 1)…
↳ from test/tests/scope checkpoint mutable ter error.ans:7:1 in block: insert(t, len(l) + 1)…
↳ from test/tests/scope checkpoint mutable ter error.ans:7:8 in call: _
↳ from test/tests/scope checkpoint mutable ter error.ans:7:1 in return boundary: _
↳ from test/tests/scope checkpoint mutable ter error.ans:43:2 in call: f(l)
↳ from ? in block: :x = *[99]…
t
--# post run check #--
--- text ---
| {}"AFTER ERROR" |

View file

@ -6,7 +6,5 @@
| {}"d=" {}"2" {}" (2)" |
--- error ---
trying to change the value of constant d
↳ from test/tests/symbol alias constant.ans:12:3 in assignment: d = 5
↳ from ? in block: :l = *[1, 2, 3]…
--# saved #--
{}

View file

@ -47,6 +47,8 @@ local function run_loop(run_state, out)
elseif e == "return" then
table.insert(out, data:format(run_state))
run_state:merge()
elseif e == "error" then
table.insert(out, (tostring(data):gsub("\n%s*↳[^\n]*", ""))) -- traceback change every day and a half due to AST changes. TODO: only keep ast layers relevant for the user
else
table.insert(out, tostring(data))
end