tclcompcmds.c

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

C
2,168
字号
/*  * tclCompCmds.c -- * *	This file contains compilation procedures that compile various *	Tcl commands into a sequence of instructions ("bytecodes").  * * Copyright (c) 1997-1998 Sun Microsystems, Inc. * Copyright (c) 2001 by Kevin B. Kenny.  All rights reserved. * Copyright (c) 2002 ActiveState Corporation. * * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * * RCS: @(#) $Id: tclCompCmds.c,v 1.39 2003/02/07 01:07:05 mdejong Exp $ */#include "tclInt.h"#include "tclCompile.h"/* * Prototypes for procedures defined later in this file: */static ClientData	DupForeachInfo _ANSI_ARGS_((ClientData clientData));static void		FreeForeachInfo _ANSI_ARGS_((ClientData clientData));static int		TclPushVarName _ANSI_ARGS_((Tcl_Interp *interp,	Tcl_Token *varTokenPtr, CompileEnv *envPtr, int flags,	int *localIndexPtr, int *simpleVarNamePtr, int *isScalarPtr));/* * Flags bits used by TclPushVarName. */#define TCL_CREATE_VAR     1 /* Create a compiled local if none is found */#define TCL_NO_LARGE_INDEX 2 /* Do not return localIndex value > 255 *//* * The structures below define the AuxData types defined in this file. */AuxDataType tclForeachInfoType = {    "ForeachInfo",				/* name */    DupForeachInfo,				/* dupProc */    FreeForeachInfo				/* freeProc */};/* *---------------------------------------------------------------------- * * TclCompileAppendCmd -- * *	Procedure called to compile the "append" 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_AppendObjCmd) at runtime. * * Side effects: *	Instructions are added to envPtr to execute the "append" command *	at runtime. * *---------------------------------------------------------------------- */intTclCompileAppendCmd(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;    numWords = parsePtr->numWords;    if (numWords == 1) {	Tcl_ResetResult(interp);	Tcl_AppendToObj(Tcl_GetObjResult(interp),		"wrong # args: should be \"append varName ?value value ...?\"",		-1);	return TCL_ERROR;    } else if (numWords == 2) {	/*	 * append varName === set varName	 */        return TclCompileSetCmd(interp, parsePtr, envPtr);    } else if (numWords > 3) {	/*	 * APPEND instructions currently only handle one value	 */        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;    }    /*     * We are doing an assignment, otherwise TclCompileSetCmd was called,     * so push the new value.  This will need to be extended to push a     * value for each argument.     */    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.     */    if (simpleVarName) {	if (isScalar) {	    if (localIndex >= 0) {		if (localIndex <= 255) {		    TclEmitInstInt1(INST_APPEND_SCALAR1, localIndex, envPtr);		} else {		    TclEmitInstInt4(INST_APPEND_SCALAR4, localIndex, envPtr);		}	    } else {		TclEmitOpcode(INST_APPEND_STK, envPtr);	    }	} else {	    if (localIndex >= 0) {		if (localIndex <= 255) {		    TclEmitInstInt1(INST_APPEND_ARRAY1, localIndex, envPtr);		} else {		    TclEmitInstInt4(INST_APPEND_ARRAY4, localIndex, envPtr);		}	    } else {		TclEmitOpcode(INST_APPEND_ARRAY_STK, envPtr);	    }	}    } else {	TclEmitOpcode(INST_APPEND_STK, envPtr);    }    done:    return code;}/* *---------------------------------------------------------------------- * * TclCompileBreakCmd -- * *	Procedure called to compile the "break" command. * * Results: *	The return value is a standard Tcl result, which is TCL_OK unless *	there was an error during compilation. If an error occurs then *	the interpreter's result contains a standard error message. * * Side effects: *	Instructions are added to envPtr to execute the "break" command *	at runtime. * *---------------------------------------------------------------------- */intTclCompileBreakCmd(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. */{    if (parsePtr->numWords != 1) {	Tcl_ResetResult(interp);	Tcl_AppendToObj(Tcl_GetObjResult(interp),	        "wrong # args: should be \"break\"", -1);	return TCL_ERROR;    }    /*     * Emit a break instruction.     */    TclEmitOpcode(INST_BREAK, envPtr);    return TCL_OK;}/* *---------------------------------------------------------------------- * * TclCompileCatchCmd -- * *	Procedure called to compile the "catch" 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 TclCompileCatchCmd, *	TCL_OUT_LINE_COMPILE is returned indicating that the catch 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 "catch" command *	at runtime. * *---------------------------------------------------------------------- */intTclCompileCatchCmd(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. */{    JumpFixup jumpFixup;    Tcl_Token *cmdTokenPtr, *nameTokenPtr;    CONST char *name;    int localIndex, nameChars, range, startOffset, jumpDist;    int code;    int savedStackDepth = envPtr->currStackDepth;    if ((parsePtr->numWords != 2) && (parsePtr->numWords != 3)) {	Tcl_ResetResult(interp);	Tcl_AppendToObj(Tcl_GetObjResult(interp),	        "wrong # args: should be \"catch command ?varName?\"", -1);	return TCL_ERROR;    }    /*     * If a variable was specified and the catch command is at global level     * (not in a procedure), don't compile it inline: the payoff is     * too small.     */    if ((parsePtr->numWords == 3) && (envPtr->procPtr == NULL)) {	return TCL_OUT_LINE_COMPILE;    }    /*     * Make sure the variable name, if any, has no substitutions and just     * refers to a local scaler.     */    localIndex = -1;    cmdTokenPtr = parsePtr->tokenPtr	    + (parsePtr->tokenPtr->numComponents + 1);    if (parsePtr->numWords == 3) {	nameTokenPtr = cmdTokenPtr + (cmdTokenPtr->numComponents + 1);	if (nameTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) {	    name = nameTokenPtr[1].start;	    nameChars = nameTokenPtr[1].size;	    if (!TclIsLocalScalar(name, nameChars)) {		return TCL_OUT_LINE_COMPILE;	    }	    localIndex = TclFindCompiledLocal(nameTokenPtr[1].start,		    nameTokenPtr[1].size, /*create*/ 1, 		    /*flags*/ VAR_SCALAR, envPtr->procPtr);	} else {	   return TCL_OUT_LINE_COMPILE;	}    }    /*     * We will compile the catch command. Emit a beginCatch instruction at     * the start of the catch body: the subcommand it controls.     */        envPtr->exceptDepth++;    envPtr->maxExceptDepth =	TclMax(envPtr->exceptDepth, envPtr->maxExceptDepth);    range = TclCreateExceptRange(CATCH_EXCEPTION_RANGE, envPtr);    TclEmitInstInt4(INST_BEGIN_CATCH4, range, envPtr);    /*     * If the body is a simple word, compile the instructions to     * eval it. Otherwise, compile instructions to substitute its     * text without catching, a catch instruction that resets the      * stack to what it was before substituting the body, and then      * an instruction to eval the body. Care has to be taken to      * register the correct startOffset for the catch range so that     * errors in the substitution are not catched [Bug 219184]     */    if (cmdTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) {	startOffset = (envPtr->codeNext - envPtr->codeStart);	code = TclCompileCmdWord(interp, cmdTokenPtr+1, 1, envPtr);    } else {	code = TclCompileTokens(interp, cmdTokenPtr+1,	        cmdTokenPtr->numComponents, envPtr);	startOffset = (envPtr->codeNext - envPtr->codeStart);	TclEmitOpcode(INST_EVAL_STK, envPtr);    }    envPtr->exceptArrayPtr[range].codeOffset = startOffset;    if (code != TCL_OK) {	code = TCL_OUT_LINE_COMPILE;	goto done;    }    envPtr->exceptArrayPtr[range].numCodeBytes =	    (envPtr->codeNext - envPtr->codeStart) - startOffset;		        /*     * The "no errors" epilogue code: store the body's result into the     * variable (if any), push "0" (TCL_OK) as the catch's "no error"     * result, and jump around the "error case" code.     */    if (localIndex != -1) {	if (localIndex <= 255) {	    TclEmitInstInt1(INST_STORE_SCALAR1, localIndex, envPtr);	} else {	    TclEmitInstInt4(INST_STORE_SCALAR4, localIndex, envPtr);	}    }    TclEmitOpcode(INST_POP, envPtr);    TclEmitPush(TclRegisterNewLiteral(envPtr, "0", 1), envPtr);    TclEmitForwardJump(envPtr, TCL_UNCONDITIONAL_JUMP, &jumpFixup);    /*     * The "error case" code: store the body's result into the variable (if     * any), then push the error result code. The initial PC offset here is     * the catch's error target.     */    envPtr->currStackDepth = savedStackDepth;    envPtr->exceptArrayPtr[range].catchOffset =	    (envPtr->codeNext - envPtr->codeStart);    if (localIndex != -1) {	TclEmitOpcode(INST_PUSH_RESULT, envPtr);	if (localIndex <= 255) {	    TclEmitInstInt1(INST_STORE_SCALAR1, localIndex, envPtr);	} else {	    TclEmitInstInt4(INST_STORE_SCALAR4, localIndex, envPtr);	}	TclEmitOpcode(INST_POP, envPtr);    }    TclEmitOpcode(INST_PUSH_RETURN_CODE, envPtr);    /*     * Update the target of the jump after the "no errors" code, then emit     * an endCatch instruction at the end of the catch command.     */    jumpDist = (envPtr->codeNext - envPtr->codeStart)	    - jumpFixup.codeOffset;    if (TclFixupForwardJump(envPtr, &jumpFixup, jumpDist, 127)) {	panic("TclCompileCatchCmd: bad jump distance %d\n", jumpDist);    }    TclEmitOpcode(INST_END_CATCH, envPtr);    done:    envPtr->currStackDepth = savedStackDepth + 1;    envPtr->exceptDepth--;    return code;}/* *---------------------------------------------------------------------- * * TclCompileContinueCmd -- * *	Procedure called to compile the "continue" 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 "continue" command *	at runtime. * *---------------------------------------------------------------------- */intTclCompileContinueCmd(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. */{    /*     * There should be no argument after the "continue".     */    if (parsePtr->numWords != 1) {	Tcl_ResetResult(interp);	Tcl_AppendToObj(Tcl_GetObjResult(interp),	        "wrong # args: should be \"continue\"", -1);	return TCL_ERROR;    }    /*     * Emit a continue instruction.     */    TclEmitOpcode(INST_CONTINUE, envPtr);    return TCL_OK;}/* *---------------------------------------------------------------------- * * TclCompileExprCmd -- *

⌨️ 快捷键说明

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