Logo Search packages:      
Sourcecode: tcltk8.0-ja version File versions  Download package

tclCompExpr.c

/* 
 * tclCompExpr.c --
 *
 *    This file contains the code to compile Tcl expressions.
 *
 * Copyright (c) 1996-1997 Sun Microsystems, Inc.
 *
 * See the file "license.terms" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 *
 * RCS: @(#) $Id: tclCompExpr.c,v 1.2 1998/09/14 18:39:58 stanton Exp $
 */

#include "tclInt.h"
#include "tclCompile.h"

/*
 * The stuff below is a bit of a hack so that this file can be used in
 * environments that include no UNIX, i.e. no errno: just arrange to use
 * the errno from tclExecute.c here.
 */

#ifndef TCL_GENERIC_ONLY
#include "tclPort.h"
#else
#define NO_ERRNO_H
#endif

#ifdef NO_ERRNO_H
extern int errno;             /* Use errno from tclExecute.c. */
#define ERANGE 34
#endif

/*
 * Boolean variable that controls whether expression compilation tracing
 * is enabled.
 */

#ifdef TCL_COMPILE_DEBUG
static int traceCompileExpr = 0;
#endif /* TCL_COMPILE_DEBUG */

/*
 * The ExprInfo structure describes the state of compiling an expression.
 * A pointer to an ExprInfo record is passed among the routines in
 * this module.
 */

typedef struct ExprInfo {
    int token;                /* Type of the last token parsed in expr.
                         * See below for definitions. Corresponds
                         * to the characters just before next. */
    int objIndex;       /* If token is a literal value, the index of
                         * an object holding the value in the code's
                         * object table; otherwise is NULL. */
    char *funcName;           /* If the token is FUNC_NAME, points to the
                         * first character of the math function's
                         * name; otherwise is NULL. */
    char *next;               /* Position of the next character to be
                         * scanned in the expression string. */
    char *originalExpr;       /* The entire expression that was originally
                         * passed to Tcl_ExprString et al. */
    char *lastChar;           /* Pointer to terminating null in
                         * originalExpr. */
    int hasOperators;         /* Set 1 if the expr has operators; 0 if
                         * expr is only a primary. If 1 after
                         * compiling an expr, a tryCvtToNumeric
                         * instruction is emitted to convert the
                         * primary to a number if possible. */
    int exprIsJustVarRef;     /* Set 1 if the expr consists of just a
                         * variable reference as in the expression
                         * of "if $b then...". Otherwise 0. If 1 the
                         * expr is compiled out-of-line in order to
                         * implement expr's 2 level substitution
                         * semantics properly. */
    int exprIsComparison;     /* Set 1 if the top-level operator in the
                         * expr is a comparison. Otherwise 0. If 1,
                         * because the operands might be strings,
                         * the expr is compiled out-of-line in order
                         * to implement expr's 2 level substitution
                         * semantics properly. */
} ExprInfo;

/*
 * Definitions of the different tokens that appear in expressions. The order
 * of these must match the corresponding entries in the operatorStrings
 * array below.
 */

#define LITERAL         0
#define FUNC_NAME (LITERAL + 1)
#define OPEN_BRACKET    (LITERAL + 2)
#define CLOSE_BRACKET   (LITERAL + 3)
#define OPEN_PAREN      (LITERAL + 4)
#define CLOSE_PAREN     (LITERAL + 5)
#define DOLLAR          (LITERAL + 6)
#define QUOTE           (LITERAL + 7)
#define COMMA           (LITERAL + 8)
#define END       (LITERAL + 9)
#define UNKNOWN         (LITERAL + 10)

/*
 * Binary operators:
 */

#define MULT            (UNKNOWN + 1)
#define DIVIDE          (MULT + 1)
#define MOD       (MULT + 2)
#define PLUS            (MULT + 3)
#define MINUS           (MULT + 4)
#define LEFT_SHIFT      (MULT + 5)
#define RIGHT_SHIFT     (MULT + 6)
#define LESS            (MULT + 7)
#define GREATER         (MULT + 8)
#define LEQ       (MULT + 9)
#define GEQ       (MULT + 10)
#define EQUAL           (MULT + 11)
#define NEQ       (MULT + 12)
#define BIT_AND         (MULT + 13)
#define BIT_XOR         (MULT + 14)
#define BIT_OR          (MULT + 15)
#define AND       (MULT + 16)
#define OR        (MULT + 17)
#define QUESTY          (MULT + 18)
#define COLON           (MULT + 19)

/*
 * Unary operators. Unary minus and plus are represented by the (binary)
 * tokens MINUS and PLUS.
 */

#define NOT       (COLON + 1)
#define BIT_NOT         (NOT + 1)

/*
 * Mapping from tokens to strings; used for debugging messages. These
 * entries must match the order and number of the token definitions above.
 */

#ifdef TCL_COMPILE_DEBUG
static char *tokenStrings[] = {
    "LITERAL", "FUNCNAME",
    "[", "]", "(", ")", "$", "\"", ",", "END", "UNKNOWN",
    "*", "/", "%", "+", "-",
    "<<", ">>", "<", ">", "<=", ">=", "==", "!=",
    "&", "^", "|", "&&", "||", "?", ":",
    "!", "~"
};
#endif /* TCL_COMPILE_DEBUG */

/*
 * Declarations for local procedures to this file:
 */

static int        CompileAddExpr _ANSI_ARGS_((Tcl_Interp *interp,
                      ExprInfo *infoPtr, int flags,
                      CompileEnv *envPtr));
static int        CompileBitAndExpr _ANSI_ARGS_((Tcl_Interp *interp,
                      ExprInfo *infoPtr, int flags,
                      CompileEnv *envPtr));
static int        CompileBitOrExpr _ANSI_ARGS_((Tcl_Interp *interp,
                      ExprInfo *infoPtr, int flags,
                      CompileEnv *envPtr));
static int        CompileBitXorExpr _ANSI_ARGS_((Tcl_Interp *interp,
                      ExprInfo *infoPtr, int flags,
                      CompileEnv *envPtr));
static int        CompileCondExpr _ANSI_ARGS_((Tcl_Interp *interp,
                      ExprInfo *infoPtr, int flags,
                      CompileEnv *envPtr));
static int        CompileEqualityExpr _ANSI_ARGS_((Tcl_Interp *interp,
                      ExprInfo *infoPtr, int flags,
                      CompileEnv *envPtr));
static int        CompileLandExpr _ANSI_ARGS_((Tcl_Interp *interp,
                      ExprInfo *infoPtr, int flags,
                      CompileEnv *envPtr));
static int        CompileLorExpr _ANSI_ARGS_((Tcl_Interp *interp,
                      ExprInfo *infoPtr, int flags,
                      CompileEnv *envPtr));
static int        CompileMathFuncCall _ANSI_ARGS_((Tcl_Interp *interp,
                      ExprInfo *infoPtr, int flags,
                      CompileEnv *envPtr));
static int        CompileMultiplyExpr _ANSI_ARGS_((Tcl_Interp *interp,
                      ExprInfo *infoPtr, int flags,
                      CompileEnv *envPtr));
static int        CompilePrimaryExpr _ANSI_ARGS_((Tcl_Interp *interp,
                      ExprInfo *infoPtr, int flags,
                      CompileEnv *envPtr));
static int        CompileRelationalExpr _ANSI_ARGS_((
                      Tcl_Interp *interp, ExprInfo *infoPtr,
                      int flags, CompileEnv *envPtr));
static int        CompileShiftExpr _ANSI_ARGS_((Tcl_Interp *interp,
                      ExprInfo *infoPtr, int flags,
                      CompileEnv *envPtr));
static int        CompileUnaryExpr _ANSI_ARGS_((Tcl_Interp *interp,
                      ExprInfo *infoPtr, int flags,
                      CompileEnv *envPtr));
static int        GetToken _ANSI_ARGS_((Tcl_Interp *interp,
                      ExprInfo *infoPtr, CompileEnv *envPtr));

/*
 * Macro used to debug the execution of the recursive descent parser used
 * to compile expressions.
 */

#ifdef TCL_COMPILE_DEBUG
#define HERE(production, level) \
    if (traceCompileExpr) { \
      fprintf(stderr, "%*s%s: token=%s, next=\"%.20s\"\n", \
            (level), " ", (production), tokenStrings[infoPtr->token], \
            infoPtr->next); \
    }
#else
#define HERE(production, level)
#endif /* TCL_COMPILE_DEBUG */

/*
 *----------------------------------------------------------------------
 *
 * TclCompileExpr --
 *
 *    This procedure compiles a string containing a Tcl expression into
 *    Tcl bytecodes. This procedure is the top-level interface to the
 *    the expression compilation module, and is used by such public
 *    procedures as Tcl_ExprString, Tcl_ExprStringObj, Tcl_ExprLong,
 *    Tcl_ExprDouble, Tcl_ExprBoolean, and Tcl_ExprBooleanObj.
 *
 *    Note that the topmost recursive-descent parsing routine used by
 *    TclCompileExpr to compile expressions is called "CompileCondExpr"
 *    and not, e.g., "CompileExpr". This is done to avoid an extra
 *    procedure call since such a procedure would only return the result
 *    of calling CompileCondExpr. Other recursive-descent procedures
 *    that need to parse expressions also call CompileCondExpr.
 *
 * Results:
 *    The return value is TCL_OK on a successful compilation and TCL_ERROR
 *    on failure. If TCL_ERROR is returned, then the interpreter's result
 *    contains an error message.
 *
 *    envPtr->termOffset is filled in with the offset of the character in
 *    "string" just after the last one successfully processed; this might
 *    be the offset of the ']' (if flags & TCL_BRACKET_TERM), or the
 *    offset of the '\0' at the end of the string.
 *
 *    envPtr->maxStackDepth is updated with the maximum number of stack
 *    elements needed to execute the expression.
 *
 *    envPtr->exprIsJustVarRef is set 1 if the expression consisted of
 *    a single variable reference as in the expression of "if $b then...".
 *    Otherwise it is set 0. This is used to implement Tcl's two level
 *    expression substitution semantics properly.
 *
 *    envPtr->exprIsComparison is set 1 if the top-level operator in the
 *    expr is a comparison. Otherwise it is set 0. If 1, because the
 *    operands might be strings, the expr is compiled out-of-line in order
 *    to implement expr's 2 level substitution semantics properly.
 *
 * Side effects:
 *    Adds instructions to envPtr to evaluate the expression at runtime.
 *
 *----------------------------------------------------------------------
 */

