skeem 0.0.17 → 0.0.18

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,15 +1,256 @@
1
1
  require_relative '../../spec_helper' # Use the RSpec framework
2
2
 
3
3
  # Load the class under test
4
- require_relative '../../../lib/skeem/primitive/primitive_builder'
4
+ require_relative '../../../lib/skeem/interpreter'
5
5
 
6
6
  module Skeem
7
- describe Primitive::PrimitiveBuilder do
8
-
9
- context 'Initialization:' do
10
- it 'should be initialized without argument' do
11
- expect { Interpreter.new() }.not_to raise_error
12
- end
13
- end # context
14
- end # describe
15
- end # module
7
+ module Primitive
8
+ describe 'Testing primitive procedures' do
9
+ subject { Interpreter.new }
10
+ context 'Arithmetic operators:' do
11
+ it 'should implement the addition operator' do
12
+ [
13
+ ['(+)', 0], # '+' as nullary operator. Example from section 6.2.6
14
+ ['(+ -3)', -3], # '+' as unary operator
15
+ ['(+ 3 4)', 7], # '+' as binary operator. Example from section 4.1.3
16
+ ['(+ 2 2.34)', 4.34]
17
+ ].each do |(expr, predicted)|
18
+ result = subject.run(expr)
19
+ expect(result.value).to eq(predicted)
20
+ end
21
+ end
22
+
23
+ it 'should implement the minus operator' do
24
+ [
25
+ ['(- 3)', -3], # '-' as unary operator (= sign change)
26
+ ['(- 3 4)', -1], # '-' as binary operator. Example from section 6.2.6
27
+ ['(- 3 4 5)', -6] # '-' as variadic operator. Example from section 6.2.6
28
+ ].each do |(expr, predicted)|
29
+ result = subject.run(expr)
30
+ expect(result.value).to eq(predicted)
31
+ end
32
+ end
33
+
34
+ it 'should implement the product operator' do
35
+ [
36
+ ['(*)', 1], # '*' as nullary operator. Example from section 6.2.6
37
+ ['(* 4)', 4], # '*' as unary operator. Example from section 6.2.6
38
+ ['(* 5 8)', 40], # '*' as binary operator.
39
+ ['(* 2 3 4 5)', 120] # '*' as variadic operator.
40
+ ].each do |(expr, predicted)|
41
+ result = subject.run(expr)
42
+ expect(result.value).to eq(predicted)
43
+ end
44
+ end
45
+
46
+ it 'should implement the division operator' do
47
+ [
48
+ ['(/ 3)', 1.0/3], # '/' as unary operator (= inverse of argument)
49
+ ['(/ 3 4)', 3.0/4], # '/' as binary operator.
50
+ ['(/ 3 4 5)', 3.0/20] # '/' as variadic operator. Example from section 6.2.6
51
+ ].each do |(expr, predicted)|
52
+ result = subject.run(expr)
53
+ expect(result.value).to eq(predicted)
54
+ end
55
+ end
56
+
57
+ it 'should implement the floor-remainder (modulo) procedure' do
58
+ checks = [
59
+ ['(floor-remainder 16 4)', 0], # Binary procedure.
60
+ ['(floor-remainder 5 2)', 1],
61
+ ['(floor-remainder -45.0 7)', 4.0],
62
+ ['(floor-remainder 10.0 -3.0)', -2.0],
63
+ ['(floor-remainder -17 -9)', -8]
64
+ ]
65
+ checks.each do |(skeem_expr, expectation)|
66
+ result = subject.run(skeem_expr)
67
+ expect(result.value).to eq(expectation)
68
+ end
69
+ end
70
+ end # context
71
+
72
+ context 'Comparison operators' do
73
+ it 'should implement the equality operator' do
74
+ checks = [
75
+ ['(= 3)', true], # '=' as unary operator
76
+ ['(= 3 3)', true], # '=' as binary operator
77
+ ['(= 3 (+ 1 2) (- 4 1))', true], # '=' as variadic operator
78
+ ['(= "foo" "foo")', true],
79
+ ['(= 3 4)', false],
80
+ ['(= "foo" "bar")', false]
81
+ ]
82
+ checks.each do |(skeem_expr, expectation)|
83
+ result = subject.run(skeem_expr)
84
+ expect(result.value).to eq(expectation)
85
+ end
86
+ end
87
+
88
+ it 'should implement the less than operator' do
89
+ checks = [
90
+ ['(< 3)', false], # '<' as unary operator
91
+ ['(< 3 4)', true], # '<' as binary operator
92
+ ['(< 3 (+ 2 2) (+ 4 1))', true], # '<' as variadic operator
93
+ ['(< 3 3)', false],
94
+ ['(< 3 2)', false],
95
+ ['(< 3 4 5 4)', false]
96
+ ]
97
+ checks.each do |(skeem_expr, expectation)|
98
+ result = subject.run(skeem_expr)
99
+ expect(result.value).to eq(expectation)
100
+ end
101
+ end
102
+
103
+ it 'should implement the greater than operator' do
104
+ checks = [
105
+ ['(> 3)', false], # '>' as unary operator
106
+ ['(> 3 2)', true], # '>' as binary operator
107
+ ['(> 3 (- 4 2) (- 2 1))', true], # '>' as variadic operator
108
+ ['(> 3 3)', false],
109
+ ['(> 3 4)', false],
110
+ ['(> 3 2 1 2)', false]
111
+ ]
112
+ checks.each do |(skeem_expr, expectation)|
113
+ result = subject.run(skeem_expr)
114
+ expect(result.value).to eq(expectation)
115
+ end
116
+ end
117
+
118
+ it 'should implement the less or equal than operator' do
119
+ checks = [
120
+ ['(<= 3)', true], # '<=' as unary operator
121
+ ['(<= 3 4)', true], # '<=' as binary operator
122
+ ['(<= 3 (+ 2 2) (+ 4 1))', true], # '<=' as variadic operator
123
+ ['(<= 3 3)', true],
124
+ ['(<= 3 2)', false],
125
+ ['(<= 3 4 5 4)', false],
126
+ ['(<= 3 4 5 5)', true]
127
+ ]
128
+ checks.each do |(skeem_expr, expectation)|
129
+ result = subject.run(skeem_expr)
130
+ expect(result.value).to eq(expectation)
131
+ end
132
+ end
133
+
134
+ it 'should implement the greater or equal than operator' do
135
+ checks = [
136
+ ['(>= 3)', true], # '>=' as unary operator
137
+ ['(>= 3 2)', true],
138
+ ['(>= 3 (- 4 2) (- 2 1))', true],
139
+ ['(>= 3 3)', true],
140
+ ['(>= 3 4)', false],
141
+ ['(>= 3 2 1 2)', false],
142
+ ['(>= 3 2 1 1)', true]
143
+ ]
144
+ checks.each do |(skeem_expr, expectation)|
145
+ result = subject.run(skeem_expr)
146
+ expect(result.value).to eq(expectation)
147
+ end
148
+ end
149
+ end # context
150
+
151
+ context 'Number predicates:' do
152
+ it 'should implement the number? predicate' do
153
+ checks = [
154
+ ['(number? 3.1)', true],
155
+ ['(number? 3)', true],
156
+ ['(number? "3")', false],
157
+ ['(number? #t)', false]
158
+ ]
159
+ checks.each do |(skeem_expr, expectation)|
160
+ result = subject.run(skeem_expr)
161
+ expect(result.value).to eq(expectation)
162
+ end
163
+ end
164
+
165
+ it 'should implement the real? predicate' do
166
+ checks = [
167
+ ['(real? 3.1)', true],
168
+ ['(real? 3)', true],
169
+ ['(real? "3")', false],
170
+ ['(real? #t)', false]
171
+ ]
172
+ checks.each do |(skeem_expr, expectation)|
173
+ result = subject.run(skeem_expr)
174
+ expect(result.value).to eq(expectation)
175
+ end
176
+ end
177
+
178
+ it 'should implement the integer? predicate' do
179
+ checks = [
180
+ ['(integer? 3.1)', false],
181
+ # ['(integer? 3.0)', true], TODO: should pass when exact? will be implemented
182
+ ['(integer? 3)', true],
183
+ ['(integer? "3")', false],
184
+ ['(integer? #t)', false]
185
+ ]
186
+ checks.each do |(skeem_expr, expectation)|
187
+ result = subject.run(skeem_expr)
188
+ expect(result.value).to eq(expectation)
189
+ end
190
+ end
191
+ end # context
192
+
193
+ context 'Boolean procedures:' do
194
+ it 'should implement the not procedure' do
195
+ checks = [
196
+ ['(not #t)', false],
197
+ ['(not 3)', false],
198
+ ['(not #f)', true]
199
+ ]
200
+ checks.each do |(skeem_expr, expectation)|
201
+ result = subject.run(skeem_expr)
202
+ expect(result.value).to eq(expectation)
203
+ end
204
+ end
205
+
206
+ it 'should implement the boolean? procedure' do
207
+ checks = [
208
+ ['(boolean? #f)', true],
209
+ ['(boolean? 0)', false]
210
+ ]
211
+ checks.each do |(skeem_expr, expectation)|
212
+ result = subject.run(skeem_expr)
213
+ expect(result.value).to eq(expectation)
214
+ end
215
+ end
216
+ end # context
217
+
218
+ context 'String procedures:' do
219
+ it 'should implement the string? procedure' do
220
+ checks = [
221
+ ['(string? #f)', false],
222
+ ['(string? 3)', false],
223
+ ['(string? "hi")', true]
224
+ ]
225
+ checks.each do |(skeem_expr, expectation)|
226
+ result = subject.run(skeem_expr)
227
+ expect(result.value).to eq(expectation)
228
+ end
229
+ end
230
+ end # context
231
+
232
+ context 'Symbol procedures:' do
233
+ it 'should implement the symbol? procedure' do
234
+ checks = [
235
+ ['(symbol? #f)', false],
236
+ ['(symbol? "bar")', false]
237
+ ]
238
+ checks.each do |(skeem_expr, expectation)|
239
+ result = subject.run(skeem_expr)
240
+ expect(result.value).to eq(expectation)
241
+ end
242
+ end
243
+ end # context
244
+
245
+ context 'IO procedures:' do
246
+ it 'should implement the newline procedure' do
247
+ default_stdout = $stdout
248
+ $stdout = StringIO.new()
249
+ subject.run('(newline) (newline) (newline)')
250
+ expect($stdout.string).to match(/\n\n\n$/)
251
+ $stdout = default_stdout
252
+ end
253
+ end # context
254
+ end # describe
255
+ end # module
256
+ end # module
@@ -0,0 +1,162 @@
1
+ require_relative '../../spec_helper' # Use the RSpec framework
2
+
3
+ # Load the class under test
4
+ require_relative '../../../lib/skeem/primitive/primitive_procedure'
5
+
6
+ module Skeem
7
+ module Primitive
8
+ describe PrimitiveProcedure do
9
+ def call_proc(aName, args)
10
+ ProcedureCall.new(nil, aName, args)
11
+ end
12
+
13
+ let(:nullary) { SkmArity.new(0, 0) }
14
+ let(:unary) { SkmArity.new(1, 1) }
15
+ let(:binary) { SkmArity.new(2, 2) }
16
+ let(:zero_or_more) {SkmArity.new(0, '*') }
17
+ let(:one_or_more) {SkmArity.new(1, '*') }
18
+ let(:newline_code) do
19
+ ->(_runtime) { "\n" }
20
+ end
21
+
22
+ let(:cube) do
23
+ ->(_runtime, operand) { operand.value * operand.value * operand.value }
24
+ end
25
+
26
+ let(:sum) do
27
+ ->(_runtime, operand1, operand2) { operand1.value + operand2.value }
28
+ end
29
+
30
+ let(:length) do
31
+ ->(_runtime, operands) { operands.length }
32
+ end
33
+
34
+ subject { PrimitiveProcedure.new('cube', unary, cube) }
35
+
36
+ before(:each) { @passing = false }
37
+
38
+ context 'Initialization:' do
39
+ it 'should be initialized with a name, arity and a lambda' do
40
+ expect { PrimitiveProcedure.new('newline', nullary, newline_code) }.not_to raise_error
41
+ end
42
+
43
+ it 'should know its name' do
44
+ expect(subject.identifier.value).to eq('cube')
45
+ end
46
+
47
+ it 'should know its arity' do
48
+ expect(subject.arity).to eq([1, 1])
49
+ end
50
+
51
+ it 'should know its lambda' do
52
+ expect(subject.code).to eq(cube)
53
+ end
54
+
55
+ it 'should complain if third argument is not a lambda' do
56
+ kode = Proc.new { puts '' }
57
+
58
+ err = StandardError
59
+ err_msg = "Primitive procedure 'newline' must be implemented with a Ruby lambda."
60
+ expect { PrimitiveProcedure.new('newline', nullary, kode) }.to raise_error(err, err_msg)
61
+ end
62
+
63
+ it 'should complain if third argument is a nullary lambda' do
64
+ kode = ->() { puts '' } # Missing slot for Runtime object
65
+
66
+ err = StandardError
67
+ err_msg = "Primitive procedure 'newline' lambda takes no parameter."
68
+ expect { PrimitiveProcedure.new('newline', nullary, kode) }.to raise_error(err, err_msg)
69
+ end
70
+
71
+ it 'should complain when arity and parameter count mismatch' do
72
+ err = StandardError
73
+ msg1 = "Discrepancy in primitive procedure 'cube' "
74
+
75
+ msg2 = "between arity (0) + 1 and parameter count of lambda 2."
76
+ expect { PrimitiveProcedure.new('cube', nullary, cube) }.to raise_error(err, msg1 + msg2)
77
+
78
+ msg2 = "between arity (2) + 1 and parameter count of lambda 2."
79
+ expect { PrimitiveProcedure.new('cube', binary, cube) }.to raise_error(err, msg1 + msg2)
80
+
81
+ # Nasty; this discrepancy isn't detected
82
+ expect { PrimitiveProcedure.new('cube', zero_or_more, cube) }.not_to raise_error
83
+
84
+ expect { PrimitiveProcedure.new('cube', unary, cube) }.not_to raise_error
85
+
86
+ msg2 = "between arity (1) + 2 and parameter count of lambda 2."
87
+ expect { PrimitiveProcedure.new('cube', one_or_more, cube) }.to raise_error(err, msg1 + msg2)
88
+ end
89
+ end # context
90
+
91
+ context 'Procedure invokation:' do
92
+ it 'should support Skeem nullary procedure' do
93
+ pproc = PrimitiveProcedure.new('newline', nullary, newline_code)
94
+ rtime = double('fake-runtime')
95
+
96
+ invokation = call_proc('newline', nil)
97
+ expect(pproc.call(rtime, invokation)).to eq("\n")
98
+
99
+ too_much = call_proc('newline', ['superfluous'])
100
+ err = StandardError
101
+ ms1 = 'Wrong number of arguments for #<Procedure newline>'
102
+ ms2 = ' (required at least 0, got 1)'
103
+ expect { pproc.call(rtime, too_much) }.to raise_error(err, ms1 + ms2)
104
+ end
105
+
106
+ it 'should support Skeem unary procedure' do
107
+ pproc = PrimitiveProcedure.new('cube', unary, cube)
108
+ rtime = double('fake-runtime')
109
+
110
+ invokation = call_proc('cube', [SkmInteger.create(3)])
111
+ expect(pproc.call(rtime, invokation)).to eq(27)
112
+
113
+ too_few = call_proc('cube', nil)
114
+ err = StandardError
115
+ ms1 = 'Wrong number of arguments for #<Procedure cube>'
116
+ ms2 = ' (required at least 1, got 0)'
117
+ expect { pproc.call(rtime, too_few) }.to raise_error(err, ms1 + ms2)
118
+
119
+ too_much = call_proc('newline', ['foo', 'bar'])
120
+ err = StandardError
121
+ ms1 = 'Wrong number of arguments for #<Procedure cube>'
122
+ ms2 = ' (required at least 1, got 2)'
123
+ expect { pproc.call(rtime, too_much) }.to raise_error(err, ms1 + ms2)
124
+ end
125
+
126
+ it 'should support Skeem binary procedure' do
127
+ pproc = PrimitiveProcedure.new('sum', binary, sum)
128
+ rtime = double('fake-runtime')
129
+
130
+ invokation = call_proc('sum', [SkmInteger.create(3), SkmInteger.create(5)])
131
+ expect(pproc.call(rtime, invokation)).to eq(8)
132
+
133
+ too_few = call_proc('sum', [SkmInteger.create(3)])
134
+ err = StandardError
135
+ ms1 = 'Wrong number of arguments for #<Procedure sum>'
136
+ ms2 = ' (required at least 2, got 1)'
137
+ expect { pproc.call(rtime, too_few) }.to raise_error(err, ms1 + ms2)
138
+
139
+ too_much = call_proc('cube', ['foo', 'bar', 'quux'])
140
+ err = StandardError
141
+ ms1 = 'Wrong number of arguments for #<Procedure sum>'
142
+ ms2 = ' (required at least 2, got 3)'
143
+ expect { pproc.call(rtime, too_much) }.to raise_error(err, ms1 + ms2)
144
+ end
145
+
146
+ it 'should support Skeem variadic procedure' do
147
+ pproc = PrimitiveProcedure.new('length', zero_or_more, length)
148
+ rtime = double('fake-runtime')
149
+
150
+ invokation = call_proc('length', [SkmInteger.create(3), SkmInteger.create(5)])
151
+ expect(pproc.call(rtime, invokation)).to eq(2)
152
+
153
+ no_arg = call_proc('sum', nil)
154
+ expect(pproc.call(rtime, no_arg)).to eq(0)
155
+
156
+ many = call_proc('cube', ['foo', 'bar', 'quux'])
157
+ expect( pproc.call(rtime, many)).to eq(3)
158
+ end
159
+ end # context
160
+ end # describe
161
+ end # module
162
+ end # module
@@ -231,6 +231,9 @@ describe SkmIdentifier do
231
231
 
