ruby_parser 1.0.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of ruby_parser might be problematic. Click here for more details.

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