bud 0.9.0 → 0.9.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.
@@ -13,9 +13,9 @@ module Bud
13
13
  # p.insert(1)
14
14
  # p.insert(nil)
15
15
  class PushElement < BudCollection
16
- attr_accessor :elem_name
17
16
  attr_accessor :rescan, :invalidated
18
- attr_reader :inputs, :found_delta, :refcount, :wired_by, :outputs
17
+ attr_accessor :elem_name
18
+ attr_reader :found_delta, :refcount, :wired_by, :outputs
19
19
 
20
20
  def initialize(name_in, bud_instance, collection_name=nil, given_schema=nil, defer_schema=false, &blk)
21
21
  super(name_in, bud_instance, given_schema, defer_schema)
@@ -33,10 +33,6 @@ module Bud
33
33
  @rescan = true
34
34
  end
35
35
 
36
- def wiring?
37
- @bud_instance.toplevel.done_wiring == false
38
- end
39
-
40
36
  def wirings
41
37
  @wirings ||= @outputs + @pendings + @deletes + @delete_keys
42
38
  end
@@ -81,40 +77,27 @@ module Bud
81
77
  def set_block(&blk)
82
78
  @blk = blk
83
79
  end
84
- def wire_to(element)
85
- unless element.respond_to? :insert
86
- raise Bud::Error, "attempt to wire_to element without insert method"
87
- end
88
- # elem_name = element.respond_to?(:tabname) ? element.tabname : element.elem_name
89
- # puts "wiring #{self.elem_name} to #{elem_name}"
90
- @outputs << element
91
- element.wired_by << self if element.respond_to? :wired_by
92
- end
93
- def wire_to_pending(element)
94
- unless element.respond_to? :pending_merge
95
- raise Bud::Error, "attempt to wire_to_pending element without pending_merge method"
96
- end
97
- # elem_name = element.respond_to?(:tabname) ? element.tabname : element.elem_name
98
- # puts "wiring #{self.elem_name} to #{elem_name}(pending)"
99
- @pendings << element
100
- element.wired_by << self if element.respond_to? :wired_by
101
- end
102
- def wire_to_delete(element)
103
- unless element.respond_to? :pending_delete
104
- raise Bud::Error, "attempt to wire_to_delete element without pending_delete method"
80
+
81
+ def wire_to(element, kind=:output)
82
+ unless @bud_instance.wiring?
83
+ raise Bud::Error, "wire_to called outside wiring phase"
105
84
  end
106
- # elem_name = element.respond_to?(:tabname) ? element.tabname : element.elem_name
107
- # puts "wiring #{self.elem_name} to #{elem_name}(delete)"
108
- @deletes << element
109
- element.wired_by << self if element.respond_to? :wired_by
110
- end
111
- def wire_to_delete_by_key(element)
112
- unless element.respond_to? :pending_delete_keys
113
- raise Bud::Error, "attempt to wire_to_delete_by_key element without pending_delete_keys method"
85
+
86
+ case kind
87
+ when :output
88
+ raise Bud::Error unless element.respond_to? :insert
89
+ @outputs << element
90
+ when :pending
91
+ raise Bud::Error unless element.respond_to? :pending_merge
92
+ @pendings << element
93
+ when :delete
94
+ raise Bud::Error unless element.respond_to? :pending_delete
95
+ @deletes << element
96
+ when :delete_by_key
97
+ raise Bud::Error unless element.respond_to? :pending_delete_keys
98
+ @delete_keys << element
114
99
  end
115
- # elem_name = element.respond_to?(:tabname) ? element.tabname : element.elem_name
116
- # puts "wiring #{self.elem_name} to #{elem_name}(delete)"
117
- @delete_keys << element
100
+
118
101
  element.wired_by << self if element.respond_to? :wired_by
119
102
  end
120
103
 
@@ -144,12 +127,8 @@ module Bud
144
127
  unless item.nil?
145
128
  @outputs.each do |ou|
146
129
  if ou.class <= Bud::PushElement
147
- #the_name = ou.elem_name
148
- # puts "#{self.object_id%10000} (#{elem_name}) -> #{ou.object_id%10000} (#{the_name}): #{item.inspect}"
149
130
  ou.insert(item, self)
