mini_kraken 0.2.02 → 0.3.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.
- checksums.yaml +4 -4
- data/.rubocop.yml +378 -333
- data/CHANGELOG.md +52 -0
- data/README.md +19 -19
- data/lib/mini_kraken.rb +0 -1
- data/lib/mini_kraken/atomic/all_atomic.rb +5 -0
- data/lib/mini_kraken/atomic/atomic_term.rb +96 -0
- data/lib/mini_kraken/atomic/k_boolean.rb +42 -0
- data/lib/mini_kraken/{core → atomic}/k_integer.rb +2 -5
- data/lib/mini_kraken/atomic/k_string.rb +17 -0
- data/lib/mini_kraken/{core → atomic}/k_symbol.rb +4 -8
- data/lib/mini_kraken/composite/all_composite.rb +4 -0
- data/lib/mini_kraken/composite/composite_term.rb +27 -0
- data/lib/mini_kraken/composite/cons_cell.rb +299 -0
- data/lib/mini_kraken/composite/cons_cell_visitor.rb +50 -0
- data/lib/mini_kraken/composite/list.rb +32 -0
- data/lib/mini_kraken/core/all_core.rb +8 -0
- data/lib/mini_kraken/core/any_value.rb +31 -7
- data/lib/mini_kraken/core/arity.rb +69 -0
- data/lib/mini_kraken/core/association.rb +29 -4
- data/lib/mini_kraken/core/association_copy.rb +50 -0
- data/lib/mini_kraken/core/base_term.rb +13 -0
- data/lib/mini_kraken/core/blackboard.rb +315 -0
- data/lib/mini_kraken/core/bookmark.rb +46 -0
- data/lib/mini_kraken/core/context.rb +492 -0
- data/lib/mini_kraken/core/duck_fiber.rb +21 -19
- data/lib/mini_kraken/core/entry.rb +40 -0
- data/lib/mini_kraken/core/fail.rb +20 -18
- data/lib/mini_kraken/core/fusion.rb +29 -0
- data/lib/mini_kraken/core/goal.rb +20 -29
- data/lib/mini_kraken/core/log_var.rb +22 -0
- data/lib/mini_kraken/core/log_var_ref.rb +108 -0
- data/lib/mini_kraken/core/nullary_relation.rb +2 -9
- data/lib/mini_kraken/core/parametrized_term.rb +61 -0
- data/lib/mini_kraken/core/relation.rb +14 -28
- data/lib/mini_kraken/core/scope.rb +67 -0
- data/lib/mini_kraken/core/solver_adapter.rb +58 -0
- data/lib/mini_kraken/core/specification.rb +48 -0
- data/lib/mini_kraken/core/succeed.rb +21 -17
- data/lib/mini_kraken/core/symbol_table.rb +137 -0
- data/lib/mini_kraken/core/term.rb +15 -4
- data/lib/mini_kraken/glue/dsl.rb +45 -81
- data/lib/mini_kraken/glue/run_star_expression.rb +28 -30
- data/lib/mini_kraken/rela/all_rela.rb +8 -0
- data/lib/mini_kraken/rela/binary_relation.rb +30 -0
- data/lib/mini_kraken/rela/conde.rb +143 -0
- data/lib/mini_kraken/rela/conj2.rb +65 -0
- data/lib/mini_kraken/rela/def_relation.rb +93 -0
- data/lib/mini_kraken/rela/disj2.rb +70 -0
- data/lib/mini_kraken/rela/fresh.rb +98 -0
- data/lib/mini_kraken/{core → rela}/goal_relation.rb +7 -9
- data/lib/mini_kraken/rela/unify.rb +258 -0
- data/lib/mini_kraken/version.rb +1 -1
- data/mini_kraken.gemspec +2 -2
- data/spec/.rubocop.yml +1 -1
- data/spec/atomic/atomic_term_spec.rb +98 -0
- data/spec/{core → atomic}/k_boolean_spec.rb +19 -34
- data/spec/{core → atomic}/k_symbol_spec.rb +3 -16
- data/spec/composite/cons_cell_spec.rb +225 -0
- data/spec/composite/cons_cell_visitor_spec.rb +158 -0
- data/spec/composite/list_spec.rb +50 -0
- data/spec/core/any_value_spec.rb +52 -0
- data/spec/core/arity_spec.rb +92 -0
- data/spec/core/association_copy_spec.rb +69 -0
- data/spec/core/association_spec.rb +31 -4
- data/spec/core/blackboard_spec.rb +287 -0
- data/spec/core/bookmark_spec.rb +40 -0
- data/spec/core/context_spec.rb +245 -0
- data/spec/core/core_spec.rb +40 -0
- data/spec/core/duck_fiber_spec.rb +16 -46
- data/spec/core/fail_spec.rb +5 -6
- data/spec/core/goal_spec.rb +24 -14
- data/spec/core/log_var_ref_spec.rb +105 -0
- data/spec/core/log_var_spec.rb +64 -0
- data/spec/core/nullary_relation_spec.rb +33 -0
- data/spec/core/parametrized_tem_spec.rb +39 -0
- data/spec/core/relation_spec.rb +33 -0
- data/spec/core/scope_spec.rb +73 -0
- data/spec/core/solver_adapter_spec.rb +70 -0
- data/spec/core/specification_spec.rb +43 -0
- data/spec/core/succeed_spec.rb +5 -5
- data/spec/core/symbol_table_spec.rb +142 -0
- data/spec/glue/dsl_chap1_spec.rb +96 -144
- data/spec/glue/dsl_chap2_spec.rb +350 -0
- data/spec/glue/run_star_expression_spec.rb +82 -906
- data/spec/rela/conde_spec.rb +153 -0
- data/spec/rela/conj2_spec.rb +123 -0
- data/spec/rela/def_relation_spec.rb +119 -0
- data/spec/rela/disj2_spec.rb +117 -0
- data/spec/rela/fresh_spec.rb +147 -0
- data/spec/rela/unify_spec.rb +369 -0
- data/spec/support/factory_atomic.rb +29 -0
- data/spec/support/factory_composite.rb +21 -0
- data/spec/support/factory_methods.rb +11 -26
- metadata +100 -64
- data/lib/mini_kraken/core/association_walker.rb +0 -183
- data/lib/mini_kraken/core/atomic_term.rb +0 -67
- data/lib/mini_kraken/core/base_arg.rb +0 -10
- data/lib/mini_kraken/core/binary_relation.rb +0 -63
- data/lib/mini_kraken/core/composite_goal.rb +0 -46
- data/lib/mini_kraken/core/composite_term.rb +0 -41
- data/lib/mini_kraken/core/conde.rb +0 -143
- data/lib/mini_kraken/core/conj2.rb +0 -79
- data/lib/mini_kraken/core/cons_cell.rb +0 -82
- data/lib/mini_kraken/core/def_relation.rb +0 -50
- data/lib/mini_kraken/core/designation.rb +0 -55
- data/lib/mini_kraken/core/disj2.rb +0 -72
- data/lib/mini_kraken/core/environment.rb +0 -73
- data/lib/mini_kraken/core/equals.rb +0 -156
- data/lib/mini_kraken/core/formal_arg.rb +0 -22
- data/lib/mini_kraken/core/formal_ref.rb +0 -25
- data/lib/mini_kraken/core/freshness.rb +0 -45
- data/lib/mini_kraken/core/goal_arg.rb +0 -12
- data/lib/mini_kraken/core/goal_template.rb +0 -62
- data/lib/mini_kraken/core/k_boolean.rb +0 -35
- data/lib/mini_kraken/core/outcome.rb +0 -53
- data/lib/mini_kraken/core/variable.rb +0 -41
- data/lib/mini_kraken/core/variable_ref.rb +0 -78
- data/lib/mini_kraken/core/vocabulary.rb +0 -442
- data/lib/mini_kraken/glue/fresh_env.rb +0 -75
- data/spec/core/association_walker_spec.rb +0 -192
- data/spec/core/conde_spec.rb +0 -147
- data/spec/core/conj2_spec.rb +0 -114
- data/spec/core/cons_cell_spec.rb +0 -107
- data/spec/core/def_relation_spec.rb +0 -96
- data/spec/core/disj2_spec.rb +0 -99
- data/spec/core/environment_spec.rb +0 -142
- data/spec/core/equals_spec.rb +0 -304
- data/spec/core/goal_template_spec.rb +0 -74
- data/spec/core/outcome_spec.rb +0 -48
- data/spec/core/variable_ref_spec.rb +0 -27
- data/spec/core/variable_spec.rb +0 -35
- data/spec/core/vocabulary_spec.rb +0 -219
- 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
|