mini_kraken 0.2.02 → 0.3.02

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 (134) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +378 -333
  3. data/CHANGELOG.md +52 -0
  4. data/README.md +19 -19
  5. data/lib/mini_kraken.rb +0 -1
  6. data/lib/mini_kraken/atomic/all_atomic.rb +5 -0
  7. data/lib/mini_kraken/atomic/atomic_term.rb +96 -0
  8. data/lib/mini_kraken/atomic/k_boolean.rb +42 -0
  9. data/lib/mini_kraken/{core → atomic}/k_integer.rb +2 -5
  10. data/lib/mini_kraken/atomic/k_string.rb +17 -0
  11. data/lib/mini_kraken/{core → atomic}/k_symbol.rb +4 -8
  12. data/lib/mini_kraken/composite/all_composite.rb +4 -0
  13. data/lib/mini_kraken/composite/composite_term.rb +27 -0
  14. data/lib/mini_kraken/composite/cons_cell.rb +299 -0
  15. data/lib/mini_kraken/composite/cons_cell_visitor.rb +50 -0
  16. data/lib/mini_kraken/composite/list.rb +32 -0
  17. data/lib/mini_kraken/core/all_core.rb +8 -0
  18. data/lib/mini_kraken/core/any_value.rb +31 -7
  19. data/lib/mini_kraken/core/arity.rb +69 -0
  20. data/lib/mini_kraken/core/association.rb +29 -4
  21. data/lib/mini_kraken/core/association_copy.rb +50 -0
  22. data/lib/mini_kraken/core/base_term.rb +13 -0
  23. data/lib/mini_kraken/core/blackboard.rb +315 -0
  24. data/lib/mini_kraken/core/bookmark.rb +46 -0
  25. data/lib/mini_kraken/core/context.rb +492 -0
  26. data/lib/mini_kraken/core/duck_fiber.rb +21 -19
  27. data/lib/mini_kraken/core/entry.rb +40 -0
  28. data/lib/mini_kraken/core/fail.rb +20 -18
  29. data/lib/mini_kraken/core/fusion.rb +29 -0
  30. data/lib/mini_kraken/core/goal.rb +20 -29
  31. data/lib/mini_kraken/core/log_var.rb +22 -0
  32. data/lib/mini_kraken/core/log_var_ref.rb +108 -0
  33. data/lib/mini_kraken/core/nullary_relation.rb +2 -9
  34. data/lib/mini_kraken/core/parametrized_term.rb +61 -0
  35. data/lib/mini_kraken/core/relation.rb +14 -28
  36. data/lib/mini_kraken/core/scope.rb +67 -0
  37. data/lib/mini_kraken/core/solver_adapter.rb +58 -0
  38. data/lib/mini_kraken/core/specification.rb +48 -0
  39. data/lib/mini_kraken/core/succeed.rb +21 -17
  40. data/lib/mini_kraken/core/symbol_table.rb +137 -0
  41. data/lib/mini_kraken/core/term.rb +15 -4
  42. data/lib/mini_kraken/glue/dsl.rb +45 -81
  43. data/lib/mini_kraken/glue/run_star_expression.rb +28 -30
  44. data/lib/mini_kraken/rela/all_rela.rb +8 -0
  45. data/lib/mini_kraken/rela/binary_relation.rb +30 -0
  46. data/lib/mini_kraken/rela/conde.rb +143 -0
  47. data/lib/mini_kraken/rela/conj2.rb +65 -0
  48. data/lib/mini_kraken/rela/def_relation.rb +93 -0
  49. data/lib/mini_kraken/rela/disj2.rb +70 -0
  50. data/lib/mini_kraken/rela/fresh.rb +98 -0
  51. data/lib/mini_kraken/{core → rela}/goal_relation.rb +7 -9
  52. data/lib/mini_kraken/rela/unify.rb +258 -0
  53. data/lib/mini_kraken/version.rb +1 -1
  54. data/mini_kraken.gemspec +2 -2
  55. data/spec/.rubocop.yml +1 -1
  56. data/spec/atomic/atomic_term_spec.rb +98 -0
  57. data/spec/{core → atomic}/k_boolean_spec.rb +19 -34
  58. data/spec/{core → atomic}/k_symbol_spec.rb +3 -16
  59. data/spec/composite/cons_cell_spec.rb +225 -0
  60. data/spec/composite/cons_cell_visitor_spec.rb +158 -0
  61. data/spec/composite/list_spec.rb +50 -0
  62. data/spec/core/any_value_spec.rb +52 -0
  63. data/spec/core/arity_spec.rb +92 -0
  64. data/spec/core/association_copy_spec.rb +69 -0
  65. data/spec/core/association_spec.rb +31 -4
  66. data/spec/core/blackboard_spec.rb +287 -0
  67. data/spec/core/bookmark_spec.rb +40 -0
  68. data/spec/core/context_spec.rb +245 -0
  69. data/spec/core/core_spec.rb +40 -0
  70. data/spec/core/duck_fiber_spec.rb +16 -46
  71. data/spec/core/fail_spec.rb +5 -6
  72. data/spec/core/goal_spec.rb +24 -14
  73. data/spec/core/log_var_ref_spec.rb +105 -0
  74. data/spec/core/log_var_spec.rb +64 -0
  75. data/spec/core/nullary_relation_spec.rb +33 -0
  76. data/spec/core/parametrized_tem_spec.rb +39 -0
  77. data/spec/core/relation_spec.rb +33 -0
  78. data/spec/core/scope_spec.rb +73 -0
  79. data/spec/core/solver_adapter_spec.rb +70 -0
  80. data/spec/core/specification_spec.rb +43 -0
  81. data/spec/core/succeed_spec.rb +5 -5
  82. data/spec/core/symbol_table_spec.rb +142 -0
  83. data/spec/glue/dsl_chap1_spec.rb +96 -144
  84. data/spec/glue/dsl_chap2_spec.rb +350 -0
  85. data/spec/glue/run_star_expression_spec.rb +82 -906
  86. data/spec/rela/conde_spec.rb +153 -0
  87. data/spec/rela/conj2_spec.rb +123 -0
  88. data/spec/rela/def_relation_spec.rb +119 -0
  89. data/spec/rela/disj2_spec.rb +117 -0
  90. data/spec/rela/fresh_spec.rb +147 -0
  91. data/spec/rela/unify_spec.rb +369 -0
  92. data/spec/support/factory_atomic.rb +29 -0
  93. data/spec/support/factory_composite.rb +21 -0
  94. data/spec/support/factory_methods.rb +11 -26
  95. metadata +100 -64
  96. data/lib/mini_kraken/core/association_walker.rb +0 -183
  97. data/lib/mini_kraken/core/atomic_term.rb +0 -67
  98. data/lib/mini_kraken/core/base_arg.rb +0 -10
  99. data/lib/mini_kraken/core/binary_relation.rb +0 -63
  100. data/lib/mini_kraken/core/composite_goal.rb +0 -46
  101. data/lib/mini_kraken/core/composite_term.rb +0 -41
  102. data/lib/mini_kraken/core/conde.rb +0 -143
  103. data/lib/mini_kraken/core/conj2.rb +0 -79
  104. data/lib/mini_kraken/core/cons_cell.rb +0 -82
  105. data/lib/mini_kraken/core/def_relation.rb +0 -50
  106. data/lib/mini_kraken/core/designation.rb +0 -55
  107. data/lib/mini_kraken/core/disj2.rb +0 -72
  108. data/lib/mini_kraken/core/environment.rb +0 -73
  109. data/lib/mini_kraken/core/equals.rb +0 -156
  110. data/lib/mini_kraken/core/formal_arg.rb +0 -22
  111. data/lib/mini_kraken/core/formal_ref.rb +0 -25
  112. data/lib/mini_kraken/core/freshness.rb +0 -45
  113. data/lib/mini_kraken/core/goal_arg.rb +0 -12
  114. data/lib/mini_kraken/core/goal_template.rb +0 -62
  115. data/lib/mini_kraken/core/k_boolean.rb +0 -35
  116. data/lib/mini_kraken/core/outcome.rb +0 -53
  117. data/lib/mini_kraken/core/variable.rb +0 -41
  118. data/lib/mini_kraken/core/variable_ref.rb +0 -78
  119. data/lib/mini_kraken/core/vocabulary.rb +0 -442
  120. data/lib/mini_kraken/glue/fresh_env.rb +0 -75
  121. data/spec/core/association_walker_spec.rb +0 -192
  122. data/spec/core/conde_spec.rb +0 -147
  123. data/spec/core/conj2_spec.rb +0 -114
  124. data/spec/core/cons_cell_spec.rb +0 -107
  125. data/spec/core/def_relation_spec.rb +0 -96
  126. data/spec/core/disj2_spec.rb +0 -99
  127. data/spec/core/environment_spec.rb +0 -142
  128. data/spec/core/equals_spec.rb +0 -304
  129. data/spec/core/goal_template_spec.rb +0 -74
  130. data/spec/core/outcome_spec.rb +0 -48
  131. data/spec/core/variable_ref_spec.rb +0 -27
  132. data/spec/core/variable_spec.rb +0 -35
  133. data/spec/core/vocabulary_spec.rb +0 -219
  134. data/spec/glue/fresh_env_spec.rb +0 -62
