GenerateAriaRoles.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398
  1. /*
  2. * Copyright (c) 2023, Jonah Shafran <jonahshafran@gmail.com>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include "GeneratorUtil.h"
  7. #include <AK/SourceGenerator.h>
  8. #include <AK/String.h>
  9. #include <LibCore/ArgsParser.h>
  10. #include <LibMain/Main.h>
  11. namespace {
  12. ErrorOr<void> generate_header_file(JsonObject& roles_data, Core::File& file)
  13. {
  14. StringBuilder builder;
  15. SourceGenerator generator { builder };
  16. generator.append(R"~~~(
  17. #pragma once
  18. #include <LibWeb/ARIA/RoleType.h>
  19. namespace Web::ARIA {
  20. )~~~");
  21. roles_data.for_each_member([&](auto& name, auto& value) -> void {
  22. VERIFY(value.is_object());
  23. JsonObject const& value_object = value.as_object();
  24. auto class_definition_generator = generator.fork();
  25. class_definition_generator.set("spec_link"sv, value_object.get_byte_string("specLink"sv).value());
  26. class_definition_generator.set("description"sv, value_object.get_byte_string("description"sv).value());
  27. class_definition_generator.set("name"sv, name);
  28. class_definition_generator.append(R"~~~(
  29. // @spec_link@
  30. // @description@
  31. class @name@ :
  32. )~~~");
  33. JsonArray const& super_classes = value_object.get_array("superClassRoles"sv).value();
  34. bool first = true;
  35. super_classes.for_each([&](JsonValue const& value) {
  36. VERIFY(value.is_string());
  37. class_definition_generator.append(first ? " "sv : ", "sv);
  38. class_definition_generator.append(MUST(String::formatted("public {}", value.as_string())));
  39. first = false;
  40. });
  41. class_definition_generator.append(R"~~~(
  42. {
  43. public:
  44. @name@(AriaData const&);
  45. virtual HashTable<StateAndProperties> const& supported_states() const override;
  46. virtual HashTable<StateAndProperties> const& supported_properties() const override;
  47. virtual HashTable<StateAndProperties> const& required_states() const override;
  48. virtual HashTable<StateAndProperties> const& required_properties() const override;
  49. virtual HashTable<StateAndProperties> const& prohibited_properties() const override;
  50. virtual HashTable<StateAndProperties> const& prohibited_states() const override;
  51. virtual HashTable<Role> const& required_context_roles() const override;
  52. virtual HashTable<Role> const& required_owned_elements() const override;
  53. virtual bool accessible_name_required() const override;
  54. virtual bool children_are_presentational() const override;
  55. virtual DefaultValueType default_value_for_property_or_state(StateAndProperties) const override;
  56. protected:
  57. @name@();
  58. )~~~");
  59. auto name_from_source = value.as_object().get("nameFromSource"sv).value();
  60. if (!name_from_source.is_null())
  61. class_definition_generator.append(R"~~~(
  62. public:
  63. virtual NameFromSource name_from_source() const override;
  64. )~~~");
  65. class_definition_generator.appendln("};");
  66. });
  67. generator.appendln("}");
  68. TRY(file.write_until_depleted((generator.as_string_view().bytes())));
  69. return {};
  70. }
  71. String generate_hash_table_population(JsonArray const& values, StringView hash_table_name, StringView enum_class)
  72. {
  73. StringBuilder builder;
  74. values.for_each([&](auto& value) {
  75. VERIFY(value.is_string());
  76. builder.appendff(" {}.set({}::{});\n", hash_table_name, enum_class, value.as_string());
  77. });
  78. return MUST(builder.to_string());
  79. }
  80. void generate_hash_table_member(SourceGenerator& generator, StringView member_name, StringView hash_table_name, StringView enum_class, JsonArray const& values)
  81. {
  82. auto member_generator = generator.fork();
  83. member_generator.set("member_name"sv, member_name);
  84. member_generator.set("hash_table_name"sv, hash_table_name);
  85. member_generator.set("enum_class"sv, enum_class);
  86. member_generator.set("hash_table_size"sv, String::number(values.size()));
  87. if (values.size() == 0) {
  88. member_generator.append(R"~~~(
  89. HashTable<@enum_class@> const& @name@::@member_name@() const
  90. {
  91. static HashTable<@enum_class@> @hash_table_name@;
  92. return @hash_table_name@;
  93. }
  94. )~~~");
  95. return;
  96. }
  97. member_generator.append(R"~~~(
  98. HashTable<@enum_class@> const& @name@::@member_name@() const
  99. {
  100. static HashTable<@enum_class@> @hash_table_name@;
  101. if (@hash_table_name@.is_empty()) {
  102. @hash_table_name@.ensure_capacity(@hash_table_size@);
  103. )~~~");
  104. member_generator.append(generate_hash_table_population(values, hash_table_name, enum_class));
  105. member_generator.append(R"~~~(
  106. }
  107. return @hash_table_name@;
  108. }
  109. )~~~");
  110. }
  111. StringView aria_name_to_enum_name(StringView name)
  112. {
  113. if (name == "aria-activedescendant"sv) {
  114. return "AriaActiveDescendant"sv;
  115. } else if (name == "aria-atomic"sv) {
  116. return "AriaAtomic"sv;
  117. } else if (name == "aria-autocomplete"sv) {
  118. return "AriaAutoComplete"sv;
  119. } else if (name == "aria-braillelabel"sv) {
  120. return "AriaBrailleLabel"sv;
  121. } else if (name == "aria-brailleroledescription"sv) {
  122. return "AriaBrailleRoleDescription"sv;
  123. } else if (name == "aria-busy"sv) {
  124. return "AriaBusy"sv;
  125. } else if (name == "aria-checked"sv) {
  126. return "AriaChecked"sv;
  127. } else if (name == "aria-colcount"sv) {
  128. return "AriaColCount"sv;
  129. } else if (name == "aria-colindex"sv) {
  130. return "AriaColIndex"sv;
  131. } else if (name == "aria-colindextext"sv) {
  132. return "AriaColIndexText"sv;
  133. } else if (name == "aria-colspan"sv) {
  134. return "AriaColSpan"sv;
  135. } else if (name == "aria-controls"sv) {
  136. return "AriaControls"sv;
  137. } else if (name == "aria-current"sv) {
  138. return "AriaCurrent"sv;
  139. } else if (name == "aria-describedby"sv) {
  140. return "AriaDescribedBy"sv;
  141. } else if (name == "aria-description"sv) {
  142. return "AriaDescription"sv;
  143. } else if (name == "aria-details"sv) {
  144. return "AriaDetails"sv;
  145. } else if (name == "aria-disabled"sv) {
  146. return "AriaDisabled"sv;
  147. } else if (name == "aria-dropeffect"sv) {
  148. return "AriaDropEffect"sv;
  149. } else if (name == "aria-errormessage"sv) {
  150. return "AriaErrorMessage"sv;
  151. } else if (name == "aria-expanded"sv) {
  152. return "AriaExpanded"sv;
  153. } else if (name == "aria-flowto"sv) {
  154. return "AriaFlowTo"sv;
  155. } else if (name == "aria-grabbed"sv) {
  156. return "AriaGrabbed"sv;
  157. } else if (name == "aria-haspopup"sv) {
  158. return "AriaHasPopup"sv;
  159. } else if (name == "aria-hidden"sv) {
  160. return "AriaHidden"sv;
  161. } else if (name == "aria-invalid"sv) {
  162. return "AriaInvalid"sv;
  163. } else if (name == "aria-keyshortcuts"sv) {
  164. return "AriaKeyShortcuts"sv;
  165. } else if (name == "aria-label"sv) {
  166. return "AriaLabel"sv;
  167. } else if (name == "aria-labelledby"sv) {
  168. return "AriaLabelledBy"sv;
  169. } else if (name == "aria-level"sv) {
  170. return "AriaLevel"sv;
  171. } else if (name == "aria-live"sv) {
  172. return "AriaLive"sv;
  173. } else if (name == "aria-modal"sv) {
  174. return "AriaModal"sv;
  175. } else if (name == "aria-multiline"sv) {
  176. return "AriaMultiLine"sv;
  177. } else if (name == "aria-multiselectable"sv) {
  178. return "AriaMultiSelectable"sv;
  179. } else if (name == "aria-orientation"sv) {
  180. return "AriaOrientation"sv;
  181. } else if (name == "aria-owns"sv) {
  182. return "AriaOwns"sv;
  183. } else if (name == "aria-placeholder"sv) {
  184. return "AriaPlaceholder"sv;
  185. } else if (name == "aria-posinset"sv) {
  186. return "AriaPosInSet"sv;
  187. } else if (name == "aria-pressed"sv) {
  188. return "AriaPressed"sv;
  189. } else if (name == "aria-readonly"sv) {
  190. return "AriaReadOnly"sv;
  191. } else if (name == "aria-relevant"sv) {
  192. return "AriaRelevant"sv;
  193. } else if (name == "aria-required"sv) {
  194. return "AriaRequired"sv;
  195. } else if (name == "aria-roledescription"sv) {
  196. return "AriaRoleDescription"sv;
  197. } else if (name == "aria-rowcount"sv) {
  198. return "AriaRowCount"sv;
  199. } else if (name == "aria-rowindex"sv) {
  200. return "AriaRowIndex"sv;
  201. } else if (name == "aria-rowindextext"sv) {
  202. return "AriaRowIndexText"sv;
  203. } else if (name == "aria-rowspan"sv) {
  204. return "AriaRowSpan"sv;
  205. } else if (name == "aria-selected"sv) {
  206. return "AriaSelected"sv;
  207. } else if (name == "aria-setsize"sv) {
  208. return "AriaSetSize"sv;
  209. } else if (name == "aria-sort"sv) {
  210. return "AriaSort"sv;
  211. } else if (name == "aria-valuemax"sv) {
  212. return "AriaValueMax"sv;
  213. } else if (name == "aria-valuemin"sv) {
  214. return "AriaValueMin"sv;
  215. } else if (name == "aria-valuenow"sv) {
  216. return "AriaValueNow"sv;
  217. } else if (name == "aria-valuetext"sv) {
  218. return "AriaValueText"sv;
  219. } else {
  220. VERIFY_NOT_REACHED();
  221. }
  222. }
  223. JsonArray translate_aria_names_to_enum(JsonArray const& names)
  224. {
  225. JsonArray translated_names;
  226. names.for_each([&](JsonValue const& value) {
  227. VERIFY(value.is_string());
  228. auto name = value.as_string();
  229. MUST(translated_names.append(aria_name_to_enum_name(name)));
  230. });
  231. return translated_names;
  232. }
  233. ErrorOr<void> generate_implementation_file(JsonObject& roles_data, Core::File& file)
  234. {
  235. StringBuilder builder;
  236. SourceGenerator generator { builder };
  237. generator.append(R"~~~(
  238. #include <LibWeb/ARIA/AriaRoles.h>
  239. namespace Web::ARIA {
  240. )~~~");
  241. roles_data.for_each_member([&](auto& name, auto& value) -> void {
  242. VERIFY(value.is_object());
  243. auto member_generator = generator.fork();
  244. member_generator.set("name"sv, name);
  245. JsonObject const& value_object = value.as_object();
  246. JsonArray const& supported_states = translate_aria_names_to_enum(value_object.get_array("supportedStates"sv).value());
  247. generate_hash_table_member(member_generator, "supported_states"sv, "states"sv, "StateAndProperties"sv, supported_states);
  248. JsonArray const& supported_properties = translate_aria_names_to_enum(value_object.get_array("supportedProperties"sv).value());
  249. generate_hash_table_member(member_generator, "supported_properties"sv, "properties"sv, "StateAndProperties"sv, supported_properties);
  250. JsonArray const& required_states = translate_aria_names_to_enum(value_object.get_array("requiredStates"sv).value());
  251. generate_hash_table_member(member_generator, "required_states"sv, "states"sv, "StateAndProperties"sv, required_states);
  252. JsonArray const& required_properties = translate_aria_names_to_enum(value_object.get_array("requiredProperties"sv).value());
  253. generate_hash_table_member(member_generator, "required_properties"sv, "properties"sv, "StateAndProperties"sv, required_properties);
  254. JsonArray const& prohibited_states = translate_aria_names_to_enum(value_object.get_array("prohibitedStates"sv).value());
  255. generate_hash_table_member(member_generator, "prohibited_states"sv, "states"sv, "StateAndProperties"sv, prohibited_states);
  256. JsonArray const& prohibited_properties = translate_aria_names_to_enum(value_object.get_array("prohibitedProperties"sv).value());
  257. generate_hash_table_member(member_generator, "prohibited_properties"sv, "properties"sv, "StateAndProperties"sv, prohibited_properties);
  258. JsonArray const& required_context_roles = value_object.get_array("requiredContextRoles"sv).value();
  259. generate_hash_table_member(member_generator, "required_context_roles"sv, "roles"sv, "Role"sv, required_context_roles);
  260. JsonArray const& required_owned_elements = value_object.get_array("requiredOwnedElements"sv).value();
  261. generate_hash_table_member(member_generator, "required_owned_elements"sv, "roles"sv, "Role"sv, required_owned_elements);
  262. bool accessible_name_required = value_object.get_bool("accessibleNameRequired"sv).value();
  263. member_generator.set("accessible_name_required"sv, accessible_name_required ? "true"sv : "false"sv);
  264. bool children_are_presentational = value_object.get_bool("childrenArePresentational"sv).value();
  265. member_generator.set("children_are_presentational", children_are_presentational ? "true"sv : "false"sv);
  266. JsonArray const& super_classes = value.as_object().get_array("superClassRoles"sv).value();
  267. member_generator.set("parent", super_classes.at(0).as_string());
  268. member_generator.append(R"~~~(
  269. @name@::@name@() { }
  270. @name@::@name@(AriaData const& data)
  271. : @parent@(data)
  272. {
  273. }
  274. bool @name@::accessible_name_required() const
  275. {
  276. return @accessible_name_required@;
  277. }
  278. bool @name@::children_are_presentational() const
  279. {
  280. return @children_are_presentational@;
  281. }
  282. )~~~");
  283. JsonObject const& implicit_value_for_role = value_object.get_object("implicitValueForRole"sv).value();
  284. if (implicit_value_for_role.size() == 0) {
  285. member_generator.append(R"~~~(
  286. DefaultValueType @name@::default_value_for_property_or_state(StateAndProperties) const
  287. {
  288. return {};
  289. }
  290. )~~~");
  291. } else {
  292. member_generator.append(R"~~~(
  293. DefaultValueType @name@::default_value_for_property_or_state(StateAndProperties state_or_property) const
  294. {
  295. switch (state_or_property) {
  296. )~~~");
  297. implicit_value_for_role.for_each_member([&](auto& name, auto& value) {
  298. auto case_generator = member_generator.fork();
  299. VERIFY(value.is_string());
  300. case_generator.set("state_or_property"sv, aria_name_to_enum_name(name));
  301. case_generator.set("implicit_value"sv, value.as_string());
  302. case_generator.append(R"~~~(
  303. case StateAndProperties::@state_or_property@:
  304. return @implicit_value@;
  305. )~~~");
  306. });
  307. member_generator.append(R"~~~(
  308. default:
  309. return {};
  310. }
  311. }
  312. )~~~");
  313. }
  314. JsonValue const& name_from_source = value.as_object().get("nameFromSource"sv).value();
  315. if (!name_from_source.is_null()) {
  316. member_generator.set("name_from_source"sv, name_from_source.as_string());
  317. member_generator.append(R"~~~(
  318. NameFromSource @name@::name_from_source() const
  319. {
  320. return NameFromSource::@name_from_source@;
  321. }
  322. )~~~");
  323. }
  324. });
  325. generator.append("}");
  326. TRY(file.write_until_depleted(generator.as_string_view().bytes()));
  327. return {};
  328. }
  329. } // end anonymous namespace
  330. ErrorOr<int> serenity_main(Main::Arguments arguments)
  331. {
  332. StringView generated_header_path;
  333. StringView generated_implementation_path;
  334. StringView json_path;
  335. Core::ArgsParser args_parser;
  336. args_parser.add_option(generated_header_path, "Path to the TransformFunctions header file to generate", "generated-header-path", 'h', "generated-header-path");
  337. args_parser.add_option(generated_implementation_path, "Path to the TransformFunctions implementation file to generate", "generated-implementation-path", 'c', "generated-implementation-path");
  338. args_parser.add_option(json_path, "Path to the JSON file to read from", "json-path", 'j', "json-path");
  339. args_parser.parse(arguments);
  340. auto json = TRY(read_entire_file_as_json(json_path));
  341. VERIFY(json.is_object());
  342. auto roles_data = json.as_object();
  343. auto generated_header_file = TRY(Core::File::open(generated_header_path, Core::File::OpenMode::Write));
  344. auto generated_implementation_file = TRY(Core::File::open(generated_implementation_path, Core::File::OpenMode::Write));
  345. TRY(generate_header_file(roles_data, *generated_header_file));
  346. TRY(generate_implementation_file(roles_data, *generated_implementation_file));
  347. return 0;
  348. }