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

Clean implementation of 👁️ and no longer increment 🏁 on checkpoint execution (redundant with 👁️)

This commit is contained in:
Étienne Fildadut 2022-10-03 18:10:47 +09:00
parent f79e2f5716
commit 2ff494d108
12 changed files with 115 additions and 77 deletions

View file

@ -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: Functions always have the following variables defined in its namespace by default:
`👁️`: number, number of times the function was executed before `👁️`: 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. `🔖`: 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. 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: 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 executed/resumed before (incremented when reaching the end of the function or a return line)
`🏁`: 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 reached before (incremented when reaching the checkpoint line; not incremented when resuming from/executing the checkpoint directly)
These variables are persistent. 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 b
c 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 ~ f.checkpoint
Will correctly resumes from the last set checkpoint, and write "b" and "c": 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 b
c 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() ~ f.checkpoint()
And will resume from the checkpoint like before: And will resume from the checkpoint like before:

View file

@ -247,7 +247,7 @@ local interpreter_methods = {
end end
if not r then coroutine.yield("error", e) end if not r then coroutine.yield("error", e) end
if self.state.interpreter.current_event then -- flush final events 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 re then coroutine.yield("error", re) end
if rf then r = rf end if rf then r = rf end
end end
@ -541,6 +541,8 @@ local vm_mt = {
-- * `signature`: string, full signature of the function -- * `signature`: string, full signature of the function
-- * `fn`: function (Lua function or table, see examples in `stdlib/functions.lua`) -- * `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. -- Returns this VM.
loadfunction = function(self, signature, fn) loadfunction = function(self, signature, fn)
if type(signature) == "table" then if type(signature) == "table" then

View file

@ -41,7 +41,7 @@ common = {
end end
end end
-- scoping: since merging means we will re-copy every variable from global state again, we need to simulate this -- 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 -- 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 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 for _, scopes in pairs(mt.scoped) do

View file

@ -397,8 +397,15 @@ local function eval(state, exp)
local fn = selected_variant.variant local fn = selected_variant.variant
if fn.type ~= "function" then if fn.type ~= "function" then
return nil, ("unknown function type %q"):format(fn.type) 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 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) local r, e = run(state, fn.child, not paren_call)
if not r then return nil, e end if not r then return nil, e end
return r return r
@ -423,8 +430,6 @@ local function eval(state, exp)
checkpoint, checkpointe = get_variable(state, fn.namespace.."🔖") checkpoint, checkpointe = get_variable(state, fn.namespace.."🔖")
if not checkpoint then return nil, checkpointe end if not checkpoint then return nil, checkpointe end
end end
local seen, seene = get_variable(state, fn.namespace.."👁️")
if not seen then return nil, seene end
-- execute lua functions -- execute lua functions
-- I guess we could technically skip getting & updating the seen and checkpoints vars since they can't be used from Anselme -- 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 -- 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 end
if not ret then return nil, e end if not ret then return nil, e end
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 -- for classes: build resulting object
if fn.subtype == "class" and ret and ret.type == "nil" then if fn.subtype == "class" and ret and ret.type == "nil" then
ret = { ret = {

View file

@ -108,7 +108,7 @@ run_line = function(state, line)
if not iv then return nil, ("%s; at %s"):format(ie, line.source) end if not iv then return nil, ("%s; at %s"):format(ie, line.source) end
end end
end end
elseif line.type == "flush_events" then elseif line.type == "flush events" then
local v, e = events:flush(state) local v, e = events:flush(state)
if not v then return nil, ("%s; in event flush at %s"):format(e, line.source) end 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 elseif line.type == "function" and line.subtype == "checkpoint" then
@ -151,36 +151,8 @@ run_block = function(state, block, resume_from_there, i, j)
end end
i = i + 1 i = i + 1
end end
-- if we are exiting a checkpoint block, mark it as ran and update checkpoint -- if we reach the end of a checkpoint block (we are resuming execution from a checkpoint), merge state
-- (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 block.parent_line and block.parent_line.type == "function" and block.parent_line.subtype == "checkpoint" then 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) merge_state(state)
end end
-- go up hierarchy if asked to resume -- go up hierarchy if asked to resume

View file

@ -83,8 +83,6 @@ TODO: fn/checkpoint/tag: maybe consider them a regular func call that takes chil
a a
~ tag.pop() ~ 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: 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. TODO: test reacheability of script paths + visualization of different branches the script can take. For one of those overarching story visualization thingy.

View file

@ -105,6 +105,7 @@ local function parse_line(line, state, namespace, parent_resumable, in_scoped)
allow_params = false allow_params = false
allow_assign = false allow_assign = false
keep_in_ast = true 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 r.parent_resumable = parent_resumable -- store parent resumable function and run checkpoint when line is read
else else
error("unknown function line type") error("unknown function line type")
@ -248,6 +249,8 @@ local function parse_line(line, state, namespace, parent_resumable, in_scoped)
-- custom code injection -- custom code injection
inject(state, r, "start", line.children, 2) inject(state, r, "start", line.children, 2)
inject(state, r, "end", line.children) inject(state, r, "end", line.children)
-- update 👁️ variable
table.insert(line.children, { content = "~👁️+=1", source = line.source })
-- define args -- define args
for _, param in ipairs(r.params) do for _, param in ipairs(r.params) do
if not state.variables[param.full_name] then 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) r.expression = ("{%s}"):format(expr)
-- return -- return
elseif l:match("^%@") then 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.type = "return"
r.child = true r.child = true
local expr = l:match("^%@(.*)$") local expr = l:match("^%@(.*)$")
@ -347,13 +351,15 @@ local function parse_line(line, state, namespace, parent_resumable, in_scoped)
-- custom code injection -- custom code injection
if not line.children then line.children = {} end if not line.children then line.children = {} end
inject(state, parent_resumable, "return", line.children) inject(state, parent_resumable, "return", line.children)
-- update 👁️ variable
table.insert(line.children, { content = "~👁️+=1", source = line.source })
-- text -- text
elseif l:match("[^%s]") then elseif l:match("[^%s]") then
r.type = "text" r.type = "text"
r.text = l r.text = l
-- flush events -- flush events
else else
r.type = "flush_events" r.type = "flush events"
end end
if not r.type then return nil, ("unknown line %s type"):format(line.source) end if not r.type then return nil, ("unknown line %s type"):format(line.source) end
return r return r

View file

@ -152,7 +152,7 @@ else
-- simple random to get the same result across lua versions -- simple random to get the same result across lua versions
local prev = 0 local prev = 0
local function badrandom(a, b) local function badrandom(a, b)
prev = (15485863 * prev + 11) % 2038074743 prev = (4241 * prev + 11) % 6997
return a + prev % (b-a+1) return a + prev % (b-a+1)
end end
function math.random(a, b) function math.random(a, b)

View file

@ -1,18 +1,22 @@
local _={} local _={}
_[29]={}
_[28]={}
_[27]={}
_[26]={}
_[25]={} _[25]={}
_[24]={} _[24]={}
_[23]={} _[23]={}
_[22]={} _[22]={}
_[21]={} _[21]={}
_[20]={tags=_[25],text="2"} _[20]={text="1",tags=_[29]}
_[19]={tags=_[25],text="Reached: "} _[19]={text="Reached: ",tags=_[28]}
_[18]={tags=_[24],text="1"} _[18]={text="1",tags=_[27]}
_[17]={tags=_[24],text="Seen: "} _[17]={text="Seen: ",tags=_[26]}
_[16]={tags=_[23],text="seen!"} _[16]={text="seen!",tags=_[25]}
_[15]={tags=_[22],text="1"} _[15]={text="1",tags=_[24]}
_[14]={tags=_[22],text="Reached: "} _[14]={text="Reached: ",tags=_[23]}
_[13]={tags=_[21],text="0"} _[13]={text="0",tags=_[22]}
_[12]={tags=_[21],text="Seen: "} _[12]={text="Seen: ",tags=_[21]}
_[11]={_[19],_[20]} _[11]={_[19],_[20]}
_[10]={_[17],_[18]} _[10]={_[17],_[18]}
_[9]={_[16]} _[9]={_[16]}
@ -27,17 +31,17 @@ _[1]={"text",_[7]}
return {_[1],_[2],_[3],_[4],_[5],_[6]} return {_[1],_[2],_[3],_[4],_[5],_[6]}
--[[ --[[
{ "text", { { { "text", { {
tags = <1>{}, tags = {},
text = "Seen: " text = "Seen: "
}, { }, {
tags = <table 1>, tags = {},
text = "0" text = "0"
} } } } } }
{ "text", { { { "text", { {
tags = <1>{}, tags = {},
text = "Reached: " text = "Reached: "
}, { }, {
tags = <table 1>, tags = {},
text = "1" text = "1"
} } } } } }
{ "text", { { { "text", { {
@ -45,18 +49,18 @@ return {_[1],_[2],_[3],_[4],_[5],_[6]}
text = "seen!" text = "seen!"
} } } } } }
{ "text", { { { "text", { {
tags = <1>{}, tags = {},
text = "Seen: " text = "Seen: "
}, { }, {
tags = <table 1>, tags = {},
text = "1" text = "1"
} } } } } }
{ "text", { { { "text", { {
tags = <1>{}, tags = {},
text = "Reached: " text = "Reached: "
}, { }, {
tags = <table 1>, tags = {},
text = "2" text = "1"
} } } } } }
{ "return" } { "return" }
]]-- ]]--

View file

@ -4,10 +4,10 @@ _[20]={}
_[19]={} _[19]={}
_[18]={} _[18]={}
_[17]={} _[17]={}
_[16]={tags=_[21],text="c"} _[16]={tags=_[21],text="a"}
_[15]={tags=_[20],text="a"} _[15]={tags=_[20],text="c"}
_[14]={tags=_[19],text="c"} _[14]={tags=_[19],text="b"}
_[13]={tags=_[18],text="b"} _[13]={tags=_[18],text="a"}
_[12]={tags=_[17],text="c"} _[12]={tags=_[17],text="c"}
_[11]={_[16]} _[11]={_[16]}
_[10]={_[15]} _[10]={_[15]}
@ -26,6 +26,10 @@ return {_[1],_[2],_[3],_[4],_[5],_[6]}
tags = {}, tags = {},
text = "c" text = "c"
} } } } } }
{ "text", { {
tags = {},
text = "a"
} } }
{ "text", { { { "text", { {
tags = {}, tags = {},
text = "b" text = "b"
@ -38,9 +42,5 @@ return {_[1],_[2],_[3],_[4],_[5],_[6]}
tags = {}, tags = {},
text = "a" text = "a"
} } } } } }
{ "text", { {
tags = {},
text = "c"
} } }
{ "return" } { "return" }
]]-- ]]--

View file

@ -0,0 +1,12 @@
:$ fn
{👁️}
:! a
a: {👁️}
~ fn.a
~ fn.a
~ fn.a

View 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" }
]]--