redparse 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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