does not accept a message argument in Guile.
441 |
442 | =head1 BUGS
443 |
444 | This unit testing framework is a work in progress. The test groups do
445 | not support as much shared set up code among their tests as I would
446 | like, and the language for explicit test group handling is
447 | ill-specified and undocumented (peruse test-group.scm if interested).
448 | Suggestions are welcome.
449 |
450 | =head1 AUTHOR
451 |
452 | Alexey Radul, axch@mit.edu
453 |
454 | =cut
455 |
--------------------------------------------------------------------------------
/src/lil_00.scm:
--------------------------------------------------------------------------------
1 | ;;
2 | ;; lil_00.scm - following along the little schemer book
3 | ;;
4 |
5 | ;; ch. 1
6 | ;; toys
7 |
8 | (define (atom? sexp)
9 | (and (not (pair? sexp)) (not (null? sexp))))
10 |
11 | ;; ch. 2
12 | ;; do it, do it again
13 |
14 | (define (lat? xs)
15 | (cond ((null? xs) #t)
16 | ((atom? (car xs)) (lat? (cdr xs)))
17 | (else #f)))
18 |
19 | (define (member? x xs)
20 | (cond ((null? xs) #f)
21 | (else (or (eq? x (car xs))
22 | (member? x (cdr xs))))))
23 |
24 | ;; ch. 3
25 | ;; cons the magnificent
26 |
27 | (define (o-rember x xs)
28 | (cond ((null? xs) '())
29 | ((eq? x (car xs)) (cdr xs))
30 | (else (cons (car xs) (o-rember x (cdr xs))))))
31 |
32 | (define (list-equal? xs ys)
33 | (cond ((null? xs) (null? ys))
34 | ((null? ys) (null? xs))
35 | ((eq? (car xs) (car ys)) (list-equal? (cdr xs) (cdr ys)))
36 | (else #f)))
37 |
38 |
39 | (define (firsts xs)
40 | (cond ((null? xs) '())
41 | (else (cons (car (car xs))
42 | (firsts (cdr xs))))))
43 |
44 | (define (insertR new old lat)
45 | (cond ((null? lat) '())
46 | (else
47 | (cond
48 | ((eq? (car lat) old)
49 | (cons old (cons new (cdr lat))))
50 | (else
51 | (cons (car lat)
52 | (insertR new old (cdr lat))))))))
53 |
54 | (define (insertL new old lat)
55 | (cond ((null? lat) '())
56 | (else
57 | (cond
58 | ((eq? (car lat) old)
59 | (cons new lat))
60 | (else
61 | (cons (car lat)
62 | (insertL new old (cdr lat))))))))
63 |
64 | (define (subst new old lat)
65 | (cond ((null? lat) '())
66 | (else
67 | (cond
68 | ((eq? (car lat) old)
69 | (cons new (cdr lat)))
70 | (else
71 | (cons (car lat)
72 | (subst new old (cdr lat))))))))
73 |
74 | (define (multirember x xs)
75 | (cond ((null? xs) '())
76 | ((eq? x (car xs)) (multirember x (cdr xs)))
77 | (else (cons (car xs) (multirember x (cdr xs))))))
78 |
79 | ;; ch. 4
80 | ;; number games
81 |
82 | (define (myplus a b)
83 | (cond ((zero? a) b)
84 | (else (myplus (sub1 a) (add1 b)))))
85 |
86 | (define (addtup tup)
87 | (cond ((null? tup) 0)
88 | (else (+ (car tup) (addtup (cdr tup))))))
89 |
90 | (define (xo a b)
91 | (cond ((= b 1) a)
92 | (else (+ a (xo a (sub1 b))))))
93 |
94 | (define (tup+ tup1 tup2)
95 | (cond ((and (null? tup1) (null? tup2)) '())
96 | (else
97 | (cons (+ (car tup1) (car tup2)) (tup+ (cdr tup1) (cdr tup2))))))
98 |
99 | (define (gt a b)
100 | (cond
101 | ((zero? a) #f)
102 | ((zero? b) #t)
103 | (else (gt (sub1 a) (sub1 b)))))
104 |
105 | (define (expo n f)
106 | (cond ((zero? f) 1)
107 | (else (* n (expo n (sub1 f))))))
108 |
109 | (define (lng lat)
110 | (cond ((null? lat) 0)
111 | (else (add1 (lng (cdr lat))))))
112 |
113 | (define (pick n lat)
114 | (cond ((zero? (sub1 n)) (car lat))
115 | (else (pick (sub1 n) (cdr lat)))))
116 |
117 | (define (rempick n lat)
118 | (cond ((zero? (sub1 n)) (cdr lat))
119 | (else (cons (car lat) (rempick (sub1 n) (cdr lat))))))
120 |
121 | ;; ch. 5
122 | ;; full of stars
123 |
124 | (define (rember* a l)
125 | (cond ((null? l) '())
126 | ((atom? (car l))
127 | (cond ((eq? a (car l)) (rember* a (cdr l)))
128 | (else (cons (car l) (rember* a (cdr l))))))
129 | (else (cons (rember* a (car l)) (rember* a (cdr l))))))
130 |
131 | (define (occur* a l)
132 | (cond ((null? l) 0)
133 | ((atom? (car l))
134 | (cond ((eq? a (car l)) (+ 1 (occur* a (cdr l))))
135 | (else (occur* a (cdr l)))))
136 | (else (+ (occur* a (car l))
137 | (occur* a (cdr l))))))
138 |
139 | (define (member* a l)
140 | (cond ((null? l) #f)
141 | ((atom? (car l)) (or (eq? a (car l)) (member* a (cdr l))))
142 | (else (or (member* a (car l)) (member* a (cdr l))))))
143 |
144 | (define myxs '(foo (bar cup) god (foo cup cup) (((cup)) doo)))
145 |
146 | ;; ch. 6
147 | ;; shadows
148 |
149 | (define (numbered? aexp)
150 | (cond ((atom? aexp) (number? aexp))
151 | (else (and (numbered? (car aexp))
152 | (numbered? (caddr aexp))))))
153 |
154 | (numbered? '(5 + (3 ^ 7)))
155 |
156 | (define (value nexp)
157 | (cond ((atom? nexp) nexp)
158 | ((eq? (cadr nexp) '+)
159 | (+ (value (car nexp)) (value (caddr nexp))))
160 | ((eq? (cadr nexp) 'x)
161 | (* (value (car nexp)) (value (caddr nexp))))))
162 |
163 | (value '(3 + 5))
164 | (value '(3 x 5))
165 | (value '(3 x (2 + 12)))
166 |
167 | (define 1st-sub-expr cadr)
168 | (define 2nd-sub-expr caddr)
169 | (define operator car)
170 |
171 | (define (valuep nexp)
172 | (cond ((atom? nexp) nexp)
173 | ((eq? (operator nexp) '+)
174 | (+ (valuep (1st-sub-expr nexp)) (value (2nd-sub-expr nexp))))
175 | ((eq? (operator nexp) 'x)
176 | (* (valuep (1st-sub-expr nexp)) (value (2nd-sub-expr nexp))))))
177 |
178 | (valuep '(+ (x 4 4) 8))
179 |
180 | ;; ch. 7
181 | ;; friends and relations
182 |
183 | (define (set? lat)
184 | (cond ((null? lat) #t)
185 | ((member? (car lat) (cdr lat)) #f)
186 | (else (set? (cdr lat)))))
187 |
188 | (eq? #t (set? '(a b c d)))
189 | (eq? #f (set? '(a b c d c)))
190 | (eq? #f (set? '(a b c 5 6 b)))
191 |
192 | ;; using `member?`
193 | (define (makeset lat)
194 | (cond ((null? lat) '())
195 | ((member? (car lat) (cdr lat)) (makeset (cdr lat)))
196 | (else (cons (car lat) (makeset (cdr lat))))))
197 |
198 | (equal? '(a b c d) (makeset '(a b c c d d)))
199 |
200 | ;; using `multirember`
201 | (define (makesetp lat)
202 | (cond ((null? lat) '())
203 | (else (cons (car lat) (makesetp (multirember (car lat) (cdr lat)))))))
204 |
205 | (equal? '(a 3 b c d) (makesetp '(a 3 a b 3 c a c d d)))
206 |
207 | (define (subset? s1 s2)
208 | (cond ((null? s1) #t)
209 | (else (and (member? (car s1) s2)
210 | (subset? (cdr s1) s2)))))
211 |
212 | (eq? #t (subset? '(a b) '(e f b a)))
213 | (eq? #f (subset? '(a c) '(e f b a)))
214 |
215 | (define (eqset? s1 s2)
216 | (and (subset? s1 s2) (subset? s2 s1)))
217 |
218 | (eq? #t (eqset? '(a b c) '(c b a a b)))
219 | (eq? #t (eqset? '(a b c) '(c b a a b)))
220 | (eq? #f (eqset? '(a b c) '(c y b a a b)))
221 |
222 | (define (intersect? s1 s2)
223 | (cond ((null? s1) #f)
224 | (else (or (member? (car s1) s2)
225 | (intersect? (cdr s1) s2)))))
226 |
227 | (eq? #t (intersect? '(a b c) '(c d e f)))
228 | (eq? #f (intersect? '(a b c) '(d e f)))
229 |
230 | (define (intersect s1 s2)
231 | (cond ((null? s1) '())
232 | ((member? (car s1) s2)
233 | (cons (car s1) (intersect (cdr s1) s2)))
234 | (else (intersect (cdr s1) s2))))
235 |
236 | (equal? '(c d) (intersect '(a b c d e f) '(y d c x)))
237 |
238 | (define (union s1 s2)
239 | (cond ((null? s1) s2)
240 | ((member? (car s1) s2) (union (cdr s1) s2))
241 | (else (cons (car s1) (union (cdr s1) s2)))))
242 |
243 | (equal? '(a b c d e f) (union '(a b c d) '(d e f)))
244 |
245 | (define (difference s1 s2)
246 | (cond ((null? s1) '())
247 | ((member? (car s1) s2) (difference (cdr s1) s2))
248 | (else (cons (car s1) (difference (cdr s1) s2)))))
249 |
250 | (equal? '(a b) (difference '(a b c d) '(c d e)))
251 |
252 | (define (intersectall l-set)
253 | (cond ((null? (cdr l-set)) (car l-set))
254 | (else (intersect (car l-set)
255 | (intersectall (cdr l-set))))))
256 |
257 | ;; evals to something like:
258 | ;; (isect car (isect car (isect car cdr)))
259 |
260 | ;; alt impl using reduce
261 | (define (intersectallp l-set)
262 | (reduce-left intersect '() l-set))
263 |
264 | (equal? '(d e) (intersectall '((a b c d e f g) (f e d y) (d e g e))))
265 |
266 | (define (a-pair? x)
267 | (cond ((atom? x) #f)
268 | ((null? x) #f)
269 | ((null? (cdr x)) #f)
270 | ((null? (cddr x)) #t)
271 | (else #f)))
272 |
273 | (eq? #t (a-pair? '(a b)))
274 | (eq? #t (a-pair? '(5 6)))
275 | (eq? #t (a-pair? '((g b) (i g))))
276 | (eq? #f (a-pair? '(a b c)))
277 |
278 | (define fst car)
279 | (define snd cadr)
280 |
281 | (define (build s1 s2)
282 | (cons s1 (cons s2 '())))
283 |
284 | (define (fun? rel)
285 | (set? (firsts rel)))
286 |
287 | (eq? #t (fun? '((a b) (c d) (5 g))))
288 | (eq? #f (fun? '((a b) (c d) (a g))))
289 |
290 | (define (revpair p) (build (snd p) (fst p)))
291 |
292 | (define (revrel rel)
293 | (cond ((null? rel) '())
294 | (else (cons
295 | (revpair (car rel))
296 | (revrel (cdr rel))))))
297 |
298 | (revrel '((a b) (1 2)))
299 |
300 | (define (fullfun? fun)
301 | (fun? (revrel fun)))
302 |
303 | (define one-to-one? fullfun?)
304 |
305 | (eq? #t (fullfun? '((a b) (x y))))
306 | (eq? #f (fullfun? '((x y) (a b) (v y))))
307 |
308 | ;; ch. 8
309 | ;; lambda the ultimate
310 |
311 | ;; skipping basic higher order fn review..
312 |
313 | ;; but this guy looks strange. we're creating
314 | ;; a new function on each recursion. the new
315 | ;; fn closes over lat free variable.
316 |
317 | (define (multirember-co a lat col)
318 | (cond
319 | ((null? lat) (col '() '()))
320 | ((eq? (car lat) a)
321 | (multirember-co
322 | a (cdr lat)
323 | (lambda (newlat seen)
324 | (col newlat (cons (car lat) seen)))))
325 | (else
326 | (multirember-co
327 | a (cdr lat)
328 | (lambda (newlat seen)
329 | (col (cons (car lat) newlat) seen))))))
330 |
331 | (define (a-friend x y) (null? y))
332 |
333 | (multirember-co 'tuna '() a-friend)
334 | ;; (a-friend '() '()) => #t
335 |
336 | (multirember-co 'tuna '(tuna) a-friend)
337 | ;; eval steps
338 | ;; 1. call fn, eq? true, so
339 | ;; 2. (multirember-co 'tuna '() f), f is a new fn,
340 | ;; of 2 args, which calls a-friend like..
341 | ;; 3. (a-friend '() '(tuna)) => #f
342 |
343 | (multirember-co 'tuna '(wahoo tuna) a-friend)
344 | ;; eval steps
345 | ;; 1. eq? wahoo tuna => false
346 | ;; 2. (multirember-co 'tuna '(tuna) f), f is a new fn
347 | ;; f calls a-friend with 2nd arg unchanged,
348 | ;; and wahoo cons'd to 1st arg
349 | ;; 3. eq? tuna tuna => true
350 | ;; 4. (multirember-co 'tuna '() f), f is a new fn
351 | ;; f is a fn, which calls previous steps fn
352 | ;; 1st arg unchanged, tuna cons'd to second arg
353 | ;; 5. null? lat => true
354 | ;; 6. call fn from previous step, with two empty lists
355 | ;;
356 | ;; end up with something like:
357 | ((lambda (a b)
358 | ((lambda (x y)
359 | (a-friend (cons 'wahoo x) y))
360 | a (cons 'tuna b)))
361 | '() '())
362 |
363 | ;; need to revisit this section of ch. 8
364 | ;; explore continuations more
365 |
366 | ;; ch. 9
367 | ;; ..and again, and again..
368 |
369 | ;; partial fn
370 | (define (keep-looking a c lat)
371 | (cond ((number? c) (keep-looking a (pick c lat) lat))
372 | (else (eq? a c))))
373 |
374 | (define (looking a lat)
375 | (keep-looking a (pick 1 lat) lat))
376 |
377 | ;; given a pair of sexp
378 | ;; first elem of pair must have length 2
379 | ;; second elem of pair can be anything
380 | ;; make a new pair as shown.. strange
381 | (define (shift p)
382 | (build (fst (fst p))
383 | (build (snd (fst p))
384 | (snd p))))
385 |
386 | (equal? (shift '((a b) c))
387 | '(a (b c)))
388 | (equal? (shift '((a b) (c d)))
389 | '(a (b (c d))))
390 |
391 | ;; base case here, when arg is atom
392 | ;; or when snd of arg is atom, and
393 | ;; fst is not a pair.. wierd
394 | (define (align pora)
395 | (cond ((atom? pora) pora)
396 | ((a-pair? (fst pora))
397 | (align (shift pora)))
398 | (else (build (fst pora)
399 | (align (snd pora))))))
400 |
401 | (align '((a b) (c d)))
402 |
403 | (define (length* l)
404 | (cond ((null? l) 0)
405 | ((atom? (car l)) (+ 1 (length* (cdr l))))
406 | (else (+ (length* (car l))
407 | (length* (cdr l))))))
408 |
409 | (eq? 6 (length* '((a b (f g)) (c d))))
410 |
411 | ;; implement length without define
412 | ;; yikes ok re-read
413 |
414 | (define (eternity x)
415 | (eternity x))
416 |
417 | (define (add1 n) (+ 1 n))
418 |
419 | ((lambda (length)
420 | (lambda (l)
421 | (cond ((null? l) 0)
422 | (else (add1 (length (cdr l)))))))
423 | eternity)
424 |
425 | ;; through fn app name mk-length
426 | ;; and also name length thru app
427 | (define l0 ; l0 is the result of applying 1st lambda to 2nd lambda
428 | ((lambda (mk-length) ; 1st lambda takes a fn and calls it w/eternity
429 | (mk-length eternity))
430 | (lambda (length) ; given arg (eternity), return lambda
431 | (lambda (l) ; the lambda we return takes the lat we will
432 | (cond ((null? l) 0) ; measure length of
433 | (else (add1 (length (cdr l)))))))))
434 |
435 | (l0 '()) ; => 0 (holy moly works! for empty list only lol)
436 |
437 | (((lambda (mk-length)
438 | (mk-length
439 | (mk-length
440 | (mk-length
441 | (mk-length eternity)))))
442 | (lambda (length)
443 | (lambda (l)
444 | (cond ((null? l) 0)
445 | (else (add1 (length (cdr l))))))))
446 | '(a b c))
447 | ;; => 3 (woah)
448 |
449 | ;; so what role does eternity have in above forms?
450 | ;; none really, just acts like a bottom value i suppose
451 |
452 | (define l-n
453 | ((lambda (mk-length)
454 | (mk-length mk-length))
455 | (lambda (mk-length)
456 | (lambda (l)
457 | (cond ((null? l) 0)
458 | (else (add1 ((mk-length eternity)
459 | (cdr l)))))))))
460 |
461 | (l-n '(a))
462 |
463 | ;; ok we're getting somewhere now, maybe
464 |
465 | ;; ((lambda (mk-length)
466 | ;; (mk-length mk-length))
467 | ;; (lambda (mk-length)
468 | ;; ((lambda (length)
469 | ;; (lambda (l)
470 | ;; (cond ((null? l) 0)
471 | ;; (else (add1 (length (cdr l)))))))
472 | ;; (mk-length mk-length))))
473 |
474 | ;; above fails, infinite recur
475 |
476 | (define l-x
477 | ((lambda (le)
478 | ((lambda (mk-length)
479 | (mk-length mk-length))
480 | (lambda (mk-length)
481 | (le (lambda (x)
482 | ((mk-length mk-length) x))))))
483 | (lambda (length)
484 | (lambda (l)
485 | (cond ((null? l) 0)
486 | (else (add1 (length (cdr l)))))))))
487 |
488 | (l-x '(a b c d e f g)) ; => 7 (jeesh finally!)
489 |
490 | ;; applicative order y-combinator
491 | (define Y
492 | (lambda (le)
493 | ((lambda (f)
494 | (f f))
495 | (lambda (f)
496 | (le (lambda (x)
497 | ((f f) x)))))))
498 |
499 | ((Y (lambda (length)
500 | (lambda (l)
501 | (cond ((null? l) 0)
502 | (else (add1 (length (cdr l))))))))
503 | '(1 2 3 4)) ; => 4
504 |
505 | ;; yikes, that is crazy stuff
506 |
507 | ;; ch. 10
508 | ;; what is the value of all this?
509 |
510 | (define new-entry build)
511 |
512 | (define (lookup-in-entry name entry entry-f)
513 | (lookup-in-entry-help
514 | name (fst entry) (snd entry) entry-f))
515 |
516 | (define (lookup-in-entry-help name names values entry-f)
517 | (cond ((null? names) (entry-f name))
518 | ((eq? (car names) name) (car values))
519 | (else (lookup-in-entry-help
520 | name (cdr names) (cdr values) entry-f))))
521 |
522 | (define extend-table cons)
523 |
524 | (define (lookup-in-table name table table-f)
525 | (cond ((null? table) (table-f name))
526 | (else (lookup-in-entry
527 | name (car table)
528 | (lambda (n)
529 | (lookup-in-table n (cdr table) table-f))))))
530 |
531 | (define (expression-to-action e)
532 | (cond ((atom? e) (atom-to-action e))
533 | (else (list-to-action e))))
534 |
535 | (define (atom-to-action e)
536 | (cond ((number? e) *const)
537 | ((eq? e #t) *const)
538 | ((eq? e #f) *const)
539 | ((eq? e 'cons) *const)
540 | ((eq? e 'car) *const)
541 | ((eq? e 'cdr) *const)
542 | ((eq? e 'null?) *const)
543 | ((eq? e 'eq?) *const)
544 | ((eq? e 'atom?) *const)
545 | ((eq? e 'zero?) *const)
546 | ((eq? e 'add1) *const)
547 | ((eq? e 'sub1) *const)
548 | ((eq? e 'number?) *const)
549 | (else *identifier)))
550 |
551 | (define (list-to-action e)
552 | (cond ((atom? (car e))
553 | (cond ((eq? (car e) 'quote)
554 | *quote)
555 | ((eq? (car e) 'lambda)
556 | *lambda)
557 | ((eq? (car e) 'cond)
558 | *cond)
559 | (else *application)))
560 | (else *application)))
561 |
562 | (define (value e)
563 | (meaning e '()))
564 |
565 | (define (meaning e table)
566 | ((expression-to-action e) e table))
567 |
568 | (define (*const e table)
569 | (cond ((number? e) e)
570 | ((eq? e #t) #t)
571 | ((eq? e #f) #f)
572 | (else (build 'primitive e))))
573 |
574 | (define (*quote e table)
575 | (text-of e))
576 |
577 | (define text-of snd)
578 |
579 | (define (*identifier e table)
580 | (lookup-in-table e table initial-table))
581 |
582 | (define (initial-table name) (car '())) ; gen err
583 |
584 | (define (*lambda e table)
585 | (build 'non-primitive (cons table (cdr e))))
586 |
587 | (meaning '(lambda (x) (cons x y)) '(((y z) ((8) 9))))
588 | ;; => (non-primitive ( (((y z) ((8) 9))) (x) (cons x y) ))
589 | ;; => (non-primitive ( env formal body ))
590 |
591 | (define table-of fst)
592 | (define formals-of snd)
593 | (define body-of caddr)
594 |
595 | (define (evcon lines table)
596 | (cond ((else? (question-of (car lines)))
597 | (meaning (answer-of (car lines)) table))
598 | ((meaning (question-of (car lines)) table)
599 | (meaning (answer-of (car lines)) table))
600 | (else (evcon (cdr lines) table))))
601 |
602 | (define (else? e)
603 | (and (atom? e) (eq? e 'else)))
604 |
605 | (define question-of fst)
606 | (define answer-of snd)
607 |
608 | (define (*cond e table)
609 | (evcon (cond-lines-of e) table))
610 |
611 | (define cond-lines-of cdr)
612 |
613 | (define (evlis args table)
614 | (cond ((null? args) '())
615 | (else (cons (meaning (car args) table)
616 | (evlis (cdr args) table)))))
617 |
618 | (define (*application e table)
619 | (apply1 (meaning (function-of e) table)
620 | (evlis (arguments-of e) table)))
621 |
622 | (define function-of car)
623 | (define arguments-of cdr)
624 |
625 | (define (primitive? l)
626 | (eq? (fst l) 'primitive))
627 | (define (non-primitive? l)
628 | (eq? (fst l) 'non-primitive))
629 |
630 | (define (apply1 fun vals)
631 | (cond ((primitive? fun)
632 | (apply-primitive (snd fun) vals))
633 | ((non-primitive? fun)
634 | (apply-closure (snd fun) vals))))
635 |
636 | (define (apply-primitive name vals)
637 | (cond
638 | ((eq? name 'cons)
639 | (cons (fst vals) (snd vals)))
640 | ((eq? name 'car)
641 | (car (fst vals)))
642 | ((eq? name 'cdr)
643 | (cdr (fst vals)))
644 | ((eq? name 'null?)
645 | (null? (fst vals)))
646 | ((eq? name 'eq?)
647 | (eq? (fst vals) (snd vals)))
648 | ((eq? name 'atom?)
649 | (:atom? (fst vals)))
650 | ((eq? name 'zero?)
651 | (zero? (fst vals)))
652 | ((eq? name 'add1)
653 | (add1 (fst vals)))
654 | ((eq? name 'sub1)
655 | (sub1 (fst vals)))
656 | ((eq? name 'number?)
657 | (number? (fst vals)))))
658 |
659 | (define (:atom? e)
660 | (cond ((atom? e) #t)
661 | ((null? e) #f)
662 | ((eq? (car e) 'primitive) #t)
663 | ((eq? (car e) 'non-primitive) #t)
664 | (else #f)))
665 |
666 | ;; how to find value of (f a b)?
667 | ;; f is (lambda (x y) (cons x y))
668 | ;; a=1, b=(2)
669 |
670 | ;; add formals to env and substitute fn body?
671 |
672 | (define (apply-closure closure vals)
673 | (meaning (body-of closure)
674 | (extend-table
675 | (new-entry (formals-of closure) vals)
676 | (table-of closure))))
677 |
678 | ;; test apply closure
679 | ;;
680 |
681 | (define a-closure '((((u v w)
682 | (1 2 3))
683 | ((x y z)
684 | (4 5 6)))
685 | (x y)
686 | (cons z x)))
687 |
688 | (define a-vals '((a b c) (d e f)))
689 |
690 | ;; test this guy
691 | (apply-closure a-closure a-vals)
692 |
693 | (body-of a-closure) ; => (cons z x)
694 | (table-of a-closure) ; => (((u v w) (1 2 3)) ((x y z) (4 5 6)))
695 | (formals-of a-closure) ; => (x y)
696 |
697 | (define a-tbl (extend-table (new-entry (formals-of a-closure) a-vals) (table-of a-closure)))
698 | ;; => (((x y) ((a b c) (d e f))) ((u v w) (1 2 3)) ((x y z) (4 5 6)))
699 |
700 | (expression-to-action '(cons z x))
701 | (meaning (function-of '(cons z x)) a-tbl)
702 | (expression-to-action '(z x)) ; => *application
703 |
704 | ;; follow
705 | (meaning '(cons z x) a-tbl)
706 | (evlis '(z x) a-tbl) ; => (6 (a b c))
707 | (meaning 'cons a-tbl) ; => (primitive cons)
708 |
709 | (apply1 '(primitive cons) '(6 (a b c)))
710 |
711 | ;; (define (*application e table)
712 | ;; (apply1 (meaning (function-of e) table)
713 | ;; (meaning (arguments-of e) table)))
714 | ;; (define (meaning e table)
715 | ;; ((expression-to-action e) e table))
716 |
717 | ;; scratch tests
718 | (define eg0 (new-entry '(a b c d) '(10 11 12 13)))
719 | (define eg1 (new-entry '(w x y z) '(100 101 102 103)))
720 | (define tbl0 (extend-table eg1 (extend-table eg0 '())))
721 | (lookup-in-entry 'b eg0 (lambda (n) n))
722 | (lookup-in-table 'x tbl0 (lambda (n) (display n)))
723 | (*cond '(cond (coffee klatsch) (else party))
724 | '(((coffee) (#t)) ((klatsch party) (5 (6)))))
725 |
726 | ;; play w/interpreter
727 | ;;
728 |
729 | ;; primitive application
730 | (value '(cons 6 (quote (a b c))))
731 |
732 | ;; non-primitive
733 | (value '((lambda (x y)
734 | (cons x y))
735 | 9 (quote (d e f))))
736 |
737 | ;; restart mit-scheme
738 | (restart 1)
739 |
--------------------------------------------------------------------------------
/src/test-manager/doc/testing.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | test-manager/ - An automatic unit-testing framework for MIT Scheme
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 | test-manager/ - An automatic unit-testing framework for MIT Scheme
42 |
43 |
44 |
45 |
46 |
47 | (load "test-manager/load.scm")
48 |
49 | ; This is a test group named simple-stuff
50 | (in-test-group
51 | simple-stuff
52 |
53 | ; This is one test named arithmetic
54 | (define-test (arithmetic)
55 | "Checking that set! and arithmetic work"
56 | (define foo 5)
57 | (check (= 5 foo) "Foo should start as five.")
58 | (set! foo 6)
59 | (check (= 36 (* foo foo))))
60 |
61 | ; Each of these will become a separate anonymous one-form test
62 | (define-each-test
63 | (check (= 4 (+ 2 2)) "Two and two should make four.")
64 | (check (= 2147483648 (+ 2147483647 1)) "Addition shouldn't overflow."))
65 |
66 | ; Each of these will become a separate anonymous one-form test using check
67 | (define-each-check
68 | (= 6 (+ 2 2 2))
69 | (equal? '(1 2 3) (cons 1 '(2 3))))
70 |
71 | ; This is a test that looks like a REPL interaction
72 | (define-test (interactive)
73 | (interaction
74 | (define foo 5)
75 | foo
76 | (produces 5) ; This compares against the value of the last form
77 | (set! foo 6)
78 | (* foo foo)
79 | (produces 36))))
80 |
81 | (run-registered-tests)
82 |
83 | ; Can run individual groups or tests with
84 | (run-test '(simple-stuff))
85 | (run-test '(simple-stuff arithmetic))
86 |
87 |
88 |
89 |
90 | This test framework defines a language for specifying test suites and
91 | a simple set of commands for running them. A test suite is a
92 | collection of individual tests grouped into a hierarchy of test
93 | groups. The test group hierarchy serves to semantically aggregate the
94 | tests, allowing the definition of shared code for set up, tear down,
95 | and surround, and also partition the test namespace to avoid
96 | collisions.
97 | The individual tests are ordinary procedures, with some associated
98 | bookkeeping. A test is considered to pass if it returns normally,
99 | and to fail if it raises some condition that it does not handle
100 | (tests escaping into continuations leads to unspecified behavior).
101 | The framework provides a check macro and a library of assertion
102 | procedures that can be invoked in tests and have the desired behavior
103 | of raising an appropriate condition if they fail. The framework also
104 | provides an interaction macro, together with a produces
105 | procedure, for simulating read-eval-print interactions, and an
106 | extensible pattern-matching facility for easier testing of the
107 | relevant aspects of a result while ignoring the irrelevant ones.
108 |
109 |
110 |
111 | All tests are grouped into a hierarchy of test groups.
112 | At any point in the definition of a test suite, there is an implicit
113 | ``current test group'', into which tests and subgroups can be added. By
114 | default, the current test group is the top-level test group, which is
115 | the root of the test group hierarchy.
116 |
117 | - (define-test (name) expression ... )
118 |
119 | -
120 |
Define a test named name that consists of the given expressions,
121 | and add it to the current test group. When the test is run, the
122 | expressions will be executed in order, just like the body of any
123 | procedure. If the test raises any condition that it does not handle,
124 | it is considered to have failed. If it returns normally, it is
125 | considered to have passed. Usually, tests will contain uses of the
126 | check macro or of assertions from the list below, which raise
127 | appropriate conditions when they fail. In the spirit of Lisp
128 | docstrings, if the first expression in the test body is a literal
129 | string, that string will be included in the failure report if the test
130 | should fail.
131 | This is the most verbose and most expressive test definition syntax.
132 | The four test definition syntaxes provided below are increasingly
133 | terse syntactic sugar for common usage patterns of this syntax.
134 |
135 | - (define-test () expression ... )
136 |
137 | -
138 |
Define an explicitly anonymous test. I can't see why you would want
139 | to do this, but it is provided for completeness.
140 |
141 | - (define-test expression)
142 |
143 | -
144 |
Define a one-expression anonymous test. The single expression will be
145 | printed in the failure report if the test fails. This is a special
146 | case of define-each-test, below.
147 |
148 | - (define-each-test expression ... )
149 |
150 | -
151 |
Define a one-expression anonymous test for each of the given
152 | expressions. If any of the tests fail, the corresponding expression
153 | will be printed in that test's failure report.
154 |
155 | - (define-each-check expression ...)
156 |
157 | -
158 |
Define a one-expression anonymous test for each of the given
159 | expressions by wrapping it in a use of the check macro, below.
160 | If you have many simple independent checks you need to make and
161 | you don't want to invent names for each individual one, this is the
162 | test definition syntax for you.
163 |
164 | - (in-test-group name expression ... )
165 |
166 | -
167 |
Locate (or create) a test subgroup called name in the current test
168 | group. Then temporarily make this subgroup the current test group,
169 | and execute the expressions in the body of in-test-group. This
170 | groups any tests and further subgroups defined by those expressions
171 | into this test group. Test groups can nest arbitrarily deep. Test
172 | groups serve to disambiguate the names of tests, and to group them
173 | semantically. In particular, should a test fail, the names of the
174 | stack of groups it's in will be displayed along with the test name
175 | itself.
176 |
177 | - (define-set-up expression ...)
178 |
179 | -
180 |
Defines a sequence of expressions to be run before every test in
181 | the current test group. Clobbers any previously defined set up
182 | for this group.
183 |
184 | - (define-tear-down expression ...)
185 |
186 | -
187 |
Defines a sequence of expressions to be run after every test in
188 | the current test group. Clobbers any previously defined tear down
189 | for this group.
190 |
191 | - (define-surround expression ...)
192 |
193 | -
194 |
Defines a sequence of expressions to be run surrounding every test in
195 | the current test group. Inside the define-surround, the identifier
196 | run-test is bound to a nullary procedure that actually runs the
197 | test. The test will get run as many times as you call run-test, so
198 | you can run each test under several conditions (or accidentally not
199 | run it at all if you forget to call run-test). Clobbers any
200 | previously defined surround for this group.
201 |
202 | - (define-group-set-up expression ...)
203 |
204 | -
205 |
Defines a sequence of expressions to be run once before running any
206 | test in the current test group. Clobbers any previously defined group
207 | set up for this group.
208 |
209 | - (define-group-tear-down expression ...)
210 |
211 | -
212 |
Defines a sequence of expressions to be run once after running all
213 | tests in the current test group. Clobbers any previously defined
214 | group tear down for this group.
215 |
216 | - (define-group-surround expression ...)
217 |
218 | -
219 |
Defines a sequence of expressions to be run once surrounding running
220 | the tests in the current test group. Inside the
221 | define-group-surround, the identifier run-test is bound to a
222 | nullary procedure that actually runs the tests in this group.
223 | Clobbers any previously defined group surround for this group.
224 |
225 |
226 |
227 |
228 |
229 | The following procedures are provided for running test suites:
230 |
231 | - (run-test name-stack)
232 |
233 | -
234 |
Looks up the test indicated by name-stack in the current test group,
235 | runs it, and prints a report of the results. Returns the number of
236 | tests that did not pass. An empty list for a name stack indicates the
237 | whole group, a singleton list indicates that immediate descendant, a
238 | two-element list indicates a descendant of a descendant, etc. For
239 | example, (run-test '(simple-stuff arithmetic)) would run the first
240 | test defined in the example at the top of this page.
241 |
242 | - (run-registered-tests)
243 |
244 | -
245 |
Runs all tests registered so far, and prints a report of the results.
246 | Returns the number of tests that did not pass. This could have been
247 | defined as (run-test '()).
248 |
249 | - (clear-registered-tests!)
250 |
251 | -
252 |
Unregister all tests. Useful when loading and reloading test suites
253 | interactively. For more elaborate test structure manipulation
254 | facilities, see also test-group.scm.
255 |
256 |
257 |
258 |
259 |
260 | The check macro is the main mechanism for asking tests to actually
261 | test something:
262 |
263 | - (check expression [message])
264 |
265 | -
266 |
Executes the expression, and passes iff that expression returns a true
267 | value (to wit, not #f). If the expression returns #f, constructs a
268 | failure report from the expression, the message if any, and the values
269 | of the immediate subexpressions of the expression.
270 |
271 |
272 | check is a macro so that it can examine the expression provided and
273 | construct a useful failure report if the expression does not return a
274 | true value. Specifically, the failure report includes the expression
275 | itself, as well as the values that all subexpressions (except the
276 | first) of that expression evaluated to. For example,
277 |
278 | (check (< (+ 2 5) (* 3 2)))
279 | fails and reports
280 |
281 | Form : (< (+ 2 5) (* 3 2))
282 | Arg values: (7 6)
283 | so you can see right away both what failed, and, to some degree, what
284 | the problem was.
285 | In the event that the failure report generated by check itself is
286 | inadequate, check also accepts an optional second argument that is
287 | interpreted as a user-supplied message to be added to the failure
288 | report. The message can be either a string, or an arbitrary object
289 | that will be coerced to a string by display, or a promise (as
290 | created by delay), which will be forced and the result coerced to a
291 | string. The latter is useful for checks with dynamically computed
292 | messages, because that computation will then only be performed if the
293 | test actually fails, and in general for doing some computation at
294 | check failure time.
295 |
296 |
297 |
298 | The style of interactively fooling with a piece of code at the
299 | read-eval-print loop differs from the style of writing units tests for
300 | a piece of code and running them. One notable difference is that at
301 | the REPL you write some expression and examine its return value to see
302 | whether it was what you expected, whereas when writing a unit test you
303 | write a check form that contains both the expression under test and
304 | the criterion you expect it to satisfy. In order to decrease the
305 | impedance mismatch between these two ways of verifying what a program
306 | does, test-manager provides the procedure produces, which
307 | retroactively checks the last return value, and the macro
308 | interaction, which enables produces to work inside a unit test.
309 |
310 | - (produces pattern)
311 |
312 | -
313 |
Checks that the return value of the previous evaluated expression
314 | matches (via generic-match, below) the provided pattern. This
315 | works at the REPL via the REPL history, and also works inside a use of
316 | the interaction macro.
317 |
318 | - (interation form ...)
319 |
320 | -
321 |
Tracks the return values of each form and makes them available for
322 | use with produces. For an example, see the last test in the
323 | synopsis.
324 |
325 |
326 |
327 |
328 |
329 | The user-extensible pattern matching facility is the generic procedure
330 | generic-match. This procedure is generic in the sense of the
331 | Scheme Object System provided with MIT Scheme. It can be used in
332 | tests directly, and is automatically invoked by produces above, and
333 | assert-match and assert-no-match below.
334 |
335 | - (generic-match pattern object)
336 |
337 | -
338 |
Returns #t iff the given object matches the given pattern. The
339 | meaning of ``matches'' is user-extensible by adding methods to this
340 | generic procedure. By default compares whether the pattern is
341 | equal? to the object, but also see provided methods below.
342 |
343 | - (generic-match pattern-string string)
344 |
345 | -
346 |
If the pattern and the object are strings, interprets the pattern
347 | as a regular expression and matches it against the object.
348 |
349 | - (generic-match pattern-pair pair)
350 |
351 | -
352 |
If the pattern and the object are pairs, recursively matches their
353 | cars and cdrs against each other.
354 |
355 | - (generic-match pattern-vector vector)
356 |
357 | -
358 |
If the pattern and the object are vectors, recursively matches their
359 | components against each other elementwise.
360 |
361 | - (generic-match x y)
362 |
363 | -
364 |
If the pattern and the object are inexact numbers, checks them for
365 | equality, and then then checks whether the object rounded to five
366 | significant digits equals the pattern. For example, (generic-match
367 | 1.4142 (sqrt 2)) returns #t, as does
368 | (generic-match 1.4142135623730951 (sqrt 2)).
369 |
370 |
371 |
372 |
373 |
374 | The following assertion procedures are provided for situations where
375 | check being a macro makes it unweildy. The message arguments to
376 | the assertions are user-specified messages to print to the output if
377 | the given assertion fails. The assert-proc assertion requires a
378 | message argument because it cannot construct a useful output without
379 | one, and because it is not really meant for extensive direct use. The
380 | message is optional for the other assertions because they can say
381 | something at least mildly informative even without a user-supplied
382 | message. In any case, the message arguments are treated the same way
383 | as by check.
384 |
385 | - (assert-proc message proc)
386 |
387 | -
388 |
Passes iff the given procedure, invoked with no arguments, returns a
389 | true value. On failure, arranges for the given message to appear in
390 | the failure report. This is a primitive assertion in whose terms
391 | other assertions are defined.
392 |
393 | - (assert-true thing [message])
394 |
395 | -
396 |
Passes iff the given value is a true value (to wit, not #f).
397 |
398 | - (assert-false thing [message])
399 |
400 | -
401 |
Passes iff the given value is a false value (to wit, #f).
402 |
403 | - (assert-equal expected actual [message])
404 | Likewise assert-eqv, assert-eq, and assert-=
405 |
406 | -
407 |
Passes iff the given actual value is equivalent, according to the
408 | corresponding predicate, to the expected value. Produces a
409 | reasonably helpful message on failure, and includes the optional
410 | message argument in it if present. When in doubt, use
411 | assert-equal to compare most things; use assert-= to compare
412 | exact numbers like integers; and use assert-in-delta, below, for
413 | inexact numbers like floating points.
414 |
415 | - assert-equals, assert=
416 |
417 | -
418 |
Are aliases for assert-equal and assert-=, respectively.
419 |
420 | - (assert-equivalent predicate [pred-name])
421 |
422 | -
423 |
This is intended as a tool for building custom assertions. Returns an
424 | assertion procedure that compares an expected and an actual value with
425 | the given predicate and produces a reasonable failure message.
426 | assert-equal and company could have been defined in terms of
427 | assert-equivalent as, for example, (define assert-equal
428 | (assert-equivalent equal? "equal?")).
429 |
430 | - assert-< assert-> assert-<= assert->=
431 |
432 | -
433 |
Like assert-=, but with a different comparator. In particular, these
434 | aren't equivalence relations, so the order of arguments matters.
435 |
436 | - (assert-matches pattern object [message])
437 |
438 | -
439 |
Passes iff the given object matches the given pattern, per
440 | generic-match.
441 |
442 | - (assert-no-match pattern object [message])
443 |
444 | -
445 |
Passes iff the given object does not match the given pattern, likewise
446 | per generic-match.
447 |
448 | - (assert-in-delta expected actual delta [message])
449 |
450 | -
451 |
Passes iff the given actual value differs, in absolute value, from
452 | the given expected value by no more than delta. Use this in
453 | preference to assert-= to check sameness of inexact numerical
454 | values.
455 |
456 |
457 |
458 |
459 |
460 |
461 | I originally started this project with MIT Scheme and Guile in mind as
462 | target Scheme implementations. That aim met with success through
463 | version 1.1, but as of version 1.2 I dropped explicit support for the
464 | Guile port. I have left all the portability code intact; the vast
465 | majority of the documented features should work in Guile. Also, since
466 | this software has been two-Scheme for much of its life, I expect it
467 | should not be hard to port to other Schemes.
468 | The specific things that I know do not work in Guile are: produces
469 | does not work in the Guile REPL (though it does still work inside
470 | interaction) which rather defeats its purpose; generic-match is
471 | not actually a generic procedure in Guile (though that could
472 | presumably be fixed by one who knew Guile's generic procedure
473 | facilities); and check does not accept a message argument in Guile.
474 |
475 |
476 |
477 |
478 | This unit testing framework is a work in progress. The test groups do
479 | not support as much shared set up code among their tests as I would
480 | like, and the language for explicit test group handling is
481 | ill-specified and undocumented (peruse test-group.scm if interested).
482 | Suggestions are welcome.
483 |
484 |
485 |
486 |
487 | Alexey Radul, axch@mit.edu
488 |
489 |
490 |
491 |
492 |
--------------------------------------------------------------------------------