private static Dictionary <Nonterminal, Set <Terminal <TTokenKind> > > ComputeFollow( Grammar <TTokenKind, TNonterminal> grammar, IFirstSymbolsAnalyzer <TTokenKind> analyzer) { var(initFollowSets, graph) = DigraphAlgorithm.GetFollowGraph(grammar, analyzer); var followSets = DigraphAlgorithm.Traverse(graph, initFollowSets); var followMap = grammar.Nonterminals.ToDictionary(v => v, v => followSets[v.Index]); return(followMap); }
// TODO: Insert into symbols overload // FIRST can be thought of as the extension of START, but often FIRST is defined for both single symbols // and sentential forms. That is FIRST is extended to all grammar symbols (i.e. sentential forms) // The FIRST function is a simple extension of START (single symbol) to the domain of sentential forms. // FIRST(α) = { x ∈ T | α ∗⇒ xβ } // An alternative definition which shows how to derive FIRST from START recursively is // FIRST(X1X2...Xk) = START(X1) ∪ FIRST(X2...Xk), if X1 is nullable // FIRST(X1X2...Xk) = START(X1) otherwise // FIRST(ε) = Ø = { } /// <summary> /// The First function yields the set of starter symbols for a sequence of grammar symbols. It is formally /// defined as /// FIRST(α) = { a ∈ T | α ∗⇒ aβ } /// for any sentential form α ∈ (T ∪ N)*. We have therefore extended the set-valued function to all sentential forms. /// </summary> /// <param name="analyzer"></param> /// <param name="symbols">The sequence of symbols (possibly empty, aka epsilon)</param> public static IReadOnlySet <Terminal <TTokenKind> > First <TTokenKind>( this IFirstSymbolsAnalyzer <TTokenKind> analyzer, IEnumerable <Symbol> symbols) where TTokenKind : struct, Enum { // If α is any string of grammar symbols, let First(α) be the set of terminals that begin the // strings derived from α. In some texts (dragon book) if α *=> ε, then ε is also in First(α). // We prefer to keep nullable in a separate Erasable function. var first = new Set <Terminal <TTokenKind> >(); foreach (var symbol in symbols) { first.AddRange(analyzer.First(symbol)); if (!analyzer.Erasable(symbol)) { break; } } return(first); }