/* GNU m4 -- A simple macro processor

   Copyright (C) 1989-1994, 2006-2007, 2009-2014, 2016-2017, 2020-2026
   Free Software Foundation, Inc.

   This file is part of GNU M4.

   GNU M4 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.

   GNU M4 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/>.
*/

/* 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.  */

#define MIN_PREC 1

typedef enum eval_token
{
  /* Value / 10 is precedence order, if >= MIN_PREC.  */
  ERROR = 0,
  BADNUM,
  BADOP,
  EOTEXT,
  LEFTP,
  RIGHTP,
  LNOT,
  NOT,
  NUMBER,
  LOR = 10,
  LAND = 20,
  OR = 30,
  XOR = 40,
  AND = 50,
  ASSIGN = 60,                  /* deprecated synonym to EQ */
  EQ,
  NOTEQ,
  GT = 70,
  GTEQ,
  LS,
  LSEQ,
  LSHIFT = 80,
  RSHIFT,
  PLUS = 90,                    /* precedence for binary op; also serves as a unary op */
  MINUS,                        /* precedence for binary op; also serves as a unary op */
  TIMES = 100,
  DIVIDE,
  MODULO,
  EXPONENT = 110
}
eval_token;

/* Error types.  */

typedef enum eval_error
{
  NO_ERROR,
  DIVIDE_ZERO,
  MODULO_ZERO,
  NEGATIVE_EXPONENT,
  /* All errors prior to SYNTAX_ERROR can be ignored in a dead
     branch of && and ||.  All errors after are just more details
     about a syntax error.  */
  SYNTAX_ERROR,
  MISSING_RIGHT,
  UNKNOWN_INPUT,
  EXCESS_INPUT,
  INVALID_NUMBER,
  INVALID_OPERATOR
}
eval_error;

static eval_error primary (int32_t *);
static eval_error parse_expr (int32_t *, eval_error, unsigned);

/*--------------------.
| 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, good for one token lookahead.  */
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 (int32_t *val)
{
  while (c_isspace (*eval_text))
    eval_text++;

  last_text = eval_text;

  if (*eval_text == '\0')
    return EOTEXT;

  if (c_isdigit (*eval_text))
    {
      unsigned int base, digit;
      /* The documentation says that "overflow silently results in wraparound".
         Therefore use an unsigned integer type to avoid undefined behaviour
         when parsing '-2147483648'.  */
      uint32_t value;
      bool seen_digit = false;

      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 (c_isdigit (*eval_text) && base <= 36)
                base = 10 * base + *eval_text++ - '0';
              if (base == 0 || base > 36 || *eval_text != ':')
                return BADNUM;
              eval_text++;
              break;

            default:
              base = 8;
              seen_digit = true;
            }
        }
      else
        base = 10;

      value = 0;
      for (; *eval_text; eval_text++, seen_digit = true)
        {
          if (c_isdigit (*eval_text))
            digit = *eval_text - '0';
          else if (c_islower (*eval_text))
            digit = *eval_text - 'a' + 10;
          else if (c_isupper (*eval_text))
            digit = *eval_text - 'A' + 10;
          else
            break;

          if (base == 1)
            {
              if (digit == 1)
                value++;
              else if (digit == 0 && value == 0)
                continue;
              else
                return BADNUM;
            }
          else if (digit >= base)
            return BADNUM;
          else
            value = value * base + digit;
        }
      *val = value;
      if (!seen_digit)
        return BADNUM;
      return NUMBER;
    }

  switch (*eval_text++)
    {
    case '+':
      if (*eval_text == '+' || *eval_text == '=')
        return BADOP;
      return PLUS;
    case '-':
      if (*eval_text == '-' || *eval_text == '=')
        return BADOP;
      return MINUS;
    case '*':
      if (*eval_text == '*')
        {
          eval_text++;
          return EXPONENT;
        }
      else if (*eval_text == '=')
        return BADOP;
      return TIMES;
    case '/':
      if (*eval_text == '=')
        return BADOP;
      return DIVIDE;
    case '%':
      if (*eval_text == '=')
        return BADOP;
      return MODULO;
    case '=':
      if (*eval_text == '=')
        {
          eval_text++;
          return EQ;
        }
      return ASSIGN;
    case '!':
      if (*eval_text == '=')
        {
          eval_text++;
          return NOTEQ;
        }
      return LNOT;
    case '>':
      if (*eval_text == '=')
        {
          eval_text++;
          return GTEQ;
        }
      else if (*eval_text == '>')
        {
          if (*++eval_text == '=')
            return BADOP;
          return RSHIFT;
        }
      return GT;
    case '<':
      if (*eval_text == '=')
        {
          eval_text++;
          return LSEQ;
        }
      else if (*eval_text == '<')
        {
          if (*++eval_text == '=')
            return BADOP;
          return LSHIFT;
        }
      return LS;
    case '^':
      if (*eval_text == '=')
        return BADOP;
      return XOR;
    case '~':
      return NOT;
    case '&':
      if (*eval_text == '&')
        {
          eval_text++;
          return LAND;
        }
      else if (*eval_text == '=')
        return BADOP;
      return AND;
    case '|':
      if (*eval_text == '|')
        {
          eval_text++;
          return LOR;
        }
      else if (*eval_text == '=')
        return BADOP;
      return OR;
    case '(':
      return LEFTP;
    case ')':
      return RIGHTP;
    default:
      return ERROR;
    }
}

/*-----------------------------------------------------.
| Operator precedence parser (based on Pratt parser).  |
`-----------------------------------------------------*/

/* Parse `(expr)', unary operators, and numbers.  */
static eval_error
primary (int32_t *v1)
{
  eval_error er;
  int32_t v2;

  switch (eval_lex (v1))
    {
      /* Number */
    case NUMBER:
      return NO_ERROR;

      /* Parenthesis */
    case LEFTP:
      er = primary (v1);
      er = parse_expr (v1, er, MIN_PREC);
      if (er >= SYNTAX_ERROR)
        return er;
      switch (eval_lex (&v2))
        {
        case ERROR:
          return UNKNOWN_INPUT;
        case BADNUM:
          return INVALID_NUMBER;
        case BADOP:
          return INVALID_OPERATOR;
        case RIGHTP:
          return er;
        default:
          return MISSING_RIGHT;
        }

      /* Unary operators */
      /* Minimize undefined C behavior on overflow.  This code assumes
         that the implementation-defined overflow when casting
         unsigned to signed is a silent twos-complement
         wrap-around.  */
    case PLUS:
      return primary (v1);
    case MINUS:
      er = primary (v1);
      *v1 = (int32_t) -(uint32_t) *v1;
      return er;
    case NOT:
      er = primary (v1);
      *v1 = ~*v1;
      return er;
    case LNOT:
      er = primary (v1);
      *v1 = *v1 == 0 ? 1 : 0;
      return er;

      /* Anything else */
    case ERROR:
      return UNKNOWN_INPUT;
    case BADNUM:
      return INVALID_NUMBER;
    case BADOP:
      return INVALID_OPERATOR;
    default:
      return SYNTAX_ERROR;
    }
}

