rubylog 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (124) hide show
  1. data/.document +5 -0
  2. data/.rspec +1 -0
  3. data/Gemfile +18 -0
  4. data/Gemfile.lock +64 -0
  5. data/LICENSE.txt +20 -0
  6. data/README.rdoc +96 -0
  7. data/Rakefile +53 -0
  8. data/VERSION +1 -0
  9. data/examples/4queens.rb +10 -0
  10. data/examples/calculation.rb +12 -0
  11. data/examples/concepts.rb +46 -0
  12. data/examples/factorial.rb +16 -0
  13. data/examples/fp.rb +56 -0
  14. data/examples/hello.rb +9 -0
  15. data/examples/historia_de_espana.rb +31 -0
  16. data/examples/idea.rb +143 -0
  17. data/examples/lists.rb +5 -0
  18. data/examples/mechanika.rb +409 -0
  19. data/examples/parse.rb +15 -0
  20. data/examples/theory.rb +20 -0
  21. data/lib/array.rb +24 -0
  22. data/lib/class.rb +11 -0
  23. data/lib/method.rb +4 -0
  24. data/lib/object.rb +5 -0
  25. data/lib/proc.rb +4 -0
  26. data/lib/rubylog/builtins.rb +193 -0
  27. data/lib/rubylog/callable.rb +20 -0
  28. data/lib/rubylog/clause.rb +113 -0
  29. data/lib/rubylog/composite_term.rb +38 -0
  30. data/lib/rubylog/dsl/constants.rb +15 -0
  31. data/lib/rubylog/dsl/first_order_functors.rb +9 -0
  32. data/lib/rubylog/dsl/global_functors.rb +3 -0
  33. data/lib/rubylog/dsl/second_order_functors.rb +8 -0
  34. data/lib/rubylog/dsl.rb +52 -0
  35. data/lib/rubylog/errors.rb +18 -0
  36. data/lib/rubylog/internal_helpers.rb +16 -0
  37. data/lib/rubylog/predicate.rb +34 -0
  38. data/lib/rubylog/proc_method_additions.rb +69 -0
  39. data/lib/rubylog/term.rb +20 -0
  40. data/lib/rubylog/theory.rb +133 -0
  41. data/lib/rubylog/unifiable.rb +19 -0
  42. data/lib/rubylog/variable.rb +97 -0
  43. data/lib/rubylog.rb +39 -0
  44. data/lib/symbol.rb +35 -0
  45. data/rubylog.gemspec +187 -0
  46. data/script/inriasuite2spec +0 -0
  47. data/script/inriasuite2spec.pl +22 -0
  48. data/spec/bartak_guide_spec.rb +91 -0
  49. data/spec/inriasuite/README +122 -0
  50. data/spec/inriasuite/abolish +18 -0
  51. data/spec/inriasuite/and +9 -0
  52. data/spec/inriasuite/arg +32 -0
  53. data/spec/inriasuite/arith_diff +10 -0
  54. data/spec/inriasuite/arith_eq +10 -0
  55. data/spec/inriasuite/arith_gt +10 -0
  56. data/spec/inriasuite/arith_gt= +10 -0
  57. data/spec/inriasuite/arith_lt +10 -0
  58. data/spec/inriasuite/arith_lt= +10 -0
  59. data/spec/inriasuite/asserta +18 -0
  60. data/spec/inriasuite/assertz +16 -0
  61. data/spec/inriasuite/atom +12 -0
  62. data/spec/inriasuite/atom_chars +19 -0
  63. data/spec/inriasuite/atom_codes +15 -0
  64. data/spec/inriasuite/atom_concat +19 -0
  65. data/spec/inriasuite/atom_length +12 -0
  66. data/spec/inriasuite/atomic +11 -0
  67. data/spec/inriasuite/bagof +31 -0
  68. data/spec/inriasuite/call +19 -0
  69. data/spec/inriasuite/catch-and-throw +16 -0
  70. data/spec/inriasuite/char_code +13 -0
  71. data/spec/inriasuite/clause +16 -0
  72. data/spec/inriasuite/compound +12 -0
  73. data/spec/inriasuite/copy_term +25 -0
  74. data/spec/inriasuite/current_input +5 -0
  75. data/spec/inriasuite/current_output +5 -0
  76. data/spec/inriasuite/current_predicate +16 -0
  77. data/spec/inriasuite/current_prolog_flag +12 -0
  78. data/spec/inriasuite/cut +9 -0
  79. data/spec/inriasuite/fail +15 -0
  80. data/spec/inriasuite/file_manip +8 -0
  81. data/spec/inriasuite/findall +22 -0
  82. data/spec/inriasuite/float +10 -0
  83. data/spec/inriasuite/functor +41 -0
  84. data/spec/inriasuite/functor-bis +41 -0
  85. data/spec/inriasuite/halt +7 -0
  86. data/spec/inriasuite/if-then +10 -0
  87. data/spec/inriasuite/if-then-else +12 -0
  88. data/spec/inriasuite/inriasuite.obp +0 -0
  89. data/spec/inriasuite/inriasuite.pl +836 -0
  90. data/spec/inriasuite/integer +10 -0
  91. data/spec/inriasuite/is +11 -0
  92. data/spec/inriasuite/junk +0 -0
  93. data/spec/inriasuite/nonvar +11 -0
  94. data/spec/inriasuite/not_provable +12 -0
  95. data/spec/inriasuite/not_unify +15 -0
  96. data/spec/inriasuite/number +10 -0
  97. data/spec/inriasuite/number_chars +22 -0
  98. data/spec/inriasuite/number_codes +19 -0
  99. data/spec/inriasuite/once +11 -0
  100. data/spec/inriasuite/or +9 -0
  101. data/spec/inriasuite/repeat +5 -0
  102. data/spec/inriasuite/retract +10 -0
  103. data/spec/inriasuite/set_prolog_flag +21 -0
  104. data/spec/inriasuite/setof +36 -0
  105. data/spec/inriasuite/sub_atom +30 -0
  106. data/spec/inriasuite/t +1 -0
  107. data/spec/inriasuite/t_foo.pl +4 -0
  108. data/spec/inriasuite/term_diff +13 -0
  109. data/spec/inriasuite/term_eq +12 -0
  110. data/spec/inriasuite/term_gt +12 -0
  111. data/spec/inriasuite/term_gt= +12 -0
  112. data/spec/inriasuite/term_lt +12 -0
  113. data/spec/inriasuite/term_lt= +12 -0
  114. data/spec/inriasuite/true +7 -0
  115. data/spec/inriasuite/unify +18 -0
  116. data/spec/inriasuite.rb +20 -0
  117. data/spec/recursion_spec.rb +18 -0
  118. data/spec/rubylog/builtins/splits_to.rb +18 -0
  119. data/spec/rubylog/clause_spec.rb +81 -0
  120. data/spec/rubylog/variable_spec.rb +25 -0
  121. data/spec/rubylog_spec.rb +914 -0
  122. data/spec/spec_helper.rb +12 -0
  123. data/spec/theory_spec.rb +1 -0
  124. metadata +339 -0
@@ -0,0 +1,914 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ RSpec::Matchers.define :stand do
4
+ match do |actual|
5
+ actual.true?
6
+ end
7
+ end
8
+
9
+
10
+ class << Rubylog.theory
11
+ Symbol.rubylog_functor \
12
+ :likes, :is_happy, :in, :has, :we_have,
13
+ :brother, :father, :uncle, :neq, :happy, :%
14
+ Integer.rubylog_functor :divides, :queens
15
+ Rubylog::Clause.rubylog_functor :-
16
+
17
+ describe Rubylog do
18
+ before do
19
+ Rubylog.theory.clear
20
+ end
21
+
22
+
23
+
24
+ describe "facts" do
25
+ it "can be asserted with assert" do
26
+ Rubylog.theory.assert(:john.is_happy)
27
+ Rubylog.theory.database[:is_happy][1].should include(Rubylog::Clause.new :-, :john.is_happy, :true)
28
+ Rubylog.theory.assert(:john.likes :beer)
29
+ Rubylog.theory.database[:likes][2].should include(Rubylog::Clause.new :-, :john.likes(:beer), :true)
30
+ Rubylog.theory.assert(:john.likes :drinking.in :bar)
31
+ Rubylog.theory.database[:likes][2].should include(:john.likes(:drinking.in :bar) - :true)
32
+ end
33
+
34
+ it "can be asserted with a bang, and it returns the zeroth arg" do
35
+ :john.is_happy!.should == :john
36
+ Rubylog.theory.database[:is_happy][1].should include(:john.is_happy.-:true)
37
+ :john.likes!(:beer).should == :john
38
+ Rubylog.theory.database[:likes][2].should include(:john.likes(:beer).-:true)
39
+ :john.likes!(:drinking.in :bar).should == :john
40
+ Rubylog.theory.database[:likes][2].should include(:john.likes(:drinking.in :bar).-:true)
41
+ end
42
+
43
+
44
+ end
45
+
46
+ describe "compilation" do
47
+
48
+ it "makes eql variables be equal" do
49
+ a = A; b = A
50
+ c = (a.likes b)
51
+ c[0].should be_equal a; c[1].should be_equal b
52
+ c[0].should_not be_equal c[1]
53
+ c = c.rubylog_compile_variables
54
+ c[0].should be_equal c[1]
55
+ end
56
+
57
+ it "makes non-eql variables be non-equal" do
58
+ a = A; b = B
59
+ c = (a.likes b)
60
+ c[0].should be_equal a; c[1].should be_equal b
61
+ c[0].should_not be_equal c[1]
62
+ c = c.rubylog_compile_variables
63
+ c[0].should_not be_equal c[1]
64
+ end
65
+
66
+ it "makes dont-care variables be non-equal" do
67
+ a = ANY; b = ANY
68
+ c = (a.likes b)
69
+ c[0].should be_equal a; c[1].should be_equal b
70
+ c[0].should_not be_equal c[1]
71
+ c = c.rubylog_compile_variables
72
+ c[0].should_not be_equal c[1]
73
+ end
74
+
75
+ it "creates new variables" do
76
+ a = A; b = B
77
+ c = (a.likes b)
78
+ c[0].should be_equal a; c[1].should be_equal b
79
+ c = c.rubylog_compile_variables
80
+ c[0].should_not be_equal a
81
+ c[1].should_not be_equal a
82
+ c[0].should_not be_equal b
83
+ c[1].should_not be_equal b
84
+ end
85
+
86
+ it "makes variables available" do
87
+ a = A; a1 = A; a2 = A; b = B; b1 = B; c = C;
88
+ (a.likes b).rubylog_compile_variables.rubylog_variables.should == [a, b]
89
+ (a.likes a1).rubylog_compile_variables.rubylog_variables.should == [a]
90
+ (a.likes a1.in b).rubylog_compile_variables.rubylog_variables.should == [a, b]
91
+ (a.likes a1,b,b1,a2,c).rubylog_compile_variables.rubylog_variables.should == [a, b, c]
92
+ end
93
+
94
+ it "does not make dont-care variables available" do
95
+ a = ANY; a1 = ANYTHING; a2 = ANYTHING; b = B; b1 = B; c = C;
96
+ (a.likes b).rubylog_compile_variables.rubylog_variables.should == [b]
97
+ (a.likes a1).rubylog_compile_variables.rubylog_variables.should == []
98
+ (a.likes a1.in b).rubylog_compile_variables.
99
+ rubylog_variables.should == [b]
100
+ (a.likes a1,b,b1,a2,c).rubylog_compile_variables.
101
+ rubylog_variables.should == [b, c]
102
+ end
103
+
104
+
105
+ end
106
+
107
+ describe "unification" do
108
+ it "works for variables" do
109
+ result = false
110
+ A.rubylog_unify(12) { result = true }
111
+ result.should == true
112
+ end
113
+ it "works for used classes" do
114
+ result = false
115
+ :john.rubylog_unify(A) { result = true }
116
+ result.should == true
117
+ end
118
+ it "works for constants" do
119
+ result = false
120
+ :john.rubylog_unify(:john) { result = true }
121
+ result.should == true
122
+ end
123
+ it "fails for different constants" do
124
+ result = false
125
+ :john.rubylog_unify(:mary) { result = true }
126
+ result.should == false
127
+ end
128
+ it "works on clauses" do
129
+ result = false
130
+ (:john.likes :beer).rubylog_unify(A.likes B) { result = true }
131
+ result.should == true
132
+ end
133
+ it "works on clauses with equal values" do
134
+ result = false
135
+ (:john.likes :beer).rubylog_unify(:john.likes :beer) { result = true }
136
+ result.should == true
137
+ end
138
+ it "works on clauses with different values" do
139
+ result = false
140
+ (:john.likes :beer).rubylog_unify(:john.likes :milk) { result = true }
141
+ result.should == false
142
+ end
143
+ it "works on clauses with variables and equal values" do
144
+ result = false
145
+ (:john.likes :beer).rubylog_unify(X.likes :beer) { result = true }
146
+ result.should == true
147
+ end
148
+ it "works on clauses with variables and equal values #2" do
149
+ result = false
150
+ (:john.likes :beer).rubylog_unify(:john.likes DRINK) { result = true }
151
+ result.should == true
152
+ end
153
+ it "works on clauses with variables and different values" do
154
+ result = false
155
+ (:john.likes :beer).rubylog_unify(X.likes :milk) { result = true }
156
+ result.should == false
157
+ end
158
+ it "works on clauses with variables and different values #2" do
159
+ result = false
160
+ (:john.likes :beer).rubylog_unify(:jane.likes D) { result = true }
161
+ result.should == false
162
+ end
163
+
164
+ it "works on clauses with repeated variables #1" do
165
+ result = false
166
+ (A.likes A).rubylog_compile_variables.rubylog_unify(:john.likes :jane) { result = true }
167
+ result.should == false
168
+ (A.likes A).rubylog_compile_variables.rubylog_unify(:john.likes :john) { result = true }
169
+ result.should == true
170
+ end
171
+ it "works on clauses with repeated variables #1" do
172
+ result = false
173
+ (:john.likes :jane).rubylog_unify(A.likes(A).rubylog_compile_variables) { result = true }
174
+ result.should == false
175
+ (:john.likes :john).rubylog_unify(A.likes(A).rubylog_compile_variables) { result = true }
176
+ result.should == true
177
+ end
178
+
179
+ it "works for second-order variables" do
180
+ result = false
181
+ (:john.likes :beer).rubylog_unify(A) { result = true }
182
+ result.should == true
183
+ end
184
+
185
+ end
186
+
187
+ describe "queries" do
188
+ it "can be run with true?" do
189
+ lambda {Rubylog.theory.true?(:john.likes :beer)}.should raise_error(Rubylog::ExistenceError)
190
+ :john.likes! :beer
191
+ Rubylog.theory.true?(:john.likes :beer).should be_true
192
+ Rubylog.theory.true?(:john.likes :milk).should be_false
193
+ end
194
+
195
+ it "can be run with question mark" do
196
+ lambda {Rubylog.theory.true?(:john.likes :beer)}.should raise_error(Rubylog::ExistenceError)
197
+ :john.likes! :beer
198
+ :john.likes?(:beer).should be_true
199
+ end
200
+
201
+ it "can be run with true?" do
202
+ lambda {Rubylog.theory.true?(:john.likes :beer)}.should raise_error(Rubylog::ExistenceError)
203
+ :john.likes! :beer
204
+ (:john.likes(:beer)).true?.should be_true
205
+ end
206
+
207
+ it "work with variables" do
208
+ lambda {Rubylog.theory.true?(:john.likes X)}.should raise_error(Rubylog::ExistenceError)
209
+ :john.likes! :water
210
+ :john.likes?(X).should be_true
211
+ end
212
+
213
+ it "yield all solutions" do
214
+ :john.likes! :beer
215
+ :john.likes! :milk
216
+
217
+ k=[]
218
+ (:john.likes X).each{|x|k << x}
219
+ k.should == [:beer, :milk]
220
+ end
221
+
222
+ it "yield all solutions with solve" do
223
+ :john.likes! :beer
224
+ :john.likes! :milk
225
+
226
+ k=[]
227
+ (:john.likes X).solve{|x|k << x}
228
+ k.should == [:beer, :milk]
229
+ end
230
+
231
+ it "yield all solutions with solve and multiple vars and multiple block parameters" do
232
+ :john.likes! :beer
233
+ :jane.likes! :milk
234
+ :jane.likes! :water
235
+
236
+ k=[]
237
+ (X.likes Y).solve{|a,b|k << [a,b]}
238
+ k.should == [[:john, :beer], [:jane, :milk], [:jane, :water]]
239
+ end
240
+
241
+ it "ignore don't-care variables" do
242
+ :john.likes! :beer
243
+
244
+ k=[]
245
+ ANYONE.likes(X).each{|x|k << x}
246
+ k.should == [:beer]
247
+
248
+ k=[]
249
+ X.likes(ANYTHING).each{|x|k << x}
250
+ k.should == [:john]
251
+ end
252
+
253
+ it "makes sure all variables are instantiated" do
254
+ res = []
255
+ A.likes(B).if {|a,b| res << a << b }
256
+ A.likes? :beer
257
+ res.should == [nil,:beer]
258
+ end
259
+
260
+ it "substitutes deeper variables" do
261
+ res = []
262
+ A.likes(B).if {|a,b| res << a << b }
263
+ (A.is(:john).and B.is(:swimming.in C).and
264
+ C.is(:sea).and A.likes B).to_a.should == [[:john,:swimming.in(:sea),:sea]]
265
+ res.should == [:john, :swimming.in(:sea)]
266
+ end
267
+
268
+
269
+ describe "support Enumerable" do
270
+ before do
271
+ :john.likes! :beer
272
+ :john.likes! :milk
273
+ end
274
+
275
+ it "#all?, #any? and #none?" do
276
+ (:john.likes A).all?{|a| Symbol===a}.should be_true
277
+ (:john.likes A).all?{|a| a == :beer}.should be_false
278
+ (:john.likes A).all?{|a| a == :beer or a == :milk}.should be_true
279
+ (:john.likes A).any?{|a| a == :beer}.should be_true
280
+ (:john.likes A).any?{|a| a == :milk}.should be_true
281
+ (:john.likes A).any?{|a| a == :water}.should be_false
282
+ (:john.likes A).none?{|a| a == :water}.should be_true
283
+ (:john.likes A).none?{|a| a == :beer}.should be_false
284
+ end
285
+
286
+ it "#to_a" do
287
+ (:john.likes A).to_a.should == [:beer, :milk]
288
+ (X.likes A).to_a.should == [[:john, :beer], [:john, :milk]]
289
+ (ANYONE.likes A).to_a.should == [:beer, :milk]
290
+ end
291
+
292
+ it "#first" do
293
+ (:john.likes A).first.should == :beer
294
+ end
295
+
296
+ it "#map" do
297
+ (:john.likes A).map{|a|a.to_s}.should == ['beer', 'milk']
298
+ end
299
+
300
+ it "#include? and #member?" do
301
+ (:john.likes B).member?(:beer).should be_true
302
+ (:john.likes B).include?(:beer).should be_true
303
+ (:john.likes B).member?(:milk).should be_true
304
+ (:john.likes B).include?(:milk).should be_true
305
+ (:john.likes B).member?(:water).should be_false
306
+ (:john.likes B).include?(:water).should be_false
307
+ end
308
+
309
+ end
310
+
311
+ it "can yield solutions with vars substituted" do
312
+ :john.likes! :beer
313
+ :john.likes! :milk
314
+ :jane.likes! :milk
315
+
316
+ (A.likes B).solutions.should == [
317
+ :john.likes(:beer),
318
+ :john.likes(:milk),
319
+ :jane.likes(:milk)
320
+ ]
321
+ (A.likes(B).and A.is :john).solutions.should == [
322
+ :john.likes(:beer).and(:john.is :john),
323
+ :john.likes(:milk).and(:john.is :john)
324
+ ]
325
+ (:john.likes(B)).solutions.should == [
326
+ :john.likes(:beer),
327
+ :john.likes(:milk)
328
+ ]
329
+ (A.likes(:milk)).solutions.should == [
330
+ :john.likes(:milk),
331
+ :jane.likes(:milk)
332
+ ]
333
+ end
334
+
335
+ end
336
+
337
+ describe "using ruby code in clauses" do
338
+ it "works" do
339
+ (:true.and? {false}).should be_false
340
+ (:true.and? {true}).should be_true
341
+ (:false.and? {false}).should be_false
342
+ (:false.and? {true}).should be_false
343
+ (:true.or? {false}).should be_true
344
+ (:true.or? {true}).should be_true
345
+ (:false.or? {false}).should be_false
346
+ (:false.or? {true}).should be_true
347
+
348
+ (:fail.or? {false}).should be_false
349
+ (:fail.or? {true}).should be_true
350
+ end
351
+ it "runs the query once at every evaluation" do
352
+ count = 0
353
+ :john.is_happy.if :true.and { count += 1 }
354
+ count.should == 0
355
+ :john.is_happy?
356
+ count.should == 1
357
+ :john.is_happy?
358
+ count.should == 2
359
+ (:false.or? {count+=1}).should be_true
360
+ count.should == 3
361
+ end
362
+
363
+ describe "bindings" do
364
+ it "works for rule bodies" do
365
+ result = nil;
366
+ (A.likes(B).if {|*args| result = args})
367
+ (:john.likes(:beer)).solve{}
368
+ result.should == [:john,:beer]
369
+ end
370
+
371
+ it "works for rules" do
372
+ result = nil
373
+ (A.likes(B).if B.is(4).and A.is(2).and C.is(5).and {|*args| result = args})
374
+ (A.likes(B)).solve{}
375
+ result.should == [2,4,5]
376
+ end
377
+
378
+ it "works for inline terms" do
379
+ result = nil
380
+ (A.is(1).and B.is(2).and {|*args| result = args}).solve{}
381
+ result.should == [1,2]
382
+ end
383
+
384
+
385
+
386
+ end
387
+ end
388
+
389
+ describe "rules" do
390
+ describe "with prolog body" do
391
+ it "cannot be asserted in a builtin's desc" do
392
+ lambda {
393
+ :john.likes(:beer).and! :jane.likes(:milk)
394
+ }.should raise_error(Rubylog::BuiltinPredicateError)
395
+ end
396
+
397
+ it "can be asserted with if" do
398
+ Rubylog.theory.predicate [:we_have, 2]
399
+ :john.is_happy.if :-@.we_have(:beer)
400
+ :john.is_happy?.should be_false
401
+ :-@.we_have!(:beer)
402
+ :john.is_happy?.should be_true
403
+ end
404
+
405
+ it "can be asserted with unless" do
406
+ Rubylog.theory.predicate [:we_have, 2]
407
+ :john.is_happy.unless :-@.we_have(:problem)
408
+ :john.is_happy?.should be_true
409
+ :-@.we_have!(:problem)
410
+ :john.is_happy?.should be_false
411
+ end
412
+
413
+ it "can do simple general implications" do
414
+ Rubylog.theory.predicate [:is_happy,1], [:has,2]
415
+ Rubylog.theory.discontinuous [:likes,2]
416
+ X.is_happy.if X.likes(Y).and X.has(Y)
417
+ :john.likes! :milk
418
+ :john.is_happy?.should be_false
419
+ :john.has! :beer
420
+ :john.is_happy?.should be_false
421
+ :john.likes! :beer
422
+ :john.is_happy?.should be_true
423
+ end
424
+
425
+ it "can yield implied solutions" do
426
+ X.brother(Y).if X.father(Z).and Y.father(Z).and X.neq(Y)
427
+ X.uncle(Y).if X.father(Z).and Z.brother(Y)
428
+ X.neq(Y).if proc {|x,y|x != y}
429
+
430
+ :john.father! :dad
431
+ :jack.father! :dad
432
+ :dad.father! :grandpa
433
+ :jim.father! :grandpa
434
+
435
+ (:john.brother X).to_a.should == [:jack]
436
+ (:john.father X).to_a.should == [:dad]
437
+ (X.father :dad).to_a.should == [:john, :jack]
438
+ (ANY.father X).to_a.should == [:dad, :dad, :grandpa, :grandpa]
439
+ (:john.uncle X).to_a.should == [:jim]
440
+ end
441
+ end
442
+
443
+ describe "with ruby body" do
444
+ it "can be asserted (true)" do
445
+ :john.is_happy.if proc{ true }
446
+ :john.is_happy?.should be_true
447
+ end
448
+ it "can be asserted (false)" do
449
+ :john.is_happy.if proc{ false }
450
+ :john.is_happy?.should be_false
451
+ end
452
+
453
+ it "can be asserted implicitly (true)" do
454
+ :john.is_happy.if { true }
455
+ :john.is_happy?.should be_true
456
+ end
457
+
458
+ it "can be asserted implicitly (false)" do
459
+ :john.is_happy.if { false }
460
+ :john.is_happy?.should be_false
461
+ end
462
+
463
+ it "run the body during every query" do
464
+ count = 0
465
+ :john.is_happy.if proc{ count += 1 }
466
+ count.should == 0
467
+ :john.is_happy?
468
+ count.should == 1
469
+ :john.is_happy?
470
+ count.should == 2
471
+ end
472
+
473
+ it "can take arguments" do
474
+ (A.divides B).if proc{|a,b| b % a == 0}
475
+ (4.divides? 16).should be_true
476
+ (4.divides? 17).should be_false
477
+ (4.divides? 18).should be_false
478
+ (3.divides? 3).should be_true
479
+ (3.divides? 4).should be_false
480
+ (3.divides? 5).should be_false
481
+ (3.divides? 6).should be_true
482
+ end
483
+ end
484
+ end
485
+
486
+ describe "custom classes" do
487
+ before do
488
+ class User
489
+ rubylog_functor :girl, :boy
490
+ include Rubylog::DSL::Constants
491
+
492
+ attr_reader :name
493
+ def initialize name
494
+ @name = name
495
+ end
496
+
497
+ U.girl.if {|u| u.name =~ /[aeiouh]$/ }
498
+ U.boy.unless U.girl
499
+ end
500
+ end
501
+
502
+ it "can have ruby predicates" do
503
+ john = User.new "John"
504
+ john.girl?.should be_false
505
+ john.boy?.should be_true
506
+ jane = User.new "Jane"
507
+ jane.girl?.should be_true
508
+ jane.boy?.should be_false
509
+ end
510
+
511
+ it "can be used in assertions" do
512
+ pete = User.new "Pete"
513
+ pete.boy?.should be_false
514
+ pete.boy!
515
+ pete.boy?.should be_true
516
+
517
+ Rubylog.theory[:girl][1].discontinuous!
518
+ janet = User.new "Janet"
519
+ janet.girl?.should be_false
520
+ janet.girl!
521
+ janet.girl?.should be_true
522
+ end
523
+
524
+
525
+
526
+
527
+ end
528
+
529
+
530
+ describe "builtin" do
531
+ it "true" do
532
+ :john.happy.if :true
533
+ :john.should be_happy
534
+ end
535
+
536
+ it "fail" do
537
+ :john.happy.if :fail
538
+ :john.should_not be_happy
539
+ end
540
+
541
+ it "false" do
542
+ :john.happy.if :false
543
+ :john.should_not be_happy
544
+ end
545
+
546
+ describe "branch or" do
547
+ it "works 1" do
548
+ :john.happy.if :fail
549
+ :john.happy.if :true
550
+ :john.should be_happy
551
+ end
552
+
553
+ it "works 2" do
554
+ :john.happy.if :true
555
+ :john.happy.if :fail
556
+ :john.should be_happy
557
+ end
558
+
559
+ it "works 3" do
560
+ :john.happy.if :fail
561
+ :john.happy.if :fail
562
+ :john.should_not be_happy
563
+ end
564
+
565
+ it "works 4" do
566
+ :john.happy.if :true
567
+ :john.happy.if :true
568
+ :john.should be_happy
569
+ end
570
+ end
571
+
572
+ describe "or" do
573
+ it "works 1" do
574
+ :john.happy.if :fail.or :true
575
+ :john.should be_happy
576
+ end
577
+
578
+ it "works 2" do
579
+ :john.happy.if :true.or :fail
580
+ :john.should be_happy
581
+ end
582
+
583
+ it "works 3" do
584
+ :john.happy.if :fail.or :fail
585
+ :john.should_not be_happy
586
+ end
587
+
588
+ it "works 4" do
589
+ :john.happy.if :true.or :true
590
+ :john.should be_happy
591
+ end
592
+ end
593
+
594
+ describe "and" do
595
+ it "works 1" do
596
+ :john.happy.if :fail.and :true
597
+ :john.should_not be_happy
598
+ end
599
+
600
+ it "works 2" do
601
+ :john.happy.if :true.and :fail
602
+ :john.should_not be_happy
603
+ end
604
+
605
+ it "works 3" do
606
+ :john.happy.if :fail.and :fail
607
+ :john.should_not be_happy
608
+ end
609
+
610
+ it "works 4" do
611
+ :john.happy.if :true.and :true
612
+ :john.should be_happy
613
+ end
614
+ end
615
+
616
+ describe "then" do
617
+ it "works 1" do
618
+ :john.happy.if :fail.then :true
619
+ :john.should_not be_happy
620
+ end
621
+
622
+ it "works 2" do
623
+ :john.happy.if :true.then :fail
624
+ :john.should_not be_happy
625
+ end
626
+
627
+ it "works 3" do
628
+ :john.happy.if :fail.then :fail
629
+ :john.should_not be_happy
630
+ end
631
+
632
+ it "works 4" do
633
+ :john.happy.if :true.then :true
634
+ :john.should be_happy
635
+ end
636
+
637
+ it "works 5" do
638
+ :john.happy.if :true.then :true
639
+ :john.should be_happy
640
+ end
641
+ end
642
+
643
+ describe "cut" do
644
+ it "works with branch or" do
645
+ :john.happy.if :true.and :cut.and :fail
646
+ :john.happy.if :true
647
+ :john.should_not be_happy
648
+ end
649
+ it "works with branch or (control)" do
650
+ :john.happy.if :true.and :fail
651
+ :john.happy.if :true
652
+ :john.should be_happy
653
+ end
654
+
655
+ it "works with or" do
656
+ :john.happy.if((:true.and :cut.and :fail).or :true)
657
+ :john.should_not be_happy
658
+ end
659
+
660
+ it "works with or (control)" do
661
+ :john.happy.if((:true.and :fail).or :true)
662
+ :john.should be_happy
663
+ end
664
+
665
+ it "returns true with branch or" do
666
+ :john.happy.if :true.and :cut.and :true
667
+ :john.happy.if :true
668
+ :john.should be_happy
669
+ end
670
+ it "returns true with branch or (control)" do
671
+ :john.happy.if :true.and :true
672
+ :john.happy.if :true
673
+ :john.should be_happy
674
+ end
675
+
676
+ it "returns true with or" do
677
+ :john.happy.if((:true.and :cut.and :true).or :true)
678
+ :john.should be_happy
679
+ end
680
+
681
+ it "returns true with or (control)" do
682
+ :john.happy.if((:true.and :true).or :true)
683
+ :john.should be_happy
684
+ end
685
+ end
686
+
687
+ describe "is_false" do
688
+ it "works 5" do
689
+ :john.happy.if :true.is_false
690
+ :john.should_not be_happy
691
+ end
692
+
693
+ it "works 5" do
694
+ :john.happy.if :fail.is_false
695
+ :john.should be_happy
696
+ end
697
+ end
698
+
699
+ describe "is" do
700
+ before do
701
+ :john.likes! :beer
702
+ :jane.likes! :milk
703
+ end
704
+
705
+ it "works for variables" do
706
+ (A.likes(B).and(B.is :milk)).to_a.should == [[:jane, :milk]]
707
+ (A.likes(B).and(:milk.is B)).to_a.should == [[:jane, :milk]]
708
+ end
709
+
710
+ it "works as calculation" do
711
+ (A.is {|| 4+4}).to_a.should == [8]
712
+ (A.is {4+4}).to_a.should == [8]
713
+ (A.is(4).and A.is{2*2}).to_a.should == [4]
714
+ (A.is(4).and A.is{2*3}).to_a.should == []
715
+ end
716
+
717
+ it "works as calculation with vars" do
718
+ (A.is(4).and B.is{|a|a*4}).to_a.should == [[4,16]]
719
+ (A.is(4).and A.is{|a|a*1}).to_a.should == [4]
720
+ (A.is(4).and A.is{|a|a*2}).to_a.should == []
721
+ end
722
+
723
+ end
724
+
725
+ describe "matches" do
726
+ before do
727
+ :john.likes! "Beer"
728
+ :jane.likes! "Water"
729
+ end
730
+
731
+ it "works for variables" do
732
+ (A.likes(B).and(B.matches /e/)).to_a.should == [[:john, "Beer"], [:jane, "Water"]]
733
+ (A.likes(B).and(B.matches /ee/)).to_a.should == [[:john, "Beer"]]
734
+ (A.likes(B).and(B.matches /w/i)).to_a.should == [[:jane, "Water"]]
735
+ end
736
+
737
+ it "works as calculation" do
738
+ (A.likes(B).and(B.matches {|a,b|/e/})).to_a.should == [[:john, "Beer"], [:jane, "Water"]]
739
+ (A.likes(B).and(B.matches {|a,b|/ee/})).to_a.should == [[:john, "Beer"]]
740
+ (A.likes(B).and(B.matches {|a,b|/w/i})).to_a.should == [[:jane, "Water"]]
741
+ (A.likes(B).and(B.matches {|a,b|b})).to_a.should == [[:john, "Beer"], [:jane, "Water"]]
742
+ (A.likes(B).and(B.matches {|a,b|a})).to_a.should == []
743
+ end
744
+
745
+
746
+ end
747
+
748
+ describe "in" do
749
+ before do
750
+ :john.likes! :beer
751
+ :jane.likes! :milk
752
+ end
753
+
754
+ it "works for variables" do
755
+ (A.likes(B).and(B.in [])).to_a.should == []
756
+ (A.likes(B).and(B.in [:milk])).to_a.should == [[:jane, :milk]]
757
+ (A.likes(B).and(B.in [:beer])).to_a.should == [[:john, :beer]]
758
+ (A.likes(B).and(B.in [:milk, :beer])).to_a.should == [[:john, :beer], [:jane, :milk]]
759
+ end
760
+
761
+ it "works with blocks" do
762
+ (A.likes(B).and(B.in {[]})).to_a.should == []
763
+ (A.likes(B).and(B.in {|a|[a,:milk]})).to_a.should == [[:jane, :milk]]
764
+ (A.likes(B).and(B.in {|a,b|[:beer]})).to_a.should == [[:john, :beer]]
765
+ (A.likes(B).and(B.in {|a,b|[b]})).to_a.should == [[:john, :beer], [:jane, :milk]]
766
+ end
767
+
768
+ it "works as iterator" do
769
+ (A.in{[1,3,4]}).to_a.should == [1,3,4]
770
+ (A.in [1,3,4]).to_a.should == [1,3,4]
771
+ end
772
+
773
+ it "works as search" do
774
+ (1.in{[1,3,4]}).to_a.should == [nil]
775
+ (2.in{[1,3,4]}).to_a.should == []
776
+ (1.in [1,3,4]).to_a.should == [nil]
777
+ (2.in [1,3,4]).to_a.should == []
778
+ end
779
+
780
+ it "works with clauses" do
781
+ (A.likes(B).and B.in{:john.likes(X)}).to_a.should == [[:john, :beer]]
782
+ end
783
+
784
+ end
785
+
786
+ describe "all,any,one,none" do
787
+ before do
788
+ :john.likes! :water
789
+ :john.likes! :beer
790
+
791
+ :jane.likes! :water
792
+ :jane.likes! :milk
793
+ :jane.likes! :beer
794
+
795
+ :jeff.likes! :water
796
+ :jeff.likes! :absinth
797
+
798
+ :todd.likes! :milk
799
+
800
+ @predicates = [:all, :any, :one, :none]
801
+ @names = :john, :jane, :jeff, :todd
802
+ @good =
803
+ [
804
+ [[1,1,0,0], [0,1,0,0], [0,0,1,0], [0,1,0,1]], # all
805
+ [[1,1,1,0], [1,1,1,1], [1,1,1,0], [0,1,0,1]], # any
806
+ [[0,0,1,0], [0,0,1,1], [1,1,0,0], [0,1,0,1]], # one
807
+ [[0,0,0,1], [0,0,0,0], [0,0,0,1], [1,0,1,0]] # none
808
+ ]
809
+ end
810
+
811
+ it "work" do
812
+ @predicates.map{|p| @names.map{|n| @names.map{|m|
813
+ (n.likes(K).send p, m.likes(K)).true? ? 1 : 0
814
+ }}}.should == @good
815
+ end
816
+
817
+ it "mimic well enumerators' predicates" do
818
+ @predicates.map{|p| @names.map{|n| @names.map{|m|
819
+ n.likes(K).to_a.send(:"#{p}?"){|x| m.likes?(x) } ? 1 : 0
820
+ }}}.should == @good
821
+ end
822
+
823
+
824
+
825
+ it "all works" do
826
+ (:john.likes(X).all(:john.likes(X))).should stand
827
+ (:john.likes(X).all(:jane.likes(X))).should stand
828
+ (:john.likes(X).all(:jeff.likes(X))).should_not stand
829
+ (:john.likes(X).all(:todd.likes(X))).should_not stand
830
+
831
+ (:jane.likes(X).all(:john.likes(X))).should_not stand
832
+ (:jane.likes(X).all(:jane.likes(X))).should stand
833
+ (:jane.likes(X).all(:jeff.likes(X))).should_not stand
834
+ (:jane.likes(X).all(:todd.likes(X))).should_not stand
835
+
836
+ (:jeff.likes(X).all(:john.likes(X))).should_not stand
837
+ (:jeff.likes(X).all(:jane.likes(X))).should_not stand
838
+ (:jeff.likes(X).all(:jeff.likes(X))).should stand
839
+ (:jeff.likes(X).all(:todd.likes(X))).should_not stand
840
+
841
+ (:todd.likes(X).all(:john.likes(X))).should_not stand
842
+ (:todd.likes(X).all(:jane.likes(X))).should stand
843
+ (:todd.likes(X).all(:jeff.likes(X))).should_not stand
844
+ (:todd.likes(X).all(:todd.likes(X))).should stand
845
+ end
846
+
847
+ it "can be called with global functor syntax" do
848
+ extend Rubylog::DSL::GlobalFunctors
849
+ all(:john.likes(X), :jane.likes(X)).should stand
850
+ all(:jane.likes(X), :john.likes(X)).should_not stand
851
+ any(:jane.likes(X), :todd.likes(X)).should stand
852
+ any(:john.likes(X), :todd.likes(X)).should_not stand
853
+ end
854
+
855
+ it "can be called unarily" do
856
+ extend Rubylog::DSL::GlobalFunctors
857
+ one(:john.likes(X)).should_not stand
858
+ one(:jane.likes(X)).should_not stand
859
+ one(:jeff.likes(X)).should_not stand
860
+ one(:todd.likes(X)).should stand
861
+ one(:jim.likes(X)).should_not stand
862
+
863
+ any(:john.likes(X)).should stand
864
+ any(:jane.likes(X)).should stand
865
+ any(:jeff.likes(X)).should stand
866
+ any(:todd.likes(X)).should stand
867
+ any(:jim.likes(X)).should_not stand
868
+
869
+ all(:john.likes(X)).should stand
870
+ all(:jane.likes(X)).should stand
871
+ all(:jeff.likes(X)).should stand
872
+ all(:todd.likes(X)).should stand
873
+ all(:jim.likes(X)).should stand
874
+
875
+ none(:john.likes(X)).should_not stand
876
+ none(:jane.likes(X)).should_not stand
877
+ none(:jeff.likes(X)).should_not stand
878
+ none(:todd.likes(X)).should_not stand
879
+ none(:jim.likes(X)).should stand
880
+ end
881
+ end
882
+
883
+ end
884
+
885
+ describe "Array" do
886
+ it "can be unified" do
887
+ result = false
888
+ [A,B].rubylog_unify(12) { result = true }
889
+ result.should == false
890
+
891
+ result = false
892
+ [A,B].rubylog_unify([12,13]) { result = true }
893
+ result.should == true
894
+
895
+ result = false
896
+ [14,B].rubylog_unify([12,13]) { result = true }
897
+ result.should == false
898
+ end
899
+ end
900
+
901
+
902
+
903
+
904
+ end
905
+
906
+
907
+ end
908
+
909
+
910
+
911
+
912
+
913
+
914
+