git.s-ol.nu alive / 52564f6
rearrange spec, fix for Lua 5.1 s-ol 6 months ago
48 changed file(s) with 2905 addition(s) and 2908 deletion(s). Raw diff Collapse all Expand all
4040
4141 --- alias for `unwrap`.
4242 __call: (...) => @unwrap ...
43
44 --- compare two values.
45 --
46 -- Compares two `SigStream`s by comparing their types and their Lua values.
47 __eq: (other) => other.type == @type and @type\eq other.value, @value
4843
4944 --- Result metatype.
5045 -- @tfield string metatype (`=`)
185180 with Constant.wrap args.value
186181 .meta = args.meta if args.meta
187182
183 :__eq
184
188185 {
189186 :Constant
190187 }
6666 @value = value
6767 @updated = COPILOT.T
6868
69 :__eq
70
7169 --- the wrapped Lua value.
7270 -- @tfield any value
7371
8179 -- @tparam ?any value the Lua value to be accessed through `unwrap`
8280 new: (type, @value) => super type
8381
82 :__eq
83
8484 {
8585 :SigStream
8686 }
+0
-58
spec/cell_spec.moon less more
0 import do_setup from require 'spec.test_setup'
1 import Cell, RootCell from require 'alv.cell'
2 import Constant, Scope, Tag, SimpleRegistry, globals from require 'alv'
3
4 setup do_setup
5
6 hello_world = Cell.parse Tag.parse('2'), {
7 '', (Constant.sym 'hello'), ' ', (Constant.str 'world'), ''
8 }
9 two_plus_two = Cell.parse Tag.parse('3'), {
10 '', (Constant.sym '+'), ' ', (Constant.num 2), ' ', (Constant.num 2), ''
11 }
12
13 describe 'Cell', ->
14 describe 'when cloned', ->
15 parent = Tag.blank '1'
16 with hello_world\clone parent
17 it 'keeps children', ->
18 assert.is.equal Cell, .__class
19 assert.is.equal (Constant.sym 'hello'), \head!
20 assert.is.same { Constant.str 'world' }, \tail!
21
22 it 'clones the tag', ->
23 assert.is.equal hello_world.tag, .tag.original
24 assert.is.equal parent, .tag.parent
25
26 describe 'when evaluated', ->
27 it 'errors when empty', ->
28 cell = Cell.parse {''}
29 assert.has.error -> cell\eval globals
30
31 it 'evaluates its head', ->
32 head = Constant.sym 'trace'
33 cell = Cell.parse { '', head, ' ', (Constant.sym 'true'), '' }
34
35 s = spy.on head, 'eval'
36 cell\eval globals
37 assert.spy(s).was_called_with (match.is_ref head), (match.is_ref globals)
38
39 describe 'RootCell', ->
40 test 'tag is always [0]', ->
41 cell = Cell.parse_root {}
42 assert.is.equal '[0]', cell.tag\stringify!
43
44 test 'head is always "do"', ->
45 cell = Cell.parse_root {}
46 assert.is.equal (Constant.sym 'do'), cell\head!
47
48 cell = RootCell nil, { hello_world, two_plus_two }
49 assert.is.equal (Constant.sym 'do'), cell\head!
50
51 test 'tail is all children', ->
52 cell = Cell.parse_root {}
53 assert.is.same {}, cell\tail!
54
55 cell = RootCell nil, { hello_world, two_plus_two }
56 assert.is.same { hello_world, two_plus_two },
57 cell\tail!
+0
-156
spec/input_spec.moon less more
0 import do_setup, do_teardown from require 'spec.test_setup'
1 import Input, T, Result from require 'alv.base'
2
3 setup do_setup
4 teardown do_teardown
5
6 basic_tests = (result, input) ->
7 it 'gives access to the Result', ->
8 assert.is.equal result, input.result
9
10 it 'forwards :unwrap', ->
11 assert.is.equal result\unwrap!, input\unwrap!, nil
12 assert.is.equal result\unwrap!, input!, nil
13
14 it 'gives access to the type string', ->
15 assert.is.equal result.type, input\type!
16
17 it 'gives access to the metatype string', ->
18 assert.is.equal result.metatype, input\metatype!
19
20 describe 'Input.cold', ->
21 result = T.num\mk_sig 1
22 input = Input.cold result
23
24 basic_tests result, input
25
26 it 'is marked cold', ->
27 assert.is.equal 'cold', input.mode
28
29 it 'is never dirty', ->
30 assert.is.false input\dirty!
31 result\set 2
32 assert.is.false input\dirty!
33
34 input\setup nil
35 assert.is.false input\dirty!
36 input\finish_setup!
37
38 new_input = Input.cold T.num\mk_sig 3
39 new_input\setup input
40 assert.is.false new_input\dirty!
41 new_input.result\set 4
42 assert.is.false new_input\dirty!
43 input\finish_setup!
44
45 describe 'Input.hot', ->
46 describe 'with Constant', ->
47 result = T.num\mk_const 1
48 input = Input.hot result
49
50 basic_tests result, input
51
52 it 'is marked cold', ->
53 assert.is.equal 'cold', input.mode
54
55 describe 'at evaltime', ->
56 it 'is dirty when new', ->
57 assert.is.false result\dirty!
58
59 newinput = Input.hot result
60 newinput\setup nil
61 assert.is.true newinput\dirty!
62 newinput\finish_setup!
63
64 it 'is dirty when different', ->
65 newval = T.num\mk_const 2
66
67 assert.is.false newval\dirty!
68 newinput = Input.hot newval
69 newinput\setup input
70 assert.is.true newinput\dirty!
71 newinput\finish_setup!
72
73 it 'is not dirty when equal', ->
74 newval = T.num\mk_const 1
75
76 assert.is.false newval\dirty!
77 newinput = Input.hot newval
78 newinput\setup input
79 assert.is.false newinput\dirty!
80 newinput\finish_setup!
81
82 describe 'with EvtStream', ->
83 result = T.num\mk_evt!
84 input = Input.hot result
85
86 basic_tests result, input
87
88 it 'is marked hot', ->
89 assert.is.equal 'hot', input.mode
90
91 it 'is dirty when the EvtStream is dirty', ->
92 assert.is.false input\dirty!
93 assert.is.false result\dirty!
94
95 input\setup nil
96 assert.is.false input\dirty!
97 input\finish_setup!
98
99 COPILOT\next_tick!
100 result\set 1
101
102 assert.is.true input\dirty!
103 assert.is.true result\dirty!
104
105 input\setup nil
106 assert.is.true input\dirty!
107 input\finish_setup!
108
109 assert.is.true input\dirty!
110 assert.is.true result\dirty!
111
112 describe 'with SigStream', ->
113 result = T.num\mk_sig 1
114 local input
115
116 describe 'at evaltime', ->
117 it 'is dirty when new', ->
118 assert.is.false result\dirty!
119
120 input = Input.hot result
121 input\setup nil
122 assert.is.true input\dirty!
123 input\finish_setup!
124
125 it 'is dirty when different', ->
126 newval = T.num\mk_sig 2
127
128 assert.is.false newval\dirty!
129 newinput = Input.hot newval
130 newinput\setup input
131 assert.is.true newinput\dirty!
132 newinput\finish_setup!
133
134 it 'is not dirty when equal', ->
135 newval = T.num\mk_sig!
136 newval\set 1
137
138 assert.is.true newval\dirty!
139 newinput = Input.hot newval
140 newinput\setup input
141 assert.is.false newinput\dirty!
142 newinput\finish_setup!
143
144 it 'is marked hot', ->
145 assert.is.equal 'hot', input.mode
146
147 describe 'at runtime', ->
148 it 'is dirty when the result is dirty', ->
149 result\set 3
150 assert.is.true result\dirty!
151 assert.is.true input\dirty!
152
153 COPILOT\next_tick!
154 assert.is.false result\dirty!
155 assert.is.false input\dirty!
0 import do_setup from require 'spec.test_setup'
1 import Cell, RootCell from require 'alv.cell'
2 import Constant, Scope, Tag, SimpleRegistry, globals from require 'alv'
3
4 setup do_setup
5
6 hello_world = Cell.parse Tag.parse('2'), {
7 '', (Constant.sym 'hello'), ' ', (Constant.str 'world'), ''
8 }
9 two_plus_two = Cell.parse Tag.parse('3'), {
10 '', (Constant.sym '+'), ' ', (Constant.num 2), ' ', (Constant.num 2), ''
11 }
12
13 describe 'Cell', ->
14 describe 'when cloned', ->
15 parent = Tag.blank '1'
16 with hello_world\clone parent
17 it 'keeps children', ->
18 assert.is.equal Cell, .__class
19 assert.is.equal (Constant.sym 'hello'), \head!
20 assert.is.same { Constant.str 'world' }, \tail!
21
22 it 'clones the tag', ->
23 assert.is.equal hello_world.tag, .tag.original
24 assert.is.equal parent, .tag.parent
25
26 describe 'when evaluated', ->
27 it 'errors when empty', ->
28 cell = Cell.parse {''}
29 assert.has.error -> cell\eval globals
30
31 it 'evaluates its head', ->
32 head = Constant.sym 'trace'
33 cell = Cell.parse { '', head, ' ', (Constant.sym 'true'), '' }
34
35 s = spy.on head, 'eval'
36 cell\eval globals
37 assert.spy(s).was_called_with (match.is_ref head), (match.is_ref globals)
38
39 describe 'RootCell', ->
40 test 'tag is always [0]', ->
41 cell = Cell.parse_root {}
42 assert.is.equal '[0]', cell.tag\stringify!
43
44 test 'head is always "do"', ->
45 cell = Cell.parse_root {}
46 assert.is.equal (Constant.sym 'do'), cell\head!
47
48 cell = RootCell nil, { hello_world, two_plus_two }
49 assert.is.equal (Constant.sym 'do'), cell\head!
50
51 test 'tail is all children', ->
52 cell = Cell.parse_root {}
53 assert.is.same {}, cell\tail!
54
55 cell = RootCell nil, { hello_world, two_plus_two }
56 assert.is.same { hello_world, two_plus_two },
57 cell\tail!
0 import do_setup, do_teardown from require 'spec.test_setup'
1 import Input, T, Result from require 'alv.base'
2
3 setup do_setup
4 teardown do_teardown
5
6 basic_tests = (result, input) ->
7 it 'gives access to the Result', ->
8 assert.is.equal result, input.result
9
10 it 'forwards :unwrap', ->
11 assert.is.equal result\unwrap!, input\unwrap!, nil
12 assert.is.equal result\unwrap!, input!, nil
13
14 it 'gives access to the type string', ->
15 assert.is.equal result.type, input\type!
16
17 it 'gives access to the metatype string', ->
18 assert.is.equal result.metatype, input\metatype!
19
20 describe 'Input.cold', ->
21 result = T.num\mk_sig 1
22 input = Input.cold result
23
24 basic_tests result, input
25
26 it 'is marked cold', ->
27 assert.is.equal 'cold', input.mode
28
29 it 'is never dirty', ->
30 assert.is.false input\dirty!
31 result\set 2
32 assert.is.false input\dirty!
33
34 input\setup nil
35 assert.is.false input\dirty!
36 input\finish_setup!
37
38 new_input = Input.cold T.num\mk_sig 3
39 new_input\setup input
40 assert.is.false new_input\dirty!
41 new_input.result\set 4
42 assert.is.false new_input\dirty!
43 input\finish_setup!
44
45 describe 'Input.hot', ->
46 describe 'with Constant', ->
47 result = T.num\mk_const 1
48 input = Input.hot result
49
50 basic_tests result, input
51
52 it 'is marked cold', ->
53 assert.is.equal 'cold', input.mode
54
55 describe 'at evaltime', ->
56 it 'is dirty when new', ->
57 assert.is.false result\dirty!
58
59 newinput = Input.hot result
60 newinput\setup nil
61 assert.is.true newinput\dirty!
62 newinput\finish_setup!
63
64 it 'is dirty when different', ->
65 newval = T.num\mk_const 2
66
67 assert.is.false newval\dirty!
68 newinput = Input.hot newval
69 newinput\setup input
70 assert.is.true newinput\dirty!
71 newinput\finish_setup!
72
73 it 'is not dirty when equal', ->
74 newval = T.num\mk_const 1
75
76 assert.is.false newval\dirty!
77 newinput = Input.hot newval
78 newinput\setup input
79 assert.is.false newinput\dirty!
80 newinput\finish_setup!
81
82 describe 'with EvtStream', ->
83 result = T.num\mk_evt!
84 input = Input.hot result
85
86 basic_tests result, input
87
88 it 'is marked hot', ->
89 assert.is.equal 'hot', input.mode
90
91 it 'is dirty when the EvtStream is dirty', ->
92 assert.is.false input\dirty!
93 assert.is.false result\dirty!
94
95 input\setup nil
96 assert.is.false input\dirty!
97 input\finish_setup!
98
99 COPILOT\next_tick!
100 result\set 1
101
102 assert.is.true input\dirty!
103 assert.is.true result\dirty!
104
105 input\setup nil
106 assert.is.true input\dirty!
107 input\finish_setup!
108
109 assert.is.true input\dirty!
110 assert.is.true result\dirty!
111
112 describe 'with SigStream', ->
113 result = T.num\mk_sig 1
114 local input
115
116 describe 'at evaltime', ->
117 it 'is dirty when new', ->
118 assert.is.false result\dirty!
119
120 input = Input.hot result
121 input\setup nil
122 assert.is.true input\dirty!
123 input\finish_setup!
124
125 it 'is dirty when different', ->
126 newval = T.num\mk_sig 2
127
128 assert.is.false newval\dirty!
129 newinput = Input.hot newval
130 newinput\setup input
131 assert.is.true newinput\dirty!
132 newinput\finish_setup!
133
134 it 'is not dirty when equal', ->
135 newval = T.num\mk_sig!
136 newval\set 1
137
138 assert.is.true newval\dirty!
139 newinput = Input.hot newval
140 newinput\setup input
141 assert.is.false newinput\dirty!
142 newinput\finish_setup!
143
144 it 'is marked hot', ->
145 assert.is.equal 'hot', input.mode
146
147 describe 'at runtime', ->
148 it 'is dirty when the result is dirty', ->
149 result\set 3
150 assert.is.true result\dirty!
151 assert.is.true input\dirty!
152
153 COPILOT\next_tick!
154 assert.is.false result\dirty!
155 assert.is.false input\dirty!
0 import const, sig, evt, any from require 'alv.base.match'
1 import Op, Input from require 'alv.base'
2 import RTNode, T, Error from require 'alv'
3
4 op_with_inputs = (inputs) ->
5 with Op!
6 \setup inputs if inputs
7
8 mk_const = (type, const) ->
9 result = T[type]\mk_const true
10 RTNode :result, op: op_with_inputs { Input.hot result }
11
12 mk_val = (type, const) ->
13 result = T[type]\mk_sig true
14 RTNode :result, op: op_with_inputs { Input.hot result }
15
16 mk_evt = (type, const) ->
17 result = T[type]\mk_evt!
18 RTNode :result, op: op_with_inputs { Input.hot result }
19
20 describe 'sig and evt', ->
21 describe 'type-less shorthand', ->
22 it 'matches metatype', ->
23 str = mk_val 'str'
24 num = mk_val 'num'
25 assert.is.equal str, sig!\match { str }
26 assert.is.equal num, sig!\match { num }
27 assert.has.error -> const!\match { str }
28 assert.has.error -> const!\match { num }
29 assert.has.error -> evt!\match { str }
30 assert.has.error -> evt!\match { num }
31
32 str = mk_evt 'str'
33 num = mk_evt 'num'
34 assert.has.error -> sig!\match { str }
35 assert.has.error -> sig!\match { num }
36 assert.has.error -> const!\match { str }
37 assert.has.error -> const!\match { num }
38 assert.is.equal str, evt!\match { str }
39 assert.is.equal num, evt!\match { num }
40
41 str = mk_const 'str'
42 num = mk_const 'num'
43 assert.is.equal str, sig!\match { str }
44 assert.is.equal num, sig!\match { num }
45 assert.is.equal str, const!\match { str }
46 assert.is.equal num, const!\match { num }
47 assert.has.error -> evt!\match { str }
48 assert.has.error -> evt!\match { num }
49
50 it 'can recall the type', ->
51 value = sig!!
52 event = evt!!
53 two_equal_values = value + value
54 two_equal_events = event + event
55
56 str1 = mk_val 'str'
57 str2 = mk_val 'str'
58 num = mk_val 'num'
59 assert.is.same { str1, str2 }, two_equal_values\match { str1, str2 }
60 assert.is.same { str2, str1 }, two_equal_values\match { str2, str1 }
61 assert.is.same { num, num }, two_equal_values\match { num, num }
62 assert.has.error -> two_equal_values\match { str1, num }
63 assert.has.error -> two_equal_values\match { num, str2 }
64 assert.has.error -> two_equal_events\match { str1, str2 }
65
66 str1 = mk_evt 'str'
67 str2 = mk_evt 'str'
68 num = mk_evt 'num'
69 assert.is.same { str1, str2 }, two_equal_events\match { str1, str2 }
70 assert.is.same { str2, str1 }, two_equal_events\match { str2, str1 }
71 assert.is.same { num, num }, two_equal_events\match { num, num }
72 assert.has.error -> two_equal_events\match { str1, num }
73 assert.has.error -> two_equal_events\match { num, str2 }
74 assert.has.error -> two_equal_values\match { str1, str2 }
75
76 it 'stringifies well', ->
77 assert.is.equal 'any=', tostring const!
78 assert.is.equal 'any!', tostring evt!
79 assert.is.equal 'any~', tostring sig!
80
81 describe 'typed shorthand', ->
82 it 'matches by metatype', ->
83 str = mk_val 'str'
84 num = mk_val 'num'
85 assert.is.equal str, sig.str\match { str }
86 assert.is.equal num, sig.num\match { num }
87 assert.has.error -> const.str\match { str }
88 assert.has.error -> const.num\match { num }
89 assert.has.error -> evt.str\match { str }
90 assert.has.error -> evt.num\match { num }
91
92 str = mk_evt 'str'
93 num = mk_evt 'num'
94 assert.has.error -> sig.str\match { str }
95 assert.has.error -> sig.num\match { num }
96 assert.has.error -> const.str\match { str }
97 assert.has.error -> const.num\match { num }
98 assert.is.equal str, evt.str\match { str }
99 assert.is.equal num, evt.num\match { num }
100
101 str = mk_const 'str'
102 num = mk_const 'num'
103 assert.is.equal str, sig.str\match { str }
104 assert.is.equal num, sig.num\match { num }
105 assert.is.equal str, const.str\match { str }
106 assert.is.equal num, const.num\match { num }
107 assert.has.error -> evt.str\match { str }
108 assert.has.error -> evt.num\match { num }
109
110 it 'matches by type', ->
111 str = mk_const 'str'
112 num = mk_const 'num'
113 assert.is.equal str, sig.str\match { str }
114 assert.is.equal num, sig.num\match { num }
115 assert.is.equal str, const.str\match { str }
116 assert.is.equal num, const.num\match { num }
117 assert.has.error -> sig.num\match { str }
118 assert.has.error -> sig.str\match { num }
119
120 str = mk_val 'str'
121 num = mk_val 'num'
122 assert.is.equal str, sig.str\match { str }
123 assert.is.equal num, sig.num\match { num }
124 assert.has.error -> const.num\match { str }
125 assert.has.error -> const.str\match { num }
126 assert.has.error -> sig.num\match { str }
127 assert.has.error -> sig.str\match { num }
128
129 str = mk_evt 'str'
130 num = mk_evt 'num'
131 assert.is.equal str, evt.str\match { str }
132 assert.is.equal num, evt.num\match { num }
133 assert.has.error -> const.num\match { str }
134 assert.has.error -> const.str\match { num }
135 assert.has.error -> evt.num\match { str }
136 assert.has.error -> evt.str\match { num }
137
138 it 'stringifies well', ->
139 assert.is.equal 'str!', tostring evt.str
140 assert.is.equal 'num!', tostring evt.num
141 assert.is.equal 'str~', tostring sig.str
142 assert.is.equal 'num~', tostring sig.num
143 assert.is.equal 'str=', tostring const.str
144 assert.is.equal 'num=', tostring const.num
145
146 describe 'choice', ->
147 str = mk_val 'str'
148 num = mk_val 'num'
149 bool = mk_val 'bool'
150 choice = sig.str / sig.num
151
152 it 'matches either type', ->
153 assert.is.equal str, choice\match { str }
154 assert.is.equal num, choice\match { num }
155 assert.has.error -> choice\match { bool }
156
157 it 'can recall the choice', ->
158 same = choice!
159 assert.is.equal num, same\match { num }
160
161 same = same + same
162 assert.is.same { str, str }, same\match { str, str }
163 assert.is.same { num, num }, same\match { num, num }
164 assert.has.error -> same\match { str, num }
165 assert.has.error -> same\match { num, str }
166 assert.has.error -> same\match { bool, bool }
167
168 it 'makes inner types recall', ->
169 same = any!!
170 same = same + same
171 assert.is.same { str, str }, same\match { str, str }
172 assert.is.same { num, num }, same\match { num, num }
173 assert.is.same { bool, bool }, same\match { bool, bool }
174 assert.has.error -> same\match { str, num }
175 assert.has.error -> same\match { num, str }
176
177 it 'stringifies well', ->
178 assert.is.equal '(str~ | num~)', tostring choice
179
180 describe 'sequence', ->
181 str = mk_val 'str'
182 num = mk_val 'num'
183 bool = mk_evt 'bool'
184 seq = sig.str + sig.num + evt.bool
185
186 it 'matches all types in order', ->
187 assert.is.same { str, num, bool }, seq\match { str, num, bool }
188
189 it 'can assign non-numeric keys', ->
190 named = seq\named 'str', 'num', 'bool'
191 assert.is.same { :str, :num, :bool }, named\match { str, num, bool }
192 assert.is.same { str, num, bool }, seq\match { str, num, bool }
193
194 it 'fails if too little arguments', ->
195 assert.has.error -> seq\match { str, num }
196
197 it 'fails if too many arguments', ->
198 assert.has.error -> seq\match { str, num, bool, bool }
199
200 it 'can handle optional children', ->
201 opt = -sig.str + sig.num
202 assert.is.same { str, num }, opt\match { str, num }
203 assert.is.same { nil, num }, opt\match { num }
204 assert.has.error -> opt\match { str, str, num }
205 assert.has.error -> opt\match { str, num, num }
206
207 it 'can handle repeat children', ->
208 rep = sig.str + sig.num*2
209 assert.is.same { str, {num} }, rep\match { str, num }
210 assert.is.same { str, {num,num} }, rep\match { str, num, num }
211 assert.has.error -> rep\match { str }
212 assert.has.error -> rep\match { str, num, num, num }
213
214 it 'stringifies well', ->
215 assert.is.equal '(str~ num~ bool!)', tostring seq
216
217 describe 'repeat', ->
218 str = mk_val 'str'
219 num = mk_val 'num'
220
221 times = (n, arg) -> return for i=1,n do arg
222
223 it '*x is [1,x[', ->
224 rep = sig.str*3
225 assert.has.error -> rep\match (times 0, str)
226 assert.is.same (times 1, str), rep\match (times 1, str)
227 assert.is.same (times 2, str), rep\match (times 2, str)
228 assert.is.same (times 3, str), rep\match (times 3, str)
229 assert.has.error -> rep\match (times 4, str)
230 assert.has.error -> rep\match (times 3, num)
231
232 it '*0 is [1,[', ->
233 rep = sig.str*0
234 assert.has.error -> rep\match (times 0, str)
235 assert.is.same (times 1, str), rep\match (times 1, str)
236 assert.is.same (times 2, str), rep\match (times 2, str)
237 assert.is.same (times 20, str), rep\match (times 20, str)
238 assert.has.error -> rep\match (times 3, num)
239
240 it '^x is [0,x[', ->
241 rep = sig.str^3
242 assert.is.same {}, rep\match {}
243 assert.is.same (times 1, str), rep\match (times 1, str)
244 assert.is.same (times 2, str), rep\match (times 2, str)
245 assert.is.same (times 3, str), rep\match (times 3, str)
246 assert.has.error -> rep\match (times 4, str)
247 assert.has.error -> rep\match (times 3, num)
248
249 it '^0 is [0,[', ->
250 rep = sig.str^0
251 assert.is.same {}, rep\match {}
252 assert.is.same (times 1, str), rep\match (times 1, str)
253 assert.is.same (times 2, str), rep\match (times 2, str)
254 assert.is.same (times 20, str), rep\match (times 20, str)
255 assert.has.error -> rep\match (times 3, num)
256
257 it ':rep(min, max) does anything else', ->
258 rep = sig.str\rep 2, 2
259 assert.has.error -> rep\match {}
260 assert.has.error -> rep\match (times 1, str)
261 assert.is.same (times 2, str), rep\match (times 2, str)
262 assert.has.error -> rep\match (times 3, str)
263 assert.has.error -> rep\match (times 2, num)
264
265 it 'works with complex inner types', ->
266 rep = (sig.num + sig.str)\rep 2, 2
267 assert.has.error -> rep\match {}
268 assert.has.error -> rep\match {num, str}
269 assert.has.error -> rep\match {num, num}
270 assert.is.same {{num, str}, {num, str}}, rep\match {num, str, num, str}
271 assert.has.error -> rep\match {str, str, str, str}
272 assert.has.error -> rep\match {num, str, num, str, num, str}
273
274 it 'stringifies well', ->
275 assert.is.equal 'str~{1-3}', tostring sig.str*3
276 assert.is.equal 'str~{1-*}', tostring sig.str*0
277 assert.is.equal 'str~{0-*}', tostring sig.str^0
278 assert.is.equal 'str~{2-2}', tostring sig.str\rep 2, 2
279
280 describe 'complex nesting', ->
281 bang = mk_evt 'bang'
282 str = mk_val 'str'
283 num = mk_val 'num'
284 num_c = mk_const 'num'
285 pattern = -evt.bang + sig.num*4 +
286 (sig.str + (sig.num / sig.str))\named('key', 'val')^0
287
288 it 'just works', ->
289 assert.is.same { bang, { num, num }, {} }, pattern\match { bang, num, num }
290 assert.is.same { nil, { num }, { { key: str, val: num },
291 { key: str, val: str } } },
292 pattern\match { num, str, num, str, str }
293 assert.has.error -> pattern\match { num, str }
294 assert.has.error -> pattern\match { num_c, str }
295 assert.has.error -> pattern\match { bang, num_c, num, num_c, num, num, num }
296 assert.has.error -> pattern\match { bang, bang, num }
297 assert.has.error -> pattern\match { num, str, num_c, str }
298 assert.has.error -> pattern\match { num, str, num, str, mk_val 'bool' }
299
300 it 'stringifies well', ->
301 assert.is.equal '(bang!? num~{1-4} (str~ (num~ | str~)){0-*})', tostring pattern
302
303 it 'gives useful error feedback', ->
304 msg = "couldn't match arguments (num~ str~ bool~) against pattern #{pattern}"
305 err = Error 'argument', msg
306 assert.has.error (-> pattern\match { num, str, mk_val 'bool' }), err
0 import space, atom, expr, explist, cell, program, comment
1 from require 'alv.parsing'
2 import Constant from require 'alv'
3 import Logger from require 'alv.logger'
4 Logger\init 'silent'
5
6 verify_parse = (parser, val) ->
7 with assert parser\match val
8 assert.is.equal val, \stringify!
9
10 verify_parse_nope = (parser, val) ->
11 with assert parser\match val
12 without_nope = val\match '^(.*) nope$'
13 assert.is.equal without_nope, \stringify!
14
15 describe 'atom parsing', ->
16 test 'symbols', ->
17 sym = verify_parse_nope atom, 'some-toast nope'
18 assert.is.equal (Constant.sym 'some-toast'), sym
19
20 describe 'numbers', ->
21 it 'parses ints', ->
22 num = verify_parse_nope atom, '1234 nope'
23 assert.is.equal (Constant.num 1234), num
24
25 it 'parses floats', ->
26 num = verify_parse_nope atom, '0.123 nope'
27 assert.is.equal (Constant.num 0.123), num
28
29 num = verify_parse_nope atom, '.123 nope'
30 assert.is.equal (Constant.num 0.123), num
31
32 num = verify_parse_nope atom, '0. nope'
33 assert.is.equal (Constant.num 0), num
34
35 describe 'strings', ->
36 it 'parses double-quote strings', ->
37 str = verify_parse_nope atom, '"help some stuff!" nope'
38 assert.is.equal (Constant.str 'help some stuff!'), str
39
40 it 'parses single-quote strings', ->
41 str = verify_parse_nope atom, "'help some stuff!' nope"
42 assert.is.equal (Constant.str "help some stuff!"), str
43
44 it 'handles escapes', ->
45 str = verify_parse_nope atom, '"string with \\"quote\\"s and \\\\" nope'
46 assert.is.equal (Constant.str 'string with \"quote\"s and \\'), str
47
48 str = verify_parse_nope atom, "'string with \\'quote\\'s and \\\\' nope"
49 assert.is.equal (Constant.str "string with \'quote\'s and \\"), str
50
51 describe 'Cell', ->
52 test 'basic parsing', ->
53 node = verify_parse cell, '( 3 ok-yes
54 "friend" )'
55
56 assert.is.equal 3, #node.children
57 assert.is.equal (Constant.num 3), node.children[1]
58 assert.is.equal (Constant.sym 'ok-yes'), node.children[2]
59 assert.is.equal (Constant.str 'friend'), node.children[3]
60
61 test 'tag parsing', ->
62 node = verify_parse cell, '([42]tagged 2)'
63
64 assert.is.equal 2, #node.children
65 assert.is.equal 42, node.tag.value
66
67 test 'tag parsing with whitespace', ->
68 node = verify_parse cell, '([42]
69 tagged 2)'
70
71 assert.is.equal 2, #node.children
72 assert.is.equal 42, node.tag.value
73
74 describe 'RootCell parsing', ->
75 describe 'handles whitespace', ->
76 verify = (str) ->
77 node = verify_parse program, str
78
79 assert.is.equal 2, #node.children
80 assert.is.equal (Constant.num 3), node.children[1]
81 assert.is.equal (Constant.sym 'ok-yes'), node.children[2]
82
83 it 'at the front of the string', ->
84 verify ' 3\tok-yes'
85
86 it 'at the end of the string', ->
87 verify ' 3\tok-yes\n'
88
89 it 'everywhere', ->
90 verify ' 3\tok-yes\n'
91
92 test 'whitespace', ->
93 assert.is.equal ' ', space\match ' '
94 assert.is.equal '\n\t ', space\match '\n\t '
95
96 describe 'comments', ->
97 comment = comment / 1
98 it 'are parsed', ->
99 str = '#(this is a comment)'
100 assert.is.equal str, comment\match str
101
102 it 'extend to matching braces', ->
103 str = '#(this is a comment #(with nested comments))'
104 assert.is.equal str, comment\match str
105
106 it 'can nest', ->
107 str = '#(this is a comment (with nested parenthesis))'
108 assert.is.equal str, comment\match str
109
110 describe 'resynthesis', ->
111 test 'mixed parsing', ->
112 str = '( 3 ok-yes
113 "friend" )'
114 node = verify_parse program, str
115 assert.is.equal str, node\stringify!
116
117 test 'complex', ->
118 str = '
119 #(send a CC controlled LFO to /radius)
120 (osc "/radius" (lfo (cc 14)))
121
122 (osc rot
123 (step
124 #(whenever a kick is received...)
125 (note "kick")
126
127 #(..cycle through random rotation values)
128 (random-rot)
129 (random-rot)
130 (random-rot)
131 (random-rot)
132 )
133 ) '
134 matched = assert.is.truthy verify_parse program, str
135 assert.is.equal str, matched\stringify!
136
137 test 'nested tags', ->
138 str = '([2]a ([3]b))'
139 matched = assert.is.truthy verify_parse program, str
140 assert.is.equal str, matched\stringify!
0 import do_setup, do_teardown, invoke_op from require 'spec.test_setup'
1 import PureOp, Input, T, sig, any from require 'alv.base'
2 import RTNode from require 'alv'
3
4 setup do_setup
5 teardown do_teardown
6
7 class TestPureOp extends PureOp
8 pattern: (any.num / sig.str)*3
9 type: T.num
10 tick: => @out\set 1
11
12 literal = (result) ->
13 eval: ->
14 si = T.num\mk_sig!
15 RTNode :result, side_inputs: { [si]: Input.hot si }
16
17 describe 'PureOp', ->
18 it 'matches the pattern', ->
19 assert.has.error -> invoke_op TestPureOp, {}
20 assert.has.error -> invoke_op TestPureOp, { literal T.bool\mk_evt! }
21 invoke_op TestPureOp, { literal T.num\mk_const 1 }
22 invoke_op TestPureOp, { literal T.str\mk_const 'hello' }
23 invoke_op TestPureOp, { literal T.num\mk_evt! }
24
25 describe 'with constant inputs', ->
26 local rtn
27 it 'ticks once', ->
28 tiq = spy.on TestPureOp.__base, 'tick'
29 rtn = invoke_op TestPureOp, { T.num\mk_const(1), T.str\mk_const('hello') }
30 assert.spy(tiq).was_called_with match.is_ref(rtn.op), true
31 assert.is.equal 1, rtn.result!
32
33 it 'is constant', ->
34 assert.is.equal '=', rtn\metatype!
35
36 describe 'with signal inputs', ->
37 a = T.num\mk_sig 1
38 b = T.num\mk_sig 2
39 c = T.num\mk_sig 3
40 tiq = spy.on TestPureOp.__base, 'tick'
41 rtn = invoke_op TestPureOp, { (literal a), (literal b), (literal c) }
42 op = rtn.op
43
44 it 'sets up hot inputs', ->
45 assert.is.equal 3, #op.inputs
46 assert.is.equal a, op.inputs[1].result
47 assert.is.equal b, op.inputs[2].result
48 assert.is.equal c, op.inputs[3].result
49 assert.is.equal 'hot', op.inputs[1].mode
50 assert.is.equal 'hot', op.inputs[2].mode
51 assert.is.equal 'hot', op.inputs[3].mode
52 assert.spy(tiq).was_called!
53
54 it 'has signal output', ->
55 assert.is.equal '~', op.out.metatype
56
57 describe 'with event inputs', ->
58 a = T.num\mk_sig 1
59 b = T.num\mk_evt 2
60 c = T.num\mk_sig 3
61 tiq = spy.on TestPureOp.__base, 'tick'
62 rtn = invoke_op TestPureOp, { (literal a), (literal b), (literal c) }
63 op = rtn.op
64
65 it 'sets up hot input only for evt', ->
66 assert.is.equal 3, #op.inputs
67 assert.is.equal a, op.inputs[1].result
68 assert.is.equal b, op.inputs[2].result
69 assert.is.equal c, op.inputs[3].result
70 assert.is.equal 'cold', op.inputs[1].mode
71 assert.is.equal 'hot', op.inputs[2].mode
72 assert.is.equal 'cold', op.inputs[3].mode
73 assert.spy(tiq).was_not_called!
74
75 it 'has event output', ->
76 assert.is.equal '!', op.out.metatype
77
78 it 'only allows one event input', ->
79 a, b = T.num\mk_evt!, T.num\mk_evt!
80 assert.has.error -> invoke_op TestPureOp, { (literal a), (literal b) }
81
82 it 'supports nested input patterns', ->
83 class NestedInputOp extends PureOp
84 pattern: (any.num + sig.str)\named('a', 'b')\rep 2, 2
85 type: T.num
86 tick: => @out\set 1
87
88 num = T.num\mk_sig 1
89 str = T.str\mk_sig 'hello'
90 oth = T.num\mk_evt 2
91 args = { (literal num), (literal str), (literal oth), (literal str) }
92 rtn = invoke_op NestedInputOp, args
93 op = rtn.op
94
95 assert.is.equal 2, #op.inputs
96 assert.is.equal num, op.inputs[1].a.result
97 assert.is.equal str, op.inputs[1].b.result
98 assert.is.equal oth, op.inputs[2].a.result
99 assert.is.equal str, op.inputs[2].b.result
100 assert.is.equal 'cold', op.inputs[1].a.mode
101 assert.is.equal 'cold', op.inputs[1].b.mode
102 assert.is.equal 'hot', op.inputs[2].a.mode
103 assert.is.equal 'cold', op.inputs[2].b.mode
104
105 it 'supports dynamically generating the output type', ->
106 class DynamicOp extends PureOp
107 pattern: sig.num + any!
108 type: (inputs) => inputs[2]\type!
109 tick: => @out\set @inputs[2]!
110 typ = spy.on DynamicOp, 'type'
111
112 a = T.num\mk_sig 1
113 num = T.num\mk_sig 1
114 str = T.str\mk_sig 1
115 sym = T.sym\mk_evt 1
116
117 rtn = invoke_op DynamicOp, { (literal a), (literal num) }
118 assert.is.equal '~', rtn.result.metatype
119 assert.is.equal num, rtn.result
120
121 rtn = invoke_op DynamicOp, { (literal a), (literal str) }
122 assert.is.equal '~', rtn.result.metatype
123 assert.is.equal str, rtn.result
124
125 rtn = invoke_op DynamicOp, { (literal a), (literal sym) }
126 assert.is.equal '!', rtn.result.metatype
127 assert.is.equal T.sym, rtn.result.type
0 import Registry, Tag from require 'alv.registry'
1 import Logger from require 'alv.logger'
2 Logger\init 'silent'
3
4 mk = ->
5 mock destroy: =>
6
7 describe 'registry', ->
0 import do_setup from require 'spec.test_setup'
1 import Constant, RTNode, Scope, SimpleRegistry, T from require 'alv'
2 import Op, Builtin from require 'alv.base'
3
4 class TestOp extends Op
5 new: (...) => super ...
6
7 class TestBuiltin extends Builtin
8 new: (...) =>
9
10 setup do_setup
11
12 describe 'Constant', ->
13 it 'requires a value', ->
14 assert.has.error -> Constant.num!
15 assert.has.error -> Constant T.num
16 assert.has.no.error -> Constant T.bool, false
17
18 it 'stringifies well', ->
19 assert.is.equal "<num= 4>", tostring Constant.num 4
20 assert.is.equal "<bool= true>", tostring Constant.bool true
21 assert.is.equal "<bool= false>", tostring Constant.bool false
22
23 describe '.wrap', ->
24 it 'wraps numbers', ->
25 got = Constant.wrap 3
26 assert.is.equal T.num, got.type
27 assert.is.equal 3, got.value
28
29 it 'wraps strings', ->
30 got = Constant.wrap "im a happy string"
31 assert.is.equal T.str, got.type
32 assert.is.equal "im a happy string", got.value
33
34 it 'wraps Constants', ->
35 pi = Constant.num 3.14
36 got = Constant.wrap pi
37
38 assert.is.equal pi, got
39
40 it 'wraps Opdefs', ->
41 got = Constant.wrap TestOp
42
43 assert.is.equal T.opdef, got.type
44 assert.is.equal TestOp, got.value
45
46 it 'wraps Bultins', ->
47 got = Constant.wrap TestBuiltin
48
49 assert.is.equal T.builtin, got.type
50 assert.is.equal TestBuiltin, got.value
51
52 it 'wraps Scopes', ->
53 sub = Scope!
54 got = Constant.wrap sub
55
56 assert.is.equal T.scope, got.type
57 assert.is.equal sub, got.value
58
59 it 'wraps tables', ->
60 pi = Constant.num 3.14
61 got = Constant.wrap { :pi }
62
63 assert.is.equal T.scope, got.type
64 assert.is.equal pi, (got.value\get 'pi')\const!
65
66 describe ':unwrap', ->
67 it 'returns the raw value!', ->
68 assert.is.equal 3.14, (Constant.num 3.14)\unwrap!
69 assert.is.equal 'hi', (Constant.str 'hi')\unwrap!
70 assert.is.equal 'hi', (Constant.sym 'hi')\unwrap!
71
72 test 'can assert the type', ->
73 assert.is.equal 3.14, (Constant.num 3.14)\unwrap T.num
74 assert.is.equal 'hi', (Constant.str 'hi')\unwrap T.str
75 assert.is.equal 'hi', (Constant.sym 'hi')\unwrap T.sym
76 assert.has_error -> (Constant.num 3.14)\unwrap T.sym
77 assert.has_error -> (Constant.str 'hi')\unwrap T.num
78 assert.has_error -> (Constant.sym 'hi')\unwrap T.str
79
80 test 'has __call shorthand', ->
81 assert.is.equal 3.14, (Constant.num 3.14)!
82 assert.is.equal 'hi', (Constant.str 'hi')!
83 assert.is.equal 'hi', (Constant.sym 'hi')!
84 assert.is.equal 3.14, (Constant.num 3.14) T.num
85 assert.is.equal 'hi', (Constant.str 'hi') T.str
86 assert.is.equal 'hi', (Constant.sym 'hi') T.sym
87 assert.has_error -> (Constant.num 3.14) T.sym
88 assert.has_error -> (Constant.str 'hi') T.num
89 assert.has_error -> (Constant.sym 'hi') T.str
90
91 describe 'overrides __eq', ->
92 it 'compares the type', ->
93 val = Constant.num 3
94 assert.is.equal (Constant.num 3), val
95 assert.not.equal (Constant.str '3'), val
96
97 val = Constant.str 'hello'
98 assert.is.equal (Constant.str 'hello'), val
99 assert.not.equal (Constant.sym 'hello'), val
100
101 it 'compares the value', ->
102 val = Constant.num 3
103 assert.is.equal (Constant.num 3), val
104 assert.not.equal (Constant.num 4), val
105
106 it ':dirty is always false', ->
107 val = Constant.num 3
108 assert.is.false val\dirty!
109
110 val.value = 4
111 assert.is.false val\dirty!
112
113 describe ':eval', ->
114 it 'turns numbers into consts', ->
115 assert_noop = (val) ->
116 assert.is.equal val, val\eval!\const!
117
118 assert_noop Constant.num 2
119 assert_noop Constant.str 'hello'
120
121 it 'looks up symbols in the scope', ->
122 scope = with Scope!
123 \set 'number', RTNode result: Constant.num 3
124 \set 'hello', RTNode result: Constant.str "world"
125 \set 'goodbye', RTNode result: Constant.sym "again"
126
127 assert_eval = (sym, val) ->
128 const = Constant.sym sym
129 assert.is.equal val, (const\eval scope)\const!
130
131 assert_eval 'number', Constant.num 3
132 assert_eval 'hello', Constant.str "world"
133 assert_eval 'goodbye', Constant.sym "again"
134
135 it ':clones literals as themselves', ->
136 assert_noop = (val) -> assert.is.equal val, val\clone!
137
138 assert_noop Constant.num 2
139 assert_noop Constant.str 'hello'
140 assert_noop Constant.sym 'world'
141
142 describe ':fork', ->
143 it 'is equal to the original', ->
144 a = Constant.num 2
145 b = Constant.str 'asdf'
146 c = Constant T.weird, {}, '(raw)'
147
148 aa, bb, cc = a\fork!, b\fork!, c\fork!
149 assert.is.equal a, aa
150 assert.is.equal b, bb
151 assert.is.equal c, cc
152
153 assert.is.false aa\dirty!
154 assert.is.false bb\dirty!
155 assert.is.false cc\dirty!
156
157 assert.is.equal c.raw, cc.raw
0 import do_setup from require 'spec.test_setup'
1 import EvtStream, RTNode, Scope, SimpleRegistry, T from require 'alv'
2 import Op, Builtin from require 'alv.base'
3
4 setup do_setup
5
6 describe 'EvtStream', ->
7 it 'stringifies well', ->
8 number = EvtStream T.num
9 assert.is.equal "<num! nil>", tostring number
10
11 number\set 4
12 assert.is.equal "<num! 4>", tostring number
13
14 bool = EvtStream T.bool
15 bool\set true
16 assert.is.equal "<bool! true>", tostring bool
17
18 COPILOT\next_tick!
19
20 bool\set false
21 assert.is.equal "<bool! false>", tostring bool
22
23 describe ':unwrap', ->
24 it 'returns the set value', ->
25 stream = EvtStream T.num
26 assert.is.nil stream\unwrap!
27
28 stream\set 3.14
29 assert.is.equal 3.14, stream\unwrap!
30
31 it 'returns nil if not dirty', ->
32 stream = EvtStream T.num
33 assert.is.nil stream\unwrap!
34
35 stream\set 3.14
36 COPILOT\next_tick!
37 assert.is.nil stream\unwrap!
38
39 test 'can assert the type', ->
40 assert.is.nil (EvtStream T.num)\unwrap T.num
41 assert.is.nil (EvtStream T.str)\unwrap T.str
42 assert.is.nil (EvtStream T.sym)\unwrap T.sym
43 assert.has_error -> (EvtStream T.num)\unwrap T.sym
44 assert.has_error -> (EvtStream T.str)\unwrap T.num
45 assert.has_error -> (EvtStream T.sym)\unwrap T.str
46
47 test 'has __call shorthand', ->
48 stream = EvtStream T.num
49 assert.is.nil stream!
50
51 stream\set 3.14
52 assert.is.equal 3.14, stream!
53
54 describe ':set', ->
55 it 'sets the value', ->
56 stream = EvtStream T.num
57 assert.is.false stream\dirty!
58
59 stream\set 4
60 assert.is.equal 4, stream\unwrap!
61 assert.is.true stream\dirty!
62
63 COPILOT\next_tick!
64
65 assert.is.false stream\dirty!
66 stream\set 3
67 assert.is.equal 3, stream\unwrap!
68 assert.is.true stream\dirty!
69
70 it 'ignores nil values', ->
71 stream = EvtStream T.num
72 assert.is.nil stream\unwrap!
73 assert.is.false stream\dirty!
74
75 stream\set!
76 assert.is.nil stream\unwrap!
77 assert.is.false stream\dirty!
78
79 stream\set nil
80 assert.is.nil stream\unwrap!
81 assert.is.false stream\dirty!
82
83 stream\set false
84 assert.is.equal false, stream\unwrap!
85 assert.is.true stream\dirty!
86
87 it 'errors when set twice', ->
88 stream = EvtStream T.num
89 stream\set 1
90 assert.has.error -> stream\set 2
91 assert.is.equal 1, stream\unwrap!
92
93 it 'resets on the next tick', ->
94 stream = EvtStream T.num
95 stream\set 1
96
97 COPILOT\next_tick!
98
99 assert.is.false stream\dirty!
100 stream\set 2
101 assert.is.equal 2, stream\unwrap!
102 assert.is.true stream\dirty!
103
104 describe ':fork', ->
105 it 'is clean', ->
106 a = EvtStream T.num
107 b = EvtStream T.str
108 b\set 'asdf'
109
110 aa, bb = a\fork!, b\fork!
111 assert.is.nil aa!
112 assert.is.nil bb!
113 assert.is.false aa\dirty!
114 assert.is.false bb\dirty!
115
116 it 'leaves the original', ->
117 a = EvtStream T.num
118 b = EvtStream T.str
119 b\set 'asdf'
120
121 aa, bb = a\fork!, b\fork!
122 assert.is.nil a!
123 assert.is.equal 'asdf', b!
124 assert.is.false a\dirty!
125 assert.is.true b\dirty!
126
127 it 'isolates the original from the fork', ->
128 a = EvtStream T.num
129 b = EvtStream T.str
130
131 aa, bb = a\fork!, b\fork!
132 a\set 1
133 bb\set 2
134
135 assert.is.equal 1, a!
136 assert.is.true a\dirty!
137 assert.is.nil aa!
138 assert.is.false aa\dirty!
139
140 assert.is.equal 2, bb!
141 assert.is.true bb\dirty!
142 assert.is.nil b!
143 assert.is.false b\dirty!
0 import do_setup from require 'spec.test_setup'
1 import SigStream, Constant, RTNode, Scope, SimpleRegistry, T from require 'alv'
2 import Op, Builtin from require 'alv.base'
3
4 setup do_setup
5
6 describe 'SigStream', ->
7 it 'stringifies well', ->
8 assert.is.equal "<num~ 4>", tostring SigStream T.num, 4
9 assert.is.equal "<bool~ true>", tostring SigStream T.bool, true
10 assert.is.equal "<bool~ false>", tostring SigStream T.bool, false
11
12 describe ':unwrap', ->
13 it 'returns the raw value!', ->
14 assert.is.equal 3.14, (SigStream T.num, 3.14)\unwrap!
15 assert.is.equal 'hi', (SigStream T.str, 'hi')\unwrap!
16 assert.is.equal 'hi', (SigStream T.sym, 'hi')\unwrap!
17
18 test 'can assert the type', ->
19 assert.is.equal 3.14, (SigStream T.num, 3.14)\unwrap T.num
20 assert.is.equal 'hi', (SigStream T.str, 'hi')\unwrap T.str
21 assert.is.equal 'hi', (SigStream T.sym, 'hi')\unwrap T.sym
22 assert.has_error -> (SigStream T.num, 3.14)\unwrap T.sym
23 assert.has_error -> (SigStream T.str, 'hi')\unwrap T.num
24 assert.has_error -> (SigStream T.sym, 'hi')\unwrap T.str
25
26 test 'has __call shorthand', ->
27 assert.is.equal 3.14, (SigStream T.num, 3.14)!
28 assert.is.equal 'hi', (SigStream T.str, 'hi')!
29 assert.is.equal 'hi', (SigStream T.sym, 'hi')!
30 assert.is.equal 3.14, (SigStream T.num, 3.14) T.num
31 assert.is.equal 'hi', (SigStream T.str, 'hi') T.str
32 assert.is.equal 'hi', (SigStream T.sym, 'hi') T.sym
33 assert.has_error -> (SigStream T.num, 3.14) T.sym
34 assert.has_error -> (SigStream T.str, 'hi') T.num
35 assert.has_error -> (SigStream T.sym, 'hi') T.str
36
37 describe 'overrides __eq', ->
38 it 'compares the type', ->
39 val = SigStream T.num, 3
40 assert.is.equal (SigStream T.num, 3), val
41 assert.not.equal (SigStream T.str, '3'), val
42
43 val = SigStream T.str, 'hello'
44 assert.is.equal (SigStream T.str, 'hello'), val
45 assert.not.equal (SigStream T.sym, 'hello'), val
46
47 it 'compares the value', ->
48 val = SigStream T.num, 3
49 assert.is.equal (SigStream T.num, 3), val
50 assert.not.equal (SigStream T.num, 4), val
51
52 it 'can be compared to a Constant', ->
53 val = SigStream T.num, 3
54 assert.is.equal (Constant.num 3), val
55 assert.not.equal (Constant.num 4), val
56
57 val = SigStream T.str, 'hello'
58 assert.is.equal (Constant.str 'hello'), val
59 assert.not.equal (Constant.sym 'hello'), val
60
61 describe ':set', ->
62 it 'sets the value', ->
63 val = SigStream T.num, 3
64 assert.is.equal (SigStream T.num, 3), val
65
66 val\set 4
67 assert.is.equal (SigStream T.num, 4), val
68 assert.not.equal (SigStream T.num, 3), val
69
70 it 'marks the value dirty', ->
71 val = SigStream T.num, 3
72 assert.is.false val\dirty!
73
74 val\set 4
75 assert.is.true val\dirty!
76
77 describe ':fork', ->
78 it 'is equal to the original', ->
79 a = SigStream T.num, 2
80 b = SigStream T.str, 'asdf'
81 c = with SigStream T.weird, {}, '(raw)'
82 \set {}
83
84 aa, bb, cc = a\fork!, b\fork!, c\fork!
85 assert.is.equal a, aa
86 assert.is.equal b, bb
87 assert.is.equal c, cc
88
89 assert.is.false aa\dirty!
90 assert.is.false bb\dirty!
91 assert.is.true cc\dirty!
92
93 assert.is.equal c.raw, cc.raw
94
95 it 'isolates the original from the fork', ->
96 a = SigStream T.num, 3
97 b = with SigStream T.weird, {}, '(raw)'
98 \set {}
99
100 aa, bb = a\fork!, b\fork!
101
102 bb\set {false}
103
104 assert.is.same {}, b!
105 assert.is.same {false}, bb!
106 assert.is.true b\dirty!
107 assert.is.true bb\dirty!
108
109 COPILOT\next_tick!
110 aa\set 4
111
112 assert.is.equal 3, a!
113 assert.is.equal 4, aa!
114 assert.is.false a\dirty!
115 assert.is.true aa\dirty!
0 import do_setup from require 'spec.test_setup'
1 import RTNode, Scope, SimpleRegistry from require 'alv'
2 import T, Input, Op, Constant from require 'alv.base'
3
4 class IOOp extends Op
5 new: (...) =>
6 super ...
7 @is_dirty = true
8
9 poll: => @is_dirty
10
11 make_op = (Class, inputs={}) ->
12 with Class!
13 \setup inputs
14
15 setup do_setup
16
17 describe 'RTNode', ->
18 describe 'constructor', ->
19 it 'takes result, children', ->
20 result = Constant.num 3
21
22 a = RTNode!
23 b = RTNode!
24 children = { a, b }
25
26 node = RTNode :result, :children
27
28 assert.is.equal result, node.result
29 assert.is.same children, node.children
30
31 it 'takes op and detects io_ops', ->
32 op = make_op Op
33 rtn = RTNode :op
34 assert.is.equal op, rtn.op
35 assert.is.same {}, rtn.io_ops
36
37 op = make_op IOOp
38 rtn = RTNode :op
39 assert.is.equal op, rtn.op
40 assert.is.same { op }, rtn.io_ops
41
42 describe 'detects Op inputs', ->
43 it '(hot -> side_input)', ->
44 sig = T.num\mk_sig!
45 inp = Input.hot sig
46 rtn = RTNode op: make_op Op, { inp }
47 assert.is.same { [sig]: inp }, rtn.side_inputs
48
49 it '(cold -> discard)', ->
50 rtn = RTNode op: make_op Op, { Input.cold T.num\mk_sig! }
51 assert.is.same {}, rtn.side_inputs
52
53 it "(childrens' results -> discard)", ->
54 child = RTNode op: make_op(Op), result: T.num\mk_sig 123
55 parent = RTNode children: { child }, op: make_op Op, { Input.hot child.result }
56 assert.is.same {}, parent.side_inputs
57
58 describe 'lifts up', ->
59 it 'side_inputs from children', ->
60 expected_inputs = {}
61 children = {}
62
63 for i=1,3
64 result = T.num\mk_sig!
65 input = Input.hot result
66 table.insert children, RTNode op: make_op Op, { input }
67 expected_inputs[result] = input
68
69 mid_rtn = RTNode children: children
70 out_rtn = RTNode children: { mid_rtn }
71
72 assert.is.same expected_inputs, mid_rtn.side_inputs
73 assert.is.same expected_inputs, out_rtn.side_inputs
74
75 it 'io_ops from children', ->
76 a_op = make_op IOOp
77 a_rtn = RTNode op: a_op
78
79 b_op = make_op IOOp
80 b_rtn = RTNode op: b_op
81
82 c_op = make_op Op
83 c_rtn = RTNode op: c_op
84
85 mid_rtn = RTNode children: { a_rtn, b_rtn, c_rtn }
86 out_op = make_op IOOp
87 out_rtn = RTNode children: { mid_rtn }, op: out_op
88
89 assert.is.same { a_op, b_op }, mid_rtn.io_ops
90 assert.is.same { a_op, b_op, out_op }, out_rtn.io_ops
91
92 it ':type gets type and assets value', ->
93 node = RTNode result: Constant.num 2
94 assert.is.equal T.num, node\type!
95
96 node = RTNode!
97 assert.has.error -> node\type!
98
99 it ':is_const', ->
100 result = Constant.num 2
101 pure = RTNode :result
102 impure = RTNode op: make_op Op, { Input.hot T.num\mk_sig! }
103
104 assert.is.true pure\is_const!
105 assert.is.false impure\is_const!
106
107 assert.is.equal result, pure\const!
108 assert.has.error -> impure\const!
109 assert.has.error (-> impure\const 'test'), 'test'
110
111 it ':make_ref', ->
112 result = T.num\mk_sig 2
113 input = Input.hot result
114 op = make_op IOOp, { input }
115 thick = RTNode :result, :op, children: { RTNode!, RTNode! }
116 ref = thick\make_ref!
117
118 assert ref
119 assert.is.equal thick.result, ref.result
120 assert.is.same thick.side_inputs, ref.side_inputs
121 assert.is.same {}, ref.children
122 assert.is.same {}, ref.io_ops
123 assert.is.nil ref.op
124
125 describe ':poll_io', ->
126 it 'polls all io_ops in tree', ->
127 child = RTNode op: make_op IOOp
128 node = RTNode children: { child }, op: make_op IOOp
129
130 sc = spy.on child.op, 'poll'
131 sn = spy.on node.op, 'poll'
132 node\poll_io!
133
134 assert.spy(sc).was_called_with match.ref child.op
135 assert.spy(sn).was_called_with match.ref node.op
136
137 it 'returns whether any ops were dirty', ->
138 child = RTNode op: make_op IOOp
139 node = RTNode children: { child }, op: make_op IOOp
140
141 assert.is.true node\poll_io!
142
143 child.op.is_dirty = false
144 assert.is.true node\poll_io!
145
146 node.op.is_dirty = false
147 assert.is.false node\poll_io!
148
149 child.op.is_dirty = true
150 assert.is.true node\poll_io!
151
152 describe ':tick', ->
153 local a_value, a_child, a_input
154 local b_value, b_child, b_input
155 before_each ->
156 a_value = T.num\mk_evt!
157 a_input = Input.hot a_value
158 a_child = RTNode op: make_op Op, { a_input }
159
160 b_value = T.num\mk_evt!
161 b_input = Input.hot b_value
162 b_child = RTNode op: make_op Op, { b_input }
163
164 it 'updates children when a side_input is dirty', ->
165 a_value\set 1
166 assert.is.true a_input\dirty!
167 assert.is.false b_input\dirty!
168
169 a = spy.on a_child, 'tick'
170 b = spy.on b_child, 'tick'
171
172 node = RTNode children: { a_child, b_child }
173 node\tick!
174
175 assert.spy(a).was_called_with match.ref a_child
176 assert.spy(b).was_called_with match.ref b_child
177
178 it 'early-outs when no side_inputs are dirty', ->
179 assert.is.false a_input\dirty!
180 assert.is.false b_input\dirty!
181
182 a = spy.on a_child, 'tick'
183 b = spy.on b_child, 'tick'
184
185 node = RTNode children: { a_child, b_child }
186 node\tick!
187
188 assert.spy(a).was_not_called!
189 assert.spy(b).was_not_called!
190
191 it 'updates op when any op-inputs are dirty', ->
192 a_value\set 1
193 assert.is.true a_input\dirty!
194 assert.is.false b_input\dirty!
195
196 op = make_op Op, { a: Input.hot a_value }
197 s = spy.on op, 'tick'
198
199 node = RTNode :op, children: { a_child, b_child }
200 node\tick!
201
202 assert.spy(s).was_called_with match.ref op
203
204 it 'early-outs when no op-inputs are dirty', ->
205 a_value\set 1
206 assert.is.true a_input\dirty!
207 assert.is.false b_input\dirty!
208
209 op = make_op Op, { Input.hot b_value }
210 s = spy.on op, 'tick'
211
212 node = RTNode :op, children: { a_child, b_child }
213 node\tick!
214
215 assert.spy(s).was_not_called!
0 import Scope, T, Constant, RTNode from require 'alv'
1 import Op from require 'alv.base'
2 import Logger from require 'alv.logger'
3 Logger\init 'silent'
4
5 class TestOp extends Op
6 new: (...) => super ...
7
8 wrap_res = (result) -> RTNode :result
9
10 describe 'Scope', ->
11 describe 'constifies', ->
12 scope = Scope!
13
14 test 'numbers', ->
15 scope\set_raw 'num', 3
16
17 got = (scope\get 'num')\const!
18 assert.is.equal (Constant.num 3), got
19
20 test 'strings', ->
21 scope\set_raw 'str', "im a happy string"
22
23 got = (scope\get 'str')\const!
24 assert.is.equal (Constant.str "im a happy string"), got
25
26 test 'Values', ->
27 pi = Constant.num 3.14
28 scope\set_raw 'pi', pi
29 assert.is.equal pi, (scope\get 'pi')\const!
30
31 test 'Opdefs', ->
32 scope\set_raw 'test', TestOp
33
34 got = (scope\get 'test')\const!
35 assert.is.equal TestOp, got T.opdef
36
37 test 'Scopes', ->
38 sub = Scope!
39 scope\set_raw 'sub', sub
40
41 got = (scope\get 'sub')\const!
42 assert.is.equal sub, got T.scope
43
44 test 'tables', ->
45 pi = Constant.num 3.14
46 scope\set_raw 'math', { :pi }
47
48 got = (scope\get 'math')\const!
49 assert.is.equal T.scope, got.type
50 assert.is.equal Scope, got.value.__class
51 assert.is.equal pi, (got.value\get 'pi')\const!
52 assert.is.equal pi, (scope\get 'math/pi')\const!
53
54 it 'stringifies well', ->
55 scope = Scope.from_table {
56 num: 3
57 str: "im a happy string"
58 :pi
59 math: :pi
60 test: TestOp
61 }
62 sub = Scope Scope scope
63
64 assert.is.equal "<Scope ^1 []>", tostring sub
65 assert.is.equal "<Scope ^-1 [math, num, str, test]>", tostring scope
66 scope\set_raw 'a', Constant.num 1
67 scope\set_raw 'b', Constant.num 2
68 scope\set_raw 'c', Constant.num 3
69 assert.is.equal "<Scope ^-1 [a, b, c, math, num, …]>", tostring scope
70
71 it 'wraps Values in from_table', ->
72 pi = Constant.num 3.14
73 scope = Scope.from_table {
74 num: 3
75 str: "im a happy string"
76 :pi
77 math: :pi
78 test: TestOp
79 }
80
81 got = (scope\get 'num')\const!
82 assert.is.equal 3, got T.num
83
84 got = (scope\get 'str')\const!
85 assert.is.equal "im a happy string", got T.str
86
87 assert.is.equal pi, (scope\get 'pi')\const!
88
89 got = (scope\get 'test')\const!
90 assert.is.equal TestOp, got T.opdef
91
92 got = (scope\get 'math')\const!
93 assert.is.equal T.scope, got.type
94 assert.is.equal pi, (scope\get 'math/pi')\const!
95
96 it 'gets from nested scopes', ->
97 root = Scope!
98 a = Scope!
99 b = Scope!
100
101 pi = Constant.num 3.14
102 b\set_raw 'test', pi
103 a\set_raw 'child', b
104 root\set_raw 'deep', a
105
106 assert.is.equal pi, (root\get 'deep/child/test')\const!
107
108 describe 'can set symbols', ->
109 one = wrap_res Constant.num 1
110 two = wrap_res Constant.num 2
111 scope = Scope!
112
113 it 'disallows re-setting symbols', ->
114 scope\set 'test', one
115 assert.is.equal one, scope\get 'test'
116
117 it 'throws if overwriting', ->
118 assert.has.error -> scope\set 'test', two
119 assert.is.equal one, scope\get 'test'
120
121 describe 'inheritance', ->
122 root = Scope!
123 root\set_raw 'hidden', 1234
124 root\set_raw 'inherited', "inherited string"
125
126 scope = Scope root
127
128 it 'allows access', ->
129 got = (scope\get 'inherited')\const!
130 assert.is.equal "inherited string", got T.str
131
132 it 'can be shadowed', ->
133 scope\set_raw 'hidden', "overwritten"
134
135 got = (scope\get 'hidden')\const!
136 assert.is.equal "overwritten", got T.str
137
138 describe 'dynamic inheritance', ->
139 root = Scope!
140 dyn_root = Scope!
141
142 root\set_raw 'normal', 'normal'
143 root\set_raw '*dynamic*', 'normal'
144 dyn_root\set_raw 'normal', 'dynamic'
145 dyn_root\set_raw '*dynamic*', 'dynamic'
146
147 dyn_root\set_raw '*nested*', { value: 3 }
148
149 it 'follows a different parent', ->
150 merged = Scope root, dyn_root
151 assert.is.equal 'normal', (merged\get 'normal').result!
152 assert.is.equal 'dynamic', (merged\get '*dynamic*').result!
153
154 it 'falls back to the immediate parent', ->
155 merged = Scope root
156 assert.is.equal 'normal', (merged\get '*dynamic*').result!
157
158 it 'looks in self first', ->
159 merged = Scope root
160 merged\set_raw '*dynamic*', 'merged'
161 assert.is.equal 'merged', (merged\get '*dynamic*').result!
162
163 it 'can resolve nested', ->
164 merged = Scope root, dyn_root
165 assert.is.equal 3, (merged\get '*nested*/value').result!
0 import do_setup from require 'spec.test_setup'
1 import Tag from require 'alv.tag'
2 import Registry from require 'alv.registry'
3
4 setup do_setup
5
6 describe 'Tag', ->
7 describe 'should be constructable', ->
8 it 'by parsing', ->
9 tag = Tag.parse '2'
10 assert tag
11 assert.is.equal 2, tag.value
12 assert.is.equal '[2]', tag\stringify!
13 assert.is.equal '2', tostring tag
14
15 it 'as blank Tags', ->
16 tag = Tag.blank!
17 assert tag
18 assert.is.nil tag.value
19 assert.is.equal '', tag\stringify!
20 assert.is.equal '?', tostring tag
21
22 describe 'should be clonable', ->
23 do_asserts = (tag, expect) ->
24 assert tag
25 assert.is.nil tag.value
26 assert.is.equal expect, tostring tag
27 assert.has.error tag\stringify
28
29 it 'from parsed tags', ->
30 parent = Tag.parse '1'
31 original = Tag.parse '2'
32 tag = original\clone parent
33 do_asserts tag, '1.2'
34
35 it 'but not from blank tags', ->
36 parent = Tag.parse '1'
37 original = Tag.blank!
38 tag = original\clone parent
39 do_asserts tag, '1.?'
40
41 it 'with blank parent', ->
42 parent = Tag.blank!
43 original = Tag.parse '2'
44 tag = original\clone parent
45 do_asserts tag, '?.2'
46
47 it 'completely blank', ->
48 parent = Tag.blank!
49 original = Tag.blank!
50 tag = original\clone parent
51 do_asserts tag, '?.?'
52
53 describe 'should be set-able', ->
54 it 'only if blank', ->
55 tag = Tag.parse '42'
56 assert.has.error -> tag\set 43
57
58 clone = tag\clone Tag.parse '3'
59 assert.has.error -> clone\set 42
60
61 clone = tag\clone Tag.blank!
62 assert.has.error -> clone\set 42
63
64 it 'and stores the value', ->
65 blank = Tag.blank!
66 blank\set 12
67
68 assert.is.equal blank.value, 12
69
70 it 'sets the original if cloned', ->
71 original = Tag.blank!
72 parent = Tag.parse '7'
73
74 o_set = spy.on original, 'set'
75 p_set = spy.on parent, 'set'
76
77 clone = original\clone parent
78 clone\set 11
79
80 assert.spy(o_set).was_called_with (match.is_ref original), 11
81 assert.spy(p_set).was_not_called!
82
83 assert.is.equal original.value, 11
84
85 it 'requires the parent to be registered if cloned', ->
86 original = Tag.blank!
87 parent = Tag.blank!
88
89 clone = original\clone parent
90 assert.has.error -> clone\set 11
0 require 'spec.test_setup'
1 import T, Type, Primitive, Array, Struct from require 'alv.type'
2 import ancestor from require 'alv.util'
3
4 bool = Primitive 'bool'
5 num = Primitive 'num'
6 str = Primitive 'str'
7
8 describe 'Primitive', ->
9 it 'inherits from Type', ->
10 assert.is.equal Type, ancestor Primitive.__class
11
12 it 'exposes name', ->
13 assert.is.equal 'bool', bool.name
14 assert.is.equal 'num', num.name
15 assert.is.equal 'str', str.name
16
17 it 'stringifies well', ->
18 assert.is.equal 'bool', tostring bool
19 assert.is.equal 'num', tostring num
20 assert.is.equal 'str', tostring str
21
22 it 'implements __eq sensibly', ->
23 assert.is.equal (Primitive 'bool'), bool
24 assert.is.equal (Primitive 'num'), num
25 assert.is.equal (Primitive 'str'), str
26 assert.not.equal num, bool
27 assert.not.equal str, num
28
29 it ':pp pretty-prints values', ->
30 assert.is.equal 'true', bool\pp true
31 assert.is.equal 'false', bool\pp false
32 assert.is.equal 'nil', bool\pp nil
33 assert.is.equal '4.134', num\pp 4.134
34 assert.is.equal '"hello"', str\pp "hello"
35
36 it ':eq compares values', ->
37 a, b = {}, {}
38 assert.is.true bool\eq true, true
39 assert.is.false bool\eq true, false
40 assert.is.true num\eq 1, 1
41 assert.is.false num\eq 1, -1
42 assert.is.true num\eq a, a
43 assert.is.false num\eq a, b
44
45 it ':get errors', ->
46 assert.has.error -> bool\get 'hello'
47 assert.has.error -> bool\get 2
48 assert.has.error -> bool\get!
49
50 describe 'Array', ->
51 vec3 = Array 3, num
52 str32 = Array 2, Array 3, str
53
54 it 'inherits from Type', ->
55 assert.is.equal Type, ancestor Array.__class
56
57 it 'exposes size', ->
58 assert.is.equal 3, vec3.size
59 assert.is.equal 2, str32.size
60
61 it 'exposes inner type', ->
62 assert.is.equal num, vec3.type
63 assert.is.equal (Array 3, str), str32.type
64
65 it 'stringifies well', ->
66 assert.is.equal 'num[3]', tostring vec3
67 assert.is.equal 'my-type[3][24]', tostring Array 24, Array 3, 'my-type'
68
69 it 'implements __eq sensibly', ->
70 assert.is.equal vec3, Array 3, num
71 assert.not.equal vec3, Array 2, num
72 assert.not.equal vec3, Array 3, str
73
74 it ':pp pretty-prints values', ->
75 assert.is.equal '[1 2 3]', vec3\pp { 1, 2, 3 }
76 assert.is.equal '[["a" "b" "c"] ["d" "e" "f"]]',
77 str32\pp { {'a', 'b', 'c'}, {'d', 'e', 'f'} }
78
79 it ':eq compares values', ->
80 a, b, c = {1, 2, 3}, {1, 2, 3}, {}
81 assert.is.true vec3\eq a, a
82 assert.is.true vec3\eq a, b
83 assert.is.false vec3\eq a, {1, 2, 4}
84 assert.is.true vec3\eq {1, 2, c}, {1, 2, c}
85 assert.is.false vec3\eq {1, 2, c}, {1, 2, {}}
86 assert.is.true str32\eq { {'a', 'b', 'c'}, {'d', 'e', 'f'} },
87 { {'a', 'b', 'c'}, {'d', 'e', 'f'} }
88 assert.is.false str32\eq { {'a', 'b', 'c'}, {'d', 'e', 'f'} },
89 { {'a', 'b', 'c'}, {'d', 'e', 'g'} }
90
91 it ':get verifies size range', ->
92 assert.has.error -> vec3\get -1
93 assert.is.equal num, vec3\get 0
94 assert.is.equal num, vec3\get 1
95 assert.is.equal num, vec3\get 2
96 assert.has.error -> vec3\get 3
97
98 assert.is.equal (Array 3, str), str32\get 1
99
100 assert.has.error -> vec3\get!
101 assert.has.error -> vec3\get 'fail'
102
103 describe 'Struct', ->
104 play = Struct { note: str, dur: num }
105 abc = Struct { c: num, b: num, a: num }
106
107 it 'inherits from Type', ->
108 assert.is.equal Type, ancestor Struct.__class
109
110 it 'exposes inner types', ->
111 assert.is.same { note: str, dur: num }, play.types
112 assert.is.same { c: num, b: num, a: num }, abc.types
113
114 it 'stringifies well', ->
115 assert.is.equal '{dur: num note: str}', tostring play
116 assert.is.equal '{a: num b: num c: num}', tostring abc
117
118 it 'implements __eq sensibly', ->
119 assert.is.equal play, Struct { note: str, dur: num }
120 assert.not.equal play, Struct { note: str }
121 assert.not.equal play, Struct { note: str, dur: str }
122 assert.not.equal play, Struct { note: str, dur: num, extra: num }
123
124 it ':pp pretty-prints values', ->
125 assert.is.equal '{dur: 0.5 note: "a"}', play\pp { dur: 0.5, note: 'a' }
126 assert.is.equal '{a: 1 b: 2 c: 3}', abc\pp { a: 1, b: 2, c: 3 }
127
128 it ':eq compares values', ->
129 a, b, c = { dur: 0.5, note: 'a' }, { dur: 0.5, note: 'a' }, {}
130 assert.is.true play\eq a, a
131 assert.is.true play\eq a, b
132 assert.is.false play\eq a, { dur: 0.5, note: 'b' }
133 assert.is.true play\eq { dur: 0.5, note: c }, { dur: 0.5, note: c }
134 assert.is.false play\eq { dur: 0.5, note: c }, { dur: 0.5, note: {} }
135
136 it ':get verifies key exists', ->
137 assert.is.equal str, play\get 'note'
138 assert.is.equal num, play\get 'dur'
139 assert.has.error -> play\get!
140 assert.has.error -> play\get ''
141 assert.has.error -> play\get 'something'
142
143 describe 'T', ->
144 it 'provides shorthand for Primitives', ->
145 assert.is.equal num, T.num
146 assert.is.equal str, T.str
147 assert.is.equal (Primitive 'midi/evt'), T['midi/evt']
148
149 for type in *{num, str, (Array 3, num)}
150 describe "#{type}", ->
151 describe ':mk_sig', ->
152 it 'can create value-less Streams', ->
153 stream = type\mk_sig!
154 assert.is.equal 'SigStream', stream.__class.__name
155 assert.is.equal type, stream.type
156 assert.is.nil stream!
157
158 it 'can take an initial value', ->
159 stream = type\mk_sig 4
160 assert.is.equal 'SigStream', stream.__class.__name
161 assert.is.equal type, stream.type
162 assert.is.equal 4, stream!
163
164 describe ':mk_const', ->
165 it 'takes a value', ->
166 stream = type\mk_const 4
167 assert.is.equal 'Constant', stream.__class.__name
168 assert.is.equal type, stream.type
169 assert.is.equal 4, stream!
170
171 describe ':mk_evt', ->
172 stream = type\mk_evt!
173 assert.is.equal 'EvtStream', stream.__class.__name
174 assert.is.equal type, stream.type
+0
-148
spec/lang/array_spec.moon less more
0 import TestPilot from require 'spec.test_setup'
1 import T, Array from require 'alv'
2
3 describe "array", ->
4 test = TestPilot '', '(import* array-)\n'
5
6 svec3 = Array 3, T.str
7
8 it "can contain any type", ->
9 COPILOT\eval_once '(array 1 2 3)'
10 COPILOT\eval_once '(array true false)'
11 COPILOT\eval_once '(array "a")'
12 COPILOT\eval_once '(array (array 1 2) (array 3 4))'
13
14 it "cannot contain mixed types", ->
15 err = assert.has.error -> COPILOT\eval_once '(array 1 false)'
16 assert.matches "argument error: couldn't match arguments", err
17
18 describe "(set)", ->
19 it "can swap values", ->
20 rt = COPILOT\eval_once '(set (array "f" "b" "c") 0 "a")'
21 assert.is.true rt\is_const!
22 assert.is.equal svec3\mk_const({ 'a', 'b', 'c' }), rt.result
23
24 it "checks value type", ->
25 err = assert.has.error -> COPILOT\eval_once '(set (array 1) 0 "a")'
26 assert.matches "expected value of type num, not str", err
27
28 it "checks index range", ->
29 err = assert.has.error -> COPILOT\eval_once '(set (array 1 2) -1 0)'
30 assert.matches "index '%-1' out of range!", err
31
32 COPILOT\eval_once '(set (array 1 2) 0 0)'
33
34 COPILOT\eval_once '(set (array 1 2) 1 0)'
35
36 err = assert.has.error -> COPILOT\eval_once '(set (array 1 2) 2 0)'
37 assert.matches "index '2' out of range!", err
38
39 describe "(get)", ->
40 it "can get a value", ->
41 rt = COPILOT\eval_once '(get (array 1 2) 0)'
42 assert.is.true rt\is_const!
43 assert.is.equal '<num= 1>', tostring rt.result
44
45 it "checks index range", ->
46 err = assert.has.error -> COPILOT\eval_once '(get (array 1 2) -1)'
47 assert.matches "index '%-1' out of range!", err
48
49 COPILOT\eval_once '(get (array 1 2) 0)'
50
51 COPILOT\eval_once '(get (array 1 2) 1)'
52
53 err = assert.has.error -> COPILOT\eval_once '(get (array 1 2) 2)'
54 assert.matches "index '2' out of range!", err
55
56 describe '(head)', ->
57 it "can peek a value", ->
58 rt = COPILOT\eval_once '(head (array 1 2))'
59 assert.is.true rt\is_const!
60 assert.is.equal '<num= 1>', tostring rt.result
61
62 describe '(tail)', ->
63 it "gets rest of an array", ->
64 rt = COPILOT\eval_once '(tail (array 1))'
65 assert.is.true rt\is_const!
66 assert.is.same (Array 0, T.num), rt.result.type
67 assert.is.same {}, rt.result!
68
69 rt = COPILOT\eval_once '(tail (array 1 2))'
70 assert.is.true rt\is_const!
71 assert.is.same (Array 1, T.num), rt.result.type
72 assert.is.same { 2 }, rt.result!
73
74 rt = COPILOT\eval_once '(tail (array 1 2 3 4))'
75 assert.is.true rt\is_const!
76 assert.is.same (Array 3, T.num), rt.result.type
77 assert.is.same { 2, 3, 4 }, rt.result!
78
79 describe '(prepend)', ->
80 it "prepends to array", ->
81 rt = COPILOT\eval_once '(prepend (array 2) 1)'
82 assert.is.true rt\is_const!
83 assert.is.same (Array 2, T.num), rt.result.type
84 assert.is.same { 1, 2 }, rt.result!
85
86 rt = COPILOT\eval_once '(prepend (array 2 3 4) 1)'
87 assert.is.true rt\is_const!
88 assert.is.same (Array 4, T.num), rt.result.type
89 assert.is.same { 1, 2, 3, 4 }, rt.result!
90
91 describe "(insert)", ->
92 it "can insert a value", ->
93 rt = COPILOT\eval_once '(insert (array "b" "c") 0 "a")'
94 assert.is.true rt\is_const!
95 assert.is.equal svec3\mk_const({ 'a', 'b', 'c' }), rt.result
96
97 rt = COPILOT\eval_once '(insert (array "a" "c") 1 "b")'
98 assert.is.true rt\is_const!
99 assert.is.equal svec3\mk_const({ 'a', 'b', 'c' }), rt.result
100
101 rt = COPILOT\eval_once '(insert (array "a" "b") 2 "c")'
102 assert.is.true rt\is_const!
103 assert.is.equal svec3\mk_const({ 'a', 'b', 'c' }), rt.result
104
105 it "checks index range", ->
106 err = assert.has.error -> COPILOT\eval_once '(insert (array 1 2) -1 0)'
107 assert.matches "index '%-1' out of range!", err
108
109 COPILOT\eval_once '(insert (array 1 2) 0 0)'
110
111 COPILOT\eval_once '(insert (array 1 2) 1 0)'
112
113 COPILOT\eval_once '(insert (array 1 2) 2 0)'
114
115 err = assert.has.error -> COPILOT\eval_once '(insert (array 1 2) 3 0)'
116 assert.matches "index '3' out of range!", err
117
118 describe "(remove)", ->
119 it "can remove a value", ->
120 rt = COPILOT\eval_once '(remove (array "d" "a" "b" "c") 0)'
121 assert.is.true rt\is_const!
122 assert.is.equal svec3\mk_const({ 'a', 'b', 'c' }), rt.result
123
124 rt = COPILOT\eval_once '(remove (array "a" "b" "c" "d") 3)'
125 assert.is.true rt\is_const!
126 assert.is.equal svec3\mk_const({ 'a', 'b', 'c' }), rt.result
127
128 it "checks index range", ->
129 err = assert.has.error -> COPILOT\eval_once '(remove (array 1 2 3) -1)'
130 assert.matches "index '%-1' out of range!", err
131
132 err = assert.has.error -> COPILOT\eval_once '(remove (array 1 2 3) 3)'
133 assert.matches "index '3' out of range!", err
134
135 it "can be concatenated with (concat)", ->
136 rt = COPILOT\eval_once '(concat (array "a" "b") (array "c"))'
137 assert.is.true rt\is_const!
138 assert.is.equal svec3\mk_const({ 'a', 'b', 'c' }), rt.result
139
140 it "size can be read using (size)", ->
141 rt = COPILOT\eval_once '(size (array 1))'
142 assert.is.true rt\is_const!
143 assert.is.equal '<num= 1>', tostring rt.result
144
145 rt = COPILOT\eval_once '(size (array 1 2 3))'
146 assert.is.true rt\is_const!
147 assert.is.equal '<num= 3>', tostring rt.result
+0
-92
spec/lang/cond_spec.moon less more
0 import TestPilot from require 'spec.test_setup'
1 import T, Struct, Array, Constant from require 'alv'
2
3 describe "if", ->
4 COPILOT = TestPilot!
5
6 it "checks truthiness", ->
7 for truthy in *{'true', '1', '-1', '1234', '(array 1 2 3)', '"test"', '""'}
8 with COPILOT\eval_once "(if #{truthy} 'yes' 'no')"
9 assert.is.true \is_const!
10 assert.is.equal 'yes', .result!
11
12 for falsy in *{'false', '0'}
13 with COPILOT\eval_once "(if #{falsy} 'yes' 'no')"
14 assert.is.true \is_const!
15 assert.is.equal 'no', .result!
16
17 it "can be used without else clause", ->
18 with COPILOT\eval_once '(if true "yes")'
19 assert.is.true \is_const!
20 assert.is.equal 'yes', .result!
21
22 with COPILOT\eval_once '(if false "yes")'
23 assert.is.true \is_const!
24 assert.is.equal nil, .result
25
26 it "doesn't evaluate the untaken branch", ->
27 with COPILOT\eval_once '(if true "yes" (error/doesnt-exist))'
28 assert.is.true \is_const!
29 assert.is.equal 'yes', .result!
30
31 with COPILOT\eval_once '(if false (error/doesnt-exist) "no")'
32 assert.is.true \is_const!
33 assert.is.equal 'no', .result!
34
35 it "errors on non-const choice", ->
36 err = assert.has.error ->
37 COPILOT\eval_once '(import* time) (if (ramp 1) "yes" "no")'
38 assert.matches "'if'%-expression needs to be constant", err
39
40 it "forwards any result", ->
41 with COPILOT\eval_once '
42 (import* time)
43 (if true (every 1 (array 1 2 3)))'
44 assert.is.false \is_const!
45 assert.is.equal '<num[3]! nil>', tostring .result
46
47 describe "when", ->
48 COPILOT = TestPilot!
49
50 it "checks truthiness", ->
51 for truthy in *{'true', '1', '-1', '1234', '(array 1 2 3)', '"test"', '""'}
52 with COPILOT\eval_once "(when #{truthy} 'yes')"
53 assert.is.true \is_const!
54 assert.is.equal 'yes', .result!
55
56 for falsy in *{'false', '0'}
57 with COPILOT\eval_once "(when #{falsy} 'yes')"
58 assert.is.true \is_const!
59 assert.is.nil .result
60
61 it "doesn't evaluate if falsy", ->
62 with COPILOT\eval_once '(when false (error/doesnt-exist))'
63 assert.is.true \is_const!
64 assert.is.nil, .result
65
66 with COPILOT\eval_once '
67 (when false
68 (error/doesnt-exist)
69 (error/doesnt-exist)
70 (error/doesnt-exist))'
71 assert.is.true \is_const!
72 assert.is.nil, .result
73
74 it "errors on non-const choice", ->
75 err = assert.has.error ->
76 COPILOT\eval_once '(import* time) (when (ramp 1) 1 2 3)'
77 assert.matches "'when'%-expression needs to be constant", err
78
79 it "forwards any result", ->
80 with COPILOT\eval_once '
81 (import* time)
82 (when true
83 (every 1 (array 1 2 3))
84 1 2 3)'
85 assert.is.false \is_const!
86 assert.is.equal '<num~ 3>', tostring .result
87
88 with COPILOT\eval_once '(when true (array 1 2 3))'
89 assert.is.true \is_const!
90 assert.is.equal '<num[3]= [1 2 3]>', tostring .result
91
+0
-27
spec/lang/do_spec.moon less more
0 import TestPilot from require 'spec.test_setup'
1
2 describe "do", ->
3 COPILOT = TestPilot!
4
5 it "can be empty", ->
6 with COPILOT\eval_once '(do)'
7 assert.is.true \is_const!
8 assert.is.nil .result
9
10 it "returns the last result, if any", ->
11 with COPILOT\eval_once '(do 1 2 3)'
12 assert.is.true \is_const!
13 assert.is.equal '<num= 3>', tostring .result
14
15 with COPILOT\eval_once '(do 1 2 (def _ 3))'
16 assert.is.true \is_const!
17 assert.is.nil .result
18
19 it "passes through side-effects", ->
20 with COPILOT\eval_once '
21 (import* time)
22 (do
23 (every 0.5 "bang! side-effect")
24 3)'
25 assert.is.false \is_const!
26 assert.is.equal '<num= 3>', tostring .result
+0
-51
spec/lang/fn_spec.moon less more
0 import TestPilot from require 'spec.test_setup'
1 import T, Struct, Array, Constant from require 'alv'
2
3 describe "function", ->
4 COPILOT = TestPilot!
5
6 it "returns constant results when constant", ->
7 rt = COPILOT\eval_once '
8 (import* math)
9
10 (defn my-plus (a b)
11 (+ a b))
12
13 (my-plus 2 3)'
14 assert.is.true rt\is_const!
15 assert.is.equal (Constant.num 5), rt.result
16
17 it "checks argument arity when invoked", ->
18 err = assert.has.error -> COPILOT\eval_once '
19 ([1]import* math)
20
21 ([2]defn my-plus (a b)
22 ([4]+ a b))
23
24 ([3]my-plus 2)'
25 assert.matches "argument error: expected 2 arguments, found 1", err
26 assert.matches "while invoking function 'my%-plus' at %[3%]", err
27
28 err = assert.has.error -> COPILOT\eval_once '
29 ([1]import* math)
30
31 ([2]defn my-plus (a b)
32 ([4]+ a b))
33
34 ([3]my-plus 2 3 4)'
35 assert.matches "argument error: expected 2 arguments, found 3", err
36 assert.matches "while invoking function 'my%-plus' at %[3%]", err
37
38 it "can be anonymously invoked", ->
39 rt = COPILOT\eval_once '
40 ([1]
41 ([2]fn (a b) b)
42 3 4)'
43 assert.is.equal (Constant.num 4), rt\const!
44
45 err = assert.has.error -> COPILOT\eval_once '
46 ([1]
47 ([2]fn (a b) b)
48 3)'
49 assert.matches "argument error: expected 2 arguments, found 1", err
50 assert.matches "while invoking function %(unnamed%) at %[1%]", err
+0
-33
spec/lang/literal_spec.moon less more
0 import TestPilot from require 'spec.test_setup'
1 import T, Struct, Array, Constant from require 'alv'
2
3 describe "literal", ->
4 TestPilot '
5 (def str "hello"
6 num 2
7 bool true
8 curl ([5]struct "a" 2 "b" false)
9 sqre ([7]array 1 2 3 4))
10 (export*)'
11
12 assert.is.true COPILOT.active_module.root\is_const!
13 scope = (assert COPILOT.active_module.root.result)\unwrap T.scope
14
15 it "string is parsed and returned correctly", ->
16 assert.is.equal (Constant.str 'hello'), (scope\get 'str')\const!
17
18 it "number is parsed and returned correctly", ->
19 assert.is.equal (Constant.num 2), (scope\get 'num')\const!
20
21 it "boolean is parsed and returned correctly", ->
22 assert.is.equal (Constant.bool true), (scope\get 'bool')\const!
23
24 it "struct is parsed and returned correctly", ->
25 struct = (scope\get 'curl')\const!
26 assert.is.equal (Struct a: T.num, b: T.bool), struct.type
27 assert.is.same { a: 2, b: false }, struct!
28
29 it "array is parsed and returned correctly", ->
30 array = (scope\get 'sqre')\const!
31 assert.is.equal (Array 4, T.num), array.type
32 assert.is.same {1, 2, 3, 4}, array!
+0
-264
spec/lang/logic_spec.moon less more
0 import TestPilot from require 'spec.test_setup'
1 import T, Array, Constant from require 'alv'
2
3 describe "logic", ->
4 test = TestPilot '', '(import* logic)\n'
5 TRUE = T.bool\mk_const true
6 FALSE = T.bool\mk_const false
7
8 describe "==", ->
9 it "can compare any type", ->
10 with COPILOT\eval_once '(== 1 1)'
11 assert.is.true \is_const!
12 assert.is.equal TRUE, .result
13
14 with COPILOT\eval_once '(== 1 2)'
15 assert.is.true \is_const!
16 assert.is.equal FALSE, .result
17
18 with COPILOT\eval_once '(== 1 "hello")'
19 assert.is.true \is_const!
20 assert.is.equal FALSE, .result
21
22 with COPILOT\eval_once '(== "hello" "hello")'
23 assert.is.true \is_const!
24 assert.is.equal TRUE, .result
25
26 with COPILOT\eval_once '(== (array 1 2 3) (array 1 2 3))'
27 assert.is.true \is_const!
28 assert.is.equal TRUE, .result
29
30 with COPILOT\eval_once '(== (array 1 2 3) (array 1 2 1))'
31 assert.is.true \is_const!
32 assert.is.equal FALSE, .result
33
34 with COPILOT\eval_once '(== (array 1 2 3) (array 1 2))'
35 assert.is.true \is_const!
36 assert.is.equal FALSE, .result
37
38 with COPILOT\eval_once '(== (array 1 2 3) (array 1 2 3 4))'
39 assert.is.true \is_const!
40 assert.is.equal FALSE, .result
41
42 with COPILOT\eval_once '(==
43 (struct "a" 1 "b" true "c" (array "test"))
44 (struct "a" 1 "b" true "c" (array "test")))'
45 assert.is.true \is_const!
46 assert.is.equal TRUE, .result
47
48 with COPILOT\eval_once '(==
49 (struct "a" 1 "b" false "c" (array "test"))
50 (struct "a" 1 "b" true "c" (array "test")))'
51 assert.is.true \is_const!
52 assert.is.equal FALSE, .result
53
54 with COPILOT\eval_once '(==
55 (struct "a" 1 "b" true "c" (array "test" "toast"))
56 (struct "a" 1 "b" true "c" (array "test")))'
57 assert.is.true \is_const!
58 assert.is.equal FALSE, .result
59
60 with COPILOT\eval_once '(==
61 (struct "a" 1 "b" true)
62 (struct "a" 1 "b" true "c" (array "test")))'
63 assert.is.true \is_const!
64 assert.is.equal FALSE, .result
65
66 with COPILOT\eval_once '(==
67 (struct "a" 1 "b" true)
68 (struct "a" 1))'
69 assert.is.true \is_const!
70 assert.is.equal FALSE, .result
71
72 with COPILOT\eval_once '(== print print)'
73 assert.is.true \is_const!
74 assert.is.equal TRUE, .result
75
76 with COPILOT\eval_once '(== print ==)'
77 assert.is.true \is_const!
78 assert.is.equal FALSE, .result
79
80 it "is aliased as eq", ->
81 with COPILOT\eval_once '(== eq ==)'
82 assert.is.true \is_const!
83 assert.is.equal TRUE, .result
84
85 describe "!=", ->
86 it "can compare any type", ->
87 with COPILOT\eval_once '(!= 1 1)'
88 assert.is.true \is_const!
89 assert.is.equal FALSE, .result
90
91 with COPILOT\eval_once '(!= 1 2)'
92 assert.is.true \is_const!
93 assert.is.equal TRUE, .result
94
95 with COPILOT\eval_once '(!= 1 "hello")'
96 assert.is.true \is_const!
97 assert.is.equal TRUE, .result
98
99 with COPILOT\eval_once '(!= "hello" "hello")'
100 assert.is.true \is_const!
101 assert.is.equal FALSE, .result
102
103 with COPILOT\eval_once '(!= (array 1 2 3) (array 1 2 3))'
104 assert.is.true \is_const!
105 assert.is.equal FALSE, .result
106
107 with COPILOT\eval_once '(!= (array 1 2 3) (array 1 2 1))'
108 assert.is.true \is_const!
109 assert.is.equal TRUE, .result
110
111 with COPILOT\eval_once '(!= (array 1 2 3) (array 1 2))'
112 assert.is.true \is_const!
113 assert.is.equal TRUE, .result
114
115 with COPILOT\eval_once '(!= (array 1 2 3) (array 1 2 3 4))'
116 assert.is.true \is_const!
117 assert.is.equal TRUE, .result
118
119 with COPILOT\eval_once '(!=
120 (struct "a" 1 "b" true "c" (array "test"))
121 (struct "a" 1 "b" true "c" (array "test")))'
122 assert.is.true \is_const!
123 assert.is.equal FALSE, .result
124
125 with COPILOT\eval_once '(!=
126 (struct "a" 1 "b" false "c" (array "test"))
127 (struct "a" 1 "b" true "c" (array "test")))'
128 assert.is.true \is_const!
129 assert.is.equal TRUE, .result
130
131 with COPILOT\eval_once '(!=
132 (struct "a" 1 "b" true "c" (array "test" "toast"))
133 (struct "a" 1 "b" true "c" (array "test")))'
134 assert.is.true \is_const!
135 assert.is.equal TRUE, .result
136
137 with COPILOT\eval_once '(!=
138 (struct "a" 1 "b" true)
139 (struct "a" 1 "b" true "c" (array "test")))'
140 assert.is.true \is_const!
141 assert.is.equal TRUE, .result
142
143 with COPILOT\eval_once '(!=
144 (struct "a" 1 "b" true)
145 (struct "a" 1))'
146 assert.is.true \is_const!
147 assert.is.equal TRUE, .result
148
149 with COPILOT\eval_once '(!= print print)'
150 assert.is.true \is_const!
151 assert.is.equal FALSE, .result
152
153 with COPILOT\eval_once '(!= print ==)'
154 assert.is.true \is_const!
155 assert.is.equal TRUE, .result
156
157 it "is aliased as note-eq", ->
158 with COPILOT\eval_once '(== not-eq !=)'
159 assert.is.true \is_const!
160 assert.is.equal TRUE, .result
161
162 describe "bool", ->
163 it "coerces numbers", ->
164 with COPILOT\eval_once '(bool 0)'
165 assert.is.true \is_const!
166 assert.is.equal FALSE, .result
167
168 with COPILOT\eval_once '(bool 1)'
169 assert.is.true \is_const!
170 assert.is.equal TRUE, .result
171
172 with COPILOT\eval_once '(bool -1)'
173 assert.is.true \is_const!
174 assert.is.equal TRUE, .result
175
176 with COPILOT\eval_once '(bool 1024)'
177 assert.is.true \is_const!
178 assert.is.equal TRUE, .result
179
180 it "accepts booleans", ->
181 with COPILOT\eval_once '(bool false)'
182 assert.is.true \is_const!
183 assert.is.equal FALSE, .result
184
185 with COPILOT\eval_once '(bool true)'
186 assert.is.true \is_const!
187 assert.is.equal TRUE, .result
188
189 describe "not", ->
190 it "accepts booleans", ->
191 with COPILOT\eval_once '(not false)'
192 assert.is.true \is_const!
193 assert.is.equal TRUE, .result
194
195 with COPILOT\eval_once '(not true)'
196 assert.is.true \is_const!
197 assert.is.equal FALSE, .result
198
199 it "coerces numbers", ->
200 with COPILOT\eval_once '(not 0)'
201 assert.is.true \is_const!
202 assert.is.equal TRUE, .result
203
204 with COPILOT\eval_once '(not 1)'
205 assert.is.true \is_const!
206 assert.is.equal FALSE, .result
207
208 with COPILOT\eval_once '(not -1)'
209 assert.is.true \is_const!
210 assert.is.equal FALSE, .result
211
212 with COPILOT\eval_once '(not 1024)'
213 assert.is.true \is_const!
214 assert.is.equal FALSE, .result
215
216
217 describe "or", ->
218 it "accepts any number of mixed arguments", ->
219 with COPILOT\eval_once '(or false 0)'
220 assert.is.true \is_const!
221 assert.is.equal FALSE, .result
222
223 with COPILOT\eval_once '(or 1 0)'
224 assert.is.true \is_const!
225 assert.is.equal TRUE, .result
226
227 with COPILOT\eval_once '(or 0 false 0 0 0 0)'
228 assert.is.true \is_const!
229 assert.is.equal FALSE, .result
230
231 with COPILOT\eval_once '(or 0 0 0 true 0 0)'
232 assert.is.true \is_const!
233 assert.is.equal TRUE, .result
234
235 with COPILOT\eval_once '(or 0 true 0 1 0 0)'
236 assert.is.true \is_const!
237 assert.is.equal TRUE, .result
238
239 describe "and", ->
240 it "accepts any number of mixed arguments", ->
241 with COPILOT\eval_once '(and false 1)'
242 assert.is.true \is_const!
243 assert.is.equal FALSE, .result
244
245 with COPILOT\eval_once '(and 1 true)'
246 assert.is.true \is_const!
247 assert.is.equal TRUE, .result
248
249 with COPILOT\eval_once '(and false 0)'
250 assert.is.true \is_const!
251 assert.is.equal FALSE, .result
252
253 with COPILOT\eval_once '(and 1 1 true 0)'
254 assert.is.true \is_const!
255 assert.is.equal FALSE, .result
256
257 with COPILOT\eval_once '(and 1 1 true true 1)'
258 assert.is.true \is_const!
259 assert.is.equal TRUE, .result
260
261 with COPILOT\eval_once '(and 1 1 1)'
262 assert.is.true \is_const!
263 assert.is.equal TRUE, .result
+0
-267
spec/lang/math_spec.moon less more
0 import TestPilot from require 'spec.test_setup'
1 import T, Array, Constant from require 'alv'
2
3 describe_both = (fn) ->
4 describe "math", ->
5 test = TestPilot '', '(import* testing math)\n'
6 fn!
7
8 describe "math-simple", ->
9 test = TestPilot '', '(import* testing math-simple)\n'
10 fn!
11
12 describe_both ->
13 describe "add, sub, mul, div, pow, mod", ->
14 it "are aliased as +-*/^%", ->
15 COPILOT\eval_once '
16 (expect= + add)
17 (expect= - sub)
18 (expect= * mul)
19 (expect= / div)
20 (expect= ^ pow)
21 (expect= % mod)
22 '
23
24 it "are sane", ->
25 COPILOT\eval_once '
26 (expect= 2 (+ 1 1))
27 (expect= 6 (+ 2 3 0 1))
28
29 (expect= -5 (- 2 7))
30 (expect= 0 (- 10 4 3 2 1 0))
31 (expect= -10 (- 10))
32
33 (expect= 1 (* 1 1))
34 (expect= -2 (* -1 2))
35 (expect= 14 (* 4 0.5 7))
36
37 (expect= 1 (/ 4 4))
38 (expect= -2 (/ -10 5))
39 (expect= 3 (/ -30 -10))
40
41 (expect= 1024 (^ 2 10))
42 (expect= 0.25 (^ 4 -1))
43 (expect= 1 (^ 999 0))
44
45 (expect= 4 (% 10 6))
46 (expect= 3 (% -2 5))
47 (expect= -2 (% -2 -5))
48 '
49
50 describe "trigonometric functions and constants", ->
51 COPILOT\eval_once '
52 (expect= tau (* pi 2))
53
54 #(darn you fp accuracy!
55 (expect= 0.5 (asin (sin 0.5)))
56 (expect= 0.5 (acos (cos 0.5)))
57 ...)
58 '
59
60 it "min, max, clamp, huge", ->
61 COPILOT\eval_once '
62 (expect= 0 (min 0 1 2))
63 (expect= 2 (max 0 1 2))
64
65 (expect= -2 (clamp -2 3.5 -4))
66 (expect= -1 (clamp -2 3.5 -1))
67 (expect= 0 (clamp -2 3.5 0))
68 (expect= 1 (clamp -2 3.5 1))
69 (expect= 3.5 (clamp -2 3.5 4))
70
71 (expect= (- huge) (min 0 1 -123456789 (- huge)))
72 (expect= huge (max 0 1 123456789 huge))
73 '
74
75 it "inc, dec", ->
76 COPILOT\eval_once '
77 (expect= 1 (inc 0))
78 (expect= -1 (dec 0))
79 (expect= 3 (inc (inc 1)))
80 (expect= -1 (dec (dec 1)))
81 (expect= 0 (inc (dec 0)) (dec (inc 0)))
82 '
83
84 describe "math", ->
85 test = TestPilot '', '(import* testing math)\n'
86
87 describe "add, sub, mul, div, pow, mod", ->
88 it "handle scalar/vector", ->
89 COPILOT\eval_once '
90 (expect= (array 3 4 5)
91 (+ 1 (array 1 2 3) 1))
92
93 (expect= (array 0 1 2)
94 (- (array 1 2 3) 1))
95
96 (expect= (array 3 6 9)
97 (* 3 (array 1 2 3)))
98 (expect= (array 3 6 9)
99 (* (array 1 2 3) 3))
100
101 (expect= (array 12 9 4)
102 (/ 36 (array 3 4 9)))
103
104 (expect= (array 9 16 25)
105 (^ (array 3 4 5) 2))
106 (expect= (array 1 2 4 8)
107 (^ 2 (array 0 1 2 3)))
108
109 (expect= (array 3 0 1)
110 (% (array 3 4 5) 4))
111 '
112
113 it "handle vector/vector and matrix/matrix", ->
114 COPILOT\eval_once '
115 (expect= (array 5 7 9)
116 (+ (array 1 2 3)
117 (array 4 5 6)))
118
119 (expect= (array (array 11 12)
120 (array 13 14))
121 (+
122 (array (array 1 2)
123 (array 3 4))
124 5
125 5))
126
127 (expect= (array 2 0 -2)
128 (- (array 3 2 1)
129 (array 1 2 3)))
130
131 (expect= (array 1 -2 -3)
132 (- (array -1 2 3)))
133 '
134
135 err = assert.has.error ->
136 COPILOT\eval_once '
137 (+ (array 1 2 3)
138 (array 1 2))'
139
140 err = assert.has.error ->
141 COPILOT\eval_once '
142 (+ (array (array 1 2) (array 1 2))
143 (array 1 2))'
144
145 err = assert.has.error ->
146 COPILOT\eval_once '
147 (- (array 1 2 3)
148 (array 1 2))'
149
150 err = assert.has.error ->
151 COPILOT\eval_once '
152 (- (array (array 1 2) (array 1 2))
153 (array 1 2))'
154
155 describe "mul", ->
156 it "handles scalars and matrices", ->
157 with COPILOT\eval_once '
158 (* 3
159 (array
160 (array 1 2)
161 (array 4 5)))'
162 assert.is.true \is_const!
163 assert.is.equal '<num[2][2]= [[3 6] [12 15]]>', tostring .result
164
165 with COPILOT\eval_once '
166 (* (array
167 (array 1 2)
168 (array 4 5))
169 3)'
170 assert.is.true \is_const!
171 assert.is.equal '<num[2][2]= [[3 6] [12 15]]>', tostring .result
172
173 it "handles vectors and matrices", ->
174 with COPILOT\eval_once '
175 (*
176 (array (array 1 0 0)
177 (array 0 1 0)
178 (array 0 0 1))
179 (array 4 5 6))'
180 assert.is.true \is_const!
181 assert.is.equal '<num[3]= [4 5 6]>', tostring .result
182
183 with COPILOT\eval_once '
184 (*
185 (array (array 1 0 0)
186 (array 0 1 0)
187 (array 3 2 1))
188 (array 4 5 1))'
189 assert.is.true \is_const!
190 assert.is.equal '<num[3]= [4 5 23]>', tostring .result
191
192 it "handles matrices", ->
193 with COPILOT\eval_once '
194 (*
195 (array (array 1 2 3)
196 (array 4 5 6))
197 (array (array 10 11)
198 (array 20 21)
199 (array 30 31)))'
200 assert.is.true \is_const!
201 assert.is.equal '<num[2][2]= [[140 146] [320 335]]>', tostring .result
202
203 it "handles everything mixed", ->
204 with COPILOT\eval_once '
205 (*
206 (array (array 1 2 3)
207 (array 4 5 6))
208 2
209 (array (array 10 11)
210 (array 20 21)
211 (array 30 31)))'
212 assert.is.true \is_const!
213 assert.is.equal '<num[2][2]= [[280 292] [640 670]]>', tostring .result
214
215 with COPILOT\eval_once '
216 (*
217 (array (array 1 2 3)
218 (array 4 5 6))
219 2
220 (array (array 10 11)
221 (array 20 21)
222 (array 30 31))
223 (array 4 7))'
224 assert.is.true \is_const!
225 assert.is.equal '<num[2]= [3164 7250]>', tostring .result
226
227 it "errors with wrong sizes (matrix and vector)", ->
228 err = assert.has.error -> COPILOT\eval_once '
229 (*
230 (array (array 1 2 3)
231 (array 4 5 6))
232 (array 1 2))'
233 -- assert.matches "", err
234
235 err = assert.has.error -> COPILOT\eval_once '
236 (*
237 (array 1 2 3)
238 (array (array 1 2 3)
239 (array 4 5 6)))'
240 -- assert.matches "", err
241
242 it "errors with wrong sizes (matrix)", ->
243 err = assert.has.error -> COPILOT\eval_once '
244 (*
245 (array (array 1 2 3)
246 (array 4 5 6))
247 (array (array 1 2)
248 (array 4 5)))'
249 -- assert.matches "", err
250
251 it "min, max, clamp, huge", ->
252 COPILOT\eval_once '
253 (expect= (array 3 2 1)
254 (min (array 3 4 1) (array 5 2 huge)))
255 (expect= (array 5 huge 4)
256 (max (array 3 huge 4) (array 5 999 2)))
257
258 (expect= (array -2 -1 0 1 3.5)
259 (clamp -2 3.5 (array -4 -1 0 1 4)))
260
261 (expect= 1 (inc 0))
262 (expect= -1 (dec 0))
263 (expect= 3 (inc (inc 1)))
264 (expect= -1 (dec (dec 1)))
265 (expect= 0 (inc (dec 0)) (dec (inc 0)))
266 '
+0
-51
spec/lang/struct_spec.moon less more
0 import TestPilot from require 'spec.test_setup'
1 import T, Struct from require 'alv'
2
3 describe "struct", ->
4 test = TestPilot '', '(import* struct-)\n'
5
6 ab = Struct { a: T.num, b: T.bool }
7
8 describe "(set)", ->
9 it "can update values", ->
10 rt = COPILOT\eval_once '(set (struct "a" 1 "b" false) "a" 2)'
11 assert.is.true rt\is_const!
12 assert.is.equal ab\mk_const({ a: 2, b: false }), rt.result
13
14 it "cannot add members", ->
15 err = assert.has.error -> COPILOT\eval_once '(set (struct "a" 1) "b" 2)'
16 assert.matches "{a: num} has no 'b' key", err
17
18 it "checks value type", ->
19 err = assert.has.error -> COPILOT\eval_once '(set (struct "a" 1) "a" "str")'
20 assert.matches "expected value for key 'a' to be num, not str", err
21
22 describe "(get)", ->
23 it "can get values", ->
24 rt = COPILOT\eval_once '(get (struct "a" 1 "b" false) "a")'
25 assert.is.true rt\is_const!
26 assert.is.equal '<num= 1>', tostring rt.result
27
28 it "checks keys", ->
29 err = assert.has.error -> COPILOT\eval_once '(get (struct "a" 1) "b")'
30 assert.matches "has no 'b' key", err
31
32 describe "(insert)", ->
33 it "can add members", ->
34 rt = COPILOT\eval_once '(insert (struct "b" true) "a" 1)'
35 assert.is.true rt\is_const!
36 assert.is.equal ab\mk_const({ a: 1, b: true }), rt.result
37
38 it "doesn't clobber existing members", ->
39 err = assert.has.error -> COPILOT\eval_once '(insert (struct "a" 1) "a" 2)'
40 assert.matches "key 'a' already exists in value of type {a: num}", err
41
42 describe "(remove)", ->
43 it "can remove members", ->
44 rt = COPILOT\eval_once '(remove (struct "a" 1 "b" false "c" "abc") "c")'
45 assert.is.true rt\is_const!
46 assert.is.equal ab\mk_const({ a: 1, b: false }), rt.result
47
48 it "checks keys", ->
49 err = assert.has.error -> COPILOT\eval_once '(remove (struct "a" 1) "b")'
50 assert.matches "has no 'b' key", err
+0
-80
spec/lang/testing_spec.moon less more
0 import TestPilot from require 'spec.test_setup'
1 import T, Array, Constant from require 'alv'
2
3 describe "testing", ->
4 test = TestPilot '', '(import* testing logic)\n'
5
6 describe "assert", ->
7 it "ignores true", ->
8 with COPILOT\eval_once '(assert true)'
9 assert.is.true \is_const!
10 assert.is.nil .result
11
12 with COPILOT\eval_once '(assert true "with message")'
13 assert.is.true \is_const!
14 assert.is.nil .result
15
16 it "throws on false", ->
17 assert.has.error -> COPILOT\eval_once '(assert false)'
18
19 it "shows failing expression", ->
20 err = assert.has.error -> COPILOT\eval_once '(assert false)'
21 assert.matches "assertion failed: false", err
22
23 err = assert.has.error -> COPILOT\eval_once '(assert (== 1 2))'
24 assert.matches "assertion failed: %(== 1 2%)", err
25
26 it "supports custom error messages", ->
27 err = assert.has.error -> COPILOT\eval_once '(assert (== "green" "red") "duck isnt green")'
28 assert.matches "duck isnt green", err
29
30 describe "expect=", ->
31 it "passes equal params", ->
32 with COPILOT\eval_once '(expect= 1 1 1)'
33 assert.is.true \is_const!
34 assert.is.nil .result
35
36 with COPILOT\eval_once '(expect= 2 2 2)'
37 assert.is.true \is_const!
38 assert.is.nil .result
39
40 with COPILOT\eval_once '(expect= true true)'
41 assert.is.true \is_const!
42 assert.is.nil .result
43
44 with COPILOT\eval_once '(expect= "hello" "hello")'
45 assert.is.true \is_const!
46 assert.is.nil .result
47
48 with COPILOT\eval_once '(expect= (array 1 2) (array 1 2))'
49 assert.is.true \is_const!
50 assert.is.nil .result
51
52 it "fails different values", ->
53 assert.has.error -> COPILOT\eval_once '(expect= 1 2)'
54 assert.has.error -> COPILOT\eval_once '(expect= 1 1 2)'
55 assert.has.error -> COPILOT\eval_once '(expect= 2 1 1)'
56
57 assert.has.error -> COPILOT\eval_once '(expect= true false)'
58
59 assert.has.error -> COPILOT\eval_once '(expect= "asdf" "bsdf")'
60 assert.has.error -> COPILOT\eval_once '(expect= (array 1 2) (array 1 3))'
61
62 it "fails different types", ->
63 assert.has.error -> COPILOT\eval_once '(expect= true 2)'
64 assert.has.error -> COPILOT\eval_once '(expect= true true 1)'
65 assert.has.error -> COPILOT\eval_once '(expect= true false "str")'
66
67 it "reports first failing arguments", ->
68 err = assert.has.error -> COPILOT\eval_once '(expect= 1 2)'
69 assert.matches "assertion error: Expected 2 to equal <num= 1> %(got <num= 2>%)", err
70
71 err = assert.has.error -> COPILOT\eval_once '(expect= 1 1 2 4)'
72 assert.matches "assertion error: Expected 2 to equal <num= 1> %(got <num= 2>%)", err
73
74 err = assert.has.error -> COPILOT\eval_once '(expect= true 2 3)'
75 assert.matches "assertion error: Expected 2 to equal <bool= true> %(got <num= 2>%)", err
76
77 it "reports full expressions", ->
78 err = assert.has.error -> COPILOT\eval_once '(import* math) (expect= 1 (+ 1 1))'
79 assert.matches "assertion error: Expected %(%+ 1 1%) to equal <num= 1> %(got <num= 2>%)", err
+0
-24
spec/lang/thread_spec.moon less more
0 import TestPilot from require 'spec.test_setup'
1
2 describe "thread macros", ->
3 COPILOT = TestPilot!
4
5 it "thread forward (->)", ->
6 rt = COPILOT\eval_once '
7 (import* math)
8 #((/ (+ 10 2) 2) = 6)
9 (-> 10
10 (+ 2)
11 (/ 2))'
12 assert.is.true rt\is_const!
13 assert.is.equal '<num= 6.0>', tostring rt.result
14
15 it "thread last forward (->>)", ->
16 rt = COPILOT\eval_once '
17 (import* math)
18 #((/ 10 (+ 2 3)) = 2)
19 (->> 3
20 (+ 2)
21 (/ 10))'
22 assert.is.true rt\is_const!
23 assert.is.equal '<num= 2.0>', tostring rt.result
0 import TestPilot from require 'spec.test_setup'
1 import T, Array from require 'alv'
2
3 describe "array", ->
4 test = TestPilot '', '(import* array-)\n'
5
6 svec3 = Array 3, T.str
7
8 it "can contain any type", ->
9 COPILOT\eval_once '(array 1 2 3)'
10 COPILOT\eval_once '(array true false)'
11 COPILOT\eval_once '(array "a")'
12 COPILOT\eval_once '(array (array 1 2) (array 3 4))'
13
14 it "cannot contain mixed types", ->
15 err = assert.has.error -> COPILOT\eval_once '(array 1 false)'
16 assert.matches "argument error: couldn't match arguments", err
17
18 describe "(set)", ->
19 it "can swap values", ->
20 rt = COPILOT\eval_once '(set (array "f" "b" "c") 0 "a")'
21 assert.is.true rt\is_const!
22 assert.is.equal svec3\mk_const({ 'a', 'b', 'c' }), rt.result
23
24 it "checks value type", ->
25 err = assert.has.error -> COPILOT\eval_once '(set (array 1) 0 "a")'
26 assert.matches "expected value of type num, not str", err
27
28 it "checks index range", ->
29 err = assert.has.error -> COPILOT\eval_once '(set (array 1 2) -1 0)'
30 assert.matches "index '%-1' out of range!", err
31
32 COPILOT\eval_once '(set (array 1 2) 0 0)'
33
34 COPILOT\eval_once '(set (array 1 2) 1 0)'
35
36 err = assert.has.error -> COPILOT\eval_once '(set (array 1 2) 2 0)'
37 assert.matches "index '2' out of range!", err
38
39 describe "(get)", ->
40 it "can get a value", ->
41 rt = COPILOT\eval_once '(get (array 1 2) 0)'
42 assert.is.true rt\is_const!
43 assert.is.equal '<num= 1>', tostring rt.result
44
45 it "checks index range", ->
46 err = assert.has.error -> COPILOT\eval_once '(get (array 1 2) -1)'
47 assert.matches "index '%-1' out of range!", err
48
49 COPILOT\eval_once '(get (array 1 2) 0)'
50
51 COPILOT\eval_once '(get (array 1 2) 1)'
52
53 err = assert.has.error -> COPILOT\eval_once '(get (array 1 2) 2)'
54 assert.matches "index '2' out of range!", err
55
56 describe '(head)', ->
57 it "can peek a value", ->
58 rt = COPILOT\eval_once '(head (array 1 2))'
59 assert.is.true rt\is_const!
60 assert.is.equal '<num= 1>', tostring rt.result
61
62 describe '(tail)', ->
63 it "gets rest of an array", ->
64 rt = COPILOT\eval_once '(tail (array 1))'
65 assert.is.true rt\is_const!
66 assert.is.same (Array 0, T.num), rt.result.type
67 assert.is.same {}, rt.result!
68
69 rt = COPILOT\eval_once '(tail (array 1 2))'
70 assert.is.true rt\is_const!
71 assert.is.same (Array 1, T.num), rt.result.type
72 assert.is.same { 2 }, rt.result!
73
74 rt = COPILOT\eval_once '(tail (array 1 2 3 4))'
75 assert.is.true rt\is_const!
76 assert.is.same (Array 3, T.num), rt.result.type
77 assert.is.same { 2, 3, 4 }, rt.result!
78
79 describe '(prepend)', ->
80 it "prepends to array", ->
81 rt = COPILOT\eval_once '(prepend (array 2) 1)'
82 assert.is.true rt\is_const!
83 assert.is.same (Array 2, T.num), rt.result.type
84 assert.is.same { 1, 2 }, rt.result!
85
86 rt = COPILOT\eval_once '(prepend (array 2 3 4) 1)'
87 assert.is.true rt\is_const!
88 assert.is.same (Array 4, T.num), rt.result.type
89 assert.is.same { 1, 2, 3, 4 }, rt.result!
90
91 describe "(insert)", ->
92 it "can insert a value", ->
93 rt = COPILOT\eval_once '(insert (array "b" "c") 0 "a")'
94 assert.is.true rt\is_const!
95 assert.is.equal svec3\mk_const({ 'a', 'b', 'c' }), rt.result
96
97 rt = COPILOT\eval_once '(insert (array "a" "c") 1 "b")'
98 assert.is.true rt\is_const!
99 assert.is.equal svec3\mk_const({ 'a', 'b', 'c' }), rt.result
100
101 rt = COPILOT\eval_once '(insert (array "a" "b") 2 "c")'
102 assert.is.true rt\is_const!
103 assert.is.equal svec3\mk_const({ 'a', 'b', 'c' }), rt.result
104
105 it "checks index range", ->
106 err = assert.has.error -> COPILOT\eval_once '(insert (array 1 2) -1 0)'
107 assert.matches "index '%-1' out of range!", err
108
109 COPILOT\eval_once '(insert (array 1 2) 0 0)'
110
111 COPILOT\eval_once '(insert (array 1 2) 1 0)'
112
113 COPILOT\eval_once '(insert (array 1 2) 2 0)'
114
115 err = assert.has.error -> COPILOT\eval_once '(insert (array 1 2) 3 0)'
116 assert.matches "index '3' out of range!", err
117
118 describe "(remove)", ->
119 it "can remove a value", ->
120 rt = COPILOT\eval_once '(remove (array "d" "a" "b" "c") 0)'
121 assert.is.true rt\is_const!
122 assert.is.equal svec3\mk_const({ 'a', 'b', 'c' }), rt.result
123
124 rt = COPILOT\eval_once '(remove (array "a" "b" "c" "d") 3)'
125 assert.is.true rt\is_const!
126 assert.is.equal svec3\mk_const({ 'a', 'b', 'c' }), rt.result
127
128 it "checks index range", ->
129 err = assert.has.error -> COPILOT\eval_once '(remove (array 1 2 3) -1)'
130 assert.matches "index '%-1' out of range!", err
131
132 err = assert.has.error -> COPILOT\eval_once '(remove (array 1 2 3) 3)'
133 assert.matches "index '3' out of range!", err
134
135 it "can be concatenated with (concat)", ->
136 rt = COPILOT\eval_once '(concat (array "a" "b") (array "c"))'
137 assert.is.true rt\is_const!
138 assert.is.equal svec3\mk_const({ 'a', 'b', 'c' }), rt.result
139
140 it "size can be read using (size)", ->
141 rt = COPILOT\eval_once '(size (array 1))'
142 assert.is.true rt\is_const!
143 assert.is.equal '<num= 1>', tostring rt.result
144