mini_kraken 0.2.02 → 0.2.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.
@@ -24,7 +24,11 @@ unless MiniKraken::Core.constants(false).include? :Outcome
24
24
  new(:"#s", aParent)
25
25
  end
26
26
 
27
- def successful?
27
+ def failure?
28
+ resultant != :"#s"
29
+ end
30
+
31
+ def success?
28
32
  resultant == :"#s"
29
33
  end
30
34
 
@@ -39,6 +43,12 @@ unless MiniKraken::Core.constants(false).include? :Outcome
39
43
  are_equal
40
44
  end
41
45
 
46
+ # Remove associations of variables of this environment, if
47
+ # persistence flag is set to false.
48
+ def prune!
49
+ parent.prune(self)
50
+ end
51
+
42
52
  protected
43
53
 
44
54
  def introspect
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'term'
4
+ require_relative 'designation'
4
5
  require_relative 'any_value'
5
6
 
6
7
  module MiniKraken
@@ -15,6 +16,11 @@ module MiniKraken
15
16
  def initialize(aName)
16
17
  super()
17
18
  init_designation(aName)
19
+ name.freeze
20
+ end
21
+
22
+ def to_s
23
+ name
18
24
  end
19
25
 
20
26
  # @param aValue [Term]
@@ -171,7 +171,7 @@ module MiniKraken
171
171
  to_fuse.map { |i_name| i_name2var(i_name) }
172
172
  end
173
173
 
174
- # Fuse the given variables, that is:
174
+ # Fuse the given variables:
175
175
  # Collect all their associations
176
176
  # Put them under a new internal name
177
177
  # Remove all entries from old internal names
@@ -395,12 +395,16 @@ module MiniKraken
395
395
  name2var(aVarName) ? true : false
396
396
  end
397
397
 
398
+ def prune(anOutcome)
399
+ anOutcome # Don't touch outcome
400
+ end
401
+
398
402
  def inspect
399
403
  result = +"#<#{self.class.name}:#{object_id.to_s(16)} @parent="
400
404
  if parent
401
405
  result << "#<#{parent.class.name}:#{parent.object_id.to_s(16)}>"
402
406
  else
403
- result << nil
407
+ result << 'nil'
404
408
  end
405
409
  result << introspect
406
410
  result << '>'
@@ -12,6 +12,7 @@ require_relative '../core/fail'
12
12
  require_relative '../core/formal_arg'
13
13
  require_relative '../core/formal_ref'
14
14
  require_relative '../glue/fresh_env'
15
+ require_relative '../glue/fresh_env_factory'
15
16
  require_relative '../core/goal_template'
16
17
  require_relative '../core/k_boolean'
17
18
  require_relative '../core/k_symbol'
@@ -39,7 +40,7 @@ module MiniKraken
39
40
  # require 'debug'
40
41
  args = goals.map do |goal_maybe|
41
42
  if goal_maybe.kind_of?(Array)
42
- goal_maybe.map { |g| convert(g)}
43
+ goal_maybe.map { |g| convert(g) }
43
44
  else
44
45
  convert(goal_maybe)
45
46
  end
@@ -98,14 +99,22 @@ module MiniKraken
98
99
 
99
100
  def fresh(var_names, goal)
100
101
  vars = nil
101
-
102
- if var_names.kind_of?(String) || var_names.kind_of?(Core::VariableRef)
103
- vars = [var_names]
102
+ if @dsl_mode == :defrel
103
+ if var_names.kind_of?(String)
104
+ vars = [var_names]
105
+ else
106
+ vars = var_names
107
+ end
108
+ FreshEnvFactory.new(vars, goal)
104
109
  else
105
- vars = var_names
106
- end
110
+ if var_names.kind_of?(String) || var_names.kind_of?(Core::VariableRef)
111
+ vars = [var_names]
112
+ else
113
+ vars = var_names
114
+ end
107
115
 
108
- FreshEnv.new(vars, goal)
116
+ FreshEnv.new(vars, goal)
117
+ end
109
118
  end
110
119
 
111
120
  def list(*members)
@@ -158,7 +167,7 @@ module MiniKraken
158
167
  when Core::FormalRef
159
168
  converted = anArgument
