2003/06/04 23:35:36
authormegacz <megacz@xwt.org>
Fri, 30 Jan 2004 07:00:44 +0000 (07:00 +0000)
committermegacz <megacz@xwt.org>
Fri, 30 Jan 2004 07:00:44 +0000 (07:00 +0000)
darcs-hash:20040130070044-2ba56-0ab3382d5fd4ae849b5735d0e3ea20c46fa6c48f.gz

src/org/xwt/js/ForthBlock.java [new file with mode: 0644]
src/org/xwt/js/JS.java
src/org/xwt/js/Lexer.java
src/org/xwt/js/Parser.java
src/org/xwt/js/Tokens.java [new file with mode: 0644]

diff --git a/src/org/xwt/js/ForthBlock.java b/src/org/xwt/js/ForthBlock.java
new file mode 100644 (file)
index 0000000..6946258
--- /dev/null
@@ -0,0 +1,349 @@
+// Copyright 2002 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.xwt.js;
+
+import org.xwt.util.*;
+import java.io.*;
+
+/** a block of Forth bytecode */
+class ForthBlock implements Tokens {
+
+    /*
+     *  Each instruction is an opcode and an optional literal literal.
+     */
+
+       // opcodes:
+       public static final byte arithmetic = -1;  //                               -- arithmetic operators from parser
+       public static final byte LITERAL = -2;     // < String | Number | null >    -- push a literal onto the stack
+       public static final byte ARRAY = -3;       // < size >                      -- create a new array of size <size>
+       public static final byte OBJECT = -4;      //                               -- push an empty object onto the stack
+       public static final byte FUNCTION = -5;    // < bytecode_block >            -- push a new instance of a function with the given bytecode
+       public static final byte DECLARE = -6;     // < name >                      -- declare <name> in the current scope
+       public static final byte THIS = -7;        //                               -- push the topmost non-transparent scope onto the stack
+       public static final byte GET = -8;         //                               -- get stack[0] from stack[1]
+       public static final byte GET_PRESERVE = -80;         //                               -- get stack[0] from stack[1]
+       public static final byte PUT = -9;         //                               -- put stack[1] to key stack[0] on stack[2]; leaves object on the stack
+       public static final byte JT = -13;         // < relative_address >          -- pop the stack; if true, jump to <relative_address>
+       public static final byte JF = -21;         // < relative_address >          -- pop the stack; if false, jump to <relative_address>
+       public static final byte JMP = -22;        // < relative_address >          -- jump to <relative_address>
+        static public final byte POP = -14;        //                               -- discard the top element on the stack
+
+       public static final byte CALL = -15;       // < numargs >                   -- call stack[0] with the topmost <numargs> values as arguments
+
+       public static final byte PUSHKEYS = -19;    //                               -- ??
+       public static final byte EXPR = -20;       //                               -- transitional
+       public static final byte SWAP = -23;       //                               -- transitional
+       public static final byte SCOPE = -30;       //                               -- transitional
+       public static final byte LOOP = -40;       //                               -- transitional
+       public static final byte DUP = -50;       //                               -- transitional
+       public static final byte LABEL = -60;       //                               -- transitional
+
+       int line;
+       String sourceName;
+       int[] op = new int[10];
+       Object[] arg = new Object[10];
+       int size = 0;
+
+       public ForthBlock(int line, String sourceName) { this.line = line; this.sourceName = sourceName; }
+       public ForthBlock(int line, String sourceName, int op_, Object arg_) { this(line, sourceName); add(op_, arg_); }
+
+       public int size() { return size; }
+       public void set(int pos, int op_, Object arg_) { op[pos] = op_; arg[pos] = arg_; }
+       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;
+       }
+       
+       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), t)); 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 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 PUT: {
+                   Object val = t.pop();
+                   Object key = t.pop();
+                   ((JS)t.peek()).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_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 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 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;
+               }
+
+               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.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) {
+               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;
+       }
+
+    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; }
+    }
+
+    static class EvaluatorException extends RuntimeException {
+       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; }
+    }
+    static class ContinueException extends ControlTransferException {
+       public String label;
+       ContinueException(String label) { this.label = label; }
+    }
+    static class ReturnException extends ControlTransferException {
+       public Object retval;
+       ReturnException(Object retval) { this.retval = retval; }
+    }
+    
+}
index 0628891..c053881 100644 (file)
@@ -26,6 +26,23 @@ public abstract class JS {
        return "unknown:??";
     }
 
+    public static boolean toBoolean(Object o) {
+       if (o == null) return false;
+       if (o instanceof Boolean) return ((Boolean)o).booleanValue();
+       if (o instanceof Number) return o.equals(new Integer(0));
+       return true;
+    }
+    public static long toLong(Object o) { return toNumber(o).longValue(); }
+    public static double toDouble(Object o) { return toNumber(o).doubleValue(); }
+    public static Number toNumber(Object o) {
+       if (o == null) return new Long(0);
+       if (o instanceof Number) return ((Number)o);
+       if (o instanceof String) try { return new Double((String)o); } catch (NumberFormatException e) { return new Double(0); }
+       if (o instanceof Boolean) return ((Boolean)o).booleanValue() ? new Long(1) : new Long(0);
+       if (o instanceof JS) return ((JS)o).coerceToNumber();
+       // FIXME
+       throw new Error("toNumber() got object of type " + o.getClass().getName());
+    }
 
     // Instance Methods ////////////////////////////////////////////////////////////////////
  
