wongi-engine 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. data/.gitignore +17 -0
  2. data/Gemfile +4 -0
  3. data/LICENSE +22 -0
  4. data/README.md +349 -0
  5. data/Rakefile +2 -0
  6. data/examples/ex01.rb +23 -0
  7. data/examples/ex02.rb +36 -0
  8. data/examples/graphviz.rb +15 -0
  9. data/examples/timeline.rb +48 -0
  10. data/lib/wongi-engine.rb +22 -0
  11. data/lib/wongi-engine/alpha_memory.rb +46 -0
  12. data/lib/wongi-engine/beta.rb +10 -0
  13. data/lib/wongi-engine/beta/beta_memory.rb +48 -0
  14. data/lib/wongi-engine/beta/beta_node.rb +164 -0
  15. data/lib/wongi-engine/beta/filter_node.rb +109 -0
  16. data/lib/wongi-engine/beta/join_node.rb +127 -0
  17. data/lib/wongi-engine/beta/ncc_node.rb +46 -0
  18. data/lib/wongi-engine/beta/ncc_partner.rb +43 -0
  19. data/lib/wongi-engine/beta/neg_node.rb +58 -0
  20. data/lib/wongi-engine/beta/optional_node.rb +43 -0
  21. data/lib/wongi-engine/beta/or_node.rb +76 -0
  22. data/lib/wongi-engine/beta/production_node.rb +31 -0
  23. data/lib/wongi-engine/core_ext.rb +57 -0
  24. data/lib/wongi-engine/dsl.rb +112 -0
  25. data/lib/wongi-engine/dsl/action.rb +12 -0
  26. data/lib/wongi-engine/dsl/actions/error_generator.rb +42 -0
  27. data/lib/wongi-engine/dsl/actions/simple_action.rb +23 -0
  28. data/lib/wongi-engine/dsl/actions/simple_collector.rb +51 -0
  29. data/lib/wongi-engine/dsl/actions/statement_generator.rb +52 -0
  30. data/lib/wongi-engine/dsl/actions/trace_action.rb +52 -0
  31. data/lib/wongi-engine/dsl/any_rule.rb +48 -0
  32. data/lib/wongi-engine/dsl/dsl_builder.rb +44 -0
  33. data/lib/wongi-engine/dsl/dsl_extensions.rb +43 -0
  34. data/lib/wongi-engine/dsl/extension_clause.rb +36 -0
  35. data/lib/wongi-engine/dsl/generation_clause.rb +15 -0
  36. data/lib/wongi-engine/dsl/generic_production_rule.rb +78 -0
  37. data/lib/wongi-engine/dsl/ncc_production_rule.rb +21 -0
  38. data/lib/wongi-engine/dsl/production_rule.rb +4 -0
  39. data/lib/wongi-engine/dsl/query.rb +24 -0
  40. data/lib/wongi-engine/graph.rb +71 -0
  41. data/lib/wongi-engine/model_context.rb +13 -0
  42. data/lib/wongi-engine/network.rb +416 -0
  43. data/lib/wongi-engine/network/collectable.rb +42 -0
  44. data/lib/wongi-engine/network/debug.rb +25 -0
  45. data/lib/wongi-engine/ruleset.rb +74 -0
  46. data/lib/wongi-engine/template.rb +111 -0
  47. data/lib/wongi-engine/token.rb +137 -0
  48. data/lib/wongi-engine/version.rb +5 -0
  49. data/lib/wongi-engine/wme.rb +134 -0
  50. data/lib/wongi-engine/wme_match_data.rb +34 -0
  51. data/spec/dataset_spec.rb +26 -0
  52. data/spec/dsl_spec.rb +9 -0
  53. data/spec/high_level_spec.rb +341 -0
  54. data/spec/ruleset_spec.rb +54 -0
  55. data/spec/simple_action_spec.rb +40 -0
  56. data/spec/spec_helper.rb +1 -0
  57. data/spec/wme_spec.rb +83 -0
  58. data/wongi-engine.gemspec +19 -0
  59. 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