mini_kraken 0.1.01 → 0.1.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 (54) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +3 -0
  3. data/README.md +0 -3
  4. data/lib/mini_kraken/core/any_value.rb +29 -0
  5. data/lib/mini_kraken/core/association.rb +21 -0
  6. data/lib/mini_kraken/core/association_walker.rb +179 -0
  7. data/lib/mini_kraken/core/atomic_term.rb +64 -0
  8. data/lib/mini_kraken/core/binary_relation.rb +61 -0
  9. data/lib/mini_kraken/core/composite_term.rb +54 -0
  10. data/lib/mini_kraken/core/cons_cell.rb +44 -0
  11. data/lib/mini_kraken/core/duck_fiber.rb +44 -0
  12. data/lib/mini_kraken/core/environment.rb +59 -0
  13. data/lib/mini_kraken/core/equals.rb +216 -0
  14. data/lib/mini_kraken/core/fail.rb +8 -5
  15. data/lib/mini_kraken/core/freshness.rb +42 -0
  16. data/lib/mini_kraken/core/goal.rb +31 -6
  17. data/lib/mini_kraken/core/k_integer.rb +15 -0
  18. data/lib/mini_kraken/core/k_symbol.rb +15 -0
  19. data/lib/mini_kraken/core/nullary_relation.rb +11 -3
  20. data/lib/mini_kraken/core/outcome.rb +35 -0
  21. data/lib/mini_kraken/core/relation.rb +31 -4
  22. data/lib/mini_kraken/core/succeed.rb +13 -1
  23. data/lib/mini_kraken/core/term.rb +7 -0
  24. data/lib/mini_kraken/core/variable.rb +45 -3
  25. data/lib/mini_kraken/core/variable_ref.rb +76 -0
  26. data/lib/mini_kraken/core/vocabulary.rb +161 -0
  27. data/lib/mini_kraken/glue/fresh_env.rb +31 -0
  28. data/lib/mini_kraken/glue/run_star_expression.rb +43 -0
  29. data/lib/mini_kraken/version.rb +1 -1
  30. data/spec/core/association_spec.rb +38 -0
  31. data/spec/core/association_walker_spec.rb +191 -0
  32. data/spec/core/cons_cell_spec.rb +63 -0
  33. data/spec/core/duck_fiber_spec.rb +62 -0
  34. data/spec/core/environment_spec.rb +154 -0
  35. data/spec/core/equals_spec.rb +289 -0
  36. data/spec/core/fail_spec.rb +16 -0
  37. data/spec/core/goal_spec.rb +36 -10
  38. data/spec/core/k_symbol_spec.rb +72 -0
  39. data/spec/core/succeed_spec.rb +43 -0
  40. data/spec/core/variable_ref_spec.rb +31 -0
  41. data/spec/core/variable_spec.rb +11 -3
  42. data/spec/core/vocabulary_spec.rb +188 -0
  43. data/spec/glue/fresh_env_spec.rb +36 -0
  44. data/spec/glue/run_star_expression_spec.rb +247 -0
  45. data/spec/support/factory_methods.rb +54 -0
  46. metadata +46 -13
  47. data/lib/mini_kraken/core/facade.rb +0 -45
  48. data/lib/mini_kraken/core/formal_arg.rb +0 -6
  49. data/lib/mini_kraken/core/publisher.rb +0 -27
  50. data/lib/mini_kraken/core/run_star_expression.rb +0 -34
  51. data/lib/mini_kraken/dsl/kraken_dsl.rb +0 -12
  52. data/spec/core/facade_spec.rb +0 -38
  53. data/spec/core/run_star_expression_spec.rb +0 -43
  54. data/spec/dsl/kraken_dsl_spec.rb +0 -31
