-
Notifications
You must be signed in to change notification settings - Fork 1
/
7-amb.rkt
329 lines (274 loc) · 6.94 KB
/
7-amb.rkt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
#lang racket
(define (lookup env s)
(match env
[(list (cons name val) rest ...)
(if (equal? name s)
val
(lookup rest s))]
[(list)
(error 'unknown (~a s))]))
(define (primitive fun)
(λ (continue fail . args)
(continue fail (apply fun args))))
(define primitives
(list (cons '+ (primitive +))
(cons '- (primitive -))
(cons '/ (primitive /))
(cons '* (primitive *))
(cons '= (primitive =))
(cons '< (primitive <))
(cons '<= (primitive <=))
(cons '> (primitive >))
(cons '>= (primitive >=))
(cons 'list (primitive list))))
(define (extend-environment env names values)
(append (map cons names values) env))
(define (make-function env parameters body)
(λ (continue fail . arguments)
(define new-env (extend-environment env parameters arguments))
(eval-sequence new-env
continue
fail
body)))
(define (eval-arguments env continue fail args)
(match args
['() (continue fail '())]
[(list arg rest ...)
(eval-exp env
(λ (fail2 arg-val)
(eval-arguments env
(λ (fail3 rest-val)
(continue fail3 (cons arg-val rest-val)))
fail2
rest))
fail
arg)]))
(define (eval-application env continue fail fun args)
(eval-exp env
(λ (fail2 fun-val)
(eval-arguments env
(λ (fail3 args-val)
(apply fun-val continue fail3 args-val))
fail2
args))
fail
fun))
(define (eval-require env continue fail exp)
(eval-exp env
(λ (fail2 value)
(if value
(continue fail2 value)
(fail)))
fail
exp))
(define (eval-amb env continue fail exps)
(match exps
[(list) (fail)]
[(list exp rest ...)
(eval-exp env
continue
(λ () (eval-amb env continue fail rest))
exp)]))
(define (eval-sequence env continue fail terms)
(match terms
[(list exp) (eval-exp env continue fail exp)]
[(list (list 'define name exp) rest ...)
(eval-exp env
(λ (fail2 value)
(define new-env (extend-environment env (list name)(list value)))
(eval-sequence new-env continue fail2 rest))
fail
exp)]
[(list trm rest ...)
(eval-exp env
(λ (fail2 ignored)
(eval-sequence env continue fail2 rest))
fail
trm)]))
(define (eval-exp env continue fail exp)
(match exp
[(? symbol?) (continue fail (lookup env exp))]
[(? number?) (continue fail exp)]
[(? boolean?) (continue fail exp)]
[(list 'if exp then else)
(eval-exp env
(λ (fail2 value) (eval-exp env continue fail2 (if value then else)))
fail
exp)]
[(list 'require exp)
(eval-require env
continue
fail
exp)]
[(list 'amb exps ...)
(eval-amb env
continue
fail
exps)]
[(list 'begin terms ...) (eval-sequence env continue fail terms)]
[(list 'λ parameters body ...) (continue fail (make-function env parameters body))]
[(list 'lambda parameters body ...) (continue fail (make-function env parameters body))]
[(list fun args ...) (eval-application env continue fail fun args)]
[_ (error 'wat (~a exp))]))
(define (evaluate input)
(eval-exp primitives
(λ (fail res) res)
(λ () (error 'ohno))
input))
(define (evaluate* input)
(eval-exp primitives
(λ (fail res) (cons res (fail)))
(λ () '())
input))
(define (repl)
(printf "> ")
(define input (read))
(unless (eof-object? input)
(define output (evaluate input))
(printf "~a~n" output)
(repl)))
(module+ test
(require rackunit)
(check-equal?
(evaluate '(+ 1 2))
3)
(check-equal?
(evaluate '(+ 1 2 3))
6)
(check-equal?
(evaluate '(- 2 1))
1)
(check-equal?
(evaluate '(* 2 4))
8)
(check-equal?
(evaluate '(/ 8 2))
4)
(check-equal?
(evaluate '(* 2 (+ 1 (- 4 2))))
6)
(check-exn
exn:fail?
(λ ()
(eval '(foo 1 2))))
(check-equal?
(lookup (list (cons 'a 1)
(cons 'b 2))
'a)
1)
(check-equal?
(lookup (list (cons 'a 1)
(cons 'b 2))
'b)
2)
(check-equal?
(lookup (list (cons 'a 0)
(cons 'a 1)
(cons 'b 2))
'a)
0)
(check-exn
exn:fail?
(λ ()
(lookup (list (cons 'a 1)
(cons 'b 2))
'c))
0)
(check-equal?
(extend-environment (list (cons 'd 2) (cons 'e 1))
(list 'a 'b 'c)
(list 5 4 3))
(list (cons 'a 5) (cons 'b 4) (cons 'c 3) (cons 'd 2) (cons 'e 1)))
(check-equal?
(evaluate
'(begin
(define a 2)
(define b 3)
(+ a b)))
5)
(check-equal?
(evaluate
'(begin
(define a 2)
(define a 3)
(+ a a)))
6)
(check-equal?
(evaluate '((λ () (+ 2 3))))
5)
(check-equal?
(evaluate '((lambda (x y) (+ x y)) 3 4))
7)
(check-equal?
(evaluate
'((lambda ()
(define a 2)
(define b 3)
(+ a b))))
5)
(check-equal?
(evaluate
'((lambda ()
(define a 2)
(define b (lambda (c) (define a 5) (+ a c)))
(b a))))
7)
(check-equal?
(eval-require primitives
(λ (x f) #t)
(λ () #f)
'(< 3 6))
#t)
(check-equal?
(eval-require primitives
(λ (x f) #t)
(λ () #f)
'(> 3 6))
#f)
(check-equal?
(evaluate
'(begin
(define a (amb 1 (- 5 3) 6 8))
(require (> a 5))
a))
6)
(check-equal?
(evaluate '(if #f 3 5))
5)
(check-equal?
(evaluate '(if (< 8 4) 1 0))
0)
(check-equal?
(evaluate '((λ (a b)
(if (> a (+ b b)) 3 6))
9 1))
3)
(check-equal?
(evaluate '((λ (a b)
(if (> a (+ b b)) 3 6))
9 5))
6)
(check-equal?
(evaluate
'(begin
(define a (amb 1 3 5 7))
(define b (amb 2 4 3 6))
(require (= (+ a b) 9))
(list a b)))
'(3 6))
(check-equal?
(evaluate*
'(begin
(define a (amb 1 (- 5 3) 6 8))
(require (> a 5))
a))
'(6 8))
(check-equal?
(evaluate*
'(begin
(define a (amb 1 3 5 7))
(define b (amb 2 4 3 6))
(require (= (+ a b) 9))
(list a b)))
'((3 6) (5 4) (7 2)))
)