LibUnicode: Parse and generate per-locale plural rules from the CLDR
Plural rules in the CLDR are of the form:
"cs": {
"pluralRule-count-one": "i = 1 and v = 0 @integer 1",
"pluralRule-count-few": "i = 2..4 and v = 0 @integer 2~4",
"pluralRule-count-many": "v != 0 @decimal 0.0~1.5, 10.0, 100.0 ...",
"pluralRule-count-other": "@integer 0, 5~19, 100, 1000, 10000 ..."
}
The syntax is described here:
https://unicode.org/reports/tr35/tr35-numbers.html#Plural_rules_syntax
There are up to 2 sets of rules for each locale, a cardinal set and an
ordinal set. The approach here is to generate a C++ function for each
set of rules. Each condition in the rules (e.g. "i = 1 and v = 0") is
transpiled to a C++ if-statement within its function. Then lookup tables
are generated to match locales to their generated functions.
NOTE: -Wno-parentheses-equality is added to the LibUnicodeData compile
flags because the generated plural rules have lots of extra parentheses
(because e.g. we need to selectively negate and combine rules). The code
to generate only exactly the right number of parentheses is quite hairy,
so this just tells the compiler to ignore the extras.
2022-07-07 13:44:17 +00:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2022, Tim Flynn <trflynn89@serenityos.org>
|
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
|
|
*/
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include <AK/Assertions.h>
|
|
|
|
#include <AK/StringView.h>
|
|
|
|
#include <AK/Types.h>
|
|
|
|
#include <LibUnicode/Forward.h>
|
|
|
|
|
|
|
|
namespace Unicode {
|
|
|
|
|
|
|
|
enum class PluralForm {
|
|
|
|
Cardinal,
|
|
|
|
Ordinal,
|
|
|
|
};
|
|
|
|
|
2022-07-08 11:52:54 +00:00
|
|
|
enum class PluralCategory : u8 {
|
|
|
|
Other,
|
|
|
|
Zero,
|
|
|
|
One,
|
|
|
|
Two,
|
|
|
|
Few,
|
|
|
|
Many,
|
2022-07-08 14:16:43 +00:00
|
|
|
|
|
|
|
// https://unicode.org/reports/tr35/tr35-numbers.html#Explicit_0_1_rules
|
|
|
|
ExactlyZero,
|
|
|
|
ExactlyOne,
|
2022-07-08 11:52:54 +00:00
|
|
|
};
|
|
|
|
|
LibUnicode: Parse and generate per-locale plural rules from the CLDR
Plural rules in the CLDR are of the form:
"cs": {
"pluralRule-count-one": "i = 1 and v = 0 @integer 1",
"pluralRule-count-few": "i = 2..4 and v = 0 @integer 2~4",
"pluralRule-count-many": "v != 0 @decimal 0.0~1.5, 10.0, 100.0 ...",
"pluralRule-count-other": "@integer 0, 5~19, 100, 1000, 10000 ..."
}
The syntax is described here:
https://unicode.org/reports/tr35/tr35-numbers.html#Plural_rules_syntax
There are up to 2 sets of rules for each locale, a cardinal set and an
ordinal set. The approach here is to generate a C++ function for each
set of rules. Each condition in the rules (e.g. "i = 1 and v = 0") is
transpiled to a C++ if-statement within its function. Then lookup tables
are generated to match locales to their generated functions.
NOTE: -Wno-parentheses-equality is added to the LibUnicodeData compile
flags because the generated plural rules have lots of extra parentheses
(because e.g. we need to selectively negate and combine rules). The code
to generate only exactly the right number of parentheses is quite hairy,
so this just tells the compiler to ignore the extras.
2022-07-07 13:44:17 +00:00
|
|
|
// https://unicode.org/reports/tr35/tr35-numbers.html#Plural_Operand_Meanings
|
|
|
|
struct PluralOperands {
|
|
|
|
static constexpr StringView symbol_to_variable_name(char symbol)
|
|
|
|
{
|
|
|
|
if (symbol == 'n')
|
|
|
|
return "number"sv;
|
|
|
|
if (symbol == 'i')
|
|
|
|
return "integer_digits"sv;
|
|
|
|
if (symbol == 'f')
|
|
|
|
return "fraction_digits"sv;
|
|
|
|
if (symbol == 'v')
|
|
|
|
return "number_of_fraction_digits"sv;
|
|
|
|
if (symbol == 't')
|
|
|
|
return "fraction_digits_without_trailing"sv;
|
|
|
|
if (symbol == 'w')
|
|
|
|
return "number_of_fraction_digits_without_trailing"sv;
|
|
|
|
VERIFY_NOT_REACHED();
|
|
|
|
}
|
|
|
|
|
|
|
|
static constexpr bool symbol_requires_floating_point_modulus(char symbol)
|
|
|
|
{
|
|
|
|
// From TR-35: "The modulus (% or mod) is a remainder operation as defined in Java; for
|
|
|
|
// example, where n = 4.3 the result of n mod 3 is 1.3."
|
|
|
|
//
|
|
|
|
// So, this returns whether the symbol represents a decimal value, and thus requires fmod.
|
|
|
|
return symbol == 'n';
|
|
|
|
}
|
|
|
|
|
|
|
|
double number { 0 };
|
|
|
|
u64 integer_digits { 0 };
|
|
|
|
u64 fraction_digits { 0 };
|
|
|
|
u64 number_of_fraction_digits { 0 };
|
|
|
|
u64 fraction_digits_without_trailing { 0 };
|
|
|
|
u64 number_of_fraction_digits_without_trailing { 0 };
|
|
|
|
};
|
|
|
|
|
|
|
|
PluralForm plural_form_from_string(StringView plural_form);
|
|
|
|
StringView plural_form_to_string(PluralForm plural_form);
|
|
|
|
|
2022-07-08 11:52:54 +00:00
|
|
|
// NOTE: This must be defined inline to be callable from the code generators.
|
|
|
|
constexpr PluralCategory plural_category_from_string(StringView category)
|
|
|
|
{
|
|
|
|
if (category == "other"sv)
|
|
|
|
return PluralCategory::Other;
|
|
|
|
if (category == "zero"sv)
|
|
|
|
return PluralCategory::Zero;
|
|
|
|
if (category == "one"sv)
|
|
|
|
return PluralCategory::One;
|
|
|
|
if (category == "two"sv)
|
|
|
|
return PluralCategory::Two;
|
|
|
|
if (category == "few"sv)
|
|
|
|
return PluralCategory::Few;
|
|
|
|
if (category == "many"sv)
|
|
|
|
return PluralCategory::Many;
|
2022-07-08 14:16:43 +00:00
|
|
|
if (category == "0"sv)
|
|
|
|
return PluralCategory::ExactlyZero;
|
|
|
|
if (category == "1"sv)
|
|
|
|
return PluralCategory::ExactlyOne;
|
2022-07-08 11:52:54 +00:00
|
|
|
VERIFY_NOT_REACHED();
|
|
|
|
}
|
|
|
|
|
|
|
|
// NOTE: This must be defined inline to be callable from the code generators.
|
|
|
|
constexpr StringView plural_category_to_string(PluralCategory category)
|
|
|
|
{
|
|
|
|
switch (category) {
|
|
|
|
case PluralCategory::Other:
|
|
|
|
return "other"sv;
|
|
|
|
case PluralCategory::Zero:
|
|
|
|
return "zero"sv;
|
|
|
|
case PluralCategory::One:
|
|
|
|
return "one"sv;
|
|
|
|
case PluralCategory::Two:
|
|
|
|
return "two"sv;
|
|
|
|
case PluralCategory::Few:
|
|
|
|
return "few"sv;
|
|
|
|
case PluralCategory::Many:
|
|
|
|
return "many"sv;
|
2022-07-08 14:16:43 +00:00
|
|
|
case PluralCategory::ExactlyZero:
|
|
|
|
return "0"sv;
|
|
|
|
case PluralCategory::ExactlyOne:
|
|
|
|
return "1"sv;
|
2022-07-08 11:52:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
VERIFY_NOT_REACHED();
|
|
|
|
}
|
LibUnicode: Parse and generate per-locale plural rules from the CLDR
Plural rules in the CLDR are of the form:
"cs": {
"pluralRule-count-one": "i = 1 and v = 0 @integer 1",
"pluralRule-count-few": "i = 2..4 and v = 0 @integer 2~4",
"pluralRule-count-many": "v != 0 @decimal 0.0~1.5, 10.0, 100.0 ...",
"pluralRule-count-other": "@integer 0, 5~19, 100, 1000, 10000 ..."
}
The syntax is described here:
https://unicode.org/reports/tr35/tr35-numbers.html#Plural_rules_syntax
There are up to 2 sets of rules for each locale, a cardinal set and an
ordinal set. The approach here is to generate a C++ function for each
set of rules. Each condition in the rules (e.g. "i = 1 and v = 0") is
transpiled to a C++ if-statement within its function. Then lookup tables
are generated to match locales to their generated functions.
NOTE: -Wno-parentheses-equality is added to the LibUnicodeData compile
flags because the generated plural rules have lots of extra parentheses
(because e.g. we need to selectively negate and combine rules). The code
to generate only exactly the right number of parentheses is quite hairy,
so this just tells the compiler to ignore the extras.
2022-07-07 13:44:17 +00:00
|
|
|
|
|
|
|
PluralCategory determine_plural_category(StringView locale, PluralForm form, PluralOperands operands);
|
2022-07-07 16:05:05 +00:00
|
|
|
Span<PluralCategory const> available_plural_categories(StringView locale, PluralForm form);
|
LibUnicode: Parse and generate per-locale plural rules from the CLDR
Plural rules in the CLDR are of the form:
"cs": {
"pluralRule-count-one": "i = 1 and v = 0 @integer 1",
"pluralRule-count-few": "i = 2..4 and v = 0 @integer 2~4",
"pluralRule-count-many": "v != 0 @decimal 0.0~1.5, 10.0, 100.0 ...",
"pluralRule-count-other": "@integer 0, 5~19, 100, 1000, 10000 ..."
}
The syntax is described here:
https://unicode.org/reports/tr35/tr35-numbers.html#Plural_rules_syntax
There are up to 2 sets of rules for each locale, a cardinal set and an
ordinal set. The approach here is to generate a C++ function for each
set of rules. Each condition in the rules (e.g. "i = 1 and v = 0") is
transpiled to a C++ if-statement within its function. Then lookup tables
are generated to match locales to their generated functions.
NOTE: -Wno-parentheses-equality is added to the LibUnicodeData compile
flags because the generated plural rules have lots of extra parentheses
(because e.g. we need to selectively negate and combine rules). The code
to generate only exactly the right number of parentheses is quite hairy,
so this just tells the compiler to ignore the extras.
2022-07-07 13:44:17 +00:00
|
|
|
|
|
|
|
}
|