/* ----------------------------------------------------------------------------- * See the LICENSE file for information on copyright, usage and redistribution * of SWIG, and the README file for authors - http://www.swig.org/release.html. * * expr.c * * Integer arithmetic expression evaluator used to handle expressions * encountered during preprocessing. * ----------------------------------------------------------------------------- */ char cvsroot_expr_c[] = "$Id: expr.c 10310 2008-03-17 00:36:35Z olly $"; #include "swig.h" #include "preprocessor.h" static Scanner *scan = 0; typedef struct { int op; long value; String *svalue; } exprval; #define EXPR_TOP 1 #define EXPR_VALUE 2 #define EXPR_OP 3 #define EXPR_GROUP 4 #define EXPR_UMINUS 100 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 char *errmsg = 0; /* Parsing error */ /* Initialize the precedence table for various operators. Low values have higher precedence */ static void init_precedence() { prec[SWIG_TOKEN_NOT] = 10; prec[EXPR_UMINUS] = 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_AND] = 50; prec[SWIG_TOKEN_XOR] = 60; prec[SWIG_TOKEN_OR] = 70; prec[SWIG_TOKEN_EQUALTO] = 80; prec[SWIG_TOKEN_NOTEQUAL] = 80; prec[SWIG_TOKEN_LESSTHAN] = 80; prec[SWIG_TOKEN_GREATERTHAN] = 80; prec[SWIG_TOKEN_LTEQUAL] = 80; prec[SWIG_TOKEN_GTEQUAL] = 80; prec[SWIG_TOKEN_LNOT] = 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) == EXPR_UMINUS)) /* Reduce a single operator on the stack */ /* return 0 on failure, 1 on success */ static int reduce_op() { 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 EXPR_UMINUS: stack[sp - 1].value = -stack[sp].value; sp--; break; case SWIG_TOKEN_SLASH: stack[sp - 2].value = stack[sp - 2].value / stack[sp].value; sp -= 2; break; case SWIG_TOKEN_PERCENT: stack[sp - 2].value = stack[sp - 2].value % stack[sp].value; sp -= 2; 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; stack[sp].value = 0; while (1) { /* Look at the top of the stack */ switch (stack[sp].op) { case EXPR_TOP: /* An expression. Can be a number or another expression enclosed in parens */ 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)); stack[sp].value = (long) strtol(c, 0, 0); stack[sp].svalue = 0; /* stack[sp].value = (long) atol(Char(Scanner_text(scan))); */ stack[sp].op = EXPR_VALUE; } else if (token == SWIG_TOKEN_PLUS) { } else if ((token == SWIG_TOKEN_MINUS) || (token == SWIG_TOKEN_LNOT) || (token == SWIG_TOKEN_NOT)) { if (token == SWIG_TOKEN_MINUS) token = EXPR_UMINUS; stack[sp].value = token; stack[sp++].op = EXPR_OP; stack[sp].op = EXPR_TOP; stack[sp].svalue = 0; } else if ((token == SWIG_TOKEN_LPAREN)) { stack[sp++].op = EXPR_GROUP; stack[sp].op = EXPR_TOP; stack[sp].value = 0; stack[sp].svalue = 0; } 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)) { stack[sp].value = 0; stack[sp].svalue = 0; stack[sp].op = EXPR_VALUE; } else goto syntax_error; break; case EXPR_VALUE: /* A value is on 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; stack[sp].value = 0; } 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; stack[sp].value = 0; } 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; sp--; break; default: goto syntax_error_expected_operator; break; } break; default: fprintf(stderr, "Internal error in expression evaluator.\n"); abort(); } } 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; } /* ----------------------------------------------------------------------------- * Preprocessor_expr_error() * * Return error message set by the evaluator (if any) * ----------------------------------------------------------------------------- */ char *Preprocessor_expr_error() { return errmsg; }