*/ public $operands; public function __construct(SemanticContext $a, SemanticContext $b) { /** @var Set $operands */ $operands = new Set(); if ($a instanceof self) { $operands->addAll($a->operands); } else { $operands->add($a); } if ($b instanceof self) { $operands->addAll($b->operands); } else { $operands->add($b); } /** @var array $precedencePredicates */ $precedencePredicates = self::filterPrecedencePredicates($operands); if (\count($precedencePredicates) !== 0) { // interested in the transition with the lowest precedence /** @var PrecedencePredicate $reduced */ $reduced = self::minPredicate($precedencePredicates); $operands->add($reduced); } $this->operands = $operands->getValues(); } /** * @return array */ public function getOperands() : array { return $this->operands; } /** * {@inheritdoc} * * The evaluation of predicates by this context is short-circuiting, but * unordered. */ public function eval(Recognizer $parser, RuleContext $parserCallStack) : bool { foreach ($this->operands as $operand) { if (!$operand->eval($parser, $parserCallStack)) { return false; } } return true; } public function evalPrecedence(Recognizer $parser, RuleContext $parserCallStack) : ?SemanticContext { $differs = false; $operands = []; foreach ($this->operands as $iValue) { $context = $iValue; $evaluated = $context->evalPrecedence($parser, $parserCallStack); $differs |= $evaluated !== $context; // The AND context is false if any element is false if ($evaluated === null) { return null; } if ($evaluated !== SemanticContext::none()) { // Reduce the result by skipping true elements $operands[] = $evaluated; } } if (!$differs) { return $this; } // all elements were true, so the AND context is true if (\count($operands) === 0) { return SemanticContext::none(); } $result = null; foreach ($operands as $operand) { $result = $result === null ? $operand : self::andContext($result, $operand); } return $result; } public function equals(object $other) : bool { if ($this === $other) { return true; } if (!$other instanceof self) { return false; } return Equality::equals($this->operands, $other->operands); } public function hashCode() : int { return Hasher::hash(41, $this->operands); } public function __toString() : string { $s = ''; foreach ($this->operands as $o) { $s .= '&& ' . $o; } return \strlen($s) > 3 ? (string) \substr($s, 3) : $s; } /** * @param array $predicates */ private static function minPredicate(array $predicates) : object { $iterator = new \ArrayIterator($predicates); $candidate = $iterator->current(); $iterator->next(); while ($iterator->valid()) { $next = $iterator->current(); $iterator->next(); if ($next->compareTo($candidate) < 0) { $candidate = $next; } } return $candidate; } }