Ejemplo n.º 1
0
Archivo: Lalr1.cs Proyecto: wushian/pck
        static ICollection <LRItem> _FillLRMove(this CfgDocument cfg, IEnumerable <LRItem> itemSet, object input, IProgress <CfgLalr1Progress> progress, ICollection <LRItem> result = null)
        {
            if (null == result)
            {
                result = new HashSet <LRItem>();
            }
            int i = 0;

            foreach (var item in itemSet)
            {
                if (null != progress)
                {
                    progress.Report(new CfgLalr1Progress(CfgLalr1Status.ComputingMove, i));
                }
                var next = item.RightIndex < item.Rule.Right.Count ? item.Rule.Right[item.RightIndex] : null;
                if (item.RightIndex < item.Rule.Right.Count)
                {
                    if (Equals(next, input))
                    {
                        var lri = new LRItem(item.Rule, item.RightIndex + 1);
                        result.Add(lri);
                    }
                }
                ++i;
            }
            _FillLRClosureInPlace(cfg, progress, result);
            return(result);
        }
Ejemplo n.º 2
0
        public override void Apply(LanguageData language, LRItem owner)
        {
            //Create custom action and put it into state.Actions table
            var state  = owner.State;
            var action = new CustomParserAction(language, state, _executeMethod);

            _previewMethod?.Invoke(action);
            if (!state.BuilderData.IsInadequate)
            {
                // adequate state, with a single possible action which is DefaultAction
                state.DefaultAction = action;
            }
            else if (owner.Core.Current != null)
            {
                //shift action
                state.Actions[owner.Core.Current] = action;
            }
            else
            {
                foreach (var lkh in owner.Lookaheads)
                {
                    state.Actions[lkh] = action;
                }
            }
            //We consider all conflicts handled by the action
            state.BuilderData.Conflicts.Clear();
        }
Ejemplo n.º 3
0
        public override void Apply(LanguageData language, LRItem owner)
        {
            var state        = owner.State;
            var allConflicts = state.BuilderData.Conflicts;

            if (allConflicts.Count == 0)
            {
                return;
            }
            //Find all conflicts that can be resolved by operator precedence
            // SL does not support Find extension, so we do it with explicit loop
            var operConflicts = allConflicts.Where(c => c.Flags.IsSet(TermFlags.IsOperator)).ToList();

            foreach (var conflict in operConflicts)
            {
                var newState    = state.BuilderData.GetNextState(conflict);
                var reduceItems = state.BuilderData.ReduceItems.SelectByLookahead(conflict).ToList();
                if (newState == null || reduceItems.Count != 1)
                {
                    // This cannot be fixed by precedence
                    continue;
                }
                state.Actions[conflict] = new PrecedenceBasedParserAction(conflict, newState, reduceItems[0].Core.Production);
                allConflicts.Remove(conflict);
            }
        }
Ejemplo n.º 4
0
 public override void Apply(LanguageData language, LRItem owner) {
   var state = owner.State;
   var conflicts = state.BuilderData.Conflicts;
   if (conflicts.Count == 0) return;
   switch (ActionType) {
     case PreferredActionType.Shift:
       var currTerm = owner.Core.Current as Terminal;
       if (currTerm == null || !conflicts.Contains(currTerm)) return; //nothing to do
       //Current term for shift item (hint owner) is a conflict - resolve it with shift action
       var newState = owner.ShiftedItem.State;
       var shiftAction = new ShiftParserAction(owner);
       state.Actions[currTerm] = shiftAction;
       conflicts.Remove(currTerm);
       return;
     case PreferredActionType.Reduce:
       if (!owner.Core.IsFinal) return; //we take care of reduce items only here
       //we have a reduce item with "Reduce" hint. Check if any of lookaheads are in conflict
       ReduceParserAction reduceAction = null;
       foreach (var lkhead in owner.Lookaheads)
         if (conflicts.Contains(lkhead)) {
           if (reduceAction == null)
             reduceAction = new ReduceParserAction(owner.Core.Production);
           state.Actions[lkhead] = reduceAction;
           conflicts.Remove(lkhead);
         }
       return;
   }//switch
 }//method
 public override void Apply(LanguageData language, LRItem owner)
 {
     //Check that owner is not final - we can imply precedence only in shift context
     var curr = owner.Core.Current;
     if (curr == null)
         return;
     //mark the state, to make sure we do stuff in Term_Shifting event handler only in appropriate states
     owner.State.CustomFlags |= ImpliedPrecedenceCustomFlag;
     curr.Shifting += Term_Shifting;
 }