@@ -0,0 +1,289 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../spec_helper' # Use the RSpec framework
4
+ require_relative '../../lib/mini_kraken/core/environment'
5
+
6
+ require_relative '../support/factory_methods'
7
+ # Load the class under test
8
+ require_relative '../../lib/mini_kraken/core/equals'
9
+
10
+ module MiniKraken
11
+ module Core
12
+ describe Equals do
13
+ include FactoryMethods
14
+
15
+ subject { Equals.instance }
16
+
17
+ context 'Initialization:' do
18
+ it 'should be created without argument' do
19
+ expect { Equals.instance }.not_to raise_error
20
+ end
21
+
22
+ it 'should know its name' do
23
+ expect(subject.name).to eq('equals')
24
+ end
25
+ end # context
26
+
27
+ context 'Provided services:' do
28
+ def build_var(aName)
29
+ new_var = Variable.new(aName)
30
+ env.add_var(new_var)
31
+ new_var
32
+ end
33
+
34
+ def build_var_ref(aName)
35
+ VariableRef.new(aName)
36
+ end
37
+
38
+ def solve_for(arg1, arg2)
39
+ solver = subject.solver_for([arg1, arg2], env)
40
+ outcome = solver.resume
41
+ env.propagate(outcome)
42
+ outcome
43
+ end
44
+
45
+ let(:pea) { KSymbol.new(:pea) }
46
+ let(:pod) { KSymbol.new(:pod) }
47
+ let(:sample_cons) { ConsCell.new(pea, nil) }
48
+ let(:a_composite) { ConsCell.new(pod) }
49
+ let(:env) { Environment.new }
50
+ let(:var_q) { build_var('q') }
51
+ let(:ref_q) do
52
+ dummy = var_q # Force dependency
53
+ build_var_ref('q')
54
+ end
55
+ let(:ref_q_bis) do
56
+ dummy = var_q # Force dependency
57
+ build_var_ref('q')
58
+ end
59
+ let(:var_x) { build_var('x') }
60
+ let(:ref_x) do
61
+ dummy = var_x # Force dependency
62
+ build_var_ref('x')
63
+ end
64
+ let(:var_y) { build_var('y') }
65
+ let(:ref_y) do
66
+ dummy = var_y # Force dependency
67
+ build_var_ref('y')
68
+ end
69
+
70
+
71
+ it 'should succeed for equal literal arguments' do
72
+ result = solve_for(pea, pea)
73
+
74
+ expect(result).to be_kind_of(Outcome)
75
+ expect(result.resultant).to eq(:"#s")
76
+ expect(result.associations).to be_empty
77
+ expect(var_q.fresh?(env)).to be_truthy
78
+ end
79
+
80
+ it 'should fail for inequal literal arguments' do
81
+ result = solve_for(pea, pod)
82
+
83
+ expect(result.resultant).to eq(:"#u")
84
+ expect(result.associations).to be_empty
85
+ expect(var_q.fresh?(env)).to be_truthy
86
+ end
87
+
88
+ it 'should fail for one left literal and one composite arguments' do
89
+ result = solve_for(pea, sample_cons)
90
+
91
+ expect(result.resultant).to eq(:"#u")
92
+ expect(result.associations).to be_empty
93
+ expect(var_q.fresh?(env)).to be_truthy
94
+ end
95
+
96
+ it 'should fail for one right literal and one composite arguments' do
97
+ result = solve_for(sample_cons, pea)
98
+
99
+ expect(result.resultant).to eq(:"#u")
100
+ expect(result.associations).to be_empty
101
+ expect(var_q.fresh?(env)).to be_truthy
102
+ end
103
+
104
+ it 'should succeed for a right-handed fresh argument' do
105
+ result = solve_for(pea, ref_q)
106
+
107
+ expect(result).to be_successful
108
+ expect(env.associations.size).to eq(1)
109
+ expect(env.associations['q'].first.value).to eq(pea)
110
+ expect(var_q.fresh?(result)).to be_falsey
111
+ expect(ref_q.values(result).first).to eq(pea)
112
+ end
113
+
114
+ it 'should succeed for a left-handed fresh argument' do
115
+ result = solve_for(ref_q, pea)
116
+
117
+ expect(result).to be_successful
118
+ expect(env.associations.size).to eq(1)
119
+ expect(env.associations['q'].first.value).to eq(pea)
120
+ expect(var_q.fresh?(result)).to be_falsey
121
+ expect(ref_q.values(result).first).to eq(pea)
122
+ end
123
+
124
+ it 'should succeed for a right-handed bound argument equal constant' do
125
+ ref_q.associate(pod, env)
126
+
127
+ result = solve_for(pod, ref_q)
128
+ expect(result).to be_successful
129
+ expect(env.associations.size).to eq(1) # No new association
130
+ expect(ref_q.fresh?(result)).not_to be_truthy
131
+ expect(ref_q.values(result).first).to eq(pod)
132
+ end
133
+
134
+ it 'should succeed for a left-handed bound argument equal constant' do
135
+ ref_q.associate(pod, env)
136
+
137
+ result = solve_for(ref_q, pod)
138
+ expect(result).to be_successful
139
+ expect(result.associations).to be_empty
140
+ expect(ref_q.fresh?(result)).to be_falsey
141
+ expect(ref_q.values(result).first).to eq(pod)
142
+ end
143
+
144
+ it 'should fail for a right-handed bound argument to a distinct literal' do
145
+ ref_q.associate(pod, env)
146
+
147
+ result = solve_for(pea, ref_q)
148
+ expect(result).not_to be_successful
149
+ expect(result.associations).to be_empty
150
+ expect(ref_q.fresh?(result)).to be_falsey
151
+ expect(ref_q.values(result).first).to eq(pod)
152
+ end
153
+
154
+ it 'should fail for a left-handed bound argument to a distinct literal' do
155
+ ref_q.associate(pod, env)
156
+
157
+ result = solve_for(ref_q, pea)
158
+ expect(result).not_to be_successful
159
+ expect(result.associations).to be_empty
160
+ expect(ref_q.fresh?(result)).to be_falsey
161
+ expect(ref_q.values(result).first).to eq(pod)
162
+ end
163
+
164
+ it 'should succeed for a composite and right-handed fresh argument' do
165
+ result = solve_for(sample_cons, ref_q)
166
+
167
+ expect(result).to be_successful
168
+ expect(env.associations.size).to eq(1)
169
+ expect(ref_q.fresh?(result)).to be_falsey
170
+ expect(ref_q.values(result).first).to eq(sample_cons)
171
+ end
172
+
173
+ it 'should succeed for composite and left-handed fresh argument' do
174
+ result = solve_for(ref_q, sample_cons)
175
+
176
+ expect(result).to be_successful
177
+ expect(env.associations.size).to eq(1)
178
+ expect(ref_q.fresh?(result)).to be_falsey
179
+ expect(ref_q.values(result).first).to eq(sample_cons)
180
+ end
181
+
182
+ it 'should succeed for a right-handed bound equal argument' do
183
+ ref_q.associate(sample_cons, env)
184
+ composite = ConsCell.new(pea)
185
+ result = solve_for(composite, ref_q)
186
+
187
+ expect(result).to be_successful
188
+ expect(result.associations).to be_empty
189
+ expect(ref_q.fresh?(result)).not_to be_truthy
190
+ expect(ref_q.values(result).first).to eq(sample_cons)
191
+ end
192
+
193
+ it 'should succeed for a left-handed bound equal argument' do
194
+ ref_q.associate(sample_cons, env)
195
+ composite = ConsCell.new(pea)
196
+ result = solve_for(ref_q, composite)
197
+
198
+ expect(result).to be_successful
199
+ expect(result.associations).to be_empty
200
+ expect(ref_q.fresh?(result)).not_to be_truthy
201
+ expect(ref_q.values(result).first).to eq(sample_cons)
202
+ end
203
+
204
+ it 'should succeed for a right-handed bound unequal argument' do
205
+ ref_q.associate(sample_cons, env)
206
+ composite = ConsCell.new(pod)
207
+ result = solve_for(composite, ref_q)
208
+
209
+ expect(result).not_to be_successful
210
+ expect(result.associations).to be_empty
211
+ expect(ref_q.fresh?(result)).not_to be_truthy
212
+ expect(ref_q.values(result).first).to eq(sample_cons)
213
+ end
214
+
215
+ it 'should succeed for a left-handed bound unequal argument' do
216
+ ref_q.associate(sample_cons, env)
217
+ composite = ConsCell.new(pod)
218
+ result = solve_for(ref_q, composite)
219
+
220
+ expect(result).not_to be_successful
221
+ expect(result.associations).to be_empty
222
+ expect(ref_q.fresh?(result)).not_to be_truthy
223
+ expect(ref_q.values(result).first).to eq(sample_cons)
224
+ end
225
+
226
+ it 'should succeed for both identical fresh arguments' do
227
+ result = solve_for(ref_q, ref_q)
228
+
229
+ expect(result).to be_successful
230
+ expect(result.associations).to be_empty
231
+ expect(ref_q.fresh?(result)).to be_truthy
232
+ end
233
+
234
+ it 'should succeed for both same fresh arguments' do
235
+ result = solve_for(ref_q, ref_q_bis)
236
+
237
+ expect(result).to be_successful
238
+ expect(result.associations).to be_empty
239
+ expect(ref_q.fresh?(result)).to be_truthy
240
+ expect(ref_q_bis.fresh?(result)).to be_truthy
241
+ end
242
+
243
+ it 'should succeed for both distinct fresh arguments' do
244
+ result = solve_for(ref_x, ref_y)
245
+
246
+ expect(result).to be_successful
247
+ expect(env.associations.size).to eq(2) # Symmetric association
248
+ expect(ref_x.fresh?(result)).to be_truthy
249
+ expect(ref_y.fresh?(result)).to be_truthy
250
+ end
251
+
252
+ it 'should succeed for arguments bound to equal values' do
253
+ ref_x.associate(pea, env)
254
+ ref_y.associate(pea, env)
255
+ expect(ref_x.fresh?(env)).to be_falsey
256
+ expect(ref_y.fresh?(env)).to be_falsey
257
+
258
+ result = solve_for(ref_x, ref_y)
259
+ expect(result).to be_successful
260
+ expect(result.associations).to be_empty
261
+ end
262
+
263
+ it 'should fail for arguments bound unequal values' do
264
+ ref_x.associate(pea, env)
265
+ ref_y.associate(pod, env)
266
+ expect(ref_x.fresh?(env)).to be_falsey
267
+ expect(ref_y.fresh?(env)).to be_falsey
268
+
269
+ result = solve_for(ref_x, ref_y)
270
+ expect(result).not_to be_successful
271
+ expect(result.associations).to be_empty
272
+ end
273
+
274
+ it 'should unify composite terms with variables' do
275
+ # Reasoned S2, frame 1:36
276
+ # (run* q (fresh (x) (== '(((,q)) (,x)) `(((,x)) pod)))) ;; => ('pod)
277
+ expr1 = cons(cons(ref_q), ref_x)
278
+ expr2 = cons(cons(ref_x), pod)
279
+
280
+ result = solve_for(expr1, expr2)
281
+ # require 'debug'
282
+ expect(result).to be_successful
283
+ expect(ref_x.fresh?(env)).to be_falsey
284
+ expect(ref_q.fresh?(env)).to be_falsey
285
+ end
286
+ end # context
287
+ end # describe
288
+ end # module
289
+ end # module
@@ -21,6 +21,22 @@ module MiniKraken
21
21
  end # context
