1*37748cd8SNickeau<?php 2*37748cd8SNickeau 3*37748cd8SNickeaudeclare(strict_types=1); 4*37748cd8SNickeau 5*37748cd8SNickeaunamespace Antlr\Antlr4\Runtime\Atn; 6*37748cd8SNickeau 7*37748cd8SNickeauuse Antlr\Antlr4\Runtime\Atn\States\ATNState; 8*37748cd8SNickeauuse Antlr\Antlr4\Runtime\Atn\States\RuleStopState; 9*37748cd8SNickeauuse Antlr\Antlr4\Runtime\Atn\Transitions\ActionTransition; 10*37748cd8SNickeauuse Antlr\Antlr4\Runtime\Atn\Transitions\PredicateTransition; 11*37748cd8SNickeauuse Antlr\Antlr4\Runtime\Atn\Transitions\RuleTransition; 12*37748cd8SNickeauuse Antlr\Antlr4\Runtime\Atn\Transitions\Transition; 13*37748cd8SNickeauuse Antlr\Antlr4\Runtime\CharStream; 14*37748cd8SNickeauuse Antlr\Antlr4\Runtime\Dfa\DFA; 15*37748cd8SNickeauuse Antlr\Antlr4\Runtime\Dfa\DFAState; 16*37748cd8SNickeauuse Antlr\Antlr4\Runtime\Error\Exceptions\LexerNoViableAltException; 17*37748cd8SNickeauuse Antlr\Antlr4\Runtime\IntStream; 18*37748cd8SNickeauuse Antlr\Antlr4\Runtime\Lexer; 19*37748cd8SNickeauuse Antlr\Antlr4\Runtime\PredictionContexts\PredictionContext; 20*37748cd8SNickeauuse Antlr\Antlr4\Runtime\PredictionContexts\PredictionContextCache; 21*37748cd8SNickeauuse Antlr\Antlr4\Runtime\PredictionContexts\SingletonPredictionContext; 22*37748cd8SNickeauuse Antlr\Antlr4\Runtime\Token; 23*37748cd8SNickeauuse Antlr\Antlr4\Runtime\Utils\StringUtils; 24*37748cd8SNickeau 25*37748cd8SNickeauclass LexerATNSimulator extends ATNSimulator 26*37748cd8SNickeau{ 27*37748cd8SNickeau public const MIN_DFA_EDGE = 0; 28*37748cd8SNickeau public const MAX_DFA_EDGE = 127; // forces unicode to stay in ATN 29*37748cd8SNickeau private const NEW_LINE_CODE = 10; 30*37748cd8SNickeau 31*37748cd8SNickeau /** @var array<string> */ 32*37748cd8SNickeau public $log = []; 33*37748cd8SNickeau 34*37748cd8SNickeau /** @var bool */ 35*37748cd8SNickeau public $debug = false; 36*37748cd8SNickeau 37*37748cd8SNickeau /** @var Lexer|null */ 38*37748cd8SNickeau protected $recog; 39*37748cd8SNickeau 40*37748cd8SNickeau /** 41*37748cd8SNickeau * The current token's starting index into the character stream. 42*37748cd8SNickeau * Shared across DFA to ATN simulation in case the ATN fails and the 43*37748cd8SNickeau * DFA did not have a previous accept state. In this case, we use the 44*37748cd8SNickeau * ATN-generated exception object. 45*37748cd8SNickeau * 46*37748cd8SNickeau * @var int 47*37748cd8SNickeau */ 48*37748cd8SNickeau protected $startIndex = -1; 49*37748cd8SNickeau 50*37748cd8SNickeau /** 51*37748cd8SNickeau * The line number 1..n within the input. 52*37748cd8SNickeau * 53*37748cd8SNickeau * @var int 54*37748cd8SNickeau */ 55*37748cd8SNickeau protected $line = 1; 56*37748cd8SNickeau 57*37748cd8SNickeau /** 58*37748cd8SNickeau * The index of the character relative to the beginning of the line 0..n-1. 59*37748cd8SNickeau * 60*37748cd8SNickeau * @var int 61*37748cd8SNickeau */ 62*37748cd8SNickeau protected $charPositionInLine = 0; 63*37748cd8SNickeau 64*37748cd8SNickeau /** @var array<DFA> */ 65*37748cd8SNickeau public $decisionToDFA = []; 66*37748cd8SNickeau 67*37748cd8SNickeau /** @var int */ 68*37748cd8SNickeau protected $mode = Lexer::DEFAULT_MODE; 69*37748cd8SNickeau 70*37748cd8SNickeau /** 71*37748cd8SNickeau * Used during DFA/ATN exec to record the most recent accept configuration info. 72*37748cd8SNickeau * 73*37748cd8SNickeau * @var SimState 74*37748cd8SNickeau */ 75*37748cd8SNickeau protected $prevAccept; 76*37748cd8SNickeau 77*37748cd8SNickeau /** 78*37748cd8SNickeau * @param array<DFA> $decisionToDFA 79*37748cd8SNickeau */ 80*37748cd8SNickeau public function __construct( 81*37748cd8SNickeau ?Lexer $recog, 82*37748cd8SNickeau ATN $atn, 83*37748cd8SNickeau array $decisionToDFA, 84*37748cd8SNickeau PredictionContextCache $sharedContextCache 85*37748cd8SNickeau ) { 86*37748cd8SNickeau parent::__construct($atn, $sharedContextCache); 87*37748cd8SNickeau 88*37748cd8SNickeau $this->decisionToDFA = $decisionToDFA; 89*37748cd8SNickeau $this->recog = $recog; 90*37748cd8SNickeau $this->prevAccept = new SimState(); 91*37748cd8SNickeau } 92*37748cd8SNickeau 93*37748cd8SNickeau public function copyState(LexerATNSimulator $simulator) : void 94*37748cd8SNickeau { 95*37748cd8SNickeau $this->charPositionInLine = $simulator->charPositionInLine; 96*37748cd8SNickeau $this->line = $simulator->line; 97*37748cd8SNickeau $this->mode = $simulator->mode; 98*37748cd8SNickeau $this->startIndex = $simulator->startIndex; 99*37748cd8SNickeau } 100*37748cd8SNickeau 101*37748cd8SNickeau public function getLine() : int 102*37748cd8SNickeau { 103*37748cd8SNickeau return $this->line; 104*37748cd8SNickeau } 105*37748cd8SNickeau 106*37748cd8SNickeau public function setLine(int $line) : void 107*37748cd8SNickeau { 108*37748cd8SNickeau $this->line = $line; 109*37748cd8SNickeau } 110*37748cd8SNickeau 111*37748cd8SNickeau public function getCharPositionInLine() : int 112*37748cd8SNickeau { 113*37748cd8SNickeau return $this->charPositionInLine; 114*37748cd8SNickeau } 115*37748cd8SNickeau 116*37748cd8SNickeau public function setCharPositionInLine(int $charPositionInLine) : void 117*37748cd8SNickeau { 118*37748cd8SNickeau $this->charPositionInLine = $charPositionInLine; 119*37748cd8SNickeau } 120*37748cd8SNickeau 121*37748cd8SNickeau public function match(CharStream $input, int $mode) : int 122*37748cd8SNickeau { 123*37748cd8SNickeau static $match_calls; 124*37748cd8SNickeau 125*37748cd8SNickeau if ($match_calls === null) { 126*37748cd8SNickeau $match_calls = 0; 127*37748cd8SNickeau } 128*37748cd8SNickeau 129*37748cd8SNickeau $match_calls++; 130*37748cd8SNickeau 131*37748cd8SNickeau $this->mode = $mode; 132*37748cd8SNickeau $mark = $input->mark(); 133*37748cd8SNickeau 134*37748cd8SNickeau try { 135*37748cd8SNickeau $this->startIndex = $input->getIndex(); 136*37748cd8SNickeau $this->prevAccept->reset(); 137*37748cd8SNickeau 138*37748cd8SNickeau $dfa = $this->decisionToDFA[$mode]; 139*37748cd8SNickeau 140*37748cd8SNickeau if ($dfa->s0 === null) { 141*37748cd8SNickeau return $this->matchATN($input); 142*37748cd8SNickeau } 143*37748cd8SNickeau return $this->execATN($input, $dfa->s0); 144*37748cd8SNickeau } finally { 145*37748cd8SNickeau $input->release($mark); 146*37748cd8SNickeau } 147*37748cd8SNickeau } 148*37748cd8SNickeau 149*37748cd8SNickeau public function reset() : void 150*37748cd8SNickeau { 151*37748cd8SNickeau $this->prevAccept->reset(); 152*37748cd8SNickeau $this->startIndex = -1; 153*37748cd8SNickeau $this->line = 1; 154*37748cd8SNickeau $this->charPositionInLine = 0; 155*37748cd8SNickeau $this->mode = Lexer::DEFAULT_MODE; 156*37748cd8SNickeau } 157*37748cd8SNickeau 158*37748cd8SNickeau protected function matchATN(CharStream $input) : int 159*37748cd8SNickeau { 160*37748cd8SNickeau $startState = $this->atn->modeToStartState[$this->mode]; 161*37748cd8SNickeau 162*37748cd8SNickeau if ($this->debug) { 163*37748cd8SNickeau $this->log[] = \sprintf('matchATN mode %d start: %s', $this->mode, (string) $startState); 164*37748cd8SNickeau } 165*37748cd8SNickeau 166*37748cd8SNickeau $old_mode = $this->mode; 167*37748cd8SNickeau 168*37748cd8SNickeau $s0_closure = $this->computeStartState($input, $startState); 169*37748cd8SNickeau $suppressEdge = $s0_closure->hasSemanticContext; 170*37748cd8SNickeau $s0_closure->hasSemanticContext = false; 171*37748cd8SNickeau 172*37748cd8SNickeau $next = $this->addDFAState($s0_closure); 173*37748cd8SNickeau 174*37748cd8SNickeau if (!$suppressEdge) { 175*37748cd8SNickeau $this->decisionToDFA[$this->mode]->s0 = $next; 176*37748cd8SNickeau } 177*37748cd8SNickeau 178*37748cd8SNickeau $predict = $this->execATN($input, $next); 179*37748cd8SNickeau 180*37748cd8SNickeau if ($this->debug) { 181*37748cd8SNickeau $this->log[] = \sprintf('DFA after matchATN: %s', $this->decisionToDFA[$old_mode]->toLexerString()); 182*37748cd8SNickeau } 183*37748cd8SNickeau 184*37748cd8SNickeau return $predict; 185*37748cd8SNickeau } 186*37748cd8SNickeau 187*37748cd8SNickeau protected function execATN(CharStream $input, DFAState $ds0) : int 188*37748cd8SNickeau { 189*37748cd8SNickeau if ($this->debug) { 190*37748cd8SNickeau $this->log[] = \sprintf('start state closure=%s', (string) $ds0->configs); 191*37748cd8SNickeau } 192*37748cd8SNickeau 193*37748cd8SNickeau if ($ds0->isAcceptState) { 194*37748cd8SNickeau // allow zero-length tokens 195*37748cd8SNickeau $this->captureSimState($this->prevAccept, $input, $ds0); 196*37748cd8SNickeau } 197*37748cd8SNickeau 198*37748cd8SNickeau $t = $input->LA(1); 199*37748cd8SNickeau $s = $ds0; // s is current/from DFA state 200*37748cd8SNickeau 201*37748cd8SNickeau while (true) { 202*37748cd8SNickeau if ($this->debug) { 203*37748cd8SNickeau $this->log[] = \sprintf('execATN loop starting closure: %s', (string) $s->configs); 204*37748cd8SNickeau } 205*37748cd8SNickeau 206*37748cd8SNickeau // As we move src->trg, src->trg, we keep track of the previous trg to 207*37748cd8SNickeau // avoid looking up the DFA state again, which is expensive. 208*37748cd8SNickeau // If the previous target was already part of the DFA, we might 209*37748cd8SNickeau // be able to avoid doing a reach operation upon t. If s!=null, 210*37748cd8SNickeau // it means that semantic predicates didn't prevent us from 211*37748cd8SNickeau // creating a DFA state. Once we know s!=null, we check to see if 212*37748cd8SNickeau // the DFA state has an edge already for t. If so, we can just reuse 213*37748cd8SNickeau // it's configuration set; there's no point in re-computing it. 214*37748cd8SNickeau // This is kind of like doing DFA simulation within the ATN 215*37748cd8SNickeau // simulation because DFA simulation is really just a way to avoid 216*37748cd8SNickeau // computing reach/closure sets. Technically, once we know that 217*37748cd8SNickeau // we have a previously added DFA state, we could jump over to 218*37748cd8SNickeau // the DFA simulator. But, that would mean popping back and forth 219*37748cd8SNickeau // a lot and making things more complicated algorithmically. 220*37748cd8SNickeau // This optimization makes a lot of sense for loops within DFA. 221*37748cd8SNickeau // A character will take us back to an existing DFA state 222*37748cd8SNickeau // that already has lots of edges out of it. e.g., .* in comments. 223*37748cd8SNickeau // print("Target for:" + str(s) + " and:" + str(t)) 224*37748cd8SNickeau $target = $this->getExistingTargetState($s, $t); 225*37748cd8SNickeau 226*37748cd8SNickeau if ($target === null) { 227*37748cd8SNickeau $target = $this->computeTargetState($input, $s, $t); 228*37748cd8SNickeau } 229*37748cd8SNickeau 230*37748cd8SNickeau if ($target === ATNSimulator::error()) { 231*37748cd8SNickeau break; 232*37748cd8SNickeau } 233*37748cd8SNickeau 234*37748cd8SNickeau // If this is a consumable input element, make sure to consume before 235*37748cd8SNickeau // capturing the accept state so the input index, line, and char 236*37748cd8SNickeau // position accurately reflect the state of the interpreter at the 237*37748cd8SNickeau // end of the token. 238*37748cd8SNickeau if ($t !== Token::EOF) { 239*37748cd8SNickeau $this->consume($input); 240*37748cd8SNickeau } 241*37748cd8SNickeau 242*37748cd8SNickeau if ($target->isAcceptState) { 243*37748cd8SNickeau $this->captureSimState($this->prevAccept, $input, $target); 244*37748cd8SNickeau if ($t === Token::EOF) { 245*37748cd8SNickeau break; 246*37748cd8SNickeau } 247*37748cd8SNickeau } 248*37748cd8SNickeau 249*37748cd8SNickeau $t = $input->LA(1); 250*37748cd8SNickeau $s = $target; // flip; current DFA target becomes new src/from state 251*37748cd8SNickeau } 252*37748cd8SNickeau 253*37748cd8SNickeau return $this->failOrAccept($this->prevAccept, $input, $s->configs, $t); 254*37748cd8SNickeau } 255*37748cd8SNickeau 256*37748cd8SNickeau /** 257*37748cd8SNickeau * Get an existing target state for an edge in the DFA. If the target state 258*37748cd8SNickeau * for the edge has not yet been computed or is otherwise not available, 259*37748cd8SNickeau * this method returns `null`. 260*37748cd8SNickeau * 261*37748cd8SNickeau * @param DFAState $s The current DFA state 262*37748cd8SNickeau * @param int $t The next input symbol 263*37748cd8SNickeau * 264*37748cd8SNickeau * @return DFAState|null The existing target DFA state for the given input symbol 265*37748cd8SNickeau * `t`, or `null` if the target state for this edg 266*37748cd8SNickeau * is not already cached. 267*37748cd8SNickeau */ 268*37748cd8SNickeau protected function getExistingTargetState(DFAState $s, int $t) : ?DFAState 269*37748cd8SNickeau { 270*37748cd8SNickeau if ($s->edges === null || $t < self::MIN_DFA_EDGE || $t > self::MAX_DFA_EDGE) { 271*37748cd8SNickeau return null; 272*37748cd8SNickeau } 273*37748cd8SNickeau 274*37748cd8SNickeau $target = $s->edges[$t - self::MIN_DFA_EDGE] ?? null; 275*37748cd8SNickeau 276*37748cd8SNickeau if ($this->debug && $target !== null) { 277*37748cd8SNickeau $this->log[] = \sprintf('reuse state %d edge to %d', $s->stateNumber, $target->stateNumber); 278*37748cd8SNickeau } 279*37748cd8SNickeau 280*37748cd8SNickeau return $target; 281*37748cd8SNickeau } 282*37748cd8SNickeau 283*37748cd8SNickeau /** 284*37748cd8SNickeau * Compute a target state for an edge in the DFA, and attempt to add the 285*37748cd8SNickeau * computed state and corresponding edge to the DFA. 286*37748cd8SNickeau * 287*37748cd8SNickeau * @param CharStream $input The input stream 288*37748cd8SNickeau * @param DFAState $s The current DFA state 289*37748cd8SNickeau * @param int $t The next input symbol 290*37748cd8SNickeau * 291*37748cd8SNickeau * @return DFAState The computed target DFA state for the given input symbol 292*37748cd8SNickeau * `t`. If `t` does not lead to a valid DFA state, this 293*37748cd8SNickeau * method returns {@see LexerATNSimulator::ERROR}. 294*37748cd8SNickeau */ 295*37748cd8SNickeau protected function computeTargetState(CharStream $input, DFAState $s, int $t) : DFAState 296*37748cd8SNickeau { 297*37748cd8SNickeau $reach = new OrderedATNConfigSet(); 298*37748cd8SNickeau 299*37748cd8SNickeau // if we don't find an existing DFA state 300*37748cd8SNickeau // Fill reach starting from closure, following t transitions 301*37748cd8SNickeau $this->getReachableConfigSet($input, $s->configs, $reach, $t); 302*37748cd8SNickeau 303*37748cd8SNickeau if (\count($reach->elements()) === 0) { 304*37748cd8SNickeau // we got nowhere on t from s 305*37748cd8SNickeau if (!$reach->hasSemanticContext) { 306*37748cd8SNickeau // we got nowhere on t, don't throw out this knowledge; it'd 307*37748cd8SNickeau // cause a failover from DFA later. 308*37748cd8SNickeau $this->addDFAEdge($s, $t, ATNSimulator::error()); 309*37748cd8SNickeau } 310*37748cd8SNickeau 311*37748cd8SNickeau // stop when we can't match any more char 312*37748cd8SNickeau return ATNSimulator::error(); 313*37748cd8SNickeau } 314*37748cd8SNickeau 315*37748cd8SNickeau // Add an edge from s to target DFA found/created for reach 316*37748cd8SNickeau return $this->addDFAEdgeATNConfigSet($s, $t, $reach) ?? ATNSimulator::error(); 317*37748cd8SNickeau } 318*37748cd8SNickeau 319*37748cd8SNickeau protected function failOrAccept(SimState $prevAccept, CharStream $input, ATNConfigSet $reach, int $t) : int 320*37748cd8SNickeau { 321*37748cd8SNickeau if ($this->prevAccept->getDfaState() !== null) { 322*37748cd8SNickeau $dfaState = $prevAccept->getDfaState(); 323*37748cd8SNickeau 324*37748cd8SNickeau if ($dfaState === null) { 325*37748cd8SNickeau throw new \RuntimeException('Unexpected null DFA State.'); 326*37748cd8SNickeau } 327*37748cd8SNickeau 328*37748cd8SNickeau $lexerActionExecutor = $dfaState->lexerActionExecutor; 329*37748cd8SNickeau 330*37748cd8SNickeau $this->accept( 331*37748cd8SNickeau $input, 332*37748cd8SNickeau $lexerActionExecutor, 333*37748cd8SNickeau $this->startIndex, 334*37748cd8SNickeau $prevAccept->getIndex(), 335*37748cd8SNickeau $prevAccept->getLine(), 336*37748cd8SNickeau $prevAccept->getCharPos() 337*37748cd8SNickeau ); 338*37748cd8SNickeau 339*37748cd8SNickeau return $dfaState->prediction; 340*37748cd8SNickeau } 341*37748cd8SNickeau 342*37748cd8SNickeau // if no accept and EOF is first char, return EOF 343*37748cd8SNickeau if ($t === IntStream::EOF && $input->getIndex() === $this->startIndex) { 344*37748cd8SNickeau return Token::EOF; 345*37748cd8SNickeau } 346*37748cd8SNickeau 347*37748cd8SNickeau if ($this->recog === null) { 348*37748cd8SNickeau throw new \RuntimeException('Unexpected null recognizer.'); 349*37748cd8SNickeau } 350*37748cd8SNickeau 351*37748cd8SNickeau throw new LexerNoViableAltException($this->recog, $input, $this->startIndex, $reach); 352*37748cd8SNickeau } 353*37748cd8SNickeau 354*37748cd8SNickeau /** 355*37748cd8SNickeau * Given a starting configuration set, figure out all ATN configurations 356*37748cd8SNickeau * we can reach upon input `t`. Parameter `reach` is a return parameter. 357*37748cd8SNickeau */ 358*37748cd8SNickeau protected function getReachableConfigSet( 359*37748cd8SNickeau CharStream $input, 360*37748cd8SNickeau ATNConfigSet $closure, 361*37748cd8SNickeau ATNConfigSet $reach, 362*37748cd8SNickeau int $t 363*37748cd8SNickeau ) : void { 364*37748cd8SNickeau // this is used to skip processing for configs which have a lower priority 365*37748cd8SNickeau // than a config that already reached an accept state for the same rule 366*37748cd8SNickeau $skipAlt = ATN::INVALID_ALT_NUMBER; 367*37748cd8SNickeau 368*37748cd8SNickeau foreach ($closure->elements() as $cfg) { 369*37748cd8SNickeau if (!$cfg instanceof LexerATNConfig) { 370*37748cd8SNickeau throw new \RuntimeException('Unexpected config type.'); 371*37748cd8SNickeau } 372*37748cd8SNickeau 373*37748cd8SNickeau $currentAltReachedAcceptState = ($cfg->alt === $skipAlt); 374*37748cd8SNickeau 375*37748cd8SNickeau if ($currentAltReachedAcceptState && $cfg->isPassedThroughNonGreedyDecision()) { 376*37748cd8SNickeau continue; 377*37748cd8SNickeau } 378*37748cd8SNickeau 379*37748cd8SNickeau if ($this->debug) { 380*37748cd8SNickeau $this->log[] = \sprintf( 381*37748cd8SNickeau "testing %s at %s\n", 382*37748cd8SNickeau $this->getTokenName($t), 383*37748cd8SNickeau $cfg->toString(true) 384*37748cd8SNickeau ); 385*37748cd8SNickeau } 386*37748cd8SNickeau 387*37748cd8SNickeau foreach ($cfg->state->getTransitions() as $trans) { 388*37748cd8SNickeau $target = $this->getReachableTarget($trans, $t); 389*37748cd8SNickeau 390*37748cd8SNickeau if ($target !== null) { 391*37748cd8SNickeau $lexerExecutor = $cfg->getLexerActionExecutor(); 392*37748cd8SNickeau 393*37748cd8SNickeau if ($lexerExecutor !== null) { 394*37748cd8SNickeau $lexerExecutor = $lexerExecutor->fixOffsetBeforeMatch($input->getIndex() - $this->startIndex); 395*37748cd8SNickeau } 396*37748cd8SNickeau 397*37748cd8SNickeau $treatEofAsEpsilon = ($t === Token::EOF); 398*37748cd8SNickeau $config = new LexerATNConfig($cfg, $target, null, $lexerExecutor); 399*37748cd8SNickeau 400*37748cd8SNickeau if ($this->closure( 401*37748cd8SNickeau $input, 402*37748cd8SNickeau $config, 403*37748cd8SNickeau $reach, 404*37748cd8SNickeau $currentAltReachedAcceptState, 405*37748cd8SNickeau true, 406*37748cd8SNickeau $treatEofAsEpsilon 407*37748cd8SNickeau )) { 408*37748cd8SNickeau // any remaining configs for this alt have a lower priority 409*37748cd8SNickeau // than the one that just reached an accept state. 410*37748cd8SNickeau $skipAlt = $cfg->alt; 411*37748cd8SNickeau } 412*37748cd8SNickeau } 413*37748cd8SNickeau } 414*37748cd8SNickeau } 415*37748cd8SNickeau } 416*37748cd8SNickeau 417*37748cd8SNickeau protected function accept( 418*37748cd8SNickeau CharStream $input, 419*37748cd8SNickeau ?LexerActionExecutor $lexerActionExecutor, 420*37748cd8SNickeau int $startIndex, 421*37748cd8SNickeau int $index, 422*37748cd8SNickeau int $line, 423*37748cd8SNickeau int $charPos 424*37748cd8SNickeau ) : void { 425*37748cd8SNickeau if ($this->debug) { 426*37748cd8SNickeau $this->log[] = \sprintf('ACTION %s', (string) $lexerActionExecutor) . \PHP_EOL; 427*37748cd8SNickeau } 428*37748cd8SNickeau 429*37748cd8SNickeau // seek to after last char in token 430*37748cd8SNickeau $input->seek($index); 431*37748cd8SNickeau $this->line = $line; 432*37748cd8SNickeau $this->charPositionInLine = $charPos; 433*37748cd8SNickeau 434*37748cd8SNickeau if ($lexerActionExecutor !== null && $this->recog) { 435*37748cd8SNickeau $lexerActionExecutor->execute($this->recog, $input, $startIndex); 436*37748cd8SNickeau } 437*37748cd8SNickeau } 438*37748cd8SNickeau 439*37748cd8SNickeau protected function getReachableTarget(Transition $trans, int $t) : ?ATNState 440*37748cd8SNickeau { 441*37748cd8SNickeau if ($trans->matches($t, Lexer::MIN_CHAR_VALUE, Lexer::MAX_CHAR_VALUE)) { 442*37748cd8SNickeau return $trans->target; 443*37748cd8SNickeau } 444*37748cd8SNickeau 445*37748cd8SNickeau return null; 446*37748cd8SNickeau } 447*37748cd8SNickeau 448*37748cd8SNickeau protected function computeStartState(CharStream $input, ATNState $p) : OrderedATNConfigSet 449*37748cd8SNickeau { 450*37748cd8SNickeau $initialContext = PredictionContext::empty(); 451*37748cd8SNickeau $configs = new OrderedATNConfigSet(); 452*37748cd8SNickeau 453*37748cd8SNickeau foreach ($p->getTransitions() as $i => $t) { 454*37748cd8SNickeau $target = $t->target; 455*37748cd8SNickeau $cfg = new LexerATNConfig(null, $target, $initialContext, null, $i + 1); 456*37748cd8SNickeau $this->closure($input, $cfg, $configs, false, false, false); 457*37748cd8SNickeau } 458*37748cd8SNickeau 459*37748cd8SNickeau return $configs; 460*37748cd8SNickeau } 461*37748cd8SNickeau 462*37748cd8SNickeau /** 463*37748cd8SNickeau * Since the alternatives within any lexer decision are ordered by 464*37748cd8SNickeau * preference, this method stops pursuing the closure as soon as an accept 465*37748cd8SNickeau * state is reached. After the first accept state is reached by depth-first 466*37748cd8SNickeau * search from `config`, all other (potentially reachable) states for 467*37748cd8SNickeau * this rule would have a lower priority. 468*37748cd8SNickeau * 469*37748cd8SNickeau * @return bool `true` if an accept state is reached, otherwise `false`. 470*37748cd8SNickeau */ 471*37748cd8SNickeau protected function closure( 472*37748cd8SNickeau CharStream $input, 473*37748cd8SNickeau LexerATNConfig $config, 474*37748cd8SNickeau ATNConfigSet $configs, 475*37748cd8SNickeau bool $currentAltReachedAcceptState, 476*37748cd8SNickeau bool $speculative, 477*37748cd8SNickeau bool $treatEofAsEpsilon 478*37748cd8SNickeau ) : bool { 479*37748cd8SNickeau $cfg = null; 480*37748cd8SNickeau 481*37748cd8SNickeau if ($this->debug) { 482*37748cd8SNickeau $this->log[] = \sprintf('closure(%s)', $config->toString(true)); 483*37748cd8SNickeau } 484*37748cd8SNickeau 485*37748cd8SNickeau if ($config->state instanceof States\RuleStopState) { 486*37748cd8SNickeau if ($this->debug) { 487*37748cd8SNickeau if ($this->recog) { 488*37748cd8SNickeau $this->log[] = \sprintf( 489*37748cd8SNickeau "closure at %s rule stop %s\n", 490*37748cd8SNickeau $this->recog->getRuleNames()[$config->state->ruleIndex], 491*37748cd8SNickeau $config 492*37748cd8SNickeau ); 493*37748cd8SNickeau } else { 494*37748cd8SNickeau $this->log[] = \sprintf("closure at rule stop %s\n", $config); 495*37748cd8SNickeau } 496*37748cd8SNickeau } 497*37748cd8SNickeau 498*37748cd8SNickeau if ($config->context === null || $config->context->hasEmptyPath()) { 499*37748cd8SNickeau if ($config->context === null || $config->context->isEmpty()) { 500*37748cd8SNickeau $configs->add($config); 501*37748cd8SNickeau 502*37748cd8SNickeau return true; 503*37748cd8SNickeau } 504*37748cd8SNickeau 505*37748cd8SNickeau $configs->add(new LexerATNConfig($config, $config->state, PredictionContext::empty())); 506*37748cd8SNickeau $currentAltReachedAcceptState = true; 507*37748cd8SNickeau } 508*37748cd8SNickeau 509*37748cd8SNickeau if ($config->context !== null && !$config->context->isEmpty()) { 510*37748cd8SNickeau for ($i = 0; $i < $config->context->getLength(); $i++) { 511*37748cd8SNickeau if ($config->context->getReturnState($i) !== PredictionContext::EMPTY_RETURN_STATE) { 512*37748cd8SNickeau $newContext = $config->context->getParent($i);// "pop" return state 513*37748cd8SNickeau $returnState = $this->atn->states[$config->context->getReturnState($i)]; 514*37748cd8SNickeau $cfg = new LexerATNConfig($config, $returnState, $newContext); 515*37748cd8SNickeau $currentAltReachedAcceptState = $this->closure( 516*37748cd8SNickeau $input, 517*37748cd8SNickeau $cfg, 518*37748cd8SNickeau $configs, 519*37748cd8SNickeau $currentAltReachedAcceptState, 520*37748cd8SNickeau $speculative, 521*37748cd8SNickeau $treatEofAsEpsilon 522*37748cd8SNickeau ); 523*37748cd8SNickeau } 524*37748cd8SNickeau } 525*37748cd8SNickeau } 526*37748cd8SNickeau 527*37748cd8SNickeau return $currentAltReachedAcceptState; 528*37748cd8SNickeau } 529*37748cd8SNickeau 530*37748cd8SNickeau // optimization 531*37748cd8SNickeau if (!$config->state->epsilonOnlyTransitions) { 532*37748cd8SNickeau if (!$currentAltReachedAcceptState || !$config->isPassedThroughNonGreedyDecision()) { 533*37748cd8SNickeau $configs->add($config); 534*37748cd8SNickeau } 535*37748cd8SNickeau } 536*37748cd8SNickeau 537*37748cd8SNickeau foreach ($config->state->getTransitions() as $trans) { 538*37748cd8SNickeau $cfg = $this->getEpsilonTarget($input, $config, $trans, $configs, $speculative, $treatEofAsEpsilon); 539*37748cd8SNickeau 540*37748cd8SNickeau if ($cfg !== null) { 541*37748cd8SNickeau $currentAltReachedAcceptState = $this->closure( 542*37748cd8SNickeau $input, 543*37748cd8SNickeau $cfg, 544*37748cd8SNickeau $configs, 545*37748cd8SNickeau $currentAltReachedAcceptState, 546*37748cd8SNickeau $speculative, 547*37748cd8SNickeau $treatEofAsEpsilon 548*37748cd8SNickeau ); 549*37748cd8SNickeau } 550*37748cd8SNickeau } 551*37748cd8SNickeau 552*37748cd8SNickeau return $currentAltReachedAcceptState; 553*37748cd8SNickeau } 554*37748cd8SNickeau 555*37748cd8SNickeau /** 556*37748cd8SNickeau * side-effect: can alter configs.hasSemanticContext 557*37748cd8SNickeau */ 558*37748cd8SNickeau protected function getEpsilonTarget( 559*37748cd8SNickeau CharStream $input, 560*37748cd8SNickeau LexerATNConfig $config, 561*37748cd8SNickeau Transition $t, 562*37748cd8SNickeau ATNConfigSet $configs, 563*37748cd8SNickeau bool $speculative, 564*37748cd8SNickeau bool $treatEofAsEpsilon 565*37748cd8SNickeau ) : ?LexerATNConfig { 566*37748cd8SNickeau $cfg = null; 567*37748cd8SNickeau 568*37748cd8SNickeau switch ($t->getSerializationType()) { 569*37748cd8SNickeau case Transition::RULE: 570*37748cd8SNickeau if (!$t instanceof RuleTransition) { 571*37748cd8SNickeau throw new \RuntimeException('Unexpected transition type.'); 572*37748cd8SNickeau } 573*37748cd8SNickeau 574*37748cd8SNickeau $newContext = SingletonPredictionContext::create($config->context, $t->followState->stateNumber); 575*37748cd8SNickeau $cfg = new LexerATNConfig($config, $t->target, $newContext); 576*37748cd8SNickeau 577*37748cd8SNickeau break; 578*37748cd8SNickeau 579*37748cd8SNickeau case Transition::PRECEDENCE: 580*37748cd8SNickeau throw new \RuntimeException('Precedence predicates are not supported in lexers.'); 581*37748cd8SNickeau 582*37748cd8SNickeau case Transition::PREDICATE: 583*37748cd8SNickeau // Track traversing semantic predicates. If we traverse, 584*37748cd8SNickeau // we cannot add a DFA state for this "reach" computation 585*37748cd8SNickeau // because the DFA would not test the predicate again in the 586*37748cd8SNickeau // future. Rather than creating collections of semantic predicates 587*37748cd8SNickeau // like v3 and testing them on prediction, v4 will test them on the 588*37748cd8SNickeau // fly all the time using the ATN not the DFA. This is slower but 589*37748cd8SNickeau // semantically it's not used that often. One of the key elements to 590*37748cd8SNickeau // this predicate mechanism is not adding DFA states that see 591*37748cd8SNickeau // predicates immediately afterwards in the ATN. For example, 592*37748cd8SNickeau 593*37748cd8SNickeau // a : ID {p1}? | ID {p2}? ; 594*37748cd8SNickeau 595*37748cd8SNickeau // should create the start state for rule 'a' (to save start state 596*37748cd8SNickeau // competition), but should not create target of ID state. The 597*37748cd8SNickeau // collection of ATN states the following ID references includes 598*37748cd8SNickeau // states reached by traversing predicates. Since this is when we 599*37748cd8SNickeau // test them, we cannot cash the DFA state target of ID. 600*37748cd8SNickeau 601*37748cd8SNickeau if (!$t instanceof PredicateTransition) { 602*37748cd8SNickeau throw new \RuntimeException('Unexpected transition type.'); 603*37748cd8SNickeau } 604*37748cd8SNickeau 605*37748cd8SNickeau if ($this->debug) { 606*37748cd8SNickeau $this->log[] = \sprintf('EVAL rule %d:%d', $t->ruleIndex, $t->predIndex); 607*37748cd8SNickeau } 608*37748cd8SNickeau 609*37748cd8SNickeau $configs->hasSemanticContext = true; 610*37748cd8SNickeau 611*37748cd8SNickeau if ($this->evaluatePredicate($input, $t->ruleIndex, $t->predIndex, $speculative)) { 612*37748cd8SNickeau $cfg = new LexerATNConfig($config, $t->target); 613*37748cd8SNickeau } 614*37748cd8SNickeau 615*37748cd8SNickeau break; 616*37748cd8SNickeau 617*37748cd8SNickeau case Transition::ACTION: 618*37748cd8SNickeau if ($config->context === null || $config->context->hasEmptyPath()) { 619*37748cd8SNickeau // execute actions anywhere in the start rule for a token. 620*37748cd8SNickeau 621*37748cd8SNickeau // TODO: if the entry rule is invoked recursively, some 622*37748cd8SNickeau // actions may be executed during the recursive call. The 623*37748cd8SNickeau // problem can appear when hasEmptyPath() is true but 624*37748cd8SNickeau // isEmpty() is false. In this case, the config needs to be 625*37748cd8SNickeau // split into two contexts - one with just the empty path 626*37748cd8SNickeau // and another with everything but the empty path. 627*37748cd8SNickeau // Unfortunately, the current algorithm does not allow 628*37748cd8SNickeau // getEpsilonTarget to return two configurations, so 629*37748cd8SNickeau // additional modifications are needed before we can support 630*37748cd8SNickeau // the split operation. 631*37748cd8SNickeau 632*37748cd8SNickeau if (!$t instanceof ActionTransition) { 633*37748cd8SNickeau throw new \RuntimeException('Unexpected transition type.'); 634*37748cd8SNickeau } 635*37748cd8SNickeau 636*37748cd8SNickeau $lexerAction = $this->atn->lexerActions[$t->actionIndex]; 637*37748cd8SNickeau 638*37748cd8SNickeau $lexerActionExecutor = LexerActionExecutor::append($config->getLexerActionExecutor(), $lexerAction); 639*37748cd8SNickeau 640*37748cd8SNickeau $cfg = new LexerATNConfig($config, $t->target, null, $lexerActionExecutor); 641*37748cd8SNickeau } else { 642*37748cd8SNickeau // ignore actions in referenced rules 643*37748cd8SNickeau $cfg = new LexerATNConfig($config, $t->target); 644*37748cd8SNickeau } 645*37748cd8SNickeau 646*37748cd8SNickeau break; 647*37748cd8SNickeau 648*37748cd8SNickeau case Transition::EPSILON: 649*37748cd8SNickeau $cfg = new LexerATNConfig($config, $t->target); 650*37748cd8SNickeau 651*37748cd8SNickeau break; 652*37748cd8SNickeau 653*37748cd8SNickeau case Transition::ATOM: 654*37748cd8SNickeau case Transition::RANGE: 655*37748cd8SNickeau case Transition::SET: 656*37748cd8SNickeau if ($treatEofAsEpsilon) { 657*37748cd8SNickeau if ($t->matches(Token::EOF, 0, Lexer::MAX_CHAR_VALUE)) { 658*37748cd8SNickeau $cfg = new LexerATNConfig($config, $t->target); 659*37748cd8SNickeau } 660*37748cd8SNickeau } 661*37748cd8SNickeau 662*37748cd8SNickeau break; 663*37748cd8SNickeau 664*37748cd8SNickeau default: 665*37748cd8SNickeau $cfg = null; 666*37748cd8SNickeau } 667*37748cd8SNickeau 668*37748cd8SNickeau return $cfg; 669*37748cd8SNickeau } 670*37748cd8SNickeau 671*37748cd8SNickeau /** 672*37748cd8SNickeau * Evaluate a predicate specified in the lexer. 673*37748cd8SNickeau * 674*37748cd8SNickeau * If `speculative` is `true`, this method was called before 675*37748cd8SNickeau * {@see LexerATNSimulator::consume()} for the matched character. This 676*37748cd8SNickeau * method should call {@see LexerATNSimulator::consume()} before evaluating 677*37748cd8SNickeau * the predicate to ensure position sensitive values, including 678*37748cd8SNickeau * {@see Lexer::getText()}, {@see Lexer::getLine()}, and 679*37748cd8SNickeau * {@see Lexer::getCharPositionInLine()}, properly reflect the current 680*37748cd8SNickeau * lexer state. This method should restore `input` and the simulator 681*37748cd8SNickeau * to the original state before returning (i.e. undo the actions made by 682*37748cd8SNickeau * the call to {@see LexerATNSimulator::consume()}. 683*37748cd8SNickeau * 684*37748cd8SNickeau * @param CharStream $input The input stream. 685*37748cd8SNickeau * @param int $ruleIndex The rule containing the predicate. 686*37748cd8SNickeau * @param int $predIndex The index of the predicate within the rule. 687*37748cd8SNickeau * @param bool $speculative `true` if the current index in `input` is 688*37748cd8SNickeau * one character before the predicate's location. 689*37748cd8SNickeau * 690*37748cd8SNickeau * @return bool `true` If the specified predicate evaluates to `true`. 691*37748cd8SNickeau */ 692*37748cd8SNickeau protected function evaluatePredicate(CharStream $input, int $ruleIndex, int $predIndex, bool $speculative) : bool 693*37748cd8SNickeau { 694*37748cd8SNickeau if ($this->recog === null) { 695*37748cd8SNickeau return true; 696*37748cd8SNickeau } 697*37748cd8SNickeau 698*37748cd8SNickeau if (!$speculative) { 699*37748cd8SNickeau return $this->recog->sempred(null, $ruleIndex, $predIndex); 700*37748cd8SNickeau } 701*37748cd8SNickeau 702*37748cd8SNickeau $savedcolumn = $this->charPositionInLine; 703*37748cd8SNickeau $savedLine = $this->line; 704*37748cd8SNickeau $index = $input->getIndex(); 705*37748cd8SNickeau $marker = $input->mark(); 706*37748cd8SNickeau 707*37748cd8SNickeau try { 708*37748cd8SNickeau $this->consume($input); 709*37748cd8SNickeau 710*37748cd8SNickeau return $this->recog->sempred(null, $ruleIndex, $predIndex); 711*37748cd8SNickeau } finally { 712*37748cd8SNickeau $this->charPositionInLine = $savedcolumn; 713*37748cd8SNickeau $this->line = $savedLine; 714*37748cd8SNickeau $input->seek($index); 715*37748cd8SNickeau $input->release($marker); 716*37748cd8SNickeau } 717*37748cd8SNickeau } 718*37748cd8SNickeau 719*37748cd8SNickeau protected function captureSimState(SimState $settings, CharStream $input, DFAState $dfaState) : void 720*37748cd8SNickeau { 721*37748cd8SNickeau $settings->setIndex($input->getIndex()); 722*37748cd8SNickeau $settings->setLine($this->line); 723*37748cd8SNickeau $settings->setCharPos($this->charPositionInLine); 724*37748cd8SNickeau $settings->setDfaState($dfaState); 725*37748cd8SNickeau } 726*37748cd8SNickeau 727*37748cd8SNickeau protected function addDFAEdgeATNConfigSet(DFAState $from, int $t, ATNConfigSet $configs) : DFAState 728*37748cd8SNickeau { 729*37748cd8SNickeau /* leading to this call, ATNConfigSet.hasSemanticContext is used as a 730*37748cd8SNickeau * marker indicating dynamic predicate evaluation makes this edge 731*37748cd8SNickeau * dependent on the specific input sequence, so the static edge in the 732*37748cd8SNickeau * DFA should be omitted. The target DFAState is still created since 733*37748cd8SNickeau * execATN has the ability to resynchronize with the DFA state cache 734*37748cd8SNickeau * following the predicate evaluation step. 735*37748cd8SNickeau * 736*37748cd8SNickeau * TJP notes: next time through the DFA, we see a pred again and eval. 737*37748cd8SNickeau * If that gets us to a previously created (but dangling) DFA 738*37748cd8SNickeau * state, we can continue in pure DFA mode from there. 739*37748cd8SNickeau */ 740*37748cd8SNickeau $suppressEdge = $configs->hasSemanticContext; 741*37748cd8SNickeau $configs->hasSemanticContext = false; 742*37748cd8SNickeau 743*37748cd8SNickeau $to = $this->addDFAState($configs); 744*37748cd8SNickeau 745*37748cd8SNickeau if ($suppressEdge) { 746*37748cd8SNickeau return $to; 747*37748cd8SNickeau } 748*37748cd8SNickeau 749*37748cd8SNickeau $this->addDFAEdge($from, $t, $to); 750*37748cd8SNickeau 751*37748cd8SNickeau return $to; 752*37748cd8SNickeau } 753*37748cd8SNickeau 754*37748cd8SNickeau protected function addDFAEdge(DFAState $from, int $t, ?DFAState $to) : void 755*37748cd8SNickeau { 756*37748cd8SNickeau // add the edge 757*37748cd8SNickeau if ($t < self::MIN_DFA_EDGE || $t > self::MAX_DFA_EDGE) { 758*37748cd8SNickeau // Only track edges within the DFA bounds 759*37748cd8SNickeau return; 760*37748cd8SNickeau } 761*37748cd8SNickeau 762*37748cd8SNickeau if ($this->debug) { 763*37748cd8SNickeau $this->log[] = \sprintf('EDGE %s->%s upon %d', $from, $to, $t); 764*37748cd8SNickeau } 765*37748cd8SNickeau 766*37748cd8SNickeau if ($from->edges === null) { 767*37748cd8SNickeau // make room for tokens 1..n and -1 masquerading as index 0 768*37748cd8SNickeau $from->edges = new \SplFixedArray(self::MAX_DFA_EDGE - self::MIN_DFA_EDGE + 1); 769*37748cd8SNickeau } 770*37748cd8SNickeau 771*37748cd8SNickeau $from->edges[$t - self::MIN_DFA_EDGE] = $to; // connect 772*37748cd8SNickeau } 773*37748cd8SNickeau 774*37748cd8SNickeau /** 775*37748cd8SNickeau * Add a new DFA state if there isn't one with this set of configurations 776*37748cd8SNickeau * already. This method also detects the first configuration containing 777*37748cd8SNickeau * an ATN rule stop state. Later, when traversing the DFA, we will know 778*37748cd8SNickeau * which rule to accept. 779*37748cd8SNickeau */ 780*37748cd8SNickeau protected function addDFAState(ATNConfigSet $configs) : DFAState 781*37748cd8SNickeau { 782*37748cd8SNickeau if ($configs->hasSemanticContext) { 783*37748cd8SNickeau throw new \RuntimeException('ATN Config Set cannot have semantic context.'); 784*37748cd8SNickeau } 785*37748cd8SNickeau 786*37748cd8SNickeau $proposed = new DFAState($configs); 787*37748cd8SNickeau 788*37748cd8SNickeau $firstConfigWithRuleStopState = null; 789*37748cd8SNickeau 790*37748cd8SNickeau foreach ($configs->elements() as $config) { 791*37748cd8SNickeau if ($config->state instanceof RuleStopState) { 792*37748cd8SNickeau $firstConfigWithRuleStopState = $config; 793*37748cd8SNickeau 794*37748cd8SNickeau break; 795*37748cd8SNickeau } 796*37748cd8SNickeau } 797*37748cd8SNickeau 798*37748cd8SNickeau if ($firstConfigWithRuleStopState !== null) { 799*37748cd8SNickeau if (!$firstConfigWithRuleStopState instanceof LexerATNConfig) { 800*37748cd8SNickeau throw new \RuntimeException('Unexpected ATN config type.'); 801*37748cd8SNickeau } 802*37748cd8SNickeau 803*37748cd8SNickeau $proposed->isAcceptState = true; 804*37748cd8SNickeau $proposed->lexerActionExecutor = $firstConfigWithRuleStopState->getLexerActionExecutor(); 805*37748cd8SNickeau 806*37748cd8SNickeau $prediction = $this->atn->ruleToTokenType[$firstConfigWithRuleStopState->state->ruleIndex]; 807*37748cd8SNickeau 808*37748cd8SNickeau $proposed->prediction = $prediction ?? 0; 809*37748cd8SNickeau } 810*37748cd8SNickeau 811*37748cd8SNickeau $dfa = $this->decisionToDFA[$this->mode]; 812*37748cd8SNickeau 813*37748cd8SNickeau $existing = $dfa->states->get($proposed); 814*37748cd8SNickeau 815*37748cd8SNickeau if ($existing !== null && $existing instanceof DFAState) { 816*37748cd8SNickeau return $existing; 817*37748cd8SNickeau } 818*37748cd8SNickeau 819*37748cd8SNickeau $newState = $proposed; 820*37748cd8SNickeau $newState->stateNumber = $dfa->states->count(); 821*37748cd8SNickeau $configs->setReadonly(true); 822*37748cd8SNickeau $newState->configs = $configs; 823*37748cd8SNickeau $dfa->states->add($newState); 824*37748cd8SNickeau 825*37748cd8SNickeau return $newState; 826*37748cd8SNickeau } 827*37748cd8SNickeau 828*37748cd8SNickeau public function getDFA(int $mode) : DFA 829*37748cd8SNickeau { 830*37748cd8SNickeau return $this->decisionToDFA[$mode]; 831*37748cd8SNickeau } 832*37748cd8SNickeau 833*37748cd8SNickeau /** 834*37748cd8SNickeau * Get the text matched so far for the current token. 835*37748cd8SNickeau */ 836*37748cd8SNickeau public function getText(CharStream $input) : string 837*37748cd8SNickeau { 838*37748cd8SNickeau // index is first lookahead char, don't include. 839*37748cd8SNickeau return $input->getText($this->startIndex, $input->getIndex() - 1); 840*37748cd8SNickeau } 841*37748cd8SNickeau 842*37748cd8SNickeau public function consume(CharStream $input) : void 843*37748cd8SNickeau { 844*37748cd8SNickeau $curChar = $input->LA(1); 845*37748cd8SNickeau 846*37748cd8SNickeau if ($curChar === self::NEW_LINE_CODE) { 847*37748cd8SNickeau $this->line++; 848*37748cd8SNickeau $this->charPositionInLine = 0; 849*37748cd8SNickeau } else { 850*37748cd8SNickeau $this->charPositionInLine++; 851*37748cd8SNickeau } 852*37748cd8SNickeau 853*37748cd8SNickeau $input->consume(); 854*37748cd8SNickeau } 855*37748cd8SNickeau 856*37748cd8SNickeau public function getTokenName(int $t) : ?string 857*37748cd8SNickeau { 858*37748cd8SNickeau if ($t === -1) { 859*37748cd8SNickeau return 'EOF'; 860*37748cd8SNickeau } 861*37748cd8SNickeau 862*37748cd8SNickeau return \sprintf('\'%s\'', StringUtils::char($t)); 863*37748cd8SNickeau } 864*37748cd8SNickeau} 865