aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authors-ol <s-ol@users.noreply.github.com>2020-09-04 09:26:39 +0000
committers-ol <s+removethis@s-ol.nu>2025-03-02 14:24:49 +0000
commitcce57208d5db4a539bcf10af09331a9ddd43f7ab (patch)
tree125c4189c10d282a99261783f1eac0809ec324b4
parentsupport :vis() on builtins (diff)
downloadalive-cce57208d5db4a539bcf10af09331a9ddd43f7ab.tar.gz
alive-cce57208d5db4a539bcf10af09331a9ddd43f7ab.zip
remove IOStreams, support optional polling in Ops
-rw-r--r--alv/base/init.moon5
-rw-r--r--alv/base/input.moon8
-rw-r--r--alv/base/match.moon2
-rw-r--r--alv/base/op.moon12
-rw-r--r--alv/init.moon5
-rw-r--r--alv/result/base.moon7
-rw-r--r--alv/result/init.moon3
-rw-r--r--alv/result/io.moon55
-rw-r--r--alv/rtnode.moon82
-rw-r--r--spec/input_spec.moon38
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