/* Parse binary operators with at least MIN_PREC precedence.  */
static eval_error
parse_expr (int32_t *v1, eval_error er, unsigned min_prec)
{
  eval_token et;
  eval_token et2;
  eval_error er2;
  int32_t v2;
  int32_t v3;
  uint32_t u1;
  uint32_t u2;
  uint32_t u3;

  if (er >= SYNTAX_ERROR)
    return er;
  et = eval_lex (&v2);
  while (et / 10 >= min_prec)
    {
      if ((er2 = primary (&v2)) >= SYNTAX_ERROR)
        return er2;
      et2 = eval_lex (&v3);
      /* Handle binary operators of higher precedence or right-associativity */
      while (et2 / 10 > et / 10 || et2 == EXPONENT)
        {
          eval_undo ();
          if ((er2 = parse_expr (&v2, er2, et2 / 10)) >= SYNTAX_ERROR)
            return er2;
          et2 = eval_lex (&v3);
        }
      /* Reduce the two values by the given binary operator */
      switch (et)
        {
        case EXPONENT:
          /* Minimize undefined C behavior on overflow.  This code assumes
             that the implementation-defined overflow when casting
             unsigned to signed is a silent twos-complement
             wrap-around.  */
          if (v2 < 0)
            er = NEGATIVE_EXPONENT;
          else if (*v1 == 0 && v2 == 0)
            er = DIVIDE_ZERO;
          else
            {
              u1 = *v1;
              u2 = v2;
              u3 = 1;
              while (u2)
                {
                  if (u2 & 1)
                    u3 *= u1;
                  u1 *= u1;
                  u2 >>= 1;
                }
              *v1 = u3;
            }
          break;

        case TIMES:
          *v1 = (int32_t) ((uint32_t) *v1 * (uint32_t) v2);
          break;
        case DIVIDE:
          if (v2 == 0)
            er = DIVIDE_ZERO;
          else if (v2 == -1)
            /* Avoid overflow, and the x86 SIGFPE on INT_MIN / -1.  */
            *v1 = (int32_t) -(uint32_t) *v1;
          else
            *v1 /= v2;
          break;
        case MODULO:
          if (v2 == 0)
            er = MODULO_ZERO;
          else if (v2 == -1)
            /* Avoid the x86 SIGFPE on INT_MIN % -1.  */
            *v1 = 0;
          else
            *v1 %= v2;
          break;

        case PLUS:
          *v1 = (int32_t) ((uint32_t) *v1 + (uint32_t) v2);
          break;
        case MINUS:
          *v1 = (int32_t) ((uint32_t) *v1 - (uint32_t) v2);
          break;

        case LSHIFT:
          u1 = *v1;
          u1 <<= (uint32_t) (v2 & 0x1f);
          *v1 = u1;
          break;
        case RSHIFT:
          u1 = *v1 < 0 ? ~*v1 : *v1;
          u1 >>= (uint32_t) (v2 & 0x1f);
          *v1 = *v1 < 0 ? ~u1 : u1;
          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;

        case ASSIGN:
          M4ERROR ((warning_status, 0, _("\
Warning: recommend ==, not =, for equality operator")));
          FALLTHROUGH;
        case EQ:
          *v1 = *v1 == v2;
          break;
        case NOTEQ:
          *v1 = *v1 != v2;
          break;

        case AND:
          *v1 &= v2;
          break;

        case XOR:
          *v1 ^= v2;
          break;

        case OR:
          *v1 |= v2;
          break;

          /* Implement short-circuiting of valid syntax.  */
        case LAND:
          if (!*v1)
            er2 = NO_ERROR;
          *v1 = *v1 && v2;
          break;

        case LOR:
          if (*v1)
            er2 = NO_ERROR;
          *v1 = *v1 || v2;
          break;

        default:
          M4ERROR ((warning_status, 0,
                    "INTERNAL ERROR: unexpected operator in evaluate ()"));
          abort ();
        }
      if (er == NO_ERROR)
        er = er2;
      et = et2;
    }

  eval_undo ();
  return er;
}

/*---------------------------------------.
| Main entry point, called from "eval".  |
`---------------------------------------*/

bool
evaluate (const char *expr, int32_t *val)
{
  eval_error err;

  eval_init_lex (expr);
  err = primary (val);
  err = parse_expr (val, err, MIN_PREC);

  if (err == NO_ERROR && *eval_text != '\0')
    {
      switch (eval_lex (val))
        {
        case BADNUM:
          err = INVALID_NUMBER;
          break;
        case BADOP:
          err = INVALID_OPERATOR;
          break;
        default:
          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 INVALID_NUMBER:
      M4ERROR ((warning_status, 0, _("invalid number in eval: %s"), expr));
      break;

    case INVALID_OPERATOR:
      M4ERROR ((warning_status, 0, _("invalid operator in eval: %s"), expr));
      retcode = EXIT_FAILURE;
      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;

    case NEGATIVE_EXPONENT:
      M4ERROR ((warning_status, 0, _("negative exponent in eval: %s"), expr));
      break;

    default:
      M4ERROR ((warning_status, 0,
                "INTERNAL ERROR: bad error code in evaluate ()"));
      abort ();
    }

  return err != NO_ERROR;
}
