fabiokung-ruby_parser 2.0.2

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,1031 @@
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.2'
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, bol), 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.line -= 1 if bol
514
+ result.comments = self.comments.pop
515
+ result
516
+ end
517
+
518
+ def new_defs val
519
+ recv, name, args, body = val[1], val[4], val[6], val[7]
520
+
521
+ body ||= s(:block)
522
+ body = s(:block, body) unless body.first == :block
523
+
524
+ result = s(:defs, recv, name.to_sym, args, s(:scope, body))
525
+ result.line = recv.line
526
+ result.comments = self.comments.pop
527
+ result
528
+ end
529
+
530
+ def new_for expr, var, body
531
+ result = s(:for, expr, var).line(var.line)
532
+ result << body if body
533
+ result
534
+ end
535
+
536
+ def new_if c, t, f
537
+ l = [c.line, t && t.line, f && f.line].compact.min
538
+ c = cond c
539
+ c, t, f = c.last, f, t if c[0] == :not
540
+ s(:if, c, t, f).line(l)
541
+ end
542
+
543
+ def new_iter call, args, body
544
+ result = s(:iter)
545
+ result << call if call
546
+ result << args
547
+ result << body if body
548
+ result
549
+ end
550
+
551
+ def new_masgn lhs, rhs, wrap = false
552
+ rhs = value_expr(rhs)
553
+ rhs = lhs[1] ? s(:to_ary, rhs) : s(:array, rhs) if wrap
554
+
555
+ lhs.delete_at 1 if lhs[1].nil?
556
+ lhs << rhs
557
+
558
+ lhs
559
+ end
560
+
561
+ def new_module val
562
+ line, path, body = val[1], val[2], val[4]
563
+ body = s(:scope, body).compact
564
+ result = s(:module, path, body)
565
+ result.line = line
566
+ result.comments = self.comments.pop
567
+ result
568
+ end
569
+
570
+ def new_op_asgn val
571
+ lhs, asgn_op, arg = val[0], val[1].to_sym, val[2]
572
+ name = lhs.value
573
+ arg = remove_begin(arg)
574
+ result = case asgn_op # REFACTOR
575
+ when :"||" then
576
+ lhs << arg
577
+ s(:op_asgn_or, self.gettable(name), lhs)
578
+ when :"&&" then
579
+ lhs << arg
580
+ s(:op_asgn_and, self.gettable(name), lhs)
581
+ else
582
+ # TODO: why [2] ?
583
+ lhs[2] = new_call(self.gettable(name), asgn_op,
584
+ s(:arglist, arg))
585
+ lhs
586
+ end
587
+ result.line = lhs.line
588
+ result
589
+ end
590
+
591
+ def new_regexp val
592
+ node = val[1] || s(:str, '')
593
+ options = val[2]
594
+
595
+ o, k = 0, nil
596
+ options.split(//).each do |c| # FIX: this has a better home
597
+ v = {
598
+ 'x' => Regexp::EXTENDED,
599
+ 'i' => Regexp::IGNORECASE,
600
+ 'm' => Regexp::MULTILINE,
601
+ 'o' => Regexp::ONCE,
602
+ 'n' => Regexp::ENC_NONE,
603
+ 'e' => Regexp::ENC_EUC,
604
+ 's' => Regexp::ENC_SJIS,
605
+ 'u' => Regexp::ENC_UTF8,
606
+ }[c]
607
+ raise "unknown regexp option: #{c}" unless v
608
+ o += v
609
+ k = c if c =~ /[esu]/
610
+ end
611
+
612
+ case node[0]
613
+ when :str then
614
+ node[0] = :lit
615
+ node[1] = if k then
616
+ Regexp.new(node[1], o, k)
617
+ else
618
+ Regexp.new(node[1], o)
619
+ end
620
+ when :dstr then
621
+ if options =~ /o/ then
622
+ node[0] = :dregx_once
623
+ else
624
+ node[0] = :dregx
625
+ end
626
+ node << o if o and o != 0
627
+ else
628
+ node = s(:dregx, '', node);
629
+ node[0] = :dregx_once if options =~ /o/
630
+ node << o if o and o != 0
631
+ end
632
+
633
+ node
634
+ end
635
+
636
+ def new_sclass val
637
+ recv, in_def, in_single, body = val[3], val[4], val[6], val[7]
638
+ scope = s(:scope, body).compact
639
+ result = s(:sclass, recv, scope)
640
+ result.line = val[2]
641
+ self.in_def = in_def
642
+ self.in_single = in_single
643
+ result
644
+ end
645
+
646
+ def new_super args
647
+ if args && args.node_type == :block_pass then
648
+ s(:super, args)
649
+ else
650
+ args ||= s(:arglist)
651
+ s(:super, *args[1..-1])
652
+ end
653
+ end
654
+
655
+ def new_undef n, m = nil
656
+ if m then
657
+ block_append(n, s(:undef, m))
658
+ else
659
+ s(:undef, n)
660
+ end
661
+ end
662
+
663
+ def new_until block, expr, pre
664
+ expr = (expr.first == :not ? expr.last : s(:not, expr)).line(expr.line)
665
+ new_while block, expr, pre
666
+ end
667
+
668
+ def new_while block, expr, pre
669
+ line = [block && block.line, expr.line].compact.min
670
+ block, pre = block.last, false if block && block[0] == :begin
671
+
672
+ expr = cond expr
673
+ result = if expr.first == :not then
674
+ s(:until, expr.last, block, pre)
675
+ else
676
+ s(:while, expr, block, pre)
677
+ end
678
+
679
+ result.line = line
680
+ result
681
+ end
682
+
683
+ def new_xstring str
684
+ if str then
685
+ case str[0]
686
+ when :str
687
+ str[0] = :xstr
688
+ when :dstr
689
+ str[0] = :dxstr
690
+ else
691
+ str = s(:dxstr, '', str)
692
+ end
693
+ str
694
+ else
695
+ s(:xstr, '')
696
+ end
697
+ end
698
+
699
+ def new_yield args = nil
700
+ # TODO: raise args.inspect unless [:arglist].include? args.first # HACK
701
+ raise SyntaxError, "Block argument should not be given." if
702
+ args && args.node_type == :block_pass
703
+
704
+ args ||= s(:arglist)
705
+
706
+ # TODO: I can prolly clean this up
707
+ args[0] = :arglist if args.first == :array
708
+ args = s(:arglist, args) unless args.first == :arglist
709
+
710
+ return s(:yield, *args[1..-1])
711
+ end
712
+
713
+ def next_token
714
+ if self.lexer.advance then
715
+ return self.lexer.token, self.lexer.yacc_value
716
+ else
717
+ return [false, '$end']
718
+ end
719
+ end
720
+
721
+ def node_assign(lhs, rhs) # TODO: rename new_assign
722
+ return nil unless lhs
723
+
724
+ rhs = value_expr rhs
725
+
726
+ case lhs[0]
727
+ when :gasgn, :iasgn, :lasgn, :dasgn, :dasgn_curr,
728
+ :masgn, :cdecl, :cvdecl, :cvasgn then
729
+ lhs << rhs
730
+ when :attrasgn, :call then
731
+ args = lhs.pop unless Symbol === lhs.last
732
+ lhs << arg_add(args, rhs)
733
+ when :const then
734
+ lhs[0] = :cdecl
735
+ lhs << rhs
736
+ else
737
+ raise "unknown lhs #{lhs.inspect}"
738
+ end
739
+
740
+ lhs
741
+ end
742
+
743
+ def process(str, file = "(string)")
744
+ raise "bad val: #{str.inspect}" unless String === str
745
+
746
+ self.file = file
747
+ self.lexer.src = str
748
+
749
+ @yydebug = ENV.has_key? 'DEBUG'
750
+
751
+ do_parse
752
+ end
753
+ alias :parse :process
754
+
755
+ def remove_begin node
756
+ oldnode = node
757
+ if node and :begin == node[0] and node.size == 2 then
758
+ node = node[-1]
759
+ node.line = oldnode.line
760
+ end
761
+ node
762
+ end
763
+
764
+ def reset
765
+ lexer.reset
766
+ self.in_def = false
767
+ self.in_single = 0
768
+ self.env.reset
769
+ self.comments.clear
770
+ end
771
+
772
+ def ret_args node
773
+ if node then
774
+ raise SyntaxError, "block argument should not be given" if
775
+ node[0] == :block_pass
776
+
777
+ node = node.last if node[0] == :array && node.size == 2
778
+ # HACK matz wraps ONE of the FOUR splats in a newline to
779
+ # distinguish. I use paren for now. ugh
780
+ node = s(:svalue, node) if node[0] == :splat and not node.paren
781
+ node[0] = :svalue if node[0] == :arglist && node[1][0] == :splat
782
+ end
783
+
784
+ node
785
+ end
786
+
787
+ def s(*args)
788
+ result = Sexp.new(*args)
789
+ result.line ||= lexer.lineno if lexer.src # otherwise...
790
+ result.file = self.file
791
+ result
792
+ end
793
+
794
+ def value_expr oldnode # HACK
795
+ node = remove_begin oldnode
796
+ node.line = oldnode.line if oldnode
797
+ node[2] = value_expr(node[2]) if node and node[0] == :if
798
+ node
799
+ end
800
+
801
+ def void_stmts node
802
+ return nil unless node
803
+ return node unless node[0] == :block
804
+
805
+ node[1..-1] = node[1..-1].map { |n| remove_begin(n) }
806
+ node
807
+ end
808
+
809
+ def warning s
810
+ # do nothing for now
811
+ end
812
+
813
+ alias :old_yyerror :yyerror
814
+ def yyerror msg
815
+ # for now do nothing with the msg
816
+ old_yyerror
817
+ end
818
+ end
819
+
820
+ class Keyword
821
+ class KWtable
822
+ attr_accessor :name, :state, :id0, :id1
823
+ def initialize(name, id=[], state=nil)
824
+ @name = name
825
+ @id0, @id1 = id
826
+ @state = state
827
+ end
828
+ end
829
+
830
+ ##
831
+ # :stopdoc:
832
+ #
833
+ # :expr_beg = ignore newline, +/- is a sign.
834
+ # :expr_end = newline significant, +/- is a operator.
835
+ # :expr_arg = newline significant, +/- is a operator.
836
+ # :expr_cmdarg = newline significant, +/- is a operator.
837
+ # :expr_endarg = newline significant, +/- is a operator.
838
+ # :expr_mid = newline significant, +/- is a operator.
839
+ # :expr_fname = ignore newline, no reserved words.
840
+ # :expr_dot = right after . or ::, no reserved words.
841
+ # :expr_class = immediate after class, no here document.
842
+
843
+ wordlist = [
844
+ ["end", [:kEND, :kEND ], :expr_end ],
845
+ ["else", [:kELSE, :kELSE ], :expr_beg ],
846
+ ["case", [:kCASE, :kCASE ], :expr_beg ],
847
+ ["ensure", [:kENSURE, :kENSURE ], :expr_beg ],
848
+ ["module", [:kMODULE, :kMODULE ], :expr_beg ],
849
+ ["elsif", [:kELSIF, :kELSIF ], :expr_beg ],
850
+ ["def", [:kDEF, :kDEF ], :expr_fname ],
851
+ ["rescue", [:kRESCUE, :kRESCUE_MOD ], :expr_mid ],
852
+ ["not", [:kNOT, :kNOT ], :expr_beg ],
853
+ ["then", [:kTHEN, :kTHEN ], :expr_beg ],
854
+ ["yield", [:kYIELD, :kYIELD ], :expr_arg ],
855
+ ["for", [:kFOR, :kFOR ], :expr_beg ],
856
+ ["self", [:kSELF, :kSELF ], :expr_end ],
857
+ ["false", [:kFALSE, :kFALSE ], :expr_end ],
858
+ ["retry", [:kRETRY, :kRETRY ], :expr_end ],
859
+ ["return", [:kRETURN, :kRETURN ], :expr_mid ],
860
+ ["true", [:kTRUE, :kTRUE ], :expr_end ],
861
+ ["if", [:kIF, :kIF_MOD ], :expr_beg ],
862
+ ["defined?", [:kDEFINED, :kDEFINED ], :expr_arg ],
863
+ ["super", [:kSUPER, :kSUPER ], :expr_arg ],
864
+ ["undef", [:kUNDEF, :kUNDEF ], :expr_fname ],
865
+ ["break", [:kBREAK, :kBREAK ], :expr_mid ],
866
+ ["in", [:kIN, :kIN ], :expr_beg ],
867
+ ["do", [:kDO, :kDO ], :expr_beg ],
868
+ ["nil", [:kNIL, :kNIL ], :expr_end ],
869
+ ["until", [:kUNTIL, :kUNTIL_MOD ], :expr_beg ],
870
+ ["unless", [:kUNLESS, :kUNLESS_MOD ], :expr_beg ],
871
+ ["or", [:kOR, :kOR ], :expr_beg ],
872
+ ["next", [:kNEXT, :kNEXT ], :expr_mid ],
873
+ ["when", [:kWHEN, :kWHEN ], :expr_beg ],
874
+ ["redo", [:kREDO, :kREDO ], :expr_end ],
875
+ ["and", [:kAND, :kAND ], :expr_beg ],
876
+ ["begin", [:kBEGIN, :kBEGIN ], :expr_beg ],
877
+ ["__LINE__", [:k__LINE__, :k__LINE__ ], :expr_end ],
878
+ ["class", [:kCLASS, :kCLASS ], :expr_class ],
879
+ ["__FILE__", [:k__FILE__, :k__FILE__ ], :expr_end ],
880
+ ["END", [:klEND, :klEND ], :expr_end ],
881
+ ["BEGIN", [:klBEGIN, :klBEGIN ], :expr_end ],
882
+ ["while", [:kWHILE, :kWHILE_MOD ], :expr_beg ],
883
+ ["alias", [:kALIAS, :kALIAS ], :expr_fname ],
884
+ ].map { |args| KWtable.new(*args) }
885
+
886
+ # :startdoc:
887
+
888
+ WORDLIST = Hash[*wordlist.map { |o| [o.name, o] }.flatten]
889
+
890
+ def self.keyword str
891
+ WORDLIST[str]
892
+ end
893
+ end
894
+
895
+ class Environment
896
+ attr_reader :env, :dyn
897
+
898
+ def [] k
899
+ self.all[k]
900
+ end
901
+
902
+ def []= k, v
903
+ raise "no" if v == true
904
+ self.current[k] = v
905
+ end
906
+
907
+ def all
908
+ idx = @dyn.index false
909
+ @env[0..idx].reverse.inject { |env, scope| env.merge scope }
910
+ end
911
+
912
+ def current
913
+ @env.first
914
+ end
915
+
916
+ def dynamic
917
+ idx = @dyn.index false
918
+ @env[0...idx].reverse.inject { |env, scope| env.merge scope } || {}
919
+ end
920
+
921
+ def dynamic?
922
+ @dyn[0] != false
923
+ end
924
+
925
+ def extend dyn = false
926
+ @dyn.unshift dyn
927
+ @env.unshift({})
928
+ @use.unshift({})
929
+ end
930
+
931
+ def initialize dyn = false
932
+ @dyn = []
933
+ @env = []
934
+ @use = []
935
+ self.reset
936
+ end
937
+
938
+ def reset
939
+ @dyn.clear
940
+ @env.clear
941
+ @use.clear
942
+ self.extend
943
+ end
944
+
945
+ def unextend
946
+ @dyn.shift
947
+ @env.shift
948
+ @use.shift
949
+ raise "You went too far unextending env" if @env.empty?
950
+ end
951
+
952
+ def use id
953
+ @env.each_with_index do |env, i|
954
+ if env[id] then
955
+ @use[i][id] = true
956
+ end
957
+ end
958
+ end
959
+
960
+ def used? id
961
+ idx = @dyn.index false # REFACTOR
962
+ u = @use[0...idx].reverse.inject { |env, scope| env.merge scope } || {}
963
+ u[id]
964
+ end
965
+ end
966
+
967
+ class StackState
968
+ attr_reader :stack
969
+
970
+ def initialize(name)
971
+ @name = name
972
+ @stack = [false]
973
+ end
974
+
975
+ def inspect
976
+ "StackState(#{@name}, #{@stack.inspect})"
977
+ end
978
+
979
+ def is_in_state
980
+ @stack.last
981
+ end
982
+
983
+ def lexpop
984
+ raise if @stack.size == 0
985
+ a = @stack.pop
986
+ b = @stack.pop
987
+ @stack.push(a || b)
988
+ end
989
+
990
+ def pop
991
+ r = @stack.pop
992
+ @stack.push false if @stack.size == 0
993
+ r
994
+ end
995
+
996
+ def push val
997
+ @stack.push val
998
+ end
999
+ end
1000
+
1001
+ ############################################################
1002
+ # HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK
1003
+
1004
+ class Symbol
1005
+ def is_argument # TODO: phase this out
1006
+ return self == :expr_arg || self == :expr_cmdarg
1007
+ end
1008
+ end
1009
+
1010
+ class Sexp
1011
+ attr_writer :paren
1012
+
1013
+ def paren
1014
+ @paren ||= false
1015
+ end
1016
+
1017
+ def value
1018
+ raise "multi item sexp" if size > 2
1019
+ last
1020
+ end
1021
+
1022
+ def to_sym
1023
+ self.value.to_sym
1024
+ end
1025
+
1026
+ alias :node_type :sexp_type
1027
+ alias :values :sexp_body # TODO: retire
1028
+ end
1029
+
1030
+ # END HACK
1031
+ ############################################################