wongi-engine 0.0.16 → 0.0.17

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 685d73d3400c03903114505a66f2bb6ca6ca8ba2
4
- data.tar.gz: ac0de44ac50ad33b7a040bcc786e82f50fa16946
3
+ metadata.gz: 5ab2c246ebcf7d3a4a381b39195f950b72f9ac22
4
+ data.tar.gz: 0378728b15e4a72274333ba56ac78e16c67bdb36
5
5
  SHA512:
6
- metadata.gz: 6d9402a2c69eda4a5262b66fefdba7212c20e62f5a98a3b9290e0279de58ec7d0a0631ee71cf106bc0dbfd0545302d3665bf4e233f630a1ff907b22203d91933
7
- data.tar.gz: 231b6574013f60209ea5debf67bc81afa575fa112861d9afc1a78ef9b098af16985c9fff9f0ca73393e474abba3e263b44f3a6b219dc051b8229ea36ff8619ac
6
+ metadata.gz: 66819b8d5b0165c204e091debcc4de62b1f7a03d4067fdc6d966bd2b17fb0d7196f08244cfa33c7426399caef75929bf6157abe8407170d908dd9009b4d2acb0
7
+ data.tar.gz: 414beb220fd40675deff5563a1c8b59a79ed644e0ef1c688c3ce559f00c239e5516c39bc1a06ec44d95da7503ba56f1a5131be5fa4dfcc207ba2a3d7d0fa3a82
data/.gitignore CHANGED
@@ -17,3 +17,4 @@ spec/reports
17
17
  test/tmp
18
18
  test/version_tmp
19
19
  tmp
20
+ .ruby-version
data/README.md CHANGED
@@ -197,6 +197,25 @@ Passes if the arguments are not equal. Alias: `ne`.
197
197
 
198
198
  Should be obvious by now.
199
199
 
200
+ #### `assuming rule_name`
201
+
202
+ This is a shortcut for extending a common base rule with additional matchers. `rule_name` must already be installed on the same engine instance. `assuming` must be the first clause in the extending rule.
203
+
204
+ ```ruby
205
+ engine << rule( :base ) {
206
+ forall {
207
+ has :x, :y, :Z
208
+ }
209
+ }
210
+
211
+ engine << rule {
212
+ forall {
213
+ assuming :base
214
+ has :Z, :u, :W
215
+ }
216
+ }
217
+ ```
218
+
200
219
  #### `assert { |token| ... }`, `assert var1, var2, ... do |val1, val2, ... | ... end`
201
220
 
202
221
  Passes if the block evaluates to `true`. Having no arguments passes the entire token as an argument, listing some variables passes only their values.
@@ -440,6 +459,10 @@ The Rete implementation in this library largely follows the outline presented in
440
459
 
441
460
  ## Changelog
442
461
 
462
+ ### 0.0.17
463
+
464
+ * introduced the `assuming` matcher
465
+
443
466
  ### 0.0.13
444
467
 
445
468
  * fixed a bug with recursive generations of multiple facts
@@ -44,12 +44,9 @@ module Wongi::Engine
44
44
 
45
45
  def wmes
46
46
  Enumerator.new do |y|
47
- copy = @wmes.dup
48
- @wmes.reject! &:deleted?
49
- copy.each do |wme|
47
+ @wmes.dup.each do |wme|
50
48
  y << wme unless wme.deleted?
51
49
  end
52
- @wmes.reject! &:deleted?
53
50
  end
54
51
  end
55
52
 
@@ -8,6 +8,7 @@ module Wongi::Engine
8
8
 
9
9
  def compile context
10
10
  context.node = context.node.beta_memory.assignment_node( @variable, @body )
11
+ context.node.context = context
11
12
  context.earlier << self
12
13
  context
13
14
  end
@@ -22,7 +23,7 @@ module Wongi::Engine
22
23
  end
23
24
 
24
25
  def beta_activate token, wme = nil, assignments = { }
25
- propagate_activation token, nil, { @variable => @body.call(token) }
26
+ propagate_activation token, nil, { @variable => @body }
26
27
  end
27
28
 
28
29
  def refresh_child child
@@ -7,6 +7,11 @@ module Wongi::Engine
7
7
  @tokens = []
8
8
  end
9
9
 
10
+ # @override
11
+ def beta_memory
12
+ self
13
+ end
14
+
10
15
  def seed assignments = {}
11
16
  @seed = assignments
12
17
  t = Token.new( nil, nil, assignments )
@@ -15,7 +20,7 @@ module Wongi::Engine
15
20
  end
16
21
 
17
22
  def subst valuations
18
- @tokens.first.delete
23
+ @tokens.first.destroy
19
24
 
20
25
  token = Token.new( nil, nil, @seed )
21
26
  token.node = self
@@ -29,7 +34,7 @@ module Wongi::Engine
29
34
 
