ruleby 0.1 → 0.2

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.
@@ -2,16 +2,16 @@ require 'ruleby'
2
2
 
3
3
  class Rulebook
4
4
  include Ruleby
5
- def initialize(rete)
6
- @rete = rete
5
+ def initialize(engine)
6
+ @engine = engine
7
7
  end
8
8
 
9
- attr_reader :rete
9
+ attr_reader :engine
10
10
 
11
11
  def rule(name, &block)
12
12
  r = Core::Rule.new name
13
13
  yield r if block_given?
14
- @rete.assert_rule r
14
+ @engine.assert_rule r
15
15
  r
16
16
  end
17
17
 
@@ -19,187 +19,267 @@ class Rulebook
19
19
  return Core::Action.new(&block)
20
20
  end
21
21
 
22
- def pattern(&block)
23
- pb = PatternBuilder.new
24
- yield pb
25
- return pb.condition
26
- end
27
22
  end
28
23
 
29
-
30
- class PatternInternal
31
- def initialize(tag, type)
32
- @tag = tag
33
- @type = type
34
- @atoms = []
24
+ class WhenBuilder < Rulebook
25
+ def initialize()
26
+ @pattern_hash = Hash.new
27
+ @pattern_keys = []
28
+ end
29
+
30
+ def method_missing(method_id, *args, &block)
31
+ method = method_id.to_sym
32
+ wi = nil
33
+ if @pattern_hash.key? method
34
+ wi = @pattern_hash[method]
35
+ elsif :not == method
36
+ @pattern_keys.push method
37
+ return self
38
+ else
39
+ wi = WhenInternal.new method, args[0]
40
+ @pattern_hash[method] = wi
41
+ @pattern_keys.push method
35
42
  end
36
-
37
- def atom(tag, name, references, &block)
38
- if references
39
- references = [references] unless references.kind_of?(Array)
40
- return Core::ReferenceAtom.new(tag, name, references, &block)
43
+ return wi
44
+ end
45
+ def pattern
46
+ operands = []
47
+ nt = false
48
+ @pattern_keys.each do |key|
49
+ if :not != key
50
+ wi = @pattern_hash[key]
51
+ tag = wi.tag
52
+ type = wi.type
53
+ atoms = wi.to_atoms
54
+ p = nil
55
+ if nt
56
+ p = Core::NotPattern.new(tag, type, atoms)
57
+ nt = false
58
+ else
59
+ p = Core::ObjectPattern.new(tag, type, atoms)
60
+ end
61
+ operands = operands + [p]
41
62
  else
42
- return Core::PropertyAtom.new(tag, name, &block)
63
+ nt = true
43
64
  end
44
65
  end
45
- def method_missing(method_id, *args, &block)
46
- if block_given?
47
- with(method_id.to_s, *args, &block)
48
- else
49
- with(method_id.to_s, *args)
50
- end
66
+ return and_pattern(operands)
67
+ end
68
+ end
69
+
70
+ class WhenInternal
71
+ public_instance_methods.each do |m|
72
+ a = [:method_missing, :new, :public_instance_methods, :__send__, :__id__]
73
+ undef_method m.to_sym unless a.include? m.to_sym
74
+ end
75
+
76
+ attr_reader :tag, :type
77
+ def initialize(tag, type)
78
+ @tag = tag
79
+ @type = type
80
+ @builder = WhenPropertyBuilder.new self
81
+ end
82
+
83
+ def to_atoms
84
+ atoms = []
85
+ tags = [@tag]
86
+ @builder.property_hash.each_value do |wp|
87
+ tags.push wp.tag if wp.tag
88
+ end
89
+ @builder.property_keys.each do |key|
90
+ wp = @builder.property_hash[key]
91
+ atoms = atoms + [wp.to_atom(tags)]
51
92
  end
52
- def with(property, options={}, &block)
53
- tag = options[:name]
54
- tag = GeneratedTag.new if tag == options.default
55
- if block_given?
56
- @atoms.push atom(tag, property, options[:references], &block)
93
+ return atoms
94
+ end
95
+
96
+ def &
97
+ return self
98
+ end
99
+
100
+ def method_missing(method_id, *args, &block)
101
+ m = method_id.to_s
102
+ suffix = m.to_s[-1..-1]
103
+ if suffix == '='
104
+ new_m = m[0,m.size-1]
105
+ if args[0].class == Array && args[0].size > 1 && args[0][1] == :%
106
+ wp = @builder.create new_m do |x,y| x == y end
107
+ wp.references args[0][0]
108
+ return wp
57
109
  else
