mini_kraken 0.1.01 → 0.1.02

Sign up to get free protection for your applications and to get access to all the features.
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