xref: /plugin/combo/vendor/antlr/antlr4-php-runtime/src/Dfa/DFA.php (revision 37748cd8654635afbeca80942126742f0f4cc346)
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