redparse 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/COPYING.LGPL +165 -0
- data/Manifest.txt +40 -0
- data/README.txt +461 -0
- data/Rakefile +26 -0
- data/lib/redparse.rb +1083 -0
- data/lib/redparse/babynodes.rb +137 -0
- data/lib/redparse/babyparser.rb +276 -0
- data/lib/redparse/decisiontree.rb +372 -0
- data/lib/redparse/node.rb +3808 -0
- data/lib/redparse/problemfiles.rb +84 -0
- data/lib/redparse/reg_more_sugar.rb +99 -0
- data/nurli/test_control.nurli +261 -0
- data/redparse.vpj +92 -0
- data/redparse.vpw +8 -0
- data/test/data/__end.rb +5 -0
- data/test/data/__f.rb +2 -0
- data/test/data/be.rb +3 -0
- data/test/data/be2.rb +6 -0
- data/test/data/bqhd.rb +3 -0
- data/test/data/bqhd2.rb +3 -0
- data/test/data/case.rb +8 -0
- data/test/data/datetime.rb +66 -0
- data/test/data/defd.rb +9 -0
- data/test/data/hd-def.rb +8 -0
- data/test/data/hd.rb +3 -0
- data/test/data/hd2.rb +3 -0
- data/test/data/hd3.rb +3 -0
- data/test/data/hd4.rb +75 -0
- data/test/data/hd5.rb +4 -0
- data/test/data/hdcat.rb +4 -0
- data/test/data/hdx.rb +3 -0
- data/test/data/heredoc.rb +3 -0
- data/test/data/if.rb +7 -0
- data/test/data/jbridge.rb +779 -0
- data/test/data/mod.rb +3 -0
- data/test/data/nl_as_strdelim.rb +7 -0
- data/test/data/pw.rb +2 -0
- data/test/data/wvt.rb +2 -0
- data/test/rp-locatetest.rb +344 -0
- data/test/test_redparse.rb +3319 -0
- metadata +113 -0
@@ -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&¶ms.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&¶ms.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
|