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); } }
//TODO: need to rewrite, looks ugly private bool Recover() { if (_currentToken.Category != TokenCategory.Error) { _currentToken = Grammar.CreateSyntaxErrorToken(_context, _currentToken.Location, "Syntax error."); } //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); // }
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)) { 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); // 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 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; }
public virtual ActionRecord OnActionConflict(Parser parser, Token input, ActionRecord action) { return(action); }
public virtual void OnActionSelected(Parser parser, Token input, ActionRecord action) { }
// Override this method in language grammar if you want a custom node creation mechanism. public virtual AstNode CreateNode(CompilerContext context, ActionRecord reduceAction, SourceSpan sourceSpan, AstNodeList childNodes) { return(null); }
}//method private AstNode CreateNode(ActionRecord reduceAction, SourceSpan sourceSpan, AstNodeList childNodes) { NonTerminal nt = reduceAction.NonTerminal; AstNode result; AstNodeArgs args = new AstNodeArgs(nt, _context, sourceSpan, childNodes); result = nt.InvokeNodeCreator(args); 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 MakeStarList; 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 if (!isList && !nt.IsSet(TermOptions.IsPunctuation) && childNodes.Count == 1) { Type childNodeType = childNodes[0].Term.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) { //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)) { result = new AstNode(args); } else #if PocketPC || SILVERLIGHT { ConstructorInfo ctor = ntNodeType.GetConstructor(new Type[] { typeof(AstNodeArgs) }); if (ctor == null) { throw new Exception("Failed to located constructor: " + ntNodeType.ToString() + "(AstNodeArgs args)"); } result = (AstNode)ctor.Invoke(new object[] { args }); } #else { result = (AstNode)Activator.CreateInstance(ntNodeType, args); } #endif } if (result != null) { nt.OnNodeCreated(result); } return(result); }
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; Stack.Reset(); return(result); } //check for scammer error if (_currentToken.Terminal.Category == TokenCategory.Error) { ReportScannerError(); if (!Recover()) { return(null); } continue; } //Get action ActionRecord action = GetCurrentAction(); if (action == null) { ReportParserError(); if (!Recover()) { return(null); //did not recover } continue; }//action==null if (action.HasConflict()) { action = 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