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
@@ -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
@@ -25,37 +25,47 @@ module MiniKraken
25
25
 
26
26
  context 'Provided services:' do
27
27
  let(:parent) { Environment.new }
28
-
28
+
29
29
  it 'should behave like a Fiber yielding a failure' do
30
30
  failing = DuckFiber.new(:failure)
31
31
  outcome = nil
32
32
  expect { outcome = failing.resume }.not_to raise_error
33
33
  expect(outcome).to eq(Failure)
34
-
34
+
35
35
  # Only one result should be yielded
36
- expect(failing.resume).to be_nil
36
+ expect(failing.resume).to be_nil
37
37
  end
38
-
38
+
39
39
  it 'should behave like a Fiber yielding a basic success' do
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)
44
-
43
+ expect(outcome).to be_successful
44
+ expect(outcome.parent).to be_nil
45
+
45
46
  # Only one result should be yielded
46
47
  expect(succeeding.resume).to be_nil
47
- end
48
+ end
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
48
59
 
49
60
  it 'should behave like a Fiber yielding a custom outcome' do
50
-
51
- tailored = DuckFiber.new(:custom) { Outcome.new(:"#s", parent) }
61
+ tailored = DuckFiber.new(:custom) { Outcome.new(:"#s", parent) }
52
62
  outcome = nil
53
63
  expect { outcome = tailored.resume }.not_to raise_error
54
64
  expect(outcome).to eq(Outcome.new(:"#s", parent))
55
-
65
+
56
66
  # Only one result should be yielded
57
- expect(tailored.resume).to be_nil
58
- end
67
+ expect(tailored.resume).to be_nil
68
+ end
59
69
  end # context
60
70
  end # describe
61
71
  end # module
@@ -4,7 +4,6 @@ require_relative '../spec_helper' # Use the RSpec framework
4
4
  require_relative '../../lib/mini_kraken/core/k_symbol'
5
5
  require_relative '../../lib/mini_kraken/core/variable'
6
6
  require_relative '../../lib/mini_kraken/core/variable_ref'
7
- require_relative '../../lib/mini_kraken/core/association'
8
7
  require_relative '../../lib/mini_kraken/core/outcome'
9
8
 
10
9
  # Load the class under test
@@ -38,7 +37,6 @@ module MiniKraken
38
37
  child = Environment.new(subject)
39
38
  expect(child.parent).to eq(subject)
40
39
  end
41
-
42
40
  end # context
43
41
 
44
42
  context 'Provided services:' do
@@ -66,8 +64,7 @@ module MiniKraken
66
64
 
67
65
  it 'should accept the addition of an association' do
68
66
  subject.add_var(var_a)
69
- assoc = Association.new('a', pea)
70
- subject.add_assoc(assoc)
67
+ assoc = subject.add_assoc('a', pea)
71
68
  expect(subject.associations.size).to eq(1)
72
69
  expect(subject.associations['a']).to eq([assoc])
73
70
  end
@@ -83,25 +80,21 @@ module MiniKraken
83
80
  subject.add_var(var_a)
84
81
 
85
82
  # Let's associate an atomic term...
86
- assoc = Association.new('a', pea)
87
- subject.add_assoc(assoc)
83
+ subject.add_assoc('a', pea)
88
84
 
89
85
  expect(subject.fresh?(var_a)).to be_falsey
90
86
  end
91
87
 
92
- it "should cope with a variable associated with another variable" do
88
+ it 'should cope with a variable associated with another variable' do
93
89
  subject.add_var(var_a)
94
90
  subject.add_var(var_b)
95
91
 
96
92
  # Let's associate a with (fresh) b
97
- assoc = Association.new(var_a, var_b)
98
- subject.add_assoc(assoc)
99
-
93
+ subject.add_assoc(var_a, var_b)
100
94
  expect(subject.fresh?(var_a)).to be_truthy
101
95
 
102
96
  # Now associate b with something ground...
103
- assoc_b = Association.new(var_b, pea)
104
- subject.add_assoc(assoc_b)
97
+ subject.add_assoc(var_b, pea)
105
98
 
106
99
  # b is no more fresh, so is ... a
107
100
  expect(subject.fresh?(var_b)).to be_falsey
@@ -110,12 +103,10 @@ module MiniKraken
110
103
 
111
104
  it 'should remove all associations' do
