skeem 0.2.15 → 0.2.16
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 +17 -11
- data/CHANGELOG.md +5 -0
- data/Gemfile +2 -0
- data/README.md +3 -2
- data/Rakefile +2 -0
- data/appveyor.yml +3 -4
- data/bin/skeem +15 -15
- data/lib/skeem.rb +2 -0
- data/lib/skeem/datum_dsl.rb +12 -3
- data/lib/skeem/element_visitor.rb +5 -2
- data/lib/skeem/grammar.rb +86 -24
- data/lib/skeem/interpreter.rb +5 -3
- data/lib/skeem/parser.rb +6 -4
- data/lib/skeem/primitive/primitive_builder.rb +128 -115
- data/lib/skeem/primitive/primitive_procedure.rb +17 -20
- data/lib/skeem/runtime.rb +9 -5
- data/lib/skeem/s_expr_builder.rb +46 -104
- data/lib/skeem/s_expr_nodes.rb +116 -90
- data/lib/skeem/skeem_exception.rb +0 -0
- data/lib/skeem/skm_binding.rb +6 -7
- data/lib/skeem/skm_compound_datum.rb +8 -4
- data/lib/skeem/skm_element.rb +14 -12
- data/lib/skeem/skm_empty_list.rb +6 -4
- data/lib/skeem/skm_exception.rb +9 -0
- data/lib/skeem/skm_expression.rb +3 -1
- data/lib/skeem/skm_frame.rb +3 -2
- data/lib/skeem/skm_pair.rb +23 -18
- data/lib/skeem/skm_procedure_exec.rb +8 -6
- data/lib/skeem/skm_simple_datum.rb +13 -12
- data/lib/skeem/skm_unary_expression.rb +15 -17
- data/lib/skeem/tokenizer.rb +32 -25
- data/lib/skeem/version.rb +3 -1
- data/skeem.gemspec +6 -4
- data/spec/skeem/add4.skm +4 -0
- data/spec/skeem/datum_dsl_spec.rb +13 -12
- data/spec/skeem/element_visitor_spec.rb +12 -10
- data/spec/skeem/interpreter_spec.rb +74 -46
- data/spec/skeem/lambda_spec.rb +9 -7
- data/spec/skeem/parser_spec.rb +21 -19
- data/spec/skeem/primitive/primitive_builder_spec.rb +57 -48
- data/spec/skeem/primitive/primitive_procedure_spec.rb +15 -13
- data/spec/skeem/runtime_spec.rb +18 -16
- data/spec/skeem/s_expr_nodes_spec.rb +8 -6
- data/spec/skeem/skm_compound_datum_spec.rb +11 -9
- data/spec/skeem/skm_element_spec.rb +7 -5
- data/spec/skeem/skm_empty_list_spec.rb +7 -5
- data/spec/skeem/skm_frame_spec.rb +5 -4
- data/spec/skeem/skm_pair_spec.rb +4 -3
- data/spec/skeem/skm_procedure_exec_spec.rb +2 -0
- data/spec/skeem/skm_simple_datum_spec.rb +24 -22
- data/spec/skeem/skm_unary_expression_spec.rb +11 -9
- data/spec/skeem/tokenizer_spec.rb +53 -44
- data/spec/skeem_spec.rb +2 -0
- data/spec/spec_helper.rb +4 -2
- metadata +7 -4
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'stringio'
|
2
4
|
require_relative '../spec_helper' # Use the RSpec framework
|
3
5
|
require_relative '../../lib/skeem/datum_dsl'
|
@@ -14,7 +16,7 @@ module Skeem
|
|
14
16
|
end
|
15
17
|
|
16
18
|
it 'could be initialized with a block argument' do
|
17
|
-
expect { Interpreter.new { |interp| }
|
19
|
+
expect { Interpreter.new { |interp| } }.not_to raise_error
|
18
20
|
end
|
19
21
|
|
20
22
|
it 'should have a parser' do
|
@@ -90,6 +92,7 @@ module Skeem
|
|
90
92
|
end
|
91
93
|
|
92
94
|
it 'should evaluate vector of constants' do
|
95
|
+
require 'benchmark'
|
93
96
|
source = '#(2018 10 20 "Sat")'
|
94
97
|
result = subject.run(source)
|
95
98
|
expect(result).to be_kind_of(SkmVector)
|
@@ -108,7 +111,7 @@ module Skeem
|
|
108
111
|
|
109
112
|
context 'Built-in primitives' do
|
110
113
|
it 'should implement variable definition' do
|
111
|
-
|
114
|
+
subject.run('(define x 28)')
|
112
115
|
expect(subject.fetch('x')).to eq(28)
|
113
116
|
end
|
114
117
|
|
@@ -125,7 +128,7 @@ SKEEM
|
|
125
128
|
end
|
126
129
|
|
127
130
|
it 'should implement the simple conditional form' do
|
128
|
-
|
131
|
+
checks = [
|
129
132
|
['(if (> 3 2) "yes")', 'yes'],
|
130
133
|
['(if (> 2 3) "yes")', SkmUndefined.instance]
|
131
134
|
]
|
@@ -157,7 +160,7 @@ SKEEM
|
|
157
160
|
((< x 0) -1)
|
158
161
|
)))
|
159
162
|
SKEEM
|
160
|
-
|
163
|
+
subject.run(source)
|
161
164
|
checks = [
|
162
165
|
['(signum 3)', 1],
|
163
166
|
['(signum 0)', 0],
|
@@ -175,7 +178,7 @@ SKEEM
|
|
175
178
|
((< x 0) => -1)
|
176
179
|
)))
|
177
180
|
SKEEM
|
178
|
-
|
181
|
+
subject.run(source)
|
179
182
|
checks = [
|
180
183
|
['(signum 3)', 1],
|
181
184
|
['(signum 0)', 0],
|
@@ -193,7 +196,7 @@ SKEEM
|
|
193
196
|
(else -1)
|
194
197
|
)))
|
195
198
|
SKEEM
|
196
|
-
|
199
|
+
subject.run(source)
|
197
200
|
checks = [
|
198
201
|
['(signum 3)', 1],
|
199
202
|
['(signum 0)', 0],
|
@@ -211,7 +214,7 @@ SKEEM
|
|
211
214
|
end
|
212
215
|
|
213
216
|
it 'should implement the quotation of constant literals' do
|
214
|
-
|
217
|
+
checks = [
|
215
218
|
['(quote a)', 'a'],
|
216
219
|
['(quote 145932)', 145932],
|
217
220
|
['(quote "abc")', 'abc'],
|
@@ -367,8 +370,6 @@ SKEEM
|
|
367
370
|
result = subject.run(source)
|
368
371
|
expect(result.last.value).to eq(20)
|
369
372
|
end
|
370
|
-
|
371
|
-
|
372
373
|
end # context
|
373
374
|
|
374
375
|
context 'Binding constructs:' do
|
@@ -406,7 +407,7 @@ SKEEM
|
|
406
407
|
end
|
407
408
|
|
408
409
|
it 'should support the nesting of a lambda in a let expression' do
|
409
|
-
source
|
410
|
+
source = <<-SKEEM
|
410
411
|
(define make-counter
|
411
412
|
(lambda ()
|
412
413
|
(let ((count 0))
|
@@ -477,7 +478,7 @@ SKEEM
|
|
477
478
|
|
478
479
|
context 'Quasiquotation:' do
|
479
480
|
it 'should implement the quasiquotation of constant literals' do
|
480
|
-
|
481
|
+
checks = [
|
481
482
|
['(quasiquote a)', 'a'],
|
482
483
|
['(quasiquote 145932)', 145932],
|
483
484
|
['(quasiquote "abc")', 'abc'],
|
@@ -518,7 +519,7 @@ SKEEM
|
|
518
519
|
expect(result.members[index]).to eq(value)
|
519
520
|
end
|
520
521
|
|
521
|
-
source =
|
522
|
+
source = '`#()'
|
522
523
|
result = subject.run(source)
|
523
524
|
expect(result).to be_kind_of(SkmVector)
|
524
525
|
expect(result).to be_empty
|
@@ -555,7 +556,7 @@ SKEEM
|
|
555
556
|
result = result.cdr
|
556
557
|
end
|
557
558
|
|
558
|
-
source =
|
559
|
+
source = '`()'
|
559
560
|
result = subject.run(source)
|
560
561
|
expect(result).to be_kind_of(SkmEmptyList)
|
561
562
|
expect(result).to be_null
|
@@ -576,7 +577,7 @@ SKEEM
|
|
576
577
|
result = result.cdr
|
577
578
|
end
|
578
579
|
|
579
|
-
source =
|
580
|
+
source = '`()'
|
580
581
|
result = subject.run(source)
|
581
582
|
expect(result).to be_kind_of(SkmEmptyList)
|
582
583
|
expect(result).to be_null
|
@@ -801,46 +802,56 @@ SKEEM
|
|
801
802
|
compare_to_predicted(checks)
|
802
803
|
end
|
803
804
|
|
804
|
-
|
805
|
-
|
806
|
-
|
807
|
-
|
808
|
-
|
809
|
-
|
810
|
-
|
811
|
-
|
812
|
-
|
813
|
-
|
814
|
-
|
815
|
-
|
816
|
-
|
805
|
+
it 'should implement the truncate-quotient procedure' do
|
806
|
+
checks = [
|
807
|
+
['(truncate-quotient 5 2)', 2],
|
808
|
+
['(truncate-quotient -5 2)', -2],
|
809
|
+
['(truncate-quotient 5 -2)', -2],
|
810
|
+
['(truncate-quotient -5 -2)', 2],
|
811
|
+
['(quotient 5 2)', 2],
|
812
|
+
['(quotient -5 2)', -2],
|
813
|
+
['(quotient 5 -2)', -2],
|
814
|
+
['(quotient -5 -2)', 2]
|
815
|
+
]
|
816
|
+
compare_to_predicted(checks)
|
817
|
+
end
|
817
818
|
|
818
|
-
|
819
|
-
|
820
|
-
|
821
|
-
|
822
|
-
|
823
|
-
|
824
|
-
|
825
|
-
|
826
|
-
|
827
|
-
|
828
|
-
|
829
|
-
|
830
|
-
|
819
|
+
it 'should implement the truncate-remainder procedure' do
|
820
|
+
checks = [
|
821
|
+
['(truncate-remainder 5 2)', 1],
|
822
|
+
['(truncate-remainder -5 2)', -1],
|
823
|
+
['(truncate-remainder 5 -2)', 1],
|
824
|
+
['(truncate-remainder -5 -2)', -1],
|
825
|
+
['(remainder 5 2)', 1],
|
826
|
+
['(remainder -5 2)', -1],
|
827
|
+
['(remainder 5 -2)', 1],
|
828
|
+
['(remainder -5 -2)', -1]
|
829
|
+
]
|
830
|
+
compare_to_predicted(checks)
|
831
|
+
end
|
831
832
|
|
832
833
|
it 'should implement the test-equal procedure' do
|
833
834
|
checks = [
|
834
|
-
[
|
835
|
+
['(test-equal (cons 1 2) (cons 1 2))', true]
|
835
836
|
]
|
836
837
|
compare_to_predicted(checks)
|
837
838
|
end
|
838
839
|
end # context
|
839
|
-
|
840
|
+
|
840
841
|
context 'Input/output:' do
|
842
|
+
it 'should implement the include expression' do
|
843
|
+
initial_dir = Dir.pwd
|
844
|
+
filedir = File.dirname(__FILE__)
|
845
|
+
Dir.chdir(filedir)
|
846
|
+
source = '(include "add4.skm")' # Path is assumed to be relative to pwd
|
847
|
+
result = subject.run(source)
|
848
|
+
expect(result.last).to eq(10)
|
849
|
+
Dir.chdir(initial_dir)
|
850
|
+
end
|
851
|
+
|
841
852
|
it 'should implement the newline procedure' do
|
842
853
|
default_stdout = $stdout
|
843
|
-
$stdout = StringIO.new
|
854
|
+
$stdout = StringIO.new
|
844
855
|
subject.run('(newline) (newline) (newline)')
|
845
856
|
expect($stdout.string).to match(/\n\n\n$/)
|
846
857
|
$stdout = default_stdout
|
@@ -895,7 +906,7 @@ SKEEM
|
|
895
906
|
SKEEM
|
896
907
|
result = subject.run(source)
|
897
908
|
expect(result).to eq([0, 1, 2, 3, 4])
|
898
|
-
|
909
|
+
|
899
910
|
source = <<-SKEEM
|
900
911
|
(let ((x '(1 3 5 7 9)))
|
901
912
|
(do (
|
@@ -904,8 +915,25 @@ SKEEM
|
|
904
915
|
((null? x) sum))) ; => 25
|
905
916
|
SKEEM
|
906
917
|
result = subject.run(source)
|
907
|
-
expect(result).to eq(25)
|
918
|
+
expect(result).to eq(25)
|
908
919
|
end
|
909
920
|
end # context
|
921
|
+
|
922
|
+
context 'Macro processing:' do
|
923
|
+
# it 'should parse macro expressions' do
|
924
|
+
# source = <<-SKEEM
|
925
|
+
# (define-syntax while
|
926
|
+
# (syntax-rules ()
|
927
|
+
# ((while condition body ...)
|
928
|
+
# (let loop ()
|
929
|
+
# (if condition
|
930
|
+
# (begin
|
931
|
+
# body ...
|
932
|
+
# (loop))
|
933
|
+
# #f)))))
|
934
|
+
# SKEEM
|
935
|
+
# ptree = subject.parse(source)
|
936
|
+
# end
|
937
|
+
end
|
910
938
|
end # describe
|
911
|
-
end # module
|
939
|
+
end # module
|
data/spec/skeem/lambda_spec.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative '../spec_helper' # Use the RSpec framework
|
2
4
|
|
3
5
|
# Load the class under test
|
@@ -75,13 +77,13 @@ SKEEM
|
|
75
77
|
|
76
78
|
expect(result.last).to eq(136)
|
77
79
|
end
|
78
|
-
|
80
|
+
|
79
81
|
it 'should accept calls to anonymous procedures' do
|
80
82
|
source = '((lambda (x) (+ x x)) 4)'
|
81
83
|
result = subject.run(source)
|
82
84
|
expect(result).to eq(8)
|
83
85
|
end
|
84
|
-
|
86
|
+
|
85
87
|
it 'should accept unary second-order lambdas' do
|
86
88
|
source = <<-SKEEM
|
87
89
|
(define add-with
|
@@ -94,10 +96,10 @@ SKEEM
|
|
94
96
|
expect(result).to eq(7)
|
95
97
|
end
|
96
98
|
end # context
|
97
|
-
|
99
|
+
|
98
100
|
context 'More advanced features:' do
|
99
101
|
subject { Interpreter.new }
|
100
|
-
|
102
|
+
|
101
103
|
it 'should implement binary second-order functions' do
|
102
104
|
source = <<-SKEEM
|
103
105
|
(define compose
|
@@ -105,10 +107,10 @@ SKEEM
|
|
105
107
|
(lambda (x)
|
106
108
|
(f (g x)))))
|
107
109
|
SKEEM
|
108
|
-
|
110
|
+
subject.run(source)
|
109
111
|
result = subject.run('((compose list square) 5)')
|
110
112
|
expect(result.last).to eq(25)
|
111
|
-
end
|
113
|
+
end
|
112
114
|
end # context
|
113
115
|
end # describe
|
114
|
-
end # module
|
116
|
+
end # module
|
data/spec/skeem/parser_spec.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative '../spec_helper' # Use the RSpec framework
|
2
4
|
require_relative '../../lib/skeem/tokenizer' # Load the class under test
|
3
5
|
|
@@ -5,7 +7,7 @@ module Skeem
|
|
5
7
|
describe Parser do
|
6
8
|
context 'Initialization:' do
|
7
9
|
it 'should be initialized without argument' do
|
8
|
-
expect { Parser.new
|
10
|
+
expect { Parser.new }.not_to raise_error
|
9
11
|
end
|
10
12
|
|
11
13
|
it 'should have its parse engine initialized' do
|
@@ -16,11 +18,11 @@ module Skeem
|
|
16
18
|
context 'Parsing literals:' do
|
17
19
|
it 'should parse isolated booleans' do
|
18
20
|
samples = [
|
19
|
-
|
21
|
+
['#f', false]
|
20
22
|
# ['#false', false],
|
21
23
|
# ['#t', true],
|
22
24
|
# ['#true', true]
|
23
|
-
|
25
|
+
]
|
24
26
|
samples.each do |source, predicted|
|
25
27
|
ptree = subject.parse(source)
|
26
28
|
expect(ptree.root).to be_kind_of(SkmBoolean)
|
@@ -30,12 +32,12 @@ module Skeem
|
|
30
32
|
|
31
33
|
it 'should parse isolated integers' do
|
32
34
|
samples = [
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
35
|
+
['0', 0],
|
36
|
+
['3', 3],
|
37
|
+
['-3', -3],
|
38
|
+
['+12345', 12345],
|
39
|
+
['-12345', -12345]
|
40
|
+
]
|
39
41
|
samples.each do |source, predicted|
|
40
42
|
ptree = subject.parse(source)
|
41
43
|
expect(ptree.root).to be_kind_of(SkmInteger)
|
@@ -45,12 +47,12 @@ module Skeem
|
|
45
47
|
|
46
48
|
it 'should parse isolated real numbers' do
|
47
49
|
samples = [
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
50
|
+
['0.0', 0.0],
|
51
|
+
['3.14', 3.14],
|
52
|
+
['-3.14', -3.14],
|
53
|
+
['+123e+45', 123e+45],
|
54
|
+
['-123e-45', -123e-45]
|
55
|
+
]
|
54
56
|
samples.each do |source, predicted|
|
55
57
|
ptree = subject.parse(source)
|
56
58
|
expect(ptree.root).to be_kind_of(SkmReal)
|
@@ -60,8 +62,8 @@ module Skeem
|
|
60
62
|
|
61
63
|
it 'should parse isolated strings' do
|
62
64
|
samples = [
|
63
|
-
|
64
|
-
|
65
|
+
['"Hello world!"', 'Hello world!']
|
66
|
+
]
|
65
67
|
samples.each do |source, predicted|
|
66
68
|
ptree = subject.parse(source)
|
67
69
|
expect(ptree.root).to be_kind_of(SkmString)
|
@@ -71,8 +73,8 @@ module Skeem
|
|
71
73
|
|
72
74
|
it 'should parse isolated identifiers' do
|
73
75
|
samples = [
|
74
|
-
|
75
|
-
|
76
|
+
%w[the-word-recursion-has-many-meanings the-word-recursion-has-many-meanings]
|
77
|
+
]
|
76
78
|
samples.each do |source, predicted|
|
77
79
|
ptree = subject.parse(source)
|
78
80
|
expect(ptree.root).to be_kind_of(SkmVariableReference)
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative '../../spec_helper' # Use the RSpec framework
|
2
4
|
|
3
5
|
# Load the class under test
|
@@ -41,8 +43,8 @@ SKEEM
|
|
41
43
|
['(+)', 0], # '+' as nullary operator. Example from section 6.2.6
|
42
44
|
['(+ -3)', -3], # '+' as unary operator
|
43
45
|
['(+ 3 4)', 7], # '+' as binary operator. Example from section 4.1.3
|
44
|
-
['(+ 1/2 2/3)', Rational(7,6)],
|
45
|
-
['(+ 1/2 3)', Rational(7,2)],
|
46
|
+
['(+ 1/2 2/3)', Rational(7, 6)],
|
47
|
+
['(+ 1/2 3)', Rational(7, 2)],
|
46
48
|
['(+ 2 2.34)', 4.34]
|
47
49
|
]
|
48
50
|
compare_to_predicted(checks)
|
@@ -219,7 +221,7 @@ SKEEM
|
|
219
221
|
["(equal? (make-vector 5 'a) (make-vector 5 'a))", true],
|
220
222
|
['(equal? car car)', true],
|
221
223
|
['(equal? car cdr)', false],
|
222
|
-
['(equal? (lambda (x) x) (lambda (y) y))', false]
|
224
|
+
['(equal? (lambda (x) x) (lambda (y) y))', false]
|
223
225
|
]
|
224
226
|
compare_to_predicted(checks) do |result, expectation|
|
225
227
|
if result.length > 1
|
@@ -355,7 +357,7 @@ SKEEM
|
|
355
357
|
['(integer? "3")', false],
|
356
358
|
['(integer? #t)', false]
|
357
359
|
]
|
358
|
-
|
360
|
+
compare_to_predicted(checks)
|
359
361
|
end
|
360
362
|
|
361
363
|
it 'should implement the number->string procedure' do
|
@@ -396,7 +398,7 @@ SKEEM
|
|
396
398
|
['(or)', false],
|
397
399
|
['(or #f)', false],
|
398
400
|
['(or #f #t)', true],
|
399
|
-
['(or #f #f #f)', false]
|
401
|
+
['(or #f #f #f)', false]
|
400
402
|
|
401
403
|
]
|
402
404
|
compare_to_predicted(checks)
|
@@ -485,11 +487,11 @@ SKEEM
|
|
485
487
|
compare_to_predicted(checks)
|
486
488
|
end
|
487
489
|
|
488
|
-
|
490
|
+
it 'should implement the string-length procedure' do
|
489
491
|
checks = [
|
490
492
|
['(string-length "abc")', 3],
|
491
493
|
['(string-length "")', 0],
|
492
|
-
['(string-length "hi there")', 8]
|
494
|
+
['(string-length "hi there")', 8]
|
493
495
|
]
|
494
496
|
compare_to_predicted(checks)
|
495
497
|
end
|
@@ -544,7 +546,7 @@ SKEEM
|
|
544
546
|
compare_to_predicted(checks)
|
545
547
|
end
|
546
548
|
|
547
|
-
|
549
|
+
it 'should implement the null? procedure' do
|
548
550
|
checks = [
|
549
551
|
['(null? #f)', false],
|
550
552
|
['(null? 1)', false],
|
@@ -591,7 +593,7 @@ SKEEM
|
|
591
593
|
example = "(cons '(a b) 'c)" # => ((a b) . c)
|
592
594
|
result = subject.run(example)
|
593
595
|
expect(result.car).to be_kind_of(SkmPair)
|
594
|
-
expect(result.car.to_a).to eq([
|
596
|
+
expect(result.car.to_a).to eq(%w[a b])
|
595
597
|
expect(result.cdr).to be_kind_of(SkmIdentifier)
|
596
598
|
expect(result.cdr).to eq('c')
|
597
599
|
end
|
@@ -616,21 +618,21 @@ SKEEM
|
|
616
618
|
result = subject.run(example)
|
617
619
|
expect(result).to be_list
|
618
620
|
expect(result.length).to eq(3)
|
619
|
-
expect(result.to_a).to eq([
|
621
|
+
expect(result.to_a).to eq(%w[b c d])
|
620
622
|
|
621
623
|
expect_expr("(cdr '(1 . 2))").to eq(2)
|
622
624
|
|
623
625
|
example = "(cdr '())" # => error
|
624
626
|
expect { subject.run(example) }.to raise_error(StandardError)
|
625
627
|
end
|
626
|
-
|
628
|
+
|
627
629
|
it 'should implement the length procedure' do
|
628
630
|
example = '(make-list 2 3)'
|
629
631
|
result = subject.run(example)
|
630
632
|
expect(result).to be_list
|
631
633
|
expect(result.length).to eq(2)
|
632
634
|
expect(result.to_a).to eq([3, 3])
|
633
|
-
end
|
635
|
+
end
|
634
636
|
|
635
637
|
it 'should implement the length procedure' do
|
636
638
|
checks = [
|
@@ -638,23 +640,23 @@ SKEEM
|
|
638
640
|
["(length '(1))", 1],
|
639
641
|
["(length '(1 2))", 2],
|
640
642
|
["(length '(1 2 3))", 3],
|
641
|
-
|
643
|
+
["(length '(a (b) (c d e)))", 3]
|
642
644
|
]
|
643
645
|
compare_to_predicted(checks)
|
644
646
|
end
|
645
647
|
|
646
648
|
it 'should implement the append procedure' do
|
647
649
|
checks = [
|
648
|
-
["(append '(a b c) '())", array2list_ids([
|
649
|
-
["(append '() '(a b c))", array2list_ids([
|
650
|
-
["(append '(x) '(y))", array2list_ids([
|
651
|
-
["(append '(a) '(b c d))", array2list_ids([
|
652
|
-
["(append '(a b) '(c d))", array2list_ids([
|
653
|
-
["(append '(a b) '(c) 'd)", array2list_ids([
|
650
|
+
["(append '(a b c) '())", array2list_ids(%w[a b c])],
|
651
|
+
["(append '() '(a b c))", array2list_ids(%w[a b c])],
|
652
|
+
["(append '(x) '(y))", array2list_ids(%w[x y])],
|
653
|
+
["(append '(a) '(b c d))", array2list_ids(%w[a b c d])],
|
654
|
+
["(append '(a b) '(c d))", array2list_ids(%w[a b c d])],
|
655
|
+
["(append '(a b) '(c) 'd)", array2list_ids(%w[a b c d])],
|
654
656
|
["(append '(a (b)) '((c)))", [SkmIdentifier.create('a'),
|
655
|
-
|
656
|
-
|
657
|
-
[
|
657
|
+
SkmPair.create_from_a(array2list_ids(['b'])),
|
658
|
+
SkmPair.create_from_a(array2list_ids(['c']))]],
|
659
|
+
["(append '() 'a)", SkmIdentifier.create('a')]
|
658
660
|
]
|
659
661
|
compare_to_predicted(checks) do |result, expectation|
|
660
662
|
if result.kind_of?(SkmPair)
|
@@ -667,18 +669,18 @@ SKEEM
|
|
667
669
|
|
668
670
|
it 'should implement the procedure for an improper list' do
|
669
671
|
result = subject.run("(append '(a b) '(c . d))")
|
670
|
-
expect(result.car).to eq(
|
671
|
-
expect(result.cdr.car).to eq(
|
672
|
-
expect(result.cdr.cdr.car).to eq(
|
673
|
-
expect(result.cdr.cdr.cdr).to eq(
|
672
|
+
expect(result.car).to eq(SkmIdentifier.create('a'))
|
673
|
+
expect(result.cdr.car).to eq(SkmIdentifier.create('b'))
|
674
|
+
expect(result.cdr.cdr.car).to eq(SkmIdentifier.create('c'))
|
675
|
+
expect(result.cdr.cdr.cdr).to eq(SkmIdentifier.create('d'))
|
674
676
|
end
|
675
677
|
|
676
678
|
|
677
679
|
it 'should implement the reverse procedure' do
|
678
680
|
checks = [
|
679
681
|
["(reverse '())", SkmEmptyList.instance],
|
680
|
-
["(reverse '(a b c))", array2list_ids([
|
681
|
-
["(reverse '((a) b c))", array2list_ids([
|
682
|
+
["(reverse '(a b c))", array2list_ids(%w[c b a])],
|
683
|
+
["(reverse '((a) b c))", array2list_ids(%w[c b]) << SkmPair.new(SkmIdentifier.create('a'), nil)]
|
682
684
|
]
|
683
685
|
compare_to_predicted(checks) do |result, expectation|
|
684
686
|
if result.kind_of?(SkmPair)
|
@@ -692,7 +694,7 @@ SKEEM
|
|
692
694
|
it 'should implement the list->vector procedure' do
|
693
695
|
checks = [
|
694
696
|
["(list->vector '())", []],
|
695
|
-
["(list->vector '(a b c))", [
|
697
|
+
["(list->vector '(a b c))", %w[a b c]]
|
696
698
|
]
|
697
699
|
compare_to_predicted(checks) do |result, expectation|
|
698
700
|
expect(result.to_a).to eq(expectation)
|
@@ -700,7 +702,7 @@ SKEEM
|
|
700
702
|
end
|
701
703
|
|
702
704
|
it 'should implement the set-car! procedure' do
|
703
|
-
source
|
705
|
+
source = <<-SKEEM
|
704
706
|
(define x '(a b c))
|
705
707
|
(set-car! x 1)
|
706
708
|
x
|
@@ -710,7 +712,7 @@ SKEEM
|
|
710
712
|
end
|
711
713
|
|
712
714
|
it 'should implement the set-cdr! procedure' do
|
713
|
-
source
|
715
|
+
source = <<-SKEEM
|
714
716
|
(define x '(a b c))
|
715
717
|
(set-cdr! x 1)
|
716
718
|
x
|
@@ -744,13 +746,13 @@ SKEEM
|
|
744
746
|
it 'should implement the list-copy procedure' do
|
745
747
|
checks = [
|
746
748
|
["(list-copy '())", []],
|
747
|
-
["(list-copy '(a b c))", [
|
749
|
+
["(list-copy '(a b c))", %w[a b c]]
|
748
750
|
]
|
749
751
|
compare_to_predicted(checks) do |result, expectation|
|
750
752
|
expect(result.to_a).to eq(expectation)
|
751
753
|
end
|
752
754
|
|
753
|
-
source
|
755
|
+
source = <<-SKEEM
|
754
756
|
(define a '(1 8 2 8)) ; a may be immutable
|
755
757
|
(define b (list-copy a))
|
756
758
|
(set-car! b 3) ; b is mutable
|
@@ -894,7 +896,7 @@ SKEEM
|
|
894
896
|
checks = [
|
895
897
|
['(vector-length (make-vector 0))', 0],
|
896
898
|
["(vector-length (make-vector 0 'a))", 0],
|
897
|
-
["(equal? (make-vector 5 'a) '#(a a a a a))", true]
|
899
|
+
["(equal? (make-vector 5 'a) '#(a a a a a))", true]
|
898
900
|
]
|
899
901
|
compare_to_predicted(checks)
|
900
902
|
end
|
@@ -907,21 +909,21 @@ SKEEM
|
|
907
909
|
end
|
908
910
|
|
909
911
|
it 'should implement the vector-set! procedure' do
|
910
|
-
source
|
912
|
+
source = <<-SKEEM
|
911
913
|
(let
|
912
914
|
((vec (vector 0 '(2 2 2 2) "Anna")))
|
913
915
|
(vector-set! vec 1 '("Sue" "Sue"))
|
914
916
|
vec)
|
915
917
|
SKEEM
|
916
|
-
#(0 ("Sue" "Sue") "Anna")
|
918
|
+
# (0 ("Sue" "Sue") "Anna")
|
917
919
|
result = subject.run(source)
|
918
920
|
expect(result).to be_kind_of(SkmVector)
|
919
921
|
expectation = [SkmInteger.create(0),
|
920
|
-
|
921
|
-
|
922
|
+
SkmPair.new(SkmString.create('Sue'), SkmPair.new(SkmString.create('Sue'), SkmEmptyList.instance)),
|
923
|
+
SkmString.create('Anna')]
|
922
924
|
expect(result).to eq(expectation)
|
923
925
|
|
924
|
-
source
|
926
|
+
source = <<-SKEEM
|
925
927
|
(let (
|
926
928
|
(v (vector 'a 'b 'c 'd 'e)))
|
927
929
|
(vector-set! v 2 'x)
|
@@ -929,13 +931,13 @@ SKEEM
|
|
929
931
|
SKEEM
|
930
932
|
result = subject.run(source)
|
931
933
|
expect(result).to be_kind_of(SkmVector)
|
932
|
-
expect(result).to eq(array2list_ids([
|
934
|
+
expect(result).to eq(array2list_ids(%w[a b x d e]))
|
933
935
|
end
|
934
936
|
|
935
937
|
it 'should implement the vector->list procedure' do
|
936
938
|
checks = [
|
937
|
-
[
|
938
|
-
["(vector->list '#(a b c))", [
|
939
|
+
['(vector->list #())', []],
|
940
|
+
["(vector->list '#(a b c))", %w[a b c]]
|
939
941
|
]
|
940
942
|
compare_to_predicted(checks) do |result, expectation|
|
941
943
|
expect(result.to_a).to eq(expectation)
|
@@ -946,9 +948,9 @@ SKEEM
|
|
946
948
|
context 'Control procedures:' do
|
947
949
|
it 'should implement the procedure? predicate' do
|
948
950
|
checks = [
|
949
|
-
[
|
951
|
+
['(procedure? car)', true],
|
950
952
|
["(procedure? 'car)", false],
|
951
|
-
[
|
953
|
+
['(procedure? (lambda (x) (* x x)))', true]
|
952
954
|
# ["(procedure? '(lambda (x) (* x x)))", false] # Parse fail: non-standard syntax
|
953
955
|
]
|
954
956
|
compare_to_predicted(checks)
|
@@ -963,7 +965,7 @@ SKEEM
|
|
963
965
|
|
964
966
|
it 'should implement the map procedure' do
|
965
967
|
checks = [
|
966
|
-
["(map car '((a b) (d e) (g h)))", [
|
968
|
+
["(map car '((a b) (d e) (g h)))", %w[a d g]],
|
967
969
|
["(map + '(1 2 3) '(4 5 6 7))", [5, 7, 9]]
|
968
970
|
]
|
969
971
|
compare_to_predicted(checks) do |result, expectation|
|
@@ -975,7 +977,7 @@ SKEEM
|
|
975
977
|
context 'IO procedures:' do
|
976
978
|
it 'should implement the display procedure' do
|
977
979
|
default_stdout = $stdout
|
978
|
-
$stdout = StringIO.new
|
980
|
+
$stdout = StringIO.new
|
979
981
|
subject.run('(display "Hello")')
|
980
982
|
expect($stdout.string).to eq('Hello')
|
981
983
|
$stdout = default_stdout
|
@@ -983,6 +985,13 @@ SKEEM
|
|
983
985
|
end # context
|
984
986
|
|
985
987
|
context 'Miscellaneous procedures' do
|
988
|
+
it 'should raise an exception with given error message' do
|
989
|
+
source = '(error "This is an error message")'
|
990
|
+
err = SkmError
|
991
|
+
msg = 'This is an error message'
|
992
|
+
expect { subject.run(source) }.to raise_error(err, msg)
|
993
|
+
end
|
994
|
+
|
986
995
|
it 'should return true when an assertion succeeds' do
|
987
996
|
source = <<-SKEEM
|
988
997
|
(define x 2)
|
@@ -1001,9 +1010,9 @@ SKEEM
|
|
1001
1010
|
err = StandardError
|
1002
1011
|
msg1 = 'Error: assertion failed on line 3, column 4'
|
1003
1012
|
msg2 = 'with <Skeem::SkmBoolean: false>'
|
1004
|
-
expect { subject.run(source) }.to raise_error(err, msg1 + ', '+ msg2)
|
1013
|
+
expect { subject.run(source) }.to raise_error(err, msg1 + ', ' + msg2)
|
1005
1014
|
end
|
1006
1015
|
end # context
|
1007
1016
|
end # describe
|
1008
1017
|
end # module
|
1009
|
-
end # module
|
1018
|
+
end # module
|