mini_kraken 0.2.02 → 0.3.02

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.
Files changed (134) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +378 -333
  3. data/CHANGELOG.md +52 -0
  4. data/README.md +19 -19
  5. data/lib/mini_kraken.rb +0 -1
  6. data/lib/mini_kraken/atomic/all_atomic.rb +5 -0
  7. data/lib/mini_kraken/atomic/atomic_term.rb +96 -0
  8. data/lib/mini_kraken/atomic/k_boolean.rb +42 -0
  9. data/lib/mini_kraken/{core → atomic}/k_integer.rb +2 -5
  10. data/lib/mini_kraken/atomic/k_string.rb +17 -0
  11. data/lib/mini_kraken/{core → atomic}/k_symbol.rb +4 -8
  12. data/lib/mini_kraken/composite/all_composite.rb +4 -0
  13. data/lib/mini_kraken/composite/composite_term.rb +27 -0
  14. data/lib/mini_kraken/composite/cons_cell.rb +299 -0
  15. data/lib/mini_kraken/composite/cons_cell_visitor.rb +50 -0
  16. data/lib/mini_kraken/composite/list.rb +32 -0
  17. data/lib/mini_kraken/core/all_core.rb +8 -0
  18. data/lib/mini_kraken/core/any_value.rb +31 -7
  19. data/lib/mini_kraken/core/arity.rb +69 -0
  20. data/lib/mini_kraken/core/association.rb +29 -4
  21. data/lib/mini_kraken/core/association_copy.rb +50 -0
  22. data/lib/mini_kraken/core/base_term.rb +13 -0
  23. data/lib/mini_kraken/core/blackboard.rb +315 -0
  24. data/lib/mini_kraken/core/bookmark.rb +46 -0
  25. data/lib/mini_kraken/core/context.rb +492 -0
  26. data/lib/mini_kraken/core/duck_fiber.rb +21 -19
  27. data/lib/mini_kraken/core/entry.rb +40 -0
  28. data/lib/mini_kraken/core/fail.rb +20 -18
  29. data/lib/mini_kraken/core/fusion.rb +29 -0
  30. data/lib/mini_kraken/core/goal.rb +20 -29
  31. data/lib/mini_kraken/core/log_var.rb +22 -0
  32. data/lib/mini_kraken/core/log_var_ref.rb +108 -0
  33. data/lib/mini_kraken/core/nullary_relation.rb +2 -9
  34. data/lib/mini_kraken/core/parametrized_term.rb +61 -0
  35. data/lib/mini_kraken/core/relation.rb +14 -28
  36. data/lib/mini_kraken/core/scope.rb +67 -0
  37. data/lib/mini_kraken/core/solver_adapter.rb +58 -0
  38. data/lib/mini_kraken/core/specification.rb +48 -0
  39. data/lib/mini_kraken/core/succeed.rb +21 -17
  40. data/lib/mini_kraken/core/symbol_table.rb +137 -0
  41. data/lib/mini_kraken/core/term.rb +15 -4
  42. data/lib/mini_kraken/glue/dsl.rb +45 -81
  43. data/lib/mini_kraken/glue/run_star_expression.rb +28 -30
  44. data/lib/mini_kraken/rela/all_rela.rb +8 -0
  45. data/lib/mini_kraken/rela/binary_relation.rb +30 -0
  46. data/lib/mini_kraken/rela/conde.rb +143 -0
  47. data/lib/mini_kraken/rela/conj2.rb +65 -0
  48. data/lib/mini_kraken/rela/def_relation.rb +93 -0
  49. data/lib/mini_kraken/rela/disj2.rb +70 -0
  50. data/lib/mini_kraken/rela/fresh.rb +98 -0
  51. data/lib/mini_kraken/{core → rela}/goal_relation.rb +7 -9
  52. data/lib/mini_kraken/rela/unify.rb +258 -0
  53. data/lib/mini_kraken/version.rb +1 -1
  54. data/mini_kraken.gemspec +2 -2
  55. data/spec/.rubocop.yml +1 -1
  56. data/spec/atomic/atomic_term_spec.rb +98 -0
  57. data/spec/{core → atomic}/k_boolean_spec.rb +19 -34
  58. data/spec/{core → atomic}/k_symbol_spec.rb +3 -16
  59. data/spec/composite/cons_cell_spec.rb +225 -0
  60. data/spec/composite/cons_cell_visitor_spec.rb +158 -0
  61. data/spec/composite/list_spec.rb +50 -0
  62. data/spec/core/any_value_spec.rb +52 -0
  63. data/spec/core/arity_spec.rb +92 -0
  64. data/spec/core/association_copy_spec.rb +69 -0
  65. data/spec/core/association_spec.rb +31 -4
  66. data/spec/core/blackboard_spec.rb +287 -0
  67. data/spec/core/bookmark_spec.rb +40 -0
  68. data/spec/core/context_spec.rb +245 -0
  69. data/spec/core/core_spec.rb +40 -0
  70. data/spec/core/duck_fiber_spec.rb +16 -46
  71. data/spec/core/fail_spec.rb +5 -6
  72. data/spec/core/goal_spec.rb +24 -14
  73. data/spec/core/log_var_ref_spec.rb +105 -0
  74. data/spec/core/log_var_spec.rb +64 -0
  75. data/spec/core/nullary_relation_spec.rb +33 -0
  76. data/spec/core/parametrized_tem_spec.rb +39 -0
  77. data/spec/core/relation_spec.rb +33 -0
  78. data/spec/core/scope_spec.rb +73 -0
  79. data/spec/core/solver_adapter_spec.rb +70 -0
  80. data/spec/core/specification_spec.rb +43 -0
  81. data/spec/core/succeed_spec.rb +5 -5
  82. data/spec/core/symbol_table_spec.rb +142 -0
  83. data/spec/glue/dsl_chap1_spec.rb +96 -144
  84. data/spec/glue/dsl_chap2_spec.rb +350 -0
  85. data/spec/glue/run_star_expression_spec.rb +82 -906
  86. data/spec/rela/conde_spec.rb +153 -0
  87. data/spec/rela/conj2_spec.rb +123 -0
  88. data/spec/rela/def_relation_spec.rb +119 -0
  89. data/spec/rela/disj2_spec.rb +117 -0
  90. data/spec/rela/fresh_spec.rb +147 -0
  91. data/spec/rela/unify_spec.rb +369 -0
  92. data/spec/support/factory_atomic.rb +29 -0
  93. data/spec/support/factory_composite.rb +21 -0
  94. data/spec/support/factory_methods.rb +11 -26
  95. metadata +100 -64
  96. data/lib/mini_kraken/core/association_walker.rb +0 -183
  97. data/lib/mini_kraken/core/atomic_term.rb +0 -67
  98. data/lib/mini_kraken/core/base_arg.rb +0 -10
  99. data/lib/mini_kraken/core/binary_relation.rb +0 -63
  100. data/lib/mini_kraken/core/composite_goal.rb +0 -46
  101. data/lib/mini_kraken/core/composite_term.rb +0 -41
  102. data/lib/mini_kraken/core/conde.rb +0 -143
  103. data/lib/mini_kraken/core/conj2.rb +0 -79
  104. data/lib/mini_kraken/core/cons_cell.rb +0 -82
  105. data/lib/mini_kraken/core/def_relation.rb +0 -50
  106. data/lib/mini_kraken/core/designation.rb +0 -55
  107. data/lib/mini_kraken/core/disj2.rb +0 -72
  108. data/lib/mini_kraken/core/environment.rb +0 -73
  109. data/lib/mini_kraken/core/equals.rb +0 -156
  110. data/lib/mini_kraken/core/formal_arg.rb +0 -22
  111. data/lib/mini_kraken/core/formal_ref.rb +0 -25
  112. data/lib/mini_kraken/core/freshness.rb +0 -45
  113. data/lib/mini_kraken/core/goal_arg.rb +0 -12
  114. data/lib/mini_kraken/core/goal_template.rb +0 -62
  115. data/lib/mini_kraken/core/k_boolean.rb +0 -35
  116. data/lib/mini_kraken/core/outcome.rb +0 -53
  117. data/lib/mini_kraken/core/variable.rb +0 -41
  118. data/lib/mini_kraken/core/variable_ref.rb +0 -78
  119. data/lib/mini_kraken/core/vocabulary.rb +0 -442
  120. data/lib/mini_kraken/glue/fresh_env.rb +0 -75
  121. data/spec/core/association_walker_spec.rb +0 -192
  122. data/spec/core/conde_spec.rb +0 -147
  123. data/spec/core/conj2_spec.rb +0 -114
  124. data/spec/core/cons_cell_spec.rb +0 -107
  125. data/spec/core/def_relation_spec.rb +0 -96
  126. data/spec/core/disj2_spec.rb +0 -99
  127. data/spec/core/environment_spec.rb +0 -142
  128. data/spec/core/equals_spec.rb +0 -304
  129. data/spec/core/goal_template_spec.rb +0 -74
  130. data/spec/core/outcome_spec.rb +0 -48
  131. data/spec/core/variable_ref_spec.rb +0 -27
  132. data/spec/core/variable_spec.rb +0 -35
  133. data/spec/core/vocabulary_spec.rb +0 -219
  134. data/spec/glue/fresh_env_spec.rb +0 -62
