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