mort666-wongi-engine 0.2.9

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.
Files changed (93) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +20 -0
  3. data/.hgignore +6 -0
  4. data/.hgtags +13 -0
  5. data/.ruby-gemset +1 -0
  6. data/.travis.yml +19 -0
  7. data/CHANGELOG.md +106 -0
  8. data/Gemfile +4 -0
  9. data/LICENSE +22 -0
  10. data/README.md +27 -0
  11. data/Rakefile +9 -0
  12. data/examples/ex01.rb +23 -0
  13. data/examples/ex02.rb +37 -0
  14. data/examples/graphviz.rb +16 -0
  15. data/examples/rdf.n3 +6 -0
  16. data/examples/rdf.rb +14 -0
  17. data/examples/timeline.rb +48 -0
  18. data/lib/wongi-engine.rb +36 -0
  19. data/lib/wongi-engine/alpha_memory.rb +60 -0
  20. data/lib/wongi-engine/beta.rb +11 -0
  21. data/lib/wongi-engine/beta/assignment_node.rb +40 -0
  22. data/lib/wongi-engine/beta/beta_memory.rb +49 -0
  23. data/lib/wongi-engine/beta/beta_node.rb +94 -0
  24. data/lib/wongi-engine/beta/filter_node.rb +48 -0
  25. data/lib/wongi-engine/beta/join_node.rb +140 -0
  26. data/lib/wongi-engine/beta/ncc_node.rb +67 -0
  27. data/lib/wongi-engine/beta/ncc_partner.rb +40 -0
  28. data/lib/wongi-engine/beta/neg_node.rb +115 -0
  29. data/lib/wongi-engine/beta/optional_node.rb +142 -0
  30. data/lib/wongi-engine/beta/or_node.rb +37 -0
  31. data/lib/wongi-engine/beta/production_node.rb +31 -0
  32. data/lib/wongi-engine/compiler.rb +115 -0
  33. data/lib/wongi-engine/core_ext.rb +63 -0
  34. data/lib/wongi-engine/data_overlay.rb +144 -0
  35. data/lib/wongi-engine/dsl.rb +132 -0
  36. data/lib/wongi-engine/dsl/action/base.rb +11 -0
  37. data/lib/wongi-engine/dsl/action/error_generator.rb +31 -0
  38. data/lib/wongi-engine/dsl/action/simple_action.rb +60 -0
  39. data/lib/wongi-engine/dsl/action/simple_collector.rb +52 -0
  40. data/lib/wongi-engine/dsl/action/statement_generator.rb +46 -0
  41. data/lib/wongi-engine/dsl/action/trace_action.rb +49 -0
  42. data/lib/wongi-engine/dsl/any_rule.rb +33 -0
  43. data/lib/wongi-engine/dsl/assuming.rb +31 -0
  44. data/lib/wongi-engine/dsl/builder.rb +44 -0
  45. data/lib/wongi-engine/dsl/clause/assign.rb +15 -0
  46. data/lib/wongi-engine/dsl/clause/fact.rb +71 -0
  47. data/lib/wongi-engine/dsl/clause/gen.rb +17 -0
  48. data/lib/wongi-engine/dsl/clause/generic.rb +38 -0
  49. data/lib/wongi-engine/dsl/generated.rb +43 -0
  50. data/lib/wongi-engine/dsl/ncc_subrule.rb +17 -0
  51. data/lib/wongi-engine/dsl/query.rb +24 -0
  52. data/lib/wongi-engine/dsl/rule.rb +84 -0
  53. data/lib/wongi-engine/enumerators.rb +21 -0
  54. data/lib/wongi-engine/error.rb +22 -0
  55. data/lib/wongi-engine/filter.rb +6 -0
  56. data/lib/wongi-engine/filter/asserting_test.rb +20 -0
  57. data/lib/wongi-engine/filter/equality_test.rb +36 -0
  58. data/lib/wongi-engine/filter/filter_test.rb +18 -0
  59. data/lib/wongi-engine/filter/greater_than_test.rb +36 -0
  60. data/lib/wongi-engine/filter/inequality_test.rb +36 -0
  61. data/lib/wongi-engine/filter/less_than_test.rb +36 -0
  62. data/lib/wongi-engine/graph.rb +73 -0
  63. data/lib/wongi-engine/network.rb +416 -0
  64. data/lib/wongi-engine/network/collectable.rb +42 -0
  65. data/lib/wongi-engine/network/debug.rb +85 -0
  66. data/lib/wongi-engine/ruleset.rb +74 -0
  67. data/lib/wongi-engine/template.rb +78 -0
  68. data/lib/wongi-engine/token.rb +114 -0
  69. data/lib/wongi-engine/version.rb +5 -0
  70. data/lib/wongi-engine/wme.rb +89 -0
  71. data/lib/wongi-engine/wme_match_data.rb +34 -0
  72. data/spec/beta_node_spec.rb +29 -0
  73. data/spec/bug_specs/issue_4_spec.rb +141 -0
  74. data/spec/dataset_spec.rb +27 -0
  75. data/spec/dsl_spec.rb +9 -0
  76. data/spec/filter_specs/assert_test_spec.rb +102 -0
  77. data/spec/filter_specs/less_test_spec.rb +41 -0
  78. data/spec/generation_spec.rb +116 -0
  79. data/spec/high_level_spec.rb +378 -0
  80. data/spec/network_spec.rb +182 -0
  81. data/spec/overlay_spec.rb +61 -0
  82. data/spec/rule_specs/any_rule_spec.rb +75 -0
  83. data/spec/rule_specs/assign_spec.rb +88 -0
  84. data/spec/rule_specs/assuming_spec.rb +66 -0
  85. data/spec/rule_specs/maybe_rule_spec.rb +101 -0
  86. data/spec/rule_specs/ncc_spec.rb +258 -0
  87. data/spec/rule_specs/negative_rule_spec.rb +105 -0
  88. data/spec/ruleset_spec.rb +54 -0
  89. data/spec/simple_action_spec.rb +40 -0
  90. data/spec/spec_helper.rb +3 -0
  91. data/spec/wme_spec.rb +83 -0
  92. data/wongi-engine.gemspec +40 -0
  93. metadata +212 -0
