skeem 0.2.21 → 0.2.22
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/.rubocop.yml +33 -337
- data/CHANGELOG.md +7 -0
- data/LICENSE.txt +1 -1
- data/lib/skeem/grammar.rb +13 -13
- data/lib/skeem/interpreter.rb +1 -1
- data/lib/skeem/primitive/primitive_builder.rb +1 -1
- data/lib/skeem/primitive/primitive_procedure.rb +1 -1
- data/lib/skeem/runtime.rb +2 -0
- data/lib/skeem/s_expr_nodes.rb +5 -3
- data/lib/skeem/tokenizer.rb +15 -20
- data/lib/skeem/version.rb +1 -1
- data/lib/skeem.rb +2 -2
- data/skeem.gemspec +8 -5
- data/spec/skeem/datum_dsl_spec.rb +50 -50
- data/spec/skeem/element_visitor_spec.rb +108 -108
- data/spec/skeem/interpreter_spec.rb +171 -169
- data/spec/skeem/lambda_spec.rb +27 -27
- data/spec/skeem/parser_spec.rb +27 -25
- data/spec/skeem/primitive/primitive_builder_spec.rb +127 -131
- data/spec/skeem/primitive/primitive_procedure_spec.rb +28 -28
- data/spec/skeem/runtime_spec.rb +52 -51
- data/spec/skeem/s_expr_nodes_spec.rb +31 -31
- data/spec/skeem/skm_compound_datum_spec.rb +36 -35
- data/spec/skeem/skm_element_spec.rb +35 -34
- data/spec/skeem/skm_empty_list_spec.rb +19 -19
- data/spec/skeem/skm_frame_spec.rb +49 -46
- data/spec/skeem/skm_pair_spec.rb +93 -93
- data/spec/skeem/skm_procedure_exec_spec.rb +11 -11
- data/spec/skeem/skm_simple_datum_spec.rb +102 -95
- data/spec/skeem/skm_unary_expression_spec.rb +60 -61
- data/spec/skeem/tokenizer_spec.rb +52 -52
- data/spec/skeem_spec.rb +1 -1
- data/spec/spec_helper.rb +0 -2
- metadata +62 -18
@@ -10,37 +10,39 @@ module Skeem
|
|
10
10
|
include InterpreterSpec
|
11
11
|
include DatumDSL
|
12
12
|
|
13
|
+
subject(:interpreter) { described_class.new }
|
14
|
+
|
13
15
|
context 'Initialization:' do
|
14
16
|
it 'could be initialized without an argument' do
|
15
|
-
expect {
|
17
|
+
expect { described_class.new }.not_to raise_error
|
16
18
|
end
|
17
19
|
|
18
20
|
it 'could be initialized with a block argument' do
|
19
|
-
expect {
|
21
|
+
expect { described_class.new(&:runtime) }.not_to raise_error
|
20
22
|
end
|
21
23
|
|
22
|
-
it '
|
23
|
-
expect(
|
24
|
+
it 'has a parser' do
|
25
|
+
expect(interpreter.parser).not_to be_nil
|
24
26
|
end
|
25
27
|
|
26
|
-
it '
|
27
|
-
expect(
|
28
|
+
it 'has a runtime object' do
|
29
|
+
expect(interpreter.runtime).to be_a(Runtime)
|
28
30
|
end
|
29
31
|
|
30
|
-
it '
|
31
|
-
expect(
|
32
|
+
it 'comes with built-in functions' do
|
33
|
+
expect(interpreter.runtime.environment).not_to be_empty
|
32
34
|
end
|
33
35
|
|
34
|
-
it '
|
35
|
-
expect(
|
36
|
-
expect(
|
37
|
-
expect(
|
38
|
-
expect(
|
36
|
+
it 'implements base bindings' do
|
37
|
+
expect(interpreter.fetch('number?')).to be_a(Primitive::PrimitiveProcedure)
|
38
|
+
expect(interpreter.fetch('abs')).to be_a(SkmLambda)
|
39
|
+
expect(interpreter.fetch('abs').formals.arity).to eq(1)
|
40
|
+
expect(interpreter.fetch('abs').formals.formals[0]).to eq('x')
|
39
41
|
end
|
40
42
|
end # context
|
41
43
|
|
42
44
|
context 'Interpreting self-evaluating expressions' do
|
43
|
-
it '
|
45
|
+
it 'evaluates isolated booleans' do
|
44
46
|
samples = [
|
45
47
|
['#f', false],
|
46
48
|
['#false', false],
|
@@ -48,12 +50,12 @@ module Skeem
|
|
48
50
|
['#true', true]
|
49
51
|
]
|
50
52
|
compare_to_predicted(samples) do |result, predicted|
|
51
|
-
expect(result).to
|
53
|
+
expect(result).to be_a(SkmBoolean)
|
52
54
|
expect(result).to eq(predicted)
|
53
55
|
end
|
54
56
|
end
|
55
57
|
|
56
|
-
it '
|
58
|
+
it 'evaluates isolated integers' do
|
57
59
|
samples = [
|
58
60
|
['0', 0],
|
59
61
|
['3', 3],
|
@@ -62,13 +64,13 @@ module Skeem
|
|
62
64
|
['-12345', -12345]
|
63
65
|
]
|
64
66
|
compare_to_predicted(samples) do |result, predicted|
|
65
|
-
expect(result).to
|
67
|
+
expect(result).to be_a(SkmInteger)
|
66
68
|
expect(result).to eq(predicted)
|
67
69
|
end
|
68
70
|
end
|
69
71
|
|
70
72
|
# rubocop: disable Style/ExponentialNotation
|
71
|
-
it '
|
73
|
+
it 'evaluates isolated real numbers' do
|
72
74
|
samples = [
|
73
75
|
['0.0', 0.0],
|
74
76
|
['3.14', 3.14],
|
@@ -77,27 +79,27 @@ module Skeem
|
|
77
79
|
['-123e-45', -123e-45]
|
78
80
|
]
|
79
81
|
compare_to_predicted(samples) do |result, predicted|
|
80
|
-
expect(result).to
|
82
|
+
expect(result).to be_a(SkmReal)
|
81
83
|
expect(result).to eq(predicted)
|
82
84
|
end
|
83
85
|
end
|
84
86
|
# rubocop: enable Style/ExponentialNotation
|
85
87
|
|
86
|
-
it '
|
88
|
+
it 'evaluates isolated strings' do
|
87
89
|
samples = [
|
88
90
|
['"Hello, world"', 'Hello, world']
|
89
91
|
]
|
90
92
|
compare_to_predicted(samples) do |result, predicted|
|
91
|
-
expect(result).to
|
93
|
+
expect(result).to be_a(SkmString)
|
92
94
|
expect(result).to eq(predicted)
|
93
95
|
end
|
94
96
|
end
|
95
97
|
|
96
|
-
it '
|
98
|
+
it 'evaluates vector of constants' do
|
97
99
|
require 'benchmark'
|
98
100
|
source = '#(2018 10 20 "Sat")'
|
99
|
-
result =
|
100
|
-
expect(result).to
|
101
|
+
result = interpreter.run(source)
|
102
|
+
expect(result).to be_a(SkmVector)
|
101
103
|
predictions = [
|
102
104
|
[SkmInteger, 2018],
|
103
105
|
[SkmInteger, 10],
|
@@ -105,31 +107,31 @@ module Skeem
|
|
105
107
|
[SkmString, 'Sat']
|
106
108
|
]
|
107
109
|
predictions.each_with_index do |(type, value), index|
|
108
|
-
expect(result.members[index]).to
|
110
|
+
expect(result.members[index]).to be_a(type)
|
109
111
|
expect(result.members[index]).to eq(value)
|
110
112
|
end
|
111
113
|
end
|
112
114
|
end # context
|
113
115
|
|
114
116
|
context 'Built-in primitives' do
|
115
|
-
it '
|
116
|
-
|
117
|
-
expect(
|
117
|
+
it 'implements variable definition' do
|
118
|
+
interpreter.run('(define x 28)')
|
119
|
+
expect(interpreter.fetch('x')).to eq(28)
|
118
120
|
end
|
119
121
|
|
120
|
-
it '
|
122
|
+
it 'implements variable reference' do
|
121
123
|
source = <<-SKEEM
|
122
124
|
; Example from R7RS section 4.1.1
|
123
125
|
(define x 28)
|
124
126
|
x
|
125
127
|
SKEEM
|
126
|
-
result =
|
128
|
+
result = interpreter.run(source)
|
127
129
|
end_result = result.last
|
128
|
-
expect(end_result).to
|
130
|
+
expect(end_result).to be_a(SkmInteger)
|
129
131
|
expect(end_result).to eq(28)
|
130
132
|
end
|
131
133
|
|
132
|
-
it '
|
134
|
+
it 'implements the simple conditional form' do
|
133
135
|
checks = [
|
134
136
|
['(if (> 3 2) "yes")', 'yes'],
|
135
137
|
['(if (> 2 3) "yes")', SkmUndefined.instance]
|
@@ -137,7 +139,7 @@ SKEEM
|
|
137
139
|
compare_to_predicted(checks)
|
138
140
|
end
|
139
141
|
|
140
|
-
it '
|
142
|
+
it 'implements the complete conditional form' do
|
141
143
|
checks = [
|
142
144
|
['(if (> 3 2) "yes" "no")', 'yes'],
|
143
145
|
['(if (> 2 3) "yes" "no")', 'no']
|
@@ -149,11 +151,11 @@ SKEEM
|
|
149
151
|
(- 3 2)
|
150
152
|
(+ 3 2))
|
151
153
|
SKEEM
|
152
|
-
result =
|
154
|
+
result = interpreter.run(source)
|
153
155
|
expect(result).to eq(1)
|
154
156
|
end
|
155
157
|
|
156
|
-
it '
|
158
|
+
it 'implements the cond form' do
|
157
159
|
source = <<-SKEEM
|
158
160
|
(define signum (lambda (x)
|
159
161
|
(cond
|
@@ -162,7 +164,7 @@ SKEEM
|
|
162
164
|
((< x 0) -1)
|
163
165
|
)))
|
164
166
|
SKEEM
|
165
|
-
|
167
|
+
interpreter.run(source)
|
166
168
|
checks = [
|
167
169
|
['(signum 3)', 1],
|
168
170
|
['(signum 0)', 0],
|
@@ -171,7 +173,7 @@ SKEEM
|
|
171
173
|
compare_to_predicted(checks)
|
172
174
|
end
|
173
175
|
|
174
|
-
it '
|
176
|
+
it 'implements the cond form with arrows' do
|
175
177
|
source = <<-SKEEM
|
176
178
|
(define signum (lambda (x)
|
177
179
|
(cond
|
@@ -180,7 +182,7 @@ SKEEM
|
|
180
182
|
((< x 0) => -1)
|
181
183
|
)))
|
182
184
|
SKEEM
|
183
|
-
|
185
|
+
interpreter.run(source)
|
184
186
|
checks = [
|
185
187
|
['(signum 3)', 1],
|
186
188
|
['(signum 0)', 0],
|
@@ -189,7 +191,7 @@ SKEEM
|
|
189
191
|
compare_to_predicted(checks)
|
190
192
|
end
|
191
193
|
|
192
|
-
it '
|
194
|
+
it 'implements the cond ... else form' do
|
193
195
|
source = <<-SKEEM
|
194
196
|
(define signum (lambda (x)
|
195
197
|
(cond
|
@@ -198,7 +200,7 @@ SKEEM
|
|
198
200
|
(else -1)
|
199
201
|
)))
|
200
202
|
SKEEM
|
201
|
-
|
203
|
+
interpreter.run(source)
|
202
204
|
checks = [
|
203
205
|
['(signum 3)', 1],
|
204
206
|
['(signum 0)', 0],
|
@@ -207,7 +209,7 @@ SKEEM
|
|
207
209
|
compare_to_predicted(checks)
|
208
210
|
end
|
209
211
|
|
210
|
-
it '
|
212
|
+
it 'implements the truncate procedure' do
|
211
213
|
checks = [
|
212
214
|
['(truncate -4.3)', -4],
|
213
215
|
['(truncate 3.5)', 3]
|
@@ -215,7 +217,7 @@ SKEEM
|
|
215
217
|
compare_to_predicted(checks)
|
216
218
|
end
|
217
219
|
|
218
|
-
it '
|
220
|
+
it 'implements the quotation of constant literals' do
|
219
221
|
checks = [
|
220
222
|
['(quote a)', 'a'],
|
221
223
|
['(quote 145932)', 145932],
|
@@ -229,25 +231,25 @@ SKEEM
|
|
229
231
|
compare_to_predicted(checks)
|
230
232
|
end
|
231
233
|
|
232
|
-
it '
|
234
|
+
it 'implements the quotation of vectors' do
|
233
235
|
source = '(quote #(a b c))'
|
234
|
-
result =
|
235
|
-
expect(result).to
|
236
|
+
result = interpreter.run(source)
|
237
|
+
expect(result).to be_a(SkmVector)
|
236
238
|
predictions = [
|
237
239
|
[SkmIdentifier, 'a'],
|
238
240
|
[SkmIdentifier, 'b'],
|
239
241
|
[SkmIdentifier, 'c']
|
240
242
|
]
|
241
243
|
predictions.each_with_index do |(type, value), index|
|
242
|
-
expect(result.members[index]).to
|
244
|
+
expect(result.members[index]).to be_a(type)
|
243
245
|
expect(result.members[index]).to eq(value)
|
244
246
|
end
|
245
247
|
end
|
246
248
|
|
247
|
-
it '
|
249
|
+
it 'implements the quotation of lists' do
|
248
250
|
source = '(quote (+ 1 2))'
|
249
|
-
result =
|
250
|
-
expect(result).to
|
251
|
+
result = interpreter.run(source)
|
252
|
+
expect(result).to be_a(SkmPair)
|
251
253
|
predictions = [
|
252
254
|
[SkmIdentifier, '+'],
|
253
255
|
[SkmInteger, 1],
|
@@ -255,25 +257,25 @@ SKEEM
|
|
255
257
|
]
|
256
258
|
members = result.to_a
|
257
259
|
predictions.each_with_index do |(type, value), index|
|
258
|
-
expect(members[index]).to
|
260
|
+
expect(members[index]).to be_a(type)
|
259
261
|
expect(members[index]).to eq(value)
|
260
262
|
end
|
261
263
|
|
262
264
|
source = "'()"
|
263
|
-
result =
|
264
|
-
expect(result).to
|
265
|
+
result = interpreter.run(source)
|
266
|
+
expect(result).to be_a(SkmEmptyList)
|
265
267
|
expect(result).to be_null
|
266
268
|
end
|
267
269
|
|
268
|
-
it '
|
270
|
+
it 'implements the lambda function with one arg' do
|
269
271
|
source = <<-SKEEM
|
270
272
|
; Simplified 'abs' function implementation
|
271
273
|
(define abs
|
272
274
|
(lambda (x)
|
273
275
|
(if (< x 0) (- x) x)))
|
274
276
|
SKEEM
|
275
|
-
|
276
|
-
procedure =
|
277
|
+
interpreter.run(source)
|
278
|
+
procedure = interpreter.fetch('abs')
|
277
279
|
expect(procedure.arity).to eq(1)
|
278
280
|
checks = [
|
279
281
|
['(abs -3)', 3],
|
@@ -283,15 +285,15 @@ SKEEM
|
|
283
285
|
compare_to_predicted(checks)
|
284
286
|
end
|
285
287
|
|
286
|
-
it '
|
288
|
+
it 'implements the lambda function with two args' do
|
287
289
|
source = <<-SKEEM
|
288
290
|
; Simplified 'min' function implementation
|
289
291
|
(define min
|
290
292
|
(lambda (x y)
|
291
293
|
(if (< x y) x y)))
|
292
294
|
SKEEM
|
293
|
-
|
294
|
-
procedure =
|
295
|
+
interpreter.run(source)
|
296
|
+
procedure = interpreter.fetch('min')
|
295
297
|
expect(procedure.arity).to eq(2)
|
296
298
|
checks = [
|
297
299
|
['(min 1 2)', 1],
|
@@ -301,7 +303,7 @@ SKEEM
|
|
301
303
|
compare_to_predicted(checks)
|
302
304
|
end
|
303
305
|
|
304
|
-
it '
|
306
|
+
it 'implements recursive functions' do
|
305
307
|
source = <<-SKEEM
|
306
308
|
; Example from R7RS section 4.1.5
|
307
309
|
(define fact (lambda (n)
|
@@ -310,57 +312,57 @@ SKEEM
|
|
310
312
|
(* n (fact (- n 1))))))
|
311
313
|
(fact 10)
|
312
314
|
SKEEM
|
313
|
-
result =
|
315
|
+
result = interpreter.run(source)
|
314
316
|
expect(result.last.value).to eq(3628800)
|
315
317
|
end
|
316
318
|
|
317
|
-
it '
|
319
|
+
it 'accepts calls to anonymous procedures' do
|
318
320
|
source = '((lambda (x) (+ x x)) 4)'
|
319
|
-
result =
|
321
|
+
result = interpreter.run(source)
|
320
322
|
expect(result).to eq(8)
|
321
323
|
end
|
322
324
|
|
323
|
-
it '
|
325
|
+
it 'supports procedures with variable number of arguments' do
|
324
326
|
# Example from R7RS section 4.1.4
|
325
327
|
source = '((lambda x x) 3 4 5 6)'
|
326
|
-
result =
|
327
|
-
expect(result).to
|
328
|
+
result = interpreter.run(source)
|
329
|
+
expect(result).to be_a(SkmPair)
|
328
330
|
expect(result.length).to eq(4)
|
329
331
|
end
|
330
332
|
|
331
|
-
it '
|
333
|
+
it 'supports procedures with dotted pair arguments' do
|
332
334
|
# Example from R7RS section 4.1.4
|
333
335
|
source = '((lambda (x y . z) z) 3 4 5 6)'
|
334
|
-
result =
|
335
|
-
expect(result).to
|
336
|
+
result = interpreter.run(source)
|
337
|
+
expect(result).to be_a(SkmPair)
|
336
338
|
expect(result.length).to eq(2)
|
337
339
|
expect(result.first).to eq(5)
|
338
340
|
expect(result.last).to eq(6)
|
339
341
|
end
|
340
342
|
|
341
|
-
it '
|
343
|
+
it 'implements the compact define + lambda syntax' do
|
342
344
|
source = <<-SKEEM
|
343
345
|
; Alternative syntax to: (define f (lambda x (+ x 42)))
|
344
346
|
(define (f x)
|
345
347
|
(+ x 42))
|
346
348
|
(f 23)
|
347
349
|
SKEEM
|
348
|
-
result =
|
350
|
+
result = interpreter.run(source)
|
349
351
|
expect(result.last.value).to eq(65)
|
350
352
|
end
|
351
353
|
|
352
|
-
it '
|
354
|
+
it 'implements the compact define + pair syntax' do
|
353
355
|
source = <<-SKEEM
|
354
356
|
; Alternative syntax to: (define nlist (lambda args args))
|
355
357
|
(define (nlist . args)
|
356
358
|
args)
|
357
359
|
(nlist 0 1 2 3 4)
|
358
360
|
SKEEM
|
359
|
-
result =
|
361
|
+
result = interpreter.run(source)
|
360
362
|
expect(result.last.last.value).to eq(4)
|
361
363
|
end
|
362
364
|
|
363
|
-
it '
|
365
|
+
it 'supports the nested define construct' do
|
364
366
|
source = <<-SKEEM
|
365
367
|
(define (quadruple x)
|
366
368
|
(define (double x) ; define a local procedure double
|
@@ -369,23 +371,23 @@ SKEEM
|
|
369
371
|
|
370
372
|
(quadruple 5) ; => 20
|
371
373
|
SKEEM
|
372
|
-
result =
|
374
|
+
result = interpreter.run(source)
|
373
375
|
expect(result.last.value).to eq(20)
|
374
376
|
end
|
375
377
|
end # context
|
376
378
|
|
377
379
|
context 'Binding constructs:' do
|
378
|
-
it '
|
380
|
+
it 'implements local bindings' do
|
379
381
|
source = <<-SKEEM
|
380
382
|
(let ((x 2)
|
381
383
|
(y 3))
|
382
384
|
(* x y))
|
383
385
|
SKEEM
|
384
|
-
result =
|
386
|
+
result = interpreter.run(source)
|
385
387
|
expect(result).to eq(6)
|
386
388
|
end
|
387
389
|
|
388
|
-
it '
|
390
|
+
it 'implements precedence of local bindings' do
|
389
391
|
source = <<-SKEEM
|
390
392
|
(define x 23)
|
391
393
|
(define y 42)
|
@@ -394,11 +396,11 @@ SKEEM
|
|
394
396
|
(let ((y 43))
|
395
397
|
(+ x y))
|
396
398
|
SKEEM
|
397
|
-
result =
|
399
|
+
result = interpreter.run(source)
|
398
400
|
expect(result.last).to eq(66)
|
399
401
|
end
|
400
402
|
|
401
|
-
it '
|
403
|
+
it 'supports the nesting of local bindings' do
|
402
404
|
source = <<-SKEEM
|
403
405
|
(let ((x 2) (y 3))
|
404
406
|
(let ((x 7)
|
@@ -408,7 +410,7 @@ SKEEM
|
|
408
410
|
expect_expr(source).to eq(35)
|
409
411
|
end
|
410
412
|
|
411
|
-
it '
|
413
|
+
it 'supports the nesting of a lambda in a let expression' do
|
412
414
|
source = <<-SKEEM
|
413
415
|
(define make-counter
|
414
416
|
(lambda ()
|
@@ -425,11 +427,11 @@ SKEEM
|
|
425
427
|
(c2)
|
426
428
|
(c1)
|
427
429
|
SKEEM
|
428
|
-
result =
|
430
|
+
result = interpreter.run(source)
|
429
431
|
expect(result.last).to eq(3)
|
430
432
|
end
|
431
433
|
|
432
|
-
it '
|
434
|
+
it 'implements let* expression' do
|
433
435
|
source = <<-SKEEM
|
434
436
|
(let ((x 2) (y 3))
|
435
437
|
(let* ((x 7)
|
@@ -441,28 +443,28 @@ SKEEM
|
|
441
443
|
end # context
|
442
444
|
|
443
445
|
context 'Sequencing constructs:' do
|
444
|
-
it '
|
446
|
+
it 'implements begin as a sequence of expressions' do
|
445
447
|
source = <<-SKEEM
|
446
448
|
(define x 0)
|
447
449
|
(and (= x 0)
|
448
450
|
(begin (set! x 5)
|
449
451
|
(+ x 1))) ; => 6
|
450
452
|
SKEEM
|
451
|
-
result =
|
453
|
+
result = interpreter.run(source)
|
452
454
|
expect(result.last).to eq(6)
|
453
455
|
end
|
454
456
|
|
455
|
-
it '
|
457
|
+
it 'implements begin as a sequence of expressions' do
|
456
458
|
source = <<-SKEEM
|
457
459
|
(let ()
|
458
460
|
(begin (define x 3) (define y 4))
|
459
461
|
(+ x y)) ; => 7
|
460
462
|
SKEEM
|
461
|
-
result =
|
463
|
+
result = interpreter.run(source)
|
462
464
|
expect(result).to eq(7)
|
463
465
|
end
|
464
466
|
|
465
|
-
it '
|
467
|
+
it 'supports begin as lambda body' do
|
466
468
|
source = <<-SKEEM
|
467
469
|
(define kube (lambda (x)
|
468
470
|
(begin
|
@@ -473,13 +475,13 @@ SKEEM
|
|
473
475
|
(kube 3)
|
474
476
|
(kube 4)
|
475
477
|
SKEEM
|
476
|
-
result =
|
478
|
+
result = interpreter.run(source)
|
477
479
|
expect(result.last).to eq(64)
|
478
480
|
end
|
479
481
|
end # context
|
480
482
|
|
481
483
|
context 'Quasiquotation:' do
|
482
|
-
it '
|
484
|
+
it 'implements the quasiquotation of constant literals' do
|
483
485
|
checks = [
|
484
486
|
['(quasiquote a)', 'a'],
|
485
487
|
['(quasiquote 145932)', 145932],
|
@@ -493,44 +495,44 @@ SKEEM
|
|
493
495
|
compare_to_predicted(checks)
|
494
496
|
end
|
495
497
|
|
496
|
-
it '
|
498
|
+
it 'implements the quasiquotation of vectors' do
|
497
499
|
source = '(quasiquote #(a b c))'
|
498
|
-
result =
|
499
|
-
expect(result).to
|
500
|
+
result = interpreter.run(source)
|
501
|
+
expect(result).to be_a(SkmVector)
|
500
502
|
predictions = [
|
501
503
|
[SkmIdentifier, 'a'],
|
502
504
|
[SkmIdentifier, 'b'],
|
503
505
|
[SkmIdentifier, 'c']
|
504
506
|
]
|
505
507
|
predictions.each_with_index do |(type, value), index|
|
506
|
-
expect(result.members[index]).to
|
508
|
+
expect(result.members[index]).to be_a(type)
|
507
509
|
expect(result.members[index]).to eq(value)
|
508
510
|
end
|
509
511
|
end
|
510
512
|
|
511
|
-
it '
|
513
|
+
it 'implements the unquote of vectors' do
|
512
514
|
source = '`#( ,(+ 1 2) 4)'
|
513
|
-
result =
|
514
|
-
expect(result).to
|
515
|
+
result = interpreter.run(source)
|
516
|
+
expect(result).to be_a(SkmVector)
|
515
517
|
predictions = [
|
516
518
|
[SkmInteger, 3],
|
517
519
|
[SkmInteger, 4]
|
518
520
|
]
|
519
521
|
predictions.each_with_index do |(type, value), index|
|
520
|
-
expect(result.members[index]).to
|
522
|
+
expect(result.members[index]).to be_a(type)
|
521
523
|
expect(result.members[index]).to eq(value)
|
522
524
|
end
|
523
525
|
|
524
526
|
source = '`#()'
|
525
|
-
result =
|
526
|
-
expect(result).to
|
527
|
+
result = interpreter.run(source)
|
528
|
+
expect(result).to be_a(SkmVector)
|
527
529
|
expect(result).to be_empty
|
528
530
|
|
529
531
|
# Nested vectors
|
530
532
|
source = '`#(a b #(,(+ 2 3) c) d)'
|
531
|
-
result =
|
533
|
+
result = interpreter.run(source)
|
532
534
|
# expected: #(a b #(5 c) d)
|
533
|
-
expect(result).to
|
535
|
+
expect(result).to be_a(SkmVector)
|
534
536
|
predictions = [
|
535
537
|
[SkmIdentifier, 'a'],
|
536
538
|
[SkmIdentifier, 'b'],
|
@@ -538,57 +540,57 @@ SKEEM
|
|
538
540
|
[SkmIdentifier, 'd']
|
539
541
|
]
|
540
542
|
predictions.each_with_index do |(type, value), index|
|
541
|
-
expect(result.members[index]).to
|
543
|
+
expect(result.members[index]).to be_a(type)
|
542
544
|
expect(result.members[index]).to eq(value)
|
543
545
|
end
|
544
546
|
end
|
545
547
|
|
546
|
-
it '
|
548
|
+
it 'implements the quasiquotation of lists' do
|
547
549
|
source = '(quasiquote (+ 1 2))'
|
548
|
-
result =
|
549
|
-
expect(result).to
|
550
|
+
result = interpreter.run(source)
|
551
|
+
expect(result).to be_a(SkmPair)
|
550
552
|
predictions = [
|
551
553
|
[SkmIdentifier, '+'],
|
552
554
|
[SkmInteger, 1],
|
553
555
|
[SkmInteger, 2]
|
554
556
|
]
|
555
557
|
predictions.each do |(type, value)|
|
556
|
-
expect(result.car).to
|
558
|
+
expect(result.car).to be_a(type)
|
557
559
|
expect(result.car).to eq(value)
|
558
560
|
result = result.cdr
|
559
561
|
end
|
560
562
|
|
561
563
|
source = '`()'
|
562
|
-
result =
|
563
|
-
expect(result).to
|
564
|
+
result = interpreter.run(source)
|
565
|
+
expect(result).to be_a(SkmEmptyList)
|
564
566
|
expect(result).to be_null
|
565
567
|
end
|
566
568
|
|
567
|
-
it '
|
569
|
+
it 'implements the unquote of lists' do
|
568
570
|
source = '`(list ,(+ 1 2) 4)'
|
569
|
-
result =
|
570
|
-
expect(result).to
|
571
|
+
result = interpreter.run(source)
|
572
|
+
expect(result).to be_a(SkmPair)
|
571
573
|
predictions = [
|
572
574
|
[SkmIdentifier, 'list'],
|
573
575
|
[SkmInteger, 3],
|
574
576
|
[SkmInteger, 4]
|
575
577
|
]
|
576
578
|
predictions.each do |(type, value)|
|
577
|
-
expect(result.car).to
|
579
|
+
expect(result.car).to be_a(type)
|
578
580
|
expect(result.car).to eq(value)
|
579
581
|
result = result.cdr
|
580
582
|
end
|
581
583
|
|
582
584
|
source = '`()'
|
583
|
-
result =
|
584
|
-
expect(result).to
|
585
|
+
result = interpreter.run(source)
|
586
|
+
expect(result).to be_a(SkmEmptyList)
|
585
587
|
expect(result).to be_null
|
586
588
|
|
587
589
|
# nested lists
|
588
590
|
source = '`(a b (,(+ 2 3) c) d)'
|
589
|
-
result =
|
591
|
+
result = interpreter.run(source)
|
590
592
|
# expected: (a b (5 c) d)
|
591
|
-
expect(result).to
|
593
|
+
expect(result).to be_a(SkmPair)
|
592
594
|
predictions = [
|
593
595
|
[SkmIdentifier, 'a'],
|
594
596
|
[SkmIdentifier, 'b'],
|
@@ -596,7 +598,7 @@ SKEEM
|
|
596
598
|
[SkmIdentifier, 'd']
|
597
599
|
]
|
598
600
|
predictions.each do |(type, value)|
|
599
|
-
expect(result.car).to
|
601
|
+
expect(result.car).to be_a(type)
|
600
602
|
expect(result.car).to eq(value)
|
601
603
|
result = result.cdr
|
602
604
|
end
|
@@ -619,21 +621,21 @@ SKEEM
|
|
619
621
|
end # context
|
620
622
|
|
621
623
|
context 'Built-in primitive procedures' do
|
622
|
-
it '
|
623
|
-
result =
|
624
|
-
expect(result).to
|
624
|
+
it 'implements the division of numbers' do
|
625
|
+
result = interpreter.run('(/ 24 3)')
|
626
|
+
expect(result).to be_a(SkmInteger)
|
625
627
|
expect(result).to eq(8)
|
626
628
|
end
|
627
629
|
|
628
|
-
it '
|
629
|
-
result =
|
630
|
-
expect(result).to
|
630
|
+
it 'handles arithmetic expressions' do
|
631
|
+
result = interpreter.run('(+ (* 2 100) (* 1 10))')
|
632
|
+
expect(result).to be_a(SkmInteger)
|
631
633
|
expect(result).to eq(210)
|
632
634
|
end
|
633
635
|
end # context
|
634
636
|
|
635
637
|
context 'Built-in standard procedures' do
|
636
|
-
it '
|
638
|
+
it 'implements the zero? predicate' do
|
637
639
|
checks = [
|
638
640
|
['(zero? 3.1)', false],
|
639
641
|
['(zero? -3.1)', false],
|
@@ -645,7 +647,7 @@ SKEEM
|
|
645
647
|
compare_to_predicted(checks)
|
646
648
|
end
|
647
649
|
|
648
|
-
it '
|
650
|
+
it 'implements the positive? predicate' do
|
649
651
|
checks = [
|
650
652
|
['(positive? 3.1)', true],
|
651
653
|
['(positive? -3.1)', false],
|
@@ -657,7 +659,7 @@ SKEEM
|
|
657
659
|
compare_to_predicted(checks)
|
658
660
|
end
|
659
661
|
|
660
|
-
it '
|
662
|
+
it 'implements the negative? predicate' do
|
661
663
|
checks = [
|
662
664
|
['(negative? 3.1)', false],
|
663
665
|
['(negative? -3.1)', true],
|
@@ -669,7 +671,7 @@ SKEEM
|
|
669
671
|
compare_to_predicted(checks)
|
670
672
|
end
|
671
673
|
|
672
|
-
it '
|
674
|
+
it 'implements the even? predicate' do
|
673
675
|
checks = [
|
674
676
|
['(even? 0)', true],
|
675
677
|
['(even? 1)', false],
|
@@ -679,7 +681,7 @@ SKEEM
|
|
679
681
|
compare_to_predicted(checks)
|
680
682
|
end
|
681
683
|
|
682
|
-
it '
|
684
|
+
it 'implements the odd? predicate' do
|
683
685
|
checks = [
|
684
686
|
['(odd? 0)', false],
|
685
687
|
['(odd? 1)', true],
|
@@ -689,7 +691,7 @@ SKEEM
|
|
689
691
|
compare_to_predicted(checks)
|
690
692
|
end
|
691
693
|
|
692
|
-
it '
|
694
|
+
it 'implements the abs function' do
|
693
695
|
checks = [
|
694
696
|
['(abs 3.1)', 3.1],
|
695
697
|
['(abs -3.1)', 3.1],
|
@@ -701,7 +703,7 @@ SKEEM
|
|
701
703
|
compare_to_predicted(checks)
|
702
704
|
end
|
703
705
|
|
704
|
-
it '
|
706
|
+
it 'implements the square function' do
|
705
707
|
checks = [
|
706
708
|
['(square 42)', 1764],
|
707
709
|
['(square 2.0)', 4.0],
|
@@ -710,7 +712,7 @@ SKEEM
|
|
710
712
|
compare_to_predicted(checks)
|
711
713
|
end
|
712
714
|
|
713
|
-
it '
|
715
|
+
it 'implements the not procedure' do
|
714
716
|
checks = [
|
715
717
|
['(not #t)', false],
|
716
718
|
['(not 3)', false],
|
@@ -723,7 +725,7 @@ SKEEM
|
|
723
725
|
compare_to_predicted(checks)
|
724
726
|
end
|
725
727
|
|
726
|
-
it '
|
728
|
+
it 'implements the list procedure' do
|
727
729
|
checks = [
|
728
730
|
['(list)', []],
|
729
731
|
['(list 1)', [1]],
|
@@ -735,7 +737,7 @@ SKEEM
|
|
735
737
|
end
|
736
738
|
end
|
737
739
|
|
738
|
-
it '
|
740
|
+
it 'implements the caar procedure' do
|
739
741
|
checks = [
|
740
742
|
["(caar '((a)))", 'a'],
|
741
743
|
["(caar '((1 2) 3 4))", 1]
|
@@ -743,7 +745,7 @@ SKEEM
|
|
743
745
|
compare_to_predicted(checks)
|
744
746
|
end
|
745
747
|
|
746
|
-
it '
|
748
|
+
it 'implements the cadr procedure' do
|
747
749
|
checks = [
|
748
750
|
["(cadr '(a b c))", 'b'],
|
749
751
|
["(cadr '((1 2) 3 4))", 3]
|
@@ -751,7 +753,7 @@ SKEEM
|
|
751
753
|
compare_to_predicted(checks)
|
752
754
|
end
|
753
755
|
|
754
|
-
it '
|
756
|
+
it 'implements the cdar procedure' do
|
755
757
|
checks = [
|
756
758
|
["(cdar '((7 6 5 4 3 2 1) 8 9))", [6, 5, 4, 3, 2, 1]]
|
757
759
|
]
|
@@ -760,7 +762,7 @@ SKEEM
|
|
760
762
|
end
|
761
763
|
end
|
762
764
|
|
763
|
-
it '
|
765
|
+
it 'implements the cddr procedure' do
|
764
766
|
checks = [
|
765
767
|
["(cddr '(2 1))", []]
|
766
768
|
]
|
@@ -769,7 +771,7 @@ SKEEM
|
|
769
771
|
end
|
770
772
|
end
|
771
773
|
|
772
|
-
it '
|
774
|
+
it 'implements the symbol=? procedure' do
|
773
775
|
checks = [
|
774
776
|
["(symbol=? 'a 'a)", true],
|
775
777
|
["(symbol=? 'a (string->symbol \"a\"))", true],
|
@@ -778,7 +780,7 @@ SKEEM
|
|
778
780
|
compare_to_predicted(checks)
|
779
781
|
end
|
780
782
|
|
781
|
-
it '
|
783
|
+
it 'implements the floor-quotient procedure' do
|
782
784
|
checks = [
|
783
785
|
['(floor-quotient 5 2)', 2],
|
784
786
|
['(floor-quotient -5 2)', -3],
|
@@ -788,7 +790,7 @@ SKEEM
|
|
788
790
|
compare_to_predicted(checks)
|
789
791
|
end
|
790
792
|
|
791
|
-
it '
|
793
|
+
it 'implements the floor-remainder (modulo) procedure' do
|
792
794
|
checks = [
|
793
795
|
['(floor-remainder 16 4)', 0],
|
794
796
|
['(floor-remainder 5 2)', 1],
|
@@ -804,7 +806,7 @@ SKEEM
|
|
804
806
|
compare_to_predicted(checks)
|
805
807
|
end
|
806
808
|
|
807
|
-
it '
|
809
|
+
it 'implements the truncate-quotient procedure' do
|
808
810
|
checks = [
|
809
811
|
['(truncate-quotient 5 2)', 2],
|
810
812
|
['(truncate-quotient -5 2)', -2],
|
@@ -818,7 +820,7 @@ SKEEM
|
|
818
820
|
compare_to_predicted(checks)
|
819
821
|
end
|
820
822
|
|
821
|
-
it '
|
823
|
+
it 'implements the truncate-remainder procedure' do
|
822
824
|
checks = [
|
823
825
|
['(truncate-remainder 5 2)', 1],
|
824
826
|
['(truncate-remainder -5 2)', -1],
|
@@ -832,7 +834,7 @@ SKEEM
|
|
832
834
|
compare_to_predicted(checks)
|
833
835
|
end
|
834
836
|
|
835
|
-
it '
|
837
|
+
it 'implements the test-equal procedure' do
|
836
838
|
checks = [
|
837
839
|
['(test-equal (cons 1 2) (cons 1 2))', true]
|
838
840
|
]
|
@@ -841,27 +843,27 @@ SKEEM
|
|
841
843
|
end # context
|
842
844
|
|
843
845
|
context 'Input/output:' do
|
844
|
-
it '
|
846
|
+
it 'implements the include expression' do
|
845
847
|
initial_dir = Dir.pwd
|
846
848
|
filedir = File.dirname(__FILE__)
|
847
849
|
Dir.chdir(filedir)
|
848
850
|
source = '(include "add4.skm")' # Path is assumed to be relative to pwd
|
849
|
-
result =
|
851
|
+
result = interpreter.run(source)
|
850
852
|
expect(result.last).to eq(10)
|
851
853
|
Dir.chdir(initial_dir)
|
852
854
|
end
|
853
855
|
|
854
|
-
it '
|
856
|
+
it 'implements the newline procedure' do
|
855
857
|
default_stdout = $stdout
|
856
858
|
$stdout = StringIO.new
|
857
|
-
|
859
|
+
interpreter.run('(newline) (newline) (newline)')
|
858
860
|
expect($stdout.string).to match(/\n\n\n$/)
|
859
861
|
$stdout = default_stdout
|
860
862
|
end
|
861
863
|
end # context
|
862
864
|
|
863
865
|
context 'Second-order functions' do
|
864
|
-
it '
|
866
|
+
it 'implements lambda that calls second-order function' do
|
865
867
|
source = <<-SKEEM
|
866
868
|
(define twice
|
867
869
|
(lambda (x)
|
@@ -875,11 +877,11 @@ SKEEM
|
|
875
877
|
(compose f f)))
|
876
878
|
((repeat twice) 5)
|
877
879
|
SKEEM
|
878
|
-
result =
|
880
|
+
result = interpreter.run(source)
|
879
881
|
expect(result.last).to eq(20)
|
880
882
|
end
|
881
883
|
|
882
|
-
it '
|
884
|
+
it 'implements the composition of second-order functions' do
|
883
885
|
source = <<-SKEEM
|
884
886
|
(define twice
|
885
887
|
(lambda (x)
|
@@ -893,20 +895,20 @@ SKEEM
|
|
893
895
|
(compose f f)))
|
894
896
|
((repeat (repeat twice)) 5)
|
895
897
|
SKEEM
|
896
|
-
result =
|
898
|
+
result = interpreter.run(source)
|
897
899
|
expect(result.last).to eq(80)
|
898
900
|
end
|
899
901
|
end # context
|
900
902
|
|
901
903
|
context 'Derived expressions' do
|
902
|
-
it '
|
904
|
+
it 'implements the do form' do
|
903
905
|
source = <<-SKEEM
|
904
906
|
(do ((vec (make-vector 5))
|
905
907
|
(i 0 (+ i 1)))
|
906
908
|
((= i 5) vec)
|
907
909
|
(vector-set! vec i i)) ; => #(0 1 2 3 4)
|
908
910
|
SKEEM
|
909
|
-
result =
|
911
|
+
result = interpreter.run(source)
|
910
912
|
expect(result).to eq([0, 1, 2, 3, 4])
|
911
913
|
|
912
914
|
source = <<-SKEEM
|
@@ -916,26 +918,26 @@ SKEEM
|
|
916
918
|
(sum 0 (+ sum (car x))))
|
917
919
|
((null? x) sum))) ; => 25
|
918
920
|
SKEEM
|
919
|
-
result =
|
921
|
+
result = interpreter.run(source)
|
920
922
|
expect(result).to eq(25)
|
921
923
|
end
|
922
924
|
end # context
|
923
925
|
|
924
|
-
|
925
|
-
|
926
|
-
|
927
|
-
|
928
|
-
|
929
|
-
|
930
|
-
|
931
|
-
|
932
|
-
|
933
|
-
|
934
|
-
|
935
|
-
|
936
|
-
# SKEEM
|
937
|
-
|
938
|
-
|
939
|
-
|
926
|
+
# context 'Macro processing:' do
|
927
|
+
# # it 'parses macro expressions' do
|
928
|
+
# # source = <<-SKEEM
|
929
|
+
# # (define-syntax while
|
930
|
+
# # (syntax-rules ()
|
931
|
+
# # ((while condition body ...)
|
932
|
+
# # (let loop ()
|
933
|
+
# # (if condition
|
934
|
+
# # (begin
|
935
|
+
# # body ...
|
936
|
+
# # (loop))
|
937
|
+
# # #f)))))
|
938
|
+
# # SKEEM
|
939
|
+
# # ptree = interpreter.parse(source)
|
940
|
+
# # end
|
941
|
+
# end
|
940
942
|
end # describe
|
941
943
|
end # module
|