wongi-engine 0.1.4 → 0.2.0

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 (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