APWTCL Arnulf's Preferred Web Tcl

Artifact [2fbe0c3d50]
Login

Artifact [2fbe0c3d50]

Artifact 2fbe0c3d50ab865462f415c9e73fe688fef9ef3c:


/*=======================================================
 * 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;
  }
}