Ejemplo n.º 6
0
 public ReduceAction(ParseTable <T> table, LRItem <T> reduceItem)
     : base(table)
 {
     if (reduceItem != null)
     {
         ReduceItem = reduceItem;
     }
     else
     {
         throw new ArgumentNullException("reduceItem", "Must be non-null");
     }
 }
Ejemplo n.º 7
0
 public AcceptAction(ParseTable <T> table, LRItem <T> acceptItem)
     : base(table)
 {
     if (acceptItem != null)
     {
         AcceptItem = acceptItem;
     }
     else
     {
         throw new ArgumentNullException("acceptItem", "Must be non-null");
     }
 }
Ejemplo n.º 8
0
        public override void Apply(LanguageData language, LRItem owner)
        {
            //Check that owner is not final - we can imply precedence only in shift context
            var curr = owner.Core.Current;

            if (curr == null)
            {
                return;
            }
            //mark the state, to make sure we do stuff in Term_Shifting event handler only in appropriate states
            owner.State.CustomFlags |= ParserStateFlags.ImpliedPrecedence;
            curr.Shifting           += Term_Shifting;
        }
Ejemplo n.º 9
0
        public override void Apply(LanguageData language, LRItem owner)
        {
            var state     = owner.State;
            var conflicts = state.BuilderData.Conflicts;

            if (conflicts.Count == 0)
            {
                return;
            }
            switch (_actionType)
            {
            case PreferredActionType.Shift:
                var currTerm = owner.Core.Current as Terminal;
                if (currTerm == null || !conflicts.Contains(currTerm))
                {
                    //nothing to do
                    return;
                }
                //Current term for shift item (hint owner) is a conflict - resolve it with shift action
                var shiftAction = new ShiftParserAction(owner);
                state.Actions[currTerm] = shiftAction;
                conflicts.Remove(currTerm);
                return;

            case PreferredActionType.Reduce:
                if (!owner.Core.IsFinal)
                {
                    //we take care of reduce items only here
                    return;
                }
                //we have a reduce item with "Reduce" hint. Check if any of lookaheads are in conflict
                ReduceParserAction reduceAction = null;
                foreach (var lkhead in owner.Lookaheads)
                {
                    if (conflicts.Contains(lkhead))
                    {
                        if (reduceAction == null)
                        {
                            reduceAction = new ReduceParserAction(owner.Core.Production);
                        }
                        state.Actions[lkhead] = reduceAction;
                        conflicts.Remove(lkhead);
                    }
                }
                return;

            default:
                throw new ArgumentOutOfRangeException();
            }
        }
        //Creates closure items with "spontaneously generated" lookaheads
        private bool AddClosureItems(ParserState state)
        {
            bool result = false;

            //note that we change collection while we iterate thru it, so we have to use "for i" loop
            for (int i = 0; i < state.Items.Count; i++)
            {
                LRItem  item     = state.Items[i];
                BnfTerm currTerm = item.Core.Current;
                if (currTerm == null || !(currTerm is NonTerminal))
                {
                    continue;
                }
                //1. Add normal closure items
                NtData currInfo = (NtData)currTerm.ParserData;
                foreach (Production prod in currInfo.Productions)
                {
                    LR0Item core    = prod.LR0Items[0]; //item at zero index is the one that starts with dot
                    LRItem  newItem = TryFindItem(state, core);
                    if (newItem == null)
                    {
                        newItem = new LRItem(state, core);
                        state.Items.Add(newItem);
                        result = true;
                    }
                    #region Comments on lookaheads processing
                    // The general idea of generating ("spontaneously") the lookaheads is the following:
                    // Let's the original item be in the form
                    //   [A -> alpha . B beta , lset]
                    //     where <B> is a non-terminal and <lset> is a set of lookaheads,
                    //      <beta> is some string (B's tail in our terminology)
                    // Then the closure item on non-teminal B is an item
                    //   [B -> x, firsts(beta + lset)]
                    //      (the lookahead set is expression after the comma).
                    // To generate lookaheads on a closure item, we simply take "firsts"
                    //   from the tail <beta> of the NonTerminal <B>.
                    //   Normally if tail <beta> is nullable we would add ("propagate")
                    //   the <lset> lookaheads from <A> to <B>.
                    //   We dont' do it right here - we simply add a propagation link.
                    //   We propagate all lookaheads later in a separate process.
                    #endregion
                    newItem.NewLookaheads.AddRange(item.Core.TailFirsts);
                    if (item.Core.TailIsNullable && !item.PropagateTargets.Contains(newItem))
                    {
                        item.PropagateTargets.Add(newItem);
                    }
                } //foreach prod
            }     //for i (LRItem)
            return(result);
        }
 public override void Apply(LanguageData language, LRItem owner)
 {
     //Create custom action and put it into state.Actions table
     var state = owner.State;
     var action = new CustomParserAction(language, state, _executeMethod);
     if (_previewMethod != null)
         _previewMethod(action);
     if (!state.BuilderData.IsInadequate) // adequate state, with a single possible action which is DefaultAction
         state.DefaultAction = action;
     else if (owner.Core.Current != null) //shift action
         state.Actions[owner.Core.Current] = action;
     else
         foreach (var lkh in owner.Lookaheads)
             state.Actions[lkh] = action;
     //We consider all conflicts handled by the action
     state.BuilderData.Conflicts.Clear();
 } //method
