wongi-engine 0.1.4 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/.hgignore +6 -0
  3. data/.hgtags +2 -4
  4. data/.travis.yml +19 -6
  5. data/README.md +18 -442
  6. data/examples/ex02.rb +3 -2
  7. data/examples/graphviz.rb +1 -0
  8. data/lib/wongi-engine/alpha_memory.rb +11 -6
  9. data/lib/wongi-engine/beta/assignment_node.rb +0 -15
  10. data/lib/wongi-engine/beta/beta_memory.rb +7 -31
  11. data/lib/wongi-engine/beta/beta_node.rb +25 -95
  12. data/lib/wongi-engine/beta/join_node.rb +14 -46
  13. data/lib/wongi-engine/beta/ncc_node.rb +8 -34
  14. data/lib/wongi-engine/beta/ncc_partner.rb +3 -19
  15. data/lib/wongi-engine/beta/neg_node.rb +7 -16
  16. data/lib/wongi-engine/beta/optional_node.rb +23 -30
  17. data/lib/wongi-engine/beta/or_node.rb +0 -51
  18. data/lib/wongi-engine/beta/production_node.rb +1 -0
  19. data/lib/wongi-engine/compiler.rb +115 -0
  20. data/lib/wongi-engine/data_overlay.rb +140 -0
  21. data/lib/wongi-engine/dsl/action/base.rb +11 -0
  22. data/lib/wongi-engine/dsl/{actions → action}/error_generator.rb +3 -14
  23. data/lib/wongi-engine/dsl/action/simple_action.rb +60 -0
  24. data/lib/wongi-engine/dsl/action/simple_collector.rb +52 -0
  25. data/lib/wongi-engine/dsl/action/statement_generator.rb +45 -0
  26. data/lib/wongi-engine/dsl/action/trace_action.rb +49 -0
  27. data/lib/wongi-engine/dsl/any_rule.rb +4 -21
  28. data/lib/wongi-engine/dsl/assuming.rb +6 -12
  29. data/lib/wongi-engine/dsl/builder.rb +44 -0
  30. data/lib/wongi-engine/dsl/clause/assign.rb +15 -0
  31. data/lib/wongi-engine/dsl/clause/fact.rb +71 -0
  32. data/lib/wongi-engine/dsl/clause/gen.rb +17 -0
  33. data/lib/wongi-engine/dsl/clause/generic.rb +38 -0
  34. data/lib/wongi-engine/dsl/{dsl_extensions.rb → generated.rb} +5 -5
  35. data/lib/wongi-engine/dsl/ncc_subrule.rb +15 -0
  36. data/lib/wongi-engine/dsl/query.rb +10 -11
  37. data/lib/wongi-engine/dsl/rule.rb +84 -0
  38. data/lib/wongi-engine/dsl.rb +102 -97
  39. data/lib/wongi-engine/enumerators.rb +21 -0
  40. data/lib/wongi-engine/error.rb +13 -2
  41. data/lib/wongi-engine/filter/filter_test.rb +1 -13
  42. data/lib/wongi-engine/graph.rb +7 -7
  43. data/lib/wongi-engine/network.rb +108 -181
  44. data/lib/wongi-engine/ruleset.rb +6 -6
  45. data/lib/wongi-engine/template.rb +30 -84
  46. data/lib/wongi-engine/token.rb +3 -34
  47. data/lib/wongi-engine/version.rb +1 -1
  48. data/lib/wongi-engine/wme.rb +9 -60
  49. data/lib/wongi-engine.rb +3 -0
  50. data/spec/beta_node_spec.rb +2 -0
  51. data/spec/generation_spec.rb +1 -1
  52. data/spec/high_level_spec.rb +29 -11
  53. data/spec/overlay_spec.rb +22 -0
  54. data/spec/simple_action_spec.rb +1 -1
  55. data/spec/spec_helper.rb +1 -0
  56. data/spec/wme_spec.rb +22 -22
  57. data/wongi-engine.gemspec +1 -1
  58. metadata +37 -17
  59. data/lib/wongi-engine/dsl/action.rb +0 -12
  60. data/lib/wongi-engine/dsl/actions/simple_action.rb +0 -62
  61. data/lib/wongi-engine/dsl/actions/simple_collector.rb +0 -51
  62. data/lib/wongi-engine/dsl/actions/statement_generator.rb +0 -67
  63. data/lib/wongi-engine/dsl/actions/trace_action.rb +0 -52
  64. data/lib/wongi-engine/dsl/dsl_builder.rb +0 -44
  65. data/lib/wongi-engine/dsl/extension_clause.rb +0 -36
  66. data/lib/wongi-engine/dsl/generation_clause.rb +0 -15
  67. data/lib/wongi-engine/dsl/generic_production_rule.rb +0 -82
  68. data/lib/wongi-engine/dsl/ncc_production_rule.rb +0 -21
  69. data/lib/wongi-engine/dsl/production_rule.rb +0 -4
  70. data/lib/wongi-engine/model_context.rb +0 -13