int
TclCompileExpr(interp, string, lastChar, flags, envPtr)
    Tcl_Interp *interp;       /* Used for error reporting. */
    char *string;       /* The source string to compile. */
    char *lastChar;           /* Pointer to terminating character of
                         * string. */
    int flags;                /* Flags to control compilation (same as
                         * passed to Tcl_Eval). */
    CompileEnv *envPtr;       /* Holds resulting instructions. */
{
    Interp *iPtr = (Interp *) interp;
    ExprInfo info;
    int maxDepth = 0;         /* Maximum number of stack elements needed
                         * to execute the expression. */
    int result;

#ifdef TCL_COMPILE_DEBUG
    if (traceCompileExpr) {
      fprintf(stderr, "expr: string=\"%.30s\"\n", string);
    }
#endif /* TCL_COMPILE_DEBUG */

    /*
     * Register the builtin math functions the first time an expression is
     * compiled.
     */

    if (!(iPtr->flags & EXPR_INITIALIZED)) {
      BuiltinFunc *funcPtr;
      Tcl_HashEntry *hPtr;
      MathFunc *mathFuncPtr;
      int i;

      iPtr->flags |= EXPR_INITIALIZED;
      i = 0;
      for (funcPtr = builtinFuncTable; funcPtr->name != NULL; funcPtr++) {
          Tcl_CreateMathFunc(interp, funcPtr->name,
                funcPtr->numArgs, funcPtr->argTypes,
                (Tcl_MathProc *) NULL, (ClientData) 0);
          
          hPtr = Tcl_FindHashEntry(&iPtr->mathFuncTable, funcPtr->name);
          if (hPtr == NULL) {
            panic("TclCompileExpr: Tcl_CreateMathFunc incorrectly registered '%s'", funcPtr->name);
            return TCL_ERROR;
          }
          mathFuncPtr = (MathFunc *) Tcl_GetHashValue(hPtr);
          mathFuncPtr->builtinFuncIndex = i;
          i++;
      }
    }

    info.token = UNKNOWN;
    info.objIndex = -1;
    info.funcName = NULL;
    info.next = string;
    info.originalExpr = string;
    info.lastChar = lastChar;
    info.hasOperators = 0;
    info.exprIsJustVarRef = 1;      /* will be set 0 if anything else is seen */
    info.exprIsComparison = 0;      /* set 1 if topmost operator is <,==,etc. */

    /*
     * Get the first token then compile an expression.
     */

    result = GetToken(interp, &info, envPtr);
    if (result != TCL_OK) {
      goto done;
    }
    
    result = CompileCondExpr(interp, &info, flags, envPtr);
    if (result != TCL_OK) {
      goto done;
    }
    if (info.token != END) {
      Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
            "syntax error in expression \"", string, "\"", (char *) NULL);
      result = TCL_ERROR;
      goto done;
    }
    if (!info.hasOperators) {
      /*
       * Attempt to convert the primary's object to an int or double.
       * This is done in order to support Tcl's policy of interpreting
       * operands if at all possible as first integers, else
       * floating-point numbers.
       */
      
      TclEmitOpcode(INST_TRY_CVT_TO_NUMERIC, envPtr);
    }
    maxDepth = envPtr->maxStackDepth;

    done:
    envPtr->termOffset = (info.next - string);
    envPtr->maxStackDepth = maxDepth;
    envPtr->exprIsJustVarRef = info.exprIsJustVarRef;
    envPtr->exprIsComparison = info.exprIsComparison;
    return result;
}

/*
 *----------------------------------------------------------------------
 *
 * CompileCondExpr --
 *
 *    This procedure compiles a Tcl conditional expression:
 *    condExpr ::= lorExpr ['?' condExpr ':' condExpr]
 *
 *    Note that this is the topmost recursive-descent parsing routine used
 *    by TclCompileExpr to compile expressions. It does not call an
 *    separate, higher-level "CompileExpr" procedure. This avoids an extra
 *    procedure call since such a procedure would only return the result
 *    of calling CompileCondExpr. Other recursive-descent procedures that
 *    need to parse expressions also call CompileCondExpr.
 *
 * Results:
 *    The return value is TCL_OK on a successful compilation and TCL_ERROR
 *    on failure. If TCL_ERROR is returned, then the interpreter's result
 *    contains an error message.
 *
 *    envPtr->maxStackDepth is updated with the maximum number of stack
 *    elements needed to execute the expression.
 *
 * Side effects:
 *    Adds instructions to envPtr to evaluate the expression at runtime.
 *
 *----------------------------------------------------------------------
 */

static int
CompileCondExpr(interp, infoPtr, flags, envPtr)
    Tcl_Interp *interp;       /* Used for error reporting. */
    ExprInfo *infoPtr;        /* Describes the compilation state for the
                         * expression being compiled. */
    int flags;                /* Flags to control compilation (same as
                         * passed to Tcl_Eval). */
    CompileEnv *envPtr;       /* Holds resulting instructions. */
{
    int maxDepth = 0;         /* Maximum number of stack elements needed
                         * to execute the expression. */
    JumpFixup jumpAroundThenFixup, jumpAroundElseFixup;
                        /* Used to update or replace one-byte jumps
                         * around the then and else expressions when
                         * their target PCs are determined. */
    int elseCodeOffset, currCodeOffset, jumpDist, result;
    
    HERE("condExpr", 1);
    result = CompileLorExpr(interp, infoPtr, flags, envPtr);
    if (result != TCL_OK) {
      goto done;
    }
    maxDepth = envPtr->maxStackDepth;
    
    if (infoPtr->token == QUESTY) {
      result = GetToken(interp, infoPtr, envPtr); /* skip over the '?' */
      if (result != TCL_OK) {
          goto done;
      }

      /*
       * Emit the jump around the "then" clause to the "else" condExpr if
       * the test was false. We emit a one byte (relative) jump here, and
       * replace it later with a four byte jump if the jump target is more
       * than 127 bytes away.
       */

      TclEmitForwardJump(envPtr, TCL_FALSE_JUMP, &jumpAroundThenFixup);

      /*
       * Compile the "then" expression. Note that if a subexpression
       * is only a primary, we need to try to convert it to numeric.
       * This is done in order to support Tcl's policy of interpreting
       * operands if at all possible as first integers, else
       * floating-point numbers.
       */

      infoPtr->hasOperators = 0;
      infoPtr->exprIsJustVarRef = 0;
      infoPtr->exprIsComparison = 0;
      result = CompileCondExpr(interp, infoPtr, flags, envPtr);
      if (result != TCL_OK) {
          goto done;
      }
      maxDepth = TclMax(envPtr->maxStackDepth, maxDepth);
      if (infoPtr->token != COLON) {
          Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
                "syntax error in expression \"", infoPtr->originalExpr,
                "\"", (char *) NULL);
          result = TCL_ERROR;
          goto done;
      }
      if (!infoPtr->hasOperators) {
          TclEmitOpcode(INST_TRY_CVT_TO_NUMERIC, envPtr);
      }
      result = GetToken(interp, infoPtr, envPtr); /* skip over the ':' */
      if (result != TCL_OK) {
          goto done;
      }

      /*
       * Emit an unconditional jump around the "else" condExpr.
       */

      TclEmitForwardJump(envPtr, TCL_UNCONDITIONAL_JUMP,
              &jumpAroundElseFixup);

      /*
       * Compile the "else" expression.
       */

      infoPtr->hasOperators = 0;
      elseCodeOffset = TclCurrCodeOffset();
      result = CompileCondExpr(interp, infoPtr, flags, envPtr);
      if (result != TCL_OK) {
          goto done;
      }
      maxDepth = TclMax(envPtr->maxStackDepth, maxDepth);
      if (!infoPtr->hasOperators) {
          TclEmitOpcode(INST_TRY_CVT_TO_NUMERIC, envPtr);
      }

      /*
       * Fix up the second jump: the unconditional jump around the "else"
       * expression. If the distance is too great (> 127 bytes), replace
       * it with a four byte instruction and move the instructions after
       * the jump down.
       */

      currCodeOffset = TclCurrCodeOffset();
      jumpDist = (currCodeOffset - jumpAroundElseFixup.codeOffset);
      if (TclFixupForwardJump(envPtr, &jumpAroundElseFixup, jumpDist, 127)) {
          /*
           * Update the else expression's starting code offset since it
           * moved down 3 bytes too.
           */
          
          elseCodeOffset += 3;
      }
      
      /*
       * Now fix up the first branch: the jumpFalse after the test. If the
       * distance is too great, replace it with a four byte instruction
       * and update the code offsets for the commands in both the "then"
       * and "else" expressions.
       */

      jumpDist = (elseCodeOffset - jumpAroundThenFixup.codeOffset);
      TclFixupForwardJump(envPtr, &jumpAroundThenFixup, jumpDist, 127);

      infoPtr->hasOperators = 1;

      /*
       * A comparison is not the top-level operator in this expression.
       */

      infoPtr->exprIsComparison = 0;
    }

    done:
    envPtr->maxStackDepth = maxDepth;
    return result;
}

/*
 *----------------------------------------------------------------------
 *
 * CompileLorExpr --
 *
 *    This procedure compiles a Tcl logical or expression:
 *    lorExpr ::= landExpr {'||' landExpr}
 *
 * Results:
 *    The return value is TCL_OK on a successful compilation and TCL_ERROR
 *    on failure. If TCL_ERROR is returned, then the interpreter's result
 *    contains an error message.
 *
 *    envPtr->maxStackDepth is updated with the maximum number of stack
 *    elements needed to execute the expression.
 *
 * Side effects:
 *    Adds instructions to envPtr to evaluate the expression at runtime.
 *
 *----------------------------------------------------------------------
 */

