mini_kraken 0.1.04 → 0.1.09

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 (60) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +5 -1
  3. data/CHANGELOG.md +54 -0
  4. data/Gemfile +3 -1
  5. data/README.md +8 -6
  6. data/Rakefile +5 -3
  7. data/lib/mini_kraken.rb +3 -1
  8. data/lib/mini_kraken/core/any_value.rb +9 -7
  9. data/lib/mini_kraken/core/association.rb +20 -7
  10. data/lib/mini_kraken/core/association_walker.rb +5 -1
  11. data/lib/mini_kraken/core/atomic_term.rb +5 -3
  12. data/lib/mini_kraken/core/binary_relation.rb +8 -6
  13. data/lib/mini_kraken/core/composite_goal.rb +46 -0
  14. data/lib/mini_kraken/core/composite_term.rb +7 -20
  15. data/lib/mini_kraken/core/conj2.rb +76 -0
  16. data/lib/mini_kraken/core/cons_cell.rb +51 -41
  17. data/lib/mini_kraken/core/designation.rb +55 -0
  18. data/lib/mini_kraken/core/disj2.rb +71 -0
  19. data/lib/mini_kraken/core/duck_fiber.rb +4 -2
  20. data/lib/mini_kraken/core/environment.rb +25 -11
  21. data/lib/mini_kraken/core/equals.rb +127 -188
  22. data/lib/mini_kraken/core/fail.rb +20 -14
  23. data/lib/mini_kraken/core/freshness.rb +11 -8
  24. data/lib/mini_kraken/core/goal.rb +8 -4
  25. data/lib/mini_kraken/core/goal_arg.rb +10 -0
  26. data/lib/mini_kraken/core/goal_relation.rb +28 -0
  27. data/lib/mini_kraken/core/k_integer.rb +4 -3
  28. data/lib/mini_kraken/core/k_symbol.rb +4 -3
  29. data/lib/mini_kraken/core/nullary_relation.rb +3 -1
  30. data/lib/mini_kraken/core/outcome.rb +29 -25
  31. data/lib/mini_kraken/core/relation.rb +4 -18
  32. data/lib/mini_kraken/core/succeed.rb +20 -14
  33. data/lib/mini_kraken/core/term.rb +7 -2
  34. data/lib/mini_kraken/core/variable.rb +11 -25
  35. data/lib/mini_kraken/core/variable_ref.rb +12 -59
  36. data/lib/mini_kraken/core/vocabulary.rb +267 -48
  37. data/lib/mini_kraken/glue/fresh_env.rb +44 -6
  38. data/lib/mini_kraken/glue/run_star_expression.rb +49 -23
  39. data/lib/mini_kraken/version.rb +3 -1
  40. data/mini_kraken.gemspec +15 -13
  41. data/spec/core/association_spec.rb +4 -4
  42. data/spec/core/association_walker_spec.rb +25 -24
  43. data/spec/core/conj2_spec.rb +114 -0
  44. data/spec/core/cons_cell_spec.rb +12 -3
  45. data/spec/core/disj2_spec.rb +99 -0
  46. data/spec/core/duck_fiber_spec.rb +22 -12
  47. data/spec/core/environment_spec.rb +16 -28
  48. data/spec/core/equals_spec.rb +7 -7
  49. data/spec/core/fail_spec.rb +7 -7
  50. data/spec/core/goal_spec.rb +10 -10
  51. data/spec/core/k_symbol_spec.rb +5 -6
  52. data/spec/core/succeed_spec.rb +4 -4
  53. data/spec/core/variable_ref_spec.rb +0 -4
  54. data/spec/core/vocabulary_spec.rb +33 -27
  55. data/spec/glue/fresh_env_spec.rb +28 -2
  56. data/spec/glue/run_star_expression_spec.rb +370 -59
  57. data/spec/mini_kraken_spec.rb +4 -0
  58. data/spec/spec_helper.rb +3 -2
  59. data/spec/support/factory_methods.rb +20 -2
  60. metadata +12 -2
@@ -24,18 +24,18 @@ module MiniKraken
24
24
  it 'should unconditionally return a success result' do
25
25
  args = double('fake-args')
26
26
  env = double('fake-env')
27
-
27
+
28
28
  solver = nil
29
29
  expect { solver = subject.solver_for(args, env) }.not_to raise_error
30
-
30
+
31
31
  # Solver should quack like a Fiber
32
32
  dummy_arg = double('dummy-stuff')
33
33
  result = solver.resume(dummy_arg)
34
34
  expect(result).to eq(BasicSuccess)
35
-
35
+
36
36
  # Only one "solution", next 'resume' call should return nil
37
37
  result = solver.resume(dummy_arg)
38
- expect(result).to be_nil
38
+ expect(result).to be_nil
39
39
  end
40
40
  end # context
41
41
  end # describe
@@ -18,10 +18,6 @@ module MiniKraken
18
18
  it 'should know the name of a variable' do
19
19
  expect(subject.var_name).to eq('q')
