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