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
data/lib/bud/lattice-lib.rb
CHANGED
@@ -118,18 +118,39 @@ class Bud::MapLattice < Bud::Lattice
|
|
118
118
|
if @v.has_key? k
|
119
119
|
@v[k]
|
120
120
|
else
|
121
|
-
|
121
|
+
if args.empty?
|
122
|
+
raise Bud::Error, "missing key for lmap#at(#{k}) but no bottom type given"
|
123
|
+
end
|
122
124
|
args.first.new
|
123
125
|
end
|
124
126
|
end
|
125
127
|
|
128
|
+
morph :filter do
|
129
|
+
rv = {}
|
130
|
+
@v.each_pair do |k, val|
|
131
|
+
unless val.class <= Bud::BoolLattice
|
132
|
+
raise Bud::Error, "filter invoked on non-boolean map value: #{val}"
|
133
|
+
end
|
134
|
+
rv[k] = val if val.reveal == true
|
135
|
+
end
|
136
|
+
wrap_unsafe(rv)
|
137
|
+
end
|
138
|
+
|
126
139
|
morph :apply_morph do |sym, *args|
|
127
|
-
|
140
|
+
unless Bud::Lattice.global_morphs.include? sym
|
141
|
+
raise Bud::Error, "apply_morph called with non-morphism: #{sym}"
|
142
|
+
end
|
128
143
|
do_apply(sym, args)
|
129
144
|
end
|
130
145
|
|
131
|
-
|
132
|
-
|
146
|
+
# NB: "apply" can be used with both monotone functions and morphisms. We also
|
147
|
+
# provide apply_morph, which is slightly faster when theprogrammer knows they
|
148
|
+
# are applying a morphism.
|
149
|
+
monotone :apply do |sym, *args|
|
150
|
+
unless Bud::Lattice.global_mfuncs.include?(sym) ||
|
151
|
+
Bud::Lattice.global_morphs.include?(sym)
|
152
|
+
raise Bud::Error, "apply called with non-monotone function: #{sym}"
|
153
|
+
end
|
133
154
|
do_apply(sym, args)
|
134
155
|
end
|
135
156
|
|
@@ -223,19 +244,6 @@ class Bud::SetLattice < Bud::Lattice
|
|
223
244
|
wrap_unsafe(@v & i.reveal)
|
224
245
|
end
|
225
246
|
|
226
|
-
morph :product do |i, &blk|
|
227
|
-
rv = Set.new
|
228
|
-
@v.each do |a|
|
229
|
-
if blk.nil?
|
230
|
-
t = i.pro {|b| [a,b]}
|
231
|
-
else
|
232
|
-
t = i.pro {|b| blk.call(a, b)}
|
233
|
-
end
|
234
|
-
rv.merge(t.reveal)
|
235
|
-
end
|
236
|
-
wrap_unsafe(rv)
|
237
|
-
end
|
238
|
-
|
239
247
|
morph :contains? do |i|
|
240
248
|
Bud::BoolLattice.new(@v.member? i)
|
241
249
|
end
|
@@ -254,44 +262,68 @@ class Bud::SetLattice < Bud::Lattice
|
|
254
262
|
Bud::MaxLattice.new(@v.size)
|
255
263
|
end
|
256
264
|
|
257
|
-
# Assuming that this set
|
258
|
-
# an equijoin between the current lattice and
|
259
|
-
#
|
260
|
-
#
|
261
|
-
# tuples to the user-supplied block
|
262
|
-
|
265
|
+
# Assuming that the elements of this set are Structs (tuples with named field
|
266
|
+
# accessors), this performs an equijoin between the current lattice and
|
267
|
+
# i. `preds` is a hash of join predicates; each k/v pair in the hash is an
|
268
|
+
# equality predicate that self_tup[k] == i_tup[v]. The return value is the
|
269
|
+
# result of passing pairs of join tuples to the user-supplied code block
|
270
|
+
# (values for which the code block returns nil are omitted from the
|
271
|
+
# result). Note that if no predicates are passed, this computes the Cartesian
|
272
|
+
# product (in which case the input elements do not need to be Structs).
|
273
|
+
morph :eqjoin do |*args, &blk|
|
274
|
+
# Need to emulate default block arguments for MRI 1.8
|
275
|
+
i, preds = args
|
276
|
+
preds ||= {}
|
263
277
|
rv = Set.new
|
264
278
|
@v.each do |a|
|
265
|
-
i.probe(
|
266
|
-
|
279
|
+
i.probe(a, preds).each do |b|
|
280
|
+
if blk.nil?
|
281
|
+
rv << [a,b]
|
282
|
+
else
|
283
|
+
val = blk.call(a, b)
|
284
|
+
rv << val unless val.nil?
|
285
|
+
end
|
267
286
|
end
|
268
287
|
end
|
269
288
|
wrap_unsafe(rv)
|
270
289
|
end
|
271
290
|
|
272
|
-
# Assuming that this set contains
|
273
|
-
#
|
274
|
-
#
|
275
|
-
def probe(
|
276
|
-
@
|
277
|
-
|
291
|
+
# Assuming that this set contains Structs, this method takes a value "val" and
|
292
|
+
# a hash of predicates "preds". It returns all the structs t where val[k] =
|
293
|
+
# t[v] for all k,v in preds; an empty array is returned if no matches found.
|
294
|
+
def probe(val, preds)
|
295
|
+
return @v if preds.empty?
|
296
|
+
|
297
|
+
probe_val = schema_fetch(val, preds.keys)
|
298
|
+
build_index(preds.values)
|
299
|
+
index = @join_indexes[preds.values]
|
300
|
+
return index[probe_val] || []
|
278
301
|
end
|
279
302
|
|
280
303
|
private
|
281
|
-
def
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
304
|
+
def schema_fetch(val, cols)
|
305
|
+
cols.map {|s| val[s]}
|
306
|
+
end
|
307
|
+
|
308
|
+
def build_index(cols)
|
309
|
+
@join_indexes ||= {}
|
310
|
+
return @join_indexes[cols] if @join_indexes.has_key? cols
|
311
|
+
|
312
|
+
idx = {}
|
313
|
+
@v.each do |val|
|
314
|
+
index_val = schema_fetch(val, cols)
|
315
|
+
idx[index_val] ||= []
|
316
|
+
idx[index_val] << val
|
287
317
|
end
|
288
|
-
|
318
|
+
|
319
|
+
@join_indexes[cols] = idx
|
320
|
+
return idx
|
289
321
|
end
|
290
322
|
end
|
291
323
|
|
292
|
-
# A set that admits only non-negative numbers. This allows "sum" to be
|
293
|
-
#
|
294
|
-
#
|
324
|
+
# A set that admits only non-negative numbers. This allows "sum" to be a
|
325
|
+
# monotone function. Note that this does duplicate elimination on its input, so
|
326
|
+
# it actually computes "SUM(DISTINCT ...)" in SQL.
|
295
327
|
#
|
296
328
|
# XXX: for methods that take a user-provided code block, we need to ensure that
|
297
329
|
# the set continues to contain only positive numbers.
|
data/lib/bud/monkeypatch.rb
CHANGED
@@ -10,33 +10,34 @@ class Class
|
|
10
10
|
end
|
11
11
|
end
|
12
12
|
|
13
|
-
# FIXME:
|
14
|
-
|
15
|
-
|
13
|
+
# FIXME: Should likely override #hash and #eql? as well.
|
14
|
+
class Bud::TupleStruct < Struct
|
15
|
+
include Comparable
|
16
|
+
|
16
17
|
def <=>(o)
|
17
18
|
if o.class == self.class
|
18
19
|
self.each_with_index do |e, i|
|
19
|
-
|
20
|
-
|
20
|
+
other = o[i]
|
21
|
+
next if e == other
|
22
|
+
return nil if e.nil?
|
23
|
+
return nil if other.nil?
|
24
|
+
return e <=> other
|
21
25
|
end
|
22
26
|
return 0
|
23
27
|
elsif o.nil?
|
24
|
-
return
|
28
|
+
return nil
|
25
29
|
else
|
26
30
|
raise "Comparison (<=>) between #{o.class} and #{self.class} not implemented"
|
27
31
|
end
|
28
32
|
end
|
29
33
|
|
30
|
-
alias oldeq :==
|
31
34
|
def ==(o)
|
32
35
|
if o.class == self.class
|
33
|
-
return
|
36
|
+
return super
|
34
37
|
elsif o.class == Array
|
35
38
|
begin
|
36
39
|
self.each_with_index do |el, i|
|
37
|
-
if el != o[i]
|
38
|
-
return false
|
39
|
-
end
|
40
|
+
return false if el != o[i]
|
40
41
|
end
|
41
42
|
return true
|
42
43
|
rescue StandardError
|
@@ -62,9 +63,7 @@ end
|
|
62
63
|
class Array
|
63
64
|
alias :oldeq :==
|
64
65
|
def ==(o)
|
65
|
-
if o.kind_of?
|
66
|
-
o = o.to_a
|
67
|
-
end
|
66
|
+
o = o.to_a if o.kind_of? Bud::TupleStruct
|
68
67
|
self.oldeq(o)
|
69
68
|
end
|
70
69
|
end
|
@@ -126,7 +125,6 @@ class Module
|
|
126
125
|
@bud_import_tbl
|
127
126
|
end
|
128
127
|
|
129
|
-
|
130
128
|
# the block of Bloom collection declarations. one per module.
|
131
129
|
def state(&block)
|
132
130
|
meth_name = Module.make_state_meth_name(self)
|
@@ -139,8 +137,9 @@ class Module
|
|
139
137
|
define_method(meth_name, &block)
|
140
138
|
end
|
141
139
|
|
142
|
-
# bloom statements to be registered with Bud runtime. optional +block_name+
|
143
|
-
#
|
140
|
+
# bloom statements to be registered with Bud runtime. optional +block_name+
|
141
|
+
# assigns a name for the block; this is useful documentation, and also allows
|
142
|
+
# the block to be overridden in a child class.
|
144
143
|
def bloom(block_name=nil, &block)
|
145
144
|
# If no block name was specified, generate a unique name
|
146
145
|
if block_name.nil?
|
data/lib/bud/rewrite.rb
CHANGED
@@ -8,7 +8,7 @@ class RuleRewriter < Ruby2Ruby # :nodoc: all
|
|
8
8
|
MONOTONE_WHITELIST = [:==, :+, :<=, :-, :<, :>, :*, :~,
|
9
9
|
:pairs, :matches, :combos, :flatten, :new,
|
10
10
|
:lefts, :rights, :map, :flat_map, :pro, :merge,
|
11
|
-
:cols, :key_cols, :val_cols, :payloads, :lambda,
|
11
|
+
:schema, :cols, :key_cols, :val_cols, :payloads, :lambda,
|
12
12
|
:tabname, :current_value].to_set
|
13
13
|
|
14
14
|
def initialize(seed, bud_instance)
|
@@ -366,7 +366,7 @@ class UnsafeFuncRewriter < SexpProcessor
|
|
366
366
|
# We assume that unsafe funcs have a nil receiver (Bud instance is implicit
|
367
367
|
# receiver).
|
368
368
|
if recv.nil? and @elem_stack.size > 0
|
369
|
-
unless is_safe_func(op) ||
|
369
|
+
unless is_safe_func(op) || is_collection_name?(op)
|
370
370
|
@unsafe_func_called = true
|
371
371
|
end
|
372
372
|
end
|
@@ -388,8 +388,8 @@ class UnsafeFuncRewriter < SexpProcessor
|
|
388
388
|
return rv
|
389
389
|
end
|
390
390
|
|
391
|
-
def
|
392
|
-
@bud_instance.lattices.has_key?
|
391
|
+
def is_collection_name?(op)
|
392
|
+
@bud_instance.tables.has_key?(op.to_sym) || @bud_instance.lattices.has_key?(op.to_sym)
|
393
393
|
end
|
394
394
|
|
395
395
|
def is_safe_func(op)
|
@@ -560,7 +560,7 @@ class TempExpander < SexpProcessor # :nodoc: all
|
|
560
560
|
attr_reader :tmp_tables
|
561
561
|
attr_accessor :did_work
|
562
562
|
|
563
|
-
|
563
|
+
TEMP_KEYWORD = :temp
|
564
564
|
|
565
565
|
def initialize
|
566
566
|
super()
|
@@ -588,8 +588,8 @@ class TempExpander < SexpProcessor # :nodoc: all
|
|
588
588
|
end
|
589
589
|
|
590
590
|
_, recv, meth, meth_args = n
|
591
|
-
if meth ==
|
592
|
-
body[i] =
|
591
|
+
if meth == TEMP_KEYWORD and recv.nil?
|
592
|
+
body[i] = rewrite_temp(n)
|
593
593
|
@did_work = true
|
594
594
|
end
|
595
595
|
end
|
@@ -602,7 +602,7 @@ class TempExpander < SexpProcessor # :nodoc: all
|
|
602
602
|
call_node = iter_body.first
|
603
603
|
_, recv, meth, *meth_args = call_node
|
604
604
|
|
605
|
-
if meth ==
|
605
|
+
if meth == TEMP_KEYWORD and recv.nil?
|
606
606
|
_, lhs, op, rhs = meth_args.first
|
607
607
|
new_rhs = s(:iter, rhs, *(iter_body[1..-1]))
|
608
608
|
meth_args.first[3] = new_rhs
|
@@ -612,7 +612,7 @@ class TempExpander < SexpProcessor # :nodoc: all
|
|
612
612
|
return nil
|
613
613
|
end
|
614
614
|
|
615
|
-
def
|
615
|
+
def rewrite_temp(exp)
|
616
616
|
_, recv, meth, *args = exp
|
617
617
|
|
618
618
|
raise Bud::CompileError unless recv.nil?
|
@@ -620,7 +620,10 @@ class TempExpander < SexpProcessor # :nodoc: all
|
|
620
620
|
raise Bud::CompileError unless nest_call.sexp_type == :call
|
621
621
|
|
622
622
|
nest_recv, nest_op, *nest_args = nest_call.sexp_body
|
623
|
-
|
623
|
+
unless nest_recv.sexp_type == :lit
|
624
|
+
recv_src = Ruby2Ruby.new.process(Marshal.load(Marshal.dump(nest_recv)))
|
625
|
+
raise Bud::CompileError, "argument to temp must be a symbol: #{recv_src}"
|
626
|
+
end
|
624
627
|
|
625
628
|
tmp_name = nest_recv.sexp_body.first
|
626
629
|
@tmp_tables << tmp_name
|
data/lib/bud/source.rb
CHANGED
@@ -17,7 +17,9 @@ module Source
|
|
17
17
|
lines = cache(filename, num)
|
18
18
|
# Note: num is 1-based.
|
19
19
|
|
20
|
-
|
20
|
+
# for_current_ruby might object if the current Ruby version is not supported
|
21
|
+
# by RubyParser; bravely try to continue on regardless
|
22
|
+
parser = RubyParser.for_current_ruby rescue RubyParser.new
|
21
23
|
stmt = "" # collection of lines that form one complete Ruby statement
|
22
24
|
ast = nil
|
23
25
|
lines[num .. -1].each do |l|
|
data/lib/bud.rb
CHANGED
@@ -71,6 +71,7 @@ module Bud
|
|
71
71
|
attr_reader :tables, :builtin_tables, :channels, :zk_tables, :dbm_tables, :app_tables, :lattices
|
72
72
|
attr_reader :push_sources, :push_elems, :push_joins, :scanners, :merge_targets
|
73
73
|
attr_reader :this_stratum, :this_rule, :rule_orig_src, :done_bootstrap
|
74
|
+
attr_reader :inside_tick
|
74
75
|
attr_accessor :stratified_rules
|
75
76
|
attr_accessor :metrics, :periodics
|
76
77
|
attr_accessor :this_rule_context, :qualified_name
|
@@ -165,11 +166,11 @@ module Bud
|
|
165
166
|
do_rewrite
|
166
167
|
if toplevel == self
|
167
168
|
# initialize per-stratum state
|
168
|
-
num_strata = @stratified_rules.length
|
169
|
-
@scanners = num_strata.times.map{{}}
|
170
|
-
@push_sources = num_strata.times.map{{}}
|
171
|
-
@push_joins = num_strata.times.map{[]}
|
172
|
-
@merge_targets = num_strata.times.map{Set.new}
|
169
|
+
@num_strata = @stratified_rules.length
|
170
|
+
@scanners = @num_strata.times.map{{}}
|
171
|
+
@push_sources = @num_strata.times.map{{}}
|
172
|
+
@push_joins = @num_strata.times.map{[]}
|
173
|
+
@merge_targets = @num_strata.times.map{Set.new}
|
173
174
|
end
|
174
175
|
end
|
175
176
|
|
@@ -318,8 +319,6 @@ module Bud
|
|
318
319
|
end
|
319
320
|
|
320
321
|
def do_wiring
|
321
|
-
@num_strata = @stratified_rules.length
|
322
|
-
|
323
322
|
@stratified_rules.each_with_index { |rules, stratum| eval_rules(rules, stratum) }
|
324
323
|
|
325
324
|
# Prepare list of tables that will be actively used at run time. First, all
|
@@ -367,6 +366,24 @@ module Bud
|
|
367
366
|
end
|
368
367
|
end
|
369
368
|
|
369
|
+
# We create "orphan" scanners for collections that don't appear on the RHS
|
370
|
+
# of any rules, but do appear on the LHS of at least one rule. These
|
371
|
+
# scanners aren't needed to compute the fixpoint, but they are used as part
|
372
|
+
# of rescan/invalidation (e.g., if an orphaned collection receives a manual
|
373
|
+
# deletion operation, we need to arrange for the collection to be
|
374
|
+
# re-filled).
|
375
|
+
@orphan_scanners = [] # Pairs of [scanner, stratum]
|
376
|
+
@app_tables.each do |t|
|
377
|
+
next unless t.class <= Bud::BudCollection # skip lattice wrappers
|
378
|
+
next if t.scanner_cnt > 0
|
379
|
+
|
380
|
+
stratum = collection_stratum(t.qualified_tabname.to_s)
|
381
|
+
# if the collection also doesn't appear on any LHSs, skip it
|
382
|
+
next if stratum.nil?
|
383
|
+
@orphan_scanners << [Bud::ScannerElement.new(t.tabname, self, t, t.schema),
|
384
|
+
stratum]
|
385
|
+
end
|
386
|
+
|
370
387
|
# Sanity check
|
371
388
|
@push_sorted_elems.each do |stratum_elems|
|
372
389
|
stratum_elems.each {|se| se.check_wiring}
|
@@ -432,19 +449,17 @@ module Bud
|
|
432
449
|
#
|
433
450
|
# scanner[stratum].rescan_set = Similar to above.
|
434
451
|
def prepare_invalidation_scheme
|
435
|
-
num_strata = @push_sorted_elems.size
|
436
452
|
if $BUD_SAFE
|
437
453
|
@app_tables = @tables.values + @lattices.values # No collections excluded
|
438
454
|
|
439
455
|
rescan = Set.new
|
440
456
|
invalidate = @app_tables.select {|t| t.class <= BudScratch}.to_set
|
441
|
-
num_strata.times do |stratum|
|
457
|
+
@num_strata.times do |stratum|
|
442
458
|
@push_sorted_elems[stratum].each do |elem|
|
443
459
|
invalidate << elem
|
444
460
|
rescan << elem
|
445
461
|
end
|
446
462
|
end
|
447
|
-
#prune_rescan_invalidate(rescan, invalidate)
|
448
463
|
@default_rescan = rescan.to_a
|
449
464
|
@default_invalidate = invalidate.to_a
|
450
465
|
@reset_list = [] # Nothing to reset at end of tick. It'll be overwritten anyway
|
@@ -474,7 +489,7 @@ module Bud
|
|
474
489
|
invalidate = @app_tables.select {|t| t.invalidate_at_tick}.to_set
|
475
490
|
rescan = Set.new
|
476
491
|
|
477
|
-
num_strata.times do |stratum|
|
492
|
+
@num_strata.times do |stratum|
|
478
493
|
@push_sorted_elems[stratum].each do |elem|
|
479
494
|
rescan << elem if elem.rescan_at_tick
|
480
495
|
|
@@ -496,29 +511,35 @@ module Bud
|
|
496
511
|
puts "Unsafe targets: #{unsafe_targets.inspect}"
|
497
512
|
end
|
498
513
|
|
499
|
-
#
|
500
|
-
# tables and elements that will
|
501
|
-
# invalidated at
|
514
|
+
# For each collection that is to be scanned, compute the set of dependent
|
515
|
+
# tables and elements that will need invalidation and/or rescan if that
|
516
|
+
# table were to be invalidated at runtime.
|
502
517
|
dflt_rescan = rescan
|
503
518
|
dflt_invalidate = invalidate
|
504
519
|
to_reset = rescan + invalidate
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
520
|
+
each_scanner do |scanner, stratum|
|
521
|
+
# If it is going to be always invalidated, it doesn't need further
|
522
|
+
# examination. Lattice scanners also don't get invalidated.
|
523
|
+
next if dflt_rescan.member? scanner
|
524
|
+
next if scanner.class <= LatticeScanner
|
525
|
+
|
526
|
+
rescan = dflt_rescan.clone
|
527
|
+
invalidate = dflt_invalidate + [scanner.collection]
|
528
|
+
rescan_invalidate_tc(stratum, rescan, invalidate)
|
529
|
+
prune_rescan_invalidate(rescan, invalidate)
|
530
|
+
|
531
|
+
# Make sure we reset the rescan/invalidate flag for this scanner at
|
532
|
+
# end-of-tick, but we can remove the scanner from its own
|
533
|
+
# rescan_set/inval_set.
|
534
|
+
to_reset.merge(rescan)
|
535
|
+
to_reset.merge(invalidate)
|
536
|
+
rescan.delete(scanner)
|
537
|
+
invalidate.delete(scanner.collection)
|
538
|
+
|
539
|
+
# Give the diffs (from default) to scanner; these are elements that are
|
540
|
+
# dependent on this scanner
|
541
|
+
diffscan = (rescan - dflt_rescan).find_all {|elem| elem.class <= PushElement}
|
542
|
+
scanner.invalidate_at_tick(diffscan, (invalidate - dflt_invalidate).to_a)
|
522
543
|
end
|
523
544
|
@reset_list = to_reset.to_a
|
524
545
|
|
@@ -560,6 +581,12 @@ module Bud
|
|
560
581
|
|
561
582
|
# Given rescan, invalidate sets, compute transitive closure
|
562
583
|
def rescan_invalidate_tc(stratum, rescan, invalidate)
|
584
|
+
# XXX: hack. If there's nothing in the given stratum, don't do
|
585
|
+
# anything. This can arise if we have an orphan scanner whose input is a
|
586
|
+
# non-monotonic operator; the stratum(LHS) = stratum(RHS) + 1, but there's
|
587
|
+
# nothing else in stratum(LHS).
|
588
|
+
return if @push_sorted_elems[stratum].nil?
|
589
|
+
|
563
590
|
rescan_len = rescan.size
|
564
591
|
invalidate_len = invalidate.size
|
565
592
|
while true
|
@@ -576,6 +603,18 @@ module Bud
|
|
576
603
|
rescan.delete_if {|e| e.rescan_at_tick}
|
577
604
|
end
|
578
605
|
|
606
|
+
def each_scanner
|
607
|
+
@num_strata.times do |stratum|
|
608
|
+
@scanners[stratum].each_value do |scanner|
|
609
|
+
yield scanner, stratum
|
610
|
+
end
|
611
|
+
end
|
612
|
+
|
613
|
+
@orphan_scanners.each do |scanner,stratum|
|
614
|
+
yield scanner, stratum
|
615
|
+
end
|
616
|
+
end
|
617
|
+
|
579
618
|
def do_rewrite
|
580
619
|
@meta_parser = BudMeta.new(self, @declarations)
|
581
620
|
@stratified_rules = @meta_parser.meta_rewrite
|
@@ -1052,23 +1091,23 @@ module Bud
|
|
1052
1091
|
elem.invalidate_cache unless elem.class <= PushElement
|
1053
1092
|
}
|
1054
1093
|
|
1055
|
-
num_strata = @push_sorted_elems.size
|
1056
1094
|
# The following loop invalidates additional (non-default) elements and
|
1057
1095
|
# tables that depend on the run-time invalidation state of a table.
|
1058
1096
|
# Loop once to set the flags.
|
1059
|
-
|
1060
|
-
|
1061
|
-
|
1062
|
-
|
1063
|
-
|
1064
|
-
|
1065
|
-
|
1066
|
-
}
|
1067
|
-
end
|
1097
|
+
each_scanner do |scanner, stratum|
|
1098
|
+
if scanner.rescan
|
1099
|
+
scanner.rescan_set.each {|e| e.rescan = true}
|
1100
|
+
scanner.invalidate_set.each {|e|
|
1101
|
+
e.invalidated = true
|
1102
|
+
e.invalidate_cache unless e.class <= PushElement
|
1103
|
+
}
|
1068
1104
|
end
|
1069
1105
|
end
|
1070
|
-
|
1071
|
-
|
1106
|
+
|
1107
|
+
# Loop a second time to actually call invalidate_cache. We can't merge
|
1108
|
+
# this with the loops above because some versions of invalidate_cache
|
1109
|
+
# (e.g., join) depend on the rescan state of other elements.
|
1110
|
+
@num_strata.times do |stratum|
|
1072
1111
|
@push_sorted_elems[stratum].each {|e| e.invalidate_cache if e.invalidated}
|
1073
1112
|
end
|
1074
1113
|
end
|
@@ -1134,14 +1173,14 @@ module Bud
|
|
1134
1173
|
end
|
1135
1174
|
|
1136
1175
|
# Return the stratum number of the given collection.
|
1137
|
-
# NB: if a collection
|
1138
|
-
# assigned to a strata.
|
1176
|
+
# NB: if a collection does not appear on the lhs or rhs of any rules, it is
|
1177
|
+
# not currently assigned to a strata.
|
1139
1178
|
def collection_stratum(collection)
|
1140
1179
|
t_stratum.each do |t|
|
1141
1180
|
return t.stratum if t.predicate == collection
|
1142
1181
|
end
|
1143
1182
|
|
1144
|
-
|
1183
|
+
return nil
|
1145
1184
|
end
|
1146
1185
|
|
1147
1186
|
private
|