bud 0.9.4 → 0.9.5
Sign up to get free protection for your applications and to get access to all the features.
- 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]
|