bud 0.9.5 → 0.9.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -12,7 +12,7 @@ module Bud
12
12
  class PushElement < BudCollection
13
13
  attr_accessor :rescan, :invalidated
14
14
  attr_accessor :elem_name
15
- attr_reader :found_delta, :wired_by, :outputs
15
+ attr_reader :found_delta, :wired_by, :outputs, :pendings
16
16
 
17
17
  def initialize(name_in, bud_instance, collection_name=nil, given_schema=nil, defer_schema=false, &blk)
18
18
  super(name_in, bud_instance, given_schema, defer_schema)
@@ -130,7 +130,7 @@ module Bud
130
130
  elsif ou.class <= Bud::LatticeWrapper
131
131
  ou.insert(item, self)
132
132
  else
133
- raise Bud::Error, "expected output target: #{ou.class}"
133
+ raise Bud::Error, "unexpected output target: #{ou.class}"
134
134
  end
135
135
  end
136
136
 
@@ -526,7 +526,8 @@ module Bud
526
526
  @rhs_rcvd = false
527
527
  @hash_tables = [{},{}]
528
528
  if @lhs_keycols.nil? and blk.nil?
529
- # pointwise comparison. Could use zip, but it creates an array for each field pair
529
+ # Pointwise comparison. Could use zip, but it creates an array for each
530
+ # field pair.
530
531
  blk = lambda {|lhs, rhs|
531
532
  lhs.to_a == rhs.to_a
532
533
  }
@@ -536,7 +537,7 @@ module Bud
536
537
 
537
538
  def setup_preds(preds)
538
539
  # This is simpler than PushSHJoin's setup_preds, because notin is a binary
539
- # operator where both lhs and rhs are collections. preds an array of
540
+ # operator where both lhs and rhs are collections. preds is an array of
540
541
  # hash_pairs. For now assume that the attributes are in the same order as
541
542
  # the tables.
542
543
  @lhs_keycols, @rhs_keycols = preds.reduce([[], []]) do |memo, item|
@@ -548,10 +549,13 @@ module Bud
548
549
  memo
549
550
  end
550
551
  end
552
+
551
553
  def find_col(colspec, rel)
552
554
  if colspec.is_a? Symbol
555
+ unless rel.respond_to? colspec
556
+ raise Bud::Error, "attribute :#{colspec} not found in #{rel.tabname}"
557
+ end
553
558
  col_desc = rel.send(colspec)
554
- raise Bud::Error, "unknown column #{colspec} in #{@rel.tabname}" if col_desc.nil?
555
559
  elsif colspec.is_a? Array
556
560
  col_desc = colspec
557
561
  else
@@ -562,7 +566,7 @@ module Bud
562
566
 
563
567
  def get_key(item, offset)
564
568
  keycols = offset == 0 ? @lhs_keycols : @rhs_keycols
565
- keycols.nil? ? $EMPTY : keycols.map{|col| item[col]}
569
+ keycols.nil? ? $EMPTY : item.values_at(*keycols)
566
570
  end
567
571
 
568
572
  public
@@ -571,7 +575,16 @@ module Bud
571
575
  end
572
576
 
573
577
  def insert(item, source)
574
- offset = source == @lhs ? 0 : 1
578
+ if source == @lhs && source == @rhs # Self join
579
+ do_insert(item, 0)
580
+ do_insert(item, 1)
581
+ else
582
+ offset = source == @lhs ? 0 : 1
583
+ do_insert(item, offset)
584
+ end
585
+ end
586
+
587
+ def do_insert(item, offset)
575
588
  key = get_key(item, offset)
576
589
  (@hash_tables[offset][key] ||= Set.new).add item
577
590
  if @rhs_rcvd and offset == 0
@@ -597,7 +610,6 @@ module Bud
597
610
  end
598
611
 
599
612
  def process_match(lhs_item, rhs_values)
600
- exclude = true
601
613
  if rhs_values.nil?
602
614
  # no corresponding rhs. Include in output
603
615
  exclude = false
@@ -605,33 +617,24 @@ module Bud
605
617
  # for any lhs * rhs pair, if block returns true, do not push lhs. lhs is pushed
606
618
  # only if there is no match (anti-join)
607
619
  exclude = rhs_values.any?{|rhs_item| @blk.call(lhs_item, rhs_item)}
620
+ else
621
+ exclude = true
608
622
  end
609
- unless exclude
610
- push_out(lhs_item)
611
- end
612
- end
613
623
 
