omf_rete 0.5 → 0.6.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.
@@ -0,0 +1,118 @@
1
+ require 'set'
2
+ require 'omf_rete/store/alpha/alpha_inner_element'
3
+ require 'omf_rete/store/alpha/alpha_leaf_element'
4
+
5
+ module OMF::Rete::Store
6
+
7
+ #
8
+ # Class to store tuples for use in MoanaFilter
9
+ #
10
+ class AlphaStore #< MObject
11
+ include OMF::Rete::Store
12
+
13
+ attr_reader :length
14
+
15
+ # Initialize a tuple store for tuples of
16
+ # fixed length +length+.
17
+ #
18
+ def initialize(length, opts = {})
19
+ @length = length
20
+ @root = Alpha::AlphaInnerElement.new(0, length)
21
+ @index = []
22
+ length.times do @index << {} end
23
+ end
24
+
25
+
26
+ # Register a +TSet+ and add all tuples currently
27
+ # and in the future matching +pattern+
28
+ #
29
+ def registerTSet(tset, pattern)
30
+ #puts "registerTSet: #{pattern}"
31
+ pat = pattern.collect do |el|
32
+ (el.is_a?(Symbol) && el.to_s.end_with?('?')) ? nil : el
33
+ end
34
+ @root.registerTSet(tset, pat)
35
+ # seed tset which already stored data
36
+ find(pat).each do |t|
37
+ tset.addTuple(t)
38
+ end
39
+ tset
40
+ end
41
+
42
+ def createTSet(description, indexPattern)
43
+ tset = Moana::Filter::IndexedTupleSet.new(description, indexPattern)
44
+ registerTSet(tset, description)
45
+ tset
46
+ end
47
+
48
+ def addTuple(tarray)
49
+ @length.times do |i|
50
+ item = tarray[i]
51
+ ia = @index[i][item] ||= Set.new
52
+ unless ia.add?(tarray)
53
+ return # this is a duplicate
54
+ end
55
+ end
56
+ @root.addTuple(tarray)
57
+ end
58
+
59
+ # Remove a tuple from the store
60
+ #
61
+ def removeTuple(tarray)
62
+ @length.times do |i|
63
+ item = tarray[i]
64
+ if ia = @index[i][item]
65
+ ia.delete(tarray)
66
+ end
67
+ end
68
+ @root.removeTuple(tarray)
69
+ end
70
+
71
+ # Return a set of tuples which match +pattern+. Pattern is
72
+ # a tuples of the same length this store is configured for
73
+ # where any non-nil element is matched directly and any
74
+ # nil element is considered a wildcard.
75
+ #
76
+ def find(pattern)
77
+ #puts "patern: #{pattern.inspect}"
78
+ seta = []
79
+ allWildcards = true
80
+ @length.times do |i|
81
+ if (item = pattern[i])
82
+ if (item != :_)
83
+ allWildcards = false
84
+ res = @index[i][item] || Set.new
85
+ #puts "res: index #{i}, res: #{res.inspect}"
86
+ seta << res
87
+ end
88
+ end
89
+ end
90
+
91
+ if (allWildcards)
92
+ res = Set.new
93
+ @index[0].each_value do |s|
94
+ res.merge(s)
95
+ end
96
+ return res
97
+ end
98
+ # get intersection of all returned sets
99
+ if (seta.empty?)
100
+ return Set.new
101
+ end
102
+ res = nil
103
+ seta.each do |s|
104
+ if res
105
+ res = res.intersection(s)
106
+ else
107
+ res = s
108
+ end
109
+ #puts "merge: in: #{s.inspect}, res: #{res.inspect}"
110
+ end
111
+ return res
112
+ end
113
+
114
+ def to_s()
115
+ "Store"
116
+ end
117
+ end # Store
118
+ end # Moana
@@ -0,0 +1,38 @@
1
+ require 'omf_rete/store/alpha_store'
2
+
3
+ module OMF::Rete::Store
4
+
5
+ class WrongNameException < StoreException
6
+ def initialize(name, tuple)
7
+ super "Expected first element in '#{tuple}' to be '#{name}'"
8
+ end
9
+ end
10
+
11
+ #
12
+ # This is a store where the first element in each
13
+ # tuple is supposed to have the same name.
14
+ #
15
+ class NamedAlphaStore < AlphaStore
16
+ include OMF::Rete::Store
17
+
18
+ # Initialize a tuple store for tuples of
19
+ # fixed length +length+ where the first element
20
+ # is always 'name' (included in length)
21
+ #
22
+ def initialize(name, length, opts = {})
23
+ @name = name
24
+ super length, opts
25
+ #@store = AlphaStore.new(length - 1)
26
+ end
27
+
28
+ def addTuple(tarray)
29
+ unless tarray[0] == @name
30
+ raise WrongNameException.new(@name, tarray)
31
+ end
32
+ super
33
+ #@store.addTuple(tarray[1 .. -1])
34
+ end
35
+
36
+
37
+ end
38
+ end
@@ -0,0 +1,119 @@
1
+ require 'set'
2
+ require 'omf_rete/store/predicate_store'
3
+
4
+ module OMF::Rete::Store
5
+
6
+ class WrongPatternLengthException < StoreException
7
+ def initialize(exp_length, tuple)
8
+ super "Expected tuple '#{tuple}' to be of length '#{exp_length}'"
9
+ end
10
+ end
11
+
12
+ #
13
+ # Turns a standard object into a store.
14
+ #
15
+ # NOTE: This store is NOT tracking changes to the
16
+ # state of the object. It is assumed to stay constant
17
+ # while it is in this store.
18
+ #
19
+ class ObjectStore
20
+ include OMF::Rete::Store
21
+
22
+
23
+
24
+ # @param opts :include_object Make the fist element the object
25
+ def initialize(opts = {})
26
+ @include_object = opts[:name] || (opts[:include_object] == true ? self : nil)
27
+ @object = nil
28
+ @tsets = {}
29
+ end
30
+
31
+ # Make this store represent 'obj'. this will
32
+ # 'eject' the currently represented object (if exist)
33
+ #
34
+ def representObject(obj)
35
+ @object = obj
36
+ # First clear registered tsets and then seed with state from 'obj'
37
+ @tsets.each do |tset, pat|
38
+ tset.clear()
39
+ end
40
+ @tsets.each do |tset, pat|
41
+ find(pat).each do |t|
42
+ tset.addTuple(t)
43
+ end
44
+ end
45
+ obj
46
+ end
47
+
48
+ # Register a +TSet+ and add all the
49
+ # object's state matching the pattern
50
+ #
51
+ def registerTSet(tset, pattern)
52
+ pat = pattern.collect do |el|
53
+ (el.is_a?(Symbol) && el.to_s.end_with?('?')) ? nil : el
54
+ end
55
+ @tsets[tset] = pat
56
+
57
+ # seed tset which already stored data
58
+ find(pat).each do |t|
59
+ tset.addTuple(t)
60
+ end
61
+ tset
62
+ end
63
+
64
+ # Return a set of tuples which match +pattern+. Pattern is
65
+ # a tuples where the first element (include_object == false)
66
+ # or the second element (include_object == true) is a property
67
+ # of this object. If the next element is nil, return the value.
68
+ # if the next one is not nil, only return a tuple if it is set
69
+ # to the same value.
70
+ #
71
+ # If the property value is an Enumerable, return a separate tuple
72
+ # for every value returned by the enumerable.
73
+ #
74
+ def find(pattern)
75
+ res = Set.new
76
+ if @include_object
77
+ raise WrongPatternLengthException.new(3, pattern) unless pattern.length == 3
78
+ obj = pattern[0]
79
+ return res if obj && obj != @include_object # not for us
80
+ else
81
+ raise WrongPatternLengthException.new(2, pattern) unless pattern.length == 2
82
+ end
83
+ unless pred = @include_object ? pattern[1] : pattern[0]
84
+ raise OMF::Rete::Store::UnknownPredicateException.new(pred, pattern)
85
+ end
86
+ pred = pred.to_sym
87
+ return res unless @object.respond_to? pred
88
+
89
+ val = @object.send(pred)
90
+ if exp_value = @include_object ? pattern[2] : pattern[1]
91
+ # Only return tuple if identical
92
+ a = [pred, exp_value]
93
+ a.insert(0, @include_object) if @include_object
94
+ # need to check if same
95
+ if (val.is_a?(Enumerable) ? val.include?(exp_value) : val == exp_value)
96
+ res << a
97
+ end
98
+ return res
99
+ end
100
+
101
+ a = [pred]
102
+ a.insert(0, @include_object) if @include_object
103
+ if val.is_a?(Enumerable)
104
+ res = Set.new(val.map {|v| a.dup << v})
105
+ else
106
+ res << (a << val)
107
+ end
108
+ return res
109
+ end
110
+
111
+ def to_s()
112
+ "ObjectStore"
113
+ end
114
+
115
+ def confirmLength(tuple)
116
+ tuple.is_a?(Array) && tuple.length == (@include_object ? 3 : 2)
117
+ end
118
+ end # class
119
+ end # module
@@ -0,0 +1,96 @@
1
+ require 'omf_rete/store/named_alpha_store'
2
+
3
+ module OMF::Rete::Store
4
+
5
+ class UnknownPredicateException < StoreException
6
+ def initialize(pred, tuple, stores = {})
7
+ if pred
8
+ super "Unknown predicate '#{pred}' in '#{tuple}' - (#{stores.keys})"
9
+ else
10
+ super "Missing predicate in '#{tuple}'"
11
+ end
12
+ end
13
+ end
14
+
15
+ class AlreadyRegisteredPredicateException < StoreException
16
+ def initialize(pred)
17
+ super "Predicate '#{pred}' is already registered"
18
+ end
19
+ end
20
+
21
+ #
22
+ # This store supports 'predicate' tuples. The predicate of a tuple is identified
23
+ # by it's first element and each predicate can have a different scheme (tuple length).
24
+ # Each predicate needs to be registered through 'registerPredicate'.
25
+ #
26
+ # Subscriptions and 'find' queries need to name the predicate. In other words, they CANNOT
27
+ # span multiple predicates.
28
+ #
29
+ class PredicateStore
30
+ include OMF::Rete::Store
31
+
32
+ def initialize(opts = {})
33
+ @stores = {}
34
+ end
35
+
36
+ # Register a new predicate 'pred_name' whose tuples are of 'length'.
37
+ #
38
+ def registerPredicate(pred_name, length, opts = {})
39
+ pred_name = pred_name.to_sym
40
+ if @stores.key? pred_name
41
+ raise AlreadyRegisteredPredicateException.new(pred_name)
42
+ end
43
+ @stores[pred_name] = NamedAlphaStore.new(pred_name, length, opts)
44
+ end
45
+
46
+ # Register a 'store' for predicate 'pred_name'.
47
+ #
48
+ def registerPredicateStore(pred_name, store)
49
+ pred_name = pred_name.to_sym
50
+ if @stores.key? pred_name
51
+ raise AlreadyRegisteredPredicateException.new(pred_name)
52
+ end
53
+ @stores[pred_name] = store
54
+ end
55
+
56
+ ## Store API ###
57
+
58
+ def registerTSet(tset, pattern)
59
+ tset = get_store(pattern).registerTSet(tset, pattern)
60
+ #puts ">>> Register tset - #{pattern} - #{tset}"
61
+ tset
62
+ end
63
+
64
+ def addTuple(tarray)
65
+ get_store(tarray).addTuple(tarray)
66
+ end
67
+
68
+ def removeTuple(tarray)
69
+ get_store(tarray).removeTuple(tarray)
70
+ end
71
+
72
+ def find(pattern)
73
+ get_store(pattern).find(pattern)
74
+ end
75
+
76
+ def to_s()
77
+ "Predicate Store"
78
+ end
79
+
80
+ def confirmLength(tuple)
81
+ tuple.is_a?(Array) && get_store(tuple).confirmLength(tuple)
82
+ end
83
+
84
+
85
+ protected
86
+
87
+ def get_store(tuple)
88
+ pred = tuple[0]
89
+ unless !pred.nil? && store = @stores[pred.to_sym]
90
+ raise UnknownPredicateException.new(pred, tuple, @stores)
91
+ end
92
+ store
93
+ end
94
+
95
+ end # class
96
+ end # module
@@ -1,26 +1,40 @@
1
1
 
