mini_kraken 0.1.04 → 0.1.05

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 (51) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +9 -0
  3. data/Gemfile +3 -1
  4. data/README.md +6 -6
  5. data/Rakefile +5 -3
  6. data/lib/mini_kraken/core/any_value.rb +9 -7
  7. data/lib/mini_kraken/core/association.rb +20 -7
  8. data/lib/mini_kraken/core/association_walker.rb +5 -1
  9. data/lib/mini_kraken/core/atomic_term.rb +5 -3
  10. data/lib/mini_kraken/core/binary_relation.rb +8 -6
  11. data/lib/mini_kraken/core/composite_term.rb +5 -20
  12. data/lib/mini_kraken/core/cons_cell.rb +7 -3
  13. data/lib/mini_kraken/core/duck_fiber.rb +3 -1
  14. data/lib/mini_kraken/core/environment.rb +24 -10
  15. data/lib/mini_kraken/core/equals.rb +106 -165
  16. data/lib/mini_kraken/core/fail.rb +3 -1
  17. data/lib/mini_kraken/core/freshness.rb +11 -8
  18. data/lib/mini_kraken/core/goal.rb +4 -2
  19. data/lib/mini_kraken/core/k_integer.rb +4 -3
  20. data/lib/mini_kraken/core/k_symbol.rb +4 -3
  21. data/lib/mini_kraken/core/nullary_relation.rb +3 -1
  22. data/lib/mini_kraken/core/outcome.rb +5 -3
  23. data/lib/mini_kraken/core/relation.rb +4 -18
  24. data/lib/mini_kraken/core/succeed.rb +4 -2
  25. data/lib/mini_kraken/core/term.rb +2 -0
  26. data/lib/mini_kraken/core/variable.rb +13 -3
  27. data/lib/mini_kraken/core/variable_ref.rb +10 -32
  28. data/lib/mini_kraken/core/vocabulary.rb +250 -35
  29. data/lib/mini_kraken/glue/fresh_env.rb +5 -3
  30. data/lib/mini_kraken/glue/run_star_expression.rb +10 -9
  31. data/lib/mini_kraken/version.rb +3 -1
  32. data/lib/mini_kraken.rb +3 -1
  33. data/mini_kraken.gemspec +15 -13
  34. data/spec/core/association_spec.rb +4 -4
  35. data/spec/core/association_walker_spec.rb +25 -24
  36. data/spec/core/cons_cell_spec.rb +4 -3
  37. data/spec/core/duck_fiber_spec.rb +10 -11
  38. data/spec/core/environment_spec.rb +16 -28
  39. data/spec/core/equals_spec.rb +7 -7
  40. data/spec/core/fail_spec.rb +7 -7
  41. data/spec/core/goal_spec.rb +10 -10
  42. data/spec/core/k_symbol_spec.rb +5 -6
  43. data/spec/core/succeed_spec.rb +4 -4
  44. data/spec/core/variable_ref_spec.rb +0 -4
  45. data/spec/core/vocabulary_spec.rb +28 -22
  46. data/spec/glue/fresh_env_spec.rb +1 -1
  47. data/spec/glue/run_star_expression_spec.rb +13 -8
  48. data/spec/mini_kraken_spec.rb +2 -0
  49. data/spec/spec_helper.rb +3 -1
  50. data/spec/support/factory_methods.rb +4 -2
  51. metadata +2 -2
@@ -1,3 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'set'
4
+ require_relative 'association'
1
5
  require_relative 'association_walker'
2
6
 
3
7
  module MiniKraken
@@ -25,7 +29,7 @@ module MiniKraken
25
29
  def ancestor_walker
26
30
  Fiber.new do
27
31
  relative = self
28
- while relative do
32
+ while relative
29
33
  Fiber.yield relative
30
34
  relative = relative.parent
31
35
  end
@@ -40,10 +44,11 @@ module MiniKraken
40
44
  loop do
41
45
  orphan_temp = walker.resume
42
46
  break unless orphan_temp
47
+
43
48
  orphan = orphan_temp
44
49
  end
45
50
 
