Skip to content

Commit c27fae6

Browse files
committed
Some improvements
1 parent 7847f1f commit c27fae6

File tree

2 files changed

+55
-32
lines changed

2 files changed

+55
-32
lines changed

src/Parser.php

Lines changed: 32 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ public static function parseDictionary(string $string): Dictionary
2323
while (true) {
2424
$key = self::parseKey($input);
2525

26-
if (!$input->empty() && $input->getChar() === '=') {
27-
$input->consumeChar('=');
26+
if ($input->isChar('=')) {
27+
$input->consumeChar();
2828
$value->{$key} = self::parseItemOrInnerList($input);
2929
} else {
3030
// Bare boolean true value.
@@ -88,7 +88,7 @@ public static function parseList(string $string): OuterList
8888

8989
private static function parseItemOrInnerList(ParsingInput $input): TupleInterface
9090
{
91-
if ($input->getChar() === '(') {
91+
if ($input->isChar('(')) {
9292
return self::parseInnerList($input);
9393
} else {
9494
return self::doParseItem($input);
@@ -107,8 +107,8 @@ private static function parseInnerList(ParsingInput $input): InnerList
107107
while (!$input->empty()) {
108108
$input->trim();
109109

110-
if ($input->getChar() === ')') {
111-
$input->consumeChar(')');
110+
if ($input->isChar(')')) {
111+
$input->consumeChar();
112112
return new InnerList(
113113
$value,
114114
self::parseParameters($input)
@@ -117,7 +117,10 @@ private static function parseInnerList(ParsingInput $input): InnerList
117117

118118
$value[] = self::doParseItem($input);
119119

120-
if (!$input->empty() && !in_array($input->getChar(), [' ', ')'])) {
120+
if (!($input->isChar(' ') || $input->isChar(')'))) {
121+
if ($input->empty()) {
122+
break;
123+
}
121124
throw new ParseException('Unexpected character in inner list at position ' . $input->position());
122125
}
123126
}
@@ -153,6 +156,8 @@ public static function parseItem(string $string): Item
153156
/**
154157
* Internal implementation of parseItem that doesn't fail if input string
155158
* has remaining characters after parsing.
159+
*
160+
* @phpstan-impure
156161
*/
157162
private static function doParseItem(ParsingInput $input): Item
158163
{
@@ -171,13 +176,13 @@ private static function parseBareItem(ParsingInput $input): mixed
171176
{
172177
$char = $input->getChar();
173178
return match (true) {
174-
preg_match('/(-|\d)/', $char) == 1 => self::parseNumber($input),
175-
'"' === $char => self::parseString($input),
176-
preg_match('/[a-z*]/i', $char) == 1 => self::parseToken($input),
177-
':' === $char => self::parseByteSequence($input),
178-
'?' === $char => self::parseBoolean($input),
179-
'@' === $char => self::parseDate($input),
180-
'%' === $char => self::parseDisplayString($input),
179+
preg_match('/(-|\d)/', $char) === 1 => self::parseNumber($input),
180+
'"' === $char => self::parseString($input),
181+
preg_match('/[a-z*]/i', $char) === 1 => self::parseToken($input),
182+
':' === $char => self::parseByteSequence($input),
183+
'?' === $char => self::parseBoolean($input),
184+
'@' === $char => self::parseDate($input),
185+
'%' === $char => self::parseDisplayString($input),
181186
default => throw new ParseException('Unknown item type at position ' . $input->position()),
182187
};
183188
}
@@ -188,15 +193,15 @@ private static function parseBareItem(ParsingInput $input): mixed
188193
private static function parseParameters(ParsingInput $input): Parameters
189194
{
190195
$parameters = new Parameters();
191-
while (!$input->empty() && $input->getChar() === ';') {
192-
$input->consumeChar(';');
196+
while ($input->isChar(';')) {
197+
$input->consumeChar();
193198
$input->trim();
194199

195200
$key = self::parseKey($input);
196201
$parameters->{$key} = true;
197202

198-
if (!$input->empty() && $input->getChar() === '=') {
199-
$input->consumeChar('=');
203+
if ($input->isChar('=')) {
204+
$input->consumeChar();
200205
$parameters->{$key} = self::parseBareItem($input);
201206
}
202207
}
@@ -221,12 +226,12 @@ private static function parseKey(ParsingInput $input): string
221226
*/
222227
private static function parseBoolean(ParsingInput $input): bool
223228
{
224-
try {
225-
$input->consumeChar('?');
226-
return '1' === $input->consumeRegex('/^[01]/');
227-
} catch (\RuntimeException) {
228-
throw new ParseException('Invalid boolean at position ' . $input->position());
229-
}
229+
$input->consumeChar('?');
230+
return match ($input->consumeChar()) {
231+
'0' => false,
232+
'1' => true,
233+
default => throw new ParseException('Invalid boolean at position ' . $input->position()),
234+
};
230235
}
231236

232237
/**
@@ -266,12 +271,12 @@ private static function parseString(ParsingInput $input): string
266271
}
267272

268273
$char = $input->consumeChar();
269-
if ($char != '"' && $char != '\\') {
274+
if ($char !== '"' && $char !== '\\') {
270275
throw new ParseException(
271276
'Invalid escaped character in string at position ' . ($input->position() - 1)
272277
);
273278
}
274-
} elseif ($char == '"') {
279+
} elseif ($char === '"') {
275280
return $output;
276281
} elseif (ord($char) <= 0x1f || ord($char) >= 0x7f) {
277282
throw new ParseException('Invalid character in string at position ' . ($input->position() - 1));
@@ -304,15 +309,15 @@ private static function parseDisplayString(ParsingInput $string): DisplayString
304309
throw new ParseException(
305310
'Invalid character in display string at position ' . ($string->position() - 1)
306311
);
307-
} elseif ($char == '%') {
312+
} elseif ($char === '%') {
308313
try {
309314
$encoded_string .= '%' . $string->consumeRegex('/^[0-9a-f]{2}/');
310315
} catch (\RuntimeException) {
311316
throw new ParseException(
312317
'Invalid hex values in display string at position ' . ($string->position() - 1)
313318
);
314319
}
315-
} elseif ($char == '"') {
320+
} elseif ($char === '"') {
316321
$display_string = new DisplayString(rawurldecode($encoded_string));
317322
// An invalid UTF-8 subject will cause the preg_* function to match nothing.
318323
// @see https://www.php.net/manual/en/reference.pcre.pattern.modifiers.php

src/ParsingInput.php

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,13 @@
99
*/
1010
class ParsingInput
1111
{
12+
private readonly int $length;
1213
private int $position = 0;
1314

1415
public function __construct(
1516
private readonly string $value,
1617
) {
18+
$this->length = strlen($this->value);
1719
}
1820

1921
public function position(): int
@@ -23,7 +25,7 @@ public function position(): int
2325

2426
public function empty(): bool
2527
{
26-
return $this->position >= strlen($this->value);
28+
return $this->position >= $this->length;
2729
}
2830

2931
public function remaining(): string
@@ -41,12 +43,28 @@ public function remaining(): string
4143
*/
4244
public function trim(bool $ows = false): void
4345
{
44-
$this->consumeRegex('/^[' . ($ows ? ' \t' : ' ') . ']*/');
46+
while (
47+
$this->position < $this->length
48+
&& (
49+
$this->value[$this->position] === ' '
50+
|| ($ows && $this->value[$this->position] === "\t")
51+
)
52+
) {
53+
$this->position++;
54+
}
55+
}
56+
57+
public function isChar(string $char): bool
58+
{
59+
assert(strlen($char) === 1);
60+
61+
return $this->position < $this->length
62+
&& $this->value[$this->position] === $char;
4563
}
4664

4765
public function getChar(): string
4866
{
49-
if ($this->empty()) {
67+
if ($this->position >= $this->length) {
5068
throw new \RuntimeException('Reached end of value');
5169
}
5270
return $this->value[$this->position];
@@ -60,7 +78,7 @@ public function consume(int $length, string $expected = null): string
6078
assert($length > 0);
6179
assert($expected === null || strlen($expected) === $length);
6280

63-
if ($length > strlen($this->value) - $this->position) {
81+
if ($length > $this->length - $this->position) {
6482
throw new \RuntimeException('Reached end of value');
6583
}
6684

@@ -102,6 +120,6 @@ public function consumeRegex(string $pattern): string
102120
return $matches[0];
103121
}
104122

105-
throw new \RuntimeException();
123+
throw new \RuntimeException('Expression did not match');
106124
}
107125
}

0 commit comments

Comments
 (0)