Implement more options to Intl.PluralRules

Signed-off-by: Seonghyun Kim <sh8281.kim@samsung.com>
This commit is contained in:
Seonghyun Kim 2025-07-18 10:57:43 +09:00 committed by Patrick Kim
commit b2ecf54887
9 changed files with 337 additions and 283 deletions

View file

@ -540,7 +540,7 @@ static Value builtinIntlPluralRulesSelect(ExecutionState& state, Value thisValue
// Let n be ? ToNumber(value).
double n = argv[0].toNumber(state);
// Return ? ResolvePlural(pr, n).
return thisValue.asObject()->asIntlPluralRulesObject()->asIntlPluralRulesObject()->resolvePlural(n);
return thisValue.asObject()->asIntlPluralRulesObject()->asIntlPluralRulesObject()->resolvePlural(state, n);
}
static Value builtinIntlPluralRulesResolvedOptions(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
@ -562,19 +562,19 @@ static Value builtinIntlPluralRulesResolvedOptions(ExecutionState& state, Value
// If v is not undefined, then
// Perform ! CreateDataPropertyOrThrow(options, p, v).
options->defineOwnPropertyThrowsException(state, ObjectPropertyName(state.context()->staticStrings().lazySmallLetterLocale()), ObjectPropertyDescriptor(pr->locale(), ObjectPropertyDescriptor::AllPresent));
options->defineOwnPropertyThrowsException(state, ObjectPropertyName(state.context()->staticStrings().lazyType()), ObjectPropertyDescriptor(pr->type(), ObjectPropertyDescriptor::AllPresent));
options->defineOwnPropertyThrowsException(state, ObjectPropertyName(state.context()->staticStrings().lazyNotation()), ObjectPropertyDescriptor(pr->notation(), ObjectPropertyDescriptor::AllPresent));
options->defineOwnPropertyThrowsException(state, ObjectPropertyName(state.context()->staticStrings().lazyMinimumIntegerDigits()), ObjectPropertyDescriptor(Value(Value::DoubleToIntConvertibleTestNeeds, pr->minimumIntegerDigits()), ObjectPropertyDescriptor::AllPresent));
if (pr->minimumSignificantDigits()) {
options->defineOwnPropertyThrowsException(state, ObjectPropertyName(state.context()->staticStrings().lazyMinimumSignificantDigits()), ObjectPropertyDescriptor(Value(Value::DoubleToIntConvertibleTestNeeds, pr->minimumSignificantDigits().value()), ObjectPropertyDescriptor::AllPresent));
options->defineOwnPropertyThrowsException(state, ObjectPropertyName(state.context()->staticStrings().lazyMaximumSignificantDigits()), ObjectPropertyDescriptor(Value(Value::DoubleToIntConvertibleTestNeeds, pr->maximumSignificantDigits().value()), ObjectPropertyDescriptor::AllPresent));
} else {
options->defineOwnPropertyThrowsException(state, ObjectPropertyName(state.context()->staticStrings().lazyMinimumFractionDigits()), ObjectPropertyDescriptor(Value(Value::DoubleToIntConvertibleTestNeeds, pr->minimumFractionDigits()), ObjectPropertyDescriptor::AllPresent));
options->defineOwnPropertyThrowsException(state, ObjectPropertyName(state.context()->staticStrings().lazyMaximumFractionDigits()), ObjectPropertyDescriptor(Value(Value::DoubleToIntConvertibleTestNeeds, pr->maximumFractionDigits()), ObjectPropertyDescriptor::AllPresent));
if (!pr->minimumSignificantDigits().isUndefined()) {
options->defineOwnPropertyThrowsException(state, ObjectPropertyName(state.context()->staticStrings().lazyMinimumSignificantDigits()), ObjectPropertyDescriptor(pr->minimumSignificantDigits(), ObjectPropertyDescriptor::AllPresent));
options->defineOwnPropertyThrowsException(state, ObjectPropertyName(state.context()->staticStrings().lazyMaximumSignificantDigits()), ObjectPropertyDescriptor(pr->maximumSignificantDigits(), ObjectPropertyDescriptor::AllPresent));
}
if (!pr->minimumFractionDigits().isUndefined()) {
options->defineOwnPropertyThrowsException(state, ObjectPropertyName(state.context()->staticStrings().lazyMinimumFractionDigits()), ObjectPropertyDescriptor(pr->minimumFractionDigits(), ObjectPropertyDescriptor::AllPresent));
options->defineOwnPropertyThrowsException(state, ObjectPropertyName(state.context()->staticStrings().lazyMaximumFractionDigits()), ObjectPropertyDescriptor(pr->maximumFractionDigits(), ObjectPropertyDescriptor::AllPresent));
}
// Let pluralCategories be a List of Strings representing the possible results of PluralRuleSelect for the selected locale pr.[[Locale]]. This List consists of unique string values, from the the list "zero", "one", "two", "few", "many" and "other", that are relevant for the locale whose localization is specified in LDML Language Plural Rules.
// Perform ! CreateDataProperty(options, "pluralCategories", CreateArrayFromList(pluralCategories)).
@ -583,8 +583,7 @@ static Value builtinIntlPluralRulesResolvedOptions(ExecutionState& state, Value
UErrorCode status = U_ZERO_ERROR;
UEnumeration* ue = uplrules_getKeywords(pr->icuPluralRules(), &status);
ASSERT(U_SUCCESS(status));
uint32_t i = 0;
Vector<String*, GCUtil::gc_malloc_allocator<String*>> v;
do {
int32_t size;
const char* str = uenum_next(ue, &size, &status);
@ -594,15 +593,49 @@ static Value builtinIntlPluralRulesResolvedOptions(ExecutionState& state, Value
break;
}
String* s = String::fromUTF8(str, size);
pluralCategories->defineOwnPropertyThrowsException(state, ObjectPropertyName(state, (size_t)i), ObjectPropertyDescriptor(Value(s), ObjectPropertyDescriptor::AllPresent));
i++;
v.pushBack(String::fromUTF8(str, size));
} while (true);
constexpr std::array<const char*, 6> candidates = { { "zero", "one", "two", "few", "many", "other" } };
size_t i = 0;
for (auto candidate : candidates) {
for (auto s : v) {
if (s->equals(candidate, strlen(candidate))) {
pluralCategories->defineOwnPropertyThrowsException(state, ObjectPropertyName(state, i++), ObjectPropertyDescriptor(Value(s), ObjectPropertyDescriptor::AllPresent));
break;
}
}
}
options->defineOwnPropertyThrowsException(state, ObjectPropertyName(AtomicString(state.context()->atomicStringMap(), "pluralCategories", sizeof("pluralCategories") - 1, AtomicString::FromExternalMemory)),
ObjectPropertyDescriptor(pluralCategories, ObjectPropertyDescriptor::AllPresent));
uenum_close(ue);
options->defineOwnPropertyThrowsException(state, ObjectPropertyName(state.context()->staticStrings().lazyRoundingIncrement()), ObjectPropertyDescriptor(Value(Value::DoubleToIntConvertibleTestNeeds, pr->roundingIncrement()), ObjectPropertyDescriptor::AllPresent));
options->defineOwnPropertyThrowsException(state, ObjectPropertyName(state.context()->staticStrings().lazyRoundingMode()), ObjectPropertyDescriptor(pr->roundingMode(), ObjectPropertyDescriptor::AllPresent));
Value roundingType;
switch (pr->computedRoundingPriority()) {
case Intl::RoundingPriority::Auto:
roundingType = state.context()->staticStrings().lazyAuto().string();
break;
case Intl::RoundingPriority::MorePrecision:
roundingType = state.context()->staticStrings().lazyMorePrecision().string();
break;
case Intl::RoundingPriority::LessPrecision:
roundingType = state.context()->staticStrings().lazyLessPrecision().string();
break;
default:
ASSERT_NOT_REACHED();
}
options->defineOwnPropertyThrowsException(state, ObjectPropertyName(state.context()->staticStrings().lazyRoundingPriority()), ObjectPropertyDescriptor(roundingType, ObjectPropertyDescriptor::AllPresent));
options->defineOwnPropertyThrowsException(state, ObjectPropertyName(state.context()->staticStrings().lazyTrailingZeroDisplay()), ObjectPropertyDescriptor(pr->trailingZeroDisplay(), ObjectPropertyDescriptor::AllPresent));
// Return options.
return options;
}

View file

@ -2647,7 +2647,7 @@ Intl::SetNumberFormatDigitOptionsResult Intl::setNumberFormatDigitOptions(Execut
}
// Set intlObj.[[MinimumIntegerDigits]] to mnid.
result.minimumIntegerDigits = Value(Value::DoubleToIntConvertibleTestNeeds, mnid);
result.minimumIntegerDigits = mnid;
// Let roundingIncrement be ? GetNumberOption(options, "roundingIncrement", 1, 5000, 1).
double roundingIncrement = Intl::getNumberOption(state, options, state.context()->staticStrings().lazyRoundingIncrement().string(), 1, 5000, 1);
@ -2678,9 +2678,9 @@ Intl::SetNumberFormatDigitOptionsResult Intl::setNumberFormatDigitOptions(Execut
// Set intlObj.[[RoundingIncrement]] to roundingIncrement.
result.roundingIncrement = roundingIncrement;
// Set intlObj.[[RoundingMode]] to roundingMode.
result.roundingMode = roundingMode;
result.roundingMode = roundingMode.asString();
// Set intlObj.[[TrailingZeroDisplay]] to trailingZeroDisplay.
result.trailingZeroDisplay = trailingZeroDisplay;
result.trailingZeroDisplay = trailingZeroDisplay.asString();
// If mnsd is undefined and mxsd is undefined, let hasSd be false. Otherwise, let hasSd be true.
bool hasSd;
if (mnsd.isUndefined() && mxsd.isUndefined()) {
@ -2814,6 +2814,186 @@ Intl::SetNumberFormatDigitOptionsResult Intl::setNumberFormatDigitOptions(Execut
return result;
}
void Intl::initNumberFormatSkeleton(ExecutionState& state, const Intl::SetNumberFormatDigitOptionsResult& formatResult, String* notation, String* compactDisplay, UTF16StringDataNonGCStd& skeleton)
{
#if defined(ENABLE_RUNTIME_ICU_BINDER)
UVersionInfo versionArray;
u_getVersion(versionArray);
if (versionArray[0] < 68) {
if (!formatResult.minimumSignificantDigits.isUndefined()) {
double mnsd = formatResult.minimumSignificantDigits.asNumber();
double mxsd = formatResult.maximumSignificantDigits.asNumber();
for (double i = 0; i < mnsd; i++) {
skeleton += '@';
}
for (double i = 0; i < mxsd - mnsd; i++) {
skeleton += '#';
}
skeleton += ' ';
}
if (!formatResult.minimumFractionDigits.isUndefined()) {
double mnfd = formatResult.minimumSignificantDigits.asNumber();
double mxfd = formatResult.maximumSignificantDigits.asNumber();
skeleton += '.';
for (double i = 0; i < mnfd; i++) {
skeleton += '0';
}
for (double i = 0; i < mxfd - mnfd; i++) {
skeleton += '#';
}
skeleton += ' ';
}
{
double mnid = formatResult.minimumIntegerDigits;
skeleton += u"integer-width/+";
for (double i = 0; i < mnid; i++) {
skeleton += '0';
}
skeleton += ' ';
}
if (notation->equals("standard")) {
} else if (notation->equals("scientific")) {
skeleton += u"scientific ";
} else if (notation->equals("engineering")) {
skeleton += u"engineering ";
} else {
if (compactDisplay->equals("short")) {
skeleton += u"compact-short ";
} else {
skeleton += u"compact-long ";
}
}
return;
}
#endif
String* rm = formatResult.roundingMode;
if (rm->equals("ceil")) {
skeleton += u"rounding-mode-ceiling ";
} else if (rm->equals("floor")) {
skeleton += u"rounding-mode-floor ";
} else if (rm->equals("expand")) {
skeleton += u"rounding-mode-up ";
} else if (rm->equals("trunc")) {
skeleton += u"rounding-mode-down ";
} else if (rm->equals("halfCeil")) {
skeleton += u"rounding-mode-half-ceiling ";
} else if (rm->equals("halfFloor")) {
skeleton += u"rounding-mode-half-floor ";
} else if (rm->equals("halfExpand")) {
skeleton += u"rounding-mode-half-up ";
} else if (rm->equals("halfTrunc")) {
skeleton += u"rounding-mode-half-down ";
} else {
ASSERT(rm->equals("halfEven"));
skeleton += u"rounding-mode-half-even ";
}
{
double mnid = formatResult.minimumIntegerDigits;
skeleton += u"integer-width/*";
for (double i = 0; i < mnid; i++) {
skeleton += '0';
}
skeleton += ' ';
}
if (formatResult.roundingIncrement != 1) {
skeleton += u"precision-increment/";
auto string = dtoa(formatResult.roundingIncrement);
if (formatResult.maximumFractionDigits.asNumber() >= string.size()) {
skeleton += u"0.";
for (size_t i = 0; i < (formatResult.maximumFractionDigits.asNumber() - string.size()); i++) {
skeleton += u"0";
}
for (auto c : string) {
skeleton += static_cast<char16_t>(c);
}
} else {
auto nonFraction = string.size() - formatResult.maximumFractionDigits.asNumber();
for (size_t i = 0; i < nonFraction; i++) {
skeleton += static_cast<char16_t>(string[i]);
}
skeleton += u".";
for (size_t i = 0; i < nonFraction; i++) {
skeleton += static_cast<char16_t>(string[i]);
}
for (size_t i = 0; i < formatResult.maximumFractionDigits.asNumber(); i++) {
skeleton += static_cast<char16_t>(string[i + nonFraction]);
}
}
} else {
if (formatResult.roundingType == Intl::RoundingType::FractionDigits) {
skeleton += u".";
for (size_t i = 0; i < formatResult.minimumFractionDigits.asNumber(); i++) {
skeleton += u"0";
}
for (size_t i = 0; i < formatResult.maximumFractionDigits.asNumber() - formatResult.minimumFractionDigits.asNumber(); i++) {
skeleton += u"#";
}
} else if (formatResult.roundingType == Intl::RoundingType::SignificantDigits) {
for (size_t i = 0; i < formatResult.minimumSignificantDigits.asNumber(); i++) {
skeleton += u"@";
}
for (size_t i = 0; i < formatResult.maximumSignificantDigits.asNumber() - formatResult.minimumSignificantDigits.asNumber(); i++) {
skeleton += u"#";
}
} else {
ASSERT(formatResult.roundingType == Intl::RoundingType::LessPrecision || formatResult.roundingType == Intl::RoundingType::MorePrecision);
skeleton += u".";
for (size_t i = 0; i < formatResult.minimumFractionDigits.asNumber(); i++) {
skeleton += u"0";
}
for (size_t i = 0; i < formatResult.maximumFractionDigits.asNumber() - formatResult.minimumFractionDigits.asNumber(); i++) {
skeleton += u"#";
}
skeleton += u"/";
for (size_t i = 0; i < formatResult.minimumSignificantDigits.asNumber(); i++) {
skeleton += u"@";
}
for (size_t i = 0; i < formatResult.maximumSignificantDigits.asNumber() - formatResult.minimumSignificantDigits.asNumber(); i++) {
skeleton += u"#";
}
if (formatResult.roundingType == Intl::RoundingType::MorePrecision) {
skeleton += u"r";
} else {
skeleton += u"s";
}
}
}
if (formatResult.trailingZeroDisplay->equals("auto")) {
skeleton += u" ";
} else {
ASSERT(formatResult.trailingZeroDisplay->equals("stripIfInteger"));
skeleton += u"/w ";
}
if (notation->equals("standard")) {
} else if (notation->equals("scientific")) {
skeleton += u"scientific ";
} else if (notation->equals("engineering")) {
skeleton += u"engineering ";
} else {
if (compactDisplay->equals("short")) {
skeleton += u"compact-short ";
} else {
skeleton += u"compact-long ";
}
}
}
} // namespace Escargot
#endif

View file

@ -100,10 +100,10 @@ public:
MorePrecision
};
struct SetNumberFormatDigitOptionsResult {
Value minimumIntegerDigits;
double minimumIntegerDigits;
double roundingIncrement;
Value roundingMode;
Value trailingZeroDisplay;
String* roundingMode;
String* trailingZeroDisplay;
Value minimumSignificantDigits;
Value maximumSignificantDigits;
Value minimumFractionDigits;
@ -114,6 +114,7 @@ public:
// https://402.ecma-international.org/12.0/index.html#sec-setnumberformatdigitoptions
static SetNumberFormatDigitOptionsResult setNumberFormatDigitOptions(ExecutionState& state, Object* options,
double mnfdDefault, double mxfdDefault, String* notation);
static void initNumberFormatSkeleton(ExecutionState& state, const Intl::SetNumberFormatDigitOptionsResult& formatResult, String* notation, String* compactDisplay, UTF16StringDataNonGCStd& skeleton);
// https://tc39.es/ecma402/#sec-defaultnumberoption
static Value defaultNumberOption(ExecutionState& state, Value value, double minimum, double maximum, double fallback);

View file

@ -490,16 +490,23 @@ void IntlNumberFormat::initialize(ExecutionState& state, Object* numberFormat, V
// Perform ? SetNumberFormatDigitOptions(numberFormat, options, mnfdDefault, mxfdDefault, notation).
auto setNumberFormatDigitOptionsResult = Intl::setNumberFormatDigitOptions(state, options.asObject(), mnfdDefault, mxfdDefault, notation.asString());
numberFormat->internalSlot()->set(state, ObjectPropertyName(state.context()->staticStrings().lazyMinimumIntegerDigits()),
Value(Value::DoubleToIntConvertibleTestNeeds, setNumberFormatDigitOptionsResult.minimumIntegerDigits),
numberFormat->internalSlot());
#define SET_PROPERTY(name, Name) \
if (!setNumberFormatDigitOptionsResult.name.isUndefined()) { \
numberFormat->internalSlot()->set(state, ObjectPropertyName(state.context()->staticStrings().lazy##Name()), \
setNumberFormatDigitOptionsResult.name, numberFormat->internalSlot()); \
}
SET_PROPERTY(minimumIntegerDigits, MinimumIntegerDigits);
SET_PROPERTY(minimumFractionDigits, MinimumFractionDigits);
SET_PROPERTY(maximumFractionDigits, MaximumFractionDigits);
SET_PROPERTY(minimumSignificantDigits, MinimumSignificantDigits);
SET_PROPERTY(maximumSignificantDigits, MaximumSignificantDigits);
#undef SET_PROPERTY
#define SET_PROPERTY(name, Name) \
numberFormat->internalSlot()->set(state, ObjectPropertyName(state.context()->staticStrings().lazy##Name()), \
setNumberFormatDigitOptionsResult.name, numberFormat->internalSlot());
SET_PROPERTY(roundingMode, RoundingMode);
SET_PROPERTY(trailingZeroDisplay, TrailingZeroDisplay);
#undef SET_PROPERTY
@ -707,64 +714,12 @@ void IntlNumberFormat::initNumberFormatSkeleton(ExecutionState& state, const Int
}
}
if (!formatResult.minimumSignificantDigits.isUndefined()) {
double mnsd = formatResult.minimumSignificantDigits.asNumber();
double mxsd = formatResult.maximumSignificantDigits.asNumber();
for (double i = 0; i < mnsd; i++) {
skeleton += '@';
}
for (double i = 0; i < mxsd - mnsd; i++) {
skeleton += '#';
}
skeleton += ' ';
}
if (!formatResult.minimumFractionDigits.isUndefined()) {
double mnfd = formatResult.minimumSignificantDigits.asNumber();
double mxfd = formatResult.maximumSignificantDigits.asNumber();
skeleton += '.';
for (double i = 0; i < mnfd; i++) {
skeleton += '0';
}
for (double i = 0; i < mxfd - mnfd; i++) {
skeleton += '#';
}
skeleton += ' ';
}
{
double mnid = formatResult.minimumIntegerDigits.asNumber();
skeleton += u"integer-width/+";
for (double i = 0; i < mnid; i++) {
skeleton += '0';
}
skeleton += ' ';
}
Intl::initNumberFormatSkeleton(state, formatResult, notation.asString(), compactDisplay.asString(), skeleton);
if (!useGrouping.toBoolean()) {
skeleton += u"group-off ";
}
String* notationString = notation.asString();
if (notationString->equals("standard")) {
} else if (notationString->equals("scientific")) {
skeleton += u"scientific ";
} else if (notationString->equals("engineering")) {
skeleton += u"engineering ";
} else {
if (compactDisplay.asString()->equals("short")) {
skeleton += u"compact-short ";
} else {
skeleton += u"compact-long ";
}
}
String* signDisplayString = signDisplay.asString();
bool accountingSign = currencySign.asString()->equals("accounting");
if (signDisplayString->equals("auto")) {
@ -791,110 +746,7 @@ void IntlNumberFormat::initNumberFormatSkeleton(ExecutionState& state, const Int
return;
}
#endif
String* rm = formatResult.roundingMode.asString();
if (rm->equals("ceil")) {
skeleton += u"rounding-mode-ceiling ";
} else if (rm->equals("floor")) {
skeleton += u"rounding-mode-floor ";
} else if (rm->equals("expand")) {
skeleton += u"rounding-mode-up ";
} else if (rm->equals("trunc")) {
skeleton += u"rounding-mode-down ";
} else if (rm->equals("halfCeil")) {
skeleton += u"rounding-mode-half-ceiling ";
} else if (rm->equals("halfFloor")) {
skeleton += u"rounding-mode-half-floor ";
} else if (rm->equals("halfExpand")) {
skeleton += u"rounding-mode-half-up ";
} else if (rm->equals("halfTrunc")) {
skeleton += u"rounding-mode-half-down ";
} else {
ASSERT(rm->equals("halfEven"));
skeleton += u"rounding-mode-half-even ";
}
{
double mnid = formatResult.minimumIntegerDigits.asNumber();
skeleton += u"integer-width/*";
for (double i = 0; i < mnid; i++) {
skeleton += '0';
}
skeleton += ' ';
}
if (formatResult.roundingIncrement != 1) {
skeleton += u"precision-increment/";
auto string = dtoa(formatResult.roundingIncrement);
if (formatResult.maximumFractionDigits.asNumber() >= string.size()) {
skeleton += u"0.";
for (size_t i = 0; i < (formatResult.maximumFractionDigits.asNumber() - string.size()); i++) {
skeleton += u"0";
}
for (auto c : string) {
skeleton += static_cast<char16_t>(c);
}
} else {
auto nonFraction = string.size() - formatResult.maximumFractionDigits.asNumber();
for (size_t i = 0; i < nonFraction; i++) {
skeleton += static_cast<char16_t>(string[i]);
}
skeleton += u".";
for (size_t i = 0; i < nonFraction; i++) {
skeleton += static_cast<char16_t>(string[i]);
}
for (size_t i = 0; i < formatResult.maximumFractionDigits.asNumber(); i++) {
skeleton += static_cast<char16_t>(string[i + nonFraction]);
}
}
} else {
if (formatResult.roundingType == Intl::RoundingType::FractionDigits) {
skeleton += u".";
for (size_t i = 0; i < formatResult.minimumFractionDigits.asNumber(); i++) {
skeleton += u"0";
}
for (size_t i = 0; i < formatResult.maximumFractionDigits.asNumber() - formatResult.minimumFractionDigits.asNumber(); i++) {
skeleton += u"#";
}
} else if (formatResult.roundingType == Intl::RoundingType::SignificantDigits) {
for (size_t i = 0; i < formatResult.minimumSignificantDigits.asNumber(); i++) {
skeleton += u"@";
}
for (size_t i = 0; i < formatResult.maximumSignificantDigits.asNumber() - formatResult.minimumSignificantDigits.asNumber(); i++) {
skeleton += u"#";
}
} else {
ASSERT(formatResult.roundingType == Intl::RoundingType::LessPrecision || formatResult.roundingType == Intl::RoundingType::MorePrecision);
skeleton += u".";
for (size_t i = 0; i < formatResult.minimumFractionDigits.asNumber(); i++) {
skeleton += u"0";
}
for (size_t i = 0; i < formatResult.maximumFractionDigits.asNumber() - formatResult.minimumFractionDigits.asNumber(); i++) {
skeleton += u"#";
}
skeleton += u"/";
for (size_t i = 0; i < formatResult.minimumSignificantDigits.asNumber(); i++) {
skeleton += u"@";
}
for (size_t i = 0; i < formatResult.maximumSignificantDigits.asNumber() - formatResult.minimumSignificantDigits.asNumber(); i++) {
skeleton += u"#";
}
if (formatResult.roundingType == Intl::RoundingType::MorePrecision) {
skeleton += u"r";
} else {
skeleton += u"s";
}
}
}
if (formatResult.trailingZeroDisplay.asString()->equals("auto")) {
skeleton += u" ";
} else {
ASSERT(formatResult.trailingZeroDisplay.asString()->equals("stripIfInteger"));
skeleton += u"/w ";
}
Intl::initNumberFormatSkeleton(state, formatResult, notation.asString(), compactDisplay.asString(), skeleton);
if (style.asString()->equals("currency")) {
if (!currency.isUndefined()) {
@ -976,20 +828,6 @@ void IntlNumberFormat::initNumberFormatSkeleton(ExecutionState& state, const Int
ASSERT_NOT_REACHED();
}
String* notationString = notation.asString();
if (notationString->equals("standard")) {
} else if (notationString->equals("scientific")) {
skeleton += u"scientific ";
} else if (notationString->equals("engineering")) {
skeleton += u"engineering ";
} else {
if (compactDisplay.asString()->equals("short")) {
skeleton += u"compact-short ";
} else {
skeleton += u"compact-long ";
}
}
String* signDisplayString = signDisplay.asString();
bool accountingSign = style.asString()->equals("currency") && currencySign.asString()->equals("accounting");
if (signDisplayString->equals("auto")) {

View file

@ -39,6 +39,9 @@ void* IntlPluralRulesObject::operator new(size_t size)
Object::fillGCDescriptor(desc);
GC_set_bit(desc, GC_WORD_OFFSET(IntlPluralRulesObject, m_locale));
GC_set_bit(desc, GC_WORD_OFFSET(IntlPluralRulesObject, m_type));
GC_set_bit(desc, GC_WORD_OFFSET(IntlPluralRulesObject, m_notation));
GC_set_bit(desc, GC_WORD_OFFSET(IntlPluralRulesObject, m_roundingMode));
GC_set_bit(desc, GC_WORD_OFFSET(IntlPluralRulesObject, m_trailingZeroDisplay));
descr = GC_make_descriptor(desc, GC_WORD_LEN(IntlPluralRulesObject));
typeInited = true;
}
@ -61,10 +64,11 @@ IntlPluralRulesObject::IntlPluralRulesObject(ExecutionState& state, Object* prot
// Let requestedLocales be ? CanonicalizeLocaleList(locales).
ValueVector requestedLocales = Intl::canonicalizeLocaleList(state, locales);
Optional<Object*> optionObject;
Object* optionObject;
// If options is undefined, then
if (options.isUndefined()) {
// Let options be ObjectCreate(null).
optionObject = new Object(state, Object::PrototypeIsNull);
} else {
// Let options be ? ToObject(options).
optionObject = options.toObject(state);
@ -75,58 +79,20 @@ IntlPluralRulesObject::IntlPluralRulesObject(ExecutionState& state, Object* prot
// Let matcher be ? GetOption(options, "localeMatcher", "string", « "lookup", "best fit" », "best fit").
// Set opt.[[localeMatcher]] to matcher.
Value localeMatcherValues[2] = { state.context()->staticStrings().lazyLookup().string(), state.context()->staticStrings().lazyBestFit().string() };
String* matcher = localeMatcherValues[1].asString();
if (optionObject) {
matcher = Intl::getOption(state, optionObject.value(), state.context()->staticStrings().lazyLocaleMatcher().string(), Intl::StringValue, localeMatcherValues, 2, localeMatcherValues[1]).asString();
}
String* matcher = Intl::getOption(state, optionObject, state.context()->staticStrings().lazyLocaleMatcher().string(), Intl::StringValue, localeMatcherValues, 2, localeMatcherValues[1]).asString();
opt.insert(std::make_pair("matcher", matcher));
// Let t be ? GetOption(options, "type", "string", « "cardinal", "ordinal" », "cardinal").
Value typeValues[2] = { state.context()->staticStrings().lazyCardinal().string(), state.context()->staticStrings().lazyOrdinal().string() };
String* t = typeValues[0].asString();
if (optionObject) {
t = Intl::getOption(state, optionObject.value(), state.context()->staticStrings().lazyType().string(), Intl::StringValue, typeValues, 2, typeValues[0]).asString();
}
String* t = Intl::getOption(state, optionObject, state.context()->staticStrings().lazyType().string(), Intl::StringValue, typeValues, 2, typeValues[0]).asString();
// Let notation be ? GetOption(options, "notation", "string", « "standard", "scientific", "engineering", "compact" », "standard").
Value notationValues[4] = { state.context()->staticStrings().lazyStandard().string(), state.context()->staticStrings().lazyScientific().string(), state.context()->staticStrings().lazyEngineering().string(), state.context()->staticStrings().lazyCompact().string() };
Value notation = Intl::getOption(state, optionObject, state.context()->staticStrings().lazyNotation().string(), Intl::StringValue, notationValues, 4, notationValues[0]);
m_notation = notation.asString();
// Perform ? SetNumberFormatDigitOptions(pluralRules, options, 0, 3).
// Let mnid be the result of calling the GetNumberOption abstract operation (defined in 9.2.10) with arguments options, "minimumIntegerDigits", 1, 21, and 1.
double mnid = Intl::getNumberOption(state, optionObject, state.context()->staticStrings().lazyMinimumIntegerDigits().string(), 1, 21, 1);
// Set the [[minimumIntegerDigits]] internal property of numberFormat to mnid.
m_minimumIntegerDigits = mnid;
double mnfdDefault = 0;
// Let mnfd be the result of calling the GetNumberOption abstract operation with arguments options, "minimumFractionDigits", 0, 20, and mnfdDefault.
double mnfd = Intl::getNumberOption(state, optionObject, state.context()->staticStrings().lazyMinimumFractionDigits().string(), 0, 20, mnfdDefault);
// Set the [[minimumFractionDigits]] internal property of numberFormat to mnfd.
m_minimumFractionDigits = mnfd;
double mxfdDefault = 3;
// Let mxfd be the result of calling the GetNumberOption abstract operation with arguments options, "maximumFractionDigits", mnfd, 20, and mxfdDefault.
double mxfd = Intl::getNumberOption(state, optionObject, state.context()->staticStrings().lazyMaximumFractionDigits().string(), mnfd, 20, mxfdDefault);
// Set the [[maximumFractionDigits]] internal property of numberFormat to mxfd.
m_maximumFractionDigits = mxfd;
if (optionObject) {
// Let mnsd be the result of calling the [[Get]] internal method of options with argument "minimumSignificantDigits".
Value mnsd = optionObject.value()->get(state, ObjectPropertyName(state.context()->staticStrings().lazyMinimumSignificantDigits())).value(state, optionObject.value());
// Let mxsd be the result of calling the [[Get]] internal method of options with argument "maximumSignificantDigits".
Value mxsd = optionObject.value()->get(state, ObjectPropertyName(state.context()->staticStrings().lazyMaximumSignificantDigits())).value(state, optionObject.value());
// If mnsd is not undefined or mxsd is not undefined, then:
if (!mnsd.isUndefined() || !mxsd.isUndefined()) {
// Let mnsd be the result of calling the GetNumberOption abstract operation with arguments options, "minimumSignificantDigits", 1, 21, and 1.
mnsd = Value(Value::DoubleToIntConvertibleTestNeeds, Intl::getNumberOption(state, optionObject, state.context()->staticStrings().lazyMinimumSignificantDigits().string(), 1, 21, 1));
// Let mxsd be the result of calling the GetNumberOption abstract operation with arguments options, "maximumSignificantDigits", mnsd, 21, and 21.
mxsd = Value(Value::DoubleToIntConvertibleTestNeeds, Intl::getNumberOption(state, optionObject, state.context()->staticStrings().lazyMaximumSignificantDigits().string(), mnsd.asNumber(), 21, 21));
// Set the [[minimumSignificantDigits]] internal property of numberFormat to mnsd,
// and the [[maximumSignificantDigits]] internal property of numberFormat to mxsd.
m_minimumSignificantDigits = mnsd.asNumber();
m_maximumSignificantDigits = mxsd.asNumber();
}
}
auto optionsResult = Intl::setNumberFormatDigitOptions(state, optionObject, 0, 3, m_notation);
// Let localeData be %PluralRules%.[[LocaleData]].
// Let r be ResolveLocale(%PluralRules%.[[AvailableLocales]], requestedLocales, opt, %PluralRules%.[[RelevantExtensionKeys]], localeData).
@ -134,24 +100,23 @@ IntlPluralRulesObject::IntlPluralRulesObject(ExecutionState& state, Object* prot
auto r = Intl::resolveLocale(state, state.context()->vmInstance()->intlPluralRulesAvailableLocales(), requestedLocales, opt, nullptr, 0, nullptr);
String* foundLocale = r.at("locale");
UErrorCode status = U_ZERO_ERROR;
m_icuNumberFormat = unum_open(UNUM_DEFAULT, nullptr, 0, foundLocale->toNonGCUTF8StringData().data(), nullptr, &status);
if (U_FAILURE(status)) {
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, "Failed to init PluralRule");
}
m_minimumIntegerDigits = optionsResult.minimumIntegerDigits;
m_roundingIncrement = optionsResult.roundingIncrement;
m_trailingZeroDisplay = optionsResult.trailingZeroDisplay;
m_minimumSignificantDigits = optionsResult.minimumSignificantDigits;
m_maximumSignificantDigits = optionsResult.maximumSignificantDigits;
m_minimumFractionDigits = optionsResult.minimumFractionDigits;
m_maximumFractionDigits = optionsResult.maximumFractionDigits;
m_roundingType = optionsResult.roundingType;
m_roundingMode = optionsResult.roundingMode;
m_computedRoundingPriority = optionsResult.computedRoundingPriority;
if (!m_minimumSignificantDigits.hasValue()) {
unum_setAttribute(m_icuNumberFormat, UNUM_MIN_INTEGER_DIGITS, m_minimumIntegerDigits);
unum_setAttribute(m_icuNumberFormat, UNUM_MIN_FRACTION_DIGITS, m_minimumFractionDigits);
unum_setAttribute(m_icuNumberFormat, UNUM_MAX_FRACTION_DIGITS, m_maximumFractionDigits);
} else {
unum_setAttribute(m_icuNumberFormat, UNUM_SIGNIFICANT_DIGITS_USED, true);
unum_setAttribute(m_icuNumberFormat, UNUM_MIN_SIGNIFICANT_DIGITS, m_minimumSignificantDigits);
unum_setAttribute(m_icuNumberFormat, UNUM_MAX_SIGNIFICANT_DIGITS, m_maximumSignificantDigits);
}
unum_setAttribute(m_icuNumberFormat, UNUM_ROUNDING_MODE, UNUM_ROUND_HALFUP);
UErrorCode status = U_ZERO_ERROR;
UTF16StringDataNonGCStd skeleton;
Intl::initNumberFormatSkeleton(state, optionsResult, m_notation, state.context()->staticStrings().lazyCompact().string(), skeleton);
m_icuNumberFormat = unumf_openForSkeletonAndLocale((UChar*)skeleton.data(), skeleton.length(), foundLocale->toNonGCUTF8StringData().data(), &status);
if (U_FAILURE(status)) {
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, "Failed to init NumberFormat");
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, "Failed to init PluralRules");
}
ASSERT(U_SUCCESS(status));
@ -175,14 +140,14 @@ IntlPluralRulesObject::IntlPluralRulesObject(ExecutionState& state, Object* prot
addFinalizer([](PointerValue* obj, void* data) {
IntlPluralRulesObject* self = (IntlPluralRulesObject*)obj;
uplrules_close(self->m_icuPluralRules);
unum_close(self->m_icuNumberFormat);
unumf_close(self->m_icuNumberFormat);
},
nullptr);
// Return pluralRules.
}
String* IntlPluralRulesObject::resolvePlural(double number)
String* IntlPluralRulesObject::resolvePlural(ExecutionState& state, double number)
{
// https://www.ecma-international.org/ecma-402/6.0/index.html#sec-resolveplural
// If n is not a finite Number, then
@ -190,13 +155,25 @@ String* IntlPluralRulesObject::resolvePlural(double number)
// Return "other".
return String::fromASCII("other");
}
UErrorCode status = U_ZERO_ERROR;
int32_t len = uplrules_selectWithFormat(m_icuPluralRules, number, m_icuNumberFormat, nullptr, 0, &status);
UChar* buf = (UChar*)alloca((len + 1) * sizeof(UChar));
LocalResourcePointer<UFormattedNumber> formattedNumber(unumf_openResult(&status), [](UFormattedNumber* f) { unumf_closeResult(f); });
if (U_FAILURE(status)) {
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, "Failed to resolve Plural");
}
unumf_formatDouble(m_icuNumberFormat, number, formattedNumber.get(), &status);
if (U_FAILURE(status)) {
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, "Failed to resolve Plural");
}
int32_t len = uplrules_selectFormatted(m_icuPluralRules, formattedNumber.get(), nullptr, 0, &status);
UChar* buf = ALLOCA((len + 1) * sizeof(UChar), UChar);
status = U_ZERO_ERROR;
uplrules_selectWithFormat(m_icuPluralRules, number, m_icuNumberFormat, buf, len + 1, &status);
uplrules_selectFormatted(m_icuPluralRules, formattedNumber.get(), buf, len + 1, &status);
ASSERT(U_SUCCESS(status));
if (U_FAILURE(status)) {
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, "Failed to resolve Plural");
}
return new UTF16String(buf, len);
}

View file

@ -36,7 +36,7 @@ public:
return true;
}
String* resolvePlural(double number);
String* resolvePlural(ExecutionState& state, double number);
String* locale() const
{
@ -48,55 +48,86 @@ public:
return m_type;
}
String* notation() const
{
return m_notation;
}
double minimumIntegerDigits() const
{
return m_minimumIntegerDigits;
}
double minimumFractionDigits() const
double roundingIncrement() const
{
return m_roundingIncrement;
}
Value minimumFractionDigits() const
{
return m_minimumFractionDigits;
}
double maximumFractionDigits() const
Value maximumFractionDigits() const
{
return m_maximumFractionDigits;
}
Optional<double> minimumSignificantDigits() const
Value minimumSignificantDigits() const
{
return m_minimumSignificantDigits;
}
Optional<double> maximumSignificantDigits() const
Value maximumSignificantDigits() const
{
return m_maximumSignificantDigits;
}
String* roundingMode() const
{
return m_roundingMode;
}
String* trailingZeroDisplay() const
{
return m_trailingZeroDisplay;
}
Intl::RoundingType roundingType() const
{
return m_roundingType;
}
Intl::RoundingPriority computedRoundingPriority() const
{
return m_computedRoundingPriority;
}
UPluralRules* icuPluralRules() const
{
return m_icuPluralRules;
}
UNumberFormat* icuNumberFormat() const
{
return m_icuNumberFormat;
}
void* operator new(size_t size);
void* operator new[](size_t size) = delete;
protected:
String* m_locale;
String* m_type;
String* m_notation;
double m_minimumIntegerDigits;
double m_minimumFractionDigits;
double m_maximumFractionDigits;
Optional<double> m_minimumSignificantDigits;
Optional<double> m_maximumSignificantDigits;
double m_roundingIncrement;
Value m_minimumFractionDigits; // double or undefined
Value m_maximumFractionDigits; // double or undefined
Value m_minimumSignificantDigits; // double or undefined
Value m_maximumSignificantDigits; // double or undefined
String* m_roundingMode;
String* m_trailingZeroDisplay;
Intl::RoundingType m_roundingType;
Intl::RoundingPriority m_computedRoundingPriority;
UPluralRules* m_icuPluralRules;
UNumberFormat* m_icuNumberFormat;
UNumberFormatter* m_icuNumberFormat;
};
} // namespace Escargot

View file

@ -179,6 +179,7 @@
#define uplrules_select RuntimeICUBinder::ICU::instance().uplrules_select
#define uplrules_getKeywords RuntimeICUBinder::ICU::instance().uplrules_getKeywords
#define uplrules_selectWithFormat RuntimeICUBinder::ICU::instance().uplrules_selectWithFormat
#define uplrules_selectFormatted RuntimeICUBinder::ICU::instance().uplrules_selectFormatted
#define ures_open RuntimeICUBinder::ICU::instance().ures_open
#define ures_openDirect RuntimeICUBinder::ICU::instance().ures_openDirect

View file

@ -169,6 +169,7 @@ namespace RuntimeICUBinder {
F(uplrules_open, UPluralRules* (*)(const char* locale, UErrorCode* status), UPluralRules*) \
F(uplrules_openForType, UPluralRules* (*)(const char* locale, UPluralType type, UErrorCode* status), UPluralRules*) \
F(uplrules_selectWithFormat, int32_t (*)(const UPluralRules* uplrules, double number, const UNumberFormat* fmt, UChar* keyword, int32_t capacity, UErrorCode* status), int32_t) \
F(uplrules_selectFormatted, int32_t (*)(const UPluralRules *uplrules, const struct UFormattedNumber* , UChar *, int32_t , UErrorCode *), int32_t) \
F(unumf_openForSkeletonAndLocale, UNumberFormatter* (*)(const UChar* skeleton, int32_t skeletonLen, const char* locale, UErrorCode* ec), UNumberFormatter*) \
F(unumf_openForSkeletonAndLocaleWithError, UNumberFormatter* (*)(const UChar* skeleton, int32_t skeletonLen, const char* locale, UParseError* perror, UErrorCode* ec), UNumberFormatter*) \
F(unumf_openResult, UFormattedNumber* (*)(UErrorCode * ec), UFormattedNumber*) \

View file

@ -4946,14 +4946,6 @@
<test id="intl402/Locale/prototype/variants/name"><reason>TODO</reason></test>
<test id="intl402/Locale/prototype/variants/prop-desc"><reason>TODO</reason></test>
<test id="intl402/Locale/reject-duplicate-variants-in-tlang"><reason>TODO</reason></test>
<test id="intl402/PluralRules/constructor-option-read-order"><reason>TODO</reason></test>
<test id="intl402/PluralRules/constructor-options-throwing-getters"><reason>TODO</reason></test>
<test id="intl402/PluralRules/default-options-object-prototype"><reason>TODO</reason></test>
<test id="intl402/PluralRules/notation"><reason>TODO</reason></test>
<test id="intl402/PluralRules/prototype/resolvedOptions/order"><reason>TODO</reason></test>
<test id="intl402/PluralRules/prototype/resolvedOptions/plural-categories-order"><reason>TODO</reason></test>
<test id="intl402/PluralRules/prototype/resolvedOptions/properties"><reason>TODO</reason></test>
<test id="intl402/PluralRules/prototype/select/notation"><reason>TODO</reason></test>
<test id="intl402/PluralRules/prototype/selectRange/default-en-us"><reason>TODO</reason></test>
<test id="intl402/PluralRules/prototype/selectRange/invoked-as-func"><reason>TODO</reason></test>
<test id="intl402/PluralRules/prototype/selectRange/length"><reason>TODO</reason></test>