wongi-engine 0.3.8 → 0.4.0.pre.alpha1

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 (54) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +2 -2
  3. data/.gitignore +2 -0
  4. data/README.md +13 -13
  5. data/lib/wongi-engine/alpha_index.rb +58 -0
  6. data/lib/wongi-engine/alpha_memory.rb +2 -24
  7. data/lib/wongi-engine/beta/aggregate_node.rb +16 -15
  8. data/lib/wongi-engine/beta/assignment_node.rb +7 -2
  9. data/lib/wongi-engine/beta/beta_node.rb +27 -23
  10. data/lib/wongi-engine/beta/filter_node.rb +8 -13
  11. data/lib/wongi-engine/beta/join_node.rb +15 -28
  12. data/lib/wongi-engine/beta/ncc_node.rb +14 -26
  13. data/lib/wongi-engine/beta/ncc_partner.rb +18 -18
  14. data/lib/wongi-engine/beta/neg_node.rb +23 -55
  15. data/lib/wongi-engine/beta/optional_node.rb +24 -48
  16. data/lib/wongi-engine/beta/or_node.rb +24 -1
  17. data/lib/wongi-engine/beta/production_node.rb +9 -3
  18. data/lib/wongi-engine/beta/root_node.rb +47 -0
  19. data/lib/wongi-engine/beta.rb +1 -1
  20. data/lib/wongi-engine/compiler.rb +6 -34
  21. data/lib/wongi-engine/dsl/action/{base.rb → base_action.rb} +5 -1
  22. data/lib/wongi-engine/dsl/action/error_generator.rb +1 -1
  23. data/lib/wongi-engine/dsl/action/simple_action.rb +1 -1
  24. data/lib/wongi-engine/dsl/action/simple_collector.rb +1 -1
  25. data/lib/wongi-engine/dsl/action/statement_generator.rb +21 -22
  26. data/lib/wongi-engine/dsl/action/trace_action.rb +1 -1
  27. data/lib/wongi-engine/dsl/clause/fact.rb +2 -6
  28. data/lib/wongi-engine/dsl.rb +1 -25
  29. data/lib/wongi-engine/graph.rb +1 -1
  30. data/lib/wongi-engine/network/debug.rb +2 -10
  31. data/lib/wongi-engine/network.rb +44 -105
  32. data/lib/wongi-engine/overlay.rb +589 -0
  33. data/lib/wongi-engine/template.rb +22 -2
  34. data/lib/wongi-engine/token.rb +10 -26
  35. data/lib/wongi-engine/token_assignment.rb +15 -0
  36. data/lib/wongi-engine/version.rb +1 -1
  37. data/lib/wongi-engine/wme.rb +10 -39
  38. data/lib/wongi-engine.rb +3 -1
  39. data/spec/alpha_index_spec.rb +78 -0
  40. data/spec/bug_specs/issue_4_spec.rb +11 -11
  41. data/spec/high_level_spec.rb +8 -101
  42. data/spec/network_spec.rb +8 -6
  43. data/spec/overlay_spec.rb +177 -1
  44. data/spec/rule_specs/any_rule_spec.rb +39 -0
  45. data/spec/rule_specs/assign_spec.rb +1 -1
  46. data/spec/rule_specs/maybe_rule_spec.rb +58 -1
  47. data/spec/rule_specs/ncc_spec.rb +78 -19
  48. data/spec/rule_specs/negative_rule_spec.rb +27 -13
  49. data/spec/spec_helper.rb +4 -0
  50. data/spec/wme_spec.rb +0 -32
  51. metadata +11 -9
  52. data/lib/wongi-engine/beta/beta_memory.rb +0 -60
  53. data/lib/wongi-engine/data_overlay.rb +0 -141
  54. data/spec/rule_specs/or_rule_spec.rb +0 -40
@@ -44,7 +44,7 @@ module Wongi::Engine
44
44
  return if @seen_betas.include? beta
45
45
 
