assert2 0.3.4 → 0.3.6

Sign up to get free protection for your applications and to get access to all the features.
data/lib/assert2.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  require 'test/unit'
2
2
 
3
3
  # FIXME the first failing assertion of a batch should suggest you get with Ruby1.9...
4
- # TODO install Coulor
4
+ # TODO install Coulor (flibberty)
5
5
  # TODO add :verbose => option to assert{}
6
6
  # TODO pay for Staff Benda Bilili ALBUM: Tr�s Tr�s Fort (Promo Sampler) !
7
7
  # TODO evaluate parts[3]
@@ -19,7 +19,7 @@ require 'test/unit'
19
19
  #~ end
20
20
 
21
21
  if RUBY_VERSION < '1.9.0'
22
- require 'assert2/rubynode_reflector' # FIXME default to null_reflector if rubynode not available
22
+ require 'assert2/rubynode_reflector'
23
23
  else
24
24
  require 'assert2/ripper_reflector'
25
25
  end
@@ -35,7 +35,7 @@ module Test; module Unit; module Assertions
35
35
  end
36
36
 
37
37
  def add_diagnostic(whatever = nil, &block)
38
- @__additional_diagnostics ||= []
38
+ @__additional_diagnostics ||= [] # TODO move that inside the reflector object, and persist it thru a test case event
39
39
 
40
40
  if whatever == :clear
41
41
  @__additional_diagnostics = []
@@ -115,7 +115,7 @@ module Test; module Unit; module Assertions
115
115
  include Coulor
116
116
 
117
117
  def split_and_read(called)
118
- if called =~ /([^:]+):(\d+):/
118
+ if called + ':' =~ /([^:]+):(\d+):/
119
119
  file, line = $1, $2.to_i
120
120
  return File.readlines(file)[line - 1 .. -1]
121
121
  end
@@ -204,7 +204,10 @@ module Test; module Unit; module Assertions
204
204
  options[:keep_diagnostics] or add_diagnostic :clear
205
205
 
206
206
  begin
207
- got = block.call(*options[:args]) and add_assertion and return got
207
+ if got = block.call(*options[:args])
208
+ add_assertion
209
+ return got
210
+ end
208
211
  rescue FlunkError
209
212
  raise # asserts inside assertions that fail do not decorate the outer assertion
210
213
  rescue => got
@@ -235,7 +238,7 @@ module Test; module Unit; module Assertions
235
238
  options[:keep_diagnostics] or add_diagnostic :clear
236
239
 
237
240
  begin
238
- got = block.call(*options[:args]) or (add_assertion and return true)
241
+ got = block.call(*options[:args]) or (add_assertion ; return true)
239
242
  rescue FlunkError
240
243
  raise
241
244
  rescue => got
@@ -251,7 +254,7 @@ module Test; module Unit; module Assertions
251
254
  options[:keep_diagnostics] or add_diagnostic :clear
252
255
 
253
256
  begin
254
- got = block.call(*options[:args]) or (add_assertion and return true)
257
+ got = block.call(*options[:args]) or (add_assertion ; return true)
255
258
  rescue FlunkError
256
259
  raise
257
260
  rescue => got
