wongi-engine 0.0.16 → 0.0.17

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.
@@ -0,0 +1,37 @@
1
+ module Wongi::Engine
2
+
3
+ class UndefinedBaseRule < StandardError
4
+ def initialize rule_name
5
+ @rule_name = rule_name
6
+ end
7
+
8
+ def message
9
+ "undefined production #@rule_name"
10
+ end
11
+ end
12
+
13
+ class AssumingClause
14
+
15
+ attr_reader :base_rule_name
16
+ attr_reader :base_production
17
+
18
+ def initialize base_rule_name, base_production = nil
19
+ @base_rule_name = base_rule_name
20
+ @base_production = base_production
21
+ end
22
+
23
+ def import_into engine
24
+ base_production = engine.productions[ base_rule_name ]
25
+ raise UndefinedBaseRule.new(base_rule_name) unless base_production
26
+ self.class.new base_rule_name, base_production
27
+ end
28
+
29
+ def compile context
30
+ raise DefinitionError.new("'assuming' cannot be preceded by other matchers") unless context.earlier.empty?
31
+ raise StandardError.new("missing base context") unless base_production.parent.context
32
+ base_production.parent.context.dup
33
+ end
34
+
35
+ end
36
+
37
+ end
@@ -58,10 +58,10 @@ module Wongi::Engine
58
58
  end
59
59
 
60
60
  copy
61
- rescue Exception => e
62
- e1 = Exception.new "in rule #{name}: #{e}"
63
- e1.set_backtrace e.backtrace
64
- raise e1
61
+ # rescue Exception => e
62
+ # e1 = Exception.new "in rule #{name}: #{e}"
63
+ # e1.set_backtrace e.backtrace
64
+ # raise e1
65
65
  end
66
66
 
67
67
  protected
@@ -16,6 +16,7 @@ module Wongi::Engine
16
16
 
17
17
  def compile context
18
18
  context.node = context.node.beta_memory.filter_node( self )
19
+ context.node.context = context
19
20
  context.earlier << self
20
21
  context
21
22
  end
