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

Fix scoping with mutable variables

This commit is contained in:
Étienne Fildadut 2021-12-12 15:38:27 +01:00
parent 16d0bb8d7a
commit bb45cc8fdd
16 changed files with 1243 additions and 38 deletions

View file

@ -487,11 +487,12 @@ local vm_mt = {
return cache[k]
end,
-- variables that keep current state and should be cleared at each checkpoint
copy_cache = {}, -- table of [original table] = copied table
modified_tables = {}, -- list of modified tables (copies) that should be merged with global state on next checkpoint
cache = {}, -- cache of previously read values (copies), to get repeatable reads & handle mutable types without changing global state
modified_tables = {}, -- list of modified tables (copies) that should be merged with global state on next checkpoint
copy_cache = {}, -- table of [original table] = copied table. Automatically filled by copy().
-- keep track of scoped variables in scoped functions [fn line] = {{scoped variables}, next scope, ...}
-- (scoped variables aren't merged on checkpoint, shouldn't be cleared at checkpoints)
-- (only stores scoped variables that have been reassigned at some point (i.e. every accessed one since they start as undefined))
scoped = {}
}),
interpreter = {

View file

@ -1,3 +1,5 @@
local common
--- replace values recursively in table t according to to_replace ([old table] = new table)
-- already_replaced is a temporary table to avoid infinite loop & duplicate processing, no need to give it
local function replace_in_table(t, to_replace, already_replaced)
@ -12,7 +14,6 @@ local function replace_in_table(t, to_replace, already_replaced)
end
end
local common
common = {
--- recursively copy a table, handle cyclic references, no metatable, don't copy keys
-- cache is table with copied tables [original table] = copied value, will use temporary table is omitted
@ -59,6 +60,29 @@ common = {
end
-- replace in t
replace_in_table(t, to_replace)
end,
--- given a table t issued from some copy, the copy cache, and a list of tables from the copied version,
-- put the original tables that are not in the list in t in place of their copied values
fix_not_modified_references = function(t, cache, copied_to_replace)
-- reverse copy cache
local ehcac = {}
for k, v in pairs(cache) do ehcac[v] = k end
-- build table of [original table] = replacement copied table
local to_replace = {}
for _, v in ipairs(copied_to_replace) do
local original = ehcac[v]
if original then -- table doesn't have an original value if it's a new table...
to_replace[original] = v
end
end
-- fix references to not-modified tables in t
local not_modified = {}
for original, modified in pairs(cache) do
if not to_replace[original] then
not_modified[modified] = original
end
end
replace_in_table(t, not_modified)
end
}

View file

@ -1,6 +1,6 @@
local atypes, ltypes
local eval, run_block
local replace_with_copied_values
local replace_with_copied_values, fix_not_modified_references
local common
local identifier_pattern
@ -44,6 +44,11 @@ end
common = {
--- merge interpreter state with global state
merge_state = function(state)
local mt = getmetatable(state.variables)
-- store current scoped variables before merging them
for fn in pairs(mt.scoped) do
common.scope:store_last_scope(state, fn)
end
-- merge alias state
local global = state.interpreter.global_state
for alias, fqm in pairs(state.aliases) do
@ -51,15 +56,38 @@ common = {
state.aliases[alias] = nil
end
-- merge modified mutable varables
local mt = getmetatable(state.variables)
replace_with_copied_values(global.variables, mt.copy_cache, mt.modified_tables)
local copy_cache, modified_tables = mt.copy_cache, mt.modified_tables
replace_with_copied_values(global.variables, copy_cache, modified_tables)
mt.copy_cache = {}
mt.modified = {}
mt.modified_tables = {}
mt.cache = {}
-- merge modified re-assigned variables
for var, value in pairs(state.variables) do
global.variables[var] = value
state.variables[var] = nil
if var:match("^"..identifier_pattern.."$") then -- skip scoped variables
global.variables[var] = value
state.variables[var] = nil
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
-- 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
for _, scope in ipairs(scopes) do
for var, value in pairs(scope) do
-- pretend the value for this scope is the global value so the cache system perform the new copy from it
local old_var = global.variables[var]
global.variables[var] = value
state.variables[var] = nil
scope[var] = state.variables[var]
mt.cache[var] = nil
global.variables[var] = old_var
end
end
end
-- restore last scopes
for fn in pairs(mt.scoped) do
common.scope:set_last_scope(state, fn)
end
end,
--- returns a variable's value, evaluating a pending expression if neccessary
@ -85,46 +113,66 @@ common = {
end,
--- handle scoped function
scope = {
push = function(self, state, fn)
init_scope = function(self, state, fn)
local scoped = getmetatable(state.variables).scoped
if not fn.scoped then error("trying to push a scope for a non-scoped function") end
-- add scope
if not scoped[fn] then
scoped[fn] = {}
end
local last_scope = scoped[fn][#scoped[fn]]
local fn_scope = {}
table.insert(scoped[fn], fn_scope)
-- add scoped variables to scope
if not fn.scoped then error("trying to initialize the scope stack for a non-scoped function") end
if not scoped[fn] then scoped[fn] = {} end
-- check scoped variables
for _, name in ipairs(fn.scoped) do
-- preserve current values in last scope
if last_scope then
last_scope[name] = state.variables[name]
-- put fresh variable from global state in scope
local val = state.interpreter.global_state.variables[name]
if val.type ~= "undefined argument" and val.type ~= "pending definition" then -- only possibilities for scoped variable, and they're immutable
error("invalid scoped variable")
end
-- set last value to nil to force to copy again from global variables in new scope
state.variables[name] = nil
local value = state.variables[name]
fn_scope[name] = value
end
end,
--- push a new scope for this function
push = function(self, state, fn)
local scoped = getmetatable(state.variables).scoped
self:init_scope(state, fn)
-- preserve current values in last scope
self:store_last_scope(state, fn)
-- add scope
local fn_scope = {}
table.insert(scoped[fn], fn_scope)
self:set_last_scope(state, fn)
end,
--- pop the last scope for this function
pop = function(self, state, fn)
local scoped = getmetatable(state.variables).scoped
if not scoped[fn] then error("trying to pop a scope without any pushed scope") end
-- remove current scope
table.remove(scoped[fn])
-- set scopped variables to previous scope
local last_scope = scoped[fn][#scoped[fn]]
-- restore last scope
self:set_last_scope(state, fn)
-- if the stack is empty,
-- we could remove mt.scoped[fn] I guess, but I don't think there's going to be a million different functions in a single game so should be ok
-- (anselme's performance is already bad enough, let's not create tables at each function call...)
end,
--- store the current values of the scoped variables in the last scope of this function
store_last_scope = function(self, state, fn)
local scopes = getmetatable(state.variables).scoped[fn]
local last_scope = scopes[#scopes]
if last_scope then
for _, name in ipairs(fn.scoped) do
state.variables[name] = last_scope[name]
for _, name in pairs(fn.scoped) do
local val = rawget(state.variables, name)
if val then
last_scope[name] = val
end
end
else -- no previous scope
for _, name in ipairs(fn.scoped) do
state.variables[name] = nil
end
end,
--- set scopped variables to previous scope
set_last_scope = function(self, state, fn)
local scopes = getmetatable(state.variables).scoped[fn]
for _, name in ipairs(fn.scoped) do
state.variables[name] = nil
end
local last_scope = scopes[#scopes]
if last_scope then
for name, val in pairs(last_scope) do
state.variables[name] = val
end
-- no need to remove this I think, there's not going to be a million different functions in a single game so we can keep the tables
-- (anselme's performance is already bad enough, let's not create tables at each function call...)
-- scoped[fn] = nil
end
end
},
@ -468,7 +516,8 @@ local types = require((...):gsub("interpreter%.common$", "stdlib.types"))
atypes, ltypes = types.anselme, types.lua
eval = require((...):gsub("common$", "expression"))
run_block = require((...):gsub("common$", "interpreter")).run_block
replace_with_copied_values = require((...):gsub("interpreter%.common$", "common")).replace_with_copied_values
local acommon = require((...):gsub("interpreter%.common$", "common"))
replace_with_copied_values, fix_not_modified_references = acommon.replace_with_copied_values, acommon.fix_not_modified_references
identifier_pattern = require((...):gsub("interpreter%.common$", "parser.common")).identifier_pattern
return common

View file

@ -77,7 +77,7 @@ lua_functions = {
local rval = r.value
local name = n.value
for _, ffqm in ipairs(rval) do
local var, vfqm = find(state.aliases, state.variables, ffqm..".", name)
local var, vfqm = find(state.aliases, state.interpreter.global_state.variables, ffqm..".", name)
if var then
return get_variable(state, vfqm)
end

View file

@ -0,0 +1,49 @@
:post run = "check"
:x = [99]
:l = [1,x]
:n = 0
$ f(t)
~ t!insert(len(l)+1)
f1: {l} {t}
CHECK
§ c
~ n < 1
REC
~ n += 1
~ f(t)
END REC
f2: {l}
CHECK 2
§ d
~ t!insert(len(t)+1)
~ t(2)!insert(len(l)+1)
~ error("t")
f3: {l} {t}
~ f(l)
FINAL
l: {l}
x: {x}
$ check
AFTER ERROR
l: {l}

View file

@ -0,0 +1,120 @@
local _={}
_[54]={}
_[53]={}
_[52]={}
_[51]={}
_[50]={}
_[49]={}
_[48]={}
_[47]={}
_[46]={}
_[45]={}
_[44]={}
_[43]={}
_[42]={}
_[41]={}
_[40]={}
_[39]={}
_[38]={}
_[37]={text="[1, [99], 3, 4]",tags=_[54]}
_[36]={text="l: ",tags=_[53]}
_[35]={text="AFTER ERROR",tags=_[52]}
_[34]={text="CHECK 2",tags=_[51]}
_[33]={text="[1, [99], 3, 4]",tags=_[50]}
_[32]={text="f2: ",tags=_[49]}
_[31]={text="CHECK",tags=_[48]}
_[30]={text="[1, [99], 3, 4]",tags=_[47]}
_[29]={text=" ",tags=_[46]}
_[28]={text="[1, [99], 3, 4]",tags=_[45]}
_[27]={text="f1: ",tags=_[44]}
_[26]={text="REC",tags=_[43]}
_[25]={text="CHECK",tags=_[42]}
_[24]={text="[1, [99], 3]",tags=_[41]}
_[23]={text=" ",tags=_[40]}
_[22]={text="[1, [99], 3]",tags=_[39]}
_[21]={text="f1: ",tags=_[38]}
_[20]={_[36],_[37]}
_[19]={_[35]}
_[18]={_[34]}
_[17]={_[32],_[33]}
_[16]={_[31]}
_[15]={_[27],_[28],_[29],_[30]}
_[14]={_[26]}
_[13]={_[25]}
_[12]={_[21],_[22],_[23],_[24]}
_[11]={"return"}
_[10]={"text",_[20]}
_[9]={"text",_[19]}
_[8]={"error","t; in Lua function \"error\"; at test/tests/scope checkpoint mutable bis error.ans:34; at test/tests/scope checkpoint mutable bis error.ans:21; at test/tests/scope checkpoint mutable bis error.ans:38"}
_[7]={"text",_[18]}
_[6]={"text",_[17]}
_[5]={"text",_[16]}
_[4]={"text",_[15]}
_[3]={"text",_[14]}
_[2]={"text",_[13]}
_[1]={"text",_[12]}
return {_[1],_[2],_[3],_[4],_[5],_[6],_[7],_[8],_[9],_[10],_[11]}
--[[
{ "text", { {
tags = {},
text = "f1: "
}, {
tags = {},
text = "[1, [99], 3]"
}, {
tags = {},
text = " "
}, {
tags = {},
text = "[1, [99], 3]"
} } }
{ "text", { {
tags = {},
text = "CHECK"
} } }
{ "text", { {
tags = {},
text = "REC"
} } }
{ "text", { {
tags = {},
text = "f1: "
}, {
tags = {},
text = "[1, [99], 3, 4]"
}, {
tags = {},
text = " "
}, {
tags = {},
text = "[1, [99], 3, 4]"
} } }
{ "text", { {
tags = {},
text = "CHECK"
} } }
{ "text", { {
tags = {},
text = "f2: "
}, {
tags = {},
text = "[1, [99], 3, 4]"
} } }
{ "text", { {
tags = {},
text = "CHECK 2"
} } }
{ "error", 't; in Lua function "error"; at test/tests/scope checkpoint mutable bis error.ans:34; at test/tests/scope checkpoint mutable bis error.ans:21; at test/tests/scope checkpoint mutable bis error.ans:38' }
{ "text", { {
tags = {},
text = "AFTER ERROR"
} } }
{ "text", { {
tags = {},
text = "l: "
}, {
tags = {},
text = "[1, [99], 3, 4]"
} } }
{ "return" }
]]--

View file

@ -0,0 +1,40 @@
:x = [99]
:l = [1,x]
:n = 0
$ f(t)
~ t!insert(len(l)+1)
f1: {l} {t}
CHECK
§ c
~ n < 1
REC
~ n += 1
~ f(t)
END REC
f2: {l}
CHECK 2
§ d
~ t!insert(len(t)+1)
~ t(2)!insert(len(l)+1)
f3: {l} {t}
~ f(l)
FINAL
l: {l}
x: {x}

View file

@ -0,0 +1,206 @@
local _={}
_[93]={}
_[92]={}
_[91]={}
_[90]={}
_[89]={}
_[88]={}
_[87]={}
_[86]={}
_[85]={}
_[84]={}
_[83]={}
_[82]={}
_[81]={}
_[80]={}
_[79]={}
_[78]={}
_[77]={}
_[76]={}
_[75]={}
_[74]={}
_[73]={}
_[72]={}
_[71]={}
_[70]={}
_[69]={}
_[68]={}
_[67]={}
_[66]={}
_[65]={}
_[64]={}
_[63]={}
_[62]={tags=_[93],text="[99, 6, 7]"}
_[61]={tags=_[92],text="x: "}
_[60]={tags=_[91],text="[1, [99, 6, 7], 3, 4, 5, 6]"}
_[59]={tags=_[90],text="l: "}
_[58]={tags=_[89],text="FINAL"}
_[57]={tags=_[88],text="[1, [99, 6, 7], 3, 4, 5, 6]"}
_[56]={tags=_[87],text=" "}
_[55]={tags=_[86],text="[1, [99, 6, 7], 3, 4, 5, 6]"}
_[54]={tags=_[85],text="f3: "}
_[53]={tags=_[84],text="CHECK 2"}
_[52]={tags=_[83],text="[1, [99, 6], 3, 4, 5]"}
_[51]={tags=_[82],text="f2: "}
_[50]={tags=_[81],text="END REC"}
_[49]={tags=_[80],text="[1, [99, 6], 3, 4, 5]"}
_[48]={tags=_[79],text=" "}
_[47]={tags=_[78],text="[1, [99, 6], 3, 4, 5]"}
_[46]={tags=_[77],text="f3: "}
_[45]={tags=_[76],text="CHECK 2"}
_[44]={tags=_[75],text="[1, [99], 3, 4]"}
_[43]={tags=_[74],text="f2: "}
_[42]={tags=_[73],text="CHECK"}
_[41]={tags=_[72],text="[1, [99], 3, 4]"}
_[40]={tags=_[71],text=" "}
_[39]={tags=_[70],text="[1, [99], 3, 4]"}
_[38]={tags=_[69],text="f1: "}
_[37]={tags=_[68],text="REC"}
_[36]={tags=_[67],text="CHECK"}
_[35]={tags=_[66],text="[1, [99], 3]"}
_[34]={tags=_[65],text=" "}
_[33]={tags=_[64],text="[1, [99], 3]"}
_[32]={tags=_[63],text="f1: "}
_[31]={_[61],_[62]}
_[30]={_[59],_[60]}
_[29]={_[58]}
_[28]={_[54],_[55],_[56],_[57]}
_[27]={_[53]}
_[26]={_[51],_[52]}
_[25]={_[50]}
_[24]={_[46],_[47],_[48],_[49]}
_[23]={_[45]}
_[22]={_[43],_[44]}
_[21]={_[42]}
_[20]={_[38],_[39],_[40],_[41]}
_[19]={_[37]}
_[18]={_[36]}
_[17]={_[32],_[33],_[34],_[35]}
_[16]={"return"}
_[15]={"text",_[31]}
_[14]={"text",_[30]}
_[13]={"text",_[29]}
_[12]={"text",_[28]}
_[11]={"text",_[27]}
_[10]={"text",_[26]}
_[9]={"text",_[25]}
_[8]={"text",_[24]}
_[7]={"text",_[23]}
_[6]={"text",_[22]}
_[5]={"text",_[21]}
_[4]={"text",_[20]}
_[3]={"text",_[19]}
_[2]={"text",_[18]}
_[1]={"text",_[17]}
return {_[1],_[2],_[3],_[4],_[5],_[6],_[7],_[8],_[9],_[10],_[11],_[12],_[13],_[14],_[15],_[16]}
--[[
{ "text", { {
tags = {},
text = "f1: "
}, {
tags = {},
text = "[1, [99], 3]"
}, {
tags = {},
text = " "
}, {
tags = {},
text = "[1, [99], 3]"
} } }
{ "text", { {
tags = {},
text = "CHECK"
} } }
{ "text", { {
tags = {},
text = "REC"
} } }
{ "text", { {
tags = {},
text = "f1: "
}, {
tags = {},
text = "[1, [99], 3, 4]"
}, {
tags = {},
text = " "
}, {
tags = {},
text = "[1, [99], 3, 4]"
} } }
{ "text", { {
tags = {},
text = "CHECK"
} } }
{ "text", { {
tags = {},
text = "f2: "
}, {
tags = {},
text = "[1, [99], 3, 4]"
} } }
{ "text", { {
tags = {},
text = "CHECK 2"
} } }
{ "text", { {
tags = {},
text = "f3: "
}, {
tags = {},
text = "[1, [99, 6], 3, 4, 5]"
}, {
tags = {},
text = " "
}, {
tags = {},
text = "[1, [99, 6], 3, 4, 5]"
} } }
{ "text", { {
tags = {},
text = "END REC"
} } }
{ "text", { {
tags = {},
text = "f2: "
}, {
tags = {},
text = "[1, [99, 6], 3, 4, 5]"
} } }
{ "text", { {
tags = {},
text = "CHECK 2"
} } }
{ "text", { {
tags = {},
text = "f3: "
}, {
tags = {},
text = "[1, [99, 6, 7], 3, 4, 5, 6]"
}, {
tags = {},
text = " "
}, {
tags = {},
text = "[1, [99, 6, 7], 3, 4, 5, 6]"
} } }
{ "text", { {
tags = {},
text = "FINAL"
} } }
{ "text", { {
tags = {},
text = "l: "
}, {
tags = {},
text = "[1, [99, 6, 7], 3, 4, 5, 6]"
} } }
{ "text", { {
tags = {},
text = "x: "
}, {
tags = {},
text = "[99, 6, 7]"
} } }
{ "return" }
]]--

View file

@ -0,0 +1,38 @@
:post run = "check"
:l = [1]
:n = 0
$ f(t)
~ t!insert(len(l)+1)
f1: {l} {t}
CHECK
§ c
~ n < 1
REC
~ n += 1
~ f(t)
END REC
~ t!insert(len(t)+1)
~ error("t")
f2: {l}
~ f(l)
FINAL
l: {l}
$ check
AFTER ERROR
l: {l}

View file

@ -0,0 +1,99 @@
local _={}
_[44]={}
_[43]={}
_[42]={}
_[41]={}
_[40]={}
_[39]={}
_[38]={}
_[37]={}
_[36]={}
_[35]={}
_[34]={}
_[33]={}
_[32]={}
_[31]={}
_[30]={tags=_[44],text="[1, 2, 3]"}
_[29]={tags=_[43],text="l: "}
_[28]={tags=_[42],text="AFTER ERROR"}
_[27]={tags=_[41],text="CHECK"}
_[26]={tags=_[40],text="[1, 2, 3]"}
_[25]={tags=_[39],text=" "}
_[24]={tags=_[38],text="[1, 2, 3]"}
_[23]={tags=_[37],text="f1: "}
_[22]={tags=_[36],text="REC"}
_[21]={tags=_[35],text="CHECK"}
_[20]={tags=_[34],text="[1, 2]"}
_[19]={tags=_[33],text=" "}
_[18]={tags=_[32],text="[1, 2]"}
_[17]={tags=_[31],text="f1: "}
_[16]={_[29],_[30]}
_[15]={_[28]}
_[14]={_[27]}
_[13]={_[23],_[24],_[25],_[26]}
_[12]={_[22]}
_[11]={_[21]}
_[10]={_[17],_[18],_[19],_[20]}
_[9]={"return"}
_[8]={"text",_[16]}
_[7]={"text",_[15]}
_[6]={"error","t; in Lua function \"error\"; at test/tests/scope checkpoint mutable error.ans:25; at test/tests/scope checkpoint mutable error.ans:19; at test/tests/scope checkpoint mutable error.ans:29"}
_[5]={"text",_[14]}
_[4]={"text",_[13]}
_[3]={"text",_[12]}
_[2]={"text",_[11]}
_[1]={"text",_[10]}
return {_[1],_[2],_[3],_[4],_[5],_[6],_[7],_[8],_[9]}
--[[
{ "text", { {
tags = {},
text = "f1: "
}, {
tags = {},
text = "[1, 2]"
}, {
tags = {},
text = " "
}, {
tags = {},
text = "[1, 2]"
} } }
{ "text", { {
tags = {},
text = "CHECK"
} } }
{ "text", { {
tags = {},
text = "REC"
} } }
{ "text", { {
tags = {},
text = "f1: "
}, {
tags = {},
text = "[1, 2, 3]"
}, {
tags = {},
text = " "
}, {
tags = {},
text = "[1, 2, 3]"
} } }
{ "text", { {
tags = {},
text = "CHECK"
} } }
{ "error", 't; in Lua function "error"; at test/tests/scope checkpoint mutable error.ans:25; at test/tests/scope checkpoint mutable error.ans:19; at test/tests/scope checkpoint mutable error.ans:29' }
{ "text", { {
tags = {},
text = "AFTER ERROR"
} } }
{ "text", { {
tags = {},
text = "l: "
}, {
tags = {},
text = "[1, 2, 3]"
} } }
{ "return" }
]]--

View file

@ -0,0 +1,51 @@
:post run = "check"
:x = [99]
:l = [1,x]
:n = 0
$ f(t)
~ t!insert(len(l)+1)
f1: {l} {t}
CHECK
§ c
~ n < 1
REC
~ n += 1
~ f(t)
END REC
~ x!insert(12)
f2: {l}
CHECK 2
§ d
~ t!insert(len(t)+1)
~ t(2)!insert(len(l)+1)
~ error("t")
f3: {l} {t}
~ f(l)
FINAL
l: {l}
x: {x}
$ check
AFTER ERROR
l: {l}

View file

@ -0,0 +1,120 @@
local _={}
_[54]={}
_[53]={}
_[52]={}
_[51]={}
_[50]={}
_[49]={}
_[48]={}
_[47]={}
_[46]={}
_[45]={}
_[44]={}
_[43]={}
_[42]={}
_[41]={}
_[40]={}
_[39]={}
_[38]={}
_[37]={text="[1, [99, 12], 3, 4]",tags=_[54]}
_[36]={text="l: ",tags=_[53]}
_[35]={text="AFTER ERROR",tags=_[52]}
_[34]={text="CHECK 2",tags=_[51]}
_[33]={text="[1, [99, 12], 3, 4]",tags=_[50]}
_[32]={text="f2: ",tags=_[49]}
_[31]={text="CHECK",tags=_[48]}
_[30]={text="[1, [99], 3, 4]",tags=_[47]}
_[29]={text=" ",tags=_[46]}
_[28]={text="[1, [99], 3, 4]",tags=_[45]}
_[27]={text="f1: ",tags=_[44]}
_[26]={text="REC",tags=_[43]}
_[25]={text="CHECK",tags=_[42]}
_[24]={text="[1, [99], 3]",tags=_[41]}
_[23]={text=" ",tags=_[40]}
_[22]={text="[1, [99], 3]",tags=_[39]}
_[21]={text="f1: ",tags=_[38]}
_[20]={_[36],_[37]}
_[19]={_[35]}
_[18]={_[34]}
_[17]={_[32],_[33]}
_[16]={_[31]}
_[15]={_[27],_[28],_[29],_[30]}
_[14]={_[26]}
_[13]={_[25]}
_[12]={_[21],_[22],_[23],_[24]}
_[11]={"return"}
_[10]={"text",_[20]}
_[9]={"text",_[19]}
_[8]={"error","t; in Lua function \"error\"; at test/tests/scope checkpoint mutable ter error.ans:36; at test/tests/scope checkpoint mutable ter error.ans:21; at test/tests/scope checkpoint mutable ter error.ans:40"}
_[7]={"text",_[18]}
_[6]={"text",_[17]}
_[5]={"text",_[16]}
_[4]={"text",_[15]}
_[3]={"text",_[14]}
_[2]={"text",_[13]}
_[1]={"text",_[12]}
return {_[1],_[2],_[3],_[4],_[5],_[6],_[7],_[8],_[9],_[10],_[11]}
--[[
{ "text", { {
tags = {},
text = "f1: "
}, {
tags = {},
text = "[1, [99], 3]"
}, {
tags = {},
text = " "
}, {
tags = {},
text = "[1, [99], 3]"
} } }
{ "text", { {
tags = {},
text = "CHECK"
} } }
{ "text", { {
tags = {},
text = "REC"
} } }
{ "text", { {
tags = {},
text = "f1: "
}, {
tags = {},
text = "[1, [99], 3, 4]"
}, {
tags = {},
text = " "
}, {
tags = {},
text = "[1, [99], 3, 4]"
} } }
{ "text", { {
tags = {},
text = "CHECK"
} } }
{ "text", { {
tags = {},
text = "f2: "
}, {
tags = {},
text = "[1, [99, 12], 3, 4]"
} } }
{ "text", { {
tags = {},
text = "CHECK 2"
} } }
{ "error", 't; in Lua function "error"; at test/tests/scope checkpoint mutable ter error.ans:36; at test/tests/scope checkpoint mutable ter error.ans:21; at test/tests/scope checkpoint mutable ter error.ans:40' }
{ "text", { {
tags = {},
text = "AFTER ERROR"
} } }
{ "text", { {
tags = {},
text = "l: "
}, {
tags = {},
text = "[1, [99, 12], 3, 4]"
} } }
{ "return" }
]]--

View file

@ -0,0 +1,42 @@
:x = [99]
:l = [1,x]
:n = 0
$ f(t)
~ t!insert(len(l)+1)
f1: {l} {t}
CHECK
§ c
~ n < 1
REC
~ n += 1
~ f(t)
END REC
~ x!insert(12)
f2: {l}
CHECK 2
§ d
~ t!insert(len(t)+1)
~ t(2)!insert(len(l)+1)
f3: {l} {t}
~ f(l)
FINAL
l: {l}
x: {x}

View file

@ -0,0 +1,206 @@
local _={}
_[93]={}
_[92]={}
_[91]={}
_[90]={}
_[89]={}
_[88]={}
_[87]={}
_[86]={}
_[85]={}
_[84]={}
_[83]={}
_[82]={}
_[81]={}
_[80]={}
_[79]={}
_[78]={}
_[77]={}
_[76]={}
_[75]={}
_[74]={}
_[73]={}
_[72]={}
_[71]={}
_[70]={}
_[69]={}
_[68]={}
_[67]={}
_[66]={}
_[65]={}
_[64]={}
_[63]={}
_[62]={tags=_[93],text="[99, 12, 6, 12, 7]"}
_[61]={tags=_[92],text="x: "}
_[60]={tags=_[91],text="[1, [99, 12, 6, 12, 7], 3, 4, 5, 6]"}
_[59]={tags=_[90],text="l: "}
_[58]={tags=_[89],text="FINAL"}
_[57]={tags=_[88],text="[1, [99, 12, 6, 12, 7], 3, 4, 5, 6]"}
_[56]={tags=_[87],text=" "}
_[55]={tags=_[86],text="[1, [99, 12, 6, 12, 7], 3, 4, 5, 6]"}
_[54]={tags=_[85],text="f3: "}
_[53]={tags=_[84],text="CHECK 2"}
_[52]={tags=_[83],text="[1, [99, 12, 6, 12], 3, 4, 5]"}
_[51]={tags=_[82],text="f2: "}
_[50]={tags=_[81],text="END REC"}
_[49]={tags=_[80],text="[1, [99, 12, 6], 3, 4, 5]"}
_[48]={tags=_[79],text=" "}
_[47]={tags=_[78],text="[1, [99, 12, 6], 3, 4, 5]"}
_[46]={tags=_[77],text="f3: "}
_[45]={tags=_[76],text="CHECK 2"}
_[44]={tags=_[75],text="[1, [99, 12], 3, 4]"}
_[43]={tags=_[74],text="f2: "}
_[42]={tags=_[73],text="CHECK"}
_[41]={tags=_[72],text="[1, [99], 3, 4]"}
_[40]={tags=_[71],text=" "}
_[39]={tags=_[70],text="[1, [99], 3, 4]"}
_[38]={tags=_[69],text="f1: "}
_[37]={tags=_[68],text="REC"}
_[36]={tags=_[67],text="CHECK"}
_[35]={tags=_[66],text="[1, [99], 3]"}
_[34]={tags=_[65],text=" "}
_[33]={tags=_[64],text="[1, [99], 3]"}
_[32]={tags=_[63],text="f1: "}
_[31]={_[61],_[62]}
_[30]={_[59],_[60]}
_[29]={_[58]}
_[28]={_[54],_[55],_[56],_[57]}
_[27]={_[53]}
_[26]={_[51],_[52]}
_[25]={_[50]}
_[24]={_[46],_[47],_[48],_[49]}
_[23]={_[45]}
_[22]={_[43],_[44]}
_[21]={_[42]}
_[20]={_[38],_[39],_[40],_[41]}
_[19]={_[37]}
_[18]={_[36]}
_[17]={_[32],_[33],_[34],_[35]}
_[16]={"return"}
_[15]={"text",_[31]}
_[14]={"text",_[30]}
_[13]={"text",_[29]}
_[12]={"text",_[28]}
_[11]={"text",_[27]}
_[10]={"text",_[26]}
_[9]={"text",_[25]}
_[8]={"text",_[24]}
_[7]={"text",_[23]}
_[6]={"text",_[22]}
_[5]={"text",_[21]}
_[4]={"text",_[20]}
_[3]={"text",_[19]}
_[2]={"text",_[18]}
_[1]={"text",_[17]}
return {_[1],_[2],_[3],_[4],_[5],_[6],_[7],_[8],_[9],_[10],_[11],_[12],_[13],_[14],_[15],_[16]}
--[[
{ "text", { {
tags = {},
text = "f1: "
}, {
tags = {},
text = "[1, [99], 3]"
}, {
tags = {},
text = " "
}, {
tags = {},
text = "[1, [99], 3]"
} } }
{ "text", { {
tags = {},
text = "CHECK"
} } }
{ "text", { {
tags = {},
text = "REC"
} } }
{ "text", { {
tags = {},
text = "f1: "
}, {
tags = {},
text = "[1, [99], 3, 4]"
}, {
tags = {},
text = " "
}, {
tags = {},
text = "[1, [99], 3, 4]"
} } }
{ "text", { {
tags = {},
text = "CHECK"
} } }
{ "text", { {
tags = {},
text = "f2: "
}, {
tags = {},
text = "[1, [99, 12], 3, 4]"
} } }
{ "text", { {
tags = {},
text = "CHECK 2"
} } }
{ "text", { {
tags = {},
text = "f3: "
}, {
tags = {},
text = "[1, [99, 12, 6], 3, 4, 5]"
}, {
tags = {},
text = " "
}, {
tags = {},
text = "[1, [99, 12, 6], 3, 4, 5]"
} } }
{ "text", { {
tags = {},
text = "END REC"
} } }
{ "text", { {
tags = {},
text = "f2: "
}, {
tags = {},
text = "[1, [99, 12, 6, 12], 3, 4, 5]"
} } }
{ "text", { {
tags = {},
text = "CHECK 2"
} } }
{ "text", { {
tags = {},
text = "f3: "
}, {
tags = {},
text = "[1, [99, 12, 6, 12, 7], 3, 4, 5, 6]"
}, {
tags = {},
text = " "
}, {
tags = {},
text = "[1, [99, 12, 6, 12, 7], 3, 4, 5, 6]"
} } }
{ "text", { {
tags = {},
text = "FINAL"
} } }
{ "text", { {
tags = {},
text = "l: "
}, {
tags = {},
text = "[1, [99, 12, 6, 12, 7], 3, 4, 5, 6]"
} } }
{ "text", { {
tags = {},
text = "x: "
}, {
tags = {},
text = "[99, 12, 6, 12, 7]"
} } }
{ "return" }
]]--

View file

@ -0,0 +1,29 @@
:l = [1]
:n = 0
$ f(t)
~ t!insert(len(l)+1)
f1: {l} {t}
CHECK
§ c
~ n < 1
REC
~ n += 1
~ f(t)
END REC
~ t!insert(len(t)+1)
f2: {l}
~ f(l)
FINAL
l: {l}

View file

@ -0,0 +1,131 @@
local _={}
_[59]={}
_[58]={}
_[57]={}
_[56]={}
_[55]={}
_[54]={}
_[53]={}
_[52]={}
_[51]={}
_[50]={}
_[49]={}
_[48]={}
_[47]={}
_[46]={}
_[45]={}
_[44]={}
_[43]={}
_[42]={}
_[41]={}
_[40]={text="[1, 2, 3, 4, 5]",tags=_[59]}
_[39]={text="l: ",tags=_[58]}
_[38]={text="FINAL",tags=_[57]}
_[37]={text="[1, 2, 3, 4, 5]",tags=_[56]}
_[36]={text="f2: ",tags=_[55]}
_[35]={text="END REC",tags=_[54]}
_[34]={text="[1, 2, 3, 4]",tags=_[53]}
_[33]={text="f2: ",tags=_[52]}
_[32]={text="CHECK",tags=_[51]}
_[31]={text="[1, 2, 3]",tags=_[50]}
_[30]={text=" ",tags=_[49]}
_[29]={text="[1, 2, 3]",tags=_[48]}
_[28]={text="f1: ",tags=_[47]}
_[27]={text="REC",tags=_[46]}
_[26]={text="CHECK",tags=_[45]}
_[25]={text="[1, 2]",tags=_[44]}
_[24]={text=" ",tags=_[43]}
_[23]={text="[1, 2]",tags=_[42]}
_[22]={text="f1: ",tags=_[41]}
_[21]={_[39],_[40]}
_[20]={_[38]}
_[19]={_[36],_[37]}
_[18]={_[35]}
_[17]={_[33],_[34]}
_[16]={_[32]}
_[15]={_[28],_[29],_[30],_[31]}
_[14]={_[27]}
_[13]={_[26]}
_[12]={_[22],_[23],_[24],_[25]}
_[11]={"return"}
_[10]={"text",_[21]}
_[9]={"text",_[20]}
_[8]={"text",_[19]}
_[7]={"text",_[18]}
_[6]={"text",_[17]}
_[5]={"text",_[16]}
_[4]={"text",_[15]}
_[3]={"text",_[14]}
_[2]={"text",_[13]}
_[1]={"text",_[12]}
return {_[1],_[2],_[3],_[4],_[5],_[6],_[7],_[8],_[9],_[10],_[11]}
--[[
{ "text", { {
tags = {},
text = "f1: "
}, {
tags = {},
text = "[1, 2]"
}, {
tags = {},
text = " "
}, {
tags = {},
text = "[1, 2]"
} } }
{ "text", { {
tags = {},
text = "CHECK"
} } }
{ "text", { {
tags = {},
text = "REC"
} } }
{ "text", { {
tags = {},
text = "f1: "
}, {
tags = {},
text = "[1, 2, 3]"
}, {
tags = {},
text = " "
}, {
tags = {},
text = "[1, 2, 3]"
} } }
{ "text", { {
tags = {},
text = "CHECK"
} } }
{ "text", { {
tags = {},
text = "f2: "
}, {
tags = {},
text = "[1, 2, 3, 4]"
} } }
{ "text", { {
tags = {},
text = "END REC"
} } }
{ "text", { {
tags = {},
text = "f2: "
}, {
tags = {},
text = "[1, 2, 3, 4, 5]"
} } }
{ "text", { {
tags = {},
text = "FINAL"
} } }
{ "text", { {
tags = {},
text = "l: "
}, {
tags = {},
text = "[1, 2, 3, 4, 5]"
} } }
{ "return" }
]]--