static int
CompileLorExpr(interp, infoPtr, flags, envPtr)
    Tcl_Interp *interp;       /* Used for error reporting. */
    ExprInfo *infoPtr;        /* Describes the compilation state for the
                         * expression being compiled. */
    int flags;                /* Flags to control compilation (same as
                         * passed to Tcl_Eval). */
    CompileEnv *envPtr;       /* Holds resulting instructions. */
{
    int maxDepth;       /* Maximum number of stack elements needed
                         * to execute the expression. */
    JumpFixupArray jumpFixupArray;
                        /* Used to fix up the forward "short
                         * circuit" jump after each or-ed
                         * subexpression to just after the last
                         * subexpression. */
    JumpFixup jumpTrueFixup, jumpFixup;
                        /* Used to emit the jumps in the code to
                         * convert the first operand to a 0 or 1. */
    int fixupIndex, jumpDist, currCodeOffset, objIndex, j, result;
    Tcl_Obj *objPtr;
    
    HERE("lorExpr", 2);
    result = CompileLandExpr(interp, infoPtr, flags, envPtr);
    if ((result != TCL_OK) || (infoPtr->token != OR)) {
      return result;          /* envPtr->maxStackDepth is already set */
    }

    infoPtr->hasOperators = 1;
    infoPtr->exprIsJustVarRef = 0;
    maxDepth = envPtr->maxStackDepth;
    TclInitJumpFixupArray(&jumpFixupArray);
    while (infoPtr->token == OR) {
      result = GetToken(interp, infoPtr, envPtr); /* skip over the '||' */
      if (result != TCL_OK) {
          goto done;
      }

      if (jumpFixupArray.next == 0) {
          /*
           * Just the first "lor" operand is on the stack. The following
           * is slightly ugly: we need to convert that first "lor" operand
           * to a "0" or "1" to get the correct result if it is nonzero.
           * Eventually we'll use a new instruction for this.
           */

          TclEmitForwardJump(envPtr, TCL_TRUE_JUMP, &jumpTrueFixup);
          
          objIndex = TclObjIndexForString("0", 1, /*allocStrRep*/ 0,
                                  /*inHeap*/ 0, envPtr);
          objPtr = envPtr->objArrayPtr[objIndex];

          Tcl_InvalidateStringRep(objPtr);
          objPtr->internalRep.longValue = 0;
          objPtr->typePtr = &tclIntType;
          
          TclEmitPush(objIndex, envPtr);
          TclEmitForwardJump(envPtr, TCL_UNCONDITIONAL_JUMP, &jumpFixup);

          jumpDist = (TclCurrCodeOffset() - jumpTrueFixup.codeOffset);
          if (TclFixupForwardJump(envPtr, &jumpTrueFixup, jumpDist, 127)) {
            panic("CompileLorExpr: bad jump distance %d\n", jumpDist);
          }
          objIndex = TclObjIndexForString("1", 1, /*allocStrRep*/ 0,
                                    /*inHeap*/ 0, envPtr);
          objPtr = envPtr->objArrayPtr[objIndex];

          Tcl_InvalidateStringRep(objPtr);
          objPtr->internalRep.longValue = 1;
          objPtr->typePtr = &tclIntType;
          
          TclEmitPush(objIndex, envPtr);

          jumpDist = (TclCurrCodeOffset() - jumpFixup.codeOffset);
          if (TclFixupForwardJump(envPtr, &jumpFixup, jumpDist, 127)) {
            panic("CompileLorExpr: bad jump distance %d\n", jumpDist);
          }
      }

      /*
       * Duplicate the value on top of the stack to prevent the jump from
       * consuming it.
       */

      TclEmitOpcode(INST_DUP, envPtr);

      /*
       * Emit the "short circuit" jump around the rest of the lorExp if
       * the previous expression was true. We emit a one byte (relative)
       * jump here, and replace it later with a four byte jump if the jump
       * target is more than 127 bytes away.
       */

      if (jumpFixupArray.next == jumpFixupArray.end) {
          TclExpandJumpFixupArray(&jumpFixupArray);
      }
      fixupIndex = jumpFixupArray.next;
      jumpFixupArray.next++;
      TclEmitForwardJump(envPtr, TCL_TRUE_JUMP,
              &(jumpFixupArray.fixup[fixupIndex]));
      
      /*
       * Compile the subexpression.
       */

      result = CompileLandExpr(interp, infoPtr, flags, envPtr);
      if (result != TCL_OK) {
          goto done;
      }
      maxDepth = TclMax((envPtr->maxStackDepth + 1), maxDepth);

      /*
       * Emit a "logical or" instruction. This does not try to "short-
       * circuit" the evaluation of both operands of a Tcl "||" operator,
       * but instead ensures that we either have a "1" or a "0" result.
       */

      TclEmitOpcode(INST_LOR, envPtr);
    }

    /*
     * Now that we know the target of the forward jumps, update the jumps
     * with the correct distance. Also, if the distance is too great (> 127
     * bytes), replace the jump with a four byte instruction and move the
     * instructions after the jump down.
     */
    
    for (j = jumpFixupArray.next;  j > 0;  j--) {
      fixupIndex = (j - 1);   /* process closest jump first */
      currCodeOffset = TclCurrCodeOffset();
      jumpDist = (currCodeOffset - jumpFixupArray.fixup[fixupIndex].codeOffset);
      TclFixupForwardJump(envPtr, &(jumpFixupArray.fixup[fixupIndex]), jumpDist, 127);
    }

    /*
     * We get here only if one or more ||'s appear as top-level operators.
     */

    done:
    infoPtr->exprIsComparison = 0;
    TclFreeJumpFixupArray(&jumpFixupArray);
    envPtr->maxStackDepth = maxDepth;
    return result;
}

/*
 *----------------------------------------------------------------------
 *
 * CompileLandExpr --
 *
 *    This procedure compiles a Tcl logical and expression:
 *    landExpr ::= bitOrExpr {'&&' bitOrExpr}
 *
 * Results:
 *    The return value is TCL_OK on a successful compilation and TCL_ERROR
 *    on failure. If TCL_ERROR is returned, then the interpreter's result
 *    contains an error message.
 *
 *    envPtr->maxStackDepth is updated with the maximum number of stack
 *    elements needed to execute the expression.
 *
 * Side effects:
 *    Adds instructions to envPtr to evaluate the expression at runtime.
 *
 *----------------------------------------------------------------------
 */

static int
CompileLandExpr(interp, infoPtr, flags, envPtr)
    Tcl_Interp *interp;       /* Used for error reporting. */
    ExprInfo *infoPtr;        /* Describes the compilation state for the
                         * expression being compiled. */
    int flags;                /* Flags to control compilation (same as
                         * passed to Tcl_Eval). */
    CompileEnv *envPtr;       /* Holds resulting instructions. */
{
    int maxDepth;       /* Maximum number of stack elements needed
                         * to execute the expression. */
    JumpFixupArray jumpFixupArray;
                        /* Used to fix up the forward "short
                         * circuit" jump after each and-ed
                         * subexpression to just after the last
                         * subexpression. */
    JumpFixup jumpTrueFixup, jumpFixup;
                        /* Used to emit the jumps in the code to
                         * convert the first operand to a 0 or 1. */
    int fixupIndex, jumpDist, currCodeOffset, objIndex, j, result;
    Tcl_Obj *objPtr;

    HERE("landExpr", 3);
    result = CompileBitOrExpr(interp, infoPtr, flags, envPtr);
    if ((result != TCL_OK) || (infoPtr->token != AND)) {
      return result;          /* envPtr->maxStackDepth is already set */
    }

    infoPtr->hasOperators = 1;
    infoPtr->exprIsJustVarRef = 0;
    maxDepth = envPtr->maxStackDepth;
    TclInitJumpFixupArray(&jumpFixupArray);
    while (infoPtr->token == AND) {
      result = GetToken(interp, infoPtr, envPtr); /* skip over the '&&' */
      if (result != TCL_OK) {
          goto done;
      }

      if (jumpFixupArray.next == 0) {
          /*
           * Just the first "land" operand is on the stack. The following
           * is slightly ugly: we need to convert the first "land" operand
           * to a "0" or "1" to get the correct result if it is
           * nonzero. Eventually we'll use a new instruction.
           */

          TclEmitForwardJump(envPtr, TCL_TRUE_JUMP, &jumpTrueFixup);
           
          objIndex = TclObjIndexForString("0", 1, /*allocStrRep*/ 0,
                                    /*inHeap*/ 0, envPtr);
          objPtr = envPtr->objArrayPtr[objIndex];

          Tcl_InvalidateStringRep(objPtr);
          objPtr->internalRep.longValue = 0;
          objPtr->typePtr = &tclIntType;
          
          TclEmitPush(objIndex, envPtr);
          TclEmitForwardJump(envPtr, TCL_UNCONDITIONAL_JUMP, &jumpFixup);

          jumpDist = (TclCurrCodeOffset() - jumpTrueFixup.codeOffset);
          if (TclFixupForwardJump(envPtr, &jumpTrueFixup, jumpDist, 127)) {
            panic("CompileLandExpr: bad jump distance %d\n", jumpDist);
          }
          objIndex = TclObjIndexForString("1", 1, /*allocStrRep*/ 0,
                                    /*inHeap*/ 0, envPtr);
          objPtr = envPtr->objArrayPtr[objIndex];

          Tcl_InvalidateStringRep(objPtr);
          objPtr->internalRep.longValue = 1;
          objPtr->typePtr = &tclIntType;
          
          TclEmitPush(objIndex, envPtr);

          jumpDist = (TclCurrCodeOffset() - jumpFixup.codeOffset);
          if (TclFixupForwardJump(envPtr, &jumpFixup, jumpDist, 127)) {
            panic("CompileLandExpr: bad jump distance %d\n", jumpDist);
          }
      }

      /*
       * Duplicate the value on top of the stack to prevent the jump from
       * consuming it.
       */

      TclEmitOpcode(INST_DUP, envPtr);

      /*
       * Emit the "short circuit" jump around the rest of the landExp if
       * the previous expression was false. We emit a one byte (relative)
       * jump here, and replace it later with a four byte jump if the jump
       * target is more than 127 bytes away.
       */

      if (jumpFixupArray.next == jumpFixupArray.end) {
          TclExpandJumpFixupArray(&jumpFixupArray);
      }
      fixupIndex = jumpFixupArray.next;
      jumpFixupArray.next++;
      TclEmitForwardJump(envPtr, TCL_FALSE_JUMP,
            &(jumpFixupArray.fixup[fixupIndex]));
      
      /*
       * Compile the subexpression.
       */

      result = CompileBitOrExpr(interp, infoPtr, flags, envPtr);
      if (result != TCL_OK) {
          goto done;
      }
      maxDepth = TclMax((envPtr->maxStackDepth + 1), maxDepth);

      /*
       * Emit a "logical and" instruction. This does not try to "short-
       * circuit" the evaluation of both operands of a Tcl "&&" operator,
       * but instead ensures that we either have a "1" or a "0" result.
       */

      TclEmitOpcode(INST_LAND, envPtr);
    }

    /*
     * Now that we know the target of the forward jumps, update the jumps
     * with the correct distance. Also, if the distance is too great (> 127
     * bytes), replace the jump with a four byte instruction and move the
     * instructions after the jump down.
     */
    
    for (j = jumpFixupArray.next;  j > 0;  j--) {
      fixupIndex = (j - 1);   /* process closest jump first */
      currCodeOffset = TclCurrCodeOffset();
      jumpDist = (currCodeOffset - jumpFixupArray.fixup[fixupIndex].codeOffset);
      TclFixupForwardJump(envPtr, &(jumpFixupArray.fixup[fixupIndex]),
              jumpDist, 127);
    }

    /*
     * We get here only if one or more &&'s appear as top-level operators.
     */

    done:
    infoPtr->exprIsComparison = 0;
    TclFreeJumpFixupArray(&jumpFixupArray);
    envPtr->maxStackDepth = maxDepth;
    return result;
}