@@ -7,6 +7,7 @@ module Wongi::Engine
7
7
  attr_reader :alpha_top, :beta_top
8
8
  attr_reader :queries, :results
9
9
  attr_reader :productions
10
+ attr_reader :overlays
10
11
 
11
12
  include NetworkParts::Collectable
12
13
 
@@ -46,7 +47,7 @@ module Wongi::Engine
46
47
  def initialize
47
48
  @timeline = []
48
49
  self.alpha_top = AlphaMemory.new( Template.new( :_, :_, :_ ), self )
49
- self.alpha_hash = { Template.hash_for( :_, :_, :_ ) => self.alpha_top }
50
+ self.alpha_hash = { alpha_top.template.hash => alpha_top }
50
51
  self.beta_top = BetaMemory.new(nil)
51
52
  self.beta_top.rete = self
52
53
  self.beta_top.seed
@@ -54,7 +55,6 @@ module Wongi::Engine
54
55
  self.results = {}
55
56
  @cache = {}
56
57
  @revns = {}
57
- @contexts = {}
58
58
 
59
59
  @productions = { }
60
60
 
@@ -67,69 +67,58 @@ module Wongi::Engine
67
67
  beta_top.dump
68
68
  end
69
69
 
70
+ def with_overlay(&block)
71
+ default_overlay.with_child(&block)
72
+ end
73
+
70
74
  def alphas
71
75
  alpha_hash.values
72
76
  end
73
77
 
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
78
+ # def import thing
79
+ # case thing
80
+ # when String, Numeric, TrueClass, FalseClass, NilClass, Wongi::RDF::Node
81
+ # thing
82
+ # when Symbol
83
+ # thing
84
+ # else
85
+ # thing
86
+ # end
87
+ # end
88
+
89
+ def default_overlay
90
+ @default_overlay ||= DataOverlay.new(self)
83
91
  end
84
92
 
85
- def assert wme
86
- @next_cascade ||= []
87
- @next_cascade << [:assert, wme]
88
- if @current_cascade.nil?
89
- @current_cascade = @next_cascade
90
- @next_cascade = nil
91
- process_cascade
92
- end
93
+ # @private
94
+ def add_overlay(o)
95
+ overlays << o
93
96
  end
94
97
 
95
- def retract wme, options = { }
96
- @next_cascade ||= []
97
- @next_cascade << [:retract, wme, options]
98
- if @current_cascade.nil?
99
- @current_cascade = @next_cascade
100
- @next_cascade = nil
101
- process_cascade
102
- end
98
+ # @private
99
+ def remove_overlay(o)
100
+ overlays.delete(o) unless o == default_overlay
103
101
  end
104
102
 
