예제 #1
0
        CreateActionTable <SYMBOL_ENUM, TREE_NODE>(Productions <SYMBOL_ENUM, TREE_NODE> productions,
                                                   PrecedenceTable <SYMBOL_ENUM> precedenceTable,
                                                   GrammarReport <SYMBOL_ENUM, TREE_NODE> report,
                                                   int lookaheadWidth)

            where SYMBOL_ENUM : struct
            where TREE_NODE : class
        {
            if (productions == null)
            {
                return(null);
            }
            if (lookaheadWidth < 1)
            {
                throw new ArgumentException("Lookahead width is too small");
            }

            precedenceTable.Validate(lookaheadWidth);

            BuilderSets <SYMBOL_ENUM, TREE_NODE> builder_sets = BuilderSets.Create(productions, lookaheadWidth);

            report.Setup(productions, builder_sets);

            {
                IEnumerable <Tuple <SYMBOL_ENUM, bool> > err_recur_lhs = productions.Entries
                                                                         // do not check Auto recursive
                                                                         .Where(prod => ((prod.Recursive == RecursiveEnum.No) && builder_sets.CoverSets.IsRecursive(prod.LhsNonTerminal)) ||
                                                                                ((prod.Recursive == RecursiveEnum.Yes) && !builder_sets.CoverSets.IsRecursive(prod.LhsNonTerminal)))
                                                                         .Select(prod => Tuple.Create(prod.LhsNonTerminal, !builder_sets.CoverSets.IsRecursive(prod.LhsNonTerminal))).Distinct().ToArray();
                if (err_recur_lhs.Any())
                {
                    if (err_recur_lhs.Where(it => it.Item2).Any())
                    {
                        report.AddError("Productions incorrectly marked as recursive: " + err_recur_lhs.Where(it => it.Item2).Select(it => "\"" + productions.SymbolsRep.Get(it.Item1) + "\"").Join(",") + ".");
                    }
                    if (err_recur_lhs.Where(it => !it.Item2).Any())
                    {
                        report.AddError("Productions incorrectly marked as non-recursive: " + err_recur_lhs.Where(it => !it.Item2).Select(it => "\"" + productions.SymbolsRep.Get(it.Item1) + "\"").Join(",") + ".");
                    }
                    return(null);
                }
            }

            Dfa <SYMBOL_ENUM, TREE_NODE> dfa = Worker.CreateDfa(productions, lookaheadWidth, builder_sets.PrecomputedRhsFirsts,
                                                                builder_sets.HorizonSets);

            report.Setup(dfa);

            return(new ActionBuilder <SYMBOL_ENUM, TREE_NODE>().FillActionTable(productions, builder_sets.FirstSets,
                                                                                builder_sets.CoverSets,
                                                                                builder_sets.HorizonSets,
                                                                                lookaheadWidth, dfa, precedenceTable, report));
        }
예제 #2
0
        Create <SYMBOL_ENUM, TREE_NODE>(Productions <SYMBOL_ENUM, TREE_NODE> productions,
                                        PrecedenceTable <SYMBOL_ENUM> precedenceTable,
                                        GrammarReport <SYMBOL_ENUM, TREE_NODE> report,
                                        int lookaheadWidth)

            where SYMBOL_ENUM : struct
            where TREE_NODE : class
        {
            ActionTable <SYMBOL_ENUM, TREE_NODE> action_table = CreateActionTable(productions, precedenceTable, report, lookaheadWidth);

            if (action_table == null)
            {
                return(null);
            }
            else
            {
                return(new Parser <SYMBOL_ENUM, TREE_NODE>(action_table, productions.SymbolsRep));
            }
        }
예제 #3
0
        public ActionTable(Dfa <SYMBOL_ENUM, TREE_NODE> dfa,
                           Productions <SYMBOL_ENUM, TREE_NODE> productions,
                           int lookaheadWidth)
            : base(null, null, null,
                   productions.StartSymbol,
                   productions.EofSymbol,
                   productions.SyntaxErrorSymbol,
                   lookaheadWidth)
        {
            this.symbolsRep = productions.SymbolsRep;
            this.symbols    = productions.NonAndTerminals.ToList();

            int symbolValuesWidth = 1 + productions.NonAndTerminals.Concat(productions.EofSymbol).Select(it => (int)(object)it).Max();

            actionsTable = new IEnumerable <ParseAction <SYMBOL_ENUM, TREE_NODE> > [
                dfa.IndexRange(),
                (int)Math.Pow(symbolValuesWidth, lookaheadWidth)
                           ];

            edgesTable = CreateEdgesTable(dfa.IndexRange(), symbolValuesWidth);

            recoveryTable = new IEnumerable <NfaCell <SYMBOL_ENUM, TREE_NODE> > [dfa.IndexRange(), symbolValuesWidth];

            foreach (Node <SYMBOL_ENUM, TREE_NODE> node in dfa.Nodes)
            {
                foreach (KeyValuePair <SYMBOL_ENUM, Node <SYMBOL_ENUM, TREE_NODE> > edge in node.EdgesTo)
                {
                    int edge_int = (int)(object)edge.Key;

                    edgesTable[node.State.Index, edge_int] = edge.Value.State.Index;

                    IEnumerable <NfaCell <SYMBOL_ENUM, TREE_NODE> > recovery_items =
                        edge.Value.State.ParsingActiveItems.Where(it => it.IsAtRecoveryPoint).Select(it => it.CreateCell()).ToList();
                    if (recovery_items.Any())
                    {
                        recoveryTable[node.State.Index, edge_int] = recovery_items;
                    }
                }
            }
        }
