wongi-engine 0.3.8 → 0.4.0.pre.alpha1

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 (54) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +2 -2
  3. data/.gitignore +2 -0
  4. data/README.md +13 -13
  5. data/lib/wongi-engine/alpha_index.rb +58 -0
  6. data/lib/wongi-engine/alpha_memory.rb +2 -24
  7. data/lib/wongi-engine/beta/aggregate_node.rb +16 -15
  8. data/lib/wongi-engine/beta/assignment_node.rb +7 -2
  9. data/lib/wongi-engine/beta/beta_node.rb +27 -23
  10. data/lib/wongi-engine/beta/filter_node.rb +8 -13
  11. data/lib/wongi-engine/beta/join_node.rb +15 -28
  12. data/lib/wongi-engine/beta/ncc_node.rb +14 -26
  13. data/lib/wongi-engine/beta/ncc_partner.rb +18 -18
  14. data/lib/wongi-engine/beta/neg_node.rb +23 -55
  15. data/lib/wongi-engine/beta/optional_node.rb +24 -48
  16. data/lib/wongi-engine/beta/or_node.rb +24 -1
  17. data/lib/wongi-engine/beta/production_node.rb +9 -3
  18. data/lib/wongi-engine/beta/root_node.rb +47 -0
  19. data/lib/wongi-engine/beta.rb +1 -1
  20. data/lib/wongi-engine/compiler.rb +6 -34
  21. data/lib/wongi-engine/dsl/action/{base.rb → base_action.rb} +5 -1
  22. data/lib/wongi-engine/dsl/action/error_generator.rb +1 -1
  23. data/lib/wongi-engine/dsl/action/simple_action.rb +1 -1
  24. data/lib/wongi-engine/dsl/action/simple_collector.rb +1 -1
  25. data/lib/wongi-engine/dsl/action/statement_generator.rb +21 -22
  26. data/lib/wongi-engine/dsl/action/trace_action.rb +1 -1
  27. data/lib/wongi-engine/dsl/clause/fact.rb +2 -6
  28. data/lib/wongi-engine/dsl.rb +1 -25
  29. data/lib/wongi-engine/graph.rb +1 -1
  30. data/lib/wongi-engine/network/debug.rb +2 -10
  31. data/lib/wongi-engine/network.rb +44 -105
  32. data/lib/wongi-engine/overlay.rb +589 -0
  33. data/lib/wongi-engine/template.rb +22 -2
  34. data/lib/wongi-engine/token.rb +10 -26
  35. data/lib/wongi-engine/token_assignment.rb +15 -0
  36. data/lib/wongi-engine/version.rb +1 -1
  37. data/lib/wongi-engine/wme.rb +10 -39
  38. data/lib/wongi-engine.rb +3 -1
  39. data/spec/alpha_index_spec.rb +78 -0
  40. data/spec/bug_specs/issue_4_spec.rb +11 -11
  41. data/spec/high_level_spec.rb +8 -101
  42. data/spec/network_spec.rb +8 -6
  43. data/spec/overlay_spec.rb +177 -1
  44. data/spec/rule_specs/any_rule_spec.rb +39 -0
  45. data/spec/rule_specs/assign_spec.rb +1 -1
  46. data/spec/rule_specs/maybe_rule_spec.rb +58 -1
  47. data/spec/rule_specs/ncc_spec.rb +78 -19
  48. data/spec/rule_specs/negative_rule_spec.rb +27 -13
  49. data/spec/spec_helper.rb +4 -0
  50. data/spec/wme_spec.rb +0 -32
  51. metadata +11 -9
  52. data/lib/wongi-engine/beta/beta_memory.rb +0 -60
  53. data/lib/wongi-engine/data_overlay.rb +0 -141
  54. data/spec/rule_specs/or_rule_spec.rb +0 -40
@@ -77,6 +77,25 @@ describe "MAYBE rule" do
77
77
  }
78
78
  end
79
79
 
80
+ it 'should work with with overlays' do
81
+ prod = engine << maybe_rule
82
+
83
+ engine << [1, 2, 3]
84
+
85
+ engine << [3, 4, 5]
86
+ expect(prod.size).to eq(1)
87
+ expect(prod.tokens.first[:Y]).to be == 5
88
+
89
+ engine.with_overlay do |overlay|
90
+ overlay.retract [3, 4, 5]
91
+ expect(prod.size).to eq(1)
92
+ expect(prod.tokens.first[:Y]).to be_nil
93
+ end
94
+
95
+ expect(prod.size).to eq(1)
96
+ expect(prod.tokens.first[:Y]).to be == 5
97
+ end
98
+
80
99
  it 'should handle retracted parent tokens' do
