Skip to content

Commit e6b19f2

Browse files
authored
[CLEANUP] Extract method DeclarationBlock::parseSelectors (#1416)
This now does the selector-parsing that `parse()` used to do itself. Should help with #1324 and #1398 (comment)
1 parent 400edb2 commit e6b19f2

File tree

1 file changed

+69
-52
lines changed

1 file changed

+69
-52
lines changed

src/RuleSet/DeclarationBlock.php

Lines changed: 69 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace Sabberworm\CSS\RuleSet;
66

7+
use Sabberworm\CSS\Comment\Comment;
78
use Sabberworm\CSS\Comment\CommentContainer;
89
use Sabberworm\CSS\CSSElement;
910
use Sabberworm\CSS\CSSList\CSSList;
@@ -65,58 +66,7 @@ public static function parse(ParserState $parserState, ?CSSList $list = null): ?
6566
$comments = [];
6667
$result = new DeclarationBlock($parserState->currentLine());
6768
try {
68-
$selectors = [];
69-
$selectorParts = [];
70-
$stringWrapperCharacter = null;
71-
$functionNestingLevel = 0;
72-
$consumedNextCharacter = false;
73-
static $stopCharacters = ['{', '}', '\'', '"', '(', ')', ','];
74-
do {
75-
if (!$consumedNextCharacter) {
76-
$selectorParts[] = $parserState->consume(1);
77-
}
78-
$selectorParts[] = $parserState->consumeUntil($stopCharacters, false, false, $comments);
79-
$nextCharacter = $parserState->peek();
80-
$consumedNextCharacter = false;
81-
switch ($nextCharacter) {
82-
case '\'':
83-
// The fallthrough is intentional.
84-
case '"':
85-
if (!\is_string($stringWrapperCharacter)) {
86-
$stringWrapperCharacter = $nextCharacter;
87-
} elseif ($stringWrapperCharacter === $nextCharacter) {
88-
if (\substr(\end($selectorParts), -1) !== '\\') {
89-
$stringWrapperCharacter = null;
90-
}
91-
}
92-
break;
93-
case '(':
94-
if (!\is_string($stringWrapperCharacter)) {
95-
++$functionNestingLevel;
96-
}
97-
break;
98-
case ')':
99-
if (!\is_string($stringWrapperCharacter)) {
100-
if ($functionNestingLevel <= 0) {
101-
throw new UnexpectedTokenException('anything but', ')');
102-
}
103-
--$functionNestingLevel;
104-
}
105-
break;
106-
case ',':
107-
if (!\is_string($stringWrapperCharacter) && $functionNestingLevel === 0) {
108-
$selectors[] = \implode('', $selectorParts);
109-
$selectorParts = [];
110-
$parserState->consume(1);
111-
$consumedNextCharacter = true;
112-
}
113-
break;
114-
}
115-
} while (!\in_array($nextCharacter, ['{', '}'], true) || \is_string($stringWrapperCharacter));
116-
if ($functionNestingLevel !== 0) {
117-
throw new UnexpectedTokenException(')', $nextCharacter);
118-
}
119-
$selectors[] = \implode('', $selectorParts); // add final or only selector
69+
$selectors = self::parseSelectors($parserState, $comments);
12070
$result->setSelectors($selectors, $list);
12171
if ($parserState->comes('{')) {
12272
$parserState->consume(1);
@@ -303,4 +253,71 @@ public function render(OutputFormat $outputFormat): string
303253

304254
return $result;
305255
}
256+
257+
/**
258+
* @param array<int, Comment> $comments
259+
*
260+
* @return list<string>
261+
*
262+
* @throws UnexpectedTokenException
263+
*/
264+
private static function parseSelectors(ParserState $parserState, array &$comments): array
265+
{
266+
$selectors = [];
267+
$selectorParts = [];
268+
$stringWrapperCharacter = null;
269+
$functionNestingLevel = 0;
270+
$consumedNextCharacter = false;
271+
static $stopCharacters = ['{', '}', '\'', '"', '(', ')', ','];
272+
273+
do {
274+
if (!$consumedNextCharacter) {
275+
$selectorParts[] = $parserState->consume(1);
276+
}
277+
$selectorParts[] = $parserState->consumeUntil($stopCharacters, false, false, $comments);
278+
$nextCharacter = $parserState->peek();
279+
$consumedNextCharacter = false;
280+
switch ($nextCharacter) {
281+
case '\'':
282+
// The fallthrough is intentional.
283+
case '"':
284+
if (!\is_string($stringWrapperCharacter)) {
285+
$stringWrapperCharacter = $nextCharacter;
286+
} elseif ($stringWrapperCharacter === $nextCharacter) {
287+
if (\substr(\end($selectorParts), -1) !== '\\') {
288+
$stringWrapperCharacter = null;
289+
}
290+
}
291+
break;
292+
case '(':
293+
if (!\is_string($stringWrapperCharacter)) {
294+
++$functionNestingLevel;
295+
}
296+
break;
297+
case ')':
298+
if (!\is_string($stringWrapperCharacter)) {
299+
if ($functionNestingLevel <= 0) {
300+
throw new UnexpectedTokenException('anything but', ')');
301+
}
302+
--$functionNestingLevel;
303+
}
304+
break;
305+
case ',':
306+
if (!\is_string($stringWrapperCharacter) && $functionNestingLevel === 0) {
307+
$selectors[] = \implode('', $selectorParts);
308+
$selectorParts = [];
309+
$parserState->consume(1);
310+
$consumedNextCharacter = true;
311+
}
312+
break;
313+
}
314+
} while (!\in_array($nextCharacter, ['{', '}'], true) || \is_string($stringWrapperCharacter));
315+
316+
if ($functionNestingLevel !== 0) {
317+
throw new UnexpectedTokenException(')', $nextCharacter);
318+
}
319+
$selectors[] = \implode('', $selectorParts); // add final or only selector
320+
321+
return $selectors;
322+
}
306323
}

0 commit comments

Comments
 (0)