46
46
  @seen_betas << beta
47
- io.puts "node#{print_hash beta.object_id} [label=\"#{beta.class.name.split('::').last}\"];"
47
+ io.puts "node#{print_hash beta.object_id} [label=\"#{beta.class.name.split('::').last}\\nid=#{beta.object_id}\"];"
48
48
  if beta.is_a? NccNode
49
49
  io.puts "node#{print_hash beta.partner.object_id} -> node#{print_hash beta.object_id};"
50
50
  io.puts "{ rank=same; node#{print_hash beta.partner.object_id} node#{print_hash beta.object_id} }"
@@ -2,14 +2,6 @@ module Wongi::Engine
2
2
  module NetworkParts
3
3
  module Debug
4
4
  def full_wme_dump
5
- @timeline.each_with_index do |slice, index|
6
- puts "time #{index - @timeline.length}"
7
- slice.each do |_key, alpha|
8
- puts "\t#{alpha.template} -> [#{alpha.wmes.map(&:to_s).join ', '}]"
9
- end
10
- puts ""
11
- end
12
- puts "time 0"
13
5
  alpha_hash.each do |_key, alpha|
14
6
  puts "\t#{alpha.template} -> [#{alpha.wmes.map(&:to_s).join ', '}]"
15
7
  end
@@ -39,8 +31,8 @@ module Wongi::Engine
39
31
  def dump_wme(wme, io)
40
32
  io.puts "\tWME: #{wme.object_id} #{wme}"
41
33
  wme.tokens.each { |token| io.puts "\t\tTOKEN #{token.object_id}" }
42
- io.puts "\tGENERATING:" unless wme.generating_tokens.empty?
43
- wme.generating_tokens.each { |token| io.puts "\t\tTOKEN #{token.object_id}" }
34
+ io.puts "\tGENERATING:" unless wme.generators.empty?
35
+ wme.generators.each { |token| io.puts "\t\tTOKEN #{token.object_id}" }
44
36
  end
45
37
 
46
38
  def dump_beta(beta, io)
@@ -3,16 +3,14 @@ require 'wongi-engine/network/debug'
3
3
 
4
4
  module Wongi::Engine
5
5
  class Network
6
- attr_reader :alpha_top, :beta_top, :queries, :results, :productions
6
+ attr_accessor :alpha_top, :beta_top, :queries, :results, :alpha_hash
7
+ attr_reader :productions, :overlays
7
8
 
8
9
  include NetworkParts::Collectable
10
+ private :overlays
11
+ private :alpha_hash, :alpha_hash=
9
12
 
10
- protected
11
-
12
- attr_accessor :alpha_hash
13
- attr_writer :alpha_top, :beta_top, :queries, :results
14
-
15
- public
13
+ private :alpha_top=, :beta_top=, :queries=, :results=
16
14
 
17
15
  def debug!
18
16
  extend NetworkParts::Debug
@@ -42,10 +40,11 @@ module Wongi::Engine
42
40
  end
43
41
 
44
42
  def initialize
45
- @timeline = []
43
+ @overlays = [base_overlay]
44
+
46
45
  self.alpha_top = AlphaMemory.new(Template.new(:_, :_, :_), self)
47
46
  self.alpha_hash = { alpha_top.template.hash => alpha_top }
48
- self.beta_top = BetaMemory.new(nil)
47
+ self.beta_top = RootNode.new(nil)
49
48
  beta_top.rete = self
50
49
  beta_top.seed
51
50
  self.queries = {}
@@ -63,7 +62,12 @@ module Wongi::Engine
63
62
  end
64
63
 
65
64
  def with_overlay(&block)
66
- default_overlay.with_child(&block)
65
+ child = current_overlay.new_child
66
+ add_overlay(child)
67
+ block.call(child)
68
+ ensure
69
+ remove_overlay(child)
70
+ child.dispose!
67
71
  end
68
72
 
69
73
  def alphas
@@ -81,17 +85,20 @@ module Wongi::Engine
81
85
  # end
