aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authors-ol <s-ol@users.noreply.github.com>2020-05-12 16:27:19 +0000
committers-ol <s+removethis@s-ol.nu>2025-03-02 14:23:21 +0000
commit8ecead9e25187e1b4dcd3c9cef2cd21b8384eaba (patch)
treee893442b00d19fb5576df24b086fe3ad44c20809
parentbase.match: never recall by default (diff)
downloadalive-8ecead9e25187e1b4dcd3c9cef2cd21b8384eaba.tar.gz
alive-8ecead9e25187e1b4dcd3c9cef2cd21b8384eaba.zip
add and use PureOp
-rw-r--r--alv-lib/logic.moon100
-rw-r--r--alv-lib/math.moon59
-rw-r--r--alv-lib/string.moon15
-rw-r--r--alv/base/init.moon4
-rw-r--r--alv/base/pureop.moon56
5 files changed, 128 insertions, 106 deletions
diff --git a/alv-lib/logic.moon b/alv-lib/logic.moon
index aaf255e..5ca4e68 100644
--- a/alv-lib/logic.moon
+++ b/alv-lib/logic.moon
@@ -1,4 +1,4 @@
-import Op, SigStream, Constant, Input, Error, T, val from require 'alv.base'
+import PureOp, Constant, T, val, evt from require 'alv.base'
all_same = (first, list) ->
for v in *list
@@ -14,19 +14,16 @@ tobool = (val) ->
else
true
-class ReduceOp extends Op
- pattern = val! + val! * 0
- setup: (inputs) =>
- @out or= SigStream T.bool
- { first, rest } = pattern\match inputs
- super
- first: Input.hot first
- rest: [Input.hot v for v in *rest]
+any = val! / evt!
+
+class ReduceOp extends PureOp
+ pattern: any\rep 2, nil
+ type: T.bool
tick: =>
- { :first, :rest } = @unwrap_all!
- accum = tobool first
- for val in *rest
+ args = @unwrap_all!
+ accum = tobool args[1]
+ for val in *args[2,]
accum = @.fn accum, tobool val
@out\set accum
@@ -38,32 +35,22 @@ eq = Constant.meta
examples: { '(== a b [c]…)', '(eq a b [c]…)' }
description: "`true` if the types and values of all arguments are equal."
- value: class extends Op
- pattern = val! + val! * 0
+ value: class extends ReduceOp
setup: (inputs) =>
- @out or= SigStream T.bool, false
- { first, rest } = pattern\match inputs
- same = all_same first\type!, [i\type! for i in *rest]
-
- super if same
- {
- first: Input.hot first
- rest: [Input.hot v for v in *rest]
- }
- else
- {}
+ @state or= {}
+ @state.same_type = all_same inputs[1]\type!, [i\type! for i in *inputs[2,]]
+ super inputs
tick: =>
- if not @inputs.first
+ if not @state.same_type
@out\set false
return
- { :first, :rest } = @unwrap_all!
- type = @inputs.first\type!
-
- equal = true
- for other in *rest
- if not type\eq first, other
+ typ = @inputs[1]\type!
+ args = @unwrap_all!
+ equal, first = true, args[1]
+ for other in *args[2,]
+ if not typ\eq first, other
equal = false
break
@@ -76,25 +63,24 @@ not_eq = Constant.meta
examples: { '(!= a b [c]…)', '(not-eq a b [c]…)' }
description: "`true` if types or values of any two arguments are different."
- value: class extends Op
+ value: class extends ReduceOp
setup: (inputs) =>
- @out or= SigStream T.bool, false
- assert #inputs > 1, Error 'argument', "need at least two values"
- super [Input.hot v for v in *inputs]
+ @state or= {}
+ @state.same_type = all_same inputs[1]\type!, [i\type! for i in *inputs[2,]]
+ super inputs
tick: =>
- if not @inputs[1]
+ if not @state.same_type
@out\set true
return
- diff = true
- for a=1, #@inputs-1
- for b=a+1, #@inputs
- if @inputs[a].result == @inputs[b].result
- diff = false
- break
-
- break unless diff
+ typ = @inputs[1]\type!
+ args = @unwrap_all!
+ diff, first = true, args[1]
+ for other in *args[2,]
+ if typ\eq first, other
+ diff = false
+ break
@out\set diff
@@ -120,14 +106,10 @@ not_ = Constant.meta
summary: "Logical NOT."
examples: { '(not a)' }
- value: class extends Op
- setup: (inputs) =>
- @out or= SigStream T.bool, false
- value = val!\match inputs
- super value: Input.hot value
-
- tick: =>
- @out\set not tobool @inputs.value!
+ value: class extends PureOp
+ pattern: any\rep 1, 1
+ type: T.bool
+ tick: => @out\set not tobool @inputs[1]!
bool = Constant.meta
meta:
@@ -136,14 +118,10 @@ bool = Constant.meta
examples: { '(bool a)' }
description: "`false` if a is `false`, `nil` or `0`, `true` otherwise."
- value: class extends Op
- setup: (inputs) =>
- @out or= SigStream T.bool
- value = val!\match inputs
- super value: Input.hot value
-
- tick: =>
- @out\set tobool @inputs.value!
+ value: class extends PureOp
+ pattern: any\rep 1, 1
+ type: T.bool
+ tick: => @out\set tobool @inputs[1]!
{
:eq, '==': eq
diff --git a/alv-lib/math.moon b/alv-lib/math.moon
index 8037e8a..b51718a 100644
--- a/alv-lib/math.moon
+++ b/alv-lib/math.moon
@@ -1,31 +1,23 @@
-import Op, Constant, SigStream, Error, Input, T, val from require 'alv.base'
+import PureOp, Constant, T, val, evt from require 'alv.base'
unpack or= table.unpack
-class ReduceOp extends Op
- pattern = val.num + val.num*0
- setup: (inputs) =>
- @out or= SigStream T.num
- { first, rest } = pattern\match inputs
- super
- first: Input.hot first
- rest: [Input.hot v for v in *rest]
+num = val.num / evt.num
+
+class ReduceOp extends PureOp
+ pattern: num\rep 2, nil
+ type: T.num
tick: =>
- { :first, :rest } = @unwrap_all!
- accum = first
- for val in *rest
+ args = @unwrap_all!
+ accum = args[1]
+ for val in *args[2,]
accum = @.fn accum, val
@out\set accum
func_op = (func, pattern) ->
- class extends Op
-
- setup: (inputs) =>
- @out or= SigStream T.num
-
- params = pattern\match inputs
- super [Input.hot p for p in *params]
-
+ class extends PureOp
+ pattern: pattern
+ type: T.num
tick: => @out\set func unpack @unwrap_all!
@@ -35,20 +27,15 @@ func_def = (name, args, func, summary, pattern) ->
:name
:summary
examples: { "(#{name} #{args})" }
- value: func_op func, pattern or val.num\rep 1, 1
+ value: func_op func, pattern or num\rep 1, 1
evenodd_op = (remainder) ->
- class extends Op
- pattern = val.num + -val.num
- setup: (inputs) =>
- @out or= SigStream T.bool
- { val, div } = pattern\match inputs
- super
- val: Input.hot val
- div: Input.hot div or SigStream.num 2
+ class extends PureOp
+ pattern: num + -num
+ type: T.bool
tick: =>
- { :val, :div } = @unwrap_all!
+ { val, div } = @unwrap_all!
@out\set (val % div) == remainder
add = Constant.meta
@@ -101,7 +88,7 @@ mod = Constant.meta
summary: 'Modulo operator.'
examples: { '(% num div)', '(mod num div)' }
description: "Calculate remainder of division by `div`."
- value: func_op ((a, b) -> a % b), val.num + val.num
+ value: func_op ((a, b) -> a % b), num + num
even = Constant.meta
meta:
@@ -127,7 +114,7 @@ mix = Constant.meta
summary: 'Linearly interpolate.'
examples: { '(mix a b i)' }
description: "Interpolate between `a` and `b` using `i` in range 0-1."
- value: func_op ((a, b, i) -> i*b + (1-i)*a), val.num + val.num + val.num
+ value: func_op ((a, b, i) -> i*b + (1-i)*a), num + num + num
min = Constant.meta
meta:
@@ -135,7 +122,7 @@ min = Constant.meta
summary: "Find the minimum."
examples: { '(min a b [c…])' }
description: "Return the lowest of arguments."
- value: func_op math.min, val.num*0
+ value: func_op math.min, num*0
max = Constant.meta
meta:
@@ -143,7 +130,7 @@ max = Constant.meta
summary: "Find the maximum."
examples: { '(max a b [c…])' }
description: "Return the highest of arguments."
- value: func_op math.max, val.num*0
+ value: func_op math.max, num*0
cos = func_def 'cos', 'alpha', math.cos, "Cosine function (radians)."
sin = func_def 'sin', 'alpha', math.sin, "Sine function (radians)."
@@ -151,7 +138,7 @@ tan = func_def 'tan', 'alpha', math.tan, "Tangent function (radians)."
acos = func_def 'acos', 'cos', math.acos, "Inverse cosine function (radians)."
asin = func_def 'asin', 'sin', math.asin, "Inverse sine function (radians)."
atan = func_def 'atan', 'tan', math.atan, "Inverse tangent function (radians)."
-atan2 = func_def 'atan2', 'y x', math.atan2, "Inverse tangent function (two argument version).", val.num + val.num
+atan2 = func_def 'atan2', 'y x', math.atan2, "Inverse tangent function (two argument version).", num + num
cosh = func_def 'cosh', 'alpha', math.cosh, "Hyperbolic cosine function (radians)."
sinh = func_def 'sinh', 'alpha', math.sinh, "Hyperbolic sine function (radians)."
tanh = func_def 'tanh', 'alpha', math.tanh, "Hyperbolic tangent function (radians)."
@@ -161,7 +148,7 @@ ceil = func_def 'ceil', 'val', math.ceil, "Round towards positive infinity."
abs = func_def 'abs', 'val', math.abs, "Get the absolute value."
exp = func_def 'exp', 'exp', math.floor, "*e* number raised to a power."
-log = func_def 'log', 'val [base]', math.log, "Logarithm with given base.", val.num + -val.num
+log = func_def 'log', 'val [base]', math.log, "Logarithm with optional base.", num + -num
log10 = func_def 'log10', 'val', math.log10, "Logarithm with base 10."
sqrt = func_def 'sqrt', 'val', math.sqrt, "Square root function."
diff --git a/alv-lib/string.moon b/alv-lib/string.moon
index 9f09f10..24a2a1b 100644
--- a/alv-lib/string.moon
+++ b/alv-lib/string.moon
@@ -1,17 +1,16 @@
-import Op, Constant, SigStream, Input, T from require 'alv.base'
+import PureOp, Constant, Input, T, val, evt from require 'alv.base'
+
+any = val! / evt!
str = Constant.meta
meta:
name: 'str'
summary: "Concatenate/stringify values."
examples: { '(.. v1 [v2…])', '(str v1 [v2…])' }
- value: class extends Op
- setup: (inputs) =>
- @out or= SigStream T.str
- super [Input.hot v for v in *inputs]
-
- tick: =>
- @out\set table.concat [tostring v! for v in *@inputs]
+ value: class extends PureOp
+ pattern: any\rep 1, nil
+ type: T.str
+ tick: => @out\set table.concat [tostring i! for i in *@inputs]
{
:str, '..': str
diff --git a/alv/base/init.moon b/alv/base/init.moon
index 0cab7e3..65730a3 100644
--- a/alv/base/init.moon
+++ b/alv/base/init.moon
@@ -6,6 +6,7 @@
--
-- @module base
-- @see Op
+-- @see PureOp
-- @see Builtin
-- @see FnDef
-- @see Input
@@ -24,6 +25,7 @@
-- @see Error
import Op from require 'alv.base.op'
+import PureOp from require 'alv.base.pureop'
import Builtin from require 'alv.base.builtin'
import FnDef from require 'alv.base.fndef'
import Input from require 'alv.base.input'
@@ -34,7 +36,7 @@ import RTNode from require 'alv.rtnode'
import Error from require 'alv.error'
{
- :Op
+ :Op, :PureOp
:Builtin
:FnDef
:Input
diff --git a/alv/base/pureop.moon b/alv/base/pureop.moon
new file mode 100644
index 0000000..ea3b810
--- /dev/null
+++ b/alv/base/pureop.moon
@@ -0,0 +1,56 @@
+----
+-- Stateless Operator base for pure functions.
+--
+-- All arguments can be evt- and val-capable. If one of the arguments is an
+-- !-stream, it will be the only `Input.hot`. If there are no !-streams,
+-- all inputs are hot. Passing more than one !-stream is an argument error.
+--
+-- To use `PureOp`, extend the class and set/implement only `pattern`, `type`
+-- and `tick`.
+-- @classmod PureOp
+import Op from require 'alv.base.op'
+import Input from require 'alv.base.input'
+import SigStream, EvtStream from require 'alv.result'
+import Error from require 'alv.error'
+
+unpack or= table.unpack
+
+class PureOp extends Op
+--- members.
+-- @section members
+
+ --- the argument pattern.
+ --
+ -- Must resolve to a simple sequence-table (depth 1).
+ --
+ -- @tfield match.Pattern pattern
+ @pattern: nil
+
+ --- the result type.
+ -- @tfield type.Type type
+ @type: nil
+
+ --- set up inputs for a range of things.
+ setup: (inputs) =>
+ args = @@pattern\match inputs
+
+ local trigger
+ for arg in *args
+ if arg.result.metatype == '!'
+ assert not trigger, Error 'argument', "pure op can take at most one !-stream."
+ trigger = arg
+
+ if trigger
+ super for a in *args
+ if a == trigger
+ Input.hot trigger
+ else
+ Input.cold a
+ @out = EvtStream @@type
+ else
+ super [Input.hot a for a in *args]
+ @out or= SigStream @@type
+
+{
+ :PureOp
+}