105
- def process_cascade
106
- iterations = 0
107
- while @current_cascade
108
- @current_cascade.each do |(operation, wme, options)|
109
- case operation
110
- when :assert
111
- real_assert wme
112
- when :retract
113
- real_retract wme, options
114
- end
115
- end
116
- @current_cascade = @next_cascade
117
- @next_cascade = nil
118
- iterations += 1
119
- end
103
+ # @private
104
+ def overlays
105
+ @overlays ||= []
106
+ end
107
+
108
+ def assert(wme)
109
+ default_overlay.assert(wme)
120
110
  end
121
111
 
122
- def real_assert wme
112
+ def retract(wme, options = {})
113
+ default_overlay.retract(wme, options)
114
+ end
123
115
 
116
+ # @private
117
+ def real_assert( wme )
124
118
  unless wme.rete == self
125
119
  wme = wme.import_into self
126
120
  end
127
121
 
128
- if @current_context
129
- @current_context.asserted_wmes << wme
130
- wme.context = @current_context
131
- end
132
-
133
122
  if existing = @cache[wme]
134
123
  existing.manual! if wme.manual?
135
124
  return
@@ -143,6 +132,28 @@ module Wongi::Engine
143
132
  wme
144
133
  end
145
134
 
135
+ # @private
136
+ def real_retract wme, options
137
+ real = @cache[wme]
138
+
139
+ return if real.nil?
140
+ if real.generated? # still some generator tokens left
141
+ if real.manual?
142
+ real.manual = false
143
+ else
144
+ raise Error, "cannot retract automatic facts"
145
+ end
146
+ else
147
+ if options[:automatic] && real.manual? # auto-retracting a fact that has been added manually
148
+ return
149
+ end
150
+ end
151
+
152
+ @cache.delete(real)
153
+
154
+ alphas_for( real ).each { |a| a.deactivate real }
155
+ end
156
+
146
157
  def wmes
147
158
  alpha_top.wmes
148
159
  end
@@ -173,82 +184,52 @@ module Wongi::Engine
173
184
  end
174
185
 
175
186
  def rule name = nil, &block
176
- r = ProductionRule.new( name || generate_rule_name )
187
+ r = DSL::Rule.new( name || generate_rule_name )
177
188
  r.instance_eval &block
178
189
  self << r
179
190
  end
180
191
 
181
192
  def query name, &block
182
- q = Query.new name
193
+ q = DSL::Query.new name
183
194
  q.instance_eval &block
184
195
  self << q
185
196
  end
186
197
 
187
198
  def << something
188
- case something
189
- when Array
190
- if something.length == 3
199
+ if something.respond_to?( :install )
200
+ something.install( self )
201
+ else
202
+ case something
203
+ when Array
191
204
  assert WME.new( *something )
205
+ when WME
206
+ assert something
207
+ # when Wongi::RDF::Statement
208
+ # assert WME.new( something.subject, something.predicate, something.object, self )
209
+ #when Wongi::RDF::Document
210
+ # something.statements.each do |st|
211
+ # assert WME.new( st.subject, st.predicate, st.object, self )
212
+ # end
213
+ when Network
214
+ something.wmes.each { |wme| assert( wme ) }
192
215
  else
193
- raise "Arrays must have 3 elements"
194
- end
195
- when ProductionRule
196
- derived = something.import_into self
197
- production = add_production derived.conditions, derived.actions
198
- if something.name
199
- productions[ something.name ] = production
216
+ raise Error, "I don't know how to accept a #{something.class}"
200
217
  end
201
- production
202
- when Query
203
- derived = something.import_into self
204
- prepare_query derived.name, derived.conditions, derived.parameters, derived.actions
205
- when Ruleset
206
- something.install self
207
- when WME
208
- assert something
209
- when Wongi::RDF::Statement
210
- assert WME.new( something.subject, something.predicate, something.object, self )
211
- #when Wongi::RDF::Document
212
- # something.statements.each do |st|
213
- # assert WME.new( st.subject, st.predicate, st.object, self )
214
- # end
215
- when Network
216
- something.each do |st|
217
- assert st.import_into( self )
218
- end
219
- else
220
- raise "I don't know how to accept a #{something.class}"
221
218
  end
