/// <summary> /// Gets possible inputs that allows for reaching the specified state from state 0 /// </summary> /// <param name="state">A LR state</param> /// <returns>A list of possible inputs for reaching the specified state</returns> public List <Phrase> GetInputsFor(State state) { List <Phrase> result = new List <Phrase>(); // for all paths from state 0 to the specified state foreach (ENode start in GetPathsTo(state)) { Phrase input = new Phrase(); ENode node = start; while (node.next != null) { Terminal terminal = node.transition as Terminal; if (terminal != null) { // this is terminal => simply add it to the input input.Append(terminal); } else { // this is a variable => try to decompose it BuildInput(input, node.transition as Variable, new Stack <RuleChoice>()); } node = node.next; } result.Add(input); } return(result); }
/// <summary> /// Builds the input by decomposing the given variable /// </summary> /// <param name="sample">The input sample to build</param> /// <param name="var">The variable to decompose</param> /// <param name="stack">The stack of rule definitions currently in use for the decomposition</param> /// <remarks> /// This methods recursively triggers the production of encoutered variables to arrive to the terminal symbols. /// The methods also tries do not go into an infinite loop by keeping track of the rule definitions that are currently used. /// If a rule definition has already been used (is in the stack) the method avoids it /// </remarks> private void BuildInput(Phrase sample, Variable var, Stack <RuleChoice> stack) { // TODO: there is a bug in this method, it may call itself forever // if the variable to decompose is nullable (epsilon is in the FIRSTS state), stop here if (var.Firsts.Contains(Epsilon.Instance)) { return; } // builds the list of possible rule definition for the variable List <RuleChoice> definitions = new List <RuleChoice>(); foreach (Rule rule in var.Rules) { definitions.Add(rule.Body.Choices[0]); } // try to find a rule definition that is not already used (in the stack) RuleChoice def = null; foreach (RuleChoice d in definitions) { if (!stack.Contains(d)) { // if it is not in the stack of rule definition // select it and stop def = d; break; } } // if all identified rule definitions are in the stack (currently in use) // f**k it, just use the first one ... if (def == null) { def = definitions[0]; } RuleChoice[] stackElements = stack.ToArray(); // push the rule definition to use onto the stack stack.Push(def); // walk the rule definition to build the sample for (int i = 0; i != def.Length; i++) { RuleBodyElement part = def[i]; if (part.Symbol is Terminal) { // easy, just add it to the sample sample.Append(part.Symbol as Terminal); } else { bool symbolFound = false; // TODO: cleanup this code, this is really not a nice patch!! // TODO: this is surely ineficient, but I don't understand the algorithm to do better yet // The idea is to try and avoid infinite recursive call by checking the symbol was not already processed before // This code checks whether the variable in this part is not already in one of the rule definition currently in the stack foreach (RuleChoice definition in stackElements) { // for all elements for (int j = 0; j != definition.Length; j++) { RuleBodyElement definitionPart = definition[j]; if (definitionPart.Symbol.Equals(part.Symbol)) { symbolFound = true; } } } // if part.Symbol is not the same as another part.symbol found in a previous definition if (!symbolFound) { BuildInput(sample, part.Symbol as Variable, stack); } // TODO: what do we do if the variable was found? } } stack.Pop(); }
/// <summary> /// Build the specified grammar /// </summary> /// <param name="method">The parsing method to use</param> /// <returns>The resulting LR graph, or <c>null</c> if it could not be generated</returns> public Graph Build(ParsingMethod method) { // build the graph switch (method) { case ParsingMethod.LR0: graph = GetGraphLR0(); break; case ParsingMethod.LR1: graph = GetGraphLR1(); break; case ParsingMethod.LALR1: graph = GetGaphLALR1(); break; case ParsingMethod.RNGLR1: graph = GetGraphRNGLR1(); break; case ParsingMethod.RNGLALR1: graph = GetGraphRNGLALR1(); break; } // builds the set of conflicts inverse = new GraphInverse(graph); foreach (State state in graph.States) { if (state.Conflicts.Count != 0) { List <Phrase> samples = inverse.GetInputsFor(state); foreach (Conflict conflict in state.Conflicts) { foreach (Phrase sample in samples) { Phrase temp = new Phrase(sample); temp.Append(conflict.ConflictSymbol); conflict.AddExample(temp); } conflicts.Add(conflict); } } List <List <State> > paths = null; foreach (Symbol symbol in state.Transitions) { Terminal terminal = symbol as Terminal; if (terminal == null) { continue; } if (terminal.Context == 0) { continue; } // this is a contextual terminal, can we reach this state without the right context being available if (paths == null) { paths = inverse.GetStatePathsTo(state); } foreach (List <State> path in paths) { path.Add(state); // append this state bool found = false; for (int i = 0; i != path.Count - 1; i++) { State element = path[i]; foreach (Item item in element.Items) { if (item.DotPosition == 0 && item.BaseRule.Context == terminal.Context) { // this is the opening of a context only if we are not going to the next state using the associated variable found |= !state.HasTransition(item.BaseRule.Head) || state.GetChildBy(item.BaseRule.Head) != path[i + 1]; break; } } if (found) { break; } } foreach (Item item in state.Items) { if (item.DotPosition == 0 && item.BaseRule.Context == terminal.Context) { found = true; break; } } if (!found) { // this is problematic path ContextualError error = new ContextualError(state); foreach (Item item in state.Items) { if (item.Action == LRActionCode.Shift && item.GetNextSymbol() == terminal) { error.AddItem(item); } } errors.Add(error); } } } } return(graph); }