Files
RedBear-OS/local/recipes/dev/m4/source/src/eval.c
T

616 lines
14 KiB
C

/* 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;
}