1
0
Fork 0
mirror of https://github.com/Reuh/anselme.git synced 2025-10-27 08:39:30 +00:00

Updated documentation; added Lua API documentation

This commit is contained in:
Étienne Fildadut 2022-01-16 23:37:35 +01:00
parent 933e8fb0ee
commit 69b9e17020
7 changed files with 566 additions and 129 deletions

6
API.md
View file

@ -1,6 +0,0 @@
Lua API reference
=================
We actively support LuaJIT and Lua 5.4. Lua 5.1, 5.2 and 5.3 *should* work but I don't always test against them.
TODO see anselme.lua, it is reasonably commentated and we should probably generate some documentation using LDoc.

View file

@ -442,9 +442,9 @@ Anselme need to give back control to the game at some point. This is done throug
Each event is composed of two elements: a type (string; `text`, `choice`, `return` or `error` by default, custom types can also be defined) and associated data; the data associated with each event depends on its type. For the default events this data is:
* `text` (text to display) is a list of text elements, each with a `text` field, containing the text contents, and a `tags` field, containing the tags associated with this text.
* `choice` (choices to choose from) is a list of tableas, each associated to a choice. Each of these choice is a list of text elements like for the `text` event.
* `choice` (choices to choose from) is a list of choices. Each of these choice is a list of text elements like for the `text` event.
* `return` (when the script ends) is the returned value.
* `error` (when the script error) is the error message.
* `error` (when there is an error) is the error message.
#### Event buffer

View file

@ -1,4 +1,4 @@
Copyright 2019-2021 Étienne "Reuh" Fildadut
Copyright 2019-2022 Étienne "Reuh" Fildadut
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.

View file

@ -3,7 +3,7 @@ Anselme
The overengineered dialog scripting system in pure Lua.
**Documentation and language are still WIP and will change. I am using this in a project and modify it as my needs change.**
Whatever is on the master branch should work fine. **Documentation and language are still WIP and will change. I am using this in a project and modify it as my needs change.** Breaking changes are documented in commit messages.
Purpose
-------
@ -28,15 +28,16 @@ And most stuff you'd expect from such a language:
* can save and restore state
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)
Defaults variables use emoji and then it's expected to alias them; works but not the most satisfying solution.
* a good documentation
Need to work on consistent naming of Anselme concepts
A step by step tutorial
* 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.
* a good documentation (need to work on consistent naming of Anselme concepts, a step by step tutorial)
Things that Anselme is not:
* a game engine. It's very specific to dialogs and text, so unless you make a text game you will need to do a lot of other stuff.
* a language based on Lua. It's imperative and arrays start at 1 but there's not much else in common.
* a high-performance language. No, really, I didn't even try to make anything fast, so don't use Anselme to compute primes.
Example
-------
@ -70,4 +71,4 @@ Reference
See [LANGUAGE.md](LANGUAGE.md) for a reference of the language.
See [API.md](API.md) for the Lua API's documentation.
See [anselme.md](anselme.md) for the Lua API's documentation.

View file

