Skip to content

Commit df61592

Browse files
committed
Add parser compiler component
1 parent 8252e0a commit df61592

File tree

2 files changed

+212
-8
lines changed

2 files changed

+212
-8
lines changed

src/Parser.php

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,37 +4,41 @@
44

55
namespace Phplrt\Parser;
66

7+
use Phplrt\Contracts\Lexer\Channel;
78
use Phplrt\Contracts\Lexer\LexerInterface;
9+
use Phplrt\Contracts\Lexer\TokenInterface;
810
use Phplrt\Contracts\Parser\ParserInterface;
911
use Phplrt\Contracts\Source\Factory\SourceFactoryInterface;
1012
use Phplrt\Contracts\Source\ReadableInterface;
1113
use Phplrt\Parser\Buffer\ArrayBuffer;
14+
use Phplrt\Parser\Grammar\Alternation;
15+
use Phplrt\Parser\Grammar\Concatenation;
16+
use Phplrt\Parser\Grammar\Lexeme;
17+
use Phplrt\Parser\Grammar\Optional;
18+
use Phplrt\Parser\Grammar\Repetition;
19+
use Phplrt\Parser\Grammar\RuleInterface;
1220
use Phplrt\Source\SourceFactory;
1321

1422
final readonly class Parser implements ParserInterface
1523
{
1624
private SourceFactoryInterface $sources;
1725

18-
private \SplStack $stack;
19-
2026
public function __construct(
2127
private LexerInterface $lexer,
28+
/** @var array<int, RuleInterface> */
2229
private array $grammar,
2330
private int $initial,
2431
?SourceFactoryInterface $sources = null,
2532
) {
2633
$this->sources = $sources ?? SourceFactory::default();
27-
$this->stack = new \SplStack();
2834
}
2935

30-
public function parse(mixed $source): iterable
36+
public function parse(ReadableInterface $source): iterable
3137
{
3238
$readable = $this->sources->create($source);
3339

34-
$buffer = new ArrayBuffer($this->lexer->lex($readable));
35-
36-
$rule = $this->grammar[$this->initial];
40+
$buffer = new ArrayBuffer($this->filter($this->lexer->lex($readable)));
3741

38-
return $this->stack;
42+
return [];
3943
}
4044
}

