omf_rete 0.5 → 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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