mini_kraken 0.2.03 → 0.3.03

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 (136) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +378 -333
  3. data/CHANGELOG.md +48 -0
  4. data/README.md +29 -21
  5. data/lib/mini_kraken/atomic/all_atomic.rb +5 -0
  6. data/lib/mini_kraken/atomic/atomic_term.rb +96 -0
  7. data/lib/mini_kraken/atomic/k_boolean.rb +42 -0
  8. data/lib/mini_kraken/{core → atomic}/k_integer.rb +2 -5
  9. data/lib/mini_kraken/atomic/k_string.rb +17 -0
  10. data/lib/mini_kraken/{core → atomic}/k_symbol.rb +4 -8
  11. data/lib/mini_kraken/composite/all_composite.rb +4 -0
  12. data/lib/mini_kraken/composite/composite_term.rb +27 -0
  13. data/lib/mini_kraken/composite/cons_cell.rb +301 -0
  14. data/lib/mini_kraken/composite/cons_cell_visitor.rb +50 -0
  15. data/lib/mini_kraken/composite/list.rb +32 -0
  16. data/lib/mini_kraken/core/all_core.rb +8 -0
  17. data/lib/mini_kraken/core/any_value.rb +31 -7
  18. data/lib/mini_kraken/core/arity.rb +69 -0
  19. data/lib/mini_kraken/core/association.rb +29 -4
  20. data/lib/mini_kraken/core/association_copy.rb +50 -0
  21. data/lib/mini_kraken/core/base_term.rb +13 -0
  22. data/lib/mini_kraken/core/blackboard.rb +315 -0
  23. data/lib/mini_kraken/core/bookmark.rb +46 -0
  24. data/lib/mini_kraken/core/context.rb +492 -0
  25. data/lib/mini_kraken/core/duck_fiber.rb +21 -19
  26. data/lib/mini_kraken/core/entry.rb +40 -0
  27. data/lib/mini_kraken/core/fail.rb +20 -18
  28. data/lib/mini_kraken/core/fusion.rb +29 -0
  29. data/lib/mini_kraken/core/goal.rb +20 -29
  30. data/lib/mini_kraken/core/log_var.rb +22 -0
  31. data/lib/mini_kraken/core/log_var_ref.rb +108 -0
  32. data/lib/mini_kraken/core/nullary_relation.rb +2 -9
  33. data/lib/mini_kraken/core/parametrized_term.rb +68 -0
  34. data/lib/mini_kraken/core/relation.rb +14 -28
  35. data/lib/mini_kraken/core/scope.rb +67 -0
  36. data/lib/mini_kraken/core/solver_adapter.rb +58 -0
  37. data/lib/mini_kraken/core/specification.rb +48 -0
  38. data/lib/mini_kraken/core/succeed.rb +21 -17
  39. data/lib/mini_kraken/core/symbol_table.rb +137 -0
  40. data/lib/mini_kraken/core/term.rb +15 -4
  41. data/lib/mini_kraken/glue/dsl.rb +44 -88
  42. data/lib/mini_kraken/glue/run_star_expression.rb +28 -30
  43. data/lib/mini_kraken/rela/all_rela.rb +8 -0
  44. data/lib/mini_kraken/rela/binary_relation.rb +30 -0
  45. data/lib/mini_kraken/rela/conde.rb +143 -0
  46. data/lib/mini_kraken/rela/conj2.rb +65 -0
  47. data/lib/mini_kraken/rela/def_relation.rb +93 -0
  48. data/lib/mini_kraken/rela/disj2.rb +70 -0
  49. data/lib/mini_kraken/rela/fresh.rb +98 -0
  50. data/lib/mini_kraken/{core → rela}/goal_relation.rb +7 -9
  51. data/lib/mini_kraken/rela/unify.rb +265 -0
  52. data/lib/mini_kraken/version.rb +1 -1
  53. data/mini_kraken.gemspec +2 -2
  54. data/spec/.rubocop.yml +1 -1
  55. data/spec/atomic/atomic_term_spec.rb +98 -0
  56. data/spec/{core → atomic}/k_boolean_spec.rb +19 -34
  57. data/spec/{core → atomic}/k_symbol_spec.rb +3 -16
  58. data/spec/composite/cons_cell_spec.rb +225 -0
  59. data/spec/{core → composite}/cons_cell_visitor_spec.rb +36 -20
  60. data/spec/composite/list_spec.rb +50 -0
  61. data/spec/core/any_value_spec.rb +52 -0
  62. data/spec/core/arity_spec.rb +92 -0
  63. data/spec/core/association_copy_spec.rb +69 -0
  64. data/spec/core/association_spec.rb +31 -4
  65. data/spec/core/blackboard_spec.rb +287 -0
  66. data/spec/core/bookmark_spec.rb +40 -0
  67. data/spec/core/context_spec.rb +245 -0
  68. data/spec/core/core_spec.rb +40 -0
  69. data/spec/core/duck_fiber_spec.rb +16 -46
  70. data/spec/core/fail_spec.rb +5 -6
  71. data/spec/core/goal_spec.rb +22 -12
  72. data/spec/core/log_var_ref_spec.rb +105 -0
  73. data/spec/core/log_var_spec.rb +64 -0
  74. data/spec/core/nullary_relation_spec.rb +33 -0
  75. data/spec/core/parametrized_tem_spec.rb +39 -0
  76. data/spec/core/relation_spec.rb +33 -0
  77. data/spec/core/scope_spec.rb +73 -0
  78. data/spec/core/solver_adapter_spec.rb +70 -0
  79. data/spec/core/specification_spec.rb +43 -0
  80. data/spec/core/succeed_spec.rb +5 -5
  81. data/spec/core/symbol_table_spec.rb +142 -0
  82. data/spec/glue/dsl_chap1_spec.rb +88 -144
  83. data/spec/glue/dsl_chap2_spec.rb +454 -19
  84. data/spec/glue/run_star_expression_spec.rb +81 -906
  85. data/spec/rela/conde_spec.rb +153 -0
  86. data/spec/rela/conj2_spec.rb +123 -0
  87. data/spec/rela/def_relation_spec.rb +119 -0
  88. data/spec/rela/disj2_spec.rb +117 -0
  89. data/spec/rela/fresh_spec.rb +147 -0
  90. data/spec/rela/unify_spec.rb +369 -0
  91. data/spec/support/factory_atomic.rb +29 -0
  92. data/spec/support/factory_composite.rb +21 -0
  93. data/spec/support/factory_methods.rb +11 -26
  94. metadata +98 -70
  95. data/lib/mini_kraken/core/association_walker.rb +0 -183
  96. data/lib/mini_kraken/core/atomic_term.rb +0 -67
  97. data/lib/mini_kraken/core/base_arg.rb +0 -10
  98. data/lib/mini_kraken/core/binary_relation.rb +0 -63
  99. data/lib/mini_kraken/core/composite_goal.rb +0 -46
  100. data/lib/mini_kraken/core/composite_term.rb +0 -41
  101. data/lib/mini_kraken/core/conde.rb +0 -143
  102. data/lib/mini_kraken/core/conj2.rb +0 -79
  103. data/lib/mini_kraken/core/cons_cell.rb +0 -82
  104. data/lib/mini_kraken/core/cons_cell_visitor.rb +0 -102
  105. data/lib/mini_kraken/core/def_relation.rb +0 -53
  106. data/lib/mini_kraken/core/designation.rb +0 -55
  107. data/lib/mini_kraken/core/disj2.rb +0 -72
  108. data/lib/mini_kraken/core/environment.rb +0 -73
  109. data/lib/mini_kraken/core/equals.rb +0 -193
  110. data/lib/mini_kraken/core/formal_arg.rb +0 -22
  111. data/lib/mini_kraken/core/formal_ref.rb +0 -25
  112. data/lib/mini_kraken/core/freshness.rb +0 -45
  113. data/lib/mini_kraken/core/goal_arg.rb +0 -12
  114. data/lib/mini_kraken/core/goal_template.rb +0 -102
  115. data/lib/mini_kraken/core/k_boolean.rb +0 -35
  116. data/lib/mini_kraken/core/outcome.rb +0 -63
  117. data/lib/mini_kraken/core/variable.rb +0 -41
  118. data/lib/mini_kraken/core/variable_ref.rb +0 -84
  119. data/lib/mini_kraken/core/vocabulary.rb +0 -446
  120. data/lib/mini_kraken/glue/fresh_env.rb +0 -103
  121. data/lib/mini_kraken/glue/fresh_env_factory.rb +0 -83
  122. data/spec/core/association_walker_spec.rb +0 -192
  123. data/spec/core/conde_spec.rb +0 -147
  124. data/spec/core/conj2_spec.rb +0 -114
  125. data/spec/core/cons_cell_spec.rb +0 -107
  126. data/spec/core/def_relation_spec.rb +0 -97
  127. data/spec/core/disj2_spec.rb +0 -99
  128. data/spec/core/environment_spec.rb +0 -142
  129. data/spec/core/equals_spec.rb +0 -317
  130. data/spec/core/goal_template_spec.rb +0 -74
  131. data/spec/core/outcome_spec.rb +0 -56
  132. data/spec/core/variable_ref_spec.rb +0 -30
  133. data/spec/core/variable_spec.rb +0 -35
  134. data/spec/core/vocabulary_spec.rb +0 -219
  135. data/spec/glue/fresh_env_factory_spec.rb +0 -97
  136. data/spec/glue/fresh_env_spec.rb +0 -62
