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.
- checksums.yaml +7 -0
- data/.gitignore +20 -0
- data/.hgignore +6 -0
- data/.hgtags +13 -0
- data/.ruby-gemset +1 -0
- data/.travis.yml +19 -0
- data/CHANGELOG.md +106 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +27 -0
- data/Rakefile +9 -0
- data/examples/ex01.rb +23 -0
- data/examples/ex02.rb +37 -0
- data/examples/graphviz.rb +16 -0
- data/examples/rdf.n3 +6 -0
- data/examples/rdf.rb +14 -0
- data/examples/timeline.rb +48 -0
- data/lib/wongi-engine.rb +36 -0
- data/lib/wongi-engine/alpha_memory.rb +60 -0
- data/lib/wongi-engine/beta.rb +11 -0
- data/lib/wongi-engine/beta/assignment_node.rb +40 -0
- data/lib/wongi-engine/beta/beta_memory.rb +49 -0
- data/lib/wongi-engine/beta/beta_node.rb +94 -0
- data/lib/wongi-engine/beta/filter_node.rb +48 -0
- data/lib/wongi-engine/beta/join_node.rb +140 -0
- data/lib/wongi-engine/beta/ncc_node.rb +67 -0
- data/lib/wongi-engine/beta/ncc_partner.rb +40 -0
- data/lib/wongi-engine/beta/neg_node.rb +115 -0
- data/lib/wongi-engine/beta/optional_node.rb +142 -0
- data/lib/wongi-engine/beta/or_node.rb +37 -0
- data/lib/wongi-engine/beta/production_node.rb +31 -0
- data/lib/wongi-engine/compiler.rb +115 -0
- data/lib/wongi-engine/core_ext.rb +63 -0
- data/lib/wongi-engine/data_overlay.rb +144 -0
- data/lib/wongi-engine/dsl.rb +132 -0
- data/lib/wongi-engine/dsl/action/base.rb +11 -0
- data/lib/wongi-engine/dsl/action/error_generator.rb +31 -0
- data/lib/wongi-engine/dsl/action/simple_action.rb +60 -0
- data/lib/wongi-engine/dsl/action/simple_collector.rb +52 -0
- data/lib/wongi-engine/dsl/action/statement_generator.rb +46 -0
- data/lib/wongi-engine/dsl/action/trace_action.rb +49 -0
- data/lib/wongi-engine/dsl/any_rule.rb +33 -0
- data/lib/wongi-engine/dsl/assuming.rb +31 -0
- data/lib/wongi-engine/dsl/builder.rb +44 -0
- data/lib/wongi-engine/dsl/clause/assign.rb +15 -0
- data/lib/wongi-engine/dsl/clause/fact.rb +71 -0
- data/lib/wongi-engine/dsl/clause/gen.rb +17 -0
- data/lib/wongi-engine/dsl/clause/generic.rb +38 -0
- data/lib/wongi-engine/dsl/generated.rb +43 -0
- data/lib/wongi-engine/dsl/ncc_subrule.rb +17 -0
- data/lib/wongi-engine/dsl/query.rb +24 -0
- data/lib/wongi-engine/dsl/rule.rb +84 -0
- data/lib/wongi-engine/enumerators.rb +21 -0
- data/lib/wongi-engine/error.rb +22 -0
- data/lib/wongi-engine/filter.rb +6 -0
- data/lib/wongi-engine/filter/asserting_test.rb +20 -0
- data/lib/wongi-engine/filter/equality_test.rb +36 -0
- data/lib/wongi-engine/filter/filter_test.rb +18 -0
- data/lib/wongi-engine/filter/greater_than_test.rb +36 -0
- data/lib/wongi-engine/filter/inequality_test.rb +36 -0
- data/lib/wongi-engine/filter/less_than_test.rb +36 -0
- data/lib/wongi-engine/graph.rb +73 -0
- data/lib/wongi-engine/network.rb +416 -0
- data/lib/wongi-engine/network/collectable.rb +42 -0
- data/lib/wongi-engine/network/debug.rb +85 -0
- data/lib/wongi-engine/ruleset.rb +74 -0
- data/lib/wongi-engine/template.rb +78 -0
- data/lib/wongi-engine/token.rb +114 -0
- data/lib/wongi-engine/version.rb +5 -0
- data/lib/wongi-engine/wme.rb +89 -0
- data/lib/wongi-engine/wme_match_data.rb +34 -0
- data/spec/beta_node_spec.rb +29 -0
- data/spec/bug_specs/issue_4_spec.rb +141 -0
- data/spec/dataset_spec.rb +27 -0
- data/spec/dsl_spec.rb +9 -0
- data/spec/filter_specs/assert_test_spec.rb +102 -0
- data/spec/filter_specs/less_test_spec.rb +41 -0
- data/spec/generation_spec.rb +116 -0
- data/spec/high_level_spec.rb +378 -0
- data/spec/network_spec.rb +182 -0
- data/spec/overlay_spec.rb +61 -0
- data/spec/rule_specs/any_rule_spec.rb +75 -0
- data/spec/rule_specs/assign_spec.rb +88 -0
- data/spec/rule_specs/assuming_spec.rb +66 -0
- data/spec/rule_specs/maybe_rule_spec.rb +101 -0
- data/spec/rule_specs/ncc_spec.rb +258 -0
- data/spec/rule_specs/negative_rule_spec.rb +105 -0
- data/spec/ruleset_spec.rb +54 -0
- data/spec/simple_action_spec.rb +40 -0
- data/spec/spec_helper.rb +3 -0
- data/spec/wme_spec.rb +83 -0
- data/wongi-engine.gemspec +40 -0
- 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,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
|