1<?php 2 3declare(strict_types=1); 4 5namespace Antlr\Antlr4\Runtime; 6 7use Antlr\Antlr4\Runtime\Atn\ATN; 8use Antlr\Antlr4\Runtime\Tree\ParseTree; 9use Antlr\Antlr4\Runtime\Tree\ParseTreeVisitor; 10use Antlr\Antlr4\Runtime\Tree\RuleNode; 11use Antlr\Antlr4\Runtime\Tree\Tree; 12use Antlr\Antlr4\Runtime\Tree\Trees; 13 14/** 15 * A rule context is a record of a single rule invocation. 16 * 17 * We form a stack of these context objects using the parent pointer. A parent 18 * pointer of null indicates that the current context is the bottom of the stack. 19 * The {@see ParserRuleContext} subclass as a children list so that we can turn 20 * this data structure into a tree. 21 * 22 * The root node always has a null pointer and invokingState of -1. 23 * 24 * Upon entry to parsing, the first invoked rule function creates a context object 25 * (a subclass specialized for that rule such as SContext) and makes it the root 26 * of a parse tree, recorded by field Parser->ctx. 27 * 28 * public final s(){ 29 * SContext _localctx = new SContext(_ctx, getState()); <-- create new node 30 * enterRule(_localctx, 0, RULE_s); <-- push it 31 * ... 32 * exitRule(); <-- pop back to _localctx 33 * return _localctx; 34 * } 35 * 36 * A subsequent rule invocation of r from the start rule s pushes a new 37 * context object for r whose parent points at s and use invoking state is 38 * the state with r emanating as edge label. 39 * 40 * The invokingState fields from a context object to the root together 41 * form a stack of rule indication states where the root (bottom of the stack) 42 * has a -1 sentinel value. If we invoke start symbol s then call r1, 43 * which calls r2, the would look like this: 44 * 45 * SContext[-1] <- root node (bottom of the stack) 46 * R1Context[p] <- p in rule s called r1 47 * R2Context[q] <- q in rule r1 called r2 48 * 49 * So the top of the stack, `_ctx`, represents a call to the current rule 50 * and it holds the return address from another rule that invoke to this rule. 51 * To invoke a rule, we must always have a current context. 52 * 53 * The parent contexts are useful for computing lookahead sets and getting 54 * error information. 55 * 56 * These objects are used during parsing and prediction. For the special case 57 * of parsers, we use the subclass {@see ParserRuleContext}. 58 */ 59class RuleContext implements RuleNode 60{ 61 /** 62 * The context that invoked this rule. 63 * 64 * @var RuleContext|null 65 */ 66 public $parentCtx; 67 68 /** 69 * What state invoked the rule associated with this context? 70 * The "return address" is the followState of invokingState. If parent 71 * is null, this should be -1 this context object represents the start rule. 72 * 73 * @var int 74 */ 75 public $invokingState = -1; 76 77 public function __construct(?RuleContext $parent, ?int $invokingState = null) 78 { 79 $this->parentCtx = $parent; 80 $this->invokingState = $invokingState ?? -1; 81 } 82 83 public static function emptyContext() : ParserRuleContext 84 { 85 static $empty; 86 87 return $empty ?? ($empty = new ParserRuleContext(null)); 88 } 89 90 public function depth() : int 91 { 92 $n = 0; 93 $p = $this; 94 95 while ($p !== null) { 96 $p = $p->parentCtx; 97 $n++; 98 } 99 100 return $n; 101 } 102 103 /** 104 * A context is empty if there is no invoking state; meaning nobody call 105 * current context. 106 */ 107 public function isEmpty() : bool 108 { 109 return $this->invokingState === -1; 110 } 111 112 public function getSourceInterval() : Interval 113 { 114 return Interval::invalid(); 115 } 116 117 public function getRuleContext() : RuleContext 118 { 119 return $this; 120 } 121 122 public function getPayload() : RuleContext 123 { 124 return $this; 125 } 126 127 /** 128 * Return the combined text of all child nodes. This method only considers 129 * tokens which have been added to the parse tree. 130 * 131 * Since tokens on hidden channels (e.g. whitespace or comments) are not 132 * added to the parse trees, they will not appear in the output 133 * of this method. 134 */ 135 public function getText() : string 136 { 137 $text = ''; 138 139 for ($i = 0, $count = $this->getChildCount(); $i < $count; $i++) { 140 $child = $this->getChild($i); 141 142 if ($child !== null) { 143 $text .= $child->getText(); 144 } 145 } 146 147 return $text; 148 } 149 150 public function getRuleIndex() : int 151 { 152 return -1; 153 } 154 155 /** 156 * For rule associated with this parse tree internal node, return the outer 157 * alternative number used to match the input. Default implementation 158 * does not compute nor store this alt num. Create a subclass of 159 * {@see ParserRuleContext} with backing field and set option 160 * `contextSuperClass` to set it. 161 */ 162 public function getAltNumber() : int 163 { 164 return ATN::INVALID_ALT_NUMBER; 165 } 166 167 /** 168 * Set the outer alternative number for this context node. Default 169 * implementation does nothing to avoid backing field overhead for trees 170 * that don't need it. Create a subclass of {@see ParserRuleContext} with backing 171 * field and set option `contextSuperClass`. 172 */ 173 public function setAltNumber(int $altNumber) : void 174 { 175 } 176 177 /** 178 * @return RuleContext|null 179 */ 180 public function getParent() : ?Tree 181 { 182 return $this->parentCtx; 183 } 184 185 public function setParent(?RuleContext $ctx) : void 186 { 187 $this->parentCtx = $ctx; 188 } 189 190 /** 191 * @return ParseTree|null 192 */ 193 public function getChild(int $i, ?string $type = null) : ?Tree 194 { 195 return null; 196 } 197 198 public function getChildCount() : int 199 { 200 return 0; 201 } 202 203 public function accept(ParseTreeVisitor $visitor) 204 { 205 return $visitor->visitChildren($this); 206 } 207 208 /** 209 * Print out a whole tree, not just a node, in LISP format 210 * (root child1 .. childN). Print just a node if this is a leaf. 211 * 212 * @param array<string>|null $ruleNames 213 */ 214 public function toStringTree(?array $ruleNames = null) : string 215 { 216 return Trees::toStringTree($this, $ruleNames); 217 } 218 219 public function __toString() : string 220 { 221 return $this->toString(); 222 } 223 224 /** 225 * @param array<string>|null $ruleNames 226 */ 227 public function toString(?array $ruleNames = null, ?RuleContext $stop = null) : string 228 { 229 $p = $this; 230 $string = '['; 231 232 while ($p !== null && $p !== $stop) { 233 if ($ruleNames === null) { 234 if (!$p->isEmpty()) { 235 $string .= $p->invokingState; 236 } 237 } else { 238 $ri = $p->getRuleIndex(); 239 $ruleName = $ri >= 0 && $ri < \count($ruleNames) ? $ruleNames[$ri] : (string) $ri; 240 $string .= $ruleName; 241 } 242 243 if ($p->parentCtx !== null && ($ruleNames !== null || !$p->parentCtx->isEmpty())) { 244 $string .= ' '; 245 } 246 247 $p = $p->parentCtx; 248 } 249 250 return $string . ']'; 251 } 252} 253