@@ -0,0 +1,350 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../spec_helper' # Use the RSpec framework
4
+
5
+ # Load the class under test
6
+ require_relative '../../lib/mini_kraken/glue/dsl'
7
+
8
+
9
+ module MiniKraken
10
+ module Glue
11
+ describe 'DSL (Chap 2)' do
12
+ include DSL
13
+
14
+ context 'Chapter 2 examples:' do
15
+ # ((:a) (:b) (:c))
16
+ let(:abc) { cons(cons(:a), cons(cons(:b), cons(cons(:c)))) }
17
+
18
+ # '(:a :c :o :r :n)
19
+ let(:acorn) { cons(:a, cons(:c, cons(:o, cons(:r, cons(:n))))) }
20
+
21
+ # '(:c :o :r :n)
22
+ let(:corn) { cons(:c, cons(:o, cons(:r, cons(:n)))) }
23
+
24
+ # '(:grape :raisin :pear)'
25
+ let(:fruits) { cons(:grape, cons(:raisin, cons(:pear))) }
26
+
27
+ let(:uuid_pattern) do
28
+ /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/
29
+ end
30
+
31
+ it 'accepts caro definition inspired from frame 2:6' do
32
+ # Reasoned S2, frame 2:6
33
+ # (defrel (caro p a)
34
+ # (fresh (d)
35
+ # (== (cons a d) p)))
36
+
37
+ # As 'p' has a special meaning in Ruby, the argument has been renamed to 'r'
38
+ caro_rel = defrel('caro', %w[r a], fresh('d', unify(cons(a, d), r)))
39
+
40
+ # Check side-effect from DSL
41
+ expect(instance_variable_get(:@defrels)['caro']).to eq(caro_rel)
42
+
43
+ # Check results of defrel
44
+ expect(caro_rel).to be_kind_of(Rela::DefRelation)
45
+ expect(caro_rel.name).to eq('caro')
46
+ expect(caro_rel.arity).to eq(2)
47
+ expect(caro_rel.formals[0]).to match(/^r_/)
48
+ expect(caro_rel.formals[0]).to match(uuid_pattern)
49
+ expect(caro_rel.formals[1]).to match(/^a_/)
50
+ expect(caro_rel.formals[1]).to match(uuid_pattern)
51
+ g_template = caro_rel.expression
52
+
53
+ # Checking the 'fresh' part
54
+ expect(g_template).to be_kind_of(Core::Goal)
55
+ expect(g_template.relation).to be_kind_of(Rela::Fresh)
56
+ expect(g_template.actuals[0]).to eq('d')
57
+ fresh_2nd_actual = g_template.actuals[1]
58
+
59
+ # Checking the (== (cons a d) r) sub-expression
60
+ expect(fresh_2nd_actual).to be_kind_of(Core::Goal)
61
+ expect(fresh_2nd_actual.relation.name).to eq('unify')
62
+ expect(fresh_2nd_actual.actuals[0]).to be_kind_of(Composite::ConsCell)
63
+ expect(fresh_2nd_actual.actuals[0].to_s).to match(/^\(a_[-0-9a-f]+ \. d\)$/)
64
+ expect(fresh_2nd_actual.actuals[1]).to be_kind_of(Core::LogVarRef)
65
+ expect(fresh_2nd_actual.actuals[1].name).to match(/^r_[-0-9a-f]+$/)
66
+ end
67
+
68
+ # In Scheme:
69
+ # (defrel (caro p a)
70
+ # (fresh (d)
71
+ # (== p (cons a d))))
72
+ # In Ruby, `p`is a standard Kernel method => replace it by `r`
73
+ def defrel_caro
74
+ defrel('caro', %w[r a], fresh('d', unify(r, cons(a, d))))
75
+ end
76
+
77
+ it 'passes frame 2:3' do
78
+ defrel_caro
79
+
80
+ # (run* q
81
+ # (caro '(a c o r n) q)) ;; => (a)
82
+ result = run_star('q', caro(acorn, q))
83
+ expect(result.car).to eq(:a)
84
+ end
85
+
86
+ it 'passes frame 2:4' do
87
+ defrel_caro
88
+
89
+ # (run* q
90
+ # (caro '(a c o r n) 'a)) ;; => (_0)
91
+ result = run_star('q', caro(acorn, :a))
92
+ expect(result.car).to eq(:_0)
93
+ end
94
+
95
+ # IT FAILS
96
+ it 'passes frame 2:5' do
97
+ defrel_caro
98
+
99
+ # (run* r
100
+ # (fresh (x y)
101
+ # (caro '(,r ,y) x)
102
+ # (== 'pear x)));; r => (pear)
103
+ result = run_star('r', fresh(%w[x y],
104
+ [caro(cons(r, cons(y)), x),
105
+ unify(:pear, x)]))
106
+ expect(result.car).to eq(:pear)
107
+ end
108
+
109
+ it 'passes frame 2:8' do
110
+ defrel_caro
111
+
112
+ # (run* r
113
+ # (fresh (x y)
114
+ # (caro '(grape raisin pear) x)
115
+ # (caro '((a) (b) (c)) y)
116
+ # (== (cons x y) r))) ;; r => ((grape a))
117
+
118
+ result = run_star('r', fresh(%w[x y],
119
+ [caro(fruits, x),
120
+ caro(abc, y),
121
+ unify(cons(x, y), r)]))
122
+ expect(result.car).to eq(cons(:grape, cons(:a)))
123
+ end
124
+
125
+ it 'accepts cdro definition inspired from frame 2:13' do
126
+ # Reasoned S2, frame 2:13
127
+ # (defrel (cdro p d)
128
+ # (fresh (a)
129
+ # (== (cons a d) p)))
130
+
131
+ # As 'p' has a special meaning in Ruby, the argument has been remaned to 'r'
132
+ cdro_rel = defrel('cdro', %w[r d], fresh('a', unify(cons(a, d), r)))
133
+
134
+ expect(cdro_rel).to be_kind_of(Rela::DefRelation)
135
+ expect(cdro_rel.name).to eq('cdro')
136
+ expect(cdro_rel.arity).to eq(2)
137
+ expect(cdro_rel.formals[0]).to match(/^r_[-0-9a-f]+$/)
138
+ expect(cdro_rel.formals[1]).to match(/^d_[-0-9a-f]+$/)
139
+ g_template = cdro_rel.expression
140
+ expect(g_template.relation).to be_kind_of(Rela::Fresh)
141
+ expect(g_template.actuals).to include('a')
142
+ end
143
+
144
+ # In Scheme:
145
+ # (defrel (cdro p d)
146
+ # (fresh (a)
147
+ # (== p (cons a d))))
148
+ # In Ruby, `p`is a standard Kernel method => replace it by `r`
149
+ def defrel_cdro
150
+ defrel('cdro', %w[r d], fresh('a', unify(r, cons(a, d))))
151
+ end
152
+
153
+ it 'passes unnesting process in frame 2:12' do
154
+ defrel_caro
155
+ defrel_cdro
156
+
157
+ # (run* r
158
+ # (fresh (v)
159
+ # (cdro '(acorn) v)
160
+ # (fresh (w)
161
+ # (cdro v w)
162
+ # (caro w r))) ;; r => (o)
163
+
164
+ result = run_star('r', fresh('v',
165
+ [cdro(acorn, v),
166
+ fresh('w',
167
+ [cdro(v, w),
168
+ caro(w, r)])]))
169
+ expect(result.car).to eq(:o)
170
+ end
171
+
172
+ it 'passes frame 2:15' do
173
+ defrel_caro
174
+ defrel_cdro
175
+
176
+ # (run* r
177
+ # (fresh (x y)
178
+ # (cdro '(grape raisin pear) x)
179
+ # (caro '((a) (b) (c)) y)
180
+ # (== (cons x y) r))) ;; r => (((raisin pear) a))
181
+
182
+ result = run_star('r', fresh(%w[x y],
183
+ [cdro(fruits, x),
184
+ caro(abc, y),
185
+ unify(cons(x, y), r)]))
186
+ expect(result.to_s).to eq('(((:raisin :pear) :a))')
187
+ end
188
+
189
+ it 'passes frame 2:16' do
190
+ defrel_cdro
191
+
192
+ # (run* q
193
+ # (cdro '(a c o r n) '(c o r n))) ;; => (_0)
194
+ result = run_star('r', cdro(acorn, corn))
195
+ expect(result.car).to eq(:_0)
196
+ end
197
+
198
+ it 'passes frame 2:17' do
199
+ defrel_cdro
200
+
201
+ # (run* x
202
+ # (cdro '(c o r n) '(,x r n))) ;; => (o)
203
+ result = run_star('x', cdro(corn, cons(x, cons(:r, cons(:n)))))
204
+ expect(result.car).to eq(:o)
205
+ end
206
+
207
+ it 'passes frame 2:18' do
208
+ defrel_caro
209
+ defrel_cdro
210
+
211
+ # (run* l
212
+ # (fresh (x)
213
+ # (cdro l '(c o r n))
214
+ # (caro l x)
215
+ # (== 'a x))) ;; l => ('(a c o r n))
216
+
217
+ result = run_star('l', fresh('x',
218
+ [cdro(l, corn),
219
+ caro(l, x),
220
+ unify(:a, x)]))
221
+ expect(result.to_s).to eq('((:a :c :o :r :n))')
222
+ end
223
+
224
+ it 'accepts conso definition inspired from frame 2:25' do
225
+ defrel_caro
226
+ defrel_cdro
227
+
228
+ # Reasoned S2, frame 2:25
229
+ # (defrel (conso a p d)
230
+ # (caro p a)
231
+ # (cdro p d))
232
+
233
+ # As 'p' has a special meaning in Ruby, the argument has been remaned to 'r'
234
+ conso_rel = defrel('conso', %w[a d r], [caro(r, a), cdro(r, d)])
235
+
236
+ expect(conso_rel).to be_kind_of(Rela::DefRelation)
237
+ expect(conso_rel.name).to eq('conso')
238
+ expect(conso_rel.arity).to eq(3)
239
+ expect(conso_rel.formals[0]).to match(/^a_[-0-9a-f]+$/)
240
+ expect(conso_rel.formals[1]).to match(/^d_[-0-9a-f]+$/)
241
+ expect(conso_rel.formals[2]).to match(/^r_[-0-9a-f]+$/)
242
+ g_template = conso_rel.expression
243
+ expect(g_template.relation).to be_kind_of(Rela::Conj2)
244
+ g1 = g_template.actuals[0]
245
+ expect(g1).to be_kind_of(Core::Goal)
246
+ expect(g1.relation.name).to eq('caro')
247
+ expect(g1.actuals[0].name).to match(/^r_/)
248
+ expect(g1.actuals[1].name).to match(/^a_/)
249
+
250
+ g2 = g_template.actuals[1]
251
+ expect(g2).to be_kind_of(Core::Goal)
252
+ expect(g2.relation.name).to eq('cdro')
253
+ expect(g2.actuals[0].name).to match(/^r_/)
254
+ expect(g2.actuals[1].name).to match(/^d_/)
255
+ end
256
+
257
+ # In Scheme:
258
+ # (defrel (conso a d p)
259
+ # (caro p a)
260
+ # (cdro p d))
261
+ # In Ruby, `p`is a standard Kernel method => replace it by `r`
262
+ def defrel_conso
263
+ defrel_caro
264
+ defrel_cdro
265
+
266
+ # Definition derived from frame 2:25
267
+ # defrel('conso', %w[a d r], [caro(r, a), cdro(r, d)])
268
+
269
+ # Definition derived from frame 2:26
270
+ defrel('conso', %w[a d r], [unify(cons(a, d), r)])
271
+ end
272
+
273
+ it 'passes frame 2:19' do
274
+ defrel_conso
275
+
276
+ # (run* l
277
+ # (conso '(a b c) '(d e) l)) ;; => (((a b c) d e))
278
+
279
+ result = run_star('l', conso(list(:a, :b, :c), list(:d, :e), l))
280
+ expect(result.to_s).to eq('(((:a :b :c) :d :e))')
281
+ end
282
+
283
+ it 'passes frame 2:20' do
284
+ defrel_conso
285
+
286
+ # (run* x
287
+ # (conso x '(a b c) '(d a b c))) ;; => (d)
288
+
289
+ result = run_star('x', conso(x, list(:a, :b, :c), list(:d, :a, :b, :c)))
290
+ expect(result.to_s).to eq('(:d)')
291
+ end
292
+
293
+ it 'passes frame 2:21' do
294
+ defrel_conso
295
+
296
+ # (run* r
297
+ # (fresh (x y z)
298
+ # (== '(e a d ,x) r)
299
+ # (conso y '(a ,z c) r))) ;; => ((e a d c)
300
+
301
+ expr = fresh(%w[x y z],
302
+ [unify(list(:e, :a, :d, x), r),
303
+ conso(y, list(:a, z, :c), r)])
304
+ result = run_star('r', expr)
305
+ expect(result.to_s).to eq('((:e :a :d :c))')
306
+ end
307
+
308
+ it 'passes frame 2:22' do
309
+ defrel_conso
310
+
311
+ # (run* x
312
+ # (conso x '(a ,x c) '(d a ,x c))) ;; => (d)
313
+
314
+ result = run_star('x', conso(x, list(:a, x, :c), list(:d, :a, x, :c)))
315
+ expect(result.to_s).to eq('(:d)')
316
+ end
317
+
318
+ it 'passes frame 2:23' do
319
+ defrel_conso
320
+
321
+ # (run* l
322
+ # (fresh (x)
323
+ # (== '(d a ,x c) l)
324
+ # (conso x '(a ,x c) l))) ;; => ((d a d c)
325
+
326
+ expr = fresh(%w[x],
327
+ [unify(list(:d, :a, x, :c), l),
328
+ conso(x, list(:a, x, :c), l)])
329
+ result = run_star('l', expr)
330
+ expect(result.to_s).to eq('((:d :a :d :c))')
331
+ end
332
+
333
+ it 'passes frame 2:24' do
334
+ defrel_conso
335
+
336
+ # (run* l
337
+ # (fresh (x)
338
+ # (conso x '(a ,x c) l)))
339
+ # (== '(d a ,x c) l) ;; => ((d a d c)
340
+
341
+ expr = fresh(%w[x],
342
+ [conso(x, list(:a, x, :c), l),
343
+ unify(list(:d, :a, x, :c), l)])
344
+ result = run_star('l', expr)
345
+ expect(result.to_s).to eq('((:d :a :d :c))')
346
+ end
347
+ end # context
348
+ end # describe
349
+ end # module
350
+ end # module
@@ -1,19 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative '../spec_helper' # Use the RSpec framework
4
- require_relative '../../lib/mini_kraken/core/goal'
5
- require_relative '../../lib/mini_kraken/core/conde'
6
- require_relative '../../lib/mini_kraken/core/conj2'
7
- require_relative '../../lib/mini_kraken/core/def_relation'
8
- require_relative '../../lib/mini_kraken/core/disj2'
9
- require_relative '../../lib/mini_kraken/core/equals'
10
- require_relative '../../lib/mini_kraken/core/fail'
11
- require_relative '../../lib/mini_kraken/core/formal_arg'
12
- require_relative '../../lib/mini_kraken/core/formal_ref'
13
- require_relative '../../lib/mini_kraken/core/goal_template'
14
- require_relative '../../lib/mini_kraken/core/succeed'
15
-
16
- require_relative '../support/factory_methods'
4
+
5
+ require_relative '../../lib/mini_kraken/core/all_core'
6
+ require_relative '../../lib/mini_kraken/rela/all_rela'
7
+ require_relative '../support/factory_atomic'
8
+ # require_relative '../support/factory_methods'
17
9
 
