wongi-engine 0.3.9 → 0.4.0.pre.alpha1

Sign up to get free protection for your applications and to get access to all the features.
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 +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 +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.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-07 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,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