Skip to content

Commit eb45582

Browse files
committed
Optimize cursor
1 parent e91d6a5 commit eb45582

11 files changed

Lines changed: 177 additions & 222 deletions

File tree

libs/phpdoc/src/DocBlock/Grammar/DescriptionGrammarRule.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
public const string NAME = 'Description';
2525

2626
public function __construct(
27-
private DescriptionParserInterface $parser,
27+
private DescriptionParserInterface $descriptionParser,
2828
) {}
2929

3030
public function __invoke(Cursor $cursor): DescriptionInterface
@@ -35,6 +35,6 @@ public function __invoke(Cursor $cursor): DescriptionInterface
3535
throw new NoMatchException('Expected a description');
3636
}
3737

38-
return $this->parser->parse($text);
38+
return $this->descriptionParser->parse($text);
3939
}
4040
}

libs/phpdoc/src/DocBlock/Grammar/NameValidator.php

Lines changed: 0 additions & 64 deletions
This file was deleted.

libs/phpdoc/src/DocBlock/Grammar/ReferenceGrammarRule.php

Lines changed: 34 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -23,145 +23,73 @@
2323
{
2424
public const string NAME = 'Reference';
2525

26-
/**
27-
* Validates a member name: letters, digits and "_".
28-
*/
29-
private NameValidator $names;
30-
31-
/**
32-
* Validates a class name: letters, digits, "_" and "\".
33-
*/
34-
private NameValidator $symbols;
35-
36-
public function __construct()
37-
{
38-
$this->names = new NameValidator();
39-
$this->symbols = new NameValidator('\\');
40-
}
41-
4226
public function __invoke(Cursor $cursor): CodeReference
4327
{
44-
$reference = $cursor->readWord();
28+
$reference = $this->parse($cursor);
4529

46-
if ($reference === '') {
30+
// A reference is a single word: nothing but whitespace may follow it.
31+
if ($reference === null || $cursor->readWord() !== '') {
4732
throw new NoMatchException('Expected a code reference');
4833
}
4934

50-
return $this->parse($reference)
51-
?? throw new NoMatchException(\sprintf('Invalid code reference "%s"', $reference));
35+
return $reference;
5236
}
5337

54-
private function parse(string $reference): ?CodeReference
38+
private function parse(Cursor $cursor): ?CodeReference
5539
{
5640
// A variable: "$name".
57-
if ($reference[0] === '$') {
58-
return $this->parseVariable($reference);
41+
if ($cursor->readLiteral('$')) {
42+
$name = $cursor->readPhpIdentifier();
43+
44+
return $name === '' ? null : new VariableReference($name);
5945
}
6046

61-
// A class member: "Class::member".
62-
$separator = \strpos($reference, '::');
47+
$symbol = $cursor->readPhpQualifiedName();
6348

64-
if ($separator !== false) {
65-
return $this->parseClassMember(
66-
\substr($reference, 0, $separator),
67-
\substr($reference, $separator + 2),
68-
);
49+
if ($symbol === '') {
50+
return null;
51+
}
52+
53+
// A class member: "Class::member".
54+
if ($cursor->readLiteral('::')) {
55+
return $this->parseMember($cursor, $symbol);
6956
}
7057

7158
// A function: "name()".
72-
if (\str_ends_with($reference, '()')) {
73-
return $this->parseFunction($reference);
59+
if ($cursor->readLiteral('()')) {
60+
return new FunctionReference($symbol);
7461
}
7562

7663
// A class or global constant: "name".
77-
return $this->parseSymbol($reference);
64+
return new SymbolReference($symbol);
7865
}
7966

8067
/**
81-
* Parses a variable: "$name".
82-
*/
83-
private function parseVariable(string $reference): ?VariableReference
84-
{
85-
$name = $this->names->validate(\substr($reference, 1));
86-
87-
return $name === null ? null : new VariableReference($name);
88-
}
89-
90-
/**
91-
* Parses a function: "name()".
92-
*/
93-
private function parseFunction(string $reference): ?FunctionReference
94-
{
95-
$symbol = $this->symbols->validate(\substr($reference, 0, -2));
96-
97-
return $symbol === null ? null : new FunctionReference($symbol);
98-
}
99-
100-
/**
101-
* Parses a class or global constant: "name".
68+
* Parses the member of a "Class::member" reference.
69+
*
70+
* @param non-empty-string $class
10271
*/
103-
private function parseSymbol(string $reference): ?SymbolReference
72+
private function parseMember(Cursor $cursor, string $class): ?CodeReference
10473
{
105-
$symbol = $this->symbols->validate($reference);
74+
// A property: "$name".
75+
if ($cursor->readLiteral('$')) {
76+
$name = $cursor->readPhpIdentifier();
10677

107-
return $symbol === null ? null : new SymbolReference($symbol);
108-
}
78+
return $name === '' ? null : new ClassPropertyReference($class, $name);
79+
}
10980

110-
private function parseClassMember(string $class, string $member): ?CodeReference
111-
{
112-
$symbol = $this->symbols->validate($class);
81+
$member = $cursor->readPhpIdentifier();
11382

114-
if ($symbol === null || $member === '') {
83+
if ($member === '') {
11584
return null;
11685
}
11786

118-
// A property: "$name".
119-
if ($member[0] === '$') {
120-
return $this->parseClassProperty($symbol, $member);
121-
}
122-
12387
// A method: "name()".
124-
if (\str_ends_with($member, '()')) {
125-
return $this->parseClassMethod($symbol, $member);
88+
if ($cursor->readLiteral('()')) {
89+
return new ClassMethodReference($class, $member);
12690
}
12791

12892
// A constant: "name".
129-
return $this->parseClassConstant($symbol, $member);
130-
}
131-
132-
/**
133-
* Parses the "$name" part of a "Class::$name" property reference.
134-
*
135-
* @param non-empty-string $class
136-
*/
137-
private function parseClassProperty(string $class, string $member): ?ClassPropertyReference
138-
{
139-
$name = $this->names->validate(\substr($member, 1));
140-
141-
return $name === null ? null : new ClassPropertyReference($class, $name);
142-
}
143-
144-
/**
145-
* Parses the "name()" part of a "Class::name()" method reference.
146-
*
147-
* @param non-empty-string $class
148-
*/
149-
private function parseClassMethod(string $class, string $member): ?ClassMethodReference
150-
{
151-
$name = $this->names->validate(\substr($member, 0, -2));
152-
153-
return $name === null ? null : new ClassMethodReference($class, $name);
154-
}
155-
156-
/**
157-
* Parses the "name" part of a "Class::name" constant reference.
158-
*
159-
* @param non-empty-string $class
160-
*/
161-
private function parseClassConstant(string $class, string $member): ?ClassConstantReference
162-
{
163-
$name = $this->names->validate($member);
164-
165-
return $name === null ? null : new ClassConstantReference($class, $name);
93+
return new ClassConstantReference($class, $member);
16694
}
16795
}

libs/phpdoc/src/DocBlock/Grammar/TypeGrammarRule.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public function __construct(
2626

2727
public function __invoke(Cursor $cursor): TypeNode
2828
{
29-
$start = $cursor->tell();
29+
$start = $cursor->position;
3030
$source = $cursor->readRemainder();
3131

3232
if ($source === '') {
@@ -38,7 +38,7 @@ public function __invoke(Cursor $cursor): TypeNode
3838
// text (e.g. a description) rather than at the end of the buffer.
3939
$result = $this->typeParser->parseTolerant($source);
4040

41-
$cursor->seek($start + $result->offset);
41+
$cursor->position = $start + $result->offset;
4242

4343
return $result->type;
4444
}

libs/phpdoc/src/DocBlock/Grammar/VariableGrammarRule.php

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,28 +17,22 @@
1717
{
1818
public const string NAME = 'Variable';
1919

20-
/**
21-
* Validates a variable name: letters, digits and "_".
22-
*/
23-
private NameValidator $names;
24-
25-
public function __construct()
26-
{
27-
$this->names = new NameValidator();
28-
}
29-
3020
/**
3121
* @return non-empty-string
3222
*/
3323
public function __invoke(Cursor $cursor): string
3424
{
35-
$variable = $cursor->readWord();
25+
if (!$cursor->readLiteral('$')) {
26+
throw new NoMatchException('Expected a variable');
27+
}
28+
29+
$name = $cursor->readPhpIdentifier();
3630

37-
if ($variable === '' || $variable[0] !== '$') {
31+
// A variable is a single word: nothing but whitespace may follow it.
32+
if ($name === '' || $cursor->readWord() !== '') {
3833
throw new NoMatchException('Expected a variable');
3934
}
4035

41-
return $this->names->validate(\substr($variable, 1))
42-
?? throw new NoMatchException(\sprintf('Invalid variable "%s"', $variable));
36+
return $name;
4337
}
4438
}

libs/phpdoc/src/DocBlock/Tag/GenericTagDefinition.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
final class GenericTagDefinition extends TagDefinition
1515
{
16-
public const string NAME = '<any>';
16+
public const string NAME = '<Tag>';
1717

1818
public private(set) string $name = self::NAME;
1919

libs/phpdoc/src/DocBlockParser.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ private function createDefaultRules(): array
8181
VariableGrammarRule::NAME => new VariableGrammarRule(),
8282
DescriptionGrammarRule::NAME => new \ReflectionClass(DescriptionGrammarRule::class)
8383
->newLazyProxy(fn(): DescriptionGrammarRule => new DescriptionGrammarRule(
84-
parser: $this->descriptionParser,
84+
descriptionParser: $this->descriptionParser,
8585
)),
8686
];
8787
}

libs/phpdoc/src/Parser/Grammar/Context.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public function capture(string $alias, mixed $value): void
3535
*/
3636
public function mark(): array
3737
{
38-
return [$this->cursor->tell(), \count($this->captures)];
38+
return [$this->cursor->position, \count($this->captures)];
3939
}
4040

4141
/**
@@ -48,7 +48,7 @@ public function rollback(array $snapshot): void
4848
{
4949
[$position, $length] = $snapshot;
5050

51-
$this->cursor->seek($position);
51+
$this->cursor->position = $position;
5252

5353
if (\count($this->captures) > $length) {
5454
$this->captures = \array_slice($this->captures, 0, $length);

0 commit comments

Comments
 (0)