82
86
  # end
83
87
 
84
- def default_overlay
85
- @default_overlay ||= DataOverlay.new(self)
88
+ def base_overlay
89
+ @base_overlay ||= Overlay.new(self)
86
90
  end
87
91
 
92
+ # TODO: deprecate this
93
+ alias default_overlay base_overlay
94
+
88
95
  # @private
89
- def add_overlay(o)
96
+ private def add_overlay(o)
90
97
  overlays << o
91
98
  end
92
99
 
93
100
  # @private
94
- def remove_overlay(o)
101
+ private def remove_overlay(o)
95
102
  overlays.delete(o) unless o == default_overlay
96
103
  end
97
104
 
@@ -100,11 +107,6 @@ module Wongi::Engine
100
107
  overlays.last
101
108
  end
102
109
 
103
- # @private
104
- def overlays
105
- @overlays ||= []
106
- end
107
-
108
110
  def assert(wme)
109
111
  default_overlay.assert(wme)
110
112
  end
@@ -115,65 +117,23 @@ module Wongi::Engine
115
117
 
116
118
  # @private
117
119
  def real_assert(wme)
118
- wme = wme.import_into self unless wme.rete == self
119
-
120
- # source = best_alpha(wme)
121
- if (existing = find(wme.subject, wme.predicate, wme.object))
122
- existing.manual! if wme.manual?
123
- return
124
- end
125
-
126
120
  alphas_for(wme).each { |a| a.activate wme }
127
-
128
121
  wme
129
122
  end
130
123
 
131
124
  # @private
132
- def real_retract(wme, options)
133
- real = find(wme.subject, wme.predicate, wme.object)
134
- return if real.nil?
135
-
136
- if real.generated? # still some generator tokens left
137
- raise Error, "cannot retract automatic facts" unless real.manual?
138
-
139
- real.manual = false
140
- elsif options[:automatic] && real.manual?
141
- return
142
- end
143
-
144
- alphas_for(real).each { |a| a.deactivate real }
125
+ def real_retract(wme)
126
+ # p real_retract: {wme:}
127
+ alphas_for(wme).each { |a| a.deactivate wme }
145
128
  end
146
129
 
147
130
  def wmes
148
- alpha_top.wmes
131
+ current_overlay.select(:_, :_, :_)
149
132
  end
150
133
 
151
134
  alias statements wmes
152
135
  alias facts wmes
153
136
 
154
- def in_snapshot?
155
- @in_snapshot
156
- end
157
-
158
- def snapshot!
159
- @timeline.each_with_index do |slice, index|
160
- source = if index == @timeline.size - 1
161
- alpha_hash
162
- else
163
- @timeline[index + 1]
164
- end
165
- # puts "source = #{source}"
166
- wmes = {}
167
- slice.each do |key, alpha|
168
- wmes[key] = alpha.wmes
169
- in_snapshot {
170
- wmes[key].dup.each(&:destroy)
171
- }
172
- alpha.snapshot! source[key]
173
- end
174
- end
175
- end
176
-
177
137
  def rule(name = nil, &block)
178
138
  r = DSL::Rule.new(name || generate_rule_name)
179
139
  r.instance_eval(&block)
@@ -192,7 +152,7 @@ module Wongi::Engine
192
152
  else
193
153
  case something
194
154
  when Array
195
- assert(WME.new(*something).tap { |wme| wme.overlay = default_overlay })
155
+ assert(WME.new(*something))
196
156
  when WME
197
157
  assert something
198
158
  # when Wongi::RDF::Statement
@@ -223,7 +183,6 @@ module Wongi::Engine
223
183
 
224
184
  def compile_alpha(condition)
225
185
  template = Template.new :_, :_, :_
226
- time = condition.time
227
186
 
228
187
  template.subject = condition.subject unless Template.variable?(condition.subject)
229
188
  template.predicate = condition.predicate unless Template.variable?(condition.predicate)