18
10
  # Load the class under test
19
11
  require_relative '../../lib/mini_kraken/glue/run_star_expression'
@@ -22,14 +14,19 @@ require_relative '../../lib/mini_kraken/glue/run_star_expression'
22
14
  module MiniKraken
23
15
  module Glue
24
16
  describe RunStarExpression do
25
- include FactoryMethods
17
+ include MiniKraken::FactoryAtomic # Use mix-in module
18
+
26
19
  let(:pea) { k_symbol(:pea) }
27
20
  let(:pod) { k_symbol(:pod) }
28
- let(:sample_goal) { equals_goal(pea, pod) }
21
+ let(:sample_goal) { unify_goal(pea, pod) }
29
22
  let(:fails) { Core::Goal.new(Core::Fail.instance, []) }
30
23
  let(:succeeds) { Core::Goal.new(Core::Succeed.instance, []) }
31
24
  subject { RunStarExpression.new('q', sample_goal) }
32
25
 
26
+ def unify_goal(arg1, arg2)
27
+ Core::Goal.new(Rela::Unify.instance, [arg1, arg2])
28
+ end
29
+
33
30
  context 'Initialization:' do
34
31
  it 'could be initialized with a name and a goal' do
35
32
  expect { RunStarExpression.new('q', sample_goal) }.not_to raise_error
@@ -39,17 +36,18 @@ module MiniKraken
39
36
  expect { RunStarExpression.new(%w[r x y], sample_goal) }.not_to raise_error
40
37
  end
41
38
 
42
- it 'could be initialized with multiple names and goals' do
43
- expect { RunStarExpression.new(%w[r x y], [succeeds, succeeds]) }.not_to raise_error
44
- end
39
+ # it 'could be initialized with multiple names and goals' do
40
+ # expect { RunStarExpression.new(%w[r x y], [succeeds, succeeds]) }.not_to raise_error
41
+ # end
45
42
 
46
43
  it 'should know its variables' do
47
- expect(subject.env.vars['q']).not_to be_nil
48
- expect(subject.env.vars.values[0].name).to eq('q')
44
+ definitions = subject.ctx.symbol_table.current_scope.defns
45
+ expect(definitions['q']).not_to be_nil
46
+ expect(definitions.values[0].name).to eq('q')
49
47
  end
50
48
 
51
49
  it 'should know its goal' do
52
- expect(subject.env.goal).to eq(sample_goal)
50
+ expect(subject.goal).to eq(sample_goal)
53
51
  end
54
52
  end # context
55
53
 
@@ -69,25 +67,37 @@ module MiniKraken
69
67
  let(:split) { k_symbol(:split) }
70
68
  let(:tea) { k_symbol(:tea) }
71
69
  let(:virgin) { k_symbol(:virgin) }
72
- let(:ref_q) { Core::VariableRef.new('q') }
73
- let(:ref_r) { Core::VariableRef.new('r') }
74
- let(:ref_x) { Core::VariableRef.new('x') }
75
- let(:ref_y) { Core::VariableRef.new('y') }
76
- let(:ref_z) { Core::VariableRef.new('z') }
77
- let(:ref_s) { Core::VariableRef.new('s') }
78
- let(:ref_t) { Core::VariableRef.new('t') }
79
- let(:ref_u) { Core::VariableRef.new('u') }
80
- let(:ref_z) { Core::VariableRef.new('z') }
70
+ let(:ref_q) { Core::LogVarRef.new('q') }
71
+ let(:ref_r) { Core::LogVarRef.new('r') }
72
+ let(:ref_x) { Core::LogVarRef.new('x') }
73
+ let(:ref_y) { Core::LogVarRef.new('y') }
74
+ let(:ref_z) { Core::LogVarRef.new('z') }
75
+ let(:ref_s) { Core::LogVarRef.new('s') }
76
+ let(:ref_t) { Core::LogVarRef.new('t') }
77
+ let(:ref_u) { Core::LogVarRef.new('u') }
78
+ let(:ref_z) { Core::LogVarRef.new('z') }
81
79
  let(:t_ref) { Core::FormalRef.new('t') }
