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

Decorator system simplification, removed paragraph decorators, added function decorators

This commit is contained in:
Étienne Fildadut 2021-04-23 17:13:39 +02:00
parent 6f564ea0e2
commit 0171d92352
16 changed files with 265 additions and 290 deletions

View file

@ -3,7 +3,7 @@ Anselme
The overengineered dialog scripting system in pure Lua. The overengineered dialog scripting system in pure Lua.
**Has been rewritten recently, doc is still WIP** **Has been rewritten recently, doc and language are still WIP**
Purpose Purpose
------- -------
@ -90,7 +90,7 @@ Another line.
When executing a piece of Anselme code, it will not directly modify the global state (i.e. the values of variables used by every script), but only locally, in this execution. When executing a piece of Anselme code, it will not directly modify the global state (i.e. the values of variables used by every script), but only locally, in this execution.
Right after reaching a checkpoint (line or decorator), Anselme will merge the local state with the global one, i.e., make every change accessible to other scripts. Right after reaching a checkpoint line, Anselme will merge the local state with the global one, i.e., make every change accessible to other scripts.
``` ```
$ main $ main
@ -296,19 +296,69 @@ And this is more text, in a different event.
### Line decorators ### Line decorators
Every line can also be followed with decorators, which are appended at the end of the line and affect its behaviour. Every line can also be followed with decorators, which are appended at the end of the line and affect its behaviour. Decorators are just syntaxic sugar to make some common operations simpler to write.
* `~`: condition decorator. Same as an condition line, behaving as if this line was it sole child. Typically used to conditionally execute line. Does not affect following else-conditions. * `~`: condition decorator. Same as an condition line, behaving as if this line was it sole child. Typically used to conditionally execute line.
* `§`: checkpoint decorator. Same as a checkpoint line, behaving as if this line was it sole child.
* `#`: tag decorator. Same as a tag line, behaving as if this line was it sole child.
``` ```
$ fn $ fn
run this line only once ~ 👁️ run this line only once ~ 👁️
``` ```
is equivalent to:
```
$ fn
~ 👁️
run this line only once
```
* `#`: tag decorator. Same as a tag line, behaving as if this line was it sole child.
```
tagged # 42
```
is equivalent to:
```
# 42
tagged
```
* `$`: function decorator. Same as a function line, behaving as if this line was it sole child, but also run the function.
```
text $ f
```
is equivalent to:
```
~ f
$ f
text
```
This is typically used for immediatletly running functions when defining them, for example for a looping choice :
```
~$ loop
> Loop
@loop
> Exit
```
is equivalent to (since empty condition is assumed true):
```
$ loop
> Loop
@loop
> Exit
~ loop
```
### 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. 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.
@ -368,7 +418,7 @@ Valid identifiers must be at least 1 caracters long and can contain anything exc
When defining an identifier (using a function, checkpoint or variable delcaration line), it will be defined into the current namespace (defined by the parent function/checkpoint). When evaluating an expression, Anselme will look for variables into the current line's namespace, then go up a level if it isn't found, and so on. When defining an identifier (using a function, checkpoint or variable delcaration line), it will be defined into the current namespace (defined by the parent function/checkpoint). When evaluating an expression, Anselme will look for variables into the current line's namespace, then go up a level if it isn't found, and so on.
In practise, this means you have to use the "genealogy" of the variable to refer to it from a line not in it indentation block: In practise, this means you have to use the "genealogy" of the variable to refer to it from a line not in the same namespace:
``` ```
$ fn1 $ fn1

View file