@@ -231,25 +190,13 @@ module Wongi::Engine
231
190
 
232
191
  hash = template.hash
233
192
  # puts "COMPILED CONDITION #{condition} WITH KEY #{key}"
234
- if time.zero?
235
- return alpha_hash[hash] if alpha_hash.key?(hash)
236
- elsif @timeline[time + 1] && @timeline[time + 1].key?(hash)
237
- return @timeline[time + 1][hash]
238
- end
193
+ return alpha_hash[hash] if alpha_hash.key?(hash)
239
194
 
240
195
  alpha = AlphaMemory.new(template, self)
241
196
 
242
- if time.zero?
243
- 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({})
250
- end
251
- @timeline[time + 1][hash] = alpha
252
- end
197
+ alpha_hash[hash] = alpha
198
+ initial_fill alpha
199
+
253
200
  alpha
254
201
  end
255
202
 
@@ -259,7 +206,7 @@ module Wongi::Engine
259
206
 
260
207
  # TODO: pick an alpha with fewer candidates to go through
261
208
  def initial_fill(alpha)
262
- alpha_top.wmes.each do |wme|
209
+ default_overlay.select(:_, :_, :_).each do |wme|
263
210
  alpha.activate wme if wme =~ alpha.template
264
211
  end
265
212
  end
@@ -269,7 +216,7 @@ module Wongi::Engine
269
216
  end
270
217
 
271
218
  def prepare_query(name, conditions, parameters, actions = [])
272
- query = queries[name] = BetaMemory.new(nil)
219
+ query = queries[name] = RootNode.new(nil)
273
220
  query.rete = self
274
221
  query.seed(parameters.to_h { |param| [param, nil] })
275
222
  results[name] = build_production query, conditions, parameters, actions, true
@@ -299,8 +246,7 @@ module Wongi::Engine
299
246
  else
300
247
  raise Error, "Network#each expect a template or nothing at all"
301
248
  end
302
- source = best_alpha(template)
303
- matching = current_overlay.wmes(source).select { |wme| wme =~ template }
249
+ matching = current_overlay.select(template)
304
250
  if block_given?
305
251
  matching.each(&block)
306
252
  else
@@ -309,29 +255,20 @@ module Wongi::Engine
309
255
  end
310
256
 
311
257
  def select(s, p, o)
312
- template = Template.new(s, p, o)
313
- source = best_alpha(template)
314
- matching = current_overlay.wmes(source).select { |wme| wme =~ template }
315
- matching.each { |st| yield st.subject, st.predicate, st.object } if block_given?
316
- matching
258
+ matching = current_overlay.select(s, p, o)
259
+ if block_given?
260
+ matching.each(&block)
261
+ else
262
+ matching.each
263
+ end
317
264
  end
318
265
 
319
266
  def find(s, p, o)
320
- template = Template.new(s, p, o)
321
- source = best_alpha(template)
322
- # puts "looking for #{template} among #{source.wmes.size} triples of #{source.template}"
323
- current_overlay.wmes(source).find { |wme| wme =~ template }
267
+ current_overlay.select(s, p, o).first
324
268
  end
325
269
 
326
270
  protected
327
271
 
328
- def in_snapshot
329
- @in_snapshot = true
330
- yield
331
- ensure
332
- @in_snapshot = false
333
- end
334
-
335
272
  def generate_rule_name
336
273
  "rule_#{productions.length}"
337
274
  end
@@ -386,8 +323,10 @@ module Wongi::Engine
386
323
  # the root node should not be deleted
387
324
  return unless node.parent
388
325
 
389
- if [BetaMemory, NegNode, NccNode, NccPartner].any? { |klass| node.is_a? klass }
390
- node.tokens.first.destroy while node.tokens.first
326
+ node.tokens.dup.each do |token|
327
+ overlays.each do |overlay|
328
+ overlay.remove_own_token(token)
329
+ end
391
330
  end
392
331
 
393
332
  node.parent.children.delete node