antlr/runtime/CSharp/Antlr4.Runtime/DefaultErrorStrategy.cs

830 lines
36 KiB
C#
Raw Normal View History

2013-02-16 05:30:47 +08:00
/*
* [The "BSD license"]
* Copyright (c) 2013 Terence Parr
* Copyright (c) 2013 Sam Harwell
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using Antlr4.Runtime;
using Antlr4.Runtime.Atn;
using Antlr4.Runtime.Misc;
using Sharpen;
namespace Antlr4.Runtime
{
2013-02-16 22:14:20 +08:00
/// <summary>
/// This is the default implementation of
/// <see cref="IAntlrErrorStrategy">IAntlrErrorStrategy</see>
/// used for
/// error reporting and recovery in ANTLR parsers.
2013-02-16 22:14:20 +08:00
/// </summary>
public class DefaultErrorStrategy : IAntlrErrorStrategy
{
/// <summary>
/// Indicates whether the error strategy is currently "recovering from an
/// error".
2013-02-16 22:14:20 +08:00
/// </summary>
/// <remarks>
/// Indicates whether the error strategy is currently "recovering from an
/// error". This is used to suppress reporting multiple error messages while
/// attempting to recover from a detected syntax error.
2013-02-16 22:14:20 +08:00
/// </remarks>
2013-08-05 10:08:51 +08:00
/// <seealso cref="InErrorRecoveryMode(Parser)">InErrorRecoveryMode(Parser)</seealso>
2013-02-16 22:14:20 +08:00
protected internal bool errorRecoveryMode = false;
2013-02-16 05:30:47 +08:00
2013-02-16 22:14:20 +08:00
/// <summary>The index into the input stream where the last error occurred.</summary>
/// <remarks>
/// The index into the input stream where the last error occurred.
/// This is used to prevent infinite loops where an error is found
/// but no token is consumed during recovery...another error is found,
/// ad nauseum. This is a failsafe mechanism to guarantee that at least
/// one token/tree node is consumed for two errors.
/// </remarks>
protected internal int lastErrorIndex = -1;
2013-02-16 05:30:47 +08:00
2013-02-16 22:14:20 +08:00
protected internal IntervalSet lastErrorStates;
2013-02-16 05:30:47 +08:00
2013-08-05 10:08:51 +08:00
/// <summary>
/// <inheritDoc></inheritDoc>
/// <p>The default implementation simply calls
2013-08-05 10:08:51 +08:00
/// <see cref="EndErrorCondition(Parser)">EndErrorCondition(Parser)</see>
/// to
/// ensure that the handler is not in error recovery mode.</p>
2013-08-05 10:08:51 +08:00
/// </summary>
public virtual void Reset(Parser recognizer)
{
EndErrorCondition(recognizer);
}
/// <summary>
/// This method is called to enter error recovery mode when a recognition
/// exception is reported.
/// </summary>
/// <remarks>
/// This method is called to enter error recovery mode when a recognition
/// exception is reported.
/// </remarks>
/// <param name="recognizer">the parser instance</param>
protected internal virtual void BeginErrorCondition(Parser recognizer)
2013-02-16 22:14:20 +08:00
{
errorRecoveryMode = true;
}
2013-02-16 05:30:47 +08:00
2013-08-05 10:08:51 +08:00
/// <summary><inheritDoc></inheritDoc></summary>
2013-02-16 22:14:20 +08:00
public virtual bool InErrorRecoveryMode(Parser recognizer)
{
return errorRecoveryMode;
}
2013-02-16 05:30:47 +08:00
2013-08-05 10:08:51 +08:00
/// <summary>
/// This method is called to leave error recovery mode after recovering from
/// a recognition exception.
/// </summary>
/// <remarks>
/// This method is called to leave error recovery mode after recovering from
/// a recognition exception.
/// </remarks>
/// <param name="recognizer"></param>
protected internal virtual void EndErrorCondition(Parser recognizer)
2013-02-16 22:14:20 +08:00
{
errorRecoveryMode = false;
lastErrorStates = null;
lastErrorIndex = -1;
}
2013-02-16 05:30:47 +08:00
2013-08-05 10:08:51 +08:00
/// <summary>
/// <inheritDoc></inheritDoc>
/// <p>The default implementation simply calls
2013-08-05 10:08:51 +08:00
/// <see cref="EndErrorCondition(Parser)">EndErrorCondition(Parser)</see>
/// .</p>
2013-08-05 10:08:51 +08:00
/// </summary>
public virtual void ReportMatch(Parser recognizer)
{
EndErrorCondition(recognizer);
}
/// <summary>
/// <inheritDoc></inheritDoc>
/// <p>The default implementation returns immediately if the handler is already
2013-08-05 10:08:51 +08:00
/// in error recovery mode. Otherwise, it calls
/// <see cref="BeginErrorCondition(Parser)">BeginErrorCondition(Parser)</see>
/// and dispatches the reporting task based on the runtime type of
/// <code>e</code>
/// according to the following table.</p>
2013-08-05 10:08:51 +08:00
/// <ul>
/// <li>
/// <see cref="NoViableAltException">NoViableAltException</see>
/// : Dispatches the call to
2014-02-17 03:33:54 +08:00
/// <see cref="ReportNoViableAlternative(Parser, NoViableAltException)">ReportNoViableAlternative(Parser, NoViableAltException)</see>
2013-08-05 10:08:51 +08:00
/// </li>
/// <li>
/// <see cref="InputMismatchException">InputMismatchException</see>
/// : Dispatches the call to
2014-02-17 03:33:54 +08:00
/// <see cref="ReportInputMismatch(Parser, InputMismatchException)">ReportInputMismatch(Parser, InputMismatchException)</see>
2013-08-05 10:08:51 +08:00
/// </li>
/// <li>
/// <see cref="FailedPredicateException">FailedPredicateException</see>
/// : Dispatches the call to
2014-02-17 03:33:54 +08:00
/// <see cref="ReportFailedPredicate(Parser, FailedPredicateException)">ReportFailedPredicate(Parser, FailedPredicateException)</see>
2013-08-05 10:08:51 +08:00
/// </li>
/// <li>All other types: calls
2014-02-17 03:33:54 +08:00
/// <see cref="Parser.NotifyErrorListeners(string)">Parser.NotifyErrorListeners(string)</see>
2013-08-05 10:08:51 +08:00
/// to report
/// the exception</li>
/// </ul>
/// </summary>
2013-02-16 22:14:20 +08:00
public virtual void ReportError(Parser recognizer, RecognitionException e)
{
// if we've already reported an error and have not matched a token
// yet successfully, don't report any errors.
2013-08-05 10:08:51 +08:00
if (InErrorRecoveryMode(recognizer))
2013-02-16 22:14:20 +08:00
{
// System.err.print("[SPURIOUS] ");
return;
}
2013-08-05 10:08:51 +08:00
// don't report spurious errors
2013-02-16 22:14:20 +08:00
BeginErrorCondition(recognizer);
if (e is NoViableAltException)
{
ReportNoViableAlternative(recognizer, (NoViableAltException)e);
}
else
{
if (e is InputMismatchException)
{
ReportInputMismatch(recognizer, (InputMismatchException)e);
}
else
{
if (e is FailedPredicateException)
{
ReportFailedPredicate(recognizer, (FailedPredicateException)e);
}
else
{
2014-02-17 03:33:54 +08:00
System.Console.Error.WriteLine("unknown recognition error type: " + e.GetType().FullName);
2013-02-16 22:14:20 +08:00
NotifyErrorListeners(recognizer, e.Message, e);
}
}
}
}
2013-02-16 05:30:47 +08:00
2014-02-17 03:33:54 +08:00
protected internal virtual void NotifyErrorListeners(Parser recognizer, string message, RecognitionException e)
2013-02-16 22:14:20 +08:00
{
2013-08-05 10:08:51 +08:00
recognizer.NotifyErrorListeners(e.OffendingToken, message, e);
2013-02-16 22:14:20 +08:00
}
2013-02-16 05:30:47 +08:00
2013-08-05 10:08:51 +08:00
/// <summary>
/// <inheritDoc></inheritDoc>
/// <p>The default implementation resynchronizes the parser by consuming tokens
2013-08-05 10:08:51 +08:00
/// until we find one in the resynchronization set--loosely the set of tokens
/// that can follow the current rule.</p>
2013-08-05 10:08:51 +08:00
/// </summary>
2013-02-16 22:14:20 +08:00
public virtual void Recover(Parser recognizer, RecognitionException e)
{
// System.out.println("recover in "+recognizer.getRuleInvocationStack()+
// " index="+recognizer.getInputStream().index()+
// ", lastErrorIndex="+
// lastErrorIndex+
// ", states="+lastErrorStates);
2014-02-17 03:33:54 +08:00
if (lastErrorIndex == ((ITokenStream)recognizer.InputStream).Index && lastErrorStates != null && lastErrorStates.Contains(recognizer.State))
2013-02-16 22:14:20 +08:00
{
// uh oh, another error at same token index and previously-visited
// state in ATN; must be a case where LT(1) is in the recovery
// token set so nothing got consumed. Consume a single token
// at least to prevent an infinite loop; this is a failsafe.
// System.err.println("seen error condition before index="+
// lastErrorIndex+", states="+lastErrorStates);
// System.err.println("FAILSAFE consumes "+recognizer.getTokenNames()[recognizer.getInputStream().LA(1)]);
recognizer.Consume();
}
2013-02-17 05:23:03 +08:00
lastErrorIndex = ((ITokenStream)recognizer.InputStream).Index;
2013-02-16 22:14:20 +08:00
if (lastErrorStates == null)
{
lastErrorStates = new IntervalSet();
}
2013-02-17 05:23:03 +08:00
lastErrorStates.Add(recognizer.State);
2013-02-16 22:14:20 +08:00
IntervalSet followSet = GetErrorRecoverySet(recognizer);
ConsumeUntil(recognizer, followSet);
}
2013-02-16 05:30:47 +08:00
2013-02-16 22:14:20 +08:00
/// <summary>
2013-08-05 10:08:51 +08:00
/// The default implementation of
/// <see cref="IAntlrErrorStrategy.Sync(Parser)">IAntlrErrorStrategy.Sync(Parser)</see>
/// makes sure
/// that the current lookahead symbol is consistent with what were expecting
/// at this point in the ATN. You can call this anytime but ANTLR only
/// generates code to check before subrules/loops and each iteration.
/// <p>Implements Jim Idle's magic sync mechanism in closures and optional
/// subrules. E.g.,</p>
2013-08-05 10:08:51 +08:00
/// <pre>
/// a : sync ( stuff sync )* ;
/// sync : {consume to what can follow sync} ;
/// </pre>
/// At the start of a sub rule upon error,
/// <see cref="Sync(Parser)">Sync(Parser)</see>
/// performs single
/// token deletion, if possible. If it can't do that, it bails on the current
/// rule and uses the default error recovery, which consumes until the
/// resynchronization set of the current rule.
/// <p>If the sub rule is optional (
2013-08-05 10:08:51 +08:00
/// <code>(...)?</code>
/// ,
/// <code>(...)*</code>
/// , or block
/// with an empty alternative), then the expected set includes what follows
/// the subrule.</p>
/// <p>During loop iteration, it consumes until it sees a token that can start a
2013-08-05 10:08:51 +08:00
/// sub rule or what follows loop. Yes, that is pretty aggressive. We opt to
/// stay in the loop as long as possible.</p>
/// <p><strong>ORIGINS</strong></p>
/// <p>Previous versions of ANTLR did a poor job of their recovery within loops.
2013-08-05 10:08:51 +08:00
/// A single mismatch token or missing token would force the parser to bail
/// out of the entire rules surrounding the loop. So, for rule</p>
2013-08-05 10:08:51 +08:00
/// <pre>
/// classDef : 'class' ID '{' member* '}'
/// </pre>
/// input with an extra token between members would force the parser to
/// consume until it found the next class definition rather than the next
/// member definition of the current class.
/// <p>This functionality cost a little bit of effort because the parser has to
2013-08-05 10:08:51 +08:00
/// compare token set at the start of the loop and at each iteration. If for
/// some reason speed is suffering for you, you can turn off this
/// functionality by simply overriding this method as a blank { }.</p>
2013-02-16 22:14:20 +08:00
/// </summary>
2013-08-05 10:08:51 +08:00
/// <exception cref="Antlr4.Runtime.RecognitionException"></exception>
2013-02-16 22:14:20 +08:00
public virtual void Sync(Parser recognizer)
{
2013-02-17 05:23:03 +08:00
ATNState s = recognizer.Interpreter.atn.states[recognizer.State];
2013-02-16 22:14:20 +08:00
// System.err.println("sync @ "+s.stateNumber+"="+s.getClass().getSimpleName());
// If already recovering, don't try to sync
2013-08-05 10:08:51 +08:00
if (InErrorRecoveryMode(recognizer))
2013-02-16 22:14:20 +08:00
{
return;
}
2013-02-17 05:23:03 +08:00
ITokenStream tokens = ((ITokenStream)recognizer.InputStream);
2013-02-16 22:14:20 +08:00
int la = tokens.La(1);
// try cheaper subset first; might get lucky. seems to shave a wee bit off
if (recognizer.Atn.NextTokens(s).Contains(la) || la == TokenConstants.Eof)
2013-02-16 22:14:20 +08:00
{
return;
}
// Return but don't end recovery. only do that upon valid token match
if (recognizer.IsExpectedToken(la))
{
return;
}
switch (s.StateType)
2013-02-16 22:14:20 +08:00
{
case StateType.BlockStart:
case StateType.StarBlockStart:
case StateType.PlusBlockStart:
case StateType.StarLoopEntry:
2013-02-16 22:14:20 +08:00
{
// report error and recover if possible
if (SingleTokenDeletion(recognizer) != null)
{
return;
}
throw new InputMismatchException(recognizer);
}
2013-02-16 05:30:47 +08:00
case StateType.PlusLoopBack:
case StateType.StarLoopBack:
2013-02-16 22:14:20 +08:00
{
// System.err.println("at loop back: "+s.getClass().getSimpleName());
ReportUnwantedToken(recognizer);
IntervalSet expecting = recognizer.GetExpectedTokens();
2014-02-17 03:33:54 +08:00
IntervalSet whatFollowsLoopIterationOrRule = expecting.Or(GetErrorRecoverySet(recognizer));
2013-02-16 22:14:20 +08:00
ConsumeUntil(recognizer, whatFollowsLoopIterationOrRule);
break;
}
2013-02-16 05:30:47 +08:00
2013-02-16 22:14:20 +08:00
default:
{
// do nothing if we can't identify the exact kind of ATN state
break;
}
}
}
2013-02-16 05:30:47 +08:00
2013-08-05 10:08:51 +08:00
/// <summary>
/// This is called by
2014-02-17 03:33:54 +08:00
/// <see cref="ReportError(Parser, RecognitionException)">ReportError(Parser, RecognitionException)</see>
2013-08-05 10:08:51 +08:00
/// when the exception is a
/// <see cref="NoViableAltException">NoViableAltException</see>
/// .
/// </summary>
2014-02-17 03:33:54 +08:00
/// <seealso cref="ReportError(Parser, RecognitionException)">ReportError(Parser, RecognitionException)</seealso>
2013-08-05 10:08:51 +08:00
/// <param name="recognizer">the parser instance</param>
/// <param name="e">the recognition exception</param>
2014-02-17 03:33:54 +08:00
protected internal virtual void ReportNoViableAlternative(Parser recognizer, NoViableAltException e)
2013-02-16 22:14:20 +08:00
{
2013-02-17 05:23:03 +08:00
ITokenStream tokens = ((ITokenStream)recognizer.InputStream);
2013-02-16 22:14:20 +08:00
string input;
if (tokens != null)
2013-02-16 22:14:20 +08:00
{
if (e.GetStartToken().Type == TokenConstants.Eof)
2013-02-16 22:14:20 +08:00
{
input = "<EOF>";
}
else
{
input = tokens.GetText(e.GetStartToken(), e.OffendingToken);
2013-02-16 22:14:20 +08:00
}
}
else
{
input = "<unknown input>";
}
string msg = "no viable alternative at input " + EscapeWSAndQuote(input);
NotifyErrorListeners(recognizer, msg, e);
}
2013-02-16 05:30:47 +08:00
2013-08-05 10:08:51 +08:00
/// <summary>
/// This is called by
2014-02-17 03:33:54 +08:00
/// <see cref="ReportError(Parser, RecognitionException)">ReportError(Parser, RecognitionException)</see>
2013-08-05 10:08:51 +08:00
/// when the exception is an
/// <see cref="InputMismatchException">InputMismatchException</see>
/// .
/// </summary>
2014-02-17 03:33:54 +08:00
/// <seealso cref="ReportError(Parser, RecognitionException)">ReportError(Parser, RecognitionException)</seealso>
2013-08-05 10:08:51 +08:00
/// <param name="recognizer">the parser instance</param>
/// <param name="e">the recognition exception</param>
2014-02-17 03:33:54 +08:00
protected internal virtual void ReportInputMismatch(Parser recognizer, InputMismatchException e)
2013-02-16 22:14:20 +08:00
{
2014-02-17 03:33:54 +08:00
string msg = "mismatched input " + GetTokenErrorDisplay(e.OffendingToken) + " expecting " + e.GetExpectedTokens().ToString(recognizer.TokenNames);
2013-02-16 22:14:20 +08:00
NotifyErrorListeners(recognizer, msg, e);
}
2013-02-16 05:30:47 +08:00
2013-08-05 10:08:51 +08:00
/// <summary>
/// This is called by
2014-02-17 03:33:54 +08:00
/// <see cref="ReportError(Parser, RecognitionException)">ReportError(Parser, RecognitionException)</see>
2013-08-05 10:08:51 +08:00
/// when the exception is a
/// <see cref="FailedPredicateException">FailedPredicateException</see>
/// .
/// </summary>
2014-02-17 03:33:54 +08:00
/// <seealso cref="ReportError(Parser, RecognitionException)">ReportError(Parser, RecognitionException)</seealso>
2013-08-05 10:08:51 +08:00
/// <param name="recognizer">the parser instance</param>
/// <param name="e">the recognition exception</param>
2014-02-17 03:33:54 +08:00
protected internal virtual void ReportFailedPredicate(Parser recognizer, FailedPredicateException e)
2013-02-16 22:14:20 +08:00
{
2013-02-17 05:23:03 +08:00
string ruleName = recognizer.RuleNames[recognizer._ctx.GetRuleIndex()];
2013-02-16 22:14:20 +08:00
string msg = "rule " + ruleName + " " + e.Message;
NotifyErrorListeners(recognizer, msg, e);
}
2013-02-16 05:30:47 +08:00
2013-08-05 10:08:51 +08:00
/// <summary>
/// This method is called to report a syntax error which requires the removal
/// of a token from the input stream.
/// </summary>
/// <remarks>
/// This method is called to report a syntax error which requires the removal
/// of a token from the input stream. At the time this method is called, the
/// erroneous symbol is current
/// <code>LT(1)</code>
/// symbol and has not yet been
/// removed from the input stream. When this method returns,
/// <code>recognizer</code>
/// is in error recovery mode.
/// <p>This method is called when
2013-08-05 10:08:51 +08:00
/// <see cref="SingleTokenDeletion(Parser)">SingleTokenDeletion(Parser)</see>
/// identifies
/// single-token deletion as a viable recovery strategy for a mismatched
/// input error.</p>
/// <p>The default implementation simply returns if the handler is already in
2013-08-05 10:08:51 +08:00
/// error recovery mode. Otherwise, it calls
/// <see cref="BeginErrorCondition(Parser)">BeginErrorCondition(Parser)</see>
/// to
/// enter error recovery mode, followed by calling
2014-02-17 03:33:54 +08:00
/// <see cref="Parser.NotifyErrorListeners(string)">Parser.NotifyErrorListeners(string)</see>
/// .</p>
2013-08-05 10:08:51 +08:00
/// </remarks>
/// <param name="recognizer">the parser instance</param>
protected internal virtual void ReportUnwantedToken(Parser recognizer)
2013-02-16 22:14:20 +08:00
{
2013-08-05 10:08:51 +08:00
if (InErrorRecoveryMode(recognizer))
2013-02-16 22:14:20 +08:00
{
return;
}
BeginErrorCondition(recognizer);
2013-02-17 05:23:03 +08:00
IToken t = recognizer.CurrentToken;
2013-02-16 22:14:20 +08:00
string tokenName = GetTokenErrorDisplay(t);
IntervalSet expecting = GetExpectedTokens(recognizer);
2014-02-17 03:33:54 +08:00
string msg = "extraneous input " + tokenName + " expecting " + expecting.ToString(recognizer.TokenNames);
2013-02-16 22:14:20 +08:00
recognizer.NotifyErrorListeners(t, msg, null);
}
2013-02-16 05:30:47 +08:00
2013-08-05 10:08:51 +08:00
/// <summary>
/// This method is called to report a syntax error which requires the
/// insertion of a missing token into the input stream.
/// </summary>
/// <remarks>
/// This method is called to report a syntax error which requires the
/// insertion of a missing token into the input stream. At the time this
/// method is called, the missing token has not yet been inserted. When this
/// method returns,
/// <code>recognizer</code>
/// is in error recovery mode.
/// <p>This method is called when
2013-08-05 10:08:51 +08:00
/// <see cref="SingleTokenInsertion(Parser)">SingleTokenInsertion(Parser)</see>
/// identifies
/// single-token insertion as a viable recovery strategy for a mismatched
/// input error.</p>
/// <p>The default implementation simply returns if the handler is already in
2013-08-05 10:08:51 +08:00
/// error recovery mode. Otherwise, it calls
/// <see cref="BeginErrorCondition(Parser)">BeginErrorCondition(Parser)</see>
/// to
/// enter error recovery mode, followed by calling
2014-02-17 03:33:54 +08:00
/// <see cref="Parser.NotifyErrorListeners(string)">Parser.NotifyErrorListeners(string)</see>
/// .</p>
2013-08-05 10:08:51 +08:00
/// </remarks>
/// <param name="recognizer">the parser instance</param>
protected internal virtual void ReportMissingToken(Parser recognizer)
2013-02-16 22:14:20 +08:00
{
2013-08-05 10:08:51 +08:00
if (InErrorRecoveryMode(recognizer))
2013-02-16 22:14:20 +08:00
{
return;
}
BeginErrorCondition(recognizer);
2013-02-17 05:23:03 +08:00
IToken t = recognizer.CurrentToken;
2013-02-16 22:14:20 +08:00
IntervalSet expecting = GetExpectedTokens(recognizer);
2014-02-17 03:33:54 +08:00
string msg = "missing " + expecting.ToString(recognizer.TokenNames) + " at " + GetTokenErrorDisplay(t);
2013-02-16 22:14:20 +08:00
recognizer.NotifyErrorListeners(t, msg, null);
}
2013-02-16 05:30:47 +08:00
2013-08-05 10:08:51 +08:00
/// <summary>
/// <inheritDoc></inheritDoc>
/// <p>The default implementation attempts to recover from the mismatched input
2013-08-05 10:08:51 +08:00
/// by using single token insertion and deletion as described below. If the
/// recovery attempt fails, this method throws an
/// <see cref="InputMismatchException">InputMismatchException</see>
/// .</p>
/// <p><strong>EXTRA TOKEN</strong> (single token deletion)</p>
/// <p>
2013-08-05 10:08:51 +08:00
/// <code>LA(1)</code>
/// is not what we are looking for. If
/// <code>LA(2)</code>
/// has the
/// right token, however, then assume
/// <code>LA(1)</code>
/// is some extra spurious
/// token and delete it. Then consume and return the next token (which was
/// the
/// <code>LA(2)</code>
/// token) as the successful result of the match operation.</p>
/// <p>This recovery strategy is implemented by
2013-08-05 10:08:51 +08:00
/// <see cref="SingleTokenDeletion(Parser)">SingleTokenDeletion(Parser)</see>
/// .</p>
/// <p><strong>MISSING TOKEN</strong> (single token insertion)</p>
/// <p>If current token (at
2013-08-05 10:08:51 +08:00
/// <code>LA(1)</code>
/// ) is consistent with what could come
/// after the expected
/// <code>LA(1)</code>
/// token, then assume the token is missing
/// and use the parser's
/// <see cref="ITokenFactory">ITokenFactory</see>
/// to create it on the fly. The
/// "insertion" is performed by returning the created token as the successful
/// result of the match operation.</p>
/// <p>This recovery strategy is implemented by
2013-08-05 10:08:51 +08:00
/// <see cref="SingleTokenInsertion(Parser)">SingleTokenInsertion(Parser)</see>
/// .</p>
/// <p><strong>EXAMPLE</strong></p>
/// <p>For example, Input
2013-08-05 10:08:51 +08:00
/// <code>i=(3;</code>
/// is clearly missing the
/// <code>')'</code>
/// . When
/// the parser returns from the nested call to
/// <code>expr</code>
/// , it will have
/// call chain:</p>
2013-08-05 10:08:51 +08:00
/// <pre>
/// stat &rarr; expr &rarr; atom
2013-08-05 10:08:51 +08:00
/// </pre>
/// and it will be trying to match the
/// <code>')'</code>
/// at this point in the
2013-02-16 22:14:20 +08:00
/// derivation:
2013-08-05 10:08:51 +08:00
/// <pre>
2013-02-16 22:14:20 +08:00
/// =&gt; ID '=' '(' INT ')' ('+' atom)* ';'
/// ^
2013-08-05 10:08:51 +08:00
/// </pre>
/// The attempt to match
/// <code>')'</code>
/// will fail when it sees
/// <code>';'</code>
/// and
/// call
/// <see cref="RecoverInline(Parser)">RecoverInline(Parser)</see>
/// . To recover, it sees that
/// <code>LA(1)==';'</code>
/// is in the set of tokens that can follow the
/// <code>')'</code>
/// token reference
/// in rule
/// <code>atom</code>
/// . It can assume that you forgot the
/// <code>')'</code>
/// .
/// </summary>
2013-02-16 22:14:20 +08:00
/// <exception cref="Antlr4.Runtime.RecognitionException"></exception>
public virtual IToken RecoverInline(Parser recognizer)
{
// SINGLE TOKEN DELETION
IToken matchedSymbol = SingleTokenDeletion(recognizer);
if (matchedSymbol != null)
{
// we have deleted the extra token.
// now, move past ttype token as if all were ok
recognizer.Consume();
return matchedSymbol;
}
// SINGLE TOKEN INSERTION
if (SingleTokenInsertion(recognizer))
{
return GetMissingSymbol(recognizer);
}
// even that didn't work; must throw the exception
throw new InputMismatchException(recognizer);
}
2013-02-16 05:30:47 +08:00
2013-08-05 10:08:51 +08:00
/// <summary>
/// This method implements the single-token insertion inline error recovery
/// strategy.
/// </summary>
/// <remarks>
/// This method implements the single-token insertion inline error recovery
/// strategy. It is called by
/// <see cref="RecoverInline(Parser)">RecoverInline(Parser)</see>
/// if the single-token
/// deletion strategy fails to recover from the mismatched input. If this
/// method returns
/// <code>true</code>
/// ,
/// <code>recognizer</code>
/// will be in error recovery
/// mode.
/// <p>This method determines whether or not single-token insertion is viable by
2013-08-05 10:08:51 +08:00
/// checking if the
/// <code>LA(1)</code>
/// input symbol could be successfully matched
/// if it were instead the
/// <code>LA(2)</code>
/// symbol. If this method returns
/// <code>true</code>
/// , the caller is responsible for creating and inserting a
/// token with the correct type to produce this behavior.</p>
2013-08-05 10:08:51 +08:00
/// </remarks>
/// <param name="recognizer">the parser instance</param>
/// <returns>
///
/// <code>true</code>
/// if single-token insertion is a viable recovery
/// strategy for the current mismatched input, otherwise
/// <code>false</code>
/// </returns>
protected internal virtual bool SingleTokenInsertion(Parser recognizer)
2013-02-16 22:14:20 +08:00
{
2013-02-17 05:23:03 +08:00
int currentSymbolType = ((ITokenStream)recognizer.InputStream).La(1);
2013-02-16 22:14:20 +08:00
// if current token is consistent with what could come after current
// ATN state, then we know we're missing a token; error recovery
// is free to conjure up and insert the missing token
2013-02-17 05:23:03 +08:00
ATNState currentState = recognizer.Interpreter.atn.states[recognizer.State];
2013-02-16 22:14:20 +08:00
ATNState next = currentState.Transition(0).target;
2013-02-17 05:23:03 +08:00
ATN atn = recognizer.Interpreter.atn;
2014-02-17 03:33:54 +08:00
IntervalSet expectingAtLL2 = atn.NextTokens(next, PredictionContext.FromRuleContext(atn, recognizer._ctx));
2013-02-16 22:14:20 +08:00
// System.out.println("LT(2) set="+expectingAtLL2.toString(recognizer.getTokenNames()));
if (expectingAtLL2.Contains(currentSymbolType))
{
ReportMissingToken(recognizer);
return true;
}
return false;
}
2013-02-16 05:30:47 +08:00
2013-08-05 10:08:51 +08:00
/// <summary>
/// This method implements the single-token deletion inline error recovery
/// strategy.
/// </summary>
/// <remarks>
/// This method implements the single-token deletion inline error recovery
/// strategy. It is called by
/// <see cref="RecoverInline(Parser)">RecoverInline(Parser)</see>
/// to attempt to recover
/// from mismatched input. If this method returns null, the parser and error
/// handler state will not have changed. If this method returns non-null,
/// <code>recognizer</code>
/// will <em>not</em> be in error recovery mode since the
/// returned token was a successful match.
/// <p>If the single-token deletion is successful, this method calls
2013-08-05 10:08:51 +08:00
/// <see cref="ReportUnwantedToken(Parser)">ReportUnwantedToken(Parser)</see>
/// to report the error, followed by
/// <see cref="Parser.Consume()">Parser.Consume()</see>
/// to actually "delete" the extraneous token. Then,
/// before returning
/// <see cref="ReportMatch(Parser)">ReportMatch(Parser)</see>
/// is called to signal a successful
/// match.</p>
2013-08-05 10:08:51 +08:00
/// </remarks>
/// <param name="recognizer">the parser instance</param>
/// <returns>
/// the successfully matched
/// <see cref="IToken">IToken</see>
/// instance if single-token
/// deletion successfully recovers from the mismatched input, otherwise
/// <code>null</code>
/// </returns>
[Nullable]
protected internal virtual IToken SingleTokenDeletion(Parser recognizer)
2013-02-16 22:14:20 +08:00
{
2013-02-17 05:23:03 +08:00
int nextTokenType = ((ITokenStream)recognizer.InputStream).La(2);
2013-02-16 22:14:20 +08:00
IntervalSet expecting = GetExpectedTokens(recognizer);
if (expecting.Contains(nextTokenType))
{
ReportUnwantedToken(recognizer);
recognizer.Consume();
// simply delete extra token
// we want to return the token we're actually matching
2013-02-17 05:23:03 +08:00
IToken matchedSymbol = recognizer.CurrentToken;
2013-08-05 10:08:51 +08:00
ReportMatch(recognizer);
2013-02-16 22:14:20 +08:00
// we know current token is correct
return matchedSymbol;
}
return null;
}
2013-02-16 05:30:47 +08:00
2013-02-16 22:14:20 +08:00
/// <summary>Conjure up a missing token during error recovery.</summary>
/// <remarks>
/// Conjure up a missing token during error recovery.
/// The recognizer attempts to recover from single missing
/// symbols. But, actions might refer to that missing symbol.
/// For example, x=ID {f($x);}. The action clearly assumes
/// that there has been an identifier matched previously and that
/// $x points at that token. If that token is missing, but
/// the next token in the stream is what we want we assume that
/// this token is missing and we keep going. Because we
/// have to return some token to replace the missing token,
/// we have to conjure one up. This method gives the user control
/// over the tokens returned for missing tokens. Mostly,
/// you will want to create something special for identifier
/// tokens. For literals such as '{' and ',', the default
/// action in the parser or tree parser works. It simply creates
/// a CommonToken of the appropriate type. The text will be the token.
/// If you change what tokens must be created by the lexer,
/// override this method to create the appropriate tokens.
/// </remarks>
2013-08-05 10:08:51 +08:00
[NotNull]
2013-02-16 22:14:20 +08:00
protected internal virtual IToken GetMissingSymbol(Parser recognizer)
{
2013-02-17 05:23:03 +08:00
IToken currentSymbol = recognizer.CurrentToken;
2013-02-16 22:14:20 +08:00
IntervalSet expecting = GetExpectedTokens(recognizer);
int expectedTokenType = expecting.GetMinElement();
// get any element
string tokenText;
if (expectedTokenType == TokenConstants.Eof)
2013-02-16 22:14:20 +08:00
{
tokenText = "<missing EOF>";
}
else
{
2013-02-17 05:23:03 +08:00
tokenText = "<missing " + recognizer.TokenNames[expectedTokenType] + ">";
2013-02-16 22:14:20 +08:00
}
IToken current = currentSymbol;
2013-02-17 05:23:03 +08:00
IToken lookback = ((ITokenStream)recognizer.InputStream).Lt(-1);
if (current.Type == TokenConstants.Eof && lookback != null)
2013-02-16 22:14:20 +08:00
{
current = lookback;
}
2014-02-17 03:33:54 +08:00
return ConstructToken(((ITokenStream)recognizer.InputStream).TokenSource, expectedTokenType, tokenText, current);
2013-02-16 22:14:20 +08:00
}
2013-02-16 05:30:47 +08:00
2014-02-17 03:33:54 +08:00
protected internal virtual IToken ConstructToken(ITokenSource tokenSource, int expectedTokenType, string tokenText, IToken current)
2013-02-16 22:14:20 +08:00
{
ITokenFactory factory = tokenSource.TokenFactory;
2014-02-17 03:33:54 +08:00
return factory.Create(Tuple.Create(tokenSource, current.TokenSource.InputStream), expectedTokenType, tokenText, TokenConstants.DefaultChannel, -1, -1, current.Line, current.Column);
2013-02-16 22:14:20 +08:00
}
2013-02-16 05:30:47 +08:00
2013-08-05 10:08:51 +08:00
[NotNull]
protected internal virtual IntervalSet GetExpectedTokens(Parser recognizer)
2013-02-16 22:14:20 +08:00
{
return recognizer.GetExpectedTokens();
}
2013-02-16 05:30:47 +08:00
2013-02-16 22:14:20 +08:00
/// <summary>
/// How should a token be displayed in an error message? The default
/// is to display just the text, but during development you might
/// want to have a lot of information spit out.
/// </summary>
/// <remarks>
/// How should a token be displayed in an error message? The default
/// is to display just the text, but during development you might
/// want to have a lot of information spit out. Override in that case
/// to use t.toString() (which, for CommonToken, dumps everything about
/// the token). This is better than forcing you to override a method in
/// your token objects because you don't have to go modify your lexer
/// so that it creates a new Java type.
/// </remarks>
2013-08-05 10:08:51 +08:00
protected internal virtual string GetTokenErrorDisplay(IToken t)
2013-02-16 22:14:20 +08:00
{
if (t == null)
{
return "<no token>";
}
string s = GetSymbolText(t);
if (s == null)
{
if (GetSymbolType(t) == TokenConstants.Eof)
2013-02-16 22:14:20 +08:00
{
s = "<EOF>";
}
else
{
s = "<" + GetSymbolType(t) + ">";
}
}
return EscapeWSAndQuote(s);
}
2013-02-16 05:30:47 +08:00
2013-02-16 22:14:20 +08:00
protected internal virtual string GetSymbolText(IToken symbol)
{
return symbol.Text;
}
2013-02-16 05:30:47 +08:00
2013-02-16 22:14:20 +08:00
protected internal virtual int GetSymbolType(IToken symbol)
{
return symbol.Type;
}
2013-02-16 05:30:47 +08:00
2013-08-05 10:08:51 +08:00
[NotNull]
2013-02-16 22:14:20 +08:00
protected internal virtual string EscapeWSAndQuote(string s)
{
// if ( s==null ) return s;
2013-02-25 02:42:19 +08:00
s = s.Replace("\n", "\\n");
s = s.Replace("\r", "\\r");
s = s.Replace("\t", "\\t");
2013-02-16 22:14:20 +08:00
return "'" + s + "'";
}
2013-02-16 05:30:47 +08:00
2013-08-05 10:08:51 +08:00
[NotNull]
2013-02-16 22:14:20 +08:00
protected internal virtual IntervalSet GetErrorRecoverySet(Parser recognizer)
{
2013-02-17 05:23:03 +08:00
ATN atn = recognizer.Interpreter.atn;
2013-02-16 22:14:20 +08:00
RuleContext ctx = recognizer._ctx;
IntervalSet recoverSet = new IntervalSet();
while (ctx != null && ctx.invokingState >= 0)
{
// compute what follows who invoked us
ATNState invokingState = atn.states[ctx.invokingState];
RuleTransition rt = (RuleTransition)invokingState.Transition(0);
IntervalSet follow = atn.NextTokens(rt.followState);
recoverSet.AddAll(follow);
ctx = ctx.parent;
}
recoverSet.Remove(TokenConstants.Epsilon);
2013-02-16 22:14:20 +08:00
// System.out.println("recover set "+recoverSet.toString(recognizer.getTokenNames()));
return recoverSet;
}
2013-02-16 05:30:47 +08:00
2013-08-05 10:08:51 +08:00
/// <summary>Consume tokens until one matches the given token set.</summary>
/// <remarks>Consume tokens until one matches the given token set.</remarks>
protected internal virtual void ConsumeUntil(Parser recognizer, IntervalSet set)
2013-02-16 22:14:20 +08:00
{
// System.err.println("consumeUntil("+set.toString(recognizer.getTokenNames())+")");
2013-02-17 05:23:03 +08:00
int ttype = ((ITokenStream)recognizer.InputStream).La(1);
while (ttype != TokenConstants.Eof && !set.Contains(ttype))
2013-02-16 22:14:20 +08:00
{
//System.out.println("consume during recover LA(1)="+getTokenNames()[input.LA(1)]);
// recognizer.getInputStream().consume();
recognizer.Consume();
2013-02-17 05:23:03 +08:00
ttype = ((ITokenStream)recognizer.InputStream).La(1);
2013-02-16 22:14:20 +08:00
}
}
}
2013-02-16 05:30:47 +08:00
}