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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -0
- data/Gemfile +3 -1
- data/README.md +6 -6
- data/Rakefile +5 -3
- data/lib/mini_kraken/core/any_value.rb +9 -7
- data/lib/mini_kraken/core/association.rb +20 -7
- data/lib/mini_kraken/core/association_walker.rb +5 -1
- data/lib/mini_kraken/core/atomic_term.rb +5 -3
- data/lib/mini_kraken/core/binary_relation.rb +8 -6
- data/lib/mini_kraken/core/composite_term.rb +5 -20
- data/lib/mini_kraken/core/cons_cell.rb +7 -3
- data/lib/mini_kraken/core/duck_fiber.rb +3 -1
- data/lib/mini_kraken/core/environment.rb +24 -10
- data/lib/mini_kraken/core/equals.rb +106 -165
- data/lib/mini_kraken/core/fail.rb +3 -1
- data/lib/mini_kraken/core/freshness.rb +11 -8
- data/lib/mini_kraken/core/goal.rb +4 -2
- data/lib/mini_kraken/core/k_integer.rb +4 -3
- data/lib/mini_kraken/core/k_symbol.rb +4 -3
- data/lib/mini_kraken/core/nullary_relation.rb +3 -1
- data/lib/mini_kraken/core/outcome.rb +5 -3
- data/lib/mini_kraken/core/relation.rb +4 -18
- data/lib/mini_kraken/core/succeed.rb +4 -2
- data/lib/mini_kraken/core/term.rb +2 -0
- data/lib/mini_kraken/core/variable.rb +13 -3
- data/lib/mini_kraken/core/variable_ref.rb +10 -32
- data/lib/mini_kraken/core/vocabulary.rb +250 -35
- data/lib/mini_kraken/glue/fresh_env.rb +5 -3
- data/lib/mini_kraken/glue/run_star_expression.rb +10 -9
- data/lib/mini_kraken/version.rb +3 -1
- data/lib/mini_kraken.rb +3 -1
- data/mini_kraken.gemspec +15 -13
- data/spec/core/association_spec.rb +4 -4
- data/spec/core/association_walker_spec.rb +25 -24
- data/spec/core/cons_cell_spec.rb +4 -3
- data/spec/core/duck_fiber_spec.rb +10 -11
- data/spec/core/environment_spec.rb +16 -28
- data/spec/core/equals_spec.rb +7 -7
- data/spec/core/fail_spec.rb +7 -7
- data/spec/core/goal_spec.rb +10 -10
- data/spec/core/k_symbol_spec.rb +5 -6
- data/spec/core/succeed_spec.rb +4 -4
- data/spec/core/variable_ref_spec.rb +0 -4
- data/spec/core/vocabulary_spec.rb +28 -22
- data/spec/glue/fresh_env_spec.rb +1 -1
- data/spec/glue/run_star_expression_spec.rb +13 -8
- data/spec/mini_kraken_spec.rb +2 -0
- data/spec/spec_helper.rb +3 -1
- data/spec/support/factory_methods.rb +4 -2
- 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
|
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
|
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
|
79
|
-
#
|
80
|
-
|
81
|
-
|
82
|
-
|
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
|
-
|
87
|
-
if
|
88
|
-
|
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
|
-
|
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.
|
110
|
-
|
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,
|
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
|
-
|
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
|
-
#
|
165
|
-
# @
|
166
|
-
def
|
167
|
-
|
168
|
-
|
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
|
-
|
171
|
-
|
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
|
-
#
|
175
|
-
#
|
176
|
-
# @return [
|
177
|
-
def
|
178
|
-
|
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?(:
|
184
|
-
|
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
|
-
|
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
|
-
|
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
|
data/lib/mini_kraken/version.rb
CHANGED
data/lib/mini_kraken.rb
CHANGED
data/mini_kraken.gemspec
CHANGED
@@ -1,6 +1,8 @@
|
|
1
|
-
|
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
|
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 =
|
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 =
|
42
|
-
spec.license =
|
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 =
|
47
|
+
spec.bindir = 'exe'
|
46
48
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
47
|
-
spec.require_paths = [
|
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
|
53
|
-
spec.add_development_dependency
|
54
|
-
spec.add_development_dependency
|
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.
|
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(
|
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(
|
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(
|
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(
|
86
|
-
env.add_assoc(
|
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(
|
97
|
-
env.add_assoc(
|
98
|
-
env.add_assoc(
|
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(
|
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(
|
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(
|
131
|
-
env.add_assoc(
|
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
|
-
|
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(
|
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
|
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(
|
159
|
-
env.add_assoc(
|
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
|
163
|
-
expect(
|
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
|