82
- let(:equals_tea) { Core::GoalTemplate.new(Core::Equals.instance, [tea, t_ref]) }
83
- let(:equals_cup) { Core::GoalTemplate.new(Core::Equals.instance, [cup, t_ref]) }
84
- let(:g_template) { Core::GoalTemplate.new(Core::Disj2.instance, [equals_tea, equals_cup]) }
85
- let(:formal_t) { Core::FormalArg.new('t') }
86
80
 
87
- # Reasoned S2, frame 1:82
88
- # (defrel (teacupo t)
89
- # (disj2 (== 'tea t) (== 'cup t)))
90
- let(:teacupo_rel) { Core::DefRelation.new('teacupo', g_template, [formal_t]) }
81
+ # @return [Core::Goal]
82
+ def fresh(names, subgoal)
83
+ # puts "#{__callee__} #{names}"
84
+ if names.kind_of?(Array)
85
+ k_names = names.map { |nm| Atomic::KString.new(nm) }
86
+ else
87
+ k_names = Atomic::KString.new(names)
88
+ end
89
+ Core::Goal.new(Rela::Fresh.instance, [k_names, subgoal])
90
+ end
91
+
92
+ def conj2(term1, term2)
93
+ Core::Goal.new(Rela::Conj2.instance, [term1, term2])
94
+ end
95
+
96
+ def unify(term1, term2)
97
+ Core::Goal.new(Rela::Unify.instance, [term1, term2])
98
+ end
99
+
100
+
91
101
 
92
102
  it 'should return a null list with the fail goal' do
93
103
  # Reasoned S2, frame 1:7
@@ -98,904 +108,70 @@ module MiniKraken
98
108
  expect(instance.run).to be_null
99
109
  end
100
110
 
101
- it 'should return a null list when a goal fails' do
111
+ it 'should return a null list with a failing goal' do
102
112
  # Reasoned S2, frame 1:10
103
- # (run* q (== 'pea 'pod) ;; => ()
104
-
105
- expect(subject.run).to be_null
106
- expect(ref_q.fresh?(subject.env)).to be_truthy
107
- end
108
-
109
- it 'should unify the variable with the equals goal with symbol' do
110
- goal = equals_goal(ref_q, pea)
111
- instance = RunStarExpression.new('q', goal)
112
-
113
- # Reasoned S2, frame 1:11
114
- # (run* q (== q 'pea) ;; => (pea)
115
- expect(instance.run.car).to eq(pea)
116
- end
113
+ # (run* q (== 'pea 'pod)) ;; => ()
114
+ instance = RunStarExpression.new('q', unify(pea, pod))
117
115
 
118
- it 'should unify the righthand variable(s)' do
119
- goal = equals_goal(pea, ref_q)
120
- instance = RunStarExpression.new('q', goal)
121
-
122
- # Reasoned S2, frame 1:12
123
- # (run* q (== 'pea q) ;; => (pea)
124
- expect(instance.run.car).to eq(pea)
116
+ expect(instance.run).to be_null
125
117
  end
126
118
 
127
- it 'should return a null list with the succeed goal' do
128
- instance = RunStarExpression.new('q', succeeds)
129
- expect(instance.env.vars).to be_include('q')
130
-
131
- # (display (run* q succeed)) ;; => (_0)
132
- # Reasoned S2, frame 1:16
133
- result = instance.run
134
-
119
+ it 'should return a _0 with the succeed goal' do
135
120
  # Reasoned S2, frame 1:17
136
- expect(result.car).to eq(any_value(0))
137
- end
121
+ # (run* q #s) ;; => (_0)
122
+ success = Core::Goal.new(Core::Succeed.instance, [])
123
+ instance = RunStarExpression.new('q', success)
138
124
 
139
- it 'should keep variable fresh when no unification occurs (I)' do
140
- goal = equals_goal(pea, pea)
141
- instance = RunStarExpression.new('q', goal)
142
-
143
- # (display (run* q (== 'pea 'pea))) ;; => (_0)
144
- # Reasoned S2, frame 1:19
145
- result = instance.run
146
- expect(result.car).to eq(any_value(0))
125
+ expect(instance.run.to_s).to eq('(_0)')
147
126
  end
148
127
 
149
- it 'should keep variable fresh when no unification occurs (II)' do
150
- ref1_q = Core::VariableRef.new('q')
151
- ref2_q = Core::VariableRef.new('q')
152
- goal = equals_goal(ref1_q, ref2_q)
153
- instance = RunStarExpression.new('q', goal)
154
-
155
- # (display (run* q (== q q))) ;; => (_0)
156
- # Reasoned S2, frame 1:20
157
- result = instance.run
158
- expect(result.car).to eq(any_value(0))
159
- end
160
-
161
- it 'should accept the nesting of sub-environment' do
162
- goal = equals_goal(pea, ref_q)
163
- fresh_env = FreshEnv.new(['x'], goal)
164
- instance = RunStarExpression.new('q', fresh_env)
165
-
166
- # Reasoned S2, frame 1:21..23
167
- # (run* q (fresh (x) (== 'pea q))) ;; => (pea)
168
- result = instance.run
169
-
170
- # Reasoned S2, frame 1:40
171
- expect(ref_q.different_from?(ref_x, fresh_env)).to be_truthy
172
- expect(result.car).to eq(pea)
173
- end
174
-
175
- it 'should unify nested variables' do
176
- goal = equals_goal(pea, ref_x)
177
- fresh_env = FreshEnv.new(['x'], goal)
178
- instance = RunStarExpression.new('q', fresh_env)
179
-
180
- # Reasoned S2, frame 1:24
181
- # (run* q (fresh (x) (== 'pea x))) ;; => (_0)
182
- result = instance.run
183
- expect(result.car).to eq(any_value(0))
184
- end
185
-
186
- it 'should accept expression with variables' do
187
- goal = equals_goal(cons(ref_x), ref_q)
188
- fresh_env = FreshEnv.new(['x'], goal)
189
- instance = RunStarExpression.new('q', fresh_env)
190
-
191
- # Reasoned S2, frame 1:25
192
- # (run* q (fresh (x) (== (cons x '()) q))) ;; => ((_0))
193
- result = instance.run
194
- expect(result.car).to eq(cons(any_value(0)))
195
- end
196
-
197
- it 'should accept fused variables' do
198
- goal = equals_goal(ref_x, ref_q)
199
- fresh_env = FreshEnv.new(['x'], goal)
200
- instance = RunStarExpression.new('q', fresh_env)
201
-
202
- # Reasoned S2, frame 1:31
203
- # (run* q (fresh (x) (== x q))) ;; => (_0)
204
- result = instance.run
205
- expect(ref_q.fresh?(instance.env)).to be_truthy
206
- expect(ref_x.fresh?(fresh_env)).to be_truthy
207
-
208
- # q should be fused with x...
209
- expect(ref_q.fused_with?(ref_x, fresh_env)).to be_truthy
210
- expect(ref_q.names_fused(fresh_env)).to eq(['x'])
211
- expect(ref_x.names_fused(fresh_env)).to eq(['q'])
212
- expect(result.car).to eq(any_value(0))
213
- end
214
-
215
- it 'should cope with complex equality expressions' do
216
- expr1 = cons(cons(cons(pea)), pod)
217
- expr2 = cons(cons(cons(pea)), pod)
218
- goal = equals_goal(expr1, expr2)
219
- instance = RunStarExpression.new('q', goal)
220
-
221
- # Reasoned S2, frame 1:32
222
- # (run* q (== '(((pea)) pod) '(((pea)) pod))) ;; => (_0)
223
- result = instance.run
224
- expect(result.car).to eq(any_value(0))
225
- end
226
-
227
- it 'should unify complex equality expressions (I)' do
228
- expr1 = cons(cons(cons(pea)), pod)
229
- expr2 = cons(cons(cons(pea)), ref_q)
230
- goal = equals_goal(expr1, expr2)
231
- instance = RunStarExpression.new('q', goal)
232
-
233
- # Beware: quasiquoting
234
- # Reasoned S2, frame 1:33
235
- # (run* q (== '(((pea)) pod) `(((pea)) ,q))) ;; => ('pod)
236
- result = instance.run
237
- expect(result.car).to eq(pod)
238
- end
239
-
240
- it 'should unify complex equality expressions (II)' do
241
- expr1 = cons(cons(cons(ref_q)), pod)
242
- expr2 = cons(cons(cons(pea)), pod)
243
- goal = equals_goal(expr1, expr2)
244
- instance = RunStarExpression.new('q', goal)
245
-
246
- # Reasoned S2, frame 1:34
247
- # (run* q (== '(((,q)) pod) `(((pea)) pod))) ;; => ('pea)
248
- result = instance.run
249
- expect(result.car).to eq(pea)
250
- end
251
-
252
- it 'should unify complex equality expressions (III)' do
253
- expr1 = cons(cons(cons(ref_q)), pod)
254
- expr2 = cons(cons(cons(ref_x)), pod)
255
- goal = equals_goal(expr1, expr2)
256
- fresh_env = FreshEnv.new(['x'], goal)
257
- instance = RunStarExpression.new('q', fresh_env)
258
-
259
- # Reasoned S2, frame 1:35
260
- # (run* q (fresh (x) (== '(((,q)) pod) `(((,x)) pod)))) ;; => (_0)
261
- result = instance.run
262
- expect(result.car).to eq(any_value(0))
263
- end
264
-
265
- it 'should unify complex equality expressions (IV)' do
266
- # Reasoned S2, frame 1:36
267
- # (run* q (fresh (x) (== '(((,q)) (,x)) `(((,x)) pod)))) ;; => ('pod)
268
- expr1 = cons(cons(cons(ref_q)), ref_x)
269
- expr2 = cons(cons(cons(ref_x)), pod)
270
- goal = equals_goal(expr1, expr2)
271
- fresh_env = FreshEnv.new(['x'], goal)
272
- instance = RunStarExpression.new('q', fresh_env)
273
-
274
- result = instance.run
275
- expect(result.car).to eq(pod)
276
- end
277
-
278
- it 'should unify with repeated fresh variable' do
279
- # Reasoned S2, frame 1:37
280
- # (run* q (fresh (x) (== '( ,x ,x) q))) ;; => (_0 _0)
281
- expr1 = cons(ref_x, cons(ref_x))
282
- goal = equals_goal(expr1, ref_q)
283
- fresh_env = FreshEnv.new(['x'], goal)
284
- instance = RunStarExpression.new('q', fresh_env)
285
-
286
- result = instance.run
287
- expect(result.car).to eq(cons(any_value(0), cons(any_value(0))))
288
- end
289
-
290
- it 'should unify multiple times' do
291
- # Reasoned S2, frame 1:38
292
- # (run* q (fresh (x) (fresh (y) (== '( ,q ,y) '((,x ,y) ,x))))) ;; => (_0 _0)
293
- expr1 = cons(ref_q, cons(ref_y))
294
- expr2 = cons(cons(ref_x, cons(ref_y)), cons(ref_x))
295
- goal = equals_goal(expr1, expr2)
296
- fresh_env_y = FreshEnv.new(['y'], goal)
297
- fresh_env_x = FreshEnv.new(['x'], fresh_env_y)
298
- instance = RunStarExpression.new('q', fresh_env_x)
299
-
300
- result = instance.run
301
-
302
- # y should be fused with x...
303
- var_x = fresh_env_y.name2var('x')
304
- var_y = fresh_env_y.name2var('y')
305
- expect(var_x.i_name).to eq(var_y.i_name)
306
- expect(ref_y.fused_with?(ref_x, fresh_env_y)).to be_truthy
307
- expect(ref_x.names_fused(fresh_env_y)).to eq(['y'])
308
- expect(ref_y.names_fused(fresh_env_y)).to eq(['x'])
309
-
310
- # q should be bound to '(,x ,x)
311
- expect(result.car).to eq(cons(any_value(0), cons(any_value(0))))
312
- end
313
-
314
- it 'should support multiple fresh variables' do
315
- # Reasoned S2, frame 1:41
316
- # (run* q (fresh (x) (fresh (y) (== '( ,x ,y) q)))) ;; => (_0 _1)
317
- expr1 = cons(ref_x, cons(ref_y))
318
- goal = equals_goal(expr1, ref_q)
319
- fresh_env_y = FreshEnv.new(['y'], goal)
320
- fresh_env_x = FreshEnv.new(['x'], fresh_env_y)
321
- instance = RunStarExpression.new('q', fresh_env_x)
128
+ it 'should return a value with a succeeding goal and q bound' do
129
+ # Reasoned S2, frame 1:11
130
+ # (run* q (== q 'pea)) ;; => (pea)
131
+ instance = RunStarExpression.new('q', unify(ref_q, pea))
322
132
 
