redparse 0.8.3 → 0.8.4
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/History.txt +63 -4
- data/Makefile +43 -0
- data/README.txt +101 -166
- data/Rakefile +1 -1
- data/bin/redparse +49 -21
- data/lib/redparse.rb +88 -1654
- data/lib/redparse/cache.rb +172 -0
- data/lib/redparse/compile.rb +1648 -0
- data/lib/redparse/float_accurate_to_s.rb +162 -0
- data/lib/redparse/generate.rb +6 -2
- data/lib/redparse/node.rb +677 -397
- data/lib/redparse/parse_tree_server.rb +129 -0
- data/lib/redparse/pthelper.rb +43 -0
- data/lib/redparse/reg_more_sugar.rb +5 -5
- data/lib/redparse/version.rb +1 -1
- data/redparse.gemspec +43 -0
- data/test/data/skkdictools.rb +3 -0
- data/test/generate_parse_tree_server_rc.rb +43 -0
- data/test/rp-locatetest.rb +41 -1
- data/test/test_1.9.rb +114 -0
- data/test/test_all.rb +3 -0
- data/test/test_redparse.rb +283 -124
- data/test/test_xform_tree.rb +66 -0
- metadata +57 -56
data/Rakefile
CHANGED
data/bin/redparse
CHANGED
@@ -18,8 +18,11 @@
|
|
18
18
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
19
19
|
=end
|
20
20
|
$VERBOSE=1 #turn on -w
|
21
|
+
$Debug=1
|
22
|
+
require 'rubygems'
|
21
23
|
require "redparse.rb"
|
22
|
-
require
|
24
|
+
require "redparse/problemfiles"
|
25
|
+
require "redparse/pthelper"
|
23
26
|
|
24
27
|
class NeverExecThis<RuntimeError; end
|
25
28
|
|
@@ -83,9 +86,10 @@ end
|
|
83
86
|
|
84
87
|
|
85
88
|
|
86
|
-
output=:
|
87
|
-
quiet=
|
88
|
-
|
89
|
+
output=:p
|
90
|
+
quiet=false
|
91
|
+
verbose=false
|
92
|
+
ruby187=ruby19=false
|
89
93
|
while /^-/===ARGV.first
|
90
94
|
case opt=ARGV.shift
|
91
95
|
when "--"; break
|
@@ -95,19 +99,21 @@ while /^-/===ARGV.first
|
|
95
99
|
when "--vsparsetree"; output=:vsparsetree
|
96
100
|
when "--vsparsetree2"; output=:vsparsetree2
|
97
101
|
when "--update-problemfiles"; problemfiles=ProblemFiles.new
|
102
|
+
when "--ignore-silly-begins"; ignorebegins=true
|
98
103
|
when "--tokens"; ENV['PRINT_TOKENS']='1'
|
99
104
|
when "--rawtokens"; ENV['RAW_PRINT_TOKENS']='1'
|
100
105
|
when "--stack"; ENV['PRINT_STACK']='1'
|
101
|
-
when "--unparse";
|
106
|
+
when "--unparse"; unparse=true
|
102
107
|
when "--compile", "-c"; compile=true
|
103
108
|
when "--macros", "--macro", "-m";
|
104
109
|
require 'rubygems'
|
105
110
|
require 'macro'
|
106
111
|
parserclass=Macro::RedParseWithMacros
|
107
112
|
when "-q"; quiet=true
|
108
|
-
when "-v";
|
113
|
+
when "-v"; verbose=true
|
109
114
|
when "-e"; inputs=[ARGV.join(" ")]; names=["-e"]; break
|
110
115
|
when "-7"; ruby187=true
|
116
|
+
when "-9"; ruby19=true
|
111
117
|
else fail "unknown option: #{opt}"
|
112
118
|
end
|
113
119
|
end
|
@@ -153,11 +159,16 @@ inputs.each_index{|i|
|
|
153
159
|
begin
|
154
160
|
tree=nil
|
155
161
|
if catch(:never_exec_this){
|
162
|
+
if ruby19
|
163
|
+
options={:rubyversion=>1.9}
|
164
|
+
else
|
165
|
+
options={:rubyversion=>1.8}
|
166
|
+
end
|
156
167
|
tree=if compile
|
157
|
-
huh (parserclass||RedParse).new(input,name).compile
|
168
|
+
huh (parserclass||RedParse).new(input,name,1,[],options).compile
|
158
169
|
huh parse
|
159
170
|
else
|
160
|
-
(parserclass||RedParse).new(input,name).parse
|
171
|
+
(parserclass||RedParse).new(input,name,1,[],options).parse
|
161
172
|
end
|
162
173
|
nil
|
163
174
|
} #raise NeverExecThis
|
@@ -165,11 +176,11 @@ inputs.each_index{|i|
|
|
165
176
|
# require 'pp'
|
166
177
|
# pp e.stack[-[15,e.stack.size].min..-1]
|
167
178
|
# raise
|
168
|
-
# rescue NeverExecThis
|
179
|
+
# rescue NeverExecThis
|
169
180
|
puts "RedParse attempted to execute parse data in #{name}"
|
170
181
|
next
|
171
182
|
end
|
172
|
-
rescue Interrupt
|
183
|
+
rescue Interrupt; exit 2
|
173
184
|
rescue Exception=>e
|
174
185
|
puts "during parse of #{name}:"
|
175
186
|
problemfiles.push name if problemfiles
|
@@ -186,10 +197,10 @@ inputs.each_index{|i|
|
|
186
197
|
when :pp
|
187
198
|
require 'pp'
|
188
199
|
pp tree
|
200
|
+
when :p
|
201
|
+
p tree
|
189
202
|
when :lisp
|
190
203
|
puts tree.to_lisp
|
191
|
-
when :unparse
|
192
|
-
puts tree.unparse
|
193
204
|
when :parsetree
|
194
205
|
tree=tree.to_parsetree
|
195
206
|
hack=tree.dup
|
@@ -217,10 +228,17 @@ inputs.each_index{|i|
|
|
217
228
|
ryans,warns=ParseTree.new.parse_tree_and_warnings_leaks_stderr(input,name); nil
|
218
229
|
} and raise NeverExecThis
|
219
230
|
delta,is_diff=arraydiff(mine,ryans)
|
220
|
-
|
231
|
+
|
232
|
+
if is_diff and ignorebegins
|
233
|
+
mine=RedParse.remove_silly_begins(mine)
|
234
|
+
ryans=RedParse.remove_silly_begins(ryans)
|
235
|
+
delta,is_diff=arraydiff(mine,ryans)
|
236
|
+
was_silly_begin_diff=!is_diff
|
237
|
+
end
|
238
|
+
rescue NeverExecThis
|
221
239
|
puts "ParseTree attempted to execute parse data in #{name}"
|
222
240
|
next
|
223
|
-
rescue Interrupt
|
241
|
+
rescue Interrupt; exit 2
|
224
242
|
rescue Exception=>e
|
225
243
|
#raise( RuntimeError.new( "#{e} during to_parsetree of #{name}" ) )
|
226
244
|
puts "error during to_parsetree of #{name}"
|
@@ -228,13 +246,13 @@ inputs.each_index{|i|
|
|
228
246
|
raise
|
229
247
|
end
|
230
248
|
if output==:vsparsetree2
|
231
|
-
if
|
249
|
+
if verbose or is_diff
|
232
250
|
puts "mine:"
|
233
251
|
pp mine
|
234
252
|
puts "ryans:" if is_diff
|
235
253
|
pp ryans if is_diff
|
236
254
|
end
|
237
|
-
elsif
|
255
|
+
elsif verbose or is_diff
|
238
256
|
puts 'differences in '+name if is_diff
|
239
257
|
pp delta
|
240
258
|
end
|
@@ -242,19 +260,29 @@ inputs.each_index{|i|
|
|
242
260
|
result=1
|
243
261
|
problemfiles.push name if problemfiles
|
244
262
|
else
|
245
|
-
|
246
|
-
|
263
|
+
if was_silly_begin_diff
|
264
|
+
puts "differed by a :begin node in "+name
|
265
|
+
problemfiles.push name if problemfiles
|
266
|
+
else
|
267
|
+
puts "no differences in "+name
|
268
|
+
problemfiles.delete name if problemfiles
|
269
|
+
end
|
247
270
|
end
|
248
271
|
end
|
272
|
+
if unparse
|
273
|
+
unparsed=tree.unparse
|
274
|
+
puts unparsed unless quiet
|
275
|
+
end
|
249
276
|
|
250
|
-
rescue NeverExecThis
|
277
|
+
rescue NeverExecThis
|
251
278
|
puts "mysterious attempt to execute parse data in #{name}"
|
252
279
|
next
|
253
|
-
rescue Interrupt,SystemExit
|
280
|
+
rescue Interrupt,SystemExit; exit 2
|
254
281
|
rescue Exception=>e
|
282
|
+
puts "exception while processing #{name}"
|
255
283
|
puts "#{e}:#{e.class}"
|
256
284
|
puts e.backtrace.join("\n")
|
257
|
-
|
285
|
+
problemfiles.push name if problemfiles
|
258
286
|
#raise
|
259
287
|
ensure
|
260
288
|
STDOUT.flush
|
data/lib/redparse.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
=begin
|
2
2
|
redparse - a ruby parser written in ruby
|
3
|
-
Copyright (C) 2008 Caleb Clausen
|
3
|
+
Copyright (C) 2008,2009 Caleb Clausen
|
4
4
|
|
5
5
|
This program is free software: you can redistribute it and/or modify
|
6
6
|
it under the terms of the GNU Lesser General Public License as published by
|
@@ -16,8 +16,6 @@
|
|
16
16
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
17
17
|
=end
|
18
18
|
|
19
|
-
#warn 'hacking up LOAD_PATH to include the latest RubyLexer!'
|
20
|
-
#$:.unshift Dir.pwd+'/../rubylexer/lib', Dir.pwd+'/../rubylexer'
|
21
19
|
|
22
20
|
|
23
21
|
require 'forwardable'
|
@@ -30,11 +28,14 @@ rescue LoadError=>e
|
|
30
28
|
end
|
31
29
|
require 'rubylexer'
|
32
30
|
require 'reg'
|
31
|
+
require 'reglookab'
|
33
32
|
|
34
33
|
require "redparse/node"
|
35
34
|
#require "redparse/decisiontree"
|
36
35
|
require "redparse/reg_more_sugar"
|
37
36
|
require "redparse/generate"
|
37
|
+
require "redparse/cache"
|
38
|
+
#require "redparse/compile"
|
38
39
|
|
39
40
|
class RedParse
|
40
41
|
|
@@ -184,7 +185,7 @@ end
|
|
184
185
|
#replace matching elements in @stack with node type found
|
185
186
|
case node_type
|
186
187
|
when Class
|
187
|
-
node=node_type.
|
188
|
+
node=node_type.create(*matching)
|
188
189
|
node.startline||=@stack[matchrange.first].startline
|
189
190
|
node.endline=@endline
|
190
191
|
@stack[matchrange]=[node]
|
@@ -242,11 +243,14 @@ end
|
|
242
243
|
end
|
243
244
|
|
244
245
|
def parse
|
246
|
+
|
245
247
|
#hack, so StringToken can know what parser its called from
|
246
248
|
#so it can use it to parse inclusions
|
247
249
|
oldparser=Thread.current[:$RedParse_parser]
|
248
250
|
Thread.current[:$RedParse_parser]||=self
|
249
251
|
|
252
|
+
return @cached_result if defined? @cached_result
|
253
|
+
|
250
254
|
@rules||=expanded_RULES()
|
251
255
|
# @inputs||=enumerate_exemplars
|
252
256
|
|
@@ -266,7 +270,7 @@ end
|
|
266
270
|
@stack.push tok
|
267
271
|
}}
|
268
272
|
|
269
|
-
@stack.size==2 and return NopNode.new #handle empty parse string
|
273
|
+
@stack.size==2 and return result=NopNode.new #handle empty parse string
|
270
274
|
|
271
275
|
#unless the @stack is 3 tokens,
|
272
276
|
#with the last an Eoi, and first a StartToken
|
@@ -296,8 +300,11 @@ end
|
|
296
300
|
#do something with error nodes
|
297
301
|
msgs=[]
|
298
302
|
result.walk{|parent,i,subi,node|
|
299
|
-
|
303
|
+
if node.respond_to? :error? and node.error?(@rubyversion)
|
300
304
|
msgs<< @filename+":"+node.blame.msg
|
305
|
+
false
|
306
|
+
else
|
307
|
+
true
|
301
308
|
end
|
302
309
|
} if result.respond_to? :walk #hack hack
|
303
310
|
result.errors=msgs unless msgs.empty?
|
@@ -329,6 +336,7 @@ end
|
|
329
336
|
# result=NopNode.new if EoiToken===result
|
330
337
|
return result
|
331
338
|
ensure
|
339
|
+
@write_cache.put(@input,result) if @write_cache and result and !result.errors
|
332
340
|
@stack=nil
|
333
341
|
Thread.current[:$RedParse_parser]=oldparser
|
334
342
|
end
|
@@ -342,1504 +350,16 @@ end
|
|
342
350
|
|
343
351
|
@reducer.reduce(@stack)
|
344
352
|
end #
|
345
|
-
#
|
346
|
-
if defined? END_ATTACK
|
347
|
-
class RuleSet
|
348
|
-
def initialize(rules)
|
349
|
-
@rules=rules.reverse
|
350
|
-
#rule order must be reversed relative to the usual RedParse rule
|
351
|
-
#order... merely so that ffs can work right.
|
352
|
-
@maxmask=(1<<@rules.size)-1
|
353
|
-
@subclasses_of=child_relations_among(*STACKABLE_CLASSES())
|
354
|
-
end
|
355
|
-
|
356
|
-
def rules2mask(rules)
|
357
|
-
mask=0
|
358
|
-
@rules.each_with_index{|r,i|
|
359
|
-
mask |= 1<<i if rules.include? r
|
360
|
-
}
|
361
|
-
return mask
|
362
|
-
end
|
363
|
-
|
364
|
-
def mask2rules(mask)
|
365
|
-
rules=[]
|
366
|
-
@rules.each_with_index{|r,i|
|
367
|
-
rules<<r if mask&(1<<i)
|
368
|
-
}
|
369
|
-
return rules
|
370
|
-
end
|
371
|
-
|
372
|
-
def mask2rules(mask)
|
373
|
-
result=[]
|
374
|
-
while mask.nonzero?
|
375
|
-
result<< @rules[i=ffs(mask)-1]
|
376
|
-
mask &= ~(1<<i)
|
377
|
-
end
|
378
|
-
return result
|
379
|
-
end
|
380
|
-
|
381
|
-
def each_rule(mask=-1)
|
382
|
-
@rules.each_with_index{|r,i|
|
383
|
-
yield r,i if mask&(1<<i)
|
384
|
-
}
|
385
|
-
end
|
386
|
-
|
387
|
-
def each_rule(mask=@maxmask)
|
388
|
-
while mask.nonzero?
|
389
|
-
yield @rules[i=ffs(mask)-1],i
|
390
|
-
mask &= ~(1<<i)
|
391
|
-
end
|
392
|
-
end
|
393
|
-
|
394
|
-
|
395
|
-
@@FFS_TABLE=[nil]
|
396
|
-
1.upto(8){|n|
|
397
|
-
@@FFS_TABLE*=2
|
398
|
-
@@FFS_TABLE[@@FFS_TABLE.size/2]=n
|
399
|
-
}
|
400
|
-
def rb_ffs(mask)
|
401
|
-
chunks=0
|
402
|
-
until mask.zero?
|
403
|
-
result=@@FFS_TABLE[mask&0xFF]
|
404
|
-
return result+(chunks<<3) if result
|
405
|
-
chunks+=1
|
406
|
-
mask>>=8
|
407
|
-
end
|
408
|
-
return 0
|
409
|
-
end
|
410
|
-
|
411
|
-
begin
|
412
|
-
require 'inline'
|
413
|
-
inline{|inline|
|
414
|
-
inline.prefix '#define _GNU_SOURCE'
|
415
|
-
inline.include '"string.h"'
|
416
|
-
inline.include '"limits.h"'
|
417
|
-
inline.c %{
|
418
|
-
unsigned c_ffs(VALUE mask){
|
419
|
-
if FIXNUM_P(mask) {
|
420
|
-
return ffsl(NUM2UINT(mask));
|
421
|
-
} else if(TYPE(mask)==T_BIGNUM) {
|
422
|
-
struct RBignum* bn=RBIGNUM(mask);
|
423
|
-
int len=bn->len;
|
424
|
-
int i;
|
425
|
-
unsigned offset=0;
|
426
|
-
unsigned result=0;
|
427
|
-
for(i=0;i<len;++i){
|
428
|
-
/*printf("least:%x\\n", ((BDIGIT*)(bn->digits))[i]);*/
|
429
|
-
/*printf("most:%x\\n", ((BDIGIT*)(bn->digits))[len]);*/
|
430
|
-
result=ffs(((BDIGIT*)(bn->digits))[i]);
|
431
|
-
if (result) break;
|
432
|
-
offset+=sizeof(int)*CHAR_BIT;
|
433
|
-
}
|
434
|
-
if (result==0) return 0;
|
435
|
-
return result+offset;
|
436
|
-
} else {
|
437
|
-
rb_fatal("bad argument to ffs");
|
438
|
-
}
|
439
|
-
}
|
440
|
-
}
|
441
|
-
}
|
442
|
-
alias ffs c_ffs
|
443
|
-
rescue Exception=>e
|
444
|
-
warn "error (#{e.class}) while defining inline c ffs()"
|
445
|
-
warn "original error: #{e}"
|
446
|
-
warn "falling back to ruby version of ffs()"
|
447
|
-
alias ffs rb_ffs
|
448
|
-
|
449
|
-
end
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
#just the left side (the stack/lookahead matchers)
|
455
|
-
def LEFT
|
456
|
-
@rules.map{|r| r.left.subregs }.flatten
|
457
|
-
end
|
458
|
-
|
459
|
-
#remove lookahead and lookback decoration
|
460
|
-
def LEFT_NO_LOOKING
|
461
|
-
l=LEFT()
|
462
|
-
l.map!{|m|
|
463
|
-
case m #
|
464
|
-
when Reg::LookAhead,Reg::LookBack; m.subregs[0]
|
465
|
-
when Proc; []
|
466
|
-
else m #
|
467
|
-
end #
|
468
|
-
}
|
469
|
-
l
|
470
|
-
end
|
471
|
-
|
472
|
-
#all classes mentioned in rules, on left and right sides
|
473
|
-
def STACKABLE_CLASSES #
|
474
|
-
return @sc_result unless @sc_result.nil?
|
475
|
-
@sc_result=false
|
476
|
-
l=LEFT_NO_LOOKING()
|
477
|
-
l=l.map{|lm| sc_juice lm}.flatten.compact
|
478
|
-
r= @rules.map{|rr| rr.right }.grep(Class) #classes in productions
|
479
|
-
result=l+r
|
480
|
-
@sc_result=result.grep(Class).uniq
|
481
|
-
fail if @sc_result.empty?
|
482
|
-
return @sc_result
|
483
|
-
end
|
484
|
-
|
485
|
-
def juice(m)
|
486
|
-
case m #
|
487
|
-
when Class;
|
488
|
-
return [m] unless @subclasses_of
|
489
|
-
result=[m] # and subclasses too
|
490
|
-
i=0
|
491
|
-
while item=result[i]
|
492
|
-
#p item
|
493
|
-
result.concat @subclasses_of[item]
|
494
|
-
i += 1
|
495
|
-
end
|
496
|
-
result
|
497
|
-
when String,Regexp; juice(RedParse.KW(m))
|
498
|
-
when Reg::And; m.subregs.map{|x| juice(x).flatten.compact}.inject{|sum,rr| sum&rr}
|
499
|
-
when Reg::Or; m.subregs.map( &method(:juice) )
|
500
|
-
when Reg::Not;
|
501
|
-
m=m.subregs[0]
|
502
|
-
if Class===m or (Reg::Or===m and
|
503
|
-
m.subregs.inject{|sum,x| sum && (Class===x) })
|
504
|
-
j=juice(m)
|
505
|
-
STACKABLE_CLASSES()-j.flatten.compact rescue j
|
506
|
-
else
|
507
|
-
STACKABLE_CLASSES()
|
508
|
-
end
|
509
|
-
else STACKABLE_CLASSES()
|
510
|
-
end
|
511
|
-
end
|
512
|
-
|
513
|
-
def sc_juice(m)
|
514
|
-
case m #
|
515
|
-
when Class; [m]
|
516
|
-
when String,Regexp; juice(RedParse.KW(m))
|
517
|
-
# when String,Regexp; [KeywordToken]
|
518
|
-
when Reg::And; m.subregs.map{|x| sc_juice(x)}.compact.map{|x| x.flatten.compact}.inject{|sum,rr| sum&rr }
|
519
|
-
when Reg::Or; m.subregs.map( &method(:sc_juice) )
|
520
|
-
when Reg::Not; sc_juice(m.subregs[0])
|
521
|
-
when Reg::LookAhead, Reg::LookBack; sc_juice(m.subregs[0])
|
522
|
-
else []
|
523
|
-
end
|
524
|
-
end
|
525
|
-
|
526
|
-
def LOOKAHEAD_CLASSES rule
|
527
|
-
last=rule.left.subregs.last
|
528
|
-
return STACKABLE_CLASSES() unless Reg::LookAhead===last
|
529
|
-
la= last.subregs[0]
|
530
|
-
return juice(la).flatten.compact
|
531
|
-
end
|
532
|
-
#
|
533
|
-
def TOS_CLASSES rule
|
534
|
-
i=-1
|
535
|
-
mats=rule.left.subregs
|
536
|
-
m=mats[i]
|
537
|
-
m=mats[i-=1] if Reg::LookAhead===m || Proc===m
|
538
|
-
result=[]
|
539
|
-
while Reg::Repeat===m and m.times.min.zero?
|
540
|
-
result<<juice(m.subregs[0])
|
541
|
-
m=mats[i-=1]
|
542
|
-
end
|
543
|
-
return (result+juice(m)).flatten.compact
|
544
|
-
end
|
545
|
-
|
546
|
-
def [](i)
|
547
|
-
@rules[i]
|
548
|
-
end
|
549
|
-
|
550
|
-
end #
|
551
|
-
#
|
552
|
-
module Reducer
|
553
|
-
@@rulesets={}
|
554
|
-
@@class_narrowerses={}
|
555
|
-
def compile(recompile=false)
|
556
|
-
klass=self.class
|
557
|
-
|
558
|
-
#use cached result if available
|
559
|
-
if @@rulesets[klass] and !recompile
|
560
|
-
@ruleset=@@rulesets[klass]
|
561
|
-
@class_narrowers=@@class_narrowerses[klass]
|
562
|
-
return
|
563
|
-
end
|
564
|
-
|
565
|
-
#actual rule compilation
|
566
|
-
@ruleset=RuleSet.new @rules
|
567
|
-
@class_narrowers=[tos=Hash.new(0),la=Hash.new(0)]
|
568
|
-
@ruleset.each_rule{|r,i|
|
569
|
-
@ruleset.LOOKAHEAD_CLASSES(r).each{|klass2|
|
570
|
-
la[klass2] |= 1<<i
|
571
|
-
}
|
572
|
-
@ruleset.TOS_CLASSES(r).each{|klass2|
|
573
|
-
tos[klass2] |= 1<<i
|
574
|
-
}
|
575
|
-
}
|
576
|
-
|
577
|
-
#save result to cache if not too dynamic
|
578
|
-
if !recompile
|
579
|
-
@@rulesets[klass]=@ruleset
|
580
|
-
@@class_narrowerses[klass]=@class_narrowers
|
581
|
-
end
|
582
|
-
end
|
583
|
-
|
584
|
-
def new_reduce
|
585
|
-
# mask=-1
|
586
|
-
# (-1).downto(-@class_narrowers.size){|i|
|
587
|
-
# mask &= @class_narrowers[i][@stack[i].class]
|
588
|
-
# }
|
589
|
-
mask=
|
590
|
-
@class_narrowers[-1][@stack[-1].class]&
|
591
|
-
@class_narrowers[-2][@stack[-2].class]
|
592
|
-
@ruleset.each_rule(mask){|r,i|
|
593
|
-
res=evaluate(r) and return res
|
594
|
-
}
|
595
|
-
return false
|
596
|
-
end
|
597
|
-
end
|
598
|
-
end
|
599
|
-
|
600
|
-
def map_with_index(list)
|
601
|
-
result=[]
|
602
|
-
list.each_with_index{|elem,i| result<<yield(elem,i)}
|
603
|
-
result
|
604
|
-
end
|
605
|
-
|
606
|
-
def all_rules
|
607
|
-
return @all_rules if defined? @all_rules
|
608
|
-
|
609
|
-
@inputs||=enumerate_exemplars
|
610
|
-
@rules=expanded_RULES #force it to be recalculated
|
611
|
-
@all_rules = map_with_index(@rules){|r,i| Rule.new r,i}
|
612
|
-
|
613
|
-
@all_rules.each{|r|
|
614
|
-
if StackMonkey===r.action
|
615
|
-
r.action.exemplars=@inputs.grep r.action.hint
|
616
|
-
end
|
617
|
-
}
|
618
|
-
|
619
|
-
warn "error recovery rules disabled for now; creates too many states and masks errors"
|
620
|
-
@all_rules.reject!{|r| r.action==MisparsedNode }
|
621
|
-
|
622
|
-
#names have to be allocated globally to make sure they don't collide
|
623
|
-
names=@all_rules.map{|r|
|
624
|
-
if r.action.respond_to? :name
|
625
|
-
r.action.name
|
626
|
-
else
|
627
|
-
r.action.to_s
|
628
|
-
end
|
629
|
-
}.sort
|
630
|
-
dups={}
|
631
|
-
names.each_with_index{|name,i|
|
632
|
-
dups[name]=0 if name==names[i+1]
|
633
|
-
}
|
634
|
-
@all_rules.each{|r|
|
635
|
-
r.name=
|
636
|
-
if r.action.respond_to? :name
|
637
|
-
r.action.name.dup
|
638
|
-
else
|
639
|
-
r.action.to_s
|
640
|
-
end
|
641
|
-
if dups[r.name]
|
642
|
-
count=dups[r.name]+=1
|
643
|
-
r.name<<"_#{count}"
|
644
|
-
end
|
645
|
-
}
|
646
|
-
end
|
647
|
-
|
648
|
-
def all_dotted_rules
|
649
|
-
all_rules.map{|rule|
|
650
|
-
(0...rule.patterns.size).map{|i|
|
651
|
-
DottedRule.create(rule,i,self)
|
652
|
-
}
|
653
|
-
}.flatten
|
654
|
-
end
|
655
|
-
|
656
|
-
#$OLD_PAA=1
|
657
|
-
|
658
|
-
def all_initial_dotted_rules
|
659
|
-
return @all_initial_dotted_rules if defined? @all_initial_dotted_rules
|
660
|
-
@all_initial_dotted_rules=result=
|
661
|
-
all_rules.map{|rule| DottedRule.create(rule,0,nil) }
|
662
|
-
|
663
|
-
p :all_init
|
664
|
-
|
665
|
-
unless defined? $OLD_PAA
|
666
|
-
scanning=result
|
667
|
-
provisionals=nil
|
668
|
-
while true
|
669
|
-
old_provisionals=provisionals
|
670
|
-
provisionals={}
|
671
|
-
scanning.each{|dr|
|
672
|
-
dr.also_allow=dr.compute_also_allow(provisional=[false]) #fill out dr.also_allow
|
673
|
-
provisionals[dr]=provisional[0]
|
674
|
-
}
|
675
|
-
scanning=provisionals.map{|dr,val| dr if val }.compact
|
676
|
-
end until provisionals==old_provisionals
|
677
|
-
end
|
678
|
-
p :all_init_done
|
679
|
-
|
680
|
-
return result
|
681
|
-
end
|
682
|
-
|
683
|
-
class Rule #original user rules, slightly chewed on
|
684
|
-
def initialize(rawrule,priority)
|
685
|
-
@priority=priority
|
686
|
-
@action=rawrule.right
|
687
|
-
@patterns=rawrule.left.subregs.dup
|
688
|
-
#remove lookback decoration if any, just note that lb was present
|
689
|
-
if Reg::LookBack===@patterns[0]
|
690
|
-
@lookback=true
|
691
|
-
@patterns[0]=@patterns[0].subregs[0]
|
692
|
-
end
|
693
|
-
|
694
|
-
case @patterns[-1]
|
695
|
-
#Symbol is pointless here, methinks.
|
696
|
-
when Proc,Symbol; #do nothing
|
697
|
-
when Reg::LookAhead; @patterns[-1]=@patterns[-1].subregs[0]
|
698
|
-
else @patterns.push Object #add la if none was present
|
699
|
-
end
|
700
|
-
|
701
|
-
#search for looping matchers with minimum >0 and replace them
|
702
|
-
#with a number of scalars (== the minimum) followed by a loop with 0 min.
|
703
|
-
#search for bare strings or regexps and replace with KW( ) wrapper
|
704
|
-
@patterns.each_with_index{|p,i|
|
705
|
-
case p
|
706
|
-
when String,Regexp; @patterns[i]=RedParse.KW(p)
|
707
|
-
when Reg::Repeat
|
708
|
-
if p.itemrange.first>0
|
709
|
-
@patterns[i,1]=
|
710
|
-
*[p.subregs[0]]*p.itemrange.first<< #minimum # as scalars
|
711
|
-
p.subregs[0].reg.* #0-based looper
|
712
|
-
end
|
713
|
-
end
|
714
|
-
}
|
715
|
-
@drs=[]
|
716
|
-
end
|
717
|
-
|
718
|
-
attr_reader :drs
|
719
|
-
|
720
|
-
def hash; priority end
|
721
|
-
def == other; Rule===other and priority==other.priority end
|
722
|
-
alias eql? ==
|
723
|
-
|
724
|
-
def lookback?; @lookback if defined? @lookback end
|
725
|
-
|
726
|
-
attr_reader :patterns,:action,:priority
|
727
|
-
attr_accessor :name
|
728
|
-
|
729
|
-
def at(n)
|
730
|
-
result=patterns[n]
|
731
|
-
result=result.subregs[0] if Reg::Repeat===result
|
732
|
-
result
|
733
|
-
end
|
734
|
-
def optional? n
|
735
|
-
p=patterns[n]
|
736
|
-
return Reg::Repeat===p && p.itemrange.first.zero?
|
737
|
-
end
|
738
|
-
def looping? n
|
739
|
-
p=patterns[n]
|
740
|
-
return false unless Reg::Repeat===p
|
741
|
-
return false if p.itemrange.last==1
|
742
|
-
fail unless p.itemrange.last.infinite?
|
743
|
-
return true
|
744
|
-
rescue Exception
|
745
|
-
return false
|
746
|
-
end
|
747
|
-
|
748
|
-
def reduces_to
|
749
|
-
case @action
|
750
|
-
when Class; @action
|
751
|
-
when StackMonkey; @action.exemplars
|
752
|
-
when :error,:shift,:accept; nil
|
753
|
-
else fail "#@action unexpected in reduces_to"
|
754
|
-
end
|
755
|
-
end
|
756
|
-
|
757
|
-
def unruly?
|
758
|
-
return if action==:accept
|
759
|
-
action.class!=Class || lookback?
|
760
|
-
end
|
761
|
-
|
762
|
-
def final_promised_pattern
|
763
|
-
case @action
|
764
|
-
when DeleteMonkey #delete_monkey
|
765
|
-
vector_indexes=(@action.first_changed_index..-1).select{|i| Reg::Repeat===@patterns[i] }
|
766
|
-
fail unless vector_indexes.empty?
|
767
|
-
result=@patterns.dup
|
768
|
-
result.delete_at @action.first_changed_index
|
769
|
-
when StackMonkey #stack_monkey
|
770
|
-
result=@patterns.dup
|
771
|
-
result[@action.first_changed_index..-1]=[@action.hint]
|
772
|
-
when Class
|
773
|
-
result= [@action,@patterns.last]
|
774
|
-
result.unshift @patterns.first if lookback?
|
775
|
-
when :accept, :error, :shift
|
776
|
-
result=@patterns.dup
|
777
|
-
else
|
778
|
-
pp @action
|
779
|
-
fail
|
780
|
-
end
|
781
|
-
result[-1]=result[-1].la unless result.empty?
|
782
|
-
result
|
783
|
-
end
|
784
|
-
|
785
|
-
def final_promised_rule
|
786
|
-
@final_promised_rule ||=
|
787
|
-
Rule.new(-final_promised_pattern>>nil,-priority)
|
788
|
-
end
|
789
|
-
end
|
790
|
-
|
791
|
-
class DottedRule
|
792
|
-
def initialize(rule,pos,parser)
|
793
|
-
@rule,@pos=rule,pos
|
794
|
-
fail unless (0...rule.patterns.size)===@pos
|
795
|
-
# @also_allow= compute_also_allow(parser) if parser unless defined? $OLD_PAA
|
796
|
-
end
|
797
|
-
def compute_also_allow(parser,provisional=[false])
|
798
|
-
parser.all_initial_dotted_rules.map{|dr|
|
799
|
-
next if dr==self
|
800
|
-
fake_rule=dr.rule.final_promised_rule
|
801
|
-
final_more_dr=DottedRule.create(fake_rule,0,nil)
|
802
|
-
also=dr.also_allow
|
803
|
-
unless also
|
804
|
-
provisional[0]||=0
|
805
|
-
provisional[0]+=1
|
806
|
-
also=[]
|
807
|
-
end
|
808
|
-
also+[dr] if optionally_combine final_more_dr,parser
|
809
|
-
}.flatten.compact.uniq
|
810
|
-
end
|
811
|
-
attr_reader :rule,:pos
|
812
|
-
attr_accessor :also_allow
|
813
|
-
|
814
|
-
def self.create(rule,pos,parser)
|
815
|
-
result=rule.drs[pos] and return result
|
816
|
-
result=rule.drs[pos]=DottedRule.new(rule,pos,parser)
|
817
|
-
unless defined? $OLD_PAA
|
818
|
-
result.also_allow=result.compute_also_allow(parser) if parser
|
819
|
-
end
|
820
|
-
return result
|
821
|
-
end
|
822
|
-
|
823
|
-
def hash; (@rule.priority<<3)^@pos end
|
824
|
-
def == other; DottedRule===other and @pos==other.pos and @rule==other.rule end
|
825
|
-
alias eql? ==
|
826
|
-
|
827
|
-
def name; @rule.name+"@#@pos" end
|
828
|
-
|
829
|
-
def looping?
|
830
|
-
@rule.looping?(@pos)
|
831
|
-
end
|
832
|
-
|
833
|
-
#returns Conditional|Rule|DottedRule|+[DottedRule.+]|nil
|
834
|
-
def evolve input, parser, seenlist,result2
|
835
|
-
#print "["
|
836
|
-
#$stdout.flush
|
837
|
-
idname=input.identity_name
|
838
|
-
idname=parser.identity_name_alias? idname
|
839
|
-
cache=seenlist[[self,idname]]
|
840
|
-
unless cache==:dunno_yet
|
841
|
-
result2.concat Array(cache).flatten.compact.uniq.sort_by{|x| x.name}
|
842
|
-
return cache
|
843
|
-
end
|
844
|
-
i=pos
|
845
|
-
lasti=i-1
|
846
|
-
result=[]
|
847
|
-
result=loop do #might need multiple tries if optional matcher(s) here
|
848
|
-
fail unless i>lasti
|
849
|
-
lasti=i
|
850
|
-
p=@rule.at(i) #what is current pattern in this dottedrule?
|
851
|
-
fail if Proc===p #shouldnt happen anymore
|
852
|
-
if parser.pattern_matches_nodes? p
|
853
|
-
|
854
|
-
#if any dotted rules have nodes at this point,
|
855
|
-
#also include the set of rules@0 which
|
856
|
-
#can (possibly indirectly) generate that node.
|
857
|
-
#(match tokens found on left sides of productions for p)
|
858
|
-
seenlist[[self,idname]]=result
|
859
|
-
if false
|
860
|
-
result.concat recurse_match_drs(parser).uniq.map{|dr|
|
861
|
-
dr and
|
862
|
-
#begin print "{#{dr.name}"
|
863
|
-
dr.evolve input,parser,seenlist,result2
|
864
|
-
#ensure print "}" end
|
865
|
-
}.flatten.compact.uniq
|
866
|
-
end
|
867
|
-
end
|
868
|
-
@saw_item_that={}
|
869
|
-
if p===input
|
870
|
-
i+=1 unless @rule.looping?(i)
|
871
|
-
fail if i>@rule.patterns.size
|
872
|
-
|
873
|
-
if !@saw_item_that.empty?
|
874
|
-
p(:saw_item_that!)
|
875
|
-
fail unless @saw_item_that.size==1
|
876
|
-
pair=@saw_item_that.to_a.first
|
877
|
-
fail unless p.equal? pair.last
|
878
|
-
it=pair.first
|
879
|
-
action=
|
880
|
-
if i==@rule.patterns.size
|
881
|
-
@rule
|
882
|
-
else
|
883
|
-
DottedRule.create(@rule,i,parser)
|
884
|
-
end
|
885
|
-
break Conditional.new(it,action)
|
886
|
-
end
|
887
|
-
@saw_item_that=nil
|
888
|
-
|
889
|
-
if i == @rule.patterns.size
|
890
|
-
break @rule
|
891
|
-
else
|
892
|
-
break result<<DottedRule.create(@rule,i,parser)
|
893
|
-
end
|
894
|
-
elsif !@rule.optional?(i)
|
895
|
-
break result.empty? ? nil : result
|
896
|
-
elsif (i+=1) >= @rule.patterns.size
|
897
|
-
break @rule
|
898
|
-
#else next p
|
899
|
-
end
|
900
|
-
end #loop
|
901
|
-
seenlist[[self,idname]]=result
|
902
|
-
result2.concat Array(result).flatten.compact.uniq.sort_by{|x| x.name}
|
903
|
-
return result
|
904
|
-
#ensure print "]"
|
905
|
-
end
|
906
|
-
|
907
|
-
#returns +[(DottedRule|nil).*]
|
908
|
-
def recurse_match_drs parser, result=nil
|
909
|
-
unless result
|
910
|
-
table=parser.rmd_cache
|
911
|
-
if table
|
912
|
-
cache=table[self]
|
913
|
-
return cache if cache
|
914
|
-
else
|
915
|
-
parser.rmd_cache={}
|
916
|
-
end
|
917
|
-
|
918
|
-
result=[]
|
919
|
-
end
|
920
|
-
#print "("
|
921
|
-
#print @rule.name+"@#@pos"
|
922
|
-
p=@rule.at(@pos)
|
923
|
-
|
924
|
-
#find set of nodes that could match here
|
925
|
-
nodes_here=parser.exemplars_that_match(p&Node)
|
926
|
-
|
927
|
-
#find the set of rules that could generate a node in our list
|
928
|
-
rrules=parser.all_rules.select{|rule|
|
929
|
-
!rule.unruly? and !nodes_here.grep(rule.action).empty?
|
930
|
-
}.map{|rule|
|
931
|
-
DottedRule.create(rule,0,parser)
|
932
|
-
}
|
933
|
-
|
934
|
-
#if any generating rules match a node in the leftmost pattern,
|
935
|
-
#add the rules which can generate _that_ node too.
|
936
|
-
result.push self #force self to be excluded from future recursion
|
937
|
-
oldsize=result.size
|
938
|
-
unless rrules.empty?
|
939
|
-
result.concat rrules
|
940
|
-
|
941
|
-
unless result.respond_to? :index_of
|
942
|
-
class<<result
|
943
|
-
attr_accessor :index_of
|
944
|
-
end
|
945
|
-
result.index_of={}
|
946
|
-
end
|
947
|
-
rio=result.index_of
|
948
|
-
oldsize.upto(result.size){|i| rio[result[i]]||=i }
|
949
|
-
rrules.each{|rrule|
|
950
|
-
i=rio[rrule] or fail #index() inside each() == O(N**2) complexity. this is the slow line.
|
951
|
-
#but skip recursion on rules already done at a higher level
|
952
|
-
rrule.recurse_match_drs parser,result if i>=oldsize
|
953
|
-
}
|
954
|
-
end
|
955
|
-
result[oldsize-1]=nil #don't actually include self in result
|
956
|
-
#result.update_indices oldsize-1, oldsize-1
|
957
|
-
|
958
|
-
parser.rmd_cache[self]=result
|
959
|
-
return result
|
960
|
-
#ensure print ")"
|
961
|
-
end
|
962
|
-
|
963
|
-
def optionally_combine weaker,parser
|
964
|
-
#lotsa caching needed if this is ever to be performant
|
965
|
-
if parser.oc_cache
|
966
|
-
result=parser.oc_cache[[self,weaker]]
|
967
|
-
return result unless result.nil?
|
968
|
-
else
|
969
|
-
parser.oc_cache={}
|
970
|
-
end
|
971
|
-
|
972
|
-
other=weaker
|
973
|
-
mymatches,myposes= self.outcomes
|
974
|
-
matches, poses = other.outcomes
|
975
|
-
matches.each_with_index{|match,i|
|
976
|
-
mymatches.each_with_index{|mymatch,myi|
|
977
|
-
intersect=parser.inputs.grep(match&mymatch)
|
978
|
-
unless intersect.empty?
|
979
|
-
|
980
|
-
#but don't allow matches that would be matched
|
981
|
-
#by an earlier (but optional) pattern.
|
982
|
-
disallowed=Reg::Or.new(
|
983
|
-
*possible_matchers_til(myi)+
|
984
|
-
other.possible_matchers_til(i)
|
985
|
-
)
|
986
|
-
intersect.reject{|x| disallowed===x }
|
987
|
-
|
988
|
-
if intersect.empty?
|
989
|
-
return result=false
|
990
|
-
elsif poses[i]>=other.rule.patterns.size
|
991
|
-
return result=true #success if weaker rule is at an end
|
992
|
-
elsif myposes[myi]>=rule.patterns.size
|
993
|
-
return result=false #fail if stronger rule at an end
|
994
|
-
else
|
995
|
-
p [:**,rule.name,myposes[myi]]
|
996
|
-
mynew=DottedRule.create(rule,myposes[myi],parser)
|
997
|
-
new=DottedRule.create(other.rule,poses[i],parser)
|
998
|
-
return result=mynew.optionally_combine( new,parser )
|
999
|
-
end
|
1000
|
-
end
|
1001
|
-
}
|
1002
|
-
}
|
1003
|
-
return result=false
|
1004
|
-
ensure
|
1005
|
-
parser.oc_cache[[self,weaker]]=result
|
1006
|
-
end
|
1007
|
-
|
1008
|
-
def possible_matchers_til i
|
1009
|
-
(pos...i-1).map{|j|
|
1010
|
-
m=rule.at(j)
|
1011
|
-
Reg::Repeat===m ? m.subregs[0] : m
|
1012
|
-
}
|
1013
|
-
end
|
1014
|
-
|
1015
|
-
def outcomes
|
1016
|
-
til=@rule.patterns.size
|
1017
|
-
at=@pos
|
1018
|
-
result=[[],[]]
|
1019
|
-
loop do
|
1020
|
-
m=@rule.patterns[at]
|
1021
|
-
case m
|
1022
|
-
when Proc;
|
1023
|
-
result.first.push Object
|
1024
|
-
result.last.push at+1
|
1025
|
-
break
|
1026
|
-
when Reg::Repeat
|
1027
|
-
assert @rule.optional?(at)
|
1028
|
-
to=at
|
1029
|
-
to+=1 unless @rule.looping? at
|
1030
|
-
result.first.push m.subregs[0]
|
1031
|
-
result.last.push to
|
1032
|
-
else
|
1033
|
-
result.first.push m
|
1034
|
-
result.last.push at+1
|
1035
|
-
break
|
1036
|
-
end
|
1037
|
-
at+=1
|
1038
|
-
break if at>=til
|
1039
|
-
end
|
1040
|
-
return result
|
1041
|
-
end
|
1042
|
-
|
1043
|
-
end
|
1044
|
-
|
1045
|
-
attr_accessor :rmd_cache
|
1046
|
-
attr_accessor :oc_cache
|
1047
|
-
attr_accessor :sl2ms_cache
|
1048
|
-
|
1049
|
-
class Conditional
|
1050
|
-
def initialize(condition,action)
|
1051
|
-
@condition,@action=condition,action
|
1052
|
-
@condition.restore :hash,:==
|
1053
|
-
end
|
1054
|
-
attr_reader :condition,:action
|
1055
|
-
|
1056
|
-
def hash
|
1057
|
-
@condition.hash^@action.hash
|
1058
|
-
end
|
1059
|
-
def == other
|
1060
|
-
Conditional===other and @condition==other.condition and @action==other.action
|
1061
|
-
end
|
1062
|
-
alias eql? ==
|
1063
|
-
|
1064
|
-
def name; @condition.inspect+"?"+@action.name end
|
1065
|
-
|
1066
|
-
def priority; @action.priority end
|
1067
|
-
end
|
1068
|
-
|
1069
|
-
class ParserState; end
|
1070
|
-
class MultiShift; end
|
1071
|
-
class MultiReduce; end
|
1072
|
-
|
1073
|
-
ACTION_PATTERN=ParserState|Rule|MultiShift|MultiReduce|:accept|:error
|
1074
|
-
class ParserState #a union of dotted rules
|
1075
|
-
def initialize(dotteds,index)
|
1076
|
-
fail if dotteds.empty? #error state
|
1077
|
-
fail unless dotteds.grep(nil).empty?
|
1078
|
-
@dotteds=dotteds
|
1079
|
-
@index=index
|
1080
|
-
sort_substates!
|
1081
|
-
@actions={} #key is an input, value is ParserState|Rule|MultiShift|MultiReduce|:accept|:error
|
1082
|
-
end
|
1083
|
-
|
1084
|
-
attr_reader :actions
|
1085
|
-
|
1086
|
-
def [](k)
|
1087
|
-
result=@actions[k]
|
1088
|
-
assert ACTION_PATTERN===result
|
1089
|
-
result
|
1090
|
-
end
|
1091
|
-
def []=(k,v)
|
1092
|
-
assert ACTION_PATTERN===v
|
1093
|
-
@actions[k]=v
|
1094
|
-
end
|
1095
|
-
|
1096
|
-
def sort_substates!
|
1097
|
-
@dotteds=@dotteds.sort_by{|dotted| -dotted.pos}.uniq
|
1098
|
-
end
|
1099
|
-
attr :dotteds
|
1100
|
-
|
1101
|
-
def dup
|
1102
|
-
result=super
|
1103
|
-
result.instance_variable_set(:@dotteds,@dotteds.dup)
|
1104
|
-
return result
|
1105
|
-
end
|
1106
|
-
|
1107
|
-
def substates; [self] end
|
1108
|
-
|
1109
|
-
def shiftlist2multishift? shiftlist,parser
|
1110
|
-
return :error if shiftlist.empty?
|
1111
|
-
parser.sl2ms_cache||={}
|
1112
|
-
cache=parser.sl2ms_cache[shiftlist]
|
1113
|
-
return cache if cache
|
1114
|
-
fixed,varying=shiftlist.partition{|res| DottedRule===res}
|
1115
|
-
result=ParserState.new(fixed,nil)
|
1116
|
-
result.perhaps_also_allow parser.all_rules,parser
|
1117
|
-
unless varying.empty? #MultiShift
|
1118
|
-
varying.map!{|v| [v.condition,v.action]}.flatten
|
1119
|
-
result=MultiShift.new(result,varying)
|
1120
|
-
end
|
1121
|
-
parser.sl2ms_cache[shiftlist]=result
|
1122
|
-
return result
|
1123
|
-
end
|
1124
|
-
|
1125
|
-
#given a list of rules, see if any of them are compatible with
|
1126
|
-
#a current substate. (compatibility means the aggregate patterns
|
1127
|
-
#can be anded together and still be able to conceivably match something.)
|
1128
|
-
#if any of morerules are actually compatible, add it to current state.
|
1129
|
-
def perhaps_also_allow(morerules,parser)
|
1130
|
-
fail unless morerules==parser.all_rules
|
1131
|
-
@dotteds.concat @dotteds.map{|d| d.also_allow }.flatten.compact.uniq
|
1132
|
-
sort_substates!
|
1133
|
-
end
|
1134
|
-
def old_perhaps_also_allow(morerules,parser)
|
1135
|
-
morerules=morerules.dup
|
1136
|
-
need_sort=false
|
1137
|
-
scan_rules=@dotteds
|
1138
|
-
added={}
|
1139
|
-
while true
|
1140
|
-
adding=[]
|
1141
|
-
morerules.each{|morerule|
|
1142
|
-
next if added[morerule]
|
1143
|
-
fake_rule=morerule.final_promised_rule
|
1144
|
-
final_more_dr=DottedRule.create(fake_rule,0,parser)
|
1145
|
-
scan_rules.each{|dotted|
|
1146
|
-
if dotted.optionally_combine final_more_dr,parser
|
1147
|
-
adding<<DottedRule.create(morerule,0,parser)
|
1148
|
-
added[morerule]=1
|
1149
|
-
break
|
1150
|
-
end
|
1151
|
-
}
|
1152
|
-
}
|
1153
|
-
break if adding.empty?
|
1154
|
-
@dotteds.concat adding
|
1155
|
-
need_sort=true
|
1156
|
-
scan_rules=adding
|
1157
|
-
end
|
1158
|
-
sort_substates! if need_sort
|
1159
|
-
end
|
1160
|
-
alias perhaps_also_allow old_perhaps_also_allow if defined? $OLD_PAA
|
1161
|
-
|
1162
|
-
|
1163
|
-
#returns ParserState|MultiShift|MultiReduce|Rule|:accept|:error
|
1164
|
-
def evolve input,parser,seenlist
|
1165
|
-
result2=[]
|
1166
|
-
@dotteds.each{|dotted|
|
1167
|
-
dotted.evolve input,parser,seenlist,result2
|
1168
|
-
}
|
1169
353
|
|
1170
|
-
result=
|
1171
|
-
#seenlist.values.flatten.compact.uniq.sort_by{|x| x.name}
|
1172
|
-
result2=result2.uniq.compact.sort_by{|x| x.name}
|
1173
|
-
#pp [result,result2].map{|x| x.map{|res| DottedRule===res ? res.name : res }}
|
1174
|
-
#pp result2.map{|res| DottedRule===res ? res.name : res }
|
1175
|
-
# result==result2 or fail
|
1176
|
-
|
1177
|
-
return result=:error if result.empty?
|
1178
|
-
|
1179
|
-
|
1180
|
-
#ok, who wants to shift and who wants to reduce?
|
1181
|
-
shiftlist,reducelist=result.partition{|res|
|
1182
|
-
DottedRule===res or
|
1183
|
-
Conditional===res && DottedRule===res.action
|
1184
|
-
}
|
1185
|
-
|
1186
|
-
#if no reducers at all, just try (multi?)shift
|
1187
|
-
return result=shiftlist2multishift?( shiftlist,parser )if reducelist.empty?
|
1188
|
-
|
1189
|
-
#line up reducers by priority
|
1190
|
-
actions=reducelist \
|
1191
|
-
.sort_by{|rule| -rule.priority }
|
1192
|
-
# .map{|rule| rule.action }
|
1193
|
-
#actions is +[(Rule|Conditional[Rule]).*]
|
1194
|
-
action=actions.shift #this first (unless conditional)
|
1195
|
-
#action is Rule|Conditional[Rule]
|
1196
|
-
result=
|
1197
|
-
case action.action
|
1198
|
-
when :error; return :error
|
1199
|
-
when Class, StackMonkey
|
1200
|
-
action
|
1201
|
-
when :accept
|
1202
|
-
:accept
|
1203
|
-
when :shift #this counts as a reduce at this point, but it writes shift instructions
|
1204
|
-
shiftlist2multishift? shiftlist,parser
|
1205
|
-
when Rule #oy, vey, was a Conditional
|
1206
|
-
shiftaction=shiftlist2multishift?(shiftlist,parser)
|
1207
|
-
fail unless Rule===action.action
|
1208
|
-
case action.action.action
|
1209
|
-
when :error; huh
|
1210
|
-
when :shift, StackMonkey, :accept, Class #MultiReduce
|
1211
|
-
first_fixed_index=actions.size
|
1212
|
-
#actions is +[(Rule|Conditional[Rule]).*]
|
1213
|
-
actions.each_with_index{|act,i|
|
1214
|
-
break first_fixed_index=i unless Conditional===act
|
1215
|
-
}
|
1216
|
-
condactions=actions[0...first_fixed_index].unshift(action)
|
1217
|
-
condactions=condactions.inject([]){|sum,cond|
|
1218
|
-
act=cond.action
|
1219
|
-
act=shiftaction if act==:shift #=>shiftlist?
|
1220
|
-
sum.push cond.condition, act
|
1221
|
-
}
|
1222
|
-
#possible optimization: one or more :shift right at end could be ignored
|
1223
|
-
if actions[first_fixed_index]
|
1224
|
-
action=actions[first_fixed_index].action
|
1225
|
-
else
|
1226
|
-
action=shiftaction
|
1227
|
-
end
|
1228
|
-
MultiReduce.new condactions,action #=>shiftlist?
|
1229
|
-
else fail
|
1230
|
-
end
|
1231
|
-
else fail "#{action} not expected here"
|
1232
|
-
end
|
1233
|
-
#stack monkeys/:accept are treated like reduce here
|
1234
|
-
ensure
|
1235
|
-
assert ACTION_PATTERN===result
|
1236
|
-
end
|
1237
|
-
|
1238
|
-
def name
|
1239
|
-
@name||@dotteds.map{|dotted| dotted.name}.join(",")
|
1240
|
-
end
|
1241
|
-
attr_writer :name
|
1242
|
-
|
1243
|
-
def rename(name2count)
|
1244
|
-
return @name if defined? @name
|
1245
|
-
name=most_prominent_members.map{|dotted| dotted.name}.join(",")
|
1246
|
-
if name2count[name]
|
1247
|
-
name2count[name]+=1
|
1248
|
-
name+="___"+name2count[name].to_s
|
1249
|
-
else
|
1250
|
-
name2count[name]=1
|
1251
|
-
end
|
1252
|
-
|
1253
|
-
@name=name
|
1254
|
-
end
|
1255
|
-
|
1256
|
-
def most_prominent_members
|
1257
|
-
result=@dotteds.select{|dr| dr.pos==@dotteds.first.pos }
|
1258
|
-
close2end=@dotteds.map{|dr| [dr,dr.rule.patterns.size-dr.pos]}.sort_by{|(o,k)| -k}
|
1259
|
-
result+=close2end.select{|(dr,k)| k==close2end.first.last}.map{|(dr,k)| dr}
|
1260
|
-
result2=result.reject{|dr| dr.pos==0 or dr.pos==1&&dr.rule.lookback?}
|
1261
|
-
result=result2 unless result2.empty?
|
1262
|
-
return result
|
1263
|
-
end
|
1264
|
-
|
1265
|
-
def hash
|
1266
|
-
-@dotteds.hash
|
1267
|
-
end
|
1268
|
-
def == other
|
1269
|
-
ParserState===other and
|
1270
|
-
@dotteds==other.dotteds
|
1271
|
-
end
|
1272
|
-
alias eql? ==
|
1273
|
-
|
1274
|
-
def looping?
|
1275
|
-
@dotteds.any?{|dotted| dotted.looping? }
|
1276
|
-
end
|
1277
|
-
|
1278
|
-
def transition_to_loop? input #not used
|
1279
|
-
action=@actions.input
|
1280
|
-
case action
|
1281
|
-
when :error; false
|
1282
|
-
when ParserState; action.looping? and action!=self
|
1283
|
-
when MultiShift,MultiReduce;
|
1284
|
-
action.transition_to_loop? input
|
1285
|
-
else fail
|
1286
|
-
end
|
1287
|
-
end
|
1288
|
-
|
1289
|
-
def make_sr_goto_tables
|
1290
|
-
name2exemplar={}
|
1291
|
-
@inputs.each{|i| name2exemplar[i.name]=i }
|
1292
|
-
|
1293
|
-
@goto={}; @sr={}
|
1294
|
-
goto_counts=Hash.new(0); sr_counts=Hash.new(0)
|
1295
|
-
actions.each_pair{|k,v|
|
1296
|
-
if Node===name2exemplar[k]
|
1297
|
-
@goto[k]=v
|
1298
|
-
goto_counts[v]+=1
|
1299
|
-
else
|
1300
|
-
assert(Token===name2exemplar[k])
|
1301
|
-
@sr[k]=v
|
1302
|
-
sr_counts[v]+=1
|
1303
|
-
end
|
1304
|
-
}
|
1305
|
-
dflt=goto_counts.sort_by{|v,c| c}.last[0]
|
1306
|
-
@goto.delete_if{|k,v| v==dflt }
|
1307
|
-
@goto.default=dflt
|
1308
|
-
|
1309
|
-
dflt=sr_counts.sort_by{|v,c| c}.last[0]
|
1310
|
-
@sr.delete_if{|k,v| v==dflt }
|
1311
|
-
@sr.default=dflt
|
1312
|
-
|
1313
|
-
@actions=nil
|
1314
|
-
end
|
1315
|
-
|
1316
|
-
end
|
1317
|
-
|
1318
|
-
class MultiReduce
|
1319
|
-
def initialize(list,default)
|
1320
|
-
@list,@default=list,default
|
1321
|
-
#default can be any valid action (except another MultiReduce)
|
1322
|
-
end
|
1323
|
-
|
1324
|
-
attr_reader :list,:default
|
1325
|
-
|
1326
|
-
def act(x)
|
1327
|
-
(0...@list.size).step(2){|i|
|
1328
|
-
return @list[i+1] if @list[i]===x
|
1329
|
-
}
|
1330
|
-
return default
|
1331
|
-
end
|
1332
|
-
|
1333
|
-
def substates
|
1334
|
-
if @default.respond_to? :substates
|
1335
|
-
@default.substates
|
1336
|
-
else
|
1337
|
-
[]
|
1338
|
-
end
|
1339
|
-
end
|
1340
|
-
|
1341
|
-
def actions
|
1342
|
-
result=[]
|
1343
|
-
(1...@list.size).step(2){|i|
|
1344
|
-
result << @list[i]
|
1345
|
-
}
|
1346
|
-
if @default.respond_to? :actions
|
1347
|
-
result.concat @default.actions
|
1348
|
-
elsif @default
|
1349
|
-
result<<@default
|
1350
|
-
end
|
1351
|
-
result
|
1352
|
-
end
|
1353
|
-
|
1354
|
-
def transition_to_loop? input #not used
|
1355
|
-
@default.transition_to_loop? input
|
1356
|
-
end
|
1357
|
-
|
1358
|
-
def hash
|
1359
|
-
@list.hash^~@default.hash
|
1360
|
-
end
|
1361
|
-
|
1362
|
-
def == other
|
1363
|
-
@list==other.list and @default==other.default
|
1364
|
-
end
|
1365
|
-
alias eql? ==
|
1366
|
-
end
|
1367
|
-
|
1368
|
-
class MultiShift
|
1369
|
-
def initialize(base,modifiers)
|
1370
|
-
@base,@modifiers=base,modifiers
|
1371
|
-
@map=
|
1372
|
-
(0...2**(modifiers.size/2)).map{|i| base.dup}
|
1373
|
-
@map.each_with_index{|state,i| #for each branch to the multishift
|
1374
|
-
(0...modifiers.size).step(2){|j| #for each predicate in the multishift
|
1375
|
-
if (i&(1<<j)).non_zero? #if the predicate tests true in this branch
|
1376
|
-
state.append modifiers[j+1] #add the predicates modifier to the state
|
1377
|
-
end
|
1378
|
-
}
|
1379
|
-
state.sort_substates!
|
1380
|
-
}
|
1381
|
-
end
|
1382
|
-
|
1383
|
-
def act(x)
|
1384
|
-
result=0
|
1385
|
-
(0...@modifiers.size).step(2){|i|
|
1386
|
-
result|=(1<<(i/2)) if @modifiers[i]===x
|
1387
|
-
}
|
1388
|
-
@map[result]
|
1389
|
-
end
|
1390
|
-
|
1391
|
-
attr_reader :map, :modifiers
|
1392
|
-
|
1393
|
-
def substates
|
1394
|
-
@map.dup
|
1395
|
-
end
|
1396
|
-
|
1397
|
-
def actions
|
1398
|
-
@map.dup
|
1399
|
-
end
|
1400
|
-
|
1401
|
-
def transition_to_loop? input #not used
|
1402
|
-
huh
|
1403
|
-
end
|
1404
|
-
|
1405
|
-
def hash
|
1406
|
-
huh
|
1407
|
-
end
|
1408
|
-
def == other
|
1409
|
-
huh
|
1410
|
-
end
|
1411
|
-
alias eql? ==
|
1412
|
-
end
|
1413
|
-
|
1414
|
-
#an action is one of:
|
1415
|
-
#a ParserState (shift)
|
1416
|
-
#a Rule (reduce)
|
1417
|
-
#nil (error)
|
1418
|
-
#:accept
|
1419
|
-
#MultiReduce
|
1420
|
-
#MultiShift
|
1421
|
-
|
1422
|
-
#just the left side (the stack/lookahead matchers)
|
1423
|
-
def LEFT
|
1424
|
-
# require 'md5'
|
1425
|
-
@rules=expanded_RULES()
|
1426
|
-
# p MD5.new(@rules).to_s
|
1427
|
-
@rules.map{|r| r.left.subregs }.flatten
|
1428
|
-
end
|
1429
|
-
|
1430
|
-
#remove lookahead and lookback decoration (not used?)
|
1431
|
-
def LEFT_NO_LOOKING
|
1432
|
-
l=LEFT()
|
1433
|
-
l.map!{|m|
|
1434
|
-
case m #
|
1435
|
-
when Reg::LookAhead,Reg::LookBack; fail #should be gone already now
|
1436
|
-
when Proc; []
|
1437
|
-
else m #
|
1438
|
-
end #
|
1439
|
-
}
|
1440
|
-
l
|
1441
|
-
end
|
1442
|
-
|
1443
|
-
def child_relations_among(*classes)
|
1444
|
-
classes.unshift Object
|
1445
|
-
result={}
|
1446
|
-
classes.each{|klass| result[klass]=[] }
|
1447
|
-
|
1448
|
-
#p classes
|
1449
|
-
classes.each{|klass|
|
1450
|
-
anclist=klass.ancestors
|
1451
|
-
anclist.shift==klass or fail
|
1452
|
-
anclist.each{|anc|
|
1453
|
-
if anc=result[anc]
|
1454
|
-
anc << klass
|
1455
|
-
break
|
1456
|
-
end
|
1457
|
-
}
|
1458
|
-
}
|
1459
|
-
|
1460
|
-
return result
|
1461
|
-
end
|
1462
|
-
|
1463
|
-
#all classes mentioned in rules, on left and right sides
|
1464
|
-
def STACKABLE_CLASSES #
|
1465
|
-
return @sc_result if defined? @sc_result
|
1466
|
-
@sc_result=[]
|
1467
|
-
@subclasses_of=child_relations_among(*vertices)
|
1468
|
-
# @sc_result=false
|
1469
|
-
l=LEFT()
|
1470
|
-
l=l.map{|lm| sc_juice lm}.flatten.compact
|
1471
|
-
assert l.grep(nil).empty?
|
1472
|
-
r= @rules.map{|rr| rr.right }.grep(Class) #classes in productions
|
1473
|
-
result=l+r
|
1474
|
-
@subclasses_of=nil
|
1475
|
-
@sc_result.replace result.grep(Class).uniq
|
1476
|
-
fail if @sc_result.empty?
|
1477
|
-
return @sc_result
|
1478
|
-
end
|
1479
|
-
|
1480
|
-
# def juice(m)
|
1481
|
-
# case m #
|
1482
|
-
# when Class
|
1483
|
-
# return [m] unless @subclasses_of
|
1484
|
-
# result=[m] # and subclasses too
|
1485
|
-
# i=0
|
1486
|
-
# while item=result[i]
|
1487
|
-
# p item
|
1488
|
-
# result.concat @subclasses_of[item] rescue nil
|
1489
|
-
# i += 1
|
1490
|
-
# end
|
1491
|
-
# result
|
1492
|
-
# when String,Regexp; juice(RedParse.KW(m))
|
1493
|
-
# when Reg::And; m.subregs.map{|x| juice(x).flatten.compact}.inject{|sum,rr| sum&rr}
|
1494
|
-
# when Reg::Or; m.subregs.map &method(:juice)
|
1495
|
-
# when Reg::Not
|
1496
|
-
# m=m.subregs[0]
|
1497
|
-
# if Class===m or (Reg::Or===m and
|
1498
|
-
# m.subregs.find{|x| Class===x })
|
1499
|
-
# juice(m)
|
1500
|
-
# else []
|
1501
|
-
# end
|
1502
|
-
# else []
|
1503
|
-
# end
|
1504
|
-
# end
|
1505
|
-
|
1506
|
-
def sc_juice(m)
|
1507
|
-
case m #
|
1508
|
-
when Class; [m]
|
1509
|
-
when String,Regexp; [KeywordToken]
|
1510
|
-
when Reg::And; m.subregs.map{|x| sc_juice(x)}.compact.map{|x| x.flatten.compact}.inject{|sum,rr| sum&rr }
|
1511
|
-
when Reg::Or; m.subregs.map(&method(:sc_juice))
|
1512
|
-
when Reg::Not; sc_juice(m.subregs[0])
|
1513
|
-
when Reg::LookAhead, Reg::LookBack; sc_juice(m.subregs[0])
|
1514
|
-
when Reg::Repeat; sc_juice(m.subregs[0])
|
1515
|
-
else []
|
1516
|
-
end
|
1517
|
-
end
|
1518
|
-
|
1519
|
-
def unruly_rules
|
1520
|
-
return @unruly_rules if defined? @unruly_rules
|
1521
|
-
|
1522
|
-
@unruly_rules=
|
1523
|
-
all_rules.select{|rule| rule.unruly? }
|
1524
|
-
|
1525
|
-
p :unruly_rules
|
1526
|
-
pp @unruly_rules.map{|r| r.name}
|
1527
|
-
|
1528
|
-
@unruly_rules
|
1529
|
-
end
|
1530
|
-
|
1531
|
-
def enumerate_exemplars
|
1532
|
-
return @@exemplars if defined? @@exemplars #dunno why this is necessary
|
1533
|
-
|
1534
|
-
result= STACKABLE_CLASSES() \
|
1535
|
-
.map{|sc| sc.enumerate_exemplars } \
|
1536
|
-
.inject{|sum,sc| sum+sc}
|
1537
|
-
|
1538
|
-
result.map!{|sc|
|
1539
|
-
res=sc.shift.allocate
|
1540
|
-
until sc.empty?
|
1541
|
-
eval "def res.#{sc.shift}; #{sc.shift.inspect} end"
|
1542
|
-
end
|
1543
|
-
def res.to_s; identity_name end
|
1544
|
-
res
|
1545
|
-
}
|
1546
|
-
|
1547
|
-
return @@exemplars=result
|
1548
|
-
end
|
1549
|
-
|
1550
|
-
def check_for_parsealike_inputs
|
1551
|
-
all_patterns=all_rules.map{|r| r.patterns.map{|rp| Reg::Repeat===rp and rp=rp.subregs[0]; rp }}.flatten.uniq
|
1552
|
-
seen={}
|
1553
|
-
@identity_name_aliases={}
|
1554
|
-
warn "why are non_empty and after_equals params to BeginNode appearently ignored?"
|
1555
|
-
warn "some token identities overlap themselves?!?"
|
1556
|
-
warn "some overlaps are duplicated"
|
1557
|
-
warn ". and :: overlap => ..... surely that's not right"
|
1558
|
-
@inputs.map{|input|
|
1559
|
-
profile=all_patterns.map{|pat| Proc===pat ? pat : !!(pat===input)}
|
1560
|
-
if seen[profile]
|
1561
|
-
puts "#{input} overlaps #{seen[profile]}"
|
1562
|
-
@identity_name_aliases[seen[profile]]=input
|
1563
|
-
nil
|
1564
|
-
else
|
1565
|
-
seen[profile]=input
|
1566
|
-
end
|
1567
|
-
}.compact
|
1568
|
-
end
|
1569
|
-
|
1570
|
-
def enumerate_states
|
1571
|
-
inputs=check_for_parsealike_inputs
|
1572
|
-
inputs.reject!{|x| StartToken===x}
|
1573
|
-
|
1574
|
-
result=[]
|
1575
|
-
todo=[start_state]
|
1576
|
-
|
1577
|
-
seenlist = {}
|
1578
|
-
seenlist.default=:dunno_yet
|
1579
|
-
|
1580
|
-
j=0
|
1581
|
-
start=was=Time.now
|
1582
|
-
in_result={} #this should go away; obsoleted by @states
|
1583
|
-
state_num=-1
|
1584
|
-
todo.each{|st| in_result[st]=(state_num+=1) }
|
1585
|
-
ps=todo.first
|
1586
|
-
pp [-in_result[ps], *ps.dotteds.map{|dr| dr.name }]
|
1587
|
-
old_todo_size=todo.size
|
1588
|
-
while state=todo.shift
|
1589
|
-
result<<state
|
1590
|
-
|
1591
|
-
i=0
|
1592
|
-
inputs.each {|input|
|
1593
|
-
newstate=state.evolve input,self,seenlist
|
1594
|
-
assert ACTION_PATTERN===newstate
|
1595
|
-
#newstate is ParserState|MultiShift|MultiReduce|Rule|:accept|:error
|
1596
|
-
state[input.identity_name]=newstate
|
1597
|
-
next unless newstate.respond_to? :substates
|
1598
|
-
#newstate.substates is just [newstate] for plain ParserStates
|
1599
|
-
morestates=newstate.substates.reject{|x| in_result[x]}
|
1600
|
-
morestates.each{|st| in_result[st]=(state_num+=1) }
|
1601
|
-
# p [in_result[state],:+,input.identity_name,:>>,pretty(newstate,in_result)]
|
1602
|
-
todo.concat morestates
|
1603
|
-
|
1604
|
-
# pp morestates.map{|ps|
|
1605
|
-
# [-in_result[ps], *ps.dotteds.map{|dr| dr.name }]
|
1606
|
-
# }
|
1607
|
-
# pp pretty(newstate,in_result) unless ParserState===newstate
|
1608
|
-
}
|
1609
|
-
|
1610
|
-
now=Time.now
|
1611
|
-
p [:*,j+=1,todo.size,todo.size-old_todo_size,now-was,j/(now-start),(100.0*j/(j+todo.size)).to_i]
|
1612
|
-
old_todo_size=todo.size
|
1613
|
-
was=now
|
1614
|
-
|
1615
|
-
# if state.actions.values.uniq==[:error]
|
1616
|
-
#this can happen when the only dotted rule is for an :error
|
1617
|
-
#maybe this case can be optimized?
|
1618
|
-
# end
|
1619
|
-
end
|
1620
|
-
self.rmd_cache=nil
|
1621
|
-
self.oc_cache=nil
|
1622
|
-
self.sl2ms_cache=nil
|
1623
|
-
return result
|
1624
|
-
end
|
1625
|
-
|
1626
|
-
def pretty(x,in_result)
|
1627
|
-
case x
|
1628
|
-
when ParserState; in_result[x]
|
1629
|
-
when MultiReduce
|
1630
|
-
pairs=x.list.dup
|
1631
|
-
result=[]
|
1632
|
-
until pairs.empty?
|
1633
|
-
cond,act,*pairs=*pairs
|
1634
|
-
cond = cond.inspect
|
1635
|
-
result<<[cond,pretty(act.action,in_result)]
|
1636
|
-
end
|
1637
|
-
result<<pretty(x.default,in_result)
|
1638
|
-
result.unshift :MultiReduce
|
1639
|
-
when MultiShift
|
1640
|
-
h={}
|
1641
|
-
mods=x.modifiers
|
1642
|
-
its=[]
|
1643
|
-
(0...mods.size).step(2){|i| its<<mods[i] }
|
1644
|
-
x.map.each_with_index{|xx,i| h[i]=pretty(xx) }
|
1645
|
-
[:MultiShift, its,h]
|
1646
|
-
when Class; x.name
|
1647
|
-
when StackMonkey; x.name
|
1648
|
-
when :accept,:error; x
|
1649
|
-
else fail "not a valid action: #{x}"
|
1650
|
-
end
|
1651
|
-
end
|
1652
|
-
|
1653
|
-
attr_accessor :inputs
|
1654
|
-
|
1655
|
-
def all_states
|
1656
|
-
return @all_states if defined? @all_states
|
1657
|
-
@all_states=enumerate_states
|
1658
|
-
end
|
1659
|
-
|
1660
|
-
def exemplars_that_match p
|
1661
|
-
@inputs.grep p
|
1662
|
-
end
|
1663
|
-
|
1664
|
-
def pattern_matches_nodes? p
|
1665
|
-
!@inputs.grep(Node&p).empty?
|
1666
|
-
end
|
1667
|
-
|
1668
|
-
def pattern_matches_tokens? p
|
1669
|
-
!@inputs.grep(Token&p).empty?
|
1670
|
-
end
|
1671
|
-
|
1672
|
-
def identity_name_alias? name
|
1673
|
-
alias_=@identity_name_aliases[name]
|
1674
|
-
return( alias_||name )
|
1675
|
-
end
|
1676
|
-
|
1677
|
-
def compile
|
1678
|
-
oldparser=Thread.current[:$RedParse_parser]
|
1679
|
-
Thread.current[:$RedParse_parser]||=self
|
1680
|
-
|
1681
|
-
if File.exist?("cached_parse_tables.drb")
|
1682
|
-
dup=Marshal.load(f=open("cached_parse_tables.drb","rb"))
|
1683
|
-
instance_variables.each{|var| remove_instance_variable var }
|
1684
|
-
extend SingleForwardable
|
1685
|
-
def_singleton_delegators(dup,public_methods+private_methods+protected_methods)
|
1686
|
-
|
1687
|
-
self.inputs=enumerate_exemplars
|
1688
|
-
else
|
1689
|
-
@generating_parse_tables=true
|
1690
|
-
@inputs||=enumerate_exemplars
|
1691
|
-
|
1692
|
-
states=all_states
|
1693
|
-
# @rules=expanded_RULES
|
1694
|
-
@inputs=nil #Marshal no like it
|
1695
|
-
|
1696
|
-
begin
|
1697
|
-
p :dumping
|
1698
|
-
Marshal.dump(self,f=open("cached_parse_tables.drb","wb"))
|
1699
|
-
p :dump_done!
|
1700
|
-
rescue Exception
|
1701
|
-
p :dump_failed
|
1702
|
-
File.unlink "cached_parse_tables.drb"
|
1703
|
-
ensure
|
1704
|
-
@inputs=enumerate_exemplars
|
1705
|
-
end
|
1706
|
-
end
|
1707
|
-
f.close
|
1708
|
-
|
1709
|
-
#look for unused dotted rules and actions
|
1710
|
-
#also states with drs past the end
|
1711
|
-
past_end=0
|
1712
|
-
drs=all_dotted_rules
|
1713
|
-
dr_count=Hash.new(0)
|
1714
|
-
acts=all_rules#.map{|r| r.action }.uniq
|
1715
|
-
act_count=Hash.new(0)
|
1716
|
-
states.each{|state|
|
1717
|
-
state.dotteds.each{|dr|
|
1718
|
-
dr_count[dr]+=1
|
1719
|
-
past_end+=1 if dr.pos>=dr.rule.patterns.size
|
1720
|
-
}
|
1721
|
-
sav=state.actions.values
|
1722
|
-
sav.grep(Class|StackMonkey).each{|act| act_count[act.__id__]+=1 }
|
1723
|
-
sav.grep(MultiReduce|MultiShift).each{|multi| multi.actions.each{|act| act_count[act.__id__]+=1} }
|
1724
|
-
#p state.name if state.dotteds.select{|dr| dr.rule.action==BeginNode}
|
1725
|
-
}
|
1726
|
-
puts "#{past_end} dotted rules found past the end of their rule" if past_end>0
|
1727
|
-
nevers=0
|
1728
|
-
drs.each{|dr|
|
1729
|
-
next unless dr_count[dr].zero?
|
1730
|
-
puts "never reached #{dr.name}"
|
1731
|
-
nevers+=1
|
1732
|
-
}
|
1733
|
-
puts "#{nevers} dotted rules were never reached (out of #{drs.size})"
|
1734
|
-
nevers=0
|
1735
|
-
acts.each{|act|
|
1736
|
-
next unless act_count[act.__id__].zero?
|
1737
|
-
puts "never reached #{act.name rescue act}"
|
1738
|
-
nevers+=1
|
1739
|
-
}
|
1740
|
-
puts "#{nevers} actions were never reached (out of #{acts.size})"
|
1741
|
-
p :most_popular_nontrivial_drs
|
1742
|
-
pp dr_count.reject{|(dr,n)| dr.pos.zero? or dr.pos==1 && dr.rule.lookback?} \
|
1743
|
-
.sort_by{|(dr,n)| n}[-15..-1].map{|(dr,n)| [dr.name,n] }
|
1744
|
-
|
1745
|
-
#look for duplicate states
|
1746
|
-
actions2state={}
|
1747
|
-
dup_states=0
|
1748
|
-
states.each{|st|
|
1749
|
-
cache=actions2state[st.actions]
|
1750
|
-
if cache
|
1751
|
-
st.equivalent_to=cache
|
1752
|
-
dup_states+=1
|
1753
|
-
else
|
1754
|
-
actions2state[st.actions]=st
|
1755
|
-
end
|
1756
|
-
}
|
1757
|
-
puts "#{dup_states} duplicate states" if dup_states.nonzero?
|
1758
|
-
|
1759
|
-
name2count={}
|
1760
|
-
states.each{|state| state.rename(name2count) }
|
1761
|
-
|
1762
|
-
#divide each state's actions into sr and goto tables
|
1763
|
-
#also scan states for the most common sr and goto actions and make them default
|
1764
|
-
states.each{|state| state.make_sr_goto_tables }
|
1765
|
-
|
1766
|
-
|
1767
|
-
# pp states
|
1768
|
-
# pp states.size
|
1769
|
-
|
1770
|
-
generate_c $stdout
|
1771
|
-
return self
|
1772
|
-
ensure
|
1773
|
-
remove_instance_variable :@generating_parse_tables rescue nil
|
1774
|
-
Thread.current[:$RedParse_parser]=oldparser
|
1775
|
-
end
|
1776
|
-
|
1777
|
-
def ultimate_goal_nodes
|
1778
|
-
result=[]
|
1779
|
-
all_rules.each{|rule|
|
1780
|
-
if rule.patterns.size==0 and
|
1781
|
-
rule.patterns.first==StartToken and
|
1782
|
-
rule.patterns.last==EoiToken
|
1783
|
-
result << juice(rule.patterns[1])
|
1784
|
-
end
|
1785
|
-
}
|
1786
|
-
result.flatten!
|
1787
|
-
return result
|
1788
|
-
end
|
1789
|
-
|
1790
|
-
|
1791
|
-
# def start_state
|
1792
|
-
# goal=ultimate_goal_nodes
|
1793
|
-
# result=all_rules.select{|rule|
|
1794
|
-
# rt=rule.reduces_to and
|
1795
|
-
# !goal.select{|node| node>=rt}.empty?
|
1796
|
-
# }
|
1797
|
-
# result.map!{|rule| DottedRule.create(rule,0,parser)}
|
1798
|
-
#
|
1799
|
-
# result=ParserState.new result
|
1800
|
-
# result.name="start_state"
|
1801
|
-
# result
|
1802
|
-
# end
|
1803
|
-
|
1804
|
-
def new_state(drs,unruly_also=false)
|
1805
|
-
result=ParserState.new drs,@states.size
|
1806
|
-
result.perhaps_also_allow all_rules,self
|
1807
|
-
cache=@states[result]
|
1808
|
-
return cache if cache
|
1809
|
-
@states[result]=@states.size
|
1810
|
-
return result
|
1811
|
-
end
|
1812
|
-
|
1813
|
-
def initial_state
|
1814
|
-
@states={}
|
1815
|
-
all_initial_dotted_rules #is this still needed?
|
1816
|
-
result=new_state all_rules.map{|r| DottedRule.create(r,0,self)}
|
1817
|
-
result.name="initial"
|
1818
|
-
#result.perhaps_also_allow all_rules,self #silly here
|
1819
|
-
result
|
1820
|
-
end
|
1821
|
-
|
1822
|
-
attr_reader :states
|
1823
|
-
|
1824
|
-
def start_state
|
1825
|
-
seenlist = {}
|
1826
|
-
seenlist.default=:dunno_yet
|
1827
|
-
result=initial_state.evolve StartToken.new, self,seenlist
|
1828
|
-
result.perhaps_also_allow all_rules,self
|
1829
|
-
result.name="start"
|
1830
|
-
result
|
1831
|
-
#pp [:initial_seenlist, seenlist]
|
1832
|
-
#ensure p :/
|
1833
|
-
end
|
1834
354
|
|
1835
355
|
#inline any subsequences in RULES right into the patterns
|
1836
356
|
#reg should do this already, but current release does not
|
1837
357
|
def expanded_RULES
|
1838
358
|
result=RULES()
|
1839
359
|
return result if (-[:foo, -[:bar]]).subregs.grep(Reg::Subseq).empty?
|
1840
|
-
result.map!{|rule|
|
1841
|
-
unless rule.left.subregs.grep(Reg::Subseq)
|
1842
|
-
then rule
|
360
|
+
result.map!{|rule|
|
361
|
+
unless rule.left.subregs.grep(Reg::Subseq)
|
362
|
+
then rule
|
1843
363
|
else
|
1844
364
|
right=rule.right
|
1845
365
|
rule=rule.left.subregs.dup
|
@@ -1853,122 +373,6 @@ end
|
|
1853
373
|
}
|
1854
374
|
end
|
1855
375
|
|
1856
|
-
module NamedConstant
|
1857
|
-
attr_accessor :constant_name
|
1858
|
-
def inspect; constant_name end
|
1859
|
-
end
|
1860
|
-
def self.inspect_constant_names
|
1861
|
-
constants.each{|kn|
|
1862
|
-
k=const_get(kn)
|
1863
|
-
next if Class|Module|Numeric|Symbol|true|false|nil===k
|
1864
|
-
k.extend NamedConstant
|
1865
|
-
k.constant_name=kn
|
1866
|
-
}
|
1867
|
-
end
|
1868
|
-
|
1869
|
-
def undumpables
|
1870
|
-
return @undumpables if @undumpables
|
1871
|
-
@rules||=expanded_RULES
|
1872
|
-
n=-1
|
1873
|
-
@undumpables={}
|
1874
|
-
abortable_graphwalk(@rules){|cntr,o,i,ty|
|
1875
|
-
!case o
|
1876
|
-
when StackMonkey
|
1877
|
-
@undumpables[o.name]=o
|
1878
|
-
when Reg::Deferred
|
1879
|
-
@undumpables[n+=1]=o
|
1880
|
-
class<<o
|
1881
|
-
attr_accessor :undump_key
|
1882
|
-
end
|
1883
|
-
o.undump_key=n
|
1884
|
-
end
|
1885
|
-
}
|
1886
|
-
end
|
1887
|
-
|
1888
|
-
class ::Proc #hack hack hack
|
1889
|
-
#only define hacky _dump if one isn't defined already
|
1890
|
-
unless instance_methods.include?("_dump") or
|
1891
|
-
instance_methods.include?("marshal_dump") or
|
1892
|
-
(Marshal.dump(proc{}) rescue false)
|
1893
|
-
def _dump depth
|
1894
|
-
undump_key.to_s
|
1895
|
-
end
|
1896
|
-
def self._load str
|
1897
|
-
Thread.current[:$RedParse_parser].undumpables[str.to_i]
|
1898
|
-
end
|
1899
|
-
end
|
1900
|
-
end
|
1901
|
-
|
1902
|
-
=begin disabled, uses too much memory!!
|
1903
|
-
class MarshalProxy
|
1904
|
-
def initialize(key)
|
1905
|
-
@key=key
|
1906
|
-
end
|
1907
|
-
attr :key
|
1908
|
-
end
|
1909
|
-
|
1910
|
-
#convert unmarshalables, such as stackmonkeys into proxies
|
1911
|
-
def proxify
|
1912
|
-
n=-1
|
1913
|
-
seen={}
|
1914
|
-
mkproxy=proc{|cntr,o,i,ty,useit|
|
1915
|
-
case o
|
1916
|
-
when StackMonkey
|
1917
|
-
useit[0]=true
|
1918
|
-
seen[o.__id__]||=MarshalProxy.new(o.name)
|
1919
|
-
when Reg::Deferred
|
1920
|
-
useit[0]=true
|
1921
|
-
seen[o.__id__]||=MarshalProxy.new(n+=1)
|
1922
|
-
end
|
1923
|
-
}
|
1924
|
-
Ron::GraphWalk.graphmodify!(@rules,&mkproxy)
|
1925
|
-
Ron::GraphWalk.graphmodify!(self,&mkproxy)
|
1926
|
-
|
1927
|
-
end
|
1928
|
-
|
1929
|
-
def _dump depth
|
1930
|
-
fail unless @rules
|
1931
|
-
proxify
|
1932
|
-
ivs=instance_variables
|
1933
|
-
a=ivs+ivs.reverse.map{|var| instance_variable_get var }
|
1934
|
-
result=Marshal.dump(a,depth)
|
1935
|
-
unproxify
|
1936
|
-
return result
|
1937
|
-
end
|
1938
|
-
|
1939
|
-
#convert marshal proxies back to the real thing
|
1940
|
-
def unproxify
|
1941
|
-
#build a lookup table for unmarshalables by walking @rules
|
1942
|
-
@rules||=expanded_RULES
|
1943
|
-
n=-1;lookup={}
|
1944
|
-
Ron::GraphWalk.graphwalk(@rules){|cntr,o,i,ty|
|
1945
|
-
case o
|
1946
|
-
when StackMonkey
|
1947
|
-
lookup[o.name]=o
|
1948
|
-
when Reg::Deferred
|
1949
|
-
lookup[n+=1]=o
|
1950
|
-
end
|
1951
|
-
}
|
1952
|
-
|
1953
|
-
Ron::GraphWalk.graphmodify!(self){|cntr,o,i,ty,useit|
|
1954
|
-
if MarshalProxy===o
|
1955
|
-
useit[0]=true
|
1956
|
-
lookup[o.key]
|
1957
|
-
end
|
1958
|
-
}
|
1959
|
-
end
|
1960
|
-
|
1961
|
-
def self._load(str,*more)
|
1962
|
-
result=allocate
|
1963
|
-
a=Marshal.load(str,*more)
|
1964
|
-
|
1965
|
-
result.unproxify
|
1966
|
-
|
1967
|
-
(0...a.size/2).each{|i| result.instance_variable_set a[i],a[-i] }
|
1968
|
-
return result
|
1969
|
-
end
|
1970
|
-
=end
|
1971
|
-
|
1972
376
|
###### specific to parsing ruby
|
1973
377
|
|
1974
378
|
|
@@ -2008,6 +412,7 @@ end
|
|
2008
412
|
"|="=>105, "&="=>105, ">>="=>105, "<<="=>105, "*="=>105,
|
2009
413
|
"&&="=>105, "||="=>105, "**="=>105, "^="=>105,
|
2010
414
|
|
415
|
+
|
2011
416
|
# "and"=>99, "or"=>99,
|
2012
417
|
|
2013
418
|
# "if"=>98, "unless"=>98, "while"=>98, "until"=>98, "rescue"=>98,
|
@@ -2058,8 +463,10 @@ end
|
|
2058
463
|
|
2059
464
|
"?"=>106, # ":"=>106, #not sure what to do with ":"
|
2060
465
|
|
2061
|
-
"unary
|
2062
|
-
"lhs*"=>105,
|
466
|
+
"unary&"=>105, #unary * and & operators
|
467
|
+
"lhs*"=>105, #this should remain above =
|
468
|
+
"lhs,"=>105,
|
469
|
+
"rescue3"=>105,
|
2063
470
|
|
2064
471
|
"="=>104, "%="=>104, "/="=>104, "-="=>104, "+="=>104,
|
2065
472
|
"|="=>104, "&="=>104, ">>="=>104, "<<="=>104, "*="=>104,
|
@@ -2068,12 +475,10 @@ end
|
|
2068
475
|
"defined?"=>103,
|
2069
476
|
"not"=>103,
|
2070
477
|
":"=>102, #but not when used as a substitute for 'then'
|
2071
|
-
"rescue3"=>102,
|
2072
478
|
|
2073
479
|
"=>"=>101,
|
2074
|
-
"lhs,"=>100,
|
2075
480
|
"rhs,"=>100, #"call,"=>100, "array,"=>100, "param,"=>100,
|
2076
|
-
","=>100,
|
481
|
+
","=>100, "rhs*"=>100, "unary*"=>100,
|
2077
482
|
#the 'precedence' of comma is somewhat controversial. it actually has
|
2078
483
|
#several different precedences depending on which kind of comma it is.
|
2079
484
|
#the precedence of , is higher than :, => and the assignment operators
|
@@ -2193,8 +598,8 @@ end
|
|
2193
598
|
|
2194
599
|
UNOP=
|
2195
600
|
(OperatorToken|KeywordToken)&-{ #sppflt! KeywordToken here is a hack too
|
2196
|
-
|
2197
|
-
:ident=>/^(?:[+-]@|unary[&])$/,
|
601
|
+
:ident=>/^(?:[+-]@|unary[&*]|(?:lhs|rhs)[*])$/,
|
602
|
+
# :ident=>/^(?:[+-]@|unary[&])$/,
|
2198
603
|
#:unary =>true,
|
2199
604
|
}|
|
2200
605
|
(OperatorToken|KeywordToken)&-{ #sppflt! KeywordToken here is a hack too
|
@@ -2255,11 +660,15 @@ end
|
|
2255
660
|
end
|
2256
661
|
|
2257
662
|
# LowerOp= proc{|parser,op2| parser.left_op_higher(parser[-3],op2) }
|
663
|
+
module LowerOp_inspect
|
664
|
+
def inspect; "lower_op" end
|
665
|
+
end
|
666
|
+
|
2258
667
|
def lower_op
|
2259
668
|
return @lower_op if defined? @lower_op
|
2260
669
|
lower_op=item_that{|op| left_op_higher(@stack[-3],op) }
|
2261
670
|
lower_op=(LOWEST_OP|(~VALUELIKE_LA & lower_op)).la
|
2262
|
-
|
671
|
+
lower_op.extend LowerOp_inspect
|
2263
672
|
@lower_op=lower_op
|
2264
673
|
end
|
2265
674
|
|
@@ -2306,7 +715,7 @@ end
|
|
2306
715
|
|
2307
716
|
BareMethod=MethNameToken|(LiteralNode&-{:bare_method=>true})
|
2308
717
|
|
2309
|
-
BEGINWORDLIST=RubyLexer::BEGINWORDLIST + %w"( [ {"
|
718
|
+
#BEGINWORDLIST=RubyLexer::BEGINWORDLIST + %w"( [ {"
|
2310
719
|
ENDWORDLIST=%w"end ) ] }"
|
2311
720
|
ENDWORDS=ENDWORDLIST.map{|x| Regexp.quote x}.join('|')
|
2312
721
|
BEGINWORDS=RubyLexer::BEGINWORDS
|
@@ -2338,23 +747,25 @@ end
|
|
2338
747
|
|
2339
748
|
#for use in lookback patterns
|
2340
749
|
OPERATORLIKE_LB=OperatorToken|
|
2341
|
-
KW(/^(not | defined\? | .*[@,] | [ ~ ! ; \( \[ \{ ? : ] | \.{1,3} | :: | => | ![=~])$/x)|
|
750
|
+
KW(/^(not | defined\? | rescue3 | .*[@,] | [ ~ ! ; \( \[ \{ ? : ] | \.{1,3} | :: | => | ![=~])$/x)|
|
2342
751
|
KW(%r{^( \*\*? | << | >> | &&? | \|\|? | \^ | % | / | - | \+ )?=$}x)|
|
2343
752
|
KW(BEGINWORDS)|KW(/^#{INNERBOUNDINGWORDS}$/)|RescueHeaderNode|StartToken|
|
2344
|
-
GoalPostToken|BlockFormalsNode
|
753
|
+
GoalPostToken|BlockFormalsNode|AssignmentRhsListStartToken
|
2345
754
|
|
2346
755
|
#for use in lookahead patterns
|
2347
756
|
VALUELIKE_LA=KW(RubyLexer::VARLIKE_KEYWORDS)|NumberToken|SymbolToken|StringToken|UNOP|DEFOP|
|
2348
|
-
KW(/^(
|
2349
|
-
|
757
|
+
KW(/^[({]$/x)|VarNameToken|MethNameToken|HerePlaceholderToken|
|
758
|
+
KW(BEGINWORDS)|FUNCLIKE_KEYWORD|AssignmentRhsListStartToken
|
759
|
+
LOWEST_OP=KW(/^(#{ENDWORDS})$/)|KW(/^#{INNERBOUNDINGWORDS.sub('rescue|','')}$/)|
|
760
|
+
EoiToken|GoalPostToken|AssignmentRhsListEndToken
|
2350
761
|
|
2351
762
|
RESCUE_BODY=-[Expr.-, RescueNode.*, ElseNode.-, EnsureNode.-,]
|
2352
763
|
|
2353
|
-
RESCUE_OP=Op('rescue')
|
764
|
+
RESCUE_OP=Op('rescue') #|(KW('rescue')&-{:infix=>true})
|
2354
765
|
|
2355
766
|
RESCUE_KW=KW('rescue')&-{:infix=>nil}
|
2356
767
|
|
2357
|
-
inspect_constant_names
|
768
|
+
inspect_constant_names if respond_to? :inspect_constant_names
|
2358
769
|
|
2359
770
|
def RULES
|
2360
771
|
lower_op= lower_op()
|
@@ -2373,10 +784,10 @@ end
|
|
2373
784
|
-[DEFOP, ParenedNode]>>UnOpNode,
|
2374
785
|
-[Op(/^(?:unary|lhs|rhs)\*$/), ValueNode, lower_op]>>UnaryStarNode,
|
2375
786
|
|
2376
|
-
-[Op('=',true)|KW(/^(rescue|when|\[)$/)|Op(/,$/,true),
|
2377
|
-
Op(/^(?:unary|rhs)\*$/), ValueNode, (MODIFYASSIGNOP|Op('=',true)).la]>>:shift,
|
2378
|
-
-[MethNameToken|FUNCLIKE_KEYWORD, KW('('),
|
2379
|
-
Op(/^(?:unary|rhs)\*$/), ValueNode, (MODIFYASSIGNOP|Op('=',true)).la]>>:shift,
|
787
|
+
# -[Op('=',true)|KW(/^(rescue|when|\[)$/)|Op(/,$/,true),
|
788
|
+
# Op(/^(?:unary|rhs)\*$/), ValueNode, (MODIFYASSIGNOP|Op('=',true)).la]>>:shift,
|
789
|
+
# -[MethNameToken|FUNCLIKE_KEYWORD, KW('('),
|
790
|
+
# Op(/^(?:unary|rhs)\*$/), ValueNode, (MODIFYASSIGNOP|Op('=',true)).la]>>:shift,
|
2380
791
|
#star should not be used in an lhs if an rhs or param list context is available to eat it.
|
2381
792
|
#(including param lists for keywords such as return,break,next,rescue,yield,when)
|
2382
793
|
|
@@ -2394,22 +805,25 @@ end
|
|
2394
805
|
#assignment
|
2395
806
|
-[Lvalue, MODIFYASSIGNOP, Expr, lower_op]>>AssignNode,
|
2396
807
|
-[Lvalue, Op('=',true), AssignmentRhsNode, lower_op]>>AssignNode,
|
2397
|
-
-[
|
808
|
+
-[AssignmentRhsListStartToken, Expr, AssignmentRhsListEndToken]>>AssignmentRhsNode,
|
2398
809
|
|
2399
810
|
# a = b rescue c acts like a ternary,,,
|
2400
811
|
#provided that both a and b are not multiple and b
|
2401
812
|
#(if it is a parenless callsite) has just 1 param
|
2402
|
-
-[Lvalue&~MULTIASSIGN, Op('=',true), AssignmentRhsNode&-{:is_list=>true},
|
2403
|
-
Op('rescue3'
|
2404
|
-
-[Lvalue
|
2405
|
-
|
2406
|
-
-[Lvalue&~MULTIASSIGN, Op('=',true), AssignmentRhsNode&-{:is_list=>true},
|
2407
|
-
|
2408
|
-
|
2409
|
-
|
2410
|
-
|
2411
|
-
|
2412
|
-
|
813
|
+
# -[Lvalue&~MULTIASSIGN, Op('=',true), AssignmentRhsNode&-{:is_list=>true},
|
814
|
+
# Op('rescue3'), Expr, lower_op]>>AssignNode,
|
815
|
+
-[Lvalue, Op('=',true), AssignmentRhsNode, Op('rescue3'), Expr, lower_op]>>AssignNode,
|
816
|
+
|
817
|
+
# -[Lvalue&~MULTIASSIGN, Op('=',true), AssignmentRhsNode&-{:is_list=>true},
|
818
|
+
# Op('rescue3',true).la]>>:shift,
|
819
|
+
|
820
|
+
# -[Lvalue&~MULTIASSIGN, Op('=',true), AssignmentRhsNode&-{:is_list=>true},
|
821
|
+
# RESCUE_OP.la] >>
|
822
|
+
# stack_monkey("rescue3",1,Op('rescue3',true)){|stack|
|
823
|
+
# resc=stack.last.dup
|
824
|
+
# resc.ident += '3'
|
825
|
+
# stack[-1]=resc
|
826
|
+
# },
|
2413
827
|
#relative precedence of = and rescue are to be inverted if rescue
|
2414
828
|
#is to the right and assignment is not multiple.
|
2415
829
|
|
@@ -2515,7 +929,8 @@ end
|
|
2515
929
|
|
2516
930
|
-[HereDocNode, StringToken+1, StringToken.~.la]>>StringCatNode,
|
2517
931
|
-[(OPERATORLIKE_LB&~(StringToken|HereDocNode)).lb, StringToken+2, StringToken.~.la]>>StringCatNode,
|
2518
|
-
-[(OPERATORLIKE_LB&~(StringToken|HereDocNode)).lb, StringToken, StringToken.~.la]>>StringNode,
|
932
|
+
-[(OPERATORLIKE_LB&~(StringToken|HereDocNode)).lb, StringToken, StringToken.~.la]>>StringNode,
|
933
|
+
#includes regexp, wordlist, backquotes
|
2519
934
|
|
2520
935
|
-['case', Expr.-, KW(';').-, WhenNode.*, ElseNode.-, 'end']>>CaseNode,
|
2521
936
|
|
@@ -2599,8 +1014,25 @@ if defined? END_ATTACK
|
|
2599
1014
|
include Reducer
|
2600
1015
|
end
|
2601
1016
|
|
2602
|
-
def initialize(input,name="(eval)",line=1,lvars=[],options={
|
2603
|
-
@rubyversion=options[:rubyversion]
|
1017
|
+
def initialize(input,name="(eval)",line=1,lvars=[],options={})
|
1018
|
+
@rubyversion=options[:rubyversion]||1.8
|
1019
|
+
|
1020
|
+
cache=Cache.new(name,line,lvars.sort.join(" "),@rubyversion,self.class.name)
|
1021
|
+
cache_mode=options[:cache_mode]||:read_write
|
1022
|
+
raise ArgumentError unless /^(?:read_(?:write|only)|write_only|none)$/===cache_mode.to_s
|
1023
|
+
read_cache= /read/===cache_mode.to_s
|
1024
|
+
input.binmode if input.respond_to? :binmode
|
1025
|
+
if read_cache and cache and result=cache.get(input)
|
1026
|
+
@cached_result=result
|
1027
|
+
@write_cache=nil
|
1028
|
+
return
|
1029
|
+
end
|
1030
|
+
if /write/===cache_mode.to_s
|
1031
|
+
@write_cache,@input= cache,input
|
1032
|
+
else
|
1033
|
+
@write_cache=nil
|
1034
|
+
end
|
1035
|
+
|
2604
1036
|
if Array===input
|
2605
1037
|
def input.get1token; shift end
|
2606
1038
|
@lexer=input
|
@@ -2669,7 +1101,7 @@ end
|
|
2669
1101
|
when KeywordToken
|
2670
1102
|
case name=result.ident
|
2671
1103
|
|
2672
|
-
when /^(#{BINOP_KEYWORDS.join '|'})
|
1104
|
+
when /^(#{BINOP_KEYWORDS.join '|'})$/o #should be like this in rubylexer
|
2673
1105
|
result=OperatorToken.new(name,result.offset) unless result.has_end?
|
2674
1106
|
when "|"; result=GoalPostToken.new(result.offset) #is this needed still?
|
2675
1107
|
when "__FILE__"; #I wish rubylexer would handle this
|
@@ -2685,6 +1117,8 @@ end
|
|
2685
1117
|
|
2686
1118
|
when EoiToken; break
|
2687
1119
|
when HereBodyToken; break
|
1120
|
+
when AssignmentRhsListStartToken; break
|
1121
|
+
when AssignmentRhsListEndToken; break
|
2688
1122
|
when IgnoreToken; redo
|
2689
1123
|
end
|
2690
1124
|
end
|
@@ -2810,11 +1244,11 @@ if __FILE__==$0
|
|
2810
1244
|
# require 'pp'
|
2811
1245
|
# pp e.stack[-[15,e.stack.size].min..-1]
|
2812
1246
|
# raise
|
2813
|
-
# rescue NeverExecThis
|
1247
|
+
# rescue NeverExecThis
|
2814
1248
|
puts "RedParse attempted to execute parse data in #{name}"
|
2815
1249
|
next
|
2816
1250
|
end
|
2817
|
-
rescue Interrupt
|
1251
|
+
rescue Interrupt; exit 2
|
2818
1252
|
rescue Exception=>e
|
2819
1253
|
# puts e.backtrace.join("\n")
|
2820
1254
|
e.message << " during parse of #{name}"
|
@@ -2851,10 +1285,10 @@ if __FILE__==$0
|
|
2851
1285
|
ryans=ParseTree.new.parse_tree_for_string(safe_inputs[i],name); nil
|
2852
1286
|
} and raise NeverExecThis
|
2853
1287
|
delta,is_diff=arraydiff(mine,ryans)
|
2854
|
-
rescue NeverExecThis
|
1288
|
+
rescue NeverExecThis
|
2855
1289
|
puts "ParseTree attempted to execute parse data in #{name}"
|
2856
1290
|
next
|
2857
|
-
rescue Interrupt
|
1291
|
+
rescue Interrupt; exit 2
|
2858
1292
|
rescue Exception=>e
|
2859
1293
|
#raise( RuntimeError.new( "#{e} during to_parsetree of #{name}" ) )
|
2860
1294
|
puts "error during to_parsetree of #{name}"
|
@@ -2881,10 +1315,10 @@ if __FILE__==$0
|
|
2881
1315
|
end
|
2882
1316
|
end
|
2883
1317
|
|
2884
|
-
rescue NeverExecThis
|
1318
|
+
rescue NeverExecThis
|
2885
1319
|
puts "mysterious attempt to execute parse data in #{name}"
|
2886
1320
|
next
|
2887
|
-
rescue Interrupt,SystemExit
|
1321
|
+
rescue Interrupt,SystemExit; exit 2
|
2888
1322
|
rescue Exception=>e
|
2889
1323
|
puts "#{e}:#{e.class}"
|
2890
1324
|
puts e.backtrace.join("\n")
|