/// <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();
        }
Example #3
0
        // Driver program here
        public static void Parse <TTokenKind>(
            this IShiftReduceParsingTable <TTokenKind> parser,
            ILexer <Token <TTokenKind> > lexer,
            TextWriter logger = null
            ) where TTokenKind : struct, Enum
        {
            var table = new TableBuilder()
                        .SetColumns(new Column("SeqNo", 8),
                                    new Column("Stack", 14, Align.Left),
                                    new Column("Symbols", 14, Align.Left),
                                    new Column("Input", 10, Align.Right),
                                    new Column("Action", 34, Align.Left))
                        .Build();

            int         seqNo       = 1;
            int         ip          = 0;
            TableWriter tableWriter = null;

            if (logger != null)
            {
                tableWriter = new TextTableWriter(table, logger);
                tableWriter.WriteHead();
            }

            // stack of states (each state uniquely identifies a symbol, such that each
            // configuration (s(0)s(1)...s(m), a(i)a(i+1)...a(n)$) of the parser can generate a
            // right sentential form X(1)X(2)...X(m)a(i+1)...a(n)$). That is X(i) is the grammar
            // symbol associated with state s(i), i > 0. Note the s(0) is the only state not associated
            // with a grammar symbol, because this state represents the initial state og the LR(0) automaton
            // and its role is as a bottom-of-stack marker we can use to accept the parsed string.
            var stack = new Stack <int>();

            // push initial state onto the stack
            stack.Push(parser.StartState);

            Token <TTokenKind>    token = lexer.GetNextToken();
            Terminal <TTokenKind> a     = parser.Terminals[token.Kind];

            while (true)
            {
                int s      = stack.Peek();
                var action = parser.Action(s, a);
                // Action(s, a) = shift t
                if (action.IsShift) // consume input token here
                {
                    // push t onto the stack
                    int t = action.ShiftToState;

                    if (lexer is IBufferedLexer <Token <TTokenKind> > fake)
                    {
                        // output 'shift t'
                        tableWriter?.WriteRow($"({seqNo++})",
                                              $" {string.Join(" ", stack.Reverse())}",
                                              $" {string.Join(" ", stack.Reverse().Skip(1).Select(state => parser.GetItems(state).SpellingSymbol.Name))}",
                                              $"{fake.GetRemainingTokens(ip++).Aggregate(new StringBuilder(), (sb, tok) => sb.Append(tok.Text))} ",
                                              $" shift {t}");
                    }
                    else
                    {
                        // output 'shift t'
                        tableWriter?.WriteRow($"({seqNo++})",
                                              $" {string.Join(" ", stack.Reverse())}",
                                              $" {string.Join(" ", stack.Reverse().Skip(1).Select(state => parser.GetItems(state).SpellingSymbol.Name))}",
                                              " ",
                                              $" shift {t}");
                    }

                    stack.Push(t);

                    // call yylex to get the next token
                    token = lexer.GetNextToken();
                    a     = parser.Terminals[token.Kind];
                }
                // Action(s, a) = reduce A → β (DFA recognized a handle)
                else if (action.IsReduce) // remaining input remains unchanged
                {
                    Production p = parser.Productions[action.ReduceToProductionIndex];

                    // output 'reduce by A → β'
                    string[] values = null;
                    if (tableWriter != null)
                    {
                        if (lexer is IBufferedLexer <Token <TTokenKind> > fake)
                        {
                            values = new[]
                            {
                                $"({seqNo++})",
                                $" {string.Join(" ", stack.Reverse())}",
                                $" {string.Join(" ", stack.Reverse().Skip(1).Select(state => parser.GetItems(state).SpellingSymbol.Name))}",
                                $"{fake.GetRemainingTokens(ip).Aggregate(new StringBuilder(), (sb, tok) => sb.Append(tok.Text))} ",
                                $" reduce by {p}"
                            };
                        }
                        else
                        {
                            values = new[]
                            {
                                $"({seqNo++})",
                                $" {string.Join(" ", stack.Reverse())}",
                                $" {string.Join(" ", stack.Reverse().Skip(1).Select(state => parser.GetItems(state).SpellingSymbol.Name))}",
                                " ",
                                $" reduce by {p}"
                            };
                        }
                    }

                    // pop |β| symbols off the stack
                    stack.PopItemsOfLength(p.Length);
                    // let state t now be on top of the stack
                    int t = stack.Peek();
                    // push GOTO(t, A) onto the stack
                    int v = parser.Goto(t, p.Head);
                    stack.Push(v);

                    if (tableWriter != null)
                    {
                        values[4] += $", goto {v}";
                        tableWriter.WriteRow(values);
                    }

                    // TODO: Create a new AST node for the (semantic) rule A → β, and build AST
                }
                // DFA recognized a the accept handle of the initial item set
                else if (action.IsAccept)
                {
                    // TODO: Should this be validated here???
                    Debug.Assert(lexer.GetNextToken().IsEof);

                    if (lexer is IBufferedLexer <Token <TTokenKind> > fake)
                    {
                        // output accept
                        tableWriter?.WriteRow($"({seqNo})",
                                              $" {string.Join(" ", stack.Reverse())}",
                                              $" {string.Join(" ", stack.Reverse().Skip(1).Select(state => parser.GetItems(state).SpellingSymbol.Name))}",
                                              $"{fake.GetRemainingTokens(ip).Aggregate(new StringBuilder(), (sb, tok) => sb.Append(tok.Text))} ",
                                              $" {action}");
                    }
                    else
                    {
                        // output accept
                        tableWriter?.WriteRow($"({seqNo})",
                                              $" {string.Join(" ", stack.Reverse())}",
                                              $" {string.Join(" ", stack.Reverse().Skip(1).Select(state => parser.GetItems(state).SpellingSymbol.Name))}",
                                              " ",
                                              $" {action}");
                    }

                    break;
                }
                else
                {
                    // error
                    throw new InvalidOperationException($"Unexpected symbol: {a.Name}");
                }
            }

            tableWriter?.WriteFooter();
        }