]> git.ipfire.org Git - thirdparty/glibc.git/blobdiff - intl/plural.y
Update copyright dates with scripts/update-copyrights.
[thirdparty/glibc.git] / intl / plural.y
index 00b6fccb4fd3d25e22630105a747507793ae282c..f69fba7c602e9f890f8e5c3b2cd7fe1c487714ea 100644 (file)
 %{
 /* Expression parsing for plural form selection.
-   Copyright (C) 2000 Free Software Foundation, Inc.
+   Copyright (C) 2000-2020 Free Software Foundation, Inc.
    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.
+   This program is free software: you can redistribute it and/or 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,
+   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
-   Library General Public License for more details.
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public License
+   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
+
+/* For bison < 2.0, 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.
+   This can go away once the AM_INTL_SUBDIR macro requires bison >= 2.0.  */
+#if defined _AIX && !defined __GNUC__
+ #pragma alloca
+#endif
 
-   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.  */
+#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
+%parse-param {struct parse_args *arg}
+%lex-param {struct parse_args *arg}
+%define api.pure full
+%expect 7
 
 %union {
   unsigned long int num;
+  enum expression_operator op;
   struct expression *exp;
 }
 
 %{
 /* Prototypes for local functions.  */
-static struct expression *new_exp (enum operator op, ...);
-static int yylex (YYSTYPE *lval, const char **pexp);
-static void yyerror (const char *str);
+static int yylex (YYSTYPE *lval, struct parse_args *arg);
+static void yyerror (struct parse_args *arg, const char *str);
+
+/* Allocation of expressions.  */
+
+static struct expression *
+new_exp (int nargs, enum expression_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 (enum expression_operator op)
+{
+  return new_exp (0, op, NULL);
+}
+
+static inline struct expression *
+new_exp_1 (enum expression_operator op, struct expression *right)
+{
+  struct expression *args[1];
+
+  args[0] = right;
+  return new_exp (1, op, args);
+}
+
+static struct expression *
+new_exp_2 (enum expression_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 (enum expression_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,141 +151,79 @@ static void yyerror (const char *str);
 
 start:   exp
          {
-           ((struct parse_args *) arg)->res = $1;
+           if ($1 == NULL)
+             YYABORT;
+           arg->res = $1;
          }
        ;
 
 exp:     exp '?' exp ':' exp
          {
-           if (($$ = new_exp (qmop, $1, $3, $5, NULL)) == NULL)
-             YYABORT
+           $$ = new_exp_3 (qmop, $1, $3, $5);
          }
        | exp '|' exp
          {
-           if (($$ = new_exp (lor, $1, $3, NULL)) == NULL)
-             YYABORT
+           $$ = new_exp_2 (lor, $1, $3);
          }
        | exp '&' exp
          {
-           if (($$ = new_exp (land, $1, $3, NULL)) == NULL)
-             YYABORT
+           $$ = new_exp_2 (land, $1, $3);
          }
-       | exp '=' exp
+       | exp EQUOP2 exp
          {
-           if (($$ = new_exp (equal, $1, $3, NULL)) == NULL)
-             YYABORT
+           $$ = new_exp_2 ($2, $1, $3);
          }
-       | exp '!' exp
+       | exp CMPOP2 exp
          {
-           if (($$ = new_exp (not_equal, $1, $3, NULL)) == NULL)
-             YYABORT
+           $$ = new_exp_2 ($2, $1, $3);
          }
-       | exp '+' exp
+       | exp ADDOP2 exp
          {
-           if (($$ = new_exp (plus, $1, $3, NULL)) == NULL)
-             YYABORT
+           $$ = new_exp_2 ($2, $1, $3);
          }
-       | exp '-' exp
+       | exp MULOP2 exp
          {
-           if (($$ = new_exp (minus, $1, $3, NULL)) == NULL)
-             YYABORT
+           $$ = new_exp_2 ($2, $1, $3);
          }
-       | exp '*' exp
+       | '!' exp
          {
-           if (($$ = new_exp (mult, $1, $3, NULL)) == NULL)
-             YYABORT
-         }
-       | exp '/' exp
-         {
-           if (($$ = new_exp (divide, $1, $3, NULL)) == NULL)
-             YYABORT
-         }
-       | exp '%' exp
-         {
-           if (($$ = new_exp (module, $1, $3, NULL)) == NULL)
-             YYABORT
+           $$ = new_exp_1 (lnot, $2);
          }
        | 'n'
          {
-           if (($$ = new_exp (var, NULL)) == NULL)
-             YYABORT
+           $$ = new_exp_0 (var);
          }
        | NUMBER
          {
-           if (($$ = new_exp (num, NULL)) == 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, ...)
-{
-  struct expression *newp = (struct expression *) malloc (sizeof (*newp));
-  va_list va;
-  struct expression *next;
-
-  va_start (va, op);
-
-  if (newp == NULL)
-    while ((next = va_arg (va, struct expression *)) != NULL)
-      __gettext_free_exp (next);
-  else
-    {
-      newp->operation = op;
-      next = va_arg (va, struct expression *);
-      if (next != NULL)
-       {
-         newp->val.args3.bexp = next;
-         next = va_arg (va, struct expression *);
-         if (next != NULL)
-           {
-             newp->val.args3.tbranch = next;
-             next = va_arg (va, struct expression *);
-             if (next != NULL)
-               newp->val.args3.fbranch = next;
-           }
-       }
-    }
-
-  va_end (va);
-
-  return newp;
-}
-
 void
-internal_function
-__gettext_free_exp (struct expression *exp)
+FREE_EXPRESSION (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;
     }
@@ -198,19 +233,20 @@ __gettext_free_exp (struct expression *exp)
 
 
 static int
-yylex (YYSTYPE *lval, const char **pexp)
+yylex (YYSTYPE *lval, struct parse_args *arg)
 {
-  const char *exp = *pexp;
+  const char *exp = arg->cp;
   int result;
 
   while (1)
     {
-      if (exp[0] == '\\' && exp[1] == '\n')
+      if (exp[0] == '\0')
        {
-         exp += 2;
-         continue;
+         arg->cp = exp;
+         return YYEOF;
        }
-      if (exp[0] != '\0' && exp[0] != ' ' && exp[0] != '\t')
+
+      if (exp[0] != ' ' && exp[0] != '\t')
        break;
 
       ++exp;
@@ -219,9 +255,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;
@@ -234,13 +271,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)
@@ -249,12 +298,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 '(':
@@ -262,6 +353,7 @@ yylex (YYSTYPE *lval, const char **pexp)
       /* Nothing, just return the character.  */
       break;
 
+    case ';':
     case '\n':
     case '\0':
       /* Be safe and let the user call this function again.  */
@@ -277,14 +369,14 @@ yylex (YYSTYPE *lval, const char **pexp)
       break;
     }
 
-  *pexp = exp;
+  arg->cp = exp;
 
   return result;
 }
 
 
 static void
-yyerror (const char *str)
+yyerror (struct parse_args *arg, const char *str)
 {
   /* Do nothing.  We don't print error messages here.  */
 }