58
- # QUESTION do we really want to do this? What if someone does:
59
- # :equals => -1
60
- # How do we handle a case where it is not a ReferenceAtom? should we
61
- # have a :equals_ref key too?
62
- # if options[:equals] != options.default
63
- # @atoms.push(atom(tag, property, [options[:equals]]) do |p,a| p==a end)
64
- # elsif options[:not_equals] != options.default
65
- # @atoms.push(atom(tag, property, [options[:not_equals]]) do |p,a| p!=a end)
66
- # else
67
- @atoms.push(atom(tag, property, nil) do |p| true end)
68
- # end
110
+ wp = @builder.create new_m do |x| x == args[0] end
111
+ return wp
69
112
  end
113
+ else
114
+ wp = @builder.create(m, &block)
115
+ if args.size > 0 && args[0]
116
+ if block_given?
117
+ wp.references args[0]
118
+ else
119
+ wp.tag = args[0]
120
+ end
121
+ end
122
+ return wp
123
+ end
124
+ end
125
+ end
126
+
127
+ class WhenPropertyBuilder
128
+ attr_reader:property_hash
129
+ attr_reader:property_keys
130
+
131
+ def initialize(parent)
132
+ @parent = parent
133
+ @property_hash = Hash.new
134
+ @property_keys = []
135
+ end
136
+
137
+ def create(method_id,&block)
138
+ method = method_id.to_sym
139
+ wp = nil
140
+ if @property_hash.key? method
141
+ wp = @property_hash[method]
142
+ else
143
+ wp = WhenProperty.new @parent, method do |p| true end
144
+ @property_hash[method] = wp
145
+ @property_keys.push method
70
146
  end
71
-
72
- def pattern
73
- @atoms = Array.new(1){@atoms} unless @atoms.kind_of?(Array)
74
- return Core::ObjectPattern.new(@tag, @type, @atoms)
75
- end
76
-
77
- def not
78
- @atoms = Array.new(1){@atoms} unless @atoms.kind_of?(Array)
79
- return Core::NotPattern.new(@tag, @type, @atoms)
147
+ if block_given?
148
+ wp.block = block
80
149
  end
81
- end
82
- class PatternInternal
83
- public_instance_methods.each do |m|
84
- a = [:not, :pattern, :with, :method_missing, :atom, :new, :public_instance_methods, :__send__, :__id__]
85
- undef_method m.to_sym unless a.include? m.to_sym
150
+ return wp
86
151
  end
87
152
  end
88
153
 
89
- class PatternBuilder < Rulebook
90
-
91
- attr_reader :condition
92
-
93
- def initialize()
94
- @condition = nil
154
+ class WhenProperty
155
+ attr_accessor :block
156
+ def initialize(parent,name, &block)
157
+ @tag = nil
158
+ @name = name
159
+ @references = nil
160
+ @block = block
161
+ @parent = parent
162
+ end
163
+ attr:tag,true
164
+ attr:type,true
165
+ attr:value,true
166
+
167
+ def &
168
+ return @parent
169
+ end
170
+
171
+ def bind(n)
172
+ @tag = n
173
+ end
174
+
175
+ def not=(value,ref=nil)
176
+ if ref && ref == :%
177
+ raise 'Using \'not=\' for references is not yet supported'
178
+ set_block do |x,y| x != y end
179
+ references value
180
+ else
181
+ set_block do |s| s != value end
95
182
  end
96
183
 
97
- def has(type, tag=GeneratedTag.new, &block)
98
- pi = pattern_internal(tag, type)
99
- yield pi if block_given?
100
- op = pi.pattern
101
- if(@condition)
102
- pat = [@condition, op]
103
- @condition = and_pattern(pat)
104
- else
105
- @condition = op
106
- end
107
- @condition
108
- end
109
- def and(operands)
110
- if @condition
111
- p = [@condition] + operands
112
- @condition = and_pattern(p)
113
- else
114
- @condition = and_pattern(operands)
115
- end
116
- @condition
184
+ end
185
+ def set_block(&block)
186
+ @block = block
187
+ end
188
+ private:set_block
189
+
190
+ def references(refs)
191
+ @references = refs
192
+ end
193
+
194
+ def to_atom(pattern_tags)
195
+ unless @tag
196
+ @tag = GeneratedTag.new
117
197
  end
118
-
119
- def or(operands)
120
- if(@condition)
121
- p = [@condition] + operands
122
- @condition = or_pattern(p)
198
+ if @references
199
+ @references = [@references] unless @references.kind_of?(Array)
200
+ i = includes_how_many(@references, pattern_tags)
201
+ if i == 0
202
+ return Core::ReferenceAtom.new(@tag, @name, @references, &@block)
203
+ elsif i == @references.size
204
+ return Core::SelfReferenceAtom.new(@tag, @name, @references, &@block)
123
205
  else
124
- @condition = or_pattern(operands)
206
+ raise 'Referencing self AND other patterns in the same atom is not yet supported'
125
207
  end
126
- @condition
208
+ else
209
+ return Core::PropertyAtom.new(@tag, @name, &@block)
127
210
  end
