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