AndOperator::getOperands()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 3
ccs 0
cts 2
cp 0
crap 2
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Antlr\Antlr4\Runtime\Atn\SemanticContexts;
6
7
use Antlr\Antlr4\Runtime\Comparison\Equality;
8
use Antlr\Antlr4\Runtime\Comparison\Hasher;
9
use Antlr\Antlr4\Runtime\Recognizer;
10
use Antlr\Antlr4\Runtime\RuleContext;
11
use Antlr\Antlr4\Runtime\Utils\Set;
12
13
/**
14
 * A semantic context which is true whenever none of the contained contexts
15
 * is false.
16
 */
17
final 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) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $differs of type false|integer is loosely compared to false; this is ambiguous if the integer can be 0. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
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