📄 tclexpr.c
字号:
#ifndef EXCLUDE_TCL/* * 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. */#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"#endif/* * 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 150typedef 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 intExprParseString(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 intExprLex(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; case '?': infoPtr->token = QUESTY; return TCL_OK; case ':': infoPtr->token = COLON;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -