0
# Error Handling
1
2
ANTLR4 provides a comprehensive error handling system with customizable recovery strategies, detailed error reporting, and multiple approaches to handling syntax errors during parsing.
3
4
## Capabilities
5
6
### Error Listener Interface
7
8
Interface for receiving and handling syntax errors.
9
10
```java { .api }
11
/**
12
* Interface for handling syntax errors during parsing
13
*/
14
public interface ANTLRErrorListener {
15
/**
16
* Called when parser encounters a syntax error
17
* @param recognizer the parser or lexer that encountered the error
18
* @param offendingSymbol the problematic token or character
19
* @param line the line number where error occurred
20
* @param charPositionInLine the character position in line
21
* @param msg error message
22
* @param e recognition exception (may be null)
23
*/
24
void syntaxError(Recognizer<?, ?> recognizer,
25
Object offendingSymbol,
26
int line,
27
int charPositionInLine,
28
String msg,
29
RecognitionException e);
30
}
31
32
/**
33
* Base error listener providing empty implementations
34
*/
35
public class BaseErrorListener implements ANTLRErrorListener {
36
@Override
37
public void syntaxError(Recognizer<?, ?> recognizer,
38
Object offendingSymbol,
39
int line,
40
int charPositionInLine,
41
String msg,
42
RecognitionException e) {
43
// Empty implementation - override in subclasses
44
}
45
}
46
47
/**
48
* Console error listener that prints errors to stderr
49
*/
50
public class ConsoleErrorListener extends BaseErrorListener {
51
/** Singleton instance */
52
public static final ConsoleErrorListener INSTANCE = new ConsoleErrorListener();
53
54
@Override
55
public void syntaxError(Recognizer<?, ?> recognizer,
56
Object offendingSymbol,
57
int line,
58
int charPositionInLine,
59
String msg,
60
RecognitionException e) {
61
System.err.println("line " + line + ":" + charPositionInLine + " " + msg);
62
}
63
}
64
```
65
66
**Usage Example:**
67
68
```java
69
import org.antlr.v4.runtime.*;
70
71
// Custom error listener
72
class MyErrorListener extends BaseErrorListener {
73
@Override
74
public void syntaxError(Recognizer<?, ?> recognizer,
75
Object offendingSymbol,
76
int line,
77
int charPositionInLine,
78
String msg,
79
RecognitionException e) {
80
System.err.println("Syntax error at " + line + ":" + charPositionInLine);
81
System.err.println("Message: " + msg);
82
83
if (offendingSymbol instanceof Token) {
84
Token token = (Token) offendingSymbol;
85
System.err.println("Problematic token: '" + token.getText() + "'");
86
}
87
}
88
}
89
90
// Add custom error listener to parser
91
MyParser parser = new MyParser(tokens);
92
parser.removeErrorListeners(); // Remove default console listener
93
parser.addErrorListener(new MyErrorListener());
94
```
95
96
### Error Recovery Strategies
97
98
Interfaces and implementations for error recovery during parsing.
99
100
```java { .api }
101
/**
102
* Interface for error recovery strategies
103
*/
104
public interface ANTLRErrorStrategy {
105
/** Reset strategy state */
106
void reset(Parser recognizer);
107
108
/** Recover from token mismatch */
109
Token recoverInline(Parser recognizer) throws RecognitionException;
110
111
/** Recover from recognition exception */
112
void recover(Parser recognizer, RecognitionException e) throws RecognitionException;
113
114
/** Synchronize parser after error */
115
void sync(Parser recognizer) throws RecognitionException;
116
117
/** Check if in error recovery mode */
118
boolean inErrorRecoveryMode(Parser recognizer);
119
120
/** Report error match */
121
void reportMatch(Parser recognizer);
122
123
/** Report error */
124
void reportError(Parser recognizer, RecognitionException e);
125
}
126
127
/**
128
* Default error recovery strategy with sync and single token insertion/deletion
129
*/
130
public class DefaultErrorStrategy implements ANTLRErrorStrategy {
131
/** Whether in error recovery mode */
132
protected boolean errorRecoveryMode = false;
133
134
/** Last error index to avoid infinite loops */
135
protected int lastErrorIndex = -1;
136
137
/** IntervalSet of tokens for synchronization */
138
protected IntervalSet lastErrorStates;
139
140
@Override
141
public void reset(Parser recognizer) {
142
endErrorCondition(recognizer);
143
}
144
145
@Override
146
public Token recoverInline(Parser recognizer) throws RecognitionException {
147
// Try single token deletion
148
Token matchedSymbol = singleTokenDeletion(recognizer);
149
if (matchedSymbol != null) {
150
recognizer.consume();
151
return matchedSymbol;
152
}
153
154
// Try single token insertion
155
if (singleTokenInsertion(recognizer)) {
156
return getMissingSymbol(recognizer);
157
}
158
159
// Fall back to more complex recovery
160
throw new InputMismatchException(recognizer);
161
}
162
163
@Override
164
public void recover(Parser recognizer, RecognitionException e) throws RecognitionException {
165
if (lastErrorIndex == recognizer.getInputStream().index() &&
166
lastErrorStates != null &&
167
lastErrorStates.contains(recognizer.getState())) {
168
// Consume at least one token to avoid infinite loops
169
recognizer.consume();
170
}
171
172
lastErrorIndex = recognizer.getInputStream().index();
173
if (lastErrorStates == null) {
174
lastErrorStates = new IntervalSet();
175
}
176
lastErrorStates.add(recognizer.getState());
177
178
IntervalSet followSet = getErrorRecoverySet(recognizer);
179
consumeUntil(recognizer, followSet);
180
}
181
182
@Override
183
public void sync(Parser recognizer) throws RecognitionException {
184
ATNState s = recognizer.getInterpreter().atn.states.get(recognizer.getState());
185
if (inErrorRecoveryMode(recognizer)) {
186
return;
187
}
188
189
TokenStream tokens = recognizer.getInputStream();
190
int la = tokens.LA(1);
191
192
if (recognizer.getATN().nextTokens(s).contains(la)) {
193
return;
194
}
195
196
switch (s.getStateType()) {
197
case ATNState.BLOCK_START:
198
case ATNState.STAR_BLOCK_START:
199
case ATNState.PLUS_BLOCK_START:
200
case ATNState.STAR_LOOP_ENTRY:
201
if (singleTokenDeletion(recognizer) != null) {
202
return;
203
}
204
throw new InputMismatchException(recognizer);
205
206
default:
207
// Nothing to sync on
208
break;
209
}
210
}
211
212
/** End error condition and exit recovery mode */
213
protected void endErrorCondition(Parser recognizer) {
214
errorRecoveryMode = false;
215
lastErrorStates = null;
216
lastErrorIndex = -1;
217
}
218
219
/** Enter error recovery mode */
220
protected void beginErrorCondition(Parser recognizer) {
221
errorRecoveryMode = true;
222
}
223
224
/** Get missing symbol for recovery */
225
protected Token getMissingSymbol(Parser recognizer) {
226
Token currentSymbol = recognizer.getCurrentToken();
227
IntervalSet expecting = getExpectedTokens(recognizer);
228
int expectedTokenType = expecting.getMinElement();
229
230
String tokenText;
231
if (expectedTokenType == Token.EOF) {
232
tokenText = "<missing EOF>";
233
} else {
234
tokenText = "<missing " + recognizer.getVocabulary().getDisplayName(expectedTokenType) + ">";
235
}
236
237
Token current = currentSymbol;
238
Token lookback = recognizer.getInputStream().LT(-1);
239
if (current.getType() == Token.EOF && lookback != null) {
240
current = lookback;
241
}
242
243
return recognizer.getTokenFactory().create(
244
new Pair<>(current.getTokenSource(), current.getInputStream()),
245
expectedTokenType, tokenText,
246
Token.DEFAULT_CHANNEL,
247
-1, -1,
248
current.getLine(), current.getCharPositionInLine()
249
);
250
}
251
}
252
253
/**
254
* Error strategy that bails out on first error (fail-fast)
255
*/
256
public class BailErrorStrategy extends DefaultErrorStrategy {
257
@Override
258
public void recover(Parser recognizer, RecognitionException e) throws RecognitionException {
259
// Don't recover; rethrow exception
260
for (ParserRuleContext context = recognizer.getContext();
261
context != null;
262
context = context.getParent()) {
263
context.exception = e;
264
}
265
throw new ParseCancellationException(e);
266
}
267
268
@Override
269
public Token recoverInline(Parser recognizer) throws RecognitionException {
270
InputMismatchException e = new InputMismatchException(recognizer);
271
for (ParserRuleContext context = recognizer.getContext();
272
context != null;
273
context = context.getParent()) {
274
context.exception = e;
275
}
276
throw new ParseCancellationException(e);
277
}
278
279
@Override
280
public void sync(Parser recognizer) throws RecognitionException {
281
// Don't sync; let exceptions bubble up
282
}
283
}
284
```
285
286
**Usage Example:**
287
288
```java
289
import org.antlr.v4.runtime.*;
290
291
// Use default error recovery
292
MyParser parser = new MyParser(tokens);
293
// Default strategy is already set
294
295
// Use bail error strategy (fail-fast)
296
MyParser parser2 = new MyParser(tokens);
297
parser2.setErrorHandler(new BailErrorStrategy());
298
299
try {
300
ParseTree tree = parser2.expr();
301
} catch (ParseCancellationException e) {
302
System.err.println("Parse failed: " + e.getCause().getMessage());
303
}
304
```
305
306
### Recognition Exceptions
307
308
Exception hierarchy for different types of parsing errors.
309
310
```java { .api }
311
/**
312
* Base class for all recognition exceptions
313
*/
314
public class RecognitionException extends RuntimeException {
315
/** Parser or lexer that threw the exception */
316
private final Recognizer<?, ?> recognizer;
317
318
/** Rule context where exception occurred */
319
private final RuleContext ctx;
320
321
/** Input stream where exception occurred */
322
private final IntStream input;
323
324
/** Offending token or character */
325
private final Object offendingToken;
326
327
/** Position information */
328
private final int offendingState;
329
330
public RecognitionException(Recognizer<?, ?> recognizer,
331
IntStream input,
332
ParserRuleContext ctx) {
333
this.recognizer = recognizer;
334
this.input = input;
335
this.ctx = ctx;
336
if (recognizer != null) {
337
this.offendingState = recognizer.getState();
338
} else {
339
this.offendingState = -1;
340
}
341
}
342
343
/** Get expected tokens at point of error */
344
public IntervalSet getExpectedTokens() {
345
if (recognizer != null) {
346
return recognizer.getATN().getExpectedTokens(offendingState, ctx);
347
}
348
return IntervalSet.EMPTY_SET;
349
}
350
351
/** Get rule context */
352
public RuleContext getCtx() {
353
return ctx;
354
}
355
356
/** Get input stream */
357
public IntStream getInputStream() {
358
return input;
359
}
360
361
/** Get offending token */
362
public Object getOffendingToken() {
363
return offendingToken;
364
}
365
366
/** Get recognizer */
367
public Recognizer<?, ?> getRecognizer() {
368
return recognizer;
369
}
370
}
371
372
/**
373
* Exception for token mismatch errors
374
*/
375
public class InputMismatchException extends RecognitionException {
376
public InputMismatchException(Parser recognizer) {
377
super(recognizer, recognizer.getInputStream(), recognizer.getContext());
378
setOffendingToken(recognizer.getCurrentToken());
379
}
380
381
public InputMismatchException(Parser recognizer, int state, ParserRuleContext ctx) {
382
super(recognizer, recognizer.getInputStream(), ctx);
383
setOffendingState(state);
384
setOffendingToken(recognizer.getCurrentToken());
385
}
386
}
387
388
/**
389
* Exception when lexer cannot match input
390
*/
391
public class LexerNoViableAltException extends RecognitionException {
392
/** Start index of problematic text */
393
private final int startIndex;
394
395
/** Dead-end configurations */
396
private final ATNConfigSet deadEndConfigs;
397
398
public LexerNoViableAltException(Lexer lexer,
399
CharStream input,
400
int startIndex,
401
ATNConfigSet deadEndConfigs) {
402
super(lexer, input, null);
403
this.startIndex = startIndex;
404
this.deadEndConfigs = deadEndConfigs;
405
}
406
407
public int getStartIndex() {
408
return startIndex;
409
}
410
411
public ATNConfigSet getDeadEndConfigs() {
412
return deadEndConfigs;
413
}
414
}
415
416
/**
417
* Exception when parser cannot choose alternative
418
*/
419
public class NoViableAltException extends RecognitionException {
420
/** Dead-end configurations */
421
private final ATNConfigSet deadEndConfigs;
422
423
/** Start token */
424
private final Token startToken;
425
426
public NoViableAltException(Parser recognizer) {
427
this(recognizer, recognizer.getInputStream(), recognizer.getCurrentToken(),
428
recognizer.getCurrentToken(), null, recognizer.getContext());
429
}
430
431
public NoViableAltException(Parser recognizer,
432
TokenStream input,
433
Token startToken,
434
Token offendingToken,
435
ATNConfigSet deadEndConfigs,
436
ParserRuleContext ctx) {
437
super(recognizer, input, ctx);
438
this.deadEndConfigs = deadEndConfigs;
439
this.startToken = startToken;
440
setOffendingToken(offendingToken);
441
}
442
443
public Token getStartToken() {
444
return startToken;
445
}
446
447
public ATNConfigSet getDeadEndConfigs() {
448
return deadEndConfigs;
449
}
450
}
451
452
/**
453
* Exception when semantic predicate fails
454
*/
455
public class FailedPredicateException extends RecognitionException {
456
/** Rule index */
457
private final int ruleIndex;
458
459
/** Predicate index */
460
private final int predicateIndex;
461
462
/** Predicate text */
463
private final String predicate;
464
465
public FailedPredicateException(Parser recognizer) {
466
this(recognizer, null);
467
}
468
469
public FailedPredicateException(Parser recognizer, String predicate) {
470
this(recognizer, predicate, null);
471
}
472
473
public FailedPredicateException(Parser recognizer, String predicate, String message) {
474
super(formatMessage(predicate, message), recognizer,
475
recognizer.getInputStream(), recognizer.getContext());
476
477
ATNState s = recognizer.getInterpreter().atn.states.get(recognizer.getState());
478
AbstractPredicateTransition trans = (AbstractPredicateTransition) s.transition(0);
479
if (trans instanceof PredicateTransition) {
480
this.ruleIndex = ((PredicateTransition) trans).ruleIndex;
481
this.predicateIndex = ((PredicateTransition) trans).predIndex;
482
} else {
483
this.ruleIndex = 0;
484
this.predicateIndex = 0;
485
}
486
487
this.predicate = predicate;
488
setOffendingToken(recognizer.getCurrentToken());
489
}
490
491
public int getRuleIndex() {
492
return ruleIndex;
493
}
494
495
public int getPredicateIndex() {
496
return predicateIndex;
497
}
498
499
public String getPredicate() {
500
return predicate;
501
}
502
}
503
```
504
505
### Diagnostic Error Listener
506
507
Advanced error listener with detailed diagnostic information.
508
509
```java { .api }
510
/**
511
* Error listener that provides detailed diagnostic information
512
*/
513
public class DiagnosticErrorListener extends BaseErrorListener {
514
/** Whether to report ambiguities */
515
protected final boolean exactOnly;
516
517
public DiagnosticErrorListener() {
518
this(true);
519
}
520
521
public DiagnosticErrorListener(boolean exactOnly) {
522
this.exactOnly = exactOnly;
523
}
524
525
@Override
526
public void syntaxError(Recognizer<?, ?> recognizer,
527
Object offendingSymbol,
528
int line,
529
int charPositionInLine,
530
String msg,
531
RecognitionException e) {
532
System.err.println("Syntax error at " + line + ":" + charPositionInLine + ": " + msg);
533
534
if (e != null) {
535
System.err.println("Exception type: " + e.getClass().getSimpleName());
536
537
if (e instanceof InputMismatchException) {
538
IntervalSet expected = e.getExpectedTokens();
539
System.err.println("Expected: " + expected.toString(recognizer.getVocabulary()));
540
} else if (e instanceof NoViableAltException) {
541
NoViableAltException nvae = (NoViableAltException) e;
542
System.err.println("No viable alternative");
543
System.err.println("Start token: " + nvae.getStartToken().getText());
544
} else if (e instanceof FailedPredicateException) {
545
FailedPredicateException fpe = (FailedPredicateException) e;
546
System.err.println("Failed predicate: " + fpe.getPredicate());
547
}
548
}
549
}
550
551
/** Report ambiguity in parse */
552
public void reportAmbiguity(Parser recognizer,
553
DFA dfa,
554
int startIndex,
555
int stopIndex,
556
boolean exact,
557
BitSet ambigAlts,
558
ATNConfigSet configs) {
559
if (exactOnly && !exact) {
560
return;
561
}
562
563
System.err.println("Ambiguity from " + startIndex + " to " + stopIndex);
564
System.err.println("Alternatives: " + ambigAlts);
565
}
566
567
/** Report attempt to use full context */
568
public void reportAttemptingFullContext(Parser recognizer,
569
DFA dfa,
570
int startIndex,
571
int stopIndex,
572
BitSet conflictingAlts,
573
ATNConfigSet configs) {
574
System.err.println("Attempting full context from " + startIndex + " to " + stopIndex);
575
}
576
577
/** Report context sensitivity */
578
public void reportContextSensitivity(Parser recognizer,
579
DFA dfa,
580
int startIndex,
581
int stopIndex,
582
int prediction,
583
ATNConfigSet configs) {
584
System.err.println("Context sensitivity from " + startIndex + " to " + stopIndex);
585
}
586
}
587
```
588
589
## Usage Patterns
590
591
### Basic Error Handling
592
593
```java
594
import org.antlr.v4.runtime.*;
595
596
// Simple error collection
597
List<String> errors = new ArrayList<>();
598
599
MyParser parser = new MyParser(tokens);
600
parser.removeErrorListeners();
601
parser.addErrorListener(new BaseErrorListener() {
602
@Override
603
public void syntaxError(Recognizer<?, ?> recognizer,
604
Object offendingSymbol,
605
int line,
606
int charPositionInLine,
607
String msg,
608
RecognitionException e) {
609
errors.add("Line " + line + ":" + charPositionInLine + " - " + msg);
610
}
611
});
612
613
ParseTree tree = parser.expr();
614
615
if (!errors.isEmpty()) {
616
System.err.println("Parse errors:");
617
for (String error : errors) {
618
System.err.println(" " + error);
619
}
620
}
621
```
622
623
### Advanced Error Recovery
624
625
```java
626
import org.antlr.v4.runtime.*;
627
628
// Custom error strategy with specific recovery
629
class MyErrorStrategy extends DefaultErrorStrategy {
630
@Override
631
protected void reportNoViableAlternative(Parser recognizer, NoViableAltException e) {
632
TokenStream tokens = recognizer.getInputStream();
633
String input;
634
if (tokens != null) {
635
if (e.getStartToken().getType() == Token.EOF) {
636
input = "<EOF>";
637
} else {
638
input = tokens.getText(e.getStartToken(), e.getOffendingToken());
639
}
640
} else {
641
input = "<unknown input>";
642
}
643
644
String msg = "no viable alternative at input " + escapeWSAndQuote(input);
645
recognizer.notifyErrorListeners(e.getOffendingToken(), msg, e);
646
}
647
648
@Override
649
protected void reportInputMismatch(Parser recognizer, InputMismatchException e) {
650
String msg = "mismatched input " + getTokenErrorDisplay(e.getOffendingToken()) +
651
" expecting " + e.getExpectedTokens().toString(recognizer.getVocabulary());
652
recognizer.notifyErrorListeners(e.getOffendingToken(), msg, e);
653
}
654
}
655
656
// Use custom strategy
657
MyParser parser = new MyParser(tokens);
658
parser.setErrorHandler(new MyErrorStrategy());
659
```
660
661
### Comprehensive Error Reporting
662
663
```java
664
import org.antlr.v4.runtime.*;
665
666
// Comprehensive error information
667
class DetailedErrorListener extends BaseErrorListener {
668
@Override
669
public void syntaxError(Recognizer<?, ?> recognizer,
670
Object offendingSymbol,
671
int line,
672
int charPositionInLine,
673
String msg,
674
RecognitionException e) {
675
676
System.err.println("=== Syntax Error ===");
677
System.err.println("Location: " + line + ":" + charPositionInLine);
678
System.err.println("Message: " + msg);
679
680
if (offendingSymbol instanceof Token) {
681
Token token = (Token) offendingSymbol;
682
System.err.println("Offending token: '" + token.getText() +
683
"' (type: " + token.getType() + ")");
684
}
685
686
if (e != null) {
687
System.err.println("Exception: " + e.getClass().getSimpleName());
688
689
if (recognizer instanceof Parser) {
690
Parser parser = (Parser) recognizer;
691
System.err.println("Rule stack: " + parser.getRuleInvocationStack());
692
693
if (e.getExpectedTokens() != null) {
694
System.err.println("Expected tokens: " +
695
e.getExpectedTokens().toString(parser.getVocabulary()));
696
}
697
}
698
}
699
System.err.println("==================");
700
}
701
}
702
```