46
- orphan.rankings.clear if orphan.rankings
51
+ orphan.rankings&.clear
47
52
  end
48
53
 
49
54
  # @param aName [String]
@@ -54,6 +59,7 @@ module MiniKraken
54
59
  loop do
55
60
  orphan_temp = walker.resume
56
61
  break unless orphan_temp
62
+
57
63
  orphan = orphan_temp
58
64
  end
59
65
 
@@ -62,8 +68,8 @@ module MiniKraken
62
68
  if orphan.rankings.include?(aName)
63
69
  orphan.rankings[aName]
64
70
  else
65
- other = alternate_names.find do |a_name|
66
- orphan.rankings.include?(a_name)
71
+ other = alternate_names.find do |a_name|
72
+ orphan.rankings.include?(a_name)
67
73
  end
68
74
  if other
69
75
  get_rank(other)
@@ -75,19 +81,24 @@ module MiniKraken
75
81
  end
76
82
  end
77
83
 
78
- # Record an association between a variable with given name and a term.
79
- # @param anAssociation [Association]
80
- def add_assoc(anAssociation)
81
- name = anAssociation.var_name
82
- unless include?(name)
84
+ # Record an association between a variable with given user-defined name
85
+ # and a term.
86
+ # @param aName [String, Variable] A user-defined variable name
87
+ # @param aTerm [Term] A term to associate with the variable
88
+ def add_assoc(aName, aTerm)
89
+ name = aName.respond_to?(:name) ? aName.name : aName
90
+
91
+ var = name2var(name)
92
+ unless var
83
93
  err_msg = "Unknown variable '#{name}'."
84
94
  raise StandardError, err_msg
85
95
  end
86
- found_assocs = associations[name]
87
- if found_assocs
88
- found_assocs << anAssociation
96
+ siblings = detect_fuse(var, aTerm)
97
+ if siblings.empty?
98
+ anAssociation = Association.new(var.i_name, aTerm)
99
+ do_add_assocs([anAssociation]).first
89
100
  else
90
- associations[name] = [anAssociation]
101
+ fuse_vars(siblings << var)
91
102
  end
92
103
  end
93
104
 
@@ -95,19 +106,117 @@ module MiniKraken
95
106
  # Can be overridden in other to propagate associations from child
96
107
  # @param _descendent [Outcome]
97
108
  def propagate(_descendent)
98
- #Do nothing...
109
+ # Do nothing...
99
110
  end
100
111
 
101
- # Remove all the associations.
112
+ # Remove all the associations of this vocabulary
102
113
  def clear
103
114
  associations.clear
104
115
  end
105
116
 
117
+ # @param aVarName [String] A user-defined variable name
118
+ # @param other [Vocabulary]
119
+ def move_assocs(aVarName, other)
120
+ i_name = to_internal(aVarName)
121
+ assocs = other.associations[i_name]
122
+ if assocs
123
+ do_add_assocs(assocs)
124
+ other.associations.delete(i_name)
125
+ end
126
+ end
127
+
106
128
  # Merge the associations from another vocabulary-like object.
107
129
  # @param another [Vocabulary]
108
130
  def merge(another)
