protected void OnActionSelected(Token input, ActionRecord action) { Data.Grammar.OnActionSelected(this, _currentToken, action); if (ActionSelected != null) { ParserActionEventArgs args = new ParserActionEventArgs(this.CurrentState, input, action); ActionSelected(this, args); } }
}//methods private bool CheckConflictResolutionByPrecedence(ActionRecord action) { SymbolTerminal opTerm = SymbolTerminal.GetSymbol(action.Key); if (opTerm != null && opTerm.IsSet(TermOptions.IsOperator)) { action.ActionType = ParserActionType.Operator; action.ConflictResolved = true; return(true); } return(false); }
//TODO: need to rewrite, looks ugly private bool Recover() { //for recovery the current token must be error token, we rely on it if (!_currentToken.IsError()) { _currentToken = _context.CreateErrorToken(_currentToken.Location, _currentToken.Text); } //Check the current state and states in stack for error shift action - this would be recovery state. ActionRecord action = GetCurrentAction(); if (action == null || action.ActionType == ParserActionType.Reduce) { while (Stack.Count > 0) { _currentState = Stack.Top.State; Stack.Pop(1); action = GetCurrentAction(); if (action != null && action.ActionType != ParserActionType.Reduce) { break; //we found shift action for error token } }//while }//if if (action == null || action.ActionType == ParserActionType.Reduce) { return(false); //could not find shift action, cannot recover } //We found recovery state, and action contains ActionRecord for "error shift". Lets shift it. ExecuteShiftAction(action);//push the error token // Now shift all tokens from input that can be shifted. // These are the ones that are found in error production after the error. We ignore all other tokens // We stop when we find a state with reduce-only action. while (_currentToken.Terminal != Grammar.Eof) { //with current token, see if we can shift it. action = GetCurrentAction(); if (action == null) { NextToken(); //skip this token and continue reading input continue; } if (action.ActionType == ParserActionType.Reduce || action.ActionType == ParserActionType.Operator) { //we can reduce - let's reduce and return success - we recovered. ExecuteReduceAction(action); return(true); } //it is shift action, let's shift ExecuteShiftAction(action); }//while return(false); // }
} //method //Checks Reduce productions of an action for a ReduceThis hint. If found, the production is moved to the beginning of the list. private bool CheckReduceHint(ActionRecord action) { foreach (Production prod in action.ReduceProductions) { GrammarHint reduceHint = GetHint(prod, prod.RValues.Count, HintType.ReduceThis); if (reduceHint != null) { action.ReduceProductions.Remove(prod); action.ReduceProductions.Insert(0, prod); action.ActionType = ParserActionType.Reduce; action.ConflictResolved = true; return(true); } //if } //foreach prod return(false); } //method
//Checks shift items for PreferShift grammar hint. Hints are associated with a particular position // inside production, which is in fact an LR0 item. The LR0 item is available thru shiftItem.Core property. // If PreferShift hint found, moves the hint-owning shiftItem to the beginning of the list and returns true. private bool CheckShiftHint(ActionRecord action) { foreach (LRItem shiftItem in action.ShiftItems) { GrammarHint shiftHint = GetHint(shiftItem.Core.Production, shiftItem.Core.Position, HintType.PreferShift); if (shiftHint != null) { action.ActionType = ParserActionType.Shift; action.ShiftItems.Remove(shiftItem); action.ShiftItems.Insert(0, shiftItem); action.ConflictResolved = true; return(true); } //if } //foreach shiftItem return(false); } //method
private void CreateParserStates() { Data.States.Clear(); _stateHash = new ParserStateTable(); CreateInitialAndFinalStates(); string augmRootKey = Data.AugmentedRoot.Key; // Iterate through states (while new ones are created) and create shift transitions and new states for (int index = 0; index < Data.States.Count; index++) { ParserState state = Data.States[index]; AddClosureItems(state); //Get keys of all possible shifts ShiftTable shiftTable = GetStateShifts(state); //Each key in shifts dict is an input element // Value is LR0ItemList of shifted LR0Items for this input element. foreach (string input in shiftTable.Keys) { LR0ItemList shiftedCoreItems = shiftTable[input]; ParserState newState = FindOrCreateState(shiftedCoreItems); ActionRecord newAction = new ActionRecord(input, ParserActionType.Shift, newState, null); state.Actions[input] = newAction; //link original LRItems in original state to derived LRItems in newState foreach (LR0Item coreItem in shiftedCoreItems) { LRItem fromItem = FindItem(state, coreItem.Production, coreItem.Position - 1); LRItem toItem = FindItem(newState, coreItem.Production, coreItem.Position); if (!fromItem.PropagateTargets.Contains(toItem)) { fromItem.PropagateTargets.Add(toItem); } //copy hints from core items into the newAction newAction.ShiftItems.Add(fromItem); } //foreach coreItem } //foreach input } //for index Data.FinalState = Data.InitialState.Actions[augmRootKey].NewState; } //method
private ActionRecord GetCurrentAction() { ActionRecord action = null; if (_currentToken.MatchByValue) { string key = CurrentToken.Text; if (!_caseSensitive) { key = key.ToLower(); } if (_currentState.Actions.TryGetValue(key, out action)) { return(action); } } if (_currentToken.MatchByType && _currentState.Actions.TryGetValue(_currentToken.Terminal.Key, out action)) { return(action); } return(null); //action not found }
public ParserActionEventArgs(ParserState state, Token input, ActionRecord action) { State = state; Input = input; Action = action; }
}//method private AstNode CreateNode(ActionRecord reduceAction, SourceSpan sourceSpan, AstNodeList childNodes) { NonTerminal nt = reduceAction.NonTerminal; AstNode result; NodeArgs nodeArgs = new NodeArgs(_context, nt, sourceSpan, childNodes); if (nt.NodeCreator != null) { result = nt.NodeCreator(nodeArgs); if (result != null) { return(result); } } Type defaultNodeType = _context.Compiler.Grammar.DefaultNodeType; Type ntNodeType = nt.NodeType ?? defaultNodeType ?? typeof(AstNode); // Check if NonTerminal is a list // List nodes are produced by .Plus() or .Star() methods of BnfElement // In this case, we have a left-recursive list formation production: // ntList -> ntList + delim? + ntElem // We check if we have already created the list node for ntList (in the first child); // if yes, we use this child as a result directly, without creating new list node. // The other incoming child - the last one - is a new list member; // we simply add it to child list of the result ntList node. Optional "delim" node is simply thrown away. bool isList = nt.IsSet(TermOptions.IsList); if (isList && childNodes.Count > 1 && childNodes[0].Term == nt) { result = childNodes[0]; AstNode newChild = childNodes[childNodes.Count - 1]; newChild.Parent = result; result.ChildNodes.Add(newChild); return(result); } //Check for StarList produced by MakeStarRule; in this case the production is: ntList -> Empty | Elem+ // where Elem+ is non-empty list of elements. The child list we are actually interested in is one-level lower if (nt.IsSet(TermOptions.IsStarList) && childNodes.Count == 1) { childNodes = childNodes[0].ChildNodes; } // Check for "node-bubbling" case. For identity productions like // A -> B // the child node B is usually a subclass of node A, // so child node B can be used directly in place of the A. So we simply return child node as a result. // TODO: probably need a grammar option to enable/disable this behavior explicitly bool canBubble = (Data.Grammar.FlagIsSet(LanguageFlags.BubbleNodes)) && !isList && !nt.IsSet(TermOptions.IsPunctuation) && childNodes.Count == 1 && (childNodes[0].Term is NonTerminal); if (canBubble) { NonTerminal childNT = childNodes[0].Term as NonTerminal; Type childNodeType = childNT.NodeType ?? defaultNodeType ?? typeof(AstNode); if (childNodeType == ntNodeType || childNodeType.IsSubclassOf(ntNodeType)) { return(childNodes[0]); } } // Try using Grammar's CreateNode method result = Data.Grammar.CreateNode(_context, reduceAction, sourceSpan, childNodes); if (result != null) { return(result); } //Finally create node directly. For perf reasons we try using "new" for AstNode type (faster), and // activator for all custom types (slower) if (ntNodeType == typeof(AstNode)) { return(new AstNode(nodeArgs)); } // if (ntNodeType.GetConstructor(new Type[] {typeof(AstNodeList)}) != null) // return (AstNode)Activator.CreateInstance(ntNodeType, childNodes); if (ntNodeType.GetConstructor(new Type[] { typeof(NodeArgs) }) != null) { return((AstNode)Activator.CreateInstance(ntNodeType, nodeArgs)); } //The following should never happen - we check that constructor exists when we validate grammar. string msg = string.Format( @"AST Node class {0} does not have a constructor for automatic node creation. Provide a constructor with a single NodeArgs parameter, or use NodeCreator delegate property in NonTerminal.", ntNodeType); throw new GrammarErrorException(msg); }
private void ExecuteReduceAction(ActionRecord action) { ParserState oldState = _currentState; int popCnt = action.PopCount; //Get new node's child nodes - these are nodes being popped from the stack AstNodeList childNodes = new AstNodeList(); for (int i = 0; i < action.PopCount; i++) { AstNode child = Stack[Stack.Count - popCnt + i].Node; if (child.Term.IsSet(TermOptions.IsPunctuation)) { continue; } //Transient nodes - don't add them but add their childrent directly to grandparent if (child.Term.IsSet(TermOptions.IsTransient)) { foreach (AstNode grandChild in child.ChildNodes) { childNodes.Add(grandChild); } continue; } //Add normal child childNodes.Add(child); } //recover state, location and pop the stack SourceSpan newNodeSpan; if (popCnt == 0) { newNodeSpan = new SourceSpan(_currentToken.Location, 0); } else { SourceLocation firstPopLoc = Stack[Stack.Count - popCnt].Node.Location; int lastPopEndPos = Stack[Stack.Count - 1].Node.Span.EndPos; newNodeSpan = new SourceSpan(firstPopLoc, lastPopEndPos - firstPopLoc.Position); _currentState = Stack[Stack.Count - popCnt].State; Stack.Pop(popCnt); } //Create new node AstNode node = CreateNode(action, newNodeSpan, childNodes); action.NonTerminal.OnNodeCreated(node); // Push node/current state into the stack Stack.Push(node, _currentState); //switch to new state ActionRecord gotoAction; if (_currentState.Actions.TryGetValue(action.NonTerminal.Key, out gotoAction)) { _currentState = gotoAction.NewState; } else { //should never happen throw new CompilerException(string.Format("Cannot find transition for input {0}; state: {1}, popped state: {2}", action.NonTerminal, oldState, _currentState)); } }//method
private void ExecuteShiftAction(ActionRecord action) { Stack.Push(_currentToken, _currentState); _currentState = action.NewState; NextToken(); }
}//method public AstNode Parse(CompilerContext context, IEnumerable <Token> tokenStream) { _context = context; _caseSensitive = _context.Compiler.Grammar.CaseSensitive; Reset(); _input = tokenStream.GetEnumerator(); NextToken(); while (true) { if (_currentState == Data.FinalState) { AstNode result = Stack[0].Node; //Check transient status if (result.Term.IsSet(TermOptions.IsTransient) && result.ChildNodes.Count == 1) { result = result.ChildNodes[0]; } Stack.Reset(); return(result); } //check for scammer error if (_currentToken.IsError()) { if (!Recover()) { return(null); } continue; } //Get action ActionRecord action = GetCurrentAction(); if (action == null) { ReportParseError(); if (!Recover()) { return(null); //did not recover } continue; }//action==null if (action.HasConflict()) { action = (ActionRecord)Data.Grammar.OnActionConflict(this, _currentToken, action); } this.OnActionSelected(_currentToken, action); switch (action.ActionType) { case ParserActionType.Operator: if (GetActionTypeForOperation(_currentToken) == ParserActionType.Shift) { goto case ParserActionType.Shift; } else { goto case ParserActionType.Reduce; } case ParserActionType.Shift: ExecuteShiftAction(action); break; case ParserActionType.Reduce: ExecuteReduceAction(action); break; } //switch } //while } //Parse