wongi-engine 0.0.1

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 (59) hide show
  1. data/.gitignore +17 -0
  2. data/Gemfile +4 -0
  3. data/LICENSE +22 -0
  4. data/README.md +349 -0
  5. data/Rakefile +2 -0
  6. data/examples/ex01.rb +23 -0
  7. data/examples/ex02.rb +36 -0
  8. data/examples/graphviz.rb +15 -0
  9. data/examples/timeline.rb +48 -0
  10. data/lib/wongi-engine.rb +22 -0
  11. data/lib/wongi-engine/alpha_memory.rb +46 -0
  12. data/lib/wongi-engine/beta.rb +10 -0
  13. data/lib/wongi-engine/beta/beta_memory.rb +48 -0
  14. data/lib/wongi-engine/beta/beta_node.rb +164 -0
  15. data/lib/wongi-engine/beta/filter_node.rb +109 -0
  16. data/lib/wongi-engine/beta/join_node.rb +127 -0
  17. data/lib/wongi-engine/beta/ncc_node.rb +46 -0
  18. data/lib/wongi-engine/beta/ncc_partner.rb +43 -0
  19. data/lib/wongi-engine/beta/neg_node.rb +58 -0
  20. data/lib/wongi-engine/beta/optional_node.rb +43 -0
  21. data/lib/wongi-engine/beta/or_node.rb +76 -0
  22. data/lib/wongi-engine/beta/production_node.rb +31 -0
  23. data/lib/wongi-engine/core_ext.rb +57 -0
  24. data/lib/wongi-engine/dsl.rb +112 -0
  25. data/lib/wongi-engine/dsl/action.rb +12 -0
  26. data/lib/wongi-engine/dsl/actions/error_generator.rb +42 -0
  27. data/lib/wongi-engine/dsl/actions/simple_action.rb +23 -0
  28. data/lib/wongi-engine/dsl/actions/simple_collector.rb +51 -0
  29. data/lib/wongi-engine/dsl/actions/statement_generator.rb +52 -0
  30. data/lib/wongi-engine/dsl/actions/trace_action.rb +52 -0
  31. data/lib/wongi-engine/dsl/any_rule.rb +48 -0
  32. data/lib/wongi-engine/dsl/dsl_builder.rb +44 -0
  33. data/lib/wongi-engine/dsl/dsl_extensions.rb +43 -0
  34. data/lib/wongi-engine/dsl/extension_clause.rb +36 -0
  35. data/lib/wongi-engine/dsl/generation_clause.rb +15 -0
  36. data/lib/wongi-engine/dsl/generic_production_rule.rb +78 -0
  37. data/lib/wongi-engine/dsl/ncc_production_rule.rb +21 -0
  38. data/lib/wongi-engine/dsl/production_rule.rb +4 -0
  39. data/lib/wongi-engine/dsl/query.rb +24 -0
  40. data/lib/wongi-engine/graph.rb +71 -0
  41. data/lib/wongi-engine/model_context.rb +13 -0
  42. data/lib/wongi-engine/network.rb +416 -0
  43. data/lib/wongi-engine/network/collectable.rb +42 -0
  44. data/lib/wongi-engine/network/debug.rb +25 -0
  45. data/lib/wongi-engine/ruleset.rb +74 -0
  46. data/lib/wongi-engine/template.rb +111 -0
  47. data/lib/wongi-engine/token.rb +137 -0
  48. data/lib/wongi-engine/version.rb +5 -0
  49. data/lib/wongi-engine/wme.rb +134 -0
  50. data/lib/wongi-engine/wme_match_data.rb +34 -0
  51. data/spec/dataset_spec.rb +26 -0
  52. data/spec/dsl_spec.rb +9 -0
  53. data/spec/high_level_spec.rb +341 -0
  54. data/spec/ruleset_spec.rb +54 -0
  55. data/spec/simple_action_spec.rb +40 -0
  56. data/spec/spec_helper.rb +1 -0
  57. data/spec/wme_spec.rb +83 -0
  58. data/wongi-engine.gemspec +19 -0
  59. metadata +110 -0
