mirror of https://github.com/swig/swig
497 lines
14 KiB
C
497 lines
14 KiB
C
/* -----------------------------------------------------------------------------
|
|
* 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 http://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;
|
|
break;
|
|
}
|
|
} 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;
|
|
break;
|
|
}
|
|
}
|
|
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_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) {
|
|
/* 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)) {
|
|
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;
|
|
}
|