mort666-wongi-engine 0.2.9
Sign up to get free protection for your applications and to get access to all the features.
- 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
|