diff options
| author | s-ol <s-ol@users.noreply.github.com> | 2020-03-07 20:58:30 +0000 |
|---|---|---|
| committer | s-ol <s-ol@users.noreply.github.com> | 2020-03-07 20:58:51 +0000 |
| commit | 6a7a2ddaca798f3cccac394d1fb9f317cbe90de6 (patch) | |
| tree | 4e49e53057b39f75813311ede13f87d238061fe6 /core/base.moon | |
| parent | spellcheck (diff) | |
| download | alive-6a7a2ddaca798f3cccac394d1fb9f317cbe90de6.tar.gz alive-6a7a2ddaca798f3cccac394d1fb9f317cbe90de6.zip | |
add ldoc documentation
Diffstat (limited to 'core/base.moon')
| -rw-r--r-- | core/base.moon | 298 |
1 files changed, 0 insertions, 298 deletions
diff --git a/core/base.moon b/core/base.moon deleted file mode 100644 index 7962545..0000000 --- a/core/base.moon +++ /dev/null @@ -1,298 +0,0 @@ --- base definitions for extensions -import Value from require 'core.value' -import Result from require 'core.result' -import match from require 'core.pattern' - -unpack or= table.unpack - --- an incoming side-effect adapter, polled by the main event loop to pump --- events into the dataflow graph. --- --- subclasses must implement this interface: --- --- :new() - construct a new instance --- --- must prepare the instance for :dirty(). --- --- :tick() - poll for changes --- --- called every frame by the event loop to update internal state. --- --- :dirty() - whether this adapter requires processing --- --- must return a boolean indicating whether `Op`s that refer to this instance --- via `IOInput` should be notified (via `Op:tick()`). May be called multiple --- multiple times. May be called before :tick() on the first frame after --- construction. --- -class IO - -- called in the main event loop - tick: => - - -- whether a tree update is necessary - dirty: => - --- a persistent expression Operator --- --- subclasses must implement this interface: --- --- :new() - construct a new instance --- --- the super-constructor can be used to construct a `Value` instance in @out. --- --- :setup(inputs, scope) - 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 `match` to parse them, then delegate to --- super to patch the `Input` instances. --- --- :tick(setup) - handle incoming events and update @out --- --- called once per frame if any inputs are dirty. Some `Input`s (like --- `ValueInput`) have special behaviour immediately after :setup(). You can --- detect this using the `setup` parameter, which is true the first time --- :tick() is called after :setup(). :tick() is not called immediately after --- :setup() if no `@inputs` are dirty. Update @out here. --- --- :destroy() - called when the Op is destroyed --- --- .out - a `Value` instance representing this Op's computed output value. --- --- @out must be set to a `Value` instance once :setup() finishes. @out must --- not change type, be removed or replaced outside of :new() and :setup(). --- @out should have a value assigned via :set() or the `Value` constructor --- once :tick(true) is called. 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 a --- `ValueInput`. --- -class Op --- super-implementations for extensions - -- 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. - new: (type, init) => - if type - @out = Value type, init - - -- setups previous @inputs, if any, with the new inputs, and writes 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. - setup: do - 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 - - (inputs) => - old_inputs = @inputs - @inputs = inputs - do_setup old_inputs, @inputs - - tick: => - destroy: => - --- utilities - -- iterate over the (potentially nested) inputs table - all_inputs: do - do_yield = (table) -> - for k, v in pairs table - if v.__class - coroutine.yield v - else - do_yield v - - => coroutine.wrap -> do_yield @inputs - - unwrap_all: do - do_unwrap = (value) -> - if value.__class - value\unwrap! - else - {k, do_unwrap v for k,v in pairs value} - - => do_unwrap @inputs - - assert_types: (...) => - num = select '#', ... - assert #@inputs >= num, "argument count mismatch" - @assert_first_types ... - - assert_first_types: (...) => - num = select '#', ... - for i = 1, num - expect = select i, ... - assert @inputs[i].type == expect, "expected argument #{i} of #{@} to be of type #{expect} but found #{@inputs[i]}" - --- static - __tostring: => "<op: #{@@__name}>" - __inherited: (cls) => cls.__base.__tostring = @__tostring - --- a builtin / special form / cell-evaluation strategy. --- --- responsible for quoting/evaluating subexpressions, instantiating and patching --- Ops updating the current Scope, etc. See core.builtin and core.invoke for --- many examples. -class Action - -- head: the (:eval'd) head of the Cell to evaluate (a Const) - -- tag: the Tag of the expression to evaluate - new: (head, @tag) => - @patch head - - -- * eval args - -- * perform scope effects - -- * patch nested exprs - -- * return runtime-tree value - eval: (scope, tail) => error "not implemented" - - -- free resources - destroy: => - - -- update this instance for :eval() with new head - -- if :patch() returns false, this instance is :destroy'ed and recreated - -- instead must *not* return false when called after :new() - -- only considered if Action types match - patch: (head) => - if head == @head - true - - @head = head - --- static - -- find & patch the action for the expression with Tag 'tag' if it exists, - -- and is compatible with the new Cell contents, otherwise instantiate it. - -- register the action with the tag, evaluate it and return the Result - @eval_cell: (scope, tag, head, tail) => - last = tag\last! - compatible = last and - (last.__class == @) and - (last\patch head) and - last - - L\trace if compatible - "reusing #{last} for #{tag} <#{@__name} #{head}>" - else if last - "replacing #{last} with new #{tag} <#{@__name} #{head}>" - else - "initializing #{tag} <#{@__name} #{head}>" - - action = if compatible - tag\keep compatible - compatible - else - last\destroy! if last - with next = @ head, tag - tag\replace next - - action\eval scope, tail - - __tostring: => "<#{@@__name} #{@head}>" - __inherited: (cls) => cls.__base.__tostring = @__tostring - --- an ALV function definition --- --- when called, expands its body with params bound to the fn arguments --- (see core.invoke.fn-invoke) -class FnDef - -- params: sequence of (:quote'd) symbols, each naming a function parameter - -- body: (:quote'd) expression the function evaluates to - -- scope: the lexical scope the function was defined in (closure) - new: (@params, @body, @scope) => - - __tostring: => - "(fn (#{table.concat [p\stringify! for p in *@params], ' '}) ...)" - --- an update scheduling policy for `Op`. --- --- subclasses must implement this interface: --- --- :new(value) - create an instance --- --- `value` is either a `Value` or a `Result` instance and should be unwrapped. --- --- :setup(prev) - copy state from old instance --- --- called by `Op:setup()` with another `Input` instance or `nil` once this instance is --- registered. Must prepare this instance for :dirty(). --- ---- :dirty() - whether this input requires processing --- --- must return a boolean indicating whether `Op`s that refer to this instance --- should be notified (via `Op:tick()`). --- --- :finish_setup() - leave setup state --- --- called after the Op has completed (or skipped) its first `Op:tick()` after --- `Op:setup()`. Must prepare this instance for dataflow operation. --- -class Input - new: (value) => - assert value, "nil passed to Input: #{value}" - @stream = switch value.__class - when Result - assert value.value, "Input from result without value!" - when Value - value - else - error "Input from unknown value: #{value}" - - setup: (previous) => - - finish_setup: => - dirty: => @stream\dirty! - unwrap: => @stream\unwrap! - type: => @stream.type - - __call: => @stream\unwrap! - __tostring: => "#{@@__name}:#{@stream}" - __inherited: (cls) => - cls.__base.__call = @__call - cls.__base.__tostring = @__tostring - --- Never marked dirty. Use this for input streams that are only read when --- another input fires. -class ColdInput extends Input - dirty: => false - --- Marked dirty for the setup-tick if old and new stream differ in current --- value. This is the most common `Input` strategy. Should be used whenever a --- value denotes state. -class ValueInput extends Input - setup: (old) => @dirty_setup = not old or @stream != old.stream - finish_setup: => @dirty_setup = false - dirty: => @dirty_setup or @stream\dirty! - --- Only marked dirty if the input stream itself is dirty. Should be used --- whenever a value denotes a momentary event or impulse. -class EventInput extends Input - --- Marked dirty when an IO object is dirty. Must be used for IO values. -class IOInput extends Input - impure: true - dirty: => @stream\unwrap!\dirty! - -{ - :IO - :Op - :Action - :FnDef - - :ValueInput, :EventInput, :IOInput, :ColdInput - - -- redundant exports, to keep anything an extension might need in one import - :Value, :Result - :match -} |
