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.
Files changed (120) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +7 -0
  3. data/README.md +16 -16
  4. data/lib/mini_kraken/atomic/all_atomic.rb +1 -0
  5. data/lib/mini_kraken/atomic/atomic_term.rb +32 -17
  6. data/lib/mini_kraken/atomic/k_integer.rb +0 -4
  7. data/lib/mini_kraken/atomic/k_string.rb +17 -0
  8. data/lib/mini_kraken/atomic/k_symbol.rb +0 -6
  9. data/lib/mini_kraken/composite/all_composite.rb +4 -0
  10. data/lib/mini_kraken/composite/composite_term.rb +2 -18
  11. data/lib/mini_kraken/composite/cons_cell.rb +178 -11
  12. data/lib/mini_kraken/composite/cons_cell_visitor.rb +12 -64
  13. data/lib/mini_kraken/composite/list.rb +32 -0
  14. data/lib/mini_kraken/core/all_core.rb +8 -0
  15. data/lib/mini_kraken/core/any_value.rb +31 -7
  16. data/lib/mini_kraken/core/arity.rb +69 -0
  17. data/lib/mini_kraken/core/association.rb +29 -4
  18. data/lib/mini_kraken/core/association_copy.rb +50 -0
  19. data/lib/mini_kraken/core/base_term.rb +13 -0
  20. data/lib/mini_kraken/core/blackboard.rb +315 -0
  21. data/lib/mini_kraken/core/bookmark.rb +46 -0
  22. data/lib/mini_kraken/core/context.rb +624 -0
  23. data/lib/mini_kraken/core/duck_fiber.rb +21 -19
  24. data/lib/mini_kraken/core/entry.rb +40 -0
  25. data/lib/mini_kraken/core/fail.rb +20 -18
  26. data/lib/mini_kraken/core/fusion.rb +29 -0
  27. data/lib/mini_kraken/core/goal.rb +20 -29
  28. data/lib/mini_kraken/core/log_var.rb +4 -30
  29. data/lib/mini_kraken/core/log_var_ref.rb +72 -48
  30. data/lib/mini_kraken/core/nullary_relation.rb +2 -9
  31. data/lib/mini_kraken/core/parametrized_term.rb +61 -0
  32. data/lib/mini_kraken/core/relation.rb +14 -28
  33. data/lib/mini_kraken/core/scope.rb +67 -0
  34. data/lib/mini_kraken/core/solver_adapter.rb +58 -0
  35. data/lib/mini_kraken/core/specification.rb +48 -0
  36. data/lib/mini_kraken/core/succeed.rb +21 -17
  37. data/lib/mini_kraken/core/symbol_table.rb +137 -0
  38. data/lib/mini_kraken/core/term.rb +15 -4
  39. data/lib/mini_kraken/glue/dsl.rb +35 -69
  40. data/lib/mini_kraken/glue/run_star_expression.rb +28 -30
  41. data/lib/mini_kraken/rela/all_rela.rb +8 -0
  42. data/lib/mini_kraken/rela/binary_relation.rb +30 -0
  43. data/lib/mini_kraken/rela/conde.rb +146 -0
  44. data/lib/mini_kraken/rela/conj2.rb +65 -0
  45. data/lib/mini_kraken/rela/def_relation.rb +64 -0
  46. data/lib/mini_kraken/rela/disj2.rb +70 -0
  47. data/lib/mini_kraken/rela/fresh.rb +98 -0
  48. data/lib/mini_kraken/{core → rela}/goal_relation.rb +6 -8
  49. data/lib/mini_kraken/rela/unify.rb +258 -0
  50. data/lib/mini_kraken/version.rb +1 -1
  51. data/spec/atomic/atomic_term_spec.rb +23 -20
  52. data/spec/atomic/k_symbol_spec.rb +0 -5
  53. data/spec/composite/cons_cell_spec.rb +116 -0
  54. data/spec/composite/cons_cell_visitor_spec.rb +16 -3
  55. data/spec/composite/list_spec.rb +50 -0
  56. data/spec/core/any_value_spec.rb +52 -0
  57. data/spec/core/arity_spec.rb +91 -0
  58. data/spec/core/association_copy_spec.rb +69 -0
  59. data/spec/core/association_spec.rb +25 -0
  60. data/spec/core/blackboard_spec.rb +287 -0
  61. data/spec/core/bookmark_spec.rb +40 -0
  62. data/spec/core/context_spec.rb +221 -0
  63. data/spec/core/core_spec.rb +40 -0
  64. data/spec/core/duck_fiber_spec.rb +22 -46
  65. data/spec/core/fail_spec.rb +5 -6
  66. data/spec/core/goal_spec.rb +20 -11
  67. data/spec/core/log_var_ref_spec.rb +80 -5
  68. data/spec/core/log_var_spec.rb +35 -6
  69. data/spec/core/nullary_relation_spec.rb +33 -0
  70. data/spec/core/parametrized_tem_spec.rb +39 -0
  71. data/spec/core/relation_spec.rb +33 -0
  72. data/spec/core/scope_spec.rb +73 -0
  73. data/spec/core/solver_adapter_spec.rb +70 -0
  74. data/spec/core/specification_spec.rb +43 -0
  75. data/spec/core/succeed_spec.rb +5 -5
  76. data/spec/core/symbol_table_spec.rb +142 -0
  77. data/spec/glue/dsl_chap1_spec.rb +88 -99
  78. data/spec/glue/dsl_chap2_spec.rb +59 -41
  79. data/spec/glue/run_star_expression_spec.rb +69 -896
  80. data/spec/{core → rela}/conde_spec.rb +50 -46
  81. data/spec/rela/conj2_spec.rb +123 -0
  82. data/spec/rela/def_relation_spec.rb +119 -0
  83. data/spec/rela/disj2_spec.rb +117 -0
  84. data/spec/rela/fresh_spec.rb +147 -0
  85. data/spec/rela/unify_spec.rb +369 -0
  86. data/spec/support/factory_atomic.rb +7 -0
  87. data/spec/support/factory_composite.rb +21 -0
  88. metadata +71 -48
  89. data/lib/mini_kraken/core/association_walker.rb +0 -183
  90. data/lib/mini_kraken/core/base_arg.rb +0 -10
  91. data/lib/mini_kraken/core/binary_relation.rb +0 -63
  92. data/lib/mini_kraken/core/composite_goal.rb +0 -46
  93. data/lib/mini_kraken/core/conde.rb +0 -143
  94. data/lib/mini_kraken/core/conj2.rb +0 -79
  95. data/lib/mini_kraken/core/def_relation.rb +0 -53
  96. data/lib/mini_kraken/core/designation.rb +0 -55
  97. data/lib/mini_kraken/core/disj2.rb +0 -72
  98. data/lib/mini_kraken/core/environment.rb +0 -73
  99. data/lib/mini_kraken/core/equals.rb +0 -191
  100. data/lib/mini_kraken/core/formal_arg.rb +0 -22
  101. data/lib/mini_kraken/core/formal_ref.rb +0 -25
  102. data/lib/mini_kraken/core/freshness.rb +0 -45
  103. data/lib/mini_kraken/core/goal_arg.rb +0 -12
  104. data/lib/mini_kraken/core/goal_template.rb +0 -102
  105. data/lib/mini_kraken/core/outcome.rb +0 -63
  106. data/lib/mini_kraken/core/tap.rb +0 -46
  107. data/lib/mini_kraken/core/vocabulary.rb +0 -446
  108. data/lib/mini_kraken/glue/fresh_env.rb +0 -108
  109. data/lib/mini_kraken/glue/fresh_env_factory.rb +0 -83
  110. data/spec/core/association_walker_spec.rb +0 -194
  111. data/spec/core/conj2_spec.rb +0 -116
  112. data/spec/core/def_relation_spec.rb +0 -99
  113. data/spec/core/disj2_spec.rb +0 -100
  114. data/spec/core/environment_spec.rb +0 -144
  115. data/spec/core/equals_spec.rb +0 -319
  116. data/spec/core/goal_template_spec.rb +0 -74
  117. data/spec/core/outcome_spec.rb +0 -56
  118. data/spec/core/vocabulary_spec.rb +0 -220
  119. data/spec/glue/fresh_env_factory_spec.rb +0 -99
  120. 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/equals'
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/core/conde'
13
+ require_relative '../../lib/mini_kraken/rela/conde'
14
14
 