112
105
  subject.add_var(var_a)
113
- assoc = Association.new(var_a, pea)
114
- subject.add_assoc(assoc)
106
+ subject.add_assoc(var_a, pea)
115
107
 
116
108
  subject.add_var(var_b)
117
- assoc = Association.new(var_b, pod)
118
- subject.add_assoc(assoc)
109
+ subject.add_assoc(var_b, pod)
119
110
 
120
111
  subject.clear
121
112
  expect(subject.fresh?(var_a)).to be_truthy
@@ -125,30 +116,27 @@ module MiniKraken
125
116
  it 'should propagate associations up in the environment hierarchy' do
126
117
  parent = Environment.new
127
118
  parent.add_var(var_a)
128
- # parent.add_var(var_c)
129
119
  instance = Environment.new(parent)
130
120
  instance.add_var(var_b)
131
- # instance.add_var(var_c_bis) # Should shadow parent's c variable
132
121
 
133
122
  outcome = Outcome.new(:"#s", instance)
134
- outcome.add_assoc(Association.new(var_a, pea))
135
- outcome.add_assoc(Association.new(var_b, pod))
136
- # outcome.add_assoc(Association.new(var_c_bis, pad))
137
- expect(outcome.associations.size).to eq(2)
138
-
123
+ outcome.add_assoc(var_a, pea)
124
+ outcome.add_assoc(var_b, pod)
125
+ expect(outcome.associations.size).to eq(2)
126
+
139
127
  # Propagate: outcome -> .. -> instance
140
- instance.propagate(outcome)
128
+ instance.propagate(outcome)
141
129
  expect(outcome.associations.size).to eq(1)
142
130
  expect(instance.associations[var_b.name]).not_to be_nil
143
131
  expect(parent.associations[var_a.name]).to be_nil
144
-
132
+
145
133
  # Propagate: outcome -> .. -> parent
146
134
  parent.propagate(outcome)
147
- expect(outcome.associations).to be_empty
135
+ expect(outcome.associations).to be_empty
148
136
  expect(parent.associations[var_b.name]).to be_nil
149
- expect(parent.associations[var_a.name]).not_to be_nil
137
+ expect(parent.associations[var_a.name]).not_to be_nil
150
138
  end
151
139
  end # context
152
140
  end # describe
153
141
  end # module
154
- end # module
142
+ end # module
@@ -49,21 +49,21 @@ module MiniKraken
49
49
  let(:env) { Environment.new }
50
50
  let(:var_q) { build_var('q') }
51
51
  let(:ref_q) do
52
- dummy = var_q # Force dependency
52
+ var_q # Force dependency
53
53
  build_var_ref('q')
54
54
  end
55
55
  let(:ref_q_bis) do
56
- dummy = var_q # Force dependency
56
+ var_q # Force dependency
57
57
  build_var_ref('q')
58
58
  end
59
59
  let(:var_x) { build_var('x') }
60
60
  let(:ref_x) do
61
- dummy = var_x # Force dependency
61
+ var_x # Force dependency
62
62
  build_var_ref('x')
63
63
  end
64
64
  let(:var_y) { build_var('y') }
65
65
  let(:ref_y) do
66
- dummy = var_y # Force dependency
66
+ var_y # Force dependency
67
67
  build_var_ref('y')
68
68
  end
69
69
 
@@ -246,7 +246,7 @@ module MiniKraken
246
246
  result = solve_for(ref_x, ref_y)
247
247
 
248
248
  expect(result).to be_successful
249
- expect(env.associations.size).to eq(2) # Symmetric association
249
+ expect(env.associations).to be_empty # Symmetric association
250
250
  expect(ref_x.fresh?(result)).to be_truthy
251
251
  expect(ref_y.fresh?(result)).to be_truthy
252
252
  end
@@ -285,7 +285,7 @@ module MiniKraken
285
285
  expect(ref_x.fresh?(env)).to be_falsey
286
286
  expect(ref_q.fresh?(env)).to be_falsey
287
287
  end
288
-
288
+
289
289
  it 'should fail for one right literal and one composite arguments' do
290
290
  # Reasoned S2, frame 1:46
291
291
  # x associated to ('pea 'pod)
@@ -297,7 +297,7 @@ module MiniKraken
297
297
 
298
298
  expect(result.resultant).to eq(:"#u")