2
2
  require 'omf_rete'
3
3
 
4
-
4
+
5
5
  module OMF::Rete::Store
6
+
7
+ class StoreException < Exception; end
8
+
9
+ class NotImplementedException < StoreException; end
10
+
6
11
  DEF_TYPE = :alpha
7
-
8
- def self.create(length, opts = {})
12
+
13
+ def self.create(length = -1, opts = {})
9
14
  case (type = opts[:type] || DEF_TYPE)
10
15
  when :alpha
11
- require 'omf_rete/store/alpha/alpha_store'
12
- return OMF::Rete::Store::Alpha::Store.new(length, opts)
16
+ require 'omf_rete/store/alpha_store'
17
+ return OMF::Rete::Store::AlphaStore.new(length, opts)
18
+ when :named_alpha
19
+ require 'omf_rete/store/named_alpha_store'
20
+ return OMF::Rete::Store::NamedAlphaStore.new(opts.delete(:name), length, opts)
21
+ when :predicate
22
+ require 'omf_rete/store/predicate_store'
23
+ return PredicateStore.new(opts)
24
+ when :object
25
+ require 'omf_rete/store/object_store'
26
+ return ObjectStore.new(opts)
13
27
  else
14
28
  raise "Unknown store type '#{type}'"
15
29
  end
