Skip to content

Commit

Permalink
ICU-21254 Add plural rule parsing for exponent operand in C++
Browse files Browse the repository at this point in the history
  • Loading branch information
echeran committed Sep 1, 2020
1 parent 196d5e1 commit 55993b8
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 0 deletions.
11 changes: 11 additions & 0 deletions icu4c/source/i18n/plurrule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ static const UChar PK_VAR_N[]={LOW_N,0};
static const UChar PK_VAR_I[]={LOW_I,0};
static const UChar PK_VAR_F[]={LOW_F,0};
static const UChar PK_VAR_T[]={LOW_T,0};
static const UChar PK_VAR_E[]={LOW_E,0};
static const UChar PK_VAR_V[]={LOW_V,0};
static const UChar PK_WITHIN[]={LOW_W,LOW_I,LOW_T,LOW_H,LOW_I,LOW_N,0};
static const UChar PK_DECIMAL[]={LOW_D,LOW_E,LOW_C,LOW_I,LOW_M,LOW_A,LOW_L,0};
Expand Down Expand Up @@ -600,6 +601,7 @@ PluralRuleParser::parse(const UnicodeString& ruleData, PluralRules *prules, UErr
case tVariableI:
case tVariableF:
case tVariableT:
case tVariableE:
case tVariableV:
U_ASSERT(curAndConstraint != nullptr);
curAndConstraint->digitsType = type;
Expand Down Expand Up @@ -984,6 +986,8 @@ static UnicodeString tokenString(tokenType tok) {
s.append(LOW_V); break;
case tVariableT:
s.append(LOW_T); break;
case tVariableE:
s.append(LOW_E); break;
default:
s.append(TILDE);
}
Expand Down Expand Up @@ -1160,6 +1164,7 @@ PluralRuleParser::checkSyntax(UErrorCode &status)
case tVariableI:
case tVariableF:
case tVariableT:
case tVariableE:
case tVariableV:
if (type != tIs && type != tMod && type != tIn &&
type != tNot && type != tWithin && type != tEqual && type != tNotEqual) {
Expand All @@ -1176,6 +1181,7 @@ PluralRuleParser::checkSyntax(UErrorCode &status)
type == tVariableI ||
type == tVariableF ||
type == tVariableT ||
type == tVariableE ||
type == tVariableV ||
type == tAt)) {
status = U_UNEXPECTED_TOKEN;
Expand Down Expand Up @@ -1207,6 +1213,7 @@ PluralRuleParser::checkSyntax(UErrorCode &status)
type != tVariableI &&
type != tVariableF &&
type != tVariableT &&
type != tVariableE &&
type != tVariableV) {
status = U_UNEXPECTED_TOKEN;
}
Expand Down Expand Up @@ -1384,6 +1391,8 @@ PluralRuleParser::getKeyType(const UnicodeString &token, tokenType keyType)
keyType = tVariableF;
} else if (0 == token.compare(PK_VAR_T, 1)) {
keyType = tVariableT;
} else if (0 == token.compare(PK_VAR_E, 1)) {
keyType = tVariableE;
} else if (0 == token.compare(PK_VAR_V, 1)) {
keyType = tVariableV;
} else if (0 == token.compare(PK_IS, 2)) {
Expand Down Expand Up @@ -1481,6 +1490,8 @@ PluralOperand tokenTypeToPluralOperand(tokenType tt) {
return PLURAL_OPERAND_V;
case tVariableT:
return PLURAL_OPERAND_T;
case tVariableE:
return PLURAL_OPERAND_E;
default:
UPRV_UNREACHABLE; // unexpected.
}
Expand Down
1 change: 1 addition & 0 deletions icu4c/source/i18n/plurrule_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ enum tokenType {
tVariableF,
tVariableV,
tVariableT,
tVariableE,
tDecimal,
tInteger,
tEOF
Expand Down
76 changes: 76 additions & 0 deletions icu4c/source/test/intltest/plurults.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include "number_decimalquantity.h"

using icu::number::impl::DecimalQuantity;
using namespace icu::number;

void setupResult(const int32_t testSource[], char result[], int32_t* max);
UBool checkEqual(const PluralRules &test, char *result, int32_t max);
Expand All @@ -49,6 +50,7 @@ void PluralRulesTest::runIndexedTest( int32_t index, UBool exec, const char* &na
TESTCASE_AUTO(testGetSamples);
TESTCASE_AUTO(testWithin);
TESTCASE_AUTO(testGetAllKeywordValues);
TESTCASE_AUTO(testCompactDecimalPluralKeyword);
TESTCASE_AUTO(testOrdinal);
TESTCASE_AUTO(testSelect);
TESTCASE_AUTO(testAvailbleLocales);
Expand Down Expand Up @@ -595,6 +597,80 @@ PluralRulesTest::testGetAllKeywordValues() {
}
}

void
PluralRulesTest::testCompactDecimalPluralKeyword() {
IcuTestErrorCode errorCode(*this, "testCompactDecimalPluralKeyword");

LocalPointer<PluralRules> rules(PluralRules::createRules(
"one: i = 0,1 @integer 0, 1 @decimal 0.0~1.5; many: e = 0 and i % 1000000 = 0 and v = 0 or e != 0 .. 5; other: @integer 2~17, 100, 1000, 10000, 100000, 1000000, @decimal 2.0~3.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …", errorCode));

const char* localeName = "fr-FR";
Locale locale = Locale::createFromName(localeName);

struct TestCase {
const char* skeleton;
const int input;
const char16_t* expectedFormattedOutput;
const char16_t* expectedPluralRuleKeyword;
} cases[] = {
// unlocalized formatter skeleton, input, string output, plural rule keyword
{"", 0, u"0", u"one"},
{"compact-long", 0, u"0", u"one"},

{"", 1, u"1", u"one"},
{"compact-long", 1, u"1", u"one"},

{"", 2, u"2", u"other"},
{"compact-long", 2, u"2", u"other"},

{"", 1000000, u"1 000 000", u"many"},
{"compact-long", 1000000, u"1 million", u"many"},

{"", 1000001, u"1 000 001", u"other"},
{"compact-long", 1000001, u"1 million", u"many"},

{"", 120000, u"1 200 000", u"other"},
{"compact-long", 1200000, u"1,2 millions", u"many"},

{"", 1200001, u"1 200 001", u"other"},
{"compact-long", 1200001, u"1,2 millions", u"many"},

{"", 2000000, u"2 000 000", u"many"},
{"compact-long", 2000000, u"2 millions", u"many"},
};
for (const auto& cas : cases) {
const char* skeleton = cas.skeleton;
const int input = cas.input;
const char16_t* expectedPluralRuleKeyword = cas.expectedPluralRuleKeyword;

UnicodeString actualPluralRuleKeyword =
getPluralKeyword(rules, locale, input, skeleton);

UnicodeString message(UnicodeString(localeName) + u" " + DoubleToUnicodeString(input));
assertEquals(message, expectedPluralRuleKeyword, actualPluralRuleKeyword);
}
}

UnicodeString PluralRulesTest::getPluralKeyword(const LocalPointer<PluralRules> &rules, Locale locale, double number, const char* skeleton) {
IcuTestErrorCode errorCode(*this, "getPluralKeyword");
UnlocalizedNumberFormatter ulnf = NumberFormatter::forSkeleton(skeleton, errorCode);
if (errorCode.errIfFailureAndReset("PluralRules::getPluralKeyword(<PluralRules>, <locale>, %d, %s) failed", number, skeleton)) {
return nullptr;
}
LocalizedNumberFormatter formatter = ulnf.locale(locale);

const FormattedNumber fn = formatter.formatDouble(number, errorCode);
if (errorCode.errIfFailureAndReset("NumberFormatter::formatDouble(%d) failed", number)) {
return nullptr;
}

UnicodeString pluralKeyword = rules->select(fn, errorCode);
if (errorCode.errIfFailureAndReset("PluralRules->select(FormattedNumber of %d) failed", number)) {
return nullptr;
}
return pluralKeyword;
}

void PluralRulesTest::testOrdinal() {
IcuTestErrorCode errorCode(*this, "testOrdinal");
LocalPointer<PluralRules> pr(PluralRules::forLocale("en", UPLURAL_TYPE_ORDINAL, errorCode));
Expand Down
2 changes: 2 additions & 0 deletions icu4c/source/test/intltest/plurults.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class PluralRulesTest : public IntlTest {
void testGetSamples();
void testWithin();
void testGetAllKeywordValues();
void testCompactDecimalPluralKeyword();
void testOrdinal();
void testSelect();
void testAvailbleLocales();
Expand All @@ -43,6 +44,7 @@ class PluralRulesTest : public IntlTest {
void assertRuleValue(const UnicodeString& rule, double expected);
void assertRuleKeyValue(const UnicodeString& rule, const UnicodeString& key,
double expected);
UnicodeString getPluralKeyword(const LocalPointer<PluralRules> &rules, Locale locale, double number, const char* skeleton);
void checkSelect(const LocalPointer<PluralRules> &rules, UErrorCode &status,
int32_t line, const char *keyword, ...);
void compareLocaleResults(const char* loc1, const char* loc2, const char* loc3);
Expand Down

0 comments on commit 55993b8

Please sign in to comment.