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.
@@ -0,0 +1,53 @@
1
+ require 'bud/labeling/labeling'
2
+
3
+ module PDG
4
+ include GuardedAsync
5
+ # a bloomgraph program that plots a NM-and-async-aware PDG
6
+ state do
7
+ scratch :bodies, [:table] => [:tbl_type]
8
+ scratch :source, [:pred]
9
+ scratch :sink, [:pred]
10
+ end
11
+
12
+ bloom do
13
+ bodies <= dep{|d| [d.body, coll_type(d.body)]}
14
+ bodies <= dep{|d| [d.head, coll_type(d.head)]}
15
+
16
+ bnode <= bodies do |b|
17
+ shape = case b.tbl_type
18
+ when Bud::BudTable then "rectangle"
19
+ when Bud::LatticeWrapper then "triangle"
20
+ else "oval"
21
+ end
22
+ [b.table, {:shape => shape}]
23
+ end
24
+
25
+ bedge <= dep do |d|
26
+ line = d.label == "A" ? "dashed" : "solid"
27
+ circle = d.label == "N" ? "veeodot" : "normal"
28
+ [d.body, d.head, {:style => line, :arrowhead => circle, :penwidth => 4}]
29
+ end
30
+ end
31
+
32
+ bloom :endpoints do
33
+ source <= t_provides do |p|
34
+ if p.input and !dep_tc.map{|d| d.head}.include? p.interface
35
+ [p.interface]
36
+ end
37
+ end
38
+
39
+ sink <= t_provides do |p|
40
+ if !p.input and !dep_tc.map{|d| d.body}.include? p.interface
41
+ [p.interface]
42
+ end
43
+ end
44
+
45
+ bedge <= source{|s| ["S", s.pred, {}]}
46
+ bedge <= sink{|s| [s.pred, "T", {}]}
47
+ end
48
+
49
+ bootstrap do
50
+ bnode << ["S", {:shape => "diamond", :color => "blue"}]
51
+ bnode << ["T", {:shape => "diamond", :color => "blue"}]
52
+ end
53
+ end
@@ -0,0 +1,288 @@
1
+ require 'rubygems'
2
+ require 'bud'
3
+
4
+ module Validate
5
+ state do
6
+ scratch :dep, [:body, :head, :label]
7
+ scratch :dep_tc, [:body, :head, :members]
8
+ scratch :scc, [:pred, :cluster]
9
+ scratch :scc_raw, scc.schema
10
+ scratch :new_dep, [:body, :head, :label]
11
+ scratch :labeled_path, [:body, :head, :path, :label]
12
+ scratch :full_path, labeled_path.schema
13
+ scratch :ndn, new_dep.schema
14
+ scratch :iinterface, t_provides.schema
15
+ scratch :ointerface, t_provides.schema
16
+ scratch :iin, t_provides.schema
17
+ scratch :iout, t_provides.schema
18
+ end
19
+
20
+ bloom do
21
+ dep <= t_depends do |d|
22
+ [d.body, d.lhs, labelof(d.op, d.nm)]
23
+ end
24
+
25
+ dep_tc <= dep do |d|
26
+ [d.body, d.head, Set.new([d.body, d.head])]
27
+ end
28
+ dep_tc <= (dep * dep_tc).pairs(:head => :body) do |d, t|
29
+ [d.body, t.head, t.members | [d.head]]
30
+ end
31
+
32
+ scc_raw <= dep_tc do |d|
33
+ if d.head == d.body
34
+ [d.head, d.members.to_a.sort]
35
+ end
36
+ end
37
+
38
+ scc <= scc_raw.reduce(Hash.new) do |memo, i|
39
+ memo[i.pred] ||= []
40
+ memo[i.pred] |= i.cluster
41
+ memo
42
+ end
43
+
44
+ new_dep <= (dep * scc * scc).combos do |d, s1, s2|
45
+ if d.head == s1.pred and d.body == s2.pred
46
+ ["#{s2.cluster.join(",")}_IN", "#{s1.cluster.join(",")}_OUT", d.label]
47
+ end
48
+ end
49
+ new_dep <= (dep * scc).pairs(:body => :pred) do |d, s|
50
+ ["#{s.cluster.join(",")}_OUT", d.head, d.label] unless s.cluster.include? d.head
51
+ end
52
+ new_dep <= (dep * scc).pairs(:head => :pred) do |d, s|
53
+ [d.body, "#{s.cluster.join(",")}_IN", d.label] unless s.cluster.include? d.body
54
+ end
55
+
56
+ ndn <= dep.notin(scc, :body => :pred)
57
+ new_dep <= ndn.notin(scc, :head => :pred)
58
+ end
59
+
60
+ bloom :channel_inputs do
61
+ temp :dummy_input <= t_provides do |p|
62
+ if p.input and coll_type(p.interface) == Bud::BudChannel
63
+ [p.interface]
64
+ end
65
+ end
66
+ dep <= dummy_input{|i| ["#{i.first}_INPUT", i.first, "A"]}
67
+ dep <= dummy_input{|i| ["#{i.first}_INPUT", i.first, "A"]}
68
+ t_provides <= dummy_input{|i| ["#{i.first}_INPUT", true]}
69
+ end
70
+
71
+ bloom :full_paths do
72
+ iin <= t_provides{|p| p if p.input}
73
+ iout <= t_provides{|p| p if !p.input}
74
+ iinterface <= iin.notin(new_dep, :interface => :head)
75
+ ointerface <= iout.notin(new_dep, :interface => :body)
76
+
77
+ labeled_path <= (new_dep * iinterface).pairs(:body => :interface) do |d, p|
78
+ [d.body, d.head, [d.body, d.head], [d.label]]
79
+ end
80
+ labeled_path <= (labeled_path * new_dep).pairs(:head => :body) do |p, d|
81
+ [p.body, d.head, p.path + [d.head], p.label + [d.label]]
82
+ end
83
+
84
+ full_path <= (labeled_path * ointerface).lefts(:head => :interface)
85
+ end
86
+
87
+ def validate
88
+ dp = Set.new
89
+ divergent_preds.each do |p|
90
+ dp.add(p.coll)
91
+ end
92
+ report = []
93
+ full_path.to_a.each do |p|
94
+ state = ["Bot"]
95
+ start_a = -1
96
+ p.label.each_with_index do |lbl, i|
97
+ if lbl == "A"
98
+ start_a = i + 1
99
+ end
100
+ os = state.first
101
+ state = do_collapse(state, [lbl])
102
+ end
103
+ if dp.include? p.head
104
+ report << (p.to_a + [:unguarded, ["D"]])
105
+ else
106
+ report << (p.to_a + [:path, state])
107
+ end
108
+ end
109
+ return report
110
+ end
111
+
112
+ def do_collapse(left, right)
113
+ l = left.pop
114
+ r = right.shift
115
+ left + collapse(l, r) + right
116
+ end
117
+
118
+ def labelof(op, nm)
119
+ if op == "<~"
120
+ "A"
121
+ elsif nm
122
+ "N"
123
+ else
124
+ "Bot"
125
+ end
126
+ end
127
+
128
+ def collapse(left, right)
129
+ return [right] if left == 'Bot'
130
+ return [left] if right == 'Bot'
131
+ return [left] if left == right
132
+ return ['D'] if left == 'D' or right == 'D'
133
+ # CALM
134
+ return ['D'] if left == 'A' and right =~ /N/
135
+ # sometimes we cannot reduce
136
+ return [left, right]
137
+ end
138
+ end
139
+
140
+
141
+ module GuardedAsync
142
+ include Validate
143
+ state do
144
+ scratch :meet, [:chan, :partner, :at, :lpath, :rpath]
145
+ scratch :meet_stg, meet.schema
146
+ scratch :channel_race, [:chan, :partner, :to, :guarded]
147
+ scratch :dep_tc_type, [:body, :head, :types]
148
+ scratch :divergent_preds, [:coll]
149
+ end
150
+
151
+ bloom do
152
+ dep_tc_type <= dep do |d|
153
+ btab = coll_type(d.body)
154
+ htab = coll_type(d.head)
155
+ [d.body, d.head, Set.new([btab, htab])]
156
+ end
157
+ dep_tc_type <= (dep * dep_tc_type).pairs(:head => :body) do |d, t|
158
+ htab = coll_type(d.head)
159
+ [d.body, t.head, t.types | [htab]]
160
+ end
161
+
162
+ meet_stg <= (dep_tc_type * dep_tc_type).pairs(:head => :head) do |l, r|
163
+ ltab = self.tables[l.body.to_sym]
164
+ rtab = self.tables[r.body.to_sym]
165
+ if ltab.class == Bud::BudChannel and rtab.class == Bud::BudChannel and l.body != r.body
166
+ [l.body, r.body, l.head, l.types, r.types]
167
+ end
168
+ end
169
+
170
+ meet <= meet_stg.notin(dep_tc_type, :chan => :body, :partner => :head)
171
+ channel_race <= meet{|m| [m.chan, m.partner, m.at, guarded(m.lpath, m.rpath)]}
172
+ divergent_preds <= channel_race{|r| [r.to] unless r.guarded}
173
+ divergent_preds <= (channel_race * dep_tc_type).pairs(:to => :body){|r, t| [t.head] unless r.guarded}
174
+ end
175
+
176
+ def coll_type(nm)
177
+ tab = self.tables[nm.to_sym]
178
+ if tab.nil?
179
+ tab = self.lattices[nm.to_sym]
180
+ end
181
+ tab.class
182
+ end
183
+
184
+ def guarded(lpath, rpath)
185
+ if lpath.include? Bud::BudTable or lpath.include? Bud::LatticeWrapper
186
+ if rpath.include? Bud::BudTable or rpath.include? Bud::LatticeWrapper
187
+ return true
188
+ end
189
+ end
190
+ false
191
+ end
192
+ end
193
+
194
+ require 'bud/labeling/bloomgraph'
195
+ require 'bud/labeling/budplot_style'
196
+
197
+ module MetaMods
198
+ include Validate
199
+ include GuardedAsync
200
+ include BloomGraph
201
+ include PDG
202
+ end
203
+
204
+ class Label
205
+ attr_reader :f
206
+
207
+ def initialize(mod)
208
+ @report = nil
209
+ @mod = Object.const_get(mod)
210
+ if @mod.class == Class
211
+ nc = new_class_from_class(@mod)
212
+ elsif @mod.class == Module
213
+ nc = new_class(@mod)
214
+ else
215
+ raise "#{mod} neither class nor module"
216
+ end
217
+ @f = nc.new
218
+ @f.tick
219
+ end
220
+
221
+ def validate
222
+ @report = @f.validate if @report.nil?
223
+ end
224
+
225
+ def output_report
226
+ validate
227
+ rep = {}
228
+ @report.each do |from, to, path, labels, reason, final|
229
+ rep[to] ||= "Bot"
230
+ rep[to] = disjunction(rep[to], final.last)
231
+ end
232
+ rep
233
+ end
234
+
235
+ def path_report
236
+ validate
237
+ zips = {}
238
+ @report.each do |from, to, path, labels, reason, final|
239
+ zips[to] ||= {}
240
+ zips[to][from] ||= "Bot"
241
+ zips[to][from] = disjunction(zips[to][from], final.last)
242
+ end
243
+ zips
244
+ end
245
+
246
+ def disjunction(l, r)
247
+ both = [l, r]
248
+ if both.include? "D"
249
+ "D"
250
+ elsif both.include? "N"
251
+ if both.include? "A"
252
+ return "D"
253
+ else
254
+ return "N"
255
+ end
256
+ elsif both.include? "A"
257
+ return "A"
258
+ else
259
+ return "Bot"
260
+ end
261
+ end
262
+
263
+ def new_class(mod)
264
+ Class.new do
265
+ include Bud
266
+ include MetaMods
267
+ include mod
268
+ end
269
+ end
270
+
271
+ def new_class_from_class(cls)
272
+ Class.new(cls) do
273
+ include MetaMods
274
+ end
275
+ end
276
+
277
+ def internal_tabs
278
+ cls = Class.new do
279
+ include Bud
280
+ include MetaMods
281
+ end
282
+ cls.new.tables.keys
283
+ end
284
+
285
+ def write_graph(fmt=:pdf)
286
+ f.finish(internal_tabs, "#{@mod.to_s}.#{fmt}", fmt)
287
+ end
288
+ end
@@ -0,0 +1,563 @@
1
+ require 'bud/executor/elements'
2
+
3
+ class Bud::Lattice
4
+ @@lattice_kinds = {}
5
+ @@global_morphs = Set.new
6
+ @@global_mfuncs = Set.new
7
+
8
+ def self.wrapper_name(name)
9
+ if @wrapper_name
10
+ raise Bud::CompileError, "lattice #{self.name} has multiple wrapper names"
11
+ end
12
+ if @@lattice_kinds.has_key? name
13
+ raise Bud::CompileError, "duplicate lattice definition: #{name}"
14
+ end
15
+ @@lattice_kinds[name] = self
16
+ @wrapper_name = name
17
+ end
18
+
19
+ def self.lattice_kinds
20
+ @@lattice_kinds
21
+ end
22
+
23
+ def self.wrapper
24
+ @wrapper_name
25
+ end
26
+
27
+ def self.morph(name, &block)
28
+ if mfuncs.include?(name) || @@global_mfuncs.include?(name)
29
+ raise Bud::CompileError, "#{name} declared as both monotone and morph"
30
+ end
31
+ @morphs ||= Set.new
32
+ @morphs << name
33
+ @@global_morphs << name
34
+ define_method(name, &block)
35
+ end
36
+
37
+ def self.morphs
38
+ @morphs || Set.new
39
+ end
40
+
41
+ def self.global_morphs
42
+ @@global_morphs
43
+ end
44
+
45
+ def self.monotone(name, &block)
46
+ if morphs.include?(name) || @@global_morphs.include?(name)
47
+ raise Bud::CompileError, "#{name} declared as both monotone and morph"
48
+ end
49
+ @mfuncs ||= Set.new
50
+ @mfuncs << name
51
+ @@global_mfuncs << name
52
+ define_method(name, &block)
53
+ end
54
+
55
+ def self.mfuncs
56
+ @mfuncs || Set.new
57
+ end
58
+
59
+ def self.global_mfuncs
60
+ @@global_mfuncs
61
+ end
62
+
63
+ def reject_input(i, meth="initialize")
64
+ site = "#{self.class.wrapper}\##{meth}"
65
+ raise Bud::TypeError, "illegal input to #{site}: #{i.inspect}"
66
+ end
67
+
68
+ # The default equality semantics for lattice objects is based on reveal. Note
69
+ # that this isn't always appropriate: if the intended equality semantics for
70
+ # the lattice type differ from the equality semantics of the object returned
71
+ # by reveal (e.g., a set lattice might return an array with an unpredictable
72
+ # order), the lattice type should override this behavior.
73
+ def ==(o)
74
+ return false unless o.kind_of? Bud::Lattice
75
+ return reveal == o.reveal
76
+ end
77
+
78
+ def eql?(o)
79
+ return self == o
80
+ end
81
+
82
+ # Ensure hashing and equality semantics are consistent.
83
+ def hash
84
+ reveal.hash
85
+ end
86
+
87
+ # Return the state valued associated with a lattice instance. Note that this
88
+ # is non-monotonic when invoked from user code; it should be used with care by
89
+ # framework code.
90
+ def reveal
91
+ @v
92
+ end
93
+
94
+ def inspect
95
+ "<#{self.class.wrapper}: #{reveal.inspect}>"
96
+ end
97
+
98
+ # Construct a new instance of the current class that wraps "new_v". We assume
99
+ # that new_v is already a legal input value for the class, so we can bypass
100
+ # the class's normal initializer -- this avoids redundant error checks.
101
+ def wrap_unsafe(new_v)
102
+ rv = self.class.new
103
+ rv.instance_variable_set('@v', new_v)
104
+ rv
105
+ end
106
+ end
107
+
108
+ # TODO:
109
+ # * merge logic for set-oriented collections
110
+ # * invalidation/rescan/non-monotonic stuff?
111
+ # * expressions on RHS ("CollExpr")
112
+ class Bud::LatticePushElement
113
+ attr_reader :wired_by, :outputs
114
+ attr_accessor :invalidated, :rescan
115
+
116
+ def initialize(bud_instance)
117
+ @bud_instance = bud_instance
118
+ @wired_by = []
119
+ @outputs = []
120
+ @pendings = []
121
+ @invalidated = true
122
+ @rescan = true
123
+ end
124
+
125
+ def wire_to(element, kind=:output)
126
+ case kind
127
+ when :output
128
+ @outputs << element
129
+ when :pending
130
+ @pendings << element
131
+ else
132
+ raise Bud::Error, "unrecognized wiring kind: #{kind}"
133
+ end
134
+
135
+ element.wired_by << self
136
+ end
137
+
138
+ def check_wiring
139
+ if @outputs.empty? and @pendings.empty?
140
+ raise Bud::Error, "no output specified for #{inspect}"
141
+ end
142
+ end
143
+
144
+ def print_wiring(depth=0, accum="")
145
+ puts "#{' ' * depth}#{accum} #{inspect}"
146
+
147
+ [@outputs, @pendings].each do |buf|
148
+ if buf == @outputs
149
+ next_accum = "=> "
150
+ else
151
+ next_accum = "+> "
152
+ end
153
+
154
+ buf.each do |o|
155
+ if o.respond_to? :print_wiring
156
+ o.print_wiring(depth + 1, next_accum)
157
+ else
158
+ puts "#{' ' * (depth + 1)}#{next_accum} #{o.inspect}"
159
+ end
160
+ end
161
+ end
162
+ end
163
+
164
+ def inspect
165
+ "#{self.class}:#{self.object_id.to_s(16)}"
166
+ end
167
+
168
+ def wirings
169
+ @outputs + @pendings
170
+ end
171
+
172
+ def method_missing(meth, *args, &blk)
173
+ if @bud_instance.wiring?
174
+ Bud::PushApplyMethod.new(@bud_instance, self, meth, args, blk)
175
+ else
176
+ super
177
+ end
178
+ end
179
+
180
+ # Push-based dataflow
181
+ def insert(v, source)
182
+ push_out(v)
183
+ end
184
+
185
+ def push_out(v)
186
+ @outputs.each do |o|
187
+ # If we're emitting outputs to a traditional Bloom collection, merge
188
+ # operators (e.g., <=, <+) take a collection of tuples, so we need to
189
+ # convert the lattice value into a collection of tuple-like values. For
190
+ # now, we hardcode a single way to do this: we simply assume the value
191
+ # embedded inside the lattice is Enumerable. We also allow lattice
192
+ # morphisms to just produce Enumerable values directly, so we don't call
193
+ # reveal in that case.
194
+ # XXX: rethink this.
195
+ if o.class <= Bud::BudCollection
196
+ o <= (v.class <= Bud::Lattice ? v.reveal : v)
197
+ else
198
+ o.insert(v, self)
199
+ end
200
+ end
201
+ @pendings.each do |o|
202
+ if o.class <= Bud::BudCollection
203
+ o.pending_merge(v.class <= Bud::Lattice ? v.reveal : v)
204
+ else
205
+ o <+ v
206
+ end
207
+ end
208
+ end
209
+
210
+ def flush
211
+ end
212
+
213
+ def stratum_end
214
+ end
215
+
216
+ # Rescan and invalidation
217
+ def add_rescan_invalidate(rescan, invalidate)
218
+ end
219
+
220
+ def invalidate_at_tick(rescan, invalidate)
221
+ end
222
+
223
+ def invalidate_cache
224
+ end
225
+
226
+ # Tick (delta processing)
227
+ def tick
228
+ end
229
+
230
+ def tick_deltas
231
+ end
232
+
233
+ def rescan_at_tick
234
+ false
235
+ end
236
+ end
237
+
238
+ # A push-based dataflow element that scans a lattice wrapper
239
+ class Bud::LatticeScanner < Bud::LatticePushElement
240
+ attr_reader :collection, :rescan_set, :invalidate_set
241
+
242
+ def initialize(bud_instance, collection)
243
+ super(bud_instance)
244
+ @collection = collection
245
+ @rescan_set = []
246
+ @invalidate_set = []
247
+ end
248
+
249
+ def scan(first_iter)
250
+ if first_iter || @bud_instance.options[:disable_lattice_semi_naive]
251
+ push_out(@collection.current_value)
252
+ else
253
+ push_out(@collection.current_delta)
254
+ end
255
+ end
256
+
257
+ def inspect
258
+ "#{super} [#{collection.qualified_tabname}]"
259
+ end
260
+ end
261
+
262
+ class Bud::LatticeWrapper; end
263
+
264
+ # A push-based dataflow element that applies a method to a lattice value
265
+ class Bud::PushApplyMethod < Bud::LatticePushElement
266
+ SOURCE_TYPES = [Bud::LatticeWrapper, Bud::BudCollection,
267
+ Bud::LatticePushElement, Bud::PushElement]
268
+
269
+ def initialize(bud_instance, recv, meth, args, blk)
270
+ super(bud_instance)
271
+ @recv = recv
272
+ @meth = meth
273
+ @blk = blk
274
+ @args = args.dup
275
+ @is_morph = Bud::Lattice.global_morphs.include? @meth
276
+ @recv_is_scanner = @recv.kind_of? Bud::LatticeScanner
277
+
278
+ recv.wire_to(self, :output)
279
+ bud_instance.push_elems[[self.object_id, recv, meth, blk]] = self
280
+
281
+ # Arguments that are normal Ruby values are assumed to remain invariant as
282
+ # rule evaluation progresses; hence, we just pass along those values when
283
+ # invoking the function. Arguments that are derived from lattices or
284
+ # collections might change; hence, we need to wire up the push dataflow to
285
+ # have the current values of the function's arguments passed to this node.
286
+
287
+ # Map from input node to a list of indexes; the indexes identify the
288
+ # positions in the args array that should be filled with the node's value
289
+ @input_sources = {}
290
+
291
+ # Similarly, map from input node to a cached value -- this is the last value
292
+ # we've seen from this input. If the input gave us a delta, we merge
293
+ # together all the deltas we've seen and cache the resulting value. XXX: In
294
+ # the common case that the input is a scanner over a lattice wrapper, this
295
+ # means we do redundant work merging together deltas.
296
+ @input_caches = {}
297
+
298
+ # Inputs for which we haven't seen a value yet.
299
+ @waiting_for_input = Set.new
300
+ @recv_cache = nil
301
+ @seen_recv = false
302
+
303
+ @args.each_with_index do |a, i|
304
+ if SOURCE_TYPES.any?{|s| a.kind_of? s}
305
+ if a.kind_of? Bud::LatticeWrapper
306
+ a = a.to_push_elem
307
+ end
308
+ a.wire_to(self, :output)
309
+ @input_sources[a] ||= []
310
+ @input_sources[a] << i
311
+ @waiting_for_input << a
312
+ @args[i] = nil # Substitute actual value before calling method
313
+ end
314
+ end
315
+
316
+ @seen_all_inputs = @waiting_for_input.empty?
317
+ end
318
+
319
+ def insert(v, source)
320
+ if source == @recv
321
+ if @seen_recv
322
+ # Update the cached value for the method receiver. Note that if we're
323
+ # applying a method directly to a LatticeScanner (i.e., method applied
324
+ # to lattice wrapper), we can avoid maintaining a separate cache and
325
+ # instead use the wrapper's current value.
326
+ if @recv_is_scanner
327
+ @recv_cache = @recv.collection.current_value
328
+ else
329
+ @recv_cache = @recv_cache.merge(v)
330
+ end
331
+ else
332
+ @recv_cache = v
333
+ end
334
+ @seen_recv = true
335
+ if @seen_all_inputs
336
+ if @is_morph
337
+ recv_val = v
338
+ else
339
+ recv_val = @recv_cache
340
+ end
341
+ res = recv_val.send(@meth, *@args, &@blk)
342
+ push_out(res)
343
+ end
344
+ else
345
+ arg_indexes = @input_sources[source]
346
+ raise Bud::Error, "unknown input #{source}" if arg_indexes.nil?
347
+ arg_val = v
348
+ unless @is_morph
349
+ if @input_caches[source]
350
+ arg_val = @input_caches[source].merge(arg_val)
351
+ end
352
+ end
353
+ arg_indexes.each do |i|
354
+ @args[i] = arg_val
355
+ end
356
+
357
+ unless @seen_all_inputs
358
+ @waiting_for_input.delete(source)
359
+ @seen_all_inputs = @waiting_for_input.empty?
360
+ end
361
+
362
+ if @seen_all_inputs && @seen_recv
363
+ res = @recv_cache.send(@meth, *@args, &@blk)
364
+ push_out(res)
365
+ end
366
+
367
+ if @input_caches.has_key? source
368
+ @input_caches[source] = @input_caches[source].merge(v)
369
+ else
370
+ @input_caches[source] = v
371
+ end
372
+ arg_indexes.each do |i|
373
+ @args[i] = @input_caches[source]
374
+ end
375
+ end
376
+ end
377
+
378
+ def inspect
379
+ "#{super} [#{@meth}]"
380
+ end
381
+ end
382
+
383
+ class Bud::LatticeWrapper
384
+ attr_reader :tabname, :wired_by, :rescan_on_merge
385
+ attr_accessor :accumulate_tick_deltas
386
+
387
+ def initialize(tabname, klass, bud_i)
388
+ @tabname = tabname
389
+ @klass = klass
390
+ @bud_instance = bud_i
391
+ @wired_by = []
392
+ @rescan_on_merge = Set.new
393
+ end
394
+
395
+ def qualified_tabname
396
+ @qualified_tabname ||= @bud_instance.toplevel? ? @tabname : "#{@bud_instance.qualified_name}.#{@tabname}".to_sym
397
+ end
398
+
399
+ def invalidate_at_tick
400
+ false
401
+ end
402
+
403
+ def current_value
404
+ @storage ||= @klass.new
405
+ @storage
406
+ end
407
+
408
+ def current_delta
409
+ @delta ||= @klass.new
410
+ @delta
411
+ end
412
+
413
+ def current_new_delta
414
+ @new_delta ||= @klass.new
415
+ @new_delta
416
+ end
417
+
418
+ def current_pending
419
+ @pending ||= @klass.new
420
+ @pending
421
+ end
422
+
423
+ def do_merge(lhs, rhs)
424
+ unless lhs.class <= Bud::Lattice
425
+ raise Bud::Error, "unexpected merge input: #{lhs.class}"
426
+ end
427
+ return lhs if rhs.nil?
428
+
429
+ unless rhs.class <= @klass
430
+ rhs = @klass.new(rhs)
431
+ end
432
+ rv = lhs.merge(rhs)
433
+ unless rv.class <= Bud::Lattice
434
+ raise Bud::Error, "#{lhs.class}\#merge did not return lattice value: #{rv.inspect}"
435
+ end
436
+ rv
437
+ end
438
+
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
+ # Merge "i" into @new_delta
464
+ public
465
+ def insert(i, source)
466
+ @new_delta = do_merge(current_new_delta, i)
467
+ end
468
+
469
+ def <=(i)
470
+ if @bud_instance.wiring?
471
+ setup_wiring(i, :output)
472
+ else
473
+ @new_delta = do_merge(current_new_delta, i)
474
+ end
475
+ end
476
+
477
+ superator "<+" do |i|
478
+ if @bud_instance.wiring?
479
+ setup_wiring(i, :pending)
480
+ else
481
+ @pending = do_merge(current_pending, i)
482
+ end
483
+ end
484
+
485
+ # XXX: refactor with BudCollection to avoid duplication of code
486
+ def add_merge_target
487
+ toplevel = @bud_instance.toplevel
488
+ if toplevel.done_bootstrap
489
+ toplevel.merge_targets[toplevel.this_stratum] << self
490
+ end
491
+ end
492
+
493
+ def to_push_elem
494
+ toplevel = @bud_instance.toplevel
495
+ this_stratum = toplevel.this_stratum
496
+ oid = self.object_id
497
+ unless toplevel.scanners[this_stratum][[oid, @tabname]]
498
+ scanner = Bud::LatticeScanner.new(@bud_instance, self)
499
+ toplevel.scanners[this_stratum][[oid, @tabname]] = scanner
500
+ toplevel.push_sources[this_stratum][[oid, @tabname]] = scanner
501
+ end
502
+ return toplevel.scanners[this_stratum][[oid, @tabname]]
503
+ end
504
+
505
+ def flush_deltas
506
+ end
507
+
508
+ def add_rescan_invalidate(rescan, invalidate)
509
+ end
510
+
511
+ def method_missing(meth, *args, &blk)
512
+ # If we're invoking a lattice method and we're currently wiring up the
513
+ # dataflow, wire up a dataflow element to invoke the given method.
514
+ if @bud_instance.wiring?
515
+ pusher = to_push_elem
516
+ Bud::PushApplyMethod.new(@bud_instance, pusher, meth, args, blk)
517
+ else
518
+ super
519
+ end
520
+ end
521
+
522
+ def bootstrap
523
+ @storage = do_merge(current_value, @pending)
524
+ @pending = nil
525
+ end
526
+
527
+ def tick
528
+ if @new_delta
529
+ raise Bud::Error, "orphaned delta value for lattice #{@tabname}: #{@new_delta.inspect}"
530
+ end
531
+ merge_to_storage(@pending)
532
+ @pending = nil
533
+ @delta = nil
534
+ end
535
+
536
+ def merge_to_storage(v)
537
+ m = do_merge(current_value, v)
538
+ if m != current_value
539
+ @storage = m
540
+ @rescan_on_merge.each do |e|
541
+ if e.kind_of? Bud::ScannerElement
542
+ e.force_rescan = true
543
+ else
544
+ e.rescan = true
545
+ end
546
+ end
547
+ return true
548
+ else
549
+ return false
550
+ end
551
+ end
552
+
553
+ def tick_deltas
554
+ result = merge_to_storage(@new_delta)
555
+ @delta = @new_delta
556
+ @new_delta = nil
557
+ return result
558
+ end
559
+
560
+ def inspect
561
+ "{#{@tabname}, #{current_value.inspect}}"
562
+ end
563
+ end