/*=======================================================
* Expr.java
*
* "A Tcl like language implementation in Java named APWTCL
* ((Java) Arnulf's Preferred Web Tcl)"
*
* APWTCL Expr class
*
* Released under same BSD license as Tcl.
* (Tcl BSD license found at <http://www.tcl.tk/software/tcltk/license.html>)
*
* Copyright 2012 Arnulf P. Wiedemann
*
*/
package org.apwtcl.lang;
import java.lang.reflect.Method;
import java.util.ArrayList;
public class Expr extends Token implements Debug {
private static int oid = 0;
private int id;
private Interp interp;
/* ==================== Expr ================================== */
public Expr(Interp interp) {
oid++;
id = oid;
this.interp = interp;
}
/* ==================== mySelf ================================== */
public String mySelf() {
String str = "Expr!"+id+"!";
return str;
}
/* ==================== toString ===================================== */
public String toString() {
return mySelf()+"!";
}
/* ==================== toDebugString ===================================== */
public String toDebugString() {
StringBuffer str = new StringBuffer(mySelf()+"\n");
return str.toString();
}
/* ==================== exprPush ================================== */
public void exprPush(ExprStack e, ApwtclObj obj_ptr) {
obj_ptr.incrRefCount("I_EXPR_1");
if (e.stacklen >= e.stack.size()) {
e.stack.add(obj_ptr);
} else {
e.stack.set(e.stacklen, obj_ptr);
}
e.stacklen++;
}
/* ==================== exprPop ================================== */
public ApwtclObj exprPop(ExprStack e) {
return e.stack.get(--e.stacklen);
}
/* ==================== exprOpNumUnary ================================== */
public int exprOpNumUnary(ExprStack e) {
int intresult = 0;
int rc = OK;
ApwtclObj A = exprPop(e);
ArrayList<Double> dA_ptr = new ArrayList<Double>();
double dA = 0;
ArrayList<Long> wA_ptr = new ArrayList<Long>();
long wA;
long wC = 0;
double dC = 0;
if ((A.obj_type != OBJ_TYPE_DOUBLE || A.bytes != null) && interp.int_obj_type.getWideNoErr(A, wA_ptr) == OK) {
intresult = 1;
wA = wA_ptr.get(0);
switch (e.opcode) {
case TOKEN_EXPROP_FUNC_INT:
wC = wA;
break;
case TOKEN_EXPROP_FUNC_ROUND:
wC = wA;
break;
case TOKEN_EXPROP_FUNC_DOUBLE:
dA = wA;
intresult = 0;
break;
case TOKEN_EXPROP_FUNC_ABS:
wC = wA >= 0 ? wA : -wA;
break;
case TOKEN_EXPROP_UNARYMINUS:
wC = -wA;
break;
case TOKEN_EXPROP_UNARYPLUS:
wC = wA;
break;
case TOKEN_EXPROP_NOT:
if (wA == 0) {
wC = 1;
} else {
wC = 0;
}
break;
default:
print("abort()");
}
} else {
if ((rc = interp.int_obj_type.getDouble(A, dA_ptr)) == OK) {
dA = dA_ptr.get(0);
switch (e.opcode) {
case TOKEN_EXPROP_FUNC_INT:
wC = dA_ptr.get(0).longValue();
intresult = 1;
break;
case TOKEN_EXPROP_FUNC_ROUND:
if (dA_ptr.get(0) < 0) {
wC = ((Double)(dA_ptr.get(0) - 0.5)).longValue();
} else {
wC = ((Double)(dA_ptr.get(0) + 0.5)).longValue();
}
intresult = 1;
break;
case TOKEN_EXPROP_FUNC_DOUBLE:
dC = dA_ptr.get(0);
break;
case TOKEN_EXPROP_FUNC_ABS:
dC = dA_ptr.get(0) >= 0 ? dA_ptr.get(0) : -dA_ptr.get(0);
break;
case TOKEN_EXPROP_UNARYMINUS:
dC = -dA_ptr.get(0);
break;
case TOKEN_EXPROP_UNARYPLUS:
dC = dA_ptr.get(0);
break;
case TOKEN_EXPROP_NOT:
if (dA_ptr.get(0) == 0) {
wC = 1;
} else {
wC = 0;
}
intresult = 1;
break;
default:
print("abort()");
}
}
}
if (rc == OK) {
if (intresult != 0) {
exprPush(e, interp.int_obj_type.newIntObj((int)(wC & 0xffffffff)));
} else {
exprPush(e, interp.double_obj_type.newDoubleObj(dC));
}
}
A.decrRefCount("D_EXPR_1");
return rc;
}
/* ==================== randDouble ================================== */
public double randDouble() {
double x = 1234.0;
// x = randomBytes(64);
return x / ~0;
}
/* ==================== exprOpIntUnary ================================== */
public int exprOpIntUnary(ExprStack e) {
ApwtclObj A = exprPop(e);
ArrayList<Long> wA = new ArrayList<Long>();
int rc;
rc = interp.int_obj_type.getWide(A, wA);
if (rc == OK) {
switch (e.opcode) {
case TOKEN_EXPROP_BITNOT:
exprPush(e, interp.int_obj_type.newIntObj((int)(~wA.get(0) & 0xffffffff)));
break;
case TOKEN_EXPROP_FUNC_SRAND:
// prngSeed(wA, 64);
exprPush(e, interp.double_obj_type.newDoubleObj(randDouble()));
break;
default:
print("abort()");
}
}
A.decrRefCount("D_EXPR_2");
return rc;
}
/* ==================== exprOpNone ================================== */
public int exprOpNone(ExprStack e) {
panic(e.opcode != TOKEN_EXPROP_FUNC_RAND, "JimExprOpNone only support rand()");
exprPush(e, interp.double_obj_type.newDoubleObj(randDouble()));
return OK;
}
/* ==================== exprOpDoubleUnary ================================== */
public int exprOpDoubleUnary(ExprStack e) {
int rc;
ApwtclObj A = exprPop(e);
ArrayList<Double> dA = new ArrayList<Double>();
double dC = 0;
rc = interp.int_obj_type.getDouble(A, dA);
if (rc == OK) {
switch (e.opcode) {
case TOKEN_EXPROP_FUNC_SIN:
// dC = sin(dA);
break;
case TOKEN_EXPROP_FUNC_COS:
// dC = cos(dA);
break;
case TOKEN_EXPROP_FUNC_TAN:
// dC = tan(dA);
break;
case TOKEN_EXPROP_FUNC_ASIN:
// dC = asin(dA);
break;
case TOKEN_EXPROP_FUNC_ACOS:
// dC = acos(dA);
break;
case TOKEN_EXPROP_FUNC_ATAN:
// dC = atan(dA);
break;
case TOKEN_EXPROP_FUNC_SINH:
// dC = sinh(dA);
break;
case TOKEN_EXPROP_FUNC_COSH:
// dC = cosh(dA);
break;
case TOKEN_EXPROP_FUNC_TANH:
// dC = tanh(dA);
break;
case TOKEN_EXPROP_FUNC_CEIL:
// dC = ceil(dA);
break;
case TOKEN_EXPROP_FUNC_FLOOR:
// dC = floor(dA);
break;
case TOKEN_EXPROP_FUNC_EXP:
// dC = exp(dA);
break;
case TOKEN_EXPROP_FUNC_LOG:
// dC = log(dA);
break;
case TOKEN_EXPROP_FUNC_LOG10:
// dC = log10(dA);
break;
case TOKEN_EXPROP_FUNC_SQRT:
// dC = sqrt(dA);
break;
default:
print("abort()");
}
exprPush(e, interp.double_obj_type.newDoubleObj(dC));
}
A.decrRefCount("D_EXPR_3");
return rc;
}
/* A binary operation on two ints */
/* ==================== exprOpIntBin ================================== */
public int exprOpIntBin(ExprStack e) {
ApwtclObj B = exprPop(e);
ApwtclObj A = exprPop(e);
ArrayList<Long> wA_ptr = new ArrayList<Long>();
long wA;
ArrayList<Long> wB_ptr = new ArrayList<Long>();
long wB;
int rc = ERROR;
if (interp.int_obj_type.getWide(A, wA_ptr) == OK && interp.int_obj_type.getWide(B, wB_ptr) == OK) {
long wC = 0;
rc = OK;
wA = wA_ptr.get(0);
wB = wB_ptr.get(0);
switch (e.opcode) {
case TOKEN_EXPROP_LSHIFT:
wC = wA << wB;
break;
case TOKEN_EXPROP_RSHIFT:
wC = wA >> wB;
break;
case TOKEN_EXPROP_BITAND:
wC = wA & wB;
break;
case TOKEN_EXPROP_BITXOR:
wC = wA ^ wB;
break;
case TOKEN_EXPROP_BITOR:
wC = wA | wB;
break;
case TOKEN_EXPROP_MOD:
if (wB == 0) {
wC = 0;
interp.setResultString("Division by zero");
rc = ERROR;
} else {
/*
* From Tcl 8.x
*
* This code is tricky: C doesn't guarantee much
* about the quotient or remainder, but Tcl does.
* The remainder always has the same sign as the
* divisor and a smaller absolute value.
*/
int negative = 0;
if (wB < 0) {
wB = -wB;
wA = -wA;
negative = 1;
}
wC = wA % wB;
if (wC < 0) {
wC += wB;
}
if (negative != 0) {
wC = -wC;
}
}
break;
case TOKEN_EXPROP_ROTL:
case TOKEN_EXPROP_ROTR:
/* uint32_t would be better. But not everyone has inttypes.h? */
long uA = wA;
long uB = wB;
int S = 8 * 8;
/* Shift left by the word size or more is undefined. */
uB %= S;
if (e.opcode == TOKEN_EXPROP_ROTR) {
uB = S - uB;
}
wC = (uA << uB) | (uA >> (S - uB));
break;
default:
print("abort()");
}
exprPush(e, interp.int_obj_type.newIntObj((int)(wC & 0xffffffff)));
}
A.decrRefCount("D_EXPR_4");
B.decrRefCount("D_EXPR_5");
return rc;
}
/* A binary operation on two ints or two doubles (or two strings for some ops) */
/* ==================== exprOpBin ================================== */
public int exprOpBin(ExprStack e) {
int intresult = 0;
int rc = OK;
ArrayList<Double> dA_ptr = new ArrayList<Double>();
ArrayList<Double> dB_ptr = new ArrayList<Double>();
double dC = 0;
ArrayList<Long> wA_ptr = new ArrayList<Long>();
ArrayList<Long> wB_ptr = new ArrayList<Long>();
long wA;
long wB;
double dA;
double dB;
long wC = 0;
boolean wD = false;
boolean have_bool = false;
ApwtclObj B = exprPop(e);
ApwtclObj A = exprPop(e);
if ((A.obj_type != OBJ_TYPE_DOUBLE || A.bytes != null) &&
(B.obj_type != OBJ_TYPE_DOUBLE || B.bytes != null) &&
interp.int_obj_type.getWideNoErr(A, wA_ptr) == OK && interp.int_obj_type.getWideNoErr(B, wB_ptr) == OK) {
wA = wA_ptr.get(0);
wB = wB_ptr.get(0);
/* Both are ints */
intresult = 1;
switch (e.opcode) {
case TOKEN_EXPROP_POW:
case TOKEN_EXPROP_FUNC_POW:
// wC = powWide(wA, wB);
break;
case TOKEN_EXPROP_ADD:
wC = wA + wB;
break;
case TOKEN_EXPROP_SUB:
wC = wA - wB;
break;
case TOKEN_EXPROP_MUL:
wC = wA * wB;
break;
case TOKEN_EXPROP_DIV:
if (wB == 0) {
interp.setResultString("Division by zero");
rc = ERROR;
} else {
/*
* From Tcl 8.x
*
* This code is tricky: C doesn't guarantee much
* about the quotient or remainder, but Tcl does.
* The remainder always has the same sign as the
* divisor and a smaller absolute value.
*/
if (wB < 0) {
wB = -wB;
wA = -wA;
}
wC = wA / wB;
if (wA % wB < 0) {
wC--;
}
}
break;
case TOKEN_EXPROP_LT:
have_bool = true;
wD = wA < wB;
break;
case TOKEN_EXPROP_GT:
have_bool = true;
wD = wA > wB;
break;
case TOKEN_EXPROP_LTE:
have_bool = true;
wD = wA <= wB;
break;
case TOKEN_EXPROP_GTE:
have_bool = true;
wD = wA >= wB;
break;
case TOKEN_EXPROP_NUMEQ:
have_bool = true;
wD = wA == wB;
break;
case TOKEN_EXPROP_NUMNE:
have_bool = true;
wD = wA != wB;
break;
default:
print("abort()");
}
} else {
if (interp.int_obj_type.getDouble(A, dA_ptr) == OK && interp.int_obj_type.getDouble(B, dB_ptr) == OK) {
dA = dA_ptr.get(0);
dB = dB_ptr.get(0);
switch (e.opcode) {
case TOKEN_EXPROP_POW:
case TOKEN_EXPROP_FUNC_POW:
// if (interp.HAVE_MATH_FUNCTIONS) {
// dC = pow(dA, dB);
// } else {
interp.setResultString("unsupported");
rc = ERROR;
// }
break;
case TOKEN_EXPROP_ADD:
dC = dA + dB;
break;
case TOKEN_EXPROP_SUB:
dC = dA - dB;
break;
case TOKEN_EXPROP_MUL:
dC = dA * dB;
break;
case TOKEN_EXPROP_DIV:
if (dB == 0) {
// if (interp.HAVE_INFINITY) {
// dC = dA < 0 ? -INFINITY : INFINITY;
// } else {
// dC = (dA < 0 ? -1.0 : 1.0) * strtod("Inf", null);
// }
} else {
dC = dA / dB;
}
break;
case TOKEN_EXPROP_LT:
wD = dA < dB;
have_bool = true;
intresult = 1;
break;
case TOKEN_EXPROP_GT:
have_bool = true;
wD = dA > dB;
intresult = 1;
break;
case TOKEN_EXPROP_LTE:
have_bool = true;
wD = dA <= dB;
intresult = 1;
break;
case TOKEN_EXPROP_GTE:
have_bool = true;
wD = dA >= dB;
intresult = 1;
break;
case TOKEN_EXPROP_NUMEQ:
have_bool = true;
wD = dA == dB;
intresult = 1;
break;
case TOKEN_EXPROP_NUMNE:
have_bool = true;
wD = dA != dB;
intresult = 1;
break;
default:
print("abort()");
}
} else {
/* Handle the string case */
/* REVISIT: Could optimise the eq/ne case by checking lengths */
int i = A.stringCompareObj(B, 0);
intresult = 1;
switch (e.opcode) {
case TOKEN_EXPROP_LT:
have_bool = true;
wD = i < 0;
break;
case TOKEN_EXPROP_GT:
have_bool = true;
wD = i > 0;
break;
case TOKEN_EXPROP_LTE:
have_bool = true;
wD = i <= 0;
break;
case TOKEN_EXPROP_GTE:
have_bool = true;
wD = i >= 0;
break;
case TOKEN_EXPROP_NUMEQ:
have_bool = true;
wD = i == 0;
break;
case TOKEN_EXPROP_NUMNE:
have_bool = true;
wD = i != 0;
break;
default:
rc = ERROR;
break;
}
}
}
if (rc == OK) {
if (intresult != 0 || have_bool) {
/* javascript returns true or false for comparisions, we need 1 or 0 for Tcl */
if (have_bool) {
if (wD) {
wC = 1;
} else {
wC = 0;
}
} else {
if (have_bool) {
if (wD) {
wC = 1;
} else {
wC = 0;
}
}
}
ApwtclObj result_obj_ptr = interp.int_obj_type.newIntObj(((Long)wC).intValue());
exprPush(e, result_obj_ptr);
} else {
exprPush(e, interp.double_obj_type.newDoubleObj(dC));
}
}
A.decrRefCount("D_EXPR_6");
B.decrRefCount("D_EXPR_7");
return rc;
}
/* ==================== exprOpStrBin ================================== */
public int exprOpStrBin(ExprStack e) {
ApwtclObj B = exprPop(e);
ApwtclObj A = exprPop(e);
boolean wC = false;
switch (e.opcode) {
case TOKEN_EXPROP_STREQ:
case TOKEN_EXPROP_STRNE:
int Alen = A.getStringLength();
int Blen = B.getStringLength();
String sA = A.getString();
String sB = B.getString();
int wD;
if (e.opcode == TOKEN_EXPROP_STREQ) {
wC = (Alen == Blen && sA == sB);
} else {
wC = (Alen != Blen || sA != sB);
}
break;
case TOKEN_EXPROP_STRIN:
wD = interp.list_obj_type.searchList(B, A);
if (wD >= 0) {
wC = true;
} else {
wC = false;
}
break;
case TOKEN_EXPROP_STRNI:
wD = interp.list_obj_type.searchList(B, A);
if (wD >= 0) {
wC = true;
} else {
wC = false;
}
break;
default:
print("abort()");
}
exprPush(e, interp.int_obj_type.newIntObj(wC ? 1 : 0));
A.decrRefCount("D_EXPR_8");
B.decrRefCount("D_EXPR_9");
return OK;
}
/* ==================== exprBool ================================== */
public int exprBool(ApwtclObj obj) {
ArrayList<Long> l_ptr = new ArrayList<Long>();
Long l;
ArrayList<Double> d_ptr = new ArrayList<Double>();
double d;
if (interp.int_obj_type.getLong(obj, l_ptr) == OK) {
l = l_ptr.get(0);
if (l != 0) {
return 1;
} else {
return 0;
}
}
if (interp.int_obj_type.getDouble(obj, d_ptr) == OK) {
d = d_ptr.get(0);
if (d != 0) {
return 1;
} else {
return 0;
}
}
return -1;
}
/* ==================== exprOpAndLeft ================================== */
public int exprOpAndLeft(ExprStack e) {
ApwtclObj skip = exprPop(e);
ApwtclObj A = exprPop(e);
int rc = OK;
switch (exprBool(A)) {
case 0:
/* false, so skip RHS opcodes with a 0 result */
e.skip = (int)interp.int_obj_type.wideValue(skip);
exprPush(e, interp.int_obj_type.newIntObj(0));
break;
case 1:
/* true so continue */
break;
case -1:
/* Invalid */
rc = ERROR;
}
A.decrRefCount("D_EXPR_10");
skip.decrRefCount("D_EXPR_11");
return rc;
}
/* ==================== exprOpOrLeft ================================== */
public int exprOpOrLeft(ExprStack e) {
ApwtclObj skip = exprPop(e);
ApwtclObj A = exprPop(e);
int rc = OK;
switch (exprBool(A)) {
case 0:
/* false, so do nothing */
break;
case 1:
/* true so skip RHS opcodes with a 1 result */
e.skip = (int)interp.int_obj_type.wideValue(skip);
exprPush(e, interp.int_obj_type.newIntObj(1));
break;
case -1:
/* Invalid */
rc = ERROR;
break;
}
A.decrRefCount("D_EXPR_12");
skip.decrRefCount("D_EXPR_13");
return rc;
}
/* ==================== exprOpAndOrRight ================================== */
public int exprOpAndOrRight(ExprStack e) {
ApwtclObj A = exprPop(e);
int rc = OK;
switch (exprBool(A)) {
case 0:
exprPush(e, interp.int_obj_type.newIntObj(0));
break;
case 1:
exprPush(e, interp.int_obj_type.newIntObj(1));
break;
case -1:
/* Invalid */
rc = ERROR;
break;
}
A.decrRefCount("D_EXPR_14");
return rc;
}
/* ==================== exprOpTernaryLeft ================================== */
public int exprOpTernaryLeft(ExprStack e) {
ApwtclObj skip = exprPop(e);
ApwtclObj A = exprPop(e);
int rc = OK;
/* Repush A */
exprPush(e, A);
switch (exprBool(A)) {
case 0:
/* false, skip RHS opcodes */
e.skip = (int)interp.int_obj_type.wideValue(skip);
/* Push a dummy value */
exprPush(e, interp.int_obj_type.newIntObj(0));
break;
case 1:
/* true so do nothing */
break;
case -1:
/* Invalid */
rc = ERROR;
break;
}
A.decrRefCount("D_EXPR_15");
skip.decrRefCount("D_EXPR_16");
return rc;
}
/* ==================== exprOpColonLeft ================================== */
public int exprOpColonLeft(ExprStack e) {
ApwtclObj skip = exprPop(e);
ApwtclObj B = exprPop(e);
ApwtclObj A = exprPop(e);
/* No need to check for A as non-boolean */
if (exprBool(A) != 0) {
/* true, so skip RHS opcodes */
e.skip = (int)interp.int_obj_type.wideValue(skip);
/* Repush B as the answer */
exprPush(e, B);
}
skip.decrRefCount("D_EXPR_17");
A.decrRefCount("D_EXPR_18");
B.decrRefCount("D_EXPR_19");
return OK;
}
/* ==================== exprOpNull ================================== */
public int exprOpNull(ExprStack e) {
return OK;
}
/* ==================== tt_name ================================== */
public String tt_name(int token) {
ArrayList<String> tt_names = new ArrayList<String>();
tt_names.add("NIL");
tt_names.add("STR");
tt_names.add("ESC");
tt_names.add("VAR");
tt_names.add("ARY");
tt_names.add("CMD");
tt_names.add("SEP");
tt_names.add("EOL");
tt_names.add("EOF");
tt_names.add("LIN");
tt_names.add("WRD");
tt_names.add("(((");
tt_names.add(")))");
tt_names.add(",,,");
tt_names.add("INT");
tt_names.add("DBL");
tt_names.add("$()");
if (token < TOKEN_EXPR_OP) {
return tt_names.get(token);
} else {
ExprOperator op = interp.expr_obj_type.exprOperatorInfoByOpcode(token);
if (op.name != null) {
return op.name;
}
return "("+token+")";
}
}
/* -----------------------------------------------------------------------------
* Expression Object
* ---------------------------------------------------------------------------*/
/* ==================== freeInternalRep ================================== */
public void freeInternalRep(ApwtclObj obj_ptr) {
ExprCompiled expr = (ExprCompiled)obj_ptr.ptrValue_GetPtr();
if (expr != null) {
// if (--expr.in_use != 0) {
// return;
// }
interp.expr_obj_type.exprFreeByteCode(expr);
}
}
/* ==================== dupExprInternalRep ================================== */
public void dupExprInternalRep(ApwtclObj src_ptr, ApwtclObj dup_ptr) {
/* Just returns an simple string. */
dup_ptr.obj_type = -1;
}
/* ==================== exprTernaryGetColonLeftIndex ================================== */
/**
* Returns the index of the COLON_LEFT to the left of 'right_index'
* taking into account nesting.
*
* The expression *must* be well formed, thus a COLON_LEFT will always be found.
*/
public int exprTernaryGetColonLeftIndex(ExprTokenStack expr, int right_index) {
int ternary_count = 1;
right_index--;
while (right_index > 1) {
if (expr.token_list.get(right_index).token == TOKEN_EXPROP_TERNARY_LEFT) {
ternary_count--;
} else {
if (expr.token_list.get(right_index).token == TOKEN_EXPROP_COLON_RIGHT) {
ternary_count++;
} else {
if (expr.token_list.get(right_index).token == TOKEN_EXPROP_COLON_LEFT && ternary_count == 1) {
return right_index;
}
}
}
right_index--;
}
/*notreached*/
return -1;
}
/* ==================== exprTernaryGetMoveIndices ================================== */
/**
* Find the left/right indices for the ternary expression to the left of 'right_index'.
*
* Returns 1 if found, and fills in *prev_right_index and *prev_left_index.
* Otherwise returns 0.
*/
public int exprTernaryGetMoveIndices(ExprTokenStack expr, int right_index, ArrayList<Integer> prev_right_index, ArrayList<Integer> prev_left_index) {
int i = right_index - 1;
int ternary_count = 1;
while (i > 1) {
if (expr.token_list.get(i).token == TOKEN_EXPROP_TERNARY_LEFT) {
if (--ternary_count == 0 && expr.token_list.get(i - 2).token == TOKEN_EXPROP_COLON_RIGHT) {
prev_right_index.add(i - 2);
prev_left_index.add(exprTernaryGetColonLeftIndex(expr, prev_right_index.get(0)));
return 1;
}
} else {
if (expr.token_list.get(i).token == TOKEN_EXPROP_COLON_RIGHT) {
if (ternary_count == 0) {
return 0;
}
ternary_count++;
}
}
i--;
}
return 0;
}
/* ==================== exprTernaryReorderExpression ================================== */
/*
* ExprTernaryReorderExpression description
* ========================================
*
* ?: is right-to-left associative which doesn't work with the stack-based
* expression engine. The fix is to reorder the bytecode.
*
* The expression:
*
* expr 1?2:0?3:4
*
* Has initial bytecode:
*
* '1' '2' (40=TERNARY_LEFT) '2' (41=TERNARY_RIGHT) '2' (43=COLON_LEFT) '0' (44=COLON_RIGHT)
* '2' (40=TERNARY_LEFT) '3' (41=TERNARY_RIGHT) '2' (43=COLON_LEFT) '4' (44=COLON_RIGHT)
*
* The fix involves simulating this expression instead:
*
* expr 1?2:(0?3:4)
*
* With the following bytecode:
*
* '1' '2' (40=TERNARY_LEFT) '2' (41=TERNARY_RIGHT) '10' (43=COLON_LEFT) '0'
* '2' (40=TERNARY_LEFT)
* '3' (41=TERNARY_RIGHT) '2' (43=COLON_LEFT) '4' (44=COLON_RIGHT) (44=COLON_RIGHT)
*
* i.e. The token COLON_RIGHT at index 8 is moved towards the end of the stack,
* all tokens above 8
* are shifted down and the skip count of the token TOKEN_EXPROP_COLON_LEFT at
* index 5 is
* incremented by the amount tokens shifted down. The token TOKEN_EXPROP_COLON_RIGHT
* that is moved
* is identified as immediately preceeding a token TOKEN_EXPROP_TERNARY_LEFT
*
* ExprTernaryReorderExpression works thus as follows :
* - start from the end of the stack
* - while walking towards the beginning of the stack
* if token=TOKEN_EXPROP_COLON_RIGHT then
* find the associated token TOKEN_EXPROP_TERNARY_LEFT, which allows to
* find the associated token previous(TOKEN_EXPROP_COLON_RIGHT)
* find the associated token previous(TOKEN_EXPROP_LEFT_RIGHT)
* if all found then
* perform the rotation
* update the skip count of the token previous(TOKEN_EXPROP_LEFT_RIGHT)
* end if
* end if
*
* Note: care has to be taken for nested ternary constructs!!!
*/
public void exprTernaryReorderExpression(ExprTokenStack expr) {
int i;
for (i = expr.len - 1; i > 1; i--) {
ArrayList<Integer> prev_right_index = new ArrayList<Integer>();
ArrayList<Integer> prev_left_index = new ArrayList<Integer>();
int j;
ParseToken tmp;
if (expr.token_list.get(i).token != TOKEN_EXPROP_COLON_RIGHT) {
continue;
}
/* COLON_RIGHT found: get the indexes needed to move the tokens in the stack (if any) */
if (exprTernaryGetMoveIndices(expr, i, prev_right_index, prev_left_index) == 0) {
continue;
}
/*
** rotate tokens down
**
** +-> [i] : TOKEN_EXPROP_COLON_RIGHT
** | | |
** | V V
** | [...] : ...
** | | |
** | V V
** | [...] : ...
** | | |
** | V V
** +- [prev_right_index] : TOKEN_EXPROP_COLON_RIGHT
*/
tmp = expr.token_list.get(prev_right_index.get(0).intValue());
for (j = prev_right_index.get(0); j < i; j++) {
expr.token_list.add(expr.token_list.get(j + 1));
}
expr.token_list.add(tmp);
/* Increment the 'skip' count associated to the previous TOKEN_EXPROP_COLON_LEFT token
*
* This is 'colon left increment' = i - prev_right_index
*
* [prev_left_index] : TOKEN_EXPROP_LEFT_RIGHT
* [prev_left_index-1] : skip_count
*
*/
// wideValue(expr.token_list.get(prev_left_index - 1).obj_ptr) += (i - prev_right_index);
// ApwtclObj my_obj_ptr = expr.token_list.get(prev_left_index.get(0) - 1).obj_ptr;
/* Adjust for i-- in the loop */
i++;
}
}
/* ==================== getExpression ================================== */
public ExprCompiled getExpression(ApwtclObj obj_ptr) {
if (obj_ptr.obj_type != OBJ_TYPE_EXPR) {
if (interp.expr_obj_type.setFromAny(obj_ptr) != OK) {
return null;
}
}
return (ExprCompiled)obj_ptr.getIntRepPtr();
}
/* ==================== evalExpression ================================== */
/* -----------------------------------------------------------------------------
* Expressions evaluation.
* RAPL uses a specialized stack-based virtual machine for expressions,
* that takes advantage of the fact that expr's operators
* can't be redefined.
*
* evalExpression() uses the bytecode compiled by
* setExprFromAny() method of the "expression" object.
*
* On success a Tcl Object containing the result of the evaluation
* is stored into expResultPtrPtr (having refcount of 1), and exp.OK is
* returned.
* On error the function returns a retcode != to exp.OK and set a suitable
* error on the interp.
* ---------------------------------------------------------------------------*/
public int evalExpression(ApwtclObj expr_obj_ptr, ArrayList<ApwtclObj> expr_result_ptr_ptr) {
ExprCompiled expr;
int i;
int retcode = OK;
String fcn;
//print("==== expr_result_ptr_ptr!"+expr_result_ptr_ptr.size()+"!");
expr = getExpression(expr_obj_ptr);
if (expr == null) {
return ERROR; /* error in expression. */
}
/* Check for one of the following common expressions used by while/for
*
* CONST
* $a
* !$a
* $a < CONST, $a < $b
* $a <= CONST, $a <= $b
* $a > CONST, $a > $b
* $a >= CONST, $a >= $b
* $a != CONST, $a != $b
* $a == CONST, $a == $b
*/
{
ApwtclObj obj_ptr;
/* STEP 1 -- Check if there are the conditions to run the specialized
* version of while */
switch (expr.len) {
case 1:
if (expr.token_list.get(0).token == TOKEN_EXPR_INT) {
expr_result_ptr_ptr.add(expr.token_list.get(0).obj_ptr);
expr_result_ptr_ptr.get(0).incrRefCount("I_EXPR_2");
return OK;
}
if (expr.token_list.get(0).token == TOKEN_VAR) {
ArrayList<Variable> var_ptr_ptr = new ArrayList<Variable>();
obj_ptr = interp.variable_obj_type.getVariable(expr.token_list.get(0).obj_ptr, FUNCTION_FLAGS_LEAVE_ERR_MSG, var_ptr_ptr);
if (obj_ptr != null) {
expr_result_ptr_ptr.add(obj_ptr);
expr_result_ptr_ptr.get(0).incrRefCount("I_EXPR_3");
return OK;
}
}
break;
case 2:
if (expr.token_list.get(1).token == TOKEN_EXPROP_NOT && expr.token_list.get(0).token == TOKEN_VAR) {
ArrayList<Long> wide_value_ptr = new ArrayList<Long>();
ArrayList<Variable> var_ptr_ptr = new ArrayList<Variable>();
long wide_value;
obj_ptr = interp.variable_obj_type.getVariable(expr.token_list.get(0).obj_ptr, FUNCTION_FLAGS_NONE, var_ptr_ptr);
if (obj_ptr != null && interp.int_obj_type.isWide(obj_ptr) && interp.int_obj_type.getWide(obj_ptr, wide_value_ptr) == OK) {
wide_value = wide_value_ptr.get(0);
expr_result_ptr_ptr.set(0, wide_value == 0 ? interp.false_obj : interp.true_obj);
expr_result_ptr_ptr.get(0).incrRefCount("I_EXPR_4");
return OK;
}
}
break;
case 3:
if (expr.token_list.get(0).token == TOKEN_VAR && (expr.token_list.get(1).token == TOKEN_EXPR_INT || expr.token_list.get(1).token == TOKEN_VAR)) {
switch (expr.token_list.get(2).token) {
case TOKEN_EXPROP_LT:
case TOKEN_EXPROP_LTE:
case TOKEN_EXPROP_GT:
case TOKEN_EXPROP_GTE:
case TOKEN_EXPROP_NUMEQ:
case TOKEN_EXPROP_NUMNE:
/* optimise ok */
ArrayList<Long> wide_valueA_ptr = new ArrayList<Long>();
long wide_valueA;
ArrayList<Long> wide_valueB_ptr = new ArrayList<Long>();
long wideValueB;
ArrayList<Variable> var_ptr_ptr = new ArrayList<Variable>();
obj_ptr = interp.variable_obj_type.getVariable(expr.token_list.get(0).obj_ptr, FUNCTION_FLAGS_NONE, var_ptr_ptr);
if (obj_ptr != null && interp.int_obj_type.isWide(obj_ptr) && interp.int_obj_type.getWide(obj_ptr, wide_valueA_ptr) == OK) {
wide_valueA = wide_valueA_ptr.get(0);
if (expr.token_list.get(1).token == TOKEN_VAR) {
obj_ptr = interp.variable_obj_type.getVariable(expr.token_list.get(1).obj_ptr, FUNCTION_FLAGS_NONE, var_ptr_ptr);
} else {
obj_ptr = expr.token_list.get(1).obj_ptr;
}
if (obj_ptr != null && interp.int_obj_type.isWide(obj_ptr) && interp.int_obj_type.getWide(obj_ptr, wide_valueB_ptr) == OK) {
long wide_valueB = wide_valueB_ptr.get(0);
boolean cmpRes;
switch (expr.token_list.get(2).token) {
case TOKEN_EXPROP_LT:
cmpRes = wide_valueA < wide_valueB;
break;
case TOKEN_EXPROP_LTE:
cmpRes = wide_valueA <= wide_valueB;
break;
case TOKEN_EXPROP_GT:
cmpRes = wide_valueA > wide_valueB;
break;
case TOKEN_EXPROP_GTE:
cmpRes = wide_valueA >= wide_valueB;
break;
case TOKEN_EXPROP_NUMEQ:
cmpRes = wide_valueA == wide_valueB;
break;
case TOKEN_EXPROP_NUMNE:
cmpRes = wide_valueA != wide_valueB;
break;
default: /*notreached */
cmpRes = false;
}
expr_result_ptr_ptr.add(cmpRes ? interp.true_obj : interp.false_obj);
expr_result_ptr_ptr.get(0).incrRefCount("I_EXPR_5");
return OK;
}
}
}
}
break;
}
}
/* In order to avoid that the internal repr gets freed due to
* shimmering of the exprObjPtr's object, we make the internal rep
* shared.
* */
expr.in_use++;
/* The stack-based expr VM itself */
/* Stack allocation. Expr programs have the feature that
* a program of length N can't require a stack longer than
* N.
* */
ExprStack e = new ExprStack(interp);
e.stack = new ArrayList<ApwtclObj>();
e.stacklen = 0;
/* Execute every instruction */
for (i = 0; i < expr.len && retcode == OK; i++) {
ApwtclObj obj_ptr;
switch (expr.token_list.get(i).token) {
case TOKEN_EXPR_INT:
case TOKEN_EXPR_DOUBLE:
case TOKEN_BRACE:
case TOKEN_QUOTE:
case TOKEN_STR:
exprPush(e, expr.token_list.get(i).obj_ptr);
break;
case TOKEN_VAR:
ArrayList<Variable> var_ptr_ptr = new ArrayList<Variable>();
obj_ptr = interp.variable_obj_type.getVariable(expr.token_list.get(i).obj_ptr, FUNCTION_FLAGS_LEAVE_ERR_MSG, var_ptr_ptr);
if (obj_ptr != null) {
exprPush(e, obj_ptr);
} else {
print("var not found!"+expr.token_list.get(i).obj_ptr+"!");
retcode = ERROR;
}
break;
case TOKEN_VAR_ARRAY_NAME:
obj_ptr = interp.array_obj_type.expandArray(expr.token_list.get(i).obj_ptr);
if (obj_ptr != null) {
exprPush(e, obj_ptr);
} else {
print("array var not found!"+expr.token_list.get(i).obj_ptr+"!");
retcode = ERROR;
}
break;
case TOKEN_QUOTE_ESC:
case TOKEN_ESC:
ArrayList<ApwtclObj> obj_ptr_ptr = new ArrayList<ApwtclObj>();
retcode = interp.script_obj_type.substObj(expr.token_list.get(i).obj_ptr, obj_ptr_ptr, FUNCTION_FLAGS_NONE);
obj_ptr = obj_ptr_ptr.get(0);
if (retcode == OK) {
exprPush(e, obj_ptr);
}
break;
case TOKEN_CMD:
// retcode = interp.eval_statement.evalObj(expr.token_list.get(0).getBuiltinCommandFromString(i).obj_ptr);
print("evalExpression TOKEN_CMD not yet implemented!!");
retcode = ERROR;
if (retcode == OK) {
exprPush(e, interp.getResult());
}
break;
default:
/* Find and execute the operation */
e.skip = 0;
e.opcode = expr.token_list.get(i).token;
fcn = interp.expr_obj_type.exprOperatorInfoByOpcode(e.opcode).funcop;
//print("fcn!"+fcn+"!");
Method m = null;
try {
Class[] parameter_types = new Class[]{ExprStack.class};
//print("1 ME!"+fcn+"!"+parameter_types+"!");
m = Expr.class.getMethod(fcn, parameter_types);
//print("2 expr fcn Command cmd_method!"+fcn+"!"+m+"!");
retcode = ((Integer)m.invoke(this, e)).intValue();
} catch(Exception excp) {
print("invoke expr Command Exception!"+excp.getMessage()+"!");
excp.printStackTrace();
}
// retcode = eval ("exp."+fcn+"(e)");
/* Skip some opcodes if necessary */
i += e.skip;
continue;
}
}
expr.in_use--;
if (retcode == OK) {
expr_result_ptr_ptr.add(exprPop(e));
} else {
for (i = 0; i < e.stacklen; i++) {
e.stack.get(i).decrRefCount("D_EXPR_20");
}
}
return retcode;
}
/* ==================== getBoolFromExpr ================================== */
public int getBoolFromExpr(ApwtclObj expr_obj_ptr, ArrayList<Integer> bool_ptr) {
int retcode;
ArrayList<Long> wide_value_ptr = new ArrayList<Long>();
long wide_value;
ArrayList<Double> double_value_ptr = new ArrayList<Double>();
double double_value;
ArrayList<ApwtclObj> expr_result_ptr_ptr = new ArrayList<ApwtclObj>();
ApwtclObj expr_result_ptr;
retcode = evalExpression(expr_obj_ptr, expr_result_ptr_ptr);
if (retcode != OK) {
return retcode;
}
expr_result_ptr = expr_result_ptr_ptr.get(0);
if (interp.int_obj_type.getWideNoErr(expr_result_ptr, wide_value_ptr) != OK) {
if (interp.int_obj_type.getDouble(expr_result_ptr, double_value_ptr) != OK) {
expr_result_ptr.decrRefCount("D_EXPR_21");
return ERROR;
} else {
double_value = double_value_ptr.get(0);
expr_result_ptr.decrRefCount("D_EXPR_22");
bool_ptr.add(double_value != 0 ? 1 : 0);
return OK;
}
}
wide_value = wide_value_ptr.get(0);
bool_ptr.add(wide_value != 0 ? 1 : 0);
expr_result_ptr.decrRefCount("D_EXPR_23");
return OK;
}
}