skeem 0.0.17 → 0.0.18
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +15 -0
- data/lib/skeem/grammar.rb +7 -7
- data/lib/skeem/interpreter.rb +10 -6
- data/lib/skeem/primitive/primitive_builder.rb +205 -131
- data/lib/skeem/primitive/primitive_procedure.rb +111 -0
- data/lib/skeem/s_expr_builder.rb +31 -11
- data/lib/skeem/s_expr_nodes.rb +134 -23
- data/lib/skeem/standard/base.skm +9 -1
- data/lib/skeem/tokenizer.rb +10 -4
- data/lib/skeem/version.rb +1 -1
- data/spec/skeem/interpreter_spec.rb +81 -255
- data/spec/skeem/primitive/primitive_builder_spec.rb +251 -10
- data/spec/skeem/primitive/primitive_procedure_spec.rb +162 -0
- data/spec/skeem/s_expr_nodes_spec.rb +3 -0
- data/spec/skeem/tokenizer_spec.rb +1 -0
- metadata +5 -3
- data/lib/skeem/primitive_procedure.rb +0 -19
data/lib/skeem/version.rb
CHANGED
@@ -20,6 +20,12 @@ module Skeem
|
|
20
20
|
it 'should come with built-in functions' do
|
21
21
|
expect(subject.runtime.environment).not_to be_empty
|
22
22
|
end
|
23
|
+
|
24
|
+
it 'should implement base bindings' do
|
25
|
+
expect(subject.fetch('number?')).to be_kind_of(Primitive::PrimitiveProcedure)
|
26
|
+
expect(subject.fetch('abs')).to be_kind_of(SkmDefinition)
|
27
|
+
expect(subject.fetch('abs').expression).to be_kind_of(SkmLambda)
|
28
|
+
end
|
23
29
|
end # context
|
24
30
|
|
25
31
|
context 'Interpreting self-evaluating expressions' do
|
@@ -92,8 +98,9 @@ module Skeem
|
|
92
98
|
x
|
93
99
|
SKEEM
|
94
100
|
result = subject.run(source)
|
95
|
-
|
96
|
-
expect(
|
101
|
+
end_result = result.last
|
102
|
+
expect(end_result).to be_kind_of(SkmInteger)
|
103
|
+
expect(end_result.value).to eq(28)
|
97
104
|
end
|
98
105
|
|
99
106
|
it 'should implement the simple conditional form' do
|
@@ -129,11 +136,13 @@ SKEEM
|
|
129
136
|
it 'should implement the lambda function with one arg' do
|
130
137
|
source = <<-SKEEM
|
131
138
|
; Simplified 'abs' function implementation
|
132
|
-
(define abs
|
139
|
+
(define abs
|
133
140
|
(lambda (x)
|
134
141
|
(if (< x 0) (- x) x)))
|
135
142
|
SKEEM
|
136
143
|
subject.run(source)
|
144
|
+
procedure = subject.fetch('abs').expression
|
145
|
+
expect(procedure.arity).to eq(1)
|
137
146
|
result = subject.run('(abs -3)')
|
138
147
|
expect(result.value).to eq(3)
|
139
148
|
result = subject.run('(abs 0)')
|
@@ -141,36 +150,65 @@ SKEEM
|
|
141
150
|
result = subject.run('(abs 3)')
|
142
151
|
expect(result.value).to eq(3)
|
143
152
|
end
|
144
|
-
|
153
|
+
|
145
154
|
it 'should implement the lambda function with two args' do
|
146
155
|
source = <<-SKEEM
|
147
156
|
; Simplified 'min' function implementation
|
148
|
-
(define min
|
157
|
+
(define min
|
149
158
|
(lambda (x y)
|
150
159
|
(if (< x y) x y)))
|
151
160
|
SKEEM
|
152
161
|
subject.run(source)
|
162
|
+
procedure = subject.fetch('min').expression
|
163
|
+
expect(procedure.arity).to eq(2)
|
153
164
|
result = subject.run('(min 1 2)')
|
154
165
|
expect(result.value).to eq(1)
|
155
166
|
result = subject.run('(min 2 1)')
|
156
167
|
expect(result.value).to eq(1)
|
157
168
|
result = subject.run('(min 2 2)')
|
158
169
|
expect(result.value).to eq(2)
|
159
|
-
end
|
160
|
-
|
170
|
+
end
|
171
|
+
|
161
172
|
it 'should implement recursive functions' do
|
162
173
|
source = <<-SKEEM
|
163
174
|
; Example from R7RS section 4.1.5
|
164
|
-
(define fact (lambda (n)
|
165
|
-
(if (<= n 1)
|
166
|
-
1
|
175
|
+
(define fact (lambda (n)
|
176
|
+
(if (<= n 1)
|
177
|
+
1
|
167
178
|
(* n (fact (- n 1))))))
|
168
179
|
(fact 10)
|
169
180
|
SKEEM
|
170
181
|
result = subject.run(source)
|
171
|
-
expect(result.value).to eq(3628800)
|
182
|
+
expect(result.last.value).to eq(3628800)
|
183
|
+
end
|
184
|
+
|
185
|
+
it 'should accept calls to anonymous procedures' do
|
186
|
+
source = '((lambda (x) (+ x x)) 4)'
|
187
|
+
result = subject.run(source)
|
188
|
+
expect(result.value).to eq(8)
|
189
|
+
end
|
190
|
+
|
191
|
+
|
192
|
+
it 'should support procedures with variable number of arguments' do
|
193
|
+
# Example from R7RS section 4.1.4
|
194
|
+
source = '((lambda x x) 3 4 5 6)'
|
195
|
+
result = subject.run(source)
|
196
|
+
expect(result).to be_kind_of(SkmList)
|
197
|
+
expect(result.length).to eq(4)
|
172
198
|
end
|
173
199
|
|
200
|
+
it 'should support procedures with dotted pair arguments' do
|
201
|
+
# Example from R7RS section 4.1.4
|
202
|
+
source = '((lambda (x y . z) z) 3 4 5 6)'
|
203
|
+
result = subject.run(source)
|
204
|
+
expect(result).to be_kind_of(SkmList)
|
205
|
+
expect(result.length).to eq(2)
|
206
|
+
expect(result.head.value).to eq(5)
|
207
|
+
expect(result.last.value).to eq(6)
|
208
|
+
end
|
209
|
+
|
210
|
+
|
211
|
+
=begin
|
174
212
|
it 'should implement the compact define + lambda syntax' do
|
175
213
|
source = <<-SKEEM
|
176
214
|
; Alternative syntax to: (define f (lambda x (+ x 42)))
|
@@ -179,254 +217,30 @@ SKEEM
|
|
179
217
|
(f 23)
|
180
218
|
SKEEM
|
181
219
|
result = subject.run(source)
|
182
|
-
expect(result.value).to eq(65)
|
183
|
-
end
|
184
|
-
|
185
|
-
it 'should call anonymous procedure' do
|
186
|
-
source = '((lambda (x) (+ x x)) 4)'
|
187
|
-
result = subject.run(source)
|
188
|
-
expect(result.value).to eq(8)
|
220
|
+
expect(result.value).to eq(65)
|
189
221
|
end
|
222
|
+
=end
|
190
223
|
end # context
|
191
224
|
|
192
225
|
context 'Built-in primitive procedures' do
|
193
|
-
it 'should implement the addition of integers' do
|
194
|
-
result = subject.run('(+ 2 2)')
|
195
|
-
expect(result).to be_kind_of(SkmInteger)
|
196
|
-
expect(result.value).to eq(4)
|
197
|
-
end
|
198
|
-
|
199
|
-
it 'should implement the addition of real numbers' do
|
200
|
-
result = subject.run('(+ 2 2.34)')
|
201
|
-
expect(result).to be_kind_of(SkmReal)
|
202
|
-
expect(result.value).to eq(4.34)
|
203
|
-
end
|
204
|
-
|
205
|
-
it 'should implement the negation of integer' do
|
206
|
-
result = subject.run('(- 3)')
|
207
|
-
expect(result).to be_kind_of(SkmInteger)
|
208
|
-
expect(result.value).to eq(-3)
|
209
|
-
end
|
210
|
-
|
211
|
-
it 'should implement the substraction of integers' do
|
212
|
-
result = subject.run('(- 3 4)')
|
213
|
-
expect(result).to be_kind_of(SkmInteger)
|
214
|
-
expect(result.value).to eq(-1)
|
215
|
-
|
216
|
-
result = subject.run('(- 3 4 5)')
|
217
|
-
expect(result).to be_kind_of(SkmInteger)
|
218
|
-
expect(result.value).to eq(-6)
|
219
|
-
end
|
220
|
-
|
221
|
-
it 'should implement the product of numbers' do
|
222
|
-
checks = [
|
223
|
-
['(* 5 8)', 40],
|
224
|
-
['(* 2 3 4)', 24]
|
225
|
-
]
|
226
|
-
checks.each do |(skeem_expr, expectation)|
|
227
|
-
result = subject.run(skeem_expr)
|
228
|
-
expect(result.value).to eq(expectation)
|
229
|
-
end
|
230
|
-
end
|
231
|
-
|
232
226
|
it 'should implement the division of numbers' do
|
233
227
|
result = subject.run('(/ 24 3)')
|
234
228
|
expect(result).to be_kind_of(SkmInteger)
|
235
229
|
expect(result.value).to eq(8)
|
236
230
|
end
|
237
231
|
|
238
|
-
it 'should
|
232
|
+
it 'should handle arithmetic expressions' do
|
239
233
|
result = subject.run('(+ (* 2 100) (* 1 10))')
|
240
234
|
expect(result).to be_kind_of(SkmInteger)
|
241
235
|
expect(result.value).to eq(210)
|
242
236
|
end
|
243
|
-
|
244
|
-
it 'should implement the floor-remainder (modulo) procedure' do
|
245
|
-
checks = [
|
246
|
-
['(modulo 16 4)', 0],
|
247
|
-
['(modulo 5 2)', 1],
|
248
|
-
['(modulo -45.0 7)', 4.0],
|
249
|
-
['(modulo 10.0 -3.0)', -2.0],
|
250
|
-
['(modulo -17 -9)', -8]
|
251
|
-
]
|
252
|
-
checks.each do |(skeem_expr, expectation)|
|
253
|
-
result = subject.run(skeem_expr)
|
254
|
-
expect(result.value).to eq(expectation)
|
255
|
-
end
|
256
|
-
end
|
257
|
-
|
258
|
-
it 'should implement the equality operator' do
|
259
|
-
checks = [
|
260
|
-
['(= 3 3)', true],
|
261
|
-
['(= 3 (+ 1 2) (- 4 1))', true],
|
262
|
-
['(= "foo" "foo")', true],
|
263
|
-
['(= 3 4)', false],
|
264
|
-
['(= "foo" "bar")', false]
|
265
|
-
]
|
266
|
-
checks.each do |(skeem_expr, expectation)|
|
267
|
-
result = subject.run(skeem_expr)
|
268
|
-
expect(result.value).to eq(expectation)
|
269
|
-
end
|
270
|
-
end
|
271
|
-
|
272
|
-
it 'should implement the less than operator' do
|
273
|
-
checks = [
|
274
|
-
['(< 3 4)', true],
|
275
|
-
['(< 3 (+ 2 2) (+ 4 1))', true],
|
276
|
-
['(< 3 3)', false],
|
277
|
-
['(< 3 2)', false],
|
278
|
-
['(< 3 4 5 4)', false]
|
279
|
-
]
|
280
|
-
checks.each do |(skeem_expr, expectation)|
|
281
|
-
result = subject.run(skeem_expr)
|
282
|
-
expect(result.value).to eq(expectation)
|
283
|
-
end
|
284
|
-
end
|
285
|
-
|
286
|
-
it 'should implement the greater than operator' do
|
287
|
-
checks = [
|
288
|
-
['(> 3 2)', true],
|
289
|
-
['(> 3 (- 4 2) (- 2 1))', true],
|
290
|
-
['(> 3 3)', false],
|
291
|
-
['(> 3 4)', false],
|
292
|
-
['(> 3 2 1 2)', false]
|
293
|
-
]
|
294
|
-
checks.each do |(skeem_expr, expectation)|
|
295
|
-
result = subject.run(skeem_expr)
|
296
|
-
expect(result.value).to eq(expectation)
|
297
|
-
end
|
298
|
-
end
|
299
|
-
|
300
|
-
it 'should implement the less or equal than operator' do
|
301
|
-
checks = [
|
302
|
-
['(<= 3 4)', true],
|
303
|
-
['(<= 3 (+ 2 2) (+ 4 1))', true],
|
304
|
-
['(<= 3 3)', true],
|
305
|
-
['(<= 3 2)', false],
|
306
|
-
['(<= 3 4 5 4)', false],
|
307
|
-
['(<= 3 4 5 5)', true]
|
308
|
-
]
|
309
|
-
checks.each do |(skeem_expr, expectation)|
|
310
|
-
result = subject.run(skeem_expr)
|
311
|
-
expect(result.value).to eq(expectation)
|
312
|
-
end
|
313
|
-
end
|
314
|
-
|
315
|
-
it 'should implement the greater or equal than operator' do
|
316
|
-
checks = [
|
317
|
-
['(>= 3 2)', true],
|
318
|
-
['(>= 3 (- 4 2) (- 2 1))', true],
|
319
|
-
['(>= 3 3)', true],
|
320
|
-
['(>= 3 4)', false],
|
321
|
-
['(>= 3 2 1 2)', false],
|
322
|
-
['(>= 3 2 1 1)', true]
|
323
|
-
]
|
324
|
-
checks.each do |(skeem_expr, expectation)|
|
325
|
-
result = subject.run(skeem_expr)
|
326
|
-
expect(result.value).to eq(expectation)
|
327
|
-
end
|
328
|
-
end
|
329
|
-
|
330
|
-
it 'should implement the number? predicate' do
|
331
|
-
checks = [
|
332
|
-
['(number? 3.1)', true],
|
333
|
-
['(number? 3)', true],
|
334
|
-
['(number? "3")', false],
|
335
|
-
['(number? #t)', false]
|
336
|
-
]
|
337
|
-
checks.each do |(skeem_expr, expectation)|
|
338
|
-
result = subject.run(skeem_expr)
|
339
|
-
expect(result.value).to eq(expectation)
|
340
|
-
end
|
341
|
-
end
|
342
|
-
|
343
|
-
it 'should implement the real? predicate' do
|
344
|
-
checks = [
|
345
|
-
['(real? 3.1)', true],
|
346
|
-
['(real? 3)', true],
|
347
|
-
['(real? "3")', false],
|
348
|
-
['(real? #t)', false]
|
349
|
-
]
|
350
|
-
checks.each do |(skeem_expr, expectation)|
|
351
|
-
result = subject.run(skeem_expr)
|
352
|
-
expect(result.value).to eq(expectation)
|
353
|
-
end
|
354
|
-
end
|
355
|
-
|
356
|
-
it 'should implement the integer? predicate' do
|
357
|
-
checks = [
|
358
|
-
['(integer? 3.1)', false],
|
359
|
-
# ['(integer? 3.0)', true], TODO: should pass when exact? will be implemented
|
360
|
-
['(integer? 3)', true],
|
361
|
-
['(integer? "3")', false],
|
362
|
-
['(integer? #t)', false]
|
363
|
-
]
|
364
|
-
checks.each do |(skeem_expr, expectation)|
|
365
|
-
result = subject.run(skeem_expr)
|
366
|
-
expect(result.value).to eq(expectation)
|
367
|
-
end
|
368
|
-
end
|
369
|
-
|
370
|
-
it 'should implement the not procedure' do
|
371
|
-
checks = [
|
372
|
-
['(not #t)', false],
|
373
|
-
['(not 3)', false],
|
374
|
-
['(not #f)', true]
|
375
|
-
]
|
376
|
-
checks.each do |(skeem_expr, expectation)|
|
377
|
-
result = subject.run(skeem_expr)
|
378
|
-
expect(result.value).to eq(expectation)
|
379
|
-
end
|
380
|
-
end
|
381
|
-
|
382
|
-
it 'should implement the boolean? procedure' do
|
383
|
-
checks = [
|
384
|
-
['(boolean? #f)', true],
|
385
|
-
['(boolean? 0)', false]
|
386
|
-
]
|
387
|
-
checks.each do |(skeem_expr, expectation)|
|
388
|
-
result = subject.run(skeem_expr)
|
389
|
-
expect(result.value).to eq(expectation)
|
390
|
-
end
|
391
|
-
end
|
392
|
-
|
393
|
-
it 'should implement the string? procedure' do
|
394
|
-
checks = [
|
395
|
-
['(string? #f)', false],
|
396
|
-
['(string? 3)', false],
|
397
|
-
['(string? "hi")', true]
|
398
|
-
]
|
399
|
-
checks.each do |(skeem_expr, expectation)|
|
400
|
-
result = subject.run(skeem_expr)
|
401
|
-
expect(result.value).to eq(expectation)
|
402
|
-
end
|
403
|
-
end
|
404
|
-
|
405
|
-
it 'should implement the symbol? procedure' do
|
406
|
-
checks = [
|
407
|
-
['(symbol? #f)', false],
|
408
|
-
['(symbol? "bar")', false]
|
409
|
-
]
|
410
|
-
checks.each do |(skeem_expr, expectation)|
|
411
|
-
result = subject.run(skeem_expr)
|
412
|
-
expect(result.value).to eq(expectation)
|
413
|
-
end
|
414
|
-
end
|
415
|
-
|
416
|
-
it 'should implement the newline procedure' do
|
417
|
-
default_stdout = $stdout
|
418
|
-
$stdout = StringIO.new()
|
419
|
-
subject.run('(newline) (newline) (newline)')
|
420
|
-
expect($stdout.string).to match(/\n\n\n$/)
|
421
|
-
$stdout = default_stdout
|
422
|
-
end
|
423
237
|
end # context
|
424
|
-
|
238
|
+
|
425
239
|
context 'Built-in standard procedures' do
|
426
240
|
it 'should implement the zero? predicate' do
|
427
241
|
checks = [
|
428
242
|
['(zero? 3.1)', false],
|
429
|
-
['(zero? -3.1)', false],
|
243
|
+
['(zero? -3.1)', false],
|
430
244
|
['(zero? 0)', true],
|
431
245
|
['(zero? 0.0)', true],
|
432
246
|
['(zero? 3)', false],
|
@@ -438,10 +252,10 @@ SKEEM
|
|
438
252
|
end
|
439
253
|
end
|
440
254
|
|
441
|
-
it 'should implement the positive? predicate' do
|
255
|
+
it 'should implement the positive? predicate' do
|
442
256
|
checks = [
|
443
257
|
['(positive? 3.1)', true],
|
444
|
-
['(positive? -3.1)', false],
|
258
|
+
['(positive? -3.1)', false],
|
445
259
|
['(positive? 0)', true],
|
446
260
|
['(positive? 0.0)', true],
|
447
261
|
['(positive? 3)', true],
|
@@ -453,10 +267,10 @@ SKEEM
|
|
453
267
|
end
|
454
268
|
end
|
455
269
|
|
456
|
-
it 'should implement the positive? predicate' do
|
270
|
+
it 'should implement the positive? predicate' do
|
457
271
|
checks = [
|
458
272
|
['(positive? 3.1)', true],
|
459
|
-
['(positive? -3.1)', false],
|
273
|
+
['(positive? -3.1)', false],
|
460
274
|
['(positive? 0)', true],
|
461
275
|
['(positive? 0.0)', true],
|
462
276
|
['(positive? 3)', true],
|
@@ -468,10 +282,10 @@ SKEEM
|
|
468
282
|
end
|
469
283
|
end
|
470
284
|
|
471
|
-
it 'should implement the negative? predicate' do
|
285
|
+
it 'should implement the negative? predicate' do
|
472
286
|
checks = [
|
473
287
|
['(negative? 3.1)', false],
|
474
|
-
['(negative? -3.1)', true],
|
288
|
+
['(negative? -3.1)', true],
|
475
289
|
['(negative? 0)', false],
|
476
290
|
['(negative? 0.0)', false],
|
477
291
|
['(negative? 3)', false],
|
@@ -483,10 +297,10 @@ SKEEM
|
|
483
297
|
end
|
484
298
|
end
|
485
299
|
|
486
|
-
it 'should implement the even? predicate' do
|
300
|
+
it 'should implement the even? predicate' do
|
487
301
|
checks = [
|
488
302
|
['(even? 0)', true],
|
489
|
-
['(even? 1)', false],
|
303
|
+
['(even? 1)', false],
|
490
304
|
['(even? 2.0)', true],
|
491
305
|
['(even? -120762398465)', false]
|
492
306
|
]
|
@@ -496,10 +310,10 @@ SKEEM
|
|
496
310
|
end
|
497
311
|
end
|
498
312
|
|
499
|
-
it 'should implement the odd? predicate' do
|
313
|
+
it 'should implement the odd? predicate' do
|
500
314
|
checks = [
|
501
315
|
['(odd? 0)', false],
|
502
|
-
['(odd? 1)', true],
|
316
|
+
['(odd? 1)', true],
|
503
317
|
['(odd? 2.0)', false],
|
504
318
|
['(odd? -120762398465)', true]
|
505
319
|
]
|
@@ -507,12 +321,12 @@ SKEEM
|
|
507
321
|
result = subject.run(skeem_expr)
|
508
322
|
expect(result.value).to eq(expectation)
|
509
323
|
end
|
510
|
-
end
|
324
|
+
end
|
511
325
|
|
512
|
-
it 'should implement the abs function' do
|
326
|
+
it 'should implement the abs function' do
|
513
327
|
checks = [
|
514
328
|
['(abs 3.1)', 3.1],
|
515
|
-
['(abs -3.1)', 3.1],
|
329
|
+
['(abs -3.1)', 3.1],
|
516
330
|
['(abs 0)', 0],
|
517
331
|
['(abs 0.0)', 0],
|
518
332
|
['(abs 3)', 3],
|
@@ -524,16 +338,28 @@ SKEEM
|
|
524
338
|
end
|
525
339
|
end
|
526
340
|
|
527
|
-
it 'should implement the square function' do
|
341
|
+
it 'should implement the square function' do
|
528
342
|
checks = [
|
529
343
|
['(square 42)', 1764],
|
530
|
-
['(square 2.0)', 4.0],
|
344
|
+
['(square 2.0)', 4.0],
|
531
345
|
['(square -7)', 49]
|
532
346
|
]
|
533
347
|
checks.each do |(skeem_expr, expectation)|
|
534
348
|
result = subject.run(skeem_expr)
|
535
349
|
expect(result.value).to eq(expectation)
|
536
350
|
end
|
351
|
+
end
|
352
|
+
|
353
|
+
it 'should implement the list procedure' do
|
354
|
+
checks = [
|
355
|
+
#['(list)', []],
|
356
|
+
['(list 1)', [1]],
|
357
|
+
['(list 1 2 3 4)', [1, 2, 3, 4]]
|
358
|
+
]
|
359
|
+
checks.each do |(skeem_expr, expectation)|
|
360
|
+
result = subject.run(skeem_expr)
|
361
|
+
expect(result.members.map(&:value)).to eq(expectation)
|
362
|
+
end
|
537
363
|
end
|
538
364
|
end # context
|
539
365
|
end # describe
|