30
35
  def beta_activate token, wme, assignments
31
36
  dp "MEMORY beta-activated with #{wme} #{wme.object_id}"
32
- existing = @tokens.reject(&:deleted?).find { |et| et.duplicate? self, token, wme, assignments }
37
+ existing = @tokens.reject(&:deleted?).find { |et| et.duplicate? token, wme, assignments }
33
38
  if existing
34
39
  t = existing
35
40
  else
@@ -7,6 +7,7 @@ module Wongi::Engine
7
7
  attr_writer :rete
8
8
  attr_reader :parent
9
9
  attr_accessor :children
10
+ attr_accessor :context
10
11
  attr_predicate :debug
11
12
 
12
13
  def initialize parent = nil
@@ -34,7 +35,6 @@ module Wongi::Engine
34
35
  end
35
36
 
36
37
  def beta_memory
37
- return self if BetaMemory === self # the easiest way to do this at the top
38
38
  beta = children.find { |node| BetaMemory === node }
39
39
  if beta.nil?
40
40
  beta = BetaMemory.new self
@@ -102,7 +102,12 @@ module Wongi::Engine
102
102
  ncc
103
103
  end
104
104
 
105
- CompilationContext = Struct.new :node, :rete, :earlier, :parameters, :alpha_deaf
105
+ CompilationContext = Struct.new :node, :rete, :earlier, :parameters, :alpha_deaf do
106
+ def dup
107
+ self.class.new( node, rete, earlier.dup, parameters.dup, alpha_deaf )
108
+ end
109
+ end
110
+
106
111
  def network conditions, earlier, parameters, alpha_deaf
107
112
  # puts "Getting beta subnetwork"
108
113
  conditions.inject(CompilationContext.new self, self.rete, earlier, parameters, alpha_deaf) do |context, condition|
@@ -1,6 +1,12 @@
1
1
  module Wongi
2
2
  module Engine
3
3
 
4
+ TokenAssignment = Struct.new(:wme, :field) do
5
+ def call token = nil
6
+ wme.send field
7
+ end
8
+ end
9
+
4
10
  class BetaTest
5
11
 
6
12
  attr_reader :field
@@ -103,7 +109,7 @@ module Wongi
103
109
  if ec.kind_of?( VariantSet )
104
110
  ec.introduces_variable?( member )
105
111
  else
106
- ! ec.kind_of?( NegTemplate ) and ec.contains?( member )
112
+ ec.respond_to?( :contains? ) and ec.contains?( member )
107
113
  end
108
114
  end
109
115
  contains = true
@@ -142,7 +148,7 @@ module Wongi
142
148
  [:subject, :predicate, :object].each do |field|
143
149
  if self.assignment_pattern.send(field) != :_
144
150
  #puts "#{self.assignment_pattern.send(field)} = #{wme.send(field)}"
145
- assignments[ self.assignment_pattern.send(field) ] = wme.send(field)
151
+ assignments[ self.assignment_pattern.send(field) ] = TokenAssignment.new( wme, field )
146
152
  end
147
153
  end
148
154
  assignments
@@ -9,6 +9,7 @@ module Wongi
9
9
 
10
10
  def compile context
11
11
  context.node = context.node.beta_memory.ncc_node( self, context.earlier, context.parameters, false )
12
+ context.node.context = context
12
13
  context.earlier << self
13
14
  context
14
15
  end
