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 + -
显示快捷键?