Ejemplo n.º 12
0
 public override void Apply(LanguageData language, LRItem owner) {
   var state = owner.State;
   var allConflicts = state.BuilderData.Conflicts;
   if (allConflicts.Count == 0)
     return; 
   //Find all conflicts that can be resolved by operator precedence
   // SL does not support Find extension, so we do it with explicit loop
   var operConflicts = new List<Terminal>(); 
   foreach(var c in allConflicts)
     if (c.Flags.IsSet(TermFlags.IsOperator))
       operConflicts.Add(c);
   foreach (var conflict in operConflicts) {
     var newState = state.BuilderData.GetNextState(conflict);
     var reduceItem = state.BuilderData.ReduceItems.SelectByLookahead(conflict).First(); //should be only one
     state.Actions[conflict] = new PrecedenceBasedParserAction(conflict, newState, reduceItem.Core.Production);
     allConflicts.Remove(conflict);
   }//foreach conflict
 }
 public override void Apply(LanguageData language, LRItem owner)
 {
     var state = owner.State;
     if (!state.BuilderData.IsInadequate) return; //the state is adequate, we don't need to do anything
     var conflicts = state.BuilderData.Conflicts;
     // Note that we remove lookaheads from the state conflicts set at the end of this method - to let parser builder know
     // that this conflict is taken care of. 
     // On the other hand we may call this method multiple times for different LRItems if we have multiple hints in the same state. 
     // Since we remove lookahead from conflicts on the first call, on the consequitive calls it will not be a conflict -
     // but we still need to add a new conditional entry to a conditional parser action for this lookahead. 
     // Thus we process the lookahead anyway, even if it is not a conflict. 
     // if (conflicts.Count == 0) return; -- this is a wrong thing to do
     switch (_actionType)
     {
         case PreferredActionType.Reduce:
             if (!owner.Core.IsFinal) return;
             //it is reduce action; find lookaheads in conflict
             var lkhs = owner.Lookaheads;
             if (lkhs.Count == 0) return; //if no conflicts then nothing to do
             var reduceAction = new ReduceParserAction(owner.Core.Production);
             var reduceCondEntry = new ConditionalParserAction.ConditionalEntry(CheckCondition, reduceAction,
                 _description);
             foreach (var lkh in lkhs)
             {
                 AddConditionalEntry(state, lkh, reduceCondEntry);
                 if (conflicts.Contains(lkh))
                     conflicts.Remove(lkh);
             }
             break;
         case PreferredActionType.Shift:
             var curr = owner.Core.Current as Terminal;
             if (curr == null) return; //it is either reduce item, or curr is a NonTerminal - we cannot shift it
             var shiftAction = new ShiftParserAction(owner);
             var shiftCondEntry = new ConditionalParserAction.ConditionalEntry(CheckCondition, shiftAction,
                 _description);
             AddConditionalEntry(state, curr, shiftCondEntry);
             if (conflicts.Contains(curr))
                 conflicts.Remove(curr);
             break;
     }
 } //method
        private void CreateParserStates()
        {
            Data.States.Clear();
            _stateHash = new ParserStateTable();
            CreateInitialAndFinalStates();

            string augmRootKey = Data.AugmentedRoot.Key;

            // Iterate through states (while new ones are created) and create shift transitions and new states
            for (int index = 0; index < Data.States.Count; index++)
            {
                ParserState state = Data.States[index];
                AddClosureItems(state);
                //Get keys of all possible shifts
                ShiftTable shiftTable = GetStateShifts(state);
                //Each key in shifts dict is an input element
                // Value is LR0ItemList of shifted LR0Items for this input element.
                foreach (string input in shiftTable.Keys)
                {
                    LR0ItemList  shiftedCoreItems = shiftTable[input];
                    ParserState  newState         = FindOrCreateState(shiftedCoreItems);
                    ActionRecord newAction        = new ActionRecord(input, ParserActionType.Shift, newState, null);
                    state.Actions[input] = newAction;
                    //link original LRItems in original state to derived LRItems in newState
                    foreach (LR0Item coreItem in shiftedCoreItems)
                    {
                        LRItem fromItem = FindItem(state, coreItem.Production, coreItem.Position - 1);
                        LRItem toItem   = FindItem(newState, coreItem.Production, coreItem.Position);
                        if (!fromItem.PropagateTargets.Contains(toItem))
                        {
                            fromItem.PropagateTargets.Add(toItem);
                        }
                        //copy hints from core items into the newAction
                        newAction.ShiftItems.Add(fromItem);
                    } //foreach coreItem
                }     //foreach input
            }         //for index
            Data.FinalState = Data.InitialState.Actions[augmRootKey].NewState;
        }             //method