614
- public
615
- def push_out(item)
616
- @outputs.each do |ou|
617
- if ou.class <= Bud::PushElement
618
- ou.insert(item, self)
619
- elsif ou.class <= Bud::BudCollection
620
- ou.do_insert(item, ou.new_delta)
621
- else
622
- raise Bud::Error, "expected either a PushElement or a BudCollection"
623
- end
624
- end
625
- # for all the following, o is a BudCollection
626
- @deletes.each{|o| o.pending_delete([item])}
627
- @delete_keys.each{|o| o.pending_delete_keys([item])}
628
- @pendings.each{|o| o.pending_merge([item])}
624
+ push_out(lhs_item, false) unless exclude
629
625
  end
630
626
 
631
627
  def invalidate_cache
632
- puts "#{self.class}/#{self.tabname} invalidated" if $BUD_DEBUG
633
- @hash_tables = [{},{}]
634
- @rhs_rcvd = false
628
+ raise Bud::Error if @rhs_rcvd # sanity check; should already be reset
629
+
630
+ if @lhs.rescan
631
+ puts "#{tabname} rel:#{@lhs.tabname} invalidated" if $BUD_DEBUG
632
+ @hash_tables[0] = {}
633
+ end
634
+ if @rhs.rescan
635
+ puts "#{tabname} rel:#{@rhs.tabname} invalidated" if $BUD_DEBUG
636
+ @hash_tables[1] = {}
637
+ end
635
638
  end
636
639
 
637
640
  def stratum_end
data/lib/bud/graphs.rb CHANGED
@@ -167,7 +167,7 @@ class GraphGen #:nodoc: all
167
167
  @edges[ekey].arrowsize = 2
168
168
 
169
169
  @edges[ekey].color = (@nodes[body]["color"].source || "")
170
- @edges[ekey].URL = "#{rule_id}.html" unless rule_id.nil?
170
+ @edges[ekey].URL = "#{rule_id}-#{head}.html" unless rule_id.nil?
171
171
  if head =~ /_msg\z/
172
172
  @edges[ekey].minlen = 2
173
173
  else
@@ -303,7 +303,7 @@ class SpaceTime
303
303
  params[:URL] = "DBM_#{k}/tm_#{item}.svg"
304
304
  end
305
305
  snd = @subs[k].add_nodes(label, params)
306
- unless @head[k].id == snd.id
306
+ unless @head[k].object_id == snd.object_id
307
307
  @subs[k].add_edges(@head[k], snd, :weight => 2)
308
308
  @head[k] = snd
309
309
  end
@@ -1,6 +1,8 @@
1
1
  require 'bud/executor/elements'
2
2
 
3
3
  class Bud::Lattice
4
+ include Comparable
5
+
4
6
  @@lattice_kinds = {}
5
7
  @@global_morphs = Set.new
6
8
  @@global_mfuncs = Set.new
@@ -76,7 +78,7 @@ class Bud::Lattice
76
78
  end
77
79
 
78
80
  def eql?(o)
79
- return self == o
81
+ self == o
80
82
  end
81
83
 
82
84
  # Ensure hashing and equality semantics are consistent.
@@ -84,6 +86,11 @@ class Bud::Lattice
84
86
  reveal.hash
85
87
  end
86
88
 
89
+ # Similarly, use reveal'ed value to implement Comparable.
90
+ def <=>(o)
91
+ reveal <=> o.reveal
92
+ end
93
+
87
94
  # Return the state valued associated with a lattice instance. Note that this
88
95
  # is non-monotonic when invoked from user code; it should be used with care by
89
96
  # framework code.
@@ -107,8 +114,6 @@ end
107
114
 
108
115
  # TODO:
109
116
  # * merge logic for set-oriented collections
110
- # * invalidation/rescan/non-monotonic stuff?
111
- # * expressions on RHS ("CollExpr")
112
117
  class Bud::LatticePushElement
113
118
  attr_reader :wired_by, :outputs
114
119
  attr_accessor :invalidated, :rescan
@@ -381,15 +386,15 @@ class Bud::PushApplyMethod < Bud::LatticePushElement
381
386
  end
382
387
 
383
388
  class Bud::LatticeWrapper
384
- attr_reader :tabname, :wired_by, :rescan_on_merge
385
- attr_accessor :accumulate_tick_deltas
389
+ attr_reader :tabname, :wired_by, :rescan_on_delta
390
+ attr_accessor :accumulate_tick_deltas, :bud_instance
386
391
 
