GenerateUnicodeData.cpp 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. /*
  2. * Copyright (c) 2021, Tim Flynn <trflynn89@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include "GeneratorUtil.h"
  7. #include <AK/AllOf.h>
  8. #include <AK/Array.h>
  9. #include <AK/ByteString.h>
  10. #include <AK/CharacterTypes.h>
  11. #include <AK/Error.h>
  12. #include <AK/Find.h>
  13. #include <AK/HashMap.h>
  14. #include <AK/Optional.h>
  15. #include <AK/QuickSort.h>
  16. #include <AK/SourceGenerator.h>
  17. #include <AK/StringUtils.h>
  18. #include <AK/Types.h>
  19. #include <AK/Vector.h>
  20. #include <LibCore/ArgsParser.h>
  21. #include <LibUnicode/CharacterTypes.h>
  22. // https://www.unicode.org/reports/tr44/#PropList.txt
  23. using PropList = HashMap<ByteString, Vector<Unicode::CodePointRange>>;
  24. // https://www.unicode.org/reports/tr44/#UnicodeData.txt
  25. struct CodePointData {
  26. u32 code_point { 0 };
  27. ByteString name;
  28. ByteString bidi_class;
  29. Optional<i8> numeric_value_decimal;
  30. Optional<i8> numeric_value_digit;
  31. Optional<i8> numeric_value_numeric;
  32. bool bidi_mirrored { false };
  33. ByteString unicode_1_name;
  34. ByteString iso_comment;
  35. };
  36. struct CodePointBidiClass {
  37. Unicode::CodePointRange code_point_range;
  38. ByteString bidi_class;
  39. };
  40. struct UnicodeData {
  41. Vector<CodePointData> code_point_data;
  42. HashTable<ByteString> bidirectional_classes;
  43. Vector<CodePointBidiClass> code_point_bidirectional_classes;
  44. };
  45. static ErrorOr<void> parse_unicode_data(Core::InputBufferedFile& file, UnicodeData& unicode_data)
  46. {
  47. Optional<u32> code_point_range_start;
  48. Array<u8, 1024> buffer;
  49. while (TRY(file.can_read_line())) {
  50. auto line = TRY(file.read_line(buffer));
  51. if (line.is_empty())
  52. continue;
  53. auto segments = line.split_view(';', SplitBehavior::KeepEmpty);
  54. VERIFY(segments.size() == 15);
  55. CodePointData data {};
  56. data.code_point = AK::StringUtils::convert_to_uint_from_hex<u32>(segments[0]).value();
  57. data.name = segments[1];
  58. data.bidi_class = segments[4];
  59. data.numeric_value_decimal = AK::StringUtils::convert_to_int<i8>(segments[6]);
  60. data.numeric_value_digit = AK::StringUtils::convert_to_int<i8>(segments[7]);
  61. data.numeric_value_numeric = AK::StringUtils::convert_to_int<i8>(segments[8]);
  62. data.bidi_mirrored = segments[9] == "Y"sv;
  63. data.unicode_1_name = segments[10];
  64. data.iso_comment = segments[11];
  65. if (data.name.starts_with("<"sv) && data.name.ends_with(", First>"sv)) {
  66. VERIFY(!code_point_range_start.has_value());
  67. code_point_range_start = data.code_point;
  68. data.name = data.name.substring(1, data.name.length() - 9);
  69. } else if (data.name.starts_with("<"sv) && data.name.ends_with(", Last>"sv)) {
  70. VERIFY(code_point_range_start.has_value());
  71. Unicode::CodePointRange code_point_range { *code_point_range_start, data.code_point };
  72. data.name = data.name.substring(1, data.name.length() - 8);
  73. code_point_range_start.clear();
  74. unicode_data.code_point_bidirectional_classes.append({ code_point_range, data.bidi_class });
  75. } else {
  76. unicode_data.code_point_bidirectional_classes.append({ { data.code_point, data.code_point }, data.bidi_class });
  77. }
  78. unicode_data.bidirectional_classes.set(data.bidi_class, AK::HashSetExistingEntryBehavior::Keep);
  79. unicode_data.code_point_data.append(move(data));
  80. }
  81. return {};
  82. }
  83. static ErrorOr<void> generate_unicode_data_header(Core::InputBufferedFile& file, UnicodeData& unicode_data)
  84. {
  85. StringBuilder builder;
  86. SourceGenerator generator { builder };
  87. auto generate_enum = [&](StringView name, StringView default_, auto values, Vector<Alias> aliases = {}) {
  88. quick_sort(values);
  89. quick_sort(aliases, [](auto& alias1, auto& alias2) { return alias1.alias < alias2.alias; });
  90. generator.set("name", name);
  91. generator.set("underlying", ByteString::formatted("{}UnderlyingType", name));
  92. generator.set("type", ((values.size() + !default_.is_empty()) < 256) ? "u8"sv : "u16"sv);
  93. generator.append(R"~~~(
  94. using @underlying@ = @type@;
  95. enum class @name@ : @underlying@ {)~~~");
  96. if (!default_.is_empty()) {
  97. generator.set("default", default_);
  98. generator.append(R"~~~(
  99. @default@,)~~~");
  100. }
  101. for (auto const& value : values) {
  102. generator.set("value", value);
  103. generator.append(R"~~~(
  104. @value@,)~~~");
  105. }
  106. for (auto const& alias : aliases) {
  107. generator.set("alias", alias.alias);
  108. generator.set("value", alias.name);
  109. generator.append(R"~~~(
  110. @alias@ = @value@,)~~~");
  111. }
  112. generator.append(R"~~~(
  113. };
  114. )~~~");
  115. };
  116. generator.append(R"~~~(
  117. #pragma once
  118. #include <AK/Types.h>
  119. #include <LibUnicode/Forward.h>
  120. namespace Unicode {
  121. )~~~");
  122. generate_enum("BidirectionalClass"sv, {}, unicode_data.bidirectional_classes.values());
  123. generator.append(R"~~~(
  124. }
  125. )~~~");
  126. TRY(file.write_until_depleted(generator.as_string_view().bytes()));
  127. return {};
  128. }
  129. static ErrorOr<void> generate_unicode_data_implementation(Core::InputBufferedFile& file, UnicodeData const& unicode_data)
  130. {
  131. StringBuilder builder;
  132. SourceGenerator generator { builder };
  133. generator.append(R"~~~(
  134. #include <AK/Array.h>
  135. #include <AK/BinarySearch.h>
  136. #include <AK/CharacterTypes.h>
  137. #include <AK/Optional.h>
  138. #include <AK/Span.h>
  139. #include <AK/ByteString.h>
  140. #include <AK/StringView.h>
  141. #include <LibUnicode/CharacterTypes.h>
  142. #include <LibUnicode/UnicodeData.h>
  143. namespace Unicode {
  144. )~~~");
  145. generator.append(R"~~~(
  146. struct BidiClassData {
  147. CodePointRange code_point_range {};
  148. BidirectionalClass bidi_class {};
  149. };
  150. struct CodePointBidiClassComparator : public CodePointRangeComparator {
  151. constexpr int operator()(u32 code_point, BidiClassData const& bidi_class)
  152. {
  153. return CodePointRangeComparator::operator()(code_point, bidi_class.code_point_range);
  154. }
  155. };
  156. )~~~");
  157. {
  158. constexpr size_t max_bidi_classes_per_row = 20;
  159. size_t bidi_classes_in_current_row = 0;
  160. generator.set("size"sv, ByteString::number(unicode_data.code_point_bidirectional_classes.size()));
  161. generator.append(R"~~~(
  162. static constexpr Array<BidiClassData, @size@> s_bidirectional_classes { {
  163. )~~~");
  164. for (auto const& data : unicode_data.code_point_bidirectional_classes) {
  165. if (bidi_classes_in_current_row++ > 0)
  166. generator.append(", ");
  167. generator.set("first", ByteString::formatted("{:#x}", data.code_point_range.first));
  168. generator.set("last", ByteString::formatted("{:#x}", data.code_point_range.last));
  169. generator.set("bidi_class", data.bidi_class);
  170. generator.append("{ { @first@, @last@ }, BidirectionalClass::@bidi_class@ }");
  171. if (bidi_classes_in_current_row == max_bidi_classes_per_row) {
  172. bidi_classes_in_current_row = 0;
  173. generator.append(",\n ");
  174. }
  175. }
  176. generator.append(R"~~~(
  177. } };
  178. )~~~");
  179. }
  180. generator.append(R"~~~(
  181. Optional<BidirectionalClass> bidirectional_class(u32 code_point)
  182. {
  183. if (auto const* entry = binary_search(s_bidirectional_classes, code_point, nullptr, CodePointBidiClassComparator {}))
  184. return entry->bidi_class;
  185. return {};
  186. }
  187. )~~~");
  188. auto append_from_string = [&](StringView enum_title, StringView enum_snake, auto const& prop_list, Vector<Alias> const& aliases) -> ErrorOr<void> {
  189. HashValueMap<StringView> hashes;
  190. TRY(hashes.try_ensure_capacity(prop_list.size() + aliases.size()));
  191. ValueFromStringOptions options {};
  192. for (auto const& prop : prop_list) {
  193. if constexpr (IsSame<RemoveCVReference<decltype(prop)>, ByteString>) {
  194. hashes.set(CaseInsensitiveASCIIStringViewTraits::hash(prop), prop);
  195. options.sensitivity = CaseSensitivity::CaseInsensitive;
  196. } else {
  197. hashes.set(prop.key.hash(), prop.key);
  198. }
  199. }
  200. for (auto const& alias : aliases)
  201. hashes.set(alias.alias.hash(), alias.alias);
  202. generate_value_from_string(generator, "{}_from_string"sv, enum_title, enum_snake, move(hashes), options);
  203. return {};
  204. };
  205. TRY(append_from_string("BidirectionalClass"sv, "bidirectional_class"sv, unicode_data.bidirectional_classes, {}));
  206. generator.append(R"~~~(
  207. }
  208. )~~~");
  209. TRY(file.write_until_depleted(generator.as_string_view().bytes()));
  210. return {};
  211. }
  212. ErrorOr<int> serenity_main(Main::Arguments arguments)
  213. {
  214. StringView generated_header_path;
  215. StringView generated_implementation_path;
  216. StringView unicode_data_path;
  217. Core::ArgsParser args_parser;
  218. args_parser.add_option(generated_header_path, "Path to the Unicode Data header file to generate", "generated-header-path", 'h', "generated-header-path");
  219. args_parser.add_option(generated_implementation_path, "Path to the Unicode Data implementation file to generate", "generated-implementation-path", 'c', "generated-implementation-path");
  220. args_parser.add_option(unicode_data_path, "Path to UnicodeData.txt file", "unicode-data-path", 'u', "unicode-data-path");
  221. args_parser.parse(arguments);
  222. auto generated_header_file = TRY(open_file(generated_header_path, Core::File::OpenMode::Write));
  223. auto generated_implementation_file = TRY(open_file(generated_implementation_path, Core::File::OpenMode::Write));
  224. auto unicode_data_file = TRY(open_file(unicode_data_path, Core::File::OpenMode::Read));
  225. UnicodeData unicode_data {};
  226. TRY(parse_unicode_data(*unicode_data_file, unicode_data));
  227. TRY(generate_unicode_data_header(*generated_header_file, unicode_data));
  228. TRY(generate_unicode_data_implementation(*generated_implementation_file, unicode_data));
  229. return 0;
  230. }