/*
 *----------------------------------------------------------------------
 *
 * CompileBitOrExpr --
 *
 *    This procedure compiles a Tcl bitwise or expression:
 *    bitOrExpr ::= bitXorExpr {'|' bitXorExpr}
 *
 * Results:
 *    The return value is TCL_OK on a successful compilation and TCL_ERROR
 *    on failure. If TCL_ERROR is returned, then the interpreter's result
 *    contains an error message.
 *
 *    envPtr->maxStackDepth is updated with the maximum number of stack
 *    elements needed to execute the expression.
 *
 * Side effects:
 *    Adds instructions to envPtr to evaluate the expression at runtime.
 *
 *----------------------------------------------------------------------
 */

static int
CompileBitOrExpr(interp, infoPtr, flags, envPtr)
    Tcl_Interp *interp;       /* Used for error reporting. */
    ExprInfo *infoPtr;        /* Describes the compilation state for the
                         * expression being compiled. */
    int flags;                /* Flags to control compilation (same as
                         * passed to Tcl_Eval). */
    CompileEnv *envPtr;       /* Holds resulting instructions. */
{
    int maxDepth = 0;         /* Maximum number of stack elements needed
                         * to execute the expression. */
    int result;

    HERE("bitOrExpr", 4);
    result = CompileBitXorExpr(interp, infoPtr, flags, envPtr);
    if (result != TCL_OK) {
      goto done;
    }
    maxDepth = envPtr->maxStackDepth;
    
    while (infoPtr->token == BIT_OR) {
      infoPtr->hasOperators = 1;
      infoPtr->exprIsJustVarRef = 0;
      result = GetToken(interp, infoPtr, envPtr); /* skip over the '|' */
      if (result != TCL_OK) {
          goto done;
      }

      result = CompileBitXorExpr(interp, infoPtr, flags, envPtr);
      if (result != TCL_OK) {
          goto done;
      }
      maxDepth = TclMax((envPtr->maxStackDepth + 1), maxDepth);
      
      TclEmitOpcode(INST_BITOR, envPtr);

      /*
       * A comparison is not the top-level operator in this expression.
       */

      infoPtr->exprIsComparison = 0;
    }

    done:
    envPtr->maxStackDepth = maxDepth;
    return result;
}

/*
 *----------------------------------------------------------------------
 *
 * CompileBitXorExpr --
 *
 *    This procedure compiles a Tcl bitwise exclusive or expression:
 *    bitXorExpr ::= bitAndExpr {'^' bitAndExpr}
 *
 * Results:
 *    The return value is TCL_OK on a successful compilation and TCL_ERROR
 *    on failure. If TCL_ERROR is returned, then the interpreter's result
 *    contains an error message.
 *
 *    envPtr->maxStackDepth is updated with the maximum number of stack
 *    elements needed to execute the expression.
 *
 * Side effects:
 *    Adds instructions to envPtr to evaluate the expression at runtime.
 *
 *----------------------------------------------------------------------
 */

static int
CompileBitXorExpr(interp, infoPtr, flags, envPtr)
    Tcl_Interp *interp;       /* Used for error reporting. */
    ExprInfo *infoPtr;        /* Describes the compilation state for the
                         * expression being compiled. */
    int flags;                /* Flags to control compilation (same as
                         * passed to Tcl_Eval). */
    CompileEnv *envPtr;       /* Holds resulting instructions. */
{
    int maxDepth = 0;         /* Maximum number of stack elements needed
                         * to execute the expression. */
    int result;

    HERE("bitXorExpr", 5);
    result = CompileBitAndExpr(interp, infoPtr, flags, envPtr);
    if (result != TCL_OK) {
      goto done;
    }
    maxDepth = envPtr->maxStackDepth;
    
    while (infoPtr->token == BIT_XOR) {
      infoPtr->hasOperators = 1;
      infoPtr->exprIsJustVarRef = 0;
      result = GetToken(interp, infoPtr, envPtr); /* skip over the '^' */
      if (result != TCL_OK) {
          goto done;
      }

      result = CompileBitAndExpr(interp, infoPtr, flags, envPtr);
      if (result != TCL_OK) {
          goto done;
      }
      maxDepth = TclMax((envPtr->maxStackDepth + 1), maxDepth);
      
      TclEmitOpcode(INST_BITXOR, envPtr);

      /*
       * A comparison is not the top-level operator in this expression.
       */

      infoPtr->exprIsComparison = 0;
    }

    done:
    envPtr->maxStackDepth = maxDepth;
    return result;
}

/*
 *----------------------------------------------------------------------
 *
 * CompileBitAndExpr --
 *
 *    This procedure compiles a Tcl bitwise and expression:
 *    bitAndExpr ::= equalityExpr {'&' equalityExpr}
 *
 * Results:
 *    The return value is TCL_OK on a successful compilation and TCL_ERROR
 *    on failure. If TCL_ERROR is returned, then the interpreter's result
 *    contains an error message.
 *
 *    envPtr->maxStackDepth is updated with the maximum number of stack
 *    elements needed to execute the expression.
 *
 * Side effects:
 *    Adds instructions to envPtr to evaluate the expression at runtime.
 *
 *----------------------------------------------------------------------
 */

static int
CompileBitAndExpr(interp, infoPtr, flags, envPtr)
    Tcl_Interp *interp;       /* Used for error reporting. */
    ExprInfo *infoPtr;        /* Describes the compilation state for the
                         * expression being compiled. */
    int flags;                /* Flags to control compilation (same as
                         * passed to Tcl_Eval). */
    CompileEnv *envPtr;       /* Holds resulting instructions. */
{
    int maxDepth = 0;         /* Maximum number of stack elements needed
                         * to execute the expression. */
    int result;

    HERE("bitAndExpr", 6);
    result = CompileEqualityExpr(interp, infoPtr, flags, envPtr);
    if (result != TCL_OK) {
      goto done;
    }
    maxDepth = envPtr->maxStackDepth;
    
    while (infoPtr->token == BIT_AND) {
      infoPtr->hasOperators = 1;
      infoPtr->exprIsJustVarRef = 0;
      result = GetToken(interp, infoPtr, envPtr); /* skip over the '&' */
      if (result != TCL_OK) {
          goto done;
      }

      result = CompileEqualityExpr(interp, infoPtr, flags, envPtr);
      if (result != TCL_OK) {
          goto done;
      }
      maxDepth = TclMax((envPtr->maxStackDepth + 1), maxDepth);
      
      TclEmitOpcode(INST_BITAND, envPtr);

      /*
       * A comparison is not the top-level operator in this expression.
       */

      infoPtr->exprIsComparison = 0;
    }

    done:
    envPtr->maxStackDepth = maxDepth;
    return result;
}

/*
 *----------------------------------------------------------------------
 *
 * CompileEqualityExpr --
 *
 *    This procedure compiles a Tcl equality (inequality) expression:
 *    equalityExpr ::= relationalExpr {('==' | '!=') relationalExpr}
 *
 * Results:
 *    The return value is TCL_OK on a successful compilation and TCL_ERROR
 *    on failure. If TCL_ERROR is returned, then the interpreter's result
 *    contains an error message.
 *
 *    envPtr->maxStackDepth is updated with the maximum number of stack
 *    elements needed to execute the expression.
 *
 * Side effects:
 *    Adds instructions to envPtr to evaluate the expression at runtime.
 *
 *----------------------------------------------------------------------
 */

