wongi-engine 0.0.17 → 0.1.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,45 +1,107 @@
1
1
  module Wongi
2
2
  module Engine
3
3
 
4
- OptionalJoinResult = Struct.new :owner, :wme
4
+ OptionalJoinResult = Struct.new :token, :wme do
5
+ def unlink
6
+ wme.opt_join_results.delete self
7
+ token.opt_join_results.delete self
8
+ end
9
+ end
10
+
11
+ class OptionalNode < BetaNode
12
+
13
+ attr_reader :alpha, :tests, :assignments, :tokens
5
14
 
6
- class OptionalNode < JoinNode
15
+ def initialize parent, alpha, tests, assignments
16
+ super( parent )
17
+ @alpha = alpha
18
+ @tests = tests
19
+ @assignments = assignments
20
+ @tokens = []
21
+ end
7
22
 
8
- def initialize parent, tests, assignment
9
- super parent, tests, assignment, []
23
+ def make_opt_result token, wme
24
+ jr = OptionalJoinResult.new token, wme
25
+ token.opt_join_results << jr
26
+ wme.opt_join_results << jr
10
27
  end
11
28
 
12
29
  def alpha_activate wme
13
- parent.tokens.each do |token|
30
+ assignments = collect_assignments( wme )
31
+ self.tokens.each do |token|
14
32
  if matches? token, wme
15
- if token.has_optional?
16
- token.has_optional = false
17
- token.delete_children
33
+ children.each do |child|
34
+ if token.optional?
35
+ token.no_optional!
36
+ child.tokens.select { |ct| ct.parent == token }.each do |ct|
37
+ child.beta_deactivate ct
38
+ end
39
+ end
40
+ child.beta_activate Token.new( child, token, wme, assignments )
18
41
  end
19
- propagate_activation(token, wme, collect_assignments(wme))
20
- jr = OptionalJoinResult.new token, wme
21
- token.opt_join_results << jr
22
- wme.opt_join_results << jr
42
+ make_opt_result token, wme
23
43
  end
24
44
  end
25
45
  end
26
46
 
27
- def beta_activate token
47
+ def alpha_deactivate wme
48
+ wme.opt_join_results.dup.each do |ojr|
49
+ safe_tokens.select { |token| token == ojr.token }.each do |token|
50
+ ojr.unlink
51
+ if token.opt_join_results.empty?
52
+ children.each do |child|
53
+ child.tokens.select { |ct| ct.parent == token }.each do |ct|
54
+ child.beta_deactivate ct
55
+ end
56
+ token.optional!
57
+ child.beta_activate Token.new( child, token, nil, { } )
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ def beta_activate t
65
+ return if @tokens.find { |token| token.parent == t }
66
+ token = Token.new( self, t, nil, { } )
67
+ @tokens << token
28
68
  match = false
29
69
  alpha.wmes.each do |wme|
30
70
  assignments = collect_assignments(wme)
31
71
  if matches? token, wme
32
72
  match = true
33
- propagate_activation(token, wme, assignments)
34
- jr = OptionalJoinResult.new token, wme
35
- token.opt_join_results << jr
36
- wme.opt_join_results << jr
73
+ children.each do |child|
74
+ child.beta_activate Token.new( child, token, wme, assignments )
75
+ end
76
+ make_opt_result token, wme
37
77
  end
38
78
  end
39
79
  unless match
40
- token.has_optional = true
41
- propagate_activation(token, nil, {})
80
+ token.optional!
81
+ children.each do |child|
82
+ child.beta_activate Token.new( child, token, nil, { } )
83
+ end
84
+ end
85
+ end
86
+
87
+ def beta_deactivate t
88
+ token = @tokens.find { |token| token.parent == t }
89
+ return unless token
90
+ return unless @tokens.delete token
91
+ token.deleted!
92
+ if token.parent
93
+ token.parent.children.delete token
42
94
  end
95
+ token.opt_join_results.each &:unlink
96
+ children.each do |child|
97
+ child.tokens.each do |t|
98
+ if t.parent == token
99
+ child.beta_deactivate t
100
+ #token.destroy
101
+ end
102
+ end
103
+ end
104
+ token
43
105
  end
44
106
 
45
107
  def refresh_child child
@@ -60,6 +122,36 @@ module Wongi
60
122
  token.opt_join_results.clear
61
123
  end
62
124
 
