heist 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +17 -0
- data/Manifest.txt +23 -19
- data/README.txt +84 -52
- data/lib/builtin/library.scm +208 -10
- data/lib/builtin/primitives.rb +154 -92
- data/lib/builtin/syntax.scm +22 -5
- data/lib/heist.rb +49 -17
- data/lib/parser/nodes.rb +47 -24
- data/lib/parser/ruby.rb +29 -0
- data/lib/parser/scheme.rb +455 -143
- data/lib/parser/scheme.tt +23 -5
- data/lib/repl.rb +19 -16
- data/lib/runtime/binding.rb +24 -2
- data/lib/runtime/callable/continuation.rb +23 -2
- data/lib/runtime/callable/function.rb +122 -21
- data/lib/runtime/callable/macro.rb +169 -123
- data/lib/runtime/callable/macro/expansion.rb +137 -2
- data/lib/runtime/callable/macro/matches.rb +125 -41
- data/lib/runtime/callable/macro/tree.rb +141 -0
- data/lib/runtime/callable/syntax.rb +44 -0
- data/lib/runtime/data/cons.rb +234 -0
- data/lib/runtime/data/expression.rb +15 -6
- data/lib/runtime/data/identifier.rb +19 -2
- data/lib/runtime/frame.rb +102 -35
- data/lib/runtime/runtime.rb +44 -19
- data/lib/runtime/scope.rb +145 -30
- data/lib/runtime/stack.rb +103 -1
- data/lib/runtime/stackless.rb +48 -6
- data/test/arithmetic.scm +11 -2
- data/test/continuations.scm +16 -2
- data/test/equivalence.scm +34 -0
- data/test/functional.scm +4 -0
- data/test/lists.scm +78 -0
- data/test/macro-helpers.scm +1 -0
- data/test/macros.scm +111 -24
- data/test/numbers.scm +30 -8
- data/test/test_heist.rb +67 -12
- metadata +25 -21
- data/lib/builtin/syntax.rb +0 -166
- data/lib/runtime/callable/macro/splice.rb +0 -56
- data/lib/runtime/data/list.rb +0 -36
data/lib/runtime/stackless.rb
CHANGED
@@ -1,7 +1,26 @@
|
|
1
1
|
module Heist
|
2
2
|
class Runtime
|
3
3
|
|
4
|
+
# The +Stackless+ class provides a faster execution model than +Stack+,
|
5
|
+
# as it does not provide fine-grained enough escape points to allow
|
6
|
+
# fully for continuations. Continuations aside, +Stackless+ supports
|
7
|
+
# all the same language features as +Stack+, including using a
|
8
|
+
# trampoline to implement tail call optimisation.
|
4
9
|
class Stackless
|
10
|
+
|
11
|
+
# Returns the result of evaluating the +Expression+ in the given
|
12
|
+
# +Frame+ object. This API probably looks a little weird; it's like
|
13
|
+
# this for consistency with the +Stack+ API so the two can be used
|
14
|
+
# interchangeably without changing the implementation of
|
15
|
+
# <tt>Expression#eval</tt>.
|
16
|
+
#
|
17
|
+
# The expression is evaluated by repeatedly calling <tt>Function</tt>s
|
18
|
+
# until a concrete value is returned. Calling a Scheme procedure
|
19
|
+
# returns a +Body+ object that binds its body to the +Scope+ created
|
20
|
+
# by calling the procedure. As functions do not evaluate themselves
|
21
|
+
# we can turn what would be a recursive process into an iterative
|
22
|
+
# one and optimise tail calls. This technique is known as trampolining.
|
23
|
+
#
|
5
24
|
def <<(frame)
|
6
25
|
@current = frame
|
7
26
|
@current = process! while incomplete?
|
@@ -10,35 +29,58 @@ module Heist
|
|
10
29
|
|
11
30
|
private
|
12
31
|
|
32
|
+
# Process the current +Frame+ or +Body+ on the top of the stack. This
|
33
|
+
# method processes one such object for each call, and we call it
|
34
|
+
# iteratively until the value of <tt>@current</tt> is a concrete value.
|
35
|
+
#
|
36
|
+
# The result of calling a +Function+ (or one of its subclasses) will be
|
37
|
+
# a value (for primitives), a +Body+ or a +Frame+, or a <tt>Macro::Expansion</tt>.
|
38
|
+
# Functions return their bodies rather than evaluating themselves to
|
39
|
+
# allow for trampolining.
|
40
|
+
#
|
13
41
|
def process!
|
14
42
|
expression, scope = @current.expression,
|
15
43
|
@current.scope
|
16
44
|
|
45
|
+
# For function bodies, evaluate all but the last expression and
|
46
|
+
# return the last expression (the tail call) as a new stack frame
|
17
47
|
if Body === @current
|
18
|
-
|
19
|
-
|
48
|
+
limit = expression.size - 1
|
49
|
+
expression.each_with_index do |expr, i|
|
50
|
+
return Frame.new(expr, scope) if i == limit
|
51
|
+
Heist.evaluate(expr, scope)
|
52
|
+
end
|
20
53
|
end
|
21
54
|
|
55
|
+
# Handle single-expression stack frames
|
22
56
|
case expression
|
23
57
|
|
24
|
-
|
25
|
-
|
58
|
+
# If the expression is a list, evaluate the first element and
|
59
|
+
# call the resulting function with the rest of the list
|
60
|
+
when Cons then
|
61
|
+
first = !expression.null? && Heist.evaluate(expression.car, scope)
|
26
62
|
raise SyntaxError.new("Invalid expression: #{expression}") unless Function === first
|
27
|
-
|
28
|
-
value = first.call(scope, expression.rest)
|
63
|
+
value = first.call(scope, expression.cdr)
|
29
64
|
return value unless Macro::Expansion === value
|
30
65
|
|
66
|
+
# If the return value is a macro expansion, inline it and
|
67
|
+
# set the expansion up as the next stack frame to run
|
31
68
|
expression.replace(value.expression)
|
32
69
|
return Frame.new(value.expression, scope)
|
33
70
|
|
71
|
+
# If the expression is an identifier, look up its value in
|
72
|
+
# the current scope
|
34
73
|
when Identifier then
|
35
74
|
scope[expression]
|
36
75
|
|
76
|
+
# Otherwise, assume the expression is data and return it
|
37
77
|
else
|
38
78
|
expression
|
39
79
|
end
|
40
80
|
end
|
41
81
|
|
82
|
+
# Returns +true+ if the current computation is incomplete, that is the
|
83
|
+
# value of <tt>@current</tt> is an expression rather than a value.
|
42
84
|
def incomplete?
|
43
85
|
(Frame === @current) or (Binding === @current)
|
44
86
|
end
|
data/test/arithmetic.scm
CHANGED
@@ -10,7 +10,7 @@
|
|
10
10
|
(assert-equal 8 (expt 2 3))
|
11
11
|
(assert-equal 2 (expt 4 1/2))
|
12
12
|
|
13
|
-
(define (sqrt x)
|
13
|
+
(define (test-sqrt x)
|
14
14
|
(define (square x)
|
15
15
|
(* x x))
|
16
16
|
(define (average x y)
|
@@ -25,7 +25,7 @@
|
|
25
25
|
(sqrt-iter (improve guess))))
|
26
26
|
(sqrt-iter 1.0))
|
27
27
|
|
28
|
-
(assert (< (abs (- (sqrt 9) 3)) 0.0001))
|
28
|
+
(assert (< (abs (- (test-sqrt 9) 3)) 0.0001))
|
29
29
|
|
30
30
|
; http://www.schemers.org/Documents/Standards/R5RS/HTML/r5rs-Z-H-9.html#%_idx_288
|
31
31
|
(assert-equal 1 (modulo 13 4))
|
@@ -42,6 +42,10 @@
|
|
42
42
|
(assert-equal 288 (lcm 32 -36))
|
43
43
|
(assert-equal 288.0 (lcm 32.0 -36)) ; inexact
|
44
44
|
|
45
|
+
(assert-equal 3 (numerator (/ 6 4)))
|
46
|
+
(assert-equal 2 (denominator (/ 6 4)))
|
47
|
+
; (assert-equal 2.0 (denominator (exact->inexact (/ 6 4)))) TODO implement this
|
48
|
+
|
45
49
|
(assert-equal -5.0 (floor -4.3))
|
46
50
|
(assert-equal -4.0 (ceiling -4.3))
|
47
51
|
(assert-equal -4.0 (truncate -4.3))
|
@@ -55,3 +59,8 @@
|
|
55
59
|
(assert-equal 4 (round 7/2)) ; exact
|
56
60
|
(assert-equal 7 (round 7))
|
57
61
|
|
62
|
+
(assert-equal 4 (real-part 4+3i))
|
63
|
+
(assert-equal 3 (imag-part 4+3i))
|
64
|
+
(assert-equal 5 (magnitude 4+3i))
|
65
|
+
(assert-equal 4 (magnitude 4))
|
66
|
+
|
data/test/continuations.scm
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
(define r)
|
2
|
-
(define value)
|
1
|
+
(define r #f)
|
2
|
+
(define value #f)
|
3
3
|
|
4
4
|
; call/cc returning normally
|
5
5
|
(set! value (+ 3 (call/cc (lambda (k) (+ 2 7)))))
|
@@ -142,3 +142,17 @@
|
|
142
142
|
(r 3)
|
143
143
|
(assert-equal 20 value)
|
144
144
|
|
145
|
+
; continuations should work inside loops
|
146
|
+
(set! value (do ((y 10)
|
147
|
+
(acc 1 (* y acc)))
|
148
|
+
((= y 1) acc)
|
149
|
+
(set! y (call/cc (lambda (k)
|
150
|
+
(set! r k)
|
151
|
+
(- y 1))))))
|
152
|
+
(r 1)
|
153
|
+
(assert-equal 362880 value)
|
154
|
+
(r 2)
|
155
|
+
(assert-equal 725760 value)
|
156
|
+
(r 3)
|
157
|
+
(assert-equal 4354560 value)
|
158
|
+
|
@@ -0,0 +1,34 @@
|
|
1
|
+
(assert (eqv? #t #t))
|
2
|
+
(assert (eqv? #f #f))
|
3
|
+
(assert (not (eqv? #t #f)))
|
4
|
+
|
5
|
+
(assert (symbol? 'foo))
|
6
|
+
(assert (eqv? 'foo 'foo))
|
7
|
+
(assert (not (eqv? 'foo 'bar)))
|
8
|
+
|
9
|
+
(assert (eqv? 42 42))
|
10
|
+
(assert (not (eqv? 42 #f)))
|
11
|
+
(assert (not (eqv? 42 42.0)))
|
12
|
+
|
13
|
+
(assert (eqv? '() '()))
|
14
|
+
(assert (not (eqv? '(1 2) '(1 2))))
|
15
|
+
(assert (not (eqv? '() '(1 2))))
|
16
|
+
|
17
|
+
(assert (eqv? ceil ceiling))
|
18
|
+
|
19
|
+
|
20
|
+
; From http://www.schemers.org/Documents/Standards/R5RS/HTML/r5rs-Z-H-9.html#%_idx_210
|
21
|
+
|
22
|
+
(define gen-counter
|
23
|
+
(lambda ()
|
24
|
+
(let ((n 0))
|
25
|
+
(lambda () (set! n (+ n 1)) n))))
|
26
|
+
|
27
|
+
(assert (let ((g (gen-counter)))
|
28
|
+
(eqv? g g)))
|
29
|
+
|
30
|
+
(assert (not (eqv? (gen-counter) (gen-counter))))
|
31
|
+
|
32
|
+
(assert (let ((x '(a)))
|
33
|
+
(eqv? x x)))
|
34
|
+
|
data/test/functional.scm
ADDED
data/test/lists.scm
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
(assert (eqv? '() '()))
|
2
|
+
(assert (not (eqv? '(5) '(5))))
|
3
|
+
|
4
|
+
(assert (null? '()))
|
5
|
+
(assert (list? '()))
|
6
|
+
(assert (not (pair? '())))
|
7
|
+
|
8
|
+
(define foo-list (list (+ 3 2) (* 4 5) 6))
|
9
|
+
(assert (not (eqv? '(5 20 6) foo-list)))
|
10
|
+
(assert (equal? '(5 20 6) foo-list))
|
11
|
+
|
12
|
+
(define bar-list (cons 12 foo-list))
|
13
|
+
(assert (equal? '(12 5 20 6) bar-list))
|
14
|
+
(assert (equal? '(5 20 6) foo-list))
|
15
|
+
(assert-equal 4 (length bar-list))
|
16
|
+
(assert-equal 3 (length foo-list))
|
17
|
+
|
18
|
+
(assert-equal 5 (car foo-list))
|
19
|
+
(assert-equal '(20 6) (cdr foo-list))
|
20
|
+
(assert-equal 20 (cadr foo-list))
|
21
|
+
(assert-equal 6 (caddr foo-list))
|
22
|
+
|
23
|
+
(define eggman '(you (walrus (hurt the) one) ((you) love)))
|
24
|
+
(assert-equal '(hurt the) (cadadr eggman))
|
25
|
+
|
26
|
+
(assert (null? (cdddr eggman)))
|
27
|
+
(assert (null? '()))
|
28
|
+
(assert (list? eggman))
|
29
|
+
(assert (pair? eggman))
|
30
|
+
(assert (not (null? eggman)))
|
31
|
+
|
32
|
+
(define my-pair (cons 'foo 'bar))
|
33
|
+
(assert (pair? my-pair))
|
34
|
+
(assert (not (list? my-pair)))
|
35
|
+
(assert (not (null? my-pair)))
|
36
|
+
|
37
|
+
(set-car! my-pair 27)
|
38
|
+
(set-cdr! my-pair (cons 64 '()))
|
39
|
+
(assert (list? my-pair))
|
40
|
+
(assert (equal? '(27 64) my-pair))
|
41
|
+
(assert-equal 2 (length my-pair))
|
42
|
+
|
43
|
+
(define (f) (list 'not-a-constant-list))
|
44
|
+
(define (g) '(constant-list))
|
45
|
+
(assert-equal 3 (set-car! (f) 3))
|
46
|
+
(assert-raise ImmutableError (set-car! (g) 3))
|
47
|
+
|
48
|
+
(assert-raise SyntaxError ())
|
49
|
+
(assert-raise SyntaxError (1 2 3))
|
50
|
+
|
51
|
+
(assert-equal (cons 1 2) '(1 . 2))
|
52
|
+
(assert-equal (cons 1 (cons 2 3)) '(1 2 . 3))
|
53
|
+
|
54
|
+
(assert-equal '(x y) (append '(x) '(y)))
|
55
|
+
(assert-equal '(a b c d) (append '(a) '(b c d)))
|
56
|
+
(assert-equal '(a (b) (c)) (append '(a (b)) '((c))))
|
57
|
+
(assert-equal '(a b c . d) (append '(a b) '(c . d)))
|
58
|
+
(assert-equal 'a (append '() 'a))
|
59
|
+
|
60
|
+
(assert-equal '(4 3 2 1) (reverse '(1 2 3 4)))
|
61
|
+
(assert-equal '(4 (3 5) 2 1) (reverse '(1 2 (3 5) 4)))
|
62
|
+
|
63
|
+
(assert-equal '(3 4) (list-tail '(1 2 3 4) 2))
|
64
|
+
|
65
|
+
(assert-equal '(a b c) (memq 'a '(a b c)))
|
66
|
+
(assert-equal '(b c) (memq 'b '(a b c)))
|
67
|
+
(assert-equal #f (memq 'a '(b c d)))
|
68
|
+
(assert-equal #f (memq (list 'a) '(b (a) c)))
|
69
|
+
(assert-equal '((a) c) (member (list 'a) '(b (a) c)))
|
70
|
+
|
71
|
+
(define e '((a 1) (b 2) (c 3)))
|
72
|
+
(assert-equal '(a 1) (assq 'a e))
|
73
|
+
(assert-equal '(b 2) (assq 'b e))
|
74
|
+
(assert-equal #f (assq 'd e))
|
75
|
+
(assert-equal #f (assq (list 'a) '(((a)) ((b)) ((c)))))
|
76
|
+
(assert-equal '((a)) (assoc (list 'a) '(((a)) ((b)) ((c)))))
|
77
|
+
(assert-equal '(5 7) (assv 5 '((2 3) (5 7) (11 13))))
|
78
|
+
|
data/test/macro-helpers.scm
CHANGED
data/test/macros.scm
CHANGED
@@ -43,6 +43,34 @@
|
|
43
43
|
|
44
44
|
(assert-equal 8 (dont-rename-else #f 6 8))
|
45
45
|
|
46
|
+
; Check that keywords are ignored if locally bound
|
47
|
+
; example from R6RS -- http://www.r6rs.org/final/html/r6rs/r6rs-Z-H-14.html#node_sec_11.19
|
48
|
+
(assert-equal 'ok (let ((=> #f))
|
49
|
+
(cond (#t => 'ok))))
|
50
|
+
|
51
|
+
; These tests come from tinkering with MZScheme
|
52
|
+
(define-syntax keyword-detect (syntax-rules (word)
|
53
|
+
[(_ word) 'keyword]
|
54
|
+
[(_ data) 'data]))
|
55
|
+
(assert-equal 'keyword (keyword-detect word))
|
56
|
+
(assert-equal 'word (let ([word 4]) (keyword-detect word)))
|
57
|
+
(define word 5)
|
58
|
+
(assert-equal 'keyword (keyword-detect word))
|
59
|
+
(define copy word)
|
60
|
+
(assert-equal 'copy (keyword-detect copy))
|
61
|
+
|
62
|
+
(define-syntax bad-keyword (syntax-rules (with)
|
63
|
+
[(_ with x)
|
64
|
+
`(,with ,x)]))
|
65
|
+
|
66
|
+
(let ([with 16])
|
67
|
+
(assert-raise SyntaxError (bad-keyword with 1)))
|
68
|
+
|
69
|
+
(assert-raise UndefinedVariable (bad-keyword with 2))
|
70
|
+
|
71
|
+
(define with 16)
|
72
|
+
(assert-equal '(16 3) (bad-keyword with 3))
|
73
|
+
|
46
74
|
|
47
75
|
; Test literal matching
|
48
76
|
|
@@ -55,6 +83,18 @@
|
|
55
83
|
(assert-equal 3 (iffy 7 #f 3))
|
56
84
|
|
57
85
|
|
86
|
+
; Test improper patterns
|
87
|
+
(define-syntax rest (syntax-rules ()
|
88
|
+
[(_ foo bar . rest)
|
89
|
+
rest]))
|
90
|
+
(assert-equal 10 (rest 4 5 + 3 7))
|
91
|
+
(let-syntax ([foo (syntax-rules ()
|
92
|
+
[(_ expr ...)
|
93
|
+
(list expr ...)])])
|
94
|
+
(assert-equal '(1 2 3) (foo 1 2 3))
|
95
|
+
(assert-raise SyntaxError (foo 1 2 3 . 4)))
|
96
|
+
|
97
|
+
|
58
98
|
; Test input execution - example from R5RS
|
59
99
|
|
60
100
|
(define-syntax my-or
|
@@ -105,6 +145,20 @@
|
|
105
145
|
(assert-equal 13 (one-or-more (+ 2 4) (+ 3 8) (+ 7 6)))
|
106
146
|
|
107
147
|
|
148
|
+
; Test that null lists terminators don't count as input
|
149
|
+
|
150
|
+
(define-syntax table (syntax-rules ()
|
151
|
+
[(_)
|
152
|
+
'()]
|
153
|
+
[(_ key value rest ...)
|
154
|
+
(cons (cons key value) (table rest ...))]))
|
155
|
+
|
156
|
+
(assert-equal (list (cons 1 2) (cons 3 4) (cons 5 6))
|
157
|
+
(table 1 2 3 4 5 6))
|
158
|
+
|
159
|
+
(assert-raise SyntaxError (table 1 2 3))
|
160
|
+
|
161
|
+
|
108
162
|
; Test execution scope using (swap)
|
109
163
|
(define a 4)
|
110
164
|
(define b 7)
|
@@ -151,6 +205,7 @@
|
|
151
205
|
(syntax-rules ()
|
152
206
|
[(swap (x y))
|
153
207
|
(let ([temp x])
|
208
|
+
(set! x temp) ; Force temp in lazy mode
|
154
209
|
(set! x y)
|
155
210
|
(set! y temp))]))
|
156
211
|
|
@@ -301,6 +356,7 @@
|
|
301
356
|
(triple-deep (((foo bar) (it)) ((wont) (matter really anyway)))
|
302
357
|
((5 6) (2)) ((4) (8 7 2))))
|
303
358
|
|
359
|
+
|
304
360
|
(define-syntax triple-deep2
|
305
361
|
(syntax-rules ()
|
306
362
|
[(_ (((name ...) ...) ...) ((value ...) ...) ...)
|
@@ -313,31 +369,62 @@
|
|
313
369
|
((5 6)) ((4) (8 7 2))))
|
314
370
|
|
315
371
|
|
316
|
-
|
317
|
-
|
318
|
-
|
372
|
+
(define-syntax trial (syntax-rules (with)
|
373
|
+
[(_ ((with (value ...) ...) ...) obj ...)
|
374
|
+
'((obj ((value ...) (value value) ...) ... (obj obj)) ...)]))
|
375
|
+
|
376
|
+
(assert-equal '((bar ((4 2 7) (4 4) (2 2) (7 7)) (bar bar)))
|
377
|
+
(trial ((with (4 2 7))) bar))
|
378
|
+
(assert-equal '((bar (bar bar)))
|
379
|
+
(trial ((with)) bar))
|
380
|
+
(assert-raise MacroTemplateMismatch (trial () bar))
|
381
|
+
|
382
|
+
|
383
|
+
(define-syntax trial2 (syntax-rules (with)
|
384
|
+
[(_ (with (value ...) ...) ... obj ...)
|
385
|
+
'((obj ((value ...) (value value) ...) ... (obj obj)) ...)]))
|
319
386
|
|
320
|
-
(
|
321
|
-
|
322
|
-
|
323
|
-
'((obj ((value ...) (value value) ...) ... (obj obj)) ...
|
324
|
-
(((name name) ... obj obj (obj (name ...))) ...))]))
|
387
|
+
(assert-equal '((foo ((a) (a a)) (foo foo))
|
388
|
+
(bar (bar bar)))
|
389
|
+
(trial2 (with (a)) (with) foo bar))
|
325
390
|
|
326
|
-
(assert-equal '((foo ((a
|
327
|
-
(()) ((q c y n) (q q) (c c) (y y) (n n)) (foo foo))
|
391
|
+
(assert-equal '((foo ((a) (a a)) (foo foo))
|
328
392
|
(bar (bar bar))
|
329
|
-
(baz ((
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
(
|
342
|
-
(
|
393
|
+
(baz ((1 2 3) (1 1) (2 2) (3 3)) (baz baz)))
|
394
|
+
(trial2 (with (a)) (with) (with (1 2 3)) foo bar baz))
|
395
|
+
|
396
|
+
|
397
|
+
; Test nested macros with keywords and nested splices
|
398
|
+
; http://fabiokung.com/2007/10/24/ruby-dsl-to-describe-automata/
|
399
|
+
|
400
|
+
(define-syntax automaton (syntax-rules (:)
|
401
|
+
[(_ init-state
|
402
|
+
[state : response ...]
|
403
|
+
...)
|
404
|
+
(let-syntax ([process-state (syntax-rules (-> accept)
|
405
|
+
[(_ accept)
|
406
|
+
(lambda (stream)
|
407
|
+
(cond [(null? stream) #t]
|
408
|
+
[else #f]))]
|
409
|
+
[(... (_ (label -> target) ...))
|
410
|
+
(lambda (stream)
|
411
|
+
(cond [(null? stream) #f]
|
412
|
+
[else (case (car stream)
|
413
|
+
[(label) (target (cdr stream))]
|
414
|
+
(... ...)
|
415
|
+
[else #f])]))])])
|
416
|
+
(letrec ([state (process-state response ...)]
|
417
|
+
...)
|
418
|
+
init-state))]))
|
419
|
+
|
420
|
+
(define cdar-sequence?
|
421
|
+
(automaton init
|
422
|
+
[init : (c -> more)]
|
423
|
+
[more : (a -> more)
|
424
|
+
(d -> more)
|
425
|
+
(r -> end)]
|
426
|
+
[end : accept]))
|
427
|
+
|
428
|
+
(assert (cdar-sequence? '(c a d a d r)))
|
429
|
+
(assert (not (cdar-sequence? '(a c a d r c))))
|
343
430
|
|