redparse 0.8.3 → 0.8.4

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -2,7 +2,7 @@
2
2
  # Distributed under the terms of Ruby's license.
3
3
  require 'rubygems'
4
4
  require 'hoe'
5
- require 'lib/redparse/version.rb'
5
+ require './lib/redparse/version.rb'
6
6
 
7
7
  if $*==["test"]
8
8
  #hack to get 'rake test' to stay in one process
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 'redparse/problemfiles'
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=:pp
87
- quiet=true
88
- ruby187=false
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"; output=: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"; quiet=false
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: exit 2
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
- rescue NeverExecThis:
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: exit 2
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 !quiet or is_diff
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 !quiet or is_diff
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
- puts "no differences in "+name
246
- problemfiles.delete name if problemfiles
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: exit 2
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
- #problemfiles.push name if problemfiles
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.new(*matching)
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
- not if node.respond_to? :error? and node.error?(@rubyversion)
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*"=>105, "unary&"=>105, #unary * and & operators
2062
- "lhs*"=>105, "rhs*"=>105, #this should remain above =, but other unary stars are below it
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
- # :ident=>/^(?:[+-]@|unary[&*]|(?:lhs|rhs)[*])$/,
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
- def lower_op.inspect; "lower_op" end
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(/^( \( | \{ | )$/x)|VarNameToken|MethNameToken|HerePlaceholderToken|KW(BEGINWORDS)|FUNCLIKE_KEYWORD
2349
- LOWEST_OP=KW(/^(#{ENDWORDS})$/)|KW(/^#{INNERBOUNDINGWORDS.sub('rescue|','')}$/)|EoiToken|GoalPostToken
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')|(KW('rescue')&-{:infix=>true})
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
- -[Op('=',true).lb, Expr, lower_op]>>AssignmentRhsNode,
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',true), Expr, lower_op]>>AssignNode,
2404
- -[Lvalue&~MULTIASSIGN, Op('=',true), AssignmentRhsNode&-{:is_list=>true},
2405
- Op('rescue3',true).la]>>:shift,
2406
- -[Lvalue&~MULTIASSIGN, Op('=',true), AssignmentRhsNode&-{:is_list=>true},
2407
- RESCUE_OP.la] >>
2408
- stack_monkey("rescue3",1,Op('rescue3',true)){|stack|
2409
- resc=stack.last.dup
2410
- resc.ident += '3'
2411
- stack[-1]=resc
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, #includes regexp, wordlist, backquotes
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={:rubyversion=>1.8})
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 '|'})$/: #should be like this in rubylexer
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: exit 2
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: exit 2
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: exit 2
1321
+ rescue Interrupt,SystemExit; exit 2
2888
1322
  rescue Exception=>e
2889
1323
  puts "#{e}:#{e.class}"
2890
1324
  puts e.backtrace.join("\n")