mini_kraken 0.2.04 → 0.3.00
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/README.md +16 -16
- data/lib/mini_kraken/atomic/all_atomic.rb +1 -0
- data/lib/mini_kraken/atomic/atomic_term.rb +32 -17
- data/lib/mini_kraken/atomic/k_integer.rb +0 -4
- data/lib/mini_kraken/atomic/k_string.rb +17 -0
- data/lib/mini_kraken/atomic/k_symbol.rb +0 -6
- data/lib/mini_kraken/composite/all_composite.rb +4 -0
- data/lib/mini_kraken/composite/composite_term.rb +2 -18
- data/lib/mini_kraken/composite/cons_cell.rb +178 -11
- data/lib/mini_kraken/composite/cons_cell_visitor.rb +12 -64
- 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 +624 -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 +4 -30
- data/lib/mini_kraken/core/log_var_ref.rb +72 -48
- 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 +35 -69
- 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 +146 -0
- data/lib/mini_kraken/rela/conj2.rb +65 -0
- data/lib/mini_kraken/rela/def_relation.rb +64 -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 +6 -8
- data/lib/mini_kraken/rela/unify.rb +258 -0
- data/lib/mini_kraken/version.rb +1 -1
- data/spec/atomic/atomic_term_spec.rb +23 -20
- data/spec/atomic/k_symbol_spec.rb +0 -5
- data/spec/composite/cons_cell_spec.rb +116 -0
- data/spec/composite/cons_cell_visitor_spec.rb +16 -3
- data/spec/composite/list_spec.rb +50 -0
- data/spec/core/any_value_spec.rb +52 -0
- data/spec/core/arity_spec.rb +91 -0
- data/spec/core/association_copy_spec.rb +69 -0
- data/spec/core/association_spec.rb +25 -0
- data/spec/core/blackboard_spec.rb +287 -0
- data/spec/core/bookmark_spec.rb +40 -0
- data/spec/core/context_spec.rb +221 -0
- data/spec/core/core_spec.rb +40 -0
- data/spec/core/duck_fiber_spec.rb +22 -46
- data/spec/core/fail_spec.rb +5 -6
- data/spec/core/goal_spec.rb +20 -11
- data/spec/core/log_var_ref_spec.rb +80 -5
- data/spec/core/log_var_spec.rb +35 -6
- 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 +88 -99
- data/spec/glue/dsl_chap2_spec.rb +59 -41
- data/spec/glue/run_star_expression_spec.rb +69 -896
- data/spec/{core → rela}/conde_spec.rb +50 -46
- 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 +7 -0
- data/spec/support/factory_composite.rb +21 -0
- metadata +71 -48
- data/lib/mini_kraken/core/association_walker.rb +0 -183
- 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/conde.rb +0 -143
- data/lib/mini_kraken/core/conj2.rb +0 -79
- data/lib/mini_kraken/core/def_relation.rb +0 -53
- 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 -191
- 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 -102
- data/lib/mini_kraken/core/outcome.rb +0 -63
- data/lib/mini_kraken/core/tap.rb +0 -46
- data/lib/mini_kraken/core/vocabulary.rb +0 -446
- data/lib/mini_kraken/glue/fresh_env.rb +0 -108
- data/lib/mini_kraken/glue/fresh_env_factory.rb +0 -83
- data/spec/core/association_walker_spec.rb +0 -194
- data/spec/core/conj2_spec.rb +0 -116
- data/spec/core/def_relation_spec.rb +0 -99
- data/spec/core/disj2_spec.rb +0 -100
- data/spec/core/environment_spec.rb +0 -144
- data/spec/core/equals_spec.rb +0 -319
- data/spec/core/goal_template_spec.rb +0 -74
- data/spec/core/outcome_spec.rb +0 -56
- data/spec/core/vocabulary_spec.rb +0 -220
- data/spec/glue/fresh_env_factory_spec.rb +0 -99
- data/spec/glue/fresh_env_spec.rb +0 -62
@@ -4,16 +4,16 @@ require_relative '../spec_helper' # Use the RSpec framework
|
|
4
4
|
require_relative '../support/factory_atomic'
|
5
5
|
require_relative '../../lib/mini_kraken/core/fail'
|
6
6
|
require_relative '../../lib/mini_kraken/core/succeed'
|
7
|
-
require_relative '../../lib/mini_kraken/core/
|
8
|
-
require_relative '../../lib/mini_kraken/core/environment'
|
7
|
+
require_relative '../../lib/mini_kraken/core/context'
|
9
8
|
require_relative '../../lib/mini_kraken/core/log_var'
|
10
9
|
require_relative '../../lib/mini_kraken/core/log_var_ref'
|
10
|
+
require_relative '../../lib/mini_kraken/rela/unify'
|
11
11
|
|
12
12
|
# Load the class under test
|
13
|
-
require_relative '../../lib/mini_kraken/
|
13
|
+
require_relative '../../lib/mini_kraken/rela/conde'
|
14
14
|
|
15
15
|
module MiniKraken
|
16
|
-
module
|
16
|
+
module Rela
|
17
17
|
describe Conde do
|
18
18
|
include MiniKraken::FactoryAtomic # Use mix-in module
|
19
19
|
|
@@ -31,115 +31,119 @@ module MiniKraken
|
|
31
31
|
|
32
32
|
context 'Provided services:' do
|
33
33
|
let(:bean) { k_symbol(:bean) }
|
34
|
-
let(:corn) {k_symbol(:corn) }
|
34
|
+
let(:corn) { k_symbol(:corn) }
|
35
35
|
let(:meal) { k_symbol(:meal) }
|
36
36
|
let(:oil) { k_symbol(:oil) }
|
37
37
|
let(:olive) { k_symbol(:olive) }
|
38
38
|
let(:pea) { k_symbol(:pea) }
|
39
39
|
let(:red) { k_symbol(:red) }
|
40
40
|
let(:split) { k_symbol(:split) }
|
41
|
-
let(:fails) { Goal.new(Fail.instance, []) }
|
42
|
-
let(:succeeds) { Goal.new(Succeed.instance, []) }
|
43
|
-
let(:var_q) { LogVar.new('q') }
|
44
|
-
let(:var_x) { LogVar.new('x') }
|
45
|
-
let(:var_y) { LogVar.new('y') }
|
46
|
-
let(:ref_q) { LogVarRef.new('q') }
|
47
|
-
let(:ref_x) { LogVarRef.new('x') }
|
48
|
-
let(:ref_y) { LogVarRef.new('y') }
|
49
|
-
let(:
|
50
|
-
e =
|
51
|
-
e.
|
52
|
-
|
53
|
-
e.add_var(var_y)
|
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
|
+
|
54
53
|
e
|
55
54
|
end
|
56
55
|
|
57
56
|
it 'should complain when one of its argument is not a goal' do
|
58
57
|
err = StandardError
|
59
|
-
expect { subject.solver_for([succeeds, pea],
|
60
|
-
expect { subject.solver_for([pea, succeeds],
|
58
|
+
expect { subject.solver_for([succeeds, pea], ctx) }.to raise_error(err)
|
59
|
+
expect { subject.solver_for([pea, succeeds], ctx) }.to raise_error(err)
|
61
60
|
end
|
62
61
|
|
63
62
|
it 'should fail when all goals fail' do
|
64
|
-
solver = subject.solver_for([fails, fails, fails],
|
63
|
+
solver = subject.solver_for([fails, fails, fails], ctx)
|
65
64
|
expect(solver.resume).not_to be_success
|
66
65
|
expect(solver.resume).to be_nil
|
67
66
|
end
|
68
67
|
|
69
68
|
it 'yield success if first argument succeeds' do
|
70
|
-
subgoal = Goal.new(
|
71
|
-
solver = subject.solver_for([subgoal, fails, fails],
|
69
|
+
subgoal = Core::Goal.new(Unify.instance, [olive, ref_q])
|
70
|
+
solver = subject.solver_for([subgoal, fails, fails], ctx)
|
72
71
|
outcome = solver.resume
|
72
|
+
|
73
73
|
expect(outcome).to be_success
|
74
|
-
|
74
|
+
sol = outcome.build_solution
|
75
|
+
expect(sol['q']).to eq(olive)
|
75
76
|
expect(solver.resume).to be_nil
|
76
77
|
end
|
77
78
|
|
78
79
|
it 'yield success if second argument succeeds' do
|
79
|
-
subgoal = Goal.new(
|
80
|
-
solver = subject.solver_for([fails, subgoal, fails],
|
80
|
+
subgoal = Core::Goal.new(Unify.instance, [oil, ref_q])
|
81
|
+
solver = subject.solver_for([fails, subgoal, fails], ctx)
|
81
82
|
outcome = solver.resume
|
82
83
|
expect(outcome).to be_success
|
83
|
-
|
84
|
+
sol = outcome.build_solution
|
85
|
+
expect(sol['q']).to eq(oil)
|
84
86
|
expect(solver.resume).to be_nil
|
85
87
|
end
|
86
88
|
|
87
89
|
it 'yield success if third argument succeeds' do
|
88
|
-
subgoal = Goal.new(
|
89
|
-
solver = subject.solver_for([fails, fails, subgoal],
|
90
|
+
subgoal = Core::Goal.new(Unify.instance, [oil, ref_q])
|
91
|
+
solver = subject.solver_for([fails, fails, subgoal], ctx)
|
90
92
|
outcome = solver.resume
|
91
93
|
expect(outcome).to be_success
|
92
|
-
expect(outcome.
|
94
|
+
expect(outcome.build_solution['q']).to eq(oil)
|
93
95
|
expect(solver.resume).to be_nil
|
94
96
|
end
|
95
97
|
|
96
98
|
it 'yields three solutions if three goals succeed' do
|
97
99
|
# Covers frame 1:58
|
98
|
-
subgoal1 = Goal.new(
|
99
|
-
subgoal2 = Goal.new(
|
100
|
-
subgoal3 = Goal.new(
|
101
|
-
solver = subject.solver_for([subgoal1, subgoal2, subgoal3, fails],
|
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)
|
102
104
|
|
103
105
|
# First solution
|
104
106
|
outcome1 = solver.resume
|
105
107
|
expect(outcome1).to be_success
|
106
|
-
expect(outcome1.
|
108
|
+
expect(outcome1.build_solution['q']).to eq(olive)
|
107
109
|
|
108
110
|
# Second solution
|
109
111
|
outcome2 = solver.resume
|
110
112
|
expect(outcome2).to be_success
|
111
|
-
expect(outcome2.
|
113
|
+
expect(outcome2.build_solution['q']).to eq(oil)
|
112
114
|
|
113
115
|
# Third solution
|
114
116
|
outcome3 = solver.resume
|
115
117
|
expect(outcome3).to be_success
|
116
|
-
expect(outcome3.
|
118
|
+
expect(outcome3.build_solution['q']).to eq(pea)
|
117
119
|
|
118
120
|
expect(solver.resume).to be_nil
|
119
121
|
end
|
120
122
|
|
121
123
|
it 'also use conjunctions for nested goals' do
|
122
124
|
# Covers frame 1:88
|
123
|
-
subgoal1 = Goal.new(
|
124
|
-
subgoal2 = Goal.new(
|
125
|
+
subgoal1 = Core::Goal.new(Unify.instance, [split, ref_x])
|
126
|
+
subgoal2 = Core::Goal.new(Unify.instance, [pea, ref_y])
|
125
127
|
combo1 = [subgoal1, subgoal2]
|
126
128
|
|
127
|
-
subgoal3 = Goal.new(
|
128
|
-
subgoal4 = Goal.new(
|
129
|
+
subgoal3 = Core::Goal.new(Unify.instance, [red, ref_x])
|
130
|
+
subgoal4 = Core::Goal.new(Unify.instance, [bean, ref_y])
|
129
131
|
combo2 = [subgoal3, subgoal4]
|
130
|
-
solver = subject.solver_for([combo1, combo2],
|
132
|
+
solver = subject.solver_for([combo1, combo2], ctx)
|
131
133
|
|
132
134
|
# First solution
|
133
135
|
outcome1 = solver.resume
|
134
136
|
expect(outcome1).to be_success
|
135
|
-
|
136
|
-
expect(
|
137
|
+
solution1 = outcome1.build_solution
|
138
|
+
expect(solution1['x']).to eq(split)
|
139
|
+
expect(solution1['y']).to eq(pea)
|
137
140
|
|
138
141
|
# Second solution
|
139
142
|
outcome2 = solver.resume
|
140
143
|
expect(outcome2).to be_success
|
141
|
-
|
142
|
-
expect(
|
144
|
+
solution2 = outcome2.build_solution
|
145
|
+
expect(solution2['x']).to eq(red)
|
146
|
+
expect(solution2['y']).to eq(bean)
|
143
147
|
|
144
148
|
expect(solver.resume).to be_nil
|
145
149
|
end
|
@@ -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
|