@@ -88,7 +105,7 @@ public abstract class JS {
            }
        }
        public void put(Object key, Object val) {
-           if (key.equals("length")) vec.setSize(Parser.toNumber(val).intValue());
+           if (key.equals("length")) vec.setSize(toNumber(val).intValue());
            int i = intVal(key);
            if (i == Integer.MIN_VALUE) super.put(key, val);
            else {
@@ -121,17 +138,17 @@ public abstract class JS {
     public static class Script extends Function {
        Vector e = null;
        private Script(Vector e) { this.e = e; }
-       public String getSourceName() throws JS.Exn { return ((Parser.Expr)e.elementAt(0)).sourceName; }
+       public String getSourceName() throws JS.Exn { return ((ForthBlock)e.elementAt(0)).sourceName; }
        public Object _call(JS.Array args) throws JS.Exn {
            Scope rootScope = (Scope)args.elementAt(0);
            Function saved = (Function)currentFunction.get(Thread.currentThread());
            currentFunction.put(Thread.currentThread(), this);
            try {
                for(int i=0; i<e.size(); i++)
-                   ((Parser.Expr)e.elementAt(i)).eval(rootScope);
-           } catch (Parser.ReturnException e) {
+                   ((ForthBlock)e.elementAt(i)).eval(rootScope);
+           } catch (ForthBlock.ReturnException e) {
                // ignore
-           } catch (Parser.ControlTransferException e) {
+           } catch (ForthBlock.ControlTransferException e) {
                throw new JS.Exn("block threw a ControlTransferException: " + e);
            } finally {
                if (saved == null) currentFunction.remove(Thread.currentThread());
@@ -144,7 +161,7 @@ public abstract class JS {
            try {
                Vector exprs = new Vector();
                while(true) {
-                   Parser.Expr ret = p.parseBlock(false);
+                   ForthBlock ret = p.parseStatement(false);
                    if (ret == null) break;
                    exprs.addElement(ret);
                }
index 3a80f5a..2a28338 100644 (file)
@@ -20,7 +20,7 @@
 package org.xwt.js;
 import java.io.*;
 
-class Lexer {
+class Lexer implements Tokens {
 
     public static void main(String[] s) throws Exception {
        Lexer l = new Lexer(new InputStreamReader(System.in));
@@ -42,113 +42,6 @@ class Lexer {
     public Lexer(Reader r) throws IOException { in = new SmartReader(r); }
 
 
-    // Token Constants //////////////////////////////////////////////////////////
-
-    public final static int
-        EOL          = 1,   // end of line
-        RETURN       = 2,   // return
-        GOTO         = 3,   // goto
-        BITOR        = 4,   // |
-        ASSIGN_BITOR = 5,   // |=
-        BITXOR       = 6,   // ^
-        ASSIGN_BITXOR= 7,   // ^=
-        BITAND       = 8,   // &
-        ASSIGN_BITAND= 9,   // &=
-        EQ           = 10,  // ==
-        NE           = 11,  // !=
-        LT           = 12,  // <
-        LE           = 13,  // <=
-        GT           = 14,  // >
-        GE           = 15,  // >=
-        LSH          = 16,  // <<
-        ASSIGN_LSH   = 17, // <<=
-        RSH          = 18,  // >>
-        ASSIGN_RSH   = 19, // >>=
-        URSH         = 20,  // >>>
-        ASSIGN_URSH  = 21, // >>>=
-        ADD          = 22,  // +
-        ASSIGN_ADD   = 23, // +=
-        SUB          = 24,  // -
-        ASSIGN_SUB   = 25, // -=
-        MUL          = 26,  // *
-        ASSIGN_MUL   = 27, // *=
-        DIV          = 28,  // /
-        ASSIGN_DIV   = 29, // /=
-        MOD          = 30,  // %
-        ASSIGN_MOD   = 31, // %=
-        BITNOT       = 32,  // ~
-        ASSIGN_BITNOT= 33, // ~=
-        DELPROP      = 34,  // delete
-        TYPEOF       = 35,  // typeof
-        NAME         = 36,  // *** identifiers ***
-        NUMBER       = 37,  // *** numeric literals ***
-        STRING       = 38,  // *** string literals ***
-        NULL         = 39,  // null
-        THIS         = 40,  // this
-        FALSE        = 41,  // false
-        TRUE         = 42,  // true
-        SHEQ         = 43,  // ===
-        SHNE         = 44,  // !==
-        THROW        = 45,  // throw
-        IN           = 46,  // in
-        INSTANCEOF   = 47,  // instanceof
-        TRY          = 48,  // try
-        SEMI         = 49,  // ;
-        LB           = 50,  // [
-        RB           = 51,  // ]
-        LC           = 52,  // {
-        RC           = 53,  // }
-        LP           = 54,  // (
-        RP           = 55,  // )
-        COMMA        = 56,  // ,
-        ASSIGN       = 57,  // =
-        HOOK         = 58,  // ?
-        COLON        = 59,  // :
-        OR           = 60, // ||
-        AND          = 61, // &&
-        INC          = 62, // ++
-        DEC          = 63, // --
-        DOT          = 64, // .
-        FUNCTION     = 65, // function
-       IF           = 66,  // if keyword
-        ELSE         = 67, // else keyword
-        SWITCH       = 68, // switch keyword
-        CASE         = 69, // case keyword
-        DEFAULT      = 70, // default keyword
-        WHILE        = 71, // while keyword
-        DO           = 72, // do keyword
-        FOR          = 73, // for keyword
-        BREAK        = 74, // break keyword
-        CONTINUE     = 75, // continue keyword
-        VAR          = 76, // var keyword
-        WITH         = 77, // with keyword
-        CATCH        = 78, // catch keyword
-        FINALLY      = 79, // finally keyword
-        RESERVED     = 80, // reserved keywords
-        NOP          = 81, // NOP
-        VOID         = 82, // void keyword
-        MOD_ASSIGN   = 83, // %=
-        BANG         = 84, // %=
-        ASSERT       = 85; // assert keyword
-
-    public static final int MAX_TOKEN = ASSERT;
-
-    public final static String[] codeToString = new String[] {
-       "0", "EOL", "RETURN", "GOTO", "BITOR", "ASSIGN_BITOR",
-       "BITXOR", "ASSIGN_BITXOR", "BITAND", "ASSIGN_BITAND", "EQ",
-       "NE", "LT", "LE", "GT", "GE", "LSH", "ASSIGN_LSH", "RSH",
-       "ASSIGN_RSH", "URSH", "ASSIGN_URSH", "ADD", "ASSIGN_ADD",
-       "SUB", "ASSIGN_SUB", "MUL", "ASSIGN_MUL", "DIV", "ASSIGN_DIV",
-       "MOD", "ASSIGN_MOD", "BITNOT", "ASSIGN_BITNOT=", "DELPROP",
-       "TYPEOF", "NAME", "NUMBER", "STRING", "NULL", "THIS", "FALSE",
-       "TRUE", "SHEQ", "SHNE", "THROW", "IN", "INSTANCEOF", "TRY",
-       "SEMI", "LB", "RB", "LC", "RC", "LP", "RP", "COMMA", "ASSIGN",
-       "HOOK", "COLON", "OR", "AND", "INC", "DEC", "DOT", "FUNCTION",
-       "IF", "ELSE", "SWITCH", "CASE", "DEFAULT", "WHILE", "DO",
-       "FOR", "BREAK", "CONTINUE", "VAR", "WITH", "CATCH", "FINALLY",
-       "RESERVED", "NOP", "VOID", "MOD_ASSIGN", "BANG", "ASSERT" };
-
-
     // Predicates ///////////////////////////////////////////////////////////////////////
 
     protected static boolean isJSIdentifier(String s) {
index 6f55b50..0a8fa22 100644 (file)
@@ -4,8 +4,7 @@ package org.xwt.js;
 import org.xwt.util.*;
 import java.io.*;
 
-
-/** parses a stream of lexed tokens into a tree of Expr's */
+/** parses a stream of lexed tokens into ForthBlock's */
 public class Parser extends Lexer {
 
     // Constructors //////////////////////////////////////////////////////
@@ -20,7 +19,7 @@ public class Parser extends Lexer {
     public static void main(String[] s) throws Exception {
        Parser p = new Parser(new InputStreamReader(System.in), "stdin", 0);
        while(true) {
-           Expr block = p.parseBlock(false);
+           ForthBlock block = p.parseStatement(false);
            if (block == null) return;
            System.out.println(block);
            if (p.peekToken() == -1) return;
@@ -64,50 +63,46 @@ public class Parser extends Lexer {
     }
 
     /** parses the largest possible expression */
-    public Expr parseMaximalExpr() throws IOException { return parseMaximalExpr(null, -1); }
-    public Expr parseMaximalExpr(Expr prefix, int minPrecedence) throws IOException {
+    public ForthBlock parseMaximalForthBlock() throws IOException { return parseMaximalForthBlock(null, -1); }
+    public ForthBlock parseMaximalForthBlock(ForthBlock prefix, int minPrecedence) throws IOException {
        while(true) {
            if (peekToken() == -1) break;
-           Expr save = prefix;
-           prefix = parseSingleExpr(prefix, minPrecedence);
+           ForthBlock save = prefix;
+           prefix = parseSingleForthBlock(prefix, minPrecedence);
            if (save == prefix) break;
-           if (prefix == null) throw new ParserException("parseSingleExpr() returned null");
+           if (prefix == null) throw new ParserException("parseSingleForthBlock() returned null");
        }
        return prefix;
     }
 
-    public Expr parseSingleExpr(Expr prefix, int minPrecedence) throws IOException {
-       Expr e1 = null, e2 = null, e3 = null, head = null, tail = null, ret = null;
+    public ForthBlock parseSingleForthBlock(ForthBlock prefix, int minPrecedence) throws IOException {
+       ForthBlock e1 = null, e2 = null, e3 = null, head = null, tail = null, ret = null;
 
        int tok = peekToken();
-       if (minPrecedence > 0 &&
-           tok < precedence.length &&
-           precedence[tok] != 0 &&
-           (isRightAssociative[tok] ?
-            (precedence[tok] < minPrecedence) :
-            (precedence[tok] <= minPrecedence))) {
+       if (minPrecedence > 0 && tok < precedence.length && precedence[tok] != 0 &&
+           (isRightAssociative[tok] ? (precedence[tok] < minPrecedence) : (precedence[tok] <= minPrecedence)))
            return prefix;
-            }
+
        getToken();
        int curLine = line;
 
-       // these case arms match the precedence of operators; each arm is a precedence level.
        switch (tok) {
 
        case VAR: {
            if (prefix != null) { pushBackToken(); return prefix; }
-           ByteCode b = new ByteCode(curLine);
-           b.add(b.THIS, NO_ARG);
+           ForthBlock b = new ForthBlock(curLine, sourceName);
+           b.add(b.THIS);
            while(true) {
                consume(NAME);
                String name = string;
-               b.add(b.DECLARE, name);
+               b.add(b.LITERAL, name);
+               b.add(b.DECLARE);
                if (peekToken() == ASSIGN) {
                    b.add(b.LITERAL, name);
                    consume(ASSIGN);
-                   b.add(b.EXPR, parseMaximalExpr());
-                   b.add(b.PUT, NO_ARG);
-                   b.add(b.POP, NO_ARG);
+                   b.add(b.EXPR, parseMaximalForthBlock());
+                   b.add(b.PUT);
+                   b.add(b.POP);
                }
                if (peekToken() != COMMA) break;
                consume(COMMA);
@@ -119,16 +114,16 @@ public class Parser extends Lexer {
 
        case IF: {
            if (prefix != null) { pushBackToken(); return prefix; }
-           ByteCode b = new ByteCode(curLine);
+           ForthBlock b = new ForthBlock(curLine, sourceName);
            consume(LP);
-           b.add(b.EXPR, parseMaximalExpr());
+           b.add(b.EXPR, parseMaximalForthBlock());
            consume(RP);
            b.add(b.JF, new Integer(3));
-           b.add(b.EXPR, parseBlock(false));
+           b.add(b.EXPR, parseStatement(false));
            b.add(b.JMP, new Integer(2));
            if (peekToken() != ELSE) return b.add(b.LITERAL, null);
            consume(ELSE);
-           b.add(b.EXPR, parseBlock(false));
+           b.add(b.EXPR, parseStatement(false));
            return b;
        }
 
@@ -145,41 +140,41 @@ public class Parser extends Lexer {
        case ASSIGN_DIV: if (tok == ASSIGN_DIV) tok = DIV;
        case ASSIGN_MOD: if (tok == ASSIGN_MOD) tok = MOD;
            {
-               ByteCode b = (ByteCode)prefix;
+               ForthBlock b = (ForthBlock)prefix;
                b.set(b.size() - 1, b.GET_PRESERVE, new Boolean(true));
-               b.add(b.EXPR, parseMaximalExpr(null, precedence[tok]));
-               b.add(tok, NO_ARG);
-               b.add(b.PUT, NO_ARG);
-               b.add(b.SWAP, NO_ARG);
-               b.add(b.POP, NO_ARG);
+               b.add(b.EXPR, parseMaximalForthBlock(null, precedence[tok]));
+               b.add(tok);
+               b.add(b.PUT);
+               b.add(b.SWAP);
+               b.add(b.POP);
                return b;
            }
 
        case INC: case DEC:
            if (prefix == null) {
                // prefix
-               ByteCode b = (ByteCode)parseMaximalExpr(null, precedence[tok]);
+               ForthBlock b = (ForthBlock)parseMaximalForthBlock(null, precedence[tok]);
                b.set(b.size() - 1, tok, new Boolean(true));
                return b;
            } else {
                // postfix
-               ByteCode b = (ByteCode)prefix;
+               ForthBlock b = (ForthBlock)prefix;
                b.set(b.size() - 1, tok, new Boolean(false));
                return b;
            }
 
        case LP:
            if (prefix == null) {  // grouping
-               ByteCode b = new ByteCode(curLine, ByteCode.EXPR, parseMaximalExpr());
+               ForthBlock b = new ForthBlock(curLine, sourceName, ForthBlock.EXPR, parseMaximalForthBlock());
                consume(RP);
                return b;
 
            } else {  // invocation
-               ByteCode b = new ByteCode(curLine);
+               ForthBlock b = new ForthBlock(curLine, sourceName);
                int i = 0;
                b.add(b.EXPR, prefix);
                while(peekToken() != RP) {
-                   b.add(b.EXPR, parseMaximalExpr());
+                   b.add(b.EXPR, parseMaximalForthBlock());
                    i++;
                    if (peekToken() == RP) break;
                    consume(COMMA);
@@ -191,49 +186,37 @@ public class Parser extends Lexer {
 
        case BANG: case BITNOT: case INSTANCEOF: case TYPEOF: {
            if (prefix != null) { pushBackToken(); return prefix; }
-           ByteCode b = new ByteCode(curLine);
-           b.add(b.EXPR, parseMaximalExpr(null, precedence[tok]));
-           b.add(tok, NO_ARG);
+           ForthBlock b = new ForthBlock(curLine, sourceName);
+           b.add(b.EXPR, parseMaximalForthBlock(null, precedence[tok]));
+           b.add(tok);
            return b;
        }
 
        case SUB:
            if (prefix == null && peekToken() == NUMBER) {
                getToken();
-               return new ByteCode(curLine, ByteCode.LITERAL, new Double(number.doubleValue() * -1));
+               return new ForthBlock(curLine, sourceName, ForthBlock.LITERAL, new Double(number.doubleValue() * -1));
            } // else fall through
         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: {
            if (prefix == null) throw new ParserException("the " + codeToString[tok] + " token cannot start an expression");
-           ByteCode b = new ByteCode(curLine);
+           ForthBlock b = new ForthBlock(curLine, sourceName);
            b.add(b.EXPR, prefix);
-           b.add(b.EXPR, parseMaximalExpr(null, precedence[tok]));
-           b.add(tok, NO_ARG);
+           b.add(b.EXPR, parseMaximalForthBlock(null, precedence[tok]));
+           b.add(tok);
            return b;
        }
 
            // includes short-circuit logic
        case OR: case AND: {
            if (prefix == null) throw new ParserException("the " + codeToString[tok] + " token cannot start an expression");
-           ByteCode b = new ByteCode(curLine);
+           ForthBlock b = new ForthBlock(curLine, sourceName);
            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, NO_ARG);
-           b.add(b.EXPR, parseMaximalExpr(null, precedence[tok]));
-           return b;
-       }
-
-       case ASSIGN: {
-           if (prefix == null) throw new ParserException("the " + codeToString[tok] + " token cannot start an expression");
-           ByteCode b = new ByteCode(curLine);
-           b.add(b.THIS, NO_ARG);
-           b.add(b.LITERAL, prefix.string);   // FIXME, this is ass-ugly
-           b.add(b.EXPR, parseMaximalExpr(null, precedence[ASSIGN]));
-           b.add(b.PUT, NO_ARG);
-           b.add(b.SWAP, NO_ARG);
-           b.add(b.POP, NO_ARG);
+           b.add(b.POP);
+           b.add(b.EXPR, parseMaximalForthBlock(null, precedence[tok]));
            return b;
        }
 
@@ -242,93 +225,91 @@ public class Parser extends Lexer {
 
        case NUMBER:
            if (prefix != null) { pushBackToken(); return prefix; }
-           return new ByteCode(curLine, ByteCode.LITERAL, number);
+           return new ForthBlock(curLine, sourceName, ForthBlock.LITERAL, number);
 
        case STRING:
            if (prefix != null) { pushBackToken(); return prefix; }
-           return new ByteCode(curLine, ByteCode.LITERAL, string);
+           return new ForthBlock(curLine, sourceName, ForthBlock.LITERAL, string);
 
        case NULL: case TRUE: case FALSE: case NOP:
            if (prefix != null) { pushBackToken(); return prefix; }
-           return new ByteCode(curLine, ByteCode.LITERAL, (tok == NULL || tok == NOP) ? null : new Boolean(tok == TRUE));
+           return new ForthBlock(curLine, sourceName, ForthBlock.LITERAL, (tok == NULL || tok == NOP) ? null : new Boolean(tok == TRUE));
 
        case COMMA: pushBackToken(); return prefix;
 
        case THIS:
            if (prefix != null) { pushBackToken(); return prefix; } 
-           return new ByteCode(curLine, ByteCode.THIS, NO_ARG);
-
-           // potential lvalues
+           return new ForthBlock(curLine, sourceName, ForthBlock.THIS, null);
 
        case NAME: {
            if (prefix != null) { pushBackToken(); return prefix; }
            String name = string;
-           ByteCode b = new ByteCode(curLine);
+           ForthBlock b = new ForthBlock(curLine, sourceName);
            if (peekToken() == ASSIGN) {
                consume(ASSIGN);
-               b.add(ByteCode.THIS, NO_ARG);
-               b.add(ByteCode.LITERAL, name);
-               b.add(ByteCode.EXPR, parseMaximalExpr(null, minPrecedence));
-               b.add(ByteCode.PUT, NO_ARG);
-               b.add(ByteCode.SWAP, NO_ARG);
-               b.add(ByteCode.POP, NO_ARG);
+               b.add(ForthBlock.THIS);
+               b.add(ForthBlock.LITERAL, name);
+               b.add(ForthBlock.EXPR, parseMaximalForthBlock(null, minPrecedence));
+               b.add(ForthBlock.PUT);
+               b.add(ForthBlock.SWAP);
+               b.add(ForthBlock.POP);
                return b;
            } else {
-               b.add(ByteCode.THIS, NO_ARG);
-               b.add(ByteCode.LITERAL, name);
-               b.add(ByteCode.GET, NO_ARG);
-               return parseMaximalExpr(b, minPrecedence);
+               b.add(ForthBlock.THIS);
+               b.add(ForthBlock.LITERAL, name);
+               b.add(ForthBlock.GET);
+               return parseMaximalForthBlock(b, minPrecedence);
            }
        }
 
        case DOT: {
            consume(NAME);
            String target = string;
-           ByteCode b = new ByteCode(curLine);
+           ForthBlock b = new ForthBlock(curLine, sourceName);
            b.add(b.EXPR, prefix);
            if (peekToken() == ASSIGN) {
                consume(ASSIGN);
-               Expr val = parseMaximalExpr();
+               ForthBlock val = parseMaximalForthBlock();
                b.add(b.LITERAL, target);
                b.add(b.EXPR, val);
-               b.add(b.PUT, NO_ARG);
-               b.add(b.SWAP, NO_ARG);
-               b.add(b.POP, NO_ARG);
+               b.add(b.PUT);
+               b.add(b.SWAP);
+               b.add(b.POP);
            } else {
                b.add(b.LITERAL, target);
-               b.add(b.GET, NO_ARG);
+               b.add(b.GET);
            }
            return b;
        }
 
        case LB: {
-           ByteCode b = new ByteCode(curLine);
+           ForthBlock b = new ForthBlock(curLine, sourceName);
            if (prefix == null) {
                b.add(b.ARRAY, new Integer(0));
                int i = 0;
                while(true) {
-                   Expr e = parseMaximalExpr();
+                   ForthBlock e = parseMaximalForthBlock();
                    if (e == null && peekToken() == RB) { consume(RB); return b; }
                    b.add(b.LITERAL, new Integer(i++));
                    if (e == null) b.add(b.LITERAL, null);
                    else b.add(b.EXPR, e);
-                   b.add(b.PUT, NO_ARG);
-                   b.add(b.POP, NO_ARG);
+                   b.add(b.PUT);
+                   b.add(b.POP);
                    if (peekToken() == RB) { consume(RB); return b; }
                    consume(COMMA);
                }
            } else {
                b.add(b.EXPR, prefix);
-               b.add(b.EXPR, parseMaximalExpr());
+               b.add(b.EXPR, parseMaximalForthBlock());
                consume(RB);
                if (peekToken() == ASSIGN) {
                    consume(ASSIGN);
-                   b.add(b.EXPR, parseMaximalExpr());
-                   b.add(b.PUT, NO_ARG);
-                   b.add(b.SWAP, NO_ARG);
-                   b.add(b.POP, NO_ARG);
+                   b.add(b.EXPR, parseMaximalForthBlock());
+                   b.add(b.PUT);
+                   b.add(b.SWAP);
+                   b.add(b.POP);
                } else {
-                   b.add(b.GET, NO_ARG);
+                   b.add(b.GET);
                }
                return b;
            }
@@ -336,7 +317,7 @@ public class Parser extends Lexer {
 
        case LC: {
            if (prefix != null) { pushBackToken(); return prefix; }
-           ByteCode b = new ByteCode(curLine);
+           ForthBlock b = new ForthBlock(curLine, sourceName);
            b.add(b.OBJECT, null);
            if (peekToken() == RC) { consume(RC); return b; }
            while(true) {
@@ -344,9 +325,9 @@ public class Parser extends Lexer {
                getToken();
                b.add(b.LITERAL, string);
                consume(COLON);
-               b.add(b.EXPR, parseMaximalExpr());
-               b.add(b.PUT, NO_ARG);
-               b.add(b.POP, NO_ARG);
+               b.add(b.EXPR, parseMaximalForthBlock());
+               b.add(b.PUT);
+               b.add(b.POP);
                if (peekToken() == RC) { consume(RC); return b; }
                consume(COMMA);
                if (peekToken() == RC) { consume(RC); return b; }
@@ -354,30 +335,30 @@ public class Parser extends Lexer {
        }
            
        case HOOK: {
-           ByteCode b = new ByteCode(curLine);
+           ForthBlock b = new ForthBlock(curLine, sourceName);
            b.add(b.EXPR, prefix);
            b.add(b.JF, new Integer(3));
-           b.add(b.EXPR, parseMaximalExpr());
+           b.add(b.EXPR, parseMaximalForthBlock());
            b.add(b.JMP, new Integer(2));
            consume(COLON);
-           b.add(b.EXPR, parseMaximalExpr());
+           b.add(b.EXPR, parseMaximalForthBlock());
            return b;
        }
            
        case FUNCTION: {
            if (prefix != null) { pushBackToken(); return prefix; }
            consume(LP);
-           ByteCode b = new ByteCode(curLine);
+           ForthBlock b = new ForthBlock(curLine, sourceName);
            int numArgs = 0;
-           b.add(b.THIS, NO_ARG);
-           b.add(b.SWAP, NO_ARG);
+           b.add(b.THIS);
+           b.add(b.SWAP);
            b.add(b.LITERAL, "arguments");
            b.add(b.LITERAL, "arguments");
-           b.add(b.DECLARE, NO_ARG);
-           b.add(b.SWAP, NO_ARG);
-           b.add(b.PUT, NO_ARG);
-           b.add(b.SWAP, NO_ARG);
-           b.add(b.POP, NO_ARG);
+           b.add(b.DECLARE);
+           b.add(b.SWAP);
+           b.add(b.PUT);
+           b.add(b.SWAP);
+           b.add(b.POP);
            if (peekToken() == RP) consume(RP);
            else while(true) {
                if (peekToken() == COMMA) {
@@ -387,24 +368,24 @@ public class Parser extends Lexer {
 
                    // declare the name
                    b.add(b.LITERAL, string);
-                   b.add(b.DECLARE, NO_ARG);
+                   b.add(b.DECLARE);
 
                    // retrieve it from the arguments array
                    b.add(b.LITERAL, new Integer(numArgs));
-                   b.add(b.GET_PRESERVE, NO_ARG);
-                   b.add(b.SWAP, NO_ARG);
-                   b.add(b.POP, NO_ARG);
+                   b.add(b.GET_PRESERVE);
+                   b.add(b.SWAP);
+                   b.add(b.POP);
 
                    // put it to the current scope
-                   b.add(b.THIS, NO_ARG);
-                   b.add(b.SWAP, NO_ARG);
+                   b.add(b.THIS);
+                   b.add(b.SWAP);
                    b.add(b.LITERAL, string);
-                   b.add(b.SWAP, NO_ARG);
-                   b.add(b.PUT, NO_ARG);
+                   b.add(b.SWAP);
+                   b.add(b.PUT);
 
                    // clean the stack
-                   b.add(b.POP, NO_ARG);
-                   b.add(b.POP, NO_ARG);
+                   b.add(b.POP);
+                   b.add(b.POP);
 
                    if (peekToken() == RP) { consume(RP); break; }
                    consume(COMMA);
@@ -412,59 +393,59 @@ public class Parser extends Lexer {
                numArgs++;
            }
            // pop off the arguments array
-           b.add(b.POP, NO_ARG);
-           parseBlock(true, b);
-           return new ByteCode(curLine, b.FUNCTION, b);
+           b.add(b.POP);
+           parseStatement(true, b);
+           return new ForthBlock(curLine, sourceName, b.FUNCTION, b);
        }
            
        case WHILE: {
            if (prefix != null) { pushBackToken(); return prefix; }
            consume(LP);
-           ByteCode r = new ByteCode(curLine);
-           ByteCode loop = new ByteCode(curLine);
+           ForthBlock r = new ForthBlock(curLine, sourceName);
+           ForthBlock loop = new ForthBlock(curLine, sourceName);
            r.add(loop.LOOP, loop);
            r.add(r.LITERAL, null);
 
-           loop.add(loop.EXPR, parseMaximalExpr());
+           loop.add(loop.EXPR, parseMaximalForthBlock());
            loop.add(loop.JT, new Integer(2));
-           loop.add(Lexer.BREAK, NO_ARG);
+           loop.add(Lexer.BREAK);
            consume(RP);
-           parseBlock(false, loop);
+           parseStatement(false, loop);
 
            // if we fall out of the end, definately continue
-           loop.add(CONTINUE, NO_ARG);
+           loop.add(CONTINUE);
            return r;
        }
 
        case SWITCH: {
            if (prefix != null) { pushBackToken(); return prefix; }
            consume(LP);
-           ByteCode r = new ByteCode(curLine);
-           ByteCode loop = new ByteCode(curLine);
+           ForthBlock r = new ForthBlock(curLine, sourceName);
+           ForthBlock loop = new ForthBlock(curLine, sourceName);
            r.add(loop.LOOP, loop);
            r.add(r.LITERAL, null);
-           loop.add(loop.EXPR, parseMaximalExpr());
+           loop.add(loop.EXPR, parseMaximalForthBlock());
            consume(RP);
            consume(LC);
            while(true) {
-               Expr caseExpr;
+               ForthBlock caseForthBlock;
                tok = getToken();
                if (tok == CASE) {
-                   loop.add(loop.DUP, NO_ARG);
-                   loop.add(loop.EXPR, parseMaximalExpr());
-                   loop.add(EQ, NO_ARG);
+                   loop.add(loop.DUP);
+                   loop.add(loop.EXPR, parseMaximalForthBlock());
+                   loop.add(EQ);
                    loop.add(loop.JF, new Integer(2));
                } else if (tok != DEFAULT) throw new ParserException("expected CASE or DEFAULT");
                consume(COLON);
-               ByteCode b = new ByteCode(curLine);
+               ForthBlock b = new ForthBlock(curLine, sourceName);
                while(peekToken() != CASE && peekToken() != DEFAULT && peekToken() != RC) {
-                   if ((e1 = parseBlock(false)) == null) break;
+                   if ((e1 = parseStatement(false)) == null) break;
                    b.add(b.EXPR, e1);
                }
                loop.add(loop.EXPR, b);
                if (peekToken() == RC) {
                    consume(RC);
-                   r.add(BREAK, NO_ARG);
+                   r.add(BREAK);
                    return r;
                }
            }
@@ -472,18 +453,18 @@ public class Parser extends Lexer {
            
        case DO: {
            if (prefix != null) { pushBackToken(); return prefix; }
-           ByteCode r = new ByteCode(curLine);
-           ByteCode loop = new ByteCode(curLine);
+           ForthBlock r = new ForthBlock(curLine, sourceName);
+           ForthBlock loop = new ForthBlock(curLine, sourceName);
            r.add(loop.LOOP, loop);
            r.add(r.LITERAL, null);
 
-           parseBlock(false, loop);
+           parseStatement(false, loop);
            consume(WHILE);
            consume(LP);
-           loop.add(loop.EXPR, parseMaximalExpr());
+           loop.add(loop.EXPR, parseMaximalForthBlock());
            loop.add(loop.JT, new Integer(2));
-           loop.add(Lexer.BREAK, NO_ARG);
-           loop.add(Lexer.CONTINUE, NO_ARG);
+           loop.add(Lexer.BREAK);
+           loop.add(Lexer.CONTINUE);
            consume(RP);
            consume(SEMI);
            return r;
@@ -493,7 +474,7 @@ public class Parser extends Lexer {
            // FIXME: don't just ignore this!
            // We deliberately allow you to omit braces in catch{}/finally{} if they are single statements...
            if (prefix != null) { pushBackToken(); return prefix; }
-           Expr tryBlock = parseBlock(true);
+           ForthBlock tryBlock = parseStatement(true);
            
            tok = peekToken();
            if (tok == CATCH) {
@@ -518,49 +499,60 @@ public class Parser extends Lexer {
            boolean forIn = peekToken() == IN;
            pushBackToken(tok, varName);
 
-           ByteCode b = new ByteCode(curLine);
+           ForthBlock b = new ForthBlock(curLine, sourceName);
            if (forIn) {
                consume(NAME);
                consume(IN);
-               b.add(b.EXPR, parseMaximalExpr());
-               b.add(b.PUSHKEYS, NO_ARG);
+               b.add(b.EXPR, parseMaximalForthBlock());
+               b.add(b.PUSHKEYS);
                b.add(b.LITERAL, "length");
-               b.add(b.GET, NO_ARG);
+               b.add(b.GET);
                consume(RP);
-               ByteCode b2 = new ByteCode(curLine);
+               ForthBlock b2 = new ForthBlock(curLine, sourceName);
                b.add(b.SCOPE, b2);
                b2.add(b.LITERAL, new Integer(1));
-               b2.add(SUB, NO_ARG);
-               b2.add(b.DUP, NO_ARG);
+               b2.add(SUB);
+               b2.add(b.DUP);
                b2.add(b.LITERAL, new Integer(0));
-               b2.add(LT, NO_ARG);
+               b2.add(LT);
                b2.add(b.JT, new Integer(7));
-               b2.add(b.GET_PRESERVE, NO_ARG);
+               b2.add(b.GET_PRESERVE);
                b2.add(b.LITERAL, varName);
                b2.add(b.LITERAL, varName);
-               b2.add(b.DECLARE, NO_ARG);
-               b2.add(b.PUT, NO_ARG);
-               b2.add(b.EXPR, parseBlock(false));
+               b2.add(b.DECLARE);
+               b2.add(b.PUT);
+               b2.add(b.EXPR, parseStatement(false));
                b2.add(b.LITERAL, null);
                return b;
                
            } else {
-               ByteCode b2 = new ByteCode(curLine);
+               ForthBlock b2 = new ForthBlock(curLine, sourceName);
                b.add(b.SCOPE, b2);
-               b2.add(b.EXPR, parseMaximalExpr());
-               b2.add(b.POP, NO_ARG);
+
+               e1 = parseMaximalForthBlock();
+               if (e1 == null) e1 = new ForthBlock(curLine, sourceName, b.LITERAL, null);
+
+               b2.add(b.EXPR, e1);
+               b2.add(b.POP);
                consume(SEMI);
-               ByteCode b3 = new ByteCode(curLine);
+               e2 = parseMaximalForthBlock();
+               consume(SEMI);
+               e3 = parseMaximalForthBlock();
+               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.EXPR, parseMaximalExpr());
-               consume(SEMI);
+               b3.add(b.JT, new Integer(3));
+               b3.add(b.EXPR, e3);
+               b3.add(b.POP);
+               b3.add(b.EXPR, e2);
                b3.add(b.JT, new Integer(2));
-               b3.add(BREAK, NO_ARG);
-               b3.add(b.JT, new Integer(2));
-               b3.add(b.EXPR, parseMaximalExpr());
-               consume(RP);
-               parseBlock(false, b3);
+               b3.add(BREAK);
+               parseStatement(false, b3);
                return b;
            }
        }
@@ -571,433 +563,33 @@ public class Parser extends Lexer {
        }
     }
     
-    // Expr //////////////////////////////////////////////////////////////////////
-
-    public static final Object NO_ARG = new Object();
-
-    class ByteCode extends Expr {
-
-       // This class interprets a small forthlike bytecode language
-       // we use to represent JavaScript.  It's a stack machine.
-
-       // Each instruction is an opcode and a literal.  Any operation
-       // that accesses the top of the stack and does not have a
-       // mandatory literal can be performed on a literal instead of
-       // the top of the stack.
-
-       // opcodes:
-       public static final byte arithmetic = -1;  //                               -- arithmetic operators from parser
-       public static final byte LITERAL = -2;     // < String | Number | null >    -- push a literal onto the stack
-       public static final byte ARRAY = -3;       // < size >                      -- create a new array of size <size>
-       public static final byte OBJECT = -4;      //                               -- push an empty object onto the stack
-       public static final byte FUNCTION = -5;    // < bytecode_block >            -- push a new instance of a function with the given bytecode
-       public static final byte DECLARE = -6;     // < name >                      -- declare <name> in the current scope
-       public static final byte THIS = -7;        //                               -- push the topmost non-transparent scope onto the stack
-       public static final byte GET = -8;         //                               -- get stack[0] from stack[1]
-       public static final byte GET_PRESERVE = -80;         //                               -- get stack[0] from stack[1]
-       public static final byte PUT = -9;         //                               -- put stack[1] to key stack[0] on stack[2]; leaves object on the stack
-       public static final byte JT = -13;         // < relative_address >          -- pop the stack; if true, jump to <relative_address>
-       public static final byte JF = -21;         // < relative_address >          -- pop the stack; if false, jump to <relative_address>
-       public static final byte JMP = -22;        // < relative_address >          -- jump to <relative_address>
-        static public final byte POP = -14;        //                               -- discard the top element on the stack
-
-       public static final byte CALL = -15;       // < numargs >                   -- call stack[0] with the topmost <numargs> values as arguments
-
-       public static final byte PUSHKEYS = -19;    //                               -- ??
-       public static final byte EXPR = -20;       //                               -- transitional
-       public static final byte SWAP = -23;       //                               -- transitional
-       public static final byte SCOPE = -30;       //                               -- transitional
-       public static final byte LOOP = -40;       //                               -- transitional
-       public static final byte DUP = -50;       //                               -- transitional
-       public static final byte LABEL = -60;       //                               -- transitional
-
-       int[] op = new int[10];
-       Object[] arg = new Object[10];
-       int size = 0;
-
-       public ByteCode(int line) { super(line, "foo"); }
-       public ByteCode(int line, int op_, Object arg_) { this(line); add(op_, arg_); }
-
-       public int size() { return size; }
-       public void set(int pos, int op_, Object arg_) { op[pos] = op_; arg[pos] = arg_; }
-       public ByteCode 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;
-       }
-       
-       public Object eval(final JS.Scope s) throws ControlTransferException, JS.Exn {
-           return eval(s, new Parser.Thread());
-       }
-       public Object eval(final JS.Scope s, Parser.Thread 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(toNumber(arg[i]).intValue())); break;
-               case DECLARE: s.declare(arg[i] == NO_ARG ? (String)t.pop() : (String)arg[i]); break;
-               case THIS: t.push(s); break;   // FIXME: transparents
-               case JT: if (toBoolean(t.pop())) i += toNumber(arg[i]).intValue() - 1; break;
-               case JF: if (!toBoolean(t.pop())) i += toNumber(arg[i]).intValue() - 1; break;
-               case JMP: i += 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(((Expr)arg[i]).eval(s)); break;
-               case SCOPE: t.push(((ByteCode)arg[i]).eval(new JS.Scope(s), t)); break;
-
-               case ASSERT: if (!toBoolean(t.pop())) throw new EvaluatorException("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 Lexer.BITNOT: t.push(new Long(~toLong(t.pop()))); break;
-               case Lexer.BANG: t.push(new Boolean(!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: {
-                   ByteCode loop = (ByteCode)arg[i];
-                   Parser.Thread t2 = new Parser.Thread();
-                   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 Parser.Thread();
-                       t2.push(Boolean.FALSE);
-                   }
-                   break;
-               }
-
-               case PUT: {
-                   Object val = arg[i] == NO_ARG ? t.pop() : arg[i];
-                   Object key = t.pop();
-                   ((JS)t.peek()).put(key, val);
-                   t.push(val);
-                   break;
-               }
-
-               case GET: {
-                   Object v = arg[i] == NO_ARG ? t.pop() : arg[i];
-                   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 = 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("attempted to call null"));
-                   t.push(f.call(arguments));
-                   break;
-               }
-
-               case FUNCTION: {
-                   final ByteCode myBytes = (ByteCode)arg[i];
-                   t.push(new JS.Function() {
-                       public String toString() { return sourceName + ":" + line; }
-                       public String getSourceName() throws JS.Exn { return sourceName; }
-                       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);
-                                   }
-                               };
-                           Parser.Thread t0 = new Parser.Thread();
-                           t0.push(args);
-                           try {
-                               return myBytes.eval(scope, t0);
-                           } catch (ReturnException r) {
-                               return r.retval;
-                           } catch (ControlTransferException c) {
-                               throw new EvaluatorException("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 = toBoolean(arg[i]);
-                   Object key = t.pop();
-                   JS obj = (JS)t.pop();
-                   Number num = 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]) {
-
-                   case Lexer.BITOR: t.push(new Long(toLong(left) | toLong(right))); break;
-                   case Lexer.BITXOR: t.push(new Long(toLong(left) ^ toLong(right))); break;
-                   case Lexer.BITAND: t.push(new Long(toLong(left) & 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(toDouble(l) + toDouble(r))); break;
-                   }
-                       
-                   case Lexer.SUB: t.push(new Double(toDouble(left) - toDouble(right))); break;
-                   case Lexer.MUL: t.push(new Double(toDouble(left) * toDouble(right))); break;
-                   case Lexer.DIV: t.push(new Double(toDouble(left) / toDouble(right))); break;
-                   case Lexer.MOD: t.push(new Double(toDouble(left) % toDouble(right))); break;
-                       
-                   case Lexer.LSH: t.push(new Long(toLong(left) << toLong(right))); break;
-                   case Lexer.RSH: t.push(new Long(toLong(left) >> toLong(right))); break;
-                   case Lexer.URSH: t.push(new Long(toLong(left) >>> toLong(right))); break;
-                       
-                       // FIXME: these need to work on strings
-                   case Lexer.LT: t.push(toDouble(left) < toDouble(right) ? Boolean.TRUE : Boolean.FALSE); break;
-                   case Lexer.LE: t.push(toDouble(left) <= toDouble(right) ? Boolean.TRUE : Boolean.FALSE); break;
-                   case Lexer.GT: t.push(toDouble(left) > toDouble(right) ? Boolean.TRUE : Boolean.FALSE); break;
-                   case Lexer.GE: t.push(toDouble(left) >= 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(toBoolean(r)).equals(l);
-                       else if (l instanceof Number) ret = toNumber(r).doubleValue() == 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) {
-               throw new EvaluatorException("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("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(toNumber(args.elementAt(0)).intValue());
-                           else if (args.length() == 2) return ((String)o).substring(toNumber(args.elementAt(0)).intValue(),
-                                                                                     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(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;
-       }
-    }
-
-    class Thread {
-       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; }
-    }
-
-    class ExprList extends Expr {
-       Vec v = new Vec();
-       public ExprList(int curLine, int code) { super(curLine, code); }
-       public void add(Expr e) { v.addElement(e); }
-       public int numExprs() { return v.size(); }
-       public int size() { return v.size(); }
-       public Expr elementAt(int i) { return (Expr)v.elementAt(i); }
-       public Object eval(final JS.Scope s) throws ControlTransferException, JS.Exn {
-           switch(code) {
-           case LC: {
-               // Block
-               JS.Scope scope = new JS.Scope(s);
-               for(int i=0; i<v.size(); i++) ((Expr)v.elementAt(i)).eval(scope);
-               return null;
-           }
-           case Lexer.LB: {
-               // Array ctor
-               JS.Array ret = new JS.Array();
-               for(int i=0; i<numExprs(); i++) ret.addElement(elementAt(i).eval(s));
-               return ret;
-           }
-           case Lexer.RC: {
-               // Object ctor
-               JS.Obj ret = new JS.Obj();
-               for(int i=0; i<numExprs(); i++) {
-                   Expr e = elementAt(i);
-                   Object key = e.left.string;
-                   Object val = e.right.eval(s);
-                   ret.put(key, val);
-               }
-               return ret;
-           }
-           case Lexer.WHILE:
-           case Lexer.DO:
-               try {
-                   boolean first = true;
-                   Object bogus = null;
-                   ExprList list = this;
-                   Expr whileExpr = list.elementAt(0);
-                   Expr bodyExpr = list.elementAt(1);
-                   Expr incExpr = list.elementAt(2);
-                   for(;(first && code == Lexer.DO) || toBoolean(whileExpr.eval(s)); bogus = incExpr.eval(s)) {
-                       first = false;
-                       try {
-                           bodyExpr.eval(s);
-                       } catch (ContinueException c) {
-                           if (c.label == null || c.label.equals(string)) continue;
-                       } catch (BreakException b) {
-                           if (b.label == null || b.label.equals(string)) return null;
-                           throw (BreakException)b.fillInStackTrace();
-                       }
-                   }
-               } catch (BreakException e) {
-                   if (e.label != null && !e.label.equals(string)) throw e;
-               }
-               return null;
-           case Lexer.VAR:
-               for(int i=0; i<size(); i++) {
-                   Expr e = elementAt(i);
-                   s.declare(e.left.string);
-                   if (e.right != null) e.right.eval(s);
-               }
-               return null;
-           default: throw new Error("augh!");
-           }
-       }
-    }
-    
     /** a block is either a single statement or a list of statements surrounded by curly braces; all expressions are also statements */
-    public Expr parseBlock(boolean requireBraces) throws IOException { return parseBlock(requireBraces, null); }
-    public Expr parseBlock(boolean requireBraces, ByteCode b) throws IOException {
-       Expr smt = null;
+    public ForthBlock parseStatement(boolean requireBraces) throws IOException { return parseStatement(requireBraces, null); }
+    public ForthBlock parseStatement(boolean requireBraces, ForthBlock b) throws IOException {
+       ForthBlock smt = null;
        int tok = peekToken();
        if (tok == -1) return null;
        boolean braced = tok == LC;
        if (requireBraces && !braced) throw new ParserException("expected {, got " + codeToString[tok]);
        if (braced) consume(LC);
        int curLine = line;
-       ByteCode ret = new ByteCode(curLine);
-       ByteCode block = b == null ? new ByteCode(curLine) : b;
+       ForthBlock ret = new ForthBlock(curLine, sourceName);
+       ForthBlock block = b == null ? new ForthBlock(curLine, sourceName) : b;
        block.add(ret.LITERAL, Boolean.TRUE);
        ret.add(block.SCOPE, block);
        while(true) {
            switch(tok = peekToken()) {
 
-           case LC: smt = parseBlock(true); break;
+           case LC: smt = parseStatement(true); break;
            case GOTO: throw new ParserException("goto not supported");
 
            case THROW: case RETURN: case ASSERT: {
                getToken();
-               ByteCode r = new ByteCode(curLine);
+               ForthBlock r = new ForthBlock(curLine, sourceName);
                if (tok == RETURN && peekToken() == SEMI) r.add(b.LITERAL, null);
-               else r.add(b.EXPR, parseMaximalExpr());
+               else r.add(b.EXPR, parseMaximalForthBlock());
                consume(SEMI);
-               r.add(tok, NO_ARG);
+               r.add(tok);
                smt = r;
                break;
            }
@@ -1005,7 +597,7 @@ public class Parser extends Lexer {
            case BREAK: case CONTINUE: {
                getToken();
                if (peekToken() == NAME) consume(NAME);
-               smt = new ByteCode(curLine, tok, string);
+               smt = new ForthBlock(curLine, sourceName, tok, string);
                consume(SEMI);
                break;
            }
@@ -1024,7 +616,7 @@ public class Parser extends Lexer {
                consume(NAME);
                if (peekToken() == COLON) {
                    consume(COLON);
-                   smt = new ByteCode(curLine, ByteCode.LABEL, string);
+                   smt = new ForthBlock(curLine, sourceName, ForthBlock.LABEL, string);
                    break;
                } else {
                    pushBackToken(NAME, name);
@@ -1034,7 +626,7 @@ public class Parser extends Lexer {
 
            case -1:
            default:
-               smt = parseMaximalExpr();
+               smt = parseMaximalForthBlock();
                if (smt == null) return block.size() == 0 ? null : ret;
                if (peekToken() == SEMI) getToken();
                break;
@@ -1042,148 +634,13 @@ public class Parser extends Lexer {
 
            if (!braced) return smt;
            block.add(block.EXPR, smt);
-           block.add(block.POP, NO_ARG);
+           block.add(block.POP);
        }
     }
     
-
-    /** sorta like gcc trees */
-    class Expr {
-       int code = -1;
-    
-       final Expr left;
-       final Expr right;
-       int line = -1;
-       String sourceName = "unknown";
-    
-       String string = null;
-       Number number = null;
-    
-       public String toString() { return toString(0); }
-       public String toString(int indent) {
-           String ret = "";
-           for(int i=0; i<indent; i++) ret += " ";
-           ret += Lexer.codeToString[code];
-           if (code == Lexer.NUMBER) ret += " " + number;
-           else if (string != null) ret += " \"" + string + "\"";
-           ret += "\n";
-           if (left != null) ret += left.toString(indent + 2);
-           if (right != null) ret += right.toString(indent + 2);
-           return ret;
-       }
-    
-       public Expr(int line, String s) { this(line, Lexer.STRING); this.string = s; }  // an identifier or label
-       public Expr(int line, int code, String s) { this(line, code); this.string = s; }
-       public Expr(int line, Number n) { this(line, Lexer.NUMBER); this.number = n; }  // an identifier or label
-       public Expr(int line, int code) { this(line, code, null, null); }
-       public Expr(int line, int code, Expr left) { this(line, code, left, null); }
-       public Expr(int line, int code, Expr left, Expr right) {
-           this.code = code; this.left = left; this.right = right;
-           this.line = line;
-           this.sourceName = Parser.this.sourceName;
-       }
-
-       public double toDouble(Object o) { return toNumber(o).doubleValue(); }
-       public long toLong(Object o) { return toNumber(o).longValue(); }
-       public boolean toBoolean(Object o) {
-           if (o == null) return false;
-           if (o instanceof Boolean) return ((Boolean)o).booleanValue();
-           if (o instanceof Number) return o.equals(new Integer(0));
-           return true;
-       }
-
-       public Object eval(final JS.Scope s) throws ControlTransferException, JS.Exn {
-           switch(code) {
-
-           case Lexer.NULL: return null;
-           case Lexer.TYPEOF: {
-               Object o = left.eval(s);
-               if (o == null) return "null";
-               if (o.getClass() == String.class) return "string";
-               if (o.getClass() == Boolean.class) return "boolean";
-               if (o instanceof Number) return "number";
-               if (o instanceof JS.Array) return "array";
-               if (o instanceof JS) return "object";
-               throw new EvaluatorException("typeof " + o.getClass().getName() + " unknown");
-           }
-
-           case Lexer.TRY: {
-               boolean safeToExit = false;
-               try {
-                   Object ret = left.eval(s);
-                   safeToExit = true;
-                   return ret;
-               } catch (JS.Exn e) {
-                   ExprList list = (ExprList)right;
-                   Expr c = list.elementAt(0);
-                   if (c.code == Lexer.CATCH) {
-                       JS.Scope scope = new JS.Scope(s);
-                       s.put(c.left.string, e);
-                       c.right.eval(scope);
-                       c = list.elementAt(1);
-                   }
-                   if (c.code == Lexer.FINALLY) {
-                       JS.Scope scope = new JS.Scope(s);
-                       c.left.eval(scope);
-                   }
-               } finally {
-                   if (!safeToExit) Log.log(this, "WARNING: Java exception penetrated a JavaScript try{} block");
-               }
-               return null;
-           }
-
-           case Lexer.FOR:
-               Object[] keys = ((JS)left.right.eval(s)).keys();
-               for(int i=0; i<keys.length; i++) {
-                   JS.Scope scope = new JS.Scope(s);
-                   scope.declare(left.string);
-                   scope.put(left.string, keys[i]);
-                   try {
-                       right.eval(scope);
-                   } catch (ContinueException c) {
-                       if (c.label == null || c.label.equals(string)) continue;
-                   } catch (BreakException b) {
-                       if (b.label == null || b.label.equals(string)) return null;
-                       throw (BreakException)b.fillInStackTrace();
-                   }
-               }
-               return null;
-
-           default: throw new EvaluatorException("don't know how to eval an Expr with code " + Lexer.codeToString[code] + "\n" + this);
-           }
-       }
-
-       class EvaluatorException extends RuntimeException {
-           public EvaluatorException(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; }
-       }
-       static class ContinueException extends ControlTransferException {
-           public String label;
-           ContinueException(String label) { this.label = label; }
-       }
-       static class ReturnException extends ControlTransferException {
-           public Object retval;
-           ReturnException(Object retval) { this.retval = retval; }
-       }
-
     class ParserException extends RuntimeException {
        public ParserException(String s) { super(sourceName + ":" + line + " " + s); }
     }
-
-       public static Number toNumber(Object o) {
-           if (o == null) return new Long(0);
-           if (o instanceof Number) return ((Number)o);
-           if (o instanceof String) try { return new Double((String)o); } catch (NumberFormatException e) { return new Double(0); }
-           if (o instanceof Boolean) return ((Boolean)o).booleanValue() ? new Long(1) : new Long(0);
-           if (o instanceof JS) return ((JS)o).coerceToNumber();
-           // FIXME
-           throw new Error("toNumber() got object of type " + o.getClass().getName());
-       }
- }
+    
+}
 
diff --git a/src/org/xwt/js/Tokens.java b/src/org/xwt/js/Tokens.java
new file mode 100644 (file)
index 0000000..3f9065c
--- /dev/null
@@ -0,0 +1,111 @@
+// Copyright 2002 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.xwt.js;
+
+public interface Tokens {
+    // Token Constants //////////////////////////////////////////////////////////
+
+    public final static int
+        EOL          = 1,   // end of line
+        RETURN       = 2,   // return
+        GOTO         = 3,   // goto
+        BITOR        = 4,   // |
+        ASSIGN_BITOR = 5,   // |=
+        BITXOR       = 6,   // ^
+        ASSIGN_BITXOR= 7,   // ^=
+        BITAND       = 8,   // &
+        ASSIGN_BITAND= 9,   // &=
+        EQ           = 10,  // ==
+        NE           = 11,  // !=
+        LT           = 12,  // <
+        LE           = 13,  // <=
+        GT           = 14,  // >
+        GE           = 15,  // >=
+        LSH          = 16,  // <<
+        ASSIGN_LSH   = 17, // <<=
+        RSH          = 18,  // >>
+        ASSIGN_RSH   = 19, // >>=
+        URSH         = 20,  // >>>
+        ASSIGN_URSH  = 21, // >>>=
+        ADD          = 22,  // +
+        ASSIGN_ADD   = 23, // +=
+        SUB          = 24,  // -
+        ASSIGN_SUB   = 25, // -=
+        MUL          = 26,  // *
+        ASSIGN_MUL   = 27, // *=
+        DIV          = 28,  // /
+        ASSIGN_DIV   = 29, // /=
+        MOD          = 30,  // %
+        ASSIGN_MOD   = 31, // %=
+        BITNOT       = 32,  // ~
+        ASSIGN_BITNOT= 33, // ~=
+        DELPROP      = 34,  // delete
+        TYPEOF       = 35,  // typeof
+        NAME         = 36,  // *** identifiers ***
+        NUMBER       = 37,  // *** numeric literals ***
+        STRING       = 38,  // *** string literals ***
+        NULL         = 39,  // null
+        THIS         = 40,  // this
+        FALSE        = 41,  // false
+        TRUE         = 42,  // true
+        SHEQ         = 43,  // ===
+        SHNE         = 44,  // !==
+        THROW        = 45,  // throw
+        IN           = 46,  // in
+        INSTANCEOF   = 47,  // instanceof
+        TRY          = 48,  // try
+        SEMI         = 49,  // ;
+        LB           = 50,  // [
+        RB           = 51,  // ]
+        LC           = 52,  // {
+        RC           = 53,  // }
+        LP           = 54,  // (
+        RP           = 55,  // )
+        COMMA        = 56,  // ,
+        ASSIGN       = 57,  // =
+        HOOK         = 58,  // ?
+        COLON        = 59,  // :
+        OR           = 60, // ||
+        AND          = 61, // &&
+        INC          = 62, // ++
+        DEC          = 63, // --
+        DOT          = 64, // .
+        FUNCTION     = 65, // function
+       IF           = 66,  // if keyword
+        ELSE         = 67, // else keyword
+        SWITCH       = 68, // switch keyword
+        CASE         = 69, // case keyword
+        DEFAULT      = 70, // default keyword
+        WHILE        = 71, // while keyword
+        DO           = 72, // do keyword
+        FOR          = 73, // for keyword
+        BREAK        = 74, // break keyword
+        CONTINUE     = 75, // continue keyword
+        VAR          = 76, // var keyword
+        WITH         = 77, // with keyword
+        CATCH        = 78, // catch keyword
+        FINALLY      = 79, // finally keyword
+        RESERVED     = 80, // reserved keywords
+        NOP          = 81, // NOP
+        VOID         = 82, // void keyword
+        MOD_ASSIGN   = 83, // %=
+        BANG         = 84, // %=
+        ASSERT       = 85; // assert keyword
+
+    public static final int MAX_TOKEN = ASSERT;
+
+    public final static String[] codeToString = new String[] {
+       "0", "EOL", "RETURN", "GOTO", "BITOR", "ASSIGN_BITOR",
+       "BITXOR", "ASSIGN_BITXOR", "BITAND", "ASSIGN_BITAND", "EQ",
+       "NE", "LT", "LE", "GT", "GE", "LSH", "ASSIGN_LSH", "RSH",
+       "ASSIGN_RSH", "URSH", "ASSIGN_URSH", "ADD", "ASSIGN_ADD",
+       "SUB", "ASSIGN_SUB", "MUL", "ASSIGN_MUL", "DIV", "ASSIGN_DIV",
+       "MOD", "ASSIGN_MOD", "BITNOT", "ASSIGN_BITNOT=", "DELPROP",
+       "TYPEOF", "NAME", "NUMBER", "STRING", "NULL", "THIS", "FALSE",
+       "TRUE", "SHEQ", "SHNE", "THROW", "IN", "INSTANCEOF", "TRY",
+       "SEMI", "LB", "RB", "LC", "RC", "LP", "RP", "COMMA", "ASSIGN",
+       "HOOK", "COLON", "OR", "AND", "INC", "DEC", "DOT", "FUNCTION",
+       "IF", "ELSE", "SWITCH", "CASE", "DEFAULT", "WHILE", "DO",
+       "FOR", "BREAK", "CONTINUE", "VAR", "WITH", "CATCH", "FINALLY",
+       "RESERVED", "NOP", "VOID", "MOD_ASSIGN", "BANG", "ASSERT" };
+
+}