sl@0: /* 
sl@0:  * tclCompCmds.c --
sl@0:  *
sl@0:  *	This file contains compilation procedures that compile various
sl@0:  *	Tcl commands into a sequence of instructions ("bytecodes"). 
sl@0:  *
sl@0:  * Copyright (c) 1997-1998 Sun Microsystems, Inc.
sl@0:  * Copyright (c) 2001 by Kevin B. Kenny.  All rights reserved.
sl@0:  * Copyright (c) 2002 ActiveState Corporation.
sl@0:  *
sl@0:  * See the file "license.terms" for information on usage and redistribution
sl@0:  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
sl@0:  *
sl@0:  * RCS: @(#) $Id: tclCompCmds.c,v 1.39.2.6 2007/03/01 16:06:19 dkf Exp $
sl@0:  */
sl@0: 
sl@0: #include "tclInt.h"
sl@0: #include "tclCompile.h"
sl@0: 
sl@0: /*
sl@0:  * Prototypes for procedures defined later in this file:
sl@0:  */
sl@0: 
sl@0: static ClientData	DupForeachInfo _ANSI_ARGS_((ClientData clientData));
sl@0: static void		FreeForeachInfo _ANSI_ARGS_((ClientData clientData));
sl@0: #ifndef TCL_TIP280
sl@0: static int		TclPushVarName _ANSI_ARGS_((Tcl_Interp *interp,
sl@0: 	Tcl_Token *varTokenPtr, CompileEnv *envPtr, int flags,
sl@0: 	int *localIndexPtr, int *simpleVarNamePtr, int *isScalarPtr));
sl@0: #else
sl@0: static int		TclPushVarName _ANSI_ARGS_((Tcl_Interp *interp,
sl@0: 	Tcl_Token *varTokenPtr, CompileEnv *envPtr, int flags,
sl@0: 	int *localIndexPtr, int *simpleVarNamePtr, int *isScalarPtr,
sl@0: 	int line));
sl@0: #endif
sl@0: 
sl@0: /*
sl@0:  * Flags bits used by TclPushVarName.
sl@0:  */
sl@0: 
sl@0: #define TCL_CREATE_VAR     1 /* Create a compiled local if none is found */
sl@0: #define TCL_NO_LARGE_INDEX 2 /* Do not return localIndex value > 255 */
sl@0: 
sl@0: /*
sl@0:  * The structures below define the AuxData types defined in this file.
sl@0:  */
sl@0: 
sl@0: AuxDataType tclForeachInfoType = {
sl@0:     "ForeachInfo",				/* name */
sl@0:     DupForeachInfo,				/* dupProc */
sl@0:     FreeForeachInfo				/* freeProc */
sl@0: };
sl@0: 
sl@0: /*
sl@0:  *----------------------------------------------------------------------
sl@0:  *
sl@0:  * TclCompileAppendCmd --
sl@0:  *
sl@0:  *	Procedure called to compile the "append" command.
sl@0:  *
sl@0:  * Results:
sl@0:  *	The return value is a standard Tcl result, which is normally TCL_OK
sl@0:  *	unless there was an error while parsing string. If an error occurs
sl@0:  *	then the interpreter's result contains a standard error message. If
sl@0:  *	complation fails because the command requires a second level of
sl@0:  *	substitutions, TCL_OUT_LINE_COMPILE is returned indicating that the
sl@0:  *	command should be compiled "out of line" by emitting code to
sl@0:  *	invoke its command procedure (Tcl_AppendObjCmd) at runtime.
sl@0:  *
sl@0:  * Side effects:
sl@0:  *	Instructions are added to envPtr to execute the "append" command
sl@0:  *	at runtime.
sl@0:  *
sl@0:  *----------------------------------------------------------------------
sl@0:  */
sl@0: 
sl@0: int
sl@0: TclCompileAppendCmd(interp, parsePtr, envPtr)
sl@0:     Tcl_Interp *interp;		/* Used for error reporting. */
sl@0:     Tcl_Parse *parsePtr;	/* Points to a parse structure for the
sl@0: 				 * command created by Tcl_ParseCommand. */
sl@0:     CompileEnv *envPtr;		/* Holds resulting instructions. */
sl@0: {
sl@0:     Tcl_Token *varTokenPtr, *valueTokenPtr;
sl@0:     int simpleVarName, isScalar, localIndex, numWords;
sl@0:     int code = TCL_OK;
sl@0: 
sl@0: #ifdef TCL_TIP280
sl@0:     /* TIP #280 : Remember the per-word line information of the current
sl@0:      * command. An index is used instead of a pointer as recursive compilation
sl@0:      * may reallocate, i.e. move, the array. This is also the reason to save
sl@0:      * the nuloc now, it may change during the course of the function.
sl@0:      */
sl@0:     ExtCmdLoc* mapPtr   = envPtr->extCmdMapPtr;
sl@0:     int        eclIndex = mapPtr->nuloc - 1;
sl@0: #endif
sl@0: 
sl@0:     numWords = parsePtr->numWords;
sl@0:     if (numWords == 1) {
sl@0: 	Tcl_ResetResult(interp);
sl@0: 	Tcl_AppendToObj(Tcl_GetObjResult(interp),
sl@0: 		"wrong # args: should be \"append varName ?value value ...?\"",
sl@0: 		-1);
sl@0: 	return TCL_ERROR;
sl@0:     } else if (numWords == 2) {
sl@0: 	/*
sl@0: 	 * append varName === set varName
sl@0: 	 */
sl@0:         return TclCompileSetCmd(interp, parsePtr, envPtr);
sl@0:     } else if (numWords > 3) {
sl@0: 	/*
sl@0: 	 * APPEND instructions currently only handle one value
sl@0: 	 */
sl@0:         return TCL_OUT_LINE_COMPILE;
sl@0:     }
sl@0: 
sl@0:     /*
sl@0:      * Decide if we can use a frame slot for the var/array name or if we
sl@0:      * need to emit code to compute and push the name at runtime. We use a
sl@0:      * frame slot (entry in the array of local vars) if we are compiling a
sl@0:      * procedure body and if the name is simple text that does not include
sl@0:      * namespace qualifiers. 
sl@0:      */
sl@0: 
sl@0:     varTokenPtr = parsePtr->tokenPtr
sl@0: 	    + (parsePtr->tokenPtr->numComponents + 1);
sl@0: 
sl@0:     code = TclPushVarName(interp, varTokenPtr, envPtr, TCL_CREATE_VAR,
sl@0: #ifndef TCL_TIP280
sl@0: 	    &localIndex, &simpleVarName, &isScalar);
sl@0: #else
sl@0: 	    &localIndex, &simpleVarName, &isScalar,
sl@0: 	    mapPtr->loc [eclIndex].line [1]);
sl@0: #endif
sl@0:     if (code != TCL_OK) {
sl@0: 	goto done;
sl@0:     }
sl@0: 
sl@0:     /*
sl@0:      * We are doing an assignment, otherwise TclCompileSetCmd was called,
sl@0:      * so push the new value.  This will need to be extended to push a
sl@0:      * value for each argument.
sl@0:      */
sl@0: 
sl@0:     if (numWords > 2) {
sl@0: 	valueTokenPtr = varTokenPtr + (varTokenPtr->numComponents + 1);
sl@0: 	if (valueTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) {
sl@0: 	    TclEmitPush(TclRegisterNewLiteral(envPtr, 
sl@0: 		    valueTokenPtr[1].start, valueTokenPtr[1].size), envPtr);
sl@0: 	} else {
sl@0: #ifdef TCL_TIP280
sl@0: 	    envPtr->line = mapPtr->loc [eclIndex].line [2];
sl@0: #endif
sl@0: 	    code = TclCompileTokens(interp, valueTokenPtr+1,
sl@0: 	            valueTokenPtr->numComponents, envPtr);
sl@0: 	    if (code != TCL_OK) {
sl@0: 		goto done;
sl@0: 	    }
sl@0: 	}
sl@0:     }
sl@0: 
sl@0:     /*
sl@0:      * Emit instructions to set/get the variable.
sl@0:      */
sl@0: 
sl@0:     if (simpleVarName) {
sl@0: 	if (isScalar) {
sl@0: 	    if (localIndex >= 0) {
sl@0: 		if (localIndex <= 255) {
sl@0: 		    TclEmitInstInt1(INST_APPEND_SCALAR1, localIndex, envPtr);
sl@0: 		} else {
sl@0: 		    TclEmitInstInt4(INST_APPEND_SCALAR4, localIndex, envPtr);
sl@0: 		}
sl@0: 	    } else {
sl@0: 		TclEmitOpcode(INST_APPEND_STK, envPtr);
sl@0: 	    }
sl@0: 	} else {
sl@0: 	    if (localIndex >= 0) {
sl@0: 		if (localIndex <= 255) {
sl@0: 		    TclEmitInstInt1(INST_APPEND_ARRAY1, localIndex, envPtr);
sl@0: 		} else {
sl@0: 		    TclEmitInstInt4(INST_APPEND_ARRAY4, localIndex, envPtr);
sl@0: 		}
sl@0: 	    } else {
sl@0: 		TclEmitOpcode(INST_APPEND_ARRAY_STK, envPtr);
sl@0: 	    }
sl@0: 	}
sl@0:     } else {
sl@0: 	TclEmitOpcode(INST_APPEND_STK, envPtr);
sl@0:     }
sl@0: 
sl@0:     done:
sl@0:     return code;
sl@0: }
sl@0: 
sl@0: /*
sl@0:  *----------------------------------------------------------------------
sl@0:  *
sl@0:  * TclCompileBreakCmd --
sl@0:  *
sl@0:  *	Procedure called to compile the "break" command.
sl@0:  *
sl@0:  * Results:
sl@0:  *	The return value is a standard Tcl result, which is TCL_OK unless
sl@0:  *	there was an error during compilation. If an error occurs then
sl@0:  *	the interpreter's result contains a standard error message.
sl@0:  *
sl@0:  * Side effects:
sl@0:  *	Instructions are added to envPtr to execute the "break" command
sl@0:  *	at runtime.
sl@0:  *
sl@0:  *----------------------------------------------------------------------
sl@0:  */
sl@0: 
sl@0: int
sl@0: TclCompileBreakCmd(interp, parsePtr, envPtr)
sl@0:     Tcl_Interp *interp;		/* Used for error reporting. */
sl@0:     Tcl_Parse *parsePtr;	/* Points to a parse structure for the
sl@0: 				 * command created by Tcl_ParseCommand. */
sl@0:     CompileEnv *envPtr;		/* Holds resulting instructions. */
sl@0: {
sl@0:     if (parsePtr->numWords != 1) {
sl@0: 	Tcl_ResetResult(interp);
sl@0: 	Tcl_AppendToObj(Tcl_GetObjResult(interp),
sl@0: 	        "wrong # args: should be \"break\"", -1);
sl@0: 	return TCL_ERROR;
sl@0:     }
sl@0: 
sl@0:     /*
sl@0:      * Emit a break instruction.
sl@0:      */
sl@0: 
sl@0:     TclEmitOpcode(INST_BREAK, envPtr);
sl@0:     return TCL_OK;
sl@0: }
sl@0: 
sl@0: /*
sl@0:  *----------------------------------------------------------------------
sl@0:  *
sl@0:  * TclCompileCatchCmd --
sl@0:  *
sl@0:  *	Procedure called to compile the "catch" command.
sl@0:  *
sl@0:  * Results:
sl@0:  *	The return value is a standard Tcl result, which is TCL_OK if
sl@0:  *	compilation was successful. If an error occurs then the
sl@0:  *	interpreter's result contains a standard error message and TCL_ERROR
sl@0:  *	is returned. If the command is too complex for TclCompileCatchCmd,
sl@0:  *	TCL_OUT_LINE_COMPILE is returned indicating that the catch command
sl@0:  *	should be compiled "out of line" by emitting code to invoke its
sl@0:  *	command procedure at runtime.
sl@0:  *
sl@0:  * Side effects:
sl@0:  *	Instructions are added to envPtr to execute the "catch" command
sl@0:  *	at runtime.
sl@0:  *
sl@0:  *----------------------------------------------------------------------
sl@0:  */
sl@0: 
sl@0: int
sl@0: TclCompileCatchCmd(interp, parsePtr, envPtr)
sl@0:     Tcl_Interp *interp;		/* Used for error reporting. */
sl@0:     Tcl_Parse *parsePtr;	/* Points to a parse structure for the
sl@0: 				 * command created by Tcl_ParseCommand. */
sl@0:     CompileEnv *envPtr;		/* Holds resulting instructions. */
sl@0: {
sl@0:     JumpFixup jumpFixup;
sl@0:     Tcl_Token *cmdTokenPtr, *nameTokenPtr;
sl@0:     CONST char *name;
sl@0:     int localIndex, nameChars, range, startOffset, jumpDist;
sl@0:     int code;
sl@0:     int savedStackDepth = envPtr->currStackDepth;
sl@0: 
sl@0: #ifdef TCL_TIP280
sl@0:     /* TIP #280 : Remember the per-word line information of the current
sl@0:      * command. An index is used instead of a pointer as recursive compilation
sl@0:      * may reallocate, i.e. move, the array. This is also the reason to save
sl@0:      * the nuloc now, it may change during the course of the function.
sl@0:      */
sl@0:     ExtCmdLoc* mapPtr   = envPtr->extCmdMapPtr;
sl@0:     int        eclIndex = mapPtr->nuloc - 1;
sl@0: #endif
sl@0: 
sl@0:     if ((parsePtr->numWords != 2) && (parsePtr->numWords != 3)) {
sl@0: 	Tcl_ResetResult(interp);
sl@0: 	Tcl_AppendToObj(Tcl_GetObjResult(interp),
sl@0: 	        "wrong # args: should be \"catch command ?varName?\"", -1);
sl@0: 	return TCL_ERROR;
sl@0:     }
sl@0: 
sl@0:     /*
sl@0:      * If a variable was specified and the catch command is at global level
sl@0:      * (not in a procedure), don't compile it inline: the payoff is
sl@0:      * too small.
sl@0:      */
sl@0: 
sl@0:     if ((parsePtr->numWords == 3) && (envPtr->procPtr == NULL)) {
sl@0: 	return TCL_OUT_LINE_COMPILE;
sl@0:     }
sl@0: 
sl@0:     /*
sl@0:      * Make sure the variable name, if any, has no substitutions and just
sl@0:      * refers to a local scaler.
sl@0:      */
sl@0: 
sl@0:     localIndex = -1;
sl@0:     cmdTokenPtr = parsePtr->tokenPtr
sl@0: 	    + (parsePtr->tokenPtr->numComponents + 1);
sl@0:     if (parsePtr->numWords == 3) {
sl@0: 	nameTokenPtr = cmdTokenPtr + (cmdTokenPtr->numComponents + 1);
sl@0: 	if (nameTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) {
sl@0: 	    name = nameTokenPtr[1].start;
sl@0: 	    nameChars = nameTokenPtr[1].size;
sl@0: 	    if (!TclIsLocalScalar(name, nameChars)) {
sl@0: 		return TCL_OUT_LINE_COMPILE;
sl@0: 	    }
sl@0: 	    localIndex = TclFindCompiledLocal(nameTokenPtr[1].start,
sl@0: 		    nameTokenPtr[1].size, /*create*/ 1, 
sl@0: 		    /*flags*/ VAR_SCALAR, envPtr->procPtr);
sl@0: 	} else {
sl@0: 	   return TCL_OUT_LINE_COMPILE;
sl@0: 	}
sl@0:     }
sl@0: 
sl@0:     /*
sl@0:      * We will compile the catch command. Emit a beginCatch instruction at
sl@0:      * the start of the catch body: the subcommand it controls.
sl@0:      */
sl@0:     
sl@0:     envPtr->exceptDepth++;
sl@0:     envPtr->maxExceptDepth =
sl@0: 	TclMax(envPtr->exceptDepth, envPtr->maxExceptDepth);
sl@0:     range = TclCreateExceptRange(CATCH_EXCEPTION_RANGE, envPtr);
sl@0:     TclEmitInstInt4(INST_BEGIN_CATCH4, range, envPtr);
sl@0: 
sl@0:     /*
sl@0:      * If the body is a simple word, compile the instructions to
sl@0:      * eval it. Otherwise, compile instructions to substitute its
sl@0:      * text without catching, a catch instruction that resets the 
sl@0:      * stack to what it was before substituting the body, and then 
sl@0:      * an instruction to eval the body. Care has to be taken to 
sl@0:      * register the correct startOffset for the catch range so that
sl@0:      * errors in the substitution are not catched [Bug 219184]
sl@0:      */
sl@0: 
sl@0: #ifdef TCL_TIP280
sl@0:     envPtr->line = mapPtr->loc [eclIndex].line [1];
sl@0: #endif
sl@0:     if (cmdTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) {
sl@0: 	startOffset = (envPtr->codeNext - envPtr->codeStart);
sl@0: 	code = TclCompileCmdWord(interp, cmdTokenPtr+1, 1, envPtr);
sl@0:     } else {
sl@0: 	code = TclCompileTokens(interp, cmdTokenPtr+1,
sl@0: 	        cmdTokenPtr->numComponents, envPtr);
sl@0: 	startOffset = (envPtr->codeNext - envPtr->codeStart);
sl@0: 	TclEmitOpcode(INST_EVAL_STK, envPtr);
sl@0:     }
sl@0:     envPtr->exceptArrayPtr[range].codeOffset = startOffset;
sl@0: 
sl@0:     if (code != TCL_OK) {
sl@0: 	code = TCL_OUT_LINE_COMPILE;
sl@0: 	goto done;
sl@0:     }
sl@0:     envPtr->exceptArrayPtr[range].numCodeBytes =
sl@0: 	    (envPtr->codeNext - envPtr->codeStart) - startOffset;
sl@0: 		    
sl@0:     /*
sl@0:      * The "no errors" epilogue code: store the body's result into the
sl@0:      * variable (if any), push "0" (TCL_OK) as the catch's "no error"
sl@0:      * result, and jump around the "error case" code.
sl@0:      */
sl@0: 
sl@0:     if (localIndex != -1) {
sl@0: 	if (localIndex <= 255) {
sl@0: 	    TclEmitInstInt1(INST_STORE_SCALAR1, localIndex, envPtr);
sl@0: 	} else {
sl@0: 	    TclEmitInstInt4(INST_STORE_SCALAR4, localIndex, envPtr);
sl@0: 	}
sl@0:     }
sl@0:     TclEmitOpcode(INST_POP, envPtr);
sl@0:     TclEmitPush(TclRegisterNewLiteral(envPtr, "0", 1), envPtr);
sl@0:     TclEmitForwardJump(envPtr, TCL_UNCONDITIONAL_JUMP, &jumpFixup);
sl@0: 
sl@0:     /*
sl@0:      * The "error case" code: store the body's result into the variable (if
sl@0:      * any), then push the error result code. The initial PC offset here is
sl@0:      * the catch's error target.
sl@0:      */
sl@0: 
sl@0:     envPtr->currStackDepth = savedStackDepth;
sl@0:     envPtr->exceptArrayPtr[range].catchOffset =
sl@0: 	    (envPtr->codeNext - envPtr->codeStart);
sl@0:     if (localIndex != -1) {
sl@0: 	TclEmitOpcode(INST_PUSH_RESULT, envPtr);
sl@0: 	if (localIndex <= 255) {
sl@0: 	    TclEmitInstInt1(INST_STORE_SCALAR1, localIndex, envPtr);
sl@0: 	} else {
sl@0: 	    TclEmitInstInt4(INST_STORE_SCALAR4, localIndex, envPtr);
sl@0: 	}
sl@0: 	TclEmitOpcode(INST_POP, envPtr);
sl@0:     }
sl@0:     TclEmitOpcode(INST_PUSH_RETURN_CODE, envPtr);
sl@0: 
sl@0: 
sl@0:     /*
sl@0:      * Update the target of the jump after the "no errors" code, then emit
sl@0:      * an endCatch instruction at the end of the catch command.
sl@0:      */
sl@0: 
sl@0:     jumpDist = (envPtr->codeNext - envPtr->codeStart)
sl@0: 	    - jumpFixup.codeOffset;
sl@0:     if (TclFixupForwardJump(envPtr, &jumpFixup, jumpDist, 127)) {
sl@0: 	panic("TclCompileCatchCmd: bad jump distance %d\n", jumpDist);
sl@0:     }
sl@0:     TclEmitOpcode(INST_END_CATCH, envPtr);
sl@0: 
sl@0:     done:
sl@0:     envPtr->currStackDepth = savedStackDepth + 1;
sl@0:     envPtr->exceptDepth--;
sl@0:     return code;
sl@0: }
sl@0: 
sl@0: /*
sl@0:  *----------------------------------------------------------------------
sl@0:  *
sl@0:  * TclCompileContinueCmd --
sl@0:  *
sl@0:  *	Procedure called to compile the "continue" command.
sl@0:  *
sl@0:  * Results:
sl@0:  *	The return value is a standard Tcl result, which is TCL_OK unless
sl@0:  *	there was an error while parsing string. If an error occurs then
sl@0:  *	the interpreter's result contains a standard error message.
sl@0:  *
sl@0:  * Side effects:
sl@0:  *	Instructions are added to envPtr to execute the "continue" command
sl@0:  *	at runtime.
sl@0:  *
sl@0:  *----------------------------------------------------------------------
sl@0:  */
sl@0: 
sl@0: int
sl@0: TclCompileContinueCmd(interp, parsePtr, envPtr)
sl@0:     Tcl_Interp *interp;		/* Used for error reporting. */
sl@0:     Tcl_Parse *parsePtr;	/* Points to a parse structure for the
sl@0: 				 * command created by Tcl_ParseCommand. */
sl@0:     CompileEnv *envPtr;		/* Holds resulting instructions. */
sl@0: {
sl@0:     /*
sl@0:      * There should be no argument after the "continue".
sl@0:      */
sl@0: 
sl@0:     if (parsePtr->numWords != 1) {
sl@0: 	Tcl_ResetResult(interp);
sl@0: 	Tcl_AppendToObj(Tcl_GetObjResult(interp),
sl@0: 	        "wrong # args: should be \"continue\"", -1);
sl@0: 	return TCL_ERROR;
sl@0:     }
sl@0: 
sl@0:     /*
sl@0:      * Emit a continue instruction.
sl@0:      */
sl@0: 
sl@0:     TclEmitOpcode(INST_CONTINUE, envPtr);
sl@0:     return TCL_OK;
sl@0: }
sl@0: 
sl@0: /*
sl@0:  *----------------------------------------------------------------------
sl@0:  *
sl@0:  * TclCompileExprCmd --
sl@0:  *
sl@0:  *	Procedure called to compile the "expr" command.
sl@0:  *
sl@0:  * Results:
sl@0:  *	The return value is a standard Tcl result, which is TCL_OK
sl@0:  *	unless there was an error while parsing string. If an error occurs
sl@0:  *	then the interpreter's result contains a standard error message.
sl@0:  *
sl@0:  * Side effects:
sl@0:  *	Instructions are added to envPtr to execute the "expr" command
sl@0:  *	at runtime.
sl@0:  *
sl@0:  *----------------------------------------------------------------------
sl@0:  */
sl@0: 
sl@0: int
sl@0: TclCompileExprCmd(interp, parsePtr, envPtr)
sl@0:     Tcl_Interp *interp;		/* Used for error reporting. */
sl@0:     Tcl_Parse *parsePtr;	/* Points to a parse structure for the
sl@0: 				 * command created by Tcl_ParseCommand. */
sl@0:     CompileEnv *envPtr;		/* Holds resulting instructions. */
sl@0: {
sl@0:     Tcl_Token *firstWordPtr;
sl@0: 
sl@0:     if (parsePtr->numWords == 1) {
sl@0: 	Tcl_ResetResult(interp);
sl@0: 	Tcl_AppendToObj(Tcl_GetObjResult(interp),
sl@0: 	        "wrong # args: should be \"expr arg ?arg ...?\"", -1);
sl@0:         return TCL_ERROR;
sl@0:     }
sl@0: 
sl@0: #ifdef TCL_TIP280
sl@0:     /* TIP #280 : Use the per-word line information of the current command.
sl@0:      */
sl@0:     envPtr->line = envPtr->extCmdMapPtr->loc [envPtr->extCmdMapPtr->nuloc - 1].line [1];
sl@0: #endif
sl@0:     firstWordPtr = parsePtr->tokenPtr
sl@0: 	    + (parsePtr->tokenPtr->numComponents + 1);
sl@0:     return TclCompileExprWords(interp, firstWordPtr, (parsePtr->numWords-1),
sl@0: 	    envPtr);
sl@0: }
sl@0: 
sl@0: /*
sl@0:  *----------------------------------------------------------------------
sl@0:  *
sl@0:  * TclCompileForCmd --
sl@0:  *
sl@0:  *	Procedure called to compile the "for" command.
sl@0:  *
sl@0:  * Results:
sl@0:  *	The return value is a standard Tcl result, which is TCL_OK unless
sl@0:  *	there was an error while parsing string. If an error occurs then
sl@0:  *	the interpreter's result contains a standard error message.
sl@0:  *
sl@0:  * Side effects:
sl@0:  *	Instructions are added to envPtr to execute the "for" command
sl@0:  *	at runtime.
sl@0:  *
sl@0:  *----------------------------------------------------------------------
sl@0:  */
sl@0: int
sl@0: TclCompileForCmd(interp, parsePtr, envPtr)
sl@0:     Tcl_Interp *interp;		/* Used for error reporting. */
sl@0:     Tcl_Parse *parsePtr;	/* Points to a parse structure for the
sl@0: 				 * command created by Tcl_ParseCommand. */
sl@0:     CompileEnv *envPtr;		/* Holds resulting instructions. */
sl@0: {
sl@0:     Tcl_Token *startTokenPtr, *testTokenPtr, *nextTokenPtr, *bodyTokenPtr;
sl@0:     JumpFixup jumpEvalCondFixup;
sl@0:     int testCodeOffset, bodyCodeOffset, nextCodeOffset, jumpDist;
sl@0:     int bodyRange, nextRange, code;
sl@0:     char buffer[32 + TCL_INTEGER_SPACE];
sl@0:     int savedStackDepth = envPtr->currStackDepth;
sl@0: 
sl@0: #ifdef TCL_TIP280
sl@0:     /* TIP #280 : Remember the per-word line information of the current
sl@0:      * command. An index is used instead of a pointer as recursive compilation
sl@0:      * may reallocate, i.e. move, the array. This is also the reason to save
sl@0:      * the nuloc now, it may change during the course of the function.
sl@0:      */
sl@0:     ExtCmdLoc* mapPtr   = envPtr->extCmdMapPtr;
sl@0:     int        eclIndex = mapPtr->nuloc - 1;
sl@0: #endif
sl@0: 
sl@0:     if (parsePtr->numWords != 5) {
sl@0: 	Tcl_ResetResult(interp);
sl@0: 	Tcl_AppendToObj(Tcl_GetObjResult(interp),
sl@0: 	        "wrong # args: should be \"for start test next command\"", -1);
sl@0: 	return TCL_ERROR;
sl@0:     }
sl@0: 
sl@0:     /*
sl@0:      * If the test expression requires substitutions, don't compile the for
sl@0:      * command inline. E.g., the expression might cause the loop to never
sl@0:      * execute or execute forever, as in "for {} "$x > 5" {incr x} {}".
sl@0:      */
sl@0: 
sl@0:     startTokenPtr = parsePtr->tokenPtr
sl@0: 	    + (parsePtr->tokenPtr->numComponents + 1);
sl@0:     testTokenPtr = startTokenPtr + (startTokenPtr->numComponents + 1);
sl@0:     if (testTokenPtr->type != TCL_TOKEN_SIMPLE_WORD) {
sl@0: 	return TCL_OUT_LINE_COMPILE;
sl@0:     }
sl@0: 
sl@0:     /*
sl@0:      * Bail out also if the body or the next expression require substitutions
sl@0:      * in order to insure correct behaviour [Bug 219166]
sl@0:      */
sl@0: 
sl@0:     nextTokenPtr = testTokenPtr + (testTokenPtr->numComponents + 1);
sl@0:     bodyTokenPtr = nextTokenPtr + (nextTokenPtr->numComponents + 1);
sl@0:     if ((nextTokenPtr->type != TCL_TOKEN_SIMPLE_WORD) 
sl@0: 	    || (bodyTokenPtr->type != TCL_TOKEN_SIMPLE_WORD)) {
sl@0: 	return TCL_OUT_LINE_COMPILE;
sl@0:     }
sl@0: 
sl@0:     /*
sl@0:      * Create ExceptionRange records for the body and the "next" command.
sl@0:      * The "next" command's ExceptionRange supports break but not continue
sl@0:      * (and has a -1 continueOffset).
sl@0:      */
sl@0: 
sl@0:     envPtr->exceptDepth++;
sl@0:     envPtr->maxExceptDepth =
sl@0: 	    TclMax(envPtr->exceptDepth, envPtr->maxExceptDepth);
sl@0:     bodyRange = TclCreateExceptRange(LOOP_EXCEPTION_RANGE, envPtr);
sl@0:     nextRange = TclCreateExceptRange(LOOP_EXCEPTION_RANGE, envPtr);
sl@0: 
sl@0:     /*
sl@0:      * Inline compile the initial command.
sl@0:      */
sl@0: 
sl@0: #ifdef TCL_TIP280
sl@0:     envPtr->line = mapPtr->loc [eclIndex].line [1];
sl@0: #endif
sl@0:     code = TclCompileCmdWord(interp, startTokenPtr+1,
sl@0: 	    startTokenPtr->numComponents, envPtr);
sl@0:     if (code != TCL_OK) {
sl@0: 	if (code == TCL_ERROR) {
sl@0:             Tcl_AddObjErrorInfo(interp,
sl@0: 	            "\n    (\"for\" initial command)", -1);
sl@0:         }
sl@0: 	goto done;
sl@0:     }
sl@0:     TclEmitOpcode(INST_POP, envPtr);
sl@0:    
sl@0:     /*
sl@0:      * Jump to the evaluation of the condition. This code uses the "loop
sl@0:      * rotation" optimisation (which eliminates one branch from the loop).
sl@0:      * "for start cond next body" produces then:
sl@0:      *       start
sl@0:      *       goto A
sl@0:      *    B: body                : bodyCodeOffset
sl@0:      *       next                : nextCodeOffset, continueOffset
sl@0:      *    A: cond -> result      : testCodeOffset
sl@0:      *       if (result) goto B
sl@0:      */
sl@0: 
sl@0:     TclEmitForwardJump(envPtr, TCL_UNCONDITIONAL_JUMP, &jumpEvalCondFixup);
sl@0: 
sl@0:     /*
sl@0:      * Compile the loop body.
sl@0:      */
sl@0: 
sl@0:     bodyCodeOffset = (envPtr->codeNext - envPtr->codeStart);
sl@0: 
sl@0: #ifdef TCL_TIP280
sl@0:     envPtr->line = mapPtr->loc [eclIndex].line [4];
sl@0: #endif
sl@0:     code = TclCompileCmdWord(interp, bodyTokenPtr+1,
sl@0: 	    bodyTokenPtr->numComponents, envPtr);
sl@0:     envPtr->currStackDepth = savedStackDepth + 1;
sl@0:     if (code != TCL_OK) {
sl@0: 	if (code == TCL_ERROR) {
sl@0: 	    sprintf(buffer, "\n    (\"for\" body line %d)",
sl@0: 		    interp->errorLine);
sl@0:             Tcl_AddObjErrorInfo(interp, buffer, -1);
sl@0:         }
sl@0: 	goto done;
sl@0:     }
sl@0:     envPtr->exceptArrayPtr[bodyRange].numCodeBytes =
sl@0: 	    (envPtr->codeNext - envPtr->codeStart) - bodyCodeOffset;
sl@0:     TclEmitOpcode(INST_POP, envPtr);
sl@0: 
sl@0: 
sl@0:     /*
sl@0:      * Compile the "next" subcommand.
sl@0:      */
sl@0: 
sl@0:     nextCodeOffset = (envPtr->codeNext - envPtr->codeStart);
sl@0: 
sl@0: #ifdef TCL_TIP280
sl@0:     envPtr->line = mapPtr->loc [eclIndex].line [3];
sl@0: #endif
sl@0:     envPtr->currStackDepth = savedStackDepth;
sl@0:     code = TclCompileCmdWord(interp, nextTokenPtr+1,
sl@0: 	    nextTokenPtr->numComponents, envPtr);
sl@0:     envPtr->currStackDepth = savedStackDepth + 1;
sl@0:     if (code != TCL_OK) {
sl@0: 	if (code == TCL_ERROR) {
sl@0: 	    Tcl_AddObjErrorInfo(interp,
sl@0: 	            "\n    (\"for\" loop-end command)", -1);
sl@0: 	}
sl@0: 	goto done;
sl@0:     }
sl@0:     envPtr->exceptArrayPtr[nextRange].numCodeBytes =
sl@0: 	    (envPtr->codeNext - envPtr->codeStart)
sl@0: 	    - nextCodeOffset;
sl@0:     TclEmitOpcode(INST_POP, envPtr);
sl@0:     envPtr->currStackDepth = savedStackDepth;
sl@0: 
sl@0:     /*
sl@0:      * Compile the test expression then emit the conditional jump that
sl@0:      * terminates the for.
sl@0:      */
sl@0: 
sl@0:     testCodeOffset = (envPtr->codeNext - envPtr->codeStart);
sl@0: 
sl@0:     jumpDist = testCodeOffset - jumpEvalCondFixup.codeOffset;
sl@0:     if (TclFixupForwardJump(envPtr, &jumpEvalCondFixup, jumpDist, 127)) {
sl@0: 	bodyCodeOffset += 3;
sl@0: 	nextCodeOffset += 3;
sl@0: 	testCodeOffset += 3;
sl@0:     }
sl@0: #ifdef TCL_TIP280
sl@0:     envPtr->line = mapPtr->loc [eclIndex].line [2];
sl@0: #endif
sl@0:     envPtr->currStackDepth = savedStackDepth;
sl@0:     code = TclCompileExprWords(interp, testTokenPtr, 1, envPtr);
sl@0:     if (code != TCL_OK) {
sl@0: 	if (code == TCL_ERROR) {
sl@0: 	    Tcl_AddObjErrorInfo(interp,
sl@0: 				"\n    (\"for\" test expression)", -1);
sl@0: 	}
sl@0: 	goto done;
sl@0:     }
sl@0:     envPtr->currStackDepth = savedStackDepth + 1;
sl@0:     
sl@0:     jumpDist = (envPtr->codeNext - envPtr->codeStart) - bodyCodeOffset;
sl@0:     if (jumpDist > 127) {
sl@0: 	TclEmitInstInt4(INST_JUMP_TRUE4, -jumpDist, envPtr);
sl@0:     } else {
sl@0: 	TclEmitInstInt1(INST_JUMP_TRUE1, -jumpDist, envPtr);
sl@0:     }
sl@0:     
sl@0:     /*
sl@0:      * Set the loop's offsets and break target.
sl@0:      */
sl@0: 
sl@0:     envPtr->exceptArrayPtr[bodyRange].codeOffset = bodyCodeOffset;
sl@0:     envPtr->exceptArrayPtr[bodyRange].continueOffset = nextCodeOffset;
sl@0: 
sl@0:     envPtr->exceptArrayPtr[nextRange].codeOffset = nextCodeOffset;
sl@0: 
sl@0:     envPtr->exceptArrayPtr[bodyRange].breakOffset =
sl@0:             envPtr->exceptArrayPtr[nextRange].breakOffset =
sl@0: 	    (envPtr->codeNext - envPtr->codeStart);
sl@0:     
sl@0:     /*
sl@0:      * The for command's result is an empty string.
sl@0:      */
sl@0: 
sl@0:     envPtr->currStackDepth = savedStackDepth;
sl@0:     TclEmitPush(TclRegisterNewLiteral(envPtr, "", 0), envPtr);
sl@0:     code = TCL_OK;
sl@0: 
sl@0:     done:
sl@0:     envPtr->exceptDepth--;
sl@0:     return code;
sl@0: }
sl@0: 
sl@0: /*
sl@0:  *----------------------------------------------------------------------
sl@0:  *
sl@0:  * TclCompileForeachCmd --
sl@0:  *
sl@0:  *	Procedure called to compile the "foreach" command.
sl@0:  *
sl@0:  * Results:
sl@0:  *	The return value is a standard Tcl result, which is TCL_OK if
sl@0:  *	compilation was successful. If an error occurs then the
sl@0:  *	interpreter's result contains a standard error message and TCL_ERROR
sl@0:  *	is returned. If the command is too complex for TclCompileForeachCmd,
sl@0:  *	TCL_OUT_LINE_COMPILE is returned indicating that the foreach command
sl@0:  *	should be compiled "out of line" by emitting code to invoke its
sl@0:  *	command procedure at runtime.
sl@0:  *
sl@0:  * Side effects:
sl@0:  *	Instructions are added to envPtr to execute the "foreach" command
sl@0:  *	at runtime.
sl@0:  *
sl@0: n*----------------------------------------------------------------------
sl@0:  */
sl@0: 
sl@0: int
sl@0: TclCompileForeachCmd(interp, parsePtr, envPtr)
sl@0:     Tcl_Interp *interp;		/* Used for error reporting. */
sl@0:     Tcl_Parse *parsePtr;	/* Points to a parse structure for the
sl@0: 				 * command created by Tcl_ParseCommand. */
sl@0:     CompileEnv *envPtr;		/* Holds resulting instructions. */
sl@0: {
sl@0:     Proc *procPtr = envPtr->procPtr;
sl@0:     ForeachInfo *infoPtr;	/* Points to the structure describing this
sl@0: 				 * foreach command. Stored in a AuxData
sl@0: 				 * record in the ByteCode. */
sl@0:     int firstValueTemp;		/* Index of the first temp var in the frame
sl@0: 				 * used to point to a value list. */
sl@0:     int loopCtTemp;		/* Index of temp var holding the loop's
sl@0: 				 * iteration count. */
sl@0:     Tcl_Token *tokenPtr, *bodyTokenPtr;
sl@0:     unsigned char *jumpPc;
sl@0:     JumpFixup jumpFalseFixup;
sl@0:     int jumpDist, jumpBackDist, jumpBackOffset, infoIndex, range;
sl@0:     int numWords, numLists, numVars, loopIndex, tempVar, i, j, code;
sl@0:     char buffer[32 + TCL_INTEGER_SPACE];
sl@0:     int savedStackDepth = envPtr->currStackDepth;
sl@0: 
sl@0: #ifdef TCL_TIP280
sl@0:     /* TIP #280 : Remember the per-word line information of the current
sl@0:      * command. An index is used instead of a pointer as recursive compilation
sl@0:      * may reallocate, i.e. move, the array. This is also the reason to save
sl@0:      * the nuloc now, it may change during the course of the function.
sl@0:      */
sl@0:     ExtCmdLoc* mapPtr   = envPtr->extCmdMapPtr;
sl@0:     int        eclIndex = mapPtr->nuloc - 1;
sl@0:     int        bodyIndex;
sl@0: #endif
sl@0: 
sl@0:     /*
sl@0:      * We parse the variable list argument words and create two arrays:
sl@0:      *    varcList[i] is number of variables in i-th var list
sl@0:      *    varvList[i] points to array of var names in i-th var list
sl@0:      */
sl@0: 
sl@0: #define STATIC_VAR_LIST_SIZE 5
sl@0:     int varcListStaticSpace[STATIC_VAR_LIST_SIZE];
sl@0:     CONST char **varvListStaticSpace[STATIC_VAR_LIST_SIZE];
sl@0:     int *varcList = varcListStaticSpace;
sl@0:     CONST char ***varvList = varvListStaticSpace;
sl@0: 
sl@0:     /*
sl@0:      * If the foreach command isn't in a procedure, don't compile it inline:
sl@0:      * the payoff is too small.
sl@0:      */
sl@0: 
sl@0:     if (procPtr == NULL) {
sl@0: 	return TCL_OUT_LINE_COMPILE;
sl@0:     }
sl@0: 
sl@0:     numWords = parsePtr->numWords;
sl@0:     if ((numWords < 4) || (numWords%2 != 0)) {
sl@0: 	Tcl_ResetResult(interp);
sl@0: 	Tcl_AppendToObj(Tcl_GetObjResult(interp),
sl@0: 	        "wrong # args: should be \"foreach varList list ?varList list ...? command\"", -1);
sl@0:         return TCL_ERROR;
sl@0:     }
sl@0: 
sl@0:     /*
sl@0:      * Bail out if the body requires substitutions
sl@0:      * in order to insure correct behaviour [Bug 219166]
sl@0:      */
sl@0:     for (i = 0, tokenPtr = parsePtr->tokenPtr;
sl@0: 	    i < numWords-1;
sl@0: 	    i++, tokenPtr += (tokenPtr->numComponents + 1)) {
sl@0:     }
sl@0:     bodyTokenPtr = tokenPtr;
sl@0:     if (bodyTokenPtr->type != TCL_TOKEN_SIMPLE_WORD) {
sl@0: 	return TCL_OUT_LINE_COMPILE;
sl@0:     }
sl@0: #ifdef TCL_TIP280
sl@0:     bodyIndex = i-1;
sl@0: #endif
sl@0: 
sl@0:     /*
sl@0:      * Allocate storage for the varcList and varvList arrays if necessary.
sl@0:      */
sl@0: 
sl@0:     numLists = (numWords - 2)/2;
sl@0:     if (numLists > STATIC_VAR_LIST_SIZE) {
sl@0:         varcList = (int *) ckalloc(numLists * sizeof(int));
sl@0:         varvList = (CONST char ***) ckalloc(numLists * sizeof(CONST char **));
sl@0:     }
sl@0:     for (loopIndex = 0;  loopIndex < numLists;  loopIndex++) {
sl@0:         varcList[loopIndex] = 0;
sl@0:         varvList[loopIndex] = NULL;
sl@0:     }
sl@0:     
sl@0:     /*
sl@0:      * Set the exception stack depth.
sl@0:      */ 
sl@0: 
sl@0:     envPtr->exceptDepth++;
sl@0:     envPtr->maxExceptDepth =
sl@0: 	TclMax(envPtr->exceptDepth, envPtr->maxExceptDepth);
sl@0: 
sl@0:     /*
sl@0:      * Break up each var list and set the varcList and varvList arrays.
sl@0:      * Don't compile the foreach inline if any var name needs substitutions
sl@0:      * or isn't a scalar, or if any var list needs substitutions.
sl@0:      */
sl@0: 
sl@0:     loopIndex = 0;
sl@0:     for (i = 0, tokenPtr = parsePtr->tokenPtr;
sl@0: 	    i < numWords-1;
sl@0: 	    i++, tokenPtr += (tokenPtr->numComponents + 1)) {
sl@0: 	if (i%2 == 1) {
sl@0: 	    if (tokenPtr->type != TCL_TOKEN_SIMPLE_WORD) {
sl@0: 		code = TCL_OUT_LINE_COMPILE;
sl@0: 		goto done;
sl@0: 	    } else {
sl@0: 		/* Lots of copying going on here.  Need a ListObj wizard
sl@0: 		 * to show a better way. */
sl@0: 
sl@0: 		Tcl_DString varList;
sl@0: 
sl@0: 		Tcl_DStringInit(&varList);
sl@0: 		Tcl_DStringAppend(&varList, tokenPtr[1].start,
sl@0: 			tokenPtr[1].size);
sl@0: 		code = Tcl_SplitList(interp, Tcl_DStringValue(&varList),
sl@0: 			&varcList[loopIndex], &varvList[loopIndex]);
sl@0: 		Tcl_DStringFree(&varList);
sl@0: 		if (code != TCL_OK) {
sl@0: 		    goto done;
sl@0: 		}
sl@0: 		numVars = varcList[loopIndex];
sl@0: 
sl@0: 		/*
sl@0: 		 * If the variable list is empty, we can enter an infinite
sl@0: 		 * loop when the interpreted version would not. Take care to
sl@0: 		 * ensure this does not happen. [Bug 1671138]
sl@0: 		 */
sl@0: 
sl@0: 		if (numVars == 0) {
sl@0: 		    code = TCL_OUT_LINE_COMPILE;
sl@0: 		    goto done;
sl@0: 		}
sl@0: 
sl@0: 		for (j = 0;  j < numVars;  j++) {
sl@0: 		    CONST char *varName = varvList[loopIndex][j];
sl@0: 		    if (!TclIsLocalScalar(varName, (int) strlen(varName))) {
sl@0: 			code = TCL_OUT_LINE_COMPILE;
sl@0: 			goto done;
sl@0: 		    }
sl@0: 		}
sl@0: 	    }
sl@0: 	    loopIndex++;
sl@0: 	}
sl@0:     }
sl@0: 
sl@0:     /*
sl@0:      * We will compile the foreach command.
sl@0:      * Reserve (numLists + 1) temporary variables:
sl@0:      *    - numLists temps to hold each value list
sl@0:      *    - 1 temp for the loop counter (index of next element in each list)
sl@0:      * At this time we don't try to reuse temporaries; if there are two
sl@0:      * nonoverlapping foreach loops, they don't share any temps.
sl@0:      */
sl@0: 
sl@0:     firstValueTemp = -1;
sl@0:     for (loopIndex = 0;  loopIndex < numLists;  loopIndex++) {
sl@0: 	tempVar = TclFindCompiledLocal(NULL, /*nameChars*/ 0,
sl@0: 		/*create*/ 1, /*flags*/ VAR_SCALAR, procPtr);
sl@0: 	if (loopIndex == 0) {
sl@0: 	    firstValueTemp = tempVar;
sl@0: 	}
sl@0:     }
sl@0:     loopCtTemp = TclFindCompiledLocal(NULL, /*nameChars*/ 0,
sl@0: 	    /*create*/ 1, /*flags*/ VAR_SCALAR, procPtr);
sl@0:     
sl@0:     /*
sl@0:      * Create and initialize the ForeachInfo and ForeachVarList data
sl@0:      * structures describing this command. Then create a AuxData record
sl@0:      * pointing to the ForeachInfo structure.
sl@0:      */
sl@0: 
sl@0:     infoPtr = (ForeachInfo *) ckalloc((unsigned)
sl@0: 	    (sizeof(ForeachInfo) + (numLists * sizeof(ForeachVarList *))));
sl@0:     infoPtr->numLists = numLists;
sl@0:     infoPtr->firstValueTemp = firstValueTemp;
sl@0:     infoPtr->loopCtTemp = loopCtTemp;
sl@0:     for (loopIndex = 0;  loopIndex < numLists;  loopIndex++) {
sl@0: 	ForeachVarList *varListPtr;
sl@0: 	numVars = varcList[loopIndex];
sl@0: 	varListPtr = (ForeachVarList *) ckalloc((unsigned)
sl@0: 	        sizeof(ForeachVarList) + (numVars * sizeof(int)));
sl@0: 	varListPtr->numVars = numVars;
sl@0: 	for (j = 0;  j < numVars;  j++) {
sl@0: 	    CONST char *varName = varvList[loopIndex][j];
sl@0: 	    int nameChars = strlen(varName);
sl@0: 	    varListPtr->varIndexes[j] = TclFindCompiledLocal(varName,
sl@0: 		    nameChars, /*create*/ 1, /*flags*/ VAR_SCALAR, procPtr);
sl@0: 	}
sl@0: 	infoPtr->varLists[loopIndex] = varListPtr;
sl@0:     }
sl@0:     infoIndex = TclCreateAuxData((ClientData) infoPtr, &tclForeachInfoType, envPtr);
sl@0: 
sl@0:     /*
sl@0:      * Evaluate then store each value list in the associated temporary.
sl@0:      */
sl@0: 
sl@0:     range = TclCreateExceptRange(LOOP_EXCEPTION_RANGE, envPtr);
sl@0:     
sl@0:     loopIndex = 0;
sl@0:     for (i = 0, tokenPtr = parsePtr->tokenPtr;
sl@0: 	    i < numWords-1;
sl@0: 	    i++, tokenPtr += (tokenPtr->numComponents + 1)) {
sl@0: 	if ((i%2 == 0) && (i > 0)) {
sl@0: #ifdef TCL_TIP280
sl@0: 	    envPtr->line = mapPtr->loc [eclIndex].line [i];
sl@0: #endif
sl@0: 	    code = TclCompileTokens(interp, tokenPtr+1,
sl@0: 		    tokenPtr->numComponents, envPtr);
sl@0: 	    if (code != TCL_OK) {
sl@0: 		goto done;
sl@0: 	    }
sl@0: 
sl@0: 	    tempVar = (firstValueTemp + loopIndex);
sl@0: 	    if (tempVar <= 255) {
sl@0: 		TclEmitInstInt1(INST_STORE_SCALAR1, tempVar, envPtr);
sl@0: 	    } else {
sl@0: 		TclEmitInstInt4(INST_STORE_SCALAR4, tempVar, envPtr);
sl@0: 	    }
sl@0: 	    TclEmitOpcode(INST_POP, envPtr);
sl@0: 	    loopIndex++;
sl@0: 	}
sl@0:     }
sl@0: 
sl@0:     /*
sl@0:      * Initialize the temporary var that holds the count of loop iterations.
sl@0:      */
sl@0: 
sl@0:     TclEmitInstInt4(INST_FOREACH_START4, infoIndex, envPtr);
sl@0:     
sl@0:     /*
sl@0:      * Top of loop code: assign each loop variable and check whether
sl@0:      * to terminate the loop.
sl@0:      */
sl@0: 
sl@0:     envPtr->exceptArrayPtr[range].continueOffset =
sl@0: 	    (envPtr->codeNext - envPtr->codeStart);
sl@0:     TclEmitInstInt4(INST_FOREACH_STEP4, infoIndex, envPtr);
sl@0:     TclEmitForwardJump(envPtr, TCL_FALSE_JUMP, &jumpFalseFixup);
sl@0:     
sl@0:     /*
sl@0:      * Inline compile the loop body.
sl@0:      */
sl@0: 
sl@0: #ifdef TCL_TIP280
sl@0:     envPtr->line = mapPtr->loc [eclIndex].line [bodyIndex];
sl@0: #endif
sl@0:     envPtr->exceptArrayPtr[range].codeOffset =
sl@0: 	    (envPtr->codeNext - envPtr->codeStart);
sl@0:     code = TclCompileCmdWord(interp, bodyTokenPtr+1,
sl@0: 	    bodyTokenPtr->numComponents, envPtr);
sl@0:     envPtr->currStackDepth = savedStackDepth + 1;
sl@0:     if (code != TCL_OK) {
sl@0: 	if (code == TCL_ERROR) {
sl@0: 	    sprintf(buffer, "\n    (\"foreach\" body line %d)",
sl@0: 		    interp->errorLine);
sl@0:             Tcl_AddObjErrorInfo(interp, buffer, -1);
sl@0:         }
sl@0: 	goto done;
sl@0:     }
sl@0:     envPtr->exceptArrayPtr[range].numCodeBytes =
sl@0: 	    (envPtr->codeNext - envPtr->codeStart)
sl@0: 	    - envPtr->exceptArrayPtr[range].codeOffset;
sl@0:     TclEmitOpcode(INST_POP, envPtr);
sl@0: 	
sl@0:     /*
sl@0:      * Jump back to the test at the top of the loop. Generate a 4 byte jump
sl@0:      * if the distance to the test is > 120 bytes. This is conservative and
sl@0:      * ensures that we won't have to replace this jump if we later need to
sl@0:      * replace the ifFalse jump with a 4 byte jump.
sl@0:      */
sl@0: 
sl@0:     jumpBackOffset = (envPtr->codeNext - envPtr->codeStart);
sl@0:     jumpBackDist =
sl@0: 	(jumpBackOffset - envPtr->exceptArrayPtr[range].continueOffset);
sl@0:     if (jumpBackDist > 120) {
sl@0: 	TclEmitInstInt4(INST_JUMP4, -jumpBackDist, envPtr);
sl@0:     } else {
sl@0: 	TclEmitInstInt1(INST_JUMP1, -jumpBackDist, envPtr);
sl@0:     }
sl@0: 
sl@0:     /*
sl@0:      * Fix the target of the jump after the foreach_step test.
sl@0:      */
sl@0: 
sl@0:     jumpDist = (envPtr->codeNext - envPtr->codeStart)
sl@0: 	    - jumpFalseFixup.codeOffset;
sl@0:     if (TclFixupForwardJump(envPtr, &jumpFalseFixup, jumpDist, 127)) {
sl@0: 	/*
sl@0: 	 * Update the loop body's starting PC offset since it moved down.
sl@0: 	 */
sl@0: 
sl@0: 	envPtr->exceptArrayPtr[range].codeOffset += 3;
sl@0: 
sl@0: 	/*
sl@0: 	 * Update the jump back to the test at the top of the loop since it
sl@0: 	 * also moved down 3 bytes.
sl@0: 	 */
sl@0: 
sl@0: 	jumpBackOffset += 3;
sl@0: 	jumpPc = (envPtr->codeStart + jumpBackOffset);
sl@0: 	jumpBackDist += 3;
sl@0: 	if (jumpBackDist > 120) {
sl@0: 	    TclUpdateInstInt4AtPc(INST_JUMP4, -jumpBackDist, jumpPc);
sl@0: 	} else {
sl@0: 	    TclUpdateInstInt1AtPc(INST_JUMP1, -jumpBackDist, jumpPc);
sl@0: 	}
sl@0:     }
sl@0: 
sl@0:     /*
sl@0:      * Set the loop's break target.
sl@0:      */
sl@0: 
sl@0:     envPtr->exceptArrayPtr[range].breakOffset =
sl@0: 	    (envPtr->codeNext - envPtr->codeStart);
sl@0:     
sl@0:     /*
sl@0:      * The foreach command's result is an empty string.
sl@0:      */
sl@0: 
sl@0:     envPtr->currStackDepth = savedStackDepth;
sl@0:     TclEmitPush(TclRegisterNewLiteral(envPtr, "", 0), envPtr);
sl@0:     envPtr->currStackDepth = savedStackDepth + 1;
sl@0: 
sl@0:     done:
sl@0:     for (loopIndex = 0;  loopIndex < numLists;  loopIndex++) {
sl@0: 	if (varvList[loopIndex] != (CONST char **) NULL) {
sl@0: 	    ckfree((char *) varvList[loopIndex]);
sl@0: 	}
sl@0:     }
sl@0:     if (varcList != varcListStaticSpace) {
sl@0: 	ckfree((char *) varcList);
sl@0:         ckfree((char *) varvList);
sl@0:     }
sl@0:     envPtr->exceptDepth--;
sl@0:     return code;
sl@0: }
sl@0: 
sl@0: /*
sl@0:  *----------------------------------------------------------------------
sl@0:  *
sl@0:  * DupForeachInfo --
sl@0:  *
sl@0:  *	This procedure duplicates a ForeachInfo structure created as
sl@0:  *	auxiliary data during the compilation of a foreach command.
sl@0:  *
sl@0:  * Results:
sl@0:  *	A pointer to a newly allocated copy of the existing ForeachInfo
sl@0:  *	structure is returned.
sl@0:  *
sl@0:  * Side effects:
sl@0:  *	Storage for the copied ForeachInfo record is allocated. If the
sl@0:  *	original ForeachInfo structure pointed to any ForeachVarList
sl@0:  *	records, these structures are also copied and pointers to them
sl@0:  *	are stored in the new ForeachInfo record.
sl@0:  *
sl@0:  *----------------------------------------------------------------------
sl@0:  */
sl@0: 
sl@0: static ClientData
sl@0: DupForeachInfo(clientData)
sl@0:     ClientData clientData;	/* The foreach command's compilation
sl@0: 				 * auxiliary data to duplicate. */
sl@0: {
sl@0:     register ForeachInfo *srcPtr = (ForeachInfo *) clientData;
sl@0:     ForeachInfo *dupPtr;
sl@0:     register ForeachVarList *srcListPtr, *dupListPtr;
sl@0:     int numLists = srcPtr->numLists;
sl@0:     int numVars, i, j;
sl@0:     
sl@0:     dupPtr = (ForeachInfo *) ckalloc((unsigned)
sl@0: 	    (sizeof(ForeachInfo) + (numLists * sizeof(ForeachVarList *))));
sl@0:     dupPtr->numLists = numLists;
sl@0:     dupPtr->firstValueTemp = srcPtr->firstValueTemp;
sl@0:     dupPtr->loopCtTemp = srcPtr->loopCtTemp;
sl@0:     
sl@0:     for (i = 0;  i < numLists;  i++) {
sl@0: 	srcListPtr = srcPtr->varLists[i];
sl@0: 	numVars = srcListPtr->numVars;
sl@0: 	dupListPtr = (ForeachVarList *) ckalloc((unsigned)
sl@0: 	        sizeof(ForeachVarList) + numVars*sizeof(int));
sl@0: 	dupListPtr->numVars = numVars;
sl@0: 	for (j = 0;  j < numVars;  j++) {
sl@0: 	    dupListPtr->varIndexes[j] =	srcListPtr->varIndexes[j];
sl@0: 	}
sl@0: 	dupPtr->varLists[i] = dupListPtr;
sl@0:     }
sl@0:     return (ClientData) dupPtr;
sl@0: }
sl@0: 
sl@0: /*
sl@0:  *----------------------------------------------------------------------
sl@0:  *
sl@0:  * FreeForeachInfo --
sl@0:  *
sl@0:  *	Procedure to free a ForeachInfo structure created as auxiliary data
sl@0:  *	during the compilation of a foreach command.
sl@0:  *
sl@0:  * Results:
sl@0:  *	None.
sl@0:  *
sl@0:  * Side effects:
sl@0:  *	Storage for the ForeachInfo structure pointed to by the ClientData
sl@0:  *	argument is freed as is any ForeachVarList record pointed to by the
sl@0:  *	ForeachInfo structure.
sl@0:  *
sl@0:  *----------------------------------------------------------------------
sl@0:  */
sl@0: 
sl@0: static void
sl@0: FreeForeachInfo(clientData)
sl@0:     ClientData clientData;	/* The foreach command's compilation
sl@0: 				 * auxiliary data to free. */
sl@0: {
sl@0:     register ForeachInfo *infoPtr = (ForeachInfo *) clientData;
sl@0:     register ForeachVarList *listPtr;
sl@0:     int numLists = infoPtr->numLists;
sl@0:     register int i;
sl@0: 
sl@0:     for (i = 0;  i < numLists;  i++) {
sl@0: 	listPtr = infoPtr->varLists[i];
sl@0: 	ckfree((char *) listPtr);
sl@0:     }
sl@0:     ckfree((char *) infoPtr);
sl@0: }
sl@0: 
sl@0: /*
sl@0:  *----------------------------------------------------------------------
sl@0:  *
sl@0:  * TclCompileIfCmd --
sl@0:  *
sl@0:  *	Procedure called to compile the "if" command.
sl@0:  *
sl@0:  * Results:
sl@0:  *	The return value is a standard Tcl result, which is TCL_OK if
sl@0:  *	compilation was successful. If an error occurs then the
sl@0:  *	interpreter's result contains a standard error message and TCL_ERROR
sl@0:  *	is returned. If the command is too complex for TclCompileIfCmd,
sl@0:  *	TCL_OUT_LINE_COMPILE is returned indicating that the if command
sl@0:  *	should be compiled "out of line" by emitting code to invoke its
sl@0:  *	command procedure at runtime.
sl@0:  *
sl@0:  * Side effects:
sl@0:  *	Instructions are added to envPtr to execute the "if" command
sl@0:  *	at runtime.
sl@0:  *
sl@0:  *----------------------------------------------------------------------
sl@0:  */
sl@0: int
sl@0: TclCompileIfCmd(interp, parsePtr, envPtr)
sl@0:     Tcl_Interp *interp;		/* Used for error reporting. */
sl@0:     Tcl_Parse *parsePtr;	/* Points to a parse structure for the
sl@0: 				 * command created by Tcl_ParseCommand. */
sl@0:     CompileEnv *envPtr;		/* Holds resulting instructions. */
sl@0: {
sl@0:     JumpFixupArray jumpFalseFixupArray;
sl@0:     				/* Used to fix the ifFalse jump after each
sl@0: 				 * test when its target PC is determined. */
sl@0:     JumpFixupArray jumpEndFixupArray;
sl@0: 				/* Used to fix the jump after each "then"
sl@0: 				 * body to the end of the "if" when that PC
sl@0: 				 * is determined. */
sl@0:     Tcl_Token *tokenPtr, *testTokenPtr;
sl@0:     int jumpDist, jumpFalseDist;
sl@0:     int jumpIndex = 0;          /* avoid compiler warning. */
sl@0:     int numWords, wordIdx, numBytes, j, code;
sl@0:     CONST char *word;
sl@0:     char buffer[100];
sl@0:     int savedStackDepth = envPtr->currStackDepth;
sl@0:                                 /* Saved stack depth at the start of the first
sl@0: 				 * test; the envPtr current depth is restored
sl@0: 				 * to this value at the start of each test. */
sl@0:     int realCond = 1;           /* set to 0 for static conditions: "if 0 {..}" */
sl@0:     int boolVal;                /* value of static condition */
sl@0:     int compileScripts = 1;            
sl@0: 
sl@0: #ifdef TCL_TIP280
sl@0:     /* TIP #280 : Remember the per-word line information of the current
sl@0:      * command. An index is used instead of a pointer as recursive compilation
sl@0:      * may reallocate, i.e. move, the array. This is also the reason to save
sl@0:      * the nuloc now, it may change during the course of the function.
sl@0:      */
sl@0:     ExtCmdLoc* mapPtr   = envPtr->extCmdMapPtr;
sl@0:     int        eclIndex = mapPtr->nuloc - 1;
sl@0: #endif
sl@0: 
sl@0:     /*
sl@0:      * Only compile the "if" command if all arguments are simple
sl@0:      * words, in order to insure correct substitution [Bug 219166]
sl@0:      */
sl@0: 
sl@0:     tokenPtr = parsePtr->tokenPtr;
sl@0:     wordIdx = 0;
sl@0:     numWords = parsePtr->numWords;
sl@0: 
sl@0:     for (wordIdx = 0; wordIdx < numWords; wordIdx++) {
sl@0: 	if (tokenPtr->type != TCL_TOKEN_SIMPLE_WORD) {
sl@0: 	    return TCL_OUT_LINE_COMPILE;
sl@0: 	}
sl@0: 	tokenPtr += 2;
sl@0:     }
sl@0: 
sl@0: 
sl@0:     TclInitJumpFixupArray(&jumpFalseFixupArray);
sl@0:     TclInitJumpFixupArray(&jumpEndFixupArray);
sl@0:     code = TCL_OK;
sl@0: 
sl@0:     /*
sl@0:      * Each iteration of this loop compiles one "if expr ?then? body"
sl@0:      * or "elseif expr ?then? body" clause. 
sl@0:      */
sl@0: 
sl@0:     tokenPtr = parsePtr->tokenPtr;
sl@0:     wordIdx = 0;
sl@0:     while (wordIdx < numWords) {
sl@0: 	/*
sl@0: 	 * Stop looping if the token isn't "if" or "elseif".
sl@0: 	 */
sl@0: 
sl@0: 	word = tokenPtr[1].start;
sl@0: 	numBytes = tokenPtr[1].size;
sl@0: 	if ((tokenPtr == parsePtr->tokenPtr)
sl@0: 	        || ((numBytes == 6) && (strncmp(word, "elseif", 6) == 0))) {
sl@0: 	    tokenPtr += (tokenPtr->numComponents + 1);
sl@0: 	    wordIdx++;
sl@0: 	} else {
sl@0: 	    break;
sl@0: 	}
sl@0: 	if (wordIdx >= numWords) {
sl@0: 	    sprintf(buffer,
sl@0: 	            "wrong # args: no expression after \"%.*s\" argument",
sl@0: 		    (numBytes > 50 ? 50 : numBytes), word);
sl@0: 	    Tcl_ResetResult(interp);
sl@0: 	    Tcl_AppendToObj(Tcl_GetObjResult(interp), buffer, -1);
sl@0: 	    code = TCL_ERROR;
sl@0: 	    goto done;
sl@0: 	}
sl@0: 
sl@0: 	/*
sl@0: 	 * Compile the test expression then emit the conditional jump
sl@0: 	 * around the "then" part. 
sl@0: 	 */
sl@0: 	
sl@0: 	envPtr->currStackDepth = savedStackDepth;
sl@0: 	testTokenPtr = tokenPtr;
sl@0: 
sl@0: 
sl@0: 	if (realCond) {
sl@0: 	    /*
sl@0: 	     * Find out if the condition is a constant. 
sl@0: 	     */
sl@0: 	
sl@0: 	    Tcl_Obj *boolObj = Tcl_NewStringObj(testTokenPtr[1].start,
sl@0: 		    testTokenPtr[1].size);
sl@0: 	    Tcl_IncrRefCount(boolObj);
sl@0: 	    code = Tcl_GetBooleanFromObj(NULL, boolObj, &boolVal);
sl@0: 	    Tcl_DecrRefCount(boolObj);
sl@0: 	    if (code == TCL_OK) {
sl@0: 		/*
sl@0: 		 * A static condition
sl@0: 		 */
sl@0: 		realCond = 0;
sl@0: 		if (!boolVal) {
sl@0: 		    compileScripts = 0;
sl@0: 		}
sl@0: 	    } else {
sl@0: 		Tcl_ResetResult(interp);
sl@0: #ifdef TCL_TIP280
sl@0: 		envPtr->line = mapPtr->loc [eclIndex].line [wordIdx];
sl@0: #endif
sl@0: 		code = TclCompileExprWords(interp, testTokenPtr, 1, envPtr);
sl@0: 		if (code != TCL_OK) {
sl@0: 		    if (code == TCL_ERROR) {
sl@0: 			Tcl_AddObjErrorInfo(interp,
sl@0: 			        "\n    (\"if\" test expression)", -1);
sl@0: 		    }
sl@0: 		    goto done;
sl@0: 		}
sl@0: 		if (jumpFalseFixupArray.next >= jumpFalseFixupArray.end) {
sl@0: 		    TclExpandJumpFixupArray(&jumpFalseFixupArray);
sl@0: 		}
sl@0: 		jumpIndex = jumpFalseFixupArray.next;
sl@0: 		jumpFalseFixupArray.next++;
sl@0: 		TclEmitForwardJump(envPtr, TCL_FALSE_JUMP,
sl@0: 			       &(jumpFalseFixupArray.fixup[jumpIndex]));	    
sl@0: 	    }
sl@0: 	}
sl@0: 
sl@0: 
sl@0: 	/*
sl@0: 	 * Skip over the optional "then" before the then clause.
sl@0: 	 */
sl@0: 
sl@0: 	tokenPtr = testTokenPtr + (testTokenPtr->numComponents + 1);
sl@0: 	wordIdx++;
sl@0: 	if (wordIdx >= numWords) {
sl@0: 	    sprintf(buffer,
sl@0: 		    "wrong # args: no script following \"%.*s\" argument",
sl@0: 		    (testTokenPtr->size > 50 ? 50 : testTokenPtr->size),
sl@0: 		    testTokenPtr->start);
sl@0: 	    Tcl_ResetResult(interp);
sl@0: 	    Tcl_AppendToObj(Tcl_GetObjResult(interp), buffer, -1);
sl@0: 	    code = TCL_ERROR;
sl@0: 	    goto done;
sl@0: 	}
sl@0: 	if (tokenPtr->type == TCL_TOKEN_SIMPLE_WORD) {
sl@0: 	    word = tokenPtr[1].start;
sl@0: 	    numBytes = tokenPtr[1].size;
sl@0: 	    if ((numBytes == 4) && (strncmp(word, "then", 4) == 0)) {
sl@0: 		tokenPtr += (tokenPtr->numComponents + 1);
sl@0: 		wordIdx++;
sl@0: 		if (wordIdx >= numWords) {
sl@0: 		    Tcl_ResetResult(interp);
sl@0: 		    Tcl_AppendToObj(Tcl_GetObjResult(interp),
sl@0: 		            "wrong # args: no script following \"then\" argument", -1);
sl@0: 		    code = TCL_ERROR;
sl@0: 		    goto done;
sl@0: 		}
sl@0: 	    }
sl@0: 	}
sl@0: 
sl@0: 	/*
sl@0: 	 * Compile the "then" command body.
sl@0: 	 */
sl@0: 
sl@0: 	if (compileScripts) {
sl@0: #ifdef TCL_TIP280
sl@0: 	    envPtr->line = mapPtr->loc [eclIndex].line [wordIdx];
sl@0: #endif
sl@0: 	    envPtr->currStackDepth = savedStackDepth;
sl@0: 	    code = TclCompileCmdWord(interp, tokenPtr+1,
sl@0: 	            tokenPtr->numComponents, envPtr);
sl@0: 	    if (code != TCL_OK) {
sl@0: 		if (code == TCL_ERROR) {
sl@0: 		    sprintf(buffer, "\n    (\"if\" then script line %d)",
sl@0: 		            interp->errorLine);
sl@0: 		    Tcl_AddObjErrorInfo(interp, buffer, -1);
sl@0: 		}
sl@0: 		goto done;
sl@0: 	    }	
sl@0: 	}
sl@0: 
sl@0: 	if (realCond) {
sl@0: 	    /*
sl@0: 	     * Jump to the end of the "if" command. Both jumpFalseFixupArray and
sl@0: 	     * jumpEndFixupArray are indexed by "jumpIndex".
sl@0: 	     */
sl@0: 	    
sl@0: 	    if (jumpEndFixupArray.next >= jumpEndFixupArray.end) {
sl@0: 		TclExpandJumpFixupArray(&jumpEndFixupArray);
sl@0: 	    }
sl@0: 	    jumpEndFixupArray.next++;
sl@0: 	    TclEmitForwardJump(envPtr, TCL_UNCONDITIONAL_JUMP,
sl@0: 	            &(jumpEndFixupArray.fixup[jumpIndex]));
sl@0: 	    
sl@0: 	    /*
sl@0: 	     * Fix the target of the jumpFalse after the test. Generate a 4 byte
sl@0: 	     * jump if the distance is > 120 bytes. This is conservative, and
sl@0: 	     * ensures that we won't have to replace this jump if we later also
sl@0: 	     * need to replace the proceeding jump to the end of the "if" with a
sl@0: 	     * 4 byte jump.
sl@0: 	     */
sl@0: 
sl@0: 	    jumpDist = (envPtr->codeNext - envPtr->codeStart)
sl@0: 	            - jumpFalseFixupArray.fixup[jumpIndex].codeOffset;
sl@0: 	    if (TclFixupForwardJump(envPtr,
sl@0: 	            &(jumpFalseFixupArray.fixup[jumpIndex]), jumpDist, 120)) {
sl@0: 		/*
sl@0: 		 * Adjust the code offset for the proceeding jump to the end
sl@0: 		 * of the "if" command.
sl@0: 		 */
sl@0: 		
sl@0: 		jumpEndFixupArray.fixup[jumpIndex].codeOffset += 3;
sl@0: 	    }
sl@0: 	} else if (boolVal) {
sl@0: 	    /* 
sl@0: 	     *We were processing an "if 1 {...}"; stop compiling
sl@0: 	     * scripts
sl@0: 	     */
sl@0: 
sl@0: 	    compileScripts = 0;
sl@0: 	} else {
sl@0: 	    /* 
sl@0: 	     *We were processing an "if 0 {...}"; reset so that
sl@0: 	     * the rest (elseif, else) is compiled correctly
sl@0: 	     */
sl@0: 
sl@0: 	    realCond = 1;
sl@0: 	    compileScripts = 1;
sl@0: 	} 
sl@0: 
sl@0: 	tokenPtr += (tokenPtr->numComponents + 1);
sl@0: 	wordIdx++;
sl@0:     }
sl@0: 
sl@0:     /*
sl@0:      * Restore the current stack depth in the environment; the 
sl@0:      * "else" clause (or its default) will add 1 to this.
sl@0:      */
sl@0: 
sl@0:     envPtr->currStackDepth = savedStackDepth;
sl@0: 
sl@0:     /*
sl@0:      * Check for the optional else clause. Do not compile
sl@0:      * anything if this was an "if 1 {...}" case.
sl@0:      */
sl@0: 
sl@0:     if ((wordIdx < numWords)
sl@0: 	    && (tokenPtr->type == TCL_TOKEN_SIMPLE_WORD)) {
sl@0: 	/*
sl@0: 	 * There is an else clause. Skip over the optional "else" word.
sl@0: 	 */
sl@0: 
sl@0: 	word = tokenPtr[1].start;
sl@0: 	numBytes = tokenPtr[1].size;
sl@0: 	if ((numBytes == 4) && (strncmp(word, "else", 4) == 0)) {
sl@0: 	    tokenPtr += (tokenPtr->numComponents + 1);
sl@0: 	    wordIdx++;
sl@0: 	    if (wordIdx >= numWords) {
sl@0: 		Tcl_ResetResult(interp);
sl@0: 		Tcl_AppendToObj(Tcl_GetObjResult(interp),
sl@0: 		        "wrong # args: no script following \"else\" argument", -1);
sl@0: 		code = TCL_ERROR;
sl@0: 		goto done;
sl@0: 	    }
sl@0: 	}
sl@0: 
sl@0: 	if (compileScripts) {
sl@0: 	    /*
sl@0: 	     * Compile the else command body.
sl@0: 	     */
sl@0: #ifdef TCL_TIP280
sl@0: 	    envPtr->line = mapPtr->loc [eclIndex].line [wordIdx];
sl@0: #endif
sl@0: 	    code = TclCompileCmdWord(interp, tokenPtr+1,
sl@0: 		    tokenPtr->numComponents, envPtr);
sl@0: 	    if (code != TCL_OK) {
sl@0: 		if (code == TCL_ERROR) {
sl@0: 		    sprintf(buffer, "\n    (\"if\" else script line %d)",
sl@0: 			    interp->errorLine);
sl@0: 		    Tcl_AddObjErrorInfo(interp, buffer, -1);
sl@0: 		}
sl@0: 		goto done;
sl@0: 	    }
sl@0: 	}
sl@0: 
sl@0: 	/*
sl@0: 	 * Make sure there are no words after the else clause.
sl@0: 	 */
sl@0: 	
sl@0: 	wordIdx++;
sl@0: 	if (wordIdx < numWords) {
sl@0: 	    Tcl_ResetResult(interp);
sl@0: 	    Tcl_AppendToObj(Tcl_GetObjResult(interp),
sl@0: 		    "wrong # args: extra words after \"else\" clause in \"if\" command", -1);
sl@0: 	    code = TCL_ERROR;
sl@0: 	    goto done;
sl@0: 	}
sl@0:     } else {
sl@0: 	/*
sl@0: 	 * No else clause: the "if" command's result is an empty string.
sl@0: 	 */
sl@0: 
sl@0: 	if (compileScripts) {
sl@0: 	    TclEmitPush(TclRegisterNewLiteral(envPtr, "", 0), envPtr);
sl@0: 	}
sl@0:     }
sl@0: 
sl@0:     /*
sl@0:      * Fix the unconditional jumps to the end of the "if" command.
sl@0:      */
sl@0:     
sl@0:     for (j = jumpEndFixupArray.next;  j > 0;  j--) {
sl@0: 	jumpIndex = (j - 1);	/* i.e. process the closest jump first */
sl@0: 	jumpDist = (envPtr->codeNext - envPtr->codeStart)
sl@0: 	        - jumpEndFixupArray.fixup[jumpIndex].codeOffset;
sl@0: 	if (TclFixupForwardJump(envPtr,
sl@0: 	        &(jumpEndFixupArray.fixup[jumpIndex]), jumpDist, 127)) {
sl@0: 	    /*
sl@0: 	     * Adjust the immediately preceeding "ifFalse" jump. We moved
sl@0: 	     * it's target (just after this jump) down three bytes.
sl@0: 	     */
sl@0: 
sl@0: 	    unsigned char *ifFalsePc = envPtr->codeStart
sl@0: 	            + jumpFalseFixupArray.fixup[jumpIndex].codeOffset;
sl@0: 	    unsigned char opCode = *ifFalsePc;
sl@0: 	    if (opCode == INST_JUMP_FALSE1) {
sl@0: 		jumpFalseDist = TclGetInt1AtPtr(ifFalsePc + 1);
sl@0: 		jumpFalseDist += 3;
sl@0: 		TclStoreInt1AtPtr(jumpFalseDist, (ifFalsePc + 1));
sl@0: 	    } else if (opCode == INST_JUMP_FALSE4) {
sl@0: 		jumpFalseDist = TclGetInt4AtPtr(ifFalsePc + 1);
sl@0: 		jumpFalseDist += 3;
sl@0: 		TclStoreInt4AtPtr(jumpFalseDist, (ifFalsePc + 1));
sl@0: 	    } else {
sl@0: 		panic("TclCompileIfCmd: unexpected opcode updating ifFalse jump");
sl@0: 	    }
sl@0: 	}
sl@0:     }
sl@0: 
sl@0:     /*
sl@0:      * Free the jumpFixupArray array if malloc'ed storage was used.
sl@0:      */
sl@0: 
sl@0:     done:
sl@0:     envPtr->currStackDepth = savedStackDepth + 1;
sl@0:     TclFreeJumpFixupArray(&jumpFalseFixupArray);
sl@0:     TclFreeJumpFixupArray(&jumpEndFixupArray);
sl@0:     return code;
sl@0: }
sl@0: 
sl@0: /*
sl@0:  *----------------------------------------------------------------------
sl@0:  *
sl@0:  * TclCompileIncrCmd --
sl@0:  *
sl@0:  *	Procedure called to compile the "incr" command.
sl@0:  *
sl@0:  * Results:
sl@0:  *	The return value is a standard Tcl result, which is TCL_OK if
sl@0:  *	compilation was successful. If an error occurs then the
sl@0:  *	interpreter's result contains a standard error message and TCL_ERROR
sl@0:  *	is returned. If the command is too complex for TclCompileIncrCmd,
sl@0:  *	TCL_OUT_LINE_COMPILE is returned indicating that the incr command
sl@0:  *	should be compiled "out of line" by emitting code to invoke its
sl@0:  *	command procedure at runtime.
sl@0:  *
sl@0:  * Side effects:
sl@0:  *	Instructions are added to envPtr to execute the "incr" command
sl@0:  *	at runtime.
sl@0:  *
sl@0:  *----------------------------------------------------------------------
sl@0:  */
sl@0: 
sl@0: int
sl@0: TclCompileIncrCmd(interp, parsePtr, envPtr)
sl@0:     Tcl_Interp *interp;		/* Used for error reporting. */
sl@0:     Tcl_Parse *parsePtr;	/* Points to a parse structure for the
sl@0: 				 * command created by Tcl_ParseCommand. */
sl@0:     CompileEnv *envPtr;		/* Holds resulting instructions. */
sl@0: {
sl@0:     Tcl_Token *varTokenPtr, *incrTokenPtr;
sl@0:     int simpleVarName, isScalar, localIndex, haveImmValue, immValue;
sl@0:     int code = TCL_OK;
sl@0: 
sl@0: #ifdef TCL_TIP280
sl@0:     /* TIP #280 : Remember the per-word line information of the current
sl@0:      * command. An index is used instead of a pointer as recursive compilation
sl@0:      * may reallocate, i.e. move, the array. This is also the reason to save
sl@0:      * the nuloc now, it may change during the course of the function.
sl@0:      */
sl@0:     ExtCmdLoc* mapPtr   = envPtr->extCmdMapPtr;
sl@0:     int        eclIndex = mapPtr->nuloc - 1;
sl@0: #endif
sl@0: 
sl@0:     if ((parsePtr->numWords != 2) && (parsePtr->numWords != 3)) {
sl@0: 	Tcl_ResetResult(interp);
sl@0: 	Tcl_AppendToObj(Tcl_GetObjResult(interp),
sl@0: 	        "wrong # args: should be \"incr varName ?increment?\"", -1);
sl@0: 	return TCL_ERROR;
sl@0:     }
sl@0: 
sl@0:     varTokenPtr = parsePtr->tokenPtr
sl@0: 	    + (parsePtr->tokenPtr->numComponents + 1);
sl@0: 
sl@0:     code = TclPushVarName(interp, varTokenPtr, envPtr, 
sl@0: 	    (TCL_NO_LARGE_INDEX | TCL_CREATE_VAR),
sl@0: #ifndef TCL_TIP280
sl@0: 	    &localIndex, &simpleVarName, &isScalar);
sl@0: #else
sl@0: 	    &localIndex, &simpleVarName, &isScalar,
sl@0: 	    mapPtr->loc [eclIndex].line [1]);
sl@0: #endif
sl@0:     if (code != TCL_OK) {
sl@0: 	goto done;
sl@0:     }
sl@0: 
sl@0:     /*
sl@0:      * If an increment is given, push it, but see first if it's a small
sl@0:      * integer.
sl@0:      */
sl@0: 
sl@0:     haveImmValue = 0;
sl@0:     immValue = 1;
sl@0:     if (parsePtr->numWords == 3) {
sl@0: 	incrTokenPtr = varTokenPtr + (varTokenPtr->numComponents + 1);
sl@0: 	if (incrTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) {
sl@0: 	    CONST char *word = incrTokenPtr[1].start;
sl@0: 	    int numBytes = incrTokenPtr[1].size;
sl@0: 
sl@0: 	    /*
sl@0: 	     * Note there is a danger that modifying the string could have
sl@0: 	     * undesirable side effects.  In this case, TclLooksLikeInt has
sl@0: 	     * no dependencies on shared strings so we should be safe.
sl@0: 	     */
sl@0: 
sl@0: 	    if (TclLooksLikeInt(word, numBytes)) {
sl@0: 		int code;
sl@0: 		Tcl_Obj *intObj = Tcl_NewStringObj(word, numBytes);
sl@0: 		Tcl_IncrRefCount(intObj);
sl@0: 		code = Tcl_GetIntFromObj(NULL, intObj, &immValue);
sl@0: 		Tcl_DecrRefCount(intObj);
sl@0: 		if ((code == TCL_OK)
sl@0: 			&& (-127 <= immValue) && (immValue <= 127)) {
sl@0: 		    haveImmValue = 1;
sl@0: 		}
sl@0: 	    }
sl@0: 	    if (!haveImmValue) {
sl@0: 		TclEmitPush(
sl@0: 			TclRegisterNewLiteral(envPtr, word, numBytes), envPtr);
sl@0: 	    }
sl@0: 	} else {
sl@0: #ifdef TCL_TIP280
sl@0: 	    envPtr->line = mapPtr->loc [eclIndex].line [2];
sl@0: #endif
sl@0: 	    code = TclCompileTokens(interp, incrTokenPtr+1, 
sl@0: 	            incrTokenPtr->numComponents, envPtr);
sl@0: 	    if (code != TCL_OK) {
sl@0: 		goto done;
sl@0: 	    }
sl@0: 	}
sl@0:     } else {			/* no incr amount given so use 1 */
sl@0: 	haveImmValue = 1;
sl@0:     }
sl@0:     
sl@0:     /*
sl@0:      * Emit the instruction to increment the variable.
sl@0:      */
sl@0: 
sl@0:     if (simpleVarName) {
sl@0: 	if (isScalar) {
sl@0: 	    if (localIndex >= 0) {
sl@0: 		if (haveImmValue) {
sl@0: 		    TclEmitInstInt1(INST_INCR_SCALAR1_IMM, localIndex, envPtr);
sl@0: 		    TclEmitInt1(immValue, envPtr);
sl@0: 		} else {
sl@0: 		    TclEmitInstInt1(INST_INCR_SCALAR1, localIndex, envPtr);
sl@0: 		}
sl@0: 	    } else {
sl@0: 		if (haveImmValue) {
sl@0: 		    TclEmitInstInt1(INST_INCR_SCALAR_STK_IMM, immValue, envPtr);
sl@0: 		} else {
sl@0: 		    TclEmitOpcode(INST_INCR_SCALAR_STK, envPtr);
sl@0: 		}
sl@0: 	    }
sl@0: 	} else {
sl@0: 	    if (localIndex >= 0) {
sl@0: 		if (haveImmValue) {
sl@0: 		    TclEmitInstInt1(INST_INCR_ARRAY1_IMM, localIndex, envPtr);
sl@0: 		    TclEmitInt1(immValue, envPtr);
sl@0: 		} else {
sl@0: 		    TclEmitInstInt1(INST_INCR_ARRAY1, localIndex, envPtr);
sl@0: 		}
sl@0: 	    } else {
sl@0: 		if (haveImmValue) {
sl@0: 		    TclEmitInstInt1(INST_INCR_ARRAY_STK_IMM, immValue, envPtr);
sl@0: 		} else {
sl@0: 		    TclEmitOpcode(INST_INCR_ARRAY_STK, envPtr);
sl@0: 		}
sl@0: 	    }
sl@0: 	}
sl@0:     } else {			/* non-simple variable name */
sl@0: 	if (haveImmValue) {
sl@0: 	    TclEmitInstInt1(INST_INCR_STK_IMM, immValue, envPtr);
sl@0: 	} else {
sl@0: 	    TclEmitOpcode(INST_INCR_STK, envPtr);
sl@0: 	}
sl@0:     }
sl@0: 	
sl@0:     done:
sl@0:     return code;
sl@0: }
sl@0: 
sl@0: /*
sl@0:  *----------------------------------------------------------------------
sl@0:  *
sl@0:  * TclCompileLappendCmd --
sl@0:  *
sl@0:  *	Procedure called to compile the "lappend" command.
sl@0:  *
sl@0:  * Results:
sl@0:  *	The return value is a standard Tcl result, which is normally TCL_OK
sl@0:  *	unless there was an error while parsing string. If an error occurs
sl@0:  *	then the interpreter's result contains a standard error message. If
sl@0:  *	complation fails because the command requires a second level of
sl@0:  *	substitutions, TCL_OUT_LINE_COMPILE is returned indicating that the
sl@0:  *	command should be compiled "out of line" by emitting code to
sl@0:  *	invoke its command procedure (Tcl_LappendObjCmd) at runtime.
sl@0:  *
sl@0:  * Side effects:
sl@0:  *	Instructions are added to envPtr to execute the "lappend" command
sl@0:  *	at runtime.
sl@0:  *
sl@0:  *----------------------------------------------------------------------
sl@0:  */
sl@0: 
sl@0: int
sl@0: TclCompileLappendCmd(interp, parsePtr, envPtr)
sl@0:     Tcl_Interp *interp;		/* Used for error reporting. */
sl@0:     Tcl_Parse *parsePtr;	/* Points to a parse structure for the
sl@0: 				 * command created by Tcl_ParseCommand. */
sl@0:     CompileEnv *envPtr;		/* Holds resulting instructions. */
sl@0: {
sl@0:     Tcl_Token *varTokenPtr, *valueTokenPtr;
sl@0:     int simpleVarName, isScalar, localIndex, numWords;
sl@0:     int code = TCL_OK;
sl@0: 
sl@0: #ifdef TCL_TIP280
sl@0:     /* TIP #280 : Remember the per-word line information of the current
sl@0:      * command. An index is used instead of a pointer as recursive compilation
sl@0:      * may reallocate, i.e. move, the array. This is also the reason to save
sl@0:      * the nuloc now, it may change during the course of the function.
sl@0:      */
sl@0:     ExtCmdLoc* mapPtr   = envPtr->extCmdMapPtr;
sl@0:     int        eclIndex = mapPtr->nuloc - 1;
sl@0: #endif
sl@0: 
sl@0:     /*
sl@0:      * If we're not in a procedure, don't compile.
sl@0:      */
sl@0:     if (envPtr->procPtr == NULL) {
sl@0: 	return TCL_OUT_LINE_COMPILE;
sl@0:     }
sl@0: 
sl@0:     numWords = parsePtr->numWords;
sl@0:     if (numWords == 1) {
sl@0: 	Tcl_ResetResult(interp);
sl@0: 	Tcl_AppendToObj(Tcl_GetObjResult(interp),
sl@0: 		"wrong # args: should be \"lappend varName ?value value ...?\"", -1);
sl@0: 	return TCL_ERROR;
sl@0:     }
sl@0:     if (numWords != 3) {
sl@0: 	/*
sl@0: 	 * LAPPEND instructions currently only handle one value appends
sl@0: 	 */
sl@0:         return TCL_OUT_LINE_COMPILE;
sl@0:     }
sl@0: 
sl@0:     /*
sl@0:      * Decide if we can use a frame slot for the var/array name or if we
sl@0:      * need to emit code to compute and push the name at runtime. We use a
sl@0:      * frame slot (entry in the array of local vars) if we are compiling a
sl@0:      * procedure body and if the name is simple text that does not include
sl@0:      * namespace qualifiers. 
sl@0:      */
sl@0: 
sl@0:     varTokenPtr = parsePtr->tokenPtr
sl@0: 	    + (parsePtr->tokenPtr->numComponents + 1);
sl@0: 
sl@0:     code = TclPushVarName(interp, varTokenPtr, envPtr, TCL_CREATE_VAR,
sl@0: #ifndef TCL_TIP280
sl@0: 	    &localIndex, &simpleVarName, &isScalar);
sl@0: #else
sl@0: 	    &localIndex, &simpleVarName, &isScalar,
sl@0: 	    mapPtr->loc [eclIndex].line [1]);
sl@0: #endif
sl@0:     if (code != TCL_OK) {
sl@0: 	goto done;
sl@0:     }
sl@0: 
sl@0:     /*
sl@0:      * If we are doing an assignment, push the new value.
sl@0:      * In the no values case, create an empty object.
sl@0:      */
sl@0: 
sl@0:     if (numWords > 2) {
sl@0: 	valueTokenPtr = varTokenPtr + (varTokenPtr->numComponents + 1);
sl@0: 	if (valueTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) {
sl@0: 	    TclEmitPush(TclRegisterNewLiteral(envPtr, 
sl@0: 		    valueTokenPtr[1].start, valueTokenPtr[1].size), envPtr);
sl@0: 	} else {
sl@0: #ifdef TCL_TIP280
sl@0: 	    envPtr->line = mapPtr->loc [eclIndex].line [2];
sl@0: #endif
sl@0: 	    code = TclCompileTokens(interp, valueTokenPtr+1,
sl@0: 	            valueTokenPtr->numComponents, envPtr);
sl@0: 	    if (code != TCL_OK) {
sl@0: 		goto done;
sl@0: 	    }
sl@0: 	}
sl@0:     }
sl@0: 
sl@0:     /*
sl@0:      * Emit instructions to set/get the variable.
sl@0:      */
sl@0: 
sl@0:     /*
sl@0:      * The *_STK opcodes should be refactored to make better use of existing
sl@0:      * LOAD/STORE instructions.
sl@0:      */
sl@0:     if (simpleVarName) {
sl@0: 	if (isScalar) {
sl@0: 	    if (localIndex >= 0) {
sl@0: 		if (localIndex <= 255) {
sl@0: 		    TclEmitInstInt1(INST_LAPPEND_SCALAR1, localIndex, envPtr);
sl@0: 		} else {
sl@0: 		    TclEmitInstInt4(INST_LAPPEND_SCALAR4, localIndex, envPtr);
sl@0: 		}
sl@0: 	    } else {
sl@0: 		TclEmitOpcode(INST_LAPPEND_STK, envPtr);
sl@0: 	    }
sl@0: 	} else {
sl@0: 	    if (localIndex >= 0) {
sl@0: 		if (localIndex <= 255) {
sl@0: 		    TclEmitInstInt1(INST_LAPPEND_ARRAY1, localIndex, envPtr);
sl@0: 		} else {
sl@0: 		    TclEmitInstInt4(INST_LAPPEND_ARRAY4, localIndex, envPtr);
sl@0: 		}
sl@0: 	    } else {
sl@0: 		TclEmitOpcode(INST_LAPPEND_ARRAY_STK, envPtr);
sl@0: 	    }
sl@0: 	}
sl@0:     } else {
sl@0: 	TclEmitOpcode(INST_LAPPEND_STK, envPtr);
sl@0:     }
sl@0: 
sl@0:     done:
sl@0:     return code;
sl@0: }
sl@0: 
sl@0: /*
sl@0:  *----------------------------------------------------------------------
sl@0:  *
sl@0:  * TclCompileLindexCmd --
sl@0:  *
sl@0:  *	Procedure called to compile the "lindex" command.
sl@0:  *
sl@0:  * Results:
sl@0:  *	The return value is a standard Tcl result, which is TCL_OK if the
sl@0:  *	compilation was successful.  If the command cannot be byte-compiled,
sl@0:  *	TCL_OUT_LINE_COMPILE is returned.  If an error occurs then the
sl@0:  *	interpreter's result contains an error message, and TCL_ERROR is
sl@0:  *	returned.
sl@0:  *
sl@0:  * Side effects:
sl@0:  *	Instructions are added to envPtr to execute the "lindex" command
sl@0:  *	at runtime.
sl@0:  *
sl@0:  *----------------------------------------------------------------------
sl@0:  */
sl@0: 
sl@0: int
sl@0: TclCompileLindexCmd(interp, parsePtr, envPtr)
sl@0:     Tcl_Interp *interp;		/* Used for error reporting. */
sl@0:     Tcl_Parse *parsePtr;	/* Points to a parse structure for the
sl@0: 				 * command created by Tcl_ParseCommand. */
sl@0:     CompileEnv *envPtr;		/* Holds resulting instructions. */
sl@0: {
sl@0:     Tcl_Token *varTokenPtr;
sl@0:     int code, i;
sl@0: 
sl@0: #ifdef TCL_TIP280
sl@0:     /* TIP #280 : Remember the per-word line information of the current
sl@0:      * command. An index is used instead of a pointer as recursive compilation
sl@0:      * may reallocate, i.e. move, the array. This is also the reason to save
sl@0:      * the nuloc now, it may change during the course of the function.
sl@0:      */
sl@0:     ExtCmdLoc* mapPtr   = envPtr->extCmdMapPtr;
sl@0:     int        eclIndex = mapPtr->nuloc - 1;
sl@0: #endif
sl@0: 
sl@0:     int numWords;
sl@0:     numWords = parsePtr->numWords;
sl@0: 
sl@0:     /*
sl@0:      * Quit if too few args
sl@0:      */
sl@0: 
sl@0:     if ( numWords <= 1 ) {
sl@0: 	return TCL_OUT_LINE_COMPILE;
sl@0:     }
sl@0: 
sl@0:     varTokenPtr = parsePtr->tokenPtr
sl@0: 	+ (parsePtr->tokenPtr->numComponents + 1);
sl@0:     
sl@0:     /*
sl@0:      * Push the operands onto the stack.
sl@0:      */
sl@0: 	
sl@0:     for ( i = 1 ; i < numWords ; i++ ) {
sl@0: 	if (varTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) {
sl@0: 	    TclEmitPush(
sl@0: 		    TclRegisterNewLiteral( envPtr, varTokenPtr[1].start,
sl@0: 		    varTokenPtr[1].size), envPtr);
sl@0: 	} else {
sl@0: #ifdef TCL_TIP280
sl@0: 	    envPtr->line = mapPtr->loc [eclIndex].line [i];
sl@0: #endif
sl@0: 	    code = TclCompileTokens(interp, varTokenPtr+1,
sl@0: 				    varTokenPtr->numComponents, envPtr);
sl@0: 	    if (code != TCL_OK) {
sl@0: 		return code;
sl@0: 	    }
sl@0: 	}
sl@0: 	varTokenPtr = varTokenPtr + (varTokenPtr->numComponents + 1);
sl@0:     }
sl@0: 	
sl@0:     /*
sl@0:      * Emit INST_LIST_INDEX if objc==3, or INST_LIST_INDEX_MULTI
sl@0:      * if there are multiple index args.
sl@0:      */
sl@0: 
sl@0:     if ( numWords == 3 ) {
sl@0: 	TclEmitOpcode( INST_LIST_INDEX, envPtr );
sl@0:     } else {
sl@0:  	TclEmitInstInt4( INST_LIST_INDEX_MULTI, numWords-1, envPtr );
sl@0:     }
sl@0: 
sl@0:     return TCL_OK;
sl@0: }
sl@0: 
sl@0: /*
sl@0:  *----------------------------------------------------------------------
sl@0:  *
sl@0:  * TclCompileListCmd --
sl@0:  *
sl@0:  *	Procedure called to compile the "list" command.
sl@0:  *
sl@0:  * Results:
sl@0:  *	The return value is a standard Tcl result, which is normally TCL_OK
sl@0:  *	unless there was an error while parsing string. If an error occurs
sl@0:  *	then the interpreter's result contains a standard error message. If
sl@0:  *	complation fails because the command requires a second level of
sl@0:  *	substitutions, TCL_OUT_LINE_COMPILE is returned indicating that the
sl@0:  *	command should be compiled "out of line" by emitting code to
sl@0:  *	invoke its command procedure (Tcl_ListObjCmd) at runtime.
sl@0:  *
sl@0:  * Side effects:
sl@0:  *	Instructions are added to envPtr to execute the "list" command
sl@0:  *	at runtime.
sl@0:  *
sl@0:  *----------------------------------------------------------------------
sl@0:  */
sl@0: 
sl@0: int
sl@0: TclCompileListCmd(interp, parsePtr, envPtr)
sl@0:     Tcl_Interp *interp;		/* Used for error reporting. */
sl@0:     Tcl_Parse *parsePtr;	/* Points to a parse structure for the
sl@0: 				 * command created by Tcl_ParseCommand. */
sl@0:     CompileEnv *envPtr;		/* Holds resulting instructions. */
sl@0: {
sl@0: #ifdef TCL_TIP280
sl@0:     /* TIP #280 : Remember the per-word line information of the current
sl@0:      * command. An index is used instead of a pointer as recursive compilation
sl@0:      * may reallocate, i.e. move, the array. This is also the reason to save
sl@0:      * the nuloc now, it may change during the course of the function.
sl@0:      */
sl@0:     ExtCmdLoc* mapPtr   = envPtr->extCmdMapPtr;
sl@0:     int        eclIndex = mapPtr->nuloc - 1;
sl@0: #endif
sl@0: 
sl@0:     /*
sl@0:      * If we're not in a procedure, don't compile.
sl@0:      */
sl@0:     if (envPtr->procPtr == NULL) {
sl@0: 	return TCL_OUT_LINE_COMPILE;
sl@0:     }
sl@0: 
sl@0:     if (parsePtr->numWords == 1) {
sl@0: 	/*
sl@0: 	 * Empty args case
sl@0: 	 */
sl@0: 
sl@0: 	TclEmitPush(TclRegisterNewLiteral(envPtr, "", 0), envPtr);
sl@0:     } else {
sl@0: 	/*
sl@0: 	 * Push the all values onto the stack.
sl@0: 	 */
sl@0: 	Tcl_Token *valueTokenPtr;
sl@0: 	int i, code, numWords;
sl@0: 
sl@0: 	numWords = parsePtr->numWords;
sl@0: 
sl@0: 	valueTokenPtr = parsePtr->tokenPtr
sl@0: 	    + (parsePtr->tokenPtr->numComponents + 1);
sl@0: 	for (i = 1; i < numWords; i++) {
sl@0: 	    if (valueTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) {
sl@0: 		TclEmitPush(TclRegisterNewLiteral(envPtr,
sl@0: 			valueTokenPtr[1].start, valueTokenPtr[1].size), envPtr);
sl@0: 	    } else {
sl@0: #ifdef TCL_TIP280
sl@0: 	        envPtr->line = mapPtr->loc [eclIndex].line [i];
sl@0: #endif
sl@0: 		code = TclCompileTokens(interp, valueTokenPtr+1,
sl@0: 			valueTokenPtr->numComponents, envPtr);
sl@0: 		if (code != TCL_OK) {
sl@0: 		    return code;
sl@0: 		}
sl@0: 	    }
sl@0: 	    valueTokenPtr = valueTokenPtr + (valueTokenPtr->numComponents + 1);
sl@0: 	}
sl@0: 	TclEmitInstInt4(INST_LIST, numWords - 1, envPtr);
sl@0:     }
sl@0: 
sl@0:     return TCL_OK;
sl@0: }
sl@0: 
sl@0: /*
sl@0:  *----------------------------------------------------------------------
sl@0:  *
sl@0:  * TclCompileLlengthCmd --
sl@0:  *
sl@0:  *	Procedure called to compile the "llength" command.
sl@0:  *
sl@0:  * Results:
sl@0:  *	The return value is a standard Tcl result, which is TCL_OK if the
sl@0:  *	compilation was successful.  If the command cannot be byte-compiled,
sl@0:  *	TCL_OUT_LINE_COMPILE is returned.  If an error occurs then the
sl@0:  *	interpreter's result contains an error message, and TCL_ERROR is
sl@0:  *	returned.
sl@0:  *
sl@0:  * Side effects:
sl@0:  *	Instructions are added to envPtr to execute the "llength" command
sl@0:  *	at runtime.
sl@0:  *
sl@0:  *----------------------------------------------------------------------
sl@0:  */
sl@0: 
sl@0: int
sl@0: TclCompileLlengthCmd(interp, parsePtr, envPtr)
sl@0:     Tcl_Interp *interp;		/* Used for error reporting. */
sl@0:     Tcl_Parse *parsePtr;	/* Points to a parse structure for the
sl@0: 				 * command created by Tcl_ParseCommand. */
sl@0:     CompileEnv *envPtr;		/* Holds resulting instructions. */
sl@0: {
sl@0:     Tcl_Token *varTokenPtr;
sl@0:     int code;
sl@0: 
sl@0: #ifdef TCL_TIP280
sl@0:     /* TIP #280 : Remember the per-word line information of the current
sl@0:      * command. An index is used instead of a pointer as recursive compilation
sl@0:      * may reallocate, i.e. move, the array. This is also the reason to save
sl@0:      * the nuloc now, it may change during the course of the function.
sl@0:      */
sl@0:     ExtCmdLoc* mapPtr   = envPtr->extCmdMapPtr;
sl@0:     int        eclIndex = mapPtr->nuloc - 1;
sl@0: #endif
sl@0: 
sl@0:     if (parsePtr->numWords != 2) {
sl@0: 	Tcl_SetResult(interp, "wrong # args: should be \"llength list\"",
sl@0: 		TCL_STATIC);
sl@0: 	return TCL_ERROR;
sl@0:     }
sl@0:     varTokenPtr = parsePtr->tokenPtr
sl@0: 	+ (parsePtr->tokenPtr->numComponents + 1);
sl@0: 
sl@0:     if (varTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) {
sl@0: 	/*
sl@0: 	 * We could simply count the number of elements here and push
sl@0: 	 * that value, but that is too rare a case to waste the code space.
sl@0: 	 */
sl@0: 	TclEmitPush(TclRegisterNewLiteral(envPtr, varTokenPtr[1].start,
sl@0: 		varTokenPtr[1].size), envPtr);
sl@0:     } else {
sl@0: #ifdef TCL_TIP280
sl@0:         envPtr->line = mapPtr->loc [eclIndex].line [1];
sl@0: #endif
sl@0: 	code = TclCompileTokens(interp, varTokenPtr+1,
sl@0: 		varTokenPtr->numComponents, envPtr);
sl@0: 	if (code != TCL_OK) {
sl@0: 	    return code;
sl@0: 	}
sl@0:     }
sl@0:     TclEmitOpcode(INST_LIST_LENGTH, envPtr);
sl@0:     return TCL_OK;
sl@0: }
sl@0: 
sl@0: /*
sl@0:  *----------------------------------------------------------------------
sl@0:  *
sl@0:  * TclCompileLsetCmd --
sl@0:  *
sl@0:  *	Procedure called to compile the "lset" command.
sl@0:  *
sl@0:  * Results:
sl@0:  *	The return value is a standard Tcl result, which is TCL_OK if
sl@0:  *	the compilation was successful.  If the "lset" command is too
sl@0:  *	complex for this function, then TCL_OUT_LINE_COMPILE is returned,
sl@0:  *	indicating that the command should be compiled "out of line"
sl@0:  *	(that is, not byte-compiled).  If an error occurs, TCL_ERROR is
sl@0:  *	returned, and the interpreter result contains an error message.
sl@0:  *
sl@0:  * Side effects:
sl@0:  *	Instructions are added to envPtr to execute the "lset" command
sl@0:  *	at runtime.
sl@0:  *
sl@0:  * The general template for execution of the "lset" command is:
sl@0:  *	(1) Instructions to push the variable name, unless the
sl@0:  *	    variable is local to the stack frame.
sl@0:  *	(2) If the variable is an array element, instructions
sl@0:  *	    to push the array element name.
sl@0:  *	(3) Instructions to push each of zero or more "index" arguments
sl@0:  *	    to the stack, followed with the "newValue" element.
sl@0:  *	(4) Instructions to duplicate the variable name and/or array
sl@0:  *	    element name onto the top of the stack, if either was
sl@0:  *	    pushed at steps (1) and (2).
sl@0:  *	(5) The appropriate INST_LOAD_* instruction to place the
sl@0:  *	    original value of the list variable at top of stack.
sl@0:  *	(6) At this point, the stack contains:
sl@0:  *	     varName? arrayElementName? index1 index2 ... newValue oldList
sl@0:  *	    The compiler emits one of INST_LSET_FLAT or INST_LSET_LIST
sl@0:  *	    according as whether there is exactly one index element (LIST)
sl@0:  *	    or either zero or else two or more (FLAT).  This instruction
sl@0:  *	    removes everything from the stack except for the two names
sl@0:  *	    and pushes the new value of the variable.
sl@0:  *	(7) Finally, INST_STORE_* stores the new value in the variable
sl@0:  *	    and cleans up the stack.
sl@0:  *
sl@0:  *----------------------------------------------------------------------
sl@0:  */
sl@0: 
sl@0: int
sl@0: TclCompileLsetCmd( interp, parsePtr, envPtr )
sl@0:     Tcl_Interp* interp;		/* Tcl interpreter for error reporting */
sl@0:     Tcl_Parse* parsePtr;	/* Points to a parse structure for
sl@0: 				 * the command */
sl@0:     CompileEnv* envPtr;		/* Holds the resulting instructions */
sl@0: {
sl@0: 
sl@0:     int tempDepth;		/* Depth used for emitting one part
sl@0: 				 * of the code burst. */
sl@0:     Tcl_Token* varTokenPtr;	/* Pointer to the Tcl_Token representing
sl@0: 				 * the parse of the variable name */
sl@0: 
sl@0:     int result;			/* Status return from library calls */
sl@0: 
sl@0:     int localIndex;		/* Index of var in local var table */
sl@0:     int simpleVarName;		/* Flag == 1 if var name is simple */
sl@0:     int isScalar;		/* Flag == 1 if scalar, 0 if array */
sl@0: 
sl@0:     int i;
sl@0: 
sl@0: #ifdef TCL_TIP280
sl@0:     /* TIP #280 : Remember the per-word line information of the current
sl@0:      * command. An index is used instead of a pointer as recursive compilation
sl@0:      * may reallocate, i.e. move, the array. This is also the reason to save
sl@0:      * the nuloc now, it may change during the course of the function.
sl@0:      */
sl@0:     ExtCmdLoc* mapPtr   = envPtr->extCmdMapPtr;
sl@0:     int        eclIndex = mapPtr->nuloc - 1;
sl@0: #endif
sl@0: 
sl@0:     /* Check argument count */
sl@0: 
sl@0:     if ( parsePtr->numWords < 3 ) {
sl@0: 	/* Fail at run time, not in compilation */
sl@0: 	return TCL_OUT_LINE_COMPILE;
sl@0:     }
sl@0: 
sl@0:     /*
sl@0:      * Decide if we can use a frame slot for the var/array name or if we
sl@0:      * need to emit code to compute and push the name at runtime. We use a
sl@0:      * frame slot (entry in the array of local vars) if we are compiling a
sl@0:      * procedure body and if the name is simple text that does not include
sl@0:      * namespace qualifiers. 
sl@0:      */
sl@0: 
sl@0:     varTokenPtr = parsePtr->tokenPtr
sl@0: 	    + (parsePtr->tokenPtr->numComponents + 1);
sl@0:     result = TclPushVarName( interp, varTokenPtr, envPtr, 
sl@0: #ifndef TCL_TIP280
sl@0:             TCL_CREATE_VAR, &localIndex, &simpleVarName, &isScalar );
sl@0: #else
sl@0:             TCL_CREATE_VAR, &localIndex, &simpleVarName, &isScalar,
sl@0: 	    mapPtr->loc [eclIndex].line [1]);
sl@0: #endif
sl@0:     if (result != TCL_OK) {
sl@0: 	return result;
sl@0:     }
sl@0: 
sl@0:     /* Push the "index" args and the new element value. */
sl@0: 
sl@0:     for ( i = 2; i < parsePtr->numWords; ++i ) {
sl@0: 
sl@0: 	/* Advance to next arg */
sl@0: 
sl@0: 	varTokenPtr = varTokenPtr + (varTokenPtr->numComponents + 1);
sl@0: 
sl@0: 	/* Push an arg */
sl@0: 
sl@0: 	if (varTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) {
sl@0: 	    TclEmitPush(TclRegisterNewLiteral( envPtr, varTokenPtr[1].start,
sl@0: 		    varTokenPtr[1].size), envPtr);
sl@0: 	} else {
sl@0: #ifdef TCL_TIP280
sl@0: 	    envPtr->line = mapPtr->loc [eclIndex].line [i];
sl@0: #endif
sl@0: 	    result = TclCompileTokens(interp, varTokenPtr+1,
sl@0: 				      varTokenPtr->numComponents, envPtr);
sl@0: 	    if ( result != TCL_OK ) {
sl@0: 		return result;
sl@0: 	    }
sl@0: 	}
sl@0:     }
sl@0: 
sl@0:     /*
sl@0:      * Duplicate the variable name if it's been pushed.  
sl@0:      */
sl@0: 
sl@0:     if ( !simpleVarName || localIndex < 0 ) {
sl@0: 	if ( !simpleVarName || isScalar ) {
sl@0: 	    tempDepth = parsePtr->numWords - 2;
sl@0: 	} else {
sl@0: 	    tempDepth = parsePtr->numWords - 1;
sl@0: 	}
sl@0: 	TclEmitInstInt4( INST_OVER, tempDepth, envPtr );
sl@0:     }
sl@0: 
sl@0:     /*
sl@0:      * Duplicate an array index if one's been pushed
sl@0:      */
sl@0: 
sl@0:     if ( simpleVarName && !isScalar ) {
sl@0: 	if ( localIndex < 0 ) {
sl@0: 	    tempDepth = parsePtr->numWords - 1;
sl@0: 	} else {
sl@0: 	    tempDepth = parsePtr->numWords - 2;
sl@0: 	}
sl@0: 	TclEmitInstInt4( INST_OVER, tempDepth, envPtr );
sl@0:     }
sl@0: 
sl@0:     /*
sl@0:      * Emit code to load the variable's value.
sl@0:      */
sl@0: 
sl@0:     if ( !simpleVarName ) {
sl@0: 	TclEmitOpcode( INST_LOAD_STK, envPtr );
sl@0:     } else if ( isScalar ) {
sl@0: 	if ( localIndex < 0 ) {
sl@0: 	    TclEmitOpcode( INST_LOAD_SCALAR_STK, envPtr );
sl@0: 	} else if ( localIndex < 0x100 ) {
sl@0: 	    TclEmitInstInt1( INST_LOAD_SCALAR1, localIndex, envPtr );
sl@0: 	} else {
sl@0: 	    TclEmitInstInt4( INST_LOAD_SCALAR4, localIndex, envPtr );
sl@0: 	}
sl@0:     } else {
sl@0: 	if ( localIndex < 0 ) {
sl@0: 	    TclEmitOpcode( INST_LOAD_ARRAY_STK, envPtr );
sl@0: 	} else if ( localIndex < 0x100 ) {
sl@0: 	    TclEmitInstInt1( INST_LOAD_ARRAY1, localIndex, envPtr );
sl@0: 	} else {
sl@0: 	    TclEmitInstInt4( INST_LOAD_ARRAY4, localIndex, envPtr );
sl@0: 	}
sl@0:     }
sl@0: 
sl@0:     /*
sl@0:      * Emit the correct variety of 'lset' instruction
sl@0:      */
sl@0: 
sl@0:     if ( parsePtr->numWords == 4 ) {
sl@0: 	TclEmitOpcode( INST_LSET_LIST, envPtr );
sl@0:     } else {
sl@0: 	TclEmitInstInt4( INST_LSET_FLAT, (parsePtr->numWords - 1), envPtr );
sl@0:     }
sl@0: 
sl@0:     /*
sl@0:      * Emit code to put the value back in the variable
sl@0:      */
sl@0: 
sl@0:     if ( !simpleVarName ) {
sl@0: 	TclEmitOpcode( INST_STORE_STK, envPtr );
sl@0:     } else if ( isScalar ) {
sl@0: 	if ( localIndex < 0 ) {
sl@0: 	    TclEmitOpcode( INST_STORE_SCALAR_STK, envPtr );
sl@0: 	} else if ( localIndex < 0x100 ) {
sl@0: 	    TclEmitInstInt1( INST_STORE_SCALAR1, localIndex, envPtr );
sl@0: 	} else {
sl@0: 	    TclEmitInstInt4( INST_STORE_SCALAR4, localIndex, envPtr );
sl@0: 	}
sl@0:     } else {
sl@0: 	if ( localIndex < 0 ) {
sl@0: 	    TclEmitOpcode( INST_STORE_ARRAY_STK, envPtr );
sl@0: 	} else if ( localIndex < 0x100 ) {
sl@0: 	    TclEmitInstInt1( INST_STORE_ARRAY1, localIndex, envPtr );
sl@0: 	} else {
sl@0: 	    TclEmitInstInt4( INST_STORE_ARRAY4, localIndex, envPtr );
sl@0: 	}
sl@0:     }
sl@0:     
sl@0:     return TCL_OK;
sl@0: 
sl@0: }
sl@0: 
sl@0: /*
sl@0:  *----------------------------------------------------------------------
sl@0:  *
sl@0:  * TclCompileRegexpCmd --
sl@0:  *
sl@0:  *	Procedure called to compile the "regexp" command.
sl@0:  *
sl@0:  * Results:
sl@0:  *	The return value is a standard Tcl result, which is TCL_OK if
sl@0:  *	the compilation was successful.  If the "regexp" command is too
sl@0:  *	complex for this function, then TCL_OUT_LINE_COMPILE is returned,
sl@0:  *	indicating that the command should be compiled "out of line"
sl@0:  *	(that is, not byte-compiled).  If an error occurs, TCL_ERROR is
sl@0:  *	returned, and the interpreter result contains an error message.
sl@0:  *
sl@0:  * Side effects:
sl@0:  *	Instructions are added to envPtr to execute the "regexp" command
sl@0:  *	at runtime.
sl@0:  *
sl@0:  *----------------------------------------------------------------------
sl@0:  */
sl@0: 
sl@0: int
sl@0: TclCompileRegexpCmd(interp, parsePtr, envPtr)
sl@0:     Tcl_Interp* interp;		/* Tcl interpreter for error reporting */
sl@0:     Tcl_Parse* parsePtr;	/* Points to a parse structure for
sl@0: 				 * the command */
sl@0:     CompileEnv* envPtr;		/* Holds the resulting instructions */
sl@0: {
sl@0:     Tcl_Token *varTokenPtr;	/* Pointer to the Tcl_Token representing
sl@0: 				 * the parse of the RE or string */
sl@0:     int i, len, code, nocase, anchorLeft, anchorRight, start;
sl@0:     char *str;
sl@0: 
sl@0: #ifdef TCL_TIP280
sl@0:     /* TIP #280 : Remember the per-word line information of the current
sl@0:      * command. An index is used instead of a pointer as recursive compilation
sl@0:      * may reallocate, i.e. move, the array. This is also the reason to save
sl@0:      * the nuloc now, it may change during the course of the function.
sl@0:      */
sl@0:     ExtCmdLoc* mapPtr   = envPtr->extCmdMapPtr;
sl@0:     int        eclIndex = mapPtr->nuloc - 1;
sl@0: #endif
sl@0: 
sl@0:     /*
sl@0:      * We are only interested in compiling simple regexp cases.
sl@0:      * Currently supported compile cases are:
sl@0:      *   regexp ?-nocase? ?--? staticString $var
sl@0:      *   regexp ?-nocase? ?--? {^staticString$} $var
sl@0:      */
sl@0:     if (parsePtr->numWords < 3) {
sl@0: 	return TCL_OUT_LINE_COMPILE;
sl@0:     }
sl@0: 
sl@0:     nocase = 0;
sl@0:     varTokenPtr = parsePtr->tokenPtr;
sl@0: 
sl@0:     /*
sl@0:      * We only look for -nocase and -- as options.  Everything else
sl@0:      * gets pushed to runtime execution.  This is different than regexp's
sl@0:      * runtime option handling, but satisfies our stricter needs.
sl@0:      */
sl@0:     for (i = 1; i < parsePtr->numWords - 2; i++) {
sl@0: 	varTokenPtr = varTokenPtr + (varTokenPtr->numComponents + 1);
sl@0: 	if (varTokenPtr->type != TCL_TOKEN_SIMPLE_WORD) {
sl@0: 	    /* Not a simple string - punt to runtime. */
sl@0: 	    return TCL_OUT_LINE_COMPILE;
sl@0: 	}
sl@0: 	str = (char *) varTokenPtr[1].start;
sl@0: 	len = varTokenPtr[1].size;
sl@0: 	if ((len == 2) && (str[0] == '-') && (str[1] == '-')) {
sl@0: 	    i++;
sl@0: 	    break;
sl@0: 	} else if ((len > 1)
sl@0: 		&& (strncmp(str, "-nocase", (unsigned) len) == 0)) {
sl@0: 	    nocase = 1;
sl@0: 	} else {
sl@0: 	    /* Not an option we recognize. */
sl@0: 	    return TCL_OUT_LINE_COMPILE;
sl@0: 	}
sl@0:     }
sl@0: 
sl@0:     if ((parsePtr->numWords - i) != 2) {
sl@0: 	/* We don't support capturing to variables */
sl@0: 	return TCL_OUT_LINE_COMPILE;
sl@0:     }
sl@0: 
sl@0:     /*
sl@0:      * Get the regexp string.  If it is not a simple string, punt to runtime.
sl@0:      * If it has a '-', it could be an incorrectly formed regexp command.
sl@0:      */
sl@0:     varTokenPtr = varTokenPtr + (varTokenPtr->numComponents + 1);
sl@0:     str = (char *) varTokenPtr[1].start;
sl@0:     len = varTokenPtr[1].size;
sl@0:     if ((varTokenPtr->type != TCL_TOKEN_SIMPLE_WORD) || (*str == '-')) {
sl@0: 	return TCL_OUT_LINE_COMPILE;
sl@0:     }
sl@0: 
sl@0:     if (len == 0) {
sl@0: 	/*
sl@0: 	 * The semantics of regexp are always match on re == "".
sl@0: 	 */
sl@0: 	TclEmitPush(TclRegisterNewLiteral(envPtr, "1", 1), envPtr);
sl@0: 	return TCL_OK;
sl@0:     }
sl@0: 
sl@0:     /*
sl@0:      * Make a copy of the string that is null-terminated for checks which
sl@0:      * require such.
sl@0:      */
sl@0:     str = (char *) ckalloc((unsigned) len + 1);
sl@0:     strncpy(str, varTokenPtr[1].start, (size_t) len);
sl@0:     str[len] = '\0';
sl@0:     start = 0;
sl@0: 
sl@0:     /*
sl@0:      * Check for anchored REs (ie ^foo$), so we can use string equal if
sl@0:      * possible. Do not alter the start of str so we can free it correctly.
sl@0:      */
sl@0:     if (str[0] == '^') {
sl@0: 	start++;
sl@0: 	anchorLeft = 1;
sl@0:     } else {
sl@0: 	anchorLeft = 0;
sl@0:     }
sl@0:     if ((str[len-1] == '$') && ((len == 1) || (str[len-2] != '\\'))) {
sl@0: 	anchorRight = 1;
sl@0: 	str[--len] = '\0';
sl@0:     } else {
sl@0: 	anchorRight = 0;
sl@0:     }
sl@0: 
sl@0:     /*
sl@0:      * On the first (pattern) arg, check to see if any RE special characters
sl@0:      * are in the word.  If not, this is the same as 'string equal'.
sl@0:      */
sl@0:     if ((len > (1+start)) && (str[start] == '.') && (str[start+1] == '*')) {
sl@0: 	start += 2;
sl@0: 	anchorLeft = 0;
sl@0:     }
sl@0:     if ((len > (2+start)) && (str[len-3] != '\\')
sl@0: 	    && (str[len-2] == '.') && (str[len-1] == '*')) {
sl@0: 	len -= 2;
sl@0: 	str[len] = '\0';
sl@0: 	anchorRight = 0;
sl@0:     }
sl@0: 
sl@0:     /*
sl@0:      * Don't do anything with REs with other special chars.  Also check if
sl@0:      * this is a bad RE (do this at the end because it can be expensive).
sl@0:      * If so, let it complain at runtime.
sl@0:      */
sl@0:     if ((strpbrk(str + start, "*+?{}()[].\\|^$") != NULL)
sl@0: 	    || (Tcl_RegExpCompile(NULL, str) == NULL)) {
sl@0: 	ckfree((char *) str);
sl@0: 	return TCL_OUT_LINE_COMPILE;
sl@0:     }
sl@0: 
sl@0:     if (anchorLeft && anchorRight) {
sl@0: 	TclEmitPush(TclRegisterNewLiteral(envPtr, str+start, len-start),
sl@0: 		envPtr);
sl@0:     } else {
sl@0: 	/*
sl@0: 	 * This needs to find the substring anywhere in the string, so
sl@0: 	 * use string match and *foo*, with appropriate anchoring.
sl@0: 	 */
sl@0: 	char *newStr  = ckalloc((unsigned) len + 3);
sl@0: 	len -= start;
sl@0: 	if (anchorLeft) {
sl@0: 	    strncpy(newStr, str + start, (size_t) len);
sl@0: 	} else {
sl@0: 	    newStr[0] = '*';
sl@0: 	    strncpy(newStr + 1, str + start, (size_t) len++);
sl@0: 	}
sl@0: 	if (!anchorRight) {
sl@0: 	    newStr[len++] = '*';
sl@0: 	}
sl@0: 	newStr[len] = '\0';
sl@0: 	TclEmitPush(TclRegisterNewLiteral(envPtr, newStr, len), envPtr);
sl@0: 	ckfree((char *) newStr);
sl@0:     }
sl@0:     ckfree((char *) str);
sl@0: 
sl@0:     /*
sl@0:      * Push the string arg
sl@0:      */
sl@0:     varTokenPtr = varTokenPtr + (varTokenPtr->numComponents + 1);
sl@0:     if (varTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) {
sl@0: 	TclEmitPush(TclRegisterNewLiteral(envPtr,
sl@0: 		varTokenPtr[1].start, varTokenPtr[1].size), envPtr);
sl@0:     } else {
sl@0: #ifdef TCL_TIP280
sl@0:         envPtr->line = mapPtr->loc [eclIndex].line [parsePtr->numWords-1];
sl@0: #endif
sl@0: 	code = TclCompileTokens(interp, varTokenPtr+1,
sl@0: 		varTokenPtr->numComponents, envPtr);
sl@0: 	if (code != TCL_OK) {
sl@0: 	    return code;
sl@0: 	}
sl@0:     }
sl@0: 
sl@0:     if (anchorLeft && anchorRight && !nocase) {
sl@0: 	TclEmitOpcode(INST_STR_EQ, envPtr);
sl@0:     } else {
sl@0: 	TclEmitInstInt1(INST_STR_MATCH, nocase, envPtr);
sl@0:     }
sl@0: 
sl@0:     return TCL_OK;
sl@0: }
sl@0: 
sl@0: /*
sl@0:  *----------------------------------------------------------------------
sl@0:  *
sl@0:  * TclCompileReturnCmd --
sl@0:  *
sl@0:  *	Procedure called to compile the "return" command.
sl@0:  *
sl@0:  * Results:
sl@0:  *	The return value is a standard Tcl result, which is TCL_OK if the
sl@0:  *	compilation was successful.  If the particular return command is
sl@0:  *	too complex for this function (ie, return with any flags like "-code"
sl@0:  *	or "-errorinfo"), TCL_OUT_LINE_COMPILE is returned, indicating that
sl@0:  *	the command should be compiled "out of line" (eg, not byte compiled).
sl@0:  *	If an error occurs then the interpreter's result contains a standard
sl@0:  *	error message.
sl@0:  *
sl@0:  * Side effects:
sl@0:  *	Instructions are added to envPtr to execute the "return" command
sl@0:  *	at runtime.
sl@0:  *
sl@0:  *----------------------------------------------------------------------
sl@0:  */
sl@0: 
sl@0: int
sl@0: TclCompileReturnCmd(interp, parsePtr, envPtr)
sl@0:     Tcl_Interp *interp;		/* Used for error reporting. */
sl@0:     Tcl_Parse *parsePtr;	/* Points to a parse structure for the
sl@0: 				 * command created by Tcl_ParseCommand. */
sl@0:     CompileEnv *envPtr;		/* Holds resulting instructions. */
sl@0: {
sl@0:     Tcl_Token *varTokenPtr;
sl@0:     int code;
sl@0:     int index = envPtr->exceptArrayNext - 1;
sl@0: 
sl@0: #ifdef TCL_TIP280
sl@0:     /* TIP #280 : Remember the per-word line information of the current
sl@0:      * command. An index is used instead of a pointer as recursive compilation
sl@0:      * may reallocate, i.e. move, the array. This is also the reason to save
sl@0:      * the nuloc now, it may change during the course of the function.
sl@0:      */
sl@0:     ExtCmdLoc* mapPtr   = envPtr->extCmdMapPtr;
sl@0:     int        eclIndex = mapPtr->nuloc - 1;
sl@0: #endif
sl@0: 
sl@0:     /*
sl@0:      * If we're not in a procedure, don't compile.
sl@0:      */
sl@0: 
sl@0:     if (envPtr->procPtr == NULL) {
sl@0: 	return TCL_OUT_LINE_COMPILE;
sl@0:     }
sl@0: 
sl@0:     /*
sl@0:      * Look back through the ExceptionRanges of the current CompileEnv,
sl@0:      * from exceptArrayPtr[(exceptArrayNext - 1)] down to 
sl@0:      * exceptArrayPtr[0] to see if any of them is an enclosing [catch].
sl@0:      * If there's an enclosing [catch], don't compile.
sl@0:      */
sl@0: 
sl@0:     while (index >= 0) {
sl@0: 	ExceptionRange range = envPtr->exceptArrayPtr[index];
sl@0: 	if ((range.type == CATCH_EXCEPTION_RANGE) 
sl@0: 		&& (range.catchOffset == -1)) {
sl@0: 	    return TCL_OUT_LINE_COMPILE;
sl@0: 	}
sl@0: 	index--;
sl@0:     }
sl@0: 
sl@0:     switch (parsePtr->numWords) {
sl@0: 	case 1: {
sl@0: 	    /*
sl@0: 	     * Simple case:  [return]
sl@0: 	     * Just push the literal string "".
sl@0: 	     */
sl@0: 	    TclEmitPush(TclRegisterNewLiteral(envPtr, "", 0), envPtr);
sl@0: 	    break;
sl@0: 	}
sl@0: 	case 2: {
sl@0: 	    /*
sl@0: 	     * More complex cases:
sl@0: 	     * [return "foo"]
sl@0: 	     * [return $value]
sl@0: 	     * [return [otherCmd]]
sl@0: 	     */
sl@0: 	    varTokenPtr = parsePtr->tokenPtr
sl@0: 		+ (parsePtr->tokenPtr->numComponents + 1);
sl@0: 	    if (varTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) {
sl@0: 		/*
sl@0: 		 * [return "foo"] case:  the parse token is a simple word,
sl@0: 		 * so just push it.
sl@0: 		 */
sl@0: 		TclEmitPush(TclRegisterNewLiteral(envPtr, varTokenPtr[1].start,
sl@0: 			varTokenPtr[1].size), envPtr);
sl@0: 	    } else {
sl@0: 		/*
sl@0: 		 * Parse token is more complex, so compile it; this handles the
sl@0: 		 * variable reference and nested command cases.  If the
sl@0: 		 * parse token can be byte-compiled, then this instance of
sl@0: 		 * "return" will be byte-compiled; otherwise it will be
sl@0: 		 * out line compiled.
sl@0: 		 */
sl@0: #ifdef TCL_TIP280
sl@0: 	        envPtr->line = mapPtr->loc [eclIndex].line [1];
sl@0: #endif
sl@0: 		code = TclCompileTokens(interp, varTokenPtr+1,
sl@0: 			varTokenPtr->numComponents, envPtr);
sl@0: 		if (code != TCL_OK) {
sl@0: 		    return code;
sl@0: 		}
sl@0: 	    }
sl@0: 	    break;
sl@0: 	}
sl@0: 	default: {
sl@0: 	    /*
sl@0: 	     * Most complex return cases: everything else, including
sl@0: 	     * [return -code error], etc.
sl@0: 	     */
sl@0: 	    return TCL_OUT_LINE_COMPILE;
sl@0: 	}
sl@0:     }
sl@0: 
sl@0:     /*
sl@0:      * The INST_DONE opcode actually causes the branching out of the
sl@0:      * subroutine, and takes the top stack item as the return result
sl@0:      * (which is why we pushed the value above).
sl@0:      */
sl@0:     TclEmitOpcode(INST_DONE, envPtr);
sl@0:     return TCL_OK;
sl@0: }
sl@0: 
sl@0: /*
sl@0:  *----------------------------------------------------------------------
sl@0:  *
sl@0:  * TclCompileSetCmd --
sl@0:  *
sl@0:  *	Procedure called to compile the "set" command.
sl@0:  *
sl@0:  * Results:
sl@0:  *	The return value is a standard Tcl result, which is normally TCL_OK
sl@0:  *	unless there was an error while parsing string. If an error occurs
sl@0:  *	then the interpreter's result contains a standard error message. If
sl@0:  *	complation fails because the set command requires a second level of
sl@0:  *	substitutions, TCL_OUT_LINE_COMPILE is returned indicating that the
sl@0:  *	set command should be compiled "out of line" by emitting code to
sl@0:  *	invoke its command procedure (Tcl_SetCmd) at runtime.
sl@0:  *
sl@0:  * Side effects:
sl@0:  *	Instructions are added to envPtr to execute the "set" command
sl@0:  *	at runtime.
sl@0:  *
sl@0:  *----------------------------------------------------------------------
sl@0:  */
sl@0: 
sl@0: int
sl@0: TclCompileSetCmd(interp, parsePtr, envPtr)
sl@0:     Tcl_Interp *interp;		/* Used for error reporting. */
sl@0:     Tcl_Parse *parsePtr;	/* Points to a parse structure for the
sl@0: 				 * command created by Tcl_ParseCommand. */
sl@0:     CompileEnv *envPtr;		/* Holds resulting instructions. */
sl@0: {
sl@0:     Tcl_Token *varTokenPtr, *valueTokenPtr;
sl@0:     int isAssignment, isScalar, simpleVarName, localIndex, numWords;
sl@0:     int code = TCL_OK;
sl@0: 
sl@0: #ifdef TCL_TIP280
sl@0:     /* TIP #280 : Remember the per-word line information of the current
sl@0:      * command. An index is used instead of a pointer as recursive compilation
sl@0:      * may reallocate, i.e. move, the array. This is also the reason to save
sl@0:      * the nuloc now, it may change during the course of the function.
sl@0:      */
sl@0:     ExtCmdLoc* mapPtr   = envPtr->extCmdMapPtr;
sl@0:     int        eclIndex = mapPtr->nuloc - 1;
sl@0: #endif
sl@0: 
sl@0:     numWords = parsePtr->numWords;
sl@0:     if ((numWords != 2) && (numWords != 3)) {
sl@0: 	Tcl_ResetResult(interp);
sl@0: 	Tcl_AppendToObj(Tcl_GetObjResult(interp),
sl@0: 	        "wrong # args: should be \"set varName ?newValue?\"", -1);
sl@0:         return TCL_ERROR;
sl@0:     }
sl@0:     isAssignment = (numWords == 3);
sl@0: 
sl@0:     /*
sl@0:      * Decide if we can use a frame slot for the var/array name or if we
sl@0:      * need to emit code to compute and push the name at runtime. We use a
sl@0:      * frame slot (entry in the array of local vars) if we are compiling a
sl@0:      * procedure body and if the name is simple text that does not include
sl@0:      * namespace qualifiers. 
sl@0:      */
sl@0: 
sl@0:     varTokenPtr = parsePtr->tokenPtr
sl@0: 	    + (parsePtr->tokenPtr->numComponents + 1);
sl@0: 
sl@0:     code = TclPushVarName(interp, varTokenPtr, envPtr, TCL_CREATE_VAR,
sl@0: #ifndef TCL_TIP280
sl@0: 	    &localIndex, &simpleVarName, &isScalar);
sl@0: #else
sl@0: 	    &localIndex, &simpleVarName, &isScalar,
sl@0: 	    mapPtr->loc [eclIndex].line [1]);
sl@0: #endif
sl@0:     if (code != TCL_OK) {
sl@0: 	goto done;
sl@0:     }
sl@0: 
sl@0:     /*
sl@0:      * If we are doing an assignment, push the new value.
sl@0:      */
sl@0: 
sl@0:     if (isAssignment) {
sl@0: 	valueTokenPtr = varTokenPtr + (varTokenPtr->numComponents + 1);
sl@0: 	if (valueTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) {
sl@0: 	    TclEmitPush(TclRegisterNewLiteral(envPtr, valueTokenPtr[1].start,
sl@0: 		    valueTokenPtr[1].size), envPtr);
sl@0: 	} else {
sl@0: #ifdef TCL_TIP280
sl@0: 	    envPtr->line = mapPtr->loc [eclIndex].line [2];
sl@0: #endif
sl@0: 	    code = TclCompileTokens(interp, valueTokenPtr+1,
sl@0: 	            valueTokenPtr->numComponents, envPtr);
sl@0: 	    if (code != TCL_OK) {
sl@0: 		goto done;
sl@0: 	    }
sl@0: 	}
sl@0:     }
sl@0: 
sl@0:     /*
sl@0:      * Emit instructions to set/get the variable.
sl@0:      */
sl@0: 
sl@0:     if (simpleVarName) {
sl@0: 	if (isScalar) {
sl@0: 	    if (localIndex >= 0) {
sl@0: 		if (localIndex <= 255) {
sl@0: 		    TclEmitInstInt1((isAssignment?
sl@0: 		            INST_STORE_SCALAR1 : INST_LOAD_SCALAR1),
sl@0: 			    localIndex, envPtr);
sl@0: 		} else {
sl@0: 		    TclEmitInstInt4((isAssignment?
sl@0: 			    INST_STORE_SCALAR4 : INST_LOAD_SCALAR4),
sl@0: 			    localIndex, envPtr);
sl@0: 		}
sl@0: 	    } else {
sl@0: 		TclEmitOpcode((isAssignment?
sl@0: 		        INST_STORE_SCALAR_STK : INST_LOAD_SCALAR_STK), envPtr);
sl@0: 	    }
sl@0: 	} else {
sl@0: 	    if (localIndex >= 0) {
sl@0: 		if (localIndex <= 255) {
sl@0: 		    TclEmitInstInt1((isAssignment?
sl@0: 		            INST_STORE_ARRAY1 : INST_LOAD_ARRAY1),
sl@0: 			    localIndex, envPtr);
sl@0: 		} else {
sl@0: 		    TclEmitInstInt4((isAssignment?
sl@0: 			    INST_STORE_ARRAY4 : INST_LOAD_ARRAY4),
sl@0: 			    localIndex, envPtr);
sl@0: 		}
sl@0: 	    } else {
sl@0: 		TclEmitOpcode((isAssignment?
sl@0: 		        INST_STORE_ARRAY_STK : INST_LOAD_ARRAY_STK), envPtr);
sl@0: 	    }
sl@0: 	}
sl@0:     } else {
sl@0: 	TclEmitOpcode((isAssignment? INST_STORE_STK : INST_LOAD_STK), envPtr);
sl@0:     }
sl@0: 	
sl@0:     done:
sl@0:     return code;
sl@0: }
sl@0: 
sl@0: /*
sl@0:  *----------------------------------------------------------------------
sl@0:  *
sl@0:  * TclCompileStringCmd --
sl@0:  *
sl@0:  *	Procedure called to compile the "string" command.
sl@0:  *
sl@0:  * Results:
sl@0:  *	The return value is a standard Tcl result, which is TCL_OK if the
sl@0:  *	compilation was successful.  If the command cannot be byte-compiled,
sl@0:  *	TCL_OUT_LINE_COMPILE is returned.  If an error occurs then the
sl@0:  *	interpreter's result contains an error message, and TCL_ERROR is
sl@0:  *	returned.
sl@0:  *
sl@0:  * Side effects:
sl@0:  *	Instructions are added to envPtr to execute the "string" command
sl@0:  *	at runtime.
sl@0:  *
sl@0:  *----------------------------------------------------------------------
sl@0:  */
sl@0: 
sl@0: int
sl@0: TclCompileStringCmd(interp, parsePtr, envPtr)
sl@0:     Tcl_Interp *interp;		/* Used for error reporting. */
sl@0:     Tcl_Parse *parsePtr;	/* Points to a parse structure for the
sl@0: 				 * command created by Tcl_ParseCommand. */
sl@0:     CompileEnv *envPtr;		/* Holds resulting instructions. */
sl@0: {
sl@0:     Tcl_Token *opTokenPtr, *varTokenPtr;
sl@0:     Tcl_Obj *opObj;
sl@0:     int index;
sl@0:     int code;
sl@0:     
sl@0:     static CONST char *options[] = {
sl@0: 	"bytelength",	"compare",	"equal",	"first",
sl@0: 	"index",	"is",		"last",		"length",
sl@0: 	"map",		"match",	"range",	"repeat",
sl@0: 	"replace",	"tolower",	"toupper",	"totitle",
sl@0: 	"trim",		"trimleft",	"trimright",
sl@0: 	"wordend",	"wordstart",	(char *) NULL
sl@0:     };
sl@0:     enum options {
sl@0: 	STR_BYTELENGTH,	STR_COMPARE,	STR_EQUAL,	STR_FIRST,
sl@0: 	STR_INDEX,	STR_IS,		STR_LAST,	STR_LENGTH,
sl@0: 	STR_MAP,	STR_MATCH,	STR_RANGE,	STR_REPEAT,
sl@0: 	STR_REPLACE,	STR_TOLOWER,	STR_TOUPPER,	STR_TOTITLE,
sl@0: 	STR_TRIM,	STR_TRIMLEFT,	STR_TRIMRIGHT,
sl@0: 	STR_WORDEND,	STR_WORDSTART
sl@0:     };	  
sl@0: 
sl@0: #ifdef TCL_TIP280
sl@0:     /* TIP #280 : Remember the per-word line information of the current
sl@0:      * command. An index is used instead of a pointer as recursive compilation
sl@0:      * may reallocate, i.e. move, the array. This is also the reason to save
sl@0:      * the nuloc now, it may change during the course of the function.
sl@0:      */
sl@0:     ExtCmdLoc* mapPtr   = envPtr->extCmdMapPtr;
sl@0:     int        eclIndex = mapPtr->nuloc - 1;
sl@0: #endif
sl@0: 
sl@0:     if (parsePtr->numWords < 2) {
sl@0: 	/* Fail at run time, not in compilation */
sl@0: 	return TCL_OUT_LINE_COMPILE;
sl@0:     }
sl@0:     opTokenPtr = parsePtr->tokenPtr
sl@0: 	+ (parsePtr->tokenPtr->numComponents + 1);
sl@0: 
sl@0:     opObj = Tcl_NewStringObj(opTokenPtr->start, opTokenPtr->size);
sl@0:     if (Tcl_GetIndexFromObj(interp, opObj, options, "option", 0,
sl@0: 	    &index) != TCL_OK) {
sl@0: 	Tcl_DecrRefCount(opObj);
sl@0: 	Tcl_ResetResult(interp);
sl@0: 	return TCL_OUT_LINE_COMPILE;
sl@0:     }
sl@0:     Tcl_DecrRefCount(opObj);
sl@0: 
sl@0:     varTokenPtr = opTokenPtr + (opTokenPtr->numComponents + 1);
sl@0: 
sl@0:     switch ((enum options) index) {
sl@0: 	case STR_BYTELENGTH:
sl@0: 	case STR_FIRST:
sl@0: 	case STR_IS:
sl@0: 	case STR_LAST:
sl@0: 	case STR_MAP:
sl@0: 	case STR_RANGE:
sl@0: 	case STR_REPEAT:
sl@0: 	case STR_REPLACE:
sl@0: 	case STR_TOLOWER:
sl@0: 	case STR_TOUPPER:
sl@0: 	case STR_TOTITLE:
sl@0: 	case STR_TRIM:
sl@0: 	case STR_TRIMLEFT:
sl@0: 	case STR_TRIMRIGHT:
sl@0: 	case STR_WORDEND:
sl@0: 	case STR_WORDSTART:
sl@0: 	    /*
sl@0: 	     * All other cases: compile out of line.
sl@0: 	     */
sl@0: 	    return TCL_OUT_LINE_COMPILE;
sl@0: 
sl@0: 	case STR_COMPARE: 
sl@0: 	case STR_EQUAL: {
sl@0: 	    int i;
sl@0: 	    /*
sl@0: 	     * If there are any flags to the command, we can't byte compile it
sl@0: 	     * because the INST_STR_EQ bytecode doesn't support flags.
sl@0: 	     */
sl@0: 
sl@0: 	    if (parsePtr->numWords != 4) {
sl@0: 		return TCL_OUT_LINE_COMPILE;
sl@0: 	    }
sl@0: 
sl@0: 	    /*
sl@0: 	     * Push the two operands onto the stack.
sl@0: 	     */
sl@0: 
sl@0: 	    for (i = 0; i < 2; i++) {
sl@0: 		if (varTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) {
sl@0: 		    TclEmitPush(TclRegisterNewLiteral(envPtr,
sl@0: 			    varTokenPtr[1].start, varTokenPtr[1].size), envPtr);
sl@0: 		} else {
sl@0: #ifdef TCL_TIP280
sl@0: 		    envPtr->line = mapPtr->loc [eclIndex].line [i];
sl@0: #endif
sl@0: 		    code = TclCompileTokens(interp, varTokenPtr+1,
sl@0: 			    varTokenPtr->numComponents, envPtr);
sl@0: 		    if (code != TCL_OK) {
sl@0: 			return code;
sl@0: 		    }
sl@0: 		}
sl@0: 		varTokenPtr = varTokenPtr + (varTokenPtr->numComponents + 1);
sl@0: 	    }
sl@0: 
sl@0: 	    TclEmitOpcode(((((enum options) index) == STR_COMPARE) ?
sl@0: 		    INST_STR_CMP : INST_STR_EQ), envPtr);
sl@0: 	    return TCL_OK;
sl@0: 	}
sl@0: 	case STR_INDEX: {
sl@0: 	    int i;
sl@0: 
sl@0: 	    if (parsePtr->numWords != 4) {
sl@0: 		/* Fail at run time, not in compilation */
sl@0: 		return TCL_OUT_LINE_COMPILE;
sl@0: 	    }
sl@0: 
sl@0: 	    /*
sl@0: 	     * Push the two operands onto the stack.
sl@0: 	     */
sl@0: 
sl@0: 	    for (i = 0; i < 2; i++) {
sl@0: 		if (varTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) {
sl@0: 		    TclEmitPush(TclRegisterNewLiteral(envPtr,
sl@0: 			    varTokenPtr[1].start, varTokenPtr[1].size), envPtr);
sl@0: 		} else {
sl@0: #ifdef TCL_TIP280
sl@0: 		    envPtr->line = mapPtr->loc [eclIndex].line [i];
sl@0: #endif
sl@0: 		    code = TclCompileTokens(interp, varTokenPtr+1,
sl@0: 			    varTokenPtr->numComponents, envPtr);
sl@0: 		    if (code != TCL_OK) {
sl@0: 			return code;
sl@0: 		    }
sl@0: 		}
sl@0: 		varTokenPtr = varTokenPtr + (varTokenPtr->numComponents + 1);
sl@0: 	    }
sl@0: 
sl@0: 	    TclEmitOpcode(INST_STR_INDEX, envPtr);
sl@0: 	    return TCL_OK;
sl@0: 	}
sl@0: 	case STR_LENGTH: {
sl@0: 	    if (parsePtr->numWords != 3) {
sl@0: 		/* Fail at run time, not in compilation */
sl@0: 		return TCL_OUT_LINE_COMPILE;
sl@0: 	    }
sl@0: 
sl@0: 	    if (varTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) {
sl@0: 		/*
sl@0: 		 * Here someone is asking for the length of a static string.
sl@0: 		 * Just push the actual character (not byte) length.
sl@0: 		 */
sl@0: 		char buf[TCL_INTEGER_SPACE];
sl@0: 		int len = Tcl_NumUtfChars(varTokenPtr[1].start,
sl@0: 			varTokenPtr[1].size);
sl@0: 		len = sprintf(buf, "%d", len);
sl@0: 		TclEmitPush(TclRegisterNewLiteral(envPtr, buf, len), envPtr);
sl@0: 		return TCL_OK;
sl@0: 	    } else {
sl@0: #ifdef TCL_TIP280
sl@0: 	        envPtr->line = mapPtr->loc [eclIndex].line [2];
sl@0: #endif
sl@0: 		code = TclCompileTokens(interp, varTokenPtr+1,
sl@0: 			varTokenPtr->numComponents, envPtr);
sl@0: 		if (code != TCL_OK) {
sl@0: 		    return code;
sl@0: 		}
sl@0: 	    }
sl@0: 	    TclEmitOpcode(INST_STR_LEN, envPtr);
sl@0: 	    return TCL_OK;
sl@0: 	}
sl@0: 	case STR_MATCH: {
sl@0: 	    int i, length, exactMatch = 0, nocase = 0;
sl@0: 	    CONST char *str;
sl@0: 
sl@0: 	    if (parsePtr->numWords < 4 || parsePtr->numWords > 5) {
sl@0: 		/* Fail at run time, not in compilation */
sl@0: 		return TCL_OUT_LINE_COMPILE;
sl@0: 	    }
sl@0: 
sl@0: 	    if (parsePtr->numWords == 5) {
sl@0: 		if (varTokenPtr->type != TCL_TOKEN_SIMPLE_WORD) {
sl@0: 		    return TCL_OUT_LINE_COMPILE;
sl@0: 		}
sl@0: 		str    = varTokenPtr[1].start;
sl@0: 		length = varTokenPtr[1].size;
sl@0: 		if ((length > 1) &&
sl@0: 			strncmp(str, "-nocase", (size_t) length) == 0) {
sl@0: 		    nocase = 1;
sl@0: 		} else {
sl@0: 		    /* Fail at run time, not in compilation */
sl@0: 		    return TCL_OUT_LINE_COMPILE;
sl@0: 		}
sl@0: 		varTokenPtr = varTokenPtr + (varTokenPtr->numComponents + 1);
sl@0: 	    }
sl@0: 
sl@0: 	    for (i = 0; i < 2; i++) {
sl@0: 		if (varTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) {
sl@0: 		    str = varTokenPtr[1].start;
sl@0: 		    length = varTokenPtr[1].size;
sl@0: 		    if (!nocase && (i == 0)) {
sl@0: 			/*
sl@0: 			 * On the first (pattern) arg, check to see if any
sl@0: 			 * glob special characters are in the word '*[]?\\'.
sl@0: 			 * If not, this is the same as 'string equal'.  We
sl@0: 			 * can use strpbrk here because the glob chars are all
sl@0: 			 * in the ascii-7 range.  If -nocase was specified,
sl@0: 			 * we can't do this because INST_STR_EQ has no support
sl@0: 			 * for nocase.
sl@0: 			 */
sl@0: 			Tcl_Obj *copy = Tcl_NewStringObj(str, length);
sl@0: 			Tcl_IncrRefCount(copy);
sl@0: 			exactMatch = (strpbrk(Tcl_GetString(copy),
sl@0: 				"*[]?\\") == NULL);
sl@0: 			Tcl_DecrRefCount(copy);
sl@0: 		    }
sl@0: 		    TclEmitPush(
sl@0: 			    TclRegisterNewLiteral(envPtr, str, length), envPtr);
sl@0: 		} else {
sl@0: #ifdef TCL_TIP280
sl@0: 		    envPtr->line = mapPtr->loc [eclIndex].line [i];
sl@0: #endif
sl@0: 		    code = TclCompileTokens(interp, varTokenPtr+1,
sl@0: 			    varTokenPtr->numComponents, envPtr);
sl@0: 		    if (code != TCL_OK) {
sl@0: 			return code;
sl@0: 		    }
sl@0: 		}
sl@0: 		varTokenPtr = varTokenPtr + (varTokenPtr->numComponents + 1);
sl@0: 	    }
sl@0: 
sl@0: 	    if (exactMatch) {
sl@0: 		TclEmitOpcode(INST_STR_EQ, envPtr);
sl@0: 	    } else {
sl@0: 		TclEmitInstInt1(INST_STR_MATCH, nocase, envPtr);
sl@0: 	    }
sl@0: 	    return TCL_OK;
sl@0: 	}
sl@0:     }
sl@0: 
sl@0:     return TCL_OK;
sl@0: }
sl@0: 
sl@0: /*
sl@0:  *----------------------------------------------------------------------
sl@0:  *
sl@0:  * TclCompileVariableCmd --
sl@0:  *
sl@0:  *	Procedure called to reserve the local variables for the 
sl@0:  *      "variable" command. The command itself is *not* compiled.
sl@0:  *
sl@0:  * Results:
sl@0:  *      Always returns TCL_OUT_LINE_COMPILE.
sl@0:  *
sl@0:  * Side effects:
sl@0:  *      Indexed local variables are added to the environment.
sl@0:  *
sl@0:  *----------------------------------------------------------------------
sl@0:  */
sl@0: int
sl@0: TclCompileVariableCmd(interp, parsePtr, envPtr)
sl@0:     Tcl_Interp *interp;		/* Used for error reporting. */
sl@0:     Tcl_Parse *parsePtr;	/* Points to a parse structure for the
sl@0: 				 * command created by Tcl_ParseCommand. */
sl@0:     CompileEnv *envPtr;		/* Holds resulting instructions. */
sl@0: {
sl@0:     Tcl_Token *varTokenPtr;
sl@0:     int i, numWords;
sl@0:     CONST char *varName, *tail;
sl@0:     
sl@0:     if (envPtr->procPtr == NULL) {
sl@0: 	return TCL_OUT_LINE_COMPILE;
sl@0:     }
sl@0: 
sl@0:     numWords = parsePtr->numWords;
sl@0:     
sl@0:     varTokenPtr = parsePtr->tokenPtr
sl@0: 	+ (parsePtr->tokenPtr->numComponents + 1);
sl@0:     for (i = 1; i < numWords; i += 2) {
sl@0: 	if (varTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) {
sl@0: 	    varName = varTokenPtr[1].start;
sl@0: 	    tail = varName + varTokenPtr[1].size - 1;
sl@0: 	    if ((*tail == ')') || (tail < varName)) continue;
sl@0: 	    while ((tail > varName) && ((*tail != ':') || (*(tail-1) != ':'))) {
sl@0: 		tail--;
sl@0: 	    }
sl@0: 	    if ((*tail == ':') && (tail > varName)) {
sl@0: 		tail++;
sl@0: 	    }
sl@0: 	    (void) TclFindCompiledLocal(tail, (tail-varName+1),
sl@0: 		    /*create*/ 1, /*flags*/ 0, envPtr->procPtr);
sl@0: 	    varTokenPtr = varTokenPtr + (varTokenPtr->numComponents + 1);
sl@0: 	}
sl@0:     }
sl@0:     return TCL_OUT_LINE_COMPILE;
sl@0: }
sl@0: 
sl@0: /*
sl@0:  *----------------------------------------------------------------------
sl@0:  *
sl@0:  * TclCompileWhileCmd --
sl@0:  *
sl@0:  *	Procedure called to compile the "while" command.
sl@0:  *
sl@0:  * Results:
sl@0:  *	The return value is a standard Tcl result, which is TCL_OK if
sl@0:  *	compilation was successful. If an error occurs then the
sl@0:  *	interpreter's result contains a standard error message and TCL_ERROR
sl@0:  *	is returned. If compilation failed because the command is too
sl@0:  *	complex for TclCompileWhileCmd, TCL_OUT_LINE_COMPILE is returned
sl@0:  *	indicating that the while command should be compiled "out of line"
sl@0:  *	by emitting code to invoke its command procedure at runtime.
sl@0:  *
sl@0:  * Side effects:
sl@0:  *	Instructions are added to envPtr to execute the "while" command
sl@0:  *	at runtime.
sl@0:  *
sl@0:  *----------------------------------------------------------------------
sl@0:  */
sl@0: 
sl@0: int
sl@0: TclCompileWhileCmd(interp, parsePtr, envPtr)
sl@0:     Tcl_Interp *interp;		/* Used for error reporting. */
sl@0:     Tcl_Parse *parsePtr;	/* Points to a parse structure for the
sl@0: 				 * command created by Tcl_ParseCommand. */
sl@0:     CompileEnv *envPtr;		/* Holds resulting instructions. */
sl@0: {
sl@0:     Tcl_Token *testTokenPtr, *bodyTokenPtr;
sl@0:     JumpFixup jumpEvalCondFixup;
sl@0:     int testCodeOffset, bodyCodeOffset, jumpDist;
sl@0:     int range, code;
sl@0:     char buffer[32 + TCL_INTEGER_SPACE];
sl@0:     int savedStackDepth = envPtr->currStackDepth;
sl@0:     int loopMayEnd = 1;         /* This is set to 0 if it is recognized as
sl@0: 				 * an infinite loop. */
sl@0:     Tcl_Obj *boolObj;
sl@0:     int boolVal;
sl@0: 
sl@0: #ifdef TCL_TIP280
sl@0:     /* TIP #280 : Remember the per-word line information of the current
sl@0:      * command. An index is used instead of a pointer as recursive compilation
sl@0:      * may reallocate, i.e. move, the array. This is also the reason to save
sl@0:      * the nuloc now, it may change during the course of the function.
sl@0:      */
sl@0:     ExtCmdLoc* mapPtr   = envPtr->extCmdMapPtr;
sl@0:     int        eclIndex = mapPtr->nuloc - 1;
sl@0: #endif
sl@0: 
sl@0:     if (parsePtr->numWords != 3) {
sl@0: 	Tcl_ResetResult(interp);
sl@0: 	Tcl_AppendToObj(Tcl_GetObjResult(interp),
sl@0: 	        "wrong # args: should be \"while test command\"", -1);
sl@0: 	return TCL_ERROR;
sl@0:     }
sl@0: 
sl@0:     /*
sl@0:      * If the test expression requires substitutions, don't compile the
sl@0:      * while command inline. E.g., the expression might cause the loop to
sl@0:      * never execute or execute forever, as in "while "$x < 5" {}".
sl@0:      *
sl@0:      * Bail out also if the body expression requires substitutions
sl@0:      * in order to insure correct behaviour [Bug 219166]
sl@0:      */
sl@0: 
sl@0:     testTokenPtr = parsePtr->tokenPtr
sl@0: 	    + (parsePtr->tokenPtr->numComponents + 1);
sl@0:     bodyTokenPtr = testTokenPtr + (testTokenPtr->numComponents + 1);
sl@0:     if ((testTokenPtr->type != TCL_TOKEN_SIMPLE_WORD)
sl@0: 	    || (bodyTokenPtr->type != TCL_TOKEN_SIMPLE_WORD)) {
sl@0: 	return TCL_OUT_LINE_COMPILE;
sl@0:     }
sl@0: 
sl@0:     /*
sl@0:      * Find out if the condition is a constant. 
sl@0:      */
sl@0: 
sl@0:     boolObj = Tcl_NewStringObj(testTokenPtr[1].start, testTokenPtr[1].size);
sl@0:     Tcl_IncrRefCount(boolObj);
sl@0:     code = Tcl_GetBooleanFromObj(NULL, boolObj, &boolVal);
sl@0:     Tcl_DecrRefCount(boolObj);
sl@0:     if (code == TCL_OK) {
sl@0: 	if (boolVal) {
sl@0: 	    /*
sl@0: 	     * it is an infinite loop 
sl@0: 	     */
sl@0: 
sl@0: 	    loopMayEnd = 0;  
sl@0: 	} else {
sl@0: 	    /*
sl@0: 	     * This is an empty loop: "while 0 {...}" or such.
sl@0: 	     * Compile no bytecodes.
sl@0: 	     */
sl@0: 
sl@0: 	    goto pushResult;
sl@0: 	}
sl@0:     }
sl@0: 
sl@0:     /* 
sl@0:      * Create a ExceptionRange record for the loop body. This is used to
sl@0:      * implement break and continue.
sl@0:      */
sl@0: 
sl@0:     envPtr->exceptDepth++;
sl@0:     envPtr->maxExceptDepth =
sl@0: 	TclMax(envPtr->exceptDepth, envPtr->maxExceptDepth);
sl@0:     range = TclCreateExceptRange(LOOP_EXCEPTION_RANGE, envPtr);
sl@0: 
sl@0:     /*
sl@0:      * Jump to the evaluation of the condition. This code uses the "loop
sl@0:      * rotation" optimisation (which eliminates one branch from the loop).
sl@0:      * "while cond body" produces then:
sl@0:      *       goto A
sl@0:      *    B: body                : bodyCodeOffset
sl@0:      *    A: cond -> result      : testCodeOffset, continueOffset
sl@0:      *       if (result) goto B
sl@0:      *
sl@0:      * The infinite loop "while 1 body" produces:
sl@0:      *    B: body                : all three offsets here
sl@0:      *       goto B
sl@0:      */
sl@0: 
sl@0:     if (loopMayEnd) {
sl@0: 	TclEmitForwardJump(envPtr, TCL_UNCONDITIONAL_JUMP, &jumpEvalCondFixup);
sl@0: 	testCodeOffset = 0; /* avoid compiler warning */
sl@0:     } else {
sl@0: 	testCodeOffset = (envPtr->codeNext - envPtr->codeStart);
sl@0:     }
sl@0:     
sl@0: 
sl@0:     /*
sl@0:      * Compile the loop body.
sl@0:      */
sl@0: 
sl@0: #ifdef TCL_TIP280
sl@0:     envPtr->line = mapPtr->loc [eclIndex].line [2];
sl@0: #endif
sl@0:     bodyCodeOffset = (envPtr->codeNext - envPtr->codeStart);
sl@0:     code = TclCompileCmdWord(interp, bodyTokenPtr+1,
sl@0: 	    bodyTokenPtr->numComponents, envPtr);
sl@0:     envPtr->currStackDepth = savedStackDepth + 1;
sl@0:     if (code != TCL_OK) {
sl@0: 	if (code == TCL_ERROR) {
sl@0: 	    sprintf(buffer, "\n    (\"while\" body line %d)",
sl@0: 		    interp->errorLine);
sl@0:             Tcl_AddObjErrorInfo(interp, buffer, -1);
sl@0:         }
sl@0: 	goto error;
sl@0:     }
sl@0:     envPtr->exceptArrayPtr[range].numCodeBytes =
sl@0: 	    (envPtr->codeNext - envPtr->codeStart) - bodyCodeOffset;
sl@0:     TclEmitOpcode(INST_POP, envPtr);
sl@0: 
sl@0:     /*
sl@0:      * Compile the test expression then emit the conditional jump that
sl@0:      * terminates the while. We already know it's a simple word.
sl@0:      */
sl@0: 
sl@0:     if (loopMayEnd) {
sl@0: 	testCodeOffset = (envPtr->codeNext - envPtr->codeStart);
sl@0: 	jumpDist = testCodeOffset - jumpEvalCondFixup.codeOffset;
sl@0: 	if (TclFixupForwardJump(envPtr, &jumpEvalCondFixup, jumpDist, 127)) {
sl@0: 	    bodyCodeOffset += 3;
sl@0: 	    testCodeOffset += 3;
sl@0: 	}
sl@0: 	envPtr->currStackDepth = savedStackDepth;
sl@0: #ifdef TCL_TIP280
sl@0: 	envPtr->line = mapPtr->loc [eclIndex].line [1];
sl@0: #endif
sl@0: 	code = TclCompileExprWords(interp, testTokenPtr, 1, envPtr);
sl@0: 	if (code != TCL_OK) {
sl@0: 	    if (code == TCL_ERROR) {
sl@0: 		Tcl_AddObjErrorInfo(interp,
sl@0: 				    "\n    (\"while\" test expression)", -1);
sl@0: 	    }
sl@0: 	    goto error;
sl@0: 	}
sl@0: 	envPtr->currStackDepth = savedStackDepth + 1;
sl@0:     
sl@0: 	jumpDist = (envPtr->codeNext - envPtr->codeStart) - bodyCodeOffset;
sl@0: 	if (jumpDist > 127) {
sl@0: 	    TclEmitInstInt4(INST_JUMP_TRUE4, -jumpDist, envPtr);
sl@0: 	} else {
sl@0: 	    TclEmitInstInt1(INST_JUMP_TRUE1, -jumpDist, envPtr);
sl@0: 	}
sl@0:     } else {
sl@0: 	jumpDist = (envPtr->codeNext - envPtr->codeStart) - bodyCodeOffset;
sl@0: 	if (jumpDist > 127) {
sl@0: 	    TclEmitInstInt4(INST_JUMP4, -jumpDist, envPtr);
sl@0: 	} else {
sl@0: 	    TclEmitInstInt1(INST_JUMP1, -jumpDist, envPtr);
sl@0: 	}	
sl@0:     }
sl@0: 
sl@0: 
sl@0:     /*
sl@0:      * Set the loop's body, continue and break offsets.
sl@0:      */
sl@0: 
sl@0:     envPtr->exceptArrayPtr[range].continueOffset = testCodeOffset;
sl@0:     envPtr->exceptArrayPtr[range].codeOffset = bodyCodeOffset;
sl@0:     envPtr->exceptArrayPtr[range].breakOffset =
sl@0: 	    (envPtr->codeNext - envPtr->codeStart);
sl@0:     
sl@0:     /*
sl@0:      * The while command's result is an empty string.
sl@0:      */
sl@0: 
sl@0:     pushResult:
sl@0:     envPtr->currStackDepth = savedStackDepth;
sl@0:     TclEmitPush(TclRegisterNewLiteral(envPtr, "", 0), envPtr);
sl@0:     envPtr->exceptDepth--;
sl@0:     return TCL_OK;
sl@0: 
sl@0:     error:
sl@0:     envPtr->exceptDepth--;
sl@0:     return code;
sl@0: }
sl@0: 
sl@0: /*
sl@0:  *----------------------------------------------------------------------
sl@0:  *
sl@0:  * TclPushVarName --
sl@0:  *
sl@0:  *	Procedure used in the compiling where pushing a variable name
sl@0:  *	is necessary (append, lappend, set).
sl@0:  *
sl@0:  * Results:
sl@0:  *	The return value is a standard Tcl result, which is normally TCL_OK
sl@0:  *	unless there was an error while parsing string. If an error occurs
sl@0:  *	then the interpreter's result contains a standard error message.
sl@0:  *
sl@0:  * Side effects:
sl@0:  *	Instructions are added to envPtr to execute the "set" command
sl@0:  *	at runtime.
sl@0:  *
sl@0:  *----------------------------------------------------------------------
sl@0:  */
sl@0: 
sl@0: static int
sl@0: TclPushVarName(interp, varTokenPtr, envPtr, flags, localIndexPtr,
sl@0: #ifndef TCL_TIP280
sl@0: 	simpleVarNamePtr, isScalarPtr)
sl@0: #else
sl@0: 	simpleVarNamePtr, isScalarPtr, line)
sl@0: #endif
sl@0:     Tcl_Interp *interp;		/* Used for error reporting. */
sl@0:     Tcl_Token *varTokenPtr;	/* Points to a variable token. */
sl@0:     CompileEnv *envPtr;		/* Holds resulting instructions. */
sl@0:     int flags;			/* takes TCL_CREATE_VAR or
sl@0: 				 * TCL_NO_LARGE_INDEX */
sl@0:     int *localIndexPtr;		/* must not be NULL */
sl@0:     int *simpleVarNamePtr;	/* must not be NULL */
sl@0:     int *isScalarPtr;		/* must not be NULL */
sl@0: #ifdef TCL_TIP280
sl@0:     int line;                   /* line the token starts on */
sl@0: #endif
sl@0: {
sl@0:     register CONST char *p;
sl@0:     CONST char *name, *elName;
sl@0:     register int i, n;
sl@0:     int nameChars, elNameChars, simpleVarName, localIndex;
sl@0:     int code = TCL_OK;
sl@0: 
sl@0:     Tcl_Token *elemTokenPtr = NULL;
sl@0:     int elemTokenCount = 0;
sl@0:     int allocedTokens = 0;
sl@0:     int removedParen = 0;
sl@0: 
sl@0:     /*
sl@0:      * Decide if we can use a frame slot for the var/array name or if we
sl@0:      * need to emit code to compute and push the name at runtime. We use a
sl@0:      * frame slot (entry in the array of local vars) if we are compiling a
sl@0:      * procedure body and if the name is simple text that does not include
sl@0:      * namespace qualifiers. 
sl@0:      */
sl@0: 
sl@0:     simpleVarName = 0;
sl@0:     name = elName = NULL;
sl@0:     nameChars = elNameChars = 0;
sl@0:     localIndex = -1;
sl@0: 
sl@0:     /*
sl@0:      * Check not only that the type is TCL_TOKEN_SIMPLE_WORD, but whether
sl@0:      * curly braces surround the variable name.
sl@0:      * This really matters for array elements to handle things like
sl@0:      *    set {x($foo)} 5
sl@0:      * which raises an undefined var error if we are not careful here.
sl@0:      */
sl@0: 
sl@0:     if ((varTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) &&
sl@0: 	    (varTokenPtr->start[0] != '{')) {
sl@0: 	/*
sl@0: 	 * A simple variable name. Divide it up into "name" and "elName"
sl@0: 	 * strings. If it is not a local variable, look it up at runtime.
sl@0: 	 */
sl@0: 	simpleVarName = 1;
sl@0: 
sl@0: 	name = varTokenPtr[1].start;
sl@0: 	nameChars = varTokenPtr[1].size;
sl@0: 	if ( *(name + nameChars - 1) == ')') {
sl@0: 	    /* 
sl@0: 	     * last char is ')' => potential array reference.
sl@0: 	     */
sl@0: 
sl@0: 	    for (i = 0, p = name;  i < nameChars;  i++, p++) {
sl@0: 		if (*p == '(') {
sl@0: 		    elName = p + 1;
sl@0: 		    elNameChars = nameChars - i - 2;
sl@0: 		    nameChars = i ;
sl@0: 		    break;
sl@0: 		}
sl@0: 	    }
sl@0: 
sl@0: 	    if ((elName != NULL) && elNameChars) {
sl@0: 		/*
sl@0: 		 * An array element, the element name is a simple
sl@0: 		 * string: assemble the corresponding token.
sl@0: 		 */
sl@0: 
sl@0: 		elemTokenPtr = (Tcl_Token *) ckalloc(sizeof(Tcl_Token));
sl@0: 		allocedTokens = 1;
sl@0: 		elemTokenPtr->type = TCL_TOKEN_TEXT;
sl@0: 		elemTokenPtr->start = elName;
sl@0: 		elemTokenPtr->size = elNameChars;
sl@0: 		elemTokenPtr->numComponents = 0;
sl@0: 		elemTokenCount = 1;
sl@0: 	    }
sl@0: 	}
sl@0:     } else if (((n = varTokenPtr->numComponents) > 1)
sl@0: 	    && (varTokenPtr[1].type == TCL_TOKEN_TEXT)
sl@0:             && (varTokenPtr[n].type == TCL_TOKEN_TEXT)
sl@0:             && (varTokenPtr[n].start[varTokenPtr[n].size - 1] == ')')) {
sl@0: 
sl@0:         /*
sl@0: 	 * Check for parentheses inside first token
sl@0: 	 */
sl@0: 
sl@0:         simpleVarName = 0;
sl@0:         for (i = 0, p = varTokenPtr[1].start; 
sl@0: 	     i < varTokenPtr[1].size; i++, p++) {
sl@0:             if (*p == '(') {
sl@0:                 simpleVarName = 1;
sl@0:                 break;
sl@0:             }
sl@0:         }
sl@0:         if (simpleVarName) {
sl@0: 	    int remainingChars;
sl@0: 
sl@0: 	    /*
sl@0: 	     * Check the last token: if it is just ')', do not count
sl@0: 	     * it. Otherwise, remove the ')' and flag so that it is
sl@0: 	     * restored at the end.
sl@0: 	     */
sl@0: 
sl@0: 	    if (varTokenPtr[n].size == 1) {
sl@0: 		--n;
sl@0: 	    } else {
sl@0: 		--varTokenPtr[n].size;
sl@0: 		removedParen = n;
sl@0: 	    }
sl@0: 
sl@0:             name = varTokenPtr[1].start;
sl@0:             nameChars = p - varTokenPtr[1].start;
sl@0:             elName = p + 1;
sl@0:             remainingChars = (varTokenPtr[2].start - p) - 1;
sl@0:             elNameChars = (varTokenPtr[n].start - p) + varTokenPtr[n].size - 2;
sl@0: 
sl@0: 	    if (remainingChars) {
sl@0: 		/*
sl@0: 		 * Make a first token with the extra characters in the first 
sl@0: 		 * token.
sl@0: 		 */
sl@0: 
sl@0: 		elemTokenPtr = (Tcl_Token *) ckalloc(n * sizeof(Tcl_Token));
sl@0: 		allocedTokens = 1;
sl@0: 		elemTokenPtr->type = TCL_TOKEN_TEXT;
sl@0: 		elemTokenPtr->start = elName;
sl@0: 		elemTokenPtr->size = remainingChars;
sl@0: 		elemTokenPtr->numComponents = 0;
sl@0: 		elemTokenCount = n;
sl@0: 		
sl@0: 		/*
sl@0: 		 * Copy the remaining tokens.
sl@0: 		 */
sl@0: 		
sl@0: 		memcpy((void *) (elemTokenPtr+1), (void *) (&varTokenPtr[2]),
sl@0: 		       ((n-1) * sizeof(Tcl_Token)));
sl@0: 	    } else {
sl@0: 		/*
sl@0: 		 * Use the already available tokens.
sl@0: 		 */
sl@0: 		
sl@0: 		elemTokenPtr = &varTokenPtr[2];
sl@0: 		elemTokenCount = n - 1;	    
sl@0: 	    }
sl@0: 	}
sl@0:     }
sl@0: 
sl@0:     if (simpleVarName) {
sl@0: 	/*
sl@0: 	 * See whether name has any namespace separators (::'s).
sl@0: 	 */
sl@0: 
sl@0: 	int hasNsQualifiers = 0;
sl@0: 	for (i = 0, p = name;  i < nameChars;  i++, p++) {
sl@0: 	    if ((*p == ':') && ((i+1) < nameChars) && (*(p+1) == ':')) {
sl@0: 		hasNsQualifiers = 1;
sl@0: 		break;
sl@0: 	    }
sl@0: 	}
sl@0: 
sl@0: 	/*
sl@0: 	 * Look up the var name's index in the array of local vars in the
sl@0: 	 * proc frame. If retrieving the var's value and it doesn't already
sl@0: 	 * exist, push its name and look it up at runtime.
sl@0: 	 */
sl@0: 
sl@0: 	if ((envPtr->procPtr != NULL) && !hasNsQualifiers) {
sl@0: 	    localIndex = TclFindCompiledLocal(name, nameChars,
sl@0: 		    /*create*/ (flags & TCL_CREATE_VAR),
sl@0:                     /*flags*/ ((elName==NULL)? VAR_SCALAR : VAR_ARRAY),
sl@0: 		    envPtr->procPtr);
sl@0: 	    if ((flags & TCL_NO_LARGE_INDEX) && (localIndex > 255)) {
sl@0: 		/* we'll push the name */
sl@0: 		localIndex = -1;
sl@0: 	    }
sl@0: 	}
sl@0: 	if (localIndex < 0) {
sl@0: 	    TclEmitPush(TclRegisterNewLiteral(envPtr, name, nameChars), envPtr);
sl@0: 	}
sl@0: 
sl@0: 	/*
sl@0: 	 * Compile the element script, if any.
sl@0: 	 */
sl@0: 
sl@0: 	if (elName != NULL) {
sl@0: 	    if (elNameChars) {
sl@0: #ifdef TCL_TIP280
sl@0: 	        envPtr->line = line;
sl@0: #endif
sl@0: 		code = TclCompileTokens(interp, elemTokenPtr,
sl@0:                         elemTokenCount, envPtr);
sl@0: 		if (code != TCL_OK) {
sl@0: 		    goto done;
sl@0: 		}
sl@0: 	    } else {
sl@0: 		TclEmitPush(TclRegisterNewLiteral(envPtr, "", 0), envPtr);
sl@0: 	    }
sl@0: 	}
sl@0:     } else {
sl@0: 	/*
sl@0: 	 * The var name isn't simple: compile and push it.
sl@0: 	 */
sl@0: 
sl@0: #ifdef TCL_TIP280
sl@0:         envPtr->line = line;
sl@0: #endif
sl@0: 	code = TclCompileTokens(interp, varTokenPtr+1,
sl@0: 		varTokenPtr->numComponents, envPtr);
sl@0: 	if (code != TCL_OK) {
sl@0: 	    goto done;
sl@0: 	}
sl@0:     }
sl@0: 
sl@0:     done:
sl@0:     if (removedParen) {
sl@0: 	++varTokenPtr[removedParen].size;
sl@0:     }
sl@0:     if (allocedTokens) {
sl@0:         ckfree((char *) elemTokenPtr);
sl@0:     }
sl@0:     *localIndexPtr	= localIndex;
sl@0:     *simpleVarNamePtr	= simpleVarName;
sl@0:     *isScalarPtr	= (elName == NULL);
sl@0:     return code;
sl@0: }
sl@0: 
sl@0: /*
sl@0:  * Local Variables:
sl@0:  * mode: c
sl@0:  * c-basic-offset: 4
sl@0:  * fill-column: 78
sl@0:  * End:
sl@0:  */
sl@0: