bud 0.9.4 → 0.9.9

Sign up to get free protection for your applications and to get access to all the features.
@@ -75,7 +75,7 @@ class GraphGen #:nodoc: all
75
75
  # its name is "CYC" + concat(sort(predicate names))
76
76
  depends.each do |d|
77
77
  # b/c bud_obj was pruned before serialization...
78
- (bud_obj, rule_id, lhs, op, body, nm) = d.to_a
78
+ bud_obj, rule_id, lhs, op, body, nm, in_body = d.to_a
79
79
  head = lhs
80
80
  body = body
81
81
 
@@ -167,7 +167,7 @@ class GraphGen #:nodoc: all
167
167
  @edges[ekey].arrowsize = 2
168
168
 
169
169
  @edges[ekey].color = (@nodes[body]["color"].source || "")
170
- @edges[ekey].URL = "#{rule_id}.html" unless rule_id.nil?
170
+ @edges[ekey].URL = "#{rule_id}-#{head}.html" unless rule_id.nil?
171
171
  if head =~ /_msg\z/
172
172
  @edges[ekey].minlen = 2
173
173
  else
@@ -303,7 +303,7 @@ class SpaceTime
303
303
  params[:URL] = "DBM_#{k}/tm_#{item}.svg"
304
304
  end
305
305
  snd = @subs[k].add_nodes(label, params)
306
- unless @head[k].id == snd.id
306
+ unless @head[k].object_id == snd.object_id
307
307
  @subs[k].add_edges(@head[k], snd, :weight => 2)
308
308
  @head[k] = snd
309
309
  end
