mort666-wongi-engine 0.2.9

Sign up to get free protection for your applications and to get access to all the features.
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,42 @@
1
+ module Wongi::Engine
2
+ module NetworkParts
3
+
4
+ module Collectable
5
+
6
+ def collectors name = nil
7
+ @collectors ||= { }
8
+ if name
9
+ @collectors[name] ||= [ ]
10
+ else
11
+ @collectors
12
+ end
13
+ end
14
+
15
+ def error_collectors
16
+ collectors :error
17
+ end
18
+
19
+ def add_collector collector, name
20
+ collectors( name ) << collector
21
+ end
22
+
23
+ def add_error_collector
24
+ add_collector collector, :error
25
+ end
26
+
27
+ def collection name
28
+ collectors( name ).map( &:default_collect ).flatten.uniq
29
+ end
30
+
31
+ def errors
32
+ error_collectors.map( &:errors ).flatten
33
+ end
34
+
35
+ def collected_tokens name
36
+ collectors( name ).map { |collector| collector.production.tokens }.flatten
37
+ end
38
+
39
+ end
40
+
41
+ end
42
+ end
@@ -0,0 +1,85 @@
1
+ module Wongi::Engine
2
+
3
+ module NetworkParts
4
+
5
+ module Debug
6
+
7
+ def full_wme_dump
8
+ @timeline.each_with_index do |slice, index|
9
+ puts "time #{ index - @timeline.length }"
10
+ slice.each do |key, alpha|
11
+ puts "\t#{alpha.template} -> [#{alpha.wmes.map(&:to_s).join ", "}]"
12
+ end
13
+ puts ""
14
+ end
15
+ puts "time 0"
16
+ alpha_hash.each do |key, alpha|
17
+ puts "\t#{alpha.template} -> [#{alpha.wmes.map(&:to_s).join ", "}]"
18
+ end
19
+ end
20
+
21
+ def full_dump io = $stdout
22
+
23
+ alpha_hash.each_value do |alpha|
24
+ io.puts "ALPHA #{alpha.template}"
25
+ alpha.wmes.each do |wme|
26
+ dump_wme wme, io
27
+ end
28
+ end
29
+ dump_beta beta_top, io
30
+
31
+ end
32
+
33
+ private
34
+
35
+ def token_lineage token
36
+ result = []
37
+ while token.parent
38
+ result << token.parent
39
+ token = token.parent
40
+ end
41
+ result
42
+ end
43
+
44
+ def dump_wme wme, io
45
+ io.puts "\tWME: #{wme.object_id} #{wme}"
46
+ wme.tokens.each { |token| io.puts "\t\tTOKEN #{token.object_id}" }
47
+ io.puts "\tGENERATING:" unless wme.generating_tokens.empty?
48
+ wme.generating_tokens.each { |token| io.puts "\t\tTOKEN #{token.object_id}" }
49
+ end
50
+
51
+ def dump_beta beta, io
52
+ case beta
53
+ when BetaMemory
54
+ dump_beta_memory beta, io
55
+ when NccNode
56
+ dump_ncc beta, io
57
+ else
58
+ io.puts "BETA #{beta.object_id} #{beta.class} : TODO"
59
+
60
+ end
61
+ io.puts "\tCHILDREN: #{beta.children.map(&:object_id).join ", "}"
62
+ beta.children.each { |child| dump_beta child, io } unless beta.children.empty?
63
+ end
64
+
65
+ def dump_beta_memory beta, io
66
+ io.puts "BETA MEMORY #{beta.object_id}"
67
+ beta.tokens.each { |token|
68
+ io.puts "\tTOKEN #{token.object_id} [#{token_lineage(token).map(&:object_id).map(&:to_s).join(" - ")}]"
69
+ token.wmes.each { |wme| io.puts "\t\tWME " + wme.object_id.to_s }
70
+ }
71
+ end
72
+
73
+ def dump_ncc beta, io
74
+ io.puts "NCC #{beta.object_id}"
75
+ beta.tokens.each { |token|
76
+ io.puts "\tTOKEN #{token.object_id} [#{token_lineage(token).map(&:object_id).map(&:to_s).join(" - ")}]"
77
+ token.wmes.each { |wme| io.puts "\t\tWME " + wme.object_id.to_s }
78
+ }
79
+ end
80
+
81
+ end
82
+
83
+ end
84
+
85
+ end
@@ -0,0 +1,74 @@
1
+ module Wongi
2
+ module Engine
3
+ class Ruleset
4
+
5
+ class << self
6
+
7
+ def [] name
8
+ raise Error, "undefined ruleset #{name}" unless rulesets.has_key?( name )
9
+ rulesets[ name ]
10
+ end
11
+
12
+ def register name, ruleset
13
+ raise Error, "ruleset #{name} already exists" if rulesets.has_key?( name )
14
+ rulesets[ name ] = ruleset
15
+ end
16
+
17
+ def rulesets
18
+ @rulesets ||= {}
19
+ end
20
+
21
+ def reset
22
+ @rulesets = { }
23
+ end
24
+
25
+ end
26
+
27
+ def initialize name = nil
28
+ @rules = []
29
+ self.name( name ) if name
30
+ end
31
+
32
+ def inspect
33
+ "<Ruleset #{name}>"
34
+ end
35
+
36
+ def install rete
37
+ # puts "Installing ruleset #{name}"
38
+ @rules.each { |rule| rete << rule }
39
+ rescue StandardError => e
40
+ e1 = Error.new "error installing ruleset '#{name||'<unnamed>'}': #{e}"
41
+ e1.set_backtrace e.backtrace
42
+ raise e1
43
+ end
44
+
45
+ def name name = nil
46
+ if name && ! @name
47
+ self.class.register name, self
48
+ @name = name
49
+ end
50
+ @name
51
+ end
52
+
53
+ # def uri uri = nil
54
+ # @uri = uri if uri
55
+ # @uri
56
+ # end
57
+
58
+ def rule name, &definition
59
+ r = DSL::Rule.new name
60
+ r.instance_eval &definition
61
+ @rules << r
62
+ r
63
+ end
64
+
65
+ def query name, &definition
66
+ r = DSL::Query.new name
67
+ r.instance_eval &definition
68
+ @rules << r
69
+ r
70
+ end
71
+
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,78 @@
1
+ module Wongi::Engine
2
+
3
+ Template = Struct.new( :subject, :predicate, :object ) do
4
+
5
+ def self.variable? thing
6
+ return false unless thing.is_a?(Symbol)
7
+ thing[0] >= 'A' && thing[0] <= 'Z'
8
+ end
9
+
10
+ # TODO: reintroduce Network#import when bringing back RDF support
11
+
12
+ def root?
13
+ subject == :_ && predicate == :_ && object == :_
14
+ end
15
+
16
+ def variables
17
+ [].tap do |a|
18
+ a << subject if Template.variable?( subject )
19
+ a << predicate if Template.variable?( predicate )
20
+ a << object if Template.variable?( object )
21
+ end
22
+ end
23
+
24
+ def hash
25
+ @hash ||= [subject.hash, predicate.hash, object.hash].hash
26
+ end
27
+
28
+ def self.hash_for *args
29
+ args.map( &:hash ).hash
30
+ end
31
+
32
+ def == other
33
+ other.is_a?(Template) && subject == other.subject && predicate == other.predicate && object == other.object
34
+ end
35
+
36
+ def =~ template
37
+ case template
38
+ when Template
39
+ ( template.subject == :_ || template.subject == subject ) &&
40
+ ( template.predicate == :_ || template.predicate == predicate ) &&
41
+ ( template.object == :_ || template.object == object )
42
+ else
43
+ raise Error, "templates can only match other templates"
44
+ end
45
+ end
46
+
47
+ def inspect
48
+ "<~#{subject.inspect} #{predicate.inspect} #{object.inspect}>"
49
+ end
50
+
51
+ def to_s
52
+ inspect
53
+ end
54
+
55
+ def resolve!(token)
56
+ s = if Template.variable?(subject)
57
+ raise DefinitionError, "unbound variable #{subject} in token #{token}" unless token.has_var?(subject)
58
+ token[subject]
59
+ else
60
+ subject
61
+ end
62
+ p = if Template.variable?(predicate)
63
+ raise DefinitionError, "unbound variable #{predicate} in token #{token}" unless token.has_var?(predicate)
64
+ token[predicate]
65
+ else
66
+ predicate
67
+ end
68
+ o = if Template.variable?(object)
69
+ raise DefinitionError, "unbound variable #{object} in token #{token}" unless token.has_var?(object)
70
+ token[object]
71
+ else
72
+ object
73
+ end
74
+ [s, p, o]
75
+ end
76
+
77
+ end
78
+ end
@@ -0,0 +1,114 @@
1
+ module Wongi::Engine
2
+
3
+ class Token
4
+
5
+ include CoreExt
6
+
7
+ attr_reader :children
8
+ attr_reader :wme
9
+ attr_reader :node
10
+ attr_reader :overlay
11
+ attr_accessor :owner, :parent
12
+ attr_reader :neg_join_results
13
+ attr_reader :opt_join_results
14
+ attr_reader :ncc_results
15
+ attr_reader :generated_wmes
16
+ attr_predicate :optional
17
+ attr_predicate :deleted
18
+
19
+ def initialize node, token, wme, assignments
20
+ @node, @parent, @wme, @assignments = node, token, wme, assignments
21
+ @overlay = wme ? wme.overlay.highest(token.overlay) : (token ? token.overlay : node.rete.default_overlay)
22
+ @children = []
23
+ @deleted = false
24
+ @neg_join_results = []
25
+ @opt_join_results = []
26
+ @ncc_results = []
27
+ @generated_wmes = []
28
+ token.children << self if token
29
+ end
30
+
31
+ def ancestors
32
+ if parent
33
+ parent.ancestors.unshift parent
34
+ else
35
+ []
36
+ end
37
+ end
38
+
39
+ def subst variable, value
40
+ @cached_assignments = nil
41
+ if @assignments.has_key? variable
42
+ @assignments[ variable ] = value
43
+ end
44
+ end
45
+
46
+ def assignments
47
+ @cached_assignments ||= all_assignments
48
+ end
49
+
50
+ def [] var
51
+ if a = assignments[ var ]
52
+ a.respond_to?(:call) ? a.call( self ) : a
53
+ end
54
+ end
55
+
56
+ def has_var? x
57
+ assignments.has_key? x
58
+ end
59
+
60
+ # TODO ignore assignments?
61
+ def duplicate? other
62
+ self.parent.equal?(other.parent) && @wme.equal?(other.wme) && self.assignments == other.assignments
63
+ end
64
+
65
+ def to_s
66
+ str = "TOKEN [ #{object_id} parent=#{parent ? parent.object_id : 'nil'} "
67
+ all_assignments.each_pair { |key, value| str << "#{key} => #{value} " }
68
+ str << "]"
69
+ str
70
+ end
71
+
72
+ def destroy
73
+ deleted!
74
+ end
75
+
76
+ def dispose!
77
+ parent.children.delete(self) if parent
78
+ neg_join_results.dup.each(&:unlink)
79
+ opt_join_results.dup.each(&:unlink)
80
+ @parent = nil
81
+ @wme = nil
82
+ end
83
+
84
+ # for neg feedback loop protection
85
+ def generated? wme
86
+ return true if generated_wmes.any? { |w| w == wme }
87
+ return children.any? { |t| t.generated? wme }
88
+ end
89
+
90
+ protected
91
+
92
+ def all_assignments
93
+ raise "Assignments is not a hash" unless @assignments.kind_of?( Hash )
94
+ if @parent
95
+ @parent.assignments.merge @assignments
96
+ else
97
+ @assignments
98
+ end
99
+ end
100
+
101
+ end
102
+
103
+ class FakeToken < Token
104
+ def initialize token, wme, assignments
105
+ @parent, @wme, @assignments = token, wme, assignments
106
+ @children = []
107
+ @neg_join_results = []
108
+ @opt_join_results = []
109
+ @ncc_results = []
110
+ @generated_wmes = []
111
+ end
112
+ end
113
+
114
+ end
@@ -0,0 +1,5 @@
1
+ module Wongi
2
+ module Engine
3
+ VERSION = "0.2.9"
4
+ end
5
+ end
@@ -0,0 +1,89 @@
1
+ module Wongi::Engine
2
+
3
+ WME = Struct.new( :subject, :predicate, :object ) do
4
+
5
+ include CoreExt
6
+
7
+ attr_reader :rete
8
+
9
+ attr_reader :generating_tokens
10
+ attr_reader :neg_join_results, :opt_join_results
11
+ attr_accessor :overlay
12
+ attr_predicate :deleted
13
+ attr_predicate :manual
14
+
15
+ def initialize s, p, o, r = nil
16
+
17
+ manual!
18
+
19
+ @deleted = false
20
+ @alphas = []
21
+ @generating_tokens = []
22
+ @neg_join_results = []
23
+ @opt_join_results = []
24
+
25
+ @rete = r
26
+
27
+ # TODO: reintroduce Network#import when bringing back RDF support
28
+ super( s, p, o )
29
+
30
+ end
31
+
32
+ def import_into r
33
+ self.class.new( subject, predicate, object, r ).tap do |wme|
34
+ wme.overlay = overlay
35
+ wme.manual = self.manual?
36
+ end
37
+ end
38
+
39
+ def dup
40
+ self.class.new( subject, predicate, object, rete ).tap do |wme|
41
+ wme.overlay = overlay
42
+ wme.manual = self.manual?
43
+ end
44
+ end
45
+
46
+ def == other
47
+ subject == other.subject && predicate == other.predicate && object == other.object
48
+ end
49
+
50
+ def =~ template
51
+ raise Wongi::Engine::Error, "Cannot match a WME against a #{template.class}" unless Template === template
52
+ result = match_member( self.subject, template.subject ) & match_member( self.predicate, template.predicate ) & match_member( self.object, template.object )
53
+ if result.match?
54
+ result
55
+ end
56
+ end
57
+
58
+ def generated?
59
+ !generating_tokens.empty?
60
+ end
61
+
62
+ def inspect
63
+ "{#{subject.inspect} #{predicate.inspect} #{object.inspect}}"
64
+ end
65
+
66
+ def to_s
67
+ inspect
68
+ end
69
+
70
+ def hash
71
+ @hash ||= [subject.hash, predicate.hash, object.hash].hash
72
+ end
73
+
74
+ protected
75
+
76
+ def match_member mine, theirs
77
+ result = WMEMatchData.new
78
+ if theirs == :_ || mine == theirs
79
+ result.match!
80
+ elsif Template.variable? theirs
81
+ result.match!
82
+ result[theirs] = mine
83
+ end
84
+ result
85
+ end
86
+
87
+ end
88
+
89
+ end