RAPL

Artifact [b29f72ac74]
Login

Artifact [b29f72ac74]

Artifact b29f72ac7443d7b848582b09cd7649519ccd0106:


/*====================================================
 * rapl_expr.js "A Tcl like language implementation in Javascript named WebRAPL 
 * (Web Rapid Application Programming Language)"
 *
 * RAPL expr implementation
 *
 * Released under BSD license.
 * (BSD license found at <http://www.tcl.tk/software/tcltk/license.html>)
 *
 * Arnulf Wiedemann    2011
 */

RP.add("rapl-expr", function(R, name) {

function Expr(interp) {
  R.log('constructor called', '2.life', 'Expr', true);
  // kweight
  var exp = this;
  exp.interp = interp;
  var constructor = exp.constructor;
  Expr.superclass.constructor.apply(exp, arguments);

  R.Base.expr_oid++;
  exp.oid = R.Base.expr_oid;

  R.log('constructor end', '2.life', 'Expr', true);
}

R.extend(Expr, R.Token, {
  my_name: "Expr",

  /* ==================== exprPush ================================== */
  exprPush: function(e, obj_ptr) {
    var exp = this;

    obj_ptr.incrRefCount();
    e.stack[e.stacklen++] = obj_ptr;
  },

  /* ==================== exprPop ================================== */
  exprPop: function(e) {
    return e.stack[--e.stacklen];
  },

  /* ==================== exprOpNumUnary ================================== */
  exprOpNumUnary: function(e) {
    var exp = this;
    var intresult = 0;
    var rc = exp.OK;
    var A = exp.exprPop(e);
    var dA = new Array();
    var dC = 0;
    var wA = new Array();
    var wC = 0;

    if ((A.obj_type != exp.interp.double_obj_type || A.bytes) && exp.interp.int_obj_type.getWideNoErr(A, wA) == exp.OK) {
      intresult = 1;

      switch (e.opcode) {
      case exp.TOKEN_EXPROP_FUNC_INT:
        wC = wA[0];
        break;
      case exp.TOKEN_EXPROP_FUNC_ROUND:
        wC = wA[0];
        break;
      case exp.TOKEN_EXPROP_FUNC_DOUBLE:
        dC = wA[0];
        intresult = 0;
        break;
      case exp.TOKEN_EXPROP_FUNC_ABS:
        wC = wA >= 0 ? wA[0] : -wA[0];
        break;
      case exp.TOKEN_EXPROP_UNARYMINUS:
        wC = -wA[0];
        break;
      case exp.TOKEN_EXPROP_UNARYPLUS:
         wC = wA[0];
        break;
      case exp.TOKEN_EXPROP_NOT:
        wC = !wA[0];
        break;
      default:
        abort();
      }
    } else {
      if ((rc = exp.getDouble(A, dA)) == exp.OK) {
        switch (e.opcode) {
        case exp.TOKEN_EXPROP_FUNC_INT:
          wC = dA[0];
          intresult = 1;
          break;
        case exp.TOKEN_EXPROP_FUNC_ROUND:
          wC = dA[0] < 0 ? (dA[0] - 0.5) : (dA[0] + 0.5);
          intresult = 1;
          break;
        case exp.TOKEN_EXPROP_FUNC_DOUBLE:
          dC = dA[0];
          break;
        case exp.TOKEN_EXPROP_FUNC_ABS:
          dC = dA[0] >= 0 ? dA[0] : -dA[0];
          break;
        case exp.TOKEN_EXPROP_UNARYMINUS:
          dC = -dA[0];
          break;
        case exp.TOKEN_EXPROP_UNARYPLUS:
          dC = dA[0];
          break;
        case exp.TOKEN_EXPROP_NOT:
          wC = !dA[0];
          intresult = 1;
          break;
        default:
          abort();
        }
      }
    }

    if (rc == exp.OK) {
      if (intresult) {
        exp.exprPush(e, exp.interp.int_obj_type.newIntObj(wC));
      } else {
        ExprPush(e, exp.interp.double_obj_type.newDoubleObj(dC));
      }
    }

    A.decrRefCount();

    return rc;
  },

  /* ==================== randDouble ================================== */
  randDouble: function() {
    var x;

    x = exp.randomBytes(64);
    return x / ~0;
  },

  /* ==================== exprOpIntUnary ================================== */
  exprOpIntUnary: function(e) {
    var exp = this;
    var A = exp.exprPop(e);
    var wA = new Array();
    var rc;

    rc = exp.getWide(A, wA);
    if (rc == exp.OK) {
      switch (e.opcode) {
      case exp.TOKEN_EXPROP_BITNOT:
        exp.exprPush(e, exp.interp.int_obj_type.newIntObj(~wA));
        break;
      case exp.TOKEN_EXPROP_FUNC_SRAND:
        exp.prngSeed(wA, 64);
        exp.exprPush(e, exp.interp.double_obj_type.newDoubleObj(exp.randDouble()));
        break;
      default:
        abort();
      }
    }
    A.decrRefCount();
    return rc;
  },

  /* ==================== exprOpNone ================================== */
  exprOpNone: function(e) {
    var exp = this;

    exp.panic(e.opcode != exp.TOKEN_EXPROP_FUNC_RAND, "JimExprOpNone only support rand()");
    exp.exprPush(e, exp.interp.double_obj_type.newDoubleObj(exp.randDouble()));
    return exp.OK;
  },

  /* ==================== exprOpDoubleUnary ================================== */
  exprOpDoubleUnary: function(e) {
    var exp = this;
    var rc;
    var A = exp.exprPop(e);
    var dA = new Array();
    var dC;

    rc = exp.getDouble(A, dA);
    if (rc == exp.OK) {
      switch (e.opcode) {
      case exp.TOKEN_EXPROP_FUNC_SIN:
        dC = sin(dA);
        break;
      case exp.TOKEN_EXPROP_FUNC_COS:
        dC = cos(dA);
        break;
      case exp.TOKEN_EXPROP_FUNC_TAN:
        dC = tan(dA);
        break;
      case exp.TOKEN_EXPROP_FUNC_ASIN:
        dC = asin(dA);
        break;
      case exp.TOKEN_EXPROP_FUNC_ACOS:
        dC = acos(dA);
        break;
      case exp.TOKEN_EXPROP_FUNC_ATAN:
        dC = atan(dA);
        break;
      case exp.TOKEN_EXPROP_FUNC_SINH:
        dC = sinh(dA);
        break;
      case exp.TOKEN_EXPROP_FUNC_COSH:
        dC = cosh(dA);
        break;
      case exp.TOKEN_EXPROP_FUNC_TANH:
        dC = tanh(dA);
        break;
      case exp.TOKEN_EXPROP_FUNC_CEIL:
        dC = ceil(dA);
        break;
      case exp.TOKEN_EXPROP_FUNC_FLOOR:
        dC = floor(dA);
        break;
      case exp.TOKEN_EXPROP_FUNC_EXP:
        dC = exp(dA);
        break;
      case exp.TOKEN_EXPROP_FUNC_LOG:
        dC = log(dA);
        break;
      case exp.TOKEN_EXPROP_FUNC_LOG10:
        dC = log10(dA);
        break;
      case exp.TOKEN_EXPROP_FUNC_SQRT:
        dC = sqrt(dA);
        break;
      default:
        abort();
      }
      exp.exprPush(e, exp.interp.double_obj_type.newDoubleObj(dC));
    }
    A.decrRefCount();
    return rc;
  },

  /* A binary operation on two ints */

  /* ==================== exprOpIntBin ================================== */
  exprOpIntBin: function(e) {
    var exp = this;
    var B = exp.exprPop(e);
    var A = exp.exprPop(e);
    var wA = new Array();
    var wB = new Array();
    var rc = exp.ERROR;

    if (exp.getWide(A, wA) == exp.OK && exp.getWide(B, wB) == exp.OK) {
      var wC;

      rc = exp.OK;
      switch (e.opcode) {
      case exp.TOKEN_EXPROP_LSHIFT:
        wC = wA << wB;
        break;
      case exp.TOKEN_EXPROP_RSHIFT:
        wC = wA >> wB;
        break;
      case exp.TOKEN_EXPROP_BITAND:
        wC = wA & wB;
        break;
      case exp.TOKEN_EXPROP_BITXOR:
        wC = wA ^ wB;
        break;
      case exp.TOKEN_EXPROP_BITOR:
        wC = wA | wB;
        break;
      case exp.TOKEN_EXPROP_MOD:
        if (wB == 0) {
          wC = 0;
          exp.interp.setResultString("Division by zero", -1);
          rc = exp.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.
             */
            var negative = 0;
            if (wB < 0) {
              wB = -wB;
              wA = -wA;
              negative = 1;
            }
            wC = wA % wB;
            if (wC < 0) {
              wC += wB;
            }
            if (negative) {
              wC = -wC;
            }
        }
        break;
      case exp.TOKEN_EXPROP_ROTL:
      case exp.TOKEN_EXPROP_ROTR:
        /* uint32_t would be better. But not everyone has inttypes.h? */
        var uA = wA;
        var uB = wB;
        S = 8 * 8;
        /* Shift left by the word size or more is undefined. */
        uB %= S;
        if (e.opcode == exp.TOKEN_EXPROP_ROTR) {
          uB = S - uB;
        }
        wC = (uA << uB) | (uA >> (S - uB));
        break;
      default:
        abort();
      }
      exp.exprPush(e, exp.interp.int_obj_type.newIntObj(wC));
    }
    A.decrRefCount();
    B.decrRefCount();
    return rc;
  },


  /* A binary operation on two ints or two doubles (or two strings for some ops) */

  /* ==================== exprOpBin ================================== */
  exprOpBin: function(e) {
    var exp = this;
    var intresult = 0;
    var rc = exp.OK;
    var dA;
    var dB;
    var dC = 0;
    var wA = new Array();
    var wB = new Array();
    var wC = 0;
    var B = exp.exprPop(e);
    var A = exp.exprPop(e);

    if ((A.obj_type != exp.interp.double_obj_type || A.bytes) &&
        (B.obj_type != exp.interp.double_obj_type || B.bytes) &&
        exp.interp.int_obj_type.getWideNoErr(A, wA) == exp.OK && exp.interp.int_obj_type.getWideNoErr(B, wB) == exp.OK) {

      wA = parseInt(wA[0]);
      wB = parseInt(wB[0]);
      /* Both are ints */
      intresult = 1;
      switch (e.opcode) {
      case exp.TOKEN_EXPROP_POW:
      case exp.TOKEN_EXPROP_FUNC_POW:
        wC = exp.powWide(wA, wB);
        break;
      case exp.TOKEN_EXPROP_ADD:
        wC = wA + wB;
        break;
      case exp.TOKEN_EXPROP_SUB:
        wC = wA - wB;
        break;
      case exp.TOKEN_EXPROP_MUL:
        wC = wA * wB;
        break;
      case exp.TOKEN_EXPROP_DIV:
        if (wB == 0) {
          exp.interp.setResultString("Division by zero", -1);
          rc = exp.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 exp.TOKEN_EXPROP_LT:
        wC = wA < wB;
        break;
      case exp.TOKEN_EXPROP_GT:
        wC = wA > wB;
        break;
      case exp.TOKEN_EXPROP_LTE:
        wC = wA <= wB;
        break;
      case exp.TOKEN_EXPROP_GTE:
        wC = wA >= wB;
        break;
      case exp.TOKEN_EXPROP_NUMEQ:
        wC = wA == wB;
        break;
      case exp.TOKEN_EXPROP_NUMNE:
        wC = wA != wB;
        break;
      default:
        abort();
      }
    } else {
      if (exp.getDouble(A, dA) == exp.OK && exp.getDouble(B, dB) == exp.OK) {
        switch (e.opcode) {
        case exp.TOKEN_EXPROP_POW:
        case exp.TOKEN_EXPROP_FUNC_POW:
          if (exp.interp.HAVE_MATH_FUNCTIONS) {
            dC = pow(dA, dB);
          } else {
            exp.interp.setResultString("unsupported", -1);
            rc = exp.ERROR;
          }
          break;
        case exp.TOKEN_EXPROP_ADD:
          dC = dA + dB;
          break;
        case exp.TOKEN_EXPROP_SUB:
          dC = dA - dB;
          break;
        case exp.TOKEN_EXPROP_MUL:
          dC = dA * dB;
          break;
        case exp.TOKEN_EXPROP_DIV:
          if (dB == 0) {
            if (exp.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 exp.TOKEN_EXPROP_LT:
          wC = dA < dB;
          intresult = 1;
          break;
        case exp.TOKEN_EXPROP_GT:
          wC = dA > dB;
          intresult = 1;
          break;
        case exp.TOKEN_EXPROP_LTE:
          wC = dA <= dB;
          intresult = 1;
          break;
        case exp.TOKEN_EXPROP_GTE:
          wC = dA >= dB;
          intresult = 1;
          break;
        case exp.TOKEN_EXPROP_NUMEQ:
          wC = dA == dB;
          intresult = 1;
          break;
        case exp.TOKEN_EXPROP_NUMNE:
          wC = dA != dB;
          intresult = 1;
          break;
        default:
          abort();
        }
      } else {
        /* Handle the string case */
        /* REVISIT: Could optimise the eq/ne case by checking lengths */
        var i = exp.stringCompareObj(A, B, 0);

        intresult = 1;
        switch (e.opcode) {
        case exp.TOKEN_EXPROP_LT:
          wC = i < 0;
          break;
        case exp.TOKEN_EXPROP_GT:
          wC = i > 0;
          break;
        case exp.TOKEN_EXPROP_LTE:
          wC = i <= 0;
          break;
        case exp.TOKEN_EXPROP_GTE:
          wC = i >= 0;
          break;
        case exp.TOKEN_EXPROP_NUMEQ:
          wC = i == 0;
          break;
        case exp.TOKEN_EXPROP_NUMNE:
          wC = i != 0;
          break;
        default:
          rc = exp.ERROR;
          break;
        }
      }
    }
    if (rc == exp.OK) {
      if (intresult) {
        /* javascript returns true or false for comparisions, we need 1 or 0 for Tcl */
        if (wC == true) {
          wC = 1;
	} else {
          if (wC == false) {
            wC = 0;
	  }
	}
        exp.exprPush(e, exp.interp.int_obj_type.newIntObj(wC));
      } else {
        exp.exprPush(e, exp.interp.double_obj_type.newDoubleObj(dC));
      }
    }
    A.decrRefCount();
    B.decrRefCount();
    return rc;
  },

  /* ==================== searchList ================================== */
  searchList: function(list_obj_ptr, val_obj) {
    var listlen;
    var i;

    listlen = exp.listLength(list_obj_ptr);
    for (i = 0; i < listlen; i++) {
      var obj_ptr = new Array();

      exp.listIndex(list_obj_ptr, i, obj_ptr, exp.FUNCTION_FLAGS_NONE);
      if (exp.stringEqObj(obj_ptr, val_obj)) {
        return 1;
      }
    }
    return 0;
  },

  /* ==================== exprOpStrBin ================================== */
  exprOpStrBin: function(e) {
    var exp = this;
    var obj_ptr = exp.interp.default_obj;
    var B = exp.exprPop(e);
    var A = exp.exprPop(e);
    var wC;

    switch (e.opcode) {
    case exp.TOKEN_EXPROP_STREQ:
    case exp.TOKEN_EXPROP_STRNE: 
      var Alen = new Array();
      var Blen = new Array();
      var sA = obj_ptr.getString(A, Alen);
      var sB = obj_ptr.getString(B, Blen);

      if (e.opcode == exp.TOKEN_EXPROP_STREQ) {
        wC = (Alen == Blen && sA == sB);
      } else {
        wC = (Alen != Blen || sA == sB);
      }
      break;
    case exp.TOKEN_EXPROP_STRIN:
      wC = exp.searchList(B, A);
      break;
    case exp.TOKEN_EXPROP_STRNI:
      wC = !exp.searchList(interp, B, A);
      break;
    default:
      abort();
    }
    exp.exprPush(e, exp.interp.int_obj_type.newIntObj(wC));
    A.decrRefCount();
    B.decrRefCount();
    return exp.OK;
  },

  /* ==================== exprBool ================================== */
  exprBool: function(obj) {
    var l = new Array();
    var d = new Array();

    if (exp.getLong(interp, obj, l) == exp.OK) {
      return l != 0;
    }
    if (exp.getDouble(d) == exp.OK) {
      return d != 0;
    }
    return -1;
  },

  /* ==================== exprOpAndLeft ================================== */
  exprOpAndLeft: function(e) {
    var exp = this;
    var skip = exp.exprPop(e);
    var A = exp.exprPop(e);
    var rc = exp.OK;

    switch (exp.exprBool(A)) {
    case 0:
      /* false, so skip RHS opcodes with a 0 result */
      e.skip = exp.wideValue(skip);
      exp.exprPush(e, exp.interp.int_obj_type.newIntObj(0));
      break;
    case 1:
      /* true so continue */
      break;
    case -1:
      /* Invalid */
      rc = exp.ERROR;
    }
    A.decrRefCount();
    skip.decrRefCount();
    return rc;
  },

  /* ==================== exprOpOrLeft ================================== */
  exprOpOrLeft: function(e) {
    var exp = this;
    var skip = exp.exprPop(e);
    var A = exp.exprPop(e);
    var rc = exp.OK;

    switch (exp.exprBool(A)) {
    case 0:
      /* false, so do nothing */
      break;
    case 1:
      /* true so skip RHS opcodes with a 1 result */
      e.skip = exp.wideValue(skip);
      exp.exprPush(e, exp.interp.int_obj_type.newIntObj(1));
      break;
    case -1:
      /* Invalid */
      rc = exp.ERROR;
      break;
    }
    A.decrRefCount();
    skip.decrRefCount();
    return rc;
  },

  /* ==================== exprOpAndOrRight ================================== */
  exprOpAndOrRight: function(e) {
    var exp = this;
    var A = exp.exprPop(e);
    var rc = exp.OK;

    switch (exp.exprBool(A)) {
    case 0:
      exp.exprPush(e, exp.interp.int_obj_type.newIntObj(0));
      break;
    case 1:
      exp.exprPush(e, exp.interp.int_obj_type.newIntObj(1));
      break;
    case -1:
      /* Invalid */
      rc = exp.ERROR;
      break;
    }
    A.decrRefCount();
    return rc;
  },

  /* ==================== exprOpTernaryLeft ================================== */
  exprOpTernaryLeft: function(e) {
    var exp = this;
    var skip = exp.exprPop(e);
    var A = exp.exprPop(e);
    var rc = expOK;

    /* Repush A */
    exp.exprPush(e, A);

    switch (exp.exprBool(A)) {
    case 0:
      /* false, skip RHS opcodes */
      e.skip = exp.wideValue(skip);
      /* Push a dummy value */
      exp.exprPush(e, exp.interp.int_obj_type.newIntObj(0));
      break;
    case 1:
      /* true so do nothing */
      break;
    case -1:
      /* Invalid */
      rc = exp.ERROR;
      break;
    }
    A.decrRefCount();
    skip.decrRefCount();
    return rc;
  },

  /* ==================== exprOpColonLeft ================================== */
  exprOpColonLeft: function(e) {
    var exp = this;
    var skip = exp.exprPop(e);
    var B = exp.exprPop(e);
    var A = exp.exprPop(e);

    /* No need to check for A as non-boolean */
    if (exp.exprBool(A)) {
      /* true, so skip RHS opcodes */
      e.skip = exp.wideValue(skip);
      /* Repush B as the answer */
      exp.exprPush(e, B);
    }
    skip.decrRefCount();
    A.decrRefCount();
    B.decrRefCount();
    return exp.OK;
  },

  /* ==================== exprOpNull ================================== */
  exprOpNull: function(e) {
    return exp.OK;
  },

  /* ==================== exprOperatorInfoByOpcode ================================== */
  exprOperatorInfoByOpcode: function(opcode) {
    var exp = this;
    var dummy_op = {
      name: null,
      precedence: 0,
      arity: 0,
      opcode: "",
      lazy: 0
    };
    if (opcode < exp.TOKEN_EXPR_OP) {
        return dummy_op;
    }
    return exp.exprOperators[opcode - exp.TOKEN_EXPR_OP];
  },

  /* ==================== tt_name ================================== */
  tt_name: function(token) {
    var exp = this;
    var tt_names = [ "NIL", "STR", "ESC", "VAR", "ARY", "CMD", "SEP", "EOL", "EOF", "LIN", "WRD", "(((", ")))", ",,,", "INT", "DBL", "$()" ];

    if (token < exp.TOKEN_EXPR_OP) {
      return tt_names[token];
    } else {
      op = exp.exprOperatorInfoByOpcode(token);

      if (op.name) {
        return op.name;
      }
      return "("+token+")";
    }
  },

  /* ==================== getWideNoErr ================================== */
  old_getWideNoErr: function(obj_ptr, wide_ptr) {
    var exp = this;

    if (obj_ptr.obj_type != exp.interp.int_obj_type && exp.interp.int_obj_type.setFromAny(obj_ptr, exp.FUNCTION_FLAGS_NONE) == exp.ERROR) {
      return exp.ERROR;
    }
    wide_ptr[0] = obj_ptr.wideValue();
    return exp.OK;
  },

  /* -----------------------------------------------------------------------------
   * Expression Object
   * ---------------------------------------------------------------------------*/
  /* ==================== exprFreeByteCode ================================== */
  exprFreeByteCode: function(expr) {
    var exp = this;
    var obj_ptr = exp.interp.default_obj;
    var i;

    for (i = 0; i < expr.len; i++) {
      expr.token_list[i].obj_ptr.decrRefCount();
    }
//    obj_ptr.free(expr.token_list);
//    obj_ptr.free(expr);
  },

  /* ==================== exprFreeInternalRep ================================== */
  freeExprInternalRep: function(obj_ptr) {
    var expr = obj_ptr.ptr();

    if (expr) {
      if (--expr.inUse != 0) {
        return;
      }
      exp.exprFreeByteCode(expr);
    }
  },

  /* ==================== dupExprInternalRep ================================== */
  dupExprInternalRep: function(src_ptr, dup_ptr) {

    /* Just returns an simple string. */
    dupPtr.obj_type = null;
  },

  /* ==================== exprCheckCorrectness ================================== */
  /* Check if an expr program looks correct. */
  exprCheckCorrectness: function(expr) {
    var exp = this;
    var i;
    var stacklen = 0;
    var ternary = 0;

    /* Try to check if there are stack underflows,
     * and make sure at the end of the program there is
     * a single result on the stack. */
    for (i = 0; i < expr.len; i++) {
        var t = expr.token_list[i];
        var op = exp.exprOperatorInfoByOpcode(t.token);

        stacklen -= op.arity;
        if (stacklen < 0) {
          break;
        }
        if (t.token == exp.TOKEN_EXPROP_TERNARY || t.token == exp.TOKEN_EXPROP_TERNARY_LEFT) {
          ternary++;
        } else {
          if (t.token == exp.TOKEN_EXPROP_COLON || t.token == exp.TOKEN_EXPROP_COLON_LEFT) {
            ternary--;
          }
        }
        /* All operations and operands add one to the stack */
        stacklen++;
    }
    if (stacklen != 1 || ternary != 0) {
        return exp.ERROR;
    }
    return exp.OK;
  },

  /* This procedure converts every occurrence of || and && opereators
   * in lazy unary versions.
   *
   * a b || is converted into:
   *
   * a <offset> |L b |R
   *
   * a b && is converted into:
   *
   * a <offset> &L b &R
   *
   * "|L" checks if 'a' is true:
   *   1) if it is true pushes 1 and skips <offset> instructions to reach
   *      the opcode just after |R.
   *   2) if it is false does nothing.
   * "|R" checks if 'b' is true:
   *   1) if it is true pushes 1, otherwise pushes 0.
   *
   * "&L" checks if 'a' is true:
   *   1) if it is true does nothing.
   *   2) If it is false pushes 0 and skips <offset> instructions to reach
   *      the opcode just after &R
   * "&R" checks if 'a' is true:
   *      if it is true pushes 1, otherwise pushes 0.
   */
  /* ==================== exprAddLazyOperator ================================== */
  exprAddLazyOperator: function(expr, t) {
    var exp = this;
    var obj_ptr = exp.interp.default_obj;
    var i;
    var leftindex;
    var arity;
    var offset;

    /* Search for the end of the first operator */
    leftindex = expr.len - 1;

    arity = 1;
    while (arity) {
      var tt = expr.token_list[leftindex];

      if (tt.token >= exp.TOKEN_EXPR_OP) {
        arity += exp.exprOperatorInfoByOpcode(tt.token).arity;
      }
      arity--;
      if (--leftindex < 0) {
        return exp.ERROR;
      }
    }
    leftindex++;
    /* Move them up */
    for (var j = leftindex + 2; j > expr.len -leftindex; j--) {
      expr.token_list[j], expr.token_list[j - 2];
    }
    expr.len += 2;
    offset = (expr.len - leftindex) - 1;

    /* Now we rely on the fact the the left and right version have opcodes
     * 1 and 2 after the main opcode respectively
     */
    expr.token_list[leftindex + 1].token = t.token + 1;
    expr.token_list[leftindex + 1].obj_ptr = exp.interp.empty_obj;

    expr.token_list[leftindex].token = exp.TOKEN_EXPR_INT;
    expr.token_list[leftindex].obj_ptr = exp.interp.int_obj_type.newIntObj(offset);

    /* Now add the 'R' operator */
    expr.token_list[expr.len].obj_ptr = exp.interp.empty_obj;
    expr.token_list[expr.len].token = t.token + 2;
    expr.len++;

    /* Do we need to adjust the skip count for any &L, |L, ?L or :L in the left operand? */
    for (i = leftindex - 1; i > 0; i--) {
      var op = exp.exprOperatorInfoByOpcode(expr.token_list[i].token);

      if (op.lazy == exp.EXPR_TYPE_LAZY_LEFT) {
        if (exp.wideValue(expr.token_list[i - 1].obj_ptr) + i - 1 >= leftindex) {
          exp.wideValue(expr.token_list[i - 1].obj_ptr) += 2;
        }
      }
    }
    return exp.OK;
  },

  /* ==================== exprAddOperator ================================== */
  exprAddOperator: function(expr, t) {
    var exp = this;
    var token = {
      token: 0,
      obj_ptr: null
    };
    var op = exp.exprOperatorInfoByOpcode(t.token);

    if (op.lazy == exp.EXPR_TYPE_LAZY_OP) {
      if (exp.exprAddLazyOperator(expr, t) != exp.OK) {
        exp.interp.setResultFormatted("Expression has bad operands to %s", op.name);
        return exp.ERROR;
      }
    } else {
      token.obj_ptr = exp.interp.empty_obj;
      token.token = t.token;
      expr.token_list[expr.len] = token;
      expr.len++;
    }
    return exp.OK;
  },

  /**
   * 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.
   */
  /* ==================== exprTernaryGetColonLeftIndex ================================== */
  exprTernaryGetColonLeftIndex: function(expr, right_index) {
    var exp = this;
    var ternary_count = 1;

    right_index--;
    while (right_index > 1) {
      if (expr.token_list[right_index].token == exp.TOKEN_EXPROP_TERNARY_LEFT) {
        ternary_count--;
      } else {
        if (expr.token_list[right_index].token == exp.TOKEN_EXPROP_COLON_RIGHT) {
          ternary_count++;
        } else {
          if (expr.token_list[right_index].token == exp.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.
   */
  exprTernaryGetMoveIndices: function(expr, right_index, prev_right_index, prev_left_index) {
    var exp = this;
    var i = right_index - 1;
    var ternary_count = 1;

    while (i > 1) {
      if (expr.token_list[i].token == exp.TOKEN_EXPROP_TERNARY_LEFT) {
        if (--ternary_count == 0 && expr.token_list[i - 2].token == exp.TOKEN_EXPROP_COLON_RIGHT) {
          prev_right_index[0] = i - 2;
          prev_left_index[0] = exp.exprTernaryGetColonLeftIndex(expr, prev_right_index);
          return 1;
        }
      } else {
        if (expr.token_list[i].token == exp.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!!!
  */
  exprTernaryReorderExpression: function(expr) {
    var exp = this;
    var i;

    for (i = expr.len - 1; i > 1; i--) {
      var prev_right_index = new Array();
      var prev_left_index = new Array();
      var j;
      var tmp;

      if (expr.token_list[i].token != exp.TOKEN_EXPROP_COLON_RIGHT) {
        continue;
      }

      /* COLON_RIGHT found: get the indexes needed to move the tokens in the stack (if any) */
      if (exp.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[prev_right_index];
      for (j = prev_right_index; j < i; j++) {
        expr.token_list[j] = expr.token_list[j + 1];
      }
      expr.token_list[i] = 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
       *
       */
      exp.wideValue(expr.token_list[prev_left_index-1].obj_ptr) += (i - prev_right_index);

      /* Adjust for i-- in the loop */
      i++;
    }
  },

  /* ==================== exprCreateByteCode ================================== */
  exprCreateByteCode: function(script, file_name_obj) {
    var exp = this;
    var token_list = script.parse_token_list;
    var obj_ptr = exp.interp.default_obj;
    var stack = new Array();
    var expr;
    var ok = 1;
    var i;
    var prev_token = exp.TOKEN_NONE;
    var have_ternary = 0;
    /* -1 for EOL */
    var count = token_list.length - 1;

    var expr = {
      len: 0,
      token_list: null,
      in_use: 1
    };
    var stack = {
      len: 0,
      maxlen: 0,
      vector: null
    }
    exp.initStack(stack);
    /* Need extra bytecodes for lazy operators.
     * Also check for the ternary operator
     */
    for (i = 0; i < token_list.length - 1; i++) {
      var t = token_list[i];
      var op = exp.exprOperatorInfoByOpcode(t.token);

      if (op.lazy == exp.EXPR_TYPE_LAZY_OP) {
        count += 2;
        /* Ternary is a lazy op but also needs reordering */
        if (t.token == exp.TOKEN_EXPROP_TERNARY) {
          have_ternary = 1;
        }
      }
    }
    expr.token_list = new Array()
    for (i = 0; i < token_list.length && ok; i++) {
      var t = token_list[i];

      var token = {
        token: 0,
	obj_ptr: null
      };

      if (t.token == exp.TOKEN_EOL) {
        break;
      }
      switch (t.token) {
      case exp.TOKEN_STR:
      case exp.TOKEN_ESC:
      case exp.TOKEN_VAR:
      case exp.TOKEN_VAR_ARRAY_NAME:
      case exp.TOKEN_EXPRSUGAR:
      case exp.TOKEN_CMD:
        token.obj_ptr = exp.interp.string_obj_type.newStringObj(script.getText(t.start, t.len), t.len);
        token.token = t.token;
        if (t.token == exp.TOKEN_CMD) {
            /* Only commands need source info */
            exp.interp.source_obj_type.setSourceInfo(token.obj_ptr, file_name_obj, t.line);
        }
        expr.token_list[expr.len] = token;
        expr.len++;
        break;
      case exp.TOKEN_EXPR_INT:
        token.obj_ptr = exp.interp.int_obj_type.newIntObj(script.getText(t.start, t.len));
        token.token = t.token;
        expr.token_list[expr.len] = token;
        expr.len++;
        break;
      case exp.TOKEN_EXPR_DOUBLE:
        token.obj_ptr = exp.interp.double_obj_type.newDoubleObj(script.getText(t.start, t.len));
        token.token = t.token;
        expr.token_list[expr.len] = token;
        expr.len++;
        break;
      case exp.TOKEN_SUBEXPR_START:
        exp.stackPush(stack, t);
        prevtt = exp.TOKEN_NONE;
        continue;
      case exp.TOKEN_SUBEXPR_COMMA:
        /* Simple approach. Comma is simply ignored */
        continue;
      case exp.TOKEN_SUBEXPR_END:
        ok = 0;
        while (stack.len) {
          var tt = exp.stackPop(stack);

          if (tt.token == exp.TOKEN_SUBEXPR_START) {
            ok = 1;
            break;
          }
          if (exp.exprAddOperator(expr, tt) != exp.OK) {
            /* Free the stack used for the compilation. */
            exp.freeStack(stack);
            for (i = 0; i < expr.len; i++) {
              expr.token_list[i].obj_ptr.incrRefCount();
            }
            if (!ok) {
              exp.exprFreeByteCode(expr);
              return null;
            }
            return expr;
          }
        }
        if (!ok) {
          exp.interp.setResultString("Unexpected close parenthesis", -1);
          /* Free the stack used for the compilation. */
          exp.freeStack(stack);
          for (i = 0; i < expr.len; i++) {
            expr.token_list[i].obj_ptr.incrRefCount();
          }
          if (!ok) {
            exp.exprFreeByteCode(expr);
            return null;
          }
          return expr;
        }
        break;
      default:
        /* Must be an operator */
        var op;
        var tt;

        /* Convert -/+ to unary minus or unary plus if necessary */
        if (prevtt == exp.TOKEN_NONE || prevtt >= exp.TOKEN_EXPR_OP) {
            if (t.token == exp.TOKEN_EXPROP_SUB) {
                t.token = exp.TOKEN_EXPROP_UNARYMINUS;
            } else {
              if (t.token == exp.TOKEN_EXPROP_ADD) {
                t.token = exp.TOKEN_EXPROP_UNARYPLUS;
              }
            }
        }
        op = exp.exprOperatorInfoByOpcode(t.token);
        /* Now handle precedence */
        while ((tt = exp.stackPeek(stack)) != null) {
          var tt_op = exp.exprOperatorInfoByOpcode(tt.token);

          /* Note that right-to-left associativity of ?: operator is handled later */
          if (op.arity != 1 && tt_op.precedence >= op.precedence) {
            if (exp.exprAddOperator(expr, tt) != exp.OK) {
              ok = 0;
              /* Free the stack used for the compilation. */
              exp.freeStack(stack);
              for (i = 0; i < expr.len; i++) {
                expr.token_list[i].obj_ptr.incrRefCount();
              }
              if (!ok) {
                exp.exprFreeByteCode(expr);
                return null;
              }
              return expr;
            }
            exp.stackPop(stack);
          } else {
            break;
          }
        }
        exp.stackPush(stack, t);
        break;
      }
      prevtt = t.token;
    }
    /* Reduce any remaining subexpr */
    while (stack.len) {
      var tt = exp.stackPop(stack);

      if (tt.token == exp.TOKEN_SUBEXPR_START) {
        ok = 0;
        exp.interp.setResultString("Missing close parenthesis", -1);
        /* Free the stack used for the compilation. */
        exp.freeStack(stack);
        for (i = 0; i < expr.len; i++) {
          expr.token_list[i].obj_ptr.incrRefCount();
        }
        if (!ok) {
          exp.exprFreeByteCode(expr);
          return null;
        }
        return expr;
      }
      if (exp.exprAddOperator(expr, tt) != exp.OK) {
        ok = 0;
      }
    }
    if (ok && have_ternary) {
      exp.exprTernaryReorderExpression(expr);
    }
    /* Free the stack used for the compilation. */
    exp.freeStack(stack);
    for (i = 0; i < expr.len; i++) {
      expr.token_list[i].obj_ptr.incrRefCount();
    }
    if (!ok) {
      exp.exprFreeByteCode(expr);
      return null;
    }
    return expr;
  },

  /* ==================== initStack ================================== */
  initStack: function(stack) {
    stack.len = 0;
    stack.vector = new Array();
  },

  /* ==================== freeStack ================================== */
  freeStack: function(stack) {
    stack.len = 0;
    stack.vector = null;
  },

  /* ==================== stackPeek ================================== */
  stackPeek: function(stack) {
    if (stack.len == 0) {
      return null;
    }
    return stack.vector[stack.len - 1];
  },

  /* ==================== stackPush ================================== */
  stackPush: function(stack, val) {
    stack.len++;
    stack.vector.push(val);
  },

  /* ==================== stackPop ================================== */
  stackPop: function(stack) {
    stack.len--;
    return stack.vector.pop();
  },

  /* ==================== setExprFromAny ================================== */
  /* This method takes the string representation of an expression
   * and generates a program for the Expr's stack-based VM.
   */
  setExprFromAny: function(obj_ptr) {
    var exp = this;
    var parser = new R.Parser();
    var expr;
    var tokenlist;
    var line;
    var file_name_obj;
    var script = new R.RaplScript(exp.interp);
    var rc = exp.ERROR;
    var debug_info = 0;

    script.text = script.interp.string_obj_type.getString(obj_ptr);
    script.text_len = script.interp.string_obj_type.getStringLength(obj_ptr);
    /* Try to get information about filename / line number */
    if (obj_ptr.obj_type == exp.interp.source_obj_type) {
      file_name_obj = obj_ptr.sourceValue.fileNameObj();
      line = obj_ptr.sourceValue.lineNumber();
    } else {
      file_name_obj = exp.interp.empty_obj;
      line = 1;
    }
    file_name_obj.incrRefCount();

    /* Initially tokenise the expression into tokenlist */
    script.scriptTokenListInit();
    parser.parserInit(script.text, script.text_len, line);
    while (!parser.parse_info.eof) {
      if (parser.parseExpression() != exp.OK) {
        script.scriptTokenListFree();
        exp.interp.setResultFormatted("syntax error in expression: \"%#s\"", obj_ptr);
        expr = null;
        /* Free the old internal rep and set the new one. */
        file_name_obj.decrRefCount();
        obj_ptr.freeIntRep();
        obj_ptr.setIntRepPtr(expr);
        obj_ptr.obj_type = exp.interp.expr_obj_type;
        return rc;
      }
      script.scriptAddToken(parser.parse_info.start, parser.parse_info.end - parser.parse_info.start + 1, parser.parse_info.token, parser.parse_info.line);
    }
    if (debug_info) {
      print("==== Expr Tokens ====");
      for (var i = 0; i < script.parse_token_list.length; i++) {
        parse_token = script.parse_token_list[i];
        print("["+i+"]@"+parse_token.line+" "+script.getTokenString(parse_token.token)+" '"+script.text.substring(parse_token.start, parse_token.start + parse_token.len)+"'");

      }
      print("==== Expr Tokens END ====");
    }
    /* Now create the expression bytecode from the tokenlist */
    expr = exp.exprCreateByteCode(script, file_name_obj);
    /* No longer need the token list */
    script.scriptTokenListFree(script.parse_token_list);
    if (!expr) {
      /* Free the old internal rep and set the new one. */
      file_name_obj.decrRefCount();
      obj_ptr.freeIntRep();
      obj_ptr.setIntRepPtr(expr);
      obj_ptr.obj_type = exp.interp.expr_obj_type;
      return rc;
    }
    if (debug_info) {
      print("==== Expr ====");
      for (var i = 0; i < expr.len; i++) {
        var t = expr.token_list[i];
        print("["+i+"]"+exp.getTokenString(t.token)+" '"+t.obj_ptr.getString()+"'");
      }
      print("==== Expr END ====");
    }
    /* Check program correctness. */
    if (exp.exprCheckCorrectness(expr) != exp.OK) {
        exp.exprFreeByteCode(expr);
        exp.interp.setResultFormatted("syntax error in expression: \"%#s\"", obj_ptr);
        expr = null;
        /* Free the old internal rep and set the new one. */
        file_name_obj.decrRefCount();
        obj_ptr.freeIntRep();
        obj_ptr.setIntRepPtr(expr);
        obj_ptr.obj_type = exp.interp.expr_obj_type;
        return rc;
    }
    rc = exp.OK;
    /* Free the old internal rep and set the new one. */
    file_name_obj.decrRefCount();
    obj_ptr.freeIntRep();
    obj_ptr.setIntRepPtr(expr);
    obj_ptr.obj_type = exp.interp.expr_obj_type;
    return rc;
  },

  /* ==================== getExpression ================================== */
  getExpression: function(obj_ptr) {
    var exp = this;

    if (obj_ptr.obj_type != exp.interp.expr_obj_type) {
      if (exp.setExprFromAny(obj_ptr) != exp.OK) {
        return null;
      }
    }
    return 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.
   * ---------------------------------------------------------------------------*/

  evalExpression: function(expr_obj_ptr, exprResultPtrPtr) {
    var exp = this;
    var expr;
    var staticStack = new Array();
    var i;
    var retcode = exp.OK;
    var fcn;
    var e = {
      stack: null,
      stacklen: 0,
      opcode: 0,
      skip: 0
    };

    expr = exp.getExpression(expr_obj_ptr);
    if (!expr) {
      return exp.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
     */
    {
      var 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[0].token == exp.TPOKEN_EXPR_INT) {
          exprResultPtrPtr[0] = expr.token_list[0].obj_ptr;
          exprResultPtrPtr[0].incrRefCount();
          return exp.OK;
        }
        if (expr.token_list[0].token == exp.TOKEN_VAR) {
          obj_ptr = exp.interp.variable_obj_type.getVariable(expr.token_list[0].obj_ptr, FUNCTION_FLAGS_LEAVE_ERR_MSG);
          if (obj_ptr) {
            exprResultPtrPtr[0] = obj_ptr;
            exprResultPtrPtr[0].incrRefCount();
            return exp.OK;
          }
        }
        break;
      case 2:
        if (expr.token_list[1].token == exp.TOKEN_EXPROP_NOT && expr.token_list[0].token == exp.TOKEN_VAR) {
          var wideValue;

          obj_ptr = exp.interp.variable_obj_type.getVariable(expr.token_list[0].obj_ptr, exp.FUNCTION_FLAGS_NONE);
          if (obj_ptr && exp.isWide(obj_ptr) && exp.getWide(obj_ptr, wideValue) == exp.OK) {
            exprResultPtrPtr[0] = wideValue ? exp.interp.false_obj : exp.interp.true_obj;
            exprResultPtrPtr[0].incrRefCount();
            return exp.OK;
          }
        }
        break;
      case 3:
        if (expr.token_list[0].token == exp.TOKEN_VAR && (expr.token_list[1].token == exp_TOKEN_EXPR_INT || expr.token_list[1].token == exp.TOKEN_VAR)) {
          switch (expr.token_list[2].token) {
          case exp.TOKEN_EXPROP_LT:
          case exp.TOKEN_EXPROP_LTE:
          case exp.TOKEN_EXPROP_GT:
          case exp.TOKEN_EXPROP_GTE:
          case exp.TOKEN_EXPROP_NUMEQ:
          case exp.TOKEN_EXPROP_NUMNE:
            /* optimise ok */
            var wideValueA;
            var wideValueB;

            obj_ptr = exp.interp.variable_obj_type.getVariable(expr.token_list[0].obj_ptr, exp_FUNCTION_FLAGS_NONE);
            if (obj_ptr && exp.isWide(obj_ptr) && exp.getWide(obj_ptr, wideValueA) == exp.OK) {
              if (expr.token_list[1].token == exp.TOKEN_VAR) {
                obj_ptr = exp.interp.variable_obj_type.getVariable(expr.token_list[1].obj_ptr, exp_FUNCTION_FLAGS_NONE);
              } else {
                obj_tr = expr.token_list[1].obj_ptr;
              }
              if (obj_ptr && exp.isWide(obj_ptr) && exp.getWide(obj_ptr, wideValueB) == exp.OK) {
                var cmpRes;

                switch (expr.token_list[2].token) {
                case exp.TOKEN_EXPROP_LT:
                  cmpRes = wideValueA < wideValueB;
                  break;
                case exp.TOKEN_EXPROP_LTE:
                  cmpRes = wideValueA <= wideValueB;
                  break;
                case exp.TOKEN_EXPROP_GT:
                  cmpRes = wideValueA > wideValueB;
                  break;
                case exp.TOKEN_EXPROP_GTE:
                  cmpRes = wideValueA >= wideValueB;
                  break;
                case exp.TOKEN_EXPROP_NUMEQ:
                  cmpRes = wideValueA == wideValueB;
                  break;
                case exp.TOKEN_EXPROP_NUMNE:
                  cmpRes = wideValueA != wideValueB;
                  break;
                default:   /*notreached */
                  cmpRes = 0;
                }
                exprResultPtrPtr[0] = cmpRes ? exp.interp.true_obj : exp.interp.false_obj;
                exprResultPtrPtr[0].incrRefCount();
                return exp.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.inUse++;
    /* 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. 
     * */
    e.stack = new Array();
    e.stacklen = 0;
    /* Execute every instruction */
    for (i = 0; i < expr.len && retcode == exp.OK; i++) {
      var obj_ptr;

//print("evalExpr!"+exp.getTokenString(expr.token_list[i].token)+"!");
      switch (expr.token_list[i].token) {
      case exp.TOKEN_EXPR_INT:
      case exp.TOKEN_EXPR_DOUBLE:
      case exp.TOKEN_STR:
        exp.exprPush(e, expr.token_list[i].obj_ptr);
        break;
      case exp.TOKEN_VAR:
        obj_ptr = exp.interp.variable_obj_type.getVariable(expr.token_list[i].obj_ptr, exp.FUNCTION_FLAGS_LEAVE_ERR_MSG);
        if (obj_ptr) {
          exp.exprPush(e, obj_ptr);
        } else {
          retcode = exp.ERROR;
        }
        break;
      case exp.TOKEN_VAR_ARRAY_NAME:
        obj_ptr = exp.expandDictSugar(expr.token_list[i].obj_ptr);
        if (obj_ptr) {
          exp.exprPush(e, obj_ptr);
        } else {
          retcode = exp.ERROR;
        }
        break;
      case exp.TOKEN_ESC:
        retcode = exp.substObj(expr.token_list[i].obj_ptr, obj_ptr, exp.FUNTION_FLAGS_NONE);
        if (retcode == expOK) {
          exp.exprPush(e, obj_ptr);
        }
        break;
      case exp.TOKEN_CMD:
        retcode = exp.interp.eval_statement.evalObj(expr.token_list[i].obj_ptr);
        if (retcode == exp.OK) {
          exp.exprPush(e, exp.interp.getResult());
        }
        break;
      default:
        /* Find and execute the operation */
        e.skip = 0;
        e.opcode = expr.token_list[i].token;
	fcn = exp.exprOperatorInfoByOpcode(e.opcode).funcop;
//print("fcn!"+fcn+"!");
        retcode = eval ("exp."+fcn+"(e)");
        /* Skip some opcodes if necessary */
        i += e.skip;
        continue;
      }
    }
    expr.inUse--;
    if (retcode == exp.OK) {
      exprResultPtrPtr[0] = exp.exprPop(e);
    } else {
      for (i = 0; i < e.stacklen; i++) {
        e.stack[i].decrRefCount();
      }
    }
    return retcode;
  },

  /* ==================== getBoolFromExpr ================================== */
  getBoolFromExpr: function(expr_obj_ptr, bool_ptr) {
    var exp = this;
    var retcode;
    var wide_value = new Array();
    var double_value = new Array();
    var expr_result_ptr = new Array();

    retcode = exp.evalExpression(expr_obj_ptr, expr_result_ptr);
    if (retcode != exp.OK) {
      return retcode;
    }
    expr_result_ptr = expr_result_ptr[0];

    if (exp.interp.int_obj_type.getWideNoErr(expr_result_ptr, wide_value) != exp.OK) {
      if (exp.getDouble(expr_result_ptr, double_value) != exp.OK) {
        expr_result_ptr.decrRefCount();
        return exp.ERROR;
      } else {
        double_value = double_value[0]
        expr_result_ptr.decrRefCount();
        boolPtr[0] = double_value != 0 ? 1 : 0;
        return exp.OK;
      }
    }
    wide_value = wide_value[0];
    bool_ptr[0] = wide_value != 0;

    expr_result_ptr.decrRefCount();
    return exp.OK;
  },

});

Expr.prototype.constructor = Expr;

R.Expr = Expr;

}, "0.0.1", {});