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