16
30
  end
17
-
31
+
18
32
  #--- INTERFACE ---
19
-
20
- def query(queryPattern, projectPattern = nil, &block)
21
- raise "'query' - Not implemented."
22
- end
23
-
33
+
34
+ # def query(queryPattern, projectPattern = nil, &block)
35
+ # raise "'query' - Not implemented."
36
+ # end
37
+
24
38
  def subscribe(name, query, out_pattern = nil, &block)
25
39
  require 'omf_rete/planner/plan_builder'
26
40
 
@@ -28,14 +42,30 @@ module OMF::Rete::Store
28
42
  pb.build
29
43
  pb.materialize(out_pattern, &block)
30
44
  end
31
-
45
+ alias :add_rule :subscribe
46
+
32
47
  def addTuple(tarray)
48
+ raise NotImplementedException.new
33
49
  end
34
-
50
+
35
51
  # alias
36
52
  def add(*els)
37
53
  addTuple(els)
38
54
  end
55
+ alias :add_fact :add
56
+
57
+ # Remove a tuple from the store
58
+ #
59
+ def removeTuple(*els)
60
+ raise NotImplementedException.new
61
+ end
62
+
63
+ # alias
64
+ def remove(*els)
65
+ removeTuple(els)
66
+ end
67
+ alias :remove_fact :remove
68
+
39
69
 