109
- another.associations.each_pair do |_name, assocs|
110
- assocs.each { |a| add_assoc(a) }
131
+ another.associations.each_value { |assocs| do_add_assocs(assocs) }
132
+ end
133
+
134
+ # Check that the provided variable must be fused with the argument.
135
+ # @return [Array<Variable>]
136
+ def detect_fuse(aVariable, aTerm)
137
+ return [] unless aTerm.kind_of?(VariableRef)
138
+
139
+ assocs = self[aTerm.var_name]
140
+ # Simplified implementation: cope with binary cycles only...
141
+ # TODO: Extend to n-ary (n > 2) cycles
142
+ assoc_refs = assocs.select { |a| a.value.kind_of?(VariableRef) }
143
+ return [] if assoc_refs.empty? # No relevant association...
144
+
145
+ visitees = Set.new
146
+ to_fuse = []
147
+ to_visit = assoc_refs
148
+ loop do
149
+ assc = to_visit.shift
150
+ next if visitees.include?(assc)
151
+
152
+ visitees.add(assc)
153
+ ref = assc.value
154
+ if ref.var_name == aVariable.name
155
+ to_fuse << assc.i_name unless assc.i_name == aVariable.i_name
156
+ end
157
+ other_assocs = self[ref.var_name]
158
+ other_assoc_refs = other_assocs.select { |a| a.value.kind_of?(VariableRef) }
159
+ other_assoc_refs.each do |a|
160
+ to_visit << a unless visitess.include?(a)
161
+ end
162
+
163
+
164
+ break if to_visit.empty?
165
+ end
166
+
167
+ to_fuse.map { |i_name| i_name2var(i_name) }
168
+ end
169
+
170
+ # Fuse the given variables, that is:
171
+ # Collect all their associations
172
+ # Put them under a new internal name
173
+ # Remove all entries from old internal names
174
+ # For all fused variables, change internal names
175
+ # @param theVars [Array<Variable>]
176
+ def fuse_vars(theVars)
177
+ new_i_name = Object.new.object_id.to_s
178
+ fused_vars = theVars.dup
179
+ fused_vars.each do |a_var|
180
+ old_i_name = a_var.i_name
181
+ old_names = fused_vars.map(&:name)
182
+ walker = ancestor_walker
183
+
184
+ loop do
185
+ voc = walker.resume
186
+ break unless voc
187
+
188
+ if voc.associations.include?(old_i_name)
189
+ assocs = voc.associations[old_i_name]
190
+ keep_assocs = assocs.reject do |assc|
191
+ assc.value.kind_of?(VariableRef) && old_names.include?(assc.value.var_name)
192
+ end
193
+ unless keep_assocs.empty?
194
+ keep_assocs.each { |assc| assc.i_name = new_i_name }
195
+ if voc.associations.include?(new_i_name)
196
+ voc.associations[new_i_name].concat(keep_assocs)
197
+ else
198
+ voc.associations[new_i_name] = keep_assocs
199
+ end
200
+ end
201
+ voc.associations.delete(old_i_name)
202
+ end
203
+ next unless voc.respond_to?(:vars) && voc.vars.include?(a_var.name)
204
+
205
+ user_names = voc.ivars[old_i_name]
206
+ unseen = user_names.reject { |nm| old_names.include?(nm) }
207
+ unseen.each do |usr_name|
208
+ new_var = name2var(usr_name)
209
+ fused_vars << new_var
210
+ end
211
+ unless voc.ivars.include?(new_i_name)
212
+ voc.ivars[new_i_name] = user_names
213
+ else
214
+ voc.ivars[new_i_name].merge(user_names)
215
+ end
216
+ voc.ivars.delete(old_i_name)
217
+ break
218
+ end
219
+ a_var.i_name = new_i_name
111
220
  end
112
221
  end
113
222
 
@@ -119,7 +228,7 @@ module MiniKraken
119
228
  end
120
229
 
121
230
  # @param var [Variable, VariableRef] variable for which the value to retrieve
122
- # @return [Term, NilClase]
231
+ # @return [Term, NilClass]
123
232
  def ground_value(var)
124
233
  name = var.respond_to?(:var_name) ? var.var_name : var.name
125
234
 
@@ -149,7 +258,7 @@ module MiniKraken
149
258
  # Determine whether the reference points to a fresh, bound or ground term.
150
259
  # @param aVariableRef [VariableRef]
151
260
  # @return [Freshness]
152
- def freshness_ref(aVariableRef)
261
+ def freshness_ref(aVariableRef)
153
262
  walker = AssociationWalker.new
154
263
  walker.determine_freshness(aVariableRef, self)
155
264
  end
@@ -161,33 +270,124 @@ module MiniKraken
161
270
  walker.quote_term(aVariableRef, self)
162
271
  end
163
272
 
164
- # @param aName [String]
165
- # @return [Array<Association>]
166
- def [](aName)
167
- assoc_arr = associations[aName]
168
- assoc_arr = [] if assoc_arr.nil?
273
+ # Return the variable with given user-defined variable name.
274
+ # @param aName [String] User-defined variable name
275
+ def name2var(aName)
276
+ var = nil
277
+ walker = ancestor_walker
169
278
 
