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:
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:
|
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:
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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 = {
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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" }
|
||||||
]]--
|
]]--
|
||||||
|
|
@ -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" }
|
||||||
]]--
|
]]--
|
||||||
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