40
70
  # Return a set of tuples which match +pattern+. Pattern is
41
71
  # a tuples of the same length this store is configured for
@@ -43,6 +73,7 @@ module OMF::Rete::Store
43
73
  # nil element is considered a wildcard.
44
74
  #
45
75
  def find(pattern)
76
+ raise NotImplementedException.new
46
77
  end
47
78
 
48
79
  # Register a function to be called whenever a query is performed
@@ -52,6 +83,20 @@ module OMF::Rete::Store
52
83
  # pattern.
53
84
  #
54
85
  def on_query(&requestProc)
86
+ raise NotImplementedException.new
55
87
  end
56
88
 
89
+ def createTSet(description, indexPattern)
90
+ tset = OMF::Rete::IndexedTupleSet.new(description, indexPattern)
91
+ registerTSet(tset, description)
92
+ tset
93
+ end
94
+
95
+ # Return true if tuple (or pattern) is a valid one for this store
96
+ #
97
+ def confirmLength(tuple)
98
+ tuple.is_a?(Array) && tuple.length == @length
99
+ end
100
+
101
+
57
102
  end
@@ -0,0 +1,53 @@
1
+
2
+ module OMF::Rete
3
+ #
4
+ # This class represents a tuple and includes various ways to access
5
+ # the contained elements..
6
+ #
7
+ class Tuple
8
+ attr_reader :description
9
+
10
+ # Return content of tuple as array of elements. The order and names are
11
+ # contained in 'description'. Use the #[] method to more robustly access
12
+ # individual elements.
13
+ #
14
+ def to_a
15
+ @tarray
16
+ end
17
+
18
+ # Return content of tuple as a hash. The key is taken from the 'description'.
19
+ #
20
+ def to_hash
21
+ h = {}
22
+ @description.each_with_index do |n, i|
23
+ name = n.to_s.chomp('?').to_sym
24
+ h[name] = @tarray[i]
25
+ end
26
+ h
27
+ end
28
+
29
+ # Return a specific element either indicated by index (number) or name
30
+ # as listed in 'description'.
31
+ #
32
+ def [](index_or_name)
33
+ if index_or_name.is_a? Integer
34
+ return @tarray[index_or_number]
35
+ elsif index_or_name.is_a? Symbol
36
+ @description.each_with_index do |n, i|
37
+ return @tarray[i] if n == index_or_name
38
+ end
39
+ end
40
+ raise "Unknown element name: '#{index_or_name}'"
41
+ end
42
+
43
+ def initialize(tarray, description)
44
+ @tarray = tarray
45
+ @description = description
46
+ end
47
+
48
+ def method_missing(name, *args, &block)
49
+ self[name]
50
+ end
51
+
52
+ end
53
+ end