@@ -93,6 +93,10 @@ def compute
9393 check_conflicts
9494 end
9595
96+ def compute_ielr
97+ report_duration ( :split_states ) { split_states }
98+ end
99+
96100 def reporter
97101 StatesReporter . new ( self )
98102 end
@@ -552,5 +556,225 @@ def check_conflicts
552556 end
553557 end
554558 end
559+
560+ def split_states
561+ @item_lookahead_set = { }
562+ @lalr_isocores = Hash . new { |hash , key | hash [ key ] = key }
563+ @ielr_isocores = Hash . new { |hash , key | hash [ key ] = [ key ] }
564+ @lookaheads_recomputed = Hash . new { |hash , key | hash [ key ] = false }
565+ @predecessors = { }
566+ @states . each do |state |
567+ state . transitions . each do |shift , next_state |
568+ compute_state ( state , shift , next_state )
569+ end
570+ end
571+ end
572+
573+ def merge_lookaheads ( state , k )
574+ return if state . kernels . all? { |item | ( k [ item ] - item_lookahead_set ( state ) [ item ] ) . empty? }
575+
576+ state . transitions . each do |shift , next_state |
577+ next if @lookaheads_recomputed [ next_state ]
578+ compute_state ( state , shift , next_state )
579+ end
580+ end
581+
582+ def compute_state ( state , shift , next_state )
583+ k = propagate_lookaheads ( state , next_state )
584+ s = @ielr_isocores [ next_state ] . find { |st | is_compatible ( st , k ) }
585+
586+ if s . nil?
587+ s = @ielr_isocores [ next_state ] . last
588+ new_state = State . new ( @states . count , s . accessing_symbol , s . kernels )
589+ new_state . closure = s . closure
590+ new_state . compute_shifts_reduces
591+ s . transitions . each do |sh , next_state |
592+ new_state . set_items_to_state ( sh . next_items , next_state )
593+ end
594+ @states << new_state
595+ @lalr_isocores [ new_state ] = s
596+ @ielr_isocores [ s ] << new_state
597+ @ielr_isocores [ s ] . each do |st |
598+ @ielr_isocores [ st ] = @ielr_isocores [ s ]
599+ end
600+ @lookaheads_recomputed [ new_state ] = true
601+ @item_lookahead_set [ new_state ] = k
602+ state . update_transition ( shift , new_state )
603+ elsif ( !@lookaheads_recomputed [ s ] )
604+ @item_lookahead_set [ s ] = k
605+ @lookaheads_recomputed [ s ] = true
606+ else
607+ merge_lookaheads ( s , k )
608+ end
609+ end
610+
611+ def propagate_lookaheads ( state , next_state )
612+ next_state . kernels . to_h { |item |
613+ lookahead_sets =
614+ if item . position == 1
615+ compute_goto_follow_set ( @lalr_isocores [ state ] , item . lhs )
616+ else
617+ kernel = state . kernels . find { |k | k . rule == item . rule && k . position == item . position - 1 }
618+ item_lookahead_set ( state ) [ kernel ]
619+ end
620+
621+ [ item , lookahead_sets & lookahead_set_filters ( next_state ) [ item ] ]
622+ }
623+ end
624+
625+ def lookahead_set_filters ( state )
626+ state . kernels . to_h { |kernel |
627+ [ kernel ,
628+ annotation_list ( @lalr_isocores [ state ] ) [ @lalr_isocores [ state ] ] . filter_map { |token , actions |
629+ token if token . term? && actions . any? { |item , _ | item == kernel }
630+ } ]
631+ }
632+ end
633+
634+ def is_compatible ( state , k )
635+ @lookaheads_recomputed [ state ] ||
636+ annotation_list ( @lalr_isocores [ state ] ) [ @lalr_isocores [ state ] ] . all? { |token , actions |
637+ a = dominant_contribution ( state , token , actions , item_lookahead_set ( state ) )
638+ b = dominant_contribution ( state , token , actions , k )
639+ a . empty? || b . empty? || a == b
640+ }
641+ end
642+
643+ def dominant_contribution ( state , token , actions , lookaheads )
644+ actions . filter_map { |action , items |
645+ action if items . empty? || items . any? { |item , bool | bool && lookaheads [ item ] . include? ( token ) }
646+ } . reject { |action |
647+ if action . is_a? ( State ::Shift )
648+ action . not_selected
649+ elsif action . is_a? ( State ::Reduce )
650+ action . not_selected_symbols . include? ( token )
651+ end
652+ }
653+ end
654+
655+ def compute_goto_follow_set ( state , nterm_token )
656+ shift , next_state = state . nterm_transitions . find { |shift , _ | shift . next_sym == nterm_token }
657+ return [ ] if shift . nil? && nterm_token . id . s_value == '$accept'
658+ state . always_follows ( shift , next_state ) . union ( state . kernels . select { |item |
659+ state . follow_kernel_items ( shift , next_state , item )
660+ } . reduce ( [ ] ) { |result , item |
661+ result . union ( item_lookahead_set ( state ) [ item ] )
662+ } )
663+ end
664+
665+ def item_lookahead_set ( state )
666+ @item_lookahead_set [ state ] ||=
667+ state . kernels . to_h { |item |
668+ value =
669+ if item . position > 1
670+ prev_state , prev_item = predecessor_with_item ( state , item )
671+ item_lookahead_set ( prev_state ) [ prev_item ]
672+ elsif item . position == 1
673+ prev_state = predecessors ( state ) . find { |p | p . shifts . any? { |shift | shift . next_sym == item . lhs } }
674+ shift , next_state = prev_state . nterm_transitions . find { |shift , _ | shift . next_sym == item . lhs }
675+ goto_follows ( prev_state , shift , next_state )
676+ else
677+ [ ]
678+ end
679+ [ item , value ]
680+ }
681+ end
682+
683+ def predecessors ( state )
684+ @predecessors [ state ] ||= @states . select { |prev | prev . transitions . any? { |_ , to_state | to_state == state } }
685+ end
686+
687+ def predecessor_with_item ( state , item )
688+ predecessors ( state ) . each do |state |
689+ state . kernels . each do |kernel |
690+ return [ state , kernel ] if kernel . rule == item . rule && kernel . position == item . position - 1
691+ end
692+ end
693+ end
694+
695+ def goto_follows ( state , shift , next_state )
696+ compute_include_dependencies ( state , shift , next_state ) . reduce ( [ ] ) { |result , goto |
697+ st , sh , next_st = goto
698+ result . union ( st . always_follows ( sh , next_st ) )
699+ }
700+ end
701+
702+ def compute_include_dependencies ( state , shift , next_state )
703+ internal = state . internal_dependencies ( shift , next_state ) . map { |sh , next_st | [ state , sh , next_st ] }
704+
705+ item = state . kernels . find { |kernel | kernel . next_sym == shift . next_sym }
706+ return internal unless item . symbols_after_dot . all? ( &:nullable )
707+
708+ s , i = state , item
709+ while i . position > 0
710+ s = predecessors ( s ) . find { |p | p . kernels . find { |item | item . rule == i . rule && item . position == i . position - 1 } }
711+ i = s . kernels . find { |item | item . rule == i . rule && item . position == i . position - 1 }
712+ end
713+
714+ p_shift , p_next_state = s . transitions . find { |sh , _ | sh . next_sym == item . lhs }
715+ internal . union ( [ [ s , p_shift , p_next_state ] ] )
716+ end
717+
718+ def annotation_list ( state )
719+ manifestations = state . inadequacy_list . transform_values { |hash | hash . to_h { |token , actions | [ token , annotate_manifestation ( state , token , actions ) ] } }
720+ state . transitions . reduce ( manifestations ) { |item , transition |
721+ item . merge ( annotate_predecessor ( state , transition [ 1 ] , annotation_list ( transition [ 1 ] ) [ transition [ 1 ] ] ) ) { |state , annotations , other_annotations |
722+ annotations . merge ( other_annotations ) { |token , actions , other_actions |
723+ actions . merge ( other_actions ) { |action , items , other_items |
724+ items . merge ( other_items ) { |item , bool , other_bool |
725+ raise if bool != other_bool
726+ bool
727+ }
728+ }
729+ }
730+ }
731+ }
732+ end
733+
734+ def annotate_manifestation ( state , token , actions )
735+ actions . to_h { |action , item |
736+ [ action ,
737+ if action . is_a? ( State ::Shift )
738+ { }
739+ elsif action . is_a? ( State ::Reduce )
740+ if item . empty_rule?
741+ compute_lhs_contributions ( state , item . lhs , token )
742+ else
743+ state . kernels . to_h { |kernel | [ kernel , kernel . rule == item . rule && kernel . end_of_rule? ] }
744+ end
745+ end
746+ ]
747+ }
748+ end
749+
750+ def compute_lhs_contributions ( state , sym , token )
751+ shift , next_state = state . nterm_transitions . find { |sh , _ | sh . next_sym == sym }
752+ if state . always_follows ( shift , next_state ) . include? ( token )
753+ { }
754+ else
755+ state . kernels . to_h { |kernel | [ kernel , state . follow_kernel? ( kernel ) && item_lookahead_set ( state ) [ kernel ] . include? ( token ) ] }
756+ end
757+ end
758+
759+ def annotate_predecessor ( state , next_state , annotation_list )
760+ { state => annotation_list . to_h { |token , actions |
761+ next [ token , { } ] if actions . empty? || actions . any? { |action , hash |
762+ hash . keys . any? { |item | hash [ item ] && item . position == 1 && compute_lhs_contributions ( state , item . lhs , token ) . empty? }
763+ }
764+ [ token , actions . to_h { |action , hash |
765+ [ action , hash . to_h { |item , _ |
766+ kernel = state . kernels . find { |k | k . rule == item . rule && k . position == item . position - 1 }
767+ [ kernel ,
768+ hash [ item ] &&
769+ (
770+ !kernel . nil? && ( item_lookahead_set ( state ) [ kernel ] . include? ( token ) ) ||
771+ ( item . position == 1 && compute_lhs_contributions ( state , item . lhs , token ) [ item ] )
772+ )
773+ ]
774+ } ]
775+ } ]
776+ }
777+ }
778+ end
555779 end
556780end
0 commit comments