mini_kraken 0.1.06 → 0.1.11

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.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +5 -1
  3. data/CHANGELOG.md +56 -1
  4. data/README.md +11 -3
  5. data/lib/mini_kraken/core/association_walker.rb +1 -1
  6. data/lib/mini_kraken/core/base_arg.rb +10 -0
  7. data/lib/mini_kraken/core/conj2.rb +58 -65
  8. data/lib/mini_kraken/core/cons_cell.rb +49 -43
  9. data/lib/mini_kraken/core/def_relation.rb +49 -0
  10. data/lib/mini_kraken/core/disj2.rb +72 -0
  11. data/lib/mini_kraken/core/duck_fiber.rb +1 -1
  12. data/lib/mini_kraken/core/environment.rb +1 -1
  13. data/lib/mini_kraken/core/equals.rb +134 -126
  14. data/lib/mini_kraken/core/fail.rb +18 -14
  15. data/lib/mini_kraken/core/formal_arg.rb +22 -0
  16. data/lib/mini_kraken/core/formal_ref.rb +24 -0
  17. data/lib/mini_kraken/core/goal_arg.rb +5 -3
  18. data/lib/mini_kraken/core/goal_relation.rb +13 -0
  19. data/lib/mini_kraken/core/goal_template.rb +60 -0
  20. data/lib/mini_kraken/core/k_boolean.rb +31 -0
  21. data/lib/mini_kraken/core/outcome.rb +40 -24
  22. data/lib/mini_kraken/core/succeed.rb +17 -13
  23. data/lib/mini_kraken/core/vocabulary.rb +37 -17
  24. data/lib/mini_kraken/glue/fresh_env.rb +45 -3
  25. data/lib/mini_kraken/glue/run_star_expression.rb +45 -19
  26. data/lib/mini_kraken/version.rb +1 -1
  27. data/spec/core/conj2_spec.rb +8 -9
  28. data/spec/core/cons_cell_spec.rb +8 -0
  29. data/spec/core/def_relation_spec.rb +96 -0
  30. data/spec/core/disj2_spec.rb +99 -0
  31. data/spec/core/duck_fiber_spec.rb +12 -1
  32. data/spec/core/equals_spec.rb +3 -3
  33. data/spec/core/goal_template_spec.rb +74 -0
  34. data/spec/core/k_boolean_spec.rb +107 -0
  35. data/spec/core/outcome_spec.rb +48 -0
  36. data/spec/core/vocabulary_spec.rb +11 -5
  37. data/spec/glue/fresh_env_spec.rb +27 -1
  38. data/spec/glue/run_star_expression_spec.rb +538 -70
  39. data/spec/mini_kraken_spec.rb +2 -0
  40. data/spec/spec_helper.rb +0 -1
  41. data/spec/support/factory_methods.rb +17 -1
  42. metadata +19 -2
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../spec_helper' # Use the RSpec framework
4
+ require_relative '../../lib/mini_kraken/core/vocabulary'
5
+
6
+ # Load the class under test
7
+ require_relative '../../lib/mini_kraken/core/outcome'
8
+
9
+ module MiniKraken
10
+ module Core
11
+ describe Outcome do
12
+ let(:voc) do
13
+ obj = Object.new
14
+ obj.extend(Vocabulary)
15
+ obj
16
+ end
17
+ subject { Outcome.new(:"#s", voc) }
18
+
19
+ context 'Initialization:' do
20
+ it 'should be created with a symbol and a vocabulary' do
21
+ expect { Outcome.new(:"#s", voc) }.not_to raise_error
22
+ end
23
+
24
+ it 'should know its resultant' do
25
+ expect(subject.resultant).to eq(:"#s")
26
+ end
27
+
28
+ it 'should know its parent' do
29
+ expect(subject.parent).to eq(voc)
30
+ end
31
+ end # context
32
+
33
+ context 'Provided services:' do
34
+ it 'should have a factory for failing outcome' do
35
+ instance = Outcome.failure(voc)
36
+ expect(instance.resultant).to eq(:"#u")
37
+ expect(instance.parent).to eq(voc)
38
+ end
39
+
40
+ it 'should have a factory for succeeding outcome' do
41
+ instance = Outcome.success(voc)
42
+ expect(instance.resultant).to eq(:"#s")
43
+ expect(instance.parent).to eq(voc)
44
+ end
45
+ end # context
46
+ end # describe
47
+ end # module
48
+ end # module
@@ -79,11 +79,11 @@ module MiniKraken
79
79
 
80
80
  it 'should provide a walker over ancestors' do
81
81
  walker = subject.ancestor_walker
82
- expect(walker).to be_kind_of(Fiber)
83
- expect(walker.resume).to eq(subject)
84
- expect(walker.resume).to eq(mother)
85
- expect(walker.resume).to eq(grandma)
86
- expect(walker.resume).to be_nil
82
+ expect(walker).to be_kind_of(Enumerator)
83
+ expect(walker.next).to eq(subject)
84
+ expect(walker.next).to eq(mother)
85
+ expect(walker.next).to eq(grandma)
86
+ expect(walker.next).to be_nil
87
87
  end
88
88
 
89
89
  it 'should know if a variable is defined' do
@@ -207,6 +207,12 @@ module MiniKraken
207
207
  expect(subject.get_rank('z')).to eq(0)
208
208
  expect(subject.get_rank('a')).to eq(1)
209
209
  end
210
+
211
+ it 'should provide a String representation of itself' do
212
+ expectation = +"#<#{subject.class}:#{subject.object_id.to_s(16)} @parent="
213
+ expectation << "#<#{subject.parent.class}:#{subject.parent.object_id.to_s(16)}>>"
214
+ expect(subject.inspect).to eq(expectation)
215
+ end
210
216
  end # context
211
217
  end # describe
212
218
  end # module
@@ -3,7 +3,9 @@
3
3
  require_relative '../spec_helper' # Use the RSpec framework
4
4
  require_relative '../../lib/mini_kraken/core/goal'
5
5
  require_relative '../../lib/mini_kraken/core/equals'
6
+ require_relative '../../lib/mini_kraken/core/fail'
6
7
  require_relative '../../lib/mini_kraken/core/k_symbol'
8
+ require_relative '../../lib/mini_kraken/core/succeed'
7
9
 
8
10
  # Load the class under test
9
11
  require_relative '../../lib/mini_kraken/glue/fresh_env'