@@ -0,0 +1,34 @@
1
+ module Wongi::Engine
2
+
3
+ class WMEMatchData
4
+
5
+ attr_reader :assignments
6
+
7
+ def initialize assignments = { }, match = false
8
+ @assignments = assignments
9
+ @match = match
10
+ end
11
+
12
+ def [] key
13
+ assignments[key]
14
+ end
15
+
16
+ def []= key, value
17
+ assignments[key] = value
18
+ end
19
+
20
+ def match?
21
+ @match
22
+ end
23
+
24
+ def match!
25
+ @match = true
26
+ end
27
+
28
+ def & other
29
+ WMEMatchData.new( assignments.merge( other.assignments ), match? && other.match? )
30
+ end
31
+
32
+ end
33
+
34
+ end
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+
3
+ describe Wongi::Engine::Network do
4
+
5
+ it 'should expose compiled productions' do
6
+
7
+ ds = Wongi::Engine::Network.new
8
+
9
+ ds << rule('test-rule') {
10
+ forall {
11
+ has 1, 2, 3
12
+ }
13
+ }
14
+
15
+ production = ds.productions['test-rule']
16
+ production.should_not be_nil
17
+
18
+ production.tokens.should be_empty
19
+
20
+ ds << [1, 2, 3]
21
+
22
+ production.should have(1).tokens
23
+
24
+ end
25
+
26
+ end
data/spec/dsl_spec.rb ADDED
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'DSL' do
4
+
5
+ it 'should register rulesets globally' do
6
+
7
+ end
8
+
9
+ end
@@ -0,0 +1,341 @@
1
+ require 'spec_helper'
2
+
3
+ dsl {
4
+
5
+ section :make
6
+ clause :test_collector
7
+ action Wongi::Engine::SimpleCollector.collector
8
+
9
+ }
10
+
11
+ describe 'the engine' do
12
+
13
+ before :each do
14
+ @rete = Wongi::Engine::Network.new
15
+ end
16
+
17
+ def rete
18
+ @rete
19
+ end
20
+
21
+ context 'with a simple generative positive rule' do
22
+
23
+ it 'should generate wmes with an existing rule' do
24
+
25
+ rete << rule('symmetric') {
26
+ forall {
27
+ has :P, "symmetric", true
28
+ has :A, :P, :B
29
+ }
30
+ make {
31
+ gen :B, :P, :A
32
+ }
33
+ }
34
+
35
+ rete << Wongi::Engine::WME.new( "friend", "symmetric", true )
36
+ rete << Wongi::Engine::WME.new( "Alice", "friend", "Bob" )
37
+
38
+ rete.should have(3).facts
39
+ rete.facts.select( &:manual? ).should have(2).items
40
+ generated = rete.facts.find( &:generated? )
41
+ generated.should == Wongi::Engine::WME.new( "Bob", "friend", "Alice" )
42
+ end
43
+
44
+ it 'should generate wmes with an added rule' do
45
+
46
+ rete << Wongi::Engine::WME.new( "friend", "symmetric", true )
47
+ rete << Wongi::Engine::WME.new( "Alice", "friend", "Bob" )
48
+
49
+ rete.should have(2).facts
50
+
51
+ rete << rule('symmetric') {
52
+ forall {
53
+ has :P, "symmetric", true
54
+ has :A, :P, :B
55
+ }
56
+ make {
57
+ gen :B, :P, :A
58
+ }
59
+ }
60
+
61
+ rete.should have(3).facts
62
+ rete.facts.select( &:manual? ).should have(2).items
63
+ end
64
+
65
+ end
66
+
67
+ it 'should check equality' do
68
+
69
+ rete << rule('equality') {
70
+ forall {
71
+ fact :A, "same", :B
72
+ same :A, :B
73
+ }
74
+ make {
75
+
76
+ }
77
+ }
78
+
79
+ rete << [ 42, "same", 42 ]
80
+
81
+ end
82
+
83
+ it 'should use collectors' do
84
+
85
+ rete << rule('collector') {
86
+ forall {
87
+ has :X, :_, 42
88
+ }
89
+ make {
90
+ test_collector :X
91
+ }
92
+ }
93
+
94
+ rete << [ "answer", "is", 42 ]
95
+ rete << [ "question", "is", -1 ]
96
+
97
+ collection = rete.collection(:test_collector)
98
+ collection.should have(1).item
99
+ collection.first.should == "answer"
100
+
101
+ end
102
+
103
+ it 'should use generic collectors' do
104
+
105
+ rete << rule('generic-collector') {
106
+ forall {
107
+ has :X, :_, 42
108
+ }
109
+ make {
110
+ collect :X, :things_that_are_42
111
+ }
112
+ }
113
+
114
+ rete << [ "answer", "is", 42 ]
115
+ rete << [ "question", "is", -1 ]
116
+
117
+ collection = rete.collection(:things_that_are_42)
118
+ collection.should have(1).item
119
+ collection.first.should == "answer"
120
+
121
+ end
122
+
123
+ it 'should accept several rules' do
124
+
125
+ lambda do
126
+
127
+ rete << rule('generic-collector') {
128
+ forall {
129
+ has :X, :_, 42
130
+ }
131
+ make {
132
+ collect :X, :things_that_are_42
133
+ }
134
+ }
135
+
136
+ rete << rule('collector') {
137
+ forall {
138
+ has :X, :_, 42
139
+ }
140
+ make {
141
+ test_collector :X
142
+ }
143
+ }
144
+
145
+ end.should_not raise_error
146
+
147
+ end
148
+
149
+ it 'should process negative nodes' do
150
+
151
+ production = (rete << rule('negative') {
152
+ forall {
153
+ neg :_, :_, 42
154
+ }
155
+ })
156
+
157
+ production.should have(1).tokens
158
+
159
+ rete << [ "answer", "is", 42 ]
160
+
161
+ production.should have(0).tokens
162
+
163
+ end
164
+
165
+ it 'should support prepared queries' do
166
+
167
+ rete << query("test-query") {
168
+ search_on :X
169
+ forall {
170
+ has :X, "is", :Y
171
+ }
172
+ }
173
+
174
+ rete << ["answer", "is", 42]
175
+
176
+ rete.execute "test-query", {X: "answer"}
177
+ rete.results["test-query"].should have(1).tokens
178
+ rete.results["test-query"].tokens.first[:Y].should == 42
179
+
180
+ end
181
+
182
+ it 'should support negative subnets' do
183
+
184
+ production = (rete << rule('ncc') {
185
+ forall {
186
+ has "base", "is", :Base
187
+ none {
188
+ has :Base, 2, :X
189
+ has :X, 4, 5
190
+ }
191
+ }
192
+ })
193
+
194
+ rete << ["base", "is", 1]
195
+
196
+ production.should have(1).tokens
197
+
198
+ rete << [1, 2, 3]
199
+
200
+ production.should have(1).tokens
201
+
202
+ rete << [3, 4, 5]
203
+
204
+ production.should have(0).tokens
205
+
206
+ rete << ["base", "is", 2]
207
+
208
+ production.should have(1).tokens
209
+
210
+ end
211
+
212
+ it 'should support optional matches' do
213
+
214
+ production = (rete << rule('optional') {
215
+ forall {
216
+ has "answer", "is", :Answer
217
+ maybe :Answer, "is", :Kind
218
+ }
219
+ })
220
+
221
+ rete << ["answer", "is", 42]
222
+ rete << ["answer", "is", 43]
223
+ rete << [42, "is", "canonical"]
224
+
225
+ production.should have(2).tokens
226
+
227
+ canon = production.tokens.select { |token| not token[:Kind].nil? }
228
+ canon.should have(1).items
229
+ canon.first[:Answer].should == 42
230
+ canon.first[:Kind].should == "canonical"
231
+
232
+ non_canon = production.tokens.select { |token| token[:Kind].nil? }
233
+ non_canon.should have(1).items
234
+ non_canon.first[:Answer].should == 43
235
+
236
+ end
237
+
238
+ context 'with timelines' do
239
+
240
+ it 'should not match with no past point' do
241
+
242
+ production = rete.rule {
243
+ forall {
244
+ has 1, 2, 3, -1
245
+ }
246
+ }
247
+
248
+ production.should have(0).tokens
249
+
250
+ rete << [1, 2, 3]
251
+
252
+ production.should have(0).tokens
253
+
254
+ end
255
+
256
+ it 'should match a simple past point' do
257
+
258
+ production = rete.rule {
259
+ forall {
260
+ has 1, 2, 3, -1
261
+ }
262
+ }
263
+
264
+ rete << [1, 2, 3]
265
+ rete.snapshot!
266
+
267
+ production.should have(1).tokens
268
+
269
+ end
270
+
271
+ context 'using the :asserted clause' do
272
+
273
+ it 'should match asserted items' do
274
+ count = 0
275
+ production = rete.rule do
276
+ forall {
277
+ asserted 1, 2, 3
278
+ }
279
+ make { action { count += 1} }
280
+ end
281
+ production.should have(0).tokens
282
+ rete.snapshot!
283
+ rete << [1, 2, 3]
284
+ production.should have(1).tokens
285
+ puts count
286
+ end
287
+
288
+ it 'should not match kept items' do
289
+ count = 0
290
+ production = rete.rule do
291
+ forall {
292
+ asserted 1, 2, 3
293
+ }
294
+ make { action { count += 1} }
295
+ end
296
+ rete << [1, 2, 3]
297
+ production.should have(1).tokens
298
+ rete.snapshot!
299
+ production.should have(0).tokens
300
+ puts count
301
+ end
302
+
303
+ end
304
+
305
+ context 'using the :kept clause' do
306
+
307
+ it 'should match kept items' do
308
+ count = 0
309
+ production = rete.rule do
310
+ forall {
311
+ kept 1, 2, 3
312
+ }
313
+ make { action { count += 1} }
314
+ end
315
+ rete << [1, 2, 3]
316
+ production.should have(0).tokens
317
+ rete.snapshot!
318
+ production.should have(1).tokens
319
+ puts count
320
+ end
321
+
322
+ it 'should not match asserted wmes' do
323
+ count = 0
324
+ production = rete.rule do
325
+ forall {
326
+ kept 1, 2, 3
327
+ }
328
+ make { action { count += 1} }
329
+ end
330
+ production.should have(0).tokens
331
+ rete.snapshot!
332
+ rete << [1, 2, 3]
333
+ production.should have(0).tokens
334
+ puts count
335
+ end
336
+
337
+ end
338
+
339
+ end
340
+
341
+ end
@@ -0,0 +1,54 @@
1
+ require 'spec_helper'
2
+
3
+ describe Wongi::Engine::Ruleset do
4
+
5
+ before :each do
6
+ Wongi::Engine::Ruleset.reset
7
+ end
8
+
9
+ context 'initially' do
10
+
11
+ it 'should have no rules' do
12
+ Wongi::Engine::Ruleset.rulesets.should be_empty
13
+ end
14
+
15
+ end
16
+
17
+ context 'when creating' do
18
+
19
+ it 'should not register itself when not given a name' do
20
+ ruleset = Wongi::Engine::Ruleset.new
21
+ ruleset.name.should be_nil
22
+ Wongi::Engine::Ruleset.rulesets.should be_empty
23
+ end
24
+
25
+ it 'should have a name' do
26
+ ruleset = Wongi::Engine::Ruleset.new 'testing-ruleset'
27
+ ruleset.name.should == 'testing-ruleset'
28
+ end
29
+
30
+ it 'should register itself when given a name' do
31
+ ruleset = Wongi::Engine::Ruleset.new 'testing-ruleset'
32
+ Wongi::Engine::Ruleset.rulesets.should_not be_empty
33
+ Wongi::Engine::Ruleset[ruleset.name].should == ruleset
34
+ end
35
+
36
+ end
37
+
38
+ it 'should be able to clear registered rulesets' do
39
+ ruleset = Wongi::Engine::Ruleset.new 'testing-ruleset'
40
+ Wongi::Engine::Ruleset.reset
41
+ Wongi::Engine::Ruleset.rulesets.should be_empty
42
+ end
43
+
44
+ it 'should install creating rules into a rete' do
45
+ rete = mock 'rete'
46
+
47
+ ruleset = Wongi::Engine::Ruleset.new
48
+ rule = ruleset.rule( 'test-rule' ) { }
49
+
50
+ rete.should_receive(:<<).with(rule).once
51
+ ruleset.install rete
52
+ end
53
+
54
+ end