wongi-engine 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|