mirror of
https://github.com/Reuh/anselme.git
synced 2025-10-27 08:39:30 +00:00
Compare commits
2 commits
ed7fe34853
...
77c6ac6ba2
| Author | SHA1 | Date | |
|---|---|---|---|
| 77c6ac6ba2 | |||
| 876135401c |
11 changed files with 425 additions and 70 deletions
|
|
@ -5,8 +5,56 @@ local Event = ast.abstract.Event
|
|||
|
||||
local operator_priority = require("anselme.common").operator_priority
|
||||
|
||||
--- ChoiceEventData represent the data returned by an event with the type `"choice"`.
|
||||
-- See the [language documentation](language.md#choices) for more details on how to create a choice event.
|
||||
--
|
||||
-- A ChoiceEventData contains a list of [LuaText](#luatext), each LuaText representing a separate choice of the choice event.
|
||||
--
|
||||
-- For example, the following Anselme script:
|
||||
--
|
||||
-- ```
|
||||
-- *| Yes!
|
||||
-- *| No.
|
||||
-- ```
|
||||
-- will return a choice event containing two LuaTexts, the first containing the text "Yes!" and the second "No.".
|
||||
--
|
||||
-- Usage:
|
||||
-- ```lua
|
||||
-- current_choice = nil
|
||||
-- waiting_for_choice = false
|
||||
--
|
||||
-- -- in your anselem event handling loop:
|
||||
-- if not waiting_for_choice then
|
||||
-- local event_type, event_data = run_state:step()
|
||||
-- if event_type == "choice" then
|
||||
-- -- event_data is a ChoiceEventData, i.e. a list of LuaText
|
||||
-- for i, luatext in ipairs(event_data) do
|
||||
-- write(("Choice number %s:"):format(i))
|
||||
-- -- luatext is a list of text parts { text = "text string", tags = { ... } }
|
||||
-- for _, textpart in ipairs(luatext) do
|
||||
-- write_choice_part_with_color(textpart.text, textpart.tags.color)
|
||||
-- end
|
||||
-- else
|
||||
-- -- handle other event types...
|
||||
-- end
|
||||
-- current_choice = event_data
|
||||
-- waiting_for_choice = true
|
||||
-- end
|
||||
-- end
|
||||
--
|
||||
-- -- somewhere in your code where choices are selected
|
||||
-- current_choice:select(choice_number)
|
||||
-- waiting_for_choice = false
|
||||
-- ```
|
||||
-- @title ChoiceEventData
|
||||
local ChoiceEventData = class {
|
||||
-- [1] = LuaText, ...
|
||||
|
||||
_selected = nil,
|
||||
|
||||
--- Choose the choice at position `choice` (number).
|
||||
--
|
||||
-- A choice must be selected after receiving a choice event and before calling `:step` again.
|
||||
choose = function(self, choice)
|
||||
self._selected = choice
|
||||
end
|
||||
|
|
@ -36,7 +84,7 @@ Choice = ast.abstract.Runtime(Event) {
|
|||
build_event_data = function(self, state, event_buffer)
|
||||
local l = ChoiceEventData:new()
|
||||
for _, c in event_buffer:iter(state) do
|
||||
table.insert(l, c.text)
|
||||
table.insert(l, c.text:to_lua(state))
|
||||
end
|
||||
return l
|
||||
end,
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@ Struct = ast.abstract.Runtime {
|
|||
|
||||
to_lua = function(self, state)
|
||||
local l = {}
|
||||
for _, e in ipairs(self.table) do
|
||||
for _, e in pairs(self.table) do
|
||||
l[e[1]:to_lua(state)] = e[2]:to_lua(state)
|
||||
end
|
||||
return l
|
||||
|
|
|
|||
|
|
@ -5,16 +5,101 @@ local ArgumentTuple, Struct
|
|||
|
||||
local to_anselme = require("anselme.common.to_anselme")
|
||||
|
||||
--- A Lua-friendly representation of an Anselme Text value.
|
||||
-- They appear in both TextEventData and ChoiceEventData to represent the text that has to be shown.
|
||||
--
|
||||
-- It contains a list of _text parts_, which are parts of a single text, each part potentially having differrent tags attached.
|
||||
-- A text will typically only consist of a single part unless it was built using text interpolation.
|
||||
--
|
||||
-- Each text part is a table containing `text` (string) and `tags` (table) properties, for example: `{ text = "text part string", tags = { color = "red" } }`.
|
||||
-- @title LuaText
|
||||
-- @defer lua text
|
||||
local LuaText
|
||||
LuaText = class {
|
||||
-- [1] = { text = "string", tags = { tag_name = value, ... } }, ...
|
||||
|
||||
_state = nil,
|
||||
|
||||
--- Anselme Text value this was created from. For advanced usage only. See the source file [Text.lua](anselme/ast/Text.lua) for more information.
|
||||
-- @defer lua text
|
||||
raw = nil,
|
||||
|
||||
init = function(self, text, state)
|
||||
self._state = state
|
||||
self.raw = text
|
||||
for _, e in ipairs(text.list) do
|
||||
table.insert(self, { text = e[1]:to_lua(state), tags = e[2]:to_lua(state) })
|
||||
end
|
||||
end,
|
||||
|
||||
--- Returns a text representation of the LuaText, using Anselme's default formatting. Useful for debugging.
|
||||
--
|
||||
-- Usage: `print(luatext)`
|
||||
-- @defer lua text
|
||||
__tostring = function(self)
|
||||
return self.raw:format(self._state)
|
||||
end
|
||||
}
|
||||
|
||||
--- TextEventData represent the data returned by an event with the type `"text"`.
|
||||
-- See the [language documentation](language.md#texts) for more details on how to create a text event.
|
||||
--
|
||||
-- A TextEventData contains a list of [LuaText](#luatext), each LuaText representing a separate line of the text event.
|
||||
--
|
||||
-- For example, the following Anselme script:
|
||||
--
|
||||
-- ```
|
||||
-- | Hi!
|
||||
-- | My name's John.
|
||||
-- ```
|
||||
-- will return a text event containing two LuaTexts, the first containing the text "Hi!" and the second "My name's John.".
|
||||
--
|
||||
-- Usage:
|
||||
-- ```lua
|
||||
-- local event_type, event_data = run_state:step()
|
||||
-- if event_type == "text" then
|
||||
-- -- event_data is a TextEventData, i.e. a list of LuaText
|
||||
-- for _, luatext in ipairs(event_data) do
|
||||
-- -- luatext is a list of text parts { text = "text string", tags = { ... } }
|
||||
-- for _, textpart in ipairs(luatext) do
|
||||
-- write_text_part_with_color(textpart.text, textpart.tags.color)
|
||||
-- end
|
||||
-- write_text("\n") -- for example, if we want a newline between each text line
|
||||
-- end
|
||||
-- else
|
||||
-- -- handle other event types...
|
||||
-- end
|
||||
-- ```
|
||||
-- @title TextEventData
|
||||
local TextEventData
|
||||
TextEventData = class {
|
||||
-- returns a list of TextEventData where the first element of each text of each TextEventData has the same value for the tag tag_name
|
||||
-- [1] = LuaText, ...
|
||||
|
||||
--- Returns a list of TextEventData where the first part of each LuaText of each TextEventData has the same value for the tag `tag_name`.
|
||||
--
|
||||
-- In other words, this groups all the LuaTexts contained in this TextEventData using the `tag_name` tag and returns a list containing these groups.
|
||||
--
|
||||
-- For example, with the following Anselme script:
|
||||
-- ```
|
||||
-- speaker: "John" #
|
||||
-- | A
|
||||
-- | B
|
||||
-- speaker: "Lana" #
|
||||
-- | C
|
||||
-- speaker: "John" #
|
||||
-- | D
|
||||
-- ```
|
||||
-- calling `text_event_data:group_by("speaker")` will return a list of three TextEventData:
|
||||
-- * the first with the texts "A" and "B"; both with the tag `speaker="John"`
|
||||
-- * the second with the text "C"; with the tag `speaker="Lana"`
|
||||
-- * the last with the text "D"; wiith the tag `speaker="John"`
|
||||
group_by = function(self, tag_name)
|
||||
local l = {}
|
||||
local current_group
|
||||
local tag_key = to_anselme(tag_name)
|
||||
local last_value
|
||||
for _, event in ipairs(self) do
|
||||
local list = event.list
|
||||
for _, luatext in ipairs(self) do
|
||||
local list = luatext.raw.list
|
||||
if #list > 0 then
|
||||
local value = list[1][2]:get_strict(tag_key)
|
||||
if (not current_group) or (last_value == nil and value) or (last_value and value == nil) or (last_value and value and last_value:hash() ~= value:hash()) then -- new group
|
||||
|
|
@ -22,11 +107,11 @@ TextEventData = class {
|
|||
table.insert(l, current_group)
|
||||
last_value = value
|
||||
end
|
||||
table.insert(current_group, event) -- add to current group
|
||||
table.insert(current_group, luatext) -- add to current group
|
||||
end
|
||||
end
|
||||
return l
|
||||
end,
|
||||
end
|
||||
}
|
||||
|
||||
local Text
|
||||
|
|
@ -68,6 +153,10 @@ Text = Runtime(Event) {
|
|||
return ("| %s |"):format(table.concat(t, " "))
|
||||
end,
|
||||
|
||||
to_lua = function(self, state)
|
||||
return LuaText:new(self, state)
|
||||
end,
|
||||
|
||||
-- autocall when used directly as a statement
|
||||
eval_statement = function(self, state)
|
||||
return self:call(state, ArgumentTuple:new())
|
||||
|
|
@ -77,8 +166,8 @@ Text = Runtime(Event) {
|
|||
|
||||
build_event_data = function(self, state, event_buffer)
|
||||
local l = TextEventData:new()
|
||||
for _, event in event_buffer:iter(state) do
|
||||
table.insert(l, event)
|
||||
for _, text in event_buffer:iter(state) do
|
||||
table.insert(l, text:to_lua(state))
|
||||
end
|
||||
return l
|
||||
end,
|
||||
|
|
|
|||
|
|
@ -13,25 +13,20 @@
|
|||
-- local state = anselme.new()
|
||||
-- state:load_stdlib()
|
||||
--
|
||||
-- -- read an anselme script file
|
||||
-- local f = assert(io.open("script.ans"))
|
||||
-- local script = anselme.parse(f:read("a"), "script.ans")
|
||||
-- f:close()
|
||||
--
|
||||
-- -- load the script in a new branch
|
||||
-- -- load an anselme script file in a new branch
|
||||
-- local run_state = state:branch()
|
||||
-- run_state:run(script)
|
||||
-- run_state:run_file("script.ans")
|
||||
--
|
||||
-- -- run the script
|
||||
-- while run_state:active() do
|
||||
-- local e, data = run_state:step()
|
||||
-- if e == "text" then
|
||||
-- for _, l in ipairs(data) do
|
||||
-- print(l:format(run_state))
|
||||
-- print(l)
|
||||
-- end
|
||||
-- elseif e == "choice" then
|
||||
-- for i, l in ipairs(data) do
|
||||
-- print(("%s> %s"):format(i, l:format(run_state)))
|
||||
-- print(("%s> %s"):format(i, l))
|
||||
-- end
|
||||
-- local choice = tonumber(io.read("l"))
|
||||
-- data:choose(choice)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
local expression_to_ast = require("anselme.parser.expression.to_ast")
|
||||
|
||||
local utf8 = utf8 or require("lua-utf8")
|
||||
local ast = require("anselme.ast")
|
||||
local PartialScope, Block, Call, Identifier = ast.PartialScope, ast.Block, ast.Call, ast.Identifier
|
||||
|
||||
|
|
|
|||
|
|
@ -7,11 +7,11 @@ local tag_manager = require("anselme.state.tag_manager")
|
|||
local event_manager = require("anselme.state.event_manager")
|
||||
local translation_manager = require("anselme.state.translation_manager")
|
||||
local persistent_manager = require("anselme.state.persistent_manager")
|
||||
local uuid = require("anselme.common").uuid
|
||||
local parser = require("anselme.parser")
|
||||
local binser = require("anselme.lib.binser")
|
||||
local assert0 = require("anselme.common").assert0
|
||||
local operator_priority = require("anselme.common").operator_priority
|
||||
local common = require("anselme.common")
|
||||
local uuid, assert0, operator_priority = common.uuid, common.assert0, common.operator_priority
|
||||
local to_anselme = require("anselme.common.to_anselme")
|
||||
local anselme
|
||||
local Identifier, Return, Node
|
||||
|
||||
|
|
@ -178,12 +178,13 @@ State = class {
|
|||
--- Load a script in this branch. It will become the active script.
|
||||
--
|
||||
-- `code` is the code string or AST to run. If `code` is a string, `source` is the source name string to show in errors (optional).
|
||||
-- `tags` is an optional Lua table; its content will be added to the tags for the duration of the script.
|
||||
--
|
||||
-- Note that this will only load the script; execution will only start by using the `:step` method. Will error if a script is already active in this State.
|
||||
run = function(self, code, source)
|
||||
run = function(self, code, source, tags)
|
||||
assert(not self:active(), "a script is already active")
|
||||
self._coroutine = coroutine.create(function()
|
||||
local r = assert0(self:eval_local(code, source))
|
||||
local r = assert0(self:eval_local(code, source, tags))
|
||||
event_manager:complete_flush(self)
|
||||
if Return:is(r) then r = r.expression end
|
||||
return "return", r
|
||||
|
|
@ -191,17 +192,19 @@ State = class {
|
|||
end,
|
||||
--- Same as `:run`, but read the code from a file.
|
||||
-- `source` will be set as the file path.
|
||||
run_file = function(self, path)
|
||||
run_file = function(self, path, tags)
|
||||
local f = assert(io.open(path, "r"))
|
||||
local block = parser(f:read("a"), path)
|
||||
f:close()
|
||||
return self:run(block)
|
||||
return self:run(block, nil, tags)
|
||||
end,
|
||||
--- When a script is active, will resume running it until the next event.
|
||||
--
|
||||
-- Will error if no script is active.
|
||||
--
|
||||
-- Returns `event type string, event data`.
|
||||
--
|
||||
-- See the [events](#events) section for details on event data types for built-in events.
|
||||
step = function(self)
|
||||
assert(self:active(), "trying to step but no script is currently active")
|
||||
local success, type, data = coroutine.resume(self._coroutine)
|
||||
|
|
@ -218,16 +221,17 @@ State = class {
|
|||
--
|
||||
-- Will error if no script is active.
|
||||
--
|
||||
-- If `code` is given, the script will not be disabled but instead will be immediately replaced with this new script.
|
||||
-- `code`, `source` and `tags` are all optional and have the same behaviour as in `:run`.
|
||||
-- If they are given, the script will not be disabled but instead will be immediately replaced with this new script.
|
||||
-- The new script will then be started on the next `:step` and will preserve the current scope. This can be used to trigger an exit function or similar in the active script.
|
||||
--
|
||||
-- If this is called from within a running script, this will raise an `interrupt` event in order to stop the current script execution.
|
||||
interrupt = function(self, code, source)
|
||||
interrupt = function(self, code, source, tags)
|
||||
assert(self:active(), "trying to interrupt but no script is currently active")
|
||||
local called_from_script = self:state() == "running"
|
||||
if code then
|
||||
self._coroutine = coroutine.create(function()
|
||||
local r = assert0(self:eval_local(code, source))
|
||||
local r = assert0(self:eval_local(code, source, tags))
|
||||
event_manager:complete_flush(self)
|
||||
self.scope:reset() -- scope stack is probably messed up after the switch
|
||||
if Return:is(r) then r = r.expression end
|
||||
|
|
@ -244,19 +248,24 @@ State = class {
|
|||
--
|
||||
-- This can be called from outside a running script, but an error will be triggered the expression raise any event other than return.
|
||||
--
|
||||
-- `code` is the code string or AST to run. If `code` is a string, `source` is the source name string to show in errors (optional).
|
||||
-- `tags` is an optional Lua table; its content will be added to the tags for the duration of the expression.
|
||||
--
|
||||
-- * returns AST in case of success. Run `:to_lua(state)` on it to convert to a Lua value.
|
||||
-- * returns `nil, error message` in case of error.
|
||||
eval = function(self, code, source)
|
||||
eval = function(self, code, source, tags)
|
||||
self.scope:push_global()
|
||||
local r, e = self:eval_local(code, source)
|
||||
local r, e = self:eval_local(code, source, tags)
|
||||
self.scope:pop()
|
||||
return r, e
|
||||
end,
|
||||
--- Same as `:eval`, but evaluate the expression in the current scope.
|
||||
eval_local = function(self, code, source)
|
||||
eval_local = function(self, code, source, tags)
|
||||
if type(code) == "string" then code = parser(code, source) end
|
||||
local stack_size = self.scope:size()
|
||||
if tags then tag_manager:push(self, to_anselme(tags)) end
|
||||
local s, e = pcall(code.eval, code, self)
|
||||
if tags then tag_manager:pop(self) end
|
||||
if not s then
|
||||
self.scope:reset(stack_size)
|
||||
return nil, e
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ return [[
|
|||
:@arrondi = stdlib.round
|
||||
:@aléatoire = stdlib.rand
|
||||
|
||||
:@égal = stdlib.equal
|
||||
:@est égal = stdlib.is equal
|
||||
:@est = stdlib.is
|
||||
:@est une ancre = stdlib.is anchor
|
||||
:@est un booléen = stdlib.is boolean
|
||||
|
|
|
|||
238
doc/api.md
238
doc/api.md
|
|
@ -1,6 +1,6 @@
|
|||
This document describes how to use the main Anselme modules. This is generated automatically from the source files.
|
||||
|
||||
Note that this file only describes the `anselme` and `state.State` modules, which are only a selection of what I consider to be the "public API" of Anselme that I will try to keep stable.
|
||||
Note that this file only describes the `anselme` and `state.State` modules, as well as the `TextEventData` and `ChoiceEventData` classes, which are only a selection of what I consider to be the "public API" of Anselme that I will try to keep stable.
|
||||
If you need more advanced control on Anselme, feel free to look into the other source files to find more; the most useful functions should all be reasonably commented.
|
||||
|
||||
# anselme
|
||||
|
|
@ -15,25 +15,20 @@ local anselme = require("anselme")
|
|||
local state = anselme.new()
|
||||
state:load_stdlib()
|
||||
|
||||
-- read an anselme script file
|
||||
local f = assert(io.open("script.ans"))
|
||||
local script = anselme.parse(f:read("a"), "script.ans")
|
||||
f:close()
|
||||
|
||||
-- load the script in a new branch
|
||||
-- load an anselme script file in a new branch
|
||||
local run_state = state:branch()
|
||||
run_state:run(script)
|
||||
run_state:run_file("script.ans")
|
||||
|
||||
-- run the script
|
||||
while run_state:active() do
|
||||
local e, data = run_state:step()
|
||||
if e == "text" then
|
||||
for _, l in ipairs(data) do
|
||||
print(l:format(run_state))
|
||||
print(l)
|
||||
end
|
||||
elseif e == "choice" then
|
||||
for i, l in ipairs(data) do
|
||||
print(("%s> %s"):format(i, l:format(run_state)))
|
||||
print(("%s> %s"):format(i, l))
|
||||
end
|
||||
local choice = tonumber(io.read("l"))
|
||||
data:choose(choice)
|
||||
|
|
@ -56,31 +51,31 @@ Anselme expects that `require("anselme.module")` will try loading both `anselme/
|
|||
|
||||
Global version string. Follow semver.
|
||||
|
||||
_defined at line 58 of [anselme/init.lua](../anselme/init.lua):_ `version = "2.0.0-rc1",`
|
||||
_defined at line 53 of [anselme/init.lua](../anselme/init.lua):_ `version = "2.0.0-rc1",`
|
||||
|
||||
### .versions
|
||||
|
||||
Table containing per-category version numbers. Incremented by one for any change that may break compatibility.
|
||||
|
||||
_defined at line 61 of [anselme/init.lua](../anselme/init.lua):_ `versions = {`
|
||||
_defined at line 56 of [anselme/init.lua](../anselme/init.lua):_ `versions = {`
|
||||
|
||||
#### .language
|
||||
|
||||
Version number for language and standard library changes.
|
||||
|
||||
_defined at line 63 of [anselme/init.lua](../anselme/init.lua):_ `language = 31,`
|
||||
_defined at line 58 of [anselme/init.lua](../anselme/init.lua):_ `language = 31,`
|
||||
|
||||
#### .save
|
||||
|
||||
Version number for save/AST format changes.
|
||||
|
||||
_defined at line 65 of [anselme/init.lua](../anselme/init.lua):_ `save = 7,`
|
||||
_defined at line 60 of [anselme/init.lua](../anselme/init.lua):_ `save = 7,`
|
||||
|
||||
#### .api
|
||||
|
||||
Version number for Lua API changes.
|
||||
|
||||
_defined at line 67 of [anselme/init.lua](../anselme/init.lua):_ `api = 10`
|
||||
_defined at line 62 of [anselme/init.lua](../anselme/init.lua):_ `api = 10`
|
||||
|
||||
### .parse (code, source)
|
||||
|
||||
|
|
@ -94,14 +89,14 @@ local ast = anselme.parse("1 + 2", "test")
|
|||
ast:eval(state)
|
||||
```
|
||||
|
||||
_defined at line 79 of [anselme/init.lua](../anselme/init.lua):_ `parse = function(code, source)`
|
||||
_defined at line 74 of [anselme/init.lua](../anselme/init.lua):_ `parse = function(code, source)`
|
||||
|
||||
### .parse_file (path)
|
||||
|
||||
Same as `:parse`, but reads the code from a file.
|
||||
`source` will be set as the file path.
|
||||
|
||||
_defined at line 84 of [anselme/init.lua](../anselme/init.lua):_ `parse_file = function(path)`
|
||||
_defined at line 79 of [anselme/init.lua](../anselme/init.lua):_ `parse_file = function(path)`
|
||||
|
||||
### .generate_translation_template (code, source)
|
||||
|
||||
|
|
@ -109,20 +104,20 @@ Generates and return Anselme code (as a string) that can be used as a base for a
|
|||
This will include every translatable element found in this code.
|
||||
`source` is an optional string; it will be used as the code source name in translation contexts.
|
||||
|
||||
_defined at line 93 of [anselme/init.lua](../anselme/init.lua):_ `generate_translation_template = function(code, source)`
|
||||
_defined at line 88 of [anselme/init.lua](../anselme/init.lua):_ `generate_translation_template = function(code, source)`
|
||||
|
||||
### .generate_translation_template_file (path)
|
||||
|
||||
Same as `:generate_translation_template`, but reads the code from a file.
|
||||
`source` will be set as the file path.
|
||||
|
||||
_defined at line 98 of [anselme/init.lua](../anselme/init.lua):_ `generate_translation_template_file = function(path)`
|
||||
_defined at line 93 of [anselme/init.lua](../anselme/init.lua):_ `generate_translation_template_file = function(path)`
|
||||
|
||||
### .new ()
|
||||
|
||||
Return a new [State](#state).
|
||||
|
||||
_defined at line 102 of [anselme/init.lua](../anselme/init.lua):_ `new = function()`
|
||||
_defined at line 97 of [anselme/init.lua](../anselme/init.lua):_ `new = function()`
|
||||
|
||||
|
||||
# State
|
||||
|
|
@ -249,22 +244,23 @@ Returns `"inactive"` if no script is loaded.
|
|||
|
||||
_defined at line 171 of [anselme/state/State.lua](../anselme/state/State.lua):_ `state = function(self)`
|
||||
|
||||
### :run (code, source)
|
||||
### :run (code, source, tags)
|
||||
|
||||
Load a script in this branch. It will become the active script.
|
||||
|
||||
`code` is the code string or AST to run. If `code` is a string, `source` is the source name string to show in errors (optional).
|
||||
`tags` is an optional Lua table; its content will be added to the tags for the duration of the script.
|
||||
|
||||
Note that this will only load the script; execution will only start by using the `:step` method. Will error if a script is already active in this State.
|
||||
|
||||
_defined at line 183 of [anselme/state/State.lua](../anselme/state/State.lua):_ `run = function(self, code, source)`
|
||||
_defined at line 184 of [anselme/state/State.lua](../anselme/state/State.lua):_ `run = function(self, code, source, tags)`
|
||||
|
||||
### :run_file (path)
|
||||
### :run_file (path, tags)
|
||||
|
||||
Same as `:run`, but read the code from a file.
|
||||
`source` will be set as the file path.
|
||||
|
||||
_defined at line 194 of [anselme/state/State.lua](../anselme/state/State.lua):_ `run_file = function(self, path)`
|
||||
_defined at line 195 of [anselme/state/State.lua](../anselme/state/State.lua):_ `run_file = function(self, path, tags)`
|
||||
|
||||
### :step ()
|
||||
|
||||
|
|
@ -274,42 +270,218 @@ Will error if no script is active.
|
|||
|
||||
Returns `event type string, event data`.
|
||||
|
||||
_defined at line 205 of [anselme/state/State.lua](../anselme/state/State.lua):_ `step = function(self)`
|
||||
See the [events](#events) section for details on event data types for built-in events.
|
||||
|
||||
### :interrupt (code, source)
|
||||
_defined at line 208 of [anselme/state/State.lua](../anselme/state/State.lua):_ `step = function(self)`
|
||||
|
||||
### :interrupt (code, source, tags)
|
||||
|
||||
Stops the currently active script.
|
||||
|
||||
Will error if no script is active.
|
||||
|
||||
If `code` is given, the script will not be disabled but instead will be immediately replaced with this new script.
|
||||
`code`, `source` and `tags` are all optional and have the same behaviour as in `:run`.
|
||||
If they are given, the script will not be disabled but instead will be immediately replaced with this new script.
|
||||
The new script will then be started on the next `:step` and will preserve the current scope. This can be used to trigger an exit function or similar in the active script.
|
||||
|
||||
If this is called from within a running script, this will raise an `interrupt` event in order to stop the current script execution.
|
||||
|
||||
_defined at line 225 of [anselme/state/State.lua](../anselme/state/State.lua):_ `interrupt = function(self, code, source)`
|
||||
_defined at line 229 of [anselme/state/State.lua](../anselme/state/State.lua):_ `interrupt = function(self, code, source, tags)`
|
||||
|
||||
### :eval (code, source)
|
||||
### :eval (code, source, tags)
|
||||
|
||||
Evaluate an expression in the global scope.
|
||||
|
||||
This can be called from outside a running script, but an error will be triggered the expression raise any event other than return.
|
||||
|
||||
`code` is the code string or AST to run. If `code` is a string, `source` is the source name string to show in errors (optional).
|
||||
`tags` is an optional Lua table; its content will be added to the tags for the duration of the expression.
|
||||
|
||||
* returns AST in case of success. Run `:to_lua(state)` on it to convert to a Lua value.
|
||||
* returns `nil, error message` in case of error.
|
||||
|
||||
_defined at line 249 of [anselme/state/State.lua](../anselme/state/State.lua):_ `eval = function(self, code, source)`
|
||||
_defined at line 256 of [anselme/state/State.lua](../anselme/state/State.lua):_ `eval = function(self, code, source, tags)`
|
||||
|
||||
### :eval_local (code, source)
|
||||
### :eval_local (code, source, tags)
|
||||
|
||||
Same as `:eval`, but evaluate the expression in the current scope.
|
||||
|
||||
_defined at line 256 of [anselme/state/State.lua](../anselme/state/State.lua):_ `eval_local = function(self, code, source)`
|
||||
_defined at line 263 of [anselme/state/State.lua](../anselme/state/State.lua):_ `eval_local = function(self, code, source, tags)`
|
||||
|
||||
If you want to perform more advanced manipulation of the resulting AST nodes, look at the `ast` modules.
|
||||
In particular, every Node inherits the methods from [ast.abstract.Node](../ast/abstract/Node.lua).
|
||||
Otherwise, each Node has its own module file defined in the [ast/](../ast) directory.
|
||||
|
||||
|
||||
# Events
|
||||
|
||||
Anselme scripts communicate with the game by sending events. See the [language documentation](language.md#events) for more details on events.
|
||||
|
||||
Custom events can be defined; to do so, simply yield the coroutine with your custom event type (using `coroutine.yield("event type", event_data)`) from a function called in the anselme script.
|
||||
|
||||
For example, to add a `wait` event that pauses the script for some time, you could do something along these lines:
|
||||
```lua
|
||||
state:define("wait", "(duration::is number)", function(duration) coroutine.yield("wait", duration) end)
|
||||
waiting = false
|
||||
|
||||
-- and edit your Anselme event handler with something like:
|
||||
if not waiting then
|
||||
local event_type, event_data = run_state = run_state:step()
|
||||
if e == "wait" then
|
||||
waiting = true
|
||||
call_after_duration(event_data, function() waiting = false end)
|
||||
else
|
||||
-- handle other event types...
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
And then from your Anselme script:
|
||||
```
|
||||
| Hello...
|
||||
---
|
||||
_file generated at 2024-11-09T17:00:43Z_
|
||||
wait(5)
|
||||
| ...world !
|
||||
```
|
||||
|
||||
## TextEventData
|
||||
|
||||
TextEventData represent the data returned by an event with the type `"text"`.
|
||||
See the [language documentation](language.md#texts) for more details on how to create a text event.
|
||||
|
||||
A TextEventData contains a list of [LuaText](#luatext), each LuaText representing a separate line of the text event.
|
||||
|
||||
For example, the following Anselme script:
|
||||
|
||||
```
|
||||
| Hi!
|
||||
| My name's John.
|
||||
```
|
||||
will return a text event containing two LuaTexts, the first containing the text "Hi!" and the second "My name's John.".
|
||||
|
||||
Usage:
|
||||
```lua
|
||||
local event_type, event_data = run_state:step()
|
||||
if event_type == "text" then
|
||||
-- event_data is a TextEventData, i.e. a list of LuaText
|
||||
for _, luatext in ipairs(event_data) do
|
||||
-- luatext is a list of text parts { text = "text string", tags = { ... } }
|
||||
for _, textpart in ipairs(luatext) do
|
||||
write_text_part_with_color(textpart.text, textpart.tags.color)
|
||||
end
|
||||
write_text("\n") -- for example, if we want a newline between each text line
|
||||
end
|
||||
else
|
||||
-- handle other event types...
|
||||
end
|
||||
```
|
||||
|
||||
_defined at line 74 of [anselme/ast/Text.lua](../anselme/ast/Text.lua):_ `local TextEventData`
|
||||
|
||||
### :group_by (tag_name)
|
||||
|
||||
Returns a list of TextEventData where the first part of each LuaText of each TextEventData has the same value for the tag `tag_name`.
|
||||
|
||||
In other words, this groups all the LuaTexts contained in this TextEventData using the `tag_name` tag and returns a list containing these groups.
|
||||
|
||||
For example, with the following Anselme script:
|
||||
```
|
||||
speaker: "John" #
|
||||
| A
|
||||
| B
|
||||
speaker: "Lana" #
|
||||
| C
|
||||
speaker: "John" #
|
||||
| D
|
||||
```
|
||||
calling `text_event_data:group_by("speaker")` will return a list of three TextEventData:
|
||||
* the first with the texts "A" and "B"; both with the tag `speaker="John"`
|
||||
* the second with the text "C"; with the tag `speaker="Lana"`
|
||||
* the last with the text "D"; wiith the tag `speaker="John"`
|
||||
|
||||
_defined at line 96 of [anselme/ast/Text.lua](../anselme/ast/Text.lua):_ `group_by = function(self, tag_name)`
|
||||
|
||||
|
||||
## ChoiceEventData
|
||||
|
||||
ChoiceEventData represent the data returned by an event with the type `"choice"`.
|
||||
See the [language documentation](language.md#choices) for more details on how to create a choice event.
|
||||
|
||||
A ChoiceEventData contains a list of [LuaText](#luatext), each LuaText representing a separate choice of the choice event.
|
||||
|
||||
For example, the following Anselme script:
|
||||
|
||||
```
|
||||
*| Yes!
|
||||
*| No.
|
||||
```
|
||||
will return a choice event containing two LuaTexts, the first containing the text "Yes!" and the second "No.".
|
||||
|
||||
Usage:
|
||||
```lua
|
||||
current_choice = nil
|
||||
waiting_for_choice = false
|
||||
|
||||
-- in your anselem event handling loop:
|
||||
if not waiting_for_choice then
|
||||
local event_type, event_data = run_state:step()
|
||||
if event_type == "choice" then
|
||||
-- event_data is a ChoiceEventData, i.e. a list of LuaText
|
||||
for i, luatext in ipairs(event_data) do
|
||||
write(("Choice number %s:"):format(i))
|
||||
-- luatext is a list of text parts { text = "text string", tags = { ... } }
|
||||
for _, textpart in ipairs(luatext) do
|
||||
write_choice_part_with_color(textpart.text, textpart.tags.color)
|
||||
end
|
||||
else
|
||||
-- handle other event types...
|
||||
end
|
||||
current_choice = event_data
|
||||
waiting_for_choice = true
|
||||
end
|
||||
end
|
||||
|
||||
-- somewhere in your code where choices are selected
|
||||
current_choice:select(choice_number)
|
||||
waiting_for_choice = false
|
||||
```
|
||||
|
||||
_defined at line 50 of [anselme/ast/Choice.lua](../anselme/ast/Choice.lua):_ `local ChoiceEventData = class {`
|
||||
|
||||
### :choose (choice)
|
||||
|
||||
Choose the choice at position `choice` (number).
|
||||
|
||||
A choice must be selected after receiving a choice event and before calling `:step` again.
|
||||
|
||||
_defined at line 58 of [anselme/ast/Choice.lua](../anselme/ast/Choice.lua):_ `choose = function(self, choice)`
|
||||
|
||||
|
||||
## LuaText
|
||||
|
||||
A Lua-friendly representation of an Anselme Text value.
|
||||
They appear in both TextEventData and ChoiceEventData to represent the text that has to be shown.
|
||||
|
||||
It contains a list of _text parts_, which are parts of a single text, each part potentially having differrent tags attached.
|
||||
A text will typically only consist of a single part unless it was built using text interpolation.
|
||||
|
||||
Each text part is a table containing `text` (string) and `tags` (table) properties, for example: `{ text = "text part string", tags = { color = "red" } }`.
|
||||
|
||||
_defined at line 17 of [anselme/ast/Text.lua](../anselme/ast/Text.lua):_ `local LuaText`
|
||||
|
||||
### .raw
|
||||
|
||||
Anselme Text value this was created from. For advanced usage only. See the source file [Text.lua](anselme/ast/Text.lua) for more information.
|
||||
|
||||
_defined at line 25 of [anselme/ast/Text.lua](../anselme/ast/Text.lua):_ `raw = nil,`
|
||||
|
||||
### :__tostring ()
|
||||
|
||||
Returns a text representation of the LuaText, using Anselme's default formatting. Useful for debugging.
|
||||
|
||||
Usage: `print(luatext)`
|
||||
|
||||
_defined at line 39 of [anselme/ast/Text.lua](../anselme/ast/Text.lua):_ `__tostring = function(self)`
|
||||
|
||||
---
|
||||
_file generated at 2024-11-11T13:33:43Z_
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
This document describes how to use the main Anselme modules. This is generated automatically from the source files.
|
||||
|
||||
Note that this file only describes the `anselme` and `state.State` modules, which are only a selection of what I consider to be the "public API" of Anselme that I will try to keep stable.
|
||||
Note that this file only describes the `anselme` and `state.State` modules, as well as the `TextEventData` and `ChoiceEventData` classes, which are only a selection of what I consider to be the "public API" of Anselme that I will try to keep stable.
|
||||
If you need more advanced control on Anselme, feel free to look into the other source files to find more; the most useful functions should all be reasonably commented.
|
||||
|
||||
# anselme
|
||||
|
|
@ -10,3 +10,40 @@ If you need more advanced control on Anselme, feel free to look into the other s
|
|||
# State
|
||||
|
||||
{{anselme/state/State.lua}}
|
||||
|
||||
# Events
|
||||
|
||||
Anselme scripts communicate with the game by sending events. See the [language documentation](language.md#events) for more details on events.
|
||||
|
||||
Custom events can be defined; to do so, simply yield the coroutine with your custom event type (using `coroutine.yield("event type", event_data)`) from a function called in the anselme script.
|
||||
|
||||
For example, to add a `wait` event that pauses the script for some time, you could do something along these lines:
|
||||
```lua
|
||||
state:define("wait", "(duration::is number)", function(duration) coroutine.yield("wait", duration) end)
|
||||
waiting = false
|
||||
|
||||
-- and edit your Anselme event handler with something like:
|
||||
if not waiting then
|
||||
local event_type, event_data = run_state = run_state:step()
|
||||
if e == "wait" then
|
||||
waiting = true
|
||||
call_after_duration(event_data, function() waiting = false end)
|
||||
else
|
||||
-- handle other event types...
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
And then from your Anselme script:
|
||||
```
|
||||
| Hello...
|
||||
---
|
||||
wait(5)
|
||||
| ...world !
|
||||
```
|
||||
|
||||
{{anselme/ast/Text.lua}}
|
||||
|
||||
{{anselme/ast/Choice.lua}}
|
||||
|
||||
{{:lua text}}
|
||||
|
|
@ -887,6 +887,8 @@ A text event value can be created using the [text literal](#text). A text is wri
|
|||
text!
|
||||
```
|
||||
|
||||
How the data returned by a Text event is structured is detailled in the [API documentation](api.md#texteventdata).
|
||||
|
||||
### Choices
|
||||
|
||||
Choice events are intended to represent a player choice in the host game. Each choice event in the buffer list is intended to represent a distinct choice.
|
||||
|
|
@ -902,6 +904,8 @@ A choice event can be written to the buffer using the `*_` operator on a text ev
|
|||
| Choice B has been selected.
|
||||
```
|
||||
|
||||
How the data returned by a Text event is structured is detailled in the [API documentation](api.md#choiceeventdata).
|
||||
|
||||
### Tags
|
||||
|
||||
Text and choice events can also carry metadata through tags. Tags are stored as a [struct](#struct).
|
||||
|
|
|
|||
|
|
@ -38,14 +38,14 @@ local function run_loop(run_state, write_output, interactive)
|
|||
for _, v in ipairs(grouped) do
|
||||
if groups then write_output(":: group ::") end
|
||||
for _, l in ipairs(v) do
|
||||
write_output(l:format(run_state))
|
||||
write_output(l.raw:format(run_state))
|
||||
end
|
||||
end
|
||||
elseif e == "choice" then
|
||||
local choice
|
||||
if interactive then
|
||||
for i, l in ipairs(data) do
|
||||
write_output(("%s> %s"):format(i, l:format(run_state)))
|
||||
write_output(("%s> %s"):format(i, l.raw:format(run_state)))
|
||||
end
|
||||
io.write(("Select choice (1-%s): "):format(#data))
|
||||
choice = tonumber(io.read("l"))
|
||||
|
|
@ -53,9 +53,9 @@ local function run_loop(run_state, write_output, interactive)
|
|||
choice = assert(run_state:eval_local("choice"), "no choice selected"):to_lua()
|
||||
for i, l in ipairs(data) do
|
||||
if i == choice then
|
||||
write_output(("=> %s"):format(l:format(run_state)))
|
||||
write_output(("=> %s"):format(l.raw:format(run_state)))
|
||||
else
|
||||
write_output((" > %s"):format(l:format(run_state)))
|
||||
write_output((" > %s"):format(l.raw:format(run_state)))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue