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 none of the contained contexts
15 * is false.
16 */
17final class AndOperator extends Operator
18{
19    /** @var array<SemanticContext> */
20    public $operands;
21
22    public function __construct(SemanticContext $a, SemanticContext $b)
23    {
24        /** @var Set<SemanticContext> $operands */
25        $operands = new Set();
26
27        if ($a instanceof self) {
28            $operands->addAll($a->operands);
29        } else {
30            $operands->add($a);
31        }
32
33        if ($b instanceof self) {
34            $operands->addAll($b->operands);
35        } else {
36            $operands->add($b);
37        }
38
39        /** @var array<PrecedencePredicate> $precedencePredicates */
40        $precedencePredicates = self::filterPrecedencePredicates($operands);
41
42        if (\count($precedencePredicates) !== 0) {
43            // interested in the transition with the lowest precedence
44
45            /** @var PrecedencePredicate $reduced */
46            $reduced = self::minPredicate($precedencePredicates);
47
48            $operands->add($reduced);
49        }
50
51        $this->operands = $operands->getValues();
52    }
53
54    /**
55     * @return array<SemanticContext>
56     */
57    public function getOperands() : array
58    {
59        return $this->operands;
60    }
61
62    /**
63     * {@inheritdoc}
64     *
65     * The evaluation of predicates by this context is short-circuiting, but
66     * unordered.
67     */
68    public function eval(Recognizer $parser, RuleContext $parserCallStack) : bool
69    {
70        foreach ($this->operands as $operand) {
71            if (!$operand->eval($parser, $parserCallStack)) {
72                return false;
73            }
74        }
75        return true;
76    }
77
78    public function evalPrecedence(Recognizer $parser, RuleContext $parserCallStack) : ?SemanticContext
79    {
80        $differs = false;
81
82        $operands = [];
83        foreach ($this->operands as $iValue) {
84            $context = $iValue;
85            $evaluated = $context->evalPrecedence($parser, $parserCallStack);
86            $differs |= $evaluated !== $context;
87
88            // The AND context is false if any element is false
89            if ($evaluated === null) {
90                return null;
91            }
92
93            if ($evaluated !== SemanticContext::none()) {
94                // Reduce the result by skipping true elements
95                $operands[] = $evaluated;
96            }
97        }
98
99        if (!$differs) {
100            return $this;
101        }
102
103        // all elements were true, so the AND context is true
104        if (\count($operands) === 0) {
105            return SemanticContext::none();
106        }
107
108        $result = null;
109        foreach ($operands as $operand) {
110            $result = $result === null ? $operand : self::andContext($result, $operand);
111        }
112
113        return $result;
114    }
115
116    public function equals(object $other) : bool
117    {
118        if ($this === $other) {
119            return true;
120        }
121
122        if (!$other instanceof self) {
123            return false;
124        }
125
126        return Equality::equals($this->operands, $other->operands);
127    }
128
129    public function hashCode() : int
130    {
131        return Hasher::hash(41, $this->operands);
132    }
133
134    public function __toString() : string
135    {
136        $s = '';
137        foreach ($this->operands as $o) {
138            $s .= '&& ' . $o;
139        }
140
141        return \strlen($s) > 3 ? (string) \substr($s, 3) : $s;
142    }
143
144    /**
145     * @param array<PrecedencePredicate> $predicates
146     */
147    private static function minPredicate(array $predicates) : object
148    {
149        $iterator = new \ArrayIterator($predicates);
150
151        $candidate = $iterator->current();
152
153        $iterator->next();
154
155        while ($iterator->valid()) {
156            $next = $iterator->current();
157
158            $iterator->next();
159
160            if ($next->compareTo($candidate) < 0) {
161                $candidate = $next;
162            }
163        }
164
165        return $candidate;
166    }
167}
168