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.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +30 -5
  3. data/Rakefile +7 -2
  4. data/bin/porolog +58 -1
  5. data/coverage/badge.svg +1 -1
  6. data/coverage/index.html +76733 -2638
  7. data/doc/Array.html +1066 -0
  8. data/doc/Object.html +674 -0
  9. data/doc/Porolog.html +4153 -74
  10. data/doc/Symbol.html +501 -0
  11. data/doc/_index.html +280 -6
  12. data/doc/class_list.html +1 -1
  13. data/doc/file.README.html +34 -39
  14. data/doc/index.html +34 -39
  15. data/doc/method_list.html +1337 -57
  16. data/doc/top-level-namespace.html +4 -2
  17. data/lib/porolog.rb +1144 -4
  18. data/lib/porolog/arguments.rb +28 -24
  19. data/lib/porolog/core_ext.rb +188 -0
  20. data/lib/porolog/error.rb +9 -0
  21. data/lib/porolog/goal.rb +357 -0
  22. data/lib/porolog/instantiation.rb +346 -0
  23. data/lib/porolog/predicate.rb +74 -31
  24. data/lib/porolog/predicate/builtin.rb +825 -0
  25. data/lib/porolog/rule.rb +162 -0
  26. data/lib/porolog/scope.rb +4 -4
  27. data/lib/porolog/tail.rb +57 -0
  28. data/lib/porolog/value.rb +105 -0
  29. data/lib/porolog/variable.rb +325 -0
  30. data/test/porolog/arguments_test.rb +244 -195
  31. data/test/porolog/core_ext_test.rb +290 -0
  32. data/test/porolog/goal_test.rb +891 -0
  33. data/test/porolog/instantiation_test.rb +910 -0
  34. data/test/porolog/porolog_test.rb +2376 -13
  35. data/test/porolog/predicate/builtin_test.rb +1340 -0
  36. data/test/porolog/predicate_test.rb +84 -30
  37. data/test/porolog/rule_test.rb +527 -0
  38. data/test/porolog/scope_test.rb +0 -2
  39. data/test/porolog/tail_test.rb +127 -0
  40. data/test/porolog/value_test.rb +315 -0
  41. data/test/porolog/variable_test.rb +1614 -0
  42. data/test/samples_test.rb +277 -0
  43. data/test/test_helper.rb +115 -0
  44. metadata +34 -7