22
22
 
23
23
  context 'Provided services:' do
24
+ it 'should unconditionally return a failure result' do
25
+ args = double('fake-args')
26
+ env = double('fake-env')
27
+
28
+ solver = nil
29
+ expect { solver = subject.solver_for(args, env) }.not_to raise_error
30
+
31
+ # Solver should quack like a Fiber
32
+ dummy_arg = double('dummy-stuff')
33
+ result = solver.resume(dummy_arg)
34
+ expect(result).to eq(Failure)
35
+
36
+ # Only one "solution", next 'resume' call should return nil
37
+ result = solver.resume(dummy_arg)
38
+ expect(result).to be_nil
39
+ end
24
40
  end # context
25
41
  end # describe
26
42
  end # module
@@ -1,7 +1,10 @@
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/environment'
5
+ require_relative '../../lib/mini_kraken/core/equals'
4
6
  require_relative '../../lib/mini_kraken/core/fail'
7
+ require_relative '../../lib/mini_kraken/core/k_symbol'
5
8
 
6
9
  # Load the class under test
7
10
  require_relative '../../lib/mini_kraken/core/goal'
@@ -9,26 +12,49 @@ require_relative '../../lib/mini_kraken/core/goal'
9
12
  module MiniKraken
10
13
  module Core
11
14
  describe Goal do
