Files
ladybird/Meta/Utils/CSSGrammar/generator.py

115 lines
4.9 KiB
Python

from math import inf
from typing import TextIO
from Utils.CSSGrammar.Parser.component_values import Keyword
from Utils.CSSGrammar.Parser.component_values import Type
from Utils.CSSGrammar.Parser.component_values import is_dimension_percentage_mix_type
from Utils.CSSGrammar.Parser.grammar_node import CombinatorGrammarNode
from Utils.CSSGrammar.Parser.grammar_node import CombinatorType
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 bound_value_to_code(value: float, type_name: str) -> str:
if value == -inf:
return "AK::NumericLimits<i32>::min()" if type_name == "integer" else "AK::NumericLimits<float>::lowest()"
if value == inf:
return "AK::NumericLimits<i32>::max()" if type_name == "integer" else "AK::NumericLimits<float>::max()"
return str(value)
def generate_css_parser_expression_for_type_component_value(out: TextIO, cpp_name: str, type: Type) -> None:
type_name = snake_casify(type.name)
additional_arguments = ""
if type.custom_ident_blacklist is not None:
additional_arguments = ", ReadonlySpan<StringView> { "
if len(type.custom_ident_blacklist) > 0:
disallowed_idents = "".join(f'"{disallowed_ident}"sv, ' for disallowed_ident in type.custom_ident_blacklist)
additional_arguments += f"Array<StringView, {len(type.custom_ident_blacklist)}> {{{disallowed_idents}}}"
additional_arguments += "}"
if type.numeric_type_accepted_range is not None:
minimum = bound_value_to_code(type.numeric_type_accepted_range.minimum, type_name)
maximum = bound_value_to_code(type.numeric_type_accepted_range.maximum, type_name)
accepted_range = f", {{ {minimum}, {maximum} }}"
additional_arguments += accepted_range
# NB: Pass the accepted range twice for dimension-percentage mixes, once for the dimension and once for the percentage.
if is_dimension_percentage_mix_type(type.name):
additional_arguments += accepted_range
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:
match grammar_node.component_value:
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__}")
def generate_css_parser_expression_for_alternatives(
out: TextIO, cpp_name: str, alternatives: list[GrammarNode]
) -> None:
out.write(f"auto const parse_{cpp_name}_alternatives = [&]() -> RefPtr<StyleValue const> {{\n")
for i, alternative in enumerate(alternatives):
alternative_name = f"{cpp_name}_alternative_{i}"
generate_css_parser_expression_for_grammar_node(out, alternative_name, alternative)
out.write(f"""if ({alternative_name})
return {alternative_name};
""")
out.write(f"""return nullptr;
}};
auto {cpp_name} = parse_{cpp_name}_alternatives();
""")
def generate_css_parser_expression_for_combinator_grammar_node(
out: TextIO, cpp_name: str, grammar_node: CombinatorGrammarNode
) -> None:
match grammar_node.combinator_type:
case CombinatorType.ALTERNATIVES:
generate_css_parser_expression_for_alternatives(out, cpp_name, grammar_node.children)
return
raise TypeError(f"Unhandled combinator type: {grammar_node.combinator_type}")
def generate_css_parser_expression_for_grammar_node(out: TextIO, cpp_name: str, grammar_node: GrammarNode) -> None:
match grammar_node:
case ComponentValueGrammarNode():
generate_css_parser_expression_for_component_value_grammar_node(out, cpp_name, grammar_node)
return
case CombinatorGrammarNode():
generate_css_parser_expression_for_combinator_grammar_node(out, cpp_name, grammar_node)
return
raise TypeError(f"Unhandled grammar node type: {type(grammar_node).__name__}")
def generate_css_parser_expression_for_grammar(out: TextIO, cpp_name: str, grammar: str) -> None:
generate_css_parser_expression_for_grammar_node(out, cpp_name, parse_value_definition_grammar(grammar))