125
+ private
126
+
127
+ def matches? token, wme
128
+ @tests.each do |test|
129
+ return false unless test.matches?( token, wme )
130
+ end
131
+ true
132
+ end
133
+
134
+ def collect_assignments wme
135
+ assignments = {}
136
+ return assignments if self.assignments.nil?
137
+ # puts "more assignments"
138
+ [:subject, :predicate, :object].each do |field|
139
+ if self.assignments.send(field) != :_
140
+ #puts "#{self.assignments.send(field)} = #{wme.send(field)}"
141
+ assignments[ self.assignments.send(field) ] = TokenAssignment.new( wme, field )
142
+ end
143
+ end
144
+ assignments
145
+ end
146
+
147
+ def safe_tokens
148
+ Enumerator.new do |y|
149
+ @tokens.dup.each do |token|
150
+ y << token unless token.deleted?
151
+ end
152
+ end
153
+ end
154
+
63
155
  end
64
156
  end
65
157
  end
@@ -7,17 +7,20 @@ module Wongi
7
7
 
8
8
  def initialize parent, actions
9
9
  super(parent)
10
- @actions = actions
11
- @actions.each { |action| action.production = self }
10
+ @actions = actions.each { |action| action.production = self }
12
11
  end
13
12
 
14
- def beta_activate token, wme, assignments
15
- generated = super
13
+ def beta_activate token
14
+ return unless super
16
15
  @actions.each do |action|
17
- # @tokens.each do |t|
18
- # action.execute t
19
- # end
20
- action.execute generated if action.respond_to? :execute
16
+ action.execute token if action.respond_to? :execute
17
+ end
18
+ end
19
+
20
+ def beta_deactivate token
21
+ return unless super
22
+ @actions.each do |action|
23
+ action.deexecute token if action.respond_to? :deexecute
21
24
  end
22
25
  end
23
26
 
@@ -46,6 +46,12 @@ module Wongi::Engine
46
46
  end
47
47
  end
48
48
 
49
+ def abstract name
50
+ define_method name do |*args|
51
+ raise NoMethodError.new "#{name} is not implemented for #{self.class.name}", name
52
+ end
53
+ end
54
+
49
55
  end
50
56
 
51
57
  def self.included mod
@@ -3,14 +3,20 @@ module Wongi::Engine
3
3
  class SimpleAction < Action
4
4
 
5
5
  def initialize action = nil, *args, &block
6
- @action = if action.is_a? Class
7
- action.new *args, &block
8
- else
9
- action || block
6
+ @args = args
7
+ case action
8
+ when Class
9
+ @action = @deaction = @reaction = action.new *args, &block
10
+ when Hash
11
+ @action = instance_or_proc action[:activate]
12
+ @deaction = instance_or_proc action[:deactivate]
13
+ @reaction = instance_or_proc action[:reactivate]
10
14
  end
15
+ @action ||= block
11
16
  end
12
17
 
13
18
  def execute token
19
+ return unless @action
14
20
  if @action.is_a?( Proc ) || @action.respond_to?( :to_proc )
15
21
  rete.instance_exec token, &@action
16
22
  elsif @action.respond_to? :call
@@ -20,6 +26,37 @@ module Wongi::Engine
20
26
  end
21
27
  end
22
28
 
29
+ def deexecute token
30
+ return unless @deaction
31
+ if @deaction.is_a?( Proc ) || @deaction.respond_to?( :to_proc )
32
+ rete.instance_exec token, &@deaction
33
+ elsif @deaction.respond_to? :call
34
+ @deaction.call token
35
+ elsif @deaction.respond_to? :deexecute
36
+ @deaction.execute token
37
+ end
38
+ end
39
+
40
+ def reexecute token, newtoken
41
+ return unless @reaction
42
+ if @reaction.is_a?( Proc ) || @reaction.respond_to?( :to_proc )
43
+ rete.instance_exec token, newtoken, &@reaction
44
+ elsif @reaction.respond_to? :call
45
+ @reaction.call token, newtoken
46
+ elsif @reaction.respond_to? :reexecute
47
+ @reaction.execute token, newtoken
48
+ end
49
+ end
50
+
51
+ def instance_or_proc thing
52
+ case thing
53
+ when Class
54
+ thing.new
55
+ when Proc
56
+ thing
57
+ end
58
+ end
59
+
23
60
  end
24
61
 
25
62
  end
@@ -51,5 +51,16 @@ module Wongi::Engine
51
51
 
52
52
  end
53
53
 
