redparse 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,3808 @@
1
+ =begin
2
+ redparse - a ruby parser written in ruby
3
+ Copyright (C) 2008 Caleb Clausen
4
+
5
+
6
+ This program is free software: you can redistribute it and/or modify
7
+ it under the terms of the GNU Lesser General Public License as published by
8
+ the Free Software Foundation, either version 3 of the License, or
9
+ (at your option) any later version.
10
+
11
+ This program is distributed in the hope that it will be useful,
12
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ GNU Lesser General Public License for more details.
15
+
16
+ You should have received a copy of the GNU Lesser General Public License
17
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
18
+ =end
19
+
20
+ require 'rubygems'
21
+ require 'tempfile'
22
+ require 'pp'
23
+ require "rubylexer"
24
+ require "reg"
25
+
26
+
27
+
28
+
29
+
30
+ class RedParse
31
+ # module Nodes
32
+ #import token classes from rubylexer
33
+ RubyLexer::constants.each{|k|
34
+ t=RubyLexer::const_get(k)
35
+ self::const_set k,t if Module===t and RubyLexer::Token>=t
36
+ }
37
+
38
+ module SimpleToLisp
39
+ def to_lisp; to_s end
40
+ end
41
+
42
+ [NumberToken, SymbolToken, VarNameToken, MethNameToken].each{|tokclass|
43
+ tokclass.send :include, SimpleToLisp
44
+ }
45
+
46
+ module FlattenedIvars
47
+ def flattened_ivars
48
+ result=[]
49
+ instance_variables.sort.each{|iv|
50
+ break if iv=="@data"
51
+ result.push iv, instance_variable_get(iv)
52
+ }
53
+ return result
54
+ end
55
+
56
+ def flattened_ivars_equal?(other)
57
+ self.class == other.class and
58
+ flattened_ivars == other.flattened_ivars
59
+ end
60
+ end
61
+
62
+ class Token
63
+ def image; "#{inspect}" end
64
+
65
+ def to_parsetree(*options)
66
+ options.include? :newlines and Thread.current[:$RedParse_parsetree_newlines]=true
67
+ options.include? :quirks and Thread.current[:$RedParse_parsetree_quirks]=true
68
+
69
+ result=[parsetree]
70
+
71
+ Thread.current[:$RedParse_parsetree_newlines]=false
72
+ Thread.current[:$RedParse_parsetree_quirks]=false
73
+
74
+ result=[] if result==[[]]
75
+
76
+ return result
77
+ end
78
+ def lvalue?; nil end
79
+ def data; [self] end
80
+ def unary; false end
81
+ def rescue_parsetree; parsetree end
82
+ def begin_parsetree; parsetree end
83
+
84
+ end
85
+
86
+ class KeywordToken
87
+ def not_real!
88
+ @not_real=true
89
+ end
90
+
91
+ def not_real?
92
+ @not_real if defined? @not_real
93
+ end
94
+
95
+ alias image ident
96
+ end
97
+
98
+ class VarNameToken
99
+ include FlattenedIvars
100
+ alias image ident
101
+
102
+ alias == flattened_ivars_equal?
103
+
104
+ def parsetree
105
+ type=case ident[0]
106
+ when ?$:
107
+ case ident[1]
108
+ when ?1..?9: return [:nth_ref,ident[1..-1].to_i]
109
+ when ?&,?+,?`,?': return [:back_ref,ident[1].chr.to_sym] #`
110
+ else :gvar
111
+ end
112
+ when ?@:
113
+ if ident[1]==?@
114
+ :cvar
115
+ else
116
+ :ivar
117
+ end
118
+ when ?A..?Z: :const
119
+ else
120
+ case lvar_type
121
+ when :local; :lvar
122
+ when :block; :dvar
123
+ when :current; :dvar#_curr
124
+ else fail
125
+ end
126
+ end
127
+ return [type,ident.to_sym]
128
+ end
129
+
130
+ def varname2assigntype
131
+ case ident[0]
132
+ when ?$: :gasgn
133
+ when ?@:
134
+ if ident[1]!=?@: :iasgn
135
+ elsif in_def: :cvasgn
136
+ else :cvdecl
137
+ end
138
+ when ?A..?Z: :cdecl
139
+ else :lasgn
140
+ case lvar_type
141
+ when :local; :lasgn
142
+ when :block; :dasgn
143
+ when :current; :dasgn_curr
144
+ else fail
145
+ end
146
+ end
147
+ end
148
+
149
+ def lvalue_parsetree
150
+ [varname2assigntype, ident.to_sym]
151
+ end
152
+
153
+ def lvalue?
154
+ return @lvalue if defined? @lvalue
155
+ @lvalue=true
156
+ end
157
+
158
+ def all_current_lvars
159
+ lvar_type==:current ? [ident] : []
160
+ end
161
+
162
+ attr_accessor :line,:lvalue
163
+
164
+ def dup
165
+ result=super
166
+ result.ident=@ident.dup
167
+ return result
168
+ end
169
+
170
+ public :remove_instance_variable
171
+
172
+ def unparse o; ident end
173
+ alias lhs_unparse unparse
174
+
175
+ def delete_extraneous_ivars!
176
+ huh
177
+ end
178
+
179
+ def walk
180
+ yield nil,nil,nil,self
181
+ end
182
+ end
183
+
184
+ class StringToken
185
+ attr :char
186
+ end
187
+
188
+ class HerePlaceholderToken
189
+ attr_accessor :node
190
+ attr :string
191
+ end
192
+
193
+ class Node<Array
194
+ include FlattenedIvars
195
+ def initialize(*data)
196
+ replace data
197
+ end
198
+
199
+ def flattened_ivars
200
+ result=super
201
+ result.each_with_index{|x,i|
202
+ if i&1==0 and x!="@data"
203
+ result[i,2]=[]
204
+ end
205
+ }
206
+ result
207
+ end
208
+
209
+
210
+ def ==(other)
211
+ super and flattened_ivars_equal?(other)
212
+ end
213
+
214
+ def image; "(#{inspect})" end
215
+
216
+ @@data_warned=nil
217
+ def data
218
+ unless @@data_warned
219
+ warn "using obsolete Node#data from #{caller.first}"
220
+ @@data_warned=true
221
+ end
222
+ Array.new(self)
223
+ end
224
+ alias unwrap data
225
+
226
+ attr_accessor :line
227
+ attr_accessor :errors
228
+
229
+ def self.[](*data)
230
+ options=data.pop if Hash===data.last
231
+ result=allocate
232
+ result.instance_eval{
233
+ replace data
234
+ options.each_pair{|name,val|
235
+ instance_variable_set name,val
236
+ } if options
237
+ }
238
+ return result
239
+ end
240
+
241
+ def inspect
242
+ ivarnames=instance_variables-["@data"]
243
+ ivars=ivarnames.map{|ivarname|
244
+ ":"+ivarname+"=>"+instance_variable_get(ivarname).inspect
245
+ }.join(', ')
246
+ bare=super
247
+ bare.gsub!(/\]\Z/, ", {"+ivars+"}]") unless ivarnames.empty?
248
+ return self.class.name+bare
249
+ end
250
+
251
+ def pretty_print(q)
252
+ ivarnames=instance_variables-["@data"]
253
+ ivars={}
254
+ ivarnames.each{|ivarname|
255
+ ivars[ivarname.to_sym]=instance_variable_get(ivarname)
256
+ }
257
+ q.group(1, self.class.name+'[', ']') {
258
+ displaylist= ivars.empty? ? self : self+[ivars]
259
+ q.seplist(displaylist) {|v|
260
+ q.pp v
261
+ }
262
+ # q.text ', '
263
+ # q.pp_hash ivars
264
+ }
265
+ end
266
+
267
+ def self.param_names(*names)
268
+ accessors=[]
269
+ namelist=[]
270
+ namelist2=[]
271
+ names.each{|name|
272
+ name=name.to_s
273
+ last=name[-1]
274
+ name.chomp! '!' and name << ?_
275
+ namelist2 << name
276
+ unless last==?_
277
+ accessors << "def #{name.chomp('_')}; self[#{namelist.size}] end\n"
278
+ accessors << "def #{name.chomp('_')}=(newval); self[#{namelist.size}]=newval end\n"
279
+ namelist << name
280
+ end
281
+ }
282
+ init="
283
+ def initialize(#{namelist2.join(', ')})
284
+ replace [#{namelist.size==1 ?
285
+ namelist.first :
286
+ namelist.join(', ')
287
+ }]
288
+ end
289
+ alias init_data initialize
290
+ "
291
+
292
+ code= "class ::#{self}\n"+init+accessors.to_s+"\nend\n"
293
+ if defined? DEBUGGER__ or defined? Debugger
294
+ Tempfile.open("param_name_defs"){|f|
295
+ f.write code
296
+ f.flush
297
+ load f.path
298
+ }
299
+ else
300
+ eval code
301
+ end
302
+ end
303
+
304
+ def lhs_unparse o; unparse(o) end
305
+
306
+ def to_parsetree(*options)
307
+ options.include? :newlines and Thread.current[:$RedParse_parsetree_newlines]=true
308
+ options.include? :quirks and Thread.current[:$RedParse_parsetree_quirks]=true
309
+
310
+ result=[parsetree]
311
+
312
+ Thread.current[:$RedParse_parsetree_newlines]=false
313
+ Thread.current[:$RedParse_parsetree_quirks]=false
314
+
315
+ result=[] if result==[[]]
316
+
317
+ return result
318
+ end
319
+ def parsetree
320
+ "wrong(#{inspect})"
321
+ end
322
+
323
+ def rescue_parsetree; parsetree end
324
+ def begin_parsetree; parsetree end
325
+
326
+ def parsetrees list
327
+ !list.empty? and list.map{|node| node.parsetree}
328
+ end
329
+
330
+ def negate(condition,offset=nil)
331
+ if UnOpNode===condition and condition.op.ident[/^(!|not)$/]
332
+ condition.val
333
+ else
334
+ UnOpNode.new(KeywordToken.new("not",offset),condition)
335
+ end
336
+ end
337
+
338
+ #callback takes four parameters:
339
+ #parent of node currently being walked, index and subindex within
340
+ #that parent, and finally the actual node being walked.
341
+ def walk(parent=nil,index=nil,subindex=nil,&callback)
342
+ callback[ parent,index,subindex,self ] and
343
+ each_with_index{|datum,i|
344
+ case datum
345
+ when Node: datum.walk(self,i,&callback)
346
+ when Array:
347
+ datum.each_with_index{|x,j|
348
+ Node===x ? x.walk(self,i,j,&callback) : callback[self,i,j,x]
349
+ }
350
+ else callback[self,i,nil,datum]
351
+ end
352
+ }
353
+ end
354
+
355
+ def linerange
356
+ min=9999999999999999999999999999999999999999999999999999
357
+ max=0
358
+ walk{|parent,i,subi,node|
359
+ if node.respond_to? :line and line=node.line
360
+ min=[min,line].min
361
+ max=[max,line].max
362
+ end
363
+ }
364
+ return min..max
365
+ end
366
+
367
+ def fixup_multiple_assignments!
368
+ result=self
369
+ walk{|parent,i,subi,node|
370
+ if CommaOpNode===node
371
+ #there should be an assignnode within this node... find it
372
+ j=nil
373
+ list=Array.new(node)
374
+ assignnode=nil
375
+ list.each_with_index{|assignnode,jj|
376
+ AssignNode===assignnode and break(j=jj)
377
+ }
378
+ fail "CommaOpNode without any assignment in final parse tree" unless j
379
+
380
+ #re-hang the current node with = at the top
381
+ lhs=list[0...j]<<list[j].left
382
+ rhs=list[j+1..-1].unshift list[j].right
383
+ if lhs.size==1 and MultiAssign===lhs.first
384
+ lhs=lhs.first
385
+ else
386
+ lhs=MultiAssign.new(lhs)
387
+ end
388
+ node=AssignNode.new(lhs, assignnode.op, rhs)
389
+
390
+ #graft the new node back onto the old tree
391
+ if parent
392
+ if subi
393
+ parent[i][subi]=node
394
+ else
395
+ parent[i]=node
396
+ end
397
+ else #replacement at top level
398
+ result=node
399
+ end
400
+
401
+ #re-scan newly made node, since we tell caller not to scan our children
402
+ node.fixup_multiple_assignments!
403
+
404
+ false #skip (your old view of) my children, please
405
+ else
406
+ true
407
+ end
408
+ }
409
+
410
+ return result
411
+
412
+ end
413
+
414
+ def prohibit_fixup x
415
+ case x
416
+ when UnaryStarNode: true
417
+ # when ParenedNode: x.size>1
418
+ when CallSiteNode: x.params and !x.real_parens
419
+ else false
420
+ end
421
+ end
422
+
423
+ def fixup_rescue_assignments!
424
+ result=self
425
+ walk{|parent,i,subi,node|
426
+ #if a rescue op with a single assignment on the lhs
427
+ if ParenedNode===node and node.op? and assign=node.first and #ick
428
+ AssignNode===assign and assign.op.ident=="=" and
429
+ !(assign.multi? or
430
+ prohibit_fixup assign.right)
431
+
432
+
433
+ #re-hang the node with = at the top instead of rescue
434
+ node=AssignNode.new(assign.left, assign.op,
435
+ ParenedNode.new(assign.right,nil,node[1][0].action)
436
+ )
437
+
438
+ #graft the new node back onto the old tree
439
+ if parent
440
+ if subi
441
+ parent[i][subi]=node
442
+ else
443
+ parent[i]=node
444
+ end
445
+ else #replacement at top level
446
+ result=node
447
+ end
448
+
449
+ #re-scan newly made node, since we tell caller not to scan our children
450
+ node.fixup_rescue_assignments!
451
+
452
+ false #skip (your old view of) my children, please
453
+ else
454
+ true
455
+ end
456
+ }
457
+ return result
458
+ end
459
+
460
+ def lvars_defined_in
461
+ result=[]
462
+ walk {|parent,i,subi,node|
463
+ case node
464
+ when MethodNode,ClassNode,ModuleNode,MetaClassNode: false
465
+ when CallSiteNode
466
+ Node===node.receiver and
467
+ result.concat node.receiver.lvars_defined_in
468
+ node.args.each{|arg|
469
+ result.concat arg.lvars_defined_in if Node===arg
470
+ } if node.args
471
+ false
472
+ when AssignNode
473
+ lvalue=node.left
474
+ lvalue.respond_to? :all_current_lvars and
475
+ result.concat lvalue.all_current_lvars
476
+ true
477
+ when ForNode:
478
+ lvalue=node.for
479
+ lvalue.respond_to? :all_current_lvars and
480
+ result.concat lvalue.all_current_lvars
481
+ true
482
+ when ParenedNode:
483
+ if node.size>1
484
+ rescues=node[1]
485
+ rescues.each{|resc|
486
+ name=resc.varname
487
+ name and result.push name.ident
488
+ }
489
+ end
490
+ true
491
+ else true
492
+ end
493
+ }
494
+
495
+ result.uniq!
496
+ return result
497
+ end
498
+
499
+ def unary; false end
500
+ def lvalue?; false end
501
+
502
+ def deep_copy transform={},&override
503
+ handler=proc{|child|
504
+ if transform.has_key? child.__id__
505
+ transform[child.__id__]
506
+ else
507
+ case child
508
+ when Node:
509
+ override&&override[child] or
510
+ child.deep_copy(transform,&override)
511
+ when Array:
512
+ child.map(&handler)
513
+ when Integer,Symbol,Float,nil,false,true,Module:
514
+ child
515
+ else
516
+ child.dup
517
+ end
518
+ end
519
+ }
520
+
521
+ newdata=map(&handler)
522
+
523
+ h={}
524
+ result_module=nil
525
+ instance_variables.each{|iv|
526
+ unless iv=="@data"
527
+ val=instance_variable_get(iv)
528
+ h[iv]=handler[val]
529
+ result_module=val if iv=="@module" #hacky
530
+ end
531
+ }
532
+ result= self.class[*newdata << h]
533
+ result.extend result_module if result_module
534
+ return result
535
+ end
536
+
537
+ def delete_extraneous_ivars!
538
+ walk{|parent,i,subi,node|
539
+ case node
540
+ when Node,VarNameToken
541
+ node.remove_instance_variable :@offset rescue nil
542
+ node.remove_instance_variable :@loopword_offset rescue nil
543
+ node.remove_instance_variable :@line rescue nil
544
+ if node.respond_to? :lvalue
545
+ node.lvalue or
546
+ node.remove_instance_variable :@lvalue rescue nil
547
+ end
548
+ when Token
549
+ print "#{node.inspect} in "; pp parent
550
+ fail "no tokens should be present in final parse tree (maybe except VarNameToken, ick)"
551
+ end
552
+ true
553
+ }
554
+ return self
555
+ end
556
+
557
+ public :remove_instance_variable
558
+
559
+ #convert to a Reg::Array expression. subnodes are also converted.
560
+ #if any matchers are present in the tree, they will be included
561
+ #directly into the enclosing Node's matcher.
562
+ #this can be a nice way to turn a (possibly deeply nested) node
563
+ #tree into a matcher.
564
+ #note: anything stored in instance variables is ignored in the
565
+ #matcher.
566
+ def +@
567
+ node2matcher=proc{|n|
568
+ case n
569
+ when Node: +n
570
+ when Array: +[*n.map(&node2matcher)]
571
+ else n
572
+ end
573
+ }
574
+ return +[*map(&node2matcher)] & self.class
575
+ end
576
+
577
+ private
578
+ def quirks
579
+ Thread.current[:$RedParse_parsetree_quirks]
580
+ end
581
+
582
+ #turn a list (array) of arrays into a linked list, in which each array
583
+ #has a reference to the next in turn as its last element.
584
+ def linked_list(arrays)
585
+ 0.upto(arrays.size-2){|i| arrays[i]<<arrays[i+1] }
586
+ return arrays.first
587
+ end
588
+
589
+ def param_list_walk(param_list)
590
+ param_list or return
591
+ limit=param_list.size
592
+ i=0
593
+ normals=[]
594
+ lownormal=nil
595
+ handle_normals=proc{
596
+ yield '',normals,lownormal..i-1 if lownormal
597
+ lownormal=nil
598
+ normals.slice! 0..-1
599
+ }
600
+ while i<limit
601
+ case param=param_list[i]
602
+ when ArrowOpNode
603
+ handle_normals[]
604
+ low=i
605
+ i+=1 while ArrowOpNode===param_list[i]
606
+ high=i-1
607
+ yield '=>',param_list[low..high],low..high
608
+ when UnaryStarNode
609
+ handle_normals[]
610
+ yield '*',param,i
611
+ when UnOpNode&-{:op=>"&@"}
612
+ handle_normals[]
613
+ yield '&',param,i
614
+ else
615
+ lownormal=i unless lownormal
616
+ normals << param
617
+ end
618
+ i+=1
619
+ end
620
+ handle_normals[]
621
+ end
622
+
623
+ def param_list_parse(param_list)
624
+ output=[]
625
+ star=amp=nil
626
+ param_list_walk(param_list){|type,val,i|
627
+ case type
628
+ when '':
629
+ output.concat val.map{|param| param.rescue_parsetree}
630
+ when '=>':
631
+ output.push HashLiteralNode.new(nil,val,nil).parsetree
632
+ when '*': star=val.parsetree
633
+ when '&': amp=val.parsetree
634
+ end
635
+ }
636
+ return output,star,amp
637
+ end
638
+ end
639
+
640
+ class ValueNode<Node
641
+ def lvalue?; nil end
642
+ end
643
+
644
+ #forward decls
645
+ module ArrowOpNode; end
646
+ module RangeNode; end
647
+ module LogicalNode; end
648
+ module WhileOpNode; end
649
+ module UntilOpNode; end
650
+ module IfOpNode; end
651
+ module UnlessOpNode; end
652
+ module OpNode; end
653
+ module NotEqualNode; end
654
+ module MatchNode; end
655
+ module NotMatchNode; end
656
+
657
+ OP2MIXIN={
658
+ "=>"=>ArrowOpNode,
659
+ ".."=>RangeNode,
660
+ "..."=>RangeNode,
661
+ "&&"=>LogicalNode,
662
+ "||"=>LogicalNode,
663
+ "and"=>LogicalNode,
664
+ "or"=>LogicalNode,
665
+ "while"=>WhileOpNode,
666
+ "until"=>UntilOpNode,
667
+ "if"=>IfOpNode,
668
+ "unless"=>UnlessOpNode,
669
+ "!="=>NotEqualNode,
670
+ "!~"=>NotMatchNode,
671
+ "=~"=>MatchNode,
672
+ }
673
+
674
+ class RawOpNode<ValueNode
675
+ param_names(:left,:op,:right)
676
+ def initialize(left,op,right)
677
+ @offset=op.offset
678
+ op=op.ident
679
+ super(left,op,right)
680
+ Array((OP2MIXIN[op]||OpNode)).each{|mod|
681
+ extend(mod)
682
+ mod.instance_method(:initialize).bind(self).call(left,op,right)
683
+ }
684
+ end
685
+ def self.[](*args)
686
+ result=super
687
+ @module and extend @module
688
+ return result
689
+ end
690
+ def image; "(#{op})" end
691
+ def raw_unparse o
692
+ l=left.unparse(o)
693
+ l[/(~| \Z)/] and maybesp=" "
694
+ [l,op,maybesp,right.unparse(o)].to_s
695
+ end
696
+ end
697
+
698
+ module OpNode
699
+ def initialize(left,op,right)
700
+ #@negative_of="="+$1 if /^!([=~])$/===op
701
+ @module=OpNode
702
+ end
703
+ def to_lisp
704
+ "(#{op} #{left.to_lisp} #{right.to_lisp})"
705
+ end
706
+
707
+ def parsetree
708
+ [:call,
709
+ left.rescue_parsetree,
710
+ op.to_sym,
711
+ [:array, right.rescue_parsetree]
712
+ ]
713
+ end
714
+ alias opnode_parsetree parsetree
715
+
716
+ def unparse o
717
+ result=l=left.unparse(o)
718
+ result+=" " if /a-z_/i===op
719
+ result+=op
720
+ result+=" " if /a-z_/i===op or / \Z/===l
721
+ result+=right.unparse(o)
722
+ end
723
+
724
+ def unparse o; raw_unparse o end
725
+ end
726
+
727
+ module MatchNode
728
+ include OpNode
729
+
730
+ def parsetree
731
+ if StringNode===left and left.char=='/'
732
+ [:match2, left.parsetree, right.parsetree]
733
+ elsif StringNode===right and right.char=='/'
734
+ [:match3, right.parsetree, left.parsetree]
735
+ else
736
+ super
737
+ end
738
+ end
739
+ def op; "=~"; end
740
+ end
741
+
742
+ module NotEqualNode
743
+ include OpNode
744
+
745
+ def parsetree
746
+ result=opnode_parsetree
747
+ result[2]="=#{op[1..1]}".to_sym
748
+ result=[:not, result]
749
+ return result
750
+ end
751
+ def op; "!="; end
752
+ end
753
+
754
+ module NotMatchNode
755
+ include NotEqualNode
756
+
757
+ def parsetree
758
+ if StringNode===left and left.char=="/"
759
+ [:not, [:match2, left.parsetree, right.parsetree]]
760
+ elsif StringNode===right and right.char=="/"
761
+ [:not, [:match3, right.parsetree, left.parsetree]]
762
+ else
763
+ super
764
+ end
765
+ end
766
+
767
+ def op; "!~"; end
768
+ end
769
+
770
+ class ListOpNode<ValueNode #abstract
771
+ def initialize(val1,op,val2)
772
+ list=if self.class===val1
773
+ Array.new(val1)
774
+ else
775
+ [val1]
776
+ end
777
+ if self.class===val2
778
+ list.push *val2
779
+ elsif val2
780
+ list.push val2
781
+ end
782
+ super *list
783
+ end
784
+ end
785
+
786
+ class CommaOpNode<ListOpNode #not to appear in final tree
787
+ def image; '(,)' end
788
+ def to_lisp
789
+ "(#{map{|x| x.to_lisp}.join(" ")})"
790
+ end
791
+ def lvalue?
792
+ return @lvalue if defined? @lvalue
793
+ @lvalue=true
794
+ end
795
+ attr_accessor :lvalue
796
+ end
797
+
798
+ class LiteralNode<ValueNode; end
799
+ class StringNode<ValueNode; end
800
+ class StringCatNode < ValueNode; end
801
+ class NopNode<ValueNode; end
802
+ class VarLikeNode<ValueNode; end #nil,false,true,__FILE__,__LINE__,self
803
+
804
+ class SequenceNode<ListOpNode
805
+ def image; '(;)' end
806
+ def to_lisp
807
+ "#{map{|x| x.to_lisp}.join("\n")}"
808
+ end
809
+
810
+ def to_lisp_with_parens
811
+ "(#{to_lisp})"
812
+ end
813
+
814
+ LITFIX=LiteralNode&-{:val=>Fixnum}
815
+ LITRANGE=RangeNode&-{:left=>LITFIX,:right=>LITFIX}
816
+ LITSTR=StringNode&-{:size=>1,:char=>/^[^`\[{]$/}
817
+ #LITCAT=proc{|item| item.grep(~LITSTR).empty?}
818
+ #class<<LITCAT; alias === call; end
819
+ LITCAT=StringCatNode& item_that.grep(~LITSTR).empty? #+[LITSTR.+]
820
+ LITNODE=LiteralNode|NopNode|LITSTR|LITCAT|LITRANGE|(VarLikeNode&-{:name=>/^__/})
821
+ #VarNameToken| #why not this too?
822
+ def parsetree
823
+ data=compact
824
+ data.empty? and return
825
+ items=Array.new(data[0...-1])
826
+ if quirks
827
+ items.shift while LITNODE===items.first
828
+ else
829
+ items.reject!{|expr| LITNODE===expr }
830
+ end
831
+ items.map!{|expr| expr.rescue_parsetree}.push last.parsetree
832
+ # items=map{|expr| expr.parsetree}
833
+ items.reject!{|expr| []==expr }
834
+ if quirks
835
+ header=items.first
836
+ (items[0,1] = *header[1..-1]) if header and header.first==:block
837
+ else
838
+ items.size.pred.downto(0){|i|
839
+ header=items[i]
840
+ (items[i,1] = *header[1..-1]) if header and header.first==:block
841
+ }
842
+ end
843
+ if items.size>1
844
+ items.unshift :block
845
+ elsif items.size==1
846
+ items.first
847
+ else
848
+ items
849
+ end
850
+ end
851
+
852
+ def unparse o
853
+ map{|expr| expr.unparse(o)}.join("\n")
854
+ end
855
+ end
856
+
857
+ class StringCatNode < ValueNode
858
+ def initialize(strs,str1,str2)
859
+ if HereDocNode===strs
860
+ hd,strs,str=strs,str1,str2
861
+ strs.push str
862
+ else
863
+ strs.push str1,str2
864
+ end
865
+ strs.map!{|str| StringNode.new(str)}
866
+ strs.unshift hd if hd
867
+ super *strs
868
+ end
869
+ def parsetree
870
+ result=map{|str| str.parsetree}
871
+ sum=''
872
+ type=:str
873
+ tree=i=nil
874
+ result.each_with_index{|tree,i|
875
+ sum+=tree[1]
876
+ tree.first==:str or break(type=:dstr)
877
+ }
878
+ [type,sum,*tree[2..-1]+result[i+1..-1].inject([]){|cat,x|
879
+ if x.first==:dstr
880
+ x.shift
881
+ x0=x[0]
882
+ if x0=='' and x.size==2
883
+ x.shift
884
+ else
885
+ x[0]=[:str,x0]
886
+ end
887
+ cat+x
888
+ else
889
+ cat+[x]
890
+ end
891
+ }
892
+ ]
893
+ end
894
+
895
+ def unparse o
896
+ map{|ss| ss.unparse(o)}.join ' '
897
+ end
898
+ end
899
+
900
+ # class ArrowOpNode<ValueNode
901
+ # param_names(:left,:arrow_,:right)
902
+ # end
903
+ module ArrowOpNode #not to appear in final tree?
904
+ def initialize(*args)
905
+ @module=ArrowOpNode
906
+ end
907
+ end
908
+
909
+ # class RangeNode<ValueNode
910
+ module RangeNode
911
+ # param_names(:first,:op_,:last)
912
+ def initialize(left,op_,right)
913
+ @exclude_end=!!op_[2]
914
+ @module=RangeNode
915
+ @as_flow_control=false
916
+ # super(left,right)
917
+ end
918
+ def begin; left end
919
+ def end; right end
920
+ def first; left end
921
+ def last; right end
922
+ def exclude_end?; @exclude_end end
923
+
924
+ def parsetree
925
+ first=first().parsetree
926
+ last=last().parsetree
927
+ if :lit==first.first and :lit==last.first and
928
+ Fixnum===first.last and Fixnum===last.last
929
+ return [:lit, Range.new(first.last,last.last,@exclude_end)]
930
+ end
931
+ tag= @as_flow_control ? "flip" : "dot"
932
+ count= @exclude_end ? ?3 : ?2
933
+ tag << count
934
+ [tag.to_sym, first, last]
935
+ end
936
+
937
+ def special_conditions!
938
+ @as_flow_control=true
939
+ end
940
+
941
+ def unparse(o)
942
+ result=left.unparse(o)+'..'
943
+ result+='.' if exclude_end?
944
+ result << right.unparse(o)
945
+ return result
946
+ end
947
+ end
948
+
949
+ class UnOpNode<ValueNode
950
+ param_names(:op,:val)
951
+ def initialize(op,val)
952
+ # /^[&*+-]$/===op.ident and op.ident+="@"
953
+ op=op.ident
954
+ /^(!|not)$/===op and
955
+ val.respond_to? :special_conditions! and
956
+ val.special_conditions!
957
+ super(op,val)
958
+ end
959
+
960
+ alias ident op
961
+
962
+ def image; "(#{op})" end
963
+
964
+ def lvalue?
965
+ # return nil unless op=="*@"
966
+
967
+ return @lvalue if defined? @lvalue
968
+ @lvalue=true
969
+ end
970
+ attr_accessor :lvalue
971
+
972
+ def to_lisp
973
+ "(#{op} #{val.to_lisp})"
974
+ end
975
+
976
+ def parsetree
977
+ node=self
978
+ node=node.val while UnOpNode===node and node.op=="+@"
979
+ return node.parsetree if LiteralNode&-{:val=>Integer|Float|Symbol}===node
980
+ return node.parsetree if StringNode&-{:char=>'/', :size=>1}===node
981
+
982
+ case op
983
+ when /^&/: [:block_arg, val.ident.to_sym]
984
+ when "!","not": [:not, val.rescue_parsetree]
985
+ when "defined?": [:defined, val.parsetree]
986
+ else
987
+ [:call, val.rescue_parsetree, op.to_sym]
988
+ end
989
+ end
990
+
991
+ def lvalue_parsetree
992
+ parsetree
993
+ end
994
+
995
+ def unparse o
996
+ op=op()
997
+ op=op.chomp "@"
998
+ result=op
999
+ result+=" " if /[a-z_]/i===op
1000
+ result+=val.unparse(o)
1001
+ end
1002
+ end
1003
+
1004
+ class UnaryStarNode<UnOpNode
1005
+ def parsetree
1006
+ [:splat, val.rescue_parsetree]
1007
+ end
1008
+
1009
+ def all_current_lvars
1010
+ val.respond_to?(:all_current_lvars) ?
1011
+ val.all_current_lvars : []
1012
+ end
1013
+ attr_accessor :after_comma
1014
+
1015
+ def lvalue_parsetree
1016
+ val.lvalue_parsetree
1017
+ end
1018
+
1019
+ def unparse o
1020
+ "*"+val.unparse(o)
1021
+ end
1022
+ end
1023
+
1024
+ class DanglingStarNode<UnaryStarNode
1025
+ #param_names :op,:val
1026
+ def initialize(star)
1027
+ @offset= star.offset
1028
+ replace ['*@',VarNameToken.new('',offset)]
1029
+ end
1030
+ attr :offset
1031
+ def lvars_defined_in; [] end
1032
+ def parsetree; [:splat] end
1033
+ alias lvalue_parsetree parsetree
1034
+
1035
+ def unparse(o); "* "; end
1036
+ end
1037
+
1038
+ class DanglingCommaNode<DanglingStarNode
1039
+ def initialize
1040
+
1041
+ end
1042
+ attr_accessor :offset
1043
+
1044
+ def lvalue_parsetree
1045
+ :dangle_comma
1046
+ end
1047
+ alias parsetree lvalue_parsetree
1048
+
1049
+ def unparse o; ""; end
1050
+ end
1051
+
1052
+ class ConstantNode<ListOpNode
1053
+ def initialize(*args)
1054
+ args.unshift nil if args.size==2
1055
+ args.map!{|node|
1056
+ if VarNameToken===node and (?A..?Z)===node.ident[0]
1057
+ then node.ident
1058
+ else node
1059
+ end
1060
+ }
1061
+ super(*args)
1062
+ end
1063
+ def unparse(o)
1064
+ if Node===first
1065
+ result=dup
1066
+ result[0]= first.unparse(o)#.gsub(/\s+\Z/,'')
1067
+ result.join('::')
1068
+ else join('::')
1069
+ end
1070
+ end
1071
+ alias image unparse
1072
+ def lvalue_parsetree
1073
+ [:cdecl,parsetree]
1074
+ end
1075
+ def parsetree
1076
+ if !first
1077
+ result=[:colon3, self[1].to_sym]
1078
+ i=2
1079
+ else
1080
+ result=first.respond_to?(:parsetree) ?
1081
+ first.parsetree :
1082
+ [:const,first.to_sym]
1083
+ i=1
1084
+ end
1085
+ (i...size).inject(result){|r,j|
1086
+ [:colon2, r, self[j].to_sym]
1087
+ }
1088
+ end
1089
+
1090
+ def lvalue?
1091
+ return @lvalue if defined? @lvalue
1092
+ @lvalue=true
1093
+ end
1094
+ attr_accessor :lvalue
1095
+ end
1096
+ LookupNode=ConstantNode
1097
+
1098
+ class DoubleColonNode<ValueNode #dunno about this name... maybe ConstantNode?
1099
+ #not used anymore
1100
+ param_names :namespace, :constant
1101
+ alias left namespace
1102
+ alias right constant
1103
+ def initialize(val1,op,val2=nil)
1104
+ val1,op,val2=nil,val1,op unless val2
1105
+ val1=val1.ident if VarNameToken===val1 and /\A[A-Z]/===val1.ident
1106
+ val2=val2.ident if VarNameToken===val2 and /\A[A-Z]/===val2.ident
1107
+ replace [val1,val2]
1108
+ end
1109
+
1110
+ def image; '(::)' end
1111
+
1112
+
1113
+ def parsetree
1114
+ if namespace
1115
+ ns= (String===namespace) ? [:const,namespace.to_sym] : namespace.parsetree
1116
+ [:colon2, ns, constant.to_sym]
1117
+ else
1118
+ [:colon3, constant.to_sym]
1119
+ end
1120
+ end
1121
+ def lvalue_parsetree
1122
+ [:cdecl,parsetree]
1123
+ end
1124
+
1125
+ def lvalue?
1126
+ return @lvalue if defined? @lvalue
1127
+ @lvalue=true
1128
+ end
1129
+ attr_accessor :lvalue
1130
+ end
1131
+
1132
+ class DotCallNode<ValueNode #obsolete
1133
+ param_names :receiver,:dot_,:callsite
1134
+
1135
+ def image; '(.)' end
1136
+
1137
+ def to_lisp
1138
+ "(#{receiver.to_lisp} #{@data.last.to_lisp[1...-1]})"
1139
+ end
1140
+
1141
+ def parsetree
1142
+ cs=self[1]
1143
+ cs &&= cs.parsetree
1144
+ cs.shift if cs.first==:vcall or cs.first==:fcall
1145
+ [:call, @data.first.parsetree, *cs]
1146
+ end
1147
+ def lvalue?
1148
+ return @lvalue if defined? @lvalue
1149
+ @lvalue=true
1150
+ end
1151
+ attr_accessor :lvalue
1152
+ end
1153
+
1154
+ class ParenedNode<ValueNode
1155
+ param_names :body, :rescues, :else!, :ensure!
1156
+ def initialize(*args)
1157
+ @empty_ensure=@op_rescue=nil
1158
+ replace(
1159
+ if args.size==3 #()
1160
+ if (KeywordToken===args.first and args.first.ident=='(')
1161
+ [args[1]]
1162
+ else
1163
+ expr,rescueword,backup=*args
1164
+ @op_rescue=true
1165
+ [expr,[RescueNode[[],nil,backup]],nil,nil]
1166
+ end
1167
+ else
1168
+ body,rescues,else_,ensure_=*args[1...-1]
1169
+ if else_
1170
+ else_=else_.val or @empty_else=true
1171
+ end
1172
+ if ensure_
1173
+ ensure_=ensure_.val or @empty_ensure=true
1174
+ end
1175
+ [body,rescues,else_,ensure_]
1176
+ end
1177
+ )
1178
+ end
1179
+
1180
+ alias ensure_ ensure
1181
+ alias else_ else
1182
+
1183
+ attr_reader :empty_ensure, :empty_else
1184
+ attr_accessor :after_comma, :after_equals
1185
+ def op?; @op_rescue; end
1186
+
1187
+ def image; '(begin)' end
1188
+
1189
+ def special_conditions!
1190
+ if size==1
1191
+ node=body
1192
+ node.special_conditions! if node.respond_to? :special_conditions!
1193
+ end
1194
+ end
1195
+
1196
+ def to_lisp
1197
+ huh #what about rescues, else, ensure?
1198
+ body.to_lisp
1199
+ end
1200
+
1201
+ def parsetree
1202
+ if size==1
1203
+ body.parsetree
1204
+ else
1205
+ body=body()
1206
+ target=result=[] #was: [:begin, ]
1207
+
1208
+ #body,rescues,else_,ensure_=*self
1209
+ target.push target=[:ensure, ] if ensure_ or @empty_ensure
1210
+
1211
+ rescues=rescues().map{|resc| resc.parsetree}
1212
+ if rescues.empty?
1213
+ else_ and
1214
+ body=SequenceNode.new(body,nil,else_)
1215
+ else_=nil
1216
+ else
1217
+ target.push newtarget=[:rescue, ]
1218
+ else_=else_()
1219
+ end
1220
+ if body
1221
+ needbegin= (ParenedNode===body and body.after_equals)
1222
+ body=body.parsetree
1223
+ body=[:begin, body] if needbegin and body.first!=:begin
1224
+ (newtarget||target).push body if body
1225
+ end
1226
+ target.push ensure_.parsetree if ensure_
1227
+ target.push [:nil] if @empty_ensure
1228
+ target=newtarget if newtarget
1229
+
1230
+ unless rescues.empty?
1231
+ target.push linked_list(rescues)
1232
+ end
1233
+ target.push else_.parsetree if else_ #and !body
1234
+ result.size==0 and result=[[:nil]]
1235
+ result=result.last #if @op_rescue
1236
+ result
1237
+ end
1238
+ end
1239
+
1240
+ # def rescue_parsetree
1241
+ # result=parsetree
1242
+ # result.first==:begin and result=result.last
1243
+ # result
1244
+ # end
1245
+
1246
+ def begin_parsetree
1247
+ body,rescues,else_,ensure_=*self
1248
+ needbegin=(rescues&&!rescues.empty?) || ensure_ || @empty_ensure
1249
+ result=parsetree
1250
+ needbegin and result=[:begin, result]
1251
+ result
1252
+ end
1253
+
1254
+ def lvalue?
1255
+ return nil unless size==1
1256
+ # case first
1257
+ # when CommaOpNode,UnaryStarNode: #do nothing
1258
+ # when ParenedNode: return first.lvalue?
1259
+ # else return nil
1260
+ # end
1261
+
1262
+ return @lvalue if defined? @lvalue
1263
+ @lvalue=true
1264
+ end
1265
+ attr_accessor :lvalue
1266
+
1267
+ def unparse(o)
1268
+ if size==1
1269
+ "("+(body&&body.unparse(o))+")"
1270
+ else
1271
+ result="begin "
1272
+ body&&result+= body.unparse(o)
1273
+ result+="\n"
1274
+ rescues.each{|resc| result+=resc.unparse(o) }
1275
+ result+="\nensure "+ensure_.unparse(o) if ensure_
1276
+ result+="\nelse "+else_.unparse(o) if else_
1277
+ result+="\nend"
1278
+ end
1279
+ end
1280
+ end
1281
+
1282
+ class AssignmentRhsNode < Node #not to appear in final parse tree
1283
+ param_names :open_, :val, :close_
1284
+ def initialize(*args)
1285
+ if args.size==1: super args.first
1286
+ else super args[1]
1287
+ end
1288
+ end
1289
+ end
1290
+
1291
+ class AssignNode<ValueNode
1292
+ param_names :left,:op,:right
1293
+ alias lhs left
1294
+ alias rhs right
1295
+ def initialize(*args)
1296
+
1297
+ if args.size==5
1298
+ if args[3].ident=="rescue3"
1299
+ lhs,op,rescuee,op2,rescuer=*args
1300
+ rhs=ParenedNode.new(rescuee.val,op2,rescuer)
1301
+ else
1302
+ lhs,op,bogus1,rhs,bogus2=*args
1303
+ end
1304
+ else
1305
+ lhs,op,rhs=*args
1306
+ rhs=rhs.val if AssignmentRhsNode===rhs
1307
+ end
1308
+ case lhs
1309
+ when UnaryStarNode #look for star on lhs
1310
+ lhs=MultiAssign.new([lhs]) unless lhs.after_comma
1311
+ when ParenedNode
1312
+ if lhs.size==1 and !lhs.after_comma #look for () around lhs
1313
+ if CommaOpNode===lhs.first
1314
+ lhs=MultiAssign.new(Array.new(lhs.first))
1315
+ else
1316
+ lhs=MultiAssign.new([lhs.first])
1317
+ end
1318
+ @lhs_parens=true
1319
+ end
1320
+ when CommaOpNode:
1321
+ lhs=MultiAssign.new lhs
1322
+ #rhs=Array.new(rhs) if CommaOpNode===rhs
1323
+ end
1324
+
1325
+ if CommaOpNode===rhs
1326
+ rhs=Array.new(rhs)
1327
+ lhs=MultiAssign.new([lhs]) unless MultiAssign===lhs
1328
+ end
1329
+
1330
+ op=op.ident
1331
+
1332
+ return super(lhs,op,rhs)
1333
+ #punting, i hope the next layer can handle += and the like
1334
+
1335
+ =begin
1336
+ #in theory, we should do something more sophisticated, like this:
1337
+ #(but the presence of side effects in lhs will screw it up)
1338
+ if op=='='
1339
+ super
1340
+ else
1341
+ super(lhs,OpNode.new(lhs,OperatorToken.new(op.chomp('=')),rhs))
1342
+ end
1343
+ =end
1344
+ end
1345
+
1346
+ def multi?
1347
+ MultiAssign===left
1348
+ end
1349
+
1350
+ def image; '(=)' end
1351
+
1352
+ def to_lisp
1353
+ case left
1354
+ when ParenedNode: huh
1355
+ when ConstantNode: huh
1356
+ when BracketsGetNode: huh
1357
+ when VarNameToken
1358
+ "(set #{left.to_lisp} (#{op.chomp('=')} #{left.to_lisp} #{right.to_lisp}))"
1359
+ when CallSiteNode
1360
+ if op=='='
1361
+ "(#{left.receiver.to_lisp} #{left.name}= #{right.to_lisp})"
1362
+ else
1363
+ op_=op.chomp('=')
1364
+ varname=nil
1365
+ "(let #{varname=huh} #{left.receiver.to_lisp} "+
1366
+ "(#{varname} #{left.name}= "+
1367
+ "(#{op_} (#{varname} #{op}) #{right.to_lisp})))"
1368
+ end
1369
+ else huh
1370
+ end
1371
+ end
1372
+
1373
+ def all_current_lvars
1374
+ left.respond_to?(:all_current_lvars) ?
1375
+ left.all_current_lvars : []
1376
+ end
1377
+
1378
+ def parsetree
1379
+ case left
1380
+ when ParenedNode: huh
1381
+ when ConstantNode:
1382
+ left.lvalue_parsetree << right.parsetree
1383
+
1384
+ when MultiAssign:
1385
+ lhs=left.lvalue_parsetree
1386
+ rhs= right.class==Array ? right : [right]
1387
+ star=rhs.pop if UnaryStarNode===rhs.last
1388
+ rhs=rhs.map{|x| x.rescue_parsetree}
1389
+ if rhs.size==0
1390
+ star or fail
1391
+ rhs= star.parsetree
1392
+ elsif rhs.size==1 and !star and !(UnaryStarNode===left.first)
1393
+ rhs.unshift :to_ary
1394
+ else
1395
+ rhs.unshift(:array)
1396
+ if star
1397
+ splat=star.val.rescue_parsetree
1398
+ #if splat.first==:call #I don't see how this can be right....
1399
+ # splat[0]=:attrasgn
1400
+ # splat[2]="#{splat[2]}=".to_sym
1401
+ #end
1402
+ rhs=[:argscat, rhs, splat]
1403
+ end
1404
+ if left.size==1 and !(UnaryStarNode===left.first) and !(NestedAssign===left.first)
1405
+ rhs=[:svalue, rhs]
1406
+ end
1407
+ end
1408
+ lhs<< rhs
1409
+
1410
+ when CallSiteNode
1411
+ op=op().chomp('=')
1412
+ rcvr=left.receiver.parsetree
1413
+ prop=left.name.+('=').to_sym
1414
+ args=right.rescue_parsetree
1415
+ UnaryStarNode===right and args=[:svalue, args]
1416
+ if op.empty?
1417
+ [:attrasgn, rcvr, prop, [:array, args] ]
1418
+ else
1419
+ [:op_asgn2, rcvr,prop, op.to_sym, args]
1420
+ end
1421
+
1422
+ when BracketsGetNode
1423
+ args=left.params
1424
+ if op()=='='
1425
+ result=left.lvalue_parsetree #[:attrasgn, left[0].parsetree, :[]=]
1426
+ result.size==3 and result.push [:array]
1427
+ rhs=right.rescue_parsetree
1428
+ UnaryStarNode===right and rhs=[:svalue, rhs]
1429
+ if args
1430
+ result[-1]=[:argspush,result[-1]] if UnaryStarNode===args.last
1431
+ #else result[-1]=[:zarray]
1432
+ end
1433
+ result.last << rhs
1434
+ result
1435
+
1436
+ else
1437
+ =begin
1438
+ args&&=args.map{|x| x.parsetree}.unshift(:array)
1439
+ splat=args.pop if :splat==args.last.first
1440
+ if splat and left.params.size==1
1441
+ args=splat
1442
+ elsif splat
1443
+ args=[:argscat, args, splat.last]
1444
+ end
1445
+ =end
1446
+ lhs=left.parsetree
1447
+ if lhs.first==:fcall
1448
+ rcvr=[:self]
1449
+ args=lhs[2]
1450
+ else
1451
+ rcvr=lhs[1]
1452
+ args=lhs[3]
1453
+ end
1454
+ args||=[:zarray]
1455
+ result=[
1456
+ :op_asgn1, rcvr, args,
1457
+ op().chomp('=').to_sym,
1458
+ right.rescue_parsetree
1459
+ ]
1460
+ end
1461
+
1462
+ when VarNameToken
1463
+ node_type=left.varname2assigntype
1464
+ if /^(&&|\|\|)=$/===op()
1465
+
1466
+ return ["op_asgn_#{$1[0]==?& ? "and" : "or"}".to_sym,
1467
+ left.parsetree,
1468
+ [node_type, left.ident.to_sym,
1469
+ right.rescue_parsetree]
1470
+ ]
1471
+ end
1472
+
1473
+ if op()=='='
1474
+ rhs=right.rescue_parsetree
1475
+ UnaryStarNode===right and rhs=[:svalue, rhs]
1476
+
1477
+ # case left
1478
+ # when VarNameToken:
1479
+ [node_type, left.ident.to_sym, rhs]
1480
+ # else [node_type, left.data[0].parsetree, left.data[1].data[0].ident.+('=').to_sym ,[:array, rhs]]
1481
+ # end
1482
+
1483
+ =begin these branches shouldn't be necessary now
1484
+ elsif node_type==:op_asgn2
1485
+ [node_type, @data[0].data[0].parsetree,
1486
+ @data[0].data[1].data[0].ident.+('=').to_sym,
1487
+ op().ident.chomp('=').to_sym,
1488
+ @data[2].parsetree
1489
+ ]
1490
+ elsif node_type==:attrasgn
1491
+ [node_type]
1492
+ =end
1493
+ else
1494
+ [node_type, left.ident.to_sym,
1495
+ [:call,
1496
+ left.parsetree,
1497
+ op().chomp('=').to_sym,
1498
+ [:array, right.rescue_parsetree]
1499
+ ]
1500
+ ]
1501
+ end
1502
+ else
1503
+ huh
1504
+ end
1505
+ end
1506
+
1507
+ def unparse(o)
1508
+ lhs.lhs_unparse(o)+op+
1509
+ (rhs.class==Array ?
1510
+ rhs.map{|rv| rv.unparse o}.join(',') :
1511
+ rhs.unparse(o)
1512
+ )
1513
+ end
1514
+ end
1515
+
1516
+ class MultiAssignNode < ValueNode #obsolete
1517
+ param_names :left,:right
1518
+
1519
+ #not called from parse table
1520
+
1521
+ def parsetree
1522
+ lhs=left.dup
1523
+ if UnaryStarNode===lhs.last
1524
+ lstar=lhs.pop
1525
+ end
1526
+ lhs.map!{|x|
1527
+ res=x.parsetree
1528
+ res[0]=x.varname2assigntype if VarNameToken===x
1529
+ res
1530
+ }
1531
+ lhs.unshift(:array) if lhs.size>1 or lstar
1532
+ rhs=right.map{|x| x.parsetree}
1533
+ if rhs.size==1
1534
+ if rhs.first.first==:splat
1535
+ rhs=rhs.first
1536
+ else
1537
+ rhs.unshift :to_ary
1538
+ end
1539
+ else
1540
+ rhs.unshift(:array)
1541
+ if rhs[-1][0]==:splat
1542
+ splat=rhs.pop[1]
1543
+ if splat.first==:call
1544
+ splat[0]=:attrasgn
1545
+ splat[2]="#{splat[2]}=".to_sym
1546
+ end
1547
+ rhs=[:argscat, rhs, splat]
1548
+ end
1549
+ end
1550
+ result=[:masgn, lhs, rhs]
1551
+ result.insert(2,lstar.data.last.parsetree) if lstar
1552
+ result
1553
+
1554
+ end
1555
+ end
1556
+
1557
+ class AssigneeList< ValueNode #abstract
1558
+ def initialize(data)
1559
+ data.each_with_index{|datum,i|
1560
+ if ParenedNode&-{:size=>1}===datum
1561
+ first=datum.first
1562
+ list=case first
1563
+ when CommaOpNode: Array.new(first)
1564
+ when UnaryStarNode,ParenedNode: [first]
1565
+ end
1566
+ data[i]=NestedAssign.new(list) if list
1567
+ end
1568
+ }
1569
+ replace data
1570
+ end
1571
+
1572
+ def unparse o
1573
+ map{|lval| lval.lhs_unparse o}.join(', ')
1574
+ end
1575
+
1576
+ def old_parsetree
1577
+ lhs=data.dup
1578
+ if UnaryStarNode===lhs.last
1579
+ lstar=lhs.pop.val
1580
+ end
1581
+ lhs.map!{|x|
1582
+ res=x.parsetree
1583
+ res[0]=x.varname2assigntype if VarNameToken===x
1584
+ res
1585
+ }
1586
+ lhs.unshift(:array) if lhs.size>1 or lstar
1587
+ result=[lhs]
1588
+ if lstar.respond_to? :varname2assigntype
1589
+ result << lstar.varname2assigntype
1590
+ elsif lstar #[]=, attrib=, or A::B=
1591
+ huh
1592
+ else #do nothing
1593
+ end
1594
+ result
1595
+
1596
+ end
1597
+
1598
+ def parsetree
1599
+ data=self
1600
+ data.empty? and return nil
1601
+ # data=data.first if data.size==1 and ParenedNode===data.first and data.first.size==1
1602
+ data=Array.new(data)
1603
+ star=data.pop if UnaryStarNode===data.last
1604
+ result=data.map{|x| x.lvalue_parsetree }
1605
+ =begin
1606
+ {
1607
+ if VarNameToken===x
1608
+ ident=x.ident
1609
+ ty=x.varname2assigntype
1610
+ # ty==:lasgn and ty=:dasgn_curr
1611
+ [ty, ident.to_sym]
1612
+ else
1613
+ x=x.parsetree
1614
+ if x[0]==:call
1615
+ x[0]=:attrasgn
1616
+ x[2]="#{x[2]}=".to_sym
1617
+ end
1618
+ x
1619
+ end
1620
+ }
1621
+ =end
1622
+ if result.size==0
1623
+ star or fail
1624
+ result=[:masgn, star.lvalue_parsetree]
1625
+ elsif result.size==1 and !star and !(NestedAssign===data.first)
1626
+ result=result.first
1627
+ else
1628
+ result=[:masgn, [:array, *result]]
1629
+ result.push star.lvalue_parsetree if star and not DanglingCommaNode===star
1630
+ end
1631
+ result
1632
+ end
1633
+
1634
+
1635
+
1636
+ def all_current_lvars
1637
+ result=[]
1638
+ each{|lvar|
1639
+ lvar.respond_to?(:all_current_lvars) and
1640
+ result.concat lvar.all_current_lvars
1641
+ }
1642
+ return result
1643
+ end
1644
+
1645
+ def lvalue_parsetree; parsetree end
1646
+ end
1647
+ class NestedAssign<AssigneeList
1648
+ # def parsetree
1649
+ # [:masgn, *super]
1650
+ # end
1651
+ def unparse o
1652
+ "("+super+")"
1653
+
1654
+ end
1655
+ end
1656
+
1657
+
1658
+ class MultiAssign<AssigneeList; end
1659
+ class BlockParams<AssigneeList;
1660
+ def initialize(data)
1661
+ item=data.first if data.size==1
1662
+ #elide 1 layer of parens if present
1663
+ if ParenedNode===item and item.size==1
1664
+ item=item.first
1665
+ data=CommaOpNode===item ? Array.new(item) : [item]
1666
+ @had_parens=true
1667
+ end
1668
+
1669
+ super(data)
1670
+ end
1671
+
1672
+ def unparse o
1673
+ unless @had_parens
1674
+ "|"+super+"|"
1675
+ else
1676
+ "|("+super+")|"
1677
+ end
1678
+ end
1679
+ end
1680
+
1681
+ class AccessorAssignNode < ValueNode #obsolete
1682
+ param_names :left,:dot_,:property,:op,:right
1683
+
1684
+ def to_lisp
1685
+ if op.ident=='='
1686
+ "(#{left.to_lisp} #{property.ident}= #{right.to_lisp})"
1687
+ else
1688
+ op=op().ident.chomp('=')
1689
+ varname=nil
1690
+ "(let #{varname=huh} #{left.to_lisp} "+
1691
+ "(#{varname} #{property.ident}= "+
1692
+ "(#{op} (#{varname} #{property.ident}) #{right.to_lisp})))"
1693
+ end
1694
+ end
1695
+
1696
+ def parsetree
1697
+ op=op().ident.chomp('=')
1698
+ rcvr=left.parsetree
1699
+ prop=property.ident.<<(?=).to_sym
1700
+ rhs=right.parsetree
1701
+ if op.empty?
1702
+ [:attrasgn, rcvr, prop, [:array, args] ]
1703
+ else
1704
+ [:op_asgn2, rcvr,prop, op.to_sym, args]
1705
+ end
1706
+ end
1707
+ end
1708
+
1709
+ module KeywordOpNode
1710
+ def unparse o
1711
+ [left.unparse(o),' ',op,' ',right.unparse(o)].to_s
1712
+ end
1713
+ end
1714
+
1715
+ module LogicalNode
1716
+ include KeywordOpNode
1717
+ def initialize(left,op,right)
1718
+ @opmap=op[0,1]
1719
+ case op
1720
+ when "&&": op="and"
1721
+ when "||": op="or"
1722
+ end
1723
+ #@reverse= op=="or"
1724
+ #@op=op
1725
+ @module=LogicalNode
1726
+ replace [left,right]
1727
+ (size-1).downto(0){|i|
1728
+ expr=self[i]
1729
+ if LogicalNode===expr and expr.op==op
1730
+ self[i,1]=Array.new expr
1731
+ opmap[i,0]=expr.opmap
1732
+ end
1733
+ }
1734
+ end
1735
+ attr_reader :opmap
1736
+
1737
+ OP_EXPAND={?o=>"or", ?a=>"and", ?&=>"&&", ?|=>"||", nil=>""}
1738
+ OP_EQUIV={?o=>"or", ?a=>"and", ?&=>"and", ?|=>"or"}
1739
+
1740
+ def reverse
1741
+ /\A[o|]/===@opmap
1742
+ end
1743
+ def op
1744
+ OP_EQUIV[@opmap[0]]
1745
+ end
1746
+
1747
+ #these 3 methods are defined in RawOpNode too, hence these
1748
+ #definitions are ignored. grrrrrrr.
1749
+ def unparse o
1750
+ result=''
1751
+
1752
+ each_with_index{|expr,i|
1753
+ result.concat expr.unparse(o)
1754
+ result.concat ?\s
1755
+ result.concat OP_EXPAND[@opmap[i]]
1756
+ result.concat ?\s
1757
+ }
1758
+ return result
1759
+ end
1760
+ def left(*args,&block)
1761
+ method_missing(:left,*args,&block)
1762
+ end
1763
+ def right(*args,&block)
1764
+ method_missing(:right,*args,&block)
1765
+ end
1766
+
1767
+ def parsetree
1768
+ result=[].replace(self).reverse
1769
+ last=result.shift.begin_parsetree
1770
+ first=result.pop
1771
+ result=result.inject(last){|sum,x|
1772
+ [op.to_sym, x.begin_parsetree, sum]
1773
+ }
1774
+ [op.to_sym, first.rescue_parsetree, result]
1775
+ end
1776
+
1777
+ def special_conditions!
1778
+ each{|x|
1779
+ if x.respond_to? :special_conditions! and !(ParenedNode===x)
1780
+ x.special_conditions!
1781
+ end
1782
+ }
1783
+ end
1784
+ end
1785
+
1786
+ module WhileOpNode
1787
+ include KeywordOpNode
1788
+ def condition; right end
1789
+ def consequent; left end
1790
+ def initialize(val1,op,val2)
1791
+ self[1]=op
1792
+ @reverse=false
1793
+ @module=WhileOpNode
1794
+ @loop=true
1795
+ @test_first= !( ParenedNode===val1 and val1.size != 1 )
1796
+ condition.special_conditions! if condition.respond_to? :special_conditions!
1797
+ end
1798
+
1799
+ def while; condition end
1800
+ def do; consequent end
1801
+
1802
+ def parsetree
1803
+ cond=condition.rescue_parsetree
1804
+ body=consequent.parsetree
1805
+ !@test_first and
1806
+ body.size == 2 and
1807
+ body.first == :begin and
1808
+ body=body.last
1809
+ if cond.first==:not
1810
+ kw=:until
1811
+ cond=cond.last
1812
+ else
1813
+ kw=:while
1814
+ end
1815
+ [kw, cond, body, (@test_first or body==[:nil])]
1816
+ end
1817
+
1818
+
1819
+ end
1820
+
1821
+ module UntilOpNode
1822
+ include KeywordOpNode
1823
+ def condition; right end
1824
+ def consequent; left end
1825
+ def initialize(val1,op,val2)
1826
+ self[1]=op
1827
+ @reverse=true
1828
+ @loop=true
1829
+ @test_first= !( ParenedNode===val1 and (val1.size != 1 ))
1830
+ @module=UntilOpNode
1831
+ condition.special_conditions! if condition.respond_to? :special_conditions!
1832
+ end
1833
+
1834
+ def while; negate condition end
1835
+ def do; consequent end
1836
+
1837
+ def parsetree
1838
+ cond=condition.rescue_parsetree
1839
+ body=consequent.parsetree
1840
+ !@test_first and
1841
+ body.size == 2 and
1842
+ body.first == :begin and
1843
+ body=body.last
1844
+ if cond.first==:not
1845
+ kw=:while
1846
+ cond=cond.last
1847
+ else
1848
+ kw=:until
1849
+ end
1850
+ tf=@test_first
1851
+ tf||= (!consequent.body and !consequent.else and !consequent.empty_else and
1852
+ !consequent.ensure and !consequent.empty_ensure and consequent.rescues.empty?
1853
+ ) if ParenedNode===consequent
1854
+ [kw, cond, body, tf]
1855
+ end
1856
+ end
1857
+
1858
+ module UnlessOpNode
1859
+ include KeywordOpNode
1860
+ def condition; right end
1861
+ def consequent; left end
1862
+ def initialize(val1,op,val2)
1863
+ self[1]=op
1864
+ @reverse=true
1865
+ @loop=false
1866
+ @module=UnlessOpNode
1867
+ condition.special_conditions! if condition.respond_to? :special_conditions!
1868
+ end
1869
+
1870
+ def if; condition end
1871
+ def then; nil end
1872
+ def else; consequent end
1873
+ def elsifs; [] end
1874
+
1875
+ def parsetree
1876
+ cond=condition.rescue_parsetree
1877
+ actions=[nil, consequent.parsetree]
1878
+ if cond.first==:not
1879
+ actions.reverse!
1880
+ cond=cond.last
1881
+ end
1882
+ [:if, cond, *actions]
1883
+ end
1884
+ end
1885
+
1886
+ module IfOpNode
1887
+ include KeywordOpNode
1888
+ def condition; right end
1889
+ def consequent; left end
1890
+ def initialize(left,op,right)
1891
+ self[1]=op
1892
+ @reverse=false
1893
+ @loop=false
1894
+ @module=IfOpNode
1895
+ condition.special_conditions! if condition.respond_to? :special_conditions!
1896
+ end
1897
+
1898
+ def if; condition end
1899
+ def then; consequent end
1900
+ def else; nil end
1901
+ def elsifs; [] end
1902
+
1903
+ def parsetree
1904
+ cond=condition.rescue_parsetree
1905
+ actions=[consequent.parsetree, nil]
1906
+ if cond.first==:not
1907
+ actions.reverse!
1908
+ cond=cond.last
1909
+ end
1910
+ [:if, cond, *actions]
1911
+ end
1912
+ end
1913
+
1914
+ class CallSiteNode<ValueNode
1915
+ param_names :receiver, :name, :params, :blockparams, :block
1916
+ alias blockargs blockparams
1917
+ alias block_args blockargs
1918
+ alias block_params blockparams
1919
+
1920
+ def initialize(method,open_paren,param_list,close_paren,block)
1921
+ @not_real_parens=!open_paren || open_paren.not_real?
1922
+
1923
+ case param_list
1924
+ when CommaOpNode
1925
+ #handle inlined hash pairs in param list (if any)
1926
+ # compr=Object.new
1927
+ # def compr.==(other) ArrowOpNode===other end
1928
+ param_list=Array.new(param_list)
1929
+ first=last=nil
1930
+ param_list.each_with_index{|param,i|
1931
+ break first=i if ArrowOpNode===param
1932
+ }
1933
+ (1..param_list.size).each{|i| param=param_list[-i]
1934
+ break last=-i if ArrowOpNode===param
1935
+ }
1936
+ if first
1937
+ arrowrange=first..last
1938
+ arrows=param_list[arrowrange]
1939
+ param_list[arrowrange]=[HashLiteralNode.new(nil,arrows,nil)]
1940
+ end
1941
+
1942
+ when ArrowOpNode:
1943
+ param_list=[HashLiteralNode.new(nil,param_list,nil)]
1944
+ # when KeywordOpNode
1945
+ # fail "didn't expect '#{param_list.inspect}' inside actual parameter list"
1946
+ when nil
1947
+ else
1948
+ param_list=[param_list]
1949
+ end
1950
+
1951
+ if block
1952
+ @do_end=block.do_end
1953
+ blockparams=block.params
1954
+ block=SequenceNode[*block.body] #||[]
1955
+ end
1956
+ @offset=method.offset
1957
+ method=method.ident
1958
+ fail unless String===method
1959
+ super(nil,method,param_list,blockparams,block)
1960
+ #receiver, if any, is tacked on later
1961
+ end
1962
+
1963
+ def real_parens; !@not_real_parens end
1964
+
1965
+ def unparse o
1966
+ fail if block==false
1967
+ result=[
1968
+ receiver&&receiver.unparse(o)+'.',name,
1969
+ real_parens ? '(' : (' ' if params),
1970
+ params&&params.map{|param| param.unparse(o)}.join(', '),
1971
+ real_parens ? ')' : nil,
1972
+
1973
+ block&&[
1974
+ @do_end ? " do " : "{",
1975
+ block_params&&block_params.unparse(o),
1976
+ " ",
1977
+ block.unparse(o),
1978
+ @do_end ? " end" : "}"
1979
+ ]
1980
+ ]
1981
+ return result.to_s
1982
+ end
1983
+
1984
+ def image
1985
+ result="(#{receiver.image if receiver}.#{name})"
1986
+ end
1987
+
1988
+ def lvalue_parsetree
1989
+ result=parsetree
1990
+ result[0]=:attrasgn
1991
+ result[2]="#{result[2]}=".to_sym
1992
+ result
1993
+ end
1994
+
1995
+ def lvalue?
1996
+ return @lvalue if defined? @lvalue
1997
+ @lvalue=true
1998
+ end
1999
+ attr_accessor :lvalue
2000
+
2001
+ def to_lisp
2002
+ "(#{receiver.to_lisp} #{self[1..-1].map{|x| x.to_lisp}.join(' ')})"
2003
+ end
2004
+
2005
+ alias args params
2006
+ alias rcvr receiver
2007
+
2008
+ def set_receiver!(expr)
2009
+ self[0]=expr
2010
+ end
2011
+
2012
+ def parsetree_with_params
2013
+ args=args()||[]
2014
+ if (UnOpNode===args.last and args.last.ident=="&@")
2015
+ lasti=args.size-2
2016
+ unamp_expr=args.last.val
2017
+ else
2018
+ lasti=args.size-1
2019
+ end
2020
+ methodname= name
2021
+ methodsym=methodname.to_sym
2022
+ is_kw= RubyLexer::FUNCLIKE_KEYWORDS&~/^(BEGIN|END|raise)$/===methodname
2023
+
2024
+ result=
2025
+ if lasti==-1
2026
+ [(@not_real_parens and /[!?]$/!~methodname and !unamp_expr) ?
2027
+ :vcall : :fcall, methodsym
2028
+ ]
2029
+ elsif (UnaryStarNode===args[lasti])
2030
+ if lasti.zero?
2031
+ [:fcall, methodsym, args.first.rescue_parsetree]
2032
+ else
2033
+ [:fcall, methodsym,
2034
+ [:argscat,
2035
+ [:array, *args[0...lasti].map{|x| x.rescue_parsetree } ],
2036
+ args[lasti].val.rescue_parsetree
2037
+ ]
2038
+ ]
2039
+ end
2040
+ else
2041
+ singlearg= lasti.zero?&&args.first
2042
+ [:fcall, methodsym,
2043
+ [:array, *args[0..lasti].map{|x| x.rescue_parsetree } ]
2044
+ ]
2045
+ end
2046
+
2047
+ result[0]=:vcall if block #and /\Af?call\Z/===result[0].to_s
2048
+
2049
+ if is_kw and !receiver
2050
+ return [methodsym, singlearg.parsetree] if singlearg and "super"!=methodname
2051
+ breaklike= /^(break|next|return)$/===methodname
2052
+ if @not_real_parens
2053
+ return [:zsuper] if "super"==methodname and !args()
2054
+ else
2055
+ return [methodsym, [:nil]] if breaklike and args.size.zero?
2056
+ end
2057
+ result.shift
2058
+ arg=result[1]
2059
+ result[1]=[:svalue,arg] if arg and arg[0]==:splat and breaklike
2060
+ end
2061
+
2062
+ if receiver
2063
+ result.shift if result.first==:vcall or result.first==:fcall #if not kw
2064
+ result=[:call, receiver.rescue_parsetree, *result]
2065
+ end
2066
+
2067
+ if unamp_expr
2068
+ # result[0]=:fcall if lasti.zero?
2069
+ result=[:block_pass, unamp_expr.rescue_parsetree, result]
2070
+ end
2071
+
2072
+ return result
2073
+ end
2074
+
2075
+ def parsetree
2076
+ callsite=parsetree_with_params
2077
+ return callsite unless blockparams or block
2078
+ call=name
2079
+ callsite[0]=:fcall if callsite[0]==:call or callsite[0]==:vcall
2080
+ unless receiver
2081
+ case call
2082
+ when "BEGIN":
2083
+ if quirks
2084
+ return []
2085
+ else
2086
+ callsite=[:preexe]
2087
+ end
2088
+ when "END": callsite=[:postexe]
2089
+ end
2090
+ else
2091
+ callsite[0]=:call if callsite[0]==:fcall
2092
+ end
2093
+
2094
+ if blockparams
2095
+ bparams=blockparams.dup
2096
+ lastparam=bparams.last
2097
+ amped=bparams.pop.val if UnOpNode===lastparam and lastparam.op=="&@"
2098
+ bparams=bparams.parsetree||0
2099
+ if amped
2100
+ bparams=[:masgn, [:array, bparams]] unless bparams==0 or bparams.first==:masgn
2101
+ bparams=[:block_pass, amped.lvalue_parsetree, bparams]
2102
+ end
2103
+ else
2104
+ bparams=nil
2105
+ end
2106
+ result=[:iter, callsite, bparams]
2107
+ unless block.empty?
2108
+ body=block.parsetree
2109
+ if curr_vars=block.lvars_defined_in
2110
+ curr_vars-=blockparams.all_current_lvars if blockparams
2111
+ if curr_vars.empty?
2112
+ result.push body
2113
+ else
2114
+ curr_vars.map!{|cv| [:dasgn_curr, cv.to_sym] }
2115
+ (0...curr_vars.size-1).each{|i| curr_vars[i]<<curr_vars[i+1] }
2116
+ #body.first==:block ? body.shift : body=[body]
2117
+ result.push((body)) #.unshift curr_vars[0]))
2118
+ end
2119
+ else
2120
+ result.push body
2121
+ end
2122
+ end
2123
+ result
2124
+ end
2125
+
2126
+ def blockformals_parsetree data
2127
+ data.empty? and return nil
2128
+ data=data.dup
2129
+ star=data.pop if UnaryStarNode===data.last
2130
+ result=data.map{|x| x.parsetree }
2131
+ =begin
2132
+ { if VarNameToken===x
2133
+ ident=x.ident
2134
+ ty=x.varname2assigntype
2135
+ # ty==:lasgn and ty=:dasgn_curr
2136
+ [ty, ident.to_sym]
2137
+ else
2138
+ x=x.parsetree
2139
+ if x[0]==:call
2140
+ x[0]=:attrasgn
2141
+ x[2]="#{x[2]}=".to_sym
2142
+ end
2143
+ x
2144
+ end
2145
+ }
2146
+ =end
2147
+ if result.size==0
2148
+ star or fail
2149
+ result=[:masgn, star.parsetree.last]
2150
+ elsif result.size==1 and !star
2151
+ result=result.first
2152
+ else
2153
+ result=[:masgn, [:array, *result]]
2154
+ if star
2155
+ old=star= star.val
2156
+ star=star.parsetree
2157
+ if star[0]==:call
2158
+ star[0]=:attrasgn
2159
+ star[2]="#{star[2]}=".to_sym
2160
+ end
2161
+
2162
+ if VarNameToken===old
2163
+ ty=old.varname2assigntype
2164
+ # ty==:lasgn and ty=:dasgn_curr
2165
+ star[0]=ty
2166
+ end
2167
+ result.push star
2168
+ end
2169
+ end
2170
+ result
2171
+ end
2172
+ end
2173
+
2174
+ class CallNode<CallSiteNode #normal method calls
2175
+ def initialize(method,open_paren,param_list,close_paren,block)
2176
+ MethNameToken===method or fail
2177
+ super
2178
+ end
2179
+ end
2180
+ class KWCallNode<CallSiteNode #keywords that look (more or less) like methods
2181
+ def initialize(method,open_paren,param_list,close_paren,block)
2182
+ KeywordToken===method or fail
2183
+ super
2184
+ end
2185
+ end
2186
+
2187
+ class BlockFormalsNode<Node #obsolete
2188
+ def initialize(goalpost1,param_list,goalpost2)
2189
+ param_list or return super()
2190
+ CommaOpNode===param_list and return super(*Array.new(param_list))
2191
+ super(param_list)
2192
+ end
2193
+
2194
+ def to_lisp
2195
+ "(#{data.join' '})"
2196
+ end
2197
+
2198
+ def parsetree
2199
+ empty? ? nil :
2200
+ [:dasgn_curr,
2201
+ *map{|x|
2202
+ VarNameToken===x ? x.ident.to_sym : x.parsetree
2203
+ }
2204
+ ]
2205
+ end
2206
+ end
2207
+
2208
+ class BlockNode<ValueNode #not to appear in final parse tree
2209
+ param_names :params,:body
2210
+ def initialize(open_brace,formals,stmts,close_brace)
2211
+ case stmts
2212
+ when SequenceNode: stmts=Array.new(stmts)
2213
+ when nil: stmts=[]
2214
+ else stmts=[stmts]
2215
+ end
2216
+
2217
+ formals&&=BlockParams.new(Array.new(formals))
2218
+ @do_end=true unless open_brace.not_real?
2219
+ super(formals,stmts)
2220
+ end
2221
+
2222
+ attr_reader :do_end
2223
+
2224
+ def to_lisp
2225
+ "(#{params.to_lisp} #{body.to_lisp})"
2226
+ end
2227
+
2228
+ def parsetree #obsolete
2229
+ callsite=@data[0].parsetree
2230
+ call=@data[0].data[0]
2231
+ callsite[0]=:fcall if call.respond_to? :ident
2232
+ if call.respond_to? :ident
2233
+ case call.ident
2234
+ when "BEGIN":
2235
+ if quirks
2236
+ return []
2237
+ else
2238
+ callsite=[:preexe]
2239
+ end
2240
+ when "END": callsite=[:postexe]
2241
+ end
2242
+ end
2243
+ result=[:iter, callsite, @data[1].parsetree]
2244
+ result.push @data[2].parsetree if @data[2]
2245
+ result
2246
+ end
2247
+ end
2248
+
2249
+ class NopNode<ValueNode
2250
+ def initialize(*args)
2251
+ super()
2252
+ end
2253
+
2254
+ def unparse o
2255
+ ''
2256
+ end
2257
+
2258
+ def to_lisp
2259
+ "()"
2260
+ end
2261
+
2262
+ alias image to_lisp
2263
+
2264
+ def to_parsetree(*options)
2265
+ []
2266
+ end
2267
+ end
2268
+
2269
+ =begin
2270
+ class ObjectNode<ValueNode
2271
+ def initialize
2272
+ super
2273
+ end
2274
+ def to_lisp
2275
+ "Object"
2276
+ end
2277
+
2278
+ def parsetree
2279
+ :Object
2280
+ end
2281
+ end
2282
+ =end
2283
+
2284
+ class CallWithBlockNode<ValueNode #obsolete
2285
+ param_names :call,:block
2286
+ def initialize(call,block)
2287
+ KeywordCall===call and extend KeywordCall
2288
+ super
2289
+ end
2290
+
2291
+ def to_lisp
2292
+ @data.first.to_lisp.chomp!(")")+" #{@data.last.to_lisp})"
2293
+ end
2294
+ end
2295
+
2296
+ class StringNode<ValueNode
2297
+ def initialize(token)
2298
+ if HerePlaceholderToken===token
2299
+ str=token.string
2300
+ @char=token.quote
2301
+ else
2302
+ str=token
2303
+ @char=str.char
2304
+ end
2305
+ @modifiers=str.modifiers #if str.modifiers
2306
+ super *with_string_data(str)
2307
+
2308
+ if /[\[{]/===@char
2309
+ @parses_like=split_into_words(str)
2310
+ end
2311
+
2312
+ first=shift
2313
+ delete_if{|x| ''==x }
2314
+ unshift(first)
2315
+ map!{|strfrag|
2316
+ if String===strfrag
2317
+ str.translate_escapes strfrag
2318
+ else
2319
+ strfrag
2320
+ end
2321
+ }
2322
+ @open=token.open
2323
+ @close=token.close
2324
+ end
2325
+
2326
+ def old_cat_initialize(*tokens) #not needed anymore?
2327
+ token=tokens.shift
2328
+
2329
+ tokens.size==1 or fail "string node must be made from a single string token"
2330
+
2331
+ newdata=with_string_data(*tokens)
2332
+
2333
+ case token
2334
+ when HereDocNode:
2335
+ token.list_to_append=newdata
2336
+ when StringNode: #do nothing
2337
+ else fail "non-string token class used to construct string node"
2338
+ end
2339
+ replace token.data
2340
+
2341
+ # size%2==1 and last<<newdata.shift
2342
+ if size==1 and String===first and String===newdata.first
2343
+ first << newdata.shift
2344
+ end
2345
+ concat newdata
2346
+
2347
+ @implicit_match=false
2348
+ end
2349
+
2350
+ ESCAPABLES={}
2351
+ EVEN_NUM_BSLASHES=/(^|[^\\])((?:\\\\)*)/
2352
+ def unparse o
2353
+
2354
+ [@open,unparse_interior(o),@close,@modifiers].to_s
2355
+ end
2356
+
2357
+ def escapable open=@open,close=@close
2358
+ unless escapable=ESCAPABLES[open]
2359
+ maybe_crunch='#' if %r{\A["`/\{]\Z} === @char #"
2360
+ #crunch (#) might need to be escaped too, depending on what @char is
2361
+ escapable=ESCAPABLES[open]=
2362
+ /[#{open[-1,1]+close}#{maybe_crunch}]/
2363
+ end
2364
+ escapable
2365
+ end
2366
+
2367
+ def unparse_interior o,open=@open,close=@close
2368
+ escapable=escapable(open,close)
2369
+ map{|substr|
2370
+ if String===substr
2371
+ substr.gsub! /\\+/ do
2372
+ result=$&*2
2373
+ result.chomp! '\\' if $&.size&1==1
2374
+ result
2375
+ end
2376
+ substr.gsub! escapable do '\\'+$& end
2377
+ substr
2378
+ else
2379
+ ['#{',substr.unparse(o),'}']
2380
+ end
2381
+ }
2382
+ end
2383
+
2384
+ def image; '(#@char)' end
2385
+
2386
+ def delete_extraneous_ivars!
2387
+ @parses_like.delete_extraneous_ivars! if @parses_like
2388
+ return super
2389
+ end
2390
+
2391
+ def special_conditions!
2392
+ @implicit_match= @char=="/"
2393
+ end
2394
+
2395
+ attr_reader :modifiers,:char#,:data
2396
+ alias type char
2397
+
2398
+ def with_string_data(token)
2399
+ # token=tokens.first
2400
+
2401
+ # data=tokens.inject([]){|sum,token|
2402
+ # data=elems=token.string.elems
2403
+ data=elems=
2404
+ case token
2405
+ when StringToken: token.elems
2406
+ when HerePlaceholderToken: token.string.elems
2407
+ else raise "unknown string token type: #{token}:#{token.class}"
2408
+ end
2409
+ # sum.size%2==1 and sum.last<<elems.shift
2410
+ # sum+elems
2411
+ # }
2412
+ 1.step(data.length-1,2){|i|
2413
+ tokens=data[i].ident.dup
2414
+ (tokens.size-1).downto(0){|j|
2415
+ tok=tokens[j]
2416
+ break(tokens[j..-1]=[EoiToken.new('',nil,tokens[j].offset)]) if tok.ident=='}'
2417
+ }
2418
+ tokens.each_with_index{|tok,j| break(tokens.delete_at j) if tok.ident=='{' }
2419
+ if tokens.size==1 and VarNameToken===tokens.first
2420
+ data[i]=tokens.first
2421
+ else
2422
+ klass=Thread.current[:$RedParse_parser].class
2423
+ data[i]=klass.new(tokens, "(string inclusion)").parse
2424
+ end
2425
+ } #if data
2426
+ # was_nul_header= (String===data.first and data.first.empty?) #and quirks
2427
+ last=data.size-1
2428
+ last.downto(1){|frag_i|
2429
+ frag=data[frag_i]
2430
+ String===frag or next
2431
+ delete= frag.empty?
2432
+ next if frag_i==last #and quirks
2433
+ next if data[frag_i-1].line != data[frag_i+1].line #and quirks
2434
+ #prev and next inclusions on different lines
2435
+ data.slice!(frag_i) if delete
2436
+ }
2437
+ # data.unshift '' if was_nul_header
2438
+ return data
2439
+ end
2440
+
2441
+ def line= line
2442
+ each{|frag|
2443
+ frag.line||=line if frag.respond_to? :line
2444
+ }
2445
+
2446
+ super
2447
+ end
2448
+
2449
+ def to_lisp
2450
+ return %{"#{first}"} if size<=1 and @char=='"'
2451
+ huh
2452
+ end
2453
+
2454
+ def split_into_words strtok
2455
+ return unless /[{\[]/===@char
2456
+ result=ArrayLiteralNode[]
2457
+ result << StringNode['',{:@char=>'"'}]
2458
+ first[/\A(?:\s|\v)+/]='' if /\A(?:\s|\v)/===first
2459
+ each{|x|
2460
+ if String===x
2461
+ double_chunks=x.split(/((?:(?:[^\\]|\A)(?:\\\\)+)|(?:[^\\\s\v]|\A))(?:\s|\v)+/,-1)
2462
+ chunks=[]
2463
+ (0..double_chunks.size).step(2){|i|
2464
+ chunks << strtok.translate_escapes(double_chunks[i,2].to_s)#.gsub(/\\([\s\v\\])/){$1}
2465
+ }
2466
+
2467
+ chunk1= chunks.shift
2468
+ if chunk1.empty?
2469
+ #do nothing more
2470
+ elsif String===result.last.last
2471
+ result.last.last << chunk1
2472
+ else
2473
+ result.last.push chunk1
2474
+ end
2475
+ # result.last.last.empty? and result.last.pop
2476
+ result.concat chunks.map{|chunk| StringNode[chunk,{:@char=>'"'}]}
2477
+ else
2478
+ result.last << x
2479
+ end
2480
+ }
2481
+ result.shift if StringNode&-{:size=>1, :first=>''}===result.first
2482
+ result.pop if StringNode&-{:size=>1, :first=>''}===result.last
2483
+
2484
+ return result
2485
+ end
2486
+
2487
+ CHAROPT2NUM={
2488
+ ?x=>Regexp::EXTENDED,
2489
+ ?m=>Regexp::MULTILINE,
2490
+ ?i=>Regexp::IGNORECASE,
2491
+ ?o=>8,
2492
+ }
2493
+ CHARSETFLAG2NUM={
2494
+ ?n=>0x10,
2495
+ ?e=>0x20,
2496
+ ?s=>0x30,
2497
+ ?u=>0x40
2498
+ }
2499
+ CHAROPT2NUM.default=0
2500
+ CHARSETFLAG2NUM.default=0
2501
+ DOWNSHIFT_STRING_TYPE={
2502
+ :dregx=>:lit,
2503
+ :dregx_once=>:lit,
2504
+ :dstr=>:str,
2505
+ :dxstr=>:xstr,
2506
+ }
2507
+ def parsetree
2508
+ if size==1
2509
+ val=first
2510
+ type=case @char
2511
+ when '"',"'"; :str
2512
+ when '/'
2513
+ numopts=0
2514
+ charset=0
2515
+ @modifiers.each_byte{|ch|
2516
+ if ch==?o
2517
+ type=:dregx_once
2518
+ elsif numopt=CHAROPT2NUM[ch].nonzero?
2519
+ numopts|=numopt
2520
+ elsif set=CHARSETFLAG2NUM[ch].nonzero?
2521
+ charset=set
2522
+ else fail
2523
+ end
2524
+ }
2525
+ val=Regexp.new val,numopts|charset
2526
+ :lit
2527
+ when '[','{'
2528
+ return @parses_like.parsetree
2529
+
2530
+ double_chunks=val.split(/([^\\]|\A)(?:\s|\v)/,-1)
2531
+ chunks=[]
2532
+ (0..double_chunks.size).step(2){|i|
2533
+ chunks << double_chunks[i,2].to_s.gsub(/\\(\s|\v)/){$1}
2534
+ }
2535
+ # last=chunks
2536
+ # last.last.empty? and last.pop if last and !last.empty?
2537
+
2538
+ words=chunks#.flatten
2539
+ words.shift if words.first.empty? unless words.empty?
2540
+ words.pop if words.last.empty? unless words.empty?
2541
+ return [:zarray] if words.empty?
2542
+ return words.map{|word| [:str,word]}.unshift(:array)
2543
+ when '`'; :xstr
2544
+ else raise "dunno what to do with #@char<StringToken"
2545
+ end
2546
+ result=[type,val]
2547
+ else
2548
+ saw_string=false
2549
+ vals=[]
2550
+ each{|elem|
2551
+ case elem
2552
+ when String:
2553
+ if saw_string
2554
+ result=[:str, elem]
2555
+ else
2556
+ saw_string=true
2557
+ result=elem
2558
+ end
2559
+ vals.push result
2560
+ when NopNode:
2561
+ vals.push [:evstr]
2562
+ when Node,VarNameToken:
2563
+ res=elem.parsetree
2564
+ if res.first==:str and @char != '{'
2565
+ vals.push res
2566
+ elsif res.first==:dstr and @char != '{'
2567
+ vals.push [:str, res[1]], *res[2..-1]
2568
+ else
2569
+ vals.push [:evstr, res]
2570
+ end
2571
+ else fail "#{elem.class} not expected here"
2572
+ end
2573
+ }
2574
+ while vals.size>1 and vals[1].first==:str
2575
+ vals[0]<<vals.delete_at(1).last
2576
+ end
2577
+ #vals.pop if vals.last==[:str, ""]
2578
+
2579
+ type=case @char
2580
+ when '"'; :dstr
2581
+ when '/'
2582
+ type=:dregx
2583
+ numopts=charset=0
2584
+ @modifiers.each_byte{|ch|
2585
+ if ch==?o
2586
+ type=:dregx_once
2587
+ elsif numopt=CHAROPT2NUM[ch].nonzero?
2588
+ numopts|=numopt
2589
+ elsif set=CHARSETFLAG2NUM[ch].nonzero?
2590
+ charset=set
2591
+ end
2592
+ }
2593
+ vals.push numopts|charset unless numopts|charset==0
2594
+ val=/#{val}/
2595
+ type
2596
+ when '{':
2597
+ return @parses_like.parsetree
2598
+ vals[0]=vals[0].sub(/\A(\s|\v)+/,'') if /\A(\s|\v)/===vals.first
2599
+ merged=Array.new(vals)
2600
+ result=[]
2601
+ merged.each{|i|
2602
+ if String===i
2603
+ next if /\A(?:\s|\v)+\Z/===i
2604
+ double_chunks=i.split(/([^\\]|\A)(?:\s|\v)/,-1)
2605
+ chunks=[]
2606
+ (0..double_chunks.size).step(2){|ii|
2607
+ chunks << double_chunks[ii,2].to_s.gsub(/\\(\s|\v)/){$1}
2608
+ }
2609
+ words=chunks.map{|word| [:str,word]}
2610
+ if !result.empty? and frag=words.shift and !frag.last.empty?
2611
+ result[-1]+=frag
2612
+ end
2613
+ result.push *words
2614
+ else
2615
+ result.push [:str,""] if result.empty?
2616
+ if i.first==:evstr and i.size>1 and i.last.first==:str
2617
+ if String===result.last[-1]
2618
+ result.last[-1]+=i.last.last
2619
+ else
2620
+ result.last[0]=:dstr
2621
+ result.last.push(i.last)
2622
+ end
2623
+ else
2624
+ result.last[0]=:dstr
2625
+ result.last.push(i)
2626
+ end
2627
+ end
2628
+ }
2629
+ return result.unshift(:array)
2630
+ when '`'; :dxstr
2631
+ else raise "dunno what to do with #@char<StringToken"
2632
+ end
2633
+
2634
+ if vals.size==1
2635
+ vals=[/#{vals[0]}/] if :dregx==type or :dregx_once==type
2636
+ type=DOWNSHIFT_STRING_TYPE[type]
2637
+ end
2638
+ result= vals.unshift(type)
2639
+ end
2640
+ result=[:match, result] if defined? @implicit_match and @implicit_match
2641
+ return result
2642
+ end
2643
+ end
2644
+
2645
+ class HereDocNode<StringNode
2646
+ param_names :token
2647
+ def initialize(token)
2648
+ token.node=self
2649
+ super(token)
2650
+ end
2651
+ attr_accessor :list_to_append
2652
+ # attr :token
2653
+
2654
+ def saw_body! #not used
2655
+ replace with_string_data(token)
2656
+ @char=token.quote
2657
+ if @list_to_append
2658
+ size%2==1 and token << @list_to_append.shift
2659
+ push *@list_to_append
2660
+ remove_instance_variable :@list_to_append
2661
+ end
2662
+ end
2663
+
2664
+
2665
+ def flattened_ivars_equal?(other)
2666
+ StringNode===other
2667
+ end
2668
+
2669
+ def unparse o
2670
+ [@char,(unparse_interior o,@char,@char),@char].to_s
2671
+ end
2672
+ end
2673
+
2674
+ class LiteralNode<ValueNode
2675
+ param_names :val
2676
+ attr_accessor :offset
2677
+ def initialize(old_val)
2678
+ @offset=old_val.offset
2679
+ val=old_val.ident
2680
+ case old_val
2681
+ when SymbolToken:
2682
+ case val[1]
2683
+ when ?': #'
2684
+ assert !old_val.raw.has_str_inc?
2685
+ val=old_val.raw.translate_escapes(old_val.raw.elems.first).to_sym
2686
+ #val=eval(val[1..-1]).to_sym
2687
+ #eval here is cheating!!!!
2688
+ #too lazy to do it right just now
2689
+ when ?": #"
2690
+ if old_val.raw.has_str_inc?
2691
+ val=StringNode.new(old_val.raw) #ugly hack: this isn't literal
2692
+ else
2693
+ val=old_val.raw.translate_escapes(old_val.raw.elems.first).to_sym
2694
+ #val=eval(val[1..-1]).to_sym
2695
+ #eval here is cheating!!!!
2696
+ #too lazy to do it right just now
2697
+ end
2698
+ else #val=val[1..-1].to_sym
2699
+ if StringToken===old_val.raw
2700
+ val=old_val.raw.translate_escapes(old_val.raw.elems.first).to_sym
2701
+ else
2702
+ val=old_val.raw.to_sym
2703
+ end
2704
+ end
2705
+ when NumberToken:
2706
+ case val
2707
+ when /\A-?0([^.]|\Z)/: val=val.oct
2708
+ when /[.e]/i: val=val.to_f
2709
+ else val=val.to_i
2710
+ end
2711
+ end
2712
+ super(val)
2713
+ end
2714
+
2715
+ def image; "(#{':' if Symbol===val}#{val})" end
2716
+
2717
+ def to_lisp
2718
+ return val.to_s
2719
+ end
2720
+
2721
+ Inf="999999999999999999999999999999999.9e999999999999999999999999999999999999"
2722
+ Nan="****shouldnt ever happen****"
2723
+
2724
+ def unparse o
2725
+ val=val()
2726
+ case val
2727
+ when StringNode #ugly hack
2728
+ ":"+
2729
+ val.unparse(o)
2730
+ when Float
2731
+ s= "%#{Float::DIG}.#{Float::DIG}f"%val
2732
+ case s
2733
+ when /-inf/i: s="-"+Inf
2734
+ when /inf/i: s= Inf
2735
+ when /nan/i: s= Nan
2736
+ else
2737
+ fail unless [s.to_f].pack("d")==[val].pack("d")
2738
+ end
2739
+ s
2740
+ else val.inspect
2741
+ end
2742
+ end
2743
+
2744
+ def parsetree
2745
+ val=val()
2746
+ case val
2747
+ when StringNode #ugly hack
2748
+ result= val.parsetree
2749
+ result[0]=:dsym
2750
+ return result
2751
+ =begin
2752
+ when String
2753
+ #float or real string? or keyword?
2754
+ val=
2755
+ case val
2756
+ when Numeric: val
2757
+ when Symbol: val
2758
+ when String: val
2759
+ when "true": true
2760
+ when "false": false
2761
+ when "nil": nil
2762
+ when "self": return :self
2763
+ when "__FILE__": "wrong-o"
2764
+ when "__LINE__": "wrong-o"
2765
+ else fail "unknown token type in LiteralNode: #{val.class}"
2766
+ end
2767
+ =end
2768
+ end
2769
+ return [:lit,val]
2770
+ end
2771
+ end
2772
+
2773
+ class VarLikeNode<ValueNode #nil,false,true,__FILE__,__LINE__,self
2774
+ param_names :name
2775
+ def initialize(name,*more)
2776
+ if name.ident=='('
2777
+ #simulate nil
2778
+ replace ['nil']
2779
+ else
2780
+ replace [name.ident]
2781
+ @value=name.respond_to?(:value) && name.value
2782
+ end
2783
+ end
2784
+
2785
+ alias ident name
2786
+
2787
+ def image; "(#{name})" end
2788
+
2789
+ def to_lisp
2790
+ name
2791
+ end
2792
+
2793
+ def unparse o
2794
+ name
2795
+ end
2796
+
2797
+ def parsetree
2798
+ if @value
2799
+ type=:lit
2800
+ val=@value
2801
+ if name=="__FILE__"
2802
+ type=:str
2803
+ val="(string)" if val=="-"
2804
+ end
2805
+ [type,val]
2806
+ else
2807
+ [name.to_sym]
2808
+ end
2809
+ end
2810
+ end
2811
+
2812
+ class ArrayLiteralNode<ValueNode
2813
+ def initialize(lbrack,contents,rbrack)
2814
+ contents or return super()
2815
+ if CommaOpNode===contents
2816
+ super *contents
2817
+ else
2818
+ super contents
2819
+ end
2820
+ end
2821
+
2822
+ def image; "([])" end
2823
+
2824
+ def unparse o
2825
+ "["+map{|item| item.unparse o}.join(', ')+"]"
2826
+ end
2827
+
2828
+ def parsetree
2829
+ size.zero? and return [:zarray]
2830
+ normals,star,amp=param_list_parse(self)
2831
+ result=normals.unshift :array
2832
+ if star
2833
+ if size==1
2834
+ result=star
2835
+ else
2836
+ result=[:argscat, result, star.last]
2837
+ end
2838
+ end
2839
+ result
2840
+ end
2841
+
2842
+ end
2843
+ #ArrayNode=ValueNode
2844
+
2845
+ class BracketsSetNode < ValueNode #obsolete
2846
+ param_names :left,:assign_,:right
2847
+ def parsetree
2848
+ [:attrasgn, left.data[0].parsetree, :[]=,
2849
+ [:array]+Array(left.data[1]).map{|x| x.parsetree}<< right.parsetree
2850
+ ]
2851
+ end
2852
+ end
2853
+
2854
+ class BracketsModifyNode < ValueNode #obsolete
2855
+ param_names :left,:assignop,:right
2856
+ def initialize(left,assignop,right)
2857
+ super
2858
+ end
2859
+
2860
+ def parsetree
2861
+ bracketargs=@data[0].data[1]
2862
+ bracketargs=bracketargs ? bracketargs.map{|x| x.parsetree}.unshift(:array) : [:zarray]
2863
+ [:op_asgn1, @data[0].data[0].parsetree, bracketargs,
2864
+ data[1].ident.chomp('=').to_sym, data[2].parsetree]
2865
+ end
2866
+ end
2867
+
2868
+ class IfNode < ValueNode
2869
+ param_names :condition,:consequent,:elsifs,:otherwise
2870
+ def initialize(iftok,condition,thentok,consequent,elsifs,else_,endtok)
2871
+ if else_
2872
+ else_=else_.val or @empty_else=true
2873
+ end
2874
+ condition.special_conditions! if condition.respond_to? :special_conditions!
2875
+ super(condition,consequent,elsifs,else_)
2876
+ @reverse= iftok.ident=="unless"
2877
+ if @reverse
2878
+ @iftok_offset=iftok.offset
2879
+ fail "elsif not allowed with unless" unless elsifs.empty?
2880
+ end
2881
+ end
2882
+ alias if condition
2883
+ alias then consequent
2884
+ alias else otherwise
2885
+ alias else_ else
2886
+ alias if_ if
2887
+ alias then_ then
2888
+
2889
+ attr_reader :empty_else
2890
+
2891
+ def unparse o
2892
+ result=@reverse ? "unless " : "if "
2893
+ result+="#{condition.unparse o}\n"
2894
+ result+="#{consequent.unparse(o)}\n" if consequent
2895
+ result+=elsifs.map{|n| n.unparse(o)}.to_s if elsifs
2896
+ result+="else "+else_.unparse(o)+"\n" if else_
2897
+ result+="end"
2898
+ return result
2899
+ end
2900
+
2901
+ def image; "(if)" end
2902
+
2903
+ def if
2904
+ if @reverse
2905
+ negate condition, @iftok_offset
2906
+ else
2907
+ condition
2908
+ end
2909
+ end
2910
+
2911
+ def then
2912
+ @reverse ? otherwise : consequent
2913
+ end
2914
+
2915
+ def else
2916
+ @reverse ? consequent : otherwise
2917
+ end
2918
+
2919
+ def to_lisp
2920
+ if elsifs.empty?
2921
+ "(#{@reverse ? :unless : :if} #{condition.to_lisp}\n"+
2922
+ "(then #{consequent.to_lisp})\n(else #{otherwise.to_lisp}))"
2923
+ else
2924
+ "(cond (#{condition.to_lisp} #{consequent.to_lisp})\n"+
2925
+ elsifs.map{|x| x.to_lisp}.join("\n")+
2926
+ "\n(else #{otherwise.to_lisp})"+
2927
+ "\n)"
2928
+ end
2929
+ end
2930
+
2931
+ def parsetree
2932
+ elsepart=otherwise.parsetree if otherwise
2933
+ elsifs.reverse_each{|elsifnode|
2934
+ elsepart=elsifnode.parsetree << elsepart
2935
+ }
2936
+ cond=condition.rescue_parsetree
2937
+ actions=[
2938
+ consequent&&consequent.parsetree,
2939
+ elsepart
2940
+ ]
2941
+ if cond.first==:not
2942
+ cond=cond.last
2943
+ reverse=!@reverse
2944
+ else
2945
+ reverse=@reverse
2946
+ end
2947
+ actions.reverse! if reverse
2948
+ result=[:if, cond, *actions]
2949
+ return result
2950
+ end
2951
+ end
2952
+
2953
+ class ElseNode<Node #not to appear in final tree
2954
+ param_names :elseword_,:val
2955
+ alias body val
2956
+
2957
+ def image; "(else)" end
2958
+
2959
+ def to_lisp
2960
+ "(else #{body.to_lisp})"
2961
+ end
2962
+ end
2963
+
2964
+ class EnsureNode<Node #not to appear in final tree
2965
+ param_names :ensureword_, :val
2966
+ alias body val
2967
+ def image; "(ensure)" end
2968
+ def parsetree #obsolete?
2969
+ (body=body()) ? body.parsetree : [:nil]
2970
+ end
2971
+ end
2972
+
2973
+ class ElsifNode<Node
2974
+ param_names(:elsifword_,:condition,:thenword_,:consequent)
2975
+ def initialize(elsifword,condition,thenword,consequent)
2976
+ condition.special_conditions! if condition.respond_to? :special_conditions!
2977
+ super(condition,consequent)
2978
+ end
2979
+
2980
+ alias if condition
2981
+ alias elsif if
2982
+ alias then consequent
2983
+
2984
+ def image; "(elsif)" end
2985
+
2986
+ def unparse o
2987
+ "elsif #{condition.unparse o}\n#{consequent.unparse o}\n"
2988
+ end
2989
+
2990
+ def to_lisp
2991
+ "("+condition.to_lisp+" "+consequent.to_lisp+")"
2992
+ end
2993
+
2994
+ def parsetree #obsolete?
2995
+ [:if, condition.rescue_parsetree, consequent&&consequent.parsetree, ]
2996
+ end
2997
+ end
2998
+
2999
+ class LoopNode<ValueNode
3000
+ #this class should be abstract and have 2 concrete descendants for while and until
3001
+ param_names :condition, :body
3002
+ def initialize(loopword,condition,thenword,body,endtok)
3003
+ condition.special_conditions! if condition.respond_to? :special_conditions!
3004
+ super(condition,body)
3005
+ @reverse= loopword.ident=="until"
3006
+ @loopword_offset=loopword.offset
3007
+ end
3008
+
3009
+ alias do body
3010
+
3011
+ def image; "(#{loopword})" end
3012
+
3013
+ def unparse o
3014
+ [@reverse? "until " : "while ",
3015
+ condition.unparse(o), "\n",
3016
+ body&&body.unparse(o),
3017
+ "\nend"
3018
+ ].to_s
3019
+ end
3020
+
3021
+ def while
3022
+ @reverse ? negate(condition, @loopword_offset) : condition
3023
+ end
3024
+
3025
+ def until
3026
+ @reverse ? condition : negate(condition, @loopword_offset)
3027
+ end
3028
+
3029
+ def to_lisp
3030
+ body=body()
3031
+ "(#{@reverse ? :until : :while} #{condition.to_lisp}\n#{body.to_lisp})"
3032
+ end
3033
+
3034
+ def parsetree
3035
+ cond=condition.rescue_parsetree
3036
+ if cond.first==:not
3037
+ reverse=!@reverse
3038
+ cond=cond.last
3039
+ else
3040
+ reverse=@reverse
3041
+ end
3042
+ [reverse ? :until : :while, cond, body&&body.parsetree, true]
3043
+ end
3044
+ end
3045
+
3046
+ class CaseNode<ValueNode
3047
+ param_names(:case!,:whens,:else!)
3048
+ alias condition case
3049
+ alias otherwise else
3050
+
3051
+ def initialize(caseword, condition, semi, whens, otherwise, endword)
3052
+ if otherwise
3053
+ otherwise=otherwise.val or @empty_else=true
3054
+ end
3055
+ super(condition,whens,otherwise)
3056
+ end
3057
+
3058
+ attr_reader :empty_else
3059
+
3060
+ def unparse o
3061
+ result="case #{condition&&condition.unparse(o)}\n"+
3062
+ whens.map{|wh| wh.unparse o}.to_s
3063
+
3064
+ result += "else "+otherwise.unparse(o)+"\n" if otherwise
3065
+ result += "end"
3066
+
3067
+ return result
3068
+ end
3069
+
3070
+ def image; "(case)" end
3071
+
3072
+ def to_lisp
3073
+ "(case #{case_.to_lisp}\n"+
3074
+ whens.map{|x| x.to_lisp}.join("\n")+"\n"+
3075
+ "(else #{else_.to_lisp}"+
3076
+ "\n)"
3077
+ end
3078
+
3079
+ def parsetree
3080
+ [:case, condition&&condition.parsetree]+
3081
+ whens.map{|whennode| whennode.parsetree}+
3082
+ [otherwise&&otherwise.parsetree]
3083
+ end
3084
+ end
3085
+
3086
+ class WhenNode<Node #not to appear in final tree?
3087
+ param_names(:whenword_,:when!,:thenword_,:then!)
3088
+ def initialize(whenword,when_,thenword,then_)
3089
+ when_=Array.new(when_) if CommaOpNode===when_
3090
+ super(when_,then_)
3091
+ end
3092
+ alias body then
3093
+ alias consequent then
3094
+ alias condition when
3095
+
3096
+ def image; "(when)" end
3097
+
3098
+ def unparse o
3099
+ result="when "
3100
+ result+=condition.class==Array ?
3101
+ condition.map{|cond| cond.unparse(o)}.join(',') :
3102
+ condition.unparse(o)
3103
+ result+="\n"
3104
+ result+=consequent.unparse(o)+"\n" if consequent
3105
+ result
3106
+ end
3107
+
3108
+ def to_lisp
3109
+ unless Node|Token===condition
3110
+ "(when (#{condition.map{|cond| cond.to_lisp}.join(" ")}) #{
3111
+ consequent&&consequent.to_lisp
3112
+ })"
3113
+ else
3114
+ "(#{when_.to_lisp} #{then_.to_lisp})"
3115
+ end
3116
+
3117
+
3118
+ end
3119
+
3120
+ def parsetree
3121
+ conds=
3122
+ if Node|Token===condition
3123
+ [condition.rescue_parsetree]
3124
+ else
3125
+ condition.map{|cond| cond.rescue_parsetree}
3126
+ end
3127
+ if conds.last[0]==:splat
3128
+ conds.last[0]=:when
3129
+ conds.last.push nil
3130
+ end
3131
+ [:when, [:array, *conds],
3132
+ consequent&&consequent.parsetree
3133
+ ]
3134
+ end
3135
+ end
3136
+
3137
+ class ForNode<ValueNode
3138
+ param_names(:forword_,:for!,:inword_,:in!,:doword_,:do!,:endword_)
3139
+ def initialize(forword,for_,inword,in_,doword,do_,endword)
3140
+ #elide 1 layer of parens if present
3141
+ for_=for_.first if ParenedNode===for_ and for_.size==1
3142
+ for_=CommaOpNode===for_ ? Array.new(for_) : [for_]
3143
+ super(BlockParams.new(for_),in_,do_)
3144
+ end
3145
+
3146
+ alias body do
3147
+ alias enumerable in
3148
+ alias iterator for
3149
+
3150
+ def image; "(for)" end
3151
+
3152
+ def unparse o
3153
+ "
3154
+ for #{iterator.lhs_unparse(o)[1...-1]} in #{enumerable.unparse o}
3155
+ #{body&&body.unparse(o)}
3156
+ end"
3157
+ end
3158
+
3159
+ def parsetree
3160
+ =begin
3161
+ case vars=@data[0]
3162
+ when Node:
3163
+ vars=vars.parsetree
3164
+ if vars.first==:call
3165
+ vars[0]=:attrasgn
3166
+ vars[2]="#{vars[2]}=".to_sym
3167
+ end
3168
+ vars
3169
+ when Array:
3170
+ vars=[:masgn, [:array,
3171
+ *vars.map{|lval|
3172
+ res=lval.parsetree
3173
+ res[0]=lval.varname2assigntype if VarNameToken===lval
3174
+ res
3175
+ }
3176
+ ]]
3177
+ when VarNameToken:
3178
+ ident=vars.ident
3179
+ vars=vars.parsetree
3180
+ (vars[0]=vars.varname2assigntype) rescue nil
3181
+ else fail
3182
+ end
3183
+ =end
3184
+
3185
+ vars=self.for.lvalue_parsetree
3186
+ result=[:for, self.in.begin_parsetree, vars]
3187
+ result.push self.do.parsetree if self.do
3188
+ result
3189
+ end
3190
+
3191
+ end
3192
+
3193
+
3194
+ class HashLiteralNode<ValueNode
3195
+ def initialize(open,contents,close)
3196
+ case contents
3197
+ when nil: super()
3198
+ when ArrowOpNode: super(contents.first,contents.last)
3199
+ when CommaOpNode,Array
3200
+ if ArrowOpNode===contents.first
3201
+ data=[]
3202
+ contents.each{|pair|
3203
+ ArrowOpNode===pair or fail
3204
+ data.push pair.first,pair.last
3205
+ }
3206
+ else
3207
+ data=Array[*contents]
3208
+ end
3209
+ super(*data)
3210
+ end
3211
+ @no_braces=true unless open
3212
+ end
3213
+
3214
+ def image; "({})" end
3215
+
3216
+ def unparse o
3217
+ result=''
3218
+ result << "{" unless @no_braces
3219
+ (0...size).step(2){|i|
3220
+ result<<
3221
+ self[i].unparse(o)+' => '+
3222
+ self[i+1].unparse(o)+', '
3223
+ }
3224
+ result.chomp! ', '
3225
+ result << "}" unless @no_braces
3226
+ return result
3227
+ end
3228
+
3229
+ def parsetree
3230
+ map{|elem| elem.rescue_parsetree}.unshift :hash
3231
+ end
3232
+ end
3233
+
3234
+ class TernaryNode<ValueNode
3235
+ param_names :if!,:qm_,:then!,:colon_,:else!
3236
+ alias condition if
3237
+ alias consequent then
3238
+ alias otherwise else
3239
+ def initialize(if_,qm,then_,colon,else_)
3240
+ super(if_,then_,else_)
3241
+ condition.special_conditions! if condition.respond_to? :special_conditions!
3242
+ end
3243
+
3244
+ def image; "(?:)" end
3245
+
3246
+ def unparse o
3247
+ "#{condition.unparse o} ? #{consequent.unparse o} : #{otherwise.unparse o}"
3248
+ end
3249
+
3250
+ def elsifs; [] end
3251
+
3252
+ def parsetree
3253
+ cond=condition.rescue_parsetree
3254
+ cond[0]=:fcall if cond[0]==:vcall and cond[1].to_s[/[?!]$/]
3255
+ [:if, cond, consequent.begin_parsetree, otherwise.begin_parsetree]
3256
+ end
3257
+ end
3258
+
3259
+ class MethodNode<ValueNode
3260
+ param_names(:defword_,:receiver,:name,:maybe_eq_,:args,:semi_,:body,:rescues,:elses,:ensures,:endword_)
3261
+ alias ensure_ ensures
3262
+ alias else_ elses
3263
+ alias ensure ensures
3264
+ alias else elses
3265
+ alias params args
3266
+
3267
+ def initialize(defword_,header,maybe_eq_,semi_,
3268
+ body,rescues,else_,ensure_,endword_)
3269
+ @empty_ensure=nil
3270
+ # if DotCallNode===header
3271
+ # header=header.data[1]
3272
+ # end
3273
+ if CallSiteNode===header
3274
+ receiver=header.receiver
3275
+ args=header.args
3276
+ header=header.name
3277
+ end
3278
+ if MethNameToken===header
3279
+ header=header.ident
3280
+ end
3281
+ unless String===header
3282
+ fail "unrecognized method header: #{header}"
3283
+ end
3284
+ if else_
3285
+ else_=else_.val or @empty_else=true
3286
+ end
3287
+ if ensure_
3288
+ ensure_=ensure_.val or @empty_ensure=true
3289
+ end
3290
+ replace [receiver,header,args,body,rescues,else_,ensure_]
3291
+ end
3292
+
3293
+ attr_reader :empty_ensure, :empty_else
3294
+
3295
+ def receiver= x
3296
+ self[0]=x
3297
+ end
3298
+
3299
+ def body= x
3300
+ self[3]=x
3301
+ end
3302
+
3303
+ def ensure_= x
3304
+ self[5]=x
3305
+ end
3306
+
3307
+ def else_= x
3308
+ self[6]=x
3309
+ end
3310
+
3311
+ def image
3312
+ "(def #{receiver.image.+('.') if receiver}#{name})"
3313
+ end
3314
+
3315
+ def unparse o
3316
+ result="
3317
+ def #{receiver&&receiver.unparse(o)+'.'}#{name}#{
3318
+ args&&'('+args.map{|arg| arg.unparse o}.join(',')+')'
3319
+ }
3320
+ #{body&&body.unparse(o)}
3321
+ "
3322
+ result+=rescues.map{|resc| resc.unparse o}.to_s
3323
+ result+="else #{else_.unparse o}\n" if else_
3324
+ result+="ensure #{ensure_.unparse o}\n" if ensure_
3325
+ result+="end"
3326
+ end
3327
+
3328
+ def to_lisp
3329
+ "(imethod #{name} is\n#{body.to_lisp}\n)\n"
3330
+ #no receiver, args, rescues, else_ or ensure_...
3331
+ end
3332
+
3333
+ def parsetree
3334
+ name=name().to_sym
3335
+
3336
+ result=[name, target=[:scope, [:block, ]] ]
3337
+ if receiver
3338
+ result.unshift :defs, receiver.rescue_parsetree
3339
+ else
3340
+ result.unshift :defn
3341
+ end
3342
+
3343
+ goodies= (body or !rescues.empty? or elses or ensures or @empty_ensure) # or args())
3344
+
3345
+ if unamp=args() and unamp=unamp.last and UnOpNode===unamp and unamp.op=="&@"
3346
+ receiver and goodies=true
3347
+ else
3348
+ unamp=false
3349
+ end
3350
+
3351
+ if receiver and !goodies
3352
+ target.delete_at 1 #omit :block
3353
+ else
3354
+ target=target[1]
3355
+ end
3356
+
3357
+ target.push args=[:args,]
3358
+ target.push unamp.parsetree if unamp
3359
+
3360
+ if args()
3361
+ initvals=[]
3362
+ args().each{|arg|
3363
+ case arg
3364
+ when VarNameToken:
3365
+ args.push arg.ident.to_sym
3366
+ when UnaryStarNode:
3367
+ args.push "*#{arg.val.ident}".to_sym
3368
+ when UnOpNode:
3369
+ nil
3370
+ when AssignNode:
3371
+ initvals << arg.parsetree
3372
+ initvals[-1][-1]=arg.right.rescue_parsetree #ugly
3373
+ args.push arg[0].ident.to_sym
3374
+ else
3375
+ fail "unsupported node type in method param list: #{arg}"
3376
+ end
3377
+ }
3378
+ unless initvals.empty?
3379
+ initvals.unshift(:block)
3380
+ args << initvals
3381
+ args[-2][0]==:block_arg and target.push args.delete_at(-2)
3382
+ end
3383
+ end
3384
+ target.push [:nil] if !goodies && !receiver
3385
+ target.push ensuretarget=target=[:ensure, ] if ensures or @empty_ensure
3386
+ #simple dup won't work... won't copy extend'd modules
3387
+ body=Marshal.load(Marshal.dump(body())) if body()
3388
+ elses=elses()
3389
+ if rescues.empty?
3390
+ case body
3391
+ when SequenceNode: body << elses;elses=nil
3392
+ when nil: body=elses;elses=nil
3393
+ else nil
3394
+ end if elses
3395
+ else
3396
+ target.push target=[:rescue, ]
3397
+ elses=elses()
3398
+ end
3399
+ if body
3400
+ if ParenedNode===body and body.size>1 and
3401
+ body.rescues.empty? and !body.ensure and !body.empty_ensure and body.body and body.body.size>1
3402
+ wantblock=true
3403
+ end
3404
+ body=body.parsetree
3405
+ if body.first==:block and rescues.empty? and not ensures||@empty_ensure
3406
+ if wantblock
3407
+ target.push body
3408
+ else
3409
+ body.shift
3410
+ target.concat body
3411
+ end
3412
+ else
3413
+ body=[:block, *body] if wantblock
3414
+ target.push body
3415
+ end
3416
+ end
3417
+ target.push linked_list(rescues.map{|rescue_| rescue_.parsetree }) unless rescues.empty?
3418
+ target.push elses.parsetree if elses
3419
+ ensuretarget.push ensures.parsetree if ensures
3420
+ ensuretarget.push [:nil] if @empty_ensure
3421
+
3422
+ return result
3423
+ end
3424
+ end
3425
+
3426
+ module BareSymbolUtils
3427
+ def baresym2str(node)
3428
+ case node
3429
+ when MethNameToken: node.ident
3430
+ when VarNameToken: node
3431
+ when LiteralNode:
3432
+ case node.val
3433
+ when Symbol:
3434
+ node.val.to_s
3435
+ when StringNode: node.val
3436
+ # when StringToken: StringNode.new(node.val)
3437
+ else fail
3438
+ end
3439
+ end
3440
+ end
3441
+
3442
+ def str_unparse(str,o)
3443
+ case str
3444
+ when VarNameToken: str.ident
3445
+ when String:
3446
+ str.to_sym.inspect
3447
+ #what if str isn't a valid method or operator name? should be quoted
3448
+ when StringNode:
3449
+ ":"+str.unparse(o)
3450
+ else fail
3451
+ end
3452
+ end
3453
+
3454
+ def str2parsetree(str)
3455
+ if String===str then [:lit, str.to_sym]
3456
+ else
3457
+ result=str.parsetree
3458
+ result[0]=:dsym
3459
+ result
3460
+ end
3461
+ end
3462
+ end
3463
+
3464
+ class AliasNode < ValueNode
3465
+ include BareSymbolUtils
3466
+ param_names(:aliasword_,:to,:from)
3467
+ def initialize(aliasword,to,from)
3468
+ to=baresym2str(to)
3469
+ from=baresym2str(from)
3470
+ super(to,from)
3471
+ end
3472
+
3473
+ def unparse o
3474
+ "alias #{str_unparse to,o} #{str_unparse from,o}"
3475
+ end
3476
+
3477
+ def image; "(alias)" end
3478
+ def parsetree
3479
+ if VarNameToken===to and to.ident[0]==?$
3480
+ [:valias, to.ident.to_sym, from.ident.to_sym]
3481
+ else
3482
+ [:alias, str2parsetree(to), str2parsetree(from)]
3483
+ end
3484
+ end
3485
+ end
3486
+
3487
+ class UndefNode < ValueNode
3488
+ include BareSymbolUtils
3489
+ def initialize(first,middle,last=nil)
3490
+ if last
3491
+ node,newsym=first,last
3492
+ super(*node << baresym2str(newsym))
3493
+ else
3494
+ super(baresym2str(middle))
3495
+ end
3496
+ end
3497
+
3498
+ def image; "(undef)" end
3499
+
3500
+ def unparse o
3501
+ "undef #{map{|name| str_unparse name,o}.join(', ')}"
3502
+ end
3503
+
3504
+ def parsetree
3505
+ result=map{|name| [:undef, str2parsetree(name)] }
3506
+ if result.size==1
3507
+ result.first
3508
+ else
3509
+ result.unshift :block
3510
+ end
3511
+ end
3512
+ end
3513
+
3514
+ class NamespaceNode<ValueNode
3515
+ end
3516
+
3517
+ class ModuleNode<NamespaceNode
3518
+ param_names(:moduleword_,:name,:semi_,:body,:endword_)
3519
+
3520
+ def image; "(module #{name})" end
3521
+
3522
+ def unparse o
3523
+ "module #{name.unparse o}\n#{body&&body.unparse(o)}\nend"
3524
+ end
3525
+
3526
+ def parent; nil end
3527
+ def to_lisp
3528
+ result="(#{name.ident} #{body.to_lisp} "
3529
+ #parent=@data[2]
3530
+ #result+="is #{parent.to_lisp} " if parent
3531
+
3532
+ result+="\n"+body.to_lisp+")"
3533
+ return result
3534
+ end
3535
+
3536
+ def parsetree
3537
+ name=name()
3538
+ if VarNameToken===name
3539
+ name=name.ident.to_sym
3540
+ elsif name.nil? #do nothing
3541
+ # elsif quirks
3542
+ # name=name.constant.ident.to_sym
3543
+ else
3544
+ name=name.parsetree
3545
+ end
3546
+ result=[:module, name, scope=[:scope, ]]
3547
+ body&&scope << body.parsetree
3548
+ return result
3549
+ end
3550
+ end
3551
+
3552
+ class ClassNode<NamespaceNode
3553
+ param_names(:name,:parent,:body)
3554
+ def initialize(classword,name,semi,body,endword)
3555
+ if OpNode===name
3556
+ name,op,parent=*name
3557
+ op=="<" or fail "invalid superclass expression: #{name}"
3558
+ end
3559
+ super(name,parent,body)
3560
+ end
3561
+
3562
+ def image; "(class #{name})" end
3563
+
3564
+ def unparse o
3565
+ result="class #{name.unparse o}"
3566
+ result+=" < #{parent.unparse o}" if parent
3567
+ result+="\n#{body&&body.unparse(o)}\nend"
3568
+ return result
3569
+ end
3570
+
3571
+ def to_lisp
3572
+ result="(class #{name.to_lisp} "
3573
+ result+="is #{parent.to_lisp} " if parent
3574
+
3575
+ result+="\n"+body.to_lisp+")"
3576
+ return result
3577
+ end
3578
+
3579
+ def parsetree
3580
+ name=name()
3581
+ if VarNameToken===name
3582
+ name=name.ident.to_sym
3583
+ elsif name.nil? #do nothing
3584
+ # elsif quirks
3585
+ # name=name.constant.ident.to_sym
3586
+ else
3587
+ name=name.parsetree
3588
+ end
3589
+ result=[:class, name, parent&&parent.parsetree, scope=[:scope,]]
3590
+ body&&scope << body.parsetree
3591
+ return result
3592
+ end
3593
+ end
3594
+
3595
+ class MetaClassNode<NamespaceNode
3596
+ param_names :classword_, :leftleft_, :val, :semi_, :body, :endword_
3597
+ alias expr val
3598
+ alias object val
3599
+ alias obj val
3600
+ alias receiver val
3601
+ alias name val
3602
+
3603
+ def image; "(class<<)" end
3604
+
3605
+ def unparse o
3606
+ "class << #{obj.unparse o}\n#{body&&body.unparse(o)}\nend"
3607
+ end
3608
+
3609
+ def parsetree
3610
+ result=[:sclass, expr.parsetree, scope=[:scope]]
3611
+ body&&scope << body.parsetree
3612
+ return result
3613
+ end
3614
+ end
3615
+
3616
+ class RescueHeaderNode<Node #not to appear in final tree
3617
+ param_names :exceptions,:varname
3618
+ def initialize(rescueword,arrowword,exceptions,thenword)
3619
+ case exceptions
3620
+ when nil
3621
+ when VarNameToken:
3622
+ if arrowword
3623
+ exvarname=exceptions
3624
+ exceptions=nil
3625
+ arrowword=nil
3626
+ end
3627
+ when ArrowOpNode:
3628
+ exvarname=exceptions.last
3629
+ exceptions=exceptions.first
3630
+ when CommaOpNode
3631
+ lastexpr=exceptions.last
3632
+ if ArrowOpNode===lastexpr
3633
+ exceptions[-1]=lastexpr.left
3634
+ exvarname=lastexpr.right
3635
+ end
3636
+ exceptions=Array.new(exceptions)
3637
+ end
3638
+ fail if arrowword
3639
+ # fail unless VarNameToken===exvarname || exvarname.nil?
3640
+ super(exceptions,exvarname)
3641
+ end
3642
+
3643
+ def image; "(rescue=>)" end
3644
+ end
3645
+
3646
+ class RescueNode<Node
3647
+ param_names :exceptions,:varname,:action
3648
+ def initialize(rescuehdr,action,semi)
3649
+ exlist=rescuehdr.exceptions||[]
3650
+ exlist=[exlist] unless exlist.class==Array
3651
+ fail unless exlist.class==Array
3652
+ super(exlist,rescuehdr.varname,action)
3653
+ end
3654
+
3655
+ def unparse o
3656
+ xx=exceptions.map{|exc| exc.unparse o}.join(',')
3657
+ "rescue #{xx} #{varname&&'=> '+varname.lhs_unparse(o)}\n#{action&&action.unparse(o)}\n"
3658
+ end
3659
+
3660
+ def parsetree
3661
+ result=[:resbody, nil]
3662
+ fail unless exceptions.class==Array
3663
+ ex=#if Node===exceptions; [exceptions.rescue_parsetree]
3664
+ #elsif exceptions
3665
+ exceptions.map{|exc| exc.rescue_parsetree}
3666
+ #end
3667
+ if !ex or ex.empty? #do nothing
3668
+ elsif ex.last.first!=:splat
3669
+ result[1]= [:array, *ex]
3670
+ elsif ex.size==1
3671
+ result[1]= ex.first
3672
+ else
3673
+ result[1]= [:argscat, ex[0...-1].unshift(:array), ex.last[1]]
3674
+ end
3675
+ VarNameToken===varname and offset=varname.offset
3676
+ action=if varname
3677
+ SequenceNode.new(
3678
+ AssignNode.new(
3679
+ varname,
3680
+ KeywordToken.new("=",offset),
3681
+ VarNameToken.new("$!",offset)
3682
+ ),nil,action()
3683
+ )
3684
+ else
3685
+ action()
3686
+ end
3687
+ result.push action.parsetree if action
3688
+ result
3689
+ end
3690
+
3691
+ def image; "(rescue)" end
3692
+ end
3693
+
3694
+ class BracketsGetNode<ValueNode
3695
+ param_names(:receiver,:lbrack_,:params,:rbrack_)
3696
+ def initialize(receiver,lbrack,params,rbrack)
3697
+ params=case params
3698
+ when CommaOpNode: Array.new params
3699
+ when nil:
3700
+ else [params]
3701
+ end
3702
+ super(receiver,params)
3703
+ end
3704
+
3705
+ def image; "(#{receiver.image}.[])" end
3706
+
3707
+ def unparse o
3708
+ [ receiver.unparse(o).sub(/\s+\Z/,''),
3709
+ '[',
3710
+ params&&params.map{|param| param.unparse o}.join(','),
3711
+ ']'
3712
+ ].to_s
3713
+ end
3714
+
3715
+ def parsetree
3716
+ result=parsetree_no_fcall
3717
+ quirks and VarLikeNode===receiver and receiver.name=='self' and
3718
+ result[0..2]=[:fcall,:[]]
3719
+ return result
3720
+ end
3721
+
3722
+ def parsetree_no_fcall
3723
+ params=params()
3724
+ output,star,amp=param_list_parse(params)
3725
+ # receiver=receiver.parsetree
3726
+ result=[:call, receiver.parsetree, :[], output]
3727
+ if params
3728
+ if star and params.size==1
3729
+ output.concat star
3730
+ else
3731
+ output.unshift :array
3732
+ result[-1]=[:argscat, output, star.last] if star
3733
+ end
3734
+ else
3735
+ result.pop
3736
+ end
3737
+ return result
3738
+ end
3739
+ def lvalue_parsetree
3740
+ result=parsetree_no_fcall
3741
+ result[0]=:attrasgn
3742
+ result[2]=:[]=
3743
+ result
3744
+ end
3745
+
3746
+ def lvalue?
3747
+ return @lvalue if defined? @lvalue
3748
+ @lvalue=true
3749
+ end
3750
+ attr_accessor :lvalue
3751
+ end
3752
+
3753
+
3754
+ class StartNode<Node #not to appear in final tree
3755
+ def initialize; end
3756
+
3757
+ def image; "(START)" end
3758
+ end #beginning of input marker
3759
+
3760
+ class GoalPostNode<Node #not to appear in final tree
3761
+ def initialize(offset); @offset=offset end
3762
+ def ident; "|" end
3763
+ attr :offset
3764
+
3765
+ def image; "|" end
3766
+ end
3767
+
3768
+ module ErrorNode
3769
+ #pass the buck to child ErrorNodes until there's no one else
3770
+ def blame
3771
+ middle.each{|node|
3772
+ node.respond_to? :blame and return node.blame
3773
+ }
3774
+ return self
3775
+ end
3776
+
3777
+
3778
+ #def msg; ... end
3779
+ end
3780
+
3781
+
3782
+ class MisparsedNode<ValueNode
3783
+ include ErrorNode
3784
+ param_names :open,:middle,:close_
3785
+ alias begin open
3786
+ alias what open
3787
+
3788
+ def image; "misparsed #{what}" end
3789
+
3790
+ def msg
3791
+ "#@line: misparsed #{what}: #{middle.map{|node| node&&node.image}.join(' ')}"
3792
+ end
3793
+ end
3794
+
3795
+ # end
3796
+
3797
+ end
3798
+ =begin a (formal?) description
3799
+ NodeMatcher=
3800
+ Recursive(nodematcher={},
3801
+ Node&-{:subnodes=>NodeList =
3802
+ Recursive(nodelist={},
3803
+ +[(nodematcher|nodelist|nil).*])
3804
+ }
3805
+
3806
+ #Nodes can also have attributes which don't appear in subnodes
3807
+ #this is where ordinary values (symbols, strings, numbers, true/false/nil) are kept
3808
+ =end