diff options
Diffstat (limited to 'gettext-tools/src/cldr-plural.y')
-rw-r--r-- | gettext-tools/src/cldr-plural.y | 469 |
1 files changed, 469 insertions, 0 deletions
diff --git a/gettext-tools/src/cldr-plural.y b/gettext-tools/src/cldr-plural.y new file mode 100644 index 0000000..d3128cd --- /dev/null +++ b/gettext-tools/src/cldr-plural.y @@ -0,0 +1,469 @@ +/* Unicode CLDR plural rule parser and converter + Copyright (C) 2015 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 <http://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" + +/* Prototypes for local functions. */ +static int yylex (YYSTYPE *lval, struct cldr_plural_parse_args *arg); +static void yyerror (struct cldr_plural_parse_args *arg, const char *str); + +/* Allocation of expressions. */ + +static struct cldr_plural_rule_ty * +new_rule (char *name, struct cldr_plural_condition_ty *condition) +{ + struct cldr_plural_rule_ty *result = + XMALLOC (struct cldr_plural_rule_ty); + result->name = name; + result->condition = condition; + return result; +} + +static struct cldr_plural_condition_ty * +new_leaf_condition (struct cldr_plural_relation_ty *relation) +{ + struct cldr_plural_condition_ty *result = + XMALLOC (struct cldr_plural_condition_ty); + result->type = CLDR_PLURAL_CONDITION_RELATION; + result->value.relation = relation; + return result; +} + +static struct cldr_plural_condition_ty * +new_branch_condition (enum cldr_plural_condition type, + struct cldr_plural_condition_ty *condition0, + struct cldr_plural_condition_ty *condition1) +{ + struct cldr_plural_condition_ty *result = + XMALLOC (struct cldr_plural_condition_ty); + result->type = type; + result->value.conditions[0] = condition0; + result->value.conditions[1] = condition1; + return result; +} + +static struct cldr_plural_relation_ty * +new_relation (struct cldr_plural_expression_ty *expression, + enum cldr_plural_relation type, + struct cldr_plural_range_list_ty *ranges) +{ + struct cldr_plural_relation_ty *result = + XMALLOC (struct cldr_plural_relation_ty); + result->expression = expression; + result->type = type; + result->ranges = ranges; + return result; +} + +static struct cldr_plural_expression_ty * +new_expression (int operand, int mod) +{ + struct cldr_plural_expression_ty *result = + XMALLOC (struct cldr_plural_expression_ty); + result->operand = operand; + result->mod = mod; + return result; +} + +static struct cldr_plural_range_list_ty * +add_range (struct cldr_plural_range_list_ty *ranges, + struct cldr_plural_range_ty *range) +{ + if (ranges->nitems == ranges->nitems_max) + { + ranges->nitems_max = ranges->nitems_max * 2 + 1; + ranges->items = xrealloc (ranges->items, + sizeof (struct cldr_plural_range_ty *) + * ranges->nitems_max); + } + ranges->items[ranges->nitems++] = range; + return ranges; +} + +static struct cldr_plural_range_ty * +new_range (struct cldr_plural_operand_ty *start, + struct cldr_plural_operand_ty *end) +{ + struct cldr_plural_range_ty *result = + XMALLOC (struct cldr_plural_range_ty); + result->start = start; + result->end = end; + return result; +} +%} + +%parse-param {struct cldr_plural_parse_args *arg} +%lex-param {struct cldr_plural_parse_args *arg} +%define api.pure full + +%union { + char *sval; + struct cldr_plural_condition_ty *cval; + struct cldr_plural_relation_ty *lval; + struct cldr_plural_expression_ty *eval; + struct cldr_plural_range_ty *gval; + struct cldr_plural_operand_ty *oval; + struct cldr_plural_range_list_ty *rval; + int ival; +} + +%destructor { free ($$); } <sval> +%destructor { cldr_plural_condition_free ($$); } <cval> +%destructor { cldr_plural_relation_free ($$); } <lval> +%destructor { free ($$); } <eval> +%destructor { cldr_plural_range_free ($$); } <gval> +%destructor { free ($$); } <oval> +%destructor { cldr_plural_range_list_free ($$); } <rval> +%destructor { } <ival> + +%token AND OR RANGE ELLIPSIS OTHER AT_INTEGER AT_DECIMAL +%token<sval> KEYWORD +%token<oval> INTEGER DECIMAL +%token<ival> OPERAND +%type<cval> condition and_condition +%type<lval> relation +%type<eval> expression +%type<gval> range range_or_integer +%type<rval> range_list + +%% + +rules: rule + | rules ';' rule + ; + +rule: KEYWORD ':' condition samples + { + struct cldr_plural_rule_ty *rule = new_rule ($1, $3); + struct cldr_plural_rule_list_ty *result = arg->result; + if (result->nitems == result->nitems_max) + { + result->nitems_max = result->nitems_max * 2 + 1; + result->items = xrealloc (result->items, + sizeof (struct cldr_plural_rule_ty *) + * result->nitems_max); + } + result->items[result->nitems++] = rule; + } + | OTHER ':' samples + ; + +condition: and_condition + { + $$ = $1; + } + | condition OR and_condition + { + $$ = new_branch_condition (CLDR_PLURAL_CONDITION_OR, $1, $3); + } + ; + +and_condition: relation + { + $$ = new_leaf_condition ($1); + } + | and_condition AND relation + { + $$ = new_branch_condition (CLDR_PLURAL_CONDITION_AND, + $1, + new_leaf_condition ($3)); + } + ; + +relation: expression '=' range_list + { + $$ = new_relation ($1, CLDR_PLURAL_RELATION_EQUAL, $3); + } + | expression '!' range_list + { + $$ = new_relation ($1, CLDR_PLURAL_RELATION_NOT_EQUAL, $3); + } + ; + +expression: OPERAND + { + $$ = new_expression ($1, 0); + } + | OPERAND '%' INTEGER + { + $$ = new_expression ($1, $3->value.ival); + } + ; + +range_list: range_or_integer + { + struct cldr_plural_range_list_ty *ranges = + XMALLOC (struct cldr_plural_range_list_ty); + memset (ranges, 0, sizeof (struct cldr_plural_range_list_ty)); + $$ = add_range (ranges, $1); + } + | range_list ',' range_or_integer + { + $$ = add_range ($1, $3); + } + ; + +range_or_integer: range + { + $$ = $1; + } + | INTEGER + { + $$ = new_range ($1, $1); + } + ; + +range: INTEGER RANGE INTEGER + { + $$ = new_range ($1, $3); + } + ; + +/* FIXME: collect samples */ +samples: at_integer at_decimal + ; + +at_integer: %empty + | AT_INTEGER sample_list + ; + +at_decimal: %empty + | AT_DECIMAL sample_list + ; + +sample_list: sample_list1 sample_ellipsis + ; +sample_list1: sample_range + | sample_list1 ',' sample_range + ; +sample_ellipsis: %empty + | ',' ELLIPSIS + ; + +sample_range: DECIMAL + { free ($1); } + | DECIMAL '~' DECIMAL + { free ($1); free ($3); } + | INTEGER + { free ($1); } + | INTEGER '~' INTEGER + { free ($1); free ($3); } + ; + +%% + +static int +yylex (YYSTYPE *lval, struct cldr_plural_parse_args *arg) +{ + const char *exp = arg->cp; + ucs4_t uc; + int length; + int result; + static char *buffer; + static size_t bufmax; + size_t bufpos; + + while (1) + { + if (exp[0] == '\0') + { + arg->cp = exp; + return YYEOF; + } + + if (exp[0] != ' ' && exp[0] != '\t') + break; + + ++exp; + } + + length = u8_mbtouc (&uc, (const uint8_t *) exp, arg->cp_end - exp); + if (uc == 0x2026) + { + arg->cp = exp + length; + return ELLIPSIS; + } + else if (strncmp ("...", exp, 3) == 0) + { + arg->cp = exp + 3; + return ELLIPSIS; + } + else if (strncmp ("..", exp, 2) == 0) + { + arg->cp = exp + 2; + return RANGE; + } + else if (strncmp ("other", exp, 5) == 0) + { + arg->cp = exp + 5; + return OTHER; + } + else if (strncmp ("@integer", exp, 8) == 0) + { + arg->cp = exp + 8; + return AT_INTEGER; + } + else if (strncmp ("@decimal", exp, 8) == 0) + { + arg->cp = exp + 8; + return AT_DECIMAL; + } + + result = *exp++; + switch (result) + { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + { + unsigned long int ival = result - '0'; + + while (exp[0] >= '0' && exp[0] <= '9') + { + ival *= 10; + ival += exp[0] - '0'; + ++exp; + } + + lval->oval = XMALLOC (struct cldr_plural_operand_ty); + if (exp[0] == '.' && exp[1] >= '0' && exp[1] <= '9') + { + double dval = ival; + int denominator = 10, nfractions = 0; + ++exp; + while (exp[0] >= '0' && exp[0] <= '9') + { + dval += (exp[0] - '0') / (double) denominator; + denominator *= 10; + ++nfractions; + ++exp; + } + lval->oval->type = CLDR_PLURAL_OPERAND_DECIMAL; + lval->oval->value.dval.d = dval; + lval->oval->value.dval.nfractions = nfractions; + result = DECIMAL; + } + else + { + lval->oval->type = CLDR_PLURAL_OPERAND_INTEGER; + lval->oval->value.ival = ival; + result = INTEGER; + } + } + break; + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': + case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': + case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': + case 'v': case 'w': case 'x': case 'y': case 'z': + bufpos = 0; + for (;;) + { + if (bufpos >= bufmax) + { + bufmax = 2 * bufmax + 10; + buffer = xrealloc (buffer, bufmax); + } + buffer[bufpos++] = result; + result = *exp; + switch (result) + { + case 'a': case 'b': case 'c': case 'd': case 'e': + case 'f': case 'g': case 'h': case 'i': case 'j': + case 'k': case 'l': case 'm': case 'n': case 'o': + case 'p': case 'q': case 'r': case 's': case 't': + case 'u': case 'v': case 'w': case 'x': case 'y': + case 'z': + ++exp; + continue; + default: + break; + } + break; + } + + if (bufpos >= bufmax) + { + bufmax = 2 * bufmax + 10; + buffer = xrealloc (buffer, bufmax); + } + buffer[bufpos] = '\0'; + + /* Operands. */ + if (bufpos == 1) + { + switch (buffer[0]) + { + case 'n': case 'i': case 'f': case 't': case 'v': case 'w': + arg->cp = exp; + lval->ival = buffer[0]; + return OPERAND; + default: + break; + } + } + + /* Keywords. */ + if (strcmp (buffer, "and") == 0) + { + arg->cp = exp; + return AND; + } + else if (strcmp (buffer, "or") == 0) + { + arg->cp = exp; + return OR; + } + + lval->sval = xstrdup (buffer); + result = KEYWORD; + break; + case '!': + if (exp[0] == '=') + { + ++exp; + result = '!'; + } + else + result = YYERRCODE; + break; + default: + break; + } + + arg->cp = exp; + + return result; +} + +static void +yyerror (struct cldr_plural_parse_args *arg, char const *s) +{ + fprintf (stderr, "%s\n", s); +} |