170
- assoc_arr.concat(parent[aName]) if parent
171
- assoc_arr
279
+ loop do
280
+ voc = walker.resume
281
+ if voc
282
+ next unless voc.respond_to?(:vars) && voc.vars.include?(aName)
283
+
284
+ var = voc.vars[aName]
285
+ end
286
+
287
+ break
288
+ end
289
+
290
+ var
172
291
  end
173
292
 
174
- # Check that a variable with given name is defined in this vocabulary
175
- # of one of its ancestor.
176
- # @return [Boolean]
177
- def include?(aVarName)
178
- var_found = false
293
+ # Return the variable with given internal variable name.
294
+ # @param aName [String] internal variable name
295
+ # @return [Variable]
296
+ def i_name2var(i_name)
297
+ var = nil
298
+ voc = nil
179
299
  walker = ancestor_walker
300
+
180
301
  loop do
181
302
  voc = walker.resume
182
303
  if voc
183
- next unless voc.respond_to?(:vars) && voc.vars.include?(aVarName)
184
- var_found = true
304
+ next unless voc.respond_to?(:ivars) && voc.ivars.include?(i_name)
305
+
306
+ var_name = voc.ivars[i_name].first # TODO: what if multiple vars?
307
+ var = voc.vars[var_name]
185
308
  end
186
309
 
187
310
  break
188
311
  end
189
312
 
190
- var_found
313
+ raise StandardError, 'Nil variable object' if var.nil?
314
+
315
+ var
316
+ end
317
+
318
+ # Return the internal name to corresponding to a given user-defined
319
+ # variable name.
320
+ # @param aName [String] User-defined variable name
321
+ def to_internal(aName)
322
+ var = name2var(aName)
323
+ var ? var.i_name : nil
324
+ end
325
+
326
+ # Return the internal names fused with given user-defined
327
+ # variable name.
328
+ # @param aName [String] User-defined variable name
329
+ def names_fused(aName)
330
+ var = name2var(aName)
331
+ return [] unless var.fused?
332
+
333
+ i_name = var.i_name
334
+ names = []
335
+ walker = ancestor_walker
336
+
337
+ loop do
338
+ voc = walker.resume
339
+ break unless voc
340
+ next unless voc.respond_to?(:ivars)
341
+
342
+ if voc.ivars.include?(i_name)
343
+ fused = voc.ivars[i_name]
344
+ names.concat(fused.to_a)
345
+ end
346
+ end
347
+
348
+ names.uniq!
349
+ names.reject { |nm| nm == aName }
350
+ end
351
+
352
+ # Retrieve all the associations for a given variable
353
+ # @param aVariable [Variable]
354
+ # @return [Array<Association>]
355
+ def assocs4var(aVariable)
356
+ i_name = aVariable.i_name
357
+ assocs = []
358
+ walker = ancestor_walker
359
+
360
+ loop do
361
+ voc = walker.resume
362
+ break unless voc
363
+ next unless voc.associations.include?(i_name)
364
+
365
+ assocs.concat(voc.associations[i_name])
366
+ end
367
+
368
+ assocs
369
+ end
370
+
371
+ # @param aName [String] User-defined variable name
372
+ # @return [Array<Association>]
373
+ def [](aName)
374
+ iname = to_internal(aName)
375
+ return [] unless iname
376
+
377
+ assoc_arr = associations[iname]
378
+ assoc_arr = [] if assoc_arr.nil?
379
+
380
+ # TODO: Optimize
381
+ assoc_arr.concat(parent[aName]) if parent
382
+ assoc_arr
383
+ end
384
+
385
+ # Check that a variable with given name is defined in this vocabulary
386
+ # or one of its ancestor.
387
+ # @param aVarName [String] A user-defined variable name.
388
+ # @return [Boolean]
389
+ def include?(aVarName)
390
+ name2var(aVarName) ? true : false
191
391
  end
192
392
 
193
393
  protected
@@ -201,6 +401,21 @@ module MiniKraken
201
401
 
202
402
  aParent
203
403
  end
