rubylog 0.0.1 → 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.
- data/Gemfile +1 -1
- data/README.hu.rb +58 -0
- data/README.rdoc +248 -89
- data/Rakefile +6 -1
- data/VERSION +1 -1
- data/bin/rubylog +18 -0
- data/examples/dcg.rb +35 -0
- data/examples/dcg2.rb +42 -0
- data/examples/enumerators.rb +30 -0
- data/examples/factorial.rb +9 -8
- data/examples/hanoi.rb +24 -0
- data/examples/hello.rb +11 -5
- data/examples/parsing.rb +27 -0
- data/examples/primitives.rb +24 -0
- data/examples/theory.rb +22 -10
- data/lib/rubylog/builtins/default.rb +10 -0
- data/lib/rubylog/builtins/file_system.rb +15 -0
- data/lib/rubylog/builtins/logic.rb +109 -0
- data/lib/rubylog/builtins/reflection.rb +94 -0
- data/lib/rubylog/builtins/term.rb +47 -0
- data/lib/rubylog/dsl/array_splat.rb +25 -0
- data/lib/rubylog/dsl/primitives.rb +17 -0
- data/lib/rubylog/dsl/thats.rb +22 -0
- data/lib/rubylog/dsl/variables.rb +30 -0
- data/lib/rubylog/dsl.rb +35 -17
- data/lib/rubylog/errors.rb +19 -1
- data/lib/rubylog/interfaces/assertable.rb +16 -0
- data/lib/rubylog/interfaces/callable.rb +18 -0
- data/lib/rubylog/interfaces/composite_term.rb +47 -0
- data/lib/rubylog/interfaces/predicate.rb +8 -0
- data/lib/rubylog/interfaces/procedure.rb +60 -0
- data/lib/rubylog/interfaces/term.rb +41 -0
- data/lib/rubylog/mixins/array.rb +118 -0
- data/lib/{class.rb → rubylog/mixins/class.rb} +2 -2
- data/lib/rubylog/mixins/hash.rb +8 -0
- data/lib/rubylog/mixins/kernel.rb +5 -0
- data/lib/rubylog/mixins/method.rb +3 -0
- data/lib/rubylog/mixins/object.rb +8 -0
- data/lib/rubylog/mixins/proc.rb +37 -0
- data/lib/rubylog/mixins/string.rb +104 -0
- data/lib/rubylog/mixins/symbol.rb +44 -0
- data/lib/rubylog/simple_procedure.rb +8 -0
- data/lib/rubylog/{clause.rb → structure.rb} +32 -31
- data/lib/rubylog/theory.rb +368 -79
- data/lib/rubylog/variable.rb +102 -23
- data/lib/rubylog.rb +33 -25
- data/logic/builtins/file_system_logic.rb +23 -0
- data/logic/builtins/reflection_logic.rb +40 -0
- data/logic/dereference_logic.rb +23 -0
- data/logic/directory_structure_logic.rb +19 -0
- data/logic/dsl_logic.rb +29 -0
- data/logic/errors_logic.rb +9 -0
- data/logic/guard_logic.rb +115 -0
- data/logic/list_logic.rb +55 -0
- data/logic/map_logic.rb +15 -0
- data/logic/multitheory.rb +23 -0
- data/logic/recursion_logic.rb +12 -0
- data/logic/string_logic.rb +41 -0
- data/logic/thats_logic.rb +51 -0
- data/logic/variable_logic.rb +24 -0
- data/rubylog.gemspec +85 -46
- data/spec/bartak_guide_spec.rb +57 -62
- data/spec/builtins/all_spec.rb +99 -0
- data/spec/builtins/and_spec.rb +22 -0
- data/spec/builtins/array_spec.rb +16 -0
- data/spec/builtins/branch_or_spec.rb +27 -0
- data/spec/builtins/cut_spec.rb +44 -0
- data/spec/builtins/fail_spec.rb +5 -0
- data/spec/builtins/false_spec.rb +5 -0
- data/spec/builtins/in_spec.rb +38 -0
- data/spec/builtins/is_false_spec.rb +12 -0
- data/spec/builtins/is_spec.rb +26 -0
- data/spec/builtins/matches_spec.rb +23 -0
- data/spec/builtins/or_spec.rb +22 -0
- data/spec/{rubylog/builtins → builtins}/splits_to.rb +0 -0
- data/spec/builtins/then_spec.rb +27 -0
- data/spec/builtins/true_spec.rb +5 -0
- data/spec/clause_spec.rb +82 -0
- data/spec/compilation_spec.rb +61 -0
- data/spec/custom_classes_spec.rb +43 -0
- data/spec/dereference.rb +10 -0
- data/spec/{inriasuite.rb → inriasuite_spec.rb} +2 -9
- data/spec/queries_spec.rb +150 -0
- data/spec/recursion_spec.rb +4 -4
- data/spec/ruby_code_spec.rb +52 -0
- data/spec/rules_spec.rb +97 -0
- data/spec/spec_helper.rb +6 -2
- data/spec/theory_spec.rb +28 -0
- data/spec/unification_spec.rb +84 -0
- data/spec/variable_spec.rb +26 -0
- metadata +153 -180
- data/examples/4queens.rb +0 -10
- data/examples/calculation.rb +0 -12
- data/examples/concepts.rb +0 -46
- data/examples/fp.rb +0 -56
- data/examples/historia_de_espana.rb +0 -31
- data/examples/idea.rb +0 -143
- data/examples/lists.rb +0 -5
- data/examples/mechanika.rb +0 -409
- data/examples/parse.rb +0 -15
- data/lib/array.rb +0 -24
- data/lib/method.rb +0 -4
- data/lib/object.rb +0 -5
- data/lib/proc.rb +0 -4
- data/lib/rubylog/builtins.rb +0 -193
- data/lib/rubylog/callable.rb +0 -20
- data/lib/rubylog/composite_term.rb +0 -38
- data/lib/rubylog/dsl/constants.rb +0 -15
- data/lib/rubylog/dsl/first_order_functors.rb +0 -9
- data/lib/rubylog/dsl/global_functors.rb +0 -3
- data/lib/rubylog/dsl/second_order_functors.rb +0 -8
- data/lib/rubylog/internal_helpers.rb +0 -16
- data/lib/rubylog/predicate.rb +0 -34
- data/lib/rubylog/proc_method_additions.rb +0 -69
- data/lib/rubylog/term.rb +0 -20
- data/lib/rubylog/unifiable.rb +0 -19
- data/lib/symbol.rb +0 -35
- data/script/inriasuite2spec +0 -0
- data/script/inriasuite2spec.pl +0 -22
- data/spec/rubylog/clause_spec.rb +0 -81
- data/spec/rubylog/variable_spec.rb +0 -25
- data/spec/rubylog_spec.rb +0 -914
data/spec/rubylog_spec.rb
DELETED
@@ -1,914 +0,0 @@
|
|
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
|
-
|