54
+ def deexecute token
55
+ token.generated_wmes.reject( &:manual? ).inject( [] ) do |list, wme|
56
+ list.tap do |l|
57
+ wme.generating_tokens.delete token
58
+ l << wme if wme.generating_tokens.empty?
59
+ end
60
+ end.each do |wme|
61
+ rete.retract wme
62
+ end
63
+ end
64
+
54
65
  end
55
66
  end
@@ -83,6 +83,43 @@ module Wongi::Engine
83
83
  end
84
84
 
85
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
+ end
94
+
95
+ def retract wme
96
+ @next_cascade ||= []
97
+ @next_cascade << [:retract, wme]
98
+ if @current_cascade.nil?
99
+ @current_cascade = @next_cascade
100
+ @next_cascade = nil
101
+ process_cascade
102
+ end
103
+ end
104
+
105
+ def process_cascade
106
+ iterations = 0
107
+ while @current_cascade
108
+ @current_cascade.each do |(operation, wme)|
109
+ case operation
110
+ when :assert
111
+ real_assert wme
112
+ when :retract
113
+ real_retract wme
114
+ end
115
+ end
116
+ @current_cascade = @next_cascade
117
+ @next_cascade = nil
118
+ iterations += 1
119
+ end
120
+ end
121
+
122
+ def real_assert wme
86
123
 
87
124
  unless wme.rete == self
88
125
  wme = wme.import_into self
@@ -98,18 +135,7 @@ module Wongi::Engine
98
135
  # puts "ASSERTING #{wme}"
99
136
  @cache[wme] = wme
100
137
 
101
- s = wme.subject
102
- p = wme.predicate
103
- o = wme.object
104
-
105
- alpha_activate(lookup( s, p, o), wme)
106
- alpha_activate(lookup( s, p, :_), wme)
107
- alpha_activate(lookup( s, :_, o), wme)
108
- alpha_activate(lookup(:_, p, o), wme)
109
- alpha_activate(lookup( s, :_, :_), wme)
110
- alpha_activate(lookup(:_, p, :_), wme)
111
- alpha_activate(lookup(:_, :_, o), wme)
112
- alpha_activate(lookup(:_, :_, :_), wme)
138
+ alphas_for( wme ).each { |a| a.activate wme }
113
139
 
114
140
  wme
115
141
  end
@@ -192,10 +218,10 @@ module Wongi::Engine
192
218
  end
193
219
  end
194
220
 
195
- def retract wme, is_real = false
221
+ def real_retract wme, is_real = false
196
222
 
197
223
  if wme.is_a? Array
198
- return retract( WME.new(*wme), is_real )
224
+ return real_retract( WME.new(*wme), is_real )
199
225
  end
200
226
 
201
227
  if ! is_real
@@ -211,14 +237,11 @@ module Wongi::Engine
211
237
  @cache[wme]
212
238
  end
213
239
 
214
- return false if real.nil?
240
+ return if real.nil?
215
241
  raise "Cannot retract inferred statements" unless real.manual?
216
242
  @cache.delete(real)
217
243
 
218
- real.destroy
219
-
220
- true
221
-
244
+ alphas_for( real ).each { |a| a.deactivate real }
222
245
  end
223
246
 
224
247
  def compile_alpha condition
@@ -362,6 +385,20 @@ module Wongi::Engine
362
385
  "rule_#{productions.length}"
363
386
  end
364
387
 
388
+ def alphas_for wme
389
+ s, p, o = wme.subject, wme.predicate, wme.object
390
+ [
391
+ lookup( s, p, o),
392
+ lookup( s, p, :_),
393
+ lookup( s, :_, o),
394
+ lookup(:_, p, o),
395
+ lookup( s, :_, :_),
396
+ lookup(:_, p, :_),
397
+ lookup(:_, :_, o),
398
+ lookup(:_, :_, :_),
399
+ ].compact.uniq
400
+ end
401
+
365
402
  def lookup s, p, o
366
403
  key = Template.hash_for(s, p, o)
367
404
  # puts "Lookup for #{key}"
@@ -4,7 +4,7 @@ module Wongi::Engine
4
4
 
5
5
  include CoreExt
6
6
 
7
- attr_reader :filters, :unsafe
7
+ attr_reader :unsafe
8
8
  attr_predicate debug: false
9
9
 
10
10
  def self.variable? thing
@@ -72,7 +72,7 @@ module Wongi::Engine
72
72
  def compile context
73
73
  tests, assignment = *JoinNode.compile( self, context.earlier, context.parameters )
74
74
  alpha = context.rete.compile_alpha( self )
