mirror of
https://github.com/Reuh/anselme.git
synced 2025-10-27 08:39:30 +00:00
Clean implementation of 👁️ and no longer increment 🏁 on checkpoint execution (redundant with 👁️)
This commit is contained in:
parent
f79e2f5716
commit
2ff494d108
12 changed files with 115 additions and 77 deletions
12
LANGUAGE.md
12
LANGUAGE.md
|
|
@ -359,8 +359,8 @@ Functions can return a value using a [return line](#lines-that-can-t-have-childr
|
|||
|
||||
Functions always have the following variables defined in its namespace by default:
|
||||
|
||||
`👁️`: number, number of times the function was executed before
|
||||
`🔖`: function reference, last reached checkpoint. `nil` if no checkpoint reached.
|
||||
`👁️`: number, number of times the function was executed/resumed before (incremented when reaching the end of the function or a return line)
|
||||
`🔖`: function reference, last reached checkpoint. `()` (nil) if no checkpoint reached. (updated when reaching a checkpoint or directly executing a checkpoint)
|
||||
|
||||
These variables are persistent, unless the function is scoped.
|
||||
|
||||
|
|
@ -382,8 +382,8 @@ When executing the parent function after this checkpoint has been reached (using
|
|||
|
||||
Checkpoints always have the following variable defined in its namespace by default:
|
||||
|
||||
`👁️`: number, number of times the checkpoint was executed before
|
||||
`🏁`: number, number of times the checkpoint was reached before (includes times where it was resumed from and executed)
|
||||
`👁️`: number, number of times the checkpoint was executed/resumed before (incremented when reaching the end of the function or a return line)
|
||||
`🏁`: number, number of times the checkpoint was reached before (incremented when reaching the checkpoint line; not incremented when resuming from/executing the checkpoint directly)
|
||||
|
||||
These variables are persistent.
|
||||
|
||||
|
|
@ -901,7 +901,7 @@ But in the cases when you want to manually set the current checkpoint, you can c
|
|||
b
|
||||
c
|
||||
|
||||
Force run the function starting from checkpoint, will write "b" and "c" and set the current checkpoint to "checkpoint":
|
||||
Set the current checkpoint to "checkpoint" and force run the function starting from this checkpoint, will write "b" and "c":
|
||||
~ f.checkpoint
|
||||
|
||||
Will correctly resumes from the last set checkpoint, and write "b" and "c":
|
||||
|
|
@ -921,7 +921,7 @@ You can also only execute the checkpoints' children code only by using a paranth
|
|||
b
|
||||
c
|
||||
|
||||
Run the checkpoint only, will only write "b" and set the current checkpoint to "checkpoint":
|
||||
Set the current checkpoint to "checkpoint" and run this checkpoint only, will only write "b":
|
||||
~ f.checkpoint()
|
||||
|
||||
And will resume from the checkpoint like before:
|
||||
|
|
|
|||
|
|
@ -247,7 +247,7 @@ local interpreter_methods = {
|
|||
end
|
||||
if not r then coroutine.yield("error", e) end
|
||||
if self.state.interpreter.current_event then -- flush final events
|
||||
local rf, re = run_line(self.state, { type = "flush_events" })
|
||||
local rf, re = run_line(self.state, { type = "flush events" })
|
||||
if re then coroutine.yield("error", re) end
|
||||
if rf then r = rf end
|
||||
end
|
||||
|
|
@ -541,6 +541,8 @@ local vm_mt = {
|
|||
-- * `signature`: string, full signature of the function
|
||||
-- * `fn`: function (Lua function or table, see examples in `stdlib/functions.lua`)
|
||||
--
|
||||
-- Alternatively, can also take a table as a sole argument to load several functions: { ["signature"] = fn, ... }
|
||||
--
|
||||
-- Returns this VM.
|
||||
loadfunction = function(self, signature, fn)
|
||||
if type(signature) == "table" then
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ common = {
|
|||
end
|
||||
end
|
||||
-- scoping: since merging means we will re-copy every variable from global state again, we need to simulate this
|
||||
-- behavious for scoped variables (to have consistent references for mutables values in particular), including
|
||||
-- behaviour for scoped variables (to have consistent references for mutables values in particular), including
|
||||
-- scopes that aren't currently active
|
||||
fix_not_modified_references(mt.scoped, copy_cache, modified_tables) -- replace not modified values in scope with original before re-copying to keep consistent references
|
||||
for _, scopes in pairs(mt.scoped) do
|
||||
|
|
|
|||
|
|
@ -397,8 +397,15 @@ local function eval(state, exp)
|
|||
local fn = selected_variant.variant
|
||||
if fn.type ~= "function" then
|
||||
return nil, ("unknown function type %q"):format(fn.type)
|
||||
-- checkpoint: no args and resume execution
|
||||
-- checkpoint: no args and can resume execution
|
||||
elseif fn.subtype == "checkpoint" then
|
||||
-- set current checkpoint
|
||||
local s, e = set_variable(state, fn.parent_resumable.namespace.."🔖", {
|
||||
type = "function reference",
|
||||
value = { fn.name }
|
||||
})
|
||||
if not s then return nil, e end
|
||||
-- run checkpoint content, eventually resuming
|
||||
local r, e = run(state, fn.child, not paren_call)
|
||||
if not r then return nil, e end
|
||||
return r
|
||||
|
|
@ -423,8 +430,6 @@ local function eval(state, exp)
|
|||
checkpoint, checkpointe = get_variable(state, fn.namespace.."🔖")
|
||||
if not checkpoint then return nil, checkpointe end
|
||||
end
|
||||
local seen, seene = get_variable(state, fn.namespace.."👁️")
|
||||
if not seen then return nil, seene end
|
||||
-- execute lua functions
|
||||
-- I guess we could technically skip getting & updating the seen and checkpoints vars since they can't be used from Anselme
|
||||
-- but it's also kinda fun to known how many time a function was ran
|
||||
|
|
@ -501,12 +506,6 @@ local function eval(state, exp)
|
|||
end
|
||||
if not ret then return nil, e end
|
||||
end
|
||||
-- update function vars
|
||||
local s, e = set_variable(state, fn.namespace.."👁️", {
|
||||
type = "number",
|
||||
value = seen.value + 1
|
||||
})
|
||||
if not s then return nil, e end
|
||||
-- for classes: build resulting object
|
||||
if fn.subtype == "class" and ret and ret.type == "nil" then
|
||||
ret = {
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ run_line = function(state, line)
|
|||
if not iv then return nil, ("%s; at %s"):format(ie, line.source) end
|
||||
end
|
||||
end
|
||||
elseif line.type == "flush_events" then
|
||||
elseif line.type == "flush events" then
|
||||
local v, e = events:flush(state)
|
||||
if not v then return nil, ("%s; in event flush at %s"):format(e, line.source) end
|
||||
elseif line.type == "function" and line.subtype == "checkpoint" then
|
||||
|
|
@ -151,36 +151,8 @@ run_block = function(state, block, resume_from_there, i, j)
|
|||
end
|
||||
i = i + 1
|
||||
end
|
||||
-- if we are exiting a checkpoint block, mark it as ran and update checkpoint
|
||||
-- (when resuming from a checkpoint, execution is resumed from inside the checkpoint, the line.subtype=="checkpoint" check in run_line is never called)
|
||||
-- (and we want this to be done after executing the checkpoint block anyway)
|
||||
-- if we reach the end of a checkpoint block (we are resuming execution from a checkpoint), merge state
|
||||
if block.parent_line and block.parent_line.type == "function" and block.parent_line.subtype == "checkpoint" then
|
||||
local parent_line = block.parent_line
|
||||
local reached, reachede = get_variable(state, parent_line.namespace.."🏁")
|
||||
if not reached then return nil, reachede end
|
||||
local seen, seene = get_variable(state, parent_line.namespace.."👁️")
|
||||
if not seen then return nil, seene end
|
||||
local checkpoint, checkpointe = get_variable(state, parent_line.parent_resumable.namespace.."🔖")
|
||||
if not checkpoint then return nil, checkpointe end
|
||||
local s, e = set_variable(state, parent_line.namespace.."👁️", {
|
||||
type = "number",
|
||||
value = seen.value + 1
|
||||
})
|
||||
if not s then return nil, e end
|
||||
s, e = set_variable(state, parent_line.namespace.."🏁", {
|
||||
type = "number",
|
||||
value = reached.value + 1
|
||||
})
|
||||
if not s then return nil, e end
|
||||
-- don't update checkpoint if an already more precise checkpoint is set
|
||||
-- (since we will go up the whole checkpoint hierarchy when resuming from a nested checkpoint)
|
||||
if checkpoint.type == "nil" or not checkpoint.value[1]:match("^"..escape(parent_line.name)) then
|
||||
s, e = set_variable(state, parent_line.parent_resumable.namespace.."🔖", {
|
||||
type = "function reference",
|
||||
value = { parent_line.name }
|
||||
})
|
||||
if not s then return nil, e end
|
||||
end
|
||||
merge_state(state)
|
||||
end
|
||||
-- go up hierarchy if asked to resume
|
||||
|
|
|
|||
|
|
@ -83,8 +83,6 @@ TODO: fn/checkpoint/tag: maybe consider them a regular func call that takes chil
|
|||
a
|
||||
~ tag.pop()
|
||||
|
||||
TODO: perform seen/reached/etc default variable not in interpreter but using parse-time macro
|
||||
|
||||
TODO: make language simple enough to be able to reimplement it in, say, nim. Especially the AST interpreter (we could precompile a lot of stuff...)
|
||||
|
||||
TODO: test reacheability of script paths + visualization of different branches the script can take. For one of those overarching story visualization thingy.
|
||||
|
|
|
|||
|
|
@ -105,6 +105,7 @@ local function parse_line(line, state, namespace, parent_resumable, in_scoped)
|
|||
allow_params = false
|
||||
allow_assign = false
|
||||
keep_in_ast = true
|
||||
if not parent_resumable then return nil, ("checkpoint definition line is not in a function; at %s"):format(line.source) end
|
||||
r.parent_resumable = parent_resumable -- store parent resumable function and run checkpoint when line is read
|
||||
else
|
||||
error("unknown function line type")
|
||||
|
|
@ -248,6 +249,8 @@ local function parse_line(line, state, namespace, parent_resumable, in_scoped)
|
|||
-- custom code injection
|
||||
inject(state, r, "start", line.children, 2)
|
||||
inject(state, r, "end", line.children)
|
||||
-- update 👁️ variable
|
||||
table.insert(line.children, { content = "~👁️+=1", source = line.source })
|
||||
-- define args
|
||||
for _, param in ipairs(r.params) do
|
||||
if not state.variables[param.full_name] then
|
||||
|
|
@ -336,6 +339,7 @@ local function parse_line(line, state, namespace, parent_resumable, in_scoped)
|
|||
r.expression = ("{%s}"):format(expr)
|
||||
-- return
|
||||
elseif l:match("^%@") then
|
||||
if not parent_resumable then return nil, ("return line is not in a function; at %s"):format(line.source) end
|
||||
r.type = "return"
|
||||
r.child = true
|
||||
local expr = l:match("^%@(.*)$")
|
||||
|
|
@ -347,13 +351,15 @@ local function parse_line(line, state, namespace, parent_resumable, in_scoped)
|
|||
-- custom code injection
|
||||
if not line.children then line.children = {} end
|
||||
inject(state, parent_resumable, "return", line.children)
|
||||
-- update 👁️ variable
|
||||
table.insert(line.children, { content = "~👁️+=1", source = line.source })
|
||||
-- text
|
||||
elseif l:match("[^%s]") then
|
||||
r.type = "text"
|
||||
r.text = l
|
||||
-- flush events
|
||||
else
|
||||
r.type = "flush_events"
|
||||
r.type = "flush events"
|
||||
end
|
||||
if not r.type then return nil, ("unknown line %s type"):format(line.source) end
|
||||
return r
|
||||
|
|
|
|||
|
|
@ -152,7 +152,7 @@ else
|
|||
-- simple random to get the same result across lua versions
|
||||
local prev = 0
|
||||
local function badrandom(a, b)
|
||||
prev = (15485863 * prev + 11) % 2038074743
|
||||
prev = (4241 * prev + 11) % 6997
|
||||
return a + prev % (b-a+1)
|
||||
end
|
||||
function math.random(a, b)
|
||||
|
|
|
|||
|
|
@ -1,18 +1,22 @@
|
|||
local _={}
|
||||
_[29]={}
|
||||
_[28]={}
|
||||
_[27]={}
|
||||
_[26]={}
|
||||
_[25]={}
|
||||
_[24]={}
|
||||
_[23]={}
|
||||
_[22]={}
|
||||
_[21]={}
|
||||
_[20]={tags=_[25],text="2"}
|
||||
_[19]={tags=_[25],text="Reached: "}
|
||||
_[18]={tags=_[24],text="1"}
|
||||
_[17]={tags=_[24],text="Seen: "}
|
||||
_[16]={tags=_[23],text="seen!"}
|
||||
_[15]={tags=_[22],text="1"}
|
||||
_[14]={tags=_[22],text="Reached: "}
|
||||
_[13]={tags=_[21],text="0"}
|
||||
_[12]={tags=_[21],text="Seen: "}
|
||||
_[20]={text="1",tags=_[29]}
|
||||
_[19]={text="Reached: ",tags=_[28]}
|
||||
_[18]={text="1",tags=_[27]}
|
||||
_[17]={text="Seen: ",tags=_[26]}
|
||||
_[16]={text="seen!",tags=_[25]}
|
||||
_[15]={text="1",tags=_[24]}
|
||||
_[14]={text="Reached: ",tags=_[23]}
|
||||
_[13]={text="0",tags=_[22]}
|
||||
_[12]={text="Seen: ",tags=_[21]}
|
||||
_[11]={_[19],_[20]}
|
||||
_[10]={_[17],_[18]}
|
||||
_[9]={_[16]}
|
||||
|
|
@ -27,17 +31,17 @@ _[1]={"text",_[7]}
|
|||
return {_[1],_[2],_[3],_[4],_[5],_[6]}
|
||||
--[[
|
||||
{ "text", { {
|
||||
tags = <1>{},
|
||||
tags = {},
|
||||
text = "Seen: "
|
||||
}, {
|
||||
tags = <table 1>,
|
||||
tags = {},
|
||||
text = "0"
|
||||
} } }
|
||||
{ "text", { {
|
||||
tags = <1>{},
|
||||
tags = {},
|
||||
text = "Reached: "
|
||||
}, {
|
||||
tags = <table 1>,
|
||||
tags = {},
|
||||
text = "1"
|
||||
} } }
|
||||
{ "text", { {
|
||||
|
|
@ -45,18 +49,18 @@ return {_[1],_[2],_[3],_[4],_[5],_[6]}
|
|||
text = "seen!"
|
||||
} } }
|
||||
{ "text", { {
|
||||
tags = <1>{},
|
||||
tags = {},
|
||||
text = "Seen: "
|
||||
}, {
|
||||
tags = <table 1>,
|
||||
tags = {},
|
||||
text = "1"
|
||||
} } }
|
||||
{ "text", { {
|
||||
tags = <1>{},
|
||||
tags = {},
|
||||
text = "Reached: "
|
||||
}, {
|
||||
tags = <table 1>,
|
||||
text = "2"
|
||||
tags = {},
|
||||
text = "1"
|
||||
} } }
|
||||
{ "return" }
|
||||
]]--
|
||||
|
|
@ -4,10 +4,10 @@ _[20]={}
|
|||
_[19]={}
|
||||
_[18]={}
|
||||
_[17]={}
|
||||
_[16]={tags=_[21],text="c"}
|
||||
_[15]={tags=_[20],text="a"}
|
||||
_[14]={tags=_[19],text="c"}
|
||||
_[13]={tags=_[18],text="b"}
|
||||
_[16]={tags=_[21],text="a"}
|
||||
_[15]={tags=_[20],text="c"}
|
||||
_[14]={tags=_[19],text="b"}
|
||||
_[13]={tags=_[18],text="a"}
|
||||
_[12]={tags=_[17],text="c"}
|
||||
_[11]={_[16]}
|
||||
_[10]={_[15]}
|
||||
|
|
@ -26,6 +26,10 @@ return {_[1],_[2],_[3],_[4],_[5],_[6]}
|
|||
tags = {},
|
||||
text = "c"
|
||||
} } }
|
||||
{ "text", { {
|
||||
tags = {},
|
||||
text = "a"
|
||||
} } }
|
||||
{ "text", { {
|
||||
tags = {},
|
||||
text = "b"
|
||||
|
|
@ -38,9 +42,5 @@ return {_[1],_[2],_[3],_[4],_[5],_[6]}
|
|||
tags = {},
|
||||
text = "a"
|
||||
} } }
|
||||
{ "text", { {
|
||||
tags = {},
|
||||
text = "c"
|
||||
} } }
|
||||
{ "return" }
|
||||
]]--
|
||||
12
test/tests/seen checkpoint resume.ans
Normal file
12
test/tests/seen checkpoint resume.ans
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
:$ fn
|
||||
{👁️}
|
||||
|
||||
:! a
|
||||
|
||||
a: {👁️}
|
||||
|
||||
~ fn.a
|
||||
|
||||
~ fn.a
|
||||
|
||||
~ fn.a
|
||||
45
test/tests/seen checkpoint resume.lua
Normal file
45
test/tests/seen checkpoint resume.lua
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
local _={}
|
||||
_[19]={}
|
||||
_[18]={}
|
||||
_[17]={}
|
||||
_[16]={}
|
||||
_[15]={}
|
||||
_[14]={}
|
||||
_[13]={text="2",tags=_[19]}
|
||||
_[12]={text="a: ",tags=_[18]}
|
||||
_[11]={text="1",tags=_[17]}
|
||||
_[10]={text="a: ",tags=_[16]}
|
||||
_[9]={text="0",tags=_[15]}
|
||||
_[8]={text="a: ",tags=_[14]}
|
||||
_[7]={_[12],_[13]}
|
||||
_[6]={_[10],_[11]}
|
||||
_[5]={_[8],_[9]}
|
||||
_[4]={"return"}
|
||||
_[3]={"text",_[7]}
|
||||
_[2]={"text",_[6]}
|
||||
_[1]={"text",_[5]}
|
||||
return {_[1],_[2],_[3],_[4]}
|
||||
--[[
|
||||
{ "text", { {
|
||||
tags = {},
|
||||
text = "a: "
|
||||
}, {
|
||||
tags = {},
|
||||
text = "0"
|
||||
} } }
|
||||
{ "text", { {
|
||||
tags = {},
|
||||
text = "a: "
|
||||
}, {
|
||||
tags = {},
|
||||
text = "1"
|
||||
} } }
|
||||
{ "text", { {
|
||||
tags = {},
|
||||
text = "a: "
|
||||
}, {
|
||||
tags = {},
|
||||
text = "2"
|
||||
} } }
|
||||
{ "return" }
|
||||
]]--
|
||||
Loading…
Add table
Add a link
Reference in a new issue