diff options
| author | s-ol <s-ol@users.noreply.github.com> | 2020-03-22 20:33:20 +0000 |
|---|---|---|
| committer | s-ol <s-ol@users.noreply.github.com> | 2020-03-22 20:33:20 +0000 |
| commit | 25d3ba6546d058ac673f6a3217ea433e91a438e7 (patch) | |
| tree | d2ae8a229e3488fa1cd28f52eb929df51e85ba8b /core | |
| parent | docs/guide: rephrasing symbols section (diff) | |
| download | alive-25d3ba6546d058ac673f6a3217ea433e91a438e7.tar.gz alive-25d3ba6546d058ac673f6a3217ea433e91a438e7.zip | |
internals/plugin-guide: first draft
Diffstat (limited to 'core')
| -rw-r--r-- | core/config.ld | 1 | ||||
| -rw-r--r-- | core/plugin-guide.md | 129 |
2 files changed, 130 insertions, 0 deletions
diff --git a/core/config.ld b/core/config.ld index 176db90..e010c5b 100644 --- a/core/config.ld +++ b/core/config.ld @@ -12,4 +12,5 @@ If you are looking for the language reference for users, head over to the format = 'discount' style = 'docs' template = 'docs' +topics={'core/plugin-guide.md'} dir = 'docs/internals' diff --git a/core/plugin-guide.md b/core/plugin-guide.md new file mode 100644 index 0000000..f5e3c50 --- /dev/null +++ b/core/plugin-guide.md @@ -0,0 +1,129 @@ +# writing `alive` plugins + +Plugins for `alive` are implemented in [Lua][lua] or [MoonScript][moonscript] +(which runs as Lua). When an `alive` module is [require][]d, alive looks for a +Lua module `lib.[module]`. You can simply add a new file with extension `.lua` +or `.moon` in the `lib` directory of your alive installation or somewhere else +in your `LUA_PATH`. + +To write plugins, a number of classes and utilities are required. All of these +are exported in the `base` module. + +## documentation metadata +The lua module should return a `Scope` or a table that will be converted using +`Scope.from_table`. All exports should be documented using `Value.meta`, which +attaches a `meta` table to the value that is used for error messages, +documentation generation and [`(doc)`][builtins-doc]. + + import Value from require 'core.base' + + two = Value.meta + meta: + name: 'two' + summary: "the number two" + value: 2 + + { + :two + } + +In the `meta` table `summary` is the only required key, but all of the +information that applies should be provided. + +- `name`: the name of this export (for error reporting). +- `summary`: a one-line plain-text description of this entry. Should be + capitalized and end with a period. +- `examples`: a table of strings, each of which is a short one-line code + example illustrating the argument names for an Op. +- `description`: a longer markdown-formatted description of the functionality + of this entry. + +## defining `Op`s +Most plugins will want to define a number of *Op*s to be used by the user. They +are implemented by deriving from the `Op` class and implementing at least the +`Op:setup` and `Op:tick` methods. + + import Value, Op, Input, match from require 'core.base' + + my_add = Value.meta + meta: + name: 'my-add' + summary: "Add two numbers." + examples: { '(my-add a b)' } + description: "Add two numbers, plugin-style." + + value: class extends Op + new: => super 'num' + + setup: (inputs, scope) => + { a, b } = match 'num num', inputs + + super + a: Inputs.value a + b: Inputs.value b + + tick: => + @out\set @inputs.a! + @inputs.b! + + { + 'my-add': my_add + } + +### Op:setup +`Op:setup` is called once every *eval cycle* to parse the Op's arguments, check +their types, choose the updating behaviour and define the output type. + +The arguments to `:setup` are a list of inputs (each is a `Result` instance), +and the `Scope` the evaluation happened in. Ops generally shouldn't use the +scope, but might look up 'magic' dynamic symbols like `\*clock\*`. + +#### argument parsing +Arguments should be parsed using `match`. It takes a string that describes the +argument types and matches them against the provided arguments: + + import match from require 'core.base' + + { str, numbers, optional } = match 'str *num any?', inputs + +`match` matches arguments greedily from left to right. Each part of the string +is the type-name of a Value. Parts can be optional (`num?`), multiple (`*num` - +one or more numbers) or both (`*num?` - zero or more numbers). If there is an +equals sign in front of a part, the corresponding `Result` has to be +*evaltime constant*. The special typename `any` can be used for generic Ops. + +If there are more complex dependencies between arguments, it is recommended to +do as much of the parsing as possible using `match`, and then continue +manually. For invalid or missing arguments, `Error` instances should be thrown +using `error` or `assert`. + +#### input setup + +(section wip since changes are anticipated) + +#### output setup + +When `Op:setup` finishes, `@out` has to be set to a `Value` instance. The +instance can be created in `Op:setup`, or by overriding the constructor and +delegating to the original one using `super`. In general this way of creating +the output value is preferred, and it is only moved to `Op:setup` if the output +type depends on the arguments received. + +### Op:tick +`Op:tick` is called whenever any of the inputs are *dirty*. This is where the +Op's main logic will go. Generally here it should be checked which input(s) +changed, and then internal state and the output value may be updated. + +## defining `Action`s +`Action`s are more powerful than `Op`s, because they control whether, which and +how their arguments are evaluated. They roughly correspond to *macros* in Lisps. +Since it is rarely necessary to implement `Action`s, there is currently no +documentation on implementing them, but the `Action` class documentation and the +examples in `core/builtin.moon` should be enough to get started. + +## `IO`s + +(wip) + +[lua]: https://www.lua.org/ +[moonscript]: http://moonscript.org/ +[builtins-doc]: ../../reference/index.html#doc |