@@ -1,442 +1,443 @@
1
- require 'wongi-engine/network/collectable'
2
- require 'wongi-engine/network/debug'
3
-
4
- module Wongi::Engine
5
- class Network
6
-
7
- attr_reader :alpha_top, :beta_top
8
- attr_reader :queries, :results
9
- attr_reader :productions
10
-
11
- include NetworkParts::Collectable
12
-
13
- protected
14
- attr_accessor :alpha_hash
15
- attr_writer :alpha_top, :beta_top
16
- attr_writer :queries, :results
17
-
18
- public
19
-
20
- def debug!
21
- extend NetworkParts::Debug
22
- end
23
-
24
- def rdf!
25
- if ! defined? Wongi::RDF::DocumentSupport
26
- begin
27
- require 'wongi-rdf'
28
- rescue LoadError => e
29
- raise "'wongi-rdf' is required for RDF support"
30
- end
31
- end
32
-
33
- extend Wongi::RDF::DocumentSupport
34
- class << self
35
- def statements
36
- alpha_top.wmes
37
- end
38
- end
39
-
40
- @namespaces = { }
41
- @blank_counter = 1
42
- @ns_counter = 0
43
- @used_blanks = { }
44
- end
45
-
46
- def initialize
47
- @timeline = []
48
- self.alpha_top = AlphaMemory.new( Template.new( :_, :_, :_ ), self )
49
- self.alpha_hash = { Template.hash_for( :_, :_, :_ ) => self.alpha_top }
50
- self.beta_top = BetaMemory.new(nil)
51
- self.beta_top.rete = self
52
- self.beta_top.seed
53
- self.queries = {}
54
- self.results = {}
55
- @cache = {}
56
- @revns = {}
57
- @contexts = {}
58
-
59
- @productions = { }
60
-
61
- @collectors = {}
62
- @collectors[:error] = []
63
-
64
- end
65
-
66
- def dump
67
- beta_top.dump
68
- end
69
-
70
- def alphas
71
- alpha_hash.values
72
- end
73
-
74
- def import thing
75
- case thing
76
- when String, Numeric, TrueClass, FalseClass, NilClass, Wongi::RDF::Node
77
- thing
78
- when Symbol
79
- thing
80
- else
81
- thing
82
- end
83
- end
84
-
85
- def assert wme
86
-
87
- unless wme.rete == self
88
- wme = wme.import_into self
89
- end
90
-
91
- if @current_context
92
- @current_context.asserted_wmes << wme
93
- wme.context = @current_context
94
- end
95
-
96
- return if @cache.has_key?(wme)
97
-
98
- # puts "ASSERTING #{wme}"
99
- @cache[wme] = wme
100
-
101
- s = wme.subject
102
- p = wme.predicate
103
- o = wme.object
104
-
105
- alpha_activate(lookup( s, p, o), wme)
106
- alpha_activate(lookup( s, p, :_), wme)
107
- alpha_activate(lookup( s, :_, o), wme)
108
- alpha_activate(lookup(:_, p, o), wme)
109
- alpha_activate(lookup( s, :_, :_), wme)
110
- alpha_activate(lookup(:_, p, :_), wme)
111
- alpha_activate(lookup(:_, :_, o), wme)
112
- alpha_activate(lookup(:_, :_, :_), wme)
113
-
114
- wme
115
- end
116
-
117
- def wmes
118
- alpha_top.wmes
119
- end
120
- alias_method :statements, :wmes
121
- alias_method :facts, :wmes
122
-
123
- def in_snapshot?
124
- @in_snapshot
125
- end
126
-
127
- def snapshot!
128
- @timeline.each_with_index do |slice, index|
129
- source = if index == @timeline.size - 1
130
- alpha_hash
131
- else
132
- @timeline[index+1]
133
- end
134
- # puts "source = #{source}"
135
- wmes = {}
136
- slice.each { |key, alpha| wmes[key] = alpha.wmes }
137
- slice.each do |key, alpha|
138
- in_snapshot {
139
- wmes[key].dup.each { |wme| wme.destroy }
140
- }
141
- alpha.snapshot! source[key]
142
- end
143
- end
144
- end
145
-
146
- def rule name = nil, &block
147
- r = ProductionRule.new( name || generate_rule_name )
148
- r.instance_eval &block
149
- self << r
150
- end
151
-
152
- def query name, &block
153
- q = Query.new name
154
- q.instance_eval &block
155
- self << q
156
- end
157
-
158
- def << something
159
- case something
160
- when Array
161
- if something.length == 3
162
- assert WME.new( *something )
163
- else
164
- raise "Arrays must have 3 elements"
165
- end
166
- when ProductionRule
167
- derived = something.import_into self
168
- production = add_production derived.conditions, derived.actions
169
- if something.name
170
- productions[ something.name ] = production
171
- end
172
- when Query
173
- derived = something.import_into self
174
- prepare_query derived.name, derived.conditions, derived.parameters, derived.actions
175
- when Ruleset
176
- something.install self
177
- when WME
178
- assert something
179
- when Wongi::RDF::Statement
180
- assert WME.new( something.subject, something.predicate, something.object, self )
181
- #when Wongi::RDF::Document
182
- # something.statements.each do |st|
183
- # assert WME.new( st.subject, st.predicate, st.object, self )
184
- # end
185
- when Network
186
- something.each do |st|
187
- assert st.import_into( self )
188
- end
189
- else
190
- raise "I don't know how to accept a #{something.class}"
191
- end
192
- end
193
-
194
- def retract wme, is_real = false
195
-
196
- if wme.is_a? Array
197
- return retract( WME.new(*wme), is_real )
198
- end
199
-
200
- if ! is_real
201
- if @current_context
202
- @current_context.retracted_wmes << wme
203
- end
204
- end
205
-
206
- real = if is_real
207
- wme
208
- else
209
- #find(wme.subject, wme.predicate, wme.object)
210
- @cache[wme]
211
- end
212
-
213
- return false if real.nil?
214
- @cache.delete(real)
215
- raise "Cannot retract inferred statements" unless real.manual?
216
-
217
- real.destroy
218
-
219
- true
220
-
221
- end
222
-
223
- def compile_alpha condition
224
- template = Template.new :_, :_, :_
225
- time = condition.time
226
-
227
- template.subject = condition.subject unless Template.variable?( condition.subject )
228
- template.predicate = condition.predicate unless Template.variable?( condition.predicate )
229
- template.object = condition.object unless Template.variable?( condition.object )
230
-
231
- hash = template.hash
232
- # puts "COMPILED CONDITION #{condition} WITH KEY #{key}"
233
- if time == 0
234
- return self.alpha_hash[ hash ] if self.alpha_hash.has_key?( hash )
235
- else
236
- return @timeline[time+1][ hash ] if @timeline[time+1] && @timeline[time+1].has_key?( hash )
237
- end
238
-
239
- alpha = AlphaMemory.new( template, self )
240
-
241
- if time == 0
242
- self.alpha_hash[ hash ] = alpha
243
- initial_fill alpha
244
- else
245
- if @timeline[time+1].nil?
246
- # => ensure lineage from 0 to time
247
- compile_alpha condition.class.new(condition.subject, condition.predicate, condition.object, time: time + 1)
248
- @timeline.unshift Hash.new
249
- end
250
- @timeline[time+1][ hash ] = alpha
251
- end
252
- alpha
253
- end
254
-
255
- def cache s, p, o
256
- compile_alpha Template.new(s, p, o).import_into( self )
257
- end
258
-
259
- def initial_fill alpha
260
- tpl = alpha.template
261
- source = more_generic_alpha(tpl)
262
- # puts "more efficient by #{alpha_top.wmes.size - source.wmes.size}" unless source ==
263
- # alpha_top
264
- source.wmes.each do |wme|
265
- alpha.activate wme if wme =~ tpl
266
- end
267
- end
268
-
269
- def add_production conditions, actions = []
270
- real_add_production self.beta_top, conditions, [], actions, false
271
- end
272
-
273
- def remove_production pnode
274
- delete_node_with_ancestors pnode
275
- end
276
-
277
- def prepare_query name, conditions, parameters, actions = []
278
- query = self.queries[ name ] = BetaMemory.new( nil )
279
- query.rete = self
280
- transformed = {}
281
- parameters.each { |param| transformed[param] = nil }
282
- query.seed transformed
283
- self.results[ name ] = real_add_production query, conditions, parameters, actions, true
284
- end
285
-
286
- def execute name, valuations
287
- beta = self.queries[name]
288
- raise "Undefined query #{name}; known queries are #{queries.keys}" unless beta
289
- beta.subst valuations
290
- end
291
-
292
- def inspect
293
- "<Rete>"
294
- end
295
-
296
- def context= name
297
- if name && !@contexts.has_key?(name)
298
- @current_context = (@contexts[name] ||= ModelContext.new name)
299
- end
300
- end
301
-
302
- def retract_context name
303
- return unless @contexts.has_key?(name)
304
-
305
- if @current_context && @current_context.name == name
306
- @current_context = nil
307
- end
308
- ctx = @contexts[name]
309
- ctx.asserted_wmes.select { |wme| wme.generating_tokens.empty? }.each { |wme| retract(wme, true) }
310
- ctx.retracted_wmes.each { |wme| assert(wme) }
311
- @contexts.delete name
312
- end
313
-
314
- def exists? wme
315
- @cache[ wme ]
316
- end
317
-
318
- def each *args
319
- return unless block_given?
320
- unless args.length == 0 || args.length == 3
321
- raise "Document#each expects a pattern or nothing at all"
322
- end
323
- s, p, o = if args.empty?
324
- [:_, :_, :_]
325
- else
326
- args
327
- end
328
- no_check = s == :_ && p == :_ && o == :_
329
- template = Template.new(s, p, o).import_into self
330
- alpha_top.wmes.each do |wme|
331
- yield wme if (no_check || wme =~ template)
332
- end
333
- end
334
-
335
- def select s, p, o
336
- template = Template.new(s, p, o).import_into self
337
- matching = alpha_top.wmes.select { |wme| wme =~ template }
338
- if block_given?
339
- matching.each { |st| yield st.subject, st.predicate, st.object }
340
- end
341
- matching
342
- end
343
-
344
- def find s, p, o
345
- template = Template.new(s, p, o).import_into self
346
- source = best_alpha(template)
347
- # puts "looking for #{template} among #{source.wmes.size} triples of #{source.template}"
348
- source.wmes.detect { |wme| wme =~ template }
349
- end
350
-
351
- protected
352
-
353
- def in_snapshot
354
- @in_snapshot = true
355
- yield
356
- ensure
357
- @in_snapshot = false
358
- end
359
-
360
- def generate_rule_name
361
- "rule_#{productions.length}"
362
- end
363
-
364
- def lookup s, p, o
365
- key = Template.hash_for(s, p, o)
366
- # puts "Lookup for #{key}"
367
- self.alpha_hash[ key ]
368
- end
369
-
370
- def alpha_activate alpha, wme
371
- alpha.activate(wme) if alpha
372
- end
373
-
374
- def more_generic_alpha template
375
- return alpha_top # OPTIMISE => temporary; may use later or not use at all
376
- return alpha_top if template.root?
377
- more_generic_templates(template).reduce alpha_top do |best, template|
378
- alpha = alpha_hash[template.hash]
379
- if alpha && alpha.wmes.size < best.wmes.size
380
- alpha
381
- else
382
- best
383
- end
384
- end
385
- end
386
-
387
- def more_generic_templates template
388
- set = []
389
- set << template.with_subject( :_ ) unless template.subject == :_
390
- set << template.with_predicate( :_ ) unless template.predicate == :_
391
- set << template.with_object( :_ ) unless template.object == :_
392
- set.select { |item| not item.root? }
393
- end
394
-
395
- def best_alpha template
396
- candidates = alpha_hash.values.select do |alpha|
397
- template =~ alpha.template
398
- end
399
- result = candidates.inject do |best, alpha|
400
- if best.nil?
401
- alpha
402
- elsif alpha.wmes.to_a.length < best.wmes.to_a.length
403
- alpha
404
- else
405
- best
406
- end
407
- end
408
- result
409
- end
410
-
411
- def real_add_production root, conditions, parameters, actions, alpha_deaf
412
- beta = root.network conditions, [], parameters, alpha_deaf
413
-
414
- production = ProductionNode.new( beta, actions )
415
- production.refresh
416
- production
417
- end
418
-
419
- def delete_node_with_ancestors node
420
-
421
- if node.kind_of?( NccNode )
422
- delete_node_with_ancestors node.partner
423
- end
424
-
425
- if [BetaMemory, NegNode, NccNode, NccPartner].any? { | klass| node.kind_of? klass }
426
- while node.tokens.first
427
- node.tokens.first.delete
428
- end
429
- end
430
-
431
- if node.parent
432
- node.parent.children.delete node
433
- if node.parent.children.empty?
434
- delete_node_with_ancestors(node.parent)
435
- end
436
- end
437
-
438
- end
439
-
440
- end
441
-
442
- end
1
+ require 'wongi-engine/network/collectable'
2
+ require 'wongi-engine/network/debug'
3
+
4
+ module Wongi::Engine
5
+ class Network
6
+
7
+ attr_reader :alpha_top, :beta_top
8
+ attr_reader :queries, :results
9
+ attr_reader :productions
10
+
11
+ include NetworkParts::Collectable
12
+
13
+ protected
14
+ attr_accessor :alpha_hash
15
+ attr_writer :alpha_top, :beta_top
16
+ attr_writer :queries, :results
17
+
18
+ public
19
+
20
+ def debug!
21
+ extend NetworkParts::Debug
22
+ end
23
+
24
+ def rdf!
25
+ if ! defined? Wongi::RDF::DocumentSupport
26
+ begin
27
+ require 'wongi-rdf'
28
+ rescue LoadError => e
29
+ raise "'wongi-rdf' is required for RDF support"
30
+ end
31
+ end
32
+
33
+ extend Wongi::RDF::DocumentSupport
34
+ class << self
35
+ def statements
36
+ alpha_top.wmes
37
+ end
38
+ end
39
+
40
+ @namespaces = { }
41
+ @blank_counter = 1
42
+ @ns_counter = 0
43
+ @used_blanks = { }
44
+ end
45
+
46
+ def initialize
47
+ @timeline = []
48
+ self.alpha_top = AlphaMemory.new( Template.new( :_, :_, :_ ), self )
49
+ self.alpha_hash = { Template.hash_for( :_, :_, :_ ) => self.alpha_top }
50
+ self.beta_top = BetaMemory.new(nil)
51
+ self.beta_top.rete = self
52
+ self.beta_top.seed
53
+ self.queries = {}
54
+ self.results = {}
55
+ @cache = {}
56
+ @revns = {}
57
+ @contexts = {}
58
+
59
+ @productions = { }
60
+
61
+ @collectors = {}
62
+ @collectors[:error] = []
63
+
64
+ end
65
+
66
+ def dump
67
+ beta_top.dump
68
+ end
69
+
70
+ def alphas
71
+ alpha_hash.values
72
+ end
73
+
74
+ def import thing
75
+ case thing
76
+ when String, Numeric, TrueClass, FalseClass, NilClass, Wongi::RDF::Node
77
+ thing
78
+ when Symbol
79
+ thing
80
+ else
81
+ thing
82
+ end
83
+ end
84
+
85
+ def assert wme
86
+
87
+ unless wme.rete == self
88
+ wme = wme.import_into self
89
+ end
90
+
91
+ if @current_context
92
+ @current_context.asserted_wmes << wme
93
+ wme.context = @current_context
94
+ end
95
+
96
+ return if @cache.has_key?(wme)
97
+
98
+ # puts "ASSERTING #{wme}"
99
+ @cache[wme] = wme
100
+
101
+ s = wme.subject
102
+ p = wme.predicate
103
+ o = wme.object
104
+
105
+ alpha_activate(lookup( s, p, o), wme)
106
+ alpha_activate(lookup( s, p, :_), wme)
107
+ alpha_activate(lookup( s, :_, o), wme)
108
+ alpha_activate(lookup(:_, p, o), wme)
109
+ alpha_activate(lookup( s, :_, :_), wme)
110
+ alpha_activate(lookup(:_, p, :_), wme)
111
+ alpha_activate(lookup(:_, :_, o), wme)
112
+ alpha_activate(lookup(:_, :_, :_), wme)
113
+
114
+ wme
115
+ end
116
+
117
+ def wmes
118
+ alpha_top.wmes
119
+ end
120
+ alias_method :statements, :wmes
121
+ alias_method :facts, :wmes
122
+
123
+ def in_snapshot?
124
+ @in_snapshot
125
+ end
126
+
127
+ def snapshot!
128
+ @timeline.each_with_index do |slice, index|
129
+ source = if index == @timeline.size - 1
130
+ alpha_hash
131
+ else
132
+ @timeline[index+1]
133
+ end
134
+ # puts "source = #{source}"
135
+ wmes = {}
136
+ slice.each { |key, alpha| wmes[key] = alpha.wmes }
137
+ slice.each do |key, alpha|
138
+ in_snapshot {
139
+ wmes[key].dup.each { |wme| wme.destroy }
140
+ }
141
+ alpha.snapshot! source[key]
142
+ end
143
+ end
144
+ end
145
+
146
+ def rule name = nil, &block
147
+ r = ProductionRule.new( name || generate_rule_name )
148
+ r.instance_eval &block
149
+ self << r
150
+ end
151
+
152
+ def query name, &block
153
+ q = Query.new name
154
+ q.instance_eval &block
155
+ self << q
156
+ end
157
+
158
+ def << something
159
+ case something
160
+ when Array
161
+ if something.length == 3
162
+ assert WME.new( *something )
163
+ else
164
+ raise "Arrays must have 3 elements"
165
+ end
166
+ when ProductionRule
167
+ derived = something.import_into self
168
+ production = add_production derived.conditions, derived.actions
169
+ if something.name
170
+ productions[ something.name ] = production
171
+ end
172
+ production
173
+ when Query
174
+ derived = something.import_into self
175
+ prepare_query derived.name, derived.conditions, derived.parameters, derived.actions
176
+ when Ruleset
177
+ something.install self
178
+ when WME
179
+ assert something
180
+ when Wongi::RDF::Statement
181
+ assert WME.new( something.subject, something.predicate, something.object, self )
182
+ #when Wongi::RDF::Document
183
+ # something.statements.each do |st|
184
+ # assert WME.new( st.subject, st.predicate, st.object, self )
185
+ # end
186
+ when Network
187
+ something.each do |st|
188
+ assert st.import_into( self )
189
+ end
190
+ else
191
+ raise "I don't know how to accept a #{something.class}"
192
+ end
193
+ end
194
+
195
+ def retract wme, is_real = false
196
+
197
+ if wme.is_a? Array
198
+ return retract( WME.new(*wme), is_real )
199
+ end
200
+
201
+ if ! is_real
202
+ if @current_context
203
+ @current_context.retracted_wmes << wme
204
+ end
205
+ end
206
+
207
+ real = if is_real
208
+ wme
209
+ else
210
+ #find(wme.subject, wme.predicate, wme.object)
211
+ @cache[wme]
212
+ end
213
+
214
+ return false if real.nil?
215
+ raise "Cannot retract inferred statements" unless real.manual?
216
+ @cache.delete(real)
217
+
218
+ real.destroy
219
+
220
+ true
221
+
222
+ end
223
+
224
+ def compile_alpha condition
225
+ template = Template.new :_, :_, :_
226
+ time = condition.time
227
+
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 )
231
+
232
+ hash = template.hash
233
+ # puts "COMPILED CONDITION #{condition} WITH KEY #{key}"
234
+ if time == 0
235
+ return self.alpha_hash[ hash ] if self.alpha_hash.has_key?( hash )
236
+ else
237
+ return @timeline[time+1][ hash ] if @timeline[time+1] && @timeline[time+1].has_key?( hash )
238
+ end
239
+
240
+ alpha = AlphaMemory.new( template, self )
241
+
242
+ if time == 0
243
+ self.alpha_hash[ hash ] = alpha
244
+ initial_fill alpha
245
+ else
246
+ if @timeline[time+1].nil?
247
+ # => ensure lineage from 0 to time
248
+ compile_alpha condition.class.new(condition.subject, condition.predicate, condition.object, time: time + 1)
249
+ @timeline.unshift Hash.new
250
+ end
251
+ @timeline[time+1][ hash ] = alpha
252
+ end
253
+ alpha
254
+ end
255
+
256
+ def cache s, p, o
257
+ compile_alpha Template.new(s, p, o).import_into( self )
258
+ end
259
+
260
+ def initial_fill alpha
261
+ tpl = alpha.template
262
+ source = more_generic_alpha(tpl)
263
+ # puts "more efficient by #{alpha_top.wmes.size - source.wmes.size}" unless source ==
264
+ # alpha_top
265
+ source.wmes.each do |wme|
266
+ alpha.activate wme if wme =~ tpl
267
+ end
268
+ end
269
+
270
+ def add_production conditions, actions = []
271
+ real_add_production self.beta_top, conditions, [], actions, false
272
+ end
273
+
274
+ def remove_production pnode
275
+ delete_node_with_ancestors pnode
276
+ end
277
+
278
+ def prepare_query name, conditions, parameters, actions = []
279
+ query = self.queries[ name ] = BetaMemory.new( nil )
280
+ query.rete = self
281
+ transformed = {}
282
+ parameters.each { |param| transformed[param] = nil }
283
+ query.seed transformed
284
+ self.results[ name ] = real_add_production query, conditions, parameters, actions, true
285
+ end
286
+
287
+ def execute name, valuations
288
+ beta = self.queries[name]
289
+ raise "Undefined query #{name}; known queries are #{queries.keys}" unless beta
290
+ beta.subst valuations
291
+ end
292
+
293
+ def inspect
294
+ "<Rete>"
295
+ end
296
+
297
+ def context= name
298
+ if name && !@contexts.has_key?(name)
299
+ @current_context = (@contexts[name] ||= ModelContext.new name)
300
+ end
301
+ end
302
+
303
+ def retract_context name
304
+ return unless @contexts.has_key?(name)
305
+
306
+ if @current_context && @current_context.name == name
307
+ @current_context = nil
308
+ end
309
+ ctx = @contexts[name]
310
+ ctx.asserted_wmes.select { |wme| wme.generating_tokens.empty? }.each { |wme| retract(wme, true) }
311
+ ctx.retracted_wmes.each { |wme| assert(wme) }
312
+ @contexts.delete name
313
+ end
314
+
315
+ def exists? wme
316
+ @cache[ wme ]
317
+ end
318
+
319
+ def each *args
320
+ return unless block_given?
321
+ unless args.length == 0 || args.length == 3
322
+ raise "Document#each expects a pattern or nothing at all"
323
+ end
324
+ s, p, o = if args.empty?
325
+ [:_, :_, :_]
326
+ else
327
+ args
328
+ end
329
+ no_check = s == :_ && p == :_ && o == :_
330
+ template = Template.new(s, p, o).import_into self
331
+ alpha_top.wmes.each do |wme|
332
+ yield wme if (no_check || wme =~ template)
333
+ end
334
+ end
335
+
336
+ def select s, p, o
337
+ template = Template.new(s, p, o).import_into self
338
+ matching = alpha_top.wmes.select { |wme| wme =~ template }
339
+ if block_given?
340
+ matching.each { |st| yield st.subject, st.predicate, st.object }
341
+ end
342
+ matching
343
+ end
344
+
345
+ def find s, p, o
346
+ template = Template.new(s, p, o).import_into self
347
+ source = best_alpha(template)
348
+ # puts "looking for #{template} among #{source.wmes.size} triples of #{source.template}"
349
+ source.wmes.detect { |wme| wme =~ template }
350
+ end
351
+
352
+ protected
353
+
354
+ def in_snapshot
355
+ @in_snapshot = true
356
+ yield
357
+ ensure
358
+ @in_snapshot = false
359
+ end
360
+
361
+ def generate_rule_name
362
+ "rule_#{productions.length}"
363
+ end
364
+
365
+ def lookup s, p, o
366
+ key = Template.hash_for(s, p, o)
367
+ # puts "Lookup for #{key}"
368
+ self.alpha_hash[ key ]
369
+ end
370
+
371
+ def alpha_activate alpha, wme
372
+ alpha.activate(wme) if alpha
373
+ end
374
+
375
+ def more_generic_alpha template
376
+ return alpha_top # OPTIMISE => temporary; may use later or not use at all
377
+ return alpha_top if template.root?
378
+ more_generic_templates(template).reduce alpha_top do |best, template|
379
+ alpha = alpha_hash[template.hash]
380
+ if alpha && alpha.wmes.size < best.wmes.size
381
+ alpha
382
+ else
383
+ best
384
+ end
385
+ end
386
+ end
387
+
388
+ def more_generic_templates template
389
+ set = []
390
+ set << template.with_subject( :_ ) unless template.subject == :_
391
+ set << template.with_predicate( :_ ) unless template.predicate == :_
392
+ set << template.with_object( :_ ) unless template.object == :_
393
+ set.select { |item| not item.root? }
394
+ end
395
+
396
+ def best_alpha template
397
+ candidates = alpha_hash.values.select do |alpha|
398
+ template =~ alpha.template
399
+ end
400
+ result = candidates.inject do |best, alpha|
401
+ if best.nil?
402
+ alpha
403
+ elsif alpha.wmes.to_a.length < best.wmes.to_a.length
404
+ alpha
405
+ else
406
+ best
407
+ end
408
+ end
409
+ result
410
+ end
411
+
412
+ def real_add_production root, conditions, parameters, actions, alpha_deaf
413
+ beta = root.network conditions, [], parameters, alpha_deaf
414
+
415
+ production = ProductionNode.new( beta, actions )
416
+ production.refresh
417
+ production
418
+ end
419
+
420
+ def delete_node_with_ancestors node
421
+
422
+ if node.kind_of?( NccNode )
423
+ delete_node_with_ancestors node.partner
424
+ end
425
+
426
+ if [BetaMemory, NegNode, NccNode, NccPartner].any? { | klass| node.kind_of? klass }
427
+ while node.tokens.first
428
+ node.tokens.first.delete
429
+ end
430
+ end
431
+
432
+ if node.parent
433
+ node.parent.children.delete node
434
+ if node.parent.children.empty?
435
+ delete_node_with_ancestors(node.parent)
436
+ end
437
+ end
438
+
439
+ end
440
+
441
+ end
442
+
443
+ end