From 0b08201fec4f0649cd8de9ed566e3e7dfe2bc603 Mon Sep 17 00:00:00 2001 From: Timothy Flynn Date: Sat, 11 Sep 2021 10:57:09 -0400 Subject: LibJS: Move Intl.ListFormat's AOs to its object file To be consistent with the style in Temporal, let's move all AOs in Intl to their object file, rather than splitting the AOs between prototype and constructor files. --- .../Libraries/LibJS/Runtime/Intl/ListFormat.cpp | 251 ++++++++++++++++++++ Userland/Libraries/LibJS/Runtime/Intl/ListFormat.h | 12 + .../LibJS/Runtime/Intl/ListFormatPrototype.cpp | 255 --------------------- 3 files changed, 263 insertions(+), 255 deletions(-) (limited to 'Userland') diff --git a/Userland/Libraries/LibJS/Runtime/Intl/ListFormat.cpp b/Userland/Libraries/LibJS/Runtime/Intl/ListFormat.cpp index b03d74fa32..0ae0de1d88 100644 --- a/Userland/Libraries/LibJS/Runtime/Intl/ListFormat.cpp +++ b/Userland/Libraries/LibJS/Runtime/Intl/ListFormat.cpp @@ -4,7 +4,12 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include +#include +#include #include +#include +#include namespace JS::Intl { @@ -68,4 +73,250 @@ StringView ListFormat::style_string() const } } +// 13.1.1 DeconstructPattern ( pattern, placeables ), https://tc39.es/ecma402/#sec-deconstructpattern +Vector deconstruct_pattern(StringView pattern, Placeables placeables) +{ + // 1. Let patternParts be PartitionPattern(pattern). + auto pattern_parts = partition_pattern(pattern); + + // 2. Let result be a new empty List. + Vector result {}; + + // 3. For each Record { [[Type]], [[Value]] } patternPart of patternParts, do + for (auto const& pattern_part : pattern_parts) { + // a. Let part be patternPart.[[Type]]. + auto part = pattern_part.type; + + // b. If part is "literal", then + if (part == "literal"sv) { + // i. Append Record { [[Type]]: "literal", [[Value]]: patternPart.[[Value]] } to result. + result.append({ part, pattern_part.value }); + } + // c. Else, + else { + // i. Assert: placeables has a field [[]]. + // ii. Let subst be placeables.[[]]. + auto subst = placeables.get(part); + VERIFY(subst.has_value()); + + subst.release_value().visit( + // iii. If Type(subst) is List, then + [&](Vector& partition) { + // 1. For each element s of subst, do + for (auto& element : partition) { + // a. Append s to result. + result.append(move(element)); + } + }, + // iv. Else, + [&](PatternPartition& partition) { + // 1. Append subst to result. + result.append(move(partition)); + }); + } + } + + // 4. Return result. + return result; +} + +// 13.1.2 CreatePartsFromList ( listFormat, list ), https://tc39.es/ecma402/#sec-createpartsfromlist +Vector create_parts_from_list(ListFormat const& list_format, Vector const& list) +{ + auto list_patterns = Unicode::get_locale_list_patterns(list_format.locale(), list_format.type_string(), list_format.style_string()); + if (!list_patterns.has_value()) + return {}; + + // 1. Let size be the number of elements of list. + auto size = list.size(); + + // 2. If size is 0, then + if (size == 0) { + // a. Return a new empty List. + return {}; + } + + // 3. If size is 2, then + if (size == 2) { + // a. Let n be an index into listFormat.[[Templates]] based on listFormat.[[Locale]], list[0], and list[1]. + // b. Let pattern be listFormat.[[Templates]][n].[[Pair]]. + auto pattern = list_patterns->pair; + + // c. Let first be a new Record { [[Type]]: "element", [[Value]]: list[0] }. + PatternPartition first { "element"sv, list[0] }; + + // d. Let second be a new Record { [[Type]]: "element", [[Value]]: list[1] }. + PatternPartition second { "element"sv, list[1] }; + + // e. Let placeables be a new Record { [[0]]: first, [[1]]: second }. + Placeables placeables; + placeables.set("0"sv, move(first)); + placeables.set("1"sv, move(second)); + + // f. Return DeconstructPattern(pattern, placeables). + return deconstruct_pattern(pattern, move(placeables)); + } + + // 4. Let last be a new Record { [[Type]]: "element", [[Value]]: list[size - 1] }. + PatternPartition last { "element"sv, list[size - 1] }; + + // 5. Let parts be « last ». + Vector parts { move(last) }; + + // The spec does not say to do this, but because size_t is unsigned, we need to take care not to wrap around 0. + if (size == 1) + return parts; + + // 6. Let i be size - 2. + size_t i = size - 2; + + // 7. Repeat, while i ≥ 0, + do { + // a. Let head be a new Record { [[Type]]: "element", [[Value]]: list[i] }. + PatternPartition head { "element"sv, list[i] }; + + // b. Let n be an implementation-defined index into listFormat.[[Templates]] based on listFormat.[[Locale]], head, and parts. + StringView pattern; + + // c. If i is 0, then + if (i == 0) { + // i. Let pattern be listFormat.[[Templates]][n].[[Start]]. + pattern = list_patterns->start; + } + // d. Else if i is less than size - 2, then + else if (i < (size - 2)) { + // i. Let pattern be listFormat.[[Templates]][n].[[Middle]]. + pattern = list_patterns->middle; + } + // e. Else, + else { + // i. Let pattern be listFormat.[[Templates]][n].[[End]]. + pattern = list_patterns->end; + } + + // f. Let placeables be a new Record { [[0]]: head, [[1]]: parts }. + Placeables placeables; + placeables.set("0"sv, move(head)); + placeables.set("1"sv, move(parts)); + + // g. Set parts to DeconstructPattern(pattern, placeables). + parts = deconstruct_pattern(pattern, move(placeables)); + + // h. Decrement i by 1. + } while (i-- != 0); + + // 8. Return parts. + return parts; +} + +// 13.1.3 FormatList ( listFormat, list ) +String format_list(ListFormat const& list_format, Vector const& list) +{ + // 1. Let parts be CreatePartsFromList(listFormat, list). + auto parts = create_parts_from_list(list_format, list); + + // 2. Let result be an empty String. + StringBuilder result; + + // 3. For each Record { [[Type]], [[Value]] } part in parts, do + for (auto const& part : parts) { + // a. Set result to the string-concatenation of result and part.[[Value]]. + result.append(part.value); + } + + // 4. Return result. + return result.build(); +} + +// 13.1.4 FormatListToParts ( listFormat, list ), https://tc39.es/ecma402/#sec-formatlisttoparts +Array* format_list_to_parts(GlobalObject& global_object, ListFormat const& list_format, Vector const& list) +{ + auto& vm = global_object.vm(); + + // 1. Let parts be CreatePartsFromList(listFormat, list). + auto parts = create_parts_from_list(list_format, list); + + // 2. Let result be ArrayCreate(0). + auto result = Array::create(global_object, 0); + + // 3. Let n be 0. + size_t n = 0; + + // 4. For each Record { [[Type]], [[Value]] } part in parts, do + for (auto const& part : parts) { + // a. Let O be OrdinaryObjectCreate(%Object.prototype%). + auto* object = Object::create(global_object, global_object.object_prototype()); + + // b. Perform ! CreateDataPropertyOrThrow(O, "type", part.[[Type]]). + object->create_data_property_or_throw(vm.names.type, js_string(vm, part.type)); + + // c. Perform ! CreateDataPropertyOrThrow(O, "value", part.[[Value]]). + object->create_data_property_or_throw(vm.names.value, js_string(vm, part.value)); + + // d. Perform ! CreateDataPropertyOrThrow(result, ! ToString(n), O). + result->create_data_property_or_throw(n, object); + + // e. Increment n by 1. + ++n; + } + + // 5. Return result. + return result; +} + +// 13.1.5 StringListFromIterable ( iterable ), https://tc39.es/ecma402/#sec-createstringlistfromiterable +Vector string_list_from_iterable(GlobalObject& global_object, Value iterable) +{ + auto& vm = global_object.vm(); + + // 1. If iterable is undefined, then + if (iterable.is_undefined()) { + // a. Return a new empty List. + return {}; + } + + // 2. Let iteratorRecord be ? GetIterator(iterable). + auto* iterator_record = get_iterator(global_object, iterable); + if (vm.exception()) + return {}; + + // 3. Let list be a new empty List. + Vector list; + + // 4. Let next be true. + Object* next = nullptr; + + // 5. Repeat, while next is not false, + do { + // a. Set next to ? IteratorStep(iteratorRecord). + next = iterator_step(global_object, *iterator_record); + if (vm.exception()) + return {}; + + // b. If next is not false, then + if (next != nullptr) { + // i. Let nextValue be ? IteratorValue(next). + auto next_value = iterator_value(global_object, *next); + if (vm.exception()) + return {}; + + // ii. If Type(nextValue) is not String, then + if (!next_value.is_string()) { + // 1. Let error be ThrowCompletion(a newly created TypeError object). + vm.throw_exception(global_object, ErrorType::NotAString, next_value); + + // 2. Return ? IteratorClose(iteratorRecord, error). + iterator_close(*iterator_record); + return {}; + } + + // iii. Append nextValue to the end of the List list. + list.append(next_value.as_string().string()); + } + } while (next != nullptr); + + // 6. Return list. + return list; +} + } diff --git a/Userland/Libraries/LibJS/Runtime/Intl/ListFormat.h b/Userland/Libraries/LibJS/Runtime/Intl/ListFormat.h index 4224ba49ec..64a2654672 100644 --- a/Userland/Libraries/LibJS/Runtime/Intl/ListFormat.h +++ b/Userland/Libraries/LibJS/Runtime/Intl/ListFormat.h @@ -6,8 +6,12 @@ #pragma once +#include #include #include +#include +#include +#include #include namespace JS::Intl { @@ -50,4 +54,12 @@ private: Style m_style { Style::Invalid }; // [[Style]] }; +using Placeables = HashMap>>; + +Vector deconstruct_pattern(StringView pattern, Placeables placeables); +Vector create_parts_from_list(ListFormat const& list_format, Vector const& list); +String format_list(ListFormat const& list_format, Vector const& list); +Array* format_list_to_parts(GlobalObject& global_object, ListFormat const& list_format, Vector const& list); +Vector string_list_from_iterable(GlobalObject& global_object, Value iterable); + } diff --git a/Userland/Libraries/LibJS/Runtime/Intl/ListFormatPrototype.cpp b/Userland/Libraries/LibJS/Runtime/Intl/ListFormatPrototype.cpp index fad3eedd89..bf209ac624 100644 --- a/Userland/Libraries/LibJS/Runtime/Intl/ListFormatPrototype.cpp +++ b/Userland/Libraries/LibJS/Runtime/Intl/ListFormatPrototype.cpp @@ -4,18 +4,11 @@ * SPDX-License-Identifier: BSD-2-Clause */ -#include -#include #include -#include -#include #include #include -#include #include #include -#include -#include namespace JS::Intl { @@ -35,254 +28,6 @@ static ListFormat* typed_this(GlobalObject& global_object) return static_cast(this_object); } -using Placeables = HashMap>>; - -// 13.1.1 DeconstructPattern ( pattern, placeables ), https://tc39.es/ecma402/#sec-deconstructpattern -static Vector deconstruct_pattern(StringView pattern, Placeables placeables) -{ - // 1. Let patternParts be PartitionPattern(pattern). - auto pattern_parts = partition_pattern(pattern); - - // 2. Let result be a new empty List. - Vector result {}; - - // 3. For each Record { [[Type]], [[Value]] } patternPart of patternParts, do - for (auto const& pattern_part : pattern_parts) { - // a. Let part be patternPart.[[Type]]. - auto part = pattern_part.type; - - // b. If part is "literal", then - if (part == "literal"sv) { - // i. Append Record { [[Type]]: "literal", [[Value]]: patternPart.[[Value]] } to result. - result.append({ part, pattern_part.value }); - } - // c. Else, - else { - // i. Assert: placeables has a field [[]]. - // ii. Let subst be placeables.[[]]. - auto subst = placeables.get(part); - VERIFY(subst.has_value()); - - subst.release_value().visit( - // iii. If Type(subst) is List, then - [&](Vector& partition) { - // 1. For each element s of subst, do - for (auto& element : partition) { - // a. Append s to result. - result.append(move(element)); - } - }, - // iv. Else, - [&](PatternPartition& partition) { - // 1. Append subst to result. - result.append(move(partition)); - }); - } - } - - // 4. Return result. - return result; -} - -// 13.1.2 CreatePartsFromList ( listFormat, list ), https://tc39.es/ecma402/#sec-createpartsfromlist -static Vector create_parts_from_list(ListFormat const& list_format, Vector const& list) -{ - auto list_patterns = Unicode::get_locale_list_patterns(list_format.locale(), list_format.type_string(), list_format.style_string()); - if (!list_patterns.has_value()) - return {}; - - // 1. Let size be the number of elements of list. - auto size = list.size(); - - // 2. If size is 0, then - if (size == 0) { - // a. Return a new empty List. - return {}; - } - - // 3. If size is 2, then - if (size == 2) { - // a. Let n be an index into listFormat.[[Templates]] based on listFormat.[[Locale]], list[0], and list[1]. - // b. Let pattern be listFormat.[[Templates]][n].[[Pair]]. - auto pattern = list_patterns->pair; - - // c. Let first be a new Record { [[Type]]: "element", [[Value]]: list[0] }. - PatternPartition first { "element"sv, list[0] }; - - // d. Let second be a new Record { [[Type]]: "element", [[Value]]: list[1] }. - PatternPartition second { "element"sv, list[1] }; - - // e. Let placeables be a new Record { [[0]]: first, [[1]]: second }. - Placeables placeables; - placeables.set("0"sv, move(first)); - placeables.set("1"sv, move(second)); - - // f. Return DeconstructPattern(pattern, placeables). - return deconstruct_pattern(pattern, move(placeables)); - } - - // 4. Let last be a new Record { [[Type]]: "element", [[Value]]: list[size - 1] }. - PatternPartition last { "element"sv, list[size - 1] }; - - // 5. Let parts be « last ». - Vector parts { move(last) }; - - // The spec does not say to do this, but because size_t is unsigned, we need to take care not to wrap around 0. - if (size == 1) - return parts; - - // 6. Let i be size - 2. - size_t i = size - 2; - - // 7. Repeat, while i ≥ 0, - do { - // a. Let head be a new Record { [[Type]]: "element", [[Value]]: list[i] }. - PatternPartition head { "element"sv, list[i] }; - - // b. Let n be an implementation-defined index into listFormat.[[Templates]] based on listFormat.[[Locale]], head, and parts. - StringView pattern; - - // c. If i is 0, then - if (i == 0) { - // i. Let pattern be listFormat.[[Templates]][n].[[Start]]. - pattern = list_patterns->start; - } - // d. Else if i is less than size - 2, then - else if (i < (size - 2)) { - // i. Let pattern be listFormat.[[Templates]][n].[[Middle]]. - pattern = list_patterns->middle; - } - // e. Else, - else { - // i. Let pattern be listFormat.[[Templates]][n].[[End]]. - pattern = list_patterns->end; - } - - // f. Let placeables be a new Record { [[0]]: head, [[1]]: parts }. - Placeables placeables; - placeables.set("0"sv, move(head)); - placeables.set("1"sv, move(parts)); - - // g. Set parts to DeconstructPattern(pattern, placeables). - parts = deconstruct_pattern(pattern, move(placeables)); - - // h. Decrement i by 1. - } while (i-- != 0); - - // 8. Return parts. - return parts; -} - -// 13.1.3 FormatList ( listFormat, list ) -static String format_list(ListFormat const& list_format, Vector const& list) -{ - // 1. Let parts be CreatePartsFromList(listFormat, list). - auto parts = create_parts_from_list(list_format, list); - - // 2. Let result be an empty String. - StringBuilder result; - - // 3. For each Record { [[Type]], [[Value]] } part in parts, do - for (auto const& part : parts) { - // a. Set result to the string-concatenation of result and part.[[Value]]. - result.append(part.value); - } - - // 4. Return result. - return result.build(); -} - -// 13.1.4 FormatListToParts ( listFormat, list ), https://tc39.es/ecma402/#sec-formatlisttoparts -static Array* format_list_to_parts(GlobalObject& global_object, ListFormat const& list_format, Vector const& list) -{ - auto& vm = global_object.vm(); - - // 1. Let parts be CreatePartsFromList(listFormat, list). - auto parts = create_parts_from_list(list_format, list); - - // 2. Let result be ArrayCreate(0). - auto result = Array::create(global_object, 0); - - // 3. Let n be 0. - size_t n = 0; - - // 4. For each Record { [[Type]], [[Value]] } part in parts, do - for (auto const& part : parts) { - // a. Let O be OrdinaryObjectCreate(%Object.prototype%). - auto* object = Object::create(global_object, global_object.object_prototype()); - - // b. Perform ! CreateDataPropertyOrThrow(O, "type", part.[[Type]]). - object->create_data_property_or_throw(vm.names.type, js_string(vm, part.type)); - - // c. Perform ! CreateDataPropertyOrThrow(O, "value", part.[[Value]]). - object->create_data_property_or_throw(vm.names.value, js_string(vm, part.value)); - - // d. Perform ! CreateDataPropertyOrThrow(result, ! ToString(n), O). - result->create_data_property_or_throw(n, object); - - // e. Increment n by 1. - ++n; - } - - // 5. Return result. - return result; -} - -// 13.1.5 StringListFromIterable ( iterable ), https://tc39.es/ecma402/#sec-createstringlistfromiterable -static Vector string_list_from_iterable(GlobalObject& global_object, Value iterable) -{ - auto& vm = global_object.vm(); - - // 1. If iterable is undefined, then - if (iterable.is_undefined()) { - // a. Return a new empty List. - return {}; - } - - // 2. Let iteratorRecord be ? GetIterator(iterable). - auto* iterator_record = get_iterator(global_object, iterable); - if (vm.exception()) - return {}; - - // 3. Let list be a new empty List. - Vector list; - - // 4. Let next be true. - Object* next = nullptr; - - // 5. Repeat, while next is not false, - do { - // a. Set next to ? IteratorStep(iteratorRecord). - next = iterator_step(global_object, *iterator_record); - if (vm.exception()) - return {}; - - // b. If next is not false, then - if (next != nullptr) { - // i. Let nextValue be ? IteratorValue(next). - auto next_value = iterator_value(global_object, *next); - if (vm.exception()) - return {}; - - // ii. If Type(nextValue) is not String, then - if (!next_value.is_string()) { - // 1. Let error be ThrowCompletion(a newly created TypeError object). - vm.throw_exception(global_object, ErrorType::NotAString, next_value); - - // 2. Return ? IteratorClose(iteratorRecord, error). - iterator_close(*iterator_record); - return {}; - } - - // iii. Append nextValue to the end of the List list. - list.append(next_value.as_string().string()); - } - } while (next != nullptr); - - // 6. Return list. - return list; -} - // 13.4 Properties of the Intl.ListFormat Prototype Object, https://tc39.es/ecma402/#sec-properties-of-intl-listformat-prototype-object ListFormatPrototype::ListFormatPrototype(GlobalObject& global_object) : Object(*global_object.object_prototype()) -- cgit v1.2.3