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

Improve naming consistency

expression/conditions -> conditions
paragraphs/checkpoints -> checkpoints
merge/flush/commit -> merge
This commit is contained in:
Étienne Fildadut 2021-04-23 02:27:06 +02:00
parent b7f38a16fd
commit 6f564ea0e2
6 changed files with 73 additions and 73 deletions

View file

@ -31,7 +31,7 @@ And things that are halfway there but *should* be there eventually (i.e., TODO):
* language independant; scripts should (hopefully) be easily localizable into any language (it's possible, but doesn't provide any batteries for this right now) * language independant; scripts should (hopefully) be easily localizable into any language (it's possible, but doesn't provide any batteries for this right now)
Defaults variables use emoji and then it's expected to alias them; works but not the most satisfying solution. Defaults variables use emoji and then it's expected to alias them; works but not the most satisfying solution.
* a good documentation * a good documentation
Need to work on consistent naming (paragraphs/checkpoints/commit, call syntaxes) Need to work on consistent naming of Anselme concepts
A step by step tutorial A step by step tutorial
Things that Anselme is not: Things that Anselme is not:
@ -86,11 +86,11 @@ Another line.
random line whith indentation which makes no sense at all. random line whith indentation which makes no sense at all.
``` ```
#### Commiting / checkpoints #### Checkpoints
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 (a paragraph line), Anselme will commit its local state into the global one, i.e., make every change accessible to other scripts. 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.
``` ```
$ main $ main
@ -103,7 +103,7 @@ $ main
(But if we run the script "parallel" in parallel at this point, it will still think var=5) (But if we run the script "parallel" in parallel at this point, it will still think var=5)
§ foo § foo
But the variable will be commited to the global state on a checkpoint But the variable will be merged with the global state on a checkpoint
after: {var}=2, still, as expected after: {var}=2, still, as expected
@ -117,9 +117,9 @@ $ parallel
The purpose of this system is both to allow several scripts to run at the same time with an easy way to avoid interferences, and to make sure the global state is always in a consistent (and not in the middle of a calculation): since scripts can be interrupted at any time, when it is interrupted, anything that was changed between the last checkpoint and the interruption will be discarded. When running the script again, it will resume correctly at the last reached checkpoint. See [function calls](#function-calls) for more details on how to call/resume a function. The purpose of this system is both to allow several scripts to run at the same time with an easy way to avoid interferences, and to make sure the global state is always in a consistent (and not in the middle of a calculation): since scripts can be interrupted at any time, when it is interrupted, anything that was changed between the last checkpoint and the interruption will be discarded. When running the script again, it will resume correctly at the last reached checkpoint. See [function calls](#function-calls) for more details on how to call/resume a function.
Checkpoints are set per function. Paragraphs are expected to be defined inside functions only. Checkpoints are set per function, and are expected to be defined inside functions only.
Commiting also happens after a paragraph has been manually called or resumed from. State merging also happens after a checkpoint has been manually called or resumed from.
### Lines types ### Lines types
@ -137,9 +137,9 @@ There's different types of lines, depending on their first character(s) (after i
here here
``` ```
* `~`: expression line. Can be followed by an [expression](#expressions); otherwise the expression `1` is assumed. If the expression evaluates to [true](#truethness), run its children. * `~`: condition line. Can be followed by an [expression](#expressions); otherwise the expression `1` is assumed. If the expression evaluates to [true](#truethness), run its children. Without children, this line is typically use to simply run an expression.
* `~~`: else expression. Same as an expression line, but is only run if the last expression or else-expression line (in the same indentation block) was false (regardless of line distance). * `~~`: else-condition. Same as a condition line, but is only run if the last condition or else-condition line (in the same indentation block) was false (regardless of line distance).
``` ```
~ 1 ~ 1
@ -164,7 +164,7 @@ There's different types of lines, depending on their first character(s) (after i
> Last choice > Last choice
``` ```
* `$`: function line. Followed by an [identifier](#identifiers), then eventually an [alias](#aliases), and eventually a parameter list. Define a function using its children as function body. * `$`: function line. Followed by an [identifier](#identifiers), then eventually an [alias](#aliases), and eventually a parameter list. Define a function using its children as function body. Also define a new namespace for its children.
The function body is not executed when the line is reached; it must be explicitely called in an expression. See [expressions](#function-calls) to see the different ways of calling a function. The function body is not executed when the line is reached; it must be explicitely called in an expression. See [expressions](#function-calls) to see the different ways of calling a function.
@ -215,15 +215,15 @@ Functions can return a value using a [return line](#lines-that-can-t-have-childr
Functions always have the following variables defined in its namespace by default: Functions always have the following variables defined in its namespace by default:
`👁️`: number, number of times the function was executed before `👁️`: number, number of times the function was executed before
`🏁`: string, name of last reached checkpoint/paragraph `🏁`: string, name of last reached checkpoint
* `§`: paragraph. Followed by an [identifier](#identifiers), then eventually an [alias](#aliases). Define a paragraph. A paragraph act as a checkpoint. * `§`: checkpoint. Followed by an [identifier](#identifiers), then eventually an [alias](#aliases). Define a checkpoint. Also define a new namespace for its children.
The function 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](#paragraph-calls) to see the different ways of calling a paragraph. The function 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.
It is a checkpoint and will commit variables when the line is reached. See [committing](#committing-checkpoints). The local interpreter state will be merged with the global state when the line is reached. See [checkpoints](#checkpoints).
When executing the parent function after this checkpoint has been reached (using the paranthesis-less function call syntax), the function will resume from this checkpoint, and the paragraph's children will be run. This is meant to be used as a way to restart the conversation from this point after it was interrupted, providing necessary context. When executing the parent function after this checkpoint has been reached (using the paranthesis-less function call syntax), the function will resume from this checkpoint, and the checkpoint's children will be run. This is meant to be used as a way to restart the conversation from this point after it was interrupted, providing necessary context.
``` ```
$ inane dialog $ inane dialog
@ -233,9 +233,9 @@ $ inane dialog
(further dialog here) (further dialog here)
``` ```
Paragraphs always have the following variable defined in its namespace by default: Checkpoints always have the following variable defined in its namespace by default:
`👁️`: number, number of times the paragraph was reached or executed before `👁️`: number, number of times the checkpoint was reached or executed before
* `#`: tag line. Can be followed by an [expression](#expressions); otherwise nil expression is assumed. The results of the [expression](#expressions) will be added to the tags send along with any event sent from its children. Can be nested. * `#`: tag line. Can be followed by an [expression](#expressions); otherwise nil expression is assumed. The results of the [expression](#expressions) will be added to the tags send along with any event sent from its children. Can be nested.
@ -298,9 +298,9 @@ And this is more text, in a different event.
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.
* `~`: expression decorator. Same as an expression 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. Does not affect following else-conditions.
* `§`: paragraph decorator. Same as a paragraph line, behaving as if this line was it sole child. * `§`: 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. * `#`: tag decorator. Same as a tag line, behaving as if this line was it sole child.
@ -366,7 +366,7 @@ Every event have a type (`text`, `choice`, `return` or `error` by default, custo
Valid identifiers must be at least 1 caracters long and can contain anything except the caracters `%/*+-()!&|=$§?><:{}[],\`. They can contain spaces. Valid identifiers must be at least 1 caracters long and can contain anything except the caracters `%/*+-()!&|=$§?><:{}[],\`. They can contain spaces.
When defining an identifier (using a function, paragraph or variable delcaration line), it will be defined into the current namespace (defined by the parent function/paragraph). 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 it indentation block:
@ -396,7 +396,7 @@ Var1 in the fn1 namespace = 2: {fn1.var1}
#### Aliases #### Aliases
When defining identifiers (in variables, functions or paragraph 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 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).
``` ```
:42 name: alias :42 name: alias
@ -424,7 +424,7 @@ Hi {player name}!
Salut {nom du joueur} ! Salut {nom du joueur} !
``` ```
Variables that are defined automatically by Anselme (`👁️` and `🏁` in paragraphs and functions) can be automatically aliased using `vm:setaliases("👁alias", "🏁alias")`. See [API](#api-reference). Variables that are defined automatically by Anselme (`👁️` and `🏁` in checkpoints and functions) can be automatically aliased using `vm:setaliases("👁alias", "🏁alias")`. See [API](#api-reference).
### Expressions ### Expressions
@ -490,7 +490,7 @@ $ f(a)
~ f("an argument") ~ f("an argument")
``` ```
Please note, however, that if the function contains checkpoints/paragraphs, these two syntaxes behave differently. Without parantheses, the function will resume from the last reached checkpoint; with parantheses, the function always restart from its beginning: Please note, however, that if the function contains checkpoints, these two syntaxes behave differently. Without parantheses, the function will resume from the last reached checkpoint; with parantheses, the function always restart from its beginning:
``` ```
$ f $ f
@ -533,9 +533,9 @@ $ f
this is text: {f} this is text: {f}
``` ```
#### Paragraph calls #### Checkpoint calls
Most of the time, you should'nt need to call paragraphs yourself - they will be automatically be set as the active checkpoint when the interperter reach their line, and they will be automatically called when resuming its parent function. Most of the time, you should'nt need to call checkpoints yourself - they will be automatically be set as the active checkpoint when the interperter reach their line, and they will be automatically called when resuming its parent function.
But in the cases when you want to manually set the current checkpoint, you can call it with a similar syntax to paranthesis-less function calls: But in the cases when you want to manually set the current checkpoint, you can call it with a similar syntax to paranthesis-less function calls:
@ -546,17 +546,17 @@ $ f
b b
c c
Force run from checkpoint, will write "b" and "c" and set the current checkpoint to "checkpoint": Force run the function starting from checkpoint, will write "b" and "c" and set the current checkpoint to "checkpoint":
~ f.checkpoint ~ f.checkpoint
Will correctly resumes from the checkpoint, and write "b" and "c": Will correctly resumes from the last set checkpoint, and write "b" and "c":
~ f ~ f
Function can always be restarted from the begining using parantheses: Function can always be restarted from the begining using parantheses:
~ f() ~ f()
``` ```
You can also only execute the paragraphs' children code only by using a parantheses-syntax: You can also only execute the checkpoints' children code only by using a parantheses-syntax:
``` ```
$ f $ f
@ -574,13 +574,13 @@ And will resume from the checkpoint like before:
Method style calling is also possible, like with functions. Method style calling is also possible, like with functions.
Paragraphs commit variables after being called (either manually or automatically from resuming a function). The commit always happen after the paragraph's child block has been ran. Checkpoints merge variables after being called (either manually or automatically from resuming a function). See [checkpoints](#checkpoints). The merge always happen after the checkpoint's child block has been ran.
Please also be aware that when resuming from a paragraph, Anselme will try to restore the interpreter state as if the function was correctly executed from the start up to this paragraph. This includes: Please also be aware that when resuming from a checkpoint, Anselme will try to restore the interpreter state as if the function was correctly executed from the start up to this checkpoint. This includes:
* if the paragraph is in a expression block, it will assume the expression was true (but will not re-evaluate it) * if the checkpoint is in a condition block, it will assume the condition was true (but will not re-evaluate it)
* if the paragraph is in a choice block, it will assume this choice was selected (but will not re-evaluate any of the choices from the same choice group) * if the checkpoint is in a choice block, it will assume this choice was selected (but will not re-evaluate any of the choices from the same choice group)
* will try to re-add every tag from parent lines; this require Anselme to re-evaluate every tag line and decorator that's a parent of the paragraph in the function. Be careful if your tag expressions have side-effects. * will try to re-add every tag from parent lines; this require Anselme to re-evaluate every tag line and decorator that's a parent of the checkpoint in the function. Be careful if your tag expressions have side-effects.
#### Operators #### Operators
@ -650,7 +650,7 @@ This only works on strings:
##### Sequential execution ##### Sequential execution
`cycle(...)`: given function/paragraph identifiers as string as arguments, will execute them in the order given each time the function is ran; e.g., `cycle("a", "b")` will execute a on the first execution, then b, then a again, etc. `cycle(...)`: given function/checkpoint identifiers as string as arguments, will execute them in the order given each time the function is ran; e.g., `cycle("a", "b")` will execute a on the first execution, then b, then a again, etc.
`next(...)`: same as cycle, but will not cycle; once the end of sequence is reached, will keep executing the last element. `next(...)`: same as cycle, but will not cycle; once the end of sequence is reached, will keep executing the last element.

View file

@ -14,7 +14,7 @@ local expression = require((...):gsub("anselme$", "parser.expression"))
local eval = require((...):gsub("anselme$", "interpreter.expression")) local eval = require((...):gsub("anselme$", "interpreter.expression"))
local run_line = require((...):gsub("anselme$", "interpreter.interpreter")).run_line local run_line = require((...):gsub("anselme$", "interpreter.interpreter")).run_line
local to_lua = require((...):gsub("anselme$", "interpreter.common")).to_lua local to_lua = require((...):gsub("anselme$", "interpreter.common")).to_lua
local flush_state = require((...):gsub("anselme$", "interpreter.common")).flush_state local merge_state = require((...):gsub("anselme$", "interpreter.common")).merge_state
local stdfuncs = require((...):gsub("anselme$", "stdlib.functions")) local stdfuncs = require((...):gsub("anselme$", "stdlib.functions"))
-- wrappers for love.filesystem / luafilesystem -- wrappers for love.filesystem / luafilesystem
@ -55,7 +55,7 @@ local interpreter_methods = {
vm = nil, vm = nil,
--- run the VM until the next event --- run the VM until the next event
-- will commit changed variables on successful script end -- will merge changed variables on successful script end
-- returns event, data; if event is "return" or "error", the interpreter must not be stepped further -- returns event, data; if event is "return" or "error", the interpreter must not be stepped further
step = function(self) step = function(self)
-- check status -- check status
@ -84,7 +84,7 @@ local interpreter_methods = {
local success, event, data = coroutine.resume(self.state.interpreter.coroutine) local success, event, data = coroutine.resume(self.state.interpreter.coroutine)
anselme.running = previous anselme.running = previous
if not success then return "error", event end if not success then return "error", event end
if event == "return" then flush_state(self.state) end if event == "return" then merge_state(self.state) end
return event, data return event, data
end, end,
@ -254,7 +254,7 @@ local vm_mt = {
return self return self
end, end,
--- set aliases for built-in variables 👁️ and 🏁 that will be defined on every new paragraph and function --- set aliases for built-in variables 👁️ and 🏁 that will be defined on every new checkpoint and function
-- nil for no alias -- nil for no alias
-- return self -- return self
setaliases = function(self, seen, checkpoint) setaliases = function(self, seen, checkpoint)
@ -296,7 +296,7 @@ local vm_mt = {
end, end,
--- save/load script state --- save/load script state
-- only saves variables full names and values, so make sure to not change important variables, paragraphs and functions names between a save and a load -- 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
load = function(self, data) load = function(self, data)
local saveMajor, currentMajor = data.anselme_version:match("^[^%.]*"), anselme.version:match("^[^%.]*") local saveMajor, currentMajor = data.anselme_version:match("^[^%.]*"), anselme.version:match("^[^%.]*")
assert(saveMajor == currentMajor, ("trying to load data from an incompatible version of Anselme; save was done using %s but current version is %s"):format(data.anselme_version, anselme.version)) assert(saveMajor == currentMajor, ("trying to load data from an incompatible version of Anselme; save was done using %s but current version is %s"):format(data.anselme_version, anselme.version))
@ -353,7 +353,7 @@ local vm_mt = {
-- choice event -- choice event
choice_selected = nil, choice_selected = nil,
choice_available = {}, choice_available = {},
-- skip next choices until next event change (to skip currently running choice block when resuming from a paragraph) -- skip next choices until next event change (to skip currently running choice block when resuming from a checkpoint)
skip_choices_until_flush = nil, skip_choices_until_flush = nil,
-- interrupt -- interrupt
interrupt = nil, interrupt = nil,
@ -397,7 +397,7 @@ return setmetatable(anselme, {
-- { -- {
-- arity = {3,42}, type = { [1] = "variable" }, check = function, rewrite = function, vararg = 2, mode = "custom", -- arity = {3,42}, type = { [1] = "variable" }, check = function, rewrite = function, vararg = 2, mode = "custom",
-- value = function(state, exp) -- value = function(state, exp)
-- end -- or paragraph, function, line -- end -- or checkpoint, function, line
-- } -- }
-- }, -- },
}, },

View file

@ -4,7 +4,7 @@ local eval
local common local common
common = { common = {
-- flush interpreter state to global state -- flush interpreter state to global state
flush_state = function(state) merge_state = function(state)
local global_vars = state.interpreter.global_state.variables local global_vars = state.interpreter.global_state.variables
for var, value in pairs(state.variables) do for var, value in pairs(state.variables) do
global_vars[var] = value global_vars[var] = value

View file

@ -99,13 +99,13 @@ local function eval(state, exp)
end end
-- anselme function -- anselme function
if type(fn.value) == "table" then if type(fn.value) == "table" then
-- paragraph & paragraph decorator -- checkpoint & checkpoint decorator
if fn.value.type == "paragraph" or fn.value.paragraph then if fn.value.type == "checkpoint" or fn.value.checkpoint then
local r, e local r, e
if fn.value.type == "paragraph" then if fn.value.type == "checkpoint" then
r, e = run(state, fn.value.child, not exp.explicit_call) r, e = run(state, fn.value.child, not exp.explicit_call)
-- paragraph decorators: run single line or resume from it. -- checkpoint decorators: run single line or resume from it.
-- checkpoint & seen variables will be updated from the interpreter usual paragraph-reaching code. -- checkpoint & seen variables will be updated from the interpreter usual checkpoint-reaching code.
elseif exp.explicit_call then elseif exp.explicit_call then
r, e = run(state, fn.value.parent_block, false, fn.value.parent_position, fn.value.parent_position) r, e = run(state, fn.value.parent_block, false, fn.value.parent_position, fn.value.parent_position)
else else
@ -123,7 +123,7 @@ local function eval(state, exp)
local r, e local r, e
if exp.explicit_call or state.variables[fn.value.namespace.."🏁"].value == "" then if exp.explicit_call or state.variables[fn.value.namespace.."🏁"].value == "" then
r, e = run(state, fn.value.child) r, e = run(state, fn.value.child)
-- resume at last paragraph -- resume at last checkpoint
else else
local expr, err = expression(state.variables[fn.value.namespace.."🏁"].value, state, "") local expr, err = expression(state.variables[fn.value.namespace.."🏁"].value, state, "")
if not expr then return expr, err end if not expr then return expr, err end

View file

@ -1,5 +1,5 @@
local eval local eval
local truthy, flush_state, to_lua, eval_text, escape local truthy, merge_state, to_lua, eval_text, escape
local tags = { local tags = {
--- push new tags on top of the stack, from Anselme values --- push new tags on top of the stack, from Anselme values
@ -148,15 +148,15 @@ local function run_line(state, line)
end end
end end
end end
elseif line.type ~= "paragraph" then elseif line.type ~= "checkpoint" then
return nil, ("unknown line type %q; at %s"):format(line.type, line.source) return nil, ("unknown line type %q; at %s"):format(line.type, line.source)
end end
-- tag decorator -- tag decorator
if line.tag then if line.tag then
tags:pop(state) tags:pop(state)
end end
-- paragraph decorator and line -- checkpoint decorator and line
if line.paragraph then if line.checkpoint then
state.variables[line.namespace.."👁️"] = { state.variables[line.namespace.."👁️"] = {
type = "number", type = "number",
value = state.variables[line.namespace.."👁️"].value + 1 value = state.variables[line.namespace.."👁️"].value + 1
@ -165,7 +165,7 @@ local function run_line(state, line)
type = "string", type = "string",
value = line.name value = line.name
} }
flush_state(state) merge_state(state)
end end
end end
end end
@ -195,10 +195,10 @@ run_block = function(state, block, resume_from_there, i, j)
end end
i = i + 1 i = i + 1
end end
-- if we are exiting a paragraph 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 paragraph, the line.paragraph check in run_line is never called) -- (when resuming from a checkpoint, execution is resumed from inside the checkpoint, the line.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.paragraph then if block.parent_line and block.parent_line.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",
@ -213,7 +213,7 @@ run_block = function(state, block, resume_from_there, i, j)
value = parent_line.name value = parent_line.name
} }
end end
flush_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 function boundary
@ -294,7 +294,7 @@ local interpreter = {
package.loaded[...] = interpreter package.loaded[...] = interpreter
eval = require((...):gsub("interpreter$", "expression")) eval = require((...):gsub("interpreter$", "expression"))
local common = require((...):gsub("interpreter$", "common")) local common = require((...):gsub("interpreter$", "common"))
truthy, flush_state, to_lua, eval_text = common.truthy, common.flush_state, common.to_lua, common.eval_text truthy, merge_state, to_lua, eval_text = common.truthy, common.merge_state, common.to_lua, common.eval_text
escape = require((...):gsub("interpreter%.interpreter$", "parser.common")).escape escape = require((...):gsub("interpreter%.interpreter$", "parser.common")).escape
return interpreter return interpreter

View file

@ -22,21 +22,21 @@ local function parse_line(line, state, namespace)
local expr local expr
l, expr = l:match("^(.-)%s*%~(.-)$") l, expr = l:match("^(.-)%s*%~(.-)$")
r.condition = expr r.condition = expr
-- paragraph -- checkpoint
elseif l:match("^..+§.-$") then elseif l:match("^..+§.-$") then
-- get identifier -- get identifier
local name local name
l, name = l:match("^(.-)%s*§(.-)$") l, name = l:match("^(.-)%s*§(.-)$")
local identifier, rem = name:match("^("..identifier_pattern..")(.-)$") local identifier, rem = name:match("^("..identifier_pattern..")(.-)$")
if not identifier then return nil, ("no valid identifier in paragraph decorator %q; at %s"):format(identifier, line.source) end if not identifier then return nil, ("no valid identifier in checkpoint decorator %q; at %s"):format(identifier, line.source) end
-- format identifier -- format identifier
local fqm = ("%s%s"):format(namespace, format_identifier(identifier)) local fqm = ("%s%s"):format(namespace, format_identifier(identifier))
-- get alias -- get alias
if rem:match("^%:") then if rem:match("^%:") then
local content = rem:sub(2) local content = rem:sub(2)
local alias, rem2 = content:match("^("..identifier_pattern..")(.-)$") local alias, rem2 = content:match("^("..identifier_pattern..")(.-)$")
if not alias then return nil, ("expected an identifier in alias in paragraph decorator, but got %q; at %s"):format(content, line.source) end 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 paragraph decorator, but got %q; at %s"):format(rem2, 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 -- format alias
local aliasfqm = ("%s%s"):format(namespace, format_identifier(alias)) local aliasfqm = ("%s%s"):format(namespace, format_identifier(alias))
-- define alias -- define alias
@ -45,11 +45,11 @@ local function parse_line(line, state, namespace)
end end
state.aliases[aliasfqm] = fqm state.aliases[aliasfqm] = fqm
elseif rem:match("[^%s]") then elseif rem:match("[^%s]") then
return nil, ("expected end-of-line after identifier in paragraph decorator, but got %q; at %s"):format(rem, line.source) return nil, ("expected end-of-line after identifier in checkpoint decorator, but got %q; at %s"):format(rem, line.source)
end end
-- define paragraph -- define checkpoint
namespace = fqm.."." namespace = fqm.."."
r.paragraph = true r.checkpoint = true
r.parent_function = true r.parent_function = true
r.namespace = fqm.."." r.namespace = fqm.."."
r.name = fqm r.name = fqm
@ -104,14 +104,14 @@ local function parse_line(line, state, namespace)
r.push_event = "choice" r.push_event = "choice"
r.child = true r.child = true
r.text = l:match("^>%s*(.-)$") r.text = l:match("^>%s*(.-)$")
-- function & paragraph -- function & checkpoint
elseif l:match("^%$") or l:match("") then -- § is a 2-bytes caracter, DO NOT USE LUA PATTERN OPERATORS as they operate on single bytes elseif l:match("^%$") or l:match("") then -- § is a 2-bytes caracter, DO NOT USE LUA PATTERN OPERATORS as they operate on single bytes
r.type = l:match("^%$") and "function" or "paragraph" r.type = l:match("^%$") and "function" or "checkpoint"
r.child = true r.child = true
-- get identifier -- get identifier
local lc = l:match("^%$(.*)$") or l:match("^§(.*)$") local lc = l:match("^%$(.*)$") or l:match("^§(.*)$")
local identifier, rem = lc:match("^("..identifier_pattern..")(.-)$") local identifier, rem = lc:match("^("..identifier_pattern..")(.-)$")
if not identifier then return nil, ("no valid identifier in paragraph/function definition line %q; at %s"):format(lc, line.source) end if not identifier then return nil, ("no valid identifier in checkpoint/function definition line %q; at %s"):format(lc, line.source) end
-- format identifier -- format identifier
local fqm = ("%s%s"):format(namespace, format_identifier(identifier)) local fqm = ("%s%s"):format(namespace, format_identifier(identifier))
-- get alias -- get alias
@ -119,12 +119,12 @@ local function parse_line(line, state, namespace)
local content = rem:sub(2) local content = rem:sub(2)
local alias local alias
alias, rem = content:match("^("..identifier_pattern..")(.-)$") alias, rem = content:match("^("..identifier_pattern..")(.-)$")
if not alias then return nil, ("expected an identifier in alias in paragraph/function definition line, but got %q; at %s"):format(content, line.source) end if not alias then return nil, ("expected an identifier in alias in checkpoint/function definition line, but got %q; at %s"):format(content, line.source) end
-- format alias -- format alias
local aliasfqm = ("%s%s"):format(namespace, format_identifier(alias)) local aliasfqm = ("%s%s"):format(namespace, format_identifier(alias))
-- define alias -- define alias
if state.aliases[aliasfqm] ~= nil and state.aliases[aliasfqm] ~= fqm then if state.aliases[aliasfqm] ~= nil and state.aliases[aliasfqm] ~= fqm then
return nil, ("trying to define alias %q for function/paragraph %q, but already exist and refer to %q; at %s"):format(aliasfqm, fqm, state.aliases[aliasfqm], line.source) return nil, ("trying to define alias %q for checkpoint/function %q, but already exist and refer to %q; at %s"):format(aliasfqm, fqm, state.aliases[aliasfqm], line.source)
end end
state.aliases[aliasfqm] = fqm state.aliases[aliasfqm] = fqm
end end
@ -159,7 +159,7 @@ local function parse_line(line, state, namespace)
table.insert(r.params, param_fqm) table.insert(r.params, param_fqm)
end end
elseif rem:match("[^%s]") then elseif rem:match("[^%s]") then
return nil, ("expected end-of-line at end of paragraph/function definition line, but got %q; at %s"):format(rem, line.source) return nil, ("expected end-of-line at end of checkpoint/function definition line, but got %q; at %s"):format(rem, line.source)
end end
local arity, vararg = #r.params, nil local arity, vararg = #r.params, nil
if arity > 0 and r.params[arity]:match("%.%.%.$") then -- varargs if arity > 0 and r.params[arity]:match("%.%.%.$") then -- varargs
@ -167,9 +167,9 @@ local function parse_line(line, state, namespace)
vararg = arity vararg = arity
arity = { arity-1, math.huge } arity = { arity-1, math.huge }
end end
-- store parent function and run paragraph when line is read -- store parent function and run checkpoint when line is read
if r.type == "paragraph" then if r.type == "checkpoint" then
r.paragraph = true 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