387
392
  def initialize(tabname, klass, bud_i)
388
393
  @tabname = tabname
389
394
  @klass = klass
390
395
  @bud_instance = bud_i
391
396
  @wired_by = []
392
- @rescan_on_merge = Set.new
397
+ @rescan_on_delta = Set.new
393
398
  end
394
399
 
395
400
  def qualified_tabname
@@ -400,6 +405,35 @@ class Bud::LatticeWrapper
400
405
  false
401
406
  end
402
407
 
408
+ def setup_wiring(input, kind)
409
+ if input.class <= Bud::LatticeWrapper
410
+ input.to_push_elem.wire_to(self, kind)
411
+ elsif (input.class <= Bud::LatticePushElement || input.class <= Bud::PushElement)
412
+ input.wire_to(self, kind)
413
+ elsif input.class <= Bud::BudCollection
414
+ input.pro.wire_to(self, kind)
415
+ elsif input.class <= Proc
416
+ tbl = register_coll_expr(input)
417
+ tbl.pro.wire_to(self, kind)
418
+ else
419
+ raise Bud::Error, "unrecognized wiring input: #{input}"
420
+ end
421
+
422
+ add_merge_target
423
+ end
424
+
425
+ def positive_predecessors
426
+ @wired_by.select {|e| e.outputs.include?(self) || e.pendings.include?(self)}
427
+ end
428
+
429
+ private
430
+ def register_coll_expr(expr)
431
+ name = "expr_#{expr.object_id}".to_sym
432
+ @bud_instance.coll_expr(name, expr, nil)
433
+ @bud_instance.send(name)
434
+ end
435
+
436
+ public
403
437
  def current_value
404
438
  @storage ||= @klass.new
405
439
  @storage
@@ -436,30 +470,6 @@ class Bud::LatticeWrapper
436
470
  rv
437
471
  end
438
472
 
439
- def setup_wiring(input, kind)
440
- if input.class <= Bud::LatticeWrapper
441
- input.to_push_elem.wire_to(self, kind)
442
- elsif (input.class <= Bud::LatticePushElement || input.class <= Bud::PushElement)
443
- input.wire_to(self, kind)
444
- elsif input.class <= Bud::BudCollection
445
- input.pro.wire_to(self, kind)
446
- elsif input.class <= Proc
447
- tbl = register_coll_expr(input)
448
- tbl.pro.wire_to(self, kind)
449
- else
450
- raise Bud::Error, "unrecognized wiring input: #{input}"
451
- end
452
-
453
- add_merge_target
454
- end
455
-
456
- private
457
- def register_coll_expr(expr)
458
- name = "expr_#{expr.object_id}".to_sym
459
- @bud_instance.coll_expr(name, expr, nil)
460
- @bud_instance.send(name)
461
- end
462
-
463
473
  # Merge "i" into @new_delta
464
474
  public
465
475
  def insert(i, source)
@@ -520,8 +530,17 @@ class Bud::LatticeWrapper
520
530
  end
521
531
 
522
532
  def bootstrap
523
- @storage = do_merge(current_value, @pending)
524
- @pending = nil
533
+ # Bootstrap blocks might install lattice values via either <= (@new_delta)
534
+ # or <+ (@pending).
535
+ if @new_delta
536
+ merge_to_storage(@new_delta)
537
+ @new_delta = nil
538
+ end
539
+
540
+ if @pending
541
+ merge_to_storage(@pending)
542
+ @pending = nil
543
+ end
525
544
  end
526
545
 
527
546
  def tick
@@ -537,7 +556,7 @@ class Bud::LatticeWrapper
537
556
  m = do_merge(current_value, v)
538
557
  if m != current_value
539
558
  @storage = m
540
- @rescan_on_merge.each do |e|
559
+ @rescan_on_delta.each do |e|
541
560
  if e.kind_of? Bud::ScannerElement
542
561
  e.force_rescan = true
543
562
  else
@@ -1,69 +1,66 @@
1
1
  require 'bud/lattice-core'
2
2
 
3
+ # Float::INFINITY only defined in MRI 1.9.2+
4
+ unless defined? Float::INFINITY
5
+ Float::INFINITY = 1.0/0.0
6
+ end
7
+
3
8
  class Bud::MaxLattice < Bud::Lattice
4
9
  wrapper_name :lmax
5
10
 
