bud 0.9.4 → 0.9.5
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.
- data/History.txt +23 -0
- data/bin/budlabel +63 -0
- data/bin/budtimelines +1 -1
- data/docs/cheat.md +1 -1
- data/docs/getstarted.md +8 -8
- data/examples/chat/README.md +2 -0
- data/examples/chat/chat.rb +3 -2
- data/examples/chat/chat_protocol.rb +1 -1
- data/examples/chat/chat_server.rb +3 -2
- data/lib/bud/aggs.rb +16 -2
- data/lib/bud/bud_meta.rb +19 -28
- data/lib/bud/collections.rb +157 -39
- data/lib/bud/depanalysis.rb +3 -4
- data/lib/bud/executor/elements.rb +62 -57
- data/lib/bud/executor/group.rb +35 -32
- data/lib/bud/executor/join.rb +0 -11
- data/lib/bud/graphs.rb +1 -1
- data/lib/bud/labeling/bloomgraph.rb +47 -0
- data/lib/bud/labeling/budplot_style.rb +53 -0
- data/lib/bud/labeling/labeling.rb +288 -0
- data/lib/bud/lattice-core.rb +563 -0
- data/lib/bud/lattice-lib.rb +367 -0
- data/lib/bud/monkeypatch.rb +18 -8
- data/lib/bud/rewrite.rb +314 -139
- data/lib/bud/server.rb +13 -2
- data/lib/bud/source.rb +34 -18
- data/lib/bud/state.rb +90 -1
- data/lib/bud/storage/zookeeper.rb +38 -33
- data/lib/bud/viz.rb +0 -1
- data/lib/bud.rb +55 -15
- metadata +15 -8
data/lib/bud/server.rb
CHANGED
@@ -55,11 +55,22 @@ class Bud::BudServer < EM::Connection #:nodoc: all
|
|
55
55
|
end
|
56
56
|
|
57
57
|
def message_received(obj)
|
58
|
-
unless (obj.class <= Array and obj.length ==
|
59
|
-
@bud.tables.include?(obj[0].to_sym) and
|
58
|
+
unless (obj.class <= Array and obj.length == 3 and
|
59
|
+
@bud.tables.include?(obj[0].to_sym) and
|
60
|
+
obj[1].class <= Array and obj[2].class <= Array)
|
60
61
|
raise Bud::Error, "bad inbound message of class #{obj.class}: #{obj.inspect}"
|
61
62
|
end
|
62
63
|
|
64
|
+
# Deserialize any nested marshalled values
|
65
|
+
tbl_name, tuple, marshall_indexes = obj
|
66
|
+
marshall_indexes.each do |i|
|
67
|
+
if i < 0 || i >= tuple.length
|
68
|
+
raise Bud::Error, "bad inbound message: marshalled value at index #{i}, #{obj.inspect}"
|
69
|
+
end
|
70
|
+
tuple[i] = Marshal.load(tuple[i])
|
71
|
+
end
|
72
|
+
|
73
|
+
obj = [tbl_name, tuple]
|
63
74
|
@bud.rtracer.recv(obj) if @bud.options[:rtrace]
|
64
75
|
@filter_buf[obj[0].to_sym] ||= []
|
65
76
|
@filter_buf[obj[0].to_sym] << obj[1]
|
data/lib/bud/source.rb
CHANGED
@@ -1,6 +1,4 @@
|
|
1
1
|
require 'rubygems'
|
2
|
-
require 'ruby_parser'
|
3
|
-
require 'bud/errors'
|
4
2
|
|
5
3
|
module Source
|
6
4
|
$cached_file_info = Struct.new(:curr_file, :lines, :last_state_bloom_line).new
|
@@ -8,35 +6,53 @@ module Source
|
|
8
6
|
# Reads the block corresponding to the location (string of the form
|
9
7
|
# "file:line_num"). Returns an ast for the block.
|
10
8
|
def Source.read_block(location)
|
11
|
-
|
9
|
+
if location.start_with? '('
|
10
|
+
raise Bud::IllegalSourceError, "source must be present in a file; cannot read interactive shell or eval block"
|
11
|
+
end
|
12
12
|
location =~ /^(.*):(\d+)/
|
13
13
|
filename, num = $1, $2.to_i
|
14
|
-
|
14
|
+
if filename.nil?
|
15
|
+
raise Bud::IllegalSourceError, "couldn't determine filename from backtrace"
|
16
|
+
end
|
15
17
|
lines = cache(filename, num)
|
16
18
|
# Note: num is 1-based.
|
17
19
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
stmt = "" # collection of lines that form one complete ruby statement
|
22
|
-
endok = true #
|
20
|
+
parser = make_parser
|
21
|
+
stmt = "" # collection of lines that form one complete Ruby statement
|
23
22
|
ast = nil
|
24
23
|
lines[num .. -1].each do |l|
|
25
24
|
next if l =~ /^\s*#/
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
25
|
+
if l =~ /^\s*([}]|end)/
|
26
|
+
# We found some syntax that looks like it might terminate the Ruby
|
27
|
+
# statement. Hence, try to parse it; if we don't find a syntax error,
|
28
|
+
# we're done.
|
29
|
+
begin
|
30
|
+
ast = parser.parse stmt
|
31
|
+
break
|
32
|
+
rescue
|
33
|
+
ast = nil
|
34
|
+
end
|
35
35
|
end
|
36
|
+
stmt += l + "\n"
|
36
37
|
end
|
37
38
|
ast
|
38
39
|
end
|
39
40
|
|
41
|
+
# ruby_parser 3.x can produce either 1.8- or 1.9-compatible ASTs. Since we
|
42
|
+
# want to eventually turn the ASTs back into code we can eval() using the
|
43
|
+
# current Ruby runtime, we want to generate an appropriately compatible AST.
|
44
|
+
def Source.make_parser
|
45
|
+
maj, min, patch = RUBY_VERSION.split(".")
|
46
|
+
case [maj.to_i, min.to_i]
|
47
|
+
when [1, 9] then
|
48
|
+
Ruby19Parser.new
|
49
|
+
when [1, 8] then
|
50
|
+
Ruby18Parser.new
|
51
|
+
else
|
52
|
+
raise Bud::Error, "unrecognized RUBY_VERSION: #{RUBY_VERSION}"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
40
56
|
def Source.cache(filename, num) # returns array of lines
|
41
57
|
if $cached_file_info.curr_file == filename
|
42
58
|
retval = $cached_file_info.lines
|
data/lib/bud/state.rb
CHANGED
@@ -2,7 +2,7 @@ module Bud
|
|
2
2
|
######## methods for registering collection types
|
3
3
|
private
|
4
4
|
def check_collection_name(name)
|
5
|
-
if @tables.has_key? name
|
5
|
+
if @tables.has_key? name or @lattices.has_key? name
|
6
6
|
raise Bud::CompileError, "collection already exists: #{name}"
|
7
7
|
end
|
8
8
|
|
@@ -26,6 +26,18 @@ module Bud
|
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
|
+
def define_lattice(name)
|
30
|
+
check_collection_name(name)
|
31
|
+
|
32
|
+
self.singleton_class.send(:define_method, name) do |*args, &blk|
|
33
|
+
if blk.nil?
|
34
|
+
return @lattices[name]
|
35
|
+
else
|
36
|
+
return @lattices[name].pro(&blk)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
29
41
|
public
|
30
42
|
def input # :nodoc: all
|
31
43
|
true
|
@@ -140,4 +152,81 @@ module Bud
|
|
140
152
|
@tables[name] = Bud::BudTerminal.new(name, [:line], self)
|
141
153
|
@channels[name] = @tables[name]
|
142
154
|
end
|
155
|
+
|
156
|
+
# an alternative approach to declaring interfaces
|
157
|
+
def interfaces(direction, collections)
|
158
|
+
mode = case direction
|
159
|
+
when :input then true
|
160
|
+
when :output then false
|
161
|
+
else
|
162
|
+
raise Bud::CompileError, "unrecognized interface type #{direction}"
|
163
|
+
end
|
164
|
+
collections.each do |tab|
|
165
|
+
t_provides << [tab.to_s, mode]
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
# Define methods to implement the state declarations for every registered kind
|
170
|
+
# of lattice.
|
171
|
+
def load_lattice_defs
|
172
|
+
Bud::Lattice.global_mfuncs.each do |m|
|
173
|
+
next if RuleRewriter::MONOTONE_WHITELIST.include? m
|
174
|
+
if Bud::BudCollection.instance_methods.include? m.to_s
|
175
|
+
puts "monotone method #{m} conflicts with non-monotonic method in BudCollection"
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
Bud::Lattice.global_morphs.each do |m|
|
180
|
+
next if RuleRewriter::MONOTONE_WHITELIST.include? m
|
181
|
+
if Bud::BudCollection.instance_methods.include? m.to_s
|
182
|
+
puts "morphism #{m} conflicts with non-monotonic method in BudCollection"
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
# Sanity-check lattice definitions
|
187
|
+
# XXX: We should do this only once per lattice
|
188
|
+
Bud::Lattice.lattice_kinds.each do |wrap_name, klass|
|
189
|
+
unless klass.method_defined? :merge
|
190
|
+
raise Bud::CompileError, "lattice #{wrap_name} does not define a merge function"
|
191
|
+
end
|
192
|
+
|
193
|
+
# If a method is marked as monotone in any lattice, every lattice that
|
194
|
+
# declares a method of that name must also mark it as monotone.
|
195
|
+
meth_list = klass.instance_methods(false).to_set
|
196
|
+
Bud::Lattice.global_mfuncs.each do |m|
|
197
|
+
next unless meth_list.include? m.to_s
|
198
|
+
unless klass.mfuncs.include? m
|
199
|
+
raise Bud::CompileError, "method #{m} in #{wrap_name} must be monotone"
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
# Apply a similar check for morphs
|
204
|
+
Bud::Lattice.global_morphs.each do |m|
|
205
|
+
next unless meth_list.include? m.to_s
|
206
|
+
unless klass.morphs.include? m
|
207
|
+
raise Bud::CompileError, "method #{m} in #{wrap_name} must be a morph"
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
# Similarly, check for non-monotone lattice methods that are found in the
|
212
|
+
# builtin list of monotone operators. The "merge" method is implicitly
|
213
|
+
# monotone (XXX: should it be declared as a morph or monotone function?)
|
214
|
+
meth_list.each do |m_str|
|
215
|
+
m = m_str.to_sym
|
216
|
+
next unless RuleRewriter::MONOTONE_WHITELIST.include? m
|
217
|
+
# XXX: ugly hack. We want to allow lattice class implementations to
|
218
|
+
# define their own equality semantics.
|
219
|
+
next if m == :==
|
220
|
+
unless klass.mfuncs.include?(m) || klass.morphs.include?(m) || m == :merge
|
221
|
+
raise Bud::CompileError, "method #{m} in #{wrap_name} must be monotone"
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
# XXX: replace "self" with toplevel?
|
226
|
+
self.singleton_class.send(:define_method, wrap_name) do |lat_name|
|
227
|
+
define_lattice(lat_name)
|
228
|
+
@lattices[lat_name] = Bud::LatticeWrapper.new(lat_name, klass, self)
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
143
232
|
end
|
@@ -8,40 +8,63 @@ module Bud
|
|
8
8
|
# Persistent table implementation based on Zookeeper.
|
9
9
|
class BudZkTable < BudPersistentCollection # :nodoc: all
|
10
10
|
def initialize(name, zk_path, zk_addr, bud_instance)
|
11
|
-
unless defined? HAVE_ZOOKEEPER
|
11
|
+
unless defined? Bud::HAVE_ZOOKEEPER
|
12
12
|
raise Bud::Error, "zookeeper gem is not installed: zookeeper-backed stores cannot be used"
|
13
13
|
end
|
14
14
|
|
15
|
-
|
16
|
-
super(name, bud_instance, nil)
|
15
|
+
super(name, bud_instance, [:key] => [:val, :opts])
|
17
16
|
|
18
|
-
zk_path = zk_path.chomp("/") unless zk_path == "/"
|
19
17
|
@zk = Zookeeper.new(zk_addr)
|
18
|
+
zk_path = zk_path.chomp("/") unless zk_path == "/"
|
20
19
|
@zk_path = zk_path
|
21
20
|
@base_path = @zk_path
|
22
21
|
@base_path += "/" unless @zk_path.end_with? "/"
|
23
22
|
@store_mutex = Mutex.new
|
23
|
+
@zk_mutex = Mutex.new
|
24
24
|
@next_storage = {}
|
25
25
|
@saw_delta = false
|
26
26
|
@child_watch_id = nil
|
27
|
-
@stat_watch_id = nil
|
28
27
|
end
|
29
28
|
|
30
29
|
# Since the watcher callbacks might invoke EventMachine, we wait until after
|
31
30
|
# EM startup to start watching for Zk events.
|
32
31
|
def start_watchers
|
33
|
-
#
|
34
|
-
|
35
|
-
|
32
|
+
# Watcher callbacks are invoked in a separate Ruby thread. Note that there
|
33
|
+
# is a possible deadlock between invoking watcher callbacks and calling
|
34
|
+
# close(): if we get a watcher event and a close at around the same time,
|
35
|
+
# the close might fire first. Closing the Zk handle will block on
|
36
|
+
# dispatching outstanding watchers, but it does so holding the @zk_mutex,
|
37
|
+
# causing a deadlock. Hence, we just have the watcher callback spin on the
|
38
|
+
# @zk_mutex, aborting if the handle is ever closed.
|
39
|
+
@child_watcher = Zookeeper::Callbacks::WatcherCallback.new do
|
40
|
+
while true
|
41
|
+
break if @zk.closed?
|
42
|
+
if @zk_mutex.try_lock
|
43
|
+
get_and_watch unless @zk.closed?
|
44
|
+
@zk_mutex.unlock
|
45
|
+
break
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
@stat_watcher = Zookeeper::Callbacks::WatcherCallback.new do
|
51
|
+
while true
|
52
|
+
break if @zk.closed?
|
53
|
+
if @zk_mutex.try_lock
|
54
|
+
stat_and_watch unless @zk.closed?
|
55
|
+
@zk_mutex.unlock
|
56
|
+
break
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
36
61
|
stat_and_watch
|
37
62
|
end
|
38
63
|
|
39
64
|
def stat_and_watch
|
40
65
|
r = @zk.stat(:path => @zk_path, :watcher => @stat_watcher)
|
41
|
-
@stat_watch_id = r[:req_id]
|
42
66
|
|
43
67
|
unless r[:stat].exists
|
44
|
-
cancel_child_watch
|
45
68
|
# The given @zk_path doesn't exist, so try to create it. Unclear
|
46
69
|
# whether this is always the best behavior.
|
47
70
|
r = @zk.create(:path => @zk_path)
|
@@ -54,27 +77,10 @@ module Bud
|
|
54
77
|
get_and_watch unless @child_watch_id
|
55
78
|
end
|
56
79
|
|
57
|
-
def cancel_child_watch
|
58
|
-
if @child_watch_id
|
59
|
-
@zk.unregister_watcher(@child_watch_id)
|
60
|
-
@child_watch_id = nil
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
def cancel_stat_watch
|
65
|
-
if @stat_watch_id
|
66
|
-
@zk.unregister_watcher(@stat_watch_id)
|
67
|
-
@stat_with_id = nil
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
80
|
def get_and_watch
|
72
81
|
r = @zk.get_children(:path => @zk_path, :watcher => @child_watcher)
|
82
|
+
return unless r[:stat].exists
|
73
83
|
@child_watch_id = r[:req_id]
|
74
|
-
unless r[:stat].exists
|
75
|
-
cancel_child_watch
|
76
|
-
return
|
77
|
-
end
|
78
84
|
|
79
85
|
# XXX: can we easily get snapshot isolation?
|
80
86
|
new_children = {}
|
@@ -128,8 +134,8 @@ module Bud
|
|
128
134
|
ephemeral = false
|
129
135
|
sequence = false
|
130
136
|
|
131
|
-
|
132
|
-
|
137
|
+
opts = t.opts
|
138
|
+
unless opts.nil?
|
133
139
|
if opts[:ephemeral] == true
|
134
140
|
ephemeral = true
|
135
141
|
end
|
@@ -150,9 +156,8 @@ module Bud
|
|
150
156
|
end
|
151
157
|
|
152
158
|
def close
|
153
|
-
|
154
|
-
|
155
|
-
@zk.close
|
159
|
+
# See notes in start_watchers.
|
160
|
+
@zk_mutex.synchronize { @zk.close }
|
156
161
|
end
|
157
162
|
|
158
163
|
superator "<~" do |o|
|
data/lib/bud/viz.rb
CHANGED
data/lib/bud.rb
CHANGED
@@ -1,16 +1,27 @@
|
|
1
1
|
require 'rubygems'
|
2
|
+
gem 'ruby2ruby', '>= 2.0.1'
|
3
|
+
gem 'ruby_parser', '>= 3.0.2'
|
4
|
+
|
2
5
|
require 'eventmachine'
|
3
6
|
require 'msgpack'
|
7
|
+
require 'ruby2ruby'
|
8
|
+
require 'ruby_parser'
|
9
|
+
require 'set'
|
4
10
|
require 'socket'
|
5
11
|
require 'superators19'
|
6
12
|
require 'thread'
|
7
|
-
require 'bud/errors'
|
8
13
|
|
14
|
+
require 'bud/errors'
|
9
15
|
require 'bud/monkeypatch'
|
10
16
|
|
11
17
|
require 'bud/aggs'
|
12
18
|
require 'bud/bud_meta'
|
13
19
|
require 'bud/collections'
|
20
|
+
require 'bud/executor/elements.rb'
|
21
|
+
require 'bud/executor/group.rb'
|
22
|
+
require 'bud/executor/join.rb'
|
23
|
+
require 'bud/lattice-core'
|
24
|
+
require 'bud/lattice-lib'
|
14
25
|
require 'bud/metrics'
|
15
26
|
require 'bud/rtrace'
|
16
27
|
require 'bud/server'
|
@@ -19,10 +30,6 @@ require 'bud/storage/dbm'
|
|
19
30
|
require 'bud/storage/zookeeper'
|
20
31
|
require 'bud/viz'
|
21
32
|
|
22
|
-
require 'bud/executor/elements.rb'
|
23
|
-
require 'bud/executor/group.rb'
|
24
|
-
require 'bud/executor/join.rb'
|
25
|
-
|
26
33
|
ILLEGAL_INSTANCE_ID = -1
|
27
34
|
SIGNAL_CHECK_PERIOD = 0.2
|
28
35
|
|
@@ -61,7 +68,7 @@ $bud_instances = {} # Map from instance id => Bud instance
|
|
61
68
|
# :main: Bud
|
62
69
|
module Bud
|
63
70
|
attr_reader :budtime, :inbound, :options, :meta_parser, :viz, :rtracer, :dsock
|
64
|
-
attr_reader :tables, :builtin_tables, :channels, :zk_tables, :dbm_tables, :app_tables
|
71
|
+
attr_reader :tables, :builtin_tables, :channels, :zk_tables, :dbm_tables, :app_tables, :lattices
|
65
72
|
attr_reader :push_sources, :push_elems, :push_joins, :scanners, :merge_targets
|
66
73
|
attr_reader :this_stratum, :this_rule, :rule_orig_src, :done_bootstrap
|
67
74
|
attr_accessor :stratified_rules
|
@@ -109,6 +116,7 @@ module Bud
|
|
109
116
|
options[:print_wiring] ||= ENV["BUD_PRINT_WIRING"].to_i > 0
|
110
117
|
@qualified_name = ""
|
111
118
|
@tables = {}
|
119
|
+
@lattices = {}
|
112
120
|
@channels = {}
|
113
121
|
@dbm_tables = {}
|
114
122
|
@zk_tables = {}
|
@@ -144,6 +152,7 @@ module Bud
|
|
144
152
|
# NB: If using an ephemeral port (specified by port = 0), the actual port
|
145
153
|
# number won't be known until we start EM
|
146
154
|
|
155
|
+
load_lattice_defs
|
147
156
|
builtin_state
|
148
157
|
resolve_imports
|
149
158
|
call_state_methods
|
@@ -248,6 +257,11 @@ module Bud
|
|
248
257
|
tables[qname.to_sym] = t
|
249
258
|
end
|
250
259
|
end
|
260
|
+
mod_inst.lattices.each_pair do |name, t|
|
261
|
+
qname = "#{local_name}.#{name}".to_sym
|
262
|
+
raise Bud::Error if lattices.has_key? qname
|
263
|
+
lattices[qname] = t
|
264
|
+
end
|
251
265
|
mod_inst.t_rules.each do |imp_rule|
|
252
266
|
qname = "#{local_name}.#{imp_rule.lhs}"
|
253
267
|
self.t_rules << [imp_rule.bud_obj, imp_rule.rule_id, qname, imp_rule.op,
|
@@ -257,7 +271,7 @@ module Bud
|
|
257
271
|
qlname = "#{local_name}.#{imp_dep.lhs}"
|
258
272
|
qrname = "#{local_name}.#{imp_dep.body}"
|
259
273
|
self.t_depends << [imp_dep.bud_obj, imp_dep.rule_id, qlname,
|
260
|
-
imp_dep.op, qrname, imp_dep.nm]
|
274
|
+
imp_dep.op, qrname, imp_dep.nm, imp_dep.in_body]
|
261
275
|
end
|
262
276
|
mod_inst.t_provides.each do |imp_pro|
|
263
277
|
qintname = "#{local_name}.#{imp_pro.interface}"
|
@@ -309,9 +323,10 @@ module Bud
|
|
309
323
|
@stratified_rules.each_with_index { |rules, stratum| eval_rules(rules, stratum) }
|
310
324
|
|
311
325
|
# Prepare list of tables that will be actively used at run time. First, all
|
312
|
-
# the user-defined
|
313
|
-
# an array later.
|
326
|
+
# the user-defined tables and lattices. We start @app_tables off as a set,
|
327
|
+
# then convert to an array later.
|
314
328
|
@app_tables = (@tables.keys - @builtin_tables.keys).map {|t| @tables[t]}.to_set
|
329
|
+
@app_tables += @lattices.values
|
315
330
|
|
316
331
|
# Check scan and merge_targets to see if any builtin_tables need to be added as well.
|
317
332
|
@scanners.each do |scs|
|
@@ -335,7 +350,7 @@ module Bud
|
|
335
350
|
wired_to = []
|
336
351
|
working.each do |e|
|
337
352
|
e.wirings.each do |out|
|
338
|
-
if (out.class <= PushElement and not seen.member?(out))
|
353
|
+
if ((out.class <= PushElement || out.class <= LatticePushElement) and not seen.member?(out))
|
339
354
|
seen << out
|
340
355
|
wired_to << out
|
341
356
|
end
|
@@ -419,7 +434,7 @@ module Bud
|
|
419
434
|
def prepare_invalidation_scheme
|
420
435
|
num_strata = @push_sorted_elems.size
|
421
436
|
if $BUD_SAFE
|
422
|
-
@app_tables = @tables.values # No
|
437
|
+
@app_tables = @tables.values + @lattices.values # No collections excluded
|
423
438
|
|
424
439
|
rescan = Set.new
|
425
440
|
invalidate = @app_tables.select {|t| t.class <= BudScratch}.to_set
|
@@ -445,7 +460,10 @@ module Bud
|
|
445
460
|
nm_targets = Set.new
|
446
461
|
t_rules.each do |rule|
|
447
462
|
lhs = rule.lhs.to_sym
|
448
|
-
|
463
|
+
if rule.op == "<="
|
464
|
+
# Note that lattices cannot be sources
|
465
|
+
@tables[lhs].is_source = false if @tables.has_key? lhs
|
466
|
+
end
|
449
467
|
nm_targets << lhs if rule.nm_funcs_called
|
450
468
|
end
|
451
469
|
|
@@ -459,7 +477,7 @@ module Bud
|
|
459
477
|
@push_sorted_elems[stratum].each do |elem|
|
460
478
|
rescan << elem if elem.rescan_at_tick
|
461
479
|
|
462
|
-
if elem.outputs.any?{|tab| not(tab.class <= PushElement) and nm_targets.member? tab.qualified_tabname.to_sym }
|
480
|
+
if elem.outputs.any?{|tab| not(tab.class <= PushElement) and not(tab.class <= LatticePushElement) and nm_targets.member? tab.qualified_tabname.to_sym }
|
463
481
|
rescan.merge(elem.wired_by)
|
464
482
|
end
|
465
483
|
end
|
@@ -499,6 +517,25 @@ module Bud
|
|
499
517
|
end
|
500
518
|
end
|
501
519
|
@reset_list = to_reset.to_a
|
520
|
+
|
521
|
+
# For each lattice, find the set of tables that should be rescanned when
|
522
|
+
# there is a new delta for the lattice. That is, if we have a rule like:
|
523
|
+
# "t2 <= t1 {|t| [t.key, lat_foo]}", whenever there is a delta on lat_foo we
|
524
|
+
# should rescan t1 (to produce tuples with the updated lat_foo value).
|
525
|
+
# TODO:
|
526
|
+
# (1) support non-join ops to be rescanned (+ tests) + lambdas
|
527
|
+
# (2) if t1 is fed by rules r1 and r2 but only r1 references lattice x,
|
528
|
+
# don't trigger rescan of r2 on deltas for x (hard)
|
529
|
+
t_depends.each do |dep|
|
530
|
+
src, dst = dep.body.to_sym, dep.lhs.to_sym
|
531
|
+
if @lattices.has_key? src and @tables.has_key? dst and dep.in_body
|
532
|
+
src_lat = @lattices[src]
|
533
|
+
dst_tbl = @tables[dst]
|
534
|
+
dst_tbl.non_temporal_predecessors.each do |e|
|
535
|
+
src_lat.rescan_on_merge << e
|
536
|
+
end
|
537
|
+
end
|
538
|
+
end
|
502
539
|
end
|
503
540
|
|
504
541
|
# given rescan, invalidate sets, compute transitive closure
|
@@ -962,7 +999,10 @@ module Bud
|
|
962
999
|
end
|
963
1000
|
bootstrap
|
964
1001
|
|
965
|
-
|
1002
|
+
if toplevel == self
|
1003
|
+
@tables.each_value {|t| t.bootstrap}
|
1004
|
+
@lattices.each_value {|l| l.bootstrap}
|
1005
|
+
end
|
966
1006
|
@done_bootstrap = true
|
967
1007
|
end
|
968
1008
|
|
@@ -1100,7 +1140,7 @@ module Bud
|
|
1100
1140
|
|
1101
1141
|
# for BUD reflection
|
1102
1142
|
table :t_rules, [:bud_obj, :rule_id] => [:lhs, :op, :src, :orig_src, :nm_funcs_called]
|
1103
|
-
table :t_depends, [:bud_obj, :rule_id, :lhs, :op, :body] => [:nm]
|
1143
|
+
table :t_depends, [:bud_obj, :rule_id, :lhs, :op, :body] => [:nm, :in_body]
|
1104
1144
|
table :t_provides, [:interface] => [:input]
|
1105
1145
|
table :t_underspecified, t_provides.schema
|
1106
1146
|
table :t_stratum, [:predicate] => [:stratum]
|