aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authors-ol <s-ol@users.noreply.github.com>2020-03-07 20:58:30 +0000
committers-ol <s-ol@users.noreply.github.com>2020-03-07 20:58:51 +0000
commit6a7a2ddaca798f3cccac394d1fb9f317cbe90de6 (patch)
tree4e49e53057b39f75813311ede13f87d238061fe6
parentspellcheck (diff)
downloadalive-6a7a2ddaca798f3cccac394d1fb9f317cbe90de6.tar.gz
alive-6a7a2ddaca798f3cccac394d1fb9f317cbe90de6.zip
add ldoc documentation
-rw-r--r--README.md1
-rw-r--r--core/base.moon298
-rw-r--r--core/base/action.moon95
-rw-r--r--core/base/fndef.moon22
-rw-r--r--core/base/init.moon35
-rw-r--r--core/base/input.moon131
-rw-r--r--core/base/io.moon34
-rw-r--r--core/base/match.moon93
-rw-r--r--core/base/op.moon132
-rw-r--r--core/cell.moon121
-rw-r--r--core/config.ld4
-rw-r--r--core/init.moon27
-rw-r--r--core/invoke.moon4
-rw-r--r--core/parsing.moon19
-rw-r--r--core/pattern.moon65
-rw-r--r--core/registry.moon35
-rw-r--r--core/result.moon56
-rw-r--r--core/scope.moon46
-rw-r--r--core/tag.moon63
-rw-r--r--core/value.moon115
-rw-r--r--core/version.moon14
-rw-r--r--docs/.gitignore1
-rwxr-xr-xextra/git-version.sh10
-rw-r--r--lib/debug.moon6
-rw-r--r--lib/logic.moon16
-rw-r--r--lib/math.moon12
-rw-r--r--lib/midi.moon22
-rw-r--r--lib/osc.moon18
-rw-r--r--lib/pilot.moon20
-rw-r--r--lib/random.moon10
-rw-r--r--lib/sc.moon10
-rw-r--r--lib/string.moon4
-rw-r--r--lib/time.moon24
-rw-r--r--lib/util.moon12
-rw-r--r--pilot.alv46
-rw-r--r--spec/core/cell_spec.moon7
-rw-r--r--spec/core/pattern_spec.moon2
-rw-r--r--test.alv9
38 files changed, 1033 insertions, 606 deletions
diff --git a/README.md b/README.md
index 2814623..216812a 100644
--- a/README.md
+++ b/README.md
@@ -17,6 +17,7 @@ text-based language and works without special editor support.
- [lua-rtmidi][rtmidi]: `luarocks install https://raw.githubusercontent.com/s-ol/lua-rtmidi/master/lua-rtmidi-dev-1.rockspec`
- [busted][busted]: `luarocks install busted` (optional, for tests)
- [discount][discount]: `luarocks install discount` (optional, for docs)
+- [ldoc][ldoc]: `luarocks install ldoc` (optional, for docs)
\* these are also `moonscript` dependencies and do not neet to be installed manually.
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
-}
diff --git a/core/base/action.moon b/core/base/action.moon
new file mode 100644
index 0000000..0b9dc77
--- /dev/null
+++ b/core/base/action.moon
@@ -0,0 +1,95 @@
+----
+-- Builtin / Special Form / `Cell`-evaluation Strategy.
+--
+-- Responsible for quoting/evaluating subexpressions, instantiating and patching
+-- `Op`s, updating the current `Scope`, etc.
+-- See `builtin` and `invoke` for examples.
+--
+-- @classmod Action
+
+class Action
+--- Action interface.
+--
+-- methods that have to be implemented by `Action` implementations.
+-- @section interface
+
+ --- create a new instance
+ -- @tparam Value head the (`\eval`d) head of the Cell to evaluate
+ -- @tparam Tag tag the Tag of the expression to evaluate
+ new: (head, @tag) =>
+ @patch head
+
+ --- 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: =>
+
+ --- attempt to update this instance with a new `@head` prior to `\eval`.
+ --
+ -- If `\patch` returns `false`, this instance is `\destroy`ed and recreated.
+ -- Must *not* return `false` when called immediately after `\new`.
+ -- Only considered if Action types of old and new expression match.
+ --
+ -- @tparam AST head the new head value
+ -- @treturn bool whether patching was successful
+ patch: (head) =>
+ if head == @head
+ true
+
+ @head = head
+
+--- static functions
+-- @section static
+
+ --- get-or-update an `Action` for a given tag, then evaluate it.
+ --
+ -- Find the action for the expression with `Tag` `tag` if it exists,
+ -- and is compatible with the new `head`, otherwise instantiate one.
+ -- Register the `Action` with `tag`, evaluate it and return the `Result`.
+ --
+ -- @tparam Scope scope the active scope
+ -- @tparam Tag tag the tag of the `Cell` being evaluated
+ -- @tparam Value head the (`\eval`d) head of the `Cell` being evaluated
+ -- @tparam {Ast,...} tail the raw AST parameters to the `Cell` being evaluated
+ -- @treturn Result the result of evaluation
+ 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
+
+{
+ :Action
+}
diff --git a/core/base/fndef.moon b/core/base/fndef.moon
new file mode 100644
index 0000000..b6c284b
--- /dev/null
+++ b/core/base/fndef.moon
@@ -0,0 +1,22 @@
+----
+-- `alive` 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
+ --- create a new instance
+ --
+ -- @tparam {Value,...} params (`\quote`d) naming the function parameters
+ -- @tparam AST body (`\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
new file mode 100644
index 0000000..64b34f4
--- /dev/null
+++ b/core/base/init.moon
@@ -0,0 +1,35 @@
+----
+-- Base definitions for extensions.
+--
+-- This module exports the following classes that extension modules may need:
+--
+-- @module base
+-- @see IO
+-- @see Op
+-- @see Action
+-- @see FnDef
+-- @see Input
+-- @see match
+-- @see Value
+-- @see Result
+
+import IO from require 'core.base.io'
+import Op from require 'core.base.op'
+import Action from require 'core.base.action'
+import FnDef from require 'core.base.fndef'
+import Input from require 'core.base.input'
+import match from require 'core.base.match'
+import Value from require 'core.value'
+import Result from require 'core.result'
+
+{
+ :IO
+ :Op
+ :Action
+ :FnDef
+ :Input
+ :match
+
+ -- redundant exports, to keep anything an extension might need in one import
+ :Value, :Result
+}
diff --git a/core/base/input.moon b/core/base/input.moon
new file mode 100644
index 0000000..14c812e
--- /dev/null
+++ b/core/base/input.moon
@@ -0,0 +1,131 @@
+----
+-- Update scheduling policy for `Op` arguments.
+--
+-- @classmod Input
+import Value from require 'core.value'
+import Result from require 'core.result'
+
+local ColdInput, ValueInput, EventInput, IOInput
+
+class Input
+--- Input interface.
+--
+-- Methods that have to be implemented by `Input` implementations.
+-- @section interface
+
+ --- create an instance (optional).
+ --
+ -- `value` is either a `Value` or a `Result` instance and should be
+ -- unwrapped and assigned to `@stream`.
+ --
+ -- @function new
+ -- @tparam Value|Result value
+ 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}"
+
+ --- 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`.
+ --
+ -- @function 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\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
+
+--- methods.
+--
+-- @section methods
+
+ --- alias for `\unwrap`.
+ __call: => @stream\unwrap!
+
+ __tostring: => "#{@@__name}:#{@stream}"
+ __inherited: (cls) =>
+ cls.__base.__call = @__call
+ cls.__base.__tostring = @__tostring
+
+--- constructors
+-- @section constructors
+
+ --- Create a `ColdInput`.
+ --
+ -- Never marked dirty. Use this for input streams that are only read when
+ -- another `Input` is dirty.
+ --
+ -- @tparam Value|Result value
+ cold: (value) -> ColdInput value
+
+ --- Create a `ValueInput`.
+ --
+ -- Marked dirty for the eval-tick if old and new `Value` differ. This is the
+ -- most common `Input` strategy. Should be used whenever a
+ -- value denotes state.
+ --
+ -- @tparam Value|Result value
+ value: (value) -> ValueInput value
+
+ --- Create an `EventInput`.
+ --
+ -- Only marked dirty if the `Value` itself is dirty. Should be used whenever
+ -- an `Input` denotes a momentary event or impulse.
+ --
+ -- @tparam Value|Result value
+ event: (value) -> EventInput value
+
+ --- Create an `IOInput`.
+ --
+ -- Marked dirty only when an `IO` is dirty. Must be used only for `Value`s
+ -- which `\unwrap` to `IO` instances.
+ --
+ -- @tparam Value|Result value
+ io: (value) -> IOInput 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 = false
+ dirty: => @dirty_setup or @stream\dirty!
+
+class EventInput extends Input
+
+class IOInput extends Input
+ impure: true
+ dirty: => @stream\unwrap!\dirty!
+
+{
+ :Input
+}
diff --git a/core/base/io.moon b/core/base/io.moon
new file mode 100644
index 0000000..e7399e8
--- /dev/null
+++ b/core/base/io.moon
@@ -0,0 +1,34 @@
+----
+-- Incoming side-effect adapter, polled by the main event loop to pump
+-- events into the dataflow graph.
+--
+-- @classmod IO
+
+class IO
+--- IO interface.
+--
+-- methods that have to be implemented by `IO` implementations.
+-- @section interface
+
+ --- construct a new instance.
+ --
+ -- Must prepare the instance for `\dirty` to be called.
+ new: =>
+
+ --- 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``.io` should be notified (via `Op``\tick`). May be called multiple
+ -- times. May be called before `\tick` on the first frame after construction.
+ --
+ -- @treturn bool whether processing is required
+ dirty: =>
+
+{
+ :IO
+}
diff --git a/core/base/match.moon b/core/base/match.moon
new file mode 100644
index 0000000..a20bcdc
--- /dev/null
+++ b/core/base/match.moon
@@ -0,0 +1,93 @@
+----
+-- Utilities for matching `Result` types.
+--
+-- @module match
+unpack or= table.unpack
+
+class Pattern
+ new: (opts) =>
+ if 'string' == type opts
+ splat, const, type, opt = opts\match '^(%*?)(=?)([%w%-%_%/]+)(%??)$'
+ assert type, "couldn't parse type pattern '#{opts}'"
+ opts = {
+ :type
+ splat: splat == '*'
+ const: const == '='
+ opt: opt == '?'
+ }
+
+ @type = opts.type
+ @const = opts.const
+ @opt = opts.opt
+ @splat = opts.splat
+
+ matches: (result) =>
+ return false unless result
+
+ if @const
+ return false unless result\is_const!
+
+ if not result.value
+ return @type == 'nil'
+
+ return true if @type == 'any'
+
+ result.value.type == @type
+
+ match: (results) =>
+ if @splat
+ matched = while @matches results[1]
+ table.remove results, 1
+
+ assert @opt or #matched > 0, "expected at least one argument for spread"
+ matched
+ else
+ matches = @matches results[1]
+ assert @opt or matches, "couldn't match argument #{results[1]} as #{@}"
+ if matches then table.remove results, 1
+
+ __tostring: =>
+ str = @type
+ str = '*' .. str if @splat
+ str = '=' .. str if @const
+ str = str .. '?' if @opt
+ str
+
+--- match inputs to a argument type definition.
+--
+-- `pattern` is a string of type entries. Every type entry can be like this:
+--
+-- - `any` - matches one `Result` and returns it.
+-- - `typename` - matches one `Result` of type `typename` and returns it.
+-- - `=typename` - matches one eval-time const `Result`s of type `typname` and
+-- returns it.
+-- - `typename?` - matches what `typename` would match, if possible (greedy).
+-- Otherwise returns `nil`.
+-- - `*typename` - matches as many `typename` `Result`s as possible (greedy).
+-- Throws if there isn't at least one such `Result`. Returns a sequence of
+-- `Result`s.
+-- - `*typename?` - like `*typename`, except it also matches zero `Result`s.
+-- - `*=typename`, `=typename?` and `*=typename?` behave as expected.
+--
+-- Except for `typename?` and `*typename?`, all entries throw if they cannot
+-- match the next `Result` in `inputs`.
+--
+-- Throws if there are leftover `inputs` after matching all of `pattern`.
+--
+-- @tparam string pattern the argument type definition
+-- @tparam {Result,...} inputs the list of inputs
+-- @treturn {Result|{Result,...},...} the inputs as matched against `pattern`
+match = (pattern, inputs) ->
+ patterns = while pattern
+ pat, rest = pattern\match '^([^ ]+) (.*)$'
+ pat = pattern unless pat
+ pattern = rest
+ Pattern pat
+ values = [p\match inputs for p in *patterns]
+ assert #inputs == 0, "#{#inputs} extra arguments given!"
+ values
+
+{
+ :Pattern
+ :match
+}
diff --git a/core/base/op.moon b/core/base/op.moon
new file mode 100644
index 0000000..6d1ec98
--- /dev/null
+++ b/core/base/op.moon
@@ -0,0 +1,132 @@
+----
+-- Persistent expression Operator.
+--
+-- @classmod Op
+import Value from require 'core.value'
+
+class Op
+--- Op interface.
+--
+-- methods that have to be implemented by `Op` implementations.
+-- @section interface
+
+ --- construct a new instance.
+ --
+ -- The super-constructor can be used to construct a `Value` instance in `@out`.
+ --
+ -- @function new
+
+ --- 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 inputs [`Result`] 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 (like
+ -- `ValueInput`) 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: =>
+
+ --- a `Value` instance representing this Op's computed output value.
+ --
+ -- Must be set to a `Value` instance once `\setup` finishes. Must not change
+ -- type, be removed or replaced outside of `\new` and `\setup`. Should have a
+ -- value assigned via `\set` or the `Value` 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 a
+ -- `ValueInput`.
+ --
+ -- @tfield Value out
+
+--- 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.
+ --
+ -- @tparam[opt] string type the type-name for `@out`
+ -- @tparam[optchain] any init the initial value for `@out`
+ new: (type, init) =>
+ if type
+ @out = Value type, init
+
+ --- 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: 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
+
+ --- yield all `Input`s from the (potentially nested) inputs table
+ --
+ -- @treturn iterator iterator over all `Input`s
+ 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 `Input`s in `@inputs` and return a table with the same
+ -- shape.
+ --
+ -- @treturn table the values of all `Input`s
+ 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
+
+ __tostring: => "<op: #{@@__name}>"
+ __inherited: (cls) => cls.__base.__tostring = @__tostring
+
+{
+ :Op
+}
diff --git a/core/cell.moon b/core/cell.moon
index 850a9db..4d46b64 100644
--- a/core/cell.moon
+++ b/core/cell.moon
@@ -1,12 +1,60 @@
--- ALV Cell type
+----
+-- 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 Value from require 'core.value'
import op_invoke, fn_invoke from require 'core.invoke'
import Tag from require 'core.tag'
--- An S-Expression with a head expression and any number of tail expressions,
--- an optional tag, and optionally the internal whitespace as parsed.
+local RootCell
+
class Cell
--- AST interface
+--- methods
+-- @section methods
+
+ -- tag: the parsed Tag
+ -- children: sequence of child AST Nodes
+ -- white: optional sequence of whitespace segments ([0 .. #@children])
+ 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
+
+--- AST interface
+--
+-- `Cell` implements the `AST` interface.
+-- @section ast
+
+ --- evaluate this Cell.
+ --
+ -- `\eval`uates the head of the expression, and finds the appropriate
+ -- `Action` 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 `\eval_cell` on the `Action`.
+ --
+ -- @tparam Scope scope the scope to evaluate in
+ -- @treturn Result the evaluation result
eval: (scope) =>
head = assert @head!, "cannot evaluate expr without head"
head = (head\eval scope)\const!
@@ -24,19 +72,36 @@ class Cell
Action\eval_cell scope, @tag, head, @tail!
- -- quoting a Cell recursively quotes children, but preserves identity. This
- -- means that a quoted Cell may only be 'used' once. Use :clone() otherwise.
+ --- 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 quoted
quote: (scope) =>
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`
- -- tag and cloning all child expressoins recursively.
+ -- to `@tag` and cloning all child expressoins recursively.
+ --
+ -- @treturn Cell clone
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]
@@ -54,22 +119,8 @@ class Cell
'(' .. tag .. buf .. ')'
--- internal
- -- tag: the parsed Tag
- -- children: sequence of child AST Nodes
- -- white: optional sequence of whitespace segments ([0 .. #@children])
- new: (@tag=Tag.blank!, @children, @white) =>
- if not @white
- @white = [' ' for i=1,#@children]
- @white[0] = ''
-
- assert #@white == #@children, "mismatched whitespace length"
-
- head: => @children[1]
- tail: => [c for c in *@children[2,]]
-
--- static
- __tostring: => @stringify 2
+--- static functions
+-- @section static
parse_args = (tag, parts) ->
if not parts
@@ -82,13 +133,26 @@ class Cell
white[i/2] = parts[i+1]
tag, children, white
- @parse: (...) =>
+ --- parse a Cell (for parsing with Lpeg).
+ --
+ -- @tparam[opt] Tag tag
+ -- @tparam table parts
+ -- @treturn Cell
+ parse: (...) =>
tag, children, white = parse_args ...
@ tag, children, white
--- A parenthesis-less Cell (root of an ALV document).
---
--- has an implicit head of 'do'.
+ --- 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: (parts) =>
+ RootCell.parse RootCell, (Tag\parse '0'), parts
+
+-- @type RootCell
class RootCell extends Cell
head: => Value.sym 'do'
tail: => @children
@@ -103,9 +167,6 @@ class RootCell extends Cell
buf
- @parse: (...) =>
- @__parent.parse @, (Tag\parse '0'), ...
-
{
:Cell
:RootCell
diff --git a/core/config.ld b/core/config.ld
new file mode 100644
index 0000000..1c07337
--- /dev/null
+++ b/core/config.ld
@@ -0,0 +1,4 @@
+project = 'alive copilot'
+title = 'alive copilot reference'
+format = 'discount'
+dir = 'docs/copilot'
diff --git a/core/init.moon b/core/init.moon
index 7122e0f..c4e8ffd 100644
--- a/core/init.moon
+++ b/core/init.moon
@@ -1,3 +1,17 @@
+----
+-- `alive` public API.
+--
+-- @see Value
+-- @see Result
+-- @see Cell
+-- @see Scope
+-- @see Registry
+-- @see Tag
+-- @field globals
+-- @field parse
+-- @field eval
+--
+-- @module init
L or= setmetatable {}, __index: => ->
import Value from require 'core.value'
@@ -6,7 +20,7 @@ import Scope from require 'core.scope'
import Registry from require 'core.registry'
import Tag from require 'core.tag'
-import Cell, RootCell from require 'core.cell'
+import Cell from require 'core.cell'
import cell, program from require 'core.parsing'
with require 'core.cycle'
@@ -14,6 +28,17 @@ with require 'core.cycle'
globals = Scope.from_table require 'core.builtin'
+--- exports
+-- @table exports
+-- @tfield Value Value
+-- @tfield Result Result
+-- @tfield Cell Cell
+-- @tfield RootCell RootCell
+-- @tfield Scope Scope
+-- @tfield Registry Registry
+-- @tfield Tag Tag
+-- @tfield Scope globals global definitons
+-- @tfield parse function to turn a `string` into a root `Cell`
{
:Value, :Result
:Cell, :RootCell
diff --git a/core/invoke.moon b/core/invoke.moon
index 7ff16dc..69ce881 100644
--- a/core/invoke.moon
+++ b/core/invoke.moon
@@ -1,3 +1,7 @@
+----
+-- Builtins for invoking `Op`s and `FnDef`s.
+--
+-- @module invoke
import Value from require 'core.value'
import Result from require 'core.result'
import Action from require 'core.base'
diff --git a/core/parsing.moon b/core/parsing.moon
index 8a99d89..911f473 100644
--- a/core/parsing.moon
+++ b/core/parsing.moon
@@ -1,5 +1,9 @@
+----
+-- Lpeg Grammar for parsing `alive` code.
+--
+-- @module parsing
import Value from require 'core.value'
-import Cell, RootCell from require 'core.cell'
+import Cell from require 'core.cell'
import Tag from require 'core.tag'
import R, S, P, V, C, Ct from require 'lpeg'
@@ -35,7 +39,7 @@ tag = (P '[') * (digit^1 / Tag\parse) * (P ']')
cell = (P '(') * tag^-1 * (V 'explist') * (P ')') / Cell\parse
root = P {
- (V 'explist') / RootCell\parse
+ (V 'explist') / Cell\parse_root
:expr, :explist, :cell
}
@@ -46,6 +50,17 @@ cell = P {
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
diff --git a/core/pattern.moon b/core/pattern.moon
deleted file mode 100644
index 13497af..0000000
--- a/core/pattern.moon
+++ /dev/null
@@ -1,65 +0,0 @@
-unpack or= table.unpack
-
-class Pattern
- new: (opts) =>
- if 'string' == type opts
- splat, const, type, opt = opts\match '^(%*?)(=?)([%w%-%_%/]+)(%??)$'
- assert type, "couldn't parse type pattern '#{opts}'"
- opts = {
- :type
- splat: splat == '*'
- const: const == '='
- opt: opt == '?'
- }
-
- @type = opts.type
- @const = opts.const
- @opt = opts.opt
- @splat = opts.splat
-
- matches: (result) =>
- return false unless result
-
- if @const
- return false unless result\is_const!
-
- if not result.value
- return @type == 'nil'
-
- return true if @type == 'any'
-
- result.value.type == @type
-
- match: (results) =>
- if @splat
- matched = while @matches results[1]
- table.remove results, 1
-
- assert @opt or #matched > 0, "expected at least one argument for spread"
- matched
- else
- matches = @matches results[1]
- assert @opt or matches, "couldn't match argument #{results[1]} as #{@}"
- if matches then table.remove results, 1
-
- __tostring: =>
- str = @type
- str = '*' .. str if @splat
- str = '=' .. str if @const
- str = str .. '?' if @opt
- str
-
-match = (pattern, results) ->
- patterns = while pattern
- pat, rest = pattern\match '^([^ ]+) (.*)$'
- pat = pattern unless pat
- pattern = rest
- Pattern pat
- values = [p\match results for p in *patterns]
- assert #results == 0, "#{#results} extra arguments given!"
- values
-
-{
- :Pattern
- :match
-}
diff --git a/core/registry.moon b/core/registry.moon
index fca7e5d..edce9bd 100644
--- a/core/registry.moon
+++ b/core/registry.moon
@@ -1,14 +1,12 @@
+----
+-- `Tag` Registry.
+--
+-- @classmod Registry
+
import Value from require 'core.value'
import Result from require 'core.result'
class Registry
- new: () =>
- @map = {}
- @io = {}
-
- @tick = 0
- @kr = Result value: Value.bool true
-
-- methods for Tag
last: (index) => @last_map[index]
@@ -24,8 +22,23 @@ class Registry
active: -> assert Registry.active_registry, "no active Registry!"
--- public methods
+ next_tag: => #@map + 1
+
+--- methods
+-- @section methods
+ --- create an instance.
+ new: =>
+ @map = {}
+ @io = {}
+
+ @tick = 0
+ @kr = Result value: Value.bool true
+
+ --- wrap a function with an eval-cycle.
+ --
+ -- @tparam function fn
+ -- @treturn function `fn` wrapped with eval-cycle logic
wrap_eval: (fn) => (...) ->
@grab!
@last_map, @map, @pending = @map, {}, {}
@@ -47,6 +60,10 @@ class Registry
@release!
+ --- wrap a function with a tick.
+ --
+ -- @tparam function fn
+ -- @treturn function `fn` wrapped with tick logic
wrap_tick: (fn) => (...) ->
@grab!
@tick += 1
@@ -66,8 +83,6 @@ class Registry
assert @ == @@active_registry, "not the active registry!"
@@active_registry, @prev = @prev, nil
- next_tag: => #@map + 1
-
class SimpleRegistry extends Registry
new: =>
@cnt = 1
diff --git a/core/result.moon b/core/result.moon
index 7fda8e5..9f0bce7 100644
--- a/core/result.moon
+++ b/core/result.moon
@@ -1,16 +1,26 @@
+----
+-- Result of evaluating an expression.
+--
+-- Contains (all optional):
+--
+-- - `@value`: a `Value`
+-- - `@op`: an `Op` (to update)
+-- - `@children`: a lsit of child `Results` (from subexpressions)
+-- - `@side_inputs`: cached list of all `Inputs` affecting any `Op` in this
+-- subtree
+--
+-- `Result`s form a tree that controls execution order and message passing
+-- between `Op`s.
+--
+-- @classmod Result
import base from require 'core.cycle'
--- Result of evaluating an expression
--- carries (all optional):
--- - a Value
--- - an Op (to update)
--- - children (results of subexpressions that were evaluated)
--- - cached list of all Dispatchers affecting all Ops in the subtree
---
--- Results form a tree that controls execution order and message passing
--- between Ops.
class Result
- -- params: table with optional keys op, value, children
+--- methods
+-- @section methods
+
+ --- create a new Result.
+ -- @param params table with optional keys op, value, children. default: {}
new: (params={}) =>
@value = params.value
@op = params.op
@@ -28,35 +38,38 @@ class Result
if input.impure or not is_child[input.stream]
@side_inputs[input.stream] = input
+ --- return whether this Result's value is const.
is_const: => not next @side_inputs
- -- asserts value-constness and returns the value
+ --- assert value-constness and returns the value.
+ -- @tparam[opt] string msg the error message to throw
const: (msg) =>
assert not (next @side_inputs), msg or "eval-time const expected"
@value
- -- asserts a value exists and returns its type
+ --- assert this result has a value, returns its type.
type: =>
assert @value, "Result with value expected"
@value.type
- -- create a value-copy of this result that has the same impulses but without
- -- affecting the original's update logic
+ --- 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 IO instances that are effecting this (sub) tree
- -- should be called once per frame on the root, right before tick
+ --- tick all IO 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
if input.__class == base.IOInput
io = input!
io\tick!
- -- in depth-first order, tick all Ops who have dirty Stream inputs or impulses
+ --- in depth-first order, tick all Ops which have dirty Inputs.
--
- -- short-circuits if there are no dirty Streams in the entire subtree
+ -- short-circuits if there are no dirty Inputs in the entire subtree
tick: =>
any_dirty = false
for stream, input in pairs @side_inputs
@@ -64,15 +77,15 @@ class Result
any_dirty = true
break
- -- early-out if no streams are dirty in this whole subtree
+ -- 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 streams from child
- -- expressions might have changed
+ -- 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!
@@ -84,7 +97,6 @@ class Result
@op\tick!
--- static
__tostring: =>
buf = "<result=#{@value}"
buf ..= " #{@op}" if @op
diff --git a/core/scope.moon b/core/scope.moon
index 4ed95d1..c61a446 100644
--- a/core/scope.moon
+++ b/core/scope.moon
@@ -1,14 +1,36 @@
+----
+-- Mapping from `sym`s to `Result`s.
+--
+-- @classmod Scope
import Value from require 'core.value'
import Result from require 'core.result'
class Scope
+--- methods
+-- @section methods
+
+ --- create an instance.
+ --
+ -- @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 = {}
+ --- set a Lua value in the scope.
+ --
+ -- wraps `val` in a `Value` and `Result` before calling `\set`.
+ --
+ -- @tparam string key
+ -- @tparam any val
set_raw: (key, val) =>
value = Value.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"
@@ -20,6 +42,11 @@ class Scope
parent or= @parent
return parent and L\push parent\get, key
+ --- resolve a key in this Scope.
+ --
+ -- @tparam string key the key to resolve
+ -- @tparam[opt] string prefix a prefix (for internal use with nested scopes)
+ -- @treturn ?Result the value of the definition that was found, or `nil`
get: (key, prefix='') =>
L\debug "checking for #{key} in #{@}"
if val = @values[key]
@@ -35,11 +62,26 @@ class Scope
assert child and child.value.type == 'scope', "#{start} is not a scope (looking for #{key})"
child.value\unwrap!\get rest, "#{prefix}#{start}/"
+ --- 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
+--- static functions
+-- @section static
+
+ --- converts a Lua table to a Scope.
+ --
+ -- `tbl` may contain more tables (or `Scope`s).
+ -- Uses `Value``.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
@@ -64,4 +106,6 @@ class Scope
buf ..= ">"
buf
-:Scope
+{
+ :Scope
+}
diff --git a/core/tag.moon b/core/tag.moon
index f38d36a..5e6de24 100644
--- a/core/tag.moon
+++ b/core/tag.moon
@@ -1,3 +1,12 @@
+----
+-- Identity provider for `Cell`s and `Action`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
@@ -10,30 +19,47 @@ class DummyReg
dummy = DummyReg!
class Tag
- -- obtain the value that was previously registered (using keep or replace) for
- -- this tag
+--- methods
+-- @section methods
+
+ --- obtain the registered value of the last eval-cycle.
+ --
+ -- Obtain the value that was previously registered (using `\keep` or
+ -- `\replace`) for this tag on the last eval-cylce.
+ --
+ -- @treturn ?any
last: =>
if index = @index!
Registry.active!\last index
- -- assert that `expr` is the value that was previously registered for this
- -- tag, and keep it for the current eval cycle.
- -- fails for blank tags.
+ --- keep using a the value from the last eval-cycle.
+ --
+ -- Assert that `expr` is the value that was previously registered for this
+ -- tag, and keep it for the current eval cycle. Fails for blank tags.
+ --
+ -- @tparam any expr the value to register
keep: (expr) =>
index = assert @index!
assert expr == Registry.active!\last index
Registry.active!\replace index, expr
- -- register `expr` for this tag for the current eval cycle.
- -- registers blank tags.
+ --- 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
replace: (expr) =>
if index = @index!
Registry.active!\replace index, expr
else
Registry.active!\init @, expr
- -- create a copy of this tag scoped to a `parent` tag.
- -- registers blank tags.
+ --- 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
@@ -45,6 +71,9 @@ class Tag
assert parent, "need parent to clone!"
ClonedTag @, parent
+ stringify: => if @value then "[#{@value}]" else ''
+ __tostring: => if @value then "#{@value}" else '?'
+
-- internal
new: (@value) =>
@@ -56,13 +85,19 @@ class Tag
assert not @value, "#{@} is not blank"
@value = value
--- static
+--- static functions
+-- @section static
- @blank: -> Tag!
- @parse: (num) => @ tonumber num
+ --- create a blank `Tag`.
+ --
+ -- @treturn Tag
+ blank: -> Tag!
- stringify: => if @value then "[#{@value}]" else ''
- __tostring: => if @value then "#{@value}" else '?'
+ --- parse a `Tag` (for Lpeg parsing).
+ --
+ -- @tparam string num the number-string
+ -- @treturn Tag
+ parse: (num) => @ tonumber num
class ClonedTag extends Tag
new: (@original, @parent) =>
diff --git a/core/value.moon b/core/value.moon
index 26771ea..c6ed42d 100644
--- a/core/value.moon
+++ b/core/value.moon
@@ -1,3 +1,7 @@
+----
+-- `alive` Value(stream), implements the `AST` inteface.
+--
+-- @classmod Value
import Result from require 'core.result'
import scope, base, registry from require 'core.cycle'
@@ -7,26 +11,63 @@ ancestor = (klass) ->
klass = klass.__parent
klass
--- ALV Type wrapper
class Value
- -- @type - type name.
- -- builtin types: * literals: sym, num, bool
- -- * scope, opdef, fndef, builtin
- -- @value - Lua value - access through :unwrap()
+--- methods
+-- @section methods
+
+ --- construct a new Value.
+ --
+ -- @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) =>
- @updated = nil
+ --- return whether this Value was changed in the current tick.
+ --
+ -- @treturn bool
dirty: => @updated == registry.Registry.active!.tick
+ --- update this Value.
+ --
+ -- Marks this Value as dirty for the remainder of the current tick.
set: (@value) => @updated = registry.Registry.active!.tick
- -- unwrap to the Lua type
- -- asserts @type == type, msg if given
+ --- 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 @value
unwrap: (type, msg) =>
assert type == @type, msg or "#{@} is not a #{type}" if type
@value
--- AST interface
+ --- alias for `\unwrap`.
+ __call: (...) => @unwrap ...
+
+ --- compare two values.
+ --
+ -- Compares two `Value`s by comparing their types and their Lua values.
+ __eq: (other) => other.type == @type and other.value == @value
+
+ __tostring: =>
+ value = if 'table' == (type @value) and rawget @value, '__base' then @value.__name else @value
+ "<#{@@__name} #{@type}: #{value}>"
+
+--- AST interface
+--
+-- `Value` 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'
@@ -36,24 +77,33 @@ class Value
else
error "cannot evaluate #{@}"
+ --- quote this literal constant.
+ --
+ -- @treturn Value self
quote: => @
+ --- stringify this literal constant.
+ --
+ -- Throws an error if `@raw` is not set.
+ --
+ -- @treturn string the exact string this Value was parsed from
stringify: => assert @raw, "stringifying Value that wasn't parsed"
+ --- clone this literal constant.
+ --
+ -- @treturn Value self
clone: (prefix) => @
- -- in case of doubt:
- -- clone: (prefix) => Value @type, @value, @raw
--- static
- __tostring: =>
- value = if 'table' == (type @value) and rawget @value, '__base' then @value.__name else @value
- "<#{@@__name} #{@type}: #{value}>"
- __call: (...) => @unwrap ...
- __eq: (other) => other.type == @type and other.value == @value
-
- -- wrap a Lua type
- @wrap: (val, name='(unknown)') ->
+--- static functions
+-- @section static
+ --- 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)
+ wrap: (val, name='(unknown)') ->
typ = switch type val
when 'number' then 'num'
when 'string' then 'str'
@@ -83,16 +133,31 @@ class Value
Value typ, val
unescape = (str) -> str\gsub '\\([\'"\\])', '%1'
- @parse: (type, sep) =>
+ --- 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
- @num: (num) -> Value 'num', num, tostring num
- @str: (str) -> Value 'str', str, "'#{str}'"
- @sym: (sym) -> Value 'sym', sym, sym
- @bool: (bool) -> Value 'bool', bool, tostring bool
+ --- create a constant number.
+ -- @tparam number num the number
+ num: (num) -> Value 'num', num, tostring num
+
+ --- create a constant string.
+ -- @tparam string str the string
+ str: (str) -> Value 'str', str, "'#{str}'"
+
+ --- create a constant symbol.
+ -- @tparam string sym the symbol
+ sym: (sym) -> Value 'sym', sym, sym
+
+ --- create a constant boolean.
+ -- @tparam boolean bool the boolean
+ bool: (bool) -> Value 'bool', bool, tostring bool
{
:Value
diff --git a/core/version.moon b/core/version.moon
index 029dd54..c33a8a1 100644
--- a/core/version.moon
+++ b/core/version.moon
@@ -1,5 +1,15 @@
+----
+-- `alive` source code version information
+--
+-- @module version
+
+--- exports
+-- @table exports
+-- @tfield string tag the last versions git tag
+-- @tfield string rev_short the short git revision hash
+-- @tfield string rev_long the full git revision hash
{
tag: "v0.0"
- rev_short: "4742960"
- rev_long: "4742960c455251f306bf271d50f69724b8beba04"
+ rev_short: "891c138"
+ rev_long: "891c138c8a8a4c9269f3eb70f424b5c1ea3898b0"
}
diff --git a/docs/.gitignore b/docs/.gitignore
index 2d19fc7..3014dfc 100644
--- a/docs/.gitignore
+++ b/docs/.gitignore
@@ -1 +1,2 @@
*.html
+copilot
diff --git a/extra/git-version.sh b/extra/git-version.sh
index e53ddbc..612bea4 100755
--- a/extra/git-version.sh
+++ b/extra/git-version.sh
@@ -5,6 +5,16 @@ REV_SHORT=`git rev-parse --short HEAD`
REV_LONG=`git rev-parse HEAD`
cat <<EOF
+----
+-- \`alive\` source code version information
+--
+-- @module version
+
+--- exports
+-- @table exports
+-- @tfield string tag the last versions git tag
+-- @tfield string rev_short the short git revision hash
+-- @tfield string rev_long the full git revision hash
{
tag: "${TAG}"
rev_short: "${REV_SHORT}"
diff --git a/lib/debug.moon b/lib/debug.moon
index 1ce56cc..4a22e10 100644
--- a/lib/debug.moon
+++ b/lib/debug.moon
@@ -1,4 +1,4 @@
-import Op, ValueInput, EventInput, match from require 'core.base'
+import Op, Input, match from require 'core.base'
class out extends Op
@doc: "(out [name-str?] value) - log value to the console"
@@ -6,8 +6,8 @@ class out extends Op
setup: (inputs) =>
{ name, value } = match 'str? any', inputs
super
- name: name and ValueInput name
- value: ValueInput value
+ name: name and Input.value name
+ value: Input.value value
tick: =>
{ :name, :value } = @unwrap_all!
diff --git a/lib/logic.moon b/lib/logic.moon
index adff5ee..7cf8695 100644
--- a/lib/logic.moon
+++ b/lib/logic.moon
@@ -1,4 +1,4 @@
-import Op, ValueInput, match from require 'core.base'
+import Op, Input, match from require 'core.base'
all_same = (first, list) ->
for v in *list
@@ -20,8 +20,8 @@ class ReduceOp extends Op
setup: (inputs) =>
{ first, rest } = match "any *any", inputs
super
- first: ValueInput first
- rest: [ValueInput v for v in *rest]
+ first: Input.value first
+ rest: [Input.value v for v in *rest]
tick: =>
{ :first, :rest } = @unwrap_all!
@@ -45,8 +45,8 @@ If the value types dont match, the result is an eval-time constant 'false'."
super if same
{
- first: ValueInput first
- rest: [ValueInput v for v in *rest]
+ first: Input.value first
+ rest: [Input.value v for v in *rest]
}
else
{}
@@ -73,7 +73,7 @@ class not_eq extends Op
setup: (inputs) =>
assert #inputs > 1, "neq need at least two values"
- super [ValueInput v for v in *inputs]
+ super [Input.value v for v in *inputs]
tick: =>
if not @inputs[1]
@@ -105,7 +105,7 @@ class not_ extends Op
setup: (inputs) =>
{ value } = match 'any', inputs
- super value: ValueInput value
+ super value: Input.value value
tick: => @out\set not tobool @inputs.value!
@@ -115,7 +115,7 @@ class bool extends Op
setup: (inputs) =>
{ value } = match 'any', inputs
- super value: ValueInput value
+ super value: Input.value value
tick: => @out\set tobool @inputs\value!
diff --git a/lib/math.moon b/lib/math.moon
index b8b4361..02ba1e1 100644
--- a/lib/math.moon
+++ b/lib/math.moon
@@ -1,4 +1,4 @@
-import Op, Value, ValueInput, match from require 'core.base'
+import Op, Value, Input, match from require 'core.base'
unpack or= table.unpack
class ReduceOp extends Op
@@ -7,8 +7,8 @@ class ReduceOp extends Op
setup: (inputs) =>
{ first, rest } = match 'num *num', inputs
super
- first: ValueInput first
- rest: [ValueInput v for v in *rest]
+ first: Input.value first
+ rest: [Input.value v for v in *rest]
tick: =>
{ :first, :rest } = @unwrap_all!
@@ -52,8 +52,8 @@ evenodd_op = (name, remainder) ->
setup: (inputs) =>
{ val, div } = match 'num num?', inputs
super
- val: ValueInput val
- div: ValueInput div or Value.num 2
+ val: Input.value val
+ div: Input.value div or Value.num 2
tick: =>
{ :val, :div } = @unwrap_all!
@@ -72,7 +72,7 @@ func_op = (name, arity, func) ->
setup: (inputs) =>
{ params } = match '*num', inputs
assert #params == arity, "#{@} needs exactly #{arity} parameters" if arity != '*'
- super [ValueInput p for p in *params]
+ super [Input.value p for p in *params]
tick: => @out\set func unpack @unwrap_all!
diff --git a/lib/midi.moon b/lib/midi.moon
index 01af84f..33fe936 100644
--- a/lib/midi.moon
+++ b/lib/midi.moon
@@ -1,4 +1,4 @@
-import Value, Op, ValueInput, IOInput, match from require 'core.base'
+import Value, Op, Input, match from require 'core.base'
import input, output, inout, apply_range from require 'lib.midi.core'
class gate extends Op
@@ -10,9 +10,9 @@ class gate extends Op
setup: (inputs) =>
{ port, note, chan } = match 'midi/port num num?', inputs
super
- port: IOInput port
- note: ValueInput note
- chan: ValueInput chan or Value.num -1
+ port: Input.io port
+ note: Input.value note
+ chan: Input.value chan or Value.num -1
tick: =>
{ :port, :note, :chan } = @inputs
@@ -37,9 +37,9 @@ class trig extends Op
setup: (inputs) =>
{ port, note, chan } = match 'midi/port num num?', inputs
super
- port: IOInput port
- note: ValueInput note
- chan: ValueInput chan or Value.num -1
+ port: Input.io port
+ note: Input.value note
+ chan: Input.value chan or Value.num -1
tick: =>
{ :port, :note, :chan } = @inputs
@@ -71,10 +71,10 @@ range can be one of:
setup: (inputs) =>
{ port, cc, chan, range } = match 'midi/port num num? any?', inputs
super
- port: IOInput port
- cc: ValueInput cc
- chan: ValueInput chan or Value.num -1
- range: ValueInput range or Value.str 'uni'
+ port: Input.io port
+ cc: Input.value cc
+ chan: Input.value chan or Value.num -1
+ range: Input.value range or Value.str 'uni'
if not @out\unwrap!
@out\set apply_range @inputs.range, 0
diff --git a/lib/osc.moon b/lib/osc.moon
index c459a66..101fb11 100644
--- a/lib/osc.moon
+++ b/lib/osc.moon
@@ -1,4 +1,4 @@
-import Op, ValueInput, EventInput, ColdInput, match from require 'core.base'
+import Op, Input, match from require 'core.base'
import pack from require 'osc'
import dns, udp from require 'socket'
@@ -12,8 +12,8 @@ class connect extends Op
setup: (inputs) =>
{ host, port } = match 'str num', inputs
super
- host: ValueInput host
- port: ValueInput port
+ host: Input.value host
+ port: Input.value port
tick: =>
{ :host, :port } = @unwrap_all!
@@ -30,9 +30,9 @@ sends a message only when val is dirty."
setup: (inputs) =>
{ socket, path, value } = match 'udp/socket str any', inputs
super
- socket: ColdInput socket
- path: ColdInput path
- value: EventInput value
+ socket: Input.cold socket
+ path: Input.cold path
+ value: Input.value value
tick: =>
if @inputs.value\dirty!
@@ -51,9 +51,9 @@ sends a whenever any parameter changes."
setup: (inputs) =>
{ socket, path, value } = match 'udp/socket str any', inputs
super
- socket: ValueInput socket
- path: ValueInput path
- value: ValueInput value
+ socket: Input.value socket
+ path: Input.value path
+ value: Input.value value
tick: =>
{ :socket, :path, :value } = @unwrap_all!
diff --git a/lib/pilot.moon b/lib/pilot.moon
index 5c680ed..f24270e 100644
--- a/lib/pilot.moon
+++ b/lib/pilot.moon
@@ -1,4 +1,4 @@
-import Op, EventInput, ValueInput, ColdInput, match from require 'core.base'
+import Op, Input, match from require 'core.base'
import udp from require 'socket'
local conn
@@ -27,8 +27,8 @@ class play extends Op
{ trig, args } = match 'bang *any', inputs
assert #args < 6, "too many arguments!"
super
- trig: EventInput trig
- args: [ColdInput a for a in *args]
+ trig: Input.event trig
+ args: [Input.cold a for a in *args]
tick: =>
{ :trig, :args } = @inputs
@@ -42,10 +42,10 @@ class play_ extends Op
{ chan, octv, note, args } = match 'any any any *any', inputs
assert #args < 3, "too many arguments!"
super
- chan: ColdInput chan
- octv: ColdInput octv
- note: EventInput note
- args: [ColdInput a for a in *args]
+ chan: Input.cold chan
+ octv: Input.cold octv
+ note: Input.event note
+ args: [Input.cold a for a in *args]
tick: =>
if @inputs.note\dirty!
@@ -60,9 +60,9 @@ which is one of 'DIS', 'CHO', 'REV' or 'FEE'"
setup: (inputs) =>
{ which, a, b } = match 'str num num', inputs
super {
- ColdInput which
- ValueInput a
- ValueInput b
+ Input.cold which
+ Input.value a
+ Input.value b
}
tick: =>
diff --git a/lib/random.moon b/lib/random.moon
index 88fdb89..7cabdf0 100644
--- a/lib/random.moon
+++ b/lib/random.moon
@@ -1,4 +1,4 @@
-import Op, Value, ValueInput, EventInput, match from require 'core.base'
+import Op, Value, Input, match from require 'core.base'
apply_range = (range, val) ->
if range\type! == 'str'
@@ -36,8 +36,8 @@ generates a random value in range on create and trigger.
setup: (inputs) =>
{ trig, range } = match 'bang? any?', inputs
super
- trig: trig and EventInput trig
- range: ValueInput range or Value.str 'uni'
+ trig: trig and Input.event trig
+ range: Input.value range or Value.str 'uni'
tick: =>
@gen! if @inputs.trig and @inputs.trig\dirty!
@@ -59,8 +59,8 @@ each component is in range.
setup: (inputs) =>
{ trig, range } = match 'bang? any?', inputs
super
- trig: trig and EventInput trig
- range: ValueInput range or Value.str 'uni'
+ trig: trig and Input.event trig
+ range: Input.value range or Value.str 'uni'
tick: =>
@gen! if @inputs.trig and @inputs.trig\dirty!
diff --git a/lib/sc.moon b/lib/sc.moon
index bae252a..ebdcd3b 100644
--- a/lib/sc.moon
+++ b/lib/sc.moon
@@ -1,4 +1,4 @@
-import Op, EventInput, ColdInput, match from require 'core.base'
+import Op, Input, match from require 'core.base'
import pack from require 'osc'
import dns, udp from require 'socket'
@@ -15,10 +15,10 @@ class play extends Op
assert val\type! == 'num', "only numbers are supported as control values"
super
- socket: ColdInput socket
- synth: ColdInput synth
- trig: EventInput trig
- ctrls: [ColdInput v for v in *ctrls]
+ trig: Input.event trig
+ socket: Input.cold socket
+ synth: Input.cold synth
+ ctrls: [Input.cold v for v in *ctrls]
tick: =>
if @inputs.trig\dirty! and @inputs.trig!
diff --git a/lib/string.moon b/lib/string.moon
index 2753381..06509f3 100644
--- a/lib/string.moon
+++ b/lib/string.moon
@@ -1,11 +1,11 @@
-import Op, ValueInput from require 'core.base'
+import Op, Input from require 'core.base'
class str extends Op
@doc: "(str v1 [v2]...)
(.. v1 [v2]...) - concatenate/stringify values"
new: => super 'str'
- setup: (inputs) => super [ValueInput v for v in *inputs]
+ setup: (inputs) => super [Input.value v for v in *inputs]
tick: => @out\set table.concat [tostring v! for v in *@inputs]
{
diff --git a/lib/time.moon b/lib/time.moon
index 5823d80..44cb998 100644
--- a/lib/time.moon
+++ b/lib/time.moon
@@ -1,4 +1,4 @@
-import Registry, Value, IO, Op, ValueInput, IOInput, match
+import Registry, Value, IO, Op, Input, match
from require 'core.base'
import monotime from require 'system'
@@ -29,7 +29,7 @@ fps defaults to 60 and has to be an eval-time constant"
setup: (inputs) =>
{ fps } = match 'num?', inputs
- super fps: ValueInput fps or Value.num 60
+ super fps: Input.value fps or Value.num 60
tick: =>
if @inputs.fps\dirty!
@@ -52,9 +52,9 @@ wave selects the wave shape from the following:
setup: (inputs, scope) =>
{ clock, freq, wave } = match 'clock? num any?', inputs
super
- clock: IOInput clock or scope\get '*clock*'
- freq: ValueInput freq
- wave: ValueInput wave or default_wave
+ clock: Input.io clock or scope\get '*clock*'
+ freq: Input.value freq
+ wave: Input.value wave or default_wave
tau = math.pi * 2
tick: =>
@@ -80,9 +80,9 @@ ramps from 0 to max (default same as ramp) once every period seconds."
setup: (inputs, scope) =>
{ clock, period, max } = match 'clock? num num?', inputs
super
- clock: IOInput clock or scope\get '*clock*'
- period: ValueInput period
- max: max and ValueInput max
+ clock: Input.io clock or scope\get '*clock*'
+ period: Input.value period
+ max: max and Input.value max
tick: =>
clock_dirty = @inputs.clock\dirty!
@@ -108,8 +108,8 @@ counts upwards by one every period seconds and returns the number of completed t
setup: (inputs, scope) =>
{ clock, period } = match 'clock? num', inputs
super
- clock: IOInput clock or scope\get '*clock*'
- period: ValueInput period
+ clock: Input.io clock or scope\get '*clock*'
+ period: Input.value period
tick: =>
if @inputs.clock\dirty!
@@ -132,8 +132,8 @@ returns true once every period seconds."
setup: (inputs, scope) =>
{ clock, period } = match 'clock? num', inputs
super
- clock: IOInput clock or scope\get '*clock*'
- period: ValueInput period
+ clock: Input.io clock or scope\get '*clock*'
+ period: Input.value period
tick: =>
if @inputs.clock\dirty!
diff --git a/lib/util.moon b/lib/util.moon
index 65bab65..2a43dd2 100644
--- a/lib/util.moon
+++ b/lib/util.moon
@@ -1,4 +1,4 @@
-import Op, Value, ValueInput, EventInput, ColdInput, match
+import Op, Value, Input, match
from require 'core.base'
all_same = (list) ->
@@ -24,8 +24,8 @@ when i is a num, it is (floor)ed and the matching argument (starting from 0) is
@out = Value typ
super
- i: ValueInput i
- values: [ValueInput v for v in *values]
+ i: Input.value i
+ values: [Input.value v for v in *values]
tick: =>
{ :i, :values } = @inputs
@@ -74,7 +74,7 @@ class edge extends Op
setup: (inputs) =>
{ value } = match 'bool', inputs
- super value: EventInput
+ super value: Input.value
tick: =>
now = @inputs.value!
@@ -91,8 +91,8 @@ default defaults to zero."
setup: (params) =>
{ value, init } = match 'any any', inputs
super
- value: EventInput value
- init: ColdInput init
+ value: Input.event value
+ init: Input.cold init
@out = Value value\type!
@out\set @inputs.init\unwrap!
diff --git a/pilot.alv b/pilot.alv
deleted file mode 100644
index 1623d46..0000000
--- a/pilot.alv
+++ /dev/null
@@ -1,46 +0,0 @@
-([1]import* math time string util)
-([2]import osc envelope midi pilot)
-
-([3]defn make-lfo (type)
- ([8]fn ([5]f) ([7]lfo ([6]* f 0.5) type)))
-
-([9]def sin-lfo ([10]make-lfo 'sin'))
-
-([11]defn send (name value)
- ([13]osc/out '127.0.0.1' 9000
- ([12].. '/param/' name '/set')
- value))
-
-([28]def trigger ([48]edge ([47]switch ([45]tick .3) true false)))
-
-([29]pilot/play
- trigger #(trigger)
- ([30]ramp 8) #(ch)
- 3 #(oct)
- ([37]switch #(note)
- ([46]tick .5)
- 'a' 'c' 'e' 'b' 'c')
- 4 4)
-
-([54]def f false t true)
-
-([67]def kick ([50]edge ([59]switch ([60]tick .15) t f f f t f f f)))
-
-([23]send 'radius' ([25]
- ([24]envelope/ar ([14]midi/cc 0) ([15]midi/cc 1))
- kick))
-
-([33]pilot/play kick
- 12 2 ([55]switch ([56]tick 2) 'c' 'a' 'f') 2)
-([51]pilot/play ([52]edge ([61]switch ([62]tick .15) f f t f f f f t)) 13 3 'c' 1)
-
-([31]defn cc-effect (name a b)
- ([41]pilot/effect name ([32]midi/cc a 0 16) ([40]midi/cc b 0 16)))
-
-([42]cc-effect 'FEE' 16 17)
-#([43]cc-effect 'REV' 18 19)
-([44]cc-effect 'BIT' 20 21)
-
-([63]pilot/effect "REV" ([66]+ 1 ([64]* ([65]lfo .18) 2)) 2)
-
-([19]send 'offset' ([20]sin-lfo ([16]keep ([26]midi/cc 24 0 4))))
diff --git a/spec/core/cell_spec.moon b/spec/core/cell_spec.moon
index 78861ab..8b6d68b 100644
--- a/spec/core/cell_spec.moon
+++ b/spec/core/cell_spec.moon
@@ -1,4 +1,5 @@
-import Cell, RootCell, Value, Scope, Registry, globals from require 'core'
+import Cell, RootCell from require 'core.cell'
+import Value, Scope, Registry, globals from require 'core'
import Logger from require 'logger'
Logger.init 'silent'
@@ -20,14 +21,14 @@ describe 'Cell', ->
describe 'RootCell', ->
test 'head is always "do"', ->
- cell = RootCell\parse {}
+ cell = Cell\parse_root {}
assert.is.equal (Value.sym 'do'), cell\head!
cell = RootCell nil, { hello_world, two_plus_two }
assert.is.equal (Value.sym 'do'), cell\head!
test 'tail is all children', ->
- cell = RootCell\parse {}
+ cell = Cell\parse_root {}
assert.is.same {}, cell\tail!
cell = RootCell nil, { hello_world, two_plus_two }
diff --git a/spec/core/pattern_spec.moon b/spec/core/pattern_spec.moon
index 64dbef2..4825dcc 100644
--- a/spec/core/pattern_spec.moon
+++ b/spec/core/pattern_spec.moon
@@ -1,4 +1,4 @@
-import Pattern, match from require 'core.pattern'
+import Pattern, match from require 'core.base.match'
import Result, Value from require 'core'
-- wrap in non-const result
diff --git a/test.alv b/test.alv
deleted file mode 100644
index 221b7ea..0000000
--- a/test.alv
+++ /dev/null
@@ -1,9 +0,0 @@
-([1]import* debug time)
-([2]import midi)
-
-([3]trace "help")
-
-([4]def device ([5]midi/input "system:midi_capture_2"))
-
-([6]out 'name' ([7]midi/cc device 0))
-([8]out 'gate' ([9]midi/gate device 44))