mini_kraken 0.1.05 → 0.1.10

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 (39) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +5 -1
  3. data/CHANGELOG.md +56 -0
  4. data/README.md +5 -3
  5. data/lib/mini_kraken/core/composite_goal.rb +46 -0
  6. data/lib/mini_kraken/core/composite_term.rb +2 -0
  7. data/lib/mini_kraken/core/conj2.rb +79 -0
  8. data/lib/mini_kraken/core/cons_cell.rb +49 -43
  9. data/lib/mini_kraken/core/designation.rb +55 -0
  10. data/lib/mini_kraken/core/disj2.rb +71 -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 -132
  14. data/lib/mini_kraken/core/fail.rb +18 -14
  15. data/lib/mini_kraken/core/goal.rb +4 -2
  16. data/lib/mini_kraken/core/goal_arg.rb +10 -0
  17. data/lib/mini_kraken/core/goal_relation.rb +28 -0
  18. data/lib/mini_kraken/core/outcome.rb +40 -24
  19. data/lib/mini_kraken/core/succeed.rb +17 -13
  20. data/lib/mini_kraken/core/term.rb +5 -2
  21. data/lib/mini_kraken/core/variable.rb +3 -27
  22. data/lib/mini_kraken/core/variable_ref.rb +3 -28
  23. data/lib/mini_kraken/core/vocabulary.rb +39 -19
  24. data/lib/mini_kraken/glue/fresh_env.rb +45 -3
  25. data/lib/mini_kraken/glue/run_star_expression.rb +44 -19
  26. data/lib/mini_kraken/version.rb +1 -1
  27. data/spec/core/conj2_spec.rb +114 -0
  28. data/spec/core/cons_cell_spec.rb +8 -0
  29. data/spec/core/disj2_spec.rb +99 -0
  30. data/spec/core/duck_fiber_spec.rb +12 -1
  31. data/spec/core/equals_spec.rb +3 -3
  32. data/spec/core/outcome_spec.rb +48 -0
  33. data/spec/core/vocabulary_spec.rb +11 -5
  34. data/spec/glue/fresh_env_spec.rb +27 -1
  35. data/spec/glue/run_star_expression_spec.rb +478 -53
  36. data/spec/mini_kraken_spec.rb +2 -0
  37. data/spec/spec_helper.rb +0 -1
  38. data/spec/support/factory_methods.rb +16 -0
  39. metadata +14 -2
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MiniKraken
4
- VERSION = '0.1.05'
4
+ VERSION = '0.1.10'
5
5
  end
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../spec_helper' # Use the RSpec framework
4
+ require_relative '../../lib/mini_kraken/core/k_symbol'
5
+ require_relative '../../lib/mini_kraken/core/fail'
6
+ require_relative '../../lib/mini_kraken/core/succeed'
7
+ require_relative '../../lib/mini_kraken/core/equals'
8
+ require_relative '../../lib/mini_kraken/core/environment'
9
+ require_relative '../../lib/mini_kraken/core/variable'
10
+ require_relative '../../lib/mini_kraken/core/variable_ref'
11
+
12
+ # Load the class under test
13
+ require_relative '../../lib/mini_kraken/core/conj2'
14
+
15
+
16
+ module MiniKraken
17
+ module Core
18
+ describe Conj2 do
19
+ subject { Conj2.instance }
20
+
21
+ context 'Initialization:' do
22
+ it 'should be initialized without argument' do
23
+ expect { Conj2.instance }.not_to raise_error
24
+ end
25
+
26
+ it 'should know its name' do
27
+ expect(subject.name).to eq('conj2')
28
+ end
29
+ end # context
30
+
31
+ context 'Provided services:' do
32
+ let(:env) { Environment.new }
33
+ let(:pea) { KSymbol.new(:pea) }
34
+ let(:corn) { KSymbol.new(:corn) }
35
+ let(:meal) { KSymbol.new(:meal) }
36
+ let(:fails) { Goal.new(Fail.instance, []) }
37
+ let(:succeeds) { Goal.new(Succeed.instance, []) }
38
+ let(:var_q) { Variable.new('q') }
39
+ let(:ref_q) { VariableRef.new('q') }
40
+
41
+ it 'should complain when one of its argument is not a goal' do
42
+ err = StandardError
43
+ expect { subject.solver_for([succeeds, pea], env) }.to raise_error(err)
44
+ expect { subject.solver_for([pea, succeeds], env) }.to raise_error(err)
45
+ end
46
+
47
+ it 'should yield one failure if one of the goal is fail' do
48
+ # Fail as first argument
49
+ solver = subject.solver_for([fails, succeeds], env)
50
+ expect(solver.resume).not_to be_successful
51
+ expect(solver.resume).to be_nil
52
+
53
+ # Fail as second argument
54
+ solver = subject.solver_for([succeeds, fails], env)
55
+ expect(solver.resume).not_to be_successful
56
+ expect(solver.resume).to be_nil
57
+ end
58
+
59
+ it 'yield success if both arguments are succeed goals' do
60
+ # Covers frame 1-50
61
+ solver = subject.solver_for([succeeds, succeeds], env)
62
+ outcome = solver.resume
63
+ expect(outcome).to be_successful
64
+ expect(outcome.associations).to be_empty
65
+ expect(solver.resume).to be_nil
66
+ end
67
+
68
+ it 'should yield success and set associations' do
69
+ # Covers frame 1-51
70
+ env.add_var(var_q)
71
+ sub_goal = Goal.new(Equals.instance, [corn, ref_q])
72
+ solver = subject.solver_for([succeeds, sub_goal], env)
73
+ outcome = solver.resume
74
+ expect(outcome).to be_successful
75
+ expect(outcome.associations).not_to be_empty
76
+ expect(outcome.associations['q'].first.value).to eq(corn)
77
+ end
78
+
79
+ it 'should yield fails and set no associations' do
80
+ # Covers frame 1-52
81
+ env.add_var(var_q)
82
+ sub_goal = Goal.new(Equals.instance, [corn, ref_q])
83
+ solver = subject.solver_for([fails, sub_goal], env)
84
+ outcome = solver.resume
85
+ expect(outcome).not_to be_successful
86
+ expect(outcome.associations).to be_empty
87
+ end
88
+
89
+ it 'should yield fails when sub-goals are incompatible' do
90
+ # Covers frame 1-53
91
+ env.add_var(var_q)
92
+ sub_goal1 = Goal.new(Equals.instance, [corn, ref_q])
93
+ sub_goal2 = Goal.new(Equals.instance, [meal, ref_q])
94
+ solver = subject.solver_for([sub_goal1, sub_goal2], env)
95
+ outcome = solver.resume
96
+ expect(outcome).not_to be_successful
97
+ expect(outcome.associations).to be_empty
98
+ end
99
+
100
+ it 'should yield success when sub-goals are same and successful' do
101
+ # Covers frame 1-54
102
+ env.add_var(var_q)
103
+ sub_goal1 = Goal.new(Equals.instance, [corn, ref_q])
104
+ sub_goal2 = Goal.new(Equals.instance, [corn, ref_q])
105
+ solver = subject.solver_for([sub_goal1, sub_goal2], env)
106
+ outcome = solver.resume
107
+ expect(outcome).to be_successful
108
+ expect(outcome.associations).not_to be_empty
109
+ expect(outcome.associations['q'].first.value).to eq(corn)
110
+ end
111
+ end # context
112
+ end # describe
113
+ end # module
114
+ end # module
@@ -58,6 +58,14 @@ module MiniKraken
58
58
  different = ConsCell.new(pea)