@@ -0,0 +1,21 @@
1
+ module Wongi::Engine
2
+ class DuplicatingEnumerator
3
+ def self.new(collection)
4
+ Enumerator.new do |y|
5
+ collection.dup.each do |e|
6
+ y << e
7
+ end
8
+ end
9
+ end
10
+ end
11
+ class DeleteSafeEnumerator
12
+ def self.new(collection)
13
+ Enumerator.new do |y|
14
+ collection.dup.each do |e|
15
+ y << e unless e.deleted?
16
+ end
17
+ collection.reject! &:deleted?
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,22 @@
1
+ module Wongi::Engine
2
+
3
+ class Error < StandardError
4
+
5
+ end
6
+
7
+ class DefinitionError < Error
8
+
9
+ end
10
+
11
+ class ReteError
12
+ attr_reader :token, :message
13
+ def initialize token, message, literate
14
+ @token, @message, @literate = token, message, literate
15
+ end
16
+ def literate?
17
+ @literate
18
+ end
19
+ end
20
+
21
+
22
+ end
@@ -0,0 +1,6 @@
1
+ require 'wongi-engine/filter/filter_test'
2
+ require 'wongi-engine/filter/equality_test'
3
+ require 'wongi-engine/filter/inequality_test'
4
+ require 'wongi-engine/filter/asserting_test'
5
+ require 'wongi-engine/filter/less_than_test'
6
+ require 'wongi-engine/filter/greater_than_test'
@@ -0,0 +1,20 @@
1
+ module Wongi::Engine
2
+
3
+ class AssertingTest < FilterTest
4
+
5
+ def initialize *vars, &body
6
+ @vars = vars
7
+ @body = body
8
+ end
9
+
10
+ def passes? token
11
+ if @vars.empty?
12
+ @body.call token
13
+ else
14
+ @body.call *( @vars.map { |var| token[var] } )
15
+ end
16
+ end
17
+
18
+ end
19
+
20
+ end
@@ -0,0 +1,36 @@
1
+ module Wongi::Engine
2
+
3
+ class EqualityTest < FilterTest
4
+
5
+ attr_reader :x, :y
6
+
7
+ def initialize x, y
8
+ @x, @y = x, y
9
+ end
10
+
11
+ def passes? token
12
+
13
+ x = if Template.variable? @x
14
+ token[@x]
15
+ else
16
+ @x
17
+ end
18
+
19
+ y = if Template.variable? @y
20
+ token[@y]
21
+ else
22
+ @y
23
+ end
24
+
25
+ return false if x == :_ || y == :_
26
+ return x == y
27
+
28
+ end
29
+
30
+ def == other
31
+ super && x == other.x && y == other.y
32
+ end
33
+
34
+ end
35
+
36
+ end
@@ -0,0 +1,18 @@
1
+ module Wongi::Engine
2
+
3
+ class FilterTest
4
+
5
+ def passes? token
6
+ raise "#{self.class} must implement #passes?"
7
+ end
8
+
9
+ def compile context
10
+ context.tap { |c| c.filter_node(self) }
11
+ end
12
+
13
+ def == other
14
+ false
15
+ end
16
+ end
17
+
18
+ end
@@ -0,0 +1,36 @@
1
+ module Wongi::Engine
2
+
3
+ class GreaterThanTest < FilterTest
4
+
5
+ attr_reader :x, :y
6
+
7
+ def initialize x, y
8
+ @x, @y = x, y
9
+ end
10
+
11
+ def passes? token
12
+
13
+ x = if Template.variable? @x
14
+ token[@x]
15
+ else
16
+ @x
17
+ end
18
+
19
+ y = if Template.variable? @y
20
+ token[@y]
21
+ else
22
+ @y
23
+ end
24
+
25
+ return false if x == :_ || y == :_
26
+ return x > y
27
+
28
+ end
29
+
30
+ def == other
31
+ super && x == other.x && y == other.y
32
+ end
33
+
34
+ end
35
+
36
+ end
@@ -0,0 +1,36 @@
1
+ module Wongi::Engine
2
+
3
+ class InequalityTest < FilterTest
4
+
5
+ attr_reader :x, :y
6
+
7
+ def initialize x, y
8
+ @x, @y = x, y
9
+ end
10
+
11
+ def passes? token
12
+
13
+ x = if Template.variable? @x
14
+ token[@x]
15
+ else
16
+ @x
17
+ end
18
+
19
+ y = if Template.variable? @y
20
+ token[@y]
21
+ else
22
+ @y
23
+ end
24
+
25
+ return false if x == :_ || y == :_
26
+ return x != y
27
+
28
+ end
29
+
30
+ def == other
31
+ super && x == other.x && y == other.y
32
+ end
33
+
34
+ end
35
+
36
+ end
@@ -0,0 +1,36 @@
1
+ module Wongi::Engine
2
+
3
+ class LessThanTest < FilterTest
4
+
5
+ attr_reader :x, :y
6
+
7
+ def initialize x, y
8
+ @x, @y = x, y
9
+ end
10
+
11
+ def passes? token
12
+
13
+ x = if Template.variable? @x
14
+ token[@x]
15
+ else
16
+ @x
17
+ end
18
+
19
+ y = if Template.variable? @y
20
+ token[@y]
21
+ else
22
+ @y
23
+ end
24
+
25
+ return false if x == :_ || y == :_
26
+ return x < y
27
+
28
+ end
29
+
30
+ def == other
31
+ super && x == other.x && y == other.y
32
+ end
33
+
34
+ end
35
+
36
+ end
@@ -0,0 +1,73 @@
1
+ module Wongi::Engine
2
+
3
+ class Graph
4
+
5
+ def initialize rete
6
+ @rete = rete
7
+ end
8
+
9
+ def dot io, opts = { }
10
+
11
+ @seen_betas = []
12
+
13
+ if String === io
14
+ File.open io, "w" do |actual_io|
15
+ dot actual_io
16
+ end
17
+ return
18
+ end
19
+
20
+ @io = io
21
+
22
+ @io.puts "digraph {"
23
+
24
+ dump_alphas(opts) unless opts[:alpha] == false
25
+ dump_betas(opts)
26
+
27
+ @io.puts "}"
28
+
29
+ ensure
30
+ @io = nil
31
+ end
32
+
33
+ private
34
+
35
+ def print_hash h
36
+ h.to_s.gsub /-/, '_'
37
+ end
38
+
39
+ def dump_alphas opts
40
+ @io.puts "subgraph cluster_alphas {"
41
+ @rete.alphas.select { |alpha| not alpha.betas.empty? }.each do |alpha|
42
+ @io.puts "node#{print_hash alpha.object_id} [shape=box label=\"#{alpha.template.to_s.gsub /"/, "\\\""}\"];"
43
+ end
44
+ @io.puts "};"
45
+ end
46
+
47
+ def dump_betas opts
48
+ dump_beta @rete.beta_top, opts
49
+ end
50
+
51
+ def dump_beta beta, opts
52
+ return if @seen_betas.include? beta
53
+ @seen_betas << beta
54
+ @io.puts "node#{print_hash beta.object_id} [label=\"#{beta.class.name.split('::').last}\"];"
55
+ if beta.is_a? NccNode
56
+ @io.puts "node#{print_hash beta.partner.object_id} -> node#{print_hash beta.object_id};"
57
+ @io.puts "{ rank=same; node#{print_hash beta.partner.object_id} node#{print_hash beta.object_id} }"
58
+ end
59
+ if beta.respond_to? :alpha and opts[:alpha] != false
60
+ alpha = beta.alpha
61
+ if alpha
62
+ @io.puts "node#{print_hash alpha.object_id} -> node#{print_hash beta.object_id};"
63
+ end
64
+ end
65
+ beta.children.each do |child|
66
+ @io.puts "node#{print_hash beta.object_id} -> node#{print_hash child.object_id};"
67
+ dump_beta child, opts
68
+ end
69
+ end
70
+
71
+ end
72
+
73
+ end
@@ -0,0 +1,416 @@
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
+ attr_reader :overlays
11
+
12
+ include NetworkParts::Collectable
13
+
14
+ protected
15
+ attr_accessor :alpha_hash
16
+ attr_writer :alpha_top, :beta_top
17
+ attr_writer :queries, :results
18
+
19
+ public
20
+
21
+ def debug!
22
+ extend NetworkParts::Debug
23
+ end
24
+
25
+ def rdf!
26
+ if ! defined? Wongi::RDF::DocumentSupport
27
+ begin
28
+ require 'wongi-rdf'
29
+ rescue LoadError => e
30
+ raise "'wongi-rdf' is required for RDF support"
31
+ end
32
+ end
33
+
34
+ extend Wongi::RDF::DocumentSupport
35
+ class << self
36
+ def statements
37
+ alpha_top.wmes
38
+ end
39
+ end
40
+
41
+ @namespaces = { }
42
+ @blank_counter = 1
43
+ @ns_counter = 0
44
+ @used_blanks = { }
45
+ end
46
+
47
+ def initialize
48
+ @timeline = []
49
+ self.alpha_top = AlphaMemory.new( Template.new( :_, :_, :_ ), self )
50
+ self.alpha_hash = { alpha_top.template.hash => alpha_top }
51
+ self.beta_top = BetaMemory.new(nil)
52
+ self.beta_top.rete = self
53
+ self.beta_top.seed
54
+ self.queries = {}
55
+ self.results = {}
56
+ @revns = {}
57
+
58
+ @productions = { }
59
+
60
+ @collectors = {}
61
+ @collectors[:error] = []
62
+
63
+ end
64
+
65
+ def dump
66
+ beta_top.dump
67
+ end
68
+
69
+ def with_overlay(&block)
70
+ default_overlay.with_child(&block)
71
+ end
72
+
73
+ def alphas
74
+ alpha_hash.values
75
+ end
76
+
77
+ # def import thing
78
+ # case thing
79
+ # when String, Numeric, TrueClass, FalseClass, NilClass, Wongi::RDF::Node
80
+ # thing
81
+ # when Symbol
82
+ # thing
83
+ # else
84
+ # thing
85
+ # end
86
+ # end
87
+
88
+ def default_overlay
89
+ @default_overlay ||= DataOverlay.new(self)
90
+ end
91
+
92
+ # @private
93
+ def add_overlay(o)
94
+ overlays << o
95
+ end
96
+
97
+ # @private
98
+ def remove_overlay(o)
99
+ overlays.delete(o) unless o == default_overlay
100
+ end
101
+
102
+ # @private
103
+ def current_overlay
104
+ overlays.last
105
+ end
106
+
107
+ # @private
108
+ def overlays
109
+ @overlays ||= []
110
+ end
111
+
112
+ def assert(wme)
113
+ default_overlay.assert(wme)
114
+ end
115
+
116
+ def retract(wme, options = {})
117
+ default_overlay.retract(wme, options)
118
+ end
119
+
120
+ # @private
121
+ def real_assert( wme )
122
+ unless wme.rete == self
123
+ wme = wme.import_into self
124
+ end
125
+
126
+ source = best_alpha(wme)
127
+ if existing = find(wme.subject, wme.predicate, wme.object)
128
+ existing.manual! if wme.manual?
129
+ return
130
+ end
131
+
132
+ alphas_for( wme ).each { |a| a.activate wme }
133
+
134
+ wme
135
+ end
136
+
137
+ # @private
138
+ def real_retract wme, options
139
+ real = find(wme.subject, wme.predicate, wme.object)
140
+ return if real.nil?
141
+ if real.generated? # still some generator tokens left
142
+ if real.manual?
143
+ real.manual = false
144
+ else
145
+ raise Error, "cannot retract automatic facts"
146
+ end
147
+ else
148
+ if options[:automatic] && real.manual? # auto-retracting a fact that has been added manually
149
+ return
150
+ end
151
+ end
152
+
153
+ alphas_for( real ).each { |a| a.deactivate real }
154
+ end
155
+
156
+ def wmes
157
+ alpha_top.wmes
158
+ end
159
+ alias_method :statements, :wmes
160
+ alias_method :facts, :wmes
161
+
162
+ def in_snapshot?
163
+ @in_snapshot
164
+ end
165
+
166
+ def snapshot!
167
+ @timeline.each_with_index do |slice, index|
168
+ source = if index == @timeline.size - 1
169
+ alpha_hash
170
+ else
171
+ @timeline[index+1]
172
+ end
173
+ # puts "source = #{source}"
174
+ wmes = {}
175
+ slice.each { |key, alpha| wmes[key] = alpha.wmes }
176
+ slice.each do |key, alpha|
177
+ in_snapshot {
178
+ wmes[key].dup.each { |wme| wme.destroy }
179
+ }
180
+ alpha.snapshot! source[key]
181
+ end
182
+ end
183
+ end
184
+
185
+ def rule name = nil, &block
186
+ r = DSL::Rule.new( name || generate_rule_name )
187
+ r.instance_eval &block
188
+ self << r
189
+ end
190
+
191
+ def query name, &block
192
+ q = DSL::Query.new name
193
+ q.instance_eval &block
194
+ self << q
195
+ end
196
+
197
+ def << something
198
+ if something.respond_to?( :install )
199
+ something.install( self )
200
+ else
201
+ case something
202
+ when Array
203
+ assert WME.new( *something ).tap { |wme| wme.overlay = default_overlay }
204
+ when WME
205
+ assert something
206
+ # when Wongi::RDF::Statement
207
+ # assert WME.new( something.subject, something.predicate, something.object, self )
208
+ #when Wongi::RDF::Document
209
+ # something.statements.each do |st|
210
+ # assert WME.new( st.subject, st.predicate, st.object, self )
211
+ # end
212
+ when Network
213
+ something.wmes.each { |wme| assert( wme ) }
214
+ else
215
+ raise Error, "I don't know how to accept a #{something.class}"
216
+ end
217
+ end
218
+ end
219
+
220
+ def install_rule( rule )
221
+ derived = rule.import_into self
222
+ production = build_production beta_top, derived.conditions, [], derived.actions, false
223
+ if rule.name
224
+ productions[ rule.name ] = production
225
+ end
226
+ production
227
+ end
228
+
229
+ def install_query( query )
230
+ derived = query.import_into self
231
+ prepare_query derived.name, derived.conditions, derived.parameters, derived.actions
232
+ end
233
+
234
+ def compile_alpha condition
235
+ template = Template.new :_, :_, :_
236
+ time = condition.time
237
+
238
+ template.subject = condition.subject unless Template.variable?( condition.subject )
239
+ template.predicate = condition.predicate unless Template.variable?( condition.predicate )
240
+ template.object = condition.object unless Template.variable?( condition.object )
241
+
242
+ hash = template.hash
243
+ # puts "COMPILED CONDITION #{condition} WITH KEY #{key}"
244
+ if time == 0
245
+ return self.alpha_hash[ hash ] if self.alpha_hash.has_key?( hash )
246
+ else
247
+ return @timeline[time+1][ hash ] if @timeline[time+1] && @timeline[time+1].has_key?( hash )
248
+ end
249
+
250
+ alpha = AlphaMemory.new( template, self )
251
+
252
+ if time == 0
253
+ self.alpha_hash[ hash ] = alpha
254
+ initial_fill alpha
255
+ else
256
+ if @timeline[time+1].nil?
257
+ # => ensure lineage from 0 to time
258
+ compile_alpha condition.class.new(condition.subject, condition.predicate, condition.object, time: time + 1)
259
+ @timeline.unshift Hash.new
260
+ end
261
+ @timeline[time+1][ hash ] = alpha
262
+ end
263
+ alpha
264
+ end
265
+
266
+ def cache s, p, o
267
+ compile_alpha Template.new(s, p, o)
268
+ end
269
+
270
+ # TODO: pick an alpha with fewer candidates to go through
271
+ def initial_fill alpha
272
+ alpha_top.wmes.each do |wme|
273
+ alpha.activate wme if wme =~ alpha.template
274
+ end
275
+ end
276
+
277
+ def remove_production pnode
278
+ delete_node_with_ancestors pnode
279
+ end
280
+
281
+ def prepare_query name, conditions, parameters, actions = []
282
+ query = self.queries[ name ] = BetaMemory.new( nil )
283
+ query.rete = self
284
+ query.seed(Hash[parameters.map{ |param| [param, nil]}])
285
+ self.results[ name ] = build_production query, conditions, parameters, actions, true
286
+ end
287
+
288
+ def execute name, valuations
289
+ beta = self.queries[name]
290
+ raise Error, "Undefined query #{name}; known queries are #{queries.keys}" unless beta
291
+ beta.subst valuations
292
+ end
293
+
294
+ def inspect
295
+ "<Rete>"
296
+ end
297
+
298
+ def exists? wme
299
+ find(wme.subject, wme.predicate, wme.object)
300
+ end
301
+
302
+ def each *args, &block
303
+ template = case args.length
304
+ when 0
305
+ Template.new(:_, :_, :_)
306
+ when 3
307
+ Template.new(*args)
308
+ else
309
+ raise Error, "Network#each expect a template or nothing at all"
310
+ end
311
+ source = best_alpha(template)
312
+ matching = current_overlay.wmes(source).select { |wme| wme =~ template }
313
+ if block_given?
314
+ matching.each(&block)
315
+ else
316
+ matching.each
317
+ end
318
+ end
319
+
320
+ def select s, p, o
321
+ template = Template.new(s, p, o)
322
+ source = best_alpha(template)
323
+ 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
327
+ matching
328
+ end
329
+
330
+ def find s, p, o
331
+ template = Template.new(s, p, o)
332
+ source = best_alpha(template)
333
+ # puts "looking for #{template} among #{source.wmes.size} triples of #{source.template}"
334
+ current_overlay.wmes(source).find { |wme| wme =~ template }
335
+ end
336
+
337
+ protected
338
+
339
+
340
+ def in_snapshot
341
+ @in_snapshot = true
342
+ yield
343
+ ensure
344
+ @in_snapshot = false
345
+ end
346
+
347
+ def generate_rule_name
348
+ "rule_#{productions.length}"
349
+ end
350
+
351
+ def alphas_for wme
352
+ s, p, o = wme.subject, wme.predicate, wme.object
353
+ [
354
+ lookup( s, p, o),
355
+ lookup( s, p, :_),
356
+ lookup( s, :_, o),
357
+ lookup(:_, p, o),
358
+ lookup( s, :_, :_),
359
+ lookup(:_, p, :_),
360
+ lookup(:_, :_, o),
361
+ lookup(:_, :_, :_),
362
+ ].compact!.tap(&:uniq!)
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)
373
+ end
374
+
375
+ def best_alpha template
376
+ alpha_hash.inject(nil) do |best, (_, alpha)|
377
+ if template =~ alpha.template && (best.nil? || alpha.size < best.size)
378
+ alpha
379
+ else
380
+ best
381
+ end
382
+ end
383
+ end
384
+
385
+ def build_production root, conditions, parameters, actions, alpha_deaf
386
+ compiler = Compiler.new(self, root, conditions, parameters, alpha_deaf)
387
+ ProductionNode.new(compiler.compile, actions).tap do |production|
388
+ production.compilation_context = compiler
389
+ production.refresh
390
+ end
391
+ end
392
+
393
+ def delete_node_with_ancestors node
394
+
395
+ if node.kind_of?( NccNode )
396
+ delete_node_with_ancestors node.partner
397
+ end
398
+
399
+ if [BetaMemory, NegNode, NccNode, NccPartner].any? { | klass| node.kind_of? klass }
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
410
+ end
411
+
412
+ end
413
+
414
+ end
415
+
416
+ end