diff options
| author | s-ol <s-ol@users.noreply.github.com> | 2020-04-13 18:40:35 +0000 |
|---|---|---|
| committer | s-ol <s-ol@users.noreply.github.com> | 2020-04-14 08:46:54 +0000 |
| commit | 1a8debe87762072b3a63b769aa515ebf63b4c70d (patch) | |
| tree | 33b42866307d11d2c0878b23e0ec0bb8925d3fcc /core | |
| parent | spec base.match __tostring (diff) | |
| download | alive-1a8debe87762072b3a63b769aa515ebf63b4c70d.tar.gz alive-1a8debe87762072b3a63b769aa515ebf63b4c70d.zip | |
move into proper Lua module (`alv`)
Diffstat (limited to 'core')
| -rw-r--r-- | core/ast.moon | 40 | ||||
| -rw-r--r-- | core/base/builtin.moon | 96 | ||||
| -rw-r--r-- | core/base/fndef.moon | 26 | ||||
| -rw-r--r-- | core/base/init.moon | 39 | ||||
| -rw-r--r-- | core/base/input.moon | 146 | ||||
| -rw-r--r-- | core/base/match.moon | 302 | ||||
| -rw-r--r-- | core/base/op.moon | 168 | ||||
| -rw-r--r-- | core/builtin.moon | 333 | ||||
| -rw-r--r-- | core/cell.moon | 189 | ||||
| -rw-r--r-- | core/config.ld | 16 | ||||
| -rw-r--r-- | core/cycle.moon | 26 | ||||
| -rw-r--r-- | core/error.moon | 94 | ||||
| -rw-r--r-- | core/extensions.md | 221 | ||||
| -rw-r--r-- | core/init.moon | 55 | ||||
| -rw-r--r-- | core/invoke.moon | 124 | ||||
| -rw-r--r-- | core/parsing.moon | 73 | ||||
| -rw-r--r-- | core/registry.moon | 145 | ||||
| -rw-r--r-- | core/result.moon | 126 | ||||
| -rw-r--r-- | core/scope.moon | 118 | ||||
| -rw-r--r-- | core/stream/base.moon | 68 | ||||
| -rw-r--r-- | core/stream/event.moon | 100 | ||||
| -rw-r--r-- | core/stream/init.moon | 18 | ||||
| -rw-r--r-- | core/stream/io.moon | 55 | ||||
| -rw-r--r-- | core/stream/value.moon | 237 | ||||
| -rw-r--r-- | core/tag.moon | 122 | ||||
| -rw-r--r-- | core/version.moon | 17 |
26 files changed, 0 insertions, 2954 deletions
diff --git a/core/ast.moon b/core/ast.moon deleted file mode 100644 index a25d1cd..0000000 --- a/core/ast.moon +++ /dev/null @@ -1,40 +0,0 @@ ----- --- AST Node Interface. --- --- implemented by `Value` and `Cell`. --- --- @classmod AST - ---- members --- @section members - - --- evaluate this AST Node. - -- - -- Evaluate this node and return a `Result`. - -- - -- @function eval - -- @tparam Scope scope the scope to evaluate in - -- @treturn Result the evaluation result - - --- quote this AST Node, preserving its identity. - -- - --- Returns a mutable copy of this Node that shares its identity. - -- - -- @function quote - -- @treturn AST - - --- create a clone with its own identity. - -- - -- creates a clone of this Cell with its own identity by prepending a `parent` - -- Tag (and cloning all child expressions recursively). - -- - -- @function clone - -- @tparam Tag parent - -- @treturn AST - - --- stringify this AST Node. - -- - -- Should return the exact string this node was parsed from (if it was parsed). - -- - -- @function stringify - -- @treturn string the exact string this Node was parsed from diff --git a/core/base/builtin.moon b/core/base/builtin.moon deleted file mode 100644 index fd36c69..0000000 --- a/core/base/builtin.moon +++ /dev/null @@ -1,96 +0,0 @@ ----- --- Builtin / Special Form evaluation Strategy (`builtin`). --- --- Responsible for quoting/evaluating subexpressions, instantiating and setting --- up `Op`s, updating the current `Scope`, etc. --- See `builtin` and `invoke` for examples. --- --- @classmod Builtin - -class Builtin ---- Builtin interface. --- --- methods that have to be implemented by `Builtin` implementations. --- @section interface - - --- create a new instance. - -- - -- @tparam Cell cell the Cell to evaluate - -- @tparam Value head the (`AST:eval`d) `head` of the Cell to evaluate - new: (@cell, @head) => - @tag = @cell.tag - @tag\register @ - - --- perform the actual evaluation. - -- - -- Implementations should: - -- - -- - eval or quote `tail` values - -- - perform scope effects - -- - wrap all child-results - -- - -- @tparam Scope scope the active scope - -- @tparam {AST,...} tail the arguments to this expression - -- @treturn Result the result of this evaluation - eval: (scope, tail) => error "not implemented" - - --- free resources - destroy: => - - --- setup or copy state from previous instance of same type. - -- - -- `prev` is only passed if Builtin types of prev and current expression match. - -- Otherwise, or when no previous expression exists, `nil` is passed. - -- - -- @tparam ?Builtin prev the previous Builtin instance - setup: (prev) => - - --- the `Cell` this Builtin was created for. - -- @tfield Cell cell - - --- the evaluated head of `cell`. - -- @tfield AST head - - --- the identity of `cell`. - -- @tfield Tag tag - ---- static functions --- @section static - - --- create and setup a `Builtin` for a given tag, then evaluate it. - -- - -- Create a new instance using `tag` and `head` and call `setup` on it. - -- If a previous instance with the same `tag` exists and has the same `head`, - -- it pass it to `setup`. Register the `Builtin` with `tag`, evaluate it - -- and return the `Result`. - -- - -- @tparam Cell cell the `Cell` being evaluated - -- @tparam Scope scope the active scope - -- @tparam Value head the (`AST:eval`d) head of the `Cell` being evaluated - -- @treturn Result the result of evaluation - @eval_cell: (cell, scope, head) => - last = cell.tag\last! - compatible = last and (last.__class == @) and last.head == head - - L\trace if compatible - "reusing #{last} for #{cell.tag} <#{@__name} #{head}>" - else if last - "replacing #{last} with new #{cell.tag} <#{@__name} #{head}>" - else - "initializing #{cell.tag} <#{@__name} #{head}>" - - builtin = @ cell, head - if compatible - builtin\setup last - else - last\destroy! if last - builtin\setup nil - - builtin\eval scope, cell\tail! - - __tostring: => "<#{@@__name} #{@head}>" - __inherited: (cls) => cls.__base.__tostring = @__tostring - -{ - :Builtin -} diff --git a/core/base/fndef.moon b/core/base/fndef.moon deleted file mode 100644 index b79ad85..0000000 --- a/core/base/fndef.moon +++ /dev/null @@ -1,26 +0,0 @@ ----- --- user-function definition (`fndef`). --- --- When called, expands to its body with params bound to the fn arguments (see --- `invoke.fn_invoke`). --- --- @classmod FnDef - -class FnDef ---- static functions --- @section static - - --- create a new instance - -- - -- @classmethod - -- @tparam {Value,...} params (`AST:quote`d) naming the function parameters - -- @tparam AST body (`AST:quote`d) expression the function evaluates to - -- @tparam Scope scope the lexical scope the function was defined in (closure) - new: (@params, @body, @scope) => - - __tostring: => - "(fn (#{table.concat [p\stringify! for p in *@params], ' '}) ...)" - -{ - :FnDef -} diff --git a/core/base/init.moon b/core/base/init.moon deleted file mode 100644 index 9153f79..0000000 --- a/core/base/init.moon +++ /dev/null @@ -1,39 +0,0 @@ ----- --- Base definitions for extensions. --- --- This module exports the following classes and tables that extension modules --- may need: --- --- @module base --- @see Op --- @see Builtin --- @see FnDef --- @see Input --- @see base.match.val --- @see base.match.evt --- @see ValueStream --- @see EventStream --- @see IOStream --- @see Result --- @see Error - -import Op from require 'core.base.op' -import Builtin from require 'core.base.builtin' -import FnDef from require 'core.base.fndef' -import Input from require 'core.base.input' -import val, evt from require 'core.base.match' -import ValueStream, EventStream, IOStream from require 'core.stream' -import Result from require 'core.result' -import Error from require 'core.error' - -{ - :Op - :Builtin - :FnDef - :Input - :val, :evt - - -- redundant exports, to keep anything an extension might need in one import - :ValueStream, :EventStream, :IOStream - :Result, :Error -} diff --git a/core/base/input.moon b/core/base/input.moon deleted file mode 100644 index bcf188e..0000000 --- a/core/base/input.moon +++ /dev/null @@ -1,146 +0,0 @@ ----- --- Update scheduling policy for `Op` arguments. --- --- @classmod Input -import ValueStream, EventStream, IOStream from require 'core.stream' -import Result from require 'core.result' - -inherits = (klass, frm) -> - assert klass, "cant find the ancestor of nil" - return true if klass == frm - while klass.__parent - return true if klass.__parent == frm - klass = klass.__parent - false - -match_parent = (inst, map) -> - klass = assert inst and inst.__class, "not an instance" - if key = map[klass] - return key - - while klass.__parent - if key = map[klass.__parent] - return key - - klass = klass.__parent - -local ColdInput, ValueInput, IOInput, mapping - -class Input ---- Input interface. --- --- Methods that have to be implemented by `Input` implementations. --- @section interface - - --- create a new Input. - -- - -- @classmethod - -- @tparam Stream stream - new: (@stream) => - assert @stream, "nil passed to Input: #{value}" - - --- copy state from old instance (optional). - -- - -- called by `Op:setup` with another `Input` instance or `nil` once this instance is - -- registered. Must prepare this instance for `dirty`. - -- - -- May enter a 'setup state' that is exited using `finish_setup`. - -- - -- @tparam ?Input prev previous `Input` intance or nil - setup: (prev) => - - --- whether this input requires processing (optional). - -- - -- must return a boolean indicating whether `Op`s that refer to this instance - -- should be notified (via `Op:tick`). If not overwritten, delegates to - -- `stream`:@{ValueStream:dirty|dirty}. - -- - -- @treturn bool whether processing is necessary - dirty: => @stream\dirty! - - --- leave setup state (optional). - -- - -- called after the `Op` has completed (or skipped) its first `Op:tick` after - -- `Op:setup`. Must prepare this instance for dataflow operation. - finish_setup: => - - --- unwrap to Lua value (optional). - -- - -- @treturn any the raw Lua value - unwrap: => @stream\unwrap! - - --- return the type name of this `Input` (optional). - type: => @stream.type - - --- return the metatype name of this `Input` (optional). - metatype: => @stream.metatype - - --- the current value - -- - -- @tfield ValueStream stream - ---- members --- @section members - - --- alias for `unwrap`. - __call: => @stream\unwrap! - - __tostring: => "#{@@__name}:#{@stream}" - __inherited: (cls) => - cls.__base.__call = @__call - cls.__base.__tostring = @__tostring - ---- static functions --- @section static - - --- Create a `cold` `Input`. - -- - -- Never marked dirty. Use this for input streams that are only read when - -- another `Input` is dirty. - -- - -- @tparam Stream|Result value - @cold: (value) -> - if value.__class == Result - value = assert value.value, "Input from result without value!" - ColdInput value - - --- Create a `hot` `Input`. - -- - -- Behaviour depends on what kind of `Stream` `value` is: - -- - -- - `ValueStream`: Marked dirty for the eval-tick if old and new `ValueStream` differ. - -- - `EventStream` and `IOStream`: Marked dirty only if the current `EventStream` is dirty. - -- - -- This is the most common `Input` strategy. - -- - -- @tparam Stream|Result value - @hot: (value) -> - if value.__class == Result - value = assert value.value, "Input from result without value!" - - InputType = match_parent value, mapping - assert InputType, "Input from unknown value: #{value}" - InputType value - -class ColdInput extends Input - dirty: => false - -class ValueInput extends Input - setup: (old) => @dirty_setup = not old or @stream != old.stream - finish_setup: => @dirty_setup = nil - dirty: => - return @dirty_setup if @dirty_setup != nil - @stream\dirty! - -class IOInput extends Input - io: true - -mapping = { - [ValueStream]: ValueInput - [EventStream]: Input - [IOStream]: IOInput -} - -{ - :Input -} diff --git a/core/base/match.moon b/core/base/match.moon deleted file mode 100644 index 700b50d..0000000 --- a/core/base/match.moon +++ /dev/null @@ -1,302 +0,0 @@ ------ ---- Pattern capturing for Op argument parsing. --- --- There is only one basic buildings block for assembling patterns: --- `Type`. It can match `ValueStream`s and `EventStream`s depending on its --- metatype argument and can take an optional type name to match as an argument. --- --- In addition to this primitive, the following modifiers are available: --- `Repeat`, `Sequence`, `Choice`, and `Optional`. They can be used directly, --- but there is also a number of shorthands for assembling patterns quickly: --- --- - `val()` and `evt()`: Shorthands for `Type('value')` and `Type('event')` --- - `val.num`: Shorthand for `Type('value', 'num')` --- - `evt.str`: Shorthand for `Type('event', 'str')` --- - `pat * 2`: Shorthand for `Repeat(pat, 1, 2)` (1-4 times `pat`) --- - `pat * 0`: Shorthand for `Repeat(pat, 1, nil)` (1-* times `pat`) --- - `pat ^ 2`: Shorthand for `Repeat(pat, 0, 2)` (0-4 times `pat`) --- - `pat ^ 0`: Shorthand for `Repeat(pat, 0, nil)` (0-* times `pat`) --- - `a + b + … + z`: Shorthand for `Sequence{ a, b, ..., z }` --- - `a / b / … / z`: Shorthand for `Choice{ a, b, ..., z }` --- - `-pat`: Shorthand for `Optional(pat)` --- --- To perform the actual matching, call the `:match` method on a pattern and --- pass a sequence of `Result`s. The method will either return the captured --- `Result`s (or a table structuring them) --- --- Any ambiguous pattern can be set to 'recall mode' by invoking it. --- Recalling patterns will memorize the first Result they match, and --- only match further Results of the same type. For example --- --- arg = (val.num / val.str)! --- pattern = arg + arg --- --- ...will match either two numbers or two strings, but not one number and one --- string. Recalling works on `Choice` and `Type` patterns. `Type` patterns --- without a type (`val!` and `evt!`) always behave like this. --- --- On `Sequence` patterns, a special method `:named` exists. It takes a --- sequence of keys that are used instead of integers when constructing the --- capture table: --- --- pattern = (val.str + val.num):named('key', 'value') --- pattern:match(...) --- -- returns { {key='a', value=1}, {key='b', value=2}, ...} --- --- @module base.match -import Error from require 'core.error' -import ValueStream, EventStream from require 'core.stream' - -local Repeat, Sequence, Choice, Optional - -typestr = (result) -> - str = result\type! - str ..= '!' if result\metatype! == 'event' - str - -class Pattern - match: (seq) => - @reset! - num, cap = @capture seq, 1 - if num != #seq - args = table.concat [typestr arg for arg in *seq], ' ' - msg = "couldn't match arguments (#{args}) against pattern #{@}" - error Error 'argument', msg - cap - - remember: (key) => - return true unless @recall - - @recalled or= key - @recalled == key - - rep: (min, max) => Repeat @, min, max - - reset: => @recalled = nil - - __call: => @ - __mul: (num) => Repeat @, 1, if num != 0 then num - __pow: (num) => Repeat @, 0, if num != 0 then num - __add: (other) => Sequence { @, other } - __div: (other) => Choice { @, other } - __unm: => Optional @ - - __inherited: (cls) => - cls.__base.__call or= @__call - cls.__base.__mul or= @__mul - cls.__base.__pow or= @__pow - cls.__base.__add or= @__add - cls.__base.__div or= @__div - cls.__base.__unm or= @__unm - ---- Base Stream Pattern. --- --- When instantiated with `type`, only succeeds for `Stream`s whose value and --- meta types match. --- --- Otherwise, matches Streams based only on `metatype` for the first match, but --- using both afterwards (recall mode). --- --- @function Stream --- @tparam string metatype "value" or "event" --- @tparam ?string type type name -class Type extends Pattern - new: (@metatype, @type) => - @recall = not @type - - capture: (seq, i) => - return unless seq[i] - type, mt = seq[i]\type!, seq[i]\metatype! - return unless @metatype == mt - match = if @type then type == @type else @remember type - if match - 1, seq[i] - - __tostring: => - str = @type or @metatype - str ..= '!' if @metatype == 'event' - str - ---- Repeat a pattern. --- --- Matches a given `inner` pattern as many times as possible, within the given --- minimum/maximum counts. Matching this pattern results in a sequence of the --- individual captures produced by the inner pattern. --- --- @function Repeat --- @tparam Pattern inner the original pattern --- @tparam ?number min minimum amount of repetitions --- @tparam ?number max maximum amount of repetitions (default infinite) -class Repeat extends Pattern - new: (@inner, @min, @max) => - - capture: (seq, i) => - take, all = 0, {} - while true - num, cap = @inner\capture seq, i+take - break unless num - - take += num - table.insert all, cap - - break if @max and take >= @max - - return if @min and take < @min - return if @max and take > @max - - take, all - - reset: => - @inner\reset! - - __call: => - @@ @inner!, @min, @max - - __tostring: => - min = @min or '0' - max = @max or '*' - "#{@inner}{#{min}-#{max}}" - ---- Match multiple patterns in order. --- --- Matches the inner patterns in order, only succeeds if all of them match. --- Captures the individual captures produced by the inner patterns in a --- sequence, or table with keys specified in `keys` or using the `:named(...)` --- modifier. --- --- @function Sequence --- @tparam {Pattern,...} elements the inner patterns --- @tparam ?{string,...} keys the keys to use when capturing matches -class Sequence extends Pattern - new: (@elements, @keys) => - - capture: (seq, i) => - take, all = 0, {} - for key, elem in ipairs @elements - num, cap = elem\capture seq, i+take - return unless num - - take += num - key = @keys[key] if @keys - all[key] = cap - - take, all - - reset: => - for elem in *@elements - elem\reset! - - named: (...) => - @@ [e for e in *@elements], { ... } - - __call: => - @@ [e! for e in *@elements] - - __add: (other) => - elements = [e for e in *@elements] - table.insert elements, other - @@ elements - - __tostring: => - core = table.concat [tostring e for e in *@elements], ' ' - "(#{core})" - ---- Match one of multiple options. --- --- Matches using the first matching pattern in `elements` and returns its --- captured value. Supports recalling the matched subpattern. --- --- @function Choice --- @tparam {Pattern,...} elements the inner patterns --- @tparam ?{string,...} keys the keys to use when capturing matches -class Choice extends Pattern - new: (@elements, @recall=false) => - - capture: (seq, i) => - for key, elem in ipairs @elements - num, cap = elem\capture seq, i - if num and @remember key - return num, cap - - reset: => - super! - for elem in *@elements - elem\reset! - - __call: => - @@ [e! for e in *@elements], true - - __div: (other) => - elements = [e for e in *@elements] - table.insert elements, other - @@ elements - - __tostring: => - core = table.concat [tostring e for e in *@elements], ' | ' - "(#{core})" - ---- Optionally match a pattern. --- --- Matches using the first matching pattern in `elements` and returns its --- captured value. Supports recalling the matched subpattern. --- --- @function Optional --- @tparam {Pattern,...} elements the inner patterns --- @tparam ?{string,...} keys the keys to use when capturing matches -class Optional extends Pattern - new: (@inner) => - - capture: (seq, i) => - num, cap = @inner\capture seq, i - num or 0, cap - - reset: => - @inner\reset! - - __call: => - @@ @inner! - - __unm: => @ - - __tostring: => "#{@inner}?" - ---- `Value` shorthands. --- --- Call or index with a string to obtain a `Type` instance. --- Call to obtain a wildcard pattern. --- --- val.str, val.num --- val['vec3'], val('vec3') --- val() --- --- @table val -val = setmetatable {}, { - __index: (key) => - with v = Type 'value', key - @[key] = v - - __call: (...) => Type 'value', ... -} - ---- `Event` shorthands. --- --- Call or index with a string to obtain an `Type` instance. --- Call to obtain a wildcard pattern. --- --- evt.bang, evt.str, evt.num --- evt['midi/message'], evt('midi/message') --- evt() --- --- @table evt -evt = setmetatable {}, { - __index: (key) => - with v = Type 'event', key - @[key] = v - - __call: (...) => Type 'event', ... -} - -{ - :Type, :Repeat, :Sequence, :Choice, :Optional - :val, :evt -} diff --git a/core/base/op.moon b/core/base/op.moon deleted file mode 100644 index b0a83dc..0000000 --- a/core/base/op.moon +++ /dev/null @@ -1,168 +0,0 @@ ----- --- Persistent expression Operator. --- --- @classmod Op - -deepcopy = (val) -> - switch type val - when 'number', 'string', 'boolean', 'nil' - val - when 'table' - assert (not getmetatable {}), "state should only contain simple tables!" - {(deepcopy k), (deepcopy v) for k,v in pairs val} - else - error "state cannot contain values of type '#{type val}'" - -class Op ---- members --- @section members - - do_yield = (table) -> - for k, v in pairs table - if v.__class - coroutine.yield v - else - do_yield v - --- yield all `Input`s from the (potentially nested) `inputs` table - -- - -- @treturn iterator iterator over `inputs` - all_inputs: => coroutine.wrap -> do_yield @inputs - - --- create a mutable copy of this Op. - -- - -- Used to wrap insulate eval-cycles from each other. The copy does not have - -- `inputs` set, since it is expected that this is (re)set in `setup`. - -- - -- @treturn Op - fork: => - out = if @out then @out\fork! - state = if @state then deepcopy @state - @@ out, state - - --- internal state of this Op. - -- - -- This may be any simple Lua value, including Lua tables, as long as it has - -- no metatables, multiple references/loops, userdata etc. - -- - -- @tfield table state - - --- `Stream` instance representing this Op's computed output value. - -- - -- Must be set to a `Stream` instance once `setup` finishes. Must not change - -- type, be removed or replaced outside of `new` and `setup`. If it is a - -- `ValueStream`, it should have a value assigned via `set` or the - -- constructor once `tick` is called the first time. If `out`'s value is not - -- initialized in `new` or `setup`, the implementation must make sure - -- `tick``(true)` is called at least on the first eval-cycle the Op goes - -- through, e.g. by using an `Input.hot` with a `ValueStream`. - -- - -- @tfield Stream out - - --- table containing `Input`s to this Op. - -- - -- The `inputs` table can be nested with string or integer keys, - -- but all leaf-entries must be `Input` instances. It must not contain loops - -- or instances of other classes. - -- - -- @tfield {Input,...} inputs - ---- Op interface. --- --- methods that have to be implemented by `Op` implementations. --- @section interface - - --- construct a new instance. - -- - -- The optional parameters `out` and `state` are used by `fork` to duplicate - -- an instance. If the constructor is overriden, these parameters must be - -- forwarded to the superconstructor unchanged. - -- - -- @function new - -- @classmethod - -- @tparam ?Stream out `out` - -- @tparam ?table state `state` - - --- parse arguments and patch self. - -- - -- Called once every eval-cycle. `inputs` is a list of `Result`s that are the - -- argument to this op. The `inputs` have to be wrapped in `Input` instances - -- to define update behaviour. Use `base.match` to parse them, then delegate to - -- `super:setup` to patch the `Input` instances. - -- - -- @function setup - -- @tparam {Result,...} inputs a sequence of `Result`s - -- @tparam Scope scope the active scope - - --- handle incoming events and update `out` (optional). - -- - -- Called once per frame if any `Input`s are dirty. Some `Input`s may have - -- special behaviour immediately after `setup` that can cause them to become - -- dirty at eval-time. In this case, an eval-time tick is executed. You can - -- detect this using the `setup` parameter. - -- - -- `tick` is called after `setup`. `tick` is not called immediately after - -- `setup` if no `inputs` are dirty. Update `out` here. - -- - -- @tparam bool setup whether this is an eval-time tick - tick: => - - --- called when the Op is destroyed (optional). - destroy: => - ---- implementation utilities. --- --- super-methods and utilities for use by implementations. --- @section super - - --- if `type` is passed, an output stream is instantiated. - -- if `init` is passed, the stream is initialized to that Lua value. - -- it is okay not to use this and create the output stream in :setup() if the - -- type is not known at this time. - -- - -- @classmethod - -- @tparam ?Stream out `out` - -- @tparam ?table state `state` - new: (@out, @state) => - - do_setup = (old, cur) -> - for k, cur_val in pairs cur - old_val = old and old[k] - - -- are these inputs or nested tables? - cur_plain = cur_val and not cur_val.__class - old_plain = old_val and not old_val.__class - - if cur_plain and old_plain - -- both are tables, recurse - do_setup old_val, cur_val - elseif not (cur_plain or old_plain) - -- both are streams (or nil), setup them - cur_val\setup old_val - --- setup previous `inputs`, if any, with the new inputs, and write them to - -- `inputs`. The `inputs` table can be nested with string or integer keys, - -- but all leaf-entries must be `Input` instances. It must not contain loops - -- or instances of other classes. - -- - -- @tparam table inputs table of `Input`s - setup: (inputs) => - old_inputs = @inputs - @inputs = inputs - do_setup old_inputs, @inputs - - do_unwrap = (value) -> - if value.__class - value\unwrap! - else - {k, do_unwrap v for k,v in pairs value} - --- `\unwrap` all `Input`s in `@inputs` and return a table with the same - -- shape. - -- - -- @treturn table the values of all `Input`s - unwrap_all: => do_unwrap @inputs - - __tostring: => "<op: #{@@__name}>" - __inherited: (cls) => cls.__base.__tostring = @__tostring - -{ - :Op -} diff --git a/core/builtin.moon b/core/builtin.moon deleted file mode 100644 index 68b825c..0000000 --- a/core/builtin.moon +++ /dev/null @@ -1,333 +0,0 @@ ----- --- Builtin `Builtin`s and `Op`s. --- --- Please see the [reference](../../reference/index.html#builtins) for --- documentation. --- --- @module builtin -import Builtin, Op, FnDef, Input, val, evt from require 'core.base' -import ValueStream, LiteralValue from require 'core.stream.value' -import Result from require 'core.result' -import Cell from require 'core.cell' -import Scope from require 'core.scope' -import Tag from require 'core.tag' -import op_invoke from require 'core.invoke' - -doc = ValueStream.meta - meta: - name: 'doc' - summary: "Print documentation in console." - examples: { '(doc sym)' } - description: "Print the documentation for `sym` to the console" - - value: class extends Builtin - format_meta = => - str = @summary - if @examples - for example in *@examples - str ..= '\n' .. example - if @description - str ..= '\n' .. @description\match '^\n*(.+)\n*$' - str - - eval: (scope, tail) => - assert #tail == 1, "'doc' takes exactly one parameter" - - result = L\push tail[1]\eval, scope - with Result children: { def } - meta = result.value.meta - L\print "(doc #{tail[1]}):\n#{format_meta meta}\n" - -def = ValueStream.meta - meta: - name: 'def' - summary: "Declare symbols in current scope." - examples: { '(def sym1 val-expr1 [sym2 val-expr2…])' } - description: " -Define the symbols `sym1`, `sym2`, … to resolve to the values of `val-expr1`, -`val-expr2`, …." - - value: class extends Builtin - eval: (scope, tail) => - L\trace "evaling #{@}" - assert #tail > 1, "'def' requires at least 2 arguments" - assert #tail % 2 == 0, "'def' requires an even number of arguments" - - children = L\push -> - return for i=1,#tail,2 - name, val_expr = tail[i], tail[i+1] - name = (name\quote scope)\unwrap 'sym' - - with val_expr\eval scope - scope\set name, \make_ref! - - Result :children - -use = ValueStream.meta - meta: - name: 'use' - summary: "Merge scopes into current scope." - examples: { '(use scope1 [scope2…])' } - description: " -Copy all symbol definitions from `scope1`, `scope2`, … to the current scope. -All arguments have to be evaltime constant." - - value: class extends Builtin - eval: (scope, tail) => - L\trace "evaling #{@}" - for child in *tail - result = L\push child\eval, scope - value = result\const! - scope\use value\unwrap 'scope', "'use' only works on scopes" - - Result! - -require_ = ValueStream.meta - meta: - name: 'require' - summary: "Load a module." - examples: { '(require name)' } - description: "Load a module and return its scope." - - value: class extends Builtin - eval: (scope, tail) => - L\trace "evaling #{@}" - assert #tail == 1, "'require' takes exactly one parameter" - - result = L\push tail[1]\eval, scope - name = result\const! - - L\trace @, "loading module #{name}" - scope = ValueStream.wrap require "lib.#{name\unwrap 'str'}" - Result :value - -import_ = ValueStream.meta - meta: - name: 'import' - summary: "Require and define modules." - examples: { '(import sym1 [sym2…])' } - description: " -Requires modules `sym1`, `sym2`, … and define them as `sym1`, `sym2`, … in the -current scope." - - value: class extends Builtin - eval: (scope, tail) => - L\trace "evaling #{@}" - assert #tail > 0, "'import' requires at least one arguments" - - for child in *tail - name = (child\quote scope)\unwrap 'sym' - value = ValueStream.wrap require "lib.#{name}" - scope\set name, Result :value -- (require "lib.#{name})\unwrap 'scope' - Result! - -import_star = ValueStream.meta - meta: - name: 'import*' - summary: "Require and use modules." - examples: { '(import* sym1 [sym2…])' } - description: " -Requires modules `sym1`, `sym2`, … and merges them into the current scope." - - value: class extends Builtin - eval: (scope, tail) => - L\trace "evaling #{@}" - assert #tail > 0, "'import' requires at least one arguments" - - - for child in *tail - name = (child\quote scope)\unwrap 'sym' - value = ValueStream.wrap require "lib.#{name}" - scope\use value\unwrap 'scope' -- (require "lib.#{name}")\unwrap 'scope' - - Result! - -fn = ValueStream.meta - meta: - name: 'fn' - summary: "Declare a function." - examples: { '(fn (p1 [p2…]) body-expr)' } - description: " -The symbols `p1`, `p2`, ... will resolve to the arguments passed when the -function is invoked." - - value: class extends Builtin - eval: (scope, tail) => - L\trace "evaling #{@}" - assert #tail == 2, "'fn' takes exactly two arguments" - { params, body } = tail - - assert params.__class == Cell, "'fn's first argument has to be an expression" - param_symbols = for param in *params.children - assert param.type == 'sym', "function parameter declaration has to be a symbol" - param\quote scope - - body = body\quote scope - Result value: with ValueStream.wrap FnDef param_symbols, body, scope - .meta = { - summary: "(user defined function)" - examples: { "(??? #{table.concat [p! for p in *param_symbols], ' '})" } - } - -defn = ValueStream.meta - meta: - name: 'defn' - summary: "Define a function." - examples: { '(defn name-sym (p1 [p2…]) body-expr)' } - description: " -Declare a function and define it as `name-sym` in the current scope. -The symbols `p1`, `p2`, ... will resolve to the arguments passed when the -function is invoked." - - value: class extends Builtin - eval: (scope, tail) => - L\trace "evaling #{@}" - assert #tail == 3, "'defn' takes exactly three arguments" - { name, params, body } = tail - - name = (name\quote scope)\unwrap 'sym' - assert params.__class == Cell, "'defn's second argument has to be an expression" - param_symbols = for param in *params.children - assert param.type == 'sym', "function parameter declaration has to be a symbol" - param\quote scope - - body = body\quote scope - - value = with ValueStream.wrap FnDef param_symbols, body, scope - .meta = - :name - summary: "(user defined function)" - examples: { "(#{name} #{table.concat [p! for p in *param_symbols], ' '})" } - - scope\set name, Result :value - Result! - -do_expr = ValueStream.meta - meta: - name: 'do_expr' - summary: "Evaluate multiple expressions in a new scope." - examples: { '(do expr1 [expr2…])' } - description: " -Evaluate `expr1`, `expr2`, … and return the value of the last expression." - - value: class extends Builtin - eval: (scope, tail) => - scope = Scope scope - Result children: [expr\eval scope for expr in *tail] - -if_ = ValueStream.meta - meta: - name: 'if' - summary: "Make an evaltime const choice." - examples: { '(if bool then-expr [else-expr])' } - description: " -`bool` has to be an evaltime constant. If it is truthy, this expression is equivalent -to `then-expr`, otherwise it is equivalent to `else-xpr` if given, or nil otherwise." - - value: class extends Builtin - eval: (scope, tail) => - L\trace "evaling #{@}" - assert #tail >= 2, "'if' needs at least two parameters" - assert #tail <= 3, "'if' needs at most three parameters" - - { xif, xthen, xelse } = tail - - xif = L\push xif\eval, scope - xif = xif\const!\unwrap! - - if xif - xthen\eval scope - elseif xelse - xelse\eval scope - -trace_ = ValueStream.meta - meta: - name: 'trace!' - summary: "Trace an expression's value at evaltime." - examples: { '(trace! expr)' } - - value: class extends Builtin - eval: (scope, tail) => - L\trace "evaling #{@}" - assert #tail == 1, "'trace!' takes exactly one parameter" - - with result = L\push tail[1]\eval, scope - L\print "trace! #{tail[1]\stringify!}: #{result.value}" - -trace = ValueStream.meta - meta: - name: 'trace' - summary: "Trace an expression's values at runtime." - examples: { '(trace expr)' } - - value: class extends Builtin - class traceOp extends Op - setup: (inputs) => - super - prefix: Input.cold inputs[1] - value: Input.hot inputs[2] - - tick: => - L\print "trace #{@inputs.prefix!}: #{@inputs.value.stream}" - - eval: (scope, tail) => - L\trace "evaling #{@}" - assert #tail == 1, "'trace!' takes exactly one parameter" - - tag = @tag\clone Tag.parse '-1' - inner = Cell tag, { - LiteralValue 'opdef', traceOp, 'trace' - ValueStream.str tostring tail[1] - tail[1] - } - inner\eval scope - -print = ValueStream.meta - meta: - name: 'print' - summary: "Print string values." - examples: { '(print str)' } - - value: class extends Op - setup: (inputs) => - value = (val.str / evt.str)\match inputs - super value: Input.hot value - - tick: => - if @inputs.value\metatype! == 'event' - for msg in *@inputs.value! - print msg - else - print @inputs.value! - -{ - :doc - :trace, 'trace!': trace_, :print - - :def, :use - require: require_ - import: import_ - 'import*': import_star - - true: ValueStream.meta - meta: - name: 'true' - summary: "The boolean constant `true`." - value: ValueStream.bool true - - false: ValueStream.meta - meta: - name: 'false' - summary: "The boolean constant `false`." - value: ValueStream.bool false - - bang: ValueStream.meta - meta: - name: 'bang' - summary: "A `bang` value-constant." - value: ValueStream 'bang', true - - :fn, :defn - 'do': do_expr - if: if_ -} diff --git a/core/cell.moon b/core/cell.moon deleted file mode 100644 index b028f14..0000000 --- a/core/cell.moon +++ /dev/null @@ -1,189 +0,0 @@ ----- --- S-Expression Cell, implements the `AST` interface. --- --- Consists of a head expression and any number of tail expressions (both `AST` --- nodes), a `Tag`, and optionally the internal whitespace as parsed. --- --- @classmod Cell -import ValueStream from require 'core.stream' -import Error from require 'core.error' -import op_invoke, fn_invoke from require 'core.invoke' -import Tag from require 'core.tag' - -local RootCell - -class Cell ---- members --- @section members - - new: (@tag=Tag.blank!, @children, @white) => - if not @white - @white = [' ' for i=1,#@children] - @white[0] = '' - - assert #@white == #@children, "mismatched whitespace length" - - --- get the head of the cell. - -- - -- @treturn AST - head: => @children[1] - - --- get the tail of the cell. - -- - -- @treturn {AST,...} - tail: => [c for c in *@children[2,]] - - __tostring: => @stringify 2 - - --- the parsed Tag. - -- - -- @tfield Tag tag - - --- sequence of child AST Nodes - -- - -- @tfield {AST,...} children - - --- optional sequence of whitespace segments. - -- - -- If set, `whitespace[i]` is the whitespace between `children[i]` and - -- `children[i+1]`, or the closing parenthesis of this Cell. `whitespace[0]` - -- is the space between the opening parenthesis and `children[1]`. - -- - -- @tfield ?{string,...} whitespace - ---- AST interface --- --- `Cell` implements the `AST` interface. --- @section ast - - --- evaluate this Cell. - -- - -- `AST:eval`uates the head of the expression, and finds the appropriate - -- `Builtin` to invoke: - -- - -- - if head is an `opdef`, use `invoke.op_invoke` - -- - if head is a `fndef`, use `invoke.fn_invoke` - -- - if head is a `builtin`, unwrap it - -- - -- then calls `Builtin:eval_cell` on it. - -- - -- @tparam Scope scope the scope to evaluate in - -- @treturn Result the evaluation result - eval: (scope) => - head = assert @head!, Error 'syntax', "cannot evaluate empty expr" - head = (head\eval scope)\const! - Builtin = switch head.type - when 'opdef' - -- scope\get 'op-invoke' - op_invoke - when 'fndef' - -- scope\get 'fn-invoke' - fn_invoke - when 'builtin' - head\unwrap! - else - error Error 'type', "#{head} is not an opdef, fndef or builtin" - - Builtin\eval_cell @, scope, head - - --- quote this Cell, preserving its identity. - -- - -- Recursively quotes children, but preserves identity (i.e, shares the - -- `Tag`). A quoted Cell may only be 'used' once. If you want to `eval` a - -- `Cell` multiple times, use `clone`. - -- - -- @treturn Cell - quote: => - children = [child\quote scope for child in *@children] - Cell @tag, children, @white - - --- create a clone with its own identity. - -- - -- creates a clone of this Cell with its own identity by prepending a `parent` - -- to `tag` and cloning all child expressions recursively. - -- - -- @tparam Tag parent - -- @treturn Cell - clone: (parent) => - tag = @tag\clone parent - children = [child\clone parent for child in *@children] - Cell tag, children, @white - - --- stringify this Cell. - -- - -- if `depth` is passed, does not faithfully recreate the original string but - -- rather create useful debug output. - -- - -- @tparam[opt] int depth the maximum depth, defaults to infinite - -- @treturn string the exact string this Cell was parsed from, unless `@tag` - -- changed - stringify: (depth=-1) => - buf = '' - buf ..= if depth > 0 then '' else @white[0] - if depth == 0 - buf ..= '...' - else - for i, child in ipairs @children - buf ..= child\stringify if depth == -1 then -1 else depth - 1 - buf ..= if depth > 0 then ' ' else @white[i] - - if depth > 0 - buf = buf\sub 1, #buf - 1 - - tag = if depth == -1 then @tag\stringify! else '' - - '(' .. tag .. buf .. ')' - ---- static functions --- @section static - - parse_args = (tag, parts) -> - if not parts - parts, tag = tag, nil - - children, white = {}, { [0]: parts[1] } - - for i = 2,#parts,2 - children[i/2] = parts[i] - white[i/2] = parts[i+1] - - tag, children, white - --- parse a Cell (for parsing with Lpeg). - -- - -- @tparam[opt] Tag tag - -- @tparam table parts - -- @treturn Cell - @parse: (...) -> - tag, children, white = parse_args ... - Cell tag, children, white - - --- parse a root Cell (for parsing with Lpeg). - -- - -- Root-Cells are at the root of an ALV document. - -- They have an implicit head of 'do' and a `[0]` tag. - -- - -- @tparam table parts - -- @treturn Cell - @parse_root: (...) -> - tag, children, white = parse_args (Tag.parse '0'), ... - RootCell tag, children, white - --- @type RootCell -class RootCell extends Cell - head: => ValueStream.sym 'do' - tail: => @children - - stringify: => - buf = '' - buf ..= @white[0] - - for i, child in ipairs @children - buf ..= child\stringify! - buf ..= @white[i] - - buf - -{ - :Cell - :RootCell -} diff --git a/core/config.ld b/core/config.ld deleted file mode 100644 index f00deba..0000000 --- a/core/config.ld +++ /dev/null @@ -1,16 +0,0 @@ -project = 'alive internals' -title = 'developer docs' - -description = "`alive` developer documentation" -full_description = [[This section documents the *implementation* of the alive -language and copilot. It is relevant to everyone who is looking to modify, -improve or extend alive with new modules, language or interpreter features. - -If you are looking for the language reference for users, head over to the -[reference](../reference/index.html) section of the documentation.]] - -format = 'discount' -style = 'docs' -template = 'docs' -topics={'core/extensions.md'} -dir = 'docs/internals' diff --git a/core/cycle.moon b/core/cycle.moon deleted file mode 100644 index 57d4a75..0000000 --- a/core/cycle.moon +++ /dev/null @@ -1,26 +0,0 @@ --- late-resolve cyclic dependencies --- --- this module provides a proxy for resolving values from modules which cannot --- be loaded due to cyclic dependencies. Instead of --- --- import Something from require 'core.somewhere' --- Something ... --- --- use --- --- import somewhere from require 'core.cycle' --- somewhere.Something ... --- --- Make sure cycle:load() is called before you access or dereference --- `somewhere`. - -load = => - for name, module in pairs @ - for k, v in pairs require "core.#{name}" - module[k] = v - -setmetatable {}, __index: (key) => - return load if key == 'load' - - with v = {} - rawset @, key, v diff --git a/core/error.moon b/core/error.moon deleted file mode 100644 index e72cc5f..0000000 --- a/core/error.moon +++ /dev/null @@ -1,94 +0,0 @@ ----- --- Language error and traceback. --- --- @classmod Error - -unpack or= table.unpack - -class Error ---- members --- @section members - - --- append a traceback frame. - -- - -- `where` should denote where the Error occured and fit grammatically to - -- complete the sentence `"{error} occured while {where}"` - -- - -- @tparam string where - add_frame: (where) => @trace ..= "\n while #{where}" - - __tostring: => - str = "#{@kind} error: #{@message}" - if @detail - str ..= "\n#{@detail}" - if @trace - str ..= @trace - str - ---- static functions --- @section static - - --- create a new Error. - -- - -- `kind` should be one of: - -- - -- - `'reference'`: error concerning symbol resolution - -- - `'argument'`: error concerning Op argument matching - -- - `'implementation'`: error in the Lua/MoonScript implementation of alive. - -- Should not occur in normal operation, and constitutes a bug. - -- - -- @classmethod - -- @tparam string kind - -- @tparam string message - -- @tparam ?string detail - new: (@kind, @message, @detail) => - @trace = '' - - handler = (err) -> - if err.__class == Error - err - else - Error 'implementation', err, debug.traceback "Lua error below:", 2 - --- Wrap function errors in a traceback frame. - -- - -- Execute `fn(...)`, and turn any error thrown as a result into an - -- `Error` instance, before re-throwing it. - -- - -- When `Error` instances are caught, `frame` is added to the traceback. - -- All other error values are turned into `'implementation'` Errors. - -- - -- @tparam string frame - -- @tparam function fn - @wrap: (frame, fn, ...) -> - results = { xpcall fn, handler, ... } - ok = table.remove results, 1 - if ok - unpack results - else - error with results[1] - \add_frame frame - - --- Capture and wrap function errors in traceback frame. - -- - -- Execute `fn(...)`, and turn any error thrown as a result into an - -- `Error` instance, before re-throwing it. - -- - -- When `Error` instances are caught, `frame` is added to the traceback. - -- All other error values are turned into `'implementation'` Errors. - -- - -- @tparam string frame - -- @tparam function fn - -- @treturn boolean `ok` true if exeuction suceeded without errors - -- @treturn Error|any `error_or_results` the `Error` instance or results - @try: (frame, fn, ...) -> - results = { xpcall fn, handler, ... } - ok = table.remove results, 1 - if ok - ok, unpack results - else - ok, with results[1] - \add_frame frame - -{ - :Error -} diff --git a/core/extensions.md b/core/extensions.md deleted file mode 100644 index 6af9bca..0000000 --- a/core/extensions.md +++ /dev/null @@ -1,221 +0,0 @@ -# writing `alive` extensions - -Extensions for `alive` are implemented in [Lua][lua] or [MoonScript][moonscript] -(which runs as Lua). When an `alive` module is [`(require)`][builtins-req]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 extensions, 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 `ValueStream.meta`, -which attaches a `meta` table to the value that is used for error messages, -documentation generation and [`(doc)`][builtins-doc]. - - import ValueStream from require 'core.base' - - two = ValueStream.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 extensions 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 ValueStream, Op, Input, evt from require 'core.base' - - total_sum = ValueStream.meta - meta: - name: 'total-sum' - summary: "Keep a total of incoming numbers." - examples: { '(total-sum num!)' } - description: "Keep a total sum of incoming number events, extension-style." - - value: class extends Op - new: (...) => - super ... - @state or= { total: 0 } - @out or= ValueStream 'num', @state.total - - setup: (inputs, scope) => - num = evt.num\match inputs - super num: Inputs.hot num - - tick: => - @state.total += @inputs.num! - @out\set @state.total - - { - 'total-sum': total_sum - } - -### 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 `base.match`. The two exports `base.match.val` -and `base.match.evt` are used to build complex patterns that can parse and -validate the Op arguments into complex structures (see the module documentation -for more information). - - import val, evt from require 'core.base' - - pattern = evt.bang + val.str + val.num*3 + -evt! - { trig, str, numbers, optional } = pattern\match inputs - -This example matches first an `EventStream` of type `bang`, then a `ValueStream` -of type `str`, followed by one, two or three `num`-values and finally an -optional argument `EventStream` of any type. `:match` will throw an error if it -couldn't (fully) match the arguments and otherwise return a structured mapping -of the inputs. - -If there are more complex dependencies between arguments, it is recommended to -do as much of the parsing as possible using the `base.match` and then continue -manually. For invalid or missing arguments, `Error` instances should be thrown -using `error` or `assert`. - -#### input setup -There are two types of inputs: `Input.hot` and `Input.cold`: - -*Cold* inputs do not cause the Op to update when changes to the input stream -are made. They are useful to 'ignore' changes to inputs which are only relevant -when another input changed value. Imagine for example a `send-value-when` Op, -which sends a value only when a `bang!` input is live. This Op doesn't have to -update when the value changes, it's enough to update only when the trigger -input changes and simply read the value in that moment. - -*Hot* inputs on the other hand mark the input stream as a dependency for the -Op. Depending on the type of `Stream`, the semantics are a little different: - -- For `ValueStream`s, the Op updates whenever the current value changes. When - an input stream is swapped out for another one at evaltime, but their values - are momentarily equal, the input is not considered dirty. -- For `EventStream`s and `IOStream`s, the Op updates whenever the stream is - dirty. There is no special handling when the stream is swapped out at - evaltime. - -All `Result`s from the `inputs` argument that are taken into consideration -should be wrapped in an `Input` instance using either `Input.hot` or -`Input.cold`, and need to be passed to the `Op:setup` super implementation. -To illustrate with the `send-value-when` example: - - setup: (inputs, scope) => - { trig, value } = match 'bang! any', inputs - - super - trig: Inputs.hot trig - value: Inputs.cold value - -`Op:setup` takes a table that can have any (even nested) shape you want, as -long as all 'leaf values' are `Input` instances. The following are both valid: - - super { (Inputs.hot trig), (Inputs.cold value) } - - super - trigger: Inputs.hot trig - values: { (Inputs.cold a), (Inputs.cold b), (Inputs.cold c) } - -#### output setup -When `Op:setup` finishes, `@out` has to be set to a `Stream` instance. The -instance can be created in `Op:setup`, or by overriding the constructor and -delegating to the original one using `super`. In general setting it in the -constructor is preferred, and it is only moved to `Op:setup` if the output -type depends on the arguments received. - -There are three types of `Stream`s that can be created: - -- `ValueStream`s track *continuous values*. They can only have one value per - tick, and downstream Ops will not update when a *ValueStream* has been set - to the same value it already had. They are updated using `ValueStream:set`. -- `EventStream`s transmit *momentary events*. They can transmit multiple events - in a single tick. `EventStream`s do not keep a value set on the last tick on - the next tick. They are updated using `EventStream:add`. -- `IOStream`s are like `EventStream`s, but their `IOStream:tick` method is - polled by the event loop at the start of every tick. This gives them a chance - to effectively create changes 'out of thin air' and kickstart the execution - of the dataflow engine. All *runtime* execution is due to an `IOStream` - becoming dirty somewhere. See the section on implementing `IOStream`s below - for more information. - -### 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 `Builtin`s -Builtins are more powerful than Ops, because they control whether, which and -how their arguments are evaluated. They roughly correspond to *macros* in Lisps. -There is less of a concrete guideline for implementing Builtins because there -are a lot more options, and it really depends a lot on what the Builtin should -achieve. Nevertheless, a good starting point is to read the `Builtin` class -documentation, take a look at `Builtin`s in `core/builtin.moon` and get -familiar with the relevant internal interfaces (especially `AST`, `Result`, and -`Scope`). - -## defining `IOStream`s -`IOStream`s are `EventStream`s that can 'magically' create events out of -nothing. They are the source of all processing in alive. Whenever you want to -bring events into alive from an external protocol or application, an IOStream -will be necessary. - -To implement a custom IOStream, create it as a class that inherits from the -`IOStream` base and implement the constructor and `IOStream:tick`: - - import IOStream from require 'core.base' - - class UnreliableStream extends IOStream - new: => super 'bang' - - tick: => - if math.random! < 0.1 - @add true - -In the constructor, you should call the super-constructor `EventStream.new` to -set the event type. Often this will be a custom event that is only used inside -your extension (such as e.g. the `midi/port` type in the [midi][modules-midi] -module), but it can also be a primitive type like `'num'` in this example. In -`:tick`, your IOStream is given a chance to communicate with the external world -and create any resulting events. The example stream above randomly sends bang -events out, with a 10% chance each 'tick' of the system. Note that there is no -guarantee about when or how often ticks occur, so you really shouldn't rely on -them this way in a real extension. - -### using `IOStream`s -There's a couple of ways IOStreams can be used and exposed to the user of your -extension. You can either expose an instance of your IOStream directly -(documented using `ValueStream.meta`), or offer an Op that creates and returns -an instance in `Op.out` - that way the IOStream can be created only on demand -and take parameters. It is also possible to not exepose the IOStream at all, -and rather pass it as a hardcoded input into an Op's `Op.inputs`. - -[lua]: https://www.lua.org/ -[moonscript]: http://moonscript.org/ -[builtins-req]: ../../reference/index.html#require -[builtins-doc]: ../../reference/index.html#doc -[modules-midi]: ../../reference/midi.html diff --git a/core/init.moon b/core/init.moon deleted file mode 100644 index 6e23fa7..0000000 --- a/core/init.moon +++ /dev/null @@ -1,55 +0,0 @@ ----- --- `alive` public API. --- --- @module init -L or= setmetatable {}, __index: => -> - -import ValueStream, EventStream, IOStream from require 'core.stream' -import Result from require 'core.result' -import Scope from require 'core.scope' -import Error from require 'core.error' -import Registry, SimpleRegistry from require 'core.registry' -import Tag from require 'core.tag' - -import Cell from require 'core.cell' -import cell, program from require 'core.parsing' - -with require 'core.cycle' - \load! - -globals = Scope.from_table require 'core.builtin' - ---- exports --- @table exports --- @tfield ValueStream ValueStream --- @tfield EventStream EventStream --- @tfield IOStream IOStream --- @tfield Result Result --- @tfield Cell Cell --- @tfield RootCell RootCell --- @tfield Scope Scope --- @tfield Error Error --- @tfield Registry Registry --- @tfield Tag Tag --- @tfield Scope globals global definitons --- @tfield parse function to turn a `string` into a root `Cell` -{ - :ValueStream, :EventStream, :IOStream - :Cell, :RootCell - :Result, :Scope, :Error - - :Registry, :SimpleRegistry, :Tag - - :globals - - parse: (str) -> - assert (program\match str), Error 'syntax', "failed to parse" - - eval: (str, inject) -> - scope = Scope nil, globals - scope\use inject if inject - - ast = assert (program\match str), "failed to parse" - result = ast\eval scope - result\const! -} diff --git a/core/invoke.moon b/core/invoke.moon deleted file mode 100644 index 8fcb525..0000000 --- a/core/invoke.moon +++ /dev/null @@ -1,124 +0,0 @@ ----- --- Builtins for invoking `Op`s and `FnDef`s. --- --- @module invoke -import Result from require 'core.result' -import Builtin from require 'core.base' -import Scope from require 'core.scope' -import Error from require 'core.error' - -get_name = (value, raw) -> - meta = if value.meta then value.meta.name - locl = if raw and raw.type == 'sym' then raw! - - if locl - if meta and meta != locl - "'#{meta}' (local '#{locl}')" - else - "'#{locl}'" - else if meta - "'#{meta}'" - else - "(unnamed)" - ---- `Builtin` implementation that invokes an `Op`. --- --- @type op_invoke -class op_invoke extends Builtin - --- `Builtin:setup` implementation. - -- - -- `Op:fork`s the `prev`'s `Op` instance if given. Creates a new instance - -- otherwise. - setup: (prev) => - if prev - @op = prev.op\fork! - else - def = @head\unwrap 'opdef', "cant op-invoke #{@head}" - @op = def! - - --- `Builtin:destroy` implementation. - -- - -- calls `op`:@{Op:destroy|destroy}. - destroy: => @op\destroy! - - --- evaluate an `Op` invocation. - -- - -- `AST:eval`s the tail, and passes the result to `op`:@{Op:setup|setup}. Then - -- checks if any of `op`:@{Op:all_inputs|all_inputs} are @{Input:dirty|dirty}, - -- and if so, calls `op`:@{Op:tick|tick}. - -- - -- The `Result` contains `op`, `Op.value` and all the `Result`s from the tail. - -- - -- @tparam Scope scope the active scope - -- @tparam {AST,...} tail the arguments to this expression - -- @treturn Result - eval: (scope, tail) => - children = [L\push expr\eval, scope for expr in *tail] - - frame = "invoking op #{get_name @head, @cell\head!} at [#{@tag}]" - Error.wrap frame, @op\setup, [result for result in *children], scope - - any_dirty = false - for input in @op\all_inputs! - if input\dirty! - any_dirty = true - break - - if any_dirty - @op\tick true - - for input in @op\all_inputs! - input\finish_setup! - - Result :children, value: @op.out, op: @op - - --- The `Op` instance. - -- - -- @tfield Op op - ---- `Builtin` implementation that invokes a `FnDef`. --- --- @type fn_invoke -class fn_invoke extends Builtin - --- evaluate a user-function invocation. - -- - -- Creates a new `Scope` that inherits from `FnDef.scope` and has - -- `outer_scope` as an additional parent for dynamic symbol resolution. - -- Then `AST:eval`s the tail in `outer_scope`, and defines the results to the - -- names in `FnDef.params` in the newly created scope. Lastly, `AST:clone`s - -- `FnDef.body` with the prefix `Builtin.tag`, and `AST:eval`s it in the newly - -- created `Scope`. - -- - -- The `Result` contains the `Stream` from the cloned AST, and its children - -- are all the `Result`s from evaluating the tail as well as the cloned - -- `AST`s. - -- - -- @tparam Scope outer_scope the active scope - -- @tparam {AST,...} tail the arguments to this expression - -- @treturn Result the result of this evaluation - eval: (outer_scope, tail) => - name = get_name @head, @cell\head! - frame = "invoking function #{name} at [#{@tag}]" - - { :params, :body, :scope } = @head\unwrap 'fndef', "cant fn-invoke #{@head}" - if #params != #tail - err = Error 'argument', "expected #{#params} arguments, found #{#tail}" - err\add_frame frame - error err - - fn_scope = Scope scope, outer_scope - - children = for i=1,#params - name = params[i]\unwrap 'sym' - with L\push tail[i]\eval, outer_scope - fn_scope\set name, \make_ref! - - clone = body\clone @tag - result = Error.wrap frame, clone\eval, fn_scope - - table.insert children, result - Result :children, value: result.value - -{ - :op_invoke, :fn_invoke -} diff --git a/core/parsing.moon b/core/parsing.moon deleted file mode 100644 index aadcab4..0000000 --- a/core/parsing.moon +++ /dev/null @@ -1,73 +0,0 @@ ----- --- Lpeg Grammar for parsing `alive` code. --- --- @module parsing -import ValueStream from require 'core.stream' -import Cell from require 'core.cell' -import Tag from require 'core.tag' -import R, S, P, V, C, Ct from require 'lpeg' - --- whitespace -wc = S ' \t\r\n' -comment = P { - 'comment', - expr: (P '(') * ((V 'expr') + (1 - P ')'))^0 * (P ')') - comment: (P '#(') * ((V 'expr') + (1 - P ')'))^0 * (P ')') -} -space = (wc^1 * (comment * wc^1)^0) / 1 -- required whitespace -mspace = (comment + wc)^0 / 1 -- optional whitespace - --- atoms -digit = R '09' -first = (R 'az', 'AZ') + S '-_+*/.!?=%' -sym = first * (first + digit)^0 / ValueStream\parse 'sym' - -strd = '"' * (C ((P '\\"') + (P '\\\\') + (1 - P '"'))^0) * '"' / ValueStream\parse 'str', '\"' -strq = "'" * (C ((P "\\'") + (P '\\\\') + (1 - P "'"))^0) * "'" / ValueStream\parse 'str', '\'' -str = strd + strq - -int = digit^1 -float = (digit^1 * '.' * digit^0) + (digit^0 * '.' * digit^1) -num = ((P '-')^-1 * (float + int)) / ValueStream\parse 'num' - -atom = num + sym + str - -expr = (V 'cell') + atom -explist = Ct mspace * ((V 'expr') * (space * (V 'expr'))^0 * mspace)^-1 - -tag = (P '[') * (digit^1 / Tag.parse) * (P ']') -cell = (P '(') * tag^-1 * (V 'explist') * (P ')') / Cell.parse - -root = P { - (V 'explist') / Cell.parse_root - :expr, :explist, :cell -} - -cell = P { - 'cell' - :expr, :explist, :cell -} - -program = root * -1 - ---- exports --- @table exports --- @tfield pattern comment --- @tfield pattern space --- @tfield pattern atom --- @tfield pattern expr --- @tfield pattern explist --- @tfield pattern explist --- @tfield pattern cell --- @tfield pattern root --- @tfield pattern program the main parsing entrypoint -{ - :comment - :space - :atom - :expr - :explist - :cell - :root - :program -} diff --git a/core/registry.moon b/core/registry.moon deleted file mode 100644 index a0134ec..0000000 --- a/core/registry.moon +++ /dev/null @@ -1,145 +0,0 @@ ----- --- `Tag` Registry. --- --- @classmod Registry -import Result from require 'core.result' -import Error from require 'core.error' - -class Registry ---- internals for `Tag` --- @section internals - - --- lookup the last registration. - -- - -- @tparam number|string index the registration index - -- @treturn any - last: (index) => @last_map[index] - - --- set the current registration. - -- - -- @tparam string\number index the registration index - -- @tparam any expr the registration value - -- @tparam[default=false] boolean ignore_dup ignore duplicate registrations - register: (index, expr, ignore_dup=false) => - L\trace "reg: setting #{index} to #{expr}" - if not ignore_dup and @map[index] - error Error 'tag', "duplicate tags [#{index}]!" - @map[index] = expr - - --- request identity and registration for blank tag. - -- - -- @tparam Tag tag the blank tag - -- @tparam any expr the registration value - init: (tag, expr) => - L\trace "reg: init pending to #{expr}" - table.insert @pending, { :tag, :expr } - ---- members --- @section members - - --- begin an evaluation cycle. - -- - -- Begin an evaltime cycle (and tick). - -- Set the active Registry and clear out pending registrations. - -- - -- All calls go `begin_eval` must be matched with either a call to - -- `end_eval` or `rollback_eval`. - begin_eval: => - @latest_map = @last_map - @begin_tick! - @map, @pending = {}, {} - - --- end an evaluation cycle. - -- - -- Register all pending `Tag`s and destroy all orphaned registrations. - -- Unset the active Registry. - end_eval: => - for tag, val in pairs @last_map - val\destroy! unless @map[tag] - - for { :tag, :expr } in *@pending - -- tag was solved by another pending registration - -- (e.g. first [A] is solved, then [5.A] is solved) - continue if tag\index! - - next_tag = #@map + 1 - L\trace "assigned new tag #{next_tag} to #{tag} #{expr}" - tag\set next_tag - @map[tag\index!] = expr - - @last_map = @map - @end_tick! - - --- abort an evaluation cycle. - -- - -- Unset the active Registry. - rollback_eval: => - @end_tick! - - --- begin a run cycle. - -- - -- Increment the tick index and set the active Registry. - begin_tick: => - @grab! - @next_tick! - - --- end a run cycle. - -- - -- Unset the active Registry. - end_tick: => - @release! - - --- manually increment the tick index (for testing). - next_tick: => - @tick += 1 - - --- set the active Registry. - grab: => - assert not @prev, "already have a previous registry? #{@prev}" - @prev, Registry.active_registry = Registry.active_registry, @ - - --- unset the active Registry. - release: => - assert @ == Registry.active_registry, "not the active registry!" - Registry.active_registry, @prev = @prev, nil - ---- static functions --- @section static - - --- create a new Registry. - -- @classmethod - new: => - @last_map, @map = {}, {} - @tick = 0 - - --- get the active Registry. - -- - -- Raises an erro when there is no active Regsitry. - -- - -- @treturn Registry - @active: -> assert Registry.active_registry, "no active Registry!" - -class SimpleRegistry extends Registry - new: => - @cnt = 1 - @tick = 0 - - next_tick: => - @tick += 1 - - init: (tag, expr) => - tag\set @cnt - @cnt += 1 - - last: (index) => - register: (index, expr) => - - wrap: (fn) => (...) -> - @grab! - with fn ... - @release! - -{ - :Registry - :SimpleRegistry -} diff --git a/core/result.moon b/core/result.moon deleted file mode 100644 index 6c994cb..0000000 --- a/core/result.moon +++ /dev/null @@ -1,126 +0,0 @@ ----- --- Result of evaluating an expression. --- --- `Result`s form a tree that controls execution order and message passing --- between `Op`s. --- --- @classmod Result -class Result ---- members --- @section members - - --- return whether this Result's value is const. - is_const: => not next @side_inputs - - --- assert value-constness and return the value. - -- @tparam[opt] string msg the error message to throw - -- @treturn any - const: (msg) => - assert not (next @side_inputs), msg or "eval-time const expected" - @value - - --- assert this result has a value, return its type. - -- @treturn string - type: => - assert @value, "Result with value expected" - @value.type - - --- assert this result has a value, returns its metatype. - -- @treturn string `"value"` or `"event"` - metatype: => - assert @value, "Result with value expected" - @value.metatype - - --- create a copy of this result with value-copy semantics. - -- the copy has the same @value and @side_inputs, but will not update - -- anything on \tick. - make_ref: => - with Result value: @value - .side_inputs = @side_inputs - - --- tick all IOStream instances that are effecting this (sub)tree. - -- should be called once per frame on the root, right before tick. - tick_io: => - for stream, input in pairs @side_inputs - stream\tick! if input.io - - --- in depth-first order, tick all Ops which have dirty Inputs. - -- - -- short-circuits if there are no dirty Inputs in the entire subtree - tick: => - any_dirty = false - for stream, input in pairs @side_inputs - if input\dirty! - any_dirty = true - break - - -- early-out if no Inputs are dirty in this whole subtree - return unless any_dirty - - for child in *@children - child\tick! - - if @op - -- we have to check self_dirty here, because Inputs from children may - -- have become dirty due to \tick - self_dirty = false - for stream in @op\all_inputs! - if stream\dirty! - self_dirty = true - - return unless self_dirty - - @op\tick! - - __tostring: => - buf = "<result=#{@value}" - buf ..= " #{@op}" if @op - buf ..= " (#{#@children} children)" if #@children > 0 - buf ..= ">" - buf - - --- the `Stream` result - -- - -- @tfield ?Stream value - - --- an Op - -- - -- @tfield ?Op op - - --- list of child `Result`s from subexpressions - -- - -- @tfield {}|{Result,...} children - - --- cached mapping of all `Stream`/`Input` pairs affecting this Result. - -- - -- This is the union of all `children`s `side_inputs` and all `Input`s from - -- `op` that are not the `value` of any child. - -- - -- @tfield {[Stream]=Input,...} side_inputs - ---- static functions --- @section static - - --- create a new Result. - -- @classmethod - -- @param params table with optional keys op, value, children. default: {} - new: (params={}) => - @value = params.value - @op = params.op - @children = params.children or {} - - @side_inputs, is_child = {}, {} - for child in *@children - for stream, input in pairs child.side_inputs - @side_inputs[stream] = input - if child.value - is_child[child.value] = true - - if @op - for input in @op\all_inputs! - if input.io or not is_child[input.stream] - @side_inputs[input.stream] = input - -{ - :Result -} diff --git a/core/scope.moon b/core/scope.moon deleted file mode 100644 index 526ca8d..0000000 --- a/core/scope.moon +++ /dev/null @@ -1,118 +0,0 @@ ----- --- Mapping from `sym`s to `Result`s. --- --- @classmod Scope -import ValueStream from require 'core.stream' -import Result from require 'core.result' -import Error from require 'core.error' - -class Scope ---- members --- @section members - - --- set a Lua value in the scope. - -- - -- wraps `val` in a `ValueStream` and `Result` before calling `set`. - -- - -- @tparam string key - -- @tparam any val - set_raw: (key, val) => - value = ValueStream.wrap val, key - @values[key] = Result :value - - --- set a symbol to a `Result`. - -- - -- @tparam string key - -- @tparam Result val - set: (key, val) => - L\trace "setting #{key} = #{val} in #{@}" - assert val.__class == Result, "expected #{key}=#{val} to be Result" - assert val.value, Error 'type', "cannot define symbol to nil" - assert not @values[key], Error 'type', "cannot redefine symbol '#{key}'!" - @values[key] = val - - recurse: (key) => - parent = if key\match '^%*.*%*$' then @dynamic_parent else @parent - parent or= @parent - if parent - L\push parent\get, key - else - error Error 'reference', "undefined symbol '#{key}'" - - --- resolve a key in this Scope. - -- - -- @tparam string key the key to resolve - -- @treturn ?Result the value of the definition that was found, or `nil` - get: (key) => - L\debug "checking for #{key} in #{@}" - if val = @values[key] - L\trace "found #{val} in #{@}" - return val - - start, rest = key\match '^(.-)/(.+)' - if not start - return @recurse key - - child = @get start - if not child - error Error 'reference', "undefined symbol '#{start}'" - if child\type! != 'scope' - error Error 'reference', "'#{start}' is not a scope" - child.value!\get rest, while_msg - - --- copy definitions from another scope. - -- - -- copies all definitions from `other`. Does not copy inherited definitions. - -- - -- @tparam Scope other - use: (other) => - L\trace "using defs from #{other} in #{@}" - for k, v in pairs other.values - @values[k] = v - - __tostring: => - buf = "<Scope" - - depth = -1 - parent = @parent - while parent - depth += 1 - parent = parent.parent - buf ..= " ^#{depth}" if depth != 0 - - keys = [key for key in pairs @values] - if #keys > 5 - keys = [key for key in *keys[,5]] - keys[6] = '...' - buf ..= " [#{table.concat keys, ', '}]" - - buf ..= ">" - buf - ---- static functions --- @section static - - --- create a new Scope. - -- - -- @classmethod - -- @tparam[opt] Scope parent a parent this scope inherits definitions from - -- @tparam[opt] Scope dynamic_parent a parent scope that should be checked for - -- dynamic definitions - new: (@parent, @dynamic_parent) => - @values = {} - - --- convert a Lua table to a Scope. - -- - -- `tbl` may contain more tables (or `Scope`s). - -- Uses `ValueStream.wrap` on the values recursively. - -- - -- @tparam table tbl the table to convert - -- @treturn Scope - @from_table: (tbl) -> - with Scope! - for k, v in pairs tbl - \set_raw k, v - -{ - :Scope -} diff --git a/core/stream/base.moon b/core/stream/base.moon deleted file mode 100644 index 37cc8a4..0000000 --- a/core/stream/base.moon +++ /dev/null @@ -1,68 +0,0 @@ ----- --- base Stream interface. --- --- implemented by `ValueStream`, `EventStream`, and `IOStream`. --- --- @classmod Stream - -class Stream ---- Stream interface. --- --- Methods that have to be implemented by `Stream` implementations --- (`ValueStream`, `EventStream`, `IOStream`). --- --- @section interface - - --- return whether this Stream was changed in the current tick. - -- - -- @function dirty - -- @treturn boolean - - --- create a mutable copy of this Stream. - -- - -- Used to insulate eval-cycles from each other. - -- - -- @function fork - -- @treturn Stream - - --- the type name of this Stream's value. - -- - -- the following builtin typenames are used: - -- - -- - `str` - strings, `value` is a Lua string - -- - `sym` - symbols, `value` is a Lua string - -- - `num` - numbers, `value` is a Lua number - -- - `bool` - booleans, `value` is a Lua boolean - -- - `bang` - trigger signals, `value` is a Lua boolean - -- - `opdef` - `value` is an `Op` subclass - -- - `builtin` - `value` is a `Builtin` subclass - -- - `fndef` - `value` is a `FnDef` instance - -- - `scope` - `value` is a `Scope` instance - -- - -- @tfield string type - - --- documentation metadata. - -- - -- an optional table containing metadata for error messages and - -- documentation. The following keys are recognized: - -- - -- - `name`: optional name - -- - `summary`: single-line description (markdown) - -- - `examples`: optional list of single-line code examples - -- - `description`: optional full-text description (markdown) - -- - -- @tfield ?table meta - ---- static functions --- @section static - - --- construct a new Stream. - -- - -- @classmethod - -- @tparam string type the type name - -- @tparam ?table meta the `meta` table - new: (@type, @meta={}) => - -{ - :Stream -} diff --git a/core/stream/event.moon b/core/stream/event.moon deleted file mode 100644 index 7712fc4..0000000 --- a/core/stream/event.moon +++ /dev/null @@ -1,100 +0,0 @@ ----- --- Stream of momentary events. --- --- @classmod EventStream -import Stream from require 'core.stream.base' -import Result from require 'core.result' -import Error from require 'core.error' -import scope, base, registry from require 'core.cycle' - -class EventStream extends Stream ---- members --- @section members - - --- return whether this stream was changed in the current tick. - -- - -- @treturn bool - dirty: => @updated == registry.Registry.active!.tick - - --- push an event value into the stream. - -- - -- Marks this stream as dirty for the remainder of the current tick. - -- - -- @tparam any event - add: (event) => - if not @dirty! - @events = {} - - @updated = registry.Registry.active!.tick - table.insert @events, event - - --- get the sequence of current events (if any). - -- - -- Returns `events` if `dirty`, or an empty table otherwise. - -- Asserts `@type == type` if `type` is given. - -- - -- @tparam[opt] string type the type to check for - -- @tparam[optchain] string msg message to throw if type don't match - -- @treturn {any,...} `events` - unwrap: (type, msg) => - assert type == @type, msg or "#{@} is not a #{type}" if type - if @dirty! then @events else {} - - --- create a mutable copy of this stream. - -- - -- Used to wrap insulate eval-cycles from each other. - -- - -- @treturn EventStream - fork: => @@ @type - - --- alias for `unwrap`. - __call: (...) => @unwrap ... - - __tostring: => - "<#{@@__name} #{@type}>" - - --- Stream metatype. - -- - -- @tfield string metatype - metatype: 'event' - - --- the type name of the stream. - -- - -- the following builtin typenames are used: - -- - -- - `str` - strings, `value` is a Lua string - -- - `sym` - symbols, `value` is a Lua string - -- - `num` - numbers, `value` is a Lua number - -- - `bool` - booleans, `value` is a Lua boolean - -- - `bang` - trigger signals, `value` is a Lua boolean - -- - `opdef` - `value` is an `Op` subclass - -- - `builtin` - `value` is a `Builtin` subclass - -- - `fndef` - `value` is a `FnDef` instance - -- - `scope` - `value` is a `Scope` instance - -- - -- @tfield string type - - --- documentation metadata. - -- - -- an optional table containing metadata for error messages and - -- documentation. The following keys are recognized: - -- - -- - `name`: optional name - -- - `summary`: single-line description (markdown) - -- - `examples`: optional list of single-line code examples - -- - `description`: optional full-text description (markdown) - -- - -- @tfield ?table meta - ---- static functions --- @section static - - --- construct a new EventStream. - -- - -- @classmethod - -- @tparam string type the type name - new: (type) => super type - -{ - :EventStream -} diff --git a/core/stream/init.moon b/core/stream/init.moon deleted file mode 100644 index 3fff136..0000000 --- a/core/stream/init.moon +++ /dev/null @@ -1,18 +0,0 @@ ----- --- `Stream` interface and implementations. --- --- @see Stream --- @see ValueStream --- @see EventStream --- @see IOStream --- --- @module stream -import ValueStream from require 'core.stream.value' -import EventStream from require 'core.stream.event' -import IOStream from require 'core.stream.io' - -{ - :ValueStream - :EventStream - :IOStream -} diff --git a/core/stream/io.moon b/core/stream/io.moon deleted file mode 100644 index e3e59a2..0000000 --- a/core/stream/io.moon +++ /dev/null @@ -1,55 +0,0 @@ ----- --- Stream of external side-effects. --- --- Unlike other `Stream`s, this is not updated/set by an `Op` instace, but is --- continuously polled for changes by the runtime and may mark itself as --- *dirty* at any point in time. All runtime execution happens due to IOStream --- updates, which ripple through the `Result` tree. --- --- @classmod IOStream -import EventStream from require 'core.stream.event' - -class IOStream extends EventStream ---- IOStream interface. --- --- methods that have to be implemented by `IOStream` implementations. --- @section interface - - --- construct a new IOStream. - -- - -- Must prepare the instance for `dirty` to be called. - -- The super-constructor should be called to set `Stream.type`. - -- - -- @classmethod - -- @tparam string type the typename of this stream. - new: (type) => super type - - --- create a mutable copy of this stream. - -- - -- Used to wrap insulate eval-cycles from each other. - -- - -- @treturn IOStream - fork: => @ - - --- poll for changes. - -- - -- Called every frame by the main event loop to update internal state. - tick: => - - --- check whether this adapter requires processing. - -- - -- Must return a boolean indicating whether `Op`s that refer to this instance - -- via `Input.hot` should be notified (via `Op:tick`). May be called multiple - -- times. May be called before `tick` on the first frame after construction. - -- - -- If this is not overrided, the `EventStream` interface can be used, see - -- `EventStream.add`, `EventStream.unwrap`, and `EventStream.dirty`. - -- - -- @function dirty - -- @treturn bool whether processing is required - - __inherited: (cls) => cls.__base.__tostring = @__tostring - -{ - :IOStream -} diff --git a/core/stream/value.moon b/core/stream/value.moon deleted file mode 100644 index 68e16b0..0000000 --- a/core/stream/value.moon +++ /dev/null @@ -1,237 +0,0 @@ ----- --- Continuous stream of values. --- --- Implements the `Stream` and `AST` intefaces. --- --- @classmod ValueStream -import Stream from require 'core.stream.base' -import Result from require 'core.result' -import Error from require 'core.error' -import scope, base, registry from require 'core.cycle' - -ancestor = (klass) -> - assert klass, "cant find the ancestor of nil" - while klass.__parent - klass = klass.__parent - klass - -class ValueStream extends Stream ---- members --- @section members - - --- return whether this stream was changed in the current tick. - -- - -- @treturn bool - dirty: => @updated == registry.Registry.active!.tick - - --- update this stream's value. - -- - -- Marks this stream as dirty for the remainder of the current tick. - set: (@value) => @updated = registry.Registry.active!.tick - - --- unwrap to the Lua type. - -- - -- Asserts `@type == type` if `type` is given. - -- - -- @tparam[opt] string type the type to check for - -- @tparam[optchain] string msg message to throw if type don't match - -- @treturn any `value` - unwrap: (type, msg) => - assert type == @type, msg or "#{@} is not a #{type}" if type - @value - - --- create a mutable copy of this stream. - -- - -- Used to insulate eval-cycles from each other. - -- - -- @treturn ValueStream - fork: => - with ValueStream @type, @value, @raw - .updated = @updated - - --- alias for `unwrap`. - __call: (...) => @unwrap ... - - --- compare two values. - -- - -- Compares two `ValueStream`s by comparing their types and their Lua values. - __eq: (other) => other.type == @type and other.value == @value - - __tostring: => - value = if @meta.name - @meta.name - else if 'table' == (type @value) and rawget @value, '__base' - @value.__name - else - tostring @value - "<#{@@__name} #{@type}: #{value}>" - - --- Stream metatype. - -- - -- @tfield string metatype - metatype: 'value' - - --- the type name of this stream. - -- - -- the following builtin typenames are used: - -- - -- - `str` - strings, `value` is a Lua string - -- - `sym` - symbols, `value` is a Lua string - -- - `num` - numbers, `value` is a Lua number - -- - `bool` - booleans, `value` is a Lua boolean - -- - `bang` - trigger signals, `value` is a Lua boolean - -- - `opdef` - `value` is an `Op` subclass - -- - `builtin` - `value` is a `Builtin` subclass - -- - `fndef` - `value` is a `FnDef` instance - -- - `scope` - `value` is a `Scope` instance - -- - -- @tfield string type - - --- the wrapped Lua value. - -- @tfield any value - - --- documentation metadata. - -- - -- an optional table containing metadata for error messages and - -- documentation. The following keys are recognized: - -- - -- - `name`: optional name - -- - `summary`: single-line description (markdown) - -- - `examples`: optional list of single-line code examples - -- - `description`: optional full-text description (markdown) - -- - -- @tfield ?table meta - ---- AST interface --- --- `ValueStream` implements the `AST` interface. --- @section ast - - --- evaluate this literal constant. - -- - -- Throws an error if `type` is not a literal (`num`, `str` or `sym`). - -- Returns an eval-time const result for `num` and `str`. - -- Resolves `sym`s in `scope` and returns a reference to them. - -- - -- @tparam Scope scope the scope to evaluate in - -- @treturn Result the evaluation result - eval: (scope) => - switch @type - when 'num', 'str' - Result value: @ - when 'sym' - Error.wrap "resolving symbol '#{@value}'", scope\get, @value - else - error "cannot evaluate #{@}" - - --- quote this literal constant. - -- - -- @treturn ValueStream self - quote: => @ - - --- stringify this literal constant. - -- - -- Throws an error if `raw` is not set. - -- - -- @treturn string the exact string this stream was parsed from - stringify: => assert @raw, "stringifying ValueStream that wasn't parsed" - - --- clone this literal constant. - -- - -- @treturn ValueStream self - clone: (prefix) => @ - ---- static functions --- @section static - - --- construct a new ValueStream. - -- - -- @classmethod - -- @tparam string type the type name - -- @tparam any value the Lua value to be accessed through `unwrap` - -- @tparam string raw the raw string that resulted in this value. Used by `parsing`. - new: (type, @value, @raw) => super type - - unescape = (str) -> str\gsub '\\([\'"\\])', '%1' - --- create a capture-function (for parsing with Lpeg). - -- - -- @tparam string type the type name (one of `num`, `sym` or `str`) - -- @tparam string sep the seperator char (only for `str`) - @parse: (type, sep) => - switch type - when 'num' then (match) -> @ 'num', (tonumber match), match - when 'sym' then (match) -> @ 'sym', match, match - when 'str' then (match) -> @ 'str', (unescape match), sep .. match .. sep - - --- wrap a Lua value. - -- - -- Attempts to guess the type and wrap a Lua value. - -- - -- @tparam any val the value to wrap - -- @tparam[opt] string name the name of this value (for error logging) - -- @treturn ValueStream - @wrap: (val, name='(unknown)') -> - typ = switch type val - when 'number' then 'num' - when 'string' then 'str' - when 'table' - if rawget val, '__base' - -- a class - switch ancestor val - when base.Op then 'opdef' - when base.Builtin then 'builtin' - else - error "#{name}: cannot wrap class '#{val.__name}'" - elseif val.__class - -- an instance - switch ancestor val.__class - when scope.Scope then 'scope' - when base.FnDef then 'fndef' - when Stream then return val - else - error "#{name}: cannot wrap '#{val.__class.__name}' instance" - else - -- plain table - return ValueStream 'scope', scope.Scope.from_table val - else - error "#{name}: cannot wrap Lua type '#{type val}'" - - ValueStream typ, val - - --- create a constant number. - -- @tparam number num the number - -- @treturn ValueStream - @num: (num) -> ValueStream 'num', num, tostring num - - --- create a constant string. - -- @tparam string str the string - -- @treturn ValueStream - @str: (str) -> ValueStream 'str', str, "'#{str}'" - - --- create a constant symbol. - -- @tparam string sym the symbol - -- @treturn ValueStream - @sym: (sym) -> ValueStream 'sym', sym, sym - - --- create a constant boolean. - -- @tparam boolean bool the boolean - -- @treturn ValueStream - @bool: (bool) -> ValueStream 'bool', bool, tostring bool - - --- wrap and document a value. - -- - -- wraps `args.value` using `wrap`, then assigns `meta`. - -- - -- @tparam table args table with keys `value` and `meta` - -- @treturn ValueStream - @meta: (args) -> - with ValueStream.wrap args.value - .meta = args.meta if args.meta - -class LiteralValue extends ValueStream - eval: => Result value: @ - -{ - :ValueStream - :LiteralValue -} diff --git a/core/tag.moon b/core/tag.moon deleted file mode 100644 index 3c90998..0000000 --- a/core/tag.moon +++ /dev/null @@ -1,122 +0,0 @@ ----- --- Identity provider for `Cell`s and `Builtin`s. --- --- Tags are one of: --- - 'blank' (`[?]`, to be auto-assigned by the Copilot) --- - literal (`[1]`) --- - cloned (`[X.Y]`, obtained by cloning Y with parent X) --- --- @classmod Tag -import Registry from require 'core.registry' - -local ClonedTag - -class DummyReg - destroy: => - - __tostring: => "<dummy>" - -dummy = DummyReg! - -class Tag ---- members --- @section members - - --- obtain the registered value of the last eval-cycle. - -- - -- Obtain the value that was previously registered for this tag on the last - -- eval-cylce. - -- - -- @treturn ?any - last: => - if index = @index! - Registry.active!\last index - - --- register `expr` for this tag for the current eval cycle. - -- - -- Will mark blank tags for auto-assignment at the end of the eval cycle. - -- - -- @tparam any expr the value to register - register: (expr) => - if index = @index! - Registry.active!\register index, expr - else - Registry.active!\init @, expr - - --- create a copy of this tag scoped to a `parent` tag. - -- - -- Will mark blank tags for auto-assignment at the end of the eval cycle. - -- - -- @tparam Tag parent the parent tag - -- @treturn Tag the cloned tag - clone: (parent) => - -- ensure this tag is registered for the current eval cycle, - -- even if it is blank and has no associated value - if index = @index! - Registry.active!\register index, dummy, true - else - Registry.active!\init @, dummy - - assert parent, "need parent to clone!" - ClonedTag @, parent - - stringify: => if @value then "[#{@value}]" else '' - __tostring: => if @value then "#{@value}" else '?' - ---- internals for `Registry` --- @section internals - - new: (@value) => - - --- get a unique index value for this Tag. - -- - -- The index is equal to `value` for simple tags and a path-like string for - -- cloned tags. - -- - -- @treturn ?number|string - index: => @value - - --- callback to set value for blank tags. - -- @tparam number value - set: (value) => - assert not @value, "#{@} is not blank" - @value = value - ---- static functions --- @section static - - --- create a blank `Tag`. - -- - -- @treturn Tag - @blank: -> Tag! - - --- parse a `Tag` (for Lpeg parsing). - -- - -- @tparam string num the number-string - -- @treturn Tag - @parse: (num) -> Tag tonumber num - -class ClonedTag extends Tag - new: (@original, @parent) => - - index: => - orig = @original\index! - parent = @parent\index! - if orig and parent - "#{parent}.#{orig}" - - set: (value) => - assert @parent.value, "cloned tag #{@} set before parent" - @original\set value - - stringify: => error "cant stringify ClonedTag" - - __tostring: => - if @parent - "#{@parent}.#{@original}" - else - tostring @original - -{ - :Tag -} diff --git a/core/version.moon b/core/version.moon deleted file mode 100644 index 6cee1b1..0000000 --- a/core/version.moon +++ /dev/null @@ -1,17 +0,0 @@ ----- --- `alive` source code version information. --- --- @module version - ---- exports --- @table exports --- @tfield string tag the last versions git tag --- @tfield string web the repo web URL --- @tfield string repo the git repo URL --- @tfield string release the web URL of this release -{ - tag: "v0.1-rc2" - web: "https://github.com/s-ol/alivecoding" - repo: "https://github.com/s-ol/alivecoding.git" - release: "https://github.com/s-ol/alivecoding/releases/tag/v0.1-rc2" -} |