160
169
  when FreshEnv
161
- converted = anArgument
170
+ converted = anArgument
162
171
  when Core::Goal
163
172
  converted = anArgument
164
173
  when Core::GoalTemplate
@@ -2,13 +2,14 @@
2
2
 
3
3
  require_relative '../core/environment'
4
4
  require_relative '../core/conj2'
5
+ require_relative '../core/goal_template'
5
6
  require_relative '../core/variable'
6
7
 
7
8
  module MiniKraken
8
9
  module Glue
9
10
  # A combination of an Environment (= a scope for one or more variables)
10
11
  # and a goal. It quacks like a Goal object: when receiving the attain message,
11
- # it attempt to achieve its given goal.
12
+ # it attempts to achieve its given goal.
12
13
  # (fresh (x) (== 'pea q))
13
14
  # Introduces the new variable 'x'
14
15
  # Takes a list of names and a goal-like object
@@ -17,22 +18,45 @@ module MiniKraken
17
18
  # @return [Goal]
18
19
  attr_reader :goal
19
20
 
21
+ # @return [TrueClass, FalseClass] Do associations persist after goal exec?
22
+ attr_reader :persistent
23
+
20
24
  # @param theNames [Array<String>] The variable names
21
25
  # @param aGoal [Goal, Array<Goal>] The goal to achieve or the conjunction of them.
22
- def initialize(theNames, aGoal)
26
+ def initialize(theNames, aGoal, persistence = true)
23
27
  super()
24
28
  @goal = valid_goal(aGoal)
25
- theNames.each { |nm| add_var(Core::Variable.new(nm)) }
29
+ theNames.each do |nm|
30
+ var = Core::Variable.new(nm)
31
+ add_var(var)
32
+ end
33
+ @persistent = persistence
26
34
  end
27
35
 
28
36
  # Attempt to achieve the goal given this environment
29
37
  # @param aParent [Environment]
30
38
  # @return [Fiber<Outcome>] A Fiber object that will generate the results.
31
39
  def attain(aParent)
40
+ # require 'debug'
32
41
  self.parent = aParent
33
42
  goal.attain(self)
34
43
  end
35
44
 
45
+ # Remove associations of variables of this environment, if
46
+ # persistence flag is set to false.
47
+ def prune(anOutcome)
48
+ return super(anOutcome) if persistent
49
+
50
+ vars.each_value do |v|
51
+ v_name = v.name
52
+ if anOutcome.associations.include?(v_name)
53
+ anOutcome.associations.delete(v_name)
54
+ end
55
+ end
56
+
57
+ anOutcome
58
+ end
59
+
36
60
  protected
37
61
 
38
62
  def introspect
@@ -49,6 +73,8 @@ module MiniKraken
49
73
  result = aGoal
50
74
  when FreshEnv
51
75
  result = aGoal
76
+ when Core::GoalTemplate
77
+ result = aGoal
52
78
  when Array # an Array of Goal?..
53
79
  goal_array = aGoal
54
80
  loop do
@@ -66,6 +92,8 @@ module MiniKraken
66
92
  end
67
93
  goal_array = conjunctions
68
94
  end
95
+ else
96
+ raise StandardError, "Cannot handle argumment type #{aGoal.class}"
69
97
  end
70
98
 
