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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +18 -0
- data/README.md +3 -3
- data/lib/mini_kraken.rb +0 -1
- data/lib/mini_kraken/core/conde.rb +1 -1
- data/lib/mini_kraken/core/conj2.rb +3 -3
- data/lib/mini_kraken/core/cons_cell_visitor.rb +102 -0
- data/lib/mini_kraken/core/def_relation.rb +3 -0
- data/lib/mini_kraken/core/disj2.rb +2 -2
- data/lib/mini_kraken/core/environment.rb +1 -1
- data/lib/mini_kraken/core/equals.rb +59 -22
- data/lib/mini_kraken/core/goal_template.rb +42 -2
- data/lib/mini_kraken/core/k_boolean.rb +1 -1
- data/lib/mini_kraken/core/outcome.rb +11 -1
- data/lib/mini_kraken/core/variable_ref.rb +6 -0
- data/lib/mini_kraken/core/vocabulary.rb +6 -2
- data/lib/mini_kraken/glue/dsl.rb +17 -8
- data/lib/mini_kraken/glue/fresh_env.rb +31 -3
- data/lib/mini_kraken/glue/fresh_env_factory.rb +83 -0
- data/lib/mini_kraken/glue/run_star_expression.rb +2 -2
- data/lib/mini_kraken/version.rb +1 -1
- data/spec/core/conde_spec.rb +9 -9
- data/spec/core/conj2_spec.rb +7 -7
- data/spec/core/cons_cell_visitor_spec.rb +144 -0
- data/spec/core/def_relation_spec.rb +6 -5
- data/spec/core/disj2_spec.rb +5 -5
- data/spec/core/duck_fiber_spec.rb +1 -1
- data/spec/core/equals_spec.rb +34 -21
- data/spec/core/goal_spec.rb +2 -2
- data/spec/core/outcome_spec.rb +8 -0
- data/spec/core/variable_ref_spec.rb +3 -0
- data/spec/glue/dsl_chap1_spec.rb +10 -2
- data/spec/glue/dsl_chap2_spec.rb +100 -0
- data/spec/glue/fresh_env_factory_spec.rb +97 -0
- data/spec/glue/run_star_expression_spec.rb +3 -2
- metadata +10 -2
@@ -24,7 +24,11 @@ unless MiniKraken::Core.constants(false).include? :Outcome
|
|
24
24
|
new(:"#s", aParent)
|
25
25
|
end
|
26
26
|
|
27
|
-
def
|
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
|
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 << '>'
|
data/lib/mini_kraken/glue/dsl.rb
CHANGED
@@ -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
|
-
|
103
|
-
|
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
|
-
|
106
|
-
|
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
|
-
|
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
|
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
|
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.
|
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.
|
42
|
+
outcome.success? ? var.quote(outcome) : nil
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
data/lib/mini_kraken/version.rb
CHANGED
data/spec/core/conde_spec.rb
CHANGED
@@ -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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
|
data/spec/core/conj2_spec.rb
CHANGED
@@ -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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|