75
- context.node = context.node.beta_memory.join_node( alpha, tests, assignment, @filters, context.alpha_deaf )
75
+ context.node = context.node.beta_memory.join_node( alpha, tests, assignment, context.alpha_deaf )
76
76
  context.node.context = context
77
77
  context.node.debug = debug?
78
78
  context.earlier << self
@@ -97,8 +97,6 @@ module Wongi::Engine
97
97
 
98
98
  class NegTemplate < Template
99
99
 
100
- undef_method :filters
101
-
102
100
  # :arg: context => Wongi::Rete::BetaNode::CompilationContext
103
101
  def compile context
104
102
  tests, assignment = *JoinNode.compile( self, context.earlier, context.parameters )
@@ -114,8 +112,6 @@ module Wongi::Engine
114
112
 
115
113
  class OptionalTemplate < Template
116
114
 
117
- undef_method :filters
118
-
119
115
  def compile context
120
116
  tests, assignment = *JoinNode.compile( self, context.earlier, context.parameters )
121
117
  alpha = context.rete.compile_alpha( self )
@@ -5,15 +5,18 @@ module Wongi::Engine
5
5
  include CoreExt
6
6
 
7
7
  attr_reader :children
8
- attr_accessor :node, :owner, :parent
8
+ attr_reader :wme
9
+ attr_reader :node
10
+ attr_accessor :owner, :parent
9
11
  attr_reader :neg_join_results
10
12
  attr_reader :opt_join_results
11
13
  attr_reader :ncc_results
12
14
  attr_reader :generated_wmes
13
- attr_predicate :has_optional
15
+ attr_predicate :optional
16
+ attr_predicate :deleted
14
17
 
15
- def initialize token, wme, assignments
16
- @parent, @wme, @assignments = token, wme, assignments
18
+ def initialize node, token, wme, assignments
19
+ @node, @parent, @wme, @assignments = node, token, wme, assignments
17
20
  @children = []
18
21
  @deleted = false
19
22
  @neg_join_results = []
@@ -49,8 +52,9 @@ module Wongi::Engine
49
52
  end
50
53
  end
51
54
 
52
- def duplicate? parent, wme, assignments
53
- self.parent.equal?(parent) && @wme.equal?(wme) && self.assignments == assignments
55
+ # TODO ignore assignments?
56
+ def duplicate? other
57
+ self.parent.equal?(other.parent) && @wme.equal?(other.wme) && self.assignments == other.assignments
54
58
  end
55
59
 
56
60
  def to_s
@@ -61,29 +65,26 @@ module Wongi::Engine
61
65
  end
62
66
 
63
67
  def destroy
64
- delete_children
65
- #@node.tokens.delete self unless @node.kind_of?( NccPartner )
66
- @wme.tokens.delete self if @wme
67
- @parent.children.delete self if @parent
68
-
69
- retract_generated
70
- @deleted = true
71
- @node.delete_token self
72
- end
73
-
74
- def deleted?
75
- @deleted
68
+ # delete_children
69
+ # #@node.tokens.delete self unless @node.kind_of?( NccPartner )
70
+ # @wme.tokens.delete self if @wme
71
+ # @parent.children.delete self if @parent
72
+
73
+ # retract_generated
74
+ deleted!
75
+ # @node.delete_token self
76
76
  end
77
77
 
78
- def delete_children
79
- children = @children
80
- @children = []
81
- children.each do |token|
82
- token.parent = nil
83
- token.destroy
84
- end
85
- end
78
+ # def delete_children
79
+ # children = @children
80
+ # @children = []
81
+ # children.each do |token|
82
+ # token.parent = nil
83
+ # token.destroy
84
+ # end
85
+ # end
86
86
 
87
+ # for neg feedback loop protection
87
88
  def generated? wme
88
89
  return true if generated_wmes.any? { |w| w == wme }
89
90
  return children.any? { |t| t.generated? wme }
@@ -91,21 +92,21 @@ module Wongi::Engine
91
92
 
92
93
  protected
93
94
 
94
- def retract_generated
95
- for_retraction = []
96
-
97
- @generated_wmes.dup.each do |wme|
98
- unless wme.manual? # => TODO: does this ever fail at all?
99
- wme.generating_tokens.delete self
100
- if wme.generating_tokens.empty?
101
- for_retraction << wme
102
- end
103
- end
104
- end
105
- @generated_wmes = []
106
- for_retraction.each { |wme| wme.rete.retract wme, true }
107
-
108
- end
95
+ # def retract_generated
96
+ # for_retraction = []
97
+
98
+ # @generated_wmes.dup.each do |wme|
99
+ # unless wme.manual? # => TODO: does this ever fail at all?
100
+ # wme.generating_tokens.delete self
101
+ # if wme.generating_tokens.empty?
102
+ # for_retraction << wme
103
+ # end
104
+ # end
105
+ # end
106
+ # @generated_wmes = []
107
+ # for_retraction.each { |wme| wme.rete.retract wme, true }
108
+
109
+ # end
109
110
 
