mort666-wongi-engine 0.2.9

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 (93) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +20 -0
  3. data/.hgignore +6 -0
  4. data/.hgtags +13 -0
  5. data/.ruby-gemset +1 -0
  6. data/.travis.yml +19 -0
  7. data/CHANGELOG.md +106 -0
  8. data/Gemfile +4 -0
  9. data/LICENSE +22 -0
  10. data/README.md +27 -0
  11. data/Rakefile +9 -0
  12. data/examples/ex01.rb +23 -0
  13. data/examples/ex02.rb +37 -0
  14. data/examples/graphviz.rb +16 -0
  15. data/examples/rdf.n3 +6 -0
  16. data/examples/rdf.rb +14 -0
  17. data/examples/timeline.rb +48 -0
  18. data/lib/wongi-engine.rb +36 -0
  19. data/lib/wongi-engine/alpha_memory.rb +60 -0
  20. data/lib/wongi-engine/beta.rb +11 -0
  21. data/lib/wongi-engine/beta/assignment_node.rb +40 -0
  22. data/lib/wongi-engine/beta/beta_memory.rb +49 -0
  23. data/lib/wongi-engine/beta/beta_node.rb +94 -0
  24. data/lib/wongi-engine/beta/filter_node.rb +48 -0
  25. data/lib/wongi-engine/beta/join_node.rb +140 -0
  26. data/lib/wongi-engine/beta/ncc_node.rb +67 -0
  27. data/lib/wongi-engine/beta/ncc_partner.rb +40 -0
  28. data/lib/wongi-engine/beta/neg_node.rb +115 -0
  29. data/lib/wongi-engine/beta/optional_node.rb +142 -0
  30. data/lib/wongi-engine/beta/or_node.rb +37 -0
  31. data/lib/wongi-engine/beta/production_node.rb +31 -0
  32. data/lib/wongi-engine/compiler.rb +115 -0
  33. data/lib/wongi-engine/core_ext.rb +63 -0
  34. data/lib/wongi-engine/data_overlay.rb +144 -0
  35. data/lib/wongi-engine/dsl.rb +132 -0
  36. data/lib/wongi-engine/dsl/action/base.rb +11 -0
  37. data/lib/wongi-engine/dsl/action/error_generator.rb +31 -0
  38. data/lib/wongi-engine/dsl/action/simple_action.rb +60 -0
  39. data/lib/wongi-engine/dsl/action/simple_collector.rb +52 -0
  40. data/lib/wongi-engine/dsl/action/statement_generator.rb +46 -0
  41. data/lib/wongi-engine/dsl/action/trace_action.rb +49 -0
  42. data/lib/wongi-engine/dsl/any_rule.rb +33 -0
  43. data/lib/wongi-engine/dsl/assuming.rb +31 -0
  44. data/lib/wongi-engine/dsl/builder.rb +44 -0
  45. data/lib/wongi-engine/dsl/clause/assign.rb +15 -0
  46. data/lib/wongi-engine/dsl/clause/fact.rb +71 -0
  47. data/lib/wongi-engine/dsl/clause/gen.rb +17 -0
  48. data/lib/wongi-engine/dsl/clause/generic.rb +38 -0
  49. data/lib/wongi-engine/dsl/generated.rb +43 -0
  50. data/lib/wongi-engine/dsl/ncc_subrule.rb +17 -0
  51. data/lib/wongi-engine/dsl/query.rb +24 -0
  52. data/lib/wongi-engine/dsl/rule.rb +84 -0
  53. data/lib/wongi-engine/enumerators.rb +21 -0
  54. data/lib/wongi-engine/error.rb +22 -0
  55. data/lib/wongi-engine/filter.rb +6 -0
  56. data/lib/wongi-engine/filter/asserting_test.rb +20 -0
  57. data/lib/wongi-engine/filter/equality_test.rb +36 -0
  58. data/lib/wongi-engine/filter/filter_test.rb +18 -0
  59. data/lib/wongi-engine/filter/greater_than_test.rb +36 -0
  60. data/lib/wongi-engine/filter/inequality_test.rb +36 -0
  61. data/lib/wongi-engine/filter/less_than_test.rb +36 -0
  62. data/lib/wongi-engine/graph.rb +73 -0
  63. data/lib/wongi-engine/network.rb +416 -0
  64. data/lib/wongi-engine/network/collectable.rb +42 -0
  65. data/lib/wongi-engine/network/debug.rb +85 -0
  66. data/lib/wongi-engine/ruleset.rb +74 -0
  67. data/lib/wongi-engine/template.rb +78 -0
  68. data/lib/wongi-engine/token.rb +114 -0
  69. data/lib/wongi-engine/version.rb +5 -0
  70. data/lib/wongi-engine/wme.rb +89 -0
  71. data/lib/wongi-engine/wme_match_data.rb +34 -0
  72. data/spec/beta_node_spec.rb +29 -0
  73. data/spec/bug_specs/issue_4_spec.rb +141 -0
  74. data/spec/dataset_spec.rb +27 -0
  75. data/spec/dsl_spec.rb +9 -0
  76. data/spec/filter_specs/assert_test_spec.rb +102 -0
  77. data/spec/filter_specs/less_test_spec.rb +41 -0
  78. data/spec/generation_spec.rb +116 -0
  79. data/spec/high_level_spec.rb +378 -0
  80. data/spec/network_spec.rb +182 -0
  81. data/spec/overlay_spec.rb +61 -0
  82. data/spec/rule_specs/any_rule_spec.rb +75 -0
  83. data/spec/rule_specs/assign_spec.rb +88 -0
  84. data/spec/rule_specs/assuming_spec.rb +66 -0
  85. data/spec/rule_specs/maybe_rule_spec.rb +101 -0
  86. data/spec/rule_specs/ncc_spec.rb +258 -0
  87. data/spec/rule_specs/negative_rule_spec.rb +105 -0
  88. data/spec/ruleset_spec.rb +54 -0
  89. data/spec/simple_action_spec.rb +40 -0
  90. data/spec/spec_helper.rb +3 -0
  91. data/spec/wme_spec.rb +83 -0
  92. data/wongi-engine.gemspec +40 -0
  93. metadata +212 -0