@ -1,17 +1,69 @@
-- anselme module
--- anselme main module
--- Anselme Lua API reference
--
-- We actively support LuaJIT and Lua 5.4. Lua 5.1, 5.2 and 5.3 *should* work but I don't always test against them.
--
-- This documentation is generated from the main module file `anselme.lua` using `ldoc --ext md anselme.lua`.
--
-- Example usage:
-- ```lua
-- local anselme = require("anselme") -- load main module
--
-- local vm = anselme() -- create new VM
-- vm:loadgame("game") -- load some scripts, etc.
-- local interpreter = vm:rungame() -- create a new interpreter using what was loaded with :loadgame
--
-- -- simple function to convert text event data into a string
-- -- in your game you may want to handle tags, here we ignore them for simplicity
-- local function format_text(text)
-- local r = ""
-- for _, l in ipairs(t) do
-- r = r .. l.text
-- end
-- return r
-- end
--
-- -- event loop
-- repeat
-- local event, data = interpreter:step() -- progress script until next event
-- if event == "text" then
-- print(format_text(d))
-- elseif event == "choice" then
-- for j, choice in ipairs(d) do
-- print(j.."> "..format_text(choice))
-- end
-- interpreter:choose(io.read())
-- elseif event == "error" then
-- error(data)
-- end
-- until t == "return" or t == "error"
-- ```
--
-- Calling the Anselme main module will create a return a new [VM](#vms).
--
-- The main module also contain a few fields:
--
-- @type anselme
local anselme = {
--- version
-- save is incremented a each update which may break save compatibility
-- language is incremented a each update which may break script file compatibility
-- api is incremented a each update which may break Lua API compatibility
--- Anselme version information table.
--
-- Contains version informations as number (higher means more recent) of Anselme divied in a few categories:
--
-- * `save`, which is incremented at each update which may break save compatibility
-- * `language`, which is incremented at each update which may break script file compatibility
-- * `api`, which is incremented at each update which may break Lua API compatibility
versions = {
save = 1,
language = 22,
api = 5
},
--- version is incremented at each update
--- General version number.
--
-- It is incremented at each update.
version = 23,
--- currently running interpreter
--- Currently running [interpreter](#interpreters).
-- `nil` if no interpreter running.
running = nil
}
package.loaded[...] = anselme
@ -62,19 +114,43 @@ local function is_file(path)
end
end
--- interpreter methods
--- Interpreters
--
-- An interpreter is in charge of running Anselme code and is spawned from a [VM](#vms).
-- Several interpreters from the same VM can run at the same time.
--
-- Typically, you would have a interpreter for each script that need at the same time, for example one for every NPC
-- that is currently talking.
--
-- Each interpreter can only run one script at a time, and will run it sequentially.
-- You can advance in the script by calling the `:step` method, which will run the script until an event is sent (for example some text needs to be displayed),
-- which will pause the whole interpreter until `:step` is called again.
--
-- @type interpreter
local interpreter_methods = {
--- interpreter state
-- for internal use, you shouldn't touch this
-- @local
state = nil,
--- VM this interpreter belongs to
--- [VM](#vms) this interpreter belongs to.
vm = nil,
--- event that stopped the interpreter
--- String, type of the event that stopped the interpreter (`nil` if interpreter is still running).
end_event = nil,
--- run the VM until the next event
-- will merge changed variables on successful script end
-- returns event, data; if event is "return" or "error", the interpreter can not be stepped further
--- Run the interpreter until the next event.
-- Returns event type (string), data (any).
--
-- Will merge changed variables on successful script end.
--
-- If event is `"return"` or `"error"`, the interpreter can not be stepped further and should be discarded.
--
-- Default event types and their associated data:
-- * `text`: text to display, data is a list of text elements, each with a `text` field, containing the text contents, and a `tags` field, containing the tags associated with this text
-- * `choice`: choices to choose from, data is a list of choices Each of these choice is a list of text elements like for the `text` event
-- * `return`: when the script ends, data is the returned value (`nil` if nothing returned)
-- * `error`: when there is an error, data is the error message.
--
-- See [LANGUAGE.md](LANGUAGE.md) for more details on events.
step = function(self)
-- check status
if self.end_event then
@ -110,21 +186,26 @@ local interpreter_methods = {
return event, data
end,
--- select an answer
-- returns self
--- Select a choice.
-- `i` is the index (number) of the choice in the choice list (from the choice event's data).
--
-- The choice will be selected on the next interpreter step.
--
-- Returns this interpreter.
choose = function(self, i)
self.state.interpreter.choice_selected = tonumber(i)
return self
end,
--- interrupt the vm on the next step, executing an expression (if specified) in the current namespace
-- returns self
--- Interrupt (abort the currently running script) the interpreter on the next step, executing an expression (string, if specified) in the current namespace instead.
--
-- Returns this interpreter.
interrupt = function(self, expr)
self.state.interpreter.interrupt = expr or true
return self
end,
--- search closest namespace from last run line
--- Returns the namespace (string) the last ran line belongs to.
current_namespace = function(self)
local line = self.state.interpreter.running_line
local namespace = ""
@ -141,9 +222,12 @@ local interpreter_methods = {
return namespace
end,
--- run an expression or block: may trigger events and must be called from within the interpreter coroutine
-- no automatic merge if this change the interpreter state, merge is done once we reach end of script in a call to :step as usual
-- return lua value (nil if nothing returned)
--- Run an expression (string) or block, optionally in a specific namespace (string, will use root namespace if not specified).
-- This may trigger events and must be called from within the interpreter coroutine (i.e. from a function called from a running script).
--
-- No automatic merge if this change the interpreter state, merge is done once we reach end of script in a call to `:step` as usual.
--
-- Returns the returned value (nil if nothing returned).
run = function(self, expr, namespace)
-- check status
if coroutine.status(self.state.interpreter.coroutine) ~= "running" then
@ -168,12 +252,15 @@ local interpreter_methods = {
end
return to_lua(r)
end,
--- evaluate an expression or block
-- can be called from outside the coroutine. Will create a new coroutine that operate on this interpreter state.
-- no automatic merge if this change the interpreter state, merge is done once we reach end of script in a call to :step as usual
-- the expression can't yield events
-- return value in case of success (nil if nothing returned)
-- return nil, err in case of error
--- Evaluate an expression (string) or block, optionally in a specific namespace (string, will use root namespace if not specified).
-- The expression can't yield events.
-- Can be called from outside the interpreter coroutine. Will create a new coroutine that operate on this interpreter state.
--
-- No automatic merge if this change the interpreter state, merge is done once we reach end of script in a call to `:step` as usual.
--
-- Returns the returned value in case of success (nil if nothing returned).
--
-- Returns nil, error message in case of error.
eval = function(self, expr, namespace)
if self.end_event then
return "error", ("interpreter can't be restarted after receiving a %s event"):format(self.end_event)
@ -211,33 +298,43 @@ local interpreter_methods = {
}
interpreter_methods.__index = interpreter_methods
--- vm methods
--- VMs
--
-- A VM stores the state required to run Anselme scripts. Each VM is completely independant from each other.
--
-- @type vm
local vm_mt = {
--- anselme state
-- for internal use, you shouldn't touch this
-- @local
state = nil,
--- loaded game state
-- for internal use, you shouldn't touch this
-- @local
game = nil,
--- wrapper for loading a whole set of scripts
-- should be preferred to other loading functions if possible
-- requires LÖVE or LuaFileSystem
-- will load in path, in order:
-- * config.ans, which will be executed in the "config" namespace and may contains various optional configuration options:
-- * anselme version: number, version of the anselme language this game was made for
-- * game version: any, version information of the game. Can be used to perform eventual migration of save with an old version in the main file.
--- Wrapper for loading a whole set of scripts (a "game").
-- Should be preferred to other loading functions if possible as this sets all the common options on its own.
--
-- Requires LÖVE or LuaFileSystem.
--
-- Will load from the directory given by `path` (string), in order:
-- * `config.ans`, which will be executed in the "config" namespace and may contains various optional configuration options:
-- * `anselme version`: number, version of the anselme language this game was made for
-- * `game version`: any, version information of the game. Can be used to perform eventual migration of save with an old version in the main file.
-- Always included in saved variables.
-- * language: string, built-in language file to load
-- * inject directory: string, directory that may contain "function start.ans", "checkpoint end.ans", etc. which content will be used to setup
-- * `language`: string, built-in language file to load
-- * `inject directory`: string, directory that may contain "function start.ans", "checkpoint end.ans", etc. which content will be used to setup
-- the custom code injection methods (see vm:setinjection)
-- * global directory: string, path of global script directory. Every script file and subdirectory in the path will be loaded in the global namespace.
-- * start expression: string, expression that will be ran when starting the game
-- * main file, if defined in config.ans
-- * `global directory`: string, path of global script directory. Every script file and subdirectory in the path will be loaded in the global namespace.
-- * `start expression`: string, expression that will be ran when starting the game
-- * files in the global directory, if defined in config.ans
-- * every other file in the path and subdirectories, using their path as namespace (i.e., contents of path/world1/john.ans will be defined in a function world1.john)
-- returns self in case of success
-- returns nil, err in case of error
--
-- Returns this VM in case of success.
--
-- Returns nil, error message in case of error.
loadgame = function(self, path)
if self.game then error("game already loaded") end
-- load config
@ -307,9 +404,11 @@ local vm_mt = {
end
return self
end,
--- return a interpreter which runs the game main file
-- return interpreter in case of success
-- returns nil, err in case of error
--- Return a interpreter which runs the game start expression (if given).
--
-- Returns interpreter in case of success.
--
-- Returns nil, error message in case of error.
rungame = function(self)
if not self.game then error("no game loaded") end
if self.game.start_expression then
@ -319,16 +418,21 @@ local vm_mt = {
end
end,
--- load code
-- similar to Lua's code loading functions.
-- name(default=""): namespace to load the code in. Will define a new function is specified; otherwise, code will be parsed but not executable from an expression.
-- return parsed block in case of success
-- returns nil, err in case of error
--- Load code from a string.
-- Similar to Lua's code loading functions.
--
-- Compared to their Lua equivalents, these also take an optional `name` argument (default="") that set the namespace to load the code in. Will define a new function is specified; otherwise, code will be parsed but not executable from an expression (as it is not named).
--
-- Returns parsed block in case of success.
--
-- Returns nil, error message in case of error.
loadstring = function(self, str, name, source)
local s, e = preparse(self.state, str, name or "", source)
if not s then return s, e end
return s
end,
--- Load code from a file.
-- See `vm:loadstring`.
loadfile = function(self, path, name)
local content
if love then
@ -345,10 +449,13 @@ local vm_mt = {
if not s then return s, err end
return s
end,
-- load every file in a directory, using filename (without .ans extension) as its namespace
-- requires LÖVE or LuaFileSystem
-- return self in case of success
-- returns nil, err in case of error
-- Load every file in a directory, using filename (without .ans extension) as its namespace.
--
-- Requires LÖVE or LuaFileSystem.
--
-- Returns this VM in case of success.
--
-- Returns nil, error message in case of error.
loaddirectory = function(self, path, name)
if not name then name = "" end
name = name == "" and "" or name.."."
@ -367,42 +474,49 @@ local vm_mt = {
return self
end,
--- set aliases for built-in variables 👁️, 🔖 and 🏁 that will be defined on every new checkpoint and function
-- this does not affect variables that were defined before this function was called
-- nil for no alias
-- return self
--- Set aliases for built-in variables 👁️, 🔖 and 🏁 that will be defined on every new checkpoint and function.
-- This does not affect variables that were defined before this function was called.
-- Set to nil for no alias.
--
-- Returns this VM.
setaliases = function(self, seen, checkpoint, reached)
self.state.builtin_aliases["👁️"] = seen
self.state.builtin_aliases["🔖"] = checkpoint
self.state.builtin_aliases["🏁"] = reached
return self
end,
--- set some code that will be injected at specific places in all code loaded after this is called
-- possible inject types:
-- * "function start": injected at the start of every non-scoped function
-- * "function end": injected at the end of every non-scoped function
-- * "function return": injected at the end of each return's children that is contained in a non-scoped function
-- * "checkpoint start": injected at the start of every checkpoint
-- * "checkpoint end": injected at the end of every checkpoint
-- * "scoped function start": injected at the start of every scoped function
-- * "scoped function end": injected at the end of every scoped function
-- * "scoped function return": injected at the end of each return's children that is contained in a scoped function
-- set to nil to disable
-- can typically be used to define variables for every function like 👁️, setting some value on every function resume, etc.
-- return self
--- Set some code that will be injected at specific places in all code loaded after this is called.
-- Can typically be used to define variables for every function like 👁️, setting some value on every function resume, etc.
--
-- Possible inject types:
-- * `"function start"`: injected at the start of every non-scoped function
-- * `"function end"`: injected at the end of every non-scoped function
-- * `"function return"`: injected at the end of each return's children that is contained in a non-scoped function
-- * `"checkpoint start"`: injected at the start of every checkpoint
-- * `"checkpoint end"`: injected at the end of every checkpoint
-- * `"scoped function start"`: injected at the start of every scoped function
-- * `"scoped function end"`: injected at the end of every scoped function
-- * `"scoped function return"`: injected at the end of each return's children that is contained in a scoped function
--
-- Set `code` to nil to disable the inject.
--
-- Returns this VM.
setinjection = function(self, inject, code)
assert(injections[inject], ("unknown injection type %q"):format(inject))
self.state.inject[injections[inject]] = code
return self
end,
--- load & execute a built-in language file
-- the language file may optionally contain the special variables:
--- Load and execute a built-in language file.
--
-- The language file may optionally contain the special variables:
-- * alias 👁️: string, default alias for 👁️
-- * alias 🏁: string, default alias for 🏁
-- * alias 🔖: string, default alias for 🔖
-- return self in case of success
-- returns nil, err in case of error
--
-- Returns this VM in case of success.
--
-- Returns nil, error message in case of error.
loadlanguage = function(self, lang)
local namespace = "anselme.languages."..lang
-- execute language file
@ -419,10 +533,12 @@ local vm_mt = {
return self
end,
--- define functions from Lua
-- signature: full signature of the function
-- fn: function (Lua function or table, see examples in stdlib/functions.lua)
-- return self
--- Define functions from Lua.
--
-- * `signature`: string, full signature of the function
-- * `fn`: function (Lua function or table, see examples in `stdlib/functions.lua`)
--
-- Returns this VM.
loadfunction = function(self, signature, fn)
if type(signature) == "table" then
for k, v in pairs(signature) do
@ -440,10 +556,13 @@ local vm_mt = {
return self
end,
--- save/load script state
-- only saves variables full names and values, so make sure to not change important variables, checkpoints and functions names between a save and a load
-- only save variables with usable identifiers, so will skip functions with arguments, operators, etc.
-- loading should be after loading scripts (otherwise you will "variable already defined" errors)
--- Save/load script state
--
-- Only saves variables full names and values, so make sure to not change important variables, checkpoints and functions names between a save and a load.
-- Also only save variables with usable identifiers, so will skip functions with arguments, operators, etc. (i.e. every scoped functions).
-- Loading should be done after loading all the game scripts (otherwise you will "variable already defined" errors).
--
-- Returns this VM.
load = function(self, data)
assert(anselme.versions.save == data.anselme.versions.save, ("trying to load data from an incompatible version of Anselme; save was done using save version %s but current version is %s"):format(data.anselme.versions.save, anselme.versions.save))
for k, v in pairs(data.variables) do
@ -451,6 +570,10 @@ local vm_mt = {
end
return self
end,
--- Save script state.
-- See `vm:load`.
--
-- Returns save data.
save = function(self)
local vars = {}
for k, v in pairs(self.state.variables) do
@ -467,10 +590,12 @@ local vm_mt = {
}
end,
--- perform parsing that needs to be done after loading code
-- automatically ran before starting an interpreter, but you may want to execute it before if you want to check for parsing error manually
-- returns self in case of success
-- returns nil, err in case of error
--- Perform parsing that needs to be done after loading code.
-- This is automatically ran before starting an interpreter, but you may want to execute it before if you want to check for parsing error manually.
--
-- Returns self in case of success.
--
-- Returns nil, error message in case of error
postload = function(self)
if #self.state.queued_lines > 0 then
local r, e = postparse(self.state)
@ -479,19 +604,20 @@ local vm_mt = {
return self
end,
--- enable feature flags
-- available flags:
-- * "strip trailing spaces": remove trailing spaces from choice and text events (enabled by default)
-- * "strip duplicate spaces": remove duplicated spaces between text elements from choice and text events (enabled by default)
-- returns self
--- Enable feature flags.
-- Available flags:
-- * `"strip trailing spaces"`: remove trailing spaces from choice and text events (enabled by default)
-- * `"strip duplicate spaces"`: remove duplicated spaces between text elements from choice and text events (enabled by default)
--
-- Returns this VM.
enable = function(self, ...)
for _, flag in ipairs{...} do
self.state.feature_flags[flag] = true
end
return self
end,
--- disable features flags
-- returns self
--- Disable features flags.
-- Returns this VM.
disable = function(self, ...)
for _, flag in ipairs{...} do
self.state.feature_flags[flag] = nil
@ -499,13 +625,16 @@ local vm_mt = {
return self
end,
--- run code
-- expr: expression to evaluate (string or parsed expression), or a block to run
-- will merge state after successful execution
-- namespace(default=""): namespace to evaluate the expression in
-- tags(default={}): defaults tags when evaluating the expression (Lua value)
-- return interpreter in case of success
-- returns nil, err in case of error
--- Run code.
-- Will merge state after successful execution
--
-- * `expr`: expression to evaluate (string or parsed expression), or a block to run
-- * `namespace`(default=""): namespace to evaluate the expression in
-- * `tags`(default={}): defaults tags when evaluating the expression (Lua value)
--
-- Return interpreter in case of success.
--
-- Returns nil, error message in case of error.
run = function(self, expr, namespace, tags)
local s, e = self:postload()
if not s then return s, e end
@ -565,14 +694,17 @@ local vm_mt = {
}
return setmetatable(interpreter, interpreter_methods)
end,
--- eval code
-- behave like :run, except the expression can not emit events and will return the result of the expression directly.
-- merge state after sucessful execution automatically like :run
-- expr: expression to evaluate (string or parsed expression), or a block to evaluate
-- namespace(default=""): namespace to evaluate the expression in
-- tags(default={}): defaults tags when evaluating the expression (Lua value)
-- return value in case of success (nil if nothing returned)
-- returns nil, err in case of error
--- Evaluate code.
-- Behave like `:run`, except the expression can not emit events and will return the result of the expression directly.
-- Merge state after sucessful execution automatically like `:run`.
--
-- * `expr`: expression to evaluate (string or parsed expression), or a block to evaluate
-- * `namespace`(default=""): namespace to evaluate the expression in
-- * `tags`(default={}): defaults tags when evaluating the expression (Lua value)
--
-- Return value in case of success (nil if nothing returned).
--
-- Returns nil, error message in case of error.
eval = function(self, expr, namespace, tags)
local interpreter, err = self:run("()", namespace, tags)
if not interpreter then return interpreter, err end
@ -584,7 +716,7 @@ local vm_mt = {
}
vm_mt.__index = vm_mt
--- anselme module
-- return anselme module
return setmetatable(anselme, {
__call = function()
-- global state

313
anselme.md Normal file
View file

@ -0,0 +1,313 @@
## Anselme Lua API reference
We actively support LuaJIT and Lua 5.4. Lua 5.1, 5.2 and 5.3 *should* work but I don't always test against them.
This documentation is generated from the main module file `anselme.lua` using `ldoc --ext md anselme.lua`.
Example usage:
```lua
local anselme = require("anselme") -- load main module
local vm = anselme() -- create new VM
vm:loadgame("game") -- load some scripts, etc.
local interpreter = vm:rungame() -- create a new interpreter using what was loaded with :loadgame
-- simple function to convert text event data into a string
-- in your game you may want to handle tags, here we ignore them for simplicity
local function format_text(text)
local r = ""
for _, l in ipairs(t) do
r = r .. l.text
end
return r
end
-- event loop
repeat
local event, data = interpreter:step() -- progress script until next event
if event == "text" then
print(format_text(d))
elseif event == "choice" then
for j, choice in ipairs(d) do
print(j.."> "..format_text(choice))
end
interpreter:choose(io.read())
elseif event == "error" then
error(data)
end
until t == "return" or t == "error"
```
Calling the Anselme main module will create a return a new [VM](#vms).
The main module also contain a few fields:
### anselme.versions
Anselme version information table.
Contains version informations as number (higher means more recent) of Anselme divied in a few categories:
* `save`, which is incremented at each update which may break save compatibility
* `language`, which is incremented at each update which may break script file compatibility
* `api`, which is incremented at each update which may break Lua API compatibility
### anselme.version
General version number.
It is incremented at each update.
### anselme.running
Currently running [interpreter](#interpreters).
`nil` if no interpreter running.
## Interpreters
An interpreter is in charge of running Anselme code and is spawned from a [VM](#vms).
Several interpreters from the same VM can run at the same time.
Typically, you would have a interpreter for each script that need at the same time, for example one for every NPC
that is currently talking.
Each interpreter can only run one script at a time, and will run it sequentially.
You can advance in the script by calling the `:step` method, which will run the script until an event is sent (for example some text needs to be displayed),
which will pause the whole interpreter until `:step` is called again.
### interpreter.vm
[VM](#vms) this interpreter belongs to.
### interpreter.end_event
String, type of the event that stopped the interpreter (`nil` if interpreter is still running).
### interpreter:step ()
Run the interpreter until the next event.
Returns event type (string), data (any).
Will merge changed variables on successful script end.
If event is `"return"` or `"error"`, the interpreter can not be stepped further and should be discarded.
Default event types and their associated data:
* `text`: text to display, data is a list of text elements, each with a `text` field, containing the text contents, and a `tags` field, containing the tags associated with this text
* `choice`: choices to choose from, data is a list of choices Each of these choice is a list of text elements like for the `text` event
* `return`: when the script ends, data is the returned value (`nil` if nothing returned)
* `error`: when there is an error, data is the error message.
See [LANGUAGE.md](LANGUAGE.md) for more details on events.
### interpreter:choose (i)
Select a choice.
`i` is the index (number) of the choice in the choice list (from the choice event's data).
The choice will be selected on the next interpreter step.
Returns this interpreter.
### interpreter:interrupt (expr)
Interrupt (abort the currently running script) the interpreter on the next step, executing an expression (string, if specified) in the current namespace instead.
Returns this interpreter.
### interpreter:current_namespace ()
Returns the namespace (string) the last ran line belongs to.
### interpreter:run (expr, namespace)
Run an expression (string) or block, optionally in a specific namespace (string, will use root namespace if not specified).
This may trigger events and must be called from within the interpreter coroutine (i.e. from a function called from a running script).
No automatic merge if this change the interpreter state, merge is done once we reach end of script in a call to `:step` as usual.
Returns the returned value (nil if nothing returned).
### interpreter:eval (expr, namespace)
Evaluate an expression (string) or block, optionally in a specific namespace (string, will use root namespace if not specified).
The expression can't yield events.
Can be called from outside the interpreter coroutine. Will create a new coroutine that operate on this interpreter state.
No automatic merge if this change the interpreter state, merge is done once we reach end of script in a call to `:step` as usual.
Returns the returned value in case of success (nil if nothing returned).
Returns nil, error message in case of error.
## VMs
A VM stores the state required to run Anselme scripts. Each VM is completely independant from each other.
### vm:loadgame (path)
Wrapper for loading a whole set of scripts (a "game").
Should be preferred to other loading functions if possible as this sets all the common options on its own.
Requires LÖVE or LuaFileSystem.
Will load from the directory given by `path` (string), in order:
* `config.ans`, which will be executed in the "config" namespace and may contains various optional configuration options:
* `anselme version`: number, version of the anselme language this game was made for
* `game version`: any, version information of the game. Can be used to perform eventual migration of save with an old version in the main file.
Always included in saved variables.
* `language`: string, built-in language file to load
* `inject directory`: string, directory that may contain "function start.ans", "checkpoint end.ans", etc. which content will be used to setup
the custom code injection methods (see vm:setinjection)
* `global directory`: string, path of global script directory. Every script file and subdirectory in the path will be loaded in the global namespace.
* `start expression`: string, expression that will be ran when starting the game
* files in the global directory, if defined in config.ans
* every other file in the path and subdirectories, using their path as namespace (i.e., contents of path/world1/john.ans will be defined in a function world1.john)
Returns this VM in case of success.
Returns nil, error message in case of error.
### vm:rungame ()
Return a interpreter which runs the game start expression (if given).
Returns interpreter in case of success.
Returns nil, error message in case of error.
### vm:loadstring (str, name, source)
Load code from a string.
Similar to Lua's code loading functions.
Compared to their Lua equivalents, these also take an optional `name` argument (default="") that set the namespace to load the code in. Will define a new function is specified; otherwise, code will be parsed but not executable from an expression (as it is not named).
Returns parsed block in case of success.
Returns nil, error message in case of error.
### vm:loadfile (path, name)
Load code from a file.
See `vm:loadstring`.
### vm:setaliases (seen, checkpoint, reached)
Set aliases for built-in variables 👁️, 🔖 and 🏁 that will be defined on every new checkpoint and function.
This does not affect variables that were defined before this function was called.
Set to nil for no alias.
Returns this VM.
### vm:setinjection (inject, code)
Set some code that will be injected at specific places in all code loaded after this is called.
Can typically be used to define variables for every function like 👁️, setting some value on every function resume, etc.
Possible inject types:
* `"function start"`: injected at the start of every non-scoped function
* `"function end"`: injected at the end of every non-scoped function
* `"function return"`: injected at the end of each return's children that is contained in a non-scoped function
* `"checkpoint start"`: injected at the start of every checkpoint
* `"checkpoint end"`: injected at the end of every checkpoint
* `"scoped function start"`: injected at the start of every scoped function
* `"scoped function end"`: injected at the end of every scoped function
* `"scoped function return"`: injected at the end of each return's children that is contained in a scoped function
Set `code` to nil to disable the inject.
Returns this VM.
### vm:loadlanguage (lang)
Load and execute a built-in language file.
The language file may optionally contain the special variables:
* alias 👁️: string, default alias for 👁️
* alias 🏁: string, default alias for 🏁
* alias 🔖: string, default alias for 🔖
Returns this VM in case of success.
Returns nil, error message in case of error.
### vm:loadfunction (signature, fn)
Define functions from Lua.
* `signature`: string, full signature of the function
* `fn`: function (Lua function or table, see examples in `stdlib/functions.lua`)
Returns this VM.
### vm:load (data)
Save/load script state
Only saves variables full names and values, so make sure to not change important variables, checkpoints and functions names between a save and a load.
Also only save variables with usable identifiers, so will skip functions with arguments, operators, etc. (i.e. every scoped functions).
Loading should be done after loading all the game scripts (otherwise you will "variable already defined" errors).
Returns this VM.
### vm:save ()
Save script state.
See `vm:load`.
Returns save data.
### vm:postload ()
Perform parsing that needs to be done after loading code.
This is automatically ran before starting an interpreter, but you may want to execute it before if you want to check for parsing error manually.
Returns self in case of success.
Returns nil, error message in case of error
### vm:enable (...)
Enable feature flags.
Available flags:
* `"strip trailing spaces"`: remove trailing spaces from choice and text events (enabled by default)
* `"strip duplicate spaces"`: remove duplicated spaces between text elements from choice and text events (enabled by default)
Returns this VM.
### vm:disable (...)
Disable features flags.
Returns this VM.
### vm:run (expr, namespace, tags)
Run code.
Will merge state after successful execution
* `expr`: expression to evaluate (string or parsed expression), or a block to run
* `namespace`(default=""): namespace to evaluate the expression in
* `tags`(default={}): defaults tags when evaluating the expression (Lua value)
Return interpreter in case of success.
Returns nil, error message in case of error.
### vm:eval (expr, namespace, tags)
Evaluate code.
Behave like `:run`, except the expression can not emit events and will return the result of the expression directly.
Merge state after sucessful execution automatically like `:run`.
* `expr`: expression to evaluate (string or parsed expression), or a block to evaluate
* `namespace`(default=""): namespace to evaluate the expression in
* `tags`(default={}): defaults tags when evaluating the expression (Lua value)
Return value in case of success (nil if nothing returned).
Returns nil, error message in case of error.

View file

@ -1,6 +1,3 @@
(FIXME compare with fe/janet types
$ f()
:a = 1