SetBased /
antlr-php-runtime
| 1 | <?php |
||||
| 2 | |||||
| 3 | declare(strict_types=1); |
||||
| 4 | |||||
| 5 | namespace Antlr\Antlr4\Runtime\Atn; |
||||
| 6 | |||||
| 7 | use Antlr\Antlr4\Runtime\Atn\Actions\LexerAction; |
||||
| 8 | use Antlr\Antlr4\Runtime\Atn\Actions\LexerActionType; |
||||
| 9 | use Antlr\Antlr4\Runtime\Atn\Actions\LexerChannelAction; |
||||
| 10 | use Antlr\Antlr4\Runtime\Atn\Actions\LexerCustomAction; |
||||
| 11 | use Antlr\Antlr4\Runtime\Atn\Actions\LexerModeAction; |
||||
| 12 | use Antlr\Antlr4\Runtime\Atn\Actions\LexerMoreAction; |
||||
| 13 | use Antlr\Antlr4\Runtime\Atn\Actions\LexerPopModeAction; |
||||
| 14 | use Antlr\Antlr4\Runtime\Atn\Actions\LexerPushModeAction; |
||||
| 15 | use Antlr\Antlr4\Runtime\Atn\Actions\LexerSkipAction; |
||||
| 16 | use Antlr\Antlr4\Runtime\Atn\Actions\LexerTypeAction; |
||||
| 17 | use Antlr\Antlr4\Runtime\Atn\States\ATNState; |
||||
| 18 | use Antlr\Antlr4\Runtime\Atn\States\BasicBlockStartState; |
||||
| 19 | use Antlr\Antlr4\Runtime\Atn\States\BasicState; |
||||
| 20 | use Antlr\Antlr4\Runtime\Atn\States\BlockEndState; |
||||
| 21 | use Antlr\Antlr4\Runtime\Atn\States\BlockStartState; |
||||
| 22 | use Antlr\Antlr4\Runtime\Atn\States\DecisionState; |
||||
| 23 | use Antlr\Antlr4\Runtime\Atn\States\LoopEndState; |
||||
| 24 | use Antlr\Antlr4\Runtime\Atn\States\PlusBlockStartState; |
||||
| 25 | use Antlr\Antlr4\Runtime\Atn\States\PlusLoopbackState; |
||||
| 26 | use Antlr\Antlr4\Runtime\Atn\States\RuleStartState; |
||||
| 27 | use Antlr\Antlr4\Runtime\Atn\States\RuleStopState; |
||||
| 28 | use Antlr\Antlr4\Runtime\Atn\States\StarBlockStartState; |
||||
| 29 | use Antlr\Antlr4\Runtime\Atn\States\StarLoopbackState; |
||||
| 30 | use Antlr\Antlr4\Runtime\Atn\States\StarLoopEntryState; |
||||
| 31 | use Antlr\Antlr4\Runtime\Atn\States\TokensStartState; |
||||
| 32 | use Antlr\Antlr4\Runtime\Atn\Transitions\ActionTransition; |
||||
| 33 | use Antlr\Antlr4\Runtime\Atn\Transitions\AtomTransition; |
||||
| 34 | use Antlr\Antlr4\Runtime\Atn\Transitions\EpsilonTransition; |
||||
| 35 | use Antlr\Antlr4\Runtime\Atn\Transitions\NotSetTransition; |
||||
| 36 | use Antlr\Antlr4\Runtime\Atn\Transitions\PrecedencePredicateTransition; |
||||
| 37 | use Antlr\Antlr4\Runtime\Atn\Transitions\PredicateTransition; |
||||
| 38 | use Antlr\Antlr4\Runtime\Atn\Transitions\RangeTransition; |
||||
| 39 | use Antlr\Antlr4\Runtime\Atn\Transitions\RuleTransition; |
||||
| 40 | use Antlr\Antlr4\Runtime\Atn\Transitions\SetTransition; |
||||
| 41 | use Antlr\Antlr4\Runtime\Atn\Transitions\Transition; |
||||
| 42 | use Antlr\Antlr4\Runtime\Atn\Transitions\WildcardTransition; |
||||
| 43 | use Antlr\Antlr4\Runtime\IntervalSet; |
||||
| 44 | use Antlr\Antlr4\Runtime\Token; |
||||
| 45 | use Antlr\Antlr4\Runtime\Utils\StringUtils; |
||||
| 46 | |||||
| 47 | final class ATNDeserializer |
||||
| 48 | { |
||||
| 49 | /** |
||||
| 50 | * This value should never change. Updates following this version are |
||||
| 51 | * reflected as change in the unique ID SERIALIZED_UUID. |
||||
| 52 | */ |
||||
| 53 | public const SERIALIZED_VERSION = 3; |
||||
| 54 | |||||
| 55 | /** |
||||
| 56 | * This is the earliest supported serialized UUID. |
||||
| 57 | * Stick to serialized version for now, we don't need a UUID instance. |
||||
| 58 | */ |
||||
| 59 | private const BASE_SERIALIZED_UUID = 'AADB8D7E-AEEF-4415-AD2B-8204D6CF042E'; |
||||
| 60 | |||||
| 61 | /** |
||||
| 62 | * This UUID indicates the serialized ATN contains two sets of IntervalSets, |
||||
| 63 | * where the second set's values are encoded as 32-bit integers to support |
||||
| 64 | * the full Unicode SMP range up to U+10FFFF. |
||||
| 65 | */ |
||||
| 66 | private const ADDED_UNICODE_SMP = '59627784-3BE5-417A-B9EB-8131A7286089'; |
||||
| 67 | |||||
| 68 | /** |
||||
| 69 | * This list contains all of the currently supported UUIDs, ordered by when |
||||
| 70 | * the feature first appeared in this branch. |
||||
| 71 | */ |
||||
| 72 | private const SUPPORTED_UUIDS = [ |
||||
| 73 | self::BASE_SERIALIZED_UUID, |
||||
| 74 | self::ADDED_UNICODE_SMP, |
||||
| 75 | ]; |
||||
| 76 | |||||
| 77 | /** |
||||
| 78 | * This is the current serialized UUID. |
||||
| 79 | */ |
||||
| 80 | private const SERIALIZED_UUID = self::ADDED_UNICODE_SMP; |
||||
| 81 | |||||
| 82 | /** @var ATNDeserializationOptions */ |
||||
| 83 | private $deserializationOptions; |
||||
| 84 | |||||
| 85 | /** @var array<int> */ |
||||
| 86 | private $data = []; |
||||
| 87 | |||||
| 88 | /** @var int */ |
||||
| 89 | private $pos = 0; |
||||
| 90 | |||||
| 91 | /** @var string */ |
||||
| 92 | private $uuid = ''; |
||||
| 93 | |||||
| 94 | /** @var array<int, callable|null>|null */ |
||||
| 95 | private $stateFactories; |
||||
|
0 ignored issues
–
show
introduced
by
Loading history...
|
|||||
| 96 | |||||
| 97 | /** @var array<int, callable|null>|null */ |
||||
| 98 | private $actionFactories; |
||||
|
0 ignored issues
–
show
|
|||||
| 99 | |||||
| 100 | 1 | public function __construct(?ATNDeserializationOptions $options = null) |
|||
| 101 | { |
||||
| 102 | 1 | $this->deserializationOptions = $options ?? ATNDeserializationOptions::defaultOptions(); |
|||
| 103 | 1 | } |
|||
| 104 | |||||
| 105 | /** |
||||
| 106 | * Determines if a particular serialized representation of an ATN supports |
||||
| 107 | * a particular feature, identified by the {@see UUID} used for serializing |
||||
| 108 | * the ATN at the time the feature was first introduced. |
||||
| 109 | * |
||||
| 110 | * @param string $feature The {@see UUID} marking the first time the |
||||
| 111 | * feature was supported in the serialized ATN. |
||||
| 112 | * @param string $actualUuid The {@see UUID} of the actual serialized ATN |
||||
| 113 | * which is currently being deserialized. |
||||
| 114 | * |
||||
| 115 | * @return bool `true` if the `actualUuid` value represents a serialized |
||||
| 116 | * ATN at or after the feature identified by `feature` was |
||||
| 117 | * introduced; otherwise, `false`. |
||||
| 118 | */ |
||||
| 119 | 1 | protected function isFeatureSupported(string $feature, string $actualUuid) : bool |
|||
| 120 | { |
||||
| 121 | 1 | $featureIndex = \array_search($feature, self::SUPPORTED_UUIDS, true); |
|||
| 122 | |||||
| 123 | 1 | if ($featureIndex === false) { |
|||
| 124 | return false; |
||||
| 125 | } |
||||
| 126 | |||||
| 127 | 1 | $actualUuidIndex = \array_search($actualUuid, self::SUPPORTED_UUIDS, true); |
|||
| 128 | |||||
| 129 | 1 | return $actualUuidIndex >= $featureIndex; |
|||
| 130 | } |
||||
| 131 | |||||
| 132 | 1 | public function deserialize(string $data) : ATN |
|||
| 133 | { |
||||
| 134 | 1 | $this->reset($data); |
|||
| 135 | 1 | $this->checkVersion(); |
|||
| 136 | 1 | $this->checkUUID(); |
|||
| 137 | 1 | $atn = $this->readATN(); |
|||
| 138 | 1 | $this->readStates($atn); |
|||
| 139 | 1 | $this->readRules($atn); |
|||
| 140 | 1 | $this->readModes($atn); |
|||
| 141 | 1 | $sets = []; |
|||
| 142 | |||||
| 143 | // First, deserialize sets with 16-bit arguments <= U+FFFF. |
||||
| 144 | 1 | $this->readSets($sets, function () { |
|||
| 145 | 1 | return $this->readInt(); |
|||
| 146 | 1 | }); |
|||
| 147 | |||||
| 148 | // Next, if the ATN was serialized with the Unicode SMP feature, |
||||
| 149 | // deserialize sets with 32-bit arguments <= U+10FFFF. |
||||
| 150 | |||||
| 151 | 1 | if ($this->isFeatureSupported(self::ADDED_UNICODE_SMP, $this->uuid)) { |
|||
| 152 | 1 | $this->readSets($sets, function () { |
|||
| 153 | return $this->readInt32(); |
||||
| 154 | 1 | }); |
|||
| 155 | } |
||||
| 156 | |||||
| 157 | 1 | $this->readEdges($atn, $sets); |
|||
| 158 | 1 | $this->readDecisions($atn); |
|||
| 159 | 1 | $this->readLexerActions($atn); |
|||
| 160 | 1 | $this->markPrecedenceDecisions($atn); |
|||
| 161 | 1 | $this->verifyATN($atn); |
|||
| 162 | |||||
| 163 | 1 | if ($atn->grammarType === ATN::ATN_TYPE_PARSER |
|||
| 164 | 1 | && $this->deserializationOptions->isGenerateRuleBypassTransitions()) { |
|||
| 165 | $this->generateRuleBypassTransitions($atn); |
||||
| 166 | // re-verify after modification |
||||
| 167 | $this->verifyATN($atn); |
||||
| 168 | } |
||||
| 169 | |||||
| 170 | 1 | return $atn; |
|||
| 171 | } |
||||
| 172 | |||||
| 173 | 1 | private function reset(string $data) : void |
|||
| 174 | { |
||||
| 175 | 1 | $characters = \preg_split('//u', $data, -1, \PREG_SPLIT_NO_EMPTY); |
|||
| 176 | |||||
| 177 | 1 | if ($characters === false) { |
|||
| 178 | return; |
||||
| 179 | } |
||||
| 180 | |||||
| 181 | 1 | $this->data = [StringUtils::codePoint($characters[0])]; |
|||
| 182 | 1 | for ($i = 1, $length = \count($characters); $i < $length; $i++) { |
|||
| 183 | 1 | $code = StringUtils::codePoint($characters[$i]); |
|||
| 184 | 1 | $this->data[] = $code > 1 ? $code - 2 : $code + 65533; |
|||
| 185 | } |
||||
| 186 | |||||
| 187 | 1 | $this->pos = 0; |
|||
| 188 | 1 | } |
|||
| 189 | |||||
| 190 | |||||
| 191 | 1 | private function checkVersion() : void |
|||
| 192 | { |
||||
| 193 | 1 | $version = $this->readInt(); |
|||
| 194 | |||||
| 195 | 1 | if ($version !== self::SERIALIZED_VERSION) { |
|||
| 196 | throw new \InvalidArgumentException(\sprintf( |
||||
| 197 | 'Could not deserialize ATN with version %d (expected %d).', |
||||
| 198 | $version, |
||||
| 199 | self::SERIALIZED_VERSION |
||||
| 200 | )); |
||||
| 201 | } |
||||
| 202 | 1 | } |
|||
| 203 | |||||
| 204 | 1 | private function checkUUID() : void |
|||
| 205 | { |
||||
| 206 | 1 | $uuid = $this->readUUID(); |
|||
| 207 | |||||
| 208 | 1 | if (!\in_array($uuid, self::SUPPORTED_UUIDS, true)) { |
|||
| 209 | throw new \InvalidArgumentException(\sprintf( |
||||
| 210 | 'Could not deserialize ATN with UUID: %s (expected %s or a legacy UUID).', |
||||
| 211 | $uuid, |
||||
| 212 | self::SERIALIZED_UUID |
||||
| 213 | )); |
||||
| 214 | } |
||||
| 215 | |||||
| 216 | 1 | $this->uuid = $uuid; |
|||
| 217 | 1 | } |
|||
| 218 | |||||
| 219 | 1 | private function readATN() : ATN |
|||
| 220 | { |
||||
| 221 | 1 | $grammarType = $this->readInt(); |
|||
| 222 | 1 | $maxTokenType = $this->readInt(); |
|||
| 223 | |||||
| 224 | 1 | return new ATN($grammarType, $maxTokenType); |
|||
| 225 | } |
||||
| 226 | |||||
| 227 | 1 | private function readStates(ATN $atn) : void |
|||
| 228 | { |
||||
| 229 | 1 | $loopBackStateNumbers = []; |
|||
| 230 | 1 | $endStateNumbers = []; |
|||
| 231 | 1 | $nstates = $this->readInt(); |
|||
| 232 | |||||
| 233 | 1 | for ($i=0; $i < $nstates; $i++) { |
|||
| 234 | 1 | $stype = $this->readInt(); |
|||
| 235 | |||||
| 236 | // ignore bad type of states |
||||
| 237 | 1 | if ($stype === ATNState::INVALID_TYPE) { |
|||
| 238 | $atn->addState(null); |
||||
| 239 | |||||
| 240 | continue; |
||||
| 241 | } |
||||
| 242 | |||||
| 243 | 1 | $ruleIndex = $this->readInt(); |
|||
| 244 | |||||
| 245 | 1 | if ($ruleIndex === 0xFFFF) { |
|||
| 246 | $ruleIndex = -1; |
||||
| 247 | } |
||||
| 248 | |||||
| 249 | 1 | $s = $this->stateFactory($stype, $ruleIndex); |
|||
| 250 | |||||
| 251 | 1 | if ($stype === ATNState::LOOP_END) { |
|||
| 252 | // special case |
||||
| 253 | 1 | $loopBackStateNumber = $this->readInt(); |
|||
| 254 | |||||
| 255 | 1 | if (!$s instanceof LoopEndState) { |
|||
| 256 | throw new \RuntimeException('Unexpected ATN State'); |
||||
| 257 | } |
||||
| 258 | |||||
| 259 | 1 | $loopBackStateNumbers[] = [$s, $loopBackStateNumber]; |
|||
| 260 | 1 | } elseif ($s instanceof BlockStartState) { |
|||
| 261 | 1 | $endStateNumber = $this->readInt(); |
|||
| 262 | |||||
| 263 | 1 | $endStateNumbers[] = [$s, $endStateNumber]; |
|||
| 264 | } |
||||
| 265 | |||||
| 266 | 1 | $atn->addState($s); |
|||
| 267 | } |
||||
| 268 | |||||
| 269 | // delay the assignment of loop back and end states until we know all the |
||||
| 270 | // state instances have been initialized |
||||
| 271 | 1 | foreach ($loopBackStateNumbers as $pair) { |
|||
| 272 | 1 | $pair[0]->loopBackState = $atn->states[$pair[1]]; |
|||
| 273 | } |
||||
| 274 | |||||
| 275 | 1 | foreach ($endStateNumbers as $pair) { |
|||
| 276 | 1 | $endState = $atn->states[$pair[1]]; |
|||
| 277 | |||||
| 278 | 1 | if (!$endState instanceof BlockEndState) { |
|||
| 279 | throw new \RuntimeException('Unexpected ATN State'); |
||||
| 280 | } |
||||
| 281 | |||||
| 282 | 1 | $pair[0]->endState = $endState; |
|||
| 283 | } |
||||
| 284 | |||||
| 285 | 1 | $numNonGreedyStates = $this->readInt(); |
|||
| 286 | |||||
| 287 | 1 | for ($j=0; $j < $numNonGreedyStates; $j++) { |
|||
| 288 | $decisionState = $atn->states[$this->readInt()]; |
||||
| 289 | |||||
| 290 | if (!$decisionState instanceof DecisionState) { |
||||
| 291 | throw new \RuntimeException('Unexpected ATN State'); |
||||
| 292 | } |
||||
| 293 | |||||
| 294 | $decisionState->nonGreedy = true; |
||||
| 295 | } |
||||
| 296 | |||||
| 297 | 1 | $numPrecedenceStates = $this->readInt(); |
|||
| 298 | |||||
| 299 | 1 | for ($j=0; $j < $numPrecedenceStates; $j++) { |
|||
| 300 | 1 | $ruleStartState = $atn->states[$this->readInt()]; |
|||
| 301 | |||||
| 302 | 1 | if (!$ruleStartState instanceof RuleStartState) { |
|||
| 303 | throw new \RuntimeException('Unexpected ATN State'); |
||||
| 304 | } |
||||
| 305 | |||||
| 306 | 1 | $ruleStartState->isLeftRecursiveRule = true; |
|||
| 307 | } |
||||
| 308 | 1 | } |
|||
| 309 | |||||
| 310 | 1 | private function readRules(ATN $atn) : void |
|||
| 311 | { |
||||
| 312 | 1 | $nRules = $this->readInt(); |
|||
| 313 | |||||
| 314 | 1 | $atn->ruleToTokenType = []; |
|||
| 315 | 1 | $atn->ruleToStartState = []; |
|||
| 316 | 1 | for ($i=0; $i < $nRules; $i++) { |
|||
| 317 | 1 | $s = $this->readInt(); |
|||
| 318 | 1 | $startState = $atn->states[$s]; |
|||
| 319 | |||||
| 320 | 1 | if (!$startState instanceof RuleStartState) { |
|||
| 321 | throw new \RuntimeException('Unexpected ATN State'); |
||||
| 322 | } |
||||
| 323 | |||||
| 324 | 1 | $atn->ruleToStartState[$i] = $startState; |
|||
| 325 | |||||
| 326 | 1 | if ($atn->grammarType === ATN::ATN_TYPE_LEXER) { |
|||
| 327 | 1 | $tokenType = $this->readInt(); |
|||
| 328 | |||||
| 329 | 1 | if ($tokenType === 0xFFFF) { |
|||
| 330 | $tokenType = Token::EOF; |
||||
| 331 | } |
||||
| 332 | |||||
| 333 | 1 | $atn->ruleToTokenType[$i] = $tokenType; |
|||
| 334 | } |
||||
| 335 | } |
||||
| 336 | |||||
| 337 | 1 | $atn->ruleToStopState = []; |
|||
| 338 | 1 | foreach ($atn->states as $state) { |
|||
| 339 | 1 | if (!$state instanceof RuleStopState) { |
|||
| 340 | 1 | continue; |
|||
| 341 | } |
||||
| 342 | |||||
| 343 | 1 | $atn->ruleToStopState[$state->ruleIndex] = $state; |
|||
| 344 | 1 | $atn->ruleToStartState[$state->ruleIndex]->stopState = $state; |
|||
| 345 | } |
||||
| 346 | 1 | } |
|||
| 347 | |||||
| 348 | 1 | private function readModes(ATN $atn) : void |
|||
| 349 | { |
||||
| 350 | 1 | $nmodes = $this->readInt(); |
|||
| 351 | |||||
| 352 | 1 | for ($i=0; $i < $nmodes; $i++) { |
|||
| 353 | 1 | $tokensStartState = $atn->states[$this->readInt()]; |
|||
| 354 | |||||
| 355 | 1 | if (!$tokensStartState instanceof TokensStartState) { |
|||
| 356 | throw new \RuntimeException('Unexpected ATN State'); |
||||
| 357 | } |
||||
| 358 | |||||
| 359 | 1 | $atn->modeToStartState[] = $tokensStartState; |
|||
| 360 | } |
||||
| 361 | 1 | } |
|||
| 362 | |||||
| 363 | /** |
||||
| 364 | * @param array<IntervalSet> $sets |
||||
| 365 | */ |
||||
| 366 | 1 | private function readSets(array &$sets, callable $readUnicode) : void |
|||
| 367 | { |
||||
| 368 | 1 | $m = $this->readInt(); |
|||
| 369 | |||||
| 370 | 1 | for ($i=0; $i < $m; $i++) { |
|||
| 371 | 1 | $iset = new IntervalSet(); |
|||
| 372 | |||||
| 373 | 1 | $sets[] = $iset; |
|||
| 374 | 1 | $n = $this->readInt(); |
|||
| 375 | 1 | $containsEof = $this->readInt(); |
|||
| 376 | |||||
| 377 | 1 | if ($containsEof !== 0) { |
|||
| 378 | $iset->addOne(-1); |
||||
| 379 | } |
||||
| 380 | |||||
| 381 | 1 | for ($j=0; $j < $n; $j++) { |
|||
| 382 | 1 | $i1 = $readUnicode(); |
|||
| 383 | 1 | $i2 = $readUnicode(); |
|||
| 384 | 1 | $iset->addRange($i1, $i2); |
|||
| 385 | } |
||||
| 386 | } |
||||
| 387 | 1 | } |
|||
| 388 | |||||
| 389 | /** |
||||
| 390 | * @param array<IntervalSet> $sets |
||||
| 391 | */ |
||||
| 392 | 1 | private function readEdges(ATN $atn, array &$sets) : void |
|||
| 393 | { |
||||
| 394 | 1 | $nEdges = $this->readInt(); |
|||
| 395 | |||||
| 396 | 1 | for ($i=0; $i < $nEdges; $i++) { |
|||
| 397 | 1 | $src = $this->readInt(); |
|||
| 398 | 1 | $trg = $this->readInt(); |
|||
| 399 | 1 | $ttype = $this->readInt(); |
|||
| 400 | 1 | $arg1 = $this->readInt(); |
|||
| 401 | 1 | $arg2 = $this->readInt(); |
|||
| 402 | 1 | $arg3 = $this->readInt(); |
|||
| 403 | 1 | $trans = $this->edgeFactory($atn, $ttype, $src, $trg, $arg1, $arg2, $arg3, $sets); |
|||
| 404 | 1 | $srcState = $atn->states[$src]; |
|||
| 405 | 1 | $srcState->addTransition($trans); |
|||
| 406 | } |
||||
| 407 | |||||
| 408 | // edges for rule stop states can be derived, so they aren't serialized |
||||
| 409 | 1 | foreach ($atn->states as $state) { |
|||
| 410 | 1 | foreach ($state->getTransitions() as $t) { |
|||
| 411 | 1 | if (!$t instanceof RuleTransition) { |
|||
| 412 | 1 | continue; |
|||
| 413 | } |
||||
| 414 | |||||
| 415 | 1 | $outermostPrecedenceReturn = -1; |
|||
| 416 | 1 | if ($atn->ruleToStartState[$t->target->ruleIndex]->isLeftRecursiveRule) { |
|||
| 417 | 1 | if ($t->precedence === 0) { |
|||
| 418 | 1 | $outermostPrecedenceReturn = $t->target->ruleIndex; |
|||
| 419 | } |
||||
| 420 | } |
||||
| 421 | |||||
| 422 | 1 | $trans = new EpsilonTransition($t->followState, $outermostPrecedenceReturn); |
|||
| 423 | 1 | $atn->ruleToStopState[$t->target->ruleIndex]->addTransition($trans); |
|||
| 424 | } |
||||
| 425 | } |
||||
| 426 | |||||
| 427 | 1 | foreach ($atn->states as $state) { |
|||
| 428 | 1 | if ($state instanceof BlockStartState) { |
|||
| 429 | // we need to know the end state to set its start state |
||||
| 430 | 1 | if ($state->endState === null) { |
|||
| 431 | throw new \RuntimeException('Unexpected null EndState.'); |
||||
| 432 | } |
||||
| 433 | |||||
| 434 | // block end states can only be associated to a single block start state |
||||
| 435 | 1 | if ($state->endState->startState !== null) { |
|||
| 436 | throw new \RuntimeException('Unexpected null StartState.'); |
||||
| 437 | } |
||||
| 438 | |||||
| 439 | 1 | $state->endState->startState = $state; |
|||
| 440 | } |
||||
| 441 | |||||
| 442 | 1 | if ($state instanceof PlusLoopbackState) { |
|||
| 443 | 1 | foreach ($state->getTransitions() as $t) { |
|||
| 444 | 1 | $target = $t->target; |
|||
| 445 | |||||
| 446 | 1 | if ($target instanceof PlusBlockStartState) { |
|||
| 447 | 1 | $target->loopBackState = $state; |
|||
| 448 | } |
||||
| 449 | } |
||||
| 450 | 1 | } elseif ($state instanceof StarLoopbackState) { |
|||
| 451 | 1 | foreach ($state->getTransitions() as $t) { |
|||
| 452 | 1 | $target = $t->target; |
|||
| 453 | |||||
| 454 | 1 | if ($target instanceof StarLoopEntryState) { |
|||
| 455 | 1 | $target->loopBackState = $state; |
|||
| 456 | } |
||||
| 457 | } |
||||
| 458 | } |
||||
| 459 | } |
||||
| 460 | 1 | } |
|||
| 461 | |||||
| 462 | 1 | private function readDecisions(ATN $atn) : void |
|||
| 463 | { |
||||
| 464 | 1 | $decisions = $this->readInt(); |
|||
| 465 | |||||
| 466 | 1 | for ($i = 0; $i < $decisions; $i++) { |
|||
| 467 | 1 | $s = $this->readInt(); |
|||
| 468 | /** @var DecisionState $decState */ |
||||
| 469 | 1 | $decState = $atn->states[$s]; |
|||
| 470 | |||||
| 471 | 1 | $atn->decisionToState[] = $decState; |
|||
| 472 | |||||
| 473 | 1 | $decState->decision = $i; |
|||
| 474 | } |
||||
| 475 | 1 | } |
|||
| 476 | |||||
| 477 | 1 | private function readLexerActions(ATN $atn) : void |
|||
| 478 | { |
||||
| 479 | 1 | if ($atn->grammarType === ATN::ATN_TYPE_LEXER) { |
|||
| 480 | 1 | $count = $this->readInt(); |
|||
| 481 | |||||
| 482 | 1 | $atn->lexerActions = []; |
|||
| 483 | 1 | for ($i = 0; $i < $count; $i++) { |
|||
| 484 | 1 | $actionType = $this->readInt(); |
|||
| 485 | 1 | $data1 = $this->readInt(); |
|||
| 486 | |||||
| 487 | 1 | if ($data1 === 0xFFFF) { |
|||
| 488 | $data1 = -1; |
||||
| 489 | } |
||||
| 490 | |||||
| 491 | 1 | $data2 = $this->readInt(); |
|||
| 492 | |||||
| 493 | 1 | if ($data2 === 0xFFFF) { |
|||
| 494 | $data2 = -1; |
||||
| 495 | } |
||||
| 496 | |||||
| 497 | 1 | $lexerAction = $this->lexerActionFactory($actionType, $data1, $data2); |
|||
| 498 | 1 | $atn->lexerActions[$i] = $lexerAction; |
|||
| 499 | } |
||||
| 500 | } |
||||
| 501 | 1 | } |
|||
| 502 | |||||
| 503 | private function generateRuleBypassTransitions(ATN $atn) : void |
||||
| 504 | { |
||||
| 505 | $count = \count($atn->ruleToStartState); |
||||
| 506 | |||||
| 507 | for ($i = 0; $i < $count; $i++) { |
||||
| 508 | $atn->ruleToTokenType[$i] = $atn->maxTokenType + $i + 1; |
||||
| 509 | } |
||||
| 510 | |||||
| 511 | for ($i = 0; $i < $count; $i++) { |
||||
| 512 | $this->generateRuleBypassTransition($atn, $i); |
||||
| 513 | } |
||||
| 514 | } |
||||
| 515 | |||||
| 516 | private function generateRuleBypassTransition(ATN $atn, int $idx) : void |
||||
| 517 | { |
||||
| 518 | $bypassStart = new BasicBlockStartState(); |
||||
| 519 | $bypassStart->ruleIndex = $idx; |
||||
| 520 | $atn->addState($bypassStart); |
||||
| 521 | |||||
| 522 | $bypassStop = new BlockEndState(); |
||||
| 523 | $bypassStop->ruleIndex = $idx; |
||||
| 524 | $atn->addState($bypassStop); |
||||
| 525 | |||||
| 526 | $bypassStart->endState = $bypassStop; |
||||
| 527 | $atn->defineDecisionState($bypassStart); |
||||
| 528 | |||||
| 529 | $bypassStop->startState = $bypassStart; |
||||
| 530 | |||||
| 531 | $excludeTransition = null; |
||||
| 532 | if ($atn->ruleToStartState[$idx]->isLeftRecursiveRule) { |
||||
| 533 | // wrap from the beginning of the rule to the StarLoopEntryState |
||||
| 534 | $endState = null; |
||||
| 535 | |||||
| 536 | foreach ($atn->states as $state) { |
||||
| 537 | if ($this->stateIsEndStateFor($state, $idx)) { |
||||
| 538 | $endState = $state; |
||||
| 539 | |||||
| 540 | if (!$state instanceof LoopEndState) { |
||||
| 541 | throw new \RuntimeException('Unexpected state type.'); |
||||
| 542 | } |
||||
| 543 | |||||
| 544 | if ($state->loopBackState === null) { |
||||
| 545 | throw new \RuntimeException('Unexpected null loop back state.'); |
||||
| 546 | } |
||||
| 547 | |||||
| 548 | $excludeTransition = $state->loopBackState->getTransition(0); |
||||
| 549 | |||||
| 550 | break; |
||||
| 551 | } |
||||
| 552 | } |
||||
| 553 | |||||
| 554 | if ($excludeTransition === null) { |
||||
| 555 | throw new \RuntimeException('Couldn\'t identify final state of the precedence rule prefix section.'); |
||||
| 556 | } |
||||
| 557 | } else { |
||||
| 558 | $endState = $atn->ruleToStopState[$idx]; |
||||
| 559 | } |
||||
| 560 | |||||
| 561 | // all non-excluded transitions that currently target end state need to target blockEnd instead |
||||
| 562 | // TODO:looks like a bug |
||||
| 563 | foreach ($atn->states as $state) { |
||||
| 564 | foreach ($state->getTransitions() as $transition) { |
||||
| 565 | if ($excludeTransition !== null && $transition->equals($excludeTransition)) { |
||||
| 566 | continue; |
||||
| 567 | } |
||||
| 568 | |||||
| 569 | if ($endState !== null && $transition->target->equals($endState)) { |
||||
| 570 | $transition->target = $bypassStop; |
||||
| 571 | } |
||||
| 572 | } |
||||
| 573 | } |
||||
| 574 | |||||
| 575 | // all transitions leaving the rule start state need to leave blockStart instead |
||||
| 576 | $ruleToStartState = $atn->ruleToStartState[$idx]; |
||||
| 577 | $count = $ruleToStartState->getNumberOfTransitions(); |
||||
| 578 | |||||
| 579 | while ($count > 0) { |
||||
| 580 | $bypassStart->addTransition($ruleToStartState->getTransition($count-1)); |
||||
| 581 | $ruleToStartState->setTransitions(\array_slice($ruleToStartState->getTransitions(), -1)); |
||||
| 582 | } |
||||
| 583 | |||||
| 584 | // link the new states |
||||
| 585 | $atn->ruleToStartState[$idx]->addTransition(new EpsilonTransition($bypassStart)); |
||||
| 586 | |||||
| 587 | if ($endState === null) { |
||||
| 588 | throw new \RuntimeException('Unexpected null end state.'); |
||||
| 589 | } |
||||
| 590 | |||||
| 591 | $bypassStop->addTransition(new EpsilonTransition($endState)); |
||||
| 592 | |||||
| 593 | $matchState = new BasicState(); |
||||
| 594 | $atn->addState($matchState); |
||||
| 595 | $matchState->addTransition(new AtomTransition($bypassStop, $atn->ruleToTokenType[$idx] ?? 0)); |
||||
| 596 | $bypassStart->addTransition(new EpsilonTransition($matchState)); |
||||
| 597 | } |
||||
| 598 | |||||
| 599 | private function stateIsEndStateFor(ATNState $state, int $idx) : ?ATNState |
||||
| 600 | { |
||||
| 601 | if ($state->ruleIndex !== $idx) { |
||||
| 602 | return null; |
||||
| 603 | } |
||||
| 604 | |||||
| 605 | if (!$state instanceof StarLoopEntryState) { |
||||
| 606 | return null; |
||||
| 607 | } |
||||
| 608 | |||||
| 609 | $maybeLoopEndState = $state->getTransition($state->getNumberOfTransitions() - 1)->target; |
||||
| 610 | |||||
| 611 | if (!$maybeLoopEndState instanceof LoopEndState) { |
||||
| 612 | return null; |
||||
| 613 | } |
||||
| 614 | |||||
| 615 | if ($maybeLoopEndState->epsilonOnlyTransitions |
||||
| 616 | && $maybeLoopEndState->getTransition(0)->target instanceof RuleStopState) { |
||||
| 617 | return $state; |
||||
| 618 | } |
||||
| 619 | |||||
| 620 | return null; |
||||
| 621 | } |
||||
| 622 | |||||
| 623 | /** |
||||
| 624 | * Analyze the {@see StarLoopEntryState} states in the specified ATN to set |
||||
| 625 | * the {@see StarLoopEntryState::$isPrecedenceDecision} field to the correct |
||||
| 626 | * value. |
||||
| 627 | * |
||||
| 628 | * @param ATN $atn The ATN. |
||||
| 629 | */ |
||||
| 630 | 1 | private function markPrecedenceDecisions(ATN $atn) : void |
|||
| 631 | { |
||||
| 632 | 1 | foreach ($atn->states as $state) { |
|||
| 633 | 1 | if (!$state instanceof StarLoopEntryState) { |
|||
| 634 | 1 | continue; |
|||
| 635 | } |
||||
| 636 | |||||
| 637 | // We analyze the ATN to determine if this ATN decision state is the |
||||
| 638 | // decision for the closure block that determines whether a |
||||
| 639 | // precedence rule should continue or complete. |
||||
| 640 | 1 | if ($atn->ruleToStartState[$state->ruleIndex]->isLeftRecursiveRule) { |
|||
| 641 | 1 | $maybeLoopEndState = $state->getTransition($state->getNumberOfTransitions() - 1)->target; |
|||
| 642 | |||||
| 643 | 1 | if ($maybeLoopEndState instanceof LoopEndState) { |
|||
| 644 | 1 | if ($maybeLoopEndState->epsilonOnlyTransitions |
|||
| 645 | 1 | && $maybeLoopEndState->getTransition(0)->target instanceof RuleStopState) { |
|||
| 646 | 1 | $state->isPrecedenceDecision = true; |
|||
| 647 | } |
||||
| 648 | } |
||||
| 649 | } |
||||
| 650 | } |
||||
| 651 | 1 | } |
|||
| 652 | |||||
| 653 | 1 | private function verifyATN(ATN $atn) : void |
|||
| 654 | { |
||||
| 655 | 1 | if (!$this->deserializationOptions->isVerifyATN()) { |
|||
| 656 | return; |
||||
| 657 | } |
||||
| 658 | |||||
| 659 | // verify assumptions |
||||
| 660 | 1 | foreach ($atn->states as $state) { |
|||
| 661 | 1 | $this->checkCondition($state->epsilonOnlyTransitions || $state->getNumberOfTransitions() <= 1); |
|||
| 662 | |||||
| 663 | switch (true) { |
||||
| 664 | 1 | case $state instanceof PlusBlockStartState: |
|||
| 665 | 1 | $this->checkCondition($state->loopBackState !== null); |
|||
| 666 | |||||
| 667 | 1 | break; |
|||
| 668 | |||||
| 669 | 1 | case $state instanceof StarLoopEntryState: |
|||
| 670 | 1 | $this->checkCondition($state->loopBackState !== null); |
|||
| 671 | 1 | $this->checkCondition($state->getNumberOfTransitions() === 2); |
|||
| 672 | |||||
| 673 | 1 | if ($state->getTransition(0)->target instanceof StarBlockStartState) { |
|||
| 674 | 1 | $this->checkCondition($state->getTransition(1)->target instanceof LoopEndState); |
|||
| 675 | 1 | $this->checkCondition(!$state->nonGreedy); |
|||
| 676 | } elseif ($state->getTransition(0)->target instanceof LoopEndState) { |
||||
| 677 | $this->checkCondition($state->getTransition(1)->target instanceof StarBlockStartState); |
||||
| 678 | $this->checkCondition($state->nonGreedy); |
||||
| 679 | } else { |
||||
| 680 | throw new \InvalidArgumentException('IllegalState'); |
||||
| 681 | } |
||||
| 682 | |||||
| 683 | 1 | break; |
|||
| 684 | |||||
| 685 | 1 | case $state instanceof StarLoopbackState: |
|||
| 686 | 1 | $this->checkCondition($state->getNumberOfTransitions() === 1); |
|||
| 687 | 1 | $this->checkCondition($state->getTransition(0)->target instanceof StarLoopEntryState); |
|||
| 688 | |||||
| 689 | 1 | break; |
|||
| 690 | |||||
| 691 | 1 | case $state instanceof LoopEndState: |
|||
| 692 | 1 | $this->checkCondition($state->loopBackState !== null); |
|||
| 693 | |||||
| 694 | 1 | break; |
|||
| 695 | |||||
| 696 | 1 | case $state instanceof RuleStartState: |
|||
| 697 | 1 | $this->checkCondition($state->stopState !== null); |
|||
| 698 | |||||
| 699 | 1 | break; |
|||
| 700 | |||||
| 701 | 1 | case $state instanceof BlockStartState: |
|||
| 702 | 1 | $this->checkCondition($state->endState !== null); |
|||
| 703 | |||||
| 704 | 1 | break; |
|||
| 705 | |||||
| 706 | 1 | case $state instanceof BlockEndState: |
|||
| 707 | 1 | $this->checkCondition($state->startState !== null); |
|||
| 708 | |||||
| 709 | 1 | break; |
|||
| 710 | |||||
| 711 | 1 | case $state instanceof DecisionState: |
|||
| 712 | 1 | $this->checkCondition($state->getNumberOfTransitions() <= 1 || $state->decision >= 0); |
|||
| 713 | |||||
| 714 | 1 | break; |
|||
| 715 | |||||
| 716 | default: |
||||
| 717 | 1 | $this->checkCondition($state->getNumberOfTransitions() <= 1 || $state instanceof RuleStopState); |
|||
| 718 | } |
||||
| 719 | } |
||||
| 720 | 1 | } |
|||
| 721 | |||||
| 722 | 1 | private function checkCondition(?bool $condition, $message = 'IllegalState') : void |
|||
| 723 | { |
||||
| 724 | 1 | if ($condition === null) { |
|||
| 725 | throw new \InvalidArgumentException($message); |
||||
| 726 | } |
||||
| 727 | 1 | } |
|||
| 728 | |||||
| 729 | 1 | private function readInt() : int |
|||
| 730 | { |
||||
| 731 | 1 | return $this->data[$this->pos++]; |
|||
| 732 | } |
||||
| 733 | |||||
| 734 | private function readInt32() : int |
||||
| 735 | { |
||||
| 736 | $low = $this->readInt(); |
||||
| 737 | $high = $this->readInt(); |
||||
| 738 | |||||
| 739 | return $low | ($high << 16); |
||||
| 740 | } |
||||
| 741 | |||||
| 742 | 1 | private function readUUID() : string |
|||
| 743 | { |
||||
| 744 | 1 | $bb = []; |
|||
| 745 | 1 | for ($i=0; $i < 8; $i++) { |
|||
| 746 | 1 | $int = $this->readInt(); |
|||
| 747 | 1 | $bb[] = $int & 0xFF; |
|||
| 748 | 1 | $bb[] = ($int >> 8) & 0xFF; |
|||
| 749 | } |
||||
| 750 | |||||
| 751 | 1 | $bb = \array_reverse($bb); |
|||
| 752 | 1 | $hex = \strtoupper(\bin2hex(\implode(\array_map('chr', $bb)))); |
|||
| 753 | |||||
| 754 | 1 | return \vsprintf('%s%s-%s-%s-%s-%s%s%s', \str_split($hex, 4)); |
|||
| 755 | } |
||||
| 756 | |||||
| 757 | /** |
||||
| 758 | * @param array<IntervalSet> $sets |
||||
| 759 | */ |
||||
| 760 | 1 | private function edgeFactory( |
|||
| 761 | ATN $atn, |
||||
| 762 | int $type, |
||||
| 763 | int $src, |
||||
|
0 ignored issues
–
show
The parameter
$src is not used and could be removed.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for parameters that have been defined for a function or method, but which are not used in the method body. Loading history...
|
|||||
| 764 | int $trg, |
||||
| 765 | int $arg1, |
||||
| 766 | int $arg2, |
||||
| 767 | int $arg3, |
||||
| 768 | array $sets |
||||
| 769 | ) : Transition { |
||||
| 770 | 1 | $target = $atn->states[$trg]; |
|||
| 771 | |||||
| 772 | switch ($type) { |
||||
| 773 | 1 | case Transition::EPSILON: |
|||
| 774 | 1 | return new EpsilonTransition($target); |
|||
| 775 | |||||
| 776 | 1 | case Transition::RANGE: |
|||
| 777 | return $arg3 !== 0 ? |
||||
| 778 | new RangeTransition($target, Token::EOF, $arg2) : |
||||
| 779 | new RangeTransition($target, $arg1, $arg2); |
||||
| 780 | |||||
| 781 | 1 | case Transition::RULE: |
|||
| 782 | 1 | $ruleStart = $atn->states[$arg1]; |
|||
| 783 | |||||
| 784 | 1 | if (!$ruleStart instanceof RuleStartState) { |
|||
| 785 | throw new \RuntimeException('Unexpected transition type.'); |
||||
| 786 | } |
||||
| 787 | |||||
| 788 | 1 | return new RuleTransition($ruleStart, $arg2, $arg3, $target); |
|||
| 789 | |||||
| 790 | 1 | case Transition::PREDICATE: |
|||
| 791 | return new PredicateTransition($target, $arg1, $arg2, $arg3 !== 0); |
||||
| 792 | |||||
| 793 | 1 | case Transition::PRECEDENCE: |
|||
| 794 | 1 | return new PrecedencePredicateTransition($target, $arg1); |
|||
| 795 | |||||
| 796 | 1 | case Transition::ATOM: |
|||
| 797 | 1 | return $arg3 !== 0 ? new AtomTransition($target, Token::EOF) : new AtomTransition($target, $arg1); |
|||
| 798 | |||||
| 799 | 1 | case Transition::ACTION: |
|||
| 800 | 1 | return new ActionTransition($target, $arg1, $arg2, $arg3 !== 0); |
|||
| 801 | |||||
| 802 | 1 | case Transition::SET: |
|||
| 803 | 1 | return new SetTransition($target, $sets[$arg1]); |
|||
| 804 | |||||
| 805 | case Transition::NOT_SET: |
||||
| 806 | return new NotSetTransition($target, $sets[$arg1]); |
||||
| 807 | |||||
| 808 | case Transition::WILDCARD: |
||||
| 809 | return new WildcardTransition($target); |
||||
| 810 | |||||
| 811 | default: |
||||
| 812 | throw new \InvalidArgumentException(\sprintf( |
||||
| 813 | 'The specified transition type: %d is not valid.', |
||||
| 814 | $type |
||||
| 815 | )); |
||||
| 816 | } |
||||
| 817 | } |
||||
| 818 | |||||
| 819 | 1 | private function stateFactory(int $type, int $ruleIndex) : ?ATNState |
|||
| 820 | { |
||||
| 821 | switch ($type) { |
||||
| 822 | 1 | case ATNState::INVALID_TYPE: |
|||
| 823 | return null; |
||||
| 824 | |||||
| 825 | 1 | case ATNState::BASIC: |
|||
| 826 | 1 | $s = new BasicState(); |
|||
| 827 | |||||
| 828 | 1 | break; |
|||
| 829 | |||||
| 830 | 1 | case ATNState::RULE_START: |
|||
| 831 | 1 | $s = new RuleStartState(); |
|||
| 832 | |||||
| 833 | 1 | break; |
|||
| 834 | |||||
| 835 | 1 | case ATNState::BLOCK_START: |
|||
| 836 | 1 | $s = new BasicBlockStartState(); |
|||
| 837 | |||||
| 838 | 1 | break; |
|||
| 839 | |||||
| 840 | 1 | case ATNState::PLUS_BLOCK_START: |
|||
| 841 | 1 | $s = new PlusBlockStartState(); |
|||
| 842 | |||||
| 843 | 1 | break; |
|||
| 844 | |||||
| 845 | 1 | case ATNState::STAR_BLOCK_START: |
|||
| 846 | 1 | $s = new StarBlockStartState(); |
|||
| 847 | |||||
| 848 | 1 | break; |
|||
| 849 | |||||
| 850 | 1 | case ATNState::TOKEN_START: |
|||
| 851 | 1 | $s = new TokensStartState(); |
|||
| 852 | |||||
| 853 | 1 | break; |
|||
| 854 | |||||
| 855 | 1 | case ATNState::RULE_STOP: |
|||
| 856 | 1 | $s = new RuleStopState(); |
|||
| 857 | |||||
| 858 | 1 | break; |
|||
| 859 | |||||
| 860 | 1 | case ATNState::BLOCK_END: |
|||
| 861 | 1 | $s = new BlockEndState(); |
|||
| 862 | |||||
| 863 | 1 | break; |
|||
| 864 | |||||
| 865 | 1 | case ATNState::STAR_LOOP_BACK: |
|||
| 866 | 1 | $s = new StarLoopbackState(); |
|||
| 867 | |||||
| 868 | 1 | break; |
|||
| 869 | |||||
| 870 | 1 | case ATNState::STAR_LOOP_ENTRY: |
|||
| 871 | 1 | $s = new StarLoopEntryState(); |
|||
| 872 | |||||
| 873 | 1 | break; |
|||
| 874 | |||||
| 875 | 1 | case ATNState::PLUS_LOOP_BACK: |
|||
| 876 | 1 | $s = new PlusLoopbackState(); |
|||
| 877 | |||||
| 878 | 1 | break; |
|||
| 879 | |||||
| 880 | 1 | case ATNState::LOOP_END: |
|||
| 881 | 1 | $s = new LoopEndState(); |
|||
| 882 | |||||
| 883 | 1 | break; |
|||
| 884 | |||||
| 885 | default: |
||||
| 886 | throw new \InvalidArgumentException(\sprintf( |
||||
| 887 | 'The specified state type %d is not valid.', |
||||
| 888 | $type |
||||
| 889 | )); |
||||
| 890 | } |
||||
| 891 | |||||
| 892 | 1 | $s->ruleIndex = $ruleIndex; |
|||
| 893 | |||||
| 894 | 1 | return $s; |
|||
| 895 | } |
||||
| 896 | |||||
| 897 | 1 | private function lexerActionFactory(int $type, int $data1, int $data2) : LexerAction |
|||
| 898 | { |
||||
| 899 | switch ($type) { |
||||
| 900 | 1 | case LexerActionType::CHANNEL: |
|||
| 901 | return new LexerChannelAction($data1); |
||||
| 902 | |||||
| 903 | 1 | case LexerActionType::CUSTOM: |
|||
| 904 | return new LexerCustomAction($data1, $data2); |
||||
| 905 | |||||
| 906 | 1 | case LexerActionType::MODE: |
|||
| 907 | return new LexerModeAction($data1); |
||||
| 908 | |||||
| 909 | 1 | case LexerActionType::MORE: |
|||
| 910 | return LexerMoreAction::instance(); |
||||
| 911 | |||||
| 912 | 1 | case LexerActionType::POP_MODE: |
|||
| 913 | return LexerPopModeAction::instance(); |
||||
| 914 | |||||
| 915 | 1 | case LexerActionType::PUSH_MODE: |
|||
| 916 | return new LexerPushModeAction($data1); |
||||
| 917 | |||||
| 918 | 1 | case LexerActionType::SKIP: |
|||
| 919 | 1 | return LexerSkipAction::instance(); |
|||
| 920 | |||||
| 921 | case LexerActionType::TYPE: |
||||
| 922 | return new LexerTypeAction($data1); |
||||
| 923 | |||||
| 924 | default: |
||||
| 925 | throw new \InvalidArgumentException(\sprintf( |
||||
| 926 | 'The specified lexer action type %d is not valid.', |
||||
| 927 | $type |
||||
| 928 | )); |
||||
| 929 | } |
||||
| 930 | } |
||||
| 931 | } |
||||
| 932 |