tclcompexpr.c

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

C
964
字号
			goto done;		    }		    tokenPtr += (tokenPtr->numComponents + 1);		    		    /*		     * Check whether the "+" or "-" is unary.		     */		    		    afterSubexprPtr = exprTokenPtr			    + exprTokenPtr->numComponents+1;		    if (tokenPtr == afterSubexprPtr) {			TclEmitOpcode(((opIndex==OP_PLUS)?			        INST_UPLUS : INST_UMINUS),			        envPtr);			break;		    }		    		    /*		     * The "+" or "-" is binary.		     */		    		    code = CompileSubExpr(tokenPtr, infoPtr, envPtr);		    if (code != TCL_OK) {			goto done;		    }		    tokenPtr += (tokenPtr->numComponents + 1);		    TclEmitOpcode(((opIndex==OP_PLUS)? INST_ADD : INST_SUB),			    envPtr);		    break;	        case OP_LAND:	        case OP_LOR:		    code = CompileLandOrLorExpr(exprTokenPtr, opIndex,			    infoPtr, envPtr, &endPtr);		    if (code != TCL_OK) {			goto done;		    }		    tokenPtr = endPtr;		    break;				        case OP_QUESTY:		    code = CompileCondExpr(exprTokenPtr, infoPtr,			    envPtr, &endPtr);		    if (code != TCL_OK) {			goto done;		    }		    tokenPtr = endPtr;		    break;		    		default:		    panic("CompileSubExpr: unexpected operator %d requiring special treatment\n",		        opIndex);	    } /* end switch on operator requiring special treatment */	    infoPtr->hasOperators = 1;	    break;        default:	    panic("CompileSubExpr: unexpected token type %d\n",	            tokenPtr->type);    }    /*     * Verify that the subexpression token had the required number of     * subtokens: that we've advanced tokenPtr just beyond the     * subexpression's last token. For example, a "*" subexpression must     * contain the tokens for exactly two operands.     */        if (tokenPtr != (exprTokenPtr + exprTokenPtr->numComponents+1)) {	LogSyntaxError(infoPtr);	code = TCL_ERROR;    }        done:    return code;}/* *---------------------------------------------------------------------- * * CompileLandOrLorExpr -- * *	This procedure compiles a Tcl logical and ("&&") or logical or *	("||") subexpression. * * Results: *	The return value is TCL_OK on a successful compilation and TCL_ERROR *	on failure. If TCL_OK is returned, a pointer to the token just after *	the last one in the subexpression is stored at the address in *	endPtrPtr. 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. * *---------------------------------------------------------------------- */static intCompileLandOrLorExpr(exprTokenPtr, opIndex, infoPtr, envPtr, endPtrPtr)    Tcl_Token *exprTokenPtr;	 /* Points to TCL_TOKEN_SUB_EXPR token				  * containing the "&&" or "||" operator. */    int opIndex;		 /* A code describing the expression				  * operator: either OP_LAND or OP_LOR. */    ExprInfo *infoPtr;		 /* Describes the compilation state for the				  * expression being compiled. */    CompileEnv *envPtr;		 /* Holds resulting instructions. */    Tcl_Token **endPtrPtr;	 /* If successful, a pointer to the token				  * just after the last token in the				  * subexpression is stored here. */{    JumpFixup shortCircuitFixup; /* Used to fix up the short circuit jump				  * after the first subexpression. */    JumpFixup lhsTrueFixup, lhsEndFixup;    				 /* Used to fix up jumps used to convert the				  * first operand to 0 or 1. */    Tcl_Token *tokenPtr;    int dist, code;    int savedStackDepth = envPtr->currStackDepth;    /*     * Emit code for the first operand.     */    tokenPtr = exprTokenPtr+2;    code = CompileSubExpr(tokenPtr, infoPtr, envPtr);    if (code != TCL_OK) {	goto done;    }    tokenPtr += (tokenPtr->numComponents + 1);    /*     * Convert the first operand to the result that Tcl requires:     * "0" or "1". Eventually we'll use a new instruction for this.     */        TclEmitForwardJump(envPtr, TCL_TRUE_JUMP, &lhsTrueFixup);    TclEmitPush(TclRegisterNewLiteral(envPtr, "0", 1), envPtr);    TclEmitForwardJump(envPtr, TCL_UNCONDITIONAL_JUMP, &lhsEndFixup);    dist = (envPtr->codeNext - envPtr->codeStart) - lhsTrueFixup.codeOffset;    if (TclFixupForwardJump(envPtr, &lhsTrueFixup, dist, 127)) {        badDist:	panic("CompileLandOrLorExpr: bad jump distance %d\n", dist);    }    envPtr->currStackDepth = savedStackDepth;    TclEmitPush(TclRegisterNewLiteral(envPtr, "1", 1), envPtr);    dist = (envPtr->codeNext - envPtr->codeStart) - lhsEndFixup.codeOffset;    if (TclFixupForwardJump(envPtr, &lhsEndFixup, dist, 127)) {	goto badDist;    }    /*     * Emit the "short circuit" jump around the rest of the expression.     * Duplicate the "0" or "1" on top of the stack first to keep the     * jump from consuming it.     */    TclEmitOpcode(INST_DUP, envPtr);    TclEmitForwardJump(envPtr,	    ((opIndex==OP_LAND)? TCL_FALSE_JUMP : TCL_TRUE_JUMP),	    &shortCircuitFixup);    /*     * Emit code for the second operand.     */    code = CompileSubExpr(tokenPtr, infoPtr, envPtr);    if (code != TCL_OK) {	goto done;    }    tokenPtr += (tokenPtr->numComponents + 1);    /*     * Emit a "logical and" or "logical or" instruction. This does not try     * to "short- circuit" the evaluation of both operands, but instead     * ensures that we either have a "1" or a "0" result.     */    TclEmitOpcode(((opIndex==OP_LAND)? INST_LAND : INST_LOR), envPtr);    /*     * Now that we know the target of the forward jump, update it with the     * correct distance.     */    dist = (envPtr->codeNext - envPtr->codeStart)	    - shortCircuitFixup.codeOffset;    TclFixupForwardJump(envPtr, &shortCircuitFixup, dist, 127);    *endPtrPtr = tokenPtr;    done:    envPtr->currStackDepth = savedStackDepth + 1;    return code;}/* *---------------------------------------------------------------------- * * CompileCondExpr -- * *	This procedure compiles a Tcl conditional expression: *	condExpr ::= lorExpr ['?' condExpr ':' condExpr] * * Results: *	The return value is TCL_OK on a successful compilation and TCL_ERROR *	on failure. If TCL_OK is returned, a pointer to the token just after *	the last one in the subexpression is stored at the address in *	endPtrPtr. 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. * *---------------------------------------------------------------------- */static intCompileCondExpr(exprTokenPtr, infoPtr, envPtr, endPtrPtr)    Tcl_Token *exprTokenPtr;	/* Points to TCL_TOKEN_SUB_EXPR token				 * containing the "?" operator. */    ExprInfo *infoPtr;		/* Describes the compilation state for the				 * expression being compiled. */    CompileEnv *envPtr;		/* Holds resulting instructions. */    Tcl_Token **endPtrPtr;	/* If successful, a pointer to the token				 * just after the last token in the				 * subexpression is stored here. */{    JumpFixup jumpAroundThenFixup, jumpAroundElseFixup;				/* Used to update or replace one-byte jumps				 * around the then and else expressions when				 * their target PCs are determined. */    Tcl_Token *tokenPtr;    int elseCodeOffset, dist, code;    int savedStackDepth = envPtr->currStackDepth;    /*     * Emit code for the test.     */    tokenPtr = exprTokenPtr+2;    code = CompileSubExpr(tokenPtr, infoPtr, envPtr);    if (code != TCL_OK) {	goto done;    }    tokenPtr += (tokenPtr->numComponents + 1);        /*     * Emit the jump to the "else" expression if the test was false.     */        TclEmitForwardJump(envPtr, TCL_FALSE_JUMP, &jumpAroundThenFixup);    /*     * Compile the "then" expression. Note that if a subexpression is only     * a primary, we need to try to convert it to numeric. We do this to     * support Tcl's policy of interpreting operands if at all possible as     * first integers, else floating-point numbers.     */    infoPtr->hasOperators = 0;    code = CompileSubExpr(tokenPtr, infoPtr, envPtr);    if (code != TCL_OK) {	goto done;    }    tokenPtr += (tokenPtr->numComponents + 1);    if (!infoPtr->hasOperators) {	TclEmitOpcode(INST_TRY_CVT_TO_NUMERIC, envPtr);    }    /*     * Emit an unconditional jump around the "else" condExpr.     */        TclEmitForwardJump(envPtr, TCL_UNCONDITIONAL_JUMP,	    &jumpAroundElseFixup);    /*     * Compile the "else" expression.     */    envPtr->currStackDepth = savedStackDepth;    elseCodeOffset = (envPtr->codeNext - envPtr->codeStart);    infoPtr->hasOperators = 0;    code = CompileSubExpr(tokenPtr, infoPtr, envPtr);    if (code != TCL_OK) {	goto done;    }    tokenPtr += (tokenPtr->numComponents + 1);    if (!infoPtr->hasOperators) {	TclEmitOpcode(INST_TRY_CVT_TO_NUMERIC, envPtr);    }    /*     * Fix up the second jump around the "else" expression.     */    dist = (envPtr->codeNext - envPtr->codeStart)	    - jumpAroundElseFixup.codeOffset;    if (TclFixupForwardJump(envPtr, &jumpAroundElseFixup, dist, 127)) {	/*	 * Update the else expression's starting code offset since it	 * moved down 3 bytes too.	 */		elseCodeOffset += 3;    }	    /*     * Fix up the first jump to the "else" expression if the test was false.     */        dist = (elseCodeOffset - jumpAroundThenFixup.codeOffset);    TclFixupForwardJump(envPtr, &jumpAroundThenFixup, dist, 127);    *endPtrPtr = tokenPtr;    done:    envPtr->currStackDepth = savedStackDepth + 1;    return code;}/* *---------------------------------------------------------------------- * * CompileMathFuncCall -- * *	This procedure compiles a call on a math function in an expression: *	mathFuncCall ::= funcName '(' [condExpr {',' condExpr}] ')' * * Results: *	The return value is TCL_OK on a successful compilation and TCL_ERROR *	on failure. If TCL_OK is returned, a pointer to the token just after *	the last one in the subexpression is stored at the address in *	endPtrPtr. If TCL_ERROR is returned, then the interpreter's result *	contains an error message. * * Side effects: *	Adds instructions to envPtr to evaluate the math function at *	runtime. * *---------------------------------------------------------------------- */static intCompileMathFuncCall(exprTokenPtr, funcName, infoPtr, envPtr, endPtrPtr)    Tcl_Token *exprTokenPtr;	/* Points to TCL_TOKEN_SUB_EXPR token				 * containing the math function call. */    CONST char *funcName;	/* Name of the math function. */    ExprInfo *infoPtr;		/* Describes the compilation state for the				 * expression being compiled. */    CompileEnv *envPtr;		/* Holds resulting instructions. */    Tcl_Token **endPtrPtr;	/* If successful, a pointer to the token				 * just after the last token in the				 * subexpression is stored here. */{    Tcl_Interp *interp = infoPtr->interp;    Interp *iPtr = (Interp *) interp;    MathFunc *mathFuncPtr;    Tcl_HashEntry *hPtr;    Tcl_Token *tokenPtr, *afterSubexprPtr;    int code, i;    /*     * Look up the MathFunc record for the function.     */    code = TCL_OK;    hPtr = Tcl_FindHashEntry(&iPtr->mathFuncTable, funcName);    if (hPtr == NULL) {	Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),		"unknown math function \"", funcName, "\"", (char *) NULL);	code = TCL_ERROR;	goto done;    }    mathFuncPtr = (MathFunc *) Tcl_GetHashValue(hPtr);    /*     * If not a builtin function, push an object with the function's name.     */    if (mathFuncPtr->builtinFuncIndex < 0) {	TclEmitPush(TclRegisterNewLiteral(envPtr, funcName, -1), envPtr);    }    /*     * Compile any arguments for the function.     */    tokenPtr = exprTokenPtr+2;    afterSubexprPtr = exprTokenPtr + (exprTokenPtr->numComponents + 1);    if (mathFuncPtr->numArgs > 0) {	for (i = 0;  i < mathFuncPtr->numArgs;  i++) {	    if (tokenPtr == afterSubexprPtr) {		Tcl_ResetResult(interp);		Tcl_AppendToObj(Tcl_GetObjResult(interp),		        "too few arguments for math function", -1);		code = TCL_ERROR;		goto done;	    }	    code = CompileSubExpr(tokenPtr, infoPtr, envPtr);	    if (code != TCL_OK) {		goto done;	    }	    tokenPtr += (tokenPtr->numComponents + 1);	}	if (tokenPtr != afterSubexprPtr) {	    Tcl_ResetResult(interp);	    Tcl_AppendToObj(Tcl_GetObjResult(interp),		    "too many arguments for math function", -1);	    code = TCL_ERROR;	    goto done;	}     } else if (tokenPtr != afterSubexprPtr) {	Tcl_ResetResult(interp);	Tcl_AppendToObj(Tcl_GetObjResult(interp),		"too many arguments for math function", -1);	code = TCL_ERROR;	goto done;    }        /*     * Compile the call on the math function. Note that the "objc" argument     * count for non-builtin functions is incremented by 1 to include the     * function name itself.     */    if (mathFuncPtr->builtinFuncIndex >= 0) { /* a builtin function */	/*	 * Adjust the current stack depth by the number of arguments	 * of the builtin function. This cannot be handled by the 	 * TclEmitInstInt1 macro as the number of arguments is not	 * passed as an operand.	 */	if (envPtr->maxStackDepth < envPtr->currStackDepth) {	    envPtr->maxStackDepth = envPtr->currStackDepth;	}	TclEmitInstInt1(INST_CALL_BUILTIN_FUNC1,	        mathFuncPtr->builtinFuncIndex, envPtr);	envPtr->currStackDepth -= mathFuncPtr->numArgs;    } else {	TclEmitInstInt1(INST_CALL_FUNC1, (mathFuncPtr->numArgs+1), envPtr);    }    *endPtrPtr = afterSubexprPtr;    done:    return code;}/* *---------------------------------------------------------------------- * * LogSyntaxError -- * *	This procedure is invoked after an error occurs when compiling an *	expression. It sets the interpreter result to an error message *	describing the error. * * Results: *	None. * * Side effects: *	Sets the interpreter result to an error message describing the *	expression that was being compiled when the error occurred. * *---------------------------------------------------------------------- */static voidLogSyntaxError(infoPtr)    ExprInfo *infoPtr;		/* Describes the compilation state for the				 * expression being compiled. */{    int numBytes = (infoPtr->lastChar - infoPtr->expr);    char buffer[100];    sprintf(buffer, "syntax error in expression \"%.*s\"",	    ((numBytes > 60)? 60 : numBytes), infoPtr->expr);    Tcl_ResetResult(infoPtr->interp);    Tcl_AppendStringsToObj(Tcl_GetObjResult(infoPtr->interp),	    buffer, (char *) NULL);}

⌨️ 快捷键说明

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