brakeman 1.0.rc1 → 1.0.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.
@@ -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
+ ############################################################