59
59
  expect(subject.eql?(different)).to be_falsey
60
60
  end
61
+
62
+ it 'should append another cons cell' do
63
+ instance = ConsCell.new(pea)
64
+ trail = ConsCell.new(pod)
65
+ instance.append(trail)
66
+ expect(instance.car).to eq(pea)
67
+ expect(instance.cdr).to eq(trail)
68
+ end
61
69
  end # context
62
70
  end # describe
63
71
  end # module
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../spec_helper' # Use the RSpec framework
4
+ require_relative '../../lib/mini_kraken/core/k_symbol'
5
+ require_relative '../../lib/mini_kraken/core/fail'
6
+ require_relative '../../lib/mini_kraken/core/succeed'
7
+ require_relative '../../lib/mini_kraken/core/equals'
8
+ require_relative '../../lib/mini_kraken/core/environment'
9
+ require_relative '../../lib/mini_kraken/core/variable'
10
+ require_relative '../../lib/mini_kraken/core/variable_ref'
11
+
12
+ # Load the class under test
13
+ require_relative '../../lib/mini_kraken/core/disj2'
14
+
15
+ module MiniKraken
16
+ module Core
17
+ describe Disj2 do
18
+ subject { Disj2.instance }
19
+
20
+ context 'Initialization:' do
21
+ it 'should be initialized without argument' do
22
+ expect { Disj2.instance }.not_to raise_error
23
+ end
24
+
25
+ it 'should know its name' do
26
+ expect(subject.name).to eq('disj2')
27
+ end
28
+ end # context
29
+
30
+ context 'Provided services:' do
31
+ let(:corn) { KSymbol.new(:corn) }
32
+ let(:meal) { KSymbol.new(:meal) }
33
+ let(:oil) { KSymbol.new(:oil) }
34
+ let(:olive) { KSymbol.new(:olive) }
35
+ let(:pea) { KSymbol.new(:pea) }
36
+ let(:fails) { Goal.new(Fail.instance, []) }
37
+ let(:succeeds) { Goal.new(Succeed.instance, []) }
38
+ let(:var_q) { Variable.new('q') }
39
+ let(:ref_q) { VariableRef.new('q') }
40
+ let(:env) do
41
+ e = Environment.new
42
+ e.add_var(var_q)
43
+ e
44
+ end
45
+
46
+ it 'should complain when one of its argument is not a goal' do
47
+ err = StandardError
48
+ expect { subject.solver_for([succeeds, pea], env) }.to raise_error(err)
49
+ expect { subject.solver_for([pea, succeeds], env) }.to raise_error(err)
50
+ end
51
+
52
+ it 'should fails if both arguments fail' do
53
+ # Covers frame 1:55
54
+ solver = subject.solver_for([fails, fails], env)
55
+ expect(solver.resume).not_to be_successful
56
+ expect(solver.resume).to be_nil
57
+ end
58
+
59
+ it 'yield success if first argument succeeds' do
60
+ # Covers frame 1:56
61
+ subgoal = Goal.new(Equals.instance, [olive, ref_q])
62
+ solver = subject.solver_for([subgoal, fails], env)
63
+ outcome = solver.resume
64
+ expect(outcome).to be_successful
65
+ expect(outcome.associations['q'].first.value).to eq(olive)
66
+ expect(solver.resume).to be_nil
67
+ end
68
+
69
+ it 'yield success if second argument succeeds' do
70
+ # Covers frame 1:57
71
+ subgoal = Goal.new(Equals.instance, [oil, ref_q])
72
+ solver = subject.solver_for([fails, subgoal], env)
73
+ outcome = solver.resume
74
+ expect(outcome).to be_successful
75
+ expect(outcome.associations['q'].first.value).to eq(oil)
76
+ expect(solver.resume).to be_nil
77
+ end
78
+
79
+ it 'yield two solutions if both arguments succeed' do
80
+ # Covers frame 1:58
81
+ subgoal1 = Goal.new(Equals.instance, [olive, ref_q])
82
+ subgoal2 = Goal.new(Equals.instance, [oil, ref_q])
83
+ solver = subject.solver_for([subgoal1, subgoal2], env)
84
+
85
+ # First solution
86
+ outcome1 = solver.resume
87
+ expect(outcome1).to be_successful
88
+ expect(outcome1.associations['q'].first.value).to eq(olive)
89
+
90
+ # Second solution
91
+ outcome2 = solver.resume
92
+ expect(outcome2).to be_successful
93
+ expect(outcome2.associations['q'].first.value).to eq(oil)
94
+ expect(solver.resume).to be_nil
95
+ end
96
+ end # context
97
+ end # describe
98
+ end # module
99
+ end # module
@@ -40,12 +40,23 @@ module MiniKraken
40
40
  succeeding = DuckFiber.new(:success)
