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
@@ -0,0 +1,589 @@
|
|
1
|
+
module Wongi::Engine
|
2
|
+
class Overlay
|
3
|
+
class JoinResults
|
4
|
+
attr_reader :by_wme, :by_token, :hidden
|
5
|
+
private :by_wme, :by_token
|
6
|
+
private :hidden
|
7
|
+
def initialize
|
8
|
+
@by_wme = Hash.new { |h, k| h[k] = {} }
|
9
|
+
@by_token = Hash.new { |h, k| h[k] = {} }
|
10
|
+
@hidden = {}
|
11
|
+
end
|
12
|
+
|
13
|
+
def for(wme: nil, token: nil)
|
14
|
+
if wme
|
15
|
+
by_wme.key?(wme.object_id) ? by_wme[wme.object_id].keys : []
|
16
|
+
elsif token
|
17
|
+
by_token.key?(token.object_id) ? by_token[token.object_id].keys : []
|
18
|
+
else
|
19
|
+
[]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def has?(jr)
|
24
|
+
by_wme.key?(jr.wme.object_id) && by_wme[jr.wme.object_id].key?(jr)
|
25
|
+
end
|
26
|
+
|
27
|
+
def hidden?(jr)
|
28
|
+
hidden.key?(jr)
|
29
|
+
end
|
30
|
+
|
31
|
+
def add(jr)
|
32
|
+
if hidden.key?(jr)
|
33
|
+
hidden.delete(jr)
|
34
|
+
else
|
35
|
+
by_wme[jr.wme.object_id][jr] = true
|
36
|
+
by_token[jr.token.object_id][jr] = true
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def remove(jr)
|
41
|
+
unless has?(jr)
|
42
|
+
hide(jr)
|
43
|
+
return
|
44
|
+
end
|
45
|
+
|
46
|
+
if by_wme.key?(jr.wme.object_id)
|
47
|
+
by_wme[jr.wme.object_id].delete(jr)
|
48
|
+
if by_wme[jr.wme.object_id].empty?
|
49
|
+
by_wme.delete(jr.wme.object_id)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
if by_token.key?(jr.token.object_id)
|
54
|
+
by_token[jr.token.object_id].delete(jr)
|
55
|
+
if by_token[jr.token.object_id].empty?
|
56
|
+
by_token.delete(jr.token.object_id)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def hide(jr)
|
62
|
+
hidden[jr] = true
|
63
|
+
end
|
64
|
+
|
65
|
+
def remove_token(token)
|
66
|
+
return unless by_token.key?(token.object_id)
|
67
|
+
|
68
|
+
by_token[token.object_id].keys.each do |jr|
|
69
|
+
remove(jr)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def remove_wme(wme)
|
74
|
+
return unless by_wme.key?(wme.object_id)
|
75
|
+
|
76
|
+
by_wme[wme.object_id].keys do |jr|
|
77
|
+
remove(jr)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
attr_reader :rete, :parent, :wmes, :tokens, :indexes, :queue, :hidden_parent_wmes, :hidden_parent_tokens, :wme_generators, :hidden_parent_wme_generators, :wme_manual, :hidden_parent_wme_manual, :neg_join_results, :opt_join_results, :ncc_tokens, :ncc_tokens_owner, :hidden_ncc_tokens
|
83
|
+
private :wmes, :tokens
|
84
|
+
private :indexes
|
85
|
+
private :queue
|
86
|
+
private :hidden_parent_wmes
|
87
|
+
private :hidden_parent_tokens
|
88
|
+
private :wme_generators
|
89
|
+
private :hidden_parent_wme_generators
|
90
|
+
private :wme_manual
|
91
|
+
private :hidden_parent_wme_manual
|
92
|
+
private :neg_join_results
|
93
|
+
private :opt_join_results
|
94
|
+
private :ncc_tokens
|
95
|
+
private :ncc_tokens_owner
|
96
|
+
private :hidden_ncc_tokens
|
97
|
+
|
98
|
+
def initialize(rete, parent = nil)
|
99
|
+
@rete = rete
|
100
|
+
@parent = parent
|
101
|
+
|
102
|
+
@wmes = []
|
103
|
+
@indexes = [
|
104
|
+
AlphaIndex.new(%i[subject]),
|
105
|
+
AlphaIndex.new(%i[predicate]),
|
106
|
+
AlphaIndex.new(%i[object]),
|
107
|
+
AlphaIndex.new(%i[subject predicate]),
|
108
|
+
AlphaIndex.new(%i[subject object]),
|
109
|
+
AlphaIndex.new(%i[predicate object]),
|
110
|
+
]
|
111
|
+
@hidden_parent_wmes = {}
|
112
|
+
|
113
|
+
@tokens = Hash.new { |h, k| h[k] = [] }
|
114
|
+
@hidden_parent_tokens = {}
|
115
|
+
|
116
|
+
@wme_generators = Hash.new { |h, k| h[k] = [] }
|
117
|
+
@hidden_parent_wme_generators = {}
|
118
|
+
|
119
|
+
@wme_manual = {}
|
120
|
+
@hidden_parent_wme_manual = {}
|
121
|
+
|
122
|
+
@neg_join_results = JoinResults.new
|
123
|
+
@opt_join_results = JoinResults.new
|
124
|
+
|
125
|
+
@ncc_tokens = Hash.new { |h, k| h[k] = [] }
|
126
|
+
@ncc_tokens_owner = {}
|
127
|
+
@hidden_ncc_tokens = Hash.new { |h, k| h[k] = {} }
|
128
|
+
|
129
|
+
@queue = []
|
130
|
+
end
|
131
|
+
|
132
|
+
def new_child
|
133
|
+
Overlay.new(rete, self)
|
134
|
+
end
|
135
|
+
|
136
|
+
def ancestor?(other)
|
137
|
+
return false if parent.nil?
|
138
|
+
return true if parent == other
|
139
|
+
|
140
|
+
parent.ancestor?(other)
|
141
|
+
end
|
142
|
+
|
143
|
+
def dispose!
|
144
|
+
return if default?
|
145
|
+
|
146
|
+
tokens.each do |_node, tokens|
|
147
|
+
tokens.each(&:dispose!)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def <<(thing)
|
152
|
+
case thing
|
153
|
+
when Array
|
154
|
+
assert(WME.new(*thing))
|
155
|
+
when WME
|
156
|
+
assert(thing)
|
157
|
+
else
|
158
|
+
raise Error, "overlays can only accept data"
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def assert(wme, generator: nil)
|
163
|
+
operation = [:assert, wme, { generator: generator }]
|
164
|
+
queue.push(operation)
|
165
|
+
|
166
|
+
run_queue if queue.length == 1
|
167
|
+
end
|
168
|
+
|
169
|
+
def retract(wme, options = {})
|
170
|
+
if wme.is_a?(Array)
|
171
|
+
wme = WME.new(*wme)
|
172
|
+
end
|
173
|
+
|
174
|
+
operation = [:retract, wme, options]
|
175
|
+
queue.push(operation)
|
176
|
+
|
177
|
+
run_queue if queue.length == 1
|
178
|
+
end
|
179
|
+
|
180
|
+
def run_queue
|
181
|
+
until queue.empty?
|
182
|
+
operation, wme, options = queue.shift
|
183
|
+
case operation
|
184
|
+
when :assert
|
185
|
+
wme = find_ignoring_hidden(wme) || wme
|
186
|
+
add_wme(wme, **options)
|
187
|
+
rete.real_assert(wme)
|
188
|
+
when :retract
|
189
|
+
wme = find_ignoring_hidden(wme)
|
190
|
+
return if wme.nil? # it's perhaps better to return quietly, because complicated cascades may delete a WME while we're going through the queue
|
191
|
+
|
192
|
+
remove_wme(wme, **options)
|
193
|
+
rete.real_retract(wme)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
def default?
|
199
|
+
self == rete.default_overlay
|
200
|
+
end
|
201
|
+
|
202
|
+
# TODO: this is inconsistent.
|
203
|
+
# A WME retracted in-flight will be visible in active enumerators
|
204
|
+
# but a token will not.
|
205
|
+
# But this is how it works.
|
206
|
+
|
207
|
+
# def wmes(template)
|
208
|
+
# DuplicatingEnumerator.new(index(template))
|
209
|
+
# end
|
210
|
+
|
211
|
+
# def tokens(beta)
|
212
|
+
# DeleteSafeEnumerator.new(raw_tokens(beta))
|
213
|
+
# end
|
214
|
+
|
215
|
+
def find(wme)
|
216
|
+
if wme.is_a?(Array)
|
217
|
+
wme = WME.new(*wme)
|
218
|
+
end
|
219
|
+
find_wme(wme)
|
220
|
+
end
|
221
|
+
|
222
|
+
def select(*args)
|
223
|
+
case args.length
|
224
|
+
when 1
|
225
|
+
case args.first
|
226
|
+
when Template
|
227
|
+
select_by_template(args.first)
|
228
|
+
else
|
229
|
+
raise ArgumentError
|
230
|
+
end
|
231
|
+
when 3
|
232
|
+
select_by_template(Template.new(*args))
|
233
|
+
else
|
234
|
+
raise ArgumentError
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
def manual?(wme)
|
239
|
+
wme_manual.key?(wme.object_id) ||
|
240
|
+
if parent
|
241
|
+
parent.manual?(wme) && !hidden_parent_wme_manual.key?(wme.object_id)
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
def generated?(wme)
|
246
|
+
generators(wme).any?
|
247
|
+
end
|
248
|
+
|
249
|
+
def generated_by?(wme, gen)
|
250
|
+
own_generated_by?(wme, gen) ||
|
251
|
+
if parent
|
252
|
+
parent.generated_by?(wme, gen) && !hidden_parent_wme_generators.key?(gen)
|
253
|
+
else
|
254
|
+
false
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
private def own_generated_by?(wme, gen)
|
259
|
+
wme_generators.key?(wme.object_id) && wme_generators[wme.object_id].include?(gen)
|
260
|
+
end
|
261
|
+
|
262
|
+
def generators(wme)
|
263
|
+
own_generators = wme_generators.key?(wme.object_id) ? wme_generators[wme.object_id] : []
|
264
|
+
parent_generators =
|
265
|
+
if parent
|
266
|
+
parent.generators(wme).reject { |g| hidden_parent_wme_generators.key?(g) }
|
267
|
+
else
|
268
|
+
[]
|
269
|
+
end
|
270
|
+
own_generators + parent_generators
|
271
|
+
end
|
272
|
+
|
273
|
+
private def own_manual?(wme)
|
274
|
+
wme_manual.key?(wme.object_id)
|
275
|
+
end
|
276
|
+
|
277
|
+
private def own_generated?(wme)
|
278
|
+
wme_generators.key?(wme.object_id) && wme_generators[wme.object_id].any?
|
279
|
+
end
|
280
|
+
|
281
|
+
private def find_wme(wme)
|
282
|
+
find_own_wme(wme) || find_parents_wme(wme)
|
283
|
+
end
|
284
|
+
|
285
|
+
private def find_own_wme(wme)
|
286
|
+
collections = indexes.map { |index|
|
287
|
+
index.collection_for_wme(wme)
|
288
|
+
}
|
289
|
+
smallest = collections.min_by(&:size)
|
290
|
+
smallest.find { _1 == wme }
|
291
|
+
end
|
292
|
+
|
293
|
+
private def find_parents_wme(wme)
|
294
|
+
return unless parent
|
295
|
+
|
296
|
+
parent_wme = parent.find(wme)
|
297
|
+
parent_wme unless hidden_wme?(parent_wme)
|
298
|
+
end
|
299
|
+
|
300
|
+
def find_ignoring_hidden(wme)
|
301
|
+
find_own_wme(wme) ||
|
302
|
+
if parent
|
303
|
+
parent.find_ignoring_hidden(wme)
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
private def select_by_template(template)
|
308
|
+
select_parents_template(template) + select_own_template(template)
|
309
|
+
end
|
310
|
+
|
311
|
+
private def select_own_template(template)
|
312
|
+
if template.concrete?
|
313
|
+
wme = find_own_wme(WME.from_concrete_template(template))
|
314
|
+
wme ? [wme] : []
|
315
|
+
elsif template.root?
|
316
|
+
wmes
|
317
|
+
else
|
318
|
+
indexes.map { |index|
|
319
|
+
index.collections_for_template(template)
|
320
|
+
}.compact.first
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
private def select_parents_template(template)
|
325
|
+
if parent
|
326
|
+
parent.select(template).reject { hidden_wme?(_1) }
|
327
|
+
else
|
328
|
+
[]
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
private def add_wme(wme, generator:)
|
333
|
+
# p add_wme: { wme:, generator: !!generator }
|
334
|
+
|
335
|
+
# if we previously hid this locally, unhide it
|
336
|
+
hidden_parent_wmes.delete(wme.object_id)
|
337
|
+
if generator
|
338
|
+
hidden_parent_wme_generators.delete(generator)
|
339
|
+
else
|
340
|
+
hidden_parent_wme_manual.delete(wme.object_id)
|
341
|
+
end
|
342
|
+
|
343
|
+
if find_own_wme(wme)
|
344
|
+
if generator
|
345
|
+
wme_generators[wme.object_id] << generator unless own_generated_by?(wme, generator)
|
346
|
+
else
|
347
|
+
wme_manual[wme.object_id] = true
|
348
|
+
end
|
349
|
+
elsif find_parents_wme(wme)
|
350
|
+
if generator
|
351
|
+
wme_generators[wme.object_id] << generator unless generated_by?(wme, generator)
|
352
|
+
else
|
353
|
+
wme_manual[wme.object_id] = true unless manual?(wme)
|
354
|
+
end
|
355
|
+
else
|
356
|
+
wmes << wme
|
357
|
+
indexes.each { _1.add(wme) }
|
358
|
+
if generator
|
359
|
+
wme_generators[wme.object_id] << generator
|
360
|
+
else
|
361
|
+
wme_manual[wme.object_id] = true
|
362
|
+
end
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
366
|
+
private def remove_wme(wme, generator: nil)
|
367
|
+
# p remove_wme: { wme:, generator: !!generator }
|
368
|
+
|
369
|
+
if find_own_wme(wme)
|
370
|
+
if generator
|
371
|
+
if own_generated_by?(wme, generator)
|
372
|
+
wme_generators[wme.object_id].delete(generator)
|
373
|
+
wme_generators.delete(wme.object_id) if wme_generators[wme.object_id].empty?
|
374
|
+
end
|
375
|
+
elsif own_manual?(wme)
|
376
|
+
wme_manual.delete(wme.object_id)
|
377
|
+
end
|
378
|
+
|
379
|
+
if !own_generated?(wme) && !own_manual?(wme)
|
380
|
+
wmes.delete(wme)
|
381
|
+
indexes.each { _1.remove(wme) }
|
382
|
+
end
|
383
|
+
|
384
|
+
neg_join_results.remove_wme(wme)
|
385
|
+
opt_join_results.remove_wme(wme)
|
386
|
+
end
|
387
|
+
|
388
|
+
# did we also have an unshadowed parent version?
|
389
|
+
return unless find_parents_wme(wme)
|
390
|
+
|
391
|
+
# must be parents' then
|
392
|
+
|
393
|
+
if generator
|
394
|
+
# first, delete local
|
395
|
+
if own_generated_by?(wme, generator)
|
396
|
+
wme_generators[wme.object_id].delete(generator)
|
397
|
+
wme_generators.delete(wme.object_id) if wme_generators[wme.object_id].empty?
|
398
|
+
end
|
399
|
+
# if we're still generated, hide parents'
|
400
|
+
if generated_by?(wme, generator)
|
401
|
+
hidden_parent_wme_generators[generator] = true
|
402
|
+
end
|
403
|
+
else
|
404
|
+
if own_manual?(wme)
|
405
|
+
wme_manual.delete(wme.object_id)
|
406
|
+
end
|
407
|
+
if manual?(wme)
|
408
|
+
hidden_parent_wme_manual[wme.object_id] = true
|
409
|
+
end
|
410
|
+
end
|
411
|
+
|
412
|
+
if !manual?(wme) && !generated?(wme)
|
413
|
+
hidden_parent_wmes[wme.object_id] = true
|
414
|
+
end
|
415
|
+
end
|
416
|
+
|
417
|
+
def add_token(token)
|
418
|
+
# p add_token: {token:}
|
419
|
+
# TODO: is this really likely to happen? we don't normally restore deleted tokens but rather create new ones in the activation
|
420
|
+
if hidden_token?(token)
|
421
|
+
hidden_parent_tokens.delete(token.object_id)
|
422
|
+
return
|
423
|
+
end
|
424
|
+
|
425
|
+
tokens[token.node.object_id].push(token) # unless tokens.include?(token) # TODO: pretty unlikely to somehow trigger a repeated evaluation for the same token?..
|
426
|
+
end
|
427
|
+
|
428
|
+
def remove_token(token)
|
429
|
+
# p remove_token: {token:}
|
430
|
+
if own_node_tokens(token.node).find { _1.equal?(token) }.nil?
|
431
|
+
if parents_node_tokens(token.node).find { _1.equal?(token) }
|
432
|
+
hidden_parent_tokens[token.object_id] = true
|
433
|
+
|
434
|
+
# do not hide JRs from the WME side: it will be done in the alpha deactivation and the JRs have to stay visible until then
|
435
|
+
parent_neg_join_results_for(token: token).each { neg_join_results.hide(_1) }
|
436
|
+
parent_opt_join_results_for(token: token).each { opt_join_results.hide(_1) }
|
437
|
+
|
438
|
+
parent_ncc_tokens_for(token).each do |ncc|
|
439
|
+
hidden_ncc_tokens[token][ncc] = true
|
440
|
+
end
|
441
|
+
end
|
442
|
+
return
|
443
|
+
end
|
444
|
+
|
445
|
+
remove_own_token(token)
|
446
|
+
end
|
447
|
+
|
448
|
+
def remove_own_token(token)
|
449
|
+
# p remove_own_token: {token:}
|
450
|
+
tokens[token.node.object_id].delete(token)
|
451
|
+
neg_join_results.remove_token(token)
|
452
|
+
opt_join_results.remove_token(token)
|
453
|
+
|
454
|
+
# if this is an NCC partner token
|
455
|
+
if (owner = ncc_tokens_owner[token])
|
456
|
+
if ncc_tokens.key?(owner)
|
457
|
+
ncc_tokens[owner].delete(token)
|
458
|
+
end
|
459
|
+
if hidden_ncc_tokens.key?(owner)
|
460
|
+
hidden_ncc_tokens[owner].delete(token)
|
461
|
+
end
|
462
|
+
end
|
463
|
+
|
464
|
+
# if this is an NCC owner token
|
465
|
+
own_ncc_tokens_for(token).each do |ncc|
|
466
|
+
ncc_tokens_owner.delete(ncc)
|
467
|
+
end
|
468
|
+
ncc_tokens.delete(token)
|
469
|
+
hidden_ncc_tokens.delete(token)
|
470
|
+
|
471
|
+
# we know we are the owner, and nobody wants it anymore, so this is the safe place to do it
|
472
|
+
token.dispose!
|
473
|
+
end
|
474
|
+
|
475
|
+
def node_tokens(beta)
|
476
|
+
parents = parents_node_tokens(beta)
|
477
|
+
own = own_node_tokens(beta)
|
478
|
+
parents + own
|
479
|
+
end
|
480
|
+
|
481
|
+
private def own_node_tokens(beta)
|
482
|
+
tokens[beta.object_id]
|
483
|
+
end
|
484
|
+
|
485
|
+
private def parents_node_tokens(beta)
|
486
|
+
if parent
|
487
|
+
parent.node_tokens(beta).reject { hidden_parent_tokens.key?(_1.object_id) }
|
488
|
+
else
|
489
|
+
[]
|
490
|
+
end
|
491
|
+
end
|
492
|
+
|
493
|
+
def add_neg_join_result(njr)
|
494
|
+
neg_join_results.add(njr)
|
495
|
+
end
|
496
|
+
|
497
|
+
def remove_neg_join_result(njr)
|
498
|
+
neg_join_results.remove(njr)
|
499
|
+
end
|
500
|
+
|
501
|
+
def neg_join_results_for(wme: nil, token: nil)
|
502
|
+
if wme
|
503
|
+
neg_join_results.for(wme: wme) + parent_neg_join_results_for(wme: wme)
|
504
|
+
elsif token
|
505
|
+
neg_join_results.for(token: token) + parent_neg_join_results_for(token: token)
|
506
|
+
else
|
507
|
+
[]
|
508
|
+
end
|
509
|
+
end
|
510
|
+
|
511
|
+
def add_opt_join_result(ojr)
|
512
|
+
opt_join_results.add(ojr)
|
513
|
+
end
|
514
|
+
|
515
|
+
def remove_opt_join_result(ojr)
|
516
|
+
# p remove_opt_join_result: {ojr:}
|
517
|
+
opt_join_results.remove(ojr)
|
518
|
+
end
|
519
|
+
|
520
|
+
def opt_join_results_for(wme: nil, token: nil)
|
521
|
+
if wme
|
522
|
+
opt_join_results.for(wme: wme) + parent_opt_join_results_for(wme: wme)
|
523
|
+
elsif token
|
524
|
+
opt_join_results.for(token: token) + parent_opt_join_results_for(token: token)
|
525
|
+
else
|
526
|
+
[]
|
527
|
+
end
|
528
|
+
end
|
529
|
+
|
530
|
+
private def parent_opt_join_results_for(wme: nil, token: nil)
|
531
|
+
if parent
|
532
|
+
parent.opt_join_results_for(wme: wme, token: token).reject { opt_join_results.hidden?(_1) }
|
533
|
+
else
|
534
|
+
[]
|
535
|
+
end
|
536
|
+
end
|
537
|
+
|
538
|
+
private def parent_neg_join_results_for(wme: nil, token: nil)
|
539
|
+
if parent
|
540
|
+
parent.neg_join_results_for(wme: wme, token: token).reject { neg_join_results.hidden?(_1) }
|
541
|
+
else
|
542
|
+
[]
|
543
|
+
end
|
544
|
+
end
|
545
|
+
|
546
|
+
def add_ncc_token(owner, ncc)
|
547
|
+
if hidden_ncc_tokens.key?(owner) && hidden_ncc_tokens[token].include?(ncc)
|
548
|
+
hidden_ncc_tokens[owner].delete(ncc)
|
549
|
+
if hidden_ncc_tokens[owner].empty?
|
550
|
+
hidden_ncc_tokens.delete(owner)
|
551
|
+
end
|
552
|
+
return
|
553
|
+
end
|
554
|
+
|
555
|
+
ncc_tokens[owner] << ncc
|
556
|
+
ncc_tokens_owner[ncc] = owner
|
557
|
+
end
|
558
|
+
|
559
|
+
def ncc_owner(ncc)
|
560
|
+
ncc_tokens_owner[ncc]
|
561
|
+
end
|
562
|
+
|
563
|
+
def ncc_tokens_for(token)
|
564
|
+
own_ncc_tokens_for(token) + parent_ncc_tokens_for(token)
|
565
|
+
end
|
566
|
+
|
567
|
+
private def own_ncc_tokens_for(token)
|
568
|
+
ncc_tokens.key?(token) ? ncc_tokens[token] : []
|
569
|
+
end
|
570
|
+
|
571
|
+
private def parent_ncc_tokens_for(token)
|
572
|
+
if parent
|
573
|
+
parent.ncc_tokens_for(token).reject { |parent_token|
|
574
|
+
hidden_ncc_tokens.key?(token) && hidden_ncc_tokens[token].key?(parent_token)
|
575
|
+
}
|
576
|
+
else
|
577
|
+
[]
|
578
|
+
end
|
579
|
+
end
|
580
|
+
|
581
|
+
private def hidden_wme?(wme)
|
582
|
+
hidden_parent_wmes.key?(wme.object_id)
|
583
|
+
end
|
584
|
+
|
585
|
+
private def hidden_token?(token)
|
586
|
+
hidden_parent_tokens.key?(token.object_id)
|
587
|
+
end
|
588
|
+
end
|
589
|
+
end
|
@@ -1,9 +1,21 @@
|
|
1
1
|
module Wongi::Engine
|
2
2
|
Template = Struct.new(:subject, :predicate, :object) do
|
3
3
|
def self.variable?(thing)
|
4
|
-
|
4
|
+
thing.is_a?(Symbol) && thing[0] >= 'A' && thing[0] <= 'Z'
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.placeholder?(thing)
|
8
|
+
thing.is_a?(Symbol) && thing.start_with?('_')
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.concrete?(thing)
|
12
|
+
!variable?(thing) && !placeholder?(thing)
|
13
|
+
end
|
5
14
|
|
6
|
-
|
15
|
+
def self.match?(wme_element, template_element)
|
16
|
+
return true if variable?(template_element) || placeholder?(template_element)
|
17
|
+
|
18
|
+
wme_element == template_element
|
7
19
|
end
|
8
20
|
|
9
21
|
# TODO: reintroduce Network#import when bringing back RDF support
|
@@ -12,6 +24,10 @@ module Wongi::Engine
|
|
12
24
|
subject == :_ && predicate == :_ && object == :_
|
13
25
|
end
|
14
26
|
|
27
|
+
def concrete?
|
28
|
+
Template.concrete?(subject) && Template.concrete?(predicate) && Template.concrete?(object)
|
29
|
+
end
|
30
|
+
|
15
31
|
def variables
|
16
32
|
[].tap do |a|
|
17
33
|
a << subject if Template.variable?(subject)
|
@@ -24,6 +40,10 @@ module Wongi::Engine
|
|
24
40
|
@hash ||= [subject.hash, predicate.hash, object.hash].hash
|
25
41
|
end
|
26
42
|
|
43
|
+
def eql?(other)
|
44
|
+
subject.eql?(other.subject) && predicate.eql?(other.predicate) && object.eql?(other.object)
|
45
|
+
end
|
46
|
+
|
27
47
|
def self.hash_for(*args)
|
28
48
|
args.map(&:hash).hash
|
29
49
|
end
|
data/lib/wongi-engine/token.rb
CHANGED
@@ -1,27 +1,15 @@
|
|
1
1
|
module Wongi::Engine
|
2
2
|
class Token
|
3
|
-
|
3
|
+
attr_reader :children, :wme, :node, :generated_wmes
|
4
|
+
attr_accessor :parent
|
4
5
|
|
5
|
-
|
6
|
-
attr_accessor :owner, :parent
|
7
|
-
|
8
|
-
attr_predicate :optional
|
9
|
-
attr_predicate :deleted
|
10
|
-
|
11
|
-
def initialize(node, token, wme, assignments)
|
6
|
+
def initialize(node, token, wme, assignments = {})
|
12
7
|
@node = node
|
13
8
|
@parent = token
|
14
9
|
@wme = wme
|
15
10
|
@assignments = assignments
|
16
|
-
@overlay = if wme
|
17
|
-
wme.overlay.highest(token.overlay)
|
18
|
-
else
|
19
|
-
token ? token.overlay : node.rete.default_overlay
|
20
|
-
end
|
21
11
|
@children = []
|
22
12
|
@deleted = false
|
23
|
-
@neg_join_results = []
|
24
|
-
@opt_join_results = []
|
25
13
|
@ncc_results = []
|
26
14
|
@generated_wmes = []
|
27
15
|
token.children << self if token
|
@@ -49,8 +37,6 @@ module Wongi::Engine
|
|
49
37
|
|
50
38
|
def [](var)
|
51
39
|
a = assignments[var]
|
52
|
-
return unless a
|
53
|
-
|
54
40
|
a.respond_to?(:call) ? a.call(self) : a
|
55
41
|
end
|
56
42
|
|
@@ -64,8 +50,8 @@ module Wongi::Engine
|
|
64
50
|
end
|
65
51
|
|
66
52
|
def to_s
|
67
|
-
str = "TOKEN [ #{object_id}
|
68
|
-
all_assignments.each_pair { |key, value| str << "#{key}
|
53
|
+
str = "TOKEN [ #{object_id} ancestors=#{ancestors.map(&:object_id).map(&:to_s).join('.')} "
|
54
|
+
all_assignments.each_pair { |key, value| str << "#{key}=#{value.is_a?(TokenAssignment) ? "#{value.call} (#{value})" : value} " }
|
69
55
|
str << "]"
|
70
56
|
str
|
71
57
|
end
|
@@ -74,15 +60,13 @@ module Wongi::Engine
|
|
74
60
|
to_s
|
75
61
|
end
|
76
62
|
|
77
|
-
def destroy
|
78
|
-
|
79
|
-
end
|
63
|
+
# def destroy
|
64
|
+
# deleted!
|
65
|
+
# end
|
80
66
|
|
81
67
|
def dispose!
|
82
|
-
parent.children.delete(self) if parent
|
83
|
-
|
84
|
-
opt_join_results.dup.each(&:unlink)
|
85
|
-
@parent = nil
|
68
|
+
# parent.children.delete(self) if parent
|
69
|
+
# @parent = nil
|
86
70
|
@wme = nil
|
87
71
|
end
|
88
72
|
|