323
- result = instance.run
324
- # q should be bound to '(,x ,y)
325
- expect(result.car).to eq(cons(any_value(0), cons(any_value(1))))
133
+ expect(instance.run.to_s).to eq('(:pea)')
326
134
  end
327
135
 
328
- it 'should work with variable names' do
329
- # Reasoned S2, frame 1:42
330
- # (run* s (fresh (t) (fresh (u) (== '( ,t ,u) s)))) ;; => (_0 _1)
331
- expr1 = cons(ref_t, cons(ref_u))
332
- goal = equals_goal(expr1, ref_s)
333
- fresh_env_u = FreshEnv.new(['u'], goal)
334
- fresh_env_t = FreshEnv.new(['t'], fresh_env_u)
335
- instance = RunStarExpression.new('s', fresh_env_t)
136
+ it 'should return a _0 with a succeeding goal and q fresh' do
137
+ # Reasoned S2, frame 1:11
138
+ # (run* q (== q q)) ;; => (_0)
139
+ instance = RunStarExpression.new('q', unify(ref_q, ref_q))
336
140
 
337
- result = instance.run
338
- # s should be bound to '(,t ,u)
339
- expect(result.car).to eq(cons(any_value(0), cons(any_value(1))))
141
+ expect(instance.run.to_s).to eq('(_0)')
340
142
  end
341
143
 
342
- it 'should support repeated variables' do
343
- # Reasoned S2, frame 1:43
344
- # (run* q (fresh (x) (fresh (y) (== '( ,x ,y ,x) q)))) ;; => (_0 _1 _0)
345
- expr1 = cons(ref_x, cons(ref_y, cons(ref_x)))
346
- goal = equals_goal(expr1, ref_q)
347
- fresh_env_y = FreshEnv.new(['y'], goal)
348
- fresh_env_x = FreshEnv.new(['x'], fresh_env_y)
349
- instance = RunStarExpression.new('q', fresh_env_x)
144
+ it 'should support the fresh form to nest scopes' do
145
+ # Reasoned S2, frame 1:21
146
+ # (run* q (fresh (x) (== 'pea q))) ;; => (pea)
147
+ subgoal = unify(pea, ref_q)
148
+ instance = RunStarExpression.new('q', fresh('x', subgoal))
350
149
 
351
- result = instance.run
352
- # q should be bound to '(,x ,y, ,x)
353
- expect(result.car).to eq(cons(any_value(0), cons(any_value(1), cons(any_value(0)))))
150
+ expect(instance.run.to_s).to eq('(:pea)')
354
151
  end
355
152
 
356
153
  it 'should support conjunction of two succeed' do
357
- goal = conj2_goal(succeeds, succeeds)
358
- instance = RunStarExpression.new('q', goal)
359
-
360
154
  # Reasoned S2, frame 1:50
361
155
  # (run* q (conj2 succeed succeed)) ;; => (_0)
156
+ goal = conj2(succeeds, succeeds)
157
+ instance = RunStarExpression.new('q', goal)
158
+
362
159
  result = instance.run
363
- expect(result.car).to eq(any_value(0))
160
+ expect(result.to_s).to eq('(_0)')
364
161
  end
365
162
 
366
163
  it 'should support conjunction of one succeed and a successful goal' do
367
- subgoal = equals_goal(corn, ref_q)
368
- goal = conj2_goal(succeeds, subgoal)
369
- instance = RunStarExpression.new('q', goal)
370
-
371
164
  # Reasoned S2, frame 1:51
372
165
  # (run* q (conj2 succeed (== 'corn q)) ;; => ('corn)
