mirror of
https://github.com/Reuh/anselme.git
synced 2025-10-27 16:49:31 +00:00
Variable must now be explicitly marked as persistent
This commit is contained in:
parent
e9606cdee0
commit
2c6d66c222
11 changed files with 384 additions and 106 deletions
27
LANGUAGE.md
27
LANGUAGE.md
|
|
@ -305,7 +305,7 @@ When a parameter list is given (or just empty parentheses `()`), the function is
|
||||||
~ g
|
~ g
|
||||||
```
|
```
|
||||||
|
|
||||||
This is basically the behaviour you'd expect from functions in most other programming languages, and what you would use in Anselme any time you don't care about storing the function variables or want the exact same initial function variables each time you call the function (e.g. recursion). Scoped variables are not kept in save files, and are not affected by checkpointing.
|
This is basically the behaviour you'd expect from functions in most other programming languages, and what you would use in Anselme any time you don't care about storing the function variables or want the exact same initial function variables each time you call the function (e.g. recursion). Scoped variables can not be persistent, and are not affected by checkpointing.
|
||||||
|
|
||||||
Functions with the same name can be defined, as long as they have a different arguments. Functions will be selected based on the number of arguments given, their name and their type constraint:
|
Functions with the same name can be defined, as long as they have a different arguments. Functions will be selected based on the number of arguments given, their name and their type constraint:
|
||||||
|
|
||||||
|
|
@ -362,6 +362,8 @@ Functions always have the following variables defined in its namespace by defaul
|
||||||
`👁️`: number, number of times the function was executed before
|
`👁️`: number, number of times the function was executed before
|
||||||
`🔖`: function reference, last reached checkpoint. `nil` if no checkpoint reached.
|
`🔖`: function reference, last reached checkpoint. `nil` if no checkpoint reached.
|
||||||
|
|
||||||
|
These variables are persistent, unless the function is scoped.
|
||||||
|
|
||||||
* `:!`: checkpoint definition. Followed by an [identifier](#identifiers), then eventually an [alias](#aliases). Define a checkpoint. Also define a new namespace for its children.
|
* `:!`: checkpoint definition. Followed by an [identifier](#identifiers), then eventually an [alias](#aliases). Define a checkpoint. Also define a new namespace for its children.
|
||||||
|
|
||||||
Checkpoints share most of their behavior with functions, with several exceptions. Like functions, the body is not executed when the line is reached; it must either be explicitely called in an expression or executed when resuming the parent function (see checkpoint behaviour below). Can be called in an expression. See [expressions](#checkpoint-calls) to see the different ways of calling a checkpoint manually.
|
Checkpoints share most of their behavior with functions, with several exceptions. Like functions, the body is not executed when the line is reached; it must either be explicitely called in an expression or executed when resuming the parent function (see checkpoint behaviour below). Can be called in an expression. See [expressions](#checkpoint-calls) to see the different ways of calling a checkpoint manually.
|
||||||
|
|
@ -383,6 +385,8 @@ Checkpoints always have the following variable defined in its namespace by defau
|
||||||
`👁️`: number, number of times the checkpoint was executed before
|
`👁️`: 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 reached before (includes times where it was resumed from and executed)
|
||||||
|
|
||||||
|
These variables are persistent.
|
||||||
|
|
||||||
* `:%`: class definition. Followed by an [identifier](#identifiers), then eventually an [alias](#aliases). Define a class. Also define a new namespace for its children.
|
* `:%`: class definition. Followed by an [identifier](#identifiers), then eventually an [alias](#aliases). Define a class. Also define a new namespace for its children.
|
||||||
|
|
||||||
Classes share most of their behavior with functions, with a few exceptions. Classes can not take arguments or be scoped; and when called, if the function does not return a value or returns `()` (nil), it will returns a new object instead based on this class. The object can be used to access variables ("attributes") defined in the class, but if one of these attributes is modified on the object it will not change the value in the base class but only in the object.
|
Classes share most of their behavior with functions, with a few exceptions. Classes can not take arguments or be scoped; and when called, if the function does not return a value or returns `()` (nil), it will returns a new object instead based on this class. The object can be used to access variables ("attributes") defined in the class, but if one of these attributes is modified on the object it will not change the value in the base class but only in the object.
|
||||||
|
|
@ -415,6 +419,8 @@ Note that the new object returned by the class is also automatically given an an
|
||||||
~ object!show
|
~ object!show
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Classes have the same default variable defined as functions.
|
||||||
|
|
||||||
* `:`: variable declaration. Followed by an [identifier](#identifiers) (with eventually an [alias](#aliases)), a `=` and an [expression](#expressions). Defines a variable with a default value and this identifier in the current [namespace]("identifiers"). The expression is not evaluated instantly, but the first time the variable is used. Don't accept children lines.
|
* `:`: variable declaration. Followed by an [identifier](#identifiers) (with eventually an [alias](#aliases)), a `=` and an [expression](#expressions). Defines a variable with a default value and this identifier in the current [namespace]("identifiers"). The expression is not evaluated instantly, but the first time the variable is used. Don't accept children lines.
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
@ -422,12 +428,19 @@ Note that the new object returned by the class is also automatically given an an
|
||||||
:bar : alias = 12
|
:bar : alias = 12
|
||||||
```
|
```
|
||||||
|
|
||||||
* `::`: constant declaration. Work the same way as a variable declaration, but the variable can't be reassigned after their declaration and first evaluation, and their value is marked as constant (i.e. can not be modified even it is of a mutable type). Constants are not stored in save files and should therefore always contain the result of the expression written in the script file, even if the script has been updated.
|
* `::`: constant declaration. Work the same way as a variable declaration, but the variable can't be reassigned after their declaration and first evaluation, and their value is marked as constant (i.e. can not be modified even it is of a mutable type).
|
||||||
|
|
||||||
```
|
```
|
||||||
::foo = 42
|
::foo = 42
|
||||||
```
|
```
|
||||||
|
|
||||||
|
* `:@`: persistent variable declaration. Work the same way as a variable declaration, but the variable will be stored in the save file, and if we loaded a save file its value will be retrieved from the save file instead of from the expression's result.
|
||||||
|
|
||||||
|
```
|
||||||
|
:@foo = 42
|
||||||
|
:@bar : alias = 12
|
||||||
|
```
|
||||||
|
|
||||||
### Text interpolation
|
### Text interpolation
|
||||||
|
|
||||||
Text and choice lines allow for arbitrary text. Expression can be evaluated and inserted into the text as the line is executed by enclosing the [expression](#expressions) into brackets. The expressions are evaluated in the same order as the reading direction.
|
Text and choice lines allow for arbitrary text. Expression can be evaluated and inserted into the text as the line is executed by enclosing the [expression](#expressions) into brackets. The expressions are evaluated in the same order as the reading direction.
|
||||||
|
|
@ -575,7 +588,7 @@ Var1 in the fn1 namespace = 2: {fn1.var1}
|
||||||
|
|
||||||
#### Aliases
|
#### Aliases
|
||||||
|
|
||||||
When defining identifiers (in variables, functions or checkpoint definitions), they can be followed by a colon and another identifier. This identifier can be used as a new way to access the identifier (i.e., an alias).
|
When defining identifiers (in variables, functions, checkpoint or class definitions), they can be followed by a colon and another identifier. This identifier can be used as a new way to access the identifier (i.e., an alias).
|
||||||
|
|
||||||
```
|
```
|
||||||
:name: alias = 42
|
:name: alias = 42
|
||||||
|
|
@ -585,20 +598,20 @@ When defining identifiers (in variables, functions or checkpoint definitions), t
|
||||||
|
|
||||||
Note that alias have priority over normal identifiers; if both an identifier and an alias have the same name, the alias will be used.
|
Note that alias have priority over normal identifiers; if both an identifier and an alias have the same name, the alias will be used.
|
||||||
|
|
||||||
The main purpose of aliases is translation. When saving the state of your game's script, Anselme will store the name of the variables and their contents, and require the name to be the same when loading the save later, in order to correctly restore their values.
|
The main purpose of aliases is translation. When saving the state of your game's script, Anselme will store the name of the persistent variables and their contents, and require the name to be the same when loading the save later, in order to correctly restore their values.
|
||||||
|
|
||||||
This behaviour is fine if you only have one language; but if you want to translate your game, this means the translations will need to keep using the original, untranslated variables and functions names if it wants to be compatible with saves in differents languages. Which is not very practical or nice to read.
|
This behaviour is fine if you only have one language; but if you want to translate your game, this means the translations will need to keep using the original, untranslated persistent variables and functions names if it wants to be compatible with saves in differents languages. Which is not very practical or nice to read.
|
||||||
|
|
||||||
Anselme's solution is to keep the original name in the translated script file, but alias them with a translated name. This way, the translated script can be written withou constantly switching languages:
|
Anselme's solution is to keep the original name in the translated script file, but alias them with a translated name. This way, the translated script can be written withou constantly switching languages:
|
||||||
|
|
||||||
```
|
```
|
||||||
(in the original, english script)
|
(in the original, english script)
|
||||||
:player name = "John Pizzapone"
|
:@player name = "John Pizzapone"
|
||||||
|
|
||||||
Hi {player name}!
|
Hi {player name}!
|
||||||
|
|
||||||
(in a translated, french script)
|
(in a translated, french script)
|
||||||
:player name : nom du joueur = "John Pizzapone"
|
:@player name : nom du joueur = "John Pizzapone"
|
||||||
|
|
||||||
Salut {nom du joueur} !
|
Salut {nom du joueur} !
|
||||||
```
|
```
|
||||||
|
|
|
||||||
47
anselme.lua
47
anselme.lua
|
|
@ -560,15 +560,26 @@ local vm_mt = {
|
||||||
|
|
||||||
--- Save/load script state
|
--- Save/load script state
|
||||||
--
|
--
|
||||||
-- Only saves variables full names and values, so make sure to not change important variables, checkpoints and functions names between a save and a load.
|
-- Only saves persistent variables' full names and values.
|
||||||
-- Also only save variables with usable identifiers, so will skip functions with arguments, operators, etc. (i.e. every scoped functions).
|
-- Make sure to not change persistent variables names, class name, class attribute names, checkpoint names and functions names between a
|
||||||
-- Loading should be done after loading all the game scripts (otherwise you will "variable already defined" errors).
|
-- save and a load (alias can of course be changed), as Anselme will not be able to match them to the old names stored in the save file.
|
||||||
|
--
|
||||||
|
-- If a variable is stored in the save file but is not marked as persistent in the current scripts (e.g. if you updated the Anselme scripts to
|
||||||
|
-- remove the persistence), it will not be loaded.
|
||||||
|
--
|
||||||
|
-- Loading should be done after loading all the game scripts (otherwise you will get "variable already defined" errors).
|
||||||
--
|
--
|
||||||
-- Returns this VM.
|
-- Returns this VM.
|
||||||
load = function(self, data)
|
load = function(self, data)
|
||||||
assert(anselme.versions.save == data.anselme.versions.save, ("trying to load data from an incompatible version of Anselme; save was done using save version %s but current version is %s"):format(data.anselme.versions.save, anselme.versions.save))
|
assert(anselme.versions.save == data.anselme.versions.save, ("trying to load data from an incompatible version of Anselme; save was done using save version %s but current version is %s"):format(data.anselme.versions.save, anselme.versions.save))
|
||||||
for k, v in pairs(data.variables) do
|
for k, v in pairs(data.variables) do
|
||||||
self.state.variables[k] = v
|
if self.state.variable_metadata[k] then
|
||||||
|
if self.state.variable_metadata[k].persistent then
|
||||||
|
self.state.variables[k] = v
|
||||||
|
end
|
||||||
|
else
|
||||||
|
self.state.variables[k] = v -- non-existent variable: keep it in case there was a mistake, it's not going to affect anything anyway
|
||||||
|
end
|
||||||
end
|
end
|
||||||
return self
|
return self
|
||||||
end,
|
end,
|
||||||
|
|
@ -580,23 +591,7 @@ local vm_mt = {
|
||||||
local vars = {}
|
local vars = {}
|
||||||
for k, v in pairs(self.state.variables) do
|
for k, v in pairs(self.state.variables) do
|
||||||
if should_keep_variable(self.state, k, v) then
|
if should_keep_variable(self.state, k, v) then
|
||||||
if v.type == "object" then -- filter object attributes
|
vars[k] = v
|
||||||
local attributes = {}
|
|
||||||
for kk, vv in pairs(v.value.attributes) do
|
|
||||||
if should_keep_variable(self.state, kk, vv) then
|
|
||||||
attributes[kk] = vv
|
|
||||||
end
|
|
||||||
end
|
|
||||||
vars[k] = {
|
|
||||||
type = "object",
|
|
||||||
value = {
|
|
||||||
class = v.value.class,
|
|
||||||
attributes = attributes
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
vars[k] = v
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return {
|
return {
|
||||||
|
|
@ -669,8 +664,7 @@ local vm_mt = {
|
||||||
builtin_aliases = self.state.builtin_aliases,
|
builtin_aliases = self.state.builtin_aliases,
|
||||||
aliases = setmetatable({}, { __index = self.state.aliases }),
|
aliases = setmetatable({}, { __index = self.state.aliases }),
|
||||||
functions = self.state.functions, -- no need for a cache as we can't define or modify any function from the interpreter for now
|
functions = self.state.functions, -- no need for a cache as we can't define or modify any function from the interpreter for now
|
||||||
variable_constraints = self.state.variable_constraints, -- no cache as constraints are expected to be constant
|
variable_metadata = self.state.variable_metadata, -- no cache as metadata are expected to be constant
|
||||||
variable_constants = self.state.variable_constants,
|
|
||||||
variables = setmetatable({}, {
|
variables = setmetatable({}, {
|
||||||
__index = function(variables, k)
|
__index = function(variables, k)
|
||||||
local cache = getmetatable(variables).cache
|
local cache = getmetatable(variables).cache
|
||||||
|
|
@ -763,11 +757,8 @@ return setmetatable(anselme, {
|
||||||
-- }, ...
|
-- }, ...
|
||||||
-- }, ...
|
-- }, ...
|
||||||
},
|
},
|
||||||
variable_constraints = {
|
variable_metadata = {
|
||||||
-- foo = { constraint }, ...
|
-- foo = { constant = true, persistent = true, constraint = constraint, ... }, ...
|
||||||
},
|
|
||||||
variable_constants = {
|
|
||||||
-- foo = true, ...
|
|
||||||
},
|
},
|
||||||
variables = {
|
variables = {
|
||||||
-- foo = {
|
-- foo = {
|
||||||
|
|
|
||||||
13
anselme.md
13
anselme.md
|
|
@ -214,6 +214,8 @@ Set some code that will be injected at specific places in all code loaded after
|
||||||
* `"function return"`: injected at the end of each return's children that is contained in a non-scoped function
|
* `"function return"`: injected at the end of each return's children that is contained in a non-scoped function
|
||||||
* `"checkpoint start"`: injected at the start of every checkpoint
|
* `"checkpoint start"`: injected at the start of every checkpoint
|
||||||
* `"checkpoint end"`: injected at the end of every checkpoint
|
* `"checkpoint end"`: injected at the end of every checkpoint
|
||||||
|
* `"class start"`: injected at the start of every class
|
||||||
|
* `"class end"`: injected at the end of every class
|
||||||
* `"scoped function start"`: injected at the start of every scoped function
|
* `"scoped function start"`: injected at the start of every scoped function
|
||||||
* `"scoped function end"`: injected at the end of every scoped function
|
* `"scoped function end"`: injected at the end of every scoped function
|
||||||
* `"scoped function return"`: injected at the end of each return's children that is contained in a scoped function
|
* `"scoped function return"`: injected at the end of each return's children that is contained in a scoped function
|
||||||
|
|
@ -248,9 +250,14 @@ Define functions from Lua.
|
||||||
|
|
||||||
Save/load script state
|
Save/load script state
|
||||||
|
|
||||||
Only saves variables full names and values, so make sure to not change important variables, checkpoints and functions names between a save and a load.
|
Only saves persistent variables' full names and values.
|
||||||
Also only save variables with usable identifiers, so will skip functions with arguments, operators, etc. (i.e. every scoped functions).
|
Make sure to not change persistent variables names, class name, class attribute names, checkpoint names and functions names between a
|
||||||
Loading should be done after loading all the game scripts (otherwise you will "variable already defined" errors).
|
save and a load (alias can of course be changed), as Anselme will not be able to match them to the old names stored in the save file.
|
||||||
|
|
||||||
|
If a variable is stored in the save file but is not marked as persistent in the current scripts (e.g. if you updated the Anselme scripts to
|
||||||
|
remove the persistence), it will not be loaded.
|
||||||
|
|
||||||
|
Loading should be done after loading all the game scripts (otherwise you will get "variable already defined" errors).
|
||||||
|
|
||||||
Returns this VM.
|
Returns this VM.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ common = {
|
||||||
-- returns depth, or math.huge if no constraint
|
-- returns depth, or math.huge if no constraint
|
||||||
-- returns nil, err
|
-- returns nil, err
|
||||||
check_constraint = function(state, fqm, val)
|
check_constraint = function(state, fqm, val)
|
||||||
local constraint = state.variable_constraints[fqm]
|
local constraint = state.variable_metadata[fqm].constraint
|
||||||
if constraint then
|
if constraint then
|
||||||
if not constraint.value then
|
if not constraint.value then
|
||||||
local v, e = eval(state, constraint.pending)
|
local v, e = eval(state, constraint.pending)
|
||||||
|
|
@ -87,7 +87,7 @@ common = {
|
||||||
-- returns true
|
-- returns true
|
||||||
-- returns nil, mutation illegal message
|
-- returns nil, mutation illegal message
|
||||||
check_mutable = function(state, fqm)
|
check_mutable = function(state, fqm)
|
||||||
if state.variable_constants[fqm] then
|
if state.variable_metadata[fqm].constant then
|
||||||
return nil, ("can't change the value of a constant %q"):format(fqm)
|
return nil, ("can't change the value of a constant %q"):format(fqm)
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
|
|
@ -114,12 +114,12 @@ common = {
|
||||||
return nil, ("%s; while evaluating default value for variable %q defined at %s"):format(e, fqm, var.value.source)
|
return nil, ("%s; while evaluating default value for variable %q defined at %s"):format(e, fqm, var.value.source)
|
||||||
end
|
end
|
||||||
-- make constant if variable is constant
|
-- make constant if variable is constant
|
||||||
if state.variable_constants[fqm] then
|
if state.variable_metadata[fqm].constant then
|
||||||
v = copy(v)
|
v = copy(v)
|
||||||
common.mark_constant(v)
|
common.mark_constant(v)
|
||||||
end
|
end
|
||||||
-- set variable
|
-- set variable
|
||||||
local s, err = common.set_variable(state, fqm, v, state.variable_constants[fqm])
|
local s, err = common.set_variable(state, fqm, v, state.variable_metadata[fqm].constant)
|
||||||
if not s then return nil, err end
|
if not s then return nil, err end
|
||||||
return v
|
return v
|
||||||
else
|
else
|
||||||
|
|
@ -218,9 +218,10 @@ common = {
|
||||||
table.insert(modified, v)
|
table.insert(modified, v)
|
||||||
end,
|
end,
|
||||||
--- returns true if a variable should be persisted on save
|
--- returns true if a variable should be persisted on save
|
||||||
-- will exclude: undefined variables, variables in scoped functions, constants, internal anselme variables
|
-- will exclude: variable that have not been evaluated yet and non-persistent variable
|
||||||
|
-- this will by consequence excludes variable in scoped variables (can be neither persistent not evaluated into global state), constants (can not be persistent), internal anselme variables (not marked persistent), etc.
|
||||||
should_keep_variable = function(state, name, value)
|
should_keep_variable = function(state, name, value)
|
||||||
return value.type ~= "undefined argument" and value.type ~= "pending definition" and name:match("^"..identifier_pattern.."$") and not name:match("^anselme%.") and not state.variable_constants[name]
|
return value.type ~= "pending definition" and state.variable_metadata[name].persistent
|
||||||
end,
|
end,
|
||||||
--- check truthyness of an anselme value
|
--- check truthyness of an anselme value
|
||||||
truthy = function(val)
|
truthy = function(val)
|
||||||
|
|
|
||||||
|
|
@ -309,7 +309,7 @@ local function eval(state, exp)
|
||||||
local depth, err = check_constraint(state, param.full_name, val)
|
local depth, err = check_constraint(state, param.full_name, val)
|
||||||
if not depth then
|
if not depth then
|
||||||
ok = false
|
ok = false
|
||||||
local v = state.variable_constraints[param.full_name].value
|
local v = state.variable_metadata[param.full_name].constraint.value
|
||||||
table.insert(tried_function_error_messages, ("%s: argument %s is not of expected type %s"):format(fn.pretty_signature, param.name, format(v) or v))
|
table.insert(tried_function_error_messages, ("%s: argument %s is not of expected type %s"):format(fn.pretty_signature, param.name, format(v) or v))
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
|
@ -347,7 +347,7 @@ local function eval(state, exp)
|
||||||
local depth, err = check_constraint(state, param.full_name, assignment)
|
local depth, err = check_constraint(state, param.full_name, assignment)
|
||||||
if not depth then
|
if not depth then
|
||||||
ok = false
|
ok = false
|
||||||
local v = state.variable_constraints[param.full_name].value
|
local v = state.variable_metadata[param.full_name].constraint.value
|
||||||
table.insert(tried_function_error_messages, ("%s: argument %s is not of expected type %s"):format(fn.pretty_signature, param.name, format(v) or v))
|
table.insert(tried_function_error_messages, ("%s: argument %s is not of expected type %s"):format(fn.pretty_signature, param.name, format(v) or v))
|
||||||
end
|
end
|
||||||
depths.assignment = depth
|
depths.assignment = depth
|
||||||
|
|
@ -418,8 +418,11 @@ local function eval(state, exp)
|
||||||
if not s then return nil, e end
|
if not s then return nil, e end
|
||||||
end
|
end
|
||||||
-- get function vars
|
-- get function vars
|
||||||
local checkpoint, checkpointe = get_variable(state, fn.namespace.."🔖")
|
local checkpoint, checkpointe
|
||||||
if not checkpoint then return nil, checkpointe end
|
if fn.resumable then
|
||||||
|
checkpoint, checkpointe = get_variable(state, fn.namespace.."🔖")
|
||||||
|
if not checkpoint then return nil, checkpointe end
|
||||||
|
end
|
||||||
local seen, seene = get_variable(state, fn.namespace.."👁️")
|
local seen, seene = get_variable(state, fn.namespace.."👁️")
|
||||||
if not seen then return nil, seene end
|
if not seen then return nil, seene end
|
||||||
-- execute lua functions
|
-- execute lua functions
|
||||||
|
|
@ -488,7 +491,7 @@ local function eval(state, exp)
|
||||||
else
|
else
|
||||||
local e
|
local e
|
||||||
-- eval function from start
|
-- eval function from start
|
||||||
if paren_call or checkpoint.type == "nil" then
|
if paren_call or not fn.resumable or checkpoint.type == "nil" then
|
||||||
ret, e = run(state, fn.child)
|
ret, e = run(state, fn.child)
|
||||||
-- resume at last checkpoint
|
-- resume at last checkpoint
|
||||||
else
|
else
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,7 @@ run_line = function(state, line)
|
||||||
value = reached.value + 1
|
value = reached.value + 1
|
||||||
})
|
})
|
||||||
if not s then return nil, e end
|
if not s then return nil, e end
|
||||||
s, e = set_variable(state, line.parent_function.namespace.."🔖", {
|
s, e = set_variable(state, line.parent_resumable.namespace.."🔖", {
|
||||||
type = "function reference",
|
type = "function reference",
|
||||||
value = { line.name }
|
value = { line.name }
|
||||||
})
|
})
|
||||||
|
|
@ -160,7 +160,7 @@ run_block = function(state, block, resume_from_there, i, j)
|
||||||
if not reached then return nil, reachede end
|
if not reached then return nil, reachede end
|
||||||
local seen, seene = get_variable(state, parent_line.namespace.."👁️")
|
local seen, seene = get_variable(state, parent_line.namespace.."👁️")
|
||||||
if not seen then return nil, seene end
|
if not seen then return nil, seene end
|
||||||
local checkpoint, checkpointe = get_variable(state, parent_line.parent_function.namespace.."🔖")
|
local checkpoint, checkpointe = get_variable(state, parent_line.parent_resumable.namespace.."🔖")
|
||||||
if not checkpoint then return nil, checkpointe end
|
if not checkpoint then return nil, checkpointe end
|
||||||
local s, e = set_variable(state, parent_line.namespace.."👁️", {
|
local s, e = set_variable(state, parent_line.namespace.."👁️", {
|
||||||
type = "number",
|
type = "number",
|
||||||
|
|
@ -175,7 +175,7 @@ run_block = function(state, block, resume_from_there, i, j)
|
||||||
-- don't update checkpoint if an already more precise checkpoint is set
|
-- 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)
|
-- (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
|
if checkpoint.type == "nil" or not checkpoint.value[1]:match("^"..escape(parent_line.name)) then
|
||||||
s, e = set_variable(state, parent_line.parent_function.namespace.."🔖", {
|
s, e = set_variable(state, parent_line.parent_resumable.namespace.."🔖", {
|
||||||
type = "function reference",
|
type = "function reference",
|
||||||
value = { parent_line.name }
|
value = { parent_line.name }
|
||||||
})
|
})
|
||||||
|
|
@ -184,11 +184,11 @@ run_block = function(state, block, resume_from_there, i, j)
|
||||||
merge_state(state)
|
merge_state(state)
|
||||||
end
|
end
|
||||||
-- go up hierarchy if asked to resume
|
-- go up hierarchy if asked to resume
|
||||||
-- will stop at function boundary
|
-- will stop at resumable function boundary
|
||||||
-- if parent is a choice, will ignore choices that belong to the same block (like the whole block was executed naturally from a higher parent)
|
-- if parent is a choice, will ignore choices that belong to the same block (like the whole block was executed naturally from a higher parent)
|
||||||
-- if parent if a condition, will mark it as a success (skipping following else-conditions) (for the same reasons as for choices)
|
-- if parent if a condition, will mark it as a success (skipping following else-conditions) (for the same reasons as for choices)
|
||||||
-- if parent pushed a tag, will pop it (tags from parents are added to the stack in run())
|
-- if parent pushed a tag, will pop it (tags from parents are added to the stack in run())
|
||||||
if resume_from_there and block.parent_line and not block.parent_line.resume_boundary then
|
if resume_from_there and block.parent_line and not block.parent_line.resumable then
|
||||||
local parent_line = block.parent_line
|
local parent_line = block.parent_line
|
||||||
if parent_line.type == "choice" then
|
if parent_line.type == "choice" then
|
||||||
state.interpreter.skip_choices_until_flush = true
|
state.interpreter.skip_choices_until_flush = true
|
||||||
|
|
@ -212,9 +212,9 @@ local function run(state, block, resume_from_there, i, j)
|
||||||
local tags_len = tags:len(state)
|
local tags_len = tags:len(state)
|
||||||
if resume_from_there then
|
if resume_from_there then
|
||||||
local tags_to_add = {}
|
local tags_to_add = {}
|
||||||
-- go up in hierarchy in ascending order until function boundary
|
-- go up in hierarchy in ascending order until resumable function boundary
|
||||||
local parent_line = block.parent_line
|
local parent_line = block.parent_line
|
||||||
while parent_line and not parent_line.resume_boundary do
|
while parent_line and not parent_line.resumable do
|
||||||
if parent_line.type == "tag" then
|
if parent_line.type == "tag" then
|
||||||
local v, e = eval(state, parent_line.expression)
|
local v, e = eval(state, parent_line.expression)
|
||||||
if not v then return v, ("%s; at %s"):format(e, parent_line.source) end
|
if not v then return v, ("%s; at %s"):format(e, parent_line.source) end
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ local function parse(state)
|
||||||
if rem:match("[^%s]") then
|
if rem:match("[^%s]") then
|
||||||
return nil, ("unexpected characters after parameter %q: %q; at %s"):format(param.full_name, rem, line.source)
|
return nil, ("unexpected characters after parameter %q: %q; at %s"):format(param.full_name, rem, line.source)
|
||||||
end
|
end
|
||||||
state.variable_constraints[param.full_name] = { pending = type_exp }
|
state.variable_metadata[param.full_name].constraint = { pending = type_exp }
|
||||||
end
|
end
|
||||||
-- get default value
|
-- get default value
|
||||||
if param.default then
|
if param.default then
|
||||||
|
|
@ -30,7 +30,7 @@ local function parse(state)
|
||||||
param.default = default_exp
|
param.default = default_exp
|
||||||
-- extract type constraint from default value
|
-- extract type constraint from default value
|
||||||
if default_exp.type == "function call" and default_exp.called_name == "_::_" then
|
if default_exp.type == "function call" and default_exp.called_name == "_::_" then
|
||||||
state.variable_constraints[param.full_name] = { pending = default_exp.argument.expression.right }
|
state.variable_metadata[param.full_name].constraint = { pending = default_exp.argument.expression.right }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -41,7 +41,7 @@ local function parse(state)
|
||||||
if rem:match("[^%s]") then
|
if rem:match("[^%s]") then
|
||||||
return nil, ("unexpected characters after parameter %q: %q; at %s"):format(line.assignment.full_name, rem, line.source)
|
return nil, ("unexpected characters after parameter %q: %q; at %s"):format(line.assignment.full_name, rem, line.source)
|
||||||
end
|
end
|
||||||
state.variable_constraints[line.assignment.full_name] = { pending = type_exp }
|
state.variable_metadata[line.assignment.full_name].constraint = { pending = type_exp }
|
||||||
end
|
end
|
||||||
-- get list of scoped variables
|
-- get list of scoped variables
|
||||||
-- (note includes every variables in the namespace of subnamespace, so subfunctions are scoped alongside this function)
|
-- (note includes every variables in the namespace of subnamespace, so subfunctions are scoped alongside this function)
|
||||||
|
|
@ -49,6 +49,7 @@ local function parse(state)
|
||||||
line.scoped = {}
|
line.scoped = {}
|
||||||
for name in pairs(state.variables) do
|
for name in pairs(state.variables) do
|
||||||
if name:sub(1, #namespace) == namespace then
|
if name:sub(1, #namespace) == namespace then
|
||||||
|
if state.variable_metadata[name].persistent then return nil, ("variable %q can not be persistent as it is in a scoped function"):format(name) end
|
||||||
table.insert(line.scoped, name)
|
table.insert(line.scoped, name)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -80,7 +81,7 @@ local function parse(state)
|
||||||
if rem2:match("[^%s]") then
|
if rem2:match("[^%s]") then
|
||||||
return nil, ("unexpected characters after variable %q: %q; at %s"):format(line.name, rem2, line.source)
|
return nil, ("unexpected characters after variable %q: %q; at %s"):format(line.name, rem2, line.source)
|
||||||
end
|
end
|
||||||
state.variable_constraints[line.name] = { pending = type_exp }
|
state.variable_metadata[line.name].constraint = { pending = type_exp }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ end
|
||||||
--- parse a single line into AST
|
--- parse a single line into AST
|
||||||
-- * ast: if success
|
-- * ast: if success
|
||||||
-- * nil, error: in case of error
|
-- * nil, error: in case of error
|
||||||
local function parse_line(line, state, namespace, parent_function)
|
local function parse_line(line, state, namespace, parent_resumable, in_scoped)
|
||||||
local l = line.content
|
local l = line.content
|
||||||
local r = {
|
local r = {
|
||||||
source = line.source
|
source = line.source
|
||||||
|
|
@ -93,10 +93,10 @@ local function parse_line(line, state, namespace, parent_function)
|
||||||
local keep_in_ast = false
|
local keep_in_ast = false
|
||||||
if lr:match("^%$") then
|
if lr:match("^%$") then
|
||||||
r.subtype = "function"
|
r.subtype = "function"
|
||||||
r.resume_boundary = true
|
r.resumable = true
|
||||||
elseif lr:match("^%%") then
|
elseif lr:match("^%%") then
|
||||||
r.subtype = "class"
|
r.subtype = "class"
|
||||||
r.resume_boundary = true
|
r.resumable = true
|
||||||
r.properties = true
|
r.properties = true
|
||||||
allow_params = false
|
allow_params = false
|
||||||
allow_assign = false
|
allow_assign = false
|
||||||
|
|
@ -105,7 +105,7 @@ local function parse_line(line, state, namespace, parent_function)
|
||||||
allow_params = false
|
allow_params = false
|
||||||
allow_assign = false
|
allow_assign = false
|
||||||
keep_in_ast = true
|
keep_in_ast = true
|
||||||
r.parent_function = parent_function -- store parent 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")
|
||||||
end
|
end
|
||||||
|
|
@ -231,36 +231,23 @@ local function parse_line(line, state, namespace, parent_function)
|
||||||
end
|
end
|
||||||
-- define variables
|
-- define variables
|
||||||
if not line.children then line.children = {} end
|
if not line.children then line.children = {} end
|
||||||
|
local scoped = in_scoped or r.scoped
|
||||||
-- define 👁️ variable
|
-- define 👁️ variable
|
||||||
local seen_alias = state.global_state.builtin_aliases["👁️"]
|
local seen_alias = state.global_state.builtin_aliases["👁️"]
|
||||||
if seen_alias then
|
table.insert(line.children, 1, { content = (":%s👁️%s=0"):format(scoped and "" or "@", seen_alias and ":"..seen_alias or ""), source = line.source })
|
||||||
table.insert(line.children, 1, { content = (":👁️:%s=0"):format(seen_alias), source = line.source })
|
-- define 🔖 variable
|
||||||
else
|
if r.resumable then
|
||||||
table.insert(line.children, 1, { content = ":👁️=0", source = line.source })
|
|
||||||
end
|
|
||||||
if r.subtype ~= "checkpoint" then
|
|
||||||
-- define 🔖 variable
|
|
||||||
local checkpoint_alias = state.global_state.builtin_aliases["🔖"]
|
local checkpoint_alias = state.global_state.builtin_aliases["🔖"]
|
||||||
if checkpoint_alias then
|
table.insert(line.children, 1, { content = (":%s🔖%s=()"):format(scoped and "" or "@", checkpoint_alias and ":"..checkpoint_alias or ""), source = line.source })
|
||||||
table.insert(line.children, 1, { content = (":🔖:%s=()"):format(checkpoint_alias), source = line.source })
|
|
||||||
else
|
|
||||||
table.insert(line.children, 1, { content = ":🔖=()", source = line.source })
|
|
||||||
end
|
|
||||||
-- custom code injection
|
|
||||||
inject(state, r, "start", line.children, 2)
|
|
||||||
inject(state, r, "end", line.children)
|
|
||||||
elseif r.subtype == "checkpoint" then
|
|
||||||
-- define 🏁 variable
|
|
||||||
local reached_alias = state.global_state.builtin_aliases["🏁"]
|
|
||||||
if reached_alias then
|
|
||||||
table.insert(line.children, 1, { content = (":🏁:%s=0"):format(reached_alias), source = line.source })
|
|
||||||
else
|
|
||||||
table.insert(line.children, 1, { content = ":🏁=0", source = line.source })
|
|
||||||
end
|
|
||||||
-- custom code injection
|
|
||||||
inject(state, r, "start", line.children, 2)
|
|
||||||
inject(state, r, "end", line.children)
|
|
||||||
end
|
end
|
||||||
|
-- define 🏁 variable
|
||||||
|
if r.subtype == "checkpoint" then
|
||||||
|
local reached_alias = state.global_state.builtin_aliases["🏁"]
|
||||||
|
table.insert(line.children, 1, { content = (":%s🏁%s=0"):format(scoped and "" or "@", reached_alias and ":"..reached_alias or ""), source = line.source })
|
||||||
|
end
|
||||||
|
-- custom code injection
|
||||||
|
inject(state, r, "start", line.children, 2)
|
||||||
|
inject(state, r, "end", line.children)
|
||||||
-- 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
|
||||||
|
|
@ -268,6 +255,7 @@ local function parse_line(line, state, namespace, parent_function)
|
||||||
type = "undefined argument",
|
type = "undefined argument",
|
||||||
value = nil
|
value = nil
|
||||||
}
|
}
|
||||||
|
state.variable_metadata[param.full_name] = {}
|
||||||
else
|
else
|
||||||
return nil, ("trying to define parameter %q, but a variable with the same name exists; at %s"):format(param.full_name, line.source)
|
return nil, ("trying to define parameter %q, but a variable with the same name exists; at %s"):format(param.full_name, line.source)
|
||||||
end
|
end
|
||||||
|
|
@ -278,6 +266,7 @@ local function parse_line(line, state, namespace, parent_function)
|
||||||
type = "undefined argument",
|
type = "undefined argument",
|
||||||
value = nil
|
value = nil
|
||||||
}
|
}
|
||||||
|
state.variable_metadata[r.assignment.full_name] = {}
|
||||||
else
|
else
|
||||||
return nil, ("trying to define parameter %q, but a variable with the same name exists; at %s"):format(r.assignment.full_name, line.source)
|
return nil, ("trying to define parameter %q, but a variable with the same name exists; at %s"):format(r.assignment.full_name, line.source)
|
||||||
end
|
end
|
||||||
|
|
@ -298,6 +287,9 @@ local function parse_line(line, state, namespace, parent_function)
|
||||||
if rem:match("^:") then
|
if rem:match("^:") then
|
||||||
rem = rem:match("^:(.*)$")
|
rem = rem:match("^:(.*)$")
|
||||||
r.constant = true
|
r.constant = true
|
||||||
|
elseif rem:match("^@") then
|
||||||
|
rem = rem:match("^@(.*)$")
|
||||||
|
r.persistent = true
|
||||||
end
|
end
|
||||||
-- get identifier
|
-- get identifier
|
||||||
local identifier
|
local identifier
|
||||||
|
|
@ -328,7 +320,9 @@ local function parse_line(line, state, namespace, parent_function)
|
||||||
r.name = fqm
|
r.name = fqm
|
||||||
r.expression = exp
|
r.expression = exp
|
||||||
state.variables[fqm] = { type = "pending definition", value = { expression = nil, source = r.source } }
|
state.variables[fqm] = { type = "pending definition", value = { expression = nil, source = r.source } }
|
||||||
if r.constant then state.variable_constants[fqm] = true end
|
state.variable_metadata[fqm] = {}
|
||||||
|
if r.constant then state.variable_metadata[fqm].constant = true end
|
||||||
|
if r.persistent then state.variable_metadata[fqm].persistent = true end
|
||||||
end
|
end
|
||||||
-- add expression line after to perform the immediate execution
|
-- add expression line after to perform the immediate execution
|
||||||
if run_immediately then
|
if run_immediately then
|
||||||
|
|
@ -344,7 +338,6 @@ local function parse_line(line, state, namespace, parent_function)
|
||||||
elseif l:match("^%@") then
|
elseif l:match("^%@") then
|
||||||
r.type = "return"
|
r.type = "return"
|
||||||
r.child = true
|
r.child = true
|
||||||
r.parent_function = parent_function
|
|
||||||
local expr = l:match("^%@(.*)$")
|
local expr = l:match("^%@(.*)$")
|
||||||
if expr:match("[^%s]") then
|
if expr:match("[^%s]") then
|
||||||
r.expression = expr
|
r.expression = expr
|
||||||
|
|
@ -353,7 +346,7 @@ local function parse_line(line, state, namespace, parent_function)
|
||||||
end
|
end
|
||||||
-- 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_function, "return", line.children)
|
inject(state, parent_resumable, "return", line.children)
|
||||||
-- text
|
-- text
|
||||||
elseif l:match("[^%s]") then
|
elseif l:match("[^%s]") then
|
||||||
r.type = "text"
|
r.type = "text"
|
||||||
|
|
@ -369,11 +362,11 @@ end
|
||||||
--- parse an indented into final AST
|
--- parse an indented into final AST
|
||||||
-- * block: in case of success
|
-- * block: in case of success
|
||||||
-- * nil, err: in case of error
|
-- * nil, err: in case of error
|
||||||
local function parse_block(indented, state, namespace, parent_function)
|
local function parse_block(indented, state, namespace, parent_resumable, in_scoped)
|
||||||
local block = { type = "block" }
|
local block = { type = "block" }
|
||||||
for i, l in ipairs(indented) do
|
for i, l in ipairs(indented) do
|
||||||
-- parsable line
|
-- parsable line
|
||||||
local ast, err = parse_line(l, state, namespace, parent_function)
|
local ast, err = parse_line(l, state, namespace, parent_resumable, in_scoped)
|
||||||
if err then return nil, err end
|
if err then return nil, err end
|
||||||
-- add to block AST
|
-- add to block AST
|
||||||
if not ast.remove_from_block_ast then
|
if not ast.remove_from_block_ast then
|
||||||
|
|
@ -392,7 +385,7 @@ local function parse_block(indented, state, namespace, parent_function)
|
||||||
if not ast.child then
|
if not ast.child then
|
||||||
return nil, ("line %s (%s) can't have children"):format(ast.source, ast.type)
|
return nil, ("line %s (%s) can't have children"):format(ast.source, ast.type)
|
||||||
else
|
else
|
||||||
local r, e = parse_block(l.children, state, ast.namespace or namespace, (ast.type == "function" and ast.subtype ~= "checkpoint") and ast or parent_function)
|
local r, e = parse_block(l.children, state, ast.namespace or namespace, (ast.type == "function" and ast.resumable) and ast or parent_resumable, (ast.type == "function" and ast.scoped) or in_scoped)
|
||||||
if not r then return r, e end
|
if not r then return r, e end
|
||||||
r.parent_line = ast
|
r.parent_line = ast
|
||||||
ast.child = r
|
ast.child = r
|
||||||
|
|
@ -507,8 +500,7 @@ local function parse(state, s, name, source)
|
||||||
local state_proxy = {
|
local state_proxy = {
|
||||||
inject = {},
|
inject = {},
|
||||||
aliases = setmetatable({}, { __index = state.aliases }),
|
aliases = setmetatable({}, { __index = state.aliases }),
|
||||||
variable_constraints = setmetatable({}, { __index = state.variable_constraints }),
|
variable_metadata = setmetatable({}, { __index = state.variable_metadata }),
|
||||||
variable_constants = setmetatable({}, { __index = state.variable_constants }),
|
|
||||||
variables = setmetatable({}, { __index = state.aliases }),
|
variables = setmetatable({}, { __index = state.aliases }),
|
||||||
functions = setmetatable({}, {
|
functions = setmetatable({}, {
|
||||||
__index = function(self, key)
|
__index = function(self, key)
|
||||||
|
|
@ -541,11 +533,8 @@ local function parse(state, s, name, source)
|
||||||
for k,v in pairs(state_proxy.aliases) do
|
for k,v in pairs(state_proxy.aliases) do
|
||||||
state.aliases[k] = v
|
state.aliases[k] = v
|
||||||
end
|
end
|
||||||
for k,v in pairs(state_proxy.variable_constraints) do
|
for k,v in pairs(state_proxy.variable_metadata) do
|
||||||
state.variable_constraints[k] = v
|
state.variable_metadata[k] = v
|
||||||
end
|
|
||||||
for k,v in pairs(state_proxy.variable_constants) do
|
|
||||||
state.variable_constants[k] = v
|
|
||||||
end
|
end
|
||||||
for k,v in pairs(state_proxy.variables) do
|
for k,v in pairs(state_proxy.variables) do
|
||||||
state.variables[k] = v
|
state.variables[k] = v
|
||||||
|
|
|
||||||
|
|
@ -158,12 +158,10 @@ lua_functions = {
|
||||||
if r.constant then
|
if r.constant then
|
||||||
return nil, "can't change the value of an attribute of a constant object"
|
return nil, "can't change the value of an attribute of a constant object"
|
||||||
end
|
end
|
||||||
if not check_mutable(state, obj.class.."."..name) then
|
|
||||||
return nil, "can't change the value of a constant attribute"
|
|
||||||
end
|
|
||||||
-- attribute already present in object
|
-- attribute already present in object
|
||||||
local var, vfqm = find(state.aliases, obj.attributes, "", obj.class.."."..name)
|
local var, vfqm = find(state.aliases, obj.attributes, "", obj.class.."."..name)
|
||||||
if var then
|
if var then
|
||||||
|
if not check_mutable(state, vfqm) then return nil, "can't change the value of a constant attribute" end
|
||||||
obj.attributes[vfqm] = v
|
obj.attributes[vfqm] = v
|
||||||
mark_as_modified(anselme.running.state, obj.attributes)
|
mark_as_modified(anselme.running.state, obj.attributes)
|
||||||
return v
|
return v
|
||||||
|
|
@ -171,6 +169,7 @@ lua_functions = {
|
||||||
-- search for attribute in base class
|
-- search for attribute in base class
|
||||||
local cvar, cvfqm = find(state.aliases, state.interpreter.global_state.variables, "", obj.class.."."..name)
|
local cvar, cvfqm = find(state.aliases, state.interpreter.global_state.variables, "", obj.class.."."..name)
|
||||||
if cvar then
|
if cvar then
|
||||||
|
if not check_mutable(state, cvfqm) then return nil, "can't change the value of a constant attribute" end
|
||||||
obj.attributes[cvfqm] = v
|
obj.attributes[cvfqm] = v
|
||||||
mark_as_modified(anselme.running.state, obj.attributes)
|
mark_as_modified(anselme.running.state, obj.attributes)
|
||||||
return v
|
return v
|
||||||
|
|
|
||||||
44
test/tests/function scoped nested.ans
Normal file
44
test/tests/function scoped nested.ans
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
:$ f()
|
||||||
|
:a = 1
|
||||||
|
|
||||||
|
{a}
|
||||||
|
|
||||||
|
~ a := a + 1
|
||||||
|
|
||||||
|
:$ g
|
||||||
|
:a = 1
|
||||||
|
|
||||||
|
{a}
|
||||||
|
|
||||||
|
~ a := a + 1
|
||||||
|
|
||||||
|
:$ h()
|
||||||
|
:a = 1
|
||||||
|
|
||||||
|
{a}
|
||||||
|
|
||||||
|
~ a := a + 1
|
||||||
|
|
||||||
|
\> depth 2, unscoped:
|
||||||
|
|
||||||
|
~ g
|
||||||
|
|
||||||
|
~ g
|
||||||
|
|
||||||
|
~ g
|
||||||
|
|
||||||
|
\> depth 2, scoped:
|
||||||
|
|
||||||
|
~ h
|
||||||
|
|
||||||
|
~ h
|
||||||
|
|
||||||
|
~ h
|
||||||
|
|
||||||
|
depth 1:
|
||||||
|
|
||||||
|
~ f
|
||||||
|
|
||||||
|
~ f
|
||||||
|
|
||||||
|
~ f
|
||||||
230
test/tests/function scoped nested.lua
Normal file
230
test/tests/function scoped nested.lua
Normal file
|
|
@ -0,0 +1,230 @@
|
||||||
|
local _={}
|
||||||
|
_[113]={}
|
||||||
|
_[112]={}
|
||||||
|
_[111]={}
|
||||||
|
_[110]={}
|
||||||
|
_[109]={}
|
||||||
|
_[108]={}
|
||||||
|
_[107]={}
|
||||||
|
_[106]={}
|
||||||
|
_[105]={}
|
||||||
|
_[104]={}
|
||||||
|
_[103]={}
|
||||||
|
_[102]={}
|
||||||
|
_[101]={}
|
||||||
|
_[100]={}
|
||||||
|
_[99]={}
|
||||||
|
_[98]={}
|
||||||
|
_[97]={}
|
||||||
|
_[96]={}
|
||||||
|
_[95]={}
|
||||||
|
_[94]={}
|
||||||
|
_[93]={}
|
||||||
|
_[92]={}
|
||||||
|
_[91]={}
|
||||||
|
_[90]={}
|
||||||
|
_[89]={}
|
||||||
|
_[88]={}
|
||||||
|
_[87]={}
|
||||||
|
_[86]={}
|
||||||
|
_[85]={tags=_[113],text="1"}
|
||||||
|
_[84]={tags=_[112],text="1"}
|
||||||
|
_[83]={tags=_[111],text="1"}
|
||||||
|
_[82]={tags=_[110],text="> depth 2, scoped:"}
|
||||||
|
_[81]={tags=_[109],text="3"}
|
||||||
|
_[80]={tags=_[108],text="2"}
|
||||||
|
_[79]={tags=_[107],text="1"}
|
||||||
|
_[78]={tags=_[106],text="> depth 2, unscoped:"}
|
||||||
|
_[77]={tags=_[105],text="1"}
|
||||||
|
_[76]={tags=_[104],text="1"}
|
||||||
|
_[75]={tags=_[103],text="1"}
|
||||||
|
_[74]={tags=_[102],text="1"}
|
||||||
|
_[73]={tags=_[101],text="> depth 2, scoped:"}
|
||||||
|
_[72]={tags=_[100],text="3"}
|
||||||
|
_[71]={tags=_[99],text="2"}
|
||||||
|
_[70]={tags=_[98],text="1"}
|
||||||
|
_[69]={tags=_[97],text="> depth 2, unscoped:"}
|
||||||
|
_[68]={tags=_[96],text="1"}
|
||||||
|
_[67]={tags=_[95],text="1"}
|
||||||
|
_[66]={tags=_[94],text="1"}
|
||||||
|
_[65]={tags=_[93],text="1"}
|
||||||
|
_[64]={tags=_[92],text="> depth 2, scoped:"}
|
||||||
|
_[63]={tags=_[91],text="3"}
|
||||||
|
_[62]={tags=_[90],text="2"}
|
||||||
|
_[61]={tags=_[89],text="1"}
|
||||||
|
_[60]={tags=_[88],text="> depth 2, unscoped:"}
|
||||||
|
_[59]={tags=_[87],text="1"}
|
||||||
|
_[58]={tags=_[86],text="depth 1:"}
|
||||||
|
_[57]={_[85]}
|
||||||
|
_[56]={_[84]}
|
||||||
|
_[55]={_[83]}
|
||||||
|
_[54]={_[82]}
|
||||||
|
_[53]={_[81]}
|
||||||
|
_[52]={_[80]}
|
||||||
|
_[51]={_[79]}
|
||||||
|
_[50]={_[78]}
|
||||||
|
_[49]={_[77]}
|
||||||
|
_[48]={_[76]}
|
||||||
|
_[47]={_[75]}
|
||||||
|
_[46]={_[74]}
|
||||||
|
_[45]={_[73]}
|
||||||
|
_[44]={_[72]}
|
||||||
|
_[43]={_[71]}
|
||||||
|
_[42]={_[70]}
|
||||||
|
_[41]={_[69]}
|
||||||
|
_[40]={_[68]}
|
||||||
|
_[39]={_[67]}
|
||||||
|
_[38]={_[66]}
|
||||||
|
_[37]={_[65]}
|
||||||
|
_[36]={_[64]}
|
||||||
|
_[35]={_[63]}
|
||||||
|
_[34]={_[62]}
|
||||||
|
_[33]={_[61]}
|
||||||
|
_[32]={_[60]}
|
||||||
|
_[31]={_[59]}
|
||||||
|
_[30]={_[58]}
|
||||||
|
_[29]={"return"}
|
||||||
|
_[28]={"text",_[57]}
|
||||||
|
_[27]={"text",_[56]}
|
||||||
|
_[26]={"text",_[55]}
|
||||||
|
_[25]={"text",_[54]}
|
||||||
|
_[24]={"text",_[53]}
|
||||||
|
_[23]={"text",_[52]}
|
||||||
|
_[22]={"text",_[51]}
|
||||||
|
_[21]={"text",_[50]}
|
||||||
|
_[20]={"text",_[49]}
|
||||||
|
_[19]={"text",_[48]}
|
||||||
|
_[18]={"text",_[47]}
|
||||||
|
_[17]={"text",_[46]}
|
||||||
|
_[16]={"text",_[45]}
|
||||||
|
_[15]={"text",_[44]}
|
||||||
|
_[14]={"text",_[43]}
|
||||||
|
_[13]={"text",_[42]}
|
||||||
|
_[12]={"text",_[41]}
|
||||||
|
_[11]={"text",_[40]}
|
||||||
|
_[10]={"text",_[39]}
|
||||||
|
_[9]={"text",_[38]}
|
||||||
|
_[8]={"text",_[37]}
|
||||||
|
_[7]={"text",_[36]}
|
||||||
|
_[6]={"text",_[35]}
|
||||||
|
_[5]={"text",_[34]}
|
||||||
|
_[4]={"text",_[33]}
|
||||||
|
_[3]={"text",_[32]}
|
||||||
|
_[2]={"text",_[31]}
|
||||||
|
_[1]={"text",_[30]}
|
||||||
|
return {_[1],_[2],_[3],_[4],_[5],_[6],_[7],_[8],_[9],_[10],_[11],_[12],_[13],_[14],_[15],_[16],_[17],_[18],_[19],_[20],_[21],_[22],_[23],_[24],_[25],_[26],_[27],_[28],_[29]}
|
||||||
|
--[[
|
||||||
|
{ "text", { {
|
||||||
|
tags = {},
|
||||||
|
text = "depth 1:"
|
||||||
|
} } }
|
||||||
|
{ "text", { {
|
||||||
|
tags = {},
|
||||||
|
text = "1"
|
||||||
|
} } }
|
||||||
|
{ "text", { {
|
||||||
|
tags = {},
|
||||||
|
text = "> depth 2, unscoped:"
|
||||||
|
} } }
|
||||||
|
{ "text", { {
|
||||||
|
tags = {},
|
||||||
|
text = "1"
|
||||||
|
} } }
|
||||||
|
{ "text", { {
|
||||||
|
tags = {},
|
||||||
|
text = "2"
|
||||||
|
} } }
|
||||||
|
{ "text", { {
|
||||||
|
tags = {},
|
||||||
|
text = "3"
|
||||||
|
} } }
|
||||||
|
{ "text", { {
|
||||||
|
tags = {},
|
||||||
|
text = "> depth 2, scoped:"
|
||||||
|
} } }
|
||||||
|
{ "text", { {
|
||||||
|
tags = {},
|
||||||
|
text = "1"
|
||||||
|
} } }
|
||||||
|
{ "text", { {
|
||||||
|
tags = {},
|
||||||
|
text = "1"
|
||||||
|
} } }
|
||||||
|
{ "text", { {
|
||||||
|
tags = {},
|
||||||
|
text = "1"
|
||||||
|
} } }
|
||||||
|
{ "text", { {
|
||||||
|
tags = {},
|
||||||
|
text = "1"
|
||||||
|
} } }
|
||||||
|
{ "text", { {
|
||||||
|
tags = {},
|
||||||
|
text = "> depth 2, unscoped:"
|
||||||
|
} } }
|
||||||
|
{ "text", { {
|
||||||
|
tags = {},
|
||||||
|
text = "1"
|
||||||
|
} } }
|
||||||
|
{ "text", { {
|
||||||
|
tags = {},
|
||||||
|
text = "2"
|
||||||
|
} } }
|
||||||
|
{ "text", { {
|
||||||
|
tags = {},
|
||||||
|
text = "3"
|
||||||
|
} } }
|
||||||
|
{ "text", { {
|
||||||
|
tags = {},
|
||||||
|
text = "> depth 2, scoped:"
|
||||||
|
} } }
|
||||||
|
{ "text", { {
|
||||||
|
tags = {},
|
||||||
|
text = "1"
|
||||||
|
} } }
|
||||||
|
{ "text", { {
|
||||||
|
tags = {},
|
||||||
|
text = "1"
|
||||||
|
} } }
|
||||||
|
{ "text", { {
|
||||||
|
tags = {},
|
||||||
|
text = "1"
|
||||||
|
} } }
|
||||||
|
{ "text", { {
|
||||||
|
tags = {},
|
||||||
|
text = "1"
|
||||||
|
} } }
|
||||||
|
{ "text", { {
|
||||||
|
tags = {},
|
||||||
|
text = "> depth 2, unscoped:"
|
||||||
|
} } }
|
||||||
|
{ "text", { {
|
||||||
|
tags = {},
|
||||||
|
text = "1"
|
||||||
|
} } }
|
||||||
|
{ "text", { {
|
||||||
|
tags = {},
|
||||||
|
text = "2"
|
||||||
|
} } }
|
||||||
|
{ "text", { {
|
||||||
|
tags = {},
|
||||||
|
text = "3"
|
||||||
|
} } }
|
||||||
|
{ "text", { {
|
||||||
|
tags = {},
|
||||||
|
text = "> depth 2, scoped:"
|
||||||
|
} } }
|
||||||
|
{ "text", { {
|
||||||
|
tags = {},
|
||||||
|
text = "1"
|
||||||
|
} } }
|
||||||
|
{ "text", { {
|
||||||
|
tags = {},
|
||||||
|
text = "1"
|
||||||
|
} } }
|
||||||
|
{ "text", { {
|
||||||
|
tags = {},
|
||||||
|
text = "1"
|
||||||
|
} } }
|
||||||
|
{ "return" }
|
||||||
|
]]--
|
||||||
Loading…
Add table
Add a link
Reference in a new issue