bud 0.1.0.pre1 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -14,7 +14,7 @@ module Bud
14
14
  @origpreds = preds
15
15
  @localpreds = nil
16
16
  @selfjoins = []
17
- @input_bufs=[[],[]]
17
+ @input_bufs = [[],[]]
18
18
  @missing_keys = Set.new
19
19
  the_join = nil
20
20
 
@@ -30,7 +30,8 @@ module Bud
30
30
  end
31
31
  end
32
32
 
33
- # check for self-joins: we currently only handle 2 instances of the same table per rule
33
+ # check for self-joins: we currently only handle 2 instances of the same
34
+ # table per rule
34
35
  counts = @all_rels_below.reduce({}) do |memo, r|
35
36
  memo[r.elem_name] ||= 0
36
37
  memo[r.elem_name] += 1
@@ -57,7 +58,7 @@ module Bud
57
58
  setup_preds(preds) unless preds.empty?
58
59
  setup_state
59
60
 
60
- super(@tabname,@bud_instance,nil,@cols)
61
+ super(@tabname, @bud_instance, nil, @cols)
61
62
  end
62
63
 
63
64
  public
@@ -249,7 +250,7 @@ module Bud
249
250
  next if (skips.include? pred)
250
251
  # assumption of left-deep joins here
251
252
  if pred[1][0] != @rels[1].tabname
252
- raise "Expected rhs table to be #{@rels[1].tabname}, not #{pred[1][0]}"
253
+ raise Bud::Error, "expected rhs table to be #{@rels[1].tabname}, not #{pred[1][0]}"
253
254
  end
254
255
  rfield = right[pred[1][1]]
255
256
  if left_is_array
@@ -296,7 +297,7 @@ module Bud
296
297
  else
297
298
  offsets = [@relnames.index(source.elem_name)]
298
299
  end
299
- raise "item #{item.inspect} inserted into join from unknown source #{source.elem_name}" if offsets == $EMPTY
300
+ raise Bud::Error, "item #{item.inspect} inserted into join from unknown source #{source.elem_name}" if offsets == $EMPTY
300
301
  offsets.each do |offset|
301
302
  buf = @input_bufs[offset]
302
303
  buf << item
@@ -342,12 +343,14 @@ module Bud
342
343
  invalidate << self
343
344
  end
344
345
 
345
- # The distinction between a join node and other stateful elements is that when a join node needs a rescan
346
- # it doesn't tell all its sources to rescan. In fact, it doesn't have to pass a rescan request up to a source,
347
- # because if a target needs a rescan, the join node has all the state necessary to feed the downstream node. And
348
- # if a source node is in rescan, then at run-time only the state associated with that particular source node
349
- # @hash_tables[offset] will be cleared, and will get filled up again because that source will rescan anyway.
350
-
346
+ # The distinction between a join node and other stateful elements is that
347
+ # when a join node needs a rescan it doesn't tell all its sources to
348
+ # rescan. In fact, it doesn't have to pass a rescan request up to a
349
+ # source, because if a target needs a rescan, the join node has all the
350
+ # state necessary to feed the downstream node. And if a source node is in
351
+ # rescan, then at run-time only the state associated with that particular
352
+ # source node @hash_tables[offset] will be cleared, and will get filled up
353
+ # again because that source will rescan anyway.
351
354
  invalidate_tables(rescan, invalidate)
352
355
  end
353
356
 
@@ -406,7 +409,7 @@ module Bud
406
409
  public
407
410
  def flush
408
411
  @input_bufs.each_with_index do |buf, offset|
409
- flush_buf(buf,offset) if buf.length > 0
412
+ flush_buf(buf, offset) if buf.length > 0
410
413
  end
411
414
  end
412
415
 
@@ -582,11 +585,10 @@ module Bud
582
585
  end
583
586
  super(rellist, bud_instance, preds)
584
587
  set_block(&blk)
585
- @cols = rellist[0].cols
588
+ @cols = rellist[0].cols
586
589
  @exclude = Set.new
587
590
  end
588
591
 
