bud 0.1.0.pre1 → 0.9.0
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/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
|