404
+
405
+ # @param theAssociations [Array<Association>]
406
+ def do_add_assocs(theAssociations)
407
+ theAssociations.each do |assc|
408
+ i_name = assc.i_name
409
+ found_assocs = associations[i_name]
410
+ if found_assocs
411
+ found_assocs << assc
412
+ else
413
+ associations[i_name] = [assc]
414
+ end
415
+
416
+ assc
417
+ end
418
+ end
204
419
  end # class
205
420
  end # module
206
- end # module
421
+ end # module
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative '../core/environment'
2
4
  require_relative '../core/variable'
3
5
 
@@ -20,12 +22,12 @@ module MiniKraken
20
22
  end
21
23
 
22
24
  # Attempt to achieve the goal given this environment
23
- # @param aParent [Environment]
24
- # @return [Fiber<Outcome>] A Fiber object that will generate the results.
25
+ # @param aParent [Environment]
26
+ # @return [Fiber<Outcome>] A Fiber object that will generate the results.
25
27
  def attain(aParent)
26
28
  self.parent = aParent
27
29
  goal.attain(self)
28
30
  end
29
31
  end # class
30
32
  end # module
31
- end # module
33
+ end # module
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative '../core/any_value'
2
4
  require_relative '../core/cons_cell'
3
5
  require_relative 'fresh_env'
@@ -20,20 +22,19 @@ module MiniKraken
20
22
  def run
21
23
  result = nil
22
24
  solver = env.goal.attain(env)
23
- # require 'debug'
25
+ # require 'debug'
24
26
  loop do
25
27
  outcome = solver.resume
26
28
  break if outcome.nil?
29
+
27
30
  env.clear
28
31
  if result # ... more than one result...
32
+ elsif outcome.successful?
33
+ env.propagate(outcome)
34
+ # require 'debug'
35
+ result = Core::ConsCell.new(var.quote(outcome))
29
36
  else
30
- if outcome.successful?
31
- env.propagate(outcome)
32
- # require 'debug'
33
- result = Core::ConsCell.new(var.quote(outcome))
34
- else
35
- result = Core::NullList
36
- end
37
+ result = Core::NullList
37
38
  end
38
39
  end
39
40
 
@@ -41,4 +42,4 @@ module MiniKraken
41
42
  end
42
43
  end # class
43
44
  end # module
44
- end # module
45
+ end # module
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module MiniKraken
2
- VERSION = '0.1.04'.freeze
4
+ VERSION = '0.1.05'
3
5
  end
data/lib/mini_kraken.rb CHANGED
@@ -1,4 +1,6 @@
1
- require "mini_kraken/version"
1
+ # frozen_string_literal: true
2
+
3
+ require 'mini_kraken/version'
2
4
 
3
5
  module MiniKraken
4
6
  class Error < StandardError; end
data/mini_kraken.gemspec CHANGED
@@ -1,6 +1,8 @@
1
- lib = File.expand_path("lib", __dir__)
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
2
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
- require "mini_kraken/version"
5
+ require 'mini_kraken/version'
4
6
 
5
7
  # Implementation module
6
8
  module PkgExtending
@@ -10,7 +12,7 @@ module PkgExtending
10
12
  '.travis.yml',
11
13
  'Gemfile',
12
14
  'Rakefile',
13
- 'CHANGELOG.md',
15
+ 'CHANGELOG.md',
14
16
  'LICENSE.txt',
15
17
  'README.md',
16
18
  'mini_kraken.gemspec',
@@ -31,25 +33,25 @@ module PkgExtending
31
33
  end # module
32
34
 
33
35
  Gem::Specification.new do |spec|
34
- spec.name = "mini_kraken"
36
+ spec.name = 'mini_kraken'
35
37
  spec.version = MiniKraken::VERSION
36
38
  spec.authors = ['Dimitri Geshef']
37
39
  spec.email = ['famished.tiger@yahoo.com']
38
40
 
39
41
  spec.summary = %q{Implementation of Minikanren language in Ruby. WIP}
40
42
  spec.description = %q{Implementation of Minikanren language in Ruby. WIP}
