assert2 0.3.9 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/assert2/rubynode_reflector.rb +1 -3
- data/lib/assert2/rubynode_reflector.rb~ +859 -0
- data/lib/assert2/xhtml.rb +42 -10
- data/lib/assert2/xhtml.rb~ +41 -10
- metadata +3 -2
@@ -49,9 +49,7 @@ module Test; module Unit; module Assertions
|
|
49
49
|
# a string of disassembled Ruby
|
50
50
|
#
|
51
51
|
def reflect_source(&block)
|
52
|
-
|
53
|
-
rf.block = block
|
54
|
-
return rf.result
|
52
|
+
RubyReflector.new(nil, block, false).result
|
55
53
|
end
|
56
54
|
|
57
55
|
# This compiles a string and +reflect+s its source...
|
@@ -0,0 +1,859 @@
|
|
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
|
+
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)
|
52
|
+
RubyReflector.new(nil, block, false).result
|
53
|
+
end
|
54
|
+
|
55
|
+
# This compiles a string and +reflect+s its source...
|
56
|
+
# as another string.
|
57
|
+
#
|
58
|
+
def reflect_string(string)
|
59
|
+
rf = RubyReflector.new # def initialize
|
60
|
+
rf.block = proc{}
|
61
|
+
rf.reflect_values = false
|
62
|
+
# pp string.parse_to_nodes.transform
|
63
|
+
got = rf.reflect_nodes(string.parse_to_nodes)
|
64
|
+
return got
|
65
|
+
end
|
66
|
+
|
67
|
+
class RubyReflector # this class turns hamburger back into live cattle
|
68
|
+
HAS_RIPPER = false
|
69
|
+
|
70
|
+
begin
|
71
|
+
require 'rubygems'
|
72
|
+
require 'rubynode'
|
73
|
+
HAS_RUBYNODE = true
|
74
|
+
rescue LoadError
|
75
|
+
HAS_RUBYNODE = false
|
76
|
+
end
|
77
|
+
|
78
|
+
attr_reader :evaluations,
|
79
|
+
:result,
|
80
|
+
:transformation
|
81
|
+
attr_writer :block,
|
82
|
+
:reflect_values
|
83
|
+
|
84
|
+
def initialize(called = nil, yo_block = nil, reflect_values = true) # note that a block, from your context, is not optional
|
85
|
+
# FIXME these args are bogus use or lose
|
86
|
+
@reflect_values = reflect_values
|
87
|
+
@evaluations = []
|
88
|
+
@result = ''
|
89
|
+
@line = 0
|
90
|
+
self.block = yo_block
|
91
|
+
end
|
92
|
+
|
93
|
+
def block=(yo_block)
|
94
|
+
@block = yo_block and @block.respond_to?(:body_node) and
|
95
|
+
reflect_nodes(@block.body_node)
|
96
|
+
end
|
97
|
+
|
98
|
+
def reflect_nodes(body_node)
|
99
|
+
if body_node
|
100
|
+
@transformation = body_node.transform(:include_node => true)
|
101
|
+
return @result = _send(@transformation)
|
102
|
+
end
|
103
|
+
rescue
|
104
|
+
puts "\nOffending line: #{ @line }"
|
105
|
+
raise
|
106
|
+
end
|
107
|
+
|
108
|
+
def absorb_block_args(code_fragments) # CONSIDER a suckier way of detecting
|
109
|
+
@captured_block_vars = nil # the block args is indeed remotely possible...
|
110
|
+
if code_fragments.first =~ /\|(.*)\|/ or code_fragments[1].to_s =~ /\|(.*)\|/
|
111
|
+
@captured_block_vars = $1
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def detect(expression)
|
116
|
+
raise 'FIXME'
|
117
|
+
expr = expression
|
118
|
+
$__args = nil
|
119
|
+
if @args and @captured_block_vars
|
120
|
+
expr = "#{@captured_block_vars} = $__args.kind_of?(Array) && $__args.length == 1 ? $__args.first : $__args\n" +
|
121
|
+
expr
|
122
|
+
$__args = @args
|
123
|
+
end
|
124
|
+
|
125
|
+
begin
|
126
|
+
intermediate = eval(expr, @block.binding)
|
127
|
+
@evaluations << [expression, intermediate, nil]
|
128
|
+
rescue SyntaxError => e
|
129
|
+
if e.message.index('syntax error, unexpected \',\'') and expression !~ /\[ /
|
130
|
+
return detect('[ ' + expression + ' ]')
|
131
|
+
end # faint prayer to infinite recursion diety here! (-;
|
132
|
+
|
133
|
+
@evaluations << [expression, nil, e.message]
|
134
|
+
rescue => e
|
135
|
+
@evaluations << [expression, nil, e.message]
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def eval_intermediate(expression)
|
140
|
+
detect(expression) if @reflect_values
|
141
|
+
return expression
|
142
|
+
end
|
143
|
+
|
144
|
+
def short_inspect(intermediate)
|
145
|
+
pretty = intermediate.inspect
|
146
|
+
# ERGO Proc is prob'ly rare here!
|
147
|
+
pretty = { '#<Proc' => '<Proc>' }.fetch(pretty.split(':').first, pretty)
|
148
|
+
prettier = pretty[0..90]
|
149
|
+
prettier << '*** ' unless prettier == pretty
|
150
|
+
return prettier
|
151
|
+
end
|
152
|
+
private :short_inspect
|
153
|
+
|
154
|
+
# ERGO spew the backrefs (?) any regular expression matchers may emit!
|
155
|
+
# ERGO don't eval the caller of a block without its block!
|
156
|
+
|
157
|
+
def format_evaluations
|
158
|
+
max_line = @evaluations.map{|exp, val, prob| exp.length}.sort.last
|
159
|
+
already = {}
|
160
|
+
lines = []
|
161
|
+
|
162
|
+
@evaluations.each do |exp, val, prob|
|
163
|
+
line = " #{ exp.center(max_line) } "
|
164
|
+
|
165
|
+
line << if prob then
|
166
|
+
orange('--? ' + prob)
|
167
|
+
else
|
168
|
+
green('--> ') + bold(short_inspect(val))
|
169
|
+
end
|
170
|
+
|
171
|
+
lines << line unless already[line] == true
|
172
|
+
already[line] = true
|
173
|
+
end
|
174
|
+
|
175
|
+
return lines.compact.join("\n")
|
176
|
+
end
|
177
|
+
|
178
|
+
def _send(node, thence = '')
|
179
|
+
return '' unless node
|
180
|
+
return node.to_s + thence if node.class == Symbol
|
181
|
+
target = :"_#{ node.first }"
|
182
|
+
last = node.last
|
183
|
+
(@line = last[:node].line) rescue nil
|
184
|
+
exp = send(target, last)
|
185
|
+
exp << thence if exp.length > 0
|
186
|
+
return exp
|
187
|
+
end
|
188
|
+
|
189
|
+
%w( args beg body cond cpath defn else end ensr
|
190
|
+
first head iter ivar lit mid next second
|
191
|
+
stts recv resq rest value var vid ).each do |sender|
|
192
|
+
define_method sender + '_' do |node, *args|
|
193
|
+
return _send(node[sender.to_sym], *args)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
########################################################
|
198
|
+
#### structures
|
199
|
+
|
200
|
+
def _block(node)
|
201
|
+
return node.map{|n| _send(n, "\n") }.join
|
202
|
+
end
|
203
|
+
|
204
|
+
def _module(node, what = 'module')
|
205
|
+
return what + ' ' + cpath_(node) + "\n" +
|
206
|
+
body_(node) +
|
207
|
+
"\nend\n"
|
208
|
+
end
|
209
|
+
|
210
|
+
def _method(node)
|
211
|
+
p node
|
212
|
+
return ''
|
213
|
+
end
|
214
|
+
|
215
|
+
def _class(node); _module(node, 'class'); end
|
216
|
+
def _self(node); 'self'; end
|
217
|
+
def _defn(node); _defs(node); end
|
218
|
+
def _super(node); 'super(' + args_(node) + ')'; end
|
219
|
+
def _zsuper(node); 'super'; end
|
220
|
+
def _begin(node); "begin\n" + body_(node) + "\nend\n"; end
|
221
|
+
def _ensure(node); head_(node) + "\nensure\n" + ensr_(node); end
|
222
|
+
|
223
|
+
def _sclass(node)
|
224
|
+
return 'class << ' + recv_(node) +
|
225
|
+
head_body(node) +
|
226
|
+
"end\n"
|
227
|
+
end
|
228
|
+
|
229
|
+
class ScopeMethod #:nodoc:
|
230
|
+
# this is complex because Ruby gloms several different
|
231
|
+
# kinds of variables into one "scope" token, and they
|
232
|
+
# don't directly match their layout in the source code
|
233
|
+
|
234
|
+
def _scopic(ref, node)
|
235
|
+
@ref = ref
|
236
|
+
@node = node
|
237
|
+
@expression = ''
|
238
|
+
@previously = false
|
239
|
+
@block_arg = false
|
240
|
+
@splat_arg = false
|
241
|
+
@previous_splat = false
|
242
|
+
|
243
|
+
if @node[:tbl]
|
244
|
+
@expression << '('
|
245
|
+
render_argument_list
|
246
|
+
@expression << ')'
|
247
|
+
end
|
248
|
+
|
249
|
+
@expression << "\n"
|
250
|
+
@expression << @ref.next_(@node)
|
251
|
+
return @expression
|
252
|
+
end
|
253
|
+
|
254
|
+
def ulterior_comma(token)
|
255
|
+
@expression << ', ' if @index > 0
|
256
|
+
@expression << token
|
257
|
+
@index = 0
|
258
|
+
end
|
259
|
+
|
260
|
+
def possible_comma(token)
|
261
|
+
@expression << ', ' if @index > 0
|
262
|
+
@expression << @ref._send(token)
|
263
|
+
end
|
264
|
+
|
265
|
+
def render_argument_list
|
266
|
+
@nekst = @node[:next]
|
267
|
+
@block = @nekst.last if @nekst && @nekst.first == :block
|
268
|
+
@args = @block.first.last if @block && @block.first.first == :args
|
269
|
+
@rest = @args[:rest] if @args
|
270
|
+
@opt = @args[:opt] if @args
|
271
|
+
|
272
|
+
@node[:tbl].each_with_index do |_n, _index|
|
273
|
+
@n, @index = _n, _index
|
274
|
+
render_argument
|
275
|
+
break if @block_arg
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
def render_argument
|
280
|
+
@splat_arg = @block_arg = false
|
281
|
+
|
282
|
+
if @rest and @rest.first == :lasgn and
|
283
|
+
(@n == nil or @rest.last[:vid] == @n)
|
284
|
+
ulterior_comma('*')
|
285
|
+
@splat_arg = true
|
286
|
+
end
|
287
|
+
|
288
|
+
if @block and (ba = @block[1]) and
|
289
|
+
ba.first == :block_arg and ba.last[:vid] == @n
|
290
|
+
ulterior_comma('&')
|
291
|
+
@block_arg = true
|
292
|
+
end
|
293
|
+
|
294
|
+
# ERGO Ruby 1.9 changes these rules!!
|
295
|
+
|
296
|
+
if !@previous_splat or @block_arg
|
297
|
+
if @opt and @opt.first == :block and # ERGO why a @block??
|
298
|
+
(lasgn = @opt.last.first).first == :lasgn and
|
299
|
+
lasgn.last[:vid] == @n
|
300
|
+
@previously = true
|
301
|
+
possible_comma(@opt.last.first)
|
302
|
+
else
|
303
|
+
possible_comma(@n)
|
304
|
+
@expression << ' = nil' if @previously and !@block_arg and !@splat_arg
|
305
|
+
end
|
306
|
+
|
307
|
+
@previous_splat ||= @splat_arg
|
308
|
+
end
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
312
|
+
def _scope(node)
|
313
|
+
return ScopeMethod.new._scopic(self, node)
|
314
|
+
end
|
315
|
+
|
316
|
+
def _defs(node)
|
317
|
+
return 'def ' + recv_(node, '.') + mid_(node) +
|
318
|
+
defn_(node) +
|
319
|
+
"end\n"
|
320
|
+
end
|
321
|
+
|
322
|
+
def _rescue(node)
|
323
|
+
if node[:else] == false and node[:head] and
|
324
|
+
node[:resq] and node[:head].first == :vcall
|
325
|
+
return head_(node) + ' rescue ' + resq_(node)
|
326
|
+
else
|
327
|
+
exp = head_(node) +
|
328
|
+
else_(node) +
|
329
|
+
"rescue"
|
330
|
+
if node[:resq] and node[:resq].first == :resbody
|
331
|
+
body = node[:resq].last
|
332
|
+
exp << ' ' + args_(body) if body and body[:args]
|
333
|
+
end
|
334
|
+
return exp + "\n" + resq_(node)
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
def _resbody(node)
|
339
|
+
return body_(node)
|
340
|
+
# already emitted: head_(node) + ' ' + args_(node)
|
341
|
+
end
|
342
|
+
|
343
|
+
def _yield(node)
|
344
|
+
exp = 'yield'
|
345
|
+
exp << '(' + head_(node) + ')' if node[:head]
|
346
|
+
return exp
|
347
|
+
end
|
348
|
+
|
349
|
+
def _alias(node)
|
350
|
+
return "alias #{ lit_(node[:new].last) } #{ lit_(node[:old].last) }"
|
351
|
+
end
|
352
|
+
|
353
|
+
def _valias(node)
|
354
|
+
return "alias #{ node[:new] } #{ node[:old] }"
|
355
|
+
end
|
356
|
+
|
357
|
+
########################################################
|
358
|
+
#### control flow
|
359
|
+
|
360
|
+
def _if(node)
|
361
|
+
expression = '( if ' + eval_parenz{cond_(node)} + ' then '
|
362
|
+
expression << eval_parenz{body_(node)} if node[:body]
|
363
|
+
expression << ' else ' +
|
364
|
+
eval_parenz{else_(node)} if node[:else]
|
365
|
+
expression << ' end )'
|
366
|
+
return expression
|
367
|
+
end
|
368
|
+
|
369
|
+
def _while(node, concept = 'while')
|
370
|
+
return '( ' + concept + ' ' + cond_(node) +
|
371
|
+
head_body(node) +
|
372
|
+
"\nend )"
|
373
|
+
end
|
374
|
+
|
375
|
+
def _for(node)
|
376
|
+
return '( for ' + var_(node) + ' in ' + iter_(node) + "\n" +
|
377
|
+
body_(node) + "\n" +
|
378
|
+
'end )'
|
379
|
+
end
|
380
|
+
|
381
|
+
def _args(node); return ''; end # _call and _fcall insert the real args
|
382
|
+
def _until(node); _while(node, 'until'); end
|
383
|
+
def _break(node); 'break'; end
|
384
|
+
def _next(node); 'next' ; end
|
385
|
+
def _case(node); '( case ' + head_body(node) + "\nend )"; end
|
386
|
+
def _when(node); 'when ' + head_body(node) + "\n" + next_(node); end
|
387
|
+
def _retry(node); 'retry'; end
|
388
|
+
def _redo(node); 'redo'; end
|
389
|
+
def head_body(node); head_(node) + "\n" + body_(node); end
|
390
|
+
|
391
|
+
def _return(node)
|
392
|
+
exp = 'return'
|
393
|
+
return exp unless stts = node[:stts]
|
394
|
+
exp << ' '
|
395
|
+
|
396
|
+
if stts.first == :array
|
397
|
+
exp << '[' + stts_(node) + ']'
|
398
|
+
elsif stts.first == :svalue
|
399
|
+
exp << stts_(node)
|
400
|
+
else
|
401
|
+
exp << eval_parenz{stts_(node)}
|
402
|
+
end
|
403
|
+
|
404
|
+
return exp
|
405
|
+
end
|
406
|
+
|
407
|
+
def _postexe(node)
|
408
|
+
raise '_postexe called with unexpected arguments' unless node == {} or node.keys == [:node]
|
409
|
+
return 'END'
|
410
|
+
end
|
411
|
+
|
412
|
+
# :argscat => [:body, :head],
|
413
|
+
|
414
|
+
########################################################
|
415
|
+
#### assignments
|
416
|
+
|
417
|
+
def _dasgn_curr(node)
|
418
|
+
expression = vid_(node)
|
419
|
+
return expression unless value = node[:value]
|
420
|
+
expression << ' = '
|
421
|
+
we_b_array = value.first == :array
|
422
|
+
expression << nest_if(we_b_array, '[', ']'){ value_(node) }
|
423
|
+
return expression
|
424
|
+
end
|
425
|
+
|
426
|
+
def _cdecl(node)
|
427
|
+
return _send(node[ node[:vid] == 0 ? :else : :vid ]) + ' = ' + value_(node)
|
428
|
+
end
|
429
|
+
|
430
|
+
def _dasgn(node); _dasgn_curr(node); end
|
431
|
+
def _iasgn(node); _dasgn_curr(node); end
|
432
|
+
def _gasgn(node); _dasgn_curr(node); end
|
433
|
+
def _lasgn(node); _dasgn_curr(node); end
|
434
|
+
def _cvasgn(node); _dasgn_curr(node); end
|
435
|
+
|
436
|
+
def _op_asgn2(node)
|
437
|
+
expression = ''
|
438
|
+
|
439
|
+
if node[:recv].first == :self
|
440
|
+
expression << 'self'
|
441
|
+
else
|
442
|
+
expression << recv_(node)
|
443
|
+
end
|
444
|
+
|
445
|
+
expression << '.'
|
446
|
+
expression << vid_(node[:next].last) + ' ||= ' + value_(node)
|
447
|
+
return expression
|
448
|
+
end
|
449
|
+
|
450
|
+
########################################################
|
451
|
+
#### operators
|
452
|
+
|
453
|
+
def _and(node, und = 'and')
|
454
|
+
return eval_intermediate( '( ' +
|
455
|
+
eval_parenz{ first_(node)} + ' ' + und + ' ' +
|
456
|
+
eval_parenz{second_(node)} + ' )' )
|
457
|
+
end
|
458
|
+
|
459
|
+
def _back_ref(node); '$' + node[:nth].chr; end
|
460
|
+
def _colon2(node); head_(node, '::') + mid_(node); end
|
461
|
+
def _colon3(node); '::' + mid_(node); end
|
462
|
+
def _cvar(node); _lvar(node); end
|
463
|
+
def _cvdecl(node); vid_(node) + ' = ' + value_(node); end
|
464
|
+
def _defined(node); 'defined? ' + head_(node); end
|
465
|
+
def _dot2(node); '( ' + beg_(node) + ' .. ' + end_(node) + ' )'; end
|
466
|
+
def _dot3(node); '( ' + beg_(node) + ' ... ' + end_(node) + ' )'; end
|
467
|
+
def _dregx(node); _dstr(node, '/'); end
|
468
|
+
def _dregx_once(node); _dstr(node, '/'); end
|
469
|
+
def _dsym(node); ':' + _lit(node[:lit]) + ' ' + rest_(node); end
|
470
|
+
def _dvar(node); eval_intermediate(vid_(node)); end
|
471
|
+
def _dxstr(node); _dstr(node, '`'); end
|
472
|
+
def eval_parenz; eval_intermediate('( ' + yield + ' )'); end
|
473
|
+
def _evstr(node); body_(node); end
|
474
|
+
def _false(nada); 'false'; end
|
475
|
+
def _gvar(node); vid_(node); end
|
476
|
+
def _ivar(node); _dvar(node); end
|
477
|
+
def _lit(node); node[:lit].inspect; end
|
478
|
+
def _lvar(node); eval_intermediate(vid_(node)); end
|
479
|
+
def _match(node); node[:lit].inspect; end
|
480
|
+
def neg_one(node); node == -1 ? '' : _send(node); end
|
481
|
+
def _nil(nada); 'nil' ; end
|
482
|
+
def _not(node); '(not(' + body_(node) + '))'; end
|
483
|
+
def _nth_ref(node); "$#{ node[:nth] }"; end # ERGO eval it?
|
484
|
+
def _op_asgn_and(node); _op_asgn_or(node, ' &&= '); end
|
485
|
+
def _or(node); _and(node, 'or'); end
|
486
|
+
def _str(node); _lit(node); end
|
487
|
+
def _svalue(node); head_(node); end
|
488
|
+
def _to_ary(node); head_(node); end
|
489
|
+
def _true(nada); 'true' ; end
|
490
|
+
def _undef(node); 'undef ' + mid_(node); end
|
491
|
+
def _vcall(node); mid_(node); end
|
492
|
+
def we_b(node); node.first.first; end
|
493
|
+
def _xstr(node); '`' + scrape_literal(node) + '`'; end
|
494
|
+
def _zarray(node); return '[]'; end
|
495
|
+
|
496
|
+
def _flip2(node) # ERGO what the heck is this??
|
497
|
+
p node
|
498
|
+
p node.keys
|
499
|
+
return ''
|
500
|
+
end
|
501
|
+
|
502
|
+
def _masgn(node)
|
503
|
+
|
504
|
+
#{:value=>
|
505
|
+
# [:splat,
|
506
|
+
# {:head=>
|
507
|
+
# [:fcall,
|
508
|
+
# {:mid=>:calc_stack,
|
509
|
+
# :args=>
|
510
|
+
# [:array,
|
511
|
+
# [[:vcall, {:mid=>:insn}],
|
512
|
+
# [:vcall, {:mid=>:from}],
|
513
|
+
# [:vcall, {:mid=>:after}],
|
514
|
+
# [:vcall, {:mid=>:opops}]]]}]}],
|
515
|
+
# :args=>false,
|
516
|
+
# :head=>
|
517
|
+
# [:array,
|
518
|
+
# [[:dasgn_curr, {:value=>false, :vid=>:name}],
|
519
|
+
# [:dasgn_curr, {:value=>false, :vid=>:pops}],
|
520
|
+
# [:dasgn_curr, {:value=>false, :vid=>:rets}],
|
521
|
+
# [:dasgn_curr, {:value=>false, :vid=>:pushs1}],
|
522
|
+
# [:dasgn_curr, {:value=>false, :vid=>:pushs2}]]]}
|
523
|
+
|
524
|
+
value, head, args = node.values_at(:value, :head, :args)
|
525
|
+
|
526
|
+
if value
|
527
|
+
return '( ' + head_(node) + ' = *' + head_(value.last) + ' )' if value.first == :splat
|
528
|
+
|
529
|
+
if head and args
|
530
|
+
exp = head_(node)
|
531
|
+
return exp + ', * = ' + value_(node) if args == -1
|
532
|
+
return exp + ', *' + args_(node) + ' = ' + value_(node)
|
533
|
+
end
|
534
|
+
|
535
|
+
return '( ' + head_(node) + ' = ' + value_(node) + ' )' if args == false
|
536
|
+
end
|
537
|
+
|
538
|
+
if value == false and head == false and args
|
539
|
+
return '*' + neg_one(args)
|
540
|
+
end
|
541
|
+
|
542
|
+
if head.kind_of?(Array) and head.first == :array
|
543
|
+
return head.last.map{|n|
|
544
|
+
nest_if(n.first == :masgn, '(', ')'){ _send(n) }
|
545
|
+
}.join(', ')
|
546
|
+
end
|
547
|
+
|
548
|
+
if head == false and args and value
|
549
|
+
return '*' + args_(node) + ' = ' + value_(node)
|
550
|
+
end
|
551
|
+
|
552
|
+
return head_(node)
|
553
|
+
end
|
554
|
+
|
555
|
+
def _splat(node)
|
556
|
+
if (head = node[:head]) and
|
557
|
+
((we_b_array = head.first == :array) or head.first == :lvar)
|
558
|
+
return '*' + nest_if(we_b_array, '[', ']'){ head_(node) }
|
559
|
+
end
|
560
|
+
|
561
|
+
return '*' + head_(node)
|
562
|
+
end # ERGO raise if any other key!
|
563
|
+
|
564
|
+
def _const(node)
|
565
|
+
expression = vid_(node)
|
566
|
+
q = eval(expression, @block.binding)
|
567
|
+
eval_intermediate(expression) unless q.kind_of?(Module)
|
568
|
+
return expression
|
569
|
+
rescue # ERGO will someone need to see whatever this was?
|
570
|
+
return expression
|
571
|
+
end
|
572
|
+
|
573
|
+
def scrape_literal(node, regex = false)
|
574
|
+
lit = node[:lit].inspect.gsub(/^"/, '').gsub(/"$/, '')
|
575
|
+
lit.gsub!('\\\\', '\\') if regex
|
576
|
+
return lit
|
577
|
+
end
|
578
|
+
|
579
|
+
def _dstr(node, delim = '"')
|
580
|
+
regex = delim == '/'
|
581
|
+
expression = delim + scrape_literal(node, regex)
|
582
|
+
|
583
|
+
if node[:next] and node[:next].first == :array
|
584
|
+
(node[:next].last || []).each do |n|
|
585
|
+
expression << if n.first == :str
|
586
|
+
scrape_literal(n.last, regex)
|
587
|
+
else
|
588
|
+
'#{ ' + _send(n) + ' }'
|
589
|
+
end
|
590
|
+
end
|
591
|
+
end
|
592
|
+
|
593
|
+
return eval_intermediate(expression + delim)
|
594
|
+
end
|
595
|
+
|
596
|
+
def _retry(node)
|
597
|
+
raise '_retry called with unexpected arguments' unless node == {} or node.keys == [:node]
|
598
|
+
return 'retry'
|
599
|
+
end
|
600
|
+
|
601
|
+
def recv_zero_self(node, plus = '')
|
602
|
+
recv = node[:recv]
|
603
|
+
return 'self' + plus if recv == 0
|
604
|
+
return recv_(node, plus)
|
605
|
+
end
|
606
|
+
|
607
|
+
def _attrasgn(node)
|
608
|
+
recv, args = node.values_at(:recv, :args)
|
609
|
+
|
610
|
+
if args
|
611
|
+
if args.first == :array
|
612
|
+
if node[:mid].class == Symbol
|
613
|
+
if node[:mid] == :'[]='
|
614
|
+
return recv_zero_self(node) + '[' +
|
615
|
+
_send(args.last.first) + '] = ' +
|
616
|
+
_send(args.last.last)
|
617
|
+
end
|
618
|
+
return recv_zero_self(node, '.') + mid_(node) + '(' + _send(args.last.last) + ')'
|
619
|
+
end
|
620
|
+
end
|
621
|
+
|
622
|
+
return recv_zero_self(node) +
|
623
|
+
'[' + head_(args.last) + '] = ' +
|
624
|
+
body_(args.last)
|
625
|
+
end
|
626
|
+
|
627
|
+
return recv_zero_self(node, '.') + node[:mid].to_s.gsub(/=$/, '')
|
628
|
+
end
|
629
|
+
|
630
|
+
def _op_asgn_or(node, op = ' ||= ')
|
631
|
+
# CONSIDER what be :aid?
|
632
|
+
#{:value=>[:lasgn, {:value=>[:str, {:lit=>"vm_opts.h"}], :cnt=>2, :vid=>:file}],
|
633
|
+
# :aid=>0,
|
634
|
+
# :head=>[:lvar, {:cnt=>2, :vid=>:file}]}
|
635
|
+
return head_(node) + op + value_(node[:value].last)
|
636
|
+
end
|
637
|
+
|
638
|
+
def fcall_args(node = nil, methodic = false)
|
639
|
+
expression = ''
|
640
|
+
return expression unless node
|
641
|
+
expression << ' ' unless methodic
|
642
|
+
return expression + nest_if(methodic, '(', ')'){ _send(node) }
|
643
|
+
end
|
644
|
+
|
645
|
+
def _fcall(node)
|
646
|
+
exp = mid_(node) + fcall_args(node[:args], true)
|
647
|
+
eval_intermediate(exp) unless %w(lambda proc).include?(exp)
|
648
|
+
return exp
|
649
|
+
end
|
650
|
+
|
651
|
+
def _block_pass(node)
|
652
|
+
fcall = node[:iter].last
|
653
|
+
return eval_intermediate(recv_(fcall, '.') +
|
654
|
+
mid_(fcall) + '(' + args_(fcall, ', ') +
|
655
|
+
'&' + body_(node) + ')' )
|
656
|
+
end
|
657
|
+
|
658
|
+
def _iter(node)
|
659
|
+
var = node[:var]
|
660
|
+
|
661
|
+
return eval_intermediate(
|
662
|
+
iter_(node) +
|
663
|
+
'{' +
|
664
|
+
nest_if(var != false, '|', '|'){ var_(node) unless var == 0 } +
|
665
|
+
nest_if(node[:body] , ' ', ' '){ body_(node) } +
|
666
|
+
'}')
|
667
|
+
end
|
668
|
+
|
669
|
+
def _array(node)
|
670
|
+
nest_if we_b(node) == :array, '[', ']' do
|
671
|
+
node.map{ |z|
|
672
|
+
exp = _send(z)
|
673
|
+
exp << ', ' unless z.object_id == node.last.object_id
|
674
|
+
exp
|
675
|
+
}.join
|
676
|
+
end
|
677
|
+
end
|
678
|
+
|
679
|
+
def _hash(node)
|
680
|
+
return '{}' unless node[:head] and (array = node[:head].last)
|
681
|
+
expression = '{ '
|
682
|
+
|
683
|
+
array.in_groups_of 2 do |key, value|
|
684
|
+
expression << _send(key) + ' => ' + _send(value)
|
685
|
+
expression << ', ' if value != array.last
|
686
|
+
end
|
687
|
+
|
688
|
+
return expression + ' }'
|
689
|
+
end
|
690
|
+
|
691
|
+
def _match2(node)
|
692
|
+
# ERGO should this work like match3?
|
693
|
+
return recv_(node) + ' =~ ' + value_(node)
|
694
|
+
end
|
695
|
+
|
696
|
+
def we_b_op(node)
|
697
|
+
return node[:mid] && node[:mid].to_s !~ /^[a-z]/i
|
698
|
+
end
|
699
|
+
|
700
|
+
def _match3(node)
|
701
|
+
# ERGO do :lit and :value exclude each other?
|
702
|
+
return recv_(node) + ' =~ ' + _send(node[:lit] || node[:value])
|
703
|
+
end
|
704
|
+
|
705
|
+
def _block_arg(node) # is this ever called?
|
706
|
+
return '' # note that _scope should not take care of this
|
707
|
+
end
|
708
|
+
|
709
|
+
class CallMethod
|
710
|
+
def bracket_args
|
711
|
+
return false unless @mid == '[]'
|
712
|
+
@expression << '[' + @ref.args_(@node) + ']'
|
713
|
+
return true
|
714
|
+
end
|
715
|
+
|
716
|
+
def insert_method_call
|
717
|
+
@expression << '.'
|
718
|
+
@expression << @mid
|
719
|
+
@expression << '(' + @ref.args_(@node) + ')'
|
720
|
+
@ref.eval_intermediate(@expression) if @methodic
|
721
|
+
end
|
722
|
+
|
723
|
+
def operator_and_arguments
|
724
|
+
@mid = @ref.mid_(@node)
|
725
|
+
|
726
|
+
unless bracket_args
|
727
|
+
@methodic = @mid =~ /[a-z]/i
|
728
|
+
|
729
|
+
if @methodic
|
730
|
+
insert_method_call
|
731
|
+
else
|
732
|
+
@expression << ' '
|
733
|
+
@expression << @mid
|
734
|
+
@expression << ' '
|
735
|
+
nest_args
|
736
|
+
end
|
737
|
+
end
|
738
|
+
end
|
739
|
+
|
740
|
+
def nest_args
|
741
|
+
return unless @args = @node[:args]
|
742
|
+
|
743
|
+
nest_me = @args.first == :array &&
|
744
|
+
@args.last.length == 1 &&
|
745
|
+
(call = @args.last.first).first == :call &&
|
746
|
+
@ref.we_b_op(call.last)
|
747
|
+
|
748
|
+
exp = @ref.nest_if(nest_me, '( ', ' )'){ @ref.args_(@node) }
|
749
|
+
@ref.eval_intermediate(exp) if nest_me
|
750
|
+
@expression << exp
|
751
|
+
end
|
752
|
+
|
753
|
+
def caller(ref, node)
|
754
|
+
@ref, @node = ref, node
|
755
|
+
@expression = ''
|
756
|
+
@recv = @node[:recv]
|
757
|
+
|
758
|
+
if @recv.first == :block_pass
|
759
|
+
@expression << @ref.recv_(@node)
|
760
|
+
operator_and_arguments
|
761
|
+
else
|
762
|
+
nest_me = @recv.first == :call && @ref.we_b_op(@recv.last)
|
763
|
+
|
764
|
+
exp = if @recv.first == :array
|
765
|
+
@ref.nest_if(true, '[ ', ' ]'){ @ref.recv_(node) }
|
766
|
+
else
|
767
|
+
exp2 = @ref.nest_if(nest_me, '( ', ' )'){ @ref.recv_(node) }
|
768
|
+
@ref.eval_intermediate(exp2) if nest_me
|
769
|
+
exp2
|
770
|
+
end
|
771
|
+
|
772
|
+
@expression << exp
|
773
|
+
operator_and_arguments
|
774
|
+
end
|
775
|
+
return @expression
|
776
|
+
end
|
777
|
+
end
|
778
|
+
|
779
|
+
def _call(node); CallMethod.new.caller(self, node); end
|
780
|
+
|
781
|
+
def nest_if(condition, before, after, &block)
|
782
|
+
exp = ''
|
783
|
+
exp << before if condition
|
784
|
+
exp << (block.call || '')
|
785
|
+
exp << after if condition
|
786
|
+
return exp
|
787
|
+
end
|
788
|
+
|
789
|
+
def _op_asgn1(node) # ERGO just look up the list of these?
|
790
|
+
return '' unless args = node[:args]
|
791
|
+
exp = recv_(node)
|
792
|
+
|
793
|
+
if [:'-', :'+', :'*', :'**', :'/', :^, :|, :&,
|
794
|
+
:'<<', :'>>',
|
795
|
+
].include?(node[:mid]) and
|
796
|
+
node[:recv] and args.first == :argscat
|
797
|
+
|
798
|
+
return exp +
|
799
|
+
"[#{ body_(args.last) }] #{ node[:mid] }= " + head_(args.last)
|
800
|
+
end
|
801
|
+
|
802
|
+
raise "unexpected mid value #{ node[:mid].inspect } in opcode for X= " unless node[:mid] == 0
|
803
|
+
|
804
|
+
if args.first == :argscat and args.last[:body]
|
805
|
+
exp << '[' + body_(args.last) + ']'
|
806
|
+
exp << ' ||= ' + head_(args.last)
|
807
|
+
else
|
808
|
+
raise "unexpected arguments in opcode for ||= "
|
809
|
+
end
|
810
|
+
|
811
|
+
return exp
|
812
|
+
end
|
813
|
+
|
814
|
+
def _argscat(node)
|
815
|
+
return head_(node) + ', *' +
|
816
|
+
nest_if(node[:body].first == :array, '[', ']'){ body_(node) }
|
817
|
+
end
|
818
|
+
|
819
|
+
def diagnose(diagnostic, result, called, options, block, additional_diagnostics)
|
820
|
+
@__additional_diagnostics = additional_diagnostics
|
821
|
+
@__additional_diagnostics.unshift diagnostic
|
822
|
+
self.args = options.fetch(:args, [])
|
823
|
+
rf = self
|
824
|
+
polarity = 'assert{ '
|
825
|
+
lines = rf.split_and_read(called)
|
826
|
+
|
827
|
+
if lines.first =~ /^\s*(assert|deny)/
|
828
|
+
polarity = $1 + '{ '
|
829
|
+
end
|
830
|
+
|
831
|
+
rf.absorb_block_args lines
|
832
|
+
rf.block = block
|
833
|
+
effect = " - should #{ 'not ' if polarity =~ /deny/ }pass\n"
|
834
|
+
|
835
|
+
report = rf.magenta(polarity) + rf.bold(rf.result) + rf.magenta(" }") +
|
836
|
+
rf.red(arrow_result(result) + effect) +
|
837
|
+
rf.format_evaluations
|
838
|
+
|
839
|
+
return __build_message(report)
|
840
|
+
end
|
841
|
+
|
842
|
+
def arrow_result(result) #:nodoc:
|
843
|
+
return "\t--> #{ result.inspect }"
|
844
|
+
end
|
845
|
+
|
846
|
+
end
|
847
|
+
|
848
|
+
end; end; end
|
849
|
+
|
850
|
+
unless [].respond_to? :in_groups_of
|
851
|
+
class Array
|
852
|
+
def in_groups_of(number, fill_with = nil, &block)
|
853
|
+
require 'enumerator'
|
854
|
+
collection = dup
|
855
|
+
collection << fill_with until collection.size.modulo(number).zero?
|
856
|
+
collection.each_slice(number, &block)
|
857
|
+
end
|
858
|
+
end
|
859
|
+
end
|
data/lib/assert2/xhtml.rb
CHANGED
@@ -51,7 +51,7 @@ requirements:
|
|
51
51
|
must match
|
52
52
|
- the specification only requires the attributes and structural
|
53
53
|
elements that its matcher demands; we skip the rest -
|
54
|
-
such as the <ol> and <li>
|
54
|
+
such as the <ol> and <li> elements. They can change
|
55
55
|
freely as our website upgrades
|
56
56
|
- at fault time, the matcher prints out the failing elements
|
57
57
|
and their immediate context.
|
@@ -127,12 +127,12 @@ class BeHtmlWith
|
|
127
127
|
builder.doc.children.each do |child|
|
128
128
|
@first_samples = []
|
129
129
|
# TODO warn if child is text
|
130
|
-
path = build_deep_xpath(child)
|
131
|
-
next if path == "//html[ refer(., '0') ]" # CONSIDER wtf is this?
|
130
|
+
@path = build_deep_xpath(child)
|
131
|
+
next if @path == "//html[ refer(., '0') ]" # CONSIDER wtf is this?
|
132
132
|
|
133
|
-
matchers = doc.root.xpath_with_callback path, :refer do |elements, index|
|
134
|
-
|
135
|
-
|
133
|
+
matchers = @doc.root.xpath_with_callback @path, :refer do |elements, index|
|
134
|
+
collect_samples(elements, index.to_i)
|
135
|
+
end
|
136
136
|
|
137
137
|
if matchers.empty?
|
138
138
|
@first_samples << @doc.root if @first_samples.empty? # TODO test the first_samples system
|
@@ -153,22 +153,54 @@ class BeHtmlWith
|
|
153
153
|
return '//' + build_xpath(element)
|
154
154
|
end
|
155
155
|
|
156
|
+
def build_deep_xpath_too(element)
|
157
|
+
@references = []
|
158
|
+
return '//' + build_xpath_too(element)
|
159
|
+
end
|
160
|
+
|
156
161
|
attr_reader :references
|
157
162
|
|
158
163
|
def build_xpath(element)
|
159
164
|
path = element.name.sub(/\!$/, '')
|
160
165
|
element_kids = element.children.grep(Nokogiri::XML::Element)
|
161
|
-
path <<
|
166
|
+
path << '[ '
|
167
|
+
count = @references.length
|
162
168
|
@references << element
|
163
169
|
|
164
170
|
if element_kids.any?
|
165
|
-
path <<
|
166
|
-
element_kids.map{|child|
|
171
|
+
path << element_kids.map{|child|
|
167
172
|
'./descendant::' + build_xpath(child)
|
168
173
|
}.join(' and ')
|
174
|
+
path << ' and '
|
175
|
+
end
|
176
|
+
|
177
|
+
path << "refer(., '#{count}') ]" # last so boolean short-circuiting optimizes
|
178
|
+
return path
|
179
|
+
end
|
180
|
+
|
181
|
+
def build_xpath_too(element)
|
182
|
+
path = element.name.sub(/\!$/, '')
|
183
|
+
element_kids = element.children.grep(Nokogiri::XML::Element)
|
184
|
+
path << '[ '
|
185
|
+
count = @references.length
|
186
|
+
@references << element
|
187
|
+
brackets_owed = 0
|
188
|
+
|
189
|
+
if element_kids.length > 0
|
190
|
+
child = element_kids[0]
|
191
|
+
path << './descendant::' + build_xpath_too(child)
|
192
|
+
# }.join(' and ')
|
193
|
+
# path << ' and '
|
194
|
+
end
|
195
|
+
|
196
|
+
if element_kids.length > 1
|
197
|
+
path << element_kids[1..-1].map{|child|
|
198
|
+
'[ ./following-sibling::*[ ./descendant-or-self::' + build_xpath_too(child) + ' ] ]'
|
199
|
+
}.join #(' and .')
|
169
200
|
end
|
201
|
+
path << ' and ' if element_kids.any?
|
170
202
|
|
171
|
-
path << ' ]
|
203
|
+
path << "refer(., '#{count}') ]" # last so boolean short-circuiting optimizes
|
172
204
|
return path
|
173
205
|
end
|
174
206
|
|
data/lib/assert2/xhtml.rb~
CHANGED
@@ -121,19 +121,18 @@ class BeHtmlWith
|
|
121
121
|
begin
|
122
122
|
bwock = block || @block || proc{} # TODO what to do with no block? validate?
|
123
123
|
builder = Nokogiri::HTML::Builder.new(&bwock)
|
124
|
-
puts builder.doc.root.to_html
|
125
124
|
@doc = Nokogiri::HTML(stwing)
|
126
125
|
@reason = nil
|
127
126
|
|
128
127
|
builder.doc.children.each do |child|
|
129
128
|
@first_samples = []
|
130
129
|
# TODO warn if child is text
|
131
|
-
path = build_deep_xpath(child)
|
132
|
-
next if path == "//html[ refer(., '0') ]" # CONSIDER wtf is this?
|
130
|
+
@path = build_deep_xpath(child)
|
131
|
+
next if @path == "//html[ refer(., '0') ]" # CONSIDER wtf is this?
|
133
132
|
|
134
|
-
matchers = doc.root.xpath_with_callback path, :refer do |elements, index|
|
135
|
-
|
136
|
-
|
133
|
+
matchers = @doc.root.xpath_with_callback @path, :refer do |elements, index|
|
134
|
+
collect_samples(elements, index.to_i)
|
135
|
+
end
|
137
136
|
|
138
137
|
if matchers.empty?
|
139
138
|
@first_samples << @doc.root if @first_samples.empty? # TODO test the first_samples system
|
@@ -154,22 +153,54 @@ puts builder.doc.root.to_html
|
|
154
153
|
return '//' + build_xpath(element)
|
155
154
|
end
|
156
155
|
|
156
|
+
def build_deep_xpath_too(element)
|
157
|
+
@references = []
|
158
|
+
return '//' + build_xpath_too(element)
|
159
|
+
end
|
160
|
+
|
157
161
|
attr_reader :references
|
158
162
|
|
159
163
|
def build_xpath(element)
|
160
164
|
path = element.name.sub(/\!$/, '')
|
161
165
|
element_kids = element.children.grep(Nokogiri::XML::Element)
|
162
|
-
path <<
|
166
|
+
path << '[ '
|
167
|
+
count = @references.length
|
163
168
|
@references << element
|
164
169
|
|
165
170
|
if element_kids.any?
|
166
|
-
path <<
|
167
|
-
element_kids.map{|child|
|
171
|
+
path << element_kids.map{|child|
|
168
172
|
'./descendant::' + build_xpath(child)
|
169
173
|
}.join(' and ')
|
174
|
+
path << ' and '
|
175
|
+
end
|
176
|
+
|
177
|
+
path << "refer(., '#{count}') ]" # last so boolean short-circuiting optimizes
|
178
|
+
return path
|
179
|
+
end
|
180
|
+
|
181
|
+
def build_xpath_too(element)
|
182
|
+
path = element.name.sub(/\!$/, '')
|
183
|
+
element_kids = element.children.grep(Nokogiri::XML::Element)
|
184
|
+
path << '[ '
|
185
|
+
count = @references.length
|
186
|
+
@references << element
|
187
|
+
brackets_owed = 0
|
188
|
+
|
189
|
+
if element_kids.length > 0
|
190
|
+
child = element_kids[0]
|
191
|
+
path << './descendant::' + build_xpath_too(child)
|
192
|
+
# }.join(' and ')
|
193
|
+
# path << ' and '
|
194
|
+
end
|
195
|
+
|
196
|
+
if element_kids.length > 1
|
197
|
+
path << element_kids[1..-1].map{|child|
|
198
|
+
'[ ./following-sibling::*[ ./descendant-or-self::' + build_xpath_too(child) + ' ] ]'
|
199
|
+
}.join #(' and .')
|
170
200
|
end
|
201
|
+
path << ' and ' if element_kids.any?
|
171
202
|
|
172
|
-
path << ' ]
|
203
|
+
path << "refer(., '#{count}') ]" # last so boolean short-circuiting optimizes
|
173
204
|
return path
|
174
205
|
end
|
175
206
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: assert2
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Phlip
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-03-
|
12
|
+
date: 2009-03-19 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -25,6 +25,7 @@ files:
|
|
25
25
|
- lib/assert2
|
26
26
|
- lib/assert2/xhtml.rb~
|
27
27
|
- lib/assert2/flunk.rb
|
28
|
+
- lib/assert2/rubynode_reflector.rb~
|
28
29
|
- lib/assert2/xpath.rb~
|
29
30
|
- lib/assert2/rubynode_reflector.rb
|
30
31
|
- lib/assert2/xpath.rb
|