1<?php 2 3declare(strict_types=1); 4 5namespace Antlr\Antlr4\Runtime; 6 7use Antlr\Antlr4\Runtime\Comparison\Equatable; 8 9/** 10 * An immutable inclusive interval start..stop (both start and stop included). 11 */ 12final class Interval implements Equatable 13{ 14 /** @var int */ 15 public $start; 16 17 /** @var int */ 18 public $stop; 19 20 public function __construct(int $start, int $stop) 21 { 22 $this->start = $start; 23 $this->stop = $stop; 24 } 25 26 public static function invalid() : self 27 { 28 static $invalid; 29 30 return $invalid ?? $invalid = new Interval(-1, -2); 31 } 32 33 public function contains(int $item) : bool 34 { 35 return $item >= $this->start && $item <= $this->stop; 36 } 37 38 public function getLength() : int 39 { 40 return $this->stop - $this->start + 1; 41 } 42 43 public function equals(object $other) : bool 44 { 45 if ($this === $other) { 46 return true; 47 } 48 49 return $other instanceof self 50 && $this->start === $other->start 51 && $this->stop === $other->stop; 52 } 53 54 /** 55 * Does this start completely before other? Disjoint. 56 */ 57 public function startsBeforeDisjoint(Interval $other) : bool 58 { 59 return $this->start < $other->start && $this->stop < $other->start; 60 } 61 62 /** 63 * Does this start at or before other? Nondisjoint. 64 */ 65 public function startsBeforeNonDisjoint(Interval $other) : bool 66 { 67 return $this->start <= $other->start && $this->stop >= $other->start; 68 } 69 70 /** 71 * Does this.a start after other.b? May or may not be disjoint. 72 */ 73 public function startsAfter(Interval $other) : bool 74 { 75 return $this->start > $other->start; 76 } 77 78 /** 79 * Does this start completely after other? Disjoint. 80 */ 81 public function startsAfterDisjoint(Interval $other) : bool 82 { 83 return $this->start > $other->stop; 84 } 85 86 /** 87 * Does this start after other? NonDisjoint 88 */ 89 public function startsAfterNonDisjoint(Interval $other) : bool 90 { 91 // this.b >= other.b implied 92 return $this->start > $other->start && $this->start <= $other->stop; 93 } 94 95 /** 96 * Are both ranges disjoint? I.e., no overlap? 97 */ 98 public function disjoint(Interval $other) : bool 99 { 100 return $this->startsBeforeDisjoint($other) || $this->startsAfterDisjoint($other); 101 } 102 103 /** 104 * Are two intervals adjacent such as 0..41 and 42..42? 105 */ 106 public function adjacent(Interval $other) : bool 107 { 108 return $this->start === $other->stop + 1 || $this->stop === $other->start - 1; 109 } 110 111 /** 112 * Return the interval computed from combining this and other 113 */ 114 public function union(Interval $other) : self 115 { 116 return new self(\min($this->start, $other->start), \max($this->stop, $other->stop)); 117 } 118 119 /** 120 * Return the interval in common between this and o 121 */ 122 public function intersection(Interval $other) : self 123 { 124 return new self(\max($this->start, $other->start), \min($this->stop, $other->stop)); 125 } 126 127 public function __toString() : string 128 { 129 if ($this->start === $this->stop) { 130 return (string) $this->start; 131 } 132 133 return $this->start . '..' . $this->stop; 134 } 135} 136