static int
CompileEqualityExpr(interp, infoPtr, flags, envPtr)
    Tcl_Interp *interp;       /* Used for error reporting. */
    ExprInfo *infoPtr;        /* Describes the compilation state for the
                         * expression being compiled. */
    int flags;                /* Flags to control compilation (same as
                         * passed to Tcl_Eval). */
    CompileEnv *envPtr;       /* Holds resulting instructions. */
{
    int maxDepth = 0;         /* Maximum number of stack elements needed
                         * to execute the expression. */
    int op, result;

    HERE("equalityExpr", 7);
    result = CompileRelationalExpr(interp, infoPtr, flags, envPtr);
    if (result != TCL_OK) {
      goto done;
    }
    maxDepth = envPtr->maxStackDepth;

    op = infoPtr->token;
    while ((op == EQUAL) || (op == NEQ)) {
      infoPtr->hasOperators = 1;
      infoPtr->exprIsJustVarRef = 0;
      result = GetToken(interp, infoPtr, envPtr); /* skip over == or != */
      if (result != TCL_OK) {
          goto done;
      }

      result = CompileRelationalExpr(interp, infoPtr, flags, envPtr);
      if (result != TCL_OK) {
          goto done;
      }
      maxDepth = TclMax((envPtr->maxStackDepth + 1), maxDepth);

      if (op == EQUAL) {
          TclEmitOpcode(INST_EQ, envPtr);
      } else {
          TclEmitOpcode(INST_NEQ, envPtr);
      }
      
      op = infoPtr->token;

      /*
       * A comparison _is_ the top-level operator in this expression.
       */
      
      infoPtr->exprIsComparison = 1;
    }

    done:
    envPtr->maxStackDepth = maxDepth;
    return result;
}

/*
 *----------------------------------------------------------------------
 *
 * CompileRelationalExpr --
 *
 *    This procedure compiles a Tcl relational expression:
 *    relationalExpr ::= shiftExpr {('<' | '>' | '<=' | '>=') shiftExpr}
 *
 * Results:
 *    The return value is TCL_OK on a successful compilation and TCL_ERROR
 *    on failure. If TCL_ERROR is returned, then the interpreter's result
 *    contains an error message.
 *
 *    envPtr->maxStackDepth is updated with the maximum number of stack
 *    elements needed to execute the expression.
 *
 * Side effects:
 *    Adds instructions to envPtr to evaluate the expression at runtime.
 *
 *----------------------------------------------------------------------
 */

static int
CompileRelationalExpr(interp, infoPtr, flags, envPtr)
    Tcl_Interp *interp;       /* Used for error reporting. */
    ExprInfo *infoPtr;        /* Describes the compilation state for the
                         * expression being compiled. */
    int flags;                /* Flags to control compilation (same as
                         * passed to Tcl_Eval). */
    CompileEnv *envPtr;       /* Holds resulting instructions. */
{
    int maxDepth = 0;         /* Maximum number of stack elements needed
                         * to execute the expression. */
    int op, result;

    HERE("relationalExpr", 8);
    result = CompileShiftExpr(interp, infoPtr, flags, envPtr);
    if (result != TCL_OK) {
      goto done;
    }
    maxDepth = envPtr->maxStackDepth;

    op = infoPtr->token;
    while ((op == LESS) || (op == GREATER) || (op == LEQ) || (op == GEQ)) {
      infoPtr->hasOperators = 1;
      infoPtr->exprIsJustVarRef = 0;
      result = GetToken(interp, infoPtr, envPtr); /* skip over the op */
      if (result != TCL_OK) {
          goto done;
      }

      result = CompileShiftExpr(interp, infoPtr, flags, envPtr);
      if (result != TCL_OK) {
          goto done;
      }
      maxDepth = TclMax((envPtr->maxStackDepth + 1), maxDepth);

      switch (op) {
      case LESS:
          TclEmitOpcode(INST_LT, envPtr);
          break;
      case GREATER:
          TclEmitOpcode(INST_GT, envPtr);
          break;
      case LEQ:
          TclEmitOpcode(INST_LE, envPtr);
          break;
      case GEQ:
          TclEmitOpcode(INST_GE, envPtr);
          break;
      }

      op = infoPtr->token;

      /*
       * A comparison _is_ the top-level operator in this expression.
       */
      
      infoPtr->exprIsComparison = 1;
    }

    done:
    envPtr->maxStackDepth = maxDepth;
    return result;
}

/*
 *----------------------------------------------------------------------
 *
 * CompileShiftExpr --
 *
 *    This procedure compiles a Tcl shift expression:
 *    shiftExpr ::= addExpr {('<<' | '>>') addExpr}
 *
 * Results:
 *    The return value is TCL_OK on a successful compilation and TCL_ERROR
 *    on failure. If TCL_ERROR is returned, then the interpreter's result
 *    contains an error message.
 *
 *    envPtr->maxStackDepth is updated with the maximum number of stack
 *    elements needed to execute the expression.
 *
 * Side effects:
 *    Adds instructions to envPtr to evaluate the expression at runtime.
 *
 *----------------------------------------------------------------------
 */

static int
CompileShiftExpr(interp, infoPtr, flags, envPtr)
    Tcl_Interp *interp;       /* Used for error reporting. */
    ExprInfo *infoPtr;        /* Describes the compilation state for the
                         * expression being compiled. */
    int flags;                /* Flags to control compilation (same as
                         * passed to Tcl_Eval). */
    CompileEnv *envPtr;       /* Holds resulting instructions. */
{
    int maxDepth = 0;         /* Maximum number of stack elements needed
                         * to execute the expression. */
    int op, result;

    HERE("shiftExpr", 9);
    result = CompileAddExpr(interp, infoPtr, flags, envPtr);
    if (result != TCL_OK) {
      goto done;
    }
    maxDepth = envPtr->maxStackDepth;

    op = infoPtr->token;
    while ((op == LEFT_SHIFT) || (op == RIGHT_SHIFT)) {
      infoPtr->hasOperators = 1;
      infoPtr->exprIsJustVarRef = 0;
      result = GetToken(interp, infoPtr, envPtr); /* skip over << or >> */
      if (result != TCL_OK) {
          goto done;
      }

      result = CompileAddExpr(interp, infoPtr, flags, envPtr);
      if (result != TCL_OK) {
          goto done;
      }
      maxDepth = TclMax((envPtr->maxStackDepth + 1), maxDepth);

      if (op == LEFT_SHIFT) {
          TclEmitOpcode(INST_LSHIFT, envPtr);
      } else {
          TclEmitOpcode(INST_RSHIFT, envPtr);
      }

      op = infoPtr->token;

      /*
       * A comparison is not the top-level operator in this expression.
       */

      infoPtr->exprIsComparison = 0;
    }

    done:
    envPtr->maxStackDepth = maxDepth;
    return result;
}

/*
 *----------------------------------------------------------------------
 *
 * CompileAddExpr --
 *
 *    This procedure compiles a Tcl addition expression:
 *    addExpr ::= multiplyExpr {('+' | '-') multiplyExpr}
 *
 * Results:
 *    The return value is TCL_OK on a successful compilation and TCL_ERROR
 *    on failure. If TCL_ERROR is returned, then the interpreter's result
 *    contains an error message.
 *
 *    envPtr->maxStackDepth is updated with the maximum number of stack
 *    elements needed to execute the expression.
 *
 * Side effects:
 *    Adds instructions to envPtr to evaluate the expression at runtime.
 *
 *----------------------------------------------------------------------
 */

static int
CompileAddExpr(interp, infoPtr, flags, envPtr)
    Tcl_Interp *interp;       /* Used for error reporting. */
    ExprInfo *infoPtr;        /* Describes the compilation state for the
                         * expression being compiled. */
    int flags;                /* Flags to control compilation (same as
                         * passed to Tcl_Eval). */
    CompileEnv *envPtr;       /* Holds resulting instructions. */
{
    int maxDepth = 0;         /* Maximum number of stack elements needed
                         * to execute the expression. */
    int op, result;

    HERE("addExpr", 10);
    result = CompileMultiplyExpr(interp, infoPtr, flags, envPtr);
    if (result != TCL_OK) {
      goto done;
    }
    maxDepth = envPtr->maxStackDepth;

    op = infoPtr->token;
    while ((op == PLUS) || (op == MINUS)) {
      infoPtr->hasOperators = 1;
      infoPtr->exprIsJustVarRef = 0;
      result = GetToken(interp, infoPtr, envPtr); /* skip over + or - */
      if (result != TCL_OK) {
          goto done;
      }

      result = CompileMultiplyExpr(interp, infoPtr, flags, envPtr);
      if (result != TCL_OK) {
          goto done;
      }
      maxDepth = TclMax((envPtr->maxStackDepth + 1), maxDepth);

      if (op == PLUS) {
          TclEmitOpcode(INST_ADD, envPtr);
      } else {
          TclEmitOpcode(INST_SUB, envPtr);
      }

      op = infoPtr->token;

      /*
       * A comparison is not the top-level operator in this expression.
       */

      infoPtr->exprIsComparison = 0;
    }

    done:
    envPtr->maxStackDepth = maxDepth;
    return result;
}

/*
 *----------------------------------------------------------------------
 *
 * CompileMultiplyExpr --
 *
 *    This procedure compiles a Tcl multiply expression:
 *    multiplyExpr ::= unaryExpr {('*' | '/' | '%') unaryExpr}
 *
 * Results:
 *    The return value is TCL_OK on a successful compilation and TCL_ERROR
 *    on failure. If TCL_ERROR is returned, then the interpreter's result
 *    contains an error message.
 *
 *    envPtr->maxStackDepth is updated with the maximum number of stack
 *    elements needed to execute the expression.
 *
 * Side effects:
 *    Adds instructions to envPtr to evaluate the expression at runtime.
 *
 *----------------------------------------------------------------------
 */

