Meta: Implement code generation for CSS keyword values

This commit is contained in:
Callum Law 2026-04-25 00:09:56 +12:00 committed by Sam Atkins
parent 9a3f2b23a1
commit 521beb5a96
4 changed files with 62 additions and 1 deletions

View file

@ -16,5 +16,13 @@ class Type:
return output + "\n"
@dataclass(frozen=True)
class Keyword:
value: str
def dump(self, indent: int) -> str:
return f"{'': >{indent}}Keyword: {self.value}\n"
# https://drafts.csswg.org/css-values-4/#component-types
ComponentValue = Union[Type]
ComponentValue = Union[Type, Keyword]

View file

@ -1,3 +1,4 @@
from Utils.CSSGrammar.Parser.component_values import Keyword
from Utils.CSSGrammar.Parser.component_values import Type
from Utils.CSSGrammar.Parser.token import Token
from Utils.CSSGrammar.Parser.token import TokenType
@ -41,6 +42,9 @@ class Tokenizer:
case "<":
return self.consume_a_non_terminal_token()
if is_identifier_character(self.lexer.peek()):
return self.consume_a_keyword_token()
raise SyntaxError("CSSGrammar::Tokenizer: Unexpected character")
def consume_custom_ident_blacklist(self) -> list[str]:
@ -87,3 +91,10 @@ class Tokenizer:
raise SyntaxError("CSSGrammar::Tokenizer: Expected '>'")
return Token.create_component_value(Type(name, custom_ident_blacklist))
def consume_a_keyword_token(self) -> Token:
value = self.consume_an_identifier()
assert value
return Token.create_component_value(Keyword(value))

View file

@ -1,5 +1,6 @@
from typing import TextIO
from Utils.CSSGrammar.Parser.component_values import Keyword
from Utils.CSSGrammar.Parser.component_values import Type
from Utils.CSSGrammar.Parser.grammar_node import CombinatorGrammarNode
from Utils.CSSGrammar.Parser.grammar_node import CombinatorType
@ -7,6 +8,7 @@ from Utils.CSSGrammar.Parser.grammar_node import ComponentValueGrammarNode
from Utils.CSSGrammar.Parser.grammar_node import GrammarNode
from Utils.CSSGrammar.Parser.parser import parse_value_definition_grammar
from Utils.utils import snake_casify
from Utils.utils import title_casify
def generate_css_parser_expression_for_type_component_value(out: TextIO, cpp_name: str, type: Type) -> None:
@ -25,6 +27,11 @@ def generate_css_parser_expression_for_type_component_value(out: TextIO, cpp_nam
out.write(f"auto {cpp_name} = parse_{type_name}_value(tokens{additional_arguments});\n")
def generate_css_parser_expression_for_keyword_component_value(out: TextIO, cpp_name: str, keyword: Keyword) -> None:
keyword_name = title_casify(keyword.value)
out.write(f"auto {cpp_name} = parse_specific_keyword_value(tokens, Keyword::{keyword_name});\n")
def generate_css_parser_expression_for_component_value_grammar_node(
out: TextIO, cpp_name: str, grammar_node: ComponentValueGrammarNode
) -> None:
@ -32,6 +39,9 @@ def generate_css_parser_expression_for_component_value_grammar_node(
case Type() as type_component_value:
generate_css_parser_expression_for_type_component_value(out, cpp_name, type_component_value)
return
case Keyword() as keyword:
generate_css_parser_expression_for_keyword_component_value(out, cpp_name, keyword)
return
raise TypeError(f"Unhandled component value type: {type(grammar_node.component_value).__name__}")

View file

@ -37,6 +37,38 @@ class TestCSSGrammarParser(unittest.TestCase):
""",
)
def test_parse_keyword(self) -> None:
syntax = parse_value_definition_grammar("auto")
self.assertEqual(
syntax.dump(),
"""ComponentValue
Keyword: auto
""",
)
def test_parse_hyphenated_keyword(self) -> None:
syntax = parse_value_definition_grammar("max-content")
self.assertEqual(
syntax.dump(),
"""ComponentValue
Keyword: max-content
""",
)
def test_parse_keyword_alternatives(self) -> None:
syntax = parse_value_definition_grammar("auto | none | <length>")
self.assertEqual(
syntax.dump(),
"""Combinator(Alternatives):
ComponentValue
Keyword: auto
ComponentValue
Keyword: none
ComponentValue
Type: length
""",
)
def test_parse_alternatives(self) -> None:
syntax = parse_value_definition_grammar("<foo> | <bar> | <baz>")
self.assertEqual(