tclcompcmds.c

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

C
2,168
字号
	    CONST char *varName = varvList[loopIndex][j];	    int nameChars = strlen(varName);	    varListPtr->varIndexes[j] = TclFindCompiledLocal(varName,		    nameChars, /*create*/ 1, /*flags*/ VAR_SCALAR, procPtr);	}	infoPtr->varLists[loopIndex] = varListPtr;    }    infoIndex = TclCreateAuxData((ClientData) infoPtr, &tclForeachInfoType, envPtr);    /*     * Evaluate then store each value list in the associated temporary.     */    range = TclCreateExceptRange(LOOP_EXCEPTION_RANGE, envPtr);        loopIndex = 0;    for (i = 0, tokenPtr = parsePtr->tokenPtr;	    i < numWords-1;	    i++, tokenPtr += (tokenPtr->numComponents + 1)) {	if ((i%2 == 0) && (i > 0)) {	    code = TclCompileTokens(interp, tokenPtr+1,		    tokenPtr->numComponents, envPtr);	    if (code != TCL_OK) {		goto done;	    }	    tempVar = (firstValueTemp + loopIndex);	    if (tempVar <= 255) {		TclEmitInstInt1(INST_STORE_SCALAR1, tempVar, envPtr);	    } else {		TclEmitInstInt4(INST_STORE_SCALAR4, tempVar, envPtr);	    }	    TclEmitOpcode(INST_POP, envPtr);	    loopIndex++;	}    }    /*     * Initialize the temporary var that holds the count of loop iterations.     */    TclEmitInstInt4(INST_FOREACH_START4, infoIndex, envPtr);        /*     * Top of loop code: assign each loop variable and check whether     * to terminate the loop.     */    envPtr->exceptArrayPtr[range].continueOffset =	    (envPtr->codeNext - envPtr->codeStart);    TclEmitInstInt4(INST_FOREACH_STEP4, infoIndex, envPtr);    TclEmitForwardJump(envPtr, TCL_FALSE_JUMP, &jumpFalseFixup);        /*     * Inline compile the loop body.     */    envPtr->exceptArrayPtr[range].codeOffset =	    (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    (\"foreach\" body line %d)",		    interp->errorLine);            Tcl_AddObjErrorInfo(interp, buffer, -1);        }	goto done;    }    envPtr->exceptArrayPtr[range].numCodeBytes =	    (envPtr->codeNext - envPtr->codeStart)	    - envPtr->exceptArrayPtr[range].codeOffset;    TclEmitOpcode(INST_POP, envPtr);	    /*     * Jump back to the test at the top of the loop. Generate a 4 byte jump     * if the distance to the test is > 120 bytes. This is conservative and     * ensures that we won't have to replace this jump if we later need to     * replace the ifFalse jump with a 4 byte jump.     */    jumpBackOffset = (envPtr->codeNext - envPtr->codeStart);    jumpBackDist =	(jumpBackOffset - envPtr->exceptArrayPtr[range].continueOffset);    if (jumpBackDist > 120) {	TclEmitInstInt4(INST_JUMP4, -jumpBackDist, envPtr);    } else {	TclEmitInstInt1(INST_JUMP1, -jumpBackDist, envPtr);    }    /*     * Fix the target of the jump after the foreach_step test.     */    jumpDist = (envPtr->codeNext - envPtr->codeStart)	    - jumpFalseFixup.codeOffset;    if (TclFixupForwardJump(envPtr, &jumpFalseFixup, jumpDist, 127)) {	/*	 * Update the loop body's starting PC offset since it moved down.	 */	envPtr->exceptArrayPtr[range].codeOffset += 3;	/*	 * Update the jump back to the test at the top of the loop since it	 * also moved down 3 bytes.	 */	jumpBackOffset += 3;	jumpPc = (envPtr->codeStart + jumpBackOffset);	jumpBackDist += 3;	if (jumpBackDist > 120) {	    TclUpdateInstInt4AtPc(INST_JUMP4, -jumpBackDist, jumpPc);	} else {	    TclUpdateInstInt1AtPc(INST_JUMP1, -jumpBackDist, jumpPc);	}    }    /*     * Set the loop's break target.     */    envPtr->exceptArrayPtr[range].breakOffset =	    (envPtr->codeNext - envPtr->codeStart);        /*     * The foreach command's result is an empty string.     */    envPtr->currStackDepth = savedStackDepth;    TclEmitPush(TclRegisterNewLiteral(envPtr, "", 0), envPtr);    envPtr->currStackDepth = savedStackDepth + 1;    done:    for (loopIndex = 0;  loopIndex < numLists;  loopIndex++) {	if (varvList[loopIndex] != (CONST char **) NULL) {	    ckfree((char *) varvList[loopIndex]);	}    }    if (varcList != varcListStaticSpace) {	ckfree((char *) varcList);        ckfree((char *) varvList);    }    envPtr->exceptDepth--;    return code;}/* *---------------------------------------------------------------------- * * DupForeachInfo -- * *	This procedure duplicates a ForeachInfo structure created as *	auxiliary data during the compilation of a foreach command. * * Results: *	A pointer to a newly allocated copy of the existing ForeachInfo *	structure is returned. * * Side effects: *	Storage for the copied ForeachInfo record is allocated. If the *	original ForeachInfo structure pointed to any ForeachVarList *	records, these structures are also copied and pointers to them *	are stored in the new ForeachInfo record. * *---------------------------------------------------------------------- */static ClientDataDupForeachInfo(clientData)    ClientData clientData;	/* The foreach command's compilation				 * auxiliary data to duplicate. */{    register ForeachInfo *srcPtr = (ForeachInfo *) clientData;    ForeachInfo *dupPtr;    register ForeachVarList *srcListPtr, *dupListPtr;    int numLists = srcPtr->numLists;    int numVars, i, j;        dupPtr = (ForeachInfo *) ckalloc((unsigned)	    (sizeof(ForeachInfo) + (numLists * sizeof(ForeachVarList *))));    dupPtr->numLists = numLists;    dupPtr->firstValueTemp = srcPtr->firstValueTemp;    dupPtr->loopCtTemp = srcPtr->loopCtTemp;        for (i = 0;  i < numLists;  i++) {	srcListPtr = srcPtr->varLists[i];	numVars = srcListPtr->numVars;	dupListPtr = (ForeachVarList *) ckalloc((unsigned)	        sizeof(ForeachVarList) + numVars*sizeof(int));	dupListPtr->numVars = numVars;	for (j = 0;  j < numVars;  j++) {	    dupListPtr->varIndexes[j] =	srcListPtr->varIndexes[j];	}	dupPtr->varLists[i] = dupListPtr;    }    return (ClientData) dupPtr;}/* *---------------------------------------------------------------------- * * FreeForeachInfo -- * *	Procedure to free a ForeachInfo structure created as auxiliary data *	during the compilation of a foreach command. * * Results: *	None. * * Side effects: *	Storage for the ForeachInfo structure pointed to by the ClientData *	argument is freed as is any ForeachVarList record pointed to by the *	ForeachInfo structure. * *---------------------------------------------------------------------- */static voidFreeForeachInfo(clientData)    ClientData clientData;	/* The foreach command's compilation				 * auxiliary data to free. */{    register ForeachInfo *infoPtr = (ForeachInfo *) clientData;    register ForeachVarList *listPtr;    int numLists = infoPtr->numLists;    register int i;    for (i = 0;  i < numLists;  i++) {	listPtr = infoPtr->varLists[i];	ckfree((char *) listPtr);    }    ckfree((char *) infoPtr);}/* *---------------------------------------------------------------------- * * TclCompileIfCmd -- * *	Procedure called to compile the "if" 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 TclCompileIfCmd, *	TCL_OUT_LINE_COMPILE is returned indicating that the if 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 "if" command *	at runtime. * *---------------------------------------------------------------------- */intTclCompileIfCmd(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. */{    JumpFixupArray jumpFalseFixupArray;    				/* Used to fix the ifFalse jump after each				 * test when its target PC is determined. */    JumpFixupArray jumpEndFixupArray;				/* Used to fix the jump after each "then"				 * body to the end of the "if" when that PC				 * is determined. */    Tcl_Token *tokenPtr, *testTokenPtr;    int jumpDist, jumpFalseDist;    int jumpIndex = 0;          /* avoid compiler warning. */    int numWords, wordIdx, numBytes, j, code;    CONST char *word;    char buffer[100];    int savedStackDepth = envPtr->currStackDepth;                                /* Saved stack depth at the start of the first				 * test; the envPtr current depth is restored				 * to this value at the start of each test. */    int realCond = 1;           /* set to 0 for static conditions: "if 0 {..}" */    int boolVal;                /* value of static condition */    int compileScripts = 1;                /*     * Only compile the "if" command if all arguments are simple     * words, in order to insure correct substitution [Bug 219166]     */    tokenPtr = parsePtr->tokenPtr;    wordIdx = 0;    numWords = parsePtr->numWords;    for (wordIdx = 0; wordIdx < numWords; wordIdx++) {	if (tokenPtr->type != TCL_TOKEN_SIMPLE_WORD) {	    return TCL_OUT_LINE_COMPILE;	}	tokenPtr += 2;    }    TclInitJumpFixupArray(&jumpFalseFixupArray);    TclInitJumpFixupArray(&jumpEndFixupArray);    code = TCL_OK;    /*     * Each iteration of this loop compiles one "if expr ?then? body"     * or "elseif expr ?then? body" clause.      */    tokenPtr = parsePtr->tokenPtr;    wordIdx = 0;    while (wordIdx < numWords) {	/*	 * Stop looping if the token isn't "if" or "elseif".	 */	word = tokenPtr[1].start;	numBytes = tokenPtr[1].size;	if ((tokenPtr == parsePtr->tokenPtr)	        || ((numBytes == 6) && (strncmp(word, "elseif", 6) == 0))) {	    tokenPtr += (tokenPtr->numComponents + 1);	    wordIdx++;	} else {	    break;	}	if (wordIdx >= numWords) {	    sprintf(buffer,	            "wrong # args: no expression after \"%.30s\" argument",		    word);	    Tcl_ResetResult(interp);	    Tcl_AppendToObj(Tcl_GetObjResult(interp), buffer, -1);	    code = TCL_ERROR;	    goto done;	}	/*	 * Compile the test expression then emit the conditional jump	 * around the "then" part. 	 */		envPtr->currStackDepth = savedStackDepth;	testTokenPtr = tokenPtr;	if (realCond) {	    /*	     * Find out if the condition is a constant. 	     */		    Tcl_Obj *boolObj = Tcl_NewStringObj(testTokenPtr[1].start,		    testTokenPtr[1].size);	    Tcl_IncrRefCount(boolObj);	    code = Tcl_GetBooleanFromObj(NULL, boolObj, &boolVal);	    Tcl_DecrRefCount(boolObj);	    if (code == TCL_OK) {		/*		 * A static condition		 */		realCond = 0;		if (!boolVal) {		    compileScripts = 0;		}	    } else {		Tcl_ResetResult(interp);		code = TclCompileExprWords(interp, testTokenPtr, 1, envPtr);		if (code != TCL_OK) {		    if (code == TCL_ERROR) {			Tcl_AddObjErrorInfo(interp,			        "\n    (\"if\" test expression)", -1);		    }		    goto done;		}		if (jumpFalseFixupArray.next >= jumpFalseFixupArray.end) {		    TclExpandJumpFixupArray(&jumpFalseFixupArray);		}		jumpIndex = jumpFalseFixupArray.next;		jumpFalseFixupArray.next++;		TclEmitForwardJump(envPtr, TCL_FALSE_JUMP,			       &(jumpFalseFixupArray.fixup[jumpIndex]));	    	    }	}	/*	 * Skip over the optional "then" before the then clause.	 */	tokenPtr = testTokenPtr + (testTokenPtr->numComponents + 1);	wordIdx++;	if (wordIdx >= numWords) {	    sprintf(buffer, "wrong # args: no script following \"%.20s\" argument", testTokenPtr->start);	    Tcl_ResetResult(interp);	    Tcl_AppendToObj(Tcl_GetObjResult(interp), buffer, -1);	    code = TCL_ERROR;	    goto done;	}	if (tokenPtr->type == TCL_TOKEN_SIMPLE_WORD) {	    word = tokenPtr[1].start;	    numBytes = tokenPtr[1].size;	    if ((numBytes == 4) && (strncmp(word, "then", 4) == 0)) {		tokenPtr += (tokenPtr->numComponents + 1);		wordIdx++;		if (wordIdx >= numWords) {		    Tcl_ResetResult(interp);		    Tcl_AppendToObj(Tcl_GetObjResult(interp),		            "wrong # args: no script following \"then\" argument", -1);		    code = TCL_ERROR;		    goto done;		}	    }	}	/*	 * Compile the "then" command body.	 */	if (compileScripts) {	    envPtr->currStackDepth = savedStackDepth;	    code = TclCompileCmdWord(interp, tokenPtr+1,	            tokenPtr->numComponents, envPtr);	    if (code != TCL_OK) {		if (code == TCL_ERROR) {		    sprintf(buffer, "\n    (\"if\" then script line %d)",		            interp->errorLine);		    Tcl_AddObjErrorInfo(interp, buffer, -1);		}		goto done;	    }		}	if (realCond) {

⌨️ 快捷键说明

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