wongi-engine 0.0.16 → 0.0.17

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