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 at least one of the contained 15 * contexts is true. 16 */ 17final class OrOperator extends Operator 18{ 19 /** @var array<SemanticContext> */ 20 public $operand; 21 22 public function __construct(SemanticContext $a, SemanticContext $b) 23 { 24 $operands = new Set(); 25 26 if ($a instanceof self) { 27 foreach ($a->operand as $o) { 28 $operands->add($o); 29 } 30 } else { 31 $operands->add($a); 32 } 33 34 if ($b instanceof self) { 35 foreach ($b->operand as $o) { 36 $operands->add($o); 37 } 38 } else { 39 $operands->add($b); 40 } 41 42 $precedencePredicates = self::filterPrecedencePredicates($operands); 43 44 if (\count($precedencePredicates) !== 0) { 45 // interested in the transition with the highest precedence 46 \usort($precedencePredicates, static function (PrecedencePredicate $a, PrecedencePredicate $b) { 47 return $a->precedence - $b->precedence; 48 }); 49 $reduced = $precedencePredicates[\count($precedencePredicates) - 1]; 50 $operands->add($reduced); 51 } 52 53 $this->operand = $operands->getValues(); 54 } 55 56 /** 57 * @return array<SemanticContext> 58 */ 59 public function getOperands() : array 60 { 61 return $this->operand; 62 } 63 64 /** 65 * {@inheritdoc} 66 * 67 * The evaluation of predicates by this context is short-circuiting, but 68 * unordered. 69 */ 70 public function eval(Recognizer $parser, RuleContext $parserCallStack) : bool 71 { 72 foreach ($this->operand as $operand) { 73 if ($operand->eval($parser, $parserCallStack)) { 74 return true; 75 } 76 } 77 78 return false; 79 } 80 81 public function evalPrecedence(Recognizer $parser, RuleContext $parserCallStack) : ?SemanticContext 82 { 83 $differs = false; 84 85 $operands = []; 86 foreach ($this->operand as $context) { 87 $evaluated = $context->evalPrecedence($parser, $parserCallStack); 88 $differs |= ($evaluated !== $context); 89 90 if ($evaluated === SemanticContext::none()) { 91 // The OR context is true if any element is true 92 return SemanticContext::none(); 93 } 94 95 if ($evaluated !== null) { 96 // Reduce the result by skipping false elements 97 $operands[] = $evaluated; 98 } 99 } 100 101 if (!$differs) { 102 return $this; 103 } 104 105 // all elements were false, so the OR context is false 106 if (\count($operands) === 0) { 107 return null; 108 } 109 110 $result = null; 111 foreach ($operands as $operand) { 112 $result = $result === null ? $operand : SemanticContext::orContext($result, $operand); 113 } 114 115 return $result; 116 } 117 118 public function equals(object $other) : bool 119 { 120 if ($this === $other) { 121 return true; 122 } 123 124 if (!$other instanceof self) { 125 return false; 126 } 127 128 return Equality::equals($this->operand, $other->operand); 129 } 130 131 public function hashCode() : int 132 { 133 return Hasher::hash(37, $this->operand); 134 } 135 136 137 public function __toString() : string 138 { 139 $s = ''; 140 foreach ($this->operand as $o) { 141 $s .= '|| ' . $o; 142 } 143 144 return \strlen($s) > 3 ? (string) \substr($s, 3) : $s; 145 } 146} 147