1<?php
2
3declare(strict_types=1);
4
5namespace Antlr\Antlr4\Runtime\Atn\SemanticContexts;
6
7use Antlr\Antlr4\Runtime\Comparison\Equality;
8use Antlr\Antlr4\Runtime\Comparison\Hasher;
9use Antlr\Antlr4\Runtime\Recognizer;
10use Antlr\Antlr4\Runtime\RuleContext;
11use Antlr\Antlr4\Runtime\Utils\Set;
12
13/**
14 * A semantic context which is true whenever at least one of the contained
15 * contexts is true.
16 */
17final class OrOperator extends Operator
18{
19    /** @var array<SemanticContext> */
20    public $operand;
21
22    public function __construct(SemanticContext $a, SemanticContext $b)
23    {
24        $operands = new Set();
25
26        if ($a instanceof self) {
27            foreach ($a->operand as $o) {
28                $operands->add($o);
29            }
30        } else {
31            $operands->add($a);
32        }
33
34        if ($b instanceof self) {
35            foreach ($b->operand as $o) {
36                $operands->add($o);
37            }
38        } else {
39            $operands->add($b);
40        }
41
42        $precedencePredicates = self::filterPrecedencePredicates($operands);
43
44        if (\count($precedencePredicates) !== 0) {
45            // interested in the transition with the highest precedence
46            \usort($precedencePredicates, static function (PrecedencePredicate $a, PrecedencePredicate $b) {
47                return $a->precedence - $b->precedence;
48            });
49            $reduced = $precedencePredicates[\count($precedencePredicates) - 1];
50            $operands->add($reduced);
51        }
52
53        $this->operand = $operands->getValues();
54    }
55
56    /**
57     * @return array<SemanticContext>
58     */
59    public function getOperands() : array
60    {
61        return $this->operand;
62    }
63
64    /**
65     * {@inheritdoc}
66     *
67     * The evaluation of predicates by this context is short-circuiting, but
68     * unordered.
69     */
70    public function eval(Recognizer $parser, RuleContext $parserCallStack) : bool
71    {
72        foreach ($this->operand as $operand) {
73            if ($operand->eval($parser, $parserCallStack)) {
74                return true;
75            }
76        }
77
78        return false;
79    }
80
81    public function evalPrecedence(Recognizer $parser, RuleContext $parserCallStack) : ?SemanticContext
82    {
83        $differs = false;
84
85        $operands = [];
86        foreach ($this->operand as $context) {
87            $evaluated = $context->evalPrecedence($parser, $parserCallStack);
88            $differs |= ($evaluated !== $context);
89
90            if ($evaluated === SemanticContext::none()) {
91                // The OR context is true if any element is true
92                return SemanticContext::none();
93            }
94
95            if ($evaluated !== null) {
96                // Reduce the result by skipping false elements
97                $operands[] = $evaluated;
98            }
99        }
100
101        if (!$differs) {
102            return $this;
103        }
104
105        // all elements were false, so the OR context is false
106        if (\count($operands) === 0) {
107            return null;
108        }
109
110        $result = null;
111        foreach ($operands as $operand) {
112            $result = $result === null ? $operand : SemanticContext::orContext($result, $operand);
113        }
114
115        return $result;
116    }
117
118    public function equals(object $other) : bool
119    {
120        if ($this === $other) {
121            return true;
122        }
123
124        if (!$other instanceof self) {
125            return false;
126        }
127
128        return Equality::equals($this->operand, $other->operand);
129    }
130
131    public function hashCode() : int
132    {
133        return Hasher::hash(37, $this->operand);
134    }
135
136
137    public function __toString() : string
138    {
139        $s = '';
140        foreach ($this->operand as $o) {
141            $s .= '|| ' . $o;
142        }
143
144        return \strlen($s) > 3 ? (string) \substr($s, 3) : $s;
145    }
146}
147