aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authors-ol <s-ol@users.noreply.github.com>2020-05-12 14:38:22 +0000
committers-ol <s+removethis@s-ol.nu>2025-03-02 14:23:21 +0000
commit525464163d1ebfc0679273bc7a706847e2411175 (patch)
treecbe6ee8d776f974c4628f11f9870a6b7ec148f75
parentRTNode constifies constant SigStreams (diff)
downloadalive-525464163d1ebfc0679273bc7a706847e2411175.tar.gz
alive-525464163d1ebfc0679273bc7a706847e2411175.zip
base.match: match constants as sigs
-rw-r--r--alv/base/init.moon5
-rw-r--r--alv/base/match.moon60
-rw-r--r--spec/match_spec.moon84
3 files changed, 107 insertions, 42 deletions
diff --git a/alv/base/init.moon b/alv/base/init.moon
index 90f1dfc..0cab7e3 100644
--- a/alv/base/init.moon
+++ b/alv/base/init.moon
@@ -9,6 +9,7 @@
-- @see Builtin
-- @see FnDef
-- @see Input
+-- @see base.match.const
-- @see base.match.val
-- @see base.match.evt
-- @see Constant
@@ -26,7 +27,7 @@ import Op from require 'alv.base.op'
import Builtin from require 'alv.base.builtin'
import FnDef from require 'alv.base.fndef'
import Input from require 'alv.base.input'
-import val, evt from require 'alv.base.match'
+import const, val, evt from require 'alv.base.match'
import Constant, SigStream, EvtStream, IOStream from require 'alv.result'
import T, Primitive, Array, Struct from require 'alv.type'
import RTNode from require 'alv.rtnode'
@@ -37,7 +38,7 @@ import Error from require 'alv.error'
:Builtin
:FnDef
:Input
- :val, :evt
+ :const, :val, :evt
-- redundant exports, to keep anything an extension might need in one import
diff --git a/alv/base/match.moon b/alv/base/match.moon
index f19c4d8..74046f2 100644
--- a/alv/base/match.moon
+++ b/alv/base/match.moon
@@ -9,9 +9,10 @@
-- `Repeat`, `Sequence`, `Choice`, and `Optional`. They can be used directly,
-- but there is also a number of shorthands for assembling patterns quickly:
--
--- - `val()` and `evt()`: Shorthands for `Type('value')` and `Type('event')`
--- - `val.num`: Shorthand for `Type('value', 'num')`
--- - `evt.str`: Shorthand for `Type('event', 'str')`
+-- - `const()`, `val()` and `evt()`: Shorthands for `Type('='), Type('~'), Type('!')`
+-- - `const.sym`: Shorthand for `Type('=', T.sym)`
+-- - `val.num`: Shorthand for `Type('~', T.num)`
+-- - `evt.str`: Shorthand for `Type('!', T.str)`
-- - `pat * 2`: Shorthand for `Repeat(pat, 1, 2)` (1-4 times `pat`)
-- - `pat * 0`: Shorthand for `Repeat(pat, 1, nil)` (1-* times `pat`)
-- - `pat ^ 2`: Shorthand for `Repeat(pat, 0, 2)` (0-4 times `pat`)
@@ -45,7 +46,7 @@
--
-- @module base.match
import Error from require 'alv.error'
-import Primitive from require 'alv.type'
+import T from require 'alv.type'
local Repeat, Sequence, Choice, Optional
@@ -95,28 +96,25 @@ class Pattern
-- using both afterwards (recall mode).
--
-- @function Type
--- @tparam string metatype "value" or "event"
+-- @tparam string metatype `'~', '!' or '='
-- @tparam ?string type type name
class Type extends Pattern
new: (@metatype, @type) =>
@recall = not @type
+ casts = { '!!': true, '==': true, '~~': true, '~=': true }
capture: (seq, i) =>
return unless seq[i]
type, mt = seq[i]\type!, seq[i]\metatype!
- if @metatype == 'event'
- return unless mt == '!'
- else
- return if mt == '!'
+
+ if not casts[@metatype .. mt]
+ return
match = if @type then type == @type else @remember type
if match
1, seq[i]
- __tostring: =>
- str = tostring @type or @metatype
- str ..= '!' if @metatype == 'event'
- str
+ __tostring: => "#{@type or 'any'}#{@metatype}"
--- Repeat a pattern.
--
@@ -261,43 +259,61 @@ class Optional extends Pattern
__tostring: => "#{@inner}?"
---- `Value` shorthands.
+--- `Type` shorthands for matching `Constant`s.
+--
+-- Call or index with a string to obtain an `Type` instance.
+-- Call to obtain a wildcard pattern.
+--
+-- const.bang, const.str, const.num
+-- const['midi/message'], const(Primitive 'midi/message')
+-- const()
+--
+-- @table const
+const = setmetatable {}, {
+ __index: (key) =>
+ with v = Type '=', T[key]
+ @[key] = v
+
+ __call: (...) => Type '=', ...
+}
+
+--- `Type` shorthands for matching `ValueStream`s and `Constant`s.
--
-- Call or index with a string to obtain a `Type` instance.
-- Call to obtain a wildcard pattern.
--
-- val.str, val.num
--- val['vec3'], val('vec3')
+-- val['vec3'], val(T.vec3)
-- val()
--
-- @table val
val = setmetatable {}, {
__index: (key) =>
- with v = Type 'value', Primitive key
+ with v = Type '~', T[key]
@[key] = v
- __call: (...) => Type 'value', ...
+ __call: (...) => Type '~', ...
}
---- `Event` shorthands.
+--- `Type` shorthands for matching `EvtStream` and `IOStream`s.
--
-- Call or index with a string to obtain an `Type` instance.
-- Call to obtain a wildcard pattern.
--
-- evt.bang, evt.str, evt.num
--- evt['midi/message'], evt('midi/message')
+-- evt['midi/message'], evt(Primitive 'midi/message')
-- evt()
--
-- @table evt
evt = setmetatable {}, {
__index: (key) =>
- with v = Type 'event', Primitive key
+ with v = Type '!', T[key]
@[key] = v
- __call: (...) => Type 'event', ...
+ __call: (...) => Type '!', ...
}
{
:Type, :Repeat, :Sequence, :Choice, :Optional
- :val, :evt
+ :const, :val, :evt
}
diff --git a/spec/match_spec.moon b/spec/match_spec.moon
index 8be64de..bb4a42b 100644
--- a/spec/match_spec.moon
+++ b/spec/match_spec.moon
@@ -1,4 +1,4 @@
-import val, evt from require 'alv.base.match'
+import const, val, evt from require 'alv.base.match'
import Op, Input from require 'alv.base'
import RTNode, T, Error from require 'alv'
@@ -6,6 +6,10 @@ op_with_inputs = (inputs) ->
with Op!
\setup inputs if inputs
+mk_const = (type, const) ->
+ result = T[type]\mk_const true
+ RTNode :result, op: op_with_inputs { Input.hot result }
+
mk_val = (type, const) ->
result = T[type]\mk_sig true
RTNode :result, op: op_with_inputs { Input.hot result }
@@ -21,15 +25,28 @@ describe 'val and evt', ->
num = mk_val 'num'
assert.is.equal str, val!\match { str }
assert.is.equal num, val!\match { num }
+ assert.has.error -> const!\match { str }
+ assert.has.error -> const!\match { num }
assert.has.error -> evt!\match { str }
assert.has.error -> evt!\match { num }
str = mk_evt 'str'
num = mk_evt 'num'
- assert.is.equal str, evt!\match { str }
- assert.is.equal num, evt!\match { num }
assert.has.error -> val!\match { str }
assert.has.error -> val!\match { num }
+ assert.has.error -> const!\match { str }
+ assert.has.error -> const!\match { num }
+ assert.is.equal str, evt!\match { str }
+ assert.is.equal num, evt!\match { num }
+
+ str = mk_const 'str'
+ num = mk_const 'num'
+ assert.is.equal str, val!\match { str }
+ assert.is.equal num, val!\match { num }
+ assert.is.equal str, const!\match { str }
+ assert.is.equal num, const!\match { num }
+ assert.has.error -> evt!\match { str }
+ assert.has.error -> evt!\match { num }
it 'is in recall mode', ->
value = val!
@@ -58,8 +75,9 @@ describe 'val and evt', ->
assert.has.error -> two_equal_values\match { str1, str2 }
it 'stringifies well', ->
- assert.is.equal 'event!', tostring evt!
- assert.is.equal 'value', tostring val!
+ assert.is.equal 'any=', tostring const!
+ assert.is.equal 'any!', tostring evt!
+ assert.is.equal 'any~', tostring val!
describe 'typed shorthand', ->
it 'matches by metatype', ->
@@ -67,21 +85,45 @@ describe 'val and evt', ->
num = mk_val 'num'
assert.is.equal str, val.str\match { str }
assert.is.equal num, val.num\match { num }
+ assert.has.error -> const.str\match { str }
+ assert.has.error -> const.num\match { num }
assert.has.error -> evt.str\match { str }
assert.has.error -> evt.num\match { num }
str = mk_evt 'str'
num = mk_evt 'num'
- assert.is.equal str, evt.str\match { str }
- assert.is.equal num, evt.num\match { num }
assert.has.error -> val.str\match { str }
assert.has.error -> val.num\match { num }
+ assert.has.error -> const.str\match { str }
+ assert.has.error -> const.num\match { num }
+ assert.is.equal str, evt.str\match { str }
+ assert.is.equal num, evt.num\match { num }
+
+ str = mk_const 'str'
+ num = mk_const 'num'
+ assert.is.equal str, val.str\match { str }
+ assert.is.equal num, val.num\match { num }
+ assert.is.equal str, const.str\match { str }
+ assert.is.equal num, const.num\match { num }
+ assert.has.error -> evt.str\match { str }
+ assert.has.error -> evt.num\match { num }
it 'matches by type', ->
+ str = mk_const 'str'
+ num = mk_const 'num'
+ assert.is.equal str, val.str\match { str }
+ assert.is.equal num, val.num\match { num }
+ assert.is.equal str, const.str\match { str }
+ assert.is.equal num, const.num\match { num }
+ assert.has.error -> val.num\match { str }
+ assert.has.error -> val.str\match { num }
+
str = mk_val 'str'
num = mk_val 'num'
assert.is.equal str, val.str\match { str }
assert.is.equal num, val.num\match { num }
+ assert.has.error -> const.num\match { str }
+ assert.has.error -> const.str\match { num }
assert.has.error -> val.num\match { str }
assert.has.error -> val.str\match { num }
@@ -89,14 +131,18 @@ describe 'val and evt', ->
num = mk_evt 'num'
assert.is.equal str, evt.str\match { str }
assert.is.equal num, evt.num\match { num }
+ assert.has.error -> const.num\match { str }
+ assert.has.error -> const.str\match { num }
assert.has.error -> evt.num\match { str }
assert.has.error -> evt.str\match { num }
it 'stringifies well', ->
assert.is.equal 'str!', tostring evt.str
assert.is.equal 'num!', tostring evt.num
- assert.is.equal 'str', tostring val.str
- assert.is.equal 'num', tostring val.num
+ assert.is.equal 'str~', tostring val.str
+ assert.is.equal 'num~', tostring val.num
+ assert.is.equal 'str=', tostring const.str
+ assert.is.equal 'num=', tostring const.num
describe 'choice', ->
str = mk_val 'str'
@@ -130,7 +176,7 @@ describe 'choice', ->
assert.has.error -> same\match { num, str }
it 'stringifies well', ->
- assert.is.equal '(str | num)', tostring choice
+ assert.is.equal '(str~ | num~)', tostring choice
describe 'sequence', ->
str = mk_val 'str'
@@ -167,7 +213,7 @@ describe 'sequence', ->
assert.has.error -> rep\match { str, num, num, num }
it 'stringifies well', ->
- assert.is.equal '(str num bool!)', tostring seq
+ assert.is.equal '(str~ num~ bool!)', tostring seq
describe 'repeat', ->
str = mk_val 'str'
@@ -218,15 +264,16 @@ describe 'repeat', ->
assert.has.error -> rep\match (times 2, num)
it 'stringifies well', ->
- assert.is.equal 'str{1-3}', tostring val.str*3
- assert.is.equal 'str{1-*}', tostring val.str*0
- assert.is.equal 'str{0-*}', tostring val.str^0
- assert.is.equal 'str{2-2}', tostring val.str\rep 2, 2
+ assert.is.equal 'str~{1-3}', tostring val.str*3
+ assert.is.equal 'str~{1-*}', tostring val.str*0
+ assert.is.equal 'str~{0-*}', tostring val.str^0
+ assert.is.equal 'str~{2-2}', tostring val.str\rep 2, 2
describe 'complex nesting', ->
bang = mk_evt 'bang'
str = mk_val 'str'
num = mk_val 'num'
+ num_c = mk_const 'num'
pattern = -evt.bang + val.num*4 +
(val.str + (val.num / val.str))\named('key', 'val')^0
@@ -236,13 +283,14 @@ describe 'complex nesting', ->
{ key: str, val: str } } },
pattern\match { num, str, num, str, str }
assert.has.error -> pattern\match { num, str }
- assert.has.error -> pattern\match { bang, num, num, num, num, num, num }
+ assert.has.error -> pattern\match { num_c, str }
+ assert.has.error -> pattern\match { bang, num_c, num, num_c, num, num, num }
assert.has.error -> pattern\match { bang, bang, num }
- assert.has.error -> pattern\match { num, str, num, str }
+ assert.has.error -> pattern\match { num, str, num_c, str }
assert.has.error -> pattern\match { num, str, num, str, mk_val 'bool' }
it 'stringifies well', ->
- assert.is.equal '(bang!? num{1-4} (str (num | str)){0-*})', tostring pattern
+ assert.is.equal '(bang!? num~{1-4} (str~ (num~ | str~)){0-*})', tostring pattern
it 'gives useful error feedback', ->
msg = "couldn't match arguments (num~ str~ bool~) against pattern #{pattern}"