bud 0.0.8 → 0.1.0.pre1
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 +4 -10
- data/bin/budplot +1 -2
- data/docs/cheat.md +2 -15
- data/examples/basics/paths.rb +7 -7
- data/lib/bud/aggs.rb +15 -19
- data/lib/bud/bud_meta.rb +165 -77
- data/lib/bud/bust/bust.rb +11 -4
- data/lib/bud/collections.rb +643 -280
- data/lib/bud/depanalysis.rb +50 -25
- data/lib/bud/executor/elements.rb +592 -0
- data/lib/bud/executor/group.rb +104 -0
- data/lib/bud/executor/join.rb +638 -0
- data/lib/bud/graphs.rb +12 -11
- data/lib/bud/joins.rb +2 -1
- data/lib/bud/meta_algebra.rb +5 -4
- data/lib/bud/metrics.rb +9 -3
- data/lib/bud/monkeypatch.rb +131 -23
- data/lib/bud/rebl.rb +41 -28
- data/lib/bud/rewrite.rb +112 -440
- data/lib/bud/server.rb +3 -2
- data/lib/bud/source.rb +109 -0
- data/lib/bud/state.rb +16 -9
- data/lib/bud/storage/dbm.rb +62 -16
- data/lib/bud/storage/zookeeper.rb +2 -2
- data/lib/bud/viz.rb +8 -4
- data/lib/bud/viz_util.rb +10 -9
- data/lib/bud.rb +413 -199
- metadata +40 -55
- data/examples/deploy/tokenring-ec2.rb +0 -26
- data/examples/deploy/tokenring-fork.rb +0 -15
- data/examples/deploy/tokenring-thread.rb +0 -15
- data/examples/deploy/tokenring.rb +0 -47
- data/lib/bud/deploy/deployer.rb +0 -67
- data/lib/bud/deploy/ec2deploy.rb +0 -199
- data/lib/bud/deploy/forkdeploy.rb +0 -90
- data/lib/bud/deploy/threaddeploy.rb +0 -38
- data/lib/bud/storage/tokyocabinet.rb +0 -190
- data/lib/bud/stratify.rb +0 -85
data/README
CHANGED
@@ -10,15 +10,11 @@ documentation.
|
|
10
10
|
|
11
11
|
Main deficiencies at this point are:
|
12
12
|
|
13
|
-
- Inefficient evaluation: Programs are run using semi-naive evaluation
|
14
|
-
strategies, but no further query optimization has been implemented, and little
|
15
|
-
effort has been spent in tuning.
|
16
|
-
|
17
13
|
- No Ruby constraints: Within Bloom programs the full power of Ruby is also
|
18
14
|
available, including mutable state. This allows programmers to get outside
|
19
15
|
the Bloom framework and lose cleanliness.
|
20
16
|
|
21
|
-
- Compatibility: Bud only works with Ruby (MRI) 1.8
|
17
|
+
- Compatibility: Bud only works with Ruby (MRI) 1.8 and 1.9. JRuby and other
|
22
18
|
Ruby implementations are currently not supported.
|
23
19
|
|
24
20
|
## Installation
|
@@ -39,8 +35,6 @@ To run the unit tests:
|
|
39
35
|
|
40
36
|
## Optional Dependencies
|
41
37
|
|
42
|
-
The bud gem has a handful of mandatory dependencies. It also has
|
43
|
-
|
44
|
-
|
45
|
-
before installing the "tokyocabinet" gem, the Tokyo Cabinet libraries should be
|
46
|
-
installed first.
|
38
|
+
The bud gem has a handful of mandatory dependencies. It also has one optional
|
39
|
+
dependency: if you wish to use Bud collections backed by Zookeeper, the
|
40
|
+
"zookeeper" gem must be installed.
|
data/bin/budplot
CHANGED
@@ -2,7 +2,6 @@
|
|
2
2
|
require 'rubygems'
|
3
3
|
require 'bud'
|
4
4
|
require 'bud/bud_meta'
|
5
|
-
require 'bud/depanalysis'
|
6
5
|
require 'bud/graphs'
|
7
6
|
require 'bud/meta_algebra'
|
8
7
|
require 'bud/viz_util'
|
@@ -33,7 +32,7 @@ def make_instance(mods)
|
|
33
32
|
if mods.length == 1
|
34
33
|
return mod_klass.new
|
35
34
|
else
|
36
|
-
puts "Error: cannot intermix
|
35
|
+
puts "Error: cannot intermix class \"#{mod_klass}\" with modules"
|
37
36
|
exit
|
38
37
|
end
|
39
38
|
elsif mod_klass.class != Module
|
data/docs/cheat.md
CHANGED
@@ -114,13 +114,12 @@ Built-in scratch collection to be used on the lhs of a rule; permanently halts t
|
|
114
114
|
If the item `[:kill]` is inserted, the Bud OS process (including all Bud instances) is also halted.
|
115
115
|
|
116
116
|
### sync ###
|
117
|
-
Persistent collection mapped to an external storage engine, with synchronous write-flushing each timestep.
|
117
|
+
Persistent collection mapped to an external storage engine, with synchronous write-flushing each timestep.
|
118
118
|
Default attributes: `[:key] => [:val]`.
|
119
119
|
|
120
120
|
sync :s1, :dbm
|
121
|
-
sync :s2, :tokyo, [:k1, :k2] => [:v1, :v2]
|
122
121
|
|
123
|
-
|
122
|
+
Currently only [dbm](http://en.wikipedia.org/wiki/Dbm) is supported. Support for tokyo cabinet present in an earlier release has been removed.<br>
|
124
123
|
|
125
124
|
### store ###
|
126
125
|
Persistent collection mapped to an external storage engine, with asynchronous write-flushing. Supported storage engines: `:zookeeper`.<br>
|
@@ -173,8 +172,6 @@ implicit map:
|
|
173
172
|
|
174
173
|
`flat_map`:
|
175
174
|
|
176
|
-
require 'backports' # flat_map not included in Ruby 1.8 by default
|
177
|
-
|
178
175
|
t3 <= bc.flat_map do |t| # unnest a collection-valued attribute
|
179
176
|
bc.col4.map { |sub| [t.col1, t.col2, t.col3, sub] }
|
180
177
|
end
|
@@ -323,16 +320,6 @@ The schema of a temp collection in inherited from the rhs; if the rhs has no
|
|
323
320
|
schema, a simple one is manufactured to suit the data found in the rhs at
|
324
321
|
runtime: `[c0, c1, ...]`.
|
325
322
|
|
326
|
-
`with`<br>
|
327
|
-
With statements define a temp collection that can be referenced only within the scope of the associated block. They are useful when you "fork" in a dataflow into two lhs destinations:
|
328
|
-
|
329
|
-
with :biggies <= request {|r| r if r.quantity > 100}, begin
|
330
|
-
to_process <= (biggies * known_good).lefts(:key=>:key)
|
331
|
-
denied <= (biggies * known_good).nopairs(:key=>key)
|
332
|
-
end
|
333
|
-
|
334
|
-
The advantage of using `with` over `temp` is modularity: all the rules referencing `biggies` have to be bundled together, making it easier to see that the contents of `request` with quantity > 100 are handled properly.
|
335
|
-
|
336
323
|
## Bud Modules ##
|
337
324
|
A Bud module combines state (collections) and logic (Bloom rules). Using modules allows your program to be decomposed into a collection of smaller units.
|
338
325
|
|
data/examples/basics/paths.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# simple shortest paths
|
2
|
-
# note use of program.tick at bottom to run a single
|
2
|
+
# note use of program.tick at bottom to run a single timestep
|
3
3
|
# and inspect relations
|
4
4
|
require 'rubygems'
|
5
5
|
require 'bud'
|
@@ -9,19 +9,19 @@ class ShortestPaths
|
|
9
9
|
|
10
10
|
state do
|
11
11
|
table :link, [:from, :to, :cost]
|
12
|
-
table :path, [:from, :to, :
|
13
|
-
table :shortest, [:from, :to] => [:
|
12
|
+
table :path, [:from, :to, :nxt, :cost]
|
13
|
+
table :shortest, [:from, :to] => [:nxt, :cost]
|
14
14
|
end
|
15
15
|
|
16
16
|
# recursive rules to define all paths from links
|
17
17
|
bloom :make_paths do
|
18
18
|
# base case: every link is a path
|
19
|
-
path <= link {|
|
19
|
+
path <= link {|l| [l.from, l.to, l.to, l.cost]}
|
20
20
|
|
21
21
|
# inductive case: make path of length n+1 by connecting a link to a path of
|
22
22
|
# length n
|
23
23
|
path <= (link*path).pairs(:to => :from) do |l,p|
|
24
|
-
[l.from, p.to,
|
24
|
+
[l.from, p.to, l.to, l.cost+p.cost]
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
@@ -43,11 +43,11 @@ program.link <= [['a', 'b', 1],
|
|
43
43
|
['d', 'e', 1]]
|
44
44
|
|
45
45
|
program.tick # one timestamp is enough for this simple program
|
46
|
-
program.shortest.sort.each {|t| puts t.inspect}
|
46
|
+
program.shortest.to_a.sort.each {|t| puts t.inspect}
|
47
47
|
|
48
48
|
puts "----"
|
49
49
|
|
50
50
|
# now lets add an extra link and recompute
|
51
51
|
program.link << ['e', 'f', 1]
|
52
52
|
program.tick
|
53
|
-
program.shortest.sort.each {|t| puts t.inspect}
|
53
|
+
program.shortest.to_a.sort.each {|t| puts t.inspect}
|
data/lib/bud/aggs.rb
CHANGED
@@ -11,7 +11,7 @@ module Bud
|
|
11
11
|
# a. :ignore tells the caller to ignore this input
|
12
12
|
# b. :keep tells the caller to save this input
|
13
13
|
# c. :replace tells the caller to keep this input alone
|
14
|
-
# d.
|
14
|
+
# d. :delete, [t1, t2, ...] tells the caller to delete the remaining tuples
|
15
15
|
# For things that do not descend from ArgExemplary, the 2nd part can simply be nil.
|
16
16
|
def trans(the_state, val)
|
17
17
|
return the_state, :ignore
|
@@ -42,6 +42,8 @@ module Bud
|
|
42
42
|
def trans(the_state, val)
|
43
43
|
if the_state < val
|
44
44
|
return the_state, :ignore
|
45
|
+
elsif the_state == val
|
46
|
+
return the_state, :keep
|
45
47
|
else
|
46
48
|
return val, :replace
|
47
49
|
end
|
@@ -86,41 +88,35 @@ module Bud
|
|
86
88
|
def choose(x)
|
87
89
|
[Choose.new, x]
|
88
90
|
end
|
89
|
-
|
90
|
-
class
|
91
|
-
|
92
|
-
|
93
|
-
the_state = {:cnt => 1, :vals => [x]}
|
91
|
+
|
92
|
+
class ChooseOneRand < ArgExemplary #:nodoc: all
|
93
|
+
def init(x=nil) # Vitter's reservoir sampling, sample size = 1
|
94
|
+
the_state = {:cnt => 1, :val => x}
|
94
95
|
end
|
95
96
|
|
96
97
|
def trans(the_state, val)
|
97
98
|
the_state[:cnt] += 1
|
98
|
-
|
99
|
-
|
100
|
-
|
99
|
+
j = rand(the_state[:cnt])
|
100
|
+
if j < 1 # replace
|
101
|
+
old_val = the_state[:val]
|
102
|
+
the_state[:val] = val
|
103
|
+
return the_state, :replace
|
101
104
|
else
|
102
|
-
|
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
|
105
|
+
return the_state, :ignore
|
110
106
|
end
|
111
107
|
end
|
112
108
|
def tie(the_state, val)
|
113
109
|
true
|
114
110
|
end
|
115
111
|
def final(the_state)
|
116
|
-
the_state[:
|
112
|
+
the_state[:val] # XXX rand(@@reservoir_size will always be 0, since @@reservoir_size is 1
|
117
113
|
end
|
118
114
|
end
|
119
115
|
|
120
116
|
# exemplary aggregate method to be used in Bud::BudCollection.group.
|
121
117
|
# randomly chooses among x entries being aggregated.
|
122
118
|
def choose_rand(x=nil)
|
123
|
-
[
|
119
|
+
[ChooseOneRand.new, x]
|
124
120
|
end
|
125
121
|
|
126
122
|
class Sum < Agg #:nodoc: all
|
data/lib/bud/bud_meta.rb
CHANGED
@@ -1,66 +1,45 @@
|
|
1
1
|
require 'bud/rewrite'
|
2
|
-
require 'bud/state'
|
3
|
-
require 'parse_tree'
|
4
2
|
require 'pp'
|
5
3
|
|
6
|
-
class BudMeta #:nodoc: all
|
7
|
-
attr_reader :depanalysis
|
8
4
|
|
5
|
+
class BudMeta #:nodoc: all
|
9
6
|
def initialize(bud_instance, declarations)
|
10
7
|
@bud_instance = bud_instance
|
11
8
|
@declarations = declarations
|
9
|
+
@dependency_analysis = nil # the results of bud_meta are analyzed further using a helper bloom instance. See depanalysis())
|
12
10
|
end
|
13
11
|
|
14
12
|
def meta_rewrite
|
15
|
-
shred_rules
|
16
|
-
top_stratum = stratify
|
17
|
-
stratum_map = binaryrel2map(@bud_instance.t_stratum)
|
18
|
-
|
19
|
-
rewritten_strata = Array.new(top_stratum + 2) { [] }
|
20
|
-
no_attr_rewrite_strata = Array.new(top_stratum + 2) { [] }
|
21
|
-
@bud_instance.t_rules.each do |d|
|
22
|
-
if d.op.to_s == '<='
|
23
|
-
# Deductive rules are assigned to strata based on the basic Datalog
|
24
|
-
# stratification algorithm
|
25
|
-
belongs_in = stratum_map[d.lhs]
|
26
|
-
belongs_in ||= 0
|
27
|
-
rewritten_strata[belongs_in] << d.src
|
28
|
-
no_attr_rewrite_strata[belongs_in] << d.orig_src
|
29
|
-
else
|
30
|
-
# All temporal rules are placed in the last stratum
|
31
|
-
rewritten_strata[top_stratum + 1] << d.src
|
32
|
-
no_attr_rewrite_strata[top_stratum + 1] << d.orig_src
|
33
|
-
end
|
34
|
-
end
|
13
|
+
shred_rules # capture dependencies, rewrite rules
|
35
14
|
|
36
|
-
|
37
|
-
@bud_instance.
|
38
|
-
|
39
|
-
3.times { @depanalysis.tick_internal }
|
15
|
+
stratified_rules = []
|
16
|
+
if @bud_instance.toplevel == @bud_instance
|
17
|
+
nodes, stratum_map, top_stratum = stratify_preds
|
40
18
|
|
41
|
-
|
42
|
-
puts "Warning: underspecified dataflow: #{u.inspect}"
|
43
|
-
@bud_instance.t_underspecified << u
|
44
|
-
end
|
45
|
-
@depanalysis.source.each do |s|
|
46
|
-
@bud_instance.sources[s.first] = true
|
47
|
-
end
|
48
|
-
@depanalysis.sink.each do |s|
|
49
|
-
@bud_instance.sinks[s.first] = true
|
50
|
-
end
|
51
|
-
|
52
|
-
dump_rewrite(no_attr_rewrite_strata) if @bud_instance.options[:dump_rewrite]
|
19
|
+
# stratum_map = {fully qualified pred => stratum}
|
53
20
|
|
54
|
-
|
55
|
-
|
21
|
+
#slot each rule into the stratum corresponding to its lhs pred (from stratum_map)
|
22
|
+
stratified_rules = Array.new(top_stratum + 2) { [] } # stratum -> [ rules ]
|
23
|
+
@bud_instance.t_rules.each do |rule|
|
24
|
+
if rule.op.to_s == '<='
|
25
|
+
# Deductive rules are assigned to strata based on the basic Datalog
|
26
|
+
# stratification algorithm
|
27
|
+
belongs_in = stratum_map[rule.lhs]
|
28
|
+
belongs_in ||= 0
|
29
|
+
stratified_rules[belongs_in] << rule
|
30
|
+
else
|
31
|
+
# All temporal rules are placed in the last stratum
|
32
|
+
stratified_rules[top_stratum + 1] << rule
|
33
|
+
end
|
34
|
+
end
|
35
|
+
# stratified_rules[0] may be empty if none of the nodes at stratum 0 are on the lhs
|
36
|
+
# stratified_rules[top_stratum+1] will be empty if there are no temporal rules.
|
37
|
+
# Cleanup
|
38
|
+
stratified_rules = stratified_rules.reject{|r| r.empty?}
|
39
|
+
dump_rewrite(stratified_rules) if @bud_instance.options[:dump_rewrite]
|
56
40
|
|
57
|
-
def binaryrel2map(rel)
|
58
|
-
map = {}
|
59
|
-
rel.each do |s|
|
60
|
-
raise Bud::Error unless s.length == 2
|
61
|
-
map[s[0]] = s[1]
|
62
41
|
end
|
63
|
-
return
|
42
|
+
return stratified_rules
|
64
43
|
end
|
65
44
|
|
66
45
|
def shred_rules
|
@@ -87,11 +66,18 @@ class BudMeta #:nodoc: all
|
|
87
66
|
end
|
88
67
|
|
89
68
|
def rewrite_rule_block(klass, block_name, seed)
|
90
|
-
|
91
|
-
return unless parse_tree.first
|
69
|
+
return unless klass.respond_to? :__bloom_asts__
|
92
70
|
|
93
|
-
pt =
|
71
|
+
pt = klass.__bloom_asts__[block_name]
|
72
|
+
return if pt.nil?
|
73
|
+
|
74
|
+
pt = Marshal.load(Marshal.dump(pt)) #deep clone because RuleRewriter mucks up pt.
|
94
75
|
pp pt if @bud_instance.options[:dump_ast]
|
76
|
+
tmp_expander = TempExpander.new
|
77
|
+
pt = tmp_expander.process(pt)
|
78
|
+
tmp_expander.tmp_tables.each do |t|
|
79
|
+
@bud_instance.temp(t.to_sym)
|
80
|
+
end
|
95
81
|
|
96
82
|
rv = check_rule_ast(pt)
|
97
83
|
unless rv.nil?
|
@@ -112,12 +98,30 @@ class BudMeta #:nodoc: all
|
|
112
98
|
end
|
113
99
|
raise Bud::CompileError, "#{error_msg} in rule block \"#{block_name}\"#{src_msg}"
|
114
100
|
end
|
115
|
-
|
116
101
|
rewriter = RuleRewriter.new(seed, @bud_instance)
|
117
102
|
rewriter.process(pt)
|
118
103
|
return rewriter
|
119
104
|
end
|
120
105
|
|
106
|
+
def get_qual_name(pt)
|
107
|
+
# expect to see a parse tree corresponding to a dotted name
|
108
|
+
# a.b.c == s(:call, s1, :c, (:args))
|
109
|
+
# where s1 == s(:call, s2, :b, (:args))
|
110
|
+
# where s2 == s(:call, nil,:a, (:args))
|
111
|
+
|
112
|
+
tag, recv, name, args = pt
|
113
|
+
return nil unless tag == :call or args.length == 1
|
114
|
+
|
115
|
+
if recv
|
116
|
+
qn = get_qual_name(recv)
|
117
|
+
return nil if qn.nil? or qn.size == 0
|
118
|
+
qn = qn + "." + name.to_s
|
119
|
+
else
|
120
|
+
qn = name.to_s
|
121
|
+
end
|
122
|
+
qn
|
123
|
+
end
|
124
|
+
|
121
125
|
# Perform some basic sanity checks on the AST of a rule block. We expect a
|
122
126
|
# rule block to consist of a :defn, a nested :scope, and then a sequence of
|
123
127
|
# statements. Each statement is a :call node. Returns nil (no error found), a
|
@@ -147,10 +151,9 @@ class BudMeta #:nodoc: all
|
|
147
151
|
tag, lhs, op, rhs = n
|
148
152
|
|
149
153
|
# Check that LHS references a named collection
|
150
|
-
|
151
|
-
lhs_name
|
152
|
-
|
153
|
-
return [n, "collection does not exist: '#{lhs_name}'"]
|
154
|
+
lhs_name = get_qual_name(lhs)
|
155
|
+
unless lhs_name and @bud_instance.tables.has_key? lhs_name.to_sym
|
156
|
+
return [n, "Collection does not exist: '#{lhs_name}'"]
|
154
157
|
end
|
155
158
|
|
156
159
|
return [n, "illegal operator: '#{op}'"] unless [:<, :<=].include? op
|
@@ -176,36 +179,121 @@ class BudMeta #:nodoc: all
|
|
176
179
|
return nil # No errors found
|
177
180
|
end
|
178
181
|
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
@bud_instance.
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
182
|
+
|
183
|
+
Node = Struct.new :name, :status, :stratum, :edges, :in_lhs, :in_body, :in_cycle, :is_neg_head
|
184
|
+
# Node.status is one of :init, :in_progress, :done
|
185
|
+
Edge = Struct.new :to, :op, :neg, :temporal
|
186
|
+
|
187
|
+
def stratify_preds
|
188
|
+
bud = @bud_instance.toplevel
|
189
|
+
nodes = {}
|
190
|
+
bud.t_depends.each do |d|
|
191
|
+
#t_depends [:bud_instance, :rule_id, :lhs, :op, :body] => [:nm]
|
192
|
+
lhs = (nodes[d.lhs.to_s] ||= Node.new(d.lhs.to_s, :init, 0, [], true, false, false, false))
|
193
|
+
lhs.in_lhs = true
|
194
|
+
body = (nodes[d.body.to_s] ||= Node.new(d.body.to_s, :init, 0, [], false, true, false, false))
|
195
|
+
temporal = d.op != "<="
|
196
|
+
lhs.edges << Edge.new(body, d.op, d.nm, temporal)
|
197
|
+
body.in_body = true
|
190
198
|
end
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
199
|
+
|
200
|
+
nodes.values.each {|n| calc_stratum(n, false, false, [n.name])}
|
201
|
+
# Normalize stratum numbers because they may not be 0-based or consecutive
|
202
|
+
remap = {}
|
203
|
+
# if the nodes stratum numbers are [2, 3, 2, 4], remap = {2 => 0, 3 => 1, 4 => 2}
|
204
|
+
nodes.values.map {|n| n.stratum}.uniq.sort.each_with_index{|num, i|
|
205
|
+
remap[num] = i
|
206
|
+
}
|
207
|
+
stratum_map = {}
|
208
|
+
top_stratum = -1
|
209
|
+
nodes.each_pair do |name, n|
|
210
|
+
n.stratum = remap[n.stratum]
|
211
|
+
stratum_map[n.name] = n.stratum
|
212
|
+
top_stratum = max(top_stratum, n.stratum)
|
197
213
|
end
|
214
|
+
analyze_dependencies(nodes)
|
215
|
+
return nodes, stratum_map, top_stratum
|
216
|
+
end
|
198
217
|
|
199
|
-
|
218
|
+
def max(a, b) ; a > b ? a : b ; end
|
219
|
+
|
220
|
+
def calc_stratum(node, neg, temporal, path)
|
221
|
+
if node.status == :in_process
|
222
|
+
node.in_cycle = true
|
223
|
+
if neg and !temporal and node.is_neg_head
|
224
|
+
raise Bud::CompileError, "unstratifiable program: #{path.uniq.join(',')}"
|
225
|
+
end
|
226
|
+
elsif node.status == :init
|
227
|
+
node.status = :in_process
|
228
|
+
node.edges.each do |edge|
|
229
|
+
node.is_neg_head = edge.neg
|
230
|
+
next if edge.op != "<="
|
231
|
+
body_stratum = calc_stratum(edge.to, (neg or edge.neg), (edge.temporal or temporal), path + [edge.to.name])
|
232
|
+
node.is_neg_head = false #reset for next edge
|
233
|
+
node.stratum = max(node.stratum, body_stratum + (edge.neg ? 1 : 0))
|
234
|
+
end
|
235
|
+
node.status = :done
|
236
|
+
end
|
237
|
+
node.stratum
|
238
|
+
end
|
239
|
+
|
240
|
+
|
241
|
+
def analyze_dependencies(nodes) # nodes = {node name => node}
|
242
|
+
bud = @bud_instance
|
243
|
+
|
244
|
+
preds_in_lhs = nodes.inject(Set.new) {|preds, name_n| preds.add(name_n[0]) if name_n[1].in_lhs; preds}
|
245
|
+
preds_in_body = nodes.inject(Set.new) {|preds, name_n| preds.add(name_n[0]) if name_n[1].in_body; preds}
|
246
|
+
|
247
|
+
bud.t_provides.each do |p|
|
248
|
+
pred, input = p.interface, p.input
|
249
|
+
if input
|
250
|
+
# an interface pred is a source if it is an input and it is not in any rule's lhs
|
251
|
+
#bud.sources << [pred] unless (preds_in_lhs.include? pred)
|
252
|
+
unless preds_in_body.include? pred.to_s
|
253
|
+
# input interface is underspecified if not used in any rule body
|
254
|
+
bud.t_underspecified << [pred, true] # true indicates input mode
|
255
|
+
puts "Warning: input interface #{pred} not used"
|
256
|
+
end
|
257
|
+
else
|
258
|
+
# an interface pred is a sink if it is not an input and it is not in any rule's body
|
259
|
+
#(if it is in the body, then it is an intermediate node feeding some lhs)
|
260
|
+
#bud.sinks << [pred] unless (preds_in_body.include? pred)
|
261
|
+
unless preds_in_lhs.include? pred.to_s
|
262
|
+
# output interface underspecified if not in any rule's lhs
|
263
|
+
bud.t_underspecified << [pred, false] #false indicates output mode.
|
264
|
+
puts "Warning: output interface #{pred} not used"
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
def depanalysis
|
271
|
+
if @dependency_analysis.nil?
|
272
|
+
require 'bud/depanalysis'
|
273
|
+
da = ::DepAnalysis.new
|
274
|
+
da.providing <+ @bud_instance.tables[:t_provides].to_a
|
275
|
+
da.depends <+ @bud_instance.t_depends.map{|d| [d.lhs, d.op, d.body, d.nm]}
|
276
|
+
|
277
|
+
#@bud_instance.tables[:t_provides].each {|t| da.providing <+ t}
|
278
|
+
#@bud_instance.tables[:t_depends].each {|t| da.depends_tc <+ t}
|
279
|
+
da.tick_internal
|
280
|
+
@dependency_analysis = da
|
281
|
+
end
|
282
|
+
@dependency_analysis
|
200
283
|
end
|
201
284
|
|
202
285
|
def dump_rewrite(strata)
|
203
286
|
fout = File.new("#{@bud_instance.class}_rewritten.txt", "w")
|
204
287
|
fout.puts "Declarations:"
|
205
288
|
|
206
|
-
strata.each_with_index do |
|
207
|
-
|
208
|
-
fout.
|
289
|
+
strata.each_with_index do |rules, i|
|
290
|
+
fout.print "=================================\n"
|
291
|
+
fout.print "Stratum #{i}\n"
|
292
|
+
rules.each do |r|
|
293
|
+
fout.puts "#{r.bud_obj.class}##{r.bud_obj.object_id} #{r.rule_id}"
|
294
|
+
fout.puts "\tsrc: #{r.src}"
|
295
|
+
fout.puts "\torig src: #{r.orig_src}"
|
296
|
+
end
|
209
297
|
end
|
210
298
|
fout.close
|
211
299
|
end
|
data/lib/bud/bust/bust.rb
CHANGED
@@ -68,23 +68,30 @@ module Bust
|
|
68
68
|
if @request =~ /GET .* HTTP*/
|
69
69
|
puts "GET shouldn't have body" if @body
|
70
70
|
# select the appropriate elements from the table
|
71
|
-
desired_elements =
|
72
|
-
uri_params.all? {|k, v|
|
71
|
+
desired_elements = @bud.send(table_name.to_sym).find_all do |t|
|
72
|
+
uri_params.all? {|k, v|
|
73
|
+
(eval "t." + k.to_s) == v[0]
|
74
|
+
}
|
73
75
|
end
|
74
76
|
@session.print success
|
77
|
+
desired_elements = desired_elements .map{|elem|
|
78
|
+
elem.class <= Struct ? elem.to_a : elem
|
79
|
+
}
|
75
80
|
@session.print desired_elements.to_json
|
76
81
|
elsif @request =~ /POST .* HTTP*/
|
77
82
|
# instantiate a new tuple
|
78
83
|
tuple_to_insert = []
|
79
84
|
@body.each do |k, v|
|
80
|
-
index =
|
85
|
+
index = @bud.send(table_name.to_sym).cols.find_index(k.to_sym)
|
81
86
|
for i in (tuple_to_insert.size..index)
|
82
87
|
tuple_to_insert << nil
|
83
88
|
end
|
84
89
|
tuple_to_insert[index] = v[0]
|
85
90
|
end
|
86
91
|
# actually insert the puppy
|
87
|
-
@bud.async_do {
|
92
|
+
@bud.async_do {
|
93
|
+
@bud.send(table_name.to_sym) <+ [tuple_to_insert]
|
94
|
+
}
|
88
95
|
@session.print success
|
89
96
|
end
|
90
97
|
rescue Exception
|