wongi-engine 0.3.6 → 0.3.7
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/.gitignore +1 -0
- data/.rubocop.yml +80 -0
- data/.travis.yml +6 -16
- data/Gemfile +4 -0
- data/README.md +3 -3
- data/Rakefile +1 -2
- data/examples/ex01.rb +1 -1
- data/examples/ex02.rb +3 -7
- data/examples/graphviz.rb +1 -1
- data/examples/rdf.rb +1 -1
- data/examples/timeline.rb +6 -6
- data/lib/wongi-engine/alpha_memory.rb +5 -9
- data/lib/wongi-engine/beta/aggregate_node.rb +93 -0
- data/lib/wongi-engine/beta/assignment_node.rb +10 -18
- data/lib/wongi-engine/beta/beta_memory.rb +20 -9
- data/lib/wongi-engine/beta/beta_node.rb +14 -22
- data/lib/wongi-engine/beta/filter_node.rb +12 -15
- data/lib/wongi-engine/beta/join_node.rb +47 -61
- data/lib/wongi-engine/beta/ncc_node.rb +17 -20
- data/lib/wongi-engine/beta/ncc_partner.rb +17 -17
- data/lib/wongi-engine/beta/neg_node.rb +37 -37
- data/lib/wongi-engine/beta/optional_node.rb +53 -60
- data/lib/wongi-engine/beta/or_node.rb +6 -9
- data/lib/wongi-engine/beta/production_node.rb +6 -9
- data/lib/wongi-engine/beta.rb +1 -0
- data/lib/wongi-engine/compiler.rb +38 -22
- data/lib/wongi-engine/core_ext.rb +11 -20
- data/lib/wongi-engine/data_overlay.rb +23 -26
- data/lib/wongi-engine/dsl/action/assign_action.rb +1 -1
- data/lib/wongi-engine/dsl/action/base.rb +1 -4
- data/lib/wongi-engine/dsl/action/error_generator.rb +9 -9
- data/lib/wongi-engine/dsl/action/simple_action.rb +14 -10
- data/lib/wongi-engine/dsl/action/simple_collector.rb +10 -25
- data/lib/wongi-engine/dsl/action/statement_generator.rb +15 -14
- data/lib/wongi-engine/dsl/action/trace_action.rb +9 -9
- data/lib/wongi-engine/dsl/any_rule.rb +16 -20
- data/lib/wongi-engine/dsl/assuming.rb +8 -15
- data/lib/wongi-engine/dsl/builder.rb +13 -12
- data/lib/wongi-engine/dsl/clause/aggregate.rb +21 -0
- data/lib/wongi-engine/dsl/clause/assign.rb +4 -4
- data/lib/wongi-engine/dsl/clause/fact.rb +10 -6
- data/lib/wongi-engine/dsl/clause/gen.rb +3 -5
- data/lib/wongi-engine/dsl/clause/generic.rb +7 -9
- data/lib/wongi-engine/dsl/generated.rb +6 -12
- data/lib/wongi-engine/dsl/ncc_subrule.rb +6 -9
- data/lib/wongi-engine/dsl/query.rb +12 -15
- data/lib/wongi-engine/dsl/rule.rb +45 -50
- data/lib/wongi-engine/dsl.rb +30 -16
- data/lib/wongi-engine/enumerators.rb +2 -1
- data/lib/wongi-engine/error.rb +6 -7
- data/lib/wongi-engine/filter/asserting_test.rb +4 -7
- data/lib/wongi-engine/filter/equality_test.rb +15 -18
- data/lib/wongi-engine/filter/filter_test.rb +3 -6
- data/lib/wongi-engine/filter/greater_than_or_equal_test.rb +5 -2
- data/lib/wongi-engine/filter/greater_than_test.rb +15 -18
- data/lib/wongi-engine/filter/inequality_test.rb +15 -18
- data/lib/wongi-engine/filter/less_than_or_equal_test.rb +5 -2
- data/lib/wongi-engine/filter/less_than_test.rb +15 -18
- data/lib/wongi-engine/filter.rb +1 -1
- data/lib/wongi-engine/graph.rb +13 -20
- data/lib/wongi-engine/network/collectable.rb +10 -14
- data/lib/wongi-engine/network/debug.rb +16 -24
- data/lib/wongi-engine/network.rb +110 -129
- data/lib/wongi-engine/ruleset.rb +18 -20
- data/lib/wongi-engine/template.rb +31 -30
- data/lib/wongi-engine/token.rb +33 -33
- data/lib/wongi-engine/version.rb +1 -1
- data/lib/wongi-engine/wme.rb +17 -23
- data/lib/wongi-engine/wme_match_data.rb +5 -9
- data/lib/wongi-engine.rb +0 -4
- data/spec/action_class_spec.rb +43 -49
- data/spec/beta_node_spec.rb +2 -8
- data/spec/bug_specs/issue_4_spec.rb +12 -20
- data/spec/dataset_spec.rb +3 -6
- data/spec/filter_specs/assert_test_spec.rb +12 -31
- data/spec/filter_specs/greater_than_equality_test_spec.rb +2 -5
- data/spec/filter_specs/less_test_spec.rb +7 -17
- data/spec/filter_specs/less_than_equality_test_spec.rb +3 -6
- data/spec/generation_spec.rb +45 -54
- data/spec/high_level_spec.rb +95 -141
- data/spec/network_spec.rb +77 -68
- data/spec/overlay_spec.rb +4 -5
- data/spec/rule_specs/aggregate_spec.rb +197 -0
- data/spec/rule_specs/any_rule_spec.rb +4 -19
- data/spec/rule_specs/assign_spec.rb +10 -16
- data/spec/rule_specs/assuming_spec.rb +10 -17
- data/spec/rule_specs/maybe_rule_spec.rb +4 -15
- data/spec/rule_specs/ncc_spec.rb +48 -65
- data/spec/rule_specs/negative_rule_spec.rb +13 -27
- data/spec/rule_specs/or_rule_spec.rb +3 -13
- data/spec/ruleset_spec.rb +11 -17
- data/spec/simple_action_spec.rb +4 -14
- data/spec/wme_spec.rb +14 -21
- data/wongi-engine.gemspec +22 -22
- metadata +16 -37
- data/.hgignore +0 -7
- data/spec/dsl_spec.rb +0 -9
data/lib/wongi-engine/network.rb
CHANGED
@@ -3,18 +3,14 @@ require 'wongi-engine/network/debug'
|
|
3
3
|
|
4
4
|
module Wongi::Engine
|
5
5
|
class Network
|
6
|
-
|
7
|
-
attr_reader :alpha_top, :beta_top
|
8
|
-
attr_reader :queries, :results
|
9
|
-
attr_reader :productions
|
10
|
-
attr_reader :overlays
|
6
|
+
attr_reader :alpha_top, :beta_top, :queries, :results, :productions
|
11
7
|
|
12
8
|
include NetworkParts::Collectable
|
13
9
|
|
14
10
|
protected
|
11
|
+
|
15
12
|
attr_accessor :alpha_hash
|
16
|
-
attr_writer :alpha_top, :beta_top
|
17
|
-
attr_writer :queries, :results
|
13
|
+
attr_writer :alpha_top, :beta_top, :queries, :results
|
18
14
|
|
19
15
|
public
|
20
16
|
|
@@ -23,43 +19,43 @@ module Wongi::Engine
|
|
23
19
|
end
|
24
20
|
|
25
21
|
def rdf!
|
26
|
-
|
22
|
+
unless defined? Wongi::RDF::DocumentSupport
|
27
23
|
begin
|
28
24
|
require 'wongi-rdf'
|
29
|
-
rescue LoadError
|
25
|
+
rescue LoadError
|
30
26
|
raise "'wongi-rdf' is required for RDF support"
|
31
27
|
end
|
32
28
|
end
|
33
29
|
|
34
30
|
extend Wongi::RDF::DocumentSupport
|
31
|
+
|
35
32
|
class << self
|
36
33
|
def statements
|
37
34
|
alpha_top.wmes
|
38
35
|
end
|
39
36
|
end
|
40
37
|
|
41
|
-
@namespaces = {
|
38
|
+
@namespaces = {}
|
42
39
|
@blank_counter = 1
|
43
40
|
@ns_counter = 0
|
44
|
-
@used_blanks = {
|
41
|
+
@used_blanks = {}
|
45
42
|
end
|
46
43
|
|
47
44
|
def initialize
|
48
45
|
@timeline = []
|
49
|
-
self.alpha_top = AlphaMemory.new(
|
46
|
+
self.alpha_top = AlphaMemory.new(Template.new(:_, :_, :_), self)
|
50
47
|
self.alpha_hash = { alpha_top.template.hash => alpha_top }
|
51
48
|
self.beta_top = BetaMemory.new(nil)
|
52
|
-
|
53
|
-
|
49
|
+
beta_top.rete = self
|
50
|
+
beta_top.seed
|
54
51
|
self.queries = {}
|
55
52
|
self.results = {}
|
56
53
|
@revns = {}
|
57
54
|
|
58
|
-
@productions = {
|
55
|
+
@productions = {}
|
59
56
|
|
60
57
|
@collectors = {}
|
61
58
|
@collectors[:error] = []
|
62
|
-
|
63
59
|
end
|
64
60
|
|
65
61
|
def dump
|
@@ -118,46 +114,42 @@ module Wongi::Engine
|
|
118
114
|
end
|
119
115
|
|
120
116
|
# @private
|
121
|
-
def real_assert(
|
122
|
-
unless wme.rete == self
|
123
|
-
wme = wme.import_into self
|
124
|
-
end
|
117
|
+
def real_assert(wme)
|
118
|
+
wme = wme.import_into self unless wme.rete == self
|
125
119
|
|
126
|
-
source = best_alpha(wme)
|
127
|
-
if existing = find(wme.subject, wme.predicate, wme.object)
|
120
|
+
# source = best_alpha(wme)
|
121
|
+
if (existing = find(wme.subject, wme.predicate, wme.object))
|
128
122
|
existing.manual! if wme.manual?
|
129
123
|
return
|
130
124
|
end
|
131
125
|
|
132
|
-
alphas_for(
|
126
|
+
alphas_for(wme).each { |a| a.activate wme }
|
133
127
|
|
134
128
|
wme
|
135
129
|
end
|
136
130
|
|
137
131
|
# @private
|
138
|
-
def real_retract
|
132
|
+
def real_retract(wme, options)
|
139
133
|
real = find(wme.subject, wme.predicate, wme.object)
|
140
134
|
return if real.nil?
|
135
|
+
|
141
136
|
if real.generated? # still some generator tokens left
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
else
|
148
|
-
if options[:automatic] && real.manual? # auto-retracting a fact that has been added manually
|
149
|
-
return
|
150
|
-
end
|
137
|
+
raise Error, "cannot retract automatic facts" unless real.manual?
|
138
|
+
|
139
|
+
real.manual = false
|
140
|
+
elsif options[:automatic] && real.manual?
|
141
|
+
return
|
151
142
|
end
|
152
143
|
|
153
|
-
alphas_for(
|
144
|
+
alphas_for(real).each { |a| a.deactivate real }
|
154
145
|
end
|
155
146
|
|
156
147
|
def wmes
|
157
148
|
alpha_top.wmes
|
158
149
|
end
|
159
|
-
|
160
|
-
|
150
|
+
|
151
|
+
alias statements wmes
|
152
|
+
alias facts wmes
|
161
153
|
|
162
154
|
def in_snapshot?
|
163
155
|
@in_snapshot
|
@@ -166,128 +158,127 @@ module Wongi::Engine
|
|
166
158
|
def snapshot!
|
167
159
|
@timeline.each_with_index do |slice, index|
|
168
160
|
source = if index == @timeline.size - 1
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
161
|
+
alpha_hash
|
162
|
+
else
|
163
|
+
@timeline[index + 1]
|
164
|
+
end
|
173
165
|
# puts "source = #{source}"
|
174
166
|
wmes = {}
|
175
|
-
slice.each { |key, alpha| wmes[key] = alpha.wmes }
|
176
167
|
slice.each do |key, alpha|
|
168
|
+
wmes[key] = alpha.wmes
|
177
169
|
in_snapshot {
|
178
|
-
wmes[key].dup.each
|
170
|
+
wmes[key].dup.each(&:destroy)
|
179
171
|
}
|
180
172
|
alpha.snapshot! source[key]
|
181
173
|
end
|
182
174
|
end
|
183
175
|
end
|
184
176
|
|
185
|
-
def rule
|
186
|
-
r = DSL::Rule.new(
|
187
|
-
r.instance_eval
|
177
|
+
def rule(name = nil, &block)
|
178
|
+
r = DSL::Rule.new(name || generate_rule_name)
|
179
|
+
r.instance_eval(&block)
|
188
180
|
self << r
|
189
181
|
end
|
190
182
|
|
191
|
-
def query
|
183
|
+
def query(name, &block)
|
192
184
|
q = DSL::Query.new name
|
193
|
-
q.instance_eval
|
185
|
+
q.instance_eval(&block)
|
194
186
|
self << q
|
195
187
|
end
|
196
188
|
|
197
|
-
def <<
|
198
|
-
if something.respond_to?(
|
199
|
-
something.install(
|
189
|
+
def <<(something)
|
190
|
+
if something.respond_to?(:install)
|
191
|
+
something.install(self)
|
200
192
|
else
|
201
193
|
case something
|
202
194
|
when Array
|
203
|
-
assert
|
195
|
+
assert(WME.new(*something).tap { |wme| wme.overlay = default_overlay })
|
204
196
|
when WME
|
205
197
|
assert something
|
206
|
-
|
207
|
-
|
208
|
-
|
198
|
+
# when Wongi::RDF::Statement
|
199
|
+
# assert WME.new( something.subject, something.predicate, something.object, self )
|
200
|
+
# when Wongi::RDF::Document
|
209
201
|
# something.statements.each do |st|
|
210
202
|
# assert WME.new( st.subject, st.predicate, st.object, self )
|
211
203
|
# end
|
212
204
|
when Network
|
213
|
-
something.wmes.each { |wme| assert(
|
205
|
+
something.wmes.each { |wme| assert(wme) }
|
214
206
|
else
|
215
207
|
raise Error, "I don't know how to accept a #{something.class}"
|
216
208
|
end
|
217
209
|
end
|
218
210
|
end
|
219
211
|
|
220
|
-
def install_rule(
|
212
|
+
def install_rule(rule)
|
221
213
|
derived = rule.import_into self
|
222
214
|
production = build_production beta_top, derived.conditions, [], derived.actions, false
|
223
|
-
if rule.name
|
224
|
-
productions[ rule.name ] = production
|
225
|
-
end
|
215
|
+
productions[rule.name] = production if rule.name
|
226
216
|
production
|
227
217
|
end
|
228
218
|
|
229
|
-
def install_query(
|
219
|
+
def install_query(query)
|
230
220
|
derived = query.import_into self
|
231
221
|
prepare_query derived.name, derived.conditions, derived.parameters, derived.actions
|
232
222
|
end
|
233
223
|
|
234
|
-
def compile_alpha
|
224
|
+
def compile_alpha(condition)
|
235
225
|
template = Template.new :_, :_, :_
|
236
226
|
time = condition.time
|
237
227
|
|
238
|
-
template.subject = condition.subject unless Template.variable?(
|
239
|
-
template.predicate = condition.predicate unless Template.variable?(
|
240
|
-
template.object = condition.object unless Template.variable?(
|
228
|
+
template.subject = condition.subject unless Template.variable?(condition.subject)
|
229
|
+
template.predicate = condition.predicate unless Template.variable?(condition.predicate)
|
230
|
+
template.object = condition.object unless Template.variable?(condition.object)
|
241
231
|
|
242
232
|
hash = template.hash
|
243
233
|
# puts "COMPILED CONDITION #{condition} WITH KEY #{key}"
|
244
|
-
if time
|
245
|
-
return
|
246
|
-
|
247
|
-
return @timeline[time+1][
|
234
|
+
if time.zero?
|
235
|
+
return alpha_hash[hash] if alpha_hash.key?(hash)
|
236
|
+
elsif @timeline[time + 1] && @timeline[time + 1].key?(hash)
|
237
|
+
return @timeline[time + 1][hash]
|
248
238
|
end
|
249
239
|
|
250
|
-
alpha = AlphaMemory.new(
|
240
|
+
alpha = AlphaMemory.new(template, self)
|
251
241
|
|
252
|
-
if time
|
253
|
-
|
242
|
+
if time.zero?
|
243
|
+
alpha_hash[hash] = alpha
|
254
244
|
initial_fill alpha
|
255
245
|
else
|
256
|
-
if @timeline[time+1].nil?
|
246
|
+
if @timeline[time + 1].nil?
|
257
247
|
# => ensure lineage from 0 to time
|
258
248
|
compile_alpha condition.class.new(condition.subject, condition.predicate, condition.object, time: time + 1)
|
259
|
-
@timeline.unshift
|
249
|
+
@timeline.unshift({})
|
260
250
|
end
|
261
|
-
@timeline[time+1][
|
251
|
+
@timeline[time + 1][hash] = alpha
|
262
252
|
end
|
263
253
|
alpha
|
264
254
|
end
|
265
255
|
|
266
|
-
def cache
|
256
|
+
def cache(s, p, o)
|
267
257
|
compile_alpha Template.new(s, p, o)
|
268
258
|
end
|
269
259
|
|
270
260
|
# TODO: pick an alpha with fewer candidates to go through
|
271
|
-
def initial_fill
|
261
|
+
def initial_fill(alpha)
|
272
262
|
alpha_top.wmes.each do |wme|
|
273
263
|
alpha.activate wme if wme =~ alpha.template
|
274
264
|
end
|
275
265
|
end
|
276
266
|
|
277
|
-
def remove_production
|
267
|
+
def remove_production(pnode)
|
278
268
|
delete_node_with_ancestors pnode
|
279
269
|
end
|
280
270
|
|
281
|
-
def prepare_query
|
282
|
-
query =
|
271
|
+
def prepare_query(name, conditions, parameters, actions = [])
|
272
|
+
query = queries[name] = BetaMemory.new(nil)
|
283
273
|
query.rete = self
|
284
|
-
query.seed(
|
285
|
-
|
274
|
+
query.seed(parameters.to_h { |param| [param, nil] })
|
275
|
+
results[name] = build_production query, conditions, parameters, actions, true
|
286
276
|
end
|
287
277
|
|
288
|
-
def execute
|
289
|
-
beta =
|
278
|
+
def execute(name, valuations)
|
279
|
+
beta = queries[name]
|
290
280
|
raise Error, "Undefined query #{name}; known queries are #{queries.keys}" unless beta
|
281
|
+
|
291
282
|
beta.subst valuations
|
292
283
|
end
|
293
284
|
|
@@ -295,19 +286,19 @@ module Wongi::Engine
|
|
295
286
|
"<Rete>"
|
296
287
|
end
|
297
288
|
|
298
|
-
def exists?
|
289
|
+
def exists?(wme)
|
299
290
|
find(wme.subject, wme.predicate, wme.object)
|
300
291
|
end
|
301
292
|
|
302
|
-
def each
|
293
|
+
def each(*args, &block)
|
303
294
|
template = case args.length
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
295
|
+
when 0
|
296
|
+
Template.new(:_, :_, :_)
|
297
|
+
when 3
|
298
|
+
Template.new(*args)
|
299
|
+
else
|
300
|
+
raise Error, "Network#each expect a template or nothing at all"
|
301
|
+
end
|
311
302
|
source = best_alpha(template)
|
312
303
|
matching = current_overlay.wmes(source).select { |wme| wme =~ template }
|
313
304
|
if block_given?
|
@@ -317,17 +308,15 @@ module Wongi::Engine
|
|
317
308
|
end
|
318
309
|
end
|
319
310
|
|
320
|
-
def select
|
311
|
+
def select(s, p, o)
|
321
312
|
template = Template.new(s, p, o)
|
322
313
|
source = best_alpha(template)
|
323
314
|
matching = current_overlay.wmes(source).select { |wme| wme =~ template }
|
324
|
-
if block_given?
|
325
|
-
matching.each { |st| yield st.subject, st.predicate, st.object }
|
326
|
-
end
|
315
|
+
matching.each { |st| yield st.subject, st.predicate, st.object } if block_given?
|
327
316
|
matching
|
328
317
|
end
|
329
318
|
|
330
|
-
def find
|
319
|
+
def find(s, p, o)
|
331
320
|
template = Template.new(s, p, o)
|
332
321
|
source = best_alpha(template)
|
333
322
|
# puts "looking for #{template} among #{source.wmes.size} triples of #{source.template}"
|
@@ -336,7 +325,6 @@ module Wongi::Engine
|
|
336
325
|
|
337
326
|
protected
|
338
327
|
|
339
|
-
|
340
328
|
def in_snapshot
|
341
329
|
@in_snapshot = true
|
342
330
|
yield
|
@@ -348,31 +336,33 @@ module Wongi::Engine
|
|
348
336
|
"rule_#{productions.length}"
|
349
337
|
end
|
350
338
|
|
351
|
-
def alphas_for
|
352
|
-
s
|
339
|
+
def alphas_for(wme)
|
340
|
+
s = wme.subject
|
341
|
+
p = wme.predicate
|
342
|
+
o = wme.object
|
353
343
|
[
|
354
|
-
lookup(
|
355
|
-
lookup(
|
356
|
-
lookup(
|
357
|
-
lookup(:_,
|
358
|
-
lookup(
|
359
|
-
lookup(:_,
|
360
|
-
lookup(:_, :_,
|
344
|
+
lookup(s, p, o),
|
345
|
+
lookup(s, p, :_),
|
346
|
+
lookup(s, :_, o),
|
347
|
+
lookup(:_, p, o),
|
348
|
+
lookup(s, :_, :_),
|
349
|
+
lookup(:_, p, :_),
|
350
|
+
lookup(:_, :_, o),
|
361
351
|
lookup(:_, :_, :_),
|
362
352
|
].compact!.tap(&:uniq!)
|
363
353
|
end
|
364
354
|
|
365
|
-
def lookup
|
355
|
+
def lookup(s, p, o)
|
366
356
|
key = Template.hash_for(s, p, o)
|
367
357
|
# puts "Lookup for #{key}"
|
368
|
-
|
358
|
+
alpha_hash[key]
|
369
359
|
end
|
370
360
|
|
371
|
-
def alpha_activate
|
361
|
+
def alpha_activate(alpha, wme)
|
372
362
|
alpha.activate(wme)
|
373
363
|
end
|
374
364
|
|
375
|
-
def best_alpha
|
365
|
+
def best_alpha(template)
|
376
366
|
alpha_hash.inject(nil) do |best, (_, alpha)|
|
377
367
|
if template =~ alpha.template && (best.nil? || alpha.size < best.size)
|
378
368
|
alpha
|
@@ -382,7 +372,7 @@ module Wongi::Engine
|
|
382
372
|
end
|
383
373
|
end
|
384
374
|
|
385
|
-
def build_production
|
375
|
+
def build_production(root, conditions, parameters, actions, alpha_deaf)
|
386
376
|
compiler = Compiler.new(self, root, conditions, parameters, alpha_deaf)
|
387
377
|
ProductionNode.new(compiler.compile, actions).tap do |production|
|
388
378
|
production.compilation_context = compiler
|
@@ -390,27 +380,18 @@ module Wongi::Engine
|
|
390
380
|
end
|
391
381
|
end
|
392
382
|
|
393
|
-
def delete_node_with_ancestors
|
383
|
+
def delete_node_with_ancestors(node)
|
384
|
+
delete_node_with_ancestors node.partner if node.is_a?(NccNode)
|
394
385
|
|
395
|
-
|
396
|
-
|
397
|
-
end
|
386
|
+
# the root node should not be deleted
|
387
|
+
return unless node.parent
|
398
388
|
|
399
|
-
if [BetaMemory, NegNode, NccNode, NccPartner].any? { |
|
400
|
-
while node.tokens.first
|
401
|
-
node.tokens.first.delete
|
402
|
-
end
|
403
|
-
end
|
404
|
-
|
405
|
-
if node.parent
|
406
|
-
node.parent.children.delete node
|
407
|
-
if node.parent.children.empty?
|
408
|
-
delete_node_with_ancestors(node.parent)
|
409
|
-
end
|
389
|
+
if [BetaMemory, NegNode, NccNode, NccPartner].any? { |klass| node.is_a? klass }
|
390
|
+
node.tokens.first.destroy while node.tokens.first
|
410
391
|
end
|
411
392
|
|
393
|
+
node.parent.children.delete node
|
394
|
+
delete_node_with_ancestors(node.parent) if node.parent.children.empty?
|
412
395
|
end
|
413
|
-
|
414
396
|
end
|
415
|
-
|
416
397
|
end
|
data/lib/wongi-engine/ruleset.rb
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
module Wongi
|
2
2
|
module Engine
|
3
3
|
class Ruleset
|
4
|
-
|
5
4
|
class << self
|
5
|
+
def [](name)
|
6
|
+
raise Error, "undefined ruleset #{name}" unless rulesets.key?(name)
|
6
7
|
|
7
|
-
|
8
|
-
raise Error, "undefined ruleset #{name}" unless rulesets.has_key?( name )
|
9
|
-
rulesets[ name ]
|
8
|
+
rulesets[name]
|
10
9
|
end
|
11
10
|
|
12
|
-
def register
|
13
|
-
raise Error, "ruleset #{name} already exists" if rulesets.
|
14
|
-
|
11
|
+
def register(name, ruleset)
|
12
|
+
raise Error, "ruleset #{name} already exists" if rulesets.key?(name)
|
13
|
+
|
14
|
+
rulesets[name] = ruleset
|
15
15
|
end
|
16
16
|
|
17
17
|
def rulesets
|
@@ -19,31 +19,30 @@ module Wongi
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def reset
|
22
|
-
@rulesets = {
|
22
|
+
@rulesets = {}
|
23
23
|
end
|
24
|
-
|
25
24
|
end
|
26
25
|
|
27
|
-
def initialize
|
26
|
+
def initialize(name = nil)
|
28
27
|
@rules = []
|
29
|
-
self.name(
|
28
|
+
self.name(name) if name
|
30
29
|
end
|
31
30
|
|
32
31
|
def inspect
|
33
32
|
"<Ruleset #{name}>"
|
34
33
|
end
|
35
34
|
|
36
|
-
def install
|
35
|
+
def install(rete)
|
37
36
|
# puts "Installing ruleset #{name}"
|
38
37
|
@rules.each { |rule| rete << rule }
|
39
38
|
rescue StandardError => e
|
40
|
-
e1 = Error.new "error installing ruleset '#{name||'<unnamed>'}': #{e}"
|
39
|
+
e1 = Error.new "error installing ruleset '#{name || '<unnamed>'}': #{e}"
|
41
40
|
e1.set_backtrace e.backtrace
|
42
41
|
raise e1
|
43
42
|
end
|
44
43
|
|
45
|
-
def name
|
46
|
-
if name &&
|
44
|
+
def name(name = nil)
|
45
|
+
if name && !@name
|
47
46
|
self.class.register name, self
|
48
47
|
@name = name
|
49
48
|
end
|
@@ -55,20 +54,19 @@ module Wongi
|
|
55
54
|
# @uri
|
56
55
|
# end
|
57
56
|
|
58
|
-
def rule
|
57
|
+
def rule(name, &definition)
|
59
58
|
r = DSL::Rule.new name
|
60
|
-
r.instance_eval
|
59
|
+
r.instance_eval(&definition)
|
61
60
|
@rules << r
|
62
61
|
r
|
63
62
|
end
|
64
63
|
|
65
|
-
def query
|
64
|
+
def query(name, &definition)
|
66
65
|
r = DSL::Query.new name
|
67
|
-
r.instance_eval
|
66
|
+
r.instance_eval(&definition)
|
68
67
|
@rules << r
|
69
68
|
r
|
70
69
|
end
|
71
|
-
|
72
70
|
end
|
73
71
|
end
|
74
72
|
end
|
@@ -1,9 +1,8 @@
|
|
1
1
|
module Wongi::Engine
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
def self.variable? thing
|
2
|
+
Template = Struct.new(:subject, :predicate, :object) do
|
3
|
+
def self.variable?(thing)
|
6
4
|
return false unless thing.is_a?(Symbol)
|
5
|
+
|
7
6
|
thing[0] >= 'A' && thing[0] <= 'Z'
|
8
7
|
end
|
9
8
|
|
@@ -15,9 +14,9 @@ module Wongi::Engine
|
|
15
14
|
|
16
15
|
def variables
|
17
16
|
[].tap do |a|
|
18
|
-
a << subject if Template.variable?(
|
19
|
-
a << predicate if Template.variable?(
|
20
|
-
a << object if Template.variable?(
|
17
|
+
a << subject if Template.variable?(subject)
|
18
|
+
a << predicate if Template.variable?(predicate)
|
19
|
+
a << object if Template.variable?(object)
|
21
20
|
end
|
22
21
|
end
|
23
22
|
|
@@ -25,20 +24,20 @@ module Wongi::Engine
|
|
25
24
|
@hash ||= [subject.hash, predicate.hash, object.hash].hash
|
26
25
|
end
|
27
26
|
|
28
|
-
def self.hash_for
|
29
|
-
args.map(
|
27
|
+
def self.hash_for(*args)
|
28
|
+
args.map(&:hash).hash
|
30
29
|
end
|
31
30
|
|
32
|
-
def ==
|
31
|
+
def ==(other)
|
33
32
|
other.is_a?(Template) && subject == other.subject && predicate == other.predicate && object == other.object
|
34
33
|
end
|
35
34
|
|
36
|
-
def =~
|
35
|
+
def =~(template)
|
37
36
|
case template
|
38
37
|
when Template
|
39
|
-
(
|
40
|
-
|
41
|
-
|
38
|
+
(template.subject == :_ || template.subject == subject) &&
|
39
|
+
(template.predicate == :_ || template.predicate == predicate) &&
|
40
|
+
(template.object == :_ || template.object == object)
|
42
41
|
else
|
43
42
|
raise Error, "templates can only match other templates"
|
44
43
|
end
|
@@ -54,25 +53,27 @@ module Wongi::Engine
|
|
54
53
|
|
55
54
|
def resolve!(token)
|
56
55
|
s = if Template.variable?(subject)
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
56
|
+
raise DefinitionError, "unbound variable #{subject} in token #{token}" unless token.has_var?(subject)
|
57
|
+
|
58
|
+
token[subject]
|
59
|
+
else
|
60
|
+
subject
|
61
|
+
end
|
62
62
|
p = if Template.variable?(predicate)
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
63
|
+
raise DefinitionError, "unbound variable #{predicate} in token #{token}" unless token.has_var?(predicate)
|
64
|
+
|
65
|
+
token[predicate]
|
66
|
+
else
|
67
|
+
predicate
|
68
|
+
end
|
68
69
|
o = if Template.variable?(object)
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
70
|
+
raise DefinitionError, "unbound variable #{object} in token #{token}" unless token.has_var?(object)
|
71
|
+
|
72
|
+
token[object]
|
73
|
+
else
|
74
|
+
object
|
75
|
+
end
|
74
76
|
[s, p, o]
|
75
77
|
end
|
76
|
-
|
77
78
|
end
|
78
79
|
end
|