bud 0.1.0.pre1 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +23 -0
- data/{README → README.md} +6 -2
- data/docs/cheat.md +1 -8
- data/docs/intro.md +1 -1
- data/lib/bud/aggs.rb +16 -16
- data/lib/bud/bud_meta.rb +8 -15
- data/lib/bud/collections.rb +85 -172
- data/lib/bud/errors.rb +5 -1
- data/lib/bud/executor/elements.rb +133 -118
- data/lib/bud/executor/group.rb +6 -6
- data/lib/bud/executor/join.rb +25 -22
- data/lib/bud/metrics.rb +1 -1
- data/lib/bud/monkeypatch.rb +18 -29
- data/lib/bud/rebl.rb +5 -4
- data/lib/bud/rewrite.rb +21 -160
- data/lib/bud/source.rb +5 -5
- data/lib/bud/state.rb +13 -12
- data/lib/bud/storage/dbm.rb +13 -23
- data/lib/bud/storage/zookeeper.rb +0 -4
- data/lib/bud.rb +184 -162
- metadata +144 -216
- data/docs/deploy.md +0 -96
- data/lib/bud/deploy/countatomicdelivery.rb +0 -38
- data/lib/bud/joins.rb +0 -526
data/lib/bud/executor/join.rb
CHANGED
@@ -14,7 +14,7 @@ module Bud
|
|
14
14
|
@origpreds = preds
|
15
15
|
@localpreds = nil
|
16
16
|
@selfjoins = []
|
17
|
-
@input_bufs=[[],[]]
|
17
|
+
@input_bufs = [[],[]]
|
18
18
|
@missing_keys = Set.new
|
19
19
|
the_join = nil
|
20
20
|
|
@@ -30,7 +30,8 @@ module Bud
|
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
|
-
# check for self-joins: we currently only handle 2 instances of the same
|
33
|
+
# check for self-joins: we currently only handle 2 instances of the same
|
34
|
+
# table per rule
|
34
35
|
counts = @all_rels_below.reduce({}) do |memo, r|
|
35
36
|
memo[r.elem_name] ||= 0
|
36
37
|
memo[r.elem_name] += 1
|
@@ -57,7 +58,7 @@ module Bud
|
|
57
58
|
setup_preds(preds) unless preds.empty?
|
58
59
|
setup_state
|
59
60
|
|
60
|
-
super(@tabname
|
61
|
+
super(@tabname, @bud_instance, nil, @cols)
|
61
62
|
end
|
62
63
|
|
63
64
|
public
|
@@ -249,7 +250,7 @@ module Bud
|
|
249
250
|
next if (skips.include? pred)
|
250
251
|
# assumption of left-deep joins here
|
251
252
|
if pred[1][0] != @rels[1].tabname
|
252
|
-
raise "
|
253
|
+
raise Bud::Error, "expected rhs table to be #{@rels[1].tabname}, not #{pred[1][0]}"
|
253
254
|
end
|
254
255
|
rfield = right[pred[1][1]]
|
255
256
|
if left_is_array
|
@@ -296,7 +297,7 @@ module Bud
|
|
296
297
|
else
|
297
298
|
offsets = [@relnames.index(source.elem_name)]
|
298
299
|
end
|
299
|
-
raise "item #{item.inspect} inserted into join from unknown source #{source.elem_name}" if offsets == $EMPTY
|
300
|
+
raise Bud::Error, "item #{item.inspect} inserted into join from unknown source #{source.elem_name}" if offsets == $EMPTY
|
300
301
|
offsets.each do |offset|
|
301
302
|
buf = @input_bufs[offset]
|
302
303
|
buf << item
|
@@ -342,12 +343,14 @@ module Bud
|
|
342
343
|
invalidate << self
|
343
344
|
end
|
344
345
|
|
345
|
-
# The distinction between a join node and other stateful elements is that
|
346
|
-
#
|
347
|
-
#
|
348
|
-
# if a
|
349
|
-
#
|
350
|
-
|
346
|
+
# The distinction between a join node and other stateful elements is that
|
347
|
+
# when a join node needs a rescan it doesn't tell all its sources to
|
348
|
+
# rescan. In fact, it doesn't have to pass a rescan request up to a
|
349
|
+
# source, because if a target needs a rescan, the join node has all the
|
350
|
+
# state necessary to feed the downstream node. And if a source node is in
|
351
|
+
# rescan, then at run-time only the state associated with that particular
|
352
|
+
# source node @hash_tables[offset] will be cleared, and will get filled up
|
353
|
+
# again because that source will rescan anyway.
|
351
354
|
invalidate_tables(rescan, invalidate)
|
352
355
|
end
|
353
356
|
|
@@ -406,7 +409,7 @@ module Bud
|
|
406
409
|
public
|
407
410
|
def flush
|
408
411
|
@input_bufs.each_with_index do |buf, offset|
|
409
|
-
flush_buf(buf,offset) if buf.length > 0
|
412
|
+
flush_buf(buf, offset) if buf.length > 0
|
410
413
|
end
|
411
414
|
end
|
412
415
|
|
@@ -582,11 +585,10 @@ module Bud
|
|
582
585
|
end
|
583
586
|
super(rellist, bud_instance, preds)
|
584
587
|
set_block(&blk)
|
585
|
-
@cols =
|
588
|
+
@cols = rellist[0].cols
|
586
589
|
@exclude = Set.new
|
587
590
|
end
|
588
591
|
|
589
|
-
|
590
592
|
def positionwise_preds(bud_instance, rels)
|
591
593
|
# pairwise colnames, for the minimum number of columns from either
|
592
594
|
return [] if rels[0].cols.length != rels[1].cols.length
|
@@ -614,23 +616,24 @@ module Bud
|
|
614
616
|
|
615
617
|
def stratum_end
|
616
618
|
flush
|
617
|
-
# Scan through all the cached left rel values, and push out those that are
|
618
|
-
|
619
|
+
# Scan through all the cached left rel values, and push out those that are
|
620
|
+
# not in exclude
|
621
|
+
@hash_tables[0].each_value do |s|
|
619
622
|
s.each do |item|
|
620
623
|
next if @exclude.member? item
|
621
624
|
@outputs.each do |ou|
|
622
625
|
if ou.class <= Bud::PushElement
|
623
|
-
ou.insert(item,self)
|
626
|
+
ou.insert(item, self)
|
624
627
|
elsif ou.class <= Bud::BudCollection
|
625
|
-
ou.do_insert(item,ou.new_delta)
|
628
|
+
ou.do_insert(item, ou.new_delta)
|
626
629
|
else
|
627
|
-
raise "
|
630
|
+
raise Bud::Error, "expected either a PushElement or a BudCollection"
|
628
631
|
end
|
629
632
|
end
|
630
633
|
# for all the following, o is a BudCollection
|
631
|
-
@deletes.each{|o| o.pending_delete([item])}
|
632
|
-
@delete_keys.each{|o| o.pending_delete_keys([item])}
|
633
|
-
@pendings.each{|o| o.pending_merge([item])}
|
634
|
+
@deletes.each{|o| o.pending_delete([item])}
|
635
|
+
@delete_keys.each{|o| o.pending_delete_keys([item])}
|
636
|
+
@pendings.each{|o| o.pending_merge([item])}
|
634
637
|
end
|
635
638
|
end
|
636
639
|
end
|
data/lib/bud/metrics.rb
CHANGED
@@ -7,7 +7,7 @@ else
|
|
7
7
|
$mod = CSV
|
8
8
|
end
|
9
9
|
|
10
|
-
#
|
10
|
+
# Metrics are reported in a nested hash representing a collection of relational tables.
|
11
11
|
# The metrics hash has the following form:
|
12
12
|
# - key of the metrics hash is the name of the metric (table), e.g. "tickstats", "collections", "rules", etc.
|
13
13
|
# - value of the metrics is itself a hash holding the rows of the table, keyed by key columns.
|
data/lib/bud/monkeypatch.rb
CHANGED
@@ -80,6 +80,9 @@ class Module
|
|
80
80
|
raise Bud::CompileError unless (spec.class <= Hash and spec.length == 1)
|
81
81
|
mod, local_name = spec.first
|
82
82
|
raise Bud::CompileError unless (mod.class <= Module and local_name.class <= Symbol)
|
83
|
+
if mod.class <= Class
|
84
|
+
raise Bud::CompileError, "import must be used with a Module, not a Class"
|
85
|
+
end
|
83
86
|
|
84
87
|
# A statement like this:
|
85
88
|
# import MyModule => :m
|
@@ -98,19 +101,18 @@ class Module
|
|
98
101
|
|
99
102
|
mod, local_name = spec.first
|
100
103
|
|
101
|
-
if self.
|
102
|
-
raise Bud::CompileError, "#{local_name} is already taken
|
104
|
+
if self.method_defined? local_name
|
105
|
+
raise Bud::CompileError, "#{local_name} is already taken"
|
103
106
|
else
|
104
107
|
src = %Q{
|
105
108
|
def #{local_name}
|
106
109
|
@#{local_name}
|
107
110
|
end
|
108
111
|
def #{local_name}=(val)
|
109
|
-
raise "
|
112
|
+
raise Bud::Error, "type error: expecting an instance of #{mod}" unless val.kind_of? #{mod}
|
110
113
|
@#{local_name} = val
|
111
114
|
end
|
112
115
|
}
|
113
|
-
#puts src
|
114
116
|
self.class_eval src
|
115
117
|
end
|
116
118
|
|
@@ -142,7 +144,7 @@ class Module
|
|
142
144
|
# If no block name was specified, generate a unique name
|
143
145
|
if block_name.nil?
|
144
146
|
@block_id ||= 0
|
145
|
-
block_name = "#{Module.get_class_name(self)}__#{@block_id
|
147
|
+
block_name = "#{Module.get_class_name(self)}__#{@block_id}".to_sym
|
146
148
|
@block_id += 1
|
147
149
|
else
|
148
150
|
unless block_name.class <= Symbol
|
@@ -157,25 +159,25 @@ class Module
|
|
157
159
|
|
158
160
|
# Don't allow duplicate named bloom blocks to be defined within a single
|
159
161
|
# module; this indicates a likely programmer error.
|
160
|
-
if instance_methods(false).include?
|
162
|
+
if instance_methods(false).include?(meth_name) ||
|
163
|
+
instance_methods(false).include?(meth_name.to_sym)
|
161
164
|
raise Bud::CompileError, "duplicate named bloom block: '#{block_name}' in #{self}"
|
162
165
|
end
|
163
166
|
ast = Source.read_block(caller[0]) # pass in caller's location via backtrace
|
164
167
|
# ast corresponds only to the statements of the block. Wrap it in a method
|
165
168
|
# definition for backward compatibility for now.
|
166
169
|
# First wrap ast in a block if it is only a single statement
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
end
|
170
|
+
ast = s(:block) if ast.nil?
|
171
|
+
ast = s(:block, ast) unless ast.sexp_type == :block
|
172
|
+
ast = s(:defn, meth_name.to_sym, s(:args), s(:scope, ast))
|
173
|
+
unless self.respond_to? :__bloom_asts__
|
174
|
+
def self.__bloom_asts__
|
175
|
+
@__bloom_asts__ ||= {}
|
176
|
+
@__bloom_asts__
|
175
177
|
end
|
176
|
-
__bloom_asts__[meth_name] = ast
|
177
|
-
define_method(meth_name.to_sym, &block)
|
178
178
|
end
|
179
|
+
__bloom_asts__[meth_name] = ast
|
180
|
+
define_method(meth_name.to_sym, &block)
|
179
181
|
end
|
180
182
|
|
181
183
|
private
|
@@ -202,19 +204,6 @@ end
|
|
202
204
|
|
203
205
|
|
204
206
|
module Enumerable
|
205
|
-
public
|
206
|
-
# Support for renaming collections and their schemas
|
207
|
-
def rename(new_tabname, new_schema=nil)
|
208
|
-
budi = (respond_to?(:bud_instance)) ? bud_instance : nil
|
209
|
-
if new_schema.nil? and respond_to?(:schema)
|
210
|
-
new_schema = schema
|
211
|
-
end
|
212
|
-
scr = Bud::BudScratch.new(new_tabname.to_s, budi, new_schema)
|
213
|
-
scr.uniquify_tabname
|
214
|
-
scr.merge(self, scr.storage)
|
215
|
-
scr
|
216
|
-
end
|
217
|
-
|
218
207
|
public
|
219
208
|
# We rewrite "map" calls in Bloom blocks to invoke the "pro" method
|
220
209
|
# instead. This is fine when applied to a BudCollection; when applied to a
|
data/lib/bud/rebl.rb
CHANGED
@@ -9,7 +9,7 @@ TABLE_TYPES = ["table", "scratch", "channel", "loopback", "periodic", "sync", "s
|
|
9
9
|
# The class to which rebl adds user-specified rules and declarations.
|
10
10
|
class ReblBase
|
11
11
|
include Bud
|
12
|
-
attr_accessor:port, :ip
|
12
|
+
attr_accessor :port, :ip
|
13
13
|
|
14
14
|
# Support for breakpoints
|
15
15
|
state { scratch :rebl_breakpoint }
|
@@ -195,9 +195,10 @@ end
|
|
195
195
|
class LibRebl
|
196
196
|
attr_accessor :rules, :state
|
197
197
|
attr_reader :ip, :port, :rebl_class_inst
|
198
|
-
@@builtin_tables = [:stdio, :
|
199
|
-
:
|
200
|
-
:
|
198
|
+
@@builtin_tables = [:stdio, :periodics_tbl, :halt, :localtick,
|
199
|
+
:t_depends, :t_cycle, :t_provides, :t_rules,
|
200
|
+
:t_depends_tc, :t_stratum, :t_underspecified,
|
201
|
+
:t_table_info, :t_table_schema, :rebl_breakpoint]
|
201
202
|
@@classid = 0
|
202
203
|
|
203
204
|
def initialize(ip, port)
|
data/lib/bud/rewrite.rb
CHANGED
@@ -12,8 +12,9 @@ class RuleRewriter < Ruby2Ruby # :nodoc: all
|
|
12
12
|
:* => 1, :pairs => 1, :matches => 1, :combos => 1, :flatten => 1,
|
13
13
|
:lefts => 1, :rights => 1, :map => 1, :flat_map => 1, :pro => 1,
|
14
14
|
:cols => 1, :key_cols => 1, :val_cols => 1, :payloads => 1, :~ => 1,
|
15
|
-
:lambda => 1, :tabname => 1
|
16
|
-
|
15
|
+
:lambda => 1, :tabname => 1,
|
16
|
+
:ip_port => 1, :port => 1, :ip => 1
|
17
|
+
}
|
17
18
|
@temp_ops = {:-@ => 1, :~ => 1, :+@ => 1}
|
18
19
|
@tables = {}
|
19
20
|
@nm = false
|
@@ -21,6 +22,7 @@ class RuleRewriter < Ruby2Ruby # :nodoc: all
|
|
21
22
|
@collect = false
|
22
23
|
@rules = []
|
23
24
|
@depends = []
|
25
|
+
@nm_funcs_called = false
|
24
26
|
super()
|
25
27
|
end
|
26
28
|
|
@@ -76,9 +78,15 @@ class RuleRewriter < Ruby2Ruby # :nodoc: all
|
|
76
78
|
end
|
77
79
|
# for CALM analysis, mark deletion rules as non-monotonic
|
78
80
|
@nm = true if op == :-@
|
79
|
-
|
80
|
-
|
81
|
-
@
|
81
|
+
if recv
|
82
|
+
# don't worry about monotone ops, table names, table.attr calls, or accessors of iterator variables
|
83
|
+
unless @monotonic_whitelist[op] or op_is_field_name or recv.first == :lvar or op.to_s.start_with?("__")
|
84
|
+
@nm = true if recv
|
85
|
+
end
|
86
|
+
else
|
87
|
+
# function called (implicit receiver = Bud instance) in a user-defined code block. Check if it is
|
88
|
+
# non-monotonic (like budtime, that produces a new answer every time it is called)
|
89
|
+
@nm_funcs_called = true unless @monotonic_whitelist[op]
|
82
90
|
end
|
83
91
|
end
|
84
92
|
if @temp_ops[op]
|
@@ -110,6 +118,7 @@ class RuleRewriter < Ruby2Ruby # :nodoc: all
|
|
110
118
|
def reset_instance_vars
|
111
119
|
@tables = {}
|
112
120
|
@nm = false
|
121
|
+
@nm_funcs_called = false
|
113
122
|
@temp_op = nil
|
114
123
|
end
|
115
124
|
|
@@ -122,7 +131,7 @@ class RuleRewriter < Ruby2Ruby # :nodoc: all
|
|
122
131
|
op = op.to_s
|
123
132
|
end
|
124
133
|
|
125
|
-
@rules << [@bud_instance, @rule_indx, lhs, op, rule_txt, rule_txt_orig]
|
134
|
+
@rules << [@bud_instance, @rule_indx, lhs, op, rule_txt, rule_txt_orig, @nm_funcs_called]
|
126
135
|
@tables.each_pair do |t, non_monotonic|
|
127
136
|
@depends << [@bud_instance, @rule_indx, lhs, op, t, non_monotonic]
|
128
137
|
end
|
@@ -172,7 +181,7 @@ class RuleRewriter < Ruby2Ruby # :nodoc: all
|
|
172
181
|
if exp[1][1] and exp[1][1][1] and exp[1][1][1][2] == :map
|
173
182
|
exp[1][1][1][2] = :pro
|
174
183
|
end
|
175
|
-
end
|
184
|
+
end
|
176
185
|
exp
|
177
186
|
end
|
178
187
|
|
@@ -246,7 +255,6 @@ class AttrNameRewriter < SexpProcessor # :nodoc: all
|
|
246
255
|
@collnames << exp[2]
|
247
256
|
elsif exp[2] and exp[2] == :rename
|
248
257
|
arglist, namelit, schemahash = exp[3]
|
249
|
-
# and add name to @collnames
|
250
258
|
@collnames << namelit[1]
|
251
259
|
else
|
252
260
|
exp.each { |e| gather_collection_names(e) if e and e.class <= Sexp }
|
@@ -288,12 +296,12 @@ class TempExpander < SexpProcessor # :nodoc: all
|
|
288
296
|
attr_reader :tmp_tables
|
289
297
|
attr_accessor :did_work
|
290
298
|
|
299
|
+
KEYWORD = :temp
|
300
|
+
|
291
301
|
def initialize
|
292
302
|
super()
|
293
303
|
self.require_empty = false
|
294
304
|
self.expected = Sexp
|
295
|
-
@keyword = :temp
|
296
|
-
|
297
305
|
@tmp_tables = []
|
298
306
|
@did_work = false
|
299
307
|
end
|
@@ -322,7 +330,7 @@ class TempExpander < SexpProcessor # :nodoc: all
|
|
322
330
|
end
|
323
331
|
|
324
332
|
_, recv, meth, meth_args = n
|
325
|
-
if meth ==
|
333
|
+
if meth == KEYWORD and recv.nil?
|
326
334
|
block[i] = rewrite_me(n)
|
327
335
|
@did_work = true
|
328
336
|
end
|
@@ -331,12 +339,13 @@ class TempExpander < SexpProcessor # :nodoc: all
|
|
331
339
|
s(tag, name, args, scope)
|
332
340
|
end
|
333
341
|
|
342
|
+
private
|
334
343
|
def fix_temp_decl(iter_body)
|
335
344
|
if iter_body.first.sexp_type == :call
|
336
345
|
call_node = iter_body.first
|
337
346
|
|
338
347
|
_, recv, meth, meth_args = call_node
|
339
|
-
if meth ==
|
348
|
+
if meth == KEYWORD and recv.nil?
|
340
349
|
_, lhs, op, rhs = meth_args.sexp_body.first
|
341
350
|
|
342
351
|
old_rhs_body = rhs.sexp_body
|
@@ -350,20 +359,6 @@ class TempExpander < SexpProcessor # :nodoc: all
|
|
350
359
|
return nil
|
351
360
|
end
|
352
361
|
|
353
|
-
def get_state_meth(klass)
|
354
|
-
return if @tmp_tables.empty?
|
355
|
-
block = s(:block)
|
356
|
-
|
357
|
-
@tmp_tables.each do |t|
|
358
|
-
args = s(:arglist, s(:lit, t.to_sym))
|
359
|
-
block << s(:call, nil, :temp, args)
|
360
|
-
end
|
361
|
-
|
362
|
-
meth_name = Module.make_state_meth_name(klass).to_s + "__" + @keyword.to_s
|
363
|
-
return s(:defn, meth_name.to_sym, s(:args), s(:scope, block))
|
364
|
-
end
|
365
|
-
|
366
|
-
private
|
367
362
|
def rewrite_me(exp)
|
368
363
|
_, recv, meth, args = exp
|
369
364
|
|
@@ -380,137 +375,3 @@ class TempExpander < SexpProcessor # :nodoc: all
|
|
380
375
|
return s(:call, new_recv, nest_op, nest_args)
|
381
376
|
end
|
382
377
|
end
|
383
|
-
|
384
|
-
# We do four things here for each "with" block
|
385
|
-
# 1) Remove it from the AST
|
386
|
-
# 2) Use rewrite_me in the parent class to get the collection name pushed onto @tmp_tables.
|
387
|
-
# 3) Extract the definition of the "with" collection and push it onto @with_defns
|
388
|
-
# 4) Extract the rules in the body of the "with" block and push it onto @with_rules
|
389
|
-
|
390
|
-
class WithExpander < TempExpander
|
391
|
-
attr_reader :with_rules, :with_defns
|
392
|
-
def initialize
|
393
|
-
super()
|
394
|
-
@keyword = :with
|
395
|
-
@with_rules = []
|
396
|
-
@with_defns = []
|
397
|
-
end
|
398
|
-
|
399
|
-
def process_defn(exp)
|
400
|
-
tag, name, args, scope = exp
|
401
|
-
if name.to_s =~ /^__bloom__.+/
|
402
|
-
block = scope[1]
|
403
|
-
|
404
|
-
block.each_with_index do |n,i|
|
405
|
-
if i == 0
|
406
|
-
raise Bud::CompileError if n != :block
|
407
|
-
next
|
408
|
-
end
|
409
|
-
|
410
|
-
# temp declarations are misparsed if the RHS contains certain constructs
|
411
|
-
# (e.g., group, "do |f| ... end" rather than "{|f| ... }"). Rewrite to
|
412
|
-
# correct the misparsing.
|
413
|
-
if n.sexp_type == :iter
|
414
|
-
block[i] = nil
|
415
|
-
iter_body = n.sexp_body
|
416
|
-
n = fix_temp_decl(iter_body)
|
417
|
-
@with_defns.push n
|
418
|
-
@did_work = true unless n.nil?
|
419
|
-
end
|
420
|
-
|
421
|
-
_, recv, meth, meth_args = n
|
422
|
-
if meth == @keyword and recv.nil?
|
423
|
-
block[i] = nil
|
424
|
-
n = rewrite_me(n)
|
425
|
-
@with_defns.push n
|
426
|
-
@did_work = true unless n.nil?
|
427
|
-
end
|
428
|
-
end
|
429
|
-
end
|
430
|
-
block.compact! unless block.nil? # remove the nils that got pulled out
|
431
|
-
|
432
|
-
return s(tag, name, args, scope)
|
433
|
-
end
|
434
|
-
|
435
|
-
def get_state_meth(klass)
|
436
|
-
return if @tmp_tables.empty?
|
437
|
-
block = s(:block)
|
438
|
-
|
439
|
-
t = @tmp_tables.pop
|
440
|
-
args = s(:arglist, s(:lit, t.to_sym))
|
441
|
-
block << s(:call, nil, :temp, args)
|
442
|
-
|
443
|
-
meth_name = Module.make_state_meth_name(klass).to_s + "__" + @keyword.to_s
|
444
|
-
return s(:defn, meth_name.to_sym, s(:args), s(:scope, block))
|
445
|
-
end
|
446
|
-
|
447
|
-
private
|
448
|
-
def rewrite_me(exp)
|
449
|
-
_, recv, meth, args = exp
|
450
|
-
|
451
|
-
raise Bud::CompileError unless recv == nil
|
452
|
-
nest_call = args.sexp_body.first
|
453
|
-
raise Bud::CompileError unless nest_call.sexp_type == :call
|
454
|
-
|
455
|
-
nest_recv, nest_op, nest_args = nest_call.sexp_body
|
456
|
-
raise Bud::CompileError unless nest_recv.sexp_type == :lit
|
457
|
-
|
458
|
-
tmp_name = nest_recv.sexp_body.first
|
459
|
-
@tmp_tables.push tmp_name
|
460
|
-
nest_block = args.sexp_body[1]
|
461
|
-
if nest_block.first == :call
|
462
|
-
# a one-rule block doesn't get wrapped in a block. wrap it ourselves.
|
463
|
-
nest_block = s(:block, nest_block)
|
464
|
-
end
|
465
|
-
@with_rules.push nest_block
|
466
|
-
new_recv = s(:call, nil, tmp_name, s(:arglist))
|
467
|
-
return s(:call, new_recv, nest_op, nest_args)
|
468
|
-
end
|
469
|
-
|
470
|
-
undef get_state_meth
|
471
|
-
|
472
|
-
public
|
473
|
-
def get_state_meth(klass)
|
474
|
-
return if @tmp_tables.empty?
|
475
|
-
block = s(:block)
|
476
|
-
|
477
|
-
args = s(:arglist, s(:lit, @tmp_tables.pop.to_sym))
|
478
|
-
block << s(:call, nil, :temp, args)
|
479
|
-
|
480
|
-
meth_name = Module.make_state_meth_name(klass).to_s + "__" + @keyword.to_s
|
481
|
-
return s(:defn, meth_name.to_sym, s(:args), s(:scope, block))
|
482
|
-
end
|
483
|
-
end
|
484
|
-
|
485
|
-
class DefnRenamer < SexpProcessor # :nodoc: all
|
486
|
-
def initialize(local_name, rename_tbl)
|
487
|
-
super()
|
488
|
-
self.require_empty = false
|
489
|
-
self.expected = Sexp
|
490
|
-
@local_name = local_name
|
491
|
-
@rename_tbl = rename_tbl
|
492
|
-
end
|
493
|
-
|
494
|
-
def process_defn(exp)
|
495
|
-
tag, name, args, scope = exp
|
496
|
-
name_s = name.to_s
|
497
|
-
|
498
|
-
if name_s =~ /^__bootstrap__.+$/
|
499
|
-
new_name = name_s.sub(/^(__bootstrap__)(.+)$/, "\\1#{@local_name}__\\2")
|
500
|
-
elsif name_s =~ /^__state\d+__/
|
501
|
-
new_name = name_s.sub(/^(__state\d+__)(.*)$/, "\\1#{@local_name}__\\2")
|
502
|
-
elsif name_s =~ /^__bloom__.+$/
|
503
|
-
new_name = name_s.sub(/^(__bloom__)(.+)$/, "\\1#{@local_name}__\\2")
|
504
|
-
else
|
505
|
-
new_name = "#{@local_name}__#{name_s}"
|
506
|
-
end
|
507
|
-
|
508
|
-
new_name = new_name.to_sym
|
509
|
-
@rename_tbl[name] = new_name
|
510
|
-
|
511
|
-
# Note that we don't bother to recurse further into the AST: we're only
|
512
|
-
# interested in top-level :defn nodes.
|
513
|
-
s(tag, new_name, args, scope)
|
514
|
-
end
|
515
|
-
end
|
516
|
-
|
data/lib/bud/source.rb
CHANGED
@@ -5,13 +5,13 @@ require 'bud/errors'
|
|
5
5
|
module Source
|
6
6
|
$cached_file_info = Struct.new(:curr_file, :lines, :last_state_bloom_line).new
|
7
7
|
|
8
|
-
#Reads the block corresponding to the location (string of the form
|
9
|
-
#Returns an ast for the block
|
8
|
+
# Reads the block corresponding to the location (string of the form
|
9
|
+
# "file:line_num"). Returns an ast for the block.
|
10
10
|
def Source.read_block(location)
|
11
|
-
raise Bud::
|
11
|
+
raise Bud::IllegalSourceError, "source must be present in a file; cannot read interactive shell or eval block" if location.start_with? '('
|
12
12
|
location =~ /^(.*):(\d+)/
|
13
13
|
filename, num = $1, $2.to_i
|
14
|
-
raise Bud::
|
14
|
+
raise Bud::IllegalSourceError, "couldn't determine filename from backtrace" if filename.nil?
|
15
15
|
lines = cache(filename, num)
|
16
16
|
# Note: num is 1-based.
|
17
17
|
|
@@ -59,7 +59,7 @@ module Source
|
|
59
59
|
}
|
60
60
|
$cached_file_info.lines = retval
|
61
61
|
end
|
62
|
-
retval # array of lines
|
62
|
+
retval # array of lines
|
63
63
|
end
|
64
64
|
|
65
65
|
# Tok is string tokenizer that extracts a substring matching the
|
data/lib/bud/state.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
module Bud
|
2
2
|
######## methods for registering collection types
|
3
|
-
|
3
|
+
private
|
4
|
+
def check_collection_name(name)
|
4
5
|
if @tables.has_key? name
|
5
6
|
raise Bud::CompileError, "collection already exists: #{name}"
|
6
7
|
end
|
@@ -11,6 +12,11 @@ module Bud
|
|
11
12
|
unless reserved.nil?
|
12
13
|
raise Bud::CompileError, "symbol :#{name} reserved, cannot be used as collection name"
|
13
14
|
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def define_collection(name)
|
18
|
+
check_collection_name(name)
|
19
|
+
|
14
20
|
self.singleton_class.send(:define_method, name) do |*args, &blk|
|
15
21
|
if blk.nil?
|
16
22
|
return @tables[name]
|
@@ -20,6 +26,7 @@ module Bud
|
|
20
26
|
end
|
21
27
|
end
|
22
28
|
|
29
|
+
public
|
23
30
|
def input # :nodoc: all
|
24
31
|
true
|
25
32
|
end
|
@@ -32,7 +39,7 @@ module Bud
|
|
32
39
|
def interface(mode, name, schema=nil)
|
33
40
|
define_collection(name)
|
34
41
|
t_provides << [name.to_s, mode]
|
35
|
-
@tables[name] = (mode ?
|
42
|
+
@tables[name] = (mode ? BudInputInterface : BudOutputInterface).new(name, self, schema)
|
36
43
|
end
|
37
44
|
|
38
45
|
# declare an in-memory, non-transient collection. default schema <tt>[:key] => [:val]</tt>.
|
@@ -41,13 +48,13 @@ module Bud
|
|
41
48
|
@tables[name] = Bud::BudTable.new(name, self, schema)
|
42
49
|
end
|
43
50
|
|
44
|
-
# declare a collection-generating expression. default schema <tt>[:key] => [:val]</tt>.
|
51
|
+
# declare a collection-generating expression. default schema <tt>[:key] => [:val]</tt>.
|
45
52
|
def coll_expr(name, expr, schema=nil)
|
46
53
|
define_collection(name)
|
47
54
|
@tables[name] = Bud::BudCollExpr.new(name, self, expr, schema)
|
48
55
|
end
|
49
|
-
|
50
|
-
# declare a syncronously-flushed persistent collection. default schema <tt>[:key] => [:val]</tt>.
|
56
|
+
|
57
|
+
# declare a syncronously-flushed persistent collection. default schema <tt>[:key] => [:val]</tt>.
|
51
58
|
def sync(name, storage, schema=nil)
|
52
59
|
define_collection(name)
|
53
60
|
case storage
|
@@ -85,12 +92,6 @@ module Bud
|
|
85
92
|
@tables[name] = Bud::BudReadOnly.new(name, self, schema)
|
86
93
|
end
|
87
94
|
|
88
|
-
def signal(name, schema=nil)
|
89
|
-
define_collection(name)
|
90
|
-
@tables[name] = Bud::BudSignal.new(name, self, schema)
|
91
|
-
end
|
92
|
-
|
93
|
-
|
94
95
|
# declare a scratch in a bloom statement lhs. schema inferred from rhs.
|
95
96
|
def temp(name)
|
96
97
|
define_collection(name)
|
@@ -104,7 +105,7 @@ module Bud
|
|
104
105
|
@tables[name] = Bud::BudChannel.new(name, self, schema, loopback)
|
105
106
|
@channels[name] = @tables[name]
|
106
107
|
end
|
107
|
-
|
108
|
+
|
108
109
|
# declare a transient network collection that delivers facts back to the
|
109
110
|
# current Bud instance. This is syntax sugar for a channel that always
|
110
111
|
# delivers to the IP/port of the current Bud instance. Default schema
|