Skip to content

Commit 15c9118

Browse files
author
Kirill Nesmeyanov
committed
Improve cached parser implementation
1 parent d9a6bad commit 15c9118

File tree

5 files changed

+85
-53
lines changed

5 files changed

+85
-53
lines changed

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414
"psr/simple-cache": "^1.0|^2.0|^3.0",
1515
"psr/log": "^1.0|^2.0|^3.0",
1616
"phplrt/lexer": "^3.6",
17-
"phplrt/parser": "^3.6"
17+
"phplrt/parser": "^3.6",
18+
"phplrt/source": "^3.6"
1819
},
1920
"autoload": {
2021
"psr-4": {

src/CachedParser.php

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,22 @@
66

77
use JetBrains\PhpStorm\Language;
88
use Phplrt\Contracts\Source\ReadableInterface;
9-
use Phplrt\Source\File;
9+
use Phplrt\Contracts\Source\SourceExceptionInterface;
10+
use Phplrt\Contracts\Source\SourceFactoryInterface;
11+
use Phplrt\Source\SourceFactory;
1012
use TypeLang\Parser\Node\Stmt\TypeStatement;
1113

1214
abstract class CachedParser implements ParserInterface
1315
{
1416
public function __construct(
1517
private readonly ParserInterface $parent,
18+
private readonly SourceFactoryInterface $sources = new SourceFactory(),
1619
) {}
1720

1821
/**
1922
* @return non-empty-string
23+
*
24+
* @throws SourceExceptionInterface In case of source hash creation error.
2025
*/
2126
protected function getCacheKey(ReadableInterface $source): string
2227
{
@@ -25,19 +30,24 @@ protected function getCacheKey(ReadableInterface $source): string
2530

2631
/**
2732
* @psalm-suppress UndefinedAttributeClass : Optional (builtin) attribute usage
33+
*
34+
* @throws SourceExceptionInterface In case of source creation error.
2835
*/
2936
public function parse(#[Language('PHP')] mixed $source): ?TypeStatement
3037
{
31-
/** @psalm-suppress PossiblyInvalidArgument */
32-
$source = File::fromSources($source);
38+
$instance = $this->sources->create($source);
3339

34-
return $this->getCachedItem($this->parent, $source);
40+
return $this->getCachedItem($this->parent, $instance);
3541
}
3642

37-
43+
/**
44+
* @throws SourceExceptionInterface In case of source creation error.
45+
*/
3846
public function clear(mixed $source): void
3947
{
40-
$this->removeCacheItem(File::new($source));
48+
$instance = $this->sources->create($source);
49+
50+
$this->removeCacheItem($instance);
4151
}
4252

4353
abstract protected function removeCacheItem(ReadableInterface $source): void;

src/Node/Literal/LiteralNode.php

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,9 @@
1313
*/
1414
abstract class LiteralNode extends TypeStatement implements LiteralNodeInterface
1515
{
16-
public readonly string $raw;
17-
18-
public function __construct(string $raw)
19-
{
20-
$this->raw = $raw;
21-
}
16+
public function __construct(
17+
public readonly string $raw,
18+
) {}
2219

2320
/**
2421
* Returns parsed literal value.

src/Parser.php

Lines changed: 64 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use Phplrt\Contracts\Lexer\TokenInterface;
1010
use Phplrt\Contracts\Parser\ParserRuntimeExceptionInterface;
1111
use Phplrt\Contracts\Source\ReadableInterface;
12+
use Phplrt\Contracts\Source\SourceExceptionInterface;
1213
use Phplrt\Contracts\Source\SourceFactoryInterface;
1314
use Phplrt\Lexer\Config\PassthroughHandler;
1415
use Phplrt\Lexer\Lexer;
@@ -20,8 +21,8 @@
2021
use Phplrt\Parser\Parser as ParserCombinator;
2122
use Phplrt\Parser\ParserConfigsInterface;
2223
use Phplrt\Source\SourceFactory;
23-
use TypeLang\Parser\Exception\SemanticException;
2424
use TypeLang\Parser\Exception\ParseException;
25+
use TypeLang\Parser\Exception\SemanticException;
2526
use TypeLang\Parser\Node\Literal\IntLiteralNode;
2627
use TypeLang\Parser\Node\Literal\StringLiteralNode;
2728
use TypeLang\Parser\Node\Stmt\TypeStatement;
@@ -125,83 +126,109 @@ public function parse(#[Language('PHP')] mixed $source): ?TypeStatement
125126
{
126127
$this->lastProcessedTokenOffset = 0;
127128

128-
/** @psalm-suppress PossiblyInvalidArgument */
129-
$source = $this->sources->create($source);
130-
131129
try {
130+
$instance = $this->sources->create($source);
132131

133-
foreach ($this->parser->parse($source) as $stmt) {
134-
if ($stmt instanceof TypeStatement) {
135-
$context = $this->parser->getLastExecutionContext();
132+
try {
133+
foreach ($this->parser->parse($instance) as $stmt) {
134+
if ($stmt instanceof TypeStatement) {
135+
$context = $this->parser->getLastExecutionContext();
136136

137-
if ($context !== null) {
138-
$token = $context->buffer->current();
137+
if ($context !== null) {
138+
$token = $context->buffer->current();
139139

140-
$this->lastProcessedTokenOffset = $token->getOffset();
141-
}
140+
$this->lastProcessedTokenOffset = $token->getOffset();
141+
}
142142

143-
return $stmt;
143+
return $stmt;
144+
}
144145
}
145-
}
146146

147-
return null;
148-
} catch (UnexpectedTokenException $e) {
149-
throw $this->unexpectedTokenError($e, $source);
150-
} catch (UnrecognizedTokenException $e) {
151-
throw $this->unrecognizedTokenError($e, $source);
152-
} catch (ParserRuntimeExceptionInterface $e) {
153-
throw $this->parserRuntimeError($e, $source);
154-
} catch (SemanticException $e) {
155-
throw $this->semanticError($e, $source);
156-
} catch (\Throwable $e) {
157-
throw $this->internalError($e, $source);
147+
return null;
148+
} catch (UnexpectedTokenException $e) {
149+
throw $this->unexpectedTokenError($e, $instance);
150+
} catch (UnrecognizedTokenException $e) {
151+
throw $this->unrecognizedTokenError($e, $instance);
152+
} catch (ParserRuntimeExceptionInterface $e) {
153+
throw $this->parserRuntimeError($e, $instance);
154+
} catch (SemanticException $e) {
155+
throw $this->semanticError($e, $instance);
156+
} catch (\Throwable $e) {
157+
throw $this->internalError($e, $instance);
158+
}
159+
} catch (SourceExceptionInterface $e) {
160+
throw new ParseException(
161+
message: $e->getMessage(),
162+
code: ParseException::ERROR_CODE_INTERNAL_ERROR,
163+
previous: $e,
164+
);
158165
}
166+
167+
return null;
159168
}
160169

170+
/**
171+
* @throws SourceExceptionInterface In case of source content reading error.
172+
*/
161173
private function unexpectedTokenError(UnexpectedTokenException $e, ReadableInterface $source): ParseException
162174
{
163175
$token = $e->getToken();
164176

165177
return ParseException::fromUnexpectedToken(
166-
$token->getValue(),
167-
$source->getContents(),
168-
$token->getOffset(),
178+
char: $token->getValue(),
179+
statement: $source->getContents(),
180+
offset: $token->getOffset(),
169181
);
170182
}
171183

184+
/**
185+
* @throws SourceExceptionInterface In case of source content reading error.
186+
*/
172187
private function unrecognizedTokenError(UnrecognizedTokenException $e, ReadableInterface $source): ParseException
173188
{
174189
$token = $e->getToken();
175190

176191
return ParseException::fromUnrecognizedToken(
177-
$token->getValue(),
178-
$source->getContents(),
179-
$token->getOffset(),
192+
token: $token->getValue(),
193+
statement: $source->getContents(),
194+
offset: $token->getOffset(),
180195
);
181196
}
182197

198+
/**
199+
* @throws SourceExceptionInterface In case of source content reading error.
200+
*/
183201
private function semanticError(SemanticException $e, ReadableInterface $source): ParseException
184202
{
185203
return ParseException::fromSemanticError(
186-
$e->getMessage(),
187-
$source->getContents(),
188-
$e->getOffset(),
189-
$e->getCode(),
204+
message: $e->getMessage(),
205+
statement: $source->getContents(),
206+
offset: $e->getOffset(),
207+
code: $e->getCode(),
190208
);
191209
}
192210

211+
/**
212+
* @throws SourceExceptionInterface In case of source content reading error.
213+
*/
193214
private function parserRuntimeError(ParserRuntimeExceptionInterface $e, ReadableInterface $source): ParseException
194215
{
195216
$token = $e->getToken();
196217

197218
return ParseException::fromUnrecognizedSyntaxError(
198-
$source->getContents(),
199-
$token->getOffset(),
219+
statement: $source->getContents(),
220+
offset: $token->getOffset(),
200221
);
201222
}
202223

224+
/**
225+
* @throws SourceExceptionInterface In case of source content reading error.
226+
*/
203227
private function internalError(\Throwable $e, ReadableInterface $source): ParseException
204228
{
205-
return ParseException::fromInternalError($source->getContents(), $e);
229+
return ParseException::fromInternalError(
230+
statement: $source->getContents(),
231+
e: $e
232+
);
206233
}
207234
}

src/ParserInterface.php

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
namespace TypeLang\Parser;
66

77
use JetBrains\PhpStorm\Language;
8-
use Phplrt\Contracts\Source\ReadableInterface;
98
use TypeLang\Parser\Exception\ParserExceptionInterface;
109
use TypeLang\Parser\Node\Stmt\TypeStatement;
1110

@@ -17,8 +16,6 @@ interface ParserInterface
1716
/**
1817
* Parses variadic sources into an abstract source tree (AST) node.
1918
*
20-
* @param resource|string|\SplFileInfo|ReadableInterface $source
21-
*
2219
* @throws ParserExceptionInterface In case of parsing exception occurs.
2320
* @throws \Throwable In case of internal error occurs.
2421
*

0 commit comments

Comments
 (0)