589
-
590
592
  def positionwise_preds(bud_instance, rels)
591
593
  # pairwise colnames, for the minimum number of columns from either
592
594
  return [] if rels[0].cols.length != rels[1].cols.length
@@ -614,23 +616,24 @@ module Bud
614
616
 
615
617
  def stratum_end
616
618
  flush
617
- # Scan through all the cached left rel values, and push out those that are not in exclude
618
- @hash_tables[0].each_value do|s| #
619
+ # Scan through all the cached left rel values, and push out those that are
620
+ # not in exclude
621
+ @hash_tables[0].each_value do |s|
619
622
  s.each do |item|
620
623
  next if @exclude.member? item
621
624
  @outputs.each do |ou|
622
625
  if ou.class <= Bud::PushElement
623
- ou.insert(item,self)
626
+ ou.insert(item, self)
624
627
  elsif ou.class <= Bud::BudCollection
625
- ou.do_insert(item,ou.new_delta)
628
+ ou.do_insert(item, ou.new_delta)
626
629
  else
627
- raise "Expected either a PushElement or a BudCollection"
630
+ raise Bud::Error, "expected either a PushElement or a BudCollection"
628
631
  end
629
632
  end
630
633
  # for all the following, o is a BudCollection
631
- @deletes.each{|o| o.pending_delete([item])} unless item.nil?
632
- @delete_keys.each{|o| o.pending_delete_keys([item])} unless item.nil?
633
- @pendings.each{|o| o.pending_merge([item])} unless item.nil?
634
+ @deletes.each{|o| o.pending_delete([item])}
635
+ @delete_keys.each{|o| o.pending_delete_keys([item])}
636
+ @pendings.each{|o| o.pending_merge([item])}
634
637
  end
635
638
  end
636
639
  end
data/lib/bud/metrics.rb CHANGED
@@ -7,7 +7,7 @@ else
7
7
  $mod = CSV
8
8
  end
9
9
 
10
- # metrics are reported in a nested hash representing a collection of relational tables.
10
+ # Metrics are reported in a nested hash representing a collection of relational tables.
11
11
  # The metrics hash has the following form:
12
12
  # - key of the metrics hash is the name of the metric (table), e.g. "tickstats", "collections", "rules", etc.
13
13
  # - value of the metrics is itself a hash holding the rows of the table, keyed by key columns.
@@ -80,6 +80,9 @@ class Module
80
80
  raise Bud::CompileError unless (spec.class <= Hash and spec.length == 1)
81
81
  mod, local_name = spec.first
82
82
  raise Bud::CompileError unless (mod.class <= Module and local_name.class <= Symbol)
83
+ if mod.class <= Class
84
+ raise Bud::CompileError, "import must be used with a Module, not a Class"
85
+ end
83
86
 
84
87
  # A statement like this:
85
88
  # import MyModule => :m
@@ -98,19 +101,18 @@ class Module
98
101
 
99
102
  mod, local_name = spec.first
100
103
 
101
- if self.instance_methods.include? local_name.to_s or self.instance_methods.include? local_name
102
- raise Bud::CompileError, "#{local_name} is already taken."
104
+ if self.method_defined? local_name
105
+ raise Bud::CompileError, "#{local_name} is already taken"
103
106
  else
104
107
  src = %Q{
105
108
  def #{local_name}
106
109
  @#{local_name}
107
110
  end
108
111
  def #{local_name}=(val)
109
- raise "Type Error: expecting an instance of #{mod}" unless val.kind_of? #{mod}
112
+ raise Bud::Error, "type error: expecting an instance of #{mod}" unless val.kind_of? #{mod}
110
113
  @#{local_name} = val
111
114
  end
112
115
  }
113
- #puts src
114
116
  self.class_eval src
115
117
  end
116
118
 
@@ -142,7 +144,7 @@ class Module
142
144
  # If no block name was specified, generate a unique name
143
145
  if block_name.nil?
144
146
  @block_id ||= 0