373
- result = instance.run
374
- expect(result.car).to eq(corn)
375
- end
376
-
377
- it 'should support conjunction of one fail and a successful goal' do
378
- subgoal = equals_goal(corn, ref_q)
379
- goal = conj2_goal(fails, subgoal)
380
- instance = RunStarExpression.new('q', goal)
381
-
382
- # Reasoned S2, frame 1:52
383
- # (run* q (conj2 fail (== 'corn q)) ;; => ()
384
- expect(instance.run).to be_null
385
- end
386
-
387
- it 'should support conjunction of two contradictory goals' do
388
- subgoal1 = equals_goal(corn, ref_q)
389
- subgoal2 = equals_goal(meal, ref_q)
390
- goal = conj2_goal(subgoal1, subgoal2)
391
- instance = RunStarExpression.new('q', goal)
392
-
393
- # Reasoned S2, frame 1:53
394
- # (run* q (conj2 (== 'corn q)(== 'meal q)) ;; => ()
395
- expect(instance.run).to be_null
396
- end
397
-
398
- it 'should succeed the conjunction of two identical goals' do
399
- subgoal1 = equals_goal(corn, ref_q)
400
- subgoal2 = equals_goal(corn, ref_q)
401
- goal = conj2_goal(subgoal1, subgoal2)
402
- instance = RunStarExpression.new('q', goal)
403
-
404
- # Reasoned S2, frame 1:54
405
- # (run* q (conj2 (== 'corn q)(== 'corn q)) ;; => ('corn)
406
- result = instance.run
407
- expect(result.car).to eq(corn)
408
- end
409
-
410
- it 'should not yield solution when both disjunction arguments fail' do
411
- goal = disj2_goal(fails, fails)
412
- instance = RunStarExpression.new('q', goal)
413
-
414
- # Reasoned S2, frame 1:55
415
- # (run* q (disj2 fail fail)) ;; => ()
416
- expect(instance.run).to be_null
417
- end
418
-
419
- it 'should yield solution when first argument succeed' do
420
- subgoal = Core::Goal.new(Core::Equals.instance, [olive, ref_q])
421
- goal = disj2_goal(subgoal, fails)
422
- instance = RunStarExpression.new('q', goal)
423
-
424
- # Reasoned S2, frame 1:56
425
- # (run* q (disj2 (== 'olive q) fail)) ;; => ('olive)
426
- result = instance.run
427
- expect(result.car).to eq(olive)
428
- end
429
-
430
- it 'should yield solution when second argument succeed' do
431
- subgoal = Core::Goal.new(Core::Equals.instance, [oil, ref_q])
432
- goal = disj2_goal(fails, subgoal)
433
- instance = RunStarExpression.new('q', goal)
434
-
435
- # Reasoned S2, frame 1:57
436
- # (run* q (disj2 fail (== 'oil q))) ;; => (oil)
437
- result = instance.run
438
- expect(result.car).to eq(oil)
439
- end
440
-
441
- it 'should yield solutions when both arguments succeed' do
442
- subgoal1 = Core::Goal.new(Core::Equals.instance, [olive, ref_q])
443
- subgoal2 = Core::Goal.new(Core::Equals.instance, [oil, ref_q])
444
- goal = disj2_goal(subgoal1, subgoal2)
445
- instance = RunStarExpression.new('q', goal)
446
-
447
- # Reasoned S2, frame 1:58
448
- # (run* q (disj2 (== 'olive q) (== 'oil q))) ;; => (olive oil)
449
- result = instance.run
450
- expect(result.car).to eq(olive)
451
- expect(result.cdr.car).to eq(oil)
452
- end
453
-
454
- it 'should support the nesting of variables and disjunction' do
455
- # Reasoned S2, frame 1:59
456
- # (run* q (fresh (x) (fresh (y) (disj2 (== '( ,x ,y ) q) (== '( ,x ,y ) q)))))
457
- # ;; => ((_0 _1) (_0 _1))
458
- expr1 = cons(ref_x, cons(ref_y))
459
- subgoal1 = equals_goal(expr1, ref_q)
460
- expr2 = cons(ref_y, cons(ref_x))
461
- subgoal2 = equals_goal(expr2, ref_q)
462
- goal = disj2_goal(subgoal1, subgoal2)
463
- fresh_env_y = FreshEnv.new(['y'], goal)
464
- fresh_env_x = FreshEnv.new(['x'], fresh_env_y)
465
- instance = RunStarExpression.new('q', fresh_env_x)
466
-
467
- result = instance.run
468
- # q should be bound to '(,x ,y), then to '(,y ,x)
469
- expect(result.car).to eq(cons(any_value(0), cons(any_value(1))))
470
- expect(result.cdr.car).to eq(cons(any_value(0), cons(any_value(1))))
471
- end
472
-
473
- it 'should accept nesting of disj2 and conj2 (I)' do
474
- conj_subgoal = Core::Goal.new(Core::Equals.instance, [olive, ref_x])
475
- conjunction = conj2_goal(conj_subgoal, fails)
476
- subgoal = Core::Goal.new(Core::Equals.instance, [oil, ref_x])
477
- goal = disj2_goal(conjunction, subgoal)
478
- instance = RunStarExpression.new('x', goal)
479
-
480
- # Reasoned S2, frame 1:62
481
- # (run* x (disj2
482
- # (conj2 (== 'olive x) fail)
483
- # (== 'oil x))) ;; => (oil)
484
- result = instance.run
485
- expect(result.car).to eq(oil)
486
- end
487
-
488
- it 'should accept nesting of disj2 and conj2 (II)' do
489
- conj_subgoal = Core::Goal.new(Core::Equals.instance, [olive, ref_x])
490
- conjunction = conj2_goal(conj_subgoal, succeeds)
491
- subgoal = Core::Goal.new(Core::Equals.instance, [oil, ref_x])
492
- goal = disj2_goal(conjunction, subgoal)
493
- instance = RunStarExpression.new('x', goal)
494
-
495
- # Reasoned S2, frame 1:63
496
- # (run* x (disj2
497
- # (conj2 (== 'olive x) succeed)
498
- # ('oil x))) ;; => (olive oil)
499
- result = instance.run
500
- expect(result.car).to eq(olive)
501
- expect(result.cdr.car).to eq(oil)
502
- end
503
-
504
- it 'should accept nesting of disj2 and conj2 (III)' do
505
- conj_subgoal = Core::Goal.new(Core::Equals.instance, [olive, ref_x])
506
- conjunction = conj2_goal(conj_subgoal, succeeds)
507
- subgoal = Core::Goal.new(Core::Equals.instance, [oil, ref_x])
508
- goal = disj2_goal(subgoal, conjunction)
509
- instance = RunStarExpression.new('x', goal)
510
-
511
- # Reasoned S2, frame 1:64
512
- # (run* x (disj2
513
- # (== 'oil x)
514
- # (conj2 (== 'olive x) succeed))) ;; => (oil olive)
515
- result = instance.run
516
- expect(result.car).to eq(oil)
517
- expect(result.cdr.car).to eq(olive)
518
- end
519
-
520
- it 'should accept nesting of disj2 and conj2 (IV)' do
521
- oil_goal = Core::Goal.new(Core::Equals.instance, [oil, ref_x])
522
- disja = disj2_goal(succeeds, oil_goal)
523
- olive_goal = Core::Goal.new(Core::Equals.instance, [olive, ref_x])
524
- disjb = disj2_goal(olive_goal, disja)
525
- virgin_goal = Core::Goal.new(Core::Equals.instance, [virgin, ref_x])
526
- conjunction = conj2_goal(virgin_goal, fails)
527
- goal = disj2_goal(conjunction, disjb)
528
- instance = RunStarExpression.new('x', goal)
529
-
530
- # Reasoned S2, frame 1:65
531
- # (run* x (disj2
532
- # (conj2(== 'virgin x) fail)
533
- # (disj2
534
- # (== 'olive x)
535
- # (dis2
536
- # succeed
537
- # (== 'oil x))))) ;; => (olive _0 oil)
538
- result = instance.run
539
- expect(result.car).to eq(olive)
540
- expect(result.cdr.car).to eq(any_value(0))
541
- expect(result.cdr.cdr.car).to eq(oil)
542
- end
543
-
544
- it 'should accept nesting fresh, disj2 and conj2 expressions (I)' do
545
- subgoal1 = equals_goal(split, ref_x)
546
- expr1 = equals_goal(pea, ref_y)
547
- expr2 = equals_goal(cons(ref_x, cons(ref_y)), ref_r)
548
- subgoal2 = conj2_goal(expr1, expr2)
549
- goal = conj2_goal(subgoal1, subgoal2)
550
- fresh_env_y = FreshEnv.new(['y'], goal)
551
- fresh_env_x = FreshEnv.new(['x'], fresh_env_y)
552
- instance = RunStarExpression.new('r', fresh_env_x)
553
-
554
- # Reasoned S2, frame 1:67
555
- # (run* r
556
- # (fresh x
557
- # (fresh y
558
- # (conj2
559
- # (== 'split x)
560
- # (conj2
561
- # (== 'pea y)
562
- # (== '(,x ,y) r)))))) ;; => ((split pea))
563
- result = instance.run
564
- expect(result.car.car).to eq(split)
565
- expect(result.car.cdr.car).to eq(pea)
566
- end
567
-
568
- it 'should accept nesting fresh, disj2 and conj2 expressions (II)' do
569
- expr1 = equals_goal(split, ref_x)
570
- expr2 = equals_goal(pea, ref_y)
571
- subgoal1 = conj2_goal(expr1, expr2)
572
- subgoal2 = equals_goal(cons(ref_x, cons(ref_y)), ref_r)
573
- goal = conj2_goal(subgoal1, subgoal2)
574
- fresh_env_y = FreshEnv.new(['y'], goal)
575
- fresh_env_x = FreshEnv.new(['x'], fresh_env_y)
576
- instance = RunStarExpression.new('r', fresh_env_x)
577
-
578
- # Reasoned S2, frame 1:68
579
- # (run* r
580
- # (fresh x
581
- # (fresh y
582
- # (conj2
583
- # (conj2
584
- # (== 'split x)
585
- # (== 'pea y)
586
- # (== '(,x ,y) r)))))) ;; => ((split pea))
587
- result = instance.run
588
- expect(result.car.car).to eq(split)
589
- expect(result.car.cdr.car).to eq(pea)
590
- end
591
-
592
- it 'should accept fresh with multiple variables' do
593
- expr1 = equals_goal(split, ref_x)
594
- expr2 = equals_goal(pea, ref_y)
595
- subgoal1 = conj2_goal(expr1, expr2)
596
- subgoal2 = equals_goal(cons(ref_x, cons(ref_y)), ref_r)
597
- goal = conj2_goal(subgoal1, subgoal2)
598
- fresh_env = FreshEnv.new(%w[x y], goal)
599
- instance = RunStarExpression.new('r', fresh_env)
600
-
601
- # Reasoned S2, frame 1:70
602
- # (run* r
603
- # (fresh (x y)
604
- # (conj2
605
- # (conj2
606
- # (== 'split x)
607
- # (== 'pea y)
608
- # (== '(,x ,y) r))))) ;; => ((split pea))
609
- result = instance.run
610
- expect(result.car.car).to eq(split)
611
- expect(result.car.cdr.car).to eq(pea)
612
- end
613
-
614
- it 'should accept multiple variables' do
615
- expr1 = equals_goal(split, ref_x)
616
- expr2 = equals_goal(pea, ref_y)
617
- subgoal1 = conj2_goal(expr1, expr2)
618
- subgoal2 = equals_goal(cons(ref_x, cons(ref_y)), ref_r)
619
- goal = conj2_goal(subgoal1, subgoal2)
620
- instance = RunStarExpression.new(%w[r x y], goal)
621
-
622
- # Reasoned S2, frame 1:72
623
- # (run* (r x y)
624
- # (conj2
625
- # (conj2
626
- # (== 'split x)
627
- # (== 'pea y))
628
- # (== '(,x ,y) r))) ;; => (((split pea) split pea))
629
- # o
630
- # / \
631
- # o nil
632
- # / \
633
- # / \
634
- # / \
635
- # / \
636
- # / \
637
- # o o
638
- # / \ / \
639
- # split o split o
640
- # / \ / \
641
- # pea nil pea nil
642
- result = instance.run
643
- expect(result.car.car.car).to eq(split)
644
- expect(result.car.car.cdr.car).to eq(pea)
645
- expect(result.car.car.cdr.cdr).to be_nil
646
- expect(result.car.cdr.car).to eq(split)
647
- expect(result.car.cdr.cdr.car).to eq(pea)
648
- expect(result.car.cdr.cdr.cdr).to be_nil
649
- end
650
-
651
- it 'should allow simplification of expressions' do
652
- expr1 = equals_goal(split, ref_x)
653
- expr2 = equals_goal(pea, ref_y)
654
- goal = conj2_goal(expr1, expr2)
655
- instance = RunStarExpression.new(%w[x y], goal)
656
-
657
- # Reasoned S2, frame 1:75
658
- # (run* (x y)
659
- # (conj2
660
- # (== 'split x)
661
- # (== 'pea y))) ;; => ((split pea))
662
- result = instance.run
663
- expect(result.car.car).to eq(split)
664
- expect(result.car.cdr.car).to eq(pea)
665
- end
166
+ subgoal = unify(corn, ref_q)
167
+ instance = RunStarExpression.new('q', conj2(succeeds, subgoal))
666
168
 