41
41
  outcome = nil
42
42
  expect { outcome = succeeding.resume }.not_to raise_error
43
- expect(outcome).to eq(BasicSuccess)
43
+ expect(outcome).to be_successful
44
+ expect(outcome.parent).to be_nil
44
45
 
45
46
  # Only one result should be yielded
46
47
  expect(succeeding.resume).to be_nil
47
48
  end
48
49
 
50
+ it 'should yield a distinct success object' do
51
+ instance1 = DuckFiber.new(:success)
52
+ outcome1 = instance1.resume
53
+
54
+ instance2 = DuckFiber.new(:success)
55
+ outcome2 = instance1.resume
56
+
57
+ expect(outcome1).not_to be_equal(outcome2)
58
+ end
59
+
49
60
  it 'should behave like a Fiber yielding a custom outcome' do
50
61
  tailored = DuckFiber.new(:custom) { Outcome.new(:"#s", parent) }
51
62
  outcome = nil
@@ -275,9 +275,9 @@ module MiniKraken
275
275
 
276
276
  it 'should unify composite terms with variables' do
277
277
  # Reasoned S2, frame 1:36
278
- # (run* q (fresh (x) (== '(((,q)) (,x)) `(((,x)) pod)))) ;; => ('pod)
279
- expr1 = cons(cons(ref_q), ref_x)
280
- expr2 = cons(cons(ref_x), pod)
278
+ # (run* q (fresh (x) (== '(((,q)) ,x) `(((,x)) pod)))) ;; => ('pod)
279
+ expr1 = cons(cons(cons(ref_q)), ref_x)
280
+ expr2 = cons(cons(cons(ref_x)), pod)
281
281
 
282
282
  result = solve_for(expr1, expr2)
283
283
  # require 'debug'
@@ -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
 
@@ -2,6 +2,8 @@
2
2
 
3
3
  require_relative '../spec_helper' # Use the RSpec framework
4
4
  require_relative '../../lib/mini_kraken/core/goal'
5
+ require_relative '../../lib/mini_kraken/core/conj2'
6
+ require_relative '../../lib/mini_kraken/core/disj2'
5
7
  require_relative '../../lib/mini_kraken/core/equals'
6
8
  require_relative '../../lib/mini_kraken/core/fail'
7
9
  require_relative '../../lib/mini_kraken/core/succeed'
@@ -19,16 +21,26 @@ module MiniKraken
19
21
  let(:pea) { k_symbol(:pea) }
20
22
  let(:pod) { k_symbol(:pod) }
21
23
  let(:sample_goal) { equals_goal(pea, pod) }
24
+ let(:fails) { Core::Goal.new(Core::Fail.instance, []) }
25
+ let(:succeeds) { Core::Goal.new(Core::Succeed.instance, []) }
22
26
  subject { RunStarExpression.new('q', sample_goal) }
23
27
 
24
28
  context 'Initialization:' do
25
- it 'should be initialized with a name and a goal' do
29
+ it 'could be initialized with a name and a goal' do
26
30
  expect { RunStarExpression.new('q', sample_goal) }.not_to raise_error
27
31
  end
28
32
 
33
+ it 'could be initialized with multiple names and a goal' do
34
+ expect { RunStarExpression.new(%w[r x y], sample_goal) }.not_to raise_error
35
+ end
36
+
37
+ it 'could be initialized with multiple names and goals' do
38
+ expect { RunStarExpression.new(%w[r x y], [succeeds, succeeds]) }.not_to raise_error
39
+ end
40
+
29
41
  it 'should know its variables' do
30
42
  expect(subject.env.vars['q']).not_to be_nil
31
- expect(subject.var.name).to eq('q')
43
+ expect(subject.env.vars.values[0].name).to eq('q')
32
44
  end
33
45
 
34
46
  it 'should know its goal' do
@@ -37,12 +49,23 @@ module MiniKraken
37
49
  end # context
38
50
 
39
51
  context 'Provided services:' do
52
+ let(:bean) { k_symbol(:bean) }
53
+ let(:corn) { k_symbol(:corn) }
54
+ let(:meal) { k_symbol(:meal) }
55
+ let(:oil) { k_symbol(:oil) }
56
+ let(:olive) { k_symbol(:olive) }
57
+ let(:red) { k_symbol(:red) }
58
+ let(:soup) { k_symbol(:soup) }
59
+ let(:split) { k_symbol(:split) }
60
+ let(:virgin) { k_symbol(:virgin) }
40
61
  let(:ref_q) { Core::VariableRef.new('q') }
62
+ let(:ref_r) { Core::VariableRef.new('r') }
41
63
  let(:ref_x) { Core::VariableRef.new('x') }
42
64
  let(:ref_y) { Core::VariableRef.new('y') }
43
65
  let(:ref_s) { Core::VariableRef.new('s') }
44
66
  let(:ref_t) { Core::VariableRef.new('t') }
45
67
  let(:ref_u) { Core::VariableRef.new('u') }
68
+ let(:ref_z) { Core::VariableRef.new('z') }
46
69
 
47
70
  it 'should return a null list with the fail goal' do
48
71
  # Reasoned S2, frame 1:7
@@ -51,7 +74,6 @@ module MiniKraken
51
74
  instance = RunStarExpression.new('q', failing)
52
75
 
53
76
  expect(instance.run).to be_null
54
- expect(ref_q.fresh?(instance.env)).to be_truthy
55
77
  end
56
78
 
57
79
  it 'should return a null list when a goal fails' do
@@ -69,7 +91,6 @@ module MiniKraken
69
91
  # Reasoned S2, frame 1:11
70
92
  # (run* q (== q 'pea) ;; => (pea)
71
93
  expect(instance.run.car).to eq(pea)
72
- expect(ref_q.fresh?(instance.env)).to be_falsey
73
94
  end
74
95
 
75
96
  it 'should unify the righthand variable(s)' do
@@ -79,19 +100,14 @@ module MiniKraken
79
100
  # Reasoned S2, frame 1:12
80
101
  # (run* q (== 'pea q) ;; => (pea)