222
219
  end
223
220
 
224
- def real_retract wme, options
225
-
226
- if wme.is_a? Array
227
- return real_retract( WME.new(*wme), options )
228
- end
229
-
230
- real = @cache[wme]
231
-
232
- return if real.nil?
233
- if real.generated? # still some generator tokens left
234
- if real.manual?
235
- real.manual = false
236
- else
237
- raise "cannot retract automatic facts"
238
- end
239
- else
240
- if options[:automatic] && real.manual? # auto-retracting a fact that has been added manually
241
- return
242
- end
243
- end
244
-
245
- if @current_context
246
- @current_context.retracted_wmes << wme
221
+ def install_rule( rule )
222
+ derived = rule.import_into self
223
+ production = build_production beta_top, derived.conditions, [], derived.actions, false
224
+ if rule.name
225
+ productions[ rule.name ] = production
247
226
  end
227
+ production
228
+ end
248
229
 
249
- @cache.delete(real)
250
-
251
- alphas_for( real ).each { |a| a.deactivate real }
230
+ def install_query( query )
231
+ derived = query.import_into self
232
+ prepare_query derived.name, derived.conditions, derived.parameters, derived.actions
252
233
  end
253
234
 
254
235
  def compile_alpha condition
@@ -284,23 +265,16 @@ module Wongi::Engine
284
265
  end
285
266
 
286
267
  def cache s, p, o
287
- compile_alpha Template.new(s, p, o).import_into( self )
268
+ compile_alpha Template.new(s, p, o)
288
269
  end
289
270
 
271
+ # TODO: pick an alpha with fewer candidates to go through
290
272
  def initial_fill alpha
291
- tpl = alpha.template
292
- source = more_generic_alpha(tpl)
293
- # puts "more efficient by #{alpha_top.wmes.size - source.wmes.size}" unless source ==
294
- # alpha_top
295
- source.wmes.each do |wme|
296
- alpha.activate wme if wme =~ tpl
273
+ alpha_top.wmes.each do |wme|
274
+ alpha.activate wme if wme =~ alpha.template
297
275
  end
298
276
  end
299
277
 
300
- def add_production conditions, actions = []
301
- real_add_production self.beta_top, conditions, [], actions, false
302
- end
303
-
304
278
  def remove_production pnode
305
279
  delete_node_with_ancestors pnode
306
280
  end
@@ -308,15 +282,13 @@ module Wongi::Engine
308
282
  def prepare_query name, conditions, parameters, actions = []
309
283
  query = self.queries[ name ] = BetaMemory.new( nil )
310
284
  query.rete = self
311
- transformed = {}
312
- parameters.each { |param| transformed[param] = nil }
313
- query.seed transformed
314
- self.results[ name ] = real_add_production query, conditions, parameters, actions, true
285
+ query.seed(Hash[parameters.map{ |param| [param, nil]}])
286
+ self.results[ name ] = build_production query, conditions, parameters, actions, true
315
287
  end
316
288
 
317
289
  def execute name, valuations
318
290
  beta = self.queries[name]
319
- raise "Undefined query #{name}; known queries are #{queries.keys}" unless beta
291
+ raise Error, "Undefined query #{name}; known queries are #{queries.keys}" unless beta
320
292
  beta.subst valuations
321
293
  end
322
294
 
@@ -324,25 +296,6 @@ module Wongi::Engine
324
296
  "<Rete>"
325
297
  end
326
298
 
327
- def context= name
328
- if name && !@contexts.has_key?(name)
329
- @current_context = (@contexts[name] ||= ModelContext.new name)
330
- end
331
- end
332
-
333
- # TODO: contexts are probably broken with the new improved handling of manual & generated
334
- def retract_context name
335
- return unless @contexts.has_key?(name)
336
-
337
- if @current_context && @current_context.name == name
338
- @current_context = nil
339
- end
340
- ctx = @contexts[name]
341
- ctx.asserted_wmes.select { |wme| wme.generating_tokens.empty? }.each { |wme| retract(wme, true) }
342
- ctx.retracted_wmes.each { |wme| assert(wme) }
343
- @contexts.delete name
344
- end
345
-
346
299
  def exists? wme
