public void GenerateAutomaton()
        {
            SubProduction startingRule = Grammar.Instance.First(x => x.Identifier == ParserConstants.Initial).First();

            GrammarSymbols = Grammar.Instance.Symbols();

            Item startingItem = new Item(startingRule, 0, new HashSet <TerminalExpressionDefinition> {
                new TerminalExpressionDefinition {
                    TokenType = TokenType.EndMarker
                }
            });

            Initial = new ItemSet(new List <Item> {
                startingItem
            }).Closure();
            CanonicalSets = Initial.GetCanonicalSets(GrammarSymbols);
            PropogateLookaheads(GrammarSymbols);
        }
        private bool Reduce(ActionParsingTableEntry entry)
        {
            SubProduction subProduction = null;

            if (entry.Items.Count > 1)
            {
                Item match = entry.Items.First(x => x.Lookahead.Any(x => x.TokenType == Current.Type));
                subProduction = match.SubProduction;
            }
            else
            {
                subProduction = entry.Items.First().SubProduction;
            }

            NonTerminalExpressionDefinition target = new NonTerminalExpressionDefinition {
                Identifier = subProduction.Production.Identifier
            };

            ParsingNode parsingNode = new ParsingNode()
            {
                Expression = new NonTerminalExpression
                {
                    Identifier = subProduction.Production.Identifier,
                },
                Parser        = this,
                SubProduction = subProduction
            };

            for (int y = subProduction.Count - 1; y >= 0; y--)
            {
                for (int i = ParsingNodes.Count - 1; i >= 0; i--)
                {
                    if (ParsingNodes[i].Parent != null)
                    {
                        continue;
                    }

                    if (ParsingNodes[i].Expression is NonTerminalExpression ne &&
                        subProduction[y] is NonTerminalExpressionDefinition ned)
                    {
                        if (ne.Identifier == ned.Identifier)
                        {
                            ParsingNodes[i].Expression.Key = ned.Key;
                            ParsingNodes[i].Parent         = parsingNode;
                            break;
                        }
                    }
                    if (ParsingNodes[i].Expression is TerminalExpression te &&
                        subProduction[y] is TerminalExpressionDefinition ted)
                    {
                        if (te.TokenType == ted.TokenType)
                        {
                            ParsingNodes[i].Parent = parsingNode;
                            break;
                        }
                    }
                }
            }

            ParsingNodes.Add(parsingNode);

            List <ExpressionDefinition> expressionDefinitionsToRemove = subProduction.Where(x => !(x is SemanticActionDefinition) && !(x is TerminalExpressionDefinition ted && ted.TokenType == TokenType.EmptyString)).ToList();

            for (int i = 0; i < expressionDefinitionsToRemove.Count(); i++)
            {
                Stack.RemoveAt(Stack.Count - 1);
            }

            ItemSet tos = Stack.Last();
            List <ParsingTableEntry> entries = ParsingTable.GetSegment(tos).Entries
                                               .Where(x => x is GotoParsingTableEntry g &&
                                                      g.ItemSet == tos &&
                                                      g.ExpressionDefinition.IsEqualTo(target)).ToList();

            if (entries.Count > 1)
            {
                throw new Exception();
            }

            GotoParsingTableEntry gotoEntry = (GotoParsingTableEntry)entries.First();

            if (DebugModeEnabled)
            {
                Console.WriteLine("REDUCE DEST " + gotoEntry.Destination.Id + ", TARGET " + target.ToString());
            }
            Stack.Add(gotoEntry.Destination);

            return(true);
        }