71
99
  result
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../core/environment'
4
+ require_relative '../core/conj2'
5
+ require_relative '../core/goal_template'
6
+ require_relative '../core/variable'
7
+ require_relative 'fresh_env'
8
+
9
+ module MiniKraken
10
+ module Glue
11
+ # A combination of an Environment (= a scope for one or more variables)
12
+ # and a goal. It quacks like a Goal template object: when receiving the
13
+ # instantiate message, it creates a FreshEnv.
14
+ class FreshEnvFactory
15
+ # @return [Array<String>] The names of variables to be.
16
+ attr_reader :names
17
+
18
+ # @return [GoalTemplate] The goal template
19
+ attr_reader :goal_template
20
+
21
+ # @param theNames [Array<String>] The names of variables to build.
22
+ # @param aGoal [GoalTemplate, Array<Goal>] The goal template(s)
23
+ def initialize(theNames, aGoalTemplate)
24
+ @goal_template = valid_goal_template(aGoalTemplate)
25
+ @names = valid_names(theNames)
26
+ end
27
+
28
+ # Factory method: Create a goal object.
29
+ # @param formals [Array<FormalArg>] Array of formal arguments
30
+ # @param actuals [Array<GoalArg>] Array of actual arguments
31
+ # @return [Goal] instantiate a goal object given the actuals and environment
32
+ def instantiate(formals, actuals)
33
+ # require 'debug'
34
+ goal = goal_template.instantiate(formals, actuals)
35
+ FreshEnv.new(names, goal, false)
36
+ end
37
+
38
+ protected
39
+
40
+ def introspect
41
+ +", @names=[#{names.join(', ')}]"
42
+ end
43
+
44
+ private
45
+
46
+ def valid_names(theNames)
47
+ theNames
48
+ end
49
+
50
+ def valid_goal_template(aGoalTemplate)
51
+ result = nil
52
+
53
+ case aGoalTemplate
54
+ when FreshEnvFactory
55
+ result = aGoalTemplate
56
+ when Core::GoalTemplate
57
+ result = aGoalTemplate
58
+ # when Array # an Array of Goal?..
59
+ # goal_array = aGoalTemplate
60
+ # loop do
61
+ # conjunctions = []
62
+ # goal_array.each_slice(2) do |uno_duo|
63
+ # if uno_duo.size == 2
64
+ # conjunctions << Core::GoalTemplate.new(Core::Conj2.instance, uno_duo)
65
+ # else
66
+ # conjunctions << uno_duo[0]
67
+ # end
68
+ # end
69
+ # if conjunctions.size == 1
70
+ # result = conjunctions[0]
71
+ # break
72
+ # end
73
+ # goal_array = conjunctions
74
+ # end
75
+ else
76
+ raise StandardError, "Cannot handle argumment type #{aGoalTemplate.class}"
77
+ end
78
+
79
+ result
80
+ end
81
+ end # class
82
+ end # module
83
+ end # module
@@ -27,7 +27,7 @@ module MiniKraken
27
27
  outcome = solver.resume
28
28
  break if outcome.nil?
29
29
 
30
- env.propagate(outcome) if result.empty? && outcome.successful?
30
+ env.propagate(outcome) if result.empty? && outcome.success?
31
31
  result << build_solution(outcome)
32
32
  end
33
33
 
@@ -39,7 +39,7 @@ module MiniKraken
39
39
  # @return [Array] A vector of assignment for each variable
40
40
  def build_solution(outcome)
41
41
  env.vars.values.map do |var|
42
- outcome.successful? ? var.quote(outcome) : nil
42
+ outcome.success? ? var.quote(outcome) : nil
43
43
  end
44
44
  end
45
45
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MiniKraken
4
- VERSION = '0.2.02'
4
+ VERSION = '0.2.03'
5
5
  end
@@ -60,7 +60,7 @@ module MiniKraken
60
60
 
61
61
  it 'should fail when all goals fail' do
62
62
  solver = subject.solver_for([fails, fails, fails], env)
63
- expect(solver.resume).not_to be_successful
63
+ expect(solver.resume).not_to be_success
64
64
  expect(solver.resume).to be_nil
65
65
  end
66
66
 
@@ -68,7 +68,7 @@ module MiniKraken
68
68
  subgoal = Goal.new(Equals.instance, [olive, ref_q])
69
69
  solver = subject.solver_for([subgoal, fails, fails], env)
70
70
  outcome = solver.resume
71
- expect(outcome).to be_successful
71
+ expect(outcome).to be_success
72
72
  expect(outcome.associations['q'].first.value).to eq(olive)
73
73
  expect(solver.resume).to be_nil
74
74
  end
@@ -77,7 +77,7 @@ module MiniKraken
77
77
  subgoal = Goal.new(Equals.instance, [oil, ref_q])
78
78
  solver = subject.solver_for([fails, subgoal, fails], env)
79
79
  outcome = solver.resume
80
- expect(outcome).to be_successful
80
+ expect(outcome).to be_success
81
81
  expect(outcome.associations['q'].first.value).to eq(oil)
82
82
  expect(solver.resume).to be_nil
83
83
  end
@@ -86,7 +86,7 @@ module MiniKraken
86
86
  subgoal = Goal.new(Equals.instance, [oil, ref_q])
87
87
  solver = subject.solver_for([fails, fails, subgoal], env)
88
88
  outcome = solver.resume
89
- expect(outcome).to be_successful
89
+ expect(outcome).to be_success
90
90
  expect(outcome.associations['q'].first.value).to eq(oil)
91
91
  expect(solver.resume).to be_nil
92
92
  end
@@ -100,17 +100,17 @@ module MiniKraken
100
100
 
101
101
  # First solution
102
102
  outcome1 = solver.resume
103
- expect(outcome1).to be_successful
103
+ expect(outcome1).to be_success
104
104
  expect(outcome1.associations['q'].first.value).to eq(olive)
105
105
 
106
106
  # Second solution
107
107
  outcome2 = solver.resume
108
- expect(outcome2).to be_successful
108
+ expect(outcome2).to be_success
109
109
  expect(outcome2.associations['q'].first.value).to eq(oil)
110
110
 
111
111
  # Third solution
112
112
  outcome3 = solver.resume
113
- expect(outcome3).to be_successful
113
+ expect(outcome3).to be_success
114
114
  expect(outcome3.associations['q'].first.value).to eq(pea)
115
115
 
116
116
  expect(solver.resume).to be_nil
@@ -129,13 +129,13 @@ module MiniKraken
129
129
 
130
130
  # First solution
131
131
  outcome1 = solver.resume
132
- expect(outcome1).to be_successful
132
+ expect(outcome1).to be_success
133
133
  expect(outcome1.associations['x'].first.value).to eq(split)
134
134
  expect(outcome1.associations['y'].first.value).to eq(pea)
135
135
 
136
136
  # Second solution
137
137
  outcome2 = solver.resume
138
- expect(outcome2).to be_successful
138
+ expect(outcome2).to be_success
139
139
  expect(outcome2.associations['x'].first.value).to eq(red)
140
140
  expect(outcome2.associations['y'].first.value).to eq(bean)
141
141
 
@@ -47,12 +47,12 @@ module MiniKraken
47
47
  it 'should yield one failure if one of the goal is fail' do
48
48
  # Fail as first argument
49
49
  solver = subject.solver_for([fails, succeeds], env)
50
- expect(solver.resume).not_to be_successful
50
+ expect(solver.resume).not_to be_success
51
51
  expect(solver.resume).to be_nil
52
52
 
53
53
  # Fail as second argument
54
54
  solver = subject.solver_for([succeeds, fails], env)
55
- expect(solver.resume).not_to be_successful
55
+ expect(solver.resume).not_to be_success
56
56
  expect(solver.resume).to be_nil
57
57
  end
58
58
 
@@ -60,7 +60,7 @@ module MiniKraken
60
60
  # Covers frame 1-50
61
61
  solver = subject.solver_for([succeeds, succeeds], env)
62
62
  outcome = solver.resume
63
- expect(outcome).to be_successful
63
+ expect(outcome).to be_success
64
64
  expect(outcome.associations).to be_empty
65
65
  expect(solver.resume).to be_nil
66
66
  end
@@ -71,7 +71,7 @@ module MiniKraken
71
71
  sub_goal = Goal.new(Equals.instance, [corn, ref_q])
72
72
  solver = subject.solver_for([succeeds, sub_goal], env)
73
73
  outcome = solver.resume
74
- expect(outcome).to be_successful
74
+ expect(outcome).to be_success
75
75
  expect(outcome.associations).not_to be_empty
76
76
  expect(outcome.associations['q'].first.value).to eq(corn)
77
77
  end
@@ -82,7 +82,7 @@ module MiniKraken
82
82
  sub_goal = Goal.new(Equals.instance, [corn, ref_q])
83
83
  solver = subject.solver_for([fails, sub_goal], env)
84
84
  outcome = solver.resume
85
- expect(outcome).not_to be_successful
85
+ expect(outcome).not_to be_success
86
86
  expect(outcome.associations).to be_empty
87
87
  end
88
88
 