@@ -0,0 +1,47 @@
1
+ require 'rubygems'
2
+ require 'bud'
3
+ require 'graphviz'
4
+
5
+ # A simple interface between graphviz and bud
6
+ module BudGraph
7
+ state do
8
+ interface input, :bnode, [:name] => [:meta]
9
+ interface input, :bedge, [:from, :to, :meta]
10
+ end
11
+ end
12
+
13
+ module BloomGraph
14
+ include BudGraph
15
+
16
+ state do
17
+ table :nodes, bnode.schema
18
+ table :edges, bedge.schema
19
+ end
20
+
21
+ bloom do
22
+ nodes <= bnode
23
+ edges <= bedge
24
+ end
25
+
26
+ def finish(ignore, name, fmt=:pdf)
27
+ it = ignore.to_set
28
+ tick
29
+ nodes.to_a.each do |n|
30
+ unless it.include? n.name.to_sym
31
+ @graph.add_nodes(n.name, n.meta)
32
+ end
33
+ end
34
+
35
+ edges.to_a.each do |e|
36
+ unless it.include? e.from.to_sym or it.include? e.to.to_sym
37
+ @graph.add_edges(e.from, e.to, e.meta)
38
+ end
39
+ end
40
+ @graph.output(fmt => name)
41
+ end
42
+
43
+ def initialize(opts={:type => :digraph})
44
+ @graph = GraphViz.new(:G, opts)
45
+ super
46
+ end
47
+ end
@@ -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,595 @@
1
+ require 'bud/executor/elements'
2
+
3
+ class Bud::Lattice
4
+ include Comparable
5
+
6
+ @@lattice_kinds = {}
7
+ @@global_morphs = Set.new
8
+ @@global_mfuncs = Set.new
9
+
10
+ def self.wrapper_name(name)
11
+ if @wrapper_name
12
+ raise Bud::CompileError, "lattice #{self.name} has multiple wrapper names"
13
+ end
14
+ if @@lattice_kinds.has_key? name
15
+ raise Bud::CompileError, "duplicate lattice definition: #{name}"
16
+ end
17
+ @@lattice_kinds[name] = self
18
+ @wrapper_name = name
19
+ end
20
+
21
+ def self.lattice_kinds
22
+ @@lattice_kinds
23
+ end
24
+
25
+ def self.wrapper
26
+ @wrapper_name
27
+ end
28
+
29
+ def self.morph(name, &block)
30
+ if mfuncs.include?(name) || @@global_mfuncs.include?(name)
31
+ raise Bud::CompileError, "#{name} declared as both monotone and morph"
32
+ end
33
+ @morphs ||= Set.new
34
+ @morphs << name
35
+ @@global_morphs << name
36
+ define_method(name, &block)
37
+ end
38
+
39
+ def self.morphs
40
+ @morphs || Set.new
41
+ end
42
+
43
+ def self.global_morphs
44
+ @@global_morphs
45
+ end
46
+
47
+ def self.monotone(name, &block)
48
+ if morphs.include?(name) || @@global_morphs.include?(name)
49
+ raise Bud::CompileError, "#{name} declared as both monotone and morph"
50
+ end
51
+ @mfuncs ||= Set.new
52
+ @mfuncs << name
53
+ @@global_mfuncs << name
54
+ define_method(name, &block)
55
+ end
56
+
57
+ def self.mfuncs
58
+ @mfuncs || Set.new
59
+ end
60
+
61
+ def self.global_mfuncs
62
+ @@global_mfuncs
63
+ end
64
+
65
+ def reject_input(i, meth="initialize")
66
+ site = "#{self.class.wrapper}\##{meth}"
67
+ raise Bud::TypeError, "illegal input to #{site}: #{i.inspect}"
68
+ end
69
+
70
+ # The default equality semantics for lattice objects is based on reveal. Note
71
+ # that this isn't always appropriate: if the intended equality semantics for
72
+ # the lattice type differ from the equality semantics of the object returned
73
+ # by reveal (e.g., a set lattice might return an array with an unpredictable
74
+ # order), the lattice type should override this behavior.
75
+ def ==(o)
76
+ return false unless o.kind_of? Bud::Lattice
77
+ return reveal == o.reveal
78
+ end
79
+
80
+ def eql?(o)
81
+ self == o
82
+ end
83
+
84
+ # Ensure hashing and equality semantics are consistent.
85
+ def hash
86
+ reveal.hash
87
+ end
88
+
89
+ # Similarly, use reveal'ed value to implement Comparable.
90
+ def <=>(o)
91
+ reveal <=> o.reveal
92
+ end
93
+
94
+ # Return the state valued associated with a lattice instance. Note that this
95
+ # is non-monotonic when invoked from user code; it should be used with care by
96
+ # framework code.
97
+ def reveal
98
+ @v
99
+ end
100
+
101
+ def inspect
102
+ "<#{self.class.wrapper}: #{reveal.inspect}>"
103
+ end
104
+
105
+ # Construct a new instance of the current class that wraps "new_v". We assume
106
+ # that new_v is already a legal input value for the class, so we can bypass
107
+ # the class's normal initializer -- this avoids redundant error checks.
108
+ def wrap_unsafe(new_v)
109
+ rv = self.class.new
110
+ rv.instance_variable_set('@v', new_v)
111
+ rv
112
+ end
113
+ end
114
+
115
+ class Bud::LatticePushElement
116
+ attr_reader :wired_by, :outputs
117
+ attr_accessor :invalidated, :rescan
118
+
119
+ def initialize(bud_instance)
120
+ @bud_instance = bud_instance
121
+ @wired_by = []
122
+ @outputs = []
123
+ @pendings = []
124
+ @deletes = []
125
+ @invalidated = true
126
+ @rescan = true
127
+ end
128
+
129
+ def wire_to(element, kind=:output)
130
+ case kind
131
+ when :output
132
+ @outputs << element
133
+ when :pending
134
+ @pendings << element
135
+ when :delete
136
+ @deletes << element
137
+ else
138
+ raise Bud::Error, "unrecognized wiring kind: #{kind}"
139
+ end
140
+
141
+ element.wired_by << self
142
+ end
143
+
144
+ def check_wiring
145
+ if @outputs.empty? and @pendings.empty? and @deletes.empty?
146
+ raise Bud::Error, "no output specified for #{inspect}"
147
+ end
148
+ end
149
+
150
+ def print_wiring(depth=0, accum="")
151
+ puts "#{' ' * depth}#{accum} #{inspect}"
152
+
153
+ [@outputs, @pendings, @deletes].each do |buf|
154
+ next_accum = case buf
155
+ when @outputs
156
+ "=> "
157
+ when @pendings
158
+ "+> "
159
+ when @deletes
160
+ "-> "
161
+ end
162
+
163
+ buf.each do |o|
164
+ if o.respond_to? :print_wiring
165
+ o.print_wiring(depth + 1, next_accum)
166
+ else
167
+ puts "#{' ' * (depth + 1)}#{next_accum} #{o.inspect}"
168
+ end
169
+ end
170
+ end
171
+ end
172
+
173
+ def inspect
174
+ "#{self.class}:#{self.object_id.to_s(16)}"
175
+ end
176
+
177
+ def wirings
178
+ @outputs + @pendings + @deletes
179
+ end
180
+
181
+ def method_missing(meth, *args, &blk)
182
+ if @bud_instance.wiring?
183
+ Bud::PushApplyMethod.new(@bud_instance, self, meth, args, blk)
184
+ else
185
+ super
186
+ end
187
+ end
188
+
189
+ # Push-based dataflow
190
+ def insert(v, source)
191
+ push_out(v)
192
+ end
193
+
194
+ def push_out(v)
195
+ @outputs.each do |o|
196
+ # If we're emitting outputs to a traditional Bloom collection, merge
197
+ # operators (e.g., <=, <+) take a collection of tuples, so we need to
198
+ # convert the lattice value into a collection of tuple-like values. For
199
+ # now, we hardcode a single way to do this: we simply assume the value
200
+ # embedded inside the lattice is an Enumerable that contains tuple-like
201
+ # values. We also allow lattice morphisms to just produce Enumerable
202
+ # values directly, so we don't call reveal in that case.
203
+ # XXX: rethink this.
204
+ if o.class <= Bud::BudCollection
205
+ o <= (v.class <= Bud::Lattice ? v.reveal : v)
206
+ else
207
+ o.insert(v, self)
208
+ end
209
+ end
210
+ @pendings.each do |o|
211
+ if o.class <= Bud::BudCollection
212
+ o.pending_merge(v.class <= Bud::Lattice ? v.reveal : v)
213
+ else
214
+ o <+ v
215
+ end
216
+ end
217
+ @deletes.each do |o|
218
+ raise Bud::Error unless o.class <= Bud::BudCollection
219
+ o.pending_delete(v.class <= Bud::Lattice ? v.reveal : v)
220
+ end
221
+ end
222
+
223
+ def flush
224
+ end
225
+
226
+ def stratum_end
227
+ end
228
+
229
+ # Rescan and invalidation
230
+ def add_rescan_invalidate(rescan, invalidate)
231
+ end
232
+
233
+ def invalidate_at_tick(rescan, invalidate)
234
+ end
235
+
236
+ def invalidate_cache
237
+ end
238
+
239
+ # Tick (delta processing)
240
+ def tick
241
+ end
242
+
243
+ def tick_deltas
244
+ end
245
+
246
+ def rescan_at_tick
247
+ false
248
+ end
249
+ end
250
+
251
+ # A push-based dataflow element that scans a lattice wrapper
252
+ class Bud::LatticeScanner < Bud::LatticePushElement
253
+ attr_reader :collection, :rescan_set, :invalidate_set
254
+
255
+ def initialize(bud_instance, collection)
256
+ super(bud_instance)
257
+ @collection = collection
258
+ @rescan_set = []
259
+ @invalidate_set = []
260
+ end
261
+
262
+ def scan(first_iter)
263
+ if first_iter || @bud_instance.options[:disable_lattice_semi_naive]
264
+ push_out(@collection.current_value)
265
+ else
266
+ push_out(@collection.current_delta)
267
+ end
268
+ end
269
+
270
+ def inspect
271
+ "#{super} [#{collection.qualified_tabname}]"
272
+ end
273
+ end
274
+
275
+ class Bud::LatticeWrapper; end
276
+
277
+ # A push-based dataflow element that applies a method to a lattice value
278
+ class Bud::PushApplyMethod < Bud::LatticePushElement
279
+ SOURCE_TYPES = [Bud::LatticeWrapper, Bud::BudCollection,
280
+ Bud::LatticePushElement, Bud::PushElement]
281
+
282
+ def initialize(bud_instance, recv, meth, args, blk)
283
+ super(bud_instance)
284
+ @recv = recv
285
+ @meth = meth
286
+ @blk = blk
287
+ @args = args.dup
288
+ @is_morph = Bud::Lattice.global_morphs.include? @meth
289
+ @recv_is_scanner = @recv.kind_of? Bud::LatticeScanner
290
+
291
+ recv.wire_to(self, :output)
292
+ bud_instance.push_elems[[self.object_id, recv, meth, blk]] = self
293
+
294
+ # Arguments that are normal Ruby values are assumed to remain invariant as
295
+ # rule evaluation progresses; hence, we just pass along those values when
296
+ # invoking the function. Arguments that are derived from lattices or
297
+ # collections might change; hence, we need to wire up the push dataflow to
298
+ # have the current values of the function's arguments passed to this node.
299
+
300
+ # Map from input node to a list of indexes; the indexes identify the
301
+ # positions in the args array that should be filled with the node's value
302
+ @input_sources = {}
303
+
304
+ # Similarly, map from input node to a cached value -- this is the last value
305
+ # we've seen from this input. If the input gave us a delta, we merge
306
+ # together all the deltas we've seen and cache the resulting value. XXX: In
307
+ # the common case that the input is a scanner over a lattice wrapper, this
308
+ # means we do redundant work merging together deltas.
309
+ @input_caches = {}
310
+
311
+ # Inputs for which we haven't seen a value yet.
312
+ @waiting_for_input = Set.new
313
+ @recv_cache = nil
314
+ @seen_recv = false
315
+
316
+ @args.each_with_index do |a, i|
317
+ if SOURCE_TYPES.any?{|s| a.kind_of? s}
318
+ if a.kind_of? Bud::LatticeWrapper
319
+ a = a.to_push_elem
320
+ end
321
+ a.wire_to(self, :output)
322
+ @input_sources[a] ||= []
323
+ @input_sources[a] << i
324
+ @waiting_for_input << a
325
+ @args[i] = nil # Substitute actual value before calling method
326
+ end
327
+ end
328
+
329
+ @seen_all_inputs = @waiting_for_input.empty?
330
+ end
331
+
332
+ def insert(v, source)
333
+ if source == @recv
334
+ if @seen_recv
335
+ # Update the cached value for the method receiver. Note that if we're
336
+ # applying a method directly to a LatticeScanner (i.e., method applied
337
+ # to lattice wrapper), we can avoid maintaining a separate cache and
338
+ # instead use the wrapper's current value.
339
+ if @recv_is_scanner
340
+ @recv_cache = @recv.collection.current_value
341
+ else
342
+ @recv_cache = @recv_cache.merge(v)
343
+ end
344
+ else
345
+ @recv_cache = v
346
+ end
347
+ @seen_recv = true
348
+ if @seen_all_inputs
349
+ if @is_morph
350
+ recv_val = v
351
+ else
352
+ recv_val = @recv_cache
353
+ end
354
+ res = recv_val.send(@meth, *@args, &@blk)
355
+ push_out(res)
356
+ end
357
+ else
358
+ arg_indexes = @input_sources[source]
359
+ raise Bud::Error, "unknown input #{source}" if arg_indexes.nil?
360
+ arg_val = v
361
+ unless @is_morph
362
+ if @input_caches[source]
363
+ arg_val = @input_caches[source].merge(arg_val)
364
+ end
365
+ end
366
+ arg_indexes.each do |i|
367
+ @args[i] = arg_val
368
+ end
369
+
370
+ unless @seen_all_inputs
371
+ @waiting_for_input.delete(source)
372
+ @seen_all_inputs = @waiting_for_input.empty?
373
+ end
374
+
375
+ if @seen_all_inputs && @seen_recv
376
+ res = @recv_cache.send(@meth, *@args, &@blk)
377
+ push_out(res)
378
+ end
379
+
380
+ if @input_caches.has_key? source
381
+ @input_caches[source] = @input_caches[source].merge(v)
382
+ else
383
+ @input_caches[source] = v
384
+ end
385
+ arg_indexes.each do |i|
386
+ @args[i] = @input_caches[source]
387
+ end
388
+ end
389
+ end
390
+
391
+ def inspect
392
+ "#{super} [#{@meth}]"
393
+ end
394
+ end
395
+
396
+ class Bud::LatticeWrapper
397
+ attr_reader :tabname, :wired_by, :rescan_on_delta
398
+ attr_accessor :accumulate_tick_deltas, :bud_instance
399
+
400
+ def initialize(tabname, klass, bud_i)
401
+ @tabname = tabname
402
+ @klass = klass
403
+ @bud_instance = bud_i
404
+ @wired_by = []
405
+ @rescan_on_delta = Set.new
406
+ end
407
+
408
+ def qualified_tabname
409
+ @qualified_tabname ||= @bud_instance.toplevel? ? @tabname : "#{@bud_instance.qualified_name}.#{@tabname}".to_sym
410
+ end
411
+
412
+ def invalidate_at_tick
413
+ false
414
+ end
415
+
416
+ def setup_wiring(input, kind)
417
+ if input.class <= Bud::LatticeWrapper
418
+ input.to_push_elem.wire_to(self, kind)
419
+ elsif (input.class <= Bud::LatticePushElement || input.class <= Bud::PushElement)
420
+ input.wire_to(self, kind)
421
+ elsif input.class <= Bud::BudCollection
422
+ input.pro.wire_to(self, kind)
423
+ elsif input.class <= Proc
424
+ tbl = register_coll_expr(input)
425
+ tbl.pro.wire_to(self, kind)
426
+ else
427
+ raise Bud::Error, "unrecognized wiring input: #{input}"
428
+ end
429
+
430
+ add_merge_target
431
+ end
432
+
433
+ def positive_predecessors
434
+ @wired_by.select {|e| e.outputs.include?(self) || e.pendings.include?(self)}
435
+ end
436
+
437
+ private
438
+ def register_coll_expr(expr)
439
+ name = "expr_#{expr.object_id}".to_sym
440
+ @bud_instance.coll_expr(name, expr, nil)
441
+ @bud_instance.send(name)
442
+ end
443
+
444
+ public
445
+ def current_value
446
+ @storage ||= @klass.new
447
+ @storage
448
+ end
449
+
450
+ def current_delta
451
+ @delta ||= @klass.new
452
+ @delta
453
+ end
454
+
455
+ def current_new_delta
456
+ @new_delta ||= @klass.new
457
+ @new_delta
458
+ end
459
+
460
+ def current_pending
461
+ @pending ||= @klass.new
462
+ @pending
463
+ end
464
+
465
+ def do_merge(lhs, rhs)
466
+ unless lhs.class <= Bud::Lattice
467
+ raise Bud::Error, "unexpected merge input: #{lhs.class}"
468
+ end
469
+ return lhs if rhs.nil?
470
+
471
+ unless rhs.class <= @klass
472
+ rhs = @klass.new(rhs)
473
+ end
474
+ rv = lhs.merge(rhs)
475
+ unless rv.class <= Bud::Lattice
476
+ raise Bud::Error, "#{lhs.class}\#merge did not return lattice value: #{rv.inspect}"
477
+ end
478
+ rv
479
+ end
480
+
481
+ # Merge "i" into @new_delta
482
+ public
483
+ def insert(i, source)
484
+ @new_delta = do_merge(current_new_delta, i)
485
+ end
486
+
487
+ def <=(i)
488
+ if @bud_instance.wiring?
489
+ setup_wiring(i, :output)
490
+ else
491
+ @new_delta = do_merge(current_new_delta, i)
492
+ end
493
+ end
494
+
495
+ superator "<+" do |i|
496
+ if @bud_instance.wiring?
497
+ setup_wiring(i, :pending)
498
+ else
499
+ @pending = do_merge(current_pending, i)
500
+ end
501
+ end
502
+
503
+ superator "<~" do |o|
504
+ # Overridden when <~ is defined (i.e., channels and terminals)
505
+ raise Bud::CompileError, "#{tabname} cannot appear on the lhs of a <~ operator"
506
+ end
507
+
508
+ # XXX: refactor with BudCollection to avoid duplication of code
509
+ def add_merge_target
510
+ toplevel = @bud_instance.toplevel
511
+ if toplevel.done_bootstrap
512
+ toplevel.merge_targets[toplevel.this_stratum] << self
513
+ end
514
+ end
515
+
516
+ def to_push_elem
517
+ toplevel = @bud_instance.toplevel
518
+ this_stratum = toplevel.this_stratum
519
+ oid = self.object_id
520
+ unless toplevel.scanners[this_stratum][[oid, @tabname]]
521
+ scanner = Bud::LatticeScanner.new(@bud_instance, self)
522
+ toplevel.scanners[this_stratum][[oid, @tabname]] = scanner
523
+ toplevel.push_sources[this_stratum][[oid, @tabname]] = scanner
524
+ end
525
+ return toplevel.scanners[this_stratum][[oid, @tabname]]
526
+ end
527
+
528
+ def flush_deltas
529
+ end
530
+
531
+ def add_rescan_invalidate(rescan, invalidate)
532
+ end
533
+
534
+ def method_missing(meth, *args, &blk)
535
+ # If we're invoking a lattice method and we're currently wiring up the
536
+ # dataflow, wire up a dataflow element to invoke the given method.
537
+ if @bud_instance.wiring?
538
+ pusher = to_push_elem
539
+ Bud::PushApplyMethod.new(@bud_instance, pusher, meth, args, blk)
540
+ else
541
+ super
542
+ end
543
+ end
544
+
545
+ def bootstrap
546
+ # Bootstrap blocks might install lattice values via either <= (@new_delta)
547
+ # or <+ (@pending).
548
+ if @new_delta
549
+ merge_to_storage(@new_delta)
550
+ @new_delta = nil
551
+ end
552
+
553
+ if @pending
554
+ merge_to_storage(@pending)
555
+ @pending = nil
556
+ end
557
+ end
558
+
559
+ def tick
560
+ if @new_delta
561
+ raise Bud::Error, "orphaned delta value for lattice #{@tabname}: #{@new_delta.inspect}"
562
+ end
563
+ merge_to_storage(@pending)
564
+ @pending = nil
565
+ @delta = nil
566
+ end
567
+
568
+ def merge_to_storage(v)
569
+ m = do_merge(current_value, v)
570
+ if m != current_value
571
+ @storage = m
572
+ @rescan_on_delta.each do |e|
573
+ if e.kind_of? Bud::ScannerElement
574
+ e.force_rescan = true
575
+ else
576
+ e.rescan = true
577
+ end
578
+ end
579
+ return true
580
+ else
581
+ return false
582
+ end
583
+ end
584
+
585
+ def tick_deltas
586
+ result = merge_to_storage(@new_delta)
587
+ @delta = @new_delta
588
+ @new_delta = nil
589
+ return result
590
+ end
591
+
592
+ def inspect
593
+ "{#{@tabname}, #{current_value.inspect}}"
594
+ end
595
+ end