예제 #4
0
        private static IEnumerable <Production <SYMBOL_ENUM, TREE_NODE> > unfoldIdentityProductions(StringRep <SYMBOL_ENUM> symbolsRep,
                                                                                                    IEnumerable <Production <SYMBOL_ENUM, TREE_NODE> > productions,
                                                                                                    SYMBOL_ENUM eofSymbol, SYMBOL_ENUM syntaxErrorSymbol)
        {
            var used_aliases = new HashSet <SYMBOL_ENUM>();

            while (true)
            {
                var result = new Productions <SYMBOL_ENUM, TREE_NODE>(symbolsRep, productions, eofSymbol, syntaxErrorSymbol, s => {});
                List <Production <SYMBOL_ENUM, TREE_NODE> > new_productions = null;

                CoverSets <SYMBOL_ENUM> cover_sets = new BuilderCoverSets <SYMBOL_ENUM, TREE_NODE>(result, lookaheadWidth: 1).ComputeCoverSets();
                foreach (SYMBOL_ENUM non_term in result.NonTerminals)
                {
                    if (!used_aliases.Contains(non_term) &&
                        !cover_sets.IsRecursive(non_term) &&
                        result.isAlias(non_term))
                    {
                        used_aliases.Add(non_term);

                        // todo: this won't work for multi-param alias rules
                        SYMBOL_ENUM[] expansions = result.FilterByLhs(non_term).Select(it => it.RhsSymbols.Single()).ToArray();
                        new_productions = new List <Production <SYMBOL_ENUM, TREE_NODE> >();
                        UnAliasing change = UnAliasing.NoChange;
                        foreach (Production <SYMBOL_ENUM, TREE_NODE> p in productions.Where(it => !it.LhsNonTerminal.Equals(non_term)))
                        {
                            new_productions.AddRange(
                                unAliasProductionRhs(non_term, expansions, p, result.StartSymbol, syntaxErrorSymbol, ref change)
                                .Select(rhs => new Production <SYMBOL_ENUM, TREE_NODE>(
                                            symbolsRep,
                                            p.LhsNonTerminal,
                                            p.Recursive,
                                            rhs,
                                            p.UserAction,
                                            // todo: this won't work for multi-param alias rules
                                            p.IdentityOuterFunctionParamIndex)));

                            // we need to break right-away, otherwise "change" variable could be changed in next expansion to "expanded"
                            if (change == UnAliasing.Forbidden)
                            {
                                break;
                            }
                        }

                        if (change == UnAliasing.Expansion)
                        {
                            break;
                        }
                        else
                        {
                            new_productions = null;
                        }
                    }
                }


                // all non terminals checked or productions were expanded
                if (new_productions == null)
                {
                    break;
                }
                else
                {
                    productions = new_productions;
                }
            }

            return(productions);
        }