Ejemplo n.º 15
0
Archivo: Lalr1.cs Proyecto: wushian/pck
        static void _FillLRClosureInPlace(CfgDocument cfg, IProgress <CfgLalr1Progress> progress, ICollection <LRItem> result)
        {
            var done = false;

            while (!done)
            {
                done = true;
                var l = result.ToArray();
                for (var i = 0; i < l.Length; i++)
                {
                    if (null != progress)
                    {
                        progress.Report(new CfgLalr1Progress(CfgLalr1Status.ComputingClosure, i));
                    }
                    var item = l[i];
                    var next = item.RightIndex < item.Rule.Right.Count ? item.Rule.Right[item.RightIndex] : null;
                    if (item.RightIndex < item.Rule.Right.Count)
                    {
                        if (cfg.IsNonTerminal(next))
                        {
                            for (int jc = cfg.Rules.Count, j = 0; j < jc; ++j)
                            {
                                var r = cfg.Rules[j];
                                if (r.Left == next)
                                {
                                    var lri = new LRItem(r, 0);
                                    if (!result.Contains(lri))
                                    {
                                        done = false;
                                        result.Add(lri);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
Ejemplo n.º 16
0
        }         //method

        private void ResolveConflictByHints(ParserState state, Terminal conflict)
        {
            var stateData = state.BuilderData;

            //reduce hints
            var reduceItems = stateData.ReduceItems.SelectByLookahead(conflict);

            foreach (var reduceItem in reduceItems)
            {
                if (reduceItem.Core.Hints.Find(h => h.HintType == HintType.ResolveToReduce) != null)
                {
                    state.Actions[conflict] = new ParserAction(ParserActionType.Reduce, null, reduceItem.Core.Production);
                    state.BuilderData.ResolvedConflicts.Add(conflict);
                    return;
                }
            }

            //shift hints
            var shiftItems = stateData.ShiftItems.SelectByCurrent(conflict);

            foreach (var shiftItem in shiftItems)
            {
                if (shiftItem.Core.Hints.Find(h => h.HintType == HintType.ResolveToShift) != null)
                {
                    //shift action is already there
                    state.BuilderData.ResolvedConflicts.Add(conflict);
                    return;
                }
            }

            //code hints
            // first prepare data for conflict action: reduceProduction (for possible reduce) and newState (for possible shift)
            var         reduceProduction = reduceItems.First().Core.Production; //take first of reduce productions
            ParserState newState         = (state.Actions.ContainsKey(conflict) ? state.Actions[conflict].NewState : null);
            // Get all items that might contain hints;
            var allItems = new LRItemList();

            allItems.AddRange(state.BuilderData.ShiftItems.SelectByCurrent(conflict));
            allItems.AddRange(state.BuilderData.ReduceItems.SelectByLookahead(conflict));
            // Scan all items and try to find hint with resolution type Code
            foreach (var item in allItems)
            {
                if (item.Core.Hints.Find(h => h.HintType == HintType.ResolveInCode) != null)
                {
                    state.Actions[conflict] = new ParserAction(ParserActionType.Code, newState, reduceProduction);
                    state.BuilderData.ResolvedConflicts.Add(conflict);
                    return;
                }
            }

            //custom hints
            // find custom grammar hints and build custom conflict resolver
            var    customHints = new List <CustomGrammarHint>();
            var    hintItems   = new Dictionary <CustomGrammarHint, LRItem>();
            LRItem defaultItem = null; // the first item with no hints

            foreach (var item in allItems)
            {
                var hints = item.Core.Hints.OfType <CustomGrammarHint>();
                foreach (var hint in hints)
                {
                    customHints.Add(hint);
                    hintItems[hint] = item;
                }
                if (defaultItem == null && !hints.Any())
                {
                    defaultItem = item;
                }
            }
            // if there are custom hints, build conflict resolver
            if (customHints.Count > 0)
            {
                state.Actions[conflict] = new ParserAction(newState, reduceProduction, args => {
                    // examine all custom hints and select the first production that matched
                    foreach (var customHint in customHints)
                    {
                        if (customHint.Match(args))
                        {
                            var item = hintItems[customHint];
                            // If the ReduceProduction was clear, then use the default production following the hints
                            if (args.ReduceProduction == null)
                            {
                                args.ReduceProduction = item.Core.Production;
                            }
                            return;
                        }
                    }
                    // no hints matched, select default LRItem
                    if (defaultItem != null)
                    {
                        args.ReduceProduction = defaultItem.Core.Production;
                        args.Result           = args.NewShiftState != null ? ParserActionType.Shift : ParserActionType.Reduce;
                        return;
                    }
                    // prefer Reduce if Shift operation is not available
                    args.Result = args.NewShiftState != null ? ParserActionType.Shift : ParserActionType.Reduce;
                    // TODO: figure out what to do next
                });
                state.BuilderData.ResolvedConflicts.Add(conflict);
                return;
            }
        }
Ejemplo n.º 17
0
 public ShiftParserAction(LRItem item)
     : this(item.Core.Current, item.ShiftedItem.State)
 {
 }
 public ShiftParserAction(LRItem item) : this(item.Core.Current, item.ShiftedItem.State)
 {
 }
Ejemplo n.º 19
0
 /// <summary> Gives a chance to a custom code in hint to interfere in parser automaton construction.</summary>
 /// <param name="language">The LanguageData instance.</param>
 /// <param name="owner">The <see cref="LRItem"/> that "owns" the hint. </param>
 /// <remarks>
 /// The most common purpose of this method (it's overrides) is to resolve the conflicts
 /// by adding specific actions into State.Actions dictionary.
 /// The owner parameter represents the position in the grammar expression where the hint
 /// is found. The parser state is available through owner.State property.
 /// </remarks>
 public virtual void Apply(LanguageData language, LRItem owner)
 {
     // owner.State  -- the parser state
     // owner.State.BuilderData.Conflicts -- as set of conflict terminals
     // owner.State.Actions -- a dictionary of actions in the current state.
 }
Ejemplo n.º 20
0
        public override void Apply(LanguageData language, LRItem owner)
        {
            var state = owner.State;

            if (!state.BuilderData.IsInadequate)
            {
                return;                                  //the state is adequate, we don't need to do anything
            }
            var conflicts = state.BuilderData.Conflicts;

            // Note that we remove lookaheads from the state conflicts set at the end of this method - to let parser builder know
            // that this conflict is taken care of.
            // On the other hand we may call this method multiple times for different LRItems if we have multiple hints in the same state.
            // Since we remove lookahead from conflicts on the first call, on the consequitive calls it will not be a conflict -
            // but we still need to add a new conditional entry to a conditional parser action for this lookahead.
            // Thus we process the lookahead anyway, even if it is not a conflict.
            // if (conflicts.Count == 0) return; -- this is a wrong thing to do
            switch (_actionType)
            {
            case PreferredActionType.Reduce:
                if (!owner.Core.IsFinal)
                {
                    return;
                }
                //it is reduce action; find lookaheads in conflict
                var lkhs = owner.Lookaheads;
                if (lkhs.Count == 0)
                {
                    return;     //if no conflicts then nothing to do
                }
                var reduceAction    = new ReduceParserAction(owner.Core.Production);
                var reduceCondEntry = new ConditionalParserAction.ConditionalEntry(CheckCondition, reduceAction, _description);
                foreach (var lkh in lkhs)
                {
                    AddConditionalEntry(state, lkh, reduceCondEntry);
                    if (conflicts.Contains(lkh))
                    {
                        conflicts.Remove(lkh);
                    }
                }
                break;

            case PreferredActionType.Shift:
                var curr = owner.Core.Current as Terminal;
                if (curr == null)
                {
                    return;     //it is either reduce item, or curr is a NonTerminal - we cannot shift it
                }
                var shiftAction    = new ShiftParserAction(owner);
                var shiftCondEntry = new ConditionalParserAction.ConditionalEntry(CheckCondition, shiftAction, _description);
                AddConditionalEntry(state, curr, shiftCondEntry);
                if (conflicts.Contains(curr))
                {
                    conflicts.Remove(curr);
                }
                break;

            default:
                throw new ArgumentOutOfRangeException();
            }
        }
Ejemplo n.º 21
0
        protected override LRItemSetCollection ComputeItemSetCollection()
        {
            var(collection, gotoSymbol) = ComputeLR0ItemSetKernelsCollectionAndGotoLookup();

            // Determine propagation of lookaheads, and initialze lookaheads based on spontaneous generation
            // (page 270-275, dragon book 2nd ed.)
            collection.StartState.Single().Lookaheads.Add(Eof);
            var rulePropagations = new Dictionary <(int state, LRItem item), HashSet <(int state, LRItem item)> >();

            // For all states
            foreach (var itemset in collection)
            {
                var gotosForState = gotoSymbol[itemset.Index];

                // For every kernel item
                foreach (var kernelitem in itemset)
                {
                    var itemKey = (state : itemset.Index, item : kernelitem);

                    // Create an item set with a dummy lookahead.
                    // This dummy item set is based on the current state (current item set).
                    var dummyLookahead = Unknown.AsSingletonEnumerable();
                    var dummyItem      = new LRItem(kernelitem.Rule, kernelitem.Marker, dummyLookahead);
                    var dummyItemSet   = new LRItemSet(dummyItem.AsSingletonEnumerable());
                    var j = LR1Closure(dummyItemSet);

                    // For every symbol/state in the goto list
                    foreach (var gotokvp in gotosForState)
                    {
                        // What would be the next state?
                        var gotoItemSet    = collection[gotokvp.Value];
                        var gotoItemLookup = gotoItemSet.ToDictionary(g => g, g => g);

                        // Find the items in the dummy set with the marker before this symbol.
                        foreach (var b in j.Where(bb => bb.Marker < bb.Length && bb.Rule.Symbols[bb.Marker].Equals(gotokvp.Key)))
                        {
                            // Get the item corresponding to the goto state with the marker advanced over the current symbol.
                            var gotoItem = gotoItemLookup[new LRItem(b.Rule, b.Marker + 1)];

                            // Note if lookaheads are propagated to the next item
                            if (b.Lookaheads.Any(l => l.CompareTo(Unknown) == 0))
                            {
                                if (!rulePropagations.ContainsKey(itemKey))
                                {
                                    rulePropagations[itemKey] = new HashSet <(int, LRItem)>();
                                }

                                rulePropagations[itemKey].Add((gotoItemSet.Index, gotoItem));
                            }

                            gotoItem.Lookaheads.UnionWith(
                                b.Lookaheads.Where(l => l.CompareTo(Unknown) != 0)
                                );
                        }
                    }
                }
            }

            bool changed;

            do
            {
                changed = false;

                foreach (var state in collection)
                {
                    foreach (var item in state)
                    {
                        var itemKey = (state.Index, item);

                        if (!rulePropagations.TryGetValue(itemKey, out var propagated))
                        {
                            continue;
                        }
                        foreach (var key in propagated)
                        {
                            if (key.item.Lookaheads.TryUnionWith(item.Lookaheads))
                            {
                                changed = true;
                            }
                        }
                    }
                }
            } while (changed);

            // Close all the kernels
            for (var i = 0; i < collection.Count; i++)
            {
                collection[i]       = LR1Closure(collection[i]);
                collection[i].Index = i;
            }

            return(collection);
        }