145
- block_name = "#{Module.get_class_name(self)}__#{@block_id.to_s}"
147
+ block_name = "#{Module.get_class_name(self)}__#{@block_id}".to_sym
146
148
  @block_id += 1
147
149
  else
148
150
  unless block_name.class <= Symbol
@@ -157,25 +159,25 @@ class Module
157
159
 
158
160
  # Don't allow duplicate named bloom blocks to be defined within a single
159
161
  # module; this indicates a likely programmer error.
160
- if instance_methods(false).include? meth_name
162
+ if instance_methods(false).include?(meth_name) ||
163
+ instance_methods(false).include?(meth_name.to_sym)
161
164
  raise Bud::CompileError, "duplicate named bloom block: '#{block_name}' in #{self}"
162
165
  end
163
166
  ast = Source.read_block(caller[0]) # pass in caller's location via backtrace
164
167
  # ast corresponds only to the statements of the block. Wrap it in a method
165
168
  # definition for backward compatibility for now.
166
169
  # First wrap ast in a block if it is only a single statement
167
- unless ast.nil?
168
- ast = s(:block, ast) unless ast.sexp_type == :block
169
- ast = s(:defn, meth_name.to_sym, s(:args), s(:scope, ast))
170
- unless self.respond_to? :__bloom_asts__
171
- def self.__bloom_asts__;
172
- @__bloom_asts__ ||= {}
173
- @__bloom_asts__
174
- end
170
+ ast = s(:block) if ast.nil?
171
+ ast = s(:block, ast) unless ast.sexp_type == :block
172
+ ast = s(:defn, meth_name.to_sym, s(:args), s(:scope, ast))
173
+ unless self.respond_to? :__bloom_asts__
174
+ def self.__bloom_asts__
175
+ @__bloom_asts__ ||= {}
176
+ @__bloom_asts__
175
177
  end
176
- __bloom_asts__[meth_name] = ast
177
- define_method(meth_name.to_sym, &block)
178
178
  end
179
+ __bloom_asts__[meth_name] = ast
180
+ define_method(meth_name.to_sym, &block)
179
181
  end
180
182
 
181
183
  private
@@ -202,19 +204,6 @@ end
202
204
 
203
205
 
204
206
  module Enumerable
205
- public
206
- # Support for renaming collections and their schemas
207
- def rename(new_tabname, new_schema=nil)
208
- budi = (respond_to?(:bud_instance)) ? bud_instance : nil
209
- if new_schema.nil? and respond_to?(:schema)
210
- new_schema = schema
211
- end
212
- scr = Bud::BudScratch.new(new_tabname.to_s, budi, new_schema)
213
- scr.uniquify_tabname
214
- scr.merge(self, scr.storage)
215
- scr
216
- end
217
-
218
207
  public
219
208
  # We rewrite "map" calls in Bloom blocks to invoke the "pro" method
220
209
  # instead. This is fine when applied to a BudCollection; when applied to a
data/lib/bud/rebl.rb CHANGED
@@ -9,7 +9,7 @@ TABLE_TYPES = ["table", "scratch", "channel", "loopback", "periodic", "sync", "s
9
9
  # The class to which rebl adds user-specified rules and declarations.
10
10
  class ReblBase
11
11
  include Bud
12
- attr_accessor:port, :ip
12
+ attr_accessor :port, :ip
13
13
 
14
14
  # Support for breakpoints
15
15
  state { scratch :rebl_breakpoint }
@@ -195,9 +195,10 @@ end
195
195
  class LibRebl
196
196
  attr_accessor :rules, :state
197
197
  attr_reader :ip, :port, :rebl_class_inst
198
- @@builtin_tables = [:stdio, :t_depends, :periodics_tbl, :t_cycle, :localtick,
199
- :t_provides, :t_rules, :t_depends_tc, :t_stratum,
200
- :rebl_breakpoint]
198
+ @@builtin_tables = [:stdio, :periodics_tbl, :halt, :localtick,
199
+ :t_depends, :t_cycle, :t_provides, :t_rules,
200
+ :t_depends_tc, :t_stratum, :t_underspecified,
201
+ :t_table_info, :t_table_schema, :rebl_breakpoint]
201
202
  @@classid = 0
202
203
 
203
204
  def initialize(ip, port)
data/lib/bud/rewrite.rb CHANGED
@@ -12,8 +12,9 @@ class RuleRewriter < Ruby2Ruby # :nodoc: all
12
12
  :* => 1, :pairs => 1, :matches => 1, :combos => 1, :flatten => 1,
13
13
  :lefts => 1, :rights => 1, :map => 1, :flat_map => 1, :pro => 1,
14
14
  :cols => 1, :key_cols => 1, :val_cols => 1, :payloads => 1, :~ => 1,
15
- :lambda => 1, :tabname => 1
16
- }
15
+ :lambda => 1, :tabname => 1,
16
+ :ip_port => 1, :port => 1, :ip => 1
17
+ }
17
18
  @temp_ops = {:-@ => 1, :~ => 1, :+@ => 1}
