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/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")