diff --git a/Meta/CMake/libweb_generators.cmake b/Meta/CMake/libweb_generators.cmake index 2ca37098b8e..db81b101285 100644 --- a/Meta/CMake/libweb_generators.cmake +++ b/Meta/CMake/libweb_generators.cmake @@ -20,9 +20,9 @@ function (generate_css_implementation) arguments -j "${LIBWEB_INPUT_FOLDER}/CSS/Enums.json" ) - invoke_cpp_generator( + invoke_py_generator( "EnvironmentVariable.cpp" - Lagom::GenerateCSSEnvironmentVariable + "generate_libweb_css_environment_variables.py" "${LIBWEB_INPUT_FOLDER}/CSS/EnvironmentVariables.json" "CSS/EnvironmentVariable.h" "CSS/EnvironmentVariable.cpp" diff --git a/Meta/Generators/generate_libweb_css_environment_variables.py b/Meta/Generators/generate_libweb_css_environment_variables.py new file mode 100644 index 00000000000..c6ae88e3ba6 --- /dev/null +++ b/Meta/Generators/generate_libweb_css_environment_variables.py @@ -0,0 +1,157 @@ +#!/usr/bin/env python3 + +# Copyright (c) 2025-2026, Sam Atkins +# Copyright (c) 2026-present, the Ladybird developers. +# +# SPDX-License-Identifier: BSD-2-Clause + +import argparse +import json +import sys + +from pathlib import Path +from typing import TextIO + +sys.path.append(str(Path(__file__).resolve().parent.parent)) + +from Utils.utils import title_casify +from Utils.utils import underlying_type_for_enum + + +def verify_alphabetical(data: dict, path: str) -> None: + most_recent_name = "" + for name in data: + if name < most_recent_name: + print( + f"`{name}` is in the wrong position in `{path}`. Please keep this list alphabetical!", file=sys.stderr + ) + sys.exit(1) + most_recent_name = name + + +def write_header_file(out: TextIO, environment_variables: dict) -> None: + underlying_type = underlying_type_for_enum(len(environment_variables)) + out.write(f""" +#pragma once + +#include +#include +#include + +namespace Web::CSS {{ + +enum class EnvironmentVariable : {underlying_type} {{ +""") + + for name in environment_variables: + out.write(f" {title_casify(name)},\n") + + out.write(""" +}; + +Optional environment_variable_from_string(StringView); +StringView to_string(EnvironmentVariable); + +ValueType environment_variable_type(EnvironmentVariable); +u32 environment_variable_dimension_count(EnvironmentVariable); +} +""") + + +def write_implementation_file(out: TextIO, environment_variables: dict) -> None: + out.write(""" +#include + +namespace Web::CSS { + +Optional environment_variable_from_string(StringView string) +{ +""") + for name in environment_variables: + out.write(f""" + if (string.equals_ignoring_ascii_case("{name}"sv)) + return EnvironmentVariable::{title_casify(name)}; +""") + + out.write(""" + + return {}; +} + +StringView to_string(EnvironmentVariable environment_variable) +{ + switch (environment_variable) { +""") + + for name in environment_variables: + out.write(f""" + case EnvironmentVariable::{title_casify(name)}: + return "{name}"sv; +""") + + out.write(""" + } + VERIFY_NOT_REACHED(); +} + +ValueType environment_variable_type(EnvironmentVariable environment_variable) +{ + switch (environment_variable) { +""") + + for name, variable in environment_variables.items(): + value_type = variable["type"].strip("<>") + out.write(f""" + case EnvironmentVariable::{title_casify(name)}: + return ValueType::{title_casify(value_type)}; +""") + + out.write(""" + } + VERIFY_NOT_REACHED(); +} + +u32 environment_variable_dimension_count(EnvironmentVariable environment_variable) +{ + switch (environment_variable) { +""") + + for name, variable in environment_variables.items(): + out.write(f""" + case EnvironmentVariable::{title_casify(name)}: + return {variable["dimensions"]}; +""") + + out.write(""" + } + VERIFY_NOT_REACHED(); +} + +} +""") + + +def main(): + parser = argparse.ArgumentParser(description="Generate CSS EnvironmentVariable", add_help=False) + parser.add_argument("--help", action="help", help="Show this help message and exit") + parser.add_argument("-h", "--header", required=True, help="Path to the EnvironmentVariable header file to generate") + parser.add_argument( + "-c", "--implementation", required=True, help="Path to the EnvironmentVariable implementation file to generate" + ) + parser.add_argument("-j", "--json", required=True, help="Path to the JSON file to read from") + args = parser.parse_args() + + with open(args.json, "r", encoding="utf-8") as input_file: + environment_variables = json.load(input_file) + + verify_alphabetical(environment_variables, args.json) + + with open(args.header, "w", encoding="utf-8") as output_file: + write_header_file(output_file, environment_variables) + + with open(args.implementation, "w", encoding="utf-8") as output_file: + write_implementation_file(output_file, environment_variables) + + +if __name__ == "__main__": + main() diff --git a/Meta/Lagom/Tools/CodeGenerators/LibWeb/CMakeLists.txt b/Meta/Lagom/Tools/CodeGenerators/LibWeb/CMakeLists.txt index 6e06d2b44fa..f1b590aff92 100644 --- a/Meta/Lagom/Tools/CodeGenerators/LibWeb/CMakeLists.txt +++ b/Meta/Lagom/Tools/CodeGenerators/LibWeb/CMakeLists.txt @@ -1,7 +1,6 @@ set(SOURCES "") # avoid pulling SOURCES from parent scope lagom_tool(GenerateCSSDescriptors SOURCES GenerateCSSDescriptors.cpp LIBS LibMain) -lagom_tool(GenerateCSSEnvironmentVariable SOURCES GenerateCSSEnvironmentVariable.cpp LIBS LibMain) lagom_tool(GenerateCSSKeyword SOURCES GenerateCSSKeyword.cpp LIBS LibMain) lagom_tool(GenerateCSSMathFunctions SOURCES GenerateCSSMathFunctions.cpp LIBS LibMain) lagom_tool(GenerateCSSMediaFeatureID SOURCES GenerateCSSMediaFeatureID.cpp LIBS LibMain) diff --git a/Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateCSSEnvironmentVariable.cpp b/Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateCSSEnvironmentVariable.cpp deleted file mode 100644 index 2661037e5d2..00000000000 --- a/Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateCSSEnvironmentVariable.cpp +++ /dev/null @@ -1,197 +0,0 @@ -/* - * Copyright (c) 2025, Sam Atkins - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "GeneratorUtil.h" -#include -#include -#include - -ErrorOr generate_header_file(JsonObject const& environment_variables, Core::File& file); -ErrorOr generate_implementation_file(JsonObject const& environment_variables, Core::File& file); - -ErrorOr ladybird_main(Main::Arguments arguments) -{ - StringView generated_header_path; - StringView generated_implementation_path; - StringView json_path; - - Core::ArgsParser args_parser; - args_parser.add_option(generated_header_path, "Path to the EnvironmentVariable header file to generate", "generated-header-path", 'h', "generated-header-path"); - args_parser.add_option(generated_implementation_path, "Path to the EnvironmentVariable implementation file to generate", "generated-implementation-path", 'c', "generated-implementation-path"); - args_parser.add_option(json_path, "Path to the JSON file to read from", "json-path", 'j', "json-path"); - args_parser.parse(arguments); - - auto read_json_object = [](auto& path) -> ErrorOr { - auto json = TRY(read_entire_file_as_json(path)); - VERIFY(json.is_object()); - - // Check we're in alphabetical order - String most_recent_name; - json.as_object().for_each_member([&](auto& name, auto&) { - if (name < most_recent_name) { - warnln("`{}` is in the wrong position in `{}`. Please keep this list alphabetical!", name, path); - VERIFY_NOT_REACHED(); - } - most_recent_name = name; - }); - - return json.as_object(); - }; - - auto environment_variables = TRY(read_json_object(json_path)); - - auto generated_header_file = TRY(Core::File::open(generated_header_path, Core::File::OpenMode::Write)); - auto generated_implementation_file = TRY(Core::File::open(generated_implementation_path, Core::File::OpenMode::Write)); - - TRY(generate_header_file(environment_variables, *generated_header_file)); - TRY(generate_implementation_file(environment_variables, *generated_implementation_file)); - - return 0; -} - -ErrorOr generate_header_file(JsonObject const& environment_variables, Core::File& file) -{ - StringBuilder builder; - SourceGenerator generator { builder }; - - generator.set("environment_variable_underlying_type", underlying_type_for_enum(environment_variables.size())); - - generator.append(R"~~~( -#pragma once - -#include -#include -#include - -namespace Web::CSS { - -enum class EnvironmentVariable : @environment_variable_underlying_type@ { -)~~~"); - - environment_variables.for_each_member([&](auto& name, JsonValue const&) { - auto member_generator = generator.fork(); - member_generator.set("name:titlecase", title_casify(name)); - member_generator.appendln(" @name:titlecase@,"); - }); - generator.append(R"~~~( -}; - -Optional environment_variable_from_string(StringView); -StringView to_string(EnvironmentVariable); - -ValueType environment_variable_type(EnvironmentVariable); -u32 environment_variable_dimension_count(EnvironmentVariable); -} -)~~~"); - - TRY(file.write_until_depleted(generator.as_string_view().bytes())); - return {}; -} - -ErrorOr generate_implementation_file(JsonObject const& environment_variables, Core::File& file) -{ - StringBuilder builder; - SourceGenerator generator { builder }; - - generator.append(R"~~~( -#include - -namespace Web::CSS { - -Optional environment_variable_from_string(StringView string) -{ -)~~~"); - environment_variables.for_each_member([&](auto& name, JsonValue const&) { - auto member_generator = generator.fork(); - member_generator.set("name", name); - member_generator.set("name:titlecase", title_casify(name)); - - member_generator.append(R"~~~( - if (string.equals_ignoring_ascii_case("@name@"sv)) - return EnvironmentVariable::@name:titlecase@; -)~~~"); - }); - - generator.append(R"~~~( - - return {}; -} - -StringView to_string(EnvironmentVariable environment_variable) -{ - switch (environment_variable) { -)~~~"); - - environment_variables.for_each_member([&](auto& name, JsonValue const&) { - auto member_generator = generator.fork(); - member_generator.set("name", name); - member_generator.set("name:titlecase", title_casify(name)); - - member_generator.append(R"~~~( - case EnvironmentVariable::@name:titlecase@: - return "@name@"sv; -)~~~"); - }); - - generator.append(R"~~~( - } - VERIFY_NOT_REACHED(); -} - -ValueType environment_variable_type(EnvironmentVariable environment_variable) -{ - switch (environment_variable) { -)~~~"); - - environment_variables.for_each_member([&](auto& name, JsonValue const& value) { - auto& variable = value.as_object(); - auto member_generator = generator.fork(); - member_generator.set("name", name); - member_generator.set("name:titlecase", title_casify(name)); - - auto value_type = MUST(variable.get_string("type"sv)->trim("<>"sv)); - member_generator.set("value_type:titlecase", title_casify(value_type)); - - member_generator.append(R"~~~( - case EnvironmentVariable::@name:titlecase@: - return ValueType::@value_type:titlecase@; -)~~~"); - }); - - generator.append(R"~~~( - } - VERIFY_NOT_REACHED(); -} - -u32 environment_variable_dimension_count(EnvironmentVariable environment_variable) -{ - switch (environment_variable) { -)~~~"); - - environment_variables.for_each_member([&](auto& name, JsonValue const& value) { - auto& variable = value.as_object(); - auto member_generator = generator.fork(); - member_generator.set("name", name); - member_generator.set("name:titlecase", title_casify(name)); - member_generator.set("dimension_count", String::number(variable.get_u32("dimensions"sv).value())); - - member_generator.append(R"~~~( - case EnvironmentVariable::@name:titlecase@: - return @dimension_count@; -)~~~"); - }); - - generator.append(R"~~~( - } - VERIFY_NOT_REACHED(); -} - -} -)~~~"); - - TRY(file.write_until_depleted(generator.as_string_view().bytes())); - return {}; -}