(_nl_find_msg): Unlock the conversions_lock when we cannot recode the message.
[kopensolaris-gnu/glibc.git] / intl / plural.y
index c13dbaf..a1ffb9e 100644 (file)
 %{
 /* Expression parsing for plural form selection.
-   Copyright (C) 2000 Free Software Foundation, Inc.
+   Copyright (C) 2000, 2001 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
    Written by Ulrich Drepper <drepper@cygnus.com>, 2000.
 
    The GNU C Library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Library General Public License as
-   published by the Free Software Foundation; either version 2 of the
-   License, or (at your option) any later version.
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
 
    The GNU C Library 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
-   Library General Public License for more details.
-
-   You should have received a copy of the GNU Library General Public
-   License along with the GNU C Library; see the file COPYING.LIB.  If not,
-   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-   Boston, MA 02111-1307, USA.  */
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+/* The bison generated parser uses alloca.  AIX 3 forces us to put this
+   declaration at the beginning of the file.  The declaration in bison's
+   skeleton file comes too late.  This must come before <config.h>
+   because <config.h> may include arbitrary system headers.  */
+#if defined _AIX && !defined __GNUC__
+ #pragma alloca
+#endif
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
 
-#include <stdarg.h>
+#include <stddef.h>
 #include <stdlib.h>
-#include "gettext.h"
-#include "gettextP.h"
+#include <string.h>
+#include "plural-exp.h"
+
+/* The main function generated by the parser is called __gettextparse,
+   but we want it to be called PLURAL_PARSE.  */
+#ifndef _LIBC
+# define __gettextparse PLURAL_PARSE
+#endif
 
 #define YYLEX_PARAM    &((struct parse_args *) arg)->cp
 #define YYPARSE_PARAM  arg
 %}
 %pure_parser
-%expect 10
+%expect 7
 
 %union {
   unsigned long int num;
+  enum operator op;
   struct expression *exp;
 }
 
 %{
 /* Prototypes for local functions.  */
-static struct expression *new_exp (enum operator op, int n, ...);
-static int yylex (YYSTYPE *lval, const char **pexp);
-static void yyerror (const char *str);
+static struct expression *new_exp PARAMS ((int nargs, enum operator op,
+                                          struct expression * const *args));
+static inline struct expression *new_exp_0 PARAMS ((enum operator op));
+static inline struct expression *new_exp_1 PARAMS ((enum operator op,
+                                                  struct expression *right));
+static struct expression *new_exp_2 PARAMS ((enum operator op,
+                                            struct expression *left,
+                                            struct expression *right));
+static inline struct expression *new_exp_3 PARAMS ((enum operator op,
+                                                  struct expression *bexp,
+                                                  struct expression *tbranch,
+                                                  struct expression *fbranch));
+static int yylex PARAMS ((YYSTYPE *lval, const char **pexp));
+static void yyerror PARAMS ((const char *str));
+
+/* Allocation of expressions.  */
+
+static struct expression *
+new_exp (nargs, op, args)
+     int nargs;
+     enum operator op;
+     struct expression * const *args;
+{
+  int i;
+  struct expression *newp;
+
+  /* If any of the argument could not be malloc'ed, just return NULL.  */
+  for (i = nargs - 1; i >= 0; i--)
+    if (args[i] == NULL)
+      goto fail;
+
+  /* Allocate a new expression.  */
+  newp = (struct expression *) malloc (sizeof (*newp));
+  if (newp != NULL)
+    {
+      newp->nargs = nargs;
+      newp->operation = op;
+      for (i = nargs - 1; i >= 0; i--)
+       newp->val.args[i] = args[i];
+      return newp;
+    }
+
+ fail:
+  for (i = nargs - 1; i >= 0; i--)
+    FREE_EXPRESSION (args[i]);
+
+  return NULL;
+}
+
+static inline struct expression *
+new_exp_0 (op)
+     enum operator op;
+{
+  return new_exp (0, op, NULL);
+}
+
+static inline struct expression *
+new_exp_1 (op, right)
+     enum operator op;
+     struct expression *right;
+{
+  struct expression *args[1];
+
+  args[0] = right;
+  return new_exp (1, op, args);
+}
+
+static struct expression *
+new_exp_2 (op, left, right)
+     enum operator op;
+     struct expression *left;
+     struct expression *right;
+{
+  struct expression *args[2];
+
+  args[0] = left;
+  args[1] = right;
+  return new_exp (2, op, args);
+}
+
+static inline struct expression *
+new_exp_3 (op, bexp, tbranch, fbranch)
+     enum operator op;
+     struct expression *bexp;
+     struct expression *tbranch;
+     struct expression *fbranch;
+{
+  struct expression *args[3];
+
+  args[0] = bexp;
+  args[1] = tbranch;
+  args[2] = fbranch;
+  return new_exp (3, op, args);
+}
+
 %}
 
