skeem 0.0.17 → 0.0.18
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.
- 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
|