wongi-engine 0.0.1
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.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +349 -0
- data/Rakefile +2 -0
- data/examples/ex01.rb +23 -0
- data/examples/ex02.rb +36 -0
- data/examples/graphviz.rb +15 -0
- data/examples/timeline.rb +48 -0
- data/lib/wongi-engine.rb +22 -0
- data/lib/wongi-engine/alpha_memory.rb +46 -0
- data/lib/wongi-engine/beta.rb +10 -0
- data/lib/wongi-engine/beta/beta_memory.rb +48 -0
- data/lib/wongi-engine/beta/beta_node.rb +164 -0
- data/lib/wongi-engine/beta/filter_node.rb +109 -0
- data/lib/wongi-engine/beta/join_node.rb +127 -0
- data/lib/wongi-engine/beta/ncc_node.rb +46 -0
- data/lib/wongi-engine/beta/ncc_partner.rb +43 -0
- data/lib/wongi-engine/beta/neg_node.rb +58 -0
- data/lib/wongi-engine/beta/optional_node.rb +43 -0
- data/lib/wongi-engine/beta/or_node.rb +76 -0
- data/lib/wongi-engine/beta/production_node.rb +31 -0
- data/lib/wongi-engine/core_ext.rb +57 -0
- data/lib/wongi-engine/dsl.rb +112 -0
- data/lib/wongi-engine/dsl/action.rb +12 -0
- data/lib/wongi-engine/dsl/actions/error_generator.rb +42 -0
- data/lib/wongi-engine/dsl/actions/simple_action.rb +23 -0
- data/lib/wongi-engine/dsl/actions/simple_collector.rb +51 -0
- data/lib/wongi-engine/dsl/actions/statement_generator.rb +52 -0
- data/lib/wongi-engine/dsl/actions/trace_action.rb +52 -0
- data/lib/wongi-engine/dsl/any_rule.rb +48 -0
- data/lib/wongi-engine/dsl/dsl_builder.rb +44 -0
- data/lib/wongi-engine/dsl/dsl_extensions.rb +43 -0
- data/lib/wongi-engine/dsl/extension_clause.rb +36 -0
- data/lib/wongi-engine/dsl/generation_clause.rb +15 -0
- data/lib/wongi-engine/dsl/generic_production_rule.rb +78 -0
- data/lib/wongi-engine/dsl/ncc_production_rule.rb +21 -0
- data/lib/wongi-engine/dsl/production_rule.rb +4 -0
- data/lib/wongi-engine/dsl/query.rb +24 -0
- data/lib/wongi-engine/graph.rb +71 -0
- data/lib/wongi-engine/model_context.rb +13 -0
- data/lib/wongi-engine/network.rb +416 -0
- data/lib/wongi-engine/network/collectable.rb +42 -0
- data/lib/wongi-engine/network/debug.rb +25 -0
- data/lib/wongi-engine/ruleset.rb +74 -0
- data/lib/wongi-engine/template.rb +111 -0
- data/lib/wongi-engine/token.rb +137 -0
- data/lib/wongi-engine/version.rb +5 -0
- data/lib/wongi-engine/wme.rb +134 -0
- data/lib/wongi-engine/wme_match_data.rb +34 -0
- data/spec/dataset_spec.rb +26 -0
- data/spec/dsl_spec.rb +9 -0
- data/spec/high_level_spec.rb +341 -0
- data/spec/ruleset_spec.rb +54 -0
- data/spec/simple_action_spec.rb +40 -0
- data/spec/spec_helper.rb +1 -0
- data/spec/wme_spec.rb +83 -0
- data/wongi-engine.gemspec +19 -0
- metadata +110 -0
@@ -0,0 +1,127 @@
|
|
1
|
+
module Wongi
|
2
|
+
module Engine
|
3
|
+
|
4
|
+
class BetaTest
|
5
|
+
|
6
|
+
attr_reader :field
|
7
|
+
attr_reader :variable
|
8
|
+
|
9
|
+
def initialize field, variable
|
10
|
+
@field, @variable = field, variable
|
11
|
+
end
|
12
|
+
|
13
|
+
def matches? token, wme
|
14
|
+
assignment = token[ self.variable ]
|
15
|
+
field = wme.send( self.field )
|
16
|
+
#field.nil? ||
|
17
|
+
assignment && field == assignment
|
18
|
+
end
|
19
|
+
|
20
|
+
def equivalent? other
|
21
|
+
other.field == self.field && other.variable == self.variable
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
class JoinNode < BetaNode
|
27
|
+
|
28
|
+
attr_accessor :alpha
|
29
|
+
attr_reader :tests
|
30
|
+
attr_reader :assignment_pattern
|
31
|
+
|
32
|
+
def initialize parent, tests, assignment
|
33
|
+
super(parent)
|
34
|
+
@tests = tests
|
35
|
+
@assignment_pattern = assignment
|
36
|
+
end
|
37
|
+
|
38
|
+
def equivalent? alpha, tests, assignment_pattern
|
39
|
+
return false unless self.alpha == alpha
|
40
|
+
return false unless self.assignment_pattern == assignment_pattern
|
41
|
+
return false unless (self.tests.empty? && tests.empty?) || self.tests.all? { |my_test|
|
42
|
+
tests.any? { |new_test|
|
43
|
+
my_test.equivalent? new_test
|
44
|
+
}
|
45
|
+
}
|
46
|
+
true
|
47
|
+
end
|
48
|
+
|
49
|
+
def alpha= a
|
50
|
+
@alpha = a
|
51
|
+
# puts "\talpha = #{alpha}"
|
52
|
+
end
|
53
|
+
|
54
|
+
def right_activate wme
|
55
|
+
ws = ' ' * depth
|
56
|
+
# puts "#{ws}JOIN #{@id} right-activated with #{wme}"
|
57
|
+
collected = collect_assignments( wme )
|
58
|
+
# puts "PARENT HAS #{parent.tokens.length} TOKENS"
|
59
|
+
self.parent.tokens.each do |token|
|
60
|
+
# puts "#{ws}matching with token"
|
61
|
+
if matches?( token, wme )
|
62
|
+
# puts "#{ws}JOIN RIGHT-MATCHED, PROPAGATING"
|
63
|
+
propagate_activation token, wme, collected
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def left_activate token
|
69
|
+
ws = ' ' * depth
|
70
|
+
self.alpha.wmes.uniq.each do |wme|
|
71
|
+
if matches?( token, wme )
|
72
|
+
propagate_activation token, wme, collect_assignments( wme )
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.compile condition, earlier, parameters
|
78
|
+
tests = []
|
79
|
+
assignment = Template.new
|
80
|
+
[:subject, :predicate, :object].each do |field|
|
81
|
+
member = condition.send field
|
82
|
+
if Template.variable?( member )
|
83
|
+
|
84
|
+
contains = parameters.include? member
|
85
|
+
if earlier.any? do |ec|
|
86
|
+
! ec.kind_of?( NegTemplate ) and ec.contains?( member )
|
87
|
+
end
|
88
|
+
contains = true
|
89
|
+
end
|
90
|
+
|
91
|
+
if contains
|
92
|
+
tests << BetaTest.new( field, member )
|
93
|
+
else
|
94
|
+
method = (field.to_s + "=").to_sym
|
95
|
+
assignment.send method, member
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
end
|
100
|
+
return tests, assignment
|
101
|
+
end
|
102
|
+
|
103
|
+
protected
|
104
|
+
|
105
|
+
def matches? token, wme
|
106
|
+
@tests.each do |test|
|
107
|
+
return false unless test.matches?( token, wme )
|
108
|
+
end
|
109
|
+
true
|
110
|
+
end
|
111
|
+
|
112
|
+
def collect_assignments wme
|
113
|
+
assignments = {}
|
114
|
+
return assignments if self.assignment_pattern.nil?
|
115
|
+
# puts "more assignments"
|
116
|
+
[:subject, :predicate, :object].each do |field|
|
117
|
+
if self.assignment_pattern.send(field)
|
118
|
+
#puts "#{self.assignment_pattern.send(field)} = #{wme.send(field)}"
|
119
|
+
assignments[ self.assignment_pattern.send(field) ] = wme.send(field)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
assignments
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Wongi
|
2
|
+
module Engine
|
3
|
+
class NccSet
|
4
|
+
|
5
|
+
attr_reader :children
|
6
|
+
def initialize conditions
|
7
|
+
@children = conditions
|
8
|
+
end
|
9
|
+
|
10
|
+
def compile context
|
11
|
+
context.node = context.node.beta_memory.ncc_node( self, context.earlier, context.parameters, false )
|
12
|
+
context.earlier << self
|
13
|
+
context
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
class NccNode < BetaNode
|
19
|
+
|
20
|
+
attr_reader :tokens
|
21
|
+
attr_accessor :partner
|
22
|
+
|
23
|
+
def initialize parent
|
24
|
+
super
|
25
|
+
@tokens = []
|
26
|
+
end
|
27
|
+
|
28
|
+
# def left_activate token, wme, assignments
|
29
|
+
# t = Token.new token, wme, assignments
|
30
|
+
# t.node = self
|
31
|
+
def left_activate token, wme = nil, assignments = { } # => FIXME: left_activate has different signatures for storing and non-storing nodes...
|
32
|
+
t = Token.new token, nil, {}
|
33
|
+
tokens << t
|
34
|
+
partner.tokens.each do |ncc_token|
|
35
|
+
t.ncc_results << ncc_token
|
36
|
+
ncc_token.owner = t
|
37
|
+
end
|
38
|
+
if t.ncc_results.empty?
|
39
|
+
children.each do |child|
|
40
|
+
child.left_activate t, nil, {}
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Wongi
|
2
|
+
module Engine
|
3
|
+
class NccPartner < BetaNode
|
4
|
+
|
5
|
+
attr_reader :tokens
|
6
|
+
# attr_accessor :conjuncts
|
7
|
+
attr_accessor :ncc
|
8
|
+
attr_accessor :divergent
|
9
|
+
|
10
|
+
def initialize parent
|
11
|
+
super
|
12
|
+
# @conjuncts = 0
|
13
|
+
@tokens = []
|
14
|
+
end
|
15
|
+
|
16
|
+
def left_activate token
|
17
|
+
t = Token.new token, nil, {}
|
18
|
+
t.node = self
|
19
|
+
# owners_t = t
|
20
|
+
# # owners_w = t.wme
|
21
|
+
# conjuncts.times do
|
22
|
+
# # owners_w = owners_t.wme
|
23
|
+
# owners_t = owners_t.parent
|
24
|
+
# end
|
25
|
+
owner = nil
|
26
|
+
ncc.tokens.each do |ncc_token|
|
27
|
+
# if ncc_token.parent == owners_t
|
28
|
+
if ncc_token.parent.node == divergent
|
29
|
+
owner = ncc_token
|
30
|
+
break
|
31
|
+
end
|
32
|
+
end
|
33
|
+
if owner
|
34
|
+
owner.ncc_results << t
|
35
|
+
t.owner = owner
|
36
|
+
owner.delete_children
|
37
|
+
else
|
38
|
+
tokens << t
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,58 @@
|
|
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
|
11
|
+
super(parent)
|
12
|
+
@tests, @alpha = tests, alpha
|
13
|
+
@tokens = []
|
14
|
+
end
|
15
|
+
|
16
|
+
def right_activate wme
|
17
|
+
self.tokens.each do |token|
|
18
|
+
if matches?( token, wme )
|
19
|
+
token.delete_children if token.neg_join_results.empty?
|
20
|
+
make_join_result(token, wme)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def left_activate token, newwme, assignments
|
26
|
+
t = Token.new token, newwme, assignments
|
27
|
+
t.node = self
|
28
|
+
self.tokens << t
|
29
|
+
@alpha.wmes.each do |wme|
|
30
|
+
if matches?( t, wme )
|
31
|
+
make_join_result(t, wme)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
if t.neg_join_results.empty?
|
35
|
+
self.children.each do |child|
|
36
|
+
child.left_activate t, nil, {}
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
protected
|
42
|
+
|
43
|
+
def matches? token, wme
|
44
|
+
puts "matching #{wme} against #{token}" if debug?
|
45
|
+
@tests.each do |test|
|
46
|
+
return false unless test.matches?( token, wme )
|
47
|
+
end
|
48
|
+
true
|
49
|
+
end
|
50
|
+
|
51
|
+
def make_join_result token, wme
|
52
|
+
njr = NegJoinResult.new token, wme
|
53
|
+
token.neg_join_results << njr
|
54
|
+
wme.neg_join_results << njr
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Wongi
|
2
|
+
module Engine
|
3
|
+
|
4
|
+
OptionalJoinResult = Struct.new :owner, :wme
|
5
|
+
|
6
|
+
class OptionalNode < JoinNode
|
7
|
+
|
8
|
+
def right_activate wme
|
9
|
+
parent.tokens.each do |token|
|
10
|
+
if matches? token, wme
|
11
|
+
if token.has_optional?
|
12
|
+
token.has_optional = false
|
13
|
+
token.delete_children
|
14
|
+
end
|
15
|
+
propagate_activation(token, wme, collect_assignments(wme))
|
16
|
+
jr = OptionalJoinResult.new token, wme
|
17
|
+
token.opt_join_results << jr
|
18
|
+
wme.opt_join_results << jr
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def left_activate token
|
24
|
+
match = false
|
25
|
+
alpha.wmes.each do |wme|
|
26
|
+
assignments = collect_assignments(wme)
|
27
|
+
if matches? token, wme
|
28
|
+
match = true
|
29
|
+
propagate_activation(token, wme, assignments)
|
30
|
+
jr = OptionalJoinResult.new token, wme
|
31
|
+
token.opt_join_results << jr
|
32
|
+
wme.opt_join_results << jr
|
33
|
+
end
|
34
|
+
end
|
35
|
+
unless match
|
36
|
+
token.has_optional = true
|
37
|
+
propagate_activation(token, nil, {})
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module Wongi
|
2
|
+
module Engine
|
3
|
+
class AnySet
|
4
|
+
|
5
|
+
attr_reader :variants
|
6
|
+
def initialize variants
|
7
|
+
@variants = variants
|
8
|
+
end
|
9
|
+
|
10
|
+
def compile context
|
11
|
+
added = []
|
12
|
+
branches = variants.map do |variant|
|
13
|
+
ctx = BetaNode::CompilationContext.new context.node, context.rete, context.earlier.dup, context.parameters, context.alpha_deaf
|
14
|
+
members = context.earlier.size
|
15
|
+
variant.compile ctx
|
16
|
+
added += ctx.earlier[ (members - ctx.earlier.size) .. -1 ] # newly added elements
|
17
|
+
ctx.node
|
18
|
+
end
|
19
|
+
context.earlier += added
|
20
|
+
context.node = OrNode.new( branches )
|
21
|
+
context.node.update_above
|
22
|
+
context
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
class VariantSet
|
28
|
+
|
29
|
+
attr_reader :children
|
30
|
+
def initialize conditions
|
31
|
+
@children = conditions
|
32
|
+
end
|
33
|
+
|
34
|
+
def compile context
|
35
|
+
context.node = context.node.beta_memory.network( children, context.earlier, context.parameters, false )
|
36
|
+
context.earlier << self
|
37
|
+
context
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
class OrNode < BetaMemory
|
43
|
+
|
44
|
+
attr_reader :parents
|
45
|
+
attr_reader :rete
|
46
|
+
|
47
|
+
def initialize parents
|
48
|
+
super nil
|
49
|
+
@parents = parents
|
50
|
+
parents.each do |parent|
|
51
|
+
parent.children << self
|
52
|
+
end
|
53
|
+
retes = parents.map( &:rete ).uniq
|
54
|
+
raise "Cannot combine variants from several Retes" if retes.size > 1
|
55
|
+
@rete = retes.first
|
56
|
+
end
|
57
|
+
|
58
|
+
def ident
|
59
|
+
ids = parents.map( &:id ).join ", "
|
60
|
+
"<R> #{self.class} #{id}, parents #{ids}"
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
def depth
|
65
|
+
parents.map( &:depth ).max + 1
|
66
|
+
end
|
67
|
+
|
68
|
+
def update_above
|
69
|
+
parents.each do |parent|
|
70
|
+
update_from parent
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Wongi
|
2
|
+
module Engine
|
3
|
+
|
4
|
+
class ProductionNode < BetaMemory
|
5
|
+
|
6
|
+
attr_accessor :tracer
|
7
|
+
|
8
|
+
def initialize parent, actions
|
9
|
+
super(parent)
|
10
|
+
@actions = actions
|
11
|
+
@actions.each { |action| action.production = self }
|
12
|
+
end
|
13
|
+
|
14
|
+
def left_activate token, wme, assignments
|
15
|
+
super
|
16
|
+
@actions.each do |action|
|
17
|
+
# @tokens.each do |t|
|
18
|
+
# action.execute t
|
19
|
+
# end
|
20
|
+
action.execute last_token if action.respond_to? :execute
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# => TODO: investigate
|
25
|
+
def deexecute token
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Wongi::Engine
|
2
|
+
|
3
|
+
module CoreExt
|
4
|
+
|
5
|
+
module ClassMethods
|
6
|
+
|
7
|
+
def attr_predicate *names
|
8
|
+
|
9
|
+
names_hash = names.inject( {} ) do |hash, element|
|
10
|
+
if Hash === element
|
11
|
+
hash.merge element
|
12
|
+
else
|
13
|
+
hash[element] = false
|
14
|
+
hash
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
names_hash.each do |name, def_value|
|
19
|
+
|
20
|
+
varname = "@#{name}".to_sym
|
21
|
+
getname = "#{name}?".to_sym
|
22
|
+
setname = "#{name}=".to_sym
|
23
|
+
exclname = "#{name}!".to_sym
|
24
|
+
noexclname = "no_#{name}!".to_sym
|
25
|
+
|
26
|
+
define_method getname do
|
27
|
+
if instance_variable_defined?( varname )
|
28
|
+
instance_variable_get( varname )
|
29
|
+
else
|
30
|
+
def_value
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
define_method setname do |newvalue|
|
35
|
+
instance_variable_set( varname, newvalue == true )
|
36
|
+
end
|
37
|
+
|
38
|
+
define_method exclname do
|
39
|
+
instance_variable_set( varname, true )
|
40
|
+
end
|
41
|
+
|
42
|
+
define_method noexclname do
|
43
|
+
instance_variable_set( varname, false )
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.included mod
|
52
|
+
mod.extend ClassMethods
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|