git.s-ol.nu alive / 6a7a2dd
add ldoc documentation s-ol 1 year, 10 months ago
38 changed file(s) with 1033 addition(s) and 606 deletion(s). Raw diff Collapse all Expand all
1616 - [lua-rtmidi][rtmidi]: `luarocks install https://raw.githubusercontent.com/s-ol/lua-rtmidi/master/lua-rtmidi-dev-1.rockspec`
1717 - [busted][busted]: `luarocks install busted` (optional, for tests)
1818 - [discount][discount]: `luarocks install discount` (optional, for docs)
19 - [ldoc][ldoc]: `luarocks install ldoc` (optional, for docs)
1920
2021 \* these are also `moonscript` dependencies and do not neet to be installed manually.
2122
0 ----
1 -- Builtin / Special Form / `Cell`-evaluation Strategy.
2 --
3 -- Responsible for quoting/evaluating subexpressions, instantiating and patching
4 -- `Op`s, updating the current `Scope`, etc.
5 -- See `builtin` and `invoke` for examples.
6 --
7 -- @classmod Action
8
9 class Action
10 --- Action interface.
11 --
12 -- methods that have to be implemented by `Action` implementations.
13 -- @section interface
14
15 --- create a new instance
16 -- @tparam Value head the (`\eval`d) head of the Cell to evaluate
17 -- @tparam Tag tag the Tag of the expression to evaluate
18 new: (head, @tag) =>
19 @patch head
20
21 --- perform the actual evaluation
22 --
23 -- Implementations should:
24 --
25 -- - eval or quote `tail` values
26 -- - perform scope effects
27 -- - wrap all child-results
28 --
29 -- @tparam Scope scope the active scope
30 -- @tparam {AST,...} tail the arguments to this expression
31 -- @treturn Result the result of this evaluation
32 eval: (scope, tail) => error "not implemented"
33
34 --- free resources
35 destroy: =>
36
37 --- attempt to update this instance with a new `@head` prior to `\eval`.
38 --
39 -- If `\patch` returns `false`, this instance is `\destroy`ed and recreated.
40 -- Must *not* return `false` when called immediately after `\new`.
41 -- Only considered if Action types of old and new expression match.
42 --
43 -- @tparam AST head the new head value
44 -- @treturn bool whether patching was successful
45 patch: (head) =>
46 if head == @head
47 true
48
49 @head = head
50
51 --- static functions
52 -- @section static
53
54 --- get-or-update an `Action` for a given tag, then evaluate it.
55 --
56 -- Find the action for the expression with `Tag` `tag` if it exists,
57 -- and is compatible with the new `head`, otherwise instantiate one.
58 -- Register the `Action` with `tag`, evaluate it and return the `Result`.
59 --
60 -- @tparam Scope scope the active scope
61 -- @tparam Tag tag the tag of the `Cell` being evaluated
62 -- @tparam Value head the (`\eval`d) head of the `Cell` being evaluated
63 -- @tparam {Ast,...} tail the raw AST parameters to the `Cell` being evaluated
64 -- @treturn Result the result of evaluation
65 eval_cell: (scope, tag, head, tail) =>
66 last = tag\last!
67 compatible = last and
68 (last.__class == @) and
69 (last\patch head) and
70 last
71
72 L\trace if compatible
73 "reusing #{last} for #{tag} <#{@__name} #{head}>"
74 else if last
75 "replacing #{last} with new #{tag} <#{@__name} #{head}>"
76 else
77 "initializing #{tag} <#{@__name} #{head}>"
78
79 action = if compatible
80 tag\keep compatible
81 compatible
82 else
83 last\destroy! if last
84 with next = @ head, tag
85 tag\replace next
86
87 action\eval scope, tail
88
89 __tostring: => "<#{@@__name} #{@head}>"
90 __inherited: (cls) => cls.__base.__tostring = @__tostring
91
92 {
93 :Action
94 }
0 ----
1 -- `alive` user-function definition (`fndef`).
2 --
3 -- When called, expands to its body with params bound to the fn arguments (see
4 -- `invoke.fn_invoke`).
5 --
6 -- @classmod FnDef
7
8 class FnDef
9 --- create a new instance
10 --
11 -- @tparam {Value,...} params (`\quote`d) naming the function parameters
12 -- @tparam AST body (`\quote`d) expression the function evaluates to
13 -- @tparam Scope scope the lexical scope the function was defined in (closure)
14 new: (@params, @body, @scope) =>
15
16 __tostring: =>
17 "(fn (#{table.concat [p\stringify! for p in *@params], ' '}) ...)"
18
19 {
20 :FnDef
21 }
0 ----
1 -- Base definitions for extensions.
2 --
3 -- This module exports the following classes that extension modules may need:
4 --
5 -- @module base
6 -- @see IO
7 -- @see Op
8 -- @see Action
9 -- @see FnDef
10 -- @see Input
11 -- @see match
12 -- @see Value
13 -- @see Result
14
15 import IO from require 'core.base.io'
16 import Op from require 'core.base.op'
17 import Action from require 'core.base.action'
18 import FnDef from require 'core.base.fndef'
19 import Input from require 'core.base.input'
20 import match from require 'core.base.match'
21 import Value from require 'core.value'
22 import Result from require 'core.result'
23
24 {
25 :IO
26 :Op
27 :Action
28 :FnDef
29 :Input
30 :match
31
32 -- redundant exports, to keep anything an extension might need in one import
33 :Value, :Result
34 }
0 ----
1 -- Update scheduling policy for `Op` arguments.
2 --
3 -- @classmod Input
4 import Value from require 'core.value'
5 import Result from require 'core.result'
6
7 local ColdInput, ValueInput, EventInput, IOInput
8
9 class Input
10 --- Input interface.
11 --
12 -- Methods that have to be implemented by `Input` implementations.
13 -- @section interface
14
15 --- create an instance (optional).
16 --
17 -- `value` is either a `Value` or a `Result` instance and should be
18 -- unwrapped and assigned to `@stream`.
19 --
20 -- @function new
21 -- @tparam Value|Result value
22 new: (value) =>
23 assert value, "nil passed to Input: #{value}"
24 @stream = switch value.__class
25 when Result
26 assert value.value, "Input from result without value!"
27 when Value
28 value
29 else
30 error "Input from unknown value: #{value}"
31
32 --- copy state from old instance (optional).
33 --
34 -- called by `Op``\setup` with another `Input` instance or `nil` once this instance is
35 -- registered. Must prepare this instance for `\dirty`.
36 --
37 -- May enter a 'setup state' that is exited using `\finish_setup`.
38 --
39 -- @function setup
40 -- @tparam `Input?` prev previous `Input` intance or nil
41 setup: (prev) =>
42
43 --- whether this input requires processing (optional).
44 --
45 -- must return a boolean indicating whether `Op`s that refer to this instance
46 -- should be notified (via `Op``\tick`). If not overwritten, delegates to
47 -- `@stream\dirty`.
48 --
49 -- @treturn bool whether processing is necessary
50 dirty: => @stream\dirty!
51
52 --- leave setup state (optional).
53 --
54 -- called after the `Op` has completed (or skipped) its first `Op``\tick` after
55 -- `Op``\setup`. Must prepare this instance for dataflow operation.
56 finish_setup: =>
57
58 --- unwrap to Lua value (optional).
59 --
60 -- @treturn any the raw Lua value
61 unwrap: => @stream\unwrap!
62
63 --- return the type name of this `Input` (optional).
64 type: => @stream.type
65
66 --- methods.
67 --
68 -- @section methods
69
70 --- alias for `\unwrap`.
71 __call: => @stream\unwrap!
72
73 __tostring: => "#{@@__name}:#{@stream}"
74 __inherited: (cls) =>
75 cls.__base.__call = @__call
76 cls.__base.__tostring = @__tostring
77
78 --- constructors
79 -- @section constructors
80
81 --- Create a `ColdInput`.
82 --
83 -- Never marked dirty. Use this for input streams that are only read when
84 -- another `Input` is dirty.
85 --
86 -- @tparam Value|Result value
87 cold: (value) -> ColdInput value
88
89 --- Create a `ValueInput`.
90 --
91 -- Marked dirty for the eval-tick if old and new `Value` differ. This is the
92 -- most common `Input` strategy. Should be used whenever a
93 -- value denotes state.
94 --
95 -- @tparam Value|Result value
96 value: (value) -> ValueInput value
97
98 --- Create an `EventInput`.
99 --
100 -- Only marked dirty if the `Value` itself is dirty. Should be used whenever
101 -- an `Input` denotes a momentary event or impulse.
102 --
103 -- @tparam Value|Result value
104 event: (value) -> EventInput value
105
106 --- Create an `IOInput`.
107 --
108 -- Marked dirty only when an `IO` is dirty. Must be used only for `Value`s
109 -- which `\unwrap` to `IO` instances.
110 --
111 -- @tparam Value|Result value
112 io: (value) -> IOInput value
113
114 class ColdInput extends Input
115 dirty: => false
116
117 class ValueInput extends Input
118 setup: (old) => @dirty_setup = not old or @stream != old.stream
119 finish_setup: => @dirty_setup = false
120 dirty: => @dirty_setup or @stream\dirty!
121
122 class EventInput extends Input
123
124 class IOInput extends Input
125 impure: true
126 dirty: => @stream\unwrap!\dirty!
127
128 {
129 :Input
130 }
0 ----
1 -- Incoming side-effect adapter, polled by the main event loop to pump
2 -- events into the dataflow graph.
3 --
4 -- @classmod IO
5
6 class IO
7 --- IO interface.
8 --
9 -- methods that have to be implemented by `IO` implementations.
10 -- @section interface
11
12 --- construct a new instance.
13 --
14 -- Must prepare the instance for `\dirty` to be called.
15 new: =>
16
17 --- poll for changes.
18 --
19 -- Called every frame by the main event loop to update internal state.
20 tick: =>
21
22 --- check whether this adapter requires processing.
23 --
24 -- Must return a boolean indicating whether `Op`s that refer to this instance
25 -- via `Input``.io` should be notified (via `Op``\tick`). May be called multiple
26 -- times. May be called before `\tick` on the first frame after construction.
27 --
28 -- @treturn bool whether processing is required
29 dirty: =>
30
31 {
32 :IO
33 }
0 ----
1 -- Utilities for matching `Result` types.
2 --
3 -- @module match
4 unpack or= table.unpack
5
6 class Pattern
7 new: (opts) =>
8 if 'string' == type opts
9 splat, const, type, opt = opts\match '^(%*?)(=?)([%w%-%_%/]+)(%??)$'
10 assert type, "couldn't parse type pattern '#{opts}'"
11 opts = {
12 :type
13 splat: splat == '*'
14 const: const == '='
15 opt: opt == '?'
16 }
17
18 @type = opts.type
19 @const = opts.const
20 @opt = opts.opt
21 @splat = opts.splat
22
23 matches: (result) =>
24 return false unless result
25
26 if @const
27 return false unless result\is_const!
28
29 if not result.value
30 return @type == 'nil'
31
32 return true if @type == 'any'
33
34 result.value.type == @type
35
36 match: (results) =>
37 if @splat
38 matched = while @matches results[1]
39 table.remove results, 1
40
41 assert @opt or #matched > 0, "expected at least one argument for spread"
42 matched
43 else
44 matches = @matches results[1]
45 assert @opt or matches, "couldn't match argument #{results[1]} as #{@}"
46 if matches then table.remove results, 1
47
48 __tostring: =>
49 str = @type
50 str = '*' .. str if @splat
51 str = '=' .. str if @const
52 str = str .. '?' if @opt
53 str
54
55 --- match inputs to a argument type definition.
56 --
57 -- `pattern` is a string of type entries. Every type entry can be like this:
58 --
59 -- - `any` - matches one `Result` and returns it.
60 -- - `typename` - matches one `Result` of type `typename` and returns it.
61 -- - `=typename` - matches one eval-time const `Result`s of type `typname` and
62 -- returns it.
63 -- - `typename?` - matches what `typename` would match, if possible (greedy).
64 -- Otherwise returns `nil`.
65 -- - `*typename` - matches as many `typename` `Result`s as possible (greedy).
66 -- Throws if there isn't at least one such `Result`. Returns a sequence of
67 -- `Result`s.
68 -- - `*typename?` - like `*typename`, except it also matches zero `Result`s.
69 -- - `*=typename`, `=typename?` and `*=typename?` behave as expected.
70 --
71 -- Except for `typename?` and `*typename?`, all entries throw if they cannot
72 -- match the next `Result` in `inputs`.
73 --
74 -- Throws if there are leftover `inputs` after matching all of `pattern`.
75 --
76 -- @tparam string pattern the argument type definition
77 -- @tparam {Result,...} inputs the list of inputs
78 -- @treturn {Result|{Result,...},...} the inputs as matched against `pattern`
79 match = (pattern, inputs) ->
80 patterns = while pattern
81 pat, rest = pattern\match '^([^ ]+) (.*)$'
82 pat = pattern unless pat
83 pattern = rest
84 Pattern pat
85 values = [p\match inputs for p in *patterns]
86 assert #inputs == 0, "#{#inputs} extra arguments given!"
87 values
88
89 {
90 :Pattern
91 :match
92 }
0 ----
1 -- Persistent expression Operator.
2 --
3 -- @classmod Op
4 import Value from require 'core.value'
5
6 class Op
7 --- Op interface.
8 --
9 -- methods that have to be implemented by `Op` implementations.
10 -- @section interface
11
12 --- construct a new instance.
13 --
14 -- The super-constructor can be used to construct a `Value` instance in `@out`.
15 --
16 -- @function new
17
18 --- parse arguments and patch self.
19 --
20 -- Called once every eval-cycle. `inputs` is a list of `Result`s that are the
21 -- argument to this op. The `inputs` have to be wrapped in `Input` instances
22 -- to define update behaviour. Use `base.match` to parse them, then delegate to
23 -- `super\setup` to patch the `Input` instances.
24 --
25 -- @function setup
26 -- @tparam inputs [`Result`] a sequence of `Result`s
27 -- @tparam scope Scope the active scope
28
29 --- handle incoming events and update `@out` (optional).
30 --
31 -- Called once per frame if any `Input`s are dirty. Some `Input`s (like
32 -- `ValueInput`) have special behaviour immediately after `\setup`, that can
33 -- cause them to become dirty at eval-time. In this case, an eval-time tick
34 -- is executed. You can detect this using the `setup` parameter.
35 --
36 -- `\tick` is called after `\setup`. `tick` is not called immediately after
37 -- `\setup` if no `@inputs` are dirty. Update `@out` here.
38 --
39 -- @tparam bool setup whether this is an eval-time tick
40 tick: =>
41
42 --- called when the Op is destroyed (optional).
43 destroy: =>
44
45 --- a `Value` instance representing this Op's computed output value.
46 --
47 -- Must be set to a `Value` instance once `\setup` finishes. Must not change
48 -- type, be removed or replaced outside of `\new` and `\setup`. Should have a
49 -- value assigned via `\set` or the `Value` constructor once `\tick` is
50 -- called the first time. If `@out`'s value is not initialized in `\new`
51 -- or `\setup`, the implementation must make sure `\tick(true)` is called at
52 -- least on the first eval-cycle the Op goes through, e.g. by using a
53 -- `ValueInput`.
54 --
55 -- @tfield Value out
56
57 --- implementation utilities.
58 --
59 -- super-methods and utilities for use by implementations.
60 -- @section super
61
62 --- if `type` is passed, an output stream is instantiated.
63 -- if `init` is passed, the stream is initialized to that Lua value.
64 -- it is okay not to use this and create the output stream in :setup() if the
65 -- type is not known at this time.
66 --
67 -- @tparam[opt] string type the type-name for `@out`
68 -- @tparam[optchain] any init the initial value for `@out`
69 new: (type, init) =>
70 if type
71 @out = Value type, init
72
73 --- setup previous `@inputs`, if any, with the new inputs, and write them to
74 -- `@inputs`. The `inputs` table can be nested with string or integer keys,
75 -- but all leaf-entries must be `Input` instances. It must not contain loops
76 -- or instances of other classes.
77 --
78 -- @tparam table inputs table of `Input`s
79 setup: do
80 do_setup = (old, cur) ->
81 for k, cur_val in pairs cur
82 old_val = old and old[k]
83
84 -- are these inputs or nested tables?
85 cur_plain = cur_val and not cur_val.__class
86 old_plain = old_val and not old_val.__class
87
88 if cur_plain and old_plain
89 -- both are tables, recurse
90 do_setup old_val, cur_val
91 elseif not (cur_plain or old_plain)
92 -- both are streams (or nil), setup them
93 cur_val\setup old_val
94
95 (inputs) =>
96 old_inputs = @inputs
97 @inputs = inputs
98 do_setup old_inputs, @inputs
99
100 --- yield all `Input`s from the (potentially nested) inputs table
101 --
102 -- @treturn iterator iterator over all `Input`s
103 all_inputs: do
104 do_yield = (table) ->
105 for k, v in pairs table
106 if v.__class
107 coroutine.yield v
108 else
109 do_yield v
110
111 => coroutine.wrap -> do_yield @inputs
112
113 --- `\unwrap` all `Input`s in `@inputs` and return a table with the same
114 -- shape.
115 --
116 -- @treturn table the values of all `Input`s
117 unwrap_all: do
118 do_unwrap = (value) ->
119 if value.__class
120 value\unwrap!
121 else
122 {k, do_unwrap v for k,v in pairs value}
123
124 => do_unwrap @inputs
125
126 __tostring: => "<op: #{@@__name}>"
127 __inherited: (cls) => cls.__base.__tostring = @__tostring
128
129 {
130 :Op
131 }
+0
-298
core/base.moon less more
0 -- base definitions for extensions
1 import Value from require 'core.value'
2 import Result from require 'core.result'
3 import match from require 'core.pattern'
4
5 unpack or= table.unpack
6
7 -- an incoming side-effect adapter, polled by the main event loop to pump
8 -- events into the dataflow graph.
9 --
10 -- subclasses must implement this interface:
11 --
12 -- :new() - construct a new instance
13 --
14 -- must prepare the instance for :dirty().
15 --
16 -- :tick() - poll for changes
17 --
18 -- called every frame by the event loop to update internal state.
19 --
20 -- :dirty() - whether this adapter requires processing
21 --
22 -- must return a boolean indicating whether `Op`s that refer to this instance
23 -- via `IOInput` should be notified (via `Op:tick()`). May be called multiple
24 -- multiple times. May be called before :tick() on the first frame after
25 -- construction.
26 --
27 class IO
28 -- called in the main event loop
29 tick: =>
30
31 -- whether a tree update is necessary
32 dirty: =>
33
34 -- a persistent expression Operator
35 --
36 -- subclasses must implement this interface:
37 --
38 -- :new() - construct a new instance
39 --
40 -- the super-constructor can be used to construct a `Value` instance in @out.
41 --
42 -- :setup(inputs, scope) - parse arguments and patch self
43 --
44 -- called once every eval-cycle. `inputs` is a list of `Result`s that are the
45 -- argument to this op. The `inputs` have to be wrapped in `Input` instances
46 -- to define update behaviour. Use `match` to parse them, then delegate to
47 -- super to patch the `Input` instances.
48 --
49 -- :tick(setup) - handle incoming events and update @out
50 --
51 -- called once per frame if any inputs are dirty. Some `Input`s (like
52 -- `ValueInput`) have special behaviour immediately after :setup(). You can
53 -- detect this using the `setup` parameter, which is true the first time
54 -- :tick() is called after :setup(). :tick() is not called immediately after
55 -- :setup() if no `@inputs` are dirty. Update @out here.
56 --
57 -- :destroy() - called when the Op is destroyed
58 --
59 -- .out - a `Value` instance representing this Op's computed output value.
60 --
61 -- @out must be set to a `Value` instance once :setup() finishes. @out must
62 -- not change type, be removed or replaced outside of :new() and :setup().
63 -- @out should have a value assigned via :set() or the `Value` constructor
64 -- once :tick(true) is called. If @out's value is not initialized in :new()
65 -- or :setup(), the implementation must make sure :tick(true) is called at
66 -- least on the first eval-cycle the Op goes through, e.g. by using a
67 -- `ValueInput`.
68 --
69 class Op
70 -- super-implementations for extensions
71 -- if `type` is passed, an output stream is instantiated.
72 -- if `init` is passed, the stream is initialized to that Lua value.
73 -- it is okay not to use this and create the output stream in :setup() if the
74 -- type is not known at this time.
75 new: (type, init) =>
76 if type
77 @out = Value type, init
78
79 -- setups previous @inputs, if any, with the new inputs, and writes them to
80 -- `@inputs`. The `inputs` table can be nested with string or integer keys,
81 -- but all leaf-entries must be `Input` instances. It must not contain loops
82 -- or instances of other classes.
83 setup: do
84 do_setup = (old, cur) ->
85 for k, cur_val in pairs cur
86 old_val = old and old[k]
87
88 -- are these inputs or nested tables?
89 cur_plain = cur_val and not cur_val.__class
90 old_plain = old_val and not old_val.__class
91
92 if cur_plain and old_plain
93 -- both are tables, recurse
94 do_setup old_val, cur_val
95 elseif not (cur_plain or old_plain)
96 -- both are streams (or nil), setup them
97 cur_val\setup old_val
98
99 (inputs) =>
100 old_inputs = @inputs
101 @inputs = inputs
102 do_setup old_inputs, @inputs
103
104 tick: =>
105 destroy: =>
106
107 -- utilities
108 -- iterate over the (potentially nested) inputs table
109 all_inputs: do
110 do_yield = (table) ->
111 for k, v in pairs table
112 if v.__class
113 coroutine.yield v
114 else
115 do_yield v
116
117 => coroutine.wrap -> do_yield @inputs
118
119 unwrap_all: do
120 do_unwrap = (value) ->
121 if value.__class
122 value\unwrap!
123 else
124 {k, do_unwrap v for k,v in pairs value}
125
126 => do_unwrap @inputs
127
128 assert_types: (...) =>
129 num = select '#', ...
130 assert #@inputs >= num, "argument count mismatch"
131 @assert_first_types ...
132
133 assert_first_types: (...) =>
134 num = select '#', ...
135 for i = 1, num
136 expect = select i, ...
137 assert @inputs[i].type == expect, "expected argument #{i} of #{@} to be of type #{expect} but found #{@inputs[i]}"
138
139 -- static
140 __tostring: => "<op: #{@@__name}>"
141 __inherited: (cls) => cls.__base.__tostring = @__tostring
142
143 -- a builtin / special form / cell-evaluation strategy.
144 --
145 -- responsible for quoting/evaluating subexpressions, instantiating and patching
146 -- Ops updating the current Scope, etc. See core.builtin and core.invoke for
147 -- many examples.
148 class Action
149 -- head: the (:eval'd) head of the Cell to evaluate (a Const)
150 -- tag: the Tag of the expression to evaluate
151 new: (head, @tag) =>
152 @patch head
153
154 -- * eval args
155 -- * perform scope effects
156 -- * patch nested exprs
157 -- * return runtime-tree value
158 eval: (scope, tail) => error "not implemented"
159
160 -- free resources
161 destroy: =>
162
163 -- update this instance for :eval() with new head
164 -- if :patch() returns false, this instance is :destroy'ed and recreated
165 -- instead must *not* return false when called after :new()
166 -- only considered if Action types match
167 patch: (head) =>
168 if head == @head
169 true
170
171 @head = head
172
173 -- static
174 -- find & patch the action for the expression with Tag 'tag' if it exists,
175 -- and is compatible with the new Cell contents, otherwise instantiate it.
176 -- register the action with the tag, evaluate it and return the Result
177 @eval_cell: (scope, tag, head, tail) =>
178 last = tag\last!
179 compatible = last and
180 (last.__class == @) and
181 (last\patch head) and
182 last
183
184 L\trace if compatible
185 "reusing #{last} for #{tag} <#{@__name} #{head}>"
186 else if last
187 "replacing #{last} with new #{tag} <#{@__name} #{head}>"
188 else
189 "initializing #{tag} <#{@__name} #{head}>"
190
191 action = if compatible
192 tag\keep compatible
193 compatible
194 else
195 last\destroy! if last
196 with next = @ head, tag
197 tag\replace next
198
199 action\eval scope, tail
200
201 __tostring: => "<#{@@__name} #{@head}>"
202 __inherited: (cls) => cls.__base.__tostring = @__tostring
203
204 -- an ALV function definition
205 --
206 -- when called, expands its body with params bound to the fn arguments
207 -- (see core.invoke.fn-invoke)
208 class FnDef
209 -- params: sequence of (:quote'd) symbols, each naming a function parameter
210 -- body: (:quote'd) expression the function evaluates to
211 -- scope: the lexical scope the function was defined in (closure)
212 new: (@params, @body, @scope) =>
213
214 __tostring: =>
215 "(fn (#{table.concat [p\stringify! for p in *@params], ' '}) ...)"
216
217 -- an update scheduling policy for `Op`.
218 --
219 -- subclasses must implement this interface:
220 --
221 -- :new(value) - create an instance
222 --
223 -- `value` is either a `Value` or a `Result` instance and should be unwrapped.
224 --
225 -- :setup(prev) - copy state from old instance
226 --
227 -- called by `Op:setup()` with another `Input` instance or `nil` once this instance is
228 -- registered. Must prepare this instance for :dirty().
229 --
230 --- :dirty() - whether this input requires processing
231 --
232 -- must return a boolean indicating whether `Op`s that refer to this instance
233 -- should be notified (via `Op:tick()`).
234 --
235 -- :finish_setup() - leave setup state
236 --
237 -- called after the Op has completed (or skipped) its first `Op:tick()` after
238 -- `Op:setup()`. Must prepare this instance for dataflow operation.
239 --
240 class Input
241 new: (value) =>
242 assert value, "nil passed to Input: #{value}"
243 @stream = switch value.__class
244 when Result
245 assert value.value, "Input from result without value!"
246 when Value
247 value
248 else
249 error "Input from unknown value: #{value}"
250
251 setup: (previous) =>
252
253 finish_setup: =>
254 dirty: => @stream\dirty!
255 unwrap: => @stream\unwrap!
256 type: => @stream.type
257
258 __call: => @stream\unwrap!
259 __tostring: => "#{@@__name}:#{@stream}"
260 __inherited: (cls) =>
261 cls.__base.__call = @__call
262 cls.__base.__tostring = @__tostring
263
264 -- Never marked dirty. Use this for input streams that are only read when
265 -- another input fires.
266 class ColdInput extends Input
267 dirty: => false
268
269 -- Marked dirty for the setup-tick if old and new stream differ in current
270 -- value. This is the most common `Input` strategy. Should be used whenever a
271 -- value denotes state.
272 class ValueInput extends Input
273 setup: (old) => @dirty_setup = not old or @stream != old.stream
274 finish_setup: => @dirty_setup = false
275 dirty: => @dirty_setup or @stream\dirty!
276
277 -- Only marked dirty if the input stream itself is dirty. Should be used
278 -- whenever a value denotes a momentary event or impulse.
279 class EventInput extends Input
280
281 -- Marked dirty when an IO object is dirty. Must be used for IO values.
282 class IOInput extends Input
283 impure: true
284 dirty: => @stream\unwrap!\dirty!
285
286 {
287 :IO
288 :Op
289 :Action
290 :FnDef
291
292 :ValueInput, :EventInput, :IOInput, :ColdInput
293
294 -- redundant exports, to keep anything an extension might need in one import
295 :Value, :Result
296 :match
297 }
0 -- ALV Cell type
0 ----
1 -- S-Expression Cell, implements the `AST` interface.
2 --
3 -- Consists of a head expression and any number of tail expressions (both `AST`
4 -- nodes), a `Tag`, and optionally the internal whitespace as parsed.
5 --
6 -- @classmod Cell
17 import Value from require 'core.value'
28 import op_invoke, fn_invoke from require 'core.invoke'
39 import Tag from require 'core.tag'
410
5 -- An S-Expression with a head expression and any number of tail expressions,
6 -- an optional tag, and optionally the internal whitespace as parsed.
11 local RootCell
12
713 class Cell
8 -- AST interface
14 --- methods
15 -- @section methods
16
17 -- tag: the parsed Tag
18 -- children: sequence of child AST Nodes
19 -- white: optional sequence of whitespace segments ([0 .. #@children])
20 new: (@tag=Tag.blank!, @children, @white) =>
21 if not @white
22 @white = [' ' for i=1,#@children]
23 @white[0] = ''
24
25 assert #@white == #@children, "mismatched whitespace length"
26
27 --- get the head of the cell.
28 --
29 -- @treturn AST
30 head: => @children[1]
31
32 --- get the tail of the cell.
33 --
34 -- @treturn {AST,...}
35 tail: => [c for c in *@children[2,]]
36
37 __tostring: => @stringify 2
38
39 --- AST interface
40 --
41 -- `Cell` implements the `AST` interface.
42 -- @section ast
43
44 --- evaluate this Cell.
45 --
46 -- `\eval`uates the head of the expression, and finds the appropriate
47 -- `Action` to invoke:
48 --
49 -- - if head is an `opdef`, use `invoke.op_invoke`
50 -- - if head is a `fndef`, use `invoke.fn_invoke`
51 -- - if head is a `builtin`, unwrap it
52 --
53 -- then calls `\eval_cell` on the `Action`.
54 --
55 -- @tparam Scope scope the scope to evaluate in
56 -- @treturn Result the evaluation result
957 eval: (scope) =>
1058 head = assert @head!, "cannot evaluate expr without head"
1159 head = (head\eval scope)\const!
2371
2472 Action\eval_cell scope, @tag, head, @tail!
2573
26 -- quoting a Cell recursively quotes children, but preserves identity. This
27 -- means that a quoted Cell may only be 'used' once. Use :clone() otherwise.
74 --- quote this Cell, preserving its identity.
75 --
76 -- Recursively quotes children, but preserves identity (i.e, shares the
77 -- `Tag`). A quoted Cell may only be 'used' once. If you want to `\eval` a
78 -- `Cell` multiple times, use `\clone`.
79 --
80 -- @treturn Cell quoted
2881 quote: (scope) =>
2982 children = [child\quote scope for child in *@children]
3083 Cell @tag, children, @white
3184
85 --- create a clone with its own identity.
86 --
3287 -- creates a clone of this Cell with its own identity by prepending a `parent`
33 -- tag and cloning all child expressoins recursively.
88 -- to `@tag` and cloning all child expressoins recursively.
89 --
90 -- @treturn Cell clone
3491 clone: (parent) =>
3592 tag = @tag\clone parent
3693 children = [child\clone parent for child in *@children]
3794 Cell tag, children, @white
3895
96 --- stringify this Cell.
97 --
98 -- if `depth` is passed, does not faithfully recreate the original string but
99 -- rather create useful debug output.
100 --
101 -- @tparam[opt] int depth the maximum depth, defaults to infinite
102 -- @treturn string the exact string this Cell was parsed from, unless `@tag`
103 -- changed
39104 stringify: (depth=-1) =>
40105 buf = ''
41106 buf ..= if depth > 0 then '' else @white[0]
53118
54119 '(' .. tag .. buf .. ')'
55120
56 -- internal
57 -- tag: the parsed Tag
58 -- children: sequence of child AST Nodes
59 -- white: optional sequence of whitespace segments ([0 .. #@children])
60 new: (@tag=Tag.blank!, @children, @white) =>
61 if not @white
62 @white = [' ' for i=1,#@children]
63 @white[0] = ''
64
65 assert #@white == #@children, "mismatched whitespace length"
66
67 head: => @children[1]
68 tail: => [c for c in *@children[2,]]
69
70 -- static
71 __tostring: => @stringify 2
121 --- static functions
122 -- @section static
72123
73124 parse_args = (tag, parts) ->
74125 if not parts
81132 white[i/2] = parts[i+1]
82133
83134 tag, children, white
84 @parse: (...) =>
135 --- parse a Cell (for parsing with Lpeg).
136 --
137 -- @tparam[opt] Tag tag
138 -- @tparam table parts
139 -- @treturn Cell
140 parse: (...) =>
85141 tag, children, white = parse_args ...
86142 @ tag, children, white
87143
88 -- A parenthesis-less Cell (root of an ALV document).
89 --
90 -- has an implicit head of 'do'.
144 --- parse a root Cell (for parsing with Lpeg).
145 --
146 -- Root-Cells are at the root of an ALV document.
147 -- They have an implicit head of 'do' and a `[0]` tag.
148 --
149 -- @tparam table parts
150 -- @treturn Cell
151 parse_root: (parts) =>
152 RootCell.parse RootCell, (Tag\parse '0'), parts
153
154 -- @type RootCell
91155 class RootCell extends Cell
92156 head: => Value.sym 'do'
93157 tail: => @children
102166
103167 buf
104168
105 @parse: (...) =>
106 @__parent.parse @, (Tag\parse '0'), ...
107
108169 {
109170 :Cell
110171 :RootCell
0 project = 'alive copilot'
1 title = 'alive copilot reference'
2 format = 'discount'
3 dir = 'docs/copilot'
0 ----
1 -- `alive` public API.
2 --
3 -- @see Value
4 -- @see Result
5 -- @see Cell
6 -- @see Scope
7 -- @see Registry
8 -- @see Tag
9 -- @field globals
10 -- @field parse
11 -- @field eval
12 --
13 -- @module init
014 L or= setmetatable {}, __index: => ->
115
216 import Value from require 'core.value'
519 import Registry from require 'core.registry'
620 import Tag from require 'core.tag'
721
8 import Cell, RootCell from require 'core.cell'
22 import Cell from require 'core.cell'
923 import cell, program from require 'core.parsing'
1024
1125 with require 'core.cycle'
1327
1428 globals = Scope.from_table require 'core.builtin'
1529
30 --- exports
31 -- @table exports
32 -- @tfield Value Value
33 -- @tfield Result Result
34 -- @tfield Cell Cell
35 -- @tfield RootCell RootCell
36 -- @tfield Scope Scope
37 -- @tfield Registry Registry
38 -- @tfield Tag Tag
39 -- @tfield Scope globals global definitons
40 -- @tfield parse function to turn a `string` into a root `Cell`
1641 {
1742 :Value, :Result
1843 :Cell, :RootCell
0 ----
1 -- Builtins for invoking `Op`s and `FnDef`s.
2 --
3 -- @module invoke
04 import Value from require 'core.value'
15 import Result from require 'core.result'
26 import Action from require 'core.base'
0 ----
1 -- Lpeg Grammar for parsing `alive` code.
2 --
3 -- @module parsing
04 import Value from require 'core.value'
1 import Cell, RootCell from require 'core.cell'
5 import Cell from require 'core.cell'
26 import Tag from require 'core.tag'
37 import R, S, P, V, C, Ct from require 'lpeg'
48
3438 cell = (P '(') * tag^-1 * (V 'explist') * (P ')') / Cell\parse
3539
3640 root = P {
37 (V 'explist') / RootCell\parse
41 (V 'explist') / Cell\parse_root
3842 :expr, :explist, :cell
3943 }
4044
4549
4650 program = root * -1
4751
52 --- exports
53 -- @table exports
54 -- @tfield pattern comment
55 -- @tfield pattern space
56 -- @tfield pattern atom
57 -- @tfield pattern expr
58 -- @tfield pattern explist
59 -- @tfield pattern explist
60 -- @tfield pattern cell
61 -- @tfield pattern root
62 -- @tfield pattern program the main parsing entrypoint
4863 {
4964 :comment
5065 :space
+0
-65
core/pattern.moon less more
0 unpack or= table.unpack
1
2 class Pattern
3 new: (opts) =>
4 if 'string' == type opts
5 splat, const, type, opt = opts\match '^(%*?)(=?)([%w%-%_%/]+)(%??)$'
6 assert type, "couldn't parse type pattern '#{opts}'"
7 opts = {
8 :type
9 splat: splat == '*'
10 const: const == '='
11 opt: opt == '?'
12 }
13
14 @type = opts.type
15 @const = opts.const
16 @opt = opts.opt
17 @splat = opts.splat
18
19 matches: (result) =>
20 return false unless result
21
22 if @const
23 return false unless result\is_const!
24
25 if not result.value
26 return @type == 'nil'
27
28 return true if @type == 'any'
29
30 result.value.type == @type
31
32 match: (results) =>
33 if @splat
34 matched = while @matches results[1]
35 table.remove results, 1
36
37 assert @opt or #matched > 0, "expected at least one argument for spread"
38 matched
39 else
40 matches = @matches results[1]
41 assert @opt or matches, "couldn't match argument #{results[1]} as #{@}"
42 if matches then table.remove results, 1
43
44 __tostring: =>
45 str = @type
46 str = '*' .. str if @splat
47 str = '=' .. str if @const
48 str = str .. '?' if @opt
49 str
50
51 match = (pattern, results) ->
52 patterns = while pattern
53 pat, rest = pattern\match '^([^ ]+) (.*)$'
54 pat = pattern unless pat
55 pattern = rest
56 Pattern pat
57 values = [p\match results for p in *patterns]
58 assert #results == 0, "#{#results} extra arguments given!"
59 values
60
61 {
62 :Pattern
63 :match
64 }
0 ----
1 -- `Tag` Registry.
2 --
3 -- @classmod Registry
4
05 import Value from require 'core.value'
16 import Result from require 'core.result'
27
38 class Registry
4 new: () =>
5 @map = {}
6 @io = {}
7
8 @tick = 0
9 @kr = Result value: Value.bool true
10
119 -- methods for Tag
1210
1311 last: (index) => @last_map[index]
2321
2422 active: -> assert Registry.active_registry, "no active Registry!"
2523
26 -- public methods
24 next_tag: => #@map + 1
2725
26 --- methods
27 -- @section methods
28
29 --- create an instance.
30 new: =>
31 @map = {}
32 @io = {}
33
34 @tick = 0
35 @kr = Result value: Value.bool true
36
37 --- wrap a function with an eval-cycle.
38 --
39 -- @tparam function fn
40 -- @treturn function `fn` wrapped with eval-cycle logic
2841 wrap_eval: (fn) => (...) ->
2942 @grab!
3043 @last_map, @map, @pending = @map, {}, {}
4659
4760 @release!
4861
62 --- wrap a function with a tick.
63 --
64 -- @tparam function fn
65 -- @treturn function `fn` wrapped with tick logic
4966 wrap_tick: (fn) => (...) ->
5067 @grab!
5168 @tick += 1
6582 assert @ == @@active_registry, "not the active registry!"
6683 @@active_registry, @prev = @prev, nil
6784
68 next_tag: => #@map + 1
69
7085 class SimpleRegistry extends Registry
7186 new: =>
7287 @cnt = 1
0 ----
1 -- Result of evaluating an expression.
2 --
3 -- Contains (all optional):
4 --
5 -- - `@value`: a `Value`
6 -- - `@op`: an `Op` (to update)
7 -- - `@children`: a lsit of child `Results` (from subexpressions)
8 -- - `@side_inputs`: cached list of all `Inputs` affecting any `Op` in this
9 -- subtree
10 --
11 -- `Result`s form a tree that controls execution order and message passing
12 -- between `Op`s.
13 --
14 -- @classmod Result
015 import base from require 'core.cycle'
116
2 -- Result of evaluating an expression
3 -- carries (all optional):
4 -- - a Value
5 -- - an Op (to update)
6 -- - children (results of subexpressions that were evaluated)
7 -- - cached list of all Dispatchers affecting all Ops in the subtree
8 --
9 -- Results form a tree that controls execution order and message passing
10 -- between Ops.
1117 class Result
12 -- params: table with optional keys op, value, children
18 --- methods
19 -- @section methods
20
21 --- create a new Result.
22 -- @param params table with optional keys op, value, children. default: {}
1323 new: (params={}) =>
1424 @value = params.value
1525 @op = params.op
2737 if input.impure or not is_child[input.stream]
2838 @side_inputs[input.stream] = input
2939
40 --- return whether this Result's value is const.
3041 is_const: => not next @side_inputs
3142
32 -- asserts value-constness and returns the value
43 --- assert value-constness and returns the value.
44 -- @tparam[opt] string msg the error message to throw
3345 const: (msg) =>
3446 assert not (next @side_inputs), msg or "eval-time const expected"
3547 @value
3648
37 -- asserts a value exists and returns its type
49 --- assert this result has a value, returns its type.
3850 type: =>
3951 assert @value, "Result with value expected"
4052 @value.type
4153
42 -- create a value-copy of this result that has the same impulses but without
43 -- affecting the original's update logic
54 --- create a copy of this result with value-copy semantics.
55 -- the copy has the same @value and @side_inputs, but will not update
56 -- anything on \tick.
4457 make_ref: =>
4558 with Result value: @value
4659 .side_inputs = @side_inputs
4760
48 -- tick all IO instances that are effecting this (sub) tree
49 -- should be called once per frame on the root, right before tick
61 --- tick all IO instances that are effecting this (sub)tree.
62 -- should be called once per frame on the root, right before tick.
5063 tick_io: =>
5164 for stream, input in pairs @side_inputs
5265 if input.__class == base.IOInput
5366 io = input!
5467 io\tick!
5568
56 -- in depth-first order, tick all Ops who have dirty Stream inputs or impulses
69 --- in depth-first order, tick all Ops which have dirty Inputs.
5770 --
58 -- short-circuits if there are no dirty Streams in the entire subtree
71 -- short-circuits if there are no dirty Inputs in the entire subtree
5972 tick: =>
6073 any_dirty = false
6174 for stream, input in pairs @side_inputs
6376 any_dirty = true
6477 break
6578
66 -- early-out if no streams are dirty in this whole subtree
79 -- early-out if no Inputs are dirty in this whole subtree
6780 return unless any_dirty
6881
6982 for child in *@children
7083 child\tick!
7184
7285 if @op
73 -- we have to check self_dirty here, because streams from child
74 -- expressions might have changed
86 -- we have to check self_dirty here, because Inputs from children may
87 -- have become dirty due to \tick
7588 self_dirty = false
7689 for stream in @op\all_inputs!
7790 if stream\dirty!
8396
8497 @op\tick!
8598
86 -- static
8799 __tostring: =>
88100 buf = "<result=#{@value}"
89101 buf ..= " #{@op}" if @op
0 ----
1 -- Mapping from `sym`s to `Result`s.
2 --
3 -- @classmod Scope
04 import Value from require 'core.value'
15 import Result from require 'core.result'
26
37 class Scope
8 --- methods
9 -- @section methods
10
11 --- create an instance.
12 --
13 -- @tparam[opt] Scope parent a parent this scope inherits definitions from
14 -- @tparam[opt] Scope dynamic_parent a parent scope that should be checked for
15 -- dynamic definitions
416 new: (@parent, @dynamic_parent) =>
517 @values = {}
618
19 --- set a Lua value in the scope.
20 --
21 -- wraps `val` in a `Value` and `Result` before calling `\set`.
22 --
23 -- @tparam string key
24 -- @tparam any val
725 set_raw: (key, val) =>
826 value = Value.wrap val, key
927 @values[key] = Result :value
1028
29 --- set a symbol to a `Result`.
30 --
31 -- @tparam string key
32 -- @tparam Result val
1133 set: (key, val) =>
1234 L\trace "setting #{key} = #{val} in #{@}"
1335 assert val.__class == Result, "expected #{key}=#{val} to be Result"
1941 parent or= @parent
2042 return parent and L\push parent\get, key
2143
44 --- resolve a key in this Scope.
45 --
46 -- @tparam string key the key to resolve
47 -- @tparam[opt] string prefix a prefix (for internal use with nested scopes)
48 -- @treturn ?Result the value of the definition that was found, or `nil`
2249 get: (key, prefix='') =>
2350 L\debug "checking for #{key} in #{@}"
2451 if val = @values[key]
3461 assert child and child.value.type == 'scope', "#{start} is not a scope (looking for #{key})"
3562 child.value\unwrap!\get rest, "#{prefix}#{start}/"
3663
64 --- copy definitions from another scope.
65 --
66 -- copies all definitions from `other`. Does not copy inherited definitions.
67 --
68 -- @tparam Scope other
3769 use: (other) =>
3870 L\trace "using defs from #{other} in #{@}"
3971 for k, v in pairs other.values
4072 @values[k] = v
4173
74 --- static functions
75 -- @section static
76
77 --- converts a Lua table to a Scope.
78 --
79 -- `tbl` may contain more tables (or `Scope`s).
80 -- Uses `Value``.wrap` on the values recursively.
81 --
82 -- @tparam table tbl the table to convert
83 -- @treturn Scope
4284 from_table: (tbl) ->
4385 with Scope!
4486 for k, v in pairs tbl
63105 buf ..= ">"
64106 buf
65107
66 :Scope
108 {
109 :Scope
110 }
0 ----
1 -- Identity provider for `Cell`s and `Action`s.
2 --
3 -- Tags are one of:
4 -- - 'blank' (`[?]`, to be auto-assigned by the Copilot)
5 -- - literal (`[1]`)
6 -- - cloned (`[X.Y]`, obtained by cloning Y with parent X)
7 --
8 -- @classmod Tag
09 import Registry from require 'core.registry'
110
211 local ClonedTag
918 dummy = DummyReg!
1019
1120 class Tag
12 -- obtain the value that was previously registered (using keep or replace) for
13 -- this tag
21 --- methods
22 -- @section methods
23
24 --- obtain the registered value of the last eval-cycle.
25 --
26 -- Obtain the value that was previously registered (using `\keep` or
27 -- `\replace`) for this tag on the last eval-cylce.
28 --
29 -- @treturn ?any
1430 last: =>
1531 if index = @index!
1632 Registry.active!\last index
1733
18 -- assert that `expr` is the value that was previously registered for this
19 -- tag, and keep it for the current eval cycle.
20 -- fails for blank tags.
34 --- keep using a the value from the last eval-cycle.
35 --
36 -- Assert that `expr` is the value that was previously registered for this
37 -- tag, and keep it for the current eval cycle. Fails for blank tags.
38 --
39 -- @tparam any expr the value to register
2140 keep: (expr) =>
2241 index = assert @index!
2342 assert expr == Registry.active!\last index
2443 Registry.active!\replace index, expr
2544
26 -- register `expr` for this tag for the current eval cycle.
27 -- registers blank tags.
45 --- register `expr` for this tag for the current eval cycle.
46 --
47 -- Will mark blank tags for auto-assignment at the end of the eval cycle.
48 --
49 -- @tparam any expr the value to register
2850 replace: (expr) =>
2951 if index = @index!
3052 Registry.active!\replace index, expr
3153 else
3254 Registry.active!\init @, expr
3355
34 -- create a copy of this tag scoped to a `parent` tag.
35 -- registers blank tags.
56 --- create a copy of this tag scoped to a `parent` tag.
57 --
58 -- Will mark blank tags for auto-assignment at the end of the eval cycle.
59 --
60 -- @tparam Tag parent the parent tag
61 -- @treturn Tag the cloned tag
3662 clone: (parent) =>
3763 -- ensure this tag is registered for the current eval cycle,
3864 -- even if it is blank and has no associated value
4369
4470 assert parent, "need parent to clone!"
4571 ClonedTag @, parent
72
73 stringify: => if @value then "[#{@value}]" else ''
74 __tostring: => if @value then "#{@value}" else '?'
4675
4776 -- internal
4877 new: (@value) =>
5584 assert not @value, "#{@} is not blank"
5685 @value = value
5786
58 -- static
87 --- static functions
88 -- @section static
5989
60 @blank: -> Tag!
61 @parse: (num) => @ tonumber num
90 --- create a blank `Tag`.
91 --
92 -- @treturn Tag
93 blank: -> Tag!
6294
63 stringify: => if @value then "[#{@value}]" else ''
64 __tostring: => if @value then "#{@value}" else '?'
95 --- parse a `Tag` (for Lpeg parsing).
96 --
97 -- @tparam string num the number-string
98 -- @treturn Tag
99 parse: (num) => @ tonumber num
65100
66101 class ClonedTag extends Tag
67102 new: (@original, @parent) =>
0 ----
1 -- `alive` Value(stream), implements the `AST` inteface.
2 --
3 -- @classmod Value
04 import Result from require 'core.result'
15 import scope, base, registry from require 'core.cycle'
26
610 klass = klass.__parent
711 klass
812
9 -- ALV Type wrapper
1013 class Value
11 -- @type - type name.
12 -- builtin types: * literals: sym, num, bool
13 -- * scope, opdef, fndef, builtin
14 -- @value - Lua value - access through :unwrap()
14 --- methods
15 -- @section methods
16
17 --- construct a new Value.
18 --
19 -- @tparam string type the type name
20 -- @tparam any value the Lua value to be accessed through \unwrap
21 -- @tparam string raw the raw string that resulted in this value. Used by `parsing`.
1522 new: (@type, @value, @raw) =>
16 @updated = nil
1723
24 --- return whether this Value was changed in the current tick.
25 --
26 -- @treturn bool
1827 dirty: => @updated == registry.Registry.active!.tick
1928
29 --- update this Value.
30 --
31 -- Marks this Value as dirty for the remainder of the current tick.
2032 set: (@value) => @updated = registry.Registry.active!.tick
2133
22 -- unwrap to the Lua type
23 -- asserts @type == type, msg if given
34 --- unwrap to the Lua type.
35 --
36 -- Asserts `@type == type` if `type` is given.
37 --
38 -- @tparam[opt] string type the type to check for
39 -- @tparam[optchain] string msg message to throw if type don't match
40 -- @treturn @value
2441 unwrap: (type, msg) =>
2542 assert type == @type, msg or "#{@} is not a #{type}" if type
2643 @value
2744
28 -- AST interface
45 --- alias for `\unwrap`.
46 __call: (...) => @unwrap ...
47
48 --- compare two values.
49 --
50 -- Compares two `Value`s by comparing their types and their Lua values.
51 __eq: (other) => other.type == @type and other.value == @value
52
53 __tostring: =>
54 value = if 'table' == (type @value) and rawget @value, '__base' then @value.__name else @value
55 "<#{@@__name} #{@type}: #{value}>"
56
57 --- AST interface
58 --
59 -- `Value` implements the `AST` interface.
60 -- @section ast
61
62 --- evaluate this literal constant.
63 --
64 -- Throws an error if `@type` is not a literal (`num`, `str` or `sym`).
65 -- Returns an eval-time const result for `num` and `str`.
66 -- Resolves `sym`s in `scope` and returns a reference to them.
67 --
68 -- @tparam Scope scope the scope to evaluate in
69 -- @treturn Result the evaluation result
2970 eval: (scope) =>
3071 switch @type
3172 when 'num', 'str'
3576 else
3677 error "cannot evaluate #{@}"
3778
79 --- quote this literal constant.
80 --
81 -- @treturn Value self
3882 quote: => @
3983
84 --- stringify this literal constant.
85 --
86 -- Throws an error if `@raw` is not set.
87 --
88 -- @treturn string the exact string this Value was parsed from
4089 stringify: => assert @raw, "stringifying Value that wasn't parsed"
4190
91 --- clone this literal constant.
92 --
93 -- @treturn Value self
4294 clone: (prefix) => @
43 -- in case of doubt:
44 -- clone: (prefix) => Value @type, @value, @raw
4595
46 -- static
47 __tostring: =>
48 value = if 'table' == (type @value) and rawget @value, '__base' then @value.__name else @value
49 "<#{@@__name} #{@type}: #{value}>"
50 __call: (...) => @unwrap ...
51 __eq: (other) => other.type == @type and other.value == @value
96 --- static functions
97 -- @section static
5298
53 -- wrap a Lua type
54 @wrap: (val, name='(unknown)') ->
55
99 --- wrap a Lua value.
100 --
101 -- Attempts to guess the type and wrap a Lua value.
102 --
103 -- @tparam any val the value to wrap
104 -- @tparam[opt] string name the name of this value (for error logging)
105 wrap: (val, name='(unknown)') ->
56106 typ = switch type val
57107 when 'number' then 'num'
58108 when 'string' then 'str'
82132 Value typ, val
83133
84134 unescape = (str) -> str\gsub '\\([\'"\\])', '%1'
85 @parse: (type, sep) =>
135 --- create a capture-function (for parsing with Lpeg).
136 --
137 -- @tparam string type the type name (one of `num`, `sym` or `str`)
138 -- @tparam string sep the seperator char (only for `str`)
139 parse: (type, sep) =>
86140 switch type
87141 when 'num' then (match) -> @ 'num', (tonumber match), match
88142 when 'sym' then (match) -> @ 'sym', match, match
89143 when 'str' then (match) -> @ 'str', (unescape match), sep .. match .. sep
90144
91 @num: (num) -> Value 'num', num, tostring num
92 @str: (str) -> Value 'str', str, "'#{str}'"
93 @sym: (sym) -> Value 'sym', sym, sym
94 @bool: (bool) -> Value 'bool', bool, tostring bool
145 --- create a constant number.
146 -- @tparam number num the number
147 num: (num) -> Value 'num', num, tostring num
148
149 --- create a constant string.
150 -- @tparam string str the string
151 str: (str) -> Value 'str', str, "'#{str}'"
152
153 --- create a constant symbol.
154 -- @tparam string sym the symbol
155 sym: (sym) -> Value 'sym', sym, sym
156
157 --- create a constant boolean.
158 -- @tparam boolean bool the boolean
159 bool: (bool) -> Value 'bool', bool, tostring bool
95160
96161 {
97162 :Value
0 ----
1 -- `alive` source code version information
2 --
3 -- @module version
4
5 --- exports
6 -- @table exports
7 -- @tfield string tag the last versions git tag
8 -- @tfield string rev_short the short git revision hash
9 -- @tfield string rev_long the full git revision hash
010 {
111 tag: "v0.0"
2 rev_short: "4742960"
3 rev_long: "4742960c455251f306bf271d50f69724b8beba04"
12 rev_short: "891c138"
13 rev_long: "891c138c8a8a4c9269f3eb70f424b5c1ea3898b0"
414 }
00 *.html
1 copilot
44 REV_LONG=`git rev-parse HEAD`
55
66 cat <<EOF
7 ----
8 -- \`alive\` source code version information
9 --
10 -- @module version
11
12 --- exports
13 -- @table exports
14 -- @tfield string tag the last versions git tag
15 -- @tfield string rev_short the short git revision hash
16 -- @tfield string rev_long the full git revision hash
717 {
818 tag: "${TAG}"
919 rev_short: "${REV_SHORT}"
0 import Op, ValueInput, EventInput, match from require 'core.base'
0 import Op, Input, match from require 'core.base'
11
22 class out extends Op
33 @doc: "(out [name-str?] value) - log value to the console"
55 setup: (inputs) =>
66 { name, value } = match 'str? any', inputs
77 super
8 name: name and ValueInput name
9 value: ValueInput value
8 name: name and Input.value name
9 value: Input.value value
1010
1111 tick: =>
1212 { :name, :value } = @unwrap_all!
0 import Op, ValueInput, match from require 'core.base'
0 import Op, Input, match from require 'core.base'
11
22 all_same = (first, list) ->
33 for v in *list
1919 setup: (inputs) =>
2020 { first, rest } = match "any *any", inputs
2121 super
22 first: ValueInput first
23 rest: [ValueInput v for v in *rest]
22 first: Input.value first
23 rest: [Input.value v for v in *rest]
2424
2525 tick: =>
2626 { :first, :rest } = @unwrap_all!
4444
4545 super if same
4646 {
47 first: ValueInput first
48 rest: [ValueInput v for v in *rest]
47 first: Input.value first
48 rest: [Input.value v for v in *rest]
4949 }
5050 else
5151 {}
7272
7373 setup: (inputs) =>
7474 assert #inputs > 1, "neq need at least two values"
75 super [ValueInput v for v in *inputs]
75 super [Input.value v for v in *inputs]
7676
7777 tick: =>
7878 if not @inputs[1]
104104
105105 setup: (inputs) =>
106106 { value } = match 'any', inputs
107 super value: ValueInput value
107 super value: Input.value value
108108
109109 tick: => @out\set not tobool @inputs.value!
110110
114114
115115 setup: (inputs) =>
116116 { value } = match 'any', inputs
117 super value: ValueInput value
117 super value: Input.value value
118118
119119 tick: => @out\set tobool @inputs\value!
120120
0 import Op, Value, ValueInput, match from require 'core.base'
0 import Op, Value, Input, match from require 'core.base'
11 unpack or= table.unpack
22
33 class ReduceOp extends Op
66 setup: (inputs) =>
77 { first, rest } = match 'num *num', inputs
88 super
9 first: ValueInput first
10 rest: [ValueInput v for v in *rest]
9 first: Input.value first
10 rest: [Input.value v for v in *rest]
1111
1212 tick: =>
1313 { :first, :rest } = @unwrap_all!
5151 setup: (inputs) =>
5252 { val, div } = match 'num num?', inputs
5353 super
54 val: ValueInput val
55 div: ValueInput div or Value.num 2
54 val: Input.value val
55 div: Input.value div or Value.num 2
5656
5757 tick: =>
5858 { :val, :div } = @unwrap_all!
7171 setup: (inputs) =>
7272 { params } = match '*num', inputs
7373 assert #params == arity, "#{@} needs exactly #{arity} parameters" if arity != '*'
74 super [ValueInput p for p in *params]
74 super [Input.value p for p in *params]
7575
7676 tick: => @out\set func unpack @unwrap_all!
7777
0 import Value, Op, ValueInput, IOInput, match from require 'core.base'
0 import Value, Op, Input, match from require 'core.base'
11 import input, output, inout, apply_range from require 'lib.midi.core'
22
33 class gate extends Op
99 setup: (inputs) =>
1010 { port, note, chan } = match 'midi/port num num?', inputs
1111 super
12 port: IOInput port
13 note: ValueInput note
14 chan: ValueInput chan or Value.num -1
12 port: Input.io port
13 note: Input.value note
14 chan: Input.value chan or Value.num -1
1515
1616 tick: =>
1717 { :port, :note, :chan } = @inputs
3636 setup: (inputs) =>
3737 { port, note, chan } = match 'midi/port num num?', inputs
3838 super
39 port: IOInput port
40 note: ValueInput note
41 chan: ValueInput chan or Value.num -1
39 port: Input.io port
40 note: Input.value note
41 chan: Input.value chan or Value.num -1
4242
4343 tick: =>
4444 { :port, :note, :chan } = @inputs
7070 setup: (inputs) =>
7171 { port, cc, chan, range } = match 'midi/port num num? any?', inputs
7272 super
73 port: IOInput port
74 cc: ValueInput cc
75 chan: ValueInput chan or Value.num -1
76 range: ValueInput range or Value.str 'uni'
73 port: Input.io port
74 cc: Input.value cc
75 chan: Input.value chan or Value.num -1
76 range: Input.value range or Value.str 'uni'
7777
7878 if not @out\unwrap!
7979 @out\set apply_range @inputs.range, 0
0 import Op, ValueInput, EventInput, ColdInput, match from require 'core.base'
0 import Op, Input, match from require 'core.base'
11 import pack from require 'osc'
22 import dns, udp from require 'socket'
33
1111 setup: (inputs) =>
1212 { host, port } = match 'str num', inputs
1313 super
14 host: ValueInput host
15 port: ValueInput port
14 host: Input.value host
15 port: Input.value port
1616
1717 tick: =>
1818 { :host, :port } = @unwrap_all!
2929 setup: (inputs) =>
3030 { socket, path, value } = match 'udp/socket str any', inputs
3131 super
32 socket: ColdInput socket
33 path: ColdInput path
34 value: EventInput value
32 socket: Input.cold socket
33 path: Input.cold path
34 value: Input.value value
3535
3636 tick: =>
3737 if @inputs.value\dirty!
5050 setup: (inputs) =>
5151 { socket, path, value } = match 'udp/socket str any', inputs
5252 super
53 socket: ValueInput socket
54 path: ValueInput path
55 value: ValueInput value
53 socket: Input.value socket
54 path: Input.value path
55 value: Input.value value
5656
5757 tick: =>
5858 { :socket, :path, :value } = @unwrap_all!
0 import Op, EventInput, ValueInput, ColdInput, match from require 'core.base'
0 import Op, Input, match from require 'core.base'
11 import udp from require 'socket'
22
33 local conn
2626 { trig, args } = match 'bang *any', inputs
2727 assert #args < 6, "too many arguments!"
2828 super
29 trig: EventInput trig
30 args: [ColdInput a for a in *args]
29 trig: Input.event trig
30 args: [Input.cold a for a in *args]
3131
3232 tick: =>
3333 { :trig, :args } = @inputs
4141 { chan, octv, note, args } = match 'any any any *any', inputs
4242 assert #args < 3, "too many arguments!"
4343 super
44 chan: ColdInput chan
45 octv: ColdInput octv
46 note: EventInput note
47 args: [ColdInput a for a in *args]
44 chan: Input.cold chan
45 octv: Input.cold octv
46 note: Input.event note
47 args: [Input.cold a for a in *args]
4848
4949 tick: =>
5050 if @inputs.note\dirty!
5959 setup: (inputs) =>
6060 { which, a, b } = match 'str num num', inputs
6161 super {
62 ColdInput which
63 ValueInput a
64 ValueInput b
62 Input.cold which
63 Input.value a
64 Input.value b
6565 }
6666
6767 tick: =>
0 import Op, Value, ValueInput, EventInput, match from require 'core.base'
0 import Op, Value, Input, match from require 'core.base'
11
22 apply_range = (range, val) ->
33 if range\type! == 'str'
3535 setup: (inputs) =>
3636 { trig, range } = match 'bang? any?', inputs
3737 super
38 trig: trig and EventInput trig
39 range: ValueInput range or Value.str 'uni'
38 trig: trig and Input.event trig
39 range: Input.value range or Value.str 'uni'
4040
4141 tick: =>
4242 @gen! if @inputs.trig and @inputs.trig\dirty!
5858 setup: (inputs) =>
5959 { trig, range } = match 'bang? any?', inputs
6060 super
61 trig: trig and EventInput trig
62 range: ValueInput range or Value.str 'uni'
61 trig: trig and Input.event trig
62 range: Input.value range or Value.str 'uni'
6363
6464 tick: =>
6565 @gen! if @inputs.trig and @inputs.trig\dirty!
0 import Op, EventInput, ColdInput, match from require 'core.base'
0 import Op, Input, match from require 'core.base'
11 import pack from require 'osc'
22 import dns, udp from require 'socket'
33
1414 assert val\type! == 'num', "only numbers are supported as control values"
1515
1616 super
17 socket: ColdInput socket
18 synth: ColdInput synth
19 trig: EventInput trig
20 ctrls: [ColdInput v for v in *ctrls]
17 trig: Input.event trig
18 socket: Input.cold socket
19 synth: Input.cold synth
20 ctrls: [Input.cold v for v in *ctrls]
2121
2222 tick: =>
2323 if @inputs.trig\dirty! and @inputs.trig!
0 import Op, ValueInput from require 'core.base'
0 import Op, Input from require 'core.base'
11
22 class str extends Op
33 @doc: "(str v1 [v2]...)
44 (.. v1 [v2]...) - concatenate/stringify values"
55 new: => super 'str'
66
7 setup: (inputs) => super [ValueInput v for v in *inputs]
7 setup: (inputs) => super [Input.value v for v in *inputs]
88 tick: => @out\set table.concat [tostring v! for v in *@inputs]
99
1010 {
0 import Registry, Value, IO, Op, ValueInput, IOInput, match
0 import Registry, Value, IO, Op, Input, match
11 from require 'core.base'
22 import monotime from require 'system'
33
2828
2929 setup: (inputs) =>
3030 { fps } = match 'num?', inputs
31 super fps: ValueInput fps or Value.num 60
31 super fps: Input.value fps or Value.num 60
3232
3333 tick: =>
3434 if @inputs.fps\dirty!
5151 setup: (inputs, scope) =>
5252 { clock, freq, wave } = match 'clock? num any?', inputs
5353 super
54 clock: IOInput clock or scope\get '*clock*'
55 freq: ValueInput freq
56 wave: ValueInput wave or default_wave
54 clock: Input.io clock or scope\get '*clock*'
55 freq: Input.value freq
56 wave: Input.value wave or default_wave
5757
5858 tau = math.pi * 2
5959 tick: =>
7979 setup: (inputs, scope) =>
8080 { clock, period, max } = match 'clock? num num?', inputs
8181 super
82 clock: IOInput clock or scope\get '*clock*'
83 period: ValueInput period
84 max: max and ValueInput max
82 clock: Input.io clock or scope\get '*clock*'
83 period: Input.value period
84 max: max and Input.value max
8585
8686 tick: =>
8787 clock_dirty = @inputs.clock\dirty!
107107 setup: (inputs, scope) =>
108108 { clock, period } = match 'clock? num', inputs
109109 super
110 clock: IOInput clock or scope\get '*clock*'
111 period: ValueInput period
110 clock: Input.io clock or scope\get '*clock*'
111 period: Input.value period
112112
113113 tick: =>
114114 if @inputs.clock\dirty!
131131 setup: (inputs, scope) =>
132132 { clock, period } = match 'clock? num', inputs
133133 super
134 clock: IOInput clock or scope\get '*clock*'
135 period: ValueInput period
134 clock: Input.io clock or scope\get '*clock*'
135 period: Input.value period
136136
137137 tick: =>
138138 if @inputs.clock\dirty!
0 import Op, Value, ValueInput, EventInput, ColdInput, match
0 import Op, Value, Input, match
11 from require 'core.base'
22
33 all_same = (list) ->
2323 @out = Value typ
2424
2525 super
26 i: ValueInput i
27 values: [ValueInput v for v in *values]
26 i: Input.value i
27 values: [Input.value v for v in *values]
2828
2929 tick: =>
3030 { :i, :values } = @inputs
7373
7474 setup: (inputs) =>
7575 { value } = match 'bool', inputs
76 super value: EventInput
76 super value: Input.value
7777
7878 tick: =>
7979 now = @inputs.value!
9090 setup: (params) =>
9191 { value, init } = match 'any any', inputs
9292 super
93 value: EventInput value
94 init: ColdInput init
93 value: Input.event value
94 init: Input.cold init
9595
9696 @out = Value value\type!
9797 @out\set @inputs.init\unwrap!
+0
-46
pilot.alv less more
0 ([1]import* math time string util)
1 ([2]import osc envelope midi pilot)
2
3 ([3]defn make-lfo (type)
4 ([8]fn ([5]f) ([7]lfo ([6]* f 0.5) type)))
5
6 ([9]def sin-lfo ([10]make-lfo 'sin'))
7
8 ([11]defn send (name value)
9 ([13]osc/out '127.0.0.1' 9000
10 ([12].. '/param/' name '/set')
11 value))
12
13 ([28]def trigger ([48]edge ([47]switch ([45]tick .3) true false)))
14
15 ([29]pilot/play
16 trigger #(trigger)
17 ([30]ramp 8) #(ch)
18 3 #(oct)
19 ([37]switch #(note)
20 ([46]tick .5)
21 'a' 'c' 'e' 'b' 'c')
22 4 4)
23
24 ([54]def f false t true)
25
26 ([67]def kick ([50]edge ([59]switch ([60]tick .15) t f f f t f f f)))
27
28 ([23]send 'radius' ([25]
29 ([24]envelope/ar ([14]midi/cc 0) ([15]midi/cc 1))
30 kick))
31
32 ([33]pilot/play kick
33 12 2 ([55]switch ([56]tick 2) 'c' 'a' 'f') 2)
34 ([51]pilot/play ([52]edge ([61]switch ([62]tick .15) f f t f f f f t)) 13 3 'c' 1)
35
36 ([31]defn cc-effect (name a b)
37 ([41]pilot/effect name ([32]midi/cc a 0 16) ([40]midi/cc b 0 16)))
38
39 ([42]cc-effect 'FEE' 16 17)
40 #([43]cc-effect 'REV' 18 19)
41 ([44]cc-effect 'BIT' 20 21)
42
43 ([63]pilot/effect "REV" ([66]+ 1 ([64]* ([65]lfo .18) 2)) 2)
44
45 ([19]send 'offset' ([20]sin-lfo ([16]keep ([26]midi/cc 24 0 4))))
0 import Cell, RootCell, Value, Scope, Registry, globals from require 'core'
0 import Cell, RootCell from require 'core.cell'
1 import Value, Scope, Registry, globals from require 'core'
12 import Logger from require 'logger'
23 Logger.init 'silent'
34
1920
2021 describe 'RootCell', ->
2122 test 'head is always "do"', ->
22 cell = RootCell\parse {}
23 cell = Cell\parse_root {}
2324 assert.is.equal (Value.sym 'do'), cell\head!
2425
2526 cell = RootCell nil, { hello_world, two_plus_two }
2627 assert.is.equal (Value.sym 'do'), cell\head!
2728
2829 test 'tail is all children', ->
29 cell = RootCell\parse {}
30 cell = Cell\parse_root {}
3031 assert.is.same {}, cell\tail!
3132
3233 cell = RootCell nil, { hello_world, two_plus_two }
0 import Pattern, match from require 'core.pattern'
0 import Pattern, match from require 'core.base.match'
11 import Result, Value from require 'core'
22
33 -- wrap in non-const result
+0
-9
test.alv less more
0 ([1]import* debug time)
1 ([2]import midi)
2
3 ([3]trace "help")
4
5 ([4]def device ([5]midi/input "system:midi_capture_2"))
6
7 ([6]out 'name' ([7]midi/cc device 0))
8 ([8]out 'gate' ([9]midi/gate device 44))