-%left '?'
-%left '|'
-%left '&'
-%left '=', '!'
-%left '+', '-'
-%left '*', '/', '%'
+/* This declares that all operators have the same associativity and the
+   precedence order as in C.  See [Harbison, Steele: C, A Reference Manual].
+   There is no unary minus and no bitwise operators.
+   Operators with the same syntactic behaviour have been merged into a single
+   token, to save space in the array generated by bison.  */
+%right '?'             /*   ?          */
+%left '|'              /*   ||         */
+%left '&'              /*   &&         */
+%left EQUOP2           /*   == !=      */
+%left CMPOP2           /*   < > <= >=  */
+%left ADDOP2           /*   + -        */
+%left MULOP2           /*   * / %      */
+%right '!'             /*   !          */
+
+%token <op> EQUOP2 CMPOP2 ADDOP2 MULOP2
 %token <num> NUMBER
 %type <exp> exp
 
@@ -54,143 +174,81 @@ static void yyerror (const char *str);
 
 start:   exp
          {
+           if ($1 == NULL)
+             YYABORT;
            ((struct parse_args *) arg)->res = $1;
          }
        ;
 
 exp:     exp '?' exp ':' exp
          {
-           if (($$ = new_exp (qmop, 3, $1, $3, $5)) == NULL)
-             YYABORT
+           $$ = new_exp_3 (qmop, $1, $3, $5);
          }
        | exp '|' exp
          {
-           if (($$ = new_exp (lor, 2, $1, $3)) == NULL)
-             YYABORT
+           $$ = new_exp_2 (lor, $1, $3);
          }
        | exp '&' exp
          {
-           if (($$ = new_exp (land, 2, $1, $3)) == NULL)
-             YYABORT
-         }
-       | exp '=' exp
-         {
-           if (($$ = new_exp (equal, 2, $1, $3)) == NULL)
-             YYABORT
+           $$ = new_exp_2 (land, $1, $3);
          }
-       | exp '!' exp
+       | exp EQUOP2 exp
          {
-           if (($$ = new_exp (not_equal, 2, $1, $3)) == NULL)
-             YYABORT
+           $$ = new_exp_2 ($2, $1, $3);
          }
-       | exp '+' exp
+       | exp CMPOP2 exp
          {
-           if (($$ = new_exp (plus, 2, $1, $3)) == NULL)
-             YYABORT
+           $$ = new_exp_2 ($2, $1, $3);
          }
-       | exp '-' exp
+       | exp ADDOP2 exp
          {
-           if (($$ = new_exp (minus, 2, $1, $3)) == NULL)
-             YYABORT
+           $$ = new_exp_2 ($2, $1, $3);
          }
-       | exp '*' exp
+       | exp MULOP2 exp
          {
-           if (($$ = new_exp (mult, 2, $1, $3)) == NULL)
-             YYABORT
+           $$ = new_exp_2 ($2, $1, $3);
          }
-       | exp '/' exp
+       | '!' exp
          {
-           if (($$ = new_exp (divide, 2, $1, $3)) == NULL)
-             YYABORT
-         }
-       | exp '%' exp
-         {
-           if (($$ = new_exp (module, 2, $1, $3)) == NULL)
-             YYABORT
+           $$ = new_exp_1 (lnot, $2);
          }
        | 'n'
          {
-           if (($$ = new_exp (var, 0)) == NULL)
-             YYABORT
+           $$ = new_exp_0 (var);
          }
        | NUMBER
          {
-           if (($$ = new_exp (num, 0)) == NULL)
-             YYABORT;
-           $$->val.num = $1
+           if (($$ = new_exp_0 (num)) != NULL)
+             $$->val.num = $1;
          }
        | '(' exp ')'
          {
-           $$ = $2
+           $$ = $2;
          }
        ;
 
 %%
 
