1<?php
2
3declare(strict_types=1);
4
5namespace Antlr\Antlr4\Runtime;
6
7use Antlr\Antlr4\Runtime\Error\Exceptions\RecognitionException;
8use Antlr\Antlr4\Runtime\Tree\ErrorNode;
9use Antlr\Antlr4\Runtime\Tree\ParseTree;
10use Antlr\Antlr4\Runtime\Tree\ParseTreeListener;
11use Antlr\Antlr4\Runtime\Tree\TerminalNode;
12use Antlr\Antlr4\Runtime\Tree\Tree;
13
14/**
15 * A rule invocation record for parsing.
16 *
17 * Contains all of the information about the current rule not stored
18 * in the RuleContext. It handles parse tree children list, any ATN state
19 * tracing, and the default values available for rule invocations: start, stop,
20 * rule index, current alt number.
21 *
22 * Subclasses made for each rule and grammar track the parameters, return values,
23 * locals, and labels specific to that rule. These are the objects
24 * that are returned from rules.
25 *
26 * Note text is not an actual field of a rule return value; it is computed
27 * from start and stop using the input stream's toString() method. I could
28 * add a ctor to this so that we can pass in and store the input stream,
29 * but I'm not sure we want to do that. It would seem to be undefined to get
30 * the `text` property anyway if the rule matches tokens from multiple
31 * input streams.
32 *
33 * I do not use getters for fields of objects that are used simply to group
34 * values such as this aggregate. The getters/setters are there to satisfy
35 * the superclass interface.
36 */
37class ParserRuleContext extends RuleContext
38{
39    /**
40     * If we are debugging or building a parse tree for a visitor,
41     * we need to track all of the tokens and rule invocations associated
42     * with this rule's context. This is empty for parsing w/o tree constr.
43     * operation because we don't the need to track the details about
44     * how we parse this rule.
45     *
46     * @var array<ParseTree>|null
47     */
48    public $children;
49
50    /** @var Token|null */
51    public $start;
52
53    /** @var Token|null */
54    public $stop;
55
56    /**
57     * The exception that forced this rule to return. If the rule successfully
58     * completed, this is `null`.
59     *
60     * @var RecognitionException|null
61     */
62    public $exception;
63
64    /**
65     * COPY a context (I'm deliberately not using copy constructor) to avoid
66     * confusion with creating node with parent. Does not copy children
67     * (except error leaves).
68     *
69     * This is used in the generated parser code to flip a generic XContext
70     * node for rule X to a YContext for alt label Y. In that sense, it is
71     * not really a generic copy function.
72     *
73     * If we do an error sync() at start of a rule, we might add error nodes
74     * to the generic XContext so this function must copy those nodes to
75     * the YContext as well else they are lost!
76     */
77    public function copyFrom(ParserRuleContext $ctx) : void
78    {
79        // from RuleContext
80        $this->parentCtx = $ctx->parentCtx;
81        $this->invokingState = $ctx->invokingState;
82        $this->children = null;
83        $this->start = $ctx->start;
84        $this->stop = $ctx->stop;
85
86        // copy any error nodes to alt label node
87        if ($ctx->children) {
88            $this->children = [];
89
90            // reset parent pointer for any error nodes
91            foreach ($ctx->children as $child) {
92                if ($child instanceof ErrorNode) {
93                    $this->addErrorNode($child);
94                }
95            }
96        }
97    }
98
99    public function enterRule(ParseTreeListener $listener) : void
100    {
101    }
102
103    public function exitRule(ParseTreeListener $listener) : void
104    {
105    }
106
107    public function addTerminalNode(TerminalNode $t) : ParseTree
108    {
109        $t->setParent($this);
110
111        return $this->addChild($t);
112    }
113
114    public function addErrorNode(ErrorNode $errorNode) : ParseTree
115    {
116        $errorNode->setParent($this);
117
118        return $this->addChild($errorNode);
119    }
120
121    /**
122     * Add a parse tree node to this as a child. Works for
123     * internal and leaf nodes. Does not set parent link;
124     * other add methods must do that. Other addChild methods
125     * call this.
126     *
127     * We cannot set the parent pointer of the incoming node
128     * because the existing interfaces do not have a `setParent()`
129     * method and I don't want to break backward compatibility for this.
130     */
131    public function addChild(ParseTree $child) : ParseTree
132    {
133        if ($this->children === null) {
134            $this->children = [];
135        }
136
137        $this->children[] = $child;
138
139        return $child;
140    }
141
142    /**
143     * Used by enterOuterAlt to toss out a RuleContext previously added as
144     * we entered a rule. If we have # label, we will need to remove
145     * generic `ruleContext` object.
146     */
147    public function removeLastChild() : void
148    {
149        if ($this->children !== null) {
150            \array_pop($this->children);
151        }
152    }
153
154    /**
155     * @return RuleContext|null
156     */
157    public function getParent() : ?Tree
158    {
159        return $this->parentCtx;
160    }
161
162    /**
163     * @return ParseTree|null
164     */
165    public function getChild(int $i, ?string $type = null) : ?Tree
166    {
167        if ($this->children === null || $i < 0 || $i >= \count($this->children)) {
168            return null;
169        }
170
171        if ($type === null) {
172            return $this->children[$i];
173        }
174
175        foreach ($this->children as $child) {
176            if ($child instanceof $type) {
177                if ($i === 0) {
178                    return $child;
179                }
180
181                $i--;
182            }
183        }
184
185        return null;
186    }
187
188    public function getToken(int $ttype, int $i) : ?TerminalNode
189    {
190        if ($this->children === null || $i < 0 || $i >= \count($this->children)) {
191            return null;
192        }
193
194        foreach ($this->children as $child) {
195            if ($child instanceof TerminalNode && $child->getSymbol()->getType() === $ttype) {
196                if ($i === 0) {
197                    return $child;
198                }
199
200                $i--;
201            }
202        }
203
204        return null;
205    }
206
207    /**
208     * @return array<TerminalNode>
209     */
210    public function getTokens(int $ttype) : array
211    {
212        if ($this->children === null) {
213            return [];
214        }
215
216        $tokens = [];
217        foreach ($this->children as $child) {
218            if ($child instanceof TerminalNode && $child->getSymbol()->getType() === $ttype) {
219                $tokens[] = $child;
220            }
221        }
222
223        return $tokens;
224    }
225
226    public function getTypedRuleContext(string $ctxType, int $i) : ?ParseTree
227    {
228        return $this->getChild($i, $ctxType);
229    }
230
231    /**
232     * @return array<ParseTree>
233     */
234    public function getTypedRuleContexts(string $ctxType) : array
235    {
236        if ($this->children=== null) {
237            return [];
238        }
239
240        $contexts = [];
241        foreach ($this->children as $child) {
242            if ($child instanceof $ctxType) {
243                $contexts[] = $child;
244            }
245        }
246
247        return $contexts;
248    }
249
250    public function getChildCount() : int
251    {
252        return $this->children !== null ? \count($this->children) : 0;
253    }
254
255    public function getSourceInterval() : Interval
256    {
257        if ($this->start === null || $this->stop === null) {
258            return Interval::invalid();
259        }
260
261        return new Interval($this->start->getTokenIndex(), $this->stop->getTokenIndex());
262    }
263
264    /**
265     * Get the initial token in this context.
266     *
267     * Note that the range from start to stop is inclusive, so for rules that
268     * do not consume anything (for example, zero length or error productions)
269     * this token may exceed stop.
270     */
271    public function getStart() : ?Token
272    {
273        return $this->start;
274    }
275
276    /**
277     * Get the final token in this context.
278     *
279     * Note that the range from start to stop is inclusive, so for rules that
280     * do not consume anything (for example, zero length or error productions)
281     * this token may precede start.
282     */
283    public function getStop() : ?Token
284    {
285        return $this->stop;
286    }
287}
288