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