porolog 0.0.7 → 0.0.8

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.
@@ -172,7 +172,7 @@ describe 'Porolog' do
172
172
 
173
173
  it 'can create predicates in different scopes' do
174
174
  left = Scope.new :left
175
- #right = Scope.new :right # Not explicitly creating scope :right
175
+ #right = Scope.new :right # Not explicitly creating scope :right
176
176
 
177
177
  assert_equal left, Predicate.scope(:left)
178
178
  assert_equal :left, Predicate.scope.name
@@ -5,6 +5,7 @@
5
5
  # created
6
6
  #
7
7
 
8
+
8
9
  require_relative '../test_helper'
9
10
 
10
11
  describe 'Porolog' do
@@ -108,6 +109,400 @@ describe 'Porolog' do
108
109
 
109
110
  end
110
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 evaluate conjunctions until a fail' do
308
+ conjunction = [true, true, true, false, true, :CUT, true]
309
+ rule = Rule.new args1, conjunction
310
+
311
+ goal.expects(:terminate!).with().times(0)
312
+ rule_spy = Spy.on(rule, :satisfy_conjunction).and_call_through
313
+ called = false
314
+
315
+ result = rule.satisfy_conjunction(goal, subgoal, rule.definition) do |solution_goal|
316
+ #:nocov:
317
+ called = true
318
+ #:nocov:
319
+ end
320
+
321
+ refute result, 'satisfy_conjunction should not succeed'
322
+ refute called, 'the satisfy block should not be called'
323
+ assert_equal 4, rule_spy.calls.size
324
+ assert_equal [goal, subgoal, conjunction[3..-1]], rule_spy.calls[0].args
325
+ assert_equal [goal, subgoal, conjunction[2..-1]], rule_spy.calls[1].args
326
+ assert_equal [goal, subgoal, conjunction[1..-1]], rule_spy.calls[2].args
327
+ assert_equal [goal, subgoal, conjunction[0..-1]], rule_spy.calls[3].args
328
+ end
329
+
330
+ it 'should unify and instantiate variables with a subgoal and satisfy the subgoal and uninstantiate the instantiations' do
331
+ goal = Goal.new args1, nil
332
+ subgoal = Goal.new args2, goal
333
+
334
+ predicate :gamma
335
+
336
+ gamma(8,5).fact!
337
+
338
+ rule = Rule.new args1, [gamma(:j,:k)]
339
+
340
+ subsubgoal = new_goal :gamma, :y, :z
341
+ subsubgoal_spy = Spy.on(Goal, :new).and_return do |*args|
342
+ Spy.off(Goal, :new)
343
+ subsubgoal
344
+ end
345
+
346
+ goal.expects(:terminate!).with().times(0)
347
+ subgoal.expects(:terminate!).with().times(0)
348
+ subsubgoal.expects(:terminate!).with().times(0)
349
+
350
+ goal.expects(:delete!).with().times(0)
351
+ subgoal.expects(:delete!).with().times(0)
352
+ subsubgoal.expects(:delete!).with().times(1)
353
+
354
+ rule_spy = Spy.on(rule, :satisfy_conjunction).and_call_through
355
+ called = false
356
+
357
+ result = rule.satisfy_conjunction(goal, subgoal, rule.definition) do |solution_goal|
358
+ called = true
359
+ assert_Goal goal, :parent, [1,2,3,4]
360
+ assert_Goal_variables goal, {}, ''
361
+ assert_Goal_variables subgoal, { x: nil, y: 8, z: 5 }, [
362
+ 'Goal2.:x',
363
+ ' Goal3.:x',
364
+ 'Goal2.:y',
365
+ ' Goal3.:y',
366
+ ' Goal4.8',
367
+ 'Goal2.:z',
368
+ ' Goal3.:z',
369
+ ' Goal4.5',
370
+ ].join("\n")
371
+ assert_Goal_variables subsubgoal, { x: nil, y: 8, z: 5 }, [
372
+ 'Goal3.:y',
373
+ ' Goal2.:y',
374
+ ' Goal4.8',
375
+ 'Goal3.:z',
376
+ ' Goal2.:z',
377
+ ' Goal4.5',
378
+ 'Goal3.:x',
379
+ ' Goal2.:x',
380
+ ].join("\n")
381
+ end
382
+
383
+ assert result, 'satisfy_conjunction should succeed'
384
+ assert called, 'the satisfy block should be called'
385
+ assert_equal 1, subsubgoal_spy.calls.size
386
+ assert_equal 1, rule_spy.calls.size
387
+ assert_equal [goal, subgoal, [gamma(:j,:k)]], rule_spy.calls[0].args
388
+
389
+ assert_Goal_variables subgoal, { x: nil, y: nil, z: nil }, [
390
+ 'Goal2.:x',
391
+ ' Goal3.:x',
392
+ 'Goal2.:y',
393
+ ' Goal3.:y',
394
+ 'Goal2.:z',
395
+ ' Goal3.:z',
396
+ ].join("\n")
397
+ assert_Goal_variables subsubgoal, { x: nil, y: nil, z: nil }, [
398
+ 'Goal3.:y',
399
+ ' Goal2.:y',
400
+ 'Goal3.:z',
401
+ ' Goal2.:z',
402
+ 'Goal3.:x',
403
+ ' Goal2.:x',
404
+ ].join("\n")
405
+ end
406
+
407
+ it 'should not unify and not instantiate variables with a subgoal nor satisfy the subgoal when it cannot be unified' do
408
+ # -- Create Goals --
409
+ goal = Goal.new args1, nil
410
+ subgoal = Goal.new args2, goal
411
+ subgoal.instantiate :y, 7
412
+
413
+ # -- Create goal for satisfy_conjunction() --
414
+ subsubgoal = new_goal :gamma, :y, :y, :z
415
+ subsubgoal.instantiate :y, 3
416
+
417
+ # -- Check goals are setup correctly --
418
+ assert_Goal goal, :parent, [1,2,3,4]
419
+ assert_Goal subgoal, :child, [:x,:y]
420
+ assert_equal 'Goal1 -- Solve parent(1,2,3,4)', goal.inspect
421
+ assert_equal 'Goal2 -- Solve child(:x,:y)', subgoal.inspect
422
+
423
+ assert_Goal_variables goal, {}, [].join("\n")
424
+
425
+ assert_Goal_variables subgoal, { x: nil, y: 7 }, [
426
+ 'Goal2.:x',
427
+ 'Goal2.:y',
428
+ ' Goal2.7',
429
+ ].join("\n")
430
+
431
+ assert_Goal_variables subsubgoal, { y: 3, z: nil }, [
432
+ 'Goal3.:y',
433
+ ' Goal3.3',
434
+ 'Goal3.:z',
435
+ ].join("\n")
436
+
437
+ # -- Define Predicate --
438
+ predicate :gamma
439
+
440
+ gamma(8,5).fact!
441
+
442
+ rule = Rule.new args1, [gamma(:j,:k)]
443
+
444
+ # -- Prepare Checks for satisfy_conjunction() --
445
+ subsubgoal_spy = Spy.on(Goal, :new).and_return do |*args|
446
+ Spy.off(Goal, :new)
447
+ subsubgoal
448
+ end
449
+
450
+ goal. expects(:terminate!).with().times(0)
451
+ subgoal. expects(:terminate!).with().times(0)
452
+ subsubgoal.expects(:terminate!).with().times(0)
453
+
454
+ goal. expects(:delete!).with().times(0)
455
+ subgoal. expects(:delete!).with().times(0)
456
+ subsubgoal.expects(:delete!).with().times(1)
457
+
458
+ rule_spy = Spy.on(rule, :satisfy_conjunction).and_call_through
459
+ called = false
460
+
461
+ # -- Pre-execution Checks --
462
+ assert_Goal_variables goal, {}, [].join("\n")
463
+ assert_Goal_variables subgoal, { x: nil, y: 7 }, [
464
+ 'Goal2.:x',
465
+ 'Goal2.:y',
466
+ ' Goal2.7',
467
+ ].join("\n")
468
+ assert_Goal_variables subsubgoal, { y: 3, z: nil }, [
469
+ 'Goal3.:y',
470
+ ' Goal3.3',
471
+ 'Goal3.:z',
472
+ ].join("\n")
473
+
474
+ # -- Execute Code --
475
+ result = rule.satisfy_conjunction(goal, subgoal, rule.definition) do |solution_goal|
476
+ #:nocov:
477
+ called = true
478
+ #:nocov:
479
+ end
480
+
481
+ # -- Peform Checks --
482
+ refute result, 'satisfy_conjunction should not succeed'
483
+ refute called, 'the satisfy block should not be called'
484
+ assert_equal 1, subsubgoal_spy.calls.size
485
+ assert_equal 1, rule_spy.calls.size
486
+ assert_equal [goal, subgoal, [gamma(:j,:k)]], rule_spy.calls[0].args
487
+
488
+ assert_Goal_variables goal, {}, [].join("\n")
489
+
490
+ assert_Goal_variables subgoal, { x: nil, y: 7 }, [
491
+ 'Goal2.:x',
492
+ 'Goal2.:y',
493
+ ' Goal2.7',
494
+ ].join("\n")
495
+
496
+ assert_Goal_variables subsubgoal, { x: nil, y: 3, z: nil }, [
497
+ 'Goal3.:y',
498
+ ' Goal3.3',
499
+ 'Goal3.:z',
500
+ 'Goal3.:x',
501
+ ].join("\n")
502
+ end
503
+
504
+ end
505
+
111
506
  end
