1<?php 2 3declare(strict_types=1); 4 5namespace Antlr\Antlr4\Runtime\Error\Exceptions; 6 7use Antlr\Antlr4\Runtime\IntervalSet; 8use Antlr\Antlr4\Runtime\IntStream; 9use Antlr\Antlr4\Runtime\ParserRuleContext; 10use Antlr\Antlr4\Runtime\Recognizer; 11use Antlr\Antlr4\Runtime\RuleContext; 12use Antlr\Antlr4\Runtime\Token; 13 14/** 15 * The root of the ANTLR exception hierarchy. In general, ANTLR tracks just 16 * 3 kinds of errors: prediction errors, failed predicate errors, and 17 * mismatched input errors. In each case, the parser knows where it is 18 * in the input, where it is in the ATN, the rule invocation stack, 19 * and what kind of problem occurred. 20 */ 21class RecognitionException extends \RuntimeException 22{ 23 /** 24 * The {@see Recognizer} where this exception originated. 25 * 26 * @var Recognizer|null 27 */ 28 private $recognizer; 29 30 /** @var RuleContext|null */ 31 private $ctx; 32 33 /** @var IntStream|null */ 34 private $input; 35 36 /** 37 * The current {@see Token} when an error occurred. Since not all streams 38 * support accessing symbols by index, we have to track the {@see Token} 39 * instance itself. 40 * 41 * @var Token|null 42 */ 43 private $offendingToken; 44 45 /** @var int */ 46 private $offendingState = -1; 47 48 public function __construct( 49 ?Recognizer $recognizer, 50 ?IntStream $input, 51 ?ParserRuleContext $ctx, 52 string $message = '' 53 ) { 54 parent::__construct($message); 55 56 $this->recognizer = $recognizer; 57 $this->input = $input; 58 $this->ctx = $ctx; 59 60 if ($this->recognizer !== null) { 61 $this->offendingState = $this->recognizer->getState(); 62 } 63 } 64 65 /** 66 * Get the ATN state number the parser was in at the time the error 67 * occurred. For {@see NoViableAltException} and 68 * {@see LexerNoViableAltException} exceptions, this is the 69 * {@see DecisionState} number. For others, it is the state whose outgoing 70 * edge we couldn't match. 71 * 72 * If the state number is not known, this method returns -1. 73 */ 74 public function getOffendingState() : int 75 { 76 return $this->offendingState; 77 } 78 79 public function setOffendingState(int $offendingState) : void 80 { 81 $this->offendingState = $offendingState; 82 } 83 84 /** 85 * If the state number is not known, this method returns -1. 86 * 87 * Gets the set of input symbols which could potentially follow the 88 * previously matched symbol at the time this exception was thrown. 89 * 90 * If the set of expected tokens is not known and could not be computed, 91 * this method returns `null`. 92 * 93 * @return IntervalSet|null The set of token types that could potentially follow 94 * the current state in the ATN, or `null` if 95 * the information is not available. 96 */ 97 public function getExpectedTokens() : ?IntervalSet 98 { 99 if ($this->recognizer === null) { 100 return null; 101 } 102 103 if ($this->ctx === null) { 104 throw new \RuntimeException('Unexpected null context.'); 105 } 106 107 return $this->recognizer->getATN()->getExpectedTokens($this->offendingState, $this->ctx); 108 } 109 110 /** 111 * Gets the {@see RuleContext} at the time this exception was thrown. 112 * 113 * If the context is not available, this method returns `null`. 114 * 115 * @return RuleContext|null The {@see RuleContext} at the time this exception 116 * was thrown. If the context is not available, this 117 * method returns `null`. 118 */ 119 public function getCtx() : ?RuleContext 120 { 121 return $this->ctx; 122 } 123 124 /** 125 * Gets the input stream which is the symbol source for the recognizer where 126 * this exception was thrown. 127 * 128 * If the input stream is not available, this method returns `null`. 129 * 130 * @return IntStream|null The input stream which is the symbol source for 131 * the recognizer where this exception was thrown, or 132 * `null` if the stream is not available. 133 */ 134 public function getInputStream() : ?IntStream 135 { 136 return $this->input; 137 } 138 139 public function getOffendingToken() : ?Token 140 { 141 return $this->offendingToken; 142 } 143 144 public function setOffendingToken(?Token $offendingToken) : void 145 { 146 $this->offendingToken = $offendingToken; 147 } 148 149 /** 150 * Gets the {@see Recognizer} where this exception occurred. 151 * 152 * If the recognizer is not available, this method returns `null`. 153 * 154 * @return Recognizer|null The recognizer where this exception occurred, or 155 * `null` if the recognizer is not available. 156 */ 157 public function getRecognizer() : ?Recognizer 158 { 159 return $this->recognizer; 160 } 161 162 public function __toString() : string 163 { 164 return $this->message; 165 } 166} 167