wongi-engine 0.3.9 → 0.4.0.pre.alpha2

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 +12 -12
  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 +17 -15
  8. data/lib/wongi-engine/beta/assignment_node.rb +7 -2
  9. data/lib/wongi-engine/beta/beta_node.rb +36 -23
  10. data/lib/wongi-engine/beta/filter_node.rb +8 -13
  11. data/lib/wongi-engine/beta/join_node.rb +17 -29
  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 +25 -55
  15. data/lib/wongi-engine/beta/optional_node.rb +26 -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 +161 -3
  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 +12 -14
  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 -149
  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
@@ -16,7 +16,7 @@ describe "negative rule" do
16
16
  engine << [:x, :y, 42]
17
17
  expect(prod).to have(1).tokens
18
18
 
19
- engine << [:x, :y, :z]
19
+ engine << %i[x y z]
20
20
  expect(prod).to have(0).tokens
21
21
  end
22
22
 
@@ -68,20 +68,18 @@ describe "negative rule" do
68
68
  expect(prod).to have(1).tokens
69
69
  end
70
70
 
71
- # it "should not create infinite feedback loops by default" do
72
-
73
- # engine << rule('feedback') {
74
- # forall {
75
- # neg :a, :b, :_
76
- # }
77
- # make {
78
- # gen :a, :b, :c
79
- # }
80
- # }
81
-
82
- # 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
+ }
83
80
 
84
- # end
81
+ engine.should have(1).facts
82
+ end
85
83
 
86
84
  it "should create infinite feedback loops with unsafe option" do
87
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.9
4
+ version: 0.4.0.pre.alpha2
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-07 00:00:00.000000000 Z
11
+ date: 2022-10-17 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,149 +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
-
39
- @raw_tokens.each_value do |tokens|
40
- tokens.each(&:dispose!)
41
- end
42
- @raw_tokens.clear
43
-
44
- @raw_wmes.values.flatten.uniq.each do |wme|
45
- next if wme.neg_join_results.empty?
46
- wme.neg_join_results.dup.each do |njr|
47
- njr.neg_node.alpha_deactivate(wme)
48
- end
49
- end
50
- end
51
-
52
- def <<(thing)
53
- case thing
54
- when Array
55
- assert(WME.new(*thing).tap { |wme| wme.overlay = self })
56
- when WME
57
- assert(thing)
58
- else
59
- raise Error, "overlays can only accept data"
60
- end
61
- end
62
-
63
- def assert(wme)
64
- @next_cascade ||= []
65
- @next_cascade << [:assert, wme]
66
- return if @current_cascade
67
-
68
- @current_cascade = @next_cascade
69
- @next_cascade = nil
70
- process_cascade
71
- end
72
-
73
- def retract(wme, options = {})
74
- wme = WME.new(*wme) if wme.is_a? Array
75
- @next_cascade ||= []
76
- @next_cascade << [:retract, wme, options]
77
- return if @current_cascade
78
-
79
- @current_cascade = @next_cascade
80
- @next_cascade = nil
81
- process_cascade
82
- end
83
-
84
- def process_cascade
85
- while @current_cascade
86
- @current_cascade.each do |(operation, wme, options)|
87
- case operation
88
- when :assert
89
- wme.overlay = self
90
- rete.real_assert(wme)
91
- when :retract
92
- rete.real_retract(wme, options)
93
- wme.overlay = nil
94
- end
95
- end
96
- @current_cascade = @next_cascade
97
- @next_cascade = nil
98
- end
99
- end
100
-
101
- def highest(other)
102
- return self if self == other
103
- return self if other.nil?
104
- return self if ancestor?(other)
105
- return other if other.ancestor?(self)
106
-
107
- nil # unrelated lineages
108
- end
109
-
110
- # TODO: this is inconsistent.
111
- # A WME retracted in-flight will be visible in active enumerators
112
- # but a token will not.
113
- # But this is how it works.
114
-
115
- def wmes(alpha)
116
- DuplicatingEnumerator.new(raw_wmes(alpha))
117
- end
118
-
119
- def tokens(beta)
120
- DeleteSafeEnumerator.new(raw_tokens(beta))
121
- end
122
-
123
- def add_wme(wme, alpha)
124
- wmes = raw_wmes(alpha)
125
- wmes << wme unless wmes.include?(wme)
126
- end
127
-
128
- def remove_wme(wme, alpha)
129
- raw_wmes(alpha).delete(wme)
130
- end
131
-
132
- def add_token(token, beta)
133
- tokens = raw_tokens(beta)
134
- tokens << token unless tokens.include?(token)
135
- end
136
-
137
- def remove_token(token, beta)
138
- raw_tokens(beta).delete(token)
139
- end
140
-
141
- def raw_wmes(alpha)
142
- @raw_wmes[alpha.object_id]
143
- end
144
-
145
- def raw_tokens(beta)
146
- @raw_tokens[beta.object_id]
147
- end
148
- end
149
- 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