@@ -4,10 +4,10 @@ require_relative '../spec_helper' # Use the RSpec framework
4
4
  require 'ostruct'
5
5
 
6
6
  # Load the class under test
7
- require_relative '../../lib/mini_kraken/core/k_boolean'
7
+ require_relative '../../lib/mini_kraken/atomic/k_boolean'
8
8
 
9
9
  module MiniKraken
10
- module Core
10
+ module Atomic
11
11
  describe KBoolean do
12
12
  subject { KBoolean.new('#t') }
13
13
 
@@ -28,12 +28,23 @@ module MiniKraken
28
28
  end
29
29
 
30
30
  it 'should know its value' do
31
- expect(subject.value).to eq(true)
32
- end
31
+ other = KBoolean.new(true)
32
+ expect(other.value).to eq(true)
33
+
34
+ other = KBoolean.new('#t')
35
+ expect(other.value).to eq(true)
36
+
37
+ other = KBoolean.new(:"#t")
38
+ expect(other.value).to eq(true)
39
+
40
+ other = KBoolean.new(false)
41
+ expect(other.value).to eq(false)
42
+
43
+ other = KBoolean.new('#f')
44
+ expect(other.value).to eq(false)
33
45
 
34
- it 'should know that it is a ground term' do
35
- env = double('mock-env')
36
- expect(subject.ground?(env)).to be_truthy
46
+ other = KBoolean.new(:"#f")
47
+ expect(other.value).to eq(false)
37
48
  end
