bud 0.9.5 → 0.9.6
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.
- checksums.yaml +15 -0
- data/History.txt +25 -1
- data/bin/budlabel +0 -0
- data/bin/budplot +11 -6
- data/bin/budtimelines +2 -2
- data/bin/budvis +7 -1
- data/docs/README.md +8 -8
- data/docs/cheat.md +2 -2
- data/lib/bud.rb +46 -26
- data/lib/bud/collections.rb +82 -45
- data/lib/bud/executor/elements.rb +2 -2
- data/lib/bud/executor/join.rb +31 -28
- data/lib/bud/graphs.rb +2 -2
- data/lib/bud/lattice-core.rb +52 -33
- data/lib/bud/lattice-lib.rb +28 -25
- data/lib/bud/monkeypatch.rb +1 -1
- data/lib/bud/rebl.rb +26 -8
- data/lib/bud/rewrite.rb +40 -20
- data/lib/bud/server.rb +3 -6
- data/lib/bud/source.rb +1 -16
- data/lib/bud/state.rb +1 -1
- data/lib/bud/viz.rb +10 -11
- data/lib/bud/viz_util.rb +8 -3
- metadata +7 -31
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
MTY4MjlkMTY1NjZlYTZiOTVjNmYyMmIzYTliNDVlZDIwZjA0YjY4Yw==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
MTE0ZGY3MDg5NzBmMTJkYTExNDNjYTcyNTFhOThmMzk0ZTM4YzgxOA==
|
7
|
+
!binary "U0hBNTEy":
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
OGJmYmMwZjU4Y2RlZWUyNDVkMGFkYTczZDhlNjk5MGQ5NmVlZDIyYzQyMjAz
|
10
|
+
NzEyZWI0YTI1MjVjM2VhZGRjMWE4ZDM4MGVlYzA4YzliYmJjNGUwZjEyY2I2
|
11
|
+
YjZhOTVjNzk5MGVjNWJkMTA0YWRmODk3MDAyYmVlOTM4MmVmYzA=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
YjZkOTlkYzQwODJkZTI4YjU0YmFhMmY4MDY5Mjg3Y2ZjMzUyN2ZmZTAyOGI5
|
14
|
+
M2M3NWQ5OTgyM2U2ZjA3NGIzMjkwZjhjMTBhNWFmYzc3OTNiYmZjOWMzNmU3
|
15
|
+
MTkzZmI1MzJlODNkMmE2Y2E1NzU5ODYzNDlhM2Q3N2Q3MDk2Yzg=
|
data/History.txt
CHANGED
@@ -1,8 +1,32 @@
|
|
1
|
+
== 0.9.6 / 2013-02-25
|
2
|
+
|
3
|
+
* Support syntax sugar for initializing lattices (#294). For example, rather
|
4
|
+
than writing "foo <= Bud::MaxLattice.new(2)", you can now just write "foo <=
|
5
|
+
2". Note that, due to a bug in the superator gem, you cannot use this syntax
|
6
|
+
with the <+ operator.
|
7
|
+
* Allow nested lattice values to be sent over channels (#295, patch from Josh
|
8
|
+
Rosen). Lattice values could previously be sent as a field value in a tuple
|
9
|
+
delivered over a channel, but they could not be embedded arbitrarily deeply
|
10
|
+
within tuples (e.g., lattices nested within an array could not previously be
|
11
|
+
sent as a field value).
|
12
|
+
* Support "Comparable" for Bud::Lattice values
|
13
|
+
* Add support for lattices to rebl
|
14
|
+
* Reject attempts to insert into stdio via <+ (#288)
|
15
|
+
* Restore functionality of reading from stdio (i.e., stdin) with MRI 1.9
|
16
|
+
* Improve MRI 1.8 compatibility
|
17
|
+
* Require ruby_parser >= 3.1.0
|
18
|
+
* Fix bug in bootstrap blocks for lattice values
|
19
|
+
* Fix bug in rescan/invalidation for rules that reference lattice values and use
|
20
|
+
the <+ or <~ operators (#290)
|
21
|
+
* Fix bug in rescan logic for notin operator (#291)
|
22
|
+
* Fix bug in notin operators involving self joins (#298)
|
23
|
+
* Fix error when output from a notin operator was fed into a lattice
|
24
|
+
|
1
25
|
== 0.9.5 / 2012-11-24
|
2
26
|
|
3
27
|
* Lattice branch (Bloom^L) merged
|
4
28
|
* Compatibility with recent versions of ruby_parser (3.0.2+) and ruby2ruby
|
5
|
-
(2.0.1+)
|
29
|
+
(2.0.1+). Older versions of these two gems are no longer supported
|
6
30
|
* Add support for aggregate functions that take multiple input columns
|
7
31
|
* Add built-in aggregate function accum_pair(x, y), which produces a Set of
|
8
32
|
pairs (two-element arrays [x,y])
|
data/bin/budlabel
CHANGED
File without changes
|
data/bin/budplot
CHANGED
@@ -77,8 +77,8 @@ def process(mods)
|
|
77
77
|
d = make_instance(mods)
|
78
78
|
|
79
79
|
interfaces = {}
|
80
|
-
d.t_provides.each do |
|
81
|
-
interfaces[
|
80
|
+
d.t_provides.to_a.each do |prov|
|
81
|
+
interfaces[prov.interface] = prov.input
|
82
82
|
end
|
83
83
|
|
84
84
|
tabinf = {}
|
@@ -201,13 +201,18 @@ def get_trace_data
|
|
201
201
|
end
|
202
202
|
|
203
203
|
if ARGV.length < 2
|
204
|
-
puts "Usage: budplot LIST_OF_FILES LIST_OF_MODULES_OR_CLASSES"
|
204
|
+
puts "Usage: budplot [-I PATH_TO_RUBY_INCLUDES] LIST_OF_FILES LIST_OF_MODULES_OR_CLASSES"
|
205
205
|
exit
|
206
206
|
end
|
207
207
|
|
208
|
-
@opts = Getopt::Std.getopts("t:")
|
209
|
-
|
210
|
-
|
208
|
+
@opts = Getopt::Std.getopts("I:t:")
|
209
|
+
unless @opts["I"].nil?
|
210
|
+
if @opts["I"].class == Array
|
211
|
+
@opts["I"].each{|i| $:.unshift i}
|
212
|
+
else
|
213
|
+
$:.unshift @opts["I"]
|
214
|
+
end
|
215
|
+
end
|
211
216
|
|
212
217
|
@data = get_trace_data
|
213
218
|
`mkdir bud_doc`
|
data/bin/budtimelines
CHANGED
@@ -10,7 +10,7 @@ include VizUtil
|
|
10
10
|
|
11
11
|
module Depends
|
12
12
|
state do
|
13
|
-
table :depends, [:rid, :lhs, :op, :rhs, :nm, :in_body]
|
13
|
+
table :depends, [:bud_obj, :rid, :lhs, :op, :rhs, :nm, :in_body]
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
@@ -49,7 +49,7 @@ end
|
|
49
49
|
module DeltaLogic
|
50
50
|
include TPSchema
|
51
51
|
bloom do
|
52
|
-
zerod_cards <= cardinalities{|c| c + [c.bud_time-1]}
|
52
|
+
zerod_cards <= cardinalities{|c| c.to_a + [c.bud_time-1]}
|
53
53
|
zerod_cards <= (times * depends).pairs do |t, d|
|
54
54
|
unless cardinalities{|c| c[1] if c[0] == t.bud_time}.include? d[1]
|
55
55
|
[t.bud_time, d[1], 0, t.bud_time - 1]
|
data/bin/budvis
CHANGED
@@ -28,7 +28,13 @@ usage unless ARGV[0]
|
|
28
28
|
usage if ARGV[0] == '--help'
|
29
29
|
|
30
30
|
meta, data = get_meta2(BUD_DBM_DIR)
|
31
|
-
|
31
|
+
|
32
|
+
# prune outbufs from tabinf
|
33
|
+
tabinf = meta[:tabinf].find_all do |k|
|
34
|
+
!(k[1] == "Bud::BudChannel" and k[0] =~ /_snd\z/)
|
35
|
+
end
|
36
|
+
|
37
|
+
vh = VizHelper.new(tabinf, meta[:cycle], meta[:depends], meta[:rules], ARGV[0], meta[:provides])
|
32
38
|
data.each do |d|
|
33
39
|
vh.full_info << d
|
34
40
|
end
|
data/docs/README.md
CHANGED
@@ -20,14 +20,14 @@ try the following:
|
|
20
20
|
`budplot` tools for visualizing Bloom program analyses.
|
21
21
|
* [bfs.md][bfs]: A walkthrough of the Bloom distributed filesystem.
|
22
22
|
|
23
|
-
[intro]: /
|
24
|
-
[getstarted]: /
|
25
|
-
[operational]: /
|
26
|
-
[cheat]: /
|
27
|
-
[modules]: /
|
28
|
-
[ruby_hooks]: /
|
29
|
-
[visualizations]: /
|
30
|
-
[bfs]: /
|
23
|
+
[intro]: /docs/intro.md
|
24
|
+
[getstarted]: /docs/getstarted.md
|
25
|
+
[operational]: /docs/operational.md
|
26
|
+
[cheat]: /docs/cheat.md
|
27
|
+
[modules]: /docs/modules.md
|
28
|
+
[ruby_hooks]: /docs/ruby_hooks.md
|
29
|
+
[visualizations]: /docs/visualizations.md
|
30
|
+
[bfs]: /docs/bfs.md
|
31
31
|
|
32
32
|
In addition, the [bud-sandbox](http://github.com/bloom-lang/bud-sandbox) GitHub
|
33
33
|
repository contains lots of useful libraries and example programs built using
|
data/docs/cheat.md
CHANGED
@@ -224,14 +224,14 @@ Output the facts in `bc` that do not appear in `bc2`, as follows. First, we form
|
|
224
224
|
:attr2`).
|
225
225
|
|
226
226
|
2. If a code block is specified, invoke the block on every pair of matching
|
227
|
-
tuples in the join result. Any matches for which the block returns `
|
227
|
+
tuples in the join result. Any matches for which the block returns `false`
|
228
228
|
are removed from `t`.
|
229
229
|
|
230
230
|
Finally, we output every tuple of `bc` that does *not* appear in `t`.
|
231
231
|
|
232
232
|
# output items from foo if (a) there is no matching key in bar, or
|
233
233
|
# (b) all matching keys in bar have a smaller value
|
234
|
-
stdio <~ foo.notin(bar, :key=>:key) {|f, b|
|
234
|
+
stdio <~ foo.notin(bar, :key=>:key) {|f, b| f.val <= b.val}
|
235
235
|
|
236
236
|
|
237
237
|
## SQL-style grouping/aggregation (and then some) ##
|
data/lib/bud.rb
CHANGED
@@ -265,7 +265,7 @@ module Bud
|
|
265
265
|
mod_inst.t_rules.each do |imp_rule|
|
266
266
|
qname = "#{local_name}.#{imp_rule.lhs}"
|
267
267
|
self.t_rules << [imp_rule.bud_obj, imp_rule.rule_id, qname, imp_rule.op,
|
268
|
-
imp_rule.src, imp_rule.orig_src, imp_rule.
|
268
|
+
imp_rule.src, imp_rule.orig_src, imp_rule.unsafe_funcs_called]
|
269
269
|
end
|
270
270
|
mod_inst.t_depends.each do |imp_dep|
|
271
271
|
qlname = "#{local_name}.#{imp_dep.lhs}"
|
@@ -326,7 +326,7 @@ module Bud
|
|
326
326
|
# the user-defined tables and lattices. We start @app_tables off as a set,
|
327
327
|
# then convert to an array later.
|
328
328
|
@app_tables = (@tables.keys - @builtin_tables.keys).map {|t| @tables[t]}.to_set
|
329
|
-
@app_tables
|
329
|
+
@app_tables.merge(@lattices.values)
|
330
330
|
|
331
331
|
# Check scan and merge_targets to see if any builtin_tables need to be added as well.
|
332
332
|
@scanners.each do |scs|
|
@@ -346,7 +346,7 @@ module Bud
|
|
346
346
|
seen = Set.new(working)
|
347
347
|
sorted_elems = [] # sorted elements in this stratum
|
348
348
|
while not working.empty?
|
349
|
-
sorted_elems
|
349
|
+
sorted_elems.concat(working)
|
350
350
|
wired_to = []
|
351
351
|
working.each do |e|
|
352
352
|
e.wirings.each do |out|
|
@@ -453,18 +453,19 @@ module Bud
|
|
453
453
|
|
454
454
|
# By default, all tables are considered sources unless they appear on the
|
455
455
|
# lhs. We only consider non-temporal rules because invalidation is only
|
456
|
-
# about this tick. Also, we track (in
|
457
|
-
# targets of user-defined code blocks that call
|
458
|
-
#
|
459
|
-
# their contents, and thus forced to
|
460
|
-
|
456
|
+
# about this tick. Also, we track (in unsafe_targets) those tables that are
|
457
|
+
# the targets of user-defined code blocks that call "unsafe" functions that
|
458
|
+
# produce a different value in every tick (e.g., budtime). Elements that
|
459
|
+
# feed these tables are forced to rescan their contents, and thus forced to
|
460
|
+
# re-execute these code blocks.
|
461
|
+
unsafe_targets = Set.new
|
461
462
|
t_rules.each do |rule|
|
462
463
|
lhs = rule.lhs.to_sym
|
463
464
|
if rule.op == "<="
|
464
465
|
# Note that lattices cannot be sources
|
465
466
|
@tables[lhs].is_source = false if @tables.has_key? lhs
|
466
467
|
end
|
467
|
-
|
468
|
+
unsafe_targets << lhs if rule.unsafe_funcs_called
|
468
469
|
end
|
469
470
|
|
470
471
|
# Compute a set of tables and elements that should be explicitly told to
|
@@ -477,7 +478,7 @@ module Bud
|
|
477
478
|
@push_sorted_elems[stratum].each do |elem|
|
478
479
|
rescan << elem if elem.rescan_at_tick
|
479
480
|
|
480
|
-
if elem.outputs.any?{|tab| not(tab.class <= PushElement) and not(tab.class <= LatticePushElement) and
|
481
|
+
if elem.outputs.any?{|tab| not(tab.class <= PushElement) and not(tab.class <= LatticePushElement) and unsafe_targets.member? tab.qualified_tabname.to_sym }
|
481
482
|
rescan.merge(elem.wired_by)
|
482
483
|
end
|
483
484
|
end
|
@@ -489,8 +490,11 @@ module Bud
|
|
489
490
|
@default_rescan = rescan.to_a
|
490
491
|
@default_invalidate = invalidate.to_a
|
491
492
|
|
492
|
-
|
493
|
-
|
493
|
+
if $BUD_DEBUG
|
494
|
+
puts "Default rescan: #{rescan.inspect}"
|
495
|
+
puts "Default inval: #{invalidate.inspect}"
|
496
|
+
puts "Unsafe targets: #{unsafe_targets.inspect}"
|
497
|
+
end
|
494
498
|
|
495
499
|
# Now compute for each table that is to be scanned, the set of dependent
|
496
500
|
# tables and elements that will be invalidated if that table were to be
|
@@ -518,27 +522,43 @@ module Bud
|
|
518
522
|
end
|
519
523
|
@reset_list = to_reset.to_a
|
520
524
|
|
521
|
-
# For each lattice, find the
|
522
|
-
#
|
525
|
+
# For each lattice, find the collections that should be rescanned when there
|
526
|
+
# is a new delta for the lattice. That is, if we have a rule like:
|
523
527
|
# "t2 <= t1 {|t| [t.key, lat_foo]}", whenever there is a delta on lat_foo we
|
524
528
|
# should rescan t1 (to produce tuples with the updated lat_foo value).
|
529
|
+
#
|
525
530
|
# TODO:
|
526
|
-
# (1)
|
527
|
-
# (2) if t1 is fed by rules r1 and r2 but only r1 references lattice x,
|
531
|
+
# (1) if t1 is fed by rules r1 and r2 but only r1 references lattice x,
|
528
532
|
# don't trigger rescan of r2 on deltas for x (hard)
|
529
533
|
t_depends.each do |dep|
|
530
|
-
src,
|
531
|
-
if @lattices.has_key? src and
|
534
|
+
src, target_name = dep.body.to_sym, dep.lhs.to_sym
|
535
|
+
if @lattices.has_key? src and dep.in_body
|
532
536
|
src_lat = @lattices[src]
|
533
|
-
|
534
|
-
|
535
|
-
|
537
|
+
if @tables.has_key? target_name
|
538
|
+
target = @tables[target_name]
|
539
|
+
else
|
540
|
+
target = @lattices[target_name]
|
541
|
+
end
|
542
|
+
|
543
|
+
# Conservatively, we rescan all the elements that feed the lhs (target)
|
544
|
+
# collection via positive (non-deletion) rules; we then also need to
|
545
|
+
# potentially rescan ancestors of those elements as well (e.g., setting
|
546
|
+
# a stateless PushElement to rescan does nothing; we want to tell its
|
547
|
+
# ancestor ScannerElement to rescan).
|
548
|
+
#
|
549
|
+
# XXX: do we need to consider all transitively reachable nodes for
|
550
|
+
# rescan?
|
551
|
+
lat_rescan = target.positive_predecessors.to_set
|
552
|
+
lat_inval = Set.new
|
553
|
+
target.positive_predecessors.each do |e|
|
554
|
+
e.add_rescan_invalidate(lat_rescan, lat_inval)
|
536
555
|
end
|
556
|
+
src_lat.rescan_on_delta.merge(lat_rescan)
|
537
557
|
end
|
538
558
|
end
|
539
559
|
end
|
540
560
|
|
541
|
-
#
|
561
|
+
# Given rescan, invalidate sets, compute transitive closure
|
542
562
|
def rescan_invalidate_tc(stratum, rescan, invalidate)
|
543
563
|
rescan_len = rescan.size
|
544
564
|
invalidate_len = invalidate.size
|
@@ -1139,14 +1159,14 @@ module Bud
|
|
1139
1159
|
@periodics = table :periodics_tbl, [:pername] => [:period]
|
1140
1160
|
|
1141
1161
|
# for BUD reflection
|
1142
|
-
table :
|
1162
|
+
table :t_cycle, [:predicate, :via, :neg, :temporal]
|
1143
1163
|
table :t_depends, [:bud_obj, :rule_id, :lhs, :op, :body] => [:nm, :in_body]
|
1144
1164
|
table :t_provides, [:interface] => [:input]
|
1145
|
-
table :
|
1165
|
+
table :t_rules, [:bud_obj, :rule_id] => [:lhs, :op, :src, :orig_src, :unsafe_funcs_called]
|
1146
1166
|
table :t_stratum, [:predicate] => [:stratum]
|
1147
|
-
table :t_cycle, [:predicate, :via, :neg, :temporal]
|
1148
1167
|
table :t_table_info, [:tab_name, :tab_type]
|
1149
1168
|
table :t_table_schema, [:tab_name, :col_name, :ord, :loc]
|
1169
|
+
table :t_underspecified, t_provides.schema
|
1150
1170
|
|
1151
1171
|
# Identify builtin tables as such
|
1152
1172
|
@builtin_tables = @tables.clone if toplevel
|
@@ -1186,7 +1206,7 @@ module Bud
|
|
1186
1206
|
begin
|
1187
1207
|
eval_rule(rule.bud_obj, rule.src)
|
1188
1208
|
rescue Exception => e
|
1189
|
-
err_msg = "** Exception while wiring rule: #{rule.
|
1209
|
+
err_msg = "** Exception while wiring rule: #{rule.orig_src}\n ****** #{e}"
|
1190
1210
|
# Create a new exception for accomodating err_msg, but reuse original backtrace
|
1191
1211
|
new_e = (e.class <= Bud::Error) ? e.class.new(err_msg) : Bud::Error.new(err_msg)
|
1192
1212
|
new_e.set_backtrace(e.backtrace)
|
data/lib/bud/collections.rb
CHANGED
@@ -52,7 +52,7 @@ module Bud
|
|
52
52
|
# user-specified schema.
|
53
53
|
@cols.each do |s|
|
54
54
|
if s.to_s.start_with? "@"
|
55
|
-
raise Bud::
|
55
|
+
raise Bud::CompileError, "illegal use of location specifier (@) in column #{s} of non-channel collection #{tabname}"
|
56
56
|
end
|
57
57
|
end
|
58
58
|
|
@@ -284,7 +284,12 @@ module Bud
|
|
284
284
|
|
285
285
|
public
|
286
286
|
def non_temporal_predecessors
|
287
|
-
@wired_by.select {|
|
287
|
+
@wired_by.select {|e| e.outputs.include? self}
|
288
|
+
end
|
289
|
+
|
290
|
+
public
|
291
|
+
def positive_predecessors
|
292
|
+
@wired_by.select {|e| e.outputs.include?(self) || e.pendings.include?(self)}
|
288
293
|
end
|
289
294
|
|
290
295
|
public
|
@@ -869,6 +874,8 @@ module Bud
|
|
869
874
|
given_schema ||= [:@address, :val]
|
870
875
|
@is_loopback = loopback
|
871
876
|
@locspec_idx = nil
|
877
|
+
@wire_buf = StringIO.new
|
878
|
+
@packer = MessagePack::Packer.new(@wire_buf)
|
872
879
|
|
873
880
|
# We're going to mutate the caller's given_schema (to remove the location
|
874
881
|
# specifier), so make a deep copy first. We also save a ref to the
|
@@ -959,19 +966,40 @@ module Bud
|
|
959
966
|
# particular, lattice values and Class instances), we first encode such
|
960
967
|
# values using Marshal, and then encode the entire tuple with
|
961
968
|
# MsgPack. Obviously, this is gross. The wire format also includes an
|
962
|
-
# array of indices, indicating which fields hold Marshall'd
|
969
|
+
# array of indices, indicating which fields hold Marshall'd values.
|
970
|
+
@packer.write_array_header(3)
|
971
|
+
@packer.write(qualified_tabname.to_s)
|
972
|
+
# The second element, wire_tuple, is an array. We will write it one
|
973
|
+
# element at a time:
|
974
|
+
@packer.write_array_header(t.length)
|
975
|
+
@packer.flush
|
963
976
|
marshall_indexes = []
|
964
|
-
|
965
|
-
|
977
|
+
t.each_with_index do |f,i|
|
978
|
+
# Performance optimization for cases where we know that we can't
|
979
|
+
# marshal the field using MsgPack:
|
966
980
|
if [Bud::Lattice, Class].any?{|t| f.class <= t}
|
967
981
|
marshall_indexes << i
|
968
|
-
|
982
|
+
@wire_buf << Marshal.dump(f).to_msgpack
|
969
983
|
else
|
970
|
-
|
984
|
+
begin
|
985
|
+
@wire_buf << f.to_msgpack
|
986
|
+
rescue NoMethodError
|
987
|
+
# If MsgPack can't marshal the field, fall back to Marshal.
|
988
|
+
# This handles fields that contain nested non-MsgPack-able
|
989
|
+
# objects (in these cases, the entire field is Marshal'd.)
|
990
|
+
marshall_indexes << i
|
991
|
+
@wire_buf << Marshal.dump(f).to_msgpack
|
992
|
+
end
|
971
993
|
end
|
972
994
|
end
|
973
|
-
|
974
|
-
|
995
|
+
@packer.write(marshall_indexes)
|
996
|
+
@packer.flush
|
997
|
+
toplevel.dsock.send_datagram(@wire_buf.string,
|
998
|
+
the_locspec[0], the_locspec[1])
|
999
|
+
|
1000
|
+
# Reset output buffer
|
1001
|
+
@wire_buf.rewind
|
1002
|
+
@wire_buf.truncate(0)
|
975
1003
|
end
|
976
1004
|
@pending.clear
|
977
1005
|
end
|
@@ -1015,45 +1043,28 @@ module Bud
|
|
1015
1043
|
end
|
1016
1044
|
|
1017
1045
|
superator "<+" do |o|
|
1018
|
-
raise Bud::
|
1046
|
+
raise Bud::CompileError, "illegal use of <+ with channel '#{@tabname}' on left"
|
1019
1047
|
end
|
1020
1048
|
|
1021
1049
|
undef merge
|
1022
1050
|
|
1023
1051
|
def <=(o)
|
1024
|
-
raise Bud::
|
1052
|
+
raise Bud::CompileError, "illegal use of <= with channel '#{@tabname}' on left"
|
1025
1053
|
end
|
1026
1054
|
end
|
1027
1055
|
|
1028
1056
|
class BudTerminal < BudScratch # :nodoc: all
|
1029
|
-
def initialize(name,
|
1030
|
-
super(name, bud_instance,
|
1057
|
+
def initialize(name, bud_instance, prompt=false) # :nodoc: all
|
1058
|
+
super(name, bud_instance, [:line])
|
1031
1059
|
@prompt = prompt
|
1032
1060
|
end
|
1033
1061
|
|
1034
1062
|
public
|
1035
1063
|
def start_stdin_reader # :nodoc: all
|
1036
|
-
|
1037
|
-
# we should add the terminal file descriptor to the EM event loop.
|
1038
|
-
@reader = Thread.new do
|
1064
|
+
Thread.new do
|
1039
1065
|
begin
|
1040
|
-
toplevel = @bud_instance.toplevel
|
1041
1066
|
while true
|
1042
|
-
|
1043
|
-
out_io.print("#{tabname} > ") if @prompt
|
1044
|
-
|
1045
|
-
in_io = toplevel.options[:stdin]
|
1046
|
-
s = in_io.gets
|
1047
|
-
break if s.nil? # Hit EOF
|
1048
|
-
s = s.chomp if s
|
1049
|
-
tup = [s]
|
1050
|
-
|
1051
|
-
ip = toplevel.ip
|
1052
|
-
port = toplevel.port
|
1053
|
-
EventMachine::schedule do
|
1054
|
-
socket = EventMachine::open_datagram_socket("127.0.0.1", 0)
|
1055
|
-
socket.send_datagram([tabname, tup, []].to_msgpack, ip, port)
|
1056
|
-
end
|
1067
|
+
break unless read_line
|
1057
1068
|
end
|
1058
1069
|
rescue Exception
|
1059
1070
|
puts "terminal reader thread failed: #{$!}"
|
@@ -1063,13 +1074,34 @@ module Bud
|
|
1063
1074
|
end
|
1064
1075
|
end
|
1065
1076
|
|
1077
|
+
# XXX: Ugly hack. Rather than sending terminal data to EM via UDP, we should
|
1078
|
+
# add the terminal file descriptor to the EM event loop.
|
1079
|
+
private
|
1080
|
+
def read_line
|
1081
|
+
toplevel = @bud_instance.toplevel
|
1082
|
+
if @prompt
|
1083
|
+
get_out_io.print("#{tabname} > ")
|
1084
|
+
end
|
1085
|
+
|
1086
|
+
in_io = toplevel.options[:stdin]
|
1087
|
+
input_str = in_io.gets
|
1088
|
+
return false if input_str.nil? # Hit EOF
|
1089
|
+
input_str.chomp!
|
1090
|
+
|
1091
|
+
EventMachine::schedule do
|
1092
|
+
socket = EventMachine::open_datagram_socket("127.0.0.1", 0)
|
1093
|
+
socket.send_datagram([tabname, [input_str], []].to_msgpack,
|
1094
|
+
toplevel.ip, toplevel.port)
|
1095
|
+
end
|
1096
|
+
|
1097
|
+
return true
|
1098
|
+
end
|
1099
|
+
|
1066
1100
|
public
|
1067
1101
|
def flush #:nodoc: all
|
1068
1102
|
out_io = get_out_io
|
1069
|
-
@pending.each_value
|
1070
|
-
|
1071
|
-
out_io.flush
|
1072
|
-
end
|
1103
|
+
@pending.each_value {|p| out_io.puts p[0]}
|
1104
|
+
out_io.flush
|
1073
1105
|
@pending.clear
|
1074
1106
|
end
|
1075
1107
|
|
@@ -1090,7 +1122,6 @@ module Bud
|
|
1090
1122
|
@tick_delta.clear
|
1091
1123
|
end
|
1092
1124
|
@invalidated = true # channels and terminals are always invalidated.
|
1093
|
-
raise Bud::Error, "orphaned pending tuples in terminal" unless @pending.empty?
|
1094
1125
|
end
|
1095
1126
|
|
1096
1127
|
public
|
@@ -1101,7 +1132,11 @@ module Bud
|
|
1101
1132
|
|
1102
1133
|
public
|
1103
1134
|
def <=(o) #:nodoc: all
|
1104
|
-
raise Bud::
|
1135
|
+
raise Bud::CompileError, "illegal use of <= with terminal '#{@tabname}' on left"
|
1136
|
+
end
|
1137
|
+
|
1138
|
+
superator "<+" do |o|
|
1139
|
+
raise Bud::CompileError, "illegal use of <+ with terminal '#{@tabname}' on left"
|
1105
1140
|
end
|
1106
1141
|
|
1107
1142
|
superator "<~" do |o|
|
@@ -1121,26 +1156,28 @@ module Bud
|
|
1121
1156
|
def get_out_io
|
1122
1157
|
rv = @bud_instance.toplevel.options[:stdout]
|
1123
1158
|
rv ||= $stdout
|
1124
|
-
|
1159
|
+
if rv.closed?
|
1160
|
+
raise Bud::Error, "attempt to write to closed terminal '#{tabname}'"
|
1161
|
+
end
|
1125
1162
|
rv
|
1126
1163
|
end
|
1127
1164
|
end
|
1128
1165
|
|
1129
1166
|
class BudPeriodic < BudScratch # :nodoc: all
|
1130
1167
|
def <=(o)
|
1131
|
-
raise Bud::
|
1168
|
+
raise Bud::CompileError, "illegal use of <= with periodic '#{tabname}' on left"
|
1132
1169
|
end
|
1133
1170
|
|
1134
1171
|
superator "<~" do |o|
|
1135
|
-
raise Bud::
|
1172
|
+
raise Bud::CompileError, "illegal use of <~ with periodic '#{tabname}' on left"
|
1136
1173
|
end
|
1137
1174
|
|
1138
1175
|
superator "<-" do |o|
|
1139
|
-
raise Bud::
|
1176
|
+
raise Bud::CompileError, "illegal use of <- with periodic '#{tabname}' on left"
|
1140
1177
|
end
|
1141
1178
|
|
1142
1179
|
superator "<+" do |o|
|
1143
|
-
raise Bud::
|
1180
|
+
raise Bud::CompileError, "illegal use of <+ with periodic '#{tabname}' on left"
|
1144
1181
|
end
|
1145
1182
|
|
1146
1183
|
def tick
|
@@ -1272,11 +1309,11 @@ module Bud
|
|
1272
1309
|
|
1273
1310
|
class BudReadOnly < BudCollection # :nodoc: all
|
1274
1311
|
superator "<+" do |o|
|
1275
|
-
raise CompileError, "illegal use of <+ with read-only collection '#{@tabname}' on left"
|
1312
|
+
raise Bud::CompileError, "illegal use of <+ with read-only collection '#{@tabname}' on left"
|
1276
1313
|
end
|
1277
1314
|
public
|
1278
1315
|
def merge(o) #:nodoc: all
|
1279
|
-
raise CompileError, "illegal use of <= with read-only collection '#{@tabname}' on left"
|
1316
|
+
raise Bud::CompileError, "illegal use of <= with read-only collection '#{@tabname}' on left"
|
1280
1317
|
end
|
1281
1318
|
public
|
1282
1319
|
def invalidate_cache
|