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