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