APWTCL Arnulf's Preferred Web Tcl

Artifact [9f27c11c40]
Login

Artifact [9f27c11c40]

Artifact 9f27c11c40979387a0ca48440db972defd4fbf1d:


/*=======================================================
 * ExprObjType.java 
 *
 * "A Tcl like language implementation in Java named APWTCL
 * ((Java) Arnulf's Preferred Web Tcl)"
 *
 * APWTCL ExprObjType 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.objtype;

import java.util.ArrayList;

import org.apwtcl.lang.ExprOperator;
import org.apwtcl.lang.ExprTokenStack;
import org.apwtcl.lang.ExprCompiled;
import org.apwtcl.lang.Interp;
import org.apwtcl.lang.Debug;
import org.apwtcl.lang.ApwtclObj;
import org.apwtcl.lang.Parse;
import org.apwtcl.lang.ParseToken;
import org.apwtcl.lang.Script;
import org.apwtcl.lang.ScriptToken;
import org.apwtcl.lang.Token;

public class ExprObjType extends Token implements Debug {
  private static int oid = 0;
  public static ExprOperator dummy_op = new ExprOperator(null, 0, 0, null, 0);

  private int id;
  private Interp interp;
  public boolean no_trace;

  /* ==================== ExprObjType ================================== */
  public ExprObjType(Interp interp) {
    oid++;
    id = oid;
    this.interp = interp;
    no_trace = false;
  }

  /* ==================== mySelf ================================== */
  public String mySelf() {
    String str = "ExprObjType!"+id+"!";
    return str;
  } 
      
  /* ==================== toString ===================================== */
  public String toString() {
    return mySelf()+"!";
  }

  /* ==================== toDebugString ===================================== */
  public String toDebugString() {
    StringBuffer str = new StringBuffer(mySelf()+"\n");
    return str.toString();
  }

  /* ==================== setFromAny ================================== */
  /* This method takes the string representation of an expression
   * and generates a program for the Expr's stack-based VM.
   */
  public int setFromAny(ApwtclObj obj_ptr) {
    Parse parser = new Parse(interp);
    ExprCompiled expr;
    int line;
    ApwtclObj file_name_obj;
    Script script = new Script(interp);
    int rc = ERROR;
    int debug_info = 1;

    script.text = interp.string_obj_type.getString(obj_ptr);
    script.text_len = interp.string_obj_type.getStringLength(obj_ptr);
    /* Try to get information about filename / line number */
    if (obj_ptr.obj_type == OBJ_TYPE_SOURCE) {
      file_name_obj = obj_ptr.sourceValue_GetFileNameObj();
      line = obj_ptr.sourceValue_GetLineNumber();
    } else {
      file_name_obj = interp.empty_string_obj;
      line = 1;
    }
    file_name_obj.incrRefCount("I_EXPR_OBJ_TYPE_1");

    /* Initially tokenise the expression into tokenlist */
    script.scriptTokenListInit();
    parser.parserInit(script.text, script.text_len, line);
    while (!parser.parse_info.eof) {
      if (parser.parseExpression() != OK) {
        script.scriptTokenListFree();
        interp.setResultFormatted("xxx1 syntax error in expression: \"%#s\"", obj_ptr);
        expr = null;
        /* Free the old internal rep and set the new one. */
        file_name_obj.decrRefCount("D_EXPR_OBJ_TYPE_1");
        obj_ptr.freeIntRep();
        obj_ptr.setIntRepPtr(expr);
        obj_ptr.obj_type = OBJ_TYPE_EXPR;
        return rc;
      }
      script.scriptAddParseToken(parser.parse_info.start, parser.parse_info.end - parser.parse_info.start + 1, parser.parse_info.token, parser.parse_info.line, script.text);
    }
    if (debug_info > 0) {
      print("==== Expr Tokens ====");
      for (int i = 0; i < script.parse_token_list.size(); i++) {
        ParseToken parse_token = script.parse_token_list.get(i);
	if (debug_info > 1) {
          print("["+i+"]@!"+parse_token.toDebugString()+"!");
	}else {
          print("["+i+"]@!"+parse_token.toString());
	}
//        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 = exprCreateByteCode(script, file_name_obj);
    /* No longer need the token list */
    script.scriptTokenListFree();
    if (expr == null) {
      /* Free the old internal rep and set the new one. */
      file_name_obj.decrRefCount("D_EXPR_OBJ_TYPE_2");
      obj_ptr.freeIntRep();
      obj_ptr.setIntRepPtr(expr);
      obj_ptr.obj_type = OBJ_TYPE_EXPR;
      return rc;
    }
    if (debug_info > 0) {
      print("==== Expr ====");
      for (int i = 0; i < expr.len; i++) {
        ScriptToken token_info = expr.token_list.get(i);
	if (debug_info > -1) {
          print("["+i+"]@!"+token_info.toDebugString()+"!");
	}else {
          print("["+i+"]@!"+token_info.toString());
	}
//        print("["+i+"]"+getTokenString(token_info.token)+" '"+token_info.obj_ptr.getString()+"'");
      }
      print("==== Expr END ====");
    }
    /* Check program correctness. */
    if (exprCheckCorrectness(expr) != OK) {
        exprFreeByteCode(expr);
print("YY");
        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("D_EXPR_OBJ_TYPE_3");
        obj_ptr.freeIntRep();
        obj_ptr.setIntRepPtr(expr);
        obj_ptr.obj_type = OBJ_TYPE_EXPR;
        return rc;
    }
    rc = OK;
    /* Free the old internal rep and set the new one. */
    file_name_obj.decrRefCount("D_EXPR_OBJ_TYPE_4");
    obj_ptr.freeIntRep();
    obj_ptr.setIntRepPtr(expr);
    obj_ptr.obj_type = OBJ_TYPE_EXPR;
    return rc;
  }

  /* ==================== freeInternalRep ===================================== */
  public void freeInternalRep(ApwtclObj obj_ptr) {
    print("expr freeInternalRep not yet implemented");
  }

  /* ==================== dupInternalRep ===================================== */
  public void dupInternalRep(ApwtclObj src_ptr, ApwtclObj dup_ptr) {
    print("expr dupInternalRep not yet implemented");
  }

  /* ==================== dumpExpr ===================================== */
  public void dumpExpr(ExprCompiled expr) {
    int debug_info = 1;

    print("==== Expr ====");
    for (int i = 0; i < expr.len; i++) {
      ScriptToken token_info = expr.token_list.get(i);
      if (debug_info > 0) {
        print("["+i+"]@!"+token_info.toDebugString()+"!");
      }else {
        print("["+i+"]@!"+token_info.toString());
      }
//      print("["+i+"]"+getTokenString(token_info.token)+" '"+token_info.obj_ptr.getString()+"'");
    }
    print("==== Expr END ====");
  }

  /* ==================== exprFreeByteCode ================================== */
  public void exprFreeByteCode(ExprCompiled expr) {
    ApwtclObj obj_ptr = interp.default_obj;
    int i;

    for (i = 0; i < expr.len; i++) {
      expr.token_list.get(i).obj_ptr.decrRefCount("D_EXPR_OBJ_TYPE_5");
    }
//    obj_ptr.free(expr.token_list);
//    obj_ptr.free(expr);
  }

  /* ==================== exprCheckCorrectness ================================== */
  /* Check if an expr program looks correct. */
  public int exprCheckCorrectness(ExprCompiled expr) {
    int i;
    int stacklen = 0;
    int 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++) {
      ScriptToken t = expr.token_list.get(i);
      ExprOperator op = exprOperatorInfoByOpcode(t.token);
//print("CC!"+getTokenString(t.token)+"!"+op.name+"!"+(op.opcode != null ? op.opcode : op.funcop)+"!");

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

  /* ==================== exprOperatorInfoByOpcode ================================== */
  public ExprOperator exprOperatorInfoByOpcode(int opcode) {
    if (opcode < TOKEN_EXPR_OP) {
        return dummy_op;
    }
    return exprOperators.get(opcode - TOKEN_EXPR_OP);
  }

  /* ==================== exprAddLazyOperator ================================== */
  /* 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.
   */
  public int exprAddLazyOperator(ExprCompiled expr, ParseToken t) {
    ApwtclObj obj_ptr = interp.default_obj;
    int i;
    int leftindex;
    int arity;
    int offset;

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

    arity = 1;
    while (arity != 0) {
      ScriptToken tt = expr.token_list.get(leftindex);

      if (tt.token >= TOKEN_EXPR_OP) {
        arity += exprOperatorInfoByOpcode(tt.token).arity;
      }
      arity--;
      if (--leftindex < 0) {
        return ERROR;
      }
    }
    leftindex++;
    /* Move them up */
    int k = leftindex + 2 + (expr.len - leftindex - 1);
    for (int j = k - 2; j >= expr.len - leftindex - 1; j--) {
      expr.token_list.set(k, new ScriptToken(interp));
      expr.token_list.get(k).token = expr.token_list.get(j).token;
      expr.token_list.get(k).obj_ptr = expr.token_list.get(j).obj_ptr;
      k--;
    }
    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.set(leftindex + 1, new ScriptToken(interp));
    expr.token_list.get(leftindex + 1).token = t.token + 1;
    expr.token_list.get(leftindex + 1).obj_ptr = interp.empty_string_obj;

    expr.token_list.set(leftindex, new ScriptToken(interp));
    expr.token_list.get(leftindex).token = TOKEN_EXPR_INT;
    expr.token_list.get(leftindex).obj_ptr = interp.int_obj_type.newIntObj(offset);

    /* Now add the 'R' operator */
    expr.token_list.set(expr.len, new ScriptToken(interp));
    expr.token_list.get(expr.len).token = t.token + 2;
    expr.token_list.get(expr.len).obj_ptr = interp.empty_string_obj;
    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--) {
      ExprOperator op = exprOperatorInfoByOpcode(expr.token_list.get(i).token);

      if (op.lazy == EXPR_TYPE_LAZY_LEFT) {
        if (expr.token_list.get(i - 1).obj_ptr.wideValue_GetValue() + i - 1 >= leftindex) {
          expr.token_list.get(i - 1).obj_ptr.wideValue_SetValue(expr.token_list.get(i - 1).obj_ptr.wideValue_GetValue() + 2);
        }
      }
    }
    return OK;
  }

  /* ==================== exprAddOperator ================================== */
  public int exprAddOperator(ExprCompiled expr, ParseToken t) {
    ScriptToken my_token = new ScriptToken(interp);
    ExprOperator op = exprOperatorInfoByOpcode(t.token);

    if (op.lazy == EXPR_TYPE_LAZY_OP) {
      if (exprAddLazyOperator(expr, t) != OK) {
        interp.setResultString("Expression has bad operands to "+op.name);
        return ERROR;
      }
    } else {
      my_token.obj_ptr = interp.empty_string_obj;
      my_token.token = t.token;
      expr.token_list.add(my_token);
      expr.len++;
    }
    return OK;
  }

  /* ==================== exprCreateByteCode ================================== */
  public ExprCompiled exprCreateByteCode(Script script, ApwtclObj file_name_obj) {
    ArrayList<ParseToken> token_list = script.parse_token_list;
    ApwtclObj obj_ptr = interp.default_obj;
    ExprTokenStack stack = new ExprTokenStack(interp);
    ExprCompiled expr = new ExprCompiled(interp);
    boolean ok = true;
    int i;
    int prev_token = TOKEN_NONE;
    boolean have_ternary = false;
    /* -1 for EOL */
    int count = token_list.size() - 1;
for (count = 0; count < token_list.size(); count++) {
print("count!"+count+"!"+token_list.get(count).toDebugString()+"!");
}
    initStack(stack);
    /* Need extra bytecodes for lazy operators.
     * Also check for the ternary operator
     */
    for (i = 0; i < token_list.size() - 1; i++) {
      ParseToken t = token_list.get(i);
      ExprOperator op = exprOperatorInfoByOpcode(t.token);
print("TL!"+t.toDebugString()+"!"+op.toDebugString()+"!");
      if (op.lazy == EXPR_TYPE_LAZY_OP) {
        count += 2;
        /* Ternary is a lazy op but also needs reordering */
        if (t.token == TOKEN_EXPROP_TERNARY) {
          have_ternary = true;
        }
      }
    }
    expr.token_list = new ArrayList<ScriptToken>();
    for (i = 0; i < token_list.size() && ok; i++) {
      ParseToken t = token_list.get(i);
      ScriptToken token = new ScriptToken(interp);
print("TL2!"+t.toDebugString()+"!");

      if (t.token == TOKEN_EOL) {
        break;
      }

      switch (t.token) {
      case TOKEN_QUOTE:
      case TOKEN_STR:
      case TOKEN_QUOTE_ESC:
      case TOKEN_ESC:
      case TOKEN_VAR:
      case TOKEN_BRACE:
      case TOKEN_VAR_ARRAY_NAME:
      case TOKEN_EXPRSUGAR:
      case TOKEN_CMD:
        token.obj_ptr = interp.string_obj_type.newStringObj(script.getText(t.start, t.len), t.len, "EXPR_OBJ_TYPE_1");
        token.token = t.token;
        if (t.token == TOKEN_CMD) {
            /* Only commands need source info */
            interp.source_obj_type.setSourceInfo(token.obj_ptr, file_name_obj, t.line);
        }
        expr.token_list.add(token);
        expr.len++;
        break;
      case TOKEN_EXPR_INT:
        token.obj_ptr = interp.int_obj_type.newIntObj(Integer.parseInt(script.getText(t.start, t.len)));
        token.token = t.token;
        expr.token_list.add(token);
        expr.len++;
        break;
      case TOKEN_EXPR_DOUBLE:
        token.obj_ptr = interp.double_obj_type.newDoubleObj(Double.parseDouble(script.getText(t.start, t.len)));
        token.token = t.token;
        expr.token_list.add(token);
        expr.len++;
        break;
      case TOKEN_SUBEXPR_START:
        stackPush(stack, t);
        prev_token = TOKEN_NONE;
        continue;
      case TOKEN_SUBEXPR_COMMA:
        /* Simple approach. Comma is simply ignored */
        continue;
      case TOKEN_SUBEXPR_END:
        ok = false;
        while (stack.len != 0) {
          ParseToken tt = stackPop(stack);

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

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

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

      if (tt.token == TOKEN_SUBEXPR_START) {
        ok = false;
        interp.setResultString("Missing close parenthesis");
        /* Free the stack used for the compilation. */
        freeStack(stack);
        for (i = 0; i < expr.len; i++) {
          if (expr.token_list.get(i).obj_ptr != null) {
            expr.token_list.get(i).obj_ptr.incrRefCount("I_EXPR_OBJ_TYPE_5");
          }
        }
        if (!ok) {
          exprFreeByteCode(expr);
          return null;
        }
        return expr;
      }
      if (exprAddOperator(expr, tt) != OK) {
        ok = false;
      }
    }
    if (!ok && have_ternary) {
// FIXME!!
print("eval_expression.exprTernaryReorderExpression not yet implemented!!");
//      interp.eval_expression.exprTernaryReorderExpression(expr);
    }
    /* Free the stack used for the compilation. */
    freeStack(stack);
print("len!"+expr.len+"!");
    for (i = 0; i < expr.len; i++) {
      if (expr.token_list.get(i).obj_ptr != null) {
        expr.token_list.get(i).obj_ptr.incrRefCount("I_EXPR_OBJ_TYPE_6");
      }
    }
    if (!ok) {
      exprFreeByteCode(expr);
      return null;
    }
    return expr;
  }

  /* ==================== expandExprSugar ================================== */
  public ApwtclObj expandExprSugar(ApwtclObj obj_ptr) {
    ArrayList<ApwtclObj> result_obj_ptr_ptr = new ArrayList<ApwtclObj>();
    ApwtclObj result_obj_ptr;

    if (interp.eval_expression.evalExpression(obj_ptr, result_obj_ptr_ptr) == OK) {
      /* Note that the result has a ref count of 1, but we need a ref count of 0 */
      result_obj_ptr = result_obj_ptr_ptr.get(0);
      result_obj_ptr.ref_count--;
      return result_obj_ptr;
    }
    return null;
  }

  /* ==================== initStack ================================== */
  public void initStack(ExprTokenStack stack) {
    stack.len = 0;
    stack.token_list = new ArrayList<ParseToken>();
  }

  /* ==================== freeStack ================================== */
  public void freeStack(ExprTokenStack stack) {
    stack.len = 0;
    stack.token_list = null;
  }

  /* ==================== stackPeek ================================== */
  public ParseToken stackPeek(ExprTokenStack stack) {
    if (stack.len == 0) {
      return null;
    }
    return stack.token_list.get(stack.len - 1);
  }

  /* ==================== stackPush ================================== */
  public void stackPush(ExprTokenStack stack, ParseToken val) {
    stack.len++;
    stack.token_list.add(val);
  }

  /* ==================== stackPop ================================== */
  public ParseToken stackPop(ExprTokenStack stack) {
    stack.len--;
    ParseToken elem = stack.token_list.get(0);
    stack.token_list.remove(0);
    return elem;
  }
}