static int
CompileMultiplyExpr(interp, infoPtr, flags, envPtr)
    Tcl_Interp *interp;       /* Used for error reporting. */
    ExprInfo *infoPtr;        /* Describes the compilation state for the
                         * expression being compiled. */
    int flags;                /* Flags to control compilation (same as
                         * passed to Tcl_Eval). */
    CompileEnv *envPtr;       /* Holds resulting instructions. */
{
    int maxDepth = 0;         /* Maximum number of stack elements needed
                         * to execute the expression. */
    int op, result;

    HERE("multiplyExpr", 11);
    result = CompileUnaryExpr(interp, infoPtr, flags, envPtr);
    if (result != TCL_OK) {
      goto done;
    }
    maxDepth = envPtr->maxStackDepth;

    op = infoPtr->token;
    while ((op == MULT) || (op == DIVIDE) || (op == MOD)) {
      infoPtr->hasOperators = 1;
      infoPtr->exprIsJustVarRef = 0;
      result = GetToken(interp, infoPtr, envPtr); /* skip over * or / */
      if (result != TCL_OK) {
          goto done;
      }

      result = CompileUnaryExpr(interp, infoPtr, flags, envPtr);
      if (result != TCL_OK) {
          goto done;
      }
      maxDepth = TclMax((envPtr->maxStackDepth + 1), maxDepth);

      if (op == MULT) {
          TclEmitOpcode(INST_MULT, envPtr);
      } else if (op == DIVIDE) {
          TclEmitOpcode(INST_DIV, envPtr);
      } else {
          TclEmitOpcode(INST_MOD, envPtr);
      }

      op = infoPtr->token;

      /*
       * A comparison is not the top-level operator in this expression.
       */

      infoPtr->exprIsComparison = 0;
    }

    done:
    envPtr->maxStackDepth = maxDepth;
    return result;
}

/*
 *----------------------------------------------------------------------
 *
 * CompileUnaryExpr --
 *
 *    This procedure compiles a Tcl unary expression:
 *    unaryExpr ::= ('+' | '-' | '~' | '!') unaryExpr | primaryExpr
 *
 * Results:
 *    The return value is TCL_OK on a successful compilation and TCL_ERROR
 *    on failure. If TCL_ERROR is returned, then the interpreter's result
 *    contains an error message.
 *
 *    envPtr->maxStackDepth is updated with the maximum number of stack
 *    elements needed to execute the expression.
 *
 * Side effects:
 *    Adds instructions to envPtr to evaluate the expression at runtime.
 *
 *----------------------------------------------------------------------
 */

static int
CompileUnaryExpr(interp, infoPtr, flags, envPtr)
    Tcl_Interp *interp;       /* Used for error reporting. */
    ExprInfo *infoPtr;        /* Describes the compilation state for the
                         * expression being compiled. */
    int flags;                /* Flags to control compilation (same as
                         * passed to Tcl_Eval). */
    CompileEnv *envPtr;       /* Holds resulting instructions. */
{
    int maxDepth = 0;         /* Maximum number of stack elements needed
                         * to execute the expression. */
    int op, result;

    HERE("unaryExpr", 12);
    op = infoPtr->token;
    if ((op == PLUS) || (op == MINUS) || (op == BIT_NOT) || (op == NOT)) {
      infoPtr->hasOperators = 1;
      infoPtr->exprIsJustVarRef = 0;
      result = GetToken(interp, infoPtr, envPtr); /* skip over the op */
      if (result != TCL_OK) {
          goto done;
      }

      result = CompileUnaryExpr(interp, infoPtr, flags, envPtr);
      if (result != TCL_OK) {
          goto done;
      }
      maxDepth = envPtr->maxStackDepth;

      switch (op) {
      case PLUS:
          TclEmitOpcode(INST_UPLUS, envPtr);
          break;
      case MINUS:
          TclEmitOpcode(INST_UMINUS, envPtr);
          break;
      case BIT_NOT:
          TclEmitOpcode(INST_BITNOT, envPtr);
          break;
      case NOT:
          TclEmitOpcode(INST_LNOT, envPtr);
          break;
      }

      /*
       * A comparison is not the top-level operator in this expression.
       */

      infoPtr->exprIsComparison = 0;
    } else {                  /* must be a primaryExpr */
      result = CompilePrimaryExpr(interp, infoPtr, flags, envPtr);
      if (result != TCL_OK) {
          goto done;
      }
      maxDepth = envPtr->maxStackDepth;
    }

    done:
    envPtr->maxStackDepth = maxDepth;
    return result;
}

/*
 *----------------------------------------------------------------------
 *
 * CompilePrimaryExpr --
 *
 *    This procedure compiles a Tcl primary expression:
 *    primaryExpr ::= literal | varReference | quotedString |
 *                '[' command ']' | mathFuncCall | '(' condExpr ')'
 *
 * Results:
 *    The return value is TCL_OK on a successful compilation and TCL_ERROR
 *    on failure. If TCL_ERROR is returned, then the interpreter's result
 *    contains an error message.
 *
 *    envPtr->maxStackDepth is updated with the maximum number of stack
 *    elements needed to execute the expression.
 *
 * Side effects:
 *    Adds instructions to envPtr to evaluate the expression at runtime.
 *
 *----------------------------------------------------------------------
 */

static int
CompilePrimaryExpr(interp, infoPtr, flags, envPtr)
    Tcl_Interp *interp;       /* Used for error reporting. */
    ExprInfo *infoPtr;        /* Describes the compilation state for the
                         * expression being compiled. */
    int flags;                /* Flags to control compilation (same as
                         * passed to Tcl_Eval). */
    CompileEnv *envPtr;       /* Holds resulting instructions. */
{
    int maxDepth = 0;         /* Maximum number of stack elements needed
                         * to execute the expression. */
    int theToken;
    char *dollarPtr, *quotePtr, *cmdPtr, *termPtr;
    int result = TCL_OK;

    /*
     * We emit tryCvtToNumeric instructions after most of these primary
     * expressions in order to support Tcl's policy of interpreting operands
     * as first integers if possible, otherwise floating-point numbers if
     * possible.
     */

    HERE("primaryExpr", 13);
    theToken = infoPtr->token;

    if ((theToken != DOLLAR) && (theToken != OPEN_PAREN)) {
      infoPtr->exprIsJustVarRef = 0;
    }
    switch (theToken) {
    case LITERAL:       /* int, double, or string in braces */
      TclEmitPush(infoPtr->objIndex, envPtr);
      maxDepth = 1;
      break;
      
    case DOLLAR:        /* $var variable reference */
      dollarPtr = (infoPtr->next - 1);
      envPtr->pushSimpleWords = 1;
      result = TclCompileDollarVar(interp, dollarPtr,
            infoPtr->lastChar, flags, envPtr);
      if (result != TCL_OK) {
          goto done;
      }
      maxDepth = envPtr->maxStackDepth;
      infoPtr->next = (dollarPtr + envPtr->termOffset);
      break;
      
    case QUOTE:               /* quotedString */
      quotePtr = infoPtr->next;
      envPtr->pushSimpleWords = 1;
      result = TclCompileQuotes(interp, quotePtr,
            infoPtr->lastChar, '"', flags, envPtr);
      if (result != TCL_OK) {
          goto done;
      }
      maxDepth = envPtr->maxStackDepth;
      infoPtr->next = (quotePtr + envPtr->termOffset);
      break;
      
    case OPEN_BRACKET:        /* '[' command ']' */
      cmdPtr = infoPtr->next;
      envPtr->pushSimpleWords = 1;
      result = TclCompileString(interp, cmdPtr,
            infoPtr->lastChar, (flags | TCL_BRACKET_TERM), envPtr);
      if (result != TCL_OK) {
          goto done;
      }
      termPtr = (cmdPtr + envPtr->termOffset);
      if (*termPtr == ']') {
          infoPtr->next = (termPtr + 1); /* advance over the ']'. */
      } else if (termPtr == infoPtr->lastChar) {
          /*
           * Missing ] at end of nested command.
           */
          
          Tcl_ResetResult(interp);
          Tcl_AppendToObj(Tcl_GetObjResult(interp),
                  "missing close-bracket", -1);
          result = TCL_ERROR;
          goto done;
      } else {
          panic("CompilePrimaryExpr: unexpected termination char '%c' for nested command\n", *termPtr);
      }
      maxDepth = envPtr->maxStackDepth;
      break;
      
    case FUNC_NAME:
      result = CompileMathFuncCall(interp, infoPtr, flags, envPtr);
      if (result != TCL_OK) {
          goto done;
      }
      maxDepth = envPtr->maxStackDepth;
      break;
      
    case OPEN_PAREN:
      result = GetToken(interp, infoPtr, envPtr); /* skip over the '(' */
      if (result != TCL_OK) {
          goto done;
      }
      infoPtr->exprIsComparison = 0;
      result = CompileCondExpr(interp, infoPtr, flags, envPtr);
      if (result != TCL_OK) {
          goto done;
      }
      maxDepth = envPtr->maxStackDepth;
      if (infoPtr->token != CLOSE_PAREN) {
          goto syntaxError;
      }
      break;
      
    default:
      goto syntaxError;
    }

    if (theToken != FUNC_NAME) {
      /*
       * Advance to the next token before returning.
       */
      
      result = GetToken(interp, infoPtr, envPtr);
      if (result != TCL_OK) {
          goto done;
      }
    }

    done:
    envPtr->maxStackDepth = maxDepth;
    return result;

    syntaxError:
    Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
          "syntax error in expression \"", infoPtr->originalExpr,
          "\"", (char *) NULL);
    return TCL_ERROR;
}

/*
 *----------------------------------------------------------------------
 *
 * CompileMathFuncCall --
 *
 *    This procedure compiles a call on a math function in an expression:
 *    mathFuncCall ::= funcName '(' [condExpr {',' condExpr}] ')'
 *
 * Results:
 *    The return value is TCL_OK on a successful compilation and TCL_ERROR
 *    on failure. If TCL_ERROR is returned, then the interpreter's result
 *    contains an error message.
 *
 *    envPtr->maxStackDepth is updated with the maximum number of stack
 *    elements needed to execute the function.
 *
 * Side effects:
 *    Adds instructions to envPtr to evaluate the math function at
 *    runtime.
 *
 *----------------------------------------------------------------------
 */