src/ParserBackup.php

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Phplrt\Parser;
6+
7+
use Phplrt\Contracts\Lexer\Channel;
8+
use Phplrt\Contracts\Lexer\LexerInterface;
9+
use Phplrt\Contracts\Lexer\TokenInterface;
10+
use Phplrt\Contracts\Parser\ParserInterface;
11+
use Phplrt\Contracts\Source\Factory\SourceFactoryInterface;
12+
use Phplrt\Contracts\Source\ReadableInterface;
13+
use Phplrt\Parser\Buffer\ArrayBuffer;
14+
use Phplrt\Parser\Grammar\Alternation;
15+
use Phplrt\Parser\Grammar\Concatenation;
16+
use Phplrt\Parser\Grammar\Lexeme;
17+
use Phplrt\Parser\Grammar\Optional;
18+
use Phplrt\Parser\Grammar\Repetition;
19+
use Phplrt\Parser\Grammar\RuleInterface;
20+
use Phplrt\Source\SourceFactory;
21+
22+
final readonly class ParserBackup implements ParserInterface
23+
{
24+
private SourceFactoryInterface $sources;
25+
26+
public function __construct(
27+
private LexerInterface $lexer,
28+
/** @var array<int, RuleInterface> */
29+
private array $grammar,
30+
private int $initial,
31+
?SourceFactoryInterface $sources = null,
32+
) {
33+
$this->sources = $sources ?? SourceFactory::default();
34+
}
35+
36+
public function parse(ReadableInterface $source): iterable
37+
{
38+
$readable = $this->sources->create($source);
39+
40+
$buffer = new ArrayBuffer($this->filter($this->lexer->lex($readable)));
41+
42+
$stack = new \SplStack();
43+
44+
if ($this->reduce($this->grammar[$this->initial], $buffer, $stack)) {
45+
return $stack;
46+
}
47+
48+
return [];
49+
}
50+
51+
/**
52+
* @param iterable<TokenInterface> $tokens
53+
* @return \Traversable<TokenInterface>
54+
*/
55+
private function filter(iterable $tokens): \Traversable
56+
{
57+
foreach ($tokens as $token) {
58+
if ($token->channel === Channel::DEFAULT) {
59+
yield $token;
60+
}
61+
}
62+
}
63+
64+
private function reduce(RuleInterface $rule, ArrayBuffer $buffer, \SplStack $resultStack): bool
65+
{
66+
$stack = [[$rule, 0, $buffer->key(), $resultStack->count()]];
67+
$lastResult = null;
68+
69+
while (\count($stack) > 0) {
70+
$index = \count($stack) - 1;
71+
[$currentRule, $step, $rollbackBuffer, $rollbackStackCount] = $stack[$index];
72+
73+
if ($lastResult !== null) {
74+
// Обработка результата дочернего правила
75+
$result = $lastResult;
76+
$lastResult = null;
77+
78+
if ($currentRule instanceof Concatenation) {
79+
if (!$result) {
80+
$buffer->seek($rollbackBuffer);
81+
while ($resultStack->count() > $rollbackStackCount) {
82+
$resultStack->pop();
83+
}
84+
$lastResult = false;
85+
\array_pop($stack);
86+
continue;
87+
}
88+
89+
$nextStep = $step + 1;
90+
if ($nextStep < \count($currentRule->ruleIds)) {
91+
$stack[$index][1] = $nextStep;
92+
$stack[] = [$this->grammar[$currentRule->ruleIds[$nextStep]], 0, $buffer->key(), $resultStack->count()];
93+
continue;
94+
}
95+
96+
$lastResult = true;
97+
\array_pop($stack);
98+
continue;
99+
}
100+
101+
if ($currentRule instanceof Alternation) {
102+
if ($result) {
103+
$lastResult = true;
104+
\array_pop($stack);
105+
continue;
106+
}
107+
108+
$nextStep = $step + 1;
109+
if ($nextStep < \count($currentRule->ruleIds)) {
110+
$stack[$index][1] = $nextStep;
111+
$stack[] = [$this->grammar[$currentRule->ruleIds[$nextStep]], 0, $buffer->key(), $resultStack->count()];
112+
continue;
113+
}
114+
115+
$lastResult = false;
116+
\array_pop($stack);
117+
continue;
118+
}
119+
120+
if ($currentRule instanceof Optional) {
121+
$lastResult = true;
122+
\array_pop($stack);
123+
continue;
124+
}
125+
126+
if ($currentRule instanceof Repetition) {
127+
if (!$result) {
128+
$buffer->seek($rollbackBuffer);
129+
while ($resultStack->count() > $rollbackStackCount) {
130+
$resultStack->pop();
131+
}
132+
133+
$lastResult = $step >= $currentRule->gte;
134+
\array_pop($stack);
135+
continue;
136+
}
137+
138+
$nextStep = $step + 1;
139+
if ($nextStep < $currentRule->lte) {
140+
$stack[$index][1] = $nextStep;
141+
// Обновляем rollback points для новой итерации
142+
$stack[$index][2] = $buffer->key();
143+
$stack[$index][3] = $resultStack->count();
144+
$stack[] = [$this->grammar[$currentRule->ruleId], 0, $buffer->key(), $resultStack->count()];
145+
continue;
146+
}
147+
148+
$lastResult = true;
149+
\array_pop($stack);
150+
continue;
151+
}
152+
}
153+
154+
// Вход в правило (первый раз)
155+
if ($currentRule instanceof Lexeme) {
156+
$token = $buffer->current();
157+
158+
if ($token->id === $currentRule->tokenId) {
159+
$resultStack->push($token);
160+
$buffer->next();
161+
$lastResult = true;
162+
} else {
163+
$lastResult = false;
164+
}
165+
\array_pop($stack);
166+
continue;
167+
}
168+
169+
if ($currentRule instanceof Concatenation) {
170+
$stack[] = [$this->grammar[$currentRule->ruleIds[0]], 0, $buffer->key(), $resultStack->count()];
171+
continue;
172+
}
173+
174+
if ($currentRule instanceof Alternation) {
175+
$stack[] = [$this->grammar[$currentRule->ruleIds[0]], 0, $buffer->key(), $resultStack->count()];
176+
continue;
177+
}
178+
179+
if ($currentRule instanceof Optional) {
180+
$stack[] = [$this->grammar[$currentRule->ruleId], 0, $buffer->key(), $resultStack->count()];
181+
continue;
182+
}
183+
184+
if ($currentRule instanceof Repetition) {
185+
if ($currentRule->lte > 0) {
186+
$stack[] = [$this->grammar[$currentRule->ruleId], 0, $buffer->key(), $resultStack->count()];
187+
} else {
188+
$lastResult = $currentRule->gte === 0;
189+
\array_pop($stack);
190+
}
191+
continue;
192+
}
193+
194+
$lastResult = false;
195+
\array_pop($stack);
196+
}
197+
198+
return $lastResult;
199+
}
200+
}

0 commit comments

Comments
 (0)