1*37748cd8SNickeau<?php 2*37748cd8SNickeau 3*37748cd8SNickeaudeclare(strict_types=1); 4*37748cd8SNickeau 5*37748cd8SNickeaunamespace Antlr\Antlr4\Runtime\Dfa; 6*37748cd8SNickeau 7*37748cd8SNickeauuse Antlr\Antlr4\Runtime\Atn\States\DecisionState; 8*37748cd8SNickeauuse Antlr\Antlr4\Runtime\Atn\States\StarLoopEntryState; 9*37748cd8SNickeauuse Antlr\Antlr4\Runtime\Utils\Set; 10*37748cd8SNickeauuse Antlr\Antlr4\Runtime\Vocabulary; 11*37748cd8SNickeauuse Antlr\Antlr4\Runtime\VocabularyImpl; 12*37748cd8SNickeau 13*37748cd8SNickeaufinal class DFA 14*37748cd8SNickeau{ 15*37748cd8SNickeau /** 16*37748cd8SNickeau * A set of all DFA states. Use {@see Map} so we can get old state back 17*37748cd8SNickeau * ({@see Set} only allows you to see if it's there). 18*37748cd8SNickeau * 19*37748cd8SNickeau * @var Set; 20*37748cd8SNickeau */ 21*37748cd8SNickeau public $states; 22*37748cd8SNickeau 23*37748cd8SNickeau /** @var DFAState|null */ 24*37748cd8SNickeau public $s0; 25*37748cd8SNickeau 26*37748cd8SNickeau /** @var int */ 27*37748cd8SNickeau public $decision; 28*37748cd8SNickeau 29*37748cd8SNickeau /** 30*37748cd8SNickeau * From which ATN state did we create this DFA? 31*37748cd8SNickeau * 32*37748cd8SNickeau * @var DecisionState 33*37748cd8SNickeau */ 34*37748cd8SNickeau public $atnStartState; 35*37748cd8SNickeau 36*37748cd8SNickeau /** 37*37748cd8SNickeau * `true` if this DFA is for a precedence decision; otherwise, `false`. 38*37748cd8SNickeau * This is the backing field for {@see DFA::isPrecedenceDfa()}. 39*37748cd8SNickeau * 40*37748cd8SNickeau * @var bool 41*37748cd8SNickeau */ 42*37748cd8SNickeau private $precedenceDfa; 43*37748cd8SNickeau 44*37748cd8SNickeau public function __construct(DecisionState $atnStartState, int $decision = 0) 45*37748cd8SNickeau { 46*37748cd8SNickeau $this->atnStartState = $atnStartState; 47*37748cd8SNickeau $this->decision = $decision; 48*37748cd8SNickeau $this->states = new Set(); 49*37748cd8SNickeau $this->precedenceDfa = false; 50*37748cd8SNickeau 51*37748cd8SNickeau if ($atnStartState instanceof StarLoopEntryState) { 52*37748cd8SNickeau if ($atnStartState->isPrecedenceDecision) { 53*37748cd8SNickeau $this->precedenceDfa = true; 54*37748cd8SNickeau $precedenceState = new DFAState(); 55*37748cd8SNickeau $precedenceState->edges = new \SplFixedArray(); 56*37748cd8SNickeau $precedenceState->isAcceptState = false; 57*37748cd8SNickeau $precedenceState->requiresFullContext = false; 58*37748cd8SNickeau $this->s0 = $precedenceState; 59*37748cd8SNickeau } 60*37748cd8SNickeau } 61*37748cd8SNickeau } 62*37748cd8SNickeau 63*37748cd8SNickeau /** 64*37748cd8SNickeau * Gets whether this DFA is a precedence DFA. Precedence DFAs use a special 65*37748cd8SNickeau * start state {@see DFA::$s0} which is not stored in {@see DFA::$states}. 66*37748cd8SNickeau * The {@see DFAState::$edges} array for this start state contains outgoing 67*37748cd8SNickeau * edges supplying individual start states corresponding to specific 68*37748cd8SNickeau * precedence values. 69*37748cd8SNickeau * 70*37748cd8SNickeau * @return bool `true` if this is a precedence DFA; otherwise, `false`. 71*37748cd8SNickeau * 72*37748cd8SNickeau * @see Parser::getPrecedence() 73*37748cd8SNickeau */ 74*37748cd8SNickeau public function isPrecedenceDfa() : bool 75*37748cd8SNickeau { 76*37748cd8SNickeau return $this->precedenceDfa; 77*37748cd8SNickeau } 78*37748cd8SNickeau 79*37748cd8SNickeau /** 80*37748cd8SNickeau * Get the start state for a specific precedence value. 81*37748cd8SNickeau * 82*37748cd8SNickeau * @param int $precedence The current precedence. 83*37748cd8SNickeau * 84*37748cd8SNickeau * @return DFAState|null The start state corresponding to the specified 85*37748cd8SNickeau * precedence, or `null` if no start state exists 86*37748cd8SNickeau * for the specified precedence. 87*37748cd8SNickeau * 88*37748cd8SNickeau * @throws \InvalidArgumentException If this is not a precedence DFA. 89*37748cd8SNickeau */ 90*37748cd8SNickeau public function getPrecedenceStartState(int $precedence) : ?DFAState 91*37748cd8SNickeau { 92*37748cd8SNickeau if (!$this->precedenceDfa || $this->s0 === null) { 93*37748cd8SNickeau throw new \InvalidArgumentException('Only precedence DFAs may contain a precedence start state.'); 94*37748cd8SNickeau } 95*37748cd8SNickeau 96*37748cd8SNickeau if ($this->s0->edges === null) { 97*37748cd8SNickeau throw new \RuntimeException('s0.edges cannot be null for a precedence DFA.'); 98*37748cd8SNickeau } 99*37748cd8SNickeau 100*37748cd8SNickeau if ($precedence < 0 || $precedence >= \count($this->s0->edges)) { 101*37748cd8SNickeau return null; 102*37748cd8SNickeau } 103*37748cd8SNickeau 104*37748cd8SNickeau return $this->s0->edges[$precedence] ?? null; 105*37748cd8SNickeau } 106*37748cd8SNickeau 107*37748cd8SNickeau /** 108*37748cd8SNickeau * Set the start state for a specific precedence value. 109*37748cd8SNickeau * 110*37748cd8SNickeau * @param int $precedence The current precedence. 111*37748cd8SNickeau * @param DFAState $startState The start state corresponding to the 112*37748cd8SNickeau * specified precedence. 113*37748cd8SNickeau * 114*37748cd8SNickeau * @throws \InvalidArgumentException If this is not a precedence DFA. 115*37748cd8SNickeau */ 116*37748cd8SNickeau public function setPrecedenceStartState(int $precedence, DFAState $startState) : void 117*37748cd8SNickeau { 118*37748cd8SNickeau if (!$this->precedenceDfa || $this->s0 === null) { 119*37748cd8SNickeau throw new \InvalidArgumentException('Only precedence DFAs may contain a precedence start state.'); 120*37748cd8SNickeau } 121*37748cd8SNickeau 122*37748cd8SNickeau if ($precedence < 0) { 123*37748cd8SNickeau return; 124*37748cd8SNickeau } 125*37748cd8SNickeau 126*37748cd8SNickeau if ($this->s0->edges === null) { 127*37748cd8SNickeau throw new \RuntimeException('Unexpected null edges.'); 128*37748cd8SNickeau } 129*37748cd8SNickeau 130*37748cd8SNickeau if ($precedence >= $this->s0->edges->count()) { 131*37748cd8SNickeau $this->s0->edges->setSize($precedence + 1); 132*37748cd8SNickeau } 133*37748cd8SNickeau 134*37748cd8SNickeau // synchronization on s0 here is ok. when the DFA is turned into a 135*37748cd8SNickeau // precedence DFA, s0 will be initialized once and not updated again 136*37748cd8SNickeau // s0.edges is never null for a precedence DFA 137*37748cd8SNickeau $this->s0->edges[$precedence] = $startState; 138*37748cd8SNickeau } 139*37748cd8SNickeau 140*37748cd8SNickeau /** 141*37748cd8SNickeau * Return a list of all states in this DFA, ordered by state number. 142*37748cd8SNickeau * 143*37748cd8SNickeau * @return array<DFAState> 144*37748cd8SNickeau */ 145*37748cd8SNickeau public function getStates() : array 146*37748cd8SNickeau { 147*37748cd8SNickeau $list = $this->states->getValues(); 148*37748cd8SNickeau 149*37748cd8SNickeau \usort($list, static function (DFAState $a, DFAState $b) { 150*37748cd8SNickeau return $a->stateNumber - $b->stateNumber; 151*37748cd8SNickeau }); 152*37748cd8SNickeau 153*37748cd8SNickeau return $list; 154*37748cd8SNickeau } 155*37748cd8SNickeau 156*37748cd8SNickeau public function __toString() : string 157*37748cd8SNickeau { 158*37748cd8SNickeau return $this->toString(VocabularyImpl::emptyVocabulary()); 159*37748cd8SNickeau } 160*37748cd8SNickeau 161*37748cd8SNickeau public function toString(Vocabulary $vocabulary) : string 162*37748cd8SNickeau { 163*37748cd8SNickeau if ($this->s0 === null) { 164*37748cd8SNickeau return ''; 165*37748cd8SNickeau } 166*37748cd8SNickeau 167*37748cd8SNickeau $serializer = new DFASerializer($this, $vocabulary); 168*37748cd8SNickeau 169*37748cd8SNickeau return (string) $serializer; 170*37748cd8SNickeau } 171*37748cd8SNickeau 172*37748cd8SNickeau public function toLexerString() : string 173*37748cd8SNickeau { 174*37748cd8SNickeau if ($this->s0 === null) { 175*37748cd8SNickeau return ''; 176*37748cd8SNickeau } 177*37748cd8SNickeau 178*37748cd8SNickeau return (new LexerDFASerializer($this))->toString(); 179*37748cd8SNickeau } 180*37748cd8SNickeau} 181