1<?php
2
3declare(strict_types=1);
4
5namespace Antlr\Antlr4\Runtime\Error;
6
7use Antlr\Antlr4\Runtime\Error\Exceptions\InputMismatchException;
8use Antlr\Antlr4\Runtime\Error\Exceptions\ParseCancellationException;
9use Antlr\Antlr4\Runtime\Error\Exceptions\RecognitionException;
10use Antlr\Antlr4\Runtime\Parser;
11use Antlr\Antlr4\Runtime\ParserRuleContext;
12use Antlr\Antlr4\Runtime\Token;
13
14/**
15 * This implementation of {@see ANTLRErrorStrategy} responds to syntax errors
16 * by immediately canceling the parse operation with a
17 * {@see ParseCancellationException}. The implementation ensures that the
18 * {@see ParserRuleContext::$exception} field is set for all parse tree nodes
19 * that were not completed prior to encountering the error.
20 *
21 * This error strategy is useful in the following scenarios.
22 *
23 * - Two-stage parsing: This error strategy allows the first stage of two-stage
24 *    parsing to immediately terminate if an error is encountered, and immediately
25 *    fall back to the second stage. In addition to avoiding wasted work by
26 *    attempting to recover from errors here, the empty implementation of
27 *    {@see BailErrorStrategy::sync()} improves the performance of the first stage.
28 * - Silent validation: When syntax errors are not being reported or logged,
29 *    and the parse result is simply ignored if errors occur, the
30 *    {@see BailErrorStrategy} avoids wasting work on recovering from errors
31 *    when the result will be ignored either way.</li>
32 *
33 * `$myparser->setErrorHandler(new BailErrorStrategy());`
34 *
35 * @see Parser::setErrorHandler()
36 */
37class BailErrorStrategy extends DefaultErrorStrategy
38{
39    /**
40     * Instead of recovering from exception `e`, re-throw it wrapped
41     * in a {@see ParseCancellationException} so it is not caught by the
42     * rule function catches. Use {@see Exception::getCause()} to get the
43     * original {@see RecognitionException}.
44     */
45    public function recover(Parser $recognizer, RecognitionException $e) : void
46    {
47        $context = $recognizer->getContext();
48
49        while ($context !== null) {
50            if (!$context instanceof ParserRuleContext) {
51                throw new \RuntimeException('Unexpected context type.');
52            }
53
54            $context->exception = $e;
55            $context = $context->getParent();
56        }
57
58        throw ParseCancellationException::from($e);
59    }
60
61    /**
62     * Make sure we don't attempt to recover inline; if the parser successfully
63     * recovers, it won't throw an exception.
64     *
65     * @throws ParseCancellationException
66     */
67    public function recoverInline(Parser $recognizer) : Token
68    {
69        $e = new InputMismatchException($recognizer);
70
71        for ($context = $recognizer->getContext(); $context; $context = $context->getParent()) {
72            if (!$context instanceof ParserRuleContext) {
73                throw new \RuntimeException('Unexpected context type.');
74            }
75
76            $context->exception = $e;
77        }
78
79        throw ParseCancellationException::from($e);
80    }
81
82    /**
83     * Make sure we don't attempt to recover from problems in subrules.
84     */
85    public function sync(Parser $recognizer) : void
86    {
87    }
88}
89