@@ -17,15 +19,39 @@ module MiniKraken
17
19
  let(:sample_goal) do
18
20
  Core::Goal.new(Core::Equals.instance, [pea, pod])
19
21
  end
22
+ let(:pea_goal) do
23
+ Core::Goal.new(Core::Equals.instance, [pea, pea])
24
+ end
25
+ let(:goal_succeeds) { Core::Goal.new(Core::Succeed.instance, []) }
26
+ let(:goal_fails) { Core::Goal.new(Core::Fail.instance, []) }
20
27
  subject { FreshEnv.new(['q'], sample_goal) }
21
28
 
22
29
  context 'Initialization:' do
23
- it 'should be initialized with an array of names' do
30
+ it 'could be initialized with names and a goal' do
24
31
  expect { FreshEnv.new(['q'], sample_goal) }.not_to raise_error
25
32
  end
26
33
 
34
+ it 'could be initialized with names and goals' do
35
+ expect { FreshEnv.new(%w[x y], [pea_goal, goal_succeeds]) }.not_to raise_error
36
+ end
37
+
27
38
  it 'should know its variables' do
28
39
  expect(subject.vars['q']).not_to be_nil
40
+
41
+ instance = FreshEnv.new(%w[x y], sample_goal)
42
+ expect(instance.vars['x']).not_to be_nil
43
+ expect(instance.vars['y']).not_to be_nil
44
+ end
45
+
46
+ it 'should know its goal' do
47
+ # Single goal at initialization
48
+ expect(subject.goal).to eq(sample_goal)
49
+
50
+ # Multiple goals at initialization
51
+ instance = FreshEnv.new(['q'], [pea_goal, goal_succeeds])
52
+ expect(instance.goal.relation.name).to eq('conj2')
53
+ expect(instance.goal.actuals[0]).to eq(pea_goal)
54
+ expect(instance.goal.actuals[1]).to eq(goal_succeeds)
29
55
  end
30
56
  end # context
31
57
 
@@ -3,8 +3,13 @@
3
3
  require_relative '../spec_helper' # Use the RSpec framework
4
4
  require_relative '../../lib/mini_kraken/core/goal'
5
5
  require_relative '../../lib/mini_kraken/core/conj2'
6
+ require_relative '../../lib/mini_kraken/core/def_relation'
7
+ require_relative '../../lib/mini_kraken/core/disj2'
6
8
  require_relative '../../lib/mini_kraken/core/equals'
7
9
  require_relative '../../lib/mini_kraken/core/fail'
10
+ require_relative '../../lib/mini_kraken/core/formal_arg'
11
+ require_relative '../../lib/mini_kraken/core/formal_ref'
12
+ require_relative '../../lib/mini_kraken/core/goal_template'
8
13
  require_relative '../../lib/mini_kraken/core/succeed'
9
14
 
10
15
  require_relative '../support/factory_methods'
@@ -20,16 +25,26 @@ module MiniKraken
20
25
  let(:pea) { k_symbol(:pea) }
21
26
  let(:pod) { k_symbol(:pod) }
22
27
  let(:sample_goal) { equals_goal(pea, pod) }
28
+ let(:fails) { Core::Goal.new(Core::Fail.instance, []) }
29
+ let(:succeeds) { Core::Goal.new(Core::Succeed.instance, []) }
23
30
  subject { RunStarExpression.new('q', sample_goal) }
24
31
 
25
32
  context 'Initialization:' do
26
- it 'should be initialized with a name and a goal' do
33
+ it 'could be initialized with a name and a goal' do
27
34
  expect { RunStarExpression.new('q', sample_goal) }.not_to raise_error
28
35
  end
29
36
 
37
+ it 'could be initialized with multiple names and a goal' do
38
+ expect { RunStarExpression.new(%w[r x y], sample_goal) }.not_to raise_error
39
+ end
40
+
41
+ it 'could be initialized with multiple names and goals' do
42
+ expect { RunStarExpression.new(%w[r x y], [succeeds, succeeds]) }.not_to raise_error
43
+ end
44
+
30
45
  it 'should know its variables' do
31
46
  expect(subject.env.vars['q']).not_to be_nil
32
- expect(subject.var.name).to eq('q')
47
+ expect(subject.env.vars.values[0].name).to eq('q')
33
48
  end
34
49
 
35
50
  it 'should know its goal' do
@@ -38,16 +53,37 @@ module MiniKraken
38
53
  end # context
39
54
 
40
55
  context 'Provided services:' do
56
+ let(:k_false) { k_boolean(false) }
57
+ let(:k_true) { k_boolean(true) }
58
+ let(:bean) { k_symbol(:bean) }
41
59
  let(:corn) { k_symbol(:corn) }
60
+ let(:cup) { k_symbol(:cup) }
42
61
  let(:meal) { k_symbol(:meal) }
62
+ let(:oil) { k_symbol(:oil) }
63
+ let(:olive) { k_symbol(:olive) }
64
+ let(:red) { k_symbol(:red) }
65
+ let(:soup) { k_symbol(:soup) }
66
+ let(:split) { k_symbol(:split) }
67
+ let(:tea) { k_symbol(:tea) }
68
+ let(:virgin) { k_symbol(:virgin) }
43
69
  let(:ref_q) { Core::VariableRef.new('q') }
70
+ let(:ref_r) { Core::VariableRef.new('r') }
44
71
  let(:ref_x) { Core::VariableRef.new('x') }
45
72
  let(:ref_y) { Core::VariableRef.new('y') }
46
73
  let(:ref_s) { Core::VariableRef.new('s') }
47
74
  let(:ref_t) { Core::VariableRef.new('t') }
48
75
  let(:ref_u) { Core::VariableRef.new('u') }
49
- let(:fails) { Core::Goal.new(Core::Fail.instance, []) }
50
- let(:succeeds) { Core::Goal.new(Core::Succeed.instance, []) }
76
+ let(:ref_z) { Core::VariableRef.new('z') }
77
+ let(:t_ref) { Core::FormalRef.new('t') }
78
+ let(:equals_tea) { Core::GoalTemplate.new(Core::Equals.instance, [tea, t_ref]) }
79
+ let(:equals_cup) { Core::GoalTemplate.new(Core::Equals.instance, [cup, t_ref]) }
80
+ let(:g_template) { Core::GoalTemplate.new(Core::Disj2.instance, [equals_tea, equals_cup]) }
81
+ let(:formal_t) { Core::FormalArg.new('t') }
82
+
83
+ # Reasoned S2, frame 1:82
84
+ # (defrel (teacupo t)
85
+ # (disj2 (== 'tea t) (== 'cup t)))
86
+ let(:teacupo_rel) { Core::DefRelation.new('teacupo', g_template, [formal_t]) }
51
87
 
