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