15
15
  module MiniKraken
16
- module Core
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(:env) do
50
- e = Environment.new
51
- e.add_var(var_q)
52
- e.add_var(var_x)
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], env) }.to raise_error(err)
60
- expect { subject.solver_for([pea, succeeds], env) }.to raise_error(err)
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], env)
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(Equals.instance, [olive, ref_q])
71
- solver = subject.solver_for([subgoal, fails, fails], env)
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
- expect(outcome.associations['q'].first.value).to eq(olive)
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(Equals.instance, [oil, ref_q])
80
- solver = subject.solver_for([fails, subgoal, fails], env)
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
- expect(outcome.associations['q'].first.value).to eq(oil)
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(Equals.instance, [oil, ref_q])
89
- solver = subject.solver_for([fails, fails, subgoal], env)
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.associations['q'].first.value).to eq(oil)
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(Equals.instance, [olive, ref_q])
99
- subgoal2 = Goal.new(Equals.instance, [oil, ref_q])
100
- subgoal3 = Goal.new(Equals.instance, [pea, ref_q])
101
- solver = subject.solver_for([subgoal1, subgoal2, subgoal3, fails], env)
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.associations['q'].first.value).to eq(olive)
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.associations['q'].first.value).to eq(oil)
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.associations['q'].first.value).to eq(pea)
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(Equals.instance, [split, ref_x])
124
- subgoal2 = Goal.new(Equals.instance, [pea, ref_y])
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(Equals.instance, [red, ref_x])
128
- subgoal4 = Goal.new(Equals.instance, [bean, ref_y])
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], env)
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
- expect(outcome1.associations['x'].first.value).to eq(split)
136
- expect(outcome1.associations['y'].first.value).to eq(pea)
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
- expect(outcome2.associations['x'].first.value).to eq(red)
142
- expect(outcome2.associations['y'].first.value).to eq(bean)
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