38
49
  end # context
39
50
 
@@ -43,27 +54,13 @@ module MiniKraken
43
54
  other = KBoolean.new(true)
44
55
  expect(subject).to be_eql(other)
45
56
 
46
- other = KBoolean.new(:"#t")
47
- expect(subject).to be_eql(other)
48
-
49
- other = KBoolean.new('#t')
50
- expect(subject).to be_eql(other)
51
-
52
57
  # Same type, other value
53
58
  another = KBoolean.new(false)
54
59
  expect(subject).not_to be_eql(another)
55
60
 
56
- # Same type, other value
57
- another = KBoolean.new(:"#f")
58
- expect(subject).not_to be_eql(another)
59
-
60
61
  # Same type, other value
61
62
  another = KBoolean.new('#f')
62
63
  expect(subject).not_to be_eql(another)
63
-
64
- # Different type, same value
65
- yet_another = OpenStruct.new(value: true)
66
- expect(subject).not_to be_eql(yet_another)
67
64
  end
68
65
 
69
66
  it 'should know whether it has same value than other object' do
@@ -71,27 +68,15 @@ module MiniKraken
71
68
  other = KBoolean.new(true)
72
69
  expect(subject == other).to be_truthy
73
70
 
74
- other = KBoolean.new(:"#t")
75
- expect(subject == other).to be_truthy
76
-
77
- other = KBoolean.new('#t')
78
- expect(subject == other).to be_truthy
79
-
80
71
  # Same type, other value
