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