Serenity Operating System
at master 210 lines 7.0 kB view raw
1/* 2 * Copyright (c) 2022, Sam Atkins <atkinssj@serenityos.org> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include "GeneratorUtil.h" 8#include <AK/GenericLexer.h> 9#include <AK/SourceGenerator.h> 10#include <AK/StringBuilder.h> 11#include <LibCore/ArgsParser.h> 12#include <LibMain/Main.h> 13 14ErrorOr<void> generate_header_file(JsonObject& transforms_data, Core::File& file); 15ErrorOr<void> generate_implementation_file(JsonObject& transforms_data, Core::File& file); 16 17ErrorOr<int> serenity_main(Main::Arguments arguments) 18{ 19 StringView generated_header_path; 20 StringView generated_implementation_path; 21 StringView identifiers_json_path; 22 23 Core::ArgsParser args_parser; 24 args_parser.add_option(generated_header_path, "Path to the TransformFunctions header file to generate", "generated-header-path", 'h', "generated-header-path"); 25 args_parser.add_option(generated_implementation_path, "Path to the TransformFunctions implementation file to generate", "generated-implementation-path", 'c', "generated-implementation-path"); 26 args_parser.add_option(identifiers_json_path, "Path to the JSON file to read from", "json-path", 'j', "json-path"); 27 args_parser.parse(arguments); 28 29 auto json = TRY(read_entire_file_as_json(identifiers_json_path)); 30 VERIFY(json.is_object()); 31 auto transforms_data = json.as_object(); 32 33 auto generated_header_file = TRY(Core::File::open(generated_header_path, Core::File::OpenMode::Write)); 34 auto generated_implementation_file = TRY(Core::File::open(generated_implementation_path, Core::File::OpenMode::Write)); 35 36 TRY(generate_header_file(transforms_data, *generated_header_file)); 37 TRY(generate_implementation_file(transforms_data, *generated_implementation_file)); 38 39 return 0; 40} 41 42static DeprecatedString title_casify_transform_function(StringView input) 43{ 44 // Transform function names look like `fooBar`, so we just have to make the first character uppercase. 45 StringBuilder builder; 46 builder.append(toupper(input[0])); 47 builder.append(input.substring_view(1)); 48 return builder.to_deprecated_string(); 49} 50 51ErrorOr<void> generate_header_file(JsonObject& transforms_data, Core::File& file) 52{ 53 StringBuilder builder; 54 SourceGenerator generator { builder }; 55 56 generator.append(R"~~~( 57#pragma once 58 59#include <AK/Optional.h> 60#include <AK/StringView.h> 61#include <AK/Vector.h> 62 63namespace Web::CSS { 64 65)~~~"); 66 67 generator.appendln("enum class TransformFunction {"); 68 transforms_data.for_each_member([&](auto& name, auto&) { 69 auto member_generator = generator.fork(); 70 member_generator.set("name:titlecase", title_casify_transform_function(name)); 71 member_generator.appendln(" @name:titlecase@,"); 72 }); 73 generator.appendln("};"); 74 75 generator.appendln("Optional<TransformFunction> transform_function_from_string(StringView);"); 76 generator.appendln("StringView to_string(TransformFunction);"); 77 78 generator.append(R"~~~( 79enum class TransformFunctionParameterType { 80 Angle, 81 Length, 82 LengthPercentage, 83 Number, 84}; 85 86struct TransformFunctionParameter { 87 TransformFunctionParameterType type; 88 bool required; 89}; 90 91struct TransformFunctionMetadata { 92 Vector<TransformFunctionParameter> parameters; 93}; 94TransformFunctionMetadata transform_function_metadata(TransformFunction); 95)~~~"); 96 97 generator.appendln("\n}"); 98 99 TRY(file.write_until_depleted(generator.as_string_view().bytes())); 100 return {}; 101} 102 103ErrorOr<void> generate_implementation_file(JsonObject& transforms_data, Core::File& file) 104{ 105 StringBuilder builder; 106 SourceGenerator generator { builder }; 107 108 generator.append(R"~~~( 109#include <LibWeb/CSS/TransformFunctions.h> 110#include <AK/Assertions.h> 111 112namespace Web::CSS { 113)~~~"); 114 115 generator.append(R"~~~( 116Optional<TransformFunction> transform_function_from_string(StringView name) 117{ 118)~~~"); 119 transforms_data.for_each_member([&](auto& name, auto&) { 120 auto member_generator = generator.fork(); 121 member_generator.set("name", name); 122 member_generator.set("name:titlecase", title_casify_transform_function(name)); 123 member_generator.append(R"~~~( 124 if (name.equals_ignoring_ascii_case("@name@"sv)) 125 return TransformFunction::@name:titlecase@; 126)~~~"); 127 }); 128 generator.append(R"~~~( 129 return {}; 130} 131)~~~"); 132 133 generator.append(R"~~~( 134StringView to_string(TransformFunction transform_function) 135{ 136 switch (transform_function) { 137)~~~"); 138 transforms_data.for_each_member([&](auto& name, auto&) { 139 auto member_generator = generator.fork(); 140 member_generator.set("name", name); 141 member_generator.set("name:titlecase", title_casify_transform_function(name)); 142 member_generator.append(R"~~~( 143 case TransformFunction::@name:titlecase@: 144 return "@name@"sv; 145)~~~"); 146 }); 147 generator.append(R"~~~( 148 default: 149 VERIFY_NOT_REACHED(); 150 } 151} 152)~~~"); 153 154 generator.append(R"~~~( 155TransformFunctionMetadata transform_function_metadata(TransformFunction transform_function) 156{ 157 switch (transform_function) { 158)~~~"); 159 transforms_data.for_each_member([&](auto& name, auto& value) { 160 VERIFY(value.is_object()); 161 162 auto member_generator = generator.fork(); 163 member_generator.set("name:titlecase", title_casify_transform_function(name)); 164 member_generator.append(R"~~~( 165 case TransformFunction::@name:titlecase@: 166 return TransformFunctionMetadata { 167 .parameters = {)~~~"); 168 169 JsonArray const& parameters = value.as_object().get_array("parameters"sv).value(); 170 bool first = true; 171 parameters.for_each([&](JsonValue const& value) { 172 GenericLexer lexer { value.as_object().get_deprecated_string("type"sv).value() }; 173 VERIFY(lexer.consume_specific('<')); 174 auto parameter_type_name = lexer.consume_until('>'); 175 VERIFY(lexer.consume_specific('>')); 176 177 StringView parameter_type = ""sv; 178 if (parameter_type_name == "angle"sv) 179 parameter_type = "Angle"sv; 180 else if (parameter_type_name == "length"sv) 181 parameter_type = "Length"sv; 182 else if (parameter_type_name == "length-percentage"sv) 183 parameter_type = "LengthPercentage"sv; 184 else if (parameter_type_name == "number"sv) 185 parameter_type = "Number"sv; 186 else 187 VERIFY_NOT_REACHED(); 188 189 member_generator.append(first ? " "sv : ", "sv); 190 first = false; 191 192 member_generator.append(DeprecatedString::formatted("{{ TransformFunctionParameterType::{}, {}}}", parameter_type, value.as_object().get("required"sv)->to_deprecated_string())); 193 }); 194 195 member_generator.append(R"~~~( } 196 }; 197)~~~"); 198 }); 199 generator.append(R"~~~( 200 default: 201 VERIFY_NOT_REACHED(); 202 } 203} 204)~~~"); 205 206 generator.appendln("\n}"); 207 208 TRY(file.write_until_depleted(generator.as_string_view().bytes())); 209 return {}; 210}