data/lib/assert2.rb~ ADDED
@@ -0,0 +1,344 @@
1
+ require 'test/unit'
2
+
3
+ # FIXME the first failing assertion of a batch should suggest you get with Ruby1.9...
4
+ # TODO install Coulor (flibberty)
5
+ # TODO add :verbose => option to assert{}
6
+ # TODO pay for Staff Benda Bilili ALBUM: Tr�s Tr�s Fort (Promo Sampler) !
7
+ # TODO evaluate parts[3]
8
+ # ERGO if the block is a block, decorate with do-end
9
+ # ERGO decorate assert_latest's block at fault time
10
+
11
+ #~ if RUBY_VERSION > '1.8.6'
12
+ #~ puts "\nWarning: This version of assert{ 2.0 } requires\n" +
13
+ #~ "RubyNode, which only works on Ruby versions < 1.8.7.\n" +
14
+ #~ "Upgrade to Ruby1.9, and try 'gem install assert21'\n\n"
15
+ #~ end
16
+
17
+ #~ def colorize(whatever)
18
+ #~ # FIXME stop ignoring this and start colorizing v2.1!
19
+ #~ end
20
+
21
+ if RUBY_VERSION < '1.9.0'
22
+ require 'assert2/rubynode_reflector'
23
+ else
24
+ require 'assert2/ripper_reflector'
25
+ end
26
+
27
+ # CONSIDER fix if an assertion contains more than one command - reflect it all!
28
+
29
+ module Test; module Unit; module Assertions
30
+
31
+ FlunkError = if defined? Test::Unit::AssertionFailedError
32
+ Test::Unit::AssertionFailedError
33
+ else
34
+ MiniTest::Assertion
35
+ end
36
+
37
+ def add_diagnostic(whatever = nil, &block)
38
+ @__additional_diagnostics ||= [] # TODO move that inside the reflector object, and persist it thru a test case event
39
+
40
+ if whatever == :clear
41
+ @__additional_diagnostics = []
42
+ whatever = nil
43
+ end
44
+
45
+ @__additional_diagnostics += [whatever, block] # note .compact will take care of them if they don't exist
46
+ end
47
+
48
+ def assert(*args, &block)
49
+ # This assertion calls a block, and faults if it returns
50
+ # +false+ or +nil+. The fault diagnostic will reflect the
51
+ # assertion's complete source - with comments - and will
52
+ # reevaluate the every variable and expression in the
53
+ # block.
54
+ #
55
+ # The first argument can be a diagnostic string:
56
+ #
57
+ # assert("foo failed"){ foo() }
58
+ #
59
+ # The fault diagnostic will print that line.
60
+ #
61
+ # The next time you think to write any of these assertions...
62
+ #
63
+ # - +assert+
64
+ # - +assert_equal+
65
+ # - +assert_instance_of+
66
+ # - +assert_kind_of+
67
+ # - +assert_operator+
68
+ # - +assert_match+
69
+ # - +assert_not_nil+
70
+ #
71
+ # use <code>assert{ 2.1 }</code> instead.
72
+ #
73
+ # If no block is provided, the assertion calls +assert_classic+,
74
+ # which simulates RubyUnit's standard <code>assert()</code>.
75
+ if block
76
+ assert_ *args, &block
77
+ else
78
+ assert_classic *args
79
+ end
80
+ return true # or die trying ;-)
81
+ end
82
+
83
+ module Coulor #:nodoc:
84
+ def colorize(we_color)
85
+ @@we_color = we_color
86
+ end
87
+ unless defined? BOLD
88
+ BOLD = "\e[1m"
89
+ CLEAR = "\e[0m"
90
+ end # ERGO modularize these; anneal with Win32
91
+ def colour(text, colour_code)
92
+ return colour_code + text + CLEAR if colorize?
93
+ return text
94
+ end
95
+ def colorize? # ERGO how other libraries set these options transparent??
96
+ we_color = (@@we_color rescue true) # ERGO parens needed?
97
+ return false if ENV['EMACS'] == 't'
98
+ return (we_color == :always or we_color && $stdout.tty?)
99
+ end
100
+ def bold(text)
101
+ return BOLD + text + CLEAR if colorize?
102
+ return text
103
+ end
104
+ def green(text); colour(text, "\e[32m"); end
105
+ def red(text); colour(text, "\e[31m"); end
106
+ def magenta(text); colour(text, "\e[35m"); end
107
+ def blue(text); colour(text, "\e[34m"); end
108
+ def orange(text); colour(text, "\e[3Bm"); end
109
+ end
110
+
111
+ class RubyReflector
112
+ attr_accessor :captured_block_vars,
113
+ :args
114
+
115
+ include Coulor
116
+
117
+ def split_and_read(called)
118
+ if called + ':' =~ /([^:]+):(\d+):/
119
+ file, line = $1, $2.to_i
120
+ return File.readlines(file)[line - 1 .. -1]
121
+ end
122
+
123
+ return nil
124
+ end
125
+
126
+ def __evaluate_diagnostics
127
+ @__additional_diagnostics.each_with_index do |d, x|
128
+ @__additional_diagnostics[x] = d.call if d.respond_to? :call
129
+ end
130
+ end # CONSIDER pass the same args as blocks take?
131
+
132
+ def __build_message(reflection)
133
+ __evaluate_diagnostics
134
+ return (@__additional_diagnostics.uniq + [reflection]).compact.join("\n")
135
+ end # TODO move this fluff to the ruby_reflector!
136
+
137
+ def format_inspection(inspection, spaces)
138
+ spaces = ' ' * spaces
139
+ inspection = inspection.gsub('\n'){ "\\n\" +\n \"" } if inspection =~ /^".*"$/
140
+ inspection = inspection.gsub("\n"){ "\n" + spaces }
141
+ return inspection.lstrip
142
+ end
143
+
144
+ def format_assertion_result(assertion_source, inspection)
145
+ spaces = " --> ".length
146
+ inspection = format_inspection(inspection, spaces)
147
+ return assertion_source.rstrip + "\n --> #{inspection.lstrip}\n"
148
+ end
149
+
150
+ def format_capture(width, snip, value)
151
+ return "#{ format_snip(width, snip) } --> #{ format_value(width, value) }"
152
+ end
153
+
154
+ def format_value(width, value) # TODO width is a de-facto instance variable
155
+ width += 4
156
+ source = value.pretty_inspect.rstrip
157
+ return format_inspection(source, width)
158
+ end
159
+
160
+ def measure_capture(kap)
161
+ return kap.split("\n").inject(0){|x, v| v.strip.length > x ? v.strip.length : x } if kap.match("\n")
162
+ kap.length
163
+ # TODO need the if?
164
+ end
165
+
166
+ end
167
+
168
+ def colorize(to_color)
169
+ RubyReflector.new.colorize(to_color)
170
+ end
171
+
172
+ # TODO work with raw MiniTest
173
+
174
+ # This is a copy of the classic assert, so your pre-existing
175
+ # +assert+ calls will not change their behavior
176
+ #
177
+ if defined? MiniTest::Assertion
178
+ def assert_classic(test, msg=nil)
179
+ msg ||= "Failed assertion, no message given."
180
+ self._assertions += 1
181
+ unless test then
182
+ msg = msg.call if Proc === msg
183
+ raise MiniTest::Assertion, msg
184
+ end
185
+ true
186
+ end
187
+
188
+ def add_assertion
189
+ self._assertions += 1
190
+ end
191
+ else
192
+ def assert_classic(boolean, message=nil)
193
+ #_wrap_assertion do
194
+ assert_block("assert<classic> should not be called with a block.") { !block_given? }
195
+ assert_block(build_message(message, "<?> is not true.", boolean)) { boolean }
196
+ #end
197
+ end
198
+ end
199
+
200
+ # The new <code>assert()</code> calls this to interpret
201
+ # blocks of assertive statements.
202
+ #
203
+ def assert_(diagnostic = nil, options = {}, &block)
204
+ options[:keep_diagnostics] or add_diagnostic :clear
205
+
206
+ begin
207
+ if got = block.call(*options[:args])
208
+ add_assertion
209
+ return got
210
+ end
211
+ rescue FlunkError
212
+ raise # asserts inside assertions that fail do not decorate the outer assertion
213
+ rescue => got
214
+ add_exception got
215
+ end
216
+
217
+ flunk diagnose(diagnostic, got, caller[1], options, block)
218
+ end
219
+
220
+ def add_exception(ex)
221
+ ex.backtrace[0..10].each do |line|
222
+ add_diagnostic ' ' + line
223
+ end
224
+ end
225
+
226
+ # This assertion replaces:
227
+ #
228
+ # - +assert_nil+
229
+ # - +assert_no_match+
230
+ # - +assert_not_equal+
231
+ #
232
+ # It faults, and prints its block's contents and values,
233
+ # if its block returns non-+false+ and non-+nil+.
234
+ #
235
+ def deny(diagnostic = nil, options = {}, &block)
236
+ # "None shall pass!" --the Black Knight
237
+
238
+ options[:keep_diagnostics] or add_diagnostic :clear
239
+
240
+ begin
241
+ got = block.call(*options[:args]) or (add_assertion and return true)
242
+ rescue FlunkError
243
+ raise
244
+ rescue => got
245
+ add_exception got
246
+ end
247
+
248
+ flunk diagnose(diagnostic, got, caller[0], options, block)
249
+ end # "You're a looney!" -- King Arthur
250
+
251
+ def deny_(diagnostic = nil, options = {}, &block)
252
+ # "None shall pass!" --the Black Knight
253
+
254
+ options[:keep_diagnostics] or add_diagnostic :clear
255
+
256
+ begin
257
+ got = block.call(*options[:args]) or (add_assertion and return true)
258
+ rescue FlunkError
259
+ raise
260
+ rescue => got
261
+ add_exception got
262
+ end
263
+
264
+ flunk diagnose(diagnostic, got, caller[0], options, block)
265
+ end # "You're a looney!" -- King Arthur
266
+
267
+ # FIXME document why this deny_ is here, and how to alias it back to deny
268
+
269
+ alias denigh deny # to line assert{ ... } and
270
+ # denigh{ ... } statements up neatly!
271
+
272
+ #~ def __reflect_assertion(called, options, block, got)
273
+ #~ effect = RubyReflector.new(called)
274
+ #~ effect.args = *options[:args]
275
+ #~ return effect.reflect_assertion(block, got)
276
+ #~ end
277
+
278
+ #~ def __reflect_assertion(called, options, block, got)
279
+ #~ effect = RubyReflector.new(called)
280
+ #~ effect.args = *options[:args]
281
+ #~ effect.block = block
282
+ #~ return effect.reflect_assertion(block, got) # TODO merge this and its copies into assert2_utilities
283
+ #~ end
284
+
285
+ #!doc!
286
+ def diagnose(diagnostic = nil, got = nil, called = caller[0],
287
+ options = {}, block = nil) # TODO make this directly callable
288
+ rf = RubyReflector.new
289
+ rf.diagnose(diagnostic, got, called, options, block, @__additional_diagnostics)
290
+ #~ options = { :args => [] }.merge(options)
291
+ #~ # CONSIDER only capture the block_vars if there be args?
292
+ #~ @__additional_diagnostics.unshift diagnostic
293
+ #~ return __build_message(__reflect_assertion(called, options, block, got))
294
+ end
295
+
296
+ if RubyReflector::HAS_RUBYNODE
297
+ # wrap this common idiom:
298
+ # foo = assemble()
299
+ # deny{ foo.bar() }
300
+ # foo.activate()
301
+ # assert{ foo.bar() }
302
+ #
303
+ # that becomes:
304
+ # foo = assemble()
305
+ #
306
+ # assert_yin_yang proc{ foo.bar() } do
307
+ # foo.activate()
308
+ # end
309
+ #
310
+ def assert_yin_yang(*args, &block)
311
+ # prock(s), diagnostic = nil, &block)
312
+ procks, diagnostic = args.partition{|p| p.respond_to? :call }
313
+ block ||= procks.shift
314
+ source = reflect_source(&block)
315
+ fuss = [diagnostic, "fault before calling:", source].compact.join("\n")
316
+ procks.each do |prock| deny(fuss, &prock); end
317
+ block.call
318
+ fuss = [diagnostic, "fault after calling:", source].compact.join("\n")
319
+ procks.each do |prock| assert(fuss, &prock); end
320
+ end
321
+
322
+ # the prock assertion must pass on both sides of the called block
323
+ #
324
+ def deny_yin_yang(*args, &block)
325
+ # prock(s), diagnostic = nil, &block)
326
+ procks, diagnostic = args.partition{|p| p.respond_to? :call }
327
+ block ||= procks.shift
328
+ source = reflect_source(&block)
329
+ fuss = [diagnostic, "fault before calling:", source].compact.join("\n")
330
+ procks.each do |prock| assert(fuss, &prock); end
331
+ block.call
332
+ fuss = [diagnostic, "fault after calling:", source].compact.join("\n")
333
+ procks.each do |prock| assert(fuss, &prock); end
334
+ end
335
+
336
+ end
337
+
338
+ end ; end ; end
339
+
340
+ class File
341
+ def self.write(filename, contents)
342
+ open(filename, 'w'){|f| f.write(contents) }
343
+ end
344
+ end
@@ -1,76 +1,75 @@
1
- require 'pp'
2
-
3
-
4
- module Test; module Unit; module Assertions
5
-
6
- # ERGO
7
- # :bmethod => [:cval],
8
- # :cfunc => [:argc, :cfnc],
9
- # :cref => [:next, :clss],
10
- # :defs => [:mid, :defn, :recv],
11
- # :dmethod => [:cval],
12
- # :dot2 => [:beg, :end],
13
- # :dot3 => [:beg, :end],
14
- # :dregx_once => [:next, :lit, :cflag],
15
- # :fbody => [:orig, :mid, :head],
16
- # :flip2 => [:cnt, :beg, :end],
17
- # :flip3 => [:cnt, :beg, :end],
18
- # :gasgn => [:vid, :value], # entry not supported
19
- # :ifunc => [:tval, :state, :cfnc],
20
- # :lasgn => [:vid, :cnt, :value],
21
- # :last => [],
22
- # :match => [:lit],
23
- # :memo => {:u1_value=>:u1_value}, # different uses in enum.c, variabe.c and eval.c ...
24
- # :method => [:body, :noex, :cnt], # cnt seems to be always 0 in 1.8.4
25
- # :module => [:cpath, :body],
26
- # :next => [:stts],
27
- # :opt_n => [:body],
28
- # :to_ary => [:head],
29
-
30
- # This +reflect+s a block of code, by evaluating it, reflecting its
31
- # source, and reflecting all its intermediate values
32
- #
33
- def reflect(&block)
34
- result = block.call
1
+ require 'pp'
2
+
3
+
4
+ module Test; module Unit; module Assertions
5
+
6
+ # ERGO
7
+ # :bmethod => [:cval],
8
+ # :cfunc => [:argc, :cfnc],
9
+ # :cref => [:next, :clss],
10
+ # :defs => [:mid, :defn, :recv],
11
+ # :dmethod => [:cval],
12
+ # :dot2 => [:beg, :end],
13
+ # :dot3 => [:beg, :end],
14
+ # :dregx_once => [:next, :lit, :cflag],
15
+ # :fbody => [:orig, :mid, :head],
16
+ # :flip2 => [:cnt, :beg, :end],
17
+ # :flip3 => [:cnt, :beg, :end],
18
+ # :gasgn => [:vid, :value], # entry not supported
19
+ # :ifunc => [:tval, :state, :cfnc],
20
+ # :lasgn => [:vid, :cnt, :value],
21
+ # :last => [],
22
+ # :match => [:lit],
23
+ # :memo => {:u1_value=>:u1_value}, # different uses in enum.c, variabe.c and eval.c ...
24
+ # :method => [:body, :noex, :cnt], # cnt seems to be always 0 in 1.8.4
25
+ # :module => [:cpath, :body],
26
+ # :next => [:stts],
27
+ # :opt_n => [:body],
28
+ # :to_ary => [:head],
29
+
30
+ # This +reflect+s a block of code, by evaluating it, reflecting its
31
+ # source, and reflecting all its intermediate values
32
+ #
33
+ def reflect(&block)
34
+ result = block.call
35
35
  rf = RubyReflector.new
