tclcompcmds.c
来自「tcl是工具命令语言」· C语言 代码 · 共 2,168 行 · 第 1/5 页
C
2,168 行
/* * Jump to the end of the "if" command. Both jumpFalseFixupArray and * jumpEndFixupArray are indexed by "jumpIndex". */ if (jumpEndFixupArray.next >= jumpEndFixupArray.end) { TclExpandJumpFixupArray(&jumpEndFixupArray); } jumpEndFixupArray.next++; TclEmitForwardJump(envPtr, TCL_UNCONDITIONAL_JUMP, &(jumpEndFixupArray.fixup[jumpIndex])); /* * Fix the target of the jumpFalse after the test. Generate a 4 byte * jump if the distance is > 120 bytes. This is conservative, and * ensures that we won't have to replace this jump if we later also * need to replace the proceeding jump to the end of the "if" with a * 4 byte jump. */ jumpDist = (envPtr->codeNext - envPtr->codeStart) - jumpFalseFixupArray.fixup[jumpIndex].codeOffset; if (TclFixupForwardJump(envPtr, &(jumpFalseFixupArray.fixup[jumpIndex]), jumpDist, 120)) { /* * Adjust the code offset for the proceeding jump to the end * of the "if" command. */ jumpEndFixupArray.fixup[jumpIndex].codeOffset += 3; } } else if (boolVal) { /* *We were processing an "if 1 {...}"; stop compiling * scripts */ compileScripts = 0; } else { /* *We were processing an "if 0 {...}"; reset so that * the rest (elseif, else) is compiled correctly */ realCond = 1; compileScripts = 1; } tokenPtr += (tokenPtr->numComponents + 1); wordIdx++; } /* * Restore the current stack depth in the environment; the * "else" clause (or its default) will add 1 to this. */ envPtr->currStackDepth = savedStackDepth; /* * Check for the optional else clause. Do not compile * anything if this was an "if 1 {...}" case. */ if ((wordIdx < numWords) && (tokenPtr->type == TCL_TOKEN_SIMPLE_WORD)) { /* * There is an else clause. Skip over the optional "else" word. */ word = tokenPtr[1].start; numBytes = tokenPtr[1].size; if ((numBytes == 4) && (strncmp(word, "else", 4) == 0)) { tokenPtr += (tokenPtr->numComponents + 1); wordIdx++; if (wordIdx >= numWords) { Tcl_ResetResult(interp); Tcl_AppendToObj(Tcl_GetObjResult(interp), "wrong # args: no script following \"else\" argument", -1); code = TCL_ERROR; goto done; } } if (compileScripts) { /* * Compile the else command body. */ code = TclCompileCmdWord(interp, tokenPtr+1, tokenPtr->numComponents, envPtr); if (code != TCL_OK) { if (code == TCL_ERROR) { sprintf(buffer, "\n (\"if\" else script line %d)", interp->errorLine); Tcl_AddObjErrorInfo(interp, buffer, -1); } goto done; } } /* * Make sure there are no words after the else clause. */ wordIdx++; if (wordIdx < numWords) { Tcl_ResetResult(interp); Tcl_AppendToObj(Tcl_GetObjResult(interp), "wrong # args: extra words after \"else\" clause in \"if\" command", -1); code = TCL_ERROR; goto done; } } else { /* * No else clause: the "if" command's result is an empty string. */ if (compileScripts) { TclEmitPush(TclRegisterNewLiteral(envPtr, "", 0), envPtr); } } /* * Fix the unconditional jumps to the end of the "if" command. */ for (j = jumpEndFixupArray.next; j > 0; j--) { jumpIndex = (j - 1); /* i.e. process the closest jump first */ jumpDist = (envPtr->codeNext - envPtr->codeStart) - jumpEndFixupArray.fixup[jumpIndex].codeOffset; if (TclFixupForwardJump(envPtr, &(jumpEndFixupArray.fixup[jumpIndex]), jumpDist, 127)) { /* * Adjust the immediately preceeding "ifFalse" jump. We moved * it's target (just after this jump) down three bytes. */ unsigned char *ifFalsePc = envPtr->codeStart + jumpFalseFixupArray.fixup[jumpIndex].codeOffset; unsigned char opCode = *ifFalsePc; if (opCode == INST_JUMP_FALSE1) { jumpFalseDist = TclGetInt1AtPtr(ifFalsePc + 1); jumpFalseDist += 3; TclStoreInt1AtPtr(jumpFalseDist, (ifFalsePc + 1)); } else if (opCode == INST_JUMP_FALSE4) { jumpFalseDist = TclGetInt4AtPtr(ifFalsePc + 1); jumpFalseDist += 3; TclStoreInt4AtPtr(jumpFalseDist, (ifFalsePc + 1)); } else { panic("TclCompileIfCmd: unexpected opcode updating ifFalse jump"); } } } /* * Free the jumpFixupArray array if malloc'ed storage was used. */ done: envPtr->currStackDepth = savedStackDepth + 1; TclFreeJumpFixupArray(&jumpFalseFixupArray); TclFreeJumpFixupArray(&jumpEndFixupArray); return code;}/* *---------------------------------------------------------------------- * * TclCompileIncrCmd -- * * Procedure called to compile the "incr" 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 TclCompileIncrCmd, * TCL_OUT_LINE_COMPILE is returned indicating that the incr 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 "incr" command * at runtime. * *---------------------------------------------------------------------- */intTclCompileIncrCmd(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 *varTokenPtr, *incrTokenPtr; int simpleVarName, isScalar, localIndex, haveImmValue, immValue; int code = TCL_OK; if ((parsePtr->numWords != 2) && (parsePtr->numWords != 3)) { Tcl_ResetResult(interp); Tcl_AppendToObj(Tcl_GetObjResult(interp), "wrong # args: should be \"incr varName ?increment?\"", -1); return TCL_ERROR; } varTokenPtr = parsePtr->tokenPtr + (parsePtr->tokenPtr->numComponents + 1); code = TclPushVarName(interp, varTokenPtr, envPtr, (TCL_NO_LARGE_INDEX | TCL_CREATE_VAR), &localIndex, &simpleVarName, &isScalar); if (code != TCL_OK) { goto done; } /* * If an increment is given, push it, but see first if it's a small * integer. */ haveImmValue = 0; immValue = 0; if (parsePtr->numWords == 3) { incrTokenPtr = varTokenPtr + (varTokenPtr->numComponents + 1); if (incrTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) { CONST char *word = incrTokenPtr[1].start; int numBytes = incrTokenPtr[1].size; int validLength = TclParseInteger(word, numBytes); long n; /* * Note there is a danger that modifying the string could have * undesirable side effects. In this case, TclLooksLikeInt and * TclGetLong do not have any dependencies on shared strings so we * should be safe. */ if (validLength == numBytes) { int code; Tcl_Obj *longObj = Tcl_NewStringObj(word, numBytes); Tcl_IncrRefCount(longObj); code = Tcl_GetLongFromObj(NULL, longObj, &n); Tcl_DecrRefCount(longObj); if ((code == TCL_OK) && (-127 <= n) && (n <= 127)) { haveImmValue = 1; immValue = n; } } if (!haveImmValue) { TclEmitPush( TclRegisterNewLiteral(envPtr, word, numBytes), envPtr); } } else { code = TclCompileTokens(interp, incrTokenPtr+1, incrTokenPtr->numComponents, envPtr); if (code != TCL_OK) { goto done; } } } else { /* no incr amount given so use 1 */ haveImmValue = 1; immValue = 1; } /* * Emit the instruction to increment the variable. */ if (simpleVarName) { if (isScalar) { if (localIndex >= 0) { if (haveImmValue) { TclEmitInstInt1(INST_INCR_SCALAR1_IMM, localIndex, envPtr); TclEmitInt1(immValue, envPtr); } else { TclEmitInstInt1(INST_INCR_SCALAR1, localIndex, envPtr); } } else { if (haveImmValue) { TclEmitInstInt1(INST_INCR_SCALAR_STK_IMM, immValue, envPtr); } else { TclEmitOpcode(INST_INCR_SCALAR_STK, envPtr); } } } else { if (localIndex >= 0) { if (haveImmValue) { TclEmitInstInt1(INST_INCR_ARRAY1_IMM, localIndex, envPtr); TclEmitInt1(immValue, envPtr); } else { TclEmitInstInt1(INST_INCR_ARRAY1, localIndex, envPtr); } } else { if (haveImmValue) { TclEmitInstInt1(INST_INCR_ARRAY_STK_IMM, immValue, envPtr); } else { TclEmitOpcode(INST_INCR_ARRAY_STK, envPtr); } } } } else { /* non-simple variable name */ if (haveImmValue) { TclEmitInstInt1(INST_INCR_STK_IMM, immValue, envPtr); } else { TclEmitOpcode(INST_INCR_STK, envPtr); } } done: return code;}/* *---------------------------------------------------------------------- * * TclCompileLappendCmd -- * * Procedure called to compile the "lappend" command. * * Results: * The return value is a standard Tcl result, which is normally TCL_OK * unless there was an error while parsing string. If an error occurs * then the interpreter's result contains a standard error message. If * complation fails because the command requires a second level of * substitutions, TCL_OUT_LINE_COMPILE is returned indicating that the * command should be compiled "out of line" by emitting code to * invoke its command procedure (Tcl_LappendObjCmd) at runtime. * * Side effects: * Instructions are added to envPtr to execute the "lappend" command * at runtime. * *---------------------------------------------------------------------- */intTclCompileLappendCmd(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 *varTokenPtr, *valueTokenPtr; int simpleVarName, isScalar, localIndex, numWords; int code = TCL_OK; /* * If we're not in a procedure, don't compile. */ if (envPtr->procPtr == NULL) { return TCL_OUT_LINE_COMPILE; } numWords = parsePtr->numWords; if (numWords == 1) { Tcl_ResetResult(interp); Tcl_AppendToObj(Tcl_GetObjResult(interp), "wrong # args: should be \"lappend varName ?value value ...?\"", -1); return TCL_ERROR; } if (numWords != 3) { /* * LAPPEND instructions currently only handle one value appends */ return TCL_OUT_LINE_COMPILE; } /* * Decide if we can use a frame slot for the var/array name or if we * need to emit code to compute and push the name at runtime. We use a * frame slot (entry in the array of local vars) if we are compiling a * procedure body and if the name is simple text that does not include * namespace qualifiers. */ varTokenPtr = parsePtr->tokenPtr + (parsePtr->tokenPtr->numComponents + 1); code = TclPushVarName(interp, varTokenPtr, envPtr, TCL_CREATE_VAR, &localIndex, &simpleVarName, &isScalar); if (code != TCL_OK) { goto done; } /* * If we are doing an assignment, push the new value. * In the no values case, create an empty object. */ if (numWords > 2) { valueTokenPtr = varTokenPtr + (varTokenPtr->numComponents + 1); if (valueTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) { TclEmitPush(TclRegisterNewLiteral(envPtr, valueTokenPtr[1].start, valueTokenPtr[1].size), envPtr); } else { code = TclCompileTokens(interp, valueTokenPtr+1, valueTokenPtr->numComponents, envPtr); if (code != TCL_OK) { goto done; } } } /* * Emit instructions to set/get the variable. */ /* * The *_STK opcodes should be refactored to make better use of existing * LOAD/STORE instructions. */ if (simpleVarName) { if (isScalar) { if (localIndex >= 0) { if (localIndex <= 255) { TclEmitInstInt1(INST_LAPPEND_SCALAR1, localIndex, envPtr); } else { TclEmitInstInt4(INST_LAPPEND_SCALAR4, localIndex, envPtr); } } else { TclEmitOpcode(INST_LAPPEND_STK, envPtr); } } else { if (localIndex >= 0) { if (localIndex <= 255) { TclEmitInstInt1(INST_LAPPEND_ARRAY1, localIndex, envPtr); } else { TclEmitInstInt4(INST_LAPPEND_ARRAY4, localIndex, envPtr); } } else { TclEmitOpcode(INST_LAPPEND_ARRAY_STK, envPtr); }
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?