18
19
  @tables = {}
19
20
  @nm = false
@@ -21,6 +22,7 @@ class RuleRewriter < Ruby2Ruby # :nodoc: all
21
22
  @collect = false
22
23
  @rules = []
23
24
  @depends = []
25
+ @nm_funcs_called = false
24
26
  super()
25
27
  end
26
28
 
@@ -76,9 +78,15 @@ class RuleRewriter < Ruby2Ruby # :nodoc: all
76
78
  end
77
79
  # for CALM analysis, mark deletion rules as non-monotonic
78
80
  @nm = true if op == :-@
79
- # don't worry about monotone ops, table names, table.attr calls, or accessors of iterator variables
80
- unless @monotonic_whitelist[op] or op_is_field_name or (recv and recv.first == :lvar) or op.to_s.start_with?("__")
81
- @nm = true
81
+ 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
85
+ end
86
+ 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]
82
90
  end
83
91
  end
84
92
  if @temp_ops[op]
@@ -110,6 +118,7 @@ class RuleRewriter < Ruby2Ruby # :nodoc: all
110
118
  def reset_instance_vars
111
119
  @tables = {}
112
120
  @nm = false
121
+ @nm_funcs_called = false
113
122
  @temp_op = nil
114
123
  end
115
124
 
@@ -122,7 +131,7 @@ class RuleRewriter < Ruby2Ruby # :nodoc: all
122
131
  op = op.to_s
123
132
  end
124
133
 
125
- @rules << [@bud_instance, @rule_indx, lhs, op, rule_txt, rule_txt_orig]
134
+ @rules << [@bud_instance, @rule_indx, lhs, op, rule_txt, rule_txt_orig, @nm_funcs_called]
126
135
  @tables.each_pair do |t, non_monotonic|
127
136
  @depends << [@bud_instance, @rule_indx, lhs, op, t, non_monotonic]
128
137
  end
@@ -172,7 +181,7 @@ class RuleRewriter < Ruby2Ruby # :nodoc: all
172
181
  if exp[1][1] and exp[1][1][1] and exp[1][1][1][2] == :map
173
182
  exp[1][1][1][2] = :pro
174
183
  end
175
- end
184
+ end
176
185
  exp
177
186
  end
178
187
 
@@ -246,7 +255,6 @@ class AttrNameRewriter < SexpProcessor # :nodoc: all
246
255
  @collnames << exp[2]
247
256
  elsif exp[2] and exp[2] == :rename
248
257
  arglist, namelit, schemahash = exp[3]
249
- # and add name to @collnames
250
258
  @collnames << namelit[1]
251
259
  else
252
260
  exp.each { |e| gather_collection_names(e) if e and e.class <= Sexp }
@@ -288,12 +296,12 @@ class TempExpander < SexpProcessor # :nodoc: all
288
296
  attr_reader :tmp_tables
289
297
  attr_accessor :did_work
290
298
 
299
+ KEYWORD = :temp
300
+
291
301
  def initialize
292
302
  super()
293
303
  self.require_empty = false
294
304
  self.expected = Sexp