347
300
  @cache[ wme ]
348
301
  end
@@ -350,7 +303,7 @@ module Wongi::Engine
350
303
  def each *args
351
304
  return unless block_given?
352
305
  unless args.length == 0 || args.length == 3
353
- raise "Document#each expects a pattern or nothing at all"
306
+ raise Error, "Network#each expects a pattern or nothing at all"
354
307
  end
355
308
  s, p, o = if args.empty?
356
309
  [:_, :_, :_]
@@ -365,7 +318,7 @@ module Wongi::Engine
365
318
  end
366
319
 
367
320
  def select s, p, o
368
- template = Template.new(s, p, o).import_into self
321
+ template = Template.new(s, p, o)
369
322
  matching = alpha_top.wmes.select { |wme| wme =~ template }
370
323
  if block_given?
371
324
  matching.each { |st| yield st.subject, st.predicate, st.object }
@@ -374,7 +327,7 @@ module Wongi::Engine
374
327
  end
375
328
 
376
329
  def find s, p, o
377
- template = Template.new(s, p, o).import_into self
330
+ template = Template.new(s, p, o)
378
331
  source = best_alpha(template)
379
332
  # puts "looking for #{template} among #{source.wmes.size} triples of #{source.template}"
380
333
  source.wmes.detect { |wme| wme =~ template }
@@ -382,6 +335,7 @@ module Wongi::Engine
382
335
 
383
336
  protected
384
337
 
338
+
385
339
  def in_snapshot
386
340
  @in_snapshot = true
387
341
  yield
@@ -404,7 +358,7 @@ module Wongi::Engine
404
358
  lookup(:_, p, :_),
405
359
  lookup(:_, :_, o),
406
360
  lookup(:_, :_, :_),
407
- ].compact.uniq
361
+ ].compact!.tap(&:uniq!)
408
362
  end
409
363
 
410
364
  def lookup s, p, o
@@ -414,52 +368,25 @@ module Wongi::Engine
414
368
  end
415
369
 
416
370
  def alpha_activate alpha, wme
417
- alpha.activate(wme) if alpha
418
- end
419
-
420
- def more_generic_alpha template
421
- return alpha_top # OPTIMISE => temporary; may use later or not use at all
422
- return alpha_top if template.root?
423
- more_generic_templates(template).reduce alpha_top do |best, template|
424
- alpha = alpha_hash[template.hash]
425
- if alpha && alpha.wmes.size < best.wmes.size
426
- alpha
427
- else
428
- best
429
- end
430
- end
431
- end
432
-
433
- def more_generic_templates template
434
- set = []
435
- set << template.with_subject( :_ ) unless template.subject == :_
436
- set << template.with_predicate( :_ ) unless template.predicate == :_
437
- set << template.with_object( :_ ) unless template.object == :_
438
- set.select { |item| not item.root? }
371
+ alpha.activate(wme)
439
372
  end
440
373
 
441
374
  def best_alpha template
442
- candidates = alpha_hash.values.select do |alpha|
443
- template =~ alpha.template
444
- end
445
- result = candidates.inject do |best, alpha|
446
- if best.nil?
447
- alpha
448
- elsif alpha.wmes.to_a.length < best.wmes.to_a.length
375
+ alpha_hash.inject(nil) do |best, (_, alpha)|
376
+ if template =~ alpha.template && (best.nil? || alpha.size < best.size)
449
377
  alpha
450
378
  else
451
379
  best
452
380
  end
453
381
  end
454
- result
455
382
  end
456
383
 