@@ -0,0 +1,40 @@
1
+ module Wongi
2
+ module Engine
3
+ class NccPartner < BetaNode
4
+ include TokenContainer
5
+
6
+ attr_accessor :ncc
7
+ attr_accessor :divergent
8
+
9
+ def beta_activate token
10
+ t = Token.new self, token, nil, {}
11
+ owner = owner_for( t )
12
+ t.overlay.add_token(t, self)
13
+ if owner
14
+ owner.ncc_results << t
15
+ t.owner = owner
16
+ owner.node.ncc_deactivate owner
17
+ end
18
+ end
19
+
20
+ def beta_deactivate t
21
+ token = tokens.find { |tok| tok.parent == t }
22
+ return unless token
23
+ token.overlay.remove_token(token, self)
24
+ if owner = token.owner
25
+ owner.ncc_results.delete token
26
+ if owner.ncc_results.empty?
27
+ ncc.ncc_activate owner
28
+ end
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ def owner_for token
35
+ divergent_token = token.ancestors.find { |t| t.node == divergent }
36
+ ncc.tokens.find { |t| t.ancestors.include? divergent_token }
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,115 @@
1
+ module Wongi
2
+ module Engine
3
+
4
+ NegJoinResult = Struct.new :token, :wme do
5
+ def unlink
6
+ wme.neg_join_results.delete self
7
+ token.neg_join_results.delete self
8
+ end
9
+ end
10
+
11
+ class NegNode < BetaNode
12
+ include TokenContainer
13
+
14
+ attr_reader :alpha, :tests
15
+
16
+ def initialize parent, tests, alpha, unsafe
17
+ super(parent)
18
+ @tests, @alpha, @unsafe = tests, alpha, unsafe
19
+ end
20
+
21
+ def alpha_activate wme
22
+ tokens.each do |token|
23
+ if matches?( token, wme ) && ( @unsafe || ! token.generated?( wme ) )# feedback loop protection
24
+ # order matters for proper invalidation
25
+ make_join_result(token, wme)
26
+ #token.delete_children #if token.neg_join_results.empty? # TODO why was this check here? it seems to break things
27
+ children.each do |child|
28
+ child.tokens.each do |t|
29
+ if t.parent == token
30
+ child.beta_deactivate t
31
+ #token.destroy
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+
39
+ def alpha_deactivate wme
40
+ wme.neg_join_results.dup.each do |njr|
41
+ tokens.each do |token|
42
+ next unless token == njr.token
43
+ njr.unlink
44
+ if token.neg_join_results.empty?
45
+ children.each do |child|
46
+ child.beta_activate Token.new( child, token, nil, {} )
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+
53
+ def beta_activate token
54
+ return if tokens.find { |et| et.duplicate? token }
55
+ token.overlay.add_token(token, self)
56
+ alpha.wmes.each do |wme|
57
+ if matches?( token, wme )
58
+ make_join_result(token, wme)
59
+ end
60
+ end
61
+ if token.neg_join_results.empty?
62
+ children.each do |child|
63
+ child.beta_activate Token.new( child, token, nil, {} )
64
+ end
65
+ end
66
+ end
67
+
68
+ def beta_deactivate token
69
+ return nil unless tokens.find token
70
+ token.overlay.remove_token(token, self)
71
+ token.deleted!
72
+ if token.parent
73
+ token.parent.children.delete token # should this go into Token#destroy?
74
+ end
75
+ token.neg_join_results.each &:unlink
76
+ children.each do |child|
77
+ child.tokens.each do |t|
78
+ if t.parent == token
79
+ child.beta_deactivate t
80
+ #token.destroy
81
+ end
82
+ end
83
+ end
84
+ token
85
+ end
86
+
87
+ def refresh_child child
88
+ tokens.each do |token|
89
+ if token.neg_join_results.empty?
90
+ child.beta_activate Token.new( child, token, nil, {} )
91
+ end
92
+ end
93
+ alpha.wmes.each do |wme|
94
+ alpha_activate wme
95
+ end
96
+ end
97
+
98
+ protected
99
+
100
+ def matches? token, wme
101
+ puts "matching #{wme} against #{token}" if debug?
102
+ @tests.each do |test|
103
+ return false unless test.matches?( token, wme )
104
+ end
105
+ true
106
+ end
107
+
108
+ def make_join_result token, wme
109
+ njr = NegJoinResult.new token, wme
110
+ token.neg_join_results << njr
111
+ wme.neg_join_results << njr
112
+ end
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,142 @@
1
+ module Wongi
2
+ module Engine
3
+
4
+ OptionalJoinResult = Struct.new :token, :wme do
5
+ def unlink
6
+ wme.opt_join_results.delete self
7
+ token.opt_join_results.delete self
8
+ end
9
+ end
10
+
11
+ class OptionalNode < BetaNode
12
+ include TokenContainer
13
+
14
+ attr_reader :alpha, :tests, :assignment_pattern
15
+
16
+ def initialize parent, alpha, tests, assignments
17
+ super( parent )
18
+ @alpha = alpha
19
+ @tests = tests
20
+ @assignment_pattern = assignments
21
+ end
22
+
23
+ def make_opt_result token, wme
24
+ jr = OptionalJoinResult.new token, wme
25
+ token.opt_join_results << jr
26
+ wme.opt_join_results << jr
27
+ end
28
+
29
+ def alpha_activate wme
30
+ assignments = collect_assignments( wme )
31
+ tokens.each do |token|
32
+ if matches? token, wme
33
+ children.each do |child|
34
+ if token.optional?
35
+ token.no_optional!
36
+ child.tokens.each do |ct|
37
+ child.beta_deactivate(ct) if ct.parent == token
38
+ end
39
+ end
40
+ child.beta_activate Token.new( child, token, wme, assignments )
41
+ end
42
+ make_opt_result token, wme
43
+ end
44
+ end
45
+ end
46
+
47
+ def alpha_deactivate wme
48
+ wme.opt_join_results.dup.each do |ojr|
49
+ tokens.each do |token|
50
+ next unless token == ojr.token
51
+ ojr.unlink
52
+ if token.opt_join_results.empty?
53
+ children.each do |child|
54
+ child.tokens.each do |ct|
55
+ child.beta_deactivate(ct) if ct.parent == token
56
+ end
57
+ token.optional!
58
+ child.beta_activate Token.new( child, token, nil, { } )
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+
65
+ def beta_activate t
66
+ return if tokens.find { |token| token.parent == t }
67
+ token = Token.new( self, t, nil, { } )
68
+ token.overlay.add_token(token, self)
69
+ match = false
70
+ alpha.wmes.each do |wme|
71
+ assignments = collect_assignments(wme)
72
+ if matches? token, wme
73
+ match = true
74
+ children.each do |child|
75
+ child.beta_activate Token.new( child, token, wme, assignments )
76
+ end
77
+ make_opt_result token, wme
78
+ end
79
+ end
80
+ unless match
81
+ token.optional!
82
+ children.each do |child|
83
+ child.beta_activate Token.new( child, token, nil, { } )
84
+ end
85
+ end
86
+ end
87
+
88
+ def beta_deactivate t
89
+ token = tokens.find { |token| token.parent == t }
90
+ return unless token
91
+ token.overlay.remove_token(token, self)
92
+ token.deleted!
93
+ if token.parent
94
+ token.parent.children.delete token
95
+ end
96
+ token.opt_join_results.each &:unlink
97
+ children.each do |child|
98
+ child.tokens.each do |t|
99
+ if t.parent == token
100
+ child.beta_deactivate t
101
+ end
102
+ end
103
+ end
104
+ token
105
+ end
106
+
107
+ def refresh_child child
108
+ tmp = children
109
+ self.children = [ child ]
110
+ refresh # do the beta part
111
+ alpha.wmes.each do |wme|
112
+ alpha_activate wme
113
+ end
114
+ self.children = tmp
115
+ end
116
+
117
+ private
118
+
119
+ def matches? token, wme
120
+ @tests.each do |test|
121
+ return false unless test.matches?( token, wme )
122
+ end
123
+ true
124
+ end
125
+
126
+ def collect_assignments wme
127
+ assignments = {}
128
+ return assignments if assignment_pattern.nil?
129
+ if assignment_pattern.subject != :_
130
+ assignments[ assignment_pattern.subject ] = TokenAssignment.new(wme, :subject)
131
+ end
132
+ if assignment_pattern.predicate != :_
133
+ assignments[ assignment_pattern.predicate ] = TokenAssignment.new(wme, :predicate)
134
+ end
135
+ if assignment_pattern.object != :_
136
+ assignments[ assignment_pattern.object ] = TokenAssignment.new(wme, :object)
137
+ end
138
+ assignments
139
+ end
140
+ end
141
+ end
142
+ end
@@ -0,0 +1,37 @@
1
+ module Wongi
2
+ module Engine
3
+ class OrNode < BetaMemory
4
+
5
+ attr_reader :parents
6
+ attr_reader :rete
7
+
8
+ def initialize parents
9
+ super nil
10
+ @parents = parents
11
+ parents.each do |parent|
12
+ parent.children << self
13
+ end
14
+ retes = parents.map( &:rete ).uniq
15
+ raise "Cannot combine variants from several Retes" if retes.size > 1
16
+ @rete = retes.first
17
+ end
18
+
19
+ def ident
20
+ ids = parents.map( &:id ).join ", "
21
+ "<R> #{self.class} #{id}, parents #{ids}"
22
+ end
23
+
24
+
25
+ def depth
26
+ parents.map( &:depth ).max + 1
27
+ end
28
+
29
+ def refresh
30
+ parents.each do |parent|
31
+ parent.refresh_child self
32
+ end
33
+ end
34
+
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,31 @@
1
+ module Wongi
2
+ module Engine
3
+
4
+ class ProductionNode < BetaMemory
5
+
6
+ attr_accessor :tracer
7
+ attr_accessor :compilation_context
8
+
9
+ def initialize parent, actions
10
+ super(parent)
11
+ @actions = actions.each { |action| action.production = self }
12
+ end
13
+
14
+ def beta_activate token
15
+ return unless super
16
+ @actions.each do |action|
17
+ action.execute token if action.respond_to? :execute
18
+ end
19
+ end
20
+
21
+ def beta_deactivate token
22
+ return unless super
23
+ @actions.each do |action|
24
+ action.deexecute token if action.respond_to? :deexecute
25
+ end
26
+ end
27
+
28
+ end
29
+
30
+ end
31
+ end
@@ -0,0 +1,115 @@
1
+ module Wongi::Engine
2
+ Compiler = Struct.new(:rete, :node, :conditions, :parameters, :alpha_deaf) do
3
+
4
+ def compile
5
+ conditions.inject(self) do |context, condition|
6
+ condition.compile context
7
+ end.node
8
+ end
9
+
10
+ def declares_variable?(v)
11
+ parameters.include?(v) || declared_variables.include?(v)
12
+ end
13
+
14
+ def declare(v)
15
+ unless declared_variables.include?(v)
16
+ declared_variables << v
17
+ end
18
+ end
19
+
20
+ def declared_variables
21
+ @declared_variables ||= []
22
+ end
23
+
24
+ def dup
25
+ Compiler.new(rete, node, conditions, parameters, alpha_deaf).tap do |compiler|
26
+ declared_variables.each { |v| compiler.declare(v) }
27
+ end
28
+ end
29
+
30
+ # TODO: should the following be the responsibility of Compiler or of each individual DSL clause?
31
+
32
+ def beta_memory
33
+ return if node.is_a?(BetaMemory)
34
+ self.node = if existing = node.children.find { |n| n.is_a?(BetaMemory) }
35
+ existing
36
+ else
37
+ BetaMemory.new(node).tap do |memory|
38
+ memory.refresh
39
+ end
40
+ end
41
+ end
42
+
43
+ def assignment_node(variable, body)
44
+ beta_memory
45
+ self.node = AssignmentNode.new(self.node, variable, body).tap &:refresh
46
+ declare(variable)
47
+ end
48
+
49
+ def join_node(condition, tests, assignment)
50
+ alpha = rete.compile_alpha(condition)
51
+ beta_memory
52
+ self.node = if existing = node.children.find { |n| n.is_a?(JoinNode) && n.equivalent?(alpha, tests, assignment) }
53
+ existing
54
+ else
55
+ JoinNode.new(node, tests, assignment).tap do |join|
56
+ join.alpha = alpha
57
+ alpha.betas << join unless alpha_deaf
58
+ end
59
+ end
60
+ end
61
+
62
+ def neg_node(condition, tests, unsafe)
63
+ alpha = rete.compile_alpha(condition)
64
+ self.node = NegNode.new(node, tests, alpha, unsafe).tap do |node|
65
+ alpha.betas << node unless alpha_deaf
66
+ node.refresh
67
+ end
68
+ end
69
+
70
+ def opt_node(condition, tests, assignment)
71
+ alpha = rete.compile_alpha(condition)
72
+ beta_memory
73
+ self.node = OptionalNode.new(node, alpha, tests, assignment).tap do |node|
74
+ alpha.betas << node unless alpha_deaf
75
+ end
76
+ end
77
+
78
+ def or_node(variants)
79
+ beta_memory
80
+ branches = variants.map do |variant|
81
+ subcompiler = Compiler.new(rete, node, variant.conditions, parameters, false)
82
+ declared_variables.each { |v| subcompiler.declare(v) }
83
+ subcompiler.compile
84
+ subcompiler.declared_variables.each { |v| declare(v) }
85
+ subcompiler.node
86
+ end
87
+ self.node = OrNode.new(branches).tap &:refresh
88
+ end
89
+
90
+ def ncc_node(subrule, alpha_deaf)
91
+ beta_memory
92
+ subcompiler = Compiler.new(rete, node, subrule.conditions, parameters, alpha_deaf)
93
+ declared_variables.each { |v| subcompiler.declare(v) }
94
+ bottom = subcompiler.compile
95
+ if existing = node.children.find { |n| n.kind_of?( NccNode ) and n.partner.parent == bottom }
96
+ self.node = existing
97
+ return
98
+ end
99
+ ncc = NccNode.new node
100
+ partner = NccPartner.new subcompiler.tap(&:beta_memory).node
101
+ ncc.partner = partner
102
+ partner.ncc = ncc
103
+ partner.divergent = node
104
+ # partner.conjuncts = condition.children.size
105
+ ncc.refresh
106
+ partner.refresh
107
+ self.node = ncc
108
+ end
109
+
110
+ def filter_node(filter)
111
+ beta_memory
112
+ self.node = FilterNode.new(node, filter)
113
+ end
114
+ end
115
+ end