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