128
-
129
- def not(type, tag=GeneratedTag.new, &block)
130
- pi = pattern_internal(tag, type)
131
- yield pi if block_given?
132
- n = pi.not
133
- if(@condition)
134
- p = [@condition, n]
135
- @condition = and_pattern(p)
136
- else
137
- @condition = n
138
- end
211
+ end
212
+
213
+ private
214
+ def includes_how_many(list1, list2)
215
+ i = 0
216
+ list2.each do |a|
217
+ i += 1 if list1.include?(a)
139
218
  end
219
+ return i
220
+ end
221
+ end
222
+
223
+
140
224
 
141
- private
142
- def pattern_internal(tag, type)
143
- return PatternInternal.new(tag, type)
225
+ private
226
+
227
+ def or_pattern(operands)
228
+ # TODO raise exception if referenceAtoms from the right do not
229
+ # have the values they referenece in the left
230
+ # TODO raise exception if there are repeated tags?
231
+ left = nil
232
+ operands.each do |operand|
233
+ if left.nil?
234
+ left = operand
235
+ else
236
+ right = operand
237
+ left = Core::OrPattern.new(left, right)
144
238
  end
239
+ end
240
+ left
241
+ end
145
242
 
146
- def or_pattern(operands)
147
- # TODO raise exception if referenceAtoms from the right do not
148
- # have the values they referenece in the left
149
- # TODO raise exception if there are repeated tags?
150
- left = nil
151
- operands.each do |operand|
152
- if left.nil?
153
- left = operand
154
- else
155
- right = operand
156
- left = Core::OrPattern.new(left, right)
157
- end
158
- end
159
- left
243
+ def and_pattern(operands)
244
+ # TODO raise exception if referenceAtoms from the right do not
245
+ # have the values they referenece in the left
246
+ # TODO raise exception if there are repeated tags?
247
+ left = nil
248
+ operands.each do |operand|
249
+ if left.nil?
250
+ left = operand
251
+ else
252
+ right = operand
253
+ left = Core::AndPattern.new(left, right)
160
254
  end
255
+ end
256
+ left
257
+ end
161
258
 
162
- def and_pattern(operands)
163
- # TODO raise exception if referenceAtoms from the right do not
164
- # have the values they referenece in the left
165
- # TODO raise exception if there are repeated tags?
166
- left = nil
167
- operands.each do |operand|
168
- if left.nil?
169
- left = operand
170
- else
171
- right = operand
172
- left = Core::AndPattern.new(left, right)
173
- end
174
- end
175
- left
176
- end
259
+ class GeneratedTag
260
+
261
+ # this counter is incremented for each UniqueTag created, and is
262
+ # appended to the end of the unique_seed in order to create a
263
+ # string that is unique for each instance of this class.
264
+ @@tag_counter = 0
265
+
266
+ # every generated tag will be prefixed with this string
267
+ @@unique_seed = 'unique_seed'
268
+
269
+ def initialize()
270
+ @@tag_counter += 1
271
+ @tag = @@unique_seed + @@tag_counter.to_s
177
272
  end
178
273
 
179
- class GeneratedTag
274
+ attr_reader:tag_counter
275
+ attr_reader:unique_seed
276
+ attr_reader:tag
180
277
 
181
- # this counter is incremented for each UniqueTag created, and is
182
- # appended to the end of the unique_seed in order to create a
183
- # string that is unique for each instance of this class.
184
- @@tag_counter = 0
185
-
186
- # every generated tag will be prefixed with this string
187
- @@unique_seed = 'unique_seed'
278
+ def ==(ut)
279
+ return ut && ut.kind_of?(GeneratedTag) && @tag == ut.tag
280
+ end
188
281
 
189
- def initialize()
190
- @@tag_counter += 1
191
- @tag = @@unique_seed + @@tag_counter.to_s
192
- end
193
-
194
- attr_reader:tag_counter
195
- attr_reader:unique_seed
196
- attr_reader:tag
197
-
198
- def ==(ut)
199
- return ut && ut.kind_of?(GeneratedTag) && @tag == ut.tag
200
- end
201
-
202
- def to_s
203
- return @tag.to_s
204
- end
282
+ def to_s
283
+ return @tag.to_s
205
284
  end
285
+ end
@@ -6,24 +6,4 @@ module Ruleby
6
6
  yield e if block_given?
7
7
  return e
8
8
  end
9
- def assert(rete,object,&block)
10
- fact(rete,object,:plus,&block)
11
- end
12
-
13
- def retract(rete,object,&block)
14
- fact(rete,object,:minus,&block)
15
- end
16
-
17
- def modify(rete,object,&block)
18
- retract(rete,object,&block)
19
- assert(rete,object,&block)
20
- end
21
-
22
- private
23
- def fact(rete, object, sign=:plus, &block)
24
- f = Core::Fact.new object, sign
25
- yield f if block_given?
26
- rete.assert_fact f
27
- f
28
- end
29
9
  end