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