rb-ruby_parser 2.0.4.1

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