150
131
  elsif ou.class <= Bud::BudCollection
151
- # the_name = ou.tabname
152
- # puts "#{self.object_id%10000} (#{elem_name}) -> #{ou.object_id%10000} (#{the_name}): #{item.inspect}"
153
132
  ou.do_insert(item, ou.new_delta)
154
133
  else
155
134
  raise Bud::Error, "expected either a PushElement or a BudCollection"
@@ -210,19 +189,14 @@ module Bud
210
189
  public
211
190
  def stratum_end
212
191
  end
213
- #public
214
- #def set_schema(schema)
215
- # @schema=schema
216
- # setup_accessors
217
- #end
218
-
219
192
 
220
193
  ####
221
194
  # and now, the Bloom-facing methods
195
+ # XXX: "the_name" parameter is unused
222
196
  public
223
- def pro(the_name=@elem_name, the_schema=schema, &blk)
197
+ def pro(the_name=elem_name, the_schema=schema, &blk)
224
198
  toplevel = @bud_instance.toplevel
225
- elem = Bud::PushElement.new('project' + object_id.to_s,
199
+ elem = Bud::PushElement.new("project#{object_id}",
226
200
  toplevel.this_rule_context,
227
201
  @collection_name, the_schema)
228
202
  self.wire_to(elem)
@@ -233,10 +207,11 @@ module Bud
233
207
 
234
208
  alias each pro
235
209
 
210
+ # XXX: "the_name" & "the_schema" parameters are unused
236
211
  public
237
212
  def each_with_index(the_name=elem_name, the_schema=schema, &blk)
238
213
  toplevel = @bud_instance.toplevel
239
- elem = Bud::PushEachWithIndex.new('each_with_index' + object_id.to_s,
214
+ elem = Bud::PushEachWithIndex.new("each_with_index#{object_id}",
240
215
  toplevel.this_rule_context,
241
216
  @collection_name)
242
217
  elem.set_block(&blk)
@@ -273,7 +248,7 @@ module Bud
273
248
  end
274
249
 
275
250
  def merge(source)
276
- if source.class <= PushElement and wiring?
251
+ if source.class <= PushElement and @bud_instance.wiring?
277
252
  source.wire_to(self)
278
253
  else
279
254
  source.each {|i| self << i}
@@ -46,7 +46,7 @@ module Bud
46
46
 
47
47
  class PushArgAgg < PushGroup
48
48
  def initialize(elem_name, bud_instance, collection_name, keys_in, aggpairs_in, schema_in, &blk)
49
- raise "Multiple aggpairs #{aggpairs_in.map{|a| a.class.name}} in ArgAgg; only one allowed" if aggpairs_in.length > 1
49
+ raise Bud::Error, "multiple aggpairs #{aggpairs_in.map{|a| a.class.name}} in ArgAgg; only one allowed" if aggpairs_in.length > 1
50
50
  super(elem_name, bud_instance, collection_name, keys_in, aggpairs_in, schema_in, &blk)
51
51
  @agg = @aggpairs[0][0]
52
52
  @aggcol = @aggpairs[0][1]
@@ -16,7 +16,6 @@ module Bud
16
16
  @selfjoins = []
17
17
  @input_bufs = [[],[]]
18
18
  @missing_keys = Set.new
19
- the_join = nil
20
19
 
21
20
  # if any elements on rellist are PushSHJoins, suck up their contents
22
21
  @all_rels_below = []
@@ -24,7 +23,6 @@ module Bud
24
23
  if r.class <= PushSHJoin
25
24
  @all_rels_below += r.all_rels_below
26
25
  preds += r.origpreds
27
- the_join = r
28
26
  else
29
27
  @all_rels_below << r
30
28
  end
@@ -85,8 +83,7 @@ module Bud
85
83
  # extract predicates on rellist[1] and recurse to left side with remainder
86
84
  protected
87
85
  def setup_preds(preds) # :nodoc: all
88
- # print "setting up preds for #{@relnames.inspect}(#{self.object_id}): "
89
- # print "setting up preds for #{@relnames.inspect}(#{self.object_id}): "
86
+ # print "setting up preds for #{@relnames.inspect}(#{self.object_id}): "
90
87
  allpreds = disambiguate_preds(preds)
91
88
  allpreds = canonicalize_localpreds(@rels, allpreds)
92
89
  # check for refs to collections that aren't being joined, Issue 191
@@ -135,7 +132,7 @@ module Bud
135
132
  else
136
133
  @keys = []
137
134
  end
138
- # puts "@keys = #{@keys.inspect}"
135
+ # puts "@keys = #{@keys.inspect}"
139
136
  end
140
137
 
141
138
  public
@@ -301,7 +298,7 @@ module Bud
301
298
  offsets.each do |offset|
302
299
  buf = @input_bufs[offset]
303
300
  buf << item
304
- if (buf.length >= ELEMENT_BUFSIZE)
301
+ if buf.length >= ELEMENT_BUFSIZE
305
302
  flush_buf(buf, offset)
306
303
  end
307
304
  end
@@ -309,7 +306,7 @@ module Bud
309
306
 
310
307
  protected
311
308
  def insert_item(item, offset)
312
- if (@keys.nil? or @keys.empty?)
309
+ if @keys.nil? or @keys.empty?
313
310
  the_key = nil
314
311
  else
315
312
  # assumes left-deep trees
@@ -337,7 +334,6 @@ module Bud
337
334
 
338
335
  public
339
336
  def add_rescan_invalidate(rescan, invalidate)
340
-
341
337
  if non_temporal_predecessors.any? {|e| rescan.member? e}
342
338
  rescan << self
343
339
  invalidate << self
@@ -450,7 +446,6 @@ module Bud
450
446
  self.extend(Bud::PushSHOuterJoin)
451
447
  end
452
448
 
453
-
454
449
  public
455
450
  def rights(*preds, &blk)
456
451
  @cols = blk.nil? ? @bud_instance.tables[@rels[1].tabname].cols : nil
@@ -535,7 +530,7 @@ module Bud
535
530
 
536
531
  private
537
532
  def insert_item(item, offset)
538
- if (@keys.nil? or @keys.empty?)
533
+ if @keys.nil? or @keys.empty?
539
534
  the_key = nil
540
535
  else
541
536
  if all_rels_below.length > 2 and offset == 1
@@ -571,7 +566,7 @@ module Bud
571
566
  if @missing_keys
572
567
  @missing_keys.each do |key|
573
568
  @hash_tables[0][key].each do |t|
574
- push_out([t, []])
569
+ push_out([t, @rels[1].null_tuple])
575
570
  end
576
571
  end
577
572
  end
@@ -580,7 +575,7 @@ module Bud
580
575
 
581
576
  class PushNotIn < PushSHJoin
582
577
  def initialize(rellist, bud_instance, preds=nil, &blk) # :nodoc: all
583
- if (preds.nil? or preds.empty?)
578
+ if preds.nil? or preds.empty?
584
579
  preds = positionwise_preds(bud_instance, rellist)
585
580
  end
586
581
  super(rellist, bud_instance, preds)
data/lib/bud/rewrite.rb CHANGED
@@ -1,21 +1,20 @@
1
1
  require 'rubygems'
2
2
  require 'ruby2ruby'
3
+ require 'set'
3
4
 
4
5
  class RuleRewriter < Ruby2Ruby # :nodoc: all
5
6
  attr_accessor :rule_indx, :rules, :depends
6
7
 
8
+ OP_LIST = Set.new([:<<, :<, :<=])
9
+ TEMP_OP_LIST = Set.new([:-@, :~, :+@])
10
+ MONOTONE_WHITELIST = Set.new([:==, :+, :<=, :-, :<, :>, :*, :~,
11
+ :pairs, :matches, :combos, :flatten,
12
+ :lefts, :rights, :map, :flat_map, :pro,
13
+ :cols, :key_cols, :val_cols, :payloads, :lambda,
14
+ :tabname, :ip_port, :port, :ip, :int_ip_port])
15
+
7
16
  def initialize(seed, bud_instance)
8
17
  @bud_instance = bud_instance
9
- @ops = {:<< => 1, :< => 1, :<= => 1}
10
- @monotonic_whitelist = {
11
- :== => 1, :+ => 1, :<= => 1, :- => 1, :< => 1, :> => 1,
12
- :* => 1, :pairs => 1, :matches => 1, :combos => 1, :flatten => 1,
13
- :lefts => 1, :rights => 1, :map => 1, :flat_map => 1, :pro => 1,
14
- :cols => 1, :key_cols => 1, :val_cols => 1, :payloads => 1, :~ => 1,
15
- :lambda => 1, :tabname => 1,
16
- :ip_port => 1, :port => 1, :ip => 1
17
- }
18
- @temp_ops = {:-@ => 1, :~ => 1, :+@ => 1}
19
18
  @tables = {}
20
19
  @nm = false
21
20
  @rule_indx = seed
@@ -29,7 +28,7 @@ class RuleRewriter < Ruby2Ruby # :nodoc: all
29
28
  $not_id = [:not_coll_id]
30
29
  def resolve(obj, prefix, name)
31
30
  qn = prefix ? prefix + "." + name.to_s : name.to_s
32
- return [:collection, qn, obj.tables[name]] if obj.tables.has_key? name
31
+ return [:collection, qn, obj.tables[name]] if obj.tables.has_key? name
33
32
 
34
33
  # does name refer to an import name?
35
34
  iobj = obj.import_instance name
@@ -56,60 +55,72 @@ class RuleRewriter < Ruby2Ruby # :nodoc: all
56
55
 
57
56
  def process_call(exp)
58
57
  recv, op, args = exp
59
- if @ops[op] and @context[1] == :block and @context.length == 4
58
+ if OP_LIST.include?(op) and @context[1] == :block and @context.length == 4
60
59
  # NB: context.length is 4 when see a method call at the top-level of a
61
60
  # :defn block -- this is where we expect Bloom statements to appear
62
61
  do_rule(exp)
63
62
  else
64
- ty = :not_coll_id
65
- ty, qn, obj = exp_id_type(recv, op, args) # qn = qualified name, obj is the corresponding object
63
+ ty, qn, _ = exp_id_type(recv, op, args) # qn = qualified name
66
64
  if ty == :collection
67
65
  @tables[qn] = @nm if @collect
68
66
  #elsif ty == :import .. do nothing
69
67
  elsif ty == :not_coll_id
70
- # check if receiver is a collection, and further if the current exp represents a field lookup
68
+ # check if receiver is a collection, and further if the current exp
69
+ # represents a field lookup
71
70
  op_is_field_name = false
72
71
  if recv and recv.first == :call
73
72
  rty, _, robj = exp_id_type(recv[1], recv[2], recv[3])
74
73
  if rty == :collection
75
74
  cols = robj.cols
76
- op_is_field_name = true if cols and cols.include?(op)
75
+ op_is_field_name = true if cols and cols.include?(op)
77
76
  end
78
77
  end
79
78
  # for CALM analysis, mark deletion rules as non-monotonic
80
79
  @nm = true if op == :-@
81
80
  if recv
82
- # don't worry about monotone ops, table names, table.attr calls, or accessors of iterator variables
83
- unless @monotonic_whitelist[op] or op_is_field_name or recv.first == :lvar or op.to_s.start_with?("__")
84
- @nm = true if recv
81
+ # don't worry about monotone ops, table names, table.attr calls, or
82
+ # accessors of iterator variables
83
+ unless RuleRewriter.is_monotone(op) or op_is_field_name or
84
+ recv.first == :lvar or op.to_s.start_with?("__")
85
+ @nm = true
85
86
  end
86
87
  else
87
- # function called (implicit receiver = Bud instance) in a user-defined code block. Check if it is
88
- # non-monotonic (like budtime, that produces a new answer every time it is called)
89
- @nm_funcs_called = true unless @monotonic_whitelist[op]
88
+ # function called (implicit receiver = Bud instance) in a user-defined
89
+ # code block. Check if it is non-monotonic (like budtime, that
90
+ # produces a new value every time it is called)
91
+ @nm_funcs_called = true unless RuleRewriter.is_monotone(op)
90
92
  end
91
93
  end
92
- if @temp_ops[op]
94
+ if TEMP_OP_LIST.include? op
93
95
  @temp_op = op.to_s.gsub("@", "")
94
96
  end
95
97
  super
96
98
  end
97
99
  end
98
100
 
101
+ def self.is_monotone(op)
102
+ MONOTONE_WHITELIST.include?(op)
103
+ end
104
+
105
+ # rewrite constant array expressions to lambdas
106
+ def lambda_rewrite(rhs)
107
+ # the <= case
108
+ if rhs[0] == :array
109
+ return s(:iter, s(:call, nil, :lambda, s(:arglist)), nil, rhs)
110
+ # the superator case
111
+ elsif rhs[0] == :call \
112
+ and rhs[1] and rhs[1][0] and rhs[1][0] == :array \
113
+ and rhs[2] and (rhs[2] == :+@ or rhs[2] == :-@ or rhs[2] == :~@)
114
+ return s(rhs[0], s(:iter, s(:call, nil, :lambda, s(:arglist)), nil, rhs[1]), rhs[2], rhs[3])
115
+ else
116
+ return rhs
117
+ end
118
+ end
119
+
99
120
  def collect_rhs(exp)
121
+ exp = lambda_rewrite(exp)
122
+
100
123
  @collect = true
101
- # rewrite constant array expressions to lambdas
102
- if exp[0] and exp[0] == :arglist
103
- # the <= case
104
- if exp[1] and exp[1][0] == :array
105
- exp = s(exp[0], s(:iter, s(:call, nil, :lambda, s(:arglist)), nil, exp[1]))
106
- # the superator case
107
- elsif exp[1] and exp[1][0] == :call \
108
- and exp[1][1] and exp[1][1][0] and exp[1][1][0] == :array \
109
- and exp[1][2] and (exp[1][2] == :+@ or exp[1][2] == :-@ or exp[1][2] == :~@)
110
- exp = s(exp[0], s(exp[1][0], s(:iter, s(:call, nil, :lambda, s(:arglist)), nil, exp[1][1]), exp[1][2], exp[1][3]))
111
- end
112
- end
113
124
  rhs = process exp
114
125
  @collect = false
115
126
  return rhs
@@ -132,8 +143,8 @@ class RuleRewriter < Ruby2Ruby # :nodoc: all
132
143
  end
133
144
 
134
145
  @rules << [@bud_instance, @rule_indx, lhs, op, rule_txt, rule_txt_orig, @nm_funcs_called]
135
- @tables.each_pair do |t, non_monotonic|
136
- @depends << [@bud_instance, @rule_indx, lhs, op, t, non_monotonic]
146
+ @tables.each_pair do |t, nm|
147
+ @depends << [@bud_instance, @rule_indx, lhs, op, t, nm]
137
148
  end
138
149
 
139
150
  reset_instance_vars
@@ -149,7 +160,9 @@ class RuleRewriter < Ruby2Ruby # :nodoc: all
149
160
  # s(:arglist) is not really sensible and it causes Ruby2Ruby < 1.3.1 to
150
161
  # misbehave (for example, s(:arglist, s(:hash, ...)) is misparsed.
151
162
  raise Bud::CompileError unless rhs_ast.sexp_type == :arglist
152
- #rhs_ast = rhs_ast[1]
163
+ rhs_ast = rhs_ast[1]
164
+
165
+ rhs_ast = RenameRewriter.new(@bud_instance).process(rhs_ast)
153
166
 
154
167
  if @bud_instance.options[:no_attr_rewrite]
155
168
  rhs = collect_rhs(rhs_ast)
@@ -191,6 +204,37 @@ class RuleRewriter < Ruby2Ruby # :nodoc: all
191
204
  end
192
205
  end
193
206
 
207
+ # Look for rename statements and define the necessary scratch collections
208
+ class RenameRewriter < SexpProcessor
209
+ def initialize(bud_instance)
210
+ super()
211
+ self.require_empty = false
212
+ self.expected = Sexp
213
+ @bud_instance = bud_instance
214
+ end
215
+
216
+ def register_scratch(name, schemahash)
217
+ # define a scratch with the name and schema in this rename block
218
+ hash, key_array, val_array = schemahash
219
+ key_array ||= []
220
+ val_array ||= []
221
+ key_cols = key_array.map{|i| i[1] if i.class <= Sexp}.compact
222
+ val_cols = val_array.map{|i| i[1] if i.class <= Sexp}.compact
223
+ @bud_instance.scratch(name, key_cols=>val_cols)
224
+ end
225
+
226
+ def process_call(exp)
227
+ call, recv, op, args = exp
228
+
229
+ if op == :rename
230
+ arglist, namelit, schemahash = args
231
+ register_scratch(namelit[1], schemahash)
232
+ end
233
+
234
+ return s(call, process(recv), op, process(args))
235
+ end
236
+ end
237
+
194
238
  # Rewrite named-column refs to positional refs
195
239
  class AttrNameRewriter < SexpProcessor # :nodoc: all
196
240
  def initialize(bud_instance)
@@ -225,7 +269,7 @@ class AttrNameRewriter < SexpProcessor # :nodoc: all
225
269
  return unless exp[2][1] and exp[2][1][0] == :array
226
270
  if exp[1][2] == :reduce
227
271
  unless @collnames.length == 1
228
- raise Bud::Error, "reduce should only one associated collection, but has #{@collnames.inspect}"
272
+ raise Bud::Error, "reduce should only have one associated collection, but has #{@collnames.inspect}"
229
273
  end
230
274
  @iterhash[exp[2][1][2][1]] = @collnames.first
231
275
  else #join
@@ -240,16 +284,6 @@ class AttrNameRewriter < SexpProcessor # :nodoc: all
240
284
  exp
241
285
  end
242
286
 
243
- def register_scratch(name, schemahash)
244
- # define a scratch with the name and schema in this rename block
245
- hash, key_array, val_array = schemahash
246
- key_array ||= []
247
- val_array ||= []
248
- key_cols = key_array.map{|i| i[1] if i.class <= Sexp}.compact
249
- val_cols = val_array.map{|i| i[1] if i.class <= Sexp}.compact
250
- @bud_instance.scratch(name, key_cols=>val_cols)
251
- end
252
-
253
287
  def gather_collection_names(exp)
254
288
  if exp[0] == :call and exp[1].nil?
255
289
  @collnames << exp[2]
@@ -264,10 +298,6 @@ class AttrNameRewriter < SexpProcessor # :nodoc: all
264
298
  def process_call(exp)
265
299
  call, recv, op, args = exp
266
300
 
267
- if op == :rename
268
- arglist, namelit, schemahash = args
269
- register_scratch(namelit[1], schemahash)
270
- end
271
301
  if recv and recv.class == Sexp and recv.first == :lvar and recv[1] and @iterhash[recv[1]]
272
302
  if @bud_instance.respond_to?(@iterhash[recv[1]])
273
303
  if @bud_instance.send(@iterhash[recv[1]]).class <= Bud::BudCollection
@@ -166,9 +166,9 @@ module Bud
166
166
  public
167
167
  def pending_delete(o)
168
168
  if o.class <= Bud::PushElement
169
- o.wire_to_delete self
169
+ o.wire_to(self, :delete)
170
170
  elsif o.class <= Bud::BudCollection
171
- o.pro.wire_to_delete self
171
+ o.pro.wire_to(self, :delete)
172
172
  else
173
173
  @to_delete = @to_delete + o.map{|t| prep_tuple(t) unless t.nil?}
174
174
  end
data/lib/bud/viz_util.rb CHANGED
@@ -146,9 +146,11 @@ module VizUtil #:nodoc: all
146
146
  end
147
147
 
148
148
  def write_graphs(tabinf, builtin_tables, cycle, depends, rules, viz_name,
149
- output_base, fmt, collapse, depanalysis=nil, budtime=-1, card_info=nil, pathsto={}, begins = {})
149
+ output_base, fmt, collapse, depanalysis=nil, budtime=-1,
150
+ card_info=nil, pathsto={}, begins={})
150
151
  staging = "#{viz_name}.staging"
151
- gv = GraphGen.new(tabinf, builtin_tables, cycle, staging, budtime, collapse, card_info, pathsto, begins)
152
+ gv = GraphGen.new(tabinf, builtin_tables, cycle, staging, budtime,
153
+ collapse, card_info, pathsto, begins)
152
154
  gv.process(depends)
153
155
  dump(rules, output_base, gv)
154
156
  gv.finish(depanalysis, fmt)
data/lib/bud.rb CHANGED
@@ -62,7 +62,7 @@ $bud_instances = {} # Map from instance id => Bud instance
62
62
  module Bud
63
63
  attr_reader :budtime, :inbound, :options, :meta_parser, :viz, :rtracer, :dsock
64
64
  attr_reader :tables, :builtin_tables, :channels, :zk_tables, :dbm_tables, :app_tables
65
- attr_reader :push_sources, :push_elems, :push_joins, :scanners, :merge_targets, :done_wiring
65
+ attr_reader :push_sources, :push_elems, :push_joins, :scanners, :merge_targets
66
66
  attr_reader :this_stratum, :this_rule, :rule_orig_src, :done_bootstrap
67
67
  attr_accessor :stratified_rules
68
68
  attr_accessor :metrics, :periodics
@@ -75,7 +75,6 @@ module Bud
75
75
  # * <tt>:port</tt> port number for this instance
76
76
  # * <tt>:ext_ip</tt> IP address at which external nodes can contact this instance
77
77
  # * <tt>:ext_port</tt> port number to go with <tt>:ext_ip</tt>
78
- # * <tt>:bust_port</tt> port number for the restful HTTP messages
79
78
  # * operating system interaction
80
79
  # * <tt>:stdin</tt> if non-nil, reading from the +stdio+ collection results in reading from this +IO+ handle
81
80
  # * <tt>:stdout</tt> writing to the +stdio+ collection results in writing to this +IO+ handle; defaults to <tt>$stdout</tt>
@@ -199,6 +198,11 @@ module Bud
199
198
  toplevel? ? @budtime : toplevel.budtime
200
199
  end
201
200
 
201
+ # Are we currently in the process of wiring together the dataflow?
202
+ def wiring?
203
+ toplevel? ? (@done_bootstrap && !@done_wiring) : toplevel.wiring?
204
+ end
205
+
202
206
  private
203
207
  def import_defs
204
208
  @imported_defs = {} # mod name -> Module map
@@ -999,20 +1003,20 @@ module Bud
999
1003
  fixpoint = false
1000
1004
  first_iter = true
1001
1005
  until fixpoint
1002
- fixpoint = true
1003
1006
  @scanners[stratum].each_value {|s| s.scan(first_iter)}
1007
+ fixpoint = true
1004
1008
  first_iter = false
1005
1009
  # flush any tuples in the pipes
1006
1010
  @push_sorted_elems[stratum].each {|p| p.flush}
1007
1011
  # tick deltas on any merge targets and look for more deltas
1008
1012
  # check to see if any joins saw a delta
1009
- push_joins[stratum].each do |p|
1013
+ @push_joins[stratum].each do |p|
1010
1014
  if p.found_delta
1011
1015
  fixpoint = false
1012
1016
  p.tick_deltas
1013
1017
  end
1014
1018
  end
1015
- merge_targets[stratum].each do |t|
1019
+ @merge_targets[stratum].each do |t|
1016
1020
  fixpoint = false if t.tick_deltas
1017
1021
  end
1018
1022
  end
@@ -1020,7 +1024,7 @@ module Bud
1020
1024
  @push_sorted_elems[stratum].each do |p|
1021
1025
  p.stratum_end
1022
1026
  end
1023
- merge_targets[stratum].each do |t|
1027
+ @merge_targets[stratum].each do |t|
1024
1028
  t.flush_deltas
1025
1029
  end
1026
1030
  end
@@ -1054,6 +1058,17 @@ module Bud
1054
1058
  @tick_clock_time
1055
1059
  end
1056
1060
 
1061
+ # Return the stratum number of the given collection.
1062
+ # NB: if a collection is not referenced by any rules, it is not currently
1063
+ # assigned to a strata.
1064
+ def collection_stratum(collection)
1065
+ t_stratum.each do |t|
1066
+ return t.stratum if t.predicate == collection
1067
+ end
1068
+
1069
+ raise Bud::Error, "no such collection: #{collection}"
1070
+ end
1071
+
1057
1072
  private
1058
1073
 
1059
1074
  # Builtin Bud state (predefined collections). We could define this using the
@@ -1125,7 +1140,6 @@ module Bud
1125
1140
  end
1126
1141
  end
1127
1142
 
1128
- private
1129
1143
  ######## ids and timers
1130
1144
  def gen_id
1131
1145
  Time.new.to_i.to_s << rand.to_s