1<?php
2
3declare(strict_types=1);
4
5namespace Antlr\Antlr4\Runtime\Atn\SemanticContexts;
6
7use Antlr\Antlr4\Runtime\Comparison\Hashable;
8use Antlr\Antlr4\Runtime\Recognizer;
9use Antlr\Antlr4\Runtime\RuleContext;
10use Antlr\Antlr4\Runtime\Utils\Set;
11
12/**
13 * A tree structure used to record the semantic context in which
14 * an ATN configuration is valid. It's either a single predicate,
15 * a conjunction `p1&&p2`, or a sum of products `p1 || p2`.
16 *
17 * I have scoped the {@see AndOperator}, {@see OrOperator}, and
18 * {@see PrecedencePredicate} subclasses of {@see SemanticContext} within
19 * the scope of this outer class.
20 */
21abstract class SemanticContext implements Hashable
22{
23    /**
24     * The default {@see SemanticContext}, which is semantically equivalent to
25     * a predicate of the form `{true}?`.
26     */
27    public static function none() : Predicate
28    {
29        static $none;
30
31        return $none = $none ?? new Predicate();
32    }
33
34    public static function andContext(?self $a, ?self $b) : ?self
35    {
36        if ($a === null || $a === self::none()) {
37            return $b;
38        }
39
40        if ($b === null || $b === self::none()) {
41            return $a;
42        }
43
44        $result = new AndOperator($a, $b);
45
46        return \count($result->operands) === 1 ? $result->operands[0] : $result;
47    }
48
49    public static function orContext(?self $a, ?self $b) : ?self
50    {
51        if ($a === null) {
52            return $b;
53        }
54
55        if ($b === null) {
56            return $a;
57        }
58
59        if ($a === self::none() || $b === self::none()) {
60            return self::none();
61        }
62
63        $result = new OrOperator($a, $b);
64
65        return \count($result->operand) === 1 ? $result->operand[0] : $result;
66    }
67
68    /**
69     * For context independent predicates, we evaluate them without a local
70     * context (i.e., null context). That way, we can evaluate them without
71     * having to create proper rule-specific context during prediction (as
72     * opposed to the parser, which creates them naturally). In a practical
73     * sense, this avoids a cast exception from RuleContext to myruleContext.
74     *
75     * For context dependent predicates, we must pass in a local context so that
76     * references such as $arg evaluate properly as _localctx.arg. We only
77     * capture context dependent predicates in the context in which we begin
78     * prediction, so we passed in the outer context here in case of context
79     * dependent predicate evaluation.
80     */
81    abstract public function eval(Recognizer $parser, RuleContext $parserCallStack);
82
83    /**
84     * Evaluate the precedence predicates for the context and reduce the result.
85     *
86     * @param Recognizer $parser The parser instance.
87     *
88     * @return self|null The simplified semantic context after precedence predicates
89     *                   are evaluated, which will be one of the following values.
90     *
91     *                   - {@see self::NONE()}: if the predicate simplifies to
92     *                      `true` after precedence predicates are evaluated.
93     *                   - `null`: if the predicate simplifies to `false` after
94     *                      precedence predicates are evaluated.
95     *                   - `this`: if the semantic context is not changed
96     *                      as a result of precedence predicate evaluation.
97     *                   - A non-`null` {@see SemanticContext}: if the new simplified
98     *                      semantic context after precedence predicates are evaluated.
99     */
100    public function evalPrecedence(Recognizer $parser, RuleContext $parserCallStack) : ?self
101    {
102        return $this;
103    }
104
105    /**
106     * @return array<PrecedencePredicate>
107     */
108    public static function filterPrecedencePredicates(Set $set) : array
109    {
110        $result = [];
111        foreach ($set->getValues() as $context) {
112            if ($context instanceof PrecedencePredicate) {
113                $result[] = $context;
114            }
115        }
116
117        return $result;
118    }
119
120    abstract public function __toString() : string;
121}
122