667
- it 'should allow simplication of expressions' do
668
- expr1 = equals_goal(split, ref_x)
669
- expr2 = equals_goal(pea, ref_y)
670
- subgoal1 = conj2_goal(expr1, expr2)
671
- expr3 = equals_goal(red, ref_x)
672
- expr4 = equals_goal(bean, ref_y)
673
- subgoal2 = conj2_goal(expr3, expr4)
674
- goal = disj2_goal(subgoal1, subgoal2)
675
- instance = RunStarExpression.new(%w[x y], goal)
676
-
677
- # Reasoned S2, frame 1:76
678
- # (run* (x y)
679
- # (disj2
680
- # (conj2 (== 'split x) (== 'pea y))
681
- # (conj2 (== 'red x) (== 'bean y)))) ;; => ((split pea)(red bean))
682
169
  result = instance.run
683
- expect(result.car.car).to eq(split)
684
- expect(result.car.cdr.car).to eq(pea)
685
- expect(result.cdr.car.car).to eq(red)
686
- expect(result.cdr.car.cdr.car).to eq(bean)
170
+ expect(result.to_s).to eq('(:corn)')
687
171
  end
688
172
 
689
- it 'should allow nesting a disjunction inside of conjunction' do
690
- expr1 = equals_goal(split, ref_x)
691
- expr2 = equals_goal(red, ref_x)
692
- subgoal1 = disj2_goal(expr1, expr2)
693
- subgoal2 = equals_goal(ref_x, ref_y)
694
- goal = conj2_goal(subgoal1, subgoal2)
695
- instance = RunStarExpression.new(%w[x y], goal)
696
-
697
- # (display (run* (x y)
698
- # (conj2
699
- # (disj2
700
- # (== 'split x)
701
- # (== 'red x))
702
- # (== x y)))) ;; => ((split split) (red red))
703
- result = instance.run
704
- expect(result.car.car).to eq(split)
705
- expect(result.car.cdr.car).to eq(split)
706
- expect(result.cdr.car.cdr.car).to eq(red)
707
- end
708
-
709
- it 'should accept fresh with multiple variables' do
710
- expr1 = equals_goal(split, ref_x)
711
- expr2 = equals_goal(pea, ref_y)
712
- subgoal1 = conj2_goal(expr1, expr2)
713
- expr3 = equals_goal(red, ref_x)
714
- expr4 = equals_goal(bean, ref_y)
715
- subgoal2 = conj2_goal(expr3, expr4)
716
- subgoal3 = disj2_goal(subgoal1, subgoal2)
717
- subgoal4 = equals_goal(cons(ref_x, cons(ref_y, cons(soup))), ref_r)
718
- goal = conj2_goal(subgoal3, subgoal4)
719
- fresh_env = FreshEnv.new(%w[x y], goal)
720
- instance = RunStarExpression.new('r', fresh_env)
721
-
722
- # Reasoned S2, frame 1:77
723
- # (run* r
724
- # (fresh (x y)
725
- # (conj2
726
- # (disj2
727
- # (conj2 (== 'split x) (== 'pea y))
728
- # (conj2 (== 'red x) (== 'bean y)))
729
- # (== '(,x ,y soup) r)))) ;; => ((split pea soup) (red bean soup))
730
- result = instance.run
731
- expect(result.car.car).to eq(split)
732
- expect(result.car.cdr.car).to eq(pea)
733
- expect(result.car.cdr.cdr.car).to eq(soup)
734
- expect(result.cdr.car.car).to eq(red)
735
- expect(result.cdr.car.cdr.car).to eq(bean)
736
- expect(result.cdr.car.cdr.cdr.car).to eq(soup)
737
- end
738
-
739
- it 'should allow fresh with multiple goals' do
740
- expr1 = equals_goal(split, ref_x)
741
- expr2 = equals_goal(pea, ref_y)
742
- subgoal1 = conj2_goal(expr1, expr2)
743
- expr3 = equals_goal(red, ref_x)
744
- expr4 = equals_goal(bean, ref_y)
745
- subgoal2 = conj2_goal(expr3, expr4)
746
- goal1 = disj2_goal(subgoal1, subgoal2)
747
- goal2 = equals_goal(cons(ref_x, cons(ref_y, cons(soup))), ref_r)
748
- fresh_env = FreshEnv.new(%w[x y], [goal1, goal2])
749
- instance = RunStarExpression.new('r', fresh_env)
750
-
751
- # Reasoned S2, frame 1:78
752
- # (run* r
753
- # (fresh (x y)
754
- # (disj2
755
- # (conj2 (== 'split x) (== 'pea y))
756
- # (conj2 (== 'red x) (== 'bean y)))
757
- # (== '(,x ,y soup) r))) ;; => ((split pea soup) (red bean soup))
758
- result = instance.run
759
- expect(result.car.car).to eq(split)
760
- expect(result.car.cdr.car).to eq(pea)
761
- expect(result.car.cdr.cdr.car).to eq(soup)
762
- expect(result.cdr.car.car).to eq(red)
763
- expect(result.cdr.car.cdr.car).to eq(bean)
764
- expect(result.cdr.car.cdr.cdr.car).to eq(soup)
765
- end
766
-
767
- it 'should allow run* with multiple goals' do
768
- expr1 = equals_goal(split, ref_x)
769
- expr2 = equals_goal(pea, ref_y)
770
- subgoal1 = conj2_goal(expr1, expr2)
771
- expr3 = equals_goal(red, ref_x)
772
- expr4 = equals_goal(bean, ref_y)
773
- subgoal2 = conj2_goal(expr3, expr4)
774
- goal1 = disj2_goal(subgoal1, subgoal2)
775
- goal2 = equals_goal(soup, ref_z)
776
- instance = RunStarExpression.new(%w[x y z], [goal1, goal2])
777
-
778
- # Reasoned S2, frame 1:80
779
- # (run* (x y z)
780
- # (disj2
781
- # (conj2 (== 'split x) (== 'pea y))
782
- # (conj2 (== 'red x) (== 'bean y)))
783
- # (== 'soup z)) ;; => ((split pea soup) (red bean soup))
784
- result = instance.run
785
- expect(result.car.car).to eq(split)
786
- expect(result.car.cdr.car).to eq(pea)
787
- expect(result.car.cdr.cdr.car).to eq(soup)
788
- expect(result.cdr.car.car).to eq(red)
789
- expect(result.cdr.car.cdr.car).to eq(bean)
790
- expect(result.cdr.car.cdr.cdr.car).to eq(soup)
791
- end
792
-
793
- it 'should allow simplified expressions with multiple goals' do
794
- expr1 = equals_goal(split, ref_x)
795
- expr2 = equals_goal(pea, ref_y)
796
- instance = RunStarExpression.new(%w[x y], [expr1, expr2])
797
-
798
- # Reasoned S2, frame 1:81
799
- # (run* (x y)
800
- # (== 'split x)
801
- # (== 'pea y)) ;; => ((split pea))
802
- result = instance.run
803
- expect(result.car.car).to eq(split)
804
- expect(result.car.cdr.car).to eq(pea)
805
- end
806
-
807
- it 'should solve expression with defrel' do
808
- teacupo_goal = Core::Goal.new(teacupo_rel, [ref_x])
809
-
810
- # Reasoned S2, frame 1:83
811
- # (run* x
812
- # (teacupo x)) ;; => ((tea cup))
813
- instance = RunStarExpression.new('x', teacupo_goal)
814
-
815
- result = instance.run
816
- expect(result.car).to eq(tea)
817
- expect(result.cdr.car).to eq(cup)
818
- end
819
-
820
- it 'should solve expression with defrel and booleans' do
821
- teacupo_goal = Core::Goal.new(teacupo_rel, [ref_x])
822
- expr2 = equals_goal(k_true, ref_y)
823
- subgoal1 = conj2_goal(teacupo_goal, expr2)
824
- expr3 = equals_goal(k_false, ref_x)
825
- expr4 = equals_goal(k_true, ref_y)
826
- subgoal2 = conj2_goal(expr3, expr4)
827
- goal = disj2_goal(subgoal1, subgoal2)
828
- # Reasoned S2, frame 1:84
829
- # (run* (x y)
830
- # (disj2
831
- # (conj2 (teacupo x) (== #t y))
832
- # (conj2 (== #f x) (== #t y))) ;; => ((#f #t)(tea #t) (cup #t))
833
- instance = RunStarExpression.new(%w[x y], goal)
834
-
835
- result = instance.run
836
- # Order of solutions differs from RS book
837
- expect(result.car).to eq(cons(tea, cons(true)))
838
- expect(result.cdr.car).to eq(cons(cup, cons(true)))
839
- expect(result.cdr.cdr.car).to eq(cons(false, cons(true)))
840
- end
841
-
842
- it 'should solve expression with two variable and defrel' do
843
- teacupo_goal1 = Core::Goal.new(teacupo_rel, [ref_x])
844
- teacupo_goal2 = Core::Goal.new(teacupo_rel, [ref_y])
845
-
846
- # Reasoned S2, frame 1:85
847
- # (run* (x y)
848
- # (teacupo x)
849
- # (teacupo y)) ;; => ((tea tea)(tea cup)(cup tea)(cup c))
850
- instance = RunStarExpression.new(%w[x y], [teacupo_goal1, teacupo_goal2])
851
-
852
- result = instance.run
853
- expect(result.car).to eq(cons(tea, cons(tea)))
854
- expect(result.cdr.car).to eq(cons(tea, cons(cup)))
855
- expect(result.cdr.cdr.car).to eq(cons(cup, cons(tea)))
856
- expect(result.cdr.cdr.cdr.car).to eq(cons(cup, cons(cup)))
857
- end
858
-
859
- it 'should solve expression with two variable and defrel' do
860
- teacupo_goal1 = Core::Goal.new(teacupo_rel, [ref_x])
861
- teacupo_goal2 = Core::Goal.new(teacupo_rel, [ref_x])
862
-
863
- # Reasoned S2, frame 1:86
864
- # (run* (x y)
865
- # (teacupo x)
866
- # (teacupo x)) ;; => ((tea _0)(cup _0))
867
- instance = RunStarExpression.new(%w[x y], [teacupo_goal1, teacupo_goal2])
868
-
869
- result = instance.run
870
- expect(result.car).to eq(cons(tea, cons(any_value(0))))
871
- expect(result.cdr.car).to eq(cons(cup, cons(any_value(0))))
872
- end
873
-
874
- it 'should solve expression with defrel and booleans' do
875
- teacupo_goal1 = Core::Goal.new(teacupo_rel, [ref_x])
876
- teacupo_goal2 = Core::Goal.new(teacupo_rel, [ref_x])
877
- subgoal1 = conj2_goal(teacupo_goal1, teacupo_goal2)
878
- expr3 = equals_goal(k_false, ref_x)
879
- expr4 = Core::Goal.new(teacupo_rel, [ref_y])
880
- subgoal2 = conj2_goal(expr3, expr4)
881
- goal = disj2_goal(subgoal1, subgoal2)
882
- # Reasoned S2, frame 1:87
883
- # (run* (x y)
884
- # (disj2
885
- # (conj2 (teacupo x) (teacupo x))
886
- # (conj2 (== #f x) (teacupo y)))) ;; => ((#f tea)(#f cup)(tea _0)(cup _0))
887
- instance = RunStarExpression.new(%w[x y], goal)
888
-
889
- result = instance.run
890
- # Order of solutions differs from RS book
891
- expect(result.car).to eq(cons(tea, cons(any_value(0))))
892
- expect(result.cdr.car).to eq(cons(cup, cons(any_value(0))))
893
- expect(result.cdr.cdr.car).to eq(cons(false, cons(tea)))
894
- expect(result.cdr.cdr.cdr.car).to eq(cons(false, cons(cup)))
895
- end
896
-
897
- it 'should allow conde in the goal expression' do
898
- teacupo_goal1 = Core::Goal.new(teacupo_rel, [ref_x])
899
- teacupo_goal2 = Core::Goal.new(teacupo_rel, [ref_x])
900
- expr3 = equals_goal(k_false, ref_x)
901
- expr4 = Core::Goal.new(teacupo_rel, [ref_y])
902
- goal = conde_goal([[teacupo_goal1, teacupo_goal2], [expr3, expr4]])
903
- # Reasoned S2, frame 1:88
904
- # (run* (x y)
905
- # (conde
906
- # ((teacupo x) (teacupo x))
907
- # ((== #f x) (teacupo y)))) ;; => ((#f tea)(#f cup)(tea _0)(cup _0))
908
- instance = RunStarExpression.new(%w[x y], goal)
909
-
910
- result = instance.run
911
- expect(result.car).to eq(cons(tea, cons(any_value(0))))
912
- expect(result.cdr.car).to eq(cons(cup, cons(any_value(0))))
913
- expect(result.cdr.cdr.car).to eq(cons(false, cons(tea)))
914
- expect(result.cdr.cdr.cdr.car).to eq(cons(false, cons(cup)))
915
- end
916
-
917
- it 'should allow simplication of expressions (conde version)' do
918
- expr1 = equals_goal(split, ref_x)
919
- expr2 = equals_goal(pea, ref_y)
920
- combo1 = [expr1, expr2]
921
- expr3 = equals_goal(red, ref_x)
922
- expr4 = equals_goal(bean, ref_y)
923
- combo2 = [expr3, expr4]
924
- goal = conde_goal([combo1, combo2])
925
- instance = RunStarExpression.new(%w[x y], goal)
926
-
927
- # Reasoned S2, frame 1:88 (second part, a rewrite of 1:76)
928
- # (run* (x y)
929
- # (conde
930
- # ((== 'split x) (== 'pea y))
931
- # ((== 'red x) (== 'bean y)))) ;; => ((split pea)(red bean))
932
- result = instance.run
933
- expect(result.car.car).to eq(split)
934
- expect(result.car.cdr.car).to eq(pea)
935
- expect(result.cdr.car.car).to eq(red)
936
- expect(result.cdr.car.cdr.car).to eq(bean)
937
- end
938
-
939
- it 'should accept nesting of disj2 and conj2 (conde version)' do
940
- equals_olive = Core::Goal.new(Core::Equals.instance, [olive, ref_x])
941
- combo = [equals_olive, fails]
942
- equals_oil = Core::Goal.new(Core::Equals.instance, [oil, ref_x])
943
- goal = conde_goal([combo, equals_oil])
944
- instance = RunStarExpression.new('x', goal)
945
-
946
- # Reasoned S2, frame 1:89 (rewrite of 1:62)
947
- # (run* x
948
- # (conde
949
- # ((== 'olive x) fail)
950
- # ('oil x))) ;; => (oil)
951
- result = instance.run
952
- expect(result.car).to eq(oil)
953
- end
954
-
955
- it 'should accept nesting of conde inside a fresh context' do
956
- equals_lentil = Core::Goal.new(Core::Equals.instance, [lentil, ref_z])
957
- fresh_env = FreshEnv.new(['z'], equals_lentil)
958
- equals_xy = Core::Goal.new(Core::Equals.instance, [ref_x, ref_y])
959
- goal = conde_goal([fresh_env, equals_xy])
960
- instance = RunStarExpression.new(%w[x y], goal)
961
- fresh_env.parent = instance.env
962
-
963
- # Reasoned S2, frame 1:90
964
- # (run* (x y)
965
- # (conde
966
- # ((fresh (z)
967
- # (== 'lentil z)))
968
- # ((== x y)))) ;; => ((_0 _1)(_0 _0))
969
- result = instance.run
970
- expect(result.car).to eq(cons(any_value(0), cons(any_value(1))))
971
- expect(result.cdr.car).to eq(cons(any_value(0), cons(any_value(0))))
972
- end
973
-
974
- it 'accepts conde with more than two condition lines' do
975
- expr1 = equals_goal(split, ref_x)
976
- expr2 = equals_goal(pea, ref_y)
977
- combo1 = [expr1, expr2]
978
- expr3 = equals_goal(red, ref_x)
979
- expr4 = equals_goal(bean, ref_y)
980
- combo2 = [expr3, expr4]
981
- expr5 = equals_goal(green, ref_x)
982
- expr6 = equals_goal(lentil, ref_y)
983
- combo3 = [expr5, expr6]
984
- goal = conde_goal([combo1, combo2, combo3])
985
- instance = RunStarExpression.new(%w[x y], goal)
986
-
987
- # Reasoned S2, frame 1:91
988
- # (run* (x y)
989
- # (conde
990
- # ((== 'split x) (== 'pea y))
991
- # ((== 'red x) (== 'bean y))
992
- # ((== 'green x) (== 'lentil y))))
993
- # ;; => ((split pea)(red bean)(green lentil))
994
- result = instance.run
995
- expect(result.car).to eq(cons(split, cons(pea)))
996
- expect(result.cdr.car).to eq(cons(red, cons(bean)))
997
- expect(result.cdr.cdr.car).to eq(cons(green, cons(lentil)))
998
- end
173
+ # TODO: add two solutions case
174
+ # TODO: add fused variables
999
175
  end # context
1000
176
  end # describe
1001
177
  end # module