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