public int size() { return size; }
public void set(int pos, int op_, Object arg_) { op[pos] = op_; arg[pos] = arg_; }
+ public void paste(ForthBlock other) { for(int i=0; i<other.size; i++) add(other.op[i], other.arg[i]); }
public ForthBlock add(int op_) { return add(op_, null); }
public ForthBlock add(int op_, Object arg_) {
- if (size == op.length - 1) {
- int[] op2 = new int[op.length * 2]; System.arraycopy(op, 0, op2, 0, op.length); op = op2;
- Object[] arg2 = new Object[op.length * 2]; System.arraycopy(arg, 0, arg2, 0, arg.length); arg = arg2;
- }
- op[size] = op_;
- arg[size] = arg_;
- size++;
- return this;
+ if (size == op.length - 1) {
+ int[] op2 = new int[op.length * 2]; System.arraycopy(op, 0, op2, 0, op.length); op = op2;
+ Object[] arg2 = new Object[op.length * 2]; System.arraycopy(arg, 0, arg2, 0, arg.length); arg = arg2;
+ }
+ op[size] = op_;
+ arg[size] = arg_;
+ size++;
+ return this;
}
-
+
public Object eval(final JS.Scope s) throws ControlTransferException, JS.Exn { return eval(s, new Stack()); }
public Object eval(final JS.Scope s, Stack t) throws ControlTransferException {
- for(int i=0; i<size; i++)
- switch(op[i]) {
- case LABEL: break; // FIXME
- case LITERAL: t.push(arg[i]); break;
- case OBJECT: t.push(new JS.Obj()); break;
- case ARRAY: t.push(new JS.Array(JS.toNumber(arg[i]).intValue())); break;
- case DECLARE: s.declare((String)t.pop()); break;
- case THIS: t.push(s); break; // FIXME: transparents
- case JT: if (JS.toBoolean(t.pop())) i += JS.toNumber(arg[i]).intValue() - 1; break;
- case JF: if (!JS.toBoolean(t.pop())) i += JS.toNumber(arg[i]).intValue() - 1; break;
- case JMP: i += JS.toNumber(arg[i]).intValue() - 1; break;
- case POP: t.pop(); break;
- case SWAP: t.swap(); break;
- case DUP: t.push(t.peek()); break;
- case NOP: break;
- case EXPR: t.push(((ForthBlock)arg[i]).eval(s)); break;
- case SCOPE: t.push(((ForthBlock)arg[i]).eval(new JS.Scope(s))); break;
+ for(int i=0; i<size; i++)
+ switch(op[i]) {
+ case LABEL: break; // FIXME
+ case LITERAL: t.push(arg[i]); break;
+ case OBJECT: t.push(new JS.Obj()); break;
+ case ARRAY: t.push(new JS.Array(JS.toNumber(arg[i]).intValue())); break;
+ case DECLARE: s.declare((String)t.pop()); break;
+ case THIS: t.push(s); break; // FIXME: transparents
+ case JT: if (JS.toBoolean(t.pop())) i += JS.toNumber(arg[i]).intValue() - 1; break;
+ case JF: if (!JS.toBoolean(t.pop())) i += JS.toNumber(arg[i]).intValue() - 1; break;
+ case JMP: i += JS.toNumber(arg[i]).intValue() - 1; break;
+ case POP: t.pop(); break;
+ case SWAP: t.swap(); break;
+ case DUP: t.push(t.peek()); break;
+ case NOP: break;
+ case EXPR: t.push(((ForthBlock)arg[i]).eval(s)); break;
+ case SCOPE: t.push(((ForthBlock)arg[i]).eval(new JS.Scope(s))); break;
- case ASSERT: if (!JS.toBoolean(t.pop())) throw new EvaluatorException(line, sourceName, "assertion failed"); break;
- case RETURN: throw new ReturnException(t.pop());
- case THROW: throw new JS.Exn(t.pop());
+ case ASSERT: if (!JS.toBoolean(t.pop())) throw new EvaluatorException(line, sourceName, "assertion failed"); break;
+ case RETURN: throw new ReturnException(t.pop());
+ case THROW: throw new JS.Exn(t.pop());
- case TRY: break;
- case INSTANCEOF: break;
- case TYPEOF: break;
- case PUSHKEYS: {
- Object o = t.peek();
- Object[] keys = ((JS)o).keys();
- JS.Array a = new JS.Array();
- a.setSize(keys.length);
- for(int j=0; j<keys.length; j++) a.setElementAt(keys[j], j);
- t.push(a);
- break;
- }
+ case TRY: break;
+ case INSTANCEOF: break;
+ case TYPEOF: break;
+ case PUSHKEYS: {
+ Object o = t.peek();
+ Object[] keys = ((JS)o).keys();
+ JS.Array a = new JS.Array();
+ a.setSize(keys.length);
+ for(int j=0; j<keys.length; j++) a.setElementAt(keys[j], j);
+ t.push(a);
+ break;
+ }
- case Lexer.BITNOT: t.push(new Long(~JS.toLong(t.pop()))); break;
- case Lexer.BANG: t.push(new Boolean(!JS.toBoolean(t.pop()))); break;
-
- case Lexer.BREAK: {
- // FIXME: make sure this can only appear in proper places
- return Boolean.FALSE;
- }
- case Lexer.CONTINUE: {
- // FIXME: make sure this can only appear in proper places
- return Boolean.TRUE;
- }
- case LOOP: {
- ForthBlock loop = (ForthBlock)arg[i];
- Stack t2 = new Stack();
- t2.push(Boolean.TRUE);
- while (true) {
- Boolean result;
- try {
- result = (Boolean)loop.eval(new JS.Scope(s), t2);
- } catch (ContinueException c) {
- result = Boolean.TRUE;
- } catch (BreakException b) {
- result = Boolean.FALSE;
- }
- if (result == Boolean.FALSE) break;
- t2 = new Stack();
- t2.push(Boolean.FALSE);
- }
- break;
- }
+ case Lexer.BITNOT: t.push(new Long(~JS.toLong(t.pop()))); break;
+ case Lexer.BANG: t.push(new Boolean(!JS.toBoolean(t.pop()))); break;
+
+ case Lexer.BREAK: {
+ // FIXME: make sure this can only appear in proper places
+ return Boolean.FALSE;
+ }
+ case Lexer.CONTINUE: {
+ // FIXME: make sure this can only appear in proper places
+ return Boolean.TRUE;
+ }
+ case LOOP: {
+ ForthBlock loop = (ForthBlock)arg[i];
+ Stack t2 = new Stack();
+ t2.push(Boolean.TRUE);
+ while (true) {
+ Boolean result;
+ try {
+ result = (Boolean)loop.eval(new JS.Scope(s), t2);
+ } catch (ContinueException c) {
+ result = Boolean.TRUE;
+ } catch (BreakException b) {
+ result = Boolean.FALSE;
+ }
+ if (result == Boolean.FALSE) break;
+ t2 = new Stack();
+ t2.push(Boolean.FALSE);
+ }
+ break;
+ }
- case PUT: {
- Object val = t.pop();
- Object key = t.pop();
- JS target = (JS)t.peek();
- if (target == null) throw new JS.Exn("tried to put a value to the " + key + " property on the null value");
- target.put(key, val);
- t.push(val);
- break;
- }
+ case PUT: {
+ Object val = t.pop();
+ Object key = t.pop();
+ JS target = (JS)t.peek();
+ if (target == null) throw new JS.Exn("tried to put a value to the " + key + " property on the null value");
+ target.put(key, val);
+ t.push(val);
+ break;
+ }
- case GET: {
- Object v = t.pop();
- Object o = t.pop();
- t.push(doGet(o, v));
- break;
- }
+ case GET: {
+ Object v = t.pop();
+ Object o = t.pop();
+ t.push(doGet(o, v));
+ break;
+ }
- case GET_PRESERVE: {
- Object v = t.pop();
- Object o = t.peek();
- t.push(v);
- t.push(doGet(o, v));
- break;
- }
-
- case CALL: {
- JS.Array arguments = new JS.Array();
- int numArgs = JS.toNumber(arg[i]).intValue();
- arguments.setSize(numArgs);
- for(int j=numArgs - 1; j >= 0; j--) arguments.setElementAt(t.pop(), j);
- JS.Function f = (JS.Function)t.pop();
- if (f == null) throw new JS.Exn(new EvaluatorException(line, sourceName, "attempted to call null"));
- t.push(f.call(arguments));
- break;
- }
+ case GET_PRESERVE: {
+ Object v = t.pop();
+ Object o = t.peek();
+ t.push(v);
+ t.push(doGet(o, v));
+ break;
+ }
+
+ case CALL: {
+ JS.Array arguments = new JS.Array();
+ int numArgs = JS.toNumber(arg[i]).intValue();
+ arguments.setSize(numArgs);
+ for(int j=numArgs - 1; j >= 0; j--) arguments.setElementAt(t.pop(), j);
+ JS.Function f = (JS.Function)t.pop();
+ if (f == null) throw new JS.Exn(new EvaluatorException(line, sourceName, "attempted to call null"));
+ t.push(f.call(arguments));
+ break;
+ }
- case OpCodes.FUNCTION: {
- final ForthBlock myBytes = (ForthBlock)arg[i];
- t.push(new JS.Function() {
- public String toString() { return sourceName + ":" + line; }
- public String getSourceName() throws JS.Exn { return sourceName; }
- public int getLine() throws JS.Exn { return line; }
- public Object _call(final JS.Array args) throws JS.Exn {
- Function save = JS.getCurrentFunction();
- JS.currentFunction.put(java.lang.Thread.currentThread(), this);
- JS.Scope scope = new JS.Scope(s) {
- // FIXME
- public String getSourceName() { return sourceName; }
- public Object get(Object key) throws JS.Exn {
- if (key.equals("trapee")) return org.xwt.Trap.currentTrapee();
- else if (key.equals("cascade")) return org.xwt.Trap.cascadeFunction;
- return super.get(key);
- }
- };
- Stack t0 = new Stack();
- t0.push(args);
- try {
- return myBytes.eval(scope, t0);
- } catch (ReturnException r) {
- return r.retval;
- } catch (ControlTransferException c) {
- throw new EvaluatorException(line, sourceName, "error, ControlTransferException tried to leave a function: " + c);
- } finally {
- if (save == null) JS.currentFunction.remove(java.lang.Thread.currentThread());
- else JS.currentFunction.put(java.lang.Thread.currentThread(), save);
- }
- }
- });
- break;
- }
+ case OpCodes.FUNCTION: {
+ final ForthBlock myBytes = (ForthBlock)arg[i];
+ t.push(new JS.Function() {
+ public String toString() { return sourceName + ":" + line; }
+ public String getSourceName() throws JS.Exn { return sourceName; }
+ public int getLine() throws JS.Exn { return line; }
+ public Object _call(final JS.Array args) throws JS.Exn {
+ Function save = JS.getCurrentFunction();
+ JS.currentFunction.put(java.lang.Thread.currentThread(), this);
+ JS.Scope scope = new JS.Scope(s) {
+ // FIXME
+ public String getSourceName() { return sourceName; }
+ public Object get(Object key) throws JS.Exn {
+ if (key.equals("trapee")) return org.xwt.Trap.currentTrapee();
+ else if (key.equals("cascade")) return org.xwt.Trap.cascadeFunction;
+ return super.get(key);
+ }
+ };
+ Stack t0 = new Stack();
+ t0.push(args);
+ try {
+ return myBytes.eval(scope, t0);
+ } catch (ReturnException r) {
+ return r.retval;
+ } catch (ControlTransferException c) {
+ throw new EvaluatorException(line, sourceName, "error, ControlTransferException tried to leave a function: " + c);
+ } finally {
+ if (save == null) JS.currentFunction.remove(java.lang.Thread.currentThread());
+ else JS.currentFunction.put(java.lang.Thread.currentThread(), save);
+ }
+ }
+ });
+ break;
+ }
- case Lexer.INC: case Lexer.DEC: {
- boolean isPrefix = JS.toBoolean(arg[i]);
- Object key = t.pop();
- JS obj = (JS)t.pop();
- Number num = JS.toNumber(obj.get(key));
- Number val = new Double(op[i] == Lexer.INC ? num.doubleValue() + 1.0 : num.doubleValue() - 1.0);
- obj.put(key, val);
- t.push(isPrefix ? val : num);
- break;
- }
+ case Lexer.INC: case Lexer.DEC: {
+ boolean isPrefix = JS.toBoolean(arg[i]);
+ Object key = t.pop();
+ JS obj = (JS)t.pop();
+ Number num = JS.toNumber(obj.get(key));
+ Number val = new Double(op[i] == Lexer.INC ? num.doubleValue() + 1.0 : num.doubleValue() - 1.0);
+ obj.put(key, val);
+ t.push(isPrefix ? val : num);
+ break;
+ }
- default: {
- Object right = t.pop();
- Object left = t.pop();
- switch(op[i]) {
+ default: {
+ Object right = t.pop();
+ Object left = t.pop();
+ switch(op[i]) {
- case Lexer.BITOR: t.push(new Long(JS.toLong(left) | JS.toLong(right))); break;
- case Lexer.BITXOR: t.push(new Long(JS.toLong(left) ^ JS.toLong(right))); break;
- case Lexer.BITAND: t.push(new Long(JS.toLong(left) & JS.toLong(right))); break;
+ case Lexer.BITOR: t.push(new Long(JS.toLong(left) | JS.toLong(right))); break;
+ case Lexer.BITXOR: t.push(new Long(JS.toLong(left) ^ JS.toLong(right))); break;
+ case Lexer.BITAND: t.push(new Long(JS.toLong(left) & JS.toLong(right))); break;
- case Lexer.ADD: {
- Object l = left;
- Object r = right;
- if (l instanceof String || r instanceof String) {
- if (l == null) l = "null";
- if (r == null) r = "null";
- if (l instanceof Number && ((Number)l).doubleValue() == ((Number)l).longValue())
- l = new Long(((Number)l).longValue());
- if (r instanceof Number && ((Number)r).doubleValue() == ((Number)r).longValue())
- r = new Long(((Number)r).longValue());
- t.push(l.toString() + r.toString()); break;
- }
- t.push(new Double(JS.toDouble(l) + JS.toDouble(r))); break;
- }
-
- case Lexer.SUB: t.push(new Double(JS.toDouble(left) - JS.toDouble(right))); break;
- case Lexer.MUL: t.push(new Double(JS.toDouble(left) * JS.toDouble(right))); break;
- case Lexer.DIV: t.push(new Double(JS.toDouble(left) / JS.toDouble(right))); break;
- case Lexer.MOD: t.push(new Double(JS.toDouble(left) % JS.toDouble(right))); break;
-
- case Lexer.LSH: t.push(new Long(JS.toLong(left) << JS.toLong(right))); break;
- case Lexer.RSH: t.push(new Long(JS.toLong(left) >> JS.toLong(right))); break;
- case Lexer.URSH: t.push(new Long(JS.toLong(left) >>> JS.toLong(right))); break;
-
- // FIXME: these need to work on strings
- case Lexer.LT: t.push(JS.toDouble(left) < JS.toDouble(right) ? Boolean.TRUE : Boolean.FALSE); break;
- case Lexer.LE: t.push(JS.toDouble(left) <= JS.toDouble(right) ? Boolean.TRUE : Boolean.FALSE); break;
- case Lexer.GT: t.push(JS.toDouble(left) > JS.toDouble(right) ? Boolean.TRUE : Boolean.FALSE); break;
- case Lexer.GE: t.push(JS.toDouble(left) >= JS.toDouble(right) ? Boolean.TRUE : Boolean.FALSE); break;
-
- case Lexer.EQ:
- case Lexer.NE: {
- // FIXME: should use Javascript coercion-equality rules
- Object l = left;
- Object r = right;
- boolean ret;
- if (l == null) { Object tmp = r; r = l; l = tmp; }
- if (l == null && r == null) ret = true;
- else if (l instanceof Boolean) ret = new Boolean(JS.toBoolean(r)).equals(l);
- else if (l instanceof Number) ret = JS.toNumber(r).doubleValue() == JS.toNumber(l).doubleValue();
- else if (l instanceof String) ret = r != null && l.equals(r.toString());
- else ret = l.equals(r);
- t.push(new Boolean(op[i] == Lexer.EQ ? ret : !ret)); break;
- }
+ case Lexer.ADD: {
+ Object l = left;
+ Object r = right;
+ if (l instanceof String || r instanceof String) {
+ if (l == null) l = "null";
+ if (r == null) r = "null";
+ if (l instanceof Number && ((Number)l).doubleValue() == ((Number)l).longValue())
+ l = new Long(((Number)l).longValue());
+ if (r instanceof Number && ((Number)r).doubleValue() == ((Number)r).longValue())
+ r = new Long(((Number)r).longValue());
+ t.push(l.toString() + r.toString()); break;
+ }
+ t.push(new Double(JS.toDouble(l) + JS.toDouble(r))); break;
+ }
+
+ case Lexer.SUB: t.push(new Double(JS.toDouble(left) - JS.toDouble(right))); break;
+ case Lexer.MUL: t.push(new Double(JS.toDouble(left) * JS.toDouble(right))); break;
+ case Lexer.DIV: t.push(new Double(JS.toDouble(left) / JS.toDouble(right))); break;
+ case Lexer.MOD: t.push(new Double(JS.toDouble(left) % JS.toDouble(right))); break;
+
+ case Lexer.LSH: t.push(new Long(JS.toLong(left) << JS.toLong(right))); break;
+ case Lexer.RSH: t.push(new Long(JS.toLong(left) >> JS.toLong(right))); break;
+ case Lexer.URSH: t.push(new Long(JS.toLong(left) >>> JS.toLong(right))); break;
+
+ // FIXME: these need to work on strings
+ case Lexer.LT: t.push(JS.toDouble(left) < JS.toDouble(right) ? Boolean.TRUE : Boolean.FALSE); break;
+ case Lexer.LE: t.push(JS.toDouble(left) <= JS.toDouble(right) ? Boolean.TRUE : Boolean.FALSE); break;
+ case Lexer.GT: t.push(JS.toDouble(left) > JS.toDouble(right) ? Boolean.TRUE : Boolean.FALSE); break;
+ case Lexer.GE: t.push(JS.toDouble(left) >= JS.toDouble(right) ? Boolean.TRUE : Boolean.FALSE); break;
+
+ case Lexer.EQ:
+ case Lexer.NE: {
+ // FIXME: should use Javascript coercion-equality rules
+ Object l = left;
+ Object r = right;
+ boolean ret;
+ if (l == null) { Object tmp = r; r = l; l = tmp; }
+ if (l == null && r == null) ret = true;
+ else if (l instanceof Boolean) ret = new Boolean(JS.toBoolean(r)).equals(l);
+ else if (l instanceof Number) ret = JS.toNumber(r).doubleValue() == JS.toNumber(l).doubleValue();
+ else if (l instanceof String) ret = r != null && l.equals(r.toString());
+ else ret = l.equals(r);
+ t.push(new Boolean(op[i] == Lexer.EQ ? ret : !ret)); break;
+ }
- default: throw new Error("unknown opcode " + op[i]);
- } }
- }
- if (t.size() != 1) {
- for(int i=0; i<size; i++) {
- System.out.println((op[i] >= 0 ? codeToString[op[i]] : "" + op[i]) + " [" + arg[i] + "]");
- }
- throw new EvaluatorException(line, sourceName, "eval() terminated with " + t.size() + " elements on the stack; one expected");
- }
- return t.pop();
+ default: throw new Error("unknown opcode " + op[i]);
+ } }
+ }
+ if (t.size() != 1) {
+ for(int i=0; i<size; i++) {
+ System.out.println((op[i] >= 0 ? codeToString[op[i]] : "" + op[i]) + " [" + arg[i] + "]");
+ }
+ throw new EvaluatorException(line, sourceName, "eval() terminated with " + t.size() + " elements on the stack; one expected");
+ }
+ return t.pop();
}
public Object doGet(final Object o, final Object v) {
- if (o == null) {
- throw new EvaluatorException(line, sourceName, "tried to get property \"" + v + "\" from the null value");
- }
- if (o instanceof String) {
- if (v.equals("length")) return new Integer(((String)o).length());
- else if (v.equals("substring")) return new JS.Function() {
- public Object _call(JS.Array args) {
- if (args.length() == 1) return ((String)o).substring(JS.toNumber(args.elementAt(0)).intValue());
- else if (args.length() == 2) return ((String)o).substring(JS.toNumber(args.elementAt(0)).intValue(),
- JS.toNumber(args.elementAt(1)).intValue());
- else throw new Error("String.substring() can only take one or two arguments");
- }
- };
- else if (v.equals("toLowerCase")) return new JS.Function() {
- public Object _call(JS.Array args) {
- return ((String)o).toLowerCase();
- } };
- else if (v.equals("toUpperCase")) return new JS.Function() {
- public Object _call(JS.Array args) {
- return ((String)o).toString().toUpperCase();
- } };
- else if (v.equals("charAt")) return new JS.Function() {
- public Object _call(JS.Array args) {
- return ((String)o).charAt(JS.toNumber(args.elementAt(0)).intValue()) + "";
- } };
- else if (v.equals("lastIndexOf")) return new JS.Function() {
- public Object _call(JS.Array args) {
- if (args.length() != 1) return null;
- return new Integer(((String)o).lastIndexOf(args.elementAt(0).toString()));
- } };
- else if (v.equals("indexOf")) return new JS.Function() {
- public Object _call(JS.Array args) {
- if (args.length() != 1) return null;
- return new Integer(((String)o).indexOf(args.elementAt(0).toString()));
- } };
- throw new Error("Not Implemented: propery " + v + " on String objects");
- } else if (o instanceof Boolean) {
- throw new Error("Not Implemented: properties on Boolean objects");
- } else if (o instanceof Number) {
- Log.log(this, "Not Implemented: properties on Number objects");
- return null;
- //throw new Error("Not Implemented: properties on Number objects");
- } else if (o instanceof JS) {
- return ((JS)o).get(v);
- }
- return null;
+ if (o == null) {
+ throw new EvaluatorException(line, sourceName, "tried to get property \"" + v + "\" from the null value");
+ }
+ if (o instanceof String) {
+ if (v.equals("length")) return new Integer(((String)o).length());
+ else if (v.equals("substring")) return new JS.Function() {
+ public Object _call(JS.Array args) {
+ if (args.length() == 1) return ((String)o).substring(JS.toNumber(args.elementAt(0)).intValue());
+ else if (args.length() == 2) return ((String)o).substring(JS.toNumber(args.elementAt(0)).intValue(),
+ JS.toNumber(args.elementAt(1)).intValue());
+ else throw new Error("String.substring() can only take one or two arguments");
+ }
+ };
+ else if (v.equals("toLowerCase")) return new JS.Function() {
+ public Object _call(JS.Array args) {
+ return ((String)o).toLowerCase();
+ } };
+ else if (v.equals("toUpperCase")) return new JS.Function() {
+ public Object _call(JS.Array args) {
+ return ((String)o).toString().toUpperCase();
+ } };
+ else if (v.equals("charAt")) return new JS.Function() {
+ public Object _call(JS.Array args) {
+ return ((String)o).charAt(JS.toNumber(args.elementAt(0)).intValue()) + "";
+ } };
+ else if (v.equals("lastIndexOf")) return new JS.Function() {
+ public Object _call(JS.Array args) {
+ if (args.length() != 1) return null;
+ return new Integer(((String)o).lastIndexOf(args.elementAt(0).toString()));
+ } };
+ else if (v.equals("indexOf")) return new JS.Function() {
+ public Object _call(JS.Array args) {
+ if (args.length() != 1) return null;
+ return new Integer(((String)o).indexOf(args.elementAt(0).toString()));
+ } };
+ throw new Error("Not Implemented: propery " + v + " on String objects");
+ } else if (o instanceof Boolean) {
+ throw new Error("Not Implemented: properties on Boolean objects");
+ } else if (o instanceof Number) {
+ Log.log(this, "Not Implemented: properties on Number objects");
+ return null;
+ //throw new Error("Not Implemented: properties on Number objects");
+ } else if (o instanceof JS) {
+ return ((JS)o).get(v);
+ }
+ return null;
}
static class Stack {
- public Object[] os = new Object[256];
- private int size = 0;
- public void push(Object o) { os[size++] = o; }
- public Object pop() { return os[--size]; }
- public Object peek() { return os[size - 1]; }
- public void swap() { Object temp = os[size - 1]; os[size - 1] = os[size - 2]; os[size - 2] = temp; }
- public int size() { return size; }
+ public Object[] os = new Object[256];
+ private int size = 0;
+ public void push(Object o) { os[size++] = o; }
+ public Object pop() { return os[--size]; }
+ public Object peek() { return os[size - 1]; }
+ public void swap() { Object temp = os[size - 1]; os[size - 1] = os[size - 2]; os[size - 2] = temp; }
+ public int size() { return size; }
}
static class EvaluatorException extends RuntimeException {
- public EvaluatorException(int line, String sourceName, String s) { super(sourceName + ":" + line + " " + s); }
+ public EvaluatorException(int line, String sourceName, String s) { super(sourceName + ":" + line + " " + s); }
}
static abstract class ControlTransferException extends Exception { }
static class BreakException extends ControlTransferException {
- public String label;
- BreakException(String label) { this.label = label; }
+ public String label;
+ BreakException(String label) { this.label = label; }
}
static class ContinueException extends ControlTransferException {
- public String label;
- ContinueException(String label) { this.label = label; }
+ public String label;
+ ContinueException(String label) { this.label = label; }
}
static class ReturnException extends ControlTransferException {
- public Object retval;
- ReturnException(Object retval) { this.retval = retval; }
+ public Object retval;
+ ReturnException(Object retval) { this.retval = retval; }
}
}
/** append the largest expression beginning with prefix containing no operators of precedence below <tt>minPrecedence</tt> */
public ForthBlock startExpr() throws IOException { return startExpr(-1); }
- public ForthBlock startExpr(int minPrecedence) throws IOException { return startExpr(minPrecedence, new ForthBlock(line, sourceName)); }
- public ForthBlock startExpr(int minPrecedence, ForthBlock appendTo) throws IOException {
+ public void startExpr(ForthBlock block) throws IOException { startExpr(-1, block); }
+ public ForthBlock startExpr(int minPrecedence) throws IOException {
+ ForthBlock ret = new ForthBlock(line, sourceName);
+ startExpr(minPrecedence, ret);
+ return ret.size() == 0 ? null : ret;
+ }
+ public void startExpr(int minPrecedence, ForthBlock appendTo) throws IOException {
int tok = getToken();
int curLine = line;
- if (tok == -1) return null;
+ if (tok == -1) return;
if (minPrecedence > 0 && precedence[tok] != 0)
if (precedence[tok] < minPrecedence || (precedence[tok] == minPrecedence && !isRightAssociative[tok]))
- { pushBackToken(); return null; }
+ { pushBackToken(); return; }
ForthBlock b = appendTo;
switch (tok) {
- case NUMBER: return continueExpr(b.add(ForthBlock.LITERAL, number), minPrecedence);
- case STRING: return continueExpr(b.add(ForthBlock.LITERAL, string), minPrecedence);
- case THIS: return continueExpr(b.add(THIS, null), minPrecedence);
- case NULL: return continueExpr(b.add(ForthBlock.LITERAL, null), minPrecedence);
- case TRUE: case FALSE: return continueExpr(b.add(ForthBlock.LITERAL, new Boolean(tok == TRUE)), minPrecedence);
+ case NUMBER: continueExpr(b.add(ForthBlock.LITERAL, number), minPrecedence); return;
+ case STRING: continueExpr(b.add(ForthBlock.LITERAL, string), minPrecedence); return;
+ case THIS: continueExpr(b.add(THIS, null), minPrecedence); return;
+ case NULL: continueExpr(b.add(ForthBlock.LITERAL, null), minPrecedence); return;
+ case TRUE: case FALSE: continueExpr(b.add(ForthBlock.LITERAL, new Boolean(tok == TRUE)), minPrecedence); return;
case LB: {
b.add(b.ARRAY, new Integer(0));
int i = 0;
while(true) {
- ForthBlock e = startExpr();
- if (e == null && peekToken() == RB) { consume(RB); return continueExpr(b, minPrecedence); }
+ int size = b.size();
+ startExpr(b);
+ if (size == b.size())
+ if (peekToken() == RB) { consume(RB); continueExpr(b, minPrecedence); return; }
b.add(b.LITERAL, new Integer(i++));
- if (e == null) b.add(b.LITERAL, null);
- else b.add(b.EXPR, e);
+ if (size == b.size()) b.add(b.LITERAL, null);
b.add(b.PUT);
b.add(b.POP);
- if (peekToken() == RB) { consume(RB); return continueExpr(b, minPrecedence); }
+ if (peekToken() == RB) { consume(RB); continueExpr(b, minPrecedence); return; }
consume(COMMA);
}
}
case SUB: {
consume(NUMBER);
- return continueExpr(b.add(ForthBlock.LITERAL, new Double(number.doubleValue() * -1)), minPrecedence);
+ continueExpr(b.add(ForthBlock.LITERAL, new Double(number.doubleValue() * -1)), minPrecedence);
+ return;
}
case LP: {
- b.add(EXPR, startExpr());
+ startExpr(b);
consume(RP);
- return continueExpr(b, minPrecedence);
+ continueExpr(b, minPrecedence);
+ return;
}
case INC: case DEC: {
// prefix
- ForthBlock subexp = startExpr(precedence[tok]);
- subexp.set(subexp.size() - 1, tok, new Boolean(true));
- b.add(b.EXPR, subexp);
- return continueExpr(b, minPrecedence);
+ startExpr(precedence[tok], b);
+ b.set(b.size() - 1, tok, new Boolean(true));
+ continueExpr(b, minPrecedence);
+ return;
}
case BANG: case BITNOT: case INSTANCEOF: case TYPEOF: {
- b.add(b.EXPR, startExpr(precedence[tok]));
+ startExpr(precedence[tok], b);
b.add(tok);
- return continueExpr(b, minPrecedence);
+ continueExpr(b, minPrecedence);
+ return;
}
case LC: {
b.add(b.OBJECT, null);
- if (peekToken() == RC) { consume(RC); return continueExpr(b, minPrecedence); }
+ if (peekToken() == RC) { consume(RC); continueExpr(b, minPrecedence); return; }
while(true) {
if (peekToken() != NAME && peekToken() != STRING) throw new Error("expected NAME or STRING");
getToken();
b.add(b.LITERAL, string);
consume(COLON);
- b.add(b.EXPR, startExpr());
+ startExpr(b);
b.add(b.PUT);
b.add(b.POP);
- if (peekToken() == RC) { consume(RC); return continueExpr(b, minPrecedence); }
+ if (peekToken() == RC) { consume(RC); continueExpr(b, minPrecedence); return; }
consume(COMMA);
- if (peekToken() == RC) { consume(RC); return continueExpr(b, minPrecedence); }
+ if (peekToken() == RC) { consume(RC); continueExpr(b, minPrecedence); return; }
}
}
case NAME: {
consume(ASSIGN);
b.add(THIS);
b.add(ForthBlock.LITERAL, name);
- b.add(ForthBlock.EXPR, startExpr(minPrecedence));
+ startExpr(minPrecedence, b);
b.add(ForthBlock.PUT);
b.add(ForthBlock.SWAP);
b.add(ForthBlock.POP);
b.add(ForthBlock.LITERAL, name);
b.add(ForthBlock.GET);
}
- return continueExpr(b, minPrecedence);
+ continueExpr(b, minPrecedence);
+ return;
}
case Tokens.FUNCTION: {
consume(LP);
parseStatement(true, b2);
b2.add(b.LITERAL, null);
b2.add(RETURN);
- return continueExpr(b.add(OpCodes.FUNCTION, b2), minPrecedence);
+ continueExpr(b.add(OpCodes.FUNCTION, b2), minPrecedence);
+ return;
}
- default: pushBackToken(); return null;
+ default: pushBackToken(); return;
}
}
-
- /** return the largest expression beginning with prefix containing no operators of precedence below <tt>minPrecedence</tt> */
- public ForthBlock continueExpr(ForthBlock prefix, int minPrecedence) throws IOException {
- return continueExpr(prefix, minPrecedence, new ForthBlock(line, sourceName));
- }
- public ForthBlock continueExpr(ForthBlock prefix, int minPrecedence, ForthBlock appendTo) throws IOException {
+ public void continueExpr(ForthBlock prefix, int minPrecedence) throws IOException {
int tok = getToken();
int curLine = line;
- if (tok == -1) return prefix;
+ if (tok == -1) return;
if (minPrecedence > 0 && precedence[tok] != 0)
if (precedence[tok] < minPrecedence || (precedence[tok] == minPrecedence && !isRightAssociative[tok]))
- { pushBackToken(); return prefix; }
+ { pushBackToken(); return; }
if (prefix == null) throw new Error("got null prefix");
-
- ForthBlock b = appendTo;
+ ForthBlock b = prefix;
switch (tok) {
case ASSIGN_BITOR: case ASSIGN_BITXOR: case ASSIGN_BITAND: case ASSIGN_LSH: case ASSIGN_RSH: case ASSIGN_URSH:
case ASSIGN_ADD: case ASSIGN_SUB: case ASSIGN_MUL: case ASSIGN_DIV: case ASSIGN_MOD: {
- b.add(b.EXPR, prefix);
prefix.set(prefix.size() - 1, b.GET_PRESERVE, new Boolean(true));
- prefix.add(prefix.EXPR, startExpr(precedence[tok - 1]));
+ startExpr(precedence[tok - 1], b);
prefix.add(tok - 1);
prefix.add(b.PUT);
prefix.add(b.SWAP);
prefix.add(b.POP);
- return continueExpr(b, minPrecedence);
+ continueExpr(b, minPrecedence);
+ return;
}
case INC: case DEC: {
// postfix
- prefix.set(prefix.size() - 1, tok, new Boolean(false));
- b.add(b.EXPR, prefix);
- return continueExpr(b, minPrecedence);
+ b.set(b.size() - 1, tok, new Boolean(false));
+ continueExpr(b, minPrecedence);
+ return;
}
case LP: {
// invocation
int i = 0;
- b.add(b.EXPR, prefix);
while(peekToken() != RP) {
- b.add(b.EXPR, startExpr());
+ startExpr(b);
i++;
if (peekToken() == RP) break;
consume(COMMA);
}
consume(RP);
b.add(b.CALL, new Integer(i));
- return continueExpr(b, minPrecedence);
+ continueExpr(b, minPrecedence);
+ return;
}
case BITOR: case BITXOR: case BITAND: case SHEQ: case SHNE: case LSH:
case RSH: case URSH: case ADD: case MUL: case DIV: case MOD:
case GT: case GE: case EQ: case NE: case LT: case LE: case SUB: {
- b.add(b.EXPR, prefix);
- b.add(b.EXPR, startExpr(precedence[tok]));
+ startExpr(precedence[tok], b);
b.add(tok);
- return continueExpr(b, minPrecedence);
+ continueExpr(b, minPrecedence);
+ return;
}
case OR: case AND: {
+ b.add(tok == AND ? b.JF : b.JT, new Integer(0));
+ int size = b.size();
+ startExpr(precedence[tok], b);
+ b.arg[size - 1] = new Integer(b.size() - size + 2);
+ b.add(b.JMP, new Integer(2));
b.add(b.LITERAL, tok == AND ? new Boolean(false) : new Boolean(true));
- b.add(b.EXPR, prefix);
- b.add(tok == AND ? b.JF : b.JT, new Integer(3));
- b.add(b.POP);
- b.add(b.EXPR, startExpr(precedence[tok]));
- return continueExpr(b, minPrecedence);
+ continueExpr(b, minPrecedence);
+ return;
}
case DOT: {
consume(NAME);
String target = string;
- b.add(b.EXPR, prefix);
if (peekToken() == ASSIGN) {
consume(ASSIGN);
- ForthBlock val = startExpr();
b.add(b.LITERAL, target);
- b.add(b.EXPR, val);
+ startExpr(b);
b.add(b.PUT);
b.add(b.SWAP);
b.add(b.POP);
b.add(b.LITERAL, target);
b.add(b.GET);
}
- return continueExpr(b, minPrecedence);
+ continueExpr(b, minPrecedence);
+ return;
}
case LB: {
- b.add(b.EXPR, prefix);
- b.add(b.EXPR, startExpr());
+ startExpr(b);
consume(RB);
if (peekToken() == ASSIGN) {
consume(ASSIGN);
- b.add(b.EXPR, startExpr());
+ startExpr(b);
b.add(b.PUT);
b.add(b.SWAP);
b.add(b.POP);
} else {
b.add(b.GET);
}
- return continueExpr(b, minPrecedence);
+ continueExpr(b, minPrecedence);
+ return;
}
case HOOK: {
- b.add(b.EXPR, prefix);
- b.add(b.JF, new Integer(3));
- b.add(b.EXPR, startExpr());
- b.add(b.JMP, new Integer(2));
+ b.add(b.JF, new Integer(0));
+ int size = b.size();
+ startExpr(b);
+ b.arg[size - 1] = new Integer(b.size() - size + 2);
+ b.add(b.JMP, new Integer(0));
consume(COLON);
- b.add(b.EXPR, startExpr());
- return continueExpr(b, minPrecedence);
+ size = b.size();
+ startExpr(b);
+ b.arg[size - 1] = new Integer(b.size() - size + 1);
+ continueExpr(b, minPrecedence);
+ return;
}
-
- default: pushBackToken(); return prefix;
+ default: { pushBackToken(); return; }
}
}
case THROW: case RETURN: case ASSERT: {
getToken();
if (tok == RETURN && peekToken() == SEMI) b.add(b.LITERAL, null);
- else b.add(b.EXPR, startExpr());
+ else startExpr(b);
consume(SEMI);
b.add(tok);
break;
if (peekToken() == ASSIGN) { // if there is an '=' after the variable name
b.add(b.LITERAL, name); // put the var name back on the stack
consume(ASSIGN);
- b.add(b.EXPR, startExpr());
+ startExpr(b);
b.add(b.PUT);
b.add(b.POP);
}
case IF: {
consume(IF);
consume(LP);
- b.add(b.EXPR, startExpr());
+ startExpr(b);
consume(RP);
- b.add(b.JF, new Integer(4));
- b.add(b.EXPR, parseStatement());
- b.add(b.POP);
- b.add(b.JMP, new Integer(3));
+
+ b.add(b.JF, new Integer(0));
+ int size = b.size();
+ parseStatement(false, b);
+
if (peekToken() == ELSE) {
consume(ELSE);
+ b.arg[size - 1] = new Integer(2 + b.size() - size);
+ b.add(b.JMP, new Integer(3));
b.add(b.EXPR, parseStatement());
b.add(b.POP);
} else {
- b.add(b.JMP, new Integer(1)); // nop
- b.add(b.JMP, new Integer(1)); // nop
+ b.arg[size - 1] = new Integer(1 + b.size() - size);
}
break;
}
b.add(loop.LOOP, loop);
loop.add(loop.POP);
- loop.add(loop.EXPR, startExpr());
+ startExpr(loop);
loop.add(loop.JT, new Integer(2));
loop.add(Lexer.BREAK);
consume(RP);
consume(LP);
ForthBlock loop = new ForthBlock(curLine, sourceName);
b.add(loop.LOOP, loop);
- loop.add(loop.EXPR, startExpr());
+ startExpr(loop);
consume(RP);
consume(LC);
- while(true) {
- ForthBlock caseForthBlock;
- tok = getToken();
- if (tok == CASE) {
+ while(true)
+ if (peekToken() == CASE) {
+ consume(CASE);
loop.add(loop.DUP);
- loop.add(loop.EXPR, startExpr());
+ startExpr(loop);
+ consume(COLON);
loop.add(EQ);
- loop.add(loop.JF, new Integer(2));
- } else if (tok != DEFAULT) throw new ParserException("expected CASE or DEFAULT");
- consume(COLON);
- ForthBlock b2 = new ForthBlock(curLine, sourceName);
- ForthBlock e1 = null;
- while(peekToken() != CASE && peekToken() != DEFAULT && peekToken() != RC) {
- if ((e1 = parseStatement()) == null) break;
- b2.add(b.EXPR, e1);
- b2.add(b.POP);
- }
- loop.add(loop.EXPR, b2);
- if (peekToken() == RC) {
+ loop.add(loop.JF, new Integer(0));
+ int size = loop.size();
+ while(peekToken() != CASE && peekToken() != DEFAULT && peekToken() != RC) {
+ int size2 = loop.size();
+ parseStatement(false, loop);
+ if (size2 == loop.size()) break;
+ }
+ loop.arg[size - 1] = new Integer(1 + loop.size() - size);
+ } else if (peekToken() == DEFAULT) {
+ consume(DEFAULT);
+ consume(COLON);
+ while(peekToken() != CASE && peekToken() != DEFAULT && peekToken() != RC) {
+ int size2 = loop.size();
+ parseStatement(false, loop);
+ if (size2 == loop.size()) break;
+ }
+ } else if (peekToken() == RC) {
consume(RC);
loop.add(BREAK);
break;
+ } else {
+ throw new ParserException("expected CASE, DEFAULT, or RC; got " + codeToString[peekToken()]);
}
- }
break;
}
parseStatement(false, loop);
consume(WHILE);
consume(LP);
- loop.add(loop.EXPR, startExpr());
+ startExpr(loop);
loop.add(loop.JT, new Integer(2));
loop.add(Lexer.BREAK);
loop.add(Lexer.CONTINUE);
if (forIn) {
consume(NAME);
consume(IN);
- b.add(b.EXPR, startExpr());
+ startExpr(b);
b.add(b.PUSHKEYS);
b.add(b.LITERAL, "length");
b.add(b.GET);
b2.add(b.DECLARE);
b2.add(b.PUT);
b2.add(b.EXPR, parseStatement());
- //b2.add(b.LITERAL, null);
break;
} else {
b.add(b.SCOPE, b2);
b.add(b.POP);
- ForthBlock e1 = startExpr();
- if (e1 == null) e1 = new ForthBlock(curLine, sourceName, b.LITERAL, null);
-
- b2.add(b.EXPR, e1);
- b2.add(b.POP);
+ int size = b2.size();
+ startExpr(b2);
+ if (b2.size() - size > 0) b2.add(b.POP);
consume(SEMI);
ForthBlock e2 = startExpr();
consume(SEMI);
- ForthBlock e3 = startExpr();
- consume(RP);
if (e2 == null) e2 = new ForthBlock(curLine, sourceName, b.LITERAL, null);
- if (e3 == null) e3 = new ForthBlock(curLine, sourceName, b.LITERAL, null);
ForthBlock b3 = new ForthBlock(curLine, sourceName);
b2.add(b.LOOP, b3);
b2.add(b.LITERAL, null);
- b3.add(b.JT, new Integer(3));
- b3.add(b.EXPR, e3);
- b3.add(b.POP);
+
+ b3.add(b.JT, new Integer(0));
+ size = b3.size();
+ startExpr(b3);
+ consume(RP);
+ if (b3.size() - size > 0) b3.add(b.POP);
+ b3.arg[size - 1] = new Integer(b3.size() - size + 1);
+
b3.add(b.EXPR, e2);
b3.add(b.JT, new Integer(2));
b3.add(BREAK);
if (tok == RC && braced) { consume(RC); return; }
// fall through
default: {
- ForthBlock ret = startExpr();
- if (ret == null) return;
- b.add(b.EXPR, ret);
+ int size = b.size();
+ startExpr(b);
+ if (size == b.size()) return;
b.add(b.POP);
if (peekToken() == SEMI) consume(SEMI);
break;