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