sl@0: /* 
sl@0:  * tclCompExpr.c --
sl@0:  *
sl@0:  *	This file contains the code to compile Tcl expressions.
sl@0:  *
sl@0:  * Copyright (c) 1997 Sun Microsystems, Inc.
sl@0:  * Copyright (c) 1998-2000 by Scriptics Corporation.
sl@0:  * Portions Copyright (c) 2007 Nokia Corporation and/or its subsidiaries. All rights reserved.  
sl@0:  *
sl@0:  * See the file "license.terms" for information on usage and redistribution
sl@0:  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
sl@0:  *
sl@0:  * RCS: @(#) $Id: tclCompExpr.c,v 1.13.2.3 2006/11/28 22:20:00 andreas_kupries Exp $
sl@0:  */
sl@0: 
sl@0: #include "tclInt.h"
sl@0: #include "tclCompile.h"
sl@0: #if defined(__SYMBIAN32__) 
sl@0: #include "tclSymbianGlobals.h"
sl@0: #endif 
sl@0: 
sl@0: /*
sl@0:  * The stuff below is a bit of a hack so that this file can be used in
sl@0:  * environments that include no UNIX, i.e. no errno: just arrange to use
sl@0:  * the errno from tclExecute.c here.
sl@0:  */
sl@0: 
sl@0: #ifndef TCL_GENERIC_ONLY
sl@0: #include "tclPort.h"
sl@0: #else
sl@0: #define NO_ERRNO_H
sl@0: #endif
sl@0: 
sl@0: #ifdef NO_ERRNO_H
sl@0: extern int errno;			/* Use errno from tclExecute.c. */
sl@0: #define ERANGE 34
sl@0: #endif
sl@0: 
sl@0: /*
sl@0:  * Boolean variable that controls whether expression compilation tracing
sl@0:  * is enabled.
sl@0:  */
sl@0: 
sl@0: #ifdef TCL_COMPILE_DEBUG
sl@0: static int traceExprComp = 0;
sl@0: #endif /* TCL_COMPILE_DEBUG */
sl@0: 
sl@0: /*
sl@0:  * The ExprInfo structure describes the state of compiling an expression.
sl@0:  * A pointer to an ExprInfo record is passed among the routines in
sl@0:  * this module.
sl@0:  */
sl@0: 
sl@0: typedef struct ExprInfo {
sl@0:     Tcl_Interp *interp;		/* Used for error reporting. */
sl@0:     Tcl_Parse *parsePtr;	/* Structure filled with information about
sl@0: 				 * the parsed expression. */
sl@0:     CONST char *expr;		/* The expression that was originally passed
sl@0: 				 * to TclCompileExpr. */
sl@0:     CONST char *lastChar;	/* Points just after last byte of expr. */
sl@0:     int hasOperators;		/* Set 1 if the expr has operators; 0 if
sl@0: 				 * expr is only a primary. If 1 after
sl@0: 				 * compiling an expr, a tryCvtToNumeric
sl@0: 				 * instruction is emitted to convert the
sl@0: 				 * primary to a number if possible. */
sl@0: } ExprInfo;
sl@0: 
sl@0: /*
sl@0:  * Definitions of numeric codes representing each expression operator.
sl@0:  * The order of these must match the entries in the operatorTable below.
sl@0:  * Also the codes for the relational operators (OP_LESS, OP_GREATER, 
sl@0:  * OP_LE, OP_GE, OP_EQ, and OP_NE) must be consecutive and in that order.
sl@0:  * Note that OP_PLUS and OP_MINUS represent both unary and binary operators.
sl@0:  */
sl@0: 
sl@0: #define OP_MULT		0
sl@0: #define OP_DIVIDE	1
sl@0: #define OP_MOD		2
sl@0: #define OP_PLUS		3
sl@0: #define OP_MINUS	4
sl@0: #define OP_LSHIFT	5
sl@0: #define OP_RSHIFT	6
sl@0: #define OP_LESS		7
sl@0: #define OP_GREATER	8
sl@0: #define OP_LE		9
sl@0: #define OP_GE		10
sl@0: #define OP_EQ		11
sl@0: #define OP_NEQ		12
sl@0: #define OP_BITAND	13
sl@0: #define OP_BITXOR	14
sl@0: #define OP_BITOR	15
sl@0: #define OP_LAND		16
sl@0: #define OP_LOR		17
sl@0: #define OP_QUESTY	18
sl@0: #define OP_LNOT		19
sl@0: #define OP_BITNOT	20
sl@0: #define OP_STREQ	21
sl@0: #define OP_STRNEQ	22
sl@0: 
sl@0: /*
sl@0:  * Table describing the expression operators. Entries in this table must
sl@0:  * correspond to the definitions of numeric codes for operators just above.
sl@0:  */
sl@0: #if !defined(__SYMBIAN32__) || !defined(__WINSCW__)
sl@0: static int opTableInitialized = 0; /* 0 means not yet initialized. */
sl@0: #endif
sl@0: 
sl@0: TCL_DECLARE_MUTEX(opMutex)
sl@0: 
sl@0: typedef struct OperatorDesc {
sl@0:     char *name;			/* Name of the operator. */
sl@0:     int numOperands;		/* Number of operands. 0 if the operator
sl@0: 				 * requires special handling. */
sl@0:     int instruction;		/* Instruction opcode for the operator.
sl@0: 				 * Ignored if numOperands is 0. */
sl@0: } OperatorDesc;
sl@0: 
sl@0: static OperatorDesc operatorTable[] = {
sl@0:     {"*",   2,  INST_MULT},
sl@0:     {"/",   2,  INST_DIV},
sl@0:     {"%",   2,  INST_MOD},
sl@0:     {"+",   0}, 
sl@0:     {"-",   0},
sl@0:     {"<<",  2,  INST_LSHIFT},
sl@0:     {">>",  2,  INST_RSHIFT},
sl@0:     {"<",   2,  INST_LT},
sl@0:     {">",   2,  INST_GT},
sl@0:     {"<=",  2,  INST_LE},
sl@0:     {">=",  2,  INST_GE},
sl@0:     {"==",  2,  INST_EQ},
sl@0:     {"!=",  2,  INST_NEQ},
sl@0:     {"&",   2,  INST_BITAND},
sl@0:     {"^",   2,  INST_BITXOR},
sl@0:     {"|",   2,  INST_BITOR},
sl@0:     {"&&",  0},
sl@0:     {"||",  0},
sl@0:     {"?",   0},
sl@0:     {"!",   1,  INST_LNOT},
sl@0:     {"~",   1,  INST_BITNOT},
sl@0:     {"eq",  2,  INST_STR_EQ},
sl@0:     {"ne",  2,  INST_STR_NEQ},
sl@0:     {NULL}
sl@0: };
sl@0: 
sl@0: #if !defined(__SYMBIAN32__) || !defined(__WINSCW__)
sl@0: /*
sl@0:  * Hashtable used to map the names of expression operators to the index
sl@0:  * of their OperatorDesc description.
sl@0:  */
sl@0: static Tcl_HashTable opHashTable;
sl@0: #endif
sl@0: 
sl@0: /*
sl@0:  * Declarations for local procedures to this file:
sl@0:  */
sl@0: 
sl@0: static int		CompileCondExpr _ANSI_ARGS_((
sl@0: 			    Tcl_Token *exprTokenPtr, ExprInfo *infoPtr,
sl@0: 			    CompileEnv *envPtr, Tcl_Token **endPtrPtr));
sl@0: static int		CompileLandOrLorExpr _ANSI_ARGS_((
sl@0: 			    Tcl_Token *exprTokenPtr, int opIndex,
sl@0: 			    ExprInfo *infoPtr, CompileEnv *envPtr,
sl@0: 			    Tcl_Token **endPtrPtr));
sl@0: static int		CompileMathFuncCall _ANSI_ARGS_((
sl@0: 			    Tcl_Token *exprTokenPtr, CONST char *funcName,
sl@0: 			    ExprInfo *infoPtr, CompileEnv *envPtr,
sl@0: 			    Tcl_Token **endPtrPtr));
sl@0: static int		CompileSubExpr _ANSI_ARGS_((
sl@0: 			    Tcl_Token *exprTokenPtr, ExprInfo *infoPtr,
sl@0: 			    CompileEnv *envPtr));
sl@0: static void		LogSyntaxError _ANSI_ARGS_((ExprInfo *infoPtr));
sl@0: 
sl@0: /*
sl@0:  * Macro used to debug the execution of the expression compiler.
sl@0:  */
sl@0: 
sl@0: #ifdef TCL_COMPILE_DEBUG
sl@0: #define TRACE(exprBytes, exprLength, tokenBytes, tokenLength) \
sl@0:     if (traceExprComp) { \
sl@0: 	fprintf(stderr, "CompileSubExpr: \"%.*s\", token \"%.*s\"\n", \
sl@0: 	        (exprLength), (exprBytes), (tokenLength), (tokenBytes)); \
sl@0:     }
sl@0: #else
sl@0: #define TRACE(exprBytes, exprLength, tokenBytes, tokenLength)
sl@0: #endif /* TCL_COMPILE_DEBUG */
sl@0: 
sl@0: /*
sl@0:  *----------------------------------------------------------------------
sl@0:  *
sl@0:  * TclCompileExpr --
sl@0:  *
sl@0:  *	This procedure compiles a string containing a Tcl expression into
sl@0:  *	Tcl bytecodes. This procedure is the top-level interface to the
sl@0:  *	the expression compilation module, and is used by such public
sl@0:  *	procedures as Tcl_ExprString, Tcl_ExprStringObj, Tcl_ExprLong,
sl@0:  *	Tcl_ExprDouble, Tcl_ExprBoolean, and Tcl_ExprBooleanObj.
sl@0:  *
sl@0:  * Results:
sl@0:  *	The return value is TCL_OK on a successful compilation and TCL_ERROR
sl@0:  *	on failure. If TCL_ERROR is returned, then the interpreter's result
sl@0:  *	contains an error message.
sl@0:  *
sl@0:  * Side effects:
sl@0:  *	Adds instructions to envPtr to evaluate the expression at runtime.
sl@0:  *
sl@0:  *----------------------------------------------------------------------
sl@0:  */
sl@0: 
sl@0: int
sl@0: TclCompileExpr(interp, script, numBytes, envPtr)
sl@0:     Tcl_Interp *interp;		/* Used for error reporting. */
sl@0:     CONST char *script;		/* The source script to compile. */
sl@0:     int numBytes;		/* Number of bytes in script. If < 0, the
sl@0: 				 * string consists of all bytes up to the
sl@0: 				 * first null character. */
sl@0:     CompileEnv *envPtr;		/* Holds resulting instructions. */
sl@0: {
sl@0:     ExprInfo info;
sl@0:     Tcl_Parse parse;
sl@0:     Tcl_HashEntry *hPtr;
sl@0:     int new, i, code;
sl@0: 
sl@0:     /*
sl@0:      * If this is the first time we've been called, initialize the table
sl@0:      * of expression operators.
sl@0:      */
sl@0: 
sl@0:     if (numBytes < 0) {
sl@0: 	numBytes = (script? strlen(script) : 0);
sl@0:     }
sl@0:     if (!opTableInitialized) {
sl@0: 	Tcl_MutexLock(&opMutex);
sl@0: 	if (!opTableInitialized) {
sl@0: 	    Tcl_InitHashTable(&opHashTable, TCL_STRING_KEYS);
sl@0: 	    for (i = 0;  operatorTable[i].name != NULL;  i++) {
sl@0: 		hPtr = Tcl_CreateHashEntry(&opHashTable,
sl@0: 			operatorTable[i].name, &new);
sl@0: 		if (new) {
sl@0: 		    Tcl_SetHashValue(hPtr, (ClientData) i);
sl@0: 		}
sl@0: 	    }
sl@0: 	    opTableInitialized = 1;
sl@0: 	}
sl@0: 	Tcl_MutexUnlock(&opMutex);
sl@0:     }
sl@0: 
sl@0:     /*
sl@0:      * Initialize the structure containing information abvout this
sl@0:      * expression compilation.
sl@0:      */
sl@0: 
sl@0:     info.interp = interp;
sl@0:     info.parsePtr = &parse;
sl@0:     info.expr = script;
sl@0:     info.lastChar = (script + numBytes); 
sl@0:     info.hasOperators = 0;
sl@0: 
sl@0:     /*
sl@0:      * Parse the expression then compile it.
sl@0:      */
sl@0: 
sl@0:     code = Tcl_ParseExpr(interp, script, numBytes, &parse);
sl@0:     if (code != TCL_OK) {
sl@0: 	goto done;
sl@0:     }
sl@0: 
sl@0: #ifdef TCL_TIP280
sl@0:     /* TIP #280 : Track Lines within the expression */
sl@0:     TclAdvanceLines (&envPtr->line, script, parse.tokenPtr->start);
sl@0: #endif
sl@0: 
sl@0:     code = CompileSubExpr(parse.tokenPtr, &info, envPtr);
sl@0:     if (code != TCL_OK) {
sl@0: 	Tcl_FreeParse(&parse);
sl@0: 	goto done;
sl@0:     }
sl@0:     
sl@0:     if (!info.hasOperators) {
sl@0: 	/*
sl@0: 	 * Attempt to convert the primary's object to an int or double.
sl@0: 	 * This is done in order to support Tcl's policy of interpreting
sl@0: 	 * operands if at all possible as first integers, else
sl@0: 	 * floating-point numbers.
sl@0: 	 */
sl@0: 	
sl@0: 	TclEmitOpcode(INST_TRY_CVT_TO_NUMERIC, envPtr);
sl@0:     }
sl@0:     Tcl_FreeParse(&parse);
sl@0: 
sl@0:     done:
sl@0:     return code;
sl@0: }
sl@0: 
sl@0: /*
sl@0:  *----------------------------------------------------------------------
sl@0:  *
sl@0:  * TclFinalizeCompilation --
sl@0:  *
sl@0:  *	Clean up the compilation environment so it can later be
sl@0:  *	properly reinitialized. This procedure is called by Tcl_Finalize().
sl@0:  *
sl@0:  * Results:
sl@0:  *	None.
sl@0:  *
sl@0:  * Side effects:
sl@0:  *	Cleans up the compilation environment. At the moment, just the
sl@0:  *	table of expression operators is freed.
sl@0:  *
sl@0:  *----------------------------------------------------------------------
sl@0:  */
sl@0: 
sl@0: void
sl@0: TclFinalizeCompilation()
sl@0: {
sl@0:     Tcl_MutexLock(&opMutex);
sl@0:     if (opTableInitialized) {
sl@0:         Tcl_DeleteHashTable(&opHashTable);
sl@0:         opTableInitialized = 0;
sl@0:     }
sl@0:     Tcl_MutexUnlock(&opMutex);
sl@0: }
sl@0: 
sl@0: /*
sl@0:  *----------------------------------------------------------------------
sl@0:  *
sl@0:  * CompileSubExpr --
sl@0:  *
sl@0:  *	Given a pointer to a TCL_TOKEN_SUB_EXPR token describing a
sl@0:  *	subexpression, this procedure emits instructions to evaluate the
sl@0:  *	subexpression at runtime.
sl@0:  *
sl@0:  * Results:
sl@0:  *	The return value is TCL_OK on a successful compilation and TCL_ERROR
sl@0:  *	on failure. If TCL_ERROR is returned, then the interpreter's result
sl@0:  *	contains an error message.
sl@0:  *
sl@0:  * Side effects:
sl@0:  *	Adds instructions to envPtr to evaluate the subexpression.
sl@0:  *
sl@0:  *----------------------------------------------------------------------
sl@0:  */
sl@0: 
sl@0: static int
sl@0: CompileSubExpr(exprTokenPtr, infoPtr, envPtr)
sl@0:     Tcl_Token *exprTokenPtr;	/* Points to TCL_TOKEN_SUB_EXPR token
sl@0: 				 * to compile. */
sl@0:     ExprInfo *infoPtr;		/* Describes the compilation state for the
sl@0: 				 * expression being compiled. */
sl@0:     CompileEnv *envPtr;		/* Holds resulting instructions. */
sl@0: {
sl@0:     Tcl_Interp *interp = infoPtr->interp;
sl@0:     Tcl_Token *tokenPtr, *endPtr = NULL; /* silence gcc 4 warning */
sl@0:     Tcl_Token *afterSubexprPtr;
sl@0:     OperatorDesc *opDescPtr;
sl@0:     Tcl_HashEntry *hPtr;
sl@0:     CONST char *operator;
sl@0:     Tcl_DString opBuf;
sl@0:     int objIndex, opIndex, length, code;
sl@0:     char buffer[TCL_UTF_MAX];
sl@0: 
sl@0:     if (exprTokenPtr->type != TCL_TOKEN_SUB_EXPR) {
sl@0: 	panic("CompileSubExpr: token type %d not TCL_TOKEN_SUB_EXPR\n",
sl@0: 	        exprTokenPtr->type);
sl@0:     }
sl@0:     code = TCL_OK;
sl@0: 
sl@0:     /*
sl@0:      * Switch on the type of the first token after the subexpression token.
sl@0:      * After processing it, advance tokenPtr to point just after the
sl@0:      * subexpression's last token.
sl@0:      */
sl@0:     
sl@0:     tokenPtr = exprTokenPtr+1;
sl@0:     TRACE(exprTokenPtr->start, exprTokenPtr->size,
sl@0: 	    tokenPtr->start, tokenPtr->size);
sl@0:     switch (tokenPtr->type) {
sl@0:         case TCL_TOKEN_WORD:
sl@0: 	    code = TclCompileTokens(interp, tokenPtr+1,
sl@0: 	            tokenPtr->numComponents, envPtr);
sl@0: 	    if (code != TCL_OK) {
sl@0: 		goto done;
sl@0: 	    }
sl@0: 	    tokenPtr += (tokenPtr->numComponents + 1);
sl@0: 	    break;
sl@0: 	    
sl@0:         case TCL_TOKEN_TEXT:
sl@0: 	    if (tokenPtr->size > 0) {
sl@0: 		objIndex = TclRegisterNewLiteral(envPtr, tokenPtr->start,
sl@0: 	                tokenPtr->size);
sl@0: 	    } else {
sl@0: 		objIndex = TclRegisterNewLiteral(envPtr, "", 0);
sl@0: 	    }
sl@0: 	    TclEmitPush(objIndex, envPtr);
sl@0: 	    tokenPtr += 1;
sl@0: 	    break;
sl@0: 	    
sl@0:         case TCL_TOKEN_BS:
sl@0: 	    length = Tcl_UtfBackslash(tokenPtr->start, (int *) NULL,
sl@0: 		    buffer);
sl@0: 	    if (length > 0) {
sl@0: 		objIndex = TclRegisterNewLiteral(envPtr, buffer, length);
sl@0: 	    } else {
sl@0: 		objIndex = TclRegisterNewLiteral(envPtr, "", 0);
sl@0: 	    }
sl@0: 	    TclEmitPush(objIndex, envPtr);
sl@0: 	    tokenPtr += 1;
sl@0: 	    break;
sl@0: 	    
sl@0:         case TCL_TOKEN_COMMAND:
sl@0: 	    code = TclCompileScript(interp, tokenPtr->start+1,
sl@0: 		    tokenPtr->size-2, /*nested*/ 0, envPtr);
sl@0: 	    if (code != TCL_OK) {
sl@0: 		goto done;
sl@0: 	    }
sl@0: 	    tokenPtr += 1;
sl@0: 	    break;
sl@0: 	    
sl@0:         case TCL_TOKEN_VARIABLE:
sl@0: 	    code = TclCompileTokens(interp, tokenPtr, 1, envPtr);
sl@0: 	    if (code != TCL_OK) {
sl@0: 		goto done;
sl@0: 	    }
sl@0: 	    tokenPtr += (tokenPtr->numComponents + 1);
sl@0: 	    break;
sl@0: 	    
sl@0:         case TCL_TOKEN_SUB_EXPR:
sl@0: 	    code = CompileSubExpr(tokenPtr, infoPtr, envPtr);
sl@0: 	    if (code != TCL_OK) {
sl@0: 		goto done;
sl@0: 	    }
sl@0: 	    tokenPtr += (tokenPtr->numComponents + 1);
sl@0: 	    break;
sl@0: 	    
sl@0:         case TCL_TOKEN_OPERATOR:
sl@0: 	    /*
sl@0: 	     * Look up the operator.  If the operator isn't found, treat it
sl@0: 	     * as a math function.
sl@0: 	     */
sl@0: 	    Tcl_DStringInit(&opBuf);
sl@0: 	    operator = Tcl_DStringAppend(&opBuf, 
sl@0: 		    tokenPtr->start, tokenPtr->size);
sl@0: 	    hPtr = Tcl_FindHashEntry(&opHashTable, operator);
sl@0: 	    if (hPtr == NULL) {
sl@0: 		code = CompileMathFuncCall(exprTokenPtr, operator, infoPtr,
sl@0: 			envPtr, &endPtr);
sl@0: 		Tcl_DStringFree(&opBuf);
sl@0: 		if (code != TCL_OK) {
sl@0: 		    goto done;
sl@0: 		}
sl@0: 		tokenPtr = endPtr;
sl@0: 		break;
sl@0: 	    }
sl@0: 	    Tcl_DStringFree(&opBuf);
sl@0: 	    opIndex = (int) Tcl_GetHashValue(hPtr);
sl@0: 	    opDescPtr = &(operatorTable[opIndex]);
sl@0: 
sl@0: 	    /*
sl@0: 	     * If the operator is "normal", compile it using information
sl@0: 	     * from the operator table.
sl@0: 	     */
sl@0: 
sl@0: 	    if (opDescPtr->numOperands > 0) {
sl@0: 		tokenPtr++;
sl@0: 		code = CompileSubExpr(tokenPtr, infoPtr, envPtr);
sl@0: 		if (code != TCL_OK) {
sl@0: 		    goto done;
sl@0: 		}
sl@0: 		tokenPtr += (tokenPtr->numComponents + 1);
sl@0: 
sl@0: 		if (opDescPtr->numOperands == 2) {
sl@0: 		    code = CompileSubExpr(tokenPtr, infoPtr, envPtr);
sl@0: 		    if (code != TCL_OK) {
sl@0: 			goto done;
sl@0: 		    }
sl@0: 		    tokenPtr += (tokenPtr->numComponents + 1);
sl@0: 		}
sl@0: 		TclEmitOpcode(opDescPtr->instruction, envPtr);
sl@0: 		infoPtr->hasOperators = 1;
sl@0: 		break;
sl@0: 	    }
sl@0: 	    
sl@0: 	    /*
sl@0: 	     * The operator requires special treatment, and is either
sl@0: 	     * "+" or "-", or one of "&&", "||" or "?".
sl@0: 	     */
sl@0: 	    
sl@0: 	    switch (opIndex) {
sl@0: 	        case OP_PLUS:
sl@0: 	        case OP_MINUS:
sl@0: 		    tokenPtr++;
sl@0: 		    code = CompileSubExpr(tokenPtr, infoPtr, envPtr);
sl@0: 		    if (code != TCL_OK) {
sl@0: 			goto done;
sl@0: 		    }
sl@0: 		    tokenPtr += (tokenPtr->numComponents + 1);
sl@0: 		    
sl@0: 		    /*
sl@0: 		     * Check whether the "+" or "-" is unary.
sl@0: 		     */
sl@0: 		    
sl@0: 		    afterSubexprPtr = exprTokenPtr
sl@0: 			    + exprTokenPtr->numComponents+1;
sl@0: 		    if (tokenPtr == afterSubexprPtr) {
sl@0: 			TclEmitOpcode(((opIndex==OP_PLUS)?
sl@0: 			        INST_UPLUS : INST_UMINUS),
sl@0: 			        envPtr);
sl@0: 			break;
sl@0: 		    }
sl@0: 		    
sl@0: 		    /*
sl@0: 		     * The "+" or "-" is binary.
sl@0: 		     */
sl@0: 		    
sl@0: 		    code = CompileSubExpr(tokenPtr, infoPtr, envPtr);
sl@0: 		    if (code != TCL_OK) {
sl@0: 			goto done;
sl@0: 		    }
sl@0: 		    tokenPtr += (tokenPtr->numComponents + 1);
sl@0: 		    TclEmitOpcode(((opIndex==OP_PLUS)? INST_ADD : INST_SUB),
sl@0: 			    envPtr);
sl@0: 		    break;
sl@0: 
sl@0: 	        case OP_LAND:
sl@0: 	        case OP_LOR:
sl@0: 		    code = CompileLandOrLorExpr(exprTokenPtr, opIndex,
sl@0: 			    infoPtr, envPtr, &endPtr);
sl@0: 		    if (code != TCL_OK) {
sl@0: 			goto done;
sl@0: 		    }
sl@0: 		    tokenPtr = endPtr;
sl@0: 		    break;
sl@0: 			
sl@0: 	        case OP_QUESTY:
sl@0: 		    code = CompileCondExpr(exprTokenPtr, infoPtr,
sl@0: 			    envPtr, &endPtr);
sl@0: 		    if (code != TCL_OK) {
sl@0: 			goto done;
sl@0: 		    }
sl@0: 		    tokenPtr = endPtr;
sl@0: 		    break;
sl@0: 		    
sl@0: 		default:
sl@0: 		    panic("CompileSubExpr: unexpected operator %d requiring special treatment\n",
sl@0: 		        opIndex);
sl@0: 	    } /* end switch on operator requiring special treatment */
sl@0: 	    infoPtr->hasOperators = 1;
sl@0: 	    break;
sl@0: 
sl@0:         default:
sl@0: 	    panic("CompileSubExpr: unexpected token type %d\n",
sl@0: 	            tokenPtr->type);
sl@0:     }
sl@0: 
sl@0:     /*
sl@0:      * Verify that the subexpression token had the required number of
sl@0:      * subtokens: that we've advanced tokenPtr just beyond the
sl@0:      * subexpression's last token. For example, a "*" subexpression must
sl@0:      * contain the tokens for exactly two operands.
sl@0:      */
sl@0:     
sl@0:     if (tokenPtr != (exprTokenPtr + exprTokenPtr->numComponents+1)) {
sl@0: 	LogSyntaxError(infoPtr);
sl@0: 	code = TCL_ERROR;
sl@0:     }
sl@0:     
sl@0:     done:
sl@0:     return code;
sl@0: }
sl@0: 
sl@0: /*
sl@0:  *----------------------------------------------------------------------
sl@0:  *
sl@0:  * CompileLandOrLorExpr --
sl@0:  *
sl@0:  *	This procedure compiles a Tcl logical and ("&&") or logical or
sl@0:  *	("||") subexpression.
sl@0:  *
sl@0:  * Results:
sl@0:  *	The return value is TCL_OK on a successful compilation and TCL_ERROR
sl@0:  *	on failure. If TCL_OK is returned, a pointer to the token just after
sl@0:  *	the last one in the subexpression is stored at the address in
sl@0:  *	endPtrPtr. If TCL_ERROR is returned, then the interpreter's result
sl@0:  *	contains an error message.
sl@0:  *
sl@0:  * Side effects:
sl@0:  *	Adds instructions to envPtr to evaluate the expression at runtime.
sl@0:  *
sl@0:  *----------------------------------------------------------------------
sl@0:  */
sl@0: 
sl@0: static int
sl@0: CompileLandOrLorExpr(exprTokenPtr, opIndex, infoPtr, envPtr, endPtrPtr)
sl@0:     Tcl_Token *exprTokenPtr;	 /* Points to TCL_TOKEN_SUB_EXPR token
sl@0: 				  * containing the "&&" or "||" operator. */
sl@0:     int opIndex;		 /* A code describing the expression
sl@0: 				  * operator: either OP_LAND or OP_LOR. */
sl@0:     ExprInfo *infoPtr;		 /* Describes the compilation state for the
sl@0: 				  * expression being compiled. */
sl@0:     CompileEnv *envPtr;		 /* Holds resulting instructions. */
sl@0:     Tcl_Token **endPtrPtr;	 /* If successful, a pointer to the token
sl@0: 				  * just after the last token in the
sl@0: 				  * subexpression is stored here. */
sl@0: {
sl@0:     JumpFixup shortCircuitFixup; /* Used to fix up the short circuit jump
sl@0: 				  * after the first subexpression. */
sl@0:     JumpFixup lhsTrueFixup, lhsEndFixup;
sl@0:     				 /* Used to fix up jumps used to convert the
sl@0: 				  * first operand to 0 or 1. */
sl@0:     Tcl_Token *tokenPtr;
sl@0:     int dist, code;
sl@0:     int savedStackDepth = envPtr->currStackDepth;
sl@0: 
sl@0:     /*
sl@0:      * Emit code for the first operand.
sl@0:      */
sl@0: 
sl@0:     tokenPtr = exprTokenPtr+2;
sl@0:     code = CompileSubExpr(tokenPtr, infoPtr, envPtr);
sl@0:     if (code != TCL_OK) {
sl@0: 	goto done;
sl@0:     }
sl@0:     tokenPtr += (tokenPtr->numComponents + 1);
sl@0: 
sl@0:     /*
sl@0:      * Convert the first operand to the result that Tcl requires:
sl@0:      * "0" or "1". Eventually we'll use a new instruction for this.
sl@0:      */
sl@0:     
sl@0:     TclEmitForwardJump(envPtr, TCL_TRUE_JUMP, &lhsTrueFixup);
sl@0:     TclEmitPush(TclRegisterNewLiteral(envPtr, "0", 1), envPtr);
sl@0:     TclEmitForwardJump(envPtr, TCL_UNCONDITIONAL_JUMP, &lhsEndFixup);
sl@0:     dist = (envPtr->codeNext - envPtr->codeStart) - lhsTrueFixup.codeOffset;
sl@0:     if (TclFixupForwardJump(envPtr, &lhsTrueFixup, dist, 127)) {
sl@0:         badDist:
sl@0: 	panic("CompileLandOrLorExpr: bad jump distance %d\n", dist);
sl@0:     }
sl@0:     envPtr->currStackDepth = savedStackDepth;
sl@0:     TclEmitPush(TclRegisterNewLiteral(envPtr, "1", 1), envPtr);
sl@0:     dist = (envPtr->codeNext - envPtr->codeStart) - lhsEndFixup.codeOffset;
sl@0:     if (TclFixupForwardJump(envPtr, &lhsEndFixup, dist, 127)) {
sl@0: 	goto badDist;
sl@0:     }
sl@0: 
sl@0:     /*
sl@0:      * Emit the "short circuit" jump around the rest of the expression.
sl@0:      * Duplicate the "0" or "1" on top of the stack first to keep the
sl@0:      * jump from consuming it.
sl@0:      */
sl@0: 
sl@0:     TclEmitOpcode(INST_DUP, envPtr);
sl@0:     TclEmitForwardJump(envPtr,
sl@0: 	    ((opIndex==OP_LAND)? TCL_FALSE_JUMP : TCL_TRUE_JUMP),
sl@0: 	    &shortCircuitFixup);
sl@0: 
sl@0:     /*
sl@0:      * Emit code for the second operand.
sl@0:      */
sl@0: 
sl@0:     code = CompileSubExpr(tokenPtr, infoPtr, envPtr);
sl@0:     if (code != TCL_OK) {
sl@0: 	goto done;
sl@0:     }
sl@0:     tokenPtr += (tokenPtr->numComponents + 1);
sl@0: 
sl@0:     /*
sl@0:      * Emit a "logical and" or "logical or" instruction. This does not try
sl@0:      * to "short- circuit" the evaluation of both operands, but instead
sl@0:      * ensures that we either have a "1" or a "0" result.
sl@0:      */
sl@0: 
sl@0:     TclEmitOpcode(((opIndex==OP_LAND)? INST_LAND : INST_LOR), envPtr);
sl@0: 
sl@0:     /*
sl@0:      * Now that we know the target of the forward jump, update it with the
sl@0:      * correct distance.
sl@0:      */
sl@0: 
sl@0:     dist = (envPtr->codeNext - envPtr->codeStart)
sl@0: 	    - shortCircuitFixup.codeOffset;
sl@0:     TclFixupForwardJump(envPtr, &shortCircuitFixup, dist, 127);
sl@0:     *endPtrPtr = tokenPtr;
sl@0: 
sl@0:     done:
sl@0:     envPtr->currStackDepth = savedStackDepth + 1;
sl@0:     return code;
sl@0: }
sl@0: 
sl@0: /*
sl@0:  *----------------------------------------------------------------------
sl@0:  *
sl@0:  * CompileCondExpr --
sl@0:  *
sl@0:  *	This procedure compiles a Tcl conditional expression:
sl@0:  *	condExpr ::= lorExpr ['?' condExpr ':' condExpr]
sl@0:  *
sl@0:  * Results:
sl@0:  *	The return value is TCL_OK on a successful compilation and TCL_ERROR
sl@0:  *	on failure. If TCL_OK is returned, a pointer to the token just after
sl@0:  *	the last one in the subexpression is stored at the address in
sl@0:  *	endPtrPtr. If TCL_ERROR is returned, then the interpreter's result
sl@0:  *	contains an error message.
sl@0:  *
sl@0:  * Side effects:
sl@0:  *	Adds instructions to envPtr to evaluate the expression at runtime.
sl@0:  *
sl@0:  *----------------------------------------------------------------------
sl@0:  */
sl@0: 
sl@0: static int
sl@0: CompileCondExpr(exprTokenPtr, infoPtr, envPtr, endPtrPtr)
sl@0:     Tcl_Token *exprTokenPtr;	/* Points to TCL_TOKEN_SUB_EXPR token
sl@0: 				 * containing the "?" operator. */
sl@0:     ExprInfo *infoPtr;		/* Describes the compilation state for the
sl@0: 				 * expression being compiled. */
sl@0:     CompileEnv *envPtr;		/* Holds resulting instructions. */
sl@0:     Tcl_Token **endPtrPtr;	/* If successful, a pointer to the token
sl@0: 				 * just after the last token in the
sl@0: 				 * subexpression is stored here. */
sl@0: {
sl@0:     JumpFixup jumpAroundThenFixup, jumpAroundElseFixup;
sl@0: 				/* Used to update or replace one-byte jumps
sl@0: 				 * around the then and else expressions when
sl@0: 				 * their target PCs are determined. */
sl@0:     Tcl_Token *tokenPtr;
sl@0:     int elseCodeOffset, dist, code;
sl@0:     int savedStackDepth = envPtr->currStackDepth;
sl@0: 
sl@0:     /*
sl@0:      * Emit code for the test.
sl@0:      */
sl@0: 
sl@0:     tokenPtr = exprTokenPtr+2;
sl@0:     code = CompileSubExpr(tokenPtr, infoPtr, envPtr);
sl@0:     if (code != TCL_OK) {
sl@0: 	goto done;
sl@0:     }
sl@0:     tokenPtr += (tokenPtr->numComponents + 1);
sl@0:     
sl@0:     /*
sl@0:      * Emit the jump to the "else" expression if the test was false.
sl@0:      */
sl@0:     
sl@0:     TclEmitForwardJump(envPtr, TCL_FALSE_JUMP, &jumpAroundThenFixup);
sl@0: 
sl@0:     /*
sl@0:      * Compile the "then" expression. Note that if a subexpression is only
sl@0:      * a primary, we need to try to convert it to numeric. We do this to
sl@0:      * support Tcl's policy of interpreting operands if at all possible as
sl@0:      * first integers, else floating-point numbers.
sl@0:      */
sl@0: 
sl@0:     infoPtr->hasOperators = 0;
sl@0:     code = CompileSubExpr(tokenPtr, infoPtr, envPtr);
sl@0:     if (code != TCL_OK) {
sl@0: 	goto done;
sl@0:     }
sl@0:     tokenPtr += (tokenPtr->numComponents + 1);
sl@0:     if (!infoPtr->hasOperators) {
sl@0: 	TclEmitOpcode(INST_TRY_CVT_TO_NUMERIC, envPtr);
sl@0:     }
sl@0: 
sl@0:     /*
sl@0:      * Emit an unconditional jump around the "else" condExpr.
sl@0:      */
sl@0:     
sl@0:     TclEmitForwardJump(envPtr, TCL_UNCONDITIONAL_JUMP,
sl@0: 	    &jumpAroundElseFixup);
sl@0: 
sl@0:     /*
sl@0:      * Compile the "else" expression.
sl@0:      */
sl@0: 
sl@0:     envPtr->currStackDepth = savedStackDepth;
sl@0:     elseCodeOffset = (envPtr->codeNext - envPtr->codeStart);
sl@0:     infoPtr->hasOperators = 0;
sl@0:     code = CompileSubExpr(tokenPtr, infoPtr, envPtr);
sl@0:     if (code != TCL_OK) {
sl@0: 	goto done;
sl@0:     }
sl@0:     tokenPtr += (tokenPtr->numComponents + 1);
sl@0:     if (!infoPtr->hasOperators) {
sl@0: 	TclEmitOpcode(INST_TRY_CVT_TO_NUMERIC, envPtr);
sl@0:     }
sl@0: 
sl@0:     /*
sl@0:      * Fix up the second jump around the "else" expression.
sl@0:      */
sl@0: 
sl@0:     dist = (envPtr->codeNext - envPtr->codeStart)
sl@0: 	    - jumpAroundElseFixup.codeOffset;
sl@0:     if (TclFixupForwardJump(envPtr, &jumpAroundElseFixup, dist, 127)) {
sl@0: 	/*
sl@0: 	 * Update the else expression's starting code offset since it
sl@0: 	 * moved down 3 bytes too.
sl@0: 	 */
sl@0: 	
sl@0: 	elseCodeOffset += 3;
sl@0:     }
sl@0: 	
sl@0:     /*
sl@0:      * Fix up the first jump to the "else" expression if the test was false.
sl@0:      */
sl@0:     
sl@0:     dist = (elseCodeOffset - jumpAroundThenFixup.codeOffset);
sl@0:     TclFixupForwardJump(envPtr, &jumpAroundThenFixup, dist, 127);
sl@0:     *endPtrPtr = tokenPtr;
sl@0: 
sl@0:     done:
sl@0:     envPtr->currStackDepth = savedStackDepth + 1;
sl@0:     return code;
sl@0: }
sl@0: 
sl@0: /*
sl@0:  *----------------------------------------------------------------------
sl@0:  *
sl@0:  * CompileMathFuncCall --
sl@0:  *
sl@0:  *	This procedure compiles a call on a math function in an expression:
sl@0:  *	mathFuncCall ::= funcName '(' [condExpr {',' condExpr}] ')'
sl@0:  *
sl@0:  * Results:
sl@0:  *	The return value is TCL_OK on a successful compilation and TCL_ERROR
sl@0:  *	on failure. If TCL_OK is returned, a pointer to the token just after
sl@0:  *	the last one in the subexpression is stored at the address in
sl@0:  *	endPtrPtr. If TCL_ERROR is returned, then the interpreter's result
sl@0:  *	contains an error message.
sl@0:  *
sl@0:  * Side effects:
sl@0:  *	Adds instructions to envPtr to evaluate the math function at
sl@0:  *	runtime.
sl@0:  *
sl@0:  *----------------------------------------------------------------------
sl@0:  */
sl@0: 
sl@0: static int
sl@0: CompileMathFuncCall(exprTokenPtr, funcName, infoPtr, envPtr, endPtrPtr)
sl@0:     Tcl_Token *exprTokenPtr;	/* Points to TCL_TOKEN_SUB_EXPR token
sl@0: 				 * containing the math function call. */
sl@0:     CONST char *funcName;	/* Name of the math function. */
sl@0:     ExprInfo *infoPtr;		/* Describes the compilation state for the
sl@0: 				 * expression being compiled. */
sl@0:     CompileEnv *envPtr;		/* Holds resulting instructions. */
sl@0:     Tcl_Token **endPtrPtr;	/* If successful, a pointer to the token
sl@0: 				 * just after the last token in the
sl@0: 				 * subexpression is stored here. */
sl@0: {
sl@0:     Tcl_Interp *interp = infoPtr->interp;
sl@0:     Interp *iPtr = (Interp *) interp;
sl@0:     MathFunc *mathFuncPtr;
sl@0:     Tcl_HashEntry *hPtr;
sl@0:     Tcl_Token *tokenPtr, *afterSubexprPtr;
sl@0:     int code, i;
sl@0: 
sl@0:     /*
sl@0:      * Look up the MathFunc record for the function.
sl@0:      */
sl@0: 
sl@0:     code = TCL_OK;
sl@0:     hPtr = Tcl_FindHashEntry(&iPtr->mathFuncTable, funcName);
sl@0:     if (hPtr == NULL) {
sl@0: 	Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
sl@0: 		"unknown math function \"", funcName, "\"", (char *) NULL);
sl@0: 	code = TCL_ERROR;
sl@0: 	goto done;
sl@0:     }
sl@0:     mathFuncPtr = (MathFunc *) Tcl_GetHashValue(hPtr);
sl@0: 
sl@0:     /*
sl@0:      * If not a builtin function, push an object with the function's name.
sl@0:      */
sl@0: 
sl@0:     if (mathFuncPtr->builtinFuncIndex < 0) {
sl@0: 	TclEmitPush(TclRegisterNewLiteral(envPtr, funcName, -1), envPtr);
sl@0:     }
sl@0: 
sl@0:     /*
sl@0:      * Compile any arguments for the function.
sl@0:      */
sl@0: 
sl@0:     tokenPtr = exprTokenPtr+2;
sl@0:     afterSubexprPtr = exprTokenPtr + (exprTokenPtr->numComponents + 1);
sl@0:     if (mathFuncPtr->numArgs > 0) {
sl@0: 	for (i = 0;  i < mathFuncPtr->numArgs;  i++) {
sl@0: 	    if (tokenPtr == afterSubexprPtr) {
sl@0: 		Tcl_ResetResult(interp);
sl@0: 		Tcl_AppendToObj(Tcl_GetObjResult(interp),
sl@0: 		        "too few arguments for math function", -1);
sl@0: 		code = TCL_ERROR;
sl@0: 		goto done;
sl@0: 	    }
sl@0: 	    code = CompileSubExpr(tokenPtr, infoPtr, envPtr);
sl@0: 	    if (code != TCL_OK) {
sl@0: 		goto done;
sl@0: 	    }
sl@0: 	    tokenPtr += (tokenPtr->numComponents + 1);
sl@0: 	}
sl@0: 	if (tokenPtr != afterSubexprPtr) {
sl@0: 	    Tcl_ResetResult(interp);
sl@0: 	    Tcl_AppendToObj(Tcl_GetObjResult(interp),
sl@0: 		    "too many arguments for math function", -1);
sl@0: 	    code = TCL_ERROR;
sl@0: 	    goto done;
sl@0: 	} 
sl@0:     } else if (tokenPtr != afterSubexprPtr) {
sl@0: 	Tcl_ResetResult(interp);
sl@0: 	Tcl_AppendToObj(Tcl_GetObjResult(interp),
sl@0: 		"too many arguments for math function", -1);
sl@0: 	code = TCL_ERROR;
sl@0: 	goto done;
sl@0:     }
sl@0:     
sl@0:     /*
sl@0:      * Compile the call on the math function. Note that the "objc" argument
sl@0:      * count for non-builtin functions is incremented by 1 to include the
sl@0:      * function name itself.
sl@0:      */
sl@0: 
sl@0:     if (mathFuncPtr->builtinFuncIndex >= 0) { /* a builtin function */
sl@0: 	/*
sl@0: 	 * Adjust the current stack depth by the number of arguments
sl@0: 	 * of the builtin function. This cannot be handled by the 
sl@0: 	 * TclEmitInstInt1 macro as the number of arguments is not
sl@0: 	 * passed as an operand.
sl@0: 	 */
sl@0: 
sl@0: 	if (envPtr->maxStackDepth < envPtr->currStackDepth) {
sl@0: 	    envPtr->maxStackDepth = envPtr->currStackDepth;
sl@0: 	}
sl@0: 	TclEmitInstInt1(INST_CALL_BUILTIN_FUNC1,
sl@0: 	        mathFuncPtr->builtinFuncIndex, envPtr);
sl@0: 	envPtr->currStackDepth -= mathFuncPtr->numArgs;
sl@0:     } else {
sl@0: 	TclEmitInstInt1(INST_CALL_FUNC1, (mathFuncPtr->numArgs+1), envPtr);
sl@0:     }
sl@0:     *endPtrPtr = afterSubexprPtr;
sl@0: 
sl@0:     done:
sl@0:     return code;
sl@0: }
sl@0: 
sl@0: /*
sl@0:  *----------------------------------------------------------------------
sl@0:  *
sl@0:  * LogSyntaxError --
sl@0:  *
sl@0:  *	This procedure is invoked after an error occurs when compiling an
sl@0:  *	expression. It sets the interpreter result to an error message
sl@0:  *	describing the error.
sl@0:  *
sl@0:  * Results:
sl@0:  *	None.
sl@0:  *
sl@0:  * Side effects:
sl@0:  *	Sets the interpreter result to an error message describing the
sl@0:  *	expression that was being compiled when the error occurred.
sl@0:  *
sl@0:  *----------------------------------------------------------------------
sl@0:  */
sl@0: 
sl@0: static void
sl@0: LogSyntaxError(infoPtr)
sl@0:     ExprInfo *infoPtr;		/* Describes the compilation state for the
sl@0: 				 * expression being compiled. */
sl@0: {
sl@0:     int numBytes = (infoPtr->lastChar - infoPtr->expr);
sl@0:     char buffer[100];
sl@0: 
sl@0:     sprintf(buffer, "syntax error in expression \"%.*s\"",
sl@0: 	    ((numBytes > 60)? 60 : numBytes), infoPtr->expr);
sl@0:     Tcl_ResetResult(infoPtr->interp);
sl@0:     Tcl_AppendStringsToObj(Tcl_GetObjResult(infoPtr->interp),
sl@0: 	    buffer, (char *) NULL);
sl@0: }