📄 tclexpr.c
字号:
/*
* tclExpr.c --
*
* This file contains the code to evaluate expressions for
* Tcl.
*
* This implementation of floating-point support was modelled
* after an initial implementation by Bill Carpenter.
*
* Copyright 1987-1991 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.
*
* $Id: tclExpr.c,v 1.1.1.1 2001/04/29 20:34:42 karll Exp $
*/
#include "tclInt.h"
/*
* The stuff below is a bit of a hack so that this file can be used
* in environments that include no UNIX, i.e. no errno. Just define
* errno here.
*/
/*
#ifndef TCL_GENERIC_ONLY
#include "tclUnix.h"
#else
int errno;
#define ERANGE 34
#endif
*/
#include <errno.h>
/*
* The data structure below is used to describe an expression value,
* which can be either an integer (the usual case), a double-precision
* floating-point value, or a string. A given number has only one
* value at a time.
*/
#define STATIC_STRING_SPACE 150
typedef struct {
long intValue; /* Integer value, if any. */
double doubleValue; /* Floating-point value, if any. */
ParseValue pv; /* Used to hold a string value, if any. */
char staticSpace[STATIC_STRING_SPACE];
/* Storage for small strings; large ones
* are malloc-ed. */
int type; /* Type of value: TYPE_INT, TYPE_DOUBLE,
* or TYPE_STRING. */
} Value;
/*
* Valid values for type:
*/
#define TYPE_INT 0
#define TYPE_DOUBLE 1
#define TYPE_STRING 2
/*
* The data structure below describes the state of parsing an expression.
* It's passed among the routines in this module.
*/
typedef struct {
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. */
} 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 VALUE 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
#define QUESTY 26
#define COLON 27
/*
* Unary operators:
*/
#define UNARY_MINUS 28
#define NOT 29
#define BIT_NOT 30
/*
* Precedence table. The values for non-operator token types are ignored.
*/
int precTable[] = {
0, 0, 0, 0, 0, 0, 0, 0,
11, 11, 11, /* MULT, DIVIDE, MOD */
10, 10, /* PLUS, MINUS */
9, 9, /* LEFT_SHIFT, RIGHT_SHIFT */
8, 8, 8, 8, /* LESS, GREATER, LEQ, GEQ */
7, 7, /* EQUAL, NEQ */
6, /* BIT_AND */
5, /* BIT_XOR */
4, /* BIT_OR */
3, /* AND */
2, /* OR */
1, 1, /* QUESTY, COLON */
12, 12, 12 /* UNARY_MINUS, NOT, BIT_NOT */
};
/*
* Mapping from operator numbers to strings; used for error messages.
*/
char *operatorStrings[] = {
"VALUE", "(", ")", "END", "UNKNOWN", "5", "6", "7",
"*", "/", "%", "+", "-", "<<", ">>", "<", ">", "<=",
">=", "==", "!=", "&", "^", "|", "&&", "||", "?", ":",
"-", "!", "~"
};
/*
* Declarations for local procedures to this file:
*/
static int ExprGetValue _ANSI_ARGS_((Tcl_Interp *interp,
ExprInfo *infoPtr, int prec, Value *valuePtr));
static int ExprLex _ANSI_ARGS_((Tcl_Interp *interp,
ExprInfo *infoPtr, Value *valuePtr));
static void ExprMakeString _ANSI_ARGS_((Value *valuePtr));
static int ExprParseString _ANSI_ARGS_((Tcl_Interp *interp,
char *string, Value *valuePtr));
static int ExprTopLevel _ANSI_ARGS_((Tcl_Interp *interp,
char *string, Value *valuePtr));
/*
*--------------------------------------------------------------
*
* ExprParseString --
*
* Given a string (such as one coming from command or variable
* substitution), make a Value based on the string. The value
* will be a floating-point or integer, if possible, or else it
* will just be a copy of the string.
*
* Results:
* TCL_OK is returned under normal circumstances, and TCL_ERROR
* is returned if a floating-point overflow or underflow occurred
* while reading in a number. The value at *valuePtr is modified
* to hold a number, if possible.
*
* Side effects:
* None.
*
*--------------------------------------------------------------
*/
static int
ExprParseString(interp, string, valuePtr)
Tcl_Interp *interp; /* Where to store error message. */
char *string; /* String to turn into value. */
Value *valuePtr; /* Where to store value information.
* Caller must have initialized pv field. */
{
register char c;
/*
* Try to convert the string to a number.
*/
c = *string;
if (((c >= '0') && (c <= '9')) || (c == '-') || (c == '.')) {
char *term;
valuePtr->type = TYPE_INT;
errno = 0;
valuePtr->intValue = strtol(string, &term, 0);
c = *term;
if ((c == '\0') && (errno != ERANGE)) {
return TCL_OK;
}
if ((c == '.') || (c == 'e') || (c == 'E') || (errno == ERANGE)) {
errno = 0;
valuePtr->doubleValue = strtod(string, &term);
if (errno == ERANGE) {
Tcl_ResetResult(interp);
if (valuePtr->doubleValue == 0.0) {
Tcl_AppendResult(interp, "floating-point value \"",
string, "\" too small to represent",
(char *) NULL);
} else {
Tcl_AppendResult(interp, "floating-point value \"",
string, "\" too large to represent",
(char *) NULL);
}
return TCL_ERROR;
}
if (*term == '\0') {
valuePtr->type = TYPE_DOUBLE;
return TCL_OK;
}
}
}
/*
* Not a valid number. Save a string value (but don't do anything
* if it's already the value).
*/
valuePtr->type = TYPE_STRING;
if (string != valuePtr->pv.buffer) {
int length, shortfall;
length = strlen(string);
valuePtr->pv.next = valuePtr->pv.buffer;
shortfall = length - (valuePtr->pv.end - valuePtr->pv.buffer);
if (shortfall > 0) {
(*valuePtr->pv.expandProc)(&valuePtr->pv, shortfall);
}
strcpy(valuePtr->pv.buffer, string);
}
return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
* ExprLex --
*
* Lexical analyzer for expression parser: parses a single value,
* operator, or other syntactic element from an expression string.
*
* 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 field in infoPtr is updated to refer to the next symbol in
* the expression string, and the expr field is advanced past that
* token; if the token is a value, then the value is stored at
* valuePtr.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static int
ExprLex(interp, infoPtr, valuePtr)
Tcl_Interp *interp; /* Interpreter to use for error
* reporting. */
register ExprInfo *infoPtr; /* Describes the state of the parse. */
register Value *valuePtr; /* Where to store value, if that is
* what's parsed from string. Caller
* must have initialized pv field
* correctly. */
{
register char *p, c;
char *var, *term;
int result;
p = infoPtr->expr;
c = *p;
while (isspace(c)) {
p++;
c = *p;
}
infoPtr->expr = p+1;
switch (c) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '.':
/*
* Number. First read an integer. Then if it looks like
* there's a floating-point number (or if it's too big a
* number to fit in an integer), parse it as a floating-point
* number.
*/
infoPtr->token = VALUE;
valuePtr->type = TYPE_INT;
errno = 0;
valuePtr->intValue = strtoul(p, &term, 0);
c = *term;
if ((c == '.') || (c == 'e') || (c == 'E') || (errno == ERANGE)) {
char *term2;
errno = 0;
valuePtr->doubleValue = strtod(p, &term2);
if (errno == ERANGE) {
Tcl_ResetResult(interp);
if (valuePtr->doubleValue == 0.0) {
interp->result =
"floating-point value too small to represent";
} else {
interp->result =
"floating-point value too large to represent";
}
return TCL_ERROR;
}
if (term2 == infoPtr->expr) {
interp->result = "poorly-formed floating-point value";
return TCL_ERROR;
}
valuePtr->type = TYPE_DOUBLE;
infoPtr->expr = term2;
} else {
infoPtr->expr = term;
}
return TCL_OK;
case '$':
/*
* Variable. Fetch its value, then see if it makes sense
* as an integer or floating-point number.
*/
infoPtr->token = VALUE;
var = Tcl_ParseVar(interp, p, &infoPtr->expr);
if (var == NULL) {
return TCL_ERROR;
}
if (((Interp *) interp)->noEval) {
valuePtr->type = TYPE_INT;
valuePtr->intValue = 0;
return TCL_OK;
}
return ExprParseString(interp, var, valuePtr);
case '[':
infoPtr->token = VALUE;
result = Tcl_Eval(interp, p+1, TCL_BRACKET_TERM,
&infoPtr->expr);
if (result != TCL_OK) {
return result;
}
infoPtr->expr++;
if (((Interp *) interp)->noEval) {
valuePtr->type = TYPE_INT;
valuePtr->intValue = 0;
Tcl_ResetResult(interp);
return TCL_OK;
}
result = ExprParseString(interp, interp->result, valuePtr);
if (result != TCL_OK) {
return result;
}
Tcl_ResetResult(interp);
return TCL_OK;
case '"':
infoPtr->token = VALUE;
result = TclParseQuotes(interp, infoPtr->expr, '"', 0,
&infoPtr->expr, &valuePtr->pv);
if (result != TCL_OK) {
return result;
}
return ExprParseString(interp, valuePtr->pv.buffer, valuePtr);
case '{':
infoPtr->token = VALUE;
result = TclParseBraces(interp, infoPtr->expr, &infoPtr->expr,
&valuePtr->pv);
if (result != TCL_OK) {
return result;
}
return ExprParseString(interp, valuePtr->pv.buffer, valuePtr);
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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -