/// <summary> /// Reads the next <see cref="Terminal"/>. /// </summary> /// <returns>The next <see cref="Terminal"/> in the input text. If no <see cref="TerminalType"/> recognizes the input text, <value>null</value> is returned.</returns> public Terminal ReadTerminal() { Terminal result; if (_queuedTerminal != null) { result = _queuedTerminal; _queuedTerminal = null; } else { result = GetNextTerminal(); } LinguaTrace.TraceEvent(TraceEventType.Information, LinguaTraceId.ID_PARSE_READTOKEN, "{0}", result); return(result); }
/// <summary> /// Constructs a new <see cref="Parser"/> which can recoganize the specified <see cref="IGrammar"/>. /// </summary> /// <param name="grammar">The <see cref="IGrammar"/> to be recognized by the <see cref="Parser"/>.</param> /// <returns>A <see cref="ParserGeneratorResult"/> containing <see cref="Parser"/> and information pertaining to the /// success or failure of the generation process. /// </returns> public ParserGeneratorResult GenerateParser(IGrammar grammar) { List <ParserGeneratorParserConflict> conflicts = new List <ParserGeneratorParserConflict>(); List <GeneratorState> states = CreateStates(grammar); // Create a parser state for each generator state. // Dictionary <GeneratorState, ParserState> parserStates = new Dictionary <GeneratorState, ParserState>(); foreach (GeneratorState state in states) { parserStates.Add(state, new ParserState(state.Id)); } foreach (GeneratorState state in states) { LinguaTrace.TraceEvent(TraceEventType.Verbose, LinguaTraceId.ID_GENERATE_PROCESS_STATE, "{0}", state); List <GeneratorStateItem> items = new List <GeneratorStateItem>(state.Items); items.Sort(); // Construct the list of actions associated with the parser state. // Dictionary <TerminalType, ParserAction> actions = new Dictionary <TerminalType, ParserAction>(); Dictionary <ParserAction, GeneratorRuleItem> actionRules = new Dictionary <ParserAction, GeneratorRuleItem>(); foreach (GeneratorStateItem item in items) { LinguaTrace.TraceEvent(TraceEventType.Verbose, LinguaTraceId.ID_GENERATE_PROCESS_ITEM, "{0}", item); if (item.RuleItem.DotElement == null) { foreach (TerminalType terminal in item.RuleItem.Rule.Lhs.Follow) { LinguaTrace.TraceEvent(TraceEventType.Verbose, LinguaTraceId.ID_GENERATE_PROCESS_TERMINAL, "{0}", terminal); if (actions.ContainsKey(terminal)) { ParserGeneratorParserConflict conflict = new ParserGeneratorParserConflict( actionRules[actions[terminal]].ToString(), item.RuleItem.ToString()); LinguaTrace.TraceEvent(TraceEventType.Information, LinguaTraceId.ID_GENERATE_PROCESS_CONFLICT, "{0}", conflict); conflicts.Add(conflict); } else if (item.RuleItem.Rule.Lhs.IsStart && terminal.IsStop) { ParserAction action = new ParserActionAccept(item.RuleItem.Rule); LinguaTrace.TraceEvent(TraceEventType.Information, LinguaTraceId.ID_GENERATE_PROCESS_ACTION, "{0}", action); actions.Add(terminal, action); actionRules.Add(action, item.RuleItem); } else { ParserAction action = new ParserActionReduce(item.RuleItem.Rule); LinguaTrace.TraceEvent(TraceEventType.Information, LinguaTraceId.ID_GENERATE_PROCESS_ACTION, "{0}", action); actions.Add(terminal, action); actionRules.Add(action, item.RuleItem); } } } else if (item.RuleItem.DotElement.ElementType == LanguageElementTypes.Terminal) { TerminalType terminal = (TerminalType)item.RuleItem.DotElement; if (actions.ContainsKey(terminal)) { ParserGeneratorParserConflict conflict = new ParserGeneratorParserConflict( actionRules[actions[terminal]].ToString(), item.RuleItem.ToString()); LinguaTrace.TraceEvent(TraceEventType.Information, LinguaTraceId.ID_GENERATE_PROCESS_CONFLICT, "{0}", conflict); conflicts.Add(conflict); } else { ParserAction action = new ParserActionShift(parserStates[state.Transitions[terminal]]); LinguaTrace.TraceEvent(TraceEventType.Information, LinguaTraceId.ID_GENERATE_PROCESS_ACTION, "{0}", action); actions.Add(terminal, action); actionRules.Add(action, item.RuleItem); } } } // Construct the GOTO table // Dictionary <NonterminalType, ParserState> gotos = new Dictionary <NonterminalType, ParserState>(); foreach (KeyValuePair <LanguageElementType, GeneratorState> transition in state.Transitions) { if (transition.Key.ElementType == LanguageElementTypes.Nonterminal) { NonterminalType nonterminal = (NonterminalType)transition.Key; gotos.Add(nonterminal, parserStates[transition.Value]); } } // Update the parser state. // ParserState parserState = parserStates[state]; foreach (KeyValuePair <TerminalType, ParserAction> action in actions) { parserState.Actions.Add(action.Key, action.Value); } foreach (KeyValuePair <NonterminalType, ParserState> gotoItem in gotos) { parserState.Gotos.Add(gotoItem.Key, gotoItem.Value); } } Parser parser = new Parser(parserStates[states[0]]); ParserGeneratorResult result = new ParserGeneratorResult(parser, conflicts); return(result); }
private List <GeneratorState> CreateStates(IGrammar grammar) { List <GeneratorState> states = new List <GeneratorState>(); List <GeneratorState> unevaluatedStates = new List <GeneratorState>(); int stateId = 0; // Compute start state. // { HashSet <GeneratorStateItem> items = new HashSet <GeneratorStateItem>(); foreach (RuleType rule in grammar.StartNonterminal.Rules) { items.Add(new GeneratorStateItem(new GeneratorRuleItem(rule, 0))); } ComputeClosure(grammar, items); GeneratorState startState = new GeneratorState(stateId++, items); states.Add(startState); unevaluatedStates.Add(startState); } List <LanguageElementType> languageElements = new List <LanguageElementType>(); languageElements.AddRange(grammar.GetTerminals()); languageElements.AddRange(grammar.GetNonterminals()); while (unevaluatedStates.Count > 0) { // Remove one of the evaluated states and process it. // GeneratorState state = unevaluatedStates[0]; unevaluatedStates.RemoveAt(0); foreach (LanguageElementType languageElement in languageElements) { HashSet <GeneratorStateItem> items = state.Apply(languageElement); if (items != null) { ComputeClosure(grammar, items); GeneratorState toState = null; foreach (GeneratorState existingState in states) { if (existingState.Items.SetEquals(items)) { toState = existingState; break; } } if (toState == null) { toState = new GeneratorState(stateId++, items); states.Add(toState); unevaluatedStates.Add(toState); } state.Transitions.Add(languageElement, toState); } } } if (LinguaTrace.TraceSource.Switch.ShouldTrace(TraceEventType.Information)) { foreach (GeneratorState state in states) { LinguaTrace.TraceEvent(TraceEventType.Information, LinguaTraceId.ID_GENERATE_STATE, "{0}", state); } } return(states); }
/// <summary> /// Performs syntax analysis against a sequence of terminals according to the <see cref="Grammar"/> used to create the <see cref="Parser"/>. /// </summary> /// <param name="terminalReader">Retrieves a sequence of <see cref="Terminal"/> objects.</param> /// <returns>If syntax analysis succeeds, returns the <see cref="Nonterminal"/> associated with <see cref="Grammar.StartNonterminal"/>. Otherwise, <value>null</value> is returned.</returns> public Nonterminal Parse(ITerminalReader terminalReader) { var stack = new ParserStack(); stack.Push(null, InitialState); var terminal = terminalReader.ReadTerminal(); while (terminal != null) { if (terminal.ElementType.Ignore) { terminal = terminalReader.ReadTerminal(); } else { var action = stack.Peek().State.GetAction(terminal.ElementType); LinguaTrace.TraceEvent(TraceEventType.Information, LinguaTraceId.ID_PARSE_ACTION, "{0}", action); if (action == null) { return(null); } switch (action.ActionType) { case ParserActionTypes.Accept: { var reduce = (ParserActionAccept)action; var rule = reduce.Rule; var lhs = Reduce(stack, rule); return(lhs); } case ParserActionTypes.Shift: { var shift = (ParserActionShift)action; stack.Push(terminal, shift.State); terminal = terminalReader.ReadTerminal(); } break; case ParserActionTypes.Reduce: { var reduce = (ParserActionReduce)action; var rule = reduce.Rule; var lhs = Reduce(stack, rule); // Push the LHS nonterminal on the stack. // stack.Push(lhs, stack.Peek().State.GetGoto(lhs.ElementType)); } break; default: throw new InvalidOperationException(string.Format("Unrecognized action type {0}.", action.ActionType)); } } } return(null); }