Skip to content

Commit a3a71b1

Browse files
committed
TASK: Refactor TextParser after builder pattern
1 parent d404ddf commit a3a71b1

File tree

1 file changed

+98
-44
lines changed

1 file changed

+98
-44
lines changed

src/Language/Parser/Text/TextParser.php

Lines changed: 98 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -30,73 +30,127 @@
3030

3131
final class TextParser
3232
{
33+
private string $value;
34+
35+
private ?Token $startingToken;
36+
private ?Token $finalToken;
37+
38+
private bool $trimLeadingSpace;
39+
private bool $trimTrailingSpace;
40+
private bool $currentlyCapturingSpace;
41+
private bool $trailingSpaceContainsLineBreak;
42+
private bool $terminated;
43+
3344
/**
3445
* @param \Iterator<mixed,Token> $tokens
3546
* @param boolean $preserveLeadingSpace
3647
* @return null|TextNode
3748
*/
3849
public function parse(\Iterator $tokens, bool $preserveLeadingSpace = false): ?TextNode
3950
{
40-
$value = '';
41-
$startingToken = null;
42-
$finalToken = null;
43-
$ignoreSpace = false;
44-
$keepTrailingSpace = false;
45-
$forceTrimTrailingSpace = false;
46-
while (!Scanner::isEnd($tokens)) {
47-
$startingToken ??= $tokens->current();
48-
switch (Scanner::type($tokens)) {
49-
case TokenType::BRACKET_CURLY_OPEN:
50-
case TokenType::TAG_START_OPENING:
51-
$keepTrailingSpace = true;
52-
break 2;
53-
case TokenType::TAG_START_CLOSING:
54-
$value = rtrim($value);
55-
break 2;
56-
case TokenType::SPACE:
57-
case TokenType::END_OF_LINE:
58-
if (!$ignoreSpace) {
59-
$value .= ' ';
60-
}
61-
$ignoreSpace = true;
62-
if (Scanner::type($tokens) === TokenType::END_OF_LINE) {
63-
$forceTrimTrailingSpace = true;
64-
}
65-
$finalToken = $tokens->current();
66-
Scanner::skipOne($tokens);
67-
break;
68-
default:
69-
$value .= Scanner::value($tokens);
70-
$ignoreSpace = false;
71-
$forceTrimTrailingSpace = false;
72-
$finalToken = $tokens->current();
73-
Scanner::skipOne($tokens);
74-
break;
51+
$this->reset($preserveLeadingSpace);
52+
53+
while (!Scanner::isEnd($tokens) && !$this->terminated) {
54+
$this->startingToken ??= $tokens->current();
55+
56+
match (Scanner::type($tokens)) {
57+
TokenType::BRACKET_CURLY_OPEN,
58+
TokenType::TAG_START_OPENING =>
59+
$this->terminateAtAdjacentChildNode(),
60+
TokenType::TAG_START_CLOSING =>
61+
$this->terminateAtClosingTag(),
62+
TokenType::SPACE =>
63+
$this->captureSpace($tokens->current()),
64+
TokenType::END_OF_LINE =>
65+
$this->captureLineBreak($tokens->current()),
66+
default =>
67+
$this->captureText($tokens->current()),
68+
};
69+
70+
if (!$this->terminated) {
71+
Scanner::skipOne($tokens);
7572
}
7673
}
7774

78-
if (is_null($startingToken) || is_null($finalToken)) {
75+
return $this->build();
76+
}
77+
78+
private function reset(bool $preserveLeadingSpace): void
79+
{
80+
$this->value = '';
81+
82+
$this->startingToken = null;
83+
$this->finalToken = null;
84+
85+
$this->trimLeadingSpace = !$preserveLeadingSpace;
86+
$this->trimTrailingSpace = true;
87+
$this->currentlyCapturingSpace = false;
88+
$this->trailingSpaceContainsLineBreak = false;
89+
$this->terminated = false;
90+
}
91+
92+
private function terminateAtAdjacentChildNode(): void
93+
{
94+
$this->terminated = true;
95+
$this->trimTrailingSpace = $this->trailingSpaceContainsLineBreak;
96+
}
97+
98+
private function terminateAtClosingTag(): void
99+
{
100+
$this->terminated = true;
101+
}
102+
103+
private function captureSpace(Token $token): void
104+
{
105+
$this->finalToken = $token;
106+
107+
if ($this->currentlyCapturingSpace) {
108+
return;
109+
}
110+
111+
$this->currentlyCapturingSpace = true;
112+
$this->value .= ' ';
113+
}
114+
115+
private function captureLineBreak(Token $token): void
116+
{
117+
$this->captureSpace($token);
118+
$this->trailingSpaceContainsLineBreak = true;
119+
}
120+
121+
private function captureText(Token $token): void
122+
{
123+
$this->finalToken = $token;
124+
$this->currentlyCapturingSpace = false;
125+
$this->trailingSpaceContainsLineBreak = false;
126+
127+
$this->value .= $token->value;
128+
}
129+
130+
private function build(): ?TextNode
131+
{
132+
if (is_null($this->startingToken) || is_null($this->finalToken)) {
79133
return null;
80134
}
81135

82-
if (!$preserveLeadingSpace) {
83-
$value = ltrim($value);
136+
if ($this->trimLeadingSpace) {
137+
$this->value = ltrim($this->value);
84138
}
85139

86-
if (!$keepTrailingSpace || $forceTrimTrailingSpace) {
87-
$value = rtrim($value);
140+
if ($this->trimTrailingSpace) {
141+
$this->value = rtrim($this->value);
88142
}
89143

90-
if ($value === '' || $value === ' ') {
144+
if ($this->value === '' || $this->value === ' ') {
91145
return null;
92146
}
93147

94148
return new TextNode(
95149
rangeInSource: Range::from(
96-
$startingToken->boundaries->start,
97-
$finalToken->boundaries->end
150+
$this->startingToken->boundaries->start,
151+
$this->finalToken->boundaries->end
98152
),
99-
value: $value
153+
value: $this->value
100154
);
101155
}
102156
}

0 commit comments

Comments
 (0)