/// <summary>
        /// Print First and Follow sets for all non-terminal symbols.
        /// </summary>
        public static void PrintFirstAndFollowSets <TTokenKind, TNonterminal>(
            this Grammar <TTokenKind, TNonterminal> grammar,
            TextWriter writer
            )
            where TTokenKind : struct, Enum
            where TNonterminal : struct, Enum
        {
            var table = new TableBuilder()
                        .SetTitle("First and Follow sets")
                        .SetColumns(new Column("Variable", 12),
                                    new Column("Nullable", 12),
                                    new Column("First", 16),
                                    new Column("Follow", 16))
                        .Build();

            var tableWriter = new TextTableWriter(table, writer);

            tableWriter.WriteHead();
            foreach (Nonterminal variable in grammar.Nonterminals)
            {
                tableWriter.WriteRow(variable.Name,
                                     grammar.Erasable(variable).FormatBoolean(),
                                     grammar.First(variable).ToVectorString(),
                                     grammar.Follow(variable).ToVectorString());
            }
            tableWriter.WriteFooter();
        }
        public static void PrintParsingTable <TTokenKind, TNonterminal>(
            this IShiftReduceParser <TTokenKind, TNonterminal> parser,
            TextWriter writer
            )
            where TTokenKind : struct, Enum
            where TNonterminal : struct, Enum
        {
            var actionTable = new TableBuilder()
                              .SetTitle("ACTION")
                              .SetColumns(new Column("State", 8).AsSingletonEnumerable()
                                          .Concat(parser.Terminals.Select(token => new Column(token.Name, 5))))
                              .Build();

            var actionTableWriter = new TextTableWriter(actionTable, writer);

            actionTableWriter.WriteHead();
            foreach (var state in parser.GetStates())
            {
                actionTableWriter.WriteRow(state.ToString().AsSingletonEnumerable()
                                           .Concat(parser.Terminals.Select(terminal => parser.Action(state, terminal).ToTableString())).ToArray());
            }

            actionTableWriter.WriteFooter();

            var gotoTable = new TableBuilder()
                            .SetTitle("GOTO")
                            .SetColumns(new Column("State", 8).AsSingletonEnumerable()
                                        .Concat(parser.TrimmedNonTerminalSymbols.Select(nonterminal => new Column(nonterminal.Name, 5))))
                            .Build();

            var gotoTableWriter = new TextTableWriter(gotoTable, writer);

            gotoTableWriter.WriteHead();
            foreach (var state in parser.GetStates())
            {
                gotoTableWriter.WriteRow(state.ToString().AsSingletonEnumerable()
                                         .Concat(parser.TrimmedNonTerminalSymbols.Select(nonterminal => parser.Goto(state, nonterminal).ToGotoTableString())).ToArray());
            }

            gotoTableWriter.WriteFooter();
        }