mini_kraken 0.1.12 → 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/.rubocop.yml +334 -0
- data/CHANGELOG.md +54 -0
- data/README.md +95 -13
- data/lib/mini_kraken.rb +7 -1
- data/lib/mini_kraken/core/any_value.rb +5 -1
- data/lib/mini_kraken/core/atomic_term.rb +1 -0
- 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.rb +29 -1
- data/lib/mini_kraken/core/cons_cell_visitor.rb +102 -0
- data/lib/mini_kraken/core/def_relation.rb +4 -0
- data/lib/mini_kraken/core/disj2.rb +2 -2
- data/lib/mini_kraken/core/environment.rb +2 -2
- data/lib/mini_kraken/core/equals.rb +60 -26
- data/lib/mini_kraken/core/formal_ref.rb +2 -1
- data/lib/mini_kraken/core/goal.rb +4 -2
- data/lib/mini_kraken/core/goal_template.rb +44 -2
- data/lib/mini_kraken/core/k_boolean.rb +4 -0
- data/lib/mini_kraken/core/k_symbol.rb +11 -0
- data/lib/mini_kraken/core/outcome.rb +11 -1
- data/lib/mini_kraken/core/variable.rb +10 -4
- data/lib/mini_kraken/core/variable_ref.rb +7 -0
- data/lib/mini_kraken/core/vocabulary.rb +8 -3
- data/lib/mini_kraken/glue/dsl.rb +236 -0
- 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 +3 -5
- data/lib/mini_kraken/version.rb +1 -1
- data/mini_kraken.gemspec +6 -3
- data/spec/.rubocop.yml +13 -0
- data/spec/core/conde_spec.rb +10 -10
- data/spec/core/conj2_spec.rb +7 -7
- data/spec/core/cons_cell_spec.rb +35 -0
- 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 +2 -2
- data/spec/core/equals_spec.rb +34 -21
- data/spec/core/goal_spec.rb +2 -2
- data/spec/core/k_boolean_spec.rb +6 -0
- data/spec/core/k_symbol_spec.rb +4 -0
- data/spec/core/outcome_spec.rb +8 -0
- data/spec/core/variable_ref_spec.rb +3 -0
- data/spec/glue/dsl_chap1_spec.rb +679 -0
- 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 +11 -11
- metadata +17 -4
@@ -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
|
|
@@ -38,11 +38,9 @@ module MiniKraken
|
|
38
38
|
|
39
39
|
# @return [Array] A vector of assignment for each variable
|
40
40
|
def build_solution(outcome)
|
41
|
-
|
42
|
-
outcome.
|
41
|
+
env.vars.values.map do |var|
|
42
|
+
outcome.success? ? var.quote(outcome) : nil
|
43
43
|
end
|
44
|
-
|
45
|
-
sol
|
46
44
|
end
|
47
45
|
|
48
46
|
# Transform the solutions into sequence of conscells.
|
data/lib/mini_kraken/version.rb
CHANGED
data/mini_kraken.gemspec
CHANGED
@@ -9,6 +9,7 @@ module PkgExtending
|
|
9
9
|
def self.pkg_files(aPackage)
|
10
10
|
file_list = Dir[
|
11
11
|
'.rspec',
|
12
|
+
'.rubocop.yml',
|
12
13
|
'.travis.yml',
|
13
14
|
'Gemfile',
|
14
15
|
'Rakefile',
|
@@ -19,7 +20,8 @@ module PkgExtending
|
|
19
20
|
'bin/*.rb',
|
20
21
|
'lib/*.*',
|
21
22
|
'lib/**/*.rb',
|
22
|
-
'spec/**/*.rb'
|
23
|
+
'spec/**/*.rb',
|
24
|
+
'spec/.rubocop.yml'
|
23
25
|
]
|
24
26
|
aPackage.files = file_list
|
25
27
|
aPackage.test_files = Dir['spec/**/*_spec.rb']
|
@@ -38,8 +40,8 @@ Gem::Specification.new do |spec|
|
|
38
40
|
spec.authors = ['Dimitri Geshef']
|
39
41
|
spec.email = ['famished.tiger@yahoo.com']
|
40
42
|
|
41
|
-
spec.summary =
|
42
|
-
spec.description =
|
43
|
+
spec.summary = 'Implementation of Minikanren language in Ruby. WIP'
|
44
|
+
spec.description = 'Implementation of Minikanren language in Ruby. WIP'
|
43
45
|
spec.homepage = 'https://github.com/famished-tiger/mini_kraken'
|
44
46
|
spec.license = 'MIT'
|
45
47
|
|
@@ -47,6 +49,7 @@ Gem::Specification.new do |spec|
|
|
47
49
|
spec.bindir = 'exe'
|
48
50
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
49
51
|
spec.require_paths = ['lib']
|
52
|
+
spec.required_ruby_version = '~> 2.4'
|
50
53
|
|
51
54
|
PkgExtending.pkg_files(spec)
|
52
55
|
PkgExtending.pkg_documentation(spec)
|
data/spec/.rubocop.yml
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
inherit_from: ../.rubocop.yml
|
2
|
+
|
3
|
+
# RSpec expectation lines can be very lenghty
|
4
|
+
Layout/LineLength:
|
5
|
+
Max: 99
|
6
|
+
|
7
|
+
# RSpec contexts can be very lenghty
|
8
|
+
Metrics/BlockLength:
|
9
|
+
Max: 1000
|
10
|
+
|
11
|
+
# RSpec modules can be very lenghty
|
12
|
+
Metrics/ModuleLength:
|
13
|
+
Max: 1000
|
data/spec/core/conde_spec.rb
CHANGED
@@ -58,9 +58,9 @@ module MiniKraken
|
|
58
58
|
expect { subject.solver_for([pea, succeeds], env) }.to raise_error(err)
|
59
59
|
end
|
60
60
|
|
61
|
-
it 'should
|
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
|
data/spec/core/cons_cell_spec.rb
CHANGED
@@ -11,6 +11,7 @@ module MiniKraken
|
|
11
11
|
describe ConsCell do
|
12
12
|
let(:pea) { KSymbol.new(:pea) }
|
13
13
|
let(:pod) { KSymbol.new(:pod) }
|
14
|
+
let(:corn) { KSymbol.new(:corn) }
|
14
15
|
subject { ConsCell.new(pea, pod) }
|
15
16
|
|
16
17
|
context 'Initialization:' do
|
@@ -39,6 +40,12 @@ module MiniKraken
|
|
39
40
|
expect(ConsCell.new(nil, nil)).to be_null
|
40
41
|
expect(NullList).to be_null
|
41
42
|
end
|
43
|
+
|
44
|
+
it 'simplifies cdr if its referencing a null list' do
|
45
|
+
instance = ConsCell.new(pea, NullList)
|
46
|
+
expect(instance.car).to eq(pea)
|
47
|
+
expect(instance.cdr).to be_nil
|
48
|
+
end
|
42
49
|
end # context
|
43
50
|
|
44
51
|
context 'Provided services:' do
|
@@ -66,6 +73,34 @@ module MiniKraken
|
|
66
73
|
expect(instance.car).to eq(pea)
|
67
74
|
expect(instance.cdr).to eq(trail)
|
68
75
|
end
|
76
|
+
|
77
|
+
it 'should provide a list representation of itself' do
|
78
|
+
# Case of null list
|
79
|
+
expect(NullList.to_s).to eq '()'
|
80
|
+
|
81
|
+
# Case of one element proper list
|
82
|
+
cell = ConsCell.new(pea)
|
83
|
+
expect(cell.to_s).to eq '(:pea)'
|
84
|
+
|
85
|
+
# Case of two elements proper list
|
86
|
+
cell = ConsCell.new(pea, ConsCell.new(pod))
|
87
|
+
expect(cell.to_s).to eq '(:pea :pod)'
|
88
|
+
|
89
|
+
# Case of two elements improper list
|
90
|
+
expect(subject.to_s).to eq '(:pea . :pod)'
|
91
|
+
|
92
|
+
# Case of three elements proper list
|
93
|
+
cell = ConsCell.new(pea, ConsCell.new(pod, ConsCell.new(corn)))
|
94
|
+
expect(cell.to_s).to eq '(:pea :pod :corn)'
|
95
|
+
|
96
|
+
# Case of three elements improper list
|
97
|
+
cell = ConsCell.new(pea, ConsCell.new(pod, corn))
|
98
|
+
expect(cell.to_s).to eq '(:pea :pod . :corn)'
|
99
|
+
|
100
|
+
# Case of a nested list
|
101
|
+
cell = ConsCell.new(ConsCell.new(pea), ConsCell.new(pod))
|
102
|
+
expect(cell.to_s).to eq '((:pea) :pod)'
|
103
|
+
end
|
69
104
|
end # context
|
70
105
|
end # describe
|
71
106
|
end # module
|
@@ -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
|