457
- def real_add_production root, conditions, parameters, actions, alpha_deaf
458
- beta = root.network conditions, [], parameters, alpha_deaf
459
-
460
- production = ProductionNode.new( beta, actions )
461
- production.refresh
462
- production
384
+ def build_production root, conditions, parameters, actions, alpha_deaf
385
+ compiler = Compiler.new(self, root, conditions, parameters, alpha_deaf)
386
+ ProductionNode.new(compiler.compile, actions).tap do |production|
387
+ production.compilation_context = compiler
388
+ production.refresh
389
+ end
463
390
  end
464
391
 
465
392
  def delete_node_with_ancestors node
@@ -5,12 +5,12 @@ module Wongi
5
5
  class << self
6
6
 
7
7
  def [] name
8
- raise "undefined ruleset #{name}" unless rulesets.has_key?( name )
8
+ raise Error, "undefined ruleset #{name}" unless rulesets.has_key?( name )
9
9
  rulesets[ name ]
10
10
  end
11
11
 
12
12
  def register name, ruleset
13
- raise "ruleset #{name} already exists" if rulesets.has_key?( name )
13
+ raise Error, "ruleset #{name} already exists" if rulesets.has_key?( name )
14
14
  rulesets[ name ] = ruleset
15
15
  end
16
16
 
@@ -36,8 +36,8 @@ module Wongi
36
36
  def install rete
37
37
  # puts "Installing ruleset #{name}"
38
38
  @rules.each { |rule| rete << rule }
39
- rescue Exception => e
40
- e1 = Exception.new "error installing ruleset '#{name||'<unnamed>'}': #{e}"
39
+ rescue StandardError => e
40
+ e1 = Error.new "error installing ruleset '#{name||'<unnamed>'}': #{e}"
41
41
  e1.set_backtrace e.backtrace
42
42
  raise e1
43
43
  end
@@ -56,14 +56,14 @@ module Wongi
56
56
  # end
57
57
 
58
58
  def rule name, &definition
59
- r = ProductionRule.new name
59
+ r = DSL::Rule.new name
60
60
  r.instance_eval &definition
61
61
  @rules << r
62
62
  r
63
63
  end
64
64
 
65
65
  def query name, &definition
66
- r = Query.new name
66
+ r = DSL::Query.new name
67
67
  r.instance_eval &definition
68
68
  @rules << r
69
69
  r
@@ -1,60 +1,36 @@
1
1
  module Wongi::Engine
2
2
 
3
- class Template < Struct.new( :subject, :predicate, :object, :time )
4
-
5
- include CoreExt
6
-
7
- attr_reader :unsafe
8
- attr_predicate debug: false
3
+ Template = Struct.new( :subject, :predicate, :object ) do
9
4
 
10
5
  def self.variable? thing
11
- Symbol === thing && thing =~ /^[A-Z]/
12
- end
13
-
14
- def initialize s, p, o, options = { }
15
- time = options[:time] || 0
16
- unsafe = options[:unsafe] || false
17
- debug! if options[:debug]
18
- raise "Cannot work with continuous time" unless time.integer?
19
- raise "Cannot look into the future" if time > 0
20
- super( s, p, o, time )
21
- @filters = []
22
- @unsafe = unsafe
6
+ return false unless thing.is_a?(Symbol)
7
+ thing[0] >= 'A' && thing[0] <= 'Z'
23
8
  end
24
9
 
25
- def import_into r
26
- copy = self.class.new r.import( subject ), r.import( predicate ), r.import( object ), time: time, unsafe: unsafe, debug: debug?
27
- @filters.each { |f| copy.filters << f }
28
- copy
29
- end
10
+ # TODO: reintroduce Network#import when bringing back RDF support
30
11
 
31
12
  def root?
32
13
  subject == :_ && predicate == :_ && object == :_
33
14
  end
34
15
 
35
16
  def variables
36
- array_form.select { |e| self.class.variable? e }
37
- end
38
-
39
- def contains? var
40
- self.class.variable?( var ) && array_form.include?( var )
17
+ [].tap do |a|
18
+ a << subject if Template.variable?( subject )
19
+ a << predicate if Template.variable?( predicate )
20
+ a << object if Template.variable?( object )
21
+ end
41
22
  end