@@ -0,0 +1,290 @@
1
+ #
2
+ # test/porolog/core_ext_test.rb - Test Suite for Core Extensions
3
+ #
4
+ # Luis Esteban 2 May 2018
5
+ # created
6
+ #
7
+
8
+ require_relative '../test_helper'
9
+
10
+ describe 'Object' do
11
+
12
+ let (:object1) { Object.new }
13
+ let (:object2) { 42 }
14
+ let (:object3) { { name: 'hello' } }
15
+ let (:object4) { 'abc' }
16
+ let (:object5) { 0.5 }
17
+
18
+ describe '#myid' do
19
+
20
+ it 'should return inspect' do
21
+ assert_equal object1.inspect, object1.myid
22
+ assert_equal object2.inspect, object2.myid
23
+ assert_equal object3.inspect, object3.myid
24
+ assert_equal object4.inspect, object4.myid
25
+ assert_equal object5.inspect, object5.myid
26
+ end
27
+
28
+ end
29
+
30
+ describe '#variables' do
31
+
32
+ it 'should return empty array' do
33
+ assert_equal [], object1.variables
34
+ assert_equal [], object2.variables
35
+ assert_equal [], object3.variables
36
+ assert_equal [], object4.variables
37
+ assert_equal [], object5.variables
38
+ end
39
+
40
+ end
41
+
42
+ describe '#type' do
43
+
44
+ it 'should return :atomic for atomic types' do
45
+ assert_equal :atomic, object1.type
46
+ assert_equal :atomic, object2.type
47
+ assert_equal :atomic, object3.type
48
+ assert_equal :atomic, object4.type
49
+ assert_equal :atomic, object5.type
50
+ end
51
+
52
+ end
53
+
54
+ describe '#/' do
55
+
56
+ it 'should create an n Array with a Tail' do
57
+ assert_Array_with_Tail object1 / :T, [object1], '*:T'
58
+ assert_Array_with_Tail [object2] / :T, [object2], '*:T'
59
+ #assert_Array_with_Tail object2.tail(:T), [object2], '*:T'
60
+ assert_Array_with_Tail object3 / :T, [object3], '*:T'
61
+ assert_Array_with_Tail object4 / :T, [object4], '*:T'
62
+ assert_Array_with_Tail [object5] / :T, [object5], '*:T'
63
+ #assert_Array_with_Tail object5.tail(:T), [object5], '*:T'
64
+ end
65
+
66
+ end
67
+
68
+ end
69
+
70
+ describe 'Symbol' do
71
+
72
+ let (:symbol1) { :alpha }
73
+ let (:symbol2) { :_ }
74
+ let (:symbol3) { :'with some spaces' }
75
+ let (:symbol4) { :X }
76
+ let (:symbol5) { :p }
77
+
78
+ describe '#myid' do
79
+
80
+ it 'should return inspect' do
81
+ assert_equal symbol1.inspect, symbol1.myid
82
+ assert_equal symbol2.inspect, symbol2.myid
83
+ assert_equal symbol3.inspect, symbol3.myid
84
+ assert_equal symbol4.inspect, symbol4.myid
85
+ assert_equal symbol5.inspect, symbol5.myid
86
+ end
87
+
88
+ end
89
+
90
+ describe '#variables' do
91
+
92
+ it 'should return an array of itself' do
93
+ assert_equal [symbol1], symbol1.variables
94
+ assert_equal [symbol2], symbol2.variables
95
+ assert_equal [symbol3], symbol3.variables
96
+ assert_equal [symbol4], symbol4.variables
97
+ assert_equal [symbol5], symbol5.variables
98
+ end
99
+
100
+ end
101
+
102
+ describe '#type' do
103
+
104
+ it 'should return :variable for symbols' do
105
+ assert_equal :variable, symbol1.type
106
+ assert_equal :variable, symbol2.type
107
+ assert_equal :variable, symbol3.type
108
+ assert_equal :variable, symbol4.type
109
+ assert_equal :variable, symbol5.type
110
+ end
111
+
112
+ end
113
+
114
+ describe '#/' do
115
+
116
+ it 'should create a HeadTail' do
117
+ assert_Array_with_Tail symbol1 / :T, [symbol1], '*:T'
118
+ assert_Array_with_Tail symbol2 / :T, [symbol2], '*:T'
119
+ assert_Array_with_Tail symbol3 / :T, [symbol3], '*:T'
120
+ assert_Array_with_Tail symbol4 / :T, [symbol4], '*:T'
121
+ assert_Array_with_Tail symbol5 / :T, [symbol5], '*:T'
122
+ end
123
+
124
+ end
125
+
126
+ end
127
+
128
+ describe 'Array' do
129
+
130
+ let(:array1) { [1, 2, :A, 4, [:B, 6, 7, :C], 9] }
131
+ let(:array2) { [] }
132
+ let(:array3) { UNKNOWN_ARRAY }
133
+ let(:array4) { [1, :B, 3, UNKNOWN_TAIL] }
134
+
135
+ describe '#/' do
136
+
137
+ it 'creates an Array using the slash notation with Arrays' do
138
+ head_tail = [1] / [2,3,4,5]
139
+
140
+ assert_equal [1,2,3,4,5], head_tail
141
+ end
142
+
143
+ it 'should combine a head Array and a tail Array when they have no variables' do
144
+ assert_equal [1,2,3,4,5,6], [1,2,3] / [4,5,6]
145
+ end
146
+
147
+ it 'should combine an embedded head Array and an embedded tail Array when they have no variables' do
148
+ assert_equal [1,2,3,4,5,6,7,8,9], [1,2,3] / [4,5,6] / [7,8,9]
149
+ end
150
+
151
+ it 'should create a HeadTail' do
152
+ assert_Array_with_Tail array1 / :T, array1, '*:T'
153
+ assert_Array_with_Tail array2 / :T, array2, '*:T'
154
+ assert_Array_with_Tail array3 / :T, array3, '*:T'
155
+ assert_Array_with_Tail array4 / :T, array4, '*:T'
156
+ end
157
+
158
+ end
159
+
160
+ describe '#variables' do
161
+
162
+ it 'should return an array of the embedded Symbols' do
163
+ assert_equal [:A, :B, :C], array1.variables
164
+ assert_equal [], array2.variables
165
+ assert_equal [], array3.variables
166
+ assert_equal [:B], array4.variables
167
+ end
168
+
169
+ end
170
+
171
+ describe '#value' do
172
+
173
+ it 'should return simple Arrays as is' do
174
+ assert_equal [1, 2, :A, 4, [:B, 6, 7, :C], 9], array1.value
175
+ assert_equal [], array2.value
176
+ assert_equal [UNKNOWN_TAIL], array3.value
177
+ assert_equal [1, :B, 3, UNKNOWN_TAIL], array4.value
178
+ end
179
+
180
+ it 'should expand Tails that are an Array' do
181
+ array = [1,2,3,Tail.new([7,8,9])]
182
+
183
+ assert_equal [1,2,3,7,8,9], array.value
184
+ end
185
+
186
+ it 'should return the value of instantiated variables in a Tail' do
187
+ goal = new_goal :tailor, :h, :t
188
+ array = goal.variablise([:h, :b] / :t)
189
+ goal.instantiate :h, [1,2,3]
190
+ goal.instantiate :t, [7,8,9]
191
+
192
+ assert_equal [goal.value([1,2,3]),goal.variable(:b), goal.value(7), goal.value(8), goal.value(9)], array.value
193
+ end
194
+
195
+ it 'should return the value of instantiated variables in a Tail' do
196
+ goal = new_goal :taylor, :head, :tail
197
+ array = [Tail.new(goal.value([5,6,7,8]))]
198
+
199
+ assert_equal [5,6,7,8], array.value
200
+ end
201
+
202
+ end
203
+
204
+ describe '#type' do
205
+
206
+ it 'should return :variable for symbols' do
207
+ assert_equal :array, array1.type
208
+ assert_equal :array, array2.type
209
+ assert_equal :array, array3.type
210
+ assert_equal :array, array4.type
211
+ end
212
+
213
+ end
214
+
215
+ describe '#head' do
216
+
217
+ it 'should return the first element when no headsize is provided' do
218
+ assert_equal 1, array1.head
219
+ assert_nil array2.head
220
+ assert_nil array3.head
221
+ assert_equal 1, array4.head
222
+ end
223
+
224
+ it 'should return the first headsize elements' do
225
+ assert_equal [1, 2], array1.head(2)
226
+ assert_equal [], array2.head(2)
227
+ assert_equal [UNKNOWN_TAIL], array3.head(2)
228
+ assert_equal [1, :B], array4.head(2)
229
+ end
230
+
231
+ it 'should return an extended head if the tail is uninstantiated' do
232
+ assert_equal [1, 2, :A, 4, [:B, 6, 7, :C], 9], array1.head(9)
233
+ assert_equal [], array2.head(9)
234
+ assert_equal [UNKNOWN_TAIL], array3.head(9)
235
+ assert_equal [1, :B, 3, UNKNOWN_TAIL], array4.head(9)
236
+ end
237
+
238
+ end
239
+
240
+ describe '#tail' do
241
+
242
+ it 'should return the tail after the first element when no headsize is provided' do
243
+ assert_equal [2, :A, 4, [:B, 6, 7, :C], 9], array1.tail
244
+ assert_equal [], array2.tail
245
+ assert_equal [UNKNOWN_TAIL], array3.tail
246
+ assert_equal [:B, 3, UNKNOWN_TAIL], array4.tail
247
+ end
248
+
249
+ it 'should return the tail after the first headsize elements' do
250
+ assert_equal [:A, 4, [:B, 6, 7, :C], 9], array1.tail(2)
251
+ assert_equal [], array2.tail(2)
252
+ assert_equal [UNKNOWN_TAIL], array3.tail(2)
253
+ assert_equal [3, UNKNOWN_TAIL], array4.tail(2)
254
+ end
255
+
256
+ it 'should return an extended tail if the tail is uninstantiated' do
257
+ assert_equal [], array1.tail(9)
258
+ assert_equal [], array2.tail(9)
259
+ assert_equal [UNKNOWN_TAIL], array3.tail(9)
260
+ assert_equal [UNKNOWN_TAIL], array4.tail(9)
261
+ end
262
+
263
+ end
264
+
265
+ describe '#clean' do
266
+
267
+ let(:predicate1) { Predicate.new :generic }
268
+ let(:arguments1) { predicate1.arguments(:m,:n) }
269
+ let(:goal1) { arguments1.goal }
270
+
271
+ let(:array5) { [1, 2, goal1[:A], 4, [goal1[:B], 6, 7, goal1[:C]], 9] }
272
+ let(:array6) { [1, goal1[:B], 3]/:T }
273
+ let(:array7) { [1, goal1[:B], 3]/goal1[:T] }
274
+
275
+ it 'should return simple Arrays as is' do
276
+ assert_equal [1, 2, :A, 4, [:B, 6, 7, :C], 9], array1.clean
277
+ assert_equal [], array2.clean
278
+ assert_equal [UNKNOWN_TAIL], array3.clean
279
+ assert_equal [1, :B, 3, UNKNOWN_TAIL], array4.clean
280
+ end
281
+
282
+ it 'should return the values of its elements with variables replaced by nil and Tails replaced by UNKNOWN_TAIL' do
283
+ assert_equal [1, 2, nil, 4, [nil, 6, 7, nil], 9], array5.clean
284
+ assert_equal [1, nil, 3, UNKNOWN_TAIL], array6.clean
285
+ assert_equal [1, nil, 3, UNKNOWN_TAIL], array7.clean
286
+ end
287
+
288
+ end
289
+
290
+ end
@@ -0,0 +1,891 @@
1
+ #
2
+ # test/porolog/goal_test.rb - Test Suite for Porolog::Goal
3
+ #
4
+ # Luis Esteban 2 May 2018
5
+ # created
6
+ #
7
+
8
+ require_relative '../test_helper'
9
+
10
+
11
+ describe 'Porolog' do
12
+
13
+ before(:all) do
14
+ reset
15
+ end
16
+
17
+ describe 'Goal' do
18
+
19
+ let(:pred) { Predicate.new :p }
20
+ let(:args) { pred.(:x,:y) }
21
+ let(:goal) { Goal.new args }
22
+
23
+ describe '.reset' do
24
+
25
+ it 'should clear all goals' do
26
+ new_goal :predicate1, :a
27
+ new_goal :predicate2, :a, :b
28
+ new_goal :predicate3, :a, :b, :c
29
+
30
+ assert_equal 3, Goal.goals.size
31
+
32
+ Goal.reset
33
+
34
+ assert_empty Goal.goals
35
+ end
36
+
37
+ it 'should call check_deleted for each goal' do
38
+ Goal.any_instance.expects(:check_deleted).with().returns(false).times(5)
39
+
40
+ new_goal :p, :x, :y
41
+ new_goal :q, :a
42
+ new_goal :r, :a, :b, :c
43
+
44
+ Goal.reset
45
+
46
+ new_goal :j, 1
47
+ new_goal :k, ['a', 'b', 'c']
48
+
49
+ Goal.reset
50
+ end
51
+
52
+ end
53
+
54
+ describe '.goals' do
55
+
56
+ it 'should return all registered goals' do
57
+ assert_equal 0, Goal.goals.size
58
+
59
+ goal1 = new_goal :predicate1, :a
60
+
61
+ assert_equal [goal1], Goal.goals
62
+
63
+ goal2 = new_goal :predicate2, :a, :b
64
+
65
+ assert_equal [goal1,goal2], Goal.goals
66
+
67
+ goal3 = new_goal :predicate3, :a, :b, :c
68
+
69
+ assert_equal [goal1,goal2,goal3], Goal.goals
70
+ end
71
+
72
+ end
73
+
74
+ describe '.new' do
75
+
76
+ it 'should create a new Goal' do
77
+ goal = Goal.new nil
78
+
79
+ assert_instance_of Goal, goal
80
+ end
81
+
82
+ end
83
+
84
+ describe '#initialize' do
85
+
86
+ it 'should initialize calling_goal' do
87
+ goal1 = Goal.new args
88
+ goal2 = Goal.new args, goal1
89
+
90
+ assert_nil goal1.calling_goal
91
+ assert_equal goal1, goal2.calling_goal
92
+
93
+ assert_Goal goal1, :p, [:x,:y]
94
+ assert_Goal goal2, :p, [:x,:y]
95
+ end
96
+
97
+ it 'should initialize arguments' do
98
+ goal = Goal.new args, nil
99
+
100
+ assert_equal goal.variablise(args), goal.arguments
101
+ end
102
+
103
+ it 'should initialize terminate' do
104
+ goal = Goal.new args, nil
105
+
106
+ assert_equal false, goal.terminated?
107
+ end
108
+
109
+ it 'should initialize variables' do
110
+ goal = Goal.new args, nil
111
+
112
+ assert_Goal_variables goal, { x: nil, y: nil }, [
113
+ 'Goal1.:x',
114
+ 'Goal1.:y',
115
+ ].join("\n")
116
+ end
117
+
118
+ it 'should register the goal as undeleted' do
119
+ goal1 = Goal.new args, nil
120
+ goal2 = Goal.new args, goal1
121
+
122
+ assert_equal [goal1,goal2], Goal.goals
123
+ end
124
+
125
+ end
126
+
127
+ describe '#myid' do
128
+
129
+ it 'should return the pretty id of the goal' do
130
+ goal1 = Goal.new args, nil
131
+ goal2 = Goal.new args, goal1
132
+
133
+ assert_equal 'Goal1', goal1.myid
134
+ assert_equal 'Goal2', goal2.myid
135
+ end
136
+
137
+ end
138
+
139
+ describe '#ancestors' do
140
+
141
+ it 'should return an Array of the parent goals' do
142
+ goal1 = Goal.new args
143
+ goal2 = Goal.new args, goal1
144
+ goal3 = Goal.new args, goal2
145
+ goal4 = Goal.new args, goal3
146
+
147
+ assert_equal [goal1], goal1.ancestors
148
+ assert_equal [goal1, goal2], goal2.ancestors
149
+ assert_equal [goal1, goal2, goal3], goal3.ancestors
150
+ assert_equal [goal1, goal2, goal3, goal4], goal4.ancestors
151
+ end
152
+
153
+ end
154
+
155
+ describe '#ancestry' do
156
+
157
+ it 'should return an Array of the parent goals' do
158
+ goal1 = Goal.new args
159
+ goal2 = Goal.new args, goal1
160
+ goal3 = Goal.new args, goal2
161
+ goal4 = Goal.new args, goal3
162
+
163
+ ancestors = [
164
+ 'Goal1 -- Solve p(:x,:y) {:x=>nil, :y=>nil}',
165
+ ' Goal2 -- Solve p(:x,:y) {:x=>nil, :y=>nil}',
166
+ ' Goal3 -- Solve p(:x,:y) {:x=>nil, :y=>nil}',
167
+ ' Goal4 -- Solve p(:x,:y) {:x=>nil, :y=>nil}',
168
+ ]
169
+
170
+ assert_equal ancestors[0...1].join("\n"), goal1.ancestry
171
+ assert_equal ancestors[0...2].join("\n"), goal2.ancestry
172
+ assert_equal ancestors[0...3].join("\n"), goal3.ancestry
173
+ assert_equal ancestors[0...4].join("\n"), goal4.ancestry
174
+ end
175
+
176
+ end
177
+
178
+ describe '#inspect' do
179
+
180
+ it 'should show a description of the goal' do
181
+ goal1 = Goal.new args
182
+ goal2 = Goal.new pred.(1,:b,'word'), goal1
183
+ goal3 = Goal.new args, goal2
184
+ goal4 = Goal.new args, goal3
185
+
186
+ assert_equal 'Goal1 -- Solve p(:x,:y)', goal1.inspect
187
+ assert_equal 'Goal2 -- Solve p(1,:b,"word")', goal2.inspect
188
+ assert_equal 'Goal3 -- Solve p(:x,:y)', goal3.inspect
189
+ assert_equal 'Goal4 -- Solve p(:x,:y)', goal4.inspect
190
+ end
191
+
192
+ end
193
+
194
+ describe '#delete!' do
195
+
196
+ it 'should delete the goal' do
197
+ goal1 = Goal.new args
198
+ goal2 = Goal.new pred.(1,:b,'word'), goal1
199
+ goal3 = Goal.new args, goal2
200
+ goal4 = Goal.new args, goal3
201
+
202
+ assert goal2.delete!, 'goal should delete'
203
+ assert_equal [goal1, goal3, goal4], Goal.goals
204
+ end
205
+
206
+ end
207
+
208
+ describe '#myid' do
209
+
210
+ let(:pred) { Predicate.new :p }
211
+ let(:args) { pred.(:x,:y) }
212
+
213
+ it 'should return the pretty id of the goal' do
214
+ goal1 = Goal.new args, nil
215
+ goal2 = Goal.new args, goal1
216
+
217
+ assert_equal 'Goal1', goal1.myid
218
+ assert_equal 'Goal2', goal2.myid
219
+ end
220
+
221
+ end
222
+
223
+ describe '#deleted?' do
224
+
225
+ it 'should return the deleted state of a goal' do
226
+ goal = Goal.new args
227
+
228
+ refute goal.deleted?, 'goal should not be deleted'
229
+
230
+ Goal.reset
231
+
232
+ assert goal.deleted?, 'goal should be deleted'
233
+ end
234
+
235
+ it 'should memoize the deleted state of a goal' do
236
+ goal = Goal.new args
237
+
238
+ check_deleted_spy = Spy.on(goal, :check_deleted).and_call_through
239
+
240
+ refute goal.deleted?
241
+ refute goal.deleted?
242
+ refute goal.deleted?
243
+ refute goal.deleted?
244
+
245
+ Goal.reset
246
+
247
+ assert goal.deleted?
248
+ assert goal.deleted?
249
+ assert goal.deleted?
250
+ assert goal.deleted?
251
+
252
+ assert_equal 5, check_deleted_spy.calls.size
253
+ end
254
+
255
+ end
256
+
257
+ describe '#check_deleted' do
258
+
259
+ it 'should return false when the goal is not deleted and keep variables intact' do
260
+ goal = Goal.new args
261
+ goal.variable(:x)
262
+ goal.variable(:y)
263
+ goal.variable(:z)
264
+
265
+ variable_remove_spy = Spy.on_instance_method(Variable, :remove)
266
+
267
+ refute goal.check_deleted, 'goal should not be deleted'
268
+
269
+ assert_equal 0, variable_remove_spy.calls.size
270
+ end
271
+
272
+ it 'should return true when the goal is deleted and remove all variables' do
273
+ goal = Goal.new args
274
+ goal.variable(:x)
275
+ goal.variable(:y)
276
+ goal.variable(:z)
277
+
278
+ variable_remove_spy = Spy.on_instance_method(Variable, :remove)
279
+
280
+ Goal.reset
281
+
282
+ assert goal.check_deleted, 'goal should be deleted'
283
+
284
+ assert_equal 3, variable_remove_spy.calls.size
285
+ end
286
+
287
+ end
288
+
289
+ describe '#terminate!' do
290
+
291
+ it 'should set the goal to terminate and log the event' do
292
+ goal = Goal.new args
293
+
294
+ assert goal.terminate!, 'the goal should be set to terminate'
295
+ assert_equal ['terminating'], goal.log
296
+ end
297
+
298
+ end
299
+
300
+ describe '#terminated?' do
301
+
302
+ it 'should return whether the goal is set to terminate or not' do
303
+ goal = Goal.new args
304
+
305
+ refute goal.terminated?, 'the goal should not be initialized to terminate'
306
+ assert goal.terminate!, 'the goal should be set to terminate'
307
+ assert goal.terminated?, 'the goal should be set to terminate'
308
+ end
309
+
310
+ end
311
+
312
+ describe '#variablise' do
313
+
314
+ it 'should convert a Symbol into a Variable' do
315
+ assert_Variable goal.variablise(:k), :k, goal, [], []
316
+ end
317
+
318
+ it 'should return a Variable as is' do
319
+ v = Variable.new :r, goal
320
+ assert_equal v, goal.variablise(v)
321
+ end
322
+
323
+ it 'should convert an Array into a Variables and Objects' do
324
+ assert_equal [goal.value(1),goal.variable(:a),goal.value('word')], goal.variablise([1,:a,'word'])
325
+ end
326
+
327
+ it 'should return a duplicate of an Arguments' do
328
+ duplicate = goal.variablise(args)
329
+ assert_Arguments duplicate, :p, goal.variablise([:x, :y])
330
+ refute_equal args, duplicate
331
+ end
332
+
333
+ it 'should convert a Tail into a Tail with a variablised value' do
334
+ assert_Tail goal.variablise(Tail.new :m), '*Goal1.:m'
335
+ end
336
+
337
+ it 'should return a Value as is' do
338
+ v = Value.new(45, goal)
339
+ assert_equal v, goal.variablise(v)
340
+ end
341
+
342
+ it 'should return an unknown array as is' do
343
+ assert_equal UNKNOWN_ARRAY, goal.variablise(UNKNOWN_ARRAY)
344
+ end
345
+
346
+ it 'should return an unknown tail as is' do
347
+ assert_equal UNKNOWN_TAIL, goal.variablise(UNKNOWN_TAIL)
348
+ end
349
+
350
+ it 'should convert any other Object into a Value' do
351
+ assert_Value goal.variablise(23.61), 23.61, goal
352
+ end
353
+
354
+ end
355
+
356
+ describe '#variables' do
357
+
358
+ it 'should return a Hash of variables and their values' do
359
+ goal = Goal.new args
360
+ goal.variable(:x)
361
+ goal.variable(:y)
362
+ goal.variable(:z)
363
+
364
+ variables = goal.variables
365
+
366
+ assert_instance_of Hash, variables
367
+
368
+ assert_Goal goal, :p, [:x,:y]
369
+ assert_Goal_variables goal, { x: nil, y: nil, z: nil }, [
370
+ 'Goal1.:x',
371
+ 'Goal1.:y',
372
+ 'Goal1.:z',
373
+ ].join("\n")
374
+ end
375
+
376
+ end
377
+
378
+ describe '#inspect_variables' do
379
+
380
+ it 'should return a string showing the instantiations of the variables of the goal' do
381
+ # -- Initial Goal --
382
+ goal = Goal.new args
383
+
384
+ x = goal.variable(:x)
385
+ y = goal.variable(:y)
386
+ z = goal.variable(:z)
387
+
388
+ expected = [
389
+ 'Goal1.:x',
390
+ 'Goal1.:y',
391
+ 'Goal1.:z',
392
+ ].join("\n")
393
+
394
+ assert_equal expected, goal.inspect_variables
395
+
396
+ # -- After First Instantiation --
397
+ # x.head = y
398
+ x.instantiate y, nil, :head
399
+
400
+ expected = [
401
+ 'Goal1.:x',
402
+ ' [:head]Goal1.:y',
403
+ 'Goal1.:y',
404
+ ' Goal1.:x[:head]',
405
+ 'Goal1.:z',
406
+ ].join("\n")
407
+
408
+ assert_equal expected, goal.inspect_variables
409
+
410
+ # -- After Second Instantiation --
411
+ # x.head = y
412
+ # x.tail = z
413
+ x.instantiate z, nil, :tail
414
+
415
+ expected = [
416
+ 'Goal1.:x',
417
+ ' [:head]Goal1.:y',
418
+ ' [:tail]Goal1.:z',
419
+ 'Goal1.:y',
420
+ ' Goal1.:x[:head]',
421
+ ' [:tail]Goal1.:z',
422
+ 'Goal1.:z',
423
+ ' Goal1.:x[:tail]',
424
+ ' [:head]Goal1.:y',
425
+ ].join("\n")
426
+
427
+ assert_equal expected, goal.inspect_variables
428
+
429
+ # -- After Third Instantiation --
430
+ # x = [*y, *z]
431
+ # y = 1,2,3
432
+ y.instantiate goal.value([1,2,3])
433
+
434
+ expected = [
435
+ 'Goal1.:x',
436
+ ' [:head]Goal1.:y',
437
+ ' Goal1.[1, 2, 3]',
438
+ ' [:tail]Goal1.:z',
439
+ 'Goal1.:y',
440
+ ' Goal1.:x[:head]',
441
+ ' [:tail]Goal1.:z',
442
+ ' Goal1.[1, 2, 3]',
443
+ 'Goal1.:z',
444
+ ' Goal1.:x[:tail]',
445
+ ' [:head]Goal1.:y',
446
+ ' Goal1.[1, 2, 3]',
447
+ ].join("\n")
448
+
449
+ assert_equal expected, goal.inspect_variables
450
+
451
+ # -- After Fourth Instantiation --
452
+ # x = [*y, *z]
453
+ # y = 1,2,3
454
+ # z = 4,5,6
455
+ z.instantiate goal.value([4,5,6])
456
+
457
+ expected = [
458
+ 'Goal1.:x',
459
+ ' [:head]Goal1.:y',
460
+ ' Goal1.[1, 2, 3]',
461
+ ' [:tail]Goal1.:z',
462
+ ' Goal1.[4, 5, 6]',
463
+ 'Goal1.:y',
464
+ ' Goal1.:x[:head]',
465
+ ' [:tail]Goal1.:z',
466
+ ' Goal1.[4, 5, 6]',
467
+ ' Goal1.[1, 2, 3]',
468
+ 'Goal1.:z',
469
+ ' Goal1.:x[:tail]',
470
+ ' [:head]Goal1.:y',
471
+ ' Goal1.[1, 2, 3]',
472
+ ' Goal1.[4, 5, 6]',
473
+ ].join("\n")
474
+
475
+ assert_equal expected, goal.inspect_variables
476
+ end
477
+
478
+ end
479
+
480
+ describe '#values' do
481
+
482
+ it 'should return the values that have been associated with the goal' do
483
+ goal = Goal.new args
484
+
485
+ x = goal.variable(:x)
486
+ y = goal.variable(:y)
487
+ z = goal.variable(:z)
488
+
489
+ x.instantiate y, nil, :head
490
+ x.instantiate z, nil, :tail
491
+ y.instantiate goal.value([1,2,3])
492
+ z.instantiate goal.value([4,5,6])
493
+
494
+ expected = [
495
+ [1,2,3],
496
+ [4,5,6],
497
+ ]
498
+
499
+ assert_equal expected, goal.values
500
+ end
501
+
502
+ end
503
+
504
+ describe '#variable' do
505
+
506
+ it 'should return a non-variable as is' do
507
+ assert_equal 4.132, goal.variable(4.132)
508
+ end
509
+
510
+ it 'should create and find a variable from a Symbol or Variable' do
511
+ refute goal.variables.key?(:v), ':v should not be a variable'
512
+
513
+ variable1 = goal.variable :v
514
+
515
+ assert goal.variables.key?(:v), ':v should be a variable'
516
+
517
+ variable2 = goal.variable :v
518
+
519
+ assert goal.variables.key?(:v), ':v should be a variable'
520
+
521
+ variable3 = goal.variable variable2
522
+
523
+ assert_equal variable1, variable2
524
+ assert_equal variable2, variable3
525
+ end
526
+
527
+ end
528
+
529
+ describe '#value' do
530
+
531
+ it 'should create and find a value from an Object' do
532
+ refute goal.values.include?(99.02), '99.02 should not be a value'
533
+
534
+ value1 = goal.value 99.02
535
+
536
+ assert goal.values.include?(99.02), '99.02 should be a value'
537
+
538
+ value2 = goal.value 99.02
539
+
540
+ assert_Value value1, 99.02, goal
541
+ assert_Value value2, 99.02, goal
542
+
543
+ assert_equal [99.02], goal.values
544
+ end
545
+
546
+ end
547
+
548
+ describe '#value_of' do
549
+
550
+ describe 'when name is a Symbol' do
551
+
552
+ let(:variable_name) { :cymbal }
553
+
554
+ it 'should create the Variable' do
555
+ refute goal.variables.key?(variable_name), "#{variable_name} should not exist"
556
+
557
+ refute_nil goal.value_of(variable_name)
558
+
559
+ assert goal.variables.key?(variable_name), "#{variable_name} should exist"
560
+
561
+ variable = goal.variable(variable_name)
562
+ variable.instantiate 44.5
563
+
564
+ assert_equal 44.5, goal.value_of(variable_name)
565
+ end
566
+
567
+ end
568
+
569
+ describe 'when name is not a Symbol' do
570
+
571
+ let(:variable_name) { 99.28 }
572
+
573
+ it 'should return the name as the value' do
574
+ assert_equal 99.28, goal.value_of(variable_name)
575
+ end
576
+
577
+ end
578
+
579
+ end
580
+
581
+ describe '#values_of' do
582
+
583
+ it 'should return an Array with values_of applied to each element when given an Array' do
584
+ goal.instantiate :thomas, '$10'
585
+
586
+ expected = [[1,'two'], '$10', 999, 'giraffe', 0.5]
587
+
588
+ assert_equal expected, goal.values_of([[1,'two'], :thomas, 999, 'giraffe', 0.5])
589
+ end
590
+
591
+ it 'should return the value_of the Variable when given a Variable' do
592
+ goal.instantiate :jones, 'Fuzzy'
593
+
594
+ variable1 = goal.variable(:jones)
595
+
596
+ assert_equal 'Fuzzy', goal.values_of(variable1)
597
+ end
598
+
599
+ it 'should return the value_of the Symbol when given a Symbol' do
600
+ goal.instantiate :x, 'Staunch'
601
+
602
+ assert_equal 'Staunch', goal.values_of(:x)
603
+ end
604
+
605
+ it 'should return the value_of the Value when given a Value' do
606
+ value1 = goal.value(123.76)
607
+
608
+ assert_equal 123.76, goal.values_of(value1)
609
+ end
610
+
611
+ it 'should somehow splat when given a Tail' do
612
+ tail1 = Tail.new ['apples','oranges','bananas']
613
+
614
+ assert_equal 'apples', goal.values_of(tail1)
615
+ end
616
+
617
+ it 'should return the Object as is when given an Object' do
618
+ object = Object.new
619
+
620
+ goal.expects(:value_of).times(0)
621
+
622
+ assert_equal object, goal.values_of(object)
623
+ end
624
+
625
+ end
626
+
627
+ describe '#solve' do
628
+
629
+ it 'should find no solutions for a goal with no rules' do
630
+ goal1 = new_goal :p, :x, :y
631
+
632
+ solutions = goal1.solve
633
+
634
+ assert_equal [], solutions
635
+ end
636
+
637
+ it 'should solve a fact' do
638
+ predicate :fact
639
+
640
+ fact(42).fact!
641
+
642
+ solutions = fact(42).solve
643
+
644
+ assert_equal [{}], solutions
645
+ end
646
+
647
+ it 'should not solve a falicy' do
648
+ predicate :fact
649
+
650
+ fact(42).falicy!
651
+
652
+ solutions = fact(42).solve
653
+
654
+ assert_equal [], solutions
655
+ end
656
+
657
+ it 'should solve using head and tail with lists' do
658
+ predicate :head_tail
659
+
660
+ head_tail([1,2,3,4,5,6,7]).fact!
661
+ head_tail(['head','body','foot']).fact!
662
+
663
+ solutions = head_tail(:head/:tail).solve
664
+
665
+ assert_equal [
666
+ { head: 1, tail: [2,3,4,5,6,7] },
667
+ { head: 'head', tail: ['body','foot'] },
668
+ ], solutions
669
+ end
670
+
671
+ it 'should solve a goal recursively' do
672
+ builtin :write
673
+ predicate :recursive
674
+
675
+ recursive([]) << [:CUT, true]
676
+ recursive(:head/:tail) << [
677
+ write('(',:head,')'),
678
+ recursive(:tail)
679
+ ]
680
+
681
+ assert_output '(1)(2)(3)(four)(5)(6)(7)' do
682
+ solutions = recursive([1,2,3,'four',5,6,7]).solve
683
+
684
+ assert_equal [{}], solutions
685
+ end
686
+ end
687
+
688
+ end
689
+
690
+ describe '#satisfy' do
691
+
692
+ let(:block) { ->(subgoal){} }
693
+
694
+ it 'should return false when the goal has no arguments' do
695
+ goal = Goal.new nil
696
+
697
+ block.expects(:call).times(0)
698
+
699
+ refute goal.satisfy(&block), name
700
+ end
701
+
702
+ it 'should access the predicate of its arguments' do
703
+ goal.arguments.expects(:predicate).with().returns(nil).times(1)
704
+ block.expects(:call).times(0)
705
+
706
+ refute goal.satisfy(&block), name
707
+ end
708
+
709
+ it 'should try to satisfy the predicate with itself' do
710
+ pred.expects(:satisfy).with(goal).returns(nil).times(1)
711
+ block.expects(:call).times(0)
712
+
713
+ refute goal.satisfy(&block), name
714
+ end
715
+
716
+ it 'should call the block when the predicate is satisfied' do
717
+ pred.(1,2).fact!
718
+ pred.(3,4).fact!
719
+ pred.(5,6).fact!
720
+
721
+ block.expects(:call).returns(true).times(3)
722
+
723
+ assert goal.satisfy(&block), name
724
+ end
725
+
726
+ end
727
+
728
+ describe '#instantiate' do
729
+
730
+ it 'creates an Array with a Tail using the slash notation with Symbols with a goal' do
731
+ goal = new_goal :bravo, :x, :y, :z
732
+
733
+ head_tail = goal.variablise(:head / :tail)
734
+
735
+ assert_Array_with_Tail head_tail, [goal.variable(:head)], '*Goal1.:tail'
736
+
737
+ assert_Goal_variables goal, { x: nil, y: nil, z: nil, head: nil, tail: nil }, [
738
+ 'Goal1.:x',
739
+ 'Goal1.:y',
740
+ 'Goal1.:z',
741
+ 'Goal1.:head',
742
+ 'Goal1.:tail',
743
+ ].join("\n")
744
+ end
745
+
746
+ it 'creates an Array with a Tail using the slash notation with a single head with a goal' do
747
+ goal = new_goal :bravo, :x, :y, :z
748
+
749
+ head_tail = goal.variablise([:head] / :tail)
750
+
751
+ assert_Array_with_Tail head_tail, [goal.variable(:head)], '*Goal1.:tail'
752
+
753
+ assert_Goal_variables goal, { x: nil, y: nil, z: nil, head: nil, tail: nil }, [
754
+ 'Goal1.:x',
755
+ 'Goal1.:y',
756
+ 'Goal1.:z',
757
+ 'Goal1.:head',
758
+ 'Goal1.:tail',
759
+ ].join("\n")
760
+ end
761
+
762
+ it 'creates variables from its contents with a goal' do
763
+ predicate :bravo
764
+ arguments = Predicate[:bravo].arguments(:x,:y,:z)
765
+ goal = arguments.goal
766
+
767
+ head_tail = [:alpha,bravo(:a,:b,:c),[:carly]] / [:x,[:y],bravo(:p,:q/:r)]
768
+
769
+ assert_equal [:alpha,bravo(:a,:b,:c),[:carly], :x,[:y],bravo(:p,:q/:r)].inspect, head_tail.inspect
770
+
771
+ assert_Goal_variables goal, { x: nil, y: nil, z: nil }, [
772
+ 'Goal1.:x',
773
+ 'Goal1.:y',
774
+ 'Goal1.:z',
775
+ ].join("\n")
776
+
777
+ head_tail = goal.variablise(head_tail)
778
+
779
+ assert_equal '[Goal1.:alpha, bravo(Goal1.:a,Goal1.:b,Goal1.:c), [Goal1.:carly], Goal1.:x, [Goal1.:y], bravo(Goal1.:p,[Goal1.:q, *Goal1.:r])]', head_tail.inspect
780
+
781
+ assert_Goal_variables goal, { x: nil, y: nil, z: nil, alpha: nil, a: nil, b: nil, c: nil, carly: nil, p: nil, q: nil, r: nil }, [
782
+ 'Goal1.:x',
783
+ 'Goal1.:y',
784
+ 'Goal1.:z',
785
+ 'Goal1.:alpha',
786
+ 'Goal1.:a',
787
+ 'Goal1.:b',
788
+ 'Goal1.:c',
789
+ 'Goal1.:carly',
790
+ 'Goal1.:p',
791
+ 'Goal1.:q',
792
+ 'Goal1.:r',
793
+ ].join("\n")
794
+ end
795
+
796
+ it 'should combine Array value of head and Array value of tail when it has a goal' do
797
+ head_tail = [:first] / :last
798
+
799
+ goal.instantiate :first, 'alpha', goal
800
+ goal.instantiate :last, ['omega'], goal
801
+
802
+ head_tail = goal.values_of(goal.variablise(head_tail))
803
+
804
+ assert_equal ['alpha','omega'], head_tail
805
+ end
806
+
807
+ end
808
+
809
+ describe '#inherit_variables' do
810
+
811
+ it 'should return true for an initial goal' do
812
+ goal = new_goal :goal, :x, :y, :z
813
+
814
+ assert goal.inherit_variables
815
+ assert_Goal_variables goal, { x: nil, y: nil, z: nil }, [
816
+ 'Goal1.:x',
817
+ 'Goal1.:y',
818
+ 'Goal1.:z',
819
+ ].join("\n")
820
+ end
821
+
822
+ it 'should instantiate combined variables of both goals' do
823
+ goal = new_goal :goal, :x, :y, :z
824
+ subgoal = new_goal :subgoal, :a, :b, :c
825
+
826
+ assert subgoal.inherit_variables(goal)
827
+ assert_Goal_variables goal, { x: nil, y: nil, z: nil, a: nil, b: nil, c: nil }, [
828
+ 'Goal1.:x',
829
+ ' Goal2.:x',
830
+ 'Goal1.:y',
831
+ ' Goal2.:y',
832
+ 'Goal1.:z',
833
+ ' Goal2.:z',
834
+ 'Goal1.:a',
835
+ ' Goal2.:a',
836
+ 'Goal1.:b',
837
+ ' Goal2.:b',
838
+ 'Goal1.:c',
839
+ ' Goal2.:c',
840
+ ].join("\n")
841
+ assert_Goal_variables subgoal, { a: nil, b: nil, c: nil, x: nil, y: nil, z: nil }, [
842
+ 'Goal2.:a',
843
+ ' Goal1.:a',
844
+ 'Goal2.:b',
845
+ ' Goal1.:b',
846
+ 'Goal2.:c',
847
+ ' Goal1.:c',
848
+ 'Goal2.:x',
849
+ ' Goal1.:x',
850
+ 'Goal2.:y',
851
+ ' Goal1.:y',
852
+ 'Goal2.:z',
853
+ ' Goal1.:z',
854
+ ].join("\n")
855
+ end
856
+
857
+ it 'should not make any instantiations when variables cannot be unified' do
858
+ goal = new_goal :goal, :x, :y, :z
859
+ subgoal = new_goal :subgoal, :a, :b, :c
860
+
861
+ goal[:x].instantiate 1
862
+ subgoal[:x].instantiate 2
863
+
864
+ refute subgoal.inherit_variables(goal)
865
+ assert_equal [
866
+ 'Cannot unify because 1 != 2 (variable != variable)',
867
+ ], goal.log
868
+ assert_equal [
869
+ 'Cannot unify because 1 != 2 (variable != variable)',
870
+ "Couldn't unify: :x WITH Goal1 AND Goal2"
871
+ ], subgoal.log
872
+ assert_Goal_variables goal, { x: 1, y: nil, z: nil }, [
873
+ 'Goal1.:x',
874
+ ' Goal1.1',
875
+ 'Goal1.:y',
876
+ 'Goal1.:z',
877
+ ].join("\n")
878
+ assert_Goal_variables subgoal, { a: nil, b: nil, c: nil, x: 2 }, [
879
+ 'Goal2.:a',
880
+ 'Goal2.:b',
881
+ 'Goal2.:c',
882
+ 'Goal2.:x',
883
+ ' Goal2.2',
884
+ ].join("\n")
885
+ end
886
+
887
+ end
888
+
889
+ end
890
+
891
+ end