36
- rf.block = block
37
-
38
- begin
39
- waz = rf.colorize?
40
- rf.colorize(false)
41
- return rf.result + rf.arrow_result(result) + "\n" + rf.format_evaluations
42
- ensure
43
- rf.colorize(waz)
44
- end
45
- end
46
-
47
- # This +reflect+s a block of code, /without/ evaluating it.
48
- # The function only compiles the source and reflects it as
49
- # a string of disassembled Ruby
50
- #
51
- def reflect_source(&block)
36
+ rf.block = block
37
+
38
+ begin
39
+ waz = rf.colorize?
40
+ rf.colorize(false)
41
+ return rf.result + rf.arrow_result(result) + "\n" + rf.format_evaluations
42
+ ensure
43
+ rf.colorize(waz)
44
+ end
45
+ end
46
+
47
+ # This +reflect+s a block of code, /without/ evaluating it.
48
+ # The function only compiles the source and reflects it as
49
+ # a string of disassembled Ruby
50
+ #
51
+ def reflect_source(&block)
52
52
  rf = RubyReflector.new(nil, false)
53
53
  rf.block = block
54
- return rf.result
55
- end
56
-
57
- # This compiles a string and +reflect+s its source...
58
- # as another string.
59
- #
54
+ return rf.result
55
+ end
56
+
57
+ # This compiles a string and +reflect+s its source...
58
+ # as another string.
59
+ #
60
60
  def reflect_string(string)
61
61
  rf = RubyReflector.new # def initialize
62
62
  rf.block = proc{}
63
- rf.reflect_values = false
64
- # pp string.parse_to_nodes.transform
65
- got = rf.reflect_nodes(string.parse_to_nodes)
66
- return got
67
- end
68
-
63
+ rf.reflect_values = false
64
+ # pp string.parse_to_nodes.transform
65
+ got = rf.reflect_nodes(string.parse_to_nodes)
66
+ return got
67
+ end
68
+
69
69
  class RubyReflector # this class turns hamburger back into live cattle
70
70
  HAS_RIPPER = false
71
-
71
+
72
72
  begin
73
- raise LoadError, 'whatever' if ENV['TEST_ASSERT_2_IN_1_8_7_MODE'] == 'true'
74
73
  require 'rubygems'
75
74
  require 'rubynode'
76
75
  HAS_RUBYNODE = true
@@ -78,744 +77,745 @@ module Test; module Unit; module Assertions
78
77
  HAS_RUBYNODE = false
79
78
  end
80
79
 
81
- attr_reader :evaluations,
82
- :result,
83
- :transformation
84
- attr_writer :block,
85
- :reflect_values
86
-
80
+ attr_reader :evaluations,
81
+ :result,
82
+ :transformation
83
+ attr_writer :block,
84
+ :reflect_values
85
+
87
86
  def initialize(called = nil, yo_block = nil, reflect_values = true) # note that a block, from your context, is not optional
88
- # FIXME these args are bogus use or lose
89
- @reflect_values = reflect_values
90
- @evaluations = []
91
- @result = ''
92
- @line = 0
93
- self.block = yo_block
94
- end
95
-
87
+ # FIXME these args are bogus use or lose
88
+ @reflect_values = reflect_values
89
+ @evaluations = []
90
+ @result = ''
91
+ @line = 0
92
+ self.block = yo_block
93
+ end
94
+
96
95
  def block=(yo_block)
