assert2 0.3.4 → 0.3.6

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/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