81
100
  prod = engine << maybe_rule
82
101
 
@@ -85,6 +104,44 @@ describe "MAYBE rule" do
85
104
  engine.retract [1, 2, 3]
86
105
 
87
106
  expect(prod).to have(0).tokens
88
- expect(engine.find(3, 4, 5).opt_join_results).to be_empty
107
+ expect(engine.base_overlay.opt_join_results_for(wme: engine.find(3, 4, 5))).to be_empty
108
+ end
109
+
110
+ context 'should handle retracted parent tokens with overlays' do
111
+ specify 'variation 1' do
112
+ prod = engine << maybe_rule
113
+
114
+ engine << [1, 2, 3]
115
+ engine << [3, 4, 5]
116
+
117
+ engine.with_overlay do |overlay|
118
+ engine.retract [1, 2, 3]
119
+
120
+ expect(prod).to have(0).tokens
121
+ expect(overlay.opt_join_results_for(wme: engine.find(3, 4, 5))).to be_empty
122
+ end
123
+
124
+ expect(prod).to have(1).tokens
125
+ expect(engine.base_overlay.opt_join_results_for(wme: engine.find(3, 4, 5))).not_to be_empty
126
+ end
127
+
128
+ specify 'variation 2' do
129
+ prod = engine << maybe_rule
130
+
131
+ engine << [1, 2, 3]
132
+ engine << [3, 4, 5]
133
+
134
+ engine.with_overlay do |overlay|
135
+ engine.retract [1, 2, 3]
136
+ engine << [3, 4, 5]
137
+ engine.retract [1, 2, 3]
138
+
139
+ expect(prod).to have(0).tokens
140
+ expect(overlay.opt_join_results_for(wme: engine.find(3, 4, 5))).to be_empty
141
+ end
142
+
143
+ expect(prod).to have(1).tokens
144
+ expect(engine.base_overlay.opt_join_results_for(wme: engine.find(3, 4, 5))).not_to be_empty
145
+ end
89
146
  end
90
147
  end
@@ -87,7 +87,6 @@ describe Wongi::Engine::NccNode do
87
87
  has :light_kitchen, :value, :on
88
88
  }
89
89
  make {
90
- # trace values: true, generation: true
91
90
  gen rule.name, :light_bathroom, :on
92
91
  gen rule.name, :want_action_for, :light_bathroom
93
92
  }
@@ -106,7 +105,6 @@ describe Wongi::Engine::NccNode do
106
105
  }
107
106
  }
108
107
  make {
109
- # trace values: true, generation: true
110
108
  gen :Actor, :value, :Value
111
109
  gen :Actor, :last_user, :Requestor
112
110
  }
@@ -119,22 +117,22 @@ describe Wongi::Engine::NccNode do
119
117
 
120
118
  engine << %i[user want_action_for light_bathroom]
121
119
  engine << %i[user light_bathroom off]
122
- expect(engine.select(:light_bathroom, :value, :_)).to be == [Wongi::Engine::WME.new(:light_bathroom, :value, :off)]
123
- expect(engine.select(:light_bathroom, :last_user, :_)).to be == [Wongi::Engine::WME.new(:light_bathroom, :last_user, :user)]
120
+ expect(engine.select(:light_bathroom, :value, :_).to_a).to be == [Wongi::Engine::WME.new(:light_bathroom, :value, :off)]
121
+ expect(engine.select(:light_bathroom, :last_user, :_).to_a).to be == [Wongi::Engine::WME.new(:light_bathroom, :last_user, :user)]
124
122
 
125
123
  engine << %i[light_kitchen value on]
126
- expect(engine.select(:light_bathroom, :value, :_)).to be == [Wongi::Engine::WME.new(:light_bathroom, :value, :on)]
127
- expect(engine.select(:light_bathroom, :last_user, :_)).to be == [Wongi::Engine::WME.new(:light_bathroom, :last_user, :rule1)]
124
+ expect(engine.select(:light_bathroom, :value, :_).to_a).to be == [Wongi::Engine::WME.new(:light_bathroom, :value, :on)]
125
+ expect(engine.select(:light_bathroom, :last_user, :_).to_a).to be == [Wongi::Engine::WME.new(:light_bathroom, :last_user, :rule1)]
128
126
 