81
72
  another = KBoolean.new(false)
82
73
  expect(subject == another).to be_falsy
83
74
 
84
- another = KBoolean.new(:"#f")
85
- expect(subject == another).to be_falsy
86
-
87
- another = KBoolean.new('#f')
88
- expect(subject == another).to be_falsy
89
-
90
75
  # Same duck type, same value
91
76
  yet_another = OpenStruct.new(value: true)
92
77
  expect(subject == yet_another).to be_truthy
93
78
 
94
- # Different duck type, different value
79
+ # Same duck type, different value
95
80
  still_another = OpenStruct.new(value: false)
96
81
  expect(subject == still_another).to be_falsy
97
82
 
@@ -4,10 +4,10 @@ require_relative '../spec_helper' # Use the RSpec framework
4
4
  require 'ostruct'
5
5
 
6
6
  # Load the class under test
7
- require_relative '../../lib/mini_kraken/core/k_symbol'
7
+ require_relative '../../lib/mini_kraken/atomic/k_symbol'
8
8
 
9
9
  module MiniKraken
10
- module Core
10
+ module Atomic
11
11
  describe KSymbol do
12
12
  let(:a_value) { :pea }
13
13
  subject { KSymbol.new(a_value) }
@@ -20,11 +20,6 @@ module MiniKraken
20
20
  it 'should know its value' do
21
21
  expect(subject.value).to eq(a_value)
22
22
  end
23
-
24
- it 'should know that it is a ground term' do
25
- env = double('mock-env')
26
- expect(subject.ground?(env)).to be_truthy
27
- end
28
23
  end # context
29
24
 
30
25
  context 'Provided services:' do
@@ -37,7 +32,7 @@ module MiniKraken
37
32
  another = KSymbol.new(:pod)
38
33
  expect(subject).not_to be_eql(another)
39
34
 
40
- # Different type, same value
35
+ # Different duck type, same value
41
36
  yet_another = OpenStruct.new(value: :pea)
42
37
  expect(subject).not_to be_eql(yet_another)
43
38
  end
@@ -51,14 +46,6 @@ module MiniKraken
51
46
  another = KSymbol.new(:pod)
52
47
  expect(subject == another).to be_falsy
53
48
 
54
- # Same duck type, same value
55
- yet_another = OpenStruct.new(value: :pea)
56
- expect(subject == yet_another).to be_truthy
57
-
58
- # Different duck type, different value
59
- still_another = OpenStruct.new(value: :pod)
60
- expect(subject == still_another).to be_falsy
61
-
62
49
  # Default Ruby representation, same value
63
50
  expect(subject == :pea).to be_truthy
64
51
 