112
507
 
113
508
  end
@@ -23,8 +23,6 @@ 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 CoreExt added'
27
-
28
26
  prime = prime1 = Predicate.new :prime, :first
29
27
 
30
28
  prime.(2).fact!
@@ -0,0 +1,127 @@
1
+ #
2
+ # test/porolog/tail_test.rb - Test Suite for Porolog::Tail
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 'Tail' do
17
+
18
+ describe '.new' do
19
+
20
+ it 'creates an unknown tail when no value is provided' do
21
+ tail = Tail.new
22
+
23
+ assert_Tail tail, '*...'
24
+ assert_equal UNKNOWN_TAIL, tail.value
25
+ end
26
+
27
+ it 'creates a tail with a value' do
28
+ tail = Tail.new [2,3,5,7,11,13]
29
+
30
+ assert_Tail tail, '*[2, 3, 5, 7, 11, 13]'
31
+ assert_equal [2,3,5,7,11,13], tail.value
32
+ end
33
+
34
+ it 'creates a tail with a variable' do
35
+ tail = Tail.new :x
36
+
37
+ assert_Tail tail, '*:x'
38
+ assert_equal :x, tail.value
39
+ end
40
+
41
+ end
42
+
43
+ describe '#value' do
44
+
45
+ let(:object) { Object.new }
46
+
47
+ before do
48
+ def object.inspect
49
+ super.gsub(/Object:0x[[:xdigit:]]+/,'Object:0xXXXXXX')
50
+ end
51
+ end
52
+
53
+ it 'returns its value' do
54
+ tail = Tail.new object
55
+
56
+ assert_Tail tail, '*#<Object:0xXXXXXX>'
57
+ assert_equal object, tail.value
58
+ end
59
+
60
+ end
61
+
62
+ describe '#inspect' do
63
+
64
+ it 'returns a string showing a splat operation is implied' do
65
+ tail = Tail.new [2,3,5,7,11,13]
66
+
67
+ assert_equal '*[2, 3, 5, 7, 11, 13]', tail.inspect
68
+ end
69
+
70
+ end
71
+
72
+ describe '#variables' do
73
+
74
+ it 'returns an empty Array when it was created without arguments' do
75
+ tail = Tail.new
76
+
77
+ assert_equal [], tail.variables
78
+ end
79
+
80
+ it 'returns an empty Array when it was created with an Array of atomics' do
81
+ tail = Tail.new [2,3,5,7,11,13]
82
+
83
+ assert_equal [], tail.variables
84
+ end
85
+
86
+ it 'returns an Array of Symbols when it was created with a Symbol' do
87
+ tail = Tail.new :t
88
+
89
+ assert_equal [:t], tail.variables
90
+ end
91
+
92
+ it 'returns an Array of Symbols when it was created with an Array with embedded Symbols' do
93
+ tail = Tail.new [2,3,:x,7,[:y,[:z]],13]
94
+
95
+ assert_equal [:x,:y,:z], tail.variables
96
+ end
97
+
98
+ end
99
+
100
+ describe '#==' do
101
+
102
+ it 'returns true for unknown tails' do
103
+ tail1 = Tail.new
104
+ tail2 = Tail.new
105
+
106
+ assert tail1 == tail2, name
107
+ end
108
+
109
+ it 'returns false for different symbols' do
110
+ tail1 = Tail.new :t
111
+ tail2 = Tail.new :x
112
+
113
+ refute tail1 == tail2, name
114
+ end
115
+
116
+ it 'returns true for equal values' do
117
+ tail1 = Tail.new 12.34
118
+ tail2 = Tail.new 12.34
119
+
120
+ assert tail1 == tail2, name
121
+ end
122
+
123
+ end
124
+
125
+ end
126
+
127
+ end