@@ -1,92 +1,92 @@
1
- module Wongi
2
- module Engine
3
-
4
- NegJoinResult = Struct.new :owner, :wme
5
-
6
- class NegNode < BetaNode
7
-
8
- attr_reader :tokens, :alpha, :tests
9
-
10
- def initialize parent, tests, alpha, unsafe
11
- super(parent)
12
- @tests, @alpha, @unsafe = tests, alpha, unsafe
13
- @tokens = []
14
- end
15
-
16
- def alpha_activate wme
17
- self.tokens.each do |token|
18
- if matches?( token, wme ) && ( @unsafe || ! token.generated?( wme ) )# feedback loop protection
19
- # order matters for proper invalidation
20
- make_join_result(token, wme)
21
- token.delete_children #if token.neg_join_results.empty? # TODO why was this check here? it seems to break things
22
- end
23
- end
24
- end
25
-
26
- def beta_activate token, newwme, assignments
27
- existing = @tokens.find { |et| et.duplicate? self, token, newwme, assignments }
28
- if existing
29
- t = existing
30
- else
31
- t = Token.new( token, newwme, assignments)
32
- t.node = self
33
- @tokens << t
34
- end
35
- @alpha.wmes.each do |wme|
36
- if matches?( t, wme )
37
- make_join_result(t, wme)
38
- end
39
- end
40
- if t.neg_join_results.empty?
41
- self.children.each do |child|
42
- child.beta_activate t, nil, {}
43
- end
44
- end
45
- end
46
-
47
- def refresh_child child
48
- safe_tokens.each do |token|
49
- if token.neg_join_results.empty?
50
- child.beta_activate token, nil, {}
51
- end
52
- end
53
- alpha.wmes.each do |wme|
54
- alpha_activate wme
55
- end
56
- end
57
-
58
- def delete_token token
59
- tokens.delete token
60
- token.neg_join_results.each do |njr|
61
- njr.wme.neg_join_results.delete njr if njr.wme
62
- end
63
- token.neg_join_results.clear
64
- end
65
-
66
- protected
67
-
68
- def matches? token, wme
69
- puts "matching #{wme} against #{token}" if debug?
70
- @tests.each do |test|
71
- return false unless test.matches?( token, wme )
72
- end
73
- true
74
- end
75
-
76
- def make_join_result token, wme
77
- njr = NegJoinResult.new token, wme
78
- token.neg_join_results << njr
79
- wme.neg_join_results << njr
80
- end
81
-
82
- def safe_tokens
83
- Enumerator.new do |y|
84
- @tokens.dup.each do |token|
85
- y << token unless token.deleted?
86
- end
87
- end
88
- end
89
-
90
- end
91
- end
92
- end
1
+ module Wongi
2
+ module Engine
3
+
4
+ NegJoinResult = Struct.new :owner, :wme
5
+
6
+ class NegNode < BetaNode
7
+
8
+ attr_reader :tokens, :alpha, :tests
9
+
10
+ def initialize parent, tests, alpha, unsafe
11
+ super(parent)
12
+ @tests, @alpha, @unsafe = tests, alpha, unsafe
13
+ @tokens = []
14
+ end
15
+
16
+ def alpha_activate wme
17
+ self.tokens.each do |token|
18
+ if matches?( token, wme ) && ( @unsafe || ! token.generated?( wme ) )# feedback loop protection
19
+ # order matters for proper invalidation
20
+ make_join_result(token, wme)
21
+ token.delete_children #if token.neg_join_results.empty? # TODO why was this check here? it seems to break things
22
+ end
23
+ end
24
+ end
25
+
26
+ def beta_activate token, newwme, assignments
27
+ existing = @tokens.find { |et| et.duplicate? token, newwme, assignments }
28
+ if existing
29
+ t = existing
30
+ else
31
+ t = Token.new( token, newwme, assignments)
32
+ t.node = self
33
+ @tokens << t
34
+ end
35
+ alpha.wmes.each do |wme|
36
+ if matches?( t, wme )
37
+ make_join_result(t, wme)
38
+ end
39
+ end
40
+ if t.neg_join_results.empty?
41
+ self.children.each do |child|
42
+ child.beta_activate t, nil, {}
43
+ end
44
+ end
45
+ end
46
+
47
+ def refresh_child child
48
+ safe_tokens.each do |token|
49
+ if token.neg_join_results.empty?
50
+ child.beta_activate token, nil, {}
51
+ end
52
+ end
53
+ alpha.wmes.each do |wme|
54
+ alpha_activate wme
55
+ end
56
+ end
57
+
58
+ def delete_token token
59
+ tokens.delete token
60
+ token.neg_join_results.each do |njr|
61
+ njr.wme.neg_join_results.delete njr if njr.wme
62
+ end
63
+ token.neg_join_results.clear
64
+ end
65
+
66
+ protected
67
+
68
+ def matches? token, wme
69
+ puts "matching #{wme} against #{token}" if debug?
70
+ @tests.each do |test|
71
+ return false unless test.matches?( token, wme )
72
+ end
73
+ true
74
+ end
75
+
76
+ def make_join_result token, wme
77
+ njr = NegJoinResult.new token, wme
78
+ token.neg_join_results << njr
79
+ wme.neg_join_results << njr
80
+ end
81
+
82
+ def safe_tokens
83
+ Enumerator.new do |y|
84
+ @tokens.dup.each do |token|
85
+ y << token unless token.deleted?
86
+ end
87
+ end
88
+ end
89
+
90
+ end
91
+ end
92
+ end
@@ -18,6 +18,7 @@ module Wongi
18
18
  end
19
19
  context.earlier += added
20
20
  context.node = OrNode.new( branches )
21
+ context.node.context = context
21
22
  context.node.refresh
22
23
  context
23
24
  end
@@ -33,6 +34,7 @@ module Wongi
33
34
 
34
35
  def compile context
35
36
  context.node = context.node.beta_memory.network( children, context.earlier, context.parameters, false )
37
+ context.node.context = context
36
38
  context.earlier << self
37
39
  context
38
40
  end
@@ -42,7 +44,7 @@ module Wongi
42
44
  if c.kind_of?( VariantSet )
43
45
  c.introduces_variable?( var )