12
- let(:a_relation) { Fail.instance }
13
- subject { Goal.new(a_relation, []) }
15
+ let(:nullary_relation) { Fail.instance }
16
+ subject { Goal.new(nullary_relation, []) }
17
+ let(:binary_relation) { Equals.instance }
18
+ let(:env) { Environment.new }
19
+ subject { Goal.new(binary_relation, [KSymbol.new(:pea), KSymbol.new(:pod)]) }
14
20
 
15
21
  context 'Initialization:' do
16
- it 'should accept one goal and argument array' do
17
- expect { Goal.new(a_relation, []) }.not_to raise_error
22
+ it 'should accept one nullary relation and empty argument array' do
23
+ expect { Goal.new(nullary_relation, []) }.not_to raise_error
18
24
  end
25
+
26
+ it 'should accept one binary relation and 2-elements array' do
27
+ expect { Goal.new(binary_relation, [KSymbol.new(:pea), KSymbol.new(:pod)]) }.not_to raise_error
28
+ end
19
29
 
20
30
  it 'should know its relation' do
21
- expect(subject.relation).to eq(a_relation)
31
+ expect(subject.relation).to eq(binary_relation)
32
+ end
33
+
34
+ it 'should know its actual arguments' do
35
+ expectations = [KSymbol.new(:pea), KSymbol.new(:pod)]
36
+ expect(subject.actuals).to eq(expectations)
22
37
  end
