m4-commit
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Changes to m4/src/eval.c [branch-1_4]


From: Gary V . Vaughan
Subject: Changes to m4/src/eval.c [branch-1_4]
Date: Sun, 01 May 2005 07:54:16 -0400

Index: m4/src/eval.c
diff -u /dev/null m4/src/eval.c:1.1.1.1.2.1
--- /dev/null   Sun May  1 11:54:16 2005
+++ m4/src/eval.c       Sun May  1 11:54:12 2005
@@ -0,0 +1,777 @@
+/* GNU m4 -- A simple macro processor
+
+   Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994
+   Free Software Foundation, Inc.
+
+   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 2 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, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301  USA
+*/
+
+/* This file contains the functions to evaluate integer expressions for
+   the "eval" macro.  It is a little, fairly self-contained module, with
+   its own scanner, and a recursive descent parser.  The only entry point
+   is evaluate ().  */
+
+#include "m4.h"
+
+/* Evaluates token types.  */
+
+typedef enum eval_token
+  {
+    ERROR,
+    PLUS, MINUS,
+    EXPONENT,
+    TIMES, DIVIDE, MODULO,
+    EQ, NOTEQ, GT, GTEQ, LS, LSEQ,
+    LSHIFT, RSHIFT,
+    LNOT, LAND, LOR,
+    NOT, AND, OR, XOR,
+    LEFTP, RIGHTP,
+    NUMBER, EOTEXT
+  }
+eval_token;
+
+/* Error types.  */
+
+typedef enum eval_error
+  {
+    NO_ERROR,
+    MISSING_RIGHT,
+    SYNTAX_ERROR,
+    UNKNOWN_INPUT,
+    EXCESS_INPUT,
+    DIVIDE_ZERO,
+    MODULO_ZERO
+  }
+eval_error;
+
+static eval_error logical_or_term _((eval_token, eval_t *));
+static eval_error logical_and_term _((eval_token, eval_t *));
+static eval_error or_term _((eval_token, eval_t *));
+static eval_error xor_term _((eval_token, eval_t *));
+static eval_error and_term _((eval_token, eval_t *));
+static eval_error not_term _((eval_token, eval_t *));
+static eval_error logical_not_term _((eval_token, eval_t *));
+static eval_error cmp_term _((eval_token, eval_t *));
+static eval_error shift_term _((eval_token, eval_t *));
+static eval_error add_term _((eval_token, eval_t *));
+static eval_error mult_term _((eval_token, eval_t *));
+static eval_error exp_term _((eval_token, eval_t *));
+static eval_error unary_term _((eval_token, eval_t *));
+static eval_error simple_term _((eval_token, eval_t *));
+
+/*--------------------.
+| Lexical functions.  |
+`--------------------*/
+
+/* Pointer to next character of input text.  */
+static const char *eval_text;
+
+/* Value of eval_text, from before last call of eval_lex ().  This is so we
+   can back up, if we have read too much.  */
+static const char *last_text;
+
+static void
+eval_init_lex (const char *text)
+{
+  eval_text = text;
+  last_text = NULL;
+}
+
+static void
+eval_undo (void)
+{
+  eval_text = last_text;
+}
+
+/* VAL is numerical value, if any.  */
+
+static eval_token
+eval_lex (eval_t *val)
+{
+  while (isspace (*eval_text))
+    eval_text++;
+
+  last_text = eval_text;
+
+  if (*eval_text == '\0')
+    return EOTEXT;
+
+  if (isdigit (*eval_text))
+    {
+      int base, digit;
+
+      if (*eval_text == '0')
+       {
+         eval_text++;
+         switch (*eval_text)
+           {
+           case 'x':
+           case 'X':
+             base = 16;
+             eval_text++;
+             break;
+
+           case 'b':
+           case 'B':
+             base = 2;
+             eval_text++;
+             break;
+
+           case 'r':
+           case 'R':
+             base = 0;
+             eval_text++;
+             while (isdigit (*eval_text) && base <= 36)
+               base = 10 * base + *eval_text++ - '0';
+             if (base == 0 || base > 36 || *eval_text != ':')
+               return ERROR;
+             eval_text++;
+             break;
+
+           default:
+             base = 8;
+           }
+       }
+      else
+       base = 10;
+
+      (*val) = 0;
+      for (; *eval_text; eval_text++)
+       {
+         if (isdigit (*eval_text))
+           digit = *eval_text - '0';
+         else if (islower (*eval_text))
+           digit = *eval_text - 'a' + 10;
+         else if (isupper (*eval_text))
+           digit = *eval_text - 'A' + 10;
+         else
+           break;
+
+         if (digit >= base)
+           break;
+
+         (*val) = (*val) * base + digit;
+       }
+      return NUMBER;
+    }
+
+  switch (*eval_text++)
+    {
+    case '+':
+      return PLUS;
+    case '-':
+      return MINUS;
+    case '*':
+      if (*eval_text == '*')
+       {
+         eval_text++;
+         return EXPONENT;
+       }
+      else
+       return TIMES;
+    case '/':
+      return DIVIDE;
+    case '%':
+      return MODULO;
+    case '=':
+      if (*eval_text == '=')
+       eval_text++;
+      return EQ;
+    case '!':
+      if (*eval_text == '=')
+       {
+         eval_text++;
+         return NOTEQ;
+       }
+      else
+       return LNOT;
+    case '>':
+      if (*eval_text == '=')
+       {
+         eval_text++;
+         return GTEQ;
+       }
+      else if (*eval_text == '>')
+       {
+         eval_text++;
+         return RSHIFT;
+       }
+      else
+       return GT;
+    case '<':
+      if (*eval_text == '=')
+       {
+         eval_text++;
+         return LSEQ;
+       }
+      else if (*eval_text == '<')
+       {
+         eval_text++;
+         return LSHIFT;
+       }
+      else
+       return LS;
+    case '^':
+      return XOR;
+    case '~':
+      return NOT;
+    case '&':
+      if (*eval_text == '&')
+       {
+         eval_text++;
+         return LAND;
+       }
+      else
+       return AND;
+    case '|':
+      if (*eval_text == '|')
+       {
+         eval_text++;
+         return LOR;
+       }
+      else
+       return OR;
+    case '(':
+      return LEFTP;
+    case ')':
+      return RIGHTP;
+    default:
+      return ERROR;
+    }
+}
+
+/*---------------------------------------.
+| Main entry point, called from "eval".         |
+`---------------------------------------*/
+
+boolean
+evaluate (const char *expr, eval_t *val)
+{
+  eval_token et;
+  eval_error err;
+
+  eval_init_lex (expr);
+  et = eval_lex (val);
+  err = logical_or_term (et, val);
+
+  if (err == NO_ERROR && *eval_text != '\0')
+    err = EXCESS_INPUT;
+
+  switch (err)
+    {
+    case NO_ERROR:
+      break;
+
+    case MISSING_RIGHT:
+      M4ERROR ((warning_status, 0,
+               "Bad expression in eval (missing right parenthesis): %s",
+               expr));
+      break;
+
+    case SYNTAX_ERROR:
+      M4ERROR ((warning_status, 0,
+               "Bad expression in eval: %s", expr));
+      break;
+
+    case UNKNOWN_INPUT:
+      M4ERROR ((warning_status, 0,
+               "Bad expression in eval (bad input): %s", expr));
+      break;
+
+    case EXCESS_INPUT:
+      M4ERROR ((warning_status, 0,
+               "Bad expression in eval (excess input): %s", expr));
+      break;
+
+    case DIVIDE_ZERO:
+      M4ERROR ((warning_status, 0,
+               "Divide by zero in eval: %s", expr));
+      break;
+
+    case MODULO_ZERO:
+      M4ERROR ((warning_status, 0,
+               "Modulo by zero in eval: %s", expr));
+      break;
+
+    default:
+      M4ERROR ((warning_status, 0,
+               "INTERNAL ERROR: Bad error code in evaluate ()"));
+      abort ();
+    }
+
+  return (boolean) (err != NO_ERROR);
+}
+
+/*---------------------------.
+| Recursive descent parser.  |
+`---------------------------*/
+
+static eval_error
+logical_or_term (eval_token et, eval_t *v1)
+{
+  eval_t v2;
+  eval_error er;
+
+  if ((er = logical_and_term (et, v1)) != NO_ERROR)
+    return er;
+
+  while ((et = eval_lex (&v2)) == LOR)
+    {
+      et = eval_lex (&v2);
+      if (et == ERROR)
+       return UNKNOWN_INPUT;
+
+      if ((er = logical_and_term (et, &v2)) != NO_ERROR)
+       return er;
+
+      *v1 = *v1 || v2;
+    }
+  if (et == ERROR)
+    return UNKNOWN_INPUT;
+
+  eval_undo ();
+  return NO_ERROR;
+}
+
+static eval_error
+logical_and_term (eval_token et, eval_t *v1)
+{
+  eval_t v2;
+  eval_error er;
+
+  if ((er = or_term (et, v1)) != NO_ERROR)
+    return er;
+
+  while ((et = eval_lex (&v2)) == LAND)
+    {
+      et = eval_lex (&v2);
+      if (et == ERROR)
+       return UNKNOWN_INPUT;
+
+      if ((er = or_term (et, &v2)) != NO_ERROR)
+       return er;
+
+      *v1 = *v1 && v2;
+    }
+  if (et == ERROR)
+    return UNKNOWN_INPUT;
+
+  eval_undo ();
+  return NO_ERROR;
+}
+
+static eval_error
+or_term (eval_token et, eval_t *v1)
+{
+  eval_t v2;
+  eval_error er;
+
+  if ((er = xor_term (et, v1)) != NO_ERROR)
+    return er;
+
+  while ((et = eval_lex (&v2)) == OR)
+    {
+      et = eval_lex (&v2);
+      if (et == ERROR)
+       return UNKNOWN_INPUT;
+
+      if ((er = xor_term (et, &v2)) != NO_ERROR)
+       return er;
+
+      *v1 = *v1 | v2;
+    }
+  if (et == ERROR)
+    return UNKNOWN_INPUT;
+
+  eval_undo ();
+  return NO_ERROR;
+}
+
+static eval_error
+xor_term (eval_token et, eval_t *v1)
+{
+  eval_t v2;
+  eval_error er;
+
+  if ((er = and_term (et, v1)) != NO_ERROR)
+    return er;
+
+  while ((et = eval_lex (&v2)) == XOR)
+    {
+      et = eval_lex (&v2);
+      if (et == ERROR)
+       return UNKNOWN_INPUT;
+
+      if ((er = and_term (et, &v2)) != NO_ERROR)
+       return er;
+
+      *v1 = *v1 ^ v2;
+    }
+  if (et == ERROR)
+    return UNKNOWN_INPUT;
+
+  eval_undo ();
+  return NO_ERROR;
+}
+
+static eval_error
+and_term (eval_token et, eval_t *v1)
+{
+  eval_t v2;
+  eval_error er;
+
+  if ((er = not_term (et, v1)) != NO_ERROR)
+    return er;
+
+  while ((et = eval_lex (&v2)) == AND)
+    {
+      et = eval_lex (&v2);
+      if (et == ERROR)
+       return UNKNOWN_INPUT;
+
+      if ((er = not_term (et, &v2)) != NO_ERROR)
+       return er;
+
+      *v1 = *v1 & v2;
+    }
+  if (et == ERROR)
+    return UNKNOWN_INPUT;
+
+  eval_undo ();
+  return NO_ERROR;
+}
+
+static eval_error
+not_term (eval_token et, eval_t *v1)
+{
+  eval_error er;
+
+  if (et == NOT)
+    {
+      et = eval_lex (v1);
+      if (et == ERROR)
+       return UNKNOWN_INPUT;
+
+      if ((er = not_term (et, v1)) != NO_ERROR)
+       return er;
+      *v1 = ~*v1;
+    }
+  else
+    if ((er = logical_not_term (et, v1)) != NO_ERROR)
+      return er;
+
+  return NO_ERROR;
+}
+
+static eval_error
+logical_not_term (eval_token et, eval_t *v1)
+{
+  eval_error er;
+
+  if (et == LNOT)
+    {
+      et = eval_lex (v1);
+      if (et == ERROR)
+       return UNKNOWN_INPUT;
+
+      if ((er = logical_not_term (et, v1)) != NO_ERROR)
+       return er;
+      *v1 = !*v1;
+    }
+  else
+    if ((er = cmp_term (et, v1)) != NO_ERROR)
+      return er;
+
+  return NO_ERROR;
+}
+
+static eval_error
+cmp_term (eval_token et, eval_t *v1)
+{
+  eval_token op;
+  eval_t v2;
+  eval_error er;
+
+  if ((er = shift_term (et, v1)) != NO_ERROR)
+    return er;
+
+  while ((op = eval_lex (&v2)) == EQ || op == NOTEQ
+        || op == GT || op == GTEQ
+        || op == LS || op == LSEQ)
+    {
+
+      et = eval_lex (&v2);
+      if (et == ERROR)
+       return UNKNOWN_INPUT;
+
+      if ((er = shift_term (et, &v2)) != NO_ERROR)
+       return er;
+
+      switch (op)
+       {
+       case EQ:
+         *v1 = *v1 == v2;
+         break;
+
+       case NOTEQ:
+         *v1 = *v1 != v2;
+         break;
+
+       case GT:
+         *v1 = *v1 > v2;
+         break;
+
+       case GTEQ:
+         *v1 = *v1 >= v2;
+         break;
+
+       case LS:
+         *v1 = *v1 < v2;
+         break;
+
+       case LSEQ:
+         *v1 = *v1 <= v2;
+         break;
+
+       default:
+         M4ERROR ((warning_status, 0,
+                   "INTERNAL ERROR: Bad comparison operator in cmp_term ()"));
+         abort ();
+       }
+    }
+  if (op == ERROR)
+    return UNKNOWN_INPUT;
+
+  eval_undo ();
+  return NO_ERROR;
+}
+
+static eval_error
+shift_term (eval_token et, eval_t *v1)
+{
+  eval_token op;
+  eval_t v2;
+  eval_error er;
+
+  if ((er = add_term (et, v1)) != NO_ERROR)
+    return er;
+
+  while ((op = eval_lex (&v2)) == LSHIFT || op == RSHIFT)
+    {
+
+      et = eval_lex (&v2);
+      if (et == ERROR)
+       return UNKNOWN_INPUT;
+
+      if ((er = add_term (et, &v2)) != NO_ERROR)
+       return er;
+
+      switch (op)
+       {
+       case LSHIFT:
+         *v1 = *v1 << v2;
+         break;
+
+       case RSHIFT:
+         *v1 = *v1 >> v2;
+         break;
+
+       default:
+         M4ERROR ((warning_status, 0,
+                   "INTERNAL ERROR: Bad shift operator in shift_term ()"));
+         abort ();
+       }
+    }
+  if (op == ERROR)
+    return UNKNOWN_INPUT;
+
+  eval_undo ();
+  return NO_ERROR;
+}
+
+static eval_error
+add_term (eval_token et, eval_t *v1)
+{
+  eval_token op;
+  eval_t v2;
+  eval_error er;
+
+  if ((er = mult_term (et, v1)) != NO_ERROR)
+    return er;
+
+  while ((op = eval_lex (&v2)) == PLUS || op == MINUS)
+    {
+      et = eval_lex (&v2);
+      if (et == ERROR)
+       return UNKNOWN_INPUT;
+
+      if ((er = mult_term (et, &v2)) != NO_ERROR)
+       return er;
+
+      if (op == PLUS)
+       *v1 = *v1 + v2;
+      else
+       *v1 = *v1 - v2;
+    }
+  if (op == ERROR)
+    return UNKNOWN_INPUT;
+
+  eval_undo ();
+  return NO_ERROR;
+}
+
+static eval_error
+mult_term (eval_token et, eval_t *v1)
+{
+  eval_token op;
+  eval_t v2;
+  eval_error er;
+
+  if ((er = exp_term (et, v1)) != NO_ERROR)
+    return er;
+
+  while ((op = eval_lex (&v2)) == TIMES || op == DIVIDE || op == MODULO)
+    {
+      et = eval_lex (&v2);
+      if (et == ERROR)
+       return UNKNOWN_INPUT;
+
+      if ((er = exp_term (et, &v2)) != NO_ERROR)
+       return er;
+
+      switch (op)
+       {
+       case TIMES:
+         *v1 = *v1 * v2;
+         break;
+
+       case DIVIDE:
+         if (v2 == 0)
+           return DIVIDE_ZERO;
+         else
+           *v1 = *v1 / v2;
+         break;
+
+       case MODULO:
+         if (v2 == 0)
+           return MODULO_ZERO;
+         else
+           *v1 = *v1 % v2;
+         break;
+
+       default:
+         M4ERROR ((warning_status, 0,
+                   "INTERNAL ERROR: Bad operator in mult_term ()"));
+         abort ();
+       }
+    }
+  if (op == ERROR)
+    return UNKNOWN_INPUT;
+
+  eval_undo ();
+  return NO_ERROR;
+}
+
+static eval_error
+exp_term (eval_token et, eval_t *v1)
+{
+  register eval_t result;
+  eval_t v2;
+  eval_error er;
+
+  if ((er = unary_term (et, v1)) != NO_ERROR)
+    return er;
+  result = *v1;
+
+  while ((et = eval_lex (&v2)) == EXPONENT)
+    {
+      et = eval_lex (&v2);
+      if (et == ERROR)
+       return UNKNOWN_INPUT;
+
+      if ((er = exp_term (et, &v2)) != NO_ERROR)
+       return er;
+
+      result = 1;
+      while (v2-- > 0)
+       result *= *v1;
+      *v1 = result;
+    }
+  if (et == ERROR)
+    return UNKNOWN_INPUT;
+
+  eval_undo ();
+  return NO_ERROR;
+}
+
+static eval_error
+unary_term (eval_token et, eval_t *v1)
+{
+  eval_token et2 = et;
+  eval_error er;
+
+  if (et == PLUS || et == MINUS)
+    {
+      et2 = eval_lex (v1);
+      if (et2 == ERROR)
+       return UNKNOWN_INPUT;
+
+      if ((er = simple_term (et2, v1)) != NO_ERROR)
+       return er;
+
+      if (et == MINUS)
+       *v1 = -*v1;
+    }
+  else
+    if ((er = simple_term (et, v1)) != NO_ERROR)
+      return er;
+
+  return NO_ERROR;
+}
+
+static eval_error
+simple_term (eval_token et, eval_t *v1)
+{
+  eval_t v2;
+  eval_error er;
+
+  switch (et)
+    {
+    case LEFTP:
+      et = eval_lex (v1);
+      if (et == ERROR)
+       return UNKNOWN_INPUT;
+
+      if ((er = logical_or_term (et, v1)) != NO_ERROR)
+       return er;
+
+      et = eval_lex (&v2);
+      if (et == ERROR)
+       return UNKNOWN_INPUT;
+
+      if (et != RIGHTP)
+       return MISSING_RIGHT;
+
+      break;
+
+    case NUMBER:
+      break;
+
+    default:
+      return SYNTAX_ERROR;
+    }
+  return NO_ERROR;
+}




reply via email to

[Prev in Thread] Current Thread [Next in Thread]