or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

error-handling.mdindex.mdparse-tree-api.mdruntime-api.mdtool-api.mdutilities.md

error-handling.mddocs/

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

```