OLD | NEW |
| (Empty) |
1 library petitparser.test.lisp_test; | |
2 | |
3 import 'package:test/test.dart'; | |
4 | |
5 import 'package:petitparser/lisp.dart'; | |
6 | |
7 void main() { | |
8 var root = new Environment(); | |
9 var native = Natives.import(root); | |
10 var standard = Standard.import(native.create()); | |
11 | |
12 dynamic exec(String value, [Environment env]) { | |
13 return evalString(lispParser, env != null ? env : standard.create(), value); | |
14 } | |
15 | |
16 group('Cell', () { | |
17 test('Name', () { | |
18 var cell1 = new Name('foo'); | |
19 var cell2 = new Name('foo'); | |
20 var cell3 = new Name('bar'); | |
21 expect(cell1, cell2); | |
22 expect(cell1, same(cell2)); | |
23 expect(cell1, isNot(cell3)); | |
24 expect(cell1, isNot(same(cell3))); | |
25 }); | |
26 test('Cons', () { | |
27 var cell = new Cons(1, 2); | |
28 expect(cell.head, 1); | |
29 expect(cell.tail, 2); | |
30 cell.head = 3; | |
31 expect(cell.head, 3); | |
32 expect(cell.tail, 2); | |
33 cell.tail = new Cons(4, 5); | |
34 expect(cell.head, 3); | |
35 expect(cell.tail.head, 4); | |
36 expect(cell.tail.tail, 5); | |
37 expect(cell == cell, isTrue); | |
38 expect(cell.hashCode, isNonZero); | |
39 expect(cell.toString(), '(3 4 . 5)'); | |
40 }); | |
41 }); | |
42 group('Environment', () { | |
43 var env = standard.create(); | |
44 test('Standard', () { | |
45 expect(env.owner, isNotNull); | |
46 expect(env.keys, isEmpty); | |
47 expect(env.owner.keys, isNot(isEmpty)); | |
48 }); | |
49 test('Create', () { | |
50 var sub = env.create(); | |
51 expect(sub.owner, same(env)); | |
52 expect(sub.keys, isEmpty); | |
53 }); | |
54 }); | |
55 group('Parser', () { | |
56 var definition = new LispParserDefinition(); | |
57 var atom = definition.build(start: definition.atom); | |
58 test('Name', () { | |
59 var cell = atom.parse('foo').value; | |
60 expect(cell, new isInstanceOf<Name>()); | |
61 expect(cell.toString(), 'foo'); | |
62 }); | |
63 test('Name for operator', () { | |
64 var cell = atom.parse('+').value; | |
65 expect(cell, new isInstanceOf<Name>()); | |
66 expect(cell.toString(), '+'); | |
67 }); | |
68 test('Name for special', () { | |
69 var cell = atom.parse('set!').value; | |
70 expect(cell, new isInstanceOf<Name>()); | |
71 expect(cell.toString(), 'set!'); | |
72 }); | |
73 test('String', () { | |
74 var cell = atom.parse('"foo"').value; | |
75 expect(cell, new isInstanceOf<String>()); | |
76 expect(cell, 'foo'); | |
77 }); | |
78 test('String with escape', () { | |
79 var cell = atom.parse('"\\""').value; | |
80 expect(cell, '"'); | |
81 }); | |
82 test('Number integer', () { | |
83 var cell = atom.parse('123').value; | |
84 expect(cell, 123); | |
85 }); | |
86 test('Number negative integer', () { | |
87 var cell = atom.parse('-123').value; | |
88 expect(cell, -123); | |
89 }); | |
90 test('Number positive integer', () { | |
91 var cell = atom.parse('+123').value; | |
92 expect(cell, 123); | |
93 }); | |
94 test('Number floating', () { | |
95 var cell = atom.parse('123.45').value; | |
96 expect(cell, 123.45); | |
97 }); | |
98 test('Number floating exponential', () { | |
99 var cell = atom.parse('1.23e4').value; | |
100 expect(cell, 1.23e4); | |
101 }); | |
102 test('List empty', () { | |
103 var cell = atom.parse('()').value; | |
104 expect(cell, isNull); | |
105 }); | |
106 test('List empty []', () { | |
107 var cell = atom.parse('[ ]').value; | |
108 expect(cell, isNull); | |
109 }); | |
110 test('List empty {}', () { | |
111 var cell = atom.parse('{ }').value; | |
112 expect(cell, isNull); | |
113 }); | |
114 test('List one element', () { | |
115 var cell = atom.parse('(1)').value; | |
116 expect(cell, new isInstanceOf<Cons>()); | |
117 expect(cell.head, 1); | |
118 expect(cell.tail, isNull); | |
119 }); | |
120 test('List two elements', () { | |
121 var cell = atom.parse('(1 2)').value; | |
122 expect(cell, new isInstanceOf<Cons>()); | |
123 expect(cell.head, 1); | |
124 expect(cell.tail, new isInstanceOf<Cons>()); | |
125 expect(cell.tail.head, 2); | |
126 expect(cell.tail.tail, isNull); | |
127 }); | |
128 test('List three elements', () { | |
129 var cell = atom.parse('(+ 1 2)').value; | |
130 expect(cell, new isInstanceOf<Cons>()); | |
131 expect(cell.head, new isInstanceOf<Name>()); | |
132 expect(cell.head.toString(), '+'); | |
133 expect(cell.tail, new isInstanceOf<Cons>()); | |
134 expect(cell.tail.head, 1); | |
135 expect(cell.tail.tail, new isInstanceOf<Cons>()); | |
136 expect(cell.tail.tail.head, 2); | |
137 expect(cell.tail.tail.tail, isNull); | |
138 }); | |
139 }); | |
140 group('Natives', () { | |
141 test('Define', () { | |
142 expect(exec('(define a 1)'), 1); | |
143 expect(exec('(define a 2) a'), 2); | |
144 expect(exec('((define (a) 3))'), 3); | |
145 expect(exec('(define (a) 4) (a)'), 4); | |
146 expect(exec('((define (a x) x) 5)'), 5); | |
147 expect(exec('(define (a x) x) (a 6)'), 6); | |
148 }); | |
149 test('Lambda', () { | |
150 expect(exec('((lambda () 1) 2)'), 1); | |
151 expect(exec('((lambda (x) x) 2)'), 2); | |
152 expect(exec('((lambda (x) (+ x x)) 2)'), 4); | |
153 expect(exec('((lambda (x y) (+ x y)) 2 4)'), 6); | |
154 expect(exec('((lambda (x y z) (+ x y z)) 2 4 6)'), 12); | |
155 }); | |
156 test('Quote', () { | |
157 expect(exec('(quote)'), null); | |
158 expect(exec('(quote 1)'), new Cons(1, null)); | |
159 expect(exec('(quote + 1)'), new Cons(new Name('+'), new Cons(1, null))); | |
160 }); | |
161 test('Quote (syntax)', () { | |
162 expect(exec('\'()'), null); | |
163 expect(exec('\'(1)'), new Cons(1, null)); | |
164 expect(exec('\'(+ 1)'), new Cons(new Name('+'), new Cons(1, null))); | |
165 }); | |
166 test('Eval', () { | |
167 expect(exec('(eval (quote + 1 2))'), 3); | |
168 }); | |
169 test('Apply', () { | |
170 expect(exec('(apply + 1 2 3)'), 6); | |
171 expect(exec('(apply + 1 2 3 (+ 2 2))'), 10); | |
172 }); | |
173 test('Let', () { | |
174 expect(exec('(let ((a 1)) a)'), 1); | |
175 expect(exec('(let ((a 1) (b 2)) a)'), 1); | |
176 expect(exec('(let ((a 1) (b 2)) b)'), 2); | |
177 expect(exec('(let ((a 1) (b 2)) (+ a b))'), 3); | |
178 expect(exec('(let ((a 1) (b 2)) (+ a b) 4)'), 4); | |
179 }); | |
180 test('Set!', () { | |
181 var env = standard.create(); | |
182 env.define(new Name('a'), null); | |
183 expect(exec('(set! a 1)', env), 1); | |
184 expect(exec('(set! a (+ 1 2))', env), 3); | |
185 expect(exec('(set! a (+ 1 2)) (+ a 1)', env), 4); | |
186 }); | |
187 test('Set! (undefined)', () { | |
188 expect(() => exec('(set! a 1)'), throws); | |
189 expect(() => standard[new Name('a')], throws); | |
190 }); | |
191 test('If', () { | |
192 expect(exec('(if true)'), isNull); | |
193 expect(exec('(if false)'), isNull); | |
194 expect(exec('(if true 1)'), 1); | |
195 expect(exec('(if false 1)'), isNull); | |
196 expect(exec('(if true 1 2)'), 1); | |
197 expect(exec('(if false 1 2)'), 2); | |
198 }); | |
199 test('If (lazyness)', () { | |
200 expect(exec('(if (= 1 1) 3 4)'), 3); | |
201 expect(exec('(if (= 1 2) 3 4)'), 4); | |
202 }); | |
203 test('While', () { | |
204 var env = standard.create(); | |
205 env.define(new Name('a'), 0); | |
206 exec('(while (< a 3) (set! a (+ a 1)))', env); | |
207 expect(env[new Name('a')], 3); | |
208 }); | |
209 test('True', () { | |
210 expect(exec('true'), isTrue); | |
211 }); | |
212 test('False', () { | |
213 expect(exec('false'), isFalse); | |
214 }); | |
215 test('And', () { | |
216 expect(exec('(and)'), isTrue); | |
217 expect(exec('(and true)'), isTrue); | |
218 expect(exec('(and false)'), isFalse); | |
219 expect(exec('(and true true)'), isTrue); | |
220 expect(exec('(and true false)'), isFalse); | |
221 expect(exec('(and false true)'), isFalse); | |
222 expect(exec('(and false false)'), isFalse); | |
223 expect(exec('(and true true true)'), isTrue); | |
224 expect(exec('(and true true false)'), isFalse); | |
225 expect(exec('(and true false true)'), isFalse); | |
226 expect(exec('(and true false false)'), isFalse); | |
227 expect(exec('(and false true true)'), isFalse); | |
228 expect(exec('(and false true false)'), isFalse); | |
229 expect(exec('(and false false true)'), isFalse); | |
230 expect(exec('(and false false false)'), isFalse); | |
231 }); | |
232 test('And (lazyness)', () { | |
233 var env = standard.create(); | |
234 env.define(new Name('a'), null); | |
235 exec('(and false (set! a true))', env); | |
236 expect(env[new Name('a')], isNull); | |
237 exec('(and true (set! a true))', env); | |
238 expect(env[new Name('a')], isTrue); | |
239 }); | |
240 test('Or', () { | |
241 expect(exec('(or)'), isFalse); | |
242 expect(exec('(or true)'), isTrue); | |
243 expect(exec('(or false)'), isFalse); | |
244 expect(exec('(or true true)'), isTrue); | |
245 expect(exec('(or true false)'), isTrue); | |
246 expect(exec('(or false true)'), isTrue); | |
247 expect(exec('(or false false)'), isFalse); | |
248 expect(exec('(or true true true)'), isTrue); | |
249 expect(exec('(or true true false)'), isTrue); | |
250 expect(exec('(or true false true)'), isTrue); | |
251 expect(exec('(or true false false)'), isTrue); | |
252 expect(exec('(or false true true)'), isTrue); | |
253 expect(exec('(or false true false)'), isTrue); | |
254 expect(exec('(or false false true)'), isTrue); | |
255 expect(exec('(or false false false)'), isFalse); | |
256 }); | |
257 test('Or (lazyness)', () { | |
258 var env = standard.create(); | |
259 env.define(new Name('a'), null); | |
260 exec('(or true (set! a true))', env); | |
261 expect(env[new Name('a')], isNull); | |
262 exec('(or false (set! a true))', env); | |
263 expect(env[new Name('a')], isTrue); | |
264 }); | |
265 test('Not', () { | |
266 expect(exec('(not true)'), isFalse); | |
267 expect(exec('(not false)'), isTrue); | |
268 }); | |
269 test('Add', () { | |
270 expect(exec('(+ 1)'), 1); | |
271 expect(exec('(+ 1 2)'), 3); | |
272 expect(exec('(+ 1 2 3)'), 6); | |
273 expect(exec('(+ 1 2 3 4)'), 10); | |
274 }); | |
275 test('Sub', () { | |
276 expect(exec('(- 1)'), -1); | |
277 expect(exec('(- 1 2)'), -1); | |
278 expect(exec('(- 1 2 3)'), -4); | |
279 expect(exec('(- 1 2 3 4)'), -8); | |
280 }); | |
281 test('Mul', () { | |
282 expect(exec('(* 2)'), 2); | |
283 expect(exec('(* 2 3)'), 6); | |
284 expect(exec('(* 2 3 4)'), 24); | |
285 }); | |
286 test('Div', () { | |
287 expect(exec('(/ 24)'), 24); | |
288 expect(exec('(/ 24 3)'), 8); | |
289 expect(exec('(/ 24 3 2)'), 4); | |
290 }); | |
291 test('Mod', () { | |
292 expect(exec('(% 24)'), 24); | |
293 expect(exec('(% 24 5)'), 4); | |
294 expect(exec('(% 24 5 3)'), 1); | |
295 }); | |
296 test('Less', () { | |
297 expect(exec('(< 1 2)'), isTrue); | |
298 expect(exec('(< 1 1)'), isFalse); | |
299 expect(exec('(< 2 1)'), isFalse); | |
300 }); | |
301 test('Less equal', () { | |
302 expect(exec('(<= 1 2)'), isTrue); | |
303 expect(exec('(<= 1 1)'), isTrue); | |
304 expect(exec('(<= 2 1)'), isFalse); | |
305 }); | |
306 test('Equal', () { | |
307 expect(exec('(= 1 1)'), isTrue); | |
308 expect(exec('(= 1 2)'), isFalse); | |
309 expect(exec('(= 2 1)'), isFalse); | |
310 }); | |
311 test('Not equal', () { | |
312 expect(exec('(!= 1 1)'), isFalse); | |
313 expect(exec('(!= 1 2)'), isTrue); | |
314 expect(exec('(!= 2 1)'), isTrue); | |
315 }); | |
316 test('Larger', () { | |
317 expect(exec('(> 1 1)'), isFalse); | |
318 expect(exec('(> 1 2)'), isFalse); | |
319 expect(exec('(> 2 1)'), isTrue); | |
320 }); | |
321 test('Larger equal', () { | |
322 expect(exec('(>= 1 1)'), isTrue); | |
323 expect(exec('(>= 1 2)'), isFalse); | |
324 expect(exec('(>= 2 1)'), isTrue); | |
325 }); | |
326 test('Cons', () { | |
327 expect(exec('(cons 1 2)'), new Cons(1, 2)); | |
328 }); | |
329 test('Car', () { | |
330 expect(exec('(car null)'), isNull); | |
331 expect(exec('(car (cons 1 2))'), 1); | |
332 }); | |
333 test('Car!', () { | |
334 expect(exec('(car! null 3)'), isNull); | |
335 expect(exec('(car! (cons 1 2) 3)'), new Cons(3, 2)); | |
336 }); | |
337 test('Cdr', () { | |
338 expect(exec('(cdr null)'), isNull); | |
339 expect(exec('(cdr (cons 1 2))'), 2); | |
340 }); | |
341 test('Cdr!', () { | |
342 expect(exec('(cdr! null 3)'), isNull); | |
343 expect(exec('(cdr! (cons 1 2) 3)'), new Cons(1, 3)); | |
344 }); | |
345 }); | |
346 group('Library', () { | |
347 test('Null', () { | |
348 expect(exec('null'), isNull); | |
349 }); | |
350 test('Null? (true)', () { | |
351 expect(exec('(null? \'())'), isTrue); | |
352 expect(exec('(null? null)'), isTrue); | |
353 }); | |
354 test('Null? (false)', () { | |
355 expect(exec('(null? 1)'), isFalse); | |
356 expect(exec('(null? "a")'), isFalse); | |
357 expect(exec('(null? (quote a))'), isFalse); | |
358 expect(exec('(null? true)'), isFalse); | |
359 expect(exec('(null? false)'), isFalse); | |
360 }); | |
361 test('Length', () { | |
362 expect(exec('(length \'())'), 0); | |
363 expect(exec('(length \'(1))'), 1); | |
364 expect(exec('(length \'(1 1))'), 2); | |
365 expect(exec('(length \'(1 1 1))'), 3); | |
366 expect(exec('(length \'(1 1 1 1))'), 4); | |
367 expect(exec('(length \'(1 1 1 1 1))'), 5); | |
368 }); | |
369 test('Append', () { | |
370 expect(exec('(append \'() \'())'), isNull); | |
371 expect(exec('(append \'(1) \'())'), exec('\'(1)')); | |
372 expect(exec('(append \'() \'(1))'), exec('\'(1)')); | |
373 expect(exec('(append \'(1) \'(2))'), exec('\'(1 2)')); | |
374 expect(exec('(append \'(1 2) \'(3))'), exec('\'(1 2 3)')); | |
375 expect(exec('(append \'(1) \'(2 3))'), exec('\'(1 2 3)')); | |
376 }); | |
377 test('List Head', () { | |
378 expect(exec('(list-head \'(5 6 7) 0)'), 5); | |
379 expect(exec('(list-head \'(5 6 7) 1)'), 6); | |
380 expect(exec('(list-head \'(5 6 7) 2)'), 7); | |
381 expect(exec('(list-head \'(5 6 7) 3)'), isNull); | |
382 }); | |
383 test('List Tail', () { | |
384 expect(exec('(list-tail \'(5 6 7) 0)'), exec('\'(6 7)')); | |
385 expect(exec('(list-tail \'(5 6 7) 1)'), exec('\'(7)')); | |
386 expect(exec('(list-tail \'(5 6 7) 2)'), isNull); | |
387 }); | |
388 test('Map', () { | |
389 expect(exec('(map \'() (lambda (x) (* 2 x)))'), isNull); | |
390 expect(exec('(map \'(2) (lambda (x) (* 2 x)))'), exec('\'(4)')); | |
391 expect(exec('(map \'(2 3) (lambda (x) (* 2 x)))'), exec('\'(4 6)')); | |
392 expect(exec('(map \'(2 3 4) (lambda (x) (* 2 x)))'), exec('\'(4 6 8)')); | |
393 }); | |
394 test('Inject', () { | |
395 expect(exec('(inject \'() 5 (lambda (s e) (+ s e 1)))'), 5); | |
396 expect(exec('(inject \'(2) 5 (lambda (s e) (+ s e 1)))'), 8); | |
397 expect(exec('(inject \'(2 3) 5 (lambda (s e) (+ s e 1)))'), 12); | |
398 }); | |
399 }); | |
400 group('Examples', () { | |
401 test('Fibonacci', () { | |
402 var env = standard.create(); | |
403 exec('(define (fib n)' | |
404 ' (if (<= n 1)' | |
405 ' 1' | |
406 ' (+ (fib (- n 1)) (fib (- n 2)))))', env); | |
407 expect(exec('(fib 0)', env), 1); | |
408 expect(exec('(fib 1)', env), 1); | |
409 expect(exec('(fib 2)', env), 2); | |
410 expect(exec('(fib 3)', env), 3); | |
411 expect(exec('(fib 4)', env), 5); | |
412 expect(exec('(fib 5)', env), 8); | |
413 }); | |
414 test('Closure', () { | |
415 var env = standard.create(); | |
416 exec('(define (mul n)' | |
417 ' (lambda (x) (* n x)))', env); | |
418 expect(exec('((mul 2) 3)', env), 6); | |
419 expect(exec('((mul 3) 4)', env), 12); | |
420 expect(exec('((mul 4) 5)', env), 20); | |
421 }); | |
422 test('Object', () { | |
423 var env = standard.create(); | |
424 exec('(define (counter start)' | |
425 ' (let ((count start))' | |
426 ' (lambda ()' | |
427 ' (set! count (+ count 1)))))', env); | |
428 exec('(define a (counter 10))', env); | |
429 exec('(define b (counter 20))', env); | |
430 expect(exec('(a)', env), 11); | |
431 expect(exec('(b)', env), 21); | |
432 expect(exec('(a)', env), 12); | |
433 expect(exec('(b)', env), 22); | |
434 expect(exec('(a)', env), 13); | |
435 expect(exec('(b)', env), 23); | |
436 }); | |
437 }); | |
438 } | |
OLD | NEW |