diff options
Diffstat (limited to 'Source/Preprocessor/expr.c')
-rw-r--r-- | Source/Preprocessor/expr.c | 436 |
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; +} |