bud 0.9.0 → 0.9.1

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