2003/06/07 11:04:13
authormegacz <megacz@xwt.org>
Fri, 30 Jan 2004 07:00:51 +0000 (07:00 +0000)
committermegacz <megacz@xwt.org>
Fri, 30 Jan 2004 07:00:51 +0000 (07:00 +0000)
darcs-hash:20040130070051-2ba56-2190a602c61da80782b142e03cc11e7157b05263.gz

src/org/xwt/js/ForthBlock.java
src/org/xwt/js/Parser.java

index 0cc4ed2..4741d76 100644 (file)
@@ -18,309 +18,310 @@ class ForthBlock implements OpCodes, Tokens {
 
     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; }
     }
     
 }
index cc55164..f7eb500 100644 (file)
@@ -67,74 +67,83 @@ public class Parser extends Lexer implements OpCodes {
 
     /** 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: {
@@ -143,7 +152,7 @@ public class Parser extends Lexer implements OpCodes {
                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);
@@ -152,7 +161,8 @@ public class Parser extends Lexer implements OpCodes {
                b.add(ForthBlock.LITERAL, name);
                b.add(ForthBlock.GET);
            }
-           return continueExpr(b, minPrecedence);
+           continueExpr(b, minPrecedence);
+           return;
        }
        case Tokens.FUNCTION: {
            consume(LP);
@@ -206,92 +216,87 @@ public class Parser extends Lexer implements OpCodes {
            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);
@@ -299,37 +304,41 @@ public class Parser extends Lexer implements OpCodes {
                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; }
        }
     }
     
@@ -358,7 +367,7 @@ public class Parser extends Lexer implements OpCodes {
            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;
@@ -388,7 +397,7 @@ public class Parser extends Lexer implements OpCodes {
                    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);
                    }
@@ -403,19 +412,21 @@ public class Parser extends Lexer implements OpCodes {
            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;
            }
@@ -427,7 +438,7 @@ public class Parser extends Lexer implements OpCodes {
                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);
@@ -443,33 +454,39 @@ public class Parser extends Lexer implements OpCodes {
                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;
            }
                
@@ -481,7 +498,7 @@ public class Parser extends Lexer implements OpCodes {
                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);
@@ -524,7 +541,7 @@ public class Parser extends Lexer implements OpCodes {
            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);
@@ -543,7 +560,6 @@ public class Parser extends Lexer implements OpCodes {
                b2.add(b.DECLARE);
                b2.add(b.PUT);
                b2.add(b.EXPR, parseStatement());
-               //b2.add(b.LITERAL, null);
                break;
                
            } else {
@@ -551,26 +567,26 @@ public class Parser extends Lexer implements OpCodes {
                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);
@@ -597,9 +613,9 @@ public class Parser extends Lexer implements OpCodes {
                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;