@@ -0,0 +1,225 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../spec_helper' # Use the RSpec framework
4
+ require_relative '../support/factory_atomic'
5
+ require_relative '../../lib/mini_kraken/core/context'
6
+ require_relative '../../lib/mini_kraken/core/log_var'
7
+ require_relative '../../lib/mini_kraken/core/log_var_ref'
8
+
9
+ # Load the class under test
10
+ require_relative '../../lib/mini_kraken/composite/cons_cell'
11
+
12
+ module MiniKraken
13
+ module Composite
14
+ describe ConsCell do
15
+ include MiniKraken::FactoryAtomic # Use mix-in module
16
+
17
+ let(:pea) { k_symbol(:pea) }
18
+ let(:pod) { k_symbol(:pod) }
19
+ let(:corn) { k_symbol(:corn) }
20
+ let(:ctx) { Core::Context.new }
21
+ subject { ConsCell.new(pea, pod) }
22
+
23
+ context 'Initialization:' do
24
+ it 'could be initialized with one argument' do
25
+ expect { ConsCell.new(pea) }.not_to raise_error
26
+ end
27
+
28
+ it 'could be initialized with a second optional argument' do
29
+ expect { ConsCell.new(pea, pod) }.not_to raise_error
30
+ end
31
+
32
+ it 'could be initialized as null list' do
33
+ expect { ConsCell.null }.not_to raise_error
34
+ end
35
+
36
+ it 'should know its car child' do
37
+ expect(subject.car).to eq(pea)
38
+ end
39
+
40
+ it 'should know its cdr child' do
41
+ expect(subject.cdr).to eq(pod)
42
+ end
43
+
44
+
45
+ it 'should know its children' do
46
+ expect(subject.children).to eq([pea, pod])
47
+ end
48
+
49
+ it 'should know if it is empty (null)' do
50
+ expect(subject).not_to be_null
51
+ expect(ConsCell.new(nil, nil)).to be_null
52
+ expect(ConsCell.null).to be_null
53
+ expect(NullList).to be_null
54
+ end
55
+
56
+ it 'simplifies cdr if its referencing a null list' do
57
+ instance = ConsCell.new(pea, NullList)
58
+ expect(instance.car).to eq(pea)
59
+ expect(instance.cdr).to be_nil
60
+ end
61
+ end # context
62
+
63
+ context 'Provided services:' do
64
+ it 'should compare to itself' do
65
+ expect(subject.eql?(subject)).to be_truthy
66
+ synonym = subject
67
+ expect(subject == synonym).to be_truthy
68
+ end
69
+
70
+ it 'should compare to another instance' do
71
+ same = ConsCell.new(pea, pod)
72
+ expect(subject.eql?(same)).to be_truthy
73
+
74
+ different = ConsCell.new(pod, pea)
75
+ expect(subject.eql?(different)).to be_falsey
76
+
77
+ different = ConsCell.new(pea)
78
+ expect(subject.eql?(different)).to be_falsey
79
+ end
80
+
81
+ it 'should set_car! another cons cell' do
82
+ instance = ConsCell.new(pea)
83
+ head = ConsCell.new(pod)
84
+ instance.set_car!(head)
85
+ expect(instance.car).to eq(head)
86
+ expect(instance.cdr).to be_nil
87
+ end
88
+
89
+ it 'should set_cdr! another cons cell' do
90
+ instance = ConsCell.new(pea)
91
+ trail = ConsCell.new(pod)
92
+ instance.set_cdr!(trail)
93
+ expect(instance.car).to eq(pea)
94
+ expect(instance.cdr).to eq(trail)
95
+ end
96
+
97
+ it 'should set a member to some term' do
98
+ instance = ConsCell.null
99
+ head = ConsCell.new(pea)
100
+ trail = ConsCell.new(pod)
101
+ instance.set!(:car, head)
102
+ instance.set!(:cdr, trail)
103
+ expect(instance.car).to eq(head)
104
+ expect(instance.cdr).to eq(trail)
105
+ end
106
+
107
+ it 'should know whether it is pinned or not' do
108
+ # Case: all pair members are atomic items
109
+ expect(subject).to be_pinned(ctx)
110
+
111
+ # Case: cdr is nil
112
+ instance = ConsCell.new(pea)
113
+ expect(instance).to be_pinned(ctx)
114
+
115
+ # Case: embedded composite
116
+ nested = ConsCell.new(ConsCell.new(pod, pea), ConsCell.new(pea))
117
+ expect(nested).to be_pinned(ctx)
118
+
119
+ ctx.add_vars('q')
120
+ nested.set_cdr!(Core::LogVarRef.new('q'))
121
+ expect(nested).not_to be_pinned(ctx)
122
+ expect(nested).to be_floating(ctx)
123
+
124
+ ctx.associate('q', ConsCell.new(pea))
125
+ expect(nested).to be_pinned(ctx)
126
+ end
127
+
128
+ it 'should provide a list representation of itself' do
129
+ # Case of null list
130
+ expect(NullList.to_s).to eq '()'
131
+
132
+ # Case of one element proper list
133
+ cell = ConsCell.new(pea)
134
+ expect(cell.to_s).to eq '(:pea)'
135
+
136
+ # Case of two elements proper list
137
+ cell = ConsCell.new(pea, ConsCell.new(pod))
138
+ expect(cell.to_s).to eq '(:pea :pod)'
139
+
140
+ # Case of two elements improper list
141
+ expect(subject.to_s).to eq '(:pea . :pod)'
142
+
143
+ # Case of one element plus null list
144
+ cell = ConsCell.new(pea)
145
+ cell.set_cdr!(ConsCell.null)
146
+ expect(cell.to_s).to eq '(:pea)'
147
+
148
+ # Case of three elements proper list
149
+ cell = ConsCell.new(pea, ConsCell.new(pod, ConsCell.new(corn)))
150
+ expect(cell.to_s).to eq '(:pea :pod :corn)'
151
+
152
+ # Case of three elements improper list
153
+ cell = ConsCell.new(pea, ConsCell.new(pod, corn))
154
+ expect(cell.to_s).to eq '(:pea :pod . :corn)'
155
+
156
+ # Case of a nested list
157
+ cell = ConsCell.new(ConsCell.new(pea), ConsCell.new(pod))
158
+ expect(cell.to_s).to eq '((:pea) :pod)'
159
+ end
160
+
161
+ it 'should know its dependencies' do
162
+ # Case: no var ref...
163
+ lst = ConsCell.new(ConsCell.new(pod, pea), ConsCell.new(pea))
164
+ expect(lst.dependencies(ctx)).to be_empty
165
+
166
+ # Case: multiple var refs
167
+ ctx.add_vars(%w[q x])
168
+ q_ref = Core::LogVarRef.new('q')
169
+ x_ref = Core::LogVarRef.new('x')
170
+ list2 = ConsCell.new(ConsCell.new(q_ref, pea), ConsCell.new(x_ref))
171
+ expect(list2.dependencies(ctx).size).to eq(2)
172
+ q_var = ctx.lookup('q')
173
+ x_var = ctx.lookup('x')
174
+ predicted = Set.new([q_var.i_name, x_var.i_name])
175
+ expect(list2.dependencies(ctx)).to eq(predicted)
176
+ end
177
+
178
+ it 'should, as list with atomic terms, provide an expanded copy' do
179
+ # Case of a list of atomic terms
180
+ lst = ConsCell.new(ConsCell.new(ConsCell.new(pea), pod))
181
+ representation = lst.to_s
182
+
183
+ copy = lst.expand(ctx, {})
184
+ expect(copy.to_s).to eq(representation)
185
+ end
186
+
187
+ it 'should, as list with variable refs, provide an expanded copy' do
188
+ # Case of a list of variable refs
189
+ ctx.add_vars(['x'])
190
+ x_ref = Core::LogVarRef.new('x')
191
+ x_var = ctx.lookup('x')
192
+ ctx.associate('x', Core::AnyValue.new(0))
193
+ lst = ConsCell.new(x_ref, ConsCell.new(x_ref))
194
+ substitutions = {}
195
+ substitutions[x_var.i_name] = Core::AnyValue.new(0)
196
+
197
+ copy = lst.expand(ctx, substitutions)
198
+ expect(copy.to_s).to eq('(_0 _0)')
199
+ end
200
+
201
+ it 'should provide a duplicate with variable replaced by their value' do
202
+ q_ref = Core::LogVarRef.new('q')
203
+ x_ref = Core::LogVarRef.new('x')
204
+ y_ref = Core::LogVarRef.new('y')
205
+ substitutions = {
206
+ 'q' => ConsCell.new(pea, ConsCell.new(x_ref, y_ref)),
207
+ 'x' => pod,
208
+ 'y' => corn
209
+ }
210
+
211
+ # Basic case: variable ref points to an atomic value
212
+ expr = ConsCell.new(pea, x_ref)
213
+ duplicate = expr.dup_cond(substitutions)
214
+ expect(duplicate.to_s).to eq('(:pea . :pod)')
215
+
216
+ expr = ConsCell.new(pod, ConsCell.new(q_ref, y_ref))
217
+ duplicate = expr.dup_cond(substitutions)
218
+ expect(duplicate.car).to eq(pod)
219
+ expect(duplicate.cdr.car.to_s).to eq('(:pea :pod . :corn)')
220
+ expect(duplicate.to_s).to eq('(:pod (:pea :pod . :corn) . :corn)')
221
+ end
222
+ end # context
223
+ end # describe
224
+ end # module
225
+ end # module
@@ -0,0 +1,158 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../spec_helper' # Use the RSpec framework
4
+ require_relative '../support/factory_atomic'
5
+ require_relative '../../lib/mini_kraken/core/log_var_ref'
6
+
7
+ # Load the class under test
8
+ require_relative '../../lib/mini_kraken/composite/cons_cell_visitor'
9
+
10
+ module MiniKraken
11
+ module Composite
12
+ describe ConsCellVisitor do
13
+ include MiniKraken::FactoryAtomic # Use mix-in module
14
+
15
+ let(:pea) { k_symbol(:pea) }
16
+ let(:pod) { k_symbol(:pod) }
17
+ let(:corn) { k_symbol(:corn) }
18
+ let(:ref_q) { LogVarRef.new('q') }
19
+ let(:l_pea) { ConsCell.new(pea) }
20
+ let(:l_pea_pod) { ConsCell.new(pea, ConsCell.new(pod)) }
21
+ let(:l_pea_pod_corn) { ConsCell.new(pea, ConsCell.new(pod, ConsCell.new(corn))) }
22
+ subject { ConsCellVisitor }
23
+
24
+ context 'Provided services:' do
25
+ it 'acts as a factory of Fiber objects' do
26
+ expect(subject.df_visitor(l_pea)).to be_kind_of(Fiber)
27
+ end
28
+ end # context
29
+
30
+ context 'proper list visiting:' do
31
+ it 'can visit a null list' do
32
+ null_list = ConsCell.null
33
+ visitor = subject.df_visitor(null_list)
34
+ expect(visitor.resume).to eq([:car, null_list])
35
+ expect(visitor.resume).to eq([:car, nil])
36
+ expect(visitor.resume).to eq([:cdr, nil])
37
+ expect(visitor.resume).to eq([:stop, nil])
38
+ end
39
+
40
+ it 'can visit a single element proper list' do
41
+ visitor = subject.df_visitor(l_pea)
42
+ expect(visitor.resume).to eq([:car, l_pea])
43
+ expect(visitor.resume).to eq([:car, pea])
44
+ expect(visitor.resume).to eq([:cdr, nil])
45
+ expect(visitor.resume).to eq([:stop, nil])
46
+ end
47
+
48
+ it 'can visit a two elements proper list' do
49
+ visitor = subject.df_visitor(l_pea_pod)
50
+ expect(visitor.resume).to eq([:car, l_pea_pod])
51
+ expect(visitor.resume).to eq([:car, pea])
52
+ expect(visitor.resume).to eq([:cdr, l_pea_pod.cdr])
53
+ expect(visitor.resume).to eq([:car, pod])
54
+ expect(visitor.resume).to eq([:cdr, nil])
55
+ expect(visitor.resume).to eq([:stop, nil])
56
+ end
57
+
58
+ it 'can visit a three elements proper list' do
59
+ visitor = subject.df_visitor(l_pea_pod_corn)
60
+ expect(visitor.resume).to eq([:car, l_pea_pod_corn])
61
+ expect(visitor.resume).to eq([:car, pea])
62
+ expect(visitor.resume).to eq([:cdr, l_pea_pod_corn.cdr])
63
+ expect(visitor.resume).to eq([:car, pod])
64
+ expect(visitor.resume).to eq([:cdr, l_pea_pod_corn.cdr.cdr])
65
+ expect(visitor.resume).to eq([:car, corn])
66
+ expect(visitor.resume).to eq([:cdr, nil])
67
+ expect(visitor.resume).to eq([:stop, nil])
68
+ end
69
+ end # context
70
+
71
+ context 'improper list visiting:' do
72
+ it 'can visit a single element improper list' do
73
+ l_improper = ConsCell.new(nil, pea)
74
+ visitor = subject.df_visitor(l_improper)
75
+ expect(visitor.resume).to eq([:car, l_improper])
76
+ expect(visitor.resume).to eq([:car, nil])
77
+ expect(visitor.resume).to eq([:cdr, pea])
78
+ expect(visitor.resume).to eq([:stop, nil])
79
+ end
80
+
81
+ it 'can visit a two elements improper list' do
82
+ l_improper = ConsCell.new(pea, pod)
83
+ visitor = subject.df_visitor(l_improper)
84
+ expect(visitor.resume).to eq([:car, l_improper])
85
+ expect(visitor.resume).to eq([:car, pea])
86
+ expect(visitor.resume).to eq([:cdr, pod])
87
+ expect(visitor.resume).to eq([:stop, nil])
88
+ end
89
+
90
+ it 'can visit a three elements improper list' do
91
+ l_improper = ConsCell.new(pea, ConsCell.new(pod, corn))
92
+ expect(l_improper.to_s).to eq('(:pea :pod . :corn)')
93
+ visitor = subject.df_visitor(l_improper)
94
+ expect(visitor.resume).to eq([:car, l_improper])
95
+ expect(visitor.resume).to eq([:car, pea])
96
+ expect(visitor.resume).to eq([:cdr, l_improper.cdr])
97
+ expect(visitor.resume).to eq([:car, pod])
98
+ expect(visitor.resume).to eq([:cdr, corn])
99
+ expect(visitor.resume).to eq([:stop, nil])
100
+ end
101
+
102
+ it 'smurch' do
103
+ composite = ConsCell.new(ConsCell.new(ConsCell.new(pea)), pod)
104
+ visitor = subject.df_visitor(composite)
105
+ expect(visitor.resume).to eq([:car, composite])
106
+ expect(visitor.resume).to eq([:car, composite.car])
107
+ expect(visitor.resume).to eq([:car, composite.car.car])
108
+ expect(visitor.resume).to eq([:car, pea])
109
+ expect(visitor.resume).to eq([:cdr, nil])
110
+ expect(visitor.resume).to eq([:cdr, nil])
111
+ expect(visitor.resume).to eq([:cdr, pod])
112
+ end
113
+ end # context
114
+
115
+ context 'Skip visit of children of a ConsCell:' do
116
+ it 'can skip the visit of null list children' do
117
+ null_list = ConsCell.new(nil)
118
+ visitor = subject.df_visitor(null_list)
119
+
120
+ # Tell to skip children by passing a true value to resume
121
+ expect(visitor.resume(true)).to eq([:car, null_list])
122
+ expect(visitor.resume).to eq([:stop, nil])
123
+ end
124
+
125
+ it 'can skip the visit of some children' do
126
+ tree = ConsCell.new(pea, ConsCell.new(l_pea_pod, ConsCell.new(corn)))
127
+ expect(tree.to_s).to eq('(:pea (:pea :pod) :corn)')
128
+ visitor = subject.df_visitor(tree)
129
+ expect(visitor.resume).to eq([:car, tree])
130
+ expect(visitor.resume).to eq([:car, pea])
131
+ expect(visitor.resume).to eq([:cdr, tree.cdr])
132
+ expect(visitor.resume).to eq([:car, l_pea_pod])
133
+
134
+ # Tell to skip children by passing a true value to resume
135
+ expect(visitor.resume(true)).to eq([:cdr, tree.cdr.cdr])
136
+ expect(visitor.resume).to eq([:car, corn])
137
+ expect(visitor.resume).to eq([:cdr, nil])
138
+ expect(visitor.resume).to eq([:stop, nil])
139
+ end
140
+ end # context
141
+
142
+ context 'Circular structures visiting:' do
143
+ it 'should cope with a circular graph' do
144
+ second_cell = ConsCell.new(pea)
145
+ first_cell = ConsCell.new(pea, second_cell)
146
+ second_cell.set_car!(first_cell)
147
+
148
+ visitor = subject.df_visitor(first_cell)
149
+ expect(visitor.resume).to eq([:car, first_cell])
150
+ expect(visitor.resume).to eq([:car, pea])
151
+ expect(visitor.resume).to eq([:cdr, second_cell])
152
+ expect(visitor.resume).to eq([:cdr, nil]) # Skip car (was already visited)
153
+ expect(visitor.resume).to eq([:stop, nil])
154
+ end
155
+ end # context
156
+ end # describe
157
+ end # module
158
+ end # module