OSDN Git Service

Please enter the commit message for your changes. Lines starting
[eos/base.git] / util / src / TclTk / tcl8.6.12 / generic / tclCompCmdsSZ.c
diff --git a/util/src/TclTk/tcl8.6.12/generic/tclCompCmdsSZ.c b/util/src/TclTk/tcl8.6.12/generic/tclCompCmdsSZ.c
new file mode 100644 (file)
index 0000000..ddfe0dc
--- /dev/null
@@ -0,0 +1,4532 @@
+/*
+ * tclCompCmdsSZ.c --
+ *
+ *     This file contains compilation procedures that compile various Tcl
+ *     commands (beginning with the letters 's' through 'z', except for
+ *     [upvar] and [variable]) into a sequence of instructions ("bytecodes").
+ *     Also includes the operator command compilers.
+ *
+ * Copyright (c) 1997-1998 Sun Microsystems, Inc.
+ * Copyright (c) 2001 by Kevin B. Kenny.  All rights reserved.
+ * Copyright (c) 2002 ActiveState Corporation.
+ * Copyright (c) 2004-2010 by Donal K. Fellows.
+ *
+ * See the file "license.terms" for information on usage and redistribution of
+ * this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ */
+
+#include "tclInt.h"
+#include "tclCompile.h"
+#include "tclStringTrim.h"
+
+/*
+ * Prototypes for procedures defined later in this file:
+ */
+
+static ClientData      DupJumptableInfo(ClientData clientData);
+static void            FreeJumptableInfo(ClientData clientData);
+static void            PrintJumptableInfo(ClientData clientData,
+                           Tcl_Obj *appendObj, ByteCode *codePtr,
+                           unsigned int pcOffset);
+static void            DisassembleJumptableInfo(ClientData clientData,
+                           Tcl_Obj *dictObj, ByteCode *codePtr,
+                           unsigned int pcOffset);
+static int             CompileAssociativeBinaryOpCmd(Tcl_Interp *interp,
+                           Tcl_Parse *parsePtr, const char *identity,
+                           int instruction, CompileEnv *envPtr);
+static int             CompileComparisonOpCmd(Tcl_Interp *interp,
+                           Tcl_Parse *parsePtr, int instruction,
+                           CompileEnv *envPtr);
+static int             CompileStrictlyBinaryOpCmd(Tcl_Interp *interp,
+                           Tcl_Parse *parsePtr, int instruction,
+                           CompileEnv *envPtr);
+static int             CompileUnaryOpCmd(Tcl_Interp *interp,
+                           Tcl_Parse *parsePtr, int instruction,
+                           CompileEnv *envPtr);
+static void            IssueSwitchChainedTests(Tcl_Interp *interp,
+                           CompileEnv *envPtr, int mode, int noCase,
+                           int valueIndex, int numWords,
+                           Tcl_Token **bodyToken, int *bodyLines,
+                           int **bodyNext);
+static void            IssueSwitchJumpTable(Tcl_Interp *interp,
+                           CompileEnv *envPtr, int valueIndex,
+                           int numWords, Tcl_Token **bodyToken,
+                           int *bodyLines, int **bodyContLines);
+static int             IssueTryClausesInstructions(Tcl_Interp *interp,
+                           CompileEnv *envPtr, Tcl_Token *bodyToken,
+                           int numHandlers, int *matchCodes,
+                           Tcl_Obj **matchClauses, int *resultVarIndices,
+                           int *optionVarIndices, Tcl_Token **handlerTokens);
+static int             IssueTryClausesFinallyInstructions(Tcl_Interp *interp,
+                           CompileEnv *envPtr, Tcl_Token *bodyToken,
+                           int numHandlers, int *matchCodes,
+                           Tcl_Obj **matchClauses, int *resultVarIndices,
+                           int *optionVarIndices, Tcl_Token **handlerTokens,
+                           Tcl_Token *finallyToken);
+static int             IssueTryFinallyInstructions(Tcl_Interp *interp,
+                           CompileEnv *envPtr, Tcl_Token *bodyToken,
+                           Tcl_Token *finallyToken);
+
+/*
+ * The structures below define the AuxData types defined in this file.
+ */
+
+const AuxDataType tclJumptableInfoType = {
+    "JumptableInfo",           /* name */
+    DupJumptableInfo,          /* dupProc */
+    FreeJumptableInfo,         /* freeProc */
+    PrintJumptableInfo,                /* printProc */
+    DisassembleJumptableInfo   /* disassembleProc */
+};
+
+/*
+ * Shorthand macros for instruction issuing.
+ */
+
+#define OP(name)       TclEmitOpcode(INST_##name, envPtr)
+#define OP1(name,val)  TclEmitInstInt1(INST_##name,(val),envPtr)
+#define OP4(name,val)  TclEmitInstInt4(INST_##name,(val),envPtr)
+#define OP14(name,val1,val2) \
+    TclEmitInstInt1(INST_##name,(val1),envPtr);TclEmitInt4((val2),envPtr)
+#define OP44(name,val1,val2) \
+    TclEmitInstInt4(INST_##name,(val1),envPtr);TclEmitInt4((val2),envPtr)
+#define PUSH(str) \
+    PushStringLiteral(envPtr, str)
+#define JUMP4(name,var) \
+    (var) = CurrentOffset(envPtr);TclEmitInstInt4(INST_##name##4,0,envPtr)
+#define FIXJUMP4(var) \
+    TclStoreInt4AtPtr(CurrentOffset(envPtr)-(var),envPtr->codeStart+(var)+1)
+#define JUMP1(name,var) \
+    (var) = CurrentOffset(envPtr);TclEmitInstInt1(INST_##name##1,0,envPtr)
+#define FIXJUMP1(var) \
+    TclStoreInt1AtPtr(CurrentOffset(envPtr)-(var),envPtr->codeStart+(var)+1)
+#define LOAD(idx) \
+    if ((idx)<256) {OP1(LOAD_SCALAR1,(idx));} else {OP4(LOAD_SCALAR4,(idx));}
+#define STORE(idx) \
+    if ((idx)<256) {OP1(STORE_SCALAR1,(idx));} else {OP4(STORE_SCALAR4,(idx));}
+#define INVOKE(name) \
+    TclEmitInvoke(envPtr,INST_##name)
+
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclCompileSetCmd --
+ *
+ *     Procedure called to compile the "set" command.
+ *
+ * Results:
+ *     Returns TCL_OK for a successful compile. Returns TCL_ERROR to defer
+ *     evaluation to runtime.
+ *
+ * Side effects:
+ *     Instructions are added to envPtr to execute the "set" command at
+ *     runtime.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TclCompileSetCmd(
+    Tcl_Interp *interp,                /* Used for error reporting. */
+    Tcl_Parse *parsePtr,       /* Points to a parse structure for the command
+                                * created by Tcl_ParseCommand. */
+    Command *cmdPtr,           /* Points to defintion of command being
+                                * compiled. */
+    CompileEnv *envPtr)                /* Holds resulting instructions. */
+{
+    DefineLineInformation;     /* TIP #280 */
+    Tcl_Token *varTokenPtr, *valueTokenPtr;
+    int isAssignment, isScalar, localIndex, numWords;
+
+    numWords = parsePtr->numWords;
+    if ((numWords != 2) && (numWords != 3)) {
+       return TCL_ERROR;
+    }
+    isAssignment = (numWords == 3);
+
+    /*
+     * Decide if we can use a frame slot for the var/array name or if we need
+     * to emit code to compute and push the name at runtime. We use a frame
+     * slot (entry in the array of local vars) if we are compiling a procedure
+     * body and if the name is simple text that does not include namespace
+     * qualifiers.
+     */
+
+    varTokenPtr = TokenAfter(parsePtr->tokenPtr);
+    PushVarNameWord(interp, varTokenPtr, envPtr, 0,
+           &localIndex, &isScalar, 1);
+
+    /*
+     * If we are doing an assignment, push the new value.
+     */
+
+    if (isAssignment) {
+       valueTokenPtr = TokenAfter(varTokenPtr);
+       CompileWord(envPtr, valueTokenPtr, interp, 2);
+    }
+
+    /*
+     * Emit instructions to set/get the variable.
+     */
+
+       if (isScalar) {
+           if (localIndex < 0) {
+               TclEmitOpcode((isAssignment?
+                       INST_STORE_STK : INST_LOAD_STK), envPtr);
+           } else if (localIndex <= 255) {
+               TclEmitInstInt1((isAssignment?
+                       INST_STORE_SCALAR1 : INST_LOAD_SCALAR1),
+                       localIndex, envPtr);
+           } else {
+               TclEmitInstInt4((isAssignment?
+                       INST_STORE_SCALAR4 : INST_LOAD_SCALAR4),
+                       localIndex, envPtr);
+           }
+       } else {
+           if (localIndex < 0) {
+               TclEmitOpcode((isAssignment?
+                       INST_STORE_ARRAY_STK : INST_LOAD_ARRAY_STK), envPtr);
+           } else if (localIndex <= 255) {
+               TclEmitInstInt1((isAssignment?
+                       INST_STORE_ARRAY1 : INST_LOAD_ARRAY1),
+                       localIndex, envPtr);
+           } else {
+               TclEmitInstInt4((isAssignment?
+                       INST_STORE_ARRAY4 : INST_LOAD_ARRAY4),
+                       localIndex, envPtr);
+           }
+       }
+
+    return TCL_OK;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclCompileString*Cmd --
+ *
+ *     Procedures called to compile various subcommands of the "string"
+ *     command.
+ *
+ * Results:
+ *     Returns TCL_OK for a successful compile. Returns TCL_ERROR to defer
+ *     evaluation to runtime.
+ *
+ * Side effects:
+ *     Instructions are added to envPtr to execute the "string" command at
+ *     runtime.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TclCompileStringCatCmd(
+    Tcl_Interp *interp,                /* Used for error reporting. */
+    Tcl_Parse *parsePtr,       /* Points to a parse structure for the command
+                                * created by Tcl_ParseCommand. */
+    Command *cmdPtr,           /* Points to defintion of command being
+                                * compiled. */
+    CompileEnv *envPtr)                /* Holds resulting instructions. */
+{
+    DefineLineInformation;     /* TIP #280 */
+    int i, numWords = parsePtr->numWords, numArgs;
+    Tcl_Token *wordTokenPtr;
+    Tcl_Obj *obj, *folded;
+
+    /* Trivial case, no arg */
+
+    if (numWords<2) {
+       PushStringLiteral(envPtr, "");
+       return TCL_OK;
+    }
+
+    /* General case: issue CONCAT1's (by chunks of 254 if needed), folding
+       contiguous constants along the way */
+
+    numArgs = 0;
+    folded = NULL;
+    wordTokenPtr = TokenAfter(parsePtr->tokenPtr);
+    for (i = 1; i < numWords; i++) {
+       obj = Tcl_NewObj();
+       if (TclWordKnownAtCompileTime(wordTokenPtr, obj)) {
+           if (folded) {
+               Tcl_AppendObjToObj(folded, obj);
+               Tcl_DecrRefCount(obj);
+           } else {
+               folded = obj;
+           }
+       } else {
+           Tcl_DecrRefCount(obj);
+           if (folded) {
+               int len;
+               const char *bytes = Tcl_GetStringFromObj(folded, &len);
+
+               PushLiteral(envPtr, bytes, len);
+               Tcl_DecrRefCount(folded);
+               folded = NULL;
+               numArgs ++;
+           }
+           CompileWord(envPtr, wordTokenPtr, interp, i);
+           numArgs ++;
+           if (numArgs >= 254) { /* 254 to take care of the possible +1 of "folded" above */
+               TclEmitInstInt1(INST_STR_CONCAT1, numArgs, envPtr);
+               numArgs = 1;    /* concat pushes 1 obj, the result */
+           }
+       }
+       wordTokenPtr = TokenAfter(wordTokenPtr);
+    }
+    if (folded) {
+       int len;
+       const char *bytes = Tcl_GetStringFromObj(folded, &len);
+
+       PushLiteral(envPtr, bytes, len);
+       Tcl_DecrRefCount(folded);
+       folded = NULL;
+       numArgs ++;
+    }
+    if (numArgs > 1) {
+       TclEmitInstInt1(INST_STR_CONCAT1, numArgs, envPtr);
+    }
+
+    return TCL_OK;
+}
+
+int
+TclCompileStringCmpCmd(
+    Tcl_Interp *interp,                /* Used for error reporting. */
+    Tcl_Parse *parsePtr,       /* Points to a parse structure for the command
+                                * created by Tcl_ParseCommand. */
+    Command *cmdPtr,           /* Points to defintion of command being
+                                * compiled. */
+    CompileEnv *envPtr)                /* Holds resulting instructions. */
+{
+    DefineLineInformation;     /* TIP #280 */
+    Tcl_Token *tokenPtr;
+
+    /*
+     * We don't support any flags; the bytecode isn't that sophisticated.
+     */
+
+    if (parsePtr->numWords != 3) {
+       return TCL_ERROR;
+    }
+
+    /*
+     * Push the two operands onto the stack and then the test.
+     */
+
+    tokenPtr = TokenAfter(parsePtr->tokenPtr);
+    CompileWord(envPtr, tokenPtr, interp, 1);
+    tokenPtr = TokenAfter(tokenPtr);
+    CompileWord(envPtr, tokenPtr, interp, 2);
+    TclEmitOpcode(INST_STR_CMP, envPtr);
+    return TCL_OK;
+}
+
+int
+TclCompileStringEqualCmd(
+    Tcl_Interp *interp,                /* Used for error reporting. */
+    Tcl_Parse *parsePtr,       /* Points to a parse structure for the command
+                                * created by Tcl_ParseCommand. */
+    Command *cmdPtr,           /* Points to defintion of command being
+                                * compiled. */
+    CompileEnv *envPtr)                /* Holds resulting instructions. */
+{
+    DefineLineInformation;     /* TIP #280 */
+    Tcl_Token *tokenPtr;
+
+    /*
+     * We don't support any flags; the bytecode isn't that sophisticated.
+     */
+
+    if (parsePtr->numWords != 3) {
+       return TCL_ERROR;
+    }
+
+    /*
+     * Push the two operands onto the stack and then the test.
+     */
+
+    tokenPtr = TokenAfter(parsePtr->tokenPtr);
+    CompileWord(envPtr, tokenPtr, interp, 1);
+    tokenPtr = TokenAfter(tokenPtr);
+    CompileWord(envPtr, tokenPtr, interp, 2);
+    TclEmitOpcode(INST_STR_EQ, envPtr);
+    return TCL_OK;
+}
+
+int
+TclCompileStringFirstCmd(
+    Tcl_Interp *interp,                /* Used for error reporting. */
+    Tcl_Parse *parsePtr,       /* Points to a parse structure for the command
+                                * created by Tcl_ParseCommand. */
+    Command *cmdPtr,           /* Points to defintion of command being
+                                * compiled. */
+    CompileEnv *envPtr)                /* Holds resulting instructions. */
+{
+    DefineLineInformation;     /* TIP #280 */
+    Tcl_Token *tokenPtr;
+
+    /*
+     * We don't support any flags; the bytecode isn't that sophisticated.
+     */
+
+    if (parsePtr->numWords != 3) {
+       return TCL_ERROR;
+    }
+
+    /*
+     * Push the two operands onto the stack and then the test.
+     */
+
+    tokenPtr = TokenAfter(parsePtr->tokenPtr);
+    CompileWord(envPtr, tokenPtr, interp, 1);
+    tokenPtr = TokenAfter(tokenPtr);
+    CompileWord(envPtr, tokenPtr, interp, 2);
+    OP(STR_FIND);
+    return TCL_OK;
+}
+
+int
+TclCompileStringLastCmd(
+    Tcl_Interp *interp,                /* Used for error reporting. */
+    Tcl_Parse *parsePtr,       /* Points to a parse structure for the command
+                                * created by Tcl_ParseCommand. */
+    Command *cmdPtr,           /* Points to defintion of command being
+                                * compiled. */
+    CompileEnv *envPtr)                /* Holds resulting instructions. */
+{
+    DefineLineInformation;     /* TIP #280 */
+    Tcl_Token *tokenPtr;
+
+    /*
+     * We don't support any flags; the bytecode isn't that sophisticated.
+     */
+
+    if (parsePtr->numWords != 3) {
+       return TCL_ERROR;
+    }
+
+    /*
+     * Push the two operands onto the stack and then the test.
+     */
+
+    tokenPtr = TokenAfter(parsePtr->tokenPtr);
+    CompileWord(envPtr, tokenPtr, interp, 1);
+    tokenPtr = TokenAfter(tokenPtr);
+    CompileWord(envPtr, tokenPtr, interp, 2);
+    OP(STR_FIND_LAST);
+    return TCL_OK;
+}
+
+int
+TclCompileStringIndexCmd(
+    Tcl_Interp *interp,                /* Used for error reporting. */
+    Tcl_Parse *parsePtr,       /* Points to a parse structure for the command
+                                * created by Tcl_ParseCommand. */
+    Command *cmdPtr,           /* Points to defintion of command being
+                                * compiled. */
+    CompileEnv *envPtr)                /* Holds resulting instructions. */
+{
+    DefineLineInformation;     /* TIP #280 */
+    Tcl_Token *tokenPtr;
+
+    if (parsePtr->numWords != 3) {
+       return TCL_ERROR;
+    }
+
+    /*
+     * Push the two operands onto the stack and then the index operation.
+     */
+
+    tokenPtr = TokenAfter(parsePtr->tokenPtr);
+    CompileWord(envPtr, tokenPtr, interp, 1);
+    tokenPtr = TokenAfter(tokenPtr);
+    CompileWord(envPtr, tokenPtr, interp, 2);
+    TclEmitOpcode(INST_STR_INDEX, envPtr);
+    return TCL_OK;
+}
+
+int
+TclCompileStringIsCmd(
+    Tcl_Interp *interp,                /* Used for error reporting. */
+    Tcl_Parse *parsePtr,       /* Points to a parse structure for the command
+                                * created by Tcl_ParseCommand. */
+    Command *cmdPtr,           /* Points to defintion of command being
+                                * compiled. */
+    CompileEnv *envPtr)                /* Holds resulting instructions. */
+{
+    DefineLineInformation;     /* TIP #280 */
+    Tcl_Token *tokenPtr = TokenAfter(parsePtr->tokenPtr);
+    static const char *const isClasses[] = {
+       "alnum",        "alpha",        "ascii",        "control",
+       "boolean",      "digit",        "double",       "entier",
+       "false",        "graph",        "integer",      "list",
+       "lower",        "print",        "punct",        "space",
+       "true",         "upper",        "wideinteger",  "wordchar",
+       "xdigit",       NULL
+    };
+    enum isClasses {
+       STR_IS_ALNUM,   STR_IS_ALPHA,   STR_IS_ASCII,   STR_IS_CONTROL,
+       STR_IS_BOOL,    STR_IS_DIGIT,   STR_IS_DOUBLE,  STR_IS_ENTIER,
+       STR_IS_FALSE,   STR_IS_GRAPH,   STR_IS_INT,     STR_IS_LIST,
+       STR_IS_LOWER,   STR_IS_PRINT,   STR_IS_PUNCT,   STR_IS_SPACE,
+       STR_IS_TRUE,    STR_IS_UPPER,   STR_IS_WIDE,    STR_IS_WORD,
+       STR_IS_XDIGIT
+    };
+    int t, range, allowEmpty = 0, end;
+    InstStringClassType strClassType;
+    Tcl_Obj *isClass;
+
+    if (parsePtr->numWords < 3 || parsePtr->numWords > 6) {
+       return TCL_ERROR;
+    }
+    isClass = Tcl_NewObj();
+    if (!TclWordKnownAtCompileTime(tokenPtr, isClass)) {
+       Tcl_DecrRefCount(isClass);
+       return TCL_ERROR;
+    } else if (Tcl_GetIndexFromObj(interp, isClass, isClasses, "class", 0,
+           &t) != TCL_OK) {
+       Tcl_DecrRefCount(isClass);
+       TclCompileSyntaxError(interp, envPtr);
+       return TCL_OK;
+    }
+    Tcl_DecrRefCount(isClass);
+
+#define GotLiteral(tokenPtr, word) \
+    ((tokenPtr)->type == TCL_TOKEN_SIMPLE_WORD &&                      \
+     (tokenPtr)[1].size > 1 &&                                         \
+     (tokenPtr)[1].start[0] == word[0] &&                              \
+     strncmp((tokenPtr)[1].start, (word), (tokenPtr)[1].size) == 0)
+
+    /*
+     * Cannot handle the -failindex option at all, and that's the only legal
+     * way to have more than 4 arguments.
+     */
+
+    if (parsePtr->numWords != 3 && parsePtr->numWords != 4) {
+       return TCL_ERROR;
+    }
+
+    tokenPtr = TokenAfter(tokenPtr);
+    if (parsePtr->numWords == 3) {
+       allowEmpty = 1;
+    } else {
+       if (!GotLiteral(tokenPtr, "-strict")) {
+           return TCL_ERROR;
+       }
+       tokenPtr = TokenAfter(tokenPtr);
+    }
+#undef GotLiteral
+
+    /*
+     * Compile the code. There are several main classes of check here.
+     * 1. Character classes
+     * 2. Booleans
+     * 3. Integers
+     * 4. Floats
+     * 5. Lists
+     */
+
+    CompileWord(envPtr, tokenPtr, interp, parsePtr->numWords-1);
+
+    switch ((enum isClasses) t) {
+    case STR_IS_ALNUM:
+       strClassType = STR_CLASS_ALNUM;
+       goto compileStrClass;
+    case STR_IS_ALPHA:
+       strClassType = STR_CLASS_ALPHA;
+       goto compileStrClass;
+    case STR_IS_ASCII:
+       strClassType = STR_CLASS_ASCII;
+       goto compileStrClass;
+    case STR_IS_CONTROL:
+       strClassType = STR_CLASS_CONTROL;
+       goto compileStrClass;
+    case STR_IS_DIGIT:
+       strClassType = STR_CLASS_DIGIT;
+       goto compileStrClass;
+    case STR_IS_GRAPH:
+       strClassType = STR_CLASS_GRAPH;
+       goto compileStrClass;
+    case STR_IS_LOWER:
+       strClassType = STR_CLASS_LOWER;
+       goto compileStrClass;
+    case STR_IS_PRINT:
+       strClassType = STR_CLASS_PRINT;
+       goto compileStrClass;
+    case STR_IS_PUNCT:
+       strClassType = STR_CLASS_PUNCT;
+       goto compileStrClass;
+    case STR_IS_SPACE:
+       strClassType = STR_CLASS_SPACE;
+       goto compileStrClass;
+    case STR_IS_UPPER:
+       strClassType = STR_CLASS_UPPER;
+       goto compileStrClass;
+    case STR_IS_WORD:
+       strClassType = STR_CLASS_WORD;
+       goto compileStrClass;
+    case STR_IS_XDIGIT:
+       strClassType = STR_CLASS_XDIGIT;
+    compileStrClass:
+       if (allowEmpty) {
+           OP1(        STR_CLASS, strClassType);
+       } else {
+           int over, over2;
+
+           OP(         DUP);
+           OP1(        STR_CLASS, strClassType);
+           JUMP1(      JUMP_TRUE, over);
+           OP(         POP);
+           PUSH(       "0");
+           JUMP1(      JUMP, over2);
+           FIXJUMP1(over);
+           PUSH(       "");
+           OP(         STR_NEQ);
+           FIXJUMP1(over2);
+       }
+       return TCL_OK;
+
+    case STR_IS_BOOL:
+    case STR_IS_FALSE:
+    case STR_IS_TRUE:
+       OP(             TRY_CVT_TO_BOOLEAN);
+       switch (t) {
+           int over, over2;
+
+       case STR_IS_BOOL:
+           if (allowEmpty) {
+               JUMP1(  JUMP_TRUE, over);
+               PUSH(   "");
+               OP(     STR_EQ);
+               JUMP1(  JUMP, over2);
+               FIXJUMP1(over);
+               OP(     POP);
+               PUSH(   "1");
+               FIXJUMP1(over2);
+           } else {
+               OP4(    REVERSE, 2);
+               OP(     POP);
+           }
+           return TCL_OK;
+       case STR_IS_TRUE:
+           JUMP1(      JUMP_TRUE, over);
+           if (allowEmpty) {
+               PUSH(   "");
+               OP(     STR_EQ);
+           } else {
+               OP(     POP);
+               PUSH(   "0");
+           }
+           FIXJUMP1(   over);
+           OP(         LNOT);
+           OP(         LNOT);
+           return TCL_OK;
+       case STR_IS_FALSE:
+           JUMP1(      JUMP_TRUE, over);
+           if (allowEmpty) {
+               PUSH(   "");
+               OP(     STR_NEQ);
+           } else {
+               OP(     POP);
+               PUSH(   "1");
+           }
+           FIXJUMP1(   over);
+           OP(         LNOT);
+           return TCL_OK;
+       }
+    break;
+
+    case STR_IS_DOUBLE: {
+       int satisfied, isEmpty;
+
+       if (allowEmpty) {
+           OP(         DUP);
+           PUSH(       "");
+           OP(         STR_EQ);
+           JUMP1(      JUMP_TRUE, isEmpty);
+           OP(         NUM_TYPE);
+           JUMP1(      JUMP_TRUE, satisfied);
+           PUSH(       "0");
+           JUMP1(      JUMP, end);
+           FIXJUMP1(   isEmpty);
+           OP(         POP);
+           FIXJUMP1(   satisfied);
+       } else {
+           OP(         NUM_TYPE);
+           JUMP1(      JUMP_TRUE, satisfied);
+           PUSH(       "0");
+           JUMP1(      JUMP, end);
+           TclAdjustStackDepth(-1, envPtr);
+           FIXJUMP1(   satisfied);
+       }
+       PUSH(           "1");
+       FIXJUMP1(       end);
+       return TCL_OK;
+    }
+
+    case STR_IS_INT:
+    case STR_IS_WIDE:
+    case STR_IS_ENTIER:
+       if (allowEmpty) {
+           int testNumType;
+
+           OP(         DUP);
+           OP(         NUM_TYPE);
+           OP(         DUP);
+           JUMP1(      JUMP_TRUE, testNumType);
+           OP(         POP);
+           PUSH(       "");
+           OP(         STR_EQ);
+           JUMP1(      JUMP, end);
+           TclAdjustStackDepth(1, envPtr);
+           FIXJUMP1(   testNumType);
+           OP4(        REVERSE, 2);
+           OP(         POP);
+       } else {
+           OP(         NUM_TYPE);
+           OP(         DUP);
+           JUMP1(      JUMP_FALSE, end);
+       }
+
+       switch (t) {
+       case STR_IS_INT:
+           PUSH(       "1");
+           OP(         EQ);
+           break;
+       case STR_IS_WIDE:
+           PUSH(       "2");
+           OP(         LE);
+           break;
+       case STR_IS_ENTIER:
+           PUSH(       "3");
+           OP(         LE);
+           break;
+       }
+       FIXJUMP1(       end);
+       return TCL_OK;
+
+    case STR_IS_LIST:
+       range = TclCreateExceptRange(CATCH_EXCEPTION_RANGE, envPtr);
+       OP4(            BEGIN_CATCH4, range);
+       ExceptionRangeStarts(envPtr, range);
+       OP(             DUP);
+       OP(             LIST_LENGTH);
+       OP(             POP);
+       ExceptionRangeEnds(envPtr, range);
+       ExceptionRangeTarget(envPtr, range, catchOffset);
+       OP(             POP);
+       OP(             PUSH_RETURN_CODE);
+       OP(             END_CATCH);
+       OP(             LNOT);
+       return TCL_OK;
+    }
+
+    return TclCompileBasicMin0ArgCmd(interp, parsePtr, cmdPtr, envPtr);
+}
+
+int
+TclCompileStringMatchCmd(
+    Tcl_Interp *interp,                /* Used for error reporting. */
+    Tcl_Parse *parsePtr,       /* Points to a parse structure for the command
+                                * created by Tcl_ParseCommand. */
+    Command *cmdPtr,           /* Points to defintion of command being
+                                * compiled. */
+    CompileEnv *envPtr)                /* Holds resulting instructions. */
+{
+    DefineLineInformation;     /* TIP #280 */
+    Tcl_Token *tokenPtr;
+    int i, length, exactMatch = 0, nocase = 0;
+    const char *str;
+
+    if (parsePtr->numWords < 3 || parsePtr->numWords > 4) {
+       return TCL_ERROR;
+    }
+    tokenPtr = TokenAfter(parsePtr->tokenPtr);
+
+    /*
+     * Check if we have a -nocase flag.
+     */
+
+    if (parsePtr->numWords == 4) {
+       if (tokenPtr->type != TCL_TOKEN_SIMPLE_WORD) {
+           return TclCompileBasic3ArgCmd(interp, parsePtr, cmdPtr, envPtr);
+       }
+       str = tokenPtr[1].start;
+       length = tokenPtr[1].size;
+       if ((length <= 1) || strncmp(str, "-nocase", length)) {
+           /*
+            * Fail at run time, not in compilation.
+            */
+
+           return TclCompileBasic3ArgCmd(interp, parsePtr, cmdPtr, envPtr);
+       }
+       nocase = 1;
+       tokenPtr = TokenAfter(tokenPtr);
+    }
+
+    /*
+     * Push the strings to match against each other.
+     */
+
+    for (i = 0; i < 2; i++) {
+       if (tokenPtr->type == TCL_TOKEN_SIMPLE_WORD) {
+           str = tokenPtr[1].start;
+           length = tokenPtr[1].size;
+           if (!nocase && (i == 0)) {
+               /*
+                * Trivial matches can be done by 'string equal'. If -nocase
+                * was specified, we can't do this because INST_STR_EQ has no
+                * support for nocase.
+                */
+
+               Tcl_Obj *copy = Tcl_NewStringObj(str, length);
+
+               Tcl_IncrRefCount(copy);
+               exactMatch = TclMatchIsTrivial(TclGetString(copy));
+               TclDecrRefCount(copy);
+           }
+           PushLiteral(envPtr, str, length);
+       } else {
+           SetLineInformation(i+1+nocase);
+           CompileTokens(envPtr, tokenPtr, interp);
+       }
+       tokenPtr = TokenAfter(tokenPtr);
+    }
+
+    /*
+     * Push the matcher.
+     */
+
+    if (exactMatch) {
+       TclEmitOpcode(INST_STR_EQ, envPtr);
+    } else {
+       TclEmitInstInt1(INST_STR_MATCH, nocase, envPtr);
+    }
+    return TCL_OK;
+}
+
+int
+TclCompileStringLenCmd(
+    Tcl_Interp *interp,                /* Used for error reporting. */
+    Tcl_Parse *parsePtr,       /* Points to a parse structure for the command
+                                * created by Tcl_ParseCommand. */
+    Command *cmdPtr,           /* Points to defintion of command being
+                                * compiled. */
+    CompileEnv *envPtr)                /* Holds resulting instructions. */
+{
+    DefineLineInformation;     /* TIP #280 */
+    Tcl_Token *tokenPtr;
+    Tcl_Obj *objPtr;
+
+    if (parsePtr->numWords != 2) {
+       return TCL_ERROR;
+    }
+
+    tokenPtr = TokenAfter(parsePtr->tokenPtr);
+    TclNewObj(objPtr);
+    if (TclWordKnownAtCompileTime(tokenPtr, objPtr)) {
+       /*
+        * Here someone is asking for the length of a static string (or
+        * something with backslashes). Just push the actual character (not
+        * byte) length.
+        */
+
+       char buf[TCL_INTEGER_SPACE];
+       int len = Tcl_GetCharLength(objPtr);
+
+       len = sprintf(buf, "%d", len);
+       PushLiteral(envPtr, buf, len);
+    } else {
+       SetLineInformation(1);
+       CompileTokens(envPtr, tokenPtr, interp);
+       TclEmitOpcode(INST_STR_LEN, envPtr);
+    }
+    TclDecrRefCount(objPtr);
+    return TCL_OK;
+}
+
+int
+TclCompileStringMapCmd(
+    Tcl_Interp *interp,                /* Used for error reporting. */
+    Tcl_Parse *parsePtr,       /* Points to a parse structure for the command
+                                * created by Tcl_ParseCommand. */
+    Command *cmdPtr,           /* Points to defintion of command being
+                                * compiled. */
+    CompileEnv *envPtr)                /* Holds resulting instructions. */
+{
+    DefineLineInformation;     /* TIP #280 */
+    Tcl_Token *mapTokenPtr, *stringTokenPtr;
+    Tcl_Obj *mapObj, **objv;
+    char *bytes;
+    int len;
+
+    /*
+     * We only handle the case:
+     *
+     *    string map {foo bar} $thing
+     *
+     * That is, a literal two-element list (doesn't need to be brace-quoted,
+     * but does need to be compile-time knowable) and any old argument (the
+     * thing to map).
+     */
+
+    if (parsePtr->numWords != 3) {
+       return TCL_ERROR;
+    }
+    mapTokenPtr = TokenAfter(parsePtr->tokenPtr);
+    stringTokenPtr = TokenAfter(mapTokenPtr);
+    mapObj = Tcl_NewObj();
+    Tcl_IncrRefCount(mapObj);
+    if (!TclWordKnownAtCompileTime(mapTokenPtr, mapObj)) {
+       Tcl_DecrRefCount(mapObj);
+       return TclCompileBasic2ArgCmd(interp, parsePtr, cmdPtr, envPtr);
+    } else if (Tcl_ListObjGetElements(NULL, mapObj, &len, &objv) != TCL_OK) {
+       Tcl_DecrRefCount(mapObj);
+       return TclCompileBasic2ArgCmd(interp, parsePtr, cmdPtr, envPtr);
+    } else if (len != 2) {
+       Tcl_DecrRefCount(mapObj);
+       return TclCompileBasic2ArgCmd(interp, parsePtr, cmdPtr, envPtr);
+    }
+
+    /*
+     * Now issue the opcodes. Note that in the case that we know that the
+     * first word is an empty word, we don't issue the map at all. That is the
+     * correct semantics for mapping.
+     */
+
+    bytes = Tcl_GetStringFromObj(objv[0], &len);
+    if (len == 0) {
+       CompileWord(envPtr, stringTokenPtr, interp, 2);
+    } else {
+       PushLiteral(envPtr, bytes, len);
+       bytes = Tcl_GetStringFromObj(objv[1], &len);
+       PushLiteral(envPtr, bytes, len);
+       CompileWord(envPtr, stringTokenPtr, interp, 2);
+       OP(STR_MAP);
+    }
+    Tcl_DecrRefCount(mapObj);
+    return TCL_OK;
+}
+
+int
+TclCompileStringRangeCmd(
+    Tcl_Interp *interp,                /* Used for error reporting. */
+    Tcl_Parse *parsePtr,       /* Points to a parse structure for the command
+                                * created by Tcl_ParseCommand. */
+    Command *cmdPtr,           /* Points to defintion of command being
+                                * compiled. */
+    CompileEnv *envPtr)                /* Holds resulting instructions. */
+{
+    DefineLineInformation;     /* TIP #280 */
+    Tcl_Token *stringTokenPtr, *fromTokenPtr, *toTokenPtr;
+    int idx1, idx2;
+
+    if (parsePtr->numWords != 4) {
+       return TCL_ERROR;
+    }
+    stringTokenPtr = TokenAfter(parsePtr->tokenPtr);
+    fromTokenPtr = TokenAfter(stringTokenPtr);
+    toTokenPtr = TokenAfter(fromTokenPtr);
+
+    /* Every path must push the string argument */
+    CompileWord(envPtr, stringTokenPtr,                        interp, 1);
+
+    /*
+     * Parse the two indices.
+     */
+
+    if (TclGetIndexFromToken(fromTokenPtr, TCL_INDEX_START, TCL_INDEX_AFTER,
+           &idx1) != TCL_OK) {
+       goto nonConstantIndices;
+    }
+    /*
+     * Token parsed as an index expression. We treat all indices before
+     * the string the same as the start of the string.
+     */
+
+    if (idx1 == TCL_INDEX_AFTER) {
+       /* [string range $s end+1 $last] must be empty string */
+       OP(             POP);
+       PUSH(           "");
+       return TCL_OK;
+    }
+
+    if (TclGetIndexFromToken(toTokenPtr, TCL_INDEX_BEFORE, TCL_INDEX_END,
+           &idx2) != TCL_OK) {
+       goto nonConstantIndices;
+    }
+    /*
+     * Token parsed as an index expression. We treat all indices after
+     * the string the same as the end of the string.
+     */
+    if (idx2 == TCL_INDEX_BEFORE) {
+       /* [string range $s $first -1] must be empty string */
+       OP(             POP);
+       PUSH(           "");
+       return TCL_OK;
+    }
+
+    /*
+     * Push the operand onto the stack and then the substring operation.
+     */
+
+    OP44(              STR_RANGE_IMM, idx1, idx2);
+    return TCL_OK;
+
+    /*
+     * Push the operands onto the stack and then the substring operation.
+     */
+
+  nonConstantIndices:
+    CompileWord(envPtr, fromTokenPtr,                  interp, 2);
+    CompileWord(envPtr, toTokenPtr,                    interp, 3);
+    OP(                        STR_RANGE);
+    return TCL_OK;
+}
+
+int
+TclCompileStringReplaceCmd(
+    Tcl_Interp *interp,                /* Tcl interpreter for context. */
+    Tcl_Parse *parsePtr,       /* Points to a parse structure for the
+                                * command. */
+    Command *cmdPtr,           /* Points to defintion of command being
+                                * compiled. */
+    CompileEnv *envPtr)                /* Holds the resulting instructions. */
+{
+    DefineLineInformation;     /* TIP #280 */
+    Tcl_Token *tokenPtr, *valueTokenPtr;
+    int first, last;
+
+    if (parsePtr->numWords < 4 || parsePtr->numWords > 5) {
+       return TCL_ERROR;
+    }
+
+    /* Bytecode to compute/push string argument being replaced */
+    valueTokenPtr = TokenAfter(parsePtr->tokenPtr);
+    CompileWord(envPtr, valueTokenPtr, interp, 1);
+
+    /*
+     * Check for first index known and useful at compile time.
+     */
+    tokenPtr = TokenAfter(valueTokenPtr);
+    if (TclGetIndexFromToken(tokenPtr, TCL_INDEX_BEFORE, TCL_INDEX_AFTER,
+           &first) != TCL_OK) {
+       goto genericReplace;
+    }
+
+    /*
+     * Check for last index known and useful at compile time.
+     */
+    tokenPtr = TokenAfter(tokenPtr);
+    if (TclGetIndexFromToken(tokenPtr, TCL_INDEX_BEFORE, TCL_INDEX_AFTER,
+           &last) != TCL_OK) {
+       goto genericReplace;
+    }
+
+    /*
+     * [string replace] is an odd bird.  For many arguments it is
+     * a conventional substring replacer.  However it also goes out
+     * of its way to become a no-op for many cases where it would be
+     * replacing an empty substring.  Precisely, it is a no-op when
+     *
+     *         (last < first)          OR
+     *         (last < 0)              OR
+     *         (end < first)
+     *
+     * For some compile-time values we can detect these cases, and
+     * compile direct to bytecode implementing the no-op.
+     */
+
+    if ((last == TCL_INDEX_BEFORE)             /* Know (last < 0) */
+           || (first == TCL_INDEX_AFTER)       /* Know (first > end) */
+
+       /*
+        * Tricky to determine when runtime (last < first) can be
+        * certainly known based on the encoded values. Consider the
+        * cases...
+        *
+        * (first <= TCL_INDEX_END) &&
+        *      (last == TCL_INDEX_AFTER) => cannot tell REJECT
+        *      (last <= TCL_INDEX END) && (last < first) => ACCEPT
+        *      else => cannot tell REJECT
+        */
+           || ((first <= TCL_INDEX_END) && (last <= TCL_INDEX_END)
+               && (last < first))              /* Know (last < first) */
+       /*
+        * (first == TCL_INDEX_BEFORE) &&
+        *      (last == TCL_INDEX_AFTER) => (first < last) REJECT
+        *      (last <= TCL_INDEX_END) => cannot tell REJECT
+        *      else            => (first < last) REJECT
+        *
+        * else [[first >= TCL_INDEX_START]] &&
+        *      (last == TCL_INDEX_AFTER) => cannot tell REJECT
+        *      (last <= TCL_INDEX_END) => cannot tell REJECT
+        *      else [[last >= TCL_INDEX START]] && (last < first) => ACCEPT
+        */
+           || ((first >= TCL_INDEX_START) && (last >= TCL_INDEX_START)
+               && (last < first))) {           /* Know (last < first) */
+       if (parsePtr->numWords == 5) {
+           tokenPtr = TokenAfter(tokenPtr);
+           CompileWord(envPtr, tokenPtr, interp, 4);
+           OP(         POP);           /* Pop newString */
+       }
+       /* Original string argument now on TOS as result */
+       return TCL_OK;
+    }
+
+    if (parsePtr->numWords == 5) {
+    /*
+     * When we have a string replacement, we have to take care about
+     * not replacing empty substrings that [string replace] promises
+     * not to replace
+     *
+     * The remaining index values might be suitable for conventional
+     * string replacement, but only if they cannot possibly meet the
+     * conditions described above at runtime. If there's a chance they
+     * might, we would have to emit bytecode to check and at that point
+     * we're paying more in bytecode execution time than would make
+     * things worthwhile. Trouble is we are very limited in
+     * how much we can detect that at compile time. After decoding,
+     * we need, first:
+     *
+     *         (first <= end)
+     *
+     * The encoded indices (first <= TCL_INDEX END) and
+     * (first == TCL_INDEX_BEFORE) always meets this condition, but
+     * any other encoded first index has some list for which it fails.
+     *
+     * We also need, second:
+     *
+     *         (last >= 0)
+     *
+     * The encoded indices (last >= TCL_INDEX_START) and
+     * (last == TCL_INDEX_AFTER) always meet this condition but any
+     * other encoded last index has some list for which it fails.
+     *
+     * Finally we need, third:
+     *
+     *         (first <= last)
+     *
+     * Considered in combination with the constraints we already have,
+     * we see that we can proceed when (first == TCL_INDEX_BEFORE)
+     * or (last == TCL_INDEX_AFTER). These also permit simplification
+     * of the prefix|replace|suffix construction. The other constraints,
+     * though, interfere with getting a guarantee that first <= last.
+     */
+
+    if ((first == TCL_INDEX_BEFORE) && (last >= TCL_INDEX_START)) {
+       /* empty prefix */
+       tokenPtr = TokenAfter(tokenPtr);
+       CompileWord(envPtr, tokenPtr, interp, 4);
+       OP4(            REVERSE, 2);
+       if (last == TCL_INDEX_AFTER) {
+           OP(         POP);           /* Pop  original */
+       } else {
+           OP44(       STR_RANGE_IMM, last + 1, TCL_INDEX_END);
+           OP1(        STR_CONCAT1, 2);
+       }
+       return TCL_OK;
+    }
+
+    if ((last == TCL_INDEX_AFTER) && (first <= TCL_INDEX_END)) {
+       OP44(           STR_RANGE_IMM, 0, first-1);
+       tokenPtr = TokenAfter(tokenPtr);
+       CompileWord(envPtr, tokenPtr, interp, 4);
+       OP1(            STR_CONCAT1, 2);
+       return TCL_OK;
+    }
+
+       /* FLOW THROUGH TO genericReplace */
+
+    } else {
+       /*
+        * When we have no replacement string to worry about, we may
+        * have more luck, because the forbidden empty string replacements
+        * are harmless when they are replaced by another empty string.
+        */
+
+       if ((first == TCL_INDEX_BEFORE) || (first == TCL_INDEX_START)) {
+           /* empty prefix - build suffix only */
+
+           if ((last == TCL_INDEX_END) || (last == TCL_INDEX_AFTER)) {
+               /* empty suffix too => empty result */
+               OP(     POP);           /* Pop  original */
+               PUSH    (       "");
+               return TCL_OK;
+           }
+           OP44(       STR_RANGE_IMM, last + 1, TCL_INDEX_END);
+           return TCL_OK;
+       } else {
+           if ((last == TCL_INDEX_END) || (last == TCL_INDEX_AFTER)) {
+               /* empty suffix - build prefix only */
+               OP44(   STR_RANGE_IMM, 0, first-1);
+               return TCL_OK;
+           }
+           OP(         DUP);
+           OP44(       STR_RANGE_IMM, 0, first-1);
+           OP4(        REVERSE, 2);
+           OP44(       STR_RANGE_IMM, last + 1, TCL_INDEX_END);
+           OP1(        STR_CONCAT1, 2);
+           return TCL_OK;
+       }
+    }
+
+    genericReplace:
+       tokenPtr = TokenAfter(valueTokenPtr);
+       CompileWord(envPtr, tokenPtr, interp, 2);
+       tokenPtr = TokenAfter(tokenPtr);
+       CompileWord(envPtr, tokenPtr, interp, 3);
+       if (parsePtr->numWords == 5) {
+           tokenPtr = TokenAfter(tokenPtr);
+           CompileWord(envPtr, tokenPtr, interp, 4);
+       } else {
+           PUSH(       "");
+       }
+       OP(             STR_REPLACE);
+       return TCL_OK;
+}
+
+int
+TclCompileStringTrimLCmd(
+    Tcl_Interp *interp,                /* Used for error reporting. */
+    Tcl_Parse *parsePtr,       /* Points to a parse structure for the command
+                                * created by Tcl_ParseCommand. */
+    Command *cmdPtr,           /* Points to defintion of command being
+                                * compiled. */
+    CompileEnv *envPtr)                /* Holds resulting instructions. */
+{
+    DefineLineInformation;     /* TIP #280 */
+    Tcl_Token *tokenPtr;
+
+    if (parsePtr->numWords != 2 && parsePtr->numWords != 3) {
+       return TCL_ERROR;
+    }
+
+    tokenPtr = TokenAfter(parsePtr->tokenPtr);
+    CompileWord(envPtr, tokenPtr,                      interp, 1);
+    if (parsePtr->numWords == 3) {
+       tokenPtr = TokenAfter(tokenPtr);
+       CompileWord(envPtr, tokenPtr,                   interp, 2);
+    } else {
+       PushLiteral(envPtr, tclDefaultTrimSet, strlen(tclDefaultTrimSet));
+    }
+    OP(                        STR_TRIM_LEFT);
+    return TCL_OK;
+}
+
+int
+TclCompileStringTrimRCmd(
+    Tcl_Interp *interp,                /* Used for error reporting. */
+    Tcl_Parse *parsePtr,       /* Points to a parse structure for the command
+                                * created by Tcl_ParseCommand. */
+    Command *cmdPtr,           /* Points to defintion of command being
+                                * compiled. */
+    CompileEnv *envPtr)                /* Holds resulting instructions. */
+{
+    DefineLineInformation;     /* TIP #280 */
+    Tcl_Token *tokenPtr;
+
+    if (parsePtr->numWords != 2 && parsePtr->numWords != 3) {
+       return TCL_ERROR;
+    }
+
+    tokenPtr = TokenAfter(parsePtr->tokenPtr);
+    CompileWord(envPtr, tokenPtr,                      interp, 1);
+    if (parsePtr->numWords == 3) {
+       tokenPtr = TokenAfter(tokenPtr);
+       CompileWord(envPtr, tokenPtr,                   interp, 2);
+    } else {
+       PushLiteral(envPtr, tclDefaultTrimSet, strlen(tclDefaultTrimSet));
+    }
+    OP(                        STR_TRIM_RIGHT);
+    return TCL_OK;
+}
+
+int
+TclCompileStringTrimCmd(
+    Tcl_Interp *interp,                /* Used for error reporting. */
+    Tcl_Parse *parsePtr,       /* Points to a parse structure for the command
+                                * created by Tcl_ParseCommand. */
+    Command *cmdPtr,           /* Points to defintion of command being
+                                * compiled. */
+    CompileEnv *envPtr)                /* Holds resulting instructions. */
+{
+    DefineLineInformation;     /* TIP #280 */
+    Tcl_Token *tokenPtr;
+
+    if (parsePtr->numWords != 2 && parsePtr->numWords != 3) {
+       return TCL_ERROR;
+    }
+
+    tokenPtr = TokenAfter(parsePtr->tokenPtr);
+    CompileWord(envPtr, tokenPtr,                      interp, 1);
+    if (parsePtr->numWords == 3) {
+       tokenPtr = TokenAfter(tokenPtr);
+       CompileWord(envPtr, tokenPtr,                   interp, 2);
+    } else {
+       PushLiteral(envPtr, tclDefaultTrimSet, strlen(tclDefaultTrimSet));
+    }
+    OP(                        STR_TRIM);
+    return TCL_OK;
+}
+
+int
+TclCompileStringToUpperCmd(
+    Tcl_Interp *interp,                /* Used for error reporting. */
+    Tcl_Parse *parsePtr,       /* Points to a parse structure for the command
+                                * created by Tcl_ParseCommand. */
+    Command *cmdPtr,           /* Points to defintion of command being
+                                * compiled. */
+    CompileEnv *envPtr)                /* Holds resulting instructions. */
+{
+    DefineLineInformation;     /* TIP #280 */
+    Tcl_Token *tokenPtr;
+
+    if (parsePtr->numWords != 2) {
+       return TclCompileBasic1To3ArgCmd(interp, parsePtr, cmdPtr, envPtr);
+    }
+
+    tokenPtr = TokenAfter(parsePtr->tokenPtr);
+    CompileWord(envPtr, tokenPtr,                      interp, 1);
+    OP(                        STR_UPPER);
+    return TCL_OK;
+}
+
+int
+TclCompileStringToLowerCmd(
+    Tcl_Interp *interp,                /* Used for error reporting. */
+    Tcl_Parse *parsePtr,       /* Points to a parse structure for the command
+                                * created by Tcl_ParseCommand. */
+    Command *cmdPtr,           /* Points to defintion of command being
+                                * compiled. */
+    CompileEnv *envPtr)                /* Holds resulting instructions. */
+{
+    DefineLineInformation;     /* TIP #280 */
+    Tcl_Token *tokenPtr;
+
+    if (parsePtr->numWords != 2) {
+       return TclCompileBasic1To3ArgCmd(interp, parsePtr, cmdPtr, envPtr);
+    }
+
+    tokenPtr = TokenAfter(parsePtr->tokenPtr);
+    CompileWord(envPtr, tokenPtr,                      interp, 1);
+    OP(                        STR_LOWER);
+    return TCL_OK;
+}
+
+int
+TclCompileStringToTitleCmd(
+    Tcl_Interp *interp,                /* Used for error reporting. */
+    Tcl_Parse *parsePtr,       /* Points to a parse structure for the command
+                                * created by Tcl_ParseCommand. */
+    Command *cmdPtr,           /* Points to defintion of command being
+                                * compiled. */
+    CompileEnv *envPtr)                /* Holds resulting instructions. */
+{
+    DefineLineInformation;     /* TIP #280 */
+    Tcl_Token *tokenPtr;
+
+    if (parsePtr->numWords != 2) {
+       return TclCompileBasic1To3ArgCmd(interp, parsePtr, cmdPtr, envPtr);
+    }
+
+    tokenPtr = TokenAfter(parsePtr->tokenPtr);
+    CompileWord(envPtr, tokenPtr,                      interp, 1);
+    OP(                        STR_TITLE);
+    return TCL_OK;
+}
+\f
+/*
+ * Support definitions for the [string is] compilation.
+ */
+
+static int
+UniCharIsAscii(
+    int character)
+{
+    return (character >= 0) && (character < 0x80);
+}
+
+static int
+UniCharIsHexDigit(
+    int character)
+{
+    return (character >= 0) && (character < 0x80) && isxdigit(character);
+}
+
+StringClassDesc const tclStringClassTable[] = {
+    {"alnum",  Tcl_UniCharIsAlnum},
+    {"alpha",  Tcl_UniCharIsAlpha},
+    {"ascii",  UniCharIsAscii},
+    {"control", Tcl_UniCharIsControl},
+    {"digit",  Tcl_UniCharIsDigit},
+    {"graph",  Tcl_UniCharIsGraph},
+    {"lower",  Tcl_UniCharIsLower},
+    {"print",  Tcl_UniCharIsPrint},
+    {"punct",  Tcl_UniCharIsPunct},
+    {"space",  Tcl_UniCharIsSpace},
+    {"upper",  Tcl_UniCharIsUpper},
+    {"word",   Tcl_UniCharIsWordChar},
+    {"xdigit", UniCharIsHexDigit},
+    {NULL,     NULL}
+};
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclCompileSubstCmd --
+ *
+ *     Procedure called to compile the "subst" command.
+ *
+ * Results:
+ *     Returns TCL_OK for successful compile, or TCL_ERROR to defer
+ *     evaluation to runtime (either when it is too complex to get the
+ *     semantics right, or when we know for sure that it is an error but need
+ *     the error to happen at the right time).
+ *
+ * Side effects:
+ *     Instructions are added to envPtr to execute the "subst" command at
+ *     runtime.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TclCompileSubstCmd(
+    Tcl_Interp *interp,                /* Used for error reporting. */
+    Tcl_Parse *parsePtr,       /* Points to a parse structure for the command
+                                * created by Tcl_ParseCommand. */
+    Command *cmdPtr,           /* Points to defintion of command being
+                                * compiled. */
+    CompileEnv *envPtr)                /* Holds resulting instructions. */
+{
+    DefineLineInformation;     /* TIP #280 */
+    int numArgs = parsePtr->numWords - 1;
+    int numOpts = numArgs - 1;
+    int objc, flags = TCL_SUBST_ALL;
+    Tcl_Obj **objv/*, *toSubst = NULL*/;
+    Tcl_Token *wordTokenPtr = TokenAfter(parsePtr->tokenPtr);
+    int code = TCL_ERROR;
+
+    if (numArgs == 0) {
+       return TCL_ERROR;
+    }
+
+    objv = TclStackAlloc(interp, /*numArgs*/ numOpts * sizeof(Tcl_Obj *));
+
+    for (objc = 0; objc < /*numArgs*/ numOpts; objc++) {
+       objv[objc] = Tcl_NewObj();
+       Tcl_IncrRefCount(objv[objc]);
+       if (!TclWordKnownAtCompileTime(wordTokenPtr, objv[objc])) {
+           objc++;
+           goto cleanup;
+       }
+       wordTokenPtr = TokenAfter(wordTokenPtr);
+    }
+
+/*
+    if (TclSubstOptions(NULL, numOpts, objv, &flags) == TCL_OK) {
+       toSubst = objv[numOpts];
+       Tcl_IncrRefCount(toSubst);
+    }
+*/
+
+    /* TODO: Figure out expansion to cover WordKnownAtCompileTime
+     * The difficulty is that WKACT makes a copy, and if TclSubstParse
+     * below parses the copy of the original source string, some deep
+     * parts of the compile machinery get upset.  They want all pointers
+     * stored in Tcl_Tokens to point back to the same original string.
+     */
+    if (wordTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) {
+       code = TclSubstOptions(NULL, numOpts, objv, &flags);
+    }
+
+  cleanup:
+    while (--objc >= 0) {
+       TclDecrRefCount(objv[objc]);
+    }
+    TclStackFree(interp, objv);
+    if (/*toSubst == NULL*/ code != TCL_OK) {
+       return TCL_ERROR;
+    }
+
+    SetLineInformation(numArgs);
+    TclSubstCompile(interp, wordTokenPtr[1].start, wordTokenPtr[1].size,
+           flags, mapPtr->loc[eclIndex].line[numArgs], envPtr);
+
+/*    TclDecrRefCount(toSubst);*/
+    return TCL_OK;
+}
+
+void
+TclSubstCompile(
+    Tcl_Interp *interp,
+    const char *bytes,
+    int numBytes,
+    int flags,
+    int line,
+    CompileEnv *envPtr)
+{
+    Tcl_Token *endTokenPtr, *tokenPtr;
+    int breakOffset = 0, count = 0, bline = line;
+    Tcl_Parse parse;
+    Tcl_InterpState state = NULL;
+
+    TclSubstParse(interp, bytes, numBytes, flags, &parse, &state);
+    if (state != NULL) {
+       Tcl_ResetResult(interp);
+    }
+
+    /*
+     * Tricky point! If the first token does not result in a *guaranteed* push
+     * of a Tcl_Obj on the stack, we must push an empty object. Otherwise it
+     * is possible to get to an INST_STR_CONCAT1 or INST_DONE without enough
+     * values on the stack, resulting in a crash. Thanks to Joe Mistachkin for
+     * identifying a script that could trigger this case.
+     */
+
+    tokenPtr = parse.tokenPtr;
+    if (tokenPtr->type != TCL_TOKEN_TEXT && tokenPtr->type != TCL_TOKEN_BS) {
+       PUSH("");
+       count++;
+    }
+
+    for (endTokenPtr = tokenPtr + parse.numTokens;
+           tokenPtr < endTokenPtr; tokenPtr = TokenAfter(tokenPtr)) {
+       int length, literal, catchRange, breakJump;
+       char buf[TCL_UTF_MAX] = "";
+       JumpFixup startFixup, okFixup, returnFixup, breakFixup;
+       JumpFixup continueFixup, otherFixup, endFixup;
+
+       switch (tokenPtr->type) {
+       case TCL_TOKEN_TEXT:
+           literal = TclRegisterNewLiteral(envPtr,
+                   tokenPtr->start, tokenPtr->size);
+           TclEmitPush(literal, envPtr);
+           TclAdvanceLines(&bline, tokenPtr->start,
+                   tokenPtr->start + tokenPtr->size);
+           count++;
+           continue;
+       case TCL_TOKEN_BS:
+           length = TclParseBackslash(tokenPtr->start, tokenPtr->size,
+                   NULL, buf);
+           literal = TclRegisterNewLiteral(envPtr, buf, length);
+           TclEmitPush(literal, envPtr);
+           count++;
+           continue;
+       case TCL_TOKEN_VARIABLE:
+           /*
+            * Check for simple variable access; see if we can only generate
+            * TCL_OK or TCL_ERROR from the substituted variable read; if so,
+            * there is no need to generate elaborate exception-management
+            * code. Note that the first component of TCL_TOKEN_VARIABLE is
+            * always TCL_TOKEN_TEXT...
+            */
+
+           if (tokenPtr->numComponents > 1) {
+               int i, foundCommand = 0;
+
+               for (i=2 ; i<=tokenPtr->numComponents ; i++) {
+                   if (tokenPtr[i].type == TCL_TOKEN_COMMAND) {
+                       foundCommand = 1;
+                       break;
+                   }
+               }
+               if (foundCommand) {
+                   break;
+               }
+           }
+
+           envPtr->line = bline;
+           TclCompileVarSubst(interp, tokenPtr, envPtr);
+           bline = envPtr->line;
+           count++;
+           continue;
+       }
+
+       while (count > 255) {
+           OP1(                STR_CONCAT1, 255);
+           count -= 254;
+       }
+       if (count > 1) {
+           OP1(                STR_CONCAT1, count);
+           count = 1;
+       }
+
+       if (breakOffset == 0) {
+           /* Jump to the start (jump over the jump to end) */
+           TclEmitForwardJump(envPtr, TCL_UNCONDITIONAL_JUMP, &startFixup);
+
+           /* Jump to the end (all BREAKs land here) */
+           breakOffset = CurrentOffset(envPtr);
+           TclEmitInstInt4(INST_JUMP4, 0, envPtr);
+
+           /* Start */
+           if (TclFixupForwardJumpToHere(envPtr, &startFixup, 127)) {
+               Tcl_Panic("TclCompileSubstCmd: bad start jump distance %d",
+                       (int) (CurrentOffset(envPtr) - startFixup.codeOffset));
+           }
+       }
+
+       envPtr->line = bline;
+       catchRange = TclCreateExceptRange(CATCH_EXCEPTION_RANGE, envPtr);
+       OP4(    BEGIN_CATCH4, catchRange);
+       ExceptionRangeStarts(envPtr, catchRange);
+
+       switch (tokenPtr->type) {
+       case TCL_TOKEN_COMMAND:
+           TclCompileScript(interp, tokenPtr->start+1, tokenPtr->size-2,
+                   envPtr);
+           count++;
+           break;
+       case TCL_TOKEN_VARIABLE:
+           TclCompileVarSubst(interp, tokenPtr, envPtr);
+           count++;
+           break;
+       default:
+           Tcl_Panic("unexpected token type in TclCompileSubstCmd: %d",
+                   tokenPtr->type);
+       }
+
+       ExceptionRangeEnds(envPtr, catchRange);
+
+       /* Substitution produced TCL_OK */
+       OP(     END_CATCH);
+       TclEmitForwardJump(envPtr, TCL_UNCONDITIONAL_JUMP, &okFixup);
+       TclAdjustStackDepth(-1, envPtr);
+
+       /* Exceptional return codes processed here */
+       ExceptionRangeTarget(envPtr, catchRange, catchOffset);
+       OP(     PUSH_RETURN_OPTIONS);
+       OP(     PUSH_RESULT);
+       OP(     PUSH_RETURN_CODE);
+       OP(     END_CATCH);
+       OP(     RETURN_CODE_BRANCH);
+
+       /* ERROR -> reraise it; NB: can't require BREAK/CONTINUE handling */
+       OP(     RETURN_STK);
+       OP(     NOP);
+
+       /* RETURN */
+       TclEmitForwardJump(envPtr, TCL_UNCONDITIONAL_JUMP, &returnFixup);
+
+       /* BREAK */
+       TclEmitForwardJump(envPtr, TCL_UNCONDITIONAL_JUMP, &breakFixup);
+
+       /* CONTINUE */
+       TclEmitForwardJump(envPtr, TCL_UNCONDITIONAL_JUMP, &continueFixup);
+
+       /* OTHER */
+       TclEmitForwardJump(envPtr, TCL_UNCONDITIONAL_JUMP, &otherFixup);
+
+       TclAdjustStackDepth(1, envPtr);
+       /* BREAK destination */
+       if (TclFixupForwardJumpToHere(envPtr, &breakFixup, 127)) {
+           Tcl_Panic("TclCompileSubstCmd: bad break jump distance %d",
+                   (int) (CurrentOffset(envPtr) - breakFixup.codeOffset));
+       }
+       OP(     POP);
+       OP(     POP);
+
+       breakJump = CurrentOffset(envPtr) - breakOffset;
+       if (breakJump > 127) {
+           OP4(JUMP4, -breakJump);
+       } else {
+           OP1(JUMP1, -breakJump);
+       }
+
+       TclAdjustStackDepth(2, envPtr);
+       /* CONTINUE destination */
+       if (TclFixupForwardJumpToHere(envPtr, &continueFixup, 127)) {
+           Tcl_Panic("TclCompileSubstCmd: bad continue jump distance %d",
+                   (int) (CurrentOffset(envPtr) - continueFixup.codeOffset));
+       }
+       OP(     POP);
+       OP(     POP);
+       TclEmitForwardJump(envPtr, TCL_UNCONDITIONAL_JUMP, &endFixup);
+
+       TclAdjustStackDepth(2, envPtr);
+       /* RETURN + other destination */
+       if (TclFixupForwardJumpToHere(envPtr, &returnFixup, 127)) {
+           Tcl_Panic("TclCompileSubstCmd: bad return jump distance %d",
+                   (int) (CurrentOffset(envPtr) - returnFixup.codeOffset));
+       }
+       if (TclFixupForwardJumpToHere(envPtr, &otherFixup, 127)) {
+           Tcl_Panic("TclCompileSubstCmd: bad other jump distance %d",
+                   (int) (CurrentOffset(envPtr) - otherFixup.codeOffset));
+       }
+
+       /*
+        * Pull the result to top of stack, discard options dict.
+        */
+
+       OP4(    REVERSE, 2);
+       OP(     POP);
+
+       /* OK destination */
+       if (TclFixupForwardJumpToHere(envPtr, &okFixup, 127)) {
+           Tcl_Panic("TclCompileSubstCmd: bad ok jump distance %d",
+                   (int) (CurrentOffset(envPtr) - okFixup.codeOffset));
+       }
+       if (count > 1) {
+           OP1(STR_CONCAT1, count);
+           count = 1;
+       }
+
+       /* CONTINUE jump to here */
+       if (TclFixupForwardJumpToHere(envPtr, &endFixup, 127)) {
+           Tcl_Panic("TclCompileSubstCmd: bad end jump distance %d",
+                   (int) (CurrentOffset(envPtr) - endFixup.codeOffset));
+       }
+       bline = envPtr->line;
+    }
+
+    while (count > 255) {
+       OP1(    STR_CONCAT1, 255);
+       count -= 254;
+    }
+    if (count > 1) {
+       OP1(    STR_CONCAT1, count);
+    }
+
+    Tcl_FreeParse(&parse);
+
+    if (state != NULL) {
+       Tcl_RestoreInterpState(interp, state);
+       TclCompileSyntaxError(interp, envPtr);
+       TclAdjustStackDepth(-1, envPtr);
+    }
+
+    /* Final target of the multi-jump from all BREAKs */
+    if (breakOffset > 0) {
+       TclUpdateInstInt4AtPc(INST_JUMP4, CurrentOffset(envPtr) - breakOffset,
+               envPtr->codeStart + breakOffset);
+    }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclCompileSwitchCmd --
+ *
+ *     Procedure called to compile the "switch" command.
+ *
+ * Results:
+ *     Returns TCL_OK for successful compile, or TCL_ERROR to defer
+ *     evaluation to runtime (either when it is too complex to get the
+ *     semantics right, or when we know for sure that it is an error but need
+ *     the error to happen at the right time).
+ *
+ * Side effects:
+ *     Instructions are added to envPtr to execute the "switch" command at
+ *     runtime.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TclCompileSwitchCmd(
+    Tcl_Interp *interp,                /* Used for error reporting. */
+    Tcl_Parse *parsePtr,       /* Points to a parse structure for the command
+                                * created by Tcl_ParseCommand. */
+    Command *cmdPtr,           /* Points to defintion of command being
+                                * compiled. */
+    CompileEnv *envPtr)                /* Holds resulting instructions. */
+{
+    DefineLineInformation;     /* TIP #280 */
+    Tcl_Token *tokenPtr;       /* Pointer to tokens in command. */
+    int numWords;              /* Number of words in command. */
+
+    Tcl_Token *valueTokenPtr;  /* Token for the value to switch on. */
+    enum {Switch_Exact, Switch_Glob, Switch_Regexp} mode;
+                               /* What kind of switch are we doing? */
+
+    Tcl_Token *bodyTokenArray; /* Array of real pattern list items. */
+    Tcl_Token **bodyToken;     /* Array of pointers to pattern list items. */
+    int *bodyLines;            /* Array of line numbers for body list
+                                * items. */
+    int **bodyContLines;       /* Array of continuation line info. */
+    int noCase;                        /* Has the -nocase flag been given? */
+    int foundMode = 0;         /* Have we seen a mode flag yet? */
+    int i, valueIndex;
+    int result = TCL_ERROR;
+    int *clNext = envPtr->clNext;
+
+    /*
+     * Only handle the following versions:
+     *   switch         ?--? word {pattern body ...}
+     *   switch -exact  ?--? word {pattern body ...}
+     *   switch -glob   ?--? word {pattern body ...}
+     *   switch -regexp ?--? word {pattern body ...}
+     *   switch         --   word simpleWordPattern simpleWordBody ...
+     *   switch -exact  --   word simpleWordPattern simpleWordBody ...
+     *   switch -glob   --   word simpleWordPattern simpleWordBody ...
+     *   switch -regexp --   word simpleWordPattern simpleWordBody ...
+     * When the mode is -glob, can also handle a -nocase flag.
+     *
+     * First off, we don't care how the command's word was generated; we're
+     * compiling it anyway! So skip it...
+     */
+
+    tokenPtr = TokenAfter(parsePtr->tokenPtr);
+    valueIndex = 1;
+    numWords = parsePtr->numWords-1;
+
+    /*
+     * Check for options.
+     */
+
+    noCase = 0;
+    mode = Switch_Exact;
+    if (numWords == 2) {
+       /*
+        * There's just the switch value and the bodies list. In that case, we
+        * can skip all option parsing and move on to consider switch values
+        * and the body list.
+        */
+
+       goto finishedOptionParse;
+    }
+
+    /*
+     * There must be at least one option, --, because without that there is no
+     * way to statically avoid the problems you get from strings-to-be-matched
+     * that start with a - (the interpreted code falls apart if it encounters
+     * them, so we punt if we *might* encounter them as that is the easiest
+     * way of emulating the behaviour).
+     */
+
+    for (; numWords>=3 ; tokenPtr=TokenAfter(tokenPtr),numWords--) {
+       unsigned size = tokenPtr[1].size;
+       const char *chrs = tokenPtr[1].start;
+
+       /*
+        * We only process literal options, and we assume that -e, -g and -n
+        * are unique prefixes of -exact, -glob and -nocase respectively (true
+        * at time of writing). Note that -exact and -glob may only be given
+        * at most once or we bail out (error case).
+        */
+
+       if (tokenPtr->type != TCL_TOKEN_SIMPLE_WORD || size < 2) {
+           return TCL_ERROR;
+       }
+
+       if ((size <= 6) && !memcmp(chrs, "-exact", size)) {
+           if (foundMode) {
+               return TCL_ERROR;
+           }
+           mode = Switch_Exact;
+           foundMode = 1;
+           valueIndex++;
+           continue;
+       } else if ((size <= 5) && !memcmp(chrs, "-glob", size)) {
+           if (foundMode) {
+               return TCL_ERROR;
+           }
+           mode = Switch_Glob;
+           foundMode = 1;
+           valueIndex++;
+           continue;
+       } else if ((size <= 7) && !memcmp(chrs, "-regexp", size)) {
+           if (foundMode) {
+               return TCL_ERROR;
+           }
+           mode = Switch_Regexp;
+           foundMode = 1;
+           valueIndex++;
+           continue;
+       } else if ((size <= 7) && !memcmp(chrs, "-nocase", size)) {
+           noCase = 1;
+           valueIndex++;
+           continue;
+       } else if ((size == 2) && !memcmp(chrs, "--", 2)) {
+           valueIndex++;
+           break;
+       }
+
+       /*
+        * The switch command has many flags we cannot compile at all (e.g.
+        * all the RE-related ones) which we must have encountered. Either
+        * that or we have run off the end. The action here is the same: punt
+        * to interpreted version.
+        */
+
+       return TCL_ERROR;
+    }
+    if (numWords < 3) {
+       return TCL_ERROR;
+    }
+    tokenPtr = TokenAfter(tokenPtr);
+    numWords--;
+    if (noCase && (mode == Switch_Exact)) {
+       /*
+        * Can't compile this case; no opcode for case-insensitive equality!
+        */
+
+       return TCL_ERROR;
+    }
+
+    /*
+     * The value to test against is going to always get pushed on the stack.
+     * But not yet; we need to verify that the rest of the command is
+     * compilable too.
+     */
+
+  finishedOptionParse:
+    valueTokenPtr = tokenPtr;
+    /* For valueIndex, see previous loop. */
+    tokenPtr = TokenAfter(tokenPtr);
+    numWords--;
+
+    /*
+     * Build an array of tokens for the matcher terms and script bodies. Note
+     * that in the case of the quoted bodies, this is tricky as we cannot use
+     * copies of the string from the input token for the generated tokens (it
+     * causes a crash during exception handling). When multiple tokens are
+     * available at this point, this is pretty easy.
+     */
+
+    if (numWords == 1) {
+       const char *bytes;
+       int maxLen, numBytes;
+       int bline;              /* TIP #280: line of the pattern/action list,
+                                * and start of list for when tracking the
+                                * location. This list comes immediately after
+                                * the value we switch on. */
+
+       if (tokenPtr->type != TCL_TOKEN_SIMPLE_WORD) {
+           return TCL_ERROR;
+       }
+       bytes = tokenPtr[1].start;
+       numBytes = tokenPtr[1].size;
+
+       /* Allocate enough space to work in. */
+       maxLen = TclMaxListLength(bytes, numBytes, NULL);
+       if (maxLen < 2)  {
+           return TCL_ERROR;
+       }
+       bodyTokenArray = ckalloc(sizeof(Tcl_Token) * maxLen);
+       bodyToken = ckalloc(sizeof(Tcl_Token *) * maxLen);
+       bodyLines = ckalloc(sizeof(int) * maxLen);
+       bodyContLines = ckalloc(sizeof(int*) * maxLen);
+
+       bline = mapPtr->loc[eclIndex].line[valueIndex+1];
+       numWords = 0;
+
+       while (numBytes > 0) {
+           const char *prevBytes = bytes;
+           int literal;
+
+           if (TCL_OK != TclFindElement(NULL, bytes, numBytes,
+                   &(bodyTokenArray[numWords].start), &bytes,
+                   &(bodyTokenArray[numWords].size), &literal) || !literal) {
+               goto abort;
+           }
+
+           bodyTokenArray[numWords].type = TCL_TOKEN_TEXT;
+           bodyTokenArray[numWords].numComponents = 0;
+           bodyToken[numWords] = bodyTokenArray + numWords;
+
+           /*
+            * TIP #280: Now determine the line the list element starts on
+            * (there is no need to do it earlier, due to the possibility of
+            * aborting, see above).
+            */
+
+           TclAdvanceLines(&bline, prevBytes, bodyTokenArray[numWords].start);
+           TclAdvanceContinuations(&bline, &clNext,
+                   bodyTokenArray[numWords].start - envPtr->source);
+           bodyLines[numWords] = bline;
+           bodyContLines[numWords] = clNext;
+           TclAdvanceLines(&bline, bodyTokenArray[numWords].start, bytes);
+           TclAdvanceContinuations(&bline, &clNext, bytes - envPtr->source);
+
+           numBytes -= (bytes - prevBytes);
+           numWords++;
+       }
+       if (numWords % 2) {
+       abort:
+           ckfree((char *) bodyToken);
+           ckfree((char *) bodyTokenArray);
+           ckfree((char *) bodyLines);
+           ckfree((char *) bodyContLines);
+           return TCL_ERROR;
+       }
+    } else if (numWords % 2 || numWords == 0) {
+       /*
+        * Odd number of words (>1) available, or no words at all available.
+        * Both are error cases, so punt and let the interpreted-version
+        * generate the error message. Note that the second case probably
+        * should get caught earlier, but it's easy to check here again anyway
+        * because it'd cause a nasty crash otherwise.
+        */
+
+       return TCL_ERROR;
+    } else {
+       /*
+        * Multi-word definition of patterns & actions.
+        */
+
+       bodyToken = ckalloc(sizeof(Tcl_Token *) * numWords);
+       bodyLines = ckalloc(sizeof(int) * numWords);
+       bodyContLines = ckalloc(sizeof(int*) * numWords);
+       bodyTokenArray = NULL;
+       for (i=0 ; i<numWords ; i++) {
+           /*
+            * We only handle the very simplest case. Anything more complex is
+            * a good reason to go to the interpreted case anyway due to
+            * traces, etc.
+            */
+
+           if (tokenPtr->type != TCL_TOKEN_SIMPLE_WORD) {
+               goto freeTemporaries;
+           }
+           bodyToken[i] = tokenPtr+1;
+
+           /*
+            * TIP #280: Copy line information from regular cmd info.
+            */
+
+           bodyLines[i] = mapPtr->loc[eclIndex].line[valueIndex+1+i];
+           bodyContLines[i] = mapPtr->loc[eclIndex].next[valueIndex+1+i];
+           tokenPtr = TokenAfter(tokenPtr);
+       }
+    }
+
+    /*
+     * Fall back to interpreted if the last body is a continuation (it's
+     * illegal, but this makes the error happen at the right time).
+     */
+
+    if (bodyToken[numWords-1]->size == 1 &&
+           bodyToken[numWords-1]->start[0] == '-') {
+       goto freeTemporaries;
+    }
+
+    /*
+     * Now we commit to generating code; the parsing stage per se is done.
+     * Check if we can generate a jump table, since if so that's faster than
+     * doing an explicit compare with each body. Note that we're definitely
+     * over-conservative with determining whether we can do the jump table,
+     * but it handles the most common case well enough.
+     */
+
+    /* Both methods push the value to match against onto the stack. */
+    CompileWord(envPtr, valueTokenPtr, interp, valueIndex);
+
+    if (mode == Switch_Exact) {
+       IssueSwitchJumpTable(interp, envPtr, valueIndex, numWords, bodyToken,
+               bodyLines, bodyContLines);
+    } else {
+       IssueSwitchChainedTests(interp, envPtr, mode, noCase, valueIndex,
+               numWords, bodyToken, bodyLines, bodyContLines);
+    }
+    result = TCL_OK;
+
+    /*
+     * Clean up all our temporary space and return.
+     */
+
+  freeTemporaries:
+    ckfree(bodyToken);
+    ckfree(bodyLines);
+    ckfree(bodyContLines);
+    if (bodyTokenArray != NULL) {
+       ckfree(bodyTokenArray);
+    }
+    return result;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * IssueSwitchChainedTests --
+ *
+ *     Generate instructions for a [switch] command that is to be compiled
+ *     into a sequence of tests. This is the generic handle-everything mode
+ *     that inherently has performance that is (on average) linear in the
+ *     number of tests. It is the only mode that can handle -glob and -regexp
+ *     matches, or anything that is case-insensitive. It does not handle the
+ *     wild-and-wooly end of regexp matching (i.e., capture of match results)
+ *     so that's when we spill to the interpreted version.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+IssueSwitchChainedTests(
+    Tcl_Interp *interp,                /* Context for compiling script bodies. */
+    CompileEnv *envPtr,                /* Holds resulting instructions. */
+    int mode,                  /* Exact, Glob or Regexp */
+    int noCase,                        /* Case-insensitivity flag. */
+    int valueIndex,            /* The value to match against. */
+    int numBodyTokens,         /* Number of tokens describing things the
+                                * switch can match against and bodies to
+                                * execute when the match succeeds. */
+    Tcl_Token **bodyToken,     /* Array of pointers to pattern list items. */
+    int *bodyLines,            /* Array of line numbers for body list
+                                * items. */
+    int **bodyContLines)       /* Array of continuation line info. */
+{
+    enum {Switch_Exact, Switch_Glob, Switch_Regexp};
+    int foundDefault;          /* Flag to indicate whether a "default" clause
+                                * is present. */
+    JumpFixup *fixupArray;     /* Array of forward-jump fixup records. */
+    unsigned int *fixupTargetArray; /* Array of places for fixups to point at. */
+    int fixupCount;            /* Number of places to fix up. */
+    int contFixIndex;          /* Where the first of the jumps due to a group
+                                * of continuation bodies starts, or -1 if
+                                * there aren't any. */
+    int contFixCount;          /* Number of continuation bodies pointing to
+                                * the current (or next) real body. */
+    int nextArmFixupIndex;
+    int simple, exact;         /* For extracting the type of regexp. */
+    int i;
+
+    /*
+     * Generate a test for each arm.
+     */
+
+    contFixIndex = -1;
+    contFixCount = 0;
+    fixupArray = TclStackAlloc(interp, sizeof(JumpFixup) * numBodyTokens);
+    fixupTargetArray = TclStackAlloc(interp, sizeof(int) * numBodyTokens);
+    memset(fixupTargetArray, 0, numBodyTokens * sizeof(int));
+    fixupCount = 0;
+    foundDefault = 0;
+    for (i=0 ; i<numBodyTokens ; i+=2) {
+       nextArmFixupIndex = -1;
+       if (i!=numBodyTokens-2 || bodyToken[numBodyTokens-2]->size != 7 ||
+               memcmp(bodyToken[numBodyTokens-2]->start, "default", 7)) {
+           /*
+            * Generate the test for the arm.
+            */
+
+           switch (mode) {
+           case Switch_Exact:
+               OP(     DUP);
+               TclCompileTokens(interp, bodyToken[i], 1,       envPtr);
+               OP(     STR_EQ);
+               break;
+           case Switch_Glob:
+               TclCompileTokens(interp, bodyToken[i], 1,       envPtr);
+               OP4(    OVER, 1);
+               OP1(    STR_MATCH, noCase);
+               break;
+           case Switch_Regexp:
+               simple = exact = 0;
+
+               /*
+                * Keep in sync with TclCompileRegexpCmd.
+                */
+
+               if (bodyToken[i]->type == TCL_TOKEN_TEXT) {
+                   Tcl_DString ds;
+
+                   if (bodyToken[i]->size == 0) {
+                       /*
+                        * The semantics of regexps are that they always match
+                        * when the RE == "".
+                        */
+
+                       PUSH("1");
+                       break;
+                   }
+
+                   /*
+                    * Attempt to convert pattern to glob. If successful, push
+                    * the converted pattern.
+                    */
+
+                   if (TclReToGlob(NULL, bodyToken[i]->start,
+                           bodyToken[i]->size, &ds, &exact, NULL) == TCL_OK){
+                       simple = 1;
+                       PushLiteral(envPtr, Tcl_DStringValue(&ds),
+                               Tcl_DStringLength(&ds));
+                       Tcl_DStringFree(&ds);
+                   }
+               }
+               if (!simple) {
+                   TclCompileTokens(interp, bodyToken[i], 1, envPtr);
+               }
+
+               OP4(    OVER, 1);
+               if (!simple) {
+                   /*
+                    * Pass correct RE compile flags. We use only Int1
+                    * (8-bit), but that handles all the flags we want to
+                    * pass. Don't use TCL_REG_NOSUB as we may have backrefs
+                    * or capture vars.
+                    */
+
+                   int cflags = TCL_REG_ADVANCED
+                           | (noCase ? TCL_REG_NOCASE : 0);
+
+                   OP1(REGEXP, cflags);
+               } else if (exact && !noCase) {
+                   OP( STR_EQ);
+               } else {
+                   OP1(STR_MATCH, noCase);
+               }
+               break;
+           default:
+               Tcl_Panic("unknown switch mode: %d", mode);
+           }
+
+           /*
+            * In a fall-through case, we will jump on _true_ to the place
+            * where the body starts (generated later, with guarantee of this
+            * ensured earlier; the final body is never a fall-through).
+            */
+
+           if (bodyToken[i+1]->size==1 && bodyToken[i+1]->start[0]=='-') {
+               if (contFixIndex == -1) {
+                   contFixIndex = fixupCount;
+                   contFixCount = 0;
+               }
+               TclEmitForwardJump(envPtr, TCL_TRUE_JUMP,
+                       &fixupArray[contFixIndex+contFixCount]);
+               fixupCount++;
+               contFixCount++;
+               continue;
+           }
+
+           TclEmitForwardJump(envPtr, TCL_FALSE_JUMP,
+                   &fixupArray[fixupCount]);
+           nextArmFixupIndex = fixupCount;
+           fixupCount++;
+       } else {
+           /*
+            * Got a default clause; set a flag to inhibit the generation of
+            * the jump after the body and the cleanup of the intermediate
+            * value that we are switching against.
+            *
+            * Note that default clauses (which are always terminal clauses)
+            * cannot be fall-through clauses as well, since the last clause
+            * is never a fall-through clause (which we have already
+            * verified).
+            */
+
+           foundDefault = 1;
+       }
+
+       /*
+        * Generate the body for the arm. This is guaranteed not to be a
+        * fall-through case, but it might have preceding fall-through cases,
+        * so we must process those first.
+        */
+
+       if (contFixIndex != -1) {
+           int j;
+
+           for (j=0 ; j<contFixCount ; j++) {
+               fixupTargetArray[contFixIndex+j] = CurrentOffset(envPtr);
+           }
+           contFixIndex = -1;
+       }
+
+       /*
+        * Now do the actual compilation. Note that we do not use BODY()
+        * because we may have synthesized the tokens in a non-standard
+        * pattern.
+        */
+
+       OP(     POP);
+       envPtr->line = bodyLines[i+1];          /* TIP #280 */
+       envPtr->clNext = bodyContLines[i+1];    /* TIP #280 */
+       TclCompileCmdWord(interp, bodyToken[i+1], 1, envPtr);
+
+       if (!foundDefault) {
+           TclEmitForwardJump(envPtr, TCL_UNCONDITIONAL_JUMP,
+                   &fixupArray[fixupCount]);
+           fixupCount++;
+           fixupTargetArray[nextArmFixupIndex] = CurrentOffset(envPtr);
+       }
+    }
+
+    /*
+     * Discard the value we are matching against unless we've had a default
+     * clause (in which case it will already be gone due to the code at the
+     * start of processing an arm, guaranteed) and make the result of the
+     * command an empty string.
+     */
+
+    if (!foundDefault) {
+       OP(     POP);
+       PUSH("");
+    }
+
+    /*
+     * Do jump fixups for arms that were executed. First, fill in the jumps of
+     * all jumps that don't point elsewhere to point to here.
+     */
+
+    for (i=0 ; i<fixupCount ; i++) {
+       if (fixupTargetArray[i] == 0) {
+           fixupTargetArray[i] = envPtr->codeNext-envPtr->codeStart;
+       }
+    }
+
+    /*
+     * Now scan backwards over all the jumps (all of which are forward jumps)
+     * doing each one. When we do one and there is a size changes, we must
+     * scan back over all the previous ones and see if they need adjusting
+     * before proceeding with further jump fixups (the interleaved nature of
+     * all the jumps makes this impossible to do without nested loops).
+     */
+
+    for (i=fixupCount-1 ; i>=0 ; i--) {
+       if (TclFixupForwardJump(envPtr, &fixupArray[i],
+               fixupTargetArray[i] - fixupArray[i].codeOffset, 127)) {
+           int j;
+
+           for (j=i-1 ; j>=0 ; j--) {
+               if (fixupTargetArray[j] > fixupArray[i].codeOffset) {
+                   fixupTargetArray[j] += 3;
+               }
+           }
+       }
+    }
+    TclStackFree(interp, fixupTargetArray);
+    TclStackFree(interp, fixupArray);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * IssueSwitchJumpTable --
+ *
+ *     Generate instructions for a [switch] command that is to be compiled
+ *     into a jump table. This only handles the case where case-sensitive,
+ *     exact matching is used, but this is actually the most common case in
+ *     real code.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+IssueSwitchJumpTable(
+    Tcl_Interp *interp,                /* Context for compiling script bodies. */
+    CompileEnv *envPtr,                /* Holds resulting instructions. */
+    int valueIndex,            /* The value to match against. */
+    int numBodyTokens,         /* Number of tokens describing things the
+                                * switch can match against and bodies to
+                                * execute when the match succeeds. */
+    Tcl_Token **bodyToken,     /* Array of pointers to pattern list items. */
+    int *bodyLines,            /* Array of line numbers for body list
+                                * items. */
+    int **bodyContLines)       /* Array of continuation line info. */
+{
+    JumptableInfo *jtPtr;
+    int infoIndex, isNew, *finalFixups, numRealBodies = 0, jumpLocation;
+    int mustGenerate, foundDefault, jumpToDefault, i;
+    Tcl_DString buffer;
+    Tcl_HashEntry *hPtr;
+
+    /*
+     * Compile the switch by using a jump table, which is basically a
+     * hashtable that maps from literal values to match against to the offset
+     * (relative to the INST_JUMP_TABLE instruction) to jump to. The jump
+     * table itself is independent of any invokation of the bytecode, and as
+     * such is stored in an auxData block.
+     *
+     * Start by allocating the jump table itself, plus some workspace.
+     */
+
+    jtPtr = ckalloc(sizeof(JumptableInfo));
+    Tcl_InitHashTable(&jtPtr->hashTable, TCL_STRING_KEYS);
+    infoIndex = TclCreateAuxData(jtPtr, &tclJumptableInfoType, envPtr);
+    finalFixups = TclStackAlloc(interp, sizeof(int) * (numBodyTokens/2));
+    foundDefault = 0;
+    mustGenerate = 1;
+
+    /*
+     * Next, issue the instruction to do the jump, together with what we want
+     * to do if things do not work out (jump to either the default clause or
+     * the "default" default, which just sets the result to empty). Note that
+     * we will come back and rewrite the jump's offset parameter when we know
+     * what it should be, and that all jumps we issue are of the wide kind
+     * because that makes the code much easier to debug!
+     */
+
+    jumpLocation = CurrentOffset(envPtr);
+    OP4(       JUMP_TABLE, infoIndex);
+    jumpToDefault = CurrentOffset(envPtr);
+    OP4(       JUMP4, 0);
+
+    for (i=0 ; i<numBodyTokens ; i+=2) {
+       /*
+        * For each arm, we must first work out what to do with the match
+        * term.
+        */
+
+       if (i!=numBodyTokens-2 || bodyToken[numBodyTokens-2]->size != 7 ||
+               memcmp(bodyToken[numBodyTokens-2]->start, "default", 7)) {
+           /*
+            * This is not a default clause, so insert the current location as
+            * a target in the jump table (assuming it isn't already there,
+            * which would indicate that this clause is probably masked by an
+            * earlier one). Note that we use a Tcl_DString here simply
+            * because the hash API does not let us specify the string length.
+            */
+
+           Tcl_DStringInit(&buffer);
+           TclDStringAppendToken(&buffer, bodyToken[i]);
+           hPtr = Tcl_CreateHashEntry(&jtPtr->hashTable,
+                   Tcl_DStringValue(&buffer), &isNew);
+           if (isNew) {
+               /*
+                * First time we've encountered this match clause, so it must
+                * point to here.
+                */
+
+               Tcl_SetHashValue(hPtr, CurrentOffset(envPtr) - jumpLocation);
+           }
+           Tcl_DStringFree(&buffer);
+       } else {
+           /*
+            * This is a default clause, so patch up the fallthrough from the
+            * INST_JUMP_TABLE instruction to here.
+            */
+
+           foundDefault = 1;
+           isNew = 1;
+           TclStoreInt4AtPtr(CurrentOffset(envPtr)-jumpToDefault,
+                   envPtr->codeStart+jumpToDefault+1);
+       }
+
+       /*
+        * Now, for each arm we must deal with the body of the clause.
+        *
+        * If this is a continuation body (never true of a final clause,
+        * whether default or not) we're done because the next jump target
+        * will also point here, so we advance to the next clause.
+        */
+
+       if (bodyToken[i+1]->size == 1 && bodyToken[i+1]->start[0] == '-') {
+           mustGenerate = 1;
+           continue;
+       }
+
+       /*
+        * Also skip this arm if its only match clause is masked. (We could
+        * probably be more aggressive about this, but that would be much more
+        * difficult to get right.)
+        */
+
+       if (!isNew && !mustGenerate) {
+           continue;
+       }
+       mustGenerate = 0;
+
+       /*
+        * Compile the body of the arm.
+        */
+
+       envPtr->line = bodyLines[i+1];          /* TIP #280 */
+       envPtr->clNext = bodyContLines[i+1];    /* TIP #280 */
+       TclCompileCmdWord(interp, bodyToken[i+1], 1, envPtr);
+
+       /*
+        * Compile a jump in to the end of the command if this body is
+        * anything other than a user-supplied default arm (to either skip
+        * over the remaining bodies or the code that generates an empty
+        * result).
+        */
+
+       if (i+2 < numBodyTokens || !foundDefault) {
+           finalFixups[numRealBodies++] = CurrentOffset(envPtr);
+
+           /*
+            * Easier by far to issue this jump as a fixed-width jump, since
+            * otherwise we'd need to do a lot more (and more awkward)
+            * rewriting when we fixed this all up.
+            */
+
+           OP4(        JUMP4, 0);
+           TclAdjustStackDepth(-1, envPtr);
+       }
+    }
+
+    /*
+     * We're at the end. If we've not already done so through the processing
+     * of a user-supplied default clause, add in a "default" default clause
+     * now.
+     */
+
+    if (!foundDefault) {
+       TclStoreInt4AtPtr(CurrentOffset(envPtr)-jumpToDefault,
+               envPtr->codeStart+jumpToDefault+1);
+       PUSH("");
+    }
+
+    /*
+     * No more instructions to be issued; everything that needs to jump to the
+     * end of the command is fixed up at this point.
+     */
+
+    for (i=0 ; i<numRealBodies ; i++) {
+       TclStoreInt4AtPtr(CurrentOffset(envPtr)-finalFixups[i],
+               envPtr->codeStart+finalFixups[i]+1);
+    }
+
+    /*
+     * Clean up all our temporary space and return.
+     */
+
+    TclStackFree(interp, finalFixups);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * DupJumptableInfo, FreeJumptableInfo --
+ *
+ *     Functions to duplicate, release and print a jump-table created for use
+ *     with the INST_JUMP_TABLE instruction.
+ *
+ * Results:
+ *     DupJumptableInfo: a copy of the jump-table
+ *     FreeJumptableInfo: none
+ *     PrintJumptableInfo: none
+ *     DisassembleJumptableInfo: none
+ *
+ * Side effects:
+ *     DupJumptableInfo: allocates memory
+ *     FreeJumptableInfo: releases memory
+ *     PrintJumptableInfo: none
+ *     DisassembleJumptableInfo: none
+ *
+ *----------------------------------------------------------------------
+ */
+
+static ClientData
+DupJumptableInfo(
+    ClientData clientData)
+{
+    JumptableInfo *jtPtr = clientData;
+    JumptableInfo *newJtPtr = ckalloc(sizeof(JumptableInfo));
+    Tcl_HashEntry *hPtr, *newHPtr;
+    Tcl_HashSearch search;
+    int isNew;
+
+    Tcl_InitHashTable(&newJtPtr->hashTable, TCL_STRING_KEYS);
+    hPtr = Tcl_FirstHashEntry(&jtPtr->hashTable, &search);
+    while (hPtr != NULL) {
+       newHPtr = Tcl_CreateHashEntry(&newJtPtr->hashTable,
+               Tcl_GetHashKey(&jtPtr->hashTable, hPtr), &isNew);
+       Tcl_SetHashValue(newHPtr, Tcl_GetHashValue(hPtr));
+    }
+    return newJtPtr;
+}
+
+static void
+FreeJumptableInfo(
+    ClientData clientData)
+{
+    JumptableInfo *jtPtr = clientData;
+
+    Tcl_DeleteHashTable(&jtPtr->hashTable);
+    ckfree(jtPtr);
+}
+
+static void
+PrintJumptableInfo(
+    ClientData clientData,
+    Tcl_Obj *appendObj,
+    ByteCode *codePtr,
+    unsigned int pcOffset)
+{
+    JumptableInfo *jtPtr = clientData;
+    Tcl_HashEntry *hPtr;
+    Tcl_HashSearch search;
+    const char *keyPtr;
+    int offset, i = 0;
+
+    hPtr = Tcl_FirstHashEntry(&jtPtr->hashTable, &search);
+    for (; hPtr ; hPtr = Tcl_NextHashEntry(&search)) {
+       keyPtr = Tcl_GetHashKey(&jtPtr->hashTable, hPtr);
+       offset = PTR2INT(Tcl_GetHashValue(hPtr));
+
+       if (i++) {
+           Tcl_AppendToObj(appendObj, ", ", -1);
+           if (i%4==0) {
+               Tcl_AppendToObj(appendObj, "\n\t\t", -1);
+           }
+       }
+       Tcl_AppendPrintfToObj(appendObj, "\"%s\"->pc %d",
+               keyPtr, pcOffset + offset);
+    }
+}
+
+static void
+DisassembleJumptableInfo(
+    ClientData clientData,
+    Tcl_Obj *dictObj,
+    ByteCode *codePtr,
+    unsigned int pcOffset)
+{
+    JumptableInfo *jtPtr = clientData;
+    Tcl_Obj *mapping = Tcl_NewObj();
+    Tcl_HashEntry *hPtr;
+    Tcl_HashSearch search;
+    const char *keyPtr;
+    int offset;
+
+    hPtr = Tcl_FirstHashEntry(&jtPtr->hashTable, &search);
+    for (; hPtr ; hPtr = Tcl_NextHashEntry(&search)) {
+       keyPtr = Tcl_GetHashKey(&jtPtr->hashTable, hPtr);
+       offset = PTR2INT(Tcl_GetHashValue(hPtr));
+       Tcl_DictObjPut(NULL, mapping, Tcl_NewStringObj(keyPtr, -1),
+               Tcl_NewIntObj(offset));
+    }
+    Tcl_DictObjPut(NULL, dictObj, Tcl_NewStringObj("mapping", -1), mapping);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclCompileTailcallCmd --
+ *
+ *     Procedure called to compile the "tailcall" command.
+ *
+ * Results:
+ *     Returns TCL_OK for a successful compile. Returns TCL_ERROR to defer
+ *     evaluation to runtime.
+ *
+ * Side effects:
+ *     Instructions are added to envPtr to execute the "tailcall" command at
+ *     runtime.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TclCompileTailcallCmd(
+    Tcl_Interp *interp,                /* Used for error reporting. */
+    Tcl_Parse *parsePtr,       /* Points to a parse structure for the command
+                                * created by Tcl_ParseCommand. */
+    Command *cmdPtr,           /* Points to defintion of command being
+                                * compiled. */
+    CompileEnv *envPtr)                /* Holds resulting instructions. */
+{
+    DefineLineInformation;     /* TIP #280 */
+    Tcl_Token *tokenPtr = parsePtr->tokenPtr;
+    int i;
+
+    if (parsePtr->numWords < 2 || parsePtr->numWords > 256
+           || envPtr->procPtr == NULL) {
+       return TCL_ERROR;
+    }
+
+    /* make room for the nsObjPtr */
+    /* TODO: Doesn't this have to be a known value? */
+    CompileWord(envPtr, tokenPtr, interp, 0);
+    for (i=1 ; i<parsePtr->numWords ; i++) {
+       tokenPtr = TokenAfter(tokenPtr);
+       CompileWord(envPtr, tokenPtr, interp, i);
+    }
+    TclEmitInstInt1(   INST_TAILCALL, parsePtr->numWords,      envPtr);
+    return TCL_OK;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclCompileThrowCmd --
+ *
+ *     Procedure called to compile the "throw" command.
+ *
+ * Results:
+ *     Returns TCL_OK for a successful compile. Returns TCL_ERROR to defer
+ *     evaluation to runtime.
+ *
+ * Side effects:
+ *     Instructions are added to envPtr to execute the "throw" command at
+ *     runtime.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TclCompileThrowCmd(
+    Tcl_Interp *interp,                /* Used for error reporting. */
+    Tcl_Parse *parsePtr,       /* Points to a parse structure for the command
+                                * created by Tcl_ParseCommand. */
+    Command *cmdPtr,           /* Points to defintion of command being
+                                * compiled. */
+    CompileEnv *envPtr)                /* Holds resulting instructions. */
+{
+    DefineLineInformation;     /* TIP #280 */
+    int numWords = parsePtr->numWords;
+    Tcl_Token *codeToken, *msgToken;
+    Tcl_Obj *objPtr;
+    int codeKnown, codeIsList, codeIsValid, len;
+
+    if (numWords != 3) {
+       return TCL_ERROR;
+    }
+    codeToken = TokenAfter(parsePtr->tokenPtr);
+    msgToken = TokenAfter(codeToken);
+
+    TclNewObj(objPtr);
+    Tcl_IncrRefCount(objPtr);
+
+    codeKnown = TclWordKnownAtCompileTime(codeToken, objPtr);
+
+    /*
+     * First we must emit the code to substitute the arguments.  This
+     * must come first in case substitution raises errors.
+     */
+    if (!codeKnown) {
+       CompileWord(envPtr, codeToken, interp, 1);
+       PUSH(                   "-errorcode");
+    }
+    CompileWord(envPtr, msgToken, interp, 2);
+
+    codeIsList = codeKnown && (TCL_OK ==
+           Tcl_ListObjLength(interp, objPtr, &len));
+    codeIsValid = codeIsList && (len != 0);
+
+    if (codeIsValid) {
+       Tcl_Obj *errPtr, *dictPtr;
+
+       TclNewLiteralStringObj(errPtr, "-errorcode");
+       TclNewObj(dictPtr);
+       Tcl_DictObjPut(NULL, dictPtr, errPtr, objPtr);
+       TclEmitPush(TclAddLiteralObj(envPtr, dictPtr, NULL), envPtr);
+    }
+    TclDecrRefCount(objPtr);
+
+    /*
+     * Simpler bytecodes when we detect invalid arguments at compile time.
+     */
+    if (codeKnown && !codeIsValid) {
+       OP(                     POP);
+       if (codeIsList) {
+           /* Must be an empty list */
+           goto issueErrorForEmptyCode;
+       }
+       TclCompileSyntaxError(interp, envPtr);
+       return TCL_OK;
+    }
+
+    if (!codeKnown) {
+       /*
+        * Argument validity checking has to be done by bytecode at
+        * run time.
+        */
+       OP4(                    REVERSE, 3);
+       OP(                     DUP);
+       OP(                     LIST_LENGTH);
+       OP1(                    JUMP_FALSE1, 16);
+       OP4(                    LIST, 2);
+       OP44(                   RETURN_IMM, TCL_ERROR, 0);
+       TclAdjustStackDepth(2, envPtr);
+       OP(                     POP);
+       OP(                     POP);
+       OP(                     POP);
+    issueErrorForEmptyCode:
+       PUSH(                   "type must be non-empty list");
+       PUSH(                   "-errorcode {TCL OPERATION THROW BADEXCEPTION}");
+    }
+    OP44(                      RETURN_IMM, TCL_ERROR, 0);
+    return TCL_OK;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclCompileTryCmd --
+ *
+ *     Procedure called to compile the "try" command.
+ *
+ * Results:
+ *     Returns TCL_OK for a successful compile. Returns TCL_ERROR to defer
+ *     evaluation to runtime.
+ *
+ * Side effects:
+ *     Instructions are added to envPtr to execute the "try" command at
+ *     runtime.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TclCompileTryCmd(
+    Tcl_Interp *interp,                /* Used for error reporting. */
+    Tcl_Parse *parsePtr,       /* Points to a parse structure for the command
+                                * created by Tcl_ParseCommand. */
+    Command *cmdPtr,           /* Points to defintion of command being
+                                * compiled. */
+    CompileEnv *envPtr)                /* Holds resulting instructions. */
+{
+    int numWords = parsePtr->numWords, numHandlers, result = TCL_ERROR;
+    Tcl_Token *bodyToken, *finallyToken, *tokenPtr;
+    Tcl_Token **handlerTokens = NULL;
+    Tcl_Obj **matchClauses = NULL;
+    int *matchCodes=NULL, *resultVarIndices=NULL, *optionVarIndices=NULL;
+    int i;
+
+    if (numWords < 2) {
+       return TCL_ERROR;
+    }
+
+    bodyToken = TokenAfter(parsePtr->tokenPtr);
+
+    if (numWords == 2) {
+       /*
+        * No handlers or finally; do nothing beyond evaluating the body.
+        */
+
+       DefineLineInformation;  /* TIP #280 */
+       BODY(bodyToken, 1);
+       return TCL_OK;
+    }
+
+    numWords -= 2;
+    tokenPtr = TokenAfter(bodyToken);
+
+    /*
+     * Extract information about what handlers there are.
+     */
+
+    numHandlers = numWords >> 2;
+    numWords -= numHandlers * 4;
+    if (numHandlers > 0) {
+       handlerTokens = TclStackAlloc(interp, sizeof(Tcl_Token*)*numHandlers);
+       matchClauses = TclStackAlloc(interp, sizeof(Tcl_Obj *) * numHandlers);
+       memset(matchClauses, 0, sizeof(Tcl_Obj *) * numHandlers);
+       matchCodes = TclStackAlloc(interp, sizeof(int) * numHandlers);
+       resultVarIndices = TclStackAlloc(interp, sizeof(int) * numHandlers);
+       optionVarIndices = TclStackAlloc(interp, sizeof(int) * numHandlers);
+
+       for (i=0 ; i<numHandlers ; i++) {
+           Tcl_Obj *tmpObj, **objv;
+           int objc;
+
+           if (tokenPtr->type != TCL_TOKEN_SIMPLE_WORD) {
+               goto failedToCompile;
+           }
+           if (tokenPtr[1].size == 4
+                   && !strncmp(tokenPtr[1].start, "trap", 4)) {
+               /*
+                * Parse the list of errorCode words to match against.
+                */
+
+               matchCodes[i] = TCL_ERROR;
+               tokenPtr = TokenAfter(tokenPtr);
+               TclNewObj(tmpObj);
+               Tcl_IncrRefCount(tmpObj);
+               if (!TclWordKnownAtCompileTime(tokenPtr, tmpObj)
+                       || Tcl_ListObjLength(NULL, tmpObj, &objc) != TCL_OK
+                       || (objc == 0)) {
+                   TclDecrRefCount(tmpObj);
+                   goto failedToCompile;
+               }
+               Tcl_ListObjReplace(NULL, tmpObj, 0, 0, 0, NULL);
+               matchClauses[i] = tmpObj;
+           } else if (tokenPtr[1].size == 2
+                   && !strncmp(tokenPtr[1].start, "on", 2)) {
+               int code;
+
+               /*
+                * Parse the result code to look for.
+                */
+
+               tokenPtr = TokenAfter(tokenPtr);
+               TclNewObj(tmpObj);
+               Tcl_IncrRefCount(tmpObj);
+               if (!TclWordKnownAtCompileTime(tokenPtr, tmpObj)) {
+                   TclDecrRefCount(tmpObj);
+                   goto failedToCompile;
+               }
+               if (TCL_ERROR == TclGetCompletionCodeFromObj(NULL, tmpObj, &code)) {
+                   TclDecrRefCount(tmpObj);
+                   goto failedToCompile;
+               }
+               matchCodes[i] = code;
+               TclDecrRefCount(tmpObj);
+           } else {
+               goto failedToCompile;
+           }
+
+           /*
+            * Parse the variable binding.
+            */
+
+           tokenPtr = TokenAfter(tokenPtr);
+           TclNewObj(tmpObj);
+           Tcl_IncrRefCount(tmpObj);
+           if (!TclWordKnownAtCompileTime(tokenPtr, tmpObj)) {
+               TclDecrRefCount(tmpObj);
+               goto failedToCompile;
+           }
+           if (Tcl_ListObjGetElements(NULL, tmpObj, &objc, &objv) != TCL_OK
+                   || (objc > 2)) {
+               TclDecrRefCount(tmpObj);
+               goto failedToCompile;
+           }
+           if (objc > 0) {
+               int len;
+               const char *varname = Tcl_GetStringFromObj(objv[0], &len);
+
+               resultVarIndices[i] = LocalScalar(varname, len, envPtr);
+               if (resultVarIndices[i] < 0) {
+                   TclDecrRefCount(tmpObj);
+                   goto failedToCompile;
+               }
+           } else {
+               resultVarIndices[i] = -1;
+           }
+           if (objc == 2) {
+               int len;
+               const char *varname = Tcl_GetStringFromObj(objv[1], &len);
+
+               optionVarIndices[i] = LocalScalar(varname, len, envPtr);
+               if (optionVarIndices[i] < 0) {
+                   TclDecrRefCount(tmpObj);
+                   goto failedToCompile;
+               }
+           } else {
+               optionVarIndices[i] = -1;
+           }
+           TclDecrRefCount(tmpObj);
+
+           /*
+            * Extract the body for this handler.
+            */
+
+           tokenPtr = TokenAfter(tokenPtr);
+           if (tokenPtr->type != TCL_TOKEN_SIMPLE_WORD) {
+               goto failedToCompile;
+           }
+           if (tokenPtr[1].size == 1 && tokenPtr[1].start[0] == '-') {
+               handlerTokens[i] = NULL;
+           } else {
+               handlerTokens[i] = tokenPtr;
+           }
+
+           tokenPtr = TokenAfter(tokenPtr);
+       }
+
+       if (handlerTokens[numHandlers-1] == NULL) {
+           goto failedToCompile;
+       }
+    }
+
+    /*
+     * Parse the finally clause
+     */
+
+    if (numWords == 0) {
+       finallyToken = NULL;
+    } else if (numWords == 2) {
+       if (tokenPtr->type != TCL_TOKEN_SIMPLE_WORD || tokenPtr[1].size != 7
+               || strncmp(tokenPtr[1].start, "finally", 7)) {
+           goto failedToCompile;
+       }
+       finallyToken = TokenAfter(tokenPtr);
+    } else {
+       goto failedToCompile;
+    }
+
+    /*
+     * Issue the bytecode.
+     */
+
+    if (!finallyToken) {
+       result = IssueTryClausesInstructions(interp, envPtr, bodyToken,
+               numHandlers, matchCodes, matchClauses, resultVarIndices,
+               optionVarIndices, handlerTokens);
+    } else if (numHandlers == 0) {
+       result = IssueTryFinallyInstructions(interp, envPtr, bodyToken,
+               finallyToken);
+    } else {
+       result = IssueTryClausesFinallyInstructions(interp, envPtr, bodyToken,
+               numHandlers, matchCodes, matchClauses, resultVarIndices,
+               optionVarIndices, handlerTokens, finallyToken);
+    }
+
+    /*
+     * Delete any temporary state and finish off.
+     */
+
+  failedToCompile:
+    if (numHandlers > 0) {
+       for (i=0 ; i<numHandlers ; i++) {
+           if (matchClauses[i]) {
+               TclDecrRefCount(matchClauses[i]);
+           }
+       }
+       TclStackFree(interp, optionVarIndices);
+       TclStackFree(interp, resultVarIndices);
+       TclStackFree(interp, matchCodes);
+       TclStackFree(interp, matchClauses);
+       TclStackFree(interp, handlerTokens);
+    }
+    return result;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * IssueTryClausesInstructions, IssueTryClausesFinallyInstructions,
+ * IssueTryFinallyInstructions --
+ *
+ *     The code generators for [try]. Split from the parsing engine for
+ *     reasons of developer sanity, and also split between no-finally,
+ *     just-finally and with-finally cases because so many of the details of
+ *     generation vary between the three.
+ *
+ *     The macros below make the instruction issuing easier to follow.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+IssueTryClausesInstructions(
+    Tcl_Interp *interp,
+    CompileEnv *envPtr,
+    Tcl_Token *bodyToken,
+    int numHandlers,
+    int *matchCodes,
+    Tcl_Obj **matchClauses,
+    int *resultVars,
+    int *optionVars,
+    Tcl_Token **handlerTokens)
+{
+    DefineLineInformation;     /* TIP #280 */
+    int range, resultVar, optionsVar;
+    int i, j, len, forwardsNeedFixing = 0, trapZero = 0, afterBody = 0;
+    int *addrsToFix, *forwardsToFix, notCodeJumpSource, notECJumpSource;
+    int *noError;
+    char buf[TCL_INTEGER_SPACE];
+
+    resultVar = AnonymousLocal(envPtr);
+    optionsVar = AnonymousLocal(envPtr);
+    if (resultVar < 0 || optionsVar < 0) {
+       return TCL_ERROR;
+    }
+
+    /*
+     * Check if we're supposed to trap a normal TCL_OK completion of the body.
+     * If not, we can handle that case much more efficiently.
+     */
+
+    for (i=0 ; i<numHandlers ; i++) {
+       if (matchCodes[i] == 0) {
+           trapZero = 1;
+           break;
+       }
+    }
+
+    /*
+     * Compile the body, trapping any error in it so that we can trap on it
+     * and/or run a finally clause. Note that there must be at least one
+     * on/trap clause; when none is present, this whole function is not called
+     * (and it's never called when there's a finally clause).
+     */
+
+    range = TclCreateExceptRange(CATCH_EXCEPTION_RANGE, envPtr);
+    OP4(                               BEGIN_CATCH4, range);
+    ExceptionRangeStarts(envPtr, range);
+    BODY(                              bodyToken, 1);
+    ExceptionRangeEnds(envPtr, range);
+    if (!trapZero) {
+       OP(                             END_CATCH);
+       JUMP4(                          JUMP, afterBody);
+       TclAdjustStackDepth(-1, envPtr);
+    } else {
+       PUSH(                           "0");
+       OP4(                            REVERSE, 2);
+       OP1(                            JUMP1, 4);
+       TclAdjustStackDepth(-2, envPtr);
+    }
+    ExceptionRangeTarget(envPtr, range, catchOffset);
+    OP(                                        PUSH_RETURN_CODE);
+    OP(                                        PUSH_RESULT);
+    OP(                                        PUSH_RETURN_OPTIONS);
+    OP(                                        END_CATCH);
+    STORE(                             optionsVar);
+    OP(                                        POP);
+    STORE(                             resultVar);
+    OP(                                        POP);
+
+    /*
+     * Now we handle all the registered 'on' and 'trap' handlers in order.
+     * For us to be here, there must be at least one handler.
+     *
+     * Slight overallocation, but reduces size of this function.
+     */
+
+    addrsToFix = TclStackAlloc(interp, sizeof(int)*numHandlers);
+    forwardsToFix = TclStackAlloc(interp, sizeof(int)*numHandlers);
+    noError = TclStackAlloc(interp, sizeof(int)*numHandlers);
+
+    for (i=0 ; i<numHandlers ; i++) {
+       noError[i] = -1;
+       sprintf(buf, "%d", matchCodes[i]);
+       OP(                             DUP);
+       PushLiteral(envPtr, buf, strlen(buf));
+       OP(                             EQ);
+       JUMP4(                          JUMP_FALSE, notCodeJumpSource);
+       if (matchClauses[i]) {
+           const char *p;
+           Tcl_ListObjLength(NULL, matchClauses[i], &len);
+
+           /*
+            * Match the errorcode according to try/trap rules.
+            */
+
+           LOAD(                       optionsVar);
+           PUSH(                       "-errorcode");
+           OP4(                        DICT_GET, 1);
+           TclAdjustStackDepth(-1, envPtr);
+           OP44(                       LIST_RANGE_IMM, 0, len-1);
+           p = Tcl_GetStringFromObj(matchClauses[i], &len);
+           PushLiteral(envPtr, p, len);
+           OP(                         STR_EQ);
+           JUMP4(                      JUMP_FALSE, notECJumpSource);
+       } else {
+           notECJumpSource = -1; /* LINT */
+       }
+       OP(                             POP);
+
+       /*
+        * There is no finally clause, so we can avoid wrapping a catch
+        * context around the handler. That simplifies what instructions need
+        * to be issued a lot since we can let errors just fall through.
+        */
+
+       if (resultVars[i] >= 0) {
+           LOAD(                       resultVar);
+           STORE(                      resultVars[i]);
+           OP(                         POP);
+           if (optionVars[i] >= 0) {
+               LOAD(                   optionsVar);
+               STORE(                  optionVars[i]);
+               OP(                     POP);
+           }
+       }
+       if (!handlerTokens[i]) {
+           forwardsNeedFixing = 1;
+           JUMP4(                      JUMP, forwardsToFix[i]);
+           TclAdjustStackDepth(1, envPtr);
+       } else {
+           int dontChangeOptions;
+
+           forwardsToFix[i] = -1;
+           if (forwardsNeedFixing) {
+               forwardsNeedFixing = 0;
+               for (j=0 ; j<i ; j++) {
+                   if (forwardsToFix[j] == -1) {
+                       continue;
+                   }
+                   FIXJUMP4(forwardsToFix[j]);
+                   forwardsToFix[j] = -1;
+               }
+           }
+           range = TclCreateExceptRange(CATCH_EXCEPTION_RANGE, envPtr);
+           OP4(                        BEGIN_CATCH4, range);
+           ExceptionRangeStarts(envPtr, range);
+           BODY(                       handlerTokens[i], 5+i*4);
+           ExceptionRangeEnds(envPtr, range);
+           OP(                         END_CATCH);
+           JUMP4(                      JUMP, noError[i]);
+           ExceptionRangeTarget(envPtr, range, catchOffset);
+           TclAdjustStackDepth(-1, envPtr);
+           OP(                         PUSH_RESULT);
+           OP(                         PUSH_RETURN_OPTIONS);
+           OP(                         PUSH_RETURN_CODE);
+           OP(                         END_CATCH);
+           PUSH(                       "1");
+           OP(                         EQ);
+           JUMP1(                      JUMP_FALSE, dontChangeOptions);
+           LOAD(                       optionsVar);
+           OP4(                        REVERSE, 2);
+           STORE(                      optionsVar);
+           OP(                         POP);
+           PUSH(                       "-during");
+           OP4(                        REVERSE, 2);
+           OP44(                       DICT_SET, 1, optionsVar);
+           TclAdjustStackDepth(-1, envPtr);
+           FIXJUMP1(           dontChangeOptions);
+           OP4(                        REVERSE, 2);
+           INVOKE(                     RETURN_STK);
+       }
+
+       JUMP4(                          JUMP, addrsToFix[i]);
+       if (matchClauses[i]) {
+           FIXJUMP4(   notECJumpSource);
+       }
+       FIXJUMP4(       notCodeJumpSource);
+    }
+
+    /*
+     * Drop the result code since it didn't match any clause, and reissue the
+     * exception. Note also that INST_RETURN_STK can proceed to the next
+     * instruction.
+     */
+
+    OP(                                        POP);
+    LOAD(                              optionsVar);
+    LOAD(                              resultVar);
+    INVOKE(                            RETURN_STK);
+
+    /*
+     * Fix all the jumps from taken clauses to here (which is the end of the
+     * [try]).
+     */
+
+    if (!trapZero) {
+       FIXJUMP4(afterBody);
+    }
+    for (i=0 ; i<numHandlers ; i++) {
+       FIXJUMP4(addrsToFix[i]);
+       if (noError[i] != -1) {
+           FIXJUMP4(noError[i]);
+       }
+    }
+    TclStackFree(interp, noError);
+    TclStackFree(interp, forwardsToFix);
+    TclStackFree(interp, addrsToFix);
+    return TCL_OK;
+}
+
+static int
+IssueTryClausesFinallyInstructions(
+    Tcl_Interp *interp,
+    CompileEnv *envPtr,
+    Tcl_Token *bodyToken,
+    int numHandlers,
+    int *matchCodes,
+    Tcl_Obj **matchClauses,
+    int *resultVars,
+    int *optionVars,
+    Tcl_Token **handlerTokens,
+    Tcl_Token *finallyToken)   /* Not NULL */
+{
+    DefineLineInformation;     /* TIP #280 */
+    int range, resultVar, optionsVar, i, j, len, forwardsNeedFixing = 0;
+    int trapZero = 0, afterBody = 0, finalOK, finalError, noFinalError;
+    int *addrsToFix, *forwardsToFix, notCodeJumpSource, notECJumpSource;
+    char buf[TCL_INTEGER_SPACE];
+
+    resultVar = AnonymousLocal(envPtr);
+    optionsVar = AnonymousLocal(envPtr);
+    if (resultVar < 0 || optionsVar < 0) {
+       return TCL_ERROR;
+    }
+
+    /*
+     * Check if we're supposed to trap a normal TCL_OK completion of the body.
+     * If not, we can handle that case much more efficiently.
+     */
+
+    for (i=0 ; i<numHandlers ; i++) {
+       if (matchCodes[i] == 0) {
+           trapZero = 1;
+           break;
+       }
+    }
+
+    /*
+     * Compile the body, trapping any error in it so that we can trap on it
+     * (if any trap matches) and run a finally clause.
+     */
+
+    range = TclCreateExceptRange(CATCH_EXCEPTION_RANGE, envPtr);
+    OP4(                               BEGIN_CATCH4, range);
+    ExceptionRangeStarts(envPtr, range);
+    BODY(                              bodyToken, 1);
+    ExceptionRangeEnds(envPtr, range);
+    if (!trapZero) {
+       OP(                             END_CATCH);
+       STORE(                          resultVar);
+       OP(                             POP);
+       PUSH(                           "-level 0 -code 0");
+       STORE(                          optionsVar);
+       OP(                             POP);
+       JUMP4(                          JUMP, afterBody);
+    } else {
+       PUSH(                           "0");
+       OP4(                            REVERSE, 2);
+       OP1(                            JUMP1, 4);
+       TclAdjustStackDepth(-2, envPtr);
+    }
+    ExceptionRangeTarget(envPtr, range, catchOffset);
+    OP(                                        PUSH_RETURN_CODE);
+    OP(                                        PUSH_RESULT);
+    OP(                                        PUSH_RETURN_OPTIONS);
+    OP(                                        END_CATCH);
+    STORE(                             optionsVar);
+    OP(                                        POP);
+    STORE(                             resultVar);
+    OP(                                        POP);
+
+    /*
+     * Now we handle all the registered 'on' and 'trap' handlers in order.
+     *
+     * Slight overallocation, but reduces size of this function.
+     */
+
+    addrsToFix = TclStackAlloc(interp, sizeof(int)*numHandlers);
+    forwardsToFix = TclStackAlloc(interp, sizeof(int)*numHandlers);
+
+    for (i=0 ; i<numHandlers ; i++) {
+       int noTrapError, trapError;
+       const char *p;
+
+       sprintf(buf, "%d", matchCodes[i]);
+       OP(                             DUP);
+       PushLiteral(envPtr, buf, strlen(buf));
+       OP(                             EQ);
+       JUMP4(                          JUMP_FALSE, notCodeJumpSource);
+       if (matchClauses[i]) {
+           Tcl_ListObjLength(NULL, matchClauses[i], &len);
+
+           /*
+            * Match the errorcode according to try/trap rules.
+            */
+
+           LOAD(                       optionsVar);
+           PUSH(                       "-errorcode");
+           OP4(                        DICT_GET, 1);
+           TclAdjustStackDepth(-1, envPtr);
+           OP44(                       LIST_RANGE_IMM, 0, len-1);
+           p = Tcl_GetStringFromObj(matchClauses[i], &len);
+           PushLiteral(envPtr, p, len);
+           OP(                         STR_EQ);
+           JUMP4(                      JUMP_FALSE, notECJumpSource);
+       } else {
+           notECJumpSource = -1; /* LINT */
+       }
+       OP(                             POP);
+
+       /*
+        * There is a finally clause, so we need a fairly complex sequence of
+        * instructions to deal with an on/trap handler because we must call
+        * the finally handler *and* we need to substitute the result from a
+        * failed trap for the result from the main script.
+        */
+
+       if (resultVars[i] >= 0 || handlerTokens[i]) {
+           range = TclCreateExceptRange(CATCH_EXCEPTION_RANGE, envPtr);
+           OP4(                        BEGIN_CATCH4, range);
+           ExceptionRangeStarts(envPtr, range);
+       }
+       if (resultVars[i] >= 0) {
+           LOAD(                       resultVar);
+           STORE(                      resultVars[i]);
+           OP(                         POP);
+           if (optionVars[i] >= 0) {
+               LOAD(                   optionsVar);
+               STORE(                  optionVars[i]);
+               OP(                     POP);
+           }
+
+           if (!handlerTokens[i]) {
+               /*
+                * No handler. Will not be the last handler (that is a
+                * condition that is checked by the caller). Chain to the next
+                * one.
+                */
+
+               ExceptionRangeEnds(envPtr, range);
+               OP(                     END_CATCH);
+               forwardsNeedFixing = 1;
+               JUMP4(                  JUMP, forwardsToFix[i]);
+               goto finishTrapCatchHandling;
+           }
+       } else if (!handlerTokens[i]) {
+           /*
+            * No handler. Will not be the last handler (that condition is
+            * checked by the caller). Chain to the next one.
+            */
+
+           forwardsNeedFixing = 1;
+           JUMP4(                      JUMP, forwardsToFix[i]);
+           goto endOfThisArm;
+       }
+
+       /*
+        * Got a handler. Make sure that any pending patch-up actions from
+        * previous unprocessed handlers are dealt with now that we know where
+        * they are to jump to.
+        */
+
+       if (forwardsNeedFixing) {
+           forwardsNeedFixing = 0;
+           OP1(                        JUMP1, 7);
+           for (j=0 ; j<i ; j++) {
+               if (forwardsToFix[j] == -1) {
+                   continue;
+               }
+               FIXJUMP4(       forwardsToFix[j]);
+               forwardsToFix[j] = -1;
+           }
+           OP4(                        BEGIN_CATCH4, range);
+       }
+       BODY(                           handlerTokens[i], 5+i*4);
+       ExceptionRangeEnds(envPtr, range);
+       PUSH(                           "0");
+       OP(                             PUSH_RETURN_OPTIONS);
+       OP4(                            REVERSE, 3);
+       OP1(                            JUMP1, 5);
+       TclAdjustStackDepth(-3, envPtr);
+       forwardsToFix[i] = -1;
+
+       /*
+        * Error in handler or setting of variables; replace the stored
+        * exception with the new one. Note that we only push this if we have
+        * either a body or some variable setting here. Otherwise this code is
+        * unreachable.
+        */
+
+    finishTrapCatchHandling:
+       ExceptionRangeTarget(envPtr, range, catchOffset);
+       OP(                             PUSH_RETURN_OPTIONS);
+       OP(                             PUSH_RETURN_CODE);
+       OP(                             PUSH_RESULT);
+       OP(                             END_CATCH);
+       STORE(                          resultVar);
+       OP(                             POP);
+       PUSH(                           "1");
+       OP(                             EQ);
+       JUMP1(                          JUMP_FALSE, noTrapError);
+       LOAD(                           optionsVar);
+       PUSH(                           "-during");
+       OP4(                            REVERSE, 3);
+       STORE(                          optionsVar);
+       OP(                             POP);
+       OP44(                           DICT_SET, 1, optionsVar);
+       TclAdjustStackDepth(-1, envPtr);
+       JUMP1(                          JUMP, trapError);
+       FIXJUMP1(               noTrapError);
+       STORE(                          optionsVar);
+       FIXJUMP1(               trapError);
+       /* Skip POP at end; can clean up with subsequent POP */
+       if (i+1 < numHandlers) {
+           OP(                         POP);
+       }
+
+    endOfThisArm:
+       if (i+1 < numHandlers) {
+           JUMP4(                      JUMP, addrsToFix[i]);
+           TclAdjustStackDepth(1, envPtr);
+       }
+       if (matchClauses[i]) {
+           FIXJUMP4(           notECJumpSource);
+       }
+       FIXJUMP4(               notCodeJumpSource);
+    }
+
+    /*
+     * Drop the result code, and fix all the jumps from taken clauses - which
+     * drop the result code as their first action - to point straight after
+     * (i.e., to the start of the finally clause).
+     */
+
+    OP(                                        POP);
+    for (i=0 ; i<numHandlers-1 ; i++) {
+       FIXJUMP4(               addrsToFix[i]);
+    }
+    TclStackFree(interp, forwardsToFix);
+    TclStackFree(interp, addrsToFix);
+
+    /*
+     * Process the finally clause (at last!) Note that we do not wrap this in
+     * error handlers because we would just rethrow immediately anyway. Then
+     * (on normal success) we reissue the exception. Note also that
+     * INST_RETURN_STK can proceed to the next instruction; that'll be the
+     * next command (or some inter-command manipulation).
+     */
+
+    if (!trapZero) {
+       FIXJUMP4(               afterBody);
+    }
+    range = TclCreateExceptRange(CATCH_EXCEPTION_RANGE, envPtr);
+    OP4(                               BEGIN_CATCH4, range);
+    ExceptionRangeStarts(envPtr, range);
+    BODY(                              finallyToken, 3 + 4*numHandlers);
+    ExceptionRangeEnds(envPtr, range);
+    OP(                                        END_CATCH);
+    OP(                                        POP);
+    JUMP1(                             JUMP, finalOK);
+    ExceptionRangeTarget(envPtr, range, catchOffset);
+    OP(                                        PUSH_RESULT);
+    OP(                                        PUSH_RETURN_OPTIONS);
+    OP(                                        PUSH_RETURN_CODE);
+    OP(                                        END_CATCH);
+    PUSH(                              "1");
+    OP(                                        EQ);
+    JUMP1(                             JUMP_FALSE, noFinalError);
+    LOAD(                              optionsVar);
+    PUSH(                              "-during");
+    OP4(                               REVERSE, 3);
+    STORE(                             optionsVar);
+    OP(                                        POP);
+    OP44(                              DICT_SET, 1, optionsVar);
+    TclAdjustStackDepth(-1, envPtr);
+    OP(                                        POP);
+    JUMP1(                             JUMP, finalError);
+    TclAdjustStackDepth(1, envPtr);
+    FIXJUMP1(                  noFinalError);
+    STORE(                             optionsVar);
+    OP(                                        POP);
+    FIXJUMP1(                  finalError);
+    STORE(                             resultVar);
+    OP(                                        POP);
+    FIXJUMP1(                  finalOK);
+    LOAD(                              optionsVar);
+    LOAD(                              resultVar);
+    INVOKE(                            RETURN_STK);
+
+    return TCL_OK;
+}
+
+static int
+IssueTryFinallyInstructions(
+    Tcl_Interp *interp,
+    CompileEnv *envPtr,
+    Tcl_Token *bodyToken,
+    Tcl_Token *finallyToken)
+{
+    DefineLineInformation;     /* TIP #280 */
+    int range, jumpOK, jumpSplice;
+
+    /*
+     * Note that this one is simple enough that we can issue it without
+     * needing a local variable table, making it a universal compilation.
+     */
+
+    range = TclCreateExceptRange(CATCH_EXCEPTION_RANGE, envPtr);
+    OP4(                               BEGIN_CATCH4, range);
+    ExceptionRangeStarts(envPtr, range);
+    BODY(                              bodyToken, 1);
+    ExceptionRangeEnds(envPtr, range);
+    OP1(                               JUMP1, 3);
+    TclAdjustStackDepth(-1, envPtr);
+    ExceptionRangeTarget(envPtr, range, catchOffset);
+    OP(                                        PUSH_RESULT);
+    OP(                                        PUSH_RETURN_OPTIONS);
+    OP(                                        END_CATCH);
+
+    range = TclCreateExceptRange(CATCH_EXCEPTION_RANGE, envPtr);
+    OP4(                               BEGIN_CATCH4, range);
+    ExceptionRangeStarts(envPtr, range);
+    BODY(                              finallyToken, 3);
+    ExceptionRangeEnds(envPtr, range);
+    OP(                                        END_CATCH);
+    OP(                                        POP);
+    JUMP1(                             JUMP, jumpOK);
+    ExceptionRangeTarget(envPtr, range, catchOffset);
+    OP(                                        PUSH_RESULT);
+    OP(                                        PUSH_RETURN_OPTIONS);
+    OP(                                        PUSH_RETURN_CODE);
+    OP(                                        END_CATCH);
+    PUSH(                              "1");
+    OP(                                        EQ);
+    JUMP1(                             JUMP_FALSE, jumpSplice);
+    PUSH(                              "-during");
+    OP4(                               OVER, 3);
+    OP4(                               LIST, 2);
+    OP(                                        LIST_CONCAT);
+    FIXJUMP1(          jumpSplice);
+    OP4(                               REVERSE, 4);
+    OP(                                        POP);
+    OP(                                        POP);
+    OP1(                               JUMP1, 7);
+    FIXJUMP1(          jumpOK);
+    OP4(                               REVERSE, 2);
+    INVOKE(                            RETURN_STK);
+    return TCL_OK;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclCompileUnsetCmd --
+ *
+ *     Procedure called to compile the "unset" command.
+ *
+ * Results:
+ *     Returns TCL_OK for a successful compile. Returns TCL_ERROR to defer
+ *     evaluation to runtime.
+ *
+ * Side effects:
+ *     Instructions are added to envPtr to execute the "unset" command at
+ *     runtime.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TclCompileUnsetCmd(
+    Tcl_Interp *interp,                /* Used for error reporting. */
+    Tcl_Parse *parsePtr,       /* Points to a parse structure for the command
+                                * created by Tcl_ParseCommand. */
+    Command *cmdPtr,           /* Points to defintion of command being
+                                * compiled. */
+    CompileEnv *envPtr)                /* Holds resulting instructions. */
+{
+    DefineLineInformation;     /* TIP #280 */
+    Tcl_Token *varTokenPtr;
+    int isScalar, localIndex, flags = 1, i, varCount = 0, haveFlags = 0;
+
+    /* TODO: Consider support for compiling expanded args. */
+
+    /*
+     * Verify that all words - except the first non-option one - are known at
+     * compile time so that we can handle them without needing to do a nasty
+     * push/rotate. [Bug 3970f54c4e]
+     */
+
+    for (i=1,varTokenPtr=parsePtr->tokenPtr ; i<parsePtr->numWords ; i++) {
+       Tcl_Obj *leadingWord = Tcl_NewObj();
+
+       varTokenPtr = TokenAfter(varTokenPtr);
+       if (!TclWordKnownAtCompileTime(varTokenPtr, leadingWord)) {
+           TclDecrRefCount(leadingWord);
+
+           /*
+            * We can tolerate non-trivial substitutions in the first variable
+            * to be unset. If a '--' or '-nocomplain' was present, anything
+            * goes in that one place! (All subsequent variable names must be
+            * constants since we don't want to have to push them all first.)
+            */
+
+           if (varCount == 0) {
+               if (haveFlags) {
+                   continue;
+               }
+
+               /*
+                * In fact, we're OK as long as we're the first argument *and*
+                * we provably don't start with a '-'. If that is true, then
+                * even if everything else is varying, we still can't be a
+                * flag. Otherwise we'll spill to runtime to place a limit on
+                * the trickiness.
+                */
+
+               if (varTokenPtr->type == TCL_TOKEN_WORD
+                       && varTokenPtr[1].type == TCL_TOKEN_TEXT
+                       && varTokenPtr[1].size > 0
+                       && varTokenPtr[1].start[0] != '-') {
+                   continue;
+               }
+           }
+           return TCL_ERROR;
+       }
+       if (varCount == 0) {
+           const char *bytes;
+           int len;
+
+           bytes = Tcl_GetStringFromObj(leadingWord, &len);
+           if (i == 1 && len == 11 && !strncmp("-nocomplain", bytes, 11)) {
+               flags = 0;
+               haveFlags++;
+           } else if (i == (2 - flags) && len == 2 && !strncmp("--", bytes, 2)) {
+               haveFlags++;
+           } else {
+               varCount++;
+           }
+       } else {
+           varCount++;
+       }
+       TclDecrRefCount(leadingWord);
+    }
+
+    /*
+     * Issue instructions to unset each of the named variables.
+     */
+
+    varTokenPtr = TokenAfter(parsePtr->tokenPtr);
+    for (i=0; i<haveFlags;i++) {
+       varTokenPtr = TokenAfter(varTokenPtr);
+    }
+    for (i=1+haveFlags ; i<parsePtr->numWords ; i++) {
+       /*
+        * Decide if we can use a frame slot for the var/array name or if we
+        * need to emit code to compute and push the name at runtime. We use a
+        * frame slot (entry in the array of local vars) if we are compiling a
+        * procedure body and if the name is simple text that does not include
+        * namespace qualifiers.
+        */
+
+       PushVarNameWord(interp, varTokenPtr, envPtr, 0,
+               &localIndex, &isScalar, i);
+
+       /*
+        * Emit instructions to unset the variable.
+        */
+
+       if (isScalar) {
+           if (localIndex < 0) {
+               OP1(    UNSET_STK, flags);
+           } else {
+               OP14(   UNSET_SCALAR, flags, localIndex);
+           }
+       } else {
+           if (localIndex < 0) {
+               OP1(    UNSET_ARRAY_STK, flags);
+           } else {
+               OP14(   UNSET_ARRAY, flags, localIndex);
+           }
+       }
+
+       varTokenPtr = TokenAfter(varTokenPtr);
+    }
+    PUSH("");
+    return TCL_OK;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclCompileWhileCmd --
+ *
+ *     Procedure called to compile the "while" command.
+ *
+ * Results:
+ *     Returns TCL_OK for a successful compile. Returns TCL_ERROR to defer
+ *     evaluation to runtime.
+ *
+ * Side effects:
+ *     Instructions are added to envPtr to execute the "while" command at
+ *     runtime.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TclCompileWhileCmd(
+    Tcl_Interp *interp,                /* Used for error reporting. */
+    Tcl_Parse *parsePtr,       /* Points to a parse structure for the command
+                                * created by Tcl_ParseCommand. */
+    Command *cmdPtr,           /* Points to defintion of command being
+                                * compiled. */
+    CompileEnv *envPtr)                /* Holds resulting instructions. */
+{
+    DefineLineInformation;     /* TIP #280 */
+    Tcl_Token *testTokenPtr, *bodyTokenPtr;
+    JumpFixup jumpEvalCondFixup;
+    int testCodeOffset, bodyCodeOffset, jumpDist, range, code, boolVal;
+    int loopMayEnd = 1;                /* This is set to 0 if it is recognized as an
+                                * infinite loop. */
+    Tcl_Obj *boolObj;
+
+    if (parsePtr->numWords != 3) {
+       return TCL_ERROR;
+    }
+
+    /*
+     * If the test expression requires substitutions, don't compile the while
+     * command inline. E.g., the expression might cause the loop to never
+     * execute or execute forever, as in "while "$x < 5" {}".
+     *
+     * Bail out also if the body expression requires substitutions in order to
+     * insure correct behaviour [Bug 219166]
+     */
+
+    testTokenPtr = TokenAfter(parsePtr->tokenPtr);
+    bodyTokenPtr = TokenAfter(testTokenPtr);
+
+    if ((testTokenPtr->type != TCL_TOKEN_SIMPLE_WORD)
+           || (bodyTokenPtr->type != TCL_TOKEN_SIMPLE_WORD)) {
+       return TCL_ERROR;
+    }
+
+    /*
+     * Find out if the condition is a constant.
+     */
+
+    boolObj = Tcl_NewStringObj(testTokenPtr[1].start, testTokenPtr[1].size);
+    Tcl_IncrRefCount(boolObj);
+    code = Tcl_GetBooleanFromObj(NULL, boolObj, &boolVal);
+    TclDecrRefCount(boolObj);
+    if (code == TCL_OK) {
+       if (boolVal) {
+           /*
+            * It is an infinite loop; flag it so that we generate a more
+            * efficient body.
+            */
+
+           loopMayEnd = 0;
+       } else {
+           /*
+            * This is an empty loop: "while 0 {...}" or such. Compile no
+            * bytecodes.
+            */
+
+           goto pushResult;
+       }
+    }
+
+    /*
+     * Create a ExceptionRange record for the loop body. This is used to
+     * implement break and continue.
+     */
+
+    range = TclCreateExceptRange(LOOP_EXCEPTION_RANGE, envPtr);
+
+    /*
+     * Jump to the evaluation of the condition. This code uses the "loop
+     * rotation" optimisation (which eliminates one branch from the loop).
+     * "while cond body" produces then:
+     *       goto A
+     *    B: body                : bodyCodeOffset
+     *    A: cond -> result      : testCodeOffset, continueOffset
+     *       if (result) goto B
+     *
+     * The infinite loop "while 1 body" produces:
+     *    B: body                : all three offsets here
+     *       goto B
+     */
+
+    if (loopMayEnd) {
+       TclEmitForwardJump(envPtr, TCL_UNCONDITIONAL_JUMP,
+               &jumpEvalCondFixup);
+       testCodeOffset = 0;     /* Avoid compiler warning. */
+    } else {
+       /*
+        * Make sure that the first command in the body is preceded by an
+        * INST_START_CMD, and hence counted properly. [Bug 1752146]
+        */
+
+       envPtr->atCmdStart &= ~1;
+       testCodeOffset = CurrentOffset(envPtr);
+    }
+
+    /*
+     * Compile the loop body.
+     */
+
+    bodyCodeOffset = ExceptionRangeStarts(envPtr, range);
+    if (!loopMayEnd) {
+       envPtr->exceptArrayPtr[range].continueOffset = testCodeOffset;
+       envPtr->exceptArrayPtr[range].codeOffset = bodyCodeOffset;
+    }
+    BODY(bodyTokenPtr, 2);
+    ExceptionRangeEnds(envPtr, range);
+    OP(                POP);
+
+    /*
+     * Compile the test expression then emit the conditional jump that
+     * terminates the while. We already know it's a simple word.
+     */
+
+    if (loopMayEnd) {
+       testCodeOffset = CurrentOffset(envPtr);
+       jumpDist = testCodeOffset - jumpEvalCondFixup.codeOffset;
+       if (TclFixupForwardJump(envPtr, &jumpEvalCondFixup, jumpDist, 127)) {
+           bodyCodeOffset += 3;
+           testCodeOffset += 3;
+       }
+       SetLineInformation(1);
+       TclCompileExprWords(interp, testTokenPtr, 1, envPtr);
+
+       jumpDist = CurrentOffset(envPtr) - bodyCodeOffset;
+       if (jumpDist > 127) {
+           TclEmitInstInt4(INST_JUMP_TRUE4, -jumpDist, envPtr);
+       } else {
+           TclEmitInstInt1(INST_JUMP_TRUE1, -jumpDist, envPtr);
+       }
+    } else {
+       jumpDist = CurrentOffset(envPtr) - bodyCodeOffset;
+       if (jumpDist > 127) {
+           TclEmitInstInt4(INST_JUMP4, -jumpDist, envPtr);
+       } else {
+           TclEmitInstInt1(INST_JUMP1, -jumpDist, envPtr);
+       }
+    }
+
+    /*
+     * Set the loop's body, continue and break offsets.
+     */
+
+    envPtr->exceptArrayPtr[range].continueOffset = testCodeOffset;
+    envPtr->exceptArrayPtr[range].codeOffset = bodyCodeOffset;
+    ExceptionRangeTarget(envPtr, range, breakOffset);
+    TclFinalizeLoopExceptionRange(envPtr, range);
+
+    /*
+     * The while command's result is an empty string.
+     */
+
+  pushResult:
+    PUSH("");
+    return TCL_OK;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclCompileYieldCmd --
+ *
+ *     Procedure called to compile the "yield" command.
+ *
+ * Results:
+ *     Returns TCL_OK for a successful compile. Returns TCL_ERROR to defer
+ *     evaluation to runtime.
+ *
+ * Side effects:
+ *     Instructions are added to envPtr to execute the "yield" command at
+ *     runtime.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TclCompileYieldCmd(
+    Tcl_Interp *interp,                /* Used for error reporting. */
+    Tcl_Parse *parsePtr,       /* Points to a parse structure for the command
+                                * created by Tcl_ParseCommand. */
+    Command *cmdPtr,           /* Points to defintion of command being
+                                * compiled. */
+    CompileEnv *envPtr)                /* Holds resulting instructions. */
+{
+    if (parsePtr->numWords < 1 || parsePtr->numWords > 2) {
+       return TCL_ERROR;
+    }
+
+    if (parsePtr->numWords == 1) {
+       PUSH("");
+    } else {
+       DefineLineInformation;  /* TIP #280 */
+       Tcl_Token *valueTokenPtr = TokenAfter(parsePtr->tokenPtr);
+
+       CompileWord(envPtr, valueTokenPtr, interp, 1);
+    }
+    OP(                YIELD);
+    return TCL_OK;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclCompileYieldToCmd --
+ *
+ *     Procedure called to compile the "yieldto" command.
+ *
+ * Results:
+ *     Returns TCL_OK for a successful compile. Returns TCL_ERROR to defer
+ *     evaluation to runtime.
+ *
+ * Side effects:
+ *     Instructions are added to envPtr to execute the "yieldto" command at
+ *     runtime.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TclCompileYieldToCmd(
+    Tcl_Interp *interp,                /* Used for error reporting. */
+    Tcl_Parse *parsePtr,       /* Points to a parse structure for the command
+                                * created by Tcl_ParseCommand. */
+    Command *cmdPtr,           /* Points to defintion of command being
+                                * compiled. */
+    CompileEnv *envPtr)                /* Holds resulting instructions. */
+{
+    DefineLineInformation;     /* TIP #280 */
+    Tcl_Token *tokenPtr = TokenAfter(parsePtr->tokenPtr);
+    int i;
+
+    if (parsePtr->numWords < 2) {
+       return TCL_ERROR;
+    }
+
+    OP(                NS_CURRENT);
+    for (i = 1 ; i < parsePtr->numWords ; i++) {
+       CompileWord(envPtr, tokenPtr, interp, i);
+       tokenPtr = TokenAfter(tokenPtr);
+    }
+    OP4(       LIST, i);
+    OP(                YIELD_TO_INVOKE);
+    return TCL_OK;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * CompileUnaryOpCmd --
+ *
+ *     Utility routine to compile the unary operator commands.
+ *
+ * Results:
+ *     Returns TCL_OK for a successful compile. Returns TCL_ERROR to defer
+ *     evaluation to runtime.
+ *
+ * Side effects:
+ *     Instructions are added to envPtr to execute the compiled command at
+ *     runtime.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+CompileUnaryOpCmd(
+    Tcl_Interp *interp,
+    Tcl_Parse *parsePtr,
+    int instruction,
+    CompileEnv *envPtr)
+{
+    DefineLineInformation;     /* TIP #280 */
+    Tcl_Token *tokenPtr;
+
+    if (parsePtr->numWords != 2) {
+       return TCL_ERROR;
+    }
+    tokenPtr = TokenAfter(parsePtr->tokenPtr);
+    CompileWord(envPtr, tokenPtr, interp, 1);
+    TclEmitOpcode(instruction, envPtr);
+    return TCL_OK;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * CompileAssociativeBinaryOpCmd --
+ *
+ *     Utility routine to compile the binary operator commands that accept an
+ *     arbitrary number of arguments, and that are associative operations.
+ *     Because of the associativity, we may combine operations from right to
+ *     left, saving us any effort of re-ordering the arguments on the stack
+ *     after substitutions are completed.
+ *
+ * Results:
+ *     Returns TCL_OK for a successful compile. Returns TCL_ERROR to defer
+ *     evaluation to runtime.
+ *
+ * Side effects:
+ *     Instructions are added to envPtr to execute the compiled command at
+ *     runtime.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+CompileAssociativeBinaryOpCmd(
+    Tcl_Interp *interp,
+    Tcl_Parse *parsePtr,
+    const char *identity,
+    int instruction,
+    CompileEnv *envPtr)
+{
+    DefineLineInformation;     /* TIP #280 */
+    Tcl_Token *tokenPtr = parsePtr->tokenPtr;
+    int words;
+
+    /* TODO: Consider support for compiling expanded args. */
+    for (words=1 ; words<parsePtr->numWords ; words++) {
+       tokenPtr = TokenAfter(tokenPtr);
+       CompileWord(envPtr, tokenPtr, interp, words);
+    }
+    if (parsePtr->numWords <= 2) {
+       PushLiteral(envPtr, identity, -1);
+       words++;
+    }
+    if (words > 3) {
+       /*
+        * Reverse order of arguments to get precise agreement with [expr] in
+        * calcuations, including roundoff errors.
+        */
+
+       OP4(    REVERSE, words-1);
+    }
+    while (--words > 1) {
+       TclEmitOpcode(instruction, envPtr);
+    }
+    return TCL_OK;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * CompileStrictlyBinaryOpCmd --
+ *
+ *     Utility routine to compile the binary operator commands, that strictly
+ *     accept exactly two arguments.
+ *
+ * Results:
+ *     Returns TCL_OK for a successful compile. Returns TCL_ERROR to defer
+ *     evaluation to runtime.
+ *
+ * Side effects:
+ *     Instructions are added to envPtr to execute the compiled command at
+ *     runtime.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+CompileStrictlyBinaryOpCmd(
+    Tcl_Interp *interp,
+    Tcl_Parse *parsePtr,
+    int instruction,
+    CompileEnv *envPtr)
+{
+    if (parsePtr->numWords != 3) {
+       return TCL_ERROR;
+    }
+    return CompileAssociativeBinaryOpCmd(interp, parsePtr,
+           NULL, instruction, envPtr);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * CompileComparisonOpCmd --
+ *
+ *     Utility routine to compile the n-ary comparison operator commands.
+ *
+ * Results:
+ *     Returns TCL_OK for a successful compile. Returns TCL_ERROR to defer
+ *     evaluation to runtime.
+ *
+ * Side effects:
+ *     Instructions are added to envPtr to execute the compiled command at
+ *     runtime.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+CompileComparisonOpCmd(
+    Tcl_Interp *interp,
+    Tcl_Parse *parsePtr,
+    int instruction,
+    CompileEnv *envPtr)
+{
+    DefineLineInformation;     /* TIP #280 */
+    Tcl_Token *tokenPtr;
+
+    /* TODO: Consider support for compiling expanded args. */
+    if (parsePtr->numWords < 3) {
+       PUSH("1");
+    } else if (parsePtr->numWords == 3) {
+       tokenPtr = TokenAfter(parsePtr->tokenPtr);
+       CompileWord(envPtr, tokenPtr, interp, 1);
+       tokenPtr = TokenAfter(tokenPtr);
+       CompileWord(envPtr, tokenPtr, interp, 2);
+       TclEmitOpcode(instruction, envPtr);
+    } else if (envPtr->procPtr == NULL) {
+       /*
+        * No local variable space!
+        */
+
+       return TCL_ERROR;
+    } else {
+       int tmpIndex = AnonymousLocal(envPtr);
+       int words;
+
+       tokenPtr = TokenAfter(parsePtr->tokenPtr);
+       CompileWord(envPtr, tokenPtr, interp, 1);
+       tokenPtr = TokenAfter(tokenPtr);
+       CompileWord(envPtr, tokenPtr, interp, 2);
+       STORE(tmpIndex);
+       TclEmitOpcode(instruction, envPtr);
+       for (words=3 ; words<parsePtr->numWords ;) {
+           LOAD(tmpIndex);
+           tokenPtr = TokenAfter(tokenPtr);
+           CompileWord(envPtr, tokenPtr, interp, words);
+           if (++words < parsePtr->numWords) {
+               STORE(tmpIndex);
+           }
+           TclEmitOpcode(instruction, envPtr);
+       }
+       for (; words>3 ; words--) {
+           OP( BITAND);
+       }
+
+       /*
+        * Drop the value from the temp variable; retaining that reference
+        * might be expensive elsewhere.
+        */
+
+       OP14(   UNSET_SCALAR, 0, tmpIndex);
+    }
+    return TCL_OK;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclCompile*OpCmd --
+ *
+ *     Procedures called to compile the corresponding "::tcl::mathop::*"
+ *     commands. These are all wrappers around the utility operator command
+ *     compiler functions, except for the compilers for subtraction and
+ *     division, which are special.
+ *
+ * Results:
+ *     Returns TCL_OK for a successful compile. Returns TCL_ERROR to defer
+ *     evaluation to runtime.
+ *
+ * Side effects:
+ *     Instructions are added to envPtr to execute the compiled command at
+ *     runtime.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TclCompileInvertOpCmd(
+    Tcl_Interp *interp,
+    Tcl_Parse *parsePtr,
+    Command *cmdPtr,           /* Points to defintion of command being
+                                * compiled. */
+    CompileEnv *envPtr)
+{
+    return CompileUnaryOpCmd(interp, parsePtr, INST_BITNOT, envPtr);
+}
+
+int
+TclCompileNotOpCmd(
+    Tcl_Interp *interp,
+    Tcl_Parse *parsePtr,
+    Command *cmdPtr,           /* Points to defintion of command being
+                                * compiled. */
+    CompileEnv *envPtr)
+{
+    return CompileUnaryOpCmd(interp, parsePtr, INST_LNOT, envPtr);
+}
+
+int
+TclCompileAddOpCmd(
+    Tcl_Interp *interp,
+    Tcl_Parse *parsePtr,
+    Command *cmdPtr,           /* Points to defintion of command being
+                                * compiled. */
+    CompileEnv *envPtr)
+{
+    return CompileAssociativeBinaryOpCmd(interp, parsePtr, "0", INST_ADD,
+           envPtr);
+}
+
+int
+TclCompileMulOpCmd(
+    Tcl_Interp *interp,
+    Tcl_Parse *parsePtr,
+    Command *cmdPtr,           /* Points to defintion of command being
+                                * compiled. */
+    CompileEnv *envPtr)
+{
+    return CompileAssociativeBinaryOpCmd(interp, parsePtr, "1", INST_MULT,
+           envPtr);
+}
+
+int
+TclCompileAndOpCmd(
+    Tcl_Interp *interp,
+    Tcl_Parse *parsePtr,
+    Command *cmdPtr,           /* Points to defintion of command being
+                                * compiled. */
+    CompileEnv *envPtr)
+{
+    return CompileAssociativeBinaryOpCmd(interp, parsePtr, "-1", INST_BITAND,
+           envPtr);
+}
+
+int
+TclCompileOrOpCmd(
+    Tcl_Interp *interp,
+    Tcl_Parse *parsePtr,
+    Command *cmdPtr,           /* Points to defintion of command being
+                                * compiled. */
+    CompileEnv *envPtr)
+{
+    return CompileAssociativeBinaryOpCmd(interp, parsePtr, "0", INST_BITOR,
+           envPtr);
+}
+
+int
+TclCompileXorOpCmd(
+    Tcl_Interp *interp,
+    Tcl_Parse *parsePtr,
+    Command *cmdPtr,           /* Points to defintion of command being
+                                * compiled. */
+    CompileEnv *envPtr)
+{
+    return CompileAssociativeBinaryOpCmd(interp, parsePtr, "0", INST_BITXOR,
+           envPtr);
+}
+
+int
+TclCompilePowOpCmd(
+    Tcl_Interp *interp,
+    Tcl_Parse *parsePtr,
+    Command *cmdPtr,           /* Points to defintion of command being
+                                * compiled. */
+    CompileEnv *envPtr)
+{
+    DefineLineInformation;     /* TIP #280 */
+    Tcl_Token *tokenPtr = parsePtr->tokenPtr;
+    int words;
+
+    /*
+     * This one has its own implementation because the ** operator is the only
+     * one with right associativity.
+     */
+
+    for (words=1 ; words<parsePtr->numWords ; words++) {
+       tokenPtr = TokenAfter(tokenPtr);
+       CompileWord(envPtr, tokenPtr, interp, words);
+    }
+    if (parsePtr->numWords <= 2) {
+       PUSH("1");
+       words++;
+    }
+    while (--words > 1) {
+       TclEmitOpcode(INST_EXPON, envPtr);
+    }
+    return TCL_OK;
+}
+
+int
+TclCompileLshiftOpCmd(
+    Tcl_Interp *interp,
+    Tcl_Parse *parsePtr,
+    Command *cmdPtr,           /* Points to defintion of command being
+                                * compiled. */
+    CompileEnv *envPtr)
+{
+    return CompileStrictlyBinaryOpCmd(interp, parsePtr, INST_LSHIFT, envPtr);
+}
+
+int
+TclCompileRshiftOpCmd(
+    Tcl_Interp *interp,
+    Tcl_Parse *parsePtr,
+    Command *cmdPtr,           /* Points to defintion of command being
+                                * compiled. */
+    CompileEnv *envPtr)
+{
+    return CompileStrictlyBinaryOpCmd(interp, parsePtr, INST_RSHIFT, envPtr);
+}
+
+int
+TclCompileModOpCmd(
+    Tcl_Interp *interp,
+    Tcl_Parse *parsePtr,
+    Command *cmdPtr,           /* Points to defintion of command being
+                                * compiled. */
+    CompileEnv *envPtr)
+{
+    return CompileStrictlyBinaryOpCmd(interp, parsePtr, INST_MOD, envPtr);
+}
+
+int
+TclCompileNeqOpCmd(
+    Tcl_Interp *interp,
+    Tcl_Parse *parsePtr,
+    Command *cmdPtr,           /* Points to defintion of command being
+                                * compiled. */
+    CompileEnv *envPtr)
+{
+    return CompileStrictlyBinaryOpCmd(interp, parsePtr, INST_NEQ, envPtr);
+}
+
+int
+TclCompileStrneqOpCmd(
+    Tcl_Interp *interp,
+    Tcl_Parse *parsePtr,
+    Command *cmdPtr,           /* Points to defintion of command being
+                                * compiled. */
+    CompileEnv *envPtr)
+{
+    return CompileStrictlyBinaryOpCmd(interp, parsePtr, INST_STR_NEQ, envPtr);
+}
+
+int
+TclCompileInOpCmd(
+    Tcl_Interp *interp,
+    Tcl_Parse *parsePtr,
+    Command *cmdPtr,           /* Points to defintion of command being
+                                * compiled. */
+    CompileEnv *envPtr)
+{
+    return CompileStrictlyBinaryOpCmd(interp, parsePtr, INST_LIST_IN, envPtr);
+}
+
+int
+TclCompileNiOpCmd(
+    Tcl_Interp *interp,
+    Tcl_Parse *parsePtr,
+    Command *cmdPtr,           /* Points to defintion of command being
+                                * compiled. */
+    CompileEnv *envPtr)
+{
+    return CompileStrictlyBinaryOpCmd(interp, parsePtr, INST_LIST_NOT_IN,
+           envPtr);
+}
+
+int
+TclCompileLessOpCmd(
+    Tcl_Interp *interp,
+    Tcl_Parse *parsePtr,
+    Command *cmdPtr,           /* Points to defintion of command being
+                                * compiled. */
+    CompileEnv *envPtr)
+{
+    return CompileComparisonOpCmd(interp, parsePtr, INST_LT, envPtr);
+}
+
+int
+TclCompileLeqOpCmd(
+    Tcl_Interp *interp,
+    Tcl_Parse *parsePtr,
+    Command *cmdPtr,           /* Points to defintion of command being
+                                * compiled. */
+    CompileEnv *envPtr)
+{
+    return CompileComparisonOpCmd(interp, parsePtr, INST_LE, envPtr);
+}
+
+int
+TclCompileGreaterOpCmd(
+    Tcl_Interp *interp,
+    Tcl_Parse *parsePtr,
+    Command *cmdPtr,           /* Points to defintion of command being
+                                * compiled. */
+    CompileEnv *envPtr)
+{
+    return CompileComparisonOpCmd(interp, parsePtr, INST_GT, envPtr);
+}
+
+int
+TclCompileGeqOpCmd(
+    Tcl_Interp *interp,
+    Tcl_Parse *parsePtr,
+    Command *cmdPtr,           /* Points to defintion of command being
+                                * compiled. */
+    CompileEnv *envPtr)
+{
+    return CompileComparisonOpCmd(interp, parsePtr, INST_GE, envPtr);
+}
+
+int
+TclCompileEqOpCmd(
+    Tcl_Interp *interp,
+    Tcl_Parse *parsePtr,
+    Command *cmdPtr,           /* Points to defintion of command being
+                                * compiled. */
+    CompileEnv *envPtr)
+{
+    return CompileComparisonOpCmd(interp, parsePtr, INST_EQ, envPtr);
+}
+
+int
+TclCompileStreqOpCmd(
+    Tcl_Interp *interp,
+    Tcl_Parse *parsePtr,
+    Command *cmdPtr,           /* Points to defintion of command being
+                                * compiled. */
+    CompileEnv *envPtr)
+{
+    return CompileComparisonOpCmd(interp, parsePtr, INST_STR_EQ, envPtr);
+}
+\f
+int
+TclCompileMinusOpCmd(
+    Tcl_Interp *interp,
+    Tcl_Parse *parsePtr,
+    Command *cmdPtr,           /* Points to defintion of command being
+                                * compiled. */
+    CompileEnv *envPtr)
+{
+    DefineLineInformation;     /* TIP #280 */
+    Tcl_Token *tokenPtr = parsePtr->tokenPtr;
+    int words;
+
+    /* TODO: Consider support for compiling expanded args. */
+    if (parsePtr->numWords == 1) {
+       /*
+        * Fallback to direct eval to report syntax error.
+        */
+
+       return TCL_ERROR;
+    }
+    for (words=1 ; words<parsePtr->numWords ; words++) {
+       tokenPtr = TokenAfter(tokenPtr);
+       CompileWord(envPtr, tokenPtr, interp, words);
+    }
+    if (words == 2) {
+       TclEmitOpcode(INST_UMINUS, envPtr);
+       return TCL_OK;
+    }
+    if (words == 3) {
+       TclEmitOpcode(INST_SUB, envPtr);
+       return TCL_OK;
+    }
+
+    /*
+     * Reverse order of arguments to get precise agreement with [expr] in
+     * calcuations, including roundoff errors.
+     */
+
+    TclEmitInstInt4(INST_REVERSE, words-1, envPtr);
+    while (--words > 1) {
+       TclEmitInstInt4(INST_REVERSE, 2, envPtr);
+       TclEmitOpcode(INST_SUB, envPtr);
+    }
+    return TCL_OK;
+}
+\f
+int
+TclCompileDivOpCmd(
+    Tcl_Interp *interp,
+    Tcl_Parse *parsePtr,
+    Command *cmdPtr,           /* Points to defintion of command being
+                                * compiled. */
+    CompileEnv *envPtr)
+{
+    DefineLineInformation;     /* TIP #280 */
+    Tcl_Token *tokenPtr = parsePtr->tokenPtr;
+    int words;
+
+    /* TODO: Consider support for compiling expanded args. */
+    if (parsePtr->numWords == 1) {
+       /*
+        * Fallback to direct eval to report syntax error.
+        */
+
+       return TCL_ERROR;
+    }
+    if (parsePtr->numWords == 2) {
+       PUSH("1.0");
+    }
+    for (words=1 ; words<parsePtr->numWords ; words++) {
+       tokenPtr = TokenAfter(tokenPtr);
+       CompileWord(envPtr, tokenPtr, interp, words);
+    }
+    if (words <= 3) {
+       TclEmitOpcode(INST_DIV, envPtr);
+       return TCL_OK;
+    }
+
+    /*
+     * Reverse order of arguments to get precise agreement with [expr] in
+     * calcuations, including roundoff errors.
+     */
+
+    TclEmitInstInt4(INST_REVERSE, words-1, envPtr);
+    while (--words > 1) {
+       TclEmitInstInt4(INST_REVERSE, 2, envPtr);
+       TclEmitOpcode(INST_DIV, envPtr);
+    }
+    return TCL_OK;
+}
+\f
+/*
+ * Local Variables:
+ * mode: c
+ * c-basic-offset: 4
+ * fill-column: 78
+ * End:
+ */