Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
CoreParser_ErrorHandling.cs
Go to the documentation of this file.
1 #region License
2 /* **********************************************************************************
3  * Copyright (c) Roman Ivantsov
4  * This source code is subject to terms and conditions of the MIT License
5  * for Irony. A copy of the license can be found in the License.txt file
6  * at the root of this distribution.
7  * By using this source code in any fashion, you are agreeing to be bound by the terms of the
8  * MIT License.
9  * You must not remove this notice from this software.
10  * **********************************************************************************/
11 #endregion
12 
13 using System;
14 using System.Collections.Generic;
15 using System.Linq;
16 using System.Text;
17 
18 //Error handling methods of CoreParser class
19 namespace Irony.Parsing {
20  public partial class CoreParser {
21 
22  private void ProcessParserError() {
23  Context.Status = ParserStatus.Error;
24  _grammar.ReportParseError(this.Context);
25  if (Context.Mode != ParseMode.CommandLine)
26  TryRecoverFromError();
27  }
28 
29 
30  private bool TryRecoverFromError() {
31  if (Context.CurrentParserInput.Term == _grammar.Eof)
32  return false; //do not recover if we're already at EOF
33  Context.Status = ParserStatus.Recovering;
34  Context.AddTrace(Resources.MsgTraceRecovering); // *** RECOVERING - searching for state with error shift ***
35  var recovered = TryRecoverImpl();
36  string msg = (recovered ? Resources.MsgTraceRecoverSuccess : Resources.MsgTraceRecoverFailed);
37  Context.AddTrace(msg); //add new trace entry
38  Context.Status = recovered? ParserStatus.Parsing : ParserStatus.Error;
39  return recovered;
40  }
41 
42  private bool TryRecoverImpl() {
43  //1. We need to find a state in the stack that has a shift item based on error production (with error token),
44  // and error terminal is current. This state would have a shift action on error token.
45  ParserAction nextAction = FindErrorShiftActionInStackTemp();
46 
47  if (nextAction == null) return false;
48 
49  var firstBnfTerm = nextAction.NewState.Actions.Keys.FirstOrDefault();
50 
51  Context.AddTrace(Resources.MsgTraceRecoverReducing);
52  Context.AddTrace(Resources.MsgTraceRecoverAction, nextAction);
53 
54  // Inject faked node
55  var newLineNode = new ParseTreeNode(firstBnfTerm);
56  Context.ParserInputStack.Insert(0, newLineNode);
57  var saveParserInput = Context.CurrentParserInput;
58  Context.CurrentParserInput = newLineNode;
59 
60  nextAction = FindActionForStateAndInput();
61 
62  while (nextAction != null && Context.CurrentParserInput != null)
63  {
64  switch (nextAction.ActionType)
65  {
66  case ParserActionType.Shift:
67  ExecuteShift(nextAction);
68  break;
69  case ParserActionType.Operator:
70  ExecuteOperatorAction(nextAction);
71  break;
72  case ParserActionType.Reduce:
73  ExecuteReduce(nextAction);
74  break;
75  case ParserActionType.Code:
76  ExecuteConflictAction(nextAction);
77  break;
78  case ParserActionType.Accept:
79  ExecuteAccept(nextAction);
80  break;
81  }
82  nextAction = FindActionForStateAndInput();
83  }
84 
85  Context.ParserInputStack.RemoveAt(0);
86  Context.CurrentParserInput = saveParserInput;
87 
88  if (!Context.CurrentParserState.Actions.TryGetValue(Context.CurrentParserInput.Term, out nextAction))
89  {
90  Context.ParserInputStack.Clear();
91  Context.CurrentParserInput = null;
92  }
93 
94  return true;
95  //ExecuteShiftTemp(firstBnfTerm, nextAction);
96 
97 /*
98  var action = GetReduceActionInCurrentState();
99  if (action != null)
100  {
101  //Clear all input token queues and buffered input, reset location back to input position token queues;
102  //Reduce error production - it creates parent non-terminal that "hides" error inside
103  ExecuteReduce(action);
104  return true; //we recovered
105  }
106  return true;
107 
108  ParserAction errorShiftAction = FindErrorShiftActionInStack();
109  if (errorShiftAction == null) return false; //we failed to recover
110  Context.AddTrace(Resources.MsgTraceRecoverFoundState, Context.CurrentParserState);
111  //2. Shift error token - execute shift action
112  Context.AddTrace(Resources.MsgTraceRecoverShiftError, errorShiftAction);
113  ExecuteShift(errorShiftAction);
114  //4. Now we need to go along error production until the end, shifting tokens that CAN be shifted and ignoring others.
115  // We shift until we can reduce
116  Context.AddTrace(Resources.MsgTraceRecoverShiftTillEnd);
117  while (true) {
118  if (Context.CurrentParserInput == null)
119  ReadInput();
120  if (Context.CurrentParserInput.Term == _grammar.Eof)
121  return false;
122  //Check if we can reduce
123  action = GetReduceActionInCurrentState();
124  if (action != null) {
125  //Clear all input token queues and buffered input, reset location back to input position token queues;
126  Context.SetSourceLocation(Context.CurrentParserInput.Span.Location);
127  Context.ParserInputStack.Clear();
128  Context.CurrentParserInput = null;
129 
130  //Reduce error production - it creates parent non-terminal that "hides" error inside
131  Context.AddTrace(Resources.MsgTraceRecoverReducing);
132  Context.AddTrace(Resources.MsgTraceRecoverAction, action);
133  ExecuteReduceOnError(action);
134  return true; //we recovered
135  }
136  //No reduce action in current state. Try to shift current token or throw it away or reduce
137  action = GetShiftActionInCurrentState();
138  if(action != null)
139  ExecuteShift(action); //shift input token
140  else //simply read input
141  ReadInput();
142  }
143  */
144  }//method
145 
146  private void ExecuteShiftTemp(BnfTerm term, ParserAction action)
147  {
148  Context.ParserStack.Push(new ParseTreeNode(term), action.NewState);
149  Context.CurrentParserState = action.NewState;
150  }
151 
152 
153  // --> START EDIT ALEX
154  private void ExecuteReduceOnError(ParserAction action)
155  {
156  var reduceProduction = action.ReduceProduction;
157  //final reduce actions ----------------------------------------------------------
158  Context.ParserStack.Pop(1);
159  //Push new node into stack and move to new state
160  //First read the state from top of the stack
161  Context.CurrentParserState = Context.ParserStack.Top.State;
162  if (_traceEnabled) Context.AddTrace(Resources.MsgTracePoppedState, reduceProduction.LValue.Name);
163  // Shift to new state (LALR) - execute shift over non-terminal
164  var shift = Context.CurrentParserState.Actions[reduceProduction.LValue];
165  Context.ParserStack.Push(new ParseTreeNode(action.ReduceProduction.LValue), shift.NewState);
166  Context.CurrentParserState = shift.NewState;
167  }
168  // --> END EDIT ALEX
169 
170  public void ResetLocationAndClearInput(SourceLocation location, int position) {
171  Context.CurrentParserInput = null;
172  Context.ParserInputStack.Clear();
173  Context.SetSourceLocation(location);
174  }
175 
176  private ParserAction FindErrorShiftActionInStack() {
177  while (Context.ParserStack.Count >= 1) {
178  ParserAction errorShiftAction;
179  if (Context.CurrentParserState.Actions.TryGetValue(_grammar.SyntaxError, out errorShiftAction) && errorShiftAction.ActionType == ParserActionType.Shift)
180  return errorShiftAction;
181  //pop next state from stack
182  if (Context.ParserStack.Count == 1)
183  return null; //don't pop the initial state
184  Context.ParserStack.Pop();
185  Context.CurrentParserState = Context.ParserStack.Top.State;
186  }
187  return null;
188  }
189 
190  private ParserAction FindErrorShiftActionInStackTemp()
191  {
192  for (int i = Context.ParserStack.Count - 1; i >= 0; i--)
193  {
194  ParserAction errorShiftAction;
195  var currentState = Context.ParserStack[i].State;
196 
197  if (currentState.Actions.TryGetValue(_grammar.SyntaxError, out errorShiftAction) && errorShiftAction.ActionType == ParserActionType.Shift)
198  {
199  return errorShiftAction;
200  }
201  }
202  return null;
203  }
204 
205  private ParserAction FindErrorShiftActionInStack(BnfTerm exitTerm)
206  {
207  while (Context.ParserStack.Count >= 1)
208  {
209  ParserAction errorShiftAction;
210  if (Context.CurrentParserState.Actions.TryGetValue(exitTerm, out errorShiftAction))
211  return errorShiftAction;
212  //pop next state from stack
213  if (Context.ParserStack.Count == 1)
214  return null; //don't pop the initial state
215  Context.ParserStack.Pop();
216  Context.CurrentParserState = Context.ParserStack.Top.State;
217  }
218  return null;
219  }
220 
221 
222 
223  private ParserAction GetReduceActionInCurrentState() {
224  if (Context.CurrentParserState.DefaultAction != null) return Context.CurrentParserState.DefaultAction;
225  foreach (var action in Context.CurrentParserState.Actions.Values)
226  {
227  // --> START EDIT ALEX. Force to reduce
228  if (action.NewState.DefaultAction != null && (action.NewState.DefaultAction.ActionType == ParserActionType.Reduce))
229  return action.NewState.DefaultAction;
230  // --> END EDIT ALEX
231  if (action.ActionType == ParserActionType.Reduce) return action;
232 
233  }
234  return null;
235  }
236 
237  private ParserAction GetShiftActionInCurrentState() {
238  ParserAction result = null;
239  if (Context.CurrentParserState.Actions.TryGetValue(Context.CurrentParserInput.Term, out result) ||
240  Context.CurrentParserInput.Token != null && Context.CurrentParserInput.Token.KeyTerm != null &&
241  Context.CurrentParserState.Actions.TryGetValue(Context.CurrentParserInput.Token.KeyTerm, out result))
242  if (result.ActionType == ParserActionType.Shift)
243  return result;
244  return null;
245  }
246 
247  #region comments
248  // Computes set of expected terms in a parser state. While there may be extended list of symbols expected at some point,
249  // we want to reorganize and reduce it. For example, if the current state expects all arithmetic operators as an input,
250  // it would be better to not list all operators (+, -, *, /, etc) but simply put "operator" covering them all.
251  // To achieve this grammar writer can group operators (or any other terminals) into named groups using Grammar's methods
252  // AddTermReportGroup, AddNoReportGroup etc. Then instead of reporting each operator separately, Irony would include
253  // a single "group name" to represent them all.
254  // The "expected report set" is not computed during parser construction (it would bite considerable time), but on demand during parsing,
255  // when error is detected and the expected set is actually needed for error message.
256  // Multi-threading concerns. When used in multi-threaded environment (web server), the LanguageData would be shared in
257  // application-wide cache to avoid rebuilding the parser data on every request. The LanguageData is immutable, except
258  // this one case - the expected sets are constructed late by CoreParser on the when-needed basis.
259  // We don't do any locking here, just compute the set and on return from this function the state field is assigned.
260  // We assume that this field assignment is an atomic, concurrency-safe operation. The worst thing that might happen
261  // is "double-effort" when two threads start computing the same set around the same time, and the last one to finish would
262  // leave its result in the state field.
263  #endregion
264  internal static StringSet ComputeGroupedExpectedSetForState(Grammar grammar, ParserState state) {
265  var terms = new TerminalSet();
266  terms.UnionWith(state.ExpectedTerminals);
267  var result = new StringSet();
268  //Eliminate no-report terminals
269  foreach(var group in grammar.TermReportGroups)
270  if (group.GroupType == TermReportGroupType.Exclude)
271  terms.ExceptWith(group.Terminals);
272  //Add normal and operator groups
273  foreach(var group in grammar.TermReportGroups)
274  if(group.GroupType == TermReportGroupType.Normal || group.GroupType == TermReportGroupType.Operator && terms.Overlaps(group.Terminals)) {
275  result.Add(group.Alias);
276  terms.ExceptWith(group.Terminals);
277  }
278  //Add remaining terminals "as is"
279  foreach(var terminal in terms)
280  result.Add(terminal.ErrorAlias);
281  return result;
282  }
283 
284 
285  }//class
286 }//namespace
static string MsgTracePoppedState
Looks up a localized string similar to Popped state from stack, pushing {0}.
static string MsgTraceRecovering
Looks up a localized string similar to RECOVERING: popping stack, looking for state with error shift...
void ResetLocationAndClearInput(SourceLocation location, int position)
static string MsgTraceRecoverFailed
Looks up a localized string similar to FAILED TO RECOVER.
static string MsgTraceRecoverAction
Looks up a localized string similar to RECOVERING: {0}.
static string MsgTraceRecoverReducing
Looks up a localized string similar to RECOVERING: Reached end of error production, reducing..