diff options
author | robot-piglet <[email protected]> | 2025-08-28 14:27:58 +0300 |
---|---|---|
committer | robot-piglet <[email protected]> | 2025-08-28 14:57:06 +0300 |
commit | 81d828c32c8d5477cb2f0ce5da06a1a8d9392ca3 (patch) | |
tree | 3081d566f0d5158d76e9093261344f6406fd09f7 /contrib/tools/swig/Source/Preprocessor/expr.c | |
parent | 77ea11423f959e51795cc3ef36a48d808b4ffb98 (diff) |
Intermediate changes
commit_hash:d5b1af16dbe9030537a04c27eb410c88c2f496cd
Diffstat (limited to 'contrib/tools/swig/Source/Preprocessor/expr.c')
-rw-r--r-- | contrib/tools/swig/Source/Preprocessor/expr.c | 515 |
1 files changed, 515 insertions, 0 deletions
diff --git a/contrib/tools/swig/Source/Preprocessor/expr.c b/contrib/tools/swig/Source/Preprocessor/expr.c new file mode 100644 index 00000000000..c14f7ed81bf --- /dev/null +++ b/contrib/tools/swig/Source/Preprocessor/expr.c @@ -0,0 +1,515 @@ +/* ----------------------------------------------------------------------------- + * This file is part of SWIG, which is licensed as a whole under version 3 + * (or any later version) of the GNU General Public License. Some additional + * terms also apply to certain portions of SWIG. The full details of the SWIG + * license and copyrights can be found in the LICENSE and COPYRIGHT files + * included with the SWIG source code as distributed by the SWIG developers + * and at https://www.swig.org/legal.html. + * + * expr.c + * + * Integer arithmetic expression evaluator used to handle expressions + * encountered during preprocessing. + * + * Note that this is used for expressions in `#if` and the like, but not + * for expressions in `#define` which SWIG wraps as constants - for those + * we inject a `%constant` directive which is handled by the parser in + * `Source/CParse/parser.y`. + * ----------------------------------------------------------------------------- */ + +#include "swig.h" +#include "preprocessor.h" + +static Scanner *scan = 0; + +typedef struct { + /* One of the EXPR_xxx values defined below. */ + int op; + /* op == EXPR_OP: value is the token specifying which operator. + * + * op == EXPR_VALUE && svalue == NULL: Numeric expression value. + * + * Otherwise unused. + */ + long value; + /* op == EXPR_VALUE: If non-NULL, string expression value; if NULL see value. + * + * Otherwise unused. + */ + String *svalue; +} exprval; + +#define EXPR_TOP 1 +#define EXPR_VALUE 2 +#define EXPR_OP 3 +#define EXPR_GROUP 4 + +/* Special token values used here to distinguish from SWIG_TOKEN_MINUS + * and SWIG_TOKEN_PLUS (which we use here for a two argument versions). + */ +#define OP_UMINUS 100 +#define OP_UPLUS 101 + +static exprval stack[256]; /* Parsing stack */ +static int sp = 0; /* Stack pointer */ +static int prec[256]; /* Precedence rules */ +static int expr_init = 0; /* Initialization flag */ +static const char *errmsg = 0; /* Parsing error */ + +/* Initialize the precedence table for various operators. Low values have higher precedence */ +static void init_precedence(void) { + prec[SWIG_TOKEN_NOT] = 10; + prec[SWIG_TOKEN_LNOT] = 10; + prec[OP_UMINUS] = 10; + prec[OP_UPLUS] = 10; + prec[SWIG_TOKEN_STAR] = 20; + prec[SWIG_TOKEN_SLASH] = 20; + prec[SWIG_TOKEN_PERCENT] = 20; + prec[SWIG_TOKEN_PLUS] = 30; + prec[SWIG_TOKEN_MINUS] = 30; + prec[SWIG_TOKEN_LSHIFT] = 40; + prec[SWIG_TOKEN_RSHIFT] = 40; + prec[SWIG_TOKEN_LESSTHAN] = 50; + prec[SWIG_TOKEN_GREATERTHAN] = 50; + prec[SWIG_TOKEN_LTEQUAL] = 50; + prec[SWIG_TOKEN_GTEQUAL] = 50; + prec[SWIG_TOKEN_EQUALTO] = 60; + prec[SWIG_TOKEN_NOTEQUAL] = 60; + prec[SWIG_TOKEN_AND] = 70; + prec[SWIG_TOKEN_XOR] = 80; + prec[SWIG_TOKEN_OR] = 90; + prec[SWIG_TOKEN_LAND] = 100; + prec[SWIG_TOKEN_LOR] = 110; + expr_init = 1; +} + +#define UNARY_OP(token) (((token) == SWIG_TOKEN_NOT) || \ + ((token) == SWIG_TOKEN_LNOT) || \ + ((token) == OP_UMINUS) || \ + ((token) == OP_UPLUS)) + +/* Reduce a single operator on the stack */ +/* return 0 on failure, 1 on success */ +static int reduce_op(void) { + long op_token = stack[sp - 1].value; + assert(sp > 0); + assert(stack[sp - 1].op == EXPR_OP); + /* do some basic checking first: */ + if (stack[sp].op != EXPR_VALUE) { + errmsg = "Right-hand side is not value"; + return 0; + } + if (UNARY_OP(op_token)) { + if (stack[sp].svalue) { + errmsg = "Syntax error: attempt to apply unary operator to string"; + return 0; + } + } else { + /* binary operator: */ + if (sp == 1) { + /* top of stack: don't attempt to use sp-2! */ + errmsg = "Missing left-hand side for binary operator"; + return 0; + } + if (stack[sp].op != EXPR_VALUE) { + errmsg = "Left-hand side of binary operator is not a value"; + return 0; + } + if ((!stack[sp - 2].svalue) != (!stack[sp].svalue)) { + errmsg = "Can't mix strings and integers in expression"; + return 0; + } + } + if (stack[sp].svalue) { + /* A binary string expression */ + switch (stack[sp - 1].value) { + case SWIG_TOKEN_EQUALTO: + stack[sp - 2].value = (Strcmp(stack[sp - 2].svalue, stack[sp].svalue) == 0); + Delete(stack[sp - 2].svalue); + Delete(stack[sp].svalue); + sp -= 2; + break; + case SWIG_TOKEN_NOTEQUAL: + stack[sp - 2].value = (Strcmp(stack[sp - 2].svalue, stack[sp].svalue) != 0); + Delete(stack[sp - 2].svalue); + Delete(stack[sp].svalue); + sp -= 2; + break; + default: + errmsg = "Syntax error: bad binary operator for strings"; + return 0; + } + } else { + switch (op_token) { + case SWIG_TOKEN_STAR: + stack[sp - 2].value = stack[sp - 2].value * stack[sp].value; + sp -= 2; + break; + case SWIG_TOKEN_EQUALTO: + stack[sp - 2].value = stack[sp - 2].value == stack[sp].value; + sp -= 2; + break; + case SWIG_TOKEN_NOTEQUAL: + stack[sp - 2].value = stack[sp - 2].value != stack[sp].value; + sp -= 2; + break; + case SWIG_TOKEN_PLUS: + stack[sp - 2].value = stack[sp - 2].value + stack[sp].value; + sp -= 2; + break; + case SWIG_TOKEN_MINUS: + stack[sp - 2].value = stack[sp - 2].value - stack[sp].value; + sp -= 2; + break; + case SWIG_TOKEN_AND: + stack[sp - 2].value = stack[sp - 2].value & stack[sp].value; + sp -= 2; + break; + case SWIG_TOKEN_LAND: + stack[sp - 2].value = stack[sp - 2].value && stack[sp].value; + sp -= 2; + break; + case SWIG_TOKEN_OR: + stack[sp - 2].value = stack[sp - 2].value | stack[sp].value; + sp -= 2; + break; + case SWIG_TOKEN_LOR: + stack[sp - 2].value = stack[sp - 2].value || stack[sp].value; + sp -= 2; + break; + case SWIG_TOKEN_XOR: + stack[sp - 2].value = stack[sp - 2].value ^ stack[sp].value; + sp -= 2; + break; + case SWIG_TOKEN_LESSTHAN: + stack[sp - 2].value = stack[sp - 2].value < stack[sp].value; + sp -= 2; + break; + case SWIG_TOKEN_GREATERTHAN: + stack[sp - 2].value = stack[sp - 2].value > stack[sp].value; + sp -= 2; + break; + case SWIG_TOKEN_LTEQUAL: + stack[sp - 2].value = stack[sp - 2].value <= stack[sp].value; + sp -= 2; + break; + case SWIG_TOKEN_GTEQUAL: + stack[sp - 2].value = stack[sp - 2].value >= stack[sp].value; + sp -= 2; + break; + case SWIG_TOKEN_NOT: + stack[sp - 1].value = ~stack[sp].value; + sp--; + break; + case SWIG_TOKEN_LNOT: + stack[sp - 1].value = !stack[sp].value; + sp--; + break; + case OP_UMINUS: + stack[sp - 1].value = -stack[sp].value; + sp--; + break; + case OP_UPLUS: + stack[sp - 1].value = stack[sp].value; + sp--; + break; + case SWIG_TOKEN_SLASH: + if (stack[sp].value != 0) { + stack[sp - 2].value = stack[sp - 2].value / stack[sp].value; + sp -= 2; + } else { + errmsg = "Division by zero in expression"; + return 0; + } + break; + case SWIG_TOKEN_PERCENT: + if (stack[sp].value != 0) { + stack[sp - 2].value = stack[sp - 2].value % stack[sp].value; + sp -= 2; + } else { + errmsg = "Modulo by zero in expression"; + return 0; + } + break; + case SWIG_TOKEN_LSHIFT: + stack[sp - 2].value = stack[sp - 2].value << stack[sp].value; + sp -= 2; + break; + case SWIG_TOKEN_RSHIFT: + stack[sp - 2].value = stack[sp - 2].value >> stack[sp].value; + sp -= 2; + break; + default: + errmsg = "Syntax error: bad operator"; + return 0; + } + } + stack[sp].op = EXPR_VALUE; + stack[sp].svalue = 0; /* ensure it's not a string! */ + return 1; +} + +/* ----------------------------------------------------------------------------- + * Preprocessor_expr_init() + * + * Initialize the expression evaluator + * ----------------------------------------------------------------------------- */ + +void Preprocessor_expr_init(void) { + if (!expr_init) + init_precedence(); + if (!scan) + scan = NewScanner(); +} + +void Preprocessor_expr_delete(void) { + DelScanner(scan); +} + + +/* ----------------------------------------------------------------------------- + * Tokenizer + * ----------------------------------------------------------------------------- */ + +static int expr_token(Scanner * s) { + int t; + while (1) { + t = Scanner_token(s); + if (!((t == SWIG_TOKEN_BACKSLASH) || (t == SWIG_TOKEN_ENDLINE) || (t == SWIG_TOKEN_COMMENT))) + break; + } + return t; +} + +/* ----------------------------------------------------------------------------- + * Preprocessor_expr() + * + * Evaluates an arithmetic expression. Returns the result and sets an error code. + * ----------------------------------------------------------------------------- */ + +int Preprocessor_expr(DOH *s, int *error) { + int token = 0; + int op = 0; + + sp = 0; + assert(s); + assert(scan); + + Seek(s, 0, SEEK_SET); + /* Printf(stdout,"evaluating : '%s'\n", s); */ + *error = 0; + Scanner_clear(scan); + Scanner_push(scan, s); + + /* Put initial state onto the stack */ + stack[sp].op = EXPR_TOP; + + while (1) { + /* Look at the top of the stack */ + switch (stack[sp].op) { + case EXPR_TOP: + /* EXPR_TOP is a place-holder which can only appear on the top of the + * stack. We can reduce it to any expression - a number, a string, an + * unary operator, or another expression enclosed in parentheses. + */ + token = expr_token(scan); + if (!token) { + errmsg = "Expected an expression"; + *error = 1; + return 0; + } + if (token == SWIG_TOKEN_BOOL) { + /* A boolean value. Reduce EXPR_TOP to an EXPR_VALUE */ + String *cc = Scanner_text(scan); + if (Strcmp(cc, "true") == 0) { + stack[sp].value = (long) 1; + } else { + stack[sp].value = (long) 0; + } + stack[sp].svalue = 0; + stack[sp].op = EXPR_VALUE; + } else if ((token == SWIG_TOKEN_INT) || (token == SWIG_TOKEN_UINT) || (token == SWIG_TOKEN_LONG) || (token == SWIG_TOKEN_ULONG)) { + /* A number. Reduce EXPR_TOP to an EXPR_VALUE */ + char *c = Char(Scanner_text(scan)); + if (c[0] == '0' && (c[1] == 'b' || c[1] == 'B')) { + /* strtol() doesn't handle binary constants */ + stack[sp].value = (long) strtol(c + 2, 0, 2); + } else { + stack[sp].value = (long) strtol(c, 0, 0); + } + stack[sp].svalue = 0; + stack[sp].op = EXPR_VALUE; + } else if ((token == SWIG_TOKEN_MINUS) || (token == SWIG_TOKEN_PLUS) || (token == SWIG_TOKEN_LNOT) || (token == SWIG_TOKEN_NOT)) { + if (token == SWIG_TOKEN_MINUS) + token = OP_UMINUS; + else if (token == SWIG_TOKEN_PLUS) + token = OP_UPLUS; + stack[sp].value = token; + stack[sp].op = EXPR_OP; + sp++; + stack[sp].op = EXPR_TOP; + } else if (token == SWIG_TOKEN_LPAREN) { + stack[sp].op = EXPR_GROUP; + sp++; + stack[sp].op = EXPR_TOP; + } else if (token == SWIG_TOKEN_ENDLINE) { + } else if (token == SWIG_TOKEN_STRING) { + stack[sp].svalue = NewString(Scanner_text(scan)); + stack[sp].op = EXPR_VALUE; + } else if (token == SWIG_TOKEN_ID) { + int next_token = expr_token(scan); + if (next_token == SWIG_TOKEN_LPAREN) { + /* This is a use of an unknown function-like macro so we emit a + * warning. + */ + errmsg = "Use of undefined function-like macro"; + *error = 1; + return 0; + } + Scanner_pushtoken(scan, next_token, Scanner_text(scan)); + + /* Defined macros have been expanded already so this is an unknown + * macro, which gets treated as zero. + */ + stack[sp].value = 0; + stack[sp].svalue = 0; + stack[sp].op = EXPR_VALUE; + } else if (token == SWIG_TOKEN_FLOAT || token == SWIG_TOKEN_DOUBLE || token == SWIG_TOKEN_LONGDOUBLE) { + errmsg = "Floating point constant in preprocessor expression"; + *error = 1; + return 0; + } else + goto syntax_error; + break; + case EXPR_VALUE: + /* A value is on top of the stack. We may reduce or evaluate depending + * on what the next token is. + */ + token = expr_token(scan); + if (!token) { + /* End of input. Might have to reduce if an operator is on stack */ + while (sp > 0) { + if (stack[sp - 1].op == EXPR_OP) { + if (!reduce_op()) + goto reduce_error; + } else if (stack[sp - 1].op == EXPR_GROUP) { + errmsg = "Missing \')\'"; + *error = 1; + return 0; + } else + goto syntax_error; + } + return stack[sp].value; + } + /* Token must be an operator */ + switch (token) { + case SWIG_TOKEN_STAR: + case SWIG_TOKEN_EQUALTO: + case SWIG_TOKEN_NOTEQUAL: + case SWIG_TOKEN_PLUS: + case SWIG_TOKEN_MINUS: + case SWIG_TOKEN_AND: + case SWIG_TOKEN_LAND: + case SWIG_TOKEN_OR: + case SWIG_TOKEN_LOR: + case SWIG_TOKEN_XOR: + case SWIG_TOKEN_LESSTHAN: + case SWIG_TOKEN_GREATERTHAN: + case SWIG_TOKEN_LTEQUAL: + case SWIG_TOKEN_GTEQUAL: + case SWIG_TOKEN_SLASH: + case SWIG_TOKEN_PERCENT: + case SWIG_TOKEN_LSHIFT: + case SWIG_TOKEN_RSHIFT: + if ((sp == 0) || (stack[sp - 1].op == EXPR_GROUP)) { + /* No possibility of reduce. Push operator and expression */ + sp++; + stack[sp].op = EXPR_OP; + stack[sp].value = token; + sp++; + stack[sp].op = EXPR_TOP; + } else { + if (stack[sp - 1].op != EXPR_OP) + goto syntax_error_expected_operator; + op = stack[sp - 1].value; /* Previous operator */ + + /* Now, depending on the precedence relationship between the last operator and the current + we will reduce or push */ + + if (prec[op] <= prec[token]) { + /* Reduce the previous operator */ + if (!reduce_op()) + goto reduce_error; + } + sp++; + stack[sp].op = EXPR_OP; + stack[sp].value = token; + sp++; + stack[sp].op = EXPR_TOP; + } + break; + case SWIG_TOKEN_RPAREN: + if (sp == 0) + goto extra_rparen; + + /* Might have to reduce operators first */ + while ((sp > 0) && (stack[sp - 1].op == EXPR_OP)) { + if (!reduce_op()) + goto reduce_error; + } + if ((sp == 0) || (stack[sp - 1].op != EXPR_GROUP)) + goto extra_rparen; + stack[sp - 1].op = EXPR_VALUE; + stack[sp - 1].value = stack[sp].value; + stack[sp - 1].svalue = stack[sp].svalue; + sp--; + break; + case SWIG_TOKEN_LTEQUALGT: + goto spaceship_not_allowed; + default: + goto syntax_error_expected_operator; + break; + } + break; + + default: + fprintf(stderr, "Internal error in expression evaluator.\n"); + Exit(EXIT_FAILURE); + } + } + +syntax_error: + errmsg = "Syntax error"; + *error = 1; + return 0; + +syntax_error_expected_operator: + errmsg = "Syntax error: expected operator"; + *error = 1; + return 0; + +reduce_error: + /* errmsg has been set by reduce_op */ + *error = 1; + return 0; + +extra_rparen: + errmsg = "Extra \')\'"; + *error = 1; + return 0; + +spaceship_not_allowed: + errmsg = "Spaceship operator (<=>) not allowed in preprocessor expression"; + *error = 1; + return 0; +} + +/* ----------------------------------------------------------------------------- + * Preprocessor_expr_error() + * + * Return error message set by the evaluator (if any) + * ----------------------------------------------------------------------------- */ + +const char *Preprocessor_expr_error(void) { + return errmsg; +} |