bud 0.9.6 → 0.9.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +6 -14
- data/History.txt +41 -0
- data/README.md +5 -4
- data/docs/cheat.md +108 -9
- data/lib/bud/aggs.rb +4 -2
- data/lib/bud/bud_meta.rb +1 -4
- data/lib/bud/collections.rb +55 -27
- data/lib/bud/executor/README.rescan +2 -1
- data/lib/bud/executor/elements.rb +33 -37
- data/lib/bud/executor/join.rb +88 -110
- data/lib/bud/lattice-core.rb +21 -13
- data/lib/bud/lattice-lib.rb +73 -41
- data/lib/bud/monkeypatch.rb +16 -17
- data/lib/bud/rewrite.rb +13 -10
- data/lib/bud/source.rb +3 -1
- data/lib/bud.rb +85 -46
- metadata +41 -27
checksums.yaml
CHANGED
@@ -1,15 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
metadata.gz: !binary |-
|
9
|
-
OGJmYmMwZjU4Y2RlZWUyNDVkMGFkYTczZDhlNjk5MGQ5NmVlZDIyYzQyMjAz
|
10
|
-
NzEyZWI0YTI1MjVjM2VhZGRjMWE4ZDM4MGVlYzA4YzliYmJjNGUwZjEyY2I2
|
11
|
-
YjZhOTVjNzk5MGVjNWJkMTA0YWRmODk3MDAyYmVlOTM4MmVmYzA=
|
12
|
-
data.tar.gz: !binary |-
|
13
|
-
YjZkOTlkYzQwODJkZTI4YjU0YmFhMmY4MDY5Mjg3Y2ZjMzUyN2ZmZTAyOGI5
|
14
|
-
M2M3NWQ5OTgyM2U2ZjA3NGIzMjkwZjhjMTBhNWFmYzc3OTNiYmZjOWMzNmU3
|
15
|
-
MTkzZmI1MzJlODNkMmE2Y2E1NzU5ODYzNDlhM2Q3N2Q3MDk2Yzg=
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 3d9ec3dd7f92dded5c77e541617853e0954fccaf
|
4
|
+
data.tar.gz: 37c31e760cbfe5305ebb73d3ff6e2417e499022d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a9fe2678a4aa143ebfa6ae677426bc60593550bda6209edc056855a6bca1b4f7ee5b4fc99e773d74679ca57fc615da7128e6db4622c855d45fda60e138bb78de
|
7
|
+
data.tar.gz: 240d532afbe76c4a7740f65276e7d0bb405b52661e8dde63fc42e54a5d40deb6d23c0bea1ef7d4a9e0e0e060c837533a0e3202c3ab41f33779861df6db673509
|
data/History.txt
CHANGED
@@ -1,3 +1,44 @@
|
|
1
|
+
== 0.9.7 / 2013-04-22
|
2
|
+
|
3
|
+
* Avoid raising an exception when Bud is used with Ruby 2.0. There isn't
|
4
|
+
anything special that Bud itself needs to do to support Ruby 2.0, but
|
5
|
+
RubyParser doesn't properly support 2.0 yet. So using 2.0-only syntax in Bud
|
6
|
+
rules is unlikely to work (until RubyParser is updated), but no other problems
|
7
|
+
have been observed.
|
8
|
+
* Reject <= on collections from outside Bloom rules (#289, Aaron Davidson). The
|
9
|
+
previous behavior was error-prone: inserting into a scratch collection via <=
|
10
|
+
was not rejected but the inserted data would immediately be discarded at the
|
11
|
+
beginning of the next tick. Hence, it is simpler to require <+ or <~ for all
|
12
|
+
insertions from outside Bloom rules.
|
13
|
+
* Fix bug in join operators whose qualifiers reference collections defined
|
14
|
+
inside imported modules (#301)
|
15
|
+
* Fix bug in join operators when the same table and column name appears on the
|
16
|
+
LHS of multiple join predicates (#313)
|
17
|
+
* Fix bug in outer joins with multiple predicates (#315)
|
18
|
+
* Fix several bugs in rescan/invalidation logic for tables in the presence of
|
19
|
+
upstream deletions (#303)
|
20
|
+
* Fix regression in file_reader collections (#304)
|
21
|
+
* Improve chaining of notin operators
|
22
|
+
* Optimize join evaluation with multiple predicates
|
23
|
+
* Optimize notin self joins
|
24
|
+
* Improve error reporting for syntax errors on temp collections (#310)
|
25
|
+
* Add Travis CI integration hooks (#300, Josh Rosen)
|
26
|
+
|
27
|
+
Lattices:
|
28
|
+
|
29
|
+
* Rename lmap#apply_monotone to lmap#apply. Also, allow #apply to take either
|
30
|
+
monotone functions or morphisms, and improve error reporting.
|
31
|
+
* Add lmap#filter. This takes an lmap whose values are elements of the lbool
|
32
|
+
lattice and returns an lmap containing only those k/v pairs where the value is
|
33
|
+
true.
|
34
|
+
* More intuitive lattice equi-join syntax (lset#eqjoin). Rather than specifying
|
35
|
+
the join predicate(s) using array indexes, instead allow them to be specified
|
36
|
+
using a hash of field names, as in traditional Bloom joins. This only works
|
37
|
+
when the lset contains Structs.
|
38
|
+
* Remove lset#project, and allow lset#eqjoin to take zero predicates; in the
|
39
|
+
latter case, eqjoin computes the Cartesian product.
|
40
|
+
* Support deletion rules (<-) with lattice values on the RHS
|
41
|
+
|
1
42
|
== 0.9.6 / 2013-02-25
|
2
43
|
|
3
44
|
* Support syntax sugar for initializing lattices (#294). For example, rather
|
data/README.md
CHANGED
@@ -11,11 +11,12 @@ documentation.
|
|
11
11
|
Main deficiencies at this point are:
|
12
12
|
|
13
13
|
- No Ruby constraints: Within Bloom programs the full power of Ruby is also
|
14
|
-
available, including mutable state.
|
15
|
-
|
14
|
+
available, including mutable state. This allows programmers to get outside the
|
15
|
+
Bloom framework and lose cleanliness.
|
16
16
|
|
17
|
-
- Compatibility: Bud only works with Ruby (MRI) 1.8.7 and 1.9.
|
18
|
-
Ruby
|
17
|
+
- Compatibility: Bud only works with Ruby (MRI) 1.8.7 and 1.9. Bud also has
|
18
|
+
experimental support for Ruby 2.0. JRuby and other Ruby implementations are
|
19
|
+
currently not supported.
|
19
20
|
|
20
21
|
## Installation
|
21
22
|
|
data/docs/cheat.md
CHANGED
@@ -21,15 +21,23 @@ As in Ruby, backslash is used to escape a newline.<br>
|
|
21
21
|
end
|
22
22
|
|
23
23
|
## State Declarations ##
|
24
|
-
|
25
|
-
|
26
|
-
|
24
|
+
|
25
|
+
A `state` block contains definitions of two kinds of program state:
|
26
|
+
*collections* and *lattices*. A Bud collection is a *set* of *facts*; each fact
|
27
|
+
is an array of Ruby values. Note that collections do not contain duplicates
|
28
|
+
(inserting a duplicate fact into a collection is ignored).
|
27
29
|
|
28
30
|
Like a table in a relational database, a subset of the columns in a collection
|
29
31
|
makeup the collection's _key_. Attempting to insert two facts into a collection
|
30
32
|
that agree on the key columns (but are not duplicates) results in a runtime
|
31
33
|
exception.
|
32
34
|
|
35
|
+
A lattice represents a value that *grows* over time, where the definition of
|
36
|
+
"growth" depends on the kind of lattice in question. For example, an `lset`
|
37
|
+
lattice contains a set of facts that grows over time (similar to a traditional
|
38
|
+
Bud collection), whereas an `lmax` lattice holds an increasing integer
|
39
|
+
value. For more information on lattices, see the section below.
|
40
|
+
|
33
41
|
### Default Declaration Syntax ###
|
34
42
|
*BudCollection :name, [keys] => [values]*
|
35
43
|
|
@@ -245,12 +253,6 @@ Finally, we output every tuple of `bc` that does *not* appear in `t`.
|
|
245
253
|
* `bc.argagg(:exemplary_agg_name, [:attr1], :attr2))`. *generalizes argmin/max: returns the bc items per attr1 that are chosen by the exemplary
|
246
254
|
aggregate named*
|
247
255
|
|
248
|
-
### Built-in Aggregates: ###
|
249
|
-
|
250
|
-
* Exemplary aggs: `min`, `max`, `choose`
|
251
|
-
* Summary aggs: `count`, `sum`, `avg`
|
252
|
-
* Structural aggs: `accum`
|
253
|
-
|
254
256
|
Note that custom aggregation can be written using `reduce`.
|
255
257
|
|
256
258
|
## Collection Combination (Join) ###
|
@@ -313,6 +315,103 @@ The schema of a temp collection in inherited from the rhs; if the rhs has no
|
|
313
315
|
schema, a simple one is manufactured to suit the data found in the rhs at
|
314
316
|
runtime: `[c0, c1, ...]`.
|
315
317
|
|
318
|
+
## Lattices ##
|
319
|
+
|
320
|
+
In addition to traditional Bud collections and relational-style statements that
|
321
|
+
operate over collections, Bud also supports lattices and rules that operate over
|
322
|
+
lattices. Lattices provide a way to represent values that *grow over time*,
|
323
|
+
where the definition of "growth" depends on the kind of lattice. The following
|
324
|
+
built-in lattice types are currently supported:
|
325
|
+
|
326
|
+
<table>
|
327
|
+
<tr>
|
328
|
+
<td><b>Name</b></td>
|
329
|
+
<td><b>Description</b></td>
|
330
|
+
<td><b>Initial Value</b></td>
|
331
|
+
<td><b>Monotone Functions</b></td>
|
332
|
+
</tr>
|
333
|
+
|
334
|
+
<tr>
|
335
|
+
<td><code>lbool</code></td>
|
336
|
+
<td>Threshold test (<code>false</code> => <code>true</code> conditional)</td>
|
337
|
+
<td>false</td>
|
338
|
+
<td>when_true</td>
|
339
|
+
</tr>
|
340
|
+
|
341
|
+
<tr>
|
342
|
+
<td><code>lmax</code></td>
|
343
|
+
<td>Increasing numeric value</td>
|
344
|
+
<td>-∞</td>
|
345
|
+
<td>gt(n), gt_eq(n), +(n), -(n)</td>
|
346
|
+
</tr>
|
347
|
+
|
348
|
+
<tr>
|
349
|
+
<td><code>lmin</code></td>
|
350
|
+
<td>Decreasing numeric value</td>
|
351
|
+
<td>+∞</td>
|
352
|
+
<td>lt(n), lt_eq(n), +(n), -(n)</td>
|
353
|
+
</tr>
|
354
|
+
|
355
|
+
<tr>
|
356
|
+
<td><code>lset</code></td>
|
357
|
+
<td>Growing set of values</td>
|
358
|
+
<td>empty set</td>
|
359
|
+
<td>contains?, eqjoin, filter, intersect, product, project, size</td>
|
360
|
+
</tr>
|
361
|
+
|
362
|
+
<tr>
|
363
|
+
<td><code>lpset</code></td>
|
364
|
+
<td>Growing set of non-negative numeric values</td>
|
365
|
+
<td>empty set</td>
|
366
|
+
<td>contains?, eqjoin, filter, intersect, product, project, size, sum</td>
|
367
|
+
</tr>
|
368
|
+
|
369
|
+
<tr>
|
370
|
+
<td><code>lbag</code></td>
|
371
|
+
<td>Growing multiset of values</td>
|
372
|
+
<td>empty multiset</td>
|
373
|
+
<td>contains?, multiplicity, intersect, product, project, size</td>
|
374
|
+
</tr>
|
375
|
+
|
376
|
+
<tr>
|
377
|
+
<td><code>lmap</code></td>
|
378
|
+
<td>Map from keys to lattice values</td>
|
379
|
+
<td>empty map</td>
|
380
|
+
<td>at, intersect, key?, key_set, project, size</td>
|
381
|
+
</tr>
|
382
|
+
</table>
|
383
|
+
|
384
|
+
Lattices can be declared in `state` blocks in a similar manner to traditional
|
385
|
+
Bud collections. Similarly, Bloom rules can invoke functions on lattice
|
386
|
+
values. A simple Bloom program that uses lattices to compute a quorum vote is as
|
387
|
+
follows:
|
388
|
+
|
389
|
+
```ruby
|
390
|
+
QUORUM_SIZE = 5
|
391
|
+
RESULT_ADDR = "example.org"
|
392
|
+
|
393
|
+
class QuorumVote
|
394
|
+
include Bud
|
395
|
+
|
396
|
+
state do
|
397
|
+
channel :vote_chn, [:@addr, :voter_id]
|
398
|
+
channel :result_chn, [:@addr]
|
399
|
+
lset :votes
|
400
|
+
lmax :vote_cnt
|
401
|
+
lbool :vote_done
|
402
|
+
end
|
403
|
+
|
404
|
+
bloom do
|
405
|
+
votes <= vote_chn {|v| v.voter_id}
|
406
|
+
vote_cnt <= votes.size
|
407
|
+
got_quorum <= vote_cnt.gt_eq(QUORUM_SIZE)
|
408
|
+
result_chn <~ got_quorum.when_true { [RESULT_ADDR] }
|
409
|
+
end
|
410
|
+
end
|
411
|
+
```
|
412
|
+
|
413
|
+
For more information on lattice support in Bloom, see this [recent paper](http://db.cs.berkeley.edu/papers/socc12-blooml.pdf).
|
414
|
+
|
316
415
|
## Bud Modules ##
|
317
416
|
A Bud module combines state (collections) and logic (Bloom rules). Using modules allows your program to be decomposed into a collection of smaller units.
|
318
417
|
|
data/lib/bud/aggs.rb
CHANGED
@@ -36,7 +36,7 @@ module Bud
|
|
36
36
|
|
37
37
|
class Min < ArgExemplary #:nodoc: all
|
38
38
|
def trans(the_state, val)
|
39
|
-
if the_state <
|
39
|
+
if (the_state <=> val) < 0
|
40
40
|
return the_state, :ignore
|
41
41
|
elsif the_state == val
|
42
42
|
return the_state, :keep
|
@@ -53,8 +53,10 @@ module Bud
|
|
53
53
|
|
54
54
|
class Max < ArgExemplary #:nodoc: all
|
55
55
|
def trans(the_state, val)
|
56
|
-
if the_state >
|
56
|
+
if (the_state <=> val) > 0
|
57
57
|
return the_state, :ignore
|
58
|
+
elsif the_state == val
|
59
|
+
return the_state, :keep
|
58
60
|
else
|
59
61
|
return val, :replace
|
60
62
|
end
|
data/lib/bud/bud_meta.rb
CHANGED
@@ -17,7 +17,7 @@ class BudMeta #:nodoc: all
|
|
17
17
|
# stratum_map = {fully qualified pred => stratum}. Copy stratum_map data
|
18
18
|
# into t_stratum format.
|
19
19
|
raise unless @bud_instance.t_stratum.to_a.empty?
|
20
|
-
@bud_instance.t_stratum
|
20
|
+
@bud_instance.t_stratum.merge(stratum_map.to_a)
|
21
21
|
|
22
22
|
# slot each rule into the stratum corresponding to its lhs pred (from stratum_map)
|
23
23
|
stratified_rules = Array.new(top_stratum + 2) { [] } # stratum -> [ rules ]
|
@@ -265,9 +265,6 @@ class BudMeta #:nodoc: all
|
|
265
265
|
da = ::DepAnalysis.new
|
266
266
|
da.providing <+ @bud_instance.tables[:t_provides].to_a
|
267
267
|
da.depends <+ @bud_instance.t_depends.map{|d| [d.lhs, d.op, d.body, d.nm]}
|
268
|
-
|
269
|
-
#@bud_instance.tables[:t_provides].each {|t| da.providing <+ t}
|
270
|
-
#@bud_instance.tables[:t_depends].each {|t| da.depends_tc <+ t}
|
271
268
|
da.tick_internal
|
272
269
|
@dependency_analysis = da
|
273
270
|
end
|
data/lib/bud/collections.rb
CHANGED
@@ -14,11 +14,11 @@ module Bud
|
|
14
14
|
class BudCollection
|
15
15
|
include Enumerable
|
16
16
|
|
17
|
-
attr_accessor :bud_instance
|
18
|
-
attr_reader :cols, :key_cols # :nodoc: all
|
17
|
+
attr_accessor :bud_instance # :nodoc: all
|
18
|
+
attr_reader :tabname, :cols, :key_cols # :nodoc: all
|
19
19
|
attr_reader :struct
|
20
20
|
attr_reader :storage, :delta, :new_delta, :pending, :tick_delta # :nodoc: all
|
21
|
-
attr_reader :wired_by, :
|
21
|
+
attr_reader :wired_by, :scanner_cnt
|
22
22
|
attr_accessor :invalidated, :rescan
|
23
23
|
attr_accessor :is_source
|
24
24
|
attr_accessor :accumulate_tick_deltas # updated in bud.do_wiring
|
@@ -28,6 +28,7 @@ module Bud
|
|
28
28
|
@bud_instance = bud_instance
|
29
29
|
@invalidated = true
|
30
30
|
@is_source = true # unless it shows up on the lhs of some rule
|
31
|
+
@scanner_cnt = 0
|
31
32
|
@wired_by = []
|
32
33
|
@accumulate_tick_deltas = false
|
33
34
|
init_schema(given_schema) unless given_schema.nil? and defer_schema
|
@@ -62,7 +63,7 @@ module Bud
|
|
62
63
|
if @cols.empty?
|
63
64
|
@cols = nil
|
64
65
|
else
|
65
|
-
@struct = ($struct_classes[@cols] ||=
|
66
|
+
@struct = ($struct_classes[@cols] ||= Bud::TupleStruct.new(*@cols))
|
66
67
|
@structlen = @struct.members.length
|
67
68
|
end
|
68
69
|
setup_accessors
|
@@ -131,11 +132,19 @@ module Bud
|
|
131
132
|
end
|
132
133
|
end
|
133
134
|
|
134
|
-
#
|
135
|
+
# Setup schema accessors, which are class methods. Note that the same
|
136
|
+
# table/column name might appear multiple times on the LHS of a single
|
137
|
+
# join (e.g., (foo * bar).combos(foo.x => bar.y, foo.x => bar.z)). Because
|
138
|
+
# the join predicates are represented as a hash, we need the two instances
|
139
|
+
# of foo.x to be distinct values (otherwise the resulting hash will only
|
140
|
+
# have a single key). Hence, we add a unique ID to the value returned by
|
141
|
+
# schema accessors.
|
135
142
|
@cols_access = Module.new do
|
136
143
|
sc.each_with_index do |c, i|
|
137
144
|
define_method c do
|
138
|
-
|
145
|
+
@counter ||= 0
|
146
|
+
@counter += 1
|
147
|
+
[qualified_tabname, i, c, @counter]
|
139
148
|
end
|
140
149
|
end
|
141
150
|
end
|
@@ -188,11 +197,8 @@ module Bud
|
|
188
197
|
if @bud_instance.wiring?
|
189
198
|
pusher = to_push_elem(the_name, the_schema)
|
190
199
|
# If there is no code block evaluate, use the scanner directly
|
191
|
-
|
192
|
-
|
193
|
-
pusher_pro.elem_name = the_name
|
194
|
-
pusher_pro.tabname = the_name
|
195
|
-
pusher_pro
|
200
|
+
pusher = pusher.pro(&blk) unless blk.nil?
|
201
|
+
pusher
|
196
202
|
else
|
197
203
|
rv = []
|
198
204
|
self.each do |t|
|
@@ -209,7 +215,7 @@ module Bud
|
|
209
215
|
def each_with_index(the_name=tabname, the_schema=schema, &blk)
|
210
216
|
if @bud_instance.wiring?
|
211
217
|
pusher = to_push_elem(the_name, the_schema)
|
212
|
-
pusher.each_with_index(
|
218
|
+
pusher.each_with_index(&blk)
|
213
219
|
else
|
214
220
|
super(&blk)
|
215
221
|
end
|
@@ -251,7 +257,7 @@ module Bud
|
|
251
257
|
end
|
252
258
|
|
253
259
|
def rename(the_name, the_schema=nil, &blk)
|
254
|
-
raise unless @bud_instance.wiring?
|
260
|
+
raise Bud::Error unless @bud_instance.wiring?
|
255
261
|
# a scratch with this name should have been defined during rewriting
|
256
262
|
unless @bud_instance.respond_to? the_name
|
257
263
|
raise Bud::Error, "rename failed to define a scratch named #{the_name}"
|
@@ -397,7 +403,7 @@ module Bud
|
|
397
403
|
private
|
398
404
|
def raise_pk_error(new, old)
|
399
405
|
key = get_key_vals(old)
|
400
|
-
raise Bud::KeyConstraintError, "key conflict inserting #{new.inspect} into \"#{
|
406
|
+
raise Bud::KeyConstraintError, "key conflict inserting #{new.inspect} into \"#{qualified_tabname}\": existing tuple #{old.inspect}, key = #{key.inspect}"
|
401
407
|
end
|
402
408
|
|
403
409
|
private
|
@@ -408,7 +414,7 @@ module Bud
|
|
408
414
|
private
|
409
415
|
def prep_tuple(o)
|
410
416
|
return o if o.class == @struct
|
411
|
-
if o.
|
417
|
+
if o.kind_of? Array
|
412
418
|
if @struct.nil?
|
413
419
|
sch = (1 .. o.length).map{|i| "c#{i}".to_sym}
|
414
420
|
init_schema(sch)
|
@@ -446,7 +452,7 @@ module Bud
|
|
446
452
|
when @delta.object_id; "delta"
|
447
453
|
when @new_delta.object_id; "new_delta"
|
448
454
|
end
|
449
|
-
puts "#{qualified_tabname}.#{storetype} ==> #{t}"
|
455
|
+
puts "#{qualified_tabname}.#{storetype} ==> #{t.inspect}"
|
450
456
|
end
|
451
457
|
return if t.nil? # silently ignore nils resulting from map predicates failing
|
452
458
|
t = prep_tuple(t)
|
@@ -600,6 +606,10 @@ module Bud
|
|
600
606
|
public
|
601
607
|
# instantaneously merge items from collection +o+ into +buf+
|
602
608
|
def <=(collection)
|
609
|
+
unless bud_instance.toplevel.inside_tick
|
610
|
+
raise Bud::CompileError, "illegal use of <= outside of bloom block, use <+ instead"
|
611
|
+
end
|
612
|
+
|
603
613
|
merge(collection)
|
604
614
|
end
|
605
615
|
|
@@ -712,6 +722,7 @@ module Bud
|
|
712
722
|
self, the_schema)
|
713
723
|
toplevel.scanners[this_stratum][[oid, the_name]] = scanner
|
714
724
|
toplevel.push_sources[this_stratum][[oid, the_name]] = scanner
|
725
|
+
@scanner_cnt += 1
|
715
726
|
end
|
716
727
|
return toplevel.scanners[this_stratum][[oid, the_name]]
|
717
728
|
end
|
@@ -839,6 +850,11 @@ module Bud
|
|
839
850
|
public
|
840
851
|
def add_rescan_invalidate(rescan, invalidate)
|
841
852
|
srcs = non_temporal_predecessors
|
853
|
+
|
854
|
+
# XXX: this seems wrong. We might rescan a node for many reasons (e.g.,
|
855
|
+
# because another one of the node's outputs needs to be refilled). We only
|
856
|
+
# need to invalidate + rescan this scratch if one of the inputs to this
|
857
|
+
# collection is *invalidated*.
|
842
858
|
if srcs.any? {|e| rescan.member? e}
|
843
859
|
invalidate << self
|
844
860
|
rescan.merge(srcs)
|
@@ -1012,7 +1028,7 @@ module Bud
|
|
1012
1028
|
if @payload_struct.nil?
|
1013
1029
|
payload_cols = cols.dup
|
1014
1030
|
payload_cols.delete_at(@locspec_idx)
|
1015
|
-
@payload_struct =
|
1031
|
+
@payload_struct = Bud::TupleStruct.new(*payload_cols)
|
1016
1032
|
@payload_colnums = payload_cols.map {|k| cols.index(k)}
|
1017
1033
|
end
|
1018
1034
|
|
@@ -1078,11 +1094,9 @@ module Bud
|
|
1078
1094
|
# add the terminal file descriptor to the EM event loop.
|
1079
1095
|
private
|
1080
1096
|
def read_line
|
1081
|
-
|
1082
|
-
if @prompt
|
1083
|
-
get_out_io.print("#{tabname} > ")
|
1084
|
-
end
|
1097
|
+
get_out_io.print("#{tabname} > ") if @prompt
|
1085
1098
|
|
1099
|
+
toplevel = @bud_instance.toplevel
|
1086
1100
|
in_io = toplevel.options[:stdin]
|
1087
1101
|
input_str = in_io.gets
|
1088
1102
|
return false if input_str.nil? # Hit EOF
|
@@ -1113,7 +1127,7 @@ module Bud
|
|
1113
1127
|
public
|
1114
1128
|
def tick #:nodoc: all
|
1115
1129
|
unless @pending.empty?
|
1116
|
-
@delta = @pending
|
1130
|
+
@delta = @pending # pending used for input tuples in this case
|
1117
1131
|
@tick_delta = @pending.values
|
1118
1132
|
@pending.clear
|
1119
1133
|
else
|
@@ -1121,7 +1135,7 @@ module Bud
|
|
1121
1135
|
@delta.clear
|
1122
1136
|
@tick_delta.clear
|
1123
1137
|
end
|
1124
|
-
@invalidated = true
|
1138
|
+
@invalidated = true # channels and terminals are always invalidated
|
1125
1139
|
end
|
1126
1140
|
|
1127
1141
|
public
|
@@ -1213,8 +1227,8 @@ module Bud
|
|
1213
1227
|
public
|
1214
1228
|
def tick #:nodoc: all
|
1215
1229
|
if $BUD_DEBUG
|
1216
|
-
puts "#{tabname}.
|
1217
|
-
puts "#{tabname}.
|
1230
|
+
puts "#{tabname}.storage -= pending deletes" unless @to_delete.empty? and @to_delete_by_key.empty?
|
1231
|
+
puts "#{tabname}.delta += pending" unless @pending.empty?
|
1218
1232
|
end
|
1219
1233
|
@tick_delta.clear
|
1220
1234
|
deleted = nil
|
@@ -1242,7 +1256,9 @@ module Bud
|
|
1242
1256
|
end
|
1243
1257
|
|
1244
1258
|
def invalidated=(val)
|
1245
|
-
|
1259
|
+
# Might be reset to false at end-of-tick, but shouldn't be set to true
|
1260
|
+
raise Bud::Error, "cannot not set invalidate on table '#{@tabname}'" if val
|
1261
|
+
super
|
1246
1262
|
end
|
1247
1263
|
|
1248
1264
|
def pending_delete(o)
|
@@ -1256,6 +1272,12 @@ module Bud
|
|
1256
1272
|
add_merge_target
|
1257
1273
|
tbl = register_coll_expr(o)
|
1258
1274
|
tbl.pro.wire_to(self, :delete)
|
1275
|
+
elsif o.class <= Bud::LatticePushElement
|
1276
|
+
add_merge_target
|
1277
|
+
o.wire_to(self, :delete)
|
1278
|
+
elsif o.class <= Bud::LatticeWrapper
|
1279
|
+
add_merge_target
|
1280
|
+
o.to_push_elem.wire_to(self, :delete)
|
1259
1281
|
else
|
1260
1282
|
unless o.nil?
|
1261
1283
|
o = o.uniq.compact if o.respond_to?(:uniq)
|
@@ -1293,7 +1315,7 @@ module Bud
|
|
1293
1315
|
# No cache to invalidate. Also, tables do not invalidate dependents,
|
1294
1316
|
# because their own state is not considered invalidated; that happens only
|
1295
1317
|
# if there were pending deletes at the beginning of a tick (see tick())
|
1296
|
-
puts "******** invalidate_cache called on
|
1318
|
+
puts "******** invalidate_cache called on table '#{@tabname}'" if $BUD_DEBUG
|
1297
1319
|
end
|
1298
1320
|
|
1299
1321
|
public
|
@@ -1339,6 +1361,7 @@ module Bud
|
|
1339
1361
|
public
|
1340
1362
|
def each(&block)
|
1341
1363
|
v = @expr.call
|
1364
|
+
return if v.nil? or v == [nil]
|
1342
1365
|
|
1343
1366
|
# XXX: Gross hack. We want to support RHS expressions that do not
|
1344
1367
|
# necessarily return BudCollections (they might instead return lattice
|
@@ -1367,6 +1390,11 @@ module Bud
|
|
1367
1390
|
# NEEDS A TRY/RESCUE BLOCK
|
1368
1391
|
@fd = File.open(@filename, "r")
|
1369
1392
|
@linenum = 0
|
1393
|
+
@invalidated = true
|
1394
|
+
end
|
1395
|
+
|
1396
|
+
def tick
|
1397
|
+
@invalidated = true
|
1370
1398
|
end
|
1371
1399
|
|
1372
1400
|
public
|
@@ -3,7 +3,8 @@ Notes on Invalidate and Rescan in Bud
|
|
3
3
|
|
4
4
|
(I'll use 'downstream' to mean rhs to lhs (like in budplot). In every stratum,
|
5
5
|
data originates at scanned sources at the "top", winds its way through various
|
6
|
-
PushElements and ends up in a collection at the "bottom".
|
6
|
+
PushElements and ends up in a collection at the "bottom". That is, data flows
|
7
|
+
from "upstream" producers to "downstream" consumers. I'll also the term
|
7
8
|
"elements" to mean both dataflow nodes (PushElements) and collections).
|
8
9
|
|
9
10
|
Invalidation strategy works through two flags/signals, rescan and
|