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.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +2 -2
- data/.gitignore +2 -0
- data/README.md +12 -12
- data/lib/wongi-engine/alpha_index.rb +58 -0
- data/lib/wongi-engine/alpha_memory.rb +2 -24
- data/lib/wongi-engine/beta/aggregate_node.rb +17 -15
- data/lib/wongi-engine/beta/assignment_node.rb +7 -2
- data/lib/wongi-engine/beta/beta_node.rb +36 -23
- data/lib/wongi-engine/beta/filter_node.rb +8 -13
- data/lib/wongi-engine/beta/join_node.rb +17 -29
- data/lib/wongi-engine/beta/ncc_node.rb +14 -26
- data/lib/wongi-engine/beta/ncc_partner.rb +18 -18
- data/lib/wongi-engine/beta/neg_node.rb +25 -55
- data/lib/wongi-engine/beta/optional_node.rb +26 -48
- data/lib/wongi-engine/beta/or_node.rb +24 -1
- data/lib/wongi-engine/beta/production_node.rb +9 -3
- data/lib/wongi-engine/beta/root_node.rb +47 -0
- data/lib/wongi-engine/beta.rb +1 -1
- data/lib/wongi-engine/compiler.rb +6 -34
- data/lib/wongi-engine/dsl/action/{base.rb → base_action.rb} +5 -1
- data/lib/wongi-engine/dsl/action/error_generator.rb +1 -1
- data/lib/wongi-engine/dsl/action/simple_action.rb +1 -1
- data/lib/wongi-engine/dsl/action/simple_collector.rb +1 -1
- data/lib/wongi-engine/dsl/action/statement_generator.rb +21 -22
- data/lib/wongi-engine/dsl/action/trace_action.rb +1 -1
- data/lib/wongi-engine/dsl/clause/fact.rb +2 -6
- data/lib/wongi-engine/dsl.rb +1 -25
- data/lib/wongi-engine/graph.rb +1 -1
- data/lib/wongi-engine/network/debug.rb +2 -10
- data/lib/wongi-engine/network.rb +44 -105
- data/lib/wongi-engine/overlay.rb +589 -0
- data/lib/wongi-engine/template.rb +22 -2
- data/lib/wongi-engine/token.rb +10 -26
- data/lib/wongi-engine/token_assignment.rb +15 -0
- data/lib/wongi-engine/version.rb +1 -1
- data/lib/wongi-engine/wme.rb +10 -39
- data/lib/wongi-engine.rb +3 -1
- data/spec/alpha_index_spec.rb +78 -0
- data/spec/bug_specs/issue_4_spec.rb +11 -11
- data/spec/high_level_spec.rb +8 -101
- data/spec/network_spec.rb +8 -6
- data/spec/overlay_spec.rb +161 -3
- data/spec/rule_specs/any_rule_spec.rb +39 -0
- data/spec/rule_specs/assign_spec.rb +1 -1
- data/spec/rule_specs/maybe_rule_spec.rb +58 -1
- data/spec/rule_specs/ncc_spec.rb +78 -19
- data/spec/rule_specs/negative_rule_spec.rb +12 -14
- data/spec/spec_helper.rb +4 -0
- data/spec/wme_spec.rb +0 -32
- metadata +11 -9
- data/lib/wongi-engine/beta/beta_memory.rb +0 -60
- data/lib/wongi-engine/data_overlay.rb +0 -149
- 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)
|
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
|
data/spec/rule_specs/ncc_spec.rb
CHANGED
@@ -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 << [
|
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
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
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
|
-
|
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
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.
|
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-
|
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/
|
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:
|
197
|
+
version: 1.3.1
|
196
198
|
requirements: []
|
197
|
-
rubygems_version: 3.
|
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
|