81
102
  expect(instance.run.car).to eq(pea)
82
-
83
- # Reasoned S2, frame 1:15
84
- expect(ref_q.fresh?(instance.env)).to be_falsey
85
103
  end
86
104
 
87
105
  it 'should return a null list with the succeed goal' do
88
- success = Core::Goal.new(Core::Succeed.instance, [])
89
- instance = RunStarExpression.new('q', success)
106
+ instance = RunStarExpression.new('q', succeeds)
90
107
 
91
108
  # (display (run* q succeed)) ;; => (_0)
92
109
  # Reasoned S2, frame 1:16
93
110
  result = instance.run
94
- expect(ref_q.fresh?(instance.env)).to be_truthy
95
111
 
96
112
  # Reasoned S2, frame 1:17
97
113
  expect(result.car).to eq(any_value(0))
@@ -104,7 +120,6 @@ module MiniKraken
104
120
  # (display (run* q (== 'pea 'pea))) ;; => (_0)
105
121
  # Reasoned S2, frame 1:19
106
122
  result = instance.run
107
- expect(ref_q.fresh?(instance.env)).to be_truthy
108
123
  expect(result.car).to eq(any_value(0))
109
124
  end
110
125
 
@@ -117,7 +132,6 @@ module MiniKraken
117
132
  # (display (run* q (== q q))) ;; => (_0)
118
133
  # Reasoned S2, frame 1:20
119
134
  result = instance.run
120
- expect(ref_q.fresh?(instance.env)).to be_truthy
121
135
  expect(result.car).to eq(any_value(0))
122
136
  end
123
137
 
@@ -129,8 +143,6 @@ module MiniKraken
129
143
  # Reasoned S2, frame 1:21..23
130
144
  # (run* q (fresh (x) (== 'pea q))) ;; => (pea)
131
145
  result = instance.run
132
- expect(ref_q.fresh?(instance.env)).to be_falsey
133
- expect(ref_x.fresh?(fresh_env)).to be_truthy
134
146
 
135
147
  # Reasoned S2, frame 1:40
136
148
  expect(ref_q.different_from?(ref_x, fresh_env)).to be_truthy
@@ -145,8 +157,6 @@ module MiniKraken
145
157
  # Reasoned S2, frame 1:24
146
158
  # (run* q (fresh (x) (== 'pea x))) ;; => (_0)
147
159
  result = instance.run
148
- expect(ref_q.fresh?(instance.env)).to be_truthy
149
- expect(ref_x.fresh?(fresh_env)).to be_falsey
150
160
  expect(result.car).to eq(any_value(0))
151
161
  end
152
162
 
@@ -158,8 +168,6 @@ module MiniKraken
158
168
  # Reasoned S2, frame 1:25
159
169
  # (run* q (fresh (x) (== (cons x '()) q))) ;; => ((_0))
160
170
  result = instance.run
161
- expect(ref_q.fresh?(instance.env)).to be_truthy
162
- expect(ref_x.fresh?(fresh_env)).to be_truthy
163
171
  expect(result.car).to eq(cons(any_value(0)))
164
172
  end
165
173
 
@@ -190,7 +198,6 @@ module MiniKraken
190
198
  # Reasoned S2, frame 1:32
191
199
  # (run* q (== '(((pea)) pod) '(((pea)) pod))) ;; => (_0)
192
200
  result = instance.run
193
- expect(ref_q.fresh?(instance.env)).to be_truthy
194
201
  expect(result.car).to eq(any_value(0))
195
202
  end
196
203
 
@@ -204,7 +211,6 @@ module MiniKraken
204
211
  # Reasoned S2, frame 1:33
205
212
  # (run* q (== '(((pea)) pod) `(((pea)) ,q))) ;; => ('pod)
206
213
  result = instance.run
207
- expect(ref_q.fresh?(instance.env)).to be_falsey
208
214
  expect(result.car).to eq(pod)
209
215
  end
210
216
 
@@ -217,7 +223,6 @@ module MiniKraken
217
223
  # Reasoned S2, frame 1:34
218
224
  # (run* q (== '(((,q)) pod) `(((pea)) pod))) ;; => ('pod)
219
225
  result = instance.run
220
- expect(ref_q.fresh?(instance.env)).to be_falsey
221
226
  expect(result.car).to eq(pea)
222
227
  end
223
228
 
@@ -231,8 +236,6 @@ module MiniKraken
231
236
  # Reasoned S2, frame 1:35
232
237
  # (run* q (fresh (x) (== '(((,q)) pod) `(((,x)) pod)))) ;; => (_0)
233
238
  result = instance.run
234
- expect(ref_q.fresh?(instance.env)).to be_truthy
235
- expect(ref_x.fresh?(fresh_env)).to be_truthy
236
239
  expect(result.car).to eq(any_value(0))
237
240
  end
238
241
 
@@ -246,11 +249,6 @@ module MiniKraken
246
249
  instance = RunStarExpression.new('q', fresh_env)
247
250
 
248
251
  result = instance.run
249
-
250
- # Does propagate work correctly?
251
- expect(ref_q.fresh?(instance.env)).to be_truthy # x isn't defined here
252
- expect(ref_q.fresh?(fresh_env)).to be_falsey
253
- expect(ref_x.fresh?(fresh_env)).to be_falsey
254
252
  expect(result.car).to eq(pod)
255
253
  end
256
254
 
@@ -263,9 +261,6 @@ module MiniKraken
263
261
  instance = RunStarExpression.new('q', fresh_env)
264
262
 
265
263
  result = instance.run
266
- expect(ref_q.fresh?(instance.env)).to be_truthy # x isn't defined here
267
- expect(ref_q.fresh?(fresh_env)).to be_truthy
268
- expect(ref_x.fresh?(fresh_env)).to be_truthy
269
264
  expect(result.car).to eq(cons(any_value(0), cons(any_value(0))))
270
265
  end
271
266
 
@@ -280,12 +275,6 @@ module MiniKraken
280
275
  instance = RunStarExpression.new('q', fresh_env_x)
281
276
 
282
277
  result = instance.run
