flog 2.2.0 → 2.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data.tar.gz.sig +1 -0
- data/.autotest +15 -0
- data/History.txt +22 -0
- data/Manifest.txt +1 -17
- data/bin/flog +1 -1
- data/lib/flog.rb +107 -103
- data/test/test_flog.rb +463 -1414
- metadata +26 -24
- metadata.gz.sig +0 -0
- data/gem_updater.rb +0 -161
- data/spec_fixtures/collection/bigger_example/acts/date_range.rb +0 -199
- data/spec_fixtures/collection/bigger_example/acts/range.rb +0 -391
- data/spec_fixtures/collection/bigger_example/association_extensions/date_ranged.rb +0 -11
- data/spec_fixtures/collection/bigger_example/association_extensions/ranged.rb +0 -13
- data/spec_fixtures/collection/bigger_example/reflection_extensions/ranged.rb +0 -50
- data/spec_fixtures/directory/bot_filter.rb +0 -70
- data/spec_fixtures/directory/bot_parser.rb +0 -79
- data/spec_fixtures/directory/bot_parser_format.rb +0 -23
- data/spec_fixtures/directory/bot_sender.rb +0 -46
- data/spec_fixtures/empty/empty.rb +0 -0
- data/spec_fixtures/simple/simple.rb +0 -191
- data/test/test_flog_command.rb +0 -343
- data/test/test_flog_integration.rb +0 -946
- data/test/test_helper.rb +0 -65
- data/unpack.rb +0 -22
- data/update_scores.rb +0 -245
data.tar.gz.sig
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
����!�������K�4+5՞��d�l=yT�n�6\�aU���dq���>�'x�\�웍{ᷰеf[ɠlW���'�T�vV�h�|��s���o4�<"�8���15��Lʴ��n�����QO/�*ώ��To�tʲ�I'ߨ�tW<z$i���Kìă���V�� ��G�I�ۗei
|
data/.autotest
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
require 'autotest/restart'
|
4
|
+
require 'autotest/rcov'
|
5
|
+
|
6
|
+
Autotest.add_hook :initialize do |at|
|
7
|
+
at.order = :random
|
8
|
+
|
9
|
+
at.add_mapping(/^spec\/.*_spec\.rb$/) do |filename, _|
|
10
|
+
filename
|
11
|
+
end
|
12
|
+
|
13
|
+
at.libs << ':../../minitest/dev/lib'
|
14
|
+
at.testlib = "minitest/autorun"
|
15
|
+
end
|
data/History.txt
CHANGED
@@ -1,3 +1,25 @@
|
|
1
|
+
=== 2.3.0 / 2009-12-09
|
2
|
+
|
3
|
+
* 1 major enhancement:
|
4
|
+
|
5
|
+
* Added file:line info to the flog report (Marty Andrews)
|
6
|
+
|
7
|
+
* 13 minor enhancements:
|
8
|
+
|
9
|
+
* Added .autotest.
|
10
|
+
* Deleted pre-gauntlet scripts.
|
11
|
+
* Flog#method_name now at least tries show when it is a class method.
|
12
|
+
* Flog.parse_options now takes args directly.
|
13
|
+
* Removed Flog#increment_total_score_by.
|
14
|
+
* Removed Flog#output_summary.
|
15
|
+
* Removed Flog#process_attrset.
|
16
|
+
* Removed Flog#record_method_score.
|
17
|
+
* Removed Flog#summarize_method.
|
18
|
+
* Removed Flog::default_options.
|
19
|
+
* Renamed Flog#analyze_list to process_until_empty.
|
20
|
+
* Renamed Flog#options to #option
|
21
|
+
* Rewrote entire test suite (3100 lines down!). Cleaner and less brittle.
|
22
|
+
|
1
23
|
=== 2.2.0 / 2009-08-14
|
2
24
|
|
3
25
|
* 1 minor enhancement:
|
data/Manifest.txt
CHANGED
@@ -1,26 +1,10 @@
|
|
1
|
+
.autotest
|
1
2
|
History.txt
|
2
3
|
Manifest.txt
|
3
4
|
README.txt
|
4
5
|
Rakefile
|
5
6
|
bin/flog
|
6
|
-
gem_updater.rb
|
7
7
|
lib/flog.rb
|
8
8
|
lib/flog_task.rb
|
9
9
|
lib/gauntlet_flog.rb
|
10
|
-
unpack.rb
|
11
|
-
update_scores.rb
|
12
|
-
spec_fixtures/collection/bigger_example/acts/date_range.rb
|
13
|
-
spec_fixtures/collection/bigger_example/acts/range.rb
|
14
|
-
spec_fixtures/collection/bigger_example/association_extensions/date_ranged.rb
|
15
|
-
spec_fixtures/collection/bigger_example/association_extensions/ranged.rb
|
16
|
-
spec_fixtures/collection/bigger_example/reflection_extensions/ranged.rb
|
17
|
-
spec_fixtures/directory/bot_filter.rb
|
18
|
-
spec_fixtures/directory/bot_parser.rb
|
19
|
-
spec_fixtures/directory/bot_parser_format.rb
|
20
|
-
spec_fixtures/directory/bot_sender.rb
|
21
|
-
spec_fixtures/empty/empty.rb
|
22
|
-
spec_fixtures/simple/simple.rb
|
23
10
|
test/test_flog.rb
|
24
|
-
test/test_flog_command.rb
|
25
|
-
test/test_flog_integration.rb
|
26
|
-
test/test_helper.rb
|
data/bin/flog
CHANGED
data/lib/flog.rb
CHANGED
@@ -4,7 +4,7 @@ require 'ruby_parser'
|
|
4
4
|
require 'optparse'
|
5
5
|
|
6
6
|
class Flog < SexpProcessor
|
7
|
-
VERSION = '2.
|
7
|
+
VERSION = '2.3.0'
|
8
8
|
|
9
9
|
THRESHOLD = 0.60
|
10
10
|
SCORES = Hash.new 1
|
@@ -69,14 +69,8 @@ class Flog < SexpProcessor
|
|
69
69
|
@@no_method = :none
|
70
70
|
|
71
71
|
attr_accessor :multiplier
|
72
|
-
attr_reader :calls, :
|
73
|
-
|
74
|
-
def self.default_options
|
75
|
-
{
|
76
|
-
:quiet => true,
|
77
|
-
:continue => false,
|
78
|
-
}
|
79
|
-
end
|
72
|
+
attr_reader :calls, :option, :class_stack, :method_stack, :mass
|
73
|
+
attr_reader :method_locations
|
80
74
|
|
81
75
|
# REFACTOR: from flay
|
82
76
|
def self.expand_dirs_to_files *dirs
|
@@ -91,27 +85,31 @@ class Flog < SexpProcessor
|
|
91
85
|
}.flatten.sort
|
92
86
|
end
|
93
87
|
|
94
|
-
def self.parse_options
|
95
|
-
|
96
|
-
|
88
|
+
def self.parse_options args = ARGV
|
89
|
+
option = {
|
90
|
+
:quiet => true,
|
91
|
+
:continue => false,
|
92
|
+
}
|
93
|
+
|
94
|
+
OptionParser.new do |opts|
|
97
95
|
opts.on("-a", "--all", "Display all flog results, not top 60%.") do
|
98
|
-
|
96
|
+
option[:all] = true
|
99
97
|
end
|
100
98
|
|
101
99
|
opts.on("-b", "--blame", "Include blame information for methods.") do
|
102
|
-
|
100
|
+
option[:blame] = true
|
103
101
|
end
|
104
102
|
|
105
103
|
opts.on("-c", "--continue", "Continue despite syntax errors.") do
|
106
|
-
|
104
|
+
option[:continue] = true
|
107
105
|
end
|
108
106
|
|
109
107
|
opts.on("-d", "--details", "Show method details.") do
|
110
|
-
|
108
|
+
option[:details] = true
|
111
109
|
end
|
112
110
|
|
113
111
|
opts.on("-g", "--group", "Group and sort by class.") do
|
114
|
-
|
112
|
+
option[:group] = true
|
115
113
|
end
|
116
114
|
|
117
115
|
opts.on("-h", "--help", "Show this message.") do
|
@@ -125,37 +123,34 @@ class Flog < SexpProcessor
|
|
125
123
|
end
|
126
124
|
end
|
127
125
|
|
128
|
-
opts.on("-m", "--methods-only", "Skip code outside of methods.") do
|
129
|
-
|
126
|
+
opts.on("-m", "--methods-only", "Skip code outside of methods.") do
|
127
|
+
option[:methods] = true
|
130
128
|
end
|
131
129
|
|
132
|
-
opts.on("-q", "--quiet", "Don't show method details. [default]")
|
133
|
-
|
130
|
+
opts.on("-q", "--quiet", "Don't show method details. [default]") do
|
131
|
+
option[:quiet] = true
|
134
132
|
end
|
135
133
|
|
136
|
-
opts.on("-s", "--score", "Display total score only.")
|
137
|
-
|
134
|
+
opts.on("-s", "--score", "Display total score only.") do
|
135
|
+
option[:score] = true
|
138
136
|
end
|
139
137
|
|
140
|
-
opts.on("-v", "--verbose", "Display progress during processing.")
|
141
|
-
|
138
|
+
opts.on("-v", "--verbose", "Display progress during processing.") do
|
139
|
+
option[:verbose] = true
|
142
140
|
end
|
143
|
-
end.parse!
|
141
|
+
end.parse! Array(args)
|
144
142
|
|
145
|
-
|
143
|
+
option
|
146
144
|
end
|
147
145
|
|
148
|
-
# TODO: rename options to option, you only deal with them one at a time...
|
149
|
-
|
150
146
|
def add_to_score name, score = OTHER_SCORES[name]
|
151
|
-
@calls[
|
147
|
+
@calls[signature][name] += score * @multiplier
|
152
148
|
end
|
153
149
|
|
154
150
|
##
|
155
151
|
# Process each element of #exp in turn.
|
156
|
-
# TODO: rename, bleed wasn't good, but this is actually worse
|
157
152
|
|
158
|
-
def
|
153
|
+
def process_until_empty exp
|
159
154
|
process exp.shift until exp.empty?
|
160
155
|
end
|
161
156
|
|
@@ -171,7 +166,7 @@ class Flog < SexpProcessor
|
|
171
166
|
begin
|
172
167
|
# TODO: replace File.open to deal with "-"
|
173
168
|
ruby = file == '-' ? $stdin.read : File.read(file)
|
174
|
-
warn "** flogging #{file}" if
|
169
|
+
warn "** flogging #{file}" if option[:verbose]
|
175
170
|
|
176
171
|
ast = @parser.process(ruby, file)
|
177
172
|
next unless ast
|
@@ -182,7 +177,7 @@ class Flog < SexpProcessor
|
|
182
177
|
warn "#{e.inspect} at #{e.backtrace.first(5).join(', ')}"
|
183
178
|
warn "\n...stupid lemmings and their bad erb templates... skipping"
|
184
179
|
else
|
185
|
-
raise e unless
|
180
|
+
raise e unless option[:continue]
|
186
181
|
warn file
|
187
182
|
warn "#{e.inspect} at #{e.backtrace.first(5).join(', ')}"
|
188
183
|
end
|
@@ -190,6 +185,9 @@ class Flog < SexpProcessor
|
|
190
185
|
end
|
191
186
|
end
|
192
187
|
|
188
|
+
##
|
189
|
+
# Adds name to the class stack, for the duration of the block
|
190
|
+
|
193
191
|
def in_klass name
|
194
192
|
@class_stack.unshift name
|
195
193
|
yield
|
@@ -197,33 +195,30 @@ class Flog < SexpProcessor
|
|
197
195
|
end
|
198
196
|
|
199
197
|
##
|
200
|
-
# Adds name to the
|
198
|
+
# Adds name to the method stack, for the duration of the block
|
201
199
|
|
202
|
-
def in_method
|
200
|
+
def in_method(name, file, line)
|
203
201
|
@method_stack.unshift name
|
202
|
+
# "#{klass_name}##{name}"
|
203
|
+
@method_locations[signature] = "#{file}:#{line}"
|
204
204
|
yield
|
205
205
|
@method_stack.shift
|
206
206
|
end
|
207
207
|
|
208
|
-
def
|
209
|
-
raise "@total_score isn't even set yet... dumbass" unless @total_score
|
210
|
-
@total_score += amount
|
211
|
-
end
|
212
|
-
|
213
|
-
def initialize options = {}
|
208
|
+
def initialize option = {}
|
214
209
|
super()
|
215
|
-
@
|
210
|
+
@option = option
|
216
211
|
@class_stack = []
|
217
212
|
@method_stack = []
|
213
|
+
@method_locations = {}
|
218
214
|
@mass = {}
|
219
215
|
@parser = RubyParser.new
|
220
216
|
self.auto_shift_type = true
|
221
|
-
self.require_empty = false # HACK
|
222
217
|
self.reset
|
223
218
|
end
|
224
219
|
|
225
220
|
##
|
226
|
-
#
|
221
|
+
# Returns the first class in the list, or @@no_class if there are
|
227
222
|
# none.
|
228
223
|
|
229
224
|
def klass_name
|
@@ -236,7 +231,7 @@ class Flog < SexpProcessor
|
|
236
231
|
name.delete :colon2
|
237
232
|
name.join("::")
|
238
233
|
when :colon3 then
|
239
|
-
name.last
|
234
|
+
name.last.to_s
|
240
235
|
else
|
241
236
|
name
|
242
237
|
end
|
@@ -245,18 +240,20 @@ class Flog < SexpProcessor
|
|
245
240
|
end
|
246
241
|
|
247
242
|
##
|
248
|
-
#
|
243
|
+
# Returns the first method in the list, or @@no_method if there are
|
249
244
|
# none.
|
250
245
|
|
251
246
|
def method_name
|
252
|
-
@method_stack.first || @@no_method
|
247
|
+
m = @method_stack.first || @@no_method
|
248
|
+
m = "##{m}" unless m =~ /::/ unless m == @@no_method # FIX
|
249
|
+
m
|
253
250
|
end
|
254
251
|
|
255
252
|
def output_details(io, max = nil)
|
256
253
|
my_totals = totals
|
257
254
|
current = 0
|
258
255
|
|
259
|
-
if
|
256
|
+
if option[:group] then
|
260
257
|
scores = Hash.new 0
|
261
258
|
methods = Hash.new { |h,k| h[k] = [] }
|
262
259
|
|
@@ -273,7 +270,12 @@ class Flog < SexpProcessor
|
|
273
270
|
io.puts
|
274
271
|
io.puts "%8.1f: %s" % [total, "#{klass} total"]
|
275
272
|
methods[klass].each do |name, score|
|
276
|
-
|
273
|
+
location = @method_locations[name]
|
274
|
+
if location then
|
275
|
+
io.puts "%8.1f: %-32s %s" % [score, name, location]
|
276
|
+
else
|
277
|
+
io.puts "%8.1f: %s" % [score, name]
|
278
|
+
end
|
277
279
|
end
|
278
280
|
end
|
279
281
|
else
|
@@ -286,21 +288,25 @@ class Flog < SexpProcessor
|
|
286
288
|
end
|
287
289
|
|
288
290
|
def output_method_details(io, class_method, call_list)
|
289
|
-
return 0 if
|
291
|
+
return 0 if option[:methods] and class_method =~ /##{@@no_method}/
|
290
292
|
|
291
293
|
total = totals[class_method]
|
292
|
-
io.puts "%8.1f: %s" % [total, class_method]
|
293
294
|
|
294
|
-
|
295
|
-
|
296
|
-
|
295
|
+
location = @method_locations[class_method]
|
296
|
+
if location then
|
297
|
+
io.puts "%8.1f: %-32s %s" % [total, class_method, location]
|
298
|
+
else
|
299
|
+
io.puts "%8.1f: %s" % [total, class_method]
|
300
|
+
end
|
297
301
|
|
298
|
-
|
299
|
-
|
302
|
+
if option[:details] then
|
303
|
+
call_list.sort_by { |k,v| -v }.each do |call, count|
|
304
|
+
io.puts " %6.1f: %s" % [count, call]
|
305
|
+
end
|
306
|
+
io.puts
|
307
|
+
end
|
300
308
|
|
301
|
-
|
302
|
-
io.puts "%8.1f: %s" % [total, "flog total"]
|
303
|
-
io.puts "%8.1f: %s" % [average, "flog/method average"]
|
309
|
+
total
|
304
310
|
end
|
305
311
|
|
306
312
|
##
|
@@ -315,19 +321,16 @@ class Flog < SexpProcessor
|
|
315
321
|
@multiplier -= bonus
|
316
322
|
end
|
317
323
|
|
318
|
-
def record_method_score(method, score)
|
319
|
-
@totals ||= Hash.new(0)
|
320
|
-
@totals[method] = score
|
321
|
-
end
|
322
|
-
|
323
324
|
##
|
324
325
|
# Report results to #io, STDOUT by default.
|
325
326
|
|
326
327
|
def report(io = $stdout)
|
327
|
-
|
328
|
-
|
328
|
+
io.puts "%8.1f: %s" % [total, "flog total"]
|
329
|
+
io.puts "%8.1f: %s" % [average, "flog/method average"]
|
330
|
+
|
331
|
+
return if option[:score]
|
329
332
|
|
330
|
-
if
|
333
|
+
if option[:all] then
|
331
334
|
output_details(io)
|
332
335
|
else
|
333
336
|
output_details(io, total * THRESHOLD)
|
@@ -354,14 +357,13 @@ class Flog < SexpProcessor
|
|
354
357
|
Math.sqrt(a*a + b*b + c*c)
|
355
358
|
end
|
356
359
|
|
357
|
-
def
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
increment_total_score_by score
|
360
|
+
def signature
|
361
|
+
m = method_name
|
362
|
+
m = "#none" if m == @@no_method
|
363
|
+
"#{klass_name}#{m}" # FIX: ugly
|
362
364
|
end
|
363
365
|
|
364
|
-
def total
|
366
|
+
def total # FIX: I hate this indirectness
|
365
367
|
totals unless @total_score # calculates total_score as well
|
366
368
|
|
367
369
|
@total_score
|
@@ -374,10 +376,16 @@ class Flog < SexpProcessor
|
|
374
376
|
unless @totals then
|
375
377
|
@total_score = 0
|
376
378
|
@totals = Hash.new(0)
|
379
|
+
|
377
380
|
calls.each do |meth, tally|
|
378
|
-
|
381
|
+
next if option[:methods] and meth =~ /##{@@no_method}$/
|
382
|
+
score = score_method(tally)
|
383
|
+
|
384
|
+
@totals[meth] = score
|
385
|
+
@total_score += score
|
379
386
|
end
|
380
387
|
end
|
388
|
+
|
381
389
|
@totals
|
382
390
|
end
|
383
391
|
|
@@ -409,15 +417,9 @@ class Flog < SexpProcessor
|
|
409
417
|
s()
|
410
418
|
end
|
411
419
|
|
412
|
-
def process_attrset(exp)
|
413
|
-
add_to_score :assignment
|
414
|
-
raise exp.inspect
|
415
|
-
s()
|
416
|
-
end
|
417
|
-
|
418
420
|
def process_block(exp)
|
419
421
|
penalize_by 0.1 do
|
420
|
-
|
422
|
+
process_until_empty exp
|
421
423
|
end
|
422
424
|
s()
|
423
425
|
end
|
@@ -461,7 +463,7 @@ class Flog < SexpProcessor
|
|
461
463
|
add_to_score :branch
|
462
464
|
process exp.shift # recv
|
463
465
|
penalize_by 0.1 do
|
464
|
-
|
466
|
+
process_until_empty exp
|
465
467
|
end
|
466
468
|
s()
|
467
469
|
end
|
@@ -469,14 +471,14 @@ class Flog < SexpProcessor
|
|
469
471
|
def process_class(exp)
|
470
472
|
in_klass exp.shift do
|
471
473
|
penalize_by 1.0 do
|
472
|
-
|
474
|
+
process exp.shift # superclass expression
|
473
475
|
end
|
474
|
-
|
476
|
+
process_until_empty exp
|
475
477
|
end
|
476
478
|
s()
|
477
479
|
end
|
478
480
|
|
479
|
-
def process_dasgn_curr(exp)
|
481
|
+
def process_dasgn_curr(exp) # FIX: remove
|
480
482
|
add_to_score :assignment
|
481
483
|
exp.shift # name
|
482
484
|
process exp.shift # assigment, if any
|
@@ -486,16 +488,16 @@ class Flog < SexpProcessor
|
|
486
488
|
alias :process_lasgn :process_dasgn_curr
|
487
489
|
|
488
490
|
def process_defn(exp)
|
489
|
-
in_method exp.shift do
|
490
|
-
|
491
|
+
in_method exp.shift, exp.file, exp.line do
|
492
|
+
process_until_empty exp
|
491
493
|
end
|
492
494
|
s()
|
493
495
|
end
|
494
496
|
|
495
497
|
def process_defs(exp)
|
496
|
-
process exp.shift
|
497
|
-
in_method exp.shift do
|
498
|
-
|
498
|
+
recv = process exp.shift
|
499
|
+
in_method "::#{exp.shift}", exp.file, exp.line do
|
500
|
+
process_until_empty exp
|
499
501
|
end
|
500
502
|
s()
|
501
503
|
end
|
@@ -504,7 +506,7 @@ class Flog < SexpProcessor
|
|
504
506
|
def process_else(exp)
|
505
507
|
add_to_score :branch
|
506
508
|
penalize_by 0.1 do
|
507
|
-
|
509
|
+
process_until_empty exp
|
508
510
|
end
|
509
511
|
s()
|
510
512
|
end
|
@@ -523,15 +525,17 @@ class Flog < SexpProcessor
|
|
523
525
|
|
524
526
|
def process_iter(exp)
|
525
527
|
context = (self.context - [:class, :module, :scope])
|
526
|
-
if context.uniq.sort_by {|s|s.to_s} == [:block, :iter] then
|
528
|
+
if context.uniq.sort_by { |s| s.to_s } == [:block, :iter] then
|
527
529
|
recv = exp.first
|
530
|
+
|
531
|
+
# DSL w/ names. eg task :name do ... end
|
528
532
|
if (recv[0] == :call and recv[1] == nil and recv.arglist[1] and
|
529
533
|
[:lit, :str].include? recv.arglist[1][0]) then
|
530
534
|
msg = recv[2]
|
531
535
|
submsg = recv.arglist[1][1]
|
532
|
-
|
533
|
-
|
534
|
-
|
536
|
+
in_klass msg do # :task
|
537
|
+
in_method submsg, exp.file, exp.line do # :name
|
538
|
+
process_until_empty exp
|
535
539
|
end
|
536
540
|
end
|
537
541
|
return s()
|
@@ -540,12 +544,12 @@ class Flog < SexpProcessor
|
|
540
544
|
|
541
545
|
add_to_score :branch
|
542
546
|
|
543
|
-
exp.delete 0
|
547
|
+
exp.delete 0 # TODO: what is this?
|
544
548
|
|
545
549
|
process exp.shift # no penalty for LHS
|
546
550
|
|
547
551
|
penalize_by 0.1 do
|
548
|
-
|
552
|
+
process_until_empty exp
|
549
553
|
end
|
550
554
|
|
551
555
|
s()
|
@@ -568,13 +572,13 @@ class Flog < SexpProcessor
|
|
568
572
|
|
569
573
|
def process_masgn(exp)
|
570
574
|
add_to_score :assignment
|
571
|
-
|
575
|
+
process_until_empty exp
|
572
576
|
s()
|
573
577
|
end
|
574
578
|
|
575
579
|
def process_module(exp)
|
576
580
|
in_klass exp.shift do
|
577
|
-
|
581
|
+
process_until_empty exp
|
578
582
|
end
|
579
583
|
s()
|
580
584
|
end
|
@@ -582,7 +586,7 @@ class Flog < SexpProcessor
|
|
582
586
|
def process_sclass(exp)
|
583
587
|
penalize_by 0.5 do
|
584
588
|
recv = process exp.shift
|
585
|
-
|
589
|
+
process_until_empty exp
|
586
590
|
end
|
587
591
|
|
588
592
|
add_to_score :sclass
|
@@ -591,7 +595,7 @@ class Flog < SexpProcessor
|
|
591
595
|
|
592
596
|
def process_super(exp)
|
593
597
|
add_to_score :super
|
594
|
-
|
598
|
+
process_until_empty exp
|
595
599
|
s()
|
596
600
|
end
|
597
601
|
|
@@ -608,7 +612,7 @@ class Flog < SexpProcessor
|
|
608
612
|
|
609
613
|
def process_yield(exp)
|
610
614
|
add_to_score :yield
|
611
|
-
|
615
|
+
process_until_empty exp
|
612
616
|
s()
|
613
617
|
end
|
614
618
|
end
|