2022-01-28 12:56:04 -05:00
/*
2023-01-12 08:17:05 -05:00
* Copyright ( c ) 2022 - 2023 , Tim Flynn < trflynn89 @ serenityos . org >
2022-01-28 12:56:04 -05:00
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
2022-07-07 10:01:00 -04:00
# include <AK/Variant.h>
2022-01-28 12:56:04 -05:00
# include <LibJS/Runtime/Intl/PluralRules.h>
2022-07-07 10:01:00 -04:00
# include <math.h>
# include <stdlib.h>
2022-01-28 12:56:04 -05:00
namespace JS : : Intl {
2023-11-19 09:45:05 +01:00
JS_DEFINE_ALLOCATOR ( PluralRules ) ;
2022-01-28 12:56:04 -05:00
// 16 PluralRules Objects, https://tc39.es/ecma402/#pluralrules-objects
PluralRules : : PluralRules ( Object & prototype )
: NumberFormatBase ( prototype )
{
}
2022-07-07 10:01:00 -04:00
// 16.5.1 GetOperands ( s ), https://tc39.es/ecma402/#sec-getoperands
2023-01-21 22:22:11 -05:00
: : Locale : : PluralOperands get_operands ( StringView string )
2022-07-07 10:01:00 -04:00
{
// 1.Let n be ! ToNumber(s).
2023-12-23 15:59:14 +13:00
auto number = string . to_number < double > ( AK : : TrimWhitespace : : Yes ) . release_value ( ) ;
2022-07-07 10:01:00 -04:00
// 2. Assert: n is finite.
VERIFY ( isfinite ( number ) ) ;
// 3. Let dp be StringIndexOf(s, ".", 0).
auto decimal_point = string . find ( ' . ' ) ;
Variant < Empty , double , StringView > integer_part ;
StringView fraction_slice ;
// 4. If dp = -1, then
if ( ! decimal_point . has_value ( ) ) {
// a. Let intPart be n.
integer_part = number ;
// b. Let fracSlice be "".
}
// 5. Else,
else {
// a. Let intPart be the substring of s from 0 to dp.
integer_part = string . substring_view ( 0 , * decimal_point ) ;
// b. Let fracSlice be the substring of s from dp + 1.
fraction_slice = string . substring_view ( * decimal_point + 1 ) ;
}
// 6. Let i be abs(! ToNumber(intPart)).
auto integer = integer_part . visit (
[ ] ( Empty ) - > u64 { VERIFY_NOT_REACHED ( ) ; } ,
[ ] ( double value ) {
return static_cast < u64 > ( fabs ( value ) ) ;
} ,
[ ] ( StringView value ) {
2023-12-23 15:59:14 +13:00
auto value_as_int = value . template to_number < i64 > ( ) . value ( ) ;
2022-07-07 10:01:00 -04:00
return static_cast < u64 > ( value_as_int ) ;
} ) ;
// 7. Let fracDigitCount be the length of fracSlice.
auto fraction_digit_count = fraction_slice . length ( ) ;
// 8. Let f be ! ToNumber(fracSlice).
2023-12-23 15:59:14 +13:00
auto fraction = fraction_slice . is_empty ( ) ? 0u : fraction_slice . template to_number < u64 > ( ) . value ( ) ;
2022-07-07 10:01:00 -04:00
// 9. Let significantFracSlice be the value of fracSlice stripped of trailing "0".
auto significant_fraction_slice = fraction_slice . trim ( " 0 " sv , TrimMode : : Right ) ;
// 10. Let significantFracDigitCount be the length of significantFracSlice.
auto significant_fraction_digit_count = significant_fraction_slice . length ( ) ;
// 11. Let significantFrac be ! ToNumber(significantFracSlice).
2023-12-23 15:59:14 +13:00
auto significant_fraction = significant_fraction_slice . is_empty ( ) ? 0u : significant_fraction_slice . template to_number < u64 > ( ) . value ( ) ;
2022-07-07 10:01:00 -04:00
// 12. Return a new Record { [[Number]]: abs(n), [[IntegerDigits]]: i, [[FractionDigits]]: f, [[NumberOfFractionDigits]]: fracDigitCount, [[FractionDigitsWithoutTrailing]]: significantFrac, [[NumberOfFractionDigitsWithoutTrailing]]: significantFracDigitCount }.
2022-09-02 12:01:10 -04:00
return : : Locale : : PluralOperands {
2022-07-07 10:01:00 -04:00
. number = fabs ( number ) ,
. integer_digits = integer ,
. fraction_digits = fraction ,
. number_of_fraction_digits = fraction_digit_count ,
. fraction_digits_without_trailing = significant_fraction ,
. number_of_fraction_digits_without_trailing = significant_fraction_digit_count ,
} ;
}
// 16.5.2 PluralRuleSelect ( locale, type, n, operands ), https://tc39.es/ecma402/#sec-pluralruleselect
2022-09-02 12:01:10 -04:00
: : Locale : : PluralCategory plural_rule_select ( StringView locale , : : Locale : : PluralForm type , Value , : : Locale : : PluralOperands operands )
2022-07-07 10:01:00 -04:00
{
2022-09-02 12:01:10 -04:00
return : : Locale : : determine_plural_category ( locale , type , move ( operands ) ) ;
2022-07-07 10:01:00 -04:00
}
// 16.5.3 ResolvePlural ( pluralRules, n ), https://tc39.es/ecma402/#sec-resolveplural
2023-08-30 12:30:39 -04:00
ResolvedPlurality resolve_plural ( PluralRules const & plural_rules , Value number )
2022-07-08 11:52:18 -04:00
{
2023-08-30 12:30:39 -04:00
return resolve_plural ( plural_rules , plural_rules . type ( ) , number ) ;
2022-07-08 11:52:18 -04:00
}
// Non-standard overload of ResolvePlural to allow using the AO without an Intl.PluralRules object.
2023-08-30 12:30:39 -04:00
ResolvedPlurality resolve_plural ( NumberFormatBase const & number_format , : : Locale : : PluralForm type , Value number )
2022-07-07 10:01:00 -04:00
{
// 1. Assert: Type(pluralRules) is Object.
// 2. Assert: pluralRules has an [[InitializedPluralRules]] internal slot.
// 3. Assert: Type(n) is Number.
// 4. If n is not a finite Number, then
if ( ! number . is_finite_number ( ) ) {
// a. Return "other".
2023-08-30 12:30:39 -04:00
return { : : Locale : : PluralCategory : : Other , String { } } ;
2022-07-07 10:01:00 -04:00
}
// 5. Let locale be pluralRules.[[Locale]].
2022-07-08 11:52:18 -04:00
auto const & locale = number_format . locale ( ) ;
2022-07-07 10:01:00 -04:00
// 6. Let type be pluralRules.[[Type]].
// 7. Let res be ! FormatNumericToString(pluralRules, n).
2023-08-30 12:30:39 -04:00
auto result = format_numeric_to_string ( number_format , number ) ;
2022-07-07 10:01:00 -04:00
// 8. Let s be res.[[FormattedString]].
2023-01-30 12:45:16 -05:00
auto string = move ( result . formatted_string ) ;
2022-07-07 10:01:00 -04:00
// 9. Let operands be ! GetOperands(s).
auto operands = get_operands ( string ) ;
2023-01-30 12:45:16 -05:00
// 10. Let p be ! PluralRuleSelect(locale, type, n, operands).
auto plural_category = plural_rule_select ( locale , type , number , move ( operands ) ) ;
// 11. Return the Record { [[PluralCategory]]: p, [[FormattedString]]: s }.
2023-08-30 12:30:39 -04:00
return { plural_category , move ( string ) } ;
2022-07-07 10:01:00 -04:00
}
2023-04-11 08:45:51 -04:00
// 16.5.4 PluralRuleSelectRange ( locale, type, xp, yp ), https://tc39.es/ecma402/#sec-resolveplural
2022-09-02 12:01:10 -04:00
: : Locale : : PluralCategory plural_rule_select_range ( StringView locale , : : Locale : : PluralForm , : : Locale : : PluralCategory start , : : Locale : : PluralCategory end )
2022-07-11 11:28:10 -04:00
{
2022-09-02 12:01:10 -04:00
return : : Locale : : determine_plural_range ( locale , start , end ) ;
2022-07-11 11:28:10 -04:00
}
2023-04-11 08:45:51 -04:00
// 16.5.5 ResolvePluralRange ( pluralRules, x, y ), https://tc39.es/ecma402/#sec-resolveplural
2022-09-02 12:01:10 -04:00
ThrowCompletionOr < : : Locale : : PluralCategory > resolve_plural_range ( VM & vm , PluralRules const & plural_rules , Value start , Value end )
2022-07-11 11:28:10 -04:00
{
// 1. Assert: Type(pluralRules) is Object.
// 2. Assert: pluralRules has an [[InitializedPluralRules]] internal slot.
// 3. Assert: Type(x) is Number.
// 4. Assert: Type(y) is Number.
// 5. If x is NaN or y is NaN, throw a RangeError exception.
if ( start . is_nan ( ) )
2023-06-25 10:11:37 -04:00
return vm . throw_completion < RangeError > ( ErrorType : : NumberIsNaN , " start " sv ) ;
2022-07-11 11:28:10 -04:00
if ( end . is_nan ( ) )
2023-06-25 10:11:37 -04:00
return vm . throw_completion < RangeError > ( ErrorType : : NumberIsNaN , " end " sv ) ;
2022-07-11 11:28:10 -04:00
2022-07-26 07:12:46 -04:00
// 6. Let xp be ! ResolvePlural(pluralRules, x).
2023-08-30 12:30:39 -04:00
auto start_plurality = resolve_plural ( plural_rules , start ) ;
2022-07-11 11:28:10 -04:00
2022-07-26 07:12:46 -04:00
// 7. Let yp be ! ResolvePlural(pluralRules, y).
2023-08-30 12:30:39 -04:00
auto end_plurality = resolve_plural ( plural_rules , end ) ;
2022-07-11 11:28:10 -04:00
2023-01-30 12:45:16 -05:00
// 8. If xp.[[FormattedString]] is yp.[[FormattedString]], then
if ( start_plurality . formatted_string = = end_plurality . formatted_string ) {
// a. Return xp.[[PluralCategory]].
return start_plurality . plural_category ;
}
// 9. Let locale be pluralRules.[[Locale]].
2022-07-11 11:28:10 -04:00
auto const & locale = plural_rules . locale ( ) ;
2023-01-30 12:45:16 -05:00
// 10. Let type be pluralRules.[[Type]].
2022-07-11 11:28:10 -04:00
auto type = plural_rules . type ( ) ;
2023-01-30 12:45:16 -05:00
// 11. Return ! PluralRuleSelectRange(locale, type, xp.[[PluralCategory]], yp.[[PluralCategory]]).
return plural_rule_select_range ( locale , type , start_plurality . plural_category , end_plurality . plural_category ) ;
2022-07-11 11:28:10 -04:00
}
2022-01-28 12:56:04 -05:00
}