42
23
 
43
24
  def hash
44
- @hash ||= array_form.map( &:hash ).hash
25
+ @hash ||= [subject.hash, predicate.hash, object.hash].hash
45
26
  end
46
27
 
47
28
  def self.hash_for *args
48
29
  args.map( &:hash ).hash
49
30
  end
50
31
 
51
- def === wme
52
- wme =~ self if WME === wme
53
- end
54
-
55
32
  def == other
56
- return false unless Template === other
57
- subject == other.subject && predicate == other.predicate && object == other.object
33
+ other.is_a?(Template) && subject == other.subject && predicate == other.predicate && object == other.object
58
34
  end
59
35
 
60
36
  def =~ template
@@ -64,66 +40,36 @@ module Wongi::Engine
64
40
  ( template.predicate == :_ || template.predicate == predicate ) &&
65
41
  ( template.object == :_ || template.object == object )
66
42
  else
67
- raise "Templates can only match templates"
43
+ raise Error, "templates can only match other templates"
68
44
  end
69
45
  end
70
46
 
71
-
72
- def compile context
73
- tests, assignment = *JoinNode.compile( self, context.earlier, context.parameters )
74
- alpha = context.rete.compile_alpha( self )
75
- context.node = context.node.beta_memory.join_node( alpha, tests, assignment, context.alpha_deaf )
76
- context.node.context = context
77
- context.node.debug = debug?
78
- context.earlier << self
79
- context
80
- end
81
-
82
47
  def inspect
83
- "<~ #{subject.inspect} #{predicate.inspect} #{object.inspect} #{time}>"
48
+ "<~#{subject.inspect} #{predicate.inspect} #{object.inspect}>"
84
49
  end
85
50
 
86
51
  def to_s
87
52
  inspect
88
53
  end
89
54
 
90
- private
91
-
92
- def array_form
93
- @array_form ||= [ subject, predicate, object ]
94
- end
95
-
96
- end
97
-
98
- class NegTemplate < Template
99
-
100
- # :arg: context => Wongi::Rete::BetaNode::CompilationContext
101
- def compile context
102
- tests, assignment = *JoinNode.compile( self, context.earlier, context.parameters )
103
- raise DefinitionError.new("Negative matches may not introduce new variables: #{assignment.variables}") unless assignment.root?
104
- alpha = context.rete.compile_alpha( self )
105
- context.node = context.node.neg_node( alpha, tests, context.alpha_deaf, unsafe )
106
- context.node.context = context
107
- context.node.debug = debug?
108
- context.earlier << self
109
- context
110
- end
111
- end
112
-
113
- class OptionalTemplate < Template
114
-
115
- def compile context
116
- tests, assignment = *JoinNode.compile( self, context.earlier, context.parameters )
117
- alpha = context.rete.compile_alpha( self )
118
- context.node = context.node.beta_memory.optional_node( alpha, tests, assignment, context.alpha_deaf )
119
- context.node.context = context
120
- context.node.debug = debug?
121
- context.earlier << self
122
- context
55
+ def resolve!(token)
56
+ s = if Template.variable?(subject)
57
+ token[subject].tap { |x| raise DefinitionError, "unbound variable #{subject} in token #{token}" unless x}
58
+ else
59
+ subject
60
+ end
61
+ p = if Template.variable?(predicate)
62
+ token[predicate].tap { |x| raise DefinitionError, "unbound variable #{predicate} in token #{token}" unless x}
63
+ else
64
+ predicate
65
+ end
66
+ o = if Template.variable?(object)
67
+ token[object].tap { |x| raise DefinitionError, "unbound variable #{object} in token #{token}" unless x}
68
+ else
69
+ object
70
+ end
71
+ [s, p, o]
123
72
  end
124
73
 
125
74
  end
126
-
127
-
128
-
129
75
  end