porolog 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +110 -63
- data/lib/porolog/predicate.rb +103 -0
- data/lib/porolog/scope.rb +1 -2
- data/lib/porolog.rb +21 -3
- data/test/porolog/predicate_test.rb +335 -0
- data/test/porolog/scope_test.rb +1 -13
- data/test/test_helper.rb +7 -0
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 595dfcb99ad8db28a4aa8b5a71b0f63c4e417dd4b2036ec0e7559c7bf0523438
|
4
|
+
data.tar.gz: dc8d09f758aea19d636bd593e3e6a9a9283027e44424ad6cc88ce78445afec64
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 88889f2063831ca8b5a6358e96727c1ad3805a5721d6b39e6b931af584ea24ac12b5dc89bdf18eeff81eed1e93e04b8637f27a1fe62db2a08d126e1e208a16e1
|
7
|
+
data.tar.gz: c564320bd055721a1de3a4af742c81f69c51574c88a84207373208870655452508aed97c21c567ba4a0205d4ae4944f9e824fc5b206043facf112f9c799150bc
|
data/README.md
CHANGED
@@ -2,43 +2,72 @@
|
|
2
2
|
|
3
3
|
Plain Old Ruby Objects Prolog
|
4
4
|
|
5
|
+
[](https://badge.fury.io/rb/porolog)
|
6
|
+
[](https://travis-ci.com/wizardofosmium/porolog)
|
7
|
+
|
5
8
|
## Introduction
|
6
9
|
|
7
|
-
porolog is a Prolog implementation using plain old Ruby objects with the aim that
|
10
|
+
`porolog` is a Prolog implementation using plain old Ruby objects with the aim that
|
8
11
|
logic queries can be called within a regular Ruby program. The goal was not to
|
9
12
|
implement a Prolog interpreter that is just implement in Ruby, but rather that
|
10
13
|
a logic engine could be embedded in a larger program.
|
11
14
|
|
15
|
+
The need that this gem aims to meet is to have a Ruby program interact with a Prolog
|
16
|
+
program using native Ruby objects (POROs); hence the name Porolog.
|
12
17
|
The goal was to implement a minimal logic engine in the style of Prolog where
|
13
18
|
Ruby objects could be passed in and Ruby objects were passed back.
|
14
19
|
|
20
|
+
## Dependencies
|
21
|
+
|
22
|
+
The aim of `porolog` is to provide a logic engine with a minimal footprint.
|
23
|
+
Thus `porolog` has no dependencies.
|
24
|
+
|
15
25
|
## Installation
|
16
26
|
|
17
|
-
|
27
|
+
```bash
|
28
|
+
gem install porolog
|
29
|
+
```
|
18
30
|
|
19
31
|
## Usage
|
20
32
|
|
33
|
+
`porolog` is used by:
|
34
|
+
|
35
|
+
* requiring the library within a Ruby program
|
36
|
+
* defining facts and rules
|
37
|
+
* solving goals
|
38
|
+
|
39
|
+
It is entirely possible to create a Ruby program that is effectively just a Prolog program.
|
40
|
+
|
21
41
|
### Basic Usage
|
22
42
|
|
23
|
-
|
43
|
+
Using `porolog` involves creating logic from facts and rules.
|
44
|
+
An example, of the most basic usage uses just facts.
|
24
45
|
|
25
|
-
|
46
|
+
```ruby
|
47
|
+
require 'porolog'
|
26
48
|
|
27
|
-
|
28
|
-
prime.(3).fact!
|
29
|
-
prime.(5).fact!
|
30
|
-
prime.(7).fact!
|
49
|
+
prime = Porolog::Predicate.new :prime
|
31
50
|
|
32
|
-
|
51
|
+
prime.(2).fact!
|
52
|
+
prime.(3).fact!
|
53
|
+
prime.(5).fact!
|
54
|
+
prime.(7).fact!
|
33
55
|
|
34
|
-
|
35
|
-
puts "#{solution[:X]} is prime"
|
36
|
-
end
|
56
|
+
solutions = prime.(:X).solve
|
37
57
|
|
58
|
+
solutions.each do |solution|
|
59
|
+
puts "#{solution[:X]} is prime"
|
60
|
+
end
|
61
|
+
```
|
38
62
|
|
39
63
|
### Common Usage
|
40
64
|
|
41
|
-
|
65
|
+
Common usage is expected to be including Porolog in a class and encapsulating the engine defined.
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
require 'porolog'
|
69
|
+
|
70
|
+
class Numbers
|
42
71
|
|
43
72
|
include Porolog
|
44
73
|
|
@@ -49,70 +78,88 @@ Ruby objects could be passed in and Ruby objects were passed back.
|
|
49
78
|
prime(5).fact!
|
50
79
|
prime(7).fact!
|
51
80
|
|
52
|
-
|
81
|
+
def show_primes
|
82
|
+
solutions = prime(:X).solve
|
53
83
|
|
54
|
-
|
55
|
-
|
84
|
+
solutions.each do |solution|
|
85
|
+
puts "#{solution[:X]} is prime"
|
86
|
+
end
|
56
87
|
end
|
57
88
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
Ruby program. A Scope object defines a scope for the predicates of a logic programme.
|
62
|
-
|
63
|
-
require 'porolog'
|
64
|
-
|
65
|
-
# -- Prime Numbers Predicate --
|
66
|
-
prime = prime1 = Predicate.new :prime, :numbers
|
67
|
-
|
68
|
-
prime.(2).fact!
|
69
|
-
prime.(3).fact!
|
70
|
-
prime.(5).fact!
|
71
|
-
prime.(7).fact!
|
72
|
-
prime.(11).fact!
|
73
|
-
|
74
|
-
# -- Pump Predicate --
|
75
|
-
prime = prime2 = Predicate.new :prime, :pumps
|
76
|
-
|
77
|
-
prime.('pump A').fact!
|
78
|
-
prime.('pump B').fact!
|
79
|
-
prime.('pump C').fact!
|
80
|
-
prime.('pump D').fact!
|
81
|
-
|
82
|
-
assert_equal [:default,:numbers,:pumps], Scope.scopes
|
89
|
+
def primes
|
90
|
+
prime(:X).solve_for(:X)
|
91
|
+
end
|
83
92
|
|
84
|
-
|
85
|
-
|
86
|
-
assert_scope Scope[:pumps], :second, [prime2]
|
93
|
+
end
|
94
|
+
```
|
87
95
|
|
88
|
-
|
89
|
-
assert_equal :prime, prime2.name
|
96
|
+
### Scope and Predicate Usage
|
90
97
|
|
91
|
-
|
92
|
-
{ X: 2 },
|
93
|
-
{ X: 3 },
|
94
|
-
{ X: 5 },
|
95
|
-
{ X: 7 },
|
96
|
-
{ X: 11 },
|
97
|
-
]
|
98
|
-
assert_equal solutions, prime1.(:X).solve
|
98
|
+
A Predicate represents a Prolog predicate. They form the basis for rules and queries.
|
99
99
|
|
100
|
-
|
101
|
-
|
102
|
-
{ X: 'pump B' },
|
103
|
-
{ X: 'pump C' },
|
104
|
-
{ X: 'pump D' },
|
105
|
-
]
|
106
|
-
assert_equal solutions, prime2.(:X).solve
|
100
|
+
The Scope class enables you to have multiple logic programs embedded in the same
|
101
|
+
Ruby program. A Scope object defines a scope for the predicates of a logic programme.
|
107
102
|
|
103
|
+
```ruby
|
104
|
+
require 'porolog'
|
105
|
+
|
106
|
+
# -- Prime Numbers Predicate --
|
107
|
+
prime = prime1 = Porolog::Predicate.new :prime, :numbers
|
108
|
+
|
109
|
+
prime.(2).fact!
|
110
|
+
prime.(3).fact!
|
111
|
+
prime.(5).fact!
|
112
|
+
prime.(7).fact!
|
113
|
+
prime.(11).fact!
|
114
|
+
|
115
|
+
# -- Pump Predicate --
|
116
|
+
prime = prime2 = Porolog::Predicate.new :prime, :pumps
|
117
|
+
|
118
|
+
prime.('pump A').fact!
|
119
|
+
prime.('pump B').fact!
|
120
|
+
prime.('pump C').fact!
|
121
|
+
prime.('pump D').fact!
|
122
|
+
|
123
|
+
# -- Assertions --
|
124
|
+
assert_equal [:default,:numbers,:pumps], Scope.scopes
|
125
|
+
|
126
|
+
assert_scope Porolog::Scope[:default], :default, []
|
127
|
+
assert_scope Porolog::Scope[:numbers], :first, [prime1]
|
128
|
+
assert_scope Porolog::Scope[:pumps], :second, [prime2]
|
129
|
+
|
130
|
+
assert_equal :prime, prime1.name
|
131
|
+
assert_equal :prime, prime2.name
|
132
|
+
|
133
|
+
solutions = [
|
134
|
+
{ X: 2 },
|
135
|
+
{ X: 3 },
|
136
|
+
{ X: 5 },
|
137
|
+
{ X: 7 },
|
138
|
+
{ X: 11 },
|
139
|
+
]
|
140
|
+
assert_equal solutions, prime1.(:X).solve
|
141
|
+
|
142
|
+
solutions = [
|
143
|
+
{ X: 'pump A' },
|
144
|
+
{ X: 'pump B' },
|
145
|
+
{ X: 'pump C' },
|
146
|
+
{ X: 'pump D' },
|
147
|
+
]
|
148
|
+
assert_equal solutions, prime2.(:X).solve
|
149
|
+
```
|
108
150
|
|
109
151
|
## Testing
|
110
152
|
|
111
|
-
|
153
|
+
```bash
|
154
|
+
rake test
|
155
|
+
```
|
112
156
|
|
113
157
|
or
|
114
158
|
|
115
|
-
|
159
|
+
```bash
|
160
|
+
rake scope_test
|
161
|
+
rake predicate_test
|
162
|
+
```
|
116
163
|
|
117
164
|
## Author
|
118
165
|
|
@@ -0,0 +1,103 @@
|
|
1
|
+
#
|
2
|
+
# lib/porolog/predicate.rb - Plain Old Ruby Objects Prolog Engine -- Predicate
|
3
|
+
#
|
4
|
+
# Luis Esteban 2 May 2018
|
5
|
+
# created
|
6
|
+
#
|
7
|
+
|
8
|
+
# Porolog::Predicate
|
9
|
+
#
|
10
|
+
# A Porolog::Predicate corresponds to a Prolog predicate.
|
11
|
+
# It contains rules (Porolog::Rule) and belongs to a Porolog::Scope.
|
12
|
+
# When provided with arguments, it produces a Porolog::Arguments.
|
13
|
+
#
|
14
|
+
|
15
|
+
module Porolog
|
16
|
+
|
17
|
+
class Predicate
|
18
|
+
|
19
|
+
class PredicateError < PorologError ; end
|
20
|
+
class NameError < PredicateError ; end
|
21
|
+
|
22
|
+
attr_reader :name, :rules
|
23
|
+
|
24
|
+
@@current_scope = Scope[:default]
|
25
|
+
@builtin_predicate_ids = {}
|
26
|
+
|
27
|
+
def self.scope(scope_name = nil)
|
28
|
+
if scope_name
|
29
|
+
@@current_scope = Scope[scope_name] || Scope.new(scope_name)
|
30
|
+
else
|
31
|
+
@@current_scope
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.scope=(scope_name)
|
36
|
+
if scope_name
|
37
|
+
@@current_scope = Scope[scope_name] || Scope.new(scope_name)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.reset
|
42
|
+
scope(:default)
|
43
|
+
@builtin_predicate_ids = {}
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.[](name)
|
47
|
+
@@current_scope[name]
|
48
|
+
end
|
49
|
+
|
50
|
+
reset
|
51
|
+
|
52
|
+
def initialize(name, scope_name = Predicate.scope.name)
|
53
|
+
@name = name.to_sym
|
54
|
+
@rules = []
|
55
|
+
|
56
|
+
raise NameError.new("Cannot name a predicate 'predicate'") if @name == :predicate
|
57
|
+
|
58
|
+
scope = Scope[scope_name] || Scope.new(scope_name)
|
59
|
+
scope[@name] = self
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.new(*args)
|
63
|
+
name, _ = *args
|
64
|
+
scope[name.to_sym] || super
|
65
|
+
end
|
66
|
+
|
67
|
+
def call(*args)
|
68
|
+
Arguments.new(self,args)
|
69
|
+
end
|
70
|
+
|
71
|
+
def arguments(*args)
|
72
|
+
Arguments.new(self,args)
|
73
|
+
end
|
74
|
+
|
75
|
+
def <<(rule)
|
76
|
+
@rules << rule
|
77
|
+
end
|
78
|
+
|
79
|
+
def inspect
|
80
|
+
lines = []
|
81
|
+
|
82
|
+
if @rules.size == 1
|
83
|
+
lines << "#{@name}:-#{@rules.first.inspect}"
|
84
|
+
else
|
85
|
+
lines << "#{@name}:-"
|
86
|
+
@rules.each do |rule|
|
87
|
+
lines << rule.inspect
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
lines.join("\n")
|
92
|
+
end
|
93
|
+
|
94
|
+
def self.builtin(key)
|
95
|
+
@builtin_predicate_ids[key] ||= 0
|
96
|
+
@builtin_predicate_ids[key] += 1
|
97
|
+
|
98
|
+
self.new("_#{key}_#{@builtin_predicate_ids[key]}")
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
data/lib/porolog/scope.rb
CHANGED
@@ -54,8 +54,7 @@ module Porolog
|
|
54
54
|
end
|
55
55
|
|
56
56
|
def []=(name,predicate)
|
57
|
-
#
|
58
|
-
#raise NotPredicateError.new("#{predicate.inspect} is not a Predicate") unless predicate.is_a?(Predicate)
|
57
|
+
raise NotPredicateError.new("#{predicate.inspect} is not a Predicate") unless predicate.is_a?(Predicate)
|
59
58
|
@predicates[name.to_sym] = predicate
|
60
59
|
end
|
61
60
|
|
data/lib/porolog.rb
CHANGED
@@ -7,10 +7,28 @@
|
|
7
7
|
|
8
8
|
module Porolog
|
9
9
|
|
10
|
-
VERSION = '0.0.
|
11
|
-
VERSION_DATE = '
|
12
|
-
|
10
|
+
VERSION = '0.0.2'
|
11
|
+
VERSION_DATE = '2019-04-20'
|
12
|
+
|
13
|
+
def predicate(*names)
|
14
|
+
names = [names].flatten
|
15
|
+
|
16
|
+
predicates = names.map{|name|
|
17
|
+
method = name.to_sym
|
18
|
+
predicate = Predicate.new(name)
|
19
|
+
Object.class_eval{
|
20
|
+
define_method(method){|*args|
|
21
|
+
predicate.(*args)
|
22
|
+
}
|
23
|
+
}
|
24
|
+
predicate
|
25
|
+
}
|
26
|
+
|
27
|
+
predicates.size > 1 && predicates || predicates.first
|
28
|
+
end
|
29
|
+
|
13
30
|
end
|
14
31
|
|
15
32
|
require_relative 'porolog/error'
|
16
33
|
require_relative 'porolog/scope'
|
34
|
+
require_relative 'porolog/predicate'
|
@@ -0,0 +1,335 @@
|
|
1
|
+
#
|
2
|
+
# test/porolog/predicate_test.rb - Test Suite for Porolog::Predicate
|
3
|
+
#
|
4
|
+
# Luis Esteban 2 May 2018
|
5
|
+
# created
|
6
|
+
#
|
7
|
+
|
8
|
+
require_relative '../test_helper'
|
9
|
+
|
10
|
+
describe 'Porolog' do
|
11
|
+
|
12
|
+
before(:all) do
|
13
|
+
reset
|
14
|
+
end
|
15
|
+
|
16
|
+
describe 'Predicate' do
|
17
|
+
|
18
|
+
it 'should default to creating predicates in the default scope' do
|
19
|
+
assert_equal :default, Predicate.scope.name
|
20
|
+
|
21
|
+
test = Predicate.new 'test'
|
22
|
+
|
23
|
+
assert_includes Scope[:default].predicates, test
|
24
|
+
end
|
25
|
+
|
26
|
+
describe '.scope' do
|
27
|
+
|
28
|
+
it 'should return the current scope for new predicates' do
|
29
|
+
assert_equal Scope[:default], Predicate.scope
|
30
|
+
|
31
|
+
alpha = Predicate.new 'alpha', :new_scope
|
32
|
+
|
33
|
+
assert_equal Scope[:default], Predicate.scope
|
34
|
+
|
35
|
+
Predicate.scope :new_scope
|
36
|
+
|
37
|
+
assert_equal Scope[:new_scope], Predicate.scope
|
38
|
+
|
39
|
+
bravo = Predicate.new 'bravo'
|
40
|
+
|
41
|
+
assert_equal [], Scope[:default ].predicates
|
42
|
+
assert_equal [alpha,bravo], Scope[:new_scope].predicates
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'should allow predicate scope to be created and set' do
|
46
|
+
Predicate.scope :internal
|
47
|
+
assert_equal :internal, Predicate.scope.name
|
48
|
+
|
49
|
+
Predicate.new 'alpha'
|
50
|
+
|
51
|
+
Predicate.scope :external
|
52
|
+
assert_equal :external, Predicate.scope.name
|
53
|
+
|
54
|
+
Predicate.new 'bravo'
|
55
|
+
|
56
|
+
Predicate.scope :internal
|
57
|
+
assert_equal :internal, Predicate.scope.name
|
58
|
+
|
59
|
+
Predicate.new 'carly'
|
60
|
+
Predicate.new 'delta', :external
|
61
|
+
|
62
|
+
assert_equal :internal, Predicate.scope.name
|
63
|
+
|
64
|
+
assert_equal [:alpha,:carly], Scope[:internal].predicates.map(&:name)
|
65
|
+
assert_equal [:bravo,:delta], Scope[:external].predicates.map(&:name)
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
describe '.scope=' do
|
71
|
+
|
72
|
+
it 'should also allow predicate scope to be assigned' do
|
73
|
+
Predicate.scope = :internal
|
74
|
+
assert_equal :internal, Predicate.scope.name
|
75
|
+
|
76
|
+
alpha = Predicate.new 'alpha'
|
77
|
+
|
78
|
+
Predicate.scope = :external
|
79
|
+
assert_equal :external, Predicate.scope.name
|
80
|
+
|
81
|
+
bravo = Predicate.new 'bravo'
|
82
|
+
|
83
|
+
Predicate.scope = :internal
|
84
|
+
assert_equal :internal, Predicate.scope.name
|
85
|
+
|
86
|
+
carly = Predicate.new 'carly'
|
87
|
+
delta = Predicate.new 'delta', :external
|
88
|
+
|
89
|
+
assert_equal :internal, Predicate.scope.name
|
90
|
+
|
91
|
+
assert_equal [alpha,carly], Scope[:internal].predicates
|
92
|
+
assert_equal [bravo,delta], Scope[:external].predicates
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
|
97
|
+
describe '.reset' do
|
98
|
+
|
99
|
+
it 'should revert to the default scope when reset' do
|
100
|
+
Predicate.scope = :other
|
101
|
+
assert_equal :other, Predicate.scope.name
|
102
|
+
|
103
|
+
Predicate.reset
|
104
|
+
assert_equal :default, Predicate.scope.name
|
105
|
+
|
106
|
+
Predicate.scope :temporary
|
107
|
+
|
108
|
+
alpha = Predicate.new :alpha
|
109
|
+
|
110
|
+
assert_equal :temporary, Predicate.scope.name
|
111
|
+
assert_equal [alpha], Predicate.scope.predicates
|
112
|
+
|
113
|
+
Predicate.reset
|
114
|
+
|
115
|
+
bravo = Predicate.new :bravo
|
116
|
+
|
117
|
+
assert_equal :default, Predicate.scope.name
|
118
|
+
assert_equal [bravo], Predicate.scope.predicates
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
|
123
|
+
describe '.[]' do
|
124
|
+
|
125
|
+
it 'should return a predicate by name' do
|
126
|
+
alpha = Predicate.new :alpha
|
127
|
+
|
128
|
+
assert_equal alpha, Predicate[:alpha]
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
132
|
+
|
133
|
+
describe '.new' do
|
134
|
+
|
135
|
+
it 'should create a new predicate' do
|
136
|
+
alpha = Predicate.new :alpha
|
137
|
+
|
138
|
+
assert_instance_of Predicate, alpha
|
139
|
+
end
|
140
|
+
|
141
|
+
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
|
142
|
+
predicate1 = Predicate.new 'predicate1', :left
|
143
|
+
predicate2 = Predicate.new 'predicate1', :right
|
144
|
+
|
145
|
+
refute_equal predicate1, predicate2
|
146
|
+
|
147
|
+
Predicate.scope :left
|
148
|
+
predicate3 = Predicate.new :predicate1
|
149
|
+
|
150
|
+
assert_equal predicate1, predicate3
|
151
|
+
end
|
152
|
+
|
153
|
+
end
|
154
|
+
|
155
|
+
describe '#initialize' do
|
156
|
+
|
157
|
+
it 'can create predicates in different scopes' do
|
158
|
+
left = Scope.new :left
|
159
|
+
#right = Scope.new :right # Not explicitly creating scope :right
|
160
|
+
|
161
|
+
assert_equal left, Predicate.scope(:left)
|
162
|
+
assert_equal :left, Predicate.scope.name
|
163
|
+
|
164
|
+
left_test = Predicate.new 'left_test'
|
165
|
+
|
166
|
+
assert_equal :left, Predicate.scope.name
|
167
|
+
refute_empty left.predicates
|
168
|
+
assert_includes left.predicates, left_test
|
169
|
+
|
170
|
+
right_test = Predicate.new 'right_test', :right
|
171
|
+
|
172
|
+
assert_includes Scope[:right].predicates, right_test
|
173
|
+
assert_equal :left, Predicate.scope.name
|
174
|
+
|
175
|
+
assert_equal [left_test], Scope[:left ].predicates
|
176
|
+
assert_equal [right_test], Scope[:right].predicates
|
177
|
+
end
|
178
|
+
|
179
|
+
it 'should not create a Predicate named "predicate"' do
|
180
|
+
assert_raises Porolog::Predicate::NameError do
|
181
|
+
predicate :predicate
|
182
|
+
end
|
183
|
+
assert_raises Porolog::Predicate::NameError do
|
184
|
+
predicate 'predicate'
|
185
|
+
end
|
186
|
+
assert_raises Porolog::Predicate::NameError do
|
187
|
+
Predicate.new :predicate
|
188
|
+
end
|
189
|
+
assert_raises Porolog::Predicate::NameError do
|
190
|
+
Predicate.new 'predicate'
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
end
|
195
|
+
|
196
|
+
describe '#call' do
|
197
|
+
|
198
|
+
it 'should create an Arguments when "called"' do
|
199
|
+
skip 'until Arguments added'
|
200
|
+
|
201
|
+
p = Predicate.new :p
|
202
|
+
|
203
|
+
arguments = p.(1,2,3)
|
204
|
+
|
205
|
+
assert_instance_of Arguments, arguments
|
206
|
+
assert_equal [1,2,3], arguments.arguments
|
207
|
+
assert_equal 'p(1,2,3)', arguments.inspect
|
208
|
+
end
|
209
|
+
|
210
|
+
end
|
211
|
+
|
212
|
+
describe '#arguments' do
|
213
|
+
|
214
|
+
it 'should provide a convenience method to create an Arguments' do
|
215
|
+
skip 'until Arguments added'
|
216
|
+
|
217
|
+
p = Predicate.new :p
|
218
|
+
|
219
|
+
arguments = p.arguments(1,2,3)
|
220
|
+
|
221
|
+
assert_instance_of Arguments, arguments
|
222
|
+
assert_equal [1,2,3], arguments.arguments
|
223
|
+
assert_equal 'p(1,2,3)', arguments.inspect
|
224
|
+
end
|
225
|
+
|
226
|
+
end
|
227
|
+
|
228
|
+
describe '#name' do
|
229
|
+
|
230
|
+
it 'should create predicates with a name attribute, which is converted to a Symbol' do
|
231
|
+
test_predicate = Predicate.new('test_predicate_name')
|
232
|
+
|
233
|
+
assert_respond_to test_predicate, :name
|
234
|
+
assert_equal :test_predicate_name, test_predicate.name
|
235
|
+
end
|
236
|
+
|
237
|
+
end
|
238
|
+
|
239
|
+
describe '#rules' do
|
240
|
+
|
241
|
+
it 'should create predicates with an empty set of rules' do
|
242
|
+
test_predicate = Predicate.new('test_predicate_name')
|
243
|
+
|
244
|
+
assert_respond_to test_predicate, :rules
|
245
|
+
assert_equal [], test_predicate.rules
|
246
|
+
end
|
247
|
+
|
248
|
+
it 'should add new facts to a predicate' do
|
249
|
+
skip 'until Arguments added'
|
250
|
+
|
251
|
+
alpha = Predicate.new 'alpha'
|
252
|
+
|
253
|
+
alpha.('p','q').fact!
|
254
|
+
|
255
|
+
assert_equal 1, alpha.rules.size
|
256
|
+
assert_equal true, alpha.rules.first.definition
|
257
|
+
assert_equal ' alpha("p","q"):- true', alpha.rules.first.inspect
|
258
|
+
assert_equal 'alpha:- alpha("p","q"):- true', alpha.inspect
|
259
|
+
|
260
|
+
assert_predicate alpha, :alpha, [alpha.rules.first]
|
261
|
+
end
|
262
|
+
|
263
|
+
it 'should add new falicies to a predicate' do
|
264
|
+
skip 'until Arguments added'
|
265
|
+
|
266
|
+
alpha = Predicate.new 'alpha'
|
267
|
+
|
268
|
+
alpha.('p','q').falicy!
|
269
|
+
|
270
|
+
assert_equal 1, alpha.rules.size
|
271
|
+
assert_equal false, alpha.rules.first.definition
|
272
|
+
assert_equal ' alpha("p","q"):- false', alpha.rules.first.inspect
|
273
|
+
assert_equal 'alpha:- alpha("p","q"):- false', alpha.inspect
|
274
|
+
|
275
|
+
assert_predicate alpha, :alpha, [alpha.rules.first]
|
276
|
+
end
|
277
|
+
|
278
|
+
end
|
279
|
+
|
280
|
+
describe '#inspect' do
|
281
|
+
|
282
|
+
it 'should return a summary of the predicate' do
|
283
|
+
skip 'until Arguments added'
|
284
|
+
|
285
|
+
alpha = Predicate.new 'alpha'
|
286
|
+
assert_equal 'alpha:-', alpha.inspect
|
287
|
+
|
288
|
+
alpha.(:x,:y) << [
|
289
|
+
alpha.(:x,:y),
|
290
|
+
alpha.(:y,:x),
|
291
|
+
:CUT
|
292
|
+
]
|
293
|
+
assert_equal 'alpha:- alpha(:x,:y):- [alpha(:x,:y), alpha(:y,:x), :CUT]', alpha.inspect
|
294
|
+
end
|
295
|
+
|
296
|
+
end
|
297
|
+
|
298
|
+
describe '#<<' do
|
299
|
+
|
300
|
+
it 'should add new rules to a predicate' do
|
301
|
+
skip 'until Arguments added'
|
302
|
+
|
303
|
+
alpha = Predicate.new 'alpha'
|
304
|
+
|
305
|
+
alpha.(:P,:Q) << [
|
306
|
+
alpha.(:P,:Q),
|
307
|
+
alpha.(:Q,:P),
|
308
|
+
]
|
309
|
+
alpha.(:P,:Q) << [
|
310
|
+
alpha.(:P,:P),
|
311
|
+
alpha.(:Q,:Q),
|
312
|
+
]
|
313
|
+
|
314
|
+
assert_equal 2, alpha.rules.size
|
315
|
+
assert_instance_of Array, alpha.rules.first.definition
|
316
|
+
assert_equal 2, alpha.rules.first.definition.size
|
317
|
+
|
318
|
+
assert_arguments alpha.rules.first.definition[0], :alpha, [:P,:Q]
|
319
|
+
assert_arguments alpha.rules.first.definition[1], :alpha, [:Q,:P]
|
320
|
+
|
321
|
+
assert_equal ' alpha(:P,:Q):- [alpha(:P,:Q), alpha(:Q,:P)]', alpha.rules.first.inspect
|
322
|
+
assert_equal [
|
323
|
+
'alpha:-',
|
324
|
+
' alpha(:P,:Q):- [alpha(:P,:Q), alpha(:Q,:P)]',
|
325
|
+
' alpha(:P,:Q):- [alpha(:P,:P), alpha(:Q,:Q)]',
|
326
|
+
].join("\n"), alpha.inspect
|
327
|
+
|
328
|
+
assert_predicate alpha, :alpha, [alpha.rules.first,alpha.rules.second]
|
329
|
+
end
|
330
|
+
|
331
|
+
end
|
332
|
+
|
333
|
+
end
|
334
|
+
|
335
|
+
end
|
data/test/porolog/scope_test.rb
CHANGED
@@ -23,7 +23,7 @@ describe 'Porolog' do
|
|
23
23
|
end
|
24
24
|
|
25
25
|
it 'should allow predicates with the same name to coexist in different scopes' do
|
26
|
-
skip 'until
|
26
|
+
skip 'until Arguments added'
|
27
27
|
|
28
28
|
prime = prime1 = Predicate.new :prime, :first
|
29
29
|
|
@@ -82,8 +82,6 @@ describe 'Porolog' do
|
|
82
82
|
describe '.reset' do
|
83
83
|
|
84
84
|
it 'should clear all scopes' do
|
85
|
-
skip 'until Predicate added'
|
86
|
-
|
87
85
|
delta = Predicate.new :delta
|
88
86
|
|
89
87
|
Scope.new :alpha
|
@@ -111,8 +109,6 @@ describe 'Porolog' do
|
|
111
109
|
end
|
112
110
|
|
113
111
|
it 'should clear all scopes and start with a default scope when reset' do
|
114
|
-
skip 'until Predicate added'
|
115
|
-
|
116
112
|
test_predicate0 = Predicate.new 'test_predicate0'
|
117
113
|
test_predicate11 = Predicate.new 'test_predicate11', :scope1
|
118
114
|
test_predicate12 = Predicate.new 'test_predicate12', :scope1
|
@@ -201,8 +197,6 @@ describe 'Porolog' do
|
|
201
197
|
describe '#predicates' do
|
202
198
|
|
203
199
|
it 'should provide access to all the predicates of a scope' do
|
204
|
-
skip 'until Predicate added'
|
205
|
-
|
206
200
|
test_predicate1 = Predicate.new 'test_predicate1', :test
|
207
201
|
test_predicate2 = Predicate.new 'test_predicate2', :test
|
208
202
|
|
@@ -215,8 +209,6 @@ describe 'Porolog' do
|
|
215
209
|
describe '#[]' do
|
216
210
|
|
217
211
|
it 'should provide access to a predicate of a scope by its name' do
|
218
|
-
skip 'until Predicate added'
|
219
|
-
|
220
212
|
test_predicate3 = Predicate.new 'test_predicate3', :test
|
221
213
|
test_predicate4 = Predicate.new 'test_predicate4', :test
|
222
214
|
|
@@ -236,8 +228,6 @@ describe 'Porolog' do
|
|
236
228
|
describe '#[]=' do
|
237
229
|
|
238
230
|
it 'should raise an error when trying to put anthing but a Predicate in a Scope' do
|
239
|
-
skip 'until Predicate added'
|
240
|
-
|
241
231
|
error = assert_raises(Porolog::Scope::NotPredicateError){
|
242
232
|
scope = Scope.new :scope
|
243
233
|
|
@@ -247,8 +237,6 @@ describe 'Porolog' do
|
|
247
237
|
end
|
248
238
|
|
249
239
|
it 'should allow an existing predicate to be assigned to a scope' do
|
250
|
-
skip 'until Predicate added'
|
251
|
-
|
252
240
|
test_predicate = Predicate.new 'test_predicate', :test
|
253
241
|
|
254
242
|
scope = Scope.new :scope
|
data/test/test_helper.rb
CHANGED
@@ -12,6 +12,7 @@ include Porolog
|
|
12
12
|
|
13
13
|
def reset
|
14
14
|
Scope.reset
|
15
|
+
Predicate.reset
|
15
16
|
end
|
16
17
|
|
17
18
|
def assert_scope(scope, name, predicates)
|
@@ -19,3 +20,9 @@ def assert_scope(scope, name, predicates)
|
|
19
20
|
assert_equal name, scope.name
|
20
21
|
assert_equal predicates, scope.predicates
|
21
22
|
end
|
23
|
+
|
24
|
+
def assert_predicate(predicate, name, rules)
|
25
|
+
assert_instance_of Predicate, predicate
|
26
|
+
assert_equal name, predicate.name
|
27
|
+
assert_equal rules, predicate.rules
|
28
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: porolog
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Luis Esteban
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-04-20 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Implements a Prolog inference engine using Plain Old Ruby Objects
|
14
14
|
email:
|
@@ -22,10 +22,12 @@ files:
|
|
22
22
|
- bin/porolog
|
23
23
|
- lib/porolog.rb
|
24
24
|
- lib/porolog/error.rb
|
25
|
+
- lib/porolog/predicate.rb
|
25
26
|
- lib/porolog/scope.rb
|
27
|
+
- test/porolog/predicate_test.rb
|
26
28
|
- test/porolog/scope_test.rb
|
27
29
|
- test/test_helper.rb
|
28
|
-
homepage:
|
30
|
+
homepage: https://github.com/wizardofosmium/porolog
|
29
31
|
licenses:
|
30
32
|
- Unlicense
|
31
33
|
metadata: {}
|
@@ -51,4 +53,5 @@ specification_version: 4
|
|
51
53
|
summary: Prolog using Plain Old Ruby Objects
|
52
54
|
test_files:
|
53
55
|
- test/test_helper.rb
|
56
|
+
- test/porolog/predicate_test.rb
|
54
57
|
- test/porolog/scope_test.rb
|