PluralRules.cpp 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. /*
  2. * Copyright (c) 2022, Tim Flynn <trflynn89@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/Variant.h>
  7. #include <LibJS/Runtime/Intl/PluralRules.h>
  8. #include <math.h>
  9. #include <stdlib.h>
  10. namespace JS::Intl {
  11. // 16 PluralRules Objects, https://tc39.es/ecma402/#pluralrules-objects
  12. PluralRules::PluralRules(Object& prototype)
  13. : NumberFormatBase(prototype)
  14. {
  15. }
  16. // 16.5.1 GetOperands ( s ), https://tc39.es/ecma402/#sec-getoperands
  17. Unicode::PluralOperands get_operands(String const& string)
  18. {
  19. // 1.Let n be ! ToNumber(s).
  20. char* end { nullptr };
  21. auto number = strtod(string.characters(), &end);
  22. VERIFY(!*end);
  23. // 2. Assert: n is finite.
  24. VERIFY(isfinite(number));
  25. // 3. Let dp be StringIndexOf(s, ".", 0).
  26. auto decimal_point = string.find('.');
  27. Variant<Empty, double, StringView> integer_part;
  28. StringView fraction_slice;
  29. // 4. If dp = -1, then
  30. if (!decimal_point.has_value()) {
  31. // a. Let intPart be n.
  32. integer_part = number;
  33. // b. Let fracSlice be "".
  34. }
  35. // 5. Else,
  36. else {
  37. // a. Let intPart be the substring of s from 0 to dp.
  38. integer_part = string.substring_view(0, *decimal_point);
  39. // b. Let fracSlice be the substring of s from dp + 1.
  40. fraction_slice = string.substring_view(*decimal_point + 1);
  41. }
  42. // 6. Let i be abs(! ToNumber(intPart)).
  43. auto integer = integer_part.visit(
  44. [](Empty) -> u64 { VERIFY_NOT_REACHED(); },
  45. [](double value) {
  46. return static_cast<u64>(fabs(value));
  47. },
  48. [](StringView value) {
  49. auto value_as_int = value.template to_int<i64>().value();
  50. return static_cast<u64>(value_as_int);
  51. });
  52. // 7. Let fracDigitCount be the length of fracSlice.
  53. auto fraction_digit_count = fraction_slice.length();
  54. // 8. Let f be ! ToNumber(fracSlice).
  55. auto fraction = fraction_slice.is_empty() ? 0u : fraction_slice.template to_uint<u64>().value();
  56. // 9. Let significantFracSlice be the value of fracSlice stripped of trailing "0".
  57. auto significant_fraction_slice = fraction_slice.trim("0"sv, TrimMode::Right);
  58. // 10. Let significantFracDigitCount be the length of significantFracSlice.
  59. auto significant_fraction_digit_count = significant_fraction_slice.length();
  60. // 11. Let significantFrac be ! ToNumber(significantFracSlice).
  61. auto significant_fraction = significant_fraction_slice.is_empty() ? 0u : significant_fraction_slice.template to_uint<u64>().value();
  62. // 12. Return a new Record { [[Number]]: abs(n), [[IntegerDigits]]: i, [[FractionDigits]]: f, [[NumberOfFractionDigits]]: fracDigitCount, [[FractionDigitsWithoutTrailing]]: significantFrac, [[NumberOfFractionDigitsWithoutTrailing]]: significantFracDigitCount }.
  63. return Unicode::PluralOperands {
  64. .number = fabs(number),
  65. .integer_digits = integer,
  66. .fraction_digits = fraction,
  67. .number_of_fraction_digits = fraction_digit_count,
  68. .fraction_digits_without_trailing = significant_fraction,
  69. .number_of_fraction_digits_without_trailing = significant_fraction_digit_count,
  70. };
  71. }
  72. // 16.5.2 PluralRuleSelect ( locale, type, n, operands ), https://tc39.es/ecma402/#sec-pluralruleselect
  73. Unicode::PluralCategory plural_rule_select(StringView locale, Unicode::PluralForm type, Value, Unicode::PluralOperands operands)
  74. {
  75. return Unicode::determine_plural_category(locale, type, move(operands));
  76. }
  77. // 16.5.3 ResolvePlural ( pluralRules, n ), https://tc39.es/ecma402/#sec-resolveplural
  78. Unicode::PluralCategory resolve_plural(GlobalObject& global_object, PluralRules const& plural_rules, Value number)
  79. {
  80. return resolve_plural(global_object, plural_rules, plural_rules.type(), number);
  81. }
  82. // Non-standard overload of ResolvePlural to allow using the AO without an Intl.PluralRules object.
  83. Unicode::PluralCategory resolve_plural(GlobalObject& global_object, NumberFormatBase const& number_format, Unicode::PluralForm type, Value number)
  84. {
  85. // 1. Assert: Type(pluralRules) is Object.
  86. // 2. Assert: pluralRules has an [[InitializedPluralRules]] internal slot.
  87. // 3. Assert: Type(n) is Number.
  88. // 4. If n is not a finite Number, then
  89. if (!number.is_finite_number()) {
  90. // a. Return "other".
  91. return Unicode::PluralCategory::Other;
  92. }
  93. // 5. Let locale be pluralRules.[[Locale]].
  94. auto const& locale = number_format.locale();
  95. // 6. Let type be pluralRules.[[Type]].
  96. // 7. Let res be ! FormatNumericToString(pluralRules, n).
  97. auto result = format_numeric_to_string(global_object, number_format, number);
  98. // 8. Let s be res.[[FormattedString]].
  99. auto const& string = result.formatted_string;
  100. // 9. Let operands be ! GetOperands(s).
  101. auto operands = get_operands(string);
  102. // 10. Return ! PluralRuleSelect(locale, type, n, operands).
  103. return plural_rule_select(locale, type, number, move(operands));
  104. }
  105. }