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
![]() |
|||
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 |