52
88
  it 'should return a null list with the fail goal' do
53
89
  # Reasoned S2, frame 1:7
@@ -56,7 +92,6 @@ module MiniKraken
56
92
  instance = RunStarExpression.new('q', failing)
57
93
 
58
94
  expect(instance.run).to be_null
59
- expect(ref_q.fresh?(instance.env)).to be_truthy
60
95
  end
61
96
 
62
97
  it 'should return a null list when a goal fails' do
@@ -74,7 +109,6 @@ module MiniKraken
74
109
  # Reasoned S2, frame 1:11
75
110
  # (run* q (== q 'pea) ;; => (pea)
76
111
  expect(instance.run.car).to eq(pea)
77
- expect(ref_q.fresh?(instance.env)).to be_falsey
78
112
  end
79
113
 
80
114
  it 'should unify the righthand variable(s)' do
@@ -84,19 +118,14 @@ module MiniKraken
84
118
  # Reasoned S2, frame 1:12
85
119
  # (run* q (== 'pea q) ;; => (pea)
86
120
  expect(instance.run.car).to eq(pea)
87
-
88
- # Reasoned S2, frame 1:15
89
- expect(ref_q.fresh?(instance.env)).to be_falsey
90
121
  end
91
122
 
92
123
  it 'should return a null list with the succeed goal' do
93
- success = Core::Goal.new(Core::Succeed.instance, [])
94
- instance = RunStarExpression.new('q', success)
124
+ instance = RunStarExpression.new('q', succeeds)
95
125
 
96
126
  # (display (run* q succeed)) ;; => (_0)
97
127
  # Reasoned S2, frame 1:16
98
128
  result = instance.run
99
- expect(ref_q.fresh?(instance.env)).to be_truthy
100
129
 
101
130
  # Reasoned S2, frame 1:17
102
131
  expect(result.car).to eq(any_value(0))
@@ -109,7 +138,6 @@ module MiniKraken
109
138
  # (display (run* q (== 'pea 'pea))) ;; => (_0)
110
139
  # Reasoned S2, frame 1:19
111
140
  result = instance.run
112
- expect(ref_q.fresh?(instance.env)).to be_truthy
113
141
  expect(result.car).to eq(any_value(0))
114
142
  end
115
143
 
@@ -122,7 +150,6 @@ module MiniKraken
122
150
  # (display (run* q (== q q))) ;; => (_0)
123
151
  # Reasoned S2, frame 1:20
124
152
  result = instance.run
125
- expect(ref_q.fresh?(instance.env)).to be_truthy
126
153
  expect(result.car).to eq(any_value(0))
127
154
  end
128
155
 
@@ -134,8 +161,6 @@ module MiniKraken
134
161
  # Reasoned S2, frame 1:21..23
135
162
  # (run* q (fresh (x) (== 'pea q))) ;; => (pea)
136
163
  result = instance.run
137
- expect(ref_q.fresh?(instance.env)).to be_falsey
138
- expect(ref_x.fresh?(fresh_env)).to be_truthy
139
164
 
140
165
  # Reasoned S2, frame 1:40
141
166
  expect(ref_q.different_from?(ref_x, fresh_env)).to be_truthy
@@ -150,8 +175,6 @@ module MiniKraken
150
175
  # Reasoned S2, frame 1:24
151
176
  # (run* q (fresh (x) (== 'pea x))) ;; => (_0)
152
177
  result = instance.run
153
- expect(ref_q.fresh?(instance.env)).to be_truthy
154
- expect(ref_x.fresh?(fresh_env)).to be_falsey
155
178
  expect(result.car).to eq(any_value(0))
156
179
  end
157
180
 
@@ -163,8 +186,6 @@ module MiniKraken
163
186
  # Reasoned S2, frame 1:25
164
187
  # (run* q (fresh (x) (== (cons x '()) q))) ;; => ((_0))
165
188
  result = instance.run
166
- expect(ref_q.fresh?(instance.env)).to be_truthy
167
- expect(ref_x.fresh?(fresh_env)).to be_truthy
168
189
  expect(result.car).to eq(cons(any_value(0)))
169
190
  end
170
191
 
@@ -195,7 +216,6 @@ module MiniKraken
195
216
  # Reasoned S2, frame 1:32
196
217
  # (run* q (== '(((pea)) pod) '(((pea)) pod))) ;; => (_0)
197
218
  result = instance.run
198
- expect(ref_q.fresh?(instance.env)).to be_truthy
199
219
  expect(result.car).to eq(any_value(0))
200
220
  end
201
221
 
@@ -209,7 +229,6 @@ module MiniKraken
209
229
  # Reasoned S2, frame 1:33
210
230
  # (run* q (== '(((pea)) pod) `(((pea)) ,q))) ;; => ('pod)
211
231
  result = instance.run
212
- expect(ref_q.fresh?(instance.env)).to be_falsey
213
232
  expect(result.car).to eq(pod)
214
233
  end
215
234
 
@@ -222,7 +241,6 @@ module MiniKraken
222
241
  # Reasoned S2, frame 1:34
223
242
  # (run* q (== '(((,q)) pod) `(((pea)) pod))) ;; => ('pod)
224
243
  result = instance.run
225
- expect(ref_q.fresh?(instance.env)).to be_falsey
226
244
  expect(result.car).to eq(pea)
227
245
  end
228
246
 
@@ -236,8 +254,6 @@ module MiniKraken
236
254
  # Reasoned S2, frame 1:35
237
255
  # (run* q (fresh (x) (== '(((,q)) pod) `(((,x)) pod)))) ;; => (_0)
238
256
  result = instance.run
239
- expect(ref_q.fresh?(instance.env)).to be_truthy
240
- expect(ref_x.fresh?(fresh_env)).to be_truthy
241
257
  expect(result.car).to eq(any_value(0))
242
258
  end
243
259
 
@@ -251,11 +267,6 @@ module MiniKraken
251
267
  instance = RunStarExpression.new('q', fresh_env)
252
268
 
