wongi-engine 0.0.16 → 0.0.17

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 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