vanderlee /
Comprehend
| 1 | <?php |
||
| 2 | |||
| 3 | namespace Vanderlee\Comprehend\Parser\Terminal; |
||
| 4 | |||
| 5 | use Exception; |
||
| 6 | use Vanderlee\Comprehend\Core\Context; |
||
| 7 | use Vanderlee\Comprehend\Match\Failure; |
||
| 8 | use Vanderlee\Comprehend\Match\Success; |
||
| 9 | use Vanderlee\Comprehend\Parser\Parser; |
||
| 10 | |||
| 11 | /** |
||
| 12 | * Matches an integer within the specified range. |
||
| 13 | * |
||
| 14 | * @author Martijn |
||
| 15 | */ |
||
| 16 | class Integer extends Parser |
||
| 17 | { |
||
| 18 | use CaseSensitiveTrait; |
||
| 19 | |||
| 20 | /** |
||
| 21 | * List of digits to use for the different bases (up to 36). |
||
| 22 | * |
||
| 23 | * @var string |
||
| 24 | */ |
||
| 25 | private static $set = '0123456789abcdefghijklmnopqrstuvwxyz'; |
||
| 26 | |||
| 27 | /** |
||
| 28 | * @var int|null |
||
| 29 | */ |
||
| 30 | private $minimum; |
||
| 31 | |||
| 32 | /** |
||
| 33 | * @var int|null |
||
| 34 | */ |
||
| 35 | private $maximum; |
||
| 36 | |||
| 37 | /** |
||
| 38 | * @var int |
||
| 39 | */ |
||
| 40 | private $base; |
||
| 41 | |||
| 42 | /** |
||
| 43 | * @param int|null $minimum |
||
| 44 | * @param int|null $maximum |
||
| 45 | * @param int $base |
||
| 46 | * |
||
| 47 | * @throws Exception |
||
| 48 | */ |
||
| 49 | 8 | public function __construct($minimum = 0, $maximum = null, $base = 10) |
|
| 50 | { |
||
| 51 | 8 | if ($minimum !== null |
|
| 52 | 8 | && $maximum !== null |
|
| 53 | 8 | && $minimum > $maximum) { |
|
| 54 | 1 | throw new Exception('Maximum must be greater than minimum'); |
|
| 55 | } |
||
| 56 | |||
| 57 | 7 | $this->setMinimum($minimum); |
|
| 58 | 6 | $this->setMaximum($maximum); |
|
| 59 | |||
| 60 | 5 | $this->base = intval($base); |
|
| 61 | 5 | if ($base < 2 |
|
| 62 | 5 | || $base > strlen(self::$set)) { |
|
| 63 | 2 | throw new Exception('Unsupported base'); |
|
| 64 | } |
||
| 65 | 3 | } |
|
| 66 | |||
| 67 | /** |
||
| 68 | * @param int|null $minimum |
||
| 69 | * |
||
| 70 | * @throws Exception |
||
| 71 | */ |
||
| 72 | 7 | private function setMinimum($minimum) |
|
| 73 | { |
||
| 74 | 7 | if ($minimum !== null |
|
| 75 | 7 | && !is_int($minimum)) { |
|
|
0 ignored issues
–
show
introduced
by
Loading history...
|
|||
| 76 | 1 | throw new Exception('Minimum must be integer or `null`'); |
|
| 77 | } |
||
| 78 | |||
| 79 | 6 | $this->minimum = $minimum; |
|
| 80 | 6 | } |
|
| 81 | |||
| 82 | /** |
||
| 83 | * @param int|null $maximum |
||
| 84 | * |
||
| 85 | * @throws Exception |
||
| 86 | */ |
||
| 87 | 6 | private function setMaximum($maximum) |
|
| 88 | { |
||
| 89 | 6 | if ($maximum !== null |
|
| 90 | 6 | && !is_int($maximum)) { |
|
|
0 ignored issues
–
show
|
|||
| 91 | 1 | throw new Exception('Maximum must be integer or `null`'); |
|
| 92 | } |
||
| 93 | |||
| 94 | 5 | $this->maximum = $maximum; |
|
| 95 | 5 | } |
|
| 96 | |||
| 97 | 51 | private function isInRange($integer) |
|
| 98 | { |
||
| 99 | 51 | return ($this->minimum === null |
|
| 100 | 51 | || $integer >= $this->minimum) |
|
| 101 | 49 | && ($this->maximum === null |
|
| 102 | 51 | || $integer <= $this->maximum); |
|
| 103 | } |
||
| 104 | |||
| 105 | /** |
||
| 106 | * @param string $input |
||
| 107 | * @param int $offset |
||
| 108 | * @param Context $context |
||
| 109 | * |
||
| 110 | * @return Failure|Success |
||
| 111 | */ |
||
| 112 | 54 | protected function parse(&$input, $offset, Context $context) |
|
| 113 | { |
||
| 114 | 54 | $this->pushCaseSensitivityToContext($context); |
|
| 115 | |||
| 116 | // Build pattern |
||
| 117 | 54 | $set0 = substr(self::$set, 0, $this->base); |
|
| 118 | 54 | $set1 = substr(self::$set, 1, $this->base - 1); |
|
| 119 | 54 | $pattern = '/(?:0|-?[' . $set1 . '][' . $set0 . ']*)/A' . ($context->isCaseSensitive() |
|
| 120 | 51 | ? '' |
|
| 121 | 54 | : 'i'); |
|
| 122 | |||
| 123 | 54 | $this->popCaseSensitivityFromContext($context); |
|
| 124 | |||
| 125 | 54 | if (preg_match($pattern, $input, $match, 0, $offset) === 1) { |
|
| 126 | do { |
||
| 127 | 51 | $integer = intval($match[0], $this->base); |
|
| 128 | 51 | if ($this->isInRange($integer) |
|
| 129 | 51 | && $match[0] !== '-') { |
|
| 130 | 40 | return $this->success($input, $offset, mb_strlen($match[0])); |
|
| 131 | } |
||
| 132 | |||
| 133 | 18 | $match[0] = substr($match[0], 0, -1); // strip off last char |
|
| 134 | 18 | } while ($match[0] !== ''); |
|
| 135 | } |
||
| 136 | |||
| 137 | 14 | return $this->failure($input, $offset); |
|
| 138 | } |
||
| 139 | |||
| 140 | /** |
||
| 141 | * @return string |
||
| 142 | */ |
||
| 143 | 54 | public function __toString() |
|
| 144 | { |
||
| 145 | 54 | return ($this->minimum === null |
|
| 146 | 7 | ? '<-INF' |
|
| 147 | 54 | : ('[' . $this->minimum)) . ',' . ($this->maximum === null |
|
| 148 | 14 | ? 'INF>' |
|
| 149 | 54 | : ($this->maximum . ']')); |
|
| 150 | } |
||
| 151 | } |
||
| 152 |