예제 #5
0
        private static IEnumerable <Production <SYMBOL_ENUM, TREE_NODE> > unfoldErrorProductions_NOT_USED(StringRep <SYMBOL_ENUM> symbolsRep,
                                                                                                          IEnumerable <Production <SYMBOL_ENUM, TREE_NODE> > productions,
                                                                                                          SYMBOL_ENUM eofSymbol, SYMBOL_ENUM syntaxErrorSymbol)
        {
            var result          = new Productions <SYMBOL_ENUM, TREE_NODE>(symbolsRep, productions, eofSymbol, syntaxErrorSymbol, s => { });
            var new_productions = new List <Production <SYMBOL_ENUM, TREE_NODE> >();

            // compute all non-terminals that serve as aliases for terminals
            // alias is a symbol that can be substituted by single terminal, for example
            // a := A | B (this is alias)
            // a := A B (this is not an alias)
            // we start with terminals, because they serve as aliases too (to themselves)
            DynamicDictionary <SYMBOL_ENUM, HashSet <SYMBOL_ENUM> > term_aliases = result.Terminals
                                                                                   .Select(it => Tuple.Create(it, new HashSet <SYMBOL_ENUM>(new[] { it })))
                                                                                   .ToDefaultDynamicDictionary();

            // this is not cover set algorithm!
            while (true)
            {
                int count = term_aliases.Count;

                foreach (SYMBOL_ENUM non_term in result.NonTerminals.Where(it => !term_aliases.ContainsKey(it)))
                {
                    bool found = true;

                    foreach (Production <SYMBOL_ENUM, TREE_NODE> prod in result.FilterByLhs(non_term))
                    {
                        if (prod.RhsSymbols.Count != 1 || !term_aliases.ContainsKey(prod.RhsSymbols.Single()))
                        {
                            found = false;
                            break;
                        }
                    }

                    if (found)
                    {
                        term_aliases[non_term].AddRange(result.FilterByLhs(non_term).Select(it => term_aliases[it.RhsSymbols.Single()]).Flatten());
                    }
                }

                if (count == term_aliases.Count)
                {
                    break;
                }
            }

            // check the placement of error token in every error production
            foreach (Production <SYMBOL_ENUM, TREE_NODE> prod in productions)
            {
                IEnumerable <SYMBOL_ENUM> error_symbols = prod.RhsSymbols.Where(it => it.Equals(result.SyntaxErrorSymbol));
                if (error_symbols.Any())
                {
                    new_productions.Add(prod);
                }
                else if (error_symbols.Count() > 1)
                {
                    throw new ArgumentException("Only one syntax error token per production: " + prod.PositionDescription);
                }
                else
                {
                    int idx = prod.RhsSymbols.IndexOf(result.SyntaxErrorSymbol);
                    if (idx != prod.RhsSymbols.Count - 2)
                    {
                        throw new ArgumentException("Syntax error token has to be next to last: " + prod.PositionDescription);
                    }
                    SYMBOL_ENUM recovery_symbol = prod.RhsSymbols[idx + 1];
                    if (!term_aliases.ContainsKey(recovery_symbol))
                    {
                        throw new ArgumentException("There has to be a terminal or alias non-terminal after syntax error token: " + prod.PositionDescription);
                    }
                    else if (result.NonTerminals.Contains(recovery_symbol))
                    {
                        foreach (SYMBOL_ENUM term in term_aliases[recovery_symbol])
                        {
                            new_productions.Add(new Production <SYMBOL_ENUM, TREE_NODE>(
                                                    symbolsRep,
                                                    prod.LhsNonTerminal,
                                                    prod.Recursive,
                                                    prod.RhsSymbols.SkipTail(1).Concat(term), // replacing aliased terminal
                                                    prod.UserAction,
                                                    prod.IdentityOuterFunctionParamIndex));
                        }
                    }
                    else
                    {
                        new_productions.Add(prod);
                    }
                }
            }

            return(new_productions);
        }
예제 #6
0
        public ActionTable <SYMBOL_ENUM, TREE_NODE> FillActionTable(Productions <SYMBOL_ENUM, TREE_NODE> productions,
                                                                    FirstSets <SYMBOL_ENUM> firstSets,
                                                                    CoverSets <SYMBOL_ENUM> coverSets,
                                                                    HorizonSets <SYMBOL_ENUM> horizonSets,
                                                                    int lookaheadWidth,
                                                                    Dfa <SYMBOL_ENUM, TREE_NODE> dfa,
                                                                    PrecedenceTable <SYMBOL_ENUM> precedenceTable,
                                                                    GrammarReport <SYMBOL_ENUM, TREE_NODE> report)
        {
            this.coverSets       = coverSets;
            this.horizonSets     = horizonSets;
            this.report          = report;
            this.precedenceTable = precedenceTable ?? new PrecedenceTable <SYMBOL_ENUM>(productions.SymbolsRep);
            this.symbolsRep      = productions.SymbolsRep;
            actionTable          = new ActionTable <SYMBOL_ENUM, TREE_NODE>(dfa, productions,
                                                                            lookaheadWidth);

            foreach (Node <SYMBOL_ENUM, TREE_NODE> node in dfa.Nodes)
            {
                foreach (SymbolChunk <SYMBOL_ENUM> chunk in node.State.PossibleInputs)
                {
                    ParseAction <SYMBOL_ENUM, TREE_NODE> action_data = computeAction(node, chunk);

                    if (!report.HasGrammarErrors)
                    {
                        actionTable.Add(node.State.Index, chunk, new[] { action_data });
                    }
                }

                // checking recovery conflicts

                IEnumerable <SingleState <SYMBOL_ENUM, TREE_NODE> > recovery_items = node.State.ParsingActiveItems
                                                                                     .Where(it => it.IsAtRecoveryPoint);

                var recovery_stats = DynamicDictionary.CreateWithDefault <SYMBOL_ENUM, List <SingleState <SYMBOL_ENUM, TREE_NODE> > >();
                foreach (SingleState <SYMBOL_ENUM, TREE_NODE> rec_state in recovery_items)
                {
                    foreach (SymbolChunk <SYMBOL_ENUM> first in firstSets[rec_state.RecoveryMarkerSymbol].Chunks)
                    {
                        recovery_stats[first.Symbols.First()].Add(rec_state);
                    }
                }

                foreach (var pair in recovery_stats.Where(it => it.Value.Count > 1))
                {
                    report.AddError(pair.Value.Select(it => it.IndexStr), "Recovery item conflict on \"" + symbolsRep.Get(pair.Key) + "\".");
                }
            }

            report.AddWarnings(precedenceTable.GetUnusedEntries(symbolsRep));

            if (report.HasGrammarErrors)
            {
                return(null);
            }
            else
            {
                report.ActionTable = actionTable;
                return(actionTable);
            }
        }