diff options
| author | s-ol <s-ol@users.noreply.github.com> | 2020-05-12 14:38:22 +0000 |
|---|---|---|
| committer | s-ol <s+removethis@s-ol.nu> | 2025-03-02 14:23:21 +0000 |
| commit | 525464163d1ebfc0679273bc7a706847e2411175 (patch) | |
| tree | cbe6ee8d776f974c4628f11f9870a6b7ec148f75 | |
| parent | RTNode constifies constant SigStreams (diff) | |
| download | alive-525464163d1ebfc0679273bc7a706847e2411175.tar.gz alive-525464163d1ebfc0679273bc7a706847e2411175.zip | |
base.match: match constants as sigs
| -rw-r--r-- | alv/base/init.moon | 5 | ||||
| -rw-r--r-- | alv/base/match.moon | 60 | ||||
| -rw-r--r-- | spec/match_spec.moon | 84 |
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}" |