23
38
  end # context
24
39
 
25
40
  context 'Provided services:' do
26
- it 'should attain its intended purpose' do
27
- pub = double('fake-publisher')
28
- expect(pub).to receive(:broadcast_entry).with(subject, [])
29
- expect(pub).to receive(:broadcast_exit).with(subject, [], [])
30
- expect(subject.attain(pub, [])).to eq([])
41
+ it 'should fail if relation does not succeed' do
42
+ solver = subject.attain(env)
43
+ expect(solver.resume).not_to be_successful
44
+
45
+ # No more solution...
46
+ expect(solver.resume).to be_nil
31
47
  end
48
+
49
+ it 'should succeed if relation succeeds' do
50
+ instance = Goal.new(binary_relation, [KSymbol.new(:pea), KSymbol.new(:pea)])
51
+
52
+ solver = instance.attain(env)
53
+ expect(solver.resume).to be_successful
54
+
55
+ # No more solution...
56
+ expect(solver.resume).to be_nil
57
+ end
32
58
  end # context
33
59
  end # describe
34
60
  end # module
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../spec_helper' # Use the RSpec framework
4
+ require 'ostruct'
5
+
6
+ # Load the class under test
7
+ require_relative '../../lib/mini_kraken/core/k_symbol'
8
+
9
+ module MiniKraken
10
+ module Core
11
+ describe KSymbol do
12
+ let(:a_value) { :pea }
13
+ subject { KSymbol.new(a_value) }
14
+
15
+ context 'Initialization:' do
16
+ it 'should be created with a Ruby symbol' do
17
+ expect { KSymbol.new(a_value) }.not_to raise_error
18
+ end
19
+
20
+ it 'should know its value' do
21
+ expect(subject.value).to eq(a_value)
22
+ end
23
+
24
+ it 'should know that it is a ground term' do
25
+ env = double('mock-env')
26
+ visitees = double('fake-visitees')
27
+ expect(subject.ground?(env)).to be_truthy
28
+ end
29
+ end # context
30
+
31
+ context 'Provided services:' do
32
+ it 'should know whether it is equal to another instance' do
33
+ # Same type, same value
34
+ other = KSymbol.new(a_value)
35
+ expect(subject).to be_eql(other)
36
+
37
+ # Same type, other value
38
+ another = KSymbol.new(:pod)
39
+ expect(subject).not_to be_eql(another)
40
+
41
+ # Different type, same value
42
+ yet_another = OpenStruct.new(:value => :pea)
43
+ expect(subject).not_to be_eql(yet_another)
44
+ end
45
+
46
+ it 'should know whether it has same value than other object' do
47
+ # Same type, same value
48
+ other = KSymbol.new(a_value)
49
+ expect(subject == other).to be_truthy
50
+
51
+ # Same type, other value
52
+ another = KSymbol.new(:pod)
53
+ expect(subject == another).to be_falsy
54
+
55
+ # Same duck type, same value
56
+ yet_another = OpenStruct.new(:value => :pea)
57
+ expect(subject == yet_another).to be_truthy
58
+
59
+ # Different duck type, different value
60
+ still_another = OpenStruct.new(:value => :pod)
61
+ expect(subject == still_another).to be_falsy
62
+
63
+ # Default Ruby representation, same value
64
+ expect(subject == :pea).to be_truthy
65
+
66
+ # Default Ruby representation, different value
67
+ expect(subject == :pod).to be_falsy
68
+ end
69
+ end # context
70
+ end # describe
71
+ end # module
72
+ end # module