129
127
  engine << %i[poweruser want_action_for light_bathroom]
130
128
  engine << %i[poweruser light_bathroom super_on]
131
- expect(engine.select(:light_bathroom, :value, :_)).to be == [Wongi::Engine::WME.new(:light_bathroom, :value, :super_on)]
132
- expect(engine.select(:light_bathroom, :last_user, :_)).to be == [Wongi::Engine::WME.new(:light_bathroom, :last_user, :poweruser)]
129
+ expect(engine.select(:light_bathroom, :value, :_).to_a).to be == [Wongi::Engine::WME.new(:light_bathroom, :value, :super_on)]
130
+ expect(engine.select(:light_bathroom, :last_user, :_).to_a).to be == [Wongi::Engine::WME.new(:light_bathroom, :last_user, :poweruser)]
133
131
 
134
132
  engine << %i[god want_action_for light_bathroom]
135
133
  engine << %i[god light_bathroom let_there_be_light]
136
- expect(engine.select(:light_bathroom, :value, :_)).to be == [Wongi::Engine::WME.new(:light_bathroom, :value, :let_there_be_light)]
137
- expect(engine.select(:light_bathroom, :last_user, :_)).to be == [Wongi::Engine::WME.new(:light_bathroom, :last_user, :god)]
134
+ expect(engine.select(:light_bathroom, :value, :_).to_a).to be == [Wongi::Engine::WME.new(:light_bathroom, :value, :let_there_be_light)]
135
+ expect(engine.select(:light_bathroom, :last_user, :_).to_a).to be == [Wongi::Engine::WME.new(:light_bathroom, :last_user, :god)]
138
136
  end
139
137
 
140
138
  it 'should clean up correctly with a different activation order' do
@@ -168,29 +166,29 @@ describe Wongi::Engine::NccNode do
168
166
  }
169
167
  end
170
168
 
171
- engine << [:user, :priority, 2]
172
169
  engine << [:rule1, :priority, 1]
170
+ engine << [:user, :priority, 2]
173
171
  engine << [:poweruser, :priority, 3]
174
172
  engine << [:god, :priority, 4]
175
173
 
176
174
  engine << %i[user want_action_for light_bathroom]
177
175
  engine << %i[user light_bathroom off]
178
- expect(engine.select(:light_bathroom, :value, :_)).to be == [Wongi::Engine::WME.new(:light_bathroom, :value, :off)]
179
- expect(engine.select(:light_bathroom, :last_user, :_)).to be == [Wongi::Engine::WME.new(:light_bathroom, :last_user, :user)]
176
+ expect(engine.select(:light_bathroom, :value, :_).to_a).to be == [Wongi::Engine::WME.new(:light_bathroom, :value, :off)]
177
+ expect(engine.select(:light_bathroom, :last_user, :_).to_a).to be == [Wongi::Engine::WME.new(:light_bathroom, :last_user, :user)]
180
178
 
181
179
  engine << %i[light_kitchen value on]
182
- expect(engine.select(:light_bathroom, :value, :_)).to be == [Wongi::Engine::WME.new(:light_bathroom, :value, :off)]
183
- expect(engine.select(:light_bathroom, :last_user, :_)).to be == [Wongi::Engine::WME.new(:light_bathroom, :last_user, :user)]
180
+ expect(engine.select(:light_bathroom, :value, :_).to_a).to be == [Wongi::Engine::WME.new(:light_bathroom, :value, :off)]
181
+ expect(engine.select(:light_bathroom, :last_user, :_).to_a).to be == [Wongi::Engine::WME.new(:light_bathroom, :last_user, :user)]
184
182
 
185
183
  engine << %i[poweruser want_action_for light_bathroom]
186
184
  engine << %i[poweruser light_bathroom super_on]
187
- expect(engine.select(:light_bathroom, :value, :_)).to be == [Wongi::Engine::WME.new(:light_bathroom, :value, :super_on)]
188
- expect(engine.select(:light_bathroom, :last_user, :_)).to be == [Wongi::Engine::WME.new(:light_bathroom, :last_user, :poweruser)]
185
+ expect(engine.select(:light_bathroom, :value, :_).to_a).to be == [Wongi::Engine::WME.new(:light_bathroom, :value, :super_on)]
186
+ expect(engine.select(:light_bathroom, :last_user, :_).to_a).to be == [Wongi::Engine::WME.new(:light_bathroom, :last_user, :poweruser)]
189
187
 