6
- def initialize(i=nil)
7
- unless i.nil? || i.class <= Comparable
8
- reject_input(i)
9
- end
11
+ def initialize(i=-Float::INFINITY)
12
+ reject_input(i) unless i.class <= Comparable
10
13
  @v = i
11
14
  end
12
15
 
13
16
  def merge(i)
14
- i_val = i.reveal
15
- (@v.nil? || (i_val != nil && i_val > @v)) ? i : self
17
+ i.reveal > @v ? i : self
16
18
  end
17
19
 
18
20
  morph :gt do |k|
19
- Bud::BoolLattice.new(!!(@v && @v > k))
21
+ Bud::BoolLattice.new(!!(@v > k))
20
22
  end
21
23
 
22
24
  morph :gt_eq do |k|
23
- Bud::BoolLattice.new(!!(@v && @v >= k))
25
+ Bud::BoolLattice.new(!!(@v >= k))
24
26
  end
25
27
 
26
28
  # XXX: support MaxLattice input?
27
29
  morph :+ do |i|
28
- # Since bottom of lmax is negative infinity, + is a no-op
29
- return self if @v.nil?
30
+ # NB: since bottom of lmax is negative infinity, + is a no-op
30
31
  reject_input(i, "+") unless i.class <= Numeric
31
32
  self.class.new(@v + i)
32
33
  end
33
34
 
34
35
  morph :min_of do |i|
35
36
  reject_input(i, "min_of") unless i.class <= Numeric
36
- (@v.nil? || i < @v) ? self.class.new(i) : self
37
+ i < @v ? self.class.new(i) : self
37
38
  end
38
39
 
39
40
  def lt_eq(k)
40
- Bud::BoolLattice.new(!!(@v && @v <= k))
41
+ Bud::BoolLattice.new(!!(@v <= k))
41
42
  end
42
43
  end
43
44
 
44
45
  class Bud::MinLattice < Bud::Lattice
45
46
  wrapper_name :lmin
46
47
 
47
- def initialize(i=nil)
48
- unless i.nil? || i.class <= Comparable
49
- reject_input(i)
50
- end
48
+ def initialize(i=Float::INFINITY)
49
+ reject_input(i) unless i.class <= Comparable
51
50
  @v = i
52
51
  end
53
52
 
54
53
  def merge(i)
55
- i_val = i.reveal
56
- (@v.nil? || (i_val != nil && i_val < @v)) ? i : self
54
+ i.reveal < @v ? i : self
57
55
  end
58
56
 
59
57
  morph :lt do |k|
60
- Bud::BoolLattice.new(!!(@v && @v < k))
58
+ Bud::BoolLattice.new(!!(@v < k))
61
59
  end
62
60
 
63
61
  # XXX: support MinLattice input
64
62
  morph :+ do |i|
65
63
  # Since bottom of lmin is infinity, + is a no-op
66
- return self if @v.nil?
67
64
  reject_input(i, "+") unless i.class <= Numeric
68
65
  self.class.new(@v + i)
69
66
  end
@@ -205,7 +202,8 @@ end
205
202
  class Bud::SetLattice < Bud::Lattice
206
203
  wrapper_name :lset
207
204
 
208
- def initialize(i=[])
205
+ def initialize(i=Set.new)
206
+ reject_input(i) unless i.kind_of? Enumerable
209
207
  reject_input(i) if i.any? {|e| e.kind_of? Bud::Lattice}
210
208
 
211
209
  i = Set.new(i) unless i.kind_of? Set
@@ -216,6 +214,11 @@ class Bud::SetLattice < Bud::Lattice
216
214
  wrap_unsafe(@v | i.reveal)
217
215
  end
218
216
 
217
+ # Override default "inspect" implementation to produce slightly nicer output
218
+ def inspect
219
+ "<#{self.class.wrapper}: #{reveal.to_a.sort.inspect}>"
220
+ end
221
+
219
222
  morph :intersect do |i|
220
223
  wrap_unsafe(@v & i.reveal)
221
224
  end
@@ -304,8 +307,8 @@ class Bud::PositiveSetLattice < Bud::SetLattice
304
307
  end
305
308
 
306
309
  monotone :pos_sum do
307
- @sum = @v.reduce(:+) if @sum.nil?
308
- Bud::MaxLattice.new(@sum)
310
+ @sum = @v.reduce(Bud::MaxLattice.new(0), :+) if @sum.nil?
311
+ @sum
309
312
  end
310
313
  end
311
314
 
@@ -361,7 +364,7 @@ class Bud::BagLattice < Bud::Lattice
361
364
  end
