0
# Error Handling
1
2
Comprehensive exception hierarchy and error recovery mechanisms for robust parsing. ANTLR provides detailed error reporting with position information and flexible recovery strategies.
3
4
## Capabilities
5
6
### Recognition Exception Hierarchy
7
8
Base exception class for all parsing and lexical analysis errors.
9
10
```java { .api }
11
/**
12
* Base class for all recognition exceptions
13
*/
14
public class RecognitionException extends Exception {
15
/** What input stream were we parsing that triggered this exception?
16
* This includes the current Token in a TokenStream or the current char
17
* in a CharStream.
18
*/
19
public IntStream input;
20
21
/** The current Token when an error occurred. Since not all streams
22
* can retrieve the ith Token, we have to track the Token object.
23
* For parsers. Even when it's a tree parser, token might be null.
24
*/
25
public Token token;
26
27
/** If this is a tree parser exception, node is set to the node with
28
* the problem.
29
*/
30
public Object node;
31
32
/** The current char when an error occurred. For lexers. */
33
public int c;
34
35
/** Track the line at which the error occurred in case this is
36
* generated from a lexer. We need to track this since the
37
* unexpected char doesn't carry the line info.
38
*/
39
public int line;
40
41
public int charPositionInLine;
42
43
/** If you are parsing a tree node stream, you will encounter som
44
* imaginary nodes but not others. We distinguish.
45
*/
46
public boolean approximateLineInfo;
47
48
/** Used for remote debugger deserialization */
49
public RecognitionException();
50
51
public RecognitionException(IntStream input);
52
53
/** Extract the unexpectedTokenType from the token */
54
protected int getUnexpectedType();
55
}
56
```
57
58
### Token Mismatch Exceptions
59
60
Exceptions for various token matching failures.
61
62
```java { .api }
63
/**
64
* Exception for token mismatch errors
65
*/
66
public class MismatchedTokenException extends RecognitionException {
67
public int expecting;
68
69
public MismatchedTokenException();
70
public MismatchedTokenException(int expecting, IntStream input);
71
72
public String toString();
73
}
74
75
/**
76
* Exception for missing expected token
77
*/
78
public class MissingTokenException extends MismatchedTokenException {
79
/** Used for remote debugger deserialization */
80
public MissingTokenException();
81
82
public MissingTokenException(int expecting, IntStream input, Object inserted);
83
84
public int getMissingType();
85
86
public String toString();
87
}
88
89
/**
90
* Exception for unwanted extra token
91
*/
92
public class UnwantedTokenException extends MismatchedTokenException {
93
public UnwantedTokenException();
94
public UnwantedTokenException(int expecting, IntStream input);
95
96
public Token getUnexpectedToken();
97
98
public String toString();
99
}
100
```
101
102
**Usage Examples:**
103
104
```java
105
import org.antlr.runtime.*;
106
107
// Catching specific token exceptions
108
try {
109
MyParser parser = new MyParser(tokens);
110
parser.program();
111
} catch (MismatchedTokenException e) {
112
System.err.println("Expected token: " + e.expecting +
113
" but found: " + e.getUnexpectedType());
114
System.err.println("At line: " + e.line +
115
" position: " + e.charPositionInLine);
116
} catch (MissingTokenException e) {
117
System.err.println("Missing token of type: " + e.getMissingType());
118
} catch (UnwantedTokenException e) {
119
System.err.println("Unwanted token: " + e.getUnexpectedToken().getText());
120
}
121
```
122
123
### Set Mismatch Exceptions
124
125
Exceptions for token set matching failures.
126
127
```java { .api }
128
/**
129
* Exception for set mismatch errors
130
*/
131
public class MismatchedSetException extends MismatchedTokenException {
132
public BitSet expecting;
133
134
public MismatchedSetException();
135
public MismatchedSetException(BitSet expecting, IntStream input);
136
137
public String toString();
138
}
139
140
/**
141
* Exception for negated set mismatch errors
142
*/
143
public class MismatchedNotSetException extends MismatchedSetException {
144
public MismatchedNotSetException();
145
public MismatchedNotSetException(BitSet expecting, IntStream input);
146
147
public String toString();
148
}
149
```
150
151
**Usage Examples:**
152
153
```java
154
// Handling set-based matching errors
155
try {
156
parser.statement();
157
} catch (MismatchedSetException e) {
158
System.err.println("Expected one of: " + e.expecting.toString(tokenNames));
159
System.err.println("But found: " + e.token.getText());
160
} catch (MismatchedNotSetException e) {
161
System.err.println("Did not expect: " + e.token.getText());
162
System.err.println("Forbidden tokens: " + e.expecting.toString(tokenNames));
163
}
164
```
165
166
### Alternative and Range Exceptions
167
168
Exceptions for alternative selection and character range failures.
169
170
```java { .api }
171
/**
172
* Exception when no alternative matches
173
*/
174
public class NoViableAltException extends RecognitionException {
175
public int decisionNumber;
176
public int stateNumber;
177
178
public NoViableAltException();
179
public NoViableAltException(String grammarDecisionDescription,
180
int decisionNumber,
181
int stateNumber,
182
IntStream input);
183
184
public String toString();
185
}
186
187
/**
188
* Exception for character range mismatch
189
*/
190
public class MismatchedRangeException extends RecognitionException {
191
public int a;
192
public int b;
193
194
public MismatchedRangeException();
195
public MismatchedRangeException(int a, int b, IntStream input);
196
197
public String toString();
198
}
199
200
/**
201
* Exception for early exit from loops
202
*/
203
public class EarlyExitException extends RecognitionException {
204
public int decisionNumber;
205
206
public EarlyExitException();
207
public EarlyExitException(int decisionNumber, IntStream input);
208
209
public String toString();
210
}
211
```
212
213
**Usage Examples:**
214
215
```java
216
// Handling decision-making exceptions
217
try {
218
parser.expression();
219
} catch (NoViableAltException e) {
220
System.err.println("No viable alternative at decision " + e.decisionNumber);
221
System.err.println("State: " + e.stateNumber);
222
System.err.println("Input: " + e.token.getText());
223
} catch (EarlyExitException e) {
224
System.err.println("Early exit from decision " + e.decisionNumber);
225
System.err.println("At least one iteration required");
226
}
227
```
228
229
### Predicate and Tree Exceptions
230
231
Exceptions for semantic predicates and tree parsing failures.
232
233
```java { .api }
234
/**
235
* Exception for failed predicate evaluation
236
*/
237
public class FailedPredicateException extends RecognitionException {
238
public String ruleName;
239
public String predicateText;
240
241
public FailedPredicateException();
242
public FailedPredicateException(IntStream input, String ruleName, String predicateText);
243
244
public String toString();
245
}
246
247
/**
248
* Exception for tree node mismatch
249
*/
250
public class MismatchedTreeNodeException extends RecognitionException {
251
public int expecting;
252
253
public MismatchedTreeNodeException();
254
public MismatchedTreeNodeException(int expecting, TreeNodeStream input);
255
256
public String toString();
257
}
258
```
259
260
**Usage Examples:**
261
262
```java
263
// Handling semantic and tree parsing exceptions
264
try {
265
parser.constrainedRule();
266
} catch (FailedPredicateException e) {
267
System.err.println("Predicate failed in rule: " + e.ruleName);
268
System.err.println("Predicate: " + e.predicateText);
269
System.err.println("At line: " + e.line);
270
} catch (MismatchedTreeNodeException e) {
271
System.err.println("Expected tree node type: " + e.expecting);
272
System.err.println("But found: " + e.getUnexpectedType());
273
}
274
```
275
276
## Error Recovery Strategies
277
278
### Single Token Recovery
279
280
Attempt to recover by inserting or deleting a single token.
281
282
```java { .api }
283
// In BaseRecognizer
284
protected Object recoverFromMismatchedToken(IntStream input, int ttype, BitSet follow)
285
throws RecognitionException {
286
287
// Try single token deletion
288
if (mismatchIsUnwantedToken(input, ttype)) {
289
UnwantedTokenException e = new UnwantedTokenException(ttype, input);
290
beginResync();
291
input.consume(); // Delete current token
292
endResync();
293
reportError(e);
294
Object matchedSymbol = getCurrentInputSymbol(input);
295
input.consume(); // Move past the token we are recovering from
296
return matchedSymbol;
297
}
298
299
// Try single token insertion
300
if (mismatchIsMissingToken(input, follow)) {
301
Object inserted = getMissingSymbol(input, null, ttype, follow);
302
MissingTokenException e = new MissingTokenException(ttype, input, inserted);
303
reportError(e);
304
return inserted;
305
}
306
307
// Could not recover
308
throw new MismatchedTokenException(ttype, input);
309
}
310
```
311
312
**Usage Examples:**
313
314
```java
315
// Custom single token recovery
316
public class MyParser extends Parser {
317
@Override
318
protected Object recoverFromMismatchedToken(IntStream input, int ttype, BitSet follow)
319
throws RecognitionException {
320
321
// Log recovery attempt
322
System.err.println("Attempting single token recovery for type: " + ttype);
323
324
// Try standard recovery
325
try {
326
return super.recoverFromMismatchedToken(input, ttype, follow);
327
} catch (RecognitionException e) {
328
// Custom recovery logic
329
System.err.println("Standard recovery failed, using custom strategy");
330
throw e;
331
}
332
}
333
}
334
```
335
336
### Panic Mode Recovery
337
338
Skip tokens until a safe recovery point is found.
339
340
```java { .api }
341
// In BaseRecognizer
342
public void recover(IntStream input, RecognitionException re) {
343
if (state.lastErrorIndex == input.index()) {
344
// Ensure we consume at least one token to avoid infinite loops
345
input.consume();
346
}
347
state.lastErrorIndex = input.index();
348
349
BitSet followSet = computeErrorRecoverySet();
350
beginResync();
351
consumeUntil(input, followSet);
352
endResync();
353
}
354
355
protected void consumeUntil(IntStream input, BitSet set) {
356
int ttype = input.LA(1);
357
while (ttype != Token.EOF && !set.member(ttype)) {
358
input.consume();
359
ttype = input.LA(1);
360
}
361
}
362
```
363
364
**Usage Examples:**
365
366
```java
367
// Custom panic mode recovery
368
public class MyParser extends Parser {
369
@Override
370
public void recover(IntStream input, RecognitionException re) {
371
System.err.println("Entering panic mode recovery");
372
373
// Create custom recovery set
374
BitSet recoverySet = new BitSet();
375
recoverySet.add(SEMICOLON);
376
recoverySet.add(RBRACE);
377
recoverySet.add(EOF);
378
379
// Skip until recovery token
380
beginResync();
381
while (input.LA(1) != Token.EOF && !recoverySet.member(input.LA(1))) {
382
System.err.println("Skipping: " + input.LT(1).getText());
383
input.consume();
384
}
385
endResync();
386
387
System.err.println("Recovery complete at: " + input.LT(1).getText());
388
}
389
}
390
```
391
392
### Follow Set Computation
393
394
Calculate recovery points based on grammar structure.
395
396
```java { .api }
397
// In BaseRecognizer
398
protected BitSet computeErrorRecoverySet() {
399
return combineFollows(false);
400
}
401
402
protected BitSet computeContextSensitiveRuleFOLLOW() {
403
return combineFollows(true);
404
}
405
406
protected BitSet combineFollows(boolean exact) {
407
int top = state._fsp;
408
BitSet followSet = new BitSet();
409
410
for (int i = top; i >= 0; i--) {
411
BitSet localFollowSet = state.following[i];
412
followSet.or(localFollowSet);
413
414
if (exact && !localFollowSet.member(Token.EOR_TOKEN_TYPE)) {
415
break;
416
}
417
}
418
419
followSet.remove(Token.EOR_TOKEN_TYPE);
420
return followSet;
421
}
422
```
423
424
## Error Reporting
425
426
### Custom Error Messages
427
428
Override error message formatting for better user experience.
429
430
```java { .api }
431
// In BaseRecognizer
432
public String getErrorMessage(RecognitionException e, String[] tokenNames) {
433
String msg;
434
435
if (e instanceof MismatchedTokenException) {
436
MismatchedTokenException mte = (MismatchedTokenException)e;
437
msg = "mismatched input " + getTokenErrorDisplay(e.token) +
438
" expecting " + getTokenErrorDisplay(mte.expecting, tokenNames);
439
} else if (e instanceof MismatchedTreeNodeException) {
440
MismatchedTreeNodeException mtne = (MismatchedTreeNodeException)e;
441
msg = "mismatched tree node: " + mtne.node +
442
" expecting " + getTokenErrorDisplay(mtne.expecting, tokenNames);
443
} else if (e instanceof NoViableAltException) {
444
msg = "no viable alternative at input " + getTokenErrorDisplay(e.token);
445
} else if (e instanceof EarlyExitException) {
446
msg = "required (...)+ loop did not match anything at input " + getTokenErrorDisplay(e.token);
447
} else if (e instanceof MismatchedSetException) {
448
MismatchedSetException mse = (MismatchedSetException)e;
449
msg = "mismatched input " + getTokenErrorDisplay(e.token) +
450
" expecting set " + mse.expecting;
451
} else if (e instanceof MismatchedNotSetException) {
452
MismatchedNotSetException mse = (MismatchedNotSetException)e;
453
msg = "mismatched input " + getTokenErrorDisplay(e.token) +
454
" expecting set " + mse.expecting;
455
} else if (e instanceof FailedPredicateException) {
456
FailedPredicateException fpe = (FailedPredicateException)e;
457
msg = "rule " + fpe.ruleName + " " + fpe.predicateText;
458
} else {
459
msg = super.getErrorMessage(e, tokenNames);
460
}
461
462
return msg;
463
}
464
```
465
466
**Usage Examples:**
467
468
```java
469
// Custom error message formatting
470
public class MyParser extends Parser {
471
@Override
472
public String getErrorMessage(RecognitionException e, String[] tokenNames) {
473
StringBuilder buf = new StringBuilder();
474
475
// Add context information
476
buf.append("Parse error at line ").append(e.line);
477
buf.append(", column ").append(e.charPositionInLine).append(": ");
478
479
// Add specific error details
480
if (e instanceof MismatchedTokenException) {
481
MismatchedTokenException mte = (MismatchedTokenException)e;
482
buf.append("Expected '").append(tokenNames[mte.expecting]);
483
buf.append("' but found '").append(e.token.getText()).append("'");
484
} else {
485
buf.append(super.getErrorMessage(e, tokenNames));
486
}
487
488
// Add suggestions
489
buf.append("\nSuggestion: Check for missing semicolon or bracket");
490
491
return buf.toString();
492
}
493
494
@Override
495
public void displayRecognitionError(String[] tokenNames, RecognitionException e) {
496
String hdr = getErrorHeader(e);
497
String msg = getErrorMessage(e, tokenNames);
498
499
// Custom error display with colors/formatting
500
System.err.println("\u001B[31m" + hdr + " " + msg + "\u001B[0m");
501
502
// Show error context
503
if (e.token != null) {
504
showErrorContext(e.token);
505
}
506
}
507
508
private void showErrorContext(Token errorToken) {
509
// Display source line with error position highlighted
510
System.err.println("Source: " + getSourceLine(errorToken.getLine()));
511
System.err.print(" ");
512
for (int i = 0; i < errorToken.getCharPositionInLine(); i++) {
513
System.err.print(" ");
514
}
515
System.err.println("^");
516
}
517
}
518
```
519
520
### Error Listeners
521
522
Implement custom error handling strategies.
523
524
```java { .api }
525
public interface ErrorListener {
526
void syntaxError(Recognizer recognizer, Object offendingSymbol,
527
int line, int charPositionInLine, String msg, RecognitionException e);
528
}
529
530
// Custom error collector
531
public class ErrorCollector implements ErrorListener {
532
private List<SyntaxError> errors = new ArrayList<>();
533
534
@Override
535
public void syntaxError(Recognizer recognizer, Object offendingSymbol,
536
int line, int charPositionInLine, String msg, RecognitionException e) {
537
errors.add(new SyntaxError(line, charPositionInLine, msg, e));
538
}
539
540
public List<SyntaxError> getErrors() {
541
return errors;
542
}
543
544
public boolean hasErrors() {
545
return !errors.isEmpty();
546
}
547
}
548
```
549
550
## Common Patterns
551
552
### Robust Parser with Error Recovery
553
554
```java
555
public class RobustParser extends MyParser {
556
private List<String> errors = new ArrayList<>();
557
558
@Override
559
public void displayRecognitionError(String[] tokenNames, RecognitionException e) {
560
String msg = getErrorHeader(e) + " " + getErrorMessage(e, tokenNames);
561
errors.add(msg);
562
563
// Continue parsing after error
564
try {
565
recover(input, e);
566
} catch (Exception recovery) {
567
// Recovery failed, give up
568
throw new RuntimeException("Cannot recover from parse error", e);
569
}
570
}
571
572
public List<String> getErrors() {
573
return errors;
574
}
575
576
public boolean hasErrors() {
577
return !errors.isEmpty();
578
}
579
}
580
```
581
582
### Error-Tolerant Parsing
583
584
```java
585
// Parse with maximum error tolerance
586
public ParseResult parseWithErrors(String input) {
587
ANTLRStringStream stream = new ANTLRStringStream(input);
588
MyLexer lexer = new MyLexer(stream);
589
CommonTokenStream tokens = new CommonTokenStream(lexer);
590
RobustParser parser = new RobustParser(tokens);
591
592
Object tree = null;
593
try {
594
tree = parser.program().getTree();
595
} catch (RecognitionException e) {
596
// Even if parsing fails, collect partial results
597
System.err.println("Parsing failed but collected " +
598
parser.getErrors().size() + " errors");
599
}
600
601
return new ParseResult(tree, parser.getErrors());
602
}
603
```
604
605
### Incremental Error Recovery
606
607
```java
608
// Recovery that attempts multiple strategies
609
@Override
610
public void recover(IntStream input, RecognitionException re) {
611
// Try different recovery strategies in order
612
if (tryStatementRecovery(input, re)) return;
613
if (tryExpressionRecovery(input, re)) return;
614
if (tryBlockRecovery(input, re)) return;
615
616
// Fall back to standard recovery
617
super.recover(input, re);
618
}
619
620
private boolean tryStatementRecovery(IntStream input, RecognitionException re) {
621
BitSet stmtSet = new BitSet();
622
stmtSet.add(IF); stmtSet.add(WHILE); stmtSet.add(FOR);
623
stmtSet.add(SEMICOLON); stmtSet.add(RBRACE);
624
625
return consumeUntilSet(input, stmtSet);
626
}
627
```