porolog 0.0.8 → 1.0.0
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/README.md +11 -1
- data/Rakefile +2 -2
- data/bin/porolog +34 -13
- data/coverage/badge.svg +1 -1
- data/coverage/index.html +43822 -25140
- data/doc/Array.html +158 -51
- data/doc/Object.html +2 -2
- data/doc/Porolog.html +1584 -1113
- data/doc/Symbol.html +2 -2
- data/doc/_index.html +40 -56
- data/doc/class_list.html +1 -1
- data/doc/file.README.html +8 -4
- data/doc/index.html +8 -4
- data/doc/method_list.html +424 -248
- data/doc/top-level-namespace.html +1 -1
- data/lib/porolog.rb +184 -61
- data/lib/porolog/arguments.rb +12 -11
- data/lib/porolog/core_ext.rb +27 -9
- data/lib/porolog/error.rb +3 -0
- data/lib/porolog/goal.rb +57 -15
- data/lib/porolog/instantiation.rb +55 -9
- data/lib/porolog/predicate.rb +52 -26
- data/lib/porolog/predicate/builtin.rb +825 -0
- data/lib/porolog/rule.rb +8 -24
- data/lib/porolog/scope.rb +1 -1
- data/lib/porolog/tail.rb +5 -0
- data/lib/porolog/value.rb +3 -3
- data/lib/porolog/variable.rb +29 -11
- data/test/porolog/arguments_test.rb +45 -66
- data/test/porolog/core_ext_test.rb +25 -0
- data/test/porolog/goal_test.rb +86 -9
- data/test/porolog/instantiation_test.rb +36 -0
- data/test/porolog/porolog_test.rb +285 -30
- data/test/porolog/predicate/builtin_test.rb +1340 -0
- data/test/porolog/predicate_test.rb +78 -16
- data/test/porolog/rule_test.rb +19 -0
- data/test/porolog/variable_test.rb +44 -55
- data/test/samples_test.rb +277 -0
- data/test/test_helper.rb +9 -0
- metadata +9 -5
@@ -102,7 +102,7 @@
|
|
102
102
|
</div>
|
103
103
|
|
104
104
|
<div id="footer">
|
105
|
-
Generated on
|
105
|
+
Generated on Sun Aug 2 19:24:16 2020 by
|
106
106
|
<a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
|
107
107
|
0.9.19 (ruby-2.6.5).
|
108
108
|
</div>
|
data/lib/porolog.rb
CHANGED
@@ -11,9 +11,9 @@
|
|
11
11
|
module Porolog
|
12
12
|
|
13
13
|
# The most recent version of the Porolog gem.
|
14
|
-
VERSION = '0.0
|
14
|
+
VERSION = '1.0.0'.freeze
|
15
15
|
# The most recent date of when the VERSION changed.
|
16
|
-
VERSION_DATE = '2020-
|
16
|
+
VERSION_DATE = '2020-08-02'.freeze
|
17
17
|
|
18
18
|
# Represents an unknown tail of a list.
|
19
19
|
UNKNOWN_TAIL = Object.new
|
@@ -23,9 +23,15 @@ module Porolog
|
|
23
23
|
'...'
|
24
24
|
end
|
25
25
|
|
26
|
-
# Specifies that the unknown tail is a
|
26
|
+
# Specifies that the unknown tail is a tail.
|
27
27
|
def UNKNOWN_TAIL.type
|
28
|
-
:
|
28
|
+
:tail
|
29
|
+
end
|
30
|
+
|
31
|
+
# @param _head_size [Integer] specifies the size of the head
|
32
|
+
# @return [Object] the tail of the Array
|
33
|
+
def UNKNOWN_TAIL.tail(_head_size = 1)
|
34
|
+
self
|
29
35
|
end
|
30
36
|
|
31
37
|
UNKNOWN_TAIL.freeze
|
@@ -33,37 +39,106 @@ module Porolog
|
|
33
39
|
# Represents a list where all elements are unknown.
|
34
40
|
UNKNOWN_ARRAY = [UNKNOWN_TAIL].freeze
|
35
41
|
|
42
|
+
# Stores the next unique anonymous variable name.
|
43
|
+
ANONYMOUS = ['_a']
|
44
|
+
|
36
45
|
# A convenience method to create a Predicate, along with a method
|
37
46
|
# that returns an Arguments based on the arguments provided to
|
38
47
|
# the method.
|
39
48
|
# @param names [Array<#to_sym>] names of the Predicates to create.
|
49
|
+
# @param class_base [Class] class to define the method in.
|
40
50
|
# @return [Porolog::Predicate] Predicate created if only one name is provided
|
41
51
|
# @return [Array<Porolog::Predicate>] Predicates created if multiple names are provided
|
42
52
|
# @example
|
43
53
|
# predicate :combobulator
|
44
54
|
# combobulator(:x,:y) # --> Porolog::Arguments
|
45
|
-
def predicate(*names)
|
55
|
+
def predicate(*names, class_base: Object)
|
46
56
|
names = [names].flatten
|
47
57
|
|
48
58
|
predicates = names.map{|name|
|
49
59
|
method = name.to_sym
|
50
60
|
predicate = Predicate.new(name)
|
51
|
-
|
61
|
+
class_base.class_eval{
|
52
62
|
remove_method(method) if public_method_defined?(method)
|
53
63
|
define_method(method){|*args|
|
54
64
|
predicate.(*args)
|
55
65
|
}
|
56
66
|
}
|
67
|
+
(class << class_base; self; end).instance_eval {
|
68
|
+
remove_method(method) if public_method_defined?(method)
|
69
|
+
define_method(method){|*args|
|
70
|
+
predicate.(*args)
|
71
|
+
}
|
72
|
+
} unless class_base == Object
|
73
|
+
predicate
|
74
|
+
}
|
75
|
+
|
76
|
+
predicates.size > 1 && predicates || predicates.first
|
77
|
+
end
|
78
|
+
|
79
|
+
# A method to declare use of a standard / builtin Predicate, along with a method
|
80
|
+
# that returns an Arguments based on the arguments provided to
|
81
|
+
# the method.
|
82
|
+
# @param names [Array<#to_sym>] names of the Predicates to declare.
|
83
|
+
# @param class_base [Class] class to define the method in.
|
84
|
+
# @return [Porolog::Predicate] Predicate created if only one name is provided
|
85
|
+
# @return [Array<Porolog::Predicate>] Predicates created if multiple names are provided
|
86
|
+
# @example
|
87
|
+
# builtin :is, :member, :append
|
88
|
+
# member(:e,:l) # --> Porolog::Arguments
|
89
|
+
def builtin(*names, class_base: Object)
|
90
|
+
names = [names].flatten
|
91
|
+
|
92
|
+
predicates = names.map{|name|
|
93
|
+
method = name.to_sym
|
94
|
+
raise NameError, "Undefined builtin predicate #{name.inspect}" unless Predicate::Builtin.instance_methods.include?(method)
|
95
|
+
predicate = Predicate.new(name, builtin: true)
|
96
|
+
if class_base == Object
|
97
|
+
# -- Add Global Method --
|
98
|
+
class_base.class_eval{
|
99
|
+
remove_method(method) if method_defined?(method)
|
100
|
+
define_method(method){|*args, &block|
|
101
|
+
predicate.(*args, &block)
|
102
|
+
}
|
103
|
+
}
|
104
|
+
else
|
105
|
+
# -- Add Instance Method --
|
106
|
+
class_base.class_eval{
|
107
|
+
remove_method(method) if methods(false).include?(method)
|
108
|
+
define_method(method){|*args, &block|
|
109
|
+
predicate.(*args, &block)
|
110
|
+
}
|
111
|
+
}
|
112
|
+
|
113
|
+
# -- Add Class Method --
|
114
|
+
(class << class_base; self; end).instance_eval {
|
115
|
+
remove_method(method) if methods(false).include?(method)
|
116
|
+
define_method(method){|*args, &block|
|
117
|
+
predicate.(*args, &block)
|
118
|
+
}
|
119
|
+
}
|
120
|
+
end
|
57
121
|
predicate
|
58
122
|
}
|
59
123
|
|
60
124
|
predicates.size > 1 && predicates || predicates.first
|
61
125
|
end
|
62
126
|
|
127
|
+
# @return [Symbol] a unique variable name.
|
128
|
+
def anonymous
|
129
|
+
anonymous = ANONYMOUS[0].to_sym
|
130
|
+
ANONYMOUS[0].succ!
|
131
|
+
anonymous
|
132
|
+
end
|
133
|
+
|
134
|
+
alias _ anonymous
|
135
|
+
|
63
136
|
# Unify the Arguments of a Goal and a sub-Goal.
|
64
137
|
# @param goal [Porolog::Goal] a Goal to solve a Predicate for specific Arguments.
|
65
138
|
# @param subgoal [Porolog::Goal] a sub-Goal to solve the Goal following the Rules of the Predicate.
|
66
|
-
# @return [
|
139
|
+
# @return [Array<Porolog::Instantiation>] the instantiations if the goals can be unified and instantiated.
|
140
|
+
# @return [false] if they cannot be unified.
|
141
|
+
# @return [nil] if they can be unified but the instantiations are inconsistent.
|
67
142
|
def unify_goals(goal, subgoal)
|
68
143
|
if goal.arguments.predicate == subgoal.arguments.predicate
|
69
144
|
unifications = unify(goal.arguments.arguments, subgoal.arguments.arguments, goal, subgoal)
|
@@ -98,9 +173,30 @@ module Porolog
|
|
98
173
|
goals_variables[left_goal][left] ||= []
|
99
174
|
goals_variables[left_goal][left] << [right_goal,right]
|
100
175
|
|
101
|
-
|
102
|
-
|
103
|
-
|
176
|
+
# -- Check Consistency --
|
177
|
+
goals_variables[left_goal][left].map(&:last).map(&:value)
|
178
|
+
values = goals_variables[left_goal][left].map{|value|
|
179
|
+
value.last.value.value
|
180
|
+
}
|
181
|
+
next if values.size < 2
|
182
|
+
|
183
|
+
arrays = values.any?{|value| value.is_a?(Array) }
|
184
|
+
if arrays && !values.variables.empty?
|
185
|
+
zipped_values = values[0].zip(*values[1..-1])
|
186
|
+
zipped_values.each do |zipped_value|
|
187
|
+
vars, atomics = zipped_value.partition{|v| v.type == :variable }
|
188
|
+
return_false = true if atomics.uniq.size > 1
|
189
|
+
vars.each do |var|
|
190
|
+
goals_variables[var.goal] ||= {}
|
191
|
+
goals_variables[var.goal][var] ||= []
|
192
|
+
goals_variables[var.goal][var] << [([left_goal,right_goal]-[var.goal]).first,atomics.first]
|
193
|
+
end
|
194
|
+
end
|
195
|
+
else
|
196
|
+
return_false = values.reject{|value|
|
197
|
+
value.is_a?(Variable) || value.is_a?(Symbol)
|
198
|
+
}.uniq.size > 1
|
199
|
+
end
|
104
200
|
|
105
201
|
return false if return_false
|
106
202
|
end
|
@@ -109,9 +205,9 @@ module Porolog
|
|
109
205
|
instantiations = []
|
110
206
|
consistent = true
|
111
207
|
|
112
|
-
goals_variables.each do |goal,variables|
|
113
|
-
variables.each do |name,others|
|
114
|
-
others.each do |other_goal,other|
|
208
|
+
goals_variables.each do |goal, variables|
|
209
|
+
variables.each do |name, others|
|
210
|
+
others.each do |other_goal, other|
|
115
211
|
instantiation = goal.instantiate(name, other, other_goal)
|
116
212
|
if instantiation
|
117
213
|
instantiations << instantiation
|
@@ -123,9 +219,12 @@ module Porolog
|
|
123
219
|
end
|
124
220
|
|
125
221
|
# -- Revert if inconsistent --
|
126
|
-
|
127
|
-
|
128
|
-
|
222
|
+
if consistent
|
223
|
+
instantiations
|
224
|
+
else
|
225
|
+
instantiations.each(&:remove)
|
226
|
+
nil
|
227
|
+
end
|
129
228
|
end
|
130
229
|
|
131
230
|
# Attempt to unify two entities of two goals.
|
@@ -155,7 +254,7 @@ module Porolog
|
|
155
254
|
nil
|
156
255
|
end
|
157
256
|
|
158
|
-
when [:array, :array]
|
257
|
+
when [:array, :array], [:tail, :tail]
|
159
258
|
_merged, unifications = unify_arrays(left, right, left_goal, right_goal, visited)
|
160
259
|
if unifications
|
161
260
|
unifications
|
@@ -165,6 +264,26 @@ module Porolog
|
|
165
264
|
nil
|
166
265
|
end
|
167
266
|
|
267
|
+
when [:array, :tail]
|
268
|
+
_merged, unifications = unify_arrays([left], right.value, left_goal, right_goal, visited)
|
269
|
+
if unifications
|
270
|
+
unifications
|
271
|
+
else
|
272
|
+
msg = "Cannot unify because #{left.inspect} != #{right.inspect} (array != tail)"
|
273
|
+
goals.each{|goal| goal.log << msg }
|
274
|
+
nil
|
275
|
+
end
|
276
|
+
|
277
|
+
when [:tail, :array]
|
278
|
+
_merged, unifications = unify_arrays(left.value, [right], left_goal, right_goal, visited)
|
279
|
+
if unifications
|
280
|
+
unifications
|
281
|
+
else
|
282
|
+
msg = "Cannot unify because #{left.inspect} != #{right.inspect} (tail != array)"
|
283
|
+
goals.each{|goal| goal.log << msg }
|
284
|
+
nil
|
285
|
+
end
|
286
|
+
|
168
287
|
when [:variable, :atomic]
|
169
288
|
left_value = left_goal.value_of(left, nil, visited)
|
170
289
|
right_value = right
|
@@ -188,17 +307,21 @@ module Porolog
|
|
188
307
|
end
|
189
308
|
|
190
309
|
when [:variable, :variable]
|
191
|
-
left_value = left_goal.value_of(left, nil, visited)
|
192
|
-
right_value = right_goal.value_of(right, nil, visited)
|
310
|
+
left_value = left_goal.value_of(left, nil, visited).value
|
311
|
+
right_value = right_goal.value_of(right, nil, visited).value
|
193
312
|
if left_value == right_value || left_value.is_a?(Variable) || right_value.is_a?(Variable) || left_value.nil? || right_value.nil?
|
194
313
|
[[left, right, left_goal, right_goal]]
|
314
|
+
elsif left_value == UNKNOWN_ARRAY && (right_value.is_a?(Variable) || right_value.nil? || right_value.is_a?(Array))
|
315
|
+
[[left, right, left_goal, right_goal]]
|
316
|
+
elsif right_value == UNKNOWN_ARRAY && (left_value.is_a?(Variable) || left_value.nil? || left_value.is_a?(Array))
|
317
|
+
[[left, right, left_goal, right_goal]]
|
195
318
|
else
|
196
319
|
msg = "Cannot unify because #{left_value.inspect} != #{right_value.inspect} (variable != variable)"
|
197
320
|
goals.each{|goal| goal.log << msg }
|
198
321
|
nil
|
199
322
|
end
|
200
323
|
|
201
|
-
when [:variable, :array]
|
324
|
+
when [:variable, :array], [:variable, :tail]
|
202
325
|
left_value = left_goal.value_of(left, nil, visited)
|
203
326
|
right_value = right
|
204
327
|
if left_value == right_value || left_value.is_a?(Variable) || left_value == UNKNOWN_ARRAY || left_value.nil?
|
@@ -218,7 +341,7 @@ module Porolog
|
|
218
341
|
nil
|
219
342
|
end
|
220
343
|
|
221
|
-
when [:array, :variable]
|
344
|
+
when [:array, :variable], [:tail, :variable]
|
222
345
|
left_value = left
|
223
346
|
right_value = right_goal.value_of(right, nil, visited)
|
224
347
|
if left_value == right_value || right_value.is_a?(Variable) || right_value == UNKNOWN_ARRAY || right_value.nil?
|
@@ -238,8 +361,8 @@ module Porolog
|
|
238
361
|
nil
|
239
362
|
end
|
240
363
|
|
241
|
-
when [:array
|
242
|
-
msg = "Cannot unify #{left.inspect} with #{right.inspect}"
|
364
|
+
when [:array, :atomic], [:atomic, :array], [:tail, :atomic], [:atomic, :tail]
|
365
|
+
msg = "Cannot unify #{left.inspect} with #{right.inspect} (#{signature.join(' != ')})"
|
243
366
|
goals.each{|goal| goal.log << msg }
|
244
367
|
nil
|
245
368
|
|
@@ -261,11 +384,11 @@ module Porolog
|
|
261
384
|
def unify_arrays(left, right, left_goal, right_goal = left_goal, visited = [])
|
262
385
|
arrays = [left, right]
|
263
386
|
arrays_goals = [left_goal, right_goal]
|
264
|
-
arrays_values = arrays.map(
|
387
|
+
arrays_values = arrays.map{|array| array.value(visited) }
|
265
388
|
|
266
389
|
# -- Trivial Unifications --
|
267
|
-
return [left_goal. variablise(left),
|
268
|
-
return [right_goal.variablise(right),[]] if left == UNKNOWN_ARRAY
|
390
|
+
return [left_goal. variablise(left), []] if right == UNKNOWN_ARRAY
|
391
|
+
return [right_goal.variablise(right), []] if left == UNKNOWN_ARRAY
|
269
392
|
|
270
393
|
# -- Validate Arrays --
|
271
394
|
unless arrays_values.all?{|array| array.is_a?(Array) || array == UNKNOWN_TAIL }
|
@@ -279,7 +402,7 @@ module Porolog
|
|
279
402
|
number_of_tails = arrays.count{|array| has_tail?(array) }
|
280
403
|
|
281
404
|
# -- Handle Tails --
|
282
|
-
if number_of_tails
|
405
|
+
if number_of_tails.zero?
|
283
406
|
unify_arrays_with_no_tails(arrays, arrays_goals, visited)
|
284
407
|
elsif number_of_tails == number_of_arrays
|
285
408
|
unify_arrays_with_all_tails(arrays, arrays_goals, visited)
|
@@ -295,7 +418,9 @@ module Porolog
|
|
295
418
|
# @return [Array<Array, Array>] the merged Array and the unifications to be instantiated.
|
296
419
|
# @return [nil] if the Arrays cannot be unified.
|
297
420
|
def unify_many_arrays(arrays, arrays_goals, visited = [])
|
298
|
-
arrays_values = arrays.
|
421
|
+
arrays_values = arrays.
|
422
|
+
map{|array| expand_splat(array) }.
|
423
|
+
map{|array| array.is_a?(Array) ? array.map(&:value) : array.value }
|
299
424
|
|
300
425
|
unless arrays_values.all?{|array_values| [:array,:variable].include?(array_values.type) }
|
301
426
|
msg = "Cannot unify: #{arrays.map(&:inspect).join(' with ')}"
|
@@ -303,7 +428,7 @@ module Porolog
|
|
303
428
|
return nil
|
304
429
|
end
|
305
430
|
|
306
|
-
# TODO: Fix
|
431
|
+
# TODO: Fix / improve
|
307
432
|
if arrays_values.size == 2 && arrays_values.any?{|array| array == UNKNOWN_ARRAY }
|
308
433
|
merged = arrays_values.reject{|array| array == UNKNOWN_ARRAY }.first
|
309
434
|
return [merged,[]]
|
@@ -312,7 +437,7 @@ module Porolog
|
|
312
437
|
number_of_arrays = arrays.size
|
313
438
|
number_of_tails = arrays.count{|array| has_tail?(array) }
|
314
439
|
|
315
|
-
if number_of_tails
|
440
|
+
if number_of_tails.zero?
|
316
441
|
unify_arrays_with_no_tails(arrays, arrays_goals, visited)
|
317
442
|
elsif number_of_tails == number_of_arrays
|
318
443
|
unify_arrays_with_all_tails(arrays, arrays_goals, visited)
|
@@ -383,11 +508,13 @@ module Porolog
|
|
383
508
|
|
384
509
|
if arrays_goals[index].nil?
|
385
510
|
value_with_goal = array.find{|element| element.respond_to?(:goal) }
|
386
|
-
arrays_goals[index] = value_with_goal.goal if value_with_goal
|
511
|
+
arrays_goals[index] = value_with_goal.goal if value_with_goal&.goal
|
387
512
|
end
|
388
513
|
|
389
514
|
if arrays_goals[index].nil?
|
515
|
+
# :nocov:
|
390
516
|
raise NoGoalError, "Array #{array.inspect} has no goal for unification"
|
517
|
+
# :nocov:
|
391
518
|
end
|
392
519
|
|
393
520
|
arrays_values[index] = expand_splat(arrays_values[index])
|
@@ -403,10 +530,10 @@ module Porolog
|
|
403
530
|
|
404
531
|
# -- Remap Arrays so that they are variablised and valuised with their Goals --
|
405
532
|
new_arrays = []
|
406
|
-
arrays_variables.each do |goal,variables|
|
533
|
+
arrays_variables.each do |goal, variables|
|
407
534
|
new_array = variables.map{|variable|
|
408
535
|
value = goal.value_of(variable, nil, visited).value
|
409
|
-
value = variable if
|
536
|
+
value = variable if [UNKNOWN_TAIL, UNKNOWN_ARRAY].include?(value)
|
410
537
|
|
411
538
|
if value.type == :variable
|
412
539
|
value = goal.variable(value)
|
@@ -440,10 +567,10 @@ module Porolog
|
|
440
567
|
merged = []
|
441
568
|
|
442
569
|
# TODO: Change these names
|
443
|
-
zipped.
|
570
|
+
zipped.each{|values_to_unify|
|
444
571
|
values_to_unify_values = values_to_unify.map{|value|
|
445
572
|
value_value = value.value
|
446
|
-
if
|
573
|
+
if [UNKNOWN_TAIL, UNKNOWN_ARRAY].include?(value_value)
|
447
574
|
value
|
448
575
|
else
|
449
576
|
value_value
|
@@ -527,15 +654,10 @@ module Porolog
|
|
527
654
|
# @return [Array<Array, Array>] the merged Array and the unifications to be instantiated.
|
528
655
|
# @return [nil] if the Arrays cannot be unified.
|
529
656
|
def unify_arrays_with_all_tails(arrays, arrays_goals, visited)
|
530
|
-
unifications = []
|
531
|
-
merged = []
|
532
|
-
|
533
657
|
# -- All tails --
|
534
|
-
signature = arrays.map(&:headtail?)
|
535
658
|
arrays = arrays.map{|array|
|
536
659
|
expand_splat(array.headtail? ? array : array.value)
|
537
660
|
}
|
538
|
-
signature = arrays.map(&:headtail?)
|
539
661
|
|
540
662
|
# -- Unify Embedded Arrays --
|
541
663
|
if arrays.any?{|array| array.type == :variable }
|
@@ -565,9 +687,9 @@ module Porolog
|
|
565
687
|
end
|
566
688
|
|
567
689
|
if unifications
|
568
|
-
|
690
|
+
[merged, unifications]
|
569
691
|
else
|
570
|
-
|
692
|
+
nil
|
571
693
|
end
|
572
694
|
end
|
573
695
|
|
@@ -615,10 +737,8 @@ module Porolog
|
|
615
737
|
first_goal = pair.first.goal if pair.first.respond_to?(:goal)
|
616
738
|
last_goal = pair.last.goal if pair.last.respond_to?(:goal)
|
617
739
|
m,u = unify_arrays(first_goal.variablise([pair.first]), last_goal.variablise([pair.last]), first_goal, last_goal, visited)
|
618
|
-
unless m
|
619
|
-
|
620
|
-
end
|
621
|
-
m[-1] = UNKNOWN_TAIL if m[-1] == nil
|
740
|
+
return nil unless m
|
741
|
+
m[-1] = UNKNOWN_TAIL if m[-1].nil?
|
622
742
|
merged_tails += m
|
623
743
|
unifications += u
|
624
744
|
end
|
@@ -682,19 +802,23 @@ module Porolog
|
|
682
802
|
|
683
803
|
# -- Variablise Arrays --
|
684
804
|
arrays = arrays_goals.zip(arrays).map do |goal, array|
|
685
|
-
|
805
|
+
if array == UNKNOWN_ARRAY
|
806
|
+
array
|
807
|
+
else
|
808
|
+
goal.variablise(array)
|
809
|
+
end
|
686
810
|
end
|
687
811
|
|
688
812
|
# -- Determine the fixed length (if any) --
|
689
813
|
fixed_length = nil
|
690
814
|
arrays.each do |array|
|
691
|
-
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
|
815
|
+
next if has_tail?(array)
|
816
|
+
|
817
|
+
array_length = array.value.size
|
818
|
+
fixed_length ||= array_length
|
819
|
+
unless fixed_length == array_length
|
820
|
+
array.goal.log << "Cannot unify #{array.value.inspect} because it has a different length from #{fixed_length}"
|
821
|
+
return nil
|
698
822
|
end
|
699
823
|
end
|
700
824
|
|
@@ -703,7 +827,7 @@ module Porolog
|
|
703
827
|
|
704
828
|
# -- Unify All HeadTail Arrays --
|
705
829
|
if headtail_arrays.size > 1
|
706
|
-
headtail_goals = headtail_arrays.map
|
830
|
+
headtail_goals = headtail_arrays.map(&:goal)
|
707
831
|
merged_headtails, headtail_unifications = unify_headtail_with_headtail(headtail_arrays, headtail_goals, visited)
|
708
832
|
unless merged_headtails
|
709
833
|
msg = "Could not unify headtail arrays: #{headtail_arrays.map(&:value).map(&:inspect).join(' with ')}"
|
@@ -724,7 +848,7 @@ module Porolog
|
|
724
848
|
|
725
849
|
# -- Unify All Tail Arrays --
|
726
850
|
if tail_arrays.size > 1
|
727
|
-
tail_goals = tail_arrays.map
|
851
|
+
tail_goals = tail_arrays.map(&:goal)
|
728
852
|
merged_tails, tail_unifications = unify_tail_with_tail(tail_arrays, tail_goals, visited)
|
729
853
|
return nil unless merged_tails
|
730
854
|
unifications += tail_unifications
|
@@ -812,7 +936,7 @@ module Porolog
|
|
812
936
|
case element.type
|
813
937
|
when :atomic
|
814
938
|
0
|
815
|
-
when :array
|
939
|
+
when :array, :tail
|
816
940
|
if [UNKNOWN_TAIL, UNKNOWN_ARRAY].include?(element)
|
817
941
|
3
|
818
942
|
else
|
@@ -931,7 +1055,7 @@ module Porolog
|
|
931
1055
|
goal = array.goal if array.is_a?(Value)
|
932
1056
|
if goal.nil? && array.is_a?(Array)
|
933
1057
|
v = array.find{|element| element.respond_to?(:goal) }
|
934
|
-
goal = v
|
1058
|
+
goal = v&.goal
|
935
1059
|
end
|
936
1060
|
end
|
937
1061
|
goal = arrays_goals.compact.last if goal.nil?
|
@@ -1001,7 +1125,6 @@ module Porolog
|
|
1001
1125
|
value_values = value.map(&:value).compact.uniq
|
1002
1126
|
if value_values.size <= 1
|
1003
1127
|
m = value.first.value
|
1004
|
-
m = nil if m.type == :variable
|
1005
1128
|
merged << m
|
1006
1129
|
else
|
1007
1130
|
if values.variables.empty?
|
@@ -1014,7 +1137,6 @@ module Porolog
|
|
1014
1137
|
_variables, nonvariables = values.reject{|v| v.value.nil? }.partition{|element| element.type == :variable }
|
1015
1138
|
if nonvariables.value.uniq.size <= 1
|
1016
1139
|
m = nonvariables.first.value
|
1017
|
-
m = nil if m.type == :variable
|
1018
1140
|
merged << m
|
1019
1141
|
|
1020
1142
|
value.combination(2).each do |vl, vr|
|
@@ -1035,6 +1157,7 @@ module Porolog
|
|
1035
1157
|
end
|
1036
1158
|
}
|
1037
1159
|
|
1160
|
+
# -- Unify Tails --
|
1038
1161
|
tails.each do |head_size,tail,goal|
|
1039
1162
|
next if tail == UNKNOWN_TAIL
|
1040
1163
|
merged_goals = arrays_goals - [goal] + [goal]
|
@@ -1048,9 +1171,9 @@ end
|
|
1048
1171
|
|
1049
1172
|
require_relative 'porolog/core_ext'
|
1050
1173
|
require_relative 'porolog/error'
|
1051
|
-
require_relative 'porolog/core_ext'
|
1052
1174
|
require_relative 'porolog/scope'
|
1053
1175
|
require_relative 'porolog/predicate'
|
1176
|
+
require_relative 'porolog/predicate/builtin'
|
1054
1177
|
require_relative 'porolog/arguments'
|
1055
1178
|
require_relative 'porolog/rule'
|
1056
1179
|
require_relative 'porolog/goal'
|