295
- @keyword = :temp
296
-
297
305
  @tmp_tables = []
298
306
  @did_work = false
299
307
  end
@@ -322,7 +330,7 @@ class TempExpander < SexpProcessor # :nodoc: all
322
330
  end
323
331
 
324
332
  _, recv, meth, meth_args = n
325
- if meth == @keyword and recv.nil?
333
+ if meth == KEYWORD and recv.nil?
326
334
  block[i] = rewrite_me(n)
327
335
  @did_work = true
328
336
  end
@@ -331,12 +339,13 @@ class TempExpander < SexpProcessor # :nodoc: all
331
339
  s(tag, name, args, scope)
332
340
  end
333
341
 
342
+ private
334
343
  def fix_temp_decl(iter_body)
335
344
  if iter_body.first.sexp_type == :call
336
345
  call_node = iter_body.first
337
346
 
338
347
  _, recv, meth, meth_args = call_node
339
- if meth == @keyword and recv.nil?
348
+ if meth == KEYWORD and recv.nil?
340
349
  _, lhs, op, rhs = meth_args.sexp_body.first
341
350
 
342
351
  old_rhs_body = rhs.sexp_body
@@ -350,20 +359,6 @@ class TempExpander < SexpProcessor # :nodoc: all
350
359
  return nil
351
360
  end
352
361
 
353
- def get_state_meth(klass)
354
- return if @tmp_tables.empty?
355
- block = s(:block)
356
-
357
- @tmp_tables.each do |t|
358
- args = s(:arglist, s(:lit, t.to_sym))
359
- block << s(:call, nil, :temp, args)
360
- end
361
-
362
- meth_name = Module.make_state_meth_name(klass).to_s + "__" + @keyword.to_s
363
- return s(:defn, meth_name.to_sym, s(:args), s(:scope, block))
364
- end
365
-
366
- private
367
362
  def rewrite_me(exp)
368
363
  _, recv, meth, args = exp
369
364
 
@@ -380,137 +375,3 @@ class TempExpander < SexpProcessor # :nodoc: all
380
375
  return s(:call, new_recv, nest_op, nest_args)
381
376
  end
382
377
  end