299
299
  expect(result.associations).to be_empty
300
- end
300
+ end
301
301
  end # context
302
302
  end # describe
303
303
  end # module
@@ -3,7 +3,7 @@
3
3
  require_relative '../spec_helper' # Use the RSpec framework
4
4
 
5
5
  # Load the class under test
6
- require_relative '../../lib/mini_kraken/core/fail'
6
+ require_relative '../../lib/mini_kraken/core/fail'
7
7
 
8
8
  module MiniKraken
9
9
  module Core
@@ -14,7 +14,7 @@ module MiniKraken
14
14
  it 'should have one instance' do
15
15
  expect { Fail.instance }.not_to raise_error
16
16
  end
17
-
17
+
18
18
  it 'should know its name' do
19
19
  expect(subject.name).to eq('fail')
20
20
  end
@@ -24,20 +24,20 @@ module MiniKraken
24
24
  it 'should unconditionally return a failure 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(Failure)
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
42
42
  end # module
43
- end # module
43
+ end # module
@@ -22,15 +22,15 @@ module MiniKraken
22
22
  it 'should accept one nullary relation and empty argument array' do
23
23
  expect { Goal.new(nullary_relation, []) }.not_to raise_error
24
24
  end
25
-
25
+
26
26
  it 'should accept one binary relation and 2-elements array' do
27
27
  expect { Goal.new(binary_relation, [KSymbol.new(:pea), KSymbol.new(:pod)]) }.not_to raise_error
28
- end
28
+ end
29
29
 
30
30
  it 'should know its relation' do
31
31
  expect(subject.relation).to eq(binary_relation)
32
32
  end
33
-
33
+
34
34
  it 'should know its actual arguments' do
35
35
  expectations = [KSymbol.new(:pea), KSymbol.new(:pod)]
36
36
  expect(subject.actuals).to eq(expectations)
@@ -41,21 +41,21 @@ module MiniKraken
41
41
  it 'should fail if relation does not succeed' do
42
42
  solver = subject.attain(env)
43
43
  expect(solver.resume).not_to be_successful
44
-
44
+
45
45
  # No more solution...
46
46
  expect(solver.resume).to be_nil
47
47
  end
48
-
48
+
49
49
  it 'should succeed if relation succeeds' do
50
- instance = Goal.new(binary_relation, [KSymbol.new(:pea), KSymbol.new(:pea)])
51
-
50
+ instance = Goal.new(binary_relation, [KSymbol.new(:pea), KSymbol.new(:pea)])
51
+
52
52
  solver = instance.attain(env)
53
53
  expect(solver.resume).to be_successful
54
-
54
+
55
55
  # No more solution...
56
56
  expect(solver.resume).to be_nil
57
- end
57
+ end
58
58
  end # context
59
59
  end # describe
60
60
  end # module
61
- end # module
61
+ end # module
@@ -20,12 +20,11 @@ module MiniKraken
20
20
  it 'should know its value' do
21
21
  expect(subject.value).to eq(a_value)
22
22
  end
23
-
23
+
24
24
  it 'should know that it is a ground term' do
25
25
  env = double('mock-env')
26
- visitees = double('fake-visitees')
27
26
  expect(subject.ground?(env)).to be_truthy
28
- end
27
+ end
29
28
  end # context
30
29
 
31
30
  context 'Provided services:' do
@@ -39,7 +38,7 @@ module MiniKraken
39
38
  expect(subject).not_to be_eql(another)
40
39
 
41
40
  # Different type, same value
42
- yet_another = OpenStruct.new(:value => :pea)
41
+ yet_another = OpenStruct.new(value: :pea)
43
42
  expect(subject).not_to be_eql(yet_another)
44
43
  end
45
44
 
@@ -53,11 +52,11 @@ module MiniKraken
53
52
  expect(subject == another).to be_falsy
54
53
 
55
54
  # Same duck type, same value
56
- yet_another = OpenStruct.new(:value => :pea)
55
+ yet_another = OpenStruct.new(value: :pea)
57
56
  expect(subject == yet_another).to be_truthy
58
57
 
59
58
  # Different duck type, different value
60
- still_another = OpenStruct.new(:value => :pod)
59
+ still_another = OpenStruct.new(value: :pod)
61
60
  expect(subject == still_another).to be_falsy
62
61
 
63
62
  # Default Ruby representation, same value