facf0c92e0
Red Bear OS is a full fork. All sources must be available from git clone with zero network access. Removed gitignore rules that excluded fetched source trees under recipes/*/source/, local/recipes/kde/*/source/, local/recipes/qt/*/source/, and vendor source trees. Build artifacts (target/, build/, source.tar, *.o, *.so) remain excluded. 127291 files added — kernel, relibc, base, bootloader, pkgar, all KDE/Qt frameworks, mesa, wayland, DRM drivers, and every other recipe source.
697 lines
23 KiB
C
697 lines
23 KiB
C
/* Unicode CLDR plural rule parser and converter
|
|
Copyright (C) 2015, 2018-2020 Free Software Foundation, Inc.
|
|
|
|
This file was written by Daiki Ueno <ueno@gnu.org>, 2015.
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include <config.h>
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include "unistr.h"
|
|
#include "xalloc.h"
|
|
|
|
#include "cldr-plural-exp.h"
|
|
#include "cldr-plural.h"
|
|
|
|
/* The grammar of Unicode CLDR plural rules is defined at:
|
|
https://unicode.org/reports/tr35/tr35-numbers.html#Plural_rules_syntax
|
|
|
|
This implementation only supports the "preferred" form, which
|
|
doesn't support obsolete keywords "in", "is", "not", and "within".
|
|
|
|
Unlike gettext, CLDR allows an unsigned decimal value as an
|
|
operand, in addition to unsigned integers. For simplicity, we
|
|
treat decimal relations as if it has a constant truth value.
|
|
|
|
The implementation is largely based on the idea of Michele Locati's
|
|
cldr-to-gettext-plural-rules:
|
|
https://github.com/mlocati/cldr-to-gettext-plural-rules */
|
|
|
|
void
|
|
cldr_plural_range_free (struct cldr_plural_range_ty *range)
|
|
{
|
|
if (range->start != range->end)
|
|
free (range->start);
|
|
free (range->end);
|
|
free (range);
|
|
}
|
|
|
|
void
|
|
cldr_plural_range_list_free (struct cldr_plural_range_list_ty *ranges)
|
|
{
|
|
while (ranges->nitems-- > 0)
|
|
cldr_plural_range_free (ranges->items[ranges->nitems]);
|
|
free (ranges->items);
|
|
free (ranges);
|
|
}
|
|
|
|
void
|
|
cldr_plural_condition_free (struct cldr_plural_condition_ty *condition)
|
|
{
|
|
if (condition->type == CLDR_PLURAL_CONDITION_AND
|
|
|| condition->type == CLDR_PLURAL_CONDITION_OR)
|
|
{
|
|
cldr_plural_condition_free (condition->value.conditions[0]);
|
|
cldr_plural_condition_free (condition->value.conditions[1]);
|
|
}
|
|
else if (condition->type == CLDR_PLURAL_CONDITION_RELATION)
|
|
cldr_plural_relation_free (condition->value.relation);
|
|
free (condition);
|
|
}
|
|
|
|
void
|
|
cldr_plural_relation_free (struct cldr_plural_relation_ty *relation)
|
|
{
|
|
free (relation->expression);
|
|
cldr_plural_range_list_free (relation->ranges);
|
|
free (relation);
|
|
}
|
|
|
|
static void
|
|
cldr_plural_rule_free (struct cldr_plural_rule_ty *rule)
|
|
{
|
|
free (rule->name);
|
|
cldr_plural_condition_free (rule->condition);
|
|
free (rule);
|
|
}
|
|
|
|
void
|
|
cldr_plural_rule_list_free (struct cldr_plural_rule_list_ty *rules)
|
|
{
|
|
while (rules->nitems-- > 0)
|
|
cldr_plural_rule_free (rules->items[rules->nitems]);
|
|
free (rules->items);
|
|
free (rules);
|
|
}
|
|
|
|
struct cldr_plural_rule_list_ty *
|
|
cldr_plural_parse (const char *input)
|
|
{
|
|
struct cldr_plural_parse_args arg;
|
|
|
|
memset (&arg, 0, sizeof (struct cldr_plural_parse_args));
|
|
arg.cp = input;
|
|
arg.cp_end = input + strlen (input);
|
|
arg.result = XMALLOC (struct cldr_plural_rule_list_ty);
|
|
memset (arg.result, 0, sizeof (struct cldr_plural_rule_list_ty));
|
|
|
|
if (yyparse (&arg) != 0)
|
|
return NULL;
|
|
|
|
return arg.result;
|
|
}
|
|
|
|
#define OPERAND_ZERO_P(o) \
|
|
(((o)->type == CLDR_PLURAL_OPERAND_INTEGER \
|
|
&& (o)->value.ival == 0) \
|
|
|| ((o)->type == CLDR_PLURAL_OPERAND_DECIMAL \
|
|
&& (o)->value.dval.d == 0))
|
|
|
|
static enum cldr_plural_condition
|
|
eval_relation (struct cldr_plural_relation_ty *relation)
|
|
{
|
|
switch (relation->expression->operand)
|
|
{
|
|
case 'n': case 'i':
|
|
{
|
|
/* Coerce decimal values in ranges into integers. */
|
|
size_t i;
|
|
for (i = 0; i < relation->ranges->nitems; i++)
|
|
{
|
|
struct cldr_plural_range_ty *range = relation->ranges->items[i];
|
|
if (range->start->type == CLDR_PLURAL_OPERAND_DECIMAL)
|
|
{
|
|
int truncated = (int) range->start->value.dval.d;
|
|
range->start->type = CLDR_PLURAL_OPERAND_INTEGER;
|
|
range->start->value.ival =
|
|
(range->start->value.dval.d == truncated
|
|
? truncated
|
|
: truncated + 1);
|
|
}
|
|
if (range->end->type == CLDR_PLURAL_OPERAND_DECIMAL)
|
|
{
|
|
range->end->type = CLDR_PLURAL_OPERAND_INTEGER;
|
|
range->end->value.ival = (int) range->end->value.dval.d;
|
|
}
|
|
}
|
|
relation->expression->operand = 'i';
|
|
}
|
|
break;
|
|
case 'f': case 't':
|
|
case 'v': case 'w':
|
|
{
|
|
/* Since plural expression in gettext only supports unsigned
|
|
integer, turn relations whose operand is either 'f', 't',
|
|
'v', or 'w' into a constant truth value. */
|
|
/* FIXME: check mod? */
|
|
size_t i;
|
|
for (i = 0; i < relation->ranges->nitems; i++)
|
|
{
|
|
struct cldr_plural_range_ty *range = relation->ranges->items[i];
|
|
if ((relation->type == CLDR_PLURAL_RELATION_EQUAL
|
|
&& (!OPERAND_ZERO_P (range->start)
|
|
|| !OPERAND_ZERO_P (range->end)))
|
|
|| (relation->type == CLDR_PLURAL_RELATION_NOT_EQUAL
|
|
&& (OPERAND_ZERO_P (range->start)
|
|
|| OPERAND_ZERO_P (range->end))))
|
|
return CLDR_PLURAL_CONDITION_FALSE;
|
|
}
|
|
return CLDR_PLURAL_CONDITION_TRUE;
|
|
}
|
|
break;
|
|
}
|
|
return CLDR_PLURAL_CONDITION_RELATION;
|
|
}
|
|
|
|
static void
|
|
eval_condition (struct cldr_plural_condition_ty *condition)
|
|
{
|
|
if (condition->type == CLDR_PLURAL_CONDITION_AND)
|
|
{
|
|
eval_condition (condition->value.conditions[0]);
|
|
eval_condition (condition->value.conditions[1]);
|
|
|
|
if ((condition->value.conditions[0]->type
|
|
== CLDR_PLURAL_CONDITION_FALSE)
|
|
|| (condition->value.conditions[1]->type
|
|
== CLDR_PLURAL_CONDITION_FALSE))
|
|
{
|
|
cldr_plural_condition_free (condition->value.conditions[0]);
|
|
cldr_plural_condition_free (condition->value.conditions[1]);
|
|
condition->type = CLDR_PLURAL_CONDITION_FALSE;
|
|
}
|
|
else if ((condition->value.conditions[0]->type
|
|
== CLDR_PLURAL_CONDITION_TRUE)
|
|
&& (condition->value.conditions[1]->type
|
|
== CLDR_PLURAL_CONDITION_TRUE))
|
|
{
|
|
cldr_plural_condition_free (condition->value.conditions[0]);
|
|
cldr_plural_condition_free (condition->value.conditions[1]);
|
|
condition->type = CLDR_PLURAL_CONDITION_TRUE;
|
|
}
|
|
else if (condition->value.conditions[0]->type
|
|
== CLDR_PLURAL_CONDITION_TRUE)
|
|
{
|
|
struct cldr_plural_condition_ty *original =
|
|
condition->value.conditions[1];
|
|
cldr_plural_condition_free (condition->value.conditions[0]);
|
|
condition->type = condition->value.conditions[1]->type;
|
|
condition->value = condition->value.conditions[1]->value;
|
|
free (original);
|
|
}
|
|
else if (condition->value.conditions[1]->type
|
|
== CLDR_PLURAL_CONDITION_TRUE)
|
|
{
|
|
struct cldr_plural_condition_ty *original =
|
|
condition->value.conditions[0];
|
|
cldr_plural_condition_free (condition->value.conditions[1]);
|
|
condition->type = condition->value.conditions[0]->type;
|
|
condition->value = condition->value.conditions[0]->value;
|
|
free (original);
|
|
}
|
|
}
|
|
else if (condition->type == CLDR_PLURAL_CONDITION_OR)
|
|
{
|
|
eval_condition (condition->value.conditions[0]);
|
|
eval_condition (condition->value.conditions[1]);
|
|
|
|
if ((condition->value.conditions[0]->type
|
|
== CLDR_PLURAL_CONDITION_TRUE)
|
|
|| (condition->value.conditions[1]->type
|
|
== CLDR_PLURAL_CONDITION_TRUE))
|
|
{
|
|
cldr_plural_condition_free (condition->value.conditions[0]);
|
|
cldr_plural_condition_free (condition->value.conditions[1]);
|
|
condition->type = CLDR_PLURAL_CONDITION_TRUE;
|
|
}
|
|
else if ((condition->value.conditions[0]->type
|
|
== CLDR_PLURAL_CONDITION_FALSE)
|
|
&& (condition->value.conditions[1]->type
|
|
== CLDR_PLURAL_CONDITION_FALSE))
|
|
{
|
|
cldr_plural_condition_free (condition->value.conditions[0]);
|
|
cldr_plural_condition_free (condition->value.conditions[1]);
|
|
condition->type = CLDR_PLURAL_CONDITION_FALSE;
|
|
}
|
|
else if (condition->value.conditions[0]->type
|
|
== CLDR_PLURAL_CONDITION_FALSE)
|
|
{
|
|
struct cldr_plural_condition_ty *original =
|
|
condition->value.conditions[1];
|
|
cldr_plural_condition_free (condition->value.conditions[0]);
|
|
condition->type = condition->value.conditions[1]->type;
|
|
condition->value = condition->value.conditions[1]->value;
|
|
free (original);
|
|
}
|
|
else if (condition->value.conditions[1]->type
|
|
== CLDR_PLURAL_CONDITION_FALSE)
|
|
{
|
|
struct cldr_plural_condition_ty *original =
|
|
condition->value.conditions[0];
|
|
cldr_plural_condition_free (condition->value.conditions[1]);
|
|
condition->type = condition->value.conditions[0]->type;
|
|
condition->value = condition->value.conditions[0]->value;
|
|
free (original);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
enum cldr_plural_condition value =
|
|
eval_relation (condition->value.relation);
|
|
if (value == CLDR_PLURAL_CONDITION_TRUE
|
|
|| value == CLDR_PLURAL_CONDITION_FALSE)
|
|
{
|
|
cldr_plural_relation_free (condition->value.relation);
|
|
condition->type = value;
|
|
}
|
|
}
|
|
}
|
|
|
|
#define MAX(a,b) ((a) > (b) ? (a) : (b))
|
|
|
|
static int
|
|
find_largest_modulus (struct cldr_plural_condition_ty *condition)
|
|
{
|
|
if (condition->type == CLDR_PLURAL_CONDITION_AND
|
|
|| condition->type == CLDR_PLURAL_CONDITION_OR)
|
|
{
|
|
int modulus0 =
|
|
find_largest_modulus (condition->value.conditions[0]);
|
|
int modulus1 =
|
|
find_largest_modulus (condition->value.conditions[1]);
|
|
return MAX (modulus0, modulus1);
|
|
}
|
|
else if (condition->type == CLDR_PLURAL_CONDITION_RELATION)
|
|
return condition->value.relation->expression->mod;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
find_largest_number (struct cldr_plural_condition_ty *condition)
|
|
{
|
|
if (condition->type == CLDR_PLURAL_CONDITION_AND
|
|
|| condition->type == CLDR_PLURAL_CONDITION_OR)
|
|
{
|
|
int number0 =
|
|
find_largest_number (condition->value.conditions[0]);
|
|
int number1 =
|
|
find_largest_number (condition->value.conditions[1]);
|
|
return MAX (number0, number1);
|
|
}
|
|
else if (condition->type == CLDR_PLURAL_CONDITION_RELATION)
|
|
{
|
|
int number = 0;
|
|
size_t i;
|
|
for (i = 0; i < condition->value.relation->ranges->nitems; i++)
|
|
{
|
|
struct cldr_plural_operand_ty *operand;
|
|
|
|
operand = condition->value.relation->ranges->items[i]->end;
|
|
if (operand->type == CLDR_PLURAL_OPERAND_INTEGER
|
|
&& operand->value.ival > number)
|
|
number = operand->value.ival;
|
|
else if (operand->type == CLDR_PLURAL_OPERAND_DECIMAL
|
|
&& operand->value.dval.d > number)
|
|
number = (int) operand->value.dval.d;
|
|
}
|
|
return number;
|
|
}
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
static bool
|
|
apply_condition (struct cldr_plural_condition_ty *condition, int value)
|
|
{
|
|
if (condition->type == CLDR_PLURAL_CONDITION_AND)
|
|
return apply_condition (condition->value.conditions[0], value)
|
|
&& apply_condition (condition->value.conditions[1], value);
|
|
else if (condition->type == CLDR_PLURAL_CONDITION_OR)
|
|
return apply_condition (condition->value.conditions[0], value)
|
|
|| apply_condition (condition->value.conditions[1], value);
|
|
else if (condition->type == CLDR_PLURAL_CONDITION_RELATION)
|
|
{
|
|
struct cldr_plural_relation_ty *relation = condition->value.relation;
|
|
int number = value;
|
|
size_t i;
|
|
|
|
if (relation->expression->mod > 0)
|
|
number %= relation->expression->mod;
|
|
for (i = 0; i < relation->ranges->nitems; i++)
|
|
{
|
|
struct cldr_plural_range_ty *range = relation->ranges->items[i];
|
|
if (range->start->value.ival <= number
|
|
&& number <= range->end->value.ival)
|
|
return relation->type == CLDR_PLURAL_RELATION_EQUAL;
|
|
}
|
|
return relation->type != CLDR_PLURAL_RELATION_EQUAL;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static void
|
|
print_expression (struct cldr_plural_expression_ty *expression, bool space,
|
|
FILE *fp)
|
|
{
|
|
if (expression->mod == 0)
|
|
fprintf (fp, "n");
|
|
else
|
|
fprintf (fp, space ? "n %% %d" : "n%%%d", expression->mod);
|
|
}
|
|
|
|
static void
|
|
print_relation (struct cldr_plural_relation_ty *relation,
|
|
enum cldr_plural_condition parent, bool space,
|
|
FILE *fp)
|
|
{
|
|
if (relation->type == CLDR_PLURAL_RELATION_EQUAL)
|
|
{
|
|
size_t i;
|
|
if (parent == CLDR_PLURAL_CONDITION_AND
|
|
&& relation->ranges->nitems > 1)
|
|
fputc ('(', fp);
|
|
for (i = 0; i < relation->ranges->nitems; i++)
|
|
{
|
|
struct cldr_plural_range_ty *range = relation->ranges->items[i];
|
|
if (i > 0)
|
|
fprintf (fp, " || ");
|
|
if (range->start->value.ival == range->end->value.ival)
|
|
{
|
|
print_expression (relation->expression, space, fp);
|
|
fprintf (fp,
|
|
(space && relation->ranges->nitems == 1
|
|
? " == %d" : "==%d"),
|
|
range->start->value.ival);
|
|
}
|
|
else if (range->start->value.ival == 0)
|
|
{
|
|
print_expression (relation->expression, false, fp);
|
|
fprintf (fp, "<=%d", range->end->value.ival);
|
|
}
|
|
else
|
|
{
|
|
if (parent == CLDR_PLURAL_CONDITION_OR
|
|
|| relation->ranges->nitems > 1)
|
|
fputc ('(', fp);
|
|
print_expression (relation->expression, false, fp);
|
|
fprintf (fp, ">=%d", range->start->value.ival);
|
|
fprintf (fp, " && ");
|
|
print_expression (relation->expression, false, fp);
|
|
fprintf (fp, "<=%d", range->end->value.ival);
|
|
if (parent == CLDR_PLURAL_CONDITION_OR
|
|
|| relation->ranges->nitems > 1)
|
|
fputc (')', fp);
|
|
}
|
|
}
|
|
if (parent == CLDR_PLURAL_CONDITION_AND
|
|
&& relation->ranges->nitems > 1)
|
|
fputc (')', fp);
|
|
}
|
|
else
|
|
{
|
|
size_t i;
|
|
if (parent == CLDR_PLURAL_CONDITION_OR
|
|
&& relation->ranges->nitems > 1)
|
|
fputc ('(', fp);
|
|
for (i = 0; i < relation->ranges->nitems; i++)
|
|
{
|
|
struct cldr_plural_range_ty *range = relation->ranges->items[i];
|
|
if (i > 0)
|
|
fprintf (fp," && ");
|
|
if (range->start->value.ival == range->end->value.ival)
|
|
{
|
|
print_expression (relation->expression, space, fp);
|
|
fprintf (fp, space && relation->ranges->nitems == 1
|
|
? " != %d" : "!=%d", range->start->value.ival);
|
|
}
|
|
else if (range->start->value.ival == 0)
|
|
{
|
|
print_expression (relation->expression, false, fp);
|
|
fprintf (fp, ">%d", range->end->value.ival);
|
|
}
|
|
else
|
|
{
|
|
if (parent == CLDR_PLURAL_CONDITION_AND
|
|
|| relation->ranges->nitems > 1)
|
|
fputc ('(', fp);
|
|
print_expression (relation->expression, false, fp);
|
|
fprintf (fp, "<%d", range->start->value.ival);
|
|
fprintf (fp, " || ");
|
|
print_expression (relation->expression, false, fp);
|
|
fprintf (fp, ">%d", range->end->value.ival);
|
|
if (parent == CLDR_PLURAL_CONDITION_AND
|
|
|| relation->ranges->nitems > 1)
|
|
fputc (')', fp);
|
|
}
|
|
}
|
|
if (parent == CLDR_PLURAL_CONDITION_OR
|
|
&& relation->ranges->nitems > 1)
|
|
fputc (')', fp);
|
|
}
|
|
}
|
|
|
|
static bool
|
|
print_condition (struct cldr_plural_condition_ty *condition,
|
|
enum cldr_plural_condition parent, bool space,
|
|
FILE *fp)
|
|
{
|
|
if (condition->type == CLDR_PLURAL_CONDITION_AND)
|
|
{
|
|
if (parent == CLDR_PLURAL_CONDITION_OR)
|
|
fputc ('(', fp);
|
|
print_condition (condition->value.conditions[0],
|
|
CLDR_PLURAL_CONDITION_AND, false,
|
|
fp);
|
|
fprintf (fp, " && ");
|
|
print_condition (condition->value.conditions[1],
|
|
CLDR_PLURAL_CONDITION_AND, false,
|
|
fp);
|
|
if (parent == CLDR_PLURAL_CONDITION_OR)
|
|
fputc (')', fp);
|
|
return true;
|
|
}
|
|
else if (condition->type == CLDR_PLURAL_CONDITION_OR)
|
|
{
|
|
if (parent == CLDR_PLURAL_CONDITION_AND)
|
|
fputc ('(', fp);
|
|
print_condition (condition->value.conditions[0],
|
|
CLDR_PLURAL_CONDITION_OR, false,
|
|
fp);
|
|
fprintf (fp, " || ");
|
|
print_condition (condition->value.conditions[1],
|
|
CLDR_PLURAL_CONDITION_OR, false,
|
|
fp);
|
|
if (parent == CLDR_PLURAL_CONDITION_AND)
|
|
fputc (')', fp);
|
|
return true;
|
|
}
|
|
else if (condition->type == CLDR_PLURAL_CONDITION_RELATION)
|
|
{
|
|
print_relation (condition->value.relation, parent, space, fp);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
#define RULE_PRINTABLE_P(r) \
|
|
((r)->condition->type != CLDR_PLURAL_CONDITION_TRUE \
|
|
&& (r)->condition->type != CLDR_PLURAL_CONDITION_FALSE)
|
|
|
|
/* Convert n == N into n != N. */
|
|
static bool
|
|
print_condition_negation (struct cldr_plural_condition_ty *condition, FILE *fp)
|
|
{
|
|
if (condition->type == CLDR_PLURAL_CONDITION_RELATION
|
|
&& condition->value.relation->type == CLDR_PLURAL_RELATION_EQUAL
|
|
&& condition->value.relation->ranges->nitems == 1
|
|
&& condition->value.relation->ranges->items[0]->start
|
|
== condition->value.relation->ranges->items[0]->end)
|
|
{
|
|
fprintf (fp, "nplurals=2; plural=(n != %d);\n",
|
|
condition->value.relation->ranges->items[0]->start->value.ival);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/* Convert n == 0,...,N into n > N. */
|
|
static bool
|
|
print_condition_greater (struct cldr_plural_condition_ty *condition, FILE *fp)
|
|
{
|
|
if (condition->type == CLDR_PLURAL_CONDITION_RELATION
|
|
&& condition->value.relation->type == CLDR_PLURAL_RELATION_EQUAL)
|
|
{
|
|
int last = -1;
|
|
size_t i;
|
|
for (i = 0; i < condition->value.relation->ranges->nitems; i++)
|
|
{
|
|
struct cldr_plural_range_ty *range =
|
|
condition->value.relation->ranges->items[i];
|
|
if (range->start->type != CLDR_PLURAL_OPERAND_INTEGER
|
|
|| range->end->type != CLDR_PLURAL_OPERAND_INTEGER
|
|
|| range->start->value.ival != last + 1)
|
|
break;
|
|
last = range->end->value.ival;
|
|
}
|
|
if (i == condition->value.relation->ranges->nitems)
|
|
{
|
|
struct cldr_plural_range_ty *range =
|
|
condition->value.relation->ranges->items[i - 1];
|
|
fprintf (fp, "nplurals=2; plural=(n > %d);\n",
|
|
range->end->value.ival);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
typedef bool (*print_condition_function_ty) (struct cldr_plural_condition_ty *,
|
|
FILE *);
|
|
static print_condition_function_ty print_condition_functions[] =
|
|
{
|
|
print_condition_negation,
|
|
print_condition_greater
|
|
};
|
|
|
|
#define SIZEOF(a) (sizeof(a) / sizeof(a[0]))
|
|
|
|
void
|
|
cldr_plural_rule_list_print (struct cldr_plural_rule_list_ty *rules, FILE *fp)
|
|
{
|
|
size_t i;
|
|
size_t count;
|
|
size_t nplurals;
|
|
int modulus_max = 0;
|
|
|
|
/* Prune trivial conditions. */
|
|
for (i = 0; i < rules->nitems; i++)
|
|
{
|
|
struct cldr_plural_rule_ty *rule = rules->items[i];
|
|
eval_condition (rule->condition);
|
|
}
|
|
|
|
/* Omit trivial rules (e.g., the last rule for "ru") with the
|
|
following algorithm:
|
|
1. From all rules, find the largest modulus M
|
|
2. Prepare a bit vector with M elements and initialize it with zeros
|
|
3. Loop over the rules, until all bits are set:
|
|
For each value in the range [1, M], apply a rule, and flip the
|
|
corresponding bit if it evaluates true */
|
|
|
|
/* Find the largest modulus. */
|
|
for (i = 0; i < rules->nitems; i++)
|
|
{
|
|
struct cldr_plural_rule_ty *rule = rules->items[i];
|
|
int modulus = find_largest_modulus (rule->condition);
|
|
int number = find_largest_number (rule->condition);
|
|
/* If the rule contains a range whose end is larger than
|
|
MODULUS, we can't use MODULUS as the upper bound. Skip
|
|
it. */
|
|
if (modulus >= number && modulus > modulus_max)
|
|
modulus_max = modulus;
|
|
}
|
|
|
|
if (modulus_max > 0)
|
|
{
|
|
bool *values = XNMALLOC (modulus_max, bool);
|
|
|
|
memset (values, 0, sizeof (bool) * modulus_max);
|
|
for (i = 0; i < rules->nitems; i++)
|
|
{
|
|
struct cldr_plural_rule_ty *rule = rules->items[i];
|
|
int j;
|
|
|
|
for (j = 0; j < modulus_max; j++)
|
|
{
|
|
bool result = apply_condition (rule->condition, j + 1);
|
|
if (result)
|
|
values[j] = true;
|
|
}
|
|
|
|
/* Check if all bits are set. Then we can omit one more rule. */
|
|
for (j = 0; j < modulus_max; j++)
|
|
if (values[j] == false)
|
|
break;
|
|
if (j == modulus_max)
|
|
break;
|
|
}
|
|
|
|
free (values);
|
|
|
|
while (i < rules->nitems)
|
|
cldr_plural_rule_free (rules->items[--rules->nitems]);
|
|
}
|
|
|
|
for (i = 0, nplurals = 1; i < rules->nitems; i++)
|
|
if (RULE_PRINTABLE_P (rules->items[i]))
|
|
nplurals++;
|
|
|
|
/* Special case when rules is empty. */
|
|
if (nplurals == 1)
|
|
{
|
|
fprintf (fp, "nplurals=1; plural=0;\n");
|
|
return;
|
|
}
|
|
|
|
/* If we have only one printable rule, apply some heuristics. */
|
|
if (nplurals == 2)
|
|
{
|
|
struct cldr_plural_condition_ty *condition;
|
|
size_t j;
|
|
|
|
for (j = 0; j < rules->nitems; j++)
|
|
if (RULE_PRINTABLE_P (rules->items[j]))
|
|
break;
|
|
|
|
condition = rules->items[j]->condition;
|
|
for (j = 0; j < SIZEOF (print_condition_functions); j++)
|
|
if (print_condition_functions[j] (condition, fp))
|
|
return;
|
|
}
|
|
|
|
/* If there are more printable rules, build a ternary operator. */
|
|
fprintf (fp, "nplurals=%lu; plural=(", (unsigned long) nplurals);
|
|
for (i = 0, count = 0; i < rules->nitems; i++)
|
|
{
|
|
struct cldr_plural_rule_ty *rule = rules->items[i];
|
|
if (print_condition (rule->condition,
|
|
CLDR_PLURAL_CONDITION_FALSE,
|
|
nplurals == 2,
|
|
fp)
|
|
&& rules->nitems > 1)
|
|
{
|
|
bool printable_left = false;
|
|
size_t j;
|
|
|
|
for (j = i + 1; j < rules->nitems; j++)
|
|
if (RULE_PRINTABLE_P (rules->items[j]))
|
|
printable_left = true;
|
|
|
|
if (i < rules->nitems - 1 && printable_left)
|
|
fprintf (fp, " ? %lu : ", (unsigned long) count++);
|
|
}
|
|
}
|
|
if (rules->nitems > 1)
|
|
fprintf (fp, " ? %lu : %lu",
|
|
(unsigned long) count, (unsigned long) (count + 1));
|
|
fprintf (fp, ");\n");
|
|
}
|