rogue_parser 1.0.1
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/.autotest +38 -0
- data/History.txt +5 -0
- data/Manifest.txt +9 -0
- data/README.txt +76 -0
- data/Rakefile +134 -0
- data/lib/ruby_lexer.rb +1329 -0
- data/lib/ruby_parser.rb +5343 -0
- data/lib/ruby_parser.y +1656 -0
- data/lib/ruby_parser_extras.rb +725 -0
- data/test/test_ruby_lexer.rb +1766 -0
- data/test/test_ruby_parser.rb +394 -0
- data/test/test_ruby_parser_extras.rb +177 -0
- metadata +87 -0
@@ -0,0 +1,725 @@
|
|
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
|
+
class Fixnum
|
16
|
+
def ord
|
17
|
+
self # I hate ruby 1.9 string changes
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class StringScanner
|
22
|
+
# if ENV['TALLY'] then
|
23
|
+
# alias :old_getch :getch
|
24
|
+
# def getch
|
25
|
+
# warn({:getch => caller[0]}.inspect)
|
26
|
+
# old_getch
|
27
|
+
# end
|
28
|
+
# end
|
29
|
+
|
30
|
+
def current_line # HAHA fuck you (HACK)
|
31
|
+
string[0..pos][/\A.*__LINE__/m].split(/\n/).size
|
32
|
+
end
|
33
|
+
|
34
|
+
def lineno
|
35
|
+
string[0..pos].split(/\n/).size
|
36
|
+
end
|
37
|
+
|
38
|
+
def unread c
|
39
|
+
return if c.nil? # UGH
|
40
|
+
warn({:unread => caller[0]}.inspect) if ENV['TALLY']
|
41
|
+
string[pos, 0] = c
|
42
|
+
end
|
43
|
+
|
44
|
+
def unread_many str
|
45
|
+
warn({:unread_many => caller[0]}.inspect) if ENV['TALLY']
|
46
|
+
string[pos, 0] = str
|
47
|
+
end
|
48
|
+
|
49
|
+
def was_begin_of_line
|
50
|
+
pos <= 2 or string[pos-2] == ?\n
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
class RubyParser < Racc::Parser
|
55
|
+
VERSION = '1.0.1'
|
56
|
+
|
57
|
+
attr_accessor :lexer, :in_def, :in_single, :file
|
58
|
+
attr_reader :env, :comments
|
59
|
+
|
60
|
+
def append_to_block head, tail # FIX: wtf is this?!? switch to block_append
|
61
|
+
return head if tail.nil?
|
62
|
+
return tail if head.nil?
|
63
|
+
|
64
|
+
head = s(:block, head) unless head.node_type == :block
|
65
|
+
head << tail
|
66
|
+
end
|
67
|
+
|
68
|
+
def arg_add(node1, node2)
|
69
|
+
return s(:array, node2) unless node1
|
70
|
+
return node1 << node2 if node1[0] == :array
|
71
|
+
return s(:argspush, node1, node2)
|
72
|
+
end
|
73
|
+
|
74
|
+
def arg_blk_pass node1, node2
|
75
|
+
if node2 then
|
76
|
+
node2.insert 1, node1
|
77
|
+
return node2
|
78
|
+
else
|
79
|
+
node1
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def arg_concat node1, node2
|
84
|
+
return node2.nil? ? node1 : s(:argscat, node1, node2)
|
85
|
+
end
|
86
|
+
|
87
|
+
def aryset receiver, index
|
88
|
+
s(:attrasgn, receiver, :"[]=", index)
|
89
|
+
end
|
90
|
+
|
91
|
+
def assignable(lhs, value = nil)
|
92
|
+
id = lhs.to_sym
|
93
|
+
id = id.to_sym if Sexp === id
|
94
|
+
|
95
|
+
raise SyntaxError, "Can't change the value of #{id}" if
|
96
|
+
id.to_s =~ /^(?:self|nil|true|false|__LINE__|__FILE__)$/
|
97
|
+
|
98
|
+
result = case id.to_s
|
99
|
+
when /^@@/ then
|
100
|
+
asgn = in_def || in_single > 0
|
101
|
+
s((asgn ? :cvasgn : :cvdecl), id)
|
102
|
+
when /^@/ then
|
103
|
+
s(:iasgn, id)
|
104
|
+
when /^\$/ then
|
105
|
+
s(:gasgn, id)
|
106
|
+
when /^[A-Z]/ then
|
107
|
+
s(:cdecl, id)
|
108
|
+
else
|
109
|
+
case self.env[id]
|
110
|
+
when :lvar then
|
111
|
+
s(:lasgn, id)
|
112
|
+
when :dvar, nil then
|
113
|
+
if self.env.current[id] == :dvar then
|
114
|
+
s(:dasgn_curr, id)
|
115
|
+
elsif self.env[id] == :dvar then
|
116
|
+
self.env.use(id)
|
117
|
+
s(:dasgn, id)
|
118
|
+
elsif ! self.env.dynamic? then
|
119
|
+
s(:lasgn, id)
|
120
|
+
else
|
121
|
+
s(:dasgn_curr, id)
|
122
|
+
end
|
123
|
+
else
|
124
|
+
raise "wtf? unknown type: #{self.env[id]}"
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
self.env[id] = (self.env.dynamic? ? :dvar : :lvar) unless self.env[id]
|
129
|
+
|
130
|
+
result << value if value
|
131
|
+
|
132
|
+
return result
|
133
|
+
end
|
134
|
+
|
135
|
+
def block_append(head, tail, strip_tail_block=false)
|
136
|
+
return head unless tail
|
137
|
+
return tail unless head
|
138
|
+
|
139
|
+
case head[0]
|
140
|
+
when :lit, :str then
|
141
|
+
return tail
|
142
|
+
end
|
143
|
+
|
144
|
+
head = remove_begin(head)
|
145
|
+
head = s(:block, head) unless head[0] == :block
|
146
|
+
|
147
|
+
if strip_tail_block and Sexp === tail and tail[0] == :block then
|
148
|
+
head.push(*tail.values)
|
149
|
+
else
|
150
|
+
head << tail
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def cond node
|
155
|
+
return nil if node.nil?
|
156
|
+
node = value_expr node
|
157
|
+
|
158
|
+
case node.first
|
159
|
+
when :dregex then
|
160
|
+
return s(:match2, node, s(:gvar, "$_".to_sym))
|
161
|
+
when :regex then
|
162
|
+
return s(:match, node)
|
163
|
+
when :lit then
|
164
|
+
if Regexp === node.last then
|
165
|
+
return s(:match, node)
|
166
|
+
else
|
167
|
+
return node
|
168
|
+
end
|
169
|
+
when :and then
|
170
|
+
return s(:and, cond(node[1]), cond(node[2]))
|
171
|
+
when :or then
|
172
|
+
return s(:or, cond(node[1]), cond(node[2]))
|
173
|
+
when :dot2 then
|
174
|
+
label = "flip#{node.hash}"
|
175
|
+
env[label] = self.env.dynamic? ? :dvar : :lvar
|
176
|
+
return s(:flip2, node[1], node[2])
|
177
|
+
when :dot3 then
|
178
|
+
label = "flip#{node.hash}"
|
179
|
+
env[label] = self.env.dynamic? ? :dvar : :lvar
|
180
|
+
return s(:flip3, node[1], node[2])
|
181
|
+
else
|
182
|
+
return node
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
##
|
187
|
+
# for pure ruby systems only
|
188
|
+
|
189
|
+
def do_parse
|
190
|
+
_racc_do_parse_rb(_racc_setup, false)
|
191
|
+
end if ENV['PURE_RUBY']
|
192
|
+
|
193
|
+
def get_match_node lhs, rhs
|
194
|
+
if lhs then
|
195
|
+
case lhs[0]
|
196
|
+
when :dregx, :dregx_once then
|
197
|
+
return s(:match2, lhs, rhs)
|
198
|
+
when :lit then
|
199
|
+
return s(:match2, lhs, rhs) if Regexp === lhs.last
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
if rhs then
|
204
|
+
case rhs[0]
|
205
|
+
when :dregx, :dregx_once then
|
206
|
+
return s(:match3, rhs, lhs)
|
207
|
+
when :lit then
|
208
|
+
return s(:match3, rhs, lhs) if Regexp === rhs.last
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
return s(:call, lhs, :"=~", s(:array, rhs))
|
213
|
+
end
|
214
|
+
|
215
|
+
def gettable(id)
|
216
|
+
id = id.to_sym if Sexp === id # HACK
|
217
|
+
id = id.to_sym if String === id # HACK
|
218
|
+
|
219
|
+
return s(:self) if id == :self
|
220
|
+
return s(:nil) if id == :nil
|
221
|
+
return s(:true) if id == :true
|
222
|
+
return s(:false) if id == :false
|
223
|
+
return s(:str, self.file) if id == :"__FILE__"
|
224
|
+
return s(:lit, lexer.src.current_line) if id == :"__LINE__"
|
225
|
+
|
226
|
+
result = case id.to_s
|
227
|
+
when /^@@/ then
|
228
|
+
s(:cvar, id)
|
229
|
+
when /^@/ then
|
230
|
+
s(:ivar, id)
|
231
|
+
when /^\$/ then
|
232
|
+
s(:gvar, id)
|
233
|
+
when /^[A-Z]/ then
|
234
|
+
s(:const, id)
|
235
|
+
else
|
236
|
+
type = env[id]
|
237
|
+
if type then
|
238
|
+
s(type, id)
|
239
|
+
elsif env.dynamic? and :dvar == env[id] then
|
240
|
+
s(:dvar, id)
|
241
|
+
else
|
242
|
+
s(:vcall, id)
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
return result if result
|
247
|
+
|
248
|
+
raise "identifier #{id.inspect} is not valid"
|
249
|
+
end
|
250
|
+
|
251
|
+
def initialize
|
252
|
+
super
|
253
|
+
self.lexer = RubyLexer.new
|
254
|
+
self.in_def = false
|
255
|
+
self.in_single = 0
|
256
|
+
@env = Environment.new
|
257
|
+
@comments = []
|
258
|
+
end
|
259
|
+
|
260
|
+
def list_append list, item # TODO: nuke me *sigh*
|
261
|
+
return s(:array, item) unless list
|
262
|
+
list << item
|
263
|
+
end
|
264
|
+
|
265
|
+
def literal_concat head, tail
|
266
|
+
return tail unless head
|
267
|
+
return head unless tail
|
268
|
+
|
269
|
+
htype, ttype = head[0], tail[0]
|
270
|
+
|
271
|
+
head = s(:dstr, '', head) if htype == :evstr
|
272
|
+
|
273
|
+
case ttype
|
274
|
+
when :str then
|
275
|
+
if htype == :str
|
276
|
+
head[-1] << tail[-1]
|
277
|
+
elsif htype == :dstr and head.size == 2 then
|
278
|
+
head[-1] << tail[-1]
|
279
|
+
else
|
280
|
+
head << tail
|
281
|
+
end
|
282
|
+
when :dstr then
|
283
|
+
if htype == :str then
|
284
|
+
tail[1] = head[-1] + tail[1]
|
285
|
+
head = tail
|
286
|
+
else
|
287
|
+
tail[0] = :array
|
288
|
+
tail[1] = s(:str, tail[1])
|
289
|
+
tail.delete_at 1 if tail[1] == s(:str, '')
|
290
|
+
|
291
|
+
head.push(*tail[1..-1])
|
292
|
+
end
|
293
|
+
when :evstr then
|
294
|
+
head[0] = :dstr if htype == :str
|
295
|
+
if head.size == 2 and tail[1][0] == :str then
|
296
|
+
head[-1] << tail[1][-1]
|
297
|
+
head[0] = :str if head.size == 2 # HACK ?
|
298
|
+
else
|
299
|
+
head.push(tail)
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
return head
|
304
|
+
end
|
305
|
+
|
306
|
+
def logop(type, left, right) # TODO: rename logical_op
|
307
|
+
left = value_expr left
|
308
|
+
|
309
|
+
if left and left[0] == type and not left.paren then
|
310
|
+
node, second = left, nil
|
311
|
+
|
312
|
+
while (second = node[2]) && second[0] == type and not second.paren do
|
313
|
+
node = second
|
314
|
+
end
|
315
|
+
|
316
|
+
node[2] = s(type, second, right)
|
317
|
+
|
318
|
+
return left
|
319
|
+
end
|
320
|
+
|
321
|
+
return s(type, left, right)
|
322
|
+
end
|
323
|
+
|
324
|
+
def new_call recv, meth, args = nil # REFACTOR - merge with fcall
|
325
|
+
if args && args[0] == :block_pass then
|
326
|
+
new_args = args.array(true) || args.argscat(true) || args.splat(true)
|
327
|
+
call = s(:call, recv, meth)
|
328
|
+
call << new_args if new_args
|
329
|
+
args << call
|
330
|
+
|
331
|
+
return args
|
332
|
+
end
|
333
|
+
result = s(:call, recv, meth)
|
334
|
+
result << args if args
|
335
|
+
result
|
336
|
+
end
|
337
|
+
|
338
|
+
def new_fcall meth, args
|
339
|
+
if args and args[0] == :block_pass then
|
340
|
+
new_args = args.array(true) || args.argscat(true) || args.splat(true)
|
341
|
+
call = s(:fcall, meth)
|
342
|
+
call << new_args if new_args
|
343
|
+
args << call
|
344
|
+
return args
|
345
|
+
end
|
346
|
+
|
347
|
+
r = s(:fcall, meth)
|
348
|
+
r << args if args and args != s(:array)
|
349
|
+
r
|
350
|
+
end
|
351
|
+
|
352
|
+
def new_super args
|
353
|
+
if args && args.node_type == :block_pass then
|
354
|
+
t, body, bp = args
|
355
|
+
result = s(t, bp, s(:super, body))
|
356
|
+
else
|
357
|
+
result = s(:super)
|
358
|
+
result << args if args and args != s(:array)
|
359
|
+
end
|
360
|
+
result
|
361
|
+
end
|
362
|
+
|
363
|
+
def new_yield(node)
|
364
|
+
if node then
|
365
|
+
raise SyntaxError, "Block argument should not be given." if
|
366
|
+
node.node_type == :block_pass
|
367
|
+
|
368
|
+
node = node.last if node.node_type == :array and node.size == 2
|
369
|
+
end
|
370
|
+
|
371
|
+
return s(:yield, node)
|
372
|
+
end
|
373
|
+
|
374
|
+
def next_token
|
375
|
+
if self.lexer.advance then
|
376
|
+
[self.lexer.token, self.lexer.yacc_value]
|
377
|
+
else
|
378
|
+
return [false, '$end']
|
379
|
+
end
|
380
|
+
end
|
381
|
+
|
382
|
+
def node_assign(lhs, rhs)
|
383
|
+
return nil unless lhs
|
384
|
+
|
385
|
+
rhs = value_expr rhs
|
386
|
+
|
387
|
+
case lhs[0]
|
388
|
+
when :gasgn, :iasgn, :lasgn, :dasgn, :dasgn_curr,
|
389
|
+
:masgn, :cdecl, :cvdecl, :cvasgn then
|
390
|
+
lhs << rhs
|
391
|
+
when :attrasgn, :call then
|
392
|
+
args = lhs.array(true) || lhs.argscat(true) || lhs.splat(true) # FIX: fragile
|
393
|
+
lhs << arg_add(args, rhs)
|
394
|
+
end
|
395
|
+
|
396
|
+
lhs
|
397
|
+
end
|
398
|
+
|
399
|
+
def parse(str, file = "(string)")
|
400
|
+
raise "bad val: #{str.inspect}" unless String === str
|
401
|
+
|
402
|
+
self.file = file
|
403
|
+
self.lexer.src = str
|
404
|
+
|
405
|
+
@yydebug = ENV.has_key? 'DEBUG'
|
406
|
+
|
407
|
+
do_parse
|
408
|
+
end
|
409
|
+
|
410
|
+
def remove_begin node
|
411
|
+
node = node[-1] if node and node[0] == :begin and node.size == 2
|
412
|
+
node
|
413
|
+
end
|
414
|
+
|
415
|
+
def ret_args node
|
416
|
+
if node then
|
417
|
+
raise SyntaxError, "block argument should not be given" if
|
418
|
+
node[0] == :block_pass
|
419
|
+
|
420
|
+
node = node.last if node[0] == :array && node.size == 2
|
421
|
+
# HACK matz wraps ONE of the FOUR splats in a newline to
|
422
|
+
# distinguish. I use paren for now. ugh
|
423
|
+
node = s(:svalue, node) if node[0] == :splat and not node.paren
|
424
|
+
end
|
425
|
+
|
426
|
+
node
|
427
|
+
end
|
428
|
+
|
429
|
+
def value_expr node # HACK
|
430
|
+
node = remove_begin node
|
431
|
+
node[2] = value_expr(node[2]) if node and node[0] == :if
|
432
|
+
node
|
433
|
+
end
|
434
|
+
|
435
|
+
def void_stmts node
|
436
|
+
return nil unless node
|
437
|
+
return node unless node[0] == :block
|
438
|
+
|
439
|
+
node[1..-2] = node[1..-2].map { |n| remove_begin(n) }
|
440
|
+
node
|
441
|
+
end
|
442
|
+
|
443
|
+
def warning s
|
444
|
+
# do nothing for now
|
445
|
+
end
|
446
|
+
end
|
447
|
+
|
448
|
+
class Keyword
|
449
|
+
class KWtable
|
450
|
+
attr_accessor :name, :id, :state
|
451
|
+
def initialize(name, id=[], state=nil)
|
452
|
+
@name = name
|
453
|
+
@id = id
|
454
|
+
@state = state
|
455
|
+
end
|
456
|
+
|
457
|
+
def id0
|
458
|
+
self.id.first
|
459
|
+
end
|
460
|
+
|
461
|
+
def id1
|
462
|
+
self.id.last
|
463
|
+
end
|
464
|
+
end
|
465
|
+
|
466
|
+
TOTAL_KEYWORDS = 40
|
467
|
+
MIN_WORD_LENGTH = 2
|
468
|
+
MAX_WORD_LENGTH = 8
|
469
|
+
MIN_HASH_VALUE = 6
|
470
|
+
MAX_HASH_VALUE = 55
|
471
|
+
# maximum key range = 50, duplicates = 0
|
472
|
+
|
473
|
+
ASSO_VALUES = [
|
474
|
+
56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
|
475
|
+
56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
|
476
|
+
56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
|
477
|
+
56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
|
478
|
+
56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
|
479
|
+
56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
|
480
|
+
56, 56, 56, 11, 56, 56, 36, 56, 1, 37,
|
481
|
+
31, 1, 56, 56, 56, 56, 29, 56, 1, 56,
|
482
|
+
56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
|
483
|
+
56, 56, 56, 56, 56, 1, 56, 32, 1, 2,
|
484
|
+
1, 1, 4, 23, 56, 17, 56, 20, 9, 2,
|
485
|
+
9, 26, 14, 56, 5, 1, 1, 16, 56, 21,
|
486
|
+
20, 9, 56, 56, 56, 56, 56, 56, 56, 56,
|
487
|
+
56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
|
488
|
+
56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
|
489
|
+
56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
|
490
|
+
56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
|
491
|
+
56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
|
492
|
+
56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
|
493
|
+
56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
|
494
|
+
56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
|
495
|
+
56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
|
496
|
+
56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
|
497
|
+
56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
|
498
|
+
56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
|
499
|
+
56, 56, 56, 56, 56, 56
|
500
|
+
]
|
501
|
+
|
502
|
+
##
|
503
|
+
# :expr_beg = ignore newline, +/- is a sign.
|
504
|
+
# :expr_end = newline significant, +/- is a operator.
|
505
|
+
# :expr_arg = newline significant, +/- is a operator.
|
506
|
+
# :expr_cmdarg = newline significant, +/- is a operator.
|
507
|
+
# :expr_endarg = newline significant, +/- is a operator.
|
508
|
+
# :expr_mid = newline significant, +/- is a operator.
|
509
|
+
# :expr_fname = ignore newline, no reserved words.
|
510
|
+
# :expr_dot = right after . or ::, no reserved words.
|
511
|
+
# :expr_class = immediate after class, no here document.
|
512
|
+
|
513
|
+
WORDLIST = [
|
514
|
+
[""], [""], [""], [""], [""], [""],
|
515
|
+
["end", [:kEND, :kEND ], :expr_end ],
|
516
|
+
["else", [:kELSE, :kELSE ], :expr_beg ],
|
517
|
+
["case", [:kCASE, :kCASE ], :expr_beg ],
|
518
|
+
["ensure", [:kENSURE, :kENSURE ], :expr_beg ],
|
519
|
+
["module", [:kMODULE, :kMODULE ], :expr_beg ],
|
520
|
+
["elsif", [:kELSIF, :kELSIF ], :expr_beg ],
|
521
|
+
["def", [:kDEF, :kDEF ], :expr_fname ],
|
522
|
+
["rescue", [:kRESCUE, :kRESCUE_MOD ], :expr_mid ],
|
523
|
+
["not", [:kNOT, :kNOT ], :expr_beg ],
|
524
|
+
["then", [:kTHEN, :kTHEN ], :expr_beg ],
|
525
|
+
["yield", [:kYIELD, :kYIELD ], :expr_arg ],
|
526
|
+
["for", [:kFOR, :kFOR ], :expr_beg ],
|
527
|
+
["self", [:kSELF, :kSELF ], :expr_end ],
|
528
|
+
["false", [:kFALSE, :kFALSE ], :expr_end ],
|
529
|
+
["retry", [:kRETRY, :kRETRY ], :expr_end ],
|
530
|
+
["return", [:kRETURN, :kRETURN ], :expr_mid ],
|
531
|
+
["true", [:kTRUE, :kTRUE ], :expr_end ],
|
532
|
+
["if", [:kIF, :kIF_MOD ], :expr_beg ],
|
533
|
+
["defined?", [:kDEFINED, :kDEFINED ], :expr_arg ],
|
534
|
+
["super", [:kSUPER, :kSUPER ], :expr_arg ],
|
535
|
+
["undef", [:kUNDEF, :kUNDEF ], :expr_fname ],
|
536
|
+
["break", [:kBREAK, :kBREAK ], :expr_mid ],
|
537
|
+
["in", [:kIN, :kIN ], :expr_beg ],
|
538
|
+
["do", [:kDO, :kDO ], :expr_beg ],
|
539
|
+
["nil", [:kNIL, :kNIL ], :expr_end ],
|
540
|
+
["until", [:kUNTIL, :kUNTIL_MOD ], :expr_beg ],
|
541
|
+
["unless", [:kUNLESS, :kUNLESS_MOD ], :expr_beg ],
|
542
|
+
["or", [:kOR, :kOR ], :expr_beg ],
|
543
|
+
["next", [:kNEXT, :kNEXT ], :expr_mid ],
|
544
|
+
["when", [:kWHEN, :kWHEN ], :expr_beg ],
|
545
|
+
["redo", [:kREDO, :kREDO ], :expr_end ],
|
546
|
+
["and", [:kAND, :kAND ], :expr_beg ],
|
547
|
+
["begin", [:kBEGIN, :kBEGIN ], :expr_beg ],
|
548
|
+
["__LINE__", [:k__LINE__, :k__LINE__ ], :expr_end ],
|
549
|
+
["class", [:kCLASS, :kCLASS ], :expr_class ],
|
550
|
+
["__FILE__", [:k__FILE__, :k__FILE__ ], :expr_end ],
|
551
|
+
["END", [:klEND, :klEND ], :expr_end ],
|
552
|
+
["BEGIN", [:klBEGIN, :klBEGIN ], :expr_end ],
|
553
|
+
["while", [:kWHILE, :kWHILE_MOD ], :expr_beg ],
|
554
|
+
[""], [""], [""], [""], [""], [""], [""], [""], [""],
|
555
|
+
[""],
|
556
|
+
["alias", [:kALIAS, :kALIAS ], :expr_fname ],
|
557
|
+
].map { |args| KWtable.new(*args) }
|
558
|
+
|
559
|
+
def self.hash_keyword(str, len)
|
560
|
+
hval = len
|
561
|
+
|
562
|
+
case hval
|
563
|
+
when 2, 1 then
|
564
|
+
hval += ASSO_VALUES[str[0].ord]
|
565
|
+
else
|
566
|
+
hval += ASSO_VALUES[str[2].ord]
|
567
|
+
hval += ASSO_VALUES[str[0].ord]
|
568
|
+
end
|
569
|
+
|
570
|
+
hval += ASSO_VALUES[str[len - 1].ord]
|
571
|
+
return hval
|
572
|
+
end
|
573
|
+
|
574
|
+
def self.keyword(str, len = str.size)
|
575
|
+
if len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH then
|
576
|
+
key = hash_keyword(str, len)
|
577
|
+
if key <= MAX_HASH_VALUE && key >= 0 then
|
578
|
+
s = WORDLIST[key].name
|
579
|
+
return WORDLIST[key] if str == s
|
580
|
+
end
|
581
|
+
end
|
582
|
+
|
583
|
+
return nil
|
584
|
+
end
|
585
|
+
end
|
586
|
+
|
587
|
+
class Environment
|
588
|
+
attr_reader :env, :dyn
|
589
|
+
attr_accessor :init
|
590
|
+
|
591
|
+
def [] k
|
592
|
+
self.all[k]
|
593
|
+
end
|
594
|
+
|
595
|
+
def []= k, v
|
596
|
+
raise "no" if v == true
|
597
|
+
self.current[k] = v
|
598
|
+
end
|
599
|
+
|
600
|
+
def all
|
601
|
+
idx = @dyn.index false
|
602
|
+
@env[0..idx].reverse.inject { |env, scope| env.merge scope }
|
603
|
+
end
|
604
|
+
|
605
|
+
def current
|
606
|
+
@env.first
|
607
|
+
end
|
608
|
+
|
609
|
+
def dynamic
|
610
|
+
idx = @dyn.index false
|
611
|
+
@env[0...idx].reverse.inject { |env, scope| env.merge scope } || {}
|
612
|
+
end
|
613
|
+
|
614
|
+
def dynamic?
|
615
|
+
@dyn[0] != false
|
616
|
+
end
|
617
|
+
|
618
|
+
def extend dyn = false
|
619
|
+
@dyn.unshift dyn
|
620
|
+
@env.unshift({})
|
621
|
+
@use.unshift({})
|
622
|
+
end
|
623
|
+
|
624
|
+
def initialize dyn = false
|
625
|
+
@dyn = []
|
626
|
+
@env = []
|
627
|
+
@use = []
|
628
|
+
@init = false
|
629
|
+
self.extend
|
630
|
+
end
|
631
|
+
|
632
|
+
def unextend
|
633
|
+
@dyn.shift
|
634
|
+
@env.shift
|
635
|
+
@use.shift
|
636
|
+
raise "You went too far unextending env" if @env.empty?
|
637
|
+
end
|
638
|
+
|
639
|
+
def use id
|
640
|
+
@env.each_with_index do |env, i|
|
641
|
+
if env[id] then
|
642
|
+
@use[i][id] = true
|
643
|
+
end
|
644
|
+
end
|
645
|
+
end
|
646
|
+
|
647
|
+
def used? id
|
648
|
+
idx = @dyn.index false # REFACTOR
|
649
|
+
u = @use[0...idx].reverse.inject { |env, scope| env.merge scope } || {}
|
650
|
+
u[id]
|
651
|
+
end
|
652
|
+
end
|
653
|
+
|
654
|
+
class StackState
|
655
|
+
attr_reader :stack
|
656
|
+
|
657
|
+
def initialize(name)
|
658
|
+
@name = name
|
659
|
+
@stack = [false]
|
660
|
+
end
|
661
|
+
|
662
|
+
def inspect
|
663
|
+
"StackState(#{@name}, #{@stack.inspect})"
|
664
|
+
end
|
665
|
+
|
666
|
+
def is_in_state
|
667
|
+
@stack.last
|
668
|
+
end
|
669
|
+
|
670
|
+
def lexpop
|
671
|
+
raise if @stack.size == 0
|
672
|
+
a = @stack.pop
|
673
|
+
b = @stack.pop
|
674
|
+
@stack.push(a || b)
|
675
|
+
end
|
676
|
+
|
677
|
+
def pop
|
678
|
+
r = @stack.pop
|
679
|
+
@stack.push false if @stack.size == 0
|
680
|
+
r
|
681
|
+
end
|
682
|
+
|
683
|
+
def push val
|
684
|
+
raise if val != true and val != false
|
685
|
+
@stack.push val
|
686
|
+
end
|
687
|
+
end
|
688
|
+
|
689
|
+
############################################################
|
690
|
+
# HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK
|
691
|
+
|
692
|
+
class Symbol
|
693
|
+
def is_argument # TODO: phase this out
|
694
|
+
return self == :expr_arg || self == :expr_cmdarg
|
695
|
+
end
|
696
|
+
end
|
697
|
+
|
698
|
+
class Sexp
|
699
|
+
attr_writer :paren
|
700
|
+
attr_accessor :comments
|
701
|
+
|
702
|
+
def node_type
|
703
|
+
first
|
704
|
+
end
|
705
|
+
|
706
|
+
def paren
|
707
|
+
@paren ||= false
|
708
|
+
end
|
709
|
+
|
710
|
+
def value
|
711
|
+
raise "multi item sexp" if size > 2
|
712
|
+
last
|
713
|
+
end
|
714
|
+
|
715
|
+
def to_sym
|
716
|
+
self.value.to_sym
|
717
|
+
end
|
718
|
+
|
719
|
+
def values
|
720
|
+
self[1..-1]
|
721
|
+
end
|
722
|
+
end
|
723
|
+
|
724
|
+
# END HACK
|
725
|
+
############################################################
|