232
232
  it 'should know its members' do
233
233
  expect(subject.members).to eq(sample_members)
234
+
235
+ other = SkmList.new([])
236
+ expect(other.members).to be_empty
234
237
  end
235
238
  end # context
236
239
 
@@ -151,6 +151,7 @@ module Skeem
151
151
  token = subject.tokens.first
152
152
  expect(token.terminal).to eq('STRING_LIT')
153
153
  expect(token.lexeme).to eq('Some text')
154
+ expect(token.position.line).to eq(2)
154
155
  end
155
156
 
156
157
  it 'should skip trailing comments' do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: skeem
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.17
4
+ version: 0.0.18
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dimitri Geshef
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-10-06 00:00:00.000000000 Z
11
+ date: 2018-10-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rley
@@ -91,7 +91,7 @@ files:
91
91
  - lib/skeem/interpreter.rb
92
92
  - lib/skeem/parser.rb
93
93
  - lib/skeem/primitive/primitive_builder.rb
94
- - lib/skeem/primitive_procedure.rb
94
+ - lib/skeem/primitive/primitive_procedure.rb
95
95
  - lib/skeem/runtime.rb
96
96
  - lib/skeem/s_expr_builder.rb
97
97
  - lib/skeem/s_expr_nodes.rb
@@ -104,6 +104,7 @@ files:
104
104
  - spec/skeem/interpreter_spec.rb
105
105
  - spec/skeem/parser_spec.rb
106
106
  - spec/skeem/primitive/primitive_builder_spec.rb
107
+ - spec/skeem/primitive/primitive_procedure_spec.rb
107
108
  - spec/skeem/runtime_spec.rb
108
109
  - spec/skeem/s_expr_nodes_spec.rb
109
110
  - spec/skeem/tokenizer_spec.rb
@@ -140,6 +141,7 @@ test_files:
140
141
  - spec/skeem/interpreter_spec.rb
141
142
  - spec/skeem/parser_spec.rb
142
143
  - spec/skeem/primitive/primitive_builder_spec.rb
144
+ - spec/skeem/primitive/primitive_procedure_spec.rb
143
145
  - spec/skeem/runtime_spec.rb
144
146
  - spec/skeem/s_expr_nodes_spec.rb
145
147
  - spec/skeem/tokenizer_spec.rb