wongi-engine 0.0.1
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.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +349 -0
- data/Rakefile +2 -0
- data/examples/ex01.rb +23 -0
- data/examples/ex02.rb +36 -0
- data/examples/graphviz.rb +15 -0
- data/examples/timeline.rb +48 -0
- data/lib/wongi-engine.rb +22 -0
- data/lib/wongi-engine/alpha_memory.rb +46 -0
- data/lib/wongi-engine/beta.rb +10 -0
- data/lib/wongi-engine/beta/beta_memory.rb +48 -0
- data/lib/wongi-engine/beta/beta_node.rb +164 -0
- data/lib/wongi-engine/beta/filter_node.rb +109 -0
- data/lib/wongi-engine/beta/join_node.rb +127 -0
- data/lib/wongi-engine/beta/ncc_node.rb +46 -0
- data/lib/wongi-engine/beta/ncc_partner.rb +43 -0
- data/lib/wongi-engine/beta/neg_node.rb +58 -0
- data/lib/wongi-engine/beta/optional_node.rb +43 -0
- data/lib/wongi-engine/beta/or_node.rb +76 -0
- data/lib/wongi-engine/beta/production_node.rb +31 -0
- data/lib/wongi-engine/core_ext.rb +57 -0
- data/lib/wongi-engine/dsl.rb +112 -0
- data/lib/wongi-engine/dsl/action.rb +12 -0
- data/lib/wongi-engine/dsl/actions/error_generator.rb +42 -0
- data/lib/wongi-engine/dsl/actions/simple_action.rb +23 -0
- data/lib/wongi-engine/dsl/actions/simple_collector.rb +51 -0
- data/lib/wongi-engine/dsl/actions/statement_generator.rb +52 -0
- data/lib/wongi-engine/dsl/actions/trace_action.rb +52 -0
- data/lib/wongi-engine/dsl/any_rule.rb +48 -0
- data/lib/wongi-engine/dsl/dsl_builder.rb +44 -0
- data/lib/wongi-engine/dsl/dsl_extensions.rb +43 -0
- data/lib/wongi-engine/dsl/extension_clause.rb +36 -0
- data/lib/wongi-engine/dsl/generation_clause.rb +15 -0
- data/lib/wongi-engine/dsl/generic_production_rule.rb +78 -0
- data/lib/wongi-engine/dsl/ncc_production_rule.rb +21 -0
- data/lib/wongi-engine/dsl/production_rule.rb +4 -0
- data/lib/wongi-engine/dsl/query.rb +24 -0
- data/lib/wongi-engine/graph.rb +71 -0
- data/lib/wongi-engine/model_context.rb +13 -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 +25 -0
- data/lib/wongi-engine/ruleset.rb +74 -0
- data/lib/wongi-engine/template.rb +111 -0
- data/lib/wongi-engine/token.rb +137 -0
- data/lib/wongi-engine/version.rb +5 -0
- data/lib/wongi-engine/wme.rb +134 -0
- data/lib/wongi-engine/wme_match_data.rb +34 -0
- data/spec/dataset_spec.rb +26 -0
- data/spec/dsl_spec.rb +9 -0
- data/spec/high_level_spec.rb +341 -0
- data/spec/ruleset_spec.rb +54 -0
- data/spec/simple_action_spec.rb +40 -0
- data/spec/spec_helper.rb +1 -0
- data/spec/wme_spec.rb +83 -0
- data/wongi-engine.gemspec +19 -0
- metadata +110 -0
@@ -0,0 +1,43 @@
|
|
1
|
+
module Wongi::Engine
|
2
|
+
module DSLExtensions
|
3
|
+
|
4
|
+
def self.create_extension extension
|
5
|
+
|
6
|
+
section = extension[:section]
|
7
|
+
clause = extension[:clause]
|
8
|
+
action = extension[:action]
|
9
|
+
body = extension[:body]
|
10
|
+
acceptor = extension[:accept]
|
11
|
+
|
12
|
+
define_method clause.first do |*args, &block|
|
13
|
+
|
14
|
+
raise "#{clause.first} can only be used in section #{section}, currently in #{@current_section}" if section != @current_section
|
15
|
+
|
16
|
+
if body
|
17
|
+
|
18
|
+
instance_exec *args, &body
|
19
|
+
|
20
|
+
elsif acceptor
|
21
|
+
|
22
|
+
accept acceptor.new( *args, &block )
|
23
|
+
|
24
|
+
elsif action
|
25
|
+
|
26
|
+
c = ExtensionClause.new *args, &block
|
27
|
+
c.name = clause.first
|
28
|
+
c.action = action
|
29
|
+
c.rule = self
|
30
|
+
accept c
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
clause[1..-1].each do |al|
|
37
|
+
alias_method al, clause.first
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Wongi::Engine
|
2
|
+
class ExtensionClause
|
3
|
+
|
4
|
+
attr_accessor :name, :action, :rule
|
5
|
+
|
6
|
+
def initialize *args, &block
|
7
|
+
@args = args
|
8
|
+
@block = block
|
9
|
+
end
|
10
|
+
|
11
|
+
def import_into rete
|
12
|
+
if action.respond_to? :call
|
13
|
+
self
|
14
|
+
else
|
15
|
+
a = action.new *@args, &@block
|
16
|
+
a.name = name if a.respond_to? :name=
|
17
|
+
a.rule = rule if a.respond_to? :rule=
|
18
|
+
a.rete = rete if a.respond_to? :rete=
|
19
|
+
a
|
20
|
+
end
|
21
|
+
rescue Exception => e
|
22
|
+
e1 = Exception.new "error defining clause #{name} handled by #{action}: #{e}"
|
23
|
+
e1.set_backtrace e.backtrace
|
24
|
+
raise e1
|
25
|
+
end
|
26
|
+
|
27
|
+
def compile *args
|
28
|
+
action.call *args
|
29
|
+
end
|
30
|
+
|
31
|
+
def execute *args
|
32
|
+
action.call *args
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Wongi::Engine
|
2
|
+
class GenerationClause
|
3
|
+
|
4
|
+
def initialize s, p, o
|
5
|
+
@triple = Template.new( s, p, o )
|
6
|
+
end
|
7
|
+
|
8
|
+
def import_into rete
|
9
|
+
generator = StatementGenerator.new @triple.import_into( rete )
|
10
|
+
generator.rete = rete
|
11
|
+
generator
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module Wongi::Engine
|
2
|
+
class GenericProductionRule
|
3
|
+
|
4
|
+
attr_reader :name
|
5
|
+
attr_reader :conditions, :actions
|
6
|
+
|
7
|
+
include DSLExtensions
|
8
|
+
|
9
|
+
class << self
|
10
|
+
|
11
|
+
def section s, *aliases
|
12
|
+
unless sections.include?(s)
|
13
|
+
sections << s
|
14
|
+
define_method s do |&d|
|
15
|
+
@current_section = s
|
16
|
+
instance_eval &d
|
17
|
+
end
|
18
|
+
aliases.each { |a| alias_method a, s }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def sections
|
23
|
+
@sections ||= []
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
section :forall, :for_all
|
29
|
+
section :make, :do!
|
30
|
+
|
31
|
+
def initialize name
|
32
|
+
@name = name
|
33
|
+
@conditions = []
|
34
|
+
@actions = []
|
35
|
+
@current_section = nil
|
36
|
+
@acceptors = {}
|
37
|
+
GenericProductionRule.sections.each { |section| @acceptors[section] ||= [] }
|
38
|
+
end
|
39
|
+
|
40
|
+
def import_into rete
|
41
|
+
|
42
|
+
copy = self.class.new @name
|
43
|
+
|
44
|
+
copy.conditions = @acceptors[:forall].map do |condition|
|
45
|
+
if condition.respond_to? :import_into
|
46
|
+
condition.import_into(rete)
|
47
|
+
else
|
48
|
+
condition
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
copy.actions = @acceptors[:make].map do |action|
|
53
|
+
if action.respond_to? :import_into
|
54
|
+
action.import_into(rete)
|
55
|
+
else
|
56
|
+
action
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
copy
|
61
|
+
rescue Exception => e
|
62
|
+
e1 = Exception.new "in rule #{name}: #{e}"
|
63
|
+
e1.set_backtrace e.backtrace
|
64
|
+
raise e1
|
65
|
+
end
|
66
|
+
|
67
|
+
protected
|
68
|
+
attr_writer :conditions, :actions
|
69
|
+
|
70
|
+
def accept stuff
|
71
|
+
@acceptors[@current_section] << stuff
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
|
78
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Wongi::Engine
|
2
|
+
class NccProductionRule < GenericProductionRule
|
3
|
+
|
4
|
+
def initialize name = nil, &block
|
5
|
+
super
|
6
|
+
if block
|
7
|
+
forall &block
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def import_into rete
|
12
|
+
NccSet.new @acceptors[:forall].map { |condition|
|
13
|
+
if condition.respond_to? :import_into
|
14
|
+
condition.import_into(rete)
|
15
|
+
else
|
16
|
+
condition
|
17
|
+
end
|
18
|
+
}
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Wongi::Engine
|
2
|
+
|
3
|
+
class Query < GenericProductionRule
|
4
|
+
|
5
|
+
attr_reader :parameters
|
6
|
+
|
7
|
+
def initialize name
|
8
|
+
super
|
9
|
+
@parameters = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def search_on *terms
|
13
|
+
terms.each { |term| @parameters << term }
|
14
|
+
end
|
15
|
+
|
16
|
+
def import_into model
|
17
|
+
copy = super
|
18
|
+
copy.search_on *@parameters
|
19
|
+
copy
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1,71 @@
|
|
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
|
+
if String === io
|
12
|
+
File.open io, "w" do |actual_io|
|
13
|
+
dot actual_io
|
14
|
+
end
|
15
|
+
return
|
16
|
+
end
|
17
|
+
|
18
|
+
@io = io
|
19
|
+
|
20
|
+
@io.puts "digraph {"
|
21
|
+
|
22
|
+
dump_alphas(opts) unless opts[:alpha] == false
|
23
|
+
dump_betas(opts)
|
24
|
+
|
25
|
+
@io.puts "};"
|
26
|
+
|
27
|
+
ensure
|
28
|
+
@io = nil
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def print_hash h
|
34
|
+
h.to_s.gsub /-/, '_'
|
35
|
+
end
|
36
|
+
|
37
|
+
def dump_alphas opts
|
38
|
+
@io.puts "subgraph cluster_alphas {"
|
39
|
+
@rete.alphas.select { |alpha| not alpha.betas.empty? }.each do |alpha|
|
40
|
+
@io.puts "node#{print_hash alpha.hash} [shape=box label=\"#{alpha.template.to_s.gsub /"/, "\\\""}\"];"
|
41
|
+
end
|
42
|
+
@io.puts "};"
|
43
|
+
end
|
44
|
+
|
45
|
+
def dump_betas opts
|
46
|
+
dump_beta @rete.beta_top, opts
|
47
|
+
end
|
48
|
+
|
49
|
+
def dump_beta beta, opts
|
50
|
+
@io.puts "node#{print_hash beta.hash} [label=\"#{beta.class.name.split('::').last}\"];"
|
51
|
+
if beta.parent
|
52
|
+
@io.puts "node#{print_hash beta.parent.hash} -> node#{print_hash beta.hash};"
|
53
|
+
end
|
54
|
+
if beta.is_a? NccNode
|
55
|
+
@io.puts "node#{print_hash beta.partner.hash} -> node#{print_hash beta.hash};"
|
56
|
+
@io.puts "{ rank=same; node#{print_hash beta.partner.hash} node#{print_hash beta.hash} }"
|
57
|
+
end
|
58
|
+
if beta.respond_to? :alpha and opts[:alpha] != false
|
59
|
+
alpha = beta.alpha
|
60
|
+
if alpha
|
61
|
+
@io.puts "node#{print_hash alpha.hash} -> node#{print_hash beta.hash};"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
beta.children.each do |child|
|
65
|
+
dump_beta child, opts
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
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
|
+
|
11
|
+
include NetworkParts::Collectable
|
12
|
+
|
13
|
+
protected
|
14
|
+
attr_accessor :alpha_hash
|
15
|
+
attr_writer :alpha_top, :beta_top
|
16
|
+
attr_writer :queries, :results
|
17
|
+
|
18
|
+
public
|
19
|
+
|
20
|
+
def debug!
|
21
|
+
extend NetworkParts::Debug
|
22
|
+
end
|
23
|
+
|
24
|
+
def initialize
|
25
|
+
@timeline = []
|
26
|
+
self.alpha_top = AlphaMemory.new( Template.new( :_, :_, :_ ), self )
|
27
|
+
self.alpha_hash = { Template.hash_for( :_, :_, :_ ) => self.alpha_top }
|
28
|
+
self.beta_top = BetaMemory.new(nil)
|
29
|
+
self.beta_top.rete = self
|
30
|
+
self.beta_top.seed
|
31
|
+
self.queries = {}
|
32
|
+
self.results = {}
|
33
|
+
@cache = {}
|
34
|
+
@revns = {}
|
35
|
+
@contexts = {}
|
36
|
+
|
37
|
+
@productions = { }
|
38
|
+
|
39
|
+
@collectors = {}
|
40
|
+
@collectors[:error] = []
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
def dump
|
45
|
+
beta_top.dump
|
46
|
+
end
|
47
|
+
|
48
|
+
def alphas
|
49
|
+
alpha_hash.values
|
50
|
+
end
|
51
|
+
|
52
|
+
def import thing
|
53
|
+
case thing
|
54
|
+
when String, Numeric, TrueClass, FalseClass, NilClass
|
55
|
+
thing
|
56
|
+
when Symbol
|
57
|
+
thing
|
58
|
+
else
|
59
|
+
raise "I don't know how to import a #{thing.class}"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def assert wme
|
64
|
+
|
65
|
+
unless wme.rete == self
|
66
|
+
wme = wme.import_into self
|
67
|
+
end
|
68
|
+
|
69
|
+
if @current_context
|
70
|
+
@current_context.asserted_wmes << wme
|
71
|
+
wme.context = @current_context
|
72
|
+
end
|
73
|
+
|
74
|
+
return if @cache.has_key?(wme)
|
75
|
+
|
76
|
+
# puts "ASSERTING #{wme}"
|
77
|
+
@cache[wme] = wme
|
78
|
+
|
79
|
+
s = wme.subject
|
80
|
+
p = wme.predicate
|
81
|
+
o = wme.object
|
82
|
+
|
83
|
+
alpha_activate(lookup( s, p, o), wme)
|
84
|
+
alpha_activate(lookup( s, p, :_), wme)
|
85
|
+
alpha_activate(lookup( s, :_, o), wme)
|
86
|
+
alpha_activate(lookup(:_, p, o), wme)
|
87
|
+
alpha_activate(lookup( s, :_, :_), wme)
|
88
|
+
alpha_activate(lookup(:_, p, :_), wme)
|
89
|
+
alpha_activate(lookup(:_, :_, o), wme)
|
90
|
+
alpha_activate(lookup(:_, :_, :_), wme)
|
91
|
+
|
92
|
+
wme
|
93
|
+
end
|
94
|
+
|
95
|
+
def wmes
|
96
|
+
alpha_top.wmes
|
97
|
+
end
|
98
|
+
alias_method :statements, :wmes
|
99
|
+
alias_method :facts, :wmes
|
100
|
+
|
101
|
+
def in_snapshot?
|
102
|
+
@in_snapshot
|
103
|
+
end
|
104
|
+
|
105
|
+
def snapshot!
|
106
|
+
@timeline.each_with_index do |slice, index|
|
107
|
+
source = if index == @timeline.size - 1
|
108
|
+
alpha_hash
|
109
|
+
else
|
110
|
+
@timeline[index+1]
|
111
|
+
end
|
112
|
+
# puts "source = #{source}"
|
113
|
+
wmes = {}
|
114
|
+
slice.each { |key, alpha| wmes[key] = alpha.wmes }
|
115
|
+
slice.each do |key, alpha|
|
116
|
+
in_snapshot {
|
117
|
+
wmes[key].dup.each { |wme| wme.destroy }
|
118
|
+
}
|
119
|
+
alpha.snapshot! source[key]
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def rule name = nil, &block
|
125
|
+
r = ProductionRule.new( name || generate_rule_name )
|
126
|
+
r.instance_eval &block
|
127
|
+
self << r
|
128
|
+
end
|
129
|
+
|
130
|
+
def query name, &block
|
131
|
+
q = Query.new name
|
132
|
+
q.instance_eval &block
|
133
|
+
self << q
|
134
|
+
end
|
135
|
+
|
136
|
+
def << something
|
137
|
+
case something
|
138
|
+
when Array
|
139
|
+
if something.length == 3
|
140
|
+
assert WME.new( *something )
|
141
|
+
else
|
142
|
+
raise "Arrays must have 3 elements"
|
143
|
+
end
|
144
|
+
when ProductionRule
|
145
|
+
derived = something.import_into self
|
146
|
+
production = add_production derived.conditions, derived.actions
|
147
|
+
if something.name
|
148
|
+
productions[ something.name ] = production
|
149
|
+
end
|
150
|
+
when Query
|
151
|
+
derived = something.import_into self
|
152
|
+
prepare_query derived.name, derived.conditions, derived.parameters, derived.actions
|
153
|
+
when Ruleset
|
154
|
+
something.install self
|
155
|
+
when WME
|
156
|
+
assert something
|
157
|
+
#when Wongi::RDF::Document
|
158
|
+
# something.statements.each do |st|
|
159
|
+
# assert WME.new( st.subject, st.predicate, st.object, self )
|
160
|
+
# end
|
161
|
+
when Rete
|
162
|
+
something.each do |st|
|
163
|
+
assert st.import_into( self )
|
164
|
+
end
|
165
|
+
else
|
166
|
+
raise "I don't know how to accept a #{something.class}"
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
def retract wme, is_real = false
|
171
|
+
|
172
|
+
if ! is_real
|
173
|
+
if @current_context
|
174
|
+
@current_context.retracted_wmes << wme
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
real = if is_real
|
179
|
+
wme
|
180
|
+
else
|
181
|
+
#find(wme.subject, wme.predicate, wme.object)
|
182
|
+
@cache[wme]
|
183
|
+
end
|
184
|
+
|
185
|
+
return false if real.nil?
|
186
|
+
@cache.delete(real)
|
187
|
+
raise "Cannot retract inferred statements" unless real.manual?
|
188
|
+
|
189
|
+
real.destroy
|
190
|
+
|
191
|
+
true
|
192
|
+
|
193
|
+
end
|
194
|
+
|
195
|
+
def compile_alpha condition
|
196
|
+
template = Template.new :_, :_, :_
|
197
|
+
time = condition.time
|
198
|
+
|
199
|
+
template.subject = condition.subject unless Template.variable?( condition.subject )
|
200
|
+
template.predicate = condition.predicate unless Template.variable?( condition.predicate )
|
201
|
+
template.object = condition.object unless Template.variable?( condition.object )
|
202
|
+
|
203
|
+
hash = template.hash
|
204
|
+
# puts "COMPILED CONDITION #{condition} WITH KEY #{key}"
|
205
|
+
if time == 0
|
206
|
+
return self.alpha_hash[ hash ] if self.alpha_hash.has_key?( hash )
|
207
|
+
else
|
208
|
+
return @timeline[time+1][ hash ] if @timeline[time+1] && @timeline[time+1].has_key?( hash )
|
209
|
+
end
|
210
|
+
|
211
|
+
alpha = AlphaMemory.new( template, self )
|
212
|
+
|
213
|
+
if time == 0
|
214
|
+
self.alpha_hash[ hash ] = alpha
|
215
|
+
initial_fill alpha
|
216
|
+
else
|
217
|
+
if @timeline[time+1].nil?
|
218
|
+
# => ensure lineage from 0 to time
|
219
|
+
compile_alpha condition.class.new(condition.subject, condition.predicate, condition.object, time + 1)
|
220
|
+
@timeline.unshift Hash.new
|
221
|
+
end
|
222
|
+
@timeline[time+1][ hash ] = alpha
|
223
|
+
end
|
224
|
+
alpha
|
225
|
+
end
|
226
|
+
|
227
|
+
def cache s, p, o
|
228
|
+
compile_alpha Template.new(s, p, o).import_into( self )
|
229
|
+
end
|
230
|
+
|
231
|
+
def initial_fill alpha
|
232
|
+
tpl = alpha.template
|
233
|
+
source = more_generic_alpha(tpl)
|
234
|
+
# puts "more efficient by #{alpha_top.wmes.size - source.wmes.size}" unless source ==
|
235
|
+
# alpha_top
|
236
|
+
source.wmes.each do |wme|
|
237
|
+
alpha.activate wme if wme =~ tpl
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
def add_production conditions, actions = []
|
242
|
+
real_add_production self.beta_top, conditions, [], actions, false
|
243
|
+
end
|
244
|
+
|
245
|
+
def remove_production pnode
|
246
|
+
delete_node_with_ancestors pnode
|
247
|
+
end
|
248
|
+
|
249
|
+
def prepare_query name, conditions, parameters, actions = []
|
250
|
+
query = self.queries[ name ] = BetaMemory.new( nil )
|
251
|
+
query.rete = self
|
252
|
+
transformed = {}
|
253
|
+
parameters.each { |param| transformed[param] = nil }
|
254
|
+
query.seed transformed
|
255
|
+
self.results[ name ] = real_add_production query, conditions, parameters, actions, true
|
256
|
+
end
|
257
|
+
|
258
|
+
def execute name, valuations
|
259
|
+
beta = self.queries[name]
|
260
|
+
raise "Undefined query #{name}; known queries are #{queries.keys}" unless beta
|
261
|
+
beta.subst valuations
|
262
|
+
end
|
263
|
+
|
264
|
+
def inspect
|
265
|
+
"<Rete>"
|
266
|
+
end
|
267
|
+
|
268
|
+
def context= name
|
269
|
+
if name && !@contexts.has_key?(name)
|
270
|
+
@current_context = (@contexts[name] ||= ModelContext.new name)
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
def retract_context name
|
275
|
+
return unless @contexts.has_key?(name)
|
276
|
+
|
277
|
+
if @current_context && @current_context.name == name
|
278
|
+
@current_context = nil
|
279
|
+
end
|
280
|
+
ctx = @contexts[name]
|
281
|
+
ctx.asserted_wmes.select { |wme| wme.generating_tokens.empty? }.each { |wme| retract(wme, true) }
|
282
|
+
ctx.retracted_wmes.each { |wme| assert(wme) }
|
283
|
+
@contexts.delete name
|
284
|
+
end
|
285
|
+
|
286
|
+
def exists? wme
|
287
|
+
@cache[ wme ]
|
288
|
+
end
|
289
|
+
|
290
|
+
def each *args
|
291
|
+
return unless block_given?
|
292
|
+
unless args.length == 0 || args.length == 3
|
293
|
+
raise "Document#each expects a pattern or nothing at all"
|
294
|
+
end
|
295
|
+
s, p, o = if args.empty?
|
296
|
+
[:_, :_, :_]
|
297
|
+
else
|
298
|
+
args
|
299
|
+
end
|
300
|
+
no_check = s == :_ && p == :_ && o == :_
|
301
|
+
template = Template.new(s, p, o).import_into self
|
302
|
+
alpha_top.wmes.each do |wme|
|
303
|
+
yield wme if (no_check || wme =~ template)
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
def select s, p, o
|
308
|
+
template = Template.new(s, p, o).import_into self
|
309
|
+
matching = alpha_top.wmes.select { |wme| wme =~ template }
|
310
|
+
if block_given?
|
311
|
+
matching.each { |st| yield st.subject, st.predicate, st.object }
|
312
|
+
end
|
313
|
+
matching
|
314
|
+
end
|
315
|
+
|
316
|
+
def find s, p, o
|
317
|
+
template = Template.new(s, p, o).import_into self
|
318
|
+
source = best_alpha(template)
|
319
|
+
# puts "looking for #{template} among #{source.wmes.size} triples of #{source.template}"
|
320
|
+
source.wmes.detect { |wme| wme =~ template }
|
321
|
+
end
|
322
|
+
|
323
|
+
protected
|
324
|
+
|
325
|
+
def in_snapshot
|
326
|
+
@in_snapshot = true
|
327
|
+
yield
|
328
|
+
ensure
|
329
|
+
@in_snapshot = false
|
330
|
+
end
|
331
|
+
|
332
|
+
def generate_rule_name
|
333
|
+
"rule_#{productions.length}"
|
334
|
+
end
|
335
|
+
|
336
|
+
def lookup s, p, o
|
337
|
+
key = Template.hash_for(s, p, o)
|
338
|
+
# puts "Lookup for #{key}"
|
339
|
+
self.alpha_hash[ key ]
|
340
|
+
end
|
341
|
+
|
342
|
+
def alpha_activate alpha, wme
|
343
|
+
alpha.activate(wme) if alpha
|
344
|
+
end
|
345
|
+
|
346
|
+
def more_generic_alpha template
|
347
|
+
return alpha_top # OPTIMISE => temporary; may use later or not use at all
|
348
|
+
return alpha_top if template.root?
|
349
|
+
more_generic_templates(template).reduce alpha_top do |best, template|
|
350
|
+
alpha = alpha_hash[template.hash]
|
351
|
+
if alpha && alpha.wmes.size < best.wmes.size
|
352
|
+
alpha
|
353
|
+
else
|
354
|
+
best
|
355
|
+
end
|
356
|
+
end
|
357
|
+
end
|
358
|
+
|
359
|
+
def more_generic_templates template
|
360
|
+
set = []
|
361
|
+
set << template.with_subject( :_ ) unless template.subject == :_
|
362
|
+
set << template.with_predicate( :_ ) unless template.predicate == :_
|
363
|
+
set << template.with_object( :_ ) unless template.object == :_
|
364
|
+
set.select { |item| not item.root? }
|
365
|
+
end
|
366
|
+
|
367
|
+
def best_alpha template
|
368
|
+
raise
|
369
|
+
candidates = alpha_hash.values.select do |alpha|
|
370
|
+
template =~ alpha.template
|
371
|
+
end
|
372
|
+
result = candidates.inject do |best, alpha|
|
373
|
+
if best.nil?
|
374
|
+
alpha
|
375
|
+
elsif alpha.wmes.length < best.wmes.length
|
376
|
+
alpha
|
377
|
+
else
|
378
|
+
best
|
379
|
+
end
|
380
|
+
end
|
381
|
+
puts "Best alpha for #{template} is #{result}"
|
382
|
+
result
|
383
|
+
end
|
384
|
+
|
385
|
+
def real_add_production root, conditions, parameters, actions, alpha_deaf
|
386
|
+
beta = root.network conditions, [], parameters, alpha_deaf
|
387
|
+
|
388
|
+
production = ProductionNode.new( beta, actions )
|
389
|
+
production.update_above
|
390
|
+
production
|
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
|