41
- spec.homepage = "https://github.com/famished-tiger/mini_kraken"
42
- spec.license = "MIT"
43
+ spec.homepage = 'https://github.com/famished-tiger/mini_kraken'
44
+ spec.license = 'MIT'
43
45
 
44
46
  # Specify which files should be added to the gem when it is released.
45
- spec.bindir = "exe"
47
+ spec.bindir = 'exe'
46
48
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
47
- spec.require_paths = ["lib"]
48
-
49
+ spec.require_paths = ['lib']
50
+
49
51
  PkgExtending.pkg_files(spec)
50
- PkgExtending.pkg_documentation(spec)
52
+ PkgExtending.pkg_documentation(spec)
51
53
 
52
- spec.add_development_dependency "bundler", "~> 2.0"
53
- spec.add_development_dependency "rake", "~> 12.0"
54
- spec.add_development_dependency "rspec", "~> 3.0"
54
+ spec.add_development_dependency 'bundler', '~> 2.0'
55
+ spec.add_development_dependency 'rake', '~> 12.0'
56
+ spec.add_development_dependency 'rspec', '~> 3.0'
55
57
  end
@@ -10,20 +10,20 @@ require_relative '../../lib/mini_kraken/core/association'
10
10
  module MiniKraken
11
11
  module Core
12
12
  describe Association do
13
- let(:pea) {KSymbol.new(:pea) }
13
+ let(:pea) { KSymbol.new(:pea) }
14
14
  subject { Association.new('q', pea) }
15
15
 
16
16
  context 'Initialization:' do
17
17
  it 'should be initialized with a name and a value' do
18
18
  expect { Association.new('q', pea) }.not_to raise_error
19
19
  end
20
-
20
+
21
21
  it 'should be initialized with a variable and a value' do
22
22
  expect { Association.new(Variable.new('p'), pea) }.not_to raise_error
23
- end
23
+ end
24
24
 
25
25
  it 'should know the variable name' do
26
- expect(subject.var_name).to eq('q')
26
+ expect(subject.i_name).to eq('q')
27
27
  end
28
28
 
29
29
  it 'should know the associated value' do
@@ -58,14 +58,14 @@ module MiniKraken
58
58
 
59
59
  it 'should return composite when it has ground composite term(s)' do
60
60
  env.add_var(var_q)
61
- env.add_assoc(Association.new('q', pea))
61
+ env.add_assoc('q', pea)
62
62
  expr1 = cons(pea, cons(pod, cons(ref_q)))
63
63
  expect(subject.walk_value(expr1, env)).to eq(expr1)
64
64
  end
65
65
 
66
66
  it 'should return nil when no assocation exists' do
67
67
  env.add_var(var_q)
68
- env.add_assoc(Association.new('q', pea))
68
+ env.add_assoc('q', pea)
69
69
  env.add_var(var_x)
70
70
 
71
71
  expect(subject.find_ground(var_x.name, env)).to be_nil
@@ -73,7 +73,7 @@ module MiniKraken
73
73
 
74
74
  it 'should find an atomic term directly associated' do
75
75
  env.add_var(var_q)
76
- env.add_assoc(Association.new('q', pea))
76
+ env.add_assoc('q', pea)
77
77
 
78
78
  result = subject.find_ground(var_q.name, env)
79
79
  expect(result).to eq(pea)
@@ -82,8 +82,8 @@ module MiniKraken
82
82
  it 'should find an atomic term directly associated' do
83
83
  env.add_var(var_q)
84
84
  env.add_var(var_x)
85
- env.add_assoc(Association.new('q', ref_x))
86
- env.add_assoc(Association.new('x', pea))
85
+ env.add_assoc('q', ref_x)
86
+ env.add_assoc('x', pea)
87
87
  expect(env['x']).not_to be_nil
88
88
 
89
89
  result = subject.find_ground(var_q.name, env)
@@ -93,9 +93,9 @@ module MiniKraken
93
93
  it 'should cope with cyclic structures' do
94
94
  env.add_var(var_q)
95
95
  env.add_var(var_x)