44
46
  else
45
- ! c.kind_of?( NegTemplate ) and c.contains?( var )
47
+ c.respond_to?( :contains? ) and c.contains?( var )
46
48
  end
47
49
  }
48
50
  end
@@ -7,7 +7,7 @@ def ruleset name = nil, &definition
7
7
  rs
8
8
  end
9
9
 
10
- def rule name, &definition
10
+ def rule name = nil, &definition
11
11
  r = Wongi::Engine::ProductionRule.new name
12
12
  r.instance_eval &definition
13
13
  r
@@ -33,6 +33,7 @@ require 'wongi-engine/dsl/production_rule'
33
33
  require 'wongi-engine/dsl/ncc_production_rule'
34
34
  require 'wongi-engine/dsl/any_rule'
35
35
  require 'wongi-engine/dsl/query'
36
+ require 'wongi-engine/dsl/assuming'
36
37
  require 'wongi-engine/dsl/actions/simple_action'
37
38
  require 'wongi-engine/dsl/actions/statement_generator'
38
39
  require 'wongi-engine/dsl/actions/simple_collector'
@@ -102,6 +103,8 @@ dsl {
102
103
  missing s, p, o, time: 0
103
104
  }
104
105
 
106
+ clause :assuming
107
+ accept Wongi::Engine::AssumingClause
105
108
 
106
109
  section :make
107
110
 
@@ -1,55 +1,55 @@
1
- module Wongi::Engine
2
- class StatementGenerator < Action
3
-
4
- def initialize template
5
- @template = template
6
- end
7
-
8
- def execute token
9
-
10
- subject = if Template.variable?( @template.subject )
11
- v = token[ @template.subject ]
12
- raise "Unbound variable #{@template.subject} in token #{token}" if v.nil?
13
- v
14
- else
15
- @template.subject
16
- end
17
-
18
- predicate = if Template.variable?( @template.predicate )
19
- v = token[ @template.predicate ]
20
- raise "Unbound variable #{@template.predicate} in token #{token}" if v.nil?
21
- v
22
- else
23
- @template.predicate
24
- end
25
-
26
- object = if Template.variable?( @template.object )
27
- v = token[ @template.object ]
28
- raise "Unbound variable #{@template.object} in token #{token}" if v.nil?
29
- v
30
- else
31
- @template.object
32
- end
33
-
34
- # link to rete here to ensure proper linking with token
35
- wme = WME.new subject, predicate, object, rete
36
-
37
- production.tracer.trace( action: self, wme: wme ) if production.tracer
38
- if existing = rete.exists?( wme )
39
- generated = existing.generating_tokens.size
40
- if generated > 0 && ! token.generated_wmes.include?( existing )
41
- token.generated_wmes << existing
42
- existing.generating_tokens << token
43
- end
44
- else
45
- token.generated_wmes << wme
46
- wme.generating_tokens << token
47
- # this MUST be done after we link the wme and the token
48
- # in order for neg rule invalidation to work
49
- rete << wme
50
- end
51
-
52
- end
53
-
54
- end
55
- end
1
+ module Wongi::Engine
2
+ class StatementGenerator < Action
3
+
4
+ def initialize template
5
+ @template = template
6
+ end
7
+
8
+ def execute token
9
+
10
+ subject = if Template.variable?( @template.subject )
11
+ v = token[ @template.subject ]
12
+ raise "Unbound variable #{@template.subject} in token #{token}" if v.nil?
13
+ v
14
+ else
15
+ @template.subject
16
+ end
17
+
18
+ predicate = if Template.variable?( @template.predicate )
19
+ v = token[ @template.predicate ]
20
+ raise "Unbound variable #{@template.predicate} in token #{token}" if v.nil?
21
+ v
22
+ else
23
+ @template.predicate
24
+ end
25
+
26
+ object = if Template.variable?( @template.object )
27
+ v = token[ @template.object ]
28
+ raise "Unbound variable #{@template.object} in token #{token}" if v.nil?
29
+ v
30
+ else
31
+ @template.object
32
+ end
33
+
34
+ # link to rete here to ensure proper linking with token
35
+ wme = WME.new subject, predicate, object, rete
36
+
37
+ production.tracer.trace( action: self, wme: wme ) if production.tracer
38
+ if existing = rete.exists?( wme )
39
+ generated = existing.generating_tokens.size
40
+ if generated > 0 && ! token.generated_wmes.include?( existing )
41
+ token.generated_wmes << existing
42
+ existing.generating_tokens << token
43
+ end
44
+ else
45
+ token.generated_wmes << wme
46
+ wme.generating_tokens << token
47
+ # this MUST be done after we link the wme and the token
48
+ # in order for neg rule invalidation to work
49
+ rete << wme
50
+ end
51
+
52
+ end
53
+
54
+ end
55
+ end