20
20
  end
21
-
22
- # it 'should be fresh by default' do
23
- # expect(subject).to be_fresh
24
- # end
25
21
  end # context
26
22
 
27
23
  context 'Provided services:' do
@@ -1,18 +1,18 @@
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/association'
5
4
  require_relative '../../lib/mini_kraken/core/k_symbol'
5
+ require_relative '../../lib/mini_kraken/core/variable'
6
6
  require_relative '../../lib/mini_kraken/core/variable_ref'
7
7
  require_relative '../support/factory_methods'
8
8
 
9
9
  # Load the class under test
10
- require_relative '../../lib/mini_kraken/core/vocabulary'
10
+ # frozen_string_literal: true
11
11
 
12
+ require_relative '../../lib/mini_kraken/core/vocabulary'
12
13
 
13
14
  module MiniKraken
14
15
  module Core
15
-
16
16
  class TestVocabulary
17
17
  include Vocabulary
18
18
 
@@ -21,16 +21,20 @@ module MiniKraken
21
21
  end
22
22
  end # class
23
23
 
24
+ # Helper module that simulates an environment-like object.
24
25
  module VariableBearer
25
26
  attr_reader :vars
27
+ attr_accessor :ivars
26
28
 
27
29
  def init_var_bearer
28
30
  @vars = {}
31
+ @ivars = {}
29
32
  self
30
33
  end
31
34
 
32
35
  def add_var(aVarName)
33
- vars[aVarName] = aVarName.hash # Just for testing purposes
36
+ vars[aVarName] = Variable.new(aVarName)
37
+ ivars[aVarName] = Set.new([aVarName])
34
38
  end
35
39
  end # module
36
40
 
@@ -75,11 +79,11 @@ module MiniKraken
75
79
 
76
80
  it 'should provide a walker over ancestors' do
77
81
  walker = subject.ancestor_walker
78
- expect(walker).to be_kind_of(Fiber)
79
- expect(walker.resume).to eq(subject)
80
- expect(walker.resume).to eq(mother)
81
- expect(walker.resume).to eq(grandma)
82
- 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
83
87
  end
84
88
 
85
89
  it 'should know if a variable is defined' do
@@ -99,11 +103,12 @@ module MiniKraken
99
103
  it 'should allow the addition of associations' do
100
104
  grandma.add_var('q')
101
105
  expect(subject['q']).to be_empty
102
- mother.add_assoc(Association.new('q', pea))
106
+ res_add = mother.add_assoc('q', pea)
107
+ expect(res_add).to be_kind_of(Association)
103
108
  expect(subject['q'].size).to eq(1)
104
109
  expect(subject['q'].first.value).to eq(pea)
105
110
 
106
- subject.add_assoc(Association.new('q', ref_x))
111
+ subject.add_assoc('q', ref_x)
107
112
  expect(subject['q'].size).to eq(2)
108
113
  expect(subject['q'].first.value).to eq(ref_x)
109
114
  expect(subject['q'].last.value).to eq(pea)
@@ -111,8 +116,8 @@ module MiniKraken
111
116
 
112
117
  it 'should allow the deletion of associations' do
113
118
  grandma.add_var('q')
114
- mother.add_assoc(Association.new('q', pea))
115
- subject.add_assoc(Association.new('q', ref_x))
119
+ mother.add_assoc('q', pea)
120
+ subject.add_assoc('q', ref_x)
116
121
  expect(mother.associations.size).to eq(1)
117
122
  expect(subject.associations.size).to eq(1)
118
123
 
@@ -127,18 +132,18 @@ module MiniKraken
127
132
  grandma.add_var('q')
128
133
  grandma.add_var('x')
129
134
  expect(subject.fresh?(ref_q)).to be_truthy
130
- subject.add_assoc(Association.new('q', ref_x)) # Dependency: q --> x
135
+ subject.add_assoc('q', ref_x) # Dependency: q --> x
131
136
  expect(subject.fresh?(ref_x)).to be_truthy
132
137
  end
133
138
 
134
139
  it 'should say not fresh when variable --> atomic value' do
135
140
  grandma.add_var('q')
136
141
  grandma.add_var('x')
137
- subject.add_assoc(Association.new('q', ref_x)) # Dependency: q --> x
142
+ subject.add_assoc('q', ref_x) # Dependency: q --> x
138
143
  expect(subject.fresh?(ref_q)).to be_truthy
139
144
 
140
145
  # Associate with an atomic term
141
- subject.add_assoc(Association.new('q', pea))
146
+ subject.add_assoc('q', pea)
142
147
  expect(subject.fresh?(ref_q)).to be_falsey
143
148
  end
144
149
 
@@ -147,16 +152,16 @@ module MiniKraken
147
152
 
148
153
  # Composite having only atomic terms as leaf elements
149
154
  expr = cons(pea, cons(pod))
150
- subject.add_assoc(Association.new('q', expr))
155
+ subject.add_assoc('q', expr)
151
156
  expect(subject.fresh?(ref_q)).to be_falsey
