Class: DefaultErrorStrategy
- Inherits:
-
ErrorStrategy
- Object
- ErrorStrategy
- DefaultErrorStrategy
- Defined in:
- lib/antlr4/error/ErrorStrategy.rb
Overview
This is the default implementation of ANTLRErrorStrategy used for error reporting and recovery in ANTLR parsers.
Direct Known Subclasses
Instance Attribute Summary collapse
-
#errorRecoveryMode ⇒ Object
Returns the value of attribute errorRecoveryMode.
-
#lastErrorIndex ⇒ Object
Returns the value of attribute lastErrorIndex.
-
#lastErrorStates ⇒ Object
Returns the value of attribute lastErrorStates.
Instance Method Summary collapse
-
#beginErrorCondition(recognizer) ⇒ Object
This method is called to enter error recovery mode when a recognition exception is reported.
-
#consumeUntil(recognizer, set_) ⇒ Object
Consume tokens until one matches the given token set.#.
-
#endErrorCondition(recognizer) ⇒ Object
This method is called to leave error recovery mode after recovering from a recognition exception.
- #escapeWSAndQuote(s) ⇒ Object
-
#getErrorRecoverySet(recognizer) ⇒ Object
Compute the error recovery set for the current rule.
- #getExpectedTokens(recognizer) ⇒ Object
-
#getMissingSymbol(recognizer) ⇒ Object
Conjure up a missing token during error recovery.
-
#getTokenErrorDisplay(token) ⇒ Object
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.
- #inErrorRecoveryMode(recognizer) ⇒ Object
-
#initialize ⇒ DefaultErrorStrategy
constructor
A new instance of DefaultErrorStrategy.
-
#recover(recognizer, e) ⇒ Object
@inheritDoc.
-
#recoverInline(recognizer) ⇒ Object
<p>The default implementation attempts to recover from the mismatched input by using single token insertion and deletion as described below.
-
#reportError(recognizer, e) ⇒ Object
@inheritDoc.
-
#reportFailedPredicate(recognizer, e) ⇒ Object
This is called by #reportError when the exception is a FailedPredicateException.
-
#reportInputMismatch(recognizer, e) ⇒ Object
This is called by #reportError when the exception is an InputMismatchException.
-
#reportMatch(recognizer) ⇒ Object
@inheritDoc.
-
#reportMissingToken(recognizer) ⇒ Object
This method is called to report a syntax error which requires the insertion of a missing token into the input stream.
-
#reportNoViableAlternative(recognizer, e) ⇒ Object
This is called by #reportError when the exception is a NoViableAltException.
-
#reportUnwantedToken(recognizer) ⇒ Object
This method is called to report a syntax error which requires the removal of a token from the input stream.
-
#reset(recognizer) ⇒ Object
<p>The default implementation simply calls #endErrorCondition to ensure that the handler is not in error recovery mode.</p>.
-
#singleTokenDeletion(recognizer) ⇒ Object
This method implements the single-token deletion inline error recovery strategy.
-
#singleTokenInsertion(recognizer) ⇒ @code true
This method implements the single-token insertion inline error recovery strategy.
-
#sync(recognizer) ⇒ Object
The default implementation of ANTLRErrorStrategy#sync makes sure that the current lookahead symbol is consistent with what were expecting at this point in the ATN.
Constructor Details
#initialize ⇒ DefaultErrorStrategy
Returns a new instance of DefaultErrorStrategy.
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
# File 'lib/antlr4/error/ErrorStrategy.rb', line 59 def initialize() super() # 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. # # @see #inErrorRecoveryMode # @errorRecoveryMode = false # 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. # @lastErrorIndex = -1 @lastErrorStates = nil end |
Instance Attribute Details
#errorRecoveryMode ⇒ Object
Returns the value of attribute errorRecoveryMode.
58 59 60 |
# File 'lib/antlr4/error/ErrorStrategy.rb', line 58 def errorRecoveryMode @errorRecoveryMode end |
#lastErrorIndex ⇒ Object
Returns the value of attribute lastErrorIndex.
58 59 60 |
# File 'lib/antlr4/error/ErrorStrategy.rb', line 58 def lastErrorIndex @lastErrorIndex end |
#lastErrorStates ⇒ Object
Returns the value of attribute lastErrorStates.
58 59 60 |
# File 'lib/antlr4/error/ErrorStrategy.rb', line 58 def lastErrorStates @lastErrorStates end |
Instance Method Details
#beginErrorCondition(recognizer) ⇒ Object
This method is called to enter error recovery mode when a recognition exception is reported.
91 92 93 |
# File 'lib/antlr4/error/ErrorStrategy.rb', line 91 def beginErrorCondition(recognizer) self.errorRecoveryMode = true end |
#consumeUntil(recognizer, set_) ⇒ Object
Consume tokens until one matches the given token set.#
682 683 684 685 686 687 688 |
# File 'lib/antlr4/error/ErrorStrategy.rb', line 682 def consumeUntil(recognizer, set_) ttype = recognizer.getTokenStream().LA(1) while ttype != Token::EOF and not set_.member? ttype do recognizer.consume() ttype = recognizer.getTokenStream().LA(1) end end |
#endErrorCondition(recognizer) ⇒ Object
This method is called to leave error recovery mode after recovering from a recognition exception.
105 106 107 108 109 |
# File 'lib/antlr4/error/ErrorStrategy.rb', line 105 def endErrorCondition(recognizer) self.errorRecoveryMode = false self.lastErrorStates = nil self.lastErrorIndex = -1 end |
#escapeWSAndQuote(s) ⇒ Object
566 567 568 569 570 571 |
# File 'lib/antlr4/error/ErrorStrategy.rb', line 566 def escapeWSAndQuote( s) s.gsub!("\n","\\n") s.gsub!("\r","\\r") s.gsub!("\t","\\t") "'#{s}'" end |
#getErrorRecoverySet(recognizer) ⇒ Object
Compute the error recovery set for the current rule. During
rule invocation, the parser pushes the set of tokens that can
follow that rule reference on the stack; this amounts to
computing FIRST of what follows the rule reference in the
enclosing rule. See LinearApproximator.FIRST().
This local follow set only includes tokens
from within the rule; i.e., the FIRST computation done by
ANTLR stops at the end of a rule.
EXAMPLE
When you find a "no viable alt exception", the input is not
consistent with any of the alternatives for rule r. The best
thing to do is to consume tokens until you see something that
can legally follow a call to r#or* any rule that called r.
You don't want the exact set of viable next tokens because the
input might just be missing a token--you might consume the
rest of the input looking for one of the missing tokens.
Consider grammar:
a : '[' b ']'
| '(' b ')'
;
b : c '^' INT ;
c : ID
| INT
;
At each rule invocation, the set of tokens that could follow
that rule is pushed on a stack. Here are the various
context-sensitive follow sets:
FOLLOW(b1_in_a) = FIRST(']') = ']'
FOLLOW(b2_in_a) = FIRST(')') = ')'
FOLLOW(c_in_b) = FIRST('^') = '^'
Upon erroneous input "[]", the call chain is
a -> b -> c
and, hence, the follow context stack is:
depth follow set start of rule execution
0 <EOF> a (from main())
1 ']' b
2 '^' c
Notice that ')' is not included, because b would have to have
been called from a different context in rule a for ')' to be
included.
For error recovery, we cannot consider FOLLOW(c)
(context-sensitive or otherwise). We need the combined set of
all context-sensitive FOLLOW sets--the set of all tokens that
could follow any reference in the call chain. We need to
resync to one of those tokens. Note that FOLLOW(c)='^' and if
we resync'd to that token, we'd consume until EOF. We need to
sync to context-sensitive FOLLOWs for a, b, and c: {']','^'}.
In this case, for input "[]", LA(1) is ']' and in the set, so we would
not consume anything. After printing an error, rule c would
return normally. Rule b would not find the required '^' though.
At this point, it gets a mismatched token error and throws an
exception (since LA(1) is not in the viable following token
set). The rule exception handler tries to recover, but finds
the same recovery set and doesn't consume anything. Rule b
exits normally returning to rule a. Now it finds the ']' (and
with the successful match exits errorRecovery mode).
So, you can see that the parser walks up the call chain looking
for the token that was a member of the recovery set.
Errors are not generated in errorRecovery mode.
ANTLR's error recovery mechanism is based upon original ideas:
"Algorithms + Data Structures = Programs" by Niklaus Wirth
and
"A note on error recovery in recursive descent parsers":
http:#portal.acm.org/citation.cfm?id=947902.947905
Later, Josef Grosch had some good ideas:
"Efficient and Comfortable Error Recovery in Recursive Descent
Parsers":
ftp:#www.cocolab.com/products/cocktail/doca4.ps/ell.ps.zip
Like Grosch I implement context-sensitive FOLLOW sets that are combined
at run-time upon error to avoid overhead during parsing.
665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 |
# File 'lib/antlr4/error/ErrorStrategy.rb', line 665 def getErrorRecoverySet(recognizer) atn = recognizer.interp.atn ctx = recognizer.ctx recoverSet = IntervalSet.new() while ctx and ctx.invokingState >= 0 do # compute what follows who invoked us invokingState = atn.states[ctx.invokingState] rt = invokingState.transitions[0] follow = atn.nextTokens(rt.followState) recoverSet.addSet(follow) ctx = ctx.parentCtx end recoverSet.remove(Token::EPSILON) return recoverSet end |
#getExpectedTokens(recognizer) ⇒ Object
542 543 544 |
# File 'lib/antlr4/error/ErrorStrategy.rb', line 542 def getExpectedTokens(recognizer) return recognizer.getExpectedTokens() end |
#getMissingSymbol(recognizer) ⇒ Object
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.
524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 |
# File 'lib/antlr4/error/ErrorStrategy.rb', line 524 def getMissingSymbol(recognizer) currentSymbol = recognizer.getCurrentToken() expecting = self.getExpectedTokens(recognizer) expectedTokenType = expecting.getMinElement # get any element if expectedTokenType==Token::EOF then tokenText = "<missing EOF>" else tokenText = "<missing #{recognizer.tokenNames[expectedTokenType]}>" end current = currentSymbol lookback = recognizer.getTokenStream().LT(-1) if current.type==Token::EOF and not lookback.nil? then current = lookback end return recognizer.getTokenFactory().create(current.source, expectedTokenType, tokenText, Token::DEFAULT_CHANNEL, -1, -1, current.line, current.column) end |
#getTokenErrorDisplay(token) ⇒ Object
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.
554 555 556 557 558 559 560 561 562 563 564 565 |
# File 'lib/antlr4/error/ErrorStrategy.rb', line 554 def getTokenErrorDisplay(token) return "<no token>" if token.nil? s = token.text if s.nil? then if token.type==Token::EOF then s = "<EOF>" else s = "<#{token.class}>" end end return self.escapeWSAndQuote(s) end |
#inErrorRecoveryMode(recognizer) ⇒ Object
95 96 97 |
# File 'lib/antlr4/error/ErrorStrategy.rb', line 95 def inErrorRecoveryMode(recognizer) return self.errorRecoveryMode end |
#recover(recognizer, e) ⇒ Object
@inheritDoc
<p>The default implementation resynchronizes the parser by consuming tokens until we find one in the resynchronization set–loosely the set of tokens that can follow the current rule.</p>
164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 |
# File 'lib/antlr4/error/ErrorStrategy.rb', line 164 def recover(recognizer, e) if self.lastErrorIndex==recognizer.getInputStream().index \ and not self.lastErrorStates.nil? \ and self.lastErrorStates.member? recognizer.state then # 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. recognizer.consume() end self.lastErrorIndex = recognizer.input.index if self.lastErrorStates.nil? then self.lastErrorStates = Array.new(32) end self.lastErrorStates.push(recognizer.state) followSet = self.getErrorRecoverySet(recognizer) self.consumeUntil(recognizer, followSet) end |
#recoverInline(recognizer) ⇒ Object
<p>The default implementation attempts to recover from the mismatched input by using single token insertion and deletion as described below. If the recovery attempt fails, this method throws an InputMismatchException.</p>
<p><strong>EXTRA TOKEN</strong> (single token deletion)</p>
<p>LA(1) is not what we are looking for. If LA(2) has the right token, however, then assume LA(1) is some extra spurious token and delete it. Then consume and return the next token (which was the LA(2) token) as the successful result of the match operation.</p>
<p>This recovery strategy is implemented by #singleTokenDeletion.</p>
<p><strong>MISSING TOKEN</strong> (single token insertion)</p>
<p>If current token (at LA(1)) is consistent with what could come after the expected LA(1) token, then assume the token is missing and use the parser’s TokenFactory 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 #singleTokenInsertion.</p>
<p><strong>EXAMPLE</strong></p>
<p>For example, Input i=(3; is clearly missing the ‘)’. When the parser returns from the nested call to expr, it will have call chain:</p>
<pre> stat → expr → atom </pre>
and it will be trying to match the ‘)’ at this point in the derivation:
<pre>
> ID ‘=’ ‘(’ INT ‘)’ (‘+’ atom)* ‘;’
^
</pre>
The attempt to match ‘)’ will fail when it sees ‘;’ and call #recoverInline. To recover, it sees that LA(1)==‘;’ is in the set of tokens that can follow the ‘)’ token reference in rule atom. It can assume that you forgot the ‘)’.
417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 |
# File 'lib/antlr4/error/ErrorStrategy.rb', line 417 def recoverInline(recognizer) # SINGLE TOKEN DELETION matchedSymbol = self.singleTokenDeletion(recognizer) if not matchedSymbol.nil? then # we have deleted the extra token. # now, move past ttype token as if all were ok recognizer.consume() return matchedSymbol end # SINGLE TOKEN INSERTION if self.singleTokenInsertion(recognizer) then return self.getMissingSymbol(recognizer) end # even that didn't work; must throw the exception raise InputMismatchException.new(recognizer) end |
#reportError(recognizer, e) ⇒ Object
@inheritDoc
<p>The default implementation returns immediately if the handler is already in error recovery mode. Otherwise, it calls #beginErrorCondition and dispatches the reporting task based on the runtime type of e according to the following table.</p>
<ul> <li>NoViableAltException: Dispatches the call to #reportNoViableAlternative</li> <li>InputMismatchException: Dispatches the call to #reportInputMismatch</li> <li>FailedPredicateException: Dispatches the call to #reportFailedPredicate</li> <li>All other types: calls Parser#notifyErrorListeners to report the exception</li> </ul>
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 |
# File 'lib/antlr4/error/ErrorStrategy.rb', line 139 def reportError(recognizer, e) # if we've already reported an error and have not matched a token # yet successfully, don't report any errors. return if self.inErrorRecoveryMode(recognizer) # don't report spurious errors self.beginErrorCondition(recognizer) case e when NoViableAltException then self.reportNoViableAlternative(recognizer, e) when InputMismatchException then self.reportInputMismatch(recognizer, e) when FailedPredicateException then self.reportFailedPredicate(recognizer, e) else puts "unknown recognition error type: #{e.class}" recognizer.notifyErrorListeners(e.getOffendingToken(), e.getMessage(), e) end end |
#reportFailedPredicate(recognizer, e) ⇒ Object
This is called by #reportError when the exception is a FailedPredicateException.
311 312 313 314 315 |
# File 'lib/antlr4/error/ErrorStrategy.rb', line 311 def reportFailedPredicate(recognizer, e) ruleName = recognizer.ruleNames[recognizer.ctx.ruleIndex] msg = "rule #{ruleName} #{e.}" recognizer.notifyErrorListeners(e.offendingToken, msg, e) end |
#reportInputMismatch(recognizer, e) ⇒ Object
This is called by #reportError when the exception is an InputMismatchException.
292 293 294 295 296 297 298 299 300 |
# File 'lib/antlr4/error/ErrorStrategy.rb', line 292 def reportInputMismatch(recognizer, e) if e.recognizer.nil? then e.recognizer = recognizer end t = self.getTokenErrorDisplay(e.offendingToken) expecting = e.getExpectedTokens().toString(recognizer.tokenNames) msg = "mismatched input #{t} expecting #{ escapeWSAndQuote(expecting) }" recognizer.notifyErrorListeners(msg, e.offendingToken, e) end |
#reportMatch(recognizer) ⇒ Object
@inheritDoc
<p>The default implementation simply calls #endErrorCondition.</p>
116 117 118 |
# File 'lib/antlr4/error/ErrorStrategy.rb', line 116 def reportMatch(recognizer) self.endErrorCondition(recognizer) end |
#reportMissingToken(recognizer) ⇒ Object
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, recognizer is in error recovery mode.
<p>This method is called when #singleTokenInsertion 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 error recovery mode. Otherwise, it calls #beginErrorCondition to enter error recovery mode, followed by calling Parser#notifyErrorListeners.</p>
360 361 362 363 364 365 366 367 368 369 |
# File 'lib/antlr4/error/ErrorStrategy.rb', line 360 def reportMissingToken(recognizer) return if self.inErrorRecoveryMode(recognizer) self.beginErrorCondition(recognizer) t = recognizer.getCurrentToken() expecting = self.getExpectedTokens(recognizer) msg = "missing " + expecting.toString(recognizer.tokenNames) \ + " at " + self.getTokenErrorDisplay(t) recognizer.notifyErrorListeners(msg, t, nil) end |
#reportNoViableAlternative(recognizer, e) ⇒ Object
This is called by #reportError when the exception is a NoViableAltException.
269 270 271 272 273 274 275 276 277 278 279 280 281 282 |
# File 'lib/antlr4/error/ErrorStrategy.rb', line 269 def reportNoViableAlternative(recognizer, e) tokens = recognizer.getTokenStream() if not tokens.nil? then if e.startToken.type==Token::EOF then input = "<EOF>" else input = tokens.getText([e.startToken, e.offendingToken]) end else input = "<unknown input>" end msg = "no viable alternative at input " + self.escapeWSAndQuote(input) recognizer.notifyErrorListeners(msg, e.offendingToken, e) end |
#reportUnwantedToken(recognizer) ⇒ Object
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 LT(1) symbol and has not yet been removed from the input stream. When this method returns, recognizer is in error recovery mode.
<p>This method is called when #singleTokenDeletion 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 error recovery mode. Otherwise, it calls #beginErrorCondition to enter error recovery mode, followed by calling Parser#notifyErrorListeners.</p>
334 335 336 337 338 339 340 341 342 343 |
# File 'lib/antlr4/error/ErrorStrategy.rb', line 334 def reportUnwantedToken(recognizer) return if self.inErrorRecoveryMode(recognizer) self.beginErrorCondition(recognizer) t = recognizer.getCurrentToken() tokenName = self.getTokenErrorDisplay(t) expecting = self.getExpectedTokens(recognizer) msg = "extraneous input #{tokenName} expecting #{expecting.toString(recognizer.tokenNames)}" recognizer.notifyErrorListeners(msg, t, nil) end |
#reset(recognizer) ⇒ Object
<p>The default implementation simply calls #endErrorCondition to ensure that the handler is not in error recovery mode.</p>
81 82 83 |
# File 'lib/antlr4/error/ErrorStrategy.rb', line 81 def reset(recognizer) self.endErrorCondition(recognizer) end |
#singleTokenDeletion(recognizer) ⇒ Object
This method implements the single-token deletion inline error recovery strategy. It is called by #recoverInline 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, recognizer will not be in error recovery mode since the returned token was a successful match.
<p>If the single-token deletion is successful, this method calls #reportUnwantedToken to report the error, followed by Parser#consume to actually “delete” the extraneous token. Then, before returning #reportMatch is called to signal a successful match.</p>
deletion successfully recovers from the mismatched input, otherwise null
486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 |
# File 'lib/antlr4/error/ErrorStrategy.rb', line 486 def singleTokenDeletion(recognizer) nextTokenType = recognizer.getTokenStream().LA(2) expecting = self.getExpectedTokens(recognizer) if expecting.member? nextTokenType then self.reportUnwantedToken(recognizer) # print("recoverFromMismatchedToken deleting " \ # + str(recognizer.getTokenStream().LT(1)) \ # + " since " + str(recognizer.getTokenStream().LT(2)) \ # + " is what we want", file=sys.stderr) recognizer.consume() # simply delete extra token # we want to return the token we're actually matching matchedSymbol = recognizer.getCurrentToken() self.reportMatch(recognizer) # we know current token is correct return matchedSymbol else return nil end end |
#singleTokenInsertion(recognizer) ⇒ @code true
This method implements the single-token insertion inline error recovery strategy. It is called by #recoverInline if the single-token deletion strategy fails to recover from the mismatched input. If this method returns true, recognizer will be in error recovery mode.
<p>This method determines whether or not single-token insertion is viable by checking if the LA(1) input symbol could be successfully matched if it were instead the LA(2) symbol. If this method returns true, the caller is responsible for creating and inserting a token with the correct type to produce this behavior.</p>
strategy for the current mismatched input, otherwise false
452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 |
# File 'lib/antlr4/error/ErrorStrategy.rb', line 452 def singleTokenInsertion(recognizer) currentSymbolType = recognizer.getTokenStream().LA(1) # 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 atn = recognizer.interp.atn currentState = atn.states[recognizer.state] nextToken = currentState.transitions[0].target expectingAtLL2 = atn.nextTokens(nextToken, recognizer.ctx) if expectingAtLL2.member? currentSymbolType then self.reportMissingToken(recognizer) return true else return false end end |
#sync(recognizer) ⇒ Object
The default implementation of ANTLRErrorStrategy#sync 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>
<pre> a : sync ( stuff sync )* ; sync : to what can follow sync ; </pre>
At the start of a sub rule upon error, #sync 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 ((…)?, (…)*, 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 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. 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>
<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 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>
229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 |
# File 'lib/antlr4/error/ErrorStrategy.rb', line 229 def sync(recognizer) # If already recovering, don't try to sync return if self.inErrorRecoveryMode(recognizer) s = recognizer.interp.atn.states[recognizer.state] la = recognizer.getTokenStream().LA(1) # try cheaper subset first; might get lucky. seems to shave a wee bit off if la==Token::EOF or recognizer.atn.nextTokens(s).member? la return end # Return but don't end recovery. only do that upon valid token match if recognizer.isExpectedToken(la) then return end possibleStates = [ATNState::BLOCK_START, ATNState::STAR_BLOCK_START, ATNState::PLUS_BLOCK_START, ATNState::STAR_LOOP_ENTRY] if possibleStates.member? s.stateType then # report error and recover if possible if self.singleTokenDeletion(recognizer).nil? raise InputMismatchException.new(recognizer) else return end elsif [ATNState::PLUS_LOOP_BACK, ATNState::STAR_LOOP_BACK].member? s.stateType then self.reportUnwantedToken(recognizer) expecting = recognizer.getExpectedTokens() whatFollowsLoopIterationOrRule = expecting.addSet(self.getErrorRecoverySet(recognizer)) self.consumeUntil(recognizer, whatFollowsLoopIterationOrRule) # else # # do nothing if we can't identify the exact kind of ATN state end end |