@@ -93,7 +93,7 @@ module MiniKraken
93
93
  sub_goal2 = Goal.new(Equals.instance, [meal, ref_q])
94
94
  solver = subject.solver_for([sub_goal1, sub_goal2], env)
95
95
  outcome = solver.resume
96
- expect(outcome).not_to be_successful
96
+ expect(outcome).not_to be_success
97
97
  expect(outcome.associations).to be_empty
98
98
  end
99
99
 
@@ -104,7 +104,7 @@ module MiniKraken
104
104
  sub_goal2 = Goal.new(Equals.instance, [corn, ref_q])
105
105
  solver = subject.solver_for([sub_goal1, sub_goal2], env)
106
106
  outcome = solver.resume
107
- expect(outcome).to be_successful
107
+ expect(outcome).to be_success
108
108
  expect(outcome.associations).not_to be_empty
109
109
  expect(outcome.associations['q'].first.value).to eq(corn)
110
110
  end
@@ -0,0 +1,144 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../spec_helper' # Use the RSpec framework
4
+ require_relative '../../lib/mini_kraken/core/k_symbol'
5
+ require_relative '../../lib/mini_kraken/core/variable_ref'
6
+
7
+ # Load the class under test
8
+ require_relative '../../lib/mini_kraken/core/cons_cell_visitor'
9
+
10
+
11
+ module MiniKraken
12
+ module Core
13
+ describe ConsCellVisitor do
14
+ let(:pea) { KSymbol.new(:pea) }
15
+ let(:pod) { KSymbol.new(:pod) }
16
+ let(:corn) { KSymbol.new(:corn) }
17
+ let(:ref_q) { VariableRef.new('q') }
18
+ let(:l_pea) { ConsCell.new(pea) }
19
+ let(:l_pea_pod) { ConsCell.new(pea, ConsCell.new(pod)) }
20
+ let(:l_pea_pod_corn) { ConsCell.new(pea, ConsCell.new(pod, ConsCell.new(corn))) }
21
+ subject { ConsCellVisitor }
22
+
23
+ context 'Provided services:' do
24
+ it 'acts as a factory of Enumerator' do
25
+ expect(subject.df_visitor(l_pea)).to be_kind_of(Fiber)
26
+ end
27
+ end # context
28
+
29
+ context 'proper list visiting:' do
30
+ it 'can visit a null list' do
31
+ null_list = ConsCell.new(nil)
32
+ visitor = subject.df_visitor(null_list)
33
+ expect(visitor.resume).to eq([:car, null_list])
34
+ expect(visitor.resume).to eq([:car, nil])
35
+ expect(visitor.resume).to eq([:cdr, nil])
36
+ expect(visitor.resume).to eq([:stop, nil])
37
+ end
38
+
39
+ it 'can visit a single element proper list' do
40
+ visitor = subject.df_visitor(l_pea)
41
+ expect(visitor.resume).to eq([:car, l_pea])
42
+ expect(visitor.resume).to eq([:car, pea])
43
+ expect(visitor.resume).to eq([:cdr, nil])
44
+ expect(visitor.resume).to eq([:stop, nil])
45
+ end
46
+
47
+ it 'can visit a two elements proper list' do
48
+ visitor = subject.df_visitor(l_pea_pod)
49
+ expect(visitor.resume).to eq([:car, l_pea_pod])
50
+ expect(visitor.resume).to eq([:car, pea])
51
+ expect(visitor.resume).to eq([:cdr, l_pea_pod.cdr])
52
+ expect(visitor.resume).to eq([:car, pod])
53
+ expect(visitor.resume).to eq([:cdr, nil])
54
+ expect(visitor.resume).to eq([:stop, nil])
55
+ end
56
+
57
+ it 'can visit a three elements proper list' do
58
+ visitor = subject.df_visitor(l_pea_pod_corn)
59
+ expect(visitor.resume).to eq([:car, l_pea_pod_corn])
60
+ expect(visitor.resume).to eq([:car, pea])
61
+ expect(visitor.resume).to eq([:cdr, l_pea_pod_corn.cdr])
62
+ expect(visitor.resume).to eq([:car, pod])
63
+ expect(visitor.resume).to eq([:cdr, l_pea_pod_corn.cdr.cdr])
64
+ expect(visitor.resume).to eq([:car, corn])
65
+ expect(visitor.resume).to eq([:cdr, nil])
66
+ expect(visitor.resume).to eq([:stop, nil])
67
+ end
68
+ end # context
69
+
70
+ context 'improper list visiting:' do
71
+ it 'can visit a single element improper list' do
72
+ l_improper = ConsCell.new(nil, pea)
73
+ visitor = subject.df_visitor(l_improper)
74
+ expect(visitor.resume).to eq([:car, l_improper])
75
+ expect(visitor.resume).to eq([:car, nil])
76
+ expect(visitor.resume).to eq([:cdr, pea])
77
+ expect(visitor.resume).to eq([:stop, nil])
78
+ end
79
+
80
+ it 'can visit a two elements improper list' do
81
+ l_improper = ConsCell.new(pea, pod)
82
+ visitor = subject.df_visitor(l_improper)
83
+ expect(visitor.resume).to eq([:car, l_improper])
84
+ expect(visitor.resume).to eq([:car, pea])
85
+ expect(visitor.resume).to eq([:cdr, pod])
86
+ expect(visitor.resume).to eq([:stop, nil])
87
+ end
88
+
89
+ it 'can visit a three elements improper list' do
90
+ l_improper = ConsCell.new(pea, ConsCell.new(pod, corn))
91
+ visitor = subject.df_visitor(l_improper)
92
+ expect(visitor.resume).to eq([:car, l_improper])
93
+ expect(visitor.resume).to eq([:car, pea])
94
+ expect(visitor.resume).to eq([:cdr, l_improper.cdr])
95
+ expect(visitor.resume).to eq([:car, pod])
96
+ expect(visitor.resume).to eq([:cdr, corn])
97
+ expect(visitor.resume).to eq([:stop, nil])
98
+ end
99
+ end # context
100
+
101
+ context 'Skip visit of children of a ConsCell:' do
102
+ it 'can skip the visit of null list children' do
103
+ null_list = ConsCell.new(nil)
104
+ visitor = subject.df_visitor(null_list)
105
+
106
+ # Tell to skip children by passing a true value to resume
107
+ expect(visitor.resume(true)).to eq([:car, null_list])
108
+ expect(visitor.resume).to eq([:stop, nil])
109
+ end
110
+
111
+ it 'can skip the visit of some children' do
112
+ tree = ConsCell.new(pea, ConsCell.new(l_pea_pod, ConsCell.new(corn)))
113
+ expect(tree.to_s).to eq('(:pea (:pea :pod) :corn)')
114
+ visitor = subject.df_visitor(tree)
115
+ expect(visitor.resume).to eq([:car, tree])
116
+ expect(visitor.resume).to eq([:car, pea])
117
+ expect(visitor.resume).to eq([:cdr, tree.cdr])
118
+ expect(visitor.resume).to eq([:car, l_pea_pod])
119
+
120
+ # Tell to skip children by passing a true value to resume
121
+ expect(visitor.resume(true)).to eq([:cdr, tree.cdr.cdr])
122
+ expect(visitor.resume).to eq([:car, corn])
123
+ expect(visitor.resume).to eq([:cdr, nil])
124
+ expect(visitor.resume).to eq([:stop, nil])
125
+ end
126
+ end # context
127
+
128
+ context 'Circular structures visiting:' do
129
+ it 'should cope with a circular graph' do
130
+ second_cell = ConsCell.new(pod)
131
+ first_cell = ConsCell.new(pea, second_cell)
132
+ second_cell.instance_variable_set(:@car, first_cell) # Ugly!
133
+
134
+ visitor = subject.df_visitor(first_cell)
135
+ expect(visitor.resume).to eq([:car, first_cell])
136
+ expect(visitor.resume).to eq([:car, pea])
137
+ expect(visitor.resume).to eq([:cdr, second_cell])
138
+ expect(visitor.resume).to eq([:cdr, nil]) # Skip car (was already visited)
139
+ expect(visitor.resume).to eq([:stop, nil])
140
+ end
141
+ end # context
142
+ end # describe
143
+ end # module
144
+ end # module