152
157
  end
153
158
 
154
159
  it 'say not fresh when variable --> composite of atomics & bound var' do
155
160
  grandma.add_var('q')
156
161
  grandma.add_var('x')
157
- subject.add_assoc(Association.new('x', pea)) # Dependency: x --> pea
162
+ subject.add_assoc('x', pea) # Dependency: x --> pea
158
163
  expr = cons(pea, cons(pod, cons(ref_x)))
159
- subject.add_assoc(Association.new('q', expr))
164
+ subject.add_assoc('q', expr)
160
165
  expect(subject.fresh?(ref_q)).to be_falsey
161
166
  end
162
167
 
@@ -164,7 +169,7 @@ module MiniKraken
164
169
  grandma.add_var('q')
165
170
  grandma.add_var('x')
166
171
  expr = cons(pea, cons(pod, cons(ref_x)))
167
- subject.add_assoc(Association.new('q', expr))
172
+ subject.add_assoc('q', expr)
168
173
  expect(subject.fresh?(ref_q)).to be_truthy
169
174
  end
170
175
 
@@ -173,13 +178,14 @@ module MiniKraken
173
178
  grandma.add_var('x')
174
179
 
175
180
  # Beware of cyclic structure
176
- subject.add_assoc(Association.new('q', ref_x)) # Dependency: q --> x
177
- subject.add_assoc(Association.new('x', ref_q)) # Dependency: x --> q
181
+ subject.add_assoc('q', ref_x) # Dependency: q --> x
182
+ subject.add_assoc('x', ref_q) # Dependency: x --> q
178
183
  expect(subject.fresh?(ref_x)).to be_truthy
179
184
  expect(subject.fresh?(ref_q)).to be_truthy
185
+ expect(subject.associations).to be_empty
180
186
 
181
187
  # Associate with an atomic term
182
- subject.add_assoc(Association.new('x', pea))
188
+ subject.add_assoc('x', pea)
183
189
  expect(subject.fresh?(ref_q)).to be_falsey
184
190
  end
185
191
 
@@ -190,16 +196,16 @@ module MiniKraken
190
196
  expect(subject.get_rank('c')).to eq(2)
191
197
  end
192
198
  end
193
-
199
+
194
200
  it 'should clear the rankings' do
195
201
  expect(subject.get_rank('a')).to eq(0)
196
202
  expect(subject.get_rank('z')).to eq(1)
197
-
203
+
198
204
  subject.clear_rankings
199
205
  expect(grandma.rankings).to be_empty
200
206
 
201
- expect(subject.get_rank('z')).to eq(0)
202
- expect(subject.get_rank('a')).to eq(1)
207
+ expect(subject.get_rank('z')).to eq(0)
208
+ expect(subject.get_rank('a')).to eq(1)
203
209
  end
204
210
  end # context
205
211
  end # describe
@@ -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'
@@ -15,17 +17,41 @@ module MiniKraken
15
17
  let(:pea) { Core::KSymbol.new(:pea) }
16
18
  let(:pod) { Core::KSymbol.new(:pod) }
17
19
  let(:sample_goal) do
18
- Core::Goal.new(Core::Equals.instance, [pea, pod])
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,24 +49,33 @@ 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
- let(:ref_u) { Core::VariableRef.new('u') }
67
+ let(:ref_u) { Core::VariableRef.new('u') }
46
68
 
47
- it "should return a null list with the fail goal" do
69
+ it 'should return a null list with the fail goal' do
48
70
  # Reasoned S2, frame 1:7
49
71
  # (run* q #u) ;; => ()
50
72
  failing = Core::Goal.new(Core::Fail.instance, [])
51
73
  instance = RunStarExpression.new('q', failing)
52
74
 
53
75
  expect(instance.run).to be_null
54
- expect(ref_q.fresh?(instance.env)).to be_truthy
55
76
  end
56
77
 
57
- it "should return a null list when a goal fails" do
78
+ it 'should return a null list when a goal fails' do
58
79
  # Reasoned S2, frame 1:10
59
80
  # (run* q (== 'pea 'pod) ;; => ()
60
81
 
@@ -69,7 +90,6 @@ module MiniKraken
69
90
  # Reasoned S2, frame 1:11
70
91
  # (run* q (== q 'pea) ;; => (pea)
71
92
  expect(instance.run.car).to eq(pea)
72
- expect(ref_q.fresh?(instance.env)).to be_falsey
73
93
  end
74
94
 
75
95
  it 'should unify the righthand variable(s)' do
@@ -79,19 +99,14 @@ module MiniKraken
79
99
  # Reasoned S2, frame 1:12
80
100
  # (run* q (== 'pea q) ;; => (pea)
81
101
  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
102
  end
86
103
 
87
104
  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)
105
+ instance = RunStarExpression.new('q', succeeds)
90
106
 
91
107
  # (display (run* q succeed)) ;; => (_0)
92
108
  # Reasoned S2, frame 1:16
