/// <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(); }
// 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(); }