bud 0.0.3 → 0.0.4
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.
- data/README +33 -16
- data/bin/budplot +42 -65
- data/bin/budtimelines +235 -0
- data/bin/budvis +24 -122
- data/bin/rebl +1 -0
- data/docs/README.md +21 -10
- data/docs/bfs.md +4 -6
- data/docs/c.html +251 -0
- data/docs/cheat.md +45 -30
- data/docs/deploy.md +26 -26
- data/docs/getstarted.md +6 -4
- data/docs/visualizations.md +43 -31
- data/examples/chat/chat.rb +4 -9
- data/examples/chat/chat_server.rb +1 -8
- data/examples/deploy/deploy_ip_port +1 -0
- data/examples/deploy/keys.rb +5 -0
- data/examples/deploy/tokenring-ec2.rb +9 -9
- data/examples/deploy/{tokenring-local.rb → tokenring-fork.rb} +3 -5
- data/examples/deploy/tokenring-thread.rb +15 -0
- data/examples/deploy/tokenring.rb +25 -17
- data/lib/bud/aggs.rb +87 -25
- data/lib/bud/bud_meta.rb +48 -31
- data/lib/bud/bust/bust.rb +16 -15
- data/lib/bud/collections.rb +207 -232
- data/lib/bud/depanalysis.rb +1 -0
- data/lib/bud/deploy/countatomicdelivery.rb +8 -20
- data/lib/bud/deploy/deployer.rb +16 -16
- data/lib/bud/deploy/ec2deploy.rb +34 -35
- data/lib/bud/deploy/forkdeploy.rb +90 -0
- data/lib/bud/deploy/threaddeploy.rb +38 -0
- data/lib/bud/graphs.rb +103 -199
- data/lib/bud/joins.rb +190 -41
- data/lib/bud/monkeypatch.rb +84 -0
- data/lib/bud/rebl.rb +8 -1
- data/lib/bud/rewrite.rb +152 -49
- data/lib/bud/server.rb +1 -0
- data/lib/bud/state.rb +24 -10
- data/lib/bud/storage/dbm.rb +170 -0
- data/lib/bud/storage/tokyocabinet.rb +5 -1
- data/lib/bud/stratify.rb +6 -7
- data/lib/bud/viz.rb +31 -17
- data/lib/bud/viz_util.rb +204 -0
- data/lib/bud.rb +271 -244
- data/lib/bud.rb.orig +806 -0
- metadata +43 -22
- data/docs/bfs.raw +0 -251
- data/docs/diffs +0 -181
- data/examples/basics/out +0 -1103
- data/examples/basics/out.new +0 -856
- data/lib/bud/deploy/localdeploy.rb +0 -53
data/docs/visualizations.md
CHANGED
@@ -1,55 +1,67 @@
|
|
1
1
|
# Visualizations
|
2
2
|
|
3
|
-
Bud programs compile naturally to dataflows, and dataflows have a natural
|
3
|
+
Bud programs compile naturally to dataflows, and dataflows have a natural
|
4
|
+
graphical representation. Viewing the dataflow graph of a program can be useful
|
5
|
+
to developers at various stages of program design, implementation and debugging.
|
4
6
|
|
5
|
-
Bud ships with two visualization utilities,
|
7
|
+
Bud ships with two visualization utilities, __budplot__ and __budvis__. Both
|
8
|
+
use _GraphViz_ to draw a directed graph representing the program state and
|
9
|
+
logic. __budplot__ provides a static analysis of the program, identifying
|
10
|
+
sources and sinks of the dataflow, unconnected components, and "points of order"
|
11
|
+
that correspond to logically nonmonotonic path edges. __budvis__ is an offline
|
12
|
+
debugging tool that analyses the trace of a (local) Bud execution and provides
|
13
|
+
an interactive representation of runtime state over time.
|
6
14
|
|
7
|
-
##
|
15
|
+
## Using budplot
|
8
16
|
|
9
|
-
|
10
|
-
|
11
|
-
The __plotter__ is a visual static analysis tool that aids in design and early implementation. The most common uses of the __plotter__ are:
|
17
|
+
__budplot__ is a visual static analysis tool that aids in design and early
|
18
|
+
implementation. The most common uses of __budplot__ are:
|
12
19
|
|
13
20
|
1. Visual sanity checking: does the dataflow look like I expected it to look?
|
14
21
|
2. Ensuring that a particular set of mixins is fully specified: e.g., did I forget to include a concrete implementation of a protocol required by other modules?
|
15
|
-
The Bloom module system, abstract interfaces and concrete implementations are described in more detail in [
|
16
|
-
3. Identifying dead code
|
22
|
+
The Bloom module system, abstract interfaces and concrete implementations are described in more detail in [modules.md](modules.md).
|
23
|
+
3. Identifying dead code
|
17
24
|
4. Experimenting with different module compositions
|
18
|
-
5. Identifying and iteratively refining a program's points of order
|
25
|
+
5. Identifying and iteratively refining a program's "points of order"
|
19
26
|
|
27
|
+
To run __budplot__, specify a list of Ruby input files, followed by a list of
|
28
|
+
Bud modules to be "mixed in" in the visualization.
|
20
29
|
|
21
|
-
$
|
22
|
-
|
23
|
-
ruby budplot LIST_OF_FILES LIST_OF_MODULES
|
30
|
+
$ budplot
|
31
|
+
Usage: budplot LIST_OF_FILES LIST_OF_MODULES
|
24
32
|
|
25
|
-
|
33
|
+
For example:
|
26
34
|
|
27
|
-
$
|
35
|
+
$ budplot kvs/kvs.rb ReplicatedKVS
|
28
36
|
Warning: underspecified dataflow: ["my_id", true]
|
29
37
|
Warning: underspecified dataflow: ["add_member", true]
|
30
38
|
Warning: underspecified dataflow: ["send_mcast", true]
|
31
39
|
Warning: underspecified dataflow: ["mcast_done", false]
|
32
40
|
fn is ReplicatedKVS_viz_collapsed.svg
|
33
|
-
$ open
|
41
|
+
$ open bud_doc/index.html
|
34
42
|
|
35
|
-
|
43
|
+
`ReplicatedKVS` includes the `MulticastProtocol` and `MembershipProtocol`
|
44
|
+
protocols, but does not specify which implementation of these abstractions to
|
45
|
+
use. The program is underspecified, and this is represented in the resulting
|
46
|
+
graph (`ReplicatedKVS_viz_collapsed.svg`) by a node labeled "??" in the
|
47
|
+
dataflow.
|
36
48
|
|
37
|
-
$
|
49
|
+
$ budplot kvs/kvs.rb ReplicatedKVS BestEffortMulticast StaticMembership
|
38
50
|
fn is ReplicatedKVS_BestEffortMulticast_StaticMembership_viz_collapsed.svg
|
39
|
-
$ open
|
40
|
-
|
41
|
-
|
42
|
-
## The Visualizer
|
51
|
+
$ open bud_doc/index.html
|
43
52
|
|
44
|
-
|
53
|
+
## Using budvis
|
45
54
|
|
46
|
-
To enable tracing, we need to set
|
55
|
+
To enable tracing, we need to set `:trace => true` in the `Bud` constructor, and
|
56
|
+
optionally provide a `:tag` to differentiate between traces by a human-readable
|
57
|
+
name (rather than by `object_id`). I modified the unit test `test/DBM_kvs.rb` as
|
58
|
+
follows:
|
47
59
|
|
48
60
|
- v = BestEffortReplicatedKVS.new(@opts.merge(:port => 12345))
|
49
61
|
- v2 = BestEffortReplicatedKVS.new(@opts.merge(:port => 12346))
|
50
62
|
|
51
|
-
+ v = BestEffortReplicatedKVS.new(@opts.merge(:
|
52
|
-
+ v2 = BestEffortReplicatedKVS.new(@opts.merge(:
|
63
|
+
+ v = BestEffortReplicatedKVS.new(@opts.merge(:port => 12345, :tag => 'dist_primary', :trace => true))
|
64
|
+
+ v2 = BestEffortReplicatedKVS.new(@opts.merge(:port => 12346, :tag => 'dist_backup', :trace => true))
|
53
65
|
|
54
66
|
|
55
67
|
Then I ran the unit test:
|
@@ -57,10 +69,10 @@ Then I ran the unit test:
|
|
57
69
|
$ ruby test/tc_kvs.rb
|
58
70
|
Loaded suite test/tc_kvs
|
59
71
|
Started
|
60
|
-
.Created directory:
|
61
|
-
Created directory:
|
62
|
-
Created directory:
|
63
|
-
Created directory:
|
72
|
+
.Created directory: DBM_BestEffortReplicatedKVS_dist_primary_2160259460_
|
73
|
+
Created directory: DBM_BestEffortReplicatedKVS_dist_primary_2160259460_/bud_
|
74
|
+
Created directory: DBM_BestEffortReplicatedKVS_dist_backup_2159579740_
|
75
|
+
Created directory: DBM_BestEffortReplicatedKVS_dist_backup_2159579740_/bud_
|
64
76
|
..
|
65
77
|
Finished in 4.366793 seconds.
|
66
78
|
|
@@ -68,8 +80,8 @@ Then I ran the unit test:
|
|
68
80
|
|
69
81
|
Then I ran the visualization utility:
|
70
82
|
|
71
|
-
$
|
83
|
+
$ budvis DBM_BestEffortReplicatedKVS_dist_primary_2160259460_/
|
72
84
|
|
73
85
|
And finally opened the (chronological) first output file:
|
74
86
|
|
75
|
-
$ open -a /Applications/Google\ Chrome.app/
|
87
|
+
$ open -a /Applications/Google\ Chrome.app/ DBM_BestEffortReplicatedKVS_dist_primary_2160259460_/tm_0_expanded.svg
|
data/examples/chat/chat.rb
CHANGED
@@ -24,9 +24,9 @@ class ChatClient
|
|
24
24
|
stdio <~ mcast { |m| [pretty_print(m.val)] }
|
25
25
|
end
|
26
26
|
|
27
|
-
# format chat messages with timestamp on the right of the screen
|
27
|
+
# format chat messages with color and timestamp on the right of the screen
|
28
28
|
def pretty_print(val)
|
29
|
-
str = val[1].to_s + ": " + (val[3].to_s || '')
|
29
|
+
str = "\033[34m"+val[1].to_s + ": " + "\033[31m" + (val[3].to_s || '') + "\033[0m"
|
30
30
|
pad = "(" + val[2].to_s + ")"
|
31
31
|
return str + " "*[66 - str.length,2].max + pad
|
32
32
|
end
|
@@ -34,12 +34,7 @@ end
|
|
34
34
|
|
35
35
|
|
36
36
|
|
37
|
-
|
38
|
-
server = ARGV[1]
|
39
|
-
else
|
40
|
-
server = ChatProtocol::DEFAULT_ADDR
|
41
|
-
end
|
42
|
-
|
37
|
+
server = (ARGV.length == 2) ? ARGV[1] : ChatProtocol::DEFAULT_ADDR
|
43
38
|
puts "Server address: #{server}"
|
44
|
-
program = ChatClient.new(ARGV[0], server, :
|
39
|
+
program = ChatClient.new(ARGV[0], server, :stdin => $stdin)
|
45
40
|
program.run_fg
|
@@ -14,15 +14,8 @@ class ChatServer
|
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
|
-
|
18
|
-
|
19
17
|
# ruby command-line wrangling
|
20
|
-
|
21
|
-
addr = ARGV.first
|
22
|
-
else
|
23
|
-
addr = ChatProtocol::DEFAULT_ADDR
|
24
|
-
end
|
25
|
-
|
18
|
+
addr = ARGV.first ? ARGV.first : ChatProtocol::DEFAULT_ADDR
|
26
19
|
ip, port = addr.split(":")
|
27
20
|
puts "Server address: #{ip}:#{port}"
|
28
21
|
program = ChatServer.new(:ip => ip, :port => port.to_i)
|
@@ -0,0 +1 @@
|
|
1
|
+
67.188.223.50:5555
|
@@ -1,26 +1,26 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'bud'
|
3
|
-
require 'tokenring'
|
4
3
|
require 'bud/deploy/ec2deploy'
|
4
|
+
require 'tokenring'
|
5
5
|
|
6
|
-
class
|
6
|
+
class RingEC2
|
7
7
|
include Bud
|
8
8
|
include TokenRing
|
9
9
|
include EC2Deploy
|
10
10
|
|
11
11
|
deploystrap do
|
12
|
+
raise "keys.rb must exist in the current directory" unless File.exists? "keys.rb"
|
13
|
+
eval(IO.read('keys.rb'), binding)
|
12
14
|
node_count << [10]
|
13
|
-
eval(IO.read('keys.rb'), binding) if File.exists?('keys.rb')
|
14
15
|
ruby_command << ["ruby tokenring-ec2.rb"]
|
15
16
|
init_dir << ["."]
|
16
17
|
end
|
17
|
-
|
18
18
|
end
|
19
19
|
|
20
20
|
ip, port = ARGV[0].split(':')
|
21
21
|
ext_ip, ext_port = ARGV[1].split(':')
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
22
|
+
RingEC2.new(:ip => ip,
|
23
|
+
:port => port,
|
24
|
+
:ext_ip => ext_ip,
|
25
|
+
:ext_port => ext_port,
|
26
|
+
:deploy => ARGV[2]).run_fg
|
@@ -1,17 +1,15 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'bud'
|
3
3
|
require 'tokenring'
|
4
|
-
require 'bud/deploy/localdeploy'
|
5
4
|
|
6
|
-
class
|
5
|
+
class RingFork
|
7
6
|
include Bud
|
8
7
|
include TokenRing
|
9
|
-
include
|
8
|
+
include ForkDeploy
|
10
9
|
|
11
10
|
deploystrap do
|
12
11
|
node_count << [10]
|
13
12
|
end
|
14
|
-
|
15
13
|
end
|
16
14
|
|
17
|
-
|
15
|
+
RingFork.new(:deploy => true).run_fg
|
@@ -2,38 +2,46 @@ require 'rubygems'
|
|
2
2
|
require 'bud'
|
3
3
|
|
4
4
|
module TokenRing
|
5
|
-
|
6
5
|
state do
|
7
|
-
table :next_node, [] => [:
|
6
|
+
table :next_node, [] => [:addr]
|
7
|
+
channel :next_node_chan, [:@loc] => [:next]
|
8
|
+
scratch :send_next_node, [:node, :next]
|
9
|
+
table :sent_next_node, [:addr]
|
8
10
|
channel :token, [:@loc]
|
9
11
|
table :token_persist, [:loc]
|
10
12
|
end
|
11
13
|
|
12
14
|
bloom :make_ring do
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
15
|
+
# Once a node and its successor have both been computed, send a message to
|
16
|
+
# the node with the address of its successor
|
17
|
+
send_next_node <= (node * node * node_ready).combos do |n1, n2, nr|
|
18
|
+
succ_id = (n1.uid + 1) % node_count[[]].num
|
19
|
+
if n2.uid == succ_id and not sent_next_node.has_key? [n1.uid]
|
20
|
+
[n1.addr, n2.addr]
|
21
|
+
end
|
19
22
|
end
|
23
|
+
next_node_chan <~ send_next_node
|
24
|
+
sent_next_node <+ send_next_node {|n| [n.node]}
|
20
25
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
[node[[0]].node] if @options [:deploy]
|
26
|
+
next_node <= next_node_chan {|n| [n.next]}
|
27
|
+
|
28
|
+
# The deployer sends an initial message to the node with ID 0
|
29
|
+
token <~ (node_ready * node).rights(:uid => :uid) do |n|
|
30
|
+
[n.addr] if (@options[:deploy] and n.uid == 0)
|
27
31
|
end
|
28
32
|
end
|
29
33
|
|
30
34
|
bloom :pass_token do
|
31
35
|
# Persist the token for as long as necessary
|
32
36
|
token_persist <= token
|
33
|
-
token_persist <-
|
37
|
+
token_persist <- (token_persist * next_node).lefts
|
34
38
|
# Pass on the token
|
35
|
-
token <~
|
36
|
-
|
39
|
+
token <~ (token_persist * next_node).rights do |nn|
|
40
|
+
[nn.addr]
|
41
|
+
end
|
37
42
|
end
|
38
43
|
|
44
|
+
bloom :print_token do
|
45
|
+
stdio <~ token {["#{@node_id}: Got token! (@ #{ip_port})"]}
|
46
|
+
end
|
39
47
|
end
|
data/lib/bud/aggs.rb
CHANGED
@@ -4,9 +4,21 @@ module Bud
|
|
4
4
|
def init(val)
|
5
5
|
val
|
6
6
|
end
|
7
|
+
|
8
|
+
# In order to support argagg, trans must return a pair:
|
9
|
+
# 1. the running aggregate state
|
10
|
+
# 2. a flag to indicate what the caller should do with the input tuple for argaggs
|
11
|
+
# a. :ignore tells the caller to ignore this input
|
12
|
+
# b. :keep tells the caller to save this input
|
13
|
+
# c. :replace tells the caller to keep this input alone
|
14
|
+
# d. [:delete, t1, t2, ...] tells the caller to delete the remaining tuples
|
15
|
+
# For things that do not descend from ArgExemplary, the 2nd part can simply be nil.
|
16
|
+
def trans(the_state, val)
|
17
|
+
return the_state, :ignore
|
18
|
+
end
|
7
19
|
|
8
|
-
def final(
|
9
|
-
|
20
|
+
def final(the_state)
|
21
|
+
the_state
|
10
22
|
end
|
11
23
|
end
|
12
24
|
|
@@ -18,17 +30,21 @@ module Bud
|
|
18
30
|
# is discarded in favor of another, v can never be the final result
|
19
31
|
|
20
32
|
class ArgExemplary < Agg #:nodoc: all
|
21
|
-
def tie(
|
22
|
-
(
|
33
|
+
def tie(the_state, val)
|
34
|
+
(the_state == val)
|
23
35
|
end
|
24
|
-
def final(
|
25
|
-
|
36
|
+
def final(the_state)
|
37
|
+
the_state
|
26
38
|
end
|
27
39
|
end
|
28
40
|
|
29
41
|
class Min < ArgExemplary #:nodoc: all
|
30
|
-
def trans(
|
31
|
-
|
42
|
+
def trans(the_state, val)
|
43
|
+
if the_state < val
|
44
|
+
return the_state, :ignore
|
45
|
+
else
|
46
|
+
return val, :replace
|
47
|
+
end
|
32
48
|
end
|
33
49
|
end
|
34
50
|
# exemplary aggregate method to be used in Bud::BudCollection.group.
|
@@ -38,8 +54,12 @@ module Bud
|
|
38
54
|
end
|
39
55
|
|
40
56
|
class Max < ArgExemplary #:nodoc: all
|
41
|
-
def trans(
|
42
|
-
|
57
|
+
def trans(the_state, val)
|
58
|
+
if the_state > val
|
59
|
+
return the_state, :ignore
|
60
|
+
else
|
61
|
+
return val, :replace
|
62
|
+
end
|
43
63
|
end
|
44
64
|
end
|
45
65
|
# exemplary aggregate method to be used in Bud::BudCollection.group.
|
@@ -49,10 +69,14 @@ module Bud
|
|
49
69
|
end
|
50
70
|
|
51
71
|
class Choose < ArgExemplary #:nodoc: all
|
52
|
-
def trans(
|
53
|
-
|
54
|
-
|
55
|
-
|
72
|
+
def trans(the_state, val)
|
73
|
+
if the_state.nil?
|
74
|
+
return val, :replace
|
75
|
+
else
|
76
|
+
return the_state, :ignore
|
77
|
+
end
|
78
|
+
end
|
79
|
+
def tie(the_state, val)
|
56
80
|
false
|
57
81
|
end
|
58
82
|
end
|
@@ -62,10 +86,46 @@ module Bud
|
|
62
86
|
def choose(x)
|
63
87
|
[Choose.new, x]
|
64
88
|
end
|
89
|
+
|
90
|
+
class ChooseRand < ArgExemplary #:nodoc: all
|
91
|
+
@@reservoir_size = 1 # Vitter's reservoir sampling, k = 1
|
92
|
+
def init(x=nil)
|
93
|
+
the_state = {:cnt => 1, :vals => [x]}
|
94
|
+
end
|
95
|
+
|
96
|
+
def trans(the_state, val)
|
97
|
+
the_state[:cnt] += 1
|
98
|
+
if the_state[:cnt] < @@reservoir_size
|
99
|
+
the_state[:vals] << val
|
100
|
+
return the_state, :keep
|
101
|
+
else
|
102
|
+
j = rand(the_state[:cnt])
|
103
|
+
if j < @@reservoir_size
|
104
|
+
old_tup = the_state[:vals][j]
|
105
|
+
the_state[:vals][j] = val
|
106
|
+
return the_state, [:delete, old_tup]
|
107
|
+
else
|
108
|
+
return the_state, :keep
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
def tie(the_state, val)
|
113
|
+
true
|
114
|
+
end
|
115
|
+
def final(the_state)
|
116
|
+
the_state[:vals][rand(the_state[@@reservoir_size])]
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# exemplary aggregate method to be used in Bud::BudCollection.group.
|
121
|
+
# randomly chooses among x entries being aggregated.
|
122
|
+
def choose_rand(x=nil)
|
123
|
+
[ChooseRand.new]
|
124
|
+
end
|
65
125
|
|
66
126
|
class Sum < Agg #:nodoc: all
|
67
|
-
def trans(
|
68
|
-
|
127
|
+
def trans(the_state, val)
|
128
|
+
return the_state + val, nil
|
69
129
|
end
|
70
130
|
end
|
71
131
|
|
@@ -79,8 +139,8 @@ module Bud
|
|
79
139
|
def init(x=nil)
|
80
140
|
1
|
81
141
|
end
|
82
|
-
def trans(
|
83
|
-
|
142
|
+
def trans(the_state, x=nil)
|
143
|
+
return the_state + 1, nil
|
84
144
|
end
|
85
145
|
end
|
86
146
|
|
@@ -94,12 +154,13 @@ module Bud
|
|
94
154
|
def init(val)
|
95
155
|
[val, 1]
|
96
156
|
end
|
97
|
-
def trans(
|
98
|
-
retval = [
|
99
|
-
retval << (
|
157
|
+
def trans(the_state, val)
|
158
|
+
retval = [the_state[0] + val]
|
159
|
+
retval << (the_state[1] + 1)
|
160
|
+
return retval, nil
|
100
161
|
end
|
101
|
-
def final(
|
102
|
-
|
162
|
+
def final(the_state)
|
163
|
+
the_state[0]*1.0 / the_state[1]
|
103
164
|
end
|
104
165
|
end
|
105
166
|
|
@@ -113,8 +174,9 @@ module Bud
|
|
113
174
|
def init(x)
|
114
175
|
[x]
|
115
176
|
end
|
116
|
-
def trans(
|
117
|
-
|
177
|
+
def trans(the_state, val)
|
178
|
+
the_state << val
|
179
|
+
return the_state, nil
|
118
180
|
end
|
119
181
|
end
|
120
182
|
|
data/lib/bud/bud_meta.rb
CHANGED
@@ -17,16 +17,19 @@ class BudMeta #:nodoc: all
|
|
17
17
|
stratum_map = binaryrel2map(@bud_instance.t_stratum)
|
18
18
|
|
19
19
|
rewritten_strata = Array.new(top_stratum + 2) { [] }
|
20
|
+
no_attr_rewrite_strata = Array.new(top_stratum + 2) { [] }
|
20
21
|
@bud_instance.t_rules.each do |d|
|
21
22
|
if d.op.to_s == '<='
|
22
|
-
#
|
23
|
-
#
|
23
|
+
# Deductive rules are assigned to strata based on the basic Datalog
|
24
|
+
# stratification algorithm
|
24
25
|
belongs_in = stratum_map[d.lhs]
|
25
26
|
belongs_in ||= 0
|
26
27
|
rewritten_strata[belongs_in] << d.src
|
28
|
+
no_attr_rewrite_strata[belongs_in] << d.orig_src
|
27
29
|
else
|
28
|
-
#
|
30
|
+
# All temporal rules are placed in the last stratum
|
29
31
|
rewritten_strata[top_stratum + 1] << d.src
|
32
|
+
no_attr_rewrite_strata[top_stratum + 1] << d.orig_src
|
30
33
|
end
|
31
34
|
end
|
32
35
|
|
@@ -41,7 +44,7 @@ class BudMeta #:nodoc: all
|
|
41
44
|
end
|
42
45
|
dump_rewrite(rewritten_strata) if @bud_instance.options[:dump_rewrite]
|
43
46
|
|
44
|
-
return rewritten_strata
|
47
|
+
return rewritten_strata, no_attr_rewrite_strata
|
45
48
|
end
|
46
49
|
|
47
50
|
def binaryrel2map(rel)
|
@@ -82,18 +85,25 @@ class BudMeta #:nodoc: all
|
|
82
85
|
|
83
86
|
pt = Unifier.new.process(parse_tree)
|
84
87
|
pp pt if @bud_instance.options[:dump_ast]
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
88
|
+
|
89
|
+
rv = check_rule_ast(pt)
|
90
|
+
unless rv.nil?
|
91
|
+
if rv.class <= Sexp
|
92
|
+
error_pt = rv
|
93
|
+
error_msg = "Parse error"
|
94
|
+
else
|
95
|
+
error_pt, error_msg = rv
|
96
|
+
end
|
97
|
+
|
98
|
+
# try to "generate" the source code associated with the problematic block,
|
99
|
+
# so as to generate a more meaningful error message.
|
91
100
|
begin
|
92
|
-
code = Ruby2Ruby.new.process(
|
93
|
-
|
94
|
-
|
101
|
+
code = Ruby2Ruby.new.process(Marshal.load(Marshal.dump(error_pt)))
|
102
|
+
src_msg = "\nCode: #{code}"
|
103
|
+
rescue Exception
|
104
|
+
src_msg = ""
|
95
105
|
end
|
96
|
-
raise
|
106
|
+
raise Bud::CompileError, "#{error_msg} in rule block \"#{block_name}\"#{src_msg}"
|
97
107
|
end
|
98
108
|
|
99
109
|
rewriter = RuleRewriter.new(seed, @bud_instance)
|
@@ -103,37 +113,38 @@ class BudMeta #:nodoc: all
|
|
103
113
|
|
104
114
|
# Perform some basic sanity checks on the AST of a rule block. We expect a
|
105
115
|
# rule block to consist of a :defn, a nested :scope, and then a sequence of
|
106
|
-
# statements. Each statement is a :call node.
|
116
|
+
# statements. Each statement is a :call node. Returns nil (no error found), a
|
117
|
+
# Sexp (containing an error), or a pair of [Sexp, error message].
|
107
118
|
def check_rule_ast(pt)
|
108
119
|
# :defn format: node tag, block name, args, nested scope
|
109
|
-
|
120
|
+
return pt if pt.sexp_type != :defn
|
110
121
|
scope = pt[3]
|
111
|
-
|
122
|
+
return pt if scope.sexp_type != :scope
|
112
123
|
block = scope[1]
|
113
124
|
|
114
125
|
block.each_with_index do |n,i|
|
115
126
|
if i == 0
|
116
|
-
|
127
|
+
return pt if n != :block
|
117
128
|
next
|
118
129
|
end
|
119
130
|
|
120
|
-
|
121
|
-
|
131
|
+
# Check for a common case
|
132
|
+
if n.sexp_type == :lasgn
|
133
|
+
return [n, "Illegal operator: '='"]
|
134
|
+
end
|
135
|
+
return pt unless n.sexp_type == :call and n.length == 4
|
122
136
|
|
123
137
|
# Rule format: call tag, lhs, op, rhs
|
124
138
|
tag, lhs, op, rhs = n
|
125
139
|
|
126
140
|
# Check that LHS references a named collection
|
127
|
-
|
141
|
+
return n if lhs.nil? or lhs.sexp_type != :call
|
128
142
|
lhs_name = lhs[2]
|
129
143
|
unless @bud_instance.tables.has_key? lhs_name.to_sym
|
130
|
-
|
144
|
+
return [n, "Table does not exist: '#{lhs_name}'"]
|
131
145
|
end
|
132
146
|
|
133
|
-
|
134
|
-
unless [:<, :<=].include? op
|
135
|
-
raise Bud::CompileError, "Illegal operator: '#{op}'"
|
136
|
-
end
|
147
|
+
return [n, "Illegal operator: '#{op}'"] unless [:<, :<=].include? op
|
137
148
|
|
138
149
|
# Check superator invocation. A superator that begins with "<" is parsed
|
139
150
|
# as a call to the binary :< operator. The right operand to :< is a :call
|
@@ -143,16 +154,17 @@ class BudMeta #:nodoc: all
|
|
143
154
|
# XXX: We don't check for illegal superators (e.g., "<--"). That would be
|
144
155
|
# tricky, because they are encoded as a nested unary op in the rule body.
|
145
156
|
if op == :<
|
146
|
-
|
157
|
+
return n unless rhs.sexp_type == :arglist
|
147
158
|
body = rhs[1]
|
148
|
-
|
159
|
+
return n unless body.sexp_type == :call
|
149
160
|
op_tail = body[2]
|
150
|
-
|
161
|
+
return n unless [:~, :-@, :+@].include? op_tail
|
151
162
|
rhs_args = body[3]
|
152
|
-
|
153
|
-
raise Bud::CompileError if rhs_args.length != 1
|
163
|
+
return n if rhs_args.sexp_type != :arglist or rhs_args.length != 1
|
154
164
|
end
|
155
165
|
end
|
166
|
+
|
167
|
+
return nil # No errors found
|
156
168
|
end
|
157
169
|
|
158
170
|
def stratify
|
@@ -162,6 +174,11 @@ class BudMeta #:nodoc: all
|
|
162
174
|
|
163
175
|
# Copy computed data back into Bud runtime
|
164
176
|
strat.stratum.each {|s| @bud_instance.t_stratum << s}
|
177
|
+
@bud_instance.stratum_collection_map = strat.stratum.inject({}) do |memo, t|
|
178
|
+
memo[t[1]] ||= []
|
179
|
+
memo[t[1]] << t[0]
|
180
|
+
memo
|
181
|
+
end
|
165
182
|
strat.depends_tc.each {|d| @bud_instance.t_depends_tc << d}
|
166
183
|
strat.cycle.each {|c| @bud_instance.t_cycle << c}
|
167
184
|
if strat.top_strat.length > 0
|