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