-
#
-
# lib/porolog.rb - Plain Old Ruby Objects Prolog Engine
-
#
-
# Luis Esteban 2 May 2018
-
# created
-
#
-
-
1
module Porolog
-
-
# The most recent version of the Porolog gem.
-
1
VERSION = '0.0.5'
-
# The most recent date of when the VERSION changed.
-
1
VERSION_DATE = '2019-04-23'
-
-
# 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.
-
# @return [Porolog::Predicate] Predicate created if only one name is provided
-
# @return [Array<Porolog::Predicate>] Predicates created if multiple names are provided
-
1
def predicate(*names)
-
6
names = [names].flatten
-
-
6
predicates = names.map{|name|
-
9
method = name.to_sym
-
9
predicate = Predicate.new(name)
-
7
Object.class_eval{
-
7
define_method(method){|*args|
-
3
predicate.(*args)
-
}
-
}
-
7
predicate
-
}
-
-
4
predicates.size > 1 && predicates || predicates.first
-
end
-
-
end
-
-
1
require_relative 'porolog/error'
-
1
require_relative 'porolog/scope'
-
1
require_relative 'porolog/predicate'
-
1
require_relative 'porolog/arguments'
-
1
require_relative 'porolog/rule'
-
#
-
# 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
-
-
# Unregisters all Arguments
-
# @return [true]
-
1
def self.reset
-
86
@@arguments = []
-
86
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)
-
53
@predicate = predicate
-
53
@arguments = arguments
-
53
@@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
-
28
"#{@predicate.name}(#{@arguments && @arguments.map(&:inspect).join(',')})"
-
end
-
-
# Creates a fact rule that states that these arguments satisfy the Predicate.
-
# @return [Porolog::Arguments] the Arguments
-
1
def fact!
-
2
@predicate << Rule.new(self, true)
-
2
self
-
end
-
-
# Creates a fact rule that states that these arguments do not satisfy the Predicate.
-
# @return [Porolog::Arguments] the Arguments
-
1
def falicy!
-
2
@predicate << Rule.new(self, false)
-
2
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!
-
1
@predicate << Rule.new(self, [:CUT, true])
-
1
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_falicy!
-
1
@predicate << Rule.new(self, [:CUT, false])
-
1
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)
-
4
@predicate << Rule.new(self, *definition)
-
4
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
-
@arguments.map{|argument|
-
argument.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)
-
Goal.new(self, calling_goal)
-
end
-
-
# Returns memoized solutions
-
# @param number [Integer] the maximum number of solutions to find (nil means find all)
-
# @return [Array<Hash>] the solutions found (memoized)
-
1
def solutions(number = nil)
-
@solutions ||= solve(number)
-
@solutions
-
end
-
-
# Solves the Arguments
-
# @param number_of_solutions [Integer] the maximum number of solutions to find (nil means find all)
-
# @return [Array<Hash>] the solutions found
-
1
def solve(number_of_solutions = nil)
-
@solutions = goal.solve(number_of_solutions)
-
end
-
-
# Extracts solution values.
-
# @param variables [Symbol, Array<Symbol>] variable or variables
-
# @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)
-
variables = [*variables]
-
solutions.map{|solution|
-
variables.map{|variable| solution[variable] }
-
}
-
end
-
-
# @return [Boolean] whether any solutions were found
-
1
def valid?
-
!solutions.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)
-
arguments_dup = arguments.dup
-
(0...arguments_dup.size).each do |i|
-
arguments_dup[i] = arguments_dup[i].dup(goal) if arguments_dup[i].is_a?(HeadTail)
-
end
-
self.class.new @predicate, arguments_dup
-
end
-
-
# @param other [Porolog::Arguments] arguments for comparison
-
# @return [Boolean] whether the Arguments match
-
1
def ==(other)
-
@predicate == other.predicate &&
-
16
@arguments == other.arguments
-
end
-
-
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.
-
1
class PorologError < RuntimeError ; 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 PredicateError < PorologError ; end
-
# Error class indicating an error with the name of a Predicate.
-
1
class NameError < PredicateError ; end
-
-
1
attr_reader :name, :rules
-
-
# 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)
-
263
if scope_name
-
92
@@current_scope = Scope[scope_name] || Scope.new(scope_name)
-
else
-
171
@@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
if scope_name
-
4
@@current_scope = Scope[scope_name] || Scope.new(scope_name)
-
end
-
4
@@current_scope
-
end
-
-
# Resets the current scope to default and deregisters builtin predicates.
-
# @return [Porolog::Scope] the current Scope.
-
1
def self.reset
-
85
scope(:default)
-
85
@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)
-
1
@@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)
-
82
@name = name.to_sym
-
82
@rules = []
-
-
82
raise NameError.new("Cannot name a predicate 'predicate'") if @name == :predicate
-
-
78
scope = Scope[scope_name] || Scope.new(scope_name)
-
78
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.
-
# @return [Porolog::Predicate] a new or existing Predicate.
-
1
def self.new(*args)
-
85
name, _ = *args
-
85
scope[name.to_sym] || super
-
end
-
-
# Create Arguments for the Predicate.
-
# Provides the syntax options:
-
# * p.(x,y,z)
-
# @return [Porolog::Arguments] Arguments of the Predicate with the given arguments.
-
1
def call(*args)
-
25
Arguments.new(self,args)
-
end
-
-
# Create Arguments for the Predicate.
-
1
def arguments(*args)
-
1
Arguments.new(self,args)
-
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)
-
11
@rules << rule
-
11
self
-
end
-
-
# A pretty print String of the Predicate.
-
# @return [String] the Predicate as a String.
-
1
def inspect
-
5
lines = []
-
-
5
if @rules.size == 1
-
3
lines << "#{@name}:-#{@rules.first.inspect}"
-
else
-
2
lines << "#{@name}:-"
-
2
@rules.each do |rule|
-
2
lines << rule.inspect
-
end
-
end
-
-
5
lines.join("\n")
-
end
-
-
# Return a builtin Predicate based on its key.
-
# @param key [Symbol] the name (or otherwise object) used to register a scope.
-
# @return [Porolog::Predicate] a Predicate with the next id based on the key.
-
1
def self.builtin(key)
-
4
@builtin_predicate_ids[key] ||= 0
-
4
@builtin_predicate_ids[key] += 1
-
-
4
self.new("_#{key}_#{@builtin_predicate_ids[key]}")
-
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
-
-
1
attr_reader :arguments, :definition
-
-
# Clears all Rules.
-
# @return [true]
-
1
def self.reset
-
85
@@rules = []
-
85
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)
-
21
@arguments = arguments
-
21
@definition = definition
-
21
@@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
-
-
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 ScopeError < PorologError ; end
-
# Error class indicating a non-Predicate was assigned to a Scope.
-
1
class NotPredicateError < ScopeError ; 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)
-
121
@@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)
-
118
@name = name
-
118
@predicates = {}
-
-
118
@@scopes[@name] = self
-
end
-
-
# Clears all scopes and re-creates the default Scope.
-
# @return [Porolog::Scope] the default Scope.
-
1
def self.reset
-
85
@@scopes = {}
-
85
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)
-
225
@@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
-
16
@@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)
-
90
@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)
-
80
raise NotPredicateError.new("#{predicate.inspect} is not a Predicate") unless predicate.is_a?(Predicate)
-
79
@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
-
38
@predicates.values
-
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
-
4
reset
-
end
-
-
1
describe '#predicate' do
-
-
1
it 'should create a Predicate' do
-
1
assert_equal 0, Scope[:default].predicates.size
-
-
1
single_predicate = predicate :single
-
-
1
assert_equal 1, Scope[:default].predicates.size
-
1
assert_equal :single, 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
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, Scope[:default].predicates.size
-
-
1
multiple_predicates = predicate :alpha, :beta, :gamma
-
-
1
assert_equal 3, Scope[:default].predicates.size
-
1
assert_equal :alpha, Scope[:default].predicates[0].name
-
1
assert_equal :beta, Scope[:default].predicates[1].name
-
1
assert_equal :gamma, 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
predicate :epsilon, :upsilon
-
-
1
assert respond_to?(:epsilon)
-
1
assert_Arguments epsilon(), :epsilon, []
-
1
assert respond_to?(:upsilon)
-
1
assert_Arguments upsilon([]), :upsilon, [[]]
-
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
-
20
reset
-
end
-
-
1
describe 'Predicate' do
-
-
1
it 'should default to creating predicates in the default scope' do
-
1
assert_equal :default, Predicate.scope.name
-
-
1
test = Predicate.new 'test'
-
-
1
assert_includes Scope[:default].predicates, test
-
end
-
-
1
describe '.scope' do
-
-
1
it 'should return the current scope for new predicates' do
-
1
assert_equal Scope[:default], Predicate.scope
-
-
1
alpha = Predicate.new 'alpha', :new_scope
-
-
1
assert_equal Scope[:default], Predicate.scope
-
-
1
Predicate.scope :new_scope
-
-
1
assert_equal Scope[:new_scope], Predicate.scope
-
-
1
bravo = Predicate.new 'bravo'
-
-
1
assert_equal [], Scope[:default ].predicates
-
1
assert_equal [alpha,bravo], Scope[:new_scope].predicates
-
end
-
-
1
it 'should allow predicate scope to be created and set' do
-
1
Predicate.scope :internal
-
1
assert_equal :internal, Predicate.scope.name
-
-
1
Predicate.new 'alpha'
-
-
1
Predicate.scope :external
-
1
assert_equal :external, Predicate.scope.name
-
-
1
Predicate.new 'bravo'
-
-
1
Predicate.scope :internal
-
1
assert_equal :internal, Predicate.scope.name
-
-
1
Predicate.new 'carly'
-
1
Predicate.new 'delta', :external
-
-
1
assert_equal :internal, Predicate.scope.name
-
-
1
assert_equal [:alpha,:carly], Scope[:internal].predicates.map(&:name)
-
1
assert_equal [:bravo,:delta], Scope[:external].predicates.map(&:name)
-
end
-
-
end
-
-
1
describe '.scope=' do
-
-
1
it 'should also allow predicate scope to be assigned' do
-
1
Predicate.scope = :internal
-
1
assert_equal :internal, Predicate.scope.name
-
-
1
alpha = Predicate.new 'alpha'
-
-
1
Predicate.scope = :external
-
1
assert_equal :external, Predicate.scope.name
-
-
1
bravo = Predicate.new 'bravo'
-
-
1
Predicate.scope = :internal
-
1
assert_equal :internal, Predicate.scope.name
-
-
1
carly = Predicate.new 'carly'
-
1
delta = Predicate.new 'delta', :external
-
-
1
assert_equal :internal, Predicate.scope.name
-
-
1
assert_equal [alpha,carly], Scope[:internal].predicates
-
1
assert_equal [bravo,delta], Scope[:external].predicates
-
end
-
-
end
-
-
1
describe '.reset' do
-
-
1
it 'should revert to the default scope when reset' do
-
1
Predicate.scope = :other
-
1
assert_equal :other, Predicate.scope.name
-
-
1
Predicate.reset
-
1
assert_equal :default, Predicate.scope.name
-
-
1
Predicate.scope :temporary
-
-
1
alpha = Predicate.new :alpha
-
-
1
assert_equal :temporary, Predicate.scope.name
-
1
assert_equal [alpha], Predicate.scope.predicates
-
-
1
Predicate.reset
-
-
1
bravo = Predicate.new :bravo
-
-
1
assert_equal :default, Predicate.scope.name
-
1
assert_equal [bravo], Predicate.scope.predicates
-
end
-
-
end
-
-
1
describe '.[]' do
-
-
1
it 'should return a predicate by name' do
-
1
alpha = Predicate.new :alpha
-
-
1
assert_equal alpha, Predicate[:alpha]
-
end
-
-
end
-
-
1
describe '.new' do
-
-
1
it 'should create a new predicate' do
-
1
alpha = Predicate.new :alpha
-
-
1
assert_instance_of 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 = Predicate.new 'predicate1', :left
-
1
predicate2 = Predicate.new 'predicate1', :right
-
-
1
refute_equal predicate1, predicate2
-
-
1
Predicate.scope :left
-
1
predicate3 = Predicate.new :predicate1
-
-
1
assert_equal predicate1, predicate3
-
end
-
-
end
-
-
1
describe '.builtin' do
-
-
1
it 'should incremently return Predicates' do
-
1
iota1 = Predicate.builtin :iota
-
1
zeta1 = Predicate.builtin :zeta
-
1
iota2 = Predicate.builtin :iota
-
1
zeta2 = Predicate.builtin :zeta
-
-
1
assert_Predicate iota1, :_iota_1, []
-
1
assert_Predicate zeta1, :_zeta_1, []
-
1
assert_Predicate iota2, :_iota_2, []
-
1
assert_Predicate zeta2, :_zeta_2, []
-
end
-
-
end
-
-
1
describe '#initialize' do
-
-
1
it 'can create predicates in different scopes' do
-
1
left = Scope.new :left
-
#right = Scope.new :right # Not explicitly creating scope :right
-
-
1
assert_equal left, Predicate.scope(:left)
-
1
assert_equal :left, Predicate.scope.name
-
-
1
left_test = Predicate.new 'left_test'
-
-
1
assert_equal :left, Predicate.scope.name
-
1
refute_empty left.predicates
-
1
assert_includes left.predicates, left_test
-
-
1
right_test = Predicate.new 'right_test', :right
-
-
1
assert_includes Scope[:right].predicates, right_test
-
1
assert_equal :left, Predicate.scope.name
-
-
1
assert_equal [left_test], Scope[:left ].predicates
-
1
assert_equal [right_test], Scope[:right].predicates
-
end
-
-
1
it 'should not create a Predicate named "predicate"' do
-
1
assert_raises Porolog::Predicate::NameError do
-
1
predicate :predicate
-
end
-
1
assert_raises Porolog::Predicate::NameError do
-
1
predicate 'predicate'
-
end
-
1
assert_raises Porolog::Predicate::NameError do
-
1
Predicate.new :predicate
-
end
-
1
assert_raises Porolog::Predicate::NameError do
-
1
Predicate.new 'predicate'
-
end
-
end
-
-
end
-
-
1
describe '#call' do
-
-
1
it 'should create an Arguments when "called"' do
-
1
p = Predicate.new :p
-
-
1
arguments = p.(1,2,3)
-
-
1
assert_instance_of 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 = Predicate.new :p
-
-
1
arguments = p.arguments(1,2,3)
-
-
1
assert_instance_of 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 = 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 = 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 = 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 falicies to a predicate' do
-
1
alpha = Predicate.new 'alpha'
-
-
1
alpha.('p','q').falicy!
-
-
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 = Predicate.new 'alpha'
-
-
1
assert_equal 'alpha:-', alpha.inspect
-
end
-
-
1
it 'should return a summary of the predicate with rules' do
-
1
alpha = Predicate.new 'alpha'
-
-
alpha.(:x,:y) << [
-
alpha.(:x,:y),
-
alpha.(:y,:x),
-
:CUT
-
1
]
-
-
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 = Predicate.new 'alpha'
-
-
alpha.(:P,:Q) << [
-
alpha.(:P,:Q),
-
alpha.(:Q,:P),
-
1
]
-
alpha.(:P,:Q) << [
-
alpha.(:P,:P),
-
alpha.(:Q,:Q),
-
1
]
-
-
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
-
-
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
-
6
reset
-
end
-
-
1
describe 'Rule' do
-
-
1
describe '.reset' do
-
-
1
it 'should delete/unregister all rules' do
-
1
pred = Predicate.new :pred
-
1
args = Arguments.new pred, [1,2,3]
-
-
1
rule1 = Rule.new args, [1,2]
-
1
rule2 = Rule.new args, [3,4,5]
-
1
rule3 = Rule.new args, [6]
-
-
1
assert_equal 'Rule1', rule1.myid
-
1
assert_equal 'Rule2', rule2.myid
-
1
assert_equal 'Rule3', rule3.myid
-
-
1
Rule.reset
-
-
1
rule4 = Rule.new args, [7,8,9,0]
-
1
rule5 = 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 = Predicate.new :pred
-
1
args = Arguments.new pred, [1,2,3]
-
1
defn = [true]
-
1
rule = 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 = Predicate.new :pred
-
1
args = Arguments.new pred, [1,2,3]
-
1
defn = [:CUT,false]
-
1
rule = 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 = Predicate.new :pred
-
1
args = Arguments.new pred, [1,2,3]
-
1
defn = [:CUT,false]
-
1
rule = Rule.new args, defn
-
-
1
assert_equal 'Rule1', rule.myid
-
end
-
-
1
it 'should show -999 when deleted/unregistered' do
-
1
pred = Predicate.new :pred
-
1
args = Arguments.new pred, [1,2,3]
-
1
defn = [:CUT,false]
-
1
rule = Rule.new args, defn
-
-
1
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 = Predicate.new :pred
-
1
args = Arguments.new pred, [1,2,3]
-
1
defn = [:CUT,false]
-
1
rule = Rule.new args, defn
-
-
1
assert_equal ' pred(1,2,3):- [:CUT, false]', rule.inspect
-
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, Scope.scopes.size
-
1
assert_equal [:default], Scope.scopes
-
-
1
assert_Scope Scope[:default], :default, []
-
end
-
-
1
it 'should allow predicates with the same name to coexist in different scopes' do
-
1
skip 'until Goal added'
-
-
prime = prime1 = Predicate.new :prime, :first
-
-
prime.(2).fact!
-
prime.(3).fact!
-
prime.(5).fact!
-
prime.(7).fact!
-
prime.(11).fact!
-
-
prime = prime2 = Predicate.new :prime, :second
-
-
prime.('pump A').fact!
-
prime.('pump B').fact!
-
prime.('pump C').fact!
-
prime.('pump D').fact!
-
-
assert_equal [:default,:first,:second], Scope.scopes
-
assert_Scope Scope[:default], :default, []
-
assert_Scope Scope[:first], :first, [prime1]
-
assert_Scope Scope[:second], :second, [prime2]
-
-
assert_equal :prime, prime1.name
-
assert_equal :prime, prime2.name
-
-
solutions = [
-
{ X: 2 },
-
{ X: 3 },
-
{ X: 5 },
-
{ X: 7 },
-
{ X: 11 },
-
]
-
assert_equal solutions, prime1.(:X).solve
-
-
solutions = [
-
{ X: 'pump A' },
-
{ X: 'pump B' },
-
{ X: 'pump C' },
-
{ X: 'pump D' },
-
]
-
assert_equal solutions, prime2.(:X).solve
-
end
-
-
1
describe '.scopes' do
-
-
1
it 'should return the names of all registered scopes' do
-
1
Scope.new :alpha
-
1
Scope.new :bravo
-
1
Scope.new :carly
-
-
1
assert_equal 4, Scope.scopes.size
-
1
assert_equal [:default, :alpha, :bravo, :carly], Scope.scopes
-
end
-
-
end
-
-
1
describe '.reset' do
-
-
1
it 'should clear all scopes' do
-
1
delta = Predicate.new :delta
-
-
1
Scope.new :alpha
-
1
Scope.new :bravo
-
1
Scope.new :carly
-
-
1
assert_equal 4, Scope.scopes.size
-
1
assert_equal [:default, :alpha, :bravo, :carly], Scope.scopes
-
-
1
assert_Scope Scope[:default], :default, [delta]
-
1
assert_Scope Scope[:alpha], :alpha, []
-
1
assert_Scope Scope[:bravo], :bravo, []
-
1
assert_Scope Scope[:carly], :carly, []
-
-
1
Scope.reset
-
-
1
assert_equal 1, Scope.scopes.size
-
1
assert_equal [:default], Scope.scopes
-
-
1
assert_Scope Scope[:default], :default, []
-
-
1
assert_nil Scope[:alpha]
-
1
assert_nil Scope[:bravo]
-
1
assert_nil Scope[:carly]
-
end
-
-
1
it 'should clear all scopes and start with a default scope when reset' do
-
1
test_predicate0 = Predicate.new 'test_predicate0'
-
1
test_predicate11 = Predicate.new 'test_predicate11', :scope1
-
1
test_predicate12 = Predicate.new 'test_predicate12', :scope1
-
1
test_predicate13 = Predicate.new 'test_predicate13', :scope1
-
1
test_predicate21 = Predicate.new 'test_predicate21', :scope2
-
1
test_predicate22 = Predicate.new 'test_predicate22', :scope2
-
1
test_predicate23 = Predicate.new 'test_predicate23', :scope2
-
-
1
assert_equal [:default, :scope1, :scope2], Scope.scopes
-
1
assert_equal [test_predicate0], Scope[:default].predicates
-
1
assert_equal [test_predicate11, test_predicate12, test_predicate13], Scope[:scope1 ].predicates
-
1
assert_equal [test_predicate21, test_predicate22, test_predicate23], Scope[:scope2 ].predicates
-
-
1
Scope.reset
-
-
1
test_predicate3 = Predicate.new 'test_predicate3'
-
-
1
assert_equal [:default], Scope.scopes
-
1
assert_equal [test_predicate3], Scope[:default].predicates
-
1
assert_nil Scope[:scope1 ]
-
1
assert_nil Scope[:scope2 ]
-
end
-
-
end
-
-
1
describe '.[]' do
-
-
1
it 'should provide access to a scope by name' do
-
1
alpha = Scope.new :alpha
-
1
bravo = Scope.new :bravo
-
1
carly = Scope.new :carly
-
-
1
assert_equal alpha, Scope[:alpha]
-
1
assert_equal bravo, Scope[:bravo]
-
1
assert_equal carly, Scope[:carly]
-
1
assert_nil Scope[:delta]
-
end
-
-
end
-
-
1
describe '.new' do
-
-
1
it 'should not create duplicate scopes (with the same name)' do
-
1
Scope.new :duplicate
-
1
Scope.new :duplicate
-
1
Scope.new :duplicate
-
1
Scope.new :duplicate
-
-
1
assert_equal 2, Scope.scopes.size
-
1
assert_equal 1, Scope.scopes.count(:duplicate)
-
1
assert_equal [:default, :duplicate], Scope.scopes
-
end
-
-
end
-
-
1
describe '#initialize' do
-
-
1
it 'should keep track of created scopes' do
-
1
Scope.new :alpha
-
1
Scope.new :bravo
-
1
Scope.new :carly
-
-
1
assert_equal [:default,:alpha,:bravo,:carly], Scope.scopes
-
end
-
-
1
it 'should create scopes with no predicates' do
-
1
scope = 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 = 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 = Predicate.new 'test_predicate1', :test
-
1
test_predicate2 = Predicate.new 'test_predicate2', :test
-
-
1
assert_respond_to Scope[:test], :predicates
-
1
assert_equal [test_predicate1,test_predicate2], 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 = Predicate.new 'test_predicate3', :test
-
1
test_predicate4 = Predicate.new 'test_predicate4', :test
-
-
1
assert_includes Scope[:test].predicates, test_predicate3
-
-
1
assert_instance_of Class, Scope
-
1
assert_instance_of Scope, Scope[:test]
-
1
assert_instance_of Predicate, Scope[:test][:test_predicate3]
-
-
1
assert_equal test_predicate3, Scope[:test][:test_predicate3]
-
1
assert_equal test_predicate3, Scope[:test]['test_predicate3']
-
1
assert_equal test_predicate4, 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 = 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 = Predicate.new 'test_predicate', :test
-
-
1
scope = Scope.new :scope
-
-
1
scope[:predicate] = test_predicate
-
-
1
assert_equal 3, Scope.scopes.size
-
1
assert_equal [:default, :test, :scope], Scope.scopes
-
-
1
assert_Scope Scope[:default], :default, []
-
1
assert_Scope Scope[:test], :test, [test_predicate]
-
1
assert_Scope Scope[:scope], :scope, [test_predicate]
-
end
-
-
end
-
-
end
-
-
end