bud 0.9.5 → 0.9.6

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.
@@ -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.