96
- env.add_assoc(Association.new('q', ref_x))
97
- env.add_assoc(Association.new('x', pea))
98
- env.add_assoc(Association.new('x', ref_q))
96
+ env.add_assoc('q', ref_x)
97
+ env.add_assoc('x', pea)
98
+ env.add_assoc('x', ref_q)
99
99
 
100
100
  result = subject.find_ground(var_q.name, env)
101
101
  expect(result).to eq(pea)
@@ -107,7 +107,7 @@ module MiniKraken
107
107
  it 'should cope with a composite with atomic terms only' do
108
108
  env.add_var(var_q)
109
109
  expr = cons(pea, cons(pod, cons(pea)))
110
- env.add_assoc(Association.new('q', expr))
110
+ env.add_assoc('q', expr)
111
111
 
112
112
  result = subject.find_ground(var_q.name, env)
113
113
  expect(result).to eq(expr)
@@ -117,7 +117,7 @@ module MiniKraken
117
117
  env.add_var(var_q)
118
118
  env.add_var(var_x)
119
119
  expr = cons(pea, cons(pod, cons(ref_x)))
120
- env.add_assoc(Association.new('q', expr))
120
+ env.add_assoc('q', expr)
121
121
 
122
122
  result = subject.find_ground(var_q.name, env)
123
123
  expect(result).to be_nil
@@ -127,14 +127,13 @@ module MiniKraken
127
127
  env.add_var(var_q)
128
128
  env.add_var(var_x)
129
129
  expr = cons(pea, cons(pod, cons(ref_x)))
130
- env.add_assoc(Association.new('q', expr))
131
- env.add_assoc(Association.new('x', pod))
130
+ env.add_assoc('q', expr)
131
+ env.add_assoc('x', pod)
132
132
 
133
133
  result = subject.find_ground(var_q.name, env)
134
134
  expect(result).to eq(expr)
135
135
  end
136
- =begin
137
- =end
136
+
138
137
  it 'should categorize a variable without association as free' do
139
138
  env.add_var(var_q)
140
139
  result = subject.determine_freshness(ref_q, env)
@@ -145,22 +144,24 @@ module MiniKraken
145
144
  it 'should categorize a variable related to fresh variable as bound' do
146
145
  env.add_var(var_q)
147
146
  env.add_var(var_x)
148
- env.add_assoc(Association.new('q', ref_x))
147
+ env.add_assoc('q', ref_x)
149
148
 
150
149
  result = subject.determine_freshness(ref_q, env)
151
150
  expect(result).to be_bound
152
151
  expect(result.associated).to eq(ref_x)
153
152
  end
154
153
 
155
- it 'should categorize a variable even in presence of cycle(s)' do
154
+ it 'should categorize a variable even in presence of fused var(s)' do
156
155
  env.add_var(var_q)
157
156
  env.add_var(var_x)
158
- env.add_assoc(Association.new('q', ref_x))
159
- env.add_assoc(Association.new('x', ref_q))
157
+ env.add_assoc('q', ref_x)
158
+ env.add_assoc('x', ref_q)
159
+ # q and x are fused...
160
160
 
161
161
  result = subject.determine_freshness(ref_q, env)
162
- expect(result).to be_bound
163
- expect(result.associated).to eq(ref_x)
162
+ expect(result).to be_fresh
163
+ expect(var_q.i_name).to eq(var_x.i_name)
164
+ expect(env.associations).to be_empty
164
165
  end
165
166
 
166
167
  it 'should categorize an atomic term as ground term' do
@@ -176,15 +177,15 @@ module MiniKraken
176
177
  expect(result).to be_ground
177
178
  expect(result.associated).to eq(composite)
178
179
  end
179
-
180
+
180
181
  it 'should categorize a composite term as bound term' do
181
- # Bound composite: a composite where at least one member is fresh
182
- env.add_var(var_q)
182
+ # Bound composite: a composite where at least one member is fresh
183
+ env.add_var(var_q)
183
184
  composite = cons(pea, cons(ref_q))
184
185
  result = subject.determine_freshness(composite, env)
185
186
  expect(result).to be_bound
186
187
  expect(result.associated).to eq(composite)
187
- end
188
+ end
188
189
  end # context
189
190
  end # describe
190
191
  end # module