diff options
| author | s-ol <s-ol@users.noreply.github.com> | 2020-09-04 09:26:39 +0000 |
|---|---|---|
| committer | s-ol <s+removethis@s-ol.nu> | 2025-03-02 14:24:49 +0000 |
| commit | cce57208d5db4a539bcf10af09331a9ddd43f7ab (patch) | |
| tree | 125c4189c10d282a99261783f1eac0809ec324b4 | |
| parent | support :vis() on builtins (diff) | |
| download | alive-cce57208d5db4a539bcf10af09331a9ddd43f7ab.tar.gz alive-cce57208d5db4a539bcf10af09331a9ddd43f7ab.zip | |
remove IOStreams, support optional polling in Ops
| -rw-r--r-- | alv/base/init.moon | 5 | ||||
| -rw-r--r-- | alv/base/input.moon | 8 | ||||
| -rw-r--r-- | alv/base/match.moon | 2 | ||||
| -rw-r--r-- | alv/base/op.moon | 12 | ||||
| -rw-r--r-- | alv/init.moon | 5 | ||||
| -rw-r--r-- | alv/result/base.moon | 7 | ||||
| -rw-r--r-- | alv/result/init.moon | 3 | ||||
| -rw-r--r-- | alv/result/io.moon | 55 | ||||
| -rw-r--r-- | alv/rtnode.moon | 82 | ||||
| -rw-r--r-- | spec/input_spec.moon | 38 |
10 files changed, 79 insertions, 138 deletions
diff --git a/alv/base/init.moon b/alv/base/init.moon index 586a6bd..69979a5 100644 --- a/alv/base/init.moon +++ b/alv/base/init.moon @@ -16,7 +16,6 @@ -- @see Constant -- @see SigStream -- @see EvtStream --- @see IOStream -- @see type.T -- @see type.Primitive -- @see type.Array @@ -30,7 +29,7 @@ import Builtin from require 'alv.base.builtin' import FnDef from require 'alv.base.fndef' import Input from require 'alv.base.input' import const, sig, evt from require 'alv.base.match' -import Constant, SigStream, EvtStream, IOStream from require 'alv.result' +import Constant, SigStream, EvtStream from require 'alv.result' import T, Primitive, Array, Struct from require 'alv.type' import RTNode from require 'alv.rtnode' import Error from require 'alv.error' @@ -45,7 +44,7 @@ import Error from require 'alv.error' -- redundant exports, to keep anything an extension might need in one import -- Results - :Constant, :SigStream, :EvtStream, :IOStream + :Constant, :SigStream, :EvtStream -- Types :T, :Primitive, :Array, :Struct diff --git a/alv/base/input.moon b/alv/base/input.moon index f29bd10..87e00de 100644 --- a/alv/base/input.moon +++ b/alv/base/input.moon @@ -2,7 +2,7 @@ -- Update scheduling policy for `Op` arguments. -- -- @classmod Input -import Constant, SigStream, EvtStream, IOStream from require 'alv.result' +import Constant, SigStream, EvtStream from require 'alv.result' import RTNode from require 'alv.rtnode' inherits = (klass, frm) -> @@ -112,9 +112,8 @@ class Input -- Behaviour depends on what kind of `Result` `value` is: -- -- - `Constant`: treated like `cold`. - -- - `SigStream`: Marked dirty only if old and new `SigStream` differ. - -- - `EvtStream` and `IOStream`: Marked dirty only if the current - -- `EvtStream` is dirty. + -- - `SigStream`: Marked dirty only if old and new `result` differ. + -- - `EvtStream`: Marked dirty only if the current `result` is dirty. -- -- This is the most common `Input` strategy. -- @@ -152,7 +151,6 @@ mapping = { [Constant]: ValueInput [SigStream]: ValueInput [EvtStream]: Input - [IOStream]: IOInput } { diff --git a/alv/base/match.moon b/alv/base/match.moon index 4653107..f5fa99c 100644 --- a/alv/base/match.moon +++ b/alv/base/match.moon @@ -295,7 +295,7 @@ sig = setmetatable {}, { __call: (...) => Type '~', ... } ---- `Type` shorthands for matching `EvtStream` and `IOStream`s. +--- `Type` shorthands for matching `EvtStream`s. -- -- Call or index with a string to obtain an `Type` instance. -- Call to obtain a wildcard pattern. diff --git a/alv/base/op.moon b/alv/base/op.moon index f20518d..bb48b09 100644 --- a/alv/base/op.moon +++ b/alv/base/op.moon @@ -112,6 +112,18 @@ class Op else {} + --- poll for external changes (optional). + -- + -- If implemented, this method will be called at a high frequency and should + -- return `true` whenever processing is required due to an external event or + -- condition. After polling all such IO Ops a new tick will be executed if any + -- returned true. The implementation of `poll` is responsible for triggering + -- the `tick` method by writing to an internally allocated `Result` that has + -- been inserted into `inputs`. + -- + -- @function poll + -- @treturn ?boolean dirty whether processing is required + --- implementation utilities. -- -- super-methods and utilities for use by implementations. diff --git a/alv/init.moon b/alv/init.moon index 7ff3e99..3de3c9b 100644 --- a/alv/init.moon +++ b/alv/init.moon @@ -15,7 +15,7 @@ version = require 'alv.version' import Logger from require 'alv.logger' import T, Primitive, Struct, Array from require 'alv.type' -import Constant, SigStream, EvtStream, IOStream from require 'alv.result' +import Constant, SigStream, EvtStream from require 'alv.result' import RTNode from require 'alv.rtnode' import Scope from require 'alv.scope' import Error from require 'alv.error' @@ -37,7 +37,6 @@ cycle\resolve! -- @tfield Constant Constant -- @tfield SigStream SigStream -- @tfield EvtStream EvtStream --- @tfield IOStream IOStream -- @tfield type.T T -- @tfield type.Primitive Primitive -- @tfield type.Array Array @@ -55,7 +54,7 @@ cycle\resolve! { :version - :Constant, :SigStream, :EvtStream, :IOStream + :Constant, :SigStream, :EvtStream :Cell, :RootCell :RTNode, :Scope, :Error diff --git a/alv/result/base.moon b/alv/result/base.moon index 734d614..a9a668d 100644 --- a/alv/result/base.moon +++ b/alv/result/base.moon @@ -1,7 +1,7 @@ ---- -- base Result interface. -- --- implemented by `Constant`, `SigStream`, `EvtStream`, and `IOStream`. +-- implemented by `Constant`, `SigStream`, and `EvtStream`. -- -- @classmod Result import Type from require 'alv.type' @@ -11,7 +11,7 @@ class Result --- Result interface. -- -- Methods that have to be implemented by `Result` implementations --- (`SigStream`, `EvtStream`, `IOStream`). +-- (`Constant`, `SigStream`, `EvtStream`). -- -- @section interface @@ -38,8 +38,7 @@ class Result --- the metatype string for this Result. -- - -- one of `=` (`Constant`), `~` (`SigStream`), - -- `!` (`EvtStream` and `IOStream`). + -- one of `=` (`Constant`), `~` (`SigStream`), `!` (`EvtStream`). -- -- @tfield string metatype diff --git a/alv/result/init.moon b/alv/result/init.moon index 200e4e1..8e5c64f 100644 --- a/alv/result/init.moon +++ b/alv/result/init.moon @@ -5,17 +5,14 @@ -- @see Constant -- @see SigStream -- @see EvtStream --- @see IOStream -- -- @module result import Constant from require 'alv.result.const' import SigStream from require 'alv.result.sig' import EvtStream from require 'alv.result.evt' -import IOStream from require 'alv.result.io' { :Constant :SigStream :EvtStream - :IOStream } diff --git a/alv/result/io.moon b/alv/result/io.moon deleted file mode 100644 index f68f672..0000000 --- a/alv/result/io.moon +++ /dev/null @@ -1,55 +0,0 @@ ----- --- Stream of external side-effects. --- --- Unlike other `Stream`s, this is not updated/set by an `Op` instace, but is --- continuously polled for changes by the runtime and may mark itself as --- *dirty* at any point in time. All runtime execution happens due to IOStream --- updates, which ripple through the `RTNode` tree. --- --- @classmod IOStream -import EvtStream from require 'alv.result.evt' - -class IOStream extends EvtStream ---- IOStream interface. --- --- methods that have to be implemented by `IOStream` implementations. --- @section interface - - --- construct a new IOStream. - -- - -- Must prepare the instance for `dirty` to be called. - -- The super-constructor should be called to set `Result.type`. - -- - -- @classmethod - -- @tparam type.Type type the type of this stream. - new: (type) => super type - - --- create a mutable copy of this stream. - -- - -- Used to wrap insulate eval-cycles from each other. - -- - -- @treturn IOStream - fork: => @ - - --- poll for changes. - -- - -- Called every frame by the main event loop to update internal state. - poll: => - - --- check whether this adapter requires processing. - -- - -- Must return a boolean indicating whether `Op`s that refer to this instance - -- via `Input.hot` should be notified (via `Op:tick`). May be called multiple - -- times. May be called before `poll` on the first frame after construction. - -- - -- If this is not overriden, the `EvtStream` interface can be used, see - -- `EvtStream.add`, `EvtStream.unwrap`, and `EvtStream.dirty`. - -- - -- @function dirty - -- @treturn bool whether processing is required - - __inherited: (cls) => cls.__base.__tostring or= @__tostring - -{ - :IOStream -} diff --git a/alv/rtnode.moon b/alv/rtnode.moon index 98c230e..28b1d77 100644 --- a/alv/rtnode.moon +++ b/alv/rtnode.moon @@ -5,7 +5,6 @@ -- between `Op`s. -- -- @classmod RTNode - import Error from require 'alv.error' class RTNode @@ -35,29 +34,38 @@ class RTNode @result.metatype --- create a copy of this result with result-copy semantics. - -- the copy has the same @result and @side_inputs, but will not update - -- anything on \tick. + -- + -- the copy has the same @result and @side_inputs, but will not (doubly)update + -- anything on `tick` or `poll`. make_ref: => RTNode result: @result, side_inputs: @side_inputs - --- poll all IOStream instances that are effecting this (sub)tree. + --- poll all IO Ops effecting this (sub)tree for changes. + -- -- should be called once per frame on the root, right before tick. + -- + -- @treturn ?boolean whether any IO Op marked itself dirty. poll_io: => - for result, input in pairs @side_inputs - result\poll! if input.mode == 'io' + dirty = false + + for op in *@io_ops + dirty or= op\poll! + + dirty --- in depth-first order, tick all Ops which have dirty Inputs. -- -- short-circuits if there are no dirty Inputs in the entire subtree tick: => - any_dirty = false - for result, input in pairs @side_inputs - if input\dirty! - any_dirty = true - break + if #@io_ops == 0 + any_dirty = false + for result, input in pairs @side_inputs + if input\dirty! + any_dirty = true + break - -- early-out if no Inputs are dirty in this whole subtree - return unless any_dirty + -- early-out if no Inputs are dirty in this whole subtree + return unless any_dirty for child in *@children child\tick! @@ -81,17 +89,17 @@ class RTNode buf ..= ">" buf - --- the `Result` result - -- + --- the `Result` result. -- @tfield ?Result result - --- an Op - -- + --- an Op. -- @tfield ?Op op - --- list of child `RTNode`s from subexpressions - -- - -- @tfield {}|{RTNode,...} children + --- list of child `RTNode`s from subexpressions. + -- @tfield {RTNode,...} children + + --- cached list of all IO Ops inside this RTNode and children. + -- @tfield {Op,...} io_ops --- cached mapping of all `Result`/`Input` pairs affecting this RTNode. -- @@ -110,28 +118,48 @@ class RTNode @result = params.result @op = params.op @children = params.children or {} + @io_ops = params.io_ops or {} if params.side_inputs @side_inputs = params.side_inputs return - @side_inputs, is_child = {}, {} + @side_inputs = {} + is_child = {} + for child in *@children + -- collect child side_inputs for result, input in pairs child.side_inputs @side_inputs[result] = input + + -- collect child io_ops + for op in *child.io_ops + table.insert @io_ops, op + if child.result is_child[child.result] = true if @op + if @op.poll + table.insert @io_ops, @op + + -- find inputs outside this tree for i in @op\all_inputs! - if i.mode == 'io' or (i.mode == 'hot' and not is_child[i.result]) + if i.mode == 'hot' and not is_child[i.result] @side_inputs[i.result] = i - if @result - if next @side_inputs - assert @result.metatype != '=', "Const result #{@result} has side_inputs" - elseif @result.metatype == '~' - @result = @result.type\mk_const @result\unwrap! + -- "freeze" ~-streams if there are no IO Ops around + return unless @result and @result.metatype == '~' + return if #@io_ops > 0 + + all_const = true + for result, input in pairs @side_inputs + if result.metatype != '=' + all_const = false + break + return unless all_const + + @result = @result.type\mk_const @result\unwrap! { :RTNode diff --git a/spec/input_spec.moon b/spec/input_spec.moon index 9237fd8..04cf16e 100644 --- a/spec/input_spec.moon +++ b/spec/input_spec.moon @@ -1,13 +1,9 @@ import do_setup, do_teardown from require 'spec.test_setup' -import Input, T, Result, IOStream from require 'alv.base' +import Input, T, Result from require 'alv.base' setup do_setup teardown do_teardown -class MyIO extends IOStream - new: => super T.my_io - dirty: => @is_dirty - basic_tests = (result, input) -> it 'gives access to the Result', -> assert.is.equal result, input.result @@ -114,38 +110,6 @@ describe 'Input.hot', -> assert.is.true input\dirty! assert.is.true result\dirty! - describe 'with IOStream', -> - result = MyIO! - input = Input.hot result - - basic_tests result, input - - it 'is marked for lifting', -> - assert.is.equal 'io', input.mode - - it 'is dirty when the IOStream is dirty', -> - result.is_dirty = false - - assert.is.false input\dirty! - assert.is.false result\dirty! - - input\setup nil - assert.is.false input\dirty! - input\finish_setup! - - COPILOT\next_tick! - result.is_dirty = true - - assert.is.true input\dirty! - assert.is.true result\dirty! - - input\setup nil - assert.is.true input\dirty! - input\finish_setup! - - assert.is.true input\dirty! - assert.is.true result\dirty! - describe 'with SigStream', -> result = T.num\mk_sig 1 local input |
