bud 0.9.4 → 0.9.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -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