616 lines
14 KiB
C
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;
|
|
}
|