190
188
  engine << %i[god want_action_for light_bathroom]
191
189
  engine << %i[god light_bathroom let_there_be_light]
192
- expect(engine.select(:light_bathroom, :value, :_)).to be == [Wongi::Engine::WME.new(:light_bathroom, :value, :let_there_be_light)]
193
- expect(engine.select(:light_bathroom, :last_user, :_)).to be == [Wongi::Engine::WME.new(:light_bathroom, :last_user, :god)]
190
+ expect(engine.select(:light_bathroom, :value, :_).to_a).to be == [Wongi::Engine::WME.new(:light_bathroom, :value, :let_there_be_light)]
191
+ expect(engine.select(:light_bathroom, :last_user, :_).to_a).to be == [Wongi::Engine::WME.new(:light_bathroom, :last_user, :god)]
194
192
  end
195
193
 
196
194
  it 'should ncc-deactivate without destroying tokens' do
@@ -219,6 +217,7 @@ describe Wongi::Engine::NccNode do
219
217
 
220
218
  %w[math science english bio].each { |req| engine << [req, :is_a, :requirement] }
221
219
  %w[CourseA CourseB CourseC].each { |course| engine << [course, :is_a, :course] }
220
+
222
221
  engine << ["StudentA", :is_a, :student]
223
222
 
224
223
  engine << ["CourseA", "math", 50]
@@ -254,4 +253,64 @@ describe Wongi::Engine::NccNode do
254
253
 
255
254
  expect(prod).to have(1).tokens
256
255
  end
256
+
257
+ context "with overlays" do
258
+ context 'should pass with a mismatching subchain' do
259
+ specify "variation 1" do
260
+ engine << ncc_rule
261
+ production = engine.productions['ncc']
262
+
263
+ engine.with_overlay do |overlay|
264
+ overlay << ["base", "is", 1]
265
+ expect(production).to have(1).token
266
+
267
+ overlay << [1, 2, 3]
268
+ expect(production).to have(1).token
269
+
270
+ overlay << [3, 4, 5]
271
+ expect(production).to have(0).token
272
+ end
273
+
274
+ expect(production).to have(0).tokens
275
+ end
276
+
277
+ specify "variation 2" do
278
+ engine << ncc_rule
279
+ overlay = engine.base_overlay
280
+ production = engine.productions['ncc']
281
+
282
+ overlay << ["base", "is", 1]
283
+ expect(production).to have(1).token
284
+
285
+ engine.with_overlay do |overlay|
286
+ overlay << [1, 2, 3]
287
+ expect(production).to have(1).token
288
+
289
+ overlay << [3, 4, 5]
290
+ expect(production).to have(0).token
291
+ end
292
+
293
+ expect(production).to have(1).tokens
294
+ end
295
+
296
+ specify "variation 3" do
297
+ engine << ncc_rule
298
+ overlay = engine.base_overlay
299
+ production = engine.productions['ncc']
300
+
301
+ overlay << ["base", "is", 1]
302
+ expect(production).to have(1).token
303
+
304
+ overlay << [1, 2, 3]
305
+ expect(production).to have(1).token
306
+
307
+ engine.with_overlay do |overlay|
308
+ overlay << [3, 4, 5]
309
+ expect(production).to have(0).token
310
+ end
311
+
312
+ expect(production).to have(1).tokens
313
+ end
314
+ end
315
+ end
257
316
  end
@@ -4,6 +4,22 @@ describe "negative rule" do
4
4
  include Wongi::Engine::DSL
5
5
  let(:engine) { Wongi::Engine.create }
6
6
 
7
+ it "works" do
8
+ prod = engine << rule {
9
+ forall {
10
+ neg :x, :y, :z
11
+ }
12
+ }
13
+
14
+ expect(prod).to have(1).tokens
15
+
16
+ engine << [:x, :y, 42]
17
+ expect(prod).to have(1).tokens
18
+
19
+ engine << %i[x y z]
20
+ expect(prod).to have(0).tokens
21
+ end
22
+
7
23
  it "should not introduce variables" do
