summaryrefslogtreecommitdiff
path: root/Source/Preprocessor/expr.c
diff options
context:
space:
mode:
Diffstat (limited to 'Source/Preprocessor/expr.c')
-rw-r--r--Source/Preprocessor/expr.c436
1 files changed, 436 insertions, 0 deletions
diff --git a/Source/Preprocessor/expr.c b/Source/Preprocessor/expr.c
new file mode 100644
index 0000000..e6e9459
--- /dev/null
+++ b/Source/Preprocessor/expr.c
@@ -0,0 +1,436 @@
+/* -----------------------------------------------------------------------------
+ * 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;
+}