93
109
  result = instance.run
94
- expect(ref_q.fresh?(instance.env)).to be_truthy
95
110
 
96
111
  # Reasoned S2, frame 1:17
97
112
  expect(result.car).to eq(any_value(0))
@@ -104,7 +119,6 @@ module MiniKraken
104
119
  # (display (run* q (== 'pea 'pea))) ;; => (_0)
105
120
  # Reasoned S2, frame 1:19
106
121
  result = instance.run
107
- expect(ref_q.fresh?(instance.env)).to be_truthy
108
122
  expect(result.car).to eq(any_value(0))
109
123
  end
110
124
 
@@ -117,7 +131,6 @@ module MiniKraken
117
131
  # (display (run* q (== q q))) ;; => (_0)
118
132
  # Reasoned S2, frame 1:20
119
133
  result = instance.run
120
- expect(ref_q.fresh?(instance.env)).to be_truthy
121
134
  expect(result.car).to eq(any_value(0))
122
135
  end
123
136
 
@@ -129,8 +142,6 @@ module MiniKraken
129
142
  # Reasoned S2, frame 1:21..23
130
143
  # (run* q (fresh (x) (== 'pea q))) ;; => (pea)
131
144
  result = instance.run
132
- expect(ref_q.fresh?(instance.env)).to be_falsey
133
- expect(ref_x.fresh?(fresh_env)).to be_truthy
134
145
 
135
146
  # Reasoned S2, frame 1:40
136
147
  expect(ref_q.different_from?(ref_x, fresh_env)).to be_truthy
@@ -145,8 +156,6 @@ module MiniKraken
145
156
  # Reasoned S2, frame 1:24
146
157
  # (run* q (fresh (x) (== 'pea x))) ;; => (_0)
147
158
  result = instance.run
148
- expect(ref_q.fresh?(instance.env)).to be_truthy
149
- expect(ref_x.fresh?(fresh_env)).to be_falsey
150
159
  expect(result.car).to eq(any_value(0))
151
160
  end
152
161
 
@@ -158,8 +167,6 @@ module MiniKraken
158
167
  # Reasoned S2, frame 1:25
159
168
  # (run* q (fresh (x) (== (cons x '()) q))) ;; => ((_0))
160
169
  result = instance.run
161
- expect(ref_q.fresh?(instance.env)).to be_truthy
162
- expect(ref_x.fresh?(fresh_env)).to be_truthy
163
170
  expect(result.car).to eq(cons(any_value(0)))
164
171
  end
165
172
 
@@ -190,7 +197,6 @@ module MiniKraken
190
197
  # Reasoned S2, frame 1:32
191
198
  # (run* q (== '(((pea)) pod) '(((pea)) pod))) ;; => (_0)
192
199
  result = instance.run
193
- expect(ref_q.fresh?(instance.env)).to be_truthy
194
200
  expect(result.car).to eq(any_value(0))
195
201
  end
196
202
 
@@ -204,7 +210,6 @@ module MiniKraken
204
210
  # Reasoned S2, frame 1:33
205
211
  # (run* q (== '(((pea)) pod) `(((pea)) ,q))) ;; => ('pod)
206
212
  result = instance.run
207
- expect(ref_q.fresh?(instance.env)).to be_falsey
208
213
  expect(result.car).to eq(pod)
209
214
  end
210
215
 
@@ -217,7 +222,6 @@ module MiniKraken
217
222
  # Reasoned S2, frame 1:34
218
223
  # (run* q (== '(((,q)) pod) `(((pea)) pod))) ;; => ('pod)
219
224
  result = instance.run
220
- expect(ref_q.fresh?(instance.env)).to be_falsey
221
225
  expect(result.car).to eq(pea)
222
226
  end
223
227
 
@@ -231,8 +235,6 @@ module MiniKraken
231
235
  # Reasoned S2, frame 1:35
232
236
  # (run* q (fresh (x) (== '(((,q)) pod) `(((,x)) pod)))) ;; => (_0)
233
237
  result = instance.run
234
- expect(ref_q.fresh?(instance.env)).to be_truthy
235
- expect(ref_x.fresh?(fresh_env)).to be_truthy
236
238
  expect(result.car).to eq(any_value(0))
237
239
  end
238
240
 
@@ -246,11 +248,6 @@ module MiniKraken
246
248
  instance = RunStarExpression.new('q', fresh_env)
247
249
 
248
250
  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
251
  expect(result.car).to eq(pod)
255
252
  end
256
253
 
@@ -263,9 +260,6 @@ module MiniKraken
263
260
  instance = RunStarExpression.new('q', fresh_env)
264
261
 
265
262
  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
263
  expect(result.car).to eq(cons(any_value(0), cons(any_value(0))))
270
264
  end
271
265
 
@@ -280,17 +274,16 @@ module MiniKraken
280
274
  instance = RunStarExpression.new('q', fresh_env_x)
281
275
 
282
276
  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_truthy
