From c484cce5ab1f6acd14ecad30adb586a6d5fa0fd1 Mon Sep 17 00:00:00 2001 From: s-ol Date: Sat, 5 Feb 2022 11:11:13 +0100 Subject: rearrange spec, fix for Lua 5.1 --- alv/result/const.moon | 7 +- alv/result/sig.moon | 4 +- spec/cell_spec.moon | 58 ------- spec/input_spec.moon | 156 ------------------ spec/internal/cell_spec.moon | 58 +++++++ spec/internal/input_spec.moon | 156 ++++++++++++++++++ spec/internal/match_spec.moon | 307 +++++++++++++++++++++++++++++++++++ spec/internal/parsing_spec.moon | 141 ++++++++++++++++ spec/internal/pureop_spec.moon | 128 +++++++++++++++ spec/internal/registry_spec.moon | 8 + spec/internal/result/const_spec.moon | 158 ++++++++++++++++++ spec/internal/result/evt_spec.moon | 144 ++++++++++++++++ spec/internal/result/sig_spec.moon | 116 +++++++++++++ spec/internal/rtnode_spec.moon | 216 ++++++++++++++++++++++++ spec/internal/scope_spec.moon | 166 +++++++++++++++++++ spec/internal/tag_spec.moon | 91 +++++++++++ spec/internal/type_spec.moon | 175 ++++++++++++++++++++ spec/lang/array_spec.moon | 148 ----------------- spec/lang/cond_spec.moon | 92 ----------- spec/lang/do_spec.moon | 27 --- spec/lang/fn_spec.moon | 51 ------ spec/lang/literal_spec.moon | 33 ---- spec/lang/logic_spec.moon | 264 ------------------------------ spec/lang/math_spec.moon | 267 ------------------------------ spec/lang/struct_spec.moon | 51 ------ spec/lang/testing_spec.moon | 80 --------- spec/lang/thread_spec.moon | 24 --- spec/lib/array_spec.moon | 148 +++++++++++++++++ spec/lib/builtins/cond_spec.moon | 92 +++++++++++ spec/lib/builtins/do_spec.moon | 27 +++ spec/lib/builtins/fn_spec.moon | 51 ++++++ spec/lib/builtins/literal_spec.moon | 33 ++++ spec/lib/builtins/thread_spec.moon | 24 +++ spec/lib/logic_spec.moon | 264 ++++++++++++++++++++++++++++++ spec/lib/math_spec.moon | 267 ++++++++++++++++++++++++++++++ spec/lib/struct_spec.moon | 51 ++++++ spec/lib/testing_spec.moon | 80 +++++++++ spec/match_spec.moon | 307 ----------------------------------- spec/parsing_spec.moon | 141 ---------------- spec/pureop_spec.moon | 128 --------------- spec/registry_spec.moon | 8 - spec/result/const_spec.moon | 158 ------------------ spec/result/evt_spec.moon | 144 ---------------- spec/result/sig_spec.moon | 116 ------------- spec/rtnode_spec.moon | 216 ------------------------ spec/scope_spec.moon | 166 ------------------- spec/tag_spec.moon | 91 ----------- spec/type_spec.moon | 175 -------------------- 48 files changed, 2905 insertions(+), 2908 deletions(-) delete mode 100644 spec/cell_spec.moon delete mode 100644 spec/input_spec.moon create mode 100644 spec/internal/cell_spec.moon create mode 100644 spec/internal/input_spec.moon create mode 100644 spec/internal/match_spec.moon create mode 100644 spec/internal/parsing_spec.moon create mode 100644 spec/internal/pureop_spec.moon create mode 100644 spec/internal/registry_spec.moon create mode 100644 spec/internal/result/const_spec.moon create mode 100644 spec/internal/result/evt_spec.moon create mode 100644 spec/internal/result/sig_spec.moon create mode 100644 spec/internal/rtnode_spec.moon create mode 100644 spec/internal/scope_spec.moon create mode 100644 spec/internal/tag_spec.moon create mode 100644 spec/internal/type_spec.moon delete mode 100644 spec/lang/array_spec.moon delete mode 100644 spec/lang/cond_spec.moon delete mode 100644 spec/lang/do_spec.moon delete mode 100644 spec/lang/fn_spec.moon delete mode 100644 spec/lang/literal_spec.moon delete mode 100644 spec/lang/logic_spec.moon delete mode 100644 spec/lang/math_spec.moon delete mode 100644 spec/lang/struct_spec.moon delete mode 100644 spec/lang/testing_spec.moon delete mode 100644 spec/lang/thread_spec.moon create mode 100644 spec/lib/array_spec.moon create mode 100644 spec/lib/builtins/cond_spec.moon create mode 100644 spec/lib/builtins/do_spec.moon create mode 100644 spec/lib/builtins/fn_spec.moon create mode 100644 spec/lib/builtins/literal_spec.moon create mode 100644 spec/lib/builtins/thread_spec.moon create mode 100644 spec/lib/logic_spec.moon create mode 100644 spec/lib/math_spec.moon create mode 100644 spec/lib/struct_spec.moon create mode 100644 spec/lib/testing_spec.moon delete mode 100644 spec/match_spec.moon delete mode 100644 spec/parsing_spec.moon delete mode 100644 spec/pureop_spec.moon delete mode 100644 spec/registry_spec.moon delete mode 100644 spec/result/const_spec.moon delete mode 100644 spec/result/evt_spec.moon delete mode 100644 spec/result/sig_spec.moon delete mode 100644 spec/rtnode_spec.moon delete mode 100644 spec/scope_spec.moon delete mode 100644 spec/tag_spec.moon delete mode 100644 spec/type_spec.moon diff --git a/alv/result/const.moon b/alv/result/const.moon index bace973..48c79f1 100644 --- a/alv/result/const.moon +++ b/alv/result/const.moon @@ -42,11 +42,6 @@ class Constant extends Result --- alias for `unwrap`. __call: (...) => @unwrap ... - --- compare two values. - -- - -- Compares two `SigStream`s by comparing their types and their Lua values. - __eq: (other) => other.type == @type and @type\eq other.value, @value - --- Result metatype. -- @tfield string metatype (`=`) metatype: '=' @@ -186,6 +181,8 @@ class Constant extends Result with Constant.wrap args.value .meta = args.meta if args.meta + :__eq + { :Constant } diff --git a/alv/result/sig.moon b/alv/result/sig.moon index 7e9582f..519ef06 100644 --- a/alv/result/sig.moon +++ b/alv/result/sig.moon @@ -67,8 +67,6 @@ class SigStream extends Result @value = value @updated = COPILOT.T - :__eq - --- the wrapped Lua value. -- @tfield any value @@ -82,6 +80,8 @@ class SigStream extends Result -- @tparam ?any value the Lua value to be accessed through `unwrap` new: (type, @value) => super type + :__eq + { :SigStream } diff --git a/spec/cell_spec.moon b/spec/cell_spec.moon deleted file mode 100644 index a8e79fe..0000000 --- a/spec/cell_spec.moon +++ /dev/null @@ -1,58 +0,0 @@ -import do_setup from require 'spec.test_setup' -import Cell, RootCell from require 'alv.cell' -import Constant, Scope, Tag, SimpleRegistry, globals from require 'alv' - -setup do_setup - -hello_world = Cell.parse Tag.parse('2'), { - '', (Constant.sym 'hello'), ' ', (Constant.str 'world'), '' -} -two_plus_two = Cell.parse Tag.parse('3'), { - '', (Constant.sym '+'), ' ', (Constant.num 2), ' ', (Constant.num 2), '' -} - -describe 'Cell', -> - describe 'when cloned', -> - parent = Tag.blank '1' - with hello_world\clone parent - it 'keeps children', -> - assert.is.equal Cell, .__class - assert.is.equal (Constant.sym 'hello'), \head! - assert.is.same { Constant.str 'world' }, \tail! - - it 'clones the tag', -> - assert.is.equal hello_world.tag, .tag.original - assert.is.equal parent, .tag.parent - - describe 'when evaluated', -> - it 'errors when empty', -> - cell = Cell.parse {''} - assert.has.error -> cell\eval globals - - it 'evaluates its head', -> - head = Constant.sym 'trace' - cell = Cell.parse { '', head, ' ', (Constant.sym 'true'), '' } - - s = spy.on head, 'eval' - cell\eval globals - assert.spy(s).was_called_with (match.is_ref head), (match.is_ref globals) - -describe 'RootCell', -> - test 'tag is always [0]', -> - cell = Cell.parse_root {} - assert.is.equal '[0]', cell.tag\stringify! - - test 'head is always "do"', -> - cell = Cell.parse_root {} - assert.is.equal (Constant.sym 'do'), cell\head! - - cell = RootCell nil, { hello_world, two_plus_two } - assert.is.equal (Constant.sym 'do'), cell\head! - - test 'tail is all children', -> - cell = Cell.parse_root {} - assert.is.same {}, cell\tail! - - cell = RootCell nil, { hello_world, two_plus_two } - assert.is.same { hello_world, two_plus_two }, - cell\tail! diff --git a/spec/input_spec.moon b/spec/input_spec.moon deleted file mode 100644 index 04cf16e..0000000 --- a/spec/input_spec.moon +++ /dev/null @@ -1,156 +0,0 @@ -import do_setup, do_teardown from require 'spec.test_setup' -import Input, T, Result from require 'alv.base' - -setup do_setup -teardown do_teardown - -basic_tests = (result, input) -> - it 'gives access to the Result', -> - assert.is.equal result, input.result - - it 'forwards :unwrap', -> - assert.is.equal result\unwrap!, input\unwrap!, nil - assert.is.equal result\unwrap!, input!, nil - - it 'gives access to the type string', -> - assert.is.equal result.type, input\type! - - it 'gives access to the metatype string', -> - assert.is.equal result.metatype, input\metatype! - -describe 'Input.cold', -> - result = T.num\mk_sig 1 - input = Input.cold result - - basic_tests result, input - - it 'is marked cold', -> - assert.is.equal 'cold', input.mode - - it 'is never dirty', -> - assert.is.false input\dirty! - result\set 2 - assert.is.false input\dirty! - - input\setup nil - assert.is.false input\dirty! - input\finish_setup! - - new_input = Input.cold T.num\mk_sig 3 - new_input\setup input - assert.is.false new_input\dirty! - new_input.result\set 4 - assert.is.false new_input\dirty! - input\finish_setup! - -describe 'Input.hot', -> - describe 'with Constant', -> - result = T.num\mk_const 1 - input = Input.hot result - - basic_tests result, input - - it 'is marked cold', -> - assert.is.equal 'cold', input.mode - - describe 'at evaltime', -> - it 'is dirty when new', -> - assert.is.false result\dirty! - - newinput = Input.hot result - newinput\setup nil - assert.is.true newinput\dirty! - newinput\finish_setup! - - it 'is dirty when different', -> - newval = T.num\mk_const 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 = T.num\mk_const 1 - - assert.is.false newval\dirty! - newinput = Input.hot newval - newinput\setup input - assert.is.false newinput\dirty! - newinput\finish_setup! - - describe 'with EvtStream', -> - result = T.num\mk_evt! - input = Input.hot result - - basic_tests result, input - - it 'is marked hot', -> - assert.is.equal 'hot', input.mode - - it 'is dirty when the EvtStream is dirty', -> - assert.is.false input\dirty! - assert.is.false result\dirty! - - input\setup nil - assert.is.false input\dirty! - input\finish_setup! - - COPILOT\next_tick! - result\set 1 - - assert.is.true input\dirty! - assert.is.true result\dirty! - - input\setup nil - assert.is.true input\dirty! - input\finish_setup! - - assert.is.true input\dirty! - assert.is.true result\dirty! - - describe 'with SigStream', -> - result = T.num\mk_sig 1 - local input - - describe 'at evaltime', -> - it 'is dirty when new', -> - assert.is.false result\dirty! - - input = Input.hot result - input\setup nil - assert.is.true input\dirty! - input\finish_setup! - - it 'is dirty when different', -> - newval = T.num\mk_sig 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 = T.num\mk_sig! - newval\set 1 - - assert.is.true newval\dirty! - newinput = Input.hot newval - newinput\setup input - assert.is.false newinput\dirty! - newinput\finish_setup! - - it 'is marked hot', -> - assert.is.equal 'hot', input.mode - - describe 'at runtime', -> - it 'is dirty when the result is dirty', -> - result\set 3 - assert.is.true result\dirty! - assert.is.true input\dirty! - - COPILOT\next_tick! - assert.is.false result\dirty! - assert.is.false input\dirty! diff --git a/spec/internal/cell_spec.moon b/spec/internal/cell_spec.moon new file mode 100644 index 0000000..a8e79fe --- /dev/null +++ b/spec/internal/cell_spec.moon @@ -0,0 +1,58 @@ +import do_setup from require 'spec.test_setup' +import Cell, RootCell from require 'alv.cell' +import Constant, Scope, Tag, SimpleRegistry, globals from require 'alv' + +setup do_setup + +hello_world = Cell.parse Tag.parse('2'), { + '', (Constant.sym 'hello'), ' ', (Constant.str 'world'), '' +} +two_plus_two = Cell.parse Tag.parse('3'), { + '', (Constant.sym '+'), ' ', (Constant.num 2), ' ', (Constant.num 2), '' +} + +describe 'Cell', -> + describe 'when cloned', -> + parent = Tag.blank '1' + with hello_world\clone parent + it 'keeps children', -> + assert.is.equal Cell, .__class + assert.is.equal (Constant.sym 'hello'), \head! + assert.is.same { Constant.str 'world' }, \tail! + + it 'clones the tag', -> + assert.is.equal hello_world.tag, .tag.original + assert.is.equal parent, .tag.parent + + describe 'when evaluated', -> + it 'errors when empty', -> + cell = Cell.parse {''} + assert.has.error -> cell\eval globals + + it 'evaluates its head', -> + head = Constant.sym 'trace' + cell = Cell.parse { '', head, ' ', (Constant.sym 'true'), '' } + + s = spy.on head, 'eval' + cell\eval globals + assert.spy(s).was_called_with (match.is_ref head), (match.is_ref globals) + +describe 'RootCell', -> + test 'tag is always [0]', -> + cell = Cell.parse_root {} + assert.is.equal '[0]', cell.tag\stringify! + + test 'head is always "do"', -> + cell = Cell.parse_root {} + assert.is.equal (Constant.sym 'do'), cell\head! + + cell = RootCell nil, { hello_world, two_plus_two } + assert.is.equal (Constant.sym 'do'), cell\head! + + test 'tail is all children', -> + cell = Cell.parse_root {} + assert.is.same {}, cell\tail! + + cell = RootCell nil, { hello_world, two_plus_two } + assert.is.same { hello_world, two_plus_two }, + cell\tail! diff --git a/spec/internal/input_spec.moon b/spec/internal/input_spec.moon new file mode 100644 index 0000000..04cf16e --- /dev/null +++ b/spec/internal/input_spec.moon @@ -0,0 +1,156 @@ +import do_setup, do_teardown from require 'spec.test_setup' +import Input, T, Result from require 'alv.base' + +setup do_setup +teardown do_teardown + +basic_tests = (result, input) -> + it 'gives access to the Result', -> + assert.is.equal result, input.result + + it 'forwards :unwrap', -> + assert.is.equal result\unwrap!, input\unwrap!, nil + assert.is.equal result\unwrap!, input!, nil + + it 'gives access to the type string', -> + assert.is.equal result.type, input\type! + + it 'gives access to the metatype string', -> + assert.is.equal result.metatype, input\metatype! + +describe 'Input.cold', -> + result = T.num\mk_sig 1 + input = Input.cold result + + basic_tests result, input + + it 'is marked cold', -> + assert.is.equal 'cold', input.mode + + it 'is never dirty', -> + assert.is.false input\dirty! + result\set 2 + assert.is.false input\dirty! + + input\setup nil + assert.is.false input\dirty! + input\finish_setup! + + new_input = Input.cold T.num\mk_sig 3 + new_input\setup input + assert.is.false new_input\dirty! + new_input.result\set 4 + assert.is.false new_input\dirty! + input\finish_setup! + +describe 'Input.hot', -> + describe 'with Constant', -> + result = T.num\mk_const 1 + input = Input.hot result + + basic_tests result, input + + it 'is marked cold', -> + assert.is.equal 'cold', input.mode + + describe 'at evaltime', -> + it 'is dirty when new', -> + assert.is.false result\dirty! + + newinput = Input.hot result + newinput\setup nil + assert.is.true newinput\dirty! + newinput\finish_setup! + + it 'is dirty when different', -> + newval = T.num\mk_const 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 = T.num\mk_const 1 + + assert.is.false newval\dirty! + newinput = Input.hot newval + newinput\setup input + assert.is.false newinput\dirty! + newinput\finish_setup! + + describe 'with EvtStream', -> + result = T.num\mk_evt! + input = Input.hot result + + basic_tests result, input + + it 'is marked hot', -> + assert.is.equal 'hot', input.mode + + it 'is dirty when the EvtStream is dirty', -> + assert.is.false input\dirty! + assert.is.false result\dirty! + + input\setup nil + assert.is.false input\dirty! + input\finish_setup! + + COPILOT\next_tick! + result\set 1 + + assert.is.true input\dirty! + assert.is.true result\dirty! + + input\setup nil + assert.is.true input\dirty! + input\finish_setup! + + assert.is.true input\dirty! + assert.is.true result\dirty! + + describe 'with SigStream', -> + result = T.num\mk_sig 1 + local input + + describe 'at evaltime', -> + it 'is dirty when new', -> + assert.is.false result\dirty! + + input = Input.hot result + input\setup nil + assert.is.true input\dirty! + input\finish_setup! + + it 'is dirty when different', -> + newval = T.num\mk_sig 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 = T.num\mk_sig! + newval\set 1 + + assert.is.true newval\dirty! + newinput = Input.hot newval + newinput\setup input + assert.is.false newinput\dirty! + newinput\finish_setup! + + it 'is marked hot', -> + assert.is.equal 'hot', input.mode + + describe 'at runtime', -> + it 'is dirty when the result is dirty', -> + result\set 3 + assert.is.true result\dirty! + assert.is.true input\dirty! + + COPILOT\next_tick! + assert.is.false result\dirty! + assert.is.false input\dirty! diff --git a/spec/internal/match_spec.moon b/spec/internal/match_spec.moon new file mode 100644 index 0000000..b9d5ef6 --- /dev/null +++ b/spec/internal/match_spec.moon @@ -0,0 +1,307 @@ +import const, sig, evt, any from require 'alv.base.match' +import Op, Input from require 'alv.base' +import RTNode, T, Error from require 'alv' + +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 } + +mk_evt = (type, const) -> + result = T[type]\mk_evt! + RTNode :result, op: op_with_inputs { Input.hot result } + +describe 'sig and evt', -> + describe 'type-less shorthand', -> + it 'matches metatype', -> + str = mk_val 'str' + num = mk_val 'num' + assert.is.equal str, sig!\match { str } + assert.is.equal num, sig!\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.has.error -> sig!\match { str } + assert.has.error -> sig!\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, sig!\match { str } + assert.is.equal num, sig!\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 'can recall the type', -> + value = sig!! + event = evt!! + two_equal_values = value + value + two_equal_events = event + event + + str1 = mk_val 'str' + str2 = mk_val 'str' + num = mk_val 'num' + assert.is.same { str1, str2 }, two_equal_values\match { str1, str2 } + assert.is.same { str2, str1 }, two_equal_values\match { str2, str1 } + assert.is.same { num, num }, two_equal_values\match { num, num } + assert.has.error -> two_equal_values\match { str1, num } + assert.has.error -> two_equal_values\match { num, str2 } + assert.has.error -> two_equal_events\match { str1, str2 } + + str1 = mk_evt 'str' + str2 = mk_evt 'str' + num = mk_evt 'num' + assert.is.same { str1, str2 }, two_equal_events\match { str1, str2 } + assert.is.same { str2, str1 }, two_equal_events\match { str2, str1 } + assert.is.same { num, num }, two_equal_events\match { num, num } + assert.has.error -> two_equal_events\match { str1, num } + assert.has.error -> two_equal_events\match { num, str2 } + assert.has.error -> two_equal_values\match { str1, str2 } + + it 'stringifies well', -> + assert.is.equal 'any=', tostring const! + assert.is.equal 'any!', tostring evt! + assert.is.equal 'any~', tostring sig! + + describe 'typed shorthand', -> + it 'matches by metatype', -> + str = mk_val 'str' + num = mk_val 'num' + assert.is.equal str, sig.str\match { str } + assert.is.equal num, sig.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.has.error -> sig.str\match { str } + assert.has.error -> sig.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, sig.str\match { str } + assert.is.equal num, sig.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, sig.str\match { str } + assert.is.equal num, sig.num\match { num } + assert.is.equal str, const.str\match { str } + assert.is.equal num, const.num\match { num } + assert.has.error -> sig.num\match { str } + assert.has.error -> sig.str\match { num } + + str = mk_val 'str' + num = mk_val 'num' + assert.is.equal str, sig.str\match { str } + assert.is.equal num, sig.num\match { num } + assert.has.error -> const.num\match { str } + assert.has.error -> const.str\match { num } + assert.has.error -> sig.num\match { str } + assert.has.error -> sig.str\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 -> 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 sig.str + assert.is.equal 'num~', tostring sig.num + assert.is.equal 'str=', tostring const.str + assert.is.equal 'num=', tostring const.num + +describe 'choice', -> + str = mk_val 'str' + num = mk_val 'num' + bool = mk_val 'bool' + choice = sig.str / sig.num + + it 'matches either type', -> + assert.is.equal str, choice\match { str } + assert.is.equal num, choice\match { num } + assert.has.error -> choice\match { bool } + + it 'can recall the choice', -> + same = choice! + assert.is.equal num, same\match { num } + + same = same + same + assert.is.same { str, str }, same\match { str, str } + assert.is.same { num, num }, same\match { num, num } + assert.has.error -> same\match { str, num } + assert.has.error -> same\match { num, str } + assert.has.error -> same\match { bool, bool } + + it 'makes inner types recall', -> + same = any!! + same = same + same + assert.is.same { str, str }, same\match { str, str } + assert.is.same { num, num }, same\match { num, num } + assert.is.same { bool, bool }, same\match { bool, bool } + assert.has.error -> same\match { str, num } + assert.has.error -> same\match { num, str } + + it 'stringifies well', -> + assert.is.equal '(str~ | num~)', tostring choice + +describe 'sequence', -> + str = mk_val 'str' + num = mk_val 'num' + bool = mk_evt 'bool' + seq = sig.str + sig.num + evt.bool + + it 'matches all types in order', -> + assert.is.same { str, num, bool }, seq\match { str, num, bool } + + it 'can assign non-numeric keys', -> + named = seq\named 'str', 'num', 'bool' + assert.is.same { :str, :num, :bool }, named\match { str, num, bool } + assert.is.same { str, num, bool }, seq\match { str, num, bool } + + it 'fails if too little arguments', -> + assert.has.error -> seq\match { str, num } + + it 'fails if too many arguments', -> + assert.has.error -> seq\match { str, num, bool, bool } + + it 'can handle optional children', -> + opt = -sig.str + sig.num + assert.is.same { str, num }, opt\match { str, num } + assert.is.same { nil, num }, opt\match { num } + assert.has.error -> opt\match { str, str, num } + assert.has.error -> opt\match { str, num, num } + + it 'can handle repeat children', -> + rep = sig.str + sig.num*2 + assert.is.same { str, {num} }, rep\match { str, num } + assert.is.same { str, {num,num} }, rep\match { str, num, num } + assert.has.error -> rep\match { str } + assert.has.error -> rep\match { str, num, num, num } + + it 'stringifies well', -> + assert.is.equal '(str~ num~ bool!)', tostring seq + +describe 'repeat', -> + str = mk_val 'str' + num = mk_val 'num' + + times = (n, arg) -> return for i=1,n do arg + + it '*x is [1,x[', -> + rep = sig.str*3 + assert.has.error -> rep\match (times 0, str) + assert.is.same (times 1, str), rep\match (times 1, str) + assert.is.same (times 2, str), rep\match (times 2, str) + assert.is.same (times 3, str), rep\match (times 3, str) + assert.has.error -> rep\match (times 4, str) + assert.has.error -> rep\match (times 3, num) + + it '*0 is [1,[', -> + rep = sig.str*0 + assert.has.error -> rep\match (times 0, str) + assert.is.same (times 1, str), rep\match (times 1, str) + assert.is.same (times 2, str), rep\match (times 2, str) + assert.is.same (times 20, str), rep\match (times 20, str) + assert.has.error -> rep\match (times 3, num) + + it '^x is [0,x[', -> + rep = sig.str^3 + assert.is.same {}, rep\match {} + assert.is.same (times 1, str), rep\match (times 1, str) + assert.is.same (times 2, str), rep\match (times 2, str) + assert.is.same (times 3, str), rep\match (times 3, str) + assert.has.error -> rep\match (times 4, str) + assert.has.error -> rep\match (times 3, num) + + it '^0 is [0,[', -> + rep = sig.str^0 + assert.is.same {}, rep\match {} + assert.is.same (times 1, str), rep\match (times 1, str) + assert.is.same (times 2, str), rep\match (times 2, str) + assert.is.same (times 20, str), rep\match (times 20, str) + assert.has.error -> rep\match (times 3, num) + + it ':rep(min, max) does anything else', -> + rep = sig.str\rep 2, 2 + assert.has.error -> rep\match {} + assert.has.error -> rep\match (times 1, str) + assert.is.same (times 2, str), rep\match (times 2, str) + assert.has.error -> rep\match (times 3, str) + assert.has.error -> rep\match (times 2, num) + + it 'works with complex inner types', -> + rep = (sig.num + sig.str)\rep 2, 2 + assert.has.error -> rep\match {} + assert.has.error -> rep\match {num, str} + assert.has.error -> rep\match {num, num} + assert.is.same {{num, str}, {num, str}}, rep\match {num, str, num, str} + assert.has.error -> rep\match {str, str, str, str} + assert.has.error -> rep\match {num, str, num, str, num, str} + + it 'stringifies well', -> + assert.is.equal 'str~{1-3}', tostring sig.str*3 + assert.is.equal 'str~{1-*}', tostring sig.str*0 + assert.is.equal 'str~{0-*}', tostring sig.str^0 + assert.is.equal 'str~{2-2}', tostring sig.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 + sig.num*4 + + (sig.str + (sig.num / sig.str))\named('key', 'val')^0 + + it 'just works', -> + assert.is.same { bang, { num, num }, {} }, pattern\match { bang, num, num } + assert.is.same { nil, { num }, { { key: str, val: num }, + { key: str, val: str } } }, + pattern\match { num, str, num, str, str } + assert.has.error -> pattern\match { num, str } + 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_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 + + it 'gives useful error feedback', -> + msg = "couldn't match arguments (num~ str~ bool~) against pattern #{pattern}" + err = Error 'argument', msg + assert.has.error (-> pattern\match { num, str, mk_val 'bool' }), err diff --git a/spec/internal/parsing_spec.moon b/spec/internal/parsing_spec.moon new file mode 100644 index 0000000..0486276 --- /dev/null +++ b/spec/internal/parsing_spec.moon @@ -0,0 +1,141 @@ +import space, atom, expr, explist, cell, program, comment + from require 'alv.parsing' +import Constant from require 'alv' +import Logger from require 'alv.logger' +Logger\init 'silent' + +verify_parse = (parser, val) -> + with assert parser\match val + assert.is.equal val, \stringify! + +verify_parse_nope = (parser, val) -> + with assert parser\match val + without_nope = val\match '^(.*) nope$' + assert.is.equal without_nope, \stringify! + +describe 'atom parsing', -> + test 'symbols', -> + sym = verify_parse_nope atom, 'some-toast nope' + assert.is.equal (Constant.sym 'some-toast'), sym + + describe 'numbers', -> + it 'parses ints', -> + num = verify_parse_nope atom, '1234 nope' + assert.is.equal (Constant.num 1234), num + + it 'parses floats', -> + num = verify_parse_nope atom, '0.123 nope' + assert.is.equal (Constant.num 0.123), num + + num = verify_parse_nope atom, '.123 nope' + assert.is.equal (Constant.num 0.123), num + + num = verify_parse_nope atom, '0. nope' + assert.is.equal (Constant.num 0), num + + describe 'strings', -> + it 'parses double-quote strings', -> + str = verify_parse_nope atom, '"help some stuff!" nope' + assert.is.equal (Constant.str 'help some stuff!'), str + + it 'parses single-quote strings', -> + str = verify_parse_nope atom, "'help some stuff!' nope" + assert.is.equal (Constant.str "help some stuff!"), str + + it 'handles escapes', -> + str = verify_parse_nope atom, '"string with \\"quote\\"s and \\\\" nope' + assert.is.equal (Constant.str 'string with \"quote\"s and \\'), str + + str = verify_parse_nope atom, "'string with \\'quote\\'s and \\\\' nope" + assert.is.equal (Constant.str "string with \'quote\'s and \\"), str + +describe 'Cell', -> + test 'basic parsing', -> + node = verify_parse cell, '( 3 ok-yes + "friend" )' + + assert.is.equal 3, #node.children + assert.is.equal (Constant.num 3), node.children[1] + assert.is.equal (Constant.sym 'ok-yes'), node.children[2] + assert.is.equal (Constant.str 'friend'), node.children[3] + + test 'tag parsing', -> + node = verify_parse cell, '([42]tagged 2)' + + assert.is.equal 2, #node.children + assert.is.equal 42, node.tag.value + + test 'tag parsing with whitespace', -> + node = verify_parse cell, '([42] + tagged 2)' + + assert.is.equal 2, #node.children + assert.is.equal 42, node.tag.value + +describe 'RootCell parsing', -> + describe 'handles whitespace', -> + verify = (str) -> + node = verify_parse program, str + + assert.is.equal 2, #node.children + assert.is.equal (Constant.num 3), node.children[1] + assert.is.equal (Constant.sym 'ok-yes'), node.children[2] + + it 'at the front of the string', -> + verify ' 3\tok-yes' + + it 'at the end of the string', -> + verify ' 3\tok-yes\n' + + it 'everywhere', -> + verify ' 3\tok-yes\n' + +test 'whitespace', -> + assert.is.equal ' ', space\match ' ' + assert.is.equal '\n\t ', space\match '\n\t ' + +describe 'comments', -> + comment = comment / 1 + it 'are parsed', -> + str = '#(this is a comment)' + assert.is.equal str, comment\match str + + it 'extend to matching braces', -> + str = '#(this is a comment #(with nested comments))' + assert.is.equal str, comment\match str + + it 'can nest', -> + str = '#(this is a comment (with nested parenthesis))' + assert.is.equal str, comment\match str + +describe 'resynthesis', -> + test 'mixed parsing', -> + str = '( 3 ok-yes + "friend" )' + node = verify_parse program, str + assert.is.equal str, node\stringify! + + test 'complex', -> + str = ' + #(send a CC controlled LFO to /radius) + (osc "/radius" (lfo (cc 14))) + + (osc rot + (step + #(whenever a kick is received...) + (note "kick") + + #(..cycle through random rotation values) + (random-rot) + (random-rot) + (random-rot) + (random-rot) + ) + ) ' + matched = assert.is.truthy verify_parse program, str + assert.is.equal str, matched\stringify! + + test 'nested tags', -> + str = '([2]a ([3]b))' + matched = assert.is.truthy verify_parse program, str + assert.is.equal str, matched\stringify! diff --git a/spec/internal/pureop_spec.moon b/spec/internal/pureop_spec.moon new file mode 100644 index 0000000..a8fa823 --- /dev/null +++ b/spec/internal/pureop_spec.moon @@ -0,0 +1,128 @@ +import do_setup, do_teardown, invoke_op from require 'spec.test_setup' +import PureOp, Input, T, sig, any from require 'alv.base' +import RTNode from require 'alv' + +setup do_setup +teardown do_teardown + +class TestPureOp extends PureOp + pattern: (any.num / sig.str)*3 + type: T.num + tick: => @out\set 1 + +literal = (result) -> + eval: -> + si = T.num\mk_sig! + RTNode :result, side_inputs: { [si]: Input.hot si } + +describe 'PureOp', -> + it 'matches the pattern', -> + assert.has.error -> invoke_op TestPureOp, {} + assert.has.error -> invoke_op TestPureOp, { literal T.bool\mk_evt! } + invoke_op TestPureOp, { literal T.num\mk_const 1 } + invoke_op TestPureOp, { literal T.str\mk_const 'hello' } + invoke_op TestPureOp, { literal T.num\mk_evt! } + + describe 'with constant inputs', -> + local rtn + it 'ticks once', -> + tiq = spy.on TestPureOp.__base, 'tick' + rtn = invoke_op TestPureOp, { T.num\mk_const(1), T.str\mk_const('hello') } + assert.spy(tiq).was_called_with match.is_ref(rtn.op), true + assert.is.equal 1, rtn.result! + + it 'is constant', -> + assert.is.equal '=', rtn\metatype! + + describe 'with signal inputs', -> + a = T.num\mk_sig 1 + b = T.num\mk_sig 2 + c = T.num\mk_sig 3 + tiq = spy.on TestPureOp.__base, 'tick' + rtn = invoke_op TestPureOp, { (literal a), (literal b), (literal c) } + op = rtn.op + + it 'sets up hot inputs', -> + assert.is.equal 3, #op.inputs + assert.is.equal a, op.inputs[1].result + assert.is.equal b, op.inputs[2].result + assert.is.equal c, op.inputs[3].result + assert.is.equal 'hot', op.inputs[1].mode + assert.is.equal 'hot', op.inputs[2].mode + assert.is.equal 'hot', op.inputs[3].mode + assert.spy(tiq).was_called! + + it 'has signal output', -> + assert.is.equal '~', op.out.metatype + + describe 'with event inputs', -> + a = T.num\mk_sig 1 + b = T.num\mk_evt 2 + c = T.num\mk_sig 3 + tiq = spy.on TestPureOp.__base, 'tick' + rtn = invoke_op TestPureOp, { (literal a), (literal b), (literal c) } + op = rtn.op + + it 'sets up hot input only for evt', -> + assert.is.equal 3, #op.inputs + assert.is.equal a, op.inputs[1].result + assert.is.equal b, op.inputs[2].result + assert.is.equal c, op.inputs[3].result + assert.is.equal 'cold', op.inputs[1].mode + assert.is.equal 'hot', op.inputs[2].mode + assert.is.equal 'cold', op.inputs[3].mode + assert.spy(tiq).was_not_called! + + it 'has event output', -> + assert.is.equal '!', op.out.metatype + + it 'only allows one event input', -> + a, b = T.num\mk_evt!, T.num\mk_evt! + assert.has.error -> invoke_op TestPureOp, { (literal a), (literal b) } + + it 'supports nested input patterns', -> + class NestedInputOp extends PureOp + pattern: (any.num + sig.str)\named('a', 'b')\rep 2, 2 + type: T.num + tick: => @out\set 1 + + num = T.num\mk_sig 1 + str = T.str\mk_sig 'hello' + oth = T.num\mk_evt 2 + args = { (literal num), (literal str), (literal oth), (literal str) } + rtn = invoke_op NestedInputOp, args + op = rtn.op + + assert.is.equal 2, #op.inputs + assert.is.equal num, op.inputs[1].a.result + assert.is.equal str, op.inputs[1].b.result + assert.is.equal oth, op.inputs[2].a.result + assert.is.equal str, op.inputs[2].b.result + assert.is.equal 'cold', op.inputs[1].a.mode + assert.is.equal 'cold', op.inputs[1].b.mode + assert.is.equal 'hot', op.inputs[2].a.mode + assert.is.equal 'cold', op.inputs[2].b.mode + + it 'supports dynamically generating the output type', -> + class DynamicOp extends PureOp + pattern: sig.num + any! + type: (inputs) => inputs[2]\type! + tick: => @out\set @inputs[2]! + typ = spy.on DynamicOp, 'type' + + a = T.num\mk_sig 1 + num = T.num\mk_sig 1 + str = T.str\mk_sig 1 + sym = T.sym\mk_evt 1 + + rtn = invoke_op DynamicOp, { (literal a), (literal num) } + assert.is.equal '~', rtn.result.metatype + assert.is.equal num, rtn.result + + rtn = invoke_op DynamicOp, { (literal a), (literal str) } + assert.is.equal '~', rtn.result.metatype + assert.is.equal str, rtn.result + + rtn = invoke_op DynamicOp, { (literal a), (literal sym) } + assert.is.equal '!', rtn.result.metatype + assert.is.equal T.sym, rtn.result.type diff --git a/spec/internal/registry_spec.moon b/spec/internal/registry_spec.moon new file mode 100644 index 0000000..4b0cfc3 --- /dev/null +++ b/spec/internal/registry_spec.moon @@ -0,0 +1,8 @@ +import Registry, Tag from require 'alv.registry' +import Logger from require 'alv.logger' +Logger\init 'silent' + +mk = -> + mock destroy: => + +describe 'registry', -> diff --git a/spec/internal/result/const_spec.moon b/spec/internal/result/const_spec.moon new file mode 100644 index 0000000..3f31306 --- /dev/null +++ b/spec/internal/result/const_spec.moon @@ -0,0 +1,158 @@ +import do_setup from require 'spec.test_setup' +import Constant, RTNode, Scope, SimpleRegistry, T from require 'alv' +import Op, Builtin from require 'alv.base' + +class TestOp extends Op + new: (...) => super ... + +class TestBuiltin extends Builtin + new: (...) => + +setup do_setup + +describe 'Constant', -> + it 'requires a value', -> + assert.has.error -> Constant.num! + assert.has.error -> Constant T.num + assert.has.no.error -> Constant T.bool, false + + it 'stringifies well', -> + assert.is.equal "", tostring Constant.num 4 + assert.is.equal "", tostring Constant.bool true + assert.is.equal "", tostring Constant.bool false + + describe '.wrap', -> + it 'wraps numbers', -> + got = Constant.wrap 3 + assert.is.equal T.num, got.type + assert.is.equal 3, got.value + + it 'wraps strings', -> + got = Constant.wrap "im a happy string" + assert.is.equal T.str, got.type + assert.is.equal "im a happy string", got.value + + it 'wraps Constants', -> + pi = Constant.num 3.14 + got = Constant.wrap pi + + assert.is.equal pi, got + + it 'wraps Opdefs', -> + got = Constant.wrap TestOp + + assert.is.equal T.opdef, got.type + assert.is.equal TestOp, got.value + + it 'wraps Bultins', -> + got = Constant.wrap TestBuiltin + + assert.is.equal T.builtin, got.type + assert.is.equal TestBuiltin, got.value + + it 'wraps Scopes', -> + sub = Scope! + got = Constant.wrap sub + + assert.is.equal T.scope, got.type + assert.is.equal sub, got.value + + it 'wraps tables', -> + pi = Constant.num 3.14 + got = Constant.wrap { :pi } + + assert.is.equal T.scope, got.type + assert.is.equal pi, (got.value\get 'pi')\const! + + describe ':unwrap', -> + it 'returns the raw value!', -> + assert.is.equal 3.14, (Constant.num 3.14)\unwrap! + assert.is.equal 'hi', (Constant.str 'hi')\unwrap! + assert.is.equal 'hi', (Constant.sym 'hi')\unwrap! + + test 'can assert the type', -> + assert.is.equal 3.14, (Constant.num 3.14)\unwrap T.num + assert.is.equal 'hi', (Constant.str 'hi')\unwrap T.str + assert.is.equal 'hi', (Constant.sym 'hi')\unwrap T.sym + assert.has_error -> (Constant.num 3.14)\unwrap T.sym + assert.has_error -> (Constant.str 'hi')\unwrap T.num + assert.has_error -> (Constant.sym 'hi')\unwrap T.str + + test 'has __call shorthand', -> + assert.is.equal 3.14, (Constant.num 3.14)! + assert.is.equal 'hi', (Constant.str 'hi')! + assert.is.equal 'hi', (Constant.sym 'hi')! + assert.is.equal 3.14, (Constant.num 3.14) T.num + assert.is.equal 'hi', (Constant.str 'hi') T.str + assert.is.equal 'hi', (Constant.sym 'hi') T.sym + assert.has_error -> (Constant.num 3.14) T.sym + assert.has_error -> (Constant.str 'hi') T.num + assert.has_error -> (Constant.sym 'hi') T.str + + describe 'overrides __eq', -> + it 'compares the type', -> + val = Constant.num 3 + assert.is.equal (Constant.num 3), val + assert.not.equal (Constant.str '3'), val + + val = Constant.str 'hello' + assert.is.equal (Constant.str 'hello'), val + assert.not.equal (Constant.sym 'hello'), val + + it 'compares the value', -> + val = Constant.num 3 + assert.is.equal (Constant.num 3), val + assert.not.equal (Constant.num 4), val + + it ':dirty is always false', -> + val = Constant.num 3 + assert.is.false val\dirty! + + val.value = 4 + assert.is.false val\dirty! + + describe ':eval', -> + it 'turns numbers into consts', -> + assert_noop = (val) -> + assert.is.equal val, val\eval!\const! + + assert_noop Constant.num 2 + assert_noop Constant.str 'hello' + + it 'looks up symbols in the scope', -> + scope = with Scope! + \set 'number', RTNode result: Constant.num 3 + \set 'hello', RTNode result: Constant.str "world" + \set 'goodbye', RTNode result: Constant.sym "again" + + assert_eval = (sym, val) -> + const = Constant.sym sym + assert.is.equal val, (const\eval scope)\const! + + assert_eval 'number', Constant.num 3 + assert_eval 'hello', Constant.str "world" + assert_eval 'goodbye', Constant.sym "again" + + it ':clones literals as themselves', -> + assert_noop = (val) -> assert.is.equal val, val\clone! + + assert_noop Constant.num 2 + assert_noop Constant.str 'hello' + assert_noop Constant.sym 'world' + + describe ':fork', -> + it 'is equal to the original', -> + a = Constant.num 2 + b = Constant.str 'asdf' + c = Constant T.weird, {}, '(raw)' + + aa, bb, cc = a\fork!, b\fork!, c\fork! + assert.is.equal a, aa + assert.is.equal b, bb + assert.is.equal c, cc + + assert.is.false aa\dirty! + assert.is.false bb\dirty! + assert.is.false cc\dirty! + + assert.is.equal c.raw, cc.raw diff --git a/spec/internal/result/evt_spec.moon b/spec/internal/result/evt_spec.moon new file mode 100644 index 0000000..46d4cec --- /dev/null +++ b/spec/internal/result/evt_spec.moon @@ -0,0 +1,144 @@ +import do_setup from require 'spec.test_setup' +import EvtStream, RTNode, Scope, SimpleRegistry, T from require 'alv' +import Op, Builtin from require 'alv.base' + +setup do_setup + +describe 'EvtStream', -> + it 'stringifies well', -> + number = EvtStream T.num + assert.is.equal "", tostring number + + number\set 4 + assert.is.equal "", tostring number + + bool = EvtStream T.bool + bool\set true + assert.is.equal "", tostring bool + + COPILOT\next_tick! + + bool\set false + assert.is.equal "", tostring bool + + describe ':unwrap', -> + it 'returns the set value', -> + stream = EvtStream T.num + assert.is.nil stream\unwrap! + + stream\set 3.14 + assert.is.equal 3.14, stream\unwrap! + + it 'returns nil if not dirty', -> + stream = EvtStream T.num + assert.is.nil stream\unwrap! + + stream\set 3.14 + COPILOT\next_tick! + assert.is.nil stream\unwrap! + + test 'can assert the type', -> + assert.is.nil (EvtStream T.num)\unwrap T.num + assert.is.nil (EvtStream T.str)\unwrap T.str + assert.is.nil (EvtStream T.sym)\unwrap T.sym + assert.has_error -> (EvtStream T.num)\unwrap T.sym + assert.has_error -> (EvtStream T.str)\unwrap T.num + assert.has_error -> (EvtStream T.sym)\unwrap T.str + + test 'has __call shorthand', -> + stream = EvtStream T.num + assert.is.nil stream! + + stream\set 3.14 + assert.is.equal 3.14, stream! + + describe ':set', -> + it 'sets the value', -> + stream = EvtStream T.num + assert.is.false stream\dirty! + + stream\set 4 + assert.is.equal 4, stream\unwrap! + assert.is.true stream\dirty! + + COPILOT\next_tick! + + assert.is.false stream\dirty! + stream\set 3 + assert.is.equal 3, stream\unwrap! + assert.is.true stream\dirty! + + it 'ignores nil values', -> + stream = EvtStream T.num + assert.is.nil stream\unwrap! + assert.is.false stream\dirty! + + stream\set! + assert.is.nil stream\unwrap! + assert.is.false stream\dirty! + + stream\set nil + assert.is.nil stream\unwrap! + assert.is.false stream\dirty! + + stream\set false + assert.is.equal false, stream\unwrap! + assert.is.true stream\dirty! + + it 'errors when set twice', -> + stream = EvtStream T.num + stream\set 1 + assert.has.error -> stream\set 2 + assert.is.equal 1, stream\unwrap! + + it 'resets on the next tick', -> + stream = EvtStream T.num + stream\set 1 + + COPILOT\next_tick! + + assert.is.false stream\dirty! + stream\set 2 + assert.is.equal 2, stream\unwrap! + assert.is.true stream\dirty! + + describe ':fork', -> + it 'is clean', -> + a = EvtStream T.num + b = EvtStream T.str + b\set 'asdf' + + aa, bb = a\fork!, b\fork! + assert.is.nil aa! + assert.is.nil bb! + assert.is.false aa\dirty! + assert.is.false bb\dirty! + + it 'leaves the original', -> + a = EvtStream T.num + b = EvtStream T.str + b\set 'asdf' + + aa, bb = a\fork!, b\fork! + assert.is.nil a! + assert.is.equal 'asdf', b! + assert.is.false a\dirty! + assert.is.true b\dirty! + + it 'isolates the original from the fork', -> + a = EvtStream T.num + b = EvtStream T.str + + aa, bb = a\fork!, b\fork! + a\set 1 + bb\set 2 + + assert.is.equal 1, a! + assert.is.true a\dirty! + assert.is.nil aa! + assert.is.false aa\dirty! + + assert.is.equal 2, bb! + assert.is.true bb\dirty! + assert.is.nil b! + assert.is.false b\dirty! diff --git a/spec/internal/result/sig_spec.moon b/spec/internal/result/sig_spec.moon new file mode 100644 index 0000000..65363bc --- /dev/null +++ b/spec/internal/result/sig_spec.moon @@ -0,0 +1,116 @@ +import do_setup from require 'spec.test_setup' +import SigStream, Constant, RTNode, Scope, SimpleRegistry, T from require 'alv' +import Op, Builtin from require 'alv.base' + +setup do_setup + +describe 'SigStream', -> + it 'stringifies well', -> + assert.is.equal "", tostring SigStream T.num, 4 + assert.is.equal "", tostring SigStream T.bool, true + assert.is.equal "", tostring SigStream T.bool, false + + describe ':unwrap', -> + it 'returns the raw value!', -> + assert.is.equal 3.14, (SigStream T.num, 3.14)\unwrap! + assert.is.equal 'hi', (SigStream T.str, 'hi')\unwrap! + assert.is.equal 'hi', (SigStream T.sym, 'hi')\unwrap! + + test 'can assert the type', -> + assert.is.equal 3.14, (SigStream T.num, 3.14)\unwrap T.num + assert.is.equal 'hi', (SigStream T.str, 'hi')\unwrap T.str + assert.is.equal 'hi', (SigStream T.sym, 'hi')\unwrap T.sym + assert.has_error -> (SigStream T.num, 3.14)\unwrap T.sym + assert.has_error -> (SigStream T.str, 'hi')\unwrap T.num + assert.has_error -> (SigStream T.sym, 'hi')\unwrap T.str + + test 'has __call shorthand', -> + assert.is.equal 3.14, (SigStream T.num, 3.14)! + assert.is.equal 'hi', (SigStream T.str, 'hi')! + assert.is.equal 'hi', (SigStream T.sym, 'hi')! + assert.is.equal 3.14, (SigStream T.num, 3.14) T.num + assert.is.equal 'hi', (SigStream T.str, 'hi') T.str + assert.is.equal 'hi', (SigStream T.sym, 'hi') T.sym + assert.has_error -> (SigStream T.num, 3.14) T.sym + assert.has_error -> (SigStream T.str, 'hi') T.num + assert.has_error -> (SigStream T.sym, 'hi') T.str + + describe 'overrides __eq', -> + it 'compares the type', -> + val = SigStream T.num, 3 + assert.is.equal (SigStream T.num, 3), val + assert.not.equal (SigStream T.str, '3'), val + + val = SigStream T.str, 'hello' + assert.is.equal (SigStream T.str, 'hello'), val + assert.not.equal (SigStream T.sym, 'hello'), val + + it 'compares the value', -> + val = SigStream T.num, 3 + assert.is.equal (SigStream T.num, 3), val + assert.not.equal (SigStream T.num, 4), val + + it 'can be compared to a Constant', -> + val = SigStream T.num, 3 + assert.is.equal (Constant.num 3), val + assert.not.equal (Constant.num 4), val + + val = SigStream T.str, 'hello' + assert.is.equal (Constant.str 'hello'), val + assert.not.equal (Constant.sym 'hello'), val + + describe ':set', -> + it 'sets the value', -> + val = SigStream T.num, 3 + assert.is.equal (SigStream T.num, 3), val + + val\set 4 + assert.is.equal (SigStream T.num, 4), val + assert.not.equal (SigStream T.num, 3), val + + it 'marks the value dirty', -> + val = SigStream T.num, 3 + assert.is.false val\dirty! + + val\set 4 + assert.is.true val\dirty! + + describe ':fork', -> + it 'is equal to the original', -> + a = SigStream T.num, 2 + b = SigStream T.str, 'asdf' + c = with SigStream T.weird, {}, '(raw)' + \set {} + + aa, bb, cc = a\fork!, b\fork!, c\fork! + assert.is.equal a, aa + assert.is.equal b, bb + assert.is.equal c, cc + + assert.is.false aa\dirty! + assert.is.false bb\dirty! + assert.is.true cc\dirty! + + assert.is.equal c.raw, cc.raw + + it 'isolates the original from the fork', -> + a = SigStream T.num, 3 + b = with SigStream T.weird, {}, '(raw)' + \set {} + + aa, bb = a\fork!, b\fork! + + bb\set {false} + + assert.is.same {}, b! + assert.is.same {false}, bb! + assert.is.true b\dirty! + assert.is.true bb\dirty! + + COPILOT\next_tick! + aa\set 4 + + assert.is.equal 3, a! + assert.is.equal 4, aa! + assert.is.false a\dirty! + assert.is.true aa\dirty! diff --git a/spec/internal/rtnode_spec.moon b/spec/internal/rtnode_spec.moon new file mode 100644 index 0000000..f757067 --- /dev/null +++ b/spec/internal/rtnode_spec.moon @@ -0,0 +1,216 @@ +import do_setup from require 'spec.test_setup' +import RTNode, Scope, SimpleRegistry from require 'alv' +import T, Input, Op, Constant from require 'alv.base' + +class IOOp extends Op + new: (...) => + super ... + @is_dirty = true + + poll: => @is_dirty + +make_op = (Class, inputs={}) -> + with Class! + \setup inputs + +setup do_setup + +describe 'RTNode', -> + describe 'constructor', -> + it 'takes result, children', -> + result = Constant.num 3 + + a = RTNode! + b = RTNode! + children = { a, b } + + node = RTNode :result, :children + + assert.is.equal result, node.result + assert.is.same children, node.children + + it 'takes op and detects io_ops', -> + op = make_op Op + rtn = RTNode :op + assert.is.equal op, rtn.op + assert.is.same {}, rtn.io_ops + + op = make_op IOOp + rtn = RTNode :op + assert.is.equal op, rtn.op + assert.is.same { op }, rtn.io_ops + + describe 'detects Op inputs', -> + it '(hot -> side_input)', -> + sig = T.num\mk_sig! + inp = Input.hot sig + rtn = RTNode op: make_op Op, { inp } + assert.is.same { [sig]: inp }, rtn.side_inputs + + it '(cold -> discard)', -> + rtn = RTNode op: make_op Op, { Input.cold T.num\mk_sig! } + assert.is.same {}, rtn.side_inputs + + it "(childrens' results -> discard)", -> + child = RTNode op: make_op(Op), result: T.num\mk_sig 123 + parent = RTNode children: { child }, op: make_op Op, { Input.hot child.result } + assert.is.same {}, parent.side_inputs + + describe 'lifts up', -> + it 'side_inputs from children', -> + expected_inputs = {} + children = {} + + for i=1,3 + result = T.num\mk_sig! + input = Input.hot result + table.insert children, RTNode op: make_op Op, { input } + expected_inputs[result] = input + + mid_rtn = RTNode children: children + out_rtn = RTNode children: { mid_rtn } + + assert.is.same expected_inputs, mid_rtn.side_inputs + assert.is.same expected_inputs, out_rtn.side_inputs + + it 'io_ops from children', -> + a_op = make_op IOOp + a_rtn = RTNode op: a_op + + b_op = make_op IOOp + b_rtn = RTNode op: b_op + + c_op = make_op Op + c_rtn = RTNode op: c_op + + mid_rtn = RTNode children: { a_rtn, b_rtn, c_rtn } + out_op = make_op IOOp + out_rtn = RTNode children: { mid_rtn }, op: out_op + + assert.is.same { a_op, b_op }, mid_rtn.io_ops + assert.is.same { a_op, b_op, out_op }, out_rtn.io_ops + + it ':type gets type and assets value', -> + node = RTNode result: Constant.num 2 + assert.is.equal T.num, node\type! + + node = RTNode! + assert.has.error -> node\type! + + it ':is_const', -> + result = Constant.num 2 + pure = RTNode :result + impure = RTNode op: make_op Op, { Input.hot T.num\mk_sig! } + + assert.is.true pure\is_const! + assert.is.false impure\is_const! + + assert.is.equal result, pure\const! + assert.has.error -> impure\const! + assert.has.error (-> impure\const 'test'), 'test' + + it ':make_ref', -> + result = T.num\mk_sig 2 + input = Input.hot result + op = make_op IOOp, { input } + thick = RTNode :result, :op, children: { RTNode!, RTNode! } + ref = thick\make_ref! + + assert ref + assert.is.equal thick.result, ref.result + assert.is.same thick.side_inputs, ref.side_inputs + assert.is.same {}, ref.children + assert.is.same {}, ref.io_ops + assert.is.nil ref.op + + describe ':poll_io', -> + it 'polls all io_ops in tree', -> + child = RTNode op: make_op IOOp + node = RTNode children: { child }, op: make_op IOOp + + sc = spy.on child.op, 'poll' + sn = spy.on node.op, 'poll' + node\poll_io! + + assert.spy(sc).was_called_with match.ref child.op + assert.spy(sn).was_called_with match.ref node.op + + it 'returns whether any ops were dirty', -> + child = RTNode op: make_op IOOp + node = RTNode children: { child }, op: make_op IOOp + + assert.is.true node\poll_io! + + child.op.is_dirty = false + assert.is.true node\poll_io! + + node.op.is_dirty = false + assert.is.false node\poll_io! + + child.op.is_dirty = true + assert.is.true node\poll_io! + + describe ':tick', -> + local a_value, a_child, a_input + local b_value, b_child, b_input + before_each -> + a_value = T.num\mk_evt! + a_input = Input.hot a_value + a_child = RTNode op: make_op Op, { a_input } + + b_value = T.num\mk_evt! + b_input = Input.hot b_value + b_child = RTNode op: make_op Op, { b_input } + + it 'updates children when a side_input is dirty', -> + a_value\set 1 + assert.is.true a_input\dirty! + assert.is.false b_input\dirty! + + a = spy.on a_child, 'tick' + b = spy.on b_child, 'tick' + + node = RTNode children: { a_child, b_child } + node\tick! + + assert.spy(a).was_called_with match.ref a_child + assert.spy(b).was_called_with match.ref b_child + + it 'early-outs when no side_inputs are dirty', -> + assert.is.false a_input\dirty! + assert.is.false b_input\dirty! + + a = spy.on a_child, 'tick' + b = spy.on b_child, 'tick' + + node = RTNode children: { a_child, b_child } + node\tick! + + assert.spy(a).was_not_called! + assert.spy(b).was_not_called! + + it 'updates op when any op-inputs are dirty', -> + a_value\set 1 + assert.is.true a_input\dirty! + assert.is.false b_input\dirty! + + op = make_op Op, { a: Input.hot a_value } + s = spy.on op, 'tick' + + node = RTNode :op, children: { a_child, b_child } + node\tick! + + assert.spy(s).was_called_with match.ref op + + it 'early-outs when no op-inputs are dirty', -> + a_value\set 1 + assert.is.true a_input\dirty! + assert.is.false b_input\dirty! + + op = make_op Op, { Input.hot b_value } + s = spy.on op, 'tick' + + node = RTNode :op, children: { a_child, b_child } + node\tick! + + assert.spy(s).was_not_called! diff --git a/spec/internal/scope_spec.moon b/spec/internal/scope_spec.moon new file mode 100644 index 0000000..b4a55f1 --- /dev/null +++ b/spec/internal/scope_spec.moon @@ -0,0 +1,166 @@ +import Scope, T, Constant, RTNode from require 'alv' +import Op from require 'alv.base' +import Logger from require 'alv.logger' +Logger\init 'silent' + +class TestOp extends Op + new: (...) => super ... + +wrap_res = (result) -> RTNode :result + +describe 'Scope', -> + describe 'constifies', -> + scope = Scope! + + test 'numbers', -> + scope\set_raw 'num', 3 + + got = (scope\get 'num')\const! + assert.is.equal (Constant.num 3), got + + test 'strings', -> + scope\set_raw 'str', "im a happy string" + + got = (scope\get 'str')\const! + assert.is.equal (Constant.str "im a happy string"), got + + test 'Values', -> + pi = Constant.num 3.14 + scope\set_raw 'pi', pi + assert.is.equal pi, (scope\get 'pi')\const! + + test 'Opdefs', -> + scope\set_raw 'test', TestOp + + got = (scope\get 'test')\const! + assert.is.equal TestOp, got T.opdef + + test 'Scopes', -> + sub = Scope! + scope\set_raw 'sub', sub + + got = (scope\get 'sub')\const! + assert.is.equal sub, got T.scope + + test 'tables', -> + pi = Constant.num 3.14 + scope\set_raw 'math', { :pi } + + got = (scope\get 'math')\const! + assert.is.equal T.scope, got.type + assert.is.equal Scope, got.value.__class + assert.is.equal pi, (got.value\get 'pi')\const! + assert.is.equal pi, (scope\get 'math/pi')\const! + + it 'stringifies well', -> + scope = Scope.from_table { + num: 3 + str: "im a happy string" + :pi + math: :pi + test: TestOp + } + sub = Scope Scope scope + + assert.is.equal "", tostring sub + assert.is.equal "", tostring scope + scope\set_raw 'a', Constant.num 1 + scope\set_raw 'b', Constant.num 2 + scope\set_raw 'c', Constant.num 3 + assert.is.equal "", tostring scope + + it 'wraps Values in from_table', -> + pi = Constant.num 3.14 + scope = Scope.from_table { + num: 3 + str: "im a happy string" + :pi + math: :pi + test: TestOp + } + + got = (scope\get 'num')\const! + assert.is.equal 3, got T.num + + got = (scope\get 'str')\const! + assert.is.equal "im a happy string", got T.str + + assert.is.equal pi, (scope\get 'pi')\const! + + got = (scope\get 'test')\const! + assert.is.equal TestOp, got T.opdef + + got = (scope\get 'math')\const! + assert.is.equal T.scope, got.type + assert.is.equal pi, (scope\get 'math/pi')\const! + + it 'gets from nested scopes', -> + root = Scope! + a = Scope! + b = Scope! + + pi = Constant.num 3.14 + b\set_raw 'test', pi + a\set_raw 'child', b + root\set_raw 'deep', a + + assert.is.equal pi, (root\get 'deep/child/test')\const! + + describe 'can set symbols', -> + one = wrap_res Constant.num 1 + two = wrap_res Constant.num 2 + scope = Scope! + + it 'disallows re-setting symbols', -> + scope\set 'test', one + assert.is.equal one, scope\get 'test' + + it 'throws if overwriting', -> + assert.has.error -> scope\set 'test', two + assert.is.equal one, scope\get 'test' + + describe 'inheritance', -> + root = Scope! + root\set_raw 'hidden', 1234 + root\set_raw 'inherited', "inherited string" + + scope = Scope root + + it 'allows access', -> + got = (scope\get 'inherited')\const! + assert.is.equal "inherited string", got T.str + + it 'can be shadowed', -> + scope\set_raw 'hidden', "overwritten" + + got = (scope\get 'hidden')\const! + assert.is.equal "overwritten", got T.str + + describe 'dynamic inheritance', -> + root = Scope! + dyn_root = Scope! + + root\set_raw 'normal', 'normal' + root\set_raw '*dynamic*', 'normal' + dyn_root\set_raw 'normal', 'dynamic' + dyn_root\set_raw '*dynamic*', 'dynamic' + + dyn_root\set_raw '*nested*', { value: 3 } + + it 'follows a different parent', -> + merged = Scope root, dyn_root + assert.is.equal 'normal', (merged\get 'normal').result! + assert.is.equal 'dynamic', (merged\get '*dynamic*').result! + + it 'falls back to the immediate parent', -> + merged = Scope root + assert.is.equal 'normal', (merged\get '*dynamic*').result! + + it 'looks in self first', -> + merged = Scope root + merged\set_raw '*dynamic*', 'merged' + assert.is.equal 'merged', (merged\get '*dynamic*').result! + + it 'can resolve nested', -> + merged = Scope root, dyn_root + assert.is.equal 3, (merged\get '*nested*/value').result! diff --git a/spec/internal/tag_spec.moon b/spec/internal/tag_spec.moon new file mode 100644 index 0000000..f92dca0 --- /dev/null +++ b/spec/internal/tag_spec.moon @@ -0,0 +1,91 @@ +import do_setup from require 'spec.test_setup' +import Tag from require 'alv.tag' +import Registry from require 'alv.registry' + +setup do_setup + +describe 'Tag', -> + describe 'should be constructable', -> + it 'by parsing', -> + tag = Tag.parse '2' + assert tag + assert.is.equal 2, tag.value + assert.is.equal '[2]', tag\stringify! + assert.is.equal '2', tostring tag + + it 'as blank Tags', -> + tag = Tag.blank! + assert tag + assert.is.nil tag.value + assert.is.equal '', tag\stringify! + assert.is.equal '?', tostring tag + + describe 'should be clonable', -> + do_asserts = (tag, expect) -> + assert tag + assert.is.nil tag.value + assert.is.equal expect, tostring tag + assert.has.error tag\stringify + + it 'from parsed tags', -> + parent = Tag.parse '1' + original = Tag.parse '2' + tag = original\clone parent + do_asserts tag, '1.2' + + it 'but not from blank tags', -> + parent = Tag.parse '1' + original = Tag.blank! + tag = original\clone parent + do_asserts tag, '1.?' + + it 'with blank parent', -> + parent = Tag.blank! + original = Tag.parse '2' + tag = original\clone parent + do_asserts tag, '?.2' + + it 'completely blank', -> + parent = Tag.blank! + original = Tag.blank! + tag = original\clone parent + do_asserts tag, '?.?' + + describe 'should be set-able', -> + it 'only if blank', -> + tag = Tag.parse '42' + assert.has.error -> tag\set 43 + + clone = tag\clone Tag.parse '3' + assert.has.error -> clone\set 42 + + clone = tag\clone Tag.blank! + assert.has.error -> clone\set 42 + + it 'and stores the value', -> + blank = Tag.blank! + blank\set 12 + + assert.is.equal blank.value, 12 + + it 'sets the original if cloned', -> + original = Tag.blank! + parent = Tag.parse '7' + + o_set = spy.on original, 'set' + p_set = spy.on parent, 'set' + + clone = original\clone parent + clone\set 11 + + assert.spy(o_set).was_called_with (match.is_ref original), 11 + assert.spy(p_set).was_not_called! + + assert.is.equal original.value, 11 + + it 'requires the parent to be registered if cloned', -> + original = Tag.blank! + parent = Tag.blank! + + clone = original\clone parent + assert.has.error -> clone\set 11 diff --git a/spec/internal/type_spec.moon b/spec/internal/type_spec.moon new file mode 100644 index 0000000..727e90b --- /dev/null +++ b/spec/internal/type_spec.moon @@ -0,0 +1,175 @@ +require 'spec.test_setup' +import T, Type, Primitive, Array, Struct from require 'alv.type' +import ancestor from require 'alv.util' + +bool = Primitive 'bool' +num = Primitive 'num' +str = Primitive 'str' + +describe 'Primitive', -> + it 'inherits from Type', -> + assert.is.equal Type, ancestor Primitive.__class + + it 'exposes name', -> + assert.is.equal 'bool', bool.name + assert.is.equal 'num', num.name + assert.is.equal 'str', str.name + + it 'stringifies well', -> + assert.is.equal 'bool', tostring bool + assert.is.equal 'num', tostring num + assert.is.equal 'str', tostring str + + it 'implements __eq sensibly', -> + assert.is.equal (Primitive 'bool'), bool + assert.is.equal (Primitive 'num'), num + assert.is.equal (Primitive 'str'), str + assert.not.equal num, bool + assert.not.equal str, num + + it ':pp pretty-prints values', -> + assert.is.equal 'true', bool\pp true + assert.is.equal 'false', bool\pp false + assert.is.equal 'nil', bool\pp nil + assert.is.equal '4.134', num\pp 4.134 + assert.is.equal '"hello"', str\pp "hello" + + it ':eq compares values', -> + a, b = {}, {} + assert.is.true bool\eq true, true + assert.is.false bool\eq true, false + assert.is.true num\eq 1, 1 + assert.is.false num\eq 1, -1 + assert.is.true num\eq a, a + assert.is.false num\eq a, b + + it ':get errors', -> + assert.has.error -> bool\get 'hello' + assert.has.error -> bool\get 2 + assert.has.error -> bool\get! + +describe 'Array', -> + vec3 = Array 3, num + str32 = Array 2, Array 3, str + + it 'inherits from Type', -> + assert.is.equal Type, ancestor Array.__class + + it 'exposes size', -> + assert.is.equal 3, vec3.size + assert.is.equal 2, str32.size + + it 'exposes inner type', -> + assert.is.equal num, vec3.type + assert.is.equal (Array 3, str), str32.type + + it 'stringifies well', -> + assert.is.equal 'num[3]', tostring vec3 + assert.is.equal 'my-type[3][24]', tostring Array 24, Array 3, 'my-type' + + it 'implements __eq sensibly', -> + assert.is.equal vec3, Array 3, num + assert.not.equal vec3, Array 2, num + assert.not.equal vec3, Array 3, str + + it ':pp pretty-prints values', -> + assert.is.equal '[1 2 3]', vec3\pp { 1, 2, 3 } + assert.is.equal '[["a" "b" "c"] ["d" "e" "f"]]', + str32\pp { {'a', 'b', 'c'}, {'d', 'e', 'f'} } + + it ':eq compares values', -> + a, b, c = {1, 2, 3}, {1, 2, 3}, {} + assert.is.true vec3\eq a, a + assert.is.true vec3\eq a, b + assert.is.false vec3\eq a, {1, 2, 4} + assert.is.true vec3\eq {1, 2, c}, {1, 2, c} + assert.is.false vec3\eq {1, 2, c}, {1, 2, {}} + assert.is.true str32\eq { {'a', 'b', 'c'}, {'d', 'e', 'f'} }, + { {'a', 'b', 'c'}, {'d', 'e', 'f'} } + assert.is.false str32\eq { {'a', 'b', 'c'}, {'d', 'e', 'f'} }, + { {'a', 'b', 'c'}, {'d', 'e', 'g'} } + + it ':get verifies size range', -> + assert.has.error -> vec3\get -1 + assert.is.equal num, vec3\get 0 + assert.is.equal num, vec3\get 1 + assert.is.equal num, vec3\get 2 + assert.has.error -> vec3\get 3 + + assert.is.equal (Array 3, str), str32\get 1 + + assert.has.error -> vec3\get! + assert.has.error -> vec3\get 'fail' + +describe 'Struct', -> + play = Struct { note: str, dur: num } + abc = Struct { c: num, b: num, a: num } + + it 'inherits from Type', -> + assert.is.equal Type, ancestor Struct.__class + + it 'exposes inner types', -> + assert.is.same { note: str, dur: num }, play.types + assert.is.same { c: num, b: num, a: num }, abc.types + + it 'stringifies well', -> + assert.is.equal '{dur: num note: str}', tostring play + assert.is.equal '{a: num b: num c: num}', tostring abc + + it 'implements __eq sensibly', -> + assert.is.equal play, Struct { note: str, dur: num } + assert.not.equal play, Struct { note: str } + assert.not.equal play, Struct { note: str, dur: str } + assert.not.equal play, Struct { note: str, dur: num, extra: num } + + it ':pp pretty-prints values', -> + assert.is.equal '{dur: 0.5 note: "a"}', play\pp { dur: 0.5, note: 'a' } + assert.is.equal '{a: 1 b: 2 c: 3}', abc\pp { a: 1, b: 2, c: 3 } + + it ':eq compares values', -> + a, b, c = { dur: 0.5, note: 'a' }, { dur: 0.5, note: 'a' }, {} + assert.is.true play\eq a, a + assert.is.true play\eq a, b + assert.is.false play\eq a, { dur: 0.5, note: 'b' } + assert.is.true play\eq { dur: 0.5, note: c }, { dur: 0.5, note: c } + assert.is.false play\eq { dur: 0.5, note: c }, { dur: 0.5, note: {} } + + it ':get verifies key exists', -> + assert.is.equal str, play\get 'note' + assert.is.equal num, play\get 'dur' + assert.has.error -> play\get! + assert.has.error -> play\get '' + assert.has.error -> play\get 'something' + +describe 'T', -> + it 'provides shorthand for Primitives', -> + assert.is.equal num, T.num + assert.is.equal str, T.str + assert.is.equal (Primitive 'midi/evt'), T['midi/evt'] + +for type in *{num, str, (Array 3, num)} + describe "#{type}", -> + describe ':mk_sig', -> + it 'can create value-less Streams', -> + stream = type\mk_sig! + assert.is.equal 'SigStream', stream.__class.__name + assert.is.equal type, stream.type + assert.is.nil stream! + + it 'can take an initial value', -> + stream = type\mk_sig 4 + assert.is.equal 'SigStream', stream.__class.__name + assert.is.equal type, stream.type + assert.is.equal 4, stream! + + describe ':mk_const', -> + it 'takes a value', -> + stream = type\mk_const 4 + assert.is.equal 'Constant', stream.__class.__name + assert.is.equal type, stream.type + assert.is.equal 4, stream! + + describe ':mk_evt', -> + stream = type\mk_evt! + assert.is.equal 'EvtStream', stream.__class.__name + assert.is.equal type, stream.type diff --git a/spec/lang/array_spec.moon b/spec/lang/array_spec.moon deleted file mode 100644 index b55cec6..0000000 --- a/spec/lang/array_spec.moon +++ /dev/null @@ -1,148 +0,0 @@ -import TestPilot from require 'spec.test_setup' -import T, Array from require 'alv' - -describe "array", -> - test = TestPilot '', '(import* array-)\n' - - svec3 = Array 3, T.str - - it "can contain any type", -> - COPILOT\eval_once '(array 1 2 3)' - COPILOT\eval_once '(array true false)' - COPILOT\eval_once '(array "a")' - COPILOT\eval_once '(array (array 1 2) (array 3 4))' - - it "cannot contain mixed types", -> - err = assert.has.error -> COPILOT\eval_once '(array 1 false)' - assert.matches "argument error: couldn't match arguments", err - - describe "(set)", -> - it "can swap values", -> - rt = COPILOT\eval_once '(set (array "f" "b" "c") 0 "a")' - assert.is.true rt\is_const! - assert.is.equal svec3\mk_const({ 'a', 'b', 'c' }), rt.result - - it "checks value type", -> - err = assert.has.error -> COPILOT\eval_once '(set (array 1) 0 "a")' - assert.matches "expected value of type num, not str", err - - it "checks index range", -> - err = assert.has.error -> COPILOT\eval_once '(set (array 1 2) -1 0)' - assert.matches "index '%-1' out of range!", err - - COPILOT\eval_once '(set (array 1 2) 0 0)' - - COPILOT\eval_once '(set (array 1 2) 1 0)' - - err = assert.has.error -> COPILOT\eval_once '(set (array 1 2) 2 0)' - assert.matches "index '2' out of range!", err - - describe "(get)", -> - it "can get a value", -> - rt = COPILOT\eval_once '(get (array 1 2) 0)' - assert.is.true rt\is_const! - assert.is.equal '', tostring rt.result - - it "checks index range", -> - err = assert.has.error -> COPILOT\eval_once '(get (array 1 2) -1)' - assert.matches "index '%-1' out of range!", err - - COPILOT\eval_once '(get (array 1 2) 0)' - - COPILOT\eval_once '(get (array 1 2) 1)' - - err = assert.has.error -> COPILOT\eval_once '(get (array 1 2) 2)' - assert.matches "index '2' out of range!", err - - describe '(head)', -> - it "can peek a value", -> - rt = COPILOT\eval_once '(head (array 1 2))' - assert.is.true rt\is_const! - assert.is.equal '', tostring rt.result - - describe '(tail)', -> - it "gets rest of an array", -> - rt = COPILOT\eval_once '(tail (array 1))' - assert.is.true rt\is_const! - assert.is.same (Array 0, T.num), rt.result.type - assert.is.same {}, rt.result! - - rt = COPILOT\eval_once '(tail (array 1 2))' - assert.is.true rt\is_const! - assert.is.same (Array 1, T.num), rt.result.type - assert.is.same { 2 }, rt.result! - - rt = COPILOT\eval_once '(tail (array 1 2 3 4))' - assert.is.true rt\is_const! - assert.is.same (Array 3, T.num), rt.result.type - assert.is.same { 2, 3, 4 }, rt.result! - - describe '(prepend)', -> - it "prepends to array", -> - rt = COPILOT\eval_once '(prepend (array 2) 1)' - assert.is.true rt\is_const! - assert.is.same (Array 2, T.num), rt.result.type - assert.is.same { 1, 2 }, rt.result! - - rt = COPILOT\eval_once '(prepend (array 2 3 4) 1)' - assert.is.true rt\is_const! - assert.is.same (Array 4, T.num), rt.result.type - assert.is.same { 1, 2, 3, 4 }, rt.result! - - describe "(insert)", -> - it "can insert a value", -> - rt = COPILOT\eval_once '(insert (array "b" "c") 0 "a")' - assert.is.true rt\is_const! - assert.is.equal svec3\mk_const({ 'a', 'b', 'c' }), rt.result - - rt = COPILOT\eval_once '(insert (array "a" "c") 1 "b")' - assert.is.true rt\is_const! - assert.is.equal svec3\mk_const({ 'a', 'b', 'c' }), rt.result - - rt = COPILOT\eval_once '(insert (array "a" "b") 2 "c")' - assert.is.true rt\is_const! - assert.is.equal svec3\mk_const({ 'a', 'b', 'c' }), rt.result - - it "checks index range", -> - err = assert.has.error -> COPILOT\eval_once '(insert (array 1 2) -1 0)' - assert.matches "index '%-1' out of range!", err - - COPILOT\eval_once '(insert (array 1 2) 0 0)' - - COPILOT\eval_once '(insert (array 1 2) 1 0)' - - COPILOT\eval_once '(insert (array 1 2) 2 0)' - - err = assert.has.error -> COPILOT\eval_once '(insert (array 1 2) 3 0)' - assert.matches "index '3' out of range!", err - - describe "(remove)", -> - it "can remove a value", -> - rt = COPILOT\eval_once '(remove (array "d" "a" "b" "c") 0)' - assert.is.true rt\is_const! - assert.is.equal svec3\mk_const({ 'a', 'b', 'c' }), rt.result - - rt = COPILOT\eval_once '(remove (array "a" "b" "c" "d") 3)' - assert.is.true rt\is_const! - assert.is.equal svec3\mk_const({ 'a', 'b', 'c' }), rt.result - - it "checks index range", -> - err = assert.has.error -> COPILOT\eval_once '(remove (array 1 2 3) -1)' - assert.matches "index '%-1' out of range!", err - - err = assert.has.error -> COPILOT\eval_once '(remove (array 1 2 3) 3)' - assert.matches "index '3' out of range!", err - - it "can be concatenated with (concat)", -> - rt = COPILOT\eval_once '(concat (array "a" "b") (array "c"))' - assert.is.true rt\is_const! - assert.is.equal svec3\mk_const({ 'a', 'b', 'c' }), rt.result - - it "size can be read using (size)", -> - rt = COPILOT\eval_once '(size (array 1))' - assert.is.true rt\is_const! - assert.is.equal '', tostring rt.result - - rt = COPILOT\eval_once '(size (array 1 2 3))' - assert.is.true rt\is_const! - assert.is.equal '', tostring rt.result diff --git a/spec/lang/cond_spec.moon b/spec/lang/cond_spec.moon deleted file mode 100644 index 89f7c7e..0000000 --- a/spec/lang/cond_spec.moon +++ /dev/null @@ -1,92 +0,0 @@ -import TestPilot from require 'spec.test_setup' -import T, Struct, Array, Constant from require 'alv' - -describe "if", -> - COPILOT = TestPilot! - - it "checks truthiness", -> - for truthy in *{'true', '1', '-1', '1234', '(array 1 2 3)', '"test"', '""'} - with COPILOT\eval_once "(if #{truthy} 'yes' 'no')" - assert.is.true \is_const! - assert.is.equal 'yes', .result! - - for falsy in *{'false', '0'} - with COPILOT\eval_once "(if #{falsy} 'yes' 'no')" - assert.is.true \is_const! - assert.is.equal 'no', .result! - - it "can be used without else clause", -> - with COPILOT\eval_once '(if true "yes")' - assert.is.true \is_const! - assert.is.equal 'yes', .result! - - with COPILOT\eval_once '(if false "yes")' - assert.is.true \is_const! - assert.is.equal nil, .result - - it "doesn't evaluate the untaken branch", -> - with COPILOT\eval_once '(if true "yes" (error/doesnt-exist))' - assert.is.true \is_const! - assert.is.equal 'yes', .result! - - with COPILOT\eval_once '(if false (error/doesnt-exist) "no")' - assert.is.true \is_const! - assert.is.equal 'no', .result! - - it "errors on non-const choice", -> - err = assert.has.error -> - COPILOT\eval_once '(import* time) (if (ramp 1) "yes" "no")' - assert.matches "'if'%-expression needs to be constant", err - - it "forwards any result", -> - with COPILOT\eval_once ' - (import* time) - (if true (every 1 (array 1 2 3)))' - assert.is.false \is_const! - assert.is.equal '', tostring .result - -describe "when", -> - COPILOT = TestPilot! - - it "checks truthiness", -> - for truthy in *{'true', '1', '-1', '1234', '(array 1 2 3)', '"test"', '""'} - with COPILOT\eval_once "(when #{truthy} 'yes')" - assert.is.true \is_const! - assert.is.equal 'yes', .result! - - for falsy in *{'false', '0'} - with COPILOT\eval_once "(when #{falsy} 'yes')" - assert.is.true \is_const! - assert.is.nil .result - - it "doesn't evaluate if falsy", -> - with COPILOT\eval_once '(when false (error/doesnt-exist))' - assert.is.true \is_const! - assert.is.nil, .result - - with COPILOT\eval_once ' - (when false - (error/doesnt-exist) - (error/doesnt-exist) - (error/doesnt-exist))' - assert.is.true \is_const! - assert.is.nil, .result - - it "errors on non-const choice", -> - err = assert.has.error -> - COPILOT\eval_once '(import* time) (when (ramp 1) 1 2 3)' - assert.matches "'when'%-expression needs to be constant", err - - it "forwards any result", -> - with COPILOT\eval_once ' - (import* time) - (when true - (every 1 (array 1 2 3)) - 1 2 3)' - assert.is.false \is_const! - assert.is.equal '', tostring .result - - with COPILOT\eval_once '(when true (array 1 2 3))' - assert.is.true \is_const! - assert.is.equal '', tostring .result - diff --git a/spec/lang/do_spec.moon b/spec/lang/do_spec.moon deleted file mode 100644 index fd2825b..0000000 --- a/spec/lang/do_spec.moon +++ /dev/null @@ -1,27 +0,0 @@ -import TestPilot from require 'spec.test_setup' - -describe "do", -> - COPILOT = TestPilot! - - it "can be empty", -> - with COPILOT\eval_once '(do)' - assert.is.true \is_const! - assert.is.nil .result - - it "returns the last result, if any", -> - with COPILOT\eval_once '(do 1 2 3)' - assert.is.true \is_const! - assert.is.equal '', tostring .result - - with COPILOT\eval_once '(do 1 2 (def _ 3))' - assert.is.true \is_const! - assert.is.nil .result - - it "passes through side-effects", -> - with COPILOT\eval_once ' - (import* time) - (do - (every 0.5 "bang! side-effect") - 3)' - assert.is.false \is_const! - assert.is.equal '', tostring .result diff --git a/spec/lang/fn_spec.moon b/spec/lang/fn_spec.moon deleted file mode 100644 index af27cf3..0000000 --- a/spec/lang/fn_spec.moon +++ /dev/null @@ -1,51 +0,0 @@ -import TestPilot from require 'spec.test_setup' -import T, Struct, Array, Constant from require 'alv' - -describe "function", -> - COPILOT = TestPilot! - - it "returns constant results when constant", -> - rt = COPILOT\eval_once ' - (import* math) - - (defn my-plus (a b) - (+ a b)) - - (my-plus 2 3)' - assert.is.true rt\is_const! - assert.is.equal (Constant.num 5), rt.result - - it "checks argument arity when invoked", -> - err = assert.has.error -> COPILOT\eval_once ' - ([1]import* math) - - ([2]defn my-plus (a b) - ([4]+ a b)) - - ([3]my-plus 2)' - assert.matches "argument error: expected 2 arguments, found 1", err - assert.matches "while invoking function 'my%-plus' at %[3%]", err - - err = assert.has.error -> COPILOT\eval_once ' - ([1]import* math) - - ([2]defn my-plus (a b) - ([4]+ a b)) - - ([3]my-plus 2 3 4)' - assert.matches "argument error: expected 2 arguments, found 3", err - assert.matches "while invoking function 'my%-plus' at %[3%]", err - - it "can be anonymously invoked", -> - rt = COPILOT\eval_once ' - ([1] - ([2]fn (a b) b) - 3 4)' - assert.is.equal (Constant.num 4), rt\const! - - err = assert.has.error -> COPILOT\eval_once ' - ([1] - ([2]fn (a b) b) - 3)' - assert.matches "argument error: expected 2 arguments, found 1", err - assert.matches "while invoking function %(unnamed%) at %[1%]", err diff --git a/spec/lang/literal_spec.moon b/spec/lang/literal_spec.moon deleted file mode 100644 index 3db010d..0000000 --- a/spec/lang/literal_spec.moon +++ /dev/null @@ -1,33 +0,0 @@ -import TestPilot from require 'spec.test_setup' -import T, Struct, Array, Constant from require 'alv' - -describe "literal", -> - TestPilot ' - (def str "hello" - num 2 - bool true - curl ([5]struct "a" 2 "b" false) - sqre ([7]array 1 2 3 4)) - (export*)' - - assert.is.true COPILOT.active_module.root\is_const! - scope = (assert COPILOT.active_module.root.result)\unwrap T.scope - - it "string is parsed and returned correctly", -> - assert.is.equal (Constant.str 'hello'), (scope\get 'str')\const! - - it "number is parsed and returned correctly", -> - assert.is.equal (Constant.num 2), (scope\get 'num')\const! - - it "boolean is parsed and returned correctly", -> - assert.is.equal (Constant.bool true), (scope\get 'bool')\const! - - it "struct is parsed and returned correctly", -> - struct = (scope\get 'curl')\const! - assert.is.equal (Struct a: T.num, b: T.bool), struct.type - assert.is.same { a: 2, b: false }, struct! - - it "array is parsed and returned correctly", -> - array = (scope\get 'sqre')\const! - assert.is.equal (Array 4, T.num), array.type - assert.is.same {1, 2, 3, 4}, array! diff --git a/spec/lang/logic_spec.moon b/spec/lang/logic_spec.moon deleted file mode 100644 index f3d0610..0000000 --- a/spec/lang/logic_spec.moon +++ /dev/null @@ -1,264 +0,0 @@ -import TestPilot from require 'spec.test_setup' -import T, Array, Constant from require 'alv' - -describe "logic", -> - test = TestPilot '', '(import* logic)\n' - TRUE = T.bool\mk_const true - FALSE = T.bool\mk_const false - - describe "==", -> - it "can compare any type", -> - with COPILOT\eval_once '(== 1 1)' - assert.is.true \is_const! - assert.is.equal TRUE, .result - - with COPILOT\eval_once '(== 1 2)' - assert.is.true \is_const! - assert.is.equal FALSE, .result - - with COPILOT\eval_once '(== 1 "hello")' - assert.is.true \is_const! - assert.is.equal FALSE, .result - - with COPILOT\eval_once '(== "hello" "hello")' - assert.is.true \is_const! - assert.is.equal TRUE, .result - - with COPILOT\eval_once '(== (array 1 2 3) (array 1 2 3))' - assert.is.true \is_const! - assert.is.equal TRUE, .result - - with COPILOT\eval_once '(== (array 1 2 3) (array 1 2 1))' - assert.is.true \is_const! - assert.is.equal FALSE, .result - - with COPILOT\eval_once '(== (array 1 2 3) (array 1 2))' - assert.is.true \is_const! - assert.is.equal FALSE, .result - - with COPILOT\eval_once '(== (array 1 2 3) (array 1 2 3 4))' - assert.is.true \is_const! - assert.is.equal FALSE, .result - - with COPILOT\eval_once '(== - (struct "a" 1 "b" true "c" (array "test")) - (struct "a" 1 "b" true "c" (array "test")))' - assert.is.true \is_const! - assert.is.equal TRUE, .result - - with COPILOT\eval_once '(== - (struct "a" 1 "b" false "c" (array "test")) - (struct "a" 1 "b" true "c" (array "test")))' - assert.is.true \is_const! - assert.is.equal FALSE, .result - - with COPILOT\eval_once '(== - (struct "a" 1 "b" true "c" (array "test" "toast")) - (struct "a" 1 "b" true "c" (array "test")))' - assert.is.true \is_const! - assert.is.equal FALSE, .result - - with COPILOT\eval_once '(== - (struct "a" 1 "b" true) - (struct "a" 1 "b" true "c" (array "test")))' - assert.is.true \is_const! - assert.is.equal FALSE, .result - - with COPILOT\eval_once '(== - (struct "a" 1 "b" true) - (struct "a" 1))' - assert.is.true \is_const! - assert.is.equal FALSE, .result - - with COPILOT\eval_once '(== print print)' - assert.is.true \is_const! - assert.is.equal TRUE, .result - - with COPILOT\eval_once '(== print ==)' - assert.is.true \is_const! - assert.is.equal FALSE, .result - - it "is aliased as eq", -> - with COPILOT\eval_once '(== eq ==)' - assert.is.true \is_const! - assert.is.equal TRUE, .result - - describe "!=", -> - it "can compare any type", -> - with COPILOT\eval_once '(!= 1 1)' - assert.is.true \is_const! - assert.is.equal FALSE, .result - - with COPILOT\eval_once '(!= 1 2)' - assert.is.true \is_const! - assert.is.equal TRUE, .result - - with COPILOT\eval_once '(!= 1 "hello")' - assert.is.true \is_const! - assert.is.equal TRUE, .result - - with COPILOT\eval_once '(!= "hello" "hello")' - assert.is.true \is_const! - assert.is.equal FALSE, .result - - with COPILOT\eval_once '(!= (array 1 2 3) (array 1 2 3))' - assert.is.true \is_const! - assert.is.equal FALSE, .result - - with COPILOT\eval_once '(!= (array 1 2 3) (array 1 2 1))' - assert.is.true \is_const! - assert.is.equal TRUE, .result - - with COPILOT\eval_once '(!= (array 1 2 3) (array 1 2))' - assert.is.true \is_const! - assert.is.equal TRUE, .result - - with COPILOT\eval_once '(!= (array 1 2 3) (array 1 2 3 4))' - assert.is.true \is_const! - assert.is.equal TRUE, .result - - with COPILOT\eval_once '(!= - (struct "a" 1 "b" true "c" (array "test")) - (struct "a" 1 "b" true "c" (array "test")))' - assert.is.true \is_const! - assert.is.equal FALSE, .result - - with COPILOT\eval_once '(!= - (struct "a" 1 "b" false "c" (array "test")) - (struct "a" 1 "b" true "c" (array "test")))' - assert.is.true \is_const! - assert.is.equal TRUE, .result - - with COPILOT\eval_once '(!= - (struct "a" 1 "b" true "c" (array "test" "toast")) - (struct "a" 1 "b" true "c" (array "test")))' - assert.is.true \is_const! - assert.is.equal TRUE, .result - - with COPILOT\eval_once '(!= - (struct "a" 1 "b" true) - (struct "a" 1 "b" true "c" (array "test")))' - assert.is.true \is_const! - assert.is.equal TRUE, .result - - with COPILOT\eval_once '(!= - (struct "a" 1 "b" true) - (struct "a" 1))' - assert.is.true \is_const! - assert.is.equal TRUE, .result - - with COPILOT\eval_once '(!= print print)' - assert.is.true \is_const! - assert.is.equal FALSE, .result - - with COPILOT\eval_once '(!= print ==)' - assert.is.true \is_const! - assert.is.equal TRUE, .result - - it "is aliased as note-eq", -> - with COPILOT\eval_once '(== not-eq !=)' - assert.is.true \is_const! - assert.is.equal TRUE, .result - - describe "bool", -> - it "coerces numbers", -> - with COPILOT\eval_once '(bool 0)' - assert.is.true \is_const! - assert.is.equal FALSE, .result - - with COPILOT\eval_once '(bool 1)' - assert.is.true \is_const! - assert.is.equal TRUE, .result - - with COPILOT\eval_once '(bool -1)' - assert.is.true \is_const! - assert.is.equal TRUE, .result - - with COPILOT\eval_once '(bool 1024)' - assert.is.true \is_const! - assert.is.equal TRUE, .result - - it "accepts booleans", -> - with COPILOT\eval_once '(bool false)' - assert.is.true \is_const! - assert.is.equal FALSE, .result - - with COPILOT\eval_once '(bool true)' - assert.is.true \is_const! - assert.is.equal TRUE, .result - - describe "not", -> - it "accepts booleans", -> - with COPILOT\eval_once '(not false)' - assert.is.true \is_const! - assert.is.equal TRUE, .result - - with COPILOT\eval_once '(not true)' - assert.is.true \is_const! - assert.is.equal FALSE, .result - - it "coerces numbers", -> - with COPILOT\eval_once '(not 0)' - assert.is.true \is_const! - assert.is.equal TRUE, .result - - with COPILOT\eval_once '(not 1)' - assert.is.true \is_const! - assert.is.equal FALSE, .result - - with COPILOT\eval_once '(not -1)' - assert.is.true \is_const! - assert.is.equal FALSE, .result - - with COPILOT\eval_once '(not 1024)' - assert.is.true \is_const! - assert.is.equal FALSE, .result - - - describe "or", -> - it "accepts any number of mixed arguments", -> - with COPILOT\eval_once '(or false 0)' - assert.is.true \is_const! - assert.is.equal FALSE, .result - - with COPILOT\eval_once '(or 1 0)' - assert.is.true \is_const! - assert.is.equal TRUE, .result - - with COPILOT\eval_once '(or 0 false 0 0 0 0)' - assert.is.true \is_const! - assert.is.equal FALSE, .result - - with COPILOT\eval_once '(or 0 0 0 true 0 0)' - assert.is.true \is_const! - assert.is.equal TRUE, .result - - with COPILOT\eval_once '(or 0 true 0 1 0 0)' - assert.is.true \is_const! - assert.is.equal TRUE, .result - - describe "and", -> - it "accepts any number of mixed arguments", -> - with COPILOT\eval_once '(and false 1)' - assert.is.true \is_const! - assert.is.equal FALSE, .result - - with COPILOT\eval_once '(and 1 true)' - assert.is.true \is_const! - assert.is.equal TRUE, .result - - with COPILOT\eval_once '(and false 0)' - assert.is.true \is_const! - assert.is.equal FALSE, .result - - with COPILOT\eval_once '(and 1 1 true 0)' - assert.is.true \is_const! - assert.is.equal FALSE, .result - - with COPILOT\eval_once '(and 1 1 true true 1)' - assert.is.true \is_const! - assert.is.equal TRUE, .result - - with COPILOT\eval_once '(and 1 1 1)' - assert.is.true \is_const! - assert.is.equal TRUE, .result diff --git a/spec/lang/math_spec.moon b/spec/lang/math_spec.moon deleted file mode 100644 index 23a353b..0000000 --- a/spec/lang/math_spec.moon +++ /dev/null @@ -1,267 +0,0 @@ -import TestPilot from require 'spec.test_setup' -import T, Array, Constant from require 'alv' - -describe_both = (fn) -> - describe "math", -> - test = TestPilot '', '(import* testing math)\n' - fn! - - describe "math-simple", -> - test = TestPilot '', '(import* testing math-simple)\n' - fn! - -describe_both -> - describe "add, sub, mul, div, pow, mod", -> - it "are aliased as +-*/^%", -> - COPILOT\eval_once ' - (expect= + add) - (expect= - sub) - (expect= * mul) - (expect= / div) - (expect= ^ pow) - (expect= % mod) - ' - - it "are sane", -> - COPILOT\eval_once ' - (expect= 2 (+ 1 1)) - (expect= 6 (+ 2 3 0 1)) - - (expect= -5 (- 2 7)) - (expect= 0 (- 10 4 3 2 1 0)) - (expect= -10 (- 10)) - - (expect= 1 (* 1 1)) - (expect= -2 (* -1 2)) - (expect= 14 (* 4 0.5 7)) - - (expect= 1 (/ 4 4)) - (expect= -2 (/ -10 5)) - (expect= 3 (/ -30 -10)) - - (expect= 1024 (^ 2 10)) - (expect= 0.25 (^ 4 -1)) - (expect= 1 (^ 999 0)) - - (expect= 4 (% 10 6)) - (expect= 3 (% -2 5)) - (expect= -2 (% -2 -5)) - ' - - describe "trigonometric functions and constants", -> - COPILOT\eval_once ' - (expect= tau (* pi 2)) - - #(darn you fp accuracy! - (expect= 0.5 (asin (sin 0.5))) - (expect= 0.5 (acos (cos 0.5))) - ...) - ' - - it "min, max, clamp, huge", -> - COPILOT\eval_once ' - (expect= 0 (min 0 1 2)) - (expect= 2 (max 0 1 2)) - - (expect= -2 (clamp -2 3.5 -4)) - (expect= -1 (clamp -2 3.5 -1)) - (expect= 0 (clamp -2 3.5 0)) - (expect= 1 (clamp -2 3.5 1)) - (expect= 3.5 (clamp -2 3.5 4)) - - (expect= (- huge) (min 0 1 -123456789 (- huge))) - (expect= huge (max 0 1 123456789 huge)) - ' - - it "inc, dec", -> - COPILOT\eval_once ' - (expect= 1 (inc 0)) - (expect= -1 (dec 0)) - (expect= 3 (inc (inc 1))) - (expect= -1 (dec (dec 1))) - (expect= 0 (inc (dec 0)) (dec (inc 0))) - ' - -describe "math", -> - test = TestPilot '', '(import* testing math)\n' - - describe "add, sub, mul, div, pow, mod", -> - it "handle scalar/vector", -> - COPILOT\eval_once ' - (expect= (array 3 4 5) - (+ 1 (array 1 2 3) 1)) - - (expect= (array 0 1 2) - (- (array 1 2 3) 1)) - - (expect= (array 3 6 9) - (* 3 (array 1 2 3))) - (expect= (array 3 6 9) - (* (array 1 2 3) 3)) - - (expect= (array 12 9 4) - (/ 36 (array 3 4 9))) - - (expect= (array 9 16 25) - (^ (array 3 4 5) 2)) - (expect= (array 1 2 4 8) - (^ 2 (array 0 1 2 3))) - - (expect= (array 3 0 1) - (% (array 3 4 5) 4)) - ' - - it "handle vector/vector and matrix/matrix", -> - COPILOT\eval_once ' - (expect= (array 5 7 9) - (+ (array 1 2 3) - (array 4 5 6))) - - (expect= (array (array 11 12) - (array 13 14)) - (+ - (array (array 1 2) - (array 3 4)) - 5 - 5)) - - (expect= (array 2 0 -2) - (- (array 3 2 1) - (array 1 2 3))) - - (expect= (array 1 -2 -3) - (- (array -1 2 3))) - ' - - err = assert.has.error -> - COPILOT\eval_once ' - (+ (array 1 2 3) - (array 1 2))' - - err = assert.has.error -> - COPILOT\eval_once ' - (+ (array (array 1 2) (array 1 2)) - (array 1 2))' - - err = assert.has.error -> - COPILOT\eval_once ' - (- (array 1 2 3) - (array 1 2))' - - err = assert.has.error -> - COPILOT\eval_once ' - (- (array (array 1 2) (array 1 2)) - (array 1 2))' - - describe "mul", -> - it "handles scalars and matrices", -> - with COPILOT\eval_once ' - (* 3 - (array - (array 1 2) - (array 4 5)))' - assert.is.true \is_const! - assert.is.equal '', tostring .result - - with COPILOT\eval_once ' - (* (array - (array 1 2) - (array 4 5)) - 3)' - assert.is.true \is_const! - assert.is.equal '', tostring .result - - it "handles vectors and matrices", -> - with COPILOT\eval_once ' - (* - (array (array 1 0 0) - (array 0 1 0) - (array 0 0 1)) - (array 4 5 6))' - assert.is.true \is_const! - assert.is.equal '', tostring .result - - with COPILOT\eval_once ' - (* - (array (array 1 0 0) - (array 0 1 0) - (array 3 2 1)) - (array 4 5 1))' - assert.is.true \is_const! - assert.is.equal '', tostring .result - - it "handles matrices", -> - with COPILOT\eval_once ' - (* - (array (array 1 2 3) - (array 4 5 6)) - (array (array 10 11) - (array 20 21) - (array 30 31)))' - assert.is.true \is_const! - assert.is.equal '', tostring .result - - it "handles everything mixed", -> - with COPILOT\eval_once ' - (* - (array (array 1 2 3) - (array 4 5 6)) - 2 - (array (array 10 11) - (array 20 21) - (array 30 31)))' - assert.is.true \is_const! - assert.is.equal '', tostring .result - - with COPILOT\eval_once ' - (* - (array (array 1 2 3) - (array 4 5 6)) - 2 - (array (array 10 11) - (array 20 21) - (array 30 31)) - (array 4 7))' - assert.is.true \is_const! - assert.is.equal '', tostring .result - - it "errors with wrong sizes (matrix and vector)", -> - err = assert.has.error -> COPILOT\eval_once ' - (* - (array (array 1 2 3) - (array 4 5 6)) - (array 1 2))' - -- assert.matches "", err - - err = assert.has.error -> COPILOT\eval_once ' - (* - (array 1 2 3) - (array (array 1 2 3) - (array 4 5 6)))' - -- assert.matches "", err - - it "errors with wrong sizes (matrix)", -> - err = assert.has.error -> COPILOT\eval_once ' - (* - (array (array 1 2 3) - (array 4 5 6)) - (array (array 1 2) - (array 4 5)))' - -- assert.matches "", err - - it "min, max, clamp, huge", -> - COPILOT\eval_once ' - (expect= (array 3 2 1) - (min (array 3 4 1) (array 5 2 huge))) - (expect= (array 5 huge 4) - (max (array 3 huge 4) (array 5 999 2))) - - (expect= (array -2 -1 0 1 3.5) - (clamp -2 3.5 (array -4 -1 0 1 4))) - - (expect= 1 (inc 0)) - (expect= -1 (dec 0)) - (expect= 3 (inc (inc 1))) - (expect= -1 (dec (dec 1))) - (expect= 0 (inc (dec 0)) (dec (inc 0))) - ' diff --git a/spec/lang/struct_spec.moon b/spec/lang/struct_spec.moon deleted file mode 100644 index 9937f99..0000000 --- a/spec/lang/struct_spec.moon +++ /dev/null @@ -1,51 +0,0 @@ -import TestPilot from require 'spec.test_setup' -import T, Struct from require 'alv' - -describe "struct", -> - test = TestPilot '', '(import* struct-)\n' - - ab = Struct { a: T.num, b: T.bool } - - describe "(set)", -> - it "can update values", -> - rt = COPILOT\eval_once '(set (struct "a" 1 "b" false) "a" 2)' - assert.is.true rt\is_const! - assert.is.equal ab\mk_const({ a: 2, b: false }), rt.result - - it "cannot add members", -> - err = assert.has.error -> COPILOT\eval_once '(set (struct "a" 1) "b" 2)' - assert.matches "{a: num} has no 'b' key", err - - it "checks value type", -> - err = assert.has.error -> COPILOT\eval_once '(set (struct "a" 1) "a" "str")' - assert.matches "expected value for key 'a' to be num, not str", err - - describe "(get)", -> - it "can get values", -> - rt = COPILOT\eval_once '(get (struct "a" 1 "b" false) "a")' - assert.is.true rt\is_const! - assert.is.equal '', tostring rt.result - - it "checks keys", -> - err = assert.has.error -> COPILOT\eval_once '(get (struct "a" 1) "b")' - assert.matches "has no 'b' key", err - - describe "(insert)", -> - it "can add members", -> - rt = COPILOT\eval_once '(insert (struct "b" true) "a" 1)' - assert.is.true rt\is_const! - assert.is.equal ab\mk_const({ a: 1, b: true }), rt.result - - it "doesn't clobber existing members", -> - err = assert.has.error -> COPILOT\eval_once '(insert (struct "a" 1) "a" 2)' - assert.matches "key 'a' already exists in value of type {a: num}", err - - describe "(remove)", -> - it "can remove members", -> - rt = COPILOT\eval_once '(remove (struct "a" 1 "b" false "c" "abc") "c")' - assert.is.true rt\is_const! - assert.is.equal ab\mk_const({ a: 1, b: false }), rt.result - - it "checks keys", -> - err = assert.has.error -> COPILOT\eval_once '(remove (struct "a" 1) "b")' - assert.matches "has no 'b' key", err diff --git a/spec/lang/testing_spec.moon b/spec/lang/testing_spec.moon deleted file mode 100644 index 5be7848..0000000 --- a/spec/lang/testing_spec.moon +++ /dev/null @@ -1,80 +0,0 @@ -import TestPilot from require 'spec.test_setup' -import T, Array, Constant from require 'alv' - -describe "testing", -> - test = TestPilot '', '(import* testing logic)\n' - - describe "assert", -> - it "ignores true", -> - with COPILOT\eval_once '(assert true)' - assert.is.true \is_const! - assert.is.nil .result - - with COPILOT\eval_once '(assert true "with message")' - assert.is.true \is_const! - assert.is.nil .result - - it "throws on false", -> - assert.has.error -> COPILOT\eval_once '(assert false)' - - it "shows failing expression", -> - err = assert.has.error -> COPILOT\eval_once '(assert false)' - assert.matches "assertion failed: false", err - - err = assert.has.error -> COPILOT\eval_once '(assert (== 1 2))' - assert.matches "assertion failed: %(== 1 2%)", err - - it "supports custom error messages", -> - err = assert.has.error -> COPILOT\eval_once '(assert (== "green" "red") "duck isnt green")' - assert.matches "duck isnt green", err - - describe "expect=", -> - it "passes equal params", -> - with COPILOT\eval_once '(expect= 1 1 1)' - assert.is.true \is_const! - assert.is.nil .result - - with COPILOT\eval_once '(expect= 2 2 2)' - assert.is.true \is_const! - assert.is.nil .result - - with COPILOT\eval_once '(expect= true true)' - assert.is.true \is_const! - assert.is.nil .result - - with COPILOT\eval_once '(expect= "hello" "hello")' - assert.is.true \is_const! - assert.is.nil .result - - with COPILOT\eval_once '(expect= (array 1 2) (array 1 2))' - assert.is.true \is_const! - assert.is.nil .result - - it "fails different values", -> - assert.has.error -> COPILOT\eval_once '(expect= 1 2)' - assert.has.error -> COPILOT\eval_once '(expect= 1 1 2)' - assert.has.error -> COPILOT\eval_once '(expect= 2 1 1)' - - assert.has.error -> COPILOT\eval_once '(expect= true false)' - - assert.has.error -> COPILOT\eval_once '(expect= "asdf" "bsdf")' - assert.has.error -> COPILOT\eval_once '(expect= (array 1 2) (array 1 3))' - - it "fails different types", -> - assert.has.error -> COPILOT\eval_once '(expect= true 2)' - assert.has.error -> COPILOT\eval_once '(expect= true true 1)' - assert.has.error -> COPILOT\eval_once '(expect= true false "str")' - - it "reports first failing arguments", -> - err = assert.has.error -> COPILOT\eval_once '(expect= 1 2)' - assert.matches "assertion error: Expected 2 to equal %(got %)", err - - err = assert.has.error -> COPILOT\eval_once '(expect= 1 1 2 4)' - assert.matches "assertion error: Expected 2 to equal %(got %)", err - - err = assert.has.error -> COPILOT\eval_once '(expect= true 2 3)' - assert.matches "assertion error: Expected 2 to equal %(got %)", err - - it "reports full expressions", -> - err = assert.has.error -> COPILOT\eval_once '(import* math) (expect= 1 (+ 1 1))' - assert.matches "assertion error: Expected %(%+ 1 1%) to equal %(got %)", err diff --git a/spec/lang/thread_spec.moon b/spec/lang/thread_spec.moon deleted file mode 100644 index 9e84575..0000000 --- a/spec/lang/thread_spec.moon +++ /dev/null @@ -1,24 +0,0 @@ -import TestPilot from require 'spec.test_setup' - -describe "thread macros", -> - COPILOT = TestPilot! - - it "thread forward (->)", -> - rt = COPILOT\eval_once ' - (import* math) - #((/ (+ 10 2) 2) = 6) - (-> 10 - (+ 2) - (/ 2))' - assert.is.true rt\is_const! - assert.is.equal '', tostring rt.result - - it "thread last forward (->>)", -> - rt = COPILOT\eval_once ' - (import* math) - #((/ 10 (+ 2 3)) = 2) - (->> 3 - (+ 2) - (/ 10))' - assert.is.true rt\is_const! - assert.is.equal '', tostring rt.result diff --git a/spec/lib/array_spec.moon b/spec/lib/array_spec.moon new file mode 100644 index 0000000..b55cec6 --- /dev/null +++ b/spec/lib/array_spec.moon @@ -0,0 +1,148 @@ +import TestPilot from require 'spec.test_setup' +import T, Array from require 'alv' + +describe "array", -> + test = TestPilot '', '(import* array-)\n' + + svec3 = Array 3, T.str + + it "can contain any type", -> + COPILOT\eval_once '(array 1 2 3)' + COPILOT\eval_once '(array true false)' + COPILOT\eval_once '(array "a")' + COPILOT\eval_once '(array (array 1 2) (array 3 4))' + + it "cannot contain mixed types", -> + err = assert.has.error -> COPILOT\eval_once '(array 1 false)' + assert.matches "argument error: couldn't match arguments", err + + describe "(set)", -> + it "can swap values", -> + rt = COPILOT\eval_once '(set (array "f" "b" "c") 0 "a")' + assert.is.true rt\is_const! + assert.is.equal svec3\mk_const({ 'a', 'b', 'c' }), rt.result + + it "checks value type", -> + err = assert.has.error -> COPILOT\eval_once '(set (array 1) 0 "a")' + assert.matches "expected value of type num, not str", err + + it "checks index range", -> + err = assert.has.error -> COPILOT\eval_once '(set (array 1 2) -1 0)' + assert.matches "index '%-1' out of range!", err + + COPILOT\eval_once '(set (array 1 2) 0 0)' + + COPILOT\eval_once '(set (array 1 2) 1 0)' + + err = assert.has.error -> COPILOT\eval_once '(set (array 1 2) 2 0)' + assert.matches "index '2' out of range!", err + + describe "(get)", -> + it "can get a value", -> + rt = COPILOT\eval_once '(get (array 1 2) 0)' + assert.is.true rt\is_const! + assert.is.equal '', tostring rt.result + + it "checks index range", -> + err = assert.has.error -> COPILOT\eval_once '(get (array 1 2) -1)' + assert.matches "index '%-1' out of range!", err + + COPILOT\eval_once '(get (array 1 2) 0)' + + COPILOT\eval_once '(get (array 1 2) 1)' + + err = assert.has.error -> COPILOT\eval_once '(get (array 1 2) 2)' + assert.matches "index '2' out of range!", err + + describe '(head)', -> + it "can peek a value", -> + rt = COPILOT\eval_once '(head (array 1 2))' + assert.is.true rt\is_const! + assert.is.equal '', tostring rt.result + + describe '(tail)', -> + it "gets rest of an array", -> + rt = COPILOT\eval_once '(tail (array 1))' + assert.is.true rt\is_const! + assert.is.same (Array 0, T.num), rt.result.type + assert.is.same {}, rt.result! + + rt = COPILOT\eval_once '(tail (array 1 2))' + assert.is.true rt\is_const! + assert.is.same (Array 1, T.num), rt.result.type + assert.is.same { 2 }, rt.result! + + rt = COPILOT\eval_once '(tail (array 1 2 3 4))' + assert.is.true rt\is_const! + assert.is.same (Array 3, T.num), rt.result.type + assert.is.same { 2, 3, 4 }, rt.result! + + describe '(prepend)', -> + it "prepends to array", -> + rt = COPILOT\eval_once '(prepend (array 2) 1)' + assert.is.true rt\is_const! + assert.is.same (Array 2, T.num), rt.result.type + assert.is.same { 1, 2 }, rt.result! + + rt = COPILOT\eval_once '(prepend (array 2 3 4) 1)' + assert.is.true rt\is_const! + assert.is.same (Array 4, T.num), rt.result.type + assert.is.same { 1, 2, 3, 4 }, rt.result! + + describe "(insert)", -> + it "can insert a value", -> + rt = COPILOT\eval_once '(insert (array "b" "c") 0 "a")' + assert.is.true rt\is_const! + assert.is.equal svec3\mk_const({ 'a', 'b', 'c' }), rt.result + + rt = COPILOT\eval_once '(insert (array "a" "c") 1 "b")' + assert.is.true rt\is_const! + assert.is.equal svec3\mk_const({ 'a', 'b', 'c' }), rt.result + + rt = COPILOT\eval_once '(insert (array "a" "b") 2 "c")' + assert.is.true rt\is_const! + assert.is.equal svec3\mk_const({ 'a', 'b', 'c' }), rt.result + + it "checks index range", -> + err = assert.has.error -> COPILOT\eval_once '(insert (array 1 2) -1 0)' + assert.matches "index '%-1' out of range!", err + + COPILOT\eval_once '(insert (array 1 2) 0 0)' + + COPILOT\eval_once '(insert (array 1 2) 1 0)' + + COPILOT\eval_once '(insert (array 1 2) 2 0)' + + err = assert.has.error -> COPILOT\eval_once '(insert (array 1 2) 3 0)' + assert.matches "index '3' out of range!", err + + describe "(remove)", -> + it "can remove a value", -> + rt = COPILOT\eval_once '(remove (array "d" "a" "b" "c") 0)' + assert.is.true rt\is_const! + assert.is.equal svec3\mk_const({ 'a', 'b', 'c' }), rt.result + + rt = COPILOT\eval_once '(remove (array "a" "b" "c" "d") 3)' + assert.is.true rt\is_const! + assert.is.equal svec3\mk_const({ 'a', 'b', 'c' }), rt.result + + it "checks index range", -> + err = assert.has.error -> COPILOT\eval_once '(remove (array 1 2 3) -1)' + assert.matches "index '%-1' out of range!", err + + err = assert.has.error -> COPILOT\eval_once '(remove (array 1 2 3) 3)' + assert.matches "index '3' out of range!", err + + it "can be concatenated with (concat)", -> + rt = COPILOT\eval_once '(concat (array "a" "b") (array "c"))' + assert.is.true rt\is_const! + assert.is.equal svec3\mk_const({ 'a', 'b', 'c' }), rt.result + + it "size can be read using (size)", -> + rt = COPILOT\eval_once '(size (array 1))' + assert.is.true rt\is_const! + assert.is.equal '', tostring rt.result + + rt = COPILOT\eval_once '(size (array 1 2 3))' + assert.is.true rt\is_const! + assert.is.equal '', tostring rt.result diff --git a/spec/lib/builtins/cond_spec.moon b/spec/lib/builtins/cond_spec.moon new file mode 100644 index 0000000..89f7c7e --- /dev/null +++ b/spec/lib/builtins/cond_spec.moon @@ -0,0 +1,92 @@ +import TestPilot from require 'spec.test_setup' +import T, Struct, Array, Constant from require 'alv' + +describe "if", -> + COPILOT = TestPilot! + + it "checks truthiness", -> + for truthy in *{'true', '1', '-1', '1234', '(array 1 2 3)', '"test"', '""'} + with COPILOT\eval_once "(if #{truthy} 'yes' 'no')" + assert.is.true \is_const! + assert.is.equal 'yes', .result! + + for falsy in *{'false', '0'} + with COPILOT\eval_once "(if #{falsy} 'yes' 'no')" + assert.is.true \is_const! + assert.is.equal 'no', .result! + + it "can be used without else clause", -> + with COPILOT\eval_once '(if true "yes")' + assert.is.true \is_const! + assert.is.equal 'yes', .result! + + with COPILOT\eval_once '(if false "yes")' + assert.is.true \is_const! + assert.is.equal nil, .result + + it "doesn't evaluate the untaken branch", -> + with COPILOT\eval_once '(if true "yes" (error/doesnt-exist))' + assert.is.true \is_const! + assert.is.equal 'yes', .result! + + with COPILOT\eval_once '(if false (error/doesnt-exist) "no")' + assert.is.true \is_const! + assert.is.equal 'no', .result! + + it "errors on non-const choice", -> + err = assert.has.error -> + COPILOT\eval_once '(import* time) (if (ramp 1) "yes" "no")' + assert.matches "'if'%-expression needs to be constant", err + + it "forwards any result", -> + with COPILOT\eval_once ' + (import* time) + (if true (every 1 (array 1 2 3)))' + assert.is.false \is_const! + assert.is.equal '', tostring .result + +describe "when", -> + COPILOT = TestPilot! + + it "checks truthiness", -> + for truthy in *{'true', '1', '-1', '1234', '(array 1 2 3)', '"test"', '""'} + with COPILOT\eval_once "(when #{truthy} 'yes')" + assert.is.true \is_const! + assert.is.equal 'yes', .result! + + for falsy in *{'false', '0'} + with COPILOT\eval_once "(when #{falsy} 'yes')" + assert.is.true \is_const! + assert.is.nil .result + + it "doesn't evaluate if falsy", -> + with COPILOT\eval_once '(when false (error/doesnt-exist))' + assert.is.true \is_const! + assert.is.nil, .result + + with COPILOT\eval_once ' + (when false + (error/doesnt-exist) + (error/doesnt-exist) + (error/doesnt-exist))' + assert.is.true \is_const! + assert.is.nil, .result + + it "errors on non-const choice", -> + err = assert.has.error -> + COPILOT\eval_once '(import* time) (when (ramp 1) 1 2 3)' + assert.matches "'when'%-expression needs to be constant", err + + it "forwards any result", -> + with COPILOT\eval_once ' + (import* time) + (when true + (every 1 (array 1 2 3)) + 1 2 3)' + assert.is.false \is_const! + assert.is.equal '', tostring .result + + with COPILOT\eval_once '(when true (array 1 2 3))' + assert.is.true \is_const! + assert.is.equal '', tostring .result + diff --git a/spec/lib/builtins/do_spec.moon b/spec/lib/builtins/do_spec.moon new file mode 100644 index 0000000..fd2825b --- /dev/null +++ b/spec/lib/builtins/do_spec.moon @@ -0,0 +1,27 @@ +import TestPilot from require 'spec.test_setup' + +describe "do", -> + COPILOT = TestPilot! + + it "can be empty", -> + with COPILOT\eval_once '(do)' + assert.is.true \is_const! + assert.is.nil .result + + it "returns the last result, if any", -> + with COPILOT\eval_once '(do 1 2 3)' + assert.is.true \is_const! + assert.is.equal '', tostring .result + + with COPILOT\eval_once '(do 1 2 (def _ 3))' + assert.is.true \is_const! + assert.is.nil .result + + it "passes through side-effects", -> + with COPILOT\eval_once ' + (import* time) + (do + (every 0.5 "bang! side-effect") + 3)' + assert.is.false \is_const! + assert.is.equal '', tostring .result diff --git a/spec/lib/builtins/fn_spec.moon b/spec/lib/builtins/fn_spec.moon new file mode 100644 index 0000000..af27cf3 --- /dev/null +++ b/spec/lib/builtins/fn_spec.moon @@ -0,0 +1,51 @@ +import TestPilot from require 'spec.test_setup' +import T, Struct, Array, Constant from require 'alv' + +describe "function", -> + COPILOT = TestPilot! + + it "returns constant results when constant", -> + rt = COPILOT\eval_once ' + (import* math) + + (defn my-plus (a b) + (+ a b)) + + (my-plus 2 3)' + assert.is.true rt\is_const! + assert.is.equal (Constant.num 5), rt.result + + it "checks argument arity when invoked", -> + err = assert.has.error -> COPILOT\eval_once ' + ([1]import* math) + + ([2]defn my-plus (a b) + ([4]+ a b)) + + ([3]my-plus 2)' + assert.matches "argument error: expected 2 arguments, found 1", err + assert.matches "while invoking function 'my%-plus' at %[3%]", err + + err = assert.has.error -> COPILOT\eval_once ' + ([1]import* math) + + ([2]defn my-plus (a b) + ([4]+ a b)) + + ([3]my-plus 2 3 4)' + assert.matches "argument error: expected 2 arguments, found 3", err + assert.matches "while invoking function 'my%-plus' at %[3%]", err + + it "can be anonymously invoked", -> + rt = COPILOT\eval_once ' + ([1] + ([2]fn (a b) b) + 3 4)' + assert.is.equal (Constant.num 4), rt\const! + + err = assert.has.error -> COPILOT\eval_once ' + ([1] + ([2]fn (a b) b) + 3)' + assert.matches "argument error: expected 2 arguments, found 1", err + assert.matches "while invoking function %(unnamed%) at %[1%]", err diff --git a/spec/lib/builtins/literal_spec.moon b/spec/lib/builtins/literal_spec.moon new file mode 100644 index 0000000..3db010d --- /dev/null +++ b/spec/lib/builtins/literal_spec.moon @@ -0,0 +1,33 @@ +import TestPilot from require 'spec.test_setup' +import T, Struct, Array, Constant from require 'alv' + +describe "literal", -> + TestPilot ' + (def str "hello" + num 2 + bool true + curl ([5]struct "a" 2 "b" false) + sqre ([7]array 1 2 3 4)) + (export*)' + + assert.is.true COPILOT.active_module.root\is_const! + scope = (assert COPILOT.active_module.root.result)\unwrap T.scope + + it "string is parsed and returned correctly", -> + assert.is.equal (Constant.str 'hello'), (scope\get 'str')\const! + + it "number is parsed and returned correctly", -> + assert.is.equal (Constant.num 2), (scope\get 'num')\const! + + it "boolean is parsed and returned correctly", -> + assert.is.equal (Constant.bool true), (scope\get 'bool')\const! + + it "struct is parsed and returned correctly", -> + struct = (scope\get 'curl')\const! + assert.is.equal (Struct a: T.num, b: T.bool), struct.type + assert.is.same { a: 2, b: false }, struct! + + it "array is parsed and returned correctly", -> + array = (scope\get 'sqre')\const! + assert.is.equal (Array 4, T.num), array.type + assert.is.same {1, 2, 3, 4}, array! diff --git a/spec/lib/builtins/thread_spec.moon b/spec/lib/builtins/thread_spec.moon new file mode 100644 index 0000000..4b81119 --- /dev/null +++ b/spec/lib/builtins/thread_spec.moon @@ -0,0 +1,24 @@ +import TestPilot from require 'spec.test_setup' + +describe "thread macros", -> + COPILOT = TestPilot! + + it "thread forward (->)", -> + rt = COPILOT\eval_once ' + (import* math) + #((/ (+ 10 2) 8) = 1.5) + (-> 10 + (+ 2) + (/ 8))' + assert.is.true rt\is_const! + assert.is.equal '', tostring rt.result + + it "thread last forward (->>)", -> + rt = COPILOT\eval_once ' + (import* math) + #((/ 10 (+ 2 2)) = 2.5) + (->> 2 + (+ 2) + (/ 10))' + assert.is.true rt\is_const! + assert.is.equal '', tostring rt.result diff --git a/spec/lib/logic_spec.moon b/spec/lib/logic_spec.moon new file mode 100644 index 0000000..f3d0610 --- /dev/null +++ b/spec/lib/logic_spec.moon @@ -0,0 +1,264 @@ +import TestPilot from require 'spec.test_setup' +import T, Array, Constant from require 'alv' + +describe "logic", -> + test = TestPilot '', '(import* logic)\n' + TRUE = T.bool\mk_const true + FALSE = T.bool\mk_const false + + describe "==", -> + it "can compare any type", -> + with COPILOT\eval_once '(== 1 1)' + assert.is.true \is_const! + assert.is.equal TRUE, .result + + with COPILOT\eval_once '(== 1 2)' + assert.is.true \is_const! + assert.is.equal FALSE, .result + + with COPILOT\eval_once '(== 1 "hello")' + assert.is.true \is_const! + assert.is.equal FALSE, .result + + with COPILOT\eval_once '(== "hello" "hello")' + assert.is.true \is_const! + assert.is.equal TRUE, .result + + with COPILOT\eval_once '(== (array 1 2 3) (array 1 2 3))' + assert.is.true \is_const! + assert.is.equal TRUE, .result + + with COPILOT\eval_once '(== (array 1 2 3) (array 1 2 1))' + assert.is.true \is_const! + assert.is.equal FALSE, .result + + with COPILOT\eval_once '(== (array 1 2 3) (array 1 2))' + assert.is.true \is_const! + assert.is.equal FALSE, .result + + with COPILOT\eval_once '(== (array 1 2 3) (array 1 2 3 4))' + assert.is.true \is_const! + assert.is.equal FALSE, .result + + with COPILOT\eval_once '(== + (struct "a" 1 "b" true "c" (array "test")) + (struct "a" 1 "b" true "c" (array "test")))' + assert.is.true \is_const! + assert.is.equal TRUE, .result + + with COPILOT\eval_once '(== + (struct "a" 1 "b" false "c" (array "test")) + (struct "a" 1 "b" true "c" (array "test")))' + assert.is.true \is_const! + assert.is.equal FALSE, .result + + with COPILOT\eval_once '(== + (struct "a" 1 "b" true "c" (array "test" "toast")) + (struct "a" 1 "b" true "c" (array "test")))' + assert.is.true \is_const! + assert.is.equal FALSE, .result + + with COPILOT\eval_once '(== + (struct "a" 1 "b" true) + (struct "a" 1 "b" true "c" (array "test")))' + assert.is.true \is_const! + assert.is.equal FALSE, .result + + with COPILOT\eval_once '(== + (struct "a" 1 "b" true) + (struct "a" 1))' + assert.is.true \is_const! + assert.is.equal FALSE, .result + + with COPILOT\eval_once '(== print print)' + assert.is.true \is_const! + assert.is.equal TRUE, .result + + with COPILOT\eval_once '(== print ==)' + assert.is.true \is_const! + assert.is.equal FALSE, .result + + it "is aliased as eq", -> + with COPILOT\eval_once '(== eq ==)' + assert.is.true \is_const! + assert.is.equal TRUE, .result + + describe "!=", -> + it "can compare any type", -> + with COPILOT\eval_once '(!= 1 1)' + assert.is.true \is_const! + assert.is.equal FALSE, .result + + with COPILOT\eval_once '(!= 1 2)' + assert.is.true \is_const! + assert.is.equal TRUE, .result + + with COPILOT\eval_once '(!= 1 "hello")' + assert.is.true \is_const! + assert.is.equal TRUE, .result + + with COPILOT\eval_once '(!= "hello" "hello")' + assert.is.true \is_const! + assert.is.equal FALSE, .result + + with COPILOT\eval_once '(!= (array 1 2 3) (array 1 2 3))' + assert.is.true \is_const! + assert.is.equal FALSE, .result + + with COPILOT\eval_once '(!= (array 1 2 3) (array 1 2 1))' + assert.is.true \is_const! + assert.is.equal TRUE, .result + + with COPILOT\eval_once '(!= (array 1 2 3) (array 1 2))' + assert.is.true \is_const! + assert.is.equal TRUE, .result + + with COPILOT\eval_once '(!= (array 1 2 3) (array 1 2 3 4))' + assert.is.true \is_const! + assert.is.equal TRUE, .result + + with COPILOT\eval_once '(!= + (struct "a" 1 "b" true "c" (array "test")) + (struct "a" 1 "b" true "c" (array "test")))' + assert.is.true \is_const! + assert.is.equal FALSE, .result + + with COPILOT\eval_once '(!= + (struct "a" 1 "b" false "c" (array "test")) + (struct "a" 1 "b" true "c" (array "test")))' + assert.is.true \is_const! + assert.is.equal TRUE, .result + + with COPILOT\eval_once '(!= + (struct "a" 1 "b" true "c" (array "test" "toast")) + (struct "a" 1 "b" true "c" (array "test")))' + assert.is.true \is_const! + assert.is.equal TRUE, .result + + with COPILOT\eval_once '(!= + (struct "a" 1 "b" true) + (struct "a" 1 "b" true "c" (array "test")))' + assert.is.true \is_const! + assert.is.equal TRUE, .result + + with COPILOT\eval_once '(!= + (struct "a" 1 "b" true) + (struct "a" 1))' + assert.is.true \is_const! + assert.is.equal TRUE, .result + + with COPILOT\eval_once '(!= print print)' + assert.is.true \is_const! + assert.is.equal FALSE, .result + + with COPILOT\eval_once '(!= print ==)' + assert.is.true \is_const! + assert.is.equal TRUE, .result + + it "is aliased as note-eq", -> + with COPILOT\eval_once '(== not-eq !=)' + assert.is.true \is_const! + assert.is.equal TRUE, .result + + describe "bool", -> + it "coerces numbers", -> + with COPILOT\eval_once '(bool 0)' + assert.is.true \is_const! + assert.is.equal FALSE, .result + + with COPILOT\eval_once '(bool 1)' + assert.is.true \is_const! + assert.is.equal TRUE, .result + + with COPILOT\eval_once '(bool -1)' + assert.is.true \is_const! + assert.is.equal TRUE, .result + + with COPILOT\eval_once '(bool 1024)' + assert.is.true \is_const! + assert.is.equal TRUE, .result + + it "accepts booleans", -> + with COPILOT\eval_once '(bool false)' + assert.is.true \is_const! + assert.is.equal FALSE, .result + + with COPILOT\eval_once '(bool true)' + assert.is.true \is_const! + assert.is.equal TRUE, .result + + describe "not", -> + it "accepts booleans", -> + with COPILOT\eval_once '(not false)' + assert.is.true \is_const! + assert.is.equal TRUE, .result + + with COPILOT\eval_once '(not true)' + assert.is.true \is_const! + assert.is.equal FALSE, .result + + it "coerces numbers", -> + with COPILOT\eval_once '(not 0)' + assert.is.true \is_const! + assert.is.equal TRUE, .result + + with COPILOT\eval_once '(not 1)' + assert.is.true \is_const! + assert.is.equal FALSE, .result + + with COPILOT\eval_once '(not -1)' + assert.is.true \is_const! + assert.is.equal FALSE, .result + + with COPILOT\eval_once '(not 1024)' + assert.is.true \is_const! + assert.is.equal FALSE, .result + + + describe "or", -> + it "accepts any number of mixed arguments", -> + with COPILOT\eval_once '(or false 0)' + assert.is.true \is_const! + assert.is.equal FALSE, .result + + with COPILOT\eval_once '(or 1 0)' + assert.is.true \is_const! + assert.is.equal TRUE, .result + + with COPILOT\eval_once '(or 0 false 0 0 0 0)' + assert.is.true \is_const! + assert.is.equal FALSE, .result + + with COPILOT\eval_once '(or 0 0 0 true 0 0)' + assert.is.true \is_const! + assert.is.equal TRUE, .result + + with COPILOT\eval_once '(or 0 true 0 1 0 0)' + assert.is.true \is_const! + assert.is.equal TRUE, .result + + describe "and", -> + it "accepts any number of mixed arguments", -> + with COPILOT\eval_once '(and false 1)' + assert.is.true \is_const! + assert.is.equal FALSE, .result + + with COPILOT\eval_once '(and 1 true)' + assert.is.true \is_const! + assert.is.equal TRUE, .result + + with COPILOT\eval_once '(and false 0)' + assert.is.true \is_const! + assert.is.equal FALSE, .result + + with COPILOT\eval_once '(and 1 1 true 0)' + assert.is.true \is_const! + assert.is.equal FALSE, .result + + with COPILOT\eval_once '(and 1 1 true true 1)' + assert.is.true \is_const! + assert.is.equal TRUE, .result + + with COPILOT\eval_once '(and 1 1 1)' + assert.is.true \is_const! + assert.is.equal TRUE, .result diff --git a/spec/lib/math_spec.moon b/spec/lib/math_spec.moon new file mode 100644 index 0000000..23a353b --- /dev/null +++ b/spec/lib/math_spec.moon @@ -0,0 +1,267 @@ +import TestPilot from require 'spec.test_setup' +import T, Array, Constant from require 'alv' + +describe_both = (fn) -> + describe "math", -> + test = TestPilot '', '(import* testing math)\n' + fn! + + describe "math-simple", -> + test = TestPilot '', '(import* testing math-simple)\n' + fn! + +describe_both -> + describe "add, sub, mul, div, pow, mod", -> + it "are aliased as +-*/^%", -> + COPILOT\eval_once ' + (expect= + add) + (expect= - sub) + (expect= * mul) + (expect= / div) + (expect= ^ pow) + (expect= % mod) + ' + + it "are sane", -> + COPILOT\eval_once ' + (expect= 2 (+ 1 1)) + (expect= 6 (+ 2 3 0 1)) + + (expect= -5 (- 2 7)) + (expect= 0 (- 10 4 3 2 1 0)) + (expect= -10 (- 10)) + + (expect= 1 (* 1 1)) + (expect= -2 (* -1 2)) + (expect= 14 (* 4 0.5 7)) + + (expect= 1 (/ 4 4)) + (expect= -2 (/ -10 5)) + (expect= 3 (/ -30 -10)) + + (expect= 1024 (^ 2 10)) + (expect= 0.25 (^ 4 -1)) + (expect= 1 (^ 999 0)) + + (expect= 4 (% 10 6)) + (expect= 3 (% -2 5)) + (expect= -2 (% -2 -5)) + ' + + describe "trigonometric functions and constants", -> + COPILOT\eval_once ' + (expect= tau (* pi 2)) + + #(darn you fp accuracy! + (expect= 0.5 (asin (sin 0.5))) + (expect= 0.5 (acos (cos 0.5))) + ...) + ' + + it "min, max, clamp, huge", -> + COPILOT\eval_once ' + (expect= 0 (min 0 1 2)) + (expect= 2 (max 0 1 2)) + + (expect= -2 (clamp -2 3.5 -4)) + (expect= -1 (clamp -2 3.5 -1)) + (expect= 0 (clamp -2 3.5 0)) + (expect= 1 (clamp -2 3.5 1)) + (expect= 3.5 (clamp -2 3.5 4)) + + (expect= (- huge) (min 0 1 -123456789 (- huge))) + (expect= huge (max 0 1 123456789 huge)) + ' + + it "inc, dec", -> + COPILOT\eval_once ' + (expect= 1 (inc 0)) + (expect= -1 (dec 0)) + (expect= 3 (inc (inc 1))) + (expect= -1 (dec (dec 1))) + (expect= 0 (inc (dec 0)) (dec (inc 0))) + ' + +describe "math", -> + test = TestPilot '', '(import* testing math)\n' + + describe "add, sub, mul, div, pow, mod", -> + it "handle scalar/vector", -> + COPILOT\eval_once ' + (expect= (array 3 4 5) + (+ 1 (array 1 2 3) 1)) + + (expect= (array 0 1 2) + (- (array 1 2 3) 1)) + + (expect= (array 3 6 9) + (* 3 (array 1 2 3))) + (expect= (array 3 6 9) + (* (array 1 2 3) 3)) + + (expect= (array 12 9 4) + (/ 36 (array 3 4 9))) + + (expect= (array 9 16 25) + (^ (array 3 4 5) 2)) + (expect= (array 1 2 4 8) + (^ 2 (array 0 1 2 3))) + + (expect= (array 3 0 1) + (% (array 3 4 5) 4)) + ' + + it "handle vector/vector and matrix/matrix", -> + COPILOT\eval_once ' + (expect= (array 5 7 9) + (+ (array 1 2 3) + (array 4 5 6))) + + (expect= (array (array 11 12) + (array 13 14)) + (+ + (array (array 1 2) + (array 3 4)) + 5 + 5)) + + (expect= (array 2 0 -2) + (- (array 3 2 1) + (array 1 2 3))) + + (expect= (array 1 -2 -3) + (- (array -1 2 3))) + ' + + err = assert.has.error -> + COPILOT\eval_once ' + (+ (array 1 2 3) + (array 1 2))' + + err = assert.has.error -> + COPILOT\eval_once ' + (+ (array (array 1 2) (array 1 2)) + (array 1 2))' + + err = assert.has.error -> + COPILOT\eval_once ' + (- (array 1 2 3) + (array 1 2))' + + err = assert.has.error -> + COPILOT\eval_once ' + (- (array (array 1 2) (array 1 2)) + (array 1 2))' + + describe "mul", -> + it "handles scalars and matrices", -> + with COPILOT\eval_once ' + (* 3 + (array + (array 1 2) + (array 4 5)))' + assert.is.true \is_const! + assert.is.equal '', tostring .result + + with COPILOT\eval_once ' + (* (array + (array 1 2) + (array 4 5)) + 3)' + assert.is.true \is_const! + assert.is.equal '', tostring .result + + it "handles vectors and matrices", -> + with COPILOT\eval_once ' + (* + (array (array 1 0 0) + (array 0 1 0) + (array 0 0 1)) + (array 4 5 6))' + assert.is.true \is_const! + assert.is.equal '', tostring .result + + with COPILOT\eval_once ' + (* + (array (array 1 0 0) + (array 0 1 0) + (array 3 2 1)) + (array 4 5 1))' + assert.is.true \is_const! + assert.is.equal '', tostring .result + + it "handles matrices", -> + with COPILOT\eval_once ' + (* + (array (array 1 2 3) + (array 4 5 6)) + (array (array 10 11) + (array 20 21) + (array 30 31)))' + assert.is.true \is_const! + assert.is.equal '', tostring .result + + it "handles everything mixed", -> + with COPILOT\eval_once ' + (* + (array (array 1 2 3) + (array 4 5 6)) + 2 + (array (array 10 11) + (array 20 21) + (array 30 31)))' + assert.is.true \is_const! + assert.is.equal '', tostring .result + + with COPILOT\eval_once ' + (* + (array (array 1 2 3) + (array 4 5 6)) + 2 + (array (array 10 11) + (array 20 21) + (array 30 31)) + (array 4 7))' + assert.is.true \is_const! + assert.is.equal '', tostring .result + + it "errors with wrong sizes (matrix and vector)", -> + err = assert.has.error -> COPILOT\eval_once ' + (* + (array (array 1 2 3) + (array 4 5 6)) + (array 1 2))' + -- assert.matches "", err + + err = assert.has.error -> COPILOT\eval_once ' + (* + (array 1 2 3) + (array (array 1 2 3) + (array 4 5 6)))' + -- assert.matches "", err + + it "errors with wrong sizes (matrix)", -> + err = assert.has.error -> COPILOT\eval_once ' + (* + (array (array 1 2 3) + (array 4 5 6)) + (array (array 1 2) + (array 4 5)))' + -- assert.matches "", err + + it "min, max, clamp, huge", -> + COPILOT\eval_once ' + (expect= (array 3 2 1) + (min (array 3 4 1) (array 5 2 huge))) + (expect= (array 5 huge 4) + (max (array 3 huge 4) (array 5 999 2))) + + (expect= (array -2 -1 0 1 3.5) + (clamp -2 3.5 (array -4 -1 0 1 4))) + + (expect= 1 (inc 0)) + (expect= -1 (dec 0)) + (expect= 3 (inc (inc 1))) + (expect= -1 (dec (dec 1))) + (expect= 0 (inc (dec 0)) (dec (inc 0))) + ' diff --git a/spec/lib/struct_spec.moon b/spec/lib/struct_spec.moon new file mode 100644 index 0000000..9937f99 --- /dev/null +++ b/spec/lib/struct_spec.moon @@ -0,0 +1,51 @@ +import TestPilot from require 'spec.test_setup' +import T, Struct from require 'alv' + +describe "struct", -> + test = TestPilot '', '(import* struct-)\n' + + ab = Struct { a: T.num, b: T.bool } + + describe "(set)", -> + it "can update values", -> + rt = COPILOT\eval_once '(set (struct "a" 1 "b" false) "a" 2)' + assert.is.true rt\is_const! + assert.is.equal ab\mk_const({ a: 2, b: false }), rt.result + + it "cannot add members", -> + err = assert.has.error -> COPILOT\eval_once '(set (struct "a" 1) "b" 2)' + assert.matches "{a: num} has no 'b' key", err + + it "checks value type", -> + err = assert.has.error -> COPILOT\eval_once '(set (struct "a" 1) "a" "str")' + assert.matches "expected value for key 'a' to be num, not str", err + + describe "(get)", -> + it "can get values", -> + rt = COPILOT\eval_once '(get (struct "a" 1 "b" false) "a")' + assert.is.true rt\is_const! + assert.is.equal '', tostring rt.result + + it "checks keys", -> + err = assert.has.error -> COPILOT\eval_once '(get (struct "a" 1) "b")' + assert.matches "has no 'b' key", err + + describe "(insert)", -> + it "can add members", -> + rt = COPILOT\eval_once '(insert (struct "b" true) "a" 1)' + assert.is.true rt\is_const! + assert.is.equal ab\mk_const({ a: 1, b: true }), rt.result + + it "doesn't clobber existing members", -> + err = assert.has.error -> COPILOT\eval_once '(insert (struct "a" 1) "a" 2)' + assert.matches "key 'a' already exists in value of type {a: num}", err + + describe "(remove)", -> + it "can remove members", -> + rt = COPILOT\eval_once '(remove (struct "a" 1 "b" false "c" "abc") "c")' + assert.is.true rt\is_const! + assert.is.equal ab\mk_const({ a: 1, b: false }), rt.result + + it "checks keys", -> + err = assert.has.error -> COPILOT\eval_once '(remove (struct "a" 1) "b")' + assert.matches "has no 'b' key", err diff --git a/spec/lib/testing_spec.moon b/spec/lib/testing_spec.moon new file mode 100644 index 0000000..5be7848 --- /dev/null +++ b/spec/lib/testing_spec.moon @@ -0,0 +1,80 @@ +import TestPilot from require 'spec.test_setup' +import T, Array, Constant from require 'alv' + +describe "testing", -> + test = TestPilot '', '(import* testing logic)\n' + + describe "assert", -> + it "ignores true", -> + with COPILOT\eval_once '(assert true)' + assert.is.true \is_const! + assert.is.nil .result + + with COPILOT\eval_once '(assert true "with message")' + assert.is.true \is_const! + assert.is.nil .result + + it "throws on false", -> + assert.has.error -> COPILOT\eval_once '(assert false)' + + it "shows failing expression", -> + err = assert.has.error -> COPILOT\eval_once '(assert false)' + assert.matches "assertion failed: false", err + + err = assert.has.error -> COPILOT\eval_once '(assert (== 1 2))' + assert.matches "assertion failed: %(== 1 2%)", err + + it "supports custom error messages", -> + err = assert.has.error -> COPILOT\eval_once '(assert (== "green" "red") "duck isnt green")' + assert.matches "duck isnt green", err + + describe "expect=", -> + it "passes equal params", -> + with COPILOT\eval_once '(expect= 1 1 1)' + assert.is.true \is_const! + assert.is.nil .result + + with COPILOT\eval_once '(expect= 2 2 2)' + assert.is.true \is_const! + assert.is.nil .result + + with COPILOT\eval_once '(expect= true true)' + assert.is.true \is_const! + assert.is.nil .result + + with COPILOT\eval_once '(expect= "hello" "hello")' + assert.is.true \is_const! + assert.is.nil .result + + with COPILOT\eval_once '(expect= (array 1 2) (array 1 2))' + assert.is.true \is_const! + assert.is.nil .result + + it "fails different values", -> + assert.has.error -> COPILOT\eval_once '(expect= 1 2)' + assert.has.error -> COPILOT\eval_once '(expect= 1 1 2)' + assert.has.error -> COPILOT\eval_once '(expect= 2 1 1)' + + assert.has.error -> COPILOT\eval_once '(expect= true false)' + + assert.has.error -> COPILOT\eval_once '(expect= "asdf" "bsdf")' + assert.has.error -> COPILOT\eval_once '(expect= (array 1 2) (array 1 3))' + + it "fails different types", -> + assert.has.error -> COPILOT\eval_once '(expect= true 2)' + assert.has.error -> COPILOT\eval_once '(expect= true true 1)' + assert.has.error -> COPILOT\eval_once '(expect= true false "str")' + + it "reports first failing arguments", -> + err = assert.has.error -> COPILOT\eval_once '(expect= 1 2)' + assert.matches "assertion error: Expected 2 to equal %(got %)", err + + err = assert.has.error -> COPILOT\eval_once '(expect= 1 1 2 4)' + assert.matches "assertion error: Expected 2 to equal %(got %)", err + + err = assert.has.error -> COPILOT\eval_once '(expect= true 2 3)' + assert.matches "assertion error: Expected 2 to equal %(got %)", err + + it "reports full expressions", -> + err = assert.has.error -> COPILOT\eval_once '(import* math) (expect= 1 (+ 1 1))' + assert.matches "assertion error: Expected %(%+ 1 1%) to equal %(got %)", err diff --git a/spec/match_spec.moon b/spec/match_spec.moon deleted file mode 100644 index b9d5ef6..0000000 --- a/spec/match_spec.moon +++ /dev/null @@ -1,307 +0,0 @@ -import const, sig, evt, any from require 'alv.base.match' -import Op, Input from require 'alv.base' -import RTNode, T, Error from require 'alv' - -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 } - -mk_evt = (type, const) -> - result = T[type]\mk_evt! - RTNode :result, op: op_with_inputs { Input.hot result } - -describe 'sig and evt', -> - describe 'type-less shorthand', -> - it 'matches metatype', -> - str = mk_val 'str' - num = mk_val 'num' - assert.is.equal str, sig!\match { str } - assert.is.equal num, sig!\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.has.error -> sig!\match { str } - assert.has.error -> sig!\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, sig!\match { str } - assert.is.equal num, sig!\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 'can recall the type', -> - value = sig!! - event = evt!! - two_equal_values = value + value - two_equal_events = event + event - - str1 = mk_val 'str' - str2 = mk_val 'str' - num = mk_val 'num' - assert.is.same { str1, str2 }, two_equal_values\match { str1, str2 } - assert.is.same { str2, str1 }, two_equal_values\match { str2, str1 } - assert.is.same { num, num }, two_equal_values\match { num, num } - assert.has.error -> two_equal_values\match { str1, num } - assert.has.error -> two_equal_values\match { num, str2 } - assert.has.error -> two_equal_events\match { str1, str2 } - - str1 = mk_evt 'str' - str2 = mk_evt 'str' - num = mk_evt 'num' - assert.is.same { str1, str2 }, two_equal_events\match { str1, str2 } - assert.is.same { str2, str1 }, two_equal_events\match { str2, str1 } - assert.is.same { num, num }, two_equal_events\match { num, num } - assert.has.error -> two_equal_events\match { str1, num } - assert.has.error -> two_equal_events\match { num, str2 } - assert.has.error -> two_equal_values\match { str1, str2 } - - it 'stringifies well', -> - assert.is.equal 'any=', tostring const! - assert.is.equal 'any!', tostring evt! - assert.is.equal 'any~', tostring sig! - - describe 'typed shorthand', -> - it 'matches by metatype', -> - str = mk_val 'str' - num = mk_val 'num' - assert.is.equal str, sig.str\match { str } - assert.is.equal num, sig.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.has.error -> sig.str\match { str } - assert.has.error -> sig.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, sig.str\match { str } - assert.is.equal num, sig.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, sig.str\match { str } - assert.is.equal num, sig.num\match { num } - assert.is.equal str, const.str\match { str } - assert.is.equal num, const.num\match { num } - assert.has.error -> sig.num\match { str } - assert.has.error -> sig.str\match { num } - - str = mk_val 'str' - num = mk_val 'num' - assert.is.equal str, sig.str\match { str } - assert.is.equal num, sig.num\match { num } - assert.has.error -> const.num\match { str } - assert.has.error -> const.str\match { num } - assert.has.error -> sig.num\match { str } - assert.has.error -> sig.str\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 -> 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 sig.str - assert.is.equal 'num~', tostring sig.num - assert.is.equal 'str=', tostring const.str - assert.is.equal 'num=', tostring const.num - -describe 'choice', -> - str = mk_val 'str' - num = mk_val 'num' - bool = mk_val 'bool' - choice = sig.str / sig.num - - it 'matches either type', -> - assert.is.equal str, choice\match { str } - assert.is.equal num, choice\match { num } - assert.has.error -> choice\match { bool } - - it 'can recall the choice', -> - same = choice! - assert.is.equal num, same\match { num } - - same = same + same - assert.is.same { str, str }, same\match { str, str } - assert.is.same { num, num }, same\match { num, num } - assert.has.error -> same\match { str, num } - assert.has.error -> same\match { num, str } - assert.has.error -> same\match { bool, bool } - - it 'makes inner types recall', -> - same = any!! - same = same + same - assert.is.same { str, str }, same\match { str, str } - assert.is.same { num, num }, same\match { num, num } - assert.is.same { bool, bool }, same\match { bool, bool } - assert.has.error -> same\match { str, num } - assert.has.error -> same\match { num, str } - - it 'stringifies well', -> - assert.is.equal '(str~ | num~)', tostring choice - -describe 'sequence', -> - str = mk_val 'str' - num = mk_val 'num' - bool = mk_evt 'bool' - seq = sig.str + sig.num + evt.bool - - it 'matches all types in order', -> - assert.is.same { str, num, bool }, seq\match { str, num, bool } - - it 'can assign non-numeric keys', -> - named = seq\named 'str', 'num', 'bool' - assert.is.same { :str, :num, :bool }, named\match { str, num, bool } - assert.is.same { str, num, bool }, seq\match { str, num, bool } - - it 'fails if too little arguments', -> - assert.has.error -> seq\match { str, num } - - it 'fails if too many arguments', -> - assert.has.error -> seq\match { str, num, bool, bool } - - it 'can handle optional children', -> - opt = -sig.str + sig.num - assert.is.same { str, num }, opt\match { str, num } - assert.is.same { nil, num }, opt\match { num } - assert.has.error -> opt\match { str, str, num } - assert.has.error -> opt\match { str, num, num } - - it 'can handle repeat children', -> - rep = sig.str + sig.num*2 - assert.is.same { str, {num} }, rep\match { str, num } - assert.is.same { str, {num,num} }, rep\match { str, num, num } - assert.has.error -> rep\match { str } - assert.has.error -> rep\match { str, num, num, num } - - it 'stringifies well', -> - assert.is.equal '(str~ num~ bool!)', tostring seq - -describe 'repeat', -> - str = mk_val 'str' - num = mk_val 'num' - - times = (n, arg) -> return for i=1,n do arg - - it '*x is [1,x[', -> - rep = sig.str*3 - assert.has.error -> rep\match (times 0, str) - assert.is.same (times 1, str), rep\match (times 1, str) - assert.is.same (times 2, str), rep\match (times 2, str) - assert.is.same (times 3, str), rep\match (times 3, str) - assert.has.error -> rep\match (times 4, str) - assert.has.error -> rep\match (times 3, num) - - it '*0 is [1,[', -> - rep = sig.str*0 - assert.has.error -> rep\match (times 0, str) - assert.is.same (times 1, str), rep\match (times 1, str) - assert.is.same (times 2, str), rep\match (times 2, str) - assert.is.same (times 20, str), rep\match (times 20, str) - assert.has.error -> rep\match (times 3, num) - - it '^x is [0,x[', -> - rep = sig.str^3 - assert.is.same {}, rep\match {} - assert.is.same (times 1, str), rep\match (times 1, str) - assert.is.same (times 2, str), rep\match (times 2, str) - assert.is.same (times 3, str), rep\match (times 3, str) - assert.has.error -> rep\match (times 4, str) - assert.has.error -> rep\match (times 3, num) - - it '^0 is [0,[', -> - rep = sig.str^0 - assert.is.same {}, rep\match {} - assert.is.same (times 1, str), rep\match (times 1, str) - assert.is.same (times 2, str), rep\match (times 2, str) - assert.is.same (times 20, str), rep\match (times 20, str) - assert.has.error -> rep\match (times 3, num) - - it ':rep(min, max) does anything else', -> - rep = sig.str\rep 2, 2 - assert.has.error -> rep\match {} - assert.has.error -> rep\match (times 1, str) - assert.is.same (times 2, str), rep\match (times 2, str) - assert.has.error -> rep\match (times 3, str) - assert.has.error -> rep\match (times 2, num) - - it 'works with complex inner types', -> - rep = (sig.num + sig.str)\rep 2, 2 - assert.has.error -> rep\match {} - assert.has.error -> rep\match {num, str} - assert.has.error -> rep\match {num, num} - assert.is.same {{num, str}, {num, str}}, rep\match {num, str, num, str} - assert.has.error -> rep\match {str, str, str, str} - assert.has.error -> rep\match {num, str, num, str, num, str} - - it 'stringifies well', -> - assert.is.equal 'str~{1-3}', tostring sig.str*3 - assert.is.equal 'str~{1-*}', tostring sig.str*0 - assert.is.equal 'str~{0-*}', tostring sig.str^0 - assert.is.equal 'str~{2-2}', tostring sig.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 + sig.num*4 + - (sig.str + (sig.num / sig.str))\named('key', 'val')^0 - - it 'just works', -> - assert.is.same { bang, { num, num }, {} }, pattern\match { bang, num, num } - assert.is.same { nil, { num }, { { key: str, val: num }, - { key: str, val: str } } }, - pattern\match { num, str, num, str, str } - assert.has.error -> pattern\match { num, str } - 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_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 - - it 'gives useful error feedback', -> - msg = "couldn't match arguments (num~ str~ bool~) against pattern #{pattern}" - err = Error 'argument', msg - assert.has.error (-> pattern\match { num, str, mk_val 'bool' }), err diff --git a/spec/parsing_spec.moon b/spec/parsing_spec.moon deleted file mode 100644 index 0486276..0000000 --- a/spec/parsing_spec.moon +++ /dev/null @@ -1,141 +0,0 @@ -import space, atom, expr, explist, cell, program, comment - from require 'alv.parsing' -import Constant from require 'alv' -import Logger from require 'alv.logger' -Logger\init 'silent' - -verify_parse = (parser, val) -> - with assert parser\match val - assert.is.equal val, \stringify! - -verify_parse_nope = (parser, val) -> - with assert parser\match val - without_nope = val\match '^(.*) nope$' - assert.is.equal without_nope, \stringify! - -describe 'atom parsing', -> - test 'symbols', -> - sym = verify_parse_nope atom, 'some-toast nope' - assert.is.equal (Constant.sym 'some-toast'), sym - - describe 'numbers', -> - it 'parses ints', -> - num = verify_parse_nope atom, '1234 nope' - assert.is.equal (Constant.num 1234), num - - it 'parses floats', -> - num = verify_parse_nope atom, '0.123 nope' - assert.is.equal (Constant.num 0.123), num - - num = verify_parse_nope atom, '.123 nope' - assert.is.equal (Constant.num 0.123), num - - num = verify_parse_nope atom, '0. nope' - assert.is.equal (Constant.num 0), num - - describe 'strings', -> - it 'parses double-quote strings', -> - str = verify_parse_nope atom, '"help some stuff!" nope' - assert.is.equal (Constant.str 'help some stuff!'), str - - it 'parses single-quote strings', -> - str = verify_parse_nope atom, "'help some stuff!' nope" - assert.is.equal (Constant.str "help some stuff!"), str - - it 'handles escapes', -> - str = verify_parse_nope atom, '"string with \\"quote\\"s and \\\\" nope' - assert.is.equal (Constant.str 'string with \"quote\"s and \\'), str - - str = verify_parse_nope atom, "'string with \\'quote\\'s and \\\\' nope" - assert.is.equal (Constant.str "string with \'quote\'s and \\"), str - -describe 'Cell', -> - test 'basic parsing', -> - node = verify_parse cell, '( 3 ok-yes - "friend" )' - - assert.is.equal 3, #node.children - assert.is.equal (Constant.num 3), node.children[1] - assert.is.equal (Constant.sym 'ok-yes'), node.children[2] - assert.is.equal (Constant.str 'friend'), node.children[3] - - test 'tag parsing', -> - node = verify_parse cell, '([42]tagged 2)' - - assert.is.equal 2, #node.children - assert.is.equal 42, node.tag.value - - test 'tag parsing with whitespace', -> - node = verify_parse cell, '([42] - tagged 2)' - - assert.is.equal 2, #node.children - assert.is.equal 42, node.tag.value - -describe 'RootCell parsing', -> - describe 'handles whitespace', -> - verify = (str) -> - node = verify_parse program, str - - assert.is.equal 2, #node.children - assert.is.equal (Constant.num 3), node.children[1] - assert.is.equal (Constant.sym 'ok-yes'), node.children[2] - - it 'at the front of the string', -> - verify ' 3\tok-yes' - - it 'at the end of the string', -> - verify ' 3\tok-yes\n' - - it 'everywhere', -> - verify ' 3\tok-yes\n' - -test 'whitespace', -> - assert.is.equal ' ', space\match ' ' - assert.is.equal '\n\t ', space\match '\n\t ' - -describe 'comments', -> - comment = comment / 1 - it 'are parsed', -> - str = '#(this is a comment)' - assert.is.equal str, comment\match str - - it 'extend to matching braces', -> - str = '#(this is a comment #(with nested comments))' - assert.is.equal str, comment\match str - - it 'can nest', -> - str = '#(this is a comment (with nested parenthesis))' - assert.is.equal str, comment\match str - -describe 'resynthesis', -> - test 'mixed parsing', -> - str = '( 3 ok-yes - "friend" )' - node = verify_parse program, str - assert.is.equal str, node\stringify! - - test 'complex', -> - str = ' - #(send a CC controlled LFO to /radius) - (osc "/radius" (lfo (cc 14))) - - (osc rot - (step - #(whenever a kick is received...) - (note "kick") - - #(..cycle through random rotation values) - (random-rot) - (random-rot) - (random-rot) - (random-rot) - ) - ) ' - matched = assert.is.truthy verify_parse program, str - assert.is.equal str, matched\stringify! - - test 'nested tags', -> - str = '([2]a ([3]b))' - matched = assert.is.truthy verify_parse program, str - assert.is.equal str, matched\stringify! diff --git a/spec/pureop_spec.moon b/spec/pureop_spec.moon deleted file mode 100644 index a8fa823..0000000 --- a/spec/pureop_spec.moon +++ /dev/null @@ -1,128 +0,0 @@ -import do_setup, do_teardown, invoke_op from require 'spec.test_setup' -import PureOp, Input, T, sig, any from require 'alv.base' -import RTNode from require 'alv' - -setup do_setup -teardown do_teardown - -class TestPureOp extends PureOp - pattern: (any.num / sig.str)*3 - type: T.num - tick: => @out\set 1 - -literal = (result) -> - eval: -> - si = T.num\mk_sig! - RTNode :result, side_inputs: { [si]: Input.hot si } - -describe 'PureOp', -> - it 'matches the pattern', -> - assert.has.error -> invoke_op TestPureOp, {} - assert.has.error -> invoke_op TestPureOp, { literal T.bool\mk_evt! } - invoke_op TestPureOp, { literal T.num\mk_const 1 } - invoke_op TestPureOp, { literal T.str\mk_const 'hello' } - invoke_op TestPureOp, { literal T.num\mk_evt! } - - describe 'with constant inputs', -> - local rtn - it 'ticks once', -> - tiq = spy.on TestPureOp.__base, 'tick' - rtn = invoke_op TestPureOp, { T.num\mk_const(1), T.str\mk_const('hello') } - assert.spy(tiq).was_called_with match.is_ref(rtn.op), true - assert.is.equal 1, rtn.result! - - it 'is constant', -> - assert.is.equal '=', rtn\metatype! - - describe 'with signal inputs', -> - a = T.num\mk_sig 1 - b = T.num\mk_sig 2 - c = T.num\mk_sig 3 - tiq = spy.on TestPureOp.__base, 'tick' - rtn = invoke_op TestPureOp, { (literal a), (literal b), (literal c) } - op = rtn.op - - it 'sets up hot inputs', -> - assert.is.equal 3, #op.inputs - assert.is.equal a, op.inputs[1].result - assert.is.equal b, op.inputs[2].result - assert.is.equal c, op.inputs[3].result - assert.is.equal 'hot', op.inputs[1].mode - assert.is.equal 'hot', op.inputs[2].mode - assert.is.equal 'hot', op.inputs[3].mode - assert.spy(tiq).was_called! - - it 'has signal output', -> - assert.is.equal '~', op.out.metatype - - describe 'with event inputs', -> - a = T.num\mk_sig 1 - b = T.num\mk_evt 2 - c = T.num\mk_sig 3 - tiq = spy.on TestPureOp.__base, 'tick' - rtn = invoke_op TestPureOp, { (literal a), (literal b), (literal c) } - op = rtn.op - - it 'sets up hot input only for evt', -> - assert.is.equal 3, #op.inputs - assert.is.equal a, op.inputs[1].result - assert.is.equal b, op.inputs[2].result - assert.is.equal c, op.inputs[3].result - assert.is.equal 'cold', op.inputs[1].mode - assert.is.equal 'hot', op.inputs[2].mode - assert.is.equal 'cold', op.inputs[3].mode - assert.spy(tiq).was_not_called! - - it 'has event output', -> - assert.is.equal '!', op.out.metatype - - it 'only allows one event input', -> - a, b = T.num\mk_evt!, T.num\mk_evt! - assert.has.error -> invoke_op TestPureOp, { (literal a), (literal b) } - - it 'supports nested input patterns', -> - class NestedInputOp extends PureOp - pattern: (any.num + sig.str)\named('a', 'b')\rep 2, 2 - type: T.num - tick: => @out\set 1 - - num = T.num\mk_sig 1 - str = T.str\mk_sig 'hello' - oth = T.num\mk_evt 2 - args = { (literal num), (literal str), (literal oth), (literal str) } - rtn = invoke_op NestedInputOp, args - op = rtn.op - - assert.is.equal 2, #op.inputs - assert.is.equal num, op.inputs[1].a.result - assert.is.equal str, op.inputs[1].b.result - assert.is.equal oth, op.inputs[2].a.result - assert.is.equal str, op.inputs[2].b.result - assert.is.equal 'cold', op.inputs[1].a.mode - assert.is.equal 'cold', op.inputs[1].b.mode - assert.is.equal 'hot', op.inputs[2].a.mode - assert.is.equal 'cold', op.inputs[2].b.mode - - it 'supports dynamically generating the output type', -> - class DynamicOp extends PureOp - pattern: sig.num + any! - type: (inputs) => inputs[2]\type! - tick: => @out\set @inputs[2]! - typ = spy.on DynamicOp, 'type' - - a = T.num\mk_sig 1 - num = T.num\mk_sig 1 - str = T.str\mk_sig 1 - sym = T.sym\mk_evt 1 - - rtn = invoke_op DynamicOp, { (literal a), (literal num) } - assert.is.equal '~', rtn.result.metatype - assert.is.equal num, rtn.result - - rtn = invoke_op DynamicOp, { (literal a), (literal str) } - assert.is.equal '~', rtn.result.metatype - assert.is.equal str, rtn.result - - rtn = invoke_op DynamicOp, { (literal a), (literal sym) } - assert.is.equal '!', rtn.result.metatype - assert.is.equal T.sym, rtn.result.type diff --git a/spec/registry_spec.moon b/spec/registry_spec.moon deleted file mode 100644 index 4b0cfc3..0000000 --- a/spec/registry_spec.moon +++ /dev/null @@ -1,8 +0,0 @@ -import Registry, Tag from require 'alv.registry' -import Logger from require 'alv.logger' -Logger\init 'silent' - -mk = -> - mock destroy: => - -describe 'registry', -> diff --git a/spec/result/const_spec.moon b/spec/result/const_spec.moon deleted file mode 100644 index 3f31306..0000000 --- a/spec/result/const_spec.moon +++ /dev/null @@ -1,158 +0,0 @@ -import do_setup from require 'spec.test_setup' -import Constant, RTNode, Scope, SimpleRegistry, T from require 'alv' -import Op, Builtin from require 'alv.base' - -class TestOp extends Op - new: (...) => super ... - -class TestBuiltin extends Builtin - new: (...) => - -setup do_setup - -describe 'Constant', -> - it 'requires a value', -> - assert.has.error -> Constant.num! - assert.has.error -> Constant T.num - assert.has.no.error -> Constant T.bool, false - - it 'stringifies well', -> - assert.is.equal "", tostring Constant.num 4 - assert.is.equal "", tostring Constant.bool true - assert.is.equal "", tostring Constant.bool false - - describe '.wrap', -> - it 'wraps numbers', -> - got = Constant.wrap 3 - assert.is.equal T.num, got.type - assert.is.equal 3, got.value - - it 'wraps strings', -> - got = Constant.wrap "im a happy string" - assert.is.equal T.str, got.type - assert.is.equal "im a happy string", got.value - - it 'wraps Constants', -> - pi = Constant.num 3.14 - got = Constant.wrap pi - - assert.is.equal pi, got - - it 'wraps Opdefs', -> - got = Constant.wrap TestOp - - assert.is.equal T.opdef, got.type - assert.is.equal TestOp, got.value - - it 'wraps Bultins', -> - got = Constant.wrap TestBuiltin - - assert.is.equal T.builtin, got.type - assert.is.equal TestBuiltin, got.value - - it 'wraps Scopes', -> - sub = Scope! - got = Constant.wrap sub - - assert.is.equal T.scope, got.type - assert.is.equal sub, got.value - - it 'wraps tables', -> - pi = Constant.num 3.14 - got = Constant.wrap { :pi } - - assert.is.equal T.scope, got.type - assert.is.equal pi, (got.value\get 'pi')\const! - - describe ':unwrap', -> - it 'returns the raw value!', -> - assert.is.equal 3.14, (Constant.num 3.14)\unwrap! - assert.is.equal 'hi', (Constant.str 'hi')\unwrap! - assert.is.equal 'hi', (Constant.sym 'hi')\unwrap! - - test 'can assert the type', -> - assert.is.equal 3.14, (Constant.num 3.14)\unwrap T.num - assert.is.equal 'hi', (Constant.str 'hi')\unwrap T.str - assert.is.equal 'hi', (Constant.sym 'hi')\unwrap T.sym - assert.has_error -> (Constant.num 3.14)\unwrap T.sym - assert.has_error -> (Constant.str 'hi')\unwrap T.num - assert.has_error -> (Constant.sym 'hi')\unwrap T.str - - test 'has __call shorthand', -> - assert.is.equal 3.14, (Constant.num 3.14)! - assert.is.equal 'hi', (Constant.str 'hi')! - assert.is.equal 'hi', (Constant.sym 'hi')! - assert.is.equal 3.14, (Constant.num 3.14) T.num - assert.is.equal 'hi', (Constant.str 'hi') T.str - assert.is.equal 'hi', (Constant.sym 'hi') T.sym - assert.has_error -> (Constant.num 3.14) T.sym - assert.has_error -> (Constant.str 'hi') T.num - assert.has_error -> (Constant.sym 'hi') T.str - - describe 'overrides __eq', -> - it 'compares the type', -> - val = Constant.num 3 - assert.is.equal (Constant.num 3), val - assert.not.equal (Constant.str '3'), val - - val = Constant.str 'hello' - assert.is.equal (Constant.str 'hello'), val - assert.not.equal (Constant.sym 'hello'), val - - it 'compares the value', -> - val = Constant.num 3 - assert.is.equal (Constant.num 3), val - assert.not.equal (Constant.num 4), val - - it ':dirty is always false', -> - val = Constant.num 3 - assert.is.false val\dirty! - - val.value = 4 - assert.is.false val\dirty! - - describe ':eval', -> - it 'turns numbers into consts', -> - assert_noop = (val) -> - assert.is.equal val, val\eval!\const! - - assert_noop Constant.num 2 - assert_noop Constant.str 'hello' - - it 'looks up symbols in the scope', -> - scope = with Scope! - \set 'number', RTNode result: Constant.num 3 - \set 'hello', RTNode result: Constant.str "world" - \set 'goodbye', RTNode result: Constant.sym "again" - - assert_eval = (sym, val) -> - const = Constant.sym sym - assert.is.equal val, (const\eval scope)\const! - - assert_eval 'number', Constant.num 3 - assert_eval 'hello', Constant.str "world" - assert_eval 'goodbye', Constant.sym "again" - - it ':clones literals as themselves', -> - assert_noop = (val) -> assert.is.equal val, val\clone! - - assert_noop Constant.num 2 - assert_noop Constant.str 'hello' - assert_noop Constant.sym 'world' - - describe ':fork', -> - it 'is equal to the original', -> - a = Constant.num 2 - b = Constant.str 'asdf' - c = Constant T.weird, {}, '(raw)' - - aa, bb, cc = a\fork!, b\fork!, c\fork! - assert.is.equal a, aa - assert.is.equal b, bb - assert.is.equal c, cc - - assert.is.false aa\dirty! - assert.is.false bb\dirty! - assert.is.false cc\dirty! - - assert.is.equal c.raw, cc.raw diff --git a/spec/result/evt_spec.moon b/spec/result/evt_spec.moon deleted file mode 100644 index 46d4cec..0000000 --- a/spec/result/evt_spec.moon +++ /dev/null @@ -1,144 +0,0 @@ -import do_setup from require 'spec.test_setup' -import EvtStream, RTNode, Scope, SimpleRegistry, T from require 'alv' -import Op, Builtin from require 'alv.base' - -setup do_setup - -describe 'EvtStream', -> - it 'stringifies well', -> - number = EvtStream T.num - assert.is.equal "", tostring number - - number\set 4 - assert.is.equal "", tostring number - - bool = EvtStream T.bool - bool\set true - assert.is.equal "", tostring bool - - COPILOT\next_tick! - - bool\set false - assert.is.equal "", tostring bool - - describe ':unwrap', -> - it 'returns the set value', -> - stream = EvtStream T.num - assert.is.nil stream\unwrap! - - stream\set 3.14 - assert.is.equal 3.14, stream\unwrap! - - it 'returns nil if not dirty', -> - stream = EvtStream T.num - assert.is.nil stream\unwrap! - - stream\set 3.14 - COPILOT\next_tick! - assert.is.nil stream\unwrap! - - test 'can assert the type', -> - assert.is.nil (EvtStream T.num)\unwrap T.num - assert.is.nil (EvtStream T.str)\unwrap T.str - assert.is.nil (EvtStream T.sym)\unwrap T.sym - assert.has_error -> (EvtStream T.num)\unwrap T.sym - assert.has_error -> (EvtStream T.str)\unwrap T.num - assert.has_error -> (EvtStream T.sym)\unwrap T.str - - test 'has __call shorthand', -> - stream = EvtStream T.num - assert.is.nil stream! - - stream\set 3.14 - assert.is.equal 3.14, stream! - - describe ':set', -> - it 'sets the value', -> - stream = EvtStream T.num - assert.is.false stream\dirty! - - stream\set 4 - assert.is.equal 4, stream\unwrap! - assert.is.true stream\dirty! - - COPILOT\next_tick! - - assert.is.false stream\dirty! - stream\set 3 - assert.is.equal 3, stream\unwrap! - assert.is.true stream\dirty! - - it 'ignores nil values', -> - stream = EvtStream T.num - assert.is.nil stream\unwrap! - assert.is.false stream\dirty! - - stream\set! - assert.is.nil stream\unwrap! - assert.is.false stream\dirty! - - stream\set nil - assert.is.nil stream\unwrap! - assert.is.false stream\dirty! - - stream\set false - assert.is.equal false, stream\unwrap! - assert.is.true stream\dirty! - - it 'errors when set twice', -> - stream = EvtStream T.num - stream\set 1 - assert.has.error -> stream\set 2 - assert.is.equal 1, stream\unwrap! - - it 'resets on the next tick', -> - stream = EvtStream T.num - stream\set 1 - - COPILOT\next_tick! - - assert.is.false stream\dirty! - stream\set 2 - assert.is.equal 2, stream\unwrap! - assert.is.true stream\dirty! - - describe ':fork', -> - it 'is clean', -> - a = EvtStream T.num - b = EvtStream T.str - b\set 'asdf' - - aa, bb = a\fork!, b\fork! - assert.is.nil aa! - assert.is.nil bb! - assert.is.false aa\dirty! - assert.is.false bb\dirty! - - it 'leaves the original', -> - a = EvtStream T.num - b = EvtStream T.str - b\set 'asdf' - - aa, bb = a\fork!, b\fork! - assert.is.nil a! - assert.is.equal 'asdf', b! - assert.is.false a\dirty! - assert.is.true b\dirty! - - it 'isolates the original from the fork', -> - a = EvtStream T.num - b = EvtStream T.str - - aa, bb = a\fork!, b\fork! - a\set 1 - bb\set 2 - - assert.is.equal 1, a! - assert.is.true a\dirty! - assert.is.nil aa! - assert.is.false aa\dirty! - - assert.is.equal 2, bb! - assert.is.true bb\dirty! - assert.is.nil b! - assert.is.false b\dirty! diff --git a/spec/result/sig_spec.moon b/spec/result/sig_spec.moon deleted file mode 100644 index 65363bc..0000000 --- a/spec/result/sig_spec.moon +++ /dev/null @@ -1,116 +0,0 @@ -import do_setup from require 'spec.test_setup' -import SigStream, Constant, RTNode, Scope, SimpleRegistry, T from require 'alv' -import Op, Builtin from require 'alv.base' - -setup do_setup - -describe 'SigStream', -> - it 'stringifies well', -> - assert.is.equal "", tostring SigStream T.num, 4 - assert.is.equal "", tostring SigStream T.bool, true - assert.is.equal "", tostring SigStream T.bool, false - - describe ':unwrap', -> - it 'returns the raw value!', -> - assert.is.equal 3.14, (SigStream T.num, 3.14)\unwrap! - assert.is.equal 'hi', (SigStream T.str, 'hi')\unwrap! - assert.is.equal 'hi', (SigStream T.sym, 'hi')\unwrap! - - test 'can assert the type', -> - assert.is.equal 3.14, (SigStream T.num, 3.14)\unwrap T.num - assert.is.equal 'hi', (SigStream T.str, 'hi')\unwrap T.str - assert.is.equal 'hi', (SigStream T.sym, 'hi')\unwrap T.sym - assert.has_error -> (SigStream T.num, 3.14)\unwrap T.sym - assert.has_error -> (SigStream T.str, 'hi')\unwrap T.num - assert.has_error -> (SigStream T.sym, 'hi')\unwrap T.str - - test 'has __call shorthand', -> - assert.is.equal 3.14, (SigStream T.num, 3.14)! - assert.is.equal 'hi', (SigStream T.str, 'hi')! - assert.is.equal 'hi', (SigStream T.sym, 'hi')! - assert.is.equal 3.14, (SigStream T.num, 3.14) T.num - assert.is.equal 'hi', (SigStream T.str, 'hi') T.str - assert.is.equal 'hi', (SigStream T.sym, 'hi') T.sym - assert.has_error -> (SigStream T.num, 3.14) T.sym - assert.has_error -> (SigStream T.str, 'hi') T.num - assert.has_error -> (SigStream T.sym, 'hi') T.str - - describe 'overrides __eq', -> - it 'compares the type', -> - val = SigStream T.num, 3 - assert.is.equal (SigStream T.num, 3), val - assert.not.equal (SigStream T.str, '3'), val - - val = SigStream T.str, 'hello' - assert.is.equal (SigStream T.str, 'hello'), val - assert.not.equal (SigStream T.sym, 'hello'), val - - it 'compares the value', -> - val = SigStream T.num, 3 - assert.is.equal (SigStream T.num, 3), val - assert.not.equal (SigStream T.num, 4), val - - it 'can be compared to a Constant', -> - val = SigStream T.num, 3 - assert.is.equal (Constant.num 3), val - assert.not.equal (Constant.num 4), val - - val = SigStream T.str, 'hello' - assert.is.equal (Constant.str 'hello'), val - assert.not.equal (Constant.sym 'hello'), val - - describe ':set', -> - it 'sets the value', -> - val = SigStream T.num, 3 - assert.is.equal (SigStream T.num, 3), val - - val\set 4 - assert.is.equal (SigStream T.num, 4), val - assert.not.equal (SigStream T.num, 3), val - - it 'marks the value dirty', -> - val = SigStream T.num, 3 - assert.is.false val\dirty! - - val\set 4 - assert.is.true val\dirty! - - describe ':fork', -> - it 'is equal to the original', -> - a = SigStream T.num, 2 - b = SigStream T.str, 'asdf' - c = with SigStream T.weird, {}, '(raw)' - \set {} - - aa, bb, cc = a\fork!, b\fork!, c\fork! - assert.is.equal a, aa - assert.is.equal b, bb - assert.is.equal c, cc - - assert.is.false aa\dirty! - assert.is.false bb\dirty! - assert.is.true cc\dirty! - - assert.is.equal c.raw, cc.raw - - it 'isolates the original from the fork', -> - a = SigStream T.num, 3 - b = with SigStream T.weird, {}, '(raw)' - \set {} - - aa, bb = a\fork!, b\fork! - - bb\set {false} - - assert.is.same {}, b! - assert.is.same {false}, bb! - assert.is.true b\dirty! - assert.is.true bb\dirty! - - COPILOT\next_tick! - aa\set 4 - - assert.is.equal 3, a! - assert.is.equal 4, aa! - assert.is.false a\dirty! - assert.is.true aa\dirty! diff --git a/spec/rtnode_spec.moon b/spec/rtnode_spec.moon deleted file mode 100644 index f757067..0000000 --- a/spec/rtnode_spec.moon +++ /dev/null @@ -1,216 +0,0 @@ -import do_setup from require 'spec.test_setup' -import RTNode, Scope, SimpleRegistry from require 'alv' -import T, Input, Op, Constant from require 'alv.base' - -class IOOp extends Op - new: (...) => - super ... - @is_dirty = true - - poll: => @is_dirty - -make_op = (Class, inputs={}) -> - with Class! - \setup inputs - -setup do_setup - -describe 'RTNode', -> - describe 'constructor', -> - it 'takes result, children', -> - result = Constant.num 3 - - a = RTNode! - b = RTNode! - children = { a, b } - - node = RTNode :result, :children - - assert.is.equal result, node.result - assert.is.same children, node.children - - it 'takes op and detects io_ops', -> - op = make_op Op - rtn = RTNode :op - assert.is.equal op, rtn.op - assert.is.same {}, rtn.io_ops - - op = make_op IOOp - rtn = RTNode :op - assert.is.equal op, rtn.op - assert.is.same { op }, rtn.io_ops - - describe 'detects Op inputs', -> - it '(hot -> side_input)', -> - sig = T.num\mk_sig! - inp = Input.hot sig - rtn = RTNode op: make_op Op, { inp } - assert.is.same { [sig]: inp }, rtn.side_inputs - - it '(cold -> discard)', -> - rtn = RTNode op: make_op Op, { Input.cold T.num\mk_sig! } - assert.is.same {}, rtn.side_inputs - - it "(childrens' results -> discard)", -> - child = RTNode op: make_op(Op), result: T.num\mk_sig 123 - parent = RTNode children: { child }, op: make_op Op, { Input.hot child.result } - assert.is.same {}, parent.side_inputs - - describe 'lifts up', -> - it 'side_inputs from children', -> - expected_inputs = {} - children = {} - - for i=1,3 - result = T.num\mk_sig! - input = Input.hot result - table.insert children, RTNode op: make_op Op, { input } - expected_inputs[result] = input - - mid_rtn = RTNode children: children - out_rtn = RTNode children: { mid_rtn } - - assert.is.same expected_inputs, mid_rtn.side_inputs - assert.is.same expected_inputs, out_rtn.side_inputs - - it 'io_ops from children', -> - a_op = make_op IOOp - a_rtn = RTNode op: a_op - - b_op = make_op IOOp - b_rtn = RTNode op: b_op - - c_op = make_op Op - c_rtn = RTNode op: c_op - - mid_rtn = RTNode children: { a_rtn, b_rtn, c_rtn } - out_op = make_op IOOp - out_rtn = RTNode children: { mid_rtn }, op: out_op - - assert.is.same { a_op, b_op }, mid_rtn.io_ops - assert.is.same { a_op, b_op, out_op }, out_rtn.io_ops - - it ':type gets type and assets value', -> - node = RTNode result: Constant.num 2 - assert.is.equal T.num, node\type! - - node = RTNode! - assert.has.error -> node\type! - - it ':is_const', -> - result = Constant.num 2 - pure = RTNode :result - impure = RTNode op: make_op Op, { Input.hot T.num\mk_sig! } - - assert.is.true pure\is_const! - assert.is.false impure\is_const! - - assert.is.equal result, pure\const! - assert.has.error -> impure\const! - assert.has.error (-> impure\const 'test'), 'test' - - it ':make_ref', -> - result = T.num\mk_sig 2 - input = Input.hot result - op = make_op IOOp, { input } - thick = RTNode :result, :op, children: { RTNode!, RTNode! } - ref = thick\make_ref! - - assert ref - assert.is.equal thick.result, ref.result - assert.is.same thick.side_inputs, ref.side_inputs - assert.is.same {}, ref.children - assert.is.same {}, ref.io_ops - assert.is.nil ref.op - - describe ':poll_io', -> - it 'polls all io_ops in tree', -> - child = RTNode op: make_op IOOp - node = RTNode children: { child }, op: make_op IOOp - - sc = spy.on child.op, 'poll' - sn = spy.on node.op, 'poll' - node\poll_io! - - assert.spy(sc).was_called_with match.ref child.op - assert.spy(sn).was_called_with match.ref node.op - - it 'returns whether any ops were dirty', -> - child = RTNode op: make_op IOOp - node = RTNode children: { child }, op: make_op IOOp - - assert.is.true node\poll_io! - - child.op.is_dirty = false - assert.is.true node\poll_io! - - node.op.is_dirty = false - assert.is.false node\poll_io! - - child.op.is_dirty = true - assert.is.true node\poll_io! - - describe ':tick', -> - local a_value, a_child, a_input - local b_value, b_child, b_input - before_each -> - a_value = T.num\mk_evt! - a_input = Input.hot a_value - a_child = RTNode op: make_op Op, { a_input } - - b_value = T.num\mk_evt! - b_input = Input.hot b_value - b_child = RTNode op: make_op Op, { b_input } - - it 'updates children when a side_input is dirty', -> - a_value\set 1 - assert.is.true a_input\dirty! - assert.is.false b_input\dirty! - - a = spy.on a_child, 'tick' - b = spy.on b_child, 'tick' - - node = RTNode children: { a_child, b_child } - node\tick! - - assert.spy(a).was_called_with match.ref a_child - assert.spy(b).was_called_with match.ref b_child - - it 'early-outs when no side_inputs are dirty', -> - assert.is.false a_input\dirty! - assert.is.false b_input\dirty! - - a = spy.on a_child, 'tick' - b = spy.on b_child, 'tick' - - node = RTNode children: { a_child, b_child } - node\tick! - - assert.spy(a).was_not_called! - assert.spy(b).was_not_called! - - it 'updates op when any op-inputs are dirty', -> - a_value\set 1 - assert.is.true a_input\dirty! - assert.is.false b_input\dirty! - - op = make_op Op, { a: Input.hot a_value } - s = spy.on op, 'tick' - - node = RTNode :op, children: { a_child, b_child } - node\tick! - - assert.spy(s).was_called_with match.ref op - - it 'early-outs when no op-inputs are dirty', -> - a_value\set 1 - assert.is.true a_input\dirty! - assert.is.false b_input\dirty! - - op = make_op Op, { Input.hot b_value } - s = spy.on op, 'tick' - - node = RTNode :op, children: { a_child, b_child } - node\tick! - - assert.spy(s).was_not_called! diff --git a/spec/scope_spec.moon b/spec/scope_spec.moon deleted file mode 100644 index b4a55f1..0000000 --- a/spec/scope_spec.moon +++ /dev/null @@ -1,166 +0,0 @@ -import Scope, T, Constant, RTNode from require 'alv' -import Op from require 'alv.base' -import Logger from require 'alv.logger' -Logger\init 'silent' - -class TestOp extends Op - new: (...) => super ... - -wrap_res = (result) -> RTNode :result - -describe 'Scope', -> - describe 'constifies', -> - scope = Scope! - - test 'numbers', -> - scope\set_raw 'num', 3 - - got = (scope\get 'num')\const! - assert.is.equal (Constant.num 3), got - - test 'strings', -> - scope\set_raw 'str', "im a happy string" - - got = (scope\get 'str')\const! - assert.is.equal (Constant.str "im a happy string"), got - - test 'Values', -> - pi = Constant.num 3.14 - scope\set_raw 'pi', pi - assert.is.equal pi, (scope\get 'pi')\const! - - test 'Opdefs', -> - scope\set_raw 'test', TestOp - - got = (scope\get 'test')\const! - assert.is.equal TestOp, got T.opdef - - test 'Scopes', -> - sub = Scope! - scope\set_raw 'sub', sub - - got = (scope\get 'sub')\const! - assert.is.equal sub, got T.scope - - test 'tables', -> - pi = Constant.num 3.14 - scope\set_raw 'math', { :pi } - - got = (scope\get 'math')\const! - assert.is.equal T.scope, got.type - assert.is.equal Scope, got.value.__class - assert.is.equal pi, (got.value\get 'pi')\const! - assert.is.equal pi, (scope\get 'math/pi')\const! - - it 'stringifies well', -> - scope = Scope.from_table { - num: 3 - str: "im a happy string" - :pi - math: :pi - test: TestOp - } - sub = Scope Scope scope - - assert.is.equal "", tostring sub - assert.is.equal "", tostring scope - scope\set_raw 'a', Constant.num 1 - scope\set_raw 'b', Constant.num 2 - scope\set_raw 'c', Constant.num 3 - assert.is.equal "", tostring scope - - it 'wraps Values in from_table', -> - pi = Constant.num 3.14 - scope = Scope.from_table { - num: 3 - str: "im a happy string" - :pi - math: :pi - test: TestOp - } - - got = (scope\get 'num')\const! - assert.is.equal 3, got T.num - - got = (scope\get 'str')\const! - assert.is.equal "im a happy string", got T.str - - assert.is.equal pi, (scope\get 'pi')\const! - - got = (scope\get 'test')\const! - assert.is.equal TestOp, got T.opdef - - got = (scope\get 'math')\const! - assert.is.equal T.scope, got.type - assert.is.equal pi, (scope\get 'math/pi')\const! - - it 'gets from nested scopes', -> - root = Scope! - a = Scope! - b = Scope! - - pi = Constant.num 3.14 - b\set_raw 'test', pi - a\set_raw 'child', b - root\set_raw 'deep', a - - assert.is.equal pi, (root\get 'deep/child/test')\const! - - describe 'can set symbols', -> - one = wrap_res Constant.num 1 - two = wrap_res Constant.num 2 - scope = Scope! - - it 'disallows re-setting symbols', -> - scope\set 'test', one - assert.is.equal one, scope\get 'test' - - it 'throws if overwriting', -> - assert.has.error -> scope\set 'test', two - assert.is.equal one, scope\get 'test' - - describe 'inheritance', -> - root = Scope! - root\set_raw 'hidden', 1234 - root\set_raw 'inherited', "inherited string" - - scope = Scope root - - it 'allows access', -> - got = (scope\get 'inherited')\const! - assert.is.equal "inherited string", got T.str - - it 'can be shadowed', -> - scope\set_raw 'hidden', "overwritten" - - got = (scope\get 'hidden')\const! - assert.is.equal "overwritten", got T.str - - describe 'dynamic inheritance', -> - root = Scope! - dyn_root = Scope! - - root\set_raw 'normal', 'normal' - root\set_raw '*dynamic*', 'normal' - dyn_root\set_raw 'normal', 'dynamic' - dyn_root\set_raw '*dynamic*', 'dynamic' - - dyn_root\set_raw '*nested*', { value: 3 } - - it 'follows a different parent', -> - merged = Scope root, dyn_root - assert.is.equal 'normal', (merged\get 'normal').result! - assert.is.equal 'dynamic', (merged\get '*dynamic*').result! - - it 'falls back to the immediate parent', -> - merged = Scope root - assert.is.equal 'normal', (merged\get '*dynamic*').result! - - it 'looks in self first', -> - merged = Scope root - merged\set_raw '*dynamic*', 'merged' - assert.is.equal 'merged', (merged\get '*dynamic*').result! - - it 'can resolve nested', -> - merged = Scope root, dyn_root - assert.is.equal 3, (merged\get '*nested*/value').result! diff --git a/spec/tag_spec.moon b/spec/tag_spec.moon deleted file mode 100644 index f92dca0..0000000 --- a/spec/tag_spec.moon +++ /dev/null @@ -1,91 +0,0 @@ -import do_setup from require 'spec.test_setup' -import Tag from require 'alv.tag' -import Registry from require 'alv.registry' - -setup do_setup - -describe 'Tag', -> - describe 'should be constructable', -> - it 'by parsing', -> - tag = Tag.parse '2' - assert tag - assert.is.equal 2, tag.value - assert.is.equal '[2]', tag\stringify! - assert.is.equal '2', tostring tag - - it 'as blank Tags', -> - tag = Tag.blank! - assert tag - assert.is.nil tag.value - assert.is.equal '', tag\stringify! - assert.is.equal '?', tostring tag - - describe 'should be clonable', -> - do_asserts = (tag, expect) -> - assert tag - assert.is.nil tag.value - assert.is.equal expect, tostring tag - assert.has.error tag\stringify - - it 'from parsed tags', -> - parent = Tag.parse '1' - original = Tag.parse '2' - tag = original\clone parent - do_asserts tag, '1.2' - - it 'but not from blank tags', -> - parent = Tag.parse '1' - original = Tag.blank! - tag = original\clone parent - do_asserts tag, '1.?' - - it 'with blank parent', -> - parent = Tag.blank! - original = Tag.parse '2' - tag = original\clone parent - do_asserts tag, '?.2' - - it 'completely blank', -> - parent = Tag.blank! - original = Tag.blank! - tag = original\clone parent - do_asserts tag, '?.?' - - describe 'should be set-able', -> - it 'only if blank', -> - tag = Tag.parse '42' - assert.has.error -> tag\set 43 - - clone = tag\clone Tag.parse '3' - assert.has.error -> clone\set 42 - - clone = tag\clone Tag.blank! - assert.has.error -> clone\set 42 - - it 'and stores the value', -> - blank = Tag.blank! - blank\set 12 - - assert.is.equal blank.value, 12 - - it 'sets the original if cloned', -> - original = Tag.blank! - parent = Tag.parse '7' - - o_set = spy.on original, 'set' - p_set = spy.on parent, 'set' - - clone = original\clone parent - clone\set 11 - - assert.spy(o_set).was_called_with (match.is_ref original), 11 - assert.spy(p_set).was_not_called! - - assert.is.equal original.value, 11 - - it 'requires the parent to be registered if cloned', -> - original = Tag.blank! - parent = Tag.blank! - - clone = original\clone parent - assert.has.error -> clone\set 11 diff --git a/spec/type_spec.moon b/spec/type_spec.moon deleted file mode 100644 index 727e90b..0000000 --- a/spec/type_spec.moon +++ /dev/null @@ -1,175 +0,0 @@ -require 'spec.test_setup' -import T, Type, Primitive, Array, Struct from require 'alv.type' -import ancestor from require 'alv.util' - -bool = Primitive 'bool' -num = Primitive 'num' -str = Primitive 'str' - -describe 'Primitive', -> - it 'inherits from Type', -> - assert.is.equal Type, ancestor Primitive.__class - - it 'exposes name', -> - assert.is.equal 'bool', bool.name - assert.is.equal 'num', num.name - assert.is.equal 'str', str.name - - it 'stringifies well', -> - assert.is.equal 'bool', tostring bool - assert.is.equal 'num', tostring num - assert.is.equal 'str', tostring str - - it 'implements __eq sensibly', -> - assert.is.equal (Primitive 'bool'), bool - assert.is.equal (Primitive 'num'), num - assert.is.equal (Primitive 'str'), str - assert.not.equal num, bool - assert.not.equal str, num - - it ':pp pretty-prints values', -> - assert.is.equal 'true', bool\pp true - assert.is.equal 'false', bool\pp false - assert.is.equal 'nil', bool\pp nil - assert.is.equal '4.134', num\pp 4.134 - assert.is.equal '"hello"', str\pp "hello" - - it ':eq compares values', -> - a, b = {}, {} - assert.is.true bool\eq true, true - assert.is.false bool\eq true, false - assert.is.true num\eq 1, 1 - assert.is.false num\eq 1, -1 - assert.is.true num\eq a, a - assert.is.false num\eq a, b - - it ':get errors', -> - assert.has.error -> bool\get 'hello' - assert.has.error -> bool\get 2 - assert.has.error -> bool\get! - -describe 'Array', -> - vec3 = Array 3, num - str32 = Array 2, Array 3, str - - it 'inherits from Type', -> - assert.is.equal Type, ancestor Array.__class - - it 'exposes size', -> - assert.is.equal 3, vec3.size - assert.is.equal 2, str32.size - - it 'exposes inner type', -> - assert.is.equal num, vec3.type - assert.is.equal (Array 3, str), str32.type - - it 'stringifies well', -> - assert.is.equal 'num[3]', tostring vec3 - assert.is.equal 'my-type[3][24]', tostring Array 24, Array 3, 'my-type' - - it 'implements __eq sensibly', -> - assert.is.equal vec3, Array 3, num - assert.not.equal vec3, Array 2, num - assert.not.equal vec3, Array 3, str - - it ':pp pretty-prints values', -> - assert.is.equal '[1 2 3]', vec3\pp { 1, 2, 3 } - assert.is.equal '[["a" "b" "c"] ["d" "e" "f"]]', - str32\pp { {'a', 'b', 'c'}, {'d', 'e', 'f'} } - - it ':eq compares values', -> - a, b, c = {1, 2, 3}, {1, 2, 3}, {} - assert.is.true vec3\eq a, a - assert.is.true vec3\eq a, b - assert.is.false vec3\eq a, {1, 2, 4} - assert.is.true vec3\eq {1, 2, c}, {1, 2, c} - assert.is.false vec3\eq {1, 2, c}, {1, 2, {}} - assert.is.true str32\eq { {'a', 'b', 'c'}, {'d', 'e', 'f'} }, - { {'a', 'b', 'c'}, {'d', 'e', 'f'} } - assert.is.false str32\eq { {'a', 'b', 'c'}, {'d', 'e', 'f'} }, - { {'a', 'b', 'c'}, {'d', 'e', 'g'} } - - it ':get verifies size range', -> - assert.has.error -> vec3\get -1 - assert.is.equal num, vec3\get 0 - assert.is.equal num, vec3\get 1 - assert.is.equal num, vec3\get 2 - assert.has.error -> vec3\get 3 - - assert.is.equal (Array 3, str), str32\get 1 - - assert.has.error -> vec3\get! - assert.has.error -> vec3\get 'fail' - -describe 'Struct', -> - play = Struct { note: str, dur: num } - abc = Struct { c: num, b: num, a: num } - - it 'inherits from Type', -> - assert.is.equal Type, ancestor Struct.__class - - it 'exposes inner types', -> - assert.is.same { note: str, dur: num }, play.types - assert.is.same { c: num, b: num, a: num }, abc.types - - it 'stringifies well', -> - assert.is.equal '{dur: num note: str}', tostring play - assert.is.equal '{a: num b: num c: num}', tostring abc - - it 'implements __eq sensibly', -> - assert.is.equal play, Struct { note: str, dur: num } - assert.not.equal play, Struct { note: str } - assert.not.equal play, Struct { note: str, dur: str } - assert.not.equal play, Struct { note: str, dur: num, extra: num } - - it ':pp pretty-prints values', -> - assert.is.equal '{dur: 0.5 note: "a"}', play\pp { dur: 0.5, note: 'a' } - assert.is.equal '{a: 1 b: 2 c: 3}', abc\pp { a: 1, b: 2, c: 3 } - - it ':eq compares values', -> - a, b, c = { dur: 0.5, note: 'a' }, { dur: 0.5, note: 'a' }, {} - assert.is.true play\eq a, a - assert.is.true play\eq a, b - assert.is.false play\eq a, { dur: 0.5, note: 'b' } - assert.is.true play\eq { dur: 0.5, note: c }, { dur: 0.5, note: c } - assert.is.false play\eq { dur: 0.5, note: c }, { dur: 0.5, note: {} } - - it ':get verifies key exists', -> - assert.is.equal str, play\get 'note' - assert.is.equal num, play\get 'dur' - assert.has.error -> play\get! - assert.has.error -> play\get '' - assert.has.error -> play\get 'something' - -describe 'T', -> - it 'provides shorthand for Primitives', -> - assert.is.equal num, T.num - assert.is.equal str, T.str - assert.is.equal (Primitive 'midi/evt'), T['midi/evt'] - -for type in *{num, str, (Array 3, num)} - describe "#{type}", -> - describe ':mk_sig', -> - it 'can create value-less Streams', -> - stream = type\mk_sig! - assert.is.equal 'SigStream', stream.__class.__name - assert.is.equal type, stream.type - assert.is.nil stream! - - it 'can take an initial value', -> - stream = type\mk_sig 4 - assert.is.equal 'SigStream', stream.__class.__name - assert.is.equal type, stream.type - assert.is.equal 4, stream! - - describe ':mk_const', -> - it 'takes a value', -> - stream = type\mk_const 4 - assert.is.equal 'Constant', stream.__class.__name - assert.is.equal type, stream.type - assert.is.equal 4, stream! - - describe ':mk_evt', -> - stream = type\mk_evt! - assert.is.equal 'EvtStream', stream.__class.__name - assert.is.equal type, stream.type -- cgit v1.2.3