bud 0.9.4 → 0.9.9

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.
@@ -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