110
111
  def all_assignments
111
112
  raise "Assignments is not a hash" unless @assignments.kind_of?( Hash )
@@ -1,5 +1,5 @@
1
1
  module Wongi
2
2
  module Engine
3
- VERSION = "0.0.17"
3
+ VERSION = "0.1.0.alpha1"
4
4
  end
5
5
  end
@@ -2,10 +2,13 @@ module Wongi::Engine
2
2
 
3
3
  class WME < Struct.new( :subject, :predicate, :object )
4
4
 
5
+ include CoreExt
6
+
5
7
  attr_reader :rete
6
8
 
7
9
  attr_reader :alphas, :tokens, :generating_tokens
8
10
  attr_reader :neg_join_results, :opt_join_results
11
+ attr_predicate :deleted
9
12
 
10
13
  def initialize s, p, o, r = nil
11
14
 
@@ -54,25 +57,21 @@ module Wongi::Engine
54
57
  !manual?
55
58
  end
56
59
 
57
- def deleted?
58
- @deleted
59
- end
60
+ # def destroy
61
+ # return if deleted?
62
+ # @deleted = true
63
+ # alphas.each { |alpha| alpha.remove self }.clear
64
+ # tokens = @tokens
65
+ # @tokens = []
66
+ # tokens.each &:destroy
60
67
 
61
- def destroy
62
- return if deleted?
63
- @deleted = true
64
- alphas.each { |alpha| alpha.remove self }.clear
65
- tokens = @tokens
66
- @tokens = []
67
- tokens.each &:destroy
68
+ # destroy_neg_join_results
69
+ # destroy_opt_join_results
68
70
 
69
- destroy_neg_join_results
70
- destroy_opt_join_results
71
-
72
- end
71
+ # end
73
72
 
74
73
  def inspect
75
- "<WME #{subject.inspect} #{predicate.inspect} #{object.inspect}>"
74
+ "{#{subject.inspect} #{predicate.inspect} #{object.inspect}}"
76
75
  end
77
76
 
78
77
  def to_s
@@ -89,38 +88,38 @@ module Wongi::Engine
89
88
  @array_form ||= [ subject, predicate, object ]
90
89
  end
91
90
 
92
- def destroy_neg_join_results
93
- neg_join_results.each do |njr|
91
+ # def destroy_neg_join_results
92
+ # neg_join_results.each do |njr|
94
93
 
95
- token = njr.owner
96
- results = token.neg_join_results
97
- results.delete njr
94
+ # token = njr.owner
95
+ # results = token.neg_join_results
96
+ # results.delete njr
98
97
 
99
- if results.empty? #&& !rete.in_snapshot?
100
- token.node.children.each { |beta|
101
- beta.beta_activate token, nil, { }
102
- }
103
- end
98
+ # if results.empty? #&& !rete.in_snapshot?
99
+ # token.node.children.each { |beta|
100
+ # beta.beta_activate token, nil, { }
101
+ # }
102
+ # end
104
103
 
105
- end.clear
106
- end
104
+ # end.clear
105
+ # end
107
106
 
108
- def destroy_opt_join_results
109
- opt_join_results.each do |ojr|
107
+ # def destroy_opt_join_results
108
+ # opt_join_results.each do |ojr|
110
109
 
111
- token = ojr.owner
112
- results = token.opt_join_results
113
- results.delete ojr
110
+ # token = ojr.owner
111
+ # results = token.opt_join_results
112
+ # results.delete ojr
114
113
 
115
- if results.empty?
116
- token.delete_children
117
- token.node.children.each { |beta|
118
- beta.beta_activate token
119
- }
120
- end
114
+ # if results.empty?
115
+ # token.delete_children
116
+ # token.node.children.each { |beta|
117
+ # beta.beta_activate token
118
+ # }
119
+ # end
121
120
 
122
- end.clear
123
- end
121
+ # end.clear
122
+ # end
124
123
 
125
124
  def match_member mine, theirs
126
125
  result = WMEMatchData.new