287
- expect(ref_y.fresh?(fresh_env_y)).to be_truthy
288
- expect(ref_y.bound?(fresh_env_y)).to be_truthy
289
277
 
290
278
  # y should be fused with x...
279
+ var_x = fresh_env_y.name2var('x')
280
+ var_y = fresh_env_y.name2var('y')
281
+ expect(var_x.i_name).to eq(var_y.i_name)
291
282
  expect(ref_y.fused_with?(ref_x, fresh_env_y)).to be_truthy
292
283
  expect(ref_x.names_fused(fresh_env_y)).to eq(['y'])
293
284
  expect(ref_y.names_fused(fresh_env_y)).to eq(['x'])
285
+
286
+ # q should be bound to '(,x ,x)
294
287
  expect(result.car).to eq(cons(any_value(0), cons(any_value(0))))
295
288
  end
296
289
 
@@ -304,16 +297,10 @@ module MiniKraken
304
297
  instance = RunStarExpression.new('q', fresh_env_x)
305
298
 
306
299
  result = instance.run
307
- expect(ref_q.fresh?(fresh_env_y)).to be_truthy
308
300
  # q should be bound to '(,x ,y)
309
- expect(ref_q.bound?(fresh_env_y)).to be_truthy
310
- expect(ref_x.fresh?(fresh_env_y)).to be_truthy
311
- expect(ref_x.bound?(fresh_env_y)).to be_falsey
312
- expect(ref_y.fresh?(fresh_env_y)).to be_truthy
313
- expect(ref_y.bound?(fresh_env_y)).to be_falsey
314
301
  expect(result.car).to eq(cons(any_value(0), cons(any_value(1))))
315
302
  end
316
-
303
+
317
304
  it 'should work with variable names' do
318
305
  # Reasoned S2, frame 1:42
319
306
  # (run* s (fresh (t) (fresh (u) (== '( ,t ,u) s)))) ;; => (_0 _1)
@@ -324,15 +311,9 @@ module MiniKraken
324
311
  instance = RunStarExpression.new('s', fresh_env_t)
325
312
 
326
313
  result = instance.run
327
- expect(ref_s.fresh?(fresh_env_u)).to be_truthy
328
314
  # s should be bound to '(,t ,u)
329
- expect(ref_s.bound?(fresh_env_u)).to be_truthy
330
- expect(ref_t.fresh?(fresh_env_u)).to be_truthy
331
- expect(ref_t.bound?(fresh_env_u)).to be_falsey
332
- expect(ref_u.fresh?(fresh_env_u)).to be_truthy
333
- expect(ref_u.bound?(fresh_env_u)).to be_falsey
334
315
  expect(result.car).to eq(cons(any_value(0), cons(any_value(1))))
335
- end
316
+ end
336
317
 
337
318
  it 'should support repeated variables' do
338
319
  # Reasoned S2, frame 1:43
@@ -344,13 +325,343 @@ module MiniKraken
344
325
  instance = RunStarExpression.new('q', fresh_env_x)
345
326
 
346
327
  result = instance.run
347
- expect(ref_q.fresh?(fresh_env_y)).to be_truthy
348
328
  # q should be bound to '(,x ,y, ,x)
349
- expect(ref_q.bound?(fresh_env_y)).to be_truthy
350
- expect(ref_x.fresh?(fresh_env_y)).to be_truthy
351
- expect(ref_y.fresh?(fresh_env_y)).to be_truthy
352
329
  expect(result.car).to eq(cons(any_value(0), cons(any_value(1), cons(any_value(0)))))
