tclcompexpr.c

来自「tcl是工具命令语言」· C语言 代码 · 共 964 行 · 第 1/2 页

C
964
字号
/*  * tclCompExpr.c -- * *	This file contains the code to compile Tcl expressions. * * Copyright (c) 1997 Sun Microsystems, Inc. * Copyright (c) 1998-2000 by Scriptics Corporation. * * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * * RCS: @(#) $Id: tclCompExpr.c,v 1.13 2003/02/16 01:36:32 msofer Exp $ */#include "tclInt.h"#include "tclCompile.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 arrange to use * the errno from tclExecute.c here. */#ifndef TCL_GENERIC_ONLY#include "tclPort.h"#else#define NO_ERRNO_H#endif#ifdef NO_ERRNO_Hextern int errno;			/* Use errno from tclExecute.c. */#define ERANGE 34#endif/* * Boolean variable that controls whether expression compilation tracing * is enabled. */#ifdef TCL_COMPILE_DEBUGstatic int traceExprComp = 0;#endif /* TCL_COMPILE_DEBUG *//* * The ExprInfo structure describes the state of compiling an expression. * A pointer to an ExprInfo record is passed among the routines in * this module. */typedef struct ExprInfo {    Tcl_Interp *interp;		/* Used for error reporting. */    Tcl_Parse *parsePtr;	/* Structure filled with information about				 * the parsed expression. */    CONST char *expr;		/* The expression that was originally passed				 * to TclCompileExpr. */    CONST char *lastChar;	/* Points just after last byte of expr. */    int hasOperators;		/* Set 1 if the expr has operators; 0 if				 * expr is only a primary. If 1 after				 * compiling an expr, a tryCvtToNumeric				 * instruction is emitted to convert the				 * primary to a number if possible. */} ExprInfo;/* * Definitions of numeric codes representing each expression operator. * The order of these must match the entries in the operatorTable below. * Also the codes for the relational operators (OP_LESS, OP_GREATER,  * OP_LE, OP_GE, OP_EQ, and OP_NE) must be consecutive and in that order. * Note that OP_PLUS and OP_MINUS represent both unary and binary operators. */#define OP_MULT		0#define OP_DIVIDE	1#define OP_MOD		2#define OP_PLUS		3#define OP_MINUS	4#define OP_LSHIFT	5#define OP_RSHIFT	6#define OP_LESS		7#define OP_GREATER	8#define OP_LE		9#define OP_GE		10#define OP_EQ		11#define OP_NEQ		12#define OP_BITAND	13#define OP_BITXOR	14#define OP_BITOR	15#define OP_LAND		16#define OP_LOR		17#define OP_QUESTY	18#define OP_LNOT		19#define OP_BITNOT	20#define OP_STREQ	21#define OP_STRNEQ	22/* * Table describing the expression operators. Entries in this table must * correspond to the definitions of numeric codes for operators just above. */static int opTableInitialized = 0; /* 0 means not yet initialized. */TCL_DECLARE_MUTEX(opMutex)typedef struct OperatorDesc {    char *name;			/* Name of the operator. */    int numOperands;		/* Number of operands. 0 if the operator				 * requires special handling. */    int instruction;		/* Instruction opcode for the operator.				 * Ignored if numOperands is 0. */} OperatorDesc;static OperatorDesc operatorTable[] = {    {"*",   2,  INST_MULT},    {"/",   2,  INST_DIV},    {"%",   2,  INST_MOD},    {"+",   0},     {"-",   0},    {"<<",  2,  INST_LSHIFT},    {">>",  2,  INST_RSHIFT},    {"<",   2,  INST_LT},    {">",   2,  INST_GT},    {"<=",  2,  INST_LE},    {">=",  2,  INST_GE},    {"==",  2,  INST_EQ},    {"!=",  2,  INST_NEQ},    {"&",   2,  INST_BITAND},    {"^",   2,  INST_BITXOR},    {"|",   2,  INST_BITOR},    {"&&",  0},    {"||",  0},    {"?",   0},    {"!",   1,  INST_LNOT},    {"~",   1,  INST_BITNOT},    {"eq",  2,  INST_STR_EQ},    {"ne",  2,  INST_STR_NEQ},    {NULL}};/* * Hashtable used to map the names of expression operators to the index * of their OperatorDesc description. */static Tcl_HashTable opHashTable;/* * Declarations for local procedures to this file: */static int		CompileCondExpr _ANSI_ARGS_((			    Tcl_Token *exprTokenPtr, ExprInfo *infoPtr,			    CompileEnv *envPtr, Tcl_Token **endPtrPtr));static int		CompileLandOrLorExpr _ANSI_ARGS_((			    Tcl_Token *exprTokenPtr, int opIndex,			    ExprInfo *infoPtr, CompileEnv *envPtr,			    Tcl_Token **endPtrPtr));static int		CompileMathFuncCall _ANSI_ARGS_((			    Tcl_Token *exprTokenPtr, CONST char *funcName,			    ExprInfo *infoPtr, CompileEnv *envPtr,			    Tcl_Token **endPtrPtr));static int		CompileSubExpr _ANSI_ARGS_((			    Tcl_Token *exprTokenPtr, ExprInfo *infoPtr,			    CompileEnv *envPtr));static void		LogSyntaxError _ANSI_ARGS_((ExprInfo *infoPtr));/* * Macro used to debug the execution of the expression compiler. */#ifdef TCL_COMPILE_DEBUG#define TRACE(exprBytes, exprLength, tokenBytes, tokenLength) \    if (traceExprComp) { \	fprintf(stderr, "CompileSubExpr: \"%.*s\", token \"%.*s\"\n", \	        (exprLength), (exprBytes), (tokenLength), (tokenBytes)); \    }#else#define TRACE(exprBytes, exprLength, tokenBytes, tokenLength)#endif /* TCL_COMPILE_DEBUG *//* *---------------------------------------------------------------------- * * TclCompileExpr -- * *	This procedure compiles a string containing a Tcl expression into *	Tcl bytecodes. This procedure is the top-level interface to the *	the expression compilation module, and is used by such public *	procedures as Tcl_ExprString, Tcl_ExprStringObj, Tcl_ExprLong, *	Tcl_ExprDouble, Tcl_ExprBoolean, and Tcl_ExprBooleanObj. * * Results: *	The return value is TCL_OK on a successful compilation and TCL_ERROR *	on failure. If TCL_ERROR is returned, then the interpreter's result *	contains an error message. * * Side effects: *	Adds instructions to envPtr to evaluate the expression at runtime. * *---------------------------------------------------------------------- */intTclCompileExpr(interp, script, numBytes, envPtr)    Tcl_Interp *interp;		/* Used for error reporting. */    CONST char *script;		/* The source script to compile. */    int numBytes;		/* Number of bytes in script. If < 0, the				 * string consists of all bytes up to the				 * first null character. */    CompileEnv *envPtr;		/* Holds resulting instructions. */{    ExprInfo info;    Tcl_Parse parse;    Tcl_HashEntry *hPtr;    int new, i, code;    /*     * If this is the first time we've been called, initialize the table     * of expression operators.     */    if (numBytes < 0) {	numBytes = (script? strlen(script) : 0);    }    if (!opTableInitialized) {	Tcl_MutexLock(&opMutex);	if (!opTableInitialized) {	    Tcl_InitHashTable(&opHashTable, TCL_STRING_KEYS);	    for (i = 0;  operatorTable[i].name != NULL;  i++) {		hPtr = Tcl_CreateHashEntry(&opHashTable,			operatorTable[i].name, &new);		if (new) {		    Tcl_SetHashValue(hPtr, (ClientData) i);		}	    }	    opTableInitialized = 1;	}	Tcl_MutexUnlock(&opMutex);    }    /*     * Initialize the structure containing information abvout this     * expression compilation.     */    info.interp = interp;    info.parsePtr = &parse;    info.expr = script;    info.lastChar = (script + numBytes);     info.hasOperators = 0;    /*     * Parse the expression then compile it.     */    code = Tcl_ParseExpr(interp, script, numBytes, &parse);    if (code != TCL_OK) {	goto done;    }    code = CompileSubExpr(parse.tokenPtr, &info, envPtr);    if (code != TCL_OK) {	Tcl_FreeParse(&parse);	goto done;    }        if (!info.hasOperators) {	/*	 * Attempt to convert the primary's object to an int or double.	 * This is done in order to support Tcl's policy of interpreting	 * operands if at all possible as first integers, else	 * floating-point numbers.	 */		TclEmitOpcode(INST_TRY_CVT_TO_NUMERIC, envPtr);    }    Tcl_FreeParse(&parse);    done:    return code;}/* *---------------------------------------------------------------------- * * TclFinalizeCompilation -- * *	Clean up the compilation environment so it can later be *	properly reinitialized. This procedure is called by *	TclFinalizeCompExecEnv() in tclObj.c, which in turn is called *	by Tcl_Finalize(). * * Results: *	None. * * Side effects: *	Cleans up the compilation environment. At the moment, just the *	table of expression operators is freed. * *---------------------------------------------------------------------- */voidTclFinalizeCompilation(){    Tcl_MutexLock(&opMutex);    if (opTableInitialized) {        Tcl_DeleteHashTable(&opHashTable);        opTableInitialized = 0;    }    Tcl_MutexUnlock(&opMutex);}/* *---------------------------------------------------------------------- * * CompileSubExpr -- * *	Given a pointer to a TCL_TOKEN_SUB_EXPR token describing a *	subexpression, this procedure emits instructions to evaluate the *	subexpression at runtime. * * Results: *	The return value is TCL_OK on a successful compilation and TCL_ERROR *	on failure. If TCL_ERROR is returned, then the interpreter's result *	contains an error message. * * Side effects: *	Adds instructions to envPtr to evaluate the subexpression. * *---------------------------------------------------------------------- */static intCompileSubExpr(exprTokenPtr, infoPtr, envPtr)    Tcl_Token *exprTokenPtr;	/* Points to TCL_TOKEN_SUB_EXPR token				 * to compile. */    ExprInfo *infoPtr;		/* Describes the compilation state for the				 * expression being compiled. */    CompileEnv *envPtr;		/* Holds resulting instructions. */{    Tcl_Interp *interp = infoPtr->interp;    Tcl_Token *tokenPtr, *endPtr, *afterSubexprPtr;    OperatorDesc *opDescPtr;    Tcl_HashEntry *hPtr;    CONST char *operator;    Tcl_DString opBuf;    int objIndex, opIndex, length, code;    char buffer[TCL_UTF_MAX];    if (exprTokenPtr->type != TCL_TOKEN_SUB_EXPR) {	panic("CompileSubExpr: token type %d not TCL_TOKEN_SUB_EXPR\n",	        exprTokenPtr->type);    }    code = TCL_OK;    /*     * Switch on the type of the first token after the subexpression token.     * After processing it, advance tokenPtr to point just after the     * subexpression's last token.     */        tokenPtr = exprTokenPtr+1;    TRACE(exprTokenPtr->start, exprTokenPtr->size,	    tokenPtr->start, tokenPtr->size);    switch (tokenPtr->type) {        case TCL_TOKEN_WORD:	    code = TclCompileTokens(interp, tokenPtr+1,	            tokenPtr->numComponents, envPtr);	    if (code != TCL_OK) {		goto done;	    }	    tokenPtr += (tokenPtr->numComponents + 1);	    break;	            case TCL_TOKEN_TEXT:	    if (tokenPtr->size > 0) {		objIndex = TclRegisterNewLiteral(envPtr, tokenPtr->start,	                tokenPtr->size);	    } else {		objIndex = TclRegisterNewLiteral(envPtr, "", 0);	    }	    TclEmitPush(objIndex, envPtr);	    tokenPtr += 1;	    break;	            case TCL_TOKEN_BS:	    length = Tcl_UtfBackslash(tokenPtr->start, (int *) NULL,		    buffer);	    if (length > 0) {		objIndex = TclRegisterNewLiteral(envPtr, buffer, length);	    } else {		objIndex = TclRegisterNewLiteral(envPtr, "", 0);	    }	    TclEmitPush(objIndex, envPtr);	    tokenPtr += 1;	    break;	            case TCL_TOKEN_COMMAND:	    code = TclCompileScript(interp, tokenPtr->start+1,		    tokenPtr->size-2, /*nested*/ 0, envPtr);	    if (code != TCL_OK) {		goto done;	    }	    tokenPtr += 1;	    break;	            case TCL_TOKEN_VARIABLE:	    code = TclCompileTokens(interp, tokenPtr, 1, envPtr);	    if (code != TCL_OK) {		goto done;	    }	    tokenPtr += (tokenPtr->numComponents + 1);	    break;	            case TCL_TOKEN_SUB_EXPR:	    code = CompileSubExpr(tokenPtr, infoPtr, envPtr);	    if (code != TCL_OK) {		goto done;	    }	    tokenPtr += (tokenPtr->numComponents + 1);	    break;	            case TCL_TOKEN_OPERATOR:	    /*	     * Look up the operator.  If the operator isn't found, treat it	     * as a math function.	     */	    Tcl_DStringInit(&opBuf);	    operator = Tcl_DStringAppend(&opBuf, 		    tokenPtr->start, tokenPtr->size);	    hPtr = Tcl_FindHashEntry(&opHashTable, operator);	    if (hPtr == NULL) {		code = CompileMathFuncCall(exprTokenPtr, operator, infoPtr,			envPtr, &endPtr);		Tcl_DStringFree(&opBuf);		if (code != TCL_OK) {		    goto done;		}		tokenPtr = endPtr;		break;	    }	    Tcl_DStringFree(&opBuf);	    opIndex = (int) Tcl_GetHashValue(hPtr);	    opDescPtr = &(operatorTable[opIndex]);	    /*	     * If the operator is "normal", compile it using information	     * from the operator table.	     */	    if (opDescPtr->numOperands > 0) {		tokenPtr++;		code = CompileSubExpr(tokenPtr, infoPtr, envPtr);		if (code != TCL_OK) {		    goto done;		}		tokenPtr += (tokenPtr->numComponents + 1);		if (opDescPtr->numOperands == 2) {		    code = CompileSubExpr(tokenPtr, infoPtr, envPtr);		    if (code != TCL_OK) {			goto done;		    }		    tokenPtr += (tokenPtr->numComponents + 1);		}		TclEmitOpcode(opDescPtr->instruction, envPtr);		infoPtr->hasOperators = 1;		break;	    }	    	    /*	     * The operator requires special treatment, and is either	     * "+" or "-", or one of "&&", "||" or "?".	     */	    	    switch (opIndex) {	        case OP_PLUS:	        case OP_MINUS:		    tokenPtr++;		    code = CompileSubExpr(tokenPtr, infoPtr, envPtr);		    if (code != TCL_OK) {

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?