mort666-wongi-engine 0.2.9

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 (93) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +20 -0
  3. data/.hgignore +6 -0
  4. data/.hgtags +13 -0
  5. data/.ruby-gemset +1 -0
  6. data/.travis.yml +19 -0
  7. data/CHANGELOG.md +106 -0
  8. data/Gemfile +4 -0
  9. data/LICENSE +22 -0
  10. data/README.md +27 -0
  11. data/Rakefile +9 -0
  12. data/examples/ex01.rb +23 -0
  13. data/examples/ex02.rb +37 -0
  14. data/examples/graphviz.rb +16 -0
  15. data/examples/rdf.n3 +6 -0
  16. data/examples/rdf.rb +14 -0
  17. data/examples/timeline.rb +48 -0
  18. data/lib/wongi-engine.rb +36 -0
  19. data/lib/wongi-engine/alpha_memory.rb +60 -0
  20. data/lib/wongi-engine/beta.rb +11 -0
  21. data/lib/wongi-engine/beta/assignment_node.rb +40 -0
  22. data/lib/wongi-engine/beta/beta_memory.rb +49 -0
  23. data/lib/wongi-engine/beta/beta_node.rb +94 -0
  24. data/lib/wongi-engine/beta/filter_node.rb +48 -0
  25. data/lib/wongi-engine/beta/join_node.rb +140 -0
  26. data/lib/wongi-engine/beta/ncc_node.rb +67 -0
  27. data/lib/wongi-engine/beta/ncc_partner.rb +40 -0
  28. data/lib/wongi-engine/beta/neg_node.rb +115 -0
  29. data/lib/wongi-engine/beta/optional_node.rb +142 -0
  30. data/lib/wongi-engine/beta/or_node.rb +37 -0
  31. data/lib/wongi-engine/beta/production_node.rb +31 -0
  32. data/lib/wongi-engine/compiler.rb +115 -0
  33. data/lib/wongi-engine/core_ext.rb +63 -0
  34. data/lib/wongi-engine/data_overlay.rb +144 -0
  35. data/lib/wongi-engine/dsl.rb +132 -0
  36. data/lib/wongi-engine/dsl/action/base.rb +11 -0
  37. data/lib/wongi-engine/dsl/action/error_generator.rb +31 -0
  38. data/lib/wongi-engine/dsl/action/simple_action.rb +60 -0
  39. data/lib/wongi-engine/dsl/action/simple_collector.rb +52 -0
  40. data/lib/wongi-engine/dsl/action/statement_generator.rb +46 -0
  41. data/lib/wongi-engine/dsl/action/trace_action.rb +49 -0
  42. data/lib/wongi-engine/dsl/any_rule.rb +33 -0
  43. data/lib/wongi-engine/dsl/assuming.rb +31 -0
  44. data/lib/wongi-engine/dsl/builder.rb +44 -0
  45. data/lib/wongi-engine/dsl/clause/assign.rb +15 -0
  46. data/lib/wongi-engine/dsl/clause/fact.rb +71 -0
  47. data/lib/wongi-engine/dsl/clause/gen.rb +17 -0
  48. data/lib/wongi-engine/dsl/clause/generic.rb +38 -0
  49. data/lib/wongi-engine/dsl/generated.rb +43 -0
  50. data/lib/wongi-engine/dsl/ncc_subrule.rb +17 -0
  51. data/lib/wongi-engine/dsl/query.rb +24 -0
  52. data/lib/wongi-engine/dsl/rule.rb +84 -0
  53. data/lib/wongi-engine/enumerators.rb +21 -0
  54. data/lib/wongi-engine/error.rb +22 -0
  55. data/lib/wongi-engine/filter.rb +6 -0
  56. data/lib/wongi-engine/filter/asserting_test.rb +20 -0
  57. data/lib/wongi-engine/filter/equality_test.rb +36 -0
  58. data/lib/wongi-engine/filter/filter_test.rb +18 -0
  59. data/lib/wongi-engine/filter/greater_than_test.rb +36 -0
  60. data/lib/wongi-engine/filter/inequality_test.rb +36 -0
  61. data/lib/wongi-engine/filter/less_than_test.rb +36 -0
  62. data/lib/wongi-engine/graph.rb +73 -0
  63. data/lib/wongi-engine/network.rb +416 -0
  64. data/lib/wongi-engine/network/collectable.rb +42 -0
  65. data/lib/wongi-engine/network/debug.rb +85 -0
  66. data/lib/wongi-engine/ruleset.rb +74 -0
  67. data/lib/wongi-engine/template.rb +78 -0
  68. data/lib/wongi-engine/token.rb +114 -0
  69. data/lib/wongi-engine/version.rb +5 -0
  70. data/lib/wongi-engine/wme.rb +89 -0
  71. data/lib/wongi-engine/wme_match_data.rb +34 -0
  72. data/spec/beta_node_spec.rb +29 -0
  73. data/spec/bug_specs/issue_4_spec.rb +141 -0
  74. data/spec/dataset_spec.rb +27 -0
  75. data/spec/dsl_spec.rb +9 -0
  76. data/spec/filter_specs/assert_test_spec.rb +102 -0
  77. data/spec/filter_specs/less_test_spec.rb +41 -0
  78. data/spec/generation_spec.rb +116 -0
  79. data/spec/high_level_spec.rb +378 -0
  80. data/spec/network_spec.rb +182 -0
  81. data/spec/overlay_spec.rb +61 -0
  82. data/spec/rule_specs/any_rule_spec.rb +75 -0
  83. data/spec/rule_specs/assign_spec.rb +88 -0
  84. data/spec/rule_specs/assuming_spec.rb +66 -0
  85. data/spec/rule_specs/maybe_rule_spec.rb +101 -0
  86. data/spec/rule_specs/ncc_spec.rb +258 -0
  87. data/spec/rule_specs/negative_rule_spec.rb +105 -0
  88. data/spec/ruleset_spec.rb +54 -0
  89. data/spec/simple_action_spec.rb +40 -0
  90. data/spec/spec_helper.rb +3 -0
  91. data/spec/wme_spec.rb +83 -0
  92. data/wongi-engine.gemspec +40 -0
  93. metadata +212 -0