-static struct expression *
-new_exp (enum operator op, int n, ...)
-{
-  struct expression *newp = (struct expression *) calloc (1, sizeof (*newp));
-  va_list va;
-
-  va_start (va, n);
-
-  if (newp == NULL)
-    while (n-- > 0)
-      __gettext_free_exp (va_arg (va, struct expression *));
-  else
-    {
-      newp->operation = op;
-      if (n > 0)
-       {
-         newp->val.args3.bexp = va_arg (va, struct expression *);
-         newp->val.args3.tbranch = va_arg (va, struct expression *);
-
-         if (n > 2)
-           newp->val.args3.fbranch = va_arg (va, struct expression *);
-
-         if (newp->val.args3.bexp == NULL
-             || newp->val.args3.tbranch == NULL
-             || (n > 2 && newp->val.args3.fbranch == NULL))
-           {
-             __gettext_free_exp (newp);
-             newp = NULL;
-           }
-       }
-    }
-
-  va_end (va);
-
-  return newp;
-}
-
 void
 internal_function
-__gettext_free_exp (struct expression *exp)
+FREE_EXPRESSION (exp)
+     struct expression *exp;
 {
   if (exp == NULL)
     return;
 
   /* Handle the recursive case.  */
-  switch (exp->operation)
+  switch (exp->nargs)
     {
-    case qmop:
-      __gettext_free_exp (exp->val.args3.fbranch);
+    case 3:
+      FREE_EXPRESSION (exp->val.args[2]);
+      /* FALLTHROUGH */
+    case 2:
+      FREE_EXPRESSION (exp->val.args[1]);
+      /* FALLTHROUGH */
+    case 1:
+      FREE_EXPRESSION (exp->val.args[0]);
       /* FALLTHROUGH */
-
-    case mult:
-    case divide:
-    case module:
-    case plus:
-    case minus:
-    case equal:
-    case not_equal:
-    case land:
-    case lor:
-      __gettext_free_exp (exp->val.args2.right);
-      __gettext_free_exp (exp->val.args2.left);
-      break;
-
     default:
       break;
     }
@@ -200,19 +258,15 @@ __gettext_free_exp (struct expression *exp)
 
 
 static int
-yylex (YYSTYPE *lval, const char **pexp)
+yylex (lval, pexp)
+     YYSTYPE *lval;
+     const char **pexp;
 {
   const char *exp = *pexp;
   int result;
 
   while (1)
     {
-      if (exp[0] == '\\' && exp[1] == '\n')
-       {
-         exp += 2;
-         continue;
-       }
-
       if (exp[0] == '\0')
        {
          *pexp = exp;
@@ -228,9 +282,10 @@ yylex (YYSTYPE *lval, const char **pexp)
   result = *exp++;
   switch (result)
     {
-    case '0' ... '9':
+    case '0': case '1': case '2': case '3': case '4':
+    case '5': case '6': case '7': case '8': case '9':
       {
-       unsigned long int n = exp[-1] - '0';
+       unsigned long int n = result - '0';
        while (exp[0] >= '0' && exp[0] <= '9')
          {
            n *= 10;
@@ -243,13 +298,25 @@ yylex (YYSTYPE *lval, const char **pexp)
       break;
 
     case '=':
-    case '!':
       if (exp[0] == '=')
-       ++exp;
+       {
+         ++exp;
+         lval->op = equal;
+         result = EQUOP2;
+       }
       else
        result = YYERRCODE;
       break;
 
+    case '!':
+      if (exp[0] == '=')
+       {
+         ++exp;
+         lval->op = not_equal;
+         result = EQUOP2;
+       }
+      break;
+
     case '&':
     case '|':
       if (exp[0] == result)
@@ -258,12 +325,54 @@ yylex (YYSTYPE *lval, const char **pexp)
        result = YYERRCODE;
       break;
 
-    case 'n':
+    case '<':
+      if (exp[0] == '=')
+       {
+         ++exp;
+         lval->op = less_or_equal;
+       }
+      else
+       lval->op = less_than;
+      result = CMPOP2;
+      break;
+
+    case '>':
+      if (exp[0] == '=')
+       {
+         ++exp;
+         lval->op = greater_or_equal;
+       }
+      else
+       lval->op = greater_than;
+      result = CMPOP2;
+      break;
+
     case '*':
+      lval->op = mult;
+      result = MULOP2;
+      break;
+
     case '/':
+      lval->op = divide;
+      result = MULOP2;
+      break;
+
     case '%':
+      lval->op = module;
+      result = MULOP2;
+      break;
+
     case '+':
+      lval->op = plus;
+      result = ADDOP2;
+      break;
+
     case '-':
+      lval->op = minus;
+      result = ADDOP2;
+      break;
+
+    case 'n':
     case '?':
     case ':':
     case '(':
@@ -294,7 +403,8 @@ yylex (YYSTYPE *lval, const char **pexp)
 
 
 static void
-yyerror (const char *str)
+yyerror (str)
+     const char *str;
 {
   /* Do nothing.  We don't print error messages here.  */
 }