ruby_parser 1.0.0 → 2.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.

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
+ ############################################################