This source file includes following definitions.
- ExprInfo
- ExprGetNum
- ExprLex
- ExprGetValue
- Tcl_Expr
#ifndef lint
static char rcsid[] = "$Header: /sprite/src/lib/tcl/RCS/tclExpr.c,v 1.11 90/01/27 14:44:32 ouster Exp $ SPRITE (Berkeley)";
#endif
#include <stdio.h>
#include <ctype.h>
#include "tcl.h"
#include "tclInt.h"
typedef struct {
Tcl_Interp *interp;
char *originalExpr;
char *expr;
int token;
int number;
} ExprInfo;
#define NUMBER 0
#define OPEN_PAREN 1
#define CLOSE_PAREN 2
#define END 3
#define UNKNOWN 4
#define MULT 8
#define DIVIDE 9
#define MOD 10
#define PLUS 11
#define MINUS 12
#define LEFT_SHIFT 13
#define RIGHT_SHIFT 14
#define LESS 15
#define GREATER 16
#define LEQ 17
#define GEQ 18
#define EQUAL 19
#define NEQ 20
#define BIT_AND 21
#define BIT_XOR 22
#define BIT_OR 23
#define AND 24
#define OR 25
#define UNARY_MINUS 26
#define NOT 27
#define BIT_NOT 28
int precTable[] = {
0, 0, 0, 0, 0, 0, 0, 0,
10, 10, 10,
9, 9,
8, 8,
7, 7, 7, 7,
6, 6,
5,
4,
3,
2,
1,
11, 11, 11
};
int
ExprGetNum(string, termPtr)
register char *string;
register char **termPtr;
{
int result, sign;
register char c;
c = *string;
result = 0;
if (c == '-') {
sign = -1;
string++; c = *string;
} else {
sign = 1;
}
if (c == '0') {
string++; c = *string;
if (c == 'x') {
while (1) {
string++; c = *string;
if ((c >= '0') && (c <= '9')) {
result = (result << 4) + (c - '0');
} else if ((c >= 'a') && (c <= 'f')) {
result = (result << 4) + 10 + (c - 'a');
} else if ((c >= 'A') && (c <= 'F')) {
result = (result << 4) + 10 + (c - 'A');
} else {
break;
}
}
} else {
while ((c >= '0') && (c <= '7')) {
result = (result << 3) + (c - '0');
string++; c = *string;
}
}
} else {
while ((c >= '0') && (c <= '9')) {
result = (result*10) + (c - '0');
string++; c = *string;
}
}
if (termPtr != NULL) {
*termPtr = string;
}
return result*sign;
}
int
ExprLex(interp, infoPtr)
Tcl_Interp *interp;
register ExprInfo *infoPtr;
{
register char *p, c;
char *var, *term;
int result;
p = infoPtr->expr;
c = *p;
while (isspace(c)) {
p++; c = *p;
}
infoPtr->expr = p+1;
if (!isascii(c)) {
infoPtr->token = UNKNOWN;
return TCL_OK;
}
switch (c) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
infoPtr->token = NUMBER;
infoPtr->number = ExprGetNum(p, &infoPtr->expr);
return TCL_OK;
case '$':
infoPtr->token = NUMBER;
var = Tcl_ParseVar(infoPtr->interp, p, &infoPtr->expr);
if (var == '\0') {
return TCL_ERROR;
}
if (((Interp *) infoPtr->interp)->noEval) {
infoPtr->number = 0;
return TCL_OK;
}
infoPtr->number = ExprGetNum(var, &term);
if ((term == var) || (*term != 0)) {
c = *infoPtr->expr;
*infoPtr->expr = 0;
Tcl_Return(interp, (char *) NULL, TCL_STATIC);
sprintf(interp->result,
"variable \"%.50s\" contained non-numeric value \"%.50s\"",
p, var);
*infoPtr->expr = c;
return TCL_ERROR;
}
return TCL_OK;
case '[':
infoPtr->token = NUMBER;
result = Tcl_Eval(infoPtr->interp, p+1, TCL_BRACKET_TERM,
&infoPtr->expr);
if (result != TCL_OK) {
return result;
}
infoPtr->expr++;
if (((Interp *) infoPtr->interp)->noEval) {
infoPtr->number = 0;
Tcl_Return(interp, (char *) NULL, TCL_STATIC);
return TCL_OK;
}
infoPtr->number = ExprGetNum(interp->result, &term);
if ((term == interp->result) || (*term != 0)) {
char string[200];
infoPtr->expr[-1];
infoPtr->expr[-1] = 0;
sprintf(string, "command \"%.50s\" returned non-numeric result \"%.50s\"",
p+1, interp->result);
infoPtr->expr[-1] = c;
Tcl_Return(interp, string, TCL_VOLATILE);
return TCL_ERROR;
}
Tcl_Return(interp, (char *) NULL, TCL_STATIC);
return TCL_OK;
case '(':
infoPtr->token = OPEN_PAREN;
return TCL_OK;
case ')':
infoPtr->token = CLOSE_PAREN;
return TCL_OK;
case '*':
infoPtr->token = MULT;
return TCL_OK;
case '/':
infoPtr->token = DIVIDE;
return TCL_OK;
case '%':
infoPtr->token = MOD;
return TCL_OK;
case '+':
infoPtr->token = PLUS;
return TCL_OK;
case '-':
infoPtr->token = MINUS;
return TCL_OK;
case '<':
switch (p[1]) {
case '<':
infoPtr->expr = p+2;
infoPtr->token = LEFT_SHIFT;
break;
case '=':
infoPtr->expr = p+2;
infoPtr->token = LEQ;
break;
default:
infoPtr->token = LESS;
break;
}
return TCL_OK;
case '>':
switch (p[1]) {
case '>':
infoPtr->expr = p+2;
infoPtr->token = RIGHT_SHIFT;
break;
case '=':
infoPtr->expr = p+2;
infoPtr->token = GEQ;
break;
default:
infoPtr->token = GREATER;
break;
}
return TCL_OK;
case '=':
if (p[1] == '=') {
infoPtr->expr = p+2;
infoPtr->token = EQUAL;
} else {
infoPtr->token = UNKNOWN;
}
return TCL_OK;
case '!':
if (p[1] == '=') {
infoPtr->expr = p+2;
infoPtr->token = NEQ;
} else {
infoPtr->token = NOT;
}
return TCL_OK;
case '&':
if (p[1] == '&') {
infoPtr->expr = p+2;
infoPtr->token = AND;
} else {
infoPtr->token = BIT_AND;
}
return TCL_OK;
case '^':
infoPtr->token = BIT_XOR;
return TCL_OK;
case '|':
if (p[1] == '|') {
infoPtr->expr = p+2;
infoPtr->token = OR;
} else {
infoPtr->token = BIT_OR;
}
return TCL_OK;
case '~':
infoPtr->token = BIT_NOT;
return TCL_OK;
case 0:
infoPtr->token = END;
infoPtr->expr = p;
return TCL_OK;
default:
infoPtr->expr = p+1;
infoPtr->token = UNKNOWN;
return TCL_OK;
}
}
int
ExprGetValue(interp, infoPtr, prec)
Tcl_Interp *interp;
register ExprInfo *infoPtr;
int prec;
{
Interp *iPtr = (Interp *) interp;
int result, operator, operand;
int gotOp;
gotOp = 0;
result = ExprLex(interp, infoPtr);
if (result != TCL_OK) {
return result;
}
if (infoPtr->token == OPEN_PAREN) {
result = ExprGetValue(interp, infoPtr, -1);
if (result != TCL_OK) {
return result;
}
if (infoPtr->token != CLOSE_PAREN) {
Tcl_Return(interp, (char *) NULL, TCL_STATIC);
sprintf(interp->result,
"unmatched parentheses in expression \"%.50s\"",
infoPtr->originalExpr);
return TCL_ERROR;
}
} else {
if (infoPtr->token == MINUS) {
infoPtr->token = UNARY_MINUS;
}
if (infoPtr->token >= UNARY_MINUS) {
operator = infoPtr->token;
result = ExprGetValue(interp, infoPtr, precTable[infoPtr->token]);
if (result != TCL_OK) {
return result;
}
switch (operator) {
case UNARY_MINUS:
infoPtr->number = -infoPtr->number;
break;
case NOT:
infoPtr->number = !infoPtr->number;
break;
case BIT_NOT:
infoPtr->number = ~infoPtr->number;
break;
}
gotOp = 1;
} else if (infoPtr->token != NUMBER) {
goto syntaxError;
}
}
if (!gotOp) {
result = ExprLex(interp, infoPtr);
if (result != TCL_OK) {
return result;
}
}
while (1) {
operand = infoPtr->number;
operator = infoPtr->token;
if ((operator < MULT) || (operator >= UNARY_MINUS)) {
if ((operator == END) || (operator == CLOSE_PAREN)) {
return TCL_OK;
} else {
goto syntaxError;
}
}
if (precTable[operator] <= prec) {
return TCL_OK;
}
if (((operator == AND) && !operand)
|| ((operator == OR) && operand)) {
iPtr->noEval++;
result = ExprGetValue(interp, infoPtr, precTable[operator]);
iPtr->noEval--;
} else {
result = ExprGetValue(interp, infoPtr, precTable[operator]);
}
if (result != TCL_OK) {
return result;
}
if ((infoPtr->token < MULT) && (infoPtr->token != NUMBER)
&& (infoPtr->token != END)
&& (infoPtr->token != CLOSE_PAREN)) {
goto syntaxError;
}
switch (operator) {
case MULT:
infoPtr->number = operand * infoPtr->number;
break;
case DIVIDE:
if (infoPtr->number == 0) {
Tcl_Return(interp, "divide by zero", TCL_STATIC);
return TCL_ERROR;
}
infoPtr->number = operand / infoPtr->number;
break;
case MOD:
if (infoPtr->number == 0) {
Tcl_Return(interp, "divide by zero", TCL_STATIC);
return TCL_ERROR;
}
infoPtr->number = operand % infoPtr->number;
break;
case PLUS:
infoPtr->number = operand + infoPtr->number;
break;
case MINUS:
infoPtr->number = operand - infoPtr->number;
break;
case LEFT_SHIFT:
infoPtr->number = operand << infoPtr->number;
break;
case RIGHT_SHIFT:
infoPtr->number = operand >> infoPtr->number;
break;
case LESS:
infoPtr->number = operand < infoPtr->number;
break;
case GREATER:
infoPtr->number = operand > infoPtr->number;
break;
case LEQ:
infoPtr->number = operand <= infoPtr->number;
break;
case GEQ:
infoPtr->number = operand >= infoPtr->number;
break;
case EQUAL:
infoPtr->number = operand == infoPtr->number;
break;
case NEQ:
infoPtr->number = operand != infoPtr->number;
break;
case BIT_AND:
infoPtr->number = operand & infoPtr->number;
break;
case BIT_XOR:
infoPtr->number = operand ^ infoPtr->number;
break;
case BIT_OR:
infoPtr->number = operand | infoPtr->number;
break;
case AND:
infoPtr->number = operand && infoPtr->number;
break;
case OR:
infoPtr->number = operand || infoPtr->number;
break;
}
}
syntaxError:
Tcl_Return(interp, (char *) NULL, TCL_STATIC);
sprintf(interp->result, "syntax error in expression \"%.50s\"",
infoPtr->originalExpr);
return TCL_ERROR;
}
int
Tcl_Expr(interp, string, valuePtr)
Tcl_Interp *interp;
char *string;
int *valuePtr;
{
ExprInfo info;
int result;
info.interp = interp;
info.originalExpr = string;
info.expr = string;
result = ExprGetValue(interp, &info, -1);
if (result != TCL_OK) {
return result;
}
if (info.token != END) {
Tcl_Return(interp, (char *) NULL, TCL_STATIC);
sprintf(interp->result, "syntax error in expression \"%.50s\"", string);
return TCL_ERROR;
}
*valuePtr = info.number;
return TCL_OK;
}