porolog 0.0.4 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +30 -5
- data/Rakefile +7 -2
- data/bin/porolog +58 -1
- data/coverage/badge.svg +1 -1
- data/coverage/index.html +76733 -2638
- data/doc/Array.html +1066 -0
- data/doc/Object.html +674 -0
- data/doc/Porolog.html +4153 -74
- data/doc/Symbol.html +501 -0
- data/doc/_index.html +280 -6
- data/doc/class_list.html +1 -1
- data/doc/file.README.html +34 -39
- data/doc/index.html +34 -39
- data/doc/method_list.html +1337 -57
- data/doc/top-level-namespace.html +4 -2
- data/lib/porolog.rb +1144 -4
- data/lib/porolog/arguments.rb +28 -24
- data/lib/porolog/core_ext.rb +188 -0
- data/lib/porolog/error.rb +9 -0
- data/lib/porolog/goal.rb +357 -0
- data/lib/porolog/instantiation.rb +346 -0
- data/lib/porolog/predicate.rb +74 -31
- data/lib/porolog/predicate/builtin.rb +825 -0
- data/lib/porolog/rule.rb +162 -0
- data/lib/porolog/scope.rb +4 -4
- data/lib/porolog/tail.rb +57 -0
- data/lib/porolog/value.rb +105 -0
- data/lib/porolog/variable.rb +325 -0
- data/test/porolog/arguments_test.rb +244 -195
- data/test/porolog/core_ext_test.rb +290 -0
- data/test/porolog/goal_test.rb +891 -0
- data/test/porolog/instantiation_test.rb +910 -0
- data/test/porolog/porolog_test.rb +2376 -13
- data/test/porolog/predicate/builtin_test.rb +1340 -0
- data/test/porolog/predicate_test.rb +84 -30
- data/test/porolog/rule_test.rb +527 -0
- data/test/porolog/scope_test.rb +0 -2
- data/test/porolog/tail_test.rb +127 -0
- data/test/porolog/value_test.rb +315 -0
- data/test/porolog/variable_test.rb +1614 -0
- data/test/samples_test.rb +277 -0
- data/test/test_helper.rb +115 -0
- metadata +34 -7
@@ -152,27 +152,11 @@ describe 'Porolog' do
|
|
152
152
|
|
153
153
|
end
|
154
154
|
|
155
|
-
describe '.builtin' do
|
156
|
-
|
157
|
-
it 'should incremently return Predicates' do
|
158
|
-
iota1 = Predicate.builtin :iota
|
159
|
-
zeta1 = Predicate.builtin :zeta
|
160
|
-
iota2 = Predicate.builtin :iota
|
161
|
-
zeta2 = Predicate.builtin :zeta
|
162
|
-
|
163
|
-
assert_Predicate iota1, :_iota_1, []
|
164
|
-
assert_Predicate zeta1, :_zeta_1, []
|
165
|
-
assert_Predicate iota2, :_iota_2, []
|
166
|
-
assert_Predicate zeta2, :_zeta_2, []
|
167
|
-
end
|
168
|
-
|
169
|
-
end
|
170
|
-
|
171
155
|
describe '#initialize' do
|
172
156
|
|
173
157
|
it 'can create predicates in different scopes' do
|
174
158
|
left = Scope.new :left
|
175
|
-
#right = Scope.new :right
|
159
|
+
#right = Scope.new :right # Not explicitly creating scope :right
|
176
160
|
|
177
161
|
assert_equal left, Predicate.scope(:left)
|
178
162
|
assert_equal :left, Predicate.scope.name
|
@@ -258,8 +242,6 @@ describe 'Porolog' do
|
|
258
242
|
end
|
259
243
|
|
260
244
|
it 'should add new facts to a predicate' do
|
261
|
-
skip 'until Rule added'
|
262
|
-
|
263
245
|
alpha = Predicate.new 'alpha'
|
264
246
|
|
265
247
|
alpha.('p','q').fact!
|
@@ -269,12 +251,10 @@ describe 'Porolog' do
|
|
269
251
|
assert_equal ' alpha("p","q"):- true', alpha.rules.first.inspect
|
270
252
|
assert_equal 'alpha:- alpha("p","q"):- true', alpha.inspect
|
271
253
|
|
272
|
-
|
254
|
+
assert_Predicate alpha, :alpha, [alpha.rules.first]
|
273
255
|
end
|
274
256
|
|
275
257
|
it 'should add new falicies to a predicate' do
|
276
|
-
skip 'until Rule added'
|
277
|
-
|
278
258
|
alpha = Predicate.new 'alpha'
|
279
259
|
|
280
260
|
alpha.('p','q').falicy!
|
@@ -284,7 +264,7 @@ describe 'Porolog' do
|
|
284
264
|
assert_equal ' alpha("p","q"):- false', alpha.rules.first.inspect
|
285
265
|
assert_equal 'alpha:- alpha("p","q"):- false', alpha.inspect
|
286
266
|
|
287
|
-
|
267
|
+
assert_Predicate alpha, :alpha, [alpha.rules.first]
|
288
268
|
end
|
289
269
|
|
290
270
|
end
|
@@ -298,8 +278,6 @@ describe 'Porolog' do
|
|
298
278
|
end
|
299
279
|
|
300
280
|
it 'should return a summary of the predicate with rules' do
|
301
|
-
skip 'until Rule added'
|
302
|
-
|
303
281
|
alpha = Predicate.new 'alpha'
|
304
282
|
|
305
283
|
alpha.(:x,:y) << [
|
@@ -316,8 +294,6 @@ describe 'Porolog' do
|
|
316
294
|
describe '#<<' do
|
317
295
|
|
318
296
|
it 'should add new rules to a predicate' do
|
319
|
-
skip 'until Rule added'
|
320
|
-
|
321
297
|
alpha = Predicate.new 'alpha'
|
322
298
|
|
323
299
|
alpha.(:P,:Q) << [
|
@@ -333,8 +309,8 @@ describe 'Porolog' do
|
|
333
309
|
assert_instance_of Array, alpha.rules.first.definition
|
334
310
|
assert_equal 2, alpha.rules.first.definition.size
|
335
311
|
|
336
|
-
|
337
|
-
|
312
|
+
assert_Arguments alpha.rules.first.definition[0], :alpha, [:P,:Q]
|
313
|
+
assert_Arguments alpha.rules.first.definition[1], :alpha, [:Q,:P]
|
338
314
|
|
339
315
|
assert_equal ' alpha(:P,:Q):- [alpha(:P,:Q), alpha(:Q,:P)]', alpha.rules.first.inspect
|
340
316
|
assert_equal [
|
@@ -343,7 +319,85 @@ describe 'Porolog' do
|
|
343
319
|
' alpha(:P,:Q):- [alpha(:P,:P), alpha(:Q,:Q)]',
|
344
320
|
].join("\n"), alpha.inspect
|
345
321
|
|
346
|
-
|
322
|
+
assert_Predicate alpha, :alpha, [alpha.rules[0],alpha.rules[1]]
|
323
|
+
end
|
324
|
+
|
325
|
+
end
|
326
|
+
|
327
|
+
describe '#builtin?' do
|
328
|
+
|
329
|
+
it 'should return false for normal predicates' do
|
330
|
+
p = predicate :normal
|
331
|
+
|
332
|
+
assert_equal false, p.builtin?
|
333
|
+
end
|
334
|
+
|
335
|
+
it 'should return true for builtin predicates' do
|
336
|
+
p = builtin :append
|
337
|
+
|
338
|
+
assert_equal true, p.builtin?
|
339
|
+
end
|
340
|
+
|
341
|
+
end
|
342
|
+
|
343
|
+
describe '#satisfy_builtin' do
|
344
|
+
|
345
|
+
module Porolog
|
346
|
+
class Predicate
|
347
|
+
module Builtin
|
348
|
+
def user_defined(goal, block, *args, &arg_block)
|
349
|
+
puts 'Called user_defined'
|
350
|
+
block.call(goal, *args) || false
|
351
|
+
end
|
352
|
+
end
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
356
|
+
let(:predicate1) { Predicate.new :user_defined, builtin: true }
|
357
|
+
let(:arguments1) { predicate1.arguments(:m,:n) }
|
358
|
+
let(:goal) { arguments1.goal }
|
359
|
+
|
360
|
+
it 'should satisfy builtin predicate' do
|
361
|
+
called = false
|
362
|
+
|
363
|
+
assert_output "Called user_defined\n" do
|
364
|
+
predicate1.satisfy_builtin(goal) {|*args|
|
365
|
+
called = args
|
366
|
+
}
|
367
|
+
end
|
368
|
+
|
369
|
+
assert_equal [goal, goal[:m], goal[:n]], called
|
370
|
+
end
|
371
|
+
|
372
|
+
end
|
373
|
+
|
374
|
+
describe '.call_builtin' do
|
375
|
+
|
376
|
+
it 'should raise an exception when no such builtin predicate exists' do
|
377
|
+
assert_raises NameError do
|
378
|
+
Predicate.call_builtin(:non_existent)
|
379
|
+
end
|
380
|
+
|
381
|
+
end
|
382
|
+
|
383
|
+
it 'should call a builtin predicate' do
|
384
|
+
module Porolog
|
385
|
+
class Predicate
|
386
|
+
module Builtin
|
387
|
+
def custom(goal, block, *args, &arg_block)
|
388
|
+
block.call(goal, *args) || false
|
389
|
+
end
|
390
|
+
end
|
391
|
+
end
|
392
|
+
end
|
393
|
+
called = false
|
394
|
+
|
395
|
+
goal = new_goal :a, :b, :c
|
396
|
+
block = ->(goal, *args){
|
397
|
+
called = [goal] + args
|
398
|
+
}
|
399
|
+
Porolog::Predicate.call_builtin(:custom, goal, block, goal[:X], [1,2,3]) { 1 }
|
400
|
+
assert_equal [goal, goal[:X], [1,2,3]], called
|
347
401
|
end
|
348
402
|
|
349
403
|
end
|
@@ -0,0 +1,527 @@
|
|
1
|
+
#
|
2
|
+
# test/porolog/rule_test.rb - Test Suite for Porolog::Rule
|
3
|
+
#
|
4
|
+
# Luis Esteban 2 May 2018
|
5
|
+
# created
|
6
|
+
#
|
7
|
+
|
8
|
+
|
9
|
+
require_relative '../test_helper'
|
10
|
+
|
11
|
+
describe 'Porolog' do
|
12
|
+
|
13
|
+
before(:all) do
|
14
|
+
reset
|
15
|
+
end
|
16
|
+
|
17
|
+
describe 'Rule' do
|
18
|
+
|
19
|
+
describe '.reset' do
|
20
|
+
|
21
|
+
it 'should delete/unregister all rules' do
|
22
|
+
pred = Predicate.new :pred
|
23
|
+
args = Arguments.new pred, [1,2,3]
|
24
|
+
|
25
|
+
rule1 = Rule.new args, [1,2]
|
26
|
+
rule2 = Rule.new args, [3,4,5]
|
27
|
+
rule3 = Rule.new args, [6]
|
28
|
+
|
29
|
+
assert_equal 'Rule1', rule1.myid
|
30
|
+
assert_equal 'Rule2', rule2.myid
|
31
|
+
assert_equal 'Rule3', rule3.myid
|
32
|
+
|
33
|
+
Rule.reset
|
34
|
+
|
35
|
+
rule4 = Rule.new args, [7,8,9,0]
|
36
|
+
rule5 = Rule.new args, [:CUT,false]
|
37
|
+
|
38
|
+
assert_equal 'Rule-999', rule1.myid
|
39
|
+
assert_equal 'Rule-999', rule2.myid
|
40
|
+
assert_equal 'Rule-999', rule3.myid
|
41
|
+
assert_equal 'Rule1', rule4.myid
|
42
|
+
assert_equal 'Rule2', rule5.myid
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
describe '.new' do
|
48
|
+
|
49
|
+
it 'should create a new rule' do
|
50
|
+
pred = Predicate.new :pred
|
51
|
+
args = Arguments.new pred, [1,2,3]
|
52
|
+
defn = [true]
|
53
|
+
rule = Rule.new args, defn
|
54
|
+
|
55
|
+
assert_Rule rule, :pred, [1,2,3], [true]
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
describe '#initialize' do
|
61
|
+
|
62
|
+
it 'should initialize arguments and definition' do
|
63
|
+
pred = Predicate.new :pred
|
64
|
+
args = Arguments.new pred, [1,2,3]
|
65
|
+
defn = [:CUT,false]
|
66
|
+
rule = Rule.new args, defn
|
67
|
+
|
68
|
+
assert_Rule rule, :pred, [1,2,3], [:CUT,false]
|
69
|
+
assert_equal args, rule.arguments
|
70
|
+
assert_equal defn, rule.definition
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
describe '#myid' do
|
76
|
+
|
77
|
+
it 'should show the rule index' do
|
78
|
+
pred = Predicate.new :pred
|
79
|
+
args = Arguments.new pred, [1,2,3]
|
80
|
+
defn = [:CUT,false]
|
81
|
+
rule = Rule.new args, defn
|
82
|
+
|
83
|
+
assert_equal 'Rule1', rule.myid
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'should show -999 when deleted/unregistered' do
|
87
|
+
pred = Predicate.new :pred
|
88
|
+
args = Arguments.new pred, [1,2,3]
|
89
|
+
defn = [:CUT,false]
|
90
|
+
rule = Rule.new args, defn
|
91
|
+
|
92
|
+
Rule.reset
|
93
|
+
|
94
|
+
assert_equal 'Rule-999', rule.myid
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
describe '#inspect' do
|
100
|
+
|
101
|
+
it 'should show the predicate, arguments, and definition' do
|
102
|
+
pred = Predicate.new :pred
|
103
|
+
args = Arguments.new pred, [1,2,3]
|
104
|
+
defn = [:CUT,false]
|
105
|
+
rule = Rule.new args, defn
|
106
|
+
|
107
|
+
assert_equal ' pred(1,2,3):- [:CUT, false]', rule.inspect
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
111
|
+
|
112
|
+
describe '#satisfy' do
|
113
|
+
|
114
|
+
it 'should create a subgoal and unify with the goal' do
|
115
|
+
pred = Predicate.new :pred
|
116
|
+
args = Arguments.new pred, [:a,:b,:c]
|
117
|
+
defn = [true]
|
118
|
+
rule = Rule.new args, defn
|
119
|
+
|
120
|
+
goal = new_goal :pred, :x, :y, :z
|
121
|
+
|
122
|
+
called = false
|
123
|
+
rule.satisfy(goal) do |solution_goal|
|
124
|
+
called = true
|
125
|
+
|
126
|
+
assert_Goal solution_goal, :pred, [:a, :b, :c].map{|name| solution_goal.variable(name) }
|
127
|
+
assert_Goal_variables solution_goal, { a: nil, b: nil, c: nil }, [
|
128
|
+
'Goal2.:a',
|
129
|
+
' Goal1.:x',
|
130
|
+
'Goal2.:b',
|
131
|
+
' Goal1.:y',
|
132
|
+
'Goal2.:c',
|
133
|
+
' Goal1.:z',
|
134
|
+
].join("\n")
|
135
|
+
|
136
|
+
assert_equal goal, solution_goal.calling_goal
|
137
|
+
end
|
138
|
+
|
139
|
+
assert called, 'the satisfy block should be called'
|
140
|
+
end
|
141
|
+
|
142
|
+
it 'should delete the subgoal even if it does not unify with the goal' do
|
143
|
+
pred = Predicate.new :alpha
|
144
|
+
args = Arguments.new pred, [1,2,3,4]
|
145
|
+
defn = [true]
|
146
|
+
rule = Rule.new args, defn
|
147
|
+
|
148
|
+
goal = new_goal :beta, :x, :y
|
149
|
+
|
150
|
+
subgoal = new_goal :gamma, :x, :y
|
151
|
+
subgoal.expects(:delete!).with().times(1)
|
152
|
+
subgoal_spy = Spy.on(Goal, :new).and_return(subgoal)
|
153
|
+
called = false
|
154
|
+
|
155
|
+
rule.satisfy(goal) do |solution_goal|
|
156
|
+
#:nocov:
|
157
|
+
called = true
|
158
|
+
#:nocov:
|
159
|
+
end
|
160
|
+
refute called, 'the satisfy block should not be called'
|
161
|
+
assert_equal 1, subgoal_spy.calls.size
|
162
|
+
end
|
163
|
+
|
164
|
+
end
|
165
|
+
|
166
|
+
describe '#satisfy_definition' do
|
167
|
+
|
168
|
+
it 'should call block with subgoal and return true when the definition is true' do
|
169
|
+
pred = Predicate.new :normal
|
170
|
+
args = Arguments.new pred, [12]
|
171
|
+
rule = Rule.new args, true
|
172
|
+
|
173
|
+
check = 56
|
174
|
+
result = rule.satisfy_definition(12, 34) do |subgoal|
|
175
|
+
assert_equal 34, subgoal
|
176
|
+
check = 78
|
177
|
+
end
|
178
|
+
assert_equal true, result
|
179
|
+
assert_equal 78, check
|
180
|
+
end
|
181
|
+
|
182
|
+
it 'should not call block but return false when the definition is false' do
|
183
|
+
pred = Predicate.new :negative
|
184
|
+
args = Arguments.new pred, [12]
|
185
|
+
rule = Rule.new args, false
|
186
|
+
|
187
|
+
check = 56
|
188
|
+
result = rule.satisfy_definition(12, 34) do |subgoal|
|
189
|
+
#:nocov:
|
190
|
+
check = 78
|
191
|
+
#:nocov:
|
192
|
+
end
|
193
|
+
assert_equal false, result
|
194
|
+
assert_equal 56, check
|
195
|
+
end
|
196
|
+
|
197
|
+
it 'should call block with subgoal when the definition is an Array' do
|
198
|
+
pred1 = Predicate.new :success
|
199
|
+
args1 = Arguments.new pred1, [:any]
|
200
|
+
Rule.new args1, true
|
201
|
+
|
202
|
+
pred2 = Predicate.new :conjunction
|
203
|
+
args2 = Arguments.new pred2, [12]
|
204
|
+
rule2 = Rule.new args2, [true,true,true]
|
205
|
+
|
206
|
+
check = 56
|
207
|
+
result = rule2.satisfy_definition(args1.goal, args2.goal) do |subgoal|
|
208
|
+
check = 78
|
209
|
+
end
|
210
|
+
assert_equal true, result
|
211
|
+
assert_equal 78, check
|
212
|
+
end
|
213
|
+
|
214
|
+
it 'should raise an exception when the definition is unknown' do
|
215
|
+
predicate :strange
|
216
|
+
|
217
|
+
strange(12) << 3.4
|
218
|
+
|
219
|
+
exception = assert_raises Rule::DefinitionError do
|
220
|
+
strange(:X).solve
|
221
|
+
end
|
222
|
+
assert_equal 'UNEXPECTED TYPE OF DEFINITION: 3.4 (Float)', exception.message
|
223
|
+
end
|
224
|
+
|
225
|
+
end
|
226
|
+
|
227
|
+
describe '#satisfy_conjunction' do
|
228
|
+
|
229
|
+
let(:pred1 ) { Predicate.new :parent }
|
230
|
+
let(:pred2 ) { Predicate.new :child }
|
231
|
+
let(:args1 ) { Arguments.new pred1, [1,2,3,4] }
|
232
|
+
let(:args2 ) { Arguments.new pred2, [:x,:y] }
|
233
|
+
let(:goal ) { Goal.new args1, nil }
|
234
|
+
let(:subgoal) { Goal.new args2, goal }
|
235
|
+
|
236
|
+
it 'should handle CUT expression' do
|
237
|
+
rule = Rule.new args1, [:CUT,true]
|
238
|
+
|
239
|
+
goal.expects(:terminate!).with().times(1)
|
240
|
+
rule_spy = Spy.on(rule, :satisfy_conjunction).and_call_through
|
241
|
+
called = false
|
242
|
+
|
243
|
+
result = rule.satisfy_conjunction(goal, subgoal, rule.definition) do |solution_goal|
|
244
|
+
called = true
|
245
|
+
end
|
246
|
+
|
247
|
+
assert result, 'satisfy_conjunction should succeed'
|
248
|
+
assert called, 'the satisfy block should be called'
|
249
|
+
assert_equal 2, rule_spy.calls.size
|
250
|
+
assert_equal [goal, subgoal, [true]], rule_spy.calls[0].args # innermost call; finishes first
|
251
|
+
assert_equal [goal, subgoal, [:CUT,true]], rule_spy.calls[1].args # outermost call; finishes last
|
252
|
+
end
|
253
|
+
|
254
|
+
it 'should handle CUT expression at the end of a conjunction' do
|
255
|
+
rule = Rule.new args1, [:CUT]
|
256
|
+
|
257
|
+
goal.expects(:terminate!).with().times(1)
|
258
|
+
rule_spy = Spy.on(rule, :satisfy_conjunction).and_call_through
|
259
|
+
called = false
|
260
|
+
|
261
|
+
result = rule.satisfy_conjunction(goal, subgoal, rule.definition) do |solution_goal|
|
262
|
+
called = true
|
263
|
+
end
|
264
|
+
|
265
|
+
assert result, 'satisfy_conjunction should succeed'
|
266
|
+
assert called, 'the satisfy block should be called'
|
267
|
+
assert_equal 1, rule_spy.calls.size
|
268
|
+
assert_equal [goal, subgoal, [:CUT]], rule_spy.calls[0].args
|
269
|
+
end
|
270
|
+
|
271
|
+
it 'should handle true expression' do
|
272
|
+
rule = Rule.new args1, [true]
|
273
|
+
|
274
|
+
goal.expects(:terminate!).with().times(0)
|
275
|
+
rule_spy = Spy.on(rule, :satisfy_conjunction).and_call_through
|
276
|
+
called = false
|
277
|
+
|
278
|
+
result = rule.satisfy_conjunction(goal, subgoal, rule.definition) do |solution_goal|
|
279
|
+
called = true
|
280
|
+
end
|
281
|
+
|
282
|
+
assert result, 'satisfy_conjunction should succeed'
|
283
|
+
assert called, 'the satisfy block should be called'
|
284
|
+
assert_equal 1, rule_spy.calls.size
|
285
|
+
assert_equal [goal, subgoal, [true]], rule_spy.calls[0].args
|
286
|
+
end
|
287
|
+
|
288
|
+
it 'should handle false expression' do
|
289
|
+
rule = Rule.new args1, [false]
|
290
|
+
|
291
|
+
goal.expects(:terminate!).with().times(0)
|
292
|
+
rule_spy = Spy.on(rule, :satisfy_conjunction).and_call_through
|
293
|
+
called = false
|
294
|
+
|
295
|
+
result = rule.satisfy_conjunction(goal, subgoal, rule.definition) do |solution_goal|
|
296
|
+
#:nocov:
|
297
|
+
called = true
|
298
|
+
#:nocov:
|
299
|
+
end
|
300
|
+
|
301
|
+
refute result, 'satisfy_conjunction should not succeed'
|
302
|
+
refute called, 'the satisfy block should not be called'
|
303
|
+
assert_equal 1, rule_spy.calls.size
|
304
|
+
assert_equal [goal, subgoal, [false]], rule_spy.calls[0].args
|
305
|
+
end
|
306
|
+
|
307
|
+
it 'should handle nil expression' do
|
308
|
+
rule = Rule.new args1, []
|
309
|
+
|
310
|
+
goal.expects(:terminate!).with().times(0)
|
311
|
+
rule_spy = Spy.on(rule, :satisfy_conjunction).and_call_through
|
312
|
+
called = false
|
313
|
+
|
314
|
+
result = rule.satisfy_conjunction(goal, subgoal, rule.definition) do |solution_goal|
|
315
|
+
#:nocov:
|
316
|
+
called = true
|
317
|
+
#:nocov:
|
318
|
+
end
|
319
|
+
|
320
|
+
assert result, 'satisfy_conjunction should not succeed'
|
321
|
+
assert called, 'the satisfy block should not be called'
|
322
|
+
assert_equal 1, rule_spy.calls.size
|
323
|
+
assert_equal [goal, subgoal, []], rule_spy.calls[0].args
|
324
|
+
end
|
325
|
+
|
326
|
+
it 'should evaluate conjunctions until a fail' do
|
327
|
+
conjunction = [true, true, true, false, true, :CUT, true]
|
328
|
+
rule = Rule.new args1, conjunction
|
329
|
+
|
330
|
+
goal.expects(:terminate!).with().times(0)
|
331
|
+
rule_spy = Spy.on(rule, :satisfy_conjunction).and_call_through
|
332
|
+
called = false
|
333
|
+
|
334
|
+
result = rule.satisfy_conjunction(goal, subgoal, rule.definition) do |solution_goal|
|
335
|
+
#:nocov:
|
336
|
+
called = true
|
337
|
+
#:nocov:
|
338
|
+
end
|
339
|
+
|
340
|
+
refute result, 'satisfy_conjunction should not succeed'
|
341
|
+
refute called, 'the satisfy block should not be called'
|
342
|
+
assert_equal 4, rule_spy.calls.size
|
343
|
+
assert_equal [goal, subgoal, conjunction[3..-1]], rule_spy.calls[0].args
|
344
|
+
assert_equal [goal, subgoal, conjunction[2..-1]], rule_spy.calls[1].args
|
345
|
+
assert_equal [goal, subgoal, conjunction[1..-1]], rule_spy.calls[2].args
|
346
|
+
assert_equal [goal, subgoal, conjunction[0..-1]], rule_spy.calls[3].args
|
347
|
+
end
|
348
|
+
|
349
|
+
it 'should unify and instantiate variables with a subgoal and satisfy the subgoal and uninstantiate the instantiations' do
|
350
|
+
goal = Goal.new args1, nil
|
351
|
+
subgoal = Goal.new args2, goal
|
352
|
+
|
353
|
+
predicate :gamma
|
354
|
+
|
355
|
+
gamma(8,5).fact!
|
356
|
+
|
357
|
+
rule = Rule.new args1, [gamma(:j,:k)]
|
358
|
+
|
359
|
+
subsubgoal = new_goal :gamma, :y, :z
|
360
|
+
subsubgoal_spy = Spy.on(Goal, :new).and_return do |*args|
|
361
|
+
Spy.off(Goal, :new)
|
362
|
+
subsubgoal
|
363
|
+
end
|
364
|
+
|
365
|
+
goal.expects(:terminate!).with().times(0)
|
366
|
+
subgoal.expects(:terminate!).with().times(0)
|
367
|
+
subsubgoal.expects(:terminate!).with().times(0)
|
368
|
+
|
369
|
+
goal.expects(:delete!).with().times(0)
|
370
|
+
subgoal.expects(:delete!).with().times(0)
|
371
|
+
subsubgoal.expects(:delete!).with().times(1)
|
372
|
+
|
373
|
+
rule_spy = Spy.on(rule, :satisfy_conjunction).and_call_through
|
374
|
+
called = false
|
375
|
+
|
376
|
+
result = rule.satisfy_conjunction(goal, subgoal, rule.definition) do |solution_goal|
|
377
|
+
called = true
|
378
|
+
assert_Goal goal, :parent, [1,2,3,4]
|
379
|
+
assert_Goal_variables goal, {}, ''
|
380
|
+
assert_Goal_variables subgoal, { x: nil, y: 8, z: 5 }, [
|
381
|
+
'Goal2.:x',
|
382
|
+
' Goal3.:x',
|
383
|
+
'Goal2.:y',
|
384
|
+
' Goal3.:y',
|
385
|
+
' Goal4.8',
|
386
|
+
'Goal2.:z',
|
387
|
+
' Goal3.:z',
|
388
|
+
' Goal4.5',
|
389
|
+
].join("\n")
|
390
|
+
assert_Goal_variables subsubgoal, { x: nil, y: 8, z: 5 }, [
|
391
|
+
'Goal3.:y',
|
392
|
+
' Goal2.:y',
|
393
|
+
' Goal4.8',
|
394
|
+
'Goal3.:z',
|
395
|
+
' Goal2.:z',
|
396
|
+
' Goal4.5',
|
397
|
+
'Goal3.:x',
|
398
|
+
' Goal2.:x',
|
399
|
+
].join("\n")
|
400
|
+
end
|
401
|
+
|
402
|
+
assert result, 'satisfy_conjunction should succeed'
|
403
|
+
assert called, 'the satisfy block should be called'
|
404
|
+
assert_equal 1, subsubgoal_spy.calls.size
|
405
|
+
assert_equal 1, rule_spy.calls.size
|
406
|
+
assert_equal [goal, subgoal, [gamma(:j,:k)]], rule_spy.calls[0].args
|
407
|
+
|
408
|
+
assert_Goal_variables subgoal, { x: nil, y: nil, z: nil }, [
|
409
|
+
'Goal2.:x',
|
410
|
+
' Goal3.:x',
|
411
|
+
'Goal2.:y',
|
412
|
+
' Goal3.:y',
|
413
|
+
'Goal2.:z',
|
414
|
+
' Goal3.:z',
|
415
|
+
].join("\n")
|
416
|
+
assert_Goal_variables subsubgoal, { x: nil, y: nil, z: nil }, [
|
417
|
+
'Goal3.:y',
|
418
|
+
' Goal2.:y',
|
419
|
+
'Goal3.:z',
|
420
|
+
' Goal2.:z',
|
421
|
+
'Goal3.:x',
|
422
|
+
' Goal2.:x',
|
423
|
+
].join("\n")
|
424
|
+
end
|
425
|
+
|
426
|
+
it 'should not unify and not instantiate variables with a subgoal nor satisfy the subgoal when it cannot be unified' do
|
427
|
+
# -- Create Goals --
|
428
|
+
goal = Goal.new args1, nil
|
429
|
+
subgoal = Goal.new args2, goal
|
430
|
+
subgoal.instantiate :y, 7
|
431
|
+
|
432
|
+
# -- Create goal for satisfy_conjunction() --
|
433
|
+
subsubgoal = new_goal :gamma, :y, :y, :z
|
434
|
+
subsubgoal.instantiate :y, 3
|
435
|
+
|
436
|
+
# -- Check goals are setup correctly --
|
437
|
+
assert_Goal goal, :parent, [1,2,3,4]
|
438
|
+
assert_Goal subgoal, :child, [:x,:y]
|
439
|
+
assert_equal 'Goal1 -- Solve parent(1,2,3,4)', goal.inspect
|
440
|
+
assert_equal 'Goal2 -- Solve child(:x,:y)', subgoal.inspect
|
441
|
+
|
442
|
+
assert_Goal_variables goal, {}, [].join("\n")
|
443
|
+
|
444
|
+
assert_Goal_variables subgoal, { x: nil, y: 7 }, [
|
445
|
+
'Goal2.:x',
|
446
|
+
'Goal2.:y',
|
447
|
+
' Goal2.7',
|
448
|
+
].join("\n")
|
449
|
+
|
450
|
+
assert_Goal_variables subsubgoal, { y: 3, z: nil }, [
|
451
|
+
'Goal3.:y',
|
452
|
+
' Goal3.3',
|
453
|
+
'Goal3.:z',
|
454
|
+
].join("\n")
|
455
|
+
|
456
|
+
# -- Define Predicate --
|
457
|
+
predicate :gamma
|
458
|
+
|
459
|
+
gamma(8,5).fact!
|
460
|
+
|
461
|
+
rule = Rule.new args1, [gamma(:j,:k)]
|
462
|
+
|
463
|
+
# -- Prepare Checks for satisfy_conjunction() --
|
464
|
+
subsubgoal_spy = Spy.on(Goal, :new).and_return do |*args|
|
465
|
+
Spy.off(Goal, :new)
|
466
|
+
subsubgoal
|
467
|
+
end
|
468
|
+
|
469
|
+
goal. expects(:terminate!).with().times(0)
|
470
|
+
subgoal. expects(:terminate!).with().times(0)
|
471
|
+
subsubgoal.expects(:terminate!).with().times(0)
|
472
|
+
|
473
|
+
goal. expects(:delete!).with().times(0)
|
474
|
+
subgoal. expects(:delete!).with().times(0)
|
475
|
+
subsubgoal.expects(:delete!).with().times(1)
|
476
|
+
|
477
|
+
rule_spy = Spy.on(rule, :satisfy_conjunction).and_call_through
|
478
|
+
called = false
|
479
|
+
|
480
|
+
# -- Pre-execution Checks --
|
481
|
+
assert_Goal_variables goal, {}, [].join("\n")
|
482
|
+
assert_Goal_variables subgoal, { x: nil, y: 7 }, [
|
483
|
+
'Goal2.:x',
|
484
|
+
'Goal2.:y',
|
485
|
+
' Goal2.7',
|
486
|
+
].join("\n")
|
487
|
+
assert_Goal_variables subsubgoal, { y: 3, z: nil }, [
|
488
|
+
'Goal3.:y',
|
489
|
+
' Goal3.3',
|
490
|
+
'Goal3.:z',
|
491
|
+
].join("\n")
|
492
|
+
|
493
|
+
# -- Execute Code --
|
494
|
+
result = rule.satisfy_conjunction(goal, subgoal, rule.definition) do |solution_goal|
|
495
|
+
#:nocov:
|
496
|
+
called = true
|
497
|
+
#:nocov:
|
498
|
+
end
|
499
|
+
|
500
|
+
# -- Peform Checks --
|
501
|
+
refute result, 'satisfy_conjunction should not succeed'
|
502
|
+
refute called, 'the satisfy block should not be called'
|
503
|
+
assert_equal 1, subsubgoal_spy.calls.size
|
504
|
+
assert_equal 1, rule_spy.calls.size
|
505
|
+
assert_equal [goal, subgoal, [gamma(:j,:k)]], rule_spy.calls[0].args
|
506
|
+
|
507
|
+
assert_Goal_variables goal, {}, [].join("\n")
|
508
|
+
|
509
|
+
assert_Goal_variables subgoal, { x: nil, y: 7 }, [
|
510
|
+
'Goal2.:x',
|
511
|
+
'Goal2.:y',
|
512
|
+
' Goal2.7',
|
513
|
+
].join("\n")
|
514
|
+
|
515
|
+
assert_Goal_variables subsubgoal, { x: nil, y: 3, z: nil }, [
|
516
|
+
'Goal3.:y',
|
517
|
+
' Goal3.3',
|
518
|
+
'Goal3.:z',
|
519
|
+
'Goal3.:x',
|
520
|
+
].join("\n")
|
521
|
+
end
|
522
|
+
|
523
|
+
end
|
524
|
+
|
525
|
+
end
|
526
|
+
|
527
|
+
end
|