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