home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Source Code 1992 March
/
Source_Code_CD-ROM_Walnut_Creek_March_1992.iso
/
usenet
/
altsrcs
/
0
/
0988
/
tclExpr.c
< prev
next >
Wrap
C/C++ Source or Header
|
1990-12-28
|
17KB
|
681 lines
/*
* tclExpr.c --
*
* This file contains the code to evaluate expressions for
* Tcl.
*
* Copyright 1987 Regents of the University of California
* Permission to use, copy, modify, and distribute this
* software and its documentation for any purpose and without
* fee is hereby granted, provided that the above copyright
* notice appear in all copies. The University of California
* makes no representations about the suitability of this
* software for any purpose. It is provided "as is" without
* express or implied warranty.
*/
#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 /* not lint */
#include <stdio.h>
#include <ctype.h>
#include "tcl.h"
#include "tclInt.h"
/*
* The data structure below describes the state of parsing an expression.
* It's passed among the routines in this module.
*/
typedef struct {
Tcl_Interp *interp; /* Intepreter to use for command execution
* and variable lookup. */
char *originalExpr; /* The entire expression, as originally
* passed to Tcl_Expr. */
char *expr; /* Position to the next character to be
* scanned from the expression string. */
int token; /* Type of the last token to be parsed from
* expr. See below for definitions.
* Corresponds to the characters just
* before expr. */
int number; /* If token is NUMBER, gives value of
* the number. */
} ExprInfo;
/*
* The token types are defined below. In addition, there is a table
* associating a precedence with each operator. The order of types
* is important. Consult the code before changing it.
*/
#define NUMBER 0
#define OPEN_PAREN 1
#define CLOSE_PAREN 2
#define END 3
#define UNKNOWN 4
/*
* Binary operators:
*/
#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
/*
* Unary operators:
*/
#define UNARY_MINUS 26
#define NOT 27
#define BIT_NOT 28
/*
* Precedence table. The values for non-operator token types are ignored.
*/
int precTable[] = {
0, 0, 0, 0, 0, 0, 0, 0,
10, 10, 10, /* MULT, DIVIDE, MOD */
9, 9, /* PLUS, MINUS */
8, 8, /* LEFT_SHIFT, RIGHT_SHIFT */
7, 7, 7, 7, /* LESS, GREATER, LEQ, GEQ */
6, 6, /* EQUAL, NEQ */
5, /* BIT_AND */
4, /* BIT_XOR */
3, /* BIT_OR */
2, /* AND */
1, /* OR */
11, 11, 11 /* UNARY_MINUS, NOT, BIT_NOT */
};
/*
*----------------------------------------------------------------------
*
* ExprGetNum --
*
* Parse off a number from a string.
*
* Results:
* The return value is the integer value corresponding to the
* leading digits of string. If termPtr isn't NULL, *termPtr
* is filled in with the address of the character after the
* last one that is part of the number.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
int
ExprGetNum(string, termPtr)
register char *string; /* ASCII representation of number.
* If leading digit is "0" then read
* in base 8; if "0x", then read in
* base 16. */
register char **termPtr; /* If non-NULL, fill in with address
* of terminating character. */
{
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;
}
/*
*----------------------------------------------------------------------
*
* ExprLex --
*
* Lexical analyzer for expression parser.
*
* Results:
* TCL_OK is returned unless an error occurred while doing lexical
* analysis or executing an embedded command. In that case a
* standard Tcl error is returned, using interp->result to hold
* an error message. In the event of a successful return, the token
* and (possibly) number fields in infoPtr are updated to refer to
* the next symbol in the expression string, and the expr field is
* advanced.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
int
ExprLex(interp, infoPtr)
Tcl_Interp *interp; /* Interpreter to use for error
* reporting. */
register ExprInfo *infoPtr; /* Describes the state of the parse. */
{
register char *p, c;
char *var, *term;
int result;
/*
* The next token is either:
* (a) a variable name (indicated by a $ sign plus a variable
* name in the standard Tcl fashion); lookup the value
* of the variable and return its numeric equivalent as a
* number.
* (b) an embedded command (anything between '[' and ']').
* Execute the command and convert its result to a number.
* (c) a series of decimal digits. Convert it to a number.
* (d) space: skip it.
* (d) an operator. See what kind it is.
*/
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;
}
}
/*
*----------------------------------------------------------------------
*
* ExprGetValue --
*
* Parse a "value" from the remainder of the expression in infoPtr.
*
* Results:
* Normally TCL_OK is returned. The value of the parsed number is
* returned in infoPtr->number. If an error occurred, then
* interp->result contains an error message and TCL_ERROR is returned.
*
* Side effects:
* Information gets parsed from the remaining expression, and the
* expr and token fields in infoPtr get updated. Information is
* parsed until either the end of the expression is reached (null
* character or close paren), an error occurs, or a binary operator
* is encountered with precedence <= prec. In any of these cases,
* infoPtr->token will be left pointing to the token AFTER the
* expression.
*
*----------------------------------------------------------------------
*/
int
ExprGetValue(interp, infoPtr, prec)
Tcl_Interp *interp; /* Interpreter to use for error
* reporting. */
register ExprInfo *infoPtr; /* Describes the state of the parse
* just before the value (i.e. ExprLex
* will be called to get first token
* of value). */
int prec; /* Treat any un-parenthesized operator
* with precedence <= this as the end
* of the expression. */
{
Interp *iPtr = (Interp *) interp;
int result, operator, operand;
int gotOp; /* Non-zero means already lexed the
* operator (while picking up value
* for unary operator). Don't lex
* again. */
/*
* There are two phases to this procedure. First, pick off an initial
* value. Then, parse (binary operator, value) pairs until done.
*/
gotOp = 0;
result = ExprLex(interp, infoPtr);
if (result != TCL_OK) {
return result;
}
if (infoPtr->token == OPEN_PAREN) {
/*
* Parenthesized sub-expression.
*/
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) {
/*
* Process unary operators.
*/
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;
}
}
/*
* Got the first operand. Now fetch (operator, operand) pairs.
*/
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 we're doing an AND or OR and the first operand already
* determines the result, don't execute anything in the
* second operand: just parse.
*/
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;
}
/*
*----------------------------------------------------------------------
*
* Tcl_Expr --
*
* Parse and evaluate an expression.
*
* Results:
* The return value is TCL_OK if the expression was correctly parsed;
* if there was a syntax error or some other error during parsing,
* then another Tcl return value is returned and Tcl_Result points
* to an error message. If all went well, *valuePtr is filled in
* with the result corresponding to the expression string.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
int
Tcl_Expr(interp, string, valuePtr)
Tcl_Interp *interp; /* Intepreter to use for variables etc. */
char *string; /* Expression to evaluate. */
int *valuePtr; /* Where to store result of evaluation. */
{
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;
}