@ -99,18 +99,9 @@ local function eval(state, exp)
end end
-- anselme function -- anselme function
if type(fn.value) == "table" then if type(fn.value) == "table" then
-- checkpoint & checkpoint decorator -- checkpoint
if fn.value.type == "checkpoint" or fn.value.checkpoint then if fn.value.type == "checkpoint" then
local r, e local r, e = run(state, fn.value.child, not exp.explicit_call)
if fn.value.type == "checkpoint" then
r, e = run(state, fn.value.child, not exp.explicit_call)
-- checkpoint decorators: run single line or resume from it.
-- checkpoint & seen variables will be updated from the interpreter usual checkpoint-reaching code.
elseif exp.explicit_call then
r, e = run(state, fn.value.parent_block, false, fn.value.parent_position, fn.value.parent_position)
else
r, e = run(state, fn.value.parent_block, true, fn.value.parent_position)
end
if not r then return r, e end if not r then return r, e end
return r return r
-- function -- function

View file

@ -57,29 +57,25 @@ local run_block
local function run_line(state, line) local function run_line(state, line)
-- store line -- store line
state.interpreter.running_line = line state.interpreter.running_line = line
-- condition decorator -- if line intend to push an event, flush buffer it it's a different event
local skipped = false if line.push_event and state.interpreter.event_buffer and state.interpreter.event_type ~= line.push_event then
if line.condition then local v, e = run_line(state, { source = line.source, type = "flush_events" })
local v, e = eval(state, line.condition) if e then return v, e end
if not v then return v, ("%s; at %s"):format(e, line.source) end if v then return v end
skipped = not truthy(v)
end end
if not skipped then -- line types
-- tag decorator if line.type == "condition" then
if line.tag then line.parent_block.last_condition_success = nil
local v, e = eval(state, line.tag) local v, e = eval(state, line.expression)
if not v then return v, ("%s; in tag decorator at %s"):format(e, line.source) end if not v then return v, ("%s; at %s"):format(e, line.source) end
tags:push(state, v) if truthy(v) then
end line.parent_block.last_condition_success = true
-- if line intend to push an event, flush buffer it it's a different event v, e = run_block(state, line.child)
if line.push_event and state.interpreter.event_buffer and state.interpreter.event_type ~= line.push_event then
local v, e = run_line(state, { source = line.source, type = "flush_events" })
if e then return v, e end if e then return v, e end
if v then return v end if v then return v end
end end
-- line types elseif line.type == "else-condition" then
if line.type == "condition" then if not line.parent_block.last_condition_success then
line.parent_block.last_condition_success = nil
local v, e = eval(state, line.expression) local v, e = eval(state, line.expression)
if not v then return v, ("%s; at %s"):format(e, line.source) end if not v then return v, ("%s; at %s"):format(e, line.source) end
if truthy(v) then if truthy(v) then
@ -88,85 +84,68 @@ local function run_line(state, line)
if e then return v, e end if e then return v, e end
if v then return v end if v then return v end
end end
elseif line.type == "else-condition" then end
if not line.parent_block.last_condition_success then elseif line.type == "choice" then
local v, e = eval(state, line.expression) local t, er = eval_text(state, line.text)
if not v then return v, ("%s; at %s"):format(e, line.source) end if not t then return t, er end
if truthy(v) then table.insert(state.interpreter.choice_available, {
line.parent_block.last_condition_success = true tags = tags:current(state),
v, e = run_block(state, line.child) block = line.child
})
write_event(state, "choice", t)
elseif line.type == "tag" then
local v, e = eval(state, line.expression)
if not v then return v, ("%s; at %s"):format(e, line.source) end
tags:push(state, v)
v, e = run_block(state, line.child)
tags:pop(state)
if e then return v, e end
if v then return v end
elseif line.type == "return" then
local v, e = eval(state, line.expression)
if not v then return v, ("%s; at %s"):format(e, line.source) end
return v
elseif line.type == "text" then
local t, er = eval_text(state, line.text)
if not t then return t, ("%s; at %s"):format(er, line.source) end
write_event(state, "text", t)
elseif line.type == "flush_events" then
while state.interpreter.event_buffer do
local type, buffer = state.interpreter.event_type, state.interpreter.event_buffer
state.interpreter.event_type = nil
state.interpreter.event_buffer = nil
-- yield
coroutine.yield(type, buffer)
-- run choice
if type == "choice" then
local sel = state.interpreter.choice_selected
state.interpreter.choice_selected = nil
if not sel or sel < 1 or sel > #state.interpreter.choice_available then
return nil, "invalid choice"
else
local choice = state.interpreter.choice_available[sel]
state.interpreter.choice_available = {}
tags:push_lua_no_merge(state, choice.tags)
local v, e = run_block(state, choice.block)
tags:pop(state)
if e then return v, e end if e then return v, e end
if v then return v end -- discard return value from choice block as the execution is delayed until an event flush
-- and we don't want to stop the execution of another function unexpectedly
end end
end end
elseif line.type == "choice" then
local t, er = eval_text(state, line.text)
if not t then return t, er end
table.insert(state.interpreter.choice_available, {
tags = tags:current(state),
block = line.child
})
write_event(state, "choice", t)
elseif line.type == "tag" then
local v, e = eval(state, line.expression)
if not v then return v, ("%s; at %s"):format(e, line.source) end
tags:push(state, v)
v, e = run_block(state, line.child)
tags:pop(state)
if e then return v, e end
if v then return v end
elseif line.type == "return" then
local v, e = eval(state, line.expression)
if not v then return v, ("%s; at %s"):format(e, line.source) end
return v
elseif line.type == "text" then
local t, er = eval_text(state, line.text)
if not t then return t, ("%s; at %s"):format(er, line.source) end
write_event(state, "text", t)
elseif line.type == "flush_events" then
while state.interpreter.event_buffer do
local type, buffer = state.interpreter.event_type, state.interpreter.event_buffer
state.interpreter.event_type = nil
state.interpreter.event_buffer = nil
-- yield
coroutine.yield(type, buffer)
-- run choice
if type == "choice" then
local sel = state.interpreter.choice_selected
state.interpreter.choice_selected = nil
if not sel or sel < 1 or sel > #state.interpreter.choice_available then
return nil, "invalid choice"
else
local choice = state.interpreter.choice_available[sel]
state.interpreter.choice_available = {}
tags:push_lua_no_merge(state, choice.tags)
local v, e = run_block(state, choice.block)
tags:pop(state)
if e then return v, e end
-- discard return value from choice block as the execution is delayed until an event flush
-- and we don't want to stop the execution of another function unexpectedly
end
end
end
elseif line.type ~= "checkpoint" then
return nil, ("unknown line type %q; at %s"):format(line.type, line.source)
end
-- tag decorator
if line.tag then
tags:pop(state)
end
-- checkpoint decorator and line
if line.checkpoint then
state.variables[line.namespace.."👁️"] = {
type = "number",
value = state.variables[line.namespace.."👁️"].value + 1
}
state.variables[line.parent_function.namespace.."🏁"] = {
type = "string",
value = line.name
}
merge_state(state)
end end
elseif line.type == "checkpoint" then
state.variables[line.namespace.."👁️"] = {
type = "number",
value = state.variables[line.namespace.."👁️"].value + 1
}
state.variables[line.parent_function.namespace.."🏁"] = {
type = "string",
value = line.name
}
merge_state(state)
else
return nil, ("unknown line type %q; at %s"):format(line.type, line.source)
end end
end end
@ -196,9 +175,9 @@ run_block = function(state, block, resume_from_there, i, j)
i = i + 1 i = i + 1
end end
-- if we are exiting a checkpoint block, mark it as ran and update checkpoint -- if we are exiting a checkpoint block, mark it as ran and update checkpoint
-- (when resuming from a checkpoint, execution is resumed from inside the checkpoint, the line.checkpoint check in run_line is never called) -- (when resuming from a checkpoint, execution is resumed from inside the checkpoint, the line.type=="checkpoint" check in run_line is never called)
-- (and we want this to be done after executing the checkpoint block anyway) -- (and we want this to be done after executing the checkpoint block anyway)
if block.parent_line and block.parent_line.checkpoint then if block.parent_line and block.parent_line.type == "checkpoint" then
local parent_line = block.parent_line local parent_line = block.parent_line
state.variables[parent_line.namespace.."👁️"] = { state.variables[parent_line.namespace.."👁️"] = {
type = "number", type = "number",
@ -227,7 +206,7 @@ run_block = function(state, block, resume_from_there, i, j)
elseif parent_line.type == "condition" or parent_line.type == "else-condition" then elseif parent_line.type == "condition" or parent_line.type == "else-condition" then
parent_line.parent_block.last_condition_success = true parent_line.parent_block.last_condition_success = true
end end
if parent_line.type == "tag" or parent_line.tag then if parent_line.type == "tag" then
tags:pop(state) tags:pop(state)
end end
local v, e = run_block(state, parent_line.parent_block, resume_from_there, parent_line.parent_position+1) local v, e = run_block(state, parent_line.parent_block, resume_from_there, parent_line.parent_position+1)
@ -252,11 +231,6 @@ local function run(state, block, resume_from_there, i, j)
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
table.insert(tags_to_add, v) table.insert(tags_to_add, v)
end end
if parent_line.tag then
local v, e = eval(state, parent_line.tag)
if not v then return v, ("%s; in tag decorator at %s"):format(e, parent_line.source) end
table.insert(tags_to_add, v)
end
parent_line = parent_line.parent_block.parent_line parent_line = parent_line.parent_block.parent_line
end end
-- re-add tag in desceding order -- re-add tag in desceding order

View file

@ -6,27 +6,6 @@ local parse_text
local function parse(state) local function parse(state)
for _, l in ipairs(state.queued_lines) do for _, l in ipairs(state.queued_lines) do
local line, namespace = l.line, l.namespace local line, namespace = l.line, l.namespace
-- decorators
if line.condition then
if line.condition:match("[^%s]") then
local exp, rem = expression(line.condition, state, namespace)
if not exp then return nil, ("%s; at %s"):format(rem, line.source) end
if rem:match("[^%s]") then return nil, ("expected end of expression before %q in condition decorator; at %s"):format(rem, line.source) end
line.condition = exp
else
line.condition = nil
end
end
if line.tag then
if line.tag:match("[^%s]") then
local exp, rem = expression(line.tag, state, namespace)
if not exp then return nil, ("%s; at %s"):format(rem, line.source) end
if rem:match("[^%s]") then return nil, ("expected end of expression before %q in condition decorator; at %s"):format(rem, line.source) end
line.tag = exp
else
line.tag = nil
end
end
-- expressions -- expressions
if line.expression then if line.expression then
local exp, rem = expression(line.expression, state, namespace) local exp, rem = expression(line.expression, state, namespace)

View file

@ -15,79 +15,6 @@ local function parse_line(line, state, namespace)
r.remove_from_block_ast = true r.remove_from_block_ast = true
return r return r
end end
-- decorators
while l:match("^..+[~#]") or l:match("^..+§") do
-- condition
if l:match("^..+%~.-$") then
local expr
l, expr = l:match("^(.-)%s*%~(.-)$")
r.condition = expr
-- checkpoint
elseif l:match("^..+§.-$") then
-- get identifier
local name
l, name = l:match("^(.-)%s*§(.-)$")
local identifier, rem = name:match("^("..identifier_pattern..")(.-)$")
if not identifier then return nil, ("no valid identifier in checkpoint decorator %q; at %s"):format(identifier, line.source) end
-- format identifier
local fqm = ("%s%s"):format(namespace, format_identifier(identifier))
-- get alias
if rem:match("^%:") then
local content = rem:sub(2)
local alias, rem2 = content:match("^("..identifier_pattern..")(.-)$")
if not alias then return nil, ("expected an identifier in alias in checkpoint decorator, but got %q; at %s"):format(content, line.source) end
if rem2:match("[^%s]") then return nil, ("expected end-of-line after identifier in alias in checkpoint decorator, but got %q; at %s"):format(rem2, line.source) end
-- format alias
local aliasfqm = ("%s%s"):format(namespace, format_identifier(alias))
-- define alias
if state.aliases[aliasfqm] ~= nil and state.aliases[aliasfqm] ~= fqm then
return nil, ("trying to define alias %q for variable %q, but already exist and refer to different variable %q; at %s"):format(aliasfqm, fqm, state.aliases[aliasfqm], line.source)
end
state.aliases[aliasfqm] = fqm
elseif rem:match("[^%s]") then
return nil, ("expected end-of-line after identifier in checkpoint decorator, but got %q; at %s"):format(rem, line.source)
end
-- define checkpoint
namespace = fqm.."."
r.checkpoint = true
r.parent_function = true
r.namespace = fqm.."."
r.name = fqm
if not state.functions[fqm] then
state.functions[fqm] = {
{
arity = 0,
value = r
}
}
if not state.variables[fqm..".👁️"] then
state.variables[fqm..".👁️"] = {
type = "number",
value = 0
}
end
-- define alias for 👁️
local seen_alias = state.builtin_aliases["👁️"]
if seen_alias then
local alias = ("%s.%s"):format(fqm, seen_alias)
if state.aliases[alias] ~= nil and state.aliases[alias] then
return nil, ("trying to define alias %q for variable %q, but already exist and refer to different variable %q; at %s"):format(alias, fqm..".👁️", state.aliases[alias], line.source)
end
state.aliases[alias] = fqm..".👁️"
end
else
table.insert(state.functions[fqm], {
arity = 0,
value = r
})
end
-- tag
elseif l:match("^..+%#.-$") then
local expr
l, expr = l:match("^(.-)%s*%#(.-)$")
r.tag = expr
end
end
-- else-condition & condition -- else-condition & condition
if l:match("^~~?") then if l:match("^~~?") then
r.type = l:match("^~~") and "else-condition" or "condition" r.type = l:match("^~~") and "else-condition" or "condition"
@ -169,7 +96,6 @@ local function parse_line(line, state, namespace)
end end
-- store parent function and run checkpoint when line is read -- store parent function and run checkpoint when line is read
if r.type == "checkpoint" then if r.type == "checkpoint" then
r.checkpoint = true
r.parent_function = true r.parent_function = true
end end
-- don't keep function node in block AST -- don't keep function node in block AST
@ -336,52 +262,78 @@ end
-- * 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_function)
local block = { type = "block" } local block = { type = "block" }
local lastLine -- last line AST for _, l in ipairs(indented) do
for i, l in ipairs(indented) do
-- parsable line -- parsable line
if l.content then local ast, err = parse_line(l, state, namespace)
local ast, err = parse_line(l, state, namespace) if err then return nil, err end
if err then return nil, err end -- store parent function
lastLine = ast if ast.parent_function then ast.parent_function = parent_function end
-- store parent function -- add to block AST
if ast.parent_function then ast.parent_function = parent_function end if not ast.remove_from_block_ast then
-- add to block AST ast.parent_block = block
if not ast.remove_from_block_ast then -- add ast node
ast.parent_block = block ast.parent_position = #block+1
-- add ast node table.insert(block, ast)
ast.parent_position = #block+1 end
if ast.replace_with then -- add child
if indented[i+1].content then if ast.child then ast.child = { type = "block", parent_line = ast } end
table.insert(indented, i+1, { content = ast.replace_with, source = l.source }) -- queue in expression evalution
else table.insert(state.queued_lines, { namespace = ast.namespace or namespace, line = ast })
table.insert(indented, i+2, { content = ast.replace_with, source = l.source }) -- if line has children
end -- indented block (ignore block comments)
else if l.children and ast.type ~= "comment" then
table.insert(block, ast) if not ast.child then
end return nil, ("line %s (%s) can't have children"):format(ast.source, ast.type)
end
-- add child
if ast.child then ast.child = { type = "block", parent_line = ast } end
-- queue in expression evalution
table.insert(state.queued_lines, { namespace = ast.namespace or namespace, line = ast })
-- indented (ignore block comments)
elseif lastLine.type ~= "comment" then
if not lastLine.child then
return nil, ("line %s (%s) can't have children"):format(lastLine.source, lastLine.type)
else else
local r, e = parse_block(l, state, lastLine.namespace or namespace, lastLine.type == "function" and lastLine or parent_function) local r, e = parse_block(l.children, state, ast.namespace or namespace, ast.type == "function" and ast or parent_function)
if not r then return r, e end if not r then return r, e end
r.parent_line = lastLine r.parent_line = ast
lastLine.child = r ast.child = r
end end
end end
end end
return block return block
end end
--- returns the nested list of lines {content="", line=1}, grouped by indentation -- returns new_indented
local function transform_indented(indented)
local i = 1
while i <= #indented do
local l = indented[i]
-- condition decorator
if l.content:match("^.-%s*[^~]%~[^#~$]-$") then
local decorator
l.content, decorator = l.content:match("^(..-)%s*(%~[^#~$]-)$")
indented[i] = { content = decorator, source = l.source, children = { l } }
-- tag decorator
elseif l.content:match("^..-%s*%#[^#~$]-$") then
local decorator
l.content, decorator = l.content:match("^(..-)%s*(%#[^#~$]-)$")
indented[i] = { content = decorator, source = l.source, children = { l } }
-- function decorator
elseif l.content:match("^..-%s*%$[^#~$]-$") then
local name
l.content, name = l.content:match("^(..-)%s*%$([^#~$]-)$")
indented[i] = { content = "~"..name, source = l.source }
table.insert(indented, i+1, { content = "$"..name, source = l.source, children = { l } })
i = i + 1 -- $ line should not contain any decorator anymore
else
i = i + 1 -- only increment when no decorator, as there may be several decorators per line
end
-- indented block
if l.children then
transform_indented(l.children)
end
end
return indented
end
--- returns the nested list of lines {content="", line=1, children={lines...} or nil}, parsing indentation
-- multiple empty lines are merged -- multiple empty lines are merged
-- * list, last line -- * list, last line, insert_empty_line: in case of success
-- * nil, err: in case of error
local function parse_indent(lines, source, i, indentLevel, insert_empty_line) local function parse_indent(lines, source, i, indentLevel, insert_empty_line)
i = i or 1 i = i or 1
indentLevel = indentLevel or 0 indentLevel = indentLevel or 0
@ -396,9 +348,14 @@ local function parse_indent(lines, source, i, indentLevel, insert_empty_line)
end end
table.insert(indented, { content = line, source = ("%s:%s"):format(source, i) }) table.insert(indented, { content = line, source = ("%s:%s"):format(source, i) })
elseif #indent > indentLevel then elseif #indent > indentLevel then
local t if #indented == 0 then
t, i, insert_empty_line = parse_indent(lines, source, i, #indent, insert_empty_line) return nil, ("unexpected indentation; at %s:%s"):format(source, i)
table.insert(indented, t) else
local t
t, i, insert_empty_line = parse_indent(lines, source, i, #indent, insert_empty_line)
if not t then return nil, i end
indented[#indented].children = t
end
else else
return indented, i-1, insert_empty_line return indented, i-1, insert_empty_line
end end
@ -426,17 +383,19 @@ end
local function parse(state, s, name, source) local function parse(state, s, name, source)
-- parse lines -- parse lines
local lines = parse_lines(s) local lines = parse_lines(s)
local indented = parse_indent(lines, source or name) local indented, e = parse_indent(lines, source or name)
if not indented then return nil, e end
-- wrap in named function if neccessary -- wrap in named function if neccessary
if name ~= "" then if name ~= "" then
if not name:match("^"..identifier_pattern.."$") then if not name:match("^"..identifier_pattern.."$") then
return nil, ("invalid function name %q"):format(name) return nil, ("invalid function name %q"):format(name)
end end
indented = { indented = {
{ content = "$ "..name, source = ("%s:%s"):format(source or name, 0) }, { content = "$ "..name, source = ("%s:%s"):format(source or name, 0), children = indented },
indented
} }
end end
-- transform ast
indented = transform_indented(indented)
-- parse -- parse
local root, err = parse_block(indented, state, "") local root, err = parse_block(indented, state, "")
if not root then return nil, err end if not root then return nil, err end

View file

@ -195,6 +195,7 @@ else
if args["write-new"] and e:match("No such file") then if args["write-new"] and e:match("No such file") then
write_result(filebase, result) write_result(filebase, result)
print("Written result file for "..filebase) print("Written result file for "..filebase)
success = success + 1
elseif not args.silent then elseif not args.silent then
print("> "..namespace) print("> "..namespace)
print(e) print(e)

View file

@ -0,0 +1,3 @@
a.👁️: {a.👁️} $ a
~ a()

View file

@ -0,0 +1,9 @@
$ f
ko
a.👁️: {a.👁️} $ a
ok
~ f.a
In function:
~ f

View file

@ -0,0 +1,37 @@
local _={}
_[15]={}
_[14]={}
_[13]={}
_[12]={}
_[11]={}
_[10]={data="ok",tags=_[15]}
_[9]={data="a.\240\159\145\129\239\184\143: 1",tags=_[14]}
_[8]={data="ko",tags=_[13]}
_[7]={data="In function:",tags=_[12]}
_[6]={data="a.\240\159\145\129\239\184\143: 0",tags=_[11]}
_[5]={_[7],_[8],_[9],_[10]}
_[4]={_[6]}
_[3]={"return"}
_[2]={"text",_[5]}
_[1]={"text",_[4]}
return {_[1],_[2],_[3]}
--[[
{ "text", { {
data = "a.👁️: 0",
tags = {}
} } }
{ "text", { {
data = "In function:",
tags = {}
}, {
data = "ko",
tags = {}
}, {
data = "a.👁️: 1",
tags = {}
}, {
data = "ok",
tags = {}
} } }
{ "return" }
]]--

View file

@ -0,0 +1 @@
a.👁️: {a.👁️} $ a

View file

@ -1,6 +1,6 @@
local _={} local _={}
_[5]={} _[5]={}
_[4]={data="a.\240\159\145\129\239\184\143: 0",tags=_[5]} _[4]={tags=_[5],data="a.\240\159\145\129\239\184\143: 0"}
_[3]={_[4]} _[3]={_[4]}
_[2]={"return"} _[2]={"return"}
_[1]={"text",_[3]} _[1]={"text",_[3]}

View file

@ -1,3 +0,0 @@
a.👁️: {a.👁️} § a
~ a()

View file

@ -1,6 +0,0 @@
$ f
ko
a.👁️: {a.👁️} § a
ok
~ f.a

View file

@ -1,19 +0,0 @@
local _={}
_[7]={}
_[6]={}
_[5]={data="ok",tags=_[7]}
_[4]={data="a.\240\159\145\129\239\184\143: 0",tags=_[6]}
_[3]={_[4],_[5]}
_[2]={"return"}
_[1]={"text",_[3]}
return {_[1],_[2]}
--[[
{ "text", { {
data = "a.👁️: 0",
tags = {}
}, {
data = "ok",
tags = {}
} } }
{ "return" }
]]--

View file

@ -1 +0,0 @@
a.👁️: {a.👁️} § a