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 +4 -4
- data/.gitignore +1 -0
- data/README.md +23 -0
- data/lib/wongi-engine/alpha_memory.rb +1 -4
- data/lib/wongi-engine/beta/assignment_node.rb +2 -1
- data/lib/wongi-engine/beta/beta_memory.rb +7 -2
- data/lib/wongi-engine/beta/beta_node.rb +7 -2
- data/lib/wongi-engine/beta/join_node.rb +8 -2
- data/lib/wongi-engine/beta/ncc_node.rb +1 -0
- data/lib/wongi-engine/beta/neg_node.rb +92 -92
- data/lib/wongi-engine/beta/or_node.rb +3 -1
- data/lib/wongi-engine/dsl.rb +4 -1
- data/lib/wongi-engine/dsl/actions/statement_generator.rb +55 -55
- data/lib/wongi-engine/dsl/assuming.rb +37 -0
- data/lib/wongi-engine/dsl/generic_production_rule.rb +4 -4
- data/lib/wongi-engine/filter/filter_test.rb +1 -0
- data/lib/wongi-engine/network.rb +443 -442
- data/lib/wongi-engine/template.rb +3 -0
- data/lib/wongi-engine/token.rb +132 -138
- data/lib/wongi-engine/version.rb +1 -1
- data/lib/wongi-engine/wme.rb +138 -138
- data/spec/beta_node_spec.rb +27 -0
- data/spec/dataset_spec.rb +1 -0
- data/spec/rule_specs/assuming_spec.rb +66 -0
- data/spec/rule_specs/ncc_spec.rb +33 -0
- data/spec/rule_specs/negative_rule_spec.rb +105 -70
- data/spec/wme_spec.rb +0 -0
- data/wongi-engine.gemspec +0 -0
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5ab2c246ebcf7d3a4a381b39195f950b72f9ac22
|
4
|
+
data.tar.gz: 0378728b15e4a72274333ba56ac78e16c67bdb36
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 66819b8d5b0165c204e091debcc4de62b1f7a03d4067fdc6d966bd2b17fb0d7196f08244cfa33c7426399caef75929bf6157abe8407170d908dd9009b4d2acb0
|
7
|
+
data.tar.gz: 414beb220fd40675deff5563a1c8b59a79ed644e0ef1c688c3ce559f00c239e5516c39bc1a06ec44d95da7503ba56f1a5131be5fa4dfcc207ba2a3d7d0fa3a82
|
data/.gitignore
CHANGED
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
|
@@ -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
|
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.
|
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?
|
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
|
-
|
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) ] =
|
151
|
+
assignments[ self.assignment_pattern.send(field) ] = TokenAssignment.new( wme, field )
|
146
152
|
end
|
147
153
|
end
|
148
154
|
assignments
|
@@ -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?
|
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
|
-
|
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
|
-
|
47
|
+
c.respond_to?( :contains? ) and c.contains?( var )
|
46
48
|
end
|
47
49
|
}
|
48
50
|
end
|
data/lib/wongi-engine/dsl.rb
CHANGED
@@ -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
|