253
269
  result = instance.run
254
-
255
- # Does propagate work correctly?
256
- expect(ref_q.fresh?(instance.env)).to be_truthy # x isn't defined here
257
- expect(ref_q.fresh?(fresh_env)).to be_falsey
258
- expect(ref_x.fresh?(fresh_env)).to be_falsey
259
270
  expect(result.car).to eq(pod)
260
271
  end
261
272
 
@@ -268,9 +279,6 @@ module MiniKraken
268
279
  instance = RunStarExpression.new('q', fresh_env)
269
280
 
270
281
  result = instance.run
271
- expect(ref_q.fresh?(instance.env)).to be_truthy # x isn't defined here
272
- expect(ref_q.fresh?(fresh_env)).to be_truthy
273
- expect(ref_x.fresh?(fresh_env)).to be_truthy
274
282
  expect(result.car).to eq(cons(any_value(0), cons(any_value(0))))
275
283
  end
276
284
 
@@ -285,12 +293,6 @@ module MiniKraken
285
293
  instance = RunStarExpression.new('q', fresh_env_x)
286
294
 
287
295
  result = instance.run
288
- expect(ref_q.fresh?(fresh_env_y)).to be_truthy
289
- expect(ref_q.bound?(fresh_env_y)).to be_truthy
290
- expect(ref_x.fresh?(fresh_env_y)).to be_truthy
291
- expect(ref_x.bound?(fresh_env_y)).to be_falsy
292
- expect(ref_y.fresh?(fresh_env_y)).to be_truthy
293
- expect(ref_y.bound?(fresh_env_y)).to be_falsy
294
296
 
295
297
  # y should be fused with x...
296
298
  var_x = fresh_env_y.name2var('x')
@@ -314,13 +316,7 @@ module MiniKraken
314
316
  instance = RunStarExpression.new('q', fresh_env_x)
315
317
 
316
318
  result = instance.run
317
- expect(ref_q.fresh?(fresh_env_y)).to be_truthy
318
319
  # q should be bound to '(,x ,y)
319
- expect(ref_q.bound?(fresh_env_y)).to be_truthy
320
- expect(ref_x.fresh?(fresh_env_y)).to be_truthy
321
- expect(ref_x.bound?(fresh_env_y)).to be_falsey
322
- expect(ref_y.fresh?(fresh_env_y)).to be_truthy
323
- expect(ref_y.bound?(fresh_env_y)).to be_falsey
324
320
  expect(result.car).to eq(cons(any_value(0), cons(any_value(1))))
325
321
  end
326
322
 
@@ -334,13 +330,7 @@ module MiniKraken
334
330
  instance = RunStarExpression.new('s', fresh_env_t)
335
331
 
336
332
  result = instance.run
337
- expect(ref_s.fresh?(fresh_env_u)).to be_truthy
338
333
  # s should be bound to '(,t ,u)
339
- expect(ref_s.bound?(fresh_env_u)).to be_truthy
340
- expect(ref_t.fresh?(fresh_env_u)).to be_truthy
341
- expect(ref_t.bound?(fresh_env_u)).to be_falsey
342
- expect(ref_u.fresh?(fresh_env_u)).to be_truthy
343
- expect(ref_u.bound?(fresh_env_u)).to be_falsey
344
334
  expect(result.car).to eq(cons(any_value(0), cons(any_value(1))))
345
335
  end
346
336
 
@@ -354,11 +344,7 @@ module MiniKraken
354
344
  instance = RunStarExpression.new('q', fresh_env_x)
355
345
 
356
346
  result = instance.run
357
- expect(ref_q.fresh?(fresh_env_y)).to be_truthy
358
347
  # q should be bound to '(,x ,y, ,x)
359
- expect(ref_q.bound?(fresh_env_y)).to be_truthy
360
- expect(ref_x.fresh?(fresh_env_y)).to be_truthy
361
- expect(ref_y.fresh?(fresh_env_y)).to be_truthy
362
348
  expect(result.car).to eq(cons(any_value(0), cons(any_value(1), cons(any_value(0)))))
363
349
  end
364
350
 
@@ -369,22 +355,20 @@ module MiniKraken
369
355
  # Reasoned S2, frame 1:50
370
356
  # (run* q (conj2 succeed succeed)) ;; => (_0)
371
357
  result = instance.run
372
- expect(ref_q.fresh?(instance.env)).to be_truthy
373
358
  expect(result.car).to eq(any_value(0))
374
359
  end
375
360
 
376
361
  # TODO: fix erratic RSpec failure
377
- # it 'should support conjunction of one succeed and a successful goal' do
378
- # subgoal = equals_goal(corn, ref_q)
379
- # goal = conj2_goal(succeeds, subgoal)
380
- # instance = RunStarExpression.new('q', goal)
381
-
382
- # # Reasoned S2, frame 1:51
383
- # # (run* q (conj2 succeed (== 'corn q)) ;; => ('corn)
384
- # result = instance.run
385
- # expect(ref_q.fresh?(instance.env)).to be_falsy
386
- # expect(result.car).to eq(corn)
387
- # end
362
+ it 'should support conjunction of one succeed and a successful goal' do
363
+ subgoal = equals_goal(corn, ref_q)
364
+ goal = conj2_goal(succeeds, subgoal)
365
+ instance = RunStarExpression.new('q', goal)
366
+
367
+ # Reasoned S2, frame 1:51
368
+ # (run* q (conj2 succeed (== 'corn q)) ;; => ('corn)
369
+ result = instance.run
370
+ expect(result.car).to eq(corn)
371
+ end
388
372
 
389
373
  it 'should support conjunction of one fail and a successful goal' do
390
374
  subgoal = equals_goal(corn, ref_q)
@@ -394,7 +378,6 @@ module MiniKraken
394
378
  # Reasoned S2, frame 1:52
395
379
  # (run* q (conj2 fail (== 'corn q)) ;; => ()
396
380
  expect(instance.run).to be_null
397
- expect(ref_q.fresh?(instance.env)).to be_truthy
398
381
  end
399
382
 
400
383
  it 'should support conjunction of two contradictory goals' do
@@ -406,7 +389,6 @@ module MiniKraken
406
389
  # Reasoned S2, frame 1:53
407
390
  # (run* q (conj2 (== 'corn q)(== 'meal q)) ;; => ()
408
391
  expect(instance.run).to be_null
