Meta: Handle juxtaposition in CSS value parsing code generation
This commit is contained in:
parent
30e58a48c6
commit
099c3fa590
4 changed files with 97 additions and 2 deletions
|
|
@ -19,6 +19,10 @@ class ComponentValueGrammarNode(GrammarNode):
|
|||
|
||||
|
||||
class CombinatorType(Enum):
|
||||
# https://drafts.csswg.org/css-values-4/#component-combinators
|
||||
# Juxtaposing components means that all of them must occur, in the given order.
|
||||
JUXTAPOSITION = "Juxtaposition"
|
||||
|
||||
# https://drafts.csswg.org/css-values-4/#comb-one
|
||||
# A bar (|) separates two or more alternatives: exactly one of them must occur.
|
||||
ALTERNATIVES = "Alternatives"
|
||||
|
|
|
|||
|
|
@ -26,17 +26,28 @@ class Parser:
|
|||
return value
|
||||
|
||||
def parse_alternatives(self) -> GrammarNode:
|
||||
children = [self.parse_component_value()]
|
||||
children = [self.parse_juxtaposition()]
|
||||
|
||||
while self.peek().is_token_type(TokenType.SINGLE_BAR):
|
||||
self.consume()
|
||||
children.append(self.parse_component_value())
|
||||
children.append(self.parse_juxtaposition())
|
||||
|
||||
if len(children) == 1:
|
||||
return children[0]
|
||||
|
||||
return CombinatorGrammarNode(CombinatorType.ALTERNATIVES, children)
|
||||
|
||||
def parse_juxtaposition(self) -> GrammarNode:
|
||||
children = [self.parse_component_value()]
|
||||
|
||||
while self.next_token_starts_component_value():
|
||||
children.append(self.parse_component_value())
|
||||
|
||||
if len(children) == 1:
|
||||
return children[0]
|
||||
|
||||
return CombinatorGrammarNode(CombinatorType.JUXTAPOSITION, children)
|
||||
|
||||
def parse_component_value(self) -> GrammarNode:
|
||||
# https://drafts.csswg.org/css-values-4/#component-multipliers
|
||||
# FIXME: Support component multipliers
|
||||
|
|
@ -67,6 +78,12 @@ class Parser:
|
|||
|
||||
return component_value
|
||||
|
||||
def next_token_starts_component_value(self) -> bool:
|
||||
return self.peek().token_type in (
|
||||
TokenType.OPEN_SQUARE_BRACKET,
|
||||
TokenType.COMPONENT_VALUE,
|
||||
)
|
||||
|
||||
def peek(self, offset: int = 0) -> Token:
|
||||
index = min(self.index + offset, len(self.tokens) - 1)
|
||||
return self.tokens[index]
|
||||
|
|
|
|||
|
|
@ -100,9 +100,38 @@ auto {cpp_name} = parse_{cpp_name}_alternatives();
|
|||
""")
|
||||
|
||||
|
||||
def generate_css_parser_expression_for_juxtaposition(out: TextIO, cpp_name: str, children: list[GrammarNode]) -> None:
|
||||
out.write(f"""auto const parse_{cpp_name}_juxtaposition = [&]() -> RefPtr<StyleValue const> {{
|
||||
auto {cpp_name}_transaction = tokens.begin_transaction();
|
||||
StyleValueVector {cpp_name}_values;
|
||||
{cpp_name}_values.ensure_capacity({len(children)});
|
||||
|
||||
""")
|
||||
|
||||
for i, component in enumerate(children):
|
||||
component_name = f"{cpp_name}_component_{i}"
|
||||
generate_css_parser_expression_for_grammar_node(out, component_name, component)
|
||||
out.write(f"""if (!{component_name})
|
||||
return nullptr;
|
||||
|
||||
{cpp_name}_values.append({component_name}.release_nonnull());
|
||||
""")
|
||||
|
||||
out.write(f"""{cpp_name}_transaction.commit();
|
||||
return StyleValueList::create(move({cpp_name}_values), StyleValueList::Separator::Space, StyleValueList::Collapsible::No);
|
||||
}};
|
||||
|
||||
auto {cpp_name} = parse_{cpp_name}_juxtaposition();
|
||||
""")
|
||||
|
||||
|
||||
def generate_css_parser_expression_for_combinator_grammar_node(
|
||||
out: TextIO, cpp_name: str, grammar_node: CombinatorGrammarNode
|
||||
) -> None:
|
||||
if grammar_node.combinator_type == CombinatorType.JUXTAPOSITION:
|
||||
generate_css_parser_expression_for_juxtaposition(out, cpp_name, grammar_node.children)
|
||||
return
|
||||
|
||||
if grammar_node.combinator_type == CombinatorType.ALTERNATIVES:
|
||||
generate_css_parser_expression_for_alternatives(out, cpp_name, grammar_node.children)
|
||||
return
|
||||
|
|
|
|||
|
|
@ -119,6 +119,51 @@ class TestCSSGrammarParser(unittest.TestCase):
|
|||
""",
|
||||
)
|
||||
|
||||
def test_parse_juxtaposition(self) -> None:
|
||||
syntax = parse_value_definition_grammar("<foo> <bar> baz")
|
||||
self.assertEqual(
|
||||
syntax.dump(),
|
||||
"""Combinator(Juxtaposition):
|
||||
ComponentValue
|
||||
Type: foo
|
||||
ComponentValue
|
||||
Type: bar
|
||||
ComponentValue
|
||||
Keyword: baz
|
||||
""",
|
||||
)
|
||||
|
||||
def test_parse_juxtaposition_has_higher_precedence_than_alternatives(self) -> None:
|
||||
syntax = parse_value_definition_grammar("<foo> <bar> | <baz>")
|
||||
self.assertEqual(
|
||||
syntax.dump(),
|
||||
"""Combinator(Alternatives):
|
||||
Combinator(Juxtaposition):
|
||||
ComponentValue
|
||||
Type: foo
|
||||
ComponentValue
|
||||
Type: bar
|
||||
ComponentValue
|
||||
Type: baz
|
||||
""",
|
||||
)
|
||||
|
||||
def test_parse_grouped_alternatives_in_juxtaposition(self) -> None:
|
||||
syntax = parse_value_definition_grammar("[ <foo> | <bar> ] <baz>")
|
||||
self.assertEqual(
|
||||
syntax.dump(),
|
||||
"""Combinator(Juxtaposition):
|
||||
Group:
|
||||
Combinator(Alternatives):
|
||||
ComponentValue
|
||||
Type: foo
|
||||
ComponentValue
|
||||
Type: bar
|
||||
ComponentValue
|
||||
Type: baz
|
||||
""",
|
||||
)
|
||||
|
||||
def test_parse_group(self) -> None:
|
||||
syntax = parse_value_definition_grammar("[ <length> ]")
|
||||
self.assertEqual(
|
||||
|
|
|
|||
Loading…
Reference in a new issue