@@ -0,0 +1,153 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../spec_helper' # Use the RSpec framework
4
+ require_relative '../support/factory_atomic'
5
+ require_relative '../../lib/mini_kraken/core/fail'
6
+ require_relative '../../lib/mini_kraken/core/succeed'
7
+ require_relative '../../lib/mini_kraken/core/context'
8
+ require_relative '../../lib/mini_kraken/core/log_var'
9
+ require_relative '../../lib/mini_kraken/core/log_var_ref'
10
+ require_relative '../../lib/mini_kraken/rela/unify'
11
+
12
+ # Load the class under test
13
+ require_relative '../../lib/mini_kraken/rela/conde'
14
+
15
+ module MiniKraken
16
+ module Rela
17
+ describe Conde do
18
+ include MiniKraken::FactoryAtomic # Use mix-in module
19
+
20
+ subject { Conde.instance }
21
+
22
+ context 'Initialization:' do
23
+ it 'should be initialized without argument' do
24
+ expect { Conde.instance }.not_to raise_error
25
+ end
26
+
27
+ it 'should know its name' do
28
+ expect(subject.name).to eq('conde')
29
+ end
30
+ end # context
31
+
32
+ context 'Provided services:' do
33
+ let(:bean) { k_symbol(:bean) }
34
+ let(:corn) { k_symbol(:corn) }
35
+ let(:meal) { k_symbol(:meal) }
36
+ let(:oil) { k_symbol(:oil) }
37
+ let(:olive) { k_symbol(:olive) }
38
+ let(:pea) { k_symbol(:pea) }
39
+ let(:red) { k_symbol(:red) }
40
+ let(:split) { k_symbol(:split) }
41
+ let(:fails) { Core::Goal.new(Core::Fail.instance, []) }
42
+ let(:succeeds) { Core::Goal.new(Succeed.instance, []) }
43
+ let(:var_q) { Core::LogVar.new('q') }
44
+ let(:var_x) { Core::LogVar.new('x') }
45
+ let(:var_y) { Core::LogVar.new('y') }
46
+ let(:ref_q) { Core::LogVarRef.new('q') }
47
+ let(:ref_x) { Core::LogVarRef.new('x') }
48
+ let(:ref_y) { Core::LogVarRef.new('y') }
49
+ let(:ctx) do
50
+ e = Core::Context.new
51
+ e.add_vars(%w[q x y])
52
+
53
+ e
54
+ end
55
+
56
+ it 'should complain when one of its argument is not a goal' do
57
+ err = StandardError
58
+ expect { subject.solver_for([succeeds, pea], ctx) }.to raise_error(err)
59
+ expect { subject.solver_for([pea, succeeds], ctx) }.to raise_error(err)
60
+ end
61
+
62
+ it 'should fail when all goals fail' do
63
+ solver = subject.solver_for([fails, fails, fails], ctx)
64
+ expect(solver.resume).not_to be_success
65
+ expect(solver.resume).to be_nil
66
+ end
67
+
68
+ it 'yield success if first argument succeeds' do
69
+ subgoal = Core::Goal.new(Unify.instance, [olive, ref_q])
70
+ solver = subject.solver_for([subgoal, fails, fails], ctx)
71
+ outcome = solver.resume
72
+
73
+ expect(outcome).to be_success
74
+ sol = outcome.build_solution
75
+ expect(sol['q']).to eq(olive)
76
+ expect(solver.resume).to be_nil
77
+ end
78
+
79
+ it 'yield success if second argument succeeds' do
80
+ subgoal = Core::Goal.new(Unify.instance, [oil, ref_q])
81
+ solver = subject.solver_for([fails, subgoal, fails], ctx)
82
+ outcome = solver.resume
83
+ expect(outcome).to be_success
84
+ sol = outcome.build_solution
85
+ expect(sol['q']).to eq(oil)
86
+ expect(solver.resume).to be_nil
87
+ end
88
+
89
+ it 'yield success if third argument succeeds' do
90
+ subgoal = Core::Goal.new(Unify.instance, [oil, ref_q])
91
+ solver = subject.solver_for([fails, fails, subgoal], ctx)
92
+ outcome = solver.resume
93
+ expect(outcome).to be_success
94
+ expect(outcome.build_solution['q']).to eq(oil)
95
+ expect(solver.resume).to be_nil
96
+ end
97
+
98
+ it 'yields three solutions if three goals succeed' do
99
+ # Covers frame 1:58
100
+ subgoal1 = Core::Goal.new(Unify.instance, [olive, ref_q])
101
+ subgoal2 = Core::Goal.new(Unify.instance, [oil, ref_q])
102
+ subgoal3 = Core::Goal.new(Unify.instance, [pea, ref_q])
103
+ solver = subject.solver_for([subgoal1, subgoal2, subgoal3, fails], ctx)
104
+
105
+ # First solution
106
+ outcome1 = solver.resume
107
+ expect(outcome1).to be_success
108
+ expect(outcome1.build_solution['q']).to eq(olive)
109
+
110
+ # Second solution
111
+ outcome2 = solver.resume
112
+ expect(outcome2).to be_success
113
+ expect(outcome2.build_solution['q']).to eq(oil)
114
+
115
+ # Third solution
116
+ outcome3 = solver.resume
117
+ expect(outcome3).to be_success
118
+ expect(outcome3.build_solution['q']).to eq(pea)
119
+
120
+ expect(solver.resume).to be_nil
121
+ end
122
+
123
+ it 'also use conjunctions for nested goals' do
124
+ # Covers frame 1:88
125
+ subgoal1 = Core::Goal.new(Unify.instance, [split, ref_x])
126
+ subgoal2 = Core::Goal.new(Unify.instance, [pea, ref_y])
127
+ combo1 = [subgoal1, subgoal2]
128
+
129
+ subgoal3 = Core::Goal.new(Unify.instance, [red, ref_x])
130
+ subgoal4 = Core::Goal.new(Unify.instance, [bean, ref_y])
131
+ combo2 = [subgoal3, subgoal4]
132
+ solver = subject.solver_for([combo1, combo2], ctx)
133
+
134
+ # First solution
135
+ outcome1 = solver.resume
136
+ expect(outcome1).to be_success
137
+ solution1 = outcome1.build_solution
138
+ expect(solution1['x']).to eq(split)
139
+ expect(solution1['y']).to eq(pea)
140
+
141
+ # Second solution
142
+ outcome2 = solver.resume
143
+ expect(outcome2).to be_success
144
+ solution2 = outcome2.build_solution
145
+ expect(solution2['x']).to eq(red)
146
+ expect(solution2['y']).to eq(bean)
147
+
148
+ expect(solver.resume).to be_nil
149
+ end
150
+ end # context
151
+ end # describe
152
+ end # module
153
+ end # module
@@ -0,0 +1,123 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../spec_helper' # Use the RSpec framework
4
+ require_relative '../../lib/mini_kraken/core/all_core'
5
+
6
+ require_relative '../support/factory_atomic'
7
+ require_relative '../support/factory_composite'
8
+ require_relative '../../lib/mini_kraken/rela/unify'
9
+
10
+ # Load the class under test
11
+ require_relative '../../lib/mini_kraken/rela/conj2'
12
+
13
+ module MiniKraken
14
+ module Rela
15
+ describe Conj2 do
16
+ include MiniKraken::FactoryAtomic # Use mix-in module
17
+ include MiniKraken::FactoryComposite # Use mix-in module
18
+ subject { Conj2.instance }
19
+
20
+
21
+ def var(aName)
22
+ Core::LogVar.new(aName)
23
+ end
24
+
25
+ # Convenience method to factor out repeated statements
26
+ def solve(arg1, arg2)
27
+ solver = subject.solver_for([arg1, arg2], ctx)
28
+ solver.resume
29
+ end
30
+
31
+ context 'Initialization:' do
32
+ it 'should know its relation name' do
33
+ expect(subject.name).to eq('conj2')
34
+ end
35
+
36
+ it 'should know its arity (binary)' do
37
+ expect(subject.arity).to be_binary
38
+ end
39
+
40
+ it 'should be frozen' do
41
+ expect(subject).to be_frozen
42
+ end
43
+ end # context
44
+
45
+ context 'Provided services:' do
46
+ let(:ctx) { Core::Context.new }
47
+ let(:pea) { k_symbol(:pea) }
48
+ let(:corn) { k_symbol(:corn) }
49
+ let(:meal) { k_symbol(:meal) }
50
+ let(:fails) { Core::Goal.new(Core::Fail.instance, []) }
51
+ let(:succeeds) { Core::Goal.new(Core::Succeed.instance, []) }
52
+ let(:var_q) { var('q') }
53
+ let(:ref_q) { Core::LogVarRef.new('q') }
54
+
55
+ def unify(term1, term2)
56
+ Core::Goal.new(Unify.instance, [term1, term2])
57
+ end
58
+
59
+ before(:each) { ctx.add_vars('q') }
60
+
61
+ it 'should complain when one of its argument is not a goal' do
62
+ err = StandardError
63
+ expect { subject.solver_for([succeeds, pea], ctx) }.to raise_error(err)
64
+ expect { subject.solver_for([pea, succeeds], ctx) }.to raise_error(err)
65
+ end
66
+
67
+ it 'should yield one failure if one of the goal is fail' do
68
+ # Fail as first argument
69
+ solver = subject.solver_for([fails, succeeds], ctx)
70
+ expect(solver.resume).not_to be_success
71
+ expect(solver.resume).to be_nil
72
+
73
+ # Fail as second argument
74
+ solver = subject.solver_for([succeeds, fails], ctx)
75
+ expect(solver.resume).not_to be_success
76
+ expect(solver.resume).to be_nil
77
+ end
78
+
79
+ it 'yield success if both arguments are succeed goals' do
80
+ solver = subject.solver_for([succeeds, succeeds], ctx)
81
+ outcome = solver.resume
82
+ expect(outcome).to be_success
83
+ expect(outcome.blackboard).to be_empty
84
+ expect(solver.resume).to be_nil
85
+ end
86
+
87
+ it 'should yield success and set associations' do
88
+ solver = subject.solver_for([succeeds, unify(corn, ref_q)], ctx)
89
+ outcome = solver.resume
90
+ expect(outcome).to be_success
91
+ expect(outcome.blackboard).not_to be_empty
92
+ expect(outcome.associations_for('q').first.value).to eq(corn)
93
+ end
94
+
95
+ it 'should yield fails and set no associations' do
96
+ solver = subject.solver_for([fails, unify(corn, ref_q)], ctx)
97
+ outcome = solver.resume
98
+ expect(outcome).not_to be_success
99
+ expect(outcome.blackboard).to be_empty
100
+ end
101
+
102
+ it 'should yield fails when sub-goals are incompatible' do
103
+ sub_goal1 = unify(corn, ref_q)
104
+ sub_goal2 = unify(meal, ref_q)
105
+ solver = subject.solver_for([sub_goal1, sub_goal2], ctx)
106
+ outcome = solver.resume
107
+ expect(outcome).not_to be_success
108
+ expect(outcome.blackboard).to be_empty
109
+ end
110
+
111
+ it 'should yield success when sub-goals are same and successful' do
112
+ sub_goal1 = unify(corn, ref_q)
113
+ sub_goal2 = unify(ref_q, corn)
114
+ solver = subject.solver_for([sub_goal1, sub_goal2], ctx)
115
+ outcome = solver.resume
116
+ expect(outcome).to be_success
117
+ expect(outcome.blackboard).not_to be_empty
118
+ expect(outcome.associations_for('q').first.value).to eq(corn)
119
+ end
120
+ end # context
121
+ end # describe
122
+ end # module
123
+ end # module
@@ -0,0 +1,119 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../spec_helper' # Use the RSpec framework
4
+ require_relative '../../lib/mini_kraken/core/all_core'
5
+
6
+ require_relative '../support/factory_atomic'
7
+ require_relative '../support/factory_composite'
8
+ require_relative '../../lib/mini_kraken/rela/disj2'
9
+ require_relative '../../lib/mini_kraken/rela/unify'
10
+
11
+ # Load the class under test
12
+ require_relative '../../lib/mini_kraken/rela/def_relation'
13
+
14
+ module MiniKraken
15
+ module Rela
16
+ describe DefRelation do
17
+ include MiniKraken::FactoryAtomic # Use mix-in module
18
+ include MiniKraken::FactoryComposite # Use mix-in module
19
+
20
+ # (defrel (teacupo t) (disj2 (== 'tea t) (== 'cup t)))
21
+ let(:tea) { k_symbol(:tea) }
22
+ let(:cup) { k_symbol(:cup) }
23
+ let(:formal_t) { 't' }
24
+ let(:t_ref) { Core::LogVarRef.new('t') }
25
+ let(:equals_tea) { unify_goal(tea, t_ref) }
26
+ let(:equals_cup) { unify_goal(cup, t_ref) }
27
+ let(:goal_template) { disj2_goal(equals_tea, equals_cup) }
28
+ let(:ctx) { Core::Context.new }
29
+ let(:uuid_pattern) do
30
+ /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/
31
+ end
32
+
33
+ subject { DefRelation.new('teacupo', goal_template, [formal_t]) }
34
+
35
+ def unify_goal(term1, term2)
36
+ Core::Goal.new(Unify.instance, [term1, term2])
37
+ end
38
+
39
+ def disj2_goal(term1, term2)
40
+ Core::Goal.new(Disj2.instance, [term1, term2])
41
+ end
42
+
43
+ context 'Initialization:' do
44
+ it 'should be initialized with a name, a goal template, formal args' do
45
+ expect do
46
+ DefRelation.new('teacupo', goal_template, [formal_t])
47
+ end.not_to raise_error
48
+ end
49
+
50
+ it 'should know its name' do
51
+ expect(subject.name).to eq('teacupo')
52
+ end
53
+
54
+ it 'should know its goal expression' do
55
+ expect(subject.expression).to be_kind_of(Core::Goal)
56
+ expect(subject.expression.relation).to eq(Disj2.instance)
57
+
58
+ g1 = subject.expression.actuals[0]
59
+ expect(g1).to be_kind_of(Core::Goal)
60
+ expect(g1.relation).to eq(Unify.instance)
61
+ expect(g1.actuals[0]).to eq(tea)
62
+ expect(g1.actuals[1]).to be_kind_of(Core::LogVarRef)
63
+ expect(g1.actuals[1].name).to match(/^t_/)
64
+ expect(g1.actuals[1].name).to match(uuid_pattern)
65
+
66
+ g2 = subject.expression.actuals[1]
67
+ expect(g2).to be_kind_of(Core::Goal)
68
+ expect(g2.relation).to eq(Unify.instance)
69
+ expect(g2.actuals[0]).to eq(cup)
70
+ expect(g2.actuals[1]).to be_kind_of(Core::LogVarRef)
71
+ expect(g2.actuals[1].name).to match(/^t_/)
72
+ expect(g2.actuals[1].name).to match(uuid_pattern)
73
+ end
74
+
75
+ it 'should know its formals' do
76
+ expect(subject.formals[0]).to match(/^t_/)
77
+ expect(subject.formals[0]).to match(uuid_pattern)
78
+ end
79
+
80
+ it 'should bear an internal name' do
81
+ expect { subject.i_name }.not_to raise_error
82
+ end
83
+ end # context
84
+
85
+ context 'Provided services:' do
86
+ it 'should provide solver for a single-node goal without ref actual' do
87
+ defrel = DefRelation.new('teao', equals_tea, [formal_t])
88
+ solver = defrel.solver_for([tea], ctx)
89
+ outcome = solver.resume
90
+ expect(outcome).to be_success
91
+ outcome = solver.resume
92
+ expect(outcome).to be_nil
93
+
94
+ solver = defrel.solver_for([cup], ctx)
95
+ outcome = solver.resume
96
+ expect(outcome).not_to be_success
97
+ outcome = solver.resume
98
+ expect(outcome).to be_nil
99
+ end
100
+
101
+ it 'should provide solver for a multiple-nodes goal with ref actual' do
102
+ expr = disj2_goal(equals_tea, equals_cup)
103
+ defrel = DefRelation.new('teacupo', expr, [formal_t])
104
+ x_ref = Core::LogVarRef.new('x')
105
+ ctx.add_vars(['x'])
106
+ solver = defrel.solver_for([x_ref], ctx)
107
+ [tea, cup].each do |predicted|
108
+ outcome = solver.resume
109
+ expect(outcome).to be_success
110
+ sol = ctx.build_solution
111
+ expect(sol['x']).to eq(predicted)
112
+ end
113
+ outcome = solver.resume
114
+ expect(outcome).to be_nil
115
+ end
116
+ end # context
117
+ end # describe
118
+ end # module
119
+ end # module
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../spec_helper' # Use the RSpec framework
4
+ require_relative '../../lib/mini_kraken/core/all_core'
5
+
6
+ require_relative '../support/factory_atomic'
7
+ require_relative '../support/factory_composite'
8
+ require_relative '../../lib/mini_kraken/rela/unify'
9
+
10
+ # Load the class under test
11
+ require_relative '../../lib/mini_kraken/rela/disj2'
12
+
13
+ module MiniKraken
14
+ module Rela
15
+ describe Disj2 do
16
+ include MiniKraken::FactoryAtomic # Use mix-in module
17
+ include MiniKraken::FactoryComposite # Use mix-in module
18
+ subject { Disj2.instance }
19
+
20
+ def var(aName)
21
+ Core::LogVar.new(aName)
22
+ end
23
+
24
+ # Convenience method to factor out repeated statements
25
+ def solve(arg1, arg2)
26
+ solver = subject.solver_for([arg1, arg2], ctx)
27
+ solver.resume
28
+ end
29
+
30
+ context 'Initialization:' do
31
+ it 'should know its relation name' do
32
+ expect(subject.name).to eq('disj2')
33
+ end
34
+
35
+ it 'should know its arity (binary)' do
36
+ expect(subject.arity).to be_binary
37
+ end
38
+
39
+ it 'should be frozen' do
40
+ expect(subject).to be_frozen
41
+ end
42
+ end # context
43
+
44
+ context 'Provided services:' do
45
+ let(:ctx) { Core::Context.new }
46
+ let(:pea) { k_symbol(:pea) }
47
+ let(:corn) { k_symbol(:corn) }
48
+ let(:oil) { k_symbol(:oil) }
49
+ let(:olive) { k_symbol(:olive) }
50
+ let(:meal) { k_symbol(:meal) }
51
+ let(:fails) { Core::Goal.new(Core::Fail.instance, []) }
52
+ let(:succeeds) { Core::Goal.new(Core::Succeed.instance, []) }
53
+ let(:var_q) { var('q') }
54
+ let(:ref_q) { Core::LogVarRef.new('q') }
55
+
56
+ def unify(term1, term2)
57
+ Core::Goal.new(Unify.instance, [term1, term2])
58
+ end
59
+
60
+ before(:each) { ctx.add_vars('q') }
61
+
62
+ it 'should complain when one of its argument is not a goal' do
63
+ err = StandardError
64
+ expect { subject.solver_for([succeeds, pea], ctx) }.to raise_error(err)
65
+ expect { subject.solver_for([pea, succeeds], ctx) }.to raise_error(err)
66
+ end
67
+
68
+
69
+ it 'should fails if both arguments fail' do
70
+ # Covers frame 1:55
71
+ solver = subject.solver_for([fails, fails], ctx)
72
+ expect(solver.resume).not_to be_success
73
+ expect(solver.resume).to be_nil
74
+ end
75
+
76
+ it 'yield success if first argument succeeds' do
77
+ # Covers frame 1:56
78
+ subgoal = Core::Goal.new(Unify.instance, [olive, ref_q])
79
+ solver = subject.solver_for([subgoal, fails], ctx)
80
+ outcome = solver.resume
81
+ expect(outcome).to be_success
82
+ expect(outcome.associations_for('q').first.value).to eq(olive)
83
+ expect(solver.resume).to be_nil
84
+ end
85
+
86
+ it 'yield success if second argument succeeds' do
87
+ # Covers frame 1:57
88
+ subgoal = Core::Goal.new(Unify.instance, [oil, ref_q])
89
+ solver = subject.solver_for([fails, subgoal], ctx)
90
+ outcome = solver.resume
91
+ expect(outcome).to be_success
92
+ expect(outcome.associations_for('q').first.value).to eq(oil)
93
+ expect(solver.resume).to be_nil
94
+ end
95
+
96
+ it 'yield two solutions if both arguments succeed' do
97
+ # Covers frame 1:58
98
+ subgoal1 = Core::Goal.new(Unify.instance, [olive, ref_q])
99
+ subgoal2 = Core::Goal.new(Unify.instance, [oil, ref_q])
100
+ solver = subject.solver_for([subgoal1, subgoal2], ctx)
101
+
102
+ # First solution
103
+ outcome1 = solver.resume
104
+ expect(outcome1).to be_success
105
+ expect(outcome1.associations_for('q').first.value).to eq(olive)
106
+
107
+ # Second solution
108
+ # require 'debug'
109
+ outcome2 = solver.resume
110
+ expect(outcome2).to be_success
111
+ expect(outcome2.associations_for('q').first.value).to eq(oil)
112
+ expect(solver.resume).to be_nil
113
+ end
114
+ end # context
115
+ end # describe
116
+ end # module
117
+ end # module