brakeman 1.0.rc1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,1075 @@
1
+ require 'stringio'
2
+ require 'racc/parser'
3
+ require 'sexp'
4
+ require 'strscan'
5
+
6
+ # WHY do I have to do this?!?
7
+ class Regexp
8
+ ONCE = 0 unless defined? ONCE # FIX: remove this - it makes no sense
9
+
10
+ unless defined? ENC_NONE then
11
+ ENC_NONE = /x/n.options
12
+ ENC_EUC = /x/e.options
13
+ ENC_SJIS = /x/s.options
14
+ ENC_UTF8 = /x/u.options
15
+ end
16
+ end
17
+
18
+ # I hate ruby 1.9 string changes
19
+ class Fixnum
20
+ def ord
21
+ self
22
+ end
23
+ end unless "a"[0] == "a"
24
+
25
+ class RPStringScanner < StringScanner
26
+ # if ENV['TALLY'] then
27
+ # alias :old_getch :getch
28
+ # def getch
29
+ # warn({:getch => caller[0]}.inspect)
30
+ # old_getch
31
+ # end
32
+ # end
33
+ def current_line # HAHA fuck you (HACK)
34
+ string[0..pos][/\A.*__LINE__/m].split(/\n/).size
35
+ end
36
+
37
+ def extra_lines_added
38
+ @extra_lines_added ||= 0
39
+ end
40
+
41
+ def extra_lines_added= val
42
+ @extra_lines_added = val
43
+ end
44
+
45
+ def lineno
46
+ string[0...pos].count("\n") + 1 - extra_lines_added
47
+ end
48
+
49
+ # TODO: once we get rid of these, we can make things like
50
+ # TODO: current_line and lineno much more accurate and easy to do
51
+
52
+ def unread_many str # TODO: remove this entirely - we should not need it
53
+ warn({:unread_many => caller[0]}.inspect) if ENV['TALLY']
54
+ self.extra_lines_added += str.count("\n")
55
+ string[pos, 0] = str
56
+ end
57
+
58
+ if ENV['DEBUG'] then
59
+ alias :old_getch :getch
60
+ def getch
61
+ c = self.old_getch
62
+ p :getch => [c, caller.first]
63
+ c
64
+ end
65
+
66
+ alias :old_scan :scan
67
+ def scan re
68
+ s = old_scan re
69
+ p :scan => [s, caller.first] if s
70
+ s
71
+ end
72
+ end
73
+
74
+ # TODO:
75
+ # def last_line(src)
76
+ # if n = src.rindex("\n")
77
+ # src[(n+1) .. -1]
78
+ # else
79
+ # src
80
+ # end
81
+ # end
82
+ # private :last_line
83
+
84
+ # def next_words_on_error
85
+ # if n = @src.rest.index("\n")
86
+ # @src.rest[0 .. (n-1)]
87
+ # else
88
+ # @src.rest
89
+ # end
90
+ # end
91
+
92
+ # def prev_words_on_error(ev)
93
+ # pre = @pre
94
+ # if ev and /#{Regexp.quote(ev)}$/ =~ pre
95
+ # pre = $`
96
+ # end
97
+ # last_line(pre)
98
+ # end
99
+
100
+ # def on_error(et, ev, values)
101
+ # lines_of_rest = @src.rest.to_a.length
102
+ # prev_words = prev_words_on_error(ev)
103
+ # at = 4 + prev_words.length
104
+ # message = <<-MSG
105
+ # RD syntax error: line #{@blockp.line_index - lines_of_rest}:
106
+ # ...#{prev_words} #{(ev||'')} #{next_words_on_error()} ...
107
+ # MSG
108
+ # message << " " * at + "^" * (ev ? ev.length : 0) + "\n"
109
+ # raise ParseError, message
110
+ # end
111
+ end
112
+
113
+ module RubyParserStuff
114
+ VERSION = '2.3.1' unless constants.include? "VERSION" # SIGH
115
+
116
+ attr_accessor :lexer, :in_def, :in_single, :file
117
+ attr_reader :env, :comments
118
+
119
+ def arg_add(node1, node2) # TODO: nuke
120
+ return s(:arglist, node2) unless node1
121
+
122
+ node1[0] = :arglist if node1[0] == :array
123
+ return node1 << node2 if node1[0] == :arglist
124
+
125
+ return s(:arglist, node1, node2)
126
+ end
127
+
128
+ def arg_blk_pass node1, node2 # TODO: nuke
129
+ node1 = s(:arglist, node1) unless [:arglist, :array].include? node1.first
130
+ node1 << node2 if node2
131
+ node1
132
+ end
133
+
134
+ def arg_concat node1, node2 # TODO: nuke
135
+ raise "huh" unless node2
136
+ node1 << s(:splat, node2).compact
137
+ node1
138
+ end
139
+
140
+ def args arg, optarg, rest_arg, block_arg, post_arg = nil
141
+ arg ||= s(:args)
142
+
143
+ result = arg
144
+ if optarg then
145
+ optarg[1..-1].each do |lasgn| # FIX clean sexp iter
146
+ raise "wtf? #{lasgn.inspect}" unless lasgn[0] == :lasgn
147
+ result << lasgn[1]
148
+ end
149
+ end
150
+
151
+ result << rest_arg if rest_arg
152
+
153
+ result << :"&#{block_arg.last}" if block_arg
154
+ result << optarg if optarg # TODO? huh - processed above as well
155
+ post_arg[1..-1].each {|pa| result << pa } if post_arg
156
+
157
+ result
158
+ end
159
+
160
+ def args19 vals # TODO: migrate to args once 1.8 tests pass as well
161
+ result = s(:args)
162
+ block = nil
163
+
164
+ vals.each do |val|
165
+ case val
166
+ when Sexp then
167
+ case val.first
168
+ when :args then
169
+ val[1..-1].each do |name|
170
+ result << name
171
+ end
172
+ when :block_arg then
173
+ result << :"&#{val.last}"
174
+ when :block then
175
+ block = val
176
+ val[1..-1].each do |lasgn| # FIX clean sexp iter
177
+ raise "wtf? #{val.inspect}" unless lasgn[0] == :lasgn
178
+ result << lasgn[1]
179
+ end
180
+ else
181
+ raise "unhandled sexp: #{val.inspect}"
182
+ end
183
+ when Symbol then
184
+ result << val
185
+ when ",", nil then
186
+ # ignore
187
+ else
188
+ raise "unhandled val: #{val.inspect} in #{vals.inspect}"
189
+ end
190
+ end
191
+
192
+ result << block if block
193
+
194
+ result
195
+ end
196
+
197
+ def aryset receiver, index
198
+ index[0] = :arglist if index[0] == :array
199
+ s(:attrasgn, receiver, :"[]=", index)
200
+ end
201
+
202
+ def assignable(lhs, value = nil)
203
+ id = lhs.to_sym
204
+ id = id.to_sym if Sexp === id
205
+
206
+ raise SyntaxError, "Can't change the value of #{id}" if
207
+ id.to_s =~ /^(?:self|nil|true|false|__LINE__|__FILE__)$/
208
+
209
+ result = case id.to_s
210
+ when /^@@/ then
211
+ asgn = in_def || in_single > 0
212
+ s((asgn ? :cvasgn : :cvdecl), id)
213
+ when /^@/ then
214
+ s(:iasgn, id)
215
+ when /^\$/ then
216
+ s(:gasgn, id)
217
+ when /^[A-Z]/ then
218
+ s(:cdecl, id)
219
+ else
220
+ case self.env[id]
221
+ when :lvar then
222
+ s(:lasgn, id)
223
+ when :dvar, nil then
224
+ if self.env.current[id] == :dvar then
225
+ s(:lasgn, id)
226
+ elsif self.env[id] == :dvar then
227
+ self.env.use(id)
228
+ s(:lasgn, id)
229
+ elsif ! self.env.dynamic? then
230
+ s(:lasgn, id)
231
+ else
232
+ s(:lasgn, id)
233
+ end
234
+ else
235
+ raise "wtf? unknown type: #{self.env[id]}"
236
+ end
237
+ end
238
+
239
+ self.env[id] ||= :lvar
240
+
241
+ result << value if value
242
+
243
+ return result
244
+ end
245
+
246
+ def block_append(head, tail)
247
+ return head if tail.nil?
248
+ return tail if head.nil?
249
+
250
+ case head[0]
251
+ when :lit, :str then
252
+ return tail
253
+ end
254
+
255
+ line = [head.line, tail.line].compact.min
256
+
257
+ head = remove_begin(head)
258
+ head = s(:block, head) unless head.node_type == :block
259
+
260
+ head.line = line
261
+ head << tail
262
+ end
263
+
264
+ def cond node
265
+ return nil if node.nil?
266
+ node = value_expr node
267
+
268
+ case node.first
269
+ when :lit then
270
+ if Regexp === node.last then
271
+ return s(:match, node)
272
+ else
273
+ return node
274
+ end
275
+ when :and then
276
+ return s(:and, cond(node[1]), cond(node[2]))
277
+ when :or then
278
+ return s(:or, cond(node[1]), cond(node[2]))
279
+ when :dot2 then
280
+ label = "flip#{node.hash}"
281
+ env[label] = :lvar
282
+ return s(:flip2, node[1], node[2])
283
+ when :dot3 then
284
+ label = "flip#{node.hash}"
285
+ env[label] = :lvar
286
+ return s(:flip3, node[1], node[2])
287
+ else
288
+ return node
289
+ end
290
+ end
291
+
292
+ ##
293
+ # for pure ruby systems only
294
+
295
+ def do_parse
296
+ _racc_do_parse_rb(_racc_setup, false)
297
+ end if ENV['PURE_RUBY']
298
+
299
+ def get_match_node lhs, rhs # TODO: rename to new_match
300
+ if lhs then
301
+ case lhs[0]
302
+ when :dregx, :dregx_once then
303
+ return s(:match2, lhs, rhs).line(lhs.line)
304
+ when :lit then
305
+ return s(:match2, lhs, rhs).line(lhs.line) if Regexp === lhs.last
306
+ end
307
+ end
308
+
309
+ if rhs then
310
+ case rhs[0]
311
+ when :dregx, :dregx_once then
312
+ return s(:match3, rhs, lhs).line(lhs.line)
313
+ when :lit then
314
+ return s(:match3, rhs, lhs).line(lhs.line) if Regexp === rhs.last
315
+ end
316
+ end
317
+
318
+ return s(:call, lhs, :"=~", s(:arglist, rhs)).line(lhs.line)
319
+ end
320
+
321
+ def gettable(id)
322
+ id = id.to_sym if String === id
323
+
324
+ result = case id.to_s
325
+ when /^@@/ then
326
+ s(:cvar, id)
327
+ when /^@/ then
328
+ s(:ivar, id)
329
+ when /^\$/ then
330
+ s(:gvar, id)
331
+ when /^[A-Z]/ then
332
+ s(:const, id)
333
+ else
334
+ type = env[id]
335
+ if type then
336
+ s(type, id)
337
+ elsif env.dynamic? and :dvar == env[id] then
338
+ s(:lvar, id)
339
+ else
340
+ s(:call, nil, id, s(:arglist))
341
+ end
342
+ end
343
+
344
+ raise "identifier #{id.inspect} is not valid" unless result
345
+
346
+ result
347
+ end
348
+
349
+ ##
350
+ # Canonicalize conditionals. Eg:
351
+ #
352
+ # not x ? a : b
353
+ #
354
+ # becomes:
355
+ #
356
+ # x ? b : a
357
+
358
+ attr_accessor :canonicalize_conditions
359
+
360
+ def initialize(options = {})
361
+ super()
362
+
363
+ v = self.class.name[/1[89]/]
364
+ self.lexer = RubyLexer.new v && v.to_i
365
+ self.lexer.parser = self
366
+ @env = Environment.new
367
+ @comments = []
368
+
369
+ @canonicalize_conditions = true
370
+
371
+ self.reset
372
+ end
373
+
374
+ def list_append list, item # TODO: nuke me *sigh*
375
+ return s(:array, item) unless list
376
+ list = s(:array, list) unless Sexp === list && list.first == :array
377
+ list << item
378
+ end
379
+
380
+ def list_prepend item, list # TODO: nuke me *sigh*
381
+ list = s(:array, list) unless Sexp === list && list[0] == :array
382
+ list.insert 1, item
383
+ list
384
+ end
385
+
386
+ def literal_concat head, tail
387
+ return tail unless head
388
+ return head unless tail
389
+
390
+ htype, ttype = head[0], tail[0]
391
+
392
+ head = s(:dstr, '', head) if htype == :evstr
393
+
394
+ case ttype
395
+ when :str then
396
+ if htype == :str
397
+ head[-1] << tail[-1]
398
+ elsif htype == :dstr and head.size == 2 then
399
+ head[-1] << tail[-1]
400
+ else
401
+ head << tail
402
+ end
403
+ when :dstr then
404
+ if htype == :str then
405
+ tail[1] = head[-1] + tail[1]
406
+ head = tail
407
+ else
408
+ tail[0] = :array
409
+ tail[1] = s(:str, tail[1])
410
+ tail.delete_at 1 if tail[1] == s(:str, '')
411
+
412
+ head.push(*tail[1..-1])
413
+ end
414
+ when :evstr then
415
+ head[0] = :dstr if htype == :str
416
+ if head.size == 2 and tail.size > 1 and tail[1][0] == :str then
417
+ head[-1] << tail[1][-1]
418
+ head[0] = :str if head.size == 2 # HACK ?
419
+ else
420
+ head.push(tail)
421
+ end
422
+ else
423
+ x = [head, tail]
424
+ raise "unknown type: #{x.inspect}"
425
+ end
426
+
427
+ return head
428
+ end
429
+
430
+ def logop(type, left, right) # TODO: rename logical_op
431
+ left = value_expr left
432
+
433
+ if left and left[0] == type and not left.paren then
434
+ node, second = left, nil
435
+
436
+ while (second = node[2]) && second[0] == type and not second.paren do
437
+ node = second
438
+ end
439
+
440
+ node[2] = s(type, second, right)
441
+
442
+ return left
443
+ end
444
+
445
+ return s(type, left, right)
446
+ end
447
+
448
+ def new_aref val
449
+ val[2] ||= s(:arglist)
450
+ val[2][0] = :arglist if val[2][0] == :array # REFACTOR
451
+ if val[0].node_type == :self then
452
+ result = new_call nil, :"[]", val[2]
453
+ else
454
+ result = new_call val[0], :"[]", val[2]
455
+ end
456
+ result
457
+ end
458
+
459
+ def new_body val
460
+ result = val[0]
461
+
462
+ if val[1] then
463
+ result = s(:rescue)
464
+ result << val[0] if val[0]
465
+
466
+ resbody = val[1]
467
+
468
+ while resbody do
469
+ result << resbody
470
+ resbody = resbody.resbody(true)
471
+ end
472
+
473
+ result << val[2] if val[2]
474
+
475
+ result.line = (val[0] || val[1]).line
476
+ elsif not val[2].nil? then
477
+ warning("else without rescue is useless")
478
+ result = block_append(result, val[2])
479
+ end
480
+
481
+ result = s(:ensure, result, val[3]).compact if val[3]
482
+ return result
483
+ end
484
+
485
+ def new_call recv, meth, args = nil
486
+ result = s(:call, recv, meth)
487
+ result.line = recv.line if recv
488
+
489
+ args ||= s(:arglist)
490
+ args[0] = :arglist if args.first == :array
491
+ args = s(:arglist, args) unless args.first == :arglist
492
+ result << args
493
+ result
494
+ end
495
+
496
+ def new_case expr, body
497
+ result = s(:case, expr)
498
+ line = (expr || body).line
499
+
500
+ while body and body.node_type == :when
501
+ result << body
502
+ body = body.delete_at 3
503
+ end
504
+
505
+ # else
506
+ body = nil if body == s(:block)
507
+ result << body
508
+
509
+ result.line = line
510
+ result
511
+ end
512
+
513
+ def new_class val
514
+ line, path, superclass, body = val[1], val[2], val[3], val[5]
515
+ scope = s(:scope, body).compact
516
+ result = s(:class, path, superclass, scope)
517
+ result.line = line
518
+ result.comments = self.comments.pop
519
+ result
520
+ end
521
+
522
+ def new_compstmt val
523
+ result = void_stmts(val[0])
524
+ result = remove_begin(result) if result
525
+ result
526
+ end
527
+
528
+ def new_defn val
529
+ (_, line), name, args, body = val[0], val[1], val[3], val[4]
530
+ body ||= s(:nil)
531
+
532
+ body ||= s(:block)
533
+ body = s(:block, body) unless body.first == :block
534
+
535
+ result = s(:defn, name.to_sym, args, s(:scope, body))
536
+ result.line = line
537
+ result.comments = self.comments.pop
538
+ result
539
+ end
540
+
541
+ def new_defs val
542
+ recv, name, args, body = val[1], val[4], val[6], val[7]
543
+
544
+ body ||= s(:block)
545
+ body = s(:block, body) unless body.first == :block
546
+
547
+ result = s(:defs, recv, name.to_sym, args, s(:scope, body))
548
+ result.line = recv.line
549
+ result.comments = self.comments.pop
550
+ result
551
+ end
552
+
553
+ def new_for expr, var, body
554
+ result = s(:for, expr, var).line(var.line)
555
+ result << body if body
556
+ result
557
+ end
558
+
559
+ def new_if c, t, f
560
+ l = [c.line, t && t.line, f && f.line].compact.min
561
+ c = cond c
562
+ c, t, f = c.last, f, t if c[0] == :not and canonicalize_conditions
563
+ s(:if, c, t, f).line(l)
564
+ end
565
+
566
+ def new_iter call, args, body
567
+ result = s(:iter)
568
+ result << call if call
569
+ result << args
570
+ result << body if body
571
+ result
572
+ end
573
+
574
+ def new_masgn lhs, rhs, wrap = false
575
+ rhs = value_expr(rhs)
576
+ rhs = lhs[1] ? s(:to_ary, rhs) : s(:array, rhs) if wrap
577
+
578
+ lhs.delete_at 1 if lhs[1].nil?
579
+ lhs << rhs
580
+
581
+ lhs
582
+ end
583
+
584
+ def new_module val
585
+ line, path, body = val[1], val[2], val[4]
586
+ body = s(:scope, body).compact
587
+ result = s(:module, path, body)
588
+ result.line = line
589
+ result.comments = self.comments.pop
590
+ result
591
+ end
592
+
593
+ def new_op_asgn val
594
+ lhs, asgn_op, arg = val[0], val[1].to_sym, val[2]
595
+ name = lhs.value
596
+ arg = remove_begin(arg)
597
+ result = case asgn_op # REFACTOR
598
+ when :"||" then
599
+ lhs << arg
600
+ s(:op_asgn_or, self.gettable(name), lhs)
601
+ when :"&&" then
602
+ lhs << arg
603
+ s(:op_asgn_and, self.gettable(name), lhs)
604
+ else
605
+ # TODO: why [2] ?
606
+ lhs[2] = new_call(self.gettable(name), asgn_op,
607
+ s(:arglist, arg))
608
+ lhs
609
+ end
610
+ result.line = lhs.line
611
+ result
612
+ end
613
+
614
+ def new_regexp val
615
+ node = val[1] || s(:str, '')
616
+ options = val[2]
617
+
618
+ o, k = 0, nil
619
+ options.split(//).uniq.each do |c| # FIX: this has a better home
620
+ v = {
621
+ 'x' => Regexp::EXTENDED,
622
+ 'i' => Regexp::IGNORECASE,
623
+ 'm' => Regexp::MULTILINE,
624
+ 'o' => Regexp::ONCE,
625
+ 'n' => Regexp::ENC_NONE,
626
+ 'e' => Regexp::ENC_EUC,
627
+ 's' => Regexp::ENC_SJIS,
628
+ 'u' => Regexp::ENC_UTF8,
629
+ }[c]
630
+ raise "unknown regexp option: #{c}" unless v
631
+ o += v
632
+ k = c if c =~ /[esu]/
633
+ end
634
+
635
+ case node[0]
636
+ when :str then
637
+ node[0] = :lit
638
+ node[1] = if k then
639
+ Regexp.new(node[1], o, k)
640
+ else
641
+ Regexp.new(node[1], o)
642
+ end
643
+ when :dstr then
644
+ if options =~ /o/ then
645
+ node[0] = :dregx_once
646
+ else
647
+ node[0] = :dregx
648
+ end
649
+ node << o if o and o != 0
650
+ else
651
+ node = s(:dregx, '', node);
652
+ node[0] = :dregx_once if options =~ /o/
653
+ node << o if o and o != 0
654
+ end
655
+
656
+ node
657
+ end
658
+
659
+ def new_sclass val
660
+ recv, in_def, in_single, body = val[3], val[4], val[6], val[7]
661
+ scope = s(:scope, body).compact
662
+ result = s(:sclass, recv, scope)
663
+ result.line = val[2]
664
+ self.in_def = in_def
665
+ self.in_single = in_single
666
+ result
667
+ end
668
+
669
+ def new_super args
670
+ if args && args.node_type == :block_pass then
671
+ s(:super, args)
672
+ else
673
+ args ||= s(:arglist)
674
+ s(:super, *args[1..-1])
675
+ end
676
+ end
677
+
678
+ def new_undef n, m = nil
679
+ if m then
680
+ block_append(n, s(:undef, m))
681
+ else
682
+ s(:undef, n)
683
+ end
684
+ end
685
+
686
+ def new_until_or_while type, block, expr, pre
687
+ other = type == :until ? :while : :until
688
+ line = [block && block.line, expr.line].compact.min
689
+ block, pre = block.last, false if block && block[0] == :begin
690
+
691
+ expr = cond expr
692
+
693
+ result = unless expr.first == :not and canonicalize_conditions then
694
+ s(type, expr, block, pre)
695
+ else
696
+ s(other, expr.last, block, pre)
697
+ end
698
+
699
+ result.line = line
700
+ result
701
+ end
702
+
703
+ def new_until block, expr, pre
704
+ new_until_or_while :until, block, expr, pre
705
+ end
706
+
707
+ def new_while block, expr, pre
708
+ new_until_or_while :while, block, expr, pre
709
+ end
710
+
711
+ def new_xstring str
712
+ if str then
713
+ case str[0]
714
+ when :str
715
+ str[0] = :xstr
716
+ when :dstr
717
+ str[0] = :dxstr
718
+ else
719
+ str = s(:dxstr, '', str)
720
+ end
721
+ str
722
+ else
723
+ s(:xstr, '')
724
+ end
725
+ end
726
+
727
+ def new_yield args = nil
728
+ # TODO: raise args.inspect unless [:arglist].include? args.first # HACK
729
+ raise SyntaxError, "Block argument should not be given." if
730
+ args && args.node_type == :block_pass
731
+
732
+ args ||= s(:arglist)
733
+
734
+ # TODO: I can prolly clean this up
735
+ args[0] = :arglist if args.first == :array
736
+ args = s(:arglist, args) unless args.first == :arglist
737
+
738
+ return s(:yield, *args[1..-1])
739
+ end
740
+
741
+ def next_token
742
+ if self.lexer.advance then
743
+ return self.lexer.token, self.lexer.yacc_value
744
+ else
745
+ return [false, '$end']
746
+ end
747
+ end
748
+
749
+ def node_assign(lhs, rhs) # TODO: rename new_assign
750
+ return nil unless lhs
751
+
752
+ rhs = value_expr rhs
753
+
754
+ case lhs[0]
755
+ when :gasgn, :iasgn, :lasgn, :dasgn, :dasgn_curr,
756
+ :masgn, :cdecl, :cvdecl, :cvasgn then
757
+ lhs << rhs
758
+ when :attrasgn, :call then
759
+ args = lhs.pop unless Symbol === lhs.last
760
+ lhs << arg_add(args, rhs)
761
+ when :const then
762
+ lhs[0] = :cdecl
763
+ lhs << rhs
764
+ else
765
+ raise "unknown lhs #{lhs.inspect}"
766
+ end
767
+
768
+ lhs
769
+ end
770
+
771
+ def process(str, file = "(string)")
772
+ raise "bad val: #{str.inspect}" unless String === str
773
+
774
+ self.file = file
775
+ self.lexer.src = str.dup
776
+
777
+ @yydebug = ENV.has_key? 'DEBUG'
778
+
779
+ do_parse
780
+ end
781
+ alias :parse :process
782
+
783
+ def remove_begin node
784
+ oldnode = node
785
+ if node and :begin == node[0] and node.size == 2 then
786
+ node = node[-1]
787
+ node.line = oldnode.line
788
+ end
789
+ node
790
+ end
791
+
792
+ def reset
793
+ lexer.reset
794
+ self.in_def = false
795
+ self.in_single = 0
796
+ self.env.reset
797
+ self.comments.clear
798
+ end
799
+
800
+ def ret_args node
801
+ if node then
802
+ raise SyntaxError, "block argument should not be given" if
803
+ node[0] == :block_pass
804
+
805
+ node = node.last if node[0] == :array && node.size == 2
806
+ # HACK matz wraps ONE of the FOUR splats in a newline to
807
+ # distinguish. I use paren for now. ugh
808
+ node = s(:svalue, node) if node[0] == :splat and not node.paren
809
+ node[0] = :svalue if node[0] == :arglist && node[1][0] == :splat
810
+ end
811
+
812
+ node
813
+ end
814
+
815
+ def s(*args)
816
+ result = Sexp.new(*args)
817
+ result.line ||= lexer.lineno if lexer.src # otherwise...
818
+ result.file = self.file
819
+ result
820
+ end
821
+
822
+ def value_expr oldnode # HACK
823
+ node = remove_begin oldnode
824
+ node.line = oldnode.line if oldnode
825
+ node[2] = value_expr(node[2]) if node and node[0] == :if
826
+ node
827
+ end
828
+
829
+ def void_stmts node
830
+ return nil unless node
831
+ return node unless node[0] == :block
832
+
833
+ node[1..-1] = node[1..-1].map { |n| remove_begin(n) }
834
+ node
835
+ end
836
+
837
+ def warning s
838
+ # do nothing for now
839
+ end
840
+
841
+ def yyerror msg
842
+ # for now do nothing with the msg
843
+ super
844
+ end
845
+
846
+ class Keyword
847
+ class KWtable
848
+ attr_accessor :name, :state, :id0, :id1
849
+ def initialize(name, id=[], state=nil)
850
+ @name = name
851
+ @id0, @id1 = id
852
+ @state = state
853
+ end
854
+ end
855
+
856
+ ##
857
+ # :stopdoc:
858
+ #
859
+ # :expr_beg = ignore newline, +/- is a sign.
860
+ # :expr_end = newline significant, +/- is a operator.
861
+ # :expr_arg = newline significant, +/- is a operator.
862
+ # :expr_cmdarg = newline significant, +/- is a operator.
863
+ # :expr_endarg = newline significant, +/- is a operator.
864
+ # :expr_mid = newline significant, +/- is a operator.
865
+ # :expr_fname = ignore newline, no reserved words.
866
+ # :expr_dot = right after . or ::, no reserved words.
867
+ # :expr_class = immediate after class, no here document.
868
+
869
+ wordlist = [
870
+ ["end", [:kEND, :kEND ], :expr_end ],
871
+ ["else", [:kELSE, :kELSE ], :expr_beg ],
872
+ ["case", [:kCASE, :kCASE ], :expr_beg ],
873
+ ["ensure", [:kENSURE, :kENSURE ], :expr_beg ],
874
+ ["module", [:kMODULE, :kMODULE ], :expr_beg ],
875
+ ["elsif", [:kELSIF, :kELSIF ], :expr_beg ],
876
+ ["def", [:kDEF, :kDEF ], :expr_fname ],
877
+ ["rescue", [:kRESCUE, :kRESCUE_MOD ], :expr_mid ],
878
+ ["not", [:kNOT, :kNOT ], :expr_beg ],
879
+ ["then", [:kTHEN, :kTHEN ], :expr_beg ],
880
+ ["yield", [:kYIELD, :kYIELD ], :expr_arg ],
881
+ ["for", [:kFOR, :kFOR ], :expr_beg ],
882
+ ["self", [:kSELF, :kSELF ], :expr_end ],
883
+ ["false", [:kFALSE, :kFALSE ], :expr_end ],
884
+ ["retry", [:kRETRY, :kRETRY ], :expr_end ],
885
+ ["return", [:kRETURN, :kRETURN ], :expr_mid ],
886
+ ["true", [:kTRUE, :kTRUE ], :expr_end ],
887
+ ["if", [:kIF, :kIF_MOD ], :expr_beg ],
888
+ ["defined?", [:kDEFINED, :kDEFINED ], :expr_arg ],
889
+ ["super", [:kSUPER, :kSUPER ], :expr_arg ],
890
+ ["undef", [:kUNDEF, :kUNDEF ], :expr_fname ],
891
+ ["break", [:kBREAK, :kBREAK ], :expr_mid ],
892
+ ["in", [:kIN, :kIN ], :expr_beg ],
893
+ ["do", [:kDO, :kDO ], :expr_beg ],
894
+ ["nil", [:kNIL, :kNIL ], :expr_end ],
895
+ ["until", [:kUNTIL, :kUNTIL_MOD ], :expr_beg ],
896
+ ["unless", [:kUNLESS, :kUNLESS_MOD ], :expr_beg ],
897
+ ["or", [:kOR, :kOR ], :expr_beg ],
898
+ ["next", [:kNEXT, :kNEXT ], :expr_mid ],
899
+ ["when", [:kWHEN, :kWHEN ], :expr_beg ],
900
+ ["redo", [:kREDO, :kREDO ], :expr_end ],
901
+ ["and", [:kAND, :kAND ], :expr_beg ],
902
+ ["begin", [:kBEGIN, :kBEGIN ], :expr_beg ],
903
+ ["__LINE__", [:k__LINE__, :k__LINE__ ], :expr_end ],
904
+ ["class", [:kCLASS, :kCLASS ], :expr_class ],
905
+ ["__FILE__", [:k__FILE__, :k__FILE__ ], :expr_end ],
906
+ ["END", [:klEND, :klEND ], :expr_end ],
907
+ ["BEGIN", [:klBEGIN, :klBEGIN ], :expr_end ],
908
+ ["while", [:kWHILE, :kWHILE_MOD ], :expr_beg ],
909
+ ["alias", [:kALIAS, :kALIAS ], :expr_fname ],
910
+ ].map { |args| KWtable.new(*args) }
911
+
912
+ # :startdoc:
913
+
914
+ WORDLIST = Hash[*wordlist.map { |o| [o.name, o] }.flatten] unless
915
+ defined? WORDLIST
916
+
917
+ def self.keyword str
918
+ WORDLIST[str]
919
+ end
920
+ end
921
+
922
+ class Environment
923
+ attr_reader :env, :dyn
924
+
925
+ def [] k
926
+ self.all[k]
927
+ end
928
+
929
+ def []= k, v
930
+ raise "no" if v == true
931
+ self.current[k] = v
932
+ end
933
+
934
+ def all
935
+ idx = @dyn.index(false) || 0
936
+ @env[0..idx].reverse.inject { |env, scope| env.merge scope }
937
+ end
938
+
939
+ def current
940
+ @env.first
941
+ end
942
+
943
+ def dynamic
944
+ idx = @dyn.index false
945
+ @env[0...idx].reverse.inject { |env, scope| env.merge scope } || {}
946
+ end
947
+
948
+ def dynamic?
949
+ @dyn[0] != false
950
+ end
951
+
952
+ def extend dyn = false
953
+ @dyn.unshift dyn
954
+ @env.unshift({})
955
+ @use.unshift({})
956
+ end
957
+
958
+ def initialize dyn = false
959
+ @dyn = []
960
+ @env = []
961
+ @use = []
962
+ self.reset
963
+ end
964
+
965
+ def reset
966
+ @dyn.clear
967
+ @env.clear
968
+ @use.clear
969
+ self.extend
970
+ end
971
+
972
+ def unextend
973
+ @dyn.shift
974
+ @env.shift
975
+ @use.shift
976
+ raise "You went too far unextending env" if @env.empty?
977
+ end
978
+
979
+ def use id
980
+ @env.each_with_index do |env, i|
981
+ if env[id] then
982
+ @use[i][id] = true
983
+ end
984
+ end
985
+ end
986
+
987
+ def used? id
988
+ idx = @dyn.index false # REFACTOR
989
+ u = @use[0...idx].reverse.inject { |env, scope| env.merge scope } || {}
990
+ u[id]
991
+ end
992
+ end
993
+
994
+ class StackState
995
+ attr_reader :stack
996
+
997
+ def initialize(name)
998
+ @name = name
999
+ @stack = [false]
1000
+ end
1001
+
1002
+ def inspect
1003
+ "StackState(#{@name}, #{@stack.inspect})"
1004
+ end
1005
+
1006
+ def is_in_state
1007
+ @stack.last
1008
+ end
1009
+
1010
+ def lexpop
1011
+ raise if @stack.size == 0
1012
+ a = @stack.pop
1013
+ b = @stack.pop
1014
+ @stack.push(a || b)
1015
+ end
1016
+
1017
+ def pop
1018
+ r = @stack.pop
1019
+ @stack.push false if @stack.size == 0
1020
+ r
1021
+ end
1022
+
1023
+ def push val
1024
+ @stack.push val
1025
+ end
1026
+ end
1027
+ end
1028
+
1029
+ class Ruby19Parser < Racc::Parser
1030
+ include RubyParserStuff
1031
+ end
1032
+
1033
+ class Ruby18Parser < Racc::Parser
1034
+ include RubyParserStuff
1035
+ end
1036
+
1037
+ class RubyParser < Ruby18Parser
1038
+ def initialize
1039
+ super
1040
+ warn "WA\RNING: Deprecated: RubyParser. Use Ruby18Parser or Ruby19Parser"
1041
+ warn " from #{caller.first}"
1042
+ end
1043
+ end
1044
+
1045
+ ############################################################
1046
+ # HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK
1047
+
1048
+ class Symbol
1049
+ def is_argument # TODO: phase this out
1050
+ return self == :expr_arg || self == :expr_cmdarg
1051
+ end
1052
+ end
1053
+
1054
+ class Sexp
1055
+ attr_writer :paren
1056
+
1057
+ def paren
1058
+ @paren ||= false
1059
+ end
1060
+
1061
+ def value
1062
+ raise "multi item sexp" if size > 2
1063
+ last
1064
+ end
1065
+
1066
+ def to_sym
1067
+ self.value.to_sym
1068
+ end
1069
+
1070
+ alias :node_type :sexp_type
1071
+ alias :values :sexp_body # TODO: retire
1072
+ end
1073
+
1074
+ # END HACK
1075
+ ############################################################