383
-
384
- # We do four things here for each "with" block
385
- # 1) Remove it from the AST
386
- # 2) Use rewrite_me in the parent class to get the collection name pushed onto @tmp_tables.
387
- # 3) Extract the definition of the "with" collection and push it onto @with_defns
388
- # 4) Extract the rules in the body of the "with" block and push it onto @with_rules
389
-
390
- class WithExpander < TempExpander
391
- attr_reader :with_rules, :with_defns
392
- def initialize
393
- super()
394
- @keyword = :with
395
- @with_rules = []
396
- @with_defns = []
397
- end
398
-
399
- def process_defn(exp)
400
- tag, name, args, scope = exp
401
- if name.to_s =~ /^__bloom__.+/
402
- block = scope[1]
403
-
404
- block.each_with_index do |n,i|
405
- if i == 0
406
- raise Bud::CompileError if n != :block
407
- next
408
- end
409
-
410
- # temp declarations are misparsed if the RHS contains certain constructs
411
- # (e.g., group, "do |f| ... end" rather than "{|f| ... }"). Rewrite to
412
- # correct the misparsing.
413
- if n.sexp_type == :iter
414
- block[i] = nil
415
- iter_body = n.sexp_body
416
- n = fix_temp_decl(iter_body)
417
- @with_defns.push n
418
- @did_work = true unless n.nil?
419
- end
420
-
421
- _, recv, meth, meth_args = n
422
- if meth == @keyword and recv.nil?
423
- block[i] = nil
424
- n = rewrite_me(n)
425
- @with_defns.push n
426
- @did_work = true unless n.nil?
427
- end
428
- end
429
- end
430
- block.compact! unless block.nil? # remove the nils that got pulled out
431
-
432
- return s(tag, name, args, scope)
433
- end
434
-
435
- def get_state_meth(klass)
436
- return if @tmp_tables.empty?
437
- block = s(:block)
438
-
439
- t = @tmp_tables.pop
440
- args = s(:arglist, s(:lit, t.to_sym))
441
- block << s(:call, nil, :temp, args)
442
-
443
- meth_name = Module.make_state_meth_name(klass).to_s + "__" + @keyword.to_s
444
- return s(:defn, meth_name.to_sym, s(:args), s(:scope, block))
445
- end
446
-
447
- private
448
- def rewrite_me(exp)
449
- _, recv, meth, args = exp
450
-
451
- raise Bud::CompileError unless recv == nil
452
- nest_call = args.sexp_body.first
453
- raise Bud::CompileError unless nest_call.sexp_type == :call
454
-
455
- nest_recv, nest_op, nest_args = nest_call.sexp_body
456
- raise Bud::CompileError unless nest_recv.sexp_type == :lit
457
-
458
- tmp_name = nest_recv.sexp_body.first
459
- @tmp_tables.push tmp_name
460
- nest_block = args.sexp_body[1]
461
- if nest_block.first == :call
462
- # a one-rule block doesn't get wrapped in a block. wrap it ourselves.
463
- nest_block = s(:block, nest_block)
464
- end
465
- @with_rules.push nest_block
466
- new_recv = s(:call, nil, tmp_name, s(:arglist))
467
- return s(:call, new_recv, nest_op, nest_args)
468
- end
469
-
470
- undef get_state_meth
471
-
472
- public
473
- def get_state_meth(klass)
474
- return if @tmp_tables.empty?
475
- block = s(:block)
476
-
477
- args = s(:arglist, s(:lit, @tmp_tables.pop.to_sym))
478
- block << s(:call, nil, :temp, args)
479
-
480
- meth_name = Module.make_state_meth_name(klass).to_s + "__" + @keyword.to_s
481
- return s(:defn, meth_name.to_sym, s(:args), s(:scope, block))
482
- end
483
- end
484
-
485
- class DefnRenamer < SexpProcessor # :nodoc: all
486
- def initialize(local_name, rename_tbl)
487
- super()
488
- self.require_empty = false
489
- self.expected = Sexp
490
- @local_name = local_name
491
- @rename_tbl = rename_tbl
492
- end
493
-
494
- def process_defn(exp)
495
- tag, name, args, scope = exp
496
- name_s = name.to_s
497
-
498
- if name_s =~ /^__bootstrap__.+$/
499
- new_name = name_s.sub(/^(__bootstrap__)(.+)$/, "\\1#{@local_name}__\\2")
500
- elsif name_s =~ /^__state\d+__/
501
- new_name = name_s.sub(/^(__state\d+__)(.*)$/, "\\1#{@local_name}__\\2")
502
- elsif name_s =~ /^__bloom__.+$/
503
- new_name = name_s.sub(/^(__bloom__)(.+)$/, "\\1#{@local_name}__\\2")
504
- else
505
- new_name = "#{@local_name}__#{name_s}"
506
- end
507
-
508
- new_name = new_name.to_sym
509
- @rename_tbl[name] = new_name
510
-
511
- # Note that we don't bother to recurse further into the AST: we're only
512
- # interested in top-level :defn nodes.
513
- s(tag, new_name, args, scope)
514
- end
515
- end
516
-
data/lib/bud/source.rb CHANGED
@@ -5,13 +5,13 @@ require 'bud/errors'
5
5
  module Source
6
6
  $cached_file_info = Struct.new(:curr_file, :lines, :last_state_bloom_line).new
7
7
 
8
- #Reads the block corresponding to the location (string of the form "file:line_num").
9
- #Returns an ast for the block
8
+ # Reads the block corresponding to the location (string of the form
9
+ # "file:line_num"). Returns an ast for the block.
10
10
  def Source.read_block(location)
11
- raise Bud::CompileError, "Source must be present in a file; cannot read interactive shell or eval block" if location.start_with? '('
11
+ raise Bud::IllegalSourceError, "source must be present in a file; cannot read interactive shell or eval block" if location.start_with? '('
12
12
  location =~ /^(.*):(\d+)/