@@ -0,0 +1,378 @@
1
+ require 'spec_helper'
2
+
3
+ include Wongi::Engine::DSL
4
+
5
+ dsl {
6
+
7
+ section :make
8
+ clause :test_collector
9
+ action Wongi::Engine::DSL::Action::SimpleCollector.collector
10
+
11
+ }
12
+
13
+ describe 'the engine' do
14
+
15
+ before :each do
16
+ @rete = Wongi::Engine::Network.new
17
+ end
18
+
19
+ def rete
20
+ @rete
21
+ end
22
+
23
+ context 'with a simple generative positive rule' do
24
+
25
+ it 'should generate wmes with an existing rule' do
26
+
27
+ rete << rule('symmetric') {
28
+ forall {
29
+ has :P, "symmetric", true
30
+ has :A, :P, :B
31
+ }
32
+ make {
33
+ gen :B, :P, :A
34
+ }
35
+ }
36
+
37
+ rete << Wongi::Engine::WME.new( "friend", "symmetric", true )
38
+ rete << Wongi::Engine::WME.new( "Alice", "friend", "Bob" )
39
+
40
+ expect(rete.facts.to_a.length).to eq(3)
41
+ expect(rete.facts.select( &:manual? ).length).to eq(2)
42
+ generated = rete.facts.find( &:generated? )
43
+ expect( generated ).to be == Wongi::Engine::WME.new( "Bob", "friend", "Alice" )
44
+ end
45
+
46
+ it 'should generate wmes with an added rule' do
47
+
48
+ rete << Wongi::Engine::WME.new( "friend", "symmetric", true )
49
+ rete << Wongi::Engine::WME.new( "Alice", "friend", "Bob" )
50
+
51
+ expect(rete.facts.to_a.length).to eq(2)
52
+
53
+ rete << rule('symmetric') {
54
+ forall {
55
+ has :P, "symmetric", true
56
+ has :A, :P, :B
57
+ }
58
+ make {
59
+ gen :B, :P, :A
60
+ }
61
+ }
62
+
63
+ expect(rete.facts.to_a.length).to eq(3)
64
+ expect(rete.facts.select( &:manual? ).size).to eq(2)
65
+ end
66
+
67
+ it 'should not get confused by recursive activations' do
68
+
69
+ rete << rule('reflexive') {
70
+ forall {
71
+ has :Predicate, "reflexive", true
72
+ has :X, :Predicate, :Y
73
+ }
74
+ make {
75
+ gen :X, :Predicate, :X
76
+ gen :Y, :Predicate, :Y
77
+ }
78
+ }
79
+
80
+ rete << [:p, "reflexive", true]
81
+ rete << [:x, :p, :y]
82
+
83
+ expect(rete.wmes.to_a.length).to eq(4)
84
+ expect(rete.select(:x, :p, :x).length).to eq(1)
85
+ expect(rete.select(:y, :p, :y).length).to eq(1)
86
+
87
+ end
88
+
89
+ end
90
+
91
+ it 'should check equality' do
92
+
93
+ node = rete << rule('equality') {
94
+ forall {
95
+ fact :A, "same", :B
96
+ same :A, :B
97
+ }
98
+ make {
99
+
100
+ }
101
+ }
102
+
103
+ rete << [ 42, "same", 42 ]
104
+ expect(node.size).to eq(1)
105
+
106
+ end
107
+
108
+ it 'should compare things' do
109
+
110
+ rete << rule('less') {
111
+ forall {
112
+ has :A, :age, :N1
113
+ has :B, :age, :N2
114
+ less :N1, :N2
115
+ }
116
+ make {
117
+ gen :A, :younger, :B
118
+ }
119
+ }
120
+
121
+ rete << rule('less') {
122
+ forall {
123
+ has :A, :age, :N1
124
+ has :B, :age, :N2
125
+ greater :N1, :N2
126
+ }
127
+ make {
128
+ gen :A, :older, :B
129
+ }
130
+ }
131
+
132
+ rete << ["Alice", :age, 42]
133
+ rete << ["Bob", :age, 43]
134
+
135
+ items = rete.select "Alice", :younger, "Bob"
136
+ expect(items.size).to eq(1)
137
+
138
+ items = rete.select "Bob", :older, "Alice"
139
+ expect(items.size).to eq(1)
140
+
141
+ end
142
+
143
+ it 'should use collectors' do
144
+
145
+ rete << rule('collector') {
146
+ forall {
147
+ has :X, :_, 42
148
+ }
149
+ make {
150
+ test_collector :X
151
+ }
152
+ }
153
+
154
+ rete << [ "answer", "is", 42 ]
155
+ rete << [ "question", "is", -1 ]
156
+
157
+ collection = rete.collection(:test_collector)
158
+ expect(collection.size).to eq(1)
159
+ expect(collection.first).to eq("answer")
160
+
161
+ end
162
+
163
+ it "should properly show error messages" do
164
+ rete << rule("Error rule") {
165
+ forall {
166
+ has :_, :_, :TestNumber
167
+ greater :TestNumber, 0
168
+ }
169
+ make {
170
+ error "An error has occurred"
171
+ }
172
+ }
173
+
174
+ rete << [ "A", "B", 1 ]
175
+
176
+ error_messages = rete.errors.map(&:message)
177
+ expect(error_messages).to eq(["An error has occurred"])
178
+ end
179
+
180
+ it 'should use generic collectors' do
181
+
182
+ rete << rule('generic-collector') {
183
+ forall {
184
+ has :X, :_, 42
185
+ }
186
+ make {
187
+ collect :X, :things_that_are_42
188
+ }
189
+ }
190
+
191
+ rete << [ "answer", "is", 42 ]
192
+ rete << [ "question", "is", -1 ]
193
+
194
+ collection = rete.collection(:things_that_are_42)
195
+ expect(collection.size).to eq(1)
196
+ expect(collection.first).to eq("answer")
197
+
198
+ end
199
+
200
+ it 'should accept several rules' do
201
+
202
+ expect {
203
+
204
+ rete << rule('generic-collector') {
205
+ forall {
206
+ has :X, :_, 42
207
+ }
208
+ make {
209
+ collect :X, :things_that_are_42
210
+ }
211
+ }
212
+
213
+ rete << rule('collector') {
214
+ forall {
215
+ has :X, :_, 42
216
+ }
217
+ make {
218
+ test_collector :X
219
+ }
220
+ }
221
+
222
+ }.not_to raise_error
223
+
224
+ end
225
+
226
+ it 'should process negative nodes' do
227
+
228
+ production = (rete << rule('negative') {
229
+ forall {
230
+ neg :_, :_, 42
231
+ }
232
+ })
233
+
234
+ expect(production.size).to eq(1)
235
+
236
+ rete << [ "answer", "is", 42 ]
237
+
238
+ expect(production.size).to eq(0)
239
+
240
+ end
241
+
242
+ context 'queries' do
243
+
244
+ before :each do
245
+ rete << query("test-query") {
246
+ search_on :X
247
+ forall {
248
+ has :X, "is", :Y
249
+ }
250
+ }
251
+ end
252
+
253
+ it 'should run' do
254
+ rete << ["answer", "is", 42]
255
+ rete.execute "test-query", {X: "answer"}
256
+ expect(rete.results["test-query"].size).to eq(1)
257
+ expect(rete.results["test-query"].tokens.first[:Y]).to eq(42)
258
+ end
259
+
260
+ it 'should run several times' do
261
+ rete << ["answer", "is", 42]
262
+ rete << ['question', 'is', '6x9']
263
+ rete.execute "test-query", {X: "answer"}
264
+ rete.execute "test-query", {X: "question"}
265
+ expect(rete.results["test-query"].tokens.to_a.last[:Y]).to eq('6x9')
266
+ expect(rete.results["test-query"].size).to eq(1)
267
+ end
268
+
269
+ end
270
+
271
+ it 'should correctly execute a query several times' do
272
+
273
+ end
274
+
275
+ context 'with timelines' do
276
+
277
+ it 'should not match with no past point' do
278
+
279
+ production = rete.rule {
280
+ forall {
281
+ has 1, 2, 3, time: -1
282
+ }
283
+ }
284
+
285
+ expect(production.size).to eq(0)
286
+
287
+ rete << [1, 2, 3]
288
+
289
+ expect(production.size).to eq(0)
290
+
291
+ end
292
+
293
+ it 'should match a simple past point' do
294
+
295
+ production = rete.rule {
296
+ forall {
297
+ has 1, 2, 3, time: -1
298
+ }
299
+ }
300
+
301
+ rete << [1, 2, 3]
302
+ rete.snapshot!
303
+
304
+ expect(production.size).to eq(1)
305
+
306
+ end
307
+
308
+ context 'using the :asserted clause' do
309
+
310
+ it 'should match asserted items' do
311
+ count = 0
312
+ production = rete.rule do
313
+ forall {
314
+ asserted 1, 2, 3
315
+ }
316
+ make { action { count += 1} }
317
+ end
318
+ expect(production.size).to eq(0)
319
+ rete.snapshot!
320
+ rete << [1, 2, 3]
321
+ expect(production.size).to eq(1)
322
+ #puts count
323
+ end
324
+
325
+ it 'should not match kept items' do
326
+ count = 0
327
+ production = rete.rule do
328
+ forall {
329
+ asserted 1, 2, 3
330
+ }
331
+ make { action { count += 1} }
332
+ end
333
+ rete << [1, 2, 3]
334
+ expect(production.size).to eq(1)
335
+ rete.snapshot!
336
+ expect(production.size).to eq(0)
337
+ #puts count
338
+ end
339
+
340
+ end
341
+
342
+ context 'using the :kept clause' do
343
+
344
+ it 'should match kept items' do
345
+ count = 0
346
+ production = rete.rule do
347
+ forall {
348
+ kept 1, 2, 3
349
+ }
350
+ make { action { count += 1} }
351
+ end
352
+ rete << [1, 2, 3]
353
+ expect(production.size).to eq(0)
354
+ rete.snapshot!
355
+ expect(production.size).to eq(1)
356
+ #puts count
357
+ end
358
+
359
+ it 'should not match asserted wmes' do
360
+ count = 0
361
+ production = rete.rule do
362
+ forall {
363
+ kept 1, 2, 3
364
+ }
365
+ make { action { count += 1} }
366
+ end
367
+ expect(production.size).to eq(0)
368
+ rete.snapshot!
369
+ rete << [1, 2, 3]
370
+ expect(production.size).to eq(0)
371
+ #puts count
372
+ end
373
+
374
+ end
375
+
376
+ end
377
+
378
+ end
@@ -0,0 +1,182 @@
1
+ require 'spec_helper'
2
+
3
+ describe Wongi::Engine::Network do
4
+
5
+ let( :engine ) { Wongi::Engine.create }
6
+ subject { engine }
7
+
8
+ it 'should assert facts' do
9
+ subject << [1,2,3]
10
+ expect( subject.select( :_, 2, :_) ).to have(1).item
11
+ end
12
+
13
+ it 'should retract facts' do
14
+ subject << [1,2,3]
15
+ subject.retract [1,2,3]
16
+ expect( subject.select( :_, 2, :_) ).to be_empty
17
+ end
18
+
19
+ it 'asserted facts end up in productions' do
20
+
21
+ prod = subject << rule { forall { has :X, 2, :Z } }
22
+ subject << [1,2,3]
23
+ expect( prod ).to have(1).tokens
24
+
25
+ end
26
+
27
+ it 'retracted facts are removed from productions' do
28
+
29
+ prod = subject << rule { forall { has :X, 2, :Z } }
30
+ subject << [1,2,3]
31
+ subject.retract [1,2,3]
32
+ expect( prod ).to have(0).tokens
33
+
34
+ end
35
+
36
+ it 'retracted facts should trigger deactivation' do
37
+
38
+ activated_z = nil
39
+ deactivated_z = nil
40
+
41
+ prod = subject << rule {
42
+ forall { has :X, 2, :Z }
43
+ make {
44
+ action activate: ->(token) { activated_z = token[:Z] },
45
+ deactivate: ->(token) { deactivated_z = token[:Z] }
46
+ }
47
+ }
48
+ subject << [1,2,3]
49
+ expect( activated_z ).to be == 3
50
+
51
+ subject.retract [1,2,3]
52
+ expect( deactivated_z ).to be == 3
53
+
54
+ end
55
+
56
+ it 'retracted facts should propagate through join chains' do
57
+
58
+ deactivated = nil
59
+
60
+ prod = engine << rule {
61
+ forall {
62
+ has :X, :is, :Y
63
+ has :Y, :is, :Z
64
+ }
65
+ make {
66
+ action deactivate: ->(token) { deactivated = token }
67
+ }
68
+ }
69
+
70
+ engine << [1, :is, 2]
71
+ engine << [2, :is, 3]
72
+
73
+ expect( prod ).to have(1).tokens
74
+
75
+ engine.retract [1, :is, 2]
76
+ expect( prod ).to have(0).tokens
77
+ expect( deactivated[:X] ).to be == 1
78
+ expect( deactivated[:Y] ).to be == 2
79
+ expect( deactivated[:Z] ).to be == 3
80
+
81
+ end
82
+
83
+ it 'retraction should reactivate neg nodes' do
84
+
85
+ prod = engine << rule { forall { neg 1, 2, 3} }
86
+
87
+ expect( prod ).to have(1).tokens
88
+
89
+ engine << [1, 2 ,3]
90
+ expect( prod ).to have(0).tokens
91
+
92
+ engine.retract [1, 2, 3]
93
+ expect( prod ).to have(1).tokens
94
+
95
+ end
96
+
97
+ describe 'retraction with neg nodes lower in the chain' do
98
+
99
+ def expect_tokens n
100
+ expect( prod ).to have(n).tokens
101
+ end
102
+
103
+ before :each do
104
+
105
+ engine << rule('retract') {
106
+ forall {
107
+ has :x, :u, :Y
108
+ neg :Y, :w, :_
109
+ }
110
+ }
111
+
112
+ end
113
+
114
+ let( :prod ) { engine.productions['retract'] }
115
+
116
+ specify 'case 1' do
117
+
118
+ engine << [:x, :u, :y]
119
+ expect_tokens 1
120
+
121
+ engine << [:y, :w, :z]
122
+ expect_tokens 0
123
+
124
+ engine.retract [:y, :w, :z]
125
+ expect_tokens 1
126
+
127
+ engine.retract [:x, :u, :y]
128
+ expect_tokens 0
129
+
130
+ end
131
+
132
+ specify 'case 2' do
133
+
134
+ engine << [:x, :u, :y]
135
+ expect_tokens 1
136
+
137
+ engine << [:y, :w, :z]
138
+ expect_tokens 0
139
+
140
+ engine.retract [:x, :u, :y]
141
+ expect_tokens 0
142
+
143
+ engine.retract [:y, :w, :z]
144
+ expect_tokens 0
145
+
146
+ end
147
+
148
+ specify 'case 3' do
149
+
150
+ engine << [:y, :w, :z]
151
+ expect_tokens 0
152
+
153
+ engine << [:x, :u, :y]
154
+ expect_tokens 0
155
+
156
+ engine.retract [:x, :u, :y]
157
+ expect_tokens 0
158
+
159
+ engine.retract [:y, :w, :z]
160
+ expect_tokens 0
161
+
162
+ end
163
+
164
+ specify 'case 4' do
165
+
166
+ engine << [:y, :w, :z]
167
+ expect_tokens 0
168
+
169
+ engine << [:x, :u, :y]
170
+ expect_tokens 0
171
+
172
+ engine.retract [:y, :w, :z]
173
+ expect_tokens 1
174
+
175
+ engine.retract [:x, :u, :y]
176
+ expect_tokens 0
177
+
178
+ end
179
+
180
+ end
181
+
182
+ end