assert2 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. data/lib/assert2.rb +124 -0
  2. data/lib/ruby_reflector.rb +825 -0
  3. metadata +62 -0
data/lib/assert2.rb ADDED
@@ -0,0 +1,124 @@
1
+ require File.dirname(__FILE__) + '/ruby_reflector'
2
+
3
+
4
+ module Assert_2_0
5
+ include RubyNodeReflector
6
+ include Coulor #:nodoc:
7
+
8
+ # This assertion calls a block, and faults if this returns
9
+ # +false+ or +nil+. The fault diagnostic will reflect the
10
+ # intermediate value of every variable and expression in the
11
+ # block.
12
+ #
13
+ # The first argument can be a diagnostic string:
14
+ #
15
+ # assert("foo failed"){ foo() }
16
+ #
17
+ # The fault diagnostic will print that line.
18
+ #
19
+ # The next time you think to write any of these assertions...
20
+ #
21
+ # - +assert+
22
+ # - +assert_equal+
23
+ # - +assert_instance_of+
24
+ # - +assert_kind_of+
25
+ # - +assert_operator+
26
+ # - +assert_match+
27
+ # - +assert_not_nil+
28
+ #
29
+ # use <code>assert{ 2.0 }</code> instead.
30
+ #
31
+ # If no block is provided, the assertion calls +assert_classic+,
32
+ # which simulates RubyUnit's standard <code>assert()</code>.
33
+ #
34
+ # Note: This only works for Ruby 1.8.6 so far...
35
+ #
36
+ def assert(*args, &block)
37
+ if block
38
+ assert_ args.first, '', &block
39
+ else
40
+ assert_classic *args
41
+ end
42
+ end
43
+
44
+ # The new <code>assert()</code> calls this to interpret
45
+ # blocks of assertive statements.
46
+ #
47
+ def assert_(diagnostic = nil, twizzler = '_', &block)
48
+ # puts reflect(&block) # activate this line and test to see all your successes!
49
+ result = block.call
50
+ return if result
51
+ _flunk_2_0("assert#{ twizzler }{ ", diagnostic, block, result)
52
+ end
53
+
54
+ # This assertion replaces:
55
+ #
56
+ # - +assert_nil+
57
+ # - +assert_no_match+
58
+ # - +assert_not_equal+
59
+ #
60
+ # It faults, and prints its block's contents and values,
61
+ # if its block returns non-+false+ and non-+nil+.
62
+ #
63
+ def deny(diagnostic = nil, &block)
64
+ # "None shall pass!" --the Black Knight
65
+ # puts reflect(&block) # activate this line and test to see all your denials!
66
+ result = block.call
67
+ return unless result
68
+ _flunk_2_0('deny{ ', diagnostic, block, result)
69
+ end # "You're a looney!" -- King Arthur
70
+
71
+ # Assert that a block raises a given Exception type matching
72
+ # a given message
73
+ #
74
+ # * +types+ - single exception class or array of classes
75
+ # * +matcher+ - Regular Expression to match the inner_text of XML nodes
76
+ # * +diagnostic+ - optional string to add to failure message
77
+ # * +block+ - Ruby statements that should raise an exception
78
+ #
79
+ # Examples:
80
+ # %transclude AssertXPathSuite#test_assert_raise_message_detects_assertion_failure
81
+ #
82
+ # %transclude AssertXPathSuite#test_assert_raise_message_raises_message
83
+ #
84
+ # See: {assert_raise - Don't Just Say "No"}[http://www.oreillynet.com/onlamp/blog/2007/07/assert_raise_on_ruby_dont_just.html]
85
+ #
86
+ def assert_raise_message(types, matcher, diagnostic = nil, &block)
87
+ args = [types].flatten + [diagnostic]
88
+ exception = assert_raise(*args, &block)
89
+ assert(diagnostic){ matcher =~ exception.message }
90
+ return exception.message
91
+ end # CONSIDER: deny_raise_message?
92
+
93
+ # This is a copy of the classic assert, so your pre�xisting
94
+ # +assert+ calls will not change their behavior
95
+ #
96
+ def assert_classic(boolean, message=nil)
97
+ _wrap_assertion do
98
+ assert_block("assert<classic> should not be called with a block.") { !block_given? }
99
+ assert_block(build_message(message, "<?> is not true.", boolean)) { boolean }
100
+ end
101
+ end
102
+
103
+ private
104
+ def build_message_(diagnostic, reflection)
105
+ diagnostic = nil if diagnostic == ''
106
+ return [diagnostic, reflection].compact.join("\n")
107
+ end
108
+
109
+ def _flunk_2_0(polarity, diagnostic, block, result)
110
+ rf = RubyReflector.new(block)
111
+ effect = " - should #{ 'not ' if polarity =~ /deny/ }pass\n"
112
+
113
+ report = magenta(polarity) + bold(rf.result) + magenta(" }") +
114
+ red(arrow_result(result) + effect) +
115
+ rf.format_evaluations
116
+
117
+ flunk build_message_(diagnostic, report)
118
+ end
119
+
120
+ end # "Eagle-eyes it!"
121
+
122
+ class Test::Unit::TestCase #:nodoc:
123
+ include Assert_2_0
124
+ end
@@ -0,0 +1,825 @@
1
+ require 'rubygems'
2
+ require 'rubynode'
3
+ require 'pp'
4
+
5
+
6
+ module RubyNodeReflector
7
+
8
+ # ERGO
9
+ # :bmethod => [:cval],
10
+ # :cfunc => [:argc, :cfnc],
11
+ # :cref => [:next, :clss],
12
+ # :defs => [:mid, :defn, :recv],
13
+ # :dmethod => [:cval],
14
+ # :dot2 => [:beg, :end],
15
+ # :dot3 => [:beg, :end],
16
+ # :dregx_once => [:next, :lit, :cflag],
17
+ # :fbody => [:orig, :mid, :head],
18
+ # :flip2 => [:cnt, :beg, :end],
19
+ # :flip3 => [:cnt, :beg, :end],
20
+ # :gasgn => [:vid, :value], # entry not supported
21
+ # :ifunc => [:tval, :state, :cfnc],
22
+ # :lasgn => [:vid, :cnt, :value],
23
+ # :last => [],
24
+ # :match => [:lit],
25
+ # :memo => {:u1_value=>:u1_value}, # different uses in enum.c, variabe.c and eval.c ...
26
+ # :method => [:body, :noex, :cnt], # cnt seems to be always 0 in 1.8.4
27
+ # :module => [:cpath, :body],
28
+ # :next => [:stts],
29
+ # :opt_n => [:body],
30
+ # :to_ary => [:head],
31
+
32
+ # This +reflect+s a block of code, by evaluating it, reflecting its
33
+ # source, and reflecting all its intermediate values
34
+ #
35
+ def reflect(&block)
36
+ result = block.call
37
+ rf = RubyReflector.new(block)
38
+
39
+ begin
40
+ waz = rf.colorize?
41
+ rf.colorize(false)
42
+ return rf.result + arrow_result(result) + "\n" + rf.format_evaluations
43
+ ensure
44
+ rf.colorize(waz)
45
+ end
46
+ end
47
+
48
+ def arrow_result(result) #:nodoc:
49
+ return "\t--> #{ result.inspect }"
50
+ end
51
+ private :arrow_result
52
+
53
+ # This +reflect+s a block of code, /without/ evaluating it.
54
+ # The function only compiles the source and reflects it as
55
+ # a string of disassembled Ruby
56
+ #
57
+ def reflect_source(&block)
58
+ return RubyReflector.new(block, false).result
59
+ end
60
+
61
+ # This compiles a string and +reflect+s its source...
62
+ # as another string.
63
+ #
64
+ def reflect_string(string)
65
+ rf = RubyReflector.new(proc{})
66
+ rf.reflect_values = false
67
+ # pp string.parse_to_nodes.transform
68
+ got = rf.reflect_nodes(string.parse_to_nodes)
69
+ return got
70
+ end
71
+
72
+ module Coulor #:nodoc:
73
+ def colorize(we_color)
74
+ @@we_color = we_color
75
+ end
76
+ unless defined? BOLD
77
+ BOLD = "\e[1m"
78
+ CLEAR = "\e[0m"
79
+ end # ERGO modularize these; anneal with Win32
80
+ def colour(text, colour_code)
81
+ return colour_code + text + CLEAR if colorize?
82
+ return text
83
+ end
84
+ def colorize? # ERGO how other libraries set these options transparent??
85
+ we_color = (@@we_color rescue true ) # ERGO parens needed?
86
+ return (we_color == :always or we_color && $stdout.tty?)
87
+ end
88
+ def bold(text)
89
+ return BOLD + text + CLEAR if colorize?
90
+ return text
91
+ end
92
+ def green(text); colour(text, "\e[32m"); end
93
+ def red(text); colour(text, "\e[31m"); end
94
+ def magenta(text); colour(text, "\e[35m"); end
95
+ def blue(text); colour(text, "\e[34m"); end
96
+ def orange(text); colour(text, "\e[3Bm"); end
97
+ end
98
+
99
+ class RubyReflector # turn hamburger back into live cattle
100
+ include Coulor
101
+ attr_reader :evaluations,
102
+ :result
103
+ attr_writer :reflect_values
104
+
105
+ def initialize(block, reflect_values = true) # note that a block, from your context, is not optional
106
+ @reflect_values = reflect_values
107
+ @evaluations = []
108
+ @result = ''
109
+ @block = block
110
+ @line = 0
111
+ reflect_nodes(@block.body_node)
112
+ end
113
+
114
+ def reflect_nodes(body_node)
115
+ return unless body_node
116
+ @transformation = body_node.transform(:include_node => true)
117
+ return @result = _send(@transformation)
118
+ rescue
119
+ puts "\nOffending line: #{ @line }"
120
+ # pp @transformation
121
+ # puts @result
122
+ raise
123
+ end
124
+
125
+ def eval_intermediate_guarded(expression)
126
+ begin
127
+ intermediate = eval(expression, @block.binding)
128
+ @evaluations << [expression, intermediate, nil]
129
+ rescue SyntaxError => e
130
+ if e.message.index('syntax error, unexpected \',\'') and expression !~ /\[ /
131
+ return eval_intermediate_guarded('[ ' + 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
+ eval_intermediate_guarded(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 _class(node); _module(node, 'class'); end
212
+ def _self(node); 'self'; end
213
+ def _defn(node); _defs(node); end
214
+ def _super(node); 'super(' + args_(node) + ')'; end
215
+ def _zsuper(node); 'super'; end
216
+ def _begin(node); "begin\n" + body_(node) + "\nend\n"; end
217
+ def _ensure(node); head_(node) + "\nensure\n" + ensr_(node); end
218
+
219
+ def _sclass(node)
220
+ return 'class << ' + recv_(node) +
221
+ head_body(node) +
222
+ "end\n"
223
+ end
224
+
225
+ class ScopeMethod #:nodoc:
226
+ # this is complex because Ruby gloms several different
227
+ # kinds of variables into one "scope" token, and they
228
+ # don't directly match their layout in the source code
229
+
230
+ def _scopic(ref, node)
231
+ @ref = ref
232
+ @node = node
233
+ @expression = ''
234
+ @previously = false
235
+ @block_arg = false
236
+ @splat_arg = false
237
+ @previous_splat = false
238
+
239
+ if @node[:tbl]
240
+ @expression << '('
241
+ render_argument_list
242
+ @expression << ')'
243
+ end
244
+
245
+ @expression << "\n"
246
+ @expression << @ref.next_(@node)
247
+ return @expression
248
+ end
249
+
250
+ def ulterior_comma(token)
251
+ @expression << ', ' if @index > 0
252
+ @expression << token
253
+ @index = 0
254
+ end
255
+
256
+ def possible_comma(token)
257
+ @expression << ', ' if @index > 0
258
+ @expression << @ref._send(token)
259
+ end
260
+
261
+ def render_argument_list
262
+ @nekst = @node[:next]
263
+ @block = @nekst.last if @nekst && @nekst.first == :block
264
+ @args = @block.first.last if @block && @block.first.first == :args
265
+ @rest = @args[:rest] if @args
266
+ @opt = @args[:opt] if @args
267
+
268
+ @node[:tbl].each_with_index do |@n, @index|
269
+ render_argument
270
+ break if @block_arg
271
+ end
272
+ end
273
+
274
+ def render_argument
275
+ @splat_arg = @block_arg = false
276
+
277
+ if @rest and @rest.first == :lasgn and
278
+ (@n == nil or @rest.last[:vid] == @n)
279
+ ulterior_comma('*')
280
+ @splat_arg = true
281
+ end
282
+
283
+ if @block and (ba = @block[1]) and
284
+ ba.first == :block_arg and ba.last[:vid] == @n
285
+ ulterior_comma('&')
286
+ @block_arg = true
287
+ end
288
+
289
+ # ERGO Ruby 1.9 changes these rules!!
290
+
291
+ if !@previous_splat or @block_arg
292
+ if @opt and @opt.first == :block and # ERGO why a @block??
293
+ (lasgn = @opt.last.first).first == :lasgn and
294
+ lasgn.last[:vid] == @n
295
+ @previously = true
296
+ possible_comma(@opt.last.first)
297
+ else
298
+ possible_comma(@n)
299
+ @expression << ' = nil' if @previously and !@block_arg and !@splat_arg
300
+ end
301
+
302
+ @previous_splat ||= @splat_arg
303
+ end
304
+ end
305
+ end
306
+
307
+ def _scope(node)
308
+ return ScopeMethod.new._scopic(self, node)
309
+ end
310
+
311
+ def _defs(node)
312
+ return 'def ' + recv_(node, '.') + mid_(node) +
313
+ defn_(node) +
314
+ "end\n"
315
+ end
316
+
317
+ def _rescue(node)
318
+ if node[:else] == false and node[:head] and
319
+ node[:resq] and node[:head].first == :vcall
320
+ return head_(node) + ' rescue ' + resq_(node)
321
+ else
322
+ exp = head_(node) +
323
+ else_(node) +
324
+ "rescue"
325
+ if node[:resq] and node[:resq].first == :resbody
326
+ body = node[:resq].last
327
+ exp << ' ' + args_(body) if body and body[:args]
328
+ end
329
+ return exp + "\n" + resq_(node)
330
+ end
331
+ end
332
+
333
+ def _resbody(node)
334
+ return body_(node)
335
+ # already emitted: head_(node) + ' ' + args_(node)
336
+ end
337
+
338
+ def _yield(node)
339
+ exp = 'yield'
340
+ exp << '(' + head_(node) + ')' if node[:head]
341
+ return exp
342
+ end
343
+
344
+ def _alias(node)
345
+ return "alias #{ lit_(node[:new].last) } #{ lit_(node[:old].last) }"
346
+ end
347
+
348
+ def _valias(node)
349
+ return "alias #{ node[:new] } #{ node[:old] }"
350
+ end
351
+
352
+ ########################################################
353
+ #### control flow
354
+
355
+ def _if(node)
356
+ expression = '( if ' + eval_parenz{cond_(node)} + ' then '
357
+ expression << eval_parenz{body_(node)} if node[:body]
358
+ expression << ' else ' +
359
+ eval_parenz{else_(node)} if node[:else]
360
+ expression << ' end )'
361
+ return expression
362
+ end
363
+
364
+ def _while(node, concept = 'while')
365
+ return '( ' + concept + ' ' + cond_(node) +
366
+ head_body(node) +
367
+ "\nend )"
368
+ end
369
+
370
+ def _for(node)
371
+ return '( for ' + var_(node) + ' in ' + iter_(node) + "\n" +
372
+ body_(node) + "\n" +
373
+ 'end )'
374
+ end
375
+
376
+ def _args(node); return ''; end # _call and _fcall insert the real args
377
+ def _until(node); _while(node, 'until'); end
378
+ def _break(node); 'break'; end
379
+ def _next(node); 'next' ; end
380
+ def _case(node); '( case ' + head_body(node) + "\nend )"; end
381
+ def _when(node); 'when ' + head_body(node) + "\n" + next_(node); end
382
+ def _retry(node); 'retry'; end
383
+ def _redo(node); 'redo'; end
384
+ def head_body(node); head_(node) + "\n" + body_(node); end
385
+
386
+ def _return(node)
387
+ exp = 'return'
388
+ return exp unless stts = node[:stts]
389
+ exp << ' '
390
+
391
+ if stts.first == :array
392
+ exp << '[' + stts_(node) + ']'
393
+ elsif stts.first == :svalue
394
+ exp << stts_(node)
395
+ else
396
+ exp << eval_parenz{stts_(node)}
397
+ end
398
+
399
+ return exp
400
+ end
401
+
402
+ def _postexe(node)
403
+ raise '_postexe called with unexpected arguments' unless node == {} or node.keys == [:node]
404
+ return 'END'
405
+ end
406
+
407
+ # :argscat => [:body, :head],
408
+
409
+ ########################################################
410
+ #### assignments
411
+
412
+ def _dasgn_curr(node)
413
+ expression = vid_(node)
414
+ return expression unless value = node[:value]
415
+ expression << ' = '
416
+ we_b_array = value.first == :array
417
+ expression << nest_if(we_b_array, '[', ']'){ value_(node) }
418
+ return expression
419
+ end
420
+
421
+ def _cdecl(node)
422
+ return _send(node[ node[:vid] == 0 ? :else : :vid ]) + ' = ' + value_(node)
423
+ end
424
+
425
+ def _dasgn(node); _dasgn_curr(node); end
426
+ def _iasgn(node); _dasgn_curr(node); end
427
+ def _gasgn(node); _dasgn_curr(node); end
428
+ def _lasgn(node); _dasgn_curr(node); end
429
+ def _cvasgn(node); _dasgn_curr(node); end
430
+
431
+ def _op_asgn2(node)
432
+ expression = ''
433
+
434
+ if node[:recv].first == :self
435
+ expression << 'self'
436
+ else
437
+ expression << recv_(node)
438
+ end
439
+
440
+ expression << '.'
441
+ expression << vid_(node[:next].last) + ' ||= ' + value_(node)
442
+ return expression
443
+ end
444
+
445
+ ########################################################
446
+ #### operators
447
+
448
+ def _and(node, und = 'and')
449
+ return eval_intermediate( '( ' +
450
+ eval_parenz{ first_(node)} + ' ' + und + ' ' +
451
+ eval_parenz{second_(node)} + ' )' )
452
+ end
453
+
454
+ def _back_ref(node); '$' + node[:nth].chr; end
455
+ def _colon2(node); head_(node, '::') + mid_(node); end
456
+ def _colon3(node); '::' + mid_(node); end
457
+ def _cvar(node); _lvar(node); end
458
+ def _cvdecl(node); vid_(node) + ' = ' + value_(node); end
459
+ def _defined(node); 'defined? ' + head_(node); end
460
+ def _dot2(node); '( ' + beg_(node) + ' .. ' + end_(node) + ' )'; end
461
+ def _dot3(node); '( ' + beg_(node) + ' ... ' + end_(node) + ' )'; end
462
+ def _dregx(node); _dstr(node, '/'); end
463
+ def _dregx_once(node); _dstr(node, '/'); end
464
+ def _dsym(node); ':' + _lit(node[:lit]) + ' ' + rest_(node); end
465
+ def _dvar(node); eval_intermediate(vid_(node)); end
466
+ def _dxstr(node); _dstr(node, '`'); end
467
+ def eval_parenz; eval_intermediate('( ' + yield + ' )'); end
468
+ def _evstr(node); body_(node); end
469
+ def _false(nada); 'false'; end
470
+ def _gvar(node); vid_(node); end
471
+ def _ivar(node); _dvar(node); end
472
+ def _lit(node); node[:lit].inspect; end
473
+ def _lvar(node); eval_intermediate(vid_(node)); end
474
+ def _match(node); node[:lit].inspect; end
475
+ def neg_one(node); node == -1 ? '' : _send(node); end
476
+ def _nil(nada); 'nil' ; end
477
+ def _not(node); '(not(' + body_(node) + '))'; end
478
+ def _nth_ref(node); "$#{ node[:nth] }"; end # ERGO eval it?
479
+ def _op_asgn_and(node); _op_asgn_or(node, ' &&= '); end
480
+ def _or(node); _and(node, 'or'); end
481
+ def _str(node); _lit(node); end
482
+ def _svalue(node); head_(node); end
483
+ def _to_ary(node); head_(node); end
484
+ def _true(nada); 'true' ; end
485
+ def _undef(node); 'undef ' + mid_(node); end
486
+ def _vcall(node); mid_(node); end
487
+ def we_b(node); node.first.first; end
488
+ def _xstr(node); '`' + scrape_literal(node) + '`'; end
489
+ def _zarray(node); return '[]'; end
490
+
491
+ def _flip2(node) # ERGO what the heck is this??
492
+ p node
493
+ p node.keys
494
+ return ''
495
+ end
496
+
497
+ def _masgn(node)
498
+
499
+ #{:value=>
500
+ # [:splat,
501
+ # {:head=>
502
+ # [:fcall,
503
+ # {:mid=>:calc_stack,
504
+ # :args=>
505
+ # [:array,
506
+ # [[:vcall, {:mid=>:insn}],
507
+ # [:vcall, {:mid=>:from}],
508
+ # [:vcall, {:mid=>:after}],
509
+ # [:vcall, {:mid=>:opops}]]]}]}],
510
+ # :args=>false,
511
+ # :head=>
512
+ # [:array,
513
+ # [[:dasgn_curr, {:value=>false, :vid=>:name}],
514
+ # [:dasgn_curr, {:value=>false, :vid=>:pops}],
515
+ # [:dasgn_curr, {:value=>false, :vid=>:rets}],
516
+ # [:dasgn_curr, {:value=>false, :vid=>:pushs1}],
517
+ # [:dasgn_curr, {:value=>false, :vid=>:pushs2}]]]}
518
+
519
+ value, head, args = node.values_at(:value, :head, :args)
520
+
521
+ if value
522
+ return '( ' + head_(node) + ' = *' + head_(value.last) + ' )' if value.first == :splat
523
+
524
+ if head and args
525
+ exp = head_(node)
526
+ return exp + ', * = ' + value_(node) if args == -1
527
+ return exp + ', *' + args_(node) + ' = ' + value_(node)
528
+ end
529
+
530
+ return '( ' + head_(node) + ' = ' + value_(node) + ' )' if args == false
531
+ end
532
+
533
+ if value == false and head == false and args
534
+ return '*' + neg_one(args)
535
+ end
536
+
537
+ if head.kind_of?(Array) and head.first == :array
538
+ return head.last.map{|n|
539
+ nest_if(n.first == :masgn, '(', ')'){ _send(n) }
540
+ }.join(', ')
541
+ end
542
+
543
+ if head == false and args and value
544
+ return '*' + args_(node) + ' = ' + value_(node)
545
+ end
546
+
547
+ return head_(node)
548
+ end
549
+
550
+ def _splat(node)
551
+ if (head = node[:head]) and
552
+ ((we_b_array = head.first == :array) or head.first == :lvar)
553
+ return '*' + nest_if(we_b_array, '[', ']'){ head_(node) }
554
+ end
555
+
556
+ return '*' + head_(node)
557
+ end # ERGO raise if any other key!
558
+
559
+ def _const(node)
560
+ expression = vid_(node)
561
+ q = eval(expression, @block.binding)
562
+ eval_intermediate(expression) unless q.kind_of?(Module)
563
+ return expression
564
+ rescue # ERGO will someone need to see whatever this was?
565
+ return expression
566
+ end
567
+
568
+ def scrape_literal(node, regex = false)
569
+ lit = node[:lit].inspect.gsub(/^"/, '').gsub(/"$/, '')
570
+ lit.gsub!('\\\\', '\\') if regex
571
+ return lit
572
+ end
573
+
574
+ def _dstr(node, delim = '"')
575
+ regex = delim == '/'
576
+ expression = delim + scrape_literal(node, regex)
577
+
578
+ if node[:next] and node[:next].first == :array
579
+ (node[:next].last || []).each do |n|
580
+ expression << if n.first == :str
581
+ scrape_literal(n.last, regex)
582
+ else
583
+ '#{ ' + _send(n) + ' }'
584
+ end
585
+ end
586
+ end
587
+
588
+ return eval_intermediate(expression + delim)
589
+ end
590
+
591
+ def _retry(node)
592
+ raise '_retry called with unexpected arguments' unless node == {} or node.keys == [:node]
593
+ return 'retry'
594
+ end
595
+
596
+ def recv_zero_self(node, plus = '')
597
+ recv = node[:recv]
598
+ return 'self' + plus if recv == 0
599
+ return recv_(node, plus)
600
+ end
601
+
602
+ def _attrasgn(node)
603
+ recv, args = node.values_at(:recv, :args)
604
+
605
+ if args
606
+ if args.first == :array
607
+ if node[:mid].class == Symbol
608
+ if node[:mid] == :'[]='
609
+ return recv_zero_self(node) + '[' +
610
+ _send(args.last.first) + '] = ' +
611
+ _send(args.last.last)
612
+ end
613
+ return recv_zero_self(node, '.') + mid_(node) + '(' + _send(args.last.last) + ')'
614
+ end
615
+ end
616
+
617
+ return recv_zero_self(node) +
618
+ '[' + head_(args.last) + '] = ' +
619
+ body_(args.last)
620
+ end
621
+
622
+ return recv_zero_self(node, '.') + node[:mid].to_s.gsub(/=$/, '')
623
+ end
624
+
625
+ def _op_asgn_or(node, op = ' ||= ')
626
+ # CONSIDER what be :aid?
627
+ #{:value=>[:lasgn, {:value=>[:str, {:lit=>"vm_opts.h"}], :cnt=>2, :vid=>:file}],
628
+ # :aid=>0,
629
+ # :head=>[:lvar, {:cnt=>2, :vid=>:file}]}
630
+ return head_(node) + op + value_(node[:value].last)
631
+ end
632
+
633
+ def fcall_args(node = nil, methodic = false)
634
+ expression = ''
635
+ return expression unless node
636
+ expression << ' ' unless methodic
637
+ return expression + nest_if(methodic, '(', ')'){ _send(node) }
638
+ end
639
+
640
+ def _fcall(node)
641
+ exp = mid_(node) + fcall_args(node[:args], true)
642
+ eval_intermediate(exp) unless %w(lambda proc).include?(exp)
643
+ return exp
644
+ end
645
+
646
+ def _block_pass(node)
647
+ fcall = node[:iter].last
648
+ return eval_intermediate(recv_(fcall, '.') +
649
+ mid_(fcall) + '(' + args_(fcall, ', ') +
650
+ '&' + body_(node) + ')' )
651
+ end
652
+
653
+ def _iter(node)
654
+ var = node[:var]
655
+
656
+ return eval_intermediate(
657
+ iter_(node) +
658
+ '{' +
659
+ nest_if(var != false, '|', '|'){ var_(node) unless var == 0 } +
660
+ nest_if(node[:body] , ' ', ' '){ body_(node) } +
661
+ '}')
662
+ end
663
+
664
+ def _array(node)
665
+ nest_if we_b(node) == :array, '[', ']' do
666
+ node.map{ |z|
667
+ exp = _send(z)
668
+ exp << ', ' unless z.object_id == node.last.object_id
669
+ exp
670
+ }.join
671
+ end
672
+ end
673
+
674
+ def _hash(node)
675
+ return '{}' unless node[:head] and (array = node[:head].last)
676
+ expression = '{ '
677
+
678
+ array.in_groups_of 2 do |key, value|
679
+ expression << _send(key) + ' => ' + _send(value)
680
+ expression << ', ' if value != array.last
681
+ end
682
+
683
+ return expression + ' }'
684
+ end
685
+
686
+ def _match2(node)
687
+ # ERGO should this work like match3?
688
+ return recv_(node) + ' =~ ' + value_(node)
689
+ end
690
+
691
+ def we_b_op(node)
692
+ return node[:mid] && node[:mid].to_s !~ /^[a-z]/i
693
+ end
694
+
695
+ def _match3(node)
696
+ # ERGO do :lit and :value exclude each other?
697
+ return recv_(node) + ' =~ ' + _send(node[:lit] || node[:value])
698
+ end
699
+
700
+ def _block_arg(node) # is this ever called?
701
+ return '' # note that _scope should not take care of this
702
+ end
703
+
704
+ class CallMethod
705
+ def bracket_args
706
+ return false unless @mid == '[]'
707
+ @expression << '[' + @ref.args_(@node) + ']'
708
+ return true
709
+ end
710
+
711
+ def insert_method_call
712
+ @expression << '.'
713
+ @expression << @mid
714
+ @expression << '(' + @ref.args_(@node) + ')'
715
+ @ref.eval_intermediate(@expression) if @methodic
716
+ end
717
+
718
+ def operator_and_arguments
719
+ @mid = @ref.mid_(@node)
720
+
721
+ unless bracket_args
722
+ @methodic = @mid =~ /[a-z]/i
723
+
724
+ if @methodic
725
+ insert_method_call
726
+ else
727
+ @expression << ' '
728
+ @expression << @mid
729
+ @expression << ' '
730
+ nest_args
731
+ end
732
+ end
733
+ end
734
+
735
+ def nest_args
736
+ return unless @args = @node[:args]
737
+
738
+ nest_me = @args.first == :array &&
739
+ @args.last.length == 1 &&
740
+ (call = @args.last.first).first == :call &&
741
+ @ref.we_b_op(call.last)
742
+
743
+ exp = @ref.nest_if(nest_me, '( ', ' )'){ @ref.args_(@node) }
744
+ @ref.eval_intermediate(exp) if nest_me
745
+ @expression << exp
746
+ end
747
+
748
+ def caller(ref, node)
749
+ @ref, @node = ref, node
750
+ @expression = ''
751
+ @recv = @node[:recv]
752
+
753
+ if @recv.first == :block_pass
754
+ @expression << @ref.recv_(@node)
755
+ operator_and_arguments
756
+ else
757
+ nest_me = @recv.first == :call && @ref.we_b_op(@recv.last)
758
+
759
+ exp = if @recv.first == :array
760
+ @ref.nest_if(true, '[ ', ' ]'){ @ref.recv_(node) }
761
+ else
762
+ exp2 = @ref.nest_if(nest_me, '( ', ' )'){ @ref.recv_(node) }
763
+ @ref.eval_intermediate(exp2) if nest_me
764
+ exp2
765
+ end
766
+
767
+ @expression << exp
768
+ operator_and_arguments
769
+ end
770
+ return @expression
771
+ end
772
+ end
773
+
774
+ def _call(node); CallMethod.new.caller(self, node); end
775
+
776
+ def nest_if(condition, before, after, &block)
777
+ exp = ''
778
+ exp << before if condition
779
+ exp << (block.call || '')
780
+ exp << after if condition
781
+ return exp
782
+ end
783
+
784
+ def _op_asgn1(node) # ERGO just look up the list of these?
785
+ return '' unless args = node[:args]
786
+ exp = recv_(node)
787
+
788
+ if [:'-', :'+', :'*', :'**', :'/', :^, :|, :&,
789
+ :'<<', :'>>',
790
+ ].include?(node[:mid]) and
791
+ node[:recv] and args.first == :argscat
792
+
793
+ return exp +
794
+ "[#{ body_(args.last) }] #{ node[:mid] }= " + head_(args.last)
795
+ end
796
+
797
+ raise "unexpected mid value #{ node[:mid].inspect } in opcode for X= " unless node[:mid] == 0
798
+
799
+ if args.first == :argscat and args.last[:body]
800
+ exp << '[' + body_(args.last) + ']'
801
+ exp << ' ||= ' + head_(args.last)
802
+ else
803
+ raise "unexpected arguments in opcode for ||= "
804
+ end
805
+
806
+ return exp
807
+ end
808
+
809
+ def _argscat(node)
810
+ return head_(node) + ', *' +
811
+ nest_if(node[:body].first == :array, '[', ']'){ body_(node) }
812
+ end
813
+
814
+ end
815
+
816
+ end
817
+
818
+ class Array
819
+ def in_groups_of(number, fill_with = nil, &block)
820
+ require 'enumerator'
821
+ collection = dup
822
+ collection << fill_with until collection.size.modulo(number).zero?
823
+ collection.each_slice(number, &block)
824
+ end
825
+ end
metadata ADDED
@@ -0,0 +1,62 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: assert2
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Phlip
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-02-11 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rubynode
17
+ version_requirement:
18
+ version_requirements: !ruby/object:Gem::Requirement
19
+ requirements:
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: "0"
23
+ version:
24
+ description:
25
+ email: phlip2005@gmail.com
26
+ executables: []
27
+
28
+ extensions: []
29
+
30
+ extra_rdoc_files: []
31
+
32
+ files:
33
+ - lib/ruby_reflector.rb
34
+ - lib/assert2.rb
35
+ has_rdoc: false
36
+ homepage: http://assert2.rubyforge.org/
37
+ post_install_message:
38
+ rdoc_options: []
39
+
40
+ require_paths:
41
+ - lib
42
+ required_ruby_version: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: "0"
47
+ version:
48
+ required_rubygems_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: "0"
53
+ version:
54
+ requirements: []
55
+
56
+ rubyforge_project: assert2
57
+ rubygems_version: 1.0.1
58
+ signing_key:
59
+ specification_version: 2
60
+ summary: An assertion that reflects its block, with all intermediate values
61
+ test_files: []
62
+