aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authors-ol <s-ol@users.noreply.github.com>2020-03-24 11:39:25 +0000
committers-ol <s-ol@users.noreply.github.com>2020-03-24 11:39:25 +0000
commit677c0d2f01e14bbeca1583ec7878d80c71c3aa68 (patch)
treecc20e04697da590b546de370599eeaaef647d20c
parentinternals/plugin-guide: first draft (diff)
downloadalive-677c0d2f01e14bbeca1583ec7878d80c71c3aa68.tar.gz
alive-677c0d2f01e14bbeca1583ec7878d80c71c3aa68.zip
Value -> Value/Event/IO-Stream
Close 12
-rw-r--r--core/ast.moon13
-rw-r--r--core/base/init.moon12
-rw-r--r--core/base/input.moon92
-rw-r--r--core/base/io.moon40
-rw-r--r--core/base/op.moon66
-rw-r--r--core/builtin.moon48
-rw-r--r--core/cell.moon4
-rw-r--r--core/init.moon20
-rw-r--r--core/invoke.moon6
-rw-r--r--core/parsing.moon10
-rw-r--r--core/plugin-guide.md111
-rw-r--r--core/registry.moon2
-rw-r--r--core/result.moon21
-rw-r--r--core/scope.moon8
-rw-r--r--core/stream/base.moon79
-rw-r--r--core/stream/event.moon92
-rw-r--r--core/stream/init.moon18
-rw-r--r--core/stream/io.moon48
-rw-r--r--core/stream/value.moon (renamed from core/value.moon)80
-rw-r--r--spec/core/cell_spec.moon26
-rw-r--r--spec/core/input_spec.moon264
-rw-r--r--spec/core/parsing_spec.moon12
-rw-r--r--spec/core/pattern_spec.moon20
-rw-r--r--spec/core/result_spec.moon65
-rw-r--r--spec/core/scope_spec.moon14
-rw-r--r--spec/core/value_spec.moon126
26 files changed, 740 insertions, 557 deletions
diff --git a/core/ast.moon b/core/ast.moon
index 31aed1f..a25d1cd 100644
--- a/core/ast.moon
+++ b/core/ast.moon
@@ -5,7 +5,6 @@
--
-- @classmod AST
-
--- members
-- @section members
@@ -13,8 +12,7 @@
--
-- Evaluate this node and return a `Result`.
--
- -- @class function
- -- @name eval
+ -- @function eval
-- @tparam Scope scope the scope to evaluate in
-- @treturn Result the evaluation result
@@ -22,8 +20,7 @@
--
--- Returns a mutable copy of this Node that shares its identity.
--
- -- @class function
- -- @name quote
+ -- @function quote
-- @treturn AST
--- create a clone with its own identity.
@@ -31,8 +28,7 @@
-- creates a clone of this Cell with its own identity by prepending a `parent`
-- Tag (and cloning all child expressions recursively).
--
- -- @class function
- -- @name clone
+ -- @function clone
-- @tparam Tag parent
-- @treturn AST
@@ -40,6 +36,5 @@
--
-- Should return the exact string this node was parsed from (if it was parsed).
--
- -- @class function
- -- @name stringify
+ -- @function stringify
-- @treturn string the exact string this Node was parsed from
diff --git a/core/base/init.moon b/core/base/init.moon
index bb35140..8e376fc 100644
--- a/core/base/init.moon
+++ b/core/base/init.moon
@@ -4,28 +4,27 @@
-- 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 ValueStream
+-- @see EventStream
+-- @see IOStream
-- @see Result
-- @see Error
-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 ValueStream, EventStream, IOStream from require 'core.stream'
import Result from require 'core.result'
import Error from require 'core.error'
{
- :IO
:Op
:Action
:FnDef
@@ -33,5 +32,6 @@ import Error from require 'core.error'
:match
-- redundant exports, to keep anything an extension might need in one import
- :Value, :Result, :Error
+ :ValueStream, :EventStream, :IOStream
+ :Result, :Error
}
diff --git a/core/base/input.moon b/core/base/input.moon
index c89337f..6922a4f 100644
--- a/core/base/input.moon
+++ b/core/base/input.moon
@@ -2,10 +2,29 @@
-- Update scheduling policy for `Op` arguments.
--
-- @classmod Input
-import Value from require 'core.value'
+import ValueStream, EventStream, IOStream from require 'core.stream'
import Result from require 'core.result'
-local ColdInput, ValueInput, EventInput, IOInput
+inherits = (klass, frm) ->
+ assert klass, "cant find the ancestor of nil"
+ return true if klass == frm
+ while klass.__parent
+ return true if klass.__parent == frm
+ klass = klass.__parent
+ false
+
+match_parent = (inst, map) ->
+ klass = assert inst and inst.__class, "not an instance"
+ if key = map[klass]
+ return key
+
+ while klass.__parent
+ if key = map[klass.__parent]
+ return key
+
+ klass = klass.__parent
+
+local ColdInput, ValueInput, IOInput, mapping
class Input
--- Input interface.
@@ -15,20 +34,10 @@ class Input
--- create a new Input.
--
- -- `value` is either a `Value` or a `Result` instance and should be
- -- unwrapped and assigned to `stream`.
- --
-- @classmethod
- -- @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}"
+ -- @tparam Stream stream
+ new: (@stream) =>
+ assert @stream, "nil passed to Input: #{value}"
--- copy state from old instance (optional).
--
@@ -44,7 +53,7 @@ class Input
--
-- 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`:@{Value:dirty|dirty}.
+ -- `stream`:@{ValueStream:dirty|dirty}.
--
-- @treturn bool whether processing is necessary
dirty: => @stream\dirty!
@@ -65,7 +74,7 @@ class Input
--- the current value
--
- -- @tfield Value stream
+ -- @tfield ValueStream stream
--- members
-- @section members
@@ -86,33 +95,29 @@ class Input
-- 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
+ -- @tparam Stream|Result value
+ @cold: (value) ->
+ if value.__class == Result
+ value = assert value.value, "Input from result without value!"
+ ColdInput value
- --- Create a `value` `Input`.
+ --- Create a `hot` `Input`.
--
- -- 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.
+ -- Behaviour depends on what kind of `Stream` `value` is:
--
- -- @tparam Value|Result value
- @value: (value) -> ValueInput value
-
- --- Create an `event` `Input`.
+ -- - `ValueStream`: Marked dirty for the eval-tick if old and new `ValueStream` differ.
+ -- - `EventStream` and `IOStream`: Marked dirty only if the current `EventStream` is dirty.
--
- -- Only marked dirty if the `Value` itself is dirty. Should be used whenever
- -- an `Input` denotes a momentary event or impulse.
+ -- This is the most common `Input` strategy.
--
- -- @tparam Value|Result value
- @event: (value) -> EventInput value
+ -- @tparam Stream|Result value
+ @hot: (value) ->
+ if value.__class == Result
+ value = assert value.value, "Input from result without value!"
- --- Create an `IO` `Input`.
- --
- -- Marked dirty only when an `IO` is dirty. Must be used only for `Value`s
- -- which @{Value:unwrap|unwrap} to `IO` instances.
- --
- -- @tparam Value|Result value
- @io: (value) -> IOInput value
+ InputType = match_parent value, mapping
+ assert InputType, "Input from unknown value: #{value}"
+ InputType value
class ColdInput extends Input
dirty: => false
@@ -124,11 +129,14 @@ class ValueInput extends Input
return @dirty_setup if @dirty_setup != nil
@stream\dirty!
-class EventInput extends Input
-
class IOInput extends Input
- impure: true
- dirty: => @stream\unwrap!\dirty!
+ io: true
+
+mapping = {
+ [ValueStream]: ValueInput
+ [EventStream]: Input
+ [IOStream]: IOInput
+}
{
:Input
diff --git a/core/base/io.moon b/core/base/io.moon
deleted file mode 100644
index cbb5823..0000000
--- a/core/base/io.moon
+++ /dev/null
@@ -1,40 +0,0 @@
-----
--- Incoming side-effect adapter, creating events for the dataflow graph.
---
--- Polled by the main event loop to kick of events that cause the the dataflow
--- graph to ripple results.
---
--- @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.
- -- @classmethod
- 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: =>
-
- __tostring: => "<IO #{@@__name}>"
- __inherited: (cls) => cls.__base.__tostring = @__tostring
-
-{
- :IO
-}
diff --git a/core/base/op.moon b/core/base/op.moon
index a8da100..4880afd 100644
--- a/core/base/op.moon
+++ b/core/base/op.moon
@@ -2,16 +2,16 @@
-- Persistent expression Operator.
--
-- @classmod Op
-import Value from require 'core.value'
-ident = (tbl) ->
- mt = getmetatable tbl
- setmetatable tbl, nil
-
- id = tostring tbl
-
- setmetatable tbl, mt
- id\sub #'table: 0x'
+deepcopy = (val) ->
+ switch type val
+ when 'number', 'string', 'boolean'
+ val
+ when 'table'
+ assert (not getmetatable {}), "state should only contain simple tables!"
+ {(deepcopy k), (deepcopy v) for k,v in pairs val}
+ else
+ error "state cannot contain values of type '#{type val}'"
class Op
--- members
@@ -33,23 +33,30 @@ class Op
-- Used to wrap insulate eval-cycles from each other. The copy does not have
-- `inputs` set, since it is expected that this is (re)set in `setup`.
--
- -- @treturn Value
+ -- @treturn Op
fork: =>
- with setmetatable {}, getmetatable @
- .state = {k,v for k,v in pairs @state} if @state
- .out = @out\fork! if @out
+ out = if @out then @out\fork
+ state = if @state then deepcopy state
+ @@ out, state
+
+ --- internal state of this Op.
+ --
+ -- This may be any simple Lua value, including Lua tables, as long as it has
+ -- no metatables, multiple references/loops, userdata etc.
+ --
+ -- @tfield table state
- --- `Value` instance representing this Op's computed output value.
+ --- `Stream` 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 an
- -- `Input.value`.
+ -- Must be set to a `Stream` instance once `setup` finishes. Must not change
+ -- type, be removed or replaced outside of `new` and `setup`. If it is a
+ -- `ValueStream`, it should have a value assigned via `set` or the
+ -- 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 an `Input.value`.
--
- -- @tfield Value out
+ -- @tfield Stream out
--- table containing `Input`s to this Op.
--
@@ -66,10 +73,14 @@ class Op
--- construct a new instance.
--
- -- The super-constructor can be used to construct a `Value` instance in `out`.
+ -- The optional parameters `out` and `state` are used by `fork` to duplicate
+ -- an instance. If the constructor is overriden, these parameters must be
+ -- forwarded to the superconstructor unchanged.
--
-- @function new
-- @classmethod
+ -- @tparam ?Stream out `out`
+ -- @tparam ?table state `state`
--- parse arguments and patch self.
--
@@ -109,12 +120,9 @@ class Op
-- type is not known at this time.
--
-- @classmethod
- -- @tparam[opt] string type the type-name for `out`
- -- @tparam[optchain] any init the initial value for `out`
- new: (type, init) =>
- @state = {}
- if type
- @out = Value type, init
+ -- @tparam ?Stream out `out`
+ -- @tparam ?table state `state`
+ new: (@out, @state) =>
do_setup = (old, cur) ->
for k, cur_val in pairs cur
diff --git a/core/builtin.moon b/core/builtin.moon
index bd3183f..f9087c4 100644
--- a/core/builtin.moon
+++ b/core/builtin.moon
@@ -6,14 +6,14 @@
--
-- @module builtin
import Action, Op, FnDef, Input, match from require 'core.base'
-import Value, LiteralValue from require 'core.value'
+import ValueStream, LiteralValue from require 'core.stream.value'
import Result from require 'core.result'
import Cell from require 'core.cell'
import Scope from require 'core.scope'
import Tag from require 'core.tag'
import op_invoke from require 'core.invoke'
-doc = Value.meta
+doc = ValueStream.meta
meta:
name: 'doc'
summary: "Print documentation in console."
@@ -38,7 +38,7 @@ doc = Value.meta
meta = result.value.meta
L\print "(doc #{tail[1]}):\n#{format_meta meta}\n"
-def = Value.meta
+def = ValueStream.meta
meta:
name: 'def'
summary: "Declare symbols in current scope."
@@ -63,7 +63,7 @@ Define the symbols `sym1`, `sym2`, … to resolve to the values of `val-expr1`,
Result :children
-use = Value.meta
+use = ValueStream.meta
meta:
name: 'use'
summary: "Merge scopes into current scope."
@@ -82,7 +82,7 @@ All arguments have to be evaltime constant."
Result!
-require_ = Value.meta
+require_ = ValueStream.meta
meta:
name: 'require'
summary: "Load a module."
@@ -98,10 +98,10 @@ require_ = Value.meta
name = result\const!
L\trace @, "loading module #{name}"
- scope = Value.wrap require "lib.#{name\unwrap 'str'}"
+ scope = ValueStream.wrap require "lib.#{name\unwrap 'str'}"
Result :value
-import_ = Value.meta
+import_ = ValueStream.meta
meta:
name: 'import'
summary: "Require and define modules."
@@ -117,11 +117,11 @@ current scope."
for child in *tail
name = (child\quote scope)\unwrap 'sym'
- value = Value.wrap require "lib.#{name}"
+ value = ValueStream.wrap require "lib.#{name}"
scope\set name, Result :value -- (require "lib.#{name})\unwrap 'scope'
Result!
-import_star = Value.meta
+import_star = ValueStream.meta
meta:
name: 'import*'
summary: "Require and use modules."
@@ -137,12 +137,12 @@ Requires modules `sym1`, `sym2`, … and merges them into the current scope."
for child in *tail
name = (child\quote scope)\unwrap 'sym'
- value = Value.wrap require "lib.#{name}"
+ value = ValueStream.wrap require "lib.#{name}"
scope\use value\unwrap 'scope' -- (require "lib.#{name}")\unwrap 'scope'
Result!
-fn = Value.meta
+fn = ValueStream.meta
meta:
name: 'fn'
summary: "Declare a function."
@@ -163,13 +163,13 @@ function is invoked."
param\quote scope
body = body\quote scope
- Result value: with Value.wrap FnDef param_symbols, body, scope
+ Result value: with ValueStream.wrap FnDef param_symbols, body, scope
.meta = {
summary: "(user defined function)"
examples: { "(??? #{table.concat [p! for p in *param_symbols], ' '})" }
}
-defn = Value.meta
+defn = ValueStream.meta
meta:
name: 'defn'
summary: "Define a function."
@@ -193,7 +193,7 @@ function is invoked."
body = body\quote scope
- value = with Value.wrap FnDef param_symbols, body, scope
+ value = with ValueStream.wrap FnDef param_symbols, body, scope
.meta =
:name
summary: "(user defined function)"
@@ -202,7 +202,7 @@ function is invoked."
scope\set name, Result :value
Result!
-do_expr = Value.meta
+do_expr = ValueStream.meta
meta:
name: 'do_expr'
summary: "Evaluate multiple expressions in a new scope."
@@ -215,7 +215,7 @@ Evaluate `expr1`, `expr2`, … and return the value of the last expression."
scope = Scope scope
Result children: [expr\eval scope for expr in *tail]
-if_ = Value.meta
+if_ = ValueStream.meta
meta:
name: 'if'
summary: "Make an evaltime const choice."
@@ -240,7 +240,7 @@ to `then-expr`, otherwise it is equivalent to `else-xpr` if given, or nil otherw
elseif xelse
xelse\eval scope
-trace_ = Value.meta
+trace_ = ValueStream.meta
meta:
name: 'trace!'
summary: "Trace an expression's value at evaltime."
@@ -254,7 +254,7 @@ trace_ = Value.meta
with result = L\push tail[1]\eval, scope
L\print "trace! #{tail[1]\stringify!}: #{result.value}"
-trace = Value.meta
+trace = ValueStream.meta
meta:
name: 'trace'
summary: "Trace an expression's values at runtime."
@@ -266,7 +266,7 @@ trace = Value.meta
{ prefix, value } = match 'str any', inputs
super
prefix: Input.cold prefix
- value: Input.value value
+ value: Input.hot value
tick: =>
L\print "trace #{@inputs.prefix!}: #{@inputs.value.stream}"
@@ -278,7 +278,7 @@ trace = Value.meta
tag = @tag\clone Tag.parse '-1'
inner = Cell tag, {
LiteralValue 'opdef', traceOp, 'trace'
- Value.str tostring tail[1]
+ ValueStream.str tostring tail[1]
tail[1]
}
inner\eval scope
@@ -292,17 +292,17 @@ trace = Value.meta
import: import_
'import*': import_star
- true: Value.meta
+ true: ValueStream.meta
meta:
name: 'true'
summary: "The boolean constant `true`."
- value: Value.bool true
+ value: ValueStream.bool true
- false: Value.meta
+ false: ValueStream.meta
meta:
name: 'false'
summary: "The boolean constant `false`."
- value: Value.bool false
+ value: ValueStream.bool false
:fn, :defn
'do': do_expr
diff --git a/core/cell.moon b/core/cell.moon
index 14bea2c..91634f8 100644
--- a/core/cell.moon
+++ b/core/cell.moon
@@ -5,7 +5,7 @@
-- nodes), a `Tag`, and optionally the internal whitespace as parsed.
--
-- @classmod Cell
-import Value from require 'core.value'
+import ValueStream from require 'core.stream'
import Error from require 'core.error'
import op_invoke, fn_invoke from require 'core.invoke'
import Tag from require 'core.tag'
@@ -170,7 +170,7 @@ class Cell
-- @type RootCell
class RootCell extends Cell
- head: => Value.sym 'do'
+ head: => ValueStream.sym 'do'
tail: => @children
stringify: =>
diff --git a/core/init.moon b/core/init.moon
index 8a0154c..6e23fa7 100644
--- a/core/init.moon
+++ b/core/init.moon
@@ -1,20 +1,10 @@
----
-- `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'
+import ValueStream, EventStream, IOStream from require 'core.stream'
import Result from require 'core.result'
import Scope from require 'core.scope'
import Error from require 'core.error'
@@ -31,7 +21,9 @@ globals = Scope.from_table require 'core.builtin'
--- exports
-- @table exports
--- @tfield Value Value
+-- @tfield ValueStream ValueStream
+-- @tfield EventStream EventStream
+-- @tfield IOStream IOStream
-- @tfield Result Result
-- @tfield Cell Cell
-- @tfield RootCell RootCell
@@ -42,9 +34,9 @@ globals = Scope.from_table require 'core.builtin'
-- @tfield Scope globals global definitons
-- @tfield parse function to turn a `string` into a root `Cell`
{
- :Value, :Result
+ :ValueStream, :EventStream, :IOStream
:Cell, :RootCell
- :Scope, :Error
+ :Result, :Scope, :Error
:Registry, :SimpleRegistry, :Tag
diff --git a/core/invoke.moon b/core/invoke.moon
index 654e436..539a778 100644
--- a/core/invoke.moon
+++ b/core/invoke.moon
@@ -2,7 +2,6 @@
-- 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'
import Scope from require 'core.scope'
@@ -90,8 +89,9 @@ class fn_invoke extends Action
-- `FnDef.body` with the prefix `Action.tag`, and `AST:eval`s it in the newly
-- created `Scope`.
--
- -- The `Result` contains the `Value` from the cloned AST, and its children are
- -- all the `Result`s from evaluating the tail as well as the cloned `AST`s.
+ -- The `Result` contains the `Stream` from the cloned AST, and its children
+ -- are all the `Result`s from evaluating the tail as well as the cloned
+ -- `AST`s.
--
-- @tparam Scope outer_scope the active scope
-- @tparam {AST,...} tail the arguments to this expression
diff --git a/core/parsing.moon b/core/parsing.moon
index b96f173..aadcab4 100644
--- a/core/parsing.moon
+++ b/core/parsing.moon
@@ -2,7 +2,7 @@
-- Lpeg Grammar for parsing `alive` code.
--
-- @module parsing
-import Value from require 'core.value'
+import ValueStream from require 'core.stream'
import Cell from require 'core.cell'
import Tag from require 'core.tag'
import R, S, P, V, C, Ct from require 'lpeg'
@@ -20,15 +20,15 @@ mspace = (comment + wc)^0 / 1 -- optional whitespace
-- atoms
digit = R '09'
first = (R 'az', 'AZ') + S '-_+*/.!?=%'
-sym = first * (first + digit)^0 / Value\parse 'sym'
+sym = first * (first + digit)^0 / ValueStream\parse 'sym'
-strd = '"' * (C ((P '\\"') + (P '\\\\') + (1 - P '"'))^0) * '"' / Value\parse 'str', '\"'
-strq = "'" * (C ((P "\\'") + (P '\\\\') + (1 - P "'"))^0) * "'" / Value\parse 'str', '\''
+strd = '"' * (C ((P '\\"') + (P '\\\\') + (1 - P '"'))^0) * '"' / ValueStream\parse 'str', '\"'
+strq = "'" * (C ((P "\\'") + (P '\\\\') + (1 - P "'"))^0) * "'" / ValueStream\parse 'str', '\''
str = strd + strq
int = digit^1
float = (digit^1 * '.' * digit^0) + (digit^0 * '.' * digit^1)
-num = ((P '-')^-1 * (float + int)) / Value\parse 'num'
+num = ((P '-')^-1 * (float + int)) / ValueStream\parse 'num'
atom = num + sym + str
diff --git a/core/plugin-guide.md b/core/plugin-guide.md
index f5e3c50..1086452 100644
--- a/core/plugin-guide.md
+++ b/core/plugin-guide.md
@@ -11,13 +11,13 @@ are exported in the `base` module.
## documentation metadata
The lua module should return a `Scope` or a table that will be converted using
-`Scope.from_table`. All exports should be documented using `Value.meta`, which
-attaches a `meta` table to the value that is used for error messages,
+`Scope.from_table`. All exports should be documented using `ValueStream.meta`,
+which attaches a `meta` table to the value that is used for error messages,
documentation generation and [`(doc)`][builtins-doc].
- import Value from require 'core.base'
+ import ValueStream from require 'core.base'
- two = Value.meta
+ two = ValueStream.meta
meta:
name: 'two'
summary: "the number two"
@@ -43,30 +43,31 @@ Most plugins will want to define a number of *Op*s to be used by the user. They
are implemented by deriving from the `Op` class and implementing at least the
`Op:setup` and `Op:tick` methods.
- import Value, Op, Input, match from require 'core.base'
+ import ValueStream, Op, Input, match from require 'core.base'
- my_add = Value.meta
+ total_sum = ValueStream.meta
meta:
- name: 'my-add'
- summary: "Add two numbers."
- examples: { '(my-add a b)' }
- description: "Add two numbers, plugin-style."
+ name: 'total-sum'
+ summary: "Keep a total of incoming numbers."
+ examples: { '(total-sum num!)' }
+ description: "Keep a total sum of incoming number events, plugin-style."
value: class extends Op
- new: => super 'num'
-
+ new: (...) =>
+ super ...
+ @state or= { total: 0 }
+ @out or= ValueStream 'num', @state.total
+
setup: (inputs, scope) =>
- { a, b } = match 'num num', inputs
+ { num } = match 'num!', inputs
- super
- a: Inputs.value a
- b: Inputs.value b
+ super num: Inputs.hot num
tick: =>
- @out\set @inputs.a! + @inputs.b!
-
+ @state.total += @inputs.num!
+ @out\set @state.total
{
- 'my-add': my_add
+ 'total-sum': total_sum
}
### Op:setup
@@ -86,9 +87,9 @@ argument types and matches them against the provided arguments:
{ str, numbers, optional } = match 'str *num any?', inputs
`match` matches arguments greedily from left to right. Each part of the string
-is the type-name of a Value. Parts can be optional (`num?`), multiple (`*num` -
-one or more numbers) or both (`*num?` - zero or more numbers). If there is an
-equals sign in front of a part, the corresponding `Result` has to be
+is the type-name of an argument. Parts can be optional (`num?`), multiple
+(`*num` - one or more numbers) or both (`*num?` - zero or more numbers). If
+there is an equals sign in front of a part, the corresponding `Result` has to be
*evaltime constant*. The special typename `any` can be used for generic Ops.
If there are more complex dependencies between arguments, it is recommended to
@@ -97,17 +98,67 @@ manually. For invalid or missing arguments, `Error` instances should be thrown
using `error` or `assert`.
#### input setup
-
-(section wip since changes are anticipated)
+There are two types of inputs: `Input.hot` and `Input.cold`:
+
+*Cold* inputs do not cause the Op to update when changes to the input stream
+are made. They are useful to 'ignore' changes to inputs which are only relevant
+when another input changed value. Imagine for example a `send-value-when` Op,
+which sends a value only when a `bang!` input is live. This Op doesn't have to
+update when the value changes, it's enough to update only when the trigger input
+changes and simply read the value in that moment.
+
+*Hot* inputs on the other hand mark the input stream as a dependency for the
+Op. Depending on the type of `Stream`, the semantics are a little different:
+
+- For `ValueStream`s, the Op updates whenever the current value changes. When
+ an input stream is swapped out for another one at evaltime, but their values
+ are momentarily equal, the input is not considered dirty.
+- For `EventStream`s and `IOStream`s, the Op updates whenever the stream is
+ dirty. There is no special handling when the stream is swapped out at
+ evaltime.
+
+All `Result`s from the `inputs` argument that are taken into consideration
+should be wrapped in an `Input` instance using either `Input.hot` or
+`Input.cold`, and need to be passed to the `Op:setup` super implementation.
+To illustrate with the `send-value-when` example:
+
+ setup: (inputs, scope) =>
+ { trig, value } = match 'bang! any', inputs
+
+ super
+ trig: Inputs.hot trig
+ value: Inputs.cold value
+
+`Op:setup` takes a table that can have any (even nested) shape you want, as
+long as all 'leaf values' are `Input` instances. The following are both valid:
+
+ super { (Inputs.hot trig), (Inputs.cold value) }
+
+ super
+ trigger:Inputs.hot trig
+ values: { (Inputs.cold val0), (Inputs.cold val1), (Inputs.cold val2) }
#### output setup
-
-When `Op:setup` finishes, `@out` has to be set to a `Value` instance. The
+When `Op:setup` finishes, `@out` has to be set to a `Stream` instance. The
instance can be created in `Op:setup`, or by overriding the constructor and
-delegating to the original one using `super`. In general this way of creating
-the output value is preferred, and it is only moved to `Op:setup` if the output
+delegating to the original one using `super`. In general setting it in the
+constructor is preferred, and it is only moved to `Op:setup` if the output
type depends on the arguments received.
+There are three types of `Stream`s that can be created:
+
+- `ValueStream`s track *continuous values*. They can only have one value per
+ tick, and downstream Ops will not update when a *ValueStream* has been set
+ to the same value it already had. They are updated using `ValueStream:set`.
+- `EventStream`s transmit *momentary events*. They can transmit multiple events
+ in a single tick. `EventStream`s do not keep a value set on the last tick on
+ the next tick. They are updated using `EventStream:add`.
+- `IOStream`s are like `EventStream`s, but their `IOStream:tick` method is
+ polled by the event loop at the start of every tick. This gives them a chance
+ to effectively create changes 'out of thin air' and kickstart the execution
+ of the dataflow engine. All *runtime* execution is due to an `IOStream`
+ becoming dirty somewhere.
+
### Op:tick
`Op:tick` is called whenever any of the inputs are *dirty*. This is where the
Op's main logic will go. Generally here it should be checked which input(s)
@@ -120,10 +171,6 @@ Since it is rarely necessary to implement `Action`s, there is currently no
documentation on implementing them, but the `Action` class documentation and the
examples in `core/builtin.moon` should be enough to get started.
-## `IO`s
-
-(wip)
-
[lua]: https://www.lua.org/
[moonscript]: http://moonscript.org/
[builtins-doc]: ../../reference/index.html#doc
diff --git a/core/registry.moon b/core/registry.moon
index 332c9e1..53d2d3a 100644
--- a/core/registry.moon
+++ b/core/registry.moon
@@ -2,8 +2,6 @@
-- `Tag` Registry.
--
-- @classmod Registry
-
-import Value from require 'core.value'
import Result from require 'core.result'
import Error from require 'core.error'
diff --git a/core/result.moon b/core/result.moon
index 52203da..cf228ec 100644
--- a/core/result.moon
+++ b/core/result.moon
@@ -30,13 +30,11 @@ class Result
with Result value: @value
.side_inputs = @side_inputs
- --- tick all IO instances that are effecting this (sub)tree.
+ --- tick all IOStream 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.__name == "IOInput"
- io = input!
- io\tick!
+ stream\tick! if input.io
--- in depth-first order, tick all Ops which have dirty Inputs.
--
@@ -73,9 +71,9 @@ class Result
buf ..= ">"
buf
- --- the `Value` result
+ --- the `Stream` result
--
- -- @tfield ?Value value
+ -- @tfield ?Stream value
--- an Op
--
@@ -85,12 +83,12 @@ class Result
--
-- @tfield {}|{Result,...} children
- --- cached mapping of all `Value`/`Input` pairs affecting this Result.
+ --- cached mapping of all `Stream`/`Input` pairs affecting this Result.
--
-- This is the union of all `children`s `side_inputs` and all `Input`s from
-- `op` that are not the `value` of any child.
--
- -- @tfield {[Value]=Input,...} side_inputs
+ -- @tfield {[Stream]=Input,...} side_inputs
--- static functions
-- @section static
@@ -105,17 +103,16 @@ class Result
@side_inputs, is_child = {}, {}
for child in *@children
- for s, d in pairs child.side_inputs
- @side_inputs[s] = d
+ for stream, input in pairs child.side_inputs
+ @side_inputs[stream] = input
if child.value
is_child[child.value] = true
if @op
for input in @op\all_inputs!
- if input.impure or not is_child[input.stream]
+ if input.io or not is_child[input.stream]
@side_inputs[input.stream] = input
-
{
:Result
}
diff --git a/core/scope.moon b/core/scope.moon
index 37a1ba3..526ca8d 100644
--- a/core/scope.moon
+++ b/core/scope.moon
@@ -2,7 +2,7 @@
-- Mapping from `sym`s to `Result`s.
--
-- @classmod Scope
-import Value from require 'core.value'
+import ValueStream from require 'core.stream'
import Result from require 'core.result'
import Error from require 'core.error'
@@ -12,12 +12,12 @@ class Scope
--- set a Lua value in the scope.
--
- -- wraps `val` in a `Value` and `Result` before calling `set`.
+ -- wraps `val` in a `ValueStream` and `Result` before calling `set`.
--
-- @tparam string key
-- @tparam any val
set_raw: (key, val) =>
- value = Value.wrap val, key
+ value = ValueStream.wrap val, key
@values[key] = Result :value
--- set a symbol to a `Result`.
@@ -104,7 +104,7 @@ class Scope
--- convert a Lua table to a Scope.
--
-- `tbl` may contain more tables (or `Scope`s).
- -- Uses `Value.wrap` on the values recursively.
+ -- Uses `ValueStream.wrap` on the values recursively.
--
-- @tparam table tbl the table to convert
-- @treturn Scope
diff --git a/core/stream/base.moon b/core/stream/base.moon
new file mode 100644
index 0000000..ee4ee17
--- /dev/null
+++ b/core/stream/base.moon
@@ -0,0 +1,79 @@
+----
+-- base Stream interface.
+--
+-- implemented by `ValueStream`, `EventStream`, and `IOStream`.
+--
+-- @classmod Stream
+
+class Stream
+--- Stream interface.
+--
+-- Methods that have to be implemented by `Stream` implementations
+-- (`ValueStream`, `EventStream`, `IOStream`).
+--
+-- @section interface
+
+ --- return whether this Stream was changed in the current tick.
+ --
+ -- @function dirty
+ -- @treturn boolean
+
+ --- create a mutable copy of this Stream.
+ --
+ -- Used to insulate eval-cycles from each other.
+ --
+ -- @function fork
+ -- @treturn Stream
+
+ --- the type name of this Stream's value.
+ --
+ -- the following builtin typenames are used:
+ --
+ -- - `str` - strings, `value` is a Lua string
+ -- - `sym` - symbols, `value` is a Lua string
+ -- - `num` - numbers, `value` is a Lua number
+ -- - `bool` - booleans, `value` is a Lua boolean
+ -- - `bang` - trigger signals, `value` is a Lua boolean
+ -- - `opdef` - `value` is an `Op` subclass
+ -- - `builtin` - `value` is an `Action` subclass
+ -- - `fndef` - `value` is a `FnDef` instance
+ -- - `scope` - `value` is a `Scope` instance
+ --
+ -- @tfield string type
+
+ --- documentation metadata.
+ --
+ -- an optional table containing metadata for error messages and
+ -- documentation. The following keys are recognized:
+ --
+ -- - `name`: optional name
+ -- - `summary`: single-line description (markdown)
+ -- - `examples`: optional list of single-line code examples
+ -- - `description`: optional full-text description (markdown)
+ --
+ -- @tfield ?table meta
+
+ __tostring: =>
+ value = if @meta.name
+ @meta.name
+ else if 'table' == (type @value) and rawget @value, '__base'
+ @value.__name
+ else
+ tostring @value
+ "<#{@@__name} #{@type}: #{value}>"
+
+ __inherited: (cls) => cls.__base.__tostring = @__tostring
+
+--- static functions
+-- @section static
+
+ --- construct a new Stream.
+ --
+ -- @classmethod
+ -- @tparam string type the type name
+ -- @tparam ?table meta the `meta` table
+ new: (@type, @meta={}) =>
+
+{
+ :Stream
+}
diff --git a/core/stream/event.moon b/core/stream/event.moon
new file mode 100644
index 0000000..5dbf846
--- /dev/null
+++ b/core/stream/event.moon
@@ -0,0 +1,92 @@
+----
+-- Stream of momentary events.
+--
+-- @classmod EventStream
+import Stream from require 'core.stream.base'
+import Result from require 'core.result'
+import Error from require 'core.error'
+import scope, base, registry from require 'core.cycle'
+
+class EventStream extends Stream
+--- members
+-- @section members
+
+ --- return whether this stream was changed in the current tick.
+ --
+ -- @treturn bool
+ dirty: => @updated == registry.Registry.active!.tick
+
+ --- push an event value into the stream.
+ --
+ -- Marks this stream as dirty for the remainder of the current tick.
+ --
+ -- @tparam any event
+ add: (event) =>
+ if not @dirty!
+ @events = {}
+
+ @updated = registry.Registry.active!.tick
+ table.insert @events, {}
+
+ --- get the sequence of current events (if any).
+ --
+ -- Returns `events` if `dirty`, or an empty table otherwise.
+ -- 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 {any,...} `events`
+ unwrap: (type, msg) =>
+ assert type == @type, msg or "#{@} is not a #{type}" if type
+ if @dirty! then @events else {}
+
+ --- create a mutable copy of this stream.
+ --
+ -- Used to wrap insulate eval-cycles from each other.
+ --
+ -- @treturn EventStream
+ fork: => EventStream @type
+
+ --- alias for `unwrap`.
+ __call: (...) => @unwrap ...
+
+ --- the type name of the stream.
+ --
+ -- the following builtin typenames are used:
+ --
+ -- - `str` - strings, `value` is a Lua string
+ -- - `sym` - symbols, `value` is a Lua string
+ -- - `num` - numbers, `value` is a Lua number
+ -- - `bool` - booleans, `value` is a Lua boolean
+ -- - `bang` - trigger signals, `value` is a Lua boolean
+ -- - `opdef` - `value` is an `Op` subclass
+ -- - `builtin` - `value` is an `Action` subclass
+ -- - `fndef` - `value` is a `FnDef` instance
+ -- - `scope` - `value` is a `Scope` instance
+ --
+ -- @tfield string type
+
+ --- documentation metadata.
+ --
+ -- an optional table containing metadata for error messages and
+ -- documentation. The following keys are recognized:
+ --
+ -- - `name`: optional name
+ -- - `summary`: single-line description (markdown)
+ -- - `examples`: optional list of single-line code examples
+ -- - `description`: optional full-text description (markdown)
+ --
+ -- @tfield ?table meta
+
+--- static functions
+-- @section static
+
+ --- construct a new EventStream.
+ --
+ -- @classmethod
+ -- @tparam string type the type name
+ new: (type) => super type
+
+{
+ :EventStream
+}
diff --git a/core/stream/init.moon b/core/stream/init.moon
new file mode 100644
index 0000000..3fff136
--- /dev/null
+++ b/core/stream/init.moon
@@ -0,0 +1,18 @@
+----
+-- `Stream` interface and implementations.
+--
+-- @see Stream
+-- @see ValueStream
+-- @see EventStream
+-- @see IOStream
+--
+-- @module stream
+import ValueStream from require 'core.stream.value'
+import EventStream from require 'core.stream.event'
+import IOStream from require 'core.stream.io'
+
+{
+ :ValueStream
+ :EventStream
+ :IOStream
+}
diff --git a/core/stream/io.moon b/core/stream/io.moon
new file mode 100644
index 0000000..fcaeb9f
--- /dev/null
+++ b/core/stream/io.moon
@@ -0,0 +1,48 @@
+----
+-- 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 `Result` tree.
+--
+-- @classmod IOStream
+import EventStream from require 'core.stream.event'
+
+class IOStream extends EventStream
+--- 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 `Stream.type`.
+ --
+ -- @classmethod
+ -- @tparam string type the typename of this stream.
+ new: (type) => super type
+
+ --- 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.
+ --
+ -- If this is not overrided, the `EventStream` interface can be used, see
+ -- `EventStream.add`, `EventStream.unwrap`, and `EventStream.dirty`.
+ --
+ -- @function dirty
+ -- @treturn bool whether processing is required
+
+ __inherited: (cls) => cls.__base.__tostring = @__tostring
+
+{
+ :IOStream
+}
diff --git a/core/value.moon b/core/stream/value.moon
index bdc4b39..0220f06 100644
--- a/core/value.moon
+++ b/core/stream/value.moon
@@ -1,7 +1,10 @@
----
--- Value(stream), implements the `AST` inteface.
+-- Continuous stream of values.
--
--- @classmod Value
+-- Implements the `Stream` and `AST` intefaces.
+--
+-- @classmod ValueStream
+import Stream from require 'core.stream.base'
import Result from require 'core.result'
import Error from require 'core.error'
import scope, base, registry from require 'core.cycle'
@@ -12,18 +15,18 @@ ancestor = (klass) ->
klass = klass.__parent
klass
-class Value
+class ValueStream extends Stream
--- members
-- @section members
- --- return whether this Value was changed in the current tick.
+ --- return whether this stream was changed in the current tick.
--
-- @treturn bool
dirty: => @updated == registry.Registry.active!.tick
- --- update this Value.
+ --- update this stream's value.
--
- -- Marks this Value as dirty for the remainder of the current tick.
+ -- Marks this stream as dirty for the remainder of the current tick.
set: (@value) => @updated = registry.Registry.active!.tick
--- unwrap to the Lua type.
@@ -37,13 +40,13 @@ class Value
assert type == @type, msg or "#{@} is not a #{type}" if type
@value
- --- create a mutable copy of this Value.
+ --- create a mutable copy of this stream.
--
- -- Used to wrap insulate eval-cycles from each other.
+ -- Used to insulate eval-cycles from each other.
--
- -- @treturn Value
+ -- @treturn ValueStream
fork: =>
- with Value @type, @value, @raw
+ with ValueStream @type, @value, @raw
.updated = @updated
--- alias for `unwrap`.
@@ -51,14 +54,10 @@ class Value
--- compare two values.
--
- -- Compares two `Value`s by comparing their types and their Lua values.
+ -- Compares two `ValueStream`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}>"
-
- --- the type name of the Value.
+ --- the type name of this stream.
--
-- the following builtin typenames are used:
--
@@ -91,7 +90,7 @@ class Value
--- AST interface
--
--- `Value` implements the `AST` interface.
+-- `ValueStream` implements the `AST` interface.
-- @section ast
--- evaluate this literal constant.
@@ -113,32 +112,31 @@ class Value
--- quote this literal constant.
--
- -- @treturn Value self
+ -- @treturn ValueStream 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"
+ -- @treturn string the exact string this stream was parsed from
+ stringify: => assert @raw, "stringifying ValueStream that wasn't parsed"
--- clone this literal constant.
--
- -- @treturn Value self
+ -- @treturn ValueStream self
clone: (prefix) => @
--- static functions
-- @section static
- --- construct a new Value.
+ --- construct a new ValueStream.
--
-- @classmethod
-- @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) =>
- @meta = {}
+ new: (type, @value, @raw) => super type
unescape = (str) -> str\gsub '\\([\'"\\])', '%1'
--- create a capture-function (for parsing with Lpeg).
@@ -157,7 +155,7 @@ class Value
--
-- @tparam any val the value to wrap
-- @tparam[opt] string name the name of this value (for error logging)
- -- @treturn Value
+ -- @treturn ValueStream
@wrap: (val, name='(unknown)') ->
typ = switch type val
when 'number' then 'num'
@@ -175,53 +173,51 @@ class Value
switch ancestor val.__class
when scope.Scope then 'scope'
when base.FnDef then 'fndef'
- when Value
- return val
+ when Stream then return val
else
error "#{name}: cannot wrap '#{val.__class.__name}' instance"
else
-- plain table
- return Value 'scope', scope.Scope.from_table val
+ return ValueStream 'scope', scope.Scope.from_table val
else
error "#{name}: cannot wrap Lua type '#{type val}'"
- Value typ, val
+ ValueStream typ, val
--- create a constant number.
-- @tparam number num the number
- -- @treturn Value
- @num: (num) -> Value 'num', num, tostring num
+ -- @treturn ValueStream
+ @num: (num) -> ValueStream 'num', num, tostring num
--- create a constant string.
-- @tparam string str the string
- -- @treturn Value
- @str: (str) -> Value 'str', str, "'#{str}'"
+ -- @treturn ValueStream
+ @str: (str) -> ValueStream 'str', str, "'#{str}'"
--- create a constant symbol.
-- @tparam string sym the symbol
- -- @treturn Value
- @sym: (sym) -> Value 'sym', sym, sym
+ -- @treturn ValueStream
+ @sym: (sym) -> ValueStream 'sym', sym, sym
--- create a constant boolean.
-- @tparam boolean bool the boolean
- -- @treturn Value
- @bool: (bool) -> Value 'bool', bool, tostring bool
+ -- @treturn ValueStream
+ @bool: (bool) -> ValueStream 'bool', bool, tostring bool
--- wrap and document a value.
--
-- wraps `args.value` using `wrap`, then assigns `meta`.
--
-- @tparam table args table with keys `value` and `meta`
- -- @treturn Value
+ -- @treturn ValueStream
@meta: (args) ->
- with Value.wrap args.value
+ with ValueStream.wrap args.value
.meta = args.meta if args.meta
-class LiteralValue extends Value
+class LiteralValue extends ValueStream
eval: => Result value: @
{
- :Value
+ :ValueStream
:LiteralValue
- :load_
}
diff --git a/spec/core/cell_spec.moon b/spec/core/cell_spec.moon
index 710b252..d16c9d8 100644
--- a/spec/core/cell_spec.moon
+++ b/spec/core/cell_spec.moon
@@ -1,10 +1,10 @@
import Cell, RootCell from require 'core.cell'
-import Value, Scope, Tag, SimpleRegistry, globals from require 'core'
+import ValueStream, Scope, Tag, SimpleRegistry, globals from require 'core'
import Logger from require 'logger'
Logger.init 'silent'
-hello_world = Cell.parse (Tag.parse '2'), { '', (Value.sym 'hello'), ' ', (Value.str 'world'), '' }
-two_plus_two = Cell.parse (Tag.parse '3'), { '', (Value.sym '+'), ' ', (Value.num 2), ' ', (Value.num 2), '' }
+hello_world = Cell.parse (Tag.parse '2'), { '', (ValueStream.sym 'hello'), ' ', (ValueStream.str 'world'), '' }
+two_plus_two = Cell.parse (Tag.parse '3'), { '', (ValueStream.sym '+'), ' ', (ValueStream.num 2), ' ', (ValueStream.num 2), '' }
reg = SimpleRegistry!
setup -> reg\grab!
@@ -15,8 +15,8 @@ describe 'Cell', ->
with hello_world\quote!
it 'stays equal', ->
assert.is.equal Cell, .__class
- assert.is.equal (Value.sym 'hello'), \head!
- assert.is.same { Value.str 'world' }, \tail!
+ assert.is.equal (ValueStream.sym 'hello'), \head!
+ assert.is.same { ValueStream.str 'world' }, \tail!
it 'shares the tag', ->
assert.is.equal hello_world.tag, .tag
@@ -24,8 +24,8 @@ describe 'Cell', ->
with two_plus_two\quote!
it 'stays equal', ->
assert.is.equal Cell, .__class
- assert.is.equal (Value.sym '+'), \head!
- assert.is.same { (Value.num 2), (Value.num 2) }, \tail!
+ assert.is.equal (ValueStream.sym '+'), \head!
+ assert.is.same { (ValueStream.num 2), (ValueStream.num 2) }, \tail!
it 'shares the tag', ->
assert.is.equal two_plus_two.tag, .tag
@@ -35,8 +35,8 @@ describe 'Cell', ->
with hello_world\clone parent
it 'keeps children', ->
assert.is.equal Cell, .__class
- assert.is.equal (Value.sym 'hello'), \head!
- assert.is.same { Value.str 'world' }, \tail!
+ assert.is.equal (ValueStream.sym 'hello'), \head!
+ assert.is.same { ValueStream.str 'world' }, \tail!
it 'clones the tag', ->
assert.is.equal hello_world.tag, .tag.original
@@ -48,8 +48,8 @@ describe 'Cell', ->
assert.has.error -> cell\eval globals
it 'evaluates its head', ->
- head = Value.sym 'trace'
- cell = Cell.parse { '', head, ' ', (Value.sym 'true'), '' }
+ head = ValueStream.sym 'trace'
+ cell = Cell.parse { '', head, ' ', (ValueStream.sym 'true'), '' }
s = spy.on head, 'eval'
cell\eval globals
@@ -62,10 +62,10 @@ describe 'RootCell', ->
test 'head is always "do"', ->
cell = Cell.parse_root {}
- assert.is.equal (Value.sym 'do'), cell\head!
+ assert.is.equal (ValueStream.sym 'do'), cell\head!
cell = RootCell nil, { hello_world, two_plus_two }
- assert.is.equal (Value.sym 'do'), cell\head!
+ assert.is.equal (ValueStream.sym 'do'), cell\head!
test 'tail is all children', ->
cell = Cell.parse_root {}
diff --git a/spec/core/input_spec.moon b/spec/core/input_spec.moon
index e2ec7e3..4b470db 100644
--- a/spec/core/input_spec.moon
+++ b/spec/core/input_spec.moon
@@ -1,4 +1,4 @@
-import Input, Result, Value, IO from require 'core.base'
+import Input, Result, ValueStream, EventStream, IOStream from require 'core.base'
import SimpleRegistry from require 'core'
import Logger from require 'logger'
Logger.init 'silent'
@@ -7,198 +7,144 @@ reg = SimpleRegistry!
setup -> reg\grab!
teardown -> reg\release!
-describe 'Input.event', ->
- val = Value.num 1
- input = Input.event val
-
- describe 'at evaltime', ->
- it 'follows Value when new', ->
- input\setup nil
-
- val\set 2
- assert.is.true val\dirty!
- assert.is.true input\dirty!
-
- reg\next_tick!
- assert.is.false val\dirty!
- assert.is.false input\dirty!
-
- input\finish_setup!
-
- it 'follows Value when different', ->
- new_input = Input.event Value.num 3
- new_input\setup input
-
- assert.is.false new_input.stream\dirty!
- assert.is.false new_input\dirty!
-
- new_input.stream\set 3
- assert.is.true new_input.stream\dirty!
- assert.is.true new_input\dirty!
-
- reg\next_tick!
- new_input\finish_setup!
-
- it 'follows Value when equal', ->
- new_input = Input.event Value.num 2
- new_input\setup input
-
- assert.is.false new_input.stream\dirty!
- assert.is.false new_input\dirty!
-
- new_input.stream\set 2
- assert.is.true new_input.stream\dirty!
- assert.is.true new_input\dirty!
-
- describe 'at runtime', ->
- it 'is dirty when the value is dirty', ->
- val\set 3
- assert.is.true val\dirty!
- assert.is.true input\dirty!
-
- reg\next_tick!
- assert.is.false val\dirty!
- assert.is.false input\dirty!
-
- it 'unwraps to the lua value', ->
- assert.is.equal 3, input\unwrap!
- assert.is.equal 3, input!
-
- it 'gives access to the type string', ->
- assert.is.equal 'num', input\type!
-
- it 'gives access to the Value', ->
- assert.is.equal val, input.stream
-
-describe 'Input.value', ->
- val = Value.num 1
- local input
-
- describe 'at evaltime', ->
- it 'is dirty when new', ->
- assert.is.false val\dirty!
-
- input = Input.value val
- input\setup nil
- assert.is.true input\dirty!
- input\finish_setup!
-
- it 'is dirty when different', ->
- newval = Value.num 2
-
- assert.is.false newval\dirty!
- newinput = Input.value newval
- newinput\setup input
- assert.is.true newinput\dirty!
- newinput\finish_setup!
-
- it 'is not dirty when equal', ->
- newval = Value.num 1
- newval\set 1
-
- assert.is.true newval\dirty!
- newinput = Input.value newval
- newinput\setup input
- assert.is.false newinput\dirty!
- newinput\finish_setup!
-
- describe 'at runtime', ->
- it 'is dirty when the value is dirty', ->
- val\set 3
- assert.is.true val\dirty!
- assert.is.true input\dirty!
+class MyIO extends IOStream
+ new: => super 'my-io'
+ dirty: => @is_dirty
- reg\next_tick!
- assert.is.false val\dirty!
- assert.is.false input\dirty!
+basic_tests = (stream, input) ->
+ it 'gives access to the Stream', ->
+ assert.is.equal stream, input.stream
- it 'unwraps to the lua value', ->
- assert.is.equal 3, input\unwrap!
- assert.is.equal 3, input!
+ it 'forwards :unwrap', ->
+ assert.is.same stream\unwrap!, input\unwrap!
+ assert.is.same stream\unwrap!, input!
it 'gives access to the type string', ->
- assert.is.equal 'num', input\type!
-
- it 'gives access to the Value', ->
- assert.is.equal val, input.stream
+ assert.is.equal stream.type, input\type!
describe 'Input.cold', ->
- val = Value.num 1
- input = Input.cold val
+ stream = ValueStream.num 1
+ input = Input.cold stream
+
+ basic_tests stream, input
it 'is never dirty', ->
assert.is.false input\dirty!
- val\set 2
+ stream\set 2
assert.is.false input\dirty!
input\setup nil
assert.is.false input\dirty!
input\finish_setup!
- new_input = Input.cold Value.num 3
+ new_input = Input.cold ValueStream.num 3
new_input\setup input
assert.is.false new_input\dirty!
new_input.stream\set 4
assert.is.false new_input\dirty!
input\finish_setup!
- it 'unwraps to the lua value', ->
- assert.is.equal 2, input\unwrap!
- assert.is.equal 2, input!
+describe 'Input.hot', ->
+ describe 'with EventStream', ->
+ stream = EventStream 'num'
+ input = Input.hot stream
- it 'gives access to the type string', ->
- assert.is.equal 'num', input\type!
+ basic_tests stream, input
- it 'gives access to the Value', ->
- assert.is.equal val, input.stream
+ it 'is marked for lifting', ->
+ assert.is.nil input.io
-class MyIO extends IO
- dirty: => @is_dirty
+ it 'is dirty when the EventStream is dirty', ->
+ assert.is.false input\dirty!
+ assert.is.false stream\dirty!
-describe 'Input.io', ->
- io = MyIO!
- val = Value 'test-io', io
- input = Input.io val
+ input\setup nil
+ assert.is.false input\dirty!
+ input\finish_setup!
- it 'is dirty when the IO is dirty', ->
- io.is_dirty = false
+ reg\next_tick!
+ stream\add 1
- assert.is.false val\dirty!
- assert.is.false input\dirty!
- assert.is.false io\dirty!
+ assert.is.true input\dirty!
+ assert.is.true stream\dirty!
- input\setup nil
- assert.is.false input\dirty!
- input\finish_setup!
+ input\setup nil
+ assert.is.true input\dirty!
+ input\finish_setup!
- val\set io
- assert.is.true val\dirty!
- assert.is.false input\dirty!
- assert.is.false io\dirty!
+ assert.is.true input\dirty!
+ assert.is.true stream\dirty!
- reg\next_tick!
+ describe 'with IOStream', ->
+ stream = MyIO!
+ input = Input.hot stream
- io.is_dirty = true
+ basic_tests stream, input
- assert.is.false val\dirty!
- assert.is.true input\dirty!
- assert.is.true io\dirty!
+ it 'is marked for lifting', ->
+ assert.is.true input.io
- input\setup nil
- assert.is.true input\dirty!
- input\finish_setup!
+ it 'is dirty when the IOStream is dirty', ->
+ stream.is_dirty = false
- val\set io
- assert.is.true val\dirty!
- assert.is.true input\dirty!
- assert.is.true io\dirty!
+ assert.is.false input\dirty!
+ assert.is.false stream\dirty!
- it 'unwraps to the io value', ->
- assert.is.equal io, input\unwrap!
- assert.is.equal io, input!
+ input\setup nil
+ assert.is.false input\dirty!
+ input\finish_setup!
- it 'gives access to the type string', ->
- assert.is.equal 'test-io', input\type!
+ reg\next_tick!
+ stream.is_dirty = true
+
+ assert.is.true input\dirty!
+ assert.is.true stream\dirty!
+
+ input\setup nil
+ assert.is.true input\dirty!
+ input\finish_setup!
- it 'gives access to the Value', ->
- assert.is.equal val, input.stream
+ assert.is.true input\dirty!
+ assert.is.true stream\dirty!
+
+ describe 'with ValueStream', ->
+ stream = ValueStream.num 1
+ local input
+
+ describe 'at evaltime', ->
+ it 'is dirty when new', ->
+ assert.is.false stream\dirty!
+
+ input = Input.hot stream
+ input\setup nil
+ assert.is.true input\dirty!
+ input\finish_setup!
+
+ it 'is dirty when different', ->
+ newval = ValueStream.num 2
+
+ assert.is.false newval\dirty!
+ newinput = Input.hot newval
+ newinput\setup input
+ assert.is.true newinput\dirty!
+ newinput\finish_setup!
+
+ it 'is not dirty when equal', ->
+ newval = ValueStream.num 1
+ newval\set 1
+
+ assert.is.true newval\dirty!
+ newinput = Input.hot newval
+ newinput\setup input
+ assert.is.false newinput\dirty!
+ newinput\finish_setup!
+
+ describe 'at runtime', ->
+ it 'is dirty when the stream is dirty', ->
+ stream\set 3
+ assert.is.true stream\dirty!
+ assert.is.true input\dirty!
+
+ reg\next_tick!
+ assert.is.false stream\dirty!
+ assert.is.false input\dirty!
diff --git a/spec/core/parsing_spec.moon b/spec/core/parsing_spec.moon
index 9958e22..32d8530 100644
--- a/spec/core/parsing_spec.moon
+++ b/spec/core/parsing_spec.moon
@@ -1,6 +1,6 @@
import space, atom, expr, explist, cell, program, comment
from require 'core.parsing'
-import Value from require 'core'
+import ValueStream from require 'core'
import Logger from require 'logger'
Logger.init 'silent'
@@ -66,9 +66,9 @@ describe 'Cell', ->
"friend" )'
assert.is.equal 3, #node.children
- assert.is.equal (Value.num 3), node.children[1]
- assert.is.equal (Value.sym 'ok-yes'), node.children[2]
- assert.is.equal (Value.str 'friend'), node.children[3]
+ assert.is.equal (ValueStream.num 3), node.children[1]
+ assert.is.equal (ValueStream.sym 'ok-yes'), node.children[2]
+ assert.is.equal (ValueStream.str 'friend'), node.children[3]
test 'tag parsing', ->
node = verify_parse cell, '([42]tagged 2)'
@@ -89,8 +89,8 @@ describe 'RootCell parsing', ->
node = verify_parse program, str
assert.is.equal 2, #node.children
- assert.is.equal (Value.num 3), node.children[1]
- assert.is.equal (Value.sym 'ok-yes'), node.children[2]
+ assert.is.equal (ValueStream.num 3), node.children[1]
+ assert.is.equal (ValueStream.sym 'ok-yes'), node.children[2]
it 'at the front of the string', ->
verify ' 3\tok-yes'
diff --git a/spec/core/pattern_spec.moon b/spec/core/pattern_spec.moon
index 4825dcc..523ce86 100644
--- a/spec/core/pattern_spec.moon
+++ b/spec/core/pattern_spec.moon
@@ -1,5 +1,5 @@
import Pattern, match from require 'core.base.match'
-import Result, Value from require 'core'
+import Result, ValueStream from require 'core'
-- wrap in non-const result
wrap = (value) ->
@@ -10,11 +10,11 @@ wrap = (value) ->
wrap_const = (value) -> Result :value
describe 'Type Pattern', ->
- num = wrap Value.num 1
- str = wrap Value.str 'hello'
- special = wrap Value 'midi/sysex-message'
- c_num = wrap_const Value.num 1
- c_str = wrap_const Value.str 'hello'
+ num = wrap ValueStream.num 1
+ str = wrap ValueStream.str 'hello'
+ special = wrap ValueStream 'midi/sysex-message'
+ c_num = wrap_const ValueStream.num 1
+ c_str = wrap_const ValueStream.str 'hello'
describe 'simple types', ->
it 'matches self type', ->
@@ -151,10 +151,10 @@ describe 'Type Pattern', ->
assert.is.same {str}, stream
describe 'match', ->
- num = wrap Value.num 1
- str = wrap Value.str 'hello'
- c_num = wrap_const Value.num 1
- c_str = wrap_const Value.str 'hello'
+ num = wrap ValueStream.num 1
+ str = wrap ValueStream.str 'hello'
+ c_num = wrap_const ValueStream.num 1
+ c_str = wrap_const ValueStream.str 'hello'
it 'matches lists', ->
assert.is.same {num, num, str}, match 'num num str', {num, num, str}
diff --git a/spec/core/result_spec.moon b/spec/core/result_spec.moon
index cc7cd71..017be58 100644
--- a/spec/core/result_spec.moon
+++ b/spec/core/result_spec.moon
@@ -1,5 +1,5 @@
-import Result, Value, Scope, SimpleRegistry from require 'core'
-import Input, Op, IO from require 'core.base'
+import Result, Scope, SimpleRegistry from require 'core'
+import Input, Op, ValueStream, EventStream, IOStream from require 'core.base'
import Logger from require 'logger'
Logger.init 'silent'
@@ -15,13 +15,13 @@ reg = SimpleRegistry!
setup -> reg\grab!
teardown -> reg\release!
-class DirtyIO extends IO
- tick: =>
+class DirtyIO extends IOStream
+ new: => super 'dirty-io'
dirty: => true
describe 'Result', ->
it 'wraps value, children', ->
- value = Value.num 3
+ value = ValueStream.num 3
a = Result!
b = Result!
@@ -33,14 +33,14 @@ describe 'Result', ->
assert.is.same children, result.children
it ':type gets type and assets value', ->
- result = Result value: Value.num 2
+ result = Result value: ValueStream.num 2
assert.is.equal 'num', result\type!
result = Result!
assert.has.error -> result\type!
it ':is_const', ->
- value = Value.num 2
+ value = ValueStream.num 2
pure = Result :value
impure = result_with_sideinput value, {}
@@ -52,8 +52,8 @@ describe 'Result', ->
assert.has.error (-> impure\const 'test'), 'test'
it ':make_ref', ->
- value = Value.num 2
- input = Input.value value
+ value = ValueStream.num 2
+ input = Input.hot value
op = op_with_inputs { input }
thick = Result :value, :op, children: { Result!, Result! }
ref = thick\make_ref!
@@ -65,11 +65,11 @@ describe 'Result', ->
assert.is.nil ref.op
it 'lifts up inputs from op', ->
- event = Value 'bang', false
- event_input = Input.event event
+ event = ValueStream 'bang', false
+ event_input = Input.hot event
- value = Value 'num', 4
- value_input = Input.value value
+ value = ValueStream 'num', 4
+ value_input = Input.hot value
op = op_with_inputs { event_input, value_input }
result = Result op: op, :value
@@ -79,11 +79,11 @@ describe 'Result', ->
result.side_inputs
it 'does not lift up op inputs that are also child values', ->
- event = Value 'bang', false
- event_input = Input.event event
+ event = ValueStream 'bang', false
+ event_input = Input.hot event
- value = Value 'num', 4
- value_input = Input.value value
+ value = ValueStream 'num', 4
+ value_input = Input.hot value
op = op_with_inputs { event_input, value_input }
result = Result op: op, :value, children: { Result :value }
@@ -91,13 +91,13 @@ describe 'Result', ->
assert.is.same { [event]: event_input }, result.side_inputs
it 'lifts up side_inputs from children', ->
- event_value = Value 'bang', false
- event_input = Input.event event_value
+ event_value = ValueStream 'bang', false
+ event_input = Input.hot event_value
event = Result op: op_with_inputs { event_input }
assert.is.same { [event_value]: event_input }, event.side_inputs
- value_value = Value 'num', 4
- value_input = Input.value value_value
+ value_value = ValueStream 'num', 4
+ value_input = Input.hot value_value
value = Result op: op_with_inputs { value_input }
assert.is.same { [value_value]: value_input }, value.side_inputs
@@ -109,16 +109,16 @@ describe 'Result', ->
local a_value, a_child, a_input
local b_value, b_child, b_input
before_each ->
- a_value = Value 'num'
- a_input = Input.event a_value
+ a_value = EventStream 'num'
+ a_input = Input.hot a_value
a_child = result_with_sideinput a_value, a_input
- b_value = Value 'num'
- b_input = Input.event b_value
+ b_value = EventStream 'num'
+ b_input = Input.hot b_value
b_child = result_with_sideinput b_value, b_input
it 'updates children when a side_input is dirty', ->
- a_value\set 1
+ a_value\add 1
assert.is.true a_input\dirty!
assert.is.false b_input\dirty!
@@ -145,11 +145,11 @@ describe 'Result', ->
assert.spy(b).was_not_called!
it 'updates op when any op-inputs are dirty', ->
- a_value\set 1
+ a_value\add 1
assert.is.true a_input\dirty!
assert.is.false b_input\dirty!
- op = op_with_inputs a: Input.event a_value
+ op = op_with_inputs a: Input.hot a_value
s = spy.on op, 'tick'
result = Result :op, children: { a_child, b_child }
@@ -158,11 +158,11 @@ describe 'Result', ->
assert.spy(s).was_called_with match.ref op
it 'early-outs when no op-inputs are dirty', ->
- a_value\set 1
+ a_value\add 1
assert.is.true a_input\dirty!
assert.is.false b_input\dirty!
- op = op_with_inputs { Input.event b_value }
+ op = op_with_inputs { Input.hot b_value }
s = spy.on op, 'tick'
result = Result :op, children: { a_child, b_child }
@@ -173,13 +173,12 @@ describe 'Result', ->
describe ':tick_io', ->
it 'ticks IOs referenced in side_inputs', ->
io = DirtyIO!
- value = Value 'an_io', io
- input = Input.io value
+ input = Input.hot io
op = op_with_inputs { input }
result = Result :op
s = spy.on io, 'tick'
- assert.is.same { [value]: input }, result.side_inputs
+ assert.is.same { [io]: input }, result.side_inputs
result\tick_io!
assert.spy(s).was_called_with match.ref io
diff --git a/spec/core/scope_spec.moon b/spec/core/scope_spec.moon
index 6d7d754..59c6bd1 100644
--- a/spec/core/scope_spec.moon
+++ b/spec/core/scope_spec.moon
@@ -1,4 +1,4 @@
-import Scope, Value, Result from require 'core'
+import Scope, ValueStream, Result from require 'core'
import Op from require 'core.base'
import Logger from require 'logger'
Logger.init 'silent'
@@ -27,7 +27,7 @@ describe 'Scope', ->
assert.is.equal "im a happy string", got.value
test 'Values', ->
- pi = Value 'num', 3.14
+ pi = ValueStream 'num', 3.14
scope\set_raw 'pi', pi
assert.is.equal pi, (scope\get 'pi')\const!
@@ -48,7 +48,7 @@ describe 'Scope', ->
assert.is.equal sub, got.value
test 'tables', ->
- pi = Value 'num', 3.14
+ pi = ValueStream 'num', 3.14
scope\set_raw 'math', { :pi }
got = (scope\get 'math')\const!
@@ -58,7 +58,7 @@ describe 'Scope', ->
assert.is.equal pi, (scope\get 'math/pi')\const!
it 'wraps Values in from_table', ->
- pi = Value 'num', 3.14
+ pi = ValueStream 'num', 3.14
scope = Scope.from_table {
num: 3
str: "im a happy string"
@@ -90,7 +90,7 @@ describe 'Scope', ->
a = Scope!
b = Scope!
- pi = Value 'num', 3.14
+ pi = ValueStream 'num', 3.14
b\set_raw 'test', pi
a\set_raw 'child', b
root\set_raw 'deep', a
@@ -98,8 +98,8 @@ describe 'Scope', ->
assert.is.equal pi, (root\get 'deep/child/test')\const!
describe 'can set symbols', ->
- one = wrap_res Value.num 1
- two = wrap_res Value.num 2
+ one = wrap_res ValueStream.num 1
+ two = wrap_res ValueStream.num 2
scope = Scope!
it 'disallows re-setting symbols', ->
diff --git a/spec/core/value_spec.moon b/spec/core/value_spec.moon
index eeb3339..b447d32 100644
--- a/spec/core/value_spec.moon
+++ b/spec/core/value_spec.moon
@@ -1,4 +1,4 @@
-import Value, Result, Scope, SimpleRegistry from require 'core'
+import ValueStream, Result, Scope, SimpleRegistry from require 'core'
import Op, Action from require 'core.base'
import Logger from require 'logger'
Logger.init 'silent'
@@ -13,101 +13,101 @@ reg = SimpleRegistry!
setup -> reg\grab!
teardown -> reg\release!
-describe 'Value', ->
+describe 'ValueStream', ->
describe '.wrap', ->
it 'wraps numbers', ->
- got = Value.wrap 3
+ got = ValueStream.wrap 3
assert.is.equal 'num', got.type
assert.is.equal 3, got.value
it 'wraps strings', ->
- got = Value.wrap "im a happy string"
+ got = ValueStream.wrap "im a happy string"
assert.is.equal 'str', got.type
assert.is.equal "im a happy string", got.value
it 'wraps Values', ->
- pi = Value 'num', 3.14
- got = Value.wrap pi
+ pi = ValueStream 'num', 3.14
+ got = ValueStream.wrap pi
assert.is.equal pi, got
it 'wraps Opdefs', ->
- got = Value.wrap TestOp
+ got = ValueStream.wrap TestOp
assert.is.equal 'opdef', got.type
assert.is.equal TestOp, got.value
it 'wraps Bultins', ->
- got = Value.wrap TestAction
+ got = ValueStream.wrap TestAction
assert.is.equal 'builtin', got.type
assert.is.equal TestAction, got.value
it 'wraps Scopes', ->
sub = Scope!
- got = Value.wrap sub
+ got = ValueStream.wrap sub
assert.is.equal 'scope', got.type
assert.is.equal sub, got.value
it 'wraps tables', ->
- pi = Value 'num', 3.14
- got = Value.wrap { :pi }
+ pi = ValueStream 'num', 3.14
+ got = ValueStream.wrap { :pi }
assert.is.equal 'scope', got.type
assert.is.equal pi, (got.value\get 'pi')\const!
describe ':unwrap', ->
it 'returns the raw value!', ->
- assert.is.equal 3.14, (Value.num 3.14)\unwrap!
- assert.is.equal 'hi', (Value.str 'hi')\unwrap!
- assert.is.equal 'hi', (Value.sym 'hi')\unwrap!
+ assert.is.equal 3.14, (ValueStream.num 3.14)\unwrap!
+ assert.is.equal 'hi', (ValueStream.str 'hi')\unwrap!
+ assert.is.equal 'hi', (ValueStream.sym 'hi')\unwrap!
test 'can assert the type', ->
- assert.is.equal 3.14, (Value.num 3.14)\unwrap 'num'
- assert.is.equal 'hi', (Value.str 'hi')\unwrap 'str'
- assert.is.equal 'hi', (Value.sym 'hi')\unwrap 'sym'
- assert.has_error -> (Value.num 3.14)\unwrap 'sym'
- assert.has_error -> (Value.str 'hi')\unwrap 'num'
- assert.has_error -> (Value.sym 'hi')\unwrap 'str'
+ assert.is.equal 3.14, (ValueStream.num 3.14)\unwrap 'num'
+ assert.is.equal 'hi', (ValueStream.str 'hi')\unwrap 'str'
+ assert.is.equal 'hi', (ValueStream.sym 'hi')\unwrap 'sym'
+ assert.has_error -> (ValueStream.num 3.14)\unwrap 'sym'
+ assert.has_error -> (ValueStream.str 'hi')\unwrap 'num'
+ assert.has_error -> (ValueStream.sym 'hi')\unwrap 'str'
test 'has __call shorthand', ->
- assert.is.equal 3.14, (Value.num 3.14)!
- assert.is.equal 'hi', (Value.str 'hi')!
- assert.is.equal 'hi', (Value.sym 'hi')!
- assert.is.equal 3.14, (Value.num 3.14) 'num'
- assert.is.equal 'hi', (Value.str 'hi') 'str'
- assert.is.equal 'hi', (Value.sym 'hi') 'sym'
- assert.has_error -> (Value.num 3.14) 'sym'
- assert.has_error -> (Value.str 'hi') 'num'
- assert.has_error -> (Value.sym 'hi') 'str'
+ assert.is.equal 3.14, (ValueStream.num 3.14)!
+ assert.is.equal 'hi', (ValueStream.str 'hi')!
+ assert.is.equal 'hi', (ValueStream.sym 'hi')!
+ assert.is.equal 3.14, (ValueStream.num 3.14) 'num'
+ assert.is.equal 'hi', (ValueStream.str 'hi') 'str'
+ assert.is.equal 'hi', (ValueStream.sym 'hi') 'sym'
+ assert.has_error -> (ValueStream.num 3.14) 'sym'
+ assert.has_error -> (ValueStream.str 'hi') 'num'
+ assert.has_error -> (ValueStream.sym 'hi') 'str'
describe 'overrides __eq', ->
it 'compares the type', ->
- val = Value 'num', 3
- assert.is.equal (Value.num 3), val
- assert.not.equal (Value.str '3'), val
+ val = ValueStream 'num', 3
+ assert.is.equal (ValueStream.num 3), val
+ assert.not.equal (ValueStream.str '3'), val
- val = Value 'str', 'hello'
- assert.is.equal (Value.str 'hello'), val
- assert.not.equal (Value.sym 'hello'), val
+ val = ValueStream 'str', 'hello'
+ assert.is.equal (ValueStream.str 'hello'), val
+ assert.not.equal (ValueStream.sym 'hello'), val
it 'compares the value', ->
- val = Value 'num', 3
- assert.is.equal (Value.num 3), val
- assert.not.equal (Value.num 4), val
+ val = ValueStream 'num', 3
+ assert.is.equal (ValueStream.num 3), val
+ assert.not.equal (ValueStream.num 4), val
describe ':set', ->
it 'sets the value', ->
- val = Value 'num', 3
- assert.is.equal (Value.num 3), val
+ val = ValueStream 'num', 3
+ assert.is.equal (ValueStream.num 3), val
val\set 4
- assert.is.equal (Value.num 4), val
- assert.not.equal (Value.num 3), val
+ assert.is.equal (ValueStream.num 4), val
+ assert.not.equal (ValueStream.num 3), val
it 'marks the value dirty', ->
- val = Value 'num', 3
+ val = ValueStream 'num', 3
assert.is.false val\dirty!
val\set 4
@@ -118,42 +118,42 @@ describe 'Value', ->
assert_noop = (val) ->
assert.is.equal val, val\eval!\const!
- assert_noop Value.num 2
- assert_noop Value.str 'hello'
+ assert_noop ValueStream.num 2
+ assert_noop ValueStream.str 'hello'
it 'looks up symbols in the scope', ->
scope = with Scope!
- \set 'number', Result value: Value.num 3
- \set 'hello', Result value: Value.str "world"
- \set 'goodbye', Result value: Value.sym "again"
+ \set 'number', Result value: ValueStream.num 3
+ \set 'hello', Result value: ValueStream.str "world"
+ \set 'goodbye', Result value: ValueStream.sym "again"
assert_eval = (sym, val) ->
- const = Value.sym sym
+ const = ValueStream.sym sym
assert.is.equal val, (const\eval scope)\const!
- assert_eval 'number', Value.num 3
- assert_eval 'hello', Value.str "world"
- assert_eval 'goodbye', Value.sym "again"
+ assert_eval 'number', ValueStream.num 3
+ assert_eval 'hello', ValueStream.str "world"
+ assert_eval 'goodbye', ValueStream.sym "again"
it ':quote s literals as themselves', ->
assert_noop = (val) -> assert.is.equal val, val\quote!
- assert_noop Value.num 2
- assert_noop Value.str 'hello'
- assert_noop Value.sym 'world'
+ assert_noop ValueStream.num 2
+ assert_noop ValueStream.str 'hello'
+ assert_noop ValueStream.sym 'world'
it ':clone sliterals as themselves', ->
assert_noop = (val) -> assert.is.equal val, val\clone!
- assert_noop Value.num 2
- assert_noop Value.str 'hello'
- assert_noop Value.sym 'world'
+ assert_noop ValueStream.num 2
+ assert_noop ValueStream.str 'hello'
+ assert_noop ValueStream.sym 'world'
describe ':fork', ->
it 'is equal to the original', ->
- a = Value.num 2
- b = Value.str 'asdf'
- c = with Value 'weird', {}, '(raw)'
+ a = ValueStream.num 2
+ b = ValueStream.str 'asdf'
+ c = with ValueStream 'weird', {}, '(raw)'
\set {}
aa, bb, cc = a\fork!, b\fork!, c\fork!
@@ -168,8 +168,8 @@ describe 'Value', ->
assert.is.equal c.raw, cc.raw
it 'isolates the original from the fork', ->
- a = Value.num 3
- b = with Value 'weird', {}, '(raw)'
+ a = ValueStream.num 3
+ b = with ValueStream 'weird', {}, '(raw)'
\set {}
aa, bb = a\fork!, b\fork!