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