static int
CompileMathFuncCall(interp, infoPtr, flags, envPtr)
    Tcl_Interp *interp;       /* Used for error reporting. */
    ExprInfo *infoPtr;        /* Describes the compilation state for the
                         * expression being compiled. */
    int flags;                /* Flags to control compilation (same as
                         * passed to Tcl_Eval). */
    CompileEnv *envPtr;       /* Holds resulting instructions. */
{
    Interp *iPtr = (Interp *) interp;
    int maxDepth = 0;         /* Maximum number of stack elements needed
                         * to execute the expression. */
    MathFunc *mathFuncPtr;    /* Info about math function. */
    int objIndex;       /* The object array index for an object
                         * holding the function name if it is not
                         * builtin. */
    Tcl_HashEntry *hPtr;
    char *p, *funcName;
    char savedChar;
    int result, i;

    /*
     * infoPtr->funcName points to the first character of the math
     * function's name. Look for the end of its name and look up the
     * MathFunc record for the function.
     */

    funcName = p = infoPtr->funcName;
    while (isalnum(UCHAR(*p)) || (*p == '_')) {
      p++;
    }
    infoPtr->next = p;
    
    result = GetToken(interp, infoPtr, envPtr); /* skip over func name */
    if (result != TCL_OK) {
      goto done;
    }
    if (infoPtr->token != OPEN_PAREN) {
      goto syntaxError;
    }
    result = GetToken(interp, infoPtr, envPtr); /* skip over '(' */
    if (result != TCL_OK) {
      goto done;
    }
    
    savedChar = *p;
    *p = 0;
    hPtr = Tcl_FindHashEntry(&iPtr->mathFuncTable, funcName);
    if (hPtr == NULL) {
      Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
            "unknown math function \"", funcName, "\"", (char *) NULL);
      result = TCL_ERROR;
      *p = savedChar;
      goto done;
    }
    mathFuncPtr = (MathFunc *) Tcl_GetHashValue(hPtr);

    /*
     * If not a builtin function, push an object with the function's name.
     */

    if (mathFuncPtr->builtinFuncIndex < 0) {   /* not builtin */
      objIndex = TclObjIndexForString(funcName, -1, /*allocStrRep*/ 1,
                                /*inHeap*/ 0, envPtr);
      TclEmitPush(objIndex, envPtr);
      maxDepth = 1;
    }

    /*
     * Restore the saved character after the function name.
     */

    *p = savedChar;

    /*
     * Compile the arguments for the function, if there are any.
     */

    if (mathFuncPtr->numArgs > 0) {
      for (i = 0;  ;  i++) {
          infoPtr->exprIsComparison = 0;
          result = CompileCondExpr(interp, infoPtr, flags, envPtr);
          if (result != TCL_OK) {
            goto done;
          }
    
          /*
           * Check for a ',' between arguments or a ')' ending the
           * argument list.
           */
    
          if (i == (mathFuncPtr->numArgs-1)) {
            if (infoPtr->token == CLOSE_PAREN) {
                break;  /* exit the argument parsing loop */
            } else if (infoPtr->token == COMMA) {
                Tcl_ResetResult(interp);
                Tcl_AppendToObj(Tcl_GetObjResult(interp),
                        "too many arguments for math function", -1);
                result = TCL_ERROR;
                goto done;
            } else {
                goto syntaxError;
            }
          }
          if (infoPtr->token != COMMA) {
            if (infoPtr->token == CLOSE_PAREN) {
                Tcl_ResetResult(interp);
                Tcl_AppendToObj(Tcl_GetObjResult(interp),
                        "too few arguments for math function", -1);
                result = TCL_ERROR;
                goto done;
            } else {
                goto syntaxError;
            }
          }
          result = GetToken(interp, infoPtr, envPtr); /* skip over , */
          if (result != TCL_OK) {
            goto done;
          }
          maxDepth++;
      }
    }

    if (infoPtr->token != CLOSE_PAREN) {
      goto syntaxError;
    }
    result = GetToken(interp, infoPtr, envPtr); /* skip over ')' */
    if (result != TCL_OK) {
      goto done;
    }
    
    /*
     * Compile the call on the math function. Note that the "objc" argument
     * count for non-builtin functions is incremented by 1 to include the
     * the function name itself.
     */

    if (mathFuncPtr->builtinFuncIndex >= 0) { /* a builtin function */
      TclEmitInstUInt1(INST_CALL_BUILTIN_FUNC1,
                  mathFuncPtr->builtinFuncIndex, envPtr);
    } else {
      TclEmitInstUInt1(INST_CALL_FUNC1, (mathFuncPtr->numArgs+1), envPtr);
    }

    /*
     * A comparison is not the top-level operator in this expression.
     */

    done:
    infoPtr->exprIsComparison = 0;
    envPtr->maxStackDepth = maxDepth;
    return result;

    syntaxError:
      Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
            "syntax error in expression \"", infoPtr->originalExpr,
            "\"", (char *) NULL);
    return TCL_ERROR;
}

/*
 *----------------------------------------------------------------------
 *
 * GetToken --
 *
 *    Lexical scanner used to compile expressions: parses a single 
 *    operator or other syntactic element from an expression string.
 *
 * Results:
 *    TCL_OK is returned unless an error occurred. In that case a standard
 *    Tcl error is returned, using the interpreter's result to hold an
 *    error message. TCL_ERROR is returned if an integer overflow, or a
 *    floating-point overflow or underflow occurred while reading in a
 *    number. If the lexical analysis is successful, infoPtr->token refers
 *    to the next symbol in the expression string, and infoPtr->next is
 *    advanced past the token. Also, if the token is a integer, double, or
 *    string literal, then infoPtr->objIndex the index of an object
 *    holding the value in the code's object table; otherwise is NULL.
 *
 * Side effects:
 *    Object are added to envPtr to hold the values of scanned literal
 *    integers, doubles, or strings.
 *
 *----------------------------------------------------------------------
 */

