-
#
-
# lib/porolog.rb - Plain Old Ruby Objects Prolog Engine
-
#
-
# Luis Esteban 2 May 2018
-
# created
-
#
-
-
#
-
# @author Luis Esteban
-
#
-
1
module Porolog
-
-
1
extend self
-
-
# The most recent version of the Porolog gem.
-
1
VERSION = '1.0.3'.freeze
-
# The most recent date of when the VERSION changed.
-
1
VERSION_DATE = '2020-08-13'.freeze
-
-
# Represents an unknown tail of a list.
-
1
UNKNOWN_TAIL = Object.new
-
-
# Defines pretty version of an unknown tail.
-
1
def UNKNOWN_TAIL.inspect
-
120
'...'
-
end
-
-
# Specifies that the unknown tail is a tail.
-
1
def UNKNOWN_TAIL.type
-
49735
:tail
-
end
-
-
# @param _head_size [Integer] specifies the size of the head
-
# @return [Object] the tail of the Array
-
1
def UNKNOWN_TAIL.tail(_head_size = 1)
-
3
self
-
end
-
-
1
UNKNOWN_TAIL.freeze
-
-
# Represents a list where all elements are unknown.
-
1
UNKNOWN_ARRAY = [UNKNOWN_TAIL].freeze
-
-
# Stores the next unique anonymous variable name.
-
1
ANONYMOUS = ['_a']
-
-
# A convenience method to create a Predicate, along with a method
-
# that returns an Arguments based on the arguments provided to
-
# the method.
-
# @param names [Array<#to_sym>] names of the Predicates to create.
-
# @param class_base [Class] class to define the method in.
-
# @return [Porolog::Predicate] Predicate created if only one name is provided
-
# @return [Array<Porolog::Predicate>] Predicates created if multiple names are provided
-
# @example
-
# predicate :combobulator
-
# combobulator(:x,:y) # --> Porolog::Arguments
-
1
def predicate(*names, class_base: Object)
-
65
names = [names].flatten
-
-
65
predicates = names.map{|name|
-
87
method = name.to_sym
-
87
predicate = Predicate.new(name)
-
85
class_base.class_eval{
-
85
remove_method(method) if public_method_defined?(method)
-
85
define_method(method){|*args|
-
290
predicate.(*args)
-
}
-
}
-
2
(class << class_base; self; end).instance_eval {
-
1
remove_method(method) if public_method_defined?(method)
-
1
define_method(method){|*args|
-
2
predicate.(*args)
-
}
-
85
} unless class_base == Object
-
85
predicate
-
}
-
-
63
predicates.size > 1 && predicates || predicates.first
-
end
-
-
# A method to declare use of a standard / builtin Predicate, along with a method
-
# that returns an Arguments based on the arguments provided to
-
# the method.
-
# @param names [Array<#to_sym>] names of the Predicates to declare.
-
# @param class_base [Class] class to define the method in.
-
# @return [Porolog::Predicate] Predicate created if only one name is provided
-
# @return [Array<Porolog::Predicate>] Predicates created if multiple names are provided
-
# @example
-
# builtin :is, :member, :append
-
# member(:e,:l) # --> Porolog::Arguments
-
1
def builtin(*names, class_base: Object)
-
158
names = [names].flatten
-
-
158
predicates = names.map{|name|
-
191
method = name.to_sym
-
191
raise NameError, "Undefined builtin predicate #{name.inspect}" unless Predicate::Builtin.instance_methods.include?(method)
-
191
predicate = Predicate.new(name, builtin: true)
-
191
if class_base == Object
-
# -- Add Global Method --
-
189
class_base.class_eval{
-
189
remove_method(method) if method_defined?(method)
-
189
define_method(method){|*args, &block|
-
230
predicate.(*args, &block)
-
}
-
}
-
else
-
# -- Add Instance Method --
-
2
class_base.class_eval{
-
2
remove_method(method) if methods(false).include?(method)
-
2
define_method(method){|*args, &block|
-
1
predicate.(*args, &block)
-
}
-
}
-
-
# -- Add Class Method --
-
4
(class << class_base; self; end).instance_eval {
-
2
remove_method(method) if methods(false).include?(method)
-
2
define_method(method){|*args, &block|
-
2
predicate.(*args, &block)
-
}
-
}
-
end
-
191
predicate
-
}
-
-
158
predicates.size > 1 && predicates || predicates.first
-
end
-
-
# @return [Symbol] a unique variable name.
-
1
def anonymous
-
217
anonymous = ANONYMOUS[0].to_sym
-
217
ANONYMOUS[0].succ!
-
217
anonymous
-
end
-
-
1
alias _ anonymous
-
-
# Unify the Arguments of a Goal and a sub-Goal.
-
# @param goal [Porolog::Goal] a Goal to solve a Predicate for specific Arguments.
-
# @param subgoal [Porolog::Goal] a sub-Goal to solve the Goal following the Rules of the Predicate.
-
# @return [Array<Porolog::Instantiation>] the instantiations if the goals can be unified and instantiated.
-
# @return [false] if they cannot be unified.
-
# @return [nil] if they can be unified but the instantiations are inconsistent.
-
1
def unify_goals(goal, subgoal)
-
2592
if goal.arguments.predicate == subgoal.arguments.predicate
-
2590
unifications = unify(goal.arguments.arguments, subgoal.arguments.arguments, goal, subgoal)
-
2590
if unifications
-
2249
instantiate_unifications(unifications)
-
else
-
341
msg = "Could not unify goals: #{goal.arguments.arguments.inspect} !~ #{subgoal.arguments.arguments.inspect}"
-
341
goal.log << msg
-
341
subgoal.log << msg
-
341
false
-
end
-
else
-
2
msg = "Cannot unify goals because they are for different predicates: #{goal.arguments.predicate.name.inspect} and #{subgoal.arguments.predicate.name.inspect}"
-
2
goal.log << msg
-
2
subgoal.log << msg
-
2
false
-
end
-
end
-
-
# Instantiates the unifications from an attempt to unify.
-
# @param unifications [Array] unifications to instantiate.
-
# @return [Boolean] whether the instantiations could be made.
-
1
def instantiate_unifications(unifications)
-
# -- Gather Unifications --
-
6074
goals_variables = {}
-
-
6074
return_false = false
-
6074
unifications.each do |unification|
-
28386
left, right, left_goal, right_goal = unification
-
-
28386
goals_variables[left_goal] ||= {}
-
28386
goals_variables[left_goal][left] ||= []
-
28386
goals_variables[left_goal][left] << [right_goal,right]
-
-
# -- Check Consistency --
-
28386
goals_variables[left_goal][left].map(&:last).map(&:value)
-
28386
values = goals_variables[left_goal][left].map{|value|
-
28733
value.last.value.value
-
}
-
28386
next if values.size < 2
-
-
1010
arrays = values.any?{|value| value.is_a?(Array) }
-
343
if arrays && !values.variables.empty?
-
8
zipped_values = values[0].zip(*values[1..-1])
-
8
zipped_values.each do |zipped_value|
-
63
vars, atomics = zipped_value.partition{|v| v.type == :variable }
-
21
return_false = true if atomics.uniq.size > 1
-
21
vars.each do |var|
-
14
goals_variables[var.goal] ||= {}
-
14
goals_variables[var.goal][var] ||= []
-
14
goals_variables[var.goal][var] << [([left_goal,right_goal]-[var.goal]).first,atomics.first]
-
end
-
end
-
else
-
335
return_false = values.reject{|value|
-
674
value.is_a?(Variable) || value.is_a?(Symbol)
-
}.uniq.size > 1
-
end
-
-
343
return false if return_false
-
end
-
-
# -- Make Instantiations --
-
5814
instantiations = []
-
5814
consistent = true
-
-
5814
goals_variables.each do |goal, variables|
-
6384
variables.each do |name, others|
-
27780
others.each do |other_goal, other|
-
27860
instantiation = goal.instantiate(name, other, other_goal)
-
27860
if instantiation
-
27859
instantiations << instantiation
-
else
-
1
consistent = false
-
end
-
end
-
end
-
end
-
-
# -- Revert if inconsistent --
-
5814
if consistent
-
5813
instantiations
-
else
-
1
instantiations.each(&:remove)
-
nil
-
end
-
end
-
-
# Attempt to unify two entities of two goals.
-
# @param left [Object] left hand side entity.
-
# @param right [Object] right hand side entity.
-
# @param left_goal [Porolog::Goal] goal of left hand side entity.
-
# @param right_goal [Porolog::Goal] goal of right hand side entity.
-
# @param visited [Array] prevents infinite recursion.
-
# @return [Array] an Array of unifications when the left hand side can be unified with the right hand.
-
# @return [nil] nil if they cannot be unified.
-
1
def unify(left, right, left_goal, right_goal = left_goal, visited = [])
-
81325
right_goal ||= left_goal
-
81325
goals = [left_goal, right_goal].uniq
-
81325
signature = [left.type, right.type]
-
-
# -- Nil is Uninstantiated (can always unify) --
-
81325
return [] unless left && right
-
-
# -- Set HeadTail Goals --
-
81320
case signature
-
when [:atomic, :atomic]
-
113
if left == right
-
96
[]
-
else
-
17
msg = "Cannot unify because #{left.inspect} != #{right.inspect} (atomic != atomic)"
-
36
goals.each{|goal| goal.log << msg }
-
nil
-
end
-
-
when [:array, :array], [:tail, :tail]
-
2754
_merged, unifications = unify_arrays(left, right, left_goal, right_goal, visited)
-
2754
if unifications
-
2294
unifications
-
else
-
460
msg = "Cannot unify because #{left.inspect} != #{right.inspect} (array != array)"
-
1261
goals.each{|goal| goal.log << msg }
-
nil
-
end
-
-
when [:array, :tail]
-
4
_merged, unifications = unify_arrays([left], right.value, left_goal, right_goal, visited)
-
4
if unifications
-
3
unifications
-
else
-
1
msg = "Cannot unify because #{left.inspect} != #{right.inspect} (array != tail)"
-
2
goals.each{|goal| goal.log << msg }
-
nil
-
end
-
-
when [:tail, :array]
-
2
_merged, unifications = unify_arrays(left.value, [right], left_goal, right_goal, visited)
-
2
if unifications
-
1
unifications
-
else
-
1
msg = "Cannot unify because #{left.inspect} != #{right.inspect} (tail != array)"
-
2
goals.each{|goal| goal.log << msg }
-
nil
-
end
-
-
when [:variable, :atomic]
-
25599
left_value = left_goal.value_of(left, nil, visited)
-
25599
right_value = right
-
25599
if left_value == right_value || left_value.is_a?(Variable) || left_value.nil?
-
25594
[[left, right, left_goal, right_goal]]
-
else
-
5
msg = "Cannot unify because #{left_value.inspect} != #{right_value.inspect} (variable != atomic)"
-
12
goals.each{|goal| goal.log << msg }
-
nil
-
end
-
-
when [:atomic, :variable]
-
3450
left_value = left
-
3450
right_value = right_goal.value_of(right, nil, visited)
-
3450
if left == right_value || right_value.is_a?(Variable) || right_value.nil?
-
3449
[[right,left,right_goal,left_goal]]
-
else
-
1
msg = "Cannot unify because #{left_value.inspect} != #{right_value.inspect} (atomic != variable)"
-
2
goals.each{|goal| goal.log << msg }
-
nil
-
end
-
-
when [:variable, :variable]
-
24092
left_value = left_goal.value_of(left, nil, visited).value
-
24092
right_value = right_goal.value_of(right, nil, visited).value
-
24092
if left_value == right_value || left_value.is_a?(Variable) || right_value.is_a?(Variable) || left_value.nil? || right_value.nil?
-
24071
[[left, right, left_goal, right_goal]]
-
21
elsif left_value == UNKNOWN_ARRAY && (right_value.is_a?(Variable) || right_value.nil? || right_value.is_a?(Array))
-
14
[[left, right, left_goal, right_goal]]
-
7
elsif right_value == UNKNOWN_ARRAY && (left_value.is_a?(Variable) || left_value.nil? || left_value.is_a?(Array))
-
1
[[left, right, left_goal, right_goal]]
-
else
-
6
msg = "Cannot unify because #{left_value.inspect} != #{right_value.inspect} (variable != variable)"
-
17
goals.each{|goal| goal.log << msg }
-
nil
-
end
-
-
when [:variable, :array], [:variable, :tail]
-
25142
left_value = left_goal.value_of(left, nil, visited)
-
25142
right_value = right
-
25142
if left_value == right_value || left_value.is_a?(Variable) || left_value == UNKNOWN_ARRAY || left_value.nil?
-
25128
[[left, right, left_goal, right_goal]]
-
14
elsif left_value.type == :array
-
13
_merged, unifications = unify_arrays(left_value, right, left_goal, right_goal, visited)
-
13
if unifications
-
10
unifications
-
else
-
3
msg = "Cannot unify because #{left_value.inspect} != #{right.inspect} (variable/array != array)"
-
8
goals.each{|goal| goal.log << msg }
-
nil
-
end
-
else
-
1
msg = "Cannot unify because #{left_value.inspect} != #{right_value.inspect} (variable != array)"
-
2
goals.each{|goal| goal.log << msg }
-
nil
-
end
-
-
when [:array, :variable], [:tail, :variable]
-
162
left_value = left
-
162
right_value = right_goal.value_of(right, nil, visited)
-
162
if left_value == right_value || right_value.is_a?(Variable) || right_value == UNKNOWN_ARRAY || right_value.nil?
-
159
[[right, left, right_goal, left_goal]]
-
3
elsif right_value.type == :array
-
2
_merged, unifications = unify_arrays(left, right_value, left_goal, right_goal, visited)
-
2
if unifications
-
1
unifications
-
else
-
1
msg = "Cannot unify because #{left.inspect} != #{right_value.inspect} (variable/array != array)"
-
2
goals.each{|goal| goal.log << msg }
-
nil
-
end
-
else
-
1
msg = "Cannot unify because #{left_value.inspect} != #{right_value.inspect} (array != variable)"
-
2
goals.each{|goal| goal.log << msg }
-
nil
-
end
-
-
when [:array, :atomic], [:atomic, :array], [:tail, :atomic], [:atomic, :tail]
-
2
msg = "Cannot unify #{left.inspect} with #{right.inspect} (#{signature.join(' != ')})"
-
4
goals.each{|goal| goal.log << msg }
-
nil
-
-
else
-
skipped
# :nocov:
-
skipped
raise UnknownUnificationSignature, "UNKNOWN UNIFICATION SIGNATURE: #{signature.inspect}"
-
skipped
# :nocov:
-
end
-
end
-
-
# Attempt to unify two Arrays.
-
# @param left [Array] left hand side Array.
-
# @param right [Array] right hand side Array.
-
# @param left_goal [Porolog::Goal] goal of left hand side Array.
-
# @param right_goal [Porolog::Goal] goal of right hand side Array.
-
# @param visited [Array] prevents infinite recursion.
-
# @return [Array<Array, Array>] the merged Array and the unifications to be instantiated.
-
# @return [nil] if the Arrays cannot be unified.
-
1
def unify_arrays(left, right, left_goal, right_goal = left_goal, visited = [])
-
40586
arrays = [left, right]
-
40586
arrays_goals = [left_goal, right_goal]
-
121758
arrays_values = arrays.map{|array| array.value(visited) }
-
-
# -- Trivial Unifications --
-
40586
return [left_goal. variablise(left), []] if right == UNKNOWN_ARRAY
-
40577
return [right_goal.variablise(right), []] if left == UNKNOWN_ARRAY
-
-
# -- Validate Arrays --
-
121706
unless arrays_values.all?{|array| array.is_a?(Array) || array == UNKNOWN_TAIL }
-
2
msg = "Cannot unify a non-array with an array: #{left.inspect} with #{right.inspect}"
-
6
arrays_goals.uniq.each{|goal| goal.log << msg }
-
2
return nil
-
end
-
-
# -- Count Tails --
-
40567
number_of_arrays = arrays.size
-
121701
number_of_tails = arrays.count{|array| has_tail?(array) }
-
-
# -- Handle Tails --
-
40567
if number_of_tails.zero?
-
2839
unify_arrays_with_no_tails(arrays, arrays_goals, visited)
-
37728
elsif number_of_tails == number_of_arrays
-
24884
unify_arrays_with_all_tails(arrays, arrays_goals, visited)
-
else
-
12844
unify_arrays_with_some_tails(arrays, arrays_goals, visited)
-
end
-
end
-
-
# Attempt to unify multiple Arrays.
-
# @param arrays [Array<Array>] the Arrays to be unified.
-
# @param arrays_goals [Array<Porolog::Goal>] the Goals of the Arrays.
-
# @param visited [Array] prevents infinite recursion.
-
# @return [Array<Array, Array>] the merged Array and the unifications to be instantiated.
-
# @return [nil] if the Arrays cannot be unified.
-
1
def unify_many_arrays(arrays, arrays_goals, visited = [])
-
3304
arrays_values = arrays.
-
9382
map{|array| expand_splat(array) }.
-
9382
map{|array| array.is_a?(Array) ? array.map(&:value) : array.value }
-
-
12685
unless arrays_values.all?{|array_values| [:array,:variable].include?(array_values.type) }
-
1
msg = "Cannot unify: #{arrays.map(&:inspect).join(' with ')}"
-
2
arrays_goals.uniq.each{|goal| goal.log << msg }
-
1
return nil
-
end
-
-
# TODO: Fix / improve
-
4299
if arrays_values.size == 2 && arrays_values.any?{|array| array == UNKNOWN_ARRAY }
-
219
merged = arrays_values.reject{|array| array == UNKNOWN_ARRAY }.first
-
73
return [merged,[]]
-
end
-
-
3230
number_of_arrays = arrays.size
-
12463
number_of_tails = arrays.count{|array| has_tail?(array) }
-
-
3230
if number_of_tails.zero?
-
71
unify_arrays_with_no_tails(arrays, arrays_goals, visited)
-
3159
elsif number_of_tails == number_of_arrays
-
2
unify_arrays_with_all_tails(arrays, arrays_goals, visited)
-
else
-
3157
unify_arrays_with_some_tails(arrays, arrays_goals, visited)
-
end
-
end
-
-
# @return [Boolean] whether the provided value has a Tail.
-
# @param value [Object] the provided value.
-
1
def has_tail?(value, apply_value = true)
-
130994
value = value.value if value.respond_to?(:value) && apply_value
-
-
130994
if value.is_a?(Array)
-
130970
value.last == UNKNOWN_TAIL || value.last.is_a?(Tail)
-
else
-
24
false
-
end
-
end
-
-
# Expands a superfluous splat.
-
# @param array [Array] the given Array.
-
# @return [Array] the given Array with any superfluous splats expanded.
-
1
def expand_splat(array)
-
190996
array = array.first.value if array.is_a?(Array) && array.length == 1 && array.first.is_a?(Tail)
-
-
190996
if array.is_a?(Array) && array.last.is_a?(Tail) && array.last.value.is_a?(Array)
-
5
array = [*array[0...-1], *array.last.value]
-
end
-
-
190996
if array.is_a?(Array)
-
63162
array = array.map{|element|
-
125957
expand_splat(element)
-
}
-
end
-
-
190996
array
-
end
-
-
# Unifies Arrays where no Array has a Tail.
-
# @param arrays [Array<Array>] the Arrays to be unified.
-
# @param arrays_goals [Array<Porolog::Goal>] the Goals of the Arrays to be unified.
-
# @param visited [Array] prevents infinite recursion.
-
# @return [Array<Array, Array>] the merged Array and the unifications to be instantiated.
-
# @return [nil] if the Arrays cannot be unified.
-
1
def unify_arrays_with_no_tails(arrays, arrays_goals, visited)
-
# -- Validate Arrays --
-
8767
if arrays.any?{|array| has_tail?(array) }
-
1
msg = "Wrong unification method called: no_tails but one or more of #{arrays.inspect} has a tail"
-
1
arrays_goals.uniq.each do |goal|
-
4
goal.log << msg
-
end
-
1
return nil
-
end
-
-
# -- Trivial Unifications --
-
2913
return [[],[]] if arrays.empty?
-
-
# -- Ensure Arrays elements have Goals --
-
2913
arrays_values = arrays.dup
-
2913
arrays_goals = arrays_goals.dup
-
-
2913
arrays.each_with_index do |array, index|
-
5851
if array.is_a?(Value)
-
43
arrays_goals[index] ||= array.goal
-
43
arrays_values[index] = array.value
-
end
-
-
5851
if arrays_goals[index].nil?
-
5
value_with_goal = array.find{|element| element.respond_to?(:goal) }
-
1
arrays_goals[index] = value_with_goal.goal if value_with_goal&.goal
-
end
-
-
5851
if arrays_goals[index].nil?
-
skipped
# :nocov:
-
skipped
raise NoGoalError, "Array #{array.inspect} has no goal for unification"
-
skipped
# :nocov:
-
end
-
-
5851
arrays_values[index] = expand_splat(arrays_values[index])
-
end
-
-
# -- Check whether all arrays are an Array --
-
8763
unless arrays_values.all?{|array| array.is_a?(Array) }
-
23
merged, unifications = unify_many_arrays(arrays.map{|array| [array] }, arrays_goals, visited)
-
6
return [merged] + [unifications]
-
end
-
-
2907
arrays_variables = arrays_goals.zip(arrays)
-
-
# -- Remap Arrays so that they are variablised and valuised with their Goals --
-
2907
new_arrays = []
-
2907
arrays_variables.each do |goal, variables|
-
5834
new_array = variables.map{|variable|
-
13100
value = goal.value_of(variable, nil, visited).value
-
13100
value = variable if [UNKNOWN_TAIL, UNKNOWN_ARRAY].include?(value)
-
-
13100
if value.type == :variable
-
5930
value = goal.variable(value)
-
7170
elsif value.is_a?(Array)
-
1372
value = value.map{|element|
-
2344
if element.type == :variable
-
682
goal.variable(element)
-
1662
elsif element.is_a?(Value)
-
143
element
-
1519
elsif element.is_a?(Tail)
-
546
element
-
else
-
973
goal.value(element)
-
end
-
}
-
5798
elsif !value.is_a?(Value)
-
5797
value = goal.value(value)
-
end
-
13100
value
-
}
-
5834
new_arrays << new_array
-
end
-
2907
arrays = new_arrays
-
-
# -- Unify All Elements --
-
2907
sizes = arrays.map(&:length).uniq
-
2907
if sizes.size <= 1
-
# -- Arrays are the same length --
-
2852
zipped = arrays[0].zip(*arrays[1..-1]).map(&:uniq).map(&:compact).uniq
-
2852
unifications = []
-
2852
merged = []
-
-
# TODO: Change these names
-
2852
zipped.each{|values_to_unify|
-
5802
values_to_unify_values = values_to_unify.map{|value|
-
11503
value_value = value.value
-
11503
if [UNKNOWN_TAIL, UNKNOWN_ARRAY].include?(value_value)
-
111
value
-
else
-
11392
value_value
-
end
-
}.compact.uniq
-
-
5802
if values_to_unify_values.size <= 1
-
# -- One Value --
-
302
value = values_to_unify_values.first
-
302
if value.type == :variable
-
15
merged << nil
-
else
-
287
merged << value.value
-
end
-
else
-
11575
if values_to_unify_values.all?{|value| value.is_a?(Array) }
-
463
submerged, subunifications = unify_many_arrays(values_to_unify_values, arrays_goals)
-
463
if subunifications
-
307
merged << submerged
-
307
unifications += subunifications
-
else
-
156
msg = "Cannot unify: #{values_to_unify_values.map(&:inspect).join(' with ')}"
-
469
arrays_goals.uniq.each{|goal| goal.log << msg }
-
156
return nil
-
end
-
5037
elsif values_to_unify.variables.empty?
-
# -- Incompatible Values --
-
317
incompatible_values = values_to_unify.map(&:value)
-
317
msg = "Cannot unify incompatible values: #{incompatible_values.compact.map(&:inspect).join(' with ')}"
-
834
arrays_goals.uniq.each{|goal| goal.log << msg }
-
317
return nil
-
else
-
# -- Potentially Compatible Values/Variables --
-
14167
nonvariables = values_to_unify.map(&:value).compact.reject{|value| value.type == :variable }
-
-
14178
values_to_unify.reject{|value| value.value.nil? }.combination(2).each do |vl, vr|
-
4736
subunifications = unify(vl, vr, vl.goal, vr.goal, visited)
-
4736
if subunifications
-
4735
unifications += subunifications
-
else
-
1
msg = "Cannot unify: #{vl.inspect} with #{vr.inspect}"
-
3
[vl.goal, vr.goal].uniq.each{|goal| goal.log << msg }
-
1
return nil
-
end
-
end
-
-
4719
if nonvariables.size > 1
-
71
subgoals = arrays_goals.dup
-
71
nonvariables.each_with_index do |nonvariable, index|
-
145
if nonvariable.respond_to?(:goal)
-
145
subgoal = nonvariable.goal
-
145
subgoals[index] = subgoal if subgoal
-
end
-
end
-
71
subgoals = subgoals[0...nonvariables.size]
-
71
merged_nonvariables, _nonvariable_unifications = unify_many_arrays(nonvariables, subgoals, visited)
-
else
-
4648
merged_nonvariables = nonvariables
-
end
-
-
4719
merged_value = merged_nonvariables
-
4719
merged_value = [nonvariables.first.value] if merged_value.nil? || merged_value == []
-
4719
merged += merged_value
-
end
-
end
-
}
-
-
2378
[merged, unifications]
-
else
-
# -- Arrays are different lengths --
-
55
msg = "Cannot unify arrays of different lengths: #{arrays_values.map(&:inspect).join(' with ')}"
-
165
arrays_goals.uniq.each{|goal| goal.log << msg }
-
nil
-
end
-
end
-
-
# Unifies Arrays where each Array has a Tail.
-
# @param arrays [Array<Array>] the Arrays to be unified.
-
# @param arrays_goals [Array<Porolog::Goal>] the Goals of the Arrays to be unified.
-
# @param visited [Array] prevents infinite recursion.
-
# @return [Array<Array, Array>] the merged Array and the unifications to be instantiated.
-
# @return [nil] if the Arrays cannot be unified.
-
1
def unify_arrays_with_all_tails(arrays, arrays_goals, visited)
-
# -- All tails --
-
24863
arrays = arrays.map{|array|
-
49732
expand_splat(array.headtail? ? array : array.value)
-
}
-
-
# -- Unify Embedded Arrays --
-
74591
if arrays.any?{|array| array.type == :variable }
-
2
unifications = unify(*arrays[0...2], *arrays_goals[0...2], visited)
-
2
if unifications
-
4
merged = arrays.reject{|value| value.type == :variable }.first || nil
-
1
return [merged, unifications]
-
else
-
1
msg = "Cannot unify embedded arrays: #{arrays[0...2].map(&:value).map(&:inspect).join(' with ')}"
-
1
arrays_goals.uniq.each do |goal|
-
3
goal.log << msg
-
end
-
1
return nil
-
end
-
end
-
-
24861
signature = arrays.map(&:headtail?)
-
24861
unifications = []
-
24861
merged = []
-
-
24861
if signature.all?
-
24855
merged, unifications = unify_headtail_with_headtail(arrays, arrays_goals, visited)
-
6
elsif signature.map(&:!).all?
-
4
merged, unifications = unify_tail_with_tail(arrays, arrays_goals, visited)
-
else
-
2
merged, unifications = unify_headtail_with_tail(arrays, arrays_goals, visited)
-
end
-
-
24861
if unifications
-
24859
[merged, unifications]
-
else
-
nil
-
end
-
end
-
-
# Unifies Arrays where each Array is not a Head/Tail array.
-
# @param arrays [Array<Array>] the Arrays to be unified.
-
# @param arrays_goals [Array<Porolog::Goal>] the Goals of the Arrays to be unified.
-
# @param visited [Array] prevents infinite recursion.
-
# @return [Array<Array, Array>] the merged Array and the unifications to be instantiated.
-
# @return [nil] if the Arrays cannot be unified.
-
1
def unify_tail_with_tail(arrays, arrays_goals, visited)
-
# -- Validate Arrays --
-
12
unless arrays.map(&:headtail?).map(&:!).all?
-
1
msg = "Wrong method called to unify #{arrays.inspect}"
-
1
arrays_goals.uniq.each do |goal|
-
3
goal.log << msg
-
end
-
1
return nil
-
end
-
-
# -- Variablise Arrays --
-
11
arrays = arrays_goals.zip(arrays).map do |goal, array|
-
28
goal.variablise(array)
-
end
-
-
# == [1,2,3]/:tail ~~ [1,2,3]/:tail ==
-
# -- Extract Elements --
-
11
merged = []
-
11
unifications = []
-
-
39
arrays = arrays.sort_by{|array| -array.length }
-
-
11
arrays[0].zip(*arrays[1..-1]).each_with_index do |values,index|
-
134
if values.any?{|value| value.is_a?(Tail) }
-
# -- Unify Remaining Values and Tails --
-
28
tails = arrays.map{|array| expand_splat(array[index..-1]) }
-
8
merged_tails = []
-
8
tails.combination(2).each do |pair|
-
17
next if pair.compact.size < 2
-
48
if pair.any?{|tail| tail == UNKNOWN_ARRAY }
-
9
merged_tails += pair.reject{|tail| tail == UNKNOWN_ARRAY }
-
3
next
-
end
-
13
first_goal = nil
-
13
last_goal = nil
-
13
first_goal = pair.first.goal if pair.first.respond_to?(:goal)
-
13
last_goal = pair.last.goal if pair.last.respond_to?(:goal)
-
13
m,u = unify_arrays(first_goal.variablise([pair.first]), last_goal.variablise([pair.last]), first_goal, last_goal, visited)
-
13
return nil unless m
-
12
m[-1] = UNKNOWN_TAIL if m[-1].nil?
-
12
merged_tails += m
-
12
unifications += u
-
end
-
7
merged_tails.uniq!
-
7
merged_tails_unifications = []
-
# TODO: Fix [first_goal] * arrays.size
-
7
if merged_tails.size > 1
-
2
merged_tails, merged_tails_unifications = unify_many_arrays(merged_tails, [arrays_goals.first] * arrays.size, visited)
-
end
-
7
merged += merged_tails.flatten(1)
-
7
unifications += merged_tails_unifications - unifications
-
7
break
-
else
-
# -- Unify Values at Index --
-
32
merged_value = []
-
32
values.combination(2).each do |pair|
-
74
if pair.any?(&:nil?)
-
28
merged_value += pair.compact
-
28
next
-
end
-
46
first_goal = nil
-
46
last_goal = nil
-
46
first_goal = pair.first.goal if pair.first.respond_to?(:goal)
-
46
last_goal = pair.last.goal if pair.last.respond_to?(:goal)
-
-
46
m,u = unify_arrays([pair.first], [pair.last], first_goal, last_goal, visited)
-
46
if m.nil?
-
1
[first_goal, last_goal].uniq.each do |goal|
-
2
goal.log << "Cannot unify #{pair.first.inspect} with #{pair.last.inspect}"
-
end
-
1
return nil
-
end
-
45
merged_value += m
-
45
unifications += u
-
end
-
31
merged << merged_value.compact.uniq.first || nil
-
end
-
end
-
-
9
[merged, unifications]
-
end
-
-
# Unifies Arrays where the Arrays are a mixture of Head/Tail and non-Head/Tail arrays.
-
# @param arrays [Array<Array>] the Arrays to be unified.
-
# @param arrays_goals [Array<Porolog::Goal>] the Goals of the Arrays to be unified.
-
# @param visited [Array] prevents infinite recursion.
-
# @return [Array<Array, Array>] the merged Array and the unifications to be instantiated.
-
# @return [nil] if the Arrays cannot be unified.
-
1
def unify_headtail_with_tail(arrays, arrays_goals, visited)
-
# -- Validate Arrays --
-
44
unless arrays.all?{|array| has_tail?(array, false) }
-
1
msg = "Wrong method called to unify #{arrays.inspect}"
-
1
arrays_goals.uniq.each do |goal|
-
3
goal.log << msg
-
end
-
1
return nil
-
end
-
-
9
merged = []
-
9
unifications = []
-
-
# -- Variablise Arrays --
-
9
arrays = arrays_goals.zip(arrays).map do |goal, array|
-
32
if array == UNKNOWN_ARRAY
-
1
array
-
else
-
31
goal.variablise(array)
-
end
-
end
-
-
# -- Determine the fixed length (if any) --
-
9
fixed_length = nil
-
9
arrays.each do |array|
-
31
next if has_tail?(array)
-
-
11
array_length = array.value.size
-
11
fixed_length ||= array_length
-
11
unless fixed_length == array_length
-
1
array.goal.log << "Cannot unify #{array.value.inspect} because it has a different length from #{fixed_length}"
-
1
return nil
-
end
-
end
-
-
# -- Partition Arrays --
-
8
headtail_arrays, tail_arrays = arrays.partition(&:headtail?)
-
-
# -- Unify All HeadTail Arrays --
-
8
if headtail_arrays.size > 1
-
6
headtail_goals = headtail_arrays.map(&:goal)
-
6
merged_headtails, headtail_unifications = unify_headtail_with_headtail(headtail_arrays, headtail_goals, visited)
-
6
unless merged_headtails
-
1
msg = "Could not unify headtail arrays: #{headtail_arrays.map(&:value).map(&:inspect).join(' with ')}"
-
1
headtail_goals.uniq.each do |goal|
-
3
goal.log << msg
-
end
-
1
return nil
-
end
-
5
unifications += headtail_unifications
-
-
5
if merged_headtails.length > merged.length
-
5
merged += [[]] * (merged_headtails.length - merged.length)
-
end
-
-
# TODO: Remove flatten
-
5
merged = merged.zip(merged_headtails).map(&:flatten)
-
end
-
-
# -- Unify All Tail Arrays --
-
7
if tail_arrays.size > 1
-
4
tail_goals = tail_arrays.map(&:goal)
-
4
merged_tails, tail_unifications = unify_tail_with_tail(tail_arrays, tail_goals, visited)
-
4
return nil unless merged_tails
-
3
unifications += tail_unifications
-
-
3
if merged_tails.length > merged.length
-
2
merged += [[]] * (merged_tails.length - merged.length)
-
end
-
-
# TODO: Remove flatten
-
3
merged = merged.zip(merged_tails).map(&:flatten).map{|merge_values|
-
10
merge_values.map{|value|
-
17
if value == UNKNOWN_TAIL
-
2
nil
-
else
-
15
value
-
end
-
}
-
}.map(&:compact)
-
end
-
-
# -- Combine HeadTail Arrays and Tail Arrays --
-
6
headtail_arrays.product(tail_arrays).each do |pair|
-
# == :head/:tail ~~ [1,2,3]/:tail ==
-
# -- Extract Elements --
-
12
left = expand_splat(pair.first)
-
12
left_head = left.head
-
12
left_tail = left.tail
-
-
12
right = expand_splat(pair.last)
-
12
right_head = right.head
-
12
right_tail = right.tail
-
-
# -- Expand Tail --
-
12
left_tail = expand_splat(left_tail)
-
12
right_tail = expand_splat(right_tail)
-
-
# -- Determine Goals --
-
12
left_goal = pair.first.goal
-
12
right_goal = pair.last.goal
-
-
# -- Unify Heads --
-
12
head_unifications = unify(left_head, right_head, left_goal, right_goal, visited)
-
12
if head_unifications.nil?
-
1
msg = "Cannot unify heads: #{left_head.inspect} with #{right_head.inspect}"
-
1
left_goal.log << msg
-
1
right_goal.log << msg unless right_goal == left_goal
-
1
return nil
-
end
-
11
unifications += head_unifications
-
-
# -- Unify Tails --
-
11
tail_unifications = unify(left_tail, right_tail, left_goal, right_goal, visited)
-
11
if tail_unifications.nil?
-
1
msg = "Cannot unify tails: #{left_tail.inspect} with #{right_tail.inspect}"
-
1
left_goal.log << msg
-
1
right_goal.log << msg unless right_goal == left_goal
-
1
return nil
-
end
-
10
unifications += tail_unifications
-
-
# -- Determine Merged and Unifications --
-
10
left_reassembled = [left_head, *left_tail ]
-
10
right_reassembled = [right_head, *right_tail]
-
10
max_length = [left_reassembled.length, right_reassembled.length].max
-
-
10
if max_length > merged.length
-
3
merged += [[]] * (max_length - merged.length)
-
end
-
-
10
merged = merged.zip(left_reassembled ).map(&:flatten)
-
10
merged = merged.zip(right_reassembled).map(&:flatten)
-
end
-
-
4
merged = merged.value
-
4
merged = merged.map(&:value)
-
-
# TODO: Cleanup names
-
# TODO: Flatten out tails
-
# E.g. [nil, [2, 3], ...] should be [nil, 2, 3, ...]
-
4
is_tails = []
-
4
merged = merged.value.map{|elements|
-
13
sorted_elements = elements.reject{|element|
-
82
element.is_a?(Variable)
-
}.uniq.compact.sort_by{|element|
-
14
case element.type
-
when :atomic
-
5
0
-
when :array, :tail
-
9
if [UNKNOWN_TAIL, UNKNOWN_ARRAY].include?(element)
-
5
3
-
else
-
4
1
-
end
-
else
-
skipped
# :nocov:
-
skipped
# There are only 3 types and variables have already been filtered.
-
skipped
2
-
skipped
# :nocov:
-
end
-
}
-
-
26
is_tails << sorted_elements.any?{|element| element.is_a?(Tail) || element == UNKNOWN_TAIL }
-
-
13
merged_value = sorted_elements.first
-
-
13
if merged_value.is_a?(Tail) && merged_value.value.is_a?(Variable)
-
2
UNKNOWN_TAIL
-
else
-
11
merged_value
-
end
-
}
-
4
merged[0...-1] = merged[0...-1].map{|value|
-
9
if value == UNKNOWN_TAIL
-
1
nil
-
else
-
8
value
-
end
-
}
-
-
4
merged = merged.map{|value|
-
13
if is_tails.shift
-
7
value
-
else
-
6
[value]
-
end
-
}
-
4
merged = merged.flatten(1)
-
4
merged = merged[0...fixed_length] if fixed_length
-
-
4
[merged, unifications]
-
end
-
-
# Unifies Arrays where each Array is a Head/Tail array.
-
# @param arrays [Array<Array>] the Arrays to be unified.
-
# @param arrays_goals [Array<Porolog::Goal>] the Goals of the Arrays to be unified.
-
# @param visited [Array] prevents infinite recursion.
-
# @return [Array<Array, Array>] the merged Array and the unifications to be instantiated.
-
# @return [nil] if the Arrays cannot be unified.
-
1
def unify_headtail_with_headtail(arrays, arrays_goals, visited)
-
24865
unless arrays.all?(&:headtail?)
-
1
msg = "Wrong method called to unify #{arrays.inspect}"
-
1
arrays_goals.uniq.each do |goal|
-
3
goal.log << msg
-
end
-
1
return nil
-
end
-
-
24864
unifications = []
-
-
24864
arrays.combination(2).each do |pair|
-
# -- Collect Goals --
-
74619
pair_goals = pair.map{|array| arrays_goals[arrays.index(array)] }
-
-
# -- Unify Heads --
-
24873
heads = pair.map(&:first)
-
24873
subunifications = unify(*heads, *pair_goals, visited)
-
24873
if subunifications
-
24872
unifications += subunifications
-
else
-
1
unifications = nil
-
1
msg = "Cannot unify headtail heads: #{heads.map(&:inspect).join(' with ').inspect}"
-
1
pair_goals.uniq.each do |goal|
-
2
goal.log << msg
-
end
-
-
1
return nil
-
end
-
-
# -- Unify Tails --
-
74616
tails = pair.map(&:last).map{|tail| tail.value(visited) }
-
24872
subunifications = unify(*tails, *pair_goals, visited)
-
24872
if subunifications
-
24871
unifications += subunifications
-
else
-
1
unifications = nil
-
1
msg = "Cannot unify headtail tails: #{tails.map(&:inspect).join(' with ')}"
-
1
pair_goals.uniq.each do |goal|
-
2
goal.log << msg
-
end
-
-
1
return nil
-
end
-
end
-
-
# -- Determine Merged --
-
merged = [
-
124318
arrays.map(&:first).map{|head| head.value(visited) }.reject{|head| head.type == :variable }.first,
-
99456
*arrays.map(&:last).map{|tail| tail.value(visited) }.reject{|tail| tail.type == :variable || tail == UNKNOWN_TAIL }.first || UNKNOWN_TAIL,
-
]
-
-
24862
[merged, unifications]
-
end
-
-
# Unifies Arrays where Arrays with a Tail are unified with Arrays without a Tail.
-
# @param arrays [Array<Array>] the Arrays to be unified.
-
# @param arrays_goals [Array<Porolog::Goal>] the Goals of the Arrays to be unified.
-
# @param visited [Array] prevents infinite recursion.
-
# @return [Array<Array, Array>] the merged Array and the unifications to be instantiated.
-
# @return [nil] if the Arrays cannot be unified.
-
1
def unify_arrays_with_some_tails(arrays, arrays_goals, visited)
-
# -- Variablise Arrays --
-
15941
arrays = arrays_goals.zip(arrays).map{|goal,array|
-
34646
if goal.nil?
-
2
goal = array.goal if array.is_a?(Value)
-
2
if goal.nil? && array.is_a?(Array)
-
10
v = array.find{|element| element.respond_to?(:goal) }
-
2
goal = v&.goal
-
end
-
end
-
34646
goal = arrays_goals.compact.last if goal.nil?
-
34646
raise NoGoalError, "#{array.inspect} has no associated goal! Cannot variablise!" if goal.nil? || !goal.is_a?(Goal)
-
34645
goal.variablise(array)
-
}
-
-
# -- Some unknown tails --
-
50585
tailed_arrays, finite_arrays = arrays.partition{|array| has_tail?(array) }
-
-
31887
finite_sizes = finite_arrays.reject{|finite_array| finite_array.type == :variable }.map(&:size).uniq
-
-
15940
unless finite_sizes.size == 1
-
1
msg = "Cannot unify different sizes of arrays: #{arrays.map(&:inspect).join(' with ')}"
-
1
arrays_goals.uniq.each do |goal|
-
3
goal.log << msg
-
end
-
1
return nil
-
end
-
-
15939
exact_size = finite_sizes.first
-
-
34636
tails = tailed_arrays.map{|array| [array[0...-1].size,array.last,arrays_goals[arrays.index(array)]] }
-
34636
tailed_arrays = tailed_arrays.map{|array| array[0...-1] }
-
-
# -- Fail --
-
# [nil,nil,nil,nil,...]
-
# [1, 2, 3]
-
15939
min_tailed_size = tailed_arrays.map(&:size).max
-
-
15939
if min_tailed_size > exact_size
-
106
msg = "Cannot unify enough elements: #{arrays.map(&:inspect).join(' with ')}"
-
106
arrays_goals.uniq.each do |goal|
-
212
goal.log << msg
-
end
-
106
return nil
-
end
-
-
# -- Succeed --
-
# [nil,nil,...]
-
# [1, 2, 3]
-
15833
arrays = tailed_arrays + finite_arrays
-
-
15833
zip_arrays = arrays.map{|array|
-
34430
if array.is_a?(Value) && array.value.is_a?(Array)
-
26
array.value.map{|v|
-
86
if v.type == :variable
-
3
array.goal.variable(v)
-
else
-
83
array.goal.value(v)
-
end
-
}
-
34404
elsif array.type == :variable
-
1
array.goal.value_of(array)
-
else
-
34403
array
-
end
-
34430
}.select{|array| array.is_a?(Array) }
-
-
15833
zipped = ([nil] * exact_size).zip(*zip_arrays).map(&:uniq).map(&:compact)
-
15833
merged = []
-
15833
unifications = []
-
-
15833
zipped.each{|zipped_values|
-
19996
values = zipped_values
-
43108
value = values.reject{|v| v.value(visited).nil? }.compact.uniq
-
19996
value_values = value.map(&:value).compact.uniq
-
19996
if value_values.size <= 1
-
16920
m = value.first.value
-
16920
merged << m
-
else
-
3076
if values.variables.empty?
-
1
msg = "Cannot unify enough elements: #{values.map(&:inspect).join(' with ')}"
-
1
arrays_goals.uniq.each do |goal|
-
3
goal.log << msg
-
end
-
1
return nil
-
else
-
15383
_variables, nonvariables = values.reject{|v| v.value.nil? }.partition{|element| element.type == :variable }
-
3075
if nonvariables.value.uniq.size <= 1
-
3074
m = nonvariables.first.value
-
3074
merged << m
-
-
3074
value.combination(2).each do |vl, vr|
-
3080
if vl.type == :variable
-
3065
unifications << [vl, vr, vl.goal, vr.goal]
-
15
elsif vr.type == :variable
-
14
unifications << [vr, vl, vr.goal, vl.goal]
-
end
-
end
-
else
-
1
msg = "Cannot unify non-variables: #{nonvariables.value.uniq.map(&:inspect).join(' with ')}"
-
1
arrays_goals.uniq.each do |goal|
-
3
goal.log << msg
-
end
-
1
return nil
-
end
-
end
-
end
-
}
-
-
# -- Unify Tails --
-
15831
tails.each do |head_size,tail,goal|
-
18588
next if tail == UNKNOWN_TAIL
-
15821
merged_goals = arrays_goals - [goal] + [goal]
-
15821
unifications << [tail.value(visited).value(visited), merged[head_size..-1], goal, merged_goals.compact.first]
-
end
-
-
15831
[merged, unifications]
-
end
-
-
end
-
-
1
require_relative 'porolog/core_ext'
-
1
require_relative 'porolog/error'
-
1
require_relative 'porolog/scope'
-
1
require_relative 'porolog/predicate'
-
1
require_relative 'porolog/predicate/builtin'
-
1
require_relative 'porolog/arguments'
-
1
require_relative 'porolog/rule'
-
1
require_relative 'porolog/goal'
-
1
require_relative 'porolog/variable'
-
1
require_relative 'porolog/value'
-
1
require_relative 'porolog/instantiation'
-
1
require_relative 'porolog/tail'
-
#
-
# lib/porolog/arguments.rb - Plain Old Ruby Objects Prolog Engine -- Arguments
-
#
-
# Luis Esteban 2 May 2018
-
# created
-
#
-
-
1
module Porolog
-
-
# A Porolog::Arguments specifies arguments of a Predicate.
-
# A predicate is not like a subroutine, although there are many similarities.
-
# An Arguments represents an instance of a predicate with a specific set of arguments.
-
# This forms the basis for implementing a goal to solve the predicate with those specific arguments.
-
#
-
# @author Luis Esteban
-
#
-
# @!attribute [r] predicate
-
# The Predicate for which these are the arguments.
-
# @!attribute [r] arguments
-
# The actual arguments.
-
1
class Arguments
-
-
1
attr_reader :predicate, :arguments, :block
-
-
# Unregisters all Arguments
-
# @return [true]
-
1
def self.reset
-
721
@@arguments = []
-
721
true
-
end
-
-
1
reset
-
-
# Creates a new Arguments for a Predicate
-
# @param predicate [Porolog::Predicate] the Predicate for which these are the arguments
-
# @param arguments [Array<Object>] the actual arguments
-
1
def initialize(predicate, arguments, &block)
-
8817
@predicate = predicate
-
8817
@arguments = arguments
-
8817
@block = block
-
8817
@@arguments << self
-
end
-
-
# Convenience method for testing/debugging
-
# @return [Array<Porolog::Arguments>] all registered Arguments
-
1
def self.arguments
-
7
@@arguments
-
end
-
-
# Convenience method for testing/debugging
-
# @return [String] pretty identification
-
1
def myid
-
19
"Arguments#{(@@arguments.index(self) || -1000) + 1}"
-
end
-
-
# @return [String] pretty representation
-
1
def inspect
-
7431
block_inspect = block.nil? ? '' : "{#{block.inspect}}"
-
7431
"#{@predicate&.name}(#{@arguments&.map(&:inspect).join(',')})#{block_inspect}"
-
end
-
-
# Creates a fact rule that states that these arguments satisfy the Predicate.
-
# @return [Porolog::Arguments] the Arguments
-
1
def fact!
-
91
@predicate << Rule.new(self, true)
-
91
self
-
end
-
-
# Creates a fact rule that states that these arguments do not satisfy the Predicate.
-
# @return [Porolog::Arguments] the Arguments
-
1
def fallacy!
-
4
@predicate << Rule.new(self, false)
-
4
self
-
end
-
-
# Creates a fact rule that states that these arguments satisfy the Predicate and terminates solving the predicate.
-
# @return [Porolog::Arguments] the Arguments
-
1
def cut_fact!
-
4
@predicate << Rule.new(self, [:CUT, true])
-
4
self
-
end
-
-
# Creates a fact rule that states that these arguments do not satisfy the Predicate and terminates solving the predicate.
-
# @return [Porolog::Arguments] the Arguments
-
1
def cut_fallacy!
-
2
@predicate << Rule.new(self, [:CUT, false])
-
2
self
-
end
-
-
# Adds a Rule to the Predicate for these Arguments
-
# @param definition [Array<Object>, Object] the Rule definition for these Arguments
-
# @return [Porolog::Arguments] the Arguments
-
1
def <<(*definition)
-
58
@predicate << Rule.new(self, *definition)
-
58
self
-
end
-
-
# Adds an evaluation Rule to the Predicate
-
# @param block the block to evaluate
-
# @return [Porolog::Arguments] the Arguments
-
1
def evaluates(&block)
-
1
@predicate << Rule.new(self, block)
-
1
self
-
end
-
-
# @return [Array<Symbol>] the variables contained in the arguments
-
1
def variables
-
7406
@arguments.map(&:variables).flatten.uniq
-
end
-
-
# Creates a Goal for solving this Arguments for the Predicate
-
# @param calling_goal [Porolog::Goal] the parent Goal (if this is a subgoal)
-
# @return [Porolog::Goal] the Goal to solve
-
1
def goal(calling_goal = nil)
-
4733
Goal.new(self, calling_goal)
-
end
-
-
# Returns memoized solutions
-
# @param max_solutions [Integer] the maximum number of solutions to find (nil means find all)
-
# @return [Array<Hash{Symbol => Object}>] the solutions found (memoized)
-
1
def solutions(max_solutions = nil)
-
18
@solutions ||= solve(max_solutions)
-
18
max_solutions && @solutions[0...max_solutions] || @solutions
-
end
-
-
# Solves the Arguments
-
# @param max_solutions [Integer] the maximum number of solutions to find (nil means find all)
-
# @return [Array<Hash{Symbol => Object}>] the solutions found
-
1
def solve(max_solutions = nil)
-
235
@solutions = goal.solve(max_solutions)
-
end
-
-
# Extracts solution values.
-
# @param variables [Symbol, Array<Symbol>] variable or variables
-
# @param max_solutions [Integer] the maximum number of solutions to find (nil means find all)
-
# @return [Array<Object>] all the values for the variables given
-
# @example
-
# predicate :treasure_at
-
# treasure_at(:X,:Y) << [...]
-
# arguments = treasure_at(:X,:Y)
-
# xs = arguments.solve_for(:X)
-
# ys = arguments.solve_for(:Y)
-
# coords = xs.zip(ys)
-
1
def solve_for(*variables, max_solutions: nil)
-
6
variables = [*variables]
-
6
solutions(max_solutions).map{|solution|
-
131
values = variables.map{|variable| solution[variable] }
-
65
if values.size == 1
-
64
values.first
-
else
-
1
values
-
end
-
}
-
end
-
-
# @return [Boolean] whether any solutions were found
-
1
def valid?
-
7
!solutions(1).empty?
-
end
-
-
# Duplicates the Arguments in the context of the given goal
-
# @param goal [Porolog::Goal] the destination goal
-
# @return [Porolog::Arguments] the duplicated Arguments
-
1
def dup(goal)
-
7402
self.class.new @predicate, goal.variablise(arguments), &@block
-
end
-
-
# @param other [Porolog::Arguments] arguments for comparison
-
# @return [Boolean] whether the Arguments match
-
1
def ==(other)
-
21
@predicate == other.predicate &&
-
@arguments == other.arguments
-
end
-
-
end
-
-
end
-
#
-
# lib/porolog/core_ext.rb - Plain Old Ruby Objects Prolog Engine -- Core Extensions
-
#
-
# Luis Esteban 2 May 2018
-
# created
-
#
-
# @author Luis Esteban
-
# Extends Core Ruy Classes
-
#
-
-
# In Porolog, Objects represent atomic values.
-
1
class Object
-
-
# A convenience method for testing/debugging.
-
# @return [String] the equivalent of inspect.
-
1
def myid
-
5
inspect
-
end
-
-
# @return [Array] embedded variables (for an Object, should be none).
-
1
def variables
-
120033
[]
-
end
-
-
# @return [Symbol] the type of the object (for an Object, should be :atomic)
-
1
def type
-
706916
:atomic
-
end
-
-
# @return [Object] the value of the object, which is itself.
-
1
def value(*)
-
1497777
self
-
end
-
-
# @param other [Object] the Object which is to be the tail
-
# @return [Array] an Array with the Object being the head and the other Object being the tail.
-
# @example
-
# 'head' / ['body', 'foot'] # ==> ['head', 'body', 'foot']
-
# 7.tail(:t) # ==> [7, *:t]
-
1
def tail(other)
-
4
[self]/other
-
end
-
-
# Syntactic sugar to look like Prolog's [H|T]
-
1
alias / :tail
-
-
# @return [Boolean] whether the Object is an Array with a head and a tail (for an Object, should be false).
-
1
def headtail?
-
24857
false
-
end
-
-
end
-
-
-
# In Porolog, Symbols represent Variables.
-
1
class Symbol
-
-
# A convenience method for testing/debugging.
-
# @return [String] the equivalent of inspect.
-
1
def myid
-
5
inspect
-
end
-
-
# @return [Array] embedded variables (for a Symbol, should be itself).
-
1
def variables
-
38
[self]
-
end
-
-
# @return [Symbol] the type of the object (for a Symbol, should be :variable)
-
1
def type
-
70192
:variable
-
end
-
-
# @param other [Object] the Object which is to be the tail
-
# @return [Array] an Array with the Object being the head and the other Object being the tail.
-
1
def /(other)
-
17
[self]/other
-
end
-
-
end
-
-
-
# In Porolog, Arrays are the equivalent of lists.
-
1
class Array
-
-
# @return [Array] embedded variables.
-
1
def variables
-
145190
map(&:variables).flatten
-
end
-
-
# @return [Array] the values of its elements.
-
1
def value(visited = [])
-
1269273
return self if visited.include?(self)
-
1269273
visited = visited + [self]
-
1269273
flat_map{|element|
-
707793
if element.is_a?(Porolog::Tail)
-
5398
tail = element.value(visited)
-
5398
if tail.is_a?(Array)
-
6
tail
-
5392
elsif tail.is_a?(Porolog::Variable) || tail.is_a?(Porolog::Value)
-
5213
tail = tail.value(visited)
-
5213
if tail.is_a?(Array)
-
1
tail
-
5212
elsif tail.is_a?(Porolog::Variable) || tail.is_a?(Porolog::Value)
-
4642
tail = tail.goal.variablise(tail.value(visited))
-
4642
if tail.is_a?(Array)
-
32
tail
-
else
-
4610
[element]
-
end
-
else
-
570
[element]
-
end
-
else
-
179
[element]
-
end
-
else
-
702395
[element.value(visited)]
-
end
-
}
-
end
-
-
# Removes Porolog processing objects.
-
# @return [Array] the values of its elements with variables replaced by nil and Tails replaced by UNKNOWN_TAIL.
-
1
def clean
-
1865
value.map{|element|
-
4091
if element.is_a?(Array)
-
280
element.clean
-
3811
elsif element.is_a?(Porolog::Tail)
-
7
Porolog::UNKNOWN_TAIL
-
3804
elsif element.is_a?(Porolog::Variable)
-
439
nil
-
else
-
3365
element.value
-
end
-
}
-
end
-
-
# @return [Symbol] the type of the object (for an Array, should be :array)
-
1
def type
-
339960
:array
-
end
-
-
# @param other [Object] the Object which is to be the tail
-
# @return [Array] an Array with the Object being the head and the other Object being the tail.
-
1
def /(other)
-
116
if other.is_a?(Porolog::Variable) || other.is_a?(Symbol)
-
110
self + [Porolog::Tail.new(other)]
-
else
-
6
self + [*other]
-
end
-
end
-
-
# @param head_size [Integer] specifies the size of the head
-
# @return [Object] the head of the Array
-
1
def head(head_size = 1)
-
13629
if head_size == 1
-
13621
if first == Porolog::UNKNOWN_TAIL
-
nil
-
else
-
13619
first
-
end
-
else
-
8
self[0...head_size]
-
end
-
end
-
-
# @param head_size [Integer] specifies the size of the head
-
# @return [Object] the tail of the Array
-
1
def tail(head_size = 1)
-
25466
if last == Porolog::UNKNOWN_TAIL
-
24876
[*self[head_size..-2], Porolog::UNKNOWN_TAIL]
-
else
-
590
[*self[head_size..-1]]
-
end
-
end
-
-
# @return [Boolean] whether the Object is an Array with a head and a tail.
-
1
def headtail?
-
124471
length == 2 && (last.is_a?(Porolog::Tail) || last == Porolog::UNKNOWN_TAIL)
-
end
-
-
# @return [Porolog::Goal] the goal that is most likely to be the goal for this array.
-
1
def goal
-
166180
map{|element| element.goal if element.respond_to?(:goal) }.flatten.find{|goal| goal.is_a?(Porolog::Goal) }
-
end
-
-
end
-
#
-
# lib/porolog/error.rb - Plain Old Ruby Objects Prolog Engine -- Error
-
#
-
# Luis Esteban 2 May 2018
-
# created
-
#
-
-
1
module Porolog
-
-
# Error class to enable rescuing or detecting any Porolog error.
-
#
-
# @author Luis Esteban
-
#
-
1
class PorologError < RuntimeError ; end
-
-
# Error indicating that a Goal is needed but could not be found.
-
1
class NoGoalError < PorologError ; end
-
-
# Error indicating that a Variable is needed but was not provided.
-
1
class NonVariableError < PorologError ; end
-
-
end
-
#
-
# lib/porolog/goal.rb - Plain Old Ruby Objects Prolog Engine -- Goal
-
#
-
# Luis Esteban 2 May 2018
-
# created
-
#
-
-
1
module Porolog
-
-
# A Porolog::Goal finds solutions for specific Arguments of a Predicate.
-
#
-
# @author Luis Esteban
-
#
-
# @!attribute calling_goal
-
# @return [Porolog::Goal, nil] The parent goal if this goal is a subgoal, otherwise nil.
-
# @!attribute arguments
-
# @return [Porolog::Arguments] The specific Arguments this goal is trying to solve.
-
# @!attribute index
-
# @return [Integer, nil] Solution index for builtin predicates.
-
# @!attribute result
-
# @return [Boolean] Whether any result has been found.
-
# @!attribute description
-
# @return [String] Description of the Arguments the Goal is attempting to solve.
-
# @!attribute log
-
# @return [Array<String>] Log of events, namely unification failures.
-
#
-
1
class Goal
-
-
# Error class for rescuing or detecting any Goal error.
-
1
class Error < PorologError ; end
-
# Error class indicating an unexpected type of Rule definition.
-
1
class DefinitionError < Error ; end
-
# Error class indicating a non-Variable was attempted to be instantiated.
-
1
class NotVariableError < Error ; end
-
-
# Clears all goals and ensures they are deleted.
-
# @return [Boolean] success
-
1
def self.reset
-
724
@@goals ||= []
-
724
goals = @@goals
-
724
@@goals = []
-
724
@@goal_count = 0
-
724
goals.map(&:deleted?)
-
724
true
-
end
-
-
# @return [Integer] the number of goals created
-
1
def self.goal_count
-
9
@@goal_count
-
end
-
-
1
reset
-
-
1
attr_accessor :calling_goal, :arguments, :index, :result, :description, :log
-
-
# Initializes and registers the Goal.
-
# @param arguments [Porolog::Arguments] the Predicate Arguments that this Goal is to solve.
-
# @param calling_goal [Porolog::Goal] the parent Goal if this Goal is a subgoal.
-
1
def initialize(arguments, calling_goal = nil)
-
7398
@@goals << self
-
7398
@@goal_count += 1
-
-
7398
@arguments = arguments
-
7398
@terminate = false
-
7398
@calling_goal = calling_goal
-
7398
@variables = {}
-
7398
@values = {}
-
7398
@result = nil
-
7398
@description = "Solve #{arguments.inspect}"
-
7398
@log = []
-
-
7398
@arguments = @arguments.dup(self) if @arguments.is_a?(Arguments)
-
7398
@solutions = []
-
end
-
-
# @return [Array<Porolog::Goal>] the calling chain for this goal.
-
1
def ancestors
-
377
ancestors = []
-
377
goal = self
-
-
377
while goal
-
448
ancestors << goal
-
448
goal = goal.calling_goal
-
end
-
-
377
ancestors.reverse
-
end
-
-
# A convenience method for testing/debugging.
-
# @return [String] the calling chain for this goal as a String.
-
1
def ancestry
-
373
indent = -1
-
373
ancestors.map do |goal|
-
438
indent += 1
-
438
"#{' ' * indent}#{goal.myid} -- #{goal.description} #{goal.variables.inspect}"
-
end.join("\n")
-
end
-
-
# A convenience method for testing/debugging.
-
# @return [String] the id of the goal.
-
1
def myid
-
9715
"Goal#{(@@goals.index(self) || -1000) + 1}"
-
end
-
-
# @return [String] pretty representation.
-
1
def inspect
-
1458
"#{myid} -- #{description}"
-
end
-
-
# A convenience method for testing/debugging.
-
# @return [Array<Porolog::Goal>]
-
1
def self.goals
-
10
@@goals
-
end
-
-
# Delete the Goal
-
# @return [Boolean] whether the Goal has been deleted (memoized)
-
1
def delete!
-
6237
@@goals.delete(self)
-
6237
deleted?
-
end
-
-
# @return [Boolean] whether the Goal has been deleted (memoized)
-
1
def deleted?
-
7416
@deleted ||= check_deleted
-
7416
@deleted
-
end
-
-
# Determines whether the Goal has been deleted and removes its Variables and Values if it has.
-
# @return [Boolean] whether the Goal has been deleted
-
1
def check_deleted
-
7408
return false if @@goals.include?(self)
-
-
7393
@variables.delete_if do |_name,variable|
-
31381
variable.remove
-
31381
true
-
end
-
-
7393
@values.delete_if do |_name,value|
-
8383
value.remove
-
8383
true
-
end
-
-
7393
true
-
end
-
-
# Sets that the goal should no longer search any further rules for the current predicate
-
# once the current rule has finished being evaluated.
-
# This method implements the :CUT rule.
-
# That is, backtracking does not go back past the point of the cut.
-
# @return [Boolean] true.
-
1
def terminate!
-
410
@log << 'terminating'
-
410
@terminate = true
-
end
-
-
# @return [Boolean] whether the goal has been cut.
-
1
def terminated?
-
7402
@terminate
-
end
-
-
# Converts all embedded Symbols to Variables within this Goal.
-
# @param object [Object] the object to variablise.
-
# @return [Object] the variablised object.
-
1
def variablise(object)
-
94030
case object
-
when Symbol,Variable
-
21464
variable(object)
-
when Array
-
73493
object.map{|element| variablise(element) }
-
when Arguments
-
4
object.dup(self)
-
when Tail
-
1004
Tail.new variablise(object.value)
-
when Value
-
18922
object
-
when NilClass
-
nil
-
else
-
9316
if [UNKNOWN_TAIL, UNKNOWN_ARRAY].include?(object)
-
2797
object
-
else
-
6519
value(object)
-
end
-
end
-
end
-
-
1
alias [] variablise
-
-
# @return [Hash{Symbol => Object}] the Variables and their current values of this Goal
-
1
def variables
-
5033
@variables.keys.each_with_object({}){|variable,variable_list|
-
24663
value = value_of(variable).value.value
-
24663
if value.is_a?(Variable)
-
6026
variable_list[variable] = nil
-
18637
elsif value.is_a?(Array)
-
1578
variable_list[variable] = value.clean
-
else
-
17059
variable_list[variable] = value
-
end
-
}
-
end
-
-
# A convenience method for testing/debugging.
-
# @return [String] a tree representation of all the instantiations of this goal's variables.
-
1
def inspect_variables
-
525
@variables.values.map(&:inspect_with_instantiations).join("\n")
-
end
-
-
# A convenience method for testing/debugging.
-
# @return [Array<Object>] the values instantiated for this goal.
-
1
def values
-
4
@values.values.map(&:value)
-
end
-
-
# Finds or tries to create a variable in the goal (as much as possible) otherwise passes the parameter back.
-
# @param name [Object] name of variable or variable
-
# @return [Porolog::Variable, Object] a variable of the goal or the original parameter
-
1
def variable(name)
-
251897
return name unless name.is_a?(Symbol) || name.is_a?(Variable)
-
-
248460
if name.is_a?(Variable)
-
114650
variable = name
-
114650
@variables[variable.name] = variable if variable.goal == self
-
114650
return variable
-
end
-
-
133810
@variables[name] ||= (name.is_a?(Variable) && name || Variable.new(name, self))
-
133810
@variables[name]
-
end
-
-
# @param value [Object] the value that needs to be associated with this Goal.
-
# @return [Porolog::Value] a Value, ensuring it has been associated with this Goal so that it can be uninstantiated.
-
1
def value(value)
-
17419
@values[value] ||= Value.new(value, self)
-
17419
@values[value]
-
end
-
-
# Returns the value of the indicated variable or value
-
# @param name [Symbol, Object] name of the variable or value
-
# @param index [Object] index is not yet used
-
# @param visited [Array] used to avoid infinite recursion
-
# @return [Object] the value
-
1
def value_of(name, index = nil, visited = [])
-
140314
variable(name).value(visited)
-
end
-
-
# Returns the values of an Object. For most objects, this will basically just be the object.
-
# For Arrays, an Array will be returned where the elements are the value of the elements.
-
# @param object [Object] the Object to find the values of.
-
# @return [Object,Array<Object>] the value(s) of the object.
-
1
def values_of(object)
-
16
case object
-
when Array
-
12
object.map{|element| values_of(element) }
-
when Variable,Symbol,Value
-
5
value_of(object)
-
when Tail
-
# TODO: This needs to splat outwards; in the meantime, it splats just the first.
-
2
value_of(object.value).first
-
else
-
6
object
-
end
-
end
-
-
# Finds all possible solutions to the Predicate for the specific Arguments.
-
# @param max_solutions [Integer] if provided, the search is stopped once the number of solutions has been found
-
# @return [Array<Hash{Symbol => Object}>]
-
1
def solve(max_solutions = nil)
-
236
return @solutions unless @arguments
-
-
236
predicate = @arguments.predicate
-
-
236
predicate&.satisfy(self) do |goal|
-
# TODO: Refactor to overrideable method (or another solution, say a lambda)
-
-
369
@solutions << variables
-
369
@log << "SOLUTION: #{variables}"
-
369
@log << goal.ancestry
-
369
@log << goal.inspect_variables
-
-
369
return @solutions if max_solutions && @solutions.size >= max_solutions
-
end
-
-
230
@solutions
-
end
-
-
# Solves the goal but not as the root goal for a query.
-
# That is, solves an intermediate goal.
-
# @param block [Proc] code to perform when the Goal is satisfied.
-
# @return [Boolean] whether the definition was satisfied.
-
1
def satisfy(&block)
-
3701
return false unless @arguments
-
-
3700
predicate = @arguments.predicate
-
-
3700
satisfied = false
-
3700
predicate&.satisfy(self) do |subgoal|
-
3284
subgoal_satisfied = block.call(subgoal)
-
3250
satisfied ||= subgoal_satisfied
-
end
-
3665
satisfied
-
end
-
-
# Instantiates a Variable to another Variable or Value, for this Goal.
-
# @param name [Symbol,Porolog::Variable,Object] the name that references the variable to be instantiated.
-
# @param other [Object] the value that the variable is to be instantiated to.
-
# @param other_goal [Porolog::Goal,nil] the Goal of the other value.
-
# @return [Porolog::Instantiation] the instantiation.
-
1
def instantiate(name, other, other_goal = self)
-
27923
variable = self.variable(name)
-
-
27923
other = if other.type == :variable
-
23958
other_goal.variable(other)
-
else
-
3965
other_goal.value(other)
-
end
-
-
27923
variable.instantiate(other)
-
end
-
-
# Inherits variables and their instantiations from another goal.
-
# @param other_goal [Porolog::Goal,nil] the Goal to inherit variables from.
-
# @return [Boolean] whether the variables could be inherited (unified).
-
1
def inherit_variables(other_goal = @calling_goal)
-
3701
return true unless other_goal
-
-
3700
unified = true
-
3700
unifications = []
-
variables = (
-
3700
other_goal.arguments.variables +
-
other_goal.variables.keys +
-
self.arguments.variables
-
).map(&:to_sym).uniq
-
-
3700
variables.each do |variable|
-
23361
name = variable
-
-
23361
unification = Porolog::unify(name, name, other_goal, self)
-
23361
unified &&= !!unification
-
23361
if unified
-
23359
unifications += unification
-
else
-
skipped
#:nocov:
-
skipped
self.log << "Couldn't unify: #{name.inspect} WITH #{other_goal.myid} AND #{self.myid}"
-
skipped
break
-
skipped
#:nocov:
-
end
-
end
-
3700
unified &&= Porolog::instantiate_unifications(unifications) if unified
-
-
3700
unified
-
end
-
-
end
-
-
end
-
#
-
# lib/porolog/instantiation.rb - Plain Old Ruby Objects Prolog Engine -- Instantiation
-
#
-
# Luis Esteban 2 May 2018
-
# created
-
#
-
-
1
module Porolog
-
-
# A Porolog::Instantiation implements an instantiation of a Variable of a Goal.
-
# An Instantiation joins two expressions. At least one expression must have a variable.
-
# An index may be provided to index into the value of either expression, or both may have an index.
-
# Thus, an element of each list could be instantiated without reference to the other elements:
-
# x = [a,b,c,d,e]
-
# |
-
# y = [p,q,r,s]
-
#
-
# @author Luis Esteban
-
#
-
# @!attribute variable1
-
# @return [Variable,Object] one end of the instantiation.
-
# @!attribute variable2
-
# @return [Variable,Object] the other end of the instantiation.
-
# @!attribute index1
-
# @return [Object,nil] the index into the value of variable1.
-
# @!attribute index2
-
# @return [Object,nil] the index into the value of variable2.
-
#
-
1
class Instantiation
-
-
# Error class for rescuing or detecting any Instantiation error.
-
1
class Error < PorologError ; end
-
# Error class indicating that an instantiation was created without any Variables.
-
1
class NoVariableError < Error ; end
-
# Error class indicating that an index could not be used although requested.
-
1
class UnhandledIndexError < Error ; end
-
-
1
attr_accessor :variable1, :variable2, :index1, :index2
-
-
# Clears all instantiations.
-
# @return [Boolean] success
-
1
def self.reset
-
719
@@instantiations = {}
-
719
true
-
end
-
-
1
reset
-
-
# @return [Hash{Array => Porolog::Instantiation}] all Instantiations
-
1
def self.instantiations
-
16
@@instantiations
-
end
-
-
# Finds or creates a new instantiation. At least one of the variables must be a Variable.
-
# @param variable1 [Porolog::Variable,Porolog::Value,Object] one end of the instantiation to be made.
-
# @param variable2 [Porolog::Variable,Porolog::Value,Object] the other end of the instantiation to be made.
-
# @param index1 [Integer,Symbol,nil] index into the value of variable1.
-
# @param index2 [Integer,Symbol,nil] index into the value of variable2.
-
# @return [Porolog::Instantiation] the found or created Instantiation.
-
1
def self.new(variable1, index1, variable2, index2)
-
29973
raise NoVariableError, "Cannot instantiate non-variables: #{variable1.inspect} and #{variable2.inspect}" unless variable1.is_a?(Variable) || variable2.is_a?(Variable)
-
-
29973
variable1 = Value.new variable1, variable2.goal unless variable1.is_a?(Variable) || variable1.is_a?(Value)
-
29973
variable2 = Value.new variable2, variable1.goal unless variable2.is_a?(Variable) || variable2.is_a?(Value)
-
-
instantiation = \
-
29973
@@instantiations[[variable1, index1, variable2, index2]] ||
-
@@instantiations[[variable2, index2, variable1, index1]] ||
-
super
-
-
29973
instantiation
-
end
-
-
# Initializes and registers a new Instantiation. At least one of the variables must be a Variable.
-
# @param variable1 [Porolog::Variable,Porolog::Value,Object] one end of the instantiation to be made.
-
# @param variable2 [Porolog::Variable,Porolog::Value,Object] the other end of the instantiation to be made.
-
# @param index1 [Integer,Symbol,nil] index into the value of variable1.
-
# @param index2 [Integer,Symbol,nil] index into the value of variable2.
-
# @return [Porolog::Instantiation] the found or created Instantiation.
-
1
def initialize(variable1, index1, variable2, index2)
-
29930
@variable1 = variable1
-
29930
@variable2 = variable2
-
29930
@index1 = index1
-
29930
@index2 = index2
-
-
29930
@variable1.instantiations << self
-
29930
@variable2.instantiations << self
-
-
29930
@@instantiations[signature] = self
-
end
-
-
# @return [Array] the signature of the Instantiation, which aids in avoiding duplication instantiations.
-
1
def signature
-
59859
@signature ||= [@variable1, @index1, @variable2, @index2].freeze
-
59859
@signature
-
end
-
-
# @return [String] pretty representation.
-
1
def inspect(indent = 0)
-
12
index1 = @index1 ? "[#{@index1.inspect}]" : ''
-
12
index2 = @index2 ? "[#{@index2.inspect}]" : ''
-
-
12
"#{' ' * indent}#{@variable1.inspect}#{index1} = #{@variable2.inspect}#{index2}"
-
end
-
-
# Removes itself from its variables' Instantiations and unregisters itself.
-
# @return [Boolean] success
-
1
def remove
-
29922
@variable1.instantiations.delete(self) if @variable1.is_a?(Variable) || @variable1.is_a?(Value)
-
29922
@variable2.instantiations.delete(self) if @variable2.is_a?(Variable) || @variable2.is_a?(Value)
-
-
29922
@deleted = true
-
29922
@@instantiations.delete(signature)
-
29922
@deleted
-
end
-
-
# Returns the Goal of the other Variable to the one provided.
-
# @param variable [Porolog::Variable] the provided Variable.
-
# @return [Porolog::Goal,nil] the Goal of the other Variable.
-
1
def other_goal_to(variable)
-
31
return nil unless variables.include?(variable)
-
-
29
other_variable = (variables - [variable]).first
-
29
other_variable&.goal
-
end
-
-
# @return [Array<Porolog::Goal>] the Goals of the Variables of the Instantiation.
-
1
def goals
-
2
[@variable1.goal,@variable2.goal]
-
end
-
-
# @return [Array<Porolog::Variable,Object>] the Variables of the Instantiation.
-
1
def variables
-
62
[@variable1,@variable2]
-
end
-
-
# @param visited [Array] prevents infinite recursion.
-
# @return [Array] the values of the Variables of the Instantiation.
-
1
def values(visited = [])
-
20
return [] if visited.include?(self)
-
-
20
values_for_variable1 = values_for(@variable1, visited)
-
20
values_for_variable2 = values_for(@variable2, visited)
-
-
20
(values_for_variable1 + values_for_variable2).uniq
-
end
-
-
# @param value [Object] the provided value.
-
# @param index [Integer,Symbol,Array<Integer>] the index.
-
# @param visited [Array] prevents infinite recursion.
-
# @return [Object] the indexed value of the provided value.
-
1
def value_indexed(value, index, visited = [])
-
1465772
return nil unless value
-
1395255
return nil if value.type == :variable
-
830150
return nil if value == [] && index == :tail
-
-
830149
visit = [value, index]
-
830149
return nil if visited.include?(visit)
-
830149
visited = visited + [visit]
-
-
830149
case index
-
when Integer
-
45
if value.respond_to?(:last) && value.last == UNKNOWN_TAIL
-
2
if index >= value.length - 1
-
1
UNKNOWN_TAIL
-
else
-
1
value[index]
-
end
-
43
elsif value.is_a?(Numeric)
-
2
value
-
else
-
41
value[index]
-
end
-
-
when Symbol
-
39051
value_value = value.value(visited)
-
39051
if index == :flathead
-
# value_value = [..., 4, 5, 6]
-
14
if value_value.first == UNKNOWN_TAIL
-
14
UNKNOWN_ARRAY
-
else
-
nil
-
end
-
39037
elsif index == :flattail
-
# value_value = [1, 2, 3, ...]
-
11
if value_value.first == UNKNOWN_TAIL
-
nil
-
11
elsif value_value.last == UNKNOWN_TAIL
-
11
UNKNOWN_ARRAY
-
end
-
39026
elsif value_value.respond_to?(index)
-
39025
value_value.send(index)
-
else
-
1
value
-
end
-
-
when Array
-
5
if index.empty?
-
1
value[1..-1]
-
else
-
4
value[0...index.first]
-
end
-
-
else
-
791048
if index
-
4
raise UnhandledIndexError, "Unhandled index: #{index.inspect} of #{value.inspect}"
-
else
-
791044
value
-
end
-
end
-
end
-
-
# @param variable [Porolog::Variable,Porolog::Value] the specified variable (or end of the Instantiation).
-
# @param visited [Array] prevents infinite recursion.
-
# @return [Array<Object>] the values for the specified variable by finding the values of the other variable (i.e. the other end) and applying any indexes.
-
1
def values_for(variable, visited = [])
-
2615476
return [] if visited.include?(self)
-
1465747
visited = visited + [self]
-
-
1465747
if variable == @variable1
-
587530
if @index1 == :flathead
-
45
flathead = value_at_index(value_indexed(@variable2.value(visited), @index2, visited), @index1).value.value
-
45
if flathead
-
23
return [[*flathead]]
-
else
-
22
return []
-
end
-
end
-
587485
if @index1 == :flattail
-
52
flattail = value_at_index(value_indexed(@variable2.value(visited), @index2, visited), @index1).value.value
-
52
if flattail
-
26
return [[UNKNOWN_TAIL, *flattail]]
-
else
-
26
return []
-
end
-
end
-
587433
if @index1
-
64
[value_at_index(value_indexed(@variable2.value(visited), @index2, visited), @index1)]
-
else
-
587369
if @variable2.is_a?(Variable)
-
341700
[value_indexed(@variable2.value(visited), @index2, visited)]
-
else
-
245669
[value_indexed(@variable2, @index2, visited)]
-
end
-
end
-
878217
elsif variable == @variable2
-
878209
if @index2 == :flathead
-
2
flathead = value_at_index(value_indexed(@variable1.value(visited), @index1, visited), @index2).value.value
-
2
if flathead
-
1
return [[*flathead]]
-
else
-
1
return []
-
end
-
end
-
878207
if @index2 == :flattail
-
2
flattail = value_at_index(value_indexed(@variable1.value(visited), @index1, visited), @index2).value.value
-
2
if flattail
-
1
return [[UNKNOWN_TAIL, *flattail]]
-
else
-
1
return []
-
end
-
end
-
878205
if @index2
-
45393
[value_at_index(value_indexed(@variable1.value(visited), @index1, visited), @index2)]
-
else
-
832812
if @variable1.is_a?(Variable)
-
832811
[value_indexed(@variable1.value(visited), @index1, visited)]
-
else
-
1
[value_indexed(@variable1, @index1, visited)]
-
end
-
end
-
else
-
8
[]
-
end.compact
-
end
-
-
# @param value [Object] the known value.
-
# @param index [Integer,Symbol,Array] the known index.
-
# @return [Array] an Array where the known value is at the known index.
-
1
def value_at_index(value, index)
-
45570
value && case index
-
when Integer
-
3
result = []
-
3
result[index] = value
-
3
result << UNKNOWN_TAIL
-
3
result
-
-
when Symbol
-
43162
case index
-
when :flathead
-
24
[*value, UNKNOWN_TAIL]
-
when :head
-
27617
[value, UNKNOWN_TAIL]
-
when :tail
-
15493
[nil, *value]
-
when :flattail
-
27
value
-
else
-
1
raise UnhandledIndexError, "Unhandled index: #{index.inspect} for #{value.inspect}"
-
end
-
-
when Array
-
4
if index.empty?
-
1
[nil, *value]
-
else
-
3
[value, UNKNOWN_TAIL]
-
end
-
-
else
-
2
if index
-
1
raise UnhandledIndexError, "Unhandled index: #{index.inspect} for #{value.inspect}"
-
else
-
1
value
-
end
-
end
-
end
-
-
# @param variable [Porolog::Variable,Porolog::Value] the specified variable.
-
# @return [Boolean] whether the specified variable is not indexed.
-
1
def without_index_on?(variable)
-
[
-
9
[@variable1,@index1],
-
[@variable2,@index2],
-
].any?{|pair|
-
16
pair.first == variable && pair.last.nil?
-
}
-
end
-
-
# @return [Boolean] whether the Instantiation has been deleted (memoized).
-
1
def deleted?
-
8
@deleted ||= @variable1.goal.deleted? || @variable2.goal.deleted?
-
8
@deleted
-
end
-
-
# @param goal [Porolog::Goal] the provided Goal.
-
# @return [Boolean] whether the Instantiation attaches to a variable in the provided Goal.
-
1
def belongs_to?(goal)
-
[
-
6
@variable1.goal,
-
@variable2.goal,
-
].include?(goal)
-
end
-
-
end
-
-
end
-
#
-
# lib/porolog/predicate.rb - Plain Old Ruby Objects Prolog Engine -- Predicate
-
#
-
# Luis Esteban 2 May 2018
-
# created
-
#
-
-
1
module Porolog
-
-
# A Porolog::Predicate corresponds to a Prolog predicate.
-
# It contains rules (Porolog::Rule) and belongs to a Porolog::Scope.
-
# When provided with arguments, it produces a Porolog::Arguments.
-
#
-
# @author Luis Esteban
-
#
-
# @!attribute [r] name
-
# Name of the Predicate.
-
#
-
# @!attribute [r] rules
-
# Rules of the Predicate.
-
#
-
1
class Predicate
-
-
# Error class for rescuing or detecting any Predicate error.
-
1
class Error < PorologError ; end
-
# Error class indicating an error with the name of a Predicate.
-
1
class NameError < Error ; end
-
-
1
attr_reader :name, :rules
-
-
# A unique value used to verify instantiations.
-
1
UNIQUE_VALUE = Object.new.freeze
-
1
private_constant :UNIQUE_VALUE
-
-
# Returns the current scope, or sets the current scope if a paramter is provided
-
# (creating the new scope if necessary).
-
# @param scope_name [Object] the name (or otherwise object) used to register a scope.
-
# @return [Porolog::Scope] the current Scope.
-
1
def self.scope(scope_name = nil)
-
3066
if scope_name
-
727
@@current_scope = Scope[scope_name] || Scope.new(scope_name)
-
else
-
2339
@@current_scope
-
end
-
end
-
-
# Sets the current scope (creating the new scope if necessary.
-
# @param scope_name [Object] the name (or otherwise object) used to register a scope.
-
# @return [Porolog::Scope] the current Scope.
-
1
def self.scope=(scope_name)
-
4
@@current_scope = Scope[scope_name] || Scope.new(scope_name) if scope_name
-
4
@@current_scope
-
end
-
-
# Resets the current scope to default and deregisters builtin predicates.
-
# @return [Porolog::Scope] the current Scope.
-
1
def self.reset
-
720
scope(:default)
-
720
@builtin_predicate_ids = {}
-
end
-
-
# Looks up a Predicate in the current scope by its name.
-
# @param name [Object] the name (or otherwise object) used to register a scope.
-
# @return [Porolog::Predicate] the Predicate with the given name.
-
1
def self.[](name)
-
2
@@current_scope[name]
-
end
-
-
1
reset
-
-
# Initializes a Porolog::Predicate and registers it by its name.
-
# @param name [#to_sym] the input object to read from
-
# @param scope_name the name of the scope in which to register the Predicate; if omitted, defaults to the name of the current scope
-
1
def initialize(name, scope_name = Predicate.scope.name, builtin: false)
-
1165
@name = name.to_sym
-
1165
@rules = []
-
1165
@builtin = builtin
-
-
1165
raise NameError, "Cannot name a predicate 'predicate'" if @name == :predicate
-
-
1161
scope = Scope[scope_name] || Scope.new(scope_name)
-
1161
scope[@name] = self
-
end
-
-
# Creates a new Predicate, or returns an existing Predicate if one already exists with the given name in the current scope.
-
# @param args the args to be passed to initialize, first of which is the name of the predicate.
-
# @return [Porolog::Predicate] a new or existing Predicate.
-
1
def self.new(*args, **)
-
1172
scope[args.first.to_sym] || super
-
end
-
-
# Create Arguments for the Predicate.
-
# Provides the syntax option:
-
# p.(x,y,z)
-
# for
-
# p.arguments(x,y,z)
-
# @return [Porolog::Arguments] Arguments of the Predicate with the given arguments.
-
1
def call(*args, &block)
-
619
Arguments.new(self, args, &block)
-
end
-
-
# Create Arguments for the Predicate.
-
# @param args the args of the Predicate.
-
# @param block [Proc,nil] the block to be called when satisfying the Predicate.
-
# @return [Porolog::Arguments] Arguments of the Predicate with the given arguments.
-
1
def arguments(*args, &block)
-
743
Arguments.new(self, args, &block)
-
end
-
-
# Add a Rule to the Predicate.
-
# @param rule [Object] a rule to add to the Predicate.
-
# @return [Porolog::Predicate] the Predicate.
-
1
def <<(rule)
-
160
@rules << rule
-
160
self
-
end
-
-
# A pretty print String of the Predicate.
-
# @return [String] the Predicate as a String.
-
1
def inspect
-
5
if @rules.size == 1
-
3
"#{@name}:-#{@rules.first.inspect}"
-
else
-
2
@rules.each_with_object(["#{@name}:-"]) do |rule, lines|
-
2
lines << rule.inspect
-
end.join("\n")
-
end
-
end
-
-
# Satisfy the Predicate within the supplied Goal.
-
# Satisfy of each rule of the Predicate is called with the Goal and success block.
-
# @param goal [Porolog::Goal] the Goal within which to satisfy the Predicate.
-
# @param block [Proc] the block to be called each time a Rule of the Predicate is satisfied.
-
# @return [Boolean] whether any Rule was satisfied.
-
1
def satisfy(goal, &block)
-
3934
if builtin?
-
2497
satisfy_builtin(goal, &block)
-
else
-
1437
satisfied = false
-
1437
@rules.each do |rule|
-
2588
rule.satisfy(goal) do |subgoal|
-
1464
satisfied = true
-
1464
block.call(subgoal)
-
end
-
2574
break if goal.terminated?
-
end
-
1423
satisfied
-
end
-
end
-
-
# @return [Boolean] whether the Predicate is a builtin predicate.
-
1
def builtin?
-
3936
@builtin
-
end
-
-
# Satisfy the builtin Predicate within the supplied Goal.
-
# Call the builtin Predicate method with the Goal and success block.
-
# The arguments and block are extracted from the Goal's Arguments.
-
# @param goal [Porolog::Goal] the Goal within which to satisfy the Predicate.
-
# @param block [Proc] the block to be called each time a Rule of the Predicate is satisfied.
-
# @return [Boolean] whether any Rule was satisfied.
-
1
def satisfy_builtin(goal, &block)
-
2498
predicate = goal.arguments.predicate.name
-
2498
arguments = goal.arguments.arguments
-
2498
arg_block = goal.arguments.block
-
-
2498
Predicate.call_builtin(predicate, goal, block, *arguments, &arg_block)
-
end
-
-
# Call a builtin predicate
-
# @param predicate [Symbol] the name of the predicate to call.
-
# @param args [Array<Object>] arguments for the predicate call.
-
# @param arg_block [Proc] the block provided in the Arguments of the Predicate.
-
# @return [Boolean] whether the predicate was satisfied.
-
1
def self.call_builtin(predicate, *args, &arg_block)
-
2561
Porolog::Predicate::Builtin.instance_method(predicate).bind(self).call(*args, &arg_block)
-
end
-
-
end
-
-
end
-
#
-
# lib/porolog/predicate/builtin.rb - Plain Old Ruby Objects Prolog Engine -- Builtin Predicates
-
#
-
# Luis Esteban 29 July 2020
-
# created
-
#
-
-
1
module Porolog
-
-
1
class Predicate
-
-
# The Porolog::Predicate::Builtin module is a collection of the implementations of the builtin predicates.
-
# It is possible to define custom builtin predicates. Each builtin requires a goal and a block,
-
# and should return the result of
-
# block.call(goal) || false
-
# if the predicate is deemed successful; otherwise, it should return false.
-
#
-
# @author Luis Esteban
-
#
-
1
module Builtin
-
-
# Corresponds to the standard Prolog print predicate.
-
# `print` could not be used because of the clash with the Ruby method.
-
# It outputs all arguments. If an argument is a variable,
-
# then if it is instantiated, its value is output; otherwise its name is output.
-
# If the value is an Array, its inspect is output instead.
-
# Use:
-
# write('X = ', :X, ', Y = ', :Y, "\n")
-
# @param goal [Porolog::Goal] the Goal to satisfy the Predicate.
-
# @param block [Proc] the continuation of solving to call if this Predicate is satisfied.
-
# @param args [Array<Object>] the arguments to be passed to the provided block.
-
# @return [Boolean] whether the goal was satisfied.
-
1
def write(goal, block, *args)
-
49
args = args.map(&:value).map(&:value)
-
49
args = args.map{|arg|
-
157
arg.is_a?(Array) ? arg.inspect : arg
-
}
-
49
args = args.map{|arg|
-
157
arg.type == :variable ? arg.to_sym.inspect : arg
-
}
-
49
$stdout.print args.join
-
49
block.call(goal) || false
-
end
-
-
# Corresponds to the standard Prolog print and nl predicate.
-
# `print` could not be used because of the clash with the Ruby method.
-
# It outputs all arguments and a new line. If an argument is a variable,
-
# then if it is instantiated, its value is output; otherwise its name is output.
-
# If the value is an Array, its inspect is output instead.
-
# Use:
-
# writenl('X = ', :X, ', Y = ', :Y)
-
# @param goal [Porolog::Goal] the Goal to satisfy the Predicate.
-
# @param block [Proc] the continuation of solving to call if this Predicate is satisfied.
-
# @param args [Array<Object>] the arguments to be passed to the provided block.
-
# @return [Boolean] whether the goal was satisfied.
-
1
def writenl(goal, block, *args)
-
4
args = args.map(&:value).map(&:value)
-
4
args = args.map{|arg|
-
4
arg.is_a?(Array) ? arg.inspect : arg
-
}
-
4
args = args.map{|arg|
-
4
arg.type == :variable ? arg.to_sym.inspect : arg
-
}
-
4
$stdout.puts args.join
-
4
block.call(goal) || false
-
end
-
-
# Corresponds to the standard Prolog nl predicate.
-
# It outputs a newline.
-
# Use:
-
# nl
-
# @param goal [Porolog::Goal] the Goal to satisfy the Predicate.
-
# @param block [Proc] the continuation of solving to call if this Predicate is satisfied.
-
# @return [Boolean] whether the goal was satisfied.
-
1
def nl(goal, block)
-
3
$stdout.puts
-
3
block.call(goal) || false
-
end
-
-
# Corresponds to the standard Prolog is predicate.
-
# It instantiates a Variable with the result of the provided block.
-
# Use:
-
# is(:Y, :X) {|x| x + 1 }
-
# is(:name, :first, :last) {|first, last| [first, last] }
-
# is(:name, :first, :last) {|first, last| "#{first} #{last}" }
-
# @param goal [Porolog::Goal] the Goal to satisfy the Predicate.
-
# @param block [Proc] the continuation of solving to call if this Predicate is satisfied.
-
# @param variable [Porolog::Variable] the Variable to be instantiated.
-
# @param args [Array<Object>] the arguments to be passed to the provided block.
-
# @param is_block [Proc] the block provided in the Goal's Arguments.
-
# @return [Boolean] whether the goal was satisfied.
-
1
def is(goal, block, variable, *args, &is_block)
-
1167
raise NonVariableError, "#{variable.inspect} is not a variable" unless variable.type == :variable
-
-
1166
result = is_block.call(*args.map(&:value).map(&:value))
-
-
1166
result && !!variable.instantiate(result) && block.call(goal) || false
-
end
-
-
# Corresponds to the standard Prolog var predicate.
-
# It is satisfied if the argument is an uninstantiated variable.
-
# Use:
-
# var(:X)
-
# @param goal [Porolog::Goal] the Goal to satisfy the Predicate.
-
# @param block [Proc] the continuation of solving to call if this Predicate is satisfied.
-
# @param variable [Porolog::Variable,Object] the argument to be tested.
-
# @return [Boolean] whether the goal was satisfied.
-
1
def var(goal, block, variable)
-
4
variable.value.value.type == :variable && block.call(goal) || false
-
end
-
-
# Corresponds to the standard Prolog nonvar predicate.
-
# It is satisfied if the argument is not an uninstantiated variable.
-
# Use:
-
# nonvar(:X)
-
# @param goal [Porolog::Goal] the Goal to satisfy the Predicate.
-
# @param block [Proc] the continuation of solving to call if this Predicate is satisfied.
-
# @param variable [Porolog::Variable,Object] the argument to be tested.
-
# @return [Boolean] whether the goal was satisfied.
-
1
def nonvar(goal, block, variable)
-
4
variable.value.value.type != :variable && block.call(goal) || false
-
end
-
-
# Corresponds to the standard Prolog atom predicate.
-
# It is satisfied if the argument is a String.
-
# Use:
-
# atom(:X)
-
# @param goal [Porolog::Goal] the Goal to satisfy the Predicate.
-
# @param block [Proc] the continuation of solving to call if this Predicate is satisfied.
-
# @param variable [Porolog::Variable,Object] the argument to be tested.
-
# @return [Boolean] whether the goal was satisfied.
-
1
def atom(goal, block, variable)
-
3
variable.value.value.is_a?(String) && block.call(goal) || false
-
end
-
-
# Corresponds to the standard Prolog atomic predicate.
-
# It is satisfied if the argument is a String or an Integer.
-
# Use:
-
# atomic(:X)
-
# @param goal [Porolog::Goal] the Goal to satisfy the Predicate.
-
# @param block [Proc] the continuation of solving to call if this Predicate is satisfied.
-
# @param variable [Porolog::Variable,Object] the argument to be tested.
-
# @return [Boolean] whether the goal was satisfied.
-
1
def atomic(goal, block, variable)
-
4
variable.value.value.type == :atomic && block.call(goal) || false
-
end
-
-
# Corresponds to the standard Prolog integer predicate.
-
# It is satisfied if the argument is an Integer.
-
# Use:
-
# integer(:X)
-
# @param goal [Porolog::Goal] the Goal to satisfy the Predicate.
-
# @param block [Proc] the continuation of solving to call if this Predicate is satisfied.
-
# @param variable [Porolog::Variable,Object] the argument to be tested.
-
# @return [Boolean] whether the goal was satisfied.
-
1
def integer(goal, block, variable)
-
4
variable.value.value.is_a?(Integer) && block.call(goal) || false
-
end
-
-
# Corresponds to the standard Prolog == predicate.
-
# It is satisfied if:
-
# - it is provided with two values (or instantiated Variables) that are equal, or
-
# - it is provided with two uninstantiaed Variables that are bound to each other.
-
# Variables are not instantiated; however, if they are uninstantiated, they are
-
# temporarily instantiated to a unique value to see if they are bound in some way.
-
# Use:
-
# eq(:X, :Y)
-
# eq(:name, ['Sam', 'Smith'])
-
# @param goal [Porolog::Goal] the Goal to satisfy the Predicate.
-
# @param block [Proc] the continuation of solving to call if this Predicate is satisfied.
-
# @param x [Object] the left hand side of the equality.
-
# @param y [Object] the right hand side of the equality.
-
# @return [Boolean] whether the goal was satisfied.
-
1
def eq(goal, block, x, y)
-
8
x = x.value.value
-
8
y = y.value.value
-
-
8
case [x.type, y.type]
-
when [:variable, :variable]
-
4
equal = false
-
4
temporary_instantiation = x.instantiate UNIQUE_VALUE
-
4
if temporary_instantiation
-
4
equal = y.value.value == UNIQUE_VALUE
-
4
temporary_instantiation.remove
-
end
-
4
equal
-
else
-
4
x == y
-
end && block.call(goal) || false
-
end
-
-
# Corresponds to a synthesis of the standard Prolog == and is predicates.
-
# The left hand side (i.e. the first parameter) must be a variable.
-
# It compares equality if the left hand side is instantiated;
-
# otherwise, it instantiates the left hand side to the right hand side.
-
# It is satisfied if:
-
# - the values are equal, or
-
# - the variable can successfully be instantiated to the right hand side.
-
# Use:
-
# is_eq(:X, :Y)
-
# is_eq(:name, ['Sam', 'Smith'])
-
# @param goal [Porolog::Goal] the Goal to satisfy the Predicate.
-
# @param block [Proc] the continuation of solving to call if this Predicate is satisfied.
-
# @param x [Porolog::Variable] the left hand side of the equality / assignment.
-
# @param y [Object] the right hand side of the equality / assignment.
-
# @return [Boolean] whether the goal was satisfied.
-
1
def is_eq(goal, block, x, y)
-
18
return false unless x.type == :variable
-
15
return block.call(goal) if x.value.value == y.value.value
-
-
14
!!x.instantiate(y) && block.call(goal) || false
-
end
-
-
# Allows a plain Ruby block to be executed as a goal.
-
# It is assumed to be successful unless evaluates to :fail .
-
# Use:
-
# ruby(:X, :Y, :Z) {|x, y, z| csv << [x, y, z] }
-
# ruby { $stdout.print '.' }
-
# ruby {
-
# $stdout.puts 'Forcing backtracking ...'
-
# :fail
-
# }
-
# @param goal [Porolog::Goal] the Goal to satisfy the Predicate.
-
# @param block [Proc] the continuation of solving to call if this Predicate is satisfied.
-
# @param args [Array<Object>] the arguments to be passed to the provided block.
-
# @param ruby_block [Proc] the block provided in the Goal's Arguments.
-
# @return [Boolean] whether the goal was satisfied.
-
1
def ruby(goal, block, *args, &ruby_block)
-
8
(ruby_block.call(goal, *args.map(&:value).map(&:value)) != :fail) && block.call(goal) || false
-
end
-
-
# Corresponds to the standard Prolog != predicate.
-
# It is satisfied if:
-
# - it is provided with two values (or instantiated Variables) that are unequal, or
-
# - it is provided with two uninstantiaed Variables that are not bound to each other.
-
# Variables are not instantiated; however, if they are uninstantiated, they are
-
# temporarily instantiated to a unique value to see if they are bound in some way.
-
# Use:
-
# noteq(:X, :Y)
-
# noteq(:name, ['Sam', 'Smith'])
-
# @param goal [Porolog::Goal] the Goal to satisfy the Predicate.
-
# @param block [Proc] the continuation of solving to call if this Predicate is satisfied.
-
# @param x [Object] the left hand side of the inequality.
-
# @param y [Object] the right hand side of the inequality.
-
# @return [Boolean] whether the goal was satisfied.
-
1
def noteq(goal, block, x, y)
-
538
x = x.value.value
-
538
y = y.value.value
-
-
538
case [x.type, y.type]
-
when [:variable, :variable]
-
4
equal = false
-
4
temporary_instantiation = x.instantiate UNIQUE_VALUE
-
4
if temporary_instantiation
-
4
equal = y.value.value == x.value.value
-
4
temporary_instantiation.remove
-
end
-
4
!equal
-
else
-
534
x != y
-
end && block.call(goal) || false
-
end
-
-
# This does not really correspond to a standard Prolog predicate.
-
# It implements a basic constraint mechanism.
-
# The Variable is instantiated (if possible) to all possible values provide
-
# except for all exclusions.
-
# Further, the exclusions are checked for collective uniqueness.
-
# Use:
-
# is_noteq(:digit, (0..9).to_a, :second_digit, :fifth_digit)
-
# @param goal [Porolog::Goal] the Goal to satisfy the Predicate.
-
# @param block [Proc] the continuation of solving to call if this Predicate is satisfied.
-
# @param variable [Porolog::Variable] the variable being instantiated.
-
# @param all_values [Array<Object>] all possible values (i.e. the domain) of the variable.
-
# @param exclusions [Array<Object>] mutually exclusive values (or variables), which the variable cannot be.
-
# @return [Boolean] whether the goal was satisfied.
-
1
def is_noteq(goal, block, variable, all_values, *exclusions)
-
130
return false unless variable.type == :variable
-
-
129
all_values = all_values.map(&:value).map(&:value)
-
129
exclusions = exclusions.map(&:value).map(&:value)
-
-
129
possible_values = goal[Porolog::anonymous]
-
-
129
if exclusions.uniq.size == exclusions.size
-
62
!!possible_values.instantiate(all_values - exclusions) && Predicate.call_builtin(:member, goal, block, variable, possible_values) || false
-
else
-
67
false
-
end
-
end
-
-
# Corresponds to the standard Prolog < predicate.
-
# It is satisfied if:
-
# - it is provided with two values (or instantiated Variables) where the first is less than the second.
-
# Variables are not instantiated.
-
# Use:
-
# less(:X, :Y)
-
# less(:X, 3)
-
# less(9, :Y)
-
# less(:name, 'max')
-
# @param goal [Porolog::Goal] the Goal to satisfy the Predicate.
-
# @param block [Proc] the continuation of solving to call if this Predicate is satisfied.
-
# @param x [Object] the left hand side of the inequality.
-
# @param y [Object] the right hand side of the inequality.
-
# @return [Boolean] whether the goal was satisfied.
-
1
def less(goal, block, x, y)
-
9
x = x.value.value
-
9
y = y.value.value
-
-
9
if [x.type, y.type].include?(:variable)
-
2
false
-
else
-
7
x < y
-
end && block.call(goal) || false
-
end
-
-
# Corresponds to the standard Prolog > predicate.
-
# It is satisfied if:
-
# - it is provided with two values (or instantiated Variables) where the first is greater than the second.
-
# Variables are not instantiated.
-
# Use:
-
# gtr(:X, :Y)
-
# gtr(:X, 3)
-
# gtr(9, :Y)
-
# gtr(:name, 'max')
-
# @param goal [Porolog::Goal] the Goal to satisfy the Predicate.
-
# @param block [Proc] the continuation of solving to call if this Predicate is satisfied.
-
# @param x [Object] the left hand side of the inequality.
-
# @param y [Object] the right hand side of the inequality.
-
# @return [Boolean] whether the goal was satisfied.
-
1
def gtr(goal, block, x, y)
-
424
x = x.value.value
-
424
y = y.value.value
-
-
424
if [x.type, y.type].include?(:variable)
-
2
false
-
else
-
422
x > y
-
end && block.call(goal) || false
-
end
-
-
# Corresponds to the standard Prolog <= predicate.
-
# It is satisfied if:
-
# - it is provided with two values (or instantiated Variables) where the first is less than or equal to the second, or
-
# - it is provided with two uninstantiaed Variables that are bound to each other.
-
# Variables are not instantiated (except temporarily to test if they are bound).
-
# Use:
-
# lesseq(:X, :Y)
-
# lesseq(:X, 3)
-
# lesseq(9, :Y)
-
# lesseq(:name, 'max')
-
# @param goal [Porolog::Goal] the Goal to satisfy the Predicate.
-
# @param block [Proc] the continuation of solving to call if this Predicate is satisfied.
-
# @param x [Object] the left hand side of the inequality.
-
# @param y [Object] the right hand side of the inequality.
-
# @return [Boolean] whether the goal was satisfied.
-
1
def lesseq(goal, block, x, y)
-
12
x = x.value.value
-
12
y = y.value.value
-
-
12
case [x.type, y.type]
-
when [:variable, :variable]
-
2
equal = false
-
2
temporary_instantiation = x.instantiate UNIQUE_VALUE
-
2
if temporary_instantiation
-
2
equal = y.value.value == UNIQUE_VALUE
-
2
temporary_instantiation.remove
-
end
-
2
equal
-
else
-
10
if [x.type, y.type].include?(:variable)
-
1
false
-
else
-
9
x <= y
-
end
-
end && block.call(goal) || false
-
end
-
-
# Corresponds to the standard Prolog >= predicate.
-
# It is satisfied if:
-
# - it is provided with two values (or instantiated Variables) where the first is greater than or equal to the second, or
-
# - it is provided with two uninstantiaed Variables that are bound to each other.
-
# Variables are not instantiated (except temporarily to test if they are bound).
-
# Use:
-
# gtreq(:X, :Y)
-
# gtreq(:X, 3)
-
# gtreq(9, :Y)
-
# gtreq(:name, 'max')
-
# @param goal [Porolog::Goal] the Goal to satisfy the Predicate.
-
# @param block [Proc] the continuation of solving to call if this Predicate is satisfied.
-
# @param x [Object] the left hand side of the inequality.
-
# @param y [Object] the right hand side of the inequality.
-
# @return [Boolean] whether the goal was satisfied.
-
1
def gtreq(goal, block, x, y)
-
12
x = x.value.value
-
12
y = y.value.value
-
-
12
case [x.type, y.type]
-
when [:variable, :variable]
-
2
equal = false
-
2
temporary_instantiation = x.instantiate UNIQUE_VALUE
-
2
if temporary_instantiation
-
2
equal = y.value.value == UNIQUE_VALUE
-
2
temporary_instantiation.remove
-
end
-
2
equal
-
else
-
10
if [x.type, y.type].include?(:variable)
-
1
false
-
else
-
9
x >= y
-
end
-
end && block.call(goal) || false
-
end
-
-
# Corresponds to the standard Prolog length predicate.
-
# It is satisfied if:
-
# - it is provided with an Array and an Integer where the Integer corresponds to the length of the Array,
-
# - it is provided with an Array and a Variable and the Variable is successfully instantiated to the length of the Array, or
-
# - it is provided with a Variable and an Integer where the Variable is successfully instantiated to an of anonymous variables.
-
# Use:
-
# length([1,2,3,4], 4)
-
# length([1,2,3,4], :Length)
-
# length(:L, 4)
-
# @param goal [Porolog::Goal] the Goal to satisfy the Predicate.
-
# @param block [Proc] the continuation of solving to call if this Predicate is satisfied.
-
# @param list [Array,Porolog::Variable] the list.
-
# @param length [Integer,Porolog::Variable] the length of the list.
-
# @return [Boolean] whether the goal was satisfied.
-
1
def length(goal, block, list, length)
-
11
list = list.value.value
-
11
length = length.value.value
-
-
11
case [list.type, length.type]
-
when [:array, :atomic]
-
2
list.length == length
-
when [:variable, :atomic]
-
25
list.instantiate(Array.new(length){goal[Porolog::anonymous]})
-
when [:array, :variable]
-
1
length.instantiate(list.length)
-
else
-
2
false
-
end && block.call(goal) || false
-
end
-
-
# Does not correspond to a standard Prolog predicate.
-
# This is a convenience Predicate to allow efficient control of
-
# iteration as well as range comparison.
-
# Use:
-
# between(5, 0, 9)
-
# between(:N, 0, 9)
-
# between(:N, :Upper, :Lower)
-
# @param goal [Porolog::Goal] the Goal to satisfy the Predicate.
-
# @param block [Proc] the continuation of solving to call if this Predicate is satisfied.
-
# @param variable [String,Integer,Porolog::Variable] the intermediate value or variable.
-
# @param lower [String,Integer] the lower bound of the iteration or range.
-
# @param upper [String,Integer] the upper bound of the iteration or range.
-
# @return [Boolean] whether the goal was satisfied.
-
1
def between(goal, block, variable, lower, upper)
-
13
variable = variable.value.value
-
13
lower = lower.value.value
-
13
upper = upper.value.value
-
-
13
case [variable.type, lower.type, upper.type]
-
when [:atomic, :atomic, :atomic]
-
2
(lower..upper) === variable && block.call(goal) || false
-
-
when [:atomic, :variable, :variable]
-
1
satisfied = false
-
1
lower_instantiation = lower.instantiate(variable)
-
1
upper_instantiation = lower_instantiation && upper.instantiate(variable)
-
1
upper_instantiation && block.call(goal) && (satisfied = true)
-
1
upper_instantiation&.remove
-
1
lower_instantiation&.remove
-
1
satisfied
-
-
when [:atomic, :atomic, :variable]
-
2
satisfied = false
-
2
if variable >= lower
-
1
upper_instantiation = upper.instantiate(variable)
-
1
upper_instantiation && block.call(goal) && (satisfied = true)
-
1
upper_instantiation&.remove
-
end
-
2
satisfied
-
-
when [:atomic, :variable, :atomic]
-
2
satisfied = false
-
2
if variable <= upper
-
1
lower_instantiation = lower.instantiate(variable)
-
1
lower_instantiation && block.call(goal) && (satisfied = true)
-
1
lower_instantiation&.remove
-
end
-
2
satisfied
-
-
when [:variable, :atomic, :atomic]
-
4
satisfied = false
-
4
(lower..upper).each do |i|
-
246
instantiation = variable.instantiate(i)
-
246
instantiation && block.call(goal) && (satisfied = true) || false
-
245
instantiation&.remove
-
245
return satisfied if goal.terminated?
-
end
-
3
satisfied
-
-
else
-
2
false
-
end
-
end
-
-
# Corresponds to the standard Prolog member predicate.
-
# This implements the usual operation of member but also
-
# provides the ability to generate lists that contain the
-
# provided element, even if the element is an uninstantiated variable.
-
# Use:
-
# member(3, [1,2,3,4,5])
-
# member(['Chris','Smith'], [['Foo','Bar'],['Boo','Far'],['Chris','Smith']])
-
# member(:X, [1,2,3,4,5])
-
# member(:X, :Y)
-
# member(:X, :Y, 5)
-
# member(3, :Y, 10)
-
# member(['Chris','Smith'], :Names, 16)
-
# @param goal [Porolog::Goal] the Goal to satisfy the Predicate.
-
# @param block [Proc] the continuation of solving to call if this Predicate is satisfied.
-
# @param element [Object] the element to be found in the provided or generated list.
-
# @param list [Array,Porolog::Variable] the provided or generated list that is to contain the element.
-
# @param limit [Integer] the number of lists to generate.
-
# @return [Boolean] whether the goal was satisfied.
-
1
def member(goal, block, element, list, limit = 100)
-
80
element_value = element.value.value
-
80
list = list.value.value
-
-
80
case [element_value.type, list.type]
-
when [:atomic, :array], [:array, :array]
-
12
satisfied = false
-
12
list.each do |i|
-
40
unifications = Porolog::unify(element_value, i, goal)
-
40
if unifications
-
21
instantiations = Porolog::instantiate_unifications(unifications)
-
21
if instantiations
-
21
block.call(goal) && (satisfied = true)
-
21
instantiations.each(&:remove)
-
end
-
end
-
-
40
return satisfied if goal.terminated?
-
end
-
12
satisfied
-
-
when [:variable, :array]
-
66
satisfied = false
-
66
list.each do |i|
-
94
instantiation = element_value.instantiate(i)
-
94
instantiation && block.call(goal) && (satisfied = true)
-
94
instantiation&.remove
-
94
return satisfied if goal.terminated?
-
94
satisfied = true
-
end
-
66
satisfied
-
-
when [:variable, :variable], [:atomic, :variable], [:array, :variable]
-
1
satisfied = false
-
1
limit.times do |i|
-
78
instantiation = list.instantiate([*Array.new(i){goal[Porolog::anonymous]}, element, Porolog::UNKNOWN_TAIL])
-
12
instantiation && block.call(goal) && (satisfied = true)
-
12
instantiation&.remove
-
12
return satisfied if goal.terminated?
-
end
-
1
satisfied
-
-
else
-
1
false
-
end
-
end
-
-
# Corresponds to the standard Prolog append predicate.
-
# This implements the usual operation of member but also
-
# provides the ability instantiate uninstantiated arguments
-
# as an instnatiation of the concatenation of the first two arguments.
-
# Use:
-
# append([1,2,3], [4,5,6], [1,2,3,4,5,6])
-
# append([1,2,3], [4,5,6], :C)
-
# append([1,2,3], :B, [1,2,3,4,5,6])
-
# append(:A, [4,5,6], [1,2,3,4,5,6])
-
# append(:A, :B, [1,2,3,4,5,6])
-
# append([1,2,3], :B, :C)
-
# append(:A, [4,5,6], :C)
-
# append(:A, :B, :C)
-
# @param goal [Porolog::Goal] the Goal to satisfy the Predicate.
-
# @param block [Proc] the continuation of solving to call if this Predicate is satisfied.
-
# @param front [Array,Porolog::Variable] the front portion of the combined front_back argument.
-
# @param back [Array,Porolog::Variable] the back portion of the combined front_back argument.
-
# @param front_back [Array,Porolog::Variable] the combined argument of the front and back arguments.
-
# @return [Boolean] whether the goal was satisfied.
-
1
def append(goal, block, front, back, front_back)
-
33
front = front.value.value
-
33
back = back.value.value
-
33
front_back = front_back.value.value
-
-
33
case [front.type, back.type, front_back.type]
-
when [:array, :array, :array]
-
5
satisfied = false
-
5
if front.length + back.length == front_back.length
-
5
unifications = Porolog::unify(front + back, front_back, goal)
-
5
instantiations = Porolog::instantiate_unifications(unifications) if unifications
-
5
instantiations && block.call(goal) && (satisfied = true)
-
5
instantiations&.each(&:remove)
-
end
-
5
satisfied
-
-
when [:array, :array, :variable]
-
21
satisfied = false
-
21
unifications = Porolog::unify(front + back, front_back, goal)
-
21
instantiations = Porolog::instantiate_unifications(unifications) if unifications
-
21
instantiations && block.call(goal) && (satisfied = true)
-
21
instantiations&.each(&:remove)
-
21
satisfied
-
-
when [:array, :variable, :array]
-
1
satisfied = false
-
1
if front.length <= front_back.length
-
1
expected_front = front_back[0...front.length]
-
1
expected_back = front_back[front.length..-1]
-
-
1
unifications = Porolog::unify(front, expected_front, goal)
-
1
unifications += Porolog::unify(back, expected_back, goal) if unifications
-
1
instantiations = Porolog::instantiate_unifications(unifications) if unifications
-
1
instantiations && block.call(goal) && (satisfied = true)
-
1
instantiations&.each(&:remove)
-
end
-
1
satisfied
-
-
when [:variable, :array, :array]
-
1
satisfied = false
-
1
if back.length <= front_back.length
-
1
expected_front = front_back[0...-back.length]
-
1
expected_back = front_back[-back.length..-1]
-
-
1
unifications = Porolog::unify(front, expected_front, goal)
-
1
unifications += Porolog::unify(back, expected_back, goal) if unifications
-
1
instantiations = Porolog::instantiate_unifications(unifications) if unifications
-
1
instantiations && block.call(goal) && (satisfied = true)
-
1
instantiations&.each(&:remove)
-
end
-
1
satisfied
-
-
when [:variable, :variable, :array]
-
1
satisfied = false
-
1
(front_back.length + 1).times do |i|
-
6
expected_front = front_back[0...i]
-
6
expected_back = front_back[i..-1]
-
-
6
unifications = Porolog::unify(front, expected_front, goal)
-
6
unifications += Porolog::unify(back, expected_back, goal) if unifications
-
6
instantiations = Porolog::instantiate_unifications(unifications) if unifications
-
6
instantiations && block.call(goal) && (satisfied = true)
-
6
instantiations&.each(&:remove)
-
6
return satisfied if goal.terminated?
-
end
-
1
satisfied
-
-
when [:array, :variable, :variable]
-
1
satisfied = false
-
1
unifications = Porolog::unify(front / back, front_back, goal)
-
1
instantiations = Porolog::instantiate_unifications(unifications) if unifications
-
1
instantiations && block.call(goal) && (satisfied = true)
-
1
instantiations&.each(&:remove)
-
1
satisfied
-
-
when [:variable, :array, :variable]
-
1
satisfied = false
-
1
instantiation_head = front_back.instantiate(front, nil, :flathead)
-
1
instantiation_tail = front_back.instantiate(back, nil, :flattail)
-
1
instantiations = [instantiation_head, instantiation_tail].compact
-
1
instantiations = nil if instantiations.empty?
-
1
instantiations && block.call(goal) && (satisfied = true)
-
1
instantiations&.each(&:remove)
-
1
satisfied
-
-
when [:variable, :variable, :variable]
-
1
satisfied = false
-
1
instantiation_head = front_back.instantiate(front, nil, :flathead)
-
1
instantiation_tail = front_back.instantiate(back, nil, :flattail)
-
1
instantiations = [instantiation_head, instantiation_tail].compact
-
1
instantiations = nil if instantiations.empty?
-
1
instantiations && block.call(goal) && (satisfied = true)
-
1
instantiations&.each(&:remove)
-
1
satisfied
-
-
else
-
1
false
-
end
-
end
-
-
# Corresponds to the standard Prolog permutation predicate.
-
# It not only returns whether one list is a permutation of the other
-
# but also can generate permutations.
-
# Use:
-
# permutation([3,1,2,4], [1,2,3,4])
-
# permutation([3,:A,2,4], [1,2,3,4])
-
# permutation([3,1,2,4], [1,2,:C,4])
-
# permutation([3,1,:B,4], [1,2,:C,4])
-
# permutation([3,1,2,4], :Q)
-
# permutation(:P, [1,2,3,4])
-
# @param goal [Porolog::Goal] the Goal to satisfy the Predicate.
-
# @param block [Proc] the continuation of solving to call if this Predicate is satisfied.
-
# @param list1 [Array,Porolog::Variable] the first list.
-
# @param list2 [Array,Porolog::Variable] the second list.
-
# @return [Boolean] whether the goal was satisfied.
-
1
def permutation(goal, block, list1, list2)
-
# TODO: Detect and deal with tails
-
# E.g. permutation([:H]/:T, [1,...])
-
10
list1 = list1.value.value
-
10
list2 = list2.value.value
-
-
10
case [list1.type, list2.type]
-
when [:array, :array]
-
7
satisfied = false
-
7
case [list1.variables.empty?, list2.variables.empty?]
-
when [true, true]
-
2
list1 = list1.sort_by(&:inspect)
-
2
list2 = list2.sort_by(&:inspect)
-
-
2
unifications = Porolog::unify(list1, list2, goal)
-
2
instantiations = Porolog::instantiate_unifications(unifications) if unifications
-
2
instantiations && block.call(goal) && (satisfied = true)
-
2
instantiations&.each(&:remove)
-
-
when [false, true], [false, false]
-
4
list2.permutation do |p|
-
96
unifications = Porolog::unify(list1, p, goal)
-
96
instantiations = nil
-
96
instantiations = Porolog::instantiate_unifications(unifications) if unifications
-
96
instantiations && block.call(goal) && (satisfied = true)
-
96
instantiations&.each(&:remove)
-
96
return satisfied if goal.terminated?
-
end
-
-
when [true, false]
-
1
list1.permutation do |p|
-
24
unifications = Porolog::unify(list2, p, goal)
-
24
instantiations = nil
-
24
instantiations = Porolog::instantiate_unifications(unifications) if unifications
-
24
instantiations && block.call(goal) && (satisfied = true)
-
24
instantiations&.each(&:remove)
-
24
return satisfied if goal.terminated?
-
end
-
end
-
7
satisfied
-
-
when [:array, :variable]
-
1
satisfied = false
-
1
list1.permutation do |p|
-
24
unifications = Porolog::unify(p, list2, goal)
-
24
instantiations = Porolog::instantiate_unifications(unifications) if unifications
-
24
instantiations && block.call(goal) && (satisfied = true)
-
24
instantiations&.each(&:remove)
-
24
return satisfied if goal.terminated?
-
end
-
1
satisfied
-
-
when [:variable, :array]
-
1
satisfied = false
-
1
list2.permutation do |p|
-
24
unifications = Porolog::unify(list1, p, goal)
-
24
instantiations = nil
-
24
instantiations = Porolog::instantiate_unifications(unifications) if unifications
-
24
instantiations && block.call(goal) && (satisfied = true)
-
24
instantiations&.each(&:remove)
-
24
return satisfied if goal.terminated?
-
end
-
1
satisfied
-
-
else
-
1
false
-
end
-
end
-
-
# Corresponds to the standard Prolog reverse predicate.
-
# It returns whether the lists are a reversal of each other,
-
# or otherwise generates a reversed list.
-
# Use:
-
# reverse([1,2,3,4], [4,3,2,1])
-
# reverse([1,:A,3,4], [4,3,2,1])
-
# reverse([1,2,3,4], [4,:B,2,1])
-
# reverse([1,:A,3,4], [4,:B,2,1])
-
# reverse(:L, [4,3,2,1])
-
# reverse([1,2,3,4], :L)
-
# @param goal [Porolog::Goal] the Goal to satisfy the Predicate.
-
# @param block [Proc] the continuation of solving to call if this Predicate is satisfied.
-
# @param list1 [Array,Porolog::Variable] the first list.
-
# @param list2 [Array,Porolog::Variable] the second list.
-
# @return [Boolean] whether the goal was satisfied.
-
1
def reverse(goal, block, list1, list2)
-
# TODO: Detect and deal with tails
-
# E.g. reverse([:H]/:T, [1,...])
-
11
list1 = list1.value.value
-
11
list2 = list2.value.value
-
-
11
case [list1.type, list2.type]
-
when [:array, :array], [:variable, :array]
-
7
satisfied = false
-
7
unifications = Porolog::unify(list1, list2.reverse, goal)
-
7
instantiations = Porolog::instantiate_unifications(unifications) if unifications
-
7
instantiations && block.call(goal) && (satisfied = true)
-
7
instantiations&.each(&:remove)
-
7
satisfied
-
-
when [:array, :variable]
-
2
satisfied = false
-
2
unifications = Porolog::unify(list1.reverse, list2, goal)
-
2
instantiations = Porolog::instantiate_unifications(unifications) if unifications
-
2
instantiations && block.call(goal) && (satisfied = true)
-
2
instantiations&.each(&:remove)
-
2
satisfied
-
-
else
-
2
false
-
end
-
end
-
-
end
-
-
end
-
-
end
-
#
-
# lib/porolog/rule.rb - Plain Old Ruby Objects Prolog Engine -- Rule
-
#
-
# Luis Esteban 2 May 2018
-
# created
-
#
-
-
1
module Porolog
-
-
# A Porolog::Rule is one clause of a Porolog::Predicate.
-
#
-
# @author Luis Esteban
-
#
-
# @!attribute [r] arguments
-
# The Arguments of the Predicate for which this Rule applies.
-
# @!attribute [r] definition
-
# The definition of the Rule.
-
#
-
1
class Rule
-
-
# Error class for rescuing or detecting any Rule error.
-
1
class Error < PorologError ; end
-
# Error class indicating that there is an error in the definition of the Rule.
-
1
class DefinitionError < Error ; end
-
-
1
attr_reader :arguments, :definition
-
-
# Clears all Rules.
-
# @return [Boolean] success
-
1
def self.reset
-
720
@@rules = []
-
720
true
-
end
-
-
1
reset
-
-
# Initializes the Rule.
-
# @param arguments [Porolog::Arguments] the Arguments of the Predicate for which this Rule applies.
-
# @param definition [Object] the definition of the Rule.
-
1
def initialize(arguments, definition = nil)
-
184
@arguments = arguments
-
184
@definition = definition
-
184
@@rules << self
-
end
-
-
# Convenience method for testing/debugging
-
# @return [String] pretty identification
-
1
def myid
-
10
"Rule#{(@@rules.index(self) || -1000) + 1}"
-
end
-
-
# @return [String] pretty representation.
-
1
def inspect
-
15
" #{@arguments.inspect}:- #{@definition.inspect}"
-
end
-
-
# Try to satisfy the Rule for the given Goal.
-
# @param goal [Porolog::Goal] the Goal in which to satisfy this Rule.
-
# @param block [Proc] code to perform when the Rule is satisfied.
-
# @return [Boolean] the success of deleting the subgoal.
-
1
def satisfy(goal, &block)
-
2590
subgoal = Goal.new self.arguments, goal
-
-
2590
unified_goals = Porolog::unify_goals(goal, subgoal)
-
2590
if unified_goals
-
1989
satisfy_definition(goal, subgoal) do |solution_goal|
-
1465
block.call(solution_goal)
-
end
-
else
-
601
subgoal.log << "Dead-end: Cannot unify with #{goal.inspect}"
-
end
-
-
2576
subgoal.delete!
-
end
-
-
# Try to satisfy the definition of the Rule for a given Goal.
-
# @param goal [Porolog::Goal] the given Goal for the Rule.
-
# @param subgoal [Porolog::Goal] the subgoal for the Rule's definition.
-
# @param block [Proc] code to perform when the definition is satisfied.
-
# @return [Boolean] whether the definition was satisfied.
-
1
def satisfy_definition(goal, subgoal, &block)
-
1992
case @definition
-
when TrueClass
-
937
block.call(subgoal)
-
934
true
-
-
when FalseClass
-
3
false
-
-
when Array
-
1051
satisfy_conjunction(goal, subgoal, @definition) do |solution_goal|
-
530
block.call(solution_goal)
-
end
-
-
else
-
1
raise DefinitionError, "UNEXPECTED TYPE OF DEFINITION: #{@definition.inspect} (#{@definition.class})"
-
end
-
end
-
-
# Try to satisfy the conjunction of the definition of the Rule for a given Goal.
-
# A conjunction is a sequence of expressions where the sequence is true
-
# if all the expressions are true.
-
#
-
# @param goal [Porolog::Goal] the given Goal for the Rule.
-
# @param subgoal [Porolog::Goal] the subgoal for the Rule's definition.
-
# @param conjunction [Array] the conjunction to satisfy.
-
# @param block [Proc] code to perform when the definition is satisfied.
-
# @return [Boolean] whether the definition was satisfied.
-
1
def satisfy_conjunction(goal, subgoal, conjunction, &block)
-
4247
arguments = conjunction.first
-
4247
conjunction = conjunction[1..-1]
-
-
# -- Handle non-Arguments --
-
4247
case arguments
-
when :CUT, true
-
441
subgoal.log << "CUTTING #{goal.inspect}..."
-
441
result = true
-
441
if conjunction.empty?
-
77
!goal.terminated? && block.call(subgoal)
-
else
-
364
result = satisfy_conjunction(goal, subgoal, conjunction, &block)
-
end
-
-
433
if arguments == :CUT
-
410
goal.terminate!
-
410
goal.log << "TERMINATED after #{subgoal.inspect}"
-
end
-
-
433
return result
-
-
when false
-
107
return false
-
-
when nil
-
1
!goal.terminated? && block.call(subgoal)
-
1
return true
-
end
-
-
# -- Unify Subsubgoal --
-
3698
subsubgoal = arguments.goal(subgoal)
-
3698
unified = subsubgoal.inherit_variables(subgoal)
-
-
# -- Satisfy Subgoal --
-
3698
result = false
-
3698
unified && subsubgoal.satisfy() do
-
3281
result = true
-
3281
if conjunction.empty?
-
457
!goal.terminated? && block.call(goal)
-
else
-
2824
result = !goal.terminated? && satisfy_conjunction(goal, subsubgoal, conjunction, &block)
-
end
-
end
-
-
# -- Uninstantiate --
-
3663
subsubgoal.delete!
-
-
3663
result && !goal.terminated?
-
end
-
-
end
-
-
end
-
#
-
# lib/porolog/scope.rb - Plain Old Ruby Objects Prolog Engine -- Scope
-
#
-
# Luis Esteban 2 May 2018
-
# created
-
#
-
-
1
module Porolog
-
-
# A Porolog::Scope is a container for Porolog::Predicates.
-
# Its purpose is to allow a Ruby program to contain multiple Prolog programs
-
# without the Prolog programs interfering with each other.
-
# @author Luis Esteban
-
# @!attribute [r] name
-
# Name of the Scope.
-
1
class Scope
-
-
# Error class for rescuing or detecting any Scope error.
-
1
class Error < PorologError ; end
-
# Error class indicating a non-Predicate was assigned to a Scope.
-
1
class NotPredicateError < Error ; end
-
-
1
attr_reader :name
-
-
# Creates a new Scope unless it already exists.
-
# @param name [Symbol, Object] the name (or otherwise object) used to register a scope.
-
# @return [Porolog::Scope] new or existing Scope.
-
1
def self.new(name)
-
758
@@scopes[name] || super
-
end
-
-
# Initializes and registers the Scope.
-
# @param name [Object] the name (or otherwise object) used to register a scope.
-
1
def initialize(name)
-
755
@name = name
-
755
@predicates = {}
-
-
755
@@scopes[@name] = self
-
end
-
-
# Clears all scopes and re-creates the default Scope.
-
# @return [Porolog::Scope] the default Scope.
-
1
def self.reset
-
720
@@scopes = {}
-
720
new(:default)
-
end
-
-
1
reset
-
-
# Looks up a Scope by its name.
-
# @param name [Object] the name (or otherwise object) used to register a scope.
-
# @return [Porolog::Scope] the default Scope.
-
1
def self.[](name)
-
1954
@@scopes[name]
-
end
-
-
# Returns the names of all registered Scopes.
-
# @return [Array<Symbol>] the names if scopes are named as Symbols (the intended case).
-
# @return [Array<Object>] the names if the names are not actually Symbols.
-
1
def self.scopes
-
17
@@scopes.keys
-
end
-
-
# Looks up a Predicate in the Scope by its name.
-
# @param name [Object] the name (or otherwise object) used to register a scope.
-
# @return [Porolog::Predicate] the Predicate indicated by the name.
-
1
def [](name)
-
1178
@predicates[name.to_sym]
-
end
-
-
# Assigns a Predicate to the Scope by its name.
-
# @param name [Object] the name (or otherwise object) used to register a scope.
-
# @param predicate [Porolog::Predicate] a Predicate to be assigned to the Scope.
-
# @return [Porolog::Predicate] the Predicate assigned to the Scope.
-
# @raise [NotPredicateError] when provided predicate is not actually a Predicate.
-
1
def []=(name, predicate)
-
1163
raise NotPredicateError, "#{predicate.inspect} is not a Predicate" unless predicate.is_a?(Predicate)
-
1162
@predicates[name.to_sym] = predicate
-
end
-
-
# Returns the Predicates contained in the Scope.
-
# @return [Array<Porolog::Predicate>] Predicates assigned to the Scope.
-
1
def predicates
-
49
@predicates.values
-
end
-
-
end
-
-
end
-
#
-
# lib/porolog/tail.rb - Plain Old Ruby Objects Prolog Engine -- Tail
-
#
-
# Luis Esteban 2 May 2018
-
# created
-
#
-
-
1
module Porolog
-
-
# A Porolog::Tail is used to represent the tail of a list.
-
#
-
# It corresponds to the use of the splat operator within an Array.
-
#
-
# @author Luis Esteban
-
#
-
# @!attribute value
-
# @return [Object] The value of the tail.
-
1
class Tail
-
-
# Creates a new Tail for an Array.
-
# @param value [Object] the value of the tail.
-
1
def initialize(value = UNKNOWN_TAIL)
-
1151
@value = value
-
end
-
-
# @return [Symbol] the type of the Tail, which should be :tail.
-
1
def type
-
553
:tail
-
end
-
-
# Returns the value of the Tail.
-
# The optional arguments are ignored; this is for polymorphic compatibility with Porolog::Value and Porolog::Variable,
-
# which are used to prevent inifinite recursion.
-
# @return [Object] the value of the Tail.
-
1
def value(*)
-
99147
@value
-
end
-
-
# @return [String] pretty representation.
-
1
def inspect
-
1196
"*#{@value.inspect}"
-
end
-
-
# @return [Array] embedded variables.
-
1
def variables
-
52514
@value.variables
-
end
-
-
# @param other [Object, #value]
-
# @return [Boolean] whether the value of the Tail is equal to the value of another Object.
-
1
def ==(other)
-
73839
@value == other.value
-
end
-
-
end
-
-
end
-
#
-
# lib/porolog/value.rb - Plain Old Ruby Objects Prolog Engine -- Value
-
#
-
# Luis Esteban 2 May 2018
-
# created
-
#
-
-
1
module Porolog
-
-
# A Porolog::Value combines a value with a goal so that when the goal
-
# is closed, the value can be uninstantiated at the same time.
-
#
-
# @author Luis Esteban
-
#
-
# @!attribute goal
-
# @return [Porolog::Goal] The goal in which the value was instantiated.
-
# @!attribute instantiations
-
# @return [Array<Porolog::Instantiation>] Instantiations of this value.
-
#
-
1
class Value
-
-
# Error class for rescuing or detecting any Value error.
-
1
class Error < PorologError ; end
-
# Error class indicating that the supplied goal is not actually a goal.
-
1
class GoalError < Error ; end
-
-
1
attr_accessor :goal, :instantiations
-
-
# @param value [Object] the value to be associated with a Goal.
-
# @param goal [Porolog::Goal] the Goal to be associated.
-
# @return [Porolog::Value] the Value.
-
1
def initialize(value, goal)
-
10088
raise GoalError, "Not a Goal: #{goal.inspect}" unless goal.is_a?(Goal)
-
-
10087
@value = value
-
10087
@value = value.value if value.is_a?(Value)
-
10087
@goal = goal
-
-
10087
@instantiations = []
-
end
-
-
# Pretty presentation.
-
# @return [String] the inspect of the value prefixed by the goal id.
-
1
def inspect
-
1713
"#{@goal.myid}.#{@value.inspect}"
-
end
-
-
# Pretty presentation with instantiations and indexes.
-
# This method is for polymorphic compatibility with Porolog::Variable.
-
# It used by Porolog::Goal#inspect_variables.
-
# @param visited [Array] the values already visited (to prevent infinite recursion).
-
# @param depth [Integer] the level of indentation that shows containment.
-
# @param index [Integer,Symbol,Array] the index into this value.
-
# @param self_index [Integer,Symbol,Array] the index of which this value belongs.
-
# @return [String] the inspect of the value in the context of the variables of a goal.
-
1
def inspect_with_instantiations(visited = [], depth = 0, index = nil, self_index = nil)
-
674
index_str = index && "[#{index.inspect}]" || ''
-
674
prefix = self_index&.inspect || ''
-
-
674
"#{' ' * depth}#{prefix}#{inspect}#{index_str}"
-
end
-
-
# Uninstantiate the Value.
-
# @return [Boolean] true
-
1
def remove
-
8385
@instantiations.dup.each(&:remove)
-
8385
@instantiations[0..-1] = []
-
8385
true
-
end
-
-
# Passes on methods to the Value's value.
-
1
def method_missing(method, *args, &block)
-
60752
@value.send(method, *args, &block)
-
end
-
-
# Responds to all the Value's value methods as well as its own.
-
# @return [Boolean] whether the value responds to the method.
-
1
def respond_to?(method, include_all = false)
-
817622
@value.respond_to?(method, include_all) || super
-
end
-
-
# @return [Object] the value of the Value.
-
1
def value(*)
-
1116409
@value
-
end
-
-
# @return [Symbol] the type of the value.
-
1
def type
-
818971
@value.type
-
end
-
-
# Compares Values for equality.
-
# @return [Boolean] whether the Values' values are equal.
-
1
def ==(other)
-
1708307
@value == other.value
-
end
-
-
# @return [Array<Porolog::Variable,Symbol>] variables embedded in the value.
-
1
def variables
-
94605
@value.variables
-
end
-
-
end
-
-
end
-
#
-
# lib/porolog/variable.rb - Plain Old Ruby Objects Prolog Engine -- Variable
-
#
-
# Luis Esteban 2 May 2018
-
# created
-
#
-
-
1
module Porolog
-
-
# A Porolog::Variable is used to hold instantiations during the process of satisfying a goal.
-
# It implements a variable of a goal.
-
# It allows instantiations to be made and removed as the goal
-
# is attempted to be satisfied.
-
#
-
# @author Luis Esteban
-
#
-
# @!attribute name
-
# @return [Symbol] The name of the variable.
-
# @!attribute goal
-
# @return [Porolog::Goal] The Goal for which this Variable's instantiations are bound.
-
# @!attribute instantiations
-
# @return [Array<Porolog::Instantiation>] The Instantiations of this Variable.
-
# @!attribute values
-
# @return [Array<Porolog::Value>] the Values of the Variable when used as an anonymous Variable.
-
#
-
1
class Variable
-
-
# Error class for rescuing or detecting any Variable error.
-
1
class Error < PorologError ; end
-
# Error class indicating a Variable has been instantiated to multiple different values at the same time.
-
1
class MultipleValuesError < Error ; end
-
# Error class indicating a Variable has been created without a Goal.
-
1
class GoalError < Error ; end
-
# Error class indicating a Variable has been instantiated to a value that contains itself.
-
1
class SelfReferentialError < Error ; end
-
# Error class indicating an unexpected scenario has occurred.
-
1
class UnexpectedError < Error ; end
-
-
1
attr_accessor :name, :goal, :instantiations, :values
-
-
# Initializes the Variable and attaches it to the Goal.
-
# @param name [Object] the name used to refer to the Variable.
-
# @param goal [Porolog::Goal] the Goal the Variable is to be attached to.
-
1
def initialize(name, goal)
-
31396
raise GoalError, "Not a Goal: #{goal.inspect}" unless goal.is_a?(Goal)
-
31395
@goal = goal
-
31395
name = name.to_sym if name.is_a?(String)
-
-
31395
case name
-
when Symbol
-
31392
@name = name
-
31392
@values = []
-
when Variable
-
1
@name = name.name
-
1
@values = []
-
when Value
-
1
@name = name.value
-
1
@values = [name]
-
else
-
1
@name = name.to_s
-
1
@values = [Value.new(name, goal)]
-
end
-
-
31395
@instantiations = []
-
31395
@goal.variable(self)
-
end
-
-
# Converts a Variable back to a Symbol.
-
# @return [Symbol, nil] the name of the Variable.
-
1
def to_sym
-
15350
@name&.to_sym
-
end
-
-
# @return [Symbol] the type of the Variable, which should be :variable.
-
1
def type
-
665917
:variable
-
end
-
-
# @return [String] pretty representation.
-
1
def inspect
-
3373
"#{@goal.myid}.#{@name.inspect}"
-
end
-
-
# @param visited [Array] used to prevent infinite recursion.
-
# @param depth [Integer] the current level of indentation.
-
# @param index [Object, nil] the index into this Variable that is instantiated.
-
# @param self_index [Object, nil] the index where this Variable is instantiated.
-
# @return [String] the inspect of the Variable showing instantiations using indentation.
-
1
def inspect_with_instantiations(visited = [], depth = 0, index = nil, self_index = nil)
-
8054
return if visited.include?(self)
-
-
2725
index_str = index && "[#{index.inspect}]" || ''
-
2725
prefix = self_index && "[#{self_index.inspect}]" || ''
-
2725
name = "#{' ' * depth}#{prefix}#{@goal.myid}.#{@name.inspect}#{index_str}"
-
-
2725
name = "#{name} = #{@values.map(&:inspect).join(',')}#{index_str}" if @values && !@values.empty? && @instantiations.empty?
-
-
2725
others = @instantiations.map{|instantiation|
-
[
-
3775
instantiation.variable1.inspect_with_instantiations(visited + [self], depth + 1, instantiation.index1, instantiation.index2),
-
instantiation.variable2.inspect_with_instantiations(visited + [self], depth + 1, instantiation.index2, instantiation.index1),
-
].compact
-
}.reject(&:empty?)
-
-
[
-
2725
name,
-
*others,
-
].join("\n")
-
end
-
-
# @return [Object,self] returns the current value of the Variable based on its current instantiations.
-
# If there are no concrete instantiations, it returns itself, indicating no value.
-
# @param visited [Array] prevents infinite recursion.
-
1
def value(visited = [])
-
1622972
return nil if visited.include?(self)
-
1513134
visited = visited + [self]
-
-
# -- Collect values --
-
1513134
values = [*@values]
-
-
1513134
@instantiations.each do |instantiation|
-
2615361
values += instantiation.values_for(self, visited)
-
end
-
-
1513134
values.uniq!
-
-
# -- Filter trivial values --
-
1513134
values = [[nil]] if values == [[UNKNOWN_TAIL], [nil]] || values == [[nil], [UNKNOWN_TAIL]]
-
-
2330986
values = values.reject{|value| value == UNKNOWN_TAIL }
-
-
3148820
values_values = values.map{|value| value.value(visited) }.reject{|value| value == UNKNOWN_ARRAY }
-
-
# -- Condense Values --
-
1513134
result = if values_values.size > 1
-
# -- Potentially Multiple Values Found --
-
40789
unifications = []
-
124719
if values_values.all?{|value| value.is_a?(Array) }
-
# -- All Values Are Arrays --
-
105290
values = values.reject{|value| value == UNKNOWN_ARRAY } if values.size > 2 && values.include?(UNKNOWN_ARRAY)
-
40385
values_goals = values.map{|value|
-
83526
value.respond_to?(:goal) && value.goal || value.variables.map(&:goal).first || values.variables.map(&:goal).first
-
}
-
-
40385
if values.size > 2
-
2756
merged, unifications = Porolog::unify_many_arrays(values, values_goals, visited)
-
37629
elsif values.size == 2
-
37629
no_variables = values.map(&:variables).flatten.empty?
-
37629
if no_variables
-
12
left_value = values[0].value.value
-
12
right_value = values[1].value.value
-
12
if left_value.last == UNKNOWN_TAIL && right_value.first == UNKNOWN_TAIL
-
7
return [*left_value[0...-1], *right_value[1..-1]]
-
5
elsif right_value.last == UNKNOWN_TAIL && left_value.first == UNKNOWN_TAIL
-
1
return [*right_value[0...-1], *left_value[1..-1]]
-
4
elsif left_value != right_value
-
1
return nil
-
end
-
end
-
37620
merged, unifications = Porolog::unify_arrays(*values, *values_goals, visited)
-
else
-
skipped
# :nocov: NOT REACHED
-
skipped
merged, unifications = values.first, []
-
skipped
# :nocov:
-
end
-
-
40376
merged.value(visited).to_a
-
else
-
# -- Not All Values Are Arrays --
-
404
values.each_cons(2){|left,right|
-
405
unification = Porolog::unify(left, right, @goal, @goal, visited)
-
405
if unification && unifications
-
404
unifications += unification
-
else
-
1
unifications = nil
-
end
-
}
-
404
if unifications
-
403
values.min_by{|value|
-
807
case value
-
311
when Porolog::Variable, Symbol then 2
-
1
when Porolog::UNKNOWN_TAIL, Porolog::UNKNOWN_ARRAY then 9
-
495
else 0
-
end
-
} || self
-
else
-
1
raise MultipleValuesError, "Multiple values detected for #{inspect}: #{values.inspect}"
-
end
-
end
-
else
-
# -- One (or None) Value Found --
-
1472345
values.min_by{|value|
-
711873
case value
-
1004
when Variable, Symbol then 2
-
32621
when UNKNOWN_TAIL, UNKNOWN_ARRAY then 9
-
678248
else 0
-
end
-
} || self
-
end
-
-
# -- Splat Tail --
-
1513124
if result.is_a?(Array) && result.size == 1 && result.first.is_a?(Tail)
-
570
result = result.first.value
-
end
-
-
1513124
result
-
end
-
-
# Instantiates Variable to another experssion.
-
# @param other [Object] the other value (or object) being instantiated.
-
# @param index_into_other [] the index into the other value.
-
# @param index_into_self [] the index into this Variable.
-
# @return [Porolog::Instantiation,nil] the instantiation made.
-
# @example
-
# # To instantiate the third element of x to the second element of y,
-
# # x = [a,b,c,d,e]
-
# # |
-
# # y = [p,q,r,s]
-
# x = goal.variable(:x)
-
# y = goal.variable(:y)
-
# x.instantiate(y, 1, 2)
-
# @example
-
# # To instantiate the tail of x to y,
-
# x.instantiate(y, nil, :tail)
-
1
def instantiate(other, index_into_other = nil, index_into_self = nil)
-
29675
raise SelfReferentialError, "Cannot instantiate self-referential definition for #{(self.variables & other.variables).inspect}" unless (self.variables & other.variables).empty?
-
-
# -- Check Instantiation is Unifiable --
-
29675
unless self.value.is_a?(Variable) || other.value.is_a?(Variable)
-
# -- Determine Other Goal --
-
173
other_goal = nil
-
173
other_goal = other.goal if other.respond_to?(:goal)
-
173
other_goal ||= self.goal
-
-
# -- Check Unification --
-
173
unless Porolog::unify(self, other, self.goal, other_goal)
-
5
self.goal.log << "Cannot unify: #{self.inspect} and #{other.inspect}"
-
5
return nil
-
end
-
end
-
-
# -- Create Instantiation --
-
29670
instantiation = Instantiation.new(self, index_into_self, other, index_into_other)
-
-
# -- Create Reverse Assymetric Instantiations --
-
29670
if other.value.is_a?(Array) && other.value.last.is_a?(Tail)
-
107
array = other.value
-
107
if array.length == 2
-
105
if array.first.is_a?(Variable)
-
105
Instantiation.new(array.first, nil, self, :head)
-
end
-
105
if array.last.is_a?(Tail) && array.last.value.is_a?(Variable)
-
105
Instantiation.new(array.last.value, nil, self, :tail)
-
end
-
end
-
end
-
-
# -- Return --
-
29670
instantiation
-
end
-
-
# Removes this Variable by removing all of its insantiations.
-
# @return [Boolean] success.
-
1
def remove
-
31381
@instantiations.dup.each(&:remove)
-
31381
@instantiations[0..-1] = []
-
31381
true
-
end
-
-
# Removes instantiations from another goal.
-
# @param other_goal [Porolog::Goal] the Goal of which instantiations are to be removed.
-
# @return [Boolean] success.
-
1
def uninstantiate(other_goal)
-
13
@instantiations.delete_if do |instantiation|
-
25
instantiation.remove if instantiation.other_goal_to(self) == other_goal
-
end
-
-
13
@values.delete_if{|value| value.goal == other_goal }
-
13
true
-
end
-
-
# Indexes the value of the Variable.
-
# @param index [Object] the index into the value.
-
# @return [Object, nil] the value at the index in the value of the Variable.
-
1
def [](index)
-
5
value = self.value
-
5
value = value.value if value.is_a?(Value)
-
-
5
case value
-
when Array
-
4
case index
-
when Integer
-
2
value[index]
-
when Symbol
-
2
case index
-
when :head
-
1
value[0]
-
when :tail
-
1
value[1..-1]
-
else
-
nil
-
end
-
else
-
nil
-
end
-
else
-
nil
-
end
-
end
-
-
# @return [Array<Porolog::Variable>] the Variables in itself, which is just itself.
-
1
def variables
-
183050
[self]
-
end
-
-
# Compares Variables.
-
# @param other [Porolog::Variable] the other Variable.
-
# @return [Boolean] whether the two Variables have the same name in the same Goal.
-
1
def ==(other)
-
31333743
other.is_a?(Variable) && @name == other.name && @goal == other.goal
-
end
-
-
end
-
-
end
-
#
-
# test/porolog/core_ext_test.rb - Test Suite for Core Extensions
-
#
-
# Luis Esteban 2 May 2018
-
# created
-
#
-
-
1
require_relative '../test_helper'
-
-
1
describe 'Object' do
-
-
5
let (:object1) { Object.new }
-
5
let (:object2) { 42 }
-
5
let (:object3) { { name: 'hello' } }
-
5
let (:object4) { 'abc' }
-
5
let (:object5) { 0.5 }
-
-
1
describe '#myid' do
-
-
1
it 'should return inspect' do
-
1
assert_equal object1.inspect, object1.myid
-
1
assert_equal object2.inspect, object2.myid
-
1
assert_equal object3.inspect, object3.myid
-
1
assert_equal object4.inspect, object4.myid
-
1
assert_equal object5.inspect, object5.myid
-
end
-
-
end
-
-
1
describe '#variables' do
-
-
1
it 'should return empty array' do
-
1
assert_equal [], object1.variables
-
1
assert_equal [], object2.variables
-
1
assert_equal [], object3.variables
-
1
assert_equal [], object4.variables
-
1
assert_equal [], object5.variables
-
end
-
-
end
-
-
1
describe '#type' do
-
-
1
it 'should return :atomic for atomic types' do
-
1
assert_equal :atomic, object1.type
-
1
assert_equal :atomic, object2.type
-
1
assert_equal :atomic, object3.type
-
1
assert_equal :atomic, object4.type
-
1
assert_equal :atomic, object5.type
-
end
-
-
end
-
-
1
describe '#/' do
-
-
1
it 'should create an n Array with a Tail' do
-
1
assert_Array_with_Tail object1 / :T, [object1], '*:T'
-
1
assert_Array_with_Tail [object2] / :T, [object2], '*:T'
-
#assert_Array_with_Tail object2.tail(:T), [object2], '*:T'
-
1
assert_Array_with_Tail object3 / :T, [object3], '*:T'
-
1
assert_Array_with_Tail object4 / :T, [object4], '*:T'
-
1
assert_Array_with_Tail [object5] / :T, [object5], '*:T'
-
#assert_Array_with_Tail object5.tail(:T), [object5], '*:T'
-
end
-
-
end
-
-
end
-
-
1
describe 'Symbol' do
-
-
5
let (:symbol1) { :alpha }
-
5
let (:symbol2) { :_ }
-
5
let (:symbol3) { :'with some spaces' }
-
5
let (:symbol4) { :X }
-
5
let (:symbol5) { :p }
-
-
1
describe '#myid' do
-
-
1
it 'should return inspect' do
-
1
assert_equal symbol1.inspect, symbol1.myid
-
1
assert_equal symbol2.inspect, symbol2.myid
-
1
assert_equal symbol3.inspect, symbol3.myid
-
1
assert_equal symbol4.inspect, symbol4.myid
-
1
assert_equal symbol5.inspect, symbol5.myid
-
end
-
-
end
-
-
1
describe '#variables' do
-
-
1
it 'should return an array of itself' do
-
1
assert_equal [symbol1], symbol1.variables
-
1
assert_equal [symbol2], symbol2.variables
-
1
assert_equal [symbol3], symbol3.variables
-
1
assert_equal [symbol4], symbol4.variables
-
1
assert_equal [symbol5], symbol5.variables
-
end
-
-
end
-
-
1
describe '#type' do
-
-
1
it 'should return :variable for symbols' do
-
1
assert_equal :variable, symbol1.type
-
1
assert_equal :variable, symbol2.type
-
1
assert_equal :variable, symbol3.type
-
1
assert_equal :variable, symbol4.type
-
1
assert_equal :variable, symbol5.type
-
end
-
-
end
-
-
1
describe '#/' do
-
-
1
it 'should create a HeadTail' do
-
1
assert_Array_with_Tail symbol1 / :T, [symbol1], '*:T'
-
1
assert_Array_with_Tail symbol2 / :T, [symbol2], '*:T'
-
1
assert_Array_with_Tail symbol3 / :T, [symbol3], '*:T'
-
1
assert_Array_with_Tail symbol4 / :T, [symbol4], '*:T'
-
1
assert_Array_with_Tail symbol5 / :T, [symbol5], '*:T'
-
end
-
-
end
-
-
end
-
-
1
describe 'Array' do
-
-
12
let(:array1) { [1, 2, :A, 4, [:B, 6, 7, :C], 9] }
-
12
let(:array2) { [] }
-
12
let(:array3) { Porolog::UNKNOWN_ARRAY }
-
12
let(:array4) { [1, :B, 3, Porolog::UNKNOWN_TAIL] }
-
-
1
describe '#/' do
-
-
1
it 'creates an Array using the slash notation with Arrays' do
-
1
head_tail = [1] / [2,3,4,5]
-
-
1
assert_equal [1,2,3,4,5], head_tail
-
end
-
-
1
it 'should combine a head Array and a tail Array when they have no variables' do
-
1
assert_equal [1,2,3,4,5,6], [1,2,3] / [4,5,6]
-
end
-
-
1
it 'should combine an embedded head Array and an embedded tail Array when they have no variables' do
-
1
assert_equal [1,2,3,4,5,6,7,8,9], [1,2,3] / [4,5,6] / [7,8,9]
-
end
-
-
1
it 'should create a HeadTail' do
-
1
assert_Array_with_Tail array1 / :T, array1, '*:T'
-
1
assert_Array_with_Tail array2 / :T, array2, '*:T'
-
1
assert_Array_with_Tail array3 / :T, array3, '*:T'
-
1
assert_Array_with_Tail array4 / :T, array4, '*:T'
-
end
-
-
end
-
-
1
describe '#variables' do
-
-
1
it 'should return an array of the embedded Symbols' do
-
1
assert_equal [:A, :B, :C], array1.variables
-
1
assert_equal [], array2.variables
-
1
assert_equal [], array3.variables
-
1
assert_equal [:B], array4.variables
-
end
-
-
end
-
-
1
describe '#value' do
-
-
1
it 'should return simple Arrays as is' do
-
1
assert_equal [1, 2, :A, 4, [:B, 6, 7, :C], 9], array1.value
-
1
assert_equal [], array2.value
-
1
assert_equal [Porolog::UNKNOWN_TAIL], array3.value
-
1
assert_equal [1, :B, 3, Porolog::UNKNOWN_TAIL], array4.value
-
end
-
-
1
it 'should expand Tails that are an Array' do
-
1
array = [1, 2, 3, Porolog::Tail.new([7, 8, 9])]
-
-
1
assert_equal [1,2,3,7,8,9], array.value
-
end
-
-
1
it 'should return the value of instantiated variables in a Tail' do
-
1
goal = new_goal :tailor, :h, :t
-
1
array = goal.variablise([:h, :b] / :t)
-
1
goal.instantiate :h, [1,2,3]
-
1
goal.instantiate :t, [7,8,9]
-
-
1
assert_equal [goal.value([1,2,3]),goal.variable(:b), goal.value(7), goal.value(8), goal.value(9)], array.value
-
end
-
-
1
it 'should return the value of instantiated variables in a Tail' do
-
1
goal = new_goal :taylor, :head, :tail
-
1
array = [Porolog::Tail.new(goal.value([5, 6, 7, 8]))]
-
-
1
assert_equal [5,6,7,8], array.value
-
end
-
-
end
-
-
1
describe '#type' do
-
-
1
it 'should return :variable for symbols' do
-
1
assert_equal :array, array1.type
-
1
assert_equal :array, array2.type
-
1
assert_equal :array, array3.type
-
1
assert_equal :array, array4.type
-
end
-
-
end
-
-
1
describe '#head' do
-
-
1
it 'should return the first element when no headsize is provided' do
-
1
assert_equal 1, array1.head
-
1
assert_nil array2.head
-
1
assert_nil array3.head
-
1
assert_equal 1, array4.head
-
end
-
-
1
it 'should return the first headsize elements' do
-
1
assert_equal [1, 2], array1.head(2)
-
1
assert_equal [], array2.head(2)
-
1
assert_equal [Porolog::UNKNOWN_TAIL], array3.head(2)
-
1
assert_equal [1, :B], array4.head(2)
-
end
-
-
1
it 'should return an extended head if the tail is uninstantiated' do
-
1
assert_equal [1, 2, :A, 4, [:B, 6, 7, :C], 9], array1.head(9)
-
1
assert_equal [], array2.head(9)
-
1
assert_equal [Porolog::UNKNOWN_TAIL], array3.head(9)
-
1
assert_equal [1, :B, 3, Porolog::UNKNOWN_TAIL], array4.head(9)
-
end
-
-
end
-
-
1
describe '#tail' do
-
-
1
it 'should return the tail after the first element when no headsize is provided' do
-
1
assert_equal [2, :A, 4, [:B, 6, 7, :C], 9], array1.tail
-
1
assert_equal [], array2.tail
-
1
assert_equal [Porolog::UNKNOWN_TAIL], array3.tail
-
1
assert_equal [:B, 3, Porolog::UNKNOWN_TAIL], array4.tail
-
end
-
-
1
it 'should return the tail after the first headsize elements' do
-
1
assert_equal [:A, 4, [:B, 6, 7, :C], 9], array1.tail(2)
-
1
assert_equal [], array2.tail(2)
-
1
assert_equal [Porolog::UNKNOWN_TAIL], array3.tail(2)
-
1
assert_equal [3, Porolog::UNKNOWN_TAIL], array4.tail(2)
-
end
-
-
1
it 'should return an extended tail if the tail is uninstantiated' do
-
1
assert_equal [], array1.tail(9)
-
1
assert_equal [], array2.tail(9)
-
1
assert_equal [Porolog::UNKNOWN_TAIL], array3.tail(9)
-
1
assert_equal [Porolog::UNKNOWN_TAIL], array4.tail(9)
-
end
-
-
end
-
-
1
describe '#clean' do
-
-
2
let(:predicate1) { Porolog::Predicate.new :generic }
-
2
let(:arguments1) { predicate1.arguments(:m,:n) }
-
2
let(:goal1) { arguments1.goal }
-
-
2
let(:array5) { [1, 2, goal1[:A], 4, [goal1[:B], 6, 7, goal1[:C]], 9] }
-
2
let(:array6) { [1, goal1[:B], 3]/:T }
-
2
let(:array7) { [1, goal1[:B], 3]/goal1[:T] }
-
-
1
it 'should return simple Arrays as is' do
-
1
assert_equal [1, 2, :A, 4, [:B, 6, 7, :C], 9], array1.clean
-
1
assert_equal [], array2.clean
-
1
assert_equal [Porolog::UNKNOWN_TAIL], array3.clean
-
1
assert_equal [1, :B, 3, Porolog::UNKNOWN_TAIL], array4.clean
-
end
-
-
1
it 'should return the values of its elements with variables replaced by nil and Tails replaced by UNKNOWN_TAIL' do
-
1
assert_equal [1, 2, nil, 4, [nil, 6, 7, nil], 9], array5.clean
-
1
assert_equal [1, nil, 3, Porolog::UNKNOWN_TAIL], array6.clean
-
1
assert_equal [1, nil, 3, Porolog::UNKNOWN_TAIL], array7.clean
-
end
-
-
end
-
-
end
-
#
-
# test/porolog/goal_test.rb - Test Suite for Porolog::Goal
-
#
-
# Luis Esteban 2 May 2018
-
# created
-
#
-
-
1
require_relative '../test_helper'
-
-
-
1
describe 'Porolog' do
-
-
1
before(:all) do
-
60
reset
-
end
-
-
1
describe 'Goal' do
-
-
44
let(:pred) { Porolog::Predicate.new :p }
-
44
let(:args) { pred.(:x,:y) }
-
25
let(:goal) { Porolog::Goal.new args }
-
-
1
describe '.reset' do
-
-
1
it 'should clear all goals' do
-
1
new_goal :predicate1, :a
-
1
new_goal :predicate2, :a, :b
-
1
new_goal :predicate3, :a, :b, :c
-
-
1
assert_equal 3, Porolog::Goal.goals.size
-
-
1
Porolog::Goal.reset
-
-
1
assert_empty Porolog::Goal.goals
-
end
-
-
1
it 'should call check_deleted for each goal' do
-
1
Porolog::Goal.any_instance.expects(:check_deleted).with().returns(false).times(5)
-
-
1
new_goal :p, :x, :y
-
1
new_goal :q, :a
-
1
new_goal :r, :a, :b, :c
-
-
1
Porolog::Goal.reset
-
-
1
new_goal :j, 1
-
1
new_goal :k, ['a', 'b', 'c']
-
-
1
Porolog::Goal.reset
-
end
-
-
end
-
-
1
describe '.goals' do
-
-
1
it 'should return all registered goals' do
-
1
assert_equal 0, Porolog::Goal.goals.size
-
-
1
goal1 = new_goal :predicate1, :a
-
-
1
assert_equal [goal1], Porolog::Goal.goals
-
-
1
goal2 = new_goal :predicate2, :a, :b
-
-
1
assert_equal [goal1,goal2], Porolog::Goal.goals
-
-
1
goal3 = new_goal :predicate3, :a, :b, :c
-
-
1
assert_equal [goal1,goal2,goal3], Porolog::Goal.goals
-
end
-
-
end
-
-
1
describe '.new' do
-
-
1
it 'should create a new Goal' do
-
1
goal = Porolog::Goal.new nil
-
-
1
assert_instance_of Porolog::Goal, goal
-
end
-
-
end
-
-
1
describe '#initialize' do
-
-
1
it 'should initialize calling_goal' do
-
1
goal1 = Porolog::Goal.new args
-
1
goal2 = Porolog::Goal.new args, goal1
-
-
1
assert_nil goal1.calling_goal
-
1
assert_equal goal1, goal2.calling_goal
-
-
1
assert_Goal goal1, :p, [:x,:y]
-
1
assert_Goal goal2, :p, [:x,:y]
-
end
-
-
1
it 'should initialize arguments' do
-
1
goal = Porolog::Goal.new args, nil
-
-
1
assert_equal goal.variablise(args), goal.arguments
-
end
-
-
1
it 'should initialize terminate' do
-
1
goal = Porolog::Goal.new args, nil
-
-
1
assert_equal false, goal.terminated?
-
end
-
-
1
it 'should initialize variables' do
-
1
goal = Porolog::Goal.new args, nil
-
-
1
assert_Goal_variables goal, { x: nil, y: nil }, [
-
'Goal1.:x',
-
'Goal1.:y',
-
].join("\n")
-
end
-
-
1
it 'should register the goal as undeleted' do
-
1
goal1 = Porolog::Goal.new args, nil
-
1
goal2 = Porolog::Goal.new args, goal1
-
-
1
assert_equal [goal1,goal2], Porolog::Goal.goals
-
end
-
-
end
-
-
1
describe '#myid' do
-
-
1
it 'should return the pretty id of the goal' do
-
1
goal1 = Porolog::Goal.new args, nil
-
1
goal2 = Porolog::Goal.new args, goal1
-
-
1
assert_equal 'Goal1', goal1.myid
-
1
assert_equal 'Goal2', goal2.myid
-
end
-
-
end
-
-
1
describe '#ancestors' do
-
-
1
it 'should return an Array of the parent goals' do
-
1
goal1 = Porolog::Goal.new args
-
1
goal2 = Porolog::Goal.new args, goal1
-
1
goal3 = Porolog::Goal.new args, goal2
-
1
goal4 = Porolog::Goal.new args, goal3
-
-
1
assert_equal [goal1], goal1.ancestors
-
1
assert_equal [goal1, goal2], goal2.ancestors
-
1
assert_equal [goal1, goal2, goal3], goal3.ancestors
-
1
assert_equal [goal1, goal2, goal3, goal4], goal4.ancestors
-
end
-
-
end
-
-
1
describe '#ancestry' do
-
-
1
it 'should return an Array of the parent goals' do
-
1
goal1 = Porolog::Goal.new args
-
1
goal2 = Porolog::Goal.new args, goal1
-
1
goal3 = Porolog::Goal.new args, goal2
-
1
goal4 = Porolog::Goal.new args, goal3
-
-
ancestors = [
-
1
'Goal1 -- Solve p(:x,:y) {:x=>nil, :y=>nil}',
-
' Goal2 -- Solve p(:x,:y) {:x=>nil, :y=>nil}',
-
' Goal3 -- Solve p(:x,:y) {:x=>nil, :y=>nil}',
-
' Goal4 -- Solve p(:x,:y) {:x=>nil, :y=>nil}',
-
]
-
-
1
assert_equal ancestors[0...1].join("\n"), goal1.ancestry
-
1
assert_equal ancestors[0...2].join("\n"), goal2.ancestry
-
1
assert_equal ancestors[0...3].join("\n"), goal3.ancestry
-
1
assert_equal ancestors[0...4].join("\n"), goal4.ancestry
-
end
-
-
end
-
-
1
describe '#inspect' do
-
-
1
it 'should show a description of the goal' do
-
1
goal1 = Porolog::Goal.new args
-
1
goal2 = Porolog::Goal.new pred.(1,:b,'word'), goal1
-
1
goal3 = Porolog::Goal.new args, goal2
-
1
goal4 = Porolog::Goal.new args, goal3
-
-
1
assert_equal 'Goal1 -- Solve p(:x,:y)', goal1.inspect
-
1
assert_equal 'Goal2 -- Solve p(1,:b,"word")', goal2.inspect
-
1
assert_equal 'Goal3 -- Solve p(:x,:y)', goal3.inspect
-
1
assert_equal 'Goal4 -- Solve p(:x,:y)', goal4.inspect
-
end
-
-
end
-
-
1
describe '#delete!' do
-
-
1
it 'should delete the goal' do
-
1
goal1 = Porolog::Goal.new args
-
1
goal2 = Porolog::Goal.new pred.(1,:b,'word'), goal1
-
1
goal3 = Porolog::Goal.new args, goal2
-
1
goal4 = Porolog::Goal.new args, goal3
-
-
1
assert goal2.delete!, 'goal should delete'
-
1
assert_equal [goal1, goal3, goal4], Porolog::Goal.goals
-
end
-
-
end
-
-
1
describe '#myid' do
-
-
2
let(:pred) { Porolog::Predicate.new :p }
-
2
let(:args) { pred.(:x,:y) }
-
-
1
it 'should return the pretty id of the goal' do
-
1
goal1 = Porolog::Goal.new args, nil
-
1
goal2 = Porolog::Goal.new args, goal1
-
-
1
assert_equal 'Goal1', goal1.myid
-
1
assert_equal 'Goal2', goal2.myid
-
end
-
-
end
-
-
1
describe '#deleted?' do
-
-
1
it 'should return the deleted state of a goal' do
-
1
goal = Porolog::Goal.new args
-
-
1
refute goal.deleted?, 'goal should not be deleted'
-
-
1
Porolog::Goal.reset
-
-
1
assert goal.deleted?, 'goal should be deleted'
-
end
-
-
1
it 'should memoize the deleted state of a goal' do
-
1
goal = Porolog::Goal.new args
-
-
1
check_deleted_spy = Spy.on(goal, :check_deleted).and_call_through
-
-
1
refute goal.deleted?
-
1
refute goal.deleted?
-
1
refute goal.deleted?
-
1
refute goal.deleted?
-
-
1
Porolog::Goal.reset
-
-
1
assert goal.deleted?
-
1
assert goal.deleted?
-
1
assert goal.deleted?
-
1
assert goal.deleted?
-
-
1
assert_equal 5, check_deleted_spy.calls.size
-
end
-
-
end
-
-
1
describe '#check_deleted' do
-
-
1
it 'should return false when the goal is not deleted and keep variables intact' do
-
1
goal = Porolog::Goal.new args
-
1
goal.variable(:x)
-
1
goal.variable(:y)
-
1
goal.variable(:z)
-
-
1
variable_remove_spy = Spy.on_instance_method(Porolog::Variable, :remove)
-
-
1
refute goal.check_deleted, 'goal should not be deleted'
-
-
1
assert_equal 0, variable_remove_spy.calls.size
-
end
-
-
1
it 'should return true when the goal is deleted and remove all variables' do
-
1
goal = Porolog::Goal.new args
-
1
goal.variable(:x)
-
1
goal.variable(:y)
-
1
goal.variable(:z)
-
-
1
variable_remove_spy = Spy.on_instance_method(Porolog::Variable, :remove)
-
-
1
Porolog::Goal.reset
-
-
1
assert goal.check_deleted, 'goal should be deleted'
-
-
1
assert_equal 3, variable_remove_spy.calls.size
-
end
-
-
end
-
-
1
describe '#terminate!' do
-
-
1
it 'should set the goal to terminate and log the event' do
-
1
goal = Porolog::Goal.new args
-
-
1
assert goal.terminate!, 'the goal should be set to terminate'
-
1
assert_equal ['terminating'], goal.log
-
end
-
-
end
-
-
1
describe '#terminated?' do
-
-
1
it 'should return whether the goal is set to terminate or not' do
-
1
goal = Porolog::Goal.new args
-
-
1
refute goal.terminated?, 'the goal should not be initialized to terminate'
-
1
assert goal.terminate!, 'the goal should be set to terminate'
-
1
assert goal.terminated?, 'the goal should be set to terminate'
-
end
-
-
end
-
-
1
describe '#variablise' do
-
-
1
it 'should convert a Symbol into a Variable' do
-
1
assert_Variable goal.variablise(:k), :k, goal, [], []
-
end
-
-
1
it 'should return a Variable as is' do
-
1
v = Porolog::Variable.new :r, goal
-
1
assert_equal v, goal.variablise(v)
-
end
-
-
1
it 'should convert an Array into a Variables and Objects' do
-
1
assert_equal [goal.value(1),goal.variable(:a),goal.value('word')], goal.variablise([1,:a,'word'])
-
end
-
-
1
it 'should return a duplicate of an Arguments' do
-
1
duplicate = goal.variablise(args)
-
1
assert_Arguments duplicate, :p, goal.variablise([:x, :y])
-
1
refute_equal args, duplicate
-
end
-
-
1
it 'should convert a Tail into a Tail with a variablised value' do
-
1
assert_Tail goal.variablise(Porolog::Tail.new :m), '*Goal1.:m'
-
end
-
-
1
it 'should return a Value as is' do
-
1
v = Porolog::Value.new(45, goal)
-
1
assert_equal v, goal.variablise(v)
-
end
-
-
1
it 'should return an unknown array as is' do
-
1
assert_equal Porolog::UNKNOWN_ARRAY, goal.variablise(Porolog::UNKNOWN_ARRAY)
-
end
-
-
1
it 'should return an unknown tail as is' do
-
1
assert_equal Porolog::UNKNOWN_TAIL, goal.variablise(Porolog::UNKNOWN_TAIL)
-
end
-
-
1
it 'should convert any other Object into a Value' do
-
1
assert_Value goal.variablise(23.61), 23.61, goal
-
end
-
-
end
-
-
1
describe '#variables' do
-
-
1
it 'should return a Hash of variables and their values' do
-
1
goal = Porolog::Goal.new args
-
1
goal.variable(:x)
-
1
goal.variable(:y)
-
1
goal.variable(:z)
-
-
1
variables = goal.variables
-
-
1
assert_instance_of Hash, variables
-
-
1
assert_Goal goal, :p, [:x,:y]
-
1
assert_Goal_variables goal, { x: nil, y: nil, z: nil }, [
-
'Goal1.:x',
-
'Goal1.:y',
-
'Goal1.:z',
-
].join("\n")
-
end
-
-
end
-
-
1
describe '#inspect_variables' do
-
-
1
it 'should return a string showing the instantiations of the variables of the goal' do
-
# -- Initial Goal --
-
1
goal = Porolog::Goal.new args
-
-
1
x = goal.variable(:x)
-
1
y = goal.variable(:y)
-
1
z = goal.variable(:z)
-
-
expected = [
-
1
'Goal1.:x',
-
'Goal1.:y',
-
'Goal1.:z',
-
].join("\n")
-
-
1
assert_equal expected, goal.inspect_variables
-
-
# -- After First Instantiation --
-
# x.head = y
-
1
x.instantiate y, nil, :head
-
-
expected = [
-
1
'Goal1.:x',
-
' [:head]Goal1.:y',
-
'Goal1.:y',
-
' Goal1.:x[:head]',
-
'Goal1.:z',
-
].join("\n")
-
-
1
assert_equal expected, goal.inspect_variables
-
-
# -- After Second Instantiation --
-
# x.head = y
-
# x.tail = z
-
1
x.instantiate z, nil, :tail
-
-
expected = [
-
1
'Goal1.:x',
-
' [:head]Goal1.:y',
-
' [:tail]Goal1.:z',
-
'Goal1.:y',
-
' Goal1.:x[:head]',
-
' [:tail]Goal1.:z',
-
'Goal1.:z',
-
' Goal1.:x[:tail]',
-
' [:head]Goal1.:y',
-
].join("\n")
-
-
1
assert_equal expected, goal.inspect_variables
-
-
# -- After Third Instantiation --
-
# x = [*y, *z]
-
# y = 1,2,3
-
1
y.instantiate goal.value([1,2,3])
-
-
expected = [
-
1
'Goal1.:x',
-
' [:head]Goal1.:y',
-
' Goal1.[1, 2, 3]',
-
' [:tail]Goal1.:z',
-
'Goal1.:y',
-
' Goal1.:x[:head]',
-
' [:tail]Goal1.:z',
-
' Goal1.[1, 2, 3]',
-
'Goal1.:z',
-
' Goal1.:x[:tail]',
-
' [:head]Goal1.:y',
-
' Goal1.[1, 2, 3]',
-
].join("\n")
-
-
1
assert_equal expected, goal.inspect_variables
-
-
# -- After Fourth Instantiation --
-
# x = [*y, *z]
-
# y = 1,2,3
-
# z = 4,5,6
-
1
z.instantiate goal.value([4,5,6])
-
-
expected = [
-
1
'Goal1.:x',
-
' [:head]Goal1.:y',
-
' Goal1.[1, 2, 3]',
-
' [:tail]Goal1.:z',
-
' Goal1.[4, 5, 6]',
-
'Goal1.:y',
-
' Goal1.:x[:head]',
-
' [:tail]Goal1.:z',
-
' Goal1.[4, 5, 6]',
-
' Goal1.[1, 2, 3]',
-
'Goal1.:z',
-
' Goal1.:x[:tail]',
-
' [:head]Goal1.:y',
-
' Goal1.[1, 2, 3]',
-
' Goal1.[4, 5, 6]',
-
].join("\n")
-
-
1
assert_equal expected, goal.inspect_variables
-
end
-
-
end
-
-
1
describe '#values' do
-
-
1
it 'should return the values that have been associated with the goal' do
-
1
goal = Porolog::Goal.new args
-
-
1
x = goal.variable(:x)
-
1
y = goal.variable(:y)
-
1
z = goal.variable(:z)
-
-
1
x.instantiate y, nil, :head
-
1
x.instantiate z, nil, :tail
-
1
y.instantiate goal.value([1,2,3])
-
1
z.instantiate goal.value([4,5,6])
-
-
expected = [
-
1
[1,2,3],
-
[4,5,6],
-
]
-
-
1
assert_equal expected, goal.values
-
end
-
-
end
-
-
1
describe '#variable' do
-
-
1
it 'should return a non-variable as is' do
-
1
assert_equal 4.132, goal.variable(4.132)
-
end
-
-
1
it 'should create and find a variable from a Symbol or Variable' do
-
1
refute goal.variables.key?(:v), ':v should not be a variable'
-
-
1
variable1 = goal.variable :v
-
-
1
assert goal.variables.key?(:v), ':v should be a variable'
-
-
1
variable2 = goal.variable :v
-
-
1
assert goal.variables.key?(:v), ':v should be a variable'
-
-
1
variable3 = goal.variable variable2
-
-
1
assert_equal variable1, variable2
-
1
assert_equal variable2, variable3
-
end
-
-
end
-
-
1
describe '#value' do
-
-
1
it 'should create and find a value from an Object' do
-
1
refute goal.values.include?(99.02), '99.02 should not be a value'
-
-
1
value1 = goal.value 99.02
-
-
1
assert goal.values.include?(99.02), '99.02 should be a value'
-
-
1
value2 = goal.value 99.02
-
-
1
assert_Value value1, 99.02, goal
-
1
assert_Value value2, 99.02, goal
-
-
1
assert_equal [99.02], goal.values
-
end
-
-
end
-
-
1
describe '#value_of' do
-
-
1
describe 'when name is a Symbol' do
-
-
2
let(:variable_name) { :cymbal }
-
-
1
it 'should create the Variable' do
-
1
refute goal.variables.key?(variable_name), "#{variable_name} should not exist"
-
-
1
refute_nil goal.value_of(variable_name)
-
-
1
assert goal.variables.key?(variable_name), "#{variable_name} should exist"
-
-
1
variable = goal.variable(variable_name)
-
1
variable.instantiate 44.5
-
-
1
assert_equal 44.5, goal.value_of(variable_name)
-
end
-
-
end
-
-
1
describe 'when name is not a Symbol' do
-
-
2
let(:variable_name) { 99.28 }
-
-
1
it 'should return the name as the value' do
-
1
assert_equal 99.28, goal.value_of(variable_name)
-
end
-
-
end
-
-
end
-
-
1
describe '#values_of' do
-
-
1
it 'should return an Array with values_of applied to each element when given an Array' do
-
1
goal.instantiate :thomas, '$10'
-
-
1
expected = [[1,'two'], '$10', 999, 'giraffe', 0.5]
-
-
1
assert_equal expected, goal.values_of([[1,'two'], :thomas, 999, 'giraffe', 0.5])
-
end
-
-
1
it 'should return the value_of the Variable when given a Variable' do
-
1
goal.instantiate :jones, 'Fuzzy'
-
-
1
variable1 = goal.variable(:jones)
-
-
1
assert_equal 'Fuzzy', goal.values_of(variable1)
-
end
-
-
1
it 'should return the value_of the Symbol when given a Symbol' do
-
1
goal.instantiate :x, 'Staunch'
-
-
1
assert_equal 'Staunch', goal.values_of(:x)
-
end
-
-
1
it 'should return the value_of the Value when given a Value' do
-
1
value1 = goal.value(123.76)
-
-
1
assert_equal 123.76, goal.values_of(value1)
-
end
-
-
1
it 'should somehow splat when given a Tail' do
-
1
tail1 = Porolog::Tail.new ['apples','oranges','bananas']
-
-
1
assert_equal 'apples', goal.values_of(tail1)
-
end
-
-
1
it 'should return the Object as is when given an Object' do
-
1
object = Object.new
-
-
1
goal.expects(:value_of).times(0)
-
-
1
assert_equal object, goal.values_of(object)
-
end
-
-
end
-
-
1
describe '#solve' do
-
-
1
it 'should find no solutions for a goal with no rules' do
-
1
goal1 = new_goal :p, :x, :y
-
-
1
solutions = goal1.solve
-
-
1
assert_equal [], solutions
-
end
-
-
1
it 'should solve a fact' do
-
1
Porolog::predicate :fact
-
-
1
fact(42).fact!
-
-
1
solutions = fact(42).solve
-
-
1
assert_equal [{}], solutions
-
end
-
-
1
it 'should not solve a fallacy' do
-
1
Porolog::predicate :fact
-
-
1
fact(42).fallacy!
-
-
1
solutions = fact(42).solve
-
-
1
assert_equal [], solutions
-
end
-
-
1
it 'should solve using head and tail with lists' do
-
1
Porolog::predicate :head_tail
-
-
1
head_tail([1,2,3,4,5,6,7]).fact!
-
1
head_tail(['head','body','foot']).fact!
-
-
1
solutions = head_tail(:head/:tail).solve
-
-
1
assert_equal [
-
{ head: 1, tail: [2,3,4,5,6,7] },
-
{ head: 'head', tail: ['body','foot'] },
-
], solutions
-
end
-
-
1
it 'should solve a goal recursively' do
-
1
Porolog::builtin :write
-
1
Porolog::predicate :recursive
-
-
1
recursive([]) << [:CUT, true]
-
1
recursive(:head/:tail) << [
-
write('(',:head,')'),
-
recursive(:tail)
-
]
-
-
1
assert_output '(1)(2)(3)(four)(5)(6)(7)' do
-
1
solutions = recursive([1,2,3,'four',5,6,7]).solve
-
-
1
assert_equal [{}], solutions
-
end
-
end
-
-
end
-
-
1
describe '#satisfy' do
-
-
5
let(:block) { ->(subgoal){} }
-
-
1
it 'should return false when the goal has no arguments' do
-
1
goal = Porolog::Goal.new nil
-
-
1
block.expects(:call).times(0)
-
-
1
refute goal.satisfy(&block), name
-
end
-
-
1
it 'should access the predicate of its arguments' do
-
1
goal.arguments.expects(:predicate).with().returns(nil).times(1)
-
1
block.expects(:call).times(0)
-
-
1
refute goal.satisfy(&block), name
-
end
-
-
1
it 'should try to satisfy the predicate with itself' do
-
1
pred.expects(:satisfy).with(goal).returns(nil).times(1)
-
1
block.expects(:call).times(0)
-
-
1
refute goal.satisfy(&block), name
-
end
-
-
1
it 'should call the block when the predicate is satisfied' do
-
1
pred.(1,2).fact!
-
1
pred.(3,4).fact!
-
1
pred.(5,6).fact!
-
-
1
block.expects(:call).returns(true).times(3)
-
-
1
assert goal.satisfy(&block), name
-
end
-
-
end
-
-
1
describe '#instantiate' do
-
-
1
it 'creates an Array with a Tail using the slash notation with Symbols with a goal' do
-
1
goal = new_goal :bravo, :x, :y, :z
-
-
1
head_tail = goal.variablise(:head / :tail)
-
-
1
assert_Array_with_Tail head_tail, [goal.variable(:head)], '*Goal1.:tail'
-
-
1
assert_Goal_variables goal, { x: nil, y: nil, z: nil, head: nil, tail: nil }, [
-
'Goal1.:x',
-
'Goal1.:y',
-
'Goal1.:z',
-
'Goal1.:head',
-
'Goal1.:tail',
-
].join("\n")
-
end
-
-
1
it 'creates an Array with a Tail using the slash notation with a single head with a goal' do
-
1
goal = new_goal :bravo, :x, :y, :z
-
-
1
head_tail = goal.variablise([:head] / :tail)
-
-
1
assert_Array_with_Tail head_tail, [goal.variable(:head)], '*Goal1.:tail'
-
-
1
assert_Goal_variables goal, { x: nil, y: nil, z: nil, head: nil, tail: nil }, [
-
'Goal1.:x',
-
'Goal1.:y',
-
'Goal1.:z',
-
'Goal1.:head',
-
'Goal1.:tail',
-
].join("\n")
-
end
-
-
1
it 'creates variables from its contents with a goal' do
-
1
Porolog::predicate :bravo
-
1
arguments = Porolog::Predicate[:bravo].arguments(:x,:y,:z)
-
1
goal = arguments.goal
-
-
1
head_tail = [:alpha,bravo(:a,:b,:c),[:carly]] / [:x,[:y],bravo(:p,:q/:r)]
-
-
1
assert_equal [:alpha,bravo(:a,:b,:c),[:carly], :x,[:y],bravo(:p,:q/:r)].inspect, head_tail.inspect
-
-
1
assert_Goal_variables goal, { x: nil, y: nil, z: nil }, [
-
'Goal1.:x',
-
'Goal1.:y',
-
'Goal1.:z',
-
].join("\n")
-
-
1
head_tail = goal.variablise(head_tail)
-
-
1
assert_equal '[Goal1.:alpha, bravo(Goal1.:a,Goal1.:b,Goal1.:c), [Goal1.:carly], Goal1.:x, [Goal1.:y], bravo(Goal1.:p,[Goal1.:q, *Goal1.:r])]', head_tail.inspect
-
-
1
assert_Goal_variables goal, { x: nil, y: nil, z: nil, alpha: nil, a: nil, b: nil, c: nil, carly: nil, p: nil, q: nil, r: nil }, [
-
'Goal1.:x',
-
'Goal1.:y',
-
'Goal1.:z',
-
'Goal1.:alpha',
-
'Goal1.:a',
-
'Goal1.:b',
-
'Goal1.:c',
-
'Goal1.:carly',
-
'Goal1.:p',
-
'Goal1.:q',
-
'Goal1.:r',
-
].join("\n")
-
end
-
-
1
it 'should combine Array value of head and Array value of tail when it has a goal' do
-
1
head_tail = [:first] / :last
-
-
1
goal.instantiate :first, 'alpha', goal
-
1
goal.instantiate :last, ['omega'], goal
-
-
1
head_tail = goal.values_of(goal.variablise(head_tail))
-
-
1
assert_equal ['alpha','omega'], head_tail
-
end
-
-
end
-
-
1
describe '#inherit_variables' do
-
-
1
it 'should return true for an initial goal' do
-
1
goal = new_goal :goal, :x, :y, :z
-
-
1
assert goal.inherit_variables
-
1
assert_Goal_variables goal, { x: nil, y: nil, z: nil }, [
-
'Goal1.:x',
-
'Goal1.:y',
-
'Goal1.:z',
-
].join("\n")
-
end
-
-
1
it 'should instantiate combined variables of both goals' do
-
1
goal = new_goal :goal, :x, :y, :z
-
1
subgoal = new_goal :subgoal, :a, :b, :c
-
-
1
assert subgoal.inherit_variables(goal)
-
1
assert_Goal_variables goal, { x: nil, y: nil, z: nil, a: nil, b: nil, c: nil }, [
-
'Goal1.:x',
-
' Goal2.:x',
-
'Goal1.:y',
-
' Goal2.:y',
-
'Goal1.:z',
-
' Goal2.:z',
-
'Goal1.:a',
-
' Goal2.:a',
-
'Goal1.:b',
-
' Goal2.:b',
-
'Goal1.:c',
-
' Goal2.:c',
-
].join("\n")
-
1
assert_Goal_variables subgoal, { a: nil, b: nil, c: nil, x: nil, y: nil, z: nil }, [
-
'Goal2.:a',
-
' Goal1.:a',
-
'Goal2.:b',
-
' Goal1.:b',
-
'Goal2.:c',
-
' Goal1.:c',
-
'Goal2.:x',
-
' Goal1.:x',
-
'Goal2.:y',
-
' Goal1.:y',
-
'Goal2.:z',
-
' Goal1.:z',
-
].join("\n")
-
end
-
-
1
it 'should not make any instantiations when variables cannot be unified' do
-
1
goal = new_goal :goal, :x, :y, :z
-
1
subgoal = new_goal :subgoal, :a, :b, :c
-
-
1
goal[:x].instantiate 1
-
1
subgoal[:x].instantiate 2
-
-
1
refute subgoal.inherit_variables(goal)
-
1
assert_equal [
-
'Cannot unify because 1 != 2 (variable != variable)',
-
], goal.log
-
1
assert_equal [
-
'Cannot unify because 1 != 2 (variable != variable)',
-
"Couldn't unify: :x WITH Goal1 AND Goal2"
-
], subgoal.log
-
1
assert_Goal_variables goal, { x: 1, y: nil, z: nil }, [
-
'Goal1.:x',
-
' Goal1.1',
-
'Goal1.:y',
-
'Goal1.:z',
-
].join("\n")
-
1
assert_Goal_variables subgoal, { a: nil, b: nil, c: nil, x: 2 }, [
-
'Goal2.:a',
-
'Goal2.:b',
-
'Goal2.:c',
-
'Goal2.:x',
-
' Goal2.2',
-
].join("\n")
-
end
-
-
end
-
-
end
-
-
end
-
#
-
# test/porolog/instantiation_test.rb - Test Suite for Porolog::Instantiation
-
#
-
# Luis Esteban 2 May 2018
-
# created
-
#
-
-
1
require_relative '../test_helper'
-
-
1
describe 'Porolog' do
-
-
1
before(:all) do
-
64
reset
-
end
-
-
1
describe 'Instantiation' do
-
-
64
let(:goal1) { new_goal :generic, :m, :n }
-
63
let(:goal2) { new_goal :specifc, :p, :q }
-
22
let(:goal3) { new_goal :other, :x, :y }
-
63
let(:variable1) { goal1.variable(:x) }
-
63
let(:variable2) { goal2.variable(:y) }
-
22
let(:variable3) { goal3.variable(:z) }
-
4
let(:variable4) { goal3.variable(:v) }
-
27
let(:instantiation) { Porolog::Instantiation.new variable1, nil, variable2, nil }
-
-
1
describe '.reset' do
-
-
1
it 'should delete/unregister all instantiations' do
-
1
assert_equal 0, Porolog::Instantiation.instantiations.size
-
-
1
Porolog::Instantiation.new variable1, nil, variable2, nil
-
1
Porolog::Instantiation.new variable1, nil, variable2, 2
-
1
Porolog::Instantiation.new variable1, nil, variable3, nil
-
1
Porolog::Instantiation.new variable1, 1, variable3, nil
-
1
Porolog::Instantiation.new variable1, nil, variable2, :head
-
-
1
assert_equal 5, Porolog::Instantiation.instantiations.size
-
-
1
assert Porolog::Instantiation.reset, name
-
-
1
assert_equal 0, Porolog::Instantiation.instantiations.size
-
end
-
-
end
-
-
1
describe '.instantiations' do
-
-
1
it 'should be empty to start with' do
-
1
assert_equal({}, Porolog::Instantiation.instantiations)
-
end
-
-
1
it 'should return all registered instantiations' do
-
1
instantiation1 = Porolog::Instantiation.new variable1, nil, variable2, nil
-
1
instantiation2 = Porolog::Instantiation.new variable1, nil, variable2, 2
-
1
instantiation3 = Porolog::Instantiation.new variable1, nil, variable3, nil
-
1
instantiation4 = Porolog::Instantiation.new variable1, 1, variable3, nil
-
1
instantiation5 = Porolog::Instantiation.new variable2, nil, variable3, nil
-
-
1
assert_equal(
-
{
-
instantiation1.signature => instantiation1,
-
instantiation2.signature => instantiation2,
-
instantiation3.signature => instantiation3,
-
instantiation4.signature => instantiation4,
-
instantiation5.signature => instantiation5,
-
},
-
Porolog::Instantiation.instantiations
-
)
-
end
-
-
end
-
-
1
describe '.new' do
-
-
1
it 'should create a new instantiation' do
-
1
instantiation = Porolog::Instantiation.new variable1, nil, variable2, nil
-
-
1
assert_instance_of Porolog::Instantiation, instantiation
-
end
-
-
1
it 'should not create duplicate instantiations' do
-
1
instantiation1 = Porolog::Instantiation.new variable1, nil, variable2, nil
-
1
instantiation2 = Porolog::Instantiation.new variable1, nil, variable2, 2
-
1
instantiation3 = Porolog::Instantiation.new variable1, nil, variable3, nil
-
1
instantiation4 = Porolog::Instantiation.new variable1, 1, variable3, nil
-
1
instantiation5 = Porolog::Instantiation.new variable1, nil, variable2, nil
-
-
1
assert_equal instantiation1, instantiation5
-
1
assert_equal(
-
{
-
[variable1, nil, variable2, nil] => instantiation1,
-
[variable1, nil, variable2, 2 ] => instantiation2,
-
[variable1, 1, variable3, nil] => instantiation4,
-
[variable1, nil, variable3, nil] => instantiation3,
-
},
-
Porolog::Instantiation.instantiations
-
)
-
end
-
-
end
-
-
1
describe '#initialize' do
-
-
1
it 'should initialize variable1, variable2, index1, and index2' do
-
1
instantiation = Porolog::Instantiation.new variable1, nil, variable2, nil
-
-
1
assert_Instantiation instantiation, variable1, variable2, nil, nil
-
end
-
-
1
it 'should append itself to its variables' do
-
1
instantiation = Porolog::Instantiation.new variable1, nil, variable2, nil
-
-
1
assert_equal [instantiation], variable1.instantiations
-
1
assert_equal [instantiation], variable2.instantiations
-
end
-
-
1
it 'should register the instantiation' do
-
1
instantiation = Porolog::Instantiation.new variable1, nil, variable2, nil
-
-
1
assert_equal [instantiation], Porolog::Instantiation.instantiations.values
-
1
assert_equal [[variable1, nil, variable2, nil]], Porolog::Instantiation.instantiations.keys
-
1
assert_equal(
-
{
-
[variable1, nil, variable2, nil] => instantiation
-
},
-
Porolog::Instantiation.instantiations
-
)
-
end
-
-
end
-
-
1
describe '#signature' do
-
-
1
it 'should return the signature of the instantiation' do
-
1
instantiation1 = Porolog::Instantiation.new variable1, nil, variable2, nil
-
1
instantiation2 = Porolog::Instantiation.new variable1, 4, variable2, :head
-
-
1
assert_equal [variable1, nil, variable2, nil ], instantiation1.signature
-
1
assert_equal [variable1, 4, variable2, :head], instantiation2.signature
-
end
-
-
end
-
-
1
describe '#inspect' do
-
-
1
it 'should show its variables and their indices' do
-
1
instantiation1 = Porolog::Instantiation.new variable1, nil, variable2, nil
-
1
instantiation2 = Porolog::Instantiation.new variable1, 4, variable2, :head
-
-
1
assert_equal 'Goal1.:x = Goal2.:y', instantiation1.inspect
-
1
assert_equal 'Goal1.:x[4] = Goal2.:y[:head]', instantiation2.inspect
-
end
-
-
end
-
-
1
describe '#remove' do
-
-
1
it 'should remove itself from instantiations of both variables' do
-
1
instantiation = Porolog::Instantiation.new variable1, nil, variable2, nil
-
-
1
assert_equal [instantiation], variable1.instantiations
-
1
assert_equal [instantiation], variable2.instantiations
-
-
1
instantiation.remove
-
-
1
assert_equal [], variable1.instantiations
-
1
assert_equal [], variable2.instantiations
-
end
-
-
1
it 'should be marked as deleted' do
-
1
instantiation = Porolog::Instantiation.new variable1, nil, variable2, nil
-
-
1
refute instantiation.deleted?, 'instantiation should not be marked deleted'
-
-
1
instantiation.remove
-
-
1
assert instantiation.deleted?, 'instantiation should be marked deleted'
-
end
-
-
1
it 'should unregister itself' do
-
1
assert_equal [], Porolog::Instantiation.instantiations.values
-
-
1
instantiation = Porolog::Instantiation.new variable1, nil, variable2, nil
-
-
1
assert_equal [instantiation], Porolog::Instantiation.instantiations.values
-
1
assert_equal [[variable1, nil, variable2, nil]], Porolog::Instantiation.instantiations.keys
-
1
assert_equal(
-
{
-
[variable1, nil, variable2, nil] => instantiation
-
},
-
Porolog::Instantiation.instantiations
-
)
-
-
1
instantiation.remove
-
-
1
assert_equal [], Porolog::Instantiation.instantiations.values
-
1
assert_equal [], Porolog::Instantiation.instantiations.keys
-
1
assert_equal(
-
{
-
},
-
Porolog::Instantiation.instantiations
-
)
-
end
-
-
end
-
-
1
describe '#other_goal_to' do
-
-
1
it 'should return the goal of the other variable' do
-
1
instantiation1 = Porolog::Instantiation.new variable1, nil, variable2, nil
-
1
instantiation2 = Porolog::Instantiation.new variable2, nil, variable3, nil
-
-
1
assert_equal goal2, instantiation1.other_goal_to(variable1)
-
1
assert_equal goal1, instantiation1.other_goal_to(variable2)
-
1
assert_nil instantiation1.other_goal_to(variable3)
-
1
assert_nil instantiation2.other_goal_to(variable1)
-
1
assert_equal goal3, instantiation2.other_goal_to(variable2)
-
1
assert_equal goal2, instantiation2.other_goal_to(variable3)
-
end
-
-
end
-
-
1
describe '#goals' do
-
-
1
it 'should return the goals' do
-
1
instantiation1 = Porolog::Instantiation.new variable1, nil, variable2, nil
-
1
instantiation2 = Porolog::Instantiation.new variable1, 4, variable3, :head
-
-
1
assert_equal [goal1, goal2], instantiation1.goals
-
1
assert_equal [goal1, goal3], instantiation2.goals
-
end
-
-
end
-
-
1
describe '#variables' do
-
-
1
it 'should return both variables' do
-
1
instantiation1 = Porolog::Instantiation.new variable1, nil, variable2, nil
-
1
instantiation2 = Porolog::Instantiation.new variable2, nil, variable3, nil
-
-
1
assert_equal [variable1,variable2], instantiation1.variables
-
1
assert_equal [variable2,variable3], instantiation2.variables
-
end
-
-
end
-
-
1
describe '#values' do
-
-
1
it 'should return all values instantiated to its variables' do
-
1
instantiation1 = Porolog::Instantiation.new variable1, nil, variable2, nil
-
1
instantiation2 = Porolog::Instantiation.new variable2, nil, variable3, nil
-
-
1
assert_equal [], instantiation1.values
-
1
assert_equal [], instantiation2.values
-
-
1
variable3.instantiate Porolog::Value.new('word',goal3)
-
-
1
assert_equal ['word'], instantiation1.values
-
1
assert_equal ['word'], instantiation2.values
-
end
-
-
1
it 'should return all indexed values instantiated to its variables' do
-
1
instantiation1 = Porolog::Instantiation.new variable1, nil, variable2, 3
-
1
instantiation2 = Porolog::Instantiation.new variable2, 4, variable3, nil
-
-
1
assert_equal [], instantiation1.values
-
1
assert_equal [], instantiation2.values
-
-
1
variable2.instantiate Porolog::Value.new([2,3,5,7,11,13,17,19,23],goal2)
-
-
1
assert_equal 7, variable1.value
-
1
assert_equal [2,3,5,7,11,13,17,19,23], variable2.value
-
1
assert_equal 11, variable3.value
-
-
1
assert_equal [7], instantiation1.values
-
1
assert_equal [11], instantiation2.values
-
-
1
variable2.uninstantiate goal2
-
-
1
assert_equal variable1, variable1.value
-
1
assert_equal variable2, variable2.value
-
1
assert_equal variable3, variable3.value
-
-
1
assert_equal [], instantiation1.values
-
1
assert_equal [], instantiation2.values
-
-
1
words = %w{two three five seven eleven thirteen seventeen nineteen twenty-three}
-
1
variable2.instantiate Porolog::Value.new(words, goal2)
-
-
1
assert_equal 'seven', variable1.value
-
1
assert_equal words, variable2.value
-
1
assert_equal 'eleven', variable3.value
-
-
1
assert_equal ['seven'], instantiation1.values
-
1
assert_equal ['eleven'], instantiation2.values
-
end
-
-
end
-
-
1
describe '#value_indexed' do
-
-
1
it 'should return unknown tail when value has an unknown tail and the index is greater than the fixed portion of the list' do
-
1
assert_equal Porolog::UNKNOWN_TAIL, instantiation.value_indexed([2, 3, 5, 7, Porolog::UNKNOWN_TAIL], 99)
-
end
-
-
1
it 'should return indexed element when value has an unknown tail and the index is less than the fixed portion of the list' do
-
1
assert_equal 5, instantiation.value_indexed([2, 3, 5, 7, Porolog::UNKNOWN_TAIL], 2)
-
end
-
-
1
it 'should return the component when value is a Variable and the index is a Symbol' do
-
1
value = goal1.value([2, 3, 5, 7, Porolog::UNKNOWN_TAIL])
-
-
1
assert_equal 2, instantiation.value_indexed(value,:head)
-
1
assert_equal [3, 5, 7, Porolog::UNKNOWN_TAIL], instantiation.value_indexed(value,:tail)
-
end
-
-
1
it 'should return nil when value is nil' do
-
1
assert_nil instantiation.value_indexed(nil,nil)
-
1
assert_nil instantiation.value_indexed(nil,4)
-
1
assert_nil instantiation.value_indexed(nil,:head)
-
1
assert_nil instantiation.value_indexed(nil,:tail)
-
end
-
-
1
it 'should return nil when value is a Variable' do
-
1
assert_nil instantiation.value_indexed(variable3,nil)
-
1
assert_nil instantiation.value_indexed(variable3,4)
-
1
assert_nil instantiation.value_indexed(variable3,:head)
-
1
assert_nil instantiation.value_indexed(variable3,:tail)
-
end
-
-
1
it 'should return nil when value is a Symbol' do
-
1
assert_nil instantiation.value_indexed(:z,nil)
-
1
assert_nil instantiation.value_indexed(:z,4)
-
1
assert_nil instantiation.value_indexed(:z,:head)
-
1
assert_nil instantiation.value_indexed(:z,:tail)
-
end
-
-
1
it 'should return nil when value is an empty Array and index is :tail' do
-
1
assert_nil instantiation.value_indexed([],:tail)
-
end
-
-
1
it 'should return the indexed value when index is an Integer' do
-
1
assert_equal 7, instantiation.value_indexed([2,3,5,7,11],3)
-
1
assert_equal 14, instantiation.value_indexed(14,3)
-
1
assert_equal 'd', instantiation.value_indexed('word',3)
-
1
assert_equal 6.4, instantiation.value_indexed(6.4,3)
-
end
-
-
1
it 'should return the component when index is a Symbol' do
-
1
assert_equal 5, instantiation.value_indexed([2,3,5,7,11],:length)
-
1
assert_equal 11, instantiation.value_indexed([2,3,5,7,11],:last)
-
1
assert_equal [2,3,5,7,11], instantiation.value_indexed([2,3,5,7,11],:nothing)
-
end
-
-
1
it 'should return the dynamically sized head when index is an Array' do
-
1
assert_equal [3,5,7,11], instantiation.value_indexed([2,3,5,7,11],[])
-
1
assert_equal [], instantiation.value_indexed([2,3,5,7,11],[0])
-
1
assert_equal [2], instantiation.value_indexed([2,3,5,7,11],[1])
-
1
assert_equal [2,3], instantiation.value_indexed([2,3,5,7,11],[2])
-
1
assert_equal [2], instantiation.value_indexed([2,3,5,7,11],[1,1])
-
end
-
-
1
it 'should raise an error when index is unexpected' do
-
1
assert_raises Porolog::Instantiation::UnhandledIndexError do
-
1
assert_equal [2,3,5,7,11], instantiation.value_indexed([2,3,5,7,11],12.34)
-
end
-
1
assert_raises Porolog::Instantiation::UnhandledIndexError do
-
1
assert_equal [2,3,5,7,11], instantiation.value_indexed([2,3,5,7,11],'hello')
-
end
-
1
assert_raises Porolog::Instantiation::UnhandledIndexError do
-
1
assert_equal [2,3,5,7,11], instantiation.value_indexed([2,3,5,7,11],Object)
-
end
-
1
object = Object.new
-
1
assert_raises Porolog::Instantiation::UnhandledIndexError do
-
1
assert_equal [2,3,5,7,11], instantiation.value_indexed([2,3,5,7,11],object)
-
end
-
end
-
-
end
-
-
1
describe '#values_for' do
-
-
1
it 'should return all values instantiated to the specified variable' do
-
1
instantiation1 = Porolog::Instantiation.new variable1, nil, variable2, nil
-
1
instantiation2 = Porolog::Instantiation.new variable2, nil, variable3, nil
-
-
1
assert_equal [], instantiation1.values_for(variable1)
-
1
assert_equal [], instantiation1.values_for(variable2)
-
1
assert_equal [], instantiation1.values_for(variable3)
-
1
assert_equal [], instantiation2.values_for(variable1)
-
1
assert_equal [], instantiation2.values_for(variable2)
-
1
assert_equal [], instantiation2.values_for(variable3)
-
-
1
variable3.instantiate Porolog::Value.new('word',goal3)
-
-
1
assert_Goal_variables goal1, { m: nil, n: nil, x: 'word' }, [
-
'Goal1.:m',
-
'Goal1.:n',
-
'Goal1.:x',
-
' Goal2.:y',
-
' Goal3.:z',
-
' Goal3."word"',
-
].join("\n")
-
1
assert_Goal_variables goal2, { p: nil, q: nil, y: 'word' }, [
-
'Goal2.:p',
-
'Goal2.:q',
-
'Goal2.:y',
-
' Goal1.:x',
-
' Goal3.:z',
-
' Goal3."word"',
-
].join("\n")
-
1
assert_Goal_variables goal3, { x: nil, y: nil, z: 'word' }, [
-
'Goal3.:x',
-
'Goal3.:y',
-
'Goal3.:z',
-
' Goal2.:y',
-
' Goal1.:x',
-
' Goal3."word"',
-
].join("\n")
-
-
1
assert_equal ['word'], instantiation1.values_for(variable1)
-
1
assert_equal [], instantiation1.values_for(variable2)
-
1
assert_equal [], instantiation1.values_for(variable3)
-
1
assert_equal [], instantiation2.values_for(variable1)
-
1
assert_equal ['word'], instantiation2.values_for(variable2)
-
1
assert_equal [], instantiation2.values_for(variable3)
-
end
-
-
1
it 'should return all indexed values instantiated to its variables' do
-
1
instantiation1 = Porolog::Instantiation.new variable1, nil, variable2, 3
-
1
instantiation2 = Porolog::Instantiation.new variable2, 4, variable3, nil
-
-
1
assert_equal [], instantiation1.values_for(variable1)
-
1
assert_equal [], instantiation1.values_for(variable2)
-
1
assert_equal [], instantiation2.values_for(variable2)
-
1
assert_equal [], instantiation2.values_for(variable3)
-
-
1
variable2.instantiate Porolog::Value.new([2,3,5,7,11,13,17,19,23],goal2)
-
-
1
assert_equal 7, variable1.value
-
1
assert_equal [2,3,5,7,11,13,17,19,23], variable2.value
-
1
assert_equal 11, variable3.value
-
-
1
assert_equal [7], instantiation1.values_for(variable1)
-
1
assert_equal [], instantiation1.values_for(variable2)
-
1
assert_equal [], instantiation2.values_for(variable2)
-
1
assert_equal [11], instantiation2.values_for(variable3)
-
-
1
variable2.uninstantiate goal2
-
-
1
assert_equal variable1, variable1.value
-
1
assert_equal variable2, variable2.value
-
1
assert_equal variable3, variable3.value
-
-
1
assert_equal [], instantiation1.values
-
1
assert_equal [], instantiation2.values
-
-
1
words = %w{two three five seven eleven thirteen seventeen nineteen twenty-three}
-
1
variable2.instantiate Porolog::Value.new(words, goal2)
-
-
1
assert_equal 'seven', variable1.value
-
1
assert_equal words, variable2.value
-
1
assert_equal 'eleven', variable3.value
-
-
1
assert_equal ['seven'], instantiation1.values
-
1
assert_equal ['eleven'], instantiation2.values
-
end
-
-
1
it 'should not infinitely recurse' do
-
1
instantiation1 = Porolog::Instantiation.new variable1, nil, variable2, nil
-
1
instantiation2 = Porolog::Instantiation.new variable2, nil, variable3, nil
-
1
instantiation3 = Porolog::Instantiation.new variable3, nil, variable1, nil
-
-
1
assert_equal [], instantiation1.values_for(variable1)
-
1
assert_equal [], instantiation1.values_for(variable2)
-
1
assert_equal [], instantiation2.values_for(variable2)
-
1
assert_equal [], instantiation2.values_for(variable3)
-
1
assert_equal [], instantiation3.values_for(variable3)
-
1
assert_equal [], instantiation3.values_for(variable1)
-
end
-
-
1
it 'should return an uninstantiated tail when the head is known only' do
-
1
instantiation1 = Porolog::Instantiation.new variable1, :head, variable2, nil
-
1
instantiation2 = Porolog::Instantiation.new variable2, nil, variable3, nil
-
1
instantiation3 = Porolog::Instantiation.new variable3, nil, variable4, nil
-
-
1
goal3.instantiate :v, 'head', goal3
-
-
1
assert_Goal_variables goal1, { m: nil, n: nil, x: [goal3.value('head'), Porolog::UNKNOWN_TAIL] }, [
-
'Goal1.:m',
-
'Goal1.:n',
-
'Goal1.:x',
-
' [:head]Goal2.:y',
-
' Goal3.:z',
-
' Goal3.:v',
-
' Goal3."head"',
-
].join("\n")
-
-
1
assert_Goal_variables goal3, { x: nil, y: nil, z: 'head', v: 'head' }, [
-
'Goal3.:x',
-
'Goal3.:y',
-
'Goal3.:z',
-
' Goal2.:y',
-
' Goal1.:x[:head]',
-
' Goal3.:v',
-
' Goal3."head"',
-
'Goal3.:v',
-
' Goal3.:z',
-
' Goal2.:y',
-
' Goal1.:x[:head]',
-
' Goal3."head"',
-
].join("\n")
-
-
1
assert_equal [['head', Porolog::UNKNOWN_TAIL]], instantiation1.values_for(variable1)
-
1
assert_equal [], instantiation1.values_for(variable2)
-
1
assert_equal ['head'], instantiation2.values_for(variable2)
-
1
assert_equal [], instantiation2.values_for(variable3)
-
1
assert_equal ['head'], instantiation3.values_for(variable3)
-
1
assert_equal [], instantiation3.values_for(variable4)
-
end
-
-
1
it 'should return an uninstantiated head when the tail is known only' do
-
1
instantiation1 = Porolog::Instantiation.new variable1, :tail, variable2, nil
-
1
instantiation2 = Porolog::Instantiation.new variable2, nil, variable3, nil
-
1
instantiation3 = Porolog::Instantiation.new variable3, nil, variable4, nil
-
-
1
goal3.instantiate :v, ['this','is','the','tail'], goal3
-
-
1
assert_Goal_variables goal1, { m: nil, n: nil, x: [nil, 'this','is','the','tail'] }, [
-
'Goal1.:m',
-
'Goal1.:n',
-
'Goal1.:x',
-
' [:tail]Goal2.:y',
-
' Goal3.:z',
-
' Goal3.:v',
-
' Goal3.["this", "is", "the", "tail"]',
-
].join("\n")
-
-
1
assert_Goal_variables goal3, { x: nil, y: nil, z: ['this','is','the','tail'], v: ['this','is','the','tail'] }, [
-
'Goal3.:x',
-
'Goal3.:y',
-
'Goal3.:z',
-
' Goal2.:y',
-
' Goal1.:x[:tail]',
-
' Goal3.:v',
-
' Goal3.["this", "is", "the", "tail"]',
-
'Goal3.:v',
-
' Goal3.:z',
-
' Goal2.:y',
-
' Goal1.:x[:tail]',
-
' Goal3.["this", "is", "the", "tail"]',
-
].join("\n")
-
-
1
assert_equal [[nil, 'this','is','the','tail']], instantiation1.values_for(variable1)
-
1
assert_equal [], instantiation1.values_for(variable2)
-
1
assert_equal [goal3.value(['this','is','the','tail'])], instantiation2.values_for(variable2)
-
1
assert_equal [], instantiation2.values_for(variable3)
-
1
assert_equal [goal3.value(['this','is','the','tail'])], instantiation3.values_for(variable3)
-
1
assert_equal [], instantiation3.values_for(variable4)
-
end
-
-
1
it 'should return a list with uninstantiated elements and tail with the value at the index' do
-
1
instantiation1 = Porolog::Instantiation.new variable1, 3, variable2, nil
-
1
instantiation2 = Porolog::Instantiation.new variable2, nil, variable3, nil
-
1
instantiation3 = Porolog::Instantiation.new variable3, nil, variable4, nil
-
-
1
goal3.instantiate :v, 45.9, goal3
-
-
1
assert_Goal_variables goal1, { m: nil, n: nil, x: [nil, nil, nil, goal3.value(45.9), Porolog::UNKNOWN_TAIL] }, [
-
'Goal1.:m',
-
'Goal1.:n',
-
'Goal1.:x',
-
' [3]Goal2.:y',
-
' Goal3.:z',
-
' Goal3.:v',
-
' Goal3.45.9',
-
].join("\n")
-
-
1
assert_Goal_variables goal3, { x: nil, y: nil, z: 45.9, v: 45.9 }, [
-
'Goal3.:x',
-
'Goal3.:y',
-
'Goal3.:z',
-
' Goal2.:y',
-
' Goal1.:x[3]',
-
' Goal3.:v',
-
' Goal3.45.9',
-
'Goal3.:v',
-
' Goal3.:z',
-
' Goal2.:y',
-
' Goal1.:x[3]',
-
' Goal3.45.9',
-
].join("\n")
-
-
1
assert_equal [[nil, nil, nil, 45.9, Porolog::UNKNOWN_TAIL]], instantiation1.values_for(variable1)
-
1
assert_equal [], instantiation1.values_for(variable2)
-
1
assert_equal [goal3.value(45.9)], instantiation2.values_for(variable2)
-
1
assert_equal [], instantiation2.values_for(variable3)
-
1
assert_equal [goal3.value(45.9)], instantiation3.values_for(variable3)
-
1
assert_equal [], instantiation3.values_for(variable4)
-
end
-
-
1
it 'should return the value of variable1 when the specified variable is variable2 and variable1 is not a Variable' do
-
1
value1 = goal1.value('titanium')
-
-
1
instantiation1 = Porolog::Instantiation.new value1, nil, variable3, nil
-
-
1
assert_equal [value1], instantiation1.values_for(variable3)
-
end
-
-
1
it 'should return the value of variable1 given the instantiation of the flathead of variable1' do
-
1
value1 = goal1.value([1,2,3,4])
-
-
1
instantiation1 = variable1.instantiate variable2, nil, :flathead
-
1
variable2.instantiate value1
-
-
1
assert_equal [[1, 2, 3, 4, Porolog::UNKNOWN_TAIL]], instantiation1.values_for(variable1)
-
end
-
-
1
it 'should return the value of variable1 given the instantiation of the flathead of variable2' do
-
1
value1 = goal1.value([1,2,3,4])
-
-
1
instantiation1 = variable2.instantiate variable1, :flathead
-
1
variable2.instantiate value1
-
-
1
assert_equal [[1, 2, 3, 4, Porolog::UNKNOWN_TAIL]], instantiation1.values_for(variable1)
-
end
-
-
1
it 'should return the value of variable1 given the instantiation of the flattail of variable1' do
-
1
value1 = goal1.value([1,2,3,4])
-
-
1
instantiation1 = variable1.instantiate variable2, nil, :flattail
-
1
variable2.instantiate value1
-
-
1
assert_equal [[Porolog::UNKNOWN_TAIL, 1, 2, 3, 4]], instantiation1.values_for(variable1)
-
end
-
-
1
it 'should return the value of variable1 given the instantiation of the flattail of variable2' do
-
1
value1 = goal1.value([1,2,3,4])
-
-
1
instantiation1 = variable2.instantiate variable1, :flattail
-
1
variable2.instantiate value1
-
-
1
assert_equal [[Porolog::UNKNOWN_TAIL, 1, 2, 3, 4]], instantiation1.values_for(variable1)
-
end
-
-
end
-
-
1
describe '#value_at_index' do
-
-
1
describe 'when index is nil' do
-
-
1
it 'should return nil for a nil value' do
-
1
assert_nil instantiation.value_at_index(nil,nil)
-
end
-
-
1
it 'should return the value for a non-nil value' do
-
1
assert_equal 'orange', instantiation.value_at_index('orange',nil)
-
end
-
-
end
-
-
1
describe 'when index is Integer' do
-
-
1
it 'should return nil for nil value' do
-
1
assert_nil instantiation.value_at_index(nil,5)
-
end
-
-
1
it 'should return an Array for an Integer indexed value' do
-
1
assert_equal [nil, nil, nil, nil, nil, 'word', Porolog::UNKNOWN_TAIL], instantiation.value_at_index('word',5)
-
end
-
-
end
-
-
1
describe 'when index is Symbol' do
-
-
1
it 'should return an Array for a head indexed value' do
-
1
assert_equal [['word', 'from', 'list'], Porolog::UNKNOWN_TAIL], instantiation.value_at_index(['word','from','list'],:head)
-
end
-
-
1
it 'should return an Array for a tail indexed value' do
-
1
assert_equal [nil, 'word', 'from', 'list'], instantiation.value_at_index(['word','from','list'],:tail)
-
end
-
-
1
it 'should return an error for an unrecognised symbol indexed value' do
-
1
assert_raises Porolog::Instantiation::UnhandledIndexError do
-
1
assert_equal :error, instantiation.value_at_index(['word','from','list'],:size)
-
end
-
end
-
-
end
-
-
1
describe 'when index is Array' do
-
-
1
it 'should return an Array for a tail indexed value' do
-
1
assert_equal [nil,'word','from','list'], instantiation.value_at_index(['word','from','list'],[])
-
end
-
-
1
it 'should return an Array for a tail indexed value' do
-
1
assert_equal [['word','from','list'], Porolog::UNKNOWN_TAIL], instantiation.value_at_index(['word','from','list'],[1])
-
end
-
-
1
it 'should return an Array for a tail indexed value' do
-
1
assert_equal [['word','from','list'], Porolog::UNKNOWN_TAIL], instantiation.value_at_index(['word','from','list'],[2])
-
end
-
-
1
it 'should return an Array for a tail indexed value' do
-
1
assert_equal [['word','from','list'], Porolog::UNKNOWN_TAIL], instantiation.value_at_index(['word','from','list'],[-2])
-
end
-
-
end
-
-
1
describe 'when index is Float' do
-
-
1
it 'should return an error for an unrecognised symbol indexed value' do
-
1
assert_raises Porolog::Instantiation::UnhandledIndexError do
-
1
assert_equal ['word','from','list'], instantiation.value_at_index(['word','from','list'],2.3)
-
end
-
end
-
-
end
-
-
1
it 'should return all values instantiated to the specified variable' do
-
1
instantiation1 = Porolog::Instantiation.new variable1, nil, variable2, nil
-
1
instantiation2 = Porolog::Instantiation.new variable2, nil, variable3, nil
-
-
1
assert_equal [], instantiation1.values_for(variable1)
-
1
assert_equal [], instantiation1.values_for(variable2)
-
1
assert_equal [], instantiation1.values_for(variable3)
-
1
assert_equal [], instantiation2.values_for(variable1)
-
1
assert_equal [], instantiation2.values_for(variable2)
-
1
assert_equal [], instantiation2.values_for(variable3)
-
-
1
variable3.instantiate Porolog::Value.new('word',goal3)
-
-
1
assert_Goal_variables goal1, { m: nil, n: nil, x: 'word' }, [
-
'Goal1.:m',
-
'Goal1.:n',
-
'Goal1.:x',
-
' Goal2.:y',
-
' Goal3.:z',
-
' Goal3."word"',
-
].join("\n")
-
-
1
assert_Goal_variables goal2, { p: nil, q: nil, y: 'word' }, [
-
'Goal2.:p',
-
'Goal2.:q',
-
'Goal2.:y',
-
' Goal1.:x',
-
' Goal3.:z',
-
' Goal3."word"',
-
].join("\n")
-
-
1
assert_Goal_variables goal3, { x: nil, y: nil, z: 'word' }, [
-
'Goal3.:x',
-
'Goal3.:y',
-
'Goal3.:z',
-
' Goal2.:y',
-
' Goal1.:x',
-
' Goal3."word"',
-
].join("\n")
-
-
1
assert_equal ['word'], instantiation1.values_for(variable1)
-
1
assert_equal [], instantiation1.values_for(variable2)
-
1
assert_equal [], instantiation1.values_for(variable3)
-
1
assert_equal [], instantiation2.values_for(variable1)
-
1
assert_equal ['word'], instantiation2.values_for(variable2)
-
1
assert_equal [], instantiation2.values_for(variable3)
-
end
-
-
1
it 'should return all indexed values instantiated to its variables' do
-
1
instantiation1 = Porolog::Instantiation.new variable1, nil, variable2, 3
-
1
instantiation2 = Porolog::Instantiation.new variable2, 4, variable3, nil
-
-
1
assert_equal [], instantiation1.values_for(variable1)
-
1
assert_equal [], instantiation1.values_for(variable2)
-
1
assert_equal [], instantiation2.values_for(variable2)
-
1
assert_equal [], instantiation2.values_for(variable3)
-
-
1
variable2.instantiate Porolog::Value.new([2,3,5,7,11,13,17,19,23],goal2)
-
-
1
assert_equal 7, variable1.value
-
1
assert_equal [2,3,5,7,11,13,17,19,23], variable2.value
-
1
assert_equal 11, variable3.value
-
-
1
assert_equal [7], instantiation1.values_for(variable1)
-
1
assert_equal [], instantiation1.values_for(variable2)
-
1
assert_equal [], instantiation2.values_for(variable2)
-
1
assert_equal [11], instantiation2.values_for(variable3)
-
-
1
variable2.uninstantiate goal2
-
-
1
assert_equal variable1, variable1.value
-
1
assert_equal variable2, variable2.value
-
1
assert_equal variable3, variable3.value
-
-
1
assert_equal [], instantiation1.values
-
1
assert_equal [], instantiation2.values
-
-
1
words = %w{two three five seven eleven thirteen seventeen nineteen twenty-three}
-
1
variable2.instantiate Porolog::Value.new(words, goal2)
-
-
1
assert_equal 'seven', variable1.value
-
1
assert_equal words, variable2.value
-
1
assert_equal 'eleven', variable3.value
-
-
1
assert_equal ['seven'], instantiation1.values
-
1
assert_equal ['eleven'], instantiation2.values
-
end
-
-
1
it 'should not infinitely recurse' do
-
1
instantiation1 = Porolog::Instantiation.new variable1, nil, variable2, nil
-
1
instantiation2 = Porolog::Instantiation.new variable2, nil, variable3, nil
-
1
instantiation3 = Porolog::Instantiation.new variable3, nil, variable1, nil
-
-
1
assert_equal [], instantiation1.values_for(variable1)
-
1
assert_equal [], instantiation1.values_for(variable2)
-
1
assert_equal [], instantiation2.values_for(variable2)
-
1
assert_equal [], instantiation2.values_for(variable3)
-
1
assert_equal [], instantiation3.values_for(variable3)
-
1
assert_equal [], instantiation3.values_for(variable1)
-
end
-
-
end
-
-
1
describe '#without_index_on' do
-
-
1
it 'should return true for a variable without an index in the instantiation' do
-
1
instantiation = Porolog::Instantiation.new variable1, nil, variable2, nil
-
-
1
assert_equal true, instantiation.without_index_on?(variable1)
-
1
assert_equal true, instantiation.without_index_on?(variable2)
-
end
-
-
1
it 'should return correctly when the instantiation has one index' do
-
1
instantiation = Porolog::Instantiation.new variable1, 3, variable2, nil
-
-
1
assert_equal false, instantiation.without_index_on?(variable1)
-
1
assert_equal true, instantiation.without_index_on?(variable2)
-
end
-
-
1
it 'should return correctly when the instantiation has one index (other one)' do
-
1
instantiation = Porolog::Instantiation.new variable1, nil, variable2, 3
-
-
1
assert_equal true, instantiation.without_index_on?(variable1)
-
1
assert_equal false, instantiation.without_index_on?(variable2)
-
end
-
-
1
it 'should return false when the instantiation has two indexes' do
-
1
instantiation = Porolog::Instantiation.new variable1, 3, variable2, 3
-
-
1
assert_equal false, instantiation.without_index_on?(variable1)
-
1
assert_equal false, instantiation.without_index_on?(variable2)
-
end
-
-
1
it 'should return false for a variable not in the instantiation' do
-
1
instantiation = Porolog::Instantiation.new variable1, nil, variable2, nil
-
-
1
assert_equal false, instantiation.without_index_on?(variable3)
-
end
-
-
end
-
-
1
describe '#deleted?' do
-
-
1
it 'should return true when removed' do
-
1
refute instantiation.deleted?, 'instantiation should not be marked deleted'
-
-
1
instantiation.remove
-
-
1
assert instantiation.deleted?, 'instantiation should be marked deleted'
-
end
-
-
1
it 'should return true when the goal of variable1 is deleted' do
-
1
refute instantiation.deleted?, 'instantiation should not be marked deleted'
-
-
1
Porolog::Goal.goals.delete(goal1)
-
-
1
assert instantiation.deleted?, 'instantiation should be marked deleted'
-
end
-
-
1
it 'should return true when the goal of variable2 is deleted' do
-
1
refute instantiation.deleted?, 'instantiation should not be marked deleted'
-
-
1
Porolog::Goal.goals.delete(goal2)
-
-
1
assert instantiation.deleted?, 'instantiation should be marked deleted'
-
end
-
-
end
-
-
1
describe '#belongs_to?' do
-
-
1
it 'should correctly determine the relationship between an instantition and a goal' do
-
1
instantiation1 = Porolog::Instantiation.new variable1, nil, variable2, nil
-
1
instantiation2 = Porolog::Instantiation.new variable2, nil, variable3, nil
-
-
1
assert instantiation1.belongs_to?(goal1), 'instantiation1 should belong to goal1'
-
1
assert instantiation1.belongs_to?(goal2), 'instantiation1 should belong to goal2'
-
1
refute instantiation1.belongs_to?(goal3), 'instantiation1 should not belong to goal3'
-
1
refute instantiation2.belongs_to?(goal1), 'instantiation2 should not belong to goal1'
-
1
assert instantiation2.belongs_to?(goal2), 'instantiation2 should belong to goal2'
-
1
assert instantiation2.belongs_to?(goal3), 'instantiation2 should belong to goal3'
-
end
-
-
end
-
-
end
-
-
end
-
#
-
# test/porolog/porolog_test.rb - Test Suite for Porolog module
-
#
-
# Luis Esteban 4 May 2018
-
# created
-
#
-
-
1
require_relative '../test_helper'
-
-
1
describe 'Porolog' do
-
-
1
before(:all) do
-
252
reset
-
end
-
-
1
describe 'UNKNOWN_TAIL' do
-
-
1
describe '#inspect' do
-
-
1
it 'should return distinctive notation' do
-
1
assert_equal '...', Porolog::UNKNOWN_TAIL.inspect
-
end
-
-
end
-
-
1
describe '#tail' do
-
-
1
it 'should return itself' do
-
1
assert_equal Porolog::UNKNOWN_TAIL, Porolog::UNKNOWN_TAIL.tail
-
1
assert_equal Porolog::UNKNOWN_TAIL, Porolog::UNKNOWN_TAIL.tail(1)
-
1
assert_equal Porolog::UNKNOWN_TAIL, Porolog::UNKNOWN_TAIL.tail(5)
-
end
-
-
end
-
-
end
-
-
1
describe '#predicate' do
-
-
1
it 'should create a Predicate' do
-
# -- Precondition Baseline --
-
1
assert_equal 0, Porolog::Scope[:default].predicates.size
-
-
# -- Test --
-
1
single_predicate = Porolog::predicate :single
-
-
# -- Compare Result Against Baseline --
-
1
assert_equal 1, Porolog::Scope[:default].predicates.size
-
1
assert_equal :single, Porolog::Scope[:default].predicates.first.name
-
1
assert_Predicate single_predicate, :single, []
-
end
-
-
1
it 'should define a method to create Arguments for solving' do
-
1
refute respond_to?(:delta)
-
-
1
Porolog::predicate :delta
-
-
1
assert respond_to?(:delta)
-
1
assert_Arguments delta(1, :X, ['left','right']), :delta, [1, :X, ['left','right']]
-
end
-
-
1
it 'should create multiple Predicates' do
-
1
assert_equal 0, Porolog::Scope[:default].predicates.size
-
-
1
multiple_predicates = Porolog::predicate :alpha, :beta, :gamma
-
-
1
assert_equal 3, Porolog::Scope[:default].predicates.size
-
1
assert_equal :alpha, Porolog::Scope[:default].predicates[0].name
-
1
assert_equal :beta, Porolog::Scope[:default].predicates[1].name
-
1
assert_equal :gamma, Porolog::Scope[:default].predicates[2].name
-
1
assert_instance_of Array, multiple_predicates
-
1
assert_equal 3, multiple_predicates.size
-
1
assert_Predicate multiple_predicates[0], :alpha, []
-
1
assert_Predicate multiple_predicates[1], :beta, []
-
1
assert_Predicate multiple_predicates[2], :gamma, []
-
end
-
-
1
it 'should define multiple methods to create Arguments for solving' do
-
1
refute respond_to?(:epsilon)
-
1
refute respond_to?(:upsilon)
-
-
1
Porolog::predicate :epsilon, :upsilon
-
-
1
assert respond_to?(:epsilon)
-
1
assert respond_to?(:upsilon)
-
1
assert_Arguments epsilon(), :epsilon, []
-
1
assert_Arguments upsilon([]), :upsilon, [[]]
-
end
-
-
1
it 'should define a method in a base class as an instance method and a class method' do
-
1
class Base1 ; end
-
-
1
refute_includes Base1.methods, :localised_predicate
-
1
refute_includes Base1.instance_methods, :localised_predicate
-
-
1
class Base1
-
-
1
Porolog::builtin :between, class_base: self
-
1
Porolog::predicate :localised_predicate, class_base: self
-
-
1
localised_predicate(:X) << [
-
between(:X, 1, 5)
-
]
-
-
1
def process
-
1
localised_predicate(:X).solve_for(:X)
-
end
-
end
-
-
1
assert_includes Base1.methods, :localised_predicate
-
1
assert_includes Base1.instance_methods, :localised_predicate
-
-
1
refute_includes methods, :localised_predicate
-
-
1
assert_solutions Base1.localised_predicate(:X), [
-
{ X: 1 },
-
{ X: 2 },
-
{ X: 3 },
-
{ X: 4 },
-
{ X: 5 },
-
]
-
-
1
assert_equal [1,2,3,4,5], Base1.new.process
-
end
-
-
end
-
-
1
describe '#unify_goals' do
-
-
1
it 'should unify goals for the same predicate' do
-
# -- Setup --
-
1
goal1 = new_goal :p, :m, :n
-
1
goal2 = new_goal :p, :s, :t
-
-
# -- Precondition Baseline --
-
1
assert_Goal_variables goal1, { m: nil, n: nil }, [
-
'Goal1.:m',
-
'Goal1.:n',
-
].join("\n")
-
-
1
assert_Goal_variables goal2, { s: nil, t: nil }, [
-
'Goal2.:s',
-
'Goal2.:t',
-
].join("\n")
-
-
# -- Test --
-
1
assert Porolog::unify_goals(goal1, goal2), name
-
-
# -- Compare Result Against Baseline --
-
1
assert_Goal_variables goal1, { m: nil, n: nil }, [
-
'Goal1.:m',
-
' Goal2.:s',
-
'Goal1.:n',
-
' Goal2.:t',
-
].join("\n")
-
-
1
assert_Goal_variables goal2, { s: nil, t: nil }, [
-
'Goal2.:s',
-
' Goal1.:m',
-
'Goal2.:t',
-
' Goal1.:n',
-
].join("\n")
-
end
-
-
1
it 'should not unify goals for different predicates' do
-
1
goal1 = new_goal :p, :x, :y
-
1
goal2 = new_goal :q, :x, :y
-
-
1
refute Porolog::unify_goals(goal1, goal2), name
-
-
1
assert_equal ['Cannot unify goals because they are for different predicates: :p and :q'], goal1.log
-
1
assert_equal ['Cannot unify goals because they are for different predicates: :p and :q'], goal2.log
-
end
-
-
end
-
-
1
describe '#instantiate_unifications' do
-
-
4
let(:goal1) { new_goal :p, :x, :y }
-
4
let(:goal2) { new_goal :q, :i, :j, :k }
-
4
let(:goal3) { new_goal :r, :n }
-
-
1
it 'should precheck inconsistent unifications' do
-
unifications = [
-
1
[:x, :k, goal1, goal2],
-
[:x, :j, goal1, goal2],
-
[:x, 12, goal1, goal3],
-
[:x, 37, goal1, goal3],
-
]
-
-
1
refute Porolog::instantiate_unifications(unifications), name
-
end
-
-
1
it 'should create instantiations' do
-
# -- Setup --
-
unifications = [
-
1
[:x, :k, goal1, goal2],
-
[:x, :j, goal1, goal2],
-
[:x, 12, goal1, goal3],
-
]
-
-
# -- Precondition Baseline --
-
1
assert_Goal_variables goal1, { x: nil, y: nil }, [
-
'Goal1.:x',
-
'Goal1.:y',
-
].join("\n")
-
-
1
assert_Goal_variables goal2, { i: nil, j: nil, k: nil }, [
-
'Goal2.:i',
-
'Goal2.:j',
-
'Goal2.:k',
-
].join("\n")
-
-
1
assert_Goal_variables goal3, { n: nil }, [
-
'Goal3.:n',
-
].join("\n")
-
-
# -- Test --
-
1
assert Porolog::instantiate_unifications(unifications), name
-
-
# -- Compare Result Against Baseline --
-
1
assert_Goal_variables goal1, { x: 12, y: nil }, [
-
'Goal1.:x',
-
' Goal2.:k',
-
' Goal2.:j',
-
' Goal3.12',
-
'Goal1.:y',
-
].join("\n")
-
-
1
assert_Goal_variables goal2, { i: nil, j: 12, k: 12 }, [
-
'Goal2.:i',
-
'Goal2.:j',
-
' Goal1.:x',
-
' Goal2.:k',
-
' Goal3.12',
-
'Goal2.:k',
-
' Goal1.:x',
-
' Goal2.:j',
-
' Goal3.12',
-
].join("\n")
-
-
1
assert_Goal_variables goal3, { n: nil }, [
-
'Goal3.:n',
-
].join("\n")
-
end
-
-
1
it 'should return false for inconsistent unifications not picked up by the precheck' do
-
# 12
-
# /
-
# i
-
# \
-
# x
-
# /
-
# j
-
# \
-
# y
-
# /
-
# k
-
# \
-
# 37
-
unifications = [
-
1
[:x, :i, goal1, goal2],
-
[:i, 12, goal2, goal3],
-
[:j, :x, goal2, goal1],
-
[:j, :y, goal2, goal1],
-
[:k, :y, goal2, goal1],
-
[:k, 37, goal2, goal3],
-
]
-
-
1
refute Porolog::instantiate_unifications(unifications), name
-
-
1
assert_Goal_variables goal1, { x: nil, y: nil }, [
-
'Goal1.:x',
-
'Goal1.:y',
-
].join("\n")
-
-
1
assert_Goal_variables goal2, { i: nil, j: nil, k: nil }, [
-
'Goal2.:i',
-
'Goal2.:j',
-
'Goal2.:k',
-
].join("\n")
-
-
1
assert_Goal_variables goal3, { n: nil }, [
-
'Goal3.:n',
-
].join("\n")
-
end
-
-
end
-
-
1
describe '#unify' do
-
-
25
let(:goal) { new_goal :p, :x, :y }
-
2
let(:goal2) { new_goal :q, :x, :y }
-
-
1
describe 'when [:atomic,:atomic]' do
-
-
1
it 'should unify equal atomic numbers' do
-
1
assert Porolog::unify(42, 42, goal), name
-
end
-
-
1
it 'should not unify unequal atomic numbers' do
-
1
refute Porolog::unify(42, 99, goal), name
-
-
expected_log = [
-
1
'Cannot unify because 42 != 99 (atomic != atomic)',
-
]
-
-
1
assert_equal expected_log, goal.log
-
end
-
-
end
-
-
1
describe 'when [:array,:array]' do
-
-
1
before do
-
4
@spy = Spy.on(Porolog, :unify_arrays).and_call_through
-
end
-
-
1
after do
-
4
assert_equal 1, @spy.calls.size, "expected unify_arrays to be called once for #{name}"
-
end
-
-
1
it 'should unify empty arrays' do
-
1
assert Porolog::unify([], [], goal), name
-
end
-
-
1
it 'should unify equal arrays' do
-
1
assert Porolog::unify([7,11,13], [7,11,13], goal), name
-
end
-
-
1
it 'should not unify unequal arrays' do
-
1
refute Porolog::unify([7,11,13], [7,11,14], goal), name
-
-
expected_log = [
-
1
'Cannot unify incompatible values: 13 with 14',
-
'Cannot unify because [7, 11, 13] != [7, 11, 14] (array != array)',
-
]
-
-
1
assert_equal expected_log, goal.log
-
end
-
-
1
it 'should not unify arrays of different lengths' do
-
1
refute Porolog::unify([7,11,13], [7,11,13,13], goal), name
-
-
expected_log = [
-
1
'Cannot unify arrays of different lengths: [7, 11, 13] with [7, 11, 13, 13]',
-
'Cannot unify because [7, 11, 13] != [7, 11, 13, 13] (array != array)',
-
]
-
-
1
assert_equal expected_log, goal.log
-
end
-
-
end
-
-
1
describe 'when [:variable,:atomic]' do
-
-
1
before do
-
2
expects(:unify_arrays).times(0)
-
end
-
-
1
it 'should return an instantiation' do
-
1
assert_equal [[:word, 'word', goal, goal]], Porolog::unify(:word, 'word', goal), name
-
end
-
-
1
it 'should not unify an instantiated variable with a different value' do
-
1
goal.instantiate :word, 'other'
-
-
1
assert_nil Porolog::unify(:word, 'word', goal), name
-
-
expected_log = [
-
1
'Cannot unify because Goal1."other" != "word" (variable != atomic)',
-
]
-
-
1
assert_equal expected_log, goal.log
-
end
-
-
end
-
-
1
describe 'when [:atomic,:variable]' do
-
-
1
before do
-
2
expects(:unify_arrays).times(0)
-
end
-
-
1
it 'should return an instantiation' do
-
1
assert_equal [[:word, 'word', goal, goal]], Porolog::unify('word', :word, goal), name
-
end
-
-
1
it 'should not unify instantiated variables with different values' do
-
1
goal.instantiate :word, 'something'
-
-
1
assert_nil Porolog::unify('word', :word, goal), name
-
-
expected_log = [
-
1
'Cannot unify because "word" != Goal1."something" (atomic != variable)',
-
]
-
-
1
assert_equal expected_log, goal.log
-
end
-
-
end
-
-
1
describe 'when [:variable, :variable]' do
-
-
1
before do
-
4
expects(:unify_arrays).times(0)
-
end
-
-
1
it 'should return an instantiation' do
-
1
assert_equal [[:x, :y, goal, goal2]], Porolog::unify(:x, :y, goal, goal2), name
-
end
-
-
1
it 'should not unify instantiated variables with different values' do
-
1
goal.instantiate :word, 'something'
-
1
goal.instantiate :draw, 'picturing'
-
-
1
assert_nil Porolog::unify(:draw, :word, goal), name
-
-
expected_log = [
-
1
'Cannot unify because "picturing" != "something" (variable != variable)'
-
]
-
-
1
assert_equal expected_log, goal.log
-
end
-
-
1
it 'should unify the unknown array with a variable' do
-
1
goal.instantiate :X, Porolog::UNKNOWN_ARRAY
-
1
goal.instantiate :Y, [1,2,3,4]
-
-
1
unifications = Porolog::unify(goal[:X], goal[:Y], goal)
-
-
expected_unifications = [
-
1
[goal[:X], goal[:Y], goal, goal]
-
]
-
-
1
assert_equal expected_unifications, unifications
-
-
1
expected_log = []
-
-
1
assert_equal expected_log, goal.log
-
end
-
-
1
it 'should unify the unknown array with an array' do
-
1
goal.instantiate :X, [1,2,3,4]
-
1
goal.instantiate :Y, Porolog::UNKNOWN_ARRAY
-
-
1
unifications = Porolog::unify(goal[:X], goal[:Y], goal)
-
-
expected_unifications = [
-
1
[goal[:X], goal[:Y], goal, goal]
-
]
-
-
1
assert_equal expected_unifications, unifications
-
-
1
expected_log = []
-
-
1
assert_equal expected_log, goal.log
-
end
-
-
end
-
-
1
describe 'when [:variable, :array]' do
-
-
1
it 'should return an instantiation' do
-
1
expects(:unify_arrays).times(0)
-
-
1
assert_equal [[:list, [7,11,13], goal, goal]], Porolog::unify(:list, [7,11,13], goal), name
-
end
-
-
1
it 'should not unify instantiated variables with different values' do
-
1
goal.instantiate :word, [1,2,3,4]
-
1
Porolog::expects(:unify_arrays).times(1)
-
-
1
assert_nil Porolog::unify(:word, [1,2,3,5], goal), name
-
-
expected_log = [
-
#'Cannot unify incompatible values: 4 with 5',
-
1
'Cannot unify because Goal1.[1, 2, 3, 4] != [1, 2, 3, 5] (variable/array != array)'
-
]
-
-
1
assert_equal expected_log, goal.log
-
end
-
-
1
it 'should not unify instantiated variables with a non-array value' do
-
1
goal.instantiate :word, 1234
-
1
Porolog::expects(:unify_arrays).times(0)
-
-
1
assert_nil Porolog::unify(:word, [1,2,3,5], goal), name
-
-
expected_log = [
-
1
'Cannot unify because Goal1.1234 != [1, 2, 3, 5] (variable != array)'
-
]
-
-
1
assert_equal expected_log, goal.log
-
end
-
-
end
-
-
1
describe 'when [:array, :variable]' do
-
-
1
it 'should not unify instantiated variables that are not instantiated with an array' do
-
1
goal.instantiate :word, '1 2 3 4'
-
-
1
assert_nil Porolog::unify([1,2,3,4], :word, goal), name
-
-
expected_log = [
-
1
'Cannot unify because [1, 2, 3, 4] != Goal1."1 2 3 4" (array != variable)'
-
]
-
-
1
assert_equal expected_log, goal.log
-
end
-
-
1
it 'should return an instantiation' do
-
1
expects(:unify_arrays).times(0)
-
-
1
assert_equal [[:list, [7,11,13], goal, goal]], Porolog::unify([7,11,13], :list, goal), name
-
end
-
-
1
it 'should not unify instantiated variables with different values' do
-
1
goal.instantiate :word, [1,2,3,4]
-
1
Porolog::expects(:unify_arrays).times(1)
-
-
1
assert_nil Porolog::unify([1,2,3,5], :word, goal), name
-
-
expected_log = [
-
#'Cannot unify incompatible values: 5 with 4',
-
1
'Cannot unify because [1, 2, 3, 5] != Goal1.[1, 2, 3, 4] (variable/array != array)'
-
]
-
-
1
assert_equal expected_log, goal.log
-
end
-
-
1
it 'should unify instantiated variables with unifiable values' do
-
1
goal.instantiate :word, [1,2,3]/:w
-
-
1
instantiations = Porolog::unify([nil,nil,3,4,5], :word, goal)
-
-
1
expected_log = []
-
expected_instantiations = [
-
1
[:w, [4,5], goal, goal],
-
]
-
-
1
assert_equal expected_log, goal.log
-
1
assert_equal expected_instantiations, instantiations
-
end
-
-
end
-
-
1
describe 'when [:array,:atomic], [:atomic,:array]' do
-
-
1
before do
-
2
Porolog::expects(:unify_arrays).times(0)
-
end
-
-
1
it 'should return nil for unifying array and atomic' do
-
1
assert_nil Porolog::unify([7,11,13], 'word', goal), name
-
-
expected_log = [
-
1
'Cannot unify [7, 11, 13] with "word" (array != atomic)',
-
]
-
-
1
assert_equal expected_log, goal.log
-
end
-
-
1
it 'should return nil for unifying atomic and array' do
-
1
assert_nil Porolog::unify('word', [7,11,13], goal), name
-
-
expected_log = [
-
1
'Cannot unify "word" with [7, 11, 13] (atomic != array)',
-
]
-
-
1
assert_equal expected_log, goal.log
-
end
-
-
end
-
-
1
describe 'when [:array, :tail]' do
-
-
3
let(:goal) { new_goal :p, :x, :y }
-
-
1
it 'should unify an array and a tail' do
-
1
assert Porolog::unify([7,11,13], Porolog::Tail.new([[7,11,goal[:P]]]), goal), name
-
-
1
expected_log = []
-
-
1
assert_equal expected_log, goal.log
-
end
-
-
1
it 'should not unify an array and tail when they cannot be unified' do
-
1
refute Porolog::unify([7,11,13], Porolog::Tail.new([[7,11,14]]), goal), name
-
-
expected_log = [
-
1
'Cannot unify incompatible values: 13 with 14',
-
'Cannot unify: [7, 11, 13] with [7, 11, 14]',
-
'Cannot unify because [7, 11, 13] != *[[7, 11, 14]] (array != tail)'
-
]
-
-
1
assert_equal expected_log, goal.log
-
end
-
-
end
-
-
1
describe 'when [:tail, :array]' do
-
-
3
let(:goal) { new_goal :p, :x, :y }
-
-
1
it 'should unify a tail and an array' do
-
1
assert Porolog::unify(Porolog::Tail.new([[7,11,13]]), [7,11,goal[:P]], goal), name
-
-
1
expected_log = []
-
-
1
assert_equal expected_log, goal.log
-
end
-
-
1
it 'should not unify a tail and an array when they cannot be unified' do
-
1
refute Porolog::unify(Porolog::Tail.new([[7,11,13]]), [7,11,14], goal), name
-
-
expected_log = [
-
1
'Cannot unify incompatible values: 13 with 14',
-
'Cannot unify: [7, 11, 13] with [7, 11, 14]',
-
'Cannot unify because *[[7, 11, 13]] != [7, 11, 14] (tail != array)'
-
]
-
-
1
assert_equal expected_log, goal.log
-
end
-
-
end
-
-
1
describe 'when [:tail, :tail]' do
-
-
1
it 'something' do
-
expected_unifications = [
-
1
[goal[:B], [goal[:H]]/:T, goal, goal],
-
[goal[:C], [goal[:H]]/:NT, goal, goal]
-
]
-
1
assert_equal expected_unifications, Porolog::unify(
-
[:A, :B, :C],
-
[:A, [:H]/:T, [:H]/:NT],
-
goal
-
)
-
end
-
-
end
-
-
end
-
-
1
describe '#unify_arrays' do
-
-
135
let(:g1) { new_goal(:p, :x, :y) }
-
135
let(:g2) { new_goal(:q, :a, :b) }
-
135
let(:goals) { [g1, g2] }
-
-
1
it 'should not unify arrays when left is not an Array' do
-
1
expect_unify_arrays_with_calls 0, 0, 0
-
-
1
refute_Unify_arrays [{}, []], goals, [
-
'Cannot unify a non-array with an array: {} with []',
-
]
-
end
-
-
1
it 'should not unify arrays when right is not an Array' do
-
1
expect_unify_arrays_with_calls 0, 0, 0
-
-
1
refute_Unify_arrays [[], 4], goals, [
-
'Cannot unify a non-array with an array: [] with 4',
-
]
-
end
-
-
1
it 'should unify but without unifications when left is the unknown array' do
-
1
expect_unify_arrays_with_calls 0, 0, 0
-
-
1
assert_Unify_arrays [Porolog::UNKNOWN_ARRAY, [:a,:b,:c]], goals, [g2[:a], g2[:b], g2[:c]]
-
end
-
-
1
it 'should unify but without unifications when right is the unknown array' do
-
1
expect_unify_arrays_with_calls 0, 0, 0
-
-
1
assert_Unify_arrays [[:x,:y,:z], Porolog::UNKNOWN_ARRAY], goals, [g1[:x], g1[:y], g1[:z]]
-
end
-
-
1
it 'should unify but without unifications when left and right are the unknown array' do
-
1
expect_unify_arrays_with_calls 0, 0, 0
-
-
1
assert_Unify_arrays [[Porolog::UNKNOWN_TAIL], Porolog::UNKNOWN_ARRAY], goals, Porolog::UNKNOWN_ARRAY
-
end
-
-
arrays_without_tails = [
-
1
[],
-
[1, 2, 3],
-
[1, 2, 3, 4],
-
[:x, :y, :z],
-
[['one'], ['word'], ['sentences']],
-
]
-
-
arrays_with_tails = [
-
1
[]/:t,
-
[1]/:z,
-
[1, 2]/:s,
-
[1, Porolog::UNKNOWN_TAIL],
-
[1, 2, 3, Porolog::UNKNOWN_TAIL],
-
[1, 2, 3, 4, Porolog::UNKNOWN_TAIL],
-
[:x, :y, :z, Porolog::UNKNOWN_TAIL],
-
[['one'], ['word'], ['sentences']]/:tail,
-
]
-
-
1
arrays_without_tails.combination(2).each do |arrays|
-
10
it "should call unify_arrays_with_no_tails when there are no tails: #{arrays.map(&:inspect).join(' and ')}" do
-
10
expect_unify_arrays_with_calls 1, 0, 0
-
-
10
Porolog::unify_arrays(*arrays, *goals)
-
end
-
end
-
-
1
arrays_with_tails.combination(2).each do |arrays|
-
28
it "should call unify_arrays_with_all_tails when all arrays have tails: #{arrays.map(&:inspect).join(' and ')}" do
-
28
expect_unify_arrays_with_calls 0, 0, 1
-
-
28
Porolog::unify_arrays(*arrays, *goals)
-
end
-
end
-
-
(
-
1
arrays_without_tails.product(arrays_with_tails) +
-
arrays_with_tails.product(arrays_without_tails)
-
).each do |arrays|
-
80
it "should call unify_arrays_with_some_tails when one array has a tail and the other does not: #{arrays.map(&:inspect).join(' and ')}" do
-
80
expect_unify_arrays_with_calls 0, 1, 0
-
-
80
Porolog::unify_arrays(*arrays, *goals)
-
end
-
end
-
-
1
it 'should unify identical arrays without variables' do
-
1
assert_Unify_arrays [[1,2,3], [1,2,3]], goals, [1,2,3]
-
end
-
-
1
it 'should unify an array of variables with an array of atomics' do
-
1
assert_Unify_arrays [[:a,:b,:c], [1,2,3]], goals, [1,2,3], [
-
[:a, 1, g1, g2],
-
[:b, 2, g1, g2],
-
[:c, 3, g1, g2],
-
]
-
end
-
-
1
it 'should unify an array of atomics with an array of variables' do
-
1
assert_Unify_arrays [[1,2,3], [:a,:b,:c]], goals, [1,2,3], [
-
[:a, 1, g2, g1],
-
[:b, 2, g2, g1],
-
[:c, 3, g2, g1],
-
]
-
end
-
-
1
it 'should unify arrays of variables' do
-
1
assert_Unify_arrays [[:x,:y,:z], [:a,:b,:c]], goals, [nil,nil,nil], [
-
[:x, :a, g1, g2],
-
[:y, :b, g1, g2],
-
[:z, :c, g1, g2],
-
]
-
end
-
-
1
it 'should unify a fixed array with an array with a tail' do
-
1
assert_Unify_arrays [[:a, 2, 3], [1]/:t], goals, [1,2,3], [
-
[:a, 1, g1, g2],
-
[:t, [2,3], g2, g1],
-
]
-
end
-
-
1
it 'should unify a fixed array with the unknown array' do
-
1
assert_Unify_arrays [[1,2,3], Porolog::UNKNOWN_ARRAY], goals, [1,2,3]
-
end
-
-
1
it 'should unify the unknown array with the unknown array' do
-
1
assert_Unify_arrays [Porolog::UNKNOWN_ARRAY, Porolog::UNKNOWN_ARRAY], goals, Porolog::UNKNOWN_ARRAY
-
end
-
-
1
it 'should unify arrays with variable tails' do
-
1
assert_Unify_arrays [[:a, :b]/:c, [:x, :y]/:z], goals, [nil, nil, Porolog::UNKNOWN_TAIL], [
-
[:a, :x, g1, g2],
-
[:b, :y, g1, g2],
-
[:c, :z, g1, g2],
-
]
-
end
-
-
1
it 'should unify arrays with variable tails of different lenths' do
-
1
assert_Unify_arrays [[:a, :b]/:c, [:x]/:z], goals, [nil, nil, Porolog::UNKNOWN_TAIL], [
-
[g2[:x], g1[:a], g2, g1],
-
[g2[:z], [g1[:b]]/g1[:c], g2, g1],
-
]
-
end
-
-
1
it 'should unify arrays with overlapping variables' do
-
1
assert_Unify_arrays [[:a,:a,:c,:c,:e], [1,:b,:b,:d,:d]], goals, [1, nil, nil, nil, nil], [
-
[g1[:a], g2[1], g1, g2],
-
[g1[:a], g2[:b], g1, g2],
-
[g1[:c], g2[:b], g1, g2],
-
[g1[:c], g2[:d], g1, g2],
-
[g1[:e], g2[:d], g1, g2],
-
]
-
end
-
-
1
it 'should unify complementary arrays' do
-
1
assert_Unify_arrays [[1,2]/:t, [:a,:b,3,4,5]], goals, [1,2,3,4,5], [
-
[g2[:a], g1[1], g2, g1],
-
[g2[:b], g1[2], g2, g1],
-
[g1[:t], [3, 4, 5], g1, g2],
-
]
-
end
-
-
end
-
-
1
describe '#unify_many_arrays' do
-
-
7
let(:goal) { new_goal(:p, :x, :y) }
-
-
1
it 'should return nil when not all arrays are Arrays or variables' do
-
arrays = [
-
1
[1,2,3],
-
123,
-
:x,
-
]
-
-
1
assert_nil Porolog::unify_many_arrays(arrays, [goal] * arrays.size), name
-
-
expected_log = [
-
1
'Cannot unify: [1, 2, 3] with 123 with :x',
-
]
-
-
1
assert_equal expected_log, goal.log
-
end
-
-
1
it 'should not return nil when all arrays are Arrays or variables' do
-
arrays = [
-
1
[1,2,3],
-
Porolog::UNKNOWN_ARRAY,
-
:x,
-
]
-
-
1
result = Porolog::unify_many_arrays(arrays, [goal] * arrays.size)
-
1
refute_nil result, name
-
-
1
merged, unifications = result
-
-
1
expected_merged = [1,2,3]
-
1
expected_unifications = [
-
]
-
-
1
assert_equal expected_merged, merged
-
1
assert_equal expected_unifications, unifications
-
end
-
-
1
it 'should automatically merge unknown arrays' do
-
arrays = [
-
1
[1,2,3],
-
Porolog::UNKNOWN_ARRAY,
-
]
-
-
1
assert_equal [[1,2,3], []], Porolog::unify_many_arrays(arrays, [goal] * arrays.size), name
-
end
-
-
1
it 'should call unify_arrays_with_no_tails when there are no Arrays with Tails' do
-
1
expect_unify_arrays_with_calls 1, 0, 0
-
-
arrays = [
-
1
[1, 2, 3, nil],
-
[1, nil, 3, 4],
-
[nil, 2, 3, nil],
-
]
-
-
1
Porolog::unify_many_arrays(arrays, [goal] * arrays.size)
-
end
-
-
1
it 'should call unify_arrays_with_all_tails when all Arrays have Tails' do
-
1
expect_unify_arrays_with_calls 0, 0, 1
-
-
arrays = [
-
1
[1, 2, 3, Porolog::UNKNOWN_TAIL],
-
[1, nil, 3, ]/:x,
-
]
-
-
1
Porolog::unify_many_arrays(arrays, [goal] * arrays.size)
-
end
-
-
1
it 'should call unify_arrays_with_some_tails when there is a combination of Arrays with and without Tails' do
-
1
expect_unify_arrays_with_calls 0, 1, 0
-
-
arrays = [
-
1
[1, 2, 3, Porolog::UNKNOWN_TAIL],
-
[1, nil, 3, 4],
-
[nil, 2, 3, nil],
-
]
-
-
1
Porolog::unify_many_arrays(arrays, [goal] * arrays.size)
-
end
-
-
end
-
-
1
describe '#has_tail?' do
-
-
# -- Non-Arrays --
-
4
let(:goal) { new_goal(:p, :x, :y) }
-
2
let(:variable) { goal.variable(:x) }
-
2
let(:object) { Object.new }
-
2
let(:symbol) { :symbol }
-
2
let(:integer) { 789 }
-
-
# -- Arrays --
-
2
let(:empty_array) { [] }
-
2
let(:finite_array) { [1,2,3,4,5] }
-
2
let(:array_with_unknown_tail) { [1,2,3,4,5, Porolog::UNKNOWN_TAIL] }
-
2
let(:array_with_variable_tail) { [1,2,3,4,5]/:tail }
-
2
let(:unknown_array) { Porolog::UNKNOWN_ARRAY }
-
-
1
describe 'when not given an Array' do
-
-
1
it 'should return false when given an uninstantiated variable' do
-
1
refute Porolog::has_tail?(variable), name
-
end
-
-
1
it 'should return false when given an Object' do
-
1
refute Porolog::has_tail?(object), name
-
end
-
-
1
it 'should return false when given a Symbol' do
-
1
refute Porolog::has_tail?(symbol), name
-
end
-
-
1
it 'should return false when given an Integer' do
-
1
refute Porolog::has_tail?(integer), name
-
end
-
-
end
-
-
1
describe 'when given an Array' do
-
-
1
it 'should return false when the Array is empty' do
-
1
refute Porolog::has_tail?(empty_array), name
-
end
-
-
1
it 'should return false when the last element is atomic' do
-
1
refute Porolog::has_tail?(finite_array), name
-
end
-
-
1
it 'should return true when the last element is unknown' do
-
1
assert Porolog::has_tail?(array_with_unknown_tail), name
-
end
-
-
1
it 'should return true when the last element is a Tail' do
-
1
assert Porolog::has_tail?(array_with_variable_tail), name
-
end
-
-
1
it 'should return true when given an unknown array' do
-
1
assert Porolog::has_tail?(unknown_array), name
-
end
-
-
1
describe 'and the array has an instantiated tail' do
-
-
1
before do
-
2
goal.instantiate :a, 1
-
2
goal.instantiate :b, 2
-
2
goal.instantiate :c, 3
-
2
goal.instantiate :d, 4
-
2
goal.instantiate :e, [5]
-
end
-
-
1
describe 'and to apply value to the array' do
-
-
1
it 'should return false' do
-
1
array = goal.variablise([:a, :b, :c, :d]/:e)
-
-
1
refute Porolog::has_tail?(array, true), name
-
end
-
-
end
-
-
1
describe 'and to not apply value to the array' do
-
-
1
it 'should return true' do
-
1
array = goal.variablise([:a, :b, :c, :d]/:e)
-
-
1
assert Porolog::has_tail?(array, false), name
-
end
-
-
end
-
-
end
-
-
end
-
-
end
-
-
1
describe '#expand_splat' do
-
-
1
it 'should return an empty array as is' do
-
1
assert_equal [], Porolog::expand_splat([])
-
end
-
-
1
it 'should return non-arrays as is' do
-
1
assert_equal 5, Porolog::expand_splat(5)
-
end
-
-
1
it 'should expand an array of just a Tail' do
-
1
assert_equal :t, Porolog::expand_splat([]/:t)
-
end
-
-
1
it 'should not expand an array with a Tail' do
-
1
assert_equal [1,2,3]/:t, Porolog::expand_splat([1,2,3]/:t)
-
end
-
-
1
it 'should expand a tail' do
-
1
assert_equal [1,2,3,4,5], Porolog::expand_splat([1, 2, 3, Porolog::Tail.new([4,5])])
-
end
-
-
1
it 'should expand nested arrays' do
-
1
assert_equal [99, [1,2,3,4], 99], Porolog::expand_splat([99,[1, 2, 3, Porolog::Tail.new([4])],99])
-
end
-
-
end
-
-
1
describe '#unify_arrays_with_no_tails' do
-
-
14
let(:g1) { new_goal(:p, :x, :y) }
-
14
let(:g2) { new_goal(:q, :a, :b) }
-
14
let(:g3) { new_goal(:r, :s, :t) }
-
10
let(:g4) { new_goal(:v, :i, :j) }
-
10
let(:goals) { [g1, g2, g3, g4] }
-
-
1
it 'should return nil when any array has a tail' do
-
arrays = [
-
1
[1, 2, 3, 4],
-
[1, 2, 3]/:tail,
-
[:w, :x, :y, :z]
-
]
-
1
arrays_goals = goals[0..arrays.size]
-
-
1
assert_nil Porolog::unify_arrays_with_no_tails(arrays, arrays_goals, []), name
-
-
expected_log = [
-
1
'Wrong unification method called: no_tails but one or more of [[1, 2, 3, 4], [1, 2, 3, *:tail], [:w, :x, :y, :z]] has a tail',
-
]
-
-
1
arrays_goals.each do |goal|
-
4
assert_equal expected_log, goal.log
-
end
-
end
-
-
1
it 'should not unify finite arrays with unequal sizes' do
-
# -- First > Last --
-
arrays = [
-
1
[nil, nil, nil, nil],
-
[1, 2, 3]
-
]
-
1
arrays_goals = goals[0...arrays.size]
-
-
1
assert_nil Porolog::unify_arrays_with_no_tails(arrays, arrays_goals, []), name
-
-
# -- First = Last --
-
arrays = [
-
1
[nil, nil, nil],
-
[1, 2, 3]
-
]
-
1
arrays_goals = goals[0...arrays.size]
-
-
1
expected_merged = [1,2,3]
-
1
expected_unifications = []
-
-
1
merged, unifications = Porolog::unify_arrays_with_no_tails(arrays, arrays_goals, [])
-
-
1
assert_equal expected_merged, merged
-
1
assert_equal expected_unifications, unifications
-
-
# -- First < Last --
-
arrays = [
-
1
[nil, nil],
-
[1, 2, 3]
-
]
-
1
arrays_goals = goals[0...arrays.size]
-
-
1
assert_nil Porolog::unify_arrays_with_no_tails(arrays, arrays_goals, []), name
-
-
# -- Check Log --
-
expected_log = [
-
1
'Cannot unify arrays of different lengths: [nil, nil, nil, nil] with [1, 2, 3]',
-
'Cannot unify arrays of different lengths: [nil, nil] with [1, 2, 3]',
-
]
-
-
1
arrays_goals.each do |goal|
-
2
assert_equal expected_log, goal.log
-
end
-
end
-
-
1
it 'should merge values with nil as an empty slot' do
-
arrays = [
-
1
[1, 2, 3, nil, nil],
-
[nil, nil, nil, 4, nil],
-
[nil, nil, nil, nil, 5],
-
]
-
1
arrays_goals = goals[0...arrays.size]
-
-
1
merged, unifications = Porolog::unify_arrays_with_no_tails(arrays, arrays_goals, [])
-
-
1
expected_merged = [1, 2, 3, 4, 5]
-
1
expected_unifications = []
-
-
1
assert_equal expected_merged, merged
-
1
assert_equal expected_unifications, unifications
-
end
-
-
1
it 'should return nil when the finite arrays have different sizes' do
-
arrays = [
-
1
[nil],
-
[nil, nil, nil, nil],
-
[nil, nil, nil, nil, nil, nil],
-
]
-
1
arrays_goals = goals[0...arrays.size]
-
-
1
assert_nil Porolog::unify_arrays_with_no_tails(arrays, arrays_goals, []), name
-
-
expected_log = [
-
1
'Cannot unify arrays of different lengths: [nil] with [nil, nil, nil, nil] with [nil, nil, nil, nil, nil, nil]',
-
]
-
-
1
arrays_goals.each do |goal|
-
3
assert_equal expected_log, goal.log
-
end
-
end
-
-
1
it 'should return nil when the arrays have incompatible values' do
-
arrays = [
-
1
[1, 2, 3, nil, nil],
-
[nil, nil, nil, 4, nil],
-
[8, nil, nil, nil, 5],
-
]
-
1
arrays_goals = goals[0...arrays.size]
-
-
1
assert_nil Porolog::unify_arrays_with_no_tails(arrays, arrays_goals, []), name
-
-
expected_log = [
-
1
'Cannot unify incompatible values: 1 with 8',
-
]
-
-
1
arrays_goals.each do |goal|
-
3
assert_equal expected_log, goal.log
-
end
-
end
-
-
1
it 'should return necessary unifications to unify variables' do
-
arrays = [
-
1
[1, 2, nil, nil, nil],
-
[:j, :n, :n, 4, nil],
-
[:x, nil, :m, :y, :z],
-
]
-
1
arrays_goals = goals[0...arrays.size]
-
-
1
merged, unifications = Porolog::unify_arrays_with_no_tails(arrays, arrays_goals, [])
-
-
1
expected_merged = [1, 2, nil, 4, nil]
-
expected_unifications = [
-
1
[g2[:j], g1[1], g2, g1],
-
[g3[:x], g1[1], g3, g1],
-
[g2[:j], g3[:x], g2, g3],
-
[g2[:n], g1[2], g2, g1],
-
[g2[:n], g3[:m], g2, g3],
-
[g3[:y], g2[4], g3, g2],
-
]
-
-
1
assert_equal expected_merged, merged
-
1
assert_equal expected_unifications, unifications
-
end
-
-
1
it 'should deduce array goals from array elements when missing' do
-
arrays = [
-
1
[ nil, 2, nil, g1.value(4), nil],
-
g2.value([1, nil, 3, nil, nil]),
-
g3.value([nil, :x, nil, nil, 5]),
-
]
-
1
missing_goals = [nil, nil, nil]
-
-
1
merged, unifications = Porolog::unify_arrays_with_no_tails(arrays, missing_goals, [])
-
-
1
expected_merged = [1, 2, 3, 4, 5]
-
expected_unifications = [
-
1
[g3.variable(:x), g1.value(2), g3, g1]
-
]
-
-
1
assert_equal expected_merged, merged
-
1
assert_equal expected_unifications, unifications
-
end
-
-
1
it 'should map Values as is' do
-
1
value1 = g1.value(91)
-
1
g1.instantiate(:gi, [nil, value1, nil, 93])
-
-
arrays = [
-
1
[ nil, 2, nil, :gi, nil],
-
g2.value([1, nil, 3, [90, nil, nil, nil], nil]),
-
g3.value([nil, :x, nil, [nil, nil, 92, :gy], 5]),
-
]
-
-
1
arrays_goals = [g1, nil, nil]
-
-
1
merged, unifications = Porolog::unify_arrays_with_no_tails(arrays, arrays_goals, [])
-
-
1
expected_merged = [1, 2, 3, [90, 91, 92, 93], 5]
-
expected_unifications = [
-
1
[g3.variable(:x), g1.value(2), g3, g1],
-
[g3.variable(:gy), g1.value(93), g3, g1],
-
]
-
-
1
assert_equal expected_merged, merged
-
1
assert_equal expected_unifications, unifications
-
end
-
-
1
it 'should detect ununifiable values embedded in subarrays' do
-
1
value1 = g1.value(91)
-
1
g1.instantiate(:gi, [nil, value1, 92.1, 93])
-
-
arrays = [
-
1
[ nil, 2, nil, :gi, nil],
-
g2.value([1, nil, 3, [90, nil, nil, nil], nil]),
-
g3.value([nil, :x, nil, [nil, nil, 92, :gy], 5]),
-
]
-
-
1
arrays_goals = [g1, nil, nil]
-
-
1
assert_nil Porolog::unify_arrays_with_no_tails(arrays, arrays_goals, []), name
-
-
expected_log = [
-
1
'Cannot unify incompatible values: 92.1 with 92',
-
'Cannot unify: [nil, 91, 92.1, 93] with [90, nil, nil, nil] with [nil, nil, 92, Goal3.:gy]',
-
]
-
-
1
[g1, g2, g3].each do |goal|
-
3
assert_equal expected_log, goal.log
-
end
-
end
-
-
1
it 'should not expand variables when they are instantiated to the unknown array' do
-
1
arrays_goals = goals[0...4]
-
1
g4.instantiate :z, Porolog::UNKNOWN_ARRAY
-
arrays = [
-
1
[1, 2, nil, nil, nil],
-
[:j, :n, :n, 4, nil],
-
[:x, nil, :m, :y, :z],
-
:z
-
]
-
-
1
assert_equal Porolog::UNKNOWN_ARRAY, g4[:z].value
-
-
1
merged, unifications = Porolog::unify_arrays_with_no_tails(arrays, arrays_goals, [])
-
-
1
expected_merged = [1, 2, nil, 4, g3[:z]]
-
expected_unifications = [
-
1
[g2[:j], g1[1], g2, g1],
-
[g2[:n], g1[2], g2, g1],
-
[g3[:x], g1[1], g3, g1],
-
[g4[:z], [g1[1], g1[2], g1.value(nil), g1.value(nil), g1.value(nil)], g4, g1],
-
[g2[:j], g3[:x], g2, g3],
-
[g2[:n], g3[:m], g2, g3],
-
[g3[:y], g2[4], g3, g2],
-
[g4[:z], [g2[:j], g2[:n], g2[:n], g2[4], g2.value(nil)], g4, g2],
-
[g4[:z], [g3[:x], g3.value(nil), g3[:m], g3[:y], g3[:z]], g4, g3],
-
]
-
-
1
assert_equal expected_merged, merged
-
1
assert_equal expected_unifications, unifications
-
end
-
-
1
it 'should not expand variables when they are instantiated to the unknown tail' do
-
1
arrays_goals = goals[0...4]
-
1
g4.instantiate :z, Porolog::UNKNOWN_TAIL
-
arrays = [
-
1
[1, 2, nil, nil, nil],
-
[:j, :n, :n, 4, nil],
-
[:x, nil, :m, :y, :z],
-
:z
-
]
-
-
1
assert_equal g4[:z], g4[:z].value
-
-
1
merged, unifications = Porolog::unify_arrays_with_no_tails(arrays, arrays_goals, [])
-
-
1
expected_merged = [1, 2, nil, 4, nil]
-
expected_unifications = [
-
1
[g2[:j], g1[1], g2, g1],
-
[g2[:n], g1[2], g2, g1],
-
[g3[:x], g1[1], g3, g1],
-
[g4[:z], [g1[1], g1[2], g1.value(nil), g1.value(nil), g1.value(nil)], g4, g1],
-
[g2[:j], g3[:x], g2, g3],
-
[g2[:n], g3[:m], g2, g3],
-
[g3[:y], g2[4], g3, g2],
-
[g4[:z], [g2[:j], g2[:n], g2[:n], g2[4], g2.value(nil)], g4, g2],
-
[g4[:z], [g3[:x], g3.value(nil), g3[:m], g3[:y], g3[:z]], g4, g3],
-
]
-
-
1
assert_equal expected_merged, merged
-
1
assert_equal expected_unifications, unifications
-
end
-
-
1
it 'should not unify an atomic with an embedded unknown array ' do
-
1
value1 = g1.value(91)
-
1
g1.instantiate(:gi, [nil, value1, 92.1, 93])
-
-
arrays = [
-
1
[ nil, 2, nil, :gi, nil],
-
g2.value([1, nil, 3, [90, nil, nil, nil], nil]),
-
g3.value([nil, :x, nil, [nil, nil, Porolog::UNKNOWN_ARRAY, :gy], 5]),
-
]
-
-
1
arrays_goals = [g1, nil, nil]
-
-
1
assert_nil Porolog::unify_arrays_with_no_tails(arrays, arrays_goals, []), name
-
-
expected_log = [
-
1
'Cannot unify incompatible values: 92.1 with [...]',
-
'Cannot unify: [nil, 91, 92.1, 93] with [90, nil, nil, nil] with [nil, nil, [...], Goal3.:gy]',
-
]
-
-
1
[g1, g2, g3].each do |goal|
-
3
assert_equal expected_log, goal.log
-
end
-
end
-
-
1
it 'should not unify arrays where one embedded variable cannot be unified' do
-
1
g1.instantiate(:a, :b)
-
1
g2.instantiate(:a, 2)
-
1
g3.instantiate(:a, 3)
-
-
arrays = [
-
1
[[:a]],
-
[[:a]],
-
[[:a]],
-
]
-
1
arrays_goals = goals[0...arrays.size]
-
-
1
assert_nil Porolog::unify_arrays_with_no_tails(arrays, arrays_goals, []), name
-
-
1
assert_equal [
-
'Cannot unify: [Goal1.:a] with [Goal2.2] with [Goal3.3]',
-
], g1.log
-
-
1
assert_equal [
-
'Cannot unify because Goal2.2 != Goal3.3 (atomic != atomic)',
-
'Cannot unify: Goal2.2 with Goal3.3',
-
'Cannot unify: [Goal1.:a] with [Goal2.2] with [Goal3.3]',
-
], g2.log
-
-
1
assert_equal [
-
'Cannot unify because Goal2.2 != Goal3.3 (atomic != atomic)',
-
'Cannot unify: Goal2.2 with Goal3.3',
-
'Cannot unify: [Goal1.:a] with [Goal2.2] with [Goal3.3]',
-
], g3.log
-
end
-
-
end
-
-
1
describe '#unify_arrays_with_some_tails' do
-
-
12
let(:g1) { new_goal(:p, :x, :y) }
-
12
let(:g2) { new_goal(:q, :a, :b) }
-
11
let(:g3) { new_goal(:r, :s, :t) }
-
11
let(:goals) { [g1, g2, g3] }
-
-
1
it 'should not unify a finite array with an array with a tail that has more finite elements' do
-
arrays = [
-
1
[nil, nil, nil, nil, Porolog::UNKNOWN_TAIL],
-
[1, 2, 3]
-
]
-
1
arrays_goals = goals[0...arrays.size]
-
-
1
assert_nil Porolog::unify_arrays_with_some_tails(arrays, arrays_goals, []), name
-
-
expected_log = [
-
1
'Cannot unify enough elements: [nil, nil, nil, nil, ...] with [Goal2.1, Goal2.2, Goal2.3]',
-
]
-
-
1
assert_equal expected_log, g1.log
-
1
assert_equal expected_log, g2.log
-
end
-
-
1
it 'should unify a finite array with an array with a tail that has less finite elements' do
-
arrays = [
-
1
[nil, nil, Porolog::UNKNOWN_TAIL],
-
[1, 2, 3]
-
]
-
1
arrays_goals = goals[0...arrays.size]
-
-
1
assert_equal [[1,2,3],[]], Porolog::unify_arrays_with_some_tails(arrays, arrays_goals, []), name
-
end
-
-
1
it 'should merge values with an unknown tail' do
-
arrays = [
-
1
[1, 2, 3, Porolog::UNKNOWN_TAIL],
-
[nil, nil, nil, 4, nil],
-
[nil, nil, nil, nil, 5],
-
]
-
1
arrays_goals = goals[0...arrays.size]
-
-
1
assert_equal [[1,2,3,4,5],[]], Porolog::unify_arrays_with_some_tails(arrays, arrays_goals, []), name
-
end
-
-
1
it 'should return nil when the finite arrays have different sizes' do
-
arrays = [
-
1
[nil, Porolog::UNKNOWN_TAIL],
-
[nil, nil, nil, nil],
-
[nil, nil, nil, nil, nil, nil],
-
]
-
1
arrays_goals = goals[0...arrays.size]
-
-
1
assert_nil Porolog::unify_arrays_with_some_tails(arrays, arrays_goals, []), name
-
-
expected_log = [
-
1
'Cannot unify different sizes of arrays: [nil, ...] with [nil, nil, nil, nil] with [nil, nil, nil, nil, nil, nil]',
-
]
-
-
1
assert_equal expected_log, g1.log
-
1
assert_equal expected_log, g2.log
-
1
assert_equal expected_log, g3.log
-
end
-
-
1
it 'should return nil when the arrays have incompatible values' do
-
arrays = [
-
1
[1, 2, 3, Porolog::UNKNOWN_TAIL],
-
[nil, nil, nil, 4, nil],
-
[5, nil, nil, nil, 5],
-
]
-
1
arrays_goals = goals[0...arrays.size]
-
-
1
assert_nil Porolog::unify_arrays_with_some_tails(arrays, arrays_goals, []), name
-
-
expected_log = [
-
1
'Cannot unify enough elements: Goal1.1 with Goal3.5',
-
]
-
-
1
assert_equal expected_log, g1.log
-
1
assert_equal expected_log, g2.log
-
1
assert_equal expected_log, g3.log
-
end
-
-
1
it 'should return necessary unification to unify variables' do
-
arrays = [
-
1
[1, 2, nil, Porolog::UNKNOWN_TAIL],
-
[:j, :n, :n, 4, nil],
-
[:x, nil, :m, :y, :z],
-
]
-
1
arrays_goals = goals[0...arrays.size]
-
-
1
merged, unifications = Porolog::unify_arrays_with_some_tails(arrays, arrays_goals, [])
-
-
1
expected_merged = [1, 2, nil, 4, g3[:z]]
-
expected_unifications = [
-
1
[g2.variable(:j), g1.value(1), g2, g1],
-
[g3.variable(:x), g1.value(1), g3, g1],
-
[g2.variable(:j), g3.variable(:x), g2, g3],
-
[g2.variable(:n), g1.value(2), g2, g1],
-
[g2.variable(:n), g3.variable(:m), g2, g3],
-
[g3.variable(:y), g2.value(4), g3, g2],
-
]
-
-
1
assert_equal [expected_merged, expected_unifications], [merged, unifications], name
-
end
-
-
1
it 'should handle a range of cases' do
-
[
-
1
[[[], [1,Porolog::UNKNOWN_TAIL] ], nil, ['Cannot unify enough elements: [] with [Goal2.1, ...]']],
-
[[[]/:t, [1] ], [[1], [[g1.variable(:t), [1], g1, g2]]]],
-
[[[], [1,2]/:s ], nil, ['Cannot unify enough elements: [] with [Goal2.1, Goal2.2, *Goal2.:s]']],
-
[[[]/:t, [1,2] ], [[1,2], [[g1.variable(:t), [1,2], g1, g2]]]],
-
[[[1,2,3], [1,Porolog::UNKNOWN_TAIL] ], [[1,2,3],[]]],
-
[[[1,2,3,Porolog::UNKNOWN_TAIL], [1] ], nil, ['Cannot unify enough elements: [Goal1.1, Goal1.2, Goal1.3, ...] with [Goal2.1]']],
-
[[[1], [1,2,3,Porolog::UNKNOWN_TAIL] ], nil, ['Cannot unify enough elements: [Goal1.1] with [Goal2.1, Goal2.2, Goal2.3, ...]']],
-
[[[1]/:z, [1,2,3] ], [[1,2,3], [[g1.variable(:z), [2, 3], g1, g2]]]],
-
[[[:x,:y,:z], [1,2,3,4,Porolog::UNKNOWN_TAIL]], nil, ['Cannot unify enough elements: [Goal1.:x, Goal1.:y, Goal1.:z] with [Goal2.1, Goal2.2, Goal2.3, Goal2.4, ...]']],
-
[[[:x,:y,:z,Porolog::UNKNOWN_TAIL], [1,2,3,4] ], [
-
[1,2,3,4],
-
[
-
[g1.variable(:x), g2.value(1), g1, g2],
-
[g1.variable(:y), g2.value(2), g1, g2],
-
[g1.variable(:z), g2.value(3), g1, g2]
-
]
-
]],
-
].each do |arrays, expected, expected_log|
-
10
arrays_goals = goals[0...arrays.size]
-
-
10
result = Porolog::unify_arrays_with_some_tails(arrays, arrays_goals, [])
-
-
10
if expected.nil?
-
5
assert_nil result
-
-
5
arrays_goals.uniq.each do |goal|
-
10
assert_equal expected_log, goal.log
-
10
goal.log[0..-1] = []
-
end
-
else
-
5
assert_equal expected, result
-
end
-
end
-
end
-
-
1
it 'should raise a no goal error when no goals are supplied and the arrays have no embedded goals' do
-
arrays = [
-
1
[nil, nil, nil, nil, Porolog::UNKNOWN_TAIL],
-
[1, 2, 3]
-
]
-
1
arrays_goals = [nil] * arrays.size
-
-
1
error = assert_raises Porolog::NoGoalError do
-
1
Porolog::unify_arrays_with_some_tails(arrays, arrays_goals, [])
-
end
-
-
1
assert_equal '[nil, nil, nil, nil, ...] has no associated goal! Cannot variablise!', error.message
-
end
-
-
1
it 'should derive goals from embedded goals when the goals are not supplied' do
-
1
arrays_goals = [g1, nil]
-
arrays = [
-
1
[nil, nil, nil, nil, Porolog::UNKNOWN_TAIL],
-
[1, 2, g2[3], 4]
-
]
-
-
3
refute arrays.all?{|array| Porolog::has_tail?(array, false) }, "not all arrays should have a tail"
-
2
refute arrays.all?{|array| !Porolog::has_tail?(array, false) }, "not all arrays should not have a tail"
-
-
1
merged, unifications = Porolog::unify_arrays_with_some_tails(arrays, arrays_goals, [])
-
-
1
expected_merged = [1,2,3,4]
-
1
expected_unifications = [
-
]
-
-
1
assert_equal expected_merged, merged
-
1
assert_equal expected_unifications, unifications
-
end
-
-
1
it 'should extract embedded variables and values in an array in a value' do
-
arrays = [
-
1
g1.value([nil, :b, nil, nil, 5]),
-
[1, 2, g2[3], 4]/:t
-
]
-
1
arrays_goals = goals[0...arrays.size]
-
-
2
refute arrays.all?{|array| Porolog::has_tail?(array, false) }, "not all arrays should have a tail"
-
3
refute arrays.all?{|array| !Porolog::has_tail?(array, false) }, "not all arrays should not have a tail"
-
-
1
merged, unifications = Porolog::unify_arrays_with_some_tails(arrays, arrays_goals, [])
-
-
1
expected_merged = [1,2,3,4,5]
-
expected_unifications = [
-
1
[g1[:b], g2[2], g1, g2],
-
[g2[:t], [5], g2, g1],
-
]
-
-
1
assert_equal expected_merged, merged
-
1
assert_equal expected_unifications, unifications
-
end
-
-
1
it 'should unify multiple atomics and tails' do
-
arrays = [
-
1
g1.value([nil, :b, nil, nil, 5, 6, 7]),
-
[1, 2, g2[3], 4]/:t,
-
[1, 2, g3[3], 4]/:t,
-
]
-
1
arrays_goals = goals[0...arrays.size]
-
-
2
refute arrays.all?{|array| Porolog::has_tail?(array, false) }, "not all arrays should have a tail"
-
3
refute arrays.all?{|array| !Porolog::has_tail?(array, false) }, "not all arrays should not have a tail"
-
-
1
merged, unifications = Porolog::unify_arrays_with_some_tails(arrays, arrays_goals, [])
-
-
1
expected_merged = [1,2,3,4,5,6,7]
-
expected_unifications = [
-
1
[g1[:b], g2[2], g1, g2],
-
[g1[:b], g3[2], g1, g3],
-
[g2[:t], [5,6,7], g2, g1],
-
[g3[:t], [5,6,7], g3, g1],
-
]
-
-
1
assert_equal expected_merged, merged
-
1
assert_equal expected_unifications, unifications
-
end
-
-
1
it 'should not unify incompatible atomics' do
-
arrays = [
-
1
g1.value([nil, :b, nil, nil, 5]),
-
[1, 2, g2[3], 4]/:t,
-
[1, 3, g3[3], 4]/:t,
-
]
-
1
arrays_goals = goals[0...arrays.size]
-
-
2
refute arrays.all?{|array| Porolog::has_tail?(array, false) }, "not all arrays should have a tail"
-
3
refute arrays.all?{|array| !Porolog::has_tail?(array, false) }, "not all arrays should not have a tail"
-
-
1
assert_nil Porolog::unify_arrays_with_some_tails(arrays, arrays_goals, []), name
-
-
1
assert_equal [
-
'Cannot unify non-variables: 2 with 3'
-
], g1.log
-
-
1
assert_equal [
-
'Cannot unify non-variables: 2 with 3'
-
], g2.log
-
-
1
assert_equal [
-
'Cannot unify non-variables: 2 with 3'
-
], g3.log
-
end
-
-
end
-
-
1
describe '#unify_arrays_with_all_tails' do
-
-
7
let(:g1) { new_goal(:p, :x, :y) }
-
7
let(:g2) { new_goal(:q, :a, :b) }
-
7
let(:g3) { new_goal(:r, :s, :t) }
-
7
let(:goals) { [g1, g2, g3] }
-
-
3
let(:array_with_tail_1) { [1, 2, Porolog::Tail.new(:h)] }
-
2
let(:array_with_tail_2) { [:h, :b]/:t }
-
2
let(:array_with_tail_3) { [:x, :y, Porolog::UNKNOWN_TAIL] }
-
1
let(:head_and_tail_1) { [1, Porolog::Tail.new([2,3])] }
-
2
let(:head_and_tail_2) { [:h]/:t }
-
2
let(:head_and_tail_3) { [:x, Porolog::UNKNOWN_TAIL] }
-
-
1
it 'should call unify_tail_with_tail when no array is headtail' do
-
1
Porolog::expects(:unify_tail_with_tail).times(1)
-
1
Porolog::expects(:unify_headtail_with_tail).times(0)
-
1
Porolog::expects(:unify_headtail_with_headtail).times(0)
-
-
arrays = [
-
1
array_with_tail_1,
-
array_with_tail_2,
-
array_with_tail_3,
-
]
-
1
arrays_goals = goals[0...arrays.size]
-
-
1
Porolog::unify_arrays_with_all_tails(arrays, arrays_goals, [])
-
end
-
-
1
it 'should call unify_headtail_with_tail when all arrays but one are headtail' do
-
1
Porolog::expects(:unify_tail_with_tail).times(0)
-
1
Porolog::expects(:unify_headtail_with_tail).times(1)
-
1
Porolog::expects(:unify_headtail_with_headtail).times(0)
-
-
arrays = [
-
1
array_with_tail_1,
-
head_and_tail_2,
-
head_and_tail_3,
-
]
-
1
arrays_goals = goals[0...arrays.size]
-
-
1
Porolog::unify_arrays_with_all_tails(arrays, arrays_goals, [])
-
end
-
-
1
it 'should include unifications of variables in a tail' do
-
arrays = [
-
1
[nil, nil, nil]/:list,
-
[1, 2, 3, nil, :y, Porolog::UNKNOWN_TAIL],
-
[nil, :x, nil, 4, Porolog::Tail.new([nil,6])],
-
]
-
1
arrays_goals = goals[0...arrays.size]
-
-
1
g1.instantiate :list, [4,5,:z]
-
-
1
merged, unifications = Porolog::unify_arrays_with_all_tails(arrays, arrays_goals, [])
-
-
1
expected_merged = [1,2,3,4,5,6]
-
expected_unifications = [
-
1
[g3[:x], g2[2], g3, g2],
-
[g2[:y], g1[5], g2, g1],
-
[g1[:z], g3[6], g1, g3],
-
[g1[:z], g1[6], g1, g1],
-
]
-
-
1
assert_equal expected_merged, merged
-
1
assert_equal expected_unifications, unifications
-
end
-
-
1
it 'should not unify arrays where one embedded variable cannot be unified' do
-
1
g1.instantiate(:a, [g1.value(1), g1.value(2), g1.value(3), Porolog::UNKNOWN_TAIL])
-
1
g2.instantiate(:a, :b)
-
1
g3.instantiate(:a, :b)
-
-
arrays = [
-
1
:a,
-
[:a, Porolog::Tail.new([1])],
-
[:a, Porolog::UNKNOWN_TAIL],
-
]
-
1
arrays_goals = goals[0...arrays.size]
-
-
1
assert_nil Porolog::unify_arrays_with_all_tails(arrays, arrays_goals, []), name
-
-
1
assert_equal [
-
'Cannot unify enough elements: Goal1.[Goal1.1, Goal1.2, Goal1.3, ...] with [Goal2.:a, Goal2.1]',
-
'Cannot unify because Goal1.[Goal1.1, Goal1.2, Goal1.3, ...] != [:a, 1] (variable/array != array)',
-
'Cannot unify embedded arrays: :a with [:a, 1]',
-
], g1.log
-
-
1
assert_equal [
-
'Cannot unify enough elements: Goal1.[Goal1.1, Goal1.2, Goal1.3, ...] with [Goal2.:a, Goal2.1]',
-
'Cannot unify because Goal1.[Goal1.1, Goal1.2, Goal1.3, ...] != [:a, 1] (variable/array != array)',
-
'Cannot unify embedded arrays: :a with [:a, 1]',
-
], g2.log
-
-
1
assert_equal [
-
'Cannot unify embedded arrays: :a with [:a, 1]',
-
], g3.log
-
end
-
-
1
it 'should unify arrays where all embedded variables can be unified' do
-
arrays = [
-
1
[g1.variable(:a), g1.value(2), g1.variable(:c), g1.value(4)],
-
[g2.value(1), g1.variable(:b), g2.value(3), Porolog::UNKNOWN_TAIL],
-
[:a, :b, Porolog::Tail.new([3,:d])],
-
]
-
1
arrays_goals = goals[0...arrays.size]
-
-
1
assert arrays.map(&:headtail?).none?
-
-
1
expected_merged = [1,2,3,4]
-
expected_unifications = [
-
1
[g1.variable(:a), g2.value(1), g1, g2],
-
[g1.variable(:a), g3.variable(:a), g1, g3],
-
[g3.variable(:a), g2.value(1), g3, g2],
-
[g1.variable(:b), g1.value(2), g1, g1],
-
[g3.variable(:b), g1.value(2), g3, g1],
-
[g1.variable(:b), g3.variable(:b), g1, g3],
-
[g1.variable(:c), g2.value(3), g1, g2],
-
[g1.variable(:c), g3.value(3), g1, g3],
-
[g3.variable(:d), g1.value(4), g3, g1]
-
]
-
1
merged, unifications = Porolog::unify_arrays_with_all_tails(arrays, arrays_goals, [])
-
-
1
assert_equal [], g1.log
-
1
assert_equal [], g2.log
-
1
assert_equal [], g3.log
-
-
1
assert_equal expected_merged, merged
-
1
assert_equal expected_unifications, unifications
-
end
-
-
1
it 'should unify embedded arrays where all embedded variables can be unified' do
-
arrays = [
-
1
:e,
-
[[1],[2],[3],[4]],
-
[[1],[2],[3],[4]],
-
]
-
1
arrays_goals = goals[0...arrays.size]
-
-
1
assert arrays.map(&:headtail?).none?
-
-
1
expected_merged = [[1],[2],[3],[4]]
-
expected_unifications = [
-
1
[:e, [[1], [2], [3], [4]], g1, g2]
-
]
-
1
merged, unifications = Porolog::unify_arrays_with_all_tails(arrays, arrays_goals, [])
-
-
1
assert_equal [], g1.log
-
1
assert_equal [], g2.log
-
1
assert_equal [], g3.log
-
-
1
assert_equal expected_merged, merged
-
1
assert_equal expected_unifications, unifications
-
end
-
-
end
-
-
1
describe '#unify_tail_with_tail' do
-
-
6
let(:g1) { new_goal(:p, :x, :y) }
-
6
let(:g2) { new_goal(:q, :a, :b) }
-
6
let(:g3) { new_goal(:r, :s, :t) }
-
6
let(:g4) { new_goal(:j, :k, :l) }
-
6
let(:goals) { [g1, g2, g3, g4] }
-
-
1
it 'should reject a non-headtail array' do
-
arrays = [
-
1
[1, Porolog::Tail.new([2,3])],
-
[4],
-
[7, Porolog::UNKNOWN_TAIL],
-
]
-
1
arrays_goals = goals[0...arrays.size]
-
-
1
refute arrays.all?(&:headtail?)
-
-
1
assert_nil Porolog::unify_tail_with_tail(arrays, arrays_goals, [])
-
-
1
msg = 'Wrong method called to unify [[1, *[2, 3]], [4], [7, ...]]'
-
-
1
assert_equal [msg], g1.log
-
1
assert_equal [msg], g2.log
-
1
assert_equal [msg], g3.log
-
end
-
-
1
it 'should process more than two arrays that are a non-headtail' do
-
arrays = [
-
1
[1, 2, Porolog::Tail.new([3,4])],
-
[:a, :b]/:c,
-
[:d, :e, Porolog::UNKNOWN_TAIL],
-
]
-
1
arrays_goals = goals[0...arrays.size]
-
-
4
assert arrays.all?{|array| Porolog::has_tail?(array, false) }
-
1
assert arrays.map(&:headtail?).map(&:!).all?
-
-
1
merged, unifications = Porolog::unify_tail_with_tail(arrays, arrays_goals, [])
-
-
1
expected_merged = [1,2,3,4]
-
-
expected_unifications = [
-
1
[g2.variable(:a), g1.value(1), g2, g1],
-
[g3.variable(:d), g1.value(1), g3, g1],
-
[g2.variable(:a), g3.variable(:d), g2, g3],
-
[g2.variable(:b), g1.value(2), g2, g1],
-
[g3.variable(:e), g1.value(2), g3, g1],
-
[g2.variable(:b), g3.variable(:e), g2, g3],
-
[g2.variable(:c), g1.variablise([3,4]), g2, g1],
-
]
-
-
1
assert_equal expected_merged, merged
-
1
assert_equal expected_unifications, unifications
-
end
-
-
1
it 'should unify arrays of different lengths' do
-
arrays = [
-
1
[1, 2, Porolog::UNKNOWN_TAIL],
-
[nil, nil, 3, 4, Porolog::UNKNOWN_TAIL],
-
[nil, nil, nil, nil, 5, 6, Porolog::UNKNOWN_TAIL],
-
]
-
1
arrays_goals = goals[0...arrays.size]
-
-
4
assert arrays.all?{|array| Porolog::has_tail?(array) }
-
1
assert arrays.map(&:headtail?).map(&:!).all?
-
-
1
merged, unifications = Porolog::unify_tail_with_tail(arrays, arrays_goals, [])
-
-
expected_merged = [
-
1
g1.value(1),
-
g1.value(2),
-
g2.value(3),
-
g2.value(4),
-
g3.value(5),
-
g3.value(6),
-
Porolog::UNKNOWN_TAIL,
-
]
-
-
1
expected_unifications = [
-
]
-
-
1
assert_equal expected_merged, merged
-
1
assert_equal expected_unifications, unifications
-
end
-
-
1
it 'should unify all combinations of tails and tails' do
-
arrays = [
-
1
[:a,:b]/:c,
-
[:d,:e]/:f,
-
[:g,:h]/:i,
-
[:j,:k]/:l,
-
]
-
1
arrays_goals = goals[0...arrays.size]
-
-
5
assert arrays.all?{|array| Porolog::has_tail?(array) }
-
1
assert arrays.map(&:headtail?).map(&:!).all?
-
-
1
merged, unifications = Porolog::unify_tail_with_tail(arrays, arrays_goals, [])
-
-
1
expected_merged = [nil, nil, Porolog::UNKNOWN_TAIL]
-
-
expected_unifications = [
-
1
[g1[:a], g2[:d], g1, g2],
-
[g1[:a], g3[:g], g1, g3],
-
[g1[:a], g4[:j], g1, g4],
-
[g2[:d], g3[:g], g2, g3],
-
[g2[:d], g4[:j], g2, g4],
-
[g3[:g], g4[:j], g3, g4],
-
[g1[:b], g2[:e], g1, g2],
-
[g1[:b], g3[:h], g1, g3],
-
[g1[:b], g4[:k], g1, g4],
-
[g2[:e], g3[:h], g2, g3],
-
[g2[:e], g4[:k], g2, g4],
-
[g3[:h], g4[:k], g3, g4],
-
[g1[:c], g2[:f], g1, g2],
-
[g1[:c], g3[:i], g1, g3],
-
[g1[:c], g4[:l], g1, g4],
-
[g2[:f], g3[:i], g2, g3],
-
[g2[:f], g4[:l], g2, g4],
-
[g3[:i], g4[:l], g3, g4],
-
]
-
-
1
assert_equal expected_merged, merged
-
1
assert_equal expected_unifications, unifications
-
end
-
-
1
it 'should not unify ununifiable arrays' do
-
arrays = [
-
1
[1, 2, 3, 4, Porolog::UNKNOWN_TAIL],
-
[1, 4, 3, 2, Porolog::UNKNOWN_TAIL],
-
]
-
1
arrays_goals = goals[0...arrays.size]
-
-
3
assert arrays.all?{|array| Porolog::has_tail?(array) }
-
1
assert arrays.map(&:headtail?).map(&:!).all?
-
-
1
assert_nil Porolog::unify_tail_with_tail(arrays, arrays_goals, []), name
-
-
expected_log = [
-
1
'Cannot unify incompatible values: 2 with 4',
-
'Cannot unify Goal1.2 with Goal2.4',
-
]
-
-
1
arrays_goals.each do |goal|
-
2
assert_equal expected_log, goal.log
-
end
-
end
-
-
end
-
-
1
describe '#unify_headtail_with_tail' do
-
-
10
let(:g1) { new_goal(:p, :x, :y) }
-
10
let(:g2) { new_goal(:q, :a, :b) }
-
10
let(:g3) { new_goal(:r, :s, :t) }
-
10
let(:g4) { new_goal(:j, :k, :l) }
-
10
let(:goals) { [g1, g2, g3, g4] }
-
-
1
it 'should reject a non-tail array' do
-
arrays = [
-
1
[1, Porolog::Tail.new([2,3])],
-
[4],
-
[1, 2, 3, 7, Porolog::UNKNOWN_TAIL],
-
]
-
1
arrays_goals = goals[0...arrays.size]
-
-
2
refute arrays.all?{|array| Porolog::has_tail?(array) }
-
-
1
assert_nil Porolog::unify_headtail_with_tail(arrays, arrays_goals, [])
-
-
1
msg = 'Wrong method called to unify [[1, *[2, 3]], [4], [1, 2, 3, 7, ...]]'
-
-
1
assert_equal [msg], g1.log
-
1
assert_equal [msg], g2.log
-
1
assert_equal [msg], g3.log
-
end
-
-
1
it 'should unify more than two arrays that have a tail' do
-
arrays = [
-
1
[1, Porolog::Tail.new([2,3])],
-
[:a, :b]/:f,
-
[:c, Porolog::UNKNOWN_TAIL],
-
[:d, :e, Porolog::UNKNOWN_TAIL]
-
]
-
1
arrays_goals = goals[0...arrays.size]
-
-
5
assert arrays.all?{|array| Porolog::has_tail?(array, false) }
-
-
1
merged, unifications = Porolog::unify_headtail_with_tail(arrays, arrays_goals, [])
-
-
1
expected_merged = [1,2,3]
-
-
expected_unifications = [
-
1
[g3[:c], g1[1], g3, g1],
-
[g2[:a], g4[:d], g2, g4],
-
[g2[:b], g4[:e], g2, g4],
-
[g2[:a], g1[1], g2, g1],
-
[g2[:b], g1[2], g2, g1],
-
[g2[:f], [3], g2, g1],
-
[g4[:d], g1[1], g4, g1],
-
[g4[:e], g1[2], g4, g1],
-
[g3[:c], g2[:a], g3, g2],
-
[g3[:c], g4[:d], g3, g4]
-
]
-
-
1
assert_equal expected_merged, merged
-
1
assert_equal expected_unifications, unifications
-
end
-
-
1
it 'should unify unifiable arrays with different length tails' do
-
arrays = [
-
1
[1, Porolog::UNKNOWN_TAIL],
-
[:a, :b, :c, :d]/:f,
-
[:c, Porolog::UNKNOWN_TAIL],
-
[:d, 2, Porolog::UNKNOWN_TAIL]
-
]
-
1
arrays_goals = goals[0...arrays.size]
-
-
5
assert arrays.all?{|array| Porolog::has_tail?(array, false) }
-
-
1
merged, unifications = Porolog::unify_headtail_with_tail(arrays, arrays_goals, [])
-
-
1
expected_merged = [1, 2, nil, nil, Porolog::UNKNOWN_TAIL]
-
-
expected_unifications = [
-
1
[g3[:c], g1[1], g3, g1],
-
[g2[:a], g4[:d], g2, g4],
-
[g2[:b], g4[2], g2, g4],
-
[g2[:a], g1[1], g2, g1],
-
[g4[:d], g1[1], g4, g1],
-
[g3[:c], g2[:a], g3, g2],
-
[g3[:c], g4[:d], g3, g4],
-
]
-
-
1
assert_equal expected_merged, merged
-
1
assert_equal expected_unifications, unifications
-
end
-
-
1
it 'should not unify unifiable heads across a headtail and an array with a tail' do
-
1
g1.instantiate :h, 1
-
1
g2.instantiate :h, 2
-
arrays = [
-
1
[:h]/:t,
-
[:h,:b]/:t,
-
[:h]/:t,
-
[:h]/:t,
-
]
-
1
arrays_goals = goals[0...arrays.size]
-
-
5
assert arrays.all?{|array| Porolog::has_tail?(array, false) }, 'all arrays should have a tail'
-
1
refute arrays.map(&:headtail?).all?, 'not all arrays should be a headtail array'
-
1
refute arrays.map(&:headtail?).map(&:!).all?, 'not all arrays should be an array with a tail'
-
-
1
assert_nil Porolog::unify_headtail_with_tail(arrays, arrays_goals, []), name
-
-
1
assert_equal [
-
'Cannot unify because 1 != 2 (variable != variable)',
-
'Cannot unify heads: Goal1.:h with Goal2.:h',
-
], g1.log
-
1
assert_equal [
-
'Cannot unify because 1 != 2 (variable != variable)',
-
'Cannot unify heads: Goal1.:h with Goal2.:h',
-
], g2.log
-
1
assert_equal [
-
], g3.log
-
1
assert_equal [
-
], g4.log
-
end
-
-
1
it 'should not unify tails with different values' do
-
1
g1.instantiate :t, [2,3]
-
1
g2.instantiate :t, [2,4]
-
1
g3.instantiate :t, 4
-
arrays = [
-
1
[:h]/:t,
-
[:h]/:t,
-
[:h,:b]/:t,
-
[:h]/:t,
-
]
-
1
arrays_goals = goals[0...arrays.size]
-
-
5
assert arrays.all?{|array| Porolog::has_tail?(array, false) }, 'all arrays should have a tail'
-
1
refute arrays.map(&:headtail?).all?, 'not all arrays should be a headtail array'
-
1
refute arrays.map(&:headtail?).map(&:!).all?, 'not all arrays should be an array with a tail'
-
-
1
assert_nil Porolog::unify_headtail_with_tail(arrays, arrays_goals, []), name
-
-
1
assert_equal [
-
'Cannot unify because [2, 3] != [2, 4] (variable != variable)',
-
'Cannot unify headtail tails: Goal1.:t with Goal2.:t',
-
'Could not unify headtail arrays: [Goal1.:h, Goal1.2, Goal1.3] with [Goal2.:h, Goal2.2, Goal2.4] with [Goal4.:h, *Goal4.:t]',
-
], g1.log
-
1
assert_equal [
-
'Cannot unify because [2, 3] != [2, 4] (variable != variable)',
-
'Cannot unify headtail tails: Goal1.:t with Goal2.:t',
-
'Could not unify headtail arrays: [Goal1.:h, Goal1.2, Goal1.3] with [Goal2.:h, Goal2.2, Goal2.4] with [Goal4.:h, *Goal4.:t]',
-
], g2.log
-
1
assert_equal [
-
], g3.log
-
1
assert_equal [
-
'Could not unify headtail arrays: [Goal1.:h, Goal1.2, Goal1.3] with [Goal2.:h, Goal2.2, Goal2.4] with [Goal4.:h, *Goal4.:t]',
-
], g4.log
-
end
-
-
1
it 'should not unify variable length arrays that have been instantiatd to different lengths' do
-
1
g1.instantiate :t, [2,3]
-
1
g2.instantiate :t, 3
-
1
g3.instantiate :t, [2,3,4]
-
arrays = [
-
1
[:h]/:t,
-
[:h,:b]/:t,
-
[:h]/:t,
-
[:h]/:t,
-
]
-
1
arrays_goals = goals[0...arrays.size]
-
-
5
assert arrays.all?{|array| Porolog::has_tail?(array, false) }, 'all arrays should have a tail'
-
1
refute arrays.map(&:headtail?).all?, 'not all arrays should be a headtail array'
-
1
refute arrays.map(&:headtail?).map(&:!).all?, 'not all arrays should be an array with a tail'
-
-
1
assert_nil Porolog::unify_headtail_with_tail(arrays, arrays_goals, []), name
-
-
1
assert_equal [
-
], g1.log
-
1
assert_equal [
-
], g2.log
-
1
assert_equal [
-
'Cannot unify [Goal3.:h, Goal3.2, Goal3.3, Goal3.4] because it has a different length from 3'
-
], g3.log
-
1
assert_equal [
-
], g4.log
-
end
-
-
1
it 'should not unify incompatible tails' do
-
1
g1.instantiate :t, [2,3]
-
1
g2.instantiate :b, 2
-
1
g2.instantiate :t, [3]
-
1
g3.instantiate :t, [4]
-
arrays = [
-
1
[:h]/:t,
-
[:h,:b]/:t,
-
[:h,:b]/:t,
-
[:h]/:t,
-
]
-
1
arrays_goals = goals[0...arrays.size]
-
-
5
assert arrays.all?{|array| Porolog::has_tail?(array, false) }, 'all arrays should have a tail'
-
1
refute arrays.map(&:headtail?).all?, 'not all arrays should be a headtail array'
-
1
refute arrays.map(&:headtail?).map(&:!).all?, 'not all arrays should be an array with a tail'
-
-
1
assert_nil Porolog::unify_headtail_with_tail(arrays, arrays_goals, []), name
-
-
1
assert_equal [
-
], g1.log
-
1
assert_equal [
-
'Cannot unify incompatible values: 3 with 4',
-
'Cannot unify: [3] with [4]',
-
], g2.log
-
1
assert_equal [
-
'Cannot unify incompatible values: 3 with 4',
-
'Cannot unify: [3] with [4]',
-
], g3.log
-
1
assert_equal [
-
], g4.log
-
end
-
-
1
it 'should expand tails that overlap extended heads' do
-
1
g1.instantiate :t, [3,3]
-
1
g2.instantiate :b, 2
-
1
g2.instantiate :t, [3]
-
arrays = [
-
1
[:h]/:t,
-
[:h,:b]/:t,
-
[:h,:b]/:t,
-
[:h]/:t,
-
]
-
1
arrays_goals = goals[0...arrays.size]
-
-
5
assert arrays.all?{|array| Porolog::has_tail?(array, false) }, 'all arrays should have a tail'
-
1
refute arrays.map(&:headtail?).all?, 'not all arrays should be a headtail array'
-
1
refute arrays.map(&:headtail?).map(&:!).all?, 'not all arrays should be an array with a tail'
-
-
1
assert_nil Porolog::unify_headtail_with_tail(arrays, arrays_goals, []), name
-
-
1
assert_equal [
-
'Cannot unify incompatible values: 3 with 2',
-
'Cannot unify because Goal1.[3, 3] != [Goal2.:b, *Goal2.:t] (variable/array != array)',
-
'Cannot unify tails: Goal1.:t with [Goal2.:b, *Goal2.:t]',
-
], g1.log
-
1
assert_equal [
-
'Cannot unify incompatible values: 3 with 2',
-
'Cannot unify because Goal1.[3, 3] != [Goal2.:b, *Goal2.:t] (variable/array != array)',
-
'Cannot unify tails: Goal1.:t with [Goal2.:b, *Goal2.:t]',
-
], g2.log
-
1
assert_equal [
-
], g3.log
-
1
assert_equal [
-
], g4.log
-
end
-
-
1
it 'prioritises X over Y when merging' do
-
# -- atomic over known array over non-array over unknown array/tail --
-
# atomic over known
-
# known array over non-array
-
# non-array over unknown array/tail
-
# atomic over non-array
-
# array over unknown array/tail
-
# atomic over unknown array/tail
-
-
1
g1.instantiate :t, [1,2,3]
-
arrays = [
-
1
:h/:t,
-
Porolog::UNKNOWN_ARRAY,
-
]
-
1
arrays_goals = goals[0...arrays.size]
-
-
1
merged, unifications = Porolog::unify_headtail_with_tail(arrays, arrays_goals, [])
-
-
1
expected_merged = [nil, 1, 2, 3]
-
1
expected_unifications = []
-
-
1
assert_equal expected_merged, merged
-
1
assert_equal expected_unifications, unifications
-
end
-
-
end
-
-
1
describe '#unify_headtail_with_headtail' do
-
-
5
let(:g1) { new_goal(:p, :x, :y) }
-
5
let(:g2) { new_goal(:q, :a, :b) }
-
5
let(:g3) { new_goal(:r, :s, :t) }
-
5
let(:g4) { new_goal(:j, :k, :l) }
-
5
let(:goals) { [g1, g2, g3, g4] }
-
-
1
it 'should reject a non-headtail array' do
-
arrays = [
-
1
[1, Porolog::Tail.new([2,3])],
-
[4],
-
[7, Porolog::UNKNOWN_TAIL],
-
]
-
1
arrays_goals = goals[0...arrays.size]
-
-
1
refute arrays.all?(&:headtail?)
-
-
1
assert_nil Porolog::unify_headtail_with_headtail(arrays, arrays_goals, [])
-
-
1
msg = 'Wrong method called to unify [[1, *[2, 3]], [4], [7, ...]]'
-
-
1
arrays_goals.each do |goal|
-
3
assert_equal [msg], goal.log
-
end
-
end
-
-
1
it 'should not unify unequal heads' do
-
arrays = [
-
1
[1, Porolog::Tail.new([2,3])],
-
[4]/:y,
-
[7, Porolog::UNKNOWN_TAIL],
-
]
-
1
arrays_goals = goals[0...arrays.size]
-
-
1
assert arrays.all?(&:headtail?), "arrays should all be headtails: #{arrays.reject(&:headtail?).map(&:inspect).join(' and ')}"
-
-
1
assert_nil Porolog::unify_headtail_with_headtail(arrays, arrays_goals, [])
-
-
1
assert_equal [
-
'Cannot unify because 1 != 4 (atomic != atomic)',
-
'Cannot unify headtail heads: "1 with 4"',
-
], g1.log
-
1
assert_equal [
-
'Cannot unify because 1 != 4 (atomic != atomic)',
-
'Cannot unify headtail heads: "1 with 4"',
-
], g2.log
-
1
assert_equal [
-
], g3.log
-
end
-
-
1
it 'should process more than two arrays that are a headtail' do
-
arrays = [
-
1
[1, Porolog::Tail.new([2,3])],
-
[:a]/:f,
-
[:c, Porolog::UNKNOWN_TAIL],
-
]
-
1
arrays_goals = goals[0...arrays.size]
-
-
1
assert arrays.all?(&:headtail?)
-
-
1
merged, unifications = Porolog::unify_headtail_with_headtail(arrays, arrays_goals, [])
-
-
1
expected_merged = [1,2,3]
-
-
expected_unifications = [
-
1
[:a, 1, g2, g1],
-
[:f, [2,3], g2, g1],
-
[:c, 1, g3, g1],
-
[:a, :c, g2, g3],
-
[:f, Porolog::UNKNOWN_TAIL, g2, g3],
-
]
-
-
1
assert_equal expected_merged, merged
-
1
assert_equal expected_unifications, unifications
-
-
1
arrays_goals.each do |goal|
-
3
assert_equal [], goal.log
-
end
-
end
-
-
1
it 'should unify all combinations of heads and tails' do
-
arrays = [
-
1
[:a]/:b,
-
[:c]/:d,
-
[:e]/:f,
-
[:g]/:h,
-
]
-
1
arrays_goals = goals[0...arrays.size]
-
-
1
assert arrays.all?(&:headtail?)
-
-
1
merged, unifications = Porolog::unify_headtail_with_headtail(arrays, arrays_goals, [])
-
-
1
expected_merged = [nil, Porolog::UNKNOWN_TAIL]
-
-
expected_unifications = [
-
1
[:a, :c, g1, g2],
-
[:b, :d, g1, g2],
-
[:a, :e, g1, g3],
-
[:b, :f, g1, g3],
-
[:a, :g, g1, g4],
-
[:b, :h, g1, g4],
-
[:c, :e, g2, g3],
-
[:d, :f, g2, g3],
-
[:c, :g, g2, g4],
-
[:d, :h, g2, g4],
-
[:e, :g, g3, g4],
-
[:f, :h, g3, g4],
-
]
-
-
1
assert_equal expected_merged, merged
-
1
assert_equal expected_unifications, unifications
-
-
1
arrays_goals.each do |goal|
-
4
assert_equal [], goal.log
-
end
-
end
-
-
end
-
-
1
describe '#builtin' do
-
-
1
it 'should create a Predicate' do
-
# -- Precondition Baseline --
-
1
assert_equal 0, Porolog::Scope[:default].predicates.size
-
-
# -- Test --
-
1
single_predicate = Porolog::builtin :member
-
-
# -- Compare Result Against Baseline --
-
1
assert_equal 1, Porolog::Scope[:default].predicates.size
-
1
assert_equal :member, Porolog::Scope[:default].predicates.first.name
-
1
assert_Predicate single_predicate, :member, []
-
end
-
-
1
it 'should define a method to create Arguments for solving' do
-
1
Object.class_eval{
-
1
remove_method(:member) if public_method_defined?(:member)
-
}
-
-
1
refute respond_to?(:member)
-
-
1
Porolog::builtin :member
-
-
1
assert respond_to?(:member)
-
1
assert_Arguments member(:X, ['left','right']), :member, [:X, ['left','right']]
-
-
1
assert_includes methods, :member
-
end
-
-
1
it 'should create multiple Predicates' do
-
1
assert_equal 0, Porolog::Scope[:default].predicates.size
-
-
1
multiple_predicates = Porolog::builtin :member, :append, :is
-
-
1
assert_equal 3, Porolog::Scope[:default].predicates.size
-
1
assert_equal :member, Porolog::Scope[:default].predicates[0].name
-
1
assert_equal :append, Porolog::Scope[:default].predicates[1].name
-
1
assert_equal :is, Porolog::Scope[:default].predicates[2].name
-
1
assert_instance_of Array, multiple_predicates
-
1
assert_equal 3, multiple_predicates.size
-
1
assert_Predicate multiple_predicates[0], :member, []
-
1
assert_Predicate multiple_predicates[1], :append, []
-
1
assert_Predicate multiple_predicates[2], :is, []
-
end
-
-
1
it 'should define multiple methods to create Arguments for solving' do
-
1
Object.class_eval{
-
1
remove_method(:member) if public_method_defined?(:member)
-
1
remove_method(:append) if public_method_defined?(:append)
-
}
-
-
1
refute respond_to?(:member)
-
1
refute respond_to?(:append)
-
-
1
Porolog::builtin :member, :append
-
-
1
assert respond_to?(:member)
-
1
assert respond_to?(:append)
-
1
assert_Arguments member(:X, :L), :member, [:X, :L]
-
1
assert_Arguments append(:A, :B, :AB), :append, [:A, :B, :AB]
-
end
-
-
1
it 'should define a method in a base class as an instance method and a class method' do
-
1
Object.class_eval{
-
1
remove_method(:member) if public_method_defined?(:member)
-
}
-
-
1
class Base2 ; end
-
-
1
refute_includes Base2.methods, :member
-
1
refute_includes Base2.instance_methods, :member
-
-
1
Porolog::builtin :member, class_base: Base2
-
-
1
assert_includes Base2.methods, :member
-
1
assert_includes Base2.instance_methods, :member
-
-
1
refute_includes methods, :member
-
-
1
class Base2
-
1
def process
-
1
member(:X, [4,5,6]).solve_for(:X)
-
end
-
end
-
-
1
assert_solutions Base2.member(:X, [1,2,3]), [
-
{ X: 1 },
-
{ X: 2 },
-
{ X: 3 },
-
]
-
-
1
assert_equal [4,5,6], Base2.new.process
-
end
-
-
end
-
-
1
describe '#_' do
-
-
1
let(:goal) { new_goal :p, :x, :y }
-
-
1
it 'should return a different variable on each call' do
-
# _ is overridden by MiniTest
-
1
variable1 = Porolog::_
-
1
variable2 = Porolog::_
-
1
variable3 = Porolog::_
-
-
1
assert_equal :_a, variable1
-
1
assert_equal :_b, variable2
-
1
assert_equal :_c, variable3
-
-
1
refute_equal variable1, variable2
-
1
refute_equal variable2, variable3
-
1
refute_equal variable3, variable1
-
end
-
-
end
-
-
end
-
#
-
# test/porolog/predicate/builtin_test.rb - Test Suite for Porolog::Predicate::Builtin
-
#
-
# Luis Esteban 30 July 2020
-
# created
-
#
-
-
1
require_relative '../../test_helper'
-
-
1
describe 'Porolog' do
-
-
1
describe 'Predicate' do
-
-
1
describe 'Builtin' do
-
-
1
before(:all) do
-
140
reset
-
end
-
-
1
describe '#write' do
-
-
1
it 'should output an atom' do
-
1
assert_output 'hello' do
-
1
Porolog::builtin :write
-
-
1
assert_solutions write('hello'), [{}]
-
end
-
end
-
-
1
it 'should output an integer' do
-
1
assert_output '456' do
-
1
Porolog::builtin :write
-
-
1
assert_solutions write(456), [{}]
-
end
-
end
-
-
1
it 'should output an uninstantiated variable' do
-
1
assert_output ':X' do
-
1
Porolog::builtin :write
-
-
1
assert_solutions write(:X), [{ X: nil }]
-
end
-
end
-
-
1
it 'should output an instantiated variable' do
-
1
assert_output '72' do
-
1
Porolog::builtin :write, :is
-
1
Porolog::predicate :inst
-
-
1
inst(:X, :Y) << [
-
1
is(:Y, :X) {|x| x + 4 },
-
write(:Y)
-
]
-
-
1
assert_solutions inst(23 + 45, :Y), [{ Y: 72 }]
-
end
-
end
-
-
end
-
-
1
describe '#writenl' do
-
-
1
it 'should output an atom and a new line' do
-
1
assert_output "hello\n" do
-
1
Porolog::builtin :writenl
-
-
1
assert_solutions writenl('hello'), [{}]
-
end
-
end
-
-
1
it 'should output an integer and a new line' do
-
1
assert_output "456\n" do
-
1
Porolog::builtin :writenl
-
-
1
assert_solutions writenl(456), [{}]
-
end
-
end
-
-
1
it 'should output an uninstantiated variable and a new line' do
-
1
assert_output ":X\n" do
-
1
Porolog::builtin :writenl
-
-
1
assert_solutions writenl(:X), [{ X: nil }]
-
end
-
end
-
-
1
it 'should output an instantiated variable and a new line' do
-
1
assert_output "72\n" do
-
1
Porolog::builtin :writenl, :is
-
1
Porolog::predicate :inst
-
-
1
inst(:X, :Y) << [
-
1
is(:Y, :X) {|x| x + 4 },
-
writenl(:Y)
-
]
-
-
1
assert_solutions inst(23 + 45, :Y), [{ Y: 72 }]
-
end
-
end
-
-
end
-
-
1
describe '#nl' do
-
-
1
it 'should output a newline' do
-
1
assert_output "\n\n\n" do
-
1
Porolog::builtin :nl
-
1
Porolog::predicate :f, :n
-
-
1
n(1).fact!
-
1
n(5).fact!
-
1
n(7).fact!
-
-
1
f() << [
-
n(:X),
-
nl
-
]
-
-
1
assert_solutions f(), [
-
{},
-
{},
-
{},
-
]
-
end
-
end
-
-
end
-
-
1
describe '#is' do
-
-
1
it 'should raise an exception when not provided with a variable' do
-
1
Porolog::builtin :is
-
1
Porolog::predicate :is_test
-
-
1
is_test(:variable) << [
-
skipped
# :nocov:
-
skipped
is(6) { 6 }
-
skipped
# :nocov:
-
]
-
-
1
assert_raises Porolog::NonVariableError do
-
1
assert_solutions is_test(6), [{}]
-
end
-
end
-
-
1
it 'should not raise an exception when provided with an instantiated variable' do
-
1
Porolog::builtin :is
-
1
Porolog::predicate :is_test
-
-
1
is_test(:variable) << [
-
1
is(:variable) { 6 }
-
]
-
-
1
assert_solutions is_test(6), [{}]
-
end
-
-
1
it 'should call the block provided and instantiate the variable to the result of the provided block' do
-
1
Porolog::builtin :is
-
1
Porolog::predicate :is_test
-
-
1
calls = []
-
-
1
is_test(:variable1, :variable2) << [
-
is(:variable1) {
-
1
calls << 'first is called'
-
1
'first'
-
},
-
is(:variable2) {
-
1
calls << 'second is called'
-
1
'second'
-
}
-
]
-
-
1
assert_solutions is_test(:X, :Y), [{ X: 'first', Y: 'second'}]
-
1
assert_equal [
-
'first is called',
-
'second is called',
-
], calls
-
end
-
-
1
it 'should return false when the variable cannot be instantiated' do
-
1
Porolog::builtin :is
-
1
Porolog::predicate :is_test
-
-
1
calls = []
-
-
1
is_test(:variable1, :variable2) << [
-
is(:variable1) {
-
1
calls << 'first is called'
-
1
'first'
-
},
-
is(:variable2) {
-
skipped
# :nocov:
-
skipped
calls << 'second is called'
-
skipped
'second'
-
skipped
# :nocov:
-
}
-
]
-
-
1
assert_solutions is_test('one', 'two'), []
-
1
assert_equal [
-
'first is called',
-
], calls
-
end
-
-
end
-
-
1
describe '#eq' do
-
-
1
it 'should return no solutions when given unequal values' do
-
1
Porolog::builtin :eq
-
-
1
assert_solutions eq([3,2,1,4], [1,2,3,4]), []
-
end
-
-
1
it 'should return a solution when given equal values' do
-
1
Porolog::builtin :eq
-
-
1
assert_solutions eq([1,2,3,4], [1,2,3,4]), [{}]
-
end
-
-
1
it 'should return a solution with an unbound variable when given the same unbound variable' do
-
1
Porolog::builtin :eq
-
-
1
assert_solutions eq(:X, :X), [{ X: nil}]
-
end
-
-
1
it 'should return no solutions when given two unbound variables' do
-
1
Porolog::builtin :eq
-
-
1
assert_solutions eq(:X, :Y), []
-
end
-
-
1
it 'should return one solution with unbound variables when given given two uninstantiated variables that are bound to each other' do
-
1
Porolog::builtin :eq, :is
-
1
Porolog::predicate :bind_and_eq
-
-
1
bind_and_eq(:X, :Y) << [
-
1
is(:X, :Y) { |y| y },
-
eq(:X, :Y)
-
]
-
-
1
assert_solutions bind_and_eq(:X, :Y), [{ X: nil, Y: nil }]
-
end
-
-
1
it 'should return no solutions when given two uninstantiated variables that are not bound to each other' do
-
1
Porolog::builtin :eq, :is
-
1
Porolog::predicate :bind_and_eq
-
-
1
bind_and_eq(:X, :Y) << [
-
1
is(:X, :P) { |p| p },
-
1
is(:Y, :Q) { |q| q },
-
eq(:X, :Y)
-
]
-
-
1
assert_solutions bind_and_eq(:X, :Y), []
-
end
-
-
1
it 'should return no solutions when given a value and an uninstantiated variable' do
-
1
Porolog::builtin :eq
-
-
1
assert_solutions eq([1,2,3,4], :L), []
-
1
assert_solutions eq(:L, [1,2,3,4]), []
-
end
-
-
end
-
-
1
describe '#is_eq' do
-
-
1
it 'should return no solutions when given unequal values' do
-
1
Porolog::builtin :is_eq
-
-
1
assert_solutions is_eq([3,2,1,4], [1,2,3,4]), []
-
end
-
-
1
it 'should return a solution when given equal values' do
-
1
Porolog::builtin :is_eq
-
-
1
assert_solutions is_eq([1,2,3,4], [1,2,3,4]), []
-
end
-
-
1
it 'should return a solution with an unbound variable when given the same unbound variable' do
-
1
Porolog::builtin :is_eq
-
-
1
assert_solutions is_eq(:X, :X), [{ X: nil}]
-
end
-
-
1
it 'should return no solutions when given given two unbound variables' do
-
1
Porolog::builtin :is_eq
-
-
1
assert_solutions is_eq(:X, :Y), [{ X: nil, Y: nil }]
-
end
-
-
1
it 'should return one solution with unbound variables when given given two uninstantiated variables that are bound to each other' do
-
1
Porolog::builtin :is_eq, :is
-
1
Porolog::predicate :bind_and_eq
-
-
1
bind_and_eq(:X, :Y) << [
-
1
is(:X, :Y) { |y| y },
-
is_eq(:X, :Y)
-
]
-
-
1
assert_solutions bind_and_eq(:X, :Y), [{ X: nil, Y: nil }]
-
end
-
-
1
it 'should return no solutions when given given two uninstantiated variables that are not bound to each other' do
-
1
Porolog::builtin :is_eq, :is
-
1
Porolog::predicate :bind_and_eq
-
-
1
bind_and_eq(:X, :Y) << [
-
1
is(:X, :P) { |p| p },
-
1
is(:Y, :Q) { |q| q },
-
is_eq(:X, :Y)
-
]
-
-
1
assert_solutions bind_and_eq(:X, :Y), [{ X: nil, Y: nil }]
-
end
-
-
1
it 'should not instantiate the right hand side when not given a left hand side variable' do
-
1
Porolog::builtin :is_eq
-
-
1
assert_solutions is_eq([1,2,3,4], :L), []
-
end
-
-
1
it 'should instantiate the left hand side variable' do
-
1
Porolog::builtin :is_eq
-
-
1
assert_solutions is_eq(:L, [1,2,3,4]), [{ L: [1,2,3,4] }]
-
end
-
-
end
-
-
1
describe '#ruby' do
-
-
1
it 'should execute ruby code' do
-
1
Porolog::builtin :ruby
-
1
Porolog::predicate :logic, :data
-
-
1
data(1).fact!
-
1
data(5).fact!
-
1
data(7).cut_fact!
-
1
data(9).fact!
-
-
1
ruby_log = []
-
-
1
logic(:N) << [
-
ruby(:N){|goal, n|
-
1
ruby_log << (n.type == :variable ? n.to_sym : n)
-
},
-
data(:N),
-
ruby(:N){|goal, n|
-
3
ruby_log << (n.type == :variable ? n.to_sym : n)
-
}
-
]
-
-
1
assert_solutions logic(:N), [
-
{ N: 1 },
-
{ N: 5 },
-
{ N: 7 },
-
]
-
1
assert_equal [:N,1,5,7], ruby_log
-
end
-
-
1
it 'should execute ruby code which triggers a failure' do
-
1
Porolog::builtin :ruby
-
1
Porolog::predicate :logic, :data
-
-
1
data(1).fact!
-
1
data(5).fact!
-
1
data(7).cut_fact!
-
1
data(9).fact!
-
-
1
ruby_log = []
-
-
1
logic(:N) << [
-
ruby(:N){|goal, n|
-
1
ruby_log << (n.type == :variable ? n.to_sym : n)
-
},
-
data(:N),
-
ruby(:N){|goal, n|
-
3
ruby_log << (n.type == :variable ? n.to_sym : n)
-
3
:fail
-
}
-
]
-
-
1
assert_solutions logic(:N), [
-
]
-
1
assert_equal [:N,1,5,7], ruby_log
-
end
-
-
end
-
-
1
describe '#noteq' do
-
-
1
it 'should return a solution when given unequal values' do
-
1
Porolog::builtin :noteq
-
-
1
assert_solutions noteq([3,2,1,4], [1,2,3,4]), [{}]
-
end
-
-
1
it 'should return no solutions when given equal values' do
-
1
Porolog::builtin :noteq
-
-
1
assert_solutions noteq([1,2,3,4], [1,2,3,4]), []
-
end
-
-
1
it 'should return no solutions with an unbound variable when given the same unbound variable' do
-
1
Porolog::builtin :noteq
-
-
1
assert_solutions noteq(:X, :X), []
-
end
-
-
1
it 'should return a solution when given two unbound variables' do
-
1
Porolog::builtin :noteq
-
-
1
assert_solutions noteq(:X, :Y), [{ X: nil, Y: nil }]
-
end
-
-
1
it 'should return no solutions with unbound variables when given given two uninstantiated variables that are bound to each other' do
-
1
Porolog::builtin :noteq, :is
-
1
Porolog::predicate :bind_and_noteq
-
-
1
bind_and_noteq(:X, :Y) << [
-
1
is(:X, :Y) { |y| y },
-
noteq(:X, :Y)
-
]
-
-
1
assert_solutions bind_and_noteq(:X, :Y), []
-
end
-
-
1
it 'should return a solution when given two uninstantiated variables that are not bound to each other' do
-
1
Porolog::builtin :noteq, :is
-
1
Porolog::predicate :bind_and_noteq
-
-
1
bind_and_noteq(:X, :Y) << [
-
1
is(:X, :P) { |p| p },
-
1
is(:Y, :Q) { |q| q },
-
noteq(:X, :Y)
-
]
-
-
1
assert_solutions bind_and_noteq(:X, :Y), [{ X: nil, Y: nil }]
-
end
-
-
1
it 'should return a solution when given an unbound variable' do
-
1
Porolog::builtin :noteq
-
-
1
assert_solutions noteq([1,2,3,4], :L), [{ L: nil }]
-
1
assert_solutions noteq(:L, [1,2,3,4]), [{ L: nil }]
-
end
-
-
end
-
-
1
describe '#is_noteq' do
-
-
1
it 'should return solutions without the exclusions' do
-
1
Porolog::builtin :is_noteq
-
-
1
assert_solutions is_noteq(:X, [1,2,3,4,5], 2, 5), [
-
{ X: 1, _a: [1,3,4] },
-
{ X: 3, _a: [1,3,4] },
-
{ X: 4, _a: [1,3,4] }
-
]
-
end
-
-
1
it 'should return no solutions when the exclusions are not unique' do
-
1
Porolog::builtin :is_noteq
-
-
1
assert_solutions is_noteq(:X, [1,2,3,4,5], 2, 5, 2), []
-
end
-
-
1
it 'should return no solutions when the target is not a variable' do
-
1
Porolog::builtin :is_noteq
-
-
1
assert_solutions is_noteq(7, [1,2,3,4,5], 2, 5, 2), []
-
end
-
-
end
-
-
1
describe '#less' do
-
-
1
it 'should return no solutions when given a greater value' do
-
1
Porolog::builtin :less
-
-
1
assert_solutions less(7, 4), []
-
1
assert_solutions less('h', 'b'), []
-
1
assert_solutions less(1.01, 1.003), []
-
end
-
-
1
it 'should return no solutions when given equal values' do
-
1
Porolog::builtin :less
-
-
1
assert_solutions less(4, 4), []
-
1
assert_solutions less('b', 'b'), []
-
1
assert_solutions less(1.003, 1.003), []
-
end
-
-
1
it 'should return a solution when given lesser values' do
-
1
Porolog::builtin :less
-
-
1
assert_solutions less(2, 4), [{}]
-
end
-
-
1
it 'should return no solutions with an unbound variable when given the same unbound variable' do
-
1
Porolog::builtin :less
-
-
1
assert_solutions less(:X, :X), []
-
end
-
-
1
it 'should return no solutions when given two unbound variables' do
-
1
Porolog::builtin :less
-
-
1
assert_solutions less(:X, :Y), []
-
end
-
-
end
-
-
1
describe '#gtr' do
-
-
1
it 'should return no solutions when given a lesser value' do
-
1
Porolog::builtin :gtr
-
-
1
assert_solutions gtr(4, 7), []
-
1
assert_solutions gtr('b', 'h'), []
-
1
assert_solutions gtr(1.003, 1.01), []
-
end
-
-
1
it 'should return no solutions when given equal values' do
-
1
Porolog::builtin :gtr
-
-
1
assert_solutions gtr(4, 4), []
-
1
assert_solutions gtr('b', 'b'), []
-
1
assert_solutions gtr(1.003, 1.003), []
-
end
-
-
1
it 'should return a solution when given greater values' do
-
1
Porolog::builtin :gtr
-
-
1
assert_solutions gtr(4, 2), [{}]
-
end
-
-
1
it 'should return no solutions with an unbound variable when given the same unbound variable' do
-
1
Porolog::builtin :gtr
-
-
1
assert_solutions gtr(:X, :X), []
-
end
-
-
1
it 'should return no solutions when given two unbound variables' do
-
1
Porolog::builtin :gtr
-
-
1
assert_solutions gtr(:X, :Y), []
-
end
-
-
end
-
-
1
describe '#lesseq' do
-
-
1
it 'should return no solutions when given a greater value' do
-
1
Porolog::builtin :lesseq
-
-
1
assert_solutions lesseq(7, 4), []
-
1
assert_solutions lesseq('h', 'b'), []
-
1
assert_solutions lesseq(1.01, 1.003), []
-
end
-
-
1
it 'should return a solution when given equal values' do
-
1
Porolog::builtin :lesseq
-
-
1
assert_solutions lesseq(4, 4), [{}]
-
1
assert_solutions lesseq('b', 'b'), [{}]
-
1
assert_solutions lesseq(1.003, 1.003), [{}]
-
end
-
-
1
it 'should return a solution when given lesser values' do
-
1
Porolog::builtin :lesseq
-
-
1
assert_solutions lesseq(2, 4), [{}]
-
1
assert_solutions lesseq('b', 'h'), [{}]
-
1
assert_solutions lesseq(1.003, 1.01), [{}]
-
end
-
-
1
it 'should return a solution with an unbound variable when given the same unbound variable' do
-
1
Porolog::builtin :lesseq
-
-
1
assert_solutions lesseq(:X, :X), [{ X: nil }]
-
end
-
-
1
it 'should return no solutions when given two unbound variables' do
-
1
Porolog::builtin :lesseq
-
-
1
assert_solutions lesseq(:X, :Y), []
-
end
-
-
1
it 'should return no solutions when given one unbound variable' do
-
1
Porolog::builtin :lesseq
-
-
1
assert_solutions lesseq(:X, 11), []
-
end
-
-
end
-
-
1
describe '#gtreq' do
-
-
1
it 'should return no solutions when given a lesser value' do
-
1
Porolog::builtin :gtreq
-
-
1
assert_solutions gtreq(4, 7), []
-
1
assert_solutions gtreq('b', 'h'), []
-
1
assert_solutions gtreq(1.003, 1.01), []
-
end
-
-
1
it 'should return a solution when given equal values' do
-
1
Porolog::builtin :gtreq
-
-
1
assert_solutions gtreq(4, 4), [{}]
-
1
assert_solutions gtreq('b', 'b'), [{}]
-
1
assert_solutions gtreq(1.003, 1.003), [{}]
-
end
-
-
1
it 'should return a solution when given gtreqer values' do
-
1
Porolog::builtin :gtreq
-
-
1
assert_solutions gtreq(4, 2), [{}]
-
1
assert_solutions gtreq('h', 'b'), [{}]
-
1
assert_solutions gtreq(1.01, 1.003), [{}]
-
end
-
-
1
it 'should return a solution with an unbound variable when given the same unbound variable' do
-
1
Porolog::builtin :gtreq
-
-
1
assert_solutions gtreq(:X, :X), [{ X: nil }]
-
end
-
-
1
it 'should return no solutions when given two unbound variables' do
-
1
Porolog::builtin :gtreq
-
-
1
assert_solutions gtreq(:X, :Y), []
-
end
-
-
1
it 'should return no solutions when given one unbound variable' do
-
1
Porolog::builtin :gtreq
-
-
1
assert_solutions gtreq(:X, 11), []
-
end
-
-
end
-
-
1
describe '#length' do
-
-
1
it 'should return a solution for an array and an integer' do
-
1
Porolog::builtin :length
-
-
1
assert_solutions length([1,2,3,4,5], 5), [{}]
-
end
-
-
1
it 'should return no solutions for an array and an integer that do not satisy the length' do
-
1
Porolog::builtin :length
-
-
1
assert_solutions length([1,2,3,4,5], 2), []
-
end
-
-
1
it 'should instaniate the length of the list' do
-
1
Porolog::builtin :length
-
-
1
assert_solutions length([1,2,3,4,5], :N), [{ N: 5 }]
-
end
-
-
1
it 'should instaniate the list' do
-
1
Porolog::builtin :length
-
-
1
assert_solutions length(:L, 4), [
-
{ L: [nil, nil, nil, nil], _a: nil, _b: nil, _c: nil, _d: nil }
-
]
-
end
-
-
1
it 'should return no solutions for a variable array and length' do
-
1
Porolog::builtin :length
-
-
1
assert_solutions length(:L, :N), []
-
end
-
-
1
it 'should return no solutions for invalid types' do
-
1
Porolog::builtin :length
-
-
1
assert_solutions length(3, [1,2,3]), []
-
end
-
-
end
-
-
1
describe '#between' do
-
-
1
it 'should return a solution when the value is in range' do
-
1
Porolog::builtin :between
-
-
1
assert_solutions between(3, 1, 10), [{}]
-
end
-
-
1
it 'should return no solutions when the value is not in range' do
-
1
Porolog::builtin :between
-
-
1
assert_solutions between(-40, 1, 10), []
-
end
-
-
1
it 'should return no solutions when given an uninstantiated variable for the range' do
-
1
Porolog::builtin :between
-
-
1
assert_solutions between(:X, 1, :Ten), []
-
1
assert_solutions between(:X, :One, 10), []
-
end
-
-
1
it 'should return solutions for instantiating' do
-
1
Porolog::builtin :between
-
-
1
assert_solutions between(:X, 1, 10), [
-
{ X: 1 },
-
{ X: 2 },
-
{ X: 3 },
-
{ X: 4 },
-
{ X: 5 },
-
{ X: 6 },
-
{ X: 7 },
-
{ X: 8 },
-
{ X: 9 },
-
{ X: 10 },
-
]
-
end
-
-
1
it 'should instantiate the minimum possible when the value is above the lower bound' do
-
1
Porolog::builtin :between
-
-
1
assert_solutions between(4, 1, :upper), [{ upper: 4}]
-
end
-
-
1
it 'should instantiate the maximum possible when the value is below the upper bound' do
-
1
Porolog::builtin :between
-
-
1
assert_solutions between(4, :lower, 10), [{ lower: 4}]
-
end
-
-
1
it 'should return no solutions the value is below the lower bound' do
-
1
Porolog::builtin :between
-
-
1
assert_solutions between(-7, 1, :upper), []
-
end
-
-
1
it 'should return no solutions the value is above the upper bound' do
-
1
Porolog::builtin :between
-
-
1
assert_solutions between(24, :lower, 10), []
-
end
-
-
1
it 'should instantiate both bounds when given a value' do
-
1
Porolog::builtin :between
-
-
1
assert_solutions between(24, :lower, :upper), [{ lower: 24, upper: 24 }]
-
end
-
-
end
-
-
1
describe '#member' do
-
-
1
it 'should return no solutions for a non-member' do
-
1
Porolog::builtin :member
-
-
1
assert_solutions member(5, [1,2,3,4]), []
-
end
-
-
1
it 'should return no solutions for a non-list' do
-
1
Porolog::builtin :member
-
-
1
assert_solutions member(5, 5), []
-
end
-
-
1
it 'should return a solution for a member' do
-
1
Porolog::builtin :member
-
-
1
assert_solutions member(3, [1,2,3,4]), [{}]
-
end
-
-
1
it 'should return a solution for an array member' do
-
1
Porolog::builtin :member
-
-
1
assert_solutions member([2,3], [[1,2],[2,3],[3,4]]), [{}]
-
end
-
-
1
it 'should return a solution for a member' do
-
1
Porolog::builtin :member
-
-
1
assert_solutions member(3, [1,2,3,4]), [{}]
-
end
-
-
1
it 'should return a solution for a member' do
-
1
Porolog::builtin :member
-
-
1
assert_solutions member(3, [1,2,:C,4]), [{ C: 3 }]
-
end
-
-
1
it 'should return a solution for an array member' do
-
1
Porolog::builtin :member
-
-
1
assert_solutions member([:A,3], [[1,2],[2,3],[3,4]]), [{ A: 2 }]
-
end
-
-
1
it 'should return a solution for an array member' do
-
1
Porolog::builtin :member
-
-
1
assert_solutions member([2,3], [[1,:B],[:B,3],[3,4]]), [{ B: 2 }]
-
end
-
-
1
it 'should return solutions for each member' do
-
1
Porolog::builtin :member
-
-
1
assert_solutions member(:M, [[1,2],[2,3],[3,4]]), [
-
{ M: [1,2] },
-
{ M: [2,3] },
-
{ M: [3,4] },
-
]
-
end
-
-
1
it 'should return limited solutions for variables' do
-
1
Porolog::builtin :member
-
-
1
assert_solutions member(:M, :L, 12), Array.new(12){|i|
-
12
v = '_a'
-
298
{ M: nil, L: [*([nil] * (i+1)), Porolog::UNKNOWN_TAIL] }.merge((1..(i * (i + 1) / 2)).map{ m = v.dup ; v.succ! ; [m.to_sym, nil] }.to_h)
-
}
-
end
-
-
end
-
-
1
describe '#append' do
-
-
1
it 'should return no solutions for a non-append' do
-
1
Porolog::builtin :append
-
-
1
assert_solutions append([1,2,3], [4,5,6], [1,2,7,4,5,6]), []
-
end
-
-
1
it 'should return a solution for a valid append' do
-
1
Porolog::builtin :append
-
-
1
assert_solutions append([1,2,3], [4,5,6], [1,2,3,4,5,6]), [{}]
-
end
-
-
1
it 'should return no solutions for a non-list' do
-
1
Porolog::builtin :append
-
-
1
assert_solutions append(5, 5, 55), []
-
end
-
-
1
it 'should return a solution for a valid append' do
-
1
Porolog::builtin :append
-
-
1
assert_solutions append([1,2,3], [4,5,6], [1,2,3,4,5,6]), [{}]
-
end
-
-
1
it 'should return a solution for an array append' do
-
1
Porolog::builtin :append
-
-
1
assert_solutions append([[1,2],[2,3],[3,4]], [[4,5],[5,6],[6,7]], [[1,2],[2,3],[3,4],[4,5],[5,6],[6,7]]), [{}]
-
end
-
-
1
it 'should instantiate prefix variable' do
-
1
Porolog::builtin :append
-
-
1
assert_solutions append(:prefix, [[4,5],[5,6],[6,7]], [[1,2],[2,3],[3,4],[4,5],[5,6],[6,7]]), [
-
{ prefix: [[1, 2], [2, 3], [3, 4]] }
-
]
-
end
-
-
1
it 'should instantiate suffix variable' do
-
1
Porolog::builtin :append
-
-
1
assert_solutions append([[1,2],[2,3],[3,4]], :suffix, [[1,2],[2,3],[3,4],[4,5],[5,6],[6,7]]), [
-
{ suffix: [[4, 5], [5, 6], [6, 7]] }
-
]
-
end
-
-
1
it 'should instantiate embedded variables' do
-
1
Porolog::builtin :append
-
-
1
assert_solutions append([[:A,:B],[:C,:D],[:E,:F]], [:G,:H,:I], [[1,2],[2,3],[3,4],[4,5],[5,6],[6,7]]), [
-
{ A: 1, B: 2, C: 2, D: 3, E: 3, F: 4, G: [4, 5], H: [5, 6], I: [6, 7] }
-
]
-
end
-
-
1
it 'should instantiate embedded variables' do
-
1
Porolog::builtin :append
-
-
1
assert_solutions append([1,2,3,4], ['apple','banana','carrot'], :L), [
-
{ L: [1, 2, 3, 4, 'apple', 'banana', 'carrot'] }
-
]
-
end
-
-
1
it 'should return solutions for all possible splits of a list' do
-
1
Porolog::builtin :append
-
-
1
assert_solutions append(:M, :L, [1,2,3,4,5]), [
-
{ M: [], L: [1, 2, 3, 4, 5] },
-
{ M: [1], L: [2, 3, 4, 5] },
-
{ M: [1, 2], L: [3, 4, 5] },
-
{ M: [1, 2, 3], L: [4, 5] },
-
{ M: [1, 2, 3, 4], L: [5] },
-
{ M: [1, 2, 3, 4, 5], L: [] }
-
]
-
end
-
-
1
it 'should instantiate using a head and tail for an unbound variable and an array' do
-
1
Porolog::builtin :append, :is_eq
-
1
Porolog::predicate :append_is_eq
-
-
1
append_is_eq(:A, :B, :C) << [
-
append(:A, :B, :C),
-
is_eq(:A, [1, 2, 3]),
-
]
-
-
1
assert_solutions append_is_eq(:A, [4,5,6], :C), [
-
{ A: [1, 2, 3], C: [1, 2, 3, 4, 5, 6] }
-
]
-
end
-
-
1
it 'should instantiate using a head and tail for unbound variables' do
-
1
Porolog::builtin :append, :is_eq
-
1
Porolog::predicate :append_is_eq
-
-
1
append_is_eq(:A, :B, :C) << [
-
append(:A, :B, :C),
-
is_eq(:B, [4, 5, 6]),
-
]
-
-
1
assert_solutions append_is_eq([1,2,3], :B, :C), [
-
{ B: [4, 5, 6], C: [1, 2, 3, 4, 5, 6] }
-
]
-
end
-
-
1
it 'should instantiate using a head and tail for unbound variables' do
-
1
Porolog::builtin :append, :is_eq
-
1
Porolog::predicate :append_is_eq
-
-
1
append_is_eq(:A, :B, :C) << [
-
append(:A, :B, :C),
-
is_eq(:A, [1, 2, 3]),
-
is_eq(:B, [4, 5, 6]),
-
]
-
-
1
assert_solutions append_is_eq(:A, :B, :C), [
-
{ A: [1, 2, 3], B: [4, 5, 6], C: [1, 2, 3, 4, 5, 6] }
-
]
-
end
-
-
end
-
-
1
describe '#permutation' do
-
-
1
it 'returns no solutions for non-arrays' do
-
1
Porolog::builtin :permutation
-
-
1
assert_solutions permutation('1234', '1234'), []
-
end
-
-
1
it 'returns a solution for identical arrays without variables' do
-
1
Porolog::builtin :permutation
-
-
1
assert_solutions permutation([1,2,3,4], [1,2,3,4]), [{}]
-
end
-
-
1
it 'returns a solutions for rearranged arrays without variables' do
-
1
Porolog::builtin :permutation
-
-
1
assert_solutions permutation([3,2,1,4], [1,2,3,4]), [{}]
-
end
-
-
1
it 'returns a solution and instantiates a variable' do
-
1
Porolog::builtin :permutation
-
-
1
assert_solutions permutation([3,2,:A,4], [1,2,3,4]), [{ A: 1 }]
-
end
-
-
1
it 'returns solutions of permutations of instantiations' do
-
1
Porolog::builtin :permutation
-
-
1
assert_solutions permutation([3,2,:A,:B], [1,2,3,4]), [
-
{ A: 1, B: 4 },
-
{ A: 4, B: 1 },
-
]
-
end
-
-
1
it 'returns solutions of permutations of instantiations in the other list' do
-
1
Porolog::builtin :permutation
-
-
1
assert_solutions permutation([3,2,1,4], [:A,2,3,:B]), [
-
{ A: 1, B: 4 },
-
{ A: 4, B: 1 },
-
]
-
end
-
-
1
it 'infers instantiations of variables in both arrays' do
-
1
Porolog::builtin :permutation
-
-
1
assert_solutions permutation([3,2,:A,4], [1,2,:C,4]), [
-
{ A: 1, C: 3 }
-
]
-
end
-
-
1
it 'infers instantiations of variables in both arrays when they represent the same value' do
-
1
Porolog::builtin :permutation
-
-
1
assert_solutions permutation([:A,2,1,4], [1,2,:C,4]), [
-
{ A: 1, C: 1 },
-
{ A: 2, C: 2 },
-
{ A: nil, C: nil },
-
{ A: 4, C: 4 }
-
]
-
end
-
-
1
it 'returns all permutations of an array' do
-
1
Porolog::builtin :permutation
-
-
1
assert_solutions permutation([3,2,1,4], :L), [
-
{ L: [3, 2, 1, 4] },
-
{ L: [3, 2, 4, 1] },
-
{ L: [3, 1, 2, 4] },
-
{ L: [3, 1, 4, 2] },
-
{ L: [3, 4, 2, 1] },
-
{ L: [3, 4, 1, 2] },
-
{ L: [2, 3, 1, 4] },
-
{ L: [2, 3, 4, 1] },
-
{ L: [2, 1, 3, 4] },
-
{ L: [2, 1, 4, 3] },
-
{ L: [2, 4, 3, 1] },
-
{ L: [2, 4, 1, 3] },
-
{ L: [1, 3, 2, 4] },
-
{ L: [1, 3, 4, 2] },
-
{ L: [1, 2, 3, 4] },
-
{ L: [1, 2, 4, 3] },
-
{ L: [1, 4, 3, 2] },
-
{ L: [1, 4, 2, 3] },
-
{ L: [4, 3, 2, 1] },
-
{ L: [4, 3, 1, 2] },
-
{ L: [4, 2, 3, 1] },
-
{ L: [4, 2, 1, 3] },
-
{ L: [4, 1, 3, 2] },
-
{ L: [4, 1, 2, 3] }
-
], goals: 1
-
end
-
-
1
it 'returns all permutations of an array when arguments are swapped' do
-
1
Porolog::builtin :permutation
-
-
1
assert_solutions permutation(:L, [3,2,1,4]), [
-
{ L: [3, 2, 1, 4] },
-
{ L: [3, 2, 4, 1] },
-
{ L: [3, 1, 2, 4] },
-
{ L: [3, 1, 4, 2] },
-
{ L: [3, 4, 2, 1] },
-
{ L: [3, 4, 1, 2] },
-
{ L: [2, 3, 1, 4] },
-
{ L: [2, 3, 4, 1] },
-
{ L: [2, 1, 3, 4] },
-
{ L: [2, 1, 4, 3] },
-
{ L: [2, 4, 3, 1] },
-
{ L: [2, 4, 1, 3] },
-
{ L: [1, 3, 2, 4] },
-
{ L: [1, 3, 4, 2] },
-
{ L: [1, 2, 3, 4] },
-
{ L: [1, 2, 4, 3] },
-
{ L: [1, 4, 3, 2] },
-
{ L: [1, 4, 2, 3] },
-
{ L: [4, 3, 2, 1] },
-
{ L: [4, 3, 1, 2] },
-
{ L: [4, 2, 3, 1] },
-
{ L: [4, 2, 1, 3] },
-
{ L: [4, 1, 3, 2] },
-
{ L: [4, 1, 2, 3] }
-
], goals: 1
-
end
-
-
end
-
-
1
describe '#reverse' do
-
-
1
it 'returns no solutions for non-arrays' do
-
1
Porolog::builtin :reverse
-
-
1
assert_solutions reverse('1234', '1234'), []
-
end
-
-
1
it 'returns a solution for mirrored arrays without variables' do
-
1
Porolog::builtin :reverse
-
-
1
assert_solutions reverse([1,2,3,4], [4,3,2,1]), [{}]
-
end
-
-
1
it 'returns a solution and instantiates a variable' do
-
1
Porolog::builtin :reverse
-
-
1
assert_solutions reverse([4,3,:A,1], [1,2,3,4]), [{ A: 2 }]
-
end
-
-
1
it 'returns a solution and instantiates multiple variables' do
-
1
Porolog::builtin :reverse
-
-
1
assert_solutions reverse([4,3,:A,:B], [1,2,3,4]), [
-
{ A: 2, B: 1 },
-
]
-
end
-
-
1
it 'returns a solution and instantiates multiple variables in the other list' do
-
1
Porolog::builtin :reverse
-
-
1
assert_solutions reverse([4,3,2,1], [:A,2,3,:B]), [
-
{ A: 1, B: 4 },
-
]
-
end
-
-
1
it 'infers instantiations of variables in both arrays' do
-
1
Porolog::builtin :reverse
-
-
1
assert_solutions reverse([4,3,:A,1], [1,2,:C,4]), [
-
{ A: 2, C: 3 }
-
]
-
end
-
-
1
it 'infers instantiations of variables in both arrays when they represent the same value' do
-
1
Porolog::builtin :reverse
-
-
1
assert_solutions reverse([:A,3,2,1], [1,:A,3,:C]), [
-
{ A: 2, C: 2 },
-
]
-
end
-
-
1
it 'allows unbound variables' do
-
1
Porolog::builtin :reverse
-
-
1
assert_solutions reverse([:A,3,2,1], :L), [
-
{ A: nil, L: [1,2,3,nil] },
-
]
-
end
-
-
1
it 'instantiates a variable to the reversed array' do
-
1
Porolog::builtin :reverse
-
-
1
assert_solutions reverse([4,3,2,1], :L), [
-
{ L: [1, 2, 3, 4] },
-
], goals: 1
-
end
-
-
1
it 'instantiates a variable to the reversed array when arguments are swapped' do
-
1
Porolog::builtin :reverse
-
-
1
assert_solutions reverse(:L, [4,3,2,1]), [
-
{ L: [1, 2, 3, 4] },
-
], goals: 1
-
end
-
-
1
it 'returns no solutions for uninstantiated variables' do
-
1
Porolog::builtin :reverse
-
-
1
assert_solutions reverse(:X, :Y), [], goals: 1
-
end
-
-
end
-
-
1
describe '#var' do
-
-
1
it 'should be unsuccessful for an atom' do
-
1
Porolog::builtin :var
-
-
1
assert_solutions var('1234'), []
-
end
-
-
1
it 'should be successful for an uninstantiated variable' do
-
1
Porolog::builtin :var, :is_eq
-
1
Porolog::predicate :vartest
-
-
1
vartest(:variable) << [
-
var(:variable),
-
is_eq(:variable, 'value')
-
]
-
-
1
assert_solutions vartest(:V), [{ V: 'value' }]
-
end
-
-
1
it 'should be successful for an uninstantiated bound variable' do
-
1
Porolog::builtin :var, :is_eq
-
1
Porolog::predicate :vartest
-
-
1
vartest(:variable) << [
-
is_eq(:variable, :bound),
-
var(:variable),
-
is_eq(:bound, 'bound')
-
]
-
-
1
assert_solutions vartest(:V), [{ V: 'bound' }]
-
end
-
-
1
it 'should be successful for an uninstantiated bound variable' do
-
1
Porolog::builtin :var, :is_eq
-
1
Porolog::predicate :vartest
-
-
1
vartest(:variable) << [
-
is_eq(:variable, 'instantiated'),
-
var(:variable)
-
]
-
-
1
assert_solutions vartest(:V), []
-
end
-
-
end
-
-
1
describe '#nonvar' do
-
-
1
it 'should be successful for an atom' do
-
1
Porolog::builtin :nonvar
-
-
1
assert_solutions nonvar('1234'), [{}]
-
end
-
-
1
it 'should be unsuccessful for an uninstantiated variable' do
-
1
Porolog::builtin :nonvar, :is_eq
-
1
Porolog::predicate :nonvartest
-
-
1
nonvartest(:variable) << [
-
nonvar(:variable),
-
is_eq(:variable, 'value')
-
]
-
-
1
assert_solutions nonvartest(:V), []
-
end
-
-
1
it 'should be unsuccessful for an uninstantiated bound variable' do
-
1
Porolog::builtin :nonvar, :is_eq
-
1
Porolog::predicate :nonvartest
-
-
1
nonvartest(:variable) << [
-
is_eq(:variable, :bound),
-
nonvar(:variable),
-
is_eq(:bound, 'bound')
-
]
-
-
1
assert_solutions nonvartest(:V), []
-
end
-
-
1
it 'should be unsuccessful for an uninstantiated bound variable' do
-
1
Porolog::builtin :nonvar, :is_eq
-
1
Porolog::predicate :nonvartest
-
-
1
nonvartest(:variable) << [
-
is_eq(:variable, 'instantiated'),
-
nonvar(:variable)
-
]
-
-
1
assert_solutions nonvartest(:V), [{ V: 'instantiated' }]
-
end
-
-
end
-
-
1
describe '#atom' do
-
-
1
it 'should be satisfied with a String' do
-
1
Porolog::builtin :atom
-
-
1
assert_solutions atom('banana'), [{}]
-
end
-
-
1
it 'should not be satisfied with an Integer' do
-
1
Porolog::builtin :atom
-
-
1
assert_solutions atom(6), []
-
end
-
-
1
it 'should not be satisfied with an Array' do
-
1
Porolog::builtin :atom
-
-
1
assert_solutions atom([]), []
-
end
-
-
end
-
-
1
describe '#atomic' do
-
-
1
it 'should be satisfied with a String' do
-
1
Porolog::builtin :atomic
-
-
1
assert_solutions atomic('banana'), [{}]
-
end
-
-
1
it 'should be satisfied with an Integer' do
-
1
Porolog::builtin :atomic
-
-
1
assert_solutions atomic(6), [{}]
-
end
-
-
1
it 'should not be satisfied with an Array' do
-
1
Porolog::builtin :atomic
-
-
1
assert_solutions atomic([]), []
-
end
-
-
1
it 'should not be satisfied with an uninstantiated variable' do
-
1
Porolog::builtin :atomic
-
-
1
assert_solutions atomic(:X), []
-
end
-
-
end
-
-
1
describe '#integer' do
-
-
1
it 'should not be satisfied with a String' do
-
1
Porolog::builtin :integer
-
-
1
assert_solutions integer('banana'), []
-
end
-
-
1
it 'should be satisfied with an Integer' do
-
1
Porolog::builtin :integer
-
-
1
assert_solutions integer(6), [{}]
-
end
-
-
1
it 'should not be satisfied with an Array' do
-
1
Porolog::builtin :integer
-
-
1
assert_solutions integer([]), []
-
end
-
-
1
it 'should not be satisfied with an uninstantiated variable' do
-
1
Porolog::builtin :integer
-
-
1
assert_solutions integer(:X), []
-
end
-
-
end
-
-
end
-
-
end
-
-
end
-
#
-
# test/porolog/predicate_test.rb - Test Suite for Porolog::Predicate
-
#
-
# Luis Esteban 2 May 2018
-
# created
-
#
-
-
1
require_relative '../test_helper'
-
-
1
describe 'Porolog' do
-
-
1
before(:all) do
-
24
reset
-
end
-
-
1
describe 'Predicate' do
-
-
1
it 'should default to creating predicates in the default scope' do
-
1
assert_equal :default, Porolog::Predicate.scope.name
-
-
1
test = Porolog::Predicate.new 'test'
-
-
1
assert_includes Porolog::Scope[:default].predicates, test
-
end
-
-
1
describe '.scope' do
-
-
1
it 'should return the current scope for new predicates' do
-
1
assert_equal Porolog::Scope[:default], Porolog::Predicate.scope
-
-
1
alpha = Porolog::Predicate.new 'alpha', :new_scope
-
-
1
assert_equal Porolog::Scope[:default], Porolog::Predicate.scope
-
-
1
Porolog::Predicate.scope :new_scope
-
-
1
assert_equal Porolog::Scope[:new_scope], Porolog::Predicate.scope
-
-
1
bravo = Porolog::Predicate.new 'bravo'
-
-
1
assert_equal [], Porolog::Scope[:default ].predicates
-
1
assert_equal [alpha,bravo], Porolog::Scope[:new_scope].predicates
-
end
-
-
1
it 'should allow predicate scope to be created and set' do
-
1
Porolog::Predicate.scope :internal
-
1
assert_equal :internal, Porolog::Predicate.scope.name
-
-
1
Porolog::Predicate.new 'alpha'
-
-
1
Porolog::Predicate.scope :external
-
1
assert_equal :external, Porolog::Predicate.scope.name
-
-
1
Porolog::Predicate.new 'bravo'
-
-
1
Porolog::Predicate.scope :internal
-
1
assert_equal :internal, Porolog::Predicate.scope.name
-
-
1
Porolog::Predicate.new 'carly'
-
1
Porolog::Predicate.new 'delta', :external
-
-
1
assert_equal :internal, Porolog::Predicate.scope.name
-
-
1
assert_equal [:alpha,:carly], Porolog::Scope[:internal].predicates.map(&:name)
-
1
assert_equal [:bravo,:delta], Porolog::Scope[:external].predicates.map(&:name)
-
end
-
-
end
-
-
1
describe '.scope=' do
-
-
1
it 'should also allow predicate scope to be assigned' do
-
1
Porolog::Predicate.scope = :internal
-
1
assert_equal :internal, Porolog::Predicate.scope.name
-
-
1
alpha = Porolog::Predicate.new 'alpha'
-
-
1
Porolog::Predicate.scope = :external
-
1
assert_equal :external, Porolog::Predicate.scope.name
-
-
1
bravo = Porolog::Predicate.new 'bravo'
-
-
1
Porolog::Predicate.scope = :internal
-
1
assert_equal :internal, Porolog::Predicate.scope.name
-
-
1
carly = Porolog::Predicate.new 'carly'
-
1
delta = Porolog::Predicate.new 'delta', :external
-
-
1
assert_equal :internal, Porolog::Predicate.scope.name
-
-
1
assert_equal [alpha,carly], Porolog::Scope[:internal].predicates
-
1
assert_equal [bravo,delta], Porolog::Scope[:external].predicates
-
end
-
-
end
-
-
1
describe '.reset' do
-
-
1
it 'should revert to the default scope when reset' do
-
1
Porolog::Predicate.scope = :other
-
1
assert_equal :other, Porolog::Predicate.scope.name
-
-
1
Porolog::Predicate.reset
-
1
assert_equal :default, Porolog::Predicate.scope.name
-
-
1
Porolog::Predicate.scope :temporary
-
-
1
alpha = Porolog::Predicate.new :alpha
-
-
1
assert_equal :temporary, Porolog::Predicate.scope.name
-
1
assert_equal [alpha], Porolog::Predicate.scope.predicates
-
-
1
Porolog::Predicate.reset
-
-
1
bravo = Porolog::Predicate.new :bravo
-
-
1
assert_equal :default, Porolog::Predicate.scope.name
-
1
assert_equal [bravo], Porolog::Predicate.scope.predicates
-
end
-
-
end
-
-
1
describe '.[]' do
-
-
1
it 'should return a predicate by name' do
-
1
alpha = Porolog::Predicate.new :alpha
-
-
1
assert_equal alpha, Porolog::Predicate[:alpha]
-
end
-
-
end
-
-
1
describe '.new' do
-
-
1
it 'should create a new predicate' do
-
1
alpha = Porolog::Predicate.new :alpha
-
-
1
assert_instance_of Porolog::Predicate, alpha
-
end
-
-
1
it 'should not create a predicate if a predicate with the same name already exists in the scope but instead return the existing one' do
-
1
predicate1 = Porolog::Predicate.new 'predicate1', :left
-
1
predicate2 = Porolog::Predicate.new 'predicate1', :right
-
-
1
refute_equal predicate1, predicate2
-
-
1
Porolog::Predicate.scope :left
-
1
predicate3 = Porolog::Predicate.new :predicate1
-
-
1
assert_equal predicate1, predicate3
-
end
-
-
end
-
-
1
describe '#initialize' do
-
-
1
it 'can create predicates in different scopes' do
-
1
left = Porolog::Scope.new :left
-
#right = Scope.new :right # Not explicitly creating scope :right
-
-
1
assert_equal left, Porolog::Predicate.scope(:left)
-
1
assert_equal :left, Porolog::Predicate.scope.name
-
-
1
left_test = Porolog::Predicate.new 'left_test'
-
-
1
assert_equal :left, Porolog::Predicate.scope.name
-
1
refute_empty left.predicates
-
1
assert_includes left.predicates, left_test
-
-
1
right_test = Porolog::Predicate.new 'right_test', :right
-
-
1
assert_includes Porolog::Scope[:right].predicates, right_test
-
1
assert_equal :left, Porolog::Predicate.scope.name
-
-
1
assert_equal [left_test], Porolog::Scope[:left ].predicates
-
1
assert_equal [right_test], Porolog::Scope[:right].predicates
-
end
-
-
1
it 'should not create a Predicate named "predicate"' do
-
1
assert_raises Porolog::Predicate::NameError do
-
1
Porolog::predicate :predicate
-
end
-
1
assert_raises Porolog::Predicate::NameError do
-
1
Porolog::predicate 'predicate'
-
end
-
1
assert_raises Porolog::Predicate::NameError do
-
1
Porolog::Predicate.new :predicate
-
end
-
1
assert_raises Porolog::Predicate::NameError do
-
1
Porolog::Predicate.new 'predicate'
-
end
-
end
-
-
end
-
-
1
describe '#call' do
-
-
1
it 'should create an Arguments when "called"' do
-
1
p = Porolog::Predicate.new :p
-
-
1
arguments = p.(1,2,3)
-
-
1
assert_instance_of Porolog::Arguments, arguments
-
1
assert_equal [1,2,3], arguments.arguments
-
1
assert_equal 'p(1,2,3)', arguments.inspect
-
end
-
-
end
-
-
1
describe '#arguments' do
-
-
1
it 'should provide a convenience method to create an Arguments' do
-
1
p = Porolog::Predicate.new :p
-
-
1
arguments = p.arguments(1,2,3)
-
-
1
assert_instance_of Porolog::Arguments, arguments
-
1
assert_equal [1,2,3], arguments.arguments
-
1
assert_equal 'p(1,2,3)', arguments.inspect
-
end
-
-
end
-
-
1
describe '#name' do
-
-
1
it 'should create predicates with a name attribute, which is converted to a Symbol' do
-
1
test_predicate = Porolog::Predicate.new('test_predicate_name')
-
-
1
assert_respond_to test_predicate, :name
-
1
assert_equal :test_predicate_name, test_predicate.name
-
end
-
-
end
-
-
1
describe '#rules' do
-
-
1
it 'should create predicates with an empty set of rules' do
-
1
test_predicate = Porolog::Predicate.new('test_predicate_name')
-
-
1
assert_respond_to test_predicate, :rules
-
1
assert_equal [], test_predicate.rules
-
end
-
-
1
it 'should add new facts to a predicate' do
-
1
alpha = Porolog::Predicate.new 'alpha'
-
-
1
alpha.('p','q').fact!
-
-
1
assert_equal 1, alpha.rules.size
-
1
assert_equal true, alpha.rules.first.definition
-
1
assert_equal ' alpha("p","q"):- true', alpha.rules.first.inspect
-
1
assert_equal 'alpha:- alpha("p","q"):- true', alpha.inspect
-
-
1
assert_Predicate alpha, :alpha, [alpha.rules.first]
-
end
-
-
1
it 'should add new fallacies to a predicate' do
-
1
alpha = Porolog::Predicate.new 'alpha'
-
-
1
alpha.('p','q').fallacy!
-
-
1
assert_equal 1, alpha.rules.size
-
1
assert_equal false, alpha.rules.first.definition
-
1
assert_equal ' alpha("p","q"):- false', alpha.rules.first.inspect
-
1
assert_equal 'alpha:- alpha("p","q"):- false', alpha.inspect
-
-
1
assert_Predicate alpha, :alpha, [alpha.rules.first]
-
end
-
-
end
-
-
1
describe '#inspect' do
-
-
1
it 'should return a summary of the predicate' do
-
1
alpha = Porolog::Predicate.new 'alpha'
-
-
1
assert_equal 'alpha:-', alpha.inspect
-
end
-
-
1
it 'should return a summary of the predicate with rules' do
-
1
alpha = Porolog::Predicate.new 'alpha'
-
-
1
alpha.(:x,:y) << [
-
alpha.(:x,:y),
-
alpha.(:y,:x),
-
:CUT
-
]
-
-
1
assert_equal 'alpha:- alpha(:x,:y):- [alpha(:x,:y), alpha(:y,:x), :CUT]', alpha.inspect
-
end
-
-
end
-
-
1
describe '#<<' do
-
-
1
it 'should add new rules to a predicate' do
-
1
alpha = Porolog::Predicate.new 'alpha'
-
-
1
alpha.(:P,:Q) << [
-
alpha.(:P,:Q),
-
alpha.(:Q,:P),
-
]
-
1
alpha.(:P,:Q) << [
-
alpha.(:P,:P),
-
alpha.(:Q,:Q),
-
]
-
-
1
assert_equal 2, alpha.rules.size
-
1
assert_instance_of Array, alpha.rules.first.definition
-
1
assert_equal 2, alpha.rules.first.definition.size
-
-
1
assert_Arguments alpha.rules.first.definition[0], :alpha, [:P,:Q]
-
1
assert_Arguments alpha.rules.first.definition[1], :alpha, [:Q,:P]
-
-
1
assert_equal ' alpha(:P,:Q):- [alpha(:P,:Q), alpha(:Q,:P)]', alpha.rules.first.inspect
-
1
assert_equal [
-
'alpha:-',
-
' alpha(:P,:Q):- [alpha(:P,:Q), alpha(:Q,:P)]',
-
' alpha(:P,:Q):- [alpha(:P,:P), alpha(:Q,:Q)]',
-
].join("\n"), alpha.inspect
-
-
1
assert_Predicate alpha, :alpha, [alpha.rules[0],alpha.rules[1]]
-
end
-
-
end
-
-
1
describe '#builtin?' do
-
-
1
it 'should return false for normal predicates' do
-
1
p = Porolog::predicate :normal
-
-
1
assert_equal false, p.builtin?
-
end
-
-
1
it 'should return true for builtin predicates' do
-
1
p = Porolog::builtin :append
-
-
1
assert_equal true, p.builtin?
-
end
-
-
end
-
-
1
describe '#satisfy_builtin' do
-
-
1
module Porolog
-
1
class Predicate
-
1
module Builtin
-
1
def user_defined(goal, block, *args, &arg_block)
-
1
puts 'Called user_defined'
-
1
block.call(goal, *args) || false
-
end
-
end
-
end
-
end
-
-
2
let(:predicate1) { Porolog::Predicate.new :user_defined, builtin: true }
-
2
let(:arguments1) { predicate1.arguments(:m,:n) }
-
2
let(:goal) { arguments1.goal }
-
-
1
it 'should satisfy builtin predicate' do
-
1
called = false
-
-
1
assert_output "Called user_defined\n" do
-
1
predicate1.satisfy_builtin(goal) {|*args|
-
1
called = args
-
}
-
end
-
-
1
assert_equal [goal, goal[:m], goal[:n]], called
-
end
-
-
end
-
-
1
describe '.call_builtin' do
-
-
1
it 'should raise an exception when no such builtin predicate exists' do
-
1
assert_raises NameError do
-
1
Predicate.call_builtin(:non_existent)
-
end
-
-
end
-
-
1
it 'should call a builtin predicate' do
-
1
module Porolog
-
1
class Predicate
-
1
module Builtin
-
1
def custom(goal, block, *args, &arg_block)
-
1
block.call(goal, *args) || false
-
end
-
end
-
end
-
end
-
1
called = false
-
-
1
goal = new_goal :a, :b, :c
-
1
block = ->(goal, *args){
-
1
called = [goal] + args
-
}
-
1
Porolog::Predicate.call_builtin(:custom, goal, block, goal[:X], [1,2,3]) { 1 }
-
1
assert_equal [goal, goal[:X], [1,2,3]], called
-
end
-
-
end
-
-
end
-
-
end
-
#
-
# test/porolog/rule_test.rb - Test Suite for Porolog::Rule
-
#
-
# Luis Esteban 2 May 2018
-
# created
-
#
-
-
-
1
require_relative '../test_helper'
-
-
1
describe 'Porolog' do
-
-
1
before(:all) do
-
20
reset
-
end
-
-
1
describe 'Rule' do
-
-
1
describe '.reset' do
-
-
1
it 'should delete/unregister all rules' do
-
1
pred = Porolog::Predicate.new :pred
-
1
args = Porolog::Arguments.new pred, [1,2,3]
-
-
1
rule1 = Porolog::Rule.new args, [1,2]
-
1
rule2 = Porolog::Rule.new args, [3,4,5]
-
1
rule3 = Porolog::Rule.new args, [6]
-
-
1
assert_equal 'Rule1', rule1.myid
-
1
assert_equal 'Rule2', rule2.myid
-
1
assert_equal 'Rule3', rule3.myid
-
-
1
Porolog::Rule.reset
-
-
1
rule4 = Porolog::Rule.new args, [7,8,9,0]
-
1
rule5 = Porolog::Rule.new args, [:CUT,false]
-
-
1
assert_equal 'Rule-999', rule1.myid
-
1
assert_equal 'Rule-999', rule2.myid
-
1
assert_equal 'Rule-999', rule3.myid
-
1
assert_equal 'Rule1', rule4.myid
-
1
assert_equal 'Rule2', rule5.myid
-
end
-
-
end
-
-
1
describe '.new' do
-
-
1
it 'should create a new rule' do
-
1
pred = Porolog::Predicate.new :pred
-
1
args = Porolog::Arguments.new pred, [1,2,3]
-
1
defn = [true]
-
1
rule = Porolog::Rule.new args, defn
-
-
1
assert_Rule rule, :pred, [1,2,3], [true]
-
end
-
-
end
-
-
1
describe '#initialize' do
-
-
1
it 'should initialize arguments and definition' do
-
1
pred = Porolog::Predicate.new :pred
-
1
args = Porolog::Arguments.new pred, [1,2,3]
-
1
defn = [:CUT,false]
-
1
rule = Porolog::Rule.new args, defn
-
-
1
assert_Rule rule, :pred, [1,2,3], [:CUT,false]
-
1
assert_equal args, rule.arguments
-
1
assert_equal defn, rule.definition
-
end
-
-
end
-
-
1
describe '#myid' do
-
-
1
it 'should show the rule index' do
-
1
pred = Porolog::Predicate.new :pred
-
1
args = Porolog::Arguments.new pred, [1,2,3]
-
1
defn = [:CUT,false]
-
1
rule = Porolog::Rule.new args, defn
-
-
1
assert_equal 'Rule1', rule.myid
-
end
-
-
1
it 'should show -999 when deleted/unregistered' do
-
1
pred = Porolog::Predicate.new :pred
-
1
args = Porolog::Arguments.new pred, [1,2,3]
-
1
defn = [:CUT,false]
-
1
rule = Porolog::Rule.new args, defn
-
-
1
Porolog::Rule.reset
-
-
1
assert_equal 'Rule-999', rule.myid
-
end
-
-
end
-
-
1
describe '#inspect' do
-
-
1
it 'should show the predicate, arguments, and definition' do
-
1
pred = Porolog::Predicate.new :pred
-
1
args = Porolog::Arguments.new pred, [1,2,3]
-
1
defn = [:CUT,false]
-
1
rule = Porolog::Rule.new args, defn
-
-
1
assert_equal ' pred(1,2,3):- [:CUT, false]', rule.inspect
-
end
-
-
end
-
-
1
describe '#satisfy' do
-
-
1
it 'should create a subgoal and unify with the goal' do
-
1
pred = Porolog::Predicate.new :pred
-
1
args = Porolog::Arguments.new pred, [:a,:b,:c]
-
1
defn = [true]
-
1
rule = Porolog::Rule.new args, defn
-
-
1
goal = new_goal :pred, :x, :y, :z
-
-
1
called = false
-
1
rule.satisfy(goal) do |solution_goal|
-
1
called = true
-
-
4
assert_Goal solution_goal, :pred, [:a, :b, :c].map{|name| solution_goal.variable(name) }
-
1
assert_Goal_variables solution_goal, { a: nil, b: nil, c: nil }, [
-
'Goal2.:a',
-
' Goal1.:x',
-
'Goal2.:b',
-
' Goal1.:y',
-
'Goal2.:c',
-
' Goal1.:z',
-
].join("\n")
-
-
1
assert_equal goal, solution_goal.calling_goal
-
end
-
-
1
assert called, 'the satisfy block should be called'
-
end
-
-
1
it 'should delete the subgoal even if it does not unify with the goal' do
-
1
pred = Porolog::Predicate.new :alpha
-
1
args = Porolog::Arguments.new pred, [1,2,3,4]
-
1
defn = [true]
-
1
rule = Porolog::Rule.new args, defn
-
-
1
goal = new_goal :beta, :x, :y
-
-
1
subgoal = new_goal :gamma, :x, :y
-
1
subgoal.expects(:delete!).with().times(1)
-
1
subgoal_spy = Spy.on(Porolog::Goal, :new).and_return(subgoal)
-
1
called = false
-
-
1
rule.satisfy(goal) do |solution_goal|
-
skipped
#:nocov:
-
skipped
called = true
-
skipped
#:nocov:
-
end
-
1
refute called, 'the satisfy block should not be called'
-
1
assert_equal 1, subgoal_spy.calls.size
-
end
-
-
end
-
-
1
describe '#satisfy_definition' do
-
-
1
it 'should call block with subgoal and return true when the definition is true' do
-
1
pred = Porolog::Predicate.new :normal
-
1
args = Porolog::Arguments.new pred, [12]
-
1
rule = Porolog::Rule.new args, true
-
-
1
check = 56
-
1
result = rule.satisfy_definition(12, 34) do |subgoal|
-
1
assert_equal 34, subgoal
-
1
check = 78
-
end
-
1
assert_equal true, result
-
1
assert_equal 78, check
-
end
-
-
1
it 'should not call block but return false when the definition is false' do
-
1
pred = Porolog::Predicate.new :negative
-
1
args = Porolog::Arguments.new pred, [12]
-
1
rule = Porolog::Rule.new args, false
-
-
1
check = 56
-
1
result = rule.satisfy_definition(12, 34) do |subgoal|
-
skipped
#:nocov:
-
skipped
check = 78
-
skipped
#:nocov:
-
end
-
1
assert_equal false, result
-
1
assert_equal 56, check
-
end
-
-
1
it 'should call block with subgoal when the definition is an Array' do
-
1
pred1 = Porolog::Predicate.new :success
-
1
args1 = Porolog::Arguments.new pred1, [:any]
-
1
Porolog::Rule.new args1, true
-
-
1
pred2 = Porolog::Predicate.new :conjunction
-
1
args2 = Porolog::Arguments.new pred2, [12]
-
1
rule2 = Porolog::Rule.new args2, [true,true,true]
-
-
1
check = 56
-
1
result = rule2.satisfy_definition(args1.goal, args2.goal) do |subgoal|
-
1
check = 78
-
end
-
1
assert_equal true, result
-
1
assert_equal 78, check
-
end
-
-
1
it 'should raise an exception when the definition is unknown' do
-
1
Porolog::predicate :strange
-
-
1
strange(12) << 3.4
-
-
1
exception = assert_raises Porolog::Rule::DefinitionError do
-
1
strange(:X).solve
-
end
-
1
assert_equal 'UNEXPECTED TYPE OF DEFINITION: 3.4 (Float)', exception.message
-
end
-
-
end
-
-
1
describe '#satisfy_conjunction' do
-
-
9
let(:pred1 ) { Porolog::Predicate.new :parent }
-
9
let(:pred2 ) { Porolog::Predicate.new :child }
-
9
let(:args1 ) { Porolog::Arguments.new pred1, [1,2,3,4] }
-
9
let(:args2 ) { Porolog::Arguments.new pred2, [:x,:y] }
-
7
let(:goal ) { Porolog::Goal.new args1, nil }
-
7
let(:subgoal) { Porolog::Goal.new args2, goal }
-
-
1
it 'should handle CUT expression' do
-
1
rule = Porolog::Rule.new args1, [:CUT,true]
-
-
1
goal.expects(:terminate!).with().times(1)
-
1
rule_spy = Spy.on(rule, :satisfy_conjunction).and_call_through
-
1
called = false
-
-
1
result = rule.satisfy_conjunction(goal, subgoal, rule.definition) do |solution_goal|
-
1
called = true
-
end
-
-
1
assert result, 'satisfy_conjunction should succeed'
-
1
assert called, 'the satisfy block should be called'
-
1
assert_equal 2, rule_spy.calls.size
-
1
assert_equal [goal, subgoal, [true]], rule_spy.calls[0].args # innermost call; finishes first
-
1
assert_equal [goal, subgoal, [:CUT,true]], rule_spy.calls[1].args # outermost call; finishes last
-
end
-
-
1
it 'should handle CUT expression at the end of a conjunction' do
-
1
rule = Porolog::Rule.new args1, [:CUT]
-
-
1
goal.expects(:terminate!).with().times(1)
-
1
rule_spy = Spy.on(rule, :satisfy_conjunction).and_call_through
-
1
called = false
-
-
1
result = rule.satisfy_conjunction(goal, subgoal, rule.definition) do |solution_goal|
-
1
called = true
-
end
-
-
1
assert result, 'satisfy_conjunction should succeed'
-
1
assert called, 'the satisfy block should be called'
-
1
assert_equal 1, rule_spy.calls.size
-
1
assert_equal [goal, subgoal, [:CUT]], rule_spy.calls[0].args
-
end
-
-
1
it 'should handle true expression' do
-
1
rule = Porolog::Rule.new args1, [true]
-
-
1
goal.expects(:terminate!).with().times(0)
-
1
rule_spy = Spy.on(rule, :satisfy_conjunction).and_call_through
-
1
called = false
-
-
1
result = rule.satisfy_conjunction(goal, subgoal, rule.definition) do |solution_goal|
-
1
called = true
-
end
-
-
1
assert result, 'satisfy_conjunction should succeed'
-
1
assert called, 'the satisfy block should be called'
-
1
assert_equal 1, rule_spy.calls.size
-
1
assert_equal [goal, subgoal, [true]], rule_spy.calls[0].args
-
end
-
-
1
it 'should handle false expression' do
-
1
rule = Porolog::Rule.new args1, [false]
-
-
1
goal.expects(:terminate!).with().times(0)
-
1
rule_spy = Spy.on(rule, :satisfy_conjunction).and_call_through
-
1
called = false
-
-
1
result = rule.satisfy_conjunction(goal, subgoal, rule.definition) do |solution_goal|
-
skipped
#:nocov:
-
skipped
called = true
-
skipped
#:nocov:
-
end
-
-
1
refute result, 'satisfy_conjunction should not succeed'
-
1
refute called, 'the satisfy block should not be called'
-
1
assert_equal 1, rule_spy.calls.size
-
1
assert_equal [goal, subgoal, [false]], rule_spy.calls[0].args
-
end
-
-
1
it 'should handle nil expression' do
-
1
rule = Porolog::Rule.new args1, []
-
-
1
goal.expects(:terminate!).with().times(0)
-
1
rule_spy = Spy.on(rule, :satisfy_conjunction).and_call_through
-
1
called = false
-
-
1
result = rule.satisfy_conjunction(goal, subgoal, rule.definition) do |solution_goal|
-
skipped
#:nocov:
-
skipped
called = true
-
skipped
#:nocov:
-
end
-
-
1
assert result, 'satisfy_conjunction should not succeed'
-
1
assert called, 'the satisfy block should not be called'
-
1
assert_equal 1, rule_spy.calls.size
-
1
assert_equal [goal, subgoal, []], rule_spy.calls[0].args
-
end
-
-
1
it 'should evaluate conjunctions until a fail' do
-
1
conjunction = [true, true, true, false, true, :CUT, true]
-
1
rule = Porolog::Rule.new args1, conjunction
-
-
1
goal.expects(:terminate!).with().times(0)
-
1
rule_spy = Spy.on(rule, :satisfy_conjunction).and_call_through
-
1
called = false
-
-
1
result = rule.satisfy_conjunction(goal, subgoal, rule.definition) do |solution_goal|
-
skipped
#:nocov:
-
skipped
called = true
-
skipped
#:nocov:
-
end
-
-
1
refute result, 'satisfy_conjunction should not succeed'
-
1
refute called, 'the satisfy block should not be called'
-
1
assert_equal 4, rule_spy.calls.size
-
1
assert_equal [goal, subgoal, conjunction[3..-1]], rule_spy.calls[0].args
-
1
assert_equal [goal, subgoal, conjunction[2..-1]], rule_spy.calls[1].args
-
1
assert_equal [goal, subgoal, conjunction[1..-1]], rule_spy.calls[2].args
-
1
assert_equal [goal, subgoal, conjunction[0..-1]], rule_spy.calls[3].args
-
end
-
-
1
it 'should unify and instantiate variables with a subgoal and satisfy the subgoal and uninstantiate the instantiations' do
-
1
goal = Porolog::Goal.new args1, nil
-
1
subgoal = Porolog::Goal.new args2, goal
-
-
1
Porolog::predicate :gamma
-
-
1
gamma(8,5).fact!
-
-
1
rule = Porolog::Rule.new args1, [gamma(:j,:k)]
-
-
1
subsubgoal = new_goal :gamma, :y, :z
-
1
subsubgoal_spy = Spy.on(Porolog::Goal, :new).and_return do |*args|
-
1
Spy.off(Porolog::Goal, :new)
-
1
subsubgoal
-
end
-
-
1
goal.expects(:terminate!).with().times(0)
-
1
subgoal.expects(:terminate!).with().times(0)
-
1
subsubgoal.expects(:terminate!).with().times(0)
-
-
1
goal.expects(:delete!).with().times(0)
-
1
subgoal.expects(:delete!).with().times(0)
-
1
subsubgoal.expects(:delete!).with().times(1)
-
-
1
rule_spy = Spy.on(rule, :satisfy_conjunction).and_call_through
-
1
called = false
-
-
1
result = rule.satisfy_conjunction(goal, subgoal, rule.definition) do |solution_goal|
-
1
called = true
-
1
assert_Goal goal, :parent, [1,2,3,4]
-
1
assert_Goal_variables goal, {}, ''
-
1
assert_Goal_variables subgoal, { x: nil, y: 8, z: 5 }, [
-
'Goal2.:x',
-
' Goal3.:x',
-
'Goal2.:y',
-
' Goal3.:y',
-
' Goal4.8',
-
'Goal2.:z',
-
' Goal3.:z',
-
' Goal4.5',
-
].join("\n")
-
1
assert_Goal_variables subsubgoal, { x: nil, y: 8, z: 5 }, [
-
'Goal3.:y',
-
' Goal2.:y',
-
' Goal4.8',
-
'Goal3.:z',
-
' Goal2.:z',
-
' Goal4.5',
-
'Goal3.:x',
-
' Goal2.:x',
-
].join("\n")
-
end
-
-
1
assert result, 'satisfy_conjunction should succeed'
-
1
assert called, 'the satisfy block should be called'
-
1
assert_equal 1, subsubgoal_spy.calls.size
-
1
assert_equal 1, rule_spy.calls.size
-
1
assert_equal [goal, subgoal, [gamma(:j,:k)]], rule_spy.calls[0].args
-
-
1
assert_Goal_variables subgoal, { x: nil, y: nil, z: nil }, [
-
'Goal2.:x',
-
' Goal3.:x',
-
'Goal2.:y',
-
' Goal3.:y',
-
'Goal2.:z',
-
' Goal3.:z',
-
].join("\n")
-
1
assert_Goal_variables subsubgoal, { x: nil, y: nil, z: nil }, [
-
'Goal3.:y',
-
' Goal2.:y',
-
'Goal3.:z',
-
' Goal2.:z',
-
'Goal3.:x',
-
' Goal2.:x',
-
].join("\n")
-
end
-
-
1
it 'should not unify and not instantiate variables with a subgoal nor satisfy the subgoal when it cannot be unified' do
-
# -- Create Goals --
-
1
goal = Porolog::Goal.new args1, nil
-
1
subgoal = Porolog::Goal.new args2, goal
-
1
subgoal.instantiate :y, 7
-
-
# -- Create goal for satisfy_conjunction() --
-
1
subsubgoal = new_goal :gamma, :y, :y, :z
-
1
subsubgoal.instantiate :y, 3
-
-
# -- Check goals are setup correctly --
-
1
assert_Goal goal, :parent, [1,2,3,4]
-
1
assert_Goal subgoal, :child, [:x,:y]
-
1
assert_equal 'Goal1 -- Solve parent(1,2,3,4)', goal.inspect
-
1
assert_equal 'Goal2 -- Solve child(:x,:y)', subgoal.inspect
-
-
1
assert_Goal_variables goal, {}, [].join("\n")
-
-
1
assert_Goal_variables subgoal, { x: nil, y: 7 }, [
-
'Goal2.:x',
-
'Goal2.:y',
-
' Goal2.7',
-
].join("\n")
-
-
1
assert_Goal_variables subsubgoal, { y: 3, z: nil }, [
-
'Goal3.:y',
-
' Goal3.3',
-
'Goal3.:z',
-
].join("\n")
-
-
# -- Define Predicate --
-
1
Porolog::predicate :gamma
-
-
1
gamma(8,5).fact!
-
-
1
rule = Porolog::Rule.new args1, [gamma(:j,:k)]
-
-
# -- Prepare Checks for satisfy_conjunction() --
-
1
subsubgoal_spy = Spy.on(Porolog::Goal, :new).and_return do |*args|
-
1
Spy.off(Porolog::Goal, :new)
-
1
subsubgoal
-
end
-
-
1
goal. expects(:terminate!).with().times(0)
-
1
subgoal. expects(:terminate!).with().times(0)
-
1
subsubgoal.expects(:terminate!).with().times(0)
-
-
1
goal. expects(:delete!).with().times(0)
-
1
subgoal. expects(:delete!).with().times(0)
-
1
subsubgoal.expects(:delete!).with().times(1)
-
-
1
rule_spy = Spy.on(rule, :satisfy_conjunction).and_call_through
-
1
called = false
-
-
# -- Pre-execution Checks --
-
1
assert_Goal_variables goal, {}, [].join("\n")
-
1
assert_Goal_variables subgoal, { x: nil, y: 7 }, [
-
'Goal2.:x',
-
'Goal2.:y',
-
' Goal2.7',
-
].join("\n")
-
1
assert_Goal_variables subsubgoal, { y: 3, z: nil }, [
-
'Goal3.:y',
-
' Goal3.3',
-
'Goal3.:z',
-
].join("\n")
-
-
# -- Execute Code --
-
1
result = rule.satisfy_conjunction(goal, subgoal, rule.definition) do |solution_goal|
-
skipped
#:nocov:
-
skipped
called = true
-
skipped
#:nocov:
-
end
-
-
# -- Peform Checks --
-
1
refute result, 'satisfy_conjunction should not succeed'
-
1
refute called, 'the satisfy block should not be called'
-
1
assert_equal 1, subsubgoal_spy.calls.size
-
1
assert_equal 1, rule_spy.calls.size
-
1
assert_equal [goal, subgoal, [gamma(:j,:k)]], rule_spy.calls[0].args
-
-
1
assert_Goal_variables goal, {}, [].join("\n")
-
-
1
assert_Goal_variables subgoal, { x: nil, y: 7 }, [
-
'Goal2.:x',
-
'Goal2.:y',
-
' Goal2.7',
-
].join("\n")
-
-
1
assert_Goal_variables subsubgoal, { x: nil, y: 3, z: nil }, [
-
'Goal3.:y',
-
' Goal3.3',
-
'Goal3.:z',
-
'Goal3.:x',
-
].join("\n")
-
end
-
-
end
-
-
end
-
-
end
-
#
-
# test/porolog/scope_test.rb - Test Suite for Porolog::Scope
-
#
-
# Luis Esteban 2 May 2018
-
# created
-
#
-
-
1
require_relative '../test_helper'
-
-
1
describe 'Porolog' do
-
-
1
before(:all) do
-
14
reset
-
end
-
-
1
describe 'Scope' do
-
-
1
it 'should already declare the default scope' do
-
1
assert_equal 1, Porolog::Scope.scopes.size
-
1
assert_equal [:default], Porolog::Scope.scopes
-
-
1
assert_Scope Porolog::Scope[:default], :default, []
-
end
-
-
1
it 'should allow predicates with the same name to coexist in different scopes' do
-
1
prime = prime1 = Porolog::Predicate.new :prime, :first
-
-
1
prime.(2).fact!
-
1
prime.(3).fact!
-
1
prime.(5).fact!
-
1
prime.(7).fact!
-
1
prime.(11).fact!
-
-
1
prime = prime2 = Porolog::Predicate.new :prime, :second
-
-
1
prime.('pump A').fact!
-
1
prime.('pump B').fact!
-
1
prime.('pump C').fact!
-
1
prime.('pump D').fact!
-
-
1
assert_equal [:default,:first,:second], Porolog::Scope.scopes
-
1
assert_Scope Porolog::Scope[:default], :default, []
-
1
assert_Scope Porolog::Scope[:first], :first, [prime1]
-
1
assert_Scope Porolog::Scope[:second], :second, [prime2]
-
-
1
assert_equal :prime, prime1.name
-
1
assert_equal :prime, prime2.name
-
-
solutions = [
-
1
{ X: 2 },
-
{ X: 3 },
-
{ X: 5 },
-
{ X: 7 },
-
{ X: 11 },
-
]
-
1
assert_equal solutions, prime1.(:X).solve
-
-
solutions = [
-
1
{ X: 'pump A' },
-
{ X: 'pump B' },
-
{ X: 'pump C' },
-
{ X: 'pump D' },
-
]
-
1
assert_equal solutions, prime2.(:X).solve
-
end
-
-
1
describe '.scopes' do
-
-
1
it 'should return the names of all registered scopes' do
-
1
Porolog::Scope.new :alpha
-
1
Porolog::Scope.new :bravo
-
1
Porolog::Scope.new :carly
-
-
1
assert_equal 4, Porolog::Scope.scopes.size
-
1
assert_equal [:default, :alpha, :bravo, :carly], Porolog::Scope.scopes
-
end
-
-
end
-
-
1
describe '.reset' do
-
-
1
it 'should clear all scopes' do
-
1
delta = Porolog::Predicate.new :delta
-
-
1
Porolog::Scope.new :alpha
-
1
Porolog::Scope.new :bravo
-
1
Porolog::Scope.new :carly
-
-
1
assert_equal 4, Porolog::Scope.scopes.size
-
1
assert_equal [:default, :alpha, :bravo, :carly], Porolog::Scope.scopes
-
-
1
assert_Scope Porolog::Scope[:default], :default, [delta]
-
1
assert_Scope Porolog::Scope[:alpha], :alpha, []
-
1
assert_Scope Porolog::Scope[:bravo], :bravo, []
-
1
assert_Scope Porolog::Scope[:carly], :carly, []
-
-
1
Porolog::Scope.reset
-
-
1
assert_equal 1, Porolog::Scope.scopes.size
-
1
assert_equal [:default], Porolog::Scope.scopes
-
-
1
assert_Scope Porolog::Scope[:default], :default, []
-
-
1
assert_nil Porolog::Scope[:alpha]
-
1
assert_nil Porolog::Scope[:bravo]
-
1
assert_nil Porolog::Scope[:carly]
-
end
-
-
1
it 'should clear all scopes and start with a default scope when reset' do
-
1
test_predicate0 = Porolog::Predicate.new 'test_predicate0'
-
1
test_predicate11 = Porolog::Predicate.new 'test_predicate11', :scope1
-
1
test_predicate12 = Porolog::Predicate.new 'test_predicate12', :scope1
-
1
test_predicate13 = Porolog::Predicate.new 'test_predicate13', :scope1
-
1
test_predicate21 = Porolog::Predicate.new 'test_predicate21', :scope2
-
1
test_predicate22 = Porolog::Predicate.new 'test_predicate22', :scope2
-
1
test_predicate23 = Porolog::Predicate.new 'test_predicate23', :scope2
-
-
1
assert_equal [:default, :scope1, :scope2], Porolog::Scope.scopes
-
1
assert_equal [test_predicate0], Porolog::Scope[:default].predicates
-
1
assert_equal [test_predicate11, test_predicate12, test_predicate13], Porolog::Scope[:scope1 ].predicates
-
1
assert_equal [test_predicate21, test_predicate22, test_predicate23], Porolog::Scope[:scope2 ].predicates
-
-
1
Porolog::Scope.reset
-
-
1
test_predicate3 = Porolog::Predicate.new 'test_predicate3'
-
-
1
assert_equal [:default], Porolog::Scope.scopes
-
1
assert_equal [test_predicate3], Porolog::Scope[:default].predicates
-
1
assert_nil Porolog::Scope[:scope1 ]
-
1
assert_nil Porolog::Scope[:scope2 ]
-
end
-
-
end
-
-
1
describe '.[]' do
-
-
1
it 'should provide access to a scope by name' do
-
1
alpha = Porolog::Scope.new :alpha
-
1
bravo = Porolog::Scope.new :bravo
-
1
carly = Porolog::Scope.new :carly
-
-
1
assert_equal alpha, Porolog::Scope[:alpha]
-
1
assert_equal bravo, Porolog::Scope[:bravo]
-
1
assert_equal carly, Porolog::Scope[:carly]
-
1
assert_nil Porolog::Scope[:delta]
-
end
-
-
end
-
-
1
describe '.new' do
-
-
1
it 'should not create duplicate scopes (with the same name)' do
-
1
Porolog::Scope.new :duplicate
-
1
Porolog::Scope.new :duplicate
-
1
Porolog::Scope.new :duplicate
-
1
Porolog::Scope.new :duplicate
-
-
1
assert_equal 2, Porolog::Scope.scopes.size
-
1
assert_equal 1, Porolog::Scope.scopes.count(:duplicate)
-
1
assert_equal [:default, :duplicate], Porolog::Scope.scopes
-
end
-
-
end
-
-
1
describe '#initialize' do
-
-
1
it 'should keep track of created scopes' do
-
1
Porolog::Scope.new :alpha
-
1
Porolog::Scope.new :bravo
-
1
Porolog::Scope.new :carly
-
-
1
assert_equal [:default,:alpha,:bravo,:carly], Porolog::Scope.scopes
-
end
-
-
1
it 'should create scopes with no predicates' do
-
1
scope = Porolog::Scope.new('test_scope_name')
-
-
1
assert_respond_to scope, :predicates
-
1
assert_equal [], scope.predicates
-
end
-
-
end
-
-
1
describe '#name' do
-
-
1
it 'should create scopes with a name attribute' do
-
1
scope = Porolog::Scope.new('test_scope_name')
-
-
1
assert_respond_to scope, :name
-
1
assert_equal 'test_scope_name', scope.name
-
end
-
-
end
-
-
1
describe '#predicates' do
-
-
1
it 'should provide access to all the predicates of a scope' do
-
1
test_predicate1 = Porolog::Predicate.new 'test_predicate1', :test
-
1
test_predicate2 = Porolog::Predicate.new 'test_predicate2', :test
-
-
1
assert_respond_to Porolog::Scope[:test], :predicates
-
1
assert_equal [test_predicate1,test_predicate2], Porolog::Scope[:test].predicates
-
end
-
-
end
-
-
1
describe '#[]' do
-
-
1
it 'should provide access to a predicate of a scope by its name' do
-
1
test_predicate3 = Porolog::Predicate.new 'test_predicate3', :test
-
1
test_predicate4 = Porolog::Predicate.new 'test_predicate4', :test
-
-
1
assert_includes Porolog::Scope[:test].predicates, test_predicate3
-
-
1
assert_instance_of Class, Porolog::Scope
-
1
assert_instance_of Porolog::Scope, Porolog::Scope[:test]
-
1
assert_instance_of Porolog::Predicate, Porolog::Scope[:test][:test_predicate3]
-
-
1
assert_equal test_predicate3, Porolog::Scope[:test][:test_predicate3]
-
1
assert_equal test_predicate3, Porolog::Scope[:test]['test_predicate3']
-
1
assert_equal test_predicate4, Porolog::Scope[:test]['test_predicate4']
-
end
-
-
end
-
-
1
describe '#[]=' do
-
-
1
it 'should raise an error when trying to put anthing but a Predicate in a Scope' do
-
1
error = assert_raises(Porolog::Scope::NotPredicateError){
-
1
scope = Porolog::Scope.new :scope
-
-
1
scope[:predicate] = 'predicate'
-
}
-
1
assert_equal error.message, '"predicate" is not a Predicate'
-
end
-
-
1
it 'should allow an existing predicate to be assigned to a scope' do
-
1
test_predicate = Porolog::Predicate.new 'test_predicate', :test
-
-
1
scope = Porolog::Scope.new :scope
-
-
1
scope[:predicate] = test_predicate
-
-
1
assert_equal 3, Porolog::Scope.scopes.size
-
1
assert_equal [:default, :test, :scope], Porolog::Scope.scopes
-
-
1
assert_Scope Porolog::Scope[:default], :default, []
-
1
assert_Scope Porolog::Scope[:test], :test, [test_predicate]
-
1
assert_Scope Porolog::Scope[:scope], :scope, [test_predicate]
-
end
-
-
end
-
-
end
-
-
end
-
#
-
# test/porolog/tail_test.rb - Test Suite for Porolog::Tail
-
#
-
# Luis Esteban 2 May 2018
-
# created
-
#
-
-
1
require_relative '../test_helper'
-
-
1
describe 'Porolog' do
-
-
1
before(:all) do
-
12
reset
-
end
-
-
1
describe 'Tail' do
-
-
1
describe '.new' do
-
-
1
it 'creates an unknown tail when no value is provided' do
-
1
tail = Porolog::Tail.new
-
-
1
assert_Tail tail, '*...'
-
1
assert_equal Porolog::UNKNOWN_TAIL, tail.value
-
end
-
-
1
it 'creates a tail with a value' do
-
1
tail = Porolog::Tail.new [2,3,5,7,11,13]
-
-
1
assert_Tail tail, '*[2, 3, 5, 7, 11, 13]'
-
1
assert_equal [2,3,5,7,11,13], tail.value
-
end
-
-
1
it 'creates a tail with a variable' do
-
1
tail = Porolog::Tail.new :x
-
-
1
assert_Tail tail, '*:x'
-
1
assert_equal :x, tail.value
-
end
-
-
end
-
-
1
describe '#value' do
-
-
2
let(:object) { Object.new }
-
-
1
before do
-
1
def object.inspect
-
1
super.gsub(/Object:0x[[:xdigit:]]+/,'Object:0xXXXXXX')
-
end
-
end
-
-
1
it 'returns its value' do
-
1
tail = Porolog::Tail.new object
-
-
1
assert_Tail tail, '*#<Object:0xXXXXXX>'
-
1
assert_equal object, tail.value
-
end
-
-
end
-
-
1
describe '#inspect' do
-
-
1
it 'returns a string showing a splat operation is implied' do
-
1
tail = Porolog::Tail.new [2,3,5,7,11,13]
-
-
1
assert_equal '*[2, 3, 5, 7, 11, 13]', tail.inspect
-
end
-
-
end
-
-
1
describe '#variables' do
-
-
1
it 'returns an empty Array when it was created without arguments' do
-
1
tail = Porolog::Tail.new
-
-
1
assert_equal [], tail.variables
-
end
-
-
1
it 'returns an empty Array when it was created with an Array of atomics' do
-
1
tail = Porolog::Tail.new [2,3,5,7,11,13]
-
-
1
assert_equal [], tail.variables
-
end
-
-
1
it 'returns an Array of Symbols when it was created with a Symbol' do
-
1
tail = Porolog::Tail.new :t
-
-
1
assert_equal [:t], tail.variables
-
end
-
-
1
it 'returns an Array of Symbols when it was created with an Array with embedded Symbols' do
-
1
tail = Porolog::Tail.new [2,3,:x,7,[:y,[:z]],13]
-
-
1
assert_equal [:x,:y,:z], tail.variables
-
end
-
-
end
-
-
1
describe '#==' do
-
-
1
it 'returns true for unknown tails' do
-
1
tail1 = Porolog::Tail.new
-
1
tail2 = Porolog::Tail.new
-
-
1
assert tail1 == tail2, name
-
end
-
-
1
it 'returns false for different symbols' do
-
1
tail1 = Porolog::Tail.new :t
-
1
tail2 = Porolog::Tail.new :x
-
-
1
refute tail1 == tail2, name
-
end
-
-
1
it 'returns true for equal values' do
-
1
tail1 = Porolog::Tail.new 12.34
-
1
tail2 = Porolog::Tail.new 12.34
-
-
1
assert tail1 == tail2, name
-
end
-
-
end
-
-
end
-
-
end
-
#
-
# test/porolog/value_test.rb - Test Suite for Porolog::Value
-
#
-
# Luis Esteban 2 May 2018
-
# created
-
#
-
-
1
require_relative '../test_helper'
-
-
1
describe 'Porolog' do
-
-
1
before(:all) do
-
25
reset
-
end
-
-
1
describe 'Value' do
-
-
23
let(:goal) { new_goal :generic, [:m, :n] }
-
12
let(:v) { Porolog::Value.new 456.123, goal }
-
-
1
describe '.new' do
-
-
1
it 'should create a new value' do
-
1
assert_instance_of Porolog::Value, v
-
end
-
-
end
-
-
1
describe '#initialize' do
-
-
1
it 'should initialize value and goal' do
-
1
assert_equal 456.123, v.value
-
1
assert_equal goal, v.goal
-
end
-
-
1
it 'should raise an error when a goal is not provided' do
-
1
assert_raises Porolog::Value::GoalError do
-
1
Porolog::Value.new 456.789, 'goal'
-
end
-
end
-
-
1
it 'should copy the value of another Value' do
-
1
other = Porolog::Value.new 123.456, goal
-
1
v = Porolog::Value.new other, goal
-
-
1
refute_instance_of Porolog::Value, v.value
-
1
assert_equal 123.456, v.value
-
end
-
-
end
-
-
1
describe '#inspect' do
-
-
1
it 'should show the goal and the value' do
-
1
assert_equal 'Goal1.456.123', v.inspect
-
end
-
-
end
-
-
1
describe '#instantiations' do
-
-
1
it 'should return no instantiations' do
-
1
assert_equal [], v.instantiations
-
end
-
-
end
-
-
1
describe '#inspect_with_instantiations' do
-
-
3
let(:depth) { rand(0..10) }
-
3
let(:index) { rand(0..10) }
-
-
1
it 'should should show the goal and value' do
-
1
assert_equal 'Goal1.456.123', v.inspect_with_instantiations
-
end
-
-
1
it 'should should show the goal and indexed value' do
-
1
assert_equal "Goal1.456.123[#{index}]", v.inspect_with_instantiations(nil, 0, index)
-
end
-
-
1
it 'should should show the goal and value indentation showing instantiation depth' do
-
1
assert_equal "#{' ' * depth}Goal1.456.123", v.inspect_with_instantiations(nil, depth)
-
end
-
-
1
it 'should should show the goal and value indentation showing instantiation depth and indexed value' do
-
1
assert_equal "#{' ' * depth}Goal1.456.123[#{index}]", v.inspect_with_instantiations(nil, depth, index)
-
end
-
-
end
-
-
1
describe '#remove' do
-
-
3
let(:predicate1) { Porolog::Predicate.new :removal }
-
3
let(:arguments1) { predicate1.arguments(:m,:n) }
-
3
let(:goal1) { arguments1.goal }
-
3
let(:goal2) { arguments1.goal }
-
3
let(:goal3) { arguments1.goal }
-
3
let(:goal4) { arguments1.goal }
-
-
3
let(:variable1) { Porolog::Variable.new :x, goal1 }
-
3
let(:variable2) { Porolog::Variable.new :y, goal2 }
-
3
let(:variable3) { Porolog::Variable.new :z, goal3 }
-
3
let(:variable4) { Porolog::Variable.new :a, goal1 }
-
3
let(:value1) { Porolog::Value.new 'word', goal2 }
-
3
let(:value2) { Porolog::Value.new 'draw', goal4 }
-
-
1
before do
-
2
reset
-
-
2
variable1.instantiate variable2
-
2
variable1.instantiate variable3
-
2
variable2.instantiate value1
-
2
variable4.instantiate value2
-
-
2
assert_Goal_variables goal1, { m: nil, n: nil, x: 'word', a: 'draw' }, [
-
'Goal1.:m',
-
'Goal1.:n',
-
'Goal1.:x',
-
' Goal2.:y',
-
' Goal2."word"',
-
' Goal3.:z',
-
'Goal1.:a',
-
' Goal4."draw"',
-
].join("\n")
-
-
2
assert_equal 2, variable1.instantiations.size
-
2
assert_equal 2, variable2.instantiations.size
-
2
assert_equal 1, variable3.instantiations.size
-
2
assert_equal 1, variable4.instantiations.size
-
-
2
assert_Instantiation variable1.instantiations[0], variable1, variable2, nil, nil
-
2
assert_Instantiation variable1.instantiations[1], variable1, variable3, nil, nil
-
2
assert_Instantiation variable2.instantiations[0], variable1, variable2, nil, nil
-
2
assert_Instantiation variable2.instantiations[1], variable2, value1, nil, nil
-
2
assert_Instantiation variable3.instantiations[0], variable1, variable3, nil, nil
-
2
assert_Instantiation variable4.instantiations[0], variable4, value2, nil, nil
-
-
2
assert_equal 'word', variable1.value
-
2
assert_equal 'word', variable2.value
-
2
assert_equal 'word', variable3.value
-
2
assert_equal 'draw', variable4.value
-
end
-
-
1
it 'should remove all instantiations case 1: remove value1' do
-
1
value1.remove
-
-
1
assert_Goal_variables goal1, { m: nil, n: nil, x: nil, a: 'draw' }, [
-
'Goal1.:m',
-
'Goal1.:n',
-
'Goal1.:x',
-
' Goal2.:y',
-
' Goal3.:z',
-
'Goal1.:a',
-
' Goal4."draw"',
-
].join("\n")
-
-
1
assert_equal 2, variable1.instantiations.size
-
1
assert_equal 1, variable2.instantiations.size
-
1
assert_equal 1, variable3.instantiations.size
-
1
assert_equal 1, variable4.instantiations.size
-
-
1
assert_Instantiation variable1.instantiations[0], variable1, variable2, nil, nil
-
1
assert_Instantiation variable1.instantiations[1], variable1, variable3, nil, nil
-
1
assert_Instantiation variable2.instantiations[0], variable1, variable2, nil, nil
-
1
assert_nil variable2.instantiations[1]
-
1
assert_Instantiation variable3.instantiations[0], variable1, variable3, nil, nil
-
1
assert_Instantiation variable4.instantiations[0], variable4, value2, nil, nil
-
-
1
assert_equal variable1, variable1.value
-
1
assert_equal variable2, variable2.value
-
1
assert_equal variable3, variable3.value
-
1
assert_equal 'draw', variable4.value
-
end
-
-
1
it 'should remove all instantiations case 2: remove value2' do
-
1
value2.remove
-
-
1
assert_Goal_variables goal1, { m: nil, n: nil, x: 'word', a: nil }, [
-
'Goal1.:m',
-
'Goal1.:n',
-
'Goal1.:x',
-
' Goal2.:y',
-
' Goal2."word"',
-
' Goal3.:z',
-
'Goal1.:a',
-
].join("\n")
-
-
1
assert_equal 2, variable1.instantiations.size
-
1
assert_equal 2, variable2.instantiations.size
-
1
assert_equal 1, variable3.instantiations.size
-
1
assert_equal 0, variable4.instantiations.size
-
-
1
assert_Instantiation variable1.instantiations[0], variable1, variable2, nil, nil
-
1
assert_Instantiation variable1.instantiations[1], variable1, variable3, nil, nil
-
1
assert_Instantiation variable2.instantiations[0], variable1, variable2, nil, nil
-
1
assert_Instantiation variable2.instantiations[1], variable2, value1, nil, nil
-
1
assert_Instantiation variable3.instantiations[0], variable1, variable3, nil, nil
-
1
assert_nil variable4.instantiations[0]
-
-
1
assert_equal 'word', variable1.value
-
1
assert_equal 'word', variable2.value
-
1
assert_equal 'word', variable3.value
-
1
assert_equal variable4, variable4.value
-
end
-
-
end
-
-
1
describe '#method_missing' do
-
-
1
it 'should pass any unexpected methods to the value' do
-
1
assert_equal 456, v.to_i
-
1
assert_equal 466.123, v + 10
-
end
-
-
end
-
-
1
describe '#respond_to?' do
-
-
1
it 'should respond to Value methods and methods of the actual value' do
-
1
assert_equal true, v.respond_to?(:instantiations)
-
1
assert_equal true, v.respond_to?(:inspect_with_instantiations)
-
1
assert_equal true, v.respond_to?(:value)
-
1
assert_equal true, v.respond_to?(:to_i)
-
1
assert_equal true, v.respond_to?(:+)
-
end
-
-
end
-
-
1
describe '#value' do
-
-
1
it 'should take any number of arguments to be compatible with the value method of a variable' do
-
1
assert_equal 456.123, v.value
-
1
assert_equal 456.123, v.value(nil)
-
1
assert_equal 456.123, v.value(1,2,3)
-
1
assert_equal 456.123, v.value([])
-
end
-
-
end
-
-
1
describe '#type' do
-
-
2
let(:float) { Porolog::Value.new 456.789, goal }
-
2
let(:integer) { Porolog::Value.new 456, goal }
-
2
let(:string) { Porolog::Value.new 'average', goal }
-
2
let(:array) { Porolog::Value.new [1,2,3], goal }
-
2
let(:object) { Porolog::Value.new Object.new, goal }
-
-
1
it 'should return atomic for Float' do
-
1
assert_equal :atomic, float.type
-
end
-
-
1
it 'should return atomic for Integer' do
-
1
assert_equal :atomic, integer.type
-
end
-
-
1
it 'should return atomic for String' do
-
1
assert_equal :atomic, string.type
-
end
-
-
1
it 'should return atomic for Array' do
-
1
assert_equal :array, array.type
-
end
-
-
1
it 'should return atomic for Object' do
-
1
assert_equal :atomic, object.type
-
end
-
-
end
-
-
1
describe '#==' do
-
-
1
it 'should return false for Values of different types' do
-
1
v1 = Porolog::Value.new 456.789, goal
-
1
v2 = Porolog::Value.new 'average', goal
-
-
1
refute v1 == v2, "#{name}: #{v1.value.inspect} == #{v2.value.inspect}"
-
end
-
-
1
it 'should return false for Values of the same type but different values' do
-
1
v1 = Porolog::Value.new 456.789, goal
-
1
v2 = Porolog::Value.new 123.456, goal
-
-
1
refute v1 == v2, "#{name}: #{v1.value.inspect} == #{v2.value.inspect}"
-
end
-
-
1
it 'should return true for Values of the same type and the same value' do
-
1
v1 = Porolog::Value.new 456.789, goal
-
1
v2 = Porolog::Value.new 456.789, goal
-
-
1
assert v1 == v2, "#{name}: #{v1.value.inspect} == #{v2.value.inspect}"
-
end
-
-
end
-
-
1
describe '#variables' do
-
-
1
it 'should return an empty Array for a Float value' do
-
1
float = Porolog::Value.new 456.789, goal
-
-
1
assert_equal [], float.variables
-
end
-
-
1
it 'should return an Array of Symbols for embedded Variables' do
-
1
array = Porolog::Value.new [1, :b, 3, ['four', :e, 6], [[:g, 8]]], goal
-
-
1
assert_equal [:b, :e, :g], array.variables
-
end
-
-
end
-
-
end
-
-
end
-
#
-
# test/porolog/variable_test.rb - Test Suite for Porolog::Variable
-
#
-
# Luis Esteban 2 May 2018
-
# created
-
#
-
-
1
require_relative '../test_helper'
-
-
1
describe 'Porolog' do
-
-
1
describe 'Variable' do
-
-
54
let(:predicate1) { Porolog::Predicate.new :generic }
-
54
let(:arguments1) { predicate1.arguments(:m,:n) }
-
53
let(:goal1) { arguments1.goal }
-
28
let(:goal2) { arguments1.goal }
-
15
let(:goal3) { arguments1.goal }
-
-
1
before do
-
54
reset
-
end
-
-
1
describe '.new' do
-
-
1
it 'should create a new variable in a goal' do
-
1
variable = Porolog::Variable.new :x, goal1
-
-
1
assert_instance_of Porolog::Variable, variable
-
1
assert_Goal_variables goal1, { m: nil, n: nil, x: nil }, [
-
'Goal1.:m',
-
'Goal1.:n',
-
'Goal1.:x',
-
].join("\n")
-
end
-
-
end
-
-
1
describe '#initialize' do
-
-
1
it 'should initialize name, goal, instantiations, and values' do
-
1
variable = Porolog::Variable.new :x, goal1
-
-
1
assert_Variable variable, :x, goal1, [], []
-
end
-
-
1
it 'should convert a string name to a symbol name' do
-
1
variable = Porolog::Variable.new 's', goal1
-
-
1
assert_equal :s, variable.name
-
end
-
-
1
it 'should convert a variable name to its name' do
-
1
other = Porolog::Variable.new 'other', goal1
-
1
variable = Porolog::Variable.new other, goal1
-
-
1
assert_equal :other, variable.name
-
1
assert_Goal_variables goal1, { m: nil, n: nil, other: nil }, [
-
'Goal1.:m',
-
'Goal1.:n',
-
'Goal1.:other',
-
].join("\n")
-
end
-
-
1
it 'should convert a value name to its name and initialize its values' do
-
1
value = Porolog::Value.new 'other', goal1
-
1
variable = Porolog::Variable.new value, goal1
-
-
1
assert_equal 'other', variable.name
-
1
assert_equal [value], variable.values
-
-
1
assert_equal 'other', variable.value
-
end
-
-
1
it 'should convert other types as a name to its value' do
-
# TECH-DEBT: Not super sure about this spec!
-
1
variable = Porolog::Variable.new 0.875, goal1
-
-
1
assert_equal '0.875', variable.name
-
1
assert_instance_of Array, variable.values
-
1
assert_equal 1, variable.values.size
-
-
1
assert_Value variable.values.first, 0.875, goal1
-
1
assert_equal 0.875, variable.value
-
end
-
-
1
it 'should raise an error when a goal is not provided' do
-
1
assert_raises Porolog::Variable::GoalError do
-
1
Porolog::Variable.new :x, 'goal'
-
end
-
end
-
-
1
it 'should declare the variable in the goal' do
-
1
Porolog::Variable.new :x, goal1
-
-
1
assert_Goal_variables goal1, { m: nil, n: nil, x: nil }, [
-
'Goal1.:m',
-
'Goal1.:n',
-
'Goal1.:x',
-
].join("\n")
-
end
-
-
end
-
-
1
describe '#to_sym' do
-
-
1
it 'should convert a variable to a Symbol' do
-
1
variable = Porolog::Variable.new 'v', goal1
-
-
1
assert_equal :v, variable.to_sym
-
end
-
-
end
-
-
1
describe '#type' do
-
-
1
it 'should return variable from uninstantiated variables' do
-
1
variable = Porolog::Variable.new 'v', goal1
-
-
1
assert_equal :variable, variable.type
-
end
-
-
1
it 'should return variable from instantiated variables' do
-
1
variable = Porolog::Variable.new 'v', goal1
-
1
variable.instantiate 'string value'
-
-
1
assert_equal :variable, variable.type
-
end
-
-
end
-
-
1
describe '#inspect' do
-
-
1
it 'should show the goal and name' do
-
1
variable = Porolog::Variable.new 'v', goal1
-
-
1
assert_equal 'Goal1.:v', variable.inspect
-
end
-
-
end
-
-
1
describe '#inspect_with_instantiations' do
-
-
1
it 'should show a variable without instantiations as inspect does' do
-
1
variable = Porolog::Variable.new :x, goal1
-
-
1
assert_equal variable.inspect, variable.inspect_with_instantiations
-
end
-
-
1
it 'should return all instantiations of a variable with nested instantiations' do
-
1
variable1 = Porolog::Variable.new :x, goal1
-
1
variable2 = Porolog::Variable.new :y, goal1
-
1
variable3 = Porolog::Variable.new :z, goal1
-
-
1
variable1.instantiate variable2
-
1
variable2.instantiate variable3
-
-
1
assert_equal 'Goal1.:x', variable1.inspect
-
1
assert_equal 'Goal1.:y', variable2.inspect
-
1
assert_equal 'Goal1.:z', variable3.inspect
-
-
1
assert_instance_of Array, variable1.instantiations
-
1
assert_instance_of Array, variable2.instantiations
-
1
assert_instance_of Array, variable3.instantiations
-
-
1
assert_equal 1, variable1.instantiations.size
-
1
assert_equal 2, variable2.instantiations.size
-
1
assert_equal 1, variable3.instantiations.size
-
-
1
assert_instance_of Porolog::Instantiation, variable1.instantiations.first
-
1
assert_equal 'Goal1.:x = Goal1.:y', variable1.instantiations.first.inspect
-
-
1
assert_instance_of Porolog::Instantiation, variable2.instantiations[0]
-
1
assert_equal 'Goal1.:x = Goal1.:y', variable2.instantiations[0].inspect
-
-
1
assert_instance_of Porolog::Instantiation, variable2.instantiations[1]
-
1
assert_equal 'Goal1.:y = Goal1.:z', variable2.instantiations[1].inspect
-
-
1
assert_instance_of Porolog::Instantiation, variable3.instantiations.first
-
1
assert_equal 'Goal1.:y = Goal1.:z', variable3.instantiations.first.inspect
-
-
1
assert_equal variable1.instantiations + variable3.instantiations, variable2.instantiations
-
-
1
assert_equal(
-
[
-
'Goal1.:x',
-
' Goal1.:y',
-
' Goal1.:z',
-
'Goal1.:y',
-
' Goal1.:x',
-
' Goal1.:z',
-
'Goal1.:z',
-
' Goal1.:y',
-
' Goal1.:x',
-
].join("\n"),
-
[
-
variable1.inspect_with_instantiations,
-
variable2.inspect_with_instantiations,
-
variable3.inspect_with_instantiations,
-
].join("\n")
-
)
-
end
-
-
end
-
-
1
describe '#value' do
-
-
1
it 'should return the variable when no value has been instantiated' do
-
1
variable1 = Porolog::Variable.new :x, goal1
-
1
variable2 = Porolog::Variable.new :y, goal1
-
-
1
variable1.instantiate variable2
-
-
1
assert_equal variable1, variable1.value
-
end
-
-
1
it 'should return the indirect value through instantiations' do
-
1
variable1 = Porolog::Variable.new :x, goal1
-
1
variable2 = Porolog::Variable.new :y, goal1
-
1
value = Porolog::Value.new [1,2,3], goal1
-
-
1
variable1.instantiate variable2
-
1
variable2.instantiate value
-
-
1
assert_equal [1,2,3], variable1.value
-
end
-
-
1
it 'should not return unbound headtails' do
-
1
assert_Goal goal1, :generic, [:m, :n]
-
-
# -- Create Variables --
-
1
variable1 = Porolog::Variable.new :x, goal1
-
1
variable2 = Porolog::Variable.new :y, goal1
-
1
variable3 = Porolog::Variable.new :z, goal1
-
-
1
assert_Goal_variables goal1, { m: nil, n: nil, x: nil, y: nil, z: nil }, [
-
'Goal1.:m',
-
'Goal1.:n',
-
'Goal1.:x',
-
'Goal1.:y',
-
'Goal1.:z',
-
].join("\n")
-
-
# -- Create Values --
-
1
headtail = goal1.variablise(:m/:n)
-
1
assert_Array_with_Tail headtail, [goal1.variable(:m)], '*Goal1.:n'
-
-
1
value1 = Porolog::Value.new headtail, goal1
-
1
value2 = Porolog::Value.new [1,2,3], goal1
-
-
1
assert_Value value1, headtail, goal1
-
1
assert_Value value2, [1,2,3], goal1
-
-
# -- Instantiate --
-
# Goal1.:x
-
# Goal1.:y
-
1
i1 = variable1.instantiate variable2
-
-
1
assert_Instantiation i1, variable1, variable2, nil, nil
-
-
# -- Assert No Values --
-
1
assert_equal variable1, variable1.value
-
1
assert_equal variable2, variable2.value
-
1
assert_equal variable3, variable3.value
-
-
1
assert_Goal_variables goal1, { m: nil, n: nil, x: nil, y: nil, z: nil }, [
-
'Goal1.:m',
-
'Goal1.:n',
-
'Goal1.:x',
-
' Goal1.:y',
-
'Goal1.:y',
-
' Goal1.:x',
-
'Goal1.:z',
-
].join("\n")
-
-
# -- Instantiate --
-
# Goal1.:x
-
# Goal1.:z
-
1
i2 = variable1.instantiate variable3
-
-
1
assert_Instantiation i2, variable1, variable3, nil, nil
-
-
# -- Assert No Values --
-
1
assert_equal variable1, variable1.value
-
1
assert_equal variable2, variable2.value
-
1
assert_equal variable3, variable3.value
-
-
1
assert_Goal_variables goal1, { m: nil, n: nil, x: nil, y: nil, z: nil }, [
-
'Goal1.:m',
-
'Goal1.:n',
-
'Goal1.:x',
-
' Goal1.:y',
-
' Goal1.:z',
-
'Goal1.:y',
-
' Goal1.:x',
-
' Goal1.:z',
-
'Goal1.:z',
-
' Goal1.:x',
-
' Goal1.:y',
-
].join("\n")
-
-
# -- Instantiate --
-
# Goal1.:y
-
# [Goal1.:m, *Goal1.:n]
-
1
i3 = variable2.instantiate value1
-
-
1
assert_Instantiation i3, variable2, value1, nil, nil
-
1
assert_equal headtail, variable1.value
-
1
assert_equal headtail, variable2.value
-
1
assert_equal headtail, variable3.value
-
1
assert_equal headtail, goal1.value_of(:x)
-
1
assert_equal headtail, goal1.value_of(:y)
-
1
assert_equal headtail, goal1.value_of(:z)
-
1
assert_Goal_variables goal1, {
-
m: nil,
-
n: nil,
-
x: [nil, Porolog::UNKNOWN_TAIL],
-
y: [nil, Porolog::UNKNOWN_TAIL],
-
z: [nil, Porolog::UNKNOWN_TAIL]
-
}, [
-
'Goal1.:m',
-
' Goal1.:y[:head]',
-
' Goal1.:x',
-
' Goal1.:z',
-
' Goal1.[Goal1.:m, *Goal1.:n]',
-
' [:tail]Goal1.:n',
-
'Goal1.:n',
-
' Goal1.:y[:tail]',
-
' Goal1.:x',
-
' Goal1.:z',
-
' Goal1.[Goal1.:m, *Goal1.:n]',
-
' [:head]Goal1.:m',
-
'Goal1.:x',
-
' Goal1.:y',
-
' Goal1.[Goal1.:m, *Goal1.:n]',
-
' [:head]Goal1.:m',
-
' [:tail]Goal1.:n',
-
' Goal1.:z',
-
'Goal1.:y',
-
' Goal1.:x',
-
' Goal1.:z',
-
' Goal1.[Goal1.:m, *Goal1.:n]',
-
' [:head]Goal1.:m',
-
' [:tail]Goal1.:n',
-
'Goal1.:z',
-
' Goal1.:x',
-
' Goal1.:y',
-
' Goal1.[Goal1.:m, *Goal1.:n]',
-
' [:head]Goal1.:m',
-
' [:tail]Goal1.:n',
-
].join("\n")
-
-
# -- Instantiate --
-
1
i3 = variable3.instantiate value2
-
-
1
assert_Instantiation i3, variable3, value2, nil, nil
-
1
assert_equal [1,2,3], variable1.value
-
1
assert_equal [1,2,3], variable2.value
-
1
assert_equal [1,2,3], variable3.value
-
-
1
assert_Goal_variables goal1, { m: 1, n: [2,3], x: [1,2,3], y: [1,2,3], z: [1,2,3] }, [
-
'Goal1.:m',
-
' Goal1.:y[:head]',
-
' Goal1.:x',
-
' Goal1.:z',
-
' Goal1.[1, 2, 3]',
-
' Goal1.[Goal1.:m, *Goal1.:n]',
-
' [:tail]Goal1.:n',
-
'Goal1.:n',
-
' Goal1.:y[:tail]',
-
' Goal1.:x',
-
' Goal1.:z',
-
' Goal1.[1, 2, 3]',
-
' Goal1.[Goal1.:m, *Goal1.:n]',
-
' [:head]Goal1.:m',
-
'Goal1.:x',
-
' Goal1.:y',
-
' Goal1.[Goal1.:m, *Goal1.:n]',
-
' [:head]Goal1.:m',
-
' [:tail]Goal1.:n',
-
' Goal1.:z',
-
' Goal1.[1, 2, 3]',
-
'Goal1.:y',
-
' Goal1.:x',
-
' Goal1.:z',
-
' Goal1.[1, 2, 3]',
-
' Goal1.[Goal1.:m, *Goal1.:n]',
-
' [:head]Goal1.:m',
-
' [:tail]Goal1.:n',
-
'Goal1.:z',
-
' Goal1.:x',
-
' Goal1.:y',
-
' Goal1.[Goal1.:m, *Goal1.:n]',
-
' [:head]Goal1.:m',
-
' [:tail]Goal1.:n',
-
' Goal1.[1, 2, 3]',
-
].join("\n")
-
end
-
-
1
it 'should not return the indirect value through instantiations after uninstantiating' do
-
1
variable1 = Porolog::Variable.new :x, goal1
-
1
variable2 = Porolog::Variable.new :y, goal1
-
1
value = Porolog::Value.new [1,2,3], goal1
-
-
1
variable1.instantiate variable2
-
1
variable2.instantiate value
-
-
1
assert_equal [1,2,3], variable1.value
-
-
1
variable2.uninstantiate goal1
-
-
1
assert_equal variable1, variable1.value
-
1
assert_equal variable2, variable2.value
-
end
-
-
1
it 'should not instantiate multiple unequal values' do
-
1
variable1 = Porolog::Variable.new :x, goal1
-
1
variable2 = Porolog::Variable.new :y, goal2
-
1
variable3 = Porolog::Variable.new :z, goal3
-
-
1
value1 = Porolog::Value.new [1,2,3], goal2
-
1
value2 = Porolog::Value.new 'word', goal3
-
-
1
i1 = variable1.instantiate variable2
-
1
i2 = variable1.instantiate variable3
-
1
i3 = variable2.instantiate value1
-
1
i4 = variable3.instantiate value2
-
-
1
assert_Instantiation i1, variable1, variable2, nil, nil
-
1
assert_Instantiation i2, variable1, variable3, nil, nil
-
1
assert_Instantiation i3, variable2, value1, nil, nil
-
1
assert_nil i4
-
-
1
assert_Goal_variables goal1, { m: nil, n: nil, x: [1,2,3] }, [
-
'Goal1.:m',
-
'Goal1.:n',
-
'Goal1.:x',
-
' Goal2.:y',
-
' Goal2.[1, 2, 3]',
-
' Goal3.:z',
-
].join("\n")
-
1
assert_Goal_variables goal2, { m: nil, n: nil, y: [1,2,3] }, [
-
'Goal2.:m',
-
'Goal2.:n',
-
'Goal2.:y',
-
' Goal1.:x',
-
' Goal3.:z',
-
' Goal2.[1, 2, 3]',
-
].join("\n")
-
1
assert_Goal_variables goal3, { m: nil, n: nil, z: [1,2,3] }, [
-
'Goal3.:m',
-
'Goal3.:n',
-
'Goal3.:z',
-
' Goal1.:x',
-
' Goal2.:y',
-
' Goal2.[1, 2, 3]',
-
].join("\n")
-
end
-
-
1
it 'should not raise an exception when multiple values are equal' do
-
1
variable1 = Porolog::Variable.new :x, goal1
-
1
variable2 = Porolog::Variable.new :y, goal2
-
1
variable3 = Porolog::Variable.new :z, goal3
-
-
1
value1 = Porolog::Value.new 'word', goal2
-
1
value2 = Porolog::Value.new 'word', goal3
-
-
1
variable1.instantiate variable2
-
1
variable1.instantiate variable3
-
1
variable2.instantiate value1
-
1
variable3.instantiate value2
-
-
[
-
1
value1,
-
value2,
-
variable1,
-
variable2,
-
variable3,
-
].each do |v|
-
5
assert_equal 'word', v.value, "#{v.inspect} value should be 'word'"
-
end
-
end
-
-
1
it 'should raise an exception when the variable has multiple different non-array values' do
-
1
variable1 = Porolog::Variable.new :x, goal1
-
-
1
variable1.values << 1
-
1
variable1.values << 'one'
-
-
1
error = assert_raises Porolog::Variable::MultipleValuesError do
-
1
variable1.value
-
end
-
-
1
assert_equal 'Multiple values detected for Goal1.:x: [1, "one"]', error.message
-
end
-
-
1
it 'should prioritise non-variables over variables' do
-
1
variable1 = Porolog::Variable.new :x, goal1
-
-
1
assert_equal goal1[:x], variable1.value
-
-
1
variable1.values << :variable
-
-
1
assert_equal :variable, variable1.value
-
-
1
variable1.values << 'non-variable'
-
1
variable1.values << :variable
-
-
1
assert_equal 'non-variable', variable1.value
-
end
-
-
1
it 'should prioritise variables over an unknown array' do
-
1
variable1 = Porolog::Variable.new :x, goal1
-
-
1
assert_equal goal1[:x], variable1.value
-
-
1
variable1.values << Porolog::UNKNOWN_ARRAY
-
-
1
assert_equal Porolog::UNKNOWN_ARRAY, variable1.value
-
-
1
variable1.values << :variable1
-
1
variable1.values << :variable2
-
1
variable1.values << Porolog::UNKNOWN_ARRAY
-
-
1
assert_equal :variable1, variable1.value
-
end
-
-
1
it 'should unify a matching flathead and flattail pair' do
-
1
variable1 = Porolog::Variable.new :x, goal1
-
-
1
variable1.values << [1, 2, Porolog::UNKNOWN_TAIL]
-
1
variable1.values << [Porolog::UNKNOWN_TAIL, 3, 4]
-
-
1
assert_equal [1,2,3,4], variable1.value
-
end
-
-
1
it 'should unify a matching flattail and flathead pair' do
-
1
variable1 = Porolog::Variable.new :x, goal1
-
-
1
variable1.values << [Porolog::UNKNOWN_TAIL, 3, 4]
-
1
variable1.values << [1, 2, Porolog::UNKNOWN_TAIL]
-
-
1
assert_equal [1,2,3,4], variable1.value
-
end
-
-
1
it 'should return nil when the values are incompatible' do
-
1
variable1 = Porolog::Variable.new :x, goal1
-
-
1
variable1.values << [1, 3, 4]
-
1
variable1.values << [1, 2, Porolog::UNKNOWN_TAIL]
-
-
1
assert_nil variable1.value
-
end
-
-
1
it 'should unify a special case' do
-
1
variable1 = Porolog::Variable.new :x, goal1
-
-
1
variable1.values << [1,[3,4]]
-
1
variable1.values << goal1[:h]/goal1[:t]
-
-
1
assert_equal [1,[3,4]], variable1.value
-
end
-
-
end
-
-
1
describe '#instantiate' do
-
-
1
it 'should instantiate with another variable' do
-
1
variable1 = Porolog::Variable.new :x, goal1
-
1
variable2 = Porolog::Variable.new :y, goal2
-
-
1
variable1.instantiate variable2
-
-
1
assert_equal "Goal1.:x\n Goal2.:y", variable1.inspect_with_instantiations
-
1
assert_equal "Goal2.:y\n Goal1.:x", variable2.inspect_with_instantiations
-
end
-
-
1
it 'should instantiate with a value' do
-
1
variable1 = Porolog::Variable.new :x, goal1
-
1
variable2 = Porolog::Variable.new :y, goal2
-
1
value = Porolog::Value.new 'word', goal2
-
-
1
variable1.instantiate variable2
-
1
variable2.instantiate value
-
-
1
assert_equal 'word', variable1.value
-
1
assert_equal "Goal1.:x\n Goal2.:y\n Goal2.\"word\"", variable1.inspect_with_instantiations
-
1
assert_equal "Goal2.:y\n Goal1.:x\n Goal2.\"word\"", variable2.inspect_with_instantiations
-
end
-
-
1
it 'should instantiate with an indexed variable' do
-
# -- Elements --
-
1
variable1 = Porolog::Variable.new :x, goal1
-
1
variable2 = Porolog::Variable.new :y, goal2
-
1
value = Porolog::Value.new [7,11,13,23,29], goal2
-
-
# -- Make Instantiations --
-
#
-
# Goal1.:x == Goal2.y[2], Goal2.y == Goal2.[7,11,13,23,29]
-
#
-
1
variable1.instantiate variable2, 2
-
1
variable2.instantiate value
-
-
# -- Assert instantiations --
-
1
assert_equal 1, variable1.instantiations.size
-
1
assert_equal 2, variable2.instantiations.size
-
-
1
assert_equal 'Goal1.:x = Goal2.:y[2]', variable1.instantiations[0].inspect
-
1
assert_equal 'Goal1.:x = Goal2.:y[2]', variable2.instantiations[0].inspect
-
1
assert_equal 'Goal2.:y = Goal2.[7, 11, 13, 23, 29]', variable2.instantiations[1].inspect
-
-
1
assert_equal variable1.instantiations[0], variable2.instantiations[0]
-
-
1
assert_Instantiation variable1.instantiations[0], variable1, variable2, nil, 2
-
1
assert_Instantiation variable2.instantiations[0], variable1, variable2, nil, 2
-
1
assert_Instantiation variable2.instantiations[1], variable2, value, nil, nil
-
-
1
assert_Value variable2.instantiations[1].variable2, [7, 11, 13, 23, 29], goal2
-
-
# -- Assert Values --
-
1
assert_equal 13, variable1.value
-
1
assert_equal [7, 11, 13, 23, 29], variable2.value
-
-
# -- Assert inspects --
-
1
assert_equal "Goal1.:x\n Goal2.:y[2]\n Goal2.[7, 11, 13, 23, 29]", variable1.inspect_with_instantiations
-
1
assert_equal "Goal2.:y\n [2]Goal1.:x\n Goal2.[7, 11, 13, 23, 29]", variable2.inspect_with_instantiations
-
end
-
-
1
it 'should instantiate indexed with an indexed variable' do
-
# -- Elements --
-
1
variable1 = Porolog::Variable.new :x, goal1
-
1
variable2 = Porolog::Variable.new :y, goal2
-
1
variable3 = Porolog::Variable.new :z, goal2
-
1
value = Porolog::Value.new [[],[1,2,3],'word',[7,11,13,23,29]], goal2
-
-
1
assert_Value value, [[],[1,2,3],'word',[7,11,13,23,29]], goal2
-
-
# -- Assert goal variables --
-
1
assert_Goal_variables goal1, { m: nil, n: nil, x: nil }, [
-
'Goal1.:m',
-
'Goal1.:n',
-
'Goal1.:x',
-
].join("\n")
-
1
assert_Goal_variables goal2, { m: nil, n: nil, y: nil, z: nil }, [
-
'Goal2.:m',
-
'Goal2.:n',
-
'Goal2.:y',
-
'Goal2.:z',
-
].join("\n")
-
-
# -- Make instantiations --
-
#
-
# Goal1.:x == Goal2.:y[2], Goal2.:y == [[],[1,2,3],'word',[7,11,13,23,29]][3]
-
#
-
1
variable1.instantiate variable2, 2
-
-
# Goal2.:y == [[],[1,2,3],'word',[7,11,13,23,29]][3]
-
# == [7,11,13,23,29]
-
1
variable2.instantiate value, 3
-
-
# -- Assert Values --
-
1
assert_equal 13, variable1.value
-
1
assert_equal [7,11,13,23,29], variable2.value
-
1
assert_equal variable3, variable3.value
-
-
# -- Assert instantiations --
-
1
assert_equal 1, variable1.instantiations.size
-
1
assert_equal 2, variable2.instantiations.size
-
1
assert_equal 0, variable3.instantiations.size
-
-
1
assert_equal 'Goal1.:x = Goal2.:y[2]', variable1.instantiations[0].inspect
-
1
assert_equal 'Goal1.:x = Goal2.:y[2]', variable2.instantiations[0].inspect
-
1
assert_equal 'Goal2.:y = Goal2.[[], [1, 2, 3], "word", [7, 11, 13, 23, 29]][3]', variable2.instantiations[1].inspect
-
-
1
assert_equal variable1.instantiations[0], variable2.instantiations[0]
-
-
1
assert_Instantiation variable1.instantiations[0], variable1, variable2, nil, 2
-
1
assert_Instantiation variable2.instantiations[0], variable1, variable2, nil, 2
-
1
assert_Instantiation variable2.instantiations[1], variable2, value, nil, 3
-
-
1
assert_Value variable2.instantiations[1].variable2, [[],[1,2,3],'word',[7,11,13,23,29]], goal2
-
-
# -- Assert overall instantiations --
-
1
assert_equal "Goal2.:y\n [2]Goal1.:x\n Goal2.[[], [1, 2, 3], \"word\", [7, 11, 13, 23, 29]][3]", variable2.inspect_with_instantiations
-
1
assert_equal "Goal1.:x\n Goal2.:y[2]\n Goal2.[[], [1, 2, 3], \"word\", [7, 11, 13, 23, 29]][3]", variable1.inspect_with_instantiations
-
-
# -- Assert values --
-
1
assert_equal [], variable1.values
-
1
assert_equal 0, variable2.values.size
-
1
assert_equal [], variable3.values
-
-
1
assert_equal [7,11,13,23,29], variable2.value
-
1
assert_equal 13, variable1.value
-
1
assert_equal [7, 11, 13, 23, 29], variable2.value
-
-
1
assert_Goal_variables goal1, { m: nil, n: nil, x: 13 }, [
-
'Goal1.:m',
-
'Goal1.:n',
-
'Goal1.:x',
-
' Goal2.:y[2]',
-
' Goal2.[[], [1, 2, 3], "word", [7, 11, 13, 23, 29]][3]',
-
].join("\n")
-
-
1
assert_Goal_variables goal2, { m: nil, n: nil, y: [7, 11, 13, 23, 29], z: nil}, [
-
'Goal2.:m',
-
'Goal2.:n',
-
'Goal2.:y',
-
' [2]Goal1.:x',
-
' Goal2.[[], [1, 2, 3], "word", [7, 11, 13, 23, 29]][3]',
-
'Goal2.:z',
-
].join("\n")
-
-
1
assert_Goal_variables goal1, { m: nil, n: nil, x: 13 }, [
-
'Goal1.:m',
-
'Goal1.:n',
-
'Goal1.:x',
-
' Goal2.:y[2]',
-
' Goal2.[[], [1, 2, 3], "word", [7, 11, 13, 23, 29]][3]'
-
].join("\n")
-
-
1
assert_Goal_variables goal2, { m: nil, n: nil, y: [7, 11, 13, 23, 29], z: nil }, [
-
'Goal2.:m',
-
'Goal2.:n',
-
'Goal2.:y',
-
' [2]Goal1.:x',
-
' Goal2.[[], [1, 2, 3], "word", [7, 11, 13, 23, 29]][3]',
-
'Goal2.:z'
-
].join("\n")
-
end
-
-
1
it 'should instantiate with an indexed value' do
-
1
variable1 = Porolog::Variable.new :x, goal1
-
1
variable2 = Porolog::Variable.new :y, goal2
-
1
value = Porolog::Value.new [7,11,13,23,29], goal2
-
-
1
variable1.instantiate variable2
-
1
variable2.instantiate value, 3
-
-
1
assert_equal "Goal1.:x\n Goal2.:y\n Goal2.[7, 11, 13, 23, 29][3]", variable1.inspect_with_instantiations
-
1
assert_equal "Goal2.:y\n Goal1.:x\n Goal2.[7, 11, 13, 23, 29][3]", variable2.inspect_with_instantiations
-
end
-
-
1
it 'should instantiate with an indexed variable' do
-
1
variable1 = Porolog::Variable.new :x, goal1
-
1
variable2 = Porolog::Variable.new :y, goal2
-
1
value = Porolog::Value.new [7,11,13,23,29], goal2
-
-
1
variable1.instantiate variable2, 2
-
1
variable2.instantiate value
-
-
1
assert_equal 13, variable1.value
-
1
assert_equal "Goal1.:x\n Goal2.:y[2]\n Goal2.[7, 11, 13, 23, 29]", variable1.inspect_with_instantiations
-
1
assert_equal "Goal2.:y\n [2]Goal1.:x\n Goal2.[7, 11, 13, 23, 29]", variable2.inspect_with_instantiations
-
end
-
-
1
it 'should instantiate with an indexed value' do
-
1
variable1 = Porolog::Variable.new :x, goal1
-
1
variable2 = Porolog::Variable.new :y, goal2
-
1
value = Porolog::Value.new [7,11,13,23,29], goal2
-
-
1
variable1.instantiate variable2
-
1
variable2.instantiate value, 3
-
-
1
assert_equal "Goal1.:x\n Goal2.:y\n Goal2.[7, 11, 13, 23, 29][3]", variable1.inspect_with_instantiations
-
1
assert_equal "Goal2.:y\n Goal1.:x\n Goal2.[7, 11, 13, 23, 29][3]", variable2.inspect_with_instantiations
-
end
-
-
1
it 'should detect deep conflicting instantiations' do
-
# g1:a
-
# g2:b g3:c
-
# g4:d g5:e
-
# g6:f g7:g
-
# 8 9
-
1
reset
-
1
g1 = arguments1.goal
-
1
g2 = arguments1.goal
-
1
g3 = arguments1.goal
-
1
g4 = arguments1.goal
-
1
g5 = arguments1.goal
-
1
g6 = arguments1.goal
-
1
g7 = arguments1.goal
-
-
1
a = g1.variable :a
-
1
b = g2.variable :b
-
1
c = g3.variable :c
-
1
d = g4.variable :d
-
1
e = g5.variable :e
-
1
f = g6.variable :f
-
1
g = g7.variable :g
-
-
1
assert_instance_of Porolog::Instantiation, f.instantiate(g6.value(8))
-
1
assert_instance_of Porolog::Instantiation, g.instantiate(g7.value(9))
-
-
1
assert_instance_of Porolog::Instantiation, a.instantiate(b)
-
1
assert_instance_of Porolog::Instantiation, a.instantiate(c)
-
1
assert_instance_of Porolog::Instantiation, b.instantiate(d)
-
1
assert_instance_of Porolog::Instantiation, d.instantiate(f)
-
1
assert_instance_of Porolog::Instantiation, c.instantiate(e)
-
1
assert_nil e.instantiate(g)
-
-
1
assert_Goal_variables g1, { m: nil, n: nil, a: 8 }, [
-
'Goal1.:m',
-
'Goal1.:n',
-
'Goal1.:a',
-
' Goal2.:b',
-
' Goal4.:d',
-
' Goal6.:f',
-
' Goal6.8',
-
' Goal3.:c',
-
' Goal5.:e',
-
].join("\n")
-
1
assert_Goal_variables g2, { m: nil, n: nil, b: 8 }, [
-
'Goal2.:m',
-
'Goal2.:n',
-
'Goal2.:b',
-
' Goal1.:a',
-
' Goal3.:c',
-
' Goal5.:e',
-
' Goal4.:d',
-
' Goal6.:f',
-
' Goal6.8',
-
].join("\n")
-
1
assert_Goal_variables g3, { m: nil, n: nil, c: 8 }, [
-
'Goal3.:m',
-
'Goal3.:n',
-
'Goal3.:c',
-
' Goal1.:a',
-
' Goal2.:b',
-
' Goal4.:d',
-
' Goal6.:f',
-
' Goal6.8',
-
' Goal5.:e',
-
].join("\n")
-
1
assert_Goal_variables g4, { m: nil, n: nil, d: 8 }, [
-
'Goal4.:m',
-
'Goal4.:n',
-
'Goal4.:d',
-
' Goal2.:b',
-
' Goal1.:a',
-
' Goal3.:c',
-
' Goal5.:e',
-
' Goal6.:f',
-
' Goal6.8',
-
].join("\n")
-
1
assert_Goal_variables g5, { m: nil, n: nil, e: 8 }, [
-
'Goal5.:m',
-
'Goal5.:n',
-
'Goal5.:e',
-
' Goal3.:c',
-
' Goal1.:a',
-
' Goal2.:b',
-
' Goal4.:d',
-
' Goal6.:f',
-
' Goal6.8',
-
].join("\n")
-
1
assert_Goal_variables g6, { m: nil, n: nil, f: 8 }, [
-
'Goal6.:m',
-
'Goal6.:n',
-
'Goal6.:f',
-
' Goal6.8',
-
' Goal4.:d',
-
' Goal2.:b',
-
' Goal1.:a',
-
' Goal3.:c',
-
' Goal5.:e',
-
].join("\n")
-
1
assert_Goal_variables g7, { m: nil, n: nil, g: 9 }, [
-
'Goal7.:m',
-
'Goal7.:n',
-
'Goal7.:g',
-
' Goal7.9',
-
].join("\n")
-
end
-
-
1
it 'should return nil when the there are multiple different values' do
-
1
variable1 = Porolog::Variable.new :x, goal1
-
1
value = Porolog::Value.new 111, goal2
-
-
1
variable1.values << 112
-
-
1
instantiation = variable1.instantiate value
-
-
1
assert_nil instantiation, name
-
end
-
-
end
-
-
1
describe '#remove' do
-
-
4
let(:variable1) { Porolog::Variable.new :x, goal1 }
-
4
let(:variable2) { Porolog::Variable.new :y, goal2 }
-
4
let(:variable3) { Porolog::Variable.new :z, goal3 }
-
4
let(:value1) { Porolog::Value.new 'word', goal2 }
-
4
let(:value2) { Porolog::Value.new 'word', goal3 }
-
-
1
before do
-
3
variable1.instantiate variable2
-
3
variable1.instantiate variable3
-
3
variable2.instantiate value1
-
3
variable3.instantiate value2
-
-
3
assert_Goal_variables goal1, { m: nil, n: nil, x: 'word' }, [
-
'Goal1.:m',
-
'Goal1.:n',
-
'Goal1.:x',
-
' Goal2.:y',
-
' Goal2."word"',
-
' Goal3.:z',
-
' Goal3."word"',
-
].join("\n")
-
-
3
assert_Goal_variables goal2, { m: nil, n: nil, y: 'word' }, [
-
'Goal2.:m',
-
'Goal2.:n',
-
'Goal2.:y',
-
' Goal1.:x',
-
' Goal3.:z',
-
' Goal3."word"',
-
' Goal2."word"',
-
].join("\n")
-
-
3
assert_Goal_variables goal3, { m: nil, n: nil, z: 'word' }, [
-
'Goal3.:m',
-
'Goal3.:n',
-
'Goal3.:z',
-
' Goal1.:x',
-
' Goal2.:y',
-
' Goal2."word"',
-
' Goal3."word"',
-
].join("\n")
-
-
3
assert_equal 2, variable1.instantiations.size
-
3
assert_equal 2, variable2.instantiations.size
-
3
assert_equal 2, variable3.instantiations.size
-
-
3
assert_Instantiation variable1.instantiations[0], variable1, variable2, nil, nil
-
3
assert_Instantiation variable1.instantiations[1], variable1, variable3, nil, nil
-
3
assert_Instantiation variable2.instantiations[0], variable1, variable2, nil, nil
-
3
assert_Instantiation variable2.instantiations[1], variable2, value1, nil, nil
-
3
assert_Instantiation variable3.instantiations[0], variable1, variable3, nil, nil
-
3
assert_Instantiation variable3.instantiations[1], variable3, value2, nil, nil
-
-
3
assert_equal 'word', variable1.value
-
3
assert_equal 'word', variable2.value
-
3
assert_equal 'word', variable3.value
-
end
-
-
1
it 'should remove all instantiations case 1: remove variable1' do
-
-
1
variable1.remove
-
-
1
assert_Goal_variables goal1, { m: nil, n: nil, x: nil }, [
-
'Goal1.:m',
-
'Goal1.:n',
-
'Goal1.:x',
-
].join("\n")
-
-
1
assert_Goal_variables goal2, { m: nil, n: nil, y: 'word' }, [
-
'Goal2.:m',
-
'Goal2.:n',
-
'Goal2.:y',
-
' Goal2."word"',
-
].join("\n")
-
-
1
assert_Goal_variables goal3, { m: nil, n: nil, z: 'word' }, [
-
'Goal3.:m',
-
'Goal3.:n',
-
'Goal3.:z',
-
' Goal3."word"',
-
].join("\n")
-
-
1
assert_equal 0, variable1.instantiations.size
-
1
assert_equal 1, variable2.instantiations.size
-
1
assert_equal 1, variable3.instantiations.size
-
-
1
assert_nil variable1.instantiations[0]
-
1
assert_nil variable1.instantiations[1]
-
1
assert_nil variable2.instantiations[1]
-
1
assert_Instantiation variable2.instantiations[0], variable2, value1, nil, nil
-
1
assert_nil variable3.instantiations[1]
-
1
assert_Instantiation variable3.instantiations[0], variable3, value2, nil, nil
-
-
1
assert_equal variable1, variable1.value
-
1
assert_equal 'word', variable2.value
-
1
assert_equal 'word', variable3.value
-
end
-
-
1
it 'should remove all instantiations case 2: remove variable2' do
-
-
1
variable2.remove
-
-
1
assert_Goal_variables goal1, { m: nil, n: nil, x: 'word' }, [
-
'Goal1.:m',
-
'Goal1.:n',
-
'Goal1.:x',
-
' Goal3.:z',
-
' Goal3."word"',
-
].join("\n")
-
-
1
assert_Goal_variables goal2, { m: nil, n: nil, y: nil }, [
-
'Goal2.:m',
-
'Goal2.:n',
-
'Goal2.:y',
-
].join("\n")
-
-
1
assert_Goal_variables goal3, { m: nil, n: nil, z: 'word' }, [
-
'Goal3.:m',
-
'Goal3.:n',
-
'Goal3.:z',
-
' Goal1.:x',
-
' Goal3."word"',
-
].join("\n")
-
-
1
assert_equal 1, variable1.instantiations.size
-
1
assert_equal 0, variable2.instantiations.size
-
1
assert_equal 2, variable3.instantiations.size
-
-
1
assert_nil variable1.instantiations[1]
-
1
assert_Instantiation variable1.instantiations[0], variable1, variable3, nil, nil
-
1
assert_nil variable2.instantiations[0]
-
1
assert_nil variable2.instantiations[1]
-
1
assert_Instantiation variable3.instantiations[0], variable1, variable3, nil, nil
-
1
assert_Instantiation variable3.instantiations[1], variable3, value2, nil, nil
-
-
1
assert_equal 'word', variable1.value
-
1
assert_equal variable2, variable2.value
-
1
assert_equal 'word', variable3.value
-
end
-
-
1
it 'should remove all instantiations case 3: remove variable3' do
-
-
1
variable3.remove
-
-
1
assert_Goal_variables goal1, { m: nil, n: nil, x: 'word' }, [
-
'Goal1.:m',
-
'Goal1.:n',
-
'Goal1.:x',
-
' Goal2.:y',
-
' Goal2."word"',
-
].join("\n")
-
-
1
assert_Goal_variables goal2, { m: nil, n: nil, y: 'word' }, [
-
'Goal2.:m',
-
'Goal2.:n',
-
'Goal2.:y',
-
' Goal1.:x',
-
' Goal2."word"',
-
].join("\n")
-
-
1
assert_Goal_variables goal3, { m: nil, n: nil, z: nil }, [
-
'Goal3.:m',
-
'Goal3.:n',
-
'Goal3.:z',
-
].join("\n")
-
-
1
assert_equal 1, variable1.instantiations.size
-
1
assert_equal 2, variable2.instantiations.size
-
1
assert_equal 0, variable3.instantiations.size
-
-
1
assert_Instantiation variable1.instantiations[0], variable1, variable2, nil, nil
-
1
assert_nil variable1.instantiations[1]
-
1
assert_Instantiation variable2.instantiations[0], variable1, variable2, nil, nil
-
1
assert_Instantiation variable2.instantiations[1], variable2, value1, nil, nil
-
1
assert_nil variable3.instantiations[0]
-
1
assert_nil variable3.instantiations[1]
-
-
1
assert_equal 'word', variable1.value
-
1
assert_equal 'word', variable2.value
-
1
assert_equal variable3, variable3.value
-
end
-
-
end
-
-
1
describe '#uninstantiate' do
-
-
10
let(:variable1) { Porolog::Variable.new :x, goal1 }
-
10
let(:variable2) { Porolog::Variable.new :y, goal2 }
-
10
let(:variable3) { Porolog::Variable.new :z, goal3 }
-
10
let(:value1) { Porolog::Value.new 'word', goal2 }
-
10
let(:value2) { Porolog::Value.new 'word', goal3 }
-
-
1
before do
-
9
variable1.instantiate variable2
-
9
variable1.instantiate variable3
-
9
variable2.instantiate value1
-
9
variable3.instantiate value2
-
-
9
assert_Goal_variables goal1, { m: nil, n: nil, x: 'word' }, [
-
'Goal1.:m',
-
'Goal1.:n',
-
'Goal1.:x',
-
' Goal2.:y',
-
' Goal2."word"',
-
' Goal3.:z',
-
' Goal3."word"',
-
].join("\n")
-
-
9
assert_Goal_variables goal2, { m: nil, n: nil, y: 'word' }, [
-
'Goal2.:m',
-
'Goal2.:n',
-
'Goal2.:y',
-
' Goal1.:x',
-
' Goal3.:z',
-
' Goal3."word"',
-
' Goal2."word"',
-
].join("\n")
-
-
9
assert_Goal_variables goal3, { m: nil, n: nil, z: 'word' }, [
-
'Goal3.:m',
-
'Goal3.:n',
-
'Goal3.:z',
-
' Goal1.:x',
-
' Goal2.:y',
-
' Goal2."word"',
-
' Goal3."word"',
-
].join("\n")
-
-
9
assert_equal 2, variable1.instantiations.size
-
9
assert_equal 2, variable2.instantiations.size
-
9
assert_equal 2, variable3.instantiations.size
-
-
9
assert_Instantiation variable1.instantiations[0], variable1, variable2, nil, nil
-
9
assert_Instantiation variable1.instantiations[1], variable1, variable3, nil, nil
-
9
assert_Instantiation variable2.instantiations[0], variable1, variable2, nil, nil
-
9
assert_Instantiation variable2.instantiations[1], variable2, value1, nil, nil
-
9
assert_Instantiation variable3.instantiations[0], variable1, variable3, nil, nil
-
9
assert_Instantiation variable3.instantiations[1], variable3, value2, nil, nil
-
-
9
assert_equal 'word', variable1.value
-
9
assert_equal 'word', variable2.value
-
9
assert_equal 'word', variable3.value
-
end
-
-
1
it 'should remove all instantiations and values case 1' do
-
-
1
variable1.uninstantiate(goal1)
-
-
1
assert_Goal_variables goal1, { m: nil, n: nil, x: 'word' }, [
-
'Goal1.:m',
-
'Goal1.:n',
-
'Goal1.:x',
-
' Goal2.:y',
-
' Goal2."word"',
-
' Goal3.:z',
-
' Goal3."word"',
-
].join("\n")
-
-
1
assert_Goal_variables goal2, { m: nil, n: nil, y: 'word' }, [
-
'Goal2.:m',
-
'Goal2.:n',
-
'Goal2.:y',
-
' Goal1.:x',
-
' Goal3.:z',
-
' Goal3."word"',
-
' Goal2."word"',
-
].join("\n")
-
-
1
assert_Goal_variables goal3, { m: nil, n: nil, z: 'word' }, [
-
'Goal3.:m',
-
'Goal3.:n',
-
'Goal3.:z',
-
' Goal1.:x',
-
' Goal2.:y',
-
' Goal2."word"',
-
' Goal3."word"',
-
].join("\n")
-
-
1
assert_equal 2, variable1.instantiations.size
-
1
assert_equal 2, variable2.instantiations.size
-
1
assert_equal 2, variable3.instantiations.size
-
-
1
assert_Instantiation variable1.instantiations[0], variable1, variable2, nil, nil
-
1
assert_Instantiation variable1.instantiations[1], variable1, variable3, nil, nil
-
1
assert_Instantiation variable2.instantiations[0], variable1, variable2, nil, nil
-
1
assert_Instantiation variable2.instantiations[1], variable2, value1, nil, nil
-
1
assert_Instantiation variable3.instantiations[0], variable1, variable3, nil, nil
-
1
assert_Instantiation variable3.instantiations[1], variable3, value2, nil, nil
-
-
1
assert_equal 'word', variable1.value
-
1
assert_equal 'word', variable2.value
-
1
assert_equal 'word', variable3.value
-
end
-
-
1
it 'should remove all instantiations and values case 2' do
-
-
1
variable1.uninstantiate(goal2)
-
-
1
assert_Goal_variables goal1, { m: nil, n: nil, x: nil }, [
-
'Goal1.:m',
-
'Goal1.:n',
-
'Goal1.:x',
-
].join("\n")
-
-
1
assert_Goal_variables goal2, { m: nil, n: nil, y: 'word' }, [
-
'Goal2.:m',
-
'Goal2.:n',
-
'Goal2.:y',
-
' Goal2."word"',
-
].join("\n")
-
-
1
assert_Goal_variables goal3, { m: nil, n: nil, z: 'word' }, [
-
'Goal3.:m',
-
'Goal3.:n',
-
'Goal3.:z',
-
' Goal1.:x',
-
' Goal3."word"',
-
].join("\n")
-
-
1
assert_equal 0, variable1.instantiations.size
-
1
assert_equal 1, variable2.instantiations.size
-
1
assert_equal 2, variable3.instantiations.size
-
-
1
assert_nil variable1.instantiations[0]
-
1
assert_nil variable1.instantiations[1]
-
1
assert_nil variable2.instantiations[1]
-
1
assert_Instantiation variable2.instantiations[0], variable2, value1, nil, nil
-
1
assert_Instantiation variable3.instantiations[0], variable1, variable3, nil, nil
-
1
assert_Instantiation variable3.instantiations[1], variable3, value2, nil, nil
-
-
1
assert_equal variable1, variable1.value
-
1
assert_equal 'word', variable2.value
-
1
assert_equal 'word', variable3.value
-
end
-
-
1
it 'should remove all instantiations and values case 3' do
-
-
1
variable1.uninstantiate(goal3)
-
-
1
assert_Goal_variables goal1, { m: nil, n: nil, x: 'word' }, [
-
'Goal1.:m',
-
'Goal1.:n',
-
'Goal1.:x',
-
' Goal2.:y',
-
' Goal2."word"',
-
].join("\n")
-
-
1
assert_Goal_variables goal2, { m: nil, n: nil, y: 'word' }, [
-
'Goal2.:m',
-
'Goal2.:n',
-
'Goal2.:y',
-
' Goal1.:x',
-
' Goal2."word"',
-
].join("\n")
-
-
1
assert_Goal_variables goal3, { m: nil, n: nil, z: 'word' }, [
-
'Goal3.:m',
-
'Goal3.:n',
-
'Goal3.:z',
-
' Goal3."word"',
-
].join("\n")
-
-
1
assert_equal 1, variable1.instantiations.size
-
1
assert_equal 2, variable2.instantiations.size
-
1
assert_equal 1, variable3.instantiations.size
-
-
1
assert_Instantiation variable1.instantiations[0], variable1, variable2, nil, nil
-
1
assert_nil variable1.instantiations[1]
-
1
assert_Instantiation variable2.instantiations[0], variable1, variable2, nil, nil
-
1
assert_Instantiation variable2.instantiations[1], variable2, value1, nil, nil
-
1
assert_nil variable3.instantiations[1]
-
1
assert_Instantiation variable3.instantiations[0], variable3, value2, nil, nil
-
-
1
assert_equal 'word', variable1.value
-
1
assert_equal 'word', variable2.value
-
1
assert_equal 'word', variable3.value
-
end
-
-
1
it 'should remove all instantiations and values case 4' do
-
-
1
variable2.uninstantiate(goal1)
-
-
1
assert_Goal_variables goal1, { m: nil, n: nil, x: 'word' }, [
-
'Goal1.:m',
-
'Goal1.:n',
-
'Goal1.:x',
-
' Goal3.:z',
-
' Goal3."word"',
-
].join("\n")
-
-
1
assert_Goal_variables goal2, { m: nil, n: nil, y: nil }, [
-
'Goal2.:m',
-
'Goal2.:n',
-
'Goal2.:y',
-
].join("\n")
-
-
1
assert_Goal_variables goal3, { m: nil, n: nil, z: 'word' }, [
-
'Goal3.:m',
-
'Goal3.:n',
-
'Goal3.:z',
-
' Goal1.:x',
-
' Goal3."word"',
-
].join("\n")
-
-
1
assert_equal 1, variable1.instantiations.size
-
1
assert_equal 0, variable2.instantiations.size
-
1
assert_equal 2, variable3.instantiations.size
-
-
1
assert_nil variable1.instantiations[1]
-
1
assert_Instantiation variable1.instantiations[0], variable1, variable3, nil, nil
-
1
assert_nil variable2.instantiations[0]
-
1
assert_nil variable2.instantiations[1]
-
1
assert_Instantiation variable3.instantiations[0], variable1, variable3, nil, nil
-
1
assert_Instantiation variable3.instantiations[1], variable3, value2, nil, nil
-
-
1
assert_equal 'word', variable1.value
-
1
assert_equal variable2, variable2.value
-
1
assert_equal 'word', variable3.value
-
end
-
-
1
it 'should remove all instantiations and values case 5' do
-
-
1
variable2.uninstantiate(goal2)
-
-
1
assert_Goal_variables goal1, { m: nil, n: nil, x: 'word' }, [
-
'Goal1.:m',
-
'Goal1.:n',
-
'Goal1.:x',
-
' Goal2.:y',
-
' Goal3.:z',
-
' Goal3."word"',
-
].join("\n")
-
-
1
assert_Goal_variables goal2, { m: nil, n: nil, y: 'word' }, [
-
'Goal2.:m',
-
'Goal2.:n',
-
'Goal2.:y',
-
' Goal1.:x',
-
' Goal3.:z',
-
' Goal3."word"',
-
].join("\n")
-
-
1
assert_Goal_variables goal3, { m: nil, n: nil, z: 'word' }, [
-
'Goal3.:m',
-
'Goal3.:n',
-
'Goal3.:z',
-
' Goal1.:x',
-
' Goal2.:y',
-
' Goal3."word"',
-
].join("\n")
-
-
1
assert_equal 2, variable1.instantiations.size
-
1
assert_equal 1, variable2.instantiations.size
-
1
assert_equal 2, variable3.instantiations.size
-
-
1
assert_Instantiation variable1.instantiations[0], variable1, variable2, nil, nil
-
1
assert_Instantiation variable1.instantiations[1], variable1, variable3, nil, nil
-
1
assert_Instantiation variable2.instantiations[0], variable1, variable2, nil, nil
-
1
assert_nil variable2.instantiations[1]
-
1
assert_Instantiation variable3.instantiations[0], variable1, variable3, nil, nil
-
1
assert_Instantiation variable3.instantiations[1], variable3, value2, nil, nil
-
-
1
assert_equal 'word', variable1.value
-
1
assert_equal 'word', variable2.value
-
1
assert_equal 'word', variable3.value
-
end
-
-
1
it 'should remove all instantiations and values case 6' do
-
-
1
variable2.uninstantiate(goal3)
-
-
1
assert_Goal_variables goal1, { m: nil, n: nil, x: 'word' }, [
-
'Goal1.:m',
-
'Goal1.:n',
-
'Goal1.:x',
-
' Goal2.:y',
-
' Goal2."word"',
-
' Goal3.:z',
-
' Goal3."word"',
-
].join("\n")
-
-
1
assert_Goal_variables goal2, { m: nil, n: nil, y: 'word' }, [
-
'Goal2.:m',
-
'Goal2.:n',
-
'Goal2.:y',
-
' Goal1.:x',
-
' Goal3.:z',
-
' Goal3."word"',
-
' Goal2."word"',
-
].join("\n")
-
-
1
assert_Goal_variables goal3, { m: nil, n: nil, z: 'word' }, [
-
'Goal3.:m',
-
'Goal3.:n',
-
'Goal3.:z',
-
' Goal1.:x',
-
' Goal2.:y',
-
' Goal2."word"',
-
' Goal3."word"',
-
].join("\n")
-
-
1
assert_equal 2, variable1.instantiations.size
-
1
assert_equal 2, variable2.instantiations.size
-
1
assert_equal 2, variable3.instantiations.size
-
-
1
assert_Instantiation variable1.instantiations[0], variable1, variable2, nil, nil
-
1
assert_Instantiation variable1.instantiations[1], variable1, variable3, nil, nil
-
1
assert_Instantiation variable2.instantiations[0], variable1, variable2, nil, nil
-
1
assert_Instantiation variable2.instantiations[1], variable2, value1, nil, nil
-
1
assert_Instantiation variable3.instantiations[0], variable1, variable3, nil, nil
-
1
assert_Instantiation variable3.instantiations[1], variable3, value2, nil, nil
-
-
1
assert_equal 'word', variable1.value
-
1
assert_equal 'word', variable2.value
-
1
assert_equal 'word', variable3.value
-
end
-
-
1
it 'should remove all instantiations and values case 7' do
-
-
1
variable3.uninstantiate(goal1)
-
-
1
assert_Goal_variables goal1, { m: nil, n: nil, x: 'word' }, [
-
'Goal1.:m',
-
'Goal1.:n',
-
'Goal1.:x',
-
' Goal2.:y',
-
' Goal2."word"',
-
].join("\n")
-
-
1
assert_Goal_variables goal2, { m: nil, n: nil, y: 'word' }, [
-
'Goal2.:m',
-
'Goal2.:n',
-
'Goal2.:y',
-
' Goal1.:x',
-
' Goal2."word"',
-
].join("\n")
-
-
1
assert_Goal_variables goal3, { m: nil, n: nil, z: nil }, [
-
'Goal3.:m',
-
'Goal3.:n',
-
'Goal3.:z',
-
].join("\n")
-
-
1
assert_equal 1, variable1.instantiations.size
-
1
assert_equal 2, variable2.instantiations.size
-
1
assert_equal 0, variable3.instantiations.size
-
-
1
assert_Instantiation variable1.instantiations[0], variable1, variable2, nil, nil
-
1
assert_nil variable1.instantiations[1]
-
1
assert_Instantiation variable2.instantiations[0], variable1, variable2, nil, nil
-
1
assert_Instantiation variable2.instantiations[1], variable2, value1, nil, nil
-
1
assert_nil variable3.instantiations[0]
-
1
assert_nil variable3.instantiations[1]
-
-
1
assert_equal 'word', variable1.value
-
1
assert_equal 'word', variable2.value
-
1
assert_equal variable3, variable3.value
-
end
-
-
1
it 'should remove all instantiations and values case 8' do
-
-
1
variable3.uninstantiate(goal2)
-
-
1
assert_Goal_variables goal1, { m: nil, n: nil, x: 'word' }, [
-
'Goal1.:m',
-
'Goal1.:n',
-
'Goal1.:x',
-
' Goal2.:y',
-
' Goal2."word"',
-
' Goal3.:z',
-
' Goal3."word"',
-
].join("\n")
-
-
1
assert_Goal_variables goal2, { m: nil, n: nil, y: 'word' }, [
-
'Goal2.:m',
-
'Goal2.:n',
-
'Goal2.:y',
-
' Goal1.:x',
-
' Goal3.:z',
-
' Goal3."word"',
-
' Goal2."word"',
-
].join("\n")
-
-
1
assert_Goal_variables goal3, { m: nil, n: nil, z: 'word' }, [
-
'Goal3.:m',
-
'Goal3.:n',
-
'Goal3.:z',
-
' Goal1.:x',
-
' Goal2.:y',
-
' Goal2."word"',
-
' Goal3."word"',
-
].join("\n")
-
-
1
assert_equal 2, variable1.instantiations.size
-
1
assert_equal 2, variable2.instantiations.size
-
1
assert_equal 2, variable3.instantiations.size
-
-
1
assert_Instantiation variable1.instantiations[0], variable1, variable2, nil, nil
-
1
assert_Instantiation variable1.instantiations[1], variable1, variable3, nil, nil
-
1
assert_Instantiation variable2.instantiations[0], variable1, variable2, nil, nil
-
1
assert_Instantiation variable2.instantiations[1], variable2, value1, nil, nil
-
1
assert_Instantiation variable3.instantiations[0], variable1, variable3, nil, nil
-
1
assert_Instantiation variable3.instantiations[1], variable3, value2, nil, nil
-
-
1
assert_equal 'word', variable1.value
-
1
assert_equal 'word', variable2.value
-
1
assert_equal 'word', variable3.value
-
end
-
-
1
it 'should remove all instantiations and values case 9' do
-
-
1
variable3.uninstantiate(goal3)
-
-
1
assert_Goal_variables goal1, { m: nil, n: nil, x: 'word' }, [
-
'Goal1.:m',
-
'Goal1.:n',
-
'Goal1.:x',
-
' Goal2.:y',
-
' Goal2."word"',
-
' Goal3.:z',
-
].join("\n")
-
-
1
assert_Goal_variables goal2, { m: nil, n: nil, y: 'word' }, [
-
'Goal2.:m',
-
'Goal2.:n',
-
'Goal2.:y',
-
' Goal1.:x',
-
' Goal3.:z',
-
' Goal2."word"',
-
].join("\n")
-
-
1
assert_Goal_variables goal3, { m: nil, n: nil, z: 'word' }, [
-
'Goal3.:m',
-
'Goal3.:n',
-
'Goal3.:z',
-
' Goal1.:x',
-
' Goal2.:y',
-
' Goal2."word"',
-
].join("\n")
-
-
1
assert_equal 2, variable1.instantiations.size
-
1
assert_equal 2, variable2.instantiations.size
-
1
assert_equal 1, variable3.instantiations.size
-
-
1
assert_Instantiation variable1.instantiations[0], variable1, variable2, nil, nil
-
1
assert_Instantiation variable1.instantiations[1], variable1, variable3, nil, nil
-
1
assert_Instantiation variable2.instantiations[0], variable1, variable2, nil, nil
-
1
assert_Instantiation variable2.instantiations[1], variable2, value1, nil, nil
-
1
assert_Instantiation variable3.instantiations[0], variable1, variable3, nil, nil
-
1
assert_nil variable3.instantiations[1]
-
-
1
assert_equal 'word', variable1.value
-
1
assert_equal 'word', variable2.value
-
1
assert_equal 'word', variable3.value
-
end
-
-
end
-
-
1
describe '#[]' do
-
-
1
it 'should return the element at the specified index' do
-
1
variable = Porolog::Variable.new :x, goal1
-
1
value = Porolog::Value.new [3,5,7,11,13,17], goal2
-
-
1
variable.instantiate value
-
-
1
assert_equal [3,5,7,11,13,17], variable.value
-
1
assert_equal 13, variable[4]
-
end
-
-
1
it 'should return nil for an out of range index' do
-
1
variable = Porolog::Variable.new :x, goal1
-
1
value = Porolog::Value.new [3,5,7,11,13,17], goal2
-
-
1
variable.instantiate value
-
-
1
assert_equal [3,5,7,11,13,17], variable.value
-
1
assert_nil variable[200]
-
end
-
-
1
it 'should return nil when the value is not an array' do
-
1
variable = Porolog::Variable.new :x, goal1
-
1
value = Porolog::Value.new 12, goal2
-
-
1
variable.instantiate value
-
-
1
assert_equal 12, variable.value
-
1
assert_nil variable[0]
-
end
-
-
1
it 'should return the head' do
-
1
variable = Porolog::Variable.new :x, goal1
-
1
value = Porolog::Value.new [3,5,7,11,13,17], goal2
-
-
1
variable.instantiate value
-
-
1
assert_equal [3,5,7,11,13,17], variable.value
-
1
assert_equal 3, variable[:head]
-
end
-
-
1
it 'should return the tail' do
-
1
variable = Porolog::Variable.new :x, goal1
-
1
value = Porolog::Value.new [3,5,7,11,13,17], goal2
-
-
1
variable.instantiate value
-
-
1
assert_equal [3,5,7,11,13,17], variable.value
-
1
assert_equal [5,7,11,13,17], variable[:tail]
-
end
-
-
end
-
-
1
describe '#variables' do
-
-
1
it 'should return an Array of itself' do
-
1
variable = Porolog::Variable.new :x, goal1
-
-
1
assert_equal [variable], variable.variables
-
end
-
-
end
-
-
end
-
-
end
-
#
-
# test/samples_test.rb - Test Suite for Porolog::Predicate
-
#
-
# Luis Esteban 13 July 2020
-
# created
-
#
-
-
1
require_relative 'test_helper'
-
-
1
describe 'Porolog' do
-
-
1
before(:all) do
-
6
reset
-
end
-
-
1
it 'implements delete first predicate' do
-
1
Porolog::predicate :delete
-
-
1
delete(:X, [:X]/:T, :T).fact!
-
-
1
assert_solutions delete(9, [1,2,3,4], [2,3,4]), []
-
1
assert_solutions delete(1, [1,2,3,4], [2,3,4]), [{}]
-
-
1
assert_solutions delete(:Removed, [1,2,3,4], [2,3,4]), [{ Removed: 1 }]
-
1
assert_solutions delete(1, :Original, [2,3,4]), [{ Original: [1,2,3,4] }]
-
1
assert_solutions delete(1, [1,2,3,4], :Result), [{ Result: [2,3,4] }]
-
1
assert_solutions delete(1, [:First,2,3,4], [2,3,4]), [{ First: 1 }]
-
1
assert_solutions delete(1, [1,:Second,3,4], [2,3,4]), [{ Second: 2 }]
-
1
assert_solutions delete(1, [1,2,:Third,4], [2,3,4]), [{ Third: 3 }]
-
1
assert_solutions delete(1, [1,2,3,4], [:Second,3,4]), [{ Second: 2 }]
-
1
assert_solutions delete(1, [1,2,3,4], [:A, :B, :C]), [{ A: 2, B: 3, C: 4 }]
-
1
assert_solutions delete(1, [:A,2,3,:D], [:B,3,4]), [{ A: 1, B: 2, D: 4 }]
-
end
-
-
1
it 'implements the delete predicate' do
-
1
Porolog::builtin :write
-
1
Porolog::predicate :delete
-
-
1
delete(:X, [:X]/:T, :T).fact!
-
1
delete(:X, [:H]/:T, [:H]/:NT) << [
-
delete(:X, :T, :NT),
-
]
-
-
1
assert_solutions delete(1, [1,2,3,4], [1,2,4]), [], goals: 9
-
1
assert_solutions delete(3, [1,2,3,4], [1,2,4]), [{}]
-
1
assert_solutions delete(4, [1,2,3,4], [1,2,3]), [{}]
-
1
assert_solutions delete(4, [1,2,3,4], [1,2,:X]), [{ X: 3 }]
-
-
1
assert_solutions delete(:Removed, [1,2,3,4], [1,2,4]), [
-
{ Removed: 3 }
-
]
-
-
1
assert_solutions delete(:Removed, [1,2,3,4], [1,2,3]), [
-
{ Removed: 4 }
-
]
-
-
1
assert_solutions delete(3, :Original, [1,2,4]), [
-
{ Original: [3, 1, 2, 4] },
-
{ Original: [1, 3, 2, 4] },
-
{ Original: [1, 2, 3, 4] },
-
{ Original: [1, 2, 4, 3] },
-
]
-
-
1
assert_solutions delete(:X, [1,2,3,4], :L), [
-
{ X: 1, L: [2, 3, 4] },
-
{ X: 2, L: [1, 3, 4] },
-
{ X: 3, L: [1, 2, 4] },
-
{ X: 4, L: [1, 2, 3] },
-
]
-
-
1
assert_solutions delete(:X, [1,2,3,4], [:A,:B,:C]), [
-
{ X: 1, A: 2, B: 3, C: 4 },
-
{ X: 2, A: 1, B: 3, C: 4 },
-
{ X: 3, A: 1, B: 2, C: 4 },
-
{ X: 4, A: 1, B: 2, C: 3 },
-
]
-
end
-
-
1
it 'implements the permutation predicate' do
-
1
warn name
-
1
Porolog::predicate :delete, :permutation
-
-
1
permutation([],[]).fact!
-
1
permutation(:List, [:H]/:Permutation) << [
-
delete(:H, :List, :Rest),
-
permutation(:Rest, :Permutation)
-
]
-
-
1
delete(:X, [:X]/:T, :T).fact!
-
1
delete(:X, [:H]/:T, [:H]/:NT) << [
-
delete(:X, :T, :NT),
-
]
-
-
1
assert_solutions permutation([1,2,3,4], [1,2,3,4]), [{}]
-
1
assert_solutions permutation([3,2,1,4], [1,2,3,4]), [{}]
-
1
assert_solutions permutation([3,2,:A,4], [1,2,3,4]), [
-
{ A: 1 }
-
]
-
1
assert_solutions permutation([3,2,:A,:B], [1,2,3,4]), [
-
{ A: 1, B: 4 },
-
{ A: 4, B: 1 },
-
]
-
1
assert_solutions permutation([3,2,:A,4], [1,2,:C,4]), [
-
{ A: 1, C: 3 }
-
]
-
1
assert_solutions permutation([2,3,1], :L), [
-
{ L: [2, 3, 1] },
-
{ L: [2, 1, 3] },
-
{ L: [3, 2, 1] },
-
{ L: [3, 1, 2] },
-
{ L: [1, 2, 3] },
-
{ L: [1, 3, 2] }
-
]
-
end
-
-
1
it 'solves the Einstein / Zebra riddle' do
-
1
warn name
-
1
Porolog::builtin :is, :member, :is_noteq
-
1
Porolog::predicate :same, :not_same, :left, :beside, :houses
-
-
1
same(:X,:X).fact!
-
-
1
not_same(:X,:X).cut_fallacy!
-
1
not_same(:X,:Y).fact!
-
-
1
(1..5).to_a.each_cons(2) do |left, right|
-
4
left(left,right).fact!
-
4
beside(left,right).fact!
-
4
beside(right,left).fact!
-
end
-
-
1
HOUSES = [1, 2, 3, 4, 5 ]
-
-
1
PEOPLE = [:Brit, :Dane, :Swede, :German, :Norwegian]
-
1
SMOKES = [:Blend, :Pallmall, :Dunhill, :Winfield, :Rothmans ]
-
1
PETS = [:Dog, :Cats, :Fish, :Birds, :Horses ]
-
1
COLOURS = [:Red, :Blue, :Green, :White, :Yellow ]
-
1
DRINKS = [:Tea, :Beer, :Milk, :Water, :Coffee ]
-
-
1
FISH_INDEX = PETS.index(:Fish)
-
-
1
houses(:People, :Smokes, :Pets, :Colours, :Drinks) << [
-
same(:Milk, 3), # 8. The man living in the house right in the center drinks milk.
-
same(:Norwegian, 1), # 9. The Norwegian lives in the first house.
-
left(:Green, :White), # 4. The green house is on the left of the white house.
-
beside(:Norwegian, :Blue), # 14. The Norwegian lives next to the blue house.
-
not_same(:Blue, :White),
-
beside(:Blend, :Water), # 15. The man who smokes Blend has a neighbour who drinks water.
-
not_same(:Milk, :Water),
-
beside(:Horses, :Dunhill), # 11. The man who keeps horses lives next to the man who smokes Dunhill.
-
same(:Yellow, :Dunhill), # 7. The owner of the yellow house smokes Dunhill.
-
same(:Green, :Coffee), # 5. The green house owner drinks coffee.
-
not_same(:Milk, :Coffee),
-
not_same(:Water, :Coffee),
-
is_noteq(:Red, HOUSES, :Green, :White, :Blue, :Yellow),
-
same(:Brit, :Red), # 1. The Brit lives in a red house.
-
not_same(:Brit, :Norwegian),
-
beside(:Blend, :Cats), # 10. The man who smokes Blend lives next to the one who keeps cats.
-
not_same(:Horses, :Cats),
-
is_noteq(:Tea, HOUSES, :Coffee, :Milk, :Water),
-
same(:Dane, :Tea), # 3. The Dane drinks tea.
-
is_noteq(:Beer, HOUSES, :Tea, :Coffee, :Milk, :Water),
-
same(:Winfield, :Beer), # 12. The owner who smokes Winfield drinks beer.
-
is_noteq(:German, HOUSES, :Norwegian, :Dane, :Brit),
-
is_noteq(:Swede, HOUSES, :German, :Norwegian, :Dane, :Brit),
-
same(:Swede, :Dog), # 2. The Swede keeps a dog.
-
is_noteq(:Pallmall, HOUSES, :Dunhill, :Blend, :Winfield),
-
is_noteq(:Rothmans, HOUSES, :Pallmall, :Dunhill, :Blend, :Winfield),
-
same(:Pallmall, :Birds), # 6. The person who smokes Pall Mall keeps birds.
-
same(:German, :Rothmans), # 13. The German smokes Rothmans.
-
is_noteq(:Fish, HOUSES, :Dog, :Birds, :Cats, :Horses),
-
-
same(:People, PEOPLE),
-
same(:Smokes, SMOKES),
-
same(:Pets, PETS),
-
same(:Colours, COLOURS),
-
same(:Drinks, DRINKS),
-
]
-
-
1
solutions = assert_solutions houses(:People, :Smokes, :Pets, :Colours, :Drinks), [
-
{
-
People: [3, 2, 5, 4, 1],
-
Smokes: [2, 3, 1, 5, 4],
-
Pets: [5, 1, 4, 3, 2],
-
Colours: [3, 2, 4, 5, 1],
-
Drinks: [2, 5, 3, 1, 4]
-
}
-
], goals: 1873
-
-
1
solution = solutions.first
-
-
1
assert_equal :German, PEOPLE[solution[:People].index(solution[:Pets][FISH_INDEX])]
-
-
1
def house_details(solution, h)
-
[
-
5
PEOPLE[solution[:People].index(h)],
-
SMOKES[solution[:Smokes].index(h)],
-
PETS[solution[:Pets].index(h)],
-
COLOURS[solution[:Colours].index(h)],
-
DRINKS[solution[:Drinks].index(h)]
-
]
-
end
-
-
1
assert_equal [:Norwegian, :Dunhill, :Cats, :Yellow, :Water ], house_details(solution, 1)
-
1
assert_equal [:Dane, :Blend, :Horses, :Blue, :Tea ], house_details(solution, 2)
-
1
assert_equal [:Brit, :Pallmall, :Birds, :Red, :Milk ], house_details(solution, 3)
-
1
assert_equal [:German, :Rothmans, :Fish, :Green, :Coffee], house_details(solution, 4)
-
1
assert_equal [:Swede, :Winfield, :Dog, :White, :Beer ], house_details(solution, 5)
-
end
-
-
1
it 'instantiates lists using member and length builtin predicates' do
-
1
Porolog::builtin :member, :length
-
1
Porolog::predicate :lists
-
-
1
lists(:L) << [
-
member(:N,[1,2,3,4,5]),
-
length(:L, :N),
-
member(:N, :L)
-
]
-
-
1
assert_solutions lists(:L), [
-
{ L: [1] },
-
{ L: [2, nil] },
-
{ L: [nil, 2] },
-
{ L: [3, nil, nil] },
-
{ L: [nil, 3, nil] },
-
{ L: [nil, nil, 3] },
-
{ L: [4, nil, nil, nil] },
-
{ L: [nil, 4, nil, nil] },
-
{ L: [nil, nil, 4, nil] },
-
{ L: [nil, nil, nil, 4] },
-
{ L: [5, nil, nil, nil, nil] },
-
{ L: [nil, 5, nil, nil, nil] },
-
{ L: [nil, nil, 5, nil, nil] },
-
{ L: [nil, nil, nil, 5, nil] },
-
{ L: [nil, nil, nil, nil, 5] },
-
], goals: 13
-
end
-
-
1
it 'implements a prime number search' do
-
1
Porolog::builtin :gtr, :is, :noteq, :between
-
1
Porolog::predicate :prime, :search_prime
-
-
1
prime(2).fact!
-
1
prime(3).fact!
-
1
prime(:X) << [
-
between(:X, 4, 20000),
-
226
is(:X_mod_2, :X){|x| x % 2 },
-
noteq(:X_mod_2, 0),
-
search_prime(:X, 3),
-
]
-
-
1
search_prime(:X, :N) << [
-
352
is(:N_squared, :N){|n| n ** 2 },
-
gtr(:N_squared, :X),
-
:CUT
-
]
-
-
1
search_prime(:X, :N) << [
-
304
is(:X_mod_N, :X, :N){|x,n| x % n },
-
noteq(:X_mod_N, 0),
-
239
is(:M, :N){|n| n + 2 },
-
:CUT,
-
search_prime(:X, :M),
-
]
-
-
1
known_primes = [
-
2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97,
-
101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199,
-
211, 223, 227, 229
-
]
-
-
1
assert_equal known_primes, prime(:number).solve_for(:number, max_solutions: 50)
-
1
assert_equal 3016, Porolog::Goal.goal_count
-
end
-
-
end