13
13
  filename, num = $1, $2.to_i
14
- raise Bud::Error, "Couldn't determine filename from backtrace" if filename.nil?
14
+ raise Bud::IllegalSourceError, "couldn't determine filename from backtrace" if filename.nil?
15
15
  lines = cache(filename, num)
16
16
  # Note: num is 1-based.
17
17
 
@@ -59,7 +59,7 @@ module Source
59
59
  }
60
60
  $cached_file_info.lines = retval
61
61
  end
62
- retval # array of lines.
62
+ retval # array of lines
63
63
  end
64
64
 
65
65
  # Tok is string tokenizer that extracts a substring matching the
data/lib/bud/state.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  module Bud
2
2
  ######## methods for registering collection types
3
- def define_collection(name)
3
+ private
4
+ def check_collection_name(name)
4
5
  if @tables.has_key? name
5
6
  raise Bud::CompileError, "collection already exists: #{name}"
6
7
  end
@@ -11,6 +12,11 @@ module Bud
11
12
  unless reserved.nil?
12
13
  raise Bud::CompileError, "symbol :#{name} reserved, cannot be used as collection name"
13
14
  end
15
+ end
16
+
17
+ def define_collection(name)
18
+ check_collection_name(name)
19
+
14
20
  self.singleton_class.send(:define_method, name) do |*args, &blk|
15
21
  if blk.nil?
16
22
  return @tables[name]
@@ -20,6 +26,7 @@ module Bud
20
26
  end
21
27
  end
22
28
 
29
+ public
23
30
  def input # :nodoc: all
24
31
  true
25
32
  end
@@ -32,7 +39,7 @@ module Bud
32
39
  def interface(mode, name, schema=nil)
33
40
  define_collection(name)
34
41
  t_provides << [name.to_s, mode]
35
- @tables[name] = (mode ? Bud::BudInputInterface : BudOutputInterface).new(name, self, schema)
42
+ @tables[name] = (mode ? BudInputInterface : BudOutputInterface).new(name, self, schema)
36
43
  end
37
44
 
38
45
  # declare an in-memory, non-transient collection. default schema <tt>[:key] => [:val]</tt>.
@@ -41,13 +48,13 @@ module Bud
41
48
  @tables[name] = Bud::BudTable.new(name, self, schema)
42
49
  end
43
50
 
44
- # declare a collection-generating expression. default schema <tt>[:key] => [:val]</tt>.
51
+ # declare a collection-generating expression. default schema <tt>[:key] => [:val]</tt>.
45
52
  def coll_expr(name, expr, schema=nil)
46
53
  define_collection(name)
47
54
  @tables[name] = Bud::BudCollExpr.new(name, self, expr, schema)
48
55
  end
49
-
50
- # declare a syncronously-flushed persistent collection. default schema <tt>[:key] => [:val]</tt>.
56
+
57
+ # declare a syncronously-flushed persistent collection. default schema <tt>[:key] => [:val]</tt>.
51
58
  def sync(name, storage, schema=nil)
52
59
  define_collection(name)
53
60
  case storage
@@ -85,12 +92,6 @@ module Bud
85
92
  @tables[name] = Bud::BudReadOnly.new(name, self, schema)
86
93
  end
87
94
 
88
- def signal(name, schema=nil)
89
- define_collection(name)
90
- @tables[name] = Bud::BudSignal.new(name, self, schema)
91
- end
92
-
93
-
94
95
  # declare a scratch in a bloom statement lhs. schema inferred from rhs.
95
96
  def temp(name)
96
97
  define_collection(name)
@@ -104,7 +105,7 @@ module Bud
104
105
  @tables[name] = Bud::BudChannel.new(name, self, schema, loopback)
105
106
  @channels[name] = @tables[name]
106
107
  end
107
-
108
+
108
109
  # declare a transient network collection that delivers facts back to the
109
110
  # current Bud instance. This is syntax sugar for a channel that always
110
111
  # delivers to the IP/port of the current Bud instance. Default schema