tclcompcmds.c
来自「tcl是工具命令语言」· C语言 代码 · 共 2,168 行 · 第 1/5 页
C
2,168 行
* Procedure called to compile the "expr" command. * * Results: * The return value is a standard Tcl result, which is TCL_OK * unless there was an error while parsing string. If an error occurs * then the interpreter's result contains a standard error message. * * Side effects: * Instructions are added to envPtr to execute the "expr" command * at runtime. * *---------------------------------------------------------------------- */intTclCompileExprCmd(interp, parsePtr, envPtr) Tcl_Interp *interp; /* Used for error reporting. */ Tcl_Parse *parsePtr; /* Points to a parse structure for the * command created by Tcl_ParseCommand. */ CompileEnv *envPtr; /* Holds resulting instructions. */{ Tcl_Token *firstWordPtr; if (parsePtr->numWords == 1) { Tcl_ResetResult(interp); Tcl_AppendToObj(Tcl_GetObjResult(interp), "wrong # args: should be \"expr arg ?arg ...?\"", -1); return TCL_ERROR; } firstWordPtr = parsePtr->tokenPtr + (parsePtr->tokenPtr->numComponents + 1); return TclCompileExprWords(interp, firstWordPtr, (parsePtr->numWords-1), envPtr);}/* *---------------------------------------------------------------------- * * TclCompileForCmd -- * * Procedure called to compile the "for" command. * * Results: * The return value is a standard Tcl result, which is TCL_OK unless * there was an error while parsing string. If an error occurs then * the interpreter's result contains a standard error message. * * Side effects: * Instructions are added to envPtr to execute the "for" command * at runtime. * *---------------------------------------------------------------------- */intTclCompileForCmd(interp, parsePtr, envPtr) Tcl_Interp *interp; /* Used for error reporting. */ Tcl_Parse *parsePtr; /* Points to a parse structure for the * command created by Tcl_ParseCommand. */ CompileEnv *envPtr; /* Holds resulting instructions. */{ Tcl_Token *startTokenPtr, *testTokenPtr, *nextTokenPtr, *bodyTokenPtr; JumpFixup jumpEvalCondFixup; int testCodeOffset, bodyCodeOffset, nextCodeOffset, jumpDist; int bodyRange, nextRange, code; char buffer[32 + TCL_INTEGER_SPACE]; int savedStackDepth = envPtr->currStackDepth; if (parsePtr->numWords != 5) { Tcl_ResetResult(interp); Tcl_AppendToObj(Tcl_GetObjResult(interp), "wrong # args: should be \"for start test next command\"", -1); return TCL_ERROR; } /* * If the test expression requires substitutions, don't compile the for * command inline. E.g., the expression might cause the loop to never * execute or execute forever, as in "for {} "$x > 5" {incr x} {}". */ startTokenPtr = parsePtr->tokenPtr + (parsePtr->tokenPtr->numComponents + 1); testTokenPtr = startTokenPtr + (startTokenPtr->numComponents + 1); if (testTokenPtr->type != TCL_TOKEN_SIMPLE_WORD) { return TCL_OUT_LINE_COMPILE; } /* * Bail out also if the body or the next expression require substitutions * in order to insure correct behaviour [Bug 219166] */ nextTokenPtr = testTokenPtr + (testTokenPtr->numComponents + 1); bodyTokenPtr = nextTokenPtr + (nextTokenPtr->numComponents + 1); if ((nextTokenPtr->type != TCL_TOKEN_SIMPLE_WORD) || (bodyTokenPtr->type != TCL_TOKEN_SIMPLE_WORD)) { return TCL_OUT_LINE_COMPILE; } /* * Create ExceptionRange records for the body and the "next" command. * The "next" command's ExceptionRange supports break but not continue * (and has a -1 continueOffset). */ envPtr->exceptDepth++; envPtr->maxExceptDepth = TclMax(envPtr->exceptDepth, envPtr->maxExceptDepth); bodyRange = TclCreateExceptRange(LOOP_EXCEPTION_RANGE, envPtr); nextRange = TclCreateExceptRange(LOOP_EXCEPTION_RANGE, envPtr); /* * Inline compile the initial command. */ code = TclCompileCmdWord(interp, startTokenPtr+1, startTokenPtr->numComponents, envPtr); if (code != TCL_OK) { if (code == TCL_ERROR) { Tcl_AddObjErrorInfo(interp, "\n (\"for\" initial command)", -1); } goto done; } TclEmitOpcode(INST_POP, envPtr); /* * Jump to the evaluation of the condition. This code uses the "loop * rotation" optimisation (which eliminates one branch from the loop). * "for start cond next body" produces then: * start * goto A * B: body : bodyCodeOffset * next : nextCodeOffset, continueOffset * A: cond -> result : testCodeOffset * if (result) goto B */ TclEmitForwardJump(envPtr, TCL_UNCONDITIONAL_JUMP, &jumpEvalCondFixup); /* * Compile the loop body. */ bodyCodeOffset = (envPtr->codeNext - envPtr->codeStart); code = TclCompileCmdWord(interp, bodyTokenPtr+1, bodyTokenPtr->numComponents, envPtr); envPtr->currStackDepth = savedStackDepth + 1; if (code != TCL_OK) { if (code == TCL_ERROR) { sprintf(buffer, "\n (\"for\" body line %d)", interp->errorLine); Tcl_AddObjErrorInfo(interp, buffer, -1); } goto done; } envPtr->exceptArrayPtr[bodyRange].numCodeBytes = (envPtr->codeNext - envPtr->codeStart) - bodyCodeOffset; TclEmitOpcode(INST_POP, envPtr); /* * Compile the "next" subcommand. */ nextCodeOffset = (envPtr->codeNext - envPtr->codeStart); envPtr->currStackDepth = savedStackDepth; code = TclCompileCmdWord(interp, nextTokenPtr+1, nextTokenPtr->numComponents, envPtr); envPtr->currStackDepth = savedStackDepth + 1; if (code != TCL_OK) { if (code == TCL_ERROR) { Tcl_AddObjErrorInfo(interp, "\n (\"for\" loop-end command)", -1); } goto done; } envPtr->exceptArrayPtr[nextRange].numCodeBytes = (envPtr->codeNext - envPtr->codeStart) - nextCodeOffset; TclEmitOpcode(INST_POP, envPtr); envPtr->currStackDepth = savedStackDepth; /* * Compile the test expression then emit the conditional jump that * terminates the for. */ testCodeOffset = (envPtr->codeNext - envPtr->codeStart); jumpDist = testCodeOffset - jumpEvalCondFixup.codeOffset; if (TclFixupForwardJump(envPtr, &jumpEvalCondFixup, jumpDist, 127)) { bodyCodeOffset += 3; nextCodeOffset += 3; testCodeOffset += 3; } envPtr->currStackDepth = savedStackDepth; code = TclCompileExprWords(interp, testTokenPtr, 1, envPtr); if (code != TCL_OK) { if (code == TCL_ERROR) { Tcl_AddObjErrorInfo(interp, "\n (\"for\" test expression)", -1); } goto done; } envPtr->currStackDepth = savedStackDepth + 1; jumpDist = (envPtr->codeNext - envPtr->codeStart) - bodyCodeOffset; if (jumpDist > 127) { TclEmitInstInt4(INST_JUMP_TRUE4, -jumpDist, envPtr); } else { TclEmitInstInt1(INST_JUMP_TRUE1, -jumpDist, envPtr); } /* * Set the loop's offsets and break target. */ envPtr->exceptArrayPtr[bodyRange].codeOffset = bodyCodeOffset; envPtr->exceptArrayPtr[bodyRange].continueOffset = nextCodeOffset; envPtr->exceptArrayPtr[nextRange].codeOffset = nextCodeOffset; envPtr->exceptArrayPtr[bodyRange].breakOffset = envPtr->exceptArrayPtr[nextRange].breakOffset = (envPtr->codeNext - envPtr->codeStart); /* * The for command's result is an empty string. */ envPtr->currStackDepth = savedStackDepth; TclEmitPush(TclRegisterNewLiteral(envPtr, "", 0), envPtr); code = TCL_OK; done: envPtr->exceptDepth--; return code;}/* *---------------------------------------------------------------------- * * TclCompileForeachCmd -- * * Procedure called to compile the "foreach" command. * * Results: * The return value is a standard Tcl result, which is TCL_OK if * compilation was successful. If an error occurs then the * interpreter's result contains a standard error message and TCL_ERROR * is returned. If the command is too complex for TclCompileForeachCmd, * TCL_OUT_LINE_COMPILE is returned indicating that the foreach command * should be compiled "out of line" by emitting code to invoke its * command procedure at runtime. * * Side effects: * Instructions are added to envPtr to execute the "foreach" command * at runtime. *n*---------------------------------------------------------------------- */intTclCompileForeachCmd(interp, parsePtr, envPtr) Tcl_Interp *interp; /* Used for error reporting. */ Tcl_Parse *parsePtr; /* Points to a parse structure for the * command created by Tcl_ParseCommand. */ CompileEnv *envPtr; /* Holds resulting instructions. */{ Proc *procPtr = envPtr->procPtr; ForeachInfo *infoPtr; /* Points to the structure describing this * foreach command. Stored in a AuxData * record in the ByteCode. */ int firstValueTemp; /* Index of the first temp var in the frame * used to point to a value list. */ int loopCtTemp; /* Index of temp var holding the loop's * iteration count. */ Tcl_Token *tokenPtr, *bodyTokenPtr; unsigned char *jumpPc; JumpFixup jumpFalseFixup; int jumpDist, jumpBackDist, jumpBackOffset, infoIndex, range; int numWords, numLists, numVars, loopIndex, tempVar, i, j, code; char buffer[32 + TCL_INTEGER_SPACE]; int savedStackDepth = envPtr->currStackDepth; /* * We parse the variable list argument words and create two arrays: * varcList[i] is number of variables in i-th var list * varvList[i] points to array of var names in i-th var list */#define STATIC_VAR_LIST_SIZE 5 int varcListStaticSpace[STATIC_VAR_LIST_SIZE]; CONST char **varvListStaticSpace[STATIC_VAR_LIST_SIZE]; int *varcList = varcListStaticSpace; CONST char ***varvList = varvListStaticSpace; /* * If the foreach command isn't in a procedure, don't compile it inline: * the payoff is too small. */ if (procPtr == NULL) { return TCL_OUT_LINE_COMPILE; } numWords = parsePtr->numWords; if ((numWords < 4) || (numWords%2 != 0)) { Tcl_ResetResult(interp); Tcl_AppendToObj(Tcl_GetObjResult(interp), "wrong # args: should be \"foreach varList list ?varList list ...? command\"", -1); return TCL_ERROR; } /* * Bail out if the body requires substitutions * in order to insure correct behaviour [Bug 219166] */ for (i = 0, tokenPtr = parsePtr->tokenPtr; i < numWords-1; i++, tokenPtr += (tokenPtr->numComponents + 1)) { } bodyTokenPtr = tokenPtr; if (bodyTokenPtr->type != TCL_TOKEN_SIMPLE_WORD) { return TCL_OUT_LINE_COMPILE; } /* * Allocate storage for the varcList and varvList arrays if necessary. */ numLists = (numWords - 2)/2; if (numLists > STATIC_VAR_LIST_SIZE) { varcList = (int *) ckalloc(numLists * sizeof(int)); varvList = (CONST char ***) ckalloc(numLists * sizeof(CONST char **)); } for (loopIndex = 0; loopIndex < numLists; loopIndex++) { varcList[loopIndex] = 0; varvList[loopIndex] = NULL; } /* * Set the exception stack depth. */ envPtr->exceptDepth++; envPtr->maxExceptDepth = TclMax(envPtr->exceptDepth, envPtr->maxExceptDepth); /* * Break up each var list and set the varcList and varvList arrays. * Don't compile the foreach inline if any var name needs substitutions * or isn't a scalar, or if any var list needs substitutions. */ loopIndex = 0; for (i = 0, tokenPtr = parsePtr->tokenPtr; i < numWords-1; i++, tokenPtr += (tokenPtr->numComponents + 1)) { if (i%2 == 1) { if (tokenPtr->type != TCL_TOKEN_SIMPLE_WORD) { code = TCL_OUT_LINE_COMPILE; goto done; } else { /* Lots of copying going on here. Need a ListObj wizard * to show a better way. */ Tcl_DString varList; Tcl_DStringInit(&varList); Tcl_DStringAppend(&varList, tokenPtr[1].start, tokenPtr[1].size); code = Tcl_SplitList(interp, Tcl_DStringValue(&varList), &varcList[loopIndex], &varvList[loopIndex]); Tcl_DStringFree(&varList); if (code != TCL_OK) { goto done; } numVars = varcList[loopIndex]; for (j = 0; j < numVars; j++) { CONST char *varName = varvList[loopIndex][j]; if (!TclIsLocalScalar(varName, (int) strlen(varName))) { code = TCL_OUT_LINE_COMPILE; goto done; } } } loopIndex++; } } /* * We will compile the foreach command. * Reserve (numLists + 1) temporary variables: * - numLists temps to hold each value list * - 1 temp for the loop counter (index of next element in each list) * At this time we don't try to reuse temporaries; if there are two * nonoverlapping foreach loops, they don't share any temps. */ firstValueTemp = -1; for (loopIndex = 0; loopIndex < numLists; loopIndex++) { tempVar = TclFindCompiledLocal(NULL, /*nameChars*/ 0, /*create*/ 1, /*flags*/ VAR_SCALAR, procPtr); if (loopIndex == 0) { firstValueTemp = tempVar; } } loopCtTemp = TclFindCompiledLocal(NULL, /*nameChars*/ 0, /*create*/ 1, /*flags*/ VAR_SCALAR, procPtr); /* * Create and initialize the ForeachInfo and ForeachVarList data * structures describing this command. Then create a AuxData record * pointing to the ForeachInfo structure. */ infoPtr = (ForeachInfo *) ckalloc((unsigned) (sizeof(ForeachInfo) + (numLists * sizeof(ForeachVarList *)))); infoPtr->numLists = numLists; infoPtr->firstValueTemp = firstValueTemp; infoPtr->loopCtTemp = loopCtTemp; for (loopIndex = 0; loopIndex < numLists; loopIndex++) { ForeachVarList *varListPtr; numVars = varcList[loopIndex]; varListPtr = (ForeachVarList *) ckalloc((unsigned) sizeof(ForeachVarList) + (numVars * sizeof(int))); varListPtr->numVars = numVars; for (j = 0; j < numVars; j++) {
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?