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