/// <summary> /// Transforms grammar according to given grammar options. /// </summary> /// <param name="grammar">target grammar</param> /// <param name="options">set of options</param> /// <returns>application of options to grammar</returns> public static CFG ApplyOptions(this CFG grammar, OrderedHashSet <GrammarOption> options) { if (options.Contains(GrammarOption.Reversed)) { return(Reversed(grammar)); } return(grammar); }
public CFGSubtypingAPIGenerator(CFG grammar, string grammarName, bool generateFluentAPI) { if (!grammar.InGreibachNormalForm()) { throw new Exception("can generate API only for grammars in Greibach normal form"); } this.grammar = grammar; this.grammarName = grammarName; this.generateFluentAPI = generateFluentAPI; }
private void Execute(GeneratorExecutionContext context, AdditionalText file) { CFG grammar = CFGParser.Parse(file.GetText().ToString()); grammar = grammar.ApplyOptions(GetOptions(context, file)).ToGreibachNormalForm(); CFGSubtypingAPIGenerator generator = new CFGSubtypingAPIGenerator(grammar, Path.GetFileNameWithoutExtension(file.Path), GetBoolConfiguration("FluentAPI", context, file)); context.AddSource(generator.NamespaceName(), SourceText.From(generator.PrintAPI(), Encoding.UTF8)); }
private static bool HasEpsilonProductionsExceptStartVariable(this CFG grammar) { foreach (Production production in grammar.productions) { if (production.lhs != grammar.startVariable && production.IsEpsilonProduction()) { return(true); } } return(false); }
/// <summary> /// Reverses context-free grammar, by reversing each of its productions. /// </summary> /// <param name="grammar">target grammar</param> /// <returns>reversed grammar</returns> public static CFG Reversed(this CFG grammar) { OrderedHashSet <Production> productions = new OrderedHashSet <Production>(); foreach (Production production in grammar.productions) { string[] reversed = new string[production.rhs.Length]; Array.Copy(production.rhs, reversed, reversed.Length); Array.Reverse(reversed); productions.Add(new Production(production.lhs, reversed)); } return(new CFG(grammar.startVariable, productions)); }
/// <summary> /// Transforms grammar into Greibach normal form, i.e., /// where productions are of the form `v ::= t s1 s2...`, `v` a variable, /// `t` a terminal. /// The grammar's start variable may be nullable, `v0 ::= epsilon`. /// </summary> /// <param name="grammar">target grammar</param> /// <returns>grammar in Greibach normal form</returns> public static CFG ToGreibachNormalForm(this CFG grammar) { if (grammar.InGreibachNormalForm()) { return(grammar); } while (true) { grammar = grammar.RemoveEpsilonProductions().RemoveLeftRecursion(); OrderedHashSet <Production> productions = new OrderedHashSet <Production>(); foreach (Production production in grammar.productions) { if (production.IsEpsilonProduction() || grammar.Terminals().Contains(production.rhs[0])) { productions.Add(production); } else { string rhsStartVariable = production.rhs[0]; foreach (Production otherProduction in grammar.productions) { if (otherProduction.lhs == rhsStartVariable) { List <string> newRHS = new List <string>(); newRHS.AddRange(otherProduction.rhs); newRHS.AddRange(production.rhs.Skip(1)); productions.Add(new Production(production.lhs, newRHS.ToArray())); } } } } grammar = verifyConsistency(grammar, new CFG(grammar.startVariable, productions)); if (grammar.InGreibachNormalForm()) { return(grammar); } } }
/// <summary> /// Checks whether grammar is in Greibach normal form, i.e., /// where productions are of the form `v ::= t s1 s2...`, `v` a variable, /// `t` a terminal. /// The grammar's start variable may be nullable, `v0 ::= epsilon`. /// </summary> /// <param name="grammar">context-free grammar</param> /// <returns>whether grammar is in Greibach normal form</returns> public static bool InGreibachNormalForm(this CFG grammar) { bool hasNilStartVariable = false; foreach (Production production in grammar.productions) { if (production.IsEpsilonProduction()) { if (production.lhs == grammar.startVariable) { hasNilStartVariable = true; } else { return(false); } } else if (!grammar.Terminals().Contains(production.rhs[0])) { return(false); } } if (hasNilStartVariable) { foreach (Production production in grammar.productions) { foreach (string symbol in production.rhs) { if (grammar.startVariable == symbol) { return(false); } } } } return(true); }
/// <summary> /// Checks whether the grammar has a left-recursive production, i.e., /// of the form `v ::= v s1 s2...` (can also be indirect). /// </summary> /// <param name="grammar">context-free grammar</param> /// <returns>whether grammar has left-recursion</returns> public static bool HasLeftRecursion(this CFG grammar) { foreach (string variable in grammar.Variables()) { OrderedHashSet <string> reachableVariables = new OrderedHashSet <string>(); reachableVariables.Add(variable); int n = 0; while (reachableVariables.Count() > n) { n = reachableVariables.Count(); OrderedHashSet <string> newReachableVariables = new OrderedHashSet <string>(); foreach (string reachableVariable in reachableVariables) { foreach (Production production in grammar.productions) { if (production.lhs == reachableVariable && production.rhs.Length > 0) { string first = production.rhs[0]; if (first == variable) { return(true); } else if (grammar.Variables().Contains(first)) { newReachableVariables.Add(first); } } } } foreach (string newVariable in newReachableVariables) { reachableVariables.Add(newVariable); } } } return(false); }
/// <summary> /// Remove epsilon productions. /// If the grammar derives epsilon, then only the start variable may derive epsilon, but then it /// would not be found on the right-hand side of any production. /// </summary> /// <param name="grammar">target grammar</param> /// <returns>grammar without epsilon productions</returns> public static CFG RemoveEpsilonProductions(this CFG grammar) { bool hasNullableStartSymbolInRHS = grammar.HasNullableStartSymbolInRHS(); if (!grammar.HasEpsilonProductionsExceptStartVariable() && !hasNullableStartSymbolInRHS) { return(grammar); } if (hasNullableStartSymbolInRHS) { OrderedHashSet <Production> newProductions = new OrderedHashSet <Production>(); foreach (Production production in grammar.productions) { newProductions.Add(production); } string newStartVariable = GenerateAuxiliaryVariable(grammar.Symbols(), grammar.startVariable); newProductions.Add(new Production(newStartVariable, grammar.startVariable)); newProductions.Add(new Production(newStartVariable)); return(RemoveEpsilonProductions(new CFG(newStartVariable, newProductions))); } OrderedHashSet <Production> productions = grammar.productions; while (true) { OrderedHashSet <string> nullableVariables = new OrderedHashSet <string>(), nullVariables = new OrderedHashSet <string>(); foreach (Production production in productions) { if (production.IsEpsilonProduction()) { nullableVariables.Add(production.lhs); nullVariables.Add(production.lhs); } } foreach (Production production in productions) { if (!production.IsEpsilonProduction()) { nullVariables.Remove(production.lhs); } } if (nullableVariables.Count == 0 || nullableVariables.Count == 1 && nullableVariables.Contains(grammar.startVariable)) { return(verifyConsistency(grammar, new CFG(grammar.startVariable, productions).Simplify())); } OrderedHashSet <Production> newProductions = new OrderedHashSet <Production>(); foreach (Production production in productions) { OrderedHashSet <List <string> > rhss = new OrderedHashSet <List <string> >(); rhss.Add(new List <string>()); foreach (string symbol in production.rhs) { OrderedHashSet <List <string> > newRHSs = new OrderedHashSet <List <string> >(); foreach (var rhs in rhss) { if (nullVariables.Contains(symbol)) { newRHSs.AddUnique(rhs); } else if (!nullableVariables.Contains(symbol)) { List <string> newRHS = new List <string>(); newRHS.AddRange(rhs); newRHS.Add(symbol); newRHSs.Add(newRHS); } else { newRHSs.AddUnique(rhs); List <string> newRHS = new List <string>(); newRHS.AddRange(rhs); newRHS.Add(symbol); newRHSs.Add(newRHS); } } rhss = newRHSs; } foreach (var newRHS in rhss) { newProductions.Add(new Production(production.lhs, newRHS.ToArray())); } } foreach (string nullableVariable in nullableVariables) { if (nullableVariable != grammar.startVariable) { newProductions.Remove(new Production(nullableVariable)); } } productions = newProductions; } }
/// <summary> /// Removes left-recursive productions from the grammar. /// Implementation of Paull's algorithm. /// </summary> /// <param name="grammar">target grammar</param> /// <returns>grammar without left recursion</returns> public static CFG RemoveLeftRecursion(this CFG grammar) { if (!grammar.HasLeftRecursion()) { return(grammar); } grammar = grammar.Simplify().RemoveEpsilonProductions(); if (!grammar.HasLeftRecursion()) { return(grammar); } bool epsilonIsDerived = grammar.productions.Contains(new Production(grammar.startVariable)); OrderedHashSet <Production> productions = new OrderedHashSet <Production>(); foreach (Production production in grammar.productions) { productions.Add(production); } if (epsilonIsDerived) { productions.Remove(new Production(grammar.startVariable)); } List <string> variables = new List <string>(grammar.Variables()); OrderedHashSet <string> usedSymbols = new OrderedHashSet <string>(); foreach (string symbol in grammar.Symbols()) { usedSymbols.Add(symbol); } for (int i = 0; i < variables.Count; ++i) { string vi = variables[i]; for (int j = 0; j < i; ++j) { string vj = variables[j]; OrderedHashSet <Production> toRemove = new OrderedHashSet <Production>(); OrderedHashSet <Production> toAdd = new OrderedHashSet <Production>(); foreach (Production production in productions) { if (production.lhs == vi && production.rhs.Length > 0 && production.rhs[0] == vj) { toRemove.Add(production); foreach (Production otherProduction in productions) { if (otherProduction.lhs == vj) { List <string> newRHS = new List <string>(); newRHS.AddRange(otherProduction.rhs); newRHS.AddRange(production.rhs.Skip(1)); toAdd.Add(new Production(vi, newRHS.ToArray())); } } break; } } foreach (Production addedProduction in toAdd) { productions.Add(addedProduction); } foreach (Production removedProduction in toRemove) { productions.Remove(removedProduction); } } OrderedHashSet <Production> productionsWithoutViDirectLeftRecursion = new OrderedHashSet <Production>(); OrderedHashSet <Production> newViProductions = new OrderedHashSet <Production>(); foreach (Production production in productions) { if (production.lhs != vi) { productionsWithoutViDirectLeftRecursion.Add(production); } else { newViProductions.Add(production); } } foreach (Production newViProduction in RemoveDirectLeftRecursion(usedSymbols, newViProductions)) { productionsWithoutViDirectLeftRecursion.Add(newViProduction); } productions = productionsWithoutViDirectLeftRecursion; } if (epsilonIsDerived) { productions.Add(new Production(grammar.startVariable)); } return(verifyConsistency(grammar, new CFG(grammar.startVariable, productions))); }
public static void Main(String[] args) { var options = new SomeOptions(); Parser.Default.ParseArguments <SomeOptions>(args).WithParsed(parsed => options = parsed); if (options.Input == null || options.Name == null) { return; } CFG grammar = null; try { grammar = CFGParser.Parse(File.ReadAllText(options.Input)); } catch (Exception e) { Console.WriteLine($"Could not parse file {options.Input}:\n{e.Message}"); return; } try { if (options.Fluent) { grammar = grammar.Reversed(); } } catch (Exception e) { Console.WriteLine($"Error when reversing grammar:\n{e.Message}"); return; } try { grammar = grammar.ToGreibachNormalForm(); } catch (Exception e) { Console.WriteLine($"Error when converting grammar to Greibach normal form:\n{e.Message}"); return; } string result = null; try { result = new CFGSubtypingAPIGenerator(grammar, options.Name, options.Fluent).PrintAPI(); } catch (Exception e) { Console.WriteLine($"Error when generating API:\n{e.Message}"); return; } try { if (options.Output == null) { Console.WriteLine(result); } else { File.WriteAllText(options.Output, result); } } catch (Exception e) { Console.WriteLine($"Error when writing output:\n{e.Message}"); return; } }