From 24610c2d43eba82000b43ac7d83488f433a1b081 Mon Sep 17 00:00:00 2001 From: s-ol Date: Tue, 14 Apr 2020 10:49:15 +0200 Subject: move spec out of spec/core --- spec/cell_spec.moon | 76 ++++++++++++++ spec/core/cell_spec.moon | 76 -------------- spec/core/input_spec.moon | 153 --------------------------- spec/core/match_spec.moon | 240 ------------------------------------------- spec/core/parsing_spec.moon | 152 --------------------------- spec/core/registry_spec.moon | 8 -- spec/core/result_spec.moon | 184 --------------------------------- spec/core/scope_spec.moon | 159 ---------------------------- spec/core/tag_spec.moon | 94 ----------------- spec/core/value_spec.moon | 190 ---------------------------------- spec/input_spec.moon | 153 +++++++++++++++++++++++++++ spec/match_spec.moon | 240 +++++++++++++++++++++++++++++++++++++++++++ spec/parsing_spec.moon | 152 +++++++++++++++++++++++++++ spec/registry_spec.moon | 8 ++ spec/result_spec.moon | 184 +++++++++++++++++++++++++++++++++ spec/scope_spec.moon | 159 ++++++++++++++++++++++++++++ spec/tag_spec.moon | 94 +++++++++++++++++ spec/value_spec.moon | 190 ++++++++++++++++++++++++++++++++++ 18 files changed, 1256 insertions(+), 1256 deletions(-) create mode 100644 spec/cell_spec.moon delete mode 100644 spec/core/cell_spec.moon delete mode 100644 spec/core/input_spec.moon delete mode 100644 spec/core/match_spec.moon delete mode 100644 spec/core/parsing_spec.moon delete mode 100644 spec/core/registry_spec.moon delete mode 100644 spec/core/result_spec.moon delete mode 100644 spec/core/scope_spec.moon delete mode 100644 spec/core/tag_spec.moon delete mode 100644 spec/core/value_spec.moon create mode 100644 spec/input_spec.moon create mode 100644 spec/match_spec.moon create mode 100644 spec/parsing_spec.moon create mode 100644 spec/registry_spec.moon create mode 100644 spec/result_spec.moon create mode 100644 spec/scope_spec.moon create mode 100644 spec/tag_spec.moon create mode 100644 spec/value_spec.moon (limited to 'spec') diff --git a/spec/cell_spec.moon b/spec/cell_spec.moon new file mode 100644 index 0000000..81f30b1 --- /dev/null +++ b/spec/cell_spec.moon @@ -0,0 +1,76 @@ +import Cell, RootCell from require 'alv.cell' +import ValueStream, Scope, Tag, SimpleRegistry, globals from require 'alv' +import Logger from require 'alv.logger' +Logger.init 'silent' + +hello_world = Cell.parse (Tag.parse '2'), { '', (ValueStream.sym 'hello'), ' ', (ValueStream.str 'world'), '' } +two_plus_two = Cell.parse (Tag.parse '3'), { '', (ValueStream.sym '+'), ' ', (ValueStream.num 2), ' ', (ValueStream.num 2), '' } + +reg = SimpleRegistry! +setup -> reg\grab! +teardown -> reg\release! + +describe 'Cell', -> + describe 'when quoted', -> + with hello_world\quote! + it 'stays equal', -> + assert.is.equal Cell, .__class + assert.is.equal (ValueStream.sym 'hello'), \head! + assert.is.same { ValueStream.str 'world' }, \tail! + + it 'shares the tag', -> + assert.is.equal hello_world.tag, .tag + + with two_plus_two\quote! + it 'stays equal', -> + assert.is.equal Cell, .__class + assert.is.equal (ValueStream.sym '+'), \head! + assert.is.same { (ValueStream.num 2), (ValueStream.num 2) }, \tail! + + it 'shares the tag', -> + assert.is.equal two_plus_two.tag, .tag + + describe 'when cloned', -> + parent = Tag.blank '1' + with hello_world\clone parent + it 'keeps children', -> + assert.is.equal Cell, .__class + assert.is.equal (ValueStream.sym 'hello'), \head! + assert.is.same { ValueStream.str 'world' }, \tail! + + it 'clones the tag', -> + assert.is.equal hello_world.tag, .tag.original + 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 = ValueStream.sym 'trace' + cell = Cell.parse { '', head, ' ', (ValueStream.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 (ValueStream.sym 'do'), cell\head! + + cell = RootCell nil, { hello_world, two_plus_two } + assert.is.equal (ValueStream.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/core/cell_spec.moon b/spec/core/cell_spec.moon deleted file mode 100644 index d16c9d8..0000000 --- a/spec/core/cell_spec.moon +++ /dev/null @@ -1,76 +0,0 @@ -import Cell, RootCell from require 'core.cell' -import ValueStream, Scope, Tag, SimpleRegistry, globals from require 'core' -import Logger from require 'logger' -Logger.init 'silent' - -hello_world = Cell.parse (Tag.parse '2'), { '', (ValueStream.sym 'hello'), ' ', (ValueStream.str 'world'), '' } -two_plus_two = Cell.parse (Tag.parse '3'), { '', (ValueStream.sym '+'), ' ', (ValueStream.num 2), ' ', (ValueStream.num 2), '' } - -reg = SimpleRegistry! -setup -> reg\grab! -teardown -> reg\release! - -describe 'Cell', -> - describe 'when quoted', -> - with hello_world\quote! - it 'stays equal', -> - assert.is.equal Cell, .__class - assert.is.equal (ValueStream.sym 'hello'), \head! - assert.is.same { ValueStream.str 'world' }, \tail! - - it 'shares the tag', -> - assert.is.equal hello_world.tag, .tag - - with two_plus_two\quote! - it 'stays equal', -> - assert.is.equal Cell, .__class - assert.is.equal (ValueStream.sym '+'), \head! - assert.is.same { (ValueStream.num 2), (ValueStream.num 2) }, \tail! - - it 'shares the tag', -> - assert.is.equal two_plus_two.tag, .tag - - describe 'when cloned', -> - parent = Tag.blank '1' - with hello_world\clone parent - it 'keeps children', -> - assert.is.equal Cell, .__class - assert.is.equal (ValueStream.sym 'hello'), \head! - assert.is.same { ValueStream.str 'world' }, \tail! - - it 'clones the tag', -> - assert.is.equal hello_world.tag, .tag.original - 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 = ValueStream.sym 'trace' - cell = Cell.parse { '', head, ' ', (ValueStream.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 (ValueStream.sym 'do'), cell\head! - - cell = RootCell nil, { hello_world, two_plus_two } - assert.is.equal (ValueStream.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/core/input_spec.moon b/spec/core/input_spec.moon deleted file mode 100644 index e867408..0000000 --- a/spec/core/input_spec.moon +++ /dev/null @@ -1,153 +0,0 @@ -import Input, Result, ValueStream, EventStream, IOStream from require 'core.base' -import SimpleRegistry from require 'core' -import Logger from require 'logger' -Logger.init 'silent' - -reg = SimpleRegistry! -setup -> reg\grab! -teardown -> reg\release! - -class MyIO extends IOStream - new: => super 'my-io' - dirty: => @is_dirty - -basic_tests = (stream, input) -> - it 'gives access to the Stream', -> - assert.is.equal stream, input.stream - - it 'forwards :unwrap', -> - assert.is.same stream\unwrap!, input\unwrap! - assert.is.same stream\unwrap!, input! - - it 'gives access to the type string', -> - assert.is.equal stream.type, input\type! - - it 'gives access to the metatype string', -> - assert.is.equal stream.metatype, input\metatype! - -describe 'Input.cold', -> - stream = ValueStream.num 1 - input = Input.cold stream - - basic_tests stream, input - - it 'is never dirty', -> - assert.is.false input\dirty! - stream\set 2 - assert.is.false input\dirty! - - input\setup nil - assert.is.false input\dirty! - input\finish_setup! - - new_input = Input.cold ValueStream.num 3 - new_input\setup input - assert.is.false new_input\dirty! - new_input.stream\set 4 - assert.is.false new_input\dirty! - input\finish_setup! - -describe 'Input.hot', -> - describe 'with EventStream', -> - stream = EventStream 'num' - input = Input.hot stream - - basic_tests stream, input - - it 'is marked for lifting', -> - assert.is.nil input.io - - it 'is dirty when the EventStream is dirty', -> - assert.is.false input\dirty! - assert.is.false stream\dirty! - - input\setup nil - assert.is.false input\dirty! - input\finish_setup! - - reg\next_tick! - stream\add 1 - - assert.is.true input\dirty! - assert.is.true stream\dirty! - - input\setup nil - assert.is.true input\dirty! - input\finish_setup! - - assert.is.true input\dirty! - assert.is.true stream\dirty! - - describe 'with IOStream', -> - stream = MyIO! - input = Input.hot stream - - basic_tests stream, input - - it 'is marked for lifting', -> - assert.is.true input.io - - it 'is dirty when the IOStream is dirty', -> - stream.is_dirty = false - - assert.is.false input\dirty! - assert.is.false stream\dirty! - - input\setup nil - assert.is.false input\dirty! - input\finish_setup! - - reg\next_tick! - stream.is_dirty = true - - assert.is.true input\dirty! - assert.is.true stream\dirty! - - input\setup nil - assert.is.true input\dirty! - input\finish_setup! - - assert.is.true input\dirty! - assert.is.true stream\dirty! - - describe 'with ValueStream', -> - stream = ValueStream.num 1 - local input - - describe 'at evaltime', -> - it 'is dirty when new', -> - assert.is.false stream\dirty! - - input = Input.hot stream - input\setup nil - assert.is.true input\dirty! - input\finish_setup! - - it 'is dirty when different', -> - newval = ValueStream.num 2 - - assert.is.false newval\dirty! - newinput = Input.hot newval - newinput\setup input - assert.is.true newinput\dirty! - newinput\finish_setup! - - it 'is not dirty when equal', -> - newval = ValueStream.num 1 - newval\set 1 - - assert.is.true newval\dirty! - newinput = Input.hot newval - newinput\setup input - assert.is.false newinput\dirty! - newinput\finish_setup! - - describe 'at runtime', -> - it 'is dirty when the stream is dirty', -> - stream\set 3 - assert.is.true stream\dirty! - assert.is.true input\dirty! - - reg\next_tick! - assert.is.false stream\dirty! - assert.is.false input\dirty! diff --git a/spec/core/match_spec.moon b/spec/core/match_spec.moon deleted file mode 100644 index ff0254d..0000000 --- a/spec/core/match_spec.moon +++ /dev/null @@ -1,240 +0,0 @@ -import val, evt from require 'core.base.match' -import Result, ValueStream, EventStream from require 'core' - -mk_val = (type, const) -> - value = ValueStream type - with Result :value - .side_inputs = { 'fake' } unless const - -mk_evt = (type, const) -> - value = EventStream type - with Result :value - .side_inputs = { 'fake' } unless const - -describe 'val and evt', -> - describe 'type-less shorthand', -> - it 'matches metatype', -> - str = mk_val 'str' - num = mk_val 'num' - assert.is.equal str, val!\match { str } - assert.is.equal num, val!\match { num } - assert.has.error -> evt!\match { str } - assert.has.error -> evt!\match { num } - - str = mk_evt 'str' - num = mk_evt 'num' - assert.is.equal str, evt!\match { str } - assert.is.equal num, evt!\match { num } - assert.has.error -> val!\match { str } - assert.has.error -> val!\match { num } - - it 'is in recall mode', -> - value = val! - 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 'event!', tostring evt! - assert.is.equal 'value', tostring val! - - describe 'typed shorthand', -> - it 'matches by metatype', -> - str = mk_val 'str' - num = mk_val 'num' - assert.is.equal str, val.str\match { str } - assert.is.equal num, val.num\match { num } - assert.has.error -> evt.str\match { str } - assert.has.error -> evt.num\match { num } - - str = mk_evt 'str' - num = mk_evt 'num' - assert.is.equal str, evt.str\match { str } - assert.is.equal num, evt.num\match { num } - assert.has.error -> val.str\match { str } - assert.has.error -> val.num\match { num } - - it 'matches by type', -> - str = mk_val 'str' - num = mk_val 'num' - assert.is.equal str, val.str\match { str } - assert.is.equal num, val.num\match { num } - assert.has.error -> val.num\match { str } - assert.has.error -> val.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 -> evt.num\match { str } - assert.has.error -> evt.str\match { num } - - it 'stringifies well', -> - assert.is.equal 'str!', tostring evt.str - assert.is.equal 'num!', tostring evt.num - assert.is.equal 'str', tostring val.str - assert.is.equal 'num', tostring val.num - -describe 'choice', -> - str = mk_val 'str' - num = mk_val 'num' - bool = mk_val 'bool' - choice = val.str / val.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 = (val! / evt!)! - 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 = val.str + val.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 = -val.str + val.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 = val.str + val.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 = val.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 = val.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 = val.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 = val.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 = val.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 'stringifies well', -> - assert.is.equal 'str{1-3}', tostring val.str*3 - assert.is.equal 'str{1-*}', tostring val.str*0 - assert.is.equal 'str{0-*}', tostring val.str^0 - assert.is.equal 'str{2-2}', tostring val.str\rep 2, 2 - -describe 'complex nesting', -> - bang = mk_evt 'bang' - str = mk_val 'str' - num = mk_val 'num' - pattern = -evt.bang + val.num*4 + (val.str + (val.num / val.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 { bang, num, num, num, num, num, num } - assert.has.error -> pattern\match { bang, bang, num } - assert.has.error -> pattern\match { num, str, num, str } - assert.has.error -> pattern\match { num, str, num, str, mk_val 'bool' } - - it 'stringifies well', -> - assert.is.equal '(bang!? num{1-4} (str (num | str)){0-*})', tostring pattern diff --git a/spec/core/parsing_spec.moon b/spec/core/parsing_spec.moon deleted file mode 100644 index 32d8530..0000000 --- a/spec/core/parsing_spec.moon +++ /dev/null @@ -1,152 +0,0 @@ -import space, atom, expr, explist, cell, program, comment - from require 'core.parsing' -import ValueStream from require 'core' -import Logger from require 'logger' -Logger.init 'silent' - -verify_parse = (parser, str) -> - with assert parser\match str - assert.is.equal str, \stringify! - -verify_parse_nope = (parser, str) -> - with assert parser\match str - without_nope = str\match '^(.*) nope$' - assert.is.equal without_nope, \stringify! - -describe 'atom parsing', -> - test 'symbols', -> - sym = verify_parse_nope atom, 'some-toast nope' - assert.is.equal 'sym', sym.type - assert.is.equal 'some-toast', sym\unwrap! - assert.is.equal 'some-toast', sym\stringify! - - describe 'numbers', -> - it 'parses ints', -> - num = verify_parse_nope atom, '1234 nope' - assert.is.equal 'num', num.type - assert.is.equal 1234, num\unwrap! - assert.is.equal '1234', num\stringify! - - it 'parses floats', -> - num = verify_parse_nope atom, '0.123 nope' - assert.is.equal 'num', num.type - assert.is.equal 0.123, num\unwrap! - - num = verify_parse_nope atom, '.123 nope' - assert.is.equal 'num', num.type - assert.is.equal 0.123, num\unwrap! - - num = verify_parse_nope atom, '0. nope' - assert.is.equal 'num', num.type - assert.is.equal 0, num\unwrap! - - describe 'strings', -> - it 'parses double-quote strings', -> - str = verify_parse_nope atom, '"help some stuff!" nope' - assert.is.equal 'str', str.type - assert.is.equal 'help some stuff!', str\unwrap! - - it 'parses single-quote strings', -> - str = verify_parse_nope atom, "'help some stuff!' nope" - assert.is.equal 'str', str.type - assert.is.equal "help some stuff!", str\unwrap! - - it 'handles escapes', -> - str = verify_parse_nope atom, '"string with \\"quote\\"s and \\\\" nope' - assert.is.equal 'str', str.type - assert.is.equal 'string with \"quote\"s and \\', str\unwrap! - - str = verify_parse_nope atom, "'string with \\'quote\\'s and \\\\' nope" - assert.is.equal 'str', str.type - assert.is.equal "string with \'quote\'s and \\", str\unwrap! - -describe 'Cell', -> - test 'basic parsing', -> - node = verify_parse cell, '( 3 ok-yes - "friend" )' - - assert.is.equal 3, #node.children - assert.is.equal (ValueStream.num 3), node.children[1] - assert.is.equal (ValueStream.sym 'ok-yes'), node.children[2] - assert.is.equal (ValueStream.str 'friend'), node.children[3] - - test 'tag parsing', -> - node = verify_parse cell, '([42]tagged 2)' - - 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 (ValueStream.num 3), node.children[1] - assert.is.equal (ValueStream.sym 'ok-yes'), node.children[2] - - it 'at the front of the string', -> - verify ' 3\tok-yes' - - 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/core/registry_spec.moon b/spec/core/registry_spec.moon deleted file mode 100644 index 6fe2e3a..0000000 --- a/spec/core/registry_spec.moon +++ /dev/null @@ -1,8 +0,0 @@ -import Registry, Tag from require 'core.registry' -import Logger from require 'logger' -Logger.init 'silent' - -mk = -> - mock destroy: => - -describe 'registry', -> diff --git a/spec/core/result_spec.moon b/spec/core/result_spec.moon deleted file mode 100644 index 017be58..0000000 --- a/spec/core/result_spec.moon +++ /dev/null @@ -1,184 +0,0 @@ -import Result, Scope, SimpleRegistry from require 'core' -import Input, Op, ValueStream, EventStream, IOStream from require 'core.base' -import Logger from require 'logger' -Logger.init 'silent' - -op_with_inputs = (inputs) -> - with Op! - \setup inputs if inputs - -result_with_sideinput = (value, input) -> - with Result :value - .side_inputs = { [value]: input } - -reg = SimpleRegistry! -setup -> reg\grab! -teardown -> reg\release! - -class DirtyIO extends IOStream - new: => super 'dirty-io' - dirty: => true - -describe 'Result', -> - it 'wraps value, children', -> - value = ValueStream.num 3 - - a = Result! - b = Result! - children = { a, b } - - result = Result :value, :children - - assert.is.equal value, result.value - assert.is.same children, result.children - - it ':type gets type and assets value', -> - result = Result value: ValueStream.num 2 - assert.is.equal 'num', result\type! - - result = Result! - assert.has.error -> result\type! - - it ':is_const', -> - value = ValueStream.num 2 - pure = Result :value - impure = result_with_sideinput value, {} - - assert.is.true pure\is_const! - assert.is.false impure\is_const! - - assert.is.equal value, pure\const! - assert.has.error -> impure\const! - assert.has.error (-> impure\const 'test'), 'test' - - it ':make_ref', -> - value = ValueStream.num 2 - input = Input.hot value - op = op_with_inputs { input } - thick = Result :value, :op, children: { Result!, Result! } - ref = thick\make_ref! - - assert ref - assert.is.equal thick.value, ref.value - assert.is.same thick.side_inputs, ref.side_inputs - assert.is.same {}, ref.children - assert.is.nil ref.op - - it 'lifts up inputs from op', -> - event = ValueStream 'bang', false - event_input = Input.hot event - - value = ValueStream 'num', 4 - value_input = Input.hot value - - op = op_with_inputs { event_input, value_input } - result = Result op: op, :value - - assert.is.equal op, result.op - assert.is.same { [event]: event_input, [value]: value_input }, - result.side_inputs - - it 'does not lift up op inputs that are also child values', -> - event = ValueStream 'bang', false - event_input = Input.hot event - - value = ValueStream 'num', 4 - value_input = Input.hot value - - op = op_with_inputs { event_input, value_input } - result = Result op: op, :value, children: { Result :value } - - assert.is.same { [event]: event_input }, result.side_inputs - - it 'lifts up side_inputs from children', -> - event_value = ValueStream 'bang', false - event_input = Input.hot event_value - event = Result op: op_with_inputs { event_input } - assert.is.same { [event_value]: event_input }, event.side_inputs - - value_value = ValueStream 'num', 4 - value_input = Input.hot value_value - value = Result op: op_with_inputs { value_input } - assert.is.same { [value_value]: value_input }, value.side_inputs - - result = Result children: { event, value } - assert.is.same { [event_value]: event_input, [value_value]: value_input }, - result.side_inputs - - describe ':tick', -> - local a_value, a_child, a_input - local b_value, b_child, b_input - before_each -> - a_value = EventStream 'num' - a_input = Input.hot a_value - a_child = result_with_sideinput a_value, a_input - - b_value = EventStream 'num' - b_input = Input.hot b_value - b_child = result_with_sideinput b_value, b_input - - it 'updates children when a side_input is dirty', -> - a_value\add 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' - - result = Result children: { a_child, b_child } - result\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' - - result = Result children: { a_child, b_child } - result\tick! - - assert.spy(a).was_not_called! - assert.spy(b).was_not_called! - - it 'updates op when any op-inputs are dirty', -> - a_value\add 1 - assert.is.true a_input\dirty! - assert.is.false b_input\dirty! - - op = op_with_inputs a: Input.hot a_value - s = spy.on op, 'tick' - - result = Result :op, children: { a_child, b_child } - result\tick! - - assert.spy(s).was_called_with match.ref op - - it 'early-outs when no op-inputs are dirty', -> - a_value\add 1 - assert.is.true a_input\dirty! - assert.is.false b_input\dirty! - - op = op_with_inputs { Input.hot b_value } - s = spy.on op, 'tick' - - result = Result :op, children: { a_child, b_child } - result\tick! - - assert.spy(s).was_not_called! - - describe ':tick_io', -> - it 'ticks IOs referenced in side_inputs', -> - io = DirtyIO! - input = Input.hot io - op = op_with_inputs { input } - result = Result :op - - s = spy.on io, 'tick' - assert.is.same { [io]: input }, result.side_inputs - result\tick_io! - - assert.spy(s).was_called_with match.ref io diff --git a/spec/core/scope_spec.moon b/spec/core/scope_spec.moon deleted file mode 100644 index 59c6bd1..0000000 --- a/spec/core/scope_spec.moon +++ /dev/null @@ -1,159 +0,0 @@ -import Scope, ValueStream, Result from require 'core' -import Op from require 'core.base' -import Logger from require 'logger' -Logger.init 'silent' - -class TestOp extends Op - new: (...) => super ... - -wrap_res = (value) -> Result :value - -describe 'Scope', -> - describe 'constifies', -> - scope = Scope! - - test 'numbers', -> - scope\set_raw 'num', 3 - - got = (scope\get 'num')\const! - assert.is.equal 'num', got.type - assert.is.equal 3, got.value - - test 'strings', -> - scope\set_raw 'str', "im a happy string" - - got = (scope\get 'str')\const! - assert.is.equal 'str', got.type - assert.is.equal "im a happy string", got.value - - test 'Values', -> - pi = ValueStream '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 'opdef', got.type - assert.is.equal TestOp, got.value - - test 'Scopes', -> - sub = Scope! - scope\set_raw 'sub', sub - - got = (scope\get 'sub')\const! - assert.is.equal 'scope', got.type - assert.is.equal sub, got.value - - test 'tables', -> - pi = ValueStream 'num', 3.14 - scope\set_raw 'math', { :pi } - - got = (scope\get 'math')\const! - assert.is.equal '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 'wraps Values in from_table', -> - pi = ValueStream '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 'num', got.type - assert.is.equal 3, got.value - - got = (scope\get 'str')\const! - assert.is.equal 'str', got.type - assert.is.equal "im a happy string", got.value - - assert.is.equal pi, (scope\get 'pi')\const! - - got = (scope\get 'test')\const! - assert.is.equal 'opdef', got.type - assert.is.equal TestOp, got.value - - got = (scope\get 'math')\const! - assert.is.equal 'scope', got.type - assert.is.equal pi, (scope\get 'math/pi')\const! - - it 'gets from nested scopes', -> - root = Scope! - a = Scope! - b = Scope! - - pi = ValueStream '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 ValueStream.num 1 - two = wrap_res ValueStream.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 'str', got.type - assert.is.equal "inherited string", got.value - - it 'can be shadowed', -> - scope\set_raw 'hidden', "overwritten" - - got = (scope\get 'hidden')\const! - assert.is.equal 'str', got.type - assert.is.equal "overwritten", got.value - - 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').value! - assert.is.equal 'dynamic', (merged\get '*dynamic*').value! - - it 'falls back to the immediate parent', -> - merged = Scope root - assert.is.equal 'normal', (merged\get '*dynamic*').value! - - it 'looks in self first', -> - merged = Scope root - merged\set_raw '*dynamic*', 'merged' - assert.is.equal 'merged', (merged\get '*dynamic*').value! - - it 'can resolve nested', -> - merged = Scope root, dyn_root - assert.is.equal 3, (merged\get '*nested*/value').value! diff --git a/spec/core/tag_spec.moon b/spec/core/tag_spec.moon deleted file mode 100644 index 87e02e2..0000000 --- a/spec/core/tag_spec.moon +++ /dev/null @@ -1,94 +0,0 @@ -import Tag from require 'core.tag' -import Registry from require 'core.registry' -import Logger from require 'logger' -Logger.init 'silent' - -reg = Registry! -setup -> reg\begin_eval! -teardown -> reg\end_eval! - -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/core/value_spec.moon b/spec/core/value_spec.moon deleted file mode 100644 index b09e8f7..0000000 --- a/spec/core/value_spec.moon +++ /dev/null @@ -1,190 +0,0 @@ -import ValueStream, Result, Scope, SimpleRegistry from require 'core' -import Op, Builtin from require 'core.base' -import Logger from require 'logger' -Logger.init 'silent' - -class TestOp extends Op - new: (...) => super ... - -class TestBuiltin extends Builtin - new: (...) => - -reg = SimpleRegistry! -setup -> reg\grab! -teardown -> reg\release! - -describe 'ValueStream', -> - describe '.wrap', -> - it 'wraps numbers', -> - got = ValueStream.wrap 3 - assert.is.equal 'num', got.type - assert.is.equal 3, got.value - - it 'wraps strings', -> - got = ValueStream.wrap "im a happy string" - assert.is.equal 'str', got.type - assert.is.equal "im a happy string", got.value - - it 'wraps Values', -> - pi = ValueStream 'num', 3.14 - got = ValueStream.wrap pi - - assert.is.equal pi, got - - it 'wraps Opdefs', -> - got = ValueStream.wrap TestOp - - assert.is.equal 'opdef', got.type - assert.is.equal TestOp, got.value - - it 'wraps Bultins', -> - got = ValueStream.wrap TestBuiltin - - assert.is.equal 'builtin', got.type - assert.is.equal TestBuiltin, got.value - - it 'wraps Scopes', -> - sub = Scope! - got = ValueStream.wrap sub - - assert.is.equal 'scope', got.type - assert.is.equal sub, got.value - - it 'wraps tables', -> - pi = ValueStream 'num', 3.14 - got = ValueStream.wrap { :pi } - - assert.is.equal 'scope', got.type - assert.is.equal pi, (got.value\get 'pi')\const! - - describe ':unwrap', -> - it 'returns the raw value!', -> - assert.is.equal 3.14, (ValueStream.num 3.14)\unwrap! - assert.is.equal 'hi', (ValueStream.str 'hi')\unwrap! - assert.is.equal 'hi', (ValueStream.sym 'hi')\unwrap! - - test 'can assert the type', -> - assert.is.equal 3.14, (ValueStream.num 3.14)\unwrap 'num' - assert.is.equal 'hi', (ValueStream.str 'hi')\unwrap 'str' - assert.is.equal 'hi', (ValueStream.sym 'hi')\unwrap 'sym' - assert.has_error -> (ValueStream.num 3.14)\unwrap 'sym' - assert.has_error -> (ValueStream.str 'hi')\unwrap 'num' - assert.has_error -> (ValueStream.sym 'hi')\unwrap 'str' - - test 'has __call shorthand', -> - assert.is.equal 3.14, (ValueStream.num 3.14)! - assert.is.equal 'hi', (ValueStream.str 'hi')! - assert.is.equal 'hi', (ValueStream.sym 'hi')! - assert.is.equal 3.14, (ValueStream.num 3.14) 'num' - assert.is.equal 'hi', (ValueStream.str 'hi') 'str' - assert.is.equal 'hi', (ValueStream.sym 'hi') 'sym' - assert.has_error -> (ValueStream.num 3.14) 'sym' - assert.has_error -> (ValueStream.str 'hi') 'num' - assert.has_error -> (ValueStream.sym 'hi') 'str' - - describe 'overrides __eq', -> - it 'compares the type', -> - val = ValueStream 'num', 3 - assert.is.equal (ValueStream.num 3), val - assert.not.equal (ValueStream.str '3'), val - - val = ValueStream 'str', 'hello' - assert.is.equal (ValueStream.str 'hello'), val - assert.not.equal (ValueStream.sym 'hello'), val - - it 'compares the value', -> - val = ValueStream 'num', 3 - assert.is.equal (ValueStream.num 3), val - assert.not.equal (ValueStream.num 4), val - - describe ':set', -> - it 'sets the value', -> - val = ValueStream 'num', 3 - assert.is.equal (ValueStream.num 3), val - - val\set 4 - assert.is.equal (ValueStream.num 4), val - assert.not.equal (ValueStream.num 3), val - - it 'marks the value dirty', -> - val = ValueStream 'num', 3 - assert.is.false val\dirty! - - val\set 4 - assert.is.true val\dirty! - - describe ':eval', -> - it 'turns numbers into consts', -> - assert_noop = (val) -> - assert.is.equal val, val\eval!\const! - - assert_noop ValueStream.num 2 - assert_noop ValueStream.str 'hello' - - it 'looks up symbols in the scope', -> - scope = with Scope! - \set 'number', Result value: ValueStream.num 3 - \set 'hello', Result value: ValueStream.str "world" - \set 'goodbye', Result value: ValueStream.sym "again" - - assert_eval = (sym, val) -> - const = ValueStream.sym sym - assert.is.equal val, (const\eval scope)\const! - - assert_eval 'number', ValueStream.num 3 - assert_eval 'hello', ValueStream.str "world" - assert_eval 'goodbye', ValueStream.sym "again" - - it ':quote s literals as themselves', -> - assert_noop = (val) -> assert.is.equal val, val\quote! - - assert_noop ValueStream.num 2 - assert_noop ValueStream.str 'hello' - assert_noop ValueStream.sym 'world' - - it ':clone sliterals as themselves', -> - assert_noop = (val) -> assert.is.equal val, val\clone! - - assert_noop ValueStream.num 2 - assert_noop ValueStream.str 'hello' - assert_noop ValueStream.sym 'world' - - describe ':fork', -> - it 'is equal to the original', -> - a = ValueStream.num 2 - b = ValueStream.str 'asdf' - c = with ValueStream '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 = ValueStream.num 3 - b = with ValueStream '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! - - reg\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/input_spec.moon b/spec/input_spec.moon new file mode 100644 index 0000000..131ed4c --- /dev/null +++ b/spec/input_spec.moon @@ -0,0 +1,153 @@ +import Input, Result, ValueStream, EventStream, IOStream from require 'alv.base' +import SimpleRegistry from require 'alv' +import Logger from require 'alv.logger' +Logger.init 'silent' + +reg = SimpleRegistry! +setup -> reg\grab! +teardown -> reg\release! + +class MyIO extends IOStream + new: => super 'my-io' + dirty: => @is_dirty + +basic_tests = (stream, input) -> + it 'gives access to the Stream', -> + assert.is.equal stream, input.stream + + it 'forwards :unwrap', -> + assert.is.same stream\unwrap!, input\unwrap! + assert.is.same stream\unwrap!, input! + + it 'gives access to the type string', -> + assert.is.equal stream.type, input\type! + + it 'gives access to the metatype string', -> + assert.is.equal stream.metatype, input\metatype! + +describe 'Input.cold', -> + stream = ValueStream.num 1 + input = Input.cold stream + + basic_tests stream, input + + it 'is never dirty', -> + assert.is.false input\dirty! + stream\set 2 + assert.is.false input\dirty! + + input\setup nil + assert.is.false input\dirty! + input\finish_setup! + + new_input = Input.cold ValueStream.num 3 + new_input\setup input + assert.is.false new_input\dirty! + new_input.stream\set 4 + assert.is.false new_input\dirty! + input\finish_setup! + +describe 'Input.hot', -> + describe 'with EventStream', -> + stream = EventStream 'num' + input = Input.hot stream + + basic_tests stream, input + + it 'is marked for lifting', -> + assert.is.nil input.io + + it 'is dirty when the EventStream is dirty', -> + assert.is.false input\dirty! + assert.is.false stream\dirty! + + input\setup nil + assert.is.false input\dirty! + input\finish_setup! + + reg\next_tick! + stream\add 1 + + assert.is.true input\dirty! + assert.is.true stream\dirty! + + input\setup nil + assert.is.true input\dirty! + input\finish_setup! + + assert.is.true input\dirty! + assert.is.true stream\dirty! + + describe 'with IOStream', -> + stream = MyIO! + input = Input.hot stream + + basic_tests stream, input + + it 'is marked for lifting', -> + assert.is.true input.io + + it 'is dirty when the IOStream is dirty', -> + stream.is_dirty = false + + assert.is.false input\dirty! + assert.is.false stream\dirty! + + input\setup nil + assert.is.false input\dirty! + input\finish_setup! + + reg\next_tick! + stream.is_dirty = true + + assert.is.true input\dirty! + assert.is.true stream\dirty! + + input\setup nil + assert.is.true input\dirty! + input\finish_setup! + + assert.is.true input\dirty! + assert.is.true stream\dirty! + + describe 'with ValueStream', -> + stream = ValueStream.num 1 + local input + + describe 'at evaltime', -> + it 'is dirty when new', -> + assert.is.false stream\dirty! + + input = Input.hot stream + input\setup nil + assert.is.true input\dirty! + input\finish_setup! + + it 'is dirty when different', -> + newval = ValueStream.num 2 + + assert.is.false newval\dirty! + newinput = Input.hot newval + newinput\setup input + assert.is.true newinput\dirty! + newinput\finish_setup! + + it 'is not dirty when equal', -> + newval = ValueStream.num 1 + newval\set 1 + + assert.is.true newval\dirty! + newinput = Input.hot newval + newinput\setup input + assert.is.false newinput\dirty! + newinput\finish_setup! + + describe 'at runtime', -> + it 'is dirty when the stream is dirty', -> + stream\set 3 + assert.is.true stream\dirty! + assert.is.true input\dirty! + + reg\next_tick! + assert.is.false stream\dirty! + assert.is.false input\dirty! diff --git a/spec/match_spec.moon b/spec/match_spec.moon new file mode 100644 index 0000000..52911db --- /dev/null +++ b/spec/match_spec.moon @@ -0,0 +1,240 @@ +import val, evt from require 'alv.base.match' +import Result, ValueStream, EventStream from require 'alv' + +mk_val = (type, const) -> + value = ValueStream type + with Result :value + .side_inputs = { 'fake' } unless const + +mk_evt = (type, const) -> + value = EventStream type + with Result :value + .side_inputs = { 'fake' } unless const + +describe 'val and evt', -> + describe 'type-less shorthand', -> + it 'matches metatype', -> + str = mk_val 'str' + num = mk_val 'num' + assert.is.equal str, val!\match { str } + assert.is.equal num, val!\match { num } + assert.has.error -> evt!\match { str } + assert.has.error -> evt!\match { num } + + str = mk_evt 'str' + num = mk_evt 'num' + assert.is.equal str, evt!\match { str } + assert.is.equal num, evt!\match { num } + assert.has.error -> val!\match { str } + assert.has.error -> val!\match { num } + + it 'is in recall mode', -> + value = val! + 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 'event!', tostring evt! + assert.is.equal 'value', tostring val! + + describe 'typed shorthand', -> + it 'matches by metatype', -> + str = mk_val 'str' + num = mk_val 'num' + assert.is.equal str, val.str\match { str } + assert.is.equal num, val.num\match { num } + assert.has.error -> evt.str\match { str } + assert.has.error -> evt.num\match { num } + + str = mk_evt 'str' + num = mk_evt 'num' + assert.is.equal str, evt.str\match { str } + assert.is.equal num, evt.num\match { num } + assert.has.error -> val.str\match { str } + assert.has.error -> val.num\match { num } + + it 'matches by type', -> + str = mk_val 'str' + num = mk_val 'num' + assert.is.equal str, val.str\match { str } + assert.is.equal num, val.num\match { num } + assert.has.error -> val.num\match { str } + assert.has.error -> val.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 -> evt.num\match { str } + assert.has.error -> evt.str\match { num } + + it 'stringifies well', -> + assert.is.equal 'str!', tostring evt.str + assert.is.equal 'num!', tostring evt.num + assert.is.equal 'str', tostring val.str + assert.is.equal 'num', tostring val.num + +describe 'choice', -> + str = mk_val 'str' + num = mk_val 'num' + bool = mk_val 'bool' + choice = val.str / val.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 = (val! / evt!)! + 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 = val.str + val.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 = -val.str + val.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 = val.str + val.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 = val.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 = val.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 = val.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 = val.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 = val.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 'stringifies well', -> + assert.is.equal 'str{1-3}', tostring val.str*3 + assert.is.equal 'str{1-*}', tostring val.str*0 + assert.is.equal 'str{0-*}', tostring val.str^0 + assert.is.equal 'str{2-2}', tostring val.str\rep 2, 2 + +describe 'complex nesting', -> + bang = mk_evt 'bang' + str = mk_val 'str' + num = mk_val 'num' + pattern = -evt.bang + val.num*4 + (val.str + (val.num / val.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 { bang, num, num, num, num, num, num } + assert.has.error -> pattern\match { bang, bang, num } + assert.has.error -> pattern\match { num, str, num, str } + assert.has.error -> pattern\match { num, str, num, str, mk_val 'bool' } + + it 'stringifies well', -> + assert.is.equal '(bang!? num{1-4} (str (num | str)){0-*})', tostring pattern diff --git a/spec/parsing_spec.moon b/spec/parsing_spec.moon new file mode 100644 index 0000000..ce519a4 --- /dev/null +++ b/spec/parsing_spec.moon @@ -0,0 +1,152 @@ +import space, atom, expr, explist, cell, program, comment + from require 'alv.parsing' +import ValueStream from require 'alv' +import Logger from require 'alv.logger' +Logger.init 'silent' + +verify_parse = (parser, str) -> + with assert parser\match str + assert.is.equal str, \stringify! + +verify_parse_nope = (parser, str) -> + with assert parser\match str + without_nope = str\match '^(.*) nope$' + assert.is.equal without_nope, \stringify! + +describe 'atom parsing', -> + test 'symbols', -> + sym = verify_parse_nope atom, 'some-toast nope' + assert.is.equal 'sym', sym.type + assert.is.equal 'some-toast', sym\unwrap! + assert.is.equal 'some-toast', sym\stringify! + + describe 'numbers', -> + it 'parses ints', -> + num = verify_parse_nope atom, '1234 nope' + assert.is.equal 'num', num.type + assert.is.equal 1234, num\unwrap! + assert.is.equal '1234', num\stringify! + + it 'parses floats', -> + num = verify_parse_nope atom, '0.123 nope' + assert.is.equal 'num', num.type + assert.is.equal 0.123, num\unwrap! + + num = verify_parse_nope atom, '.123 nope' + assert.is.equal 'num', num.type + assert.is.equal 0.123, num\unwrap! + + num = verify_parse_nope atom, '0. nope' + assert.is.equal 'num', num.type + assert.is.equal 0, num\unwrap! + + describe 'strings', -> + it 'parses double-quote strings', -> + str = verify_parse_nope atom, '"help some stuff!" nope' + assert.is.equal 'str', str.type + assert.is.equal 'help some stuff!', str\unwrap! + + it 'parses single-quote strings', -> + str = verify_parse_nope atom, "'help some stuff!' nope" + assert.is.equal 'str', str.type + assert.is.equal "help some stuff!", str\unwrap! + + it 'handles escapes', -> + str = verify_parse_nope atom, '"string with \\"quote\\"s and \\\\" nope' + assert.is.equal 'str', str.type + assert.is.equal 'string with \"quote\"s and \\', str\unwrap! + + str = verify_parse_nope atom, "'string with \\'quote\\'s and \\\\' nope" + assert.is.equal 'str', str.type + assert.is.equal "string with \'quote\'s and \\", str\unwrap! + +describe 'Cell', -> + test 'basic parsing', -> + node = verify_parse cell, '( 3 ok-yes + "friend" )' + + assert.is.equal 3, #node.children + assert.is.equal (ValueStream.num 3), node.children[1] + assert.is.equal (ValueStream.sym 'ok-yes'), node.children[2] + assert.is.equal (ValueStream.str 'friend'), node.children[3] + + test 'tag parsing', -> + node = verify_parse cell, '([42]tagged 2)' + + 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 (ValueStream.num 3), node.children[1] + assert.is.equal (ValueStream.sym 'ok-yes'), node.children[2] + + it 'at the front of the string', -> + verify ' 3\tok-yes' + + 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/registry_spec.moon b/spec/registry_spec.moon new file mode 100644 index 0000000..82816ba --- /dev/null +++ b/spec/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/result_spec.moon b/spec/result_spec.moon new file mode 100644 index 0000000..643514e --- /dev/null +++ b/spec/result_spec.moon @@ -0,0 +1,184 @@ +import Result, Scope, SimpleRegistry from require 'alv' +import Input, Op, ValueStream, EventStream, IOStream from require 'alv.base' +import Logger from require 'alv.logger' +Logger.init 'silent' + +op_with_inputs = (inputs) -> + with Op! + \setup inputs if inputs + +result_with_sideinput = (value, input) -> + with Result :value + .side_inputs = { [value]: input } + +reg = SimpleRegistry! +setup -> reg\grab! +teardown -> reg\release! + +class DirtyIO extends IOStream + new: => super 'dirty-io' + dirty: => true + +describe 'Result', -> + it 'wraps value, children', -> + value = ValueStream.num 3 + + a = Result! + b = Result! + children = { a, b } + + result = Result :value, :children + + assert.is.equal value, result.value + assert.is.same children, result.children + + it ':type gets type and assets value', -> + result = Result value: ValueStream.num 2 + assert.is.equal 'num', result\type! + + result = Result! + assert.has.error -> result\type! + + it ':is_const', -> + value = ValueStream.num 2 + pure = Result :value + impure = result_with_sideinput value, {} + + assert.is.true pure\is_const! + assert.is.false impure\is_const! + + assert.is.equal value, pure\const! + assert.has.error -> impure\const! + assert.has.error (-> impure\const 'test'), 'test' + + it ':make_ref', -> + value = ValueStream.num 2 + input = Input.hot value + op = op_with_inputs { input } + thick = Result :value, :op, children: { Result!, Result! } + ref = thick\make_ref! + + assert ref + assert.is.equal thick.value, ref.value + assert.is.same thick.side_inputs, ref.side_inputs + assert.is.same {}, ref.children + assert.is.nil ref.op + + it 'lifts up inputs from op', -> + event = ValueStream 'bang', false + event_input = Input.hot event + + value = ValueStream 'num', 4 + value_input = Input.hot value + + op = op_with_inputs { event_input, value_input } + result = Result op: op, :value + + assert.is.equal op, result.op + assert.is.same { [event]: event_input, [value]: value_input }, + result.side_inputs + + it 'does not lift up op inputs that are also child values', -> + event = ValueStream 'bang', false + event_input = Input.hot event + + value = ValueStream 'num', 4 + value_input = Input.hot value + + op = op_with_inputs { event_input, value_input } + result = Result op: op, :value, children: { Result :value } + + assert.is.same { [event]: event_input }, result.side_inputs + + it 'lifts up side_inputs from children', -> + event_value = ValueStream 'bang', false + event_input = Input.hot event_value + event = Result op: op_with_inputs { event_input } + assert.is.same { [event_value]: event_input }, event.side_inputs + + value_value = ValueStream 'num', 4 + value_input = Input.hot value_value + value = Result op: op_with_inputs { value_input } + assert.is.same { [value_value]: value_input }, value.side_inputs + + result = Result children: { event, value } + assert.is.same { [event_value]: event_input, [value_value]: value_input }, + result.side_inputs + + describe ':tick', -> + local a_value, a_child, a_input + local b_value, b_child, b_input + before_each -> + a_value = EventStream 'num' + a_input = Input.hot a_value + a_child = result_with_sideinput a_value, a_input + + b_value = EventStream 'num' + b_input = Input.hot b_value + b_child = result_with_sideinput b_value, b_input + + it 'updates children when a side_input is dirty', -> + a_value\add 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' + + result = Result children: { a_child, b_child } + result\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' + + result = Result children: { a_child, b_child } + result\tick! + + assert.spy(a).was_not_called! + assert.spy(b).was_not_called! + + it 'updates op when any op-inputs are dirty', -> + a_value\add 1 + assert.is.true a_input\dirty! + assert.is.false b_input\dirty! + + op = op_with_inputs a: Input.hot a_value + s = spy.on op, 'tick' + + result = Result :op, children: { a_child, b_child } + result\tick! + + assert.spy(s).was_called_with match.ref op + + it 'early-outs when no op-inputs are dirty', -> + a_value\add 1 + assert.is.true a_input\dirty! + assert.is.false b_input\dirty! + + op = op_with_inputs { Input.hot b_value } + s = spy.on op, 'tick' + + result = Result :op, children: { a_child, b_child } + result\tick! + + assert.spy(s).was_not_called! + + describe ':tick_io', -> + it 'ticks IOs referenced in side_inputs', -> + io = DirtyIO! + input = Input.hot io + op = op_with_inputs { input } + result = Result :op + + s = spy.on io, 'tick' + assert.is.same { [io]: input }, result.side_inputs + result\tick_io! + + assert.spy(s).was_called_with match.ref io diff --git a/spec/scope_spec.moon b/spec/scope_spec.moon new file mode 100644 index 0000000..301512c --- /dev/null +++ b/spec/scope_spec.moon @@ -0,0 +1,159 @@ +import Scope, ValueStream, Result 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 = (value) -> Result :value + +describe 'Scope', -> + describe 'constifies', -> + scope = Scope! + + test 'numbers', -> + scope\set_raw 'num', 3 + + got = (scope\get 'num')\const! + assert.is.equal 'num', got.type + assert.is.equal 3, got.value + + test 'strings', -> + scope\set_raw 'str', "im a happy string" + + got = (scope\get 'str')\const! + assert.is.equal 'str', got.type + assert.is.equal "im a happy string", got.value + + test 'Values', -> + pi = ValueStream '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 'opdef', got.type + assert.is.equal TestOp, got.value + + test 'Scopes', -> + sub = Scope! + scope\set_raw 'sub', sub + + got = (scope\get 'sub')\const! + assert.is.equal 'scope', got.type + assert.is.equal sub, got.value + + test 'tables', -> + pi = ValueStream 'num', 3.14 + scope\set_raw 'math', { :pi } + + got = (scope\get 'math')\const! + assert.is.equal '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 'wraps Values in from_table', -> + pi = ValueStream '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 'num', got.type + assert.is.equal 3, got.value + + got = (scope\get 'str')\const! + assert.is.equal 'str', got.type + assert.is.equal "im a happy string", got.value + + assert.is.equal pi, (scope\get 'pi')\const! + + got = (scope\get 'test')\const! + assert.is.equal 'opdef', got.type + assert.is.equal TestOp, got.value + + got = (scope\get 'math')\const! + assert.is.equal 'scope', got.type + assert.is.equal pi, (scope\get 'math/pi')\const! + + it 'gets from nested scopes', -> + root = Scope! + a = Scope! + b = Scope! + + pi = ValueStream '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 ValueStream.num 1 + two = wrap_res ValueStream.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 'str', got.type + assert.is.equal "inherited string", got.value + + it 'can be shadowed', -> + scope\set_raw 'hidden', "overwritten" + + got = (scope\get 'hidden')\const! + assert.is.equal 'str', got.type + assert.is.equal "overwritten", got.value + + 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').value! + assert.is.equal 'dynamic', (merged\get '*dynamic*').value! + + it 'falls back to the immediate parent', -> + merged = Scope root + assert.is.equal 'normal', (merged\get '*dynamic*').value! + + it 'looks in self first', -> + merged = Scope root + merged\set_raw '*dynamic*', 'merged' + assert.is.equal 'merged', (merged\get '*dynamic*').value! + + it 'can resolve nested', -> + merged = Scope root, dyn_root + assert.is.equal 3, (merged\get '*nested*/value').value! diff --git a/spec/tag_spec.moon b/spec/tag_spec.moon new file mode 100644 index 0000000..e0f2023 --- /dev/null +++ b/spec/tag_spec.moon @@ -0,0 +1,94 @@ +import Tag from require 'alv.tag' +import Registry from require 'alv.registry' +import Logger from require 'alv.logger' +Logger.init 'silent' + +reg = Registry! +setup -> reg\begin_eval! +teardown -> reg\end_eval! + +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/value_spec.moon b/spec/value_spec.moon new file mode 100644 index 0000000..457557d --- /dev/null +++ b/spec/value_spec.moon @@ -0,0 +1,190 @@ +import ValueStream, Result, Scope, SimpleRegistry from require 'alv' +import Op, Builtin from require 'alv.base' +import Logger from require 'alv.logger' +Logger.init 'silent' + +class TestOp extends Op + new: (...) => super ... + +class TestBuiltin extends Builtin + new: (...) => + +reg = SimpleRegistry! +setup -> reg\grab! +teardown -> reg\release! + +describe 'ValueStream', -> + describe '.wrap', -> + it 'wraps numbers', -> + got = ValueStream.wrap 3 + assert.is.equal 'num', got.type + assert.is.equal 3, got.value + + it 'wraps strings', -> + got = ValueStream.wrap "im a happy string" + assert.is.equal 'str', got.type + assert.is.equal "im a happy string", got.value + + it 'wraps Values', -> + pi = ValueStream 'num', 3.14 + got = ValueStream.wrap pi + + assert.is.equal pi, got + + it 'wraps Opdefs', -> + got = ValueStream.wrap TestOp + + assert.is.equal 'opdef', got.type + assert.is.equal TestOp, got.value + + it 'wraps Bultins', -> + got = ValueStream.wrap TestBuiltin + + assert.is.equal 'builtin', got.type + assert.is.equal TestBuiltin, got.value + + it 'wraps Scopes', -> + sub = Scope! + got = ValueStream.wrap sub + + assert.is.equal 'scope', got.type + assert.is.equal sub, got.value + + it 'wraps tables', -> + pi = ValueStream 'num', 3.14 + got = ValueStream.wrap { :pi } + + assert.is.equal 'scope', got.type + assert.is.equal pi, (got.value\get 'pi')\const! + + describe ':unwrap', -> + it 'returns the raw value!', -> + assert.is.equal 3.14, (ValueStream.num 3.14)\unwrap! + assert.is.equal 'hi', (ValueStream.str 'hi')\unwrap! + assert.is.equal 'hi', (ValueStream.sym 'hi')\unwrap! + + test 'can assert the type', -> + assert.is.equal 3.14, (ValueStream.num 3.14)\unwrap 'num' + assert.is.equal 'hi', (ValueStream.str 'hi')\unwrap 'str' + assert.is.equal 'hi', (ValueStream.sym 'hi')\unwrap 'sym' + assert.has_error -> (ValueStream.num 3.14)\unwrap 'sym' + assert.has_error -> (ValueStream.str 'hi')\unwrap 'num' + assert.has_error -> (ValueStream.sym 'hi')\unwrap 'str' + + test 'has __call shorthand', -> + assert.is.equal 3.14, (ValueStream.num 3.14)! + assert.is.equal 'hi', (ValueStream.str 'hi')! + assert.is.equal 'hi', (ValueStream.sym 'hi')! + assert.is.equal 3.14, (ValueStream.num 3.14) 'num' + assert.is.equal 'hi', (ValueStream.str 'hi') 'str' + assert.is.equal 'hi', (ValueStream.sym 'hi') 'sym' + assert.has_error -> (ValueStream.num 3.14) 'sym' + assert.has_error -> (ValueStream.str 'hi') 'num' + assert.has_error -> (ValueStream.sym 'hi') 'str' + + describe 'overrides __eq', -> + it 'compares the type', -> + val = ValueStream 'num', 3 + assert.is.equal (ValueStream.num 3), val + assert.not.equal (ValueStream.str '3'), val + + val = ValueStream 'str', 'hello' + assert.is.equal (ValueStream.str 'hello'), val + assert.not.equal (ValueStream.sym 'hello'), val + + it 'compares the value', -> + val = ValueStream 'num', 3 + assert.is.equal (ValueStream.num 3), val + assert.not.equal (ValueStream.num 4), val + + describe ':set', -> + it 'sets the value', -> + val = ValueStream 'num', 3 + assert.is.equal (ValueStream.num 3), val + + val\set 4 + assert.is.equal (ValueStream.num 4), val + assert.not.equal (ValueStream.num 3), val + + it 'marks the value dirty', -> + val = ValueStream 'num', 3 + assert.is.false val\dirty! + + val\set 4 + assert.is.true val\dirty! + + describe ':eval', -> + it 'turns numbers into consts', -> + assert_noop = (val) -> + assert.is.equal val, val\eval!\const! + + assert_noop ValueStream.num 2 + assert_noop ValueStream.str 'hello' + + it 'looks up symbols in the scope', -> + scope = with Scope! + \set 'number', Result value: ValueStream.num 3 + \set 'hello', Result value: ValueStream.str "world" + \set 'goodbye', Result value: ValueStream.sym "again" + + assert_eval = (sym, val) -> + const = ValueStream.sym sym + assert.is.equal val, (const\eval scope)\const! + + assert_eval 'number', ValueStream.num 3 + assert_eval 'hello', ValueStream.str "world" + assert_eval 'goodbye', ValueStream.sym "again" + + it ':quote s literals as themselves', -> + assert_noop = (val) -> assert.is.equal val, val\quote! + + assert_noop ValueStream.num 2 + assert_noop ValueStream.str 'hello' + assert_noop ValueStream.sym 'world' + + it ':clone sliterals as themselves', -> + assert_noop = (val) -> assert.is.equal val, val\clone! + + assert_noop ValueStream.num 2 + assert_noop ValueStream.str 'hello' + assert_noop ValueStream.sym 'world' + + describe ':fork', -> + it 'is equal to the original', -> + a = ValueStream.num 2 + b = ValueStream.str 'asdf' + c = with ValueStream '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 = ValueStream.num 3 + b = with ValueStream '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! + + reg\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! -- cgit v1.2.3