8
24
  proc = lambda {
9
25
  engine << rule('one-option') {
@@ -52,20 +68,18 @@ describe "negative rule" do
52
68
  expect(prod).to have(1).tokens
53
69
  end
54
70
 
55
- # it "should not create infinite feedback loops by default" do
56
-
57
- # engine << rule('feedback') {
58
- # forall {
59
- # neg :a, :b, :_
60
- # }
61
- # make {
62
- # gen :a, :b, :c
63
- # }
64
- # }
65
-
66
- # engine.should have(1).facts
71
+ it "should not create infinite feedback loops by default" do
72
+ engine << rule('feedback') {
73
+ forall {
74
+ neg :a, :b, :_
75
+ }
76
+ make {
77
+ gen :a, :b, :c
78
+ }
79
+ }
67
80
 
68
- # end
81
+ engine.should have(1).facts
82
+ end
69
83
 
70
84
  it "should create infinite feedback loops with unsafe option" do
71
85
  counter = 0
data/spec/spec_helper.rb CHANGED
@@ -1,3 +1,7 @@
1
1
  require 'pry'
2
2
  require 'wongi-engine'
3
3
  require 'rspec/collection_matchers'
4
+
5
+ def print_dot_graph(engine, io: $stderr)
6
+ Wongi::Engine::Graph.new(engine).dot(io)
7
+ end
data/spec/wme_spec.rb CHANGED
@@ -19,40 +19,8 @@ describe Wongi::Engine::WME do
19
19
  expect(subject.predicate).to be == "b"
20
20
  expect(subject.object).to be == "c"
21
21
  end
22
-
23
- # it 'should use the rete to import members' do
24
-
25
- # rete = capitalizing_rete
26
-
27
- # wme = Wongi::Engine::WME.new "a", "b", "c", rete
28
-
29
- # expect( wme.subject ).to be == "A"
30
- # expect( wme.predicate ).to be == "B"
31
- # expect( wme.object ).to be == "C"
32
-
33
- # end
34
-
35
- it {
36
- should be_manual
37
- }
38
-
39
- it {
40
- should_not be_generated
41
- }
42
22
  end
43
23
 
44
- # it 'should be able to import into rete' do
45
-
46
- # rete = capitalizing_rete
47
-
48
- # imported = subject.import_into rete
49
-
50
- # expect( imported.subject ).to be == "A"
51
- # expect( imported.predicate ).to be == "B"
52
- # expect( imported.object ).to be == "C"
53
-
54
- # end
55
-
56
24
  it 'should compare instances' do
57
25
  wme1 = Wongi::Engine::WME.new "a", "b", "c"
58
26
  wme2 = Wongi::Engine::WME.new "a", "b", "c"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wongi-engine
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.8
4
+ version: 0.4.0.pre.alpha1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Valeri Sokolov
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-10-06 00:00:00.000000000 Z
11
+ date: 2022-10-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pry
@@ -91,11 +91,11 @@ files:
91
91
  - examples/rdf.rb
92
92
  - examples/timeline.rb
93
93
  - lib/wongi-engine.rb
94
+ - lib/wongi-engine/alpha_index.rb
94
95
  - lib/wongi-engine/alpha_memory.rb
95
96
  - lib/wongi-engine/beta.rb
96
97
  - lib/wongi-engine/beta/aggregate_node.rb
97
98
  - lib/wongi-engine/beta/assignment_node.rb
98
- - lib/wongi-engine/beta/beta_memory.rb
99
99
  - lib/wongi-engine/beta/beta_node.rb
100
100
  - lib/wongi-engine/beta/filter_node.rb
101
101
  - lib/wongi-engine/beta/join_node.rb
@@ -105,12 +105,12 @@ files:
105
105
  - lib/wongi-engine/beta/optional_node.rb
106
106
  - lib/wongi-engine/beta/or_node.rb
107
107
  - lib/wongi-engine/beta/production_node.rb
108
+ - lib/wongi-engine/beta/root_node.rb
108
109
  - lib/wongi-engine/compiler.rb
109
110
  - lib/wongi-engine/core_ext.rb
110
- - lib/wongi-engine/data_overlay.rb
111
111
  - lib/wongi-engine/dsl.rb
112
112
  - lib/wongi-engine/dsl/action/assign_action.rb
113
- - lib/wongi-engine/dsl/action/base.rb
113
+ - lib/wongi-engine/dsl/action/base_action.rb
114
114
  - lib/wongi-engine/dsl/action/error_generator.rb
115
115
  - lib/wongi-engine/dsl/action/simple_action.rb
116
116
  - lib/wongi-engine/dsl/action/simple_collector.rb
@@ -143,13 +143,16 @@ files:
143
143
  - lib/wongi-engine/network.rb
144
144
  - lib/wongi-engine/network/collectable.rb
145
145
  - lib/wongi-engine/network/debug.rb
146
+ - lib/wongi-engine/overlay.rb
146
147
  - lib/wongi-engine/ruleset.rb
147
148
  - lib/wongi-engine/template.rb
148
149
  - lib/wongi-engine/token.rb
150
+ - lib/wongi-engine/token_assignment.rb
149
151
  - lib/wongi-engine/version.rb
150
152
  - lib/wongi-engine/wme.rb
151
153
  - lib/wongi-engine/wme_match_data.rb
152
154
  - spec/action_class_spec.rb
155
+ - spec/alpha_index_spec.rb
153
156
  - spec/beta_node_spec.rb
154
157
  - spec/bug_specs/issue_4_spec.rb
155
158
  - spec/dataset_spec.rb
@@ -168,7 +171,6 @@ files:
168
171
  - spec/rule_specs/maybe_rule_spec.rb
169
172
  - spec/rule_specs/ncc_spec.rb
170
173
  - spec/rule_specs/negative_rule_spec.rb
171
- - spec/rule_specs/or_rule_spec.rb
172
174
  - spec/ruleset_spec.rb
173
175
  - spec/simple_action_spec.rb
174
176
  - spec/spec_helper.rb
@@ -190,11 +192,11 @@ required_ruby_version: !ruby/object:Gem::Requirement
190
192
  version: '0'
191
193
  required_rubygems_version: !ruby/object:Gem::Requirement
192
194
  requirements:
193
- - - ">="
195
+ - - ">"
194
196
  - !ruby/object:Gem::Version
195
- version: '0'
197
+ version: 1.3.1
196
198
  requirements: []
197
- rubygems_version: 3.3.7
199
+ rubygems_version: 3.1.6
198
200
  signing_key:
199
201
  specification_version: 4
200
202
  summary: A forward-chaining rule engine in pure Ruby.
@@ -1,60 +0,0 @@
1
- module Wongi::Engine
2
- class BetaMemory < BetaNode
3
- include TokenContainer
4
-
5
- def seed(assignments = {})
6
- @seed = assignments
7
- t = Token.new(self, nil, nil, assignments)
8
- rete.default_overlay.add_token(t, self)
9
- end
10
-
11
- def subst(valuations)
12
- beta_deactivate(tokens.first)
13
- token = Token.new(self, nil, nil, @seed)
14
- valuations.each { |variable, value| token.subst variable, value }
15
- beta_activate(token)
16
- end
17
-
18
- def beta_activate(token)
19
- existing = tokens.find { |et| et.duplicate? token }
20
- return if existing # TODO: really?
21
-
22
- token.overlay.add_token(token, self)
23
- children.each do |child|
24
- child.beta_activate token
25
- end
26
- token
27
- end
28
-
29
- def beta_deactivate(token)
30
- return nil unless tokens.find token
31
-
32
- token.overlay.remove_token(token, self)
33
- token.deleted!
34
- if token.parent
35
- token.parent.children.delete token # should this go into Token#destroy?
36
- end
37
- children.each do |child|
38
- child.beta_deactivate token
39
- end
40
- token
41
- end
42
-
43
- def refresh_child(child)
44
- tokens.each do |token|
45
- child.beta_activate token
46
- end
47
- end
48
- end
49
-
50
- # SingletonBetaMemory is a memory node that only holds one token at a time.
51
- # It should only be used after aggregate nodes.
52
- class SingletonBetaMemory < BetaMemory
53
- def beta_activate(token)
54
- if (t = tokens.first)
55
- beta_deactivate(t)
56
- end
57
- super
58
- end
59
- end
60
- end
@@ -1,141 +0,0 @@
1
- module Wongi::Engine
2
- class DataOverlay
3
- attr_reader :rete, :parent
4
-
5
- def initialize(rete, parent = nil)
6
- @rete = rete
7
- @parent = parent
8
- @raw_wmes = Hash.new { |h, k| h[k] = [] }
9
- @raw_tokens = Hash.new { |h, k| h[k] = [] }
10
- rete.add_overlay(self)
11
- end
12
-
13
- def new_child
14
- DataOverlay.new(rete, self)
15
- end
16
-
17
- def with_child
18
- return unless block_given?
19
-
20
- new_child.tap do |overlay|
21
- yield overlay
22
- ensure
23
- overlay.dispose
24
- end
25
- end
26
-
27
- def ancestor?(other)
28
- return false if parent.nil?
29
- return true if parent == other
30
-
31
- parent.ancestor?(other)
32
- end
33
-
34
- def dispose
35
- return if self == rete.default_overlay
36
-
37
- rete.remove_overlay(self)
38
- @raw_tokens.each_value do |tokens|
39
- tokens.each(&:dispose!)
40
- end
41
- @raw_tokens.clear
42
- end
43
-
44
- def <<(thing)
45
- case thing
46
- when Array
47
- assert(WME.new(*thing).tap { |wme| wme.overlay = self })
48
- when WME
49
- assert(thing)
50
- else
51
- raise Error, "overlays can only accept data"
52
- end
53
- end
54
-
55
- def assert(wme)
56
- @next_cascade ||= []
57
- @next_cascade << [:assert, wme]
58
- return if @current_cascade
59
-
60
- @current_cascade = @next_cascade
61
- @next_cascade = nil
62
- process_cascade
63
- end
64
-
65
- def retract(wme, options = {})
66
- wme = WME.new(*wme) if wme.is_a? Array
67
- @next_cascade ||= []
68
- @next_cascade << [:retract, wme, options]
69
- return if @current_cascade
70
-
71
- @current_cascade = @next_cascade
72
- @next_cascade = nil
73
- process_cascade
74
- end
75
-
76
- def process_cascade
77
- while @current_cascade
78
- @current_cascade.each do |(operation, wme, options)|
79
- case operation
80
- when :assert
81
- wme.overlay = self
82
- rete.real_assert(wme)
83
- when :retract
84
- rete.real_retract(wme, options)
85
- wme.overlay = nil
86
- end
87
- end
88
- @current_cascade = @next_cascade
89
- @next_cascade = nil
90
- end
91
- end
92
-
93
- def highest(other)
94
- return self if self == other
95
- return self if other.nil?
96
- return self if ancestor?(other)
97
- return other if other.ancestor?(self)
98
-
99
- nil # unrelated lineages
100
- end
101
-
102
- # TODO: this is inconsistent.
103
- # A WME retracted in-flight will be visible in active enumerators
104
- # but a token will not.
105
- # But this is how it works.
106
-
107
- def wmes(alpha)
108
- DuplicatingEnumerator.new(raw_wmes(alpha))
109
- end
110
-
111
- def tokens(beta)
112
- DeleteSafeEnumerator.new(raw_tokens(beta))
113
- end
114
-
115
- def add_wme(wme, alpha)
116
- wmes = raw_wmes(alpha)
117
- wmes << wme unless wmes.include?(wme)
118
- end
119
-
120
- def remove_wme(wme, alpha)
121
- raw_wmes(alpha).delete(wme)
122
- end
123
-
124
- def add_token(token, beta)
125
- tokens = raw_tokens(beta)
126
- tokens << token unless tokens.include?(token)
127
- end
128
-
129
- def remove_token(token, beta)
130
- raw_tokens(beta).delete(token)
131
- end
132
-
133
- def raw_wmes(alpha)
134
- @raw_wmes[alpha.object_id]
135
- end
136
-
137
- def raw_tokens(beta)
138
- @raw_tokens[beta.object_id]
139
- end
140
- end
141
- end
@@ -1,40 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe "ANY rule" do
4
- include Wongi::Engine::DSL
5
-
6
- let(:engine) { Wongi::Engine.create }
7
-
8
- context 'with two options' do
9
- let :production do
10
- engine << rule do
11
- forall {
12
- any {
13
- option {
14
- has :A, :path1, :_
15
- }
16
- option {
17
- has :A, :path2, :_
18
- }
19
- }
20
- }
21
- end
22
- end
23
-
24
- it 'should fire on the first path' do
25
- engine << [:x, :path1, true]
26
- expect(production.tokens).to have(1).item
27
- end
28
-
29
- it 'should fire on the second path' do
30
- engine << [:x, :path2, true]
31
- expect(production.tokens).to have(1).item
32
- end
33
-
34
- it 'should fire twice on both paths at once' do
35
- engine << [:x, :path1, true]
36
- engine << [:x, :path2, true]
37
- expect(production.tokens).to have(2).items
38
- end
39
- end
40
- end