97
- @block = yo_block and HAS_RUBYNODE and
98
- reflect_nodes(@block.body_node)
99
- end
100
-
101
- def reflect_nodes(body_node)
102
- return unless body_node
103
- @transformation = body_node.transform(:include_node => true)
104
- return @result = _send(@transformation)
105
- rescue
106
- puts "\nOffending line: #{ @line }"
107
- raise
108
- end
109
-
110
- def absorb_block_args(code_fragments) # CONSIDER a suckier way of detecting
111
- @captured_block_vars = nil # the block args is indeed remotely possible...
112
- if code_fragments.first =~ /\|(.*)\|/ or code_fragments[1].to_s =~ /\|(.*)\|/
113
- @captured_block_vars = $1
114
- end
115
- end
116
-
117
- def detect(expression)
118
- expr = expression
119
- $__args = nil
120
- if @args and @captured_block_vars
121
- expr = "#{@captured_block_vars} = $__args.kind_of?(Array) && $__args.length == 1 ? $__args.first : $__args\n" +
122
- expr
123
- $__args = @args
124
- end
125
-
126
- begin
127
- intermediate = eval(expr, @block.binding)
128
- @evaluations << [expression, intermediate, nil]
129
- rescue SyntaxError => e
130
- if e.message.index('syntax error, unexpected \',\'') and expression !~ /\[ /
131
- return detect('[ ' + expression + ' ]')
132
- end # faint prayer to infinite recursion diety here! (-;
133
-
134
- @evaluations << [expression, nil, e.message]
135
- rescue => e
136
- @evaluations << [expression, nil, e.message]
137
- end
138
- end
139
-
140
- def eval_intermediate(expression)
141
- detect(expression) if @reflect_values
142
- return expression
143
- end
144
-
145
- def short_inspect(intermediate)
146
- pretty = intermediate.inspect
147
- # ERGO Proc is prob'ly rare here!
148
- pretty = { '#<Proc' => '<Proc>' }.fetch(pretty.split(':').first, pretty)
149
- prettier = pretty[0..90]
150
- prettier << '*** ' unless prettier == pretty
151
- return prettier
152
- end
153
- private :short_inspect
154
-
155
- # ERGO spew the backrefs (?) any regular expression matchers may emit!
156
- # ERGO don't eval the caller of a block without its block!
157
-
158
- def format_evaluations
159
- max_line = @evaluations.map{|exp, val, prob| exp.length}.sort.last
160
- already = {}
161
- lines = []
162
-
163
- @evaluations.each do |exp, val, prob|
164
- line = " #{ exp.center(max_line) } "
165
-
166
- line << if prob then
167
- orange('--? ' + prob)
168
- else
169
- green('--> ') + bold(short_inspect(val))
170
- end
171
-
172
- lines << line unless already[line] == true
173
- already[line] = true
174
- end
175
-
176
- return lines.compact.join("\n")
177
- end
178
-
179
- def _send(node, thence = '')
180
- return '' unless node
181
- return node.to_s + thence if node.class == Symbol
182
- target = :"_#{ node.first }"
183
- last = node.last
184
- (@line = last[:node].line) rescue nil
185
- exp = send(target, last)
186
- exp << thence if exp.length > 0
187
- return exp
188
- end
189
-
190
- %w( args beg body cond cpath defn else end ensr
191
- first head iter ivar lit mid next second
192
- stts recv resq rest value var vid ).each do |sender|
193
- define_method sender + '_' do |node, *args|
194
- return _send(node[sender.to_sym], *args)
195
- end
196
- end
197
-
198
- ########################################################
199
- #### structures
200
-
201
- def _block(node)
202
- return node.map{|n| _send(n, "\n") }.join
203
- end
204
-
205
- def _module(node, what = 'module')
206
- return what + ' ' + cpath_(node) + "\n" +
207
- body_(node) +
208
- "\nend\n"
209
- end
210
-
211
- def _method(node)
212
- p node
213
- return ''
214
- end
215
-
216
- def _class(node); _module(node, 'class'); end
217
- def _self(node); 'self'; end
218
- def _defn(node); _defs(node); end
219
- def _super(node); 'super(' + args_(node) + ')'; end
220
- def _zsuper(node); 'super'; end
221
- def _begin(node); "begin\n" + body_(node) + "\nend\n"; end
222
- def _ensure(node); head_(node) + "\nensure\n" + ensr_(node); end
223
-
224
- def _sclass(node)
225
- return 'class << ' + recv_(node) +
226
- head_body(node) +
227
- "end\n"
228
- end
229
-
230
- class ScopeMethod #:nodoc:
231
- # this is complex because Ruby gloms several different
232
- # kinds of variables into one "scope" token, and they
233
- # don't directly match their layout in the source code
234
-
235
- def _scopic(ref, node)
236
- @ref = ref
237
- @node = node
238
- @expression = ''
239
- @previously = false
240
- @block_arg = false
241
- @splat_arg = false
242
- @previous_splat = false
243
-
244
- if @node[:tbl]
245
- @expression << '('
246
- render_argument_list
247
- @expression << ')'
248
- end
249
-
250
- @expression << "\n"
251
- @expression << @ref.next_(@node)
252
- return @expression
253
- end
254
-
255
- def ulterior_comma(token)
256
- @expression << ', ' if @index > 0
257
- @expression << token
258
- @index = 0
259
- end
260
-
261
- def possible_comma(token)
262
- @expression << ', ' if @index > 0
263
- @expression << @ref._send(token)
264
- end
265
-
266
- def render_argument_list
267
- @nekst = @node[:next]
268
- @block = @nekst.last if @nekst && @nekst.first == :block
269
- @args = @block.first.last if @block && @block.first.first == :args
270
- @rest = @args[:rest] if @args
271
- @opt = @args[:opt] if @args
272
-
273
- @node[:tbl].each_with_index do |_n, _index|
274
- @n, @index = _n, _index
275
- render_argument
276
- break if @block_arg
277
- end
278
- end
279
-
280
- def render_argument
281
- @splat_arg = @block_arg = false
282
-
283
- if @rest and @rest.first == :lasgn and
284
- (@n == nil or @rest.last[:vid] == @n)
285
- ulterior_comma('*')
286
- @splat_arg = true
287
- end
288
-
289
- if @block and (ba = @block[1]) and
290
- ba.first == :block_arg and ba.last[:vid] == @n
291
- ulterior_comma('&')
292
- @block_arg = true
293
- end
294
-
295
- # ERGO Ruby 1.9 changes these rules!!
296
-
297
- if !@previous_splat or @block_arg
298
- if @opt and @opt.first == :block and # ERGO why a @block??
299
- (lasgn = @opt.last.first).first == :lasgn and
300
- lasgn.last[:vid] == @n
301
- @previously = true
302
- possible_comma(@opt.last.first)
303
- else
304
- possible_comma(@n)
305
- @expression << ' = nil' if @previously and !@block_arg and !@splat_arg
306
- end
307
-
308
- @previous_splat ||= @splat_arg
309
- end
310
- end
311
- end
312
-
313
- def _scope(node)
314
- return ScopeMethod.new._scopic(self, node)
315
- end
316
-
317
- def _defs(node)
318
- return 'def ' + recv_(node, '.') + mid_(node) +
319
- defn_(node) +
320
- "end\n"
321
- end
322
-
323
- def _rescue(node)
324
- if node[:else] == false and node[:head] and
325
- node[:resq] and node[:head].first == :vcall
326
- return head_(node) + ' rescue ' + resq_(node)
327
- else
328
- exp = head_(node) +
329
- else_(node) +
330
- "rescue"
331
- if node[:resq] and node[:resq].first == :resbody
332
- body = node[:resq].last
333
- exp << ' ' + args_(body) if body and body[:args]
334
- end
335
- return exp + "\n" + resq_(node)
336
- end
337
- end
338
-
339
- def _resbody(node)
340
- return body_(node)
341
- # already emitted: head_(node) + ' ' + args_(node)
342
- end
343
-
344
- def _yield(node)
345
- exp = 'yield'
346
- exp << '(' + head_(node) + ')' if node[:head]
347
- return exp
348
- end
349
-
350
- def _alias(node)
351
- return "alias #{ lit_(node[:new].last) } #{ lit_(node[:old].last) }"
352
- end
353
-
354
- def _valias(node)
355
- return "alias #{ node[:new] } #{ node[:old] }"
356
- end
357
-
358
- ########################################################
359
- #### control flow
360
-
361
- def _if(node)
362
- expression = '( if ' + eval_parenz{cond_(node)} + ' then '
363
- expression << eval_parenz{body_(node)} if node[:body]
364
- expression << ' else ' +
365
- eval_parenz{else_(node)} if node[:else]
366
- expression << ' end )'
367
- return expression
368
- end
369
-
370
- def _while(node, concept = 'while')
371
- return '( ' + concept + ' ' + cond_(node) +
372
- head_body(node) +
373
- "\nend )"
374
- end
375
-
376
- def _for(node)
377
- return '( for ' + var_(node) + ' in ' + iter_(node) + "\n" +
378
- body_(node) + "\n" +
379
- 'end )'
380
- end
381
-
382
- def _args(node); return ''; end # _call and _fcall insert the real args
383
- def _until(node); _while(node, 'until'); end
384
- def _break(node); 'break'; end
385
- def _next(node); 'next' ; end
386
- def _case(node); '( case ' + head_body(node) + "\nend )"; end
387
- def _when(node); 'when ' + head_body(node) + "\n" + next_(node); end
388
- def _retry(node); 'retry'; end
389
- def _redo(node); 'redo'; end
390
- def head_body(node); head_(node) + "\n" + body_(node); end
391
-
392
- def _return(node)
393
- exp = 'return'
394
- return exp unless stts = node[:stts]
395
- exp << ' '
396
-
397
- if stts.first == :array
398
- exp << '[' + stts_(node) + ']'
399
- elsif stts.first == :svalue
400
- exp << stts_(node)
401
- else
402
- exp << eval_parenz{stts_(node)}
403
- end
404
-
405
- return exp
406
- end
407
-
408
- def _postexe(node)
409
- raise '_postexe called with unexpected arguments' unless node == {} or node.keys == [:node]
410
- return 'END'
411
- end
412
-
413
- # :argscat => [:body, :head],
414
-
415
- ########################################################
416
- #### assignments
417
-
418
- def _dasgn_curr(node)
419
- expression = vid_(node)
420
- return expression unless value = node[:value]
421
- expression << ' = '
422
- we_b_array = value.first == :array
423
- expression << nest_if(we_b_array, '[', ']'){ value_(node) }
424
- return expression
425
- end
426
-
427
- def _cdecl(node)
428
- return _send(node[ node[:vid] == 0 ? :else : :vid ]) + ' = ' + value_(node)
429
- end
430
-
431
- def _dasgn(node); _dasgn_curr(node); end
432
- def _iasgn(node); _dasgn_curr(node); end
433
- def _gasgn(node); _dasgn_curr(node); end
434
- def _lasgn(node); _dasgn_curr(node); end
435
- def _cvasgn(node); _dasgn_curr(node); end
436
-
437
- def _op_asgn2(node)
438
- expression = ''
439
-
440
- if node[:recv].first == :self
441
- expression << 'self'
442
- else
443
- expression << recv_(node)
444
- end
445
-
446
- expression << '.'
447
- expression << vid_(node[:next].last) + ' ||= ' + value_(node)
448
- return expression
449
- end
450
-
451
- ########################################################
452
- #### operators
453
-
454
- def _and(node, und = 'and')
455
- return eval_intermediate( '( ' +
456
- eval_parenz{ first_(node)} + ' ' + und + ' ' +
457
- eval_parenz{second_(node)} + ' )' )
458
- end
459
-
460
- def _back_ref(node); '$' + node[:nth].chr; end
461
- def _colon2(node); head_(node, '::') + mid_(node); end
462
- def _colon3(node); '::' + mid_(node); end
463
- def _cvar(node); _lvar(node); end
464
- def _cvdecl(node); vid_(node) + ' = ' + value_(node); end
465
- def _defined(node); 'defined? ' + head_(node); end
466
- def _dot2(node); '( ' + beg_(node) + ' .. ' + end_(node) + ' )'; end
467
- def _dot3(node); '( ' + beg_(node) + ' ... ' + end_(node) + ' )'; end
468
- def _dregx(node); _dstr(node, '/'); end
469
- def _dregx_once(node); _dstr(node, '/'); end
470
- def _dsym(node); ':' + _lit(node[:lit]) + ' ' + rest_(node); end
471
- def _dvar(node); eval_intermediate(vid_(node)); end
472
- def _dxstr(node); _dstr(node, '`'); end
473
- def eval_parenz; eval_intermediate('( ' + yield + ' )'); end
474
- def _evstr(node); body_(node); end
475
- def _false(nada); 'false'; end
476
- def _gvar(node); vid_(node); end
477
- def _ivar(node); _dvar(node); end
478
- def _lit(node); node[:lit].inspect; end
479
- def _lvar(node); eval_intermediate(vid_(node)); end
480
- def _match(node); node[:lit].inspect; end
481
- def neg_one(node); node == -1 ? '' : _send(node); end
482
- def _nil(nada); 'nil' ; end
483
- def _not(node); '(not(' + body_(node) + '))'; end
484
- def _nth_ref(node); "$#{ node[:nth] }"; end # ERGO eval it?
485
- def _op_asgn_and(node); _op_asgn_or(node, ' &&= '); end
486
- def _or(node); _and(node, 'or'); end
487
- def _str(node); _lit(node); end
488
- def _svalue(node); head_(node); end
489
- def _to_ary(node); head_(node); end
490
- def _true(nada); 'true' ; end
491
- def _undef(node); 'undef ' + mid_(node); end
492
- def _vcall(node); mid_(node); end
493
- def we_b(node); node.first.first; end
494
- def _xstr(node); '`' + scrape_literal(node) + '`'; end
495
- def _zarray(node); return '[]'; end
496
-
497
- def _flip2(node) # ERGO what the heck is this??
498
- p node
499
- p node.keys
500
- return ''
501
- end
502
-
503
- def _masgn(node)
504
-
505
- #{:value=>
506
- # [:splat,
507
- # {:head=>
508
- # [:fcall,
509
- # {:mid=>:calc_stack,
510
- # :args=>
511
- # [:array,
512
- # [[:vcall, {:mid=>:insn}],
513
- # [:vcall, {:mid=>:from}],
514
- # [:vcall, {:mid=>:after}],
515
- # [:vcall, {:mid=>:opops}]]]}]}],
516
- # :args=>false,
517
- # :head=>
518
- # [:array,
519
- # [[:dasgn_curr, {:value=>false, :vid=>:name}],
520
- # [:dasgn_curr, {:value=>false, :vid=>:pops}],
521
- # [:dasgn_curr, {:value=>false, :vid=>:rets}],
522
- # [:dasgn_curr, {:value=>false, :vid=>:pushs1}],
523
- # [:dasgn_curr, {:value=>false, :vid=>:pushs2}]]]}
524
-
525
- value, head, args = node.values_at(:value, :head, :args)
526
-
527
- if value
528
- return '( ' + head_(node) + ' = *' + head_(value.last) + ' )' if value.first == :splat
529
-
530
- if head and args
531
- exp = head_(node)
532
- return exp + ', * = ' + value_(node) if args == -1
533
- return exp + ', *' + args_(node) + ' = ' + value_(node)
534
- end
535
-
536
- return '( ' + head_(node) + ' = ' + value_(node) + ' )' if args == false
537
- end
538
-
539
- if value == false and head == false and args
540
- return '*' + neg_one(args)
541
- end
542
-
543
- if head.kind_of?(Array) and head.first == :array
544
- return head.last.map{|n|
545
- nest_if(n.first == :masgn, '(', ')'){ _send(n) }
546
- }.join(', ')
547
- end
548
-
549
- if head == false and args and value
550
- return '*' + args_(node) + ' = ' + value_(node)
551
- end
552
-
553
- return head_(node)
554
- end
555
-
556
- def _splat(node)
557
- if (head = node[:head]) and
558
- ((we_b_array = head.first == :array) or head.first == :lvar)
559
- return '*' + nest_if(we_b_array, '[', ']'){ head_(node) }
560
- end
561
-
562
- return '*' + head_(node)
563
- end # ERGO raise if any other key!
564
-
565
- def _const(node)
566
- expression = vid_(node)
567
- q = eval(expression, @block.binding)
568
- eval_intermediate(expression) unless q.kind_of?(Module)
569
- return expression
570
- rescue # ERGO will someone need to see whatever this was?
571
- return expression
572
- end
573
-
574
- def scrape_literal(node, regex = false)
575
- lit = node[:lit].inspect.gsub(/^"/, '').gsub(/"$/, '')
576
- lit.gsub!('\\\\', '\\') if regex
577
- return lit
578
- end
579
-
580
- def _dstr(node, delim = '"')
581
- regex = delim == '/'
582
- expression = delim + scrape_literal(node, regex)
583
-
584
- if node[:next] and node[:next].first == :array
585
- (node[:next].last || []).each do |n|
586
- expression << if n.first == :str
587
- scrape_literal(n.last, regex)
588
- else
589
- '#{ ' + _send(n) + ' }'
590
- end
591
- end
592
- end
593
-
594
- return eval_intermediate(expression + delim)
595
- end
596
-
597
- def _retry(node)
598
- raise '_retry called with unexpected arguments' unless node == {} or node.keys == [:node]
599
- return 'retry'
600
- end
601
-
602
- def recv_zero_self(node, plus = '')
603
- recv = node[:recv]
604
- return 'self' + plus if recv == 0
605
- return recv_(node, plus)
606
- end
607
-
608
- def _attrasgn(node)
609
- recv, args = node.values_at(:recv, :args)
610
-
611
- if args
612
- if args.first == :array
613
- if node[:mid].class == Symbol
614
- if node[:mid] == :'[]='
615
- return recv_zero_self(node) + '[' +
616
- _send(args.last.first) + '] = ' +
617
- _send(args.last.last)
618
- end
619
- return recv_zero_self(node, '.') + mid_(node) + '(' + _send(args.last.last) + ')'
620
- end
621
- end
622
-
623
- return recv_zero_self(node) +
624
- '[' + head_(args.last) + '] = ' +
625
- body_(args.last)
626
- end
627
-
628
- return recv_zero_self(node, '.') + node[:mid].to_s.gsub(/=$/, '')
629
- end
630
-
631
- def _op_asgn_or(node, op = ' ||= ')
632
- # CONSIDER what be :aid?
633
- #{:value=>[:lasgn, {:value=>[:str, {:lit=>"vm_opts.h"}], :cnt=>2, :vid=>:file}],
634
- # :aid=>0,
635
- # :head=>[:lvar, {:cnt=>2, :vid=>:file}]}
636
- return head_(node) + op + value_(node[:value].last)
637
- end
638
-
639
- def fcall_args(node = nil, methodic = false)
640
- expression = ''
641
- return expression unless node
642
- expression << ' ' unless methodic
643
- return expression + nest_if(methodic, '(', ')'){ _send(node) }
644
- end
645
-
646
- def _fcall(node)
647
- exp = mid_(node) + fcall_args(node[:args], true)
648
- eval_intermediate(exp) unless %w(lambda proc).include?(exp)
649
- return exp
650
- end
651
-
652
- def _block_pass(node)
653
- fcall = node[:iter].last
654
- return eval_intermediate(recv_(fcall, '.') +
655
- mid_(fcall) + '(' + args_(fcall, ', ') +
656
- '&' + body_(node) + ')' )
657
- end
658
-
659
- def _iter(node)
660
- var = node[:var]
661
-
662
- return eval_intermediate(
663
- iter_(node) +
664
- '{' +
665
- nest_if(var != false, '|', '|'){ var_(node) unless var == 0 } +
666
- nest_if(node[:body] , ' ', ' '){ body_(node) } +
667
- '}')
668
- end
669
-
670
- def _array(node)
671
- nest_if we_b(node) == :array, '[', ']' do
672
- node.map{ |z|
673
- exp = _send(z)
674
- exp << ', ' unless z.object_id == node.last.object_id
675
- exp
676
- }.join
677
- end
678
- end
679
-
680
- def _hash(node)
681
- return '{}' unless node[:head] and (array = node[:head].last)
682
- expression = '{ '
683
-
684
- array.in_groups_of 2 do |key, value|
685
- expression << _send(key) + ' => ' + _send(value)
686
- expression << ', ' if value != array.last
687
- end
688
-
689
- return expression + ' }'
690
- end
691
-
692
- def _match2(node)
693
- # ERGO should this work like match3?
694
- return recv_(node) + ' =~ ' + value_(node)
695
- end
696
-
697
- def we_b_op(node)
698
- return node[:mid] && node[:mid].to_s !~ /^[a-z]/i
699
- end
700
-
701
- def _match3(node)
702
- # ERGO do :lit and :value exclude each other?
703
- return recv_(node) + ' =~ ' + _send(node[:lit] || node[:value])
704
- end
705
-
706
- def _block_arg(node) # is this ever called?
707
- return '' # note that _scope should not take care of this
708
- end
709
-
710
- class CallMethod
711
- def bracket_args
712
- return false unless @mid == '[]'
713
- @expression << '[' + @ref.args_(@node) + ']'
714
- return true
715
- end
716
-
717
- def insert_method_call
718
- @expression << '.'
719
- @expression << @mid
720
- @expression << '(' + @ref.args_(@node) + ')'
721
- @ref.eval_intermediate(@expression) if @methodic
722
- end
723
-
724
- def operator_and_arguments
725
- @mid = @ref.mid_(@node)
726
-
727
- unless bracket_args
728
- @methodic = @mid =~ /[a-z]/i
729
-
730
- if @methodic
731
- insert_method_call
732
- else
733
- @expression << ' '
734
- @expression << @mid
735
- @expression << ' '
736
- nest_args
737
- end
738
- end
739
- end
740
-
741
- def nest_args
742
- return unless @args = @node[:args]
743
-
744
- nest_me = @args.first == :array &&
745
- @args.last.length == 1 &&
746
- (call = @args.last.first).first == :call &&
747
- @ref.we_b_op(call.last)
748
-
749
- exp = @ref.nest_if(nest_me, '( ', ' )'){ @ref.args_(@node) }
750
- @ref.eval_intermediate(exp) if nest_me
751
- @expression << exp
752
- end
753
-
754
- def caller(ref, node)
755
- @ref, @node = ref, node
756
- @expression = ''
757
- @recv = @node[:recv]
758
-
759
- if @recv.first == :block_pass
760
- @expression << @ref.recv_(@node)
761
- operator_and_arguments
762
- else
763
- nest_me = @recv.first == :call && @ref.we_b_op(@recv.last)
764
-
765
- exp = if @recv.first == :array
766
- @ref.nest_if(true, '[ ', ' ]'){ @ref.recv_(node) }
767
- else
768
- exp2 = @ref.nest_if(nest_me, '( ', ' )'){ @ref.recv_(node) }
769
- @ref.eval_intermediate(exp2) if nest_me
770
- exp2
771
- end
772
-
773
- @expression << exp
774
- operator_and_arguments
775
- end
776
- return @expression
777
- end
778
- end
779
-
780
- def _call(node); CallMethod.new.caller(self, node); end
781
-
782
- def nest_if(condition, before, after, &block)
783
- exp = ''
784
- exp << before if condition
785
- exp << (block.call || '')
786
- exp << after if condition
787
- return exp
788
- end
789
-
790
- def _op_asgn1(node) # ERGO just look up the list of these?
791
- return '' unless args = node[:args]
792
- exp = recv_(node)
793
-
794
- if [:'-', :'+', :'*', :'**', :'/', :^, :|, :&,
795
- :'<<', :'>>',
796
- ].include?(node[:mid]) and
797
- node[:recv] and args.first == :argscat
798
-
799
- return exp +
800
- "[#{ body_(args.last) }] #{ node[:mid] }= " + head_(args.last)
801
- end
802
-
803
- raise "unexpected mid value #{ node[:mid].inspect } in opcode for X= " unless node[:mid] == 0
804
-
805
- if args.first == :argscat and args.last[:body]
806
- exp << '[' + body_(args.last) + ']'
807
- exp << ' ||= ' + head_(args.last)
808
- else
809
- raise "unexpected arguments in opcode for ||= "
810
- end
811
-
812
- return exp
813
- end
814
-
815
- def _argscat(node)
816
- return head_(node) + ', *' +
817
- nest_if(node[:body].first == :array, '[', ']'){ body_(node) }
818
- end
96
+ @block = yo_block and @block.respond_to?(:body_node) and
97
+ reflect_nodes(@block.body_node)
98
+ end
99
+
100
+ def reflect_nodes(body_node)
101
+ if body_node
102
+ @transformation = body_node.transform(:include_node => true)
103
+ return @result = _send(@transformation)
104
+ end
105
+ rescue
106
+ puts "\nOffending line: #{ @line }"
107
+ raise
108
+ end
109
+
110
+ def absorb_block_args(code_fragments) # CONSIDER a suckier way of detecting
111
+ @captured_block_vars = nil # the block args is indeed remotely possible...
112
+ if code_fragments.first =~ /\|(.*)\|/ or code_fragments[1].to_s =~ /\|(.*)\|/
113
+ @captured_block_vars = $1
114
+ end
115
+ end
116
+
117
+ def detect(expression)
118
+ expr = expression
119
+ $__args = nil
120
+ if @args and @captured_block_vars
121
+ expr = "#{@captured_block_vars} = $__args.kind_of?(Array) && $__args.length == 1 ? $__args.first : $__args\n" +
122
+ expr
123
+ $__args = @args
124
+ end
125
+
126
+ begin
127
+ intermediate = eval(expr, @block.binding)
128
+ @evaluations << [expression, intermediate, nil]
129
+ rescue SyntaxError => e
130
+ if e.message.index('syntax error, unexpected \',\'') and expression !~ /\[ /
131
+ return detect('[ ' + expression + ' ]')
132
+ end # faint prayer to infinite recursion diety here! (-;
133
+
134
+ @evaluations << [expression, nil, e.message]
135
+ rescue => e
136
+ @evaluations << [expression, nil, e.message]
137
+ end
138
+ end
139
+
140
+ def eval_intermediate(expression)
141
+ detect(expression) if @reflect_values
142
+ return expression
143
+ end
144
+
145
+ def short_inspect(intermediate)
146
+ pretty = intermediate.inspect
147
+ # ERGO Proc is prob'ly rare here!
148
+ pretty = { '#<Proc' => '<Proc>' }.fetch(pretty.split(':').first, pretty)
149
+ prettier = pretty[0..90]
150
+ prettier << '*** ' unless prettier == pretty
151
+ return prettier
152
+ end
153
+ private :short_inspect
154
+
155
+ # ERGO spew the backrefs (?) any regular expression matchers may emit!
156
+ # ERGO don't eval the caller of a block without its block!
157
+
158
+ def format_evaluations
159
+ max_line = @evaluations.map{|exp, val, prob| exp.length}.sort.last
160
+ already = {}
161
+ lines = []
162
+
163
+ @evaluations.each do |exp, val, prob|
164
+ line = " #{ exp.center(max_line) } "
165
+
166
+ line << if prob then
167
+ orange('--? ' + prob)
168
+ else
169
+ green('--> ') + bold(short_inspect(val))
170
+ end
171
+
172
+ lines << line unless already[line] == true
173
+ already[line] = true
174
+ end
175
+
176
+ return lines.compact.join("\n")
177
+ end
178
+
179
+ def _send(node, thence = '')
180
+ return '' unless node
181
+ return node.to_s + thence if node.class == Symbol
182
+ target = :"_#{ node.first }"
183
+ last = node.last
184
+ (@line = last[:node].line) rescue nil
185
+ exp = send(target, last)
186
+ exp << thence if exp.length > 0
187
+ return exp
188
+ end
189
+
190
+ %w( args beg body cond cpath defn else end ensr
191
+ first head iter ivar lit mid next second
192
+ stts recv resq rest value var vid ).each do |sender|
193
+ define_method sender + '_' do |node, *args|
194
+ return _send(node[sender.to_sym], *args)
195
+ end
196
+ end
197
+
198
+ ########################################################
199
+ #### structures
200
+
201
+ def _block(node)
202
+ return node.map{|n| _send(n, "\n") }.join
203
+ end
204
+
205
+ def _module(node, what = 'module')
206
+ return what + ' ' + cpath_(node) + "\n" +
207
+ body_(node) +
208
+ "\nend\n"
209
+ end
210
+
211
+ def _method(node)
212
+ p node
213
+ return ''
214
+ end
215
+
216
+ def _class(node); _module(node, 'class'); end
217
+ def _self(node); 'self'; end
218
+ def _defn(node); _defs(node); end
219
+ def _super(node); 'super(' + args_(node) + ')'; end
220
+ def _zsuper(node); 'super'; end
221
+ def _begin(node); "begin\n" + body_(node) + "\nend\n"; end
222
+ def _ensure(node); head_(node) + "\nensure\n" + ensr_(node); end
223
+
224
+ def _sclass(node)
225
+ return 'class << ' + recv_(node) +
226
+ head_body(node) +
227
+ "end\n"
228
+ end
229
+
230
+ class ScopeMethod #:nodoc:
231
+ # this is complex because Ruby gloms several different
232
+ # kinds of variables into one "scope" token, and they
233
+ # don't directly match their layout in the source code
234
+
235
+ def _scopic(ref, node)
236
+ @ref = ref
237
+ @node = node
238
+ @expression = ''
239
+ @previously = false
240
+ @block_arg = false
241
+ @splat_arg = false
242
+ @previous_splat = false
243
+
244
+ if @node[:tbl]
245
+ @expression << '('
246
+ render_argument_list
247
+ @expression << ')'
248
+ end
249
+
250
+ @expression << "\n"
251
+ @expression << @ref.next_(@node)
252
+ return @expression
253
+ end
254
+
255
+ def ulterior_comma(token)
256
+ @expression << ', ' if @index > 0
257
+ @expression << token
258
+ @index = 0
259
+ end
260
+
261
+ def possible_comma(token)
262
+ @expression << ', ' if @index > 0
263
+ @expression << @ref._send(token)
264
+ end
265
+
266
+ def render_argument_list
267
+ @nekst = @node[:next]
268
+ @block = @nekst.last if @nekst && @nekst.first == :block
269
+ @args = @block.first.last if @block && @block.first.first == :args
270
+ @rest = @args[:rest] if @args
271
+ @opt = @args[:opt] if @args
272
+
273
+ @node[:tbl].each_with_index do |_n, _index|
274
+ @n, @index = _n, _index
275
+ render_argument
276
+ break if @block_arg
277
+ end
278
+ end
279
+
280
+ def render_argument
281
+ @splat_arg = @block_arg = false
282
+
283
+ if @rest and @rest.first == :lasgn and
284
+ (@n == nil or @rest.last[:vid] == @n)
285
+ ulterior_comma('*')
286
+ @splat_arg = true
287
+ end
288
+
289
+ if @block and (ba = @block[1]) and
290
+ ba.first == :block_arg and ba.last[:vid] == @n
291
+ ulterior_comma('&')
292
+ @block_arg = true
293
+ end
294
+
295
+ # ERGO Ruby 1.9 changes these rules!!
296
+
297
+ if !@previous_splat or @block_arg
298
+ if @opt and @opt.first == :block and # ERGO why a @block??
299
+ (lasgn = @opt.last.first).first == :lasgn and
300
+ lasgn.last[:vid] == @n
301
+ @previously = true
302
+ possible_comma(@opt.last.first)
303
+ else
304
+ possible_comma(@n)
305
+ @expression << ' = nil' if @previously and !@block_arg and !@splat_arg
306
+ end
307
+
308
+ @previous_splat ||= @splat_arg
309
+ end
310
+ end
311
+ end
312
+
313
+ def _scope(node)
314
+ return ScopeMethod.new._scopic(self, node)
315
+ end
316
+
317
+ def _defs(node)
318
+ return 'def ' + recv_(node, '.') + mid_(node) +
319
+ defn_(node) +
320
+ "end\n"
321
+ end
322
+
323
+ def _rescue(node)
324
+ if node[:else] == false and node[:head] and
325
+ node[:resq] and node[:head].first == :vcall
326
+ return head_(node) + ' rescue ' + resq_(node)
327
+ else
328
+ exp = head_(node) +
329
+ else_(node) +
330
+ "rescue"
331
+ if node[:resq] and node[:resq].first == :resbody
332
+ body = node[:resq].last
333
+ exp << ' ' + args_(body) if body and body[:args]
334
+ end
335
+ return exp + "\n" + resq_(node)
336
+ end
337
+ end
338
+
339
+ def _resbody(node)
340
+ return body_(node)
341
+ # already emitted: head_(node) + ' ' + args_(node)
342
+ end
343
+
344
+ def _yield(node)
345
+ exp = 'yield'
346
+ exp << '(' + head_(node) + ')' if node[:head]
347
+ return exp
348
+ end
349
+
350
+ def _alias(node)
351
+ return "alias #{ lit_(node[:new].last) } #{ lit_(node[:old].last) }"
352
+ end
353
+
354
+ def _valias(node)
355
+ return "alias #{ node[:new] } #{ node[:old] }"
356
+ end
357
+
358
+ ########################################################
359
+ #### control flow
360
+
361
+ def _if(node)
362
+ expression = '( if ' + eval_parenz{cond_(node)} + ' then '
363
+ expression << eval_parenz{body_(node)} if node[:body]
364
+ expression << ' else ' +
365
+ eval_parenz{else_(node)} if node[:else]
366
+ expression << ' end )'
367
+ return expression
368
+ end
369
+
370
+ def _while(node, concept = 'while')
371
+ return '( ' + concept + ' ' + cond_(node) +
372
+ head_body(node) +
373
+ "\nend )"
374
+ end
375
+
376
+ def _for(node)
377
+ return '( for ' + var_(node) + ' in ' + iter_(node) + "\n" +
378
+ body_(node) + "\n" +
379
+ 'end )'
380
+ end
381
+
382
+ def _args(node); return ''; end # _call and _fcall insert the real args
383
+ def _until(node); _while(node, 'until'); end
384
+ def _break(node); 'break'; end
385
+ def _next(node); 'next' ; end
386
+ def _case(node); '( case ' + head_body(node) + "\nend )"; end
387
+ def _when(node); 'when ' + head_body(node) + "\n" + next_(node); end
388
+ def _retry(node); 'retry'; end
389
+ def _redo(node); 'redo'; end
390
+ def head_body(node); head_(node) + "\n" + body_(node); end
391
+
392
+ def _return(node)
393
+ exp = 'return'
394
+ return exp unless stts = node[:stts]
395
+ exp << ' '
396
+
397
+ if stts.first == :array
398
+ exp << '[' + stts_(node) + ']'
399
+ elsif stts.first == :svalue
400
+ exp << stts_(node)
401
+ else
402
+ exp << eval_parenz{stts_(node)}
403
+ end
404
+
405
+ return exp
406
+ end
407
+
408
+ def _postexe(node)
409
+ raise '_postexe called with unexpected arguments' unless node == {} or node.keys == [:node]
410
+ return 'END'
411
+ end
412
+
413
+ # :argscat => [:body, :head],
414
+
415
+ ########################################################
416
+ #### assignments
417
+
418
+ def _dasgn_curr(node)
419
+ expression = vid_(node)
420
+ return expression unless value = node[:value]
421
+ expression << ' = '
422
+ we_b_array = value.first == :array
423
+ expression << nest_if(we_b_array, '[', ']'){ value_(node) }
424
+ return expression
425
+ end
426
+
427
+ def _cdecl(node)
428
+ return _send(node[ node[:vid] == 0 ? :else : :vid ]) + ' = ' + value_(node)
429
+ end
430
+
431
+ def _dasgn(node); _dasgn_curr(node); end
432
+ def _iasgn(node); _dasgn_curr(node); end
433
+ def _gasgn(node); _dasgn_curr(node); end
434
+ def _lasgn(node); _dasgn_curr(node); end
435
+ def _cvasgn(node); _dasgn_curr(node); end
436
+
437
+ def _op_asgn2(node)
438
+ expression = ''
439
+
440
+ if node[:recv].first == :self
441
+ expression << 'self'
442
+ else
443
+ expression << recv_(node)
444
+ end
445
+
446
+ expression << '.'
447
+ expression << vid_(node[:next].last) + ' ||= ' + value_(node)
448
+ return expression
449
+ end
450
+
451
+ ########################################################
452
+ #### operators
453
+
454
+ def _and(node, und = 'and')
455
+ return eval_intermediate( '( ' +
456
+ eval_parenz{ first_(node)} + ' ' + und + ' ' +
457
+ eval_parenz{second_(node)} + ' )' )
458
+ end
459
+
460
+ def _back_ref(node); '$' + node[:nth].chr; end
461
+ def _colon2(node); head_(node, '::') + mid_(node); end
462
+ def _colon3(node); '::' + mid_(node); end
463
+ def _cvar(node); _lvar(node); end
464
+ def _cvdecl(node); vid_(node) + ' = ' + value_(node); end
465
+ def _defined(node); 'defined? ' + head_(node); end
466
+ def _dot2(node); '( ' + beg_(node) + ' .. ' + end_(node) + ' )'; end
467
+ def _dot3(node); '( ' + beg_(node) + ' ... ' + end_(node) + ' )'; end
468
+ def _dregx(node); _dstr(node, '/'); end
469
+ def _dregx_once(node); _dstr(node, '/'); end
470
+ def _dsym(node); ':' + _lit(node[:lit]) + ' ' + rest_(node); end
471
+ def _dvar(node); eval_intermediate(vid_(node)); end
472
+ def _dxstr(node); _dstr(node, '`'); end
473
+ def eval_parenz; eval_intermediate('( ' + yield + ' )'); end
474
+ def _evstr(node); body_(node); end
475
+ def _false(nada); 'false'; end
476
+ def _gvar(node); vid_(node); end
477
+ def _ivar(node); _dvar(node); end
478
+ def _lit(node); node[:lit].inspect; end
479
+ def _lvar(node); eval_intermediate(vid_(node)); end
480
+ def _match(node); node[:lit].inspect; end
481
+ def neg_one(node); node == -1 ? '' : _send(node); end
482
+ def _nil(nada); 'nil' ; end
483
+ def _not(node); '(not(' + body_(node) + '))'; end
484
+ def _nth_ref(node); "$#{ node[:nth] }"; end # ERGO eval it?
485
+ def _op_asgn_and(node); _op_asgn_or(node, ' &&= '); end
486
+ def _or(node); _and(node, 'or'); end
487
+ def _str(node); _lit(node); end
488
+ def _svalue(node); head_(node); end
489
+ def _to_ary(node); head_(node); end
490
+ def _true(nada); 'true' ; end
491
+ def _undef(node); 'undef ' + mid_(node); end
492
+ def _vcall(node); mid_(node); end
493
+ def we_b(node); node.first.first; end
494
+ def _xstr(node); '`' + scrape_literal(node) + '`'; end
495
+ def _zarray(node); return '[]'; end
496
+
497
+ def _flip2(node) # ERGO what the heck is this??
498
+ p node
499
+ p node.keys
500
+ return ''
501
+ end
502
+
503
+ def _masgn(node)
504
+
505
+ #{:value=>
506
+ # [:splat,
507
+ # {:head=>
508
+ # [:fcall,
509
+ # {:mid=>:calc_stack,
510
+ # :args=>
511
+ # [:array,
512
+ # [[:vcall, {:mid=>:insn}],
513
+ # [:vcall, {:mid=>:from}],
514
+ # [:vcall, {:mid=>:after}],
515
+ # [:vcall, {:mid=>:opops}]]]}]}],
516
+ # :args=>false,
517
+ # :head=>
518
+ # [:array,
519
+ # [[:dasgn_curr, {:value=>false, :vid=>:name}],
520
+ # [:dasgn_curr, {:value=>false, :vid=>:pops}],
521
+ # [:dasgn_curr, {:value=>false, :vid=>:rets}],
522
+ # [:dasgn_curr, {:value=>false, :vid=>:pushs1}],
523
+ # [:dasgn_curr, {:value=>false, :vid=>:pushs2}]]]}
524
+
525
+ value, head, args = node.values_at(:value, :head, :args)
526
+
527
+ if value
528
+ return '( ' + head_(node) + ' = *' + head_(value.last) + ' )' if value.first == :splat
529
+
530
+ if head and args
531
+ exp = head_(node)
532
+ return exp + ', * = ' + value_(node) if args == -1
533
+ return exp + ', *' + args_(node) + ' = ' + value_(node)
534
+ end
535
+
536
+ return '( ' + head_(node) + ' = ' + value_(node) + ' )' if args == false
537
+ end
538
+
539
+ if value == false and head == false and args
540
+ return '*' + neg_one(args)
541
+ end
542
+
543
+ if head.kind_of?(Array) and head.first == :array
544
+ return head.last.map{|n|
545
+ nest_if(n.first == :masgn, '(', ')'){ _send(n) }
546
+ }.join(', ')
547
+ end
548
+
549
+ if head == false and args and value
550
+ return '*' + args_(node) + ' = ' + value_(node)
551
+ end
552
+
553
+ return head_(node)
554
+ end
555
+
556
+ def _splat(node)
557
+ if (head = node[:head]) and
558
+ ((we_b_array = head.first == :array) or head.first == :lvar)
559
+ return '*' + nest_if(we_b_array, '[', ']'){ head_(node) }
560
+ end
561
+
562
+ return '*' + head_(node)
563
+ end # ERGO raise if any other key!
564
+
565
+ def _const(node)
566
+ expression = vid_(node)
567
+ q = eval(expression, @block.binding)
568
+ eval_intermediate(expression) unless q.kind_of?(Module)
569
+ return expression
570
+ rescue # ERGO will someone need to see whatever this was?
571
+ return expression
572
+ end
573
+
574
+ def scrape_literal(node, regex = false)
575
+ lit = node[:lit].inspect.gsub(/^"/, '').gsub(/"$/, '')
576
+ lit.gsub!('\\\\', '\\') if regex
577
+ return lit
578
+ end
579
+
580
+ def _dstr(node, delim = '"')
581
+ regex = delim == '/'
582
+ expression = delim + scrape_literal(node, regex)
583
+
584
+ if node[:next] and node[:next].first == :array
585
+ (node[:next].last || []).each do |n|
586
+ expression << if n.first == :str
587
+ scrape_literal(n.last, regex)
588
+ else
589
+ '#{ ' + _send(n) + ' }'
590
+ end
591
+ end
592
+ end
593
+
594
+ return eval_intermediate(expression + delim)
595
+ end
596
+
597
+ def _retry(node)
598
+ raise '_retry called with unexpected arguments' unless node == {} or node.keys == [:node]
599
+ return 'retry'
600
+ end
601
+
602
+ def recv_zero_self(node, plus = '')
603
+ recv = node[:recv]
604
+ return 'self' + plus if recv == 0
605
+ return recv_(node, plus)
606
+ end
607
+
608
+ def _attrasgn(node)
609
+ recv, args = node.values_at(:recv, :args)
610
+
611
+ if args
612
+ if args.first == :array
613
+ if node[:mid].class == Symbol
614
+ if node[:mid] == :'[]='
615
+ return recv_zero_self(node) + '[' +
616
+ _send(args.last.first) + '] = ' +
617
+ _send(args.last.last)
618
+ end
619
+ return recv_zero_self(node, '.') + mid_(node) + '(' + _send(args.last.last) + ')'
620
+ end
621
+ end
622
+
623
+ return recv_zero_self(node) +
624
+ '[' + head_(args.last) + '] = ' +
625
+ body_(args.last)
626
+ end
627
+
628
+ return recv_zero_self(node, '.') + node[:mid].to_s.gsub(/=$/, '')
629
+ end
630
+
631
+ def _op_asgn_or(node, op = ' ||= ')
632
+ # CONSIDER what be :aid?
633
+ #{:value=>[:lasgn, {:value=>[:str, {:lit=>"vm_opts.h"}], :cnt=>2, :vid=>:file}],
634
+ # :aid=>0,
635
+ # :head=>[:lvar, {:cnt=>2, :vid=>:file}]}
636
+ return head_(node) + op + value_(node[:value].last)
637
+ end
638
+
639
+ def fcall_args(node = nil, methodic = false)
640
+ expression = ''
641
+ return expression unless node
642
+ expression << ' ' unless methodic
643
+ return expression + nest_if(methodic, '(', ')'){ _send(node) }
644
+ end
645
+
646
+ def _fcall(node)
647
+ exp = mid_(node) + fcall_args(node[:args], true)
648
+ eval_intermediate(exp) unless %w(lambda proc).include?(exp)
649
+ return exp
650
+ end
651
+
652
+ def _block_pass(node)
653
+ fcall = node[:iter].last
654
+ return eval_intermediate(recv_(fcall, '.') +
655
+ mid_(fcall) + '(' + args_(fcall, ', ') +
656
+ '&' + body_(node) + ')' )
657
+ end
658
+
659
+ def _iter(node)
660
+ var = node[:var]
661
+
662
+ return eval_intermediate(
663
+ iter_(node) +
664
+ '{' +
665
+ nest_if(var != false, '|', '|'){ var_(node) unless var == 0 } +
666
+ nest_if(node[:body] , ' ', ' '){ body_(node) } +
667
+ '}')
668
+ end
669
+
670
+ def _array(node)
671
+ nest_if we_b(node) == :array, '[', ']' do
672
+ node.map{ |z|
673
+ exp = _send(z)
674
+ exp << ', ' unless z.object_id == node.last.object_id
675
+ exp
676
+ }.join
677
+ end
678
+ end
679
+
680
+ def _hash(node)
681
+ return '{}' unless node[:head] and (array = node[:head].last)
682
+ expression = '{ '
683
+
684
+ array.in_groups_of 2 do |key, value|
685
+ expression << _send(key) + ' => ' + _send(value)
686
+ expression << ', ' if value != array.last
687
+ end
688
+
689
+ return expression + ' }'
690
+ end
691
+
692
+ def _match2(node)
693
+ # ERGO should this work like match3?
694
+ return recv_(node) + ' =~ ' + value_(node)
695
+ end
696
+
697
+ def we_b_op(node)
698
+ return node[:mid] && node[:mid].to_s !~ /^[a-z]/i
699
+ end
700
+
701
+ def _match3(node)
702
+ # ERGO do :lit and :value exclude each other?
703
+ return recv_(node) + ' =~ ' + _send(node[:lit] || node[:value])
704
+ end
705
+
706
+ def _block_arg(node) # is this ever called?
707
+ return '' # note that _scope should not take care of this
708
+ end
709
+
710
+ class CallMethod
711
+ def bracket_args
712
+ return false unless @mid == '[]'
713
+ @expression << '[' + @ref.args_(@node) + ']'
714
+ return true
715
+ end
716
+
717
+ def insert_method_call
718
+ @expression << '.'
719
+ @expression << @mid
720
+ @expression << '(' + @ref.args_(@node) + ')'
721
+ @ref.eval_intermediate(@expression) if @methodic
722
+ end
723
+
724
+ def operator_and_arguments
725
+ @mid = @ref.mid_(@node)
726
+
727
+ unless bracket_args
728
+ @methodic = @mid =~ /[a-z]/i
729
+
730
+ if @methodic
731
+ insert_method_call
732
+ else
733
+ @expression << ' '
734
+ @expression << @mid
735
+ @expression << ' '
736
+ nest_args
737
+ end
738
+ end
739
+ end
740
+
741
+ def nest_args
742
+ return unless @args = @node[:args]
743
+
744
+ nest_me = @args.first == :array &&
745
+ @args.last.length == 1 &&
746
+ (call = @args.last.first).first == :call &&
747
+ @ref.we_b_op(call.last)
748
+
749
+ exp = @ref.nest_if(nest_me, '( ', ' )'){ @ref.args_(@node) }
750
+ @ref.eval_intermediate(exp) if nest_me
751
+ @expression << exp
752
+ end
753
+
754
+ def caller(ref, node)
755
+ @ref, @node = ref, node
756
+ @expression = ''
757
+ @recv = @node[:recv]
758
+
759
+ if @recv.first == :block_pass
760
+ @expression << @ref.recv_(@node)
761
+ operator_and_arguments
762
+ else
763
+ nest_me = @recv.first == :call && @ref.we_b_op(@recv.last)
764
+
765
+ exp = if @recv.first == :array
766
+ @ref.nest_if(true, '[ ', ' ]'){ @ref.recv_(node) }
767
+ else
768
+ exp2 = @ref.nest_if(nest_me, '( ', ' )'){ @ref.recv_(node) }
769
+ @ref.eval_intermediate(exp2) if nest_me
770
+ exp2
771
+ end
772
+
773
+ @expression << exp
774
+ operator_and_arguments
775
+ end
776
+ return @expression
777
+ end
778
+ end
779
+
780
+ def _call(node); CallMethod.new.caller(self, node); end
781
+
782
+ def nest_if(condition, before, after, &block)
783
+ exp = ''
784
+ exp << before if condition
785
+ exp << (block.call || '')
786
+ exp << after if condition
787
+ return exp
788
+ end
789
+
790
+ def _op_asgn1(node) # ERGO just look up the list of these?
791
+ return '' unless args = node[:args]
792
+ exp = recv_(node)
793
+
794
+ if [:'-', :'+', :'*', :'**', :'/', :^, :|, :&,
795
+ :'<<', :'>>',
796
+ ].include?(node[:mid]) and
797
+ node[:recv] and args.first == :argscat
798
+
799
+ return exp +
800
+ "[#{ body_(args.last) }] #{ node[:mid] }= " + head_(args.last)
801
+ end
802
+
803
+ raise "unexpected mid value #{ node[:mid].inspect } in opcode for X= " unless node[:mid] == 0
804
+
805
+ if args.first == :argscat and args.last[:body]
806
+ exp << '[' + body_(args.last) + ']'
807
+ exp << ' ||= ' + head_(args.last)
808
+ else
809
+ raise "unexpected arguments in opcode for ||= "
810
+ end
811
+
812
+ return exp
813
+ end
814
+
815
+ def _argscat(node)
816
+ return head_(node) + ', *' +
817
+ nest_if(node[:body].first == :array, '[', ']'){ body_(node) }
818
+ end
819
819
 
820
820
  def diagnose(diagnostic, result, called, options, block, additional_diagnostics)
821
821
  @__additional_diagnostics = additional_diagnostics
@@ -843,18 +843,18 @@ p node
843
843
  def arrow_result(result) #:nodoc:
844
844
  return "\t--> #{ result.inspect }"
845
845
  end
846
-
847
- end
848
-
849
- end; end; end
850
-
851
- unless [].respond_to? :in_groups_of
852
- class Array
853
- def in_groups_of(number, fill_with = nil, &block)
854
- require 'enumerator'
855
- collection = dup
856
- collection << fill_with until collection.size.modulo(number).zero?
857
- collection.each_slice(number, &block)
858
- end
859
- end
860
- end
846
+
847
+ end
848
+
849
+ end; end; end
850
+
851
+ unless [].respond_to? :in_groups_of
852
+ class Array
853
+ def in_groups_of(number, fill_with = nil, &block)
854
+ require 'enumerator'
855
+ collection = dup
856
+ collection << fill_with until collection.size.modulo(number).zero?
857
+ collection.each_slice(number, &block)
858
+ end
859
+ end
860
+ end