1<?php 2 3declare(strict_types=1); 4 5namespace Antlr\Antlr4\Runtime\Atn; 6 7use Antlr\Antlr4\Runtime\Atn\SemanticContexts\SemanticContext; 8use Antlr\Antlr4\Runtime\Atn\States\ATNState; 9use Antlr\Antlr4\Runtime\Comparison\Equality; 10use Antlr\Antlr4\Runtime\Comparison\Hashable; 11use Antlr\Antlr4\Runtime\Comparison\Hasher; 12use Antlr\Antlr4\Runtime\PredictionContexts\PredictionContext; 13 14/** 15 * A tuple: (ATN state, predicted alt, syntactic, semantic context). 16 * The syntactic context is a graph-structured stack node whose path(s) 17 * to the root is the rule invocation(s) chain used to arrive at the state. 18 * The semantic context is the tree of semantic predicates encountered 19 * before reaching an ATN state. 20 */ 21class ATNConfig implements Hashable 22{ 23 /** 24 * This field stores the bit mask for implementing the 25 * {@see ATNConfig::isPrecedenceFilterSuppressed()} property as a bit within 26 * the existing {@see ATNConfig::$reachesIntoOuterContext} field. 27 */ 28 private const SUPPRESS_PRECEDENCE_FILTER = 0x40000000; 29 30 /** 31 * The ATN state associated with this configuration. 32 * 33 * @var ATNState 34 */ 35 public $state; 36 37 /** 38 * What alt (or lexer rule) is predicted by this configuration. 39 * 40 * @var int 41 */ 42 public $alt; 43 44 /** 45 * The stack of invoking states leading to the rule/states associated 46 * with this config. We track only those contexts pushed during 47 * execution of the ATN simulator. 48 * 49 * @var PredictionContext|null 50 */ 51 public $context; 52 53 /** 54 * We cannot execute predicates dependent upon local context unless 55 * we know for sure we are in the correct context. Because there is 56 * no way to do this efficiently, we simply cannot evaluate 57 * dependent predicates unless we are in the rule that initially 58 * invokes the ATN simulator. 59 * 60 * closure() tracks the depth of how far we dip into the outer context: 61 * `depth > 0`. Note that it may not be totally accurate depth since I 62 * don't ever decrement. TODO: make it a boolean then 63 * 64 * For memory efficiency, {@see ATNConfig::isPrecedenceFilterSuppressed()} 65 * is also backed by this field. Since the field is publicly accessible, the 66 * highest bit which would not cause the value to become negative is used to 67 * store this field. This choice minimizes the risk that code which only 68 * compares this value to 0 would be affected by the new purpose of the 69 * flag. It also ensures the performance of the existing {@see ATNConfig} 70 * constructors as well as certain operations like 71 * {@see ATNConfigSet::add()} method are completely unaffected by the change. 72 * 73 * @var int 74 */ 75 public $reachesIntoOuterContext; 76 77 /** @var SemanticContext */ 78 public $semanticContext; 79 80 public function __construct( 81 ?self $oldConfig, 82 ?ATNState $state, 83 ?PredictionContext $context = null, 84 ?SemanticContext $semanticContext = null, 85 ?int $alt = null 86 ) { 87 if ($oldConfig === null) { 88 if ($state === null) { 89 throw new \RuntimeException('ATN State cannot be null.'); 90 } 91 92 $this->state = $state; 93 $this->alt = $alt ?? 0; 94 $this->context = $context; 95 $this->semanticContext = $semanticContext ?? SemanticContext::none(); 96 } else { 97 $this->state = $state ?? $oldConfig->state; 98 $this->alt = $alt ?? $oldConfig->alt; 99 $this->context = $context ?? $oldConfig->context; 100 $this->semanticContext = $semanticContext ?? $oldConfig->semanticContext; 101 $this->reachesIntoOuterContext = $oldConfig->reachesIntoOuterContext; 102 } 103 } 104 105 /** 106 * This method gets the value of the {@see ATNConfig::$reachesIntoOuterContext} 107 * field as it existed prior to the introduction of the 108 * {@see ATNConfig::isPrecedenceFilterSuppressed()} method. 109 */ 110 public function getOuterContextDepth() : int 111 { 112 return $this->reachesIntoOuterContext & ~self::SUPPRESS_PRECEDENCE_FILTER; 113 } 114 115 public function isPrecedenceFilterSuppressed() : bool 116 { 117 return ($this->reachesIntoOuterContext & self::SUPPRESS_PRECEDENCE_FILTER) !== 0; 118 } 119 120 public function setPrecedenceFilterSuppressed(bool $value) : void 121 { 122 if ($value) { 123 $this->reachesIntoOuterContext |= self::SUPPRESS_PRECEDENCE_FILTER; 124 } else { 125 $this->reachesIntoOuterContext &= ~self::SUPPRESS_PRECEDENCE_FILTER; 126 } 127 } 128 129 /** 130 * An ATN configuration is equal to another if both have the same state, they 131 * predict the same alternative, and syntactic/semantic contexts are the same. 132 */ 133 public function equals(object $other) : bool 134 { 135 if ($this === $other) { 136 return true; 137 } 138 139 return $other instanceof self 140 && $this->alt === $other->alt 141 && $this->isPrecedenceFilterSuppressed() === $other->isPrecedenceFilterSuppressed() 142 && $this->semanticContext->equals($other->semanticContext) 143 && Equality::equals($this->state, $other->state) 144 && Equality::equals($this->context, $other->context); 145 } 146 147 public function hashCode() : int 148 { 149 return Hasher::hash( 150 $this->state->stateNumber, 151 $this->alt, 152 $this->context, 153 $this->semanticContext 154 ); 155 } 156 157 public function toString(bool $showAlt) : string 158 { 159 $buf = '(' . $this->state; 160 161 if ($showAlt) { 162 $buf .= ',' . $this->alt; 163 } 164 165 if ($this->context !== null) { 166 $buf .= ',[' . $this->context . ']'; 167 } 168 169 if ($this->semanticContext->equals(SemanticContext::none())) { 170 $buf .= ',' . $this->semanticContext; 171 } 172 173 if ($this->getOuterContextDepth() > 0) { 174 $buf .= ',up=' . $this->getOuterContextDepth(); 175 } 176 177 $buf .= ')'; 178 179 return $buf; 180 } 181 182 public function __toString() : string 183 { 184 return \sprintf( 185 '(%s,%d%s%s%s)', 186 $this->state, 187 $this->alt, 188 $this->context !== null ? ',[' . $this->context . ']' : '', 189 $this->semanticContext !== null && $this->semanticContext->equals(SemanticContext::none()) ? 190 ',' . $this->semanticContext : 191 '', 192 $this->reachesIntoOuterContext > 0 ? ',up=' . $this->reachesIntoOuterContext : '' 193 ); 194 } 195} 196