362
365
 
363
366
  monotone :size do
364
- @size = @v.values.reduce(:+) if @size.nil?
365
- Bud::MaxLattice.new(@size)
367
+ @size = @v.values.reduce(Bud::MaxLattice.new(0), :+) if @size.nil?
368
+ @size
366
369
  end
367
370
  end
@@ -46,7 +46,7 @@ class Struct
46
46
  false
47
47
  end
48
48
 
49
- def to_msgpack(out='')
49
+ def to_msgpack(out=nil)
50
50
  self.to_a.to_msgpack(out)
51
51
  end
52
52
 
data/lib/bud/rebl.rb CHANGED
@@ -4,7 +4,9 @@ require 'rubygems'
4
4
  require 'bud'
5
5
  require 'abbrev'
6
6
  require 'tempfile'
7
- TABLE_TYPES = ["table", "scratch", "channel", "loopback", "periodic", "sync", "store"]
7
+
8
+ TABLE_TYPES = ["table", "scratch", "channel", "loopback", "periodic",
9
+ "sync", "store", "interface", "interfaces"]
8
10
 
9
11
  # The class to which rebl adds user-specified rules and declarations.
10
12
  class ReblBase
@@ -93,10 +95,13 @@ class ReblShell
93
95
 
94
96
  # One step of the rebl shell loop: processes one rebl shell line from stdin
95
97
  # and returns. May raise an Exception.
96
- def self.rebl_loop(lib,noreadline=false)
98
+ def self.rebl_loop(lib, noreadline=false)
97
99
  begin
98
- line = Readline::readline('rebl> ') unless noreadline
99
- line = gets if noreadline
100
+ if noreadline
101
+ line = gets
102
+ else
103
+ line = Readline::readline('rebl> ')
104
+ end
100
105
  do_exit if line.nil?
101
106
  line.strip!
102
107
  return if line.empty?
@@ -110,7 +115,7 @@ class ReblShell
110
115
  else
111
116
  puts "invalid command or ambiguous command prefix"
112
117
  end
113
- elsif TABLE_TYPES.include? split_line[0]
118
+ elsif is_collection? split_line[0]
114
119
  # Collection
115
120
  lib.add_collection(line)
116
121
  else
@@ -186,6 +191,12 @@ class ReblShell
186
191
  puts "\n" + @@exit_message
187
192
  exit!
188
193
  end
194
+
195
+ # Checks if a given string refers to a collection type (one of the builtin
196
+ # collection types or a wrapper_name for a lattice).
197
+ def self.is_collection?(c)
198
+ TABLE_TYPES.include?(c) || Bud::Lattice.lattice_kinds.has_key?(c.to_sym)
199
+ end
189
200
  end
190
201
 
191
202
 
@@ -233,11 +244,14 @@ class LibRebl
233
244
  def dump(c)
234
245
  if c.nil?
235
246
  puts "Error: dump must be passed a collection name"
236
- elsif not @rebl_class_inst.tables.has_key? c.to_sym
237
- puts "Error: non-existent collection \"#{c}\""
238
- else
247
+ elsif @rebl_class_inst.tables.has_key? c.to_sym
239
248
  tups = @rebl_class_inst.tables[c.to_sym].to_a.sort
240
249
  puts(tups.empty? ? "(empty)" : tups.sort.map{|t| "#{t}"}.join("\n"))
250
+ elsif @rebl_class_inst.lattices.has_key? c.to_sym
251
+ val = @rebl_class_inst.lattices[c.to_sym].current_value
252
+ puts val.inspect
253
+ else
254
+ puts "Error: non-existent collection \"#{c}\""
241
255
  end
242
256
  end
243
257
 
@@ -334,11 +348,15 @@ class LibRebl
334
348
  end)
335
349
  @rebl_class_inst.dbm_tables.merge! @old_inst.dbm_tables
336
350
  @rebl_class_inst.zk_tables.merge! @old_inst.zk_tables
351
+ @rebl_class_inst.lattices.merge! @old_inst.lattices
337
352
 
338
353
  # Fix the bud instance pointers from copied tables.
339
354
  @rebl_class_inst.tables.each_value do |v|
340
355
  v.bud_instance = @rebl_class_inst
341
356
  end
357
+ @rebl_class_inst.lattices.each_value do |v|
358
+ v.bud_instance = @rebl_class_inst
359
+ end
342
360
  end
343
361
 
344
362
  # Run lazily in background, shutting down old instance.