283
- expect(ref_q.fresh?(fresh_env_y)).to be_truthy
284
- expect(ref_q.bound?(fresh_env_y)).to be_truthy
285
- expect(ref_x.fresh?(fresh_env_y)).to be_truthy
286
- expect(ref_x.bound?(fresh_env_y)).to be_falsy
287
- expect(ref_y.fresh?(fresh_env_y)).to be_truthy
288
- expect(ref_y.bound?(fresh_env_y)).to be_falsy
289
278
 
290
279
  # y should be fused with x...
291
280
  var_x = fresh_env_y.name2var('x')
@@ -309,13 +298,7 @@ module MiniKraken
309
298
  instance = RunStarExpression.new('q', fresh_env_x)
310
299
 
311
300
  result = instance.run
312
- expect(ref_q.fresh?(fresh_env_y)).to be_truthy
313
301
  # q should be bound to '(,x ,y)
314
- expect(ref_q.bound?(fresh_env_y)).to be_truthy
315
- expect(ref_x.fresh?(fresh_env_y)).to be_truthy
316
- expect(ref_x.bound?(fresh_env_y)).to be_falsey
317
- expect(ref_y.fresh?(fresh_env_y)).to be_truthy
318
- expect(ref_y.bound?(fresh_env_y)).to be_falsey
319
302
  expect(result.car).to eq(cons(any_value(0), cons(any_value(1))))
320
303
  end
321
304
 
@@ -329,13 +312,7 @@ module MiniKraken
329
312
  instance = RunStarExpression.new('s', fresh_env_t)
330
313
 
331
314
  result = instance.run
332
- expect(ref_s.fresh?(fresh_env_u)).to be_truthy
333
315
  # s should be bound to '(,t ,u)
334
- expect(ref_s.bound?(fresh_env_u)).to be_truthy
335
- expect(ref_t.fresh?(fresh_env_u)).to be_truthy
336
- expect(ref_t.bound?(fresh_env_u)).to be_falsey
337
- expect(ref_u.fresh?(fresh_env_u)).to be_truthy
338
- expect(ref_u.bound?(fresh_env_u)).to be_falsey
339
316
  expect(result.car).to eq(cons(any_value(0), cons(any_value(1))))
340
317
  end
341
318
 
@@ -349,13 +326,461 @@ module MiniKraken
349
326
  instance = RunStarExpression.new('q', fresh_env_x)
350
327
 
351
328
  result = instance.run
352
- expect(ref_q.fresh?(fresh_env_y)).to be_truthy
353
329
  # q should be bound to '(,x ,y, ,x)
354
- expect(ref_q.bound?(fresh_env_y)).to be_truthy
355
- expect(ref_x.fresh?(fresh_env_y)).to be_truthy
356
- expect(ref_y.fresh?(fresh_env_y)).to be_truthy
357
330
  expect(result.car).to eq(cons(any_value(0), cons(any_value(1), cons(any_value(0)))))
358
331
  end