353
- end
330
+ end
331
+
332
+ it 'should support conjunction of two succeed' do
333
+ goal = conj2_goal(succeeds, succeeds)
334
+ instance = RunStarExpression.new('q', goal)
335
+
336
+ # Reasoned S2, frame 1:50
337
+ # (run* q (conj2 succeed succeed)) ;; => (_0)
338
+ result = instance.run
339
+ expect(result.car).to eq(any_value(0))
340
+ end
341
+
342
+ # TODO: fix erratic RSpec failure
343
+ it 'should support conjunction of one succeed and a successful goal' do
344
+ subgoal = equals_goal(corn, ref_q)
345
+ goal = conj2_goal(succeeds, subgoal)
346
+ instance = RunStarExpression.new('q', goal)
347
+
348
+ # Reasoned S2, frame 1:51
349
+ # (run* q (conj2 succeed (== 'corn q)) ;; => ('corn)
350
+ result = instance.run
351
+ expect(result.car).to eq(corn)
352
+ end
353
+
354
+ it 'should support conjunction of one fail and a successful goal' do
355
+ subgoal = equals_goal(corn, ref_q)
356
+ goal = conj2_goal(fails, subgoal)
357
+ instance = RunStarExpression.new('q', goal)
358
+
359
+ # Reasoned S2, frame 1:52
360
+ # (run* q (conj2 fail (== 'corn q)) ;; => ()
361
+ expect(instance.run).to be_null
362
+ end
363
+
364
+ it 'should support conjunction of two contradictory goals' do
365
+ subgoal1 = equals_goal(corn, ref_q)
366
+ subgoal2 = equals_goal(meal, ref_q)
367
+ goal = conj2_goal(subgoal1, subgoal2)
368
+ instance = RunStarExpression.new('q', goal)
369
+
370
+ # Reasoned S2, frame 1:53
371
+ # (run* q (conj2 (== 'corn q)(== 'meal q)) ;; => ()
372
+ expect(instance.run).to be_null
373
+ end
374
+
375
+ it 'should succeed the conjunction of two identical goals' do
376
+ subgoal1 = equals_goal(corn, ref_q)
377
+ subgoal2 = equals_goal(corn, ref_q)
378
+ goal = conj2_goal(subgoal1, subgoal2)
379
+ instance = RunStarExpression.new('q', goal)
380
+
381
+ # Reasoned S2, frame 1:54
382
+ # (run* q (conj2 (== 'corn q)(== 'corn q)) ;; => ('corn)
383
+ result = instance.run
384
+ expect(result.car).to eq(corn)
385
+ end
386
+
387
+ it 'should not yield solution when both disjunction arguments fail' do
388
+ goal = disj2_goal(fails, fails)
389
+ instance = RunStarExpression.new('q', goal)
390
+
391
+ # Reasoned S2, frame 1:55
392
+ # (run* q (disj2 fail fail)) ;; => ()
393
+ expect(instance.run).to be_null
394
+ end
395
+
396
+ it 'should yield solution when first argument succeed' do
397
+ subgoal = Core::Goal.new(Core::Equals.instance, [olive, ref_q])
398
+ goal = disj2_goal(subgoal, fails)
399
+ instance = RunStarExpression.new('q', goal)
400
+
401
+ # Reasoned S2, frame 1:56
402
+ # (run* q (disj2 (== 'olive q) fail)) ;; => ('olive)
403
+ result = instance.run
404
+ expect(result.car).to eq(olive)
405
+ end
406
+
407
+ it 'should yield solution when second argument succeed' do
408
+ subgoal = Core::Goal.new(Core::Equals.instance, [oil, ref_q])
409
+ goal = disj2_goal(fails, subgoal)
410
+ instance = RunStarExpression.new('q', goal)
411
+
412
+ # Reasoned S2, frame 1:57
413
+ # (run* q (disj2 fail (== 'oil q))) ;; => (oil)
414
+ result = instance.run
415
+ expect(result.car).to eq(oil)
416
+ end
417
+
418
+ it 'should yield solutions when both arguments succeed' do
419
+ subgoal1 = Core::Goal.new(Core::Equals.instance, [olive, ref_q])
420
+ subgoal2 = Core::Goal.new(Core::Equals.instance, [oil, ref_q])
421
+ goal = disj2_goal(subgoal1, subgoal2)
422
+ instance = RunStarExpression.new('q', goal)
423
+
424
+ # Reasoned S2, frame 1:58
425
+ # (run* q (disj2 (== 'olive q) (== 'oil q))) ;; => (olive oil)
426
+ result = instance.run
427
+ expect(result.car).to eq(olive)
428
+ expect(result.cdr.car).to eq(oil)
429
+ end
430
+
431
+ it 'should support the nesting of variables and disjunction' do
432
+ # Reasoned S2, frame 1:59
433
+ # (run* q (fresh (x) (fresh (y) (disj2 (== '( ,x ,y ) q) (== '( ,x ,y ) q)))))
434
+ # ;; => ((_0 _1) (_0 _1))
435
+ expr1 = cons(ref_x, cons(ref_y))
436
+ subgoal1 = equals_goal(expr1, ref_q)
437
+ expr2 = cons(ref_y, cons(ref_x))
438
+ subgoal2 = equals_goal(expr2, ref_q)
439
+ goal = disj2_goal(subgoal1, subgoal2)
440
+ fresh_env_y = FreshEnv.new(['y'], goal)
441
+ fresh_env_x = FreshEnv.new(['x'], fresh_env_y)
442
+ instance = RunStarExpression.new('q', fresh_env_x)
443
+
444
+ result = instance.run
445
+ # q should be bound to '(,x ,y), then to '(,y ,x)
446
+ expect(result.car).to eq(cons(any_value(0), cons(any_value(1))))
447
+ expect(result.cdr.car).to eq(cons(any_value(0), cons(any_value(1))))
448
+ end
449
+
450
+ it 'should accept nesting of disj2 and conj2 (I)' do
451
+ conj_subgoal = Core::Goal.new(Core::Equals.instance, [olive, ref_x])
452
+ conjunction = conj2_goal(conj_subgoal, fails)
453
+ subgoal = Core::Goal.new(Core::Equals.instance, [oil, ref_x])
454
+ goal = disj2_goal(conjunction, subgoal)
455
+ instance = RunStarExpression.new('x', goal)
456
+
457
+ # Reasoned S2, frame 1:62
458
+ # (run* x (disj2
459
+ # (conj2 (== 'olive x) fail)
460
+ # ('oil x))) ;; => (oil)
461
+ result = instance.run
462
+ expect(result.car).to eq(oil)
463
+ end
464
+
465
+ it 'should accept nesting of disj2 and conj2 (II)' do
466
+ conj_subgoal = Core::Goal.new(Core::Equals.instance, [olive, ref_x])
467
+ conjunction = conj2_goal(conj_subgoal, succeeds)
468
+ subgoal = Core::Goal.new(Core::Equals.instance, [oil, ref_x])
469
+ goal = disj2_goal(conjunction, subgoal)
470
+ instance = RunStarExpression.new('x', goal)
471
+
472
+ # Reasoned S2, frame 1:63
473
+ # (run* x (disj2
474
+ # (conj2 (== 'olive x) succeed)
475
+ # ('oil x))) ;; => (olive oil)
476
+ result = instance.run
477
+ expect(result.car).to eq(olive)
478
+ expect(result.cdr.car).to eq(oil)
479
+ end
480
+
481
+ it 'should accept nesting of disj2 and conj2 (III)' do
482
+ conj_subgoal = Core::Goal.new(Core::Equals.instance, [olive, ref_x])
483
+ conjunction = conj2_goal(conj_subgoal, succeeds)
484
+ subgoal = Core::Goal.new(Core::Equals.instance, [oil, ref_x])
485
+ goal = disj2_goal(subgoal, conjunction)
486
+ instance = RunStarExpression.new('x', goal)
487
+
488
+ # Reasoned S2, frame 1:64
489
+ # (run* x (disj2
490
+ # ('oil x)
491
+ # (conj2 (== 'olive x) succeed))) ;; => (oil olive)
492
+ result = instance.run
493
+ expect(result.car).to eq(oil)
494
+ expect(result.cdr.car).to eq(olive)
495
+ end
496
+
497
+ it 'should accept nesting of disj2 and conj2 (IV)' do
498
+ oil_goal = Core::Goal.new(Core::Equals.instance, [oil, ref_x])
499
+ disja = disj2_goal(succeeds, oil_goal)
500
+ olive_goal = Core::Goal.new(Core::Equals.instance, [olive, ref_x])
501
+ disjb = disj2_goal(olive_goal, disja)
502
+ virgin_goal = Core::Goal.new(Core::Equals.instance, [virgin, ref_x])
503
+ conjunction = conj2_goal(virgin_goal, fails)
504
+ goal = disj2_goal(conjunction, disjb)
505
+ instance = RunStarExpression.new('x', goal)
506
+
507
+ # Reasoned S2, frame 1:65
508
+ # (run* x (disj2
509
+ # (conj2(== 'virgin x) fails)
510
+ # (disj2
511
+ # (== 'olive x)
512
+ # (dis2
513
+ # succeeds
514
+ # (== 'oil x))))) ;; => (olive _0 oil)
515
+ result = instance.run
516
+ expect(result.car).to eq(olive)
517
+ expect(result.cdr.car).to eq(any_value(0))
518
+ expect(result.cdr.cdr.car).to eq(oil)
519
+ end
520
+
521
+ it 'should accept nesting fresh, disj2 and conj2 expressions (I)' do
522
+ subgoal1 = equals_goal(split, ref_x)
523
+ expr1 = equals_goal(pea, ref_y)
524
+ expr2 = equals_goal(cons(ref_x, cons(ref_y)), ref_r)
525
+ subgoal2 = conj2_goal(expr1, expr2)
526
+ goal = conj2_goal(subgoal1, subgoal2)
527
+ fresh_env_y = FreshEnv.new(['y'], goal)
528
+ fresh_env_x = FreshEnv.new(['x'], fresh_env_y)
529
+ instance = RunStarExpression.new('r', fresh_env_x)
530
+
531
+ # Reasoned S2, frame 1:67
532
+ # (run* r
533
+ # (fresh x
534
+ # (fresh y
535
+ # (conj2
536
+ # (== 'split x)
537
+ # (conj2
538
+ # (== 'pea y)
539
+ # (== '(,x ,y) r)))))) ;; => ((split pea))
540
+ result = instance.run
541
+ expect(result.car.car).to eq(split)
542
+ expect(result.car.cdr.car).to eq(pea)
543
+ end
544
+
545
+ it 'should accept nesting fresh, disj2 and conj2 expressions (II)' do
546
+ expr1 = equals_goal(split, ref_x)
547
+ expr2 = equals_goal(pea, ref_y)
548
+ subgoal1 = conj2_goal(expr1, expr2)
549
+ subgoal2 = equals_goal(cons(ref_x, cons(ref_y)), ref_r)
550
+ goal = conj2_goal(subgoal1, subgoal2)
551
+ fresh_env_y = FreshEnv.new(['y'], goal)
552
+ fresh_env_x = FreshEnv.new(['x'], fresh_env_y)
553
+ instance = RunStarExpression.new('r', fresh_env_x)
554
+
555
+ # Reasoned S2, frame 1:68
556
+ # (run* r
557
+ # (fresh x
558
+ # (fresh y
559
+ # (conj2
560
+ # (conj2
561
+ # (== 'split x)
562
+ # (== 'pea y)
563
+ # (== '(,x ,y) r)))))) ;; => ((split pea))
564
+ result = instance.run
565
+ expect(result.car.car).to eq(split)
566
+ expect(result.car.cdr.car).to eq(pea)
567
+ end
568
+
569
+ it 'should accept fresh with multiple variables' do
570
+ expr1 = equals_goal(split, ref_x)
571
+ expr2 = equals_goal(pea, ref_y)
572
+ subgoal1 = conj2_goal(expr1, expr2)
573
+ subgoal2 = equals_goal(cons(ref_x, cons(ref_y)), ref_r)
574
+ goal = conj2_goal(subgoal1, subgoal2)
575
+ fresh_env = FreshEnv.new(%w[x y], goal)
576
+ instance = RunStarExpression.new('r', fresh_env)
577
+
578
+ # Reasoned S2, frame 1:70
579
+ # (run* r
580
+ # (fresh (x y)
581
+ # (conj2
582
+ # (conj2
583
+ # (== 'split x)
584
+ # (== 'pea y)
585
+ # (== '(,x ,y) r))))) ;; => ((split pea))
586
+ result = instance.run
587
+ expect(result.car.car).to eq(split)
588
+ expect(result.car.cdr.car).to eq(pea)
589
+ end
590
+
591
+ it 'should accept multiple variables' do
592
+ expr1 = equals_goal(split, ref_x)
593
+ expr2 = equals_goal(pea, ref_y)
594
+ subgoal1 = conj2_goal(expr1, expr2)
595
+ subgoal2 = equals_goal(cons(ref_x, cons(ref_y)), ref_r)
596
+ goal = conj2_goal(subgoal1, subgoal2)
597
+ instance = RunStarExpression.new(%w[r x y], goal)
598
+
599
+ # Reasoned S2, frame 1:72
600
+ # (run* (r x y)
601
+ # (conj2
602
+ # (conj2
603
+ # (== 'split x)
604
+ # (== 'pea y))
605
+ # (== '(,x ,y) r))) ;; => (((split pea) split pea))
606
+ # o
607
+ # / \
608
+ # o nil
609
+ # / \
610
+ # / \
611
+ # / \
612
+ # / \
613
+ # / \
614
+ # o o
615
+ # / \ / \
616
+ # split o split o
617
+ # / \ / \
618
+ # pea nil pea nil
619
+ result = instance.run
620
+ expect(result.car.car.car).to eq(split)
621
+ expect(result.car.car.cdr.car).to eq(pea)
622
+ expect(result.car.car.cdr.cdr).to be_nil
623
+ expect(result.car.cdr.car).to eq(split)
624
+ expect(result.car.cdr.cdr.car).to eq(pea)
625
+ expect(result.car.cdr.cdr.cdr).to be_nil
626
+ end
627
+
628
+ it 'should allow simplication of expressions' do
629
+ expr1 = equals_goal(split, ref_x)
630
+ expr2 = equals_goal(pea, ref_y)
631
+ goal = conj2_goal(expr1, expr2)
632
+ instance = RunStarExpression.new(%w[x y], goal)
633
+
634
+ # Reasoned S2, frame 1:75
635
+ # (run* (x y)
636
+ # (conj2
637
+ # (== 'split x)
638
+ # (== 'pea y))) ;; => ((split pea))
639
+ result = instance.run
640
+ expect(result.car.car).to eq(split)
641
+ expect(result.car.cdr.car).to eq(pea)
642
+ end
643
+
644
+ it 'should allow simplication of expressions' do
645
+ expr1 = equals_goal(split, ref_x)
646
+ expr2 = equals_goal(pea, ref_y)
647
+ subgoal1 = conj2_goal(expr1, expr2)
648
+ expr3 = equals_goal(red, ref_x)
649
+ expr4 = equals_goal(bean, ref_y)
650
+ subgoal2 = conj2_goal(expr3, expr4)
651
+ goal = disj2_goal(subgoal1, subgoal2)
652
+ instance = RunStarExpression.new(%w[x y], goal)
653
+
654
+ # Reasoned S2, frame 1:76
655
+ # (run* (x y)
656
+ # (disj2
657
+ # (conj2 (== 'split x) (== 'pea y))
658
+ # (conj2 (== 'red x) (== 'bean y)))) ;; => ((split pea)(red bean))
659
+ result = instance.run
660
+ expect(result.car.car).to eq(split)
661
+ expect(result.car.cdr.car).to eq(pea)
662
+ expect(result.cdr.car.car).to eq(red)
663
+ expect(result.cdr.car.cdr.car).to eq(bean)
664
+ end
354
665
  end # context
355
666
  end # describe
356
667
  end # module