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