332
+
333
+ it 'should support conjunction of two succeed' do
334
+ goal = conj2_goal(succeeds, succeeds)
335
+ instance = RunStarExpression.new('q', goal)
336
+
337
+ # Reasoned S2, frame 1:50
338
+ # (run* q (conj2 succeed succeed)) ;; => (_0)
339
+ result = instance.run
340
+ expect(result.car).to eq(any_value(0))
341
+ end
342
+
343
+ # TODO: fix erratic RSpec failure
344
+ it 'should support conjunction of one succeed and a successful goal' do
345
+ subgoal = equals_goal(corn, ref_q)
346
+ goal = conj2_goal(succeeds, subgoal)
347
+ instance = RunStarExpression.new('q', goal)
348
+
349
+ # Reasoned S2, frame 1:51
350
+ # (run* q (conj2 succeed (== 'corn q)) ;; => ('corn)
351
+ result = instance.run
352
+ expect(result.car).to eq(corn)
353
+ end
354
+
355
+ it 'should support conjunction of one fail and a successful goal' do
356
+ subgoal = equals_goal(corn, ref_q)
357
+ goal = conj2_goal(fails, subgoal)
358
+ instance = RunStarExpression.new('q', goal)
359
+
360
+ # Reasoned S2, frame 1:52
361
+ # (run* q (conj2 fail (== 'corn q)) ;; => ()
362
+ expect(instance.run).to be_null
363
+ end
364
+
365
+ it 'should support conjunction of two contradictory goals' do
366
+ subgoal1 = equals_goal(corn, ref_q)
367
+ subgoal2 = equals_goal(meal, ref_q)
368
+ goal = conj2_goal(subgoal1, subgoal2)
369
+ instance = RunStarExpression.new('q', goal)
370
+
371
+ # Reasoned S2, frame 1:53
372
+ # (run* q (conj2 (== 'corn q)(== 'meal q)) ;; => ()
373
+ expect(instance.run).to be_null
374
+ end
375
+
376
+ it 'should succeed the conjunction of two identical goals' do
377
+ subgoal1 = equals_goal(corn, ref_q)
378
+ subgoal2 = equals_goal(corn, ref_q)
379
+ goal = conj2_goal(subgoal1, subgoal2)
380
+ instance = RunStarExpression.new('q', goal)
381
+
382
+ # Reasoned S2, frame 1:54
383
+ # (run* q (conj2 (== 'corn q)(== 'corn q)) ;; => ('corn)
384
+ result = instance.run
385
+ expect(result.car).to eq(corn)
386
+ end
387
+
388
+ it 'should not yield solution when both disjunction arguments fail' do
389
+ goal = disj2_goal(fails, fails)
390
+ instance = RunStarExpression.new('q', goal)
391
+
392
+ # Reasoned S2, frame 1:55
393
+ # (run* q (disj2 fail fail)) ;; => ()
394
+ expect(instance.run).to be_null
395
+ end
396
+
397
+ it 'should yield solution when first argument succeed' do
398
+ subgoal = Core::Goal.new(Core::Equals.instance, [olive, ref_q])
399
+ goal = disj2_goal(subgoal, fails)
400
+ instance = RunStarExpression.new('q', goal)
401
+
402
+ # Reasoned S2, frame 1:56
403
+ # (run* q (disj2 (== 'olive q) fail)) ;; => ('olive)
404
+ result = instance.run
405
+ expect(result.car).to eq(olive)
406
+ end
407
+
408
+ it 'should yield solution when second argument succeed' do
409
+ subgoal = Core::Goal.new(Core::Equals.instance, [oil, ref_q])
410
+ goal = disj2_goal(fails, subgoal)
411
+ instance = RunStarExpression.new('q', goal)
412
+
413
+ # Reasoned S2, frame 1:57
414
+ # (run* q (disj2 fail (== 'oil q))) ;; => (oil)
415
+ result = instance.run
416
+ expect(result.car).to eq(oil)
417
+ end
418
+
419
+ it 'should yield solutions when both arguments succeed' do
420
+ subgoal1 = Core::Goal.new(Core::Equals.instance, [olive, ref_q])
421
+ subgoal2 = Core::Goal.new(Core::Equals.instance, [oil, ref_q])
422
+ goal = disj2_goal(subgoal1, subgoal2)
423
+ instance = RunStarExpression.new('q', goal)
424
+
425
+ # Reasoned S2, frame 1:58
426
+ # (run* q (disj2 (== 'olive q) (== 'oil q))) ;; => (olive oil)
427
+ result = instance.run
428
+ expect(result.car).to eq(olive)
429
+ expect(result.cdr.car).to eq(oil)
430
+ end
431
+
432
+ it 'should support the nesting of variables and disjunction' do
433
+ # Reasoned S2, frame 1:59
434
+ # (run* q (fresh (x) (fresh (y) (disj2 (== '( ,x ,y ) q) (== '( ,x ,y ) q)))))
435
+ # ;; => ((_0 _1) (_0 _1))
436
+ expr1 = cons(ref_x, cons(ref_y))
437
+ subgoal1 = equals_goal(expr1, ref_q)
438
+ expr2 = cons(ref_y, cons(ref_x))
439
+ subgoal2 = equals_goal(expr2, ref_q)
440
+ goal = disj2_goal(subgoal1, subgoal2)
441
+ fresh_env_y = FreshEnv.new(['y'], goal)
442
+ fresh_env_x = FreshEnv.new(['x'], fresh_env_y)
443
+ instance = RunStarExpression.new('q', fresh_env_x)
444
+
445
+ result = instance.run
446
+ # q should be bound to '(,x ,y), then to '(,y ,x)
447
+ expect(result.car).to eq(cons(any_value(0), cons(any_value(1))))
448
+ expect(result.cdr.car).to eq(cons(any_value(0), cons(any_value(1))))
449
+ end
450
+
451
+ it 'should accept nesting of disj2 and conj2 (I)' do
452
+ conj_subgoal = Core::Goal.new(Core::Equals.instance, [olive, ref_x])
453
+ conjunction = conj2_goal(conj_subgoal, fails)
454
+ subgoal = Core::Goal.new(Core::Equals.instance, [oil, ref_x])
455
+ goal = disj2_goal(conjunction, subgoal)
456
+ instance = RunStarExpression.new('x', goal)
457
+
458
+ # Reasoned S2, frame 1:62
459
+ # (run* x (disj2
460
+ # (conj2 (== 'olive x) fail)
461
+ # ('oil x))) ;; => (oil)
462
+ result = instance.run
463
+ expect(result.car).to eq(oil)
464
+ end
465
+
466
+ it 'should accept nesting of disj2 and conj2 (II)' do
467
+ conj_subgoal = Core::Goal.new(Core::Equals.instance, [olive, ref_x])
468
+ conjunction = conj2_goal(conj_subgoal, succeeds)
469
+ subgoal = Core::Goal.new(Core::Equals.instance, [oil, ref_x])
470
+ goal = disj2_goal(conjunction, subgoal)
471
+ instance = RunStarExpression.new('x', goal)
472
+
473
+ # Reasoned S2, frame 1:63
474
+ # (run* x (disj2
475
+ # (conj2 (== 'olive x) succeed)
476
+ # ('oil x))) ;; => (olive oil)
477
+ result = instance.run
478
+ expect(result.car).to eq(olive)
479
+ expect(result.cdr.car).to eq(oil)
480
+ end
481
+
482
+ it 'should accept nesting of disj2 and conj2 (III)' do
483
+ conj_subgoal = Core::Goal.new(Core::Equals.instance, [olive, ref_x])
484
+ conjunction = conj2_goal(conj_subgoal, succeeds)
485
+ subgoal = Core::Goal.new(Core::Equals.instance, [oil, ref_x])
486
+ goal = disj2_goal(subgoal, conjunction)
487
+ instance = RunStarExpression.new('x', goal)
488
+
489
+ # Reasoned S2, frame 1:64
490
+ # (run* x (disj2
491
+ # ('oil x)
492
+ # (conj2 (== 'olive x) succeed))) ;; => (oil olive)
493
+ result = instance.run
494
+ expect(result.car).to eq(oil)
495
+ expect(result.cdr.car).to eq(olive)
496
+ end
497
+
498
+ it 'should accept nesting of disj2 and conj2 (IV)' do
499
+ oil_goal = Core::Goal.new(Core::Equals.instance, [oil, ref_x])
500
+ disja = disj2_goal(succeeds, oil_goal)
501
+ olive_goal = Core::Goal.new(Core::Equals.instance, [olive, ref_x])
502
+ disjb = disj2_goal(olive_goal, disja)
503
+ virgin_goal = Core::Goal.new(Core::Equals.instance, [virgin, ref_x])
504
+ conjunction = conj2_goal(virgin_goal, fails)
505
+ goal = disj2_goal(conjunction, disjb)
506
+ instance = RunStarExpression.new('x', goal)
507
+
508
+ # Reasoned S2, frame 1:65
509
+ # (run* x (disj2
510
+ # (conj2(== 'virgin x) fails)
511
+ # (disj2
512
+ # (== 'olive x)
513
+ # (dis2
514
+ # succeeds
515
+ # (== 'oil x))))) ;; => (olive _0 oil)
516
+ result = instance.run
517
+ expect(result.car).to eq(olive)
518
+ expect(result.cdr.car).to eq(any_value(0))
519
+ expect(result.cdr.cdr.car).to eq(oil)
520
+ end
521
+
522
+ it 'should accept nesting fresh, disj2 and conj2 expressions (I)' do
523
+ subgoal1 = equals_goal(split, ref_x)
524
+ expr1 = equals_goal(pea, ref_y)
525
+ expr2 = equals_goal(cons(ref_x, cons(ref_y)), ref_r)
526
+ subgoal2 = conj2_goal(expr1, expr2)
527
+ goal = conj2_goal(subgoal1, subgoal2)
528
+ fresh_env_y = FreshEnv.new(['y'], goal)
529
+ fresh_env_x = FreshEnv.new(['x'], fresh_env_y)
530
+ instance = RunStarExpression.new('r', fresh_env_x)
531
+
532
+ # Reasoned S2, frame 1:67
533
+ # (run* r
534
+ # (fresh x
535
+ # (fresh y
536
+ # (conj2
537
+ # (== 'split x)
538
+ # (conj2
539
+ # (== 'pea y)
540
+ # (== '(,x ,y) r)))))) ;; => ((split pea))
541
+ result = instance.run
542
+ expect(result.car.car).to eq(split)
543
+ expect(result.car.cdr.car).to eq(pea)
544
+ end
545
+
546
+ it 'should accept nesting fresh, disj2 and conj2 expressions (II)' do
547
+ expr1 = equals_goal(split, ref_x)
548
+ expr2 = equals_goal(pea, ref_y)
549
+ subgoal1 = conj2_goal(expr1, expr2)
550
+ subgoal2 = equals_goal(cons(ref_x, cons(ref_y)), ref_r)
551
+ goal = conj2_goal(subgoal1, subgoal2)
552
+ fresh_env_y = FreshEnv.new(['y'], goal)
553
+ fresh_env_x = FreshEnv.new(['x'], fresh_env_y)
554
+ instance = RunStarExpression.new('r', fresh_env_x)
555
+
556
+ # Reasoned S2, frame 1:68
557
+ # (run* r
558
+ # (fresh x
559
+ # (fresh y
560
+ # (conj2
561
+ # (conj2
562
+ # (== 'split x)
563
+ # (== 'pea y)
564
+ # (== '(,x ,y) r)))))) ;; => ((split pea))
565
+ result = instance.run
566
+ expect(result.car.car).to eq(split)
567
+ expect(result.car.cdr.car).to eq(pea)
568
+ end
569
+
570
+ it 'should accept fresh with multiple variables' do
571
+ expr1 = equals_goal(split, ref_x)
572
+ expr2 = equals_goal(pea, ref_y)
573
+ subgoal1 = conj2_goal(expr1, expr2)
574
+ subgoal2 = equals_goal(cons(ref_x, cons(ref_y)), ref_r)
575
+ goal = conj2_goal(subgoal1, subgoal2)
576
+ fresh_env = FreshEnv.new(%w[x y], goal)
577
+ instance = RunStarExpression.new('r', fresh_env)
578
+
579
+ # Reasoned S2, frame 1:70
580
+ # (run* r
581
+ # (fresh (x 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 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
+ instance = RunStarExpression.new(%w[r x y], goal)
599
+
600
+ # Reasoned S2, frame 1:72
601
+ # (run* (r x y)
602
+ # (conj2
603
+ # (conj2
604
+ # (== 'split x)
605
+ # (== 'pea y))
606
+ # (== '(,x ,y) r))) ;; => (((split pea) split pea))
607
+ # o
608
+ # / \
609
+ # o nil
610
+ # / \
611
+ # / \
612
+ # / \
613
+ # / \
614
+ # / \
615
+ # o o
616
+ # / \ / \
617
+ # split o split o
618
+ # / \ / \
619
+ # pea nil pea nil
620
+ result = instance.run
621
+ expect(result.car.car.car).to eq(split)
622
+ expect(result.car.car.cdr.car).to eq(pea)
623
+ expect(result.car.car.cdr.cdr).to be_nil
624
+ expect(result.car.cdr.car).to eq(split)
625
+ expect(result.car.cdr.cdr.car).to eq(pea)
626
+ expect(result.car.cdr.cdr.cdr).to be_nil
627
+ end
628
+
629
+ it 'should allow simplication of expressions' do
630
+ expr1 = equals_goal(split, ref_x)
631
+ expr2 = equals_goal(pea, ref_y)
632
+ goal = conj2_goal(expr1, expr2)
633
+ instance = RunStarExpression.new(%w[x y], goal)
634
+
635
+ # Reasoned S2, frame 1:75
636
+ # (run* (x y)
637
+ # (conj2
638
+ # (== 'split x)
639
+ # (== 'pea y))) ;; => ((split pea))
640
+ result = instance.run
641
+ expect(result.car.car).to eq(split)
642
+ expect(result.car.cdr.car).to eq(pea)
643
+ end
644
+
645
+ it 'should allow simplication of expressions' do
646
+ expr1 = equals_goal(split, ref_x)
647
+ expr2 = equals_goal(pea, ref_y)
648
+ subgoal1 = conj2_goal(expr1, expr2)
649
+ expr3 = equals_goal(red, ref_x)
650
+ expr4 = equals_goal(bean, ref_y)
651
+ subgoal2 = conj2_goal(expr3, expr4)
652
+ goal = disj2_goal(subgoal1, subgoal2)
653
+ instance = RunStarExpression.new(%w[x y], goal)
654
+
655
+ # Reasoned S2, frame 1:76
656
+ # (run* (x y)
657
+ # (disj2
658
+ # (conj2 (== 'split x) (== 'pea y))
659
+ # (conj2 (== 'red x) (== 'bean y)))) ;; => ((split pea)(red bean))
660
+ result = instance.run
661
+ expect(result.car.car).to eq(split)
662
+ expect(result.car.cdr.car).to eq(pea)
663
+ expect(result.cdr.car.car).to eq(red)
664
+ expect(result.cdr.car.cdr.car).to eq(bean)
665
+ end
666
+
667
+ it 'should allow nesting a disjunction inside of conjunction' do
668
+ expr1 = equals_goal(split, ref_x)
669
+ expr2 = equals_goal(red, ref_x)
670
+ subgoal1 = disj2_goal(expr1, expr2)
671
+ subgoal2 = equals_goal(ref_x, ref_y)
672
+ goal = conj2_goal(subgoal1, subgoal2)
673
+ instance = RunStarExpression.new(%w[x y], goal)
674
+
675
+ # (display (run* (x y)
676
+ # (conj2
677
+ # (disj2
678
+ # (== 'split x)
679
+ # (== 'red x))
680
+ # (== x y)))) ;; => ((split split) (red red))
681
+ result = instance.run
682
+ expect(result.car.car).to eq(split)
683
+ expect(result.car.cdr.car).to eq(split)
684
+ expect(result.cdr.car.cdr.car).to eq(red)
685
+ end
686
+
687
+ it 'should accept fresh with multiple variables' do
688
+ expr1 = equals_goal(split, ref_x)
689
+ expr2 = equals_goal(pea, ref_y)
690
+ subgoal1 = conj2_goal(expr1, expr2)
691
+ expr3 = equals_goal(red, ref_x)
692
+ expr4 = equals_goal(bean, ref_y)
693
+ subgoal2 = conj2_goal(expr3, expr4)
694
+ subgoal3 = disj2_goal(subgoal1, subgoal2)
695
+ subgoal4 = equals_goal(cons(ref_x, cons(ref_y, cons(soup))), ref_r)
696
+ goal = conj2_goal(subgoal3, subgoal4)
697
+ fresh_env = FreshEnv.new(%w[x y], goal)
698
+ instance = RunStarExpression.new('r', fresh_env)
699
+
700
+ # Reasoned S2, frame 1:77
701
+ # (run* r
702
+ # (fresh (x y)
703
+ # (conj2
704
+ # (disj2
705
+ # (conj2 (== 'split x) (== 'pea y))
706
+ # (conj2 (== 'red x) (== 'bean y)))
707
+ # (== '(,x ,y soup) r)))) ;; => ((split pea soup) (red bean soup))
708
+ result = instance.run
709
+ expect(result.car.car).to eq(split)
710
+ expect(result.car.cdr.car).to eq(pea)
711
+ expect(result.car.cdr.cdr.car).to eq(soup)
712
+ expect(result.cdr.car.car).to eq(red)
713
+ expect(result.cdr.car.cdr.car).to eq(bean)
714
+ expect(result.cdr.car.cdr.cdr.car).to eq(soup)
715
+ end
716
+
717
+ it 'should allow fresh with multiple goals' do
718
+ expr1 = equals_goal(split, ref_x)
719
+ expr2 = equals_goal(pea, ref_y)
720
+ subgoal1 = conj2_goal(expr1, expr2)
721
+ expr3 = equals_goal(red, ref_x)
722
+ expr4 = equals_goal(bean, ref_y)
723
+ subgoal2 = conj2_goal(expr3, expr4)
724
+ goal1 = disj2_goal(subgoal1, subgoal2)
725
+ goal2 = equals_goal(cons(ref_x, cons(ref_y, cons(soup))), ref_r)
726
+ fresh_env = FreshEnv.new(%w[x y], [goal1, goal2])
727
+ instance = RunStarExpression.new('r', fresh_env)
728
+
729
+ # Reasoned S2, frame 1:78
730
+ # (run* r
731
+ # (fresh (x y)
732
+ # (disj2
733
+ # (conj2 (== 'split x) (== 'pea y))
734
+ # (conj2 (== 'red x) (== 'bean y)))
735
+ # (== '(,x ,y soup) r))) ;; => ((split pea soup) (red bean soup))
736
+ result = instance.run
737
+ expect(result.car.car).to eq(split)
738
+ expect(result.car.cdr.car).to eq(pea)
739
+ expect(result.car.cdr.cdr.car).to eq(soup)
740
+ expect(result.cdr.car.car).to eq(red)
741
+ expect(result.cdr.car.cdr.car).to eq(bean)
742
+ expect(result.cdr.car.cdr.cdr.car).to eq(soup)
743
+ end
744
+
745
+ it 'should allow run* with multiple goals' do
746
+ expr1 = equals_goal(split, ref_x)
747
+ expr2 = equals_goal(pea, ref_y)
748
+ subgoal1 = conj2_goal(expr1, expr2)
749
+ expr3 = equals_goal(red, ref_x)
750
+ expr4 = equals_goal(bean, ref_y)
751
+ subgoal2 = conj2_goal(expr3, expr4)
752
+ goal1 = disj2_goal(subgoal1, subgoal2)
753
+ goal2 = equals_goal(soup, ref_z)
754
+ instance = RunStarExpression.new(%w[x y z], [goal1, goal2])
755
+
756
+ # Reasoned S2, frame 1:80
757
+ # (run* (x y z)
758
+ # (disj2
759
+ # (conj2 (== 'split x) (== 'pea y))
760
+ # (conj2 (== 'red x) (== 'bean y)))
761
+ # (== 'soup z)) ;; => ((split pea soup) (red bean soup))
762
+ result = instance.run
763
+ expect(result.car.car).to eq(split)
764
+ expect(result.car.cdr.car).to eq(pea)
765
+ expect(result.car.cdr.cdr.car).to eq(soup)
766
+ expect(result.cdr.car.car).to eq(red)
767
+ expect(result.cdr.car.cdr.car).to eq(bean)
768
+ expect(result.cdr.car.cdr.cdr.car).to eq(soup)
769
+ end
770
+
771
+ it 'should allow simplified expressions with multiple goals' do
772
+ expr1 = equals_goal(split, ref_x)
773
+ expr2 = equals_goal(pea, ref_y)
774
+ instance = RunStarExpression.new(%w[x y], [expr1, expr2])
775
+
776
+ # Reasoned S2, frame 1:81
777
+ # (run* (x y)
778
+ # (== 'split x)
779
+ # (== 'pea y)) ;; => ((split pea))
780
+ result = instance.run
781
+ expect(result.car.car).to eq(split)
782
+ expect(result.car.cdr.car).to eq(pea)
783
+ end
359
784
  end # context
360
785
  end # describe
361
786
  end # module