#include "tclInt.h"
#include "tclCompile.h"
#ifndef TCL_GENERIC_ONLY
#include "tclPort.h"
#else
#define NO_ERRNO_H
#endif
#ifdef NO_ERRNO_H
extern int errno;
#define ERANGE 34
#endif
#ifdef TCL_COMPILE_DEBUG
static int traceCompileExpr = 0;
#endif
typedef struct ExprInfo {
int token;
int objIndex;
char *funcName;
char *next;
char *originalExpr;
char *lastChar;
int hasOperators;
int exprIsJustVarRef;
int exprIsComparison;
} ExprInfo;
#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)
#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)
#define NOT (COLON + 1)
#define BIT_NOT (NOT + 1)
#ifdef TCL_COMPILE_DEBUG
static char *tokenStrings[] = {
"LITERAL", "FUNCNAME",
"[", "]", "(", ")", "$", "\"", ",", "END", "UNKNOWN",
"*", "/", "%", "+", "-",
"<<", ">>", "<", ">", "<=", ">=", "==", "!=",
"&", "^", "|", "&&", "||", "?", ":",
"!", "~"
};
#endif
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));
#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
int
TclCompileExpr(interp, string, lastChar, flags, envPtr)
Tcl_Interp *interp;
char *string;
char *lastChar;
int flags;
CompileEnv *envPtr;
{
Interp *iPtr = (Interp *) interp;
ExprInfo info;
int maxDepth = 0;
int result;
#ifdef TCL_COMPILE_DEBUG
if (traceCompileExpr) {
fprintf(stderr, "expr: string=\"%.30s\"\n", string);
}
#endif
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;
info.exprIsComparison = 0;
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) {
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;
}
static int
CompileCondExpr(interp, infoPtr, flags, envPtr)
Tcl_Interp *interp;
ExprInfo *infoPtr;
int flags;
CompileEnv *envPtr;
{
int maxDepth = 0;
JumpFixup jumpAroundThenFixup, jumpAroundElseFixup;
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);
if (result != TCL_OK) {
goto done;
}
TclEmitForwardJump(envPtr, TCL_FALSE_JUMP, &jumpAroundThenFixup);
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);
if (result != TCL_OK) {
goto done;
}
TclEmitForwardJump(envPtr, TCL_UNCONDITIONAL_JUMP,
&jumpAroundElseFixup);
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);
}
currCodeOffset = TclCurrCodeOffset();
jumpDist = (currCodeOffset - jumpAroundElseFixup.codeOffset);
if (TclFixupForwardJump(envPtr, &jumpAroundElseFixup, jumpDist, 127)) {
elseCodeOffset += 3;
}
jumpDist = (elseCodeOffset - jumpAroundThenFixup.codeOffset);
TclFixupForwardJump(envPtr, &jumpAroundThenFixup, jumpDist, 127);
infoPtr->hasOperators = 1;
infoPtr->exprIsComparison = 0;
}
done:
envPtr->maxStackDepth = maxDepth;
return result;
}
static int
CompileLorExpr(interp, infoPtr, flags, envPtr)
Tcl_Interp *interp;
ExprInfo *infoPtr;
int flags;
CompileEnv *envPtr;
{
int maxDepth;
JumpFixupArray jumpFixupArray;
JumpFixup jumpTrueFixup, jumpFixup;
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;
}
infoPtr->hasOperators = 1;
infoPtr->exprIsJustVarRef = 0;
maxDepth = envPtr->maxStackDepth;
TclInitJumpFixupArray(&jumpFixupArray);
while (infoPtr->token == OR) {
result = GetToken(interp, infoPtr, envPtr);
if (result != TCL_OK) {
goto done;
}
if (jumpFixupArray.next == 0) {
TclEmitForwardJump(envPtr, TCL_TRUE_JUMP, &jumpTrueFixup);
objIndex = TclObjIndexForString("0", 1, 0,
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, 0,
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);
}
}
TclEmitOpcode(INST_DUP, envPtr);
if (jumpFixupArray.next == jumpFixupArray.end) {
TclExpandJumpFixupArray(&jumpFixupArray);
}
fixupIndex = jumpFixupArray.next;
jumpFixupArray.next++;
TclEmitForwardJump(envPtr, TCL_TRUE_JUMP,
&(jumpFixupArray.fixup[fixupIndex]));
result = CompileLandExpr(interp, infoPtr, flags, envPtr);
if (result != TCL_OK) {
goto done;
}
maxDepth = TclMax((envPtr->maxStackDepth + 1), maxDepth);
TclEmitOpcode(INST_LOR, envPtr);
}
for (j = jumpFixupArray.next; j > 0; j--) {
fixupIndex = (j - 1);
currCodeOffset = TclCurrCodeOffset();
jumpDist = (currCodeOffset - jumpFixupArray.fixup[fixupIndex].codeOffset);
TclFixupForwardJump(envPtr, &(jumpFixupArray.fixup[fixupIndex]), jumpDist, 127);
}
done:
infoPtr->exprIsComparison = 0;
TclFreeJumpFixupArray(&jumpFixupArray);
envPtr->maxStackDepth = maxDepth;
return result;
}
static int
CompileLandExpr(interp, infoPtr, flags, envPtr)
Tcl_Interp *interp;
ExprInfo *infoPtr;
int flags;
CompileEnv *envPtr;
{
int maxDepth;
JumpFixupArray jumpFixupArray;
JumpFixup jumpTrueFixup, jumpFixup;
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;
}
infoPtr->hasOperators = 1;
infoPtr->exprIsJustVarRef = 0;
maxDepth = envPtr->maxStackDepth;
TclInitJumpFixupArray(&jumpFixupArray);
while (infoPtr->token == AND) {
result = GetToken(interp, infoPtr, envPtr);
if (result != TCL_OK) {
goto done;
}
if (jumpFixupArray.next == 0) {
TclEmitForwardJump(envPtr, TCL_TRUE_JUMP, &jumpTrueFixup);
objIndex = TclObjIndexForString("0", 1, 0,
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, 0,
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);
}
}
TclEmitOpcode(INST_DUP, envPtr);
if (jumpFixupArray.next == jumpFixupArray.end) {
TclExpandJumpFixupArray(&jumpFixupArray);
}
fixupIndex = jumpFixupArray.next;
jumpFixupArray.next++;
TclEmitForwardJump(envPtr, TCL_FALSE_JUMP,
&(jumpFixupArray.fixup[fixupIndex]));
result = CompileBitOrExpr(interp, infoPtr, flags, envPtr);
if (result != TCL_OK) {
goto done;
}
maxDepth = TclMax((envPtr->maxStackDepth + 1), maxDepth);
TclEmitOpcode(INST_LAND, envPtr);
}
for (j = jumpFixupArray.next; j > 0; j--) {
fixupIndex = (j - 1);
currCodeOffset = TclCurrCodeOffset();
jumpDist = (currCodeOffset - jumpFixupArray.fixup[fixupIndex].codeOffset);
TclFixupForwardJump(envPtr, &(jumpFixupArray.fixup[fixupIndex]),
jumpDist, 127);
}
done:
infoPtr->exprIsComparison = 0;
TclFreeJumpFixupArray(&jumpFixupArray);
envPtr->maxStackDepth = maxDepth;
return result;
}
static int
CompileBitOrExpr(interp, infoPtr, flags, envPtr)
Tcl_Interp *interp;
ExprInfo *infoPtr;
int flags;
CompileEnv *envPtr;
{
int maxDepth = 0;
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);
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);
infoPtr->exprIsComparison = 0;
}
done:
envPtr->maxStackDepth = maxDepth;
return result;
}
static int
CompileBitXorExpr(interp, infoPtr, flags, envPtr)
Tcl_Interp *interp;
ExprInfo *infoPtr;
int flags;
CompileEnv *envPtr;
{
int maxDepth = 0;
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);
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);
infoPtr->exprIsComparison = 0;
}
done:
envPtr->maxStackDepth = maxDepth;
return result;
}
static int
CompileBitAndExpr(interp, infoPtr, flags, envPtr)
Tcl_Interp *interp;
ExprInfo *infoPtr;
int flags;
CompileEnv *envPtr;
{
int maxDepth = 0;
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);
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);
infoPtr->exprIsComparison = 0;
}
done:
envPtr->maxStackDepth = maxDepth;
return result;
}
static int
CompileEqualityExpr(interp, infoPtr, flags, envPtr)
Tcl_Interp *interp;
ExprInfo *infoPtr;
int flags;
CompileEnv *envPtr;
{
int maxDepth = 0;
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);
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;
infoPtr->exprIsComparison = 1;
}
done:
envPtr->maxStackDepth = maxDepth;
return result;
}
static int
CompileRelationalExpr(interp, infoPtr, flags, envPtr)
Tcl_Interp *interp;
ExprInfo *infoPtr;
int flags;
CompileEnv *envPtr;
{
int maxDepth = 0;
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);
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;
infoPtr->exprIsComparison = 1;
}
done:
envPtr->maxStackDepth = maxDepth;
return result;
}
static int
CompileShiftExpr(interp, infoPtr, flags, envPtr)
Tcl_Interp *interp;
ExprInfo *infoPtr;
int flags;
CompileEnv *envPtr;
{
int maxDepth = 0;
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);
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;
infoPtr->exprIsComparison = 0;
}
done:
envPtr->maxStackDepth = maxDepth;
return result;
}
static int
CompileAddExpr(interp, infoPtr, flags, envPtr)
Tcl_Interp *interp;
ExprInfo *infoPtr;
int flags;
CompileEnv *envPtr;
{
int maxDepth = 0;
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);
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;
infoPtr->exprIsComparison = 0;
}
done:
envPtr->maxStackDepth = maxDepth;
return result;
}
static int
CompileMultiplyExpr(interp, infoPtr, flags, envPtr)
Tcl_Interp *interp;
ExprInfo *infoPtr;
int flags;
CompileEnv *envPtr;
{
int maxDepth = 0;
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);
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;
infoPtr->exprIsComparison = 0;
}
done:
envPtr->maxStackDepth = maxDepth;
return result;
}
static int
CompileUnaryExpr(interp, infoPtr, flags, envPtr)
Tcl_Interp *interp;
ExprInfo *infoPtr;
int flags;
CompileEnv *envPtr;
{
int maxDepth = 0;
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);
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;
}
infoPtr->exprIsComparison = 0;
} else {
result = CompilePrimaryExpr(interp, infoPtr, flags, envPtr);
if (result != TCL_OK) {
goto done;
}
maxDepth = envPtr->maxStackDepth;
}
done:
envPtr->maxStackDepth = maxDepth;
return result;
}
static int
CompilePrimaryExpr(interp, infoPtr, flags, envPtr)
Tcl_Interp *interp;
ExprInfo *infoPtr;
int flags;
CompileEnv *envPtr;
{
int maxDepth = 0;
int theToken;
char *dollarPtr, *quotePtr, *cmdPtr, *termPtr;
int result = TCL_OK;
HERE("primaryExpr", 13);
theToken = infoPtr->token;
if ((theToken != DOLLAR) && (theToken != OPEN_PAREN)) {
infoPtr->exprIsJustVarRef = 0;
}
switch (theToken) {
case LITERAL:
TclEmitPush(infoPtr->objIndex, envPtr);
maxDepth = 1;
break;
case DOLLAR:
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:
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:
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);
} else if (termPtr == infoPtr->lastChar) {
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);
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) {
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;
}
static int
CompileMathFuncCall(interp, infoPtr, flags, envPtr)
Tcl_Interp *interp;
ExprInfo *infoPtr;
int flags;
CompileEnv *envPtr;
{
Interp *iPtr = (Interp *) interp;
int maxDepth = 0;
MathFunc *mathFuncPtr;
int objIndex;
Tcl_HashEntry *hPtr;
char *p, *funcName;
char savedChar;
int result, i;
funcName = p = infoPtr->funcName;
while (isalnum(UCHAR(*p)) || (*p == '_')) {
p++;
}
infoPtr->next = p;
result = GetToken(interp, infoPtr, envPtr);
if (result != TCL_OK) {
goto done;
}
if (infoPtr->token != OPEN_PAREN) {
goto syntaxError;
}
result = GetToken(interp, infoPtr, envPtr);
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 (mathFuncPtr->builtinFuncIndex < 0) {
objIndex = TclObjIndexForString(funcName, -1, 1,
0, envPtr);
TclEmitPush(objIndex, envPtr);
maxDepth = 1;
}
*p = savedChar;
if (mathFuncPtr->numArgs > 0) {
for (i = 0; ; i++) {
infoPtr->exprIsComparison = 0;
result = CompileCondExpr(interp, infoPtr, flags, envPtr);
if (result != TCL_OK) {
goto done;
}
if (i == (mathFuncPtr->numArgs-1)) {
if (infoPtr->token == CLOSE_PAREN) {
break;
} 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);
if (result != TCL_OK) {
goto done;
}
maxDepth++;
}
}
if (infoPtr->token != CLOSE_PAREN) {
goto syntaxError;
}
result = GetToken(interp, infoPtr, envPtr);
if (result != TCL_OK) {
goto done;
}
if (mathFuncPtr->builtinFuncIndex >= 0) {
TclEmitInstUInt1(INST_CALL_BUILTIN_FUNC1,
mathFuncPtr->builtinFuncIndex, envPtr);
} else {
TclEmitInstUInt1(INST_CALL_FUNC1, (mathFuncPtr->numArgs+1), envPtr);
}
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;
}
static int
GetToken(interp, infoPtr, envPtr)
Tcl_Interp *interp;
register ExprInfo *infoPtr;
CompileEnv *envPtr;
{
register char *src;
register char c;
register int type;
char *termPtr;
char savedChar;
int objIndex;
long longValue;
double doubleValue;
Tcl_Obj *objPtr;
infoPtr->token = UNKNOWN;
infoPtr->objIndex = -1;
infoPtr->funcName = NULL;
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;
}
} else {
src++;
}
c = *src;
type = CHAR_TYPE(src, infoPtr->lastChar);
}
if (src == infoPtr->lastChar) {
infoPtr->token = END;
infoPtr->next = src;
return TCL_OK;
}
if ((*src != '+') && (*src != '-')) {
int startsWithDigit = isdigit(UCHAR(*src));
if (startsWithDigit && TclLooksLikeInt(src)) {
errno = 0;
longValue = strtoul(src, &termPtr, 0);
if (errno == ERANGE) {
char *s = "integer value too large to represent";
Tcl_ResetResult(interp);
Tcl_AppendToObj(Tcl_GetObjResult(interp), s, -1);
Tcl_SetErrorCode(interp, "ARITH", "IOVERFLOW", s,
(char *) NULL);
return TCL_ERROR;
}
if (termPtr != src) {
savedChar = *termPtr;
*termPtr = '\0';
objIndex = TclObjIndexForString(src, termPtr - src,
0, 0, envPtr);
*termPtr = savedChar;
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;
}
savedChar = *termPtr;
*termPtr = '\0';
objIndex = TclObjIndexForString(src, termPtr - src,
1, 0, envPtr);
*termPtr = savedChar;
objPtr = envPtr->objArrayPtr[objIndex];
objPtr->internalRep.doubleValue = doubleValue;
objPtr->typePtr = &tclDoubleType;
infoPtr->token = LITERAL;
infoPtr->objIndex = objIndex;
infoPtr->next = termPtr;
return TCL_OK;
}
}
}
if (*src == '{') {
int level = 0;
int hasBackslashNL = 0;
char *string = src;
char *last;
int numChars;
char savedChar;
int numRead;
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);
break;
}
} else if (c == '\\') {
if (*src == '\n') {
hasBackslashNL = 1;
}
(void) Tcl_Backslash(src-1, &numRead);
src += numRead - 1;
}
}
string++;
numChars = (last - string + 1);
savedChar = string[numChars];
string[numChars] = '\0';
if (hasBackslashNL && (numChars > 0)) {
char *buffer = ckalloc((unsigned) numChars + 1);
register char *dst = buffer;
register char *p = string;
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,
1, 1, envPtr);
} else {
objIndex = TclObjIndexForString(string, numChars,
1, 0, envPtr);
}
string[numChars] = savedChar;
infoPtr->token = LITERAL;
infoPtr->objIndex = objIndex;
infoPtr->next = src;
return TCL_OK;
}
infoPtr->next = src+1;
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;
}
}
void
Tcl_CreateMathFunc(interp, name, numArgs, argTypes, proc, clientData)
Tcl_Interp *interp;
char *name;
int numArgs;
Tcl_ValueType *argTypes;
Tcl_MathProc *proc;
ClientData clientData;
{
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) {
iPtr->compileEpoch++;
} else {
if (numArgs != mathFuncPtr->numArgs) {
iPtr->compileEpoch++;
}
}
}
mathFuncPtr->builtinFuncIndex = -1;
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;
}