xref: /template/strap/vendor/antlr/antlr4-php-runtime/src/LL1Analyzer.php (revision 37748cd8654635afbeca80942126742f0f4cc346)
1*37748cd8SNickeau<?php
2*37748cd8SNickeau
3*37748cd8SNickeaudeclare(strict_types=1);
4*37748cd8SNickeau
5*37748cd8SNickeaunamespace Antlr\Antlr4\Runtime;
6*37748cd8SNickeau
7*37748cd8SNickeauuse Antlr\Antlr4\Runtime\Atn\ATN;
8*37748cd8SNickeauuse Antlr\Antlr4\Runtime\Atn\ATNConfig;
9*37748cd8SNickeauuse Antlr\Antlr4\Runtime\Atn\States\ATNState;
10*37748cd8SNickeauuse Antlr\Antlr4\Runtime\Atn\States\RuleStopState;
11*37748cd8SNickeauuse Antlr\Antlr4\Runtime\Atn\Transitions\AbstractPredicateTransition;
12*37748cd8SNickeauuse Antlr\Antlr4\Runtime\Atn\Transitions\NotSetTransition;
13*37748cd8SNickeauuse Antlr\Antlr4\Runtime\Atn\Transitions\RuleTransition;
14*37748cd8SNickeauuse Antlr\Antlr4\Runtime\Atn\Transitions\Transition;
15*37748cd8SNickeauuse Antlr\Antlr4\Runtime\Atn\Transitions\WildcardTransition;
16*37748cd8SNickeauuse Antlr\Antlr4\Runtime\PredictionContexts\PredictionContext;
17*37748cd8SNickeauuse Antlr\Antlr4\Runtime\PredictionContexts\SingletonPredictionContext;
18*37748cd8SNickeauuse Antlr\Antlr4\Runtime\Utils\BitSet;
19*37748cd8SNickeauuse Antlr\Antlr4\Runtime\Utils\Set;
20*37748cd8SNickeau
21*37748cd8SNickeauclass LL1Analyzer
22*37748cd8SNickeau{
23*37748cd8SNickeau    /**
24*37748cd8SNickeau     * Special value added to the lookahead sets to indicate that we hit
25*37748cd8SNickeau     * a predicate during analysis if `seeThruPreds === false`.
26*37748cd8SNickeau     */
27*37748cd8SNickeau    public const HIT_PRED = Token::INVALID_TYPE;
28*37748cd8SNickeau
29*37748cd8SNickeau    /** @var ATN */
30*37748cd8SNickeau    public $atn;
31*37748cd8SNickeau
32*37748cd8SNickeau    public function __construct(ATN $atn)
33*37748cd8SNickeau    {
34*37748cd8SNickeau        $this->atn = $atn;
35*37748cd8SNickeau    }
36*37748cd8SNickeau
37*37748cd8SNickeau    /**
38*37748cd8SNickeau     * Calculates the SLL(1) expected lookahead set for each outgoing transition
39*37748cd8SNickeau     * of an {@see ATNState}. The returned array has one element for each
40*37748cd8SNickeau     * outgoing transition in `s`. If the closure from transition
41*37748cd8SNickeau     * `i` leads to a semantic predicate before matching a symbol, the
42*37748cd8SNickeau     * element at index `i` of the result will be `null`.
43*37748cd8SNickeau     *
44*37748cd8SNickeau     * @param ATNState|null $s The ATN state
45*37748cd8SNickeau     *
46*37748cd8SNickeau     * @return array<IntervalSet|null>|null The expected symbols for
47*37748cd8SNickeau     *                                      each outgoing transition of `s`.
48*37748cd8SNickeau     */
49*37748cd8SNickeau    public function getDecisionLookahead(?ATNState $s) : ?array
50*37748cd8SNickeau    {
51*37748cd8SNickeau        if ($s === null) {
52*37748cd8SNickeau            return null;
53*37748cd8SNickeau        }
54*37748cd8SNickeau
55*37748cd8SNickeau        $look = [];
56*37748cd8SNickeau        for ($alt = 0; $alt < $s->getNumberOfTransitions(); $alt++) {
57*37748cd8SNickeau            $interval = new IntervalSet();
58*37748cd8SNickeau            $lookBusy = new Set();
59*37748cd8SNickeau            $seeThruPreds = false; // fail to get lookahead upon pred
60*37748cd8SNickeau
61*37748cd8SNickeau            $this->lookRecursively(
62*37748cd8SNickeau                $s->getTransition($alt)->target,
63*37748cd8SNickeau                null,
64*37748cd8SNickeau                PredictionContext::empty(),
65*37748cd8SNickeau                $interval,
66*37748cd8SNickeau                $lookBusy,
67*37748cd8SNickeau                new BitSet(),
68*37748cd8SNickeau                $seeThruPreds,
69*37748cd8SNickeau                false
70*37748cd8SNickeau            );
71*37748cd8SNickeau
72*37748cd8SNickeau            // Wipe out lookahead for this alternative if we found nothing
73*37748cd8SNickeau            // or we had a predicate when we !seeThruPreds
74*37748cd8SNickeau            if ($interval->length() === 0 || $interval->contains(self::HIT_PRED)) {
75*37748cd8SNickeau                $look[$alt] = null;
76*37748cd8SNickeau            } else {
77*37748cd8SNickeau                $look[$alt] = $interval;
78*37748cd8SNickeau            }
79*37748cd8SNickeau        }
80*37748cd8SNickeau
81*37748cd8SNickeau        return $look;
82*37748cd8SNickeau    }
83*37748cd8SNickeau
84*37748cd8SNickeau    /**
85*37748cd8SNickeau     * Compute set of tokens that can follow `s` in the ATN in the
86*37748cd8SNickeau     * specified `context`.
87*37748cd8SNickeau     *
88*37748cd8SNickeau     * If `context` is `null` and the end of the rule containing
89*37748cd8SNickeau     * `s` is reached, {@see Token::EPSILON} is added to the result set.
90*37748cd8SNickeau     * If `context` is not `null` and the end of the outermost rule is
91*37748cd8SNickeau     * reached, {@see Token::EOF} is added to the result set.
92*37748cd8SNickeau     *
93*37748cd8SNickeau     * @param ATNState         $s         The ATN state.
94*37748cd8SNickeau     * @param ATNState|null    $stopState The ATN state to stop at. This can be
95*37748cd8SNickeau     *                                    a {@see BlockEndState} to detect
96*37748cd8SNickeau     *                                    epsilon paths through a closure.
97*37748cd8SNickeau     * @param RuleContext|null $context   The complete parser context, or `null`
98*37748cd8SNickeau     *                                    if the context should be ignored.
99*37748cd8SNickeau     *
100*37748cd8SNickeau     * @return IntervalSet The set of tokens that can follow `s` in the ATN
101*37748cd8SNickeau     *                     in the specified `context`.
102*37748cd8SNickeau     */
103*37748cd8SNickeau    public function look(ATNState $s, ?ATNState $stopState, ?RuleContext $context) : IntervalSet
104*37748cd8SNickeau    {
105*37748cd8SNickeau        $r = new IntervalSet();
106*37748cd8SNickeau        $seeThruPreds = true;// ignore preds; get all lookahead
107*37748cd8SNickeau
108*37748cd8SNickeau        $lookContext = $context !== null && $s->atn !== null ?
109*37748cd8SNickeau            PredictionContext::fromRuleContext($s->atn, $context) :
110*37748cd8SNickeau            null;
111*37748cd8SNickeau
112*37748cd8SNickeau        $this->lookRecursively(
113*37748cd8SNickeau            $s,
114*37748cd8SNickeau            $stopState,
115*37748cd8SNickeau            $lookContext,
116*37748cd8SNickeau            $r,
117*37748cd8SNickeau            new Set(),
118*37748cd8SNickeau            new BitSet(),
119*37748cd8SNickeau            $seeThruPreds,
120*37748cd8SNickeau            true
121*37748cd8SNickeau        );
122*37748cd8SNickeau
123*37748cd8SNickeau        return $r;
124*37748cd8SNickeau    }
125*37748cd8SNickeau
126*37748cd8SNickeau    /**
127*37748cd8SNickeau     * Compute set of tokens that can follow `s` in the ATN in the
128*37748cd8SNickeau     * specified `context`.
129*37748cd8SNickeau     *
130*37748cd8SNickeau     * If `context` is `null` and `stopState` or the end of the
131*37748cd8SNickeau     * rule containing `s` is reached, {@see Token::EPSILON} is added to
132*37748cd8SNickeau     * the result set. If `context` is not `null` and `addEOF` is
133*37748cd8SNickeau     * `true` and `stopState` or the end of the outermost rule is
134*37748cd8SNickeau     * reached, {@see Token::EOF} is added to the result set.
135*37748cd8SNickeau     *
136*37748cd8SNickeau     * @param ATNState               $s               The ATN state.
137*37748cd8SNickeau     * @param ATNState|null          $stopState       The ATN state to stop at.
138*37748cd8SNickeau     *                                                This can be a
139*37748cd8SNickeau     *                                                {@see BlockEndState} to
140*37748cd8SNickeau     *                                                detect epsilon paths
141*37748cd8SNickeau     *                                                through a closure.
142*37748cd8SNickeau     * @param PredictionContext|null $context         The outer context, or `null`
143*37748cd8SNickeau     *                                                if the outer context should
144*37748cd8SNickeau     *                                                not be used.
145*37748cd8SNickeau     * @param IntervalSet            $look            The result lookahead set.
146*37748cd8SNickeau     * @param Set                    $lookBusy        A set used for preventing
147*37748cd8SNickeau     *                                                epsilon closures in the ATN
148*37748cd8SNickeau     *                                                from causing a stack overflow.
149*37748cd8SNickeau     *                                                Outside code should pass
150*37748cd8SNickeau     *                                                `new Set<ATNConfig>}` for
151*37748cd8SNickeau     *                                                this argument.
152*37748cd8SNickeau     * @param BitSet                 $calledRuleStack A set used for preventing
153*37748cd8SNickeau     *                                                left recursion in the ATN
154*37748cd8SNickeau     *                                                from causing a stack overflow.
155*37748cd8SNickeau     *                                                Outside code should pass
156*37748cd8SNickeau     *                                                `new BitSet()` for
157*37748cd8SNickeau     *                                                this argument.
158*37748cd8SNickeau     * @param bool                   $seeThruPreds    `true` to true semantic
159*37748cd8SNickeau     *                                                predicates as implicitly
160*37748cd8SNickeau     *                                                `true` and "see through them",
161*37748cd8SNickeau     *                                                otherwise `false` to treat
162*37748cd8SNickeau     *                                                semantic predicates as
163*37748cd8SNickeau     *                                                opaque and add
164*37748cd8SNickeau     *                                                {@see self::HIT_PRED} to
165*37748cd8SNickeau     *                                                the result if one is
166*37748cd8SNickeau     *                                                encountered.
167*37748cd8SNickeau     * @param bool                   $addEOF          Add {@see Token::EOF} to
168*37748cd8SNickeau     *                                                the result if the end of
169*37748cd8SNickeau     *                                                the outermost context is
170*37748cd8SNickeau     *                                                reached. This parameter
171*37748cd8SNickeau     *                                                has no effect if `context`
172*37748cd8SNickeau     *                                                is `null`.
173*37748cd8SNickeau     */
174*37748cd8SNickeau    protected function lookRecursively(
175*37748cd8SNickeau        ATNState $s,
176*37748cd8SNickeau        ?ATNState $stopState,
177*37748cd8SNickeau        ?PredictionContext $context,
178*37748cd8SNickeau        IntervalSet $look,
179*37748cd8SNickeau        Set $lookBusy,
180*37748cd8SNickeau        BitSet $calledRuleStack,
181*37748cd8SNickeau        bool $seeThruPreds,
182*37748cd8SNickeau        bool $addEOF
183*37748cd8SNickeau    ) : void {
184*37748cd8SNickeau        $c = new ATNConfig(null, $s, $context, null, 0);
185*37748cd8SNickeau
186*37748cd8SNickeau        if (!$lookBusy->add($c)) {
187*37748cd8SNickeau            return;
188*37748cd8SNickeau        }
189*37748cd8SNickeau
190*37748cd8SNickeau        if ($stopState !== null && $s->equals($stopState)) {
191*37748cd8SNickeau            if ($context === null) {
192*37748cd8SNickeau                $look->addOne(Token::EPSILON);
193*37748cd8SNickeau
194*37748cd8SNickeau                return;
195*37748cd8SNickeau            }
196*37748cd8SNickeau
197*37748cd8SNickeau            if ($context->isEmpty() && $addEOF) {
198*37748cd8SNickeau                $look->addOne(Token::EOF);
199*37748cd8SNickeau
200*37748cd8SNickeau                return;
201*37748cd8SNickeau            }
202*37748cd8SNickeau        }
203*37748cd8SNickeau
204*37748cd8SNickeau        if ($s instanceof RuleStopState) {
205*37748cd8SNickeau            if ($context === null) {
206*37748cd8SNickeau                $look->addOne(Token::EPSILON);
207*37748cd8SNickeau
208*37748cd8SNickeau                return;
209*37748cd8SNickeau            }
210*37748cd8SNickeau
211*37748cd8SNickeau            if ($context->isEmpty() && $addEOF) {
212*37748cd8SNickeau                $look->addOne(Token::EOF);
213*37748cd8SNickeau
214*37748cd8SNickeau                return;
215*37748cd8SNickeau            }
216*37748cd8SNickeau
217*37748cd8SNickeau            if ($context !== PredictionContext::empty()) {
218*37748cd8SNickeau                // run thru all possible stack tops in ctx
219*37748cd8SNickeau                $removed = $calledRuleStack->contains($s->ruleIndex);
220*37748cd8SNickeau
221*37748cd8SNickeau                try {
222*37748cd8SNickeau                    $calledRuleStack->remove($s->ruleIndex);
223*37748cd8SNickeau                    for ($i = 0; $i < $context->getLength(); $i++) {
224*37748cd8SNickeau                        $returnState = $this->atn->states[$context->getReturnState($i)];
225*37748cd8SNickeau                        $this->lookRecursively(
226*37748cd8SNickeau                            $returnState,
227*37748cd8SNickeau                            $stopState,
228*37748cd8SNickeau                            $context->getParent($i),
229*37748cd8SNickeau                            $look,
230*37748cd8SNickeau                            $lookBusy,
231*37748cd8SNickeau                            $calledRuleStack,
232*37748cd8SNickeau                            $seeThruPreds,
233*37748cd8SNickeau                            $addEOF
234*37748cd8SNickeau                        );
235*37748cd8SNickeau                    }
236*37748cd8SNickeau                } finally {
237*37748cd8SNickeau                    if ($removed) {
238*37748cd8SNickeau                        $calledRuleStack->add($s->ruleIndex);
239*37748cd8SNickeau                    }
240*37748cd8SNickeau                }
241*37748cd8SNickeau
242*37748cd8SNickeau                return;
243*37748cd8SNickeau            }
244*37748cd8SNickeau        }
245*37748cd8SNickeau
246*37748cd8SNickeau        /** @var Transition $t */
247*37748cd8SNickeau        foreach ($s->getTransitions() as $t) {
248*37748cd8SNickeau            if ($t instanceof RuleTransition) {
249*37748cd8SNickeau                if ($calledRuleStack->contains($t->target->ruleIndex)) {
250*37748cd8SNickeau                    continue;
251*37748cd8SNickeau                }
252*37748cd8SNickeau
253*37748cd8SNickeau                $newContext = SingletonPredictionContext::create($context, $t->followState->stateNumber);
254*37748cd8SNickeau
255*37748cd8SNickeau                try {
256*37748cd8SNickeau                    $calledRuleStack->add($t->target->ruleIndex);
257*37748cd8SNickeau                    $this->lookRecursively(
258*37748cd8SNickeau                        $t->target,
259*37748cd8SNickeau                        $stopState,
260*37748cd8SNickeau                        $newContext,
261*37748cd8SNickeau                        $look,
262*37748cd8SNickeau                        $lookBusy,
263*37748cd8SNickeau                        $calledRuleStack,
264*37748cd8SNickeau                        $seeThruPreds,
265*37748cd8SNickeau                        $addEOF
266*37748cd8SNickeau                    );
267*37748cd8SNickeau                } finally {
268*37748cd8SNickeau                    $calledRuleStack->remove($t->target->ruleIndex);
269*37748cd8SNickeau                }
270*37748cd8SNickeau            } elseif ($t instanceof AbstractPredicateTransition) {
271*37748cd8SNickeau                if ($seeThruPreds) {
272*37748cd8SNickeau                    $this->lookRecursively(
273*37748cd8SNickeau                        $t->target,
274*37748cd8SNickeau                        $stopState,
275*37748cd8SNickeau                        $context,
276*37748cd8SNickeau                        $look,
277*37748cd8SNickeau                        $lookBusy,
278*37748cd8SNickeau                        $calledRuleStack,
279*37748cd8SNickeau                        $seeThruPreds,
280*37748cd8SNickeau                        $addEOF
281*37748cd8SNickeau                    );
282*37748cd8SNickeau                } else {
283*37748cd8SNickeau                    $look->addOne(self::HIT_PRED);
284*37748cd8SNickeau                }
285*37748cd8SNickeau            } elseif ($t->isEpsilon()) {
286*37748cd8SNickeau                $this->lookRecursively(
287*37748cd8SNickeau                    $t->target,
288*37748cd8SNickeau                    $stopState,
289*37748cd8SNickeau                    $context,
290*37748cd8SNickeau                    $look,
291*37748cd8SNickeau                    $lookBusy,
292*37748cd8SNickeau                    $calledRuleStack,
293*37748cd8SNickeau                    $seeThruPreds,
294*37748cd8SNickeau                    $addEOF
295*37748cd8SNickeau                );
296*37748cd8SNickeau            } elseif ($t instanceof WildcardTransition) {
297*37748cd8SNickeau                $look->addRange(Token::MIN_USER_TOKEN_TYPE, $this->atn->maxTokenType);
298*37748cd8SNickeau            } else {
299*37748cd8SNickeau                $set = $t->label();
300*37748cd8SNickeau
301*37748cd8SNickeau                if ($set !== null) {
302*37748cd8SNickeau                    if ($t instanceof NotSetTransition) {
303*37748cd8SNickeau                        $set = $set->complement(IntervalSet::fromRange(
304*37748cd8SNickeau                            Token::MIN_USER_TOKEN_TYPE,
305*37748cd8SNickeau                            $this->atn->maxTokenType
306*37748cd8SNickeau                        ));
307*37748cd8SNickeau                    }
308*37748cd8SNickeau
309*37748cd8SNickeau                    if ($set !== null) {
310*37748cd8SNickeau                        $look->addSet($set);
311*37748cd8SNickeau                    }
312*37748cd8SNickeau                }
313*37748cd8SNickeau            }
314*37748cd8SNickeau        }
315*37748cd8SNickeau    }
316*37748cd8SNickeau}
317