409
- expect(ref_q.fresh?(instance.env)).to be_truthy
410
392
  end
411
393
 
412
394
  it 'should succeed the conjunction of two identical goals' do
@@ -418,9 +400,495 @@ module MiniKraken
418
400
  # Reasoned S2, frame 1:54
419
401
  # (run* q (conj2 (== 'corn q)(== 'corn q)) ;; => ('corn)
420
402
  result = instance.run
421
- expect(ref_q.fresh?(instance.env)).to be_falsy
422
403
  expect(result.car).to eq(corn)
423
404
  end
405
+
406
+ it 'should not yield solution when both disjunction arguments fail' do
407
+ goal = disj2_goal(fails, fails)
408
+ instance = RunStarExpression.new('q', goal)
409
+
410
+ # Reasoned S2, frame 1:55
411
+ # (run* q (disj2 fail fail)) ;; => ()
412
+ expect(instance.run).to be_null
413
+ end
414
+
415
+ it 'should yield solution when first argument succeed' do
416
+ subgoal = Core::Goal.new(Core::Equals.instance, [olive, ref_q])
417
+ goal = disj2_goal(subgoal, fails)
418
+ instance = RunStarExpression.new('q', goal)
419
+
420
+ # Reasoned S2, frame 1:56
421
+ # (run* q (disj2 (== 'olive q) fail)) ;; => ('olive)
422
+ result = instance.run
423
+ expect(result.car).to eq(olive)
424
+ end
425
+
426
+ it 'should yield solution when second argument succeed' do
427
+ subgoal = Core::Goal.new(Core::Equals.instance, [oil, ref_q])
428
+ goal = disj2_goal(fails, subgoal)
429
+ instance = RunStarExpression.new('q', goal)
430
+
431
+ # Reasoned S2, frame 1:57
432
+ # (run* q (disj2 fail (== 'oil q))) ;; => (oil)
433
+ result = instance.run
434
+ expect(result.car).to eq(oil)
435
+ end
436
+
437
+ it 'should yield solutions when both arguments succeed' do
438
+ subgoal1 = Core::Goal.new(Core::Equals.instance, [olive, ref_q])
439
+ subgoal2 = Core::Goal.new(Core::Equals.instance, [oil, ref_q])
440
+ goal = disj2_goal(subgoal1, subgoal2)
441
+ instance = RunStarExpression.new('q', goal)
442
+
443
+ # Reasoned S2, frame 1:58
444
+ # (run* q (disj2 (== 'olive q) (== 'oil q))) ;; => (olive oil)
445
+ result = instance.run
446
+ expect(result.car).to eq(olive)
447
+ expect(result.cdr.car).to eq(oil)
448
+ end
449
+
450
+ it 'should support the nesting of variables and disjunction' do
451
+ # Reasoned S2, frame 1:59
452
+ # (run* q (fresh (x) (fresh (y) (disj2 (== '( ,x ,y ) q) (== '( ,x ,y ) q)))))
453
+ # ;; => ((_0 _1) (_0 _1))
454
+ expr1 = cons(ref_x, cons(ref_y))
455
+ subgoal1 = equals_goal(expr1, ref_q)
456
+ expr2 = cons(ref_y, cons(ref_x))
457
+ subgoal2 = equals_goal(expr2, ref_q)
458
+ goal = disj2_goal(subgoal1, subgoal2)
459
+ fresh_env_y = FreshEnv.new(['y'], goal)
460
+ fresh_env_x = FreshEnv.new(['x'], fresh_env_y)
461
+ instance = RunStarExpression.new('q', fresh_env_x)
462
+
463
+ result = instance.run
464
+ # q should be bound to '(,x ,y), then to '(,y ,x)
465
+ expect(result.car).to eq(cons(any_value(0), cons(any_value(1))))
466
+ expect(result.cdr.car).to eq(cons(any_value(0), cons(any_value(1))))
467
+ end
468
+
469
+ it 'should accept nesting of disj2 and conj2 (I)' do
470
+ conj_subgoal = Core::Goal.new(Core::Equals.instance, [olive, ref_x])
471
+ conjunction = conj2_goal(conj_subgoal, fails)
472
+ subgoal = Core::Goal.new(Core::Equals.instance, [oil, ref_x])
473
+ goal = disj2_goal(conjunction, subgoal)
474
+ instance = RunStarExpression.new('x', goal)
475
+
476
+ # Reasoned S2, frame 1:62
477
+ # (run* x (disj2
478
+ # (conj2 (== 'olive x) fail)
479
+ # ('oil x))) ;; => (oil)
480
+ result = instance.run
481
+ expect(result.car).to eq(oil)
482
+ end
483
+
484
+ it 'should accept nesting of disj2 and conj2 (II)' do
485
+ conj_subgoal = Core::Goal.new(Core::Equals.instance, [olive, ref_x])
486
+ conjunction = conj2_goal(conj_subgoal, succeeds)
487
+ subgoal = Core::Goal.new(Core::Equals.instance, [oil, ref_x])
488
+ goal = disj2_goal(conjunction, subgoal)
489
+ instance = RunStarExpression.new('x', goal)
490
+
491
+ # Reasoned S2, frame 1:63
492
+ # (run* x (disj2
493
+ # (conj2 (== 'olive x) succeed)
494
+ # ('oil x))) ;; => (olive oil)
495
+ result = instance.run
496
+ expect(result.car).to eq(olive)
497
+ expect(result.cdr.car).to eq(oil)
498
+ end
499
+
500
+ it 'should accept nesting of disj2 and conj2 (III)' do
501
+ conj_subgoal = Core::Goal.new(Core::Equals.instance, [olive, ref_x])
502
+ conjunction = conj2_goal(conj_subgoal, succeeds)
503
+ subgoal = Core::Goal.new(Core::Equals.instance, [oil, ref_x])
504
+ goal = disj2_goal(subgoal, conjunction)
505
+ instance = RunStarExpression.new('x', goal)
506
+
507
+ # Reasoned S2, frame 1:64
508
+ # (run* x (disj2
509
+ # ('oil x)
510
+ # (conj2 (== 'olive x) succeed))) ;; => (oil olive)
511
+ result = instance.run
512
+ expect(result.car).to eq(oil)
513
+ expect(result.cdr.car).to eq(olive)
514
+ end
515
+
516
+ it 'should accept nesting of disj2 and conj2 (IV)' do
517
+ oil_goal = Core::Goal.new(Core::Equals.instance, [oil, ref_x])
518
+ disja = disj2_goal(succeeds, oil_goal)
519
+ olive_goal = Core::Goal.new(Core::Equals.instance, [olive, ref_x])
520
+ disjb = disj2_goal(olive_goal, disja)
521
+ virgin_goal = Core::Goal.new(Core::Equals.instance, [virgin, ref_x])
522
+ conjunction = conj2_goal(virgin_goal, fails)
523
+ goal = disj2_goal(conjunction, disjb)
524
+ instance = RunStarExpression.new('x', goal)
525
+
526
+ # Reasoned S2, frame 1:65
527
+ # (run* x (disj2
528
+ # (conj2(== 'virgin x) fails)
529
+ # (disj2
530
+ # (== 'olive x)
531
+ # (dis2
532
+ # succeeds
533
+ # (== 'oil x))))) ;; => (olive _0 oil)
534
+ result = instance.run
535
+ expect(result.car).to eq(olive)
536
+ expect(result.cdr.car).to eq(any_value(0))
537
+ expect(result.cdr.cdr.car).to eq(oil)
538
+ end
539
+
540
+ it 'should accept nesting fresh, disj2 and conj2 expressions (I)' do
541
+ subgoal1 = equals_goal(split, ref_x)
542
+ expr1 = equals_goal(pea, ref_y)
543
+ expr2 = equals_goal(cons(ref_x, cons(ref_y)), ref_r)
544
+ subgoal2 = conj2_goal(expr1, expr2)
545
+ goal = conj2_goal(subgoal1, subgoal2)
546
+ fresh_env_y = FreshEnv.new(['y'], goal)
547
+ fresh_env_x = FreshEnv.new(['x'], fresh_env_y)
548
+ instance = RunStarExpression.new('r', fresh_env_x)
549
+
550
+ # Reasoned S2, frame 1:67
551
+ # (run* r
552
+ # (fresh x
553
+ # (fresh y
554
+ # (conj2
555
+ # (== 'split x)
556
+ # (conj2
557
+ # (== 'pea y)
558
+ # (== '(,x ,y) r)))))) ;; => ((split pea))
559
+ result = instance.run
560
+ expect(result.car.car).to eq(split)
561
+ expect(result.car.cdr.car).to eq(pea)
562
+ end
563
+
564
+ it 'should accept nesting fresh, disj2 and conj2 expressions (II)' do
565
+ expr1 = equals_goal(split, ref_x)
566
+ expr2 = equals_goal(pea, ref_y)
567
+ subgoal1 = conj2_goal(expr1, expr2)
568
+ subgoal2 = equals_goal(cons(ref_x, cons(ref_y)), ref_r)
569
+ goal = conj2_goal(subgoal1, subgoal2)
570
+ fresh_env_y = FreshEnv.new(['y'], goal)
571
+ fresh_env_x = FreshEnv.new(['x'], fresh_env_y)
572
+ instance = RunStarExpression.new('r', fresh_env_x)
573
+
574
+ # Reasoned S2, frame 1:68
575
+ # (run* r
576
+ # (fresh x
577
+ # (fresh y
578
+ # (conj2
579
+ # (conj2
580
+ # (== 'split x)
581
+ # (== 'pea y)
582
+ # (== '(,x ,y) r)))))) ;; => ((split pea))
583
+ result = instance.run
584
+ expect(result.car.car).to eq(split)
585
+ expect(result.car.cdr.car).to eq(pea)
586
+ end
587
+
588
+ it 'should accept fresh with multiple variables' do
589
+ expr1 = equals_goal(split, ref_x)
590
+ expr2 = equals_goal(pea, ref_y)
591
+ subgoal1 = conj2_goal(expr1, expr2)
592
+ subgoal2 = equals_goal(cons(ref_x, cons(ref_y)), ref_r)
593
+ goal = conj2_goal(subgoal1, subgoal2)
594
+ fresh_env = FreshEnv.new(%w[x y], goal)
595
+ instance = RunStarExpression.new('r', fresh_env)
596
+
597
+ # Reasoned S2, frame 1:70
598
+ # (run* r
599
+ # (fresh (x y)
600
+ # (conj2
601
+ # (conj2
602
+ # (== 'split x)
603
+ # (== 'pea y)
604
+ # (== '(,x ,y) r))))) ;; => ((split pea))
605
+ result = instance.run
606
+ expect(result.car.car).to eq(split)
607
+ expect(result.car.cdr.car).to eq(pea)
608
+ end
609
+
610
+ it 'should accept multiple variables' do
611
+ expr1 = equals_goal(split, ref_x)
612
+ expr2 = equals_goal(pea, ref_y)
613
+ subgoal1 = conj2_goal(expr1, expr2)
614
+ subgoal2 = equals_goal(cons(ref_x, cons(ref_y)), ref_r)
615
+ goal = conj2_goal(subgoal1, subgoal2)
616
+ instance = RunStarExpression.new(%w[r x y], goal)
617
+
618
+ # Reasoned S2, frame 1:72
619
+ # (run* (r x y)
620
+ # (conj2
621
+ # (conj2
622
+ # (== 'split x)
623
+ # (== 'pea y))
624
+ # (== '(,x ,y) r))) ;; => (((split pea) split pea))
625
+ # o
626
+ # / \
627
+ # o nil
628
+ # / \
629
+ # / \
630
+ # / \
631
+ # / \
632
+ # / \
633
+ # o o
634
+ # / \ / \
635
+ # split o split o
636
+ # / \ / \
637
+ # pea nil pea nil
638
+ result = instance.run
639
+ expect(result.car.car.car).to eq(split)
640
+ expect(result.car.car.cdr.car).to eq(pea)
641
+ expect(result.car.car.cdr.cdr).to be_nil
642
+ expect(result.car.cdr.car).to eq(split)
643
+ expect(result.car.cdr.cdr.car).to eq(pea)
644
+ expect(result.car.cdr.cdr.cdr).to be_nil
645
+ end
646
+
647
+ it 'should allow simplication of expressions' do
648
+ expr1 = equals_goal(split, ref_x)
649
+ expr2 = equals_goal(pea, ref_y)
650
+ goal = conj2_goal(expr1, expr2)
651
+ instance = RunStarExpression.new(%w[x y], goal)
652
+
653
+ # Reasoned S2, frame 1:75
654
+ # (run* (x y)
655
+ # (conj2
656
+ # (== 'split x)
657
+ # (== 'pea y))) ;; => ((split pea))
658
+ result = instance.run
659
+ expect(result.car.car).to eq(split)
660
+ expect(result.car.cdr.car).to eq(pea)
661
+ end
662
+
663
+ it 'should allow simplication of expressions' do
664
+ expr1 = equals_goal(split, ref_x)
665
+ expr2 = equals_goal(pea, ref_y)
666
+ subgoal1 = conj2_goal(expr1, expr2)
667
+ expr3 = equals_goal(red, ref_x)
668
+ expr4 = equals_goal(bean, ref_y)
669
+ subgoal2 = conj2_goal(expr3, expr4)
670
+ goal = disj2_goal(subgoal1, subgoal2)
671
+ instance = RunStarExpression.new(%w[x y], goal)
672
+
673
+ # Reasoned S2, frame 1:76
674
+ # (run* (x y)
675
+ # (disj2
676
+ # (conj2 (== 'split x) (== 'pea y))
677
+ # (conj2 (== 'red x) (== 'bean y)))) ;; => ((split pea)(red bean))
678
+ result = instance.run
679
+ expect(result.car.car).to eq(split)
680
+ expect(result.car.cdr.car).to eq(pea)
681
+ expect(result.cdr.car.car).to eq(red)
682
+ expect(result.cdr.car.cdr.car).to eq(bean)
683
+ end
684
+
685
+ it 'should allow nesting a disjunction inside of conjunction' do
686
+ expr1 = equals_goal(split, ref_x)
687
+ expr2 = equals_goal(red, ref_x)
688
+ subgoal1 = disj2_goal(expr1, expr2)
689
+ subgoal2 = equals_goal(ref_x, ref_y)
690
+ goal = conj2_goal(subgoal1, subgoal2)
691
+ instance = RunStarExpression.new(%w[x y], goal)
692
+
693
+ # (display (run* (x y)
694
+ # (conj2
695
+ # (disj2
696
+ # (== 'split x)
697
+ # (== 'red x))
698
+ # (== x y)))) ;; => ((split split) (red red))
699
+ result = instance.run
700
+ expect(result.car.car).to eq(split)
701
+ expect(result.car.cdr.car).to eq(split)
702
+ expect(result.cdr.car.cdr.car).to eq(red)
703
+ end
704
+
705
+ it 'should accept fresh with multiple variables' do
706
+ expr1 = equals_goal(split, ref_x)
707
+ expr2 = equals_goal(pea, ref_y)
708
+ subgoal1 = conj2_goal(expr1, expr2)
709
+ expr3 = equals_goal(red, ref_x)
710
+ expr4 = equals_goal(bean, ref_y)
711
+ subgoal2 = conj2_goal(expr3, expr4)
712
+ subgoal3 = disj2_goal(subgoal1, subgoal2)
713
+ subgoal4 = equals_goal(cons(ref_x, cons(ref_y, cons(soup))), ref_r)
714
+ goal = conj2_goal(subgoal3, subgoal4)
715
+ fresh_env = FreshEnv.new(%w[x y], goal)
716
+ instance = RunStarExpression.new('r', fresh_env)
717
+
718
+ # Reasoned S2, frame 1:77
719
+ # (run* r
720
+ # (fresh (x y)
721
+ # (conj2
722
+ # (disj2
723
+ # (conj2 (== 'split x) (== 'pea y))
724
+ # (conj2 (== 'red x) (== 'bean y)))
725
+ # (== '(,x ,y soup) r)))) ;; => ((split pea soup) (red bean soup))
726
+ result = instance.run
727
+ expect(result.car.car).to eq(split)
728
+ expect(result.car.cdr.car).to eq(pea)
729
+ expect(result.car.cdr.cdr.car).to eq(soup)
730
+ expect(result.cdr.car.car).to eq(red)
731
+ expect(result.cdr.car.cdr.car).to eq(bean)
732
+ expect(result.cdr.car.cdr.cdr.car).to eq(soup)
733
+ end
734
+
735
+ it 'should allow fresh with multiple goals' do
736
+ expr1 = equals_goal(split, ref_x)
737
+ expr2 = equals_goal(pea, ref_y)
738
+ subgoal1 = conj2_goal(expr1, expr2)
739
+ expr3 = equals_goal(red, ref_x)
740
+ expr4 = equals_goal(bean, ref_y)
741
+ subgoal2 = conj2_goal(expr3, expr4)
742
+ goal1 = disj2_goal(subgoal1, subgoal2)
743
+ goal2 = equals_goal(cons(ref_x, cons(ref_y, cons(soup))), ref_r)
744
+ fresh_env = FreshEnv.new(%w[x y], [goal1, goal2])
745
+ instance = RunStarExpression.new('r', fresh_env)
746
+
747
+ # Reasoned S2, frame 1:78
748
+ # (run* r
749
+ # (fresh (x y)
750
+ # (disj2
751
+ # (conj2 (== 'split x) (== 'pea y))
752
+ # (conj2 (== 'red x) (== 'bean y)))
753
+ # (== '(,x ,y soup) r))) ;; => ((split pea soup) (red bean soup))
754
+ result = instance.run
755
+ expect(result.car.car).to eq(split)
756
+ expect(result.car.cdr.car).to eq(pea)
757
+ expect(result.car.cdr.cdr.car).to eq(soup)
758
+ expect(result.cdr.car.car).to eq(red)
759
+ expect(result.cdr.car.cdr.car).to eq(bean)
760
+ expect(result.cdr.car.cdr.cdr.car).to eq(soup)
761
+ end
762
+
763
+ it 'should allow run* with multiple goals' do
764
+ expr1 = equals_goal(split, ref_x)
765
+ expr2 = equals_goal(pea, ref_y)
766
+ subgoal1 = conj2_goal(expr1, expr2)
767
+ expr3 = equals_goal(red, ref_x)
768
+ expr4 = equals_goal(bean, ref_y)
769
+ subgoal2 = conj2_goal(expr3, expr4)
770
+ goal1 = disj2_goal(subgoal1, subgoal2)
771
+ goal2 = equals_goal(soup, ref_z)
772
+ instance = RunStarExpression.new(%w[x y z], [goal1, goal2])
773
+
774
+ # Reasoned S2, frame 1:80
775
+ # (run* (x y z)
776
+ # (disj2
777
+ # (conj2 (== 'split x) (== 'pea y))
778
+ # (conj2 (== 'red x) (== 'bean y)))
779
+ # (== 'soup z)) ;; => ((split pea soup) (red bean soup))
780
+ result = instance.run
781
+ expect(result.car.car).to eq(split)
782
+ expect(result.car.cdr.car).to eq(pea)
783
+ expect(result.car.cdr.cdr.car).to eq(soup)
784
+ expect(result.cdr.car.car).to eq(red)
785
+ expect(result.cdr.car.cdr.car).to eq(bean)
786
+ expect(result.cdr.car.cdr.cdr.car).to eq(soup)
787
+ end
788
+
789
+ it 'should allow simplified expressions with multiple goals' do
790
+ expr1 = equals_goal(split, ref_x)
791
+ expr2 = equals_goal(pea, ref_y)
792
+ instance = RunStarExpression.new(%w[x y], [expr1, expr2])
793
+
794
+ # Reasoned S2, frame 1:81
795
+ # (run* (x y)
796
+ # (== 'split x)
797
+ # (== 'pea y)) ;; => ((split pea))
798
+ result = instance.run
799
+ expect(result.car.car).to eq(split)
800
+ expect(result.car.cdr.car).to eq(pea)
801
+ end
802
+
803
+ it 'should solve expression with defrel' do
804
+ teacupo_goal = Core::Goal.new(teacupo_rel, [ref_x])
805
+
806
+ # Reasoned S2, frame 1:83
807
+ # (run* x
808
+ # (teacupo x)) ;; => ((tea cup))
809
+ instance = RunStarExpression.new('x', teacupo_goal)
810
+
811
+ result = instance.run
812
+ expect(result.car).to eq(tea)
813
+ expect(result.cdr.car).to eq(cup)
814
+ end
815
+
816
+ it 'should solve expression with defrel and booleans' do
817
+ teacupo_goal = Core::Goal.new(teacupo_rel, [ref_x])
818
+ expr2 = equals_goal(k_true, ref_y)
819
+ subgoal1 = conj2_goal(teacupo_goal, expr2)
820
+ expr3 = equals_goal(k_false, ref_x)
821
+ expr4 = equals_goal(k_true, ref_y)
822
+ subgoal2 = conj2_goal(expr3, expr4)
823
+ goal = disj2_goal(subgoal1, subgoal2)
824
+ # Reasoned S2, frame 1:84
825
+ # (run* (x y)
826
+ # (disj2
827
+ # (conj2 (teacupo x) (== #t y))
828
+ # (conj2 (== #f x) (== #t y))) ;; => ((#f #t)(tea #t) (cup #t))
829
+ instance = RunStarExpression.new(%w[x y], goal)
830
+
831
+ result = instance.run
832
+ # Order of solutions differs from RS book
833
+ expect(result.car).to eq(cons(tea, cons(true)))
834
+ expect(result.cdr.car).to eq(cons(cup, cons(true)))
835
+ expect(result.cdr.cdr.car).to eq(cons(false, cons(true)))
836
+ end
837
+
838
+ it 'should solve expression with two variable and defrel' do
839
+ teacupo_goal1 = Core::Goal.new(teacupo_rel, [ref_x])
840
+ teacupo_goal2 = Core::Goal.new(teacupo_rel, [ref_y])
841
+
842
+ # Reasoned S2, frame 1:85
843
+ # (run* (x y)
844
+ # (teacupo x)
845
+ # (teacupo y)) ;; => ((tea tea)(tea cup)(cup tea)(cup c))
846
+ instance = RunStarExpression.new(%w[x y], [teacupo_goal1, teacupo_goal2])
847
+
848
+ result = instance.run
849
+ expect(result.car).to eq(cons(tea, cons(tea)))
850
+ expect(result.cdr.car).to eq(cons(tea, cons(cup)))
851
+ expect(result.cdr.cdr.car).to eq(cons(cup, cons(tea)))
852
+ expect(result.cdr.cdr.cdr.car).to eq(cons(cup, cons(cup)))
853
+ end
854
+
855
+ it 'should solve expression with two variable and defrel' do
856
+ teacupo_goal1 = Core::Goal.new(teacupo_rel, [ref_x])
857
+ teacupo_goal2 = Core::Goal.new(teacupo_rel, [ref_x])
858
+
859
+ # Reasoned S2, frame 1:86
860
+ # (run* (x y)
861
+ # (teacupo x)
862
+ # (teacupo x)) ;; => ((tea _0)(cup _0))
863
+ instance = RunStarExpression.new(%w[x y], [teacupo_goal1, teacupo_goal2])
864
+
865
+ result = instance.run
866
+ expect(result.car).to eq(cons(tea, cons(any_value(0))))
867
+ expect(result.cdr.car).to eq(cons(cup, cons(any_value(0))))
868
+ end
869
+
870
+ it 'should solve expression with defrel and booleans' do
871
+ teacupo_goal1 = Core::Goal.new(teacupo_rel, [ref_x])
872
+ teacupo_goal2 = Core::Goal.new(teacupo_rel, [ref_x])
873
+ subgoal1 = conj2_goal(teacupo_goal1, teacupo_goal2)
874
+ expr3 = equals_goal(k_false, ref_x)
875
+ expr4 = Core::Goal.new(teacupo_rel, [ref_y])
876
+ subgoal2 = conj2_goal(expr3, expr4)
877
+ goal = disj2_goal(subgoal1, subgoal2)
878
+ # Reasoned S2, frame 1:87
879
+ # (run* (x y)
880
+ # (disj2
881
+ # (conj2 (teacupo x) (teacupo x))
882
+ # (conj2 (== #f x) (teacupo y)))) ;; => ((#f tea)(#f cup)(tea _0)(cup _0))
883
+ instance = RunStarExpression.new(%w[x y], goal)
884
+
885
+ result = instance.run
886
+ # Order of solutions differs from RS book
887
+ expect(result.car).to eq(cons(tea, cons(any_value(0))))
888
+ expect(result.cdr.car).to eq(cons(cup, cons(any_value(0))))
889
+ expect(result.cdr.cdr.car).to eq(cons(false, cons(tea)))
890
+ expect(result.cdr.cdr.cdr.car).to eq(cons(false, cons(cup)))
891
+ end
424
892
  end # context
425
893
  end # describe
426
894
  end # module