static int
GetToken(interp, infoPtr, envPtr)
    Tcl_Interp *interp;             /* Interpreter to use for error
                               * reporting. */
    register ExprInfo *infoPtr;         /* Describes the state of the
                               * compiling the expression,
                               * including the resulting token. */
    CompileEnv *envPtr;             /* Holds objects that store literal
                               * values that are scanned. */
{
    register char *src;       /* Points to current source char. */
    register char c;          /* The current char. */
    register int type;        /* Current char's CHAR_TYPE type. */
    char *termPtr;            /* Points to char terminating a literal. */
    char savedChar;           /* Holds the character termporarily replaced
                         * by a null character during processing of
                         * literal tokens. */
    int objIndex;       /* The object array index for an object
                         * holding a scanned literal. */
    long longValue;           /* Value of a scanned integer literal. */
    double doubleValue;       /* Value of a scanned double literal. */
    Tcl_Obj *objPtr;

    /*
     * First initialize the scanner's "result" fields to default values.
     */
    
    infoPtr->token = UNKNOWN;
    infoPtr->objIndex = -1;
    infoPtr->funcName = NULL;

    /*
     * Scan over leading white space at the start of a token. Note that a
     * backslash-newline is treated as a space.
     */

    src = infoPtr->next;
    c = *src;
    type = CHAR_TYPE(src, infoPtr->lastChar);
    while ((type & (TCL_SPACE | TCL_BACKSLASH)) || (c == '\n')) {
      if (type == TCL_BACKSLASH) {
          if (src[1] == '\n') {
            src += 2;
          } else {
            break;      /* no longer white space */
          }
      } else {
          src++;
      }
      c = *src;
      type = CHAR_TYPE(src, infoPtr->lastChar);
    }
    if (src == infoPtr->lastChar) {
      infoPtr->token = END;
      infoPtr->next = src;
      return TCL_OK;
    }

    /*
     * Try to parse the token first as an integer or floating-point
     * number. Don't check for a number if the first character is "+" or
     * "-". If we did, we might treat a binary operator as unary by mistake,
     * which would eventually cause a syntax error.
     */

    if ((*src != '+') && (*src != '-')) {
      int startsWithDigit = isdigit(UCHAR(*src));
      
      if (startsWithDigit && TclLooksLikeInt(src)) {
          errno = 0;
          longValue = strtoul(src, &termPtr, 0);
          if (errno == ERANGE) {
            char *s = "integer value too large to represent";
            
            Tcl_ResetResult(interp);
            Tcl_AppendToObj(Tcl_GetObjResult(interp), s, -1);
            Tcl_SetErrorCode(interp, "ARITH", "IOVERFLOW", s,
                  (char *) NULL);
            return TCL_ERROR;
          }
          if (termPtr != src) {
            /*
             * src was the start of a valid integer. Find/create an
             * object in envPtr's object array to contain the integer.
             */
          
            savedChar = *termPtr;
            *termPtr = '\0';
            objIndex = TclObjIndexForString(src, termPtr - src,
                    /*allocStrRep*/ 0, /*inHeap*/ 0, envPtr);
            *termPtr = savedChar;  /* restore the saved char */
            
            objPtr = envPtr->objArrayPtr[objIndex];
            Tcl_InvalidateStringRep(objPtr);
            objPtr->internalRep.longValue = longValue;
            objPtr->typePtr = &tclIntType;
            
            infoPtr->token = LITERAL;
            infoPtr->objIndex = objIndex;
            infoPtr->next = termPtr;
            return TCL_OK;
          }
      } else if (startsWithDigit || (*src == '.')
              || (*src == 'n') || (*src == 'N')) {
          errno = 0;
          doubleValue = strtod(src, &termPtr);
          if (termPtr != src) {
            if (errno != 0) {
                TclExprFloatError(interp, doubleValue);
                return TCL_ERROR;
            }

            /*
             * Find/create an object in the object array containing the
             * double.
             */
            
            savedChar = *termPtr;
            *termPtr = '\0';
            objIndex = TclObjIndexForString(src, termPtr - src,
                  /*allocStrRep*/ 1, /*inHeap*/ 0, envPtr);
            *termPtr = savedChar;  /* restore the saved char */
            
            objPtr = envPtr->objArrayPtr[objIndex];
            objPtr->internalRep.doubleValue = doubleValue;
            objPtr->typePtr = &tclDoubleType;
            
            infoPtr->token = LITERAL;
            infoPtr->objIndex = objIndex;
            infoPtr->next = termPtr;
            return TCL_OK;
          }
      }
    }

    /*
     * Not an integer or double literal. Check next for a string literal
     * in braces.
     */

    if (*src == '{') {
      int level = 0;           /* The {} nesting level. */
      int hasBackslashNL = 0;  /* Nonzero if '\newline' was found. */
      char *string = src;      /* Set below to point just after the
                          * starting '{'. */
      char *last;        /* Points just before terminating '}'. */
      int numChars;            /* Number of chars in braced string. */
      char savedChar;          /* Holds the character from string
                          * termporarily replaced by a null char
                          * during braced string processing. */
      int numRead;

      /*
       * Check first for any backslash-newlines, since we must treat
       * backslash-newlines specially (they must be replaced by spaces).
       */

      while (1) {
          if (src == infoPtr->lastChar) {
            Tcl_ResetResult(interp);
            Tcl_AppendToObj(Tcl_GetObjResult(interp),
                    "missing close-brace", -1);
            return TCL_ERROR;
          } else if (CHAR_TYPE(src, infoPtr->lastChar) == TCL_NORMAL) {
            src++;
            continue;
          }
          c = *src++;
          if (c == '{') {
            level++;
          } else if (c == '}') {
            --level;
            if (level == 0) {
                last = (src - 2); /* i.e. just before terminating } */
                break;
            }
          } else if (c == '\\') {
            if (*src == '\n') {
                hasBackslashNL = 1;
            }
            (void) Tcl_Backslash(src-1, &numRead);
            src += numRead - 1;
          }
      }

      /*
       * Create a string object for the braced string. This will start at
       * "string" and ends just after "last" (which points to the final
       * character before the terminating '}'). If backslash-newlines were
       * found, we copy characters one at a time into a heap-allocated
       * buffer and do backslash-newline substitutions.
       */

      string++;
      numChars = (last - string + 1);
      savedChar = string[numChars];
      string[numChars] = '\0';
      if (hasBackslashNL && (numChars > 0)) {
          char *buffer = ckalloc((unsigned) numChars + 1);
          register char *dst = buffer;
          register char *p = string;
#ifdef KANJI
          int doConv = envPtr->iPtr->kanjiConvertWhenCompile;
#endif /* KANJI */
          while (p <= last) {
            c = *dst++ = *p++;
            if (c == '\\') {
                if (*p == '\n') {
                  dst[-1] = Tcl_Backslash(p-1, &numRead);
                  p += numRead - 1;
                } else {
                  (void) Tcl_Backslash(p-1, &numRead);
                  while (numRead > 1) {
                      *dst++ = *p++;
                      numRead--;
                  }
                }
            }
          }
          *dst = '\0';
#ifdef KANJI
          /* Avoiding re-conversion of backslash sequence to kanji. */
          envPtr->iPtr->kanjiConvertWhenCompile = BS_ASUMP_KANJI;
#endif /* KANJI */
          objIndex = TclObjIndexForString(buffer, dst - buffer,
                /*allocStrRep*/ 1, /*inHeap*/ 1, envPtr);
#ifdef KANJI
          envPtr->iPtr->kanjiConvertWhenCompile = doConv;
#endif /* KANJI */
      } else {
          objIndex = TclObjIndexForString(string, numChars,
                /*allocStrRep*/ 1, /*inHeap*/ 0, envPtr);
      }
      string[numChars] = savedChar;   /* restore the saved char */

      infoPtr->token = LITERAL;
      infoPtr->objIndex = objIndex;
      infoPtr->next = src;
      return TCL_OK;
    }

    /*
     * Not an literal value.
     */

    infoPtr->next = src+1;   /* assume a 1 char token and advance over it */
    switch (*src) {
      case '[':
          infoPtr->token = OPEN_BRACKET;
          return TCL_OK;

      case ']':
          infoPtr->token = CLOSE_BRACKET;
          return TCL_OK;

      case '(':
          infoPtr->token = OPEN_PAREN;
          return TCL_OK;

      case ')':
          infoPtr->token = CLOSE_PAREN;
          return TCL_OK;

      case '$':
          infoPtr->token = DOLLAR;
          return TCL_OK;

      case '"':
          infoPtr->token = QUOTE;
          return TCL_OK;

      case ',':
          infoPtr->token = COMMA;
          return TCL_OK;

      case '*':
          infoPtr->token = MULT;
          return TCL_OK;

      case '/':
          infoPtr->token = DIVIDE;
          return TCL_OK;

      case '%':
          infoPtr->token = MOD;
          return TCL_OK;

      case '+':
          infoPtr->token = PLUS;
          return TCL_OK;

      case '-':
          infoPtr->token = MINUS;
          return TCL_OK;

      case '?':
          infoPtr->token = QUESTY;
          return TCL_OK;

      case ':':
          infoPtr->token = COLON;
          return TCL_OK;

      case '<':
          switch (src[1]) {
            case '<':
                infoPtr->next = src+2;
                infoPtr->token = LEFT_SHIFT;
                break;
            case '=':
                infoPtr->next = src+2;
                infoPtr->token = LEQ;
                break;
            default:
                infoPtr->token = LESS;
                break;
          }
          return TCL_OK;

      case '>':
          switch (src[1]) {
            case '>':
                infoPtr->next = src+2;
                infoPtr->token = RIGHT_SHIFT;
                break;
            case '=':
                infoPtr->next = src+2;
                infoPtr->token = GEQ;
                break;
            default:
                infoPtr->token = GREATER;
                break;
          }
          return TCL_OK;

      case '=':
          if (src[1] == '=') {
            infoPtr->next = src+2;
            infoPtr->token = EQUAL;
          } else {
            infoPtr->token = UNKNOWN;
          }
          return TCL_OK;

      case '!':
          if (src[1] == '=') {
            infoPtr->next = src+2;
            infoPtr->token = NEQ;
          } else {
            infoPtr->token = NOT;
          }
          return TCL_OK;

      case '&':
          if (src[1] == '&') {
            infoPtr->next = src+2;
            infoPtr->token = AND;
          } else {
            infoPtr->token = BIT_AND;
          }
          return TCL_OK;

      case '^':
          infoPtr->token = BIT_XOR;
          return TCL_OK;

      case '|':
          if (src[1] == '|') {
            infoPtr->next = src+2;
            infoPtr->token = OR;
          } else {
            infoPtr->token = BIT_OR;
          }
          return TCL_OK;

      case '~':
          infoPtr->token = BIT_NOT;
          return TCL_OK;

      default:
          if (isalpha(UCHAR(*src))) {
            infoPtr->token = FUNC_NAME;
            infoPtr->funcName = src;
            while (isalnum(UCHAR(*src)) || (*src == '_')) {
                src++;
            }
            infoPtr->next = src;
            return TCL_OK;
          }
          infoPtr->next = src+1;
          infoPtr->token = UNKNOWN;
          return TCL_OK;
    }
}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_CreateMathFunc --
 *
 *    Creates a new math function for expressions in a given
 *    interpreter.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    The function defined by "name" is created or redefined. If the
 *    function already exists then its definition is replaced; this
 *    includes the builtin functions. Redefining a builtin function forces
 *    all existing code to be invalidated since that code may be compiled
 *    using an instruction specific to the replaced function. In addition,
 *    redefioning a non-builtin function will force existing code to be
 *    invalidated if the number of arguments has changed.
 *
 *----------------------------------------------------------------------
 */

void
Tcl_CreateMathFunc(interp, name, numArgs, argTypes, proc, clientData)
    Tcl_Interp *interp;             /* Interpreter in which function is
                               * to be available. */
    char *name;                     /* Name of function (e.g. "sin"). */
    int numArgs;              /* Nnumber of arguments required by
                               * function. */
    Tcl_ValueType *argTypes;        /* Array of types acceptable for
                               * each argument. */
    Tcl_MathProc *proc;             /* Procedure that implements the
                               * math function. */
    ClientData clientData;          /* Additional value to pass to the
                               * function. */
{
    Interp *iPtr = (Interp *) interp;
    Tcl_HashEntry *hPtr;
    MathFunc *mathFuncPtr;
    int new, i;

    hPtr = Tcl_CreateHashEntry(&iPtr->mathFuncTable, name, &new);
    if (new) {
      Tcl_SetHashValue(hPtr, ckalloc(sizeof(MathFunc)));
    }
    mathFuncPtr = (MathFunc *) Tcl_GetHashValue(hPtr);

    if (!new) {   
      if (mathFuncPtr->builtinFuncIndex >= 0) {
          /*
           * We are redefining a builtin math function. Invalidate the
             * interpreter's existing code by incrementing its
             * compileEpoch member. This field is checked in Tcl_EvalObj
             * and ObjInterpProc, and code whose compilation epoch doesn't
             * match is recompiled. Newly compiled code will no longer
             * treat the function as builtin.
           */

          iPtr->compileEpoch++;
      } else {
          /*
           * A non-builtin function is being redefined. We must invalidate
             * existing code if the number of arguments has changed. This
           * is because existing code was compiled assuming that number.
           */

          if (numArgs != mathFuncPtr->numArgs) {
            iPtr->compileEpoch++;
          }
      }
    }
    
    mathFuncPtr->builtinFuncIndex = -1;   /* can't be a builtin function */
    if (numArgs > MAX_MATH_ARGS) {
      numArgs = MAX_MATH_ARGS;
    }
    mathFuncPtr->numArgs = numArgs;
    for (i = 0;  i < numArgs;  i++) {
      mathFuncPtr->argTypes[i] = argTypes[i];
    }
    mathFuncPtr->proc = proc;
    mathFuncPtr->clientData = clientData;
}

Generated by  Doxygen 1.6.0   Back to index