2384 lines
68 KiB
C
2384 lines
68 KiB
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.
|
||
*
|
||
* SCCS: @(#) tclCompExpr.c 1.31 97/08/07 10:14:07
|
||
*/
|
||
|
||
#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) {
|
||
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;
|
||
}
|
||
|
||
/*
|
||
* Find/create an object in envPtr's object array that contains
|
||
* 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+1; /* Points 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 starts 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.
|
||
*/
|
||
|
||
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;
|
||
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';
|
||
objIndex = TclObjIndexForString(buffer, dst - buffer,
|
||
/*allocStrRep*/ 1, /*inHeap*/ 1, envPtr);
|
||
} 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;
|
||
}
|