local scope vars indexed by number
authorbrian <brian@brianweb.net>
Tue, 13 Jul 2004 04:28:31 +0000 (04:28 +0000)
committerbrian <brian@brianweb.net>
Tue, 13 Jul 2004 04:28:31 +0000 (04:28 +0000)
darcs-hash:20040713042831-24bed-3c9fc5e4a850d016160df3578433df0c340586c2.gz

src/org/ibex/js/ByteCodes.java
src/org/ibex/js/Interpreter.java
src/org/ibex/js/JS.java
src/org/ibex/js/JSArray.java
src/org/ibex/js/JSFunction.java
src/org/ibex/js/JSRegexp.java
src/org/ibex/js/JSScope.java
src/org/ibex/js/Lexer.java
src/org/ibex/js/Parser.java
src/org/ibex/js/Test.java
src/org/ibex/js/Tokens.java

index e8170f1..84c5926 100644 (file)
@@ -24,10 +24,11 @@ interface ByteCodes {
     /** if given a non-null argument declare its argument in the current scope and push
         it to the stack, else, declares the element on the top of the stack and leaves it
         there */
     /** if given a non-null argument declare its argument in the current scope and push
         it to the stack, else, declares the element on the top of the stack and leaves it
         there */
-    public static final byte DECLARE = -6;       
+    //public static final byte DECLARE = -6;       
 
     /** push a reference to the current scope onto the stack */
 
     /** push a reference to the current scope onto the stack */
-    public static final byte TOPSCOPE = -7;
+    // FIXME: Document this
+    public static final byte GLOBALSCOPE = -7;
 
     /** if given a null literal pop two elements off the stack; push stack[-1].get(stack[top])
         else pop one element off the stack, push stack[top].get(literal) */
 
     /** if given a null literal pop two elements off the stack; push stack[-1].get(stack[top])
         else pop one element off the stack, push stack[top].get(literal) */
@@ -85,10 +86,14 @@ interface ByteCodes {
     /** finish a finally block and carry out whatever instruction initiated the finally block */
     public static final byte MAKE_GRAMMAR = -25;
     
     /** finish a finally block and carry out whatever instruction initiated the finally block */
     public static final byte MAKE_GRAMMAR = -25;
     
+    // FIXME: Document these and NEWSCOPE/OLDSCOPE/TOPSCOPE changes
+    public static final byte SCOPEGET = -26;
+    public static final byte SCOPEPUT = -27;    
+    
     public static final String[] bytecodeToString = new String[] {
     public static final String[] bytecodeToString = new String[] {
-        "", "", "LITERAL", "ARRAY", "OBJECT", "NEWFUNCTION", "DECLARE", "TOPSCOPE",
+        "", "", "LITERAL", "ARRAY", "OBJECT", "NEWFUNCTION", "DECLARE", "GLOBALSCOPE",
         "GET", "GET_PRESERVE", "PUT", "JT", "JF", "JMP", "POP", "CALL", "PUSHKEYS",
         "SWAP", "NEWSCOPE", "OLDSCOPE", "DUP", "LABEL", "LOOP", "CALLMETHOD",
         "GET", "GET_PRESERVE", "PUT", "JT", "JF", "JMP", "POP", "CALL", "PUSHKEYS",
         "SWAP", "NEWSCOPE", "OLDSCOPE", "DUP", "LABEL", "LOOP", "CALLMETHOD",
-        "FINALLY_DONE", "MAKE_GRAMMAR"
+        "FINALLY_DONE", "MAKE_GRAMMAR", "SCOPEGET", "SCOPEPUT"
     };
 }
     };
 }
index 83ff539..04f6784 100644 (file)
@@ -6,8 +6,6 @@ import java.util.*;
 
 /** Encapsulates a single JS interpreter (ie call stack) */
 class Interpreter implements ByteCodes, Tokens {
 
 /** Encapsulates a single JS interpreter (ie call stack) */
 class Interpreter implements ByteCodes, Tokens {
-    private static final JS CASCADE = JSString.intern("cascade");
-    
     // Thread-Interpreter Mapping /////////////////////////////////////////////////////////////////////////
 
     static Interpreter current() { return (Interpreter)threadToInterpreter.get(Thread.currentThread()); }
     // Thread-Interpreter Mapping /////////////////////////////////////////////////////////////////////////
 
     static Interpreter current() { return (Interpreter)threadToInterpreter.get(Thread.currentThread()); }
@@ -25,7 +23,7 @@ class Interpreter implements ByteCodes, Tokens {
     Interpreter(JSFunction f, boolean pauseable, JSArgs args) {
         this.f = f;
         this.pausecount = pauseable ? 0 : -1;
     Interpreter(JSFunction f, boolean pauseable, JSArgs args) {
         this.f = f;
         this.pausecount = pauseable ? 0 : -1;
-        this.scope = new JSScope(f.parentScope);
+        this.scope = f.parentScope;
         try {
             stack.push(new CallMarker(null));    // the "root function returned" marker -- f==null
             stack.push(args);
         try {
             stack.push(new CallMarker(null));    // the "root function returned" marker -- f==null
             stack.push(args);
@@ -46,6 +44,7 @@ class Interpreter implements ByteCodes, Tokens {
     /** this is the only synchronization point we need in order to be threadsafe */
     synchronized JS resume() throws JSExn {
         if(f == null) throw new RuntimeException("function already finished");
     /** this is the only synchronization point we need in order to be threadsafe */
     synchronized JS resume() throws JSExn {
         if(f == null) throw new RuntimeException("function already finished");
+        if(scope == null) throw new RuntimeException("scope is null");
         Thread t = Thread.currentThread();
         Interpreter old = (Interpreter)threadToInterpreter.get(t);
         threadToInterpreter.put(t, this);
         Thread t = Thread.currentThread();
         Interpreter old = (Interpreter)threadToInterpreter.get(t);
         threadToInterpreter.put(t, this);
@@ -89,16 +88,22 @@ class Interpreter implements ByteCodes, Tokens {
             case LITERAL: stack.push((JS)arg); break;
             case OBJECT: stack.push(new JS.O()); break;
             case ARRAY: stack.push(new JSArray(JS.toInt((JS)arg))); break;
             case LITERAL: stack.push((JS)arg); break;
             case OBJECT: stack.push(new JS.O()); break;
             case ARRAY: stack.push(new JSArray(JS.toInt((JS)arg))); break;
-            case DECLARE: scope.declare((JS)(arg==null ? stack.peek() : arg)); if(arg != null) stack.push((JS)arg); break;
-            case TOPSCOPE: stack.push(scope); break;
+            //case DECLARE: scope.declare((JS)(arg==null ? stack.peek() : arg)); if(arg != null) stack.push((JS)arg); break;
             case JT: if (JS.toBoolean(stack.pop())) pc += JS.toInt((JS)arg) - 1; break;
             case JF: if (!JS.toBoolean(stack.pop())) pc += JS.toInt((JS)arg) - 1; break;
             case JMP: pc += JS.toInt((JS)arg) - 1; break;
             case POP: stack.pop(); break;
             case SWAP: stack.swap(); break;
             case DUP: stack.push(stack.peek()); break;
             case JT: if (JS.toBoolean(stack.pop())) pc += JS.toInt((JS)arg) - 1; break;
             case JF: if (!JS.toBoolean(stack.pop())) pc += JS.toInt((JS)arg) - 1; break;
             case JMP: pc += JS.toInt((JS)arg) - 1; break;
             case POP: stack.pop(); break;
             case SWAP: stack.swap(); break;
             case DUP: stack.push(stack.peek()); break;
-            case NEWSCOPE: scope = new JSScope(scope); break;
-            case OLDSCOPE: scope = scope.getParentScope(); break;
+            case NEWSCOPE: {
+                int n = JS.toInt((JS)arg);
+                scope = new JSScope(scope,(n>>>16)&0xffff,(n>>>0)&0xffff);
+                break;
+            }
+            case OLDSCOPE: scope = scope.parent; break;
+            case GLOBALSCOPE: stack.push(scope.getGlobal()); break;
+            case SCOPEGET: stack.push(scope.get((JS)arg)); break;
+            case SCOPEPUT: scope.put((JS)arg,stack.peek()); break;
             case ASSERT: if (!JS.toBoolean(stack.pop())) throw je("ibex.assertion.failed"); break;
             case BITNOT: stack.push(JS.N(~JS.toLong(stack.pop()))); break;
             case BANG: stack.push(JS.B(!JS.toBoolean(stack.pop()))); break;
             case ASSERT: if (!JS.toBoolean(stack.pop())) throw je("ibex.assertion.failed"); break;
             case BITNOT: stack.push(JS.N(~JS.toLong(stack.pop()))); break;
             case BANG: stack.push(JS.B(!JS.toBoolean(stack.pop()))); break;
@@ -173,7 +178,7 @@ class Interpreter implements ByteCodes, Tokens {
                         continue OUTER;
                     } else if (o instanceof CallMarker) {
                         boolean didTrapPut = false;
                         continue OUTER;
                     } else if (o instanceof CallMarker) {
                         boolean didTrapPut = false;
-                        if (o instanceof TrapMarker) { // handles return component of a read trap
+                        if (o instanceof TrapMarker) { // handles return component of a write trap
                             TrapMarker tm = (TrapMarker) o;
                             boolean cascade = tm.t.isWriteTrap() && !tm.cascadeHappened && !JS.toBoolean(retval);
                             if(cascade) {
                             TrapMarker tm = (TrapMarker) o;
                             boolean cascade = tm.t.isWriteTrap() && !tm.cascadeHappened && !JS.toBoolean(retval);
                             if(cascade) {
@@ -209,7 +214,44 @@ class Interpreter implements ByteCodes, Tokens {
                 }
                 throw new Error("error: RETURN invoked but couldn't find a CallMarker!");
             }
                 }
                 throw new Error("error: RETURN invoked but couldn't find a CallMarker!");
             }
-
+                
+            case CASCADE: {
+                boolean write = JS.toBoolean((JS)arg);
+                JS val = write ? stack.pop() : null;
+                CallMarker o = stack.findCall();
+                if(!(o instanceof TrapMarker)) throw new JSExn("tried to CASCADE while not in a trap");
+                TrapMarker tm = (TrapMarker) o;
+                JS key = tm.t.key;
+                JS target = tm.t.target;
+                if(tm.t.isWriteTrap() != write) throw new JSExn("tried to do a " + (write?"write":"read") + " cascade in a " + (write?"read":"write") + " trap");
+                Trap t = write ? tm.t.nextWriteTrap() : tm.t.nextReadTrap();
+                // FIXME: Doesn't handle multiple levels of clone's (probably can just make this a while loop)
+                if(t == null && target instanceof JS.Clone) {
+                    target = ((JS.Clone)target).clonee;
+                    t = target.getTrap(key);
+                    if(t != null) t = write ? t.writeTrap() : t.readTrap();
+                }
+                if(write) {
+                    tm.cascadeHappened = true;
+                    stack.push(val);
+                }
+                if(t != null) {
+                    setupTrap(t,val,new TrapMarker(this,t,val,tm.pauseOnPut));
+                    pc--; // we increment later
+                } else {
+                    if(write) {
+                        if (tm.pauseOnPut) { pc++; return val; }
+                        target.put(key,val);
+                    } else {
+                        JS ret = target.get(key);
+                        if (ret == JS.METHOD) ret = new Stub(target, key);
+                        stack.push(ret);                        
+                    }
+                    if (pausecount > initialPauseCount) { pc++; return null; }   // we were paused                    
+                }
+                break;
+            }
+                
             case PUT: {
                 JS val = stack.pop();
                 JS key = stack.pop();
             case PUT: {
                 JS val = stack.pop();
                 JS key = stack.pop();
@@ -217,24 +259,9 @@ class Interpreter implements ByteCodes, Tokens {
                 if (target == null) throw je("tried to put " + JS.debugToString(val) + " to the " + JS.debugToString(key) + " property on the null value");
                 if (key == null) throw je("tried to assign \"" + JS.debugToString(val) + "\" to the null key");
                 
                 if (target == null) throw je("tried to put " + JS.debugToString(val) + " to the " + JS.debugToString(key) + " property on the null value");
                 if (key == null) throw je("tried to assign \"" + JS.debugToString(val) + "\" to the null key");
                 
-                Trap t = null;
-                TrapMarker tm = null;
-                if(target instanceof JSScope && key.jsequals(CASCADE)) {
-                    CallMarker o = stack.findCall();
-                    if(o instanceof TrapMarker) {
-                        tm = (TrapMarker) o;
-                        target = tm.t.target;
-                        key = tm.t.key;
-                        tm.cascadeHappened = true;
-                        t = tm.t;
-                        if(t.isReadTrap()) throw new JSExn("can't do a write cascade in a read trap");
-                        t = t.nextWriteTrap();
-                    }
-                }
-                if(tm == null) { // not cascading
-                    t = target instanceof JSScope ? t = ((JSScope)target).top().getTrap(key) : target.getTrap(key);
-                    if(t != null) t = t.writeTrap();
-                }
+                Trap t = target.getTrap(key);
+                if(t != null) t = t.writeTrap();
+                
                 if(t == null && target instanceof JS.Clone) {
                     target = ((JS.Clone)target).clonee;
                     t = target.getTrap(key);
                 if(t == null && target instanceof JS.Clone) {
                     target = ((JS.Clone)target).clonee;
                     t = target.getTrap(key);
@@ -244,15 +271,13 @@ class Interpreter implements ByteCodes, Tokens {
                 stack.push(val);
                 
                 if(t != null) {
                 stack.push(val);
                 
                 if(t != null) {
-                    setupTrap(t,val,new TrapMarker(this,t,val, tm != null && tm.pauseOnPut));
+                    setupTrap(t,val,new TrapMarker(this,t,val));
                     pc--; // we increment later
                     pc--; // we increment later
-                    break;
                 } else {
                 } else {
-                    if (tm != null && tm.pauseOnPut) { pc++; return val; }
                     target.put(key,val);
                     if (pausecount > initialPauseCount) { pc++; return null; }   // we were paused
                     target.put(key,val);
                     if (pausecount > initialPauseCount) { pc++; return null; }   // we were paused
-                    break;
                 }
                 }
+                break;
             }
 
             case GET:
             }
 
             case GET:
@@ -267,26 +292,12 @@ class Interpreter implements ByteCodes, Tokens {
                     stack.push(key);
                 }
                 JS ret = null;
                     stack.push(key);
                 }
                 JS ret = null;
-                if (key == null) throw je("tried to get the null key from " + target);
-                if (target == null) throw je("tried to get property \"" + key + "\" from the null object");
+                if (key == null) throw je("tried to get the null key from " + JS.debugToString(target));
+                if (target == null) throw je("tried to get property \"" + JS.debugToString(key) + "\" from the null object");
+                
+                Trap t = target.getTrap(key);
+                if(t != null) t = t.readTrap();
                 
                 
-                Trap t = null;
-                TrapMarker tm = null;
-                if(target instanceof JSScope && key.jsequals(CASCADE)) {
-                    CallMarker o = stack.findCall();
-                    if(o instanceof TrapMarker) {
-                        tm = (TrapMarker) o;
-                        target = tm.t.target;
-                        key = tm.t.key;
-                        t = tm.t;
-                        if(t.isWriteTrap()) throw new JSExn("can't do a read cascade in a write trap");
-                        t = t.nextReadTrap();
-                    }
-                }
-                if(tm == null) { // not cascading
-                    t = target instanceof JSScope ? t = ((JSScope)target).top().getTrap(key) : ((JS)target).getTrap(key);
-                    if(t != null) t = t.readTrap();
-                }
                 if(t == null && target instanceof JS.Clone) {
                     target = ((JS.Clone)target).clonee;
                     t = target.getTrap(key);
                 if(t == null && target instanceof JS.Clone) {
                     target = ((JS.Clone)target).clonee;
                     t = target.getTrap(key);
@@ -296,14 +307,13 @@ class Interpreter implements ByteCodes, Tokens {
                 if(t != null) {
                     setupTrap(t,null,new TrapMarker(this,t,null));
                     pc--; // we increment later
                 if(t != null) {
                     setupTrap(t,null,new TrapMarker(this,t,null));
                     pc--; // we increment later
-                    break;
                 } else {
                     ret = target.get(key);
                     if (pausecount > initialPauseCount) { pc++; return null; }   // we were paused
                     if (ret == JS.METHOD) ret = new Stub(target, key);
                     stack.push(ret);
                 } else {
                     ret = target.get(key);
                     if (pausecount > initialPauseCount) { pc++; return null; }   // we were paused
                     if (ret == JS.METHOD) ret = new Stub(target, key);
                     stack.push(ret);
-                    break;
                 }
                 }
+                break;
             }
             
             case CALL: case CALLMETHOD: {
             }
             
             case CALL: case CALLMETHOD: {
@@ -337,7 +347,7 @@ class Interpreter implements ByteCodes, Tokens {
                     stack.push(new CallMarker(this));
                     stack.push(new JSArgs(a0,a1,a2,rest,numArgs,object));
                     f = (JSFunction)object;
                     stack.push(new CallMarker(this));
                     stack.push(new JSArgs(a0,a1,a2,rest,numArgs,object));
                     f = (JSFunction)object;
-                    scope = new JSScope(f.parentScope);
+                    scope = f.parentScope;
                     pc = -1;
                     break;
                 } else {
                     pc = -1;
                     break;
                 } else {
@@ -382,14 +392,6 @@ class Interpreter implements ByteCodes, Tokens {
                 JS js = stack.peek();
                 // A trap addition/removal
                 if(!(val instanceof JSFunction)) throw new JSExn("tried to add/remove a non-function trap");
                 JS js = stack.peek();
                 // A trap addition/removal
                 if(!(val instanceof JSFunction)) throw new JSExn("tried to add/remove a non-function trap");
-                if(js instanceof JSScope) {
-                    JSScope s = (JSScope) js;
-                    while(s.getParentScope() != null) {
-                        if(s.has(key)) throw new JSExn("cannot trap a variable that isn't at the top level scope");
-                        s = s.getParentScope();
-                    }
-                    js = s;
-                }
                 if(op == ADD_TRAP) js.addTrap(key, (JSFunction)val);
                 else js.delTrap(key, (JSFunction)val);
                 break;
                 if(op == ADD_TRAP) js.addTrap(key, (JSFunction)val);
                 else js.delTrap(key, (JSFunction)val);
                 break;
@@ -528,9 +530,9 @@ class Interpreter implements ByteCodes, Tokens {
 
     void setupTrap(Trap t, JS val, CallMarker cm) throws JSExn {
         stack.push(cm);
 
     void setupTrap(Trap t, JS val, CallMarker cm) throws JSExn {
         stack.push(cm);
-        stack.push(t.isWriteTrap() ? new JSArgs(val,t.f) : new JSArgs(t.f));
+        stack.push(new TrapArgs(t,val));
         f = t.f;
         f = t.f;
-        scope = new TrapScope(t.f.parentScope,t);
+        scope = f.parentScope;
         pc = 0;
     }
 
         pc = 0;
     }
 
@@ -603,16 +605,17 @@ class Interpreter implements ByteCodes, Tokens {
         public FinallyData(JSExn exn) { this.exn = exn; this.op = -1; this.arg = null; } // Just throw this exn
     }
 
         public FinallyData(JSExn exn) { this.exn = exn; this.op = -1; this.arg = null; } // Just throw this exn
     }
 
-    static class TrapScope extends JSScope {
+    static class TrapArgs extends JS {
         private Trap t;
         private Trap t;
-        public TrapScope(JSScope parent, Trap t) {
-            super(parent); this.t = t;
-        }
+        private JS val;
+        public TrapArgs(Trap t, JS val) { this.t = t; this.val = val; }
         public JS get(JS key) throws JSExn {
         public JS get(JS key) throws JSExn {
+            if(JS.isInt(key) && JS.toInt(key) == 0) return val;
             //#jswitch(key)
             case "trapee": return t.target;
             case "callee": return t.f;
             case "trapname": return t.key;
             //#jswitch(key)
             case "trapee": return t.target;
             case "callee": return t.f;
             case "trapname": return t.key;
+            case "length": return t.isWriteTrap() ? ONE : ZERO;
             //#end
             return super.get(key);
         }
             //#end
             return super.get(key);
         }
index 2d5c9aa..d602da3 100644 (file)
@@ -8,43 +8,35 @@ import java.util.*;
 /** The minimum set of functionality required for objects which are manipulated by JavaScript */
 public abstract class JS { 
     public static final JS METHOD = new JS() { };
 /** The minimum set of functionality required for objects which are manipulated by JavaScript */
 public abstract class JS { 
     public static final JS METHOD = new JS() { };
-    public final JS unclone() { return _unclone(); }
-    public final JS jsclone() throws JSExn { return new Clone(this); }
 
     public JS.Enumeration keys() throws JSExn { throw new JSExn("you can't enumerate the keys of this object (class=" + getClass().getName() +")"); }
     public JS get(JS key) throws JSExn { return null; }
     public void put(JS key, JS val) throws JSExn { throw new JSExn("" + key + " is read only (class=" + getClass().getName() +")"); }
     
 
     public JS.Enumeration keys() throws JSExn { throw new JSExn("you can't enumerate the keys of this object (class=" + getClass().getName() +")"); }
     public JS get(JS key) throws JSExn { return null; }
     public void put(JS key, JS val) throws JSExn { throw new JSExn("" + key + " is read only (class=" + getClass().getName() +")"); }
     
-    public InputStream getInputStream() throws IOException {
-        throw new IOException("this object doesn't have a stream associated with it " + getClass().getName() + ")");
-    }
-        
-    public final boolean hasTrap(JS key) { return getTrap(key) != null; }
-    
     public JS callMethod(JS method, JS a0, JS a1, JS a2, JS[] rest, int nargs) throws JSExn {
         throw new JSExn("method not found (" + JS.debugToString(method) + ")");
     }
     public JS callMethod(JS method, JS a0, JS a1, JS a2, JS[] rest, int nargs) throws JSExn {
         throw new JSExn("method not found (" + JS.debugToString(method) + ")");
     }
-    
     public JS call(JS a0, JS a1, JS a2, JS[] rest, int nargs) throws JSExn {
         throw new JSExn("you cannot call this object (class=" + this.getClass().getName() +")");
     }
     public JS call(JS a0, JS a1, JS a2, JS[] rest, int nargs) throws JSExn {
         throw new JSExn("you cannot call this object (class=" + this.getClass().getName() +")");
     }
+    public InputStream getInputStream() throws IOException {
+        throw new IOException("this object doesn't have a stream associated with it " + getClass().getName() + ")");
+    }
     
     
+    public final JS unclone() { return _unclone(); }
+    public final JS jsclone() throws JSExn { return new Clone(this); }
+    public final boolean hasTrap(JS key) { return getTrap(key) != null; }
     public final boolean equals(Object o) { return this == o || ((o instanceof JS) && jsequals((JS)o)); }
     public final boolean equals(Object o) { return this == o || ((o instanceof JS) && jsequals((JS)o)); }
+    // Discourage people from using toString()
+    public final String toString() { return "JS Object [class=" + getClass().getName() + "]"; }
     
     
-    public final String toString() {
-        // Discourage people from using toString()
-        String s = "JS Object [class=" + getClass().getName() + "]";
-        String ext = extendedToString();
-        return ext == null ? s : s + " " + ext;
-    }
-    
-    // These are only used internally by org.ibex.js
+    // Package private methods
     Trap getTrap(JS key) { return null; }
     void putTrap(JS key, Trap value) throws JSExn { throw new JSExn("traps cannot be placed on this object (class=" + this.getClass().getName() +")"); }
     String coerceToString() throws JSExn { throw new JSExn("can't coerce to a string (class=" + getClass().getName() +")"); }
     boolean jsequals(JS o) { return this == o; }
     Trap getTrap(JS key) { return null; }
     void putTrap(JS key, Trap value) throws JSExn { throw new JSExn("traps cannot be placed on this object (class=" + this.getClass().getName() +")"); }
     String coerceToString() throws JSExn { throw new JSExn("can't coerce to a string (class=" + getClass().getName() +")"); }
     boolean jsequals(JS o) { return this == o; }
-    String extendedToString() { return null; }
-    
+    JS _unclone() { return this; }
+        
     public static class O extends JS implements Cloneable {
         private Hash entries;
         
     public static class O extends JS implements Cloneable {
         private Hash entries;
         
@@ -64,29 +56,29 @@ public abstract class JS {
         }    
     }
     
         }    
     }
     
-    JS _unclone() { return this; }
-    
     public interface Cloneable { }
     public interface Cloneable { }
-    
-    public static class Clone extends JS.O {
+        
+    public static class Clone extends O {
         protected final JS clonee;
         protected final JS clonee;
-        JS _unclone() { return clonee.unclone(); }
-        public JS getClonee() { return clonee; }
         public Clone(JS clonee) throws JSExn {
             if(!(clonee instanceof Cloneable)) throw new JSExn("" + clonee.getClass().getName() + " isn't cloneable");
             this.clonee = clonee;
         }
         public Clone(JS clonee) throws JSExn {
             if(!(clonee instanceof Cloneable)) throw new JSExn("" + clonee.getClass().getName() + " isn't cloneable");
             this.clonee = clonee;
         }
-        public boolean jsequals(JS o) { return unclone().jsequals(o.unclone()); }
+        JS _unclone() { return clonee.unclone(); }
+        boolean jsequals(JS o) { return clonee.jsequals(o); }
+        
         public Enumeration keys() throws JSExn { return clonee.keys(); }
         public Enumeration keys() throws JSExn { return clonee.keys(); }
-        public JS get(JS key) throws JSExn { return clonee.getAndTriggerTraps(key); }
-        public void put(JS key, JS val) throws JSExn { clonee.putAndTriggerTraps(key, val); }
-        public JS callMethod(JS method, JS a0, JS a1, JS a2, JS[] rest, int nargs) throws JSExn {
-            return clonee.callMethod(method, a0, a1, a2, rest, nargs);
+        public final JS get(JS key) throws JSExn { return clonee.get(key); }
+        public final void put(JS key, JS val) throws JSExn { clonee.put(key,val); }
+        public final JS callMethod(JS method, JS a0, JS a1, JS a2, JS[] rest, int nargs) throws JSExn {
+            return clonee.callMethod(method,a0,a1,a2,rest,nargs); 
         }
         public JS call(JS a0, JS a1, JS a2, JS[] rest, int nargs) throws JSExn {
             return clonee.call(a0, a1, a2, rest, nargs);
         }
         public InputStream getInputStream() throws IOException { return clonee.getInputStream(); }
         }
         public JS call(JS a0, JS a1, JS a2, JS[] rest, int nargs) throws JSExn {
             return clonee.call(a0, a1, a2, rest, nargs);
         }
         public InputStream getInputStream() throws IOException { return clonee.getInputStream(); }
+        // FIXME: This shouldn't be necessary (its for Ibex.Blessing)
+        public JS getClonee() { return clonee; }
     }
     
     public static abstract class Enumeration extends JS {
     }
     
     public static abstract class Enumeration extends JS {
@@ -110,6 +102,7 @@ public abstract class JS {
             return super.get(key);
         }
     }
             return super.get(key);
         }
     }
+
     public static class EmptyEnumeration extends Enumeration {
         public EmptyEnumeration(Enumeration parent) { super(parent); }
         protected boolean _hasMoreElements() { return false; }
     public static class EmptyEnumeration extends Enumeration {
         public EmptyEnumeration(Enumeration parent) { super(parent); }
         protected boolean _hasMoreElements() { return false; }
@@ -217,9 +210,6 @@ public abstract class JS {
         return false;
     }
     
         return false;
     }
     
-    public static JS newArray() { return new JSArray(); }
-    public static JS newRegexp(JS pat, JS flags) throws JSExn { return new JSRegexp(pat,flags); }
-
     // Instance Methods ////////////////////////////////////////////////////////////////////
  
     public final static JS NaN = new JSNumber.D(Double.NaN);
     // Instance Methods ////////////////////////////////////////////////////////////////////
  
     public final static JS NaN = new JSNumber.D(Double.NaN);
@@ -274,39 +264,43 @@ public abstract class JS {
     private static Enumeration EMPTY_ENUMERATION = new EmptyEnumeration(null);
     
     public static JS fromReader(String sourceName, int firstLine, Reader sourceCode) throws IOException {
     private static Enumeration EMPTY_ENUMERATION = new EmptyEnumeration(null);
     
     public static JS fromReader(String sourceName, int firstLine, Reader sourceCode) throws IOException {
-        return JSFunction._fromReader(sourceName, firstLine, sourceCode);
+        return Parser.fromReader(sourceName, firstLine, sourceCode);
     }
 
     }
 
-    // HACK: caller can't know if the argument is a JSFunction or not...
-    public static JS cloneWithNewParentScope(JS j, JSScope s) {
-        return ((JSFunction)j)._cloneWithNewParentScope(s);
+    public static JS cloneWithNewGlobalScope(JS js, JS s) {
+        if(js instanceof JSFunction)
+            return ((JSFunction)js)._cloneWithNewParentScope(new JSScope.Top(s));
+        else
+            return js;
     }
 
 
     // Trap support //////////////////////////////////////////////////////////////////////////////
 
     /** performs a put, triggering traps if present; traps are run in an unpauseable interpreter */
     }
 
 
     // Trap support //////////////////////////////////////////////////////////////////////////////
 
     /** performs a put, triggering traps if present; traps are run in an unpauseable interpreter */
-    public void putAndTriggerTraps(JS key, JS value) throws JSExn {
+    public final void putAndTriggerTraps(JS key, JS value) throws JSExn {
         Trap t = getTrap(key);
         Trap t = getTrap(key);
+        if(JS.isString(key) && JS.toString(key).equals("action")) System.err.println("got put to action! + " + t);
         if(t == null || (t = t.writeTrap()) == null) put(key,value);
         else new Interpreter(t,value,false).resume();
     }
 
     /** performs a get, triggering traps if present; traps are run in an unpauseable interpreter */
         if(t == null || (t = t.writeTrap()) == null) put(key,value);
         else new Interpreter(t,value,false).resume();
     }
 
     /** performs a get, triggering traps if present; traps are run in an unpauseable interpreter */
-    public JS getAndTriggerTraps(JS key) throws JSExn {
+    public final JS getAndTriggerTraps(JS key) throws JSExn {
         Trap t = getTrap(key);
         if (t == null || (t = t.readTrap()) == null) return get(key);
         else return new Interpreter(t,null,false).resume();
     }
     
         Trap t = getTrap(key);
         if (t == null || (t = t.readTrap()) == null) return get(key);
         else return new Interpreter(t,null,false).resume();
     }
     
-    public JS justTriggerTraps(JS key, JS value) throws JSExn {
+    public final JS justTriggerTraps(JS key, JS value) throws JSExn {
         Trap t = getTrap(key);
         if(t == null || (t = t.writeTrap()) == null) return value;
         else return new Interpreter(t,value,true).resume();
     }
 
     /** adds a trap, avoiding duplicates */
         Trap t = getTrap(key);
         if(t == null || (t = t.writeTrap()) == null) return value;
         else return new Interpreter(t,value,true).resume();
     }
 
     /** adds a trap, avoiding duplicates */
-    final void addTrap(JS key, JSFunction f) throws JSExn {
+    // FIXME: This shouldn't be public, it is needed for a hack in Template.java
+    public void addTrap(JS key, JSFunction f) throws JSExn {
         if (f.numFormalArgs > 1) throw new JSExn("traps must take either one argument (write) or no arguments (read)");
         boolean isRead = f.numFormalArgs == 0;
         for(Trap t = getTrap(key); t != null; t = t.next) if (t.f == f) return;
         if (f.numFormalArgs > 1) throw new JSExn("traps must take either one argument (write) or no arguments (read)");
         boolean isRead = f.numFormalArgs == 0;
         for(Trap t = getTrap(key); t != null; t = t.next) if (t.f == f) return;
@@ -314,7 +308,8 @@ public abstract class JS {
     }
 
     /** deletes a trap, if present */
     }
 
     /** deletes a trap, if present */
-    final void delTrap(JS key, JSFunction f) throws JSExn {
+    // FIXME: This shouldn't be public, it is needed for a hack in Template.java
+    public void delTrap(JS key, JSFunction f) throws JSExn {
         Trap t = (Trap)getTrap(key);
         if (t == null) return;
         if (t.f == f) { putTrap(t.target, t.next); return; }
         Trap t = (Trap)getTrap(key);
         if (t == null) return;
         if (t.f == f) { putTrap(t.target, t.next); return; }
index 5afa82a..a9483d0 100644 (file)
@@ -5,7 +5,7 @@ import org.ibex.util.*;
 import java.util.*;
 
 /** A JavaScript JSArray */
 import java.util.*;
 
 /** A JavaScript JSArray */
-class JSArray extends JS.O {
+public class JSArray extends JS.O {
     private static final Object NULL = new Object();
     private final BalancedTree bt = new BalancedTree();
     
     private static final Object NULL = new Object();
     private final BalancedTree bt = new BalancedTree();
     
index 71da162..d50e566 100644 (file)
@@ -5,7 +5,8 @@ import java.io.*;
 import org.ibex.util.*;
 
 /** A JavaScript function, compiled into bytecode */
 import org.ibex.util.*;
 
 /** A JavaScript function, compiled into bytecode */
-class JSFunction extends JS implements ByteCodes, Tokens, Task {
+// FIXME: This shouldn't be public, needed for public add/delTrap (which is needed for the Template.java hack)
+public class JSFunction extends JS implements ByteCodes, Tokens, Task {
 
 
     // Fields and Accessors ///////////////////////////////////////////////
 
 
     // Fields and Accessors ///////////////////////////////////////////////
@@ -32,21 +33,6 @@ class JSFunction extends JS implements ByteCodes, Tokens, Task {
         i.resume();
     }
 
         i.resume();
     }
 
-    /** parse and compile a function */
-    public static JSFunction _fromReader(String sourceName, int firstLine, Reader sourceCode) throws IOException {
-        JSFunction ret = new JSFunction(sourceName, firstLine, null);
-        if (sourceCode == null) return ret;
-        Parser p = new Parser(sourceCode, sourceName, firstLine);
-        while(true) {
-            int s = ret.size;
-            p.parseStatement(ret, null);
-            if (s == ret.size) break;
-        }
-        ret.add(-1, LITERAL, null); 
-        ret.add(-1, RETURN);
-        return ret;
-    }
-
     public JSFunction _cloneWithNewParentScope(JSScope s) {
         JSFunction ret = new JSFunction(sourceName, firstLine, s);
         // Reuse the same op, arg, line, and size variables for the new "instance" of the function
     public JSFunction _cloneWithNewParentScope(JSScope s) {
         JSFunction ret = new JSFunction(sourceName, firstLine, s);
         // Reuse the same op, arg, line, and size variables for the new "instance" of the function
@@ -106,7 +92,7 @@ class JSFunction extends JS implements ByteCodes, Tokens, Task {
         sb.append("\n" + sourceName + ": " + firstLine + "\n");
         for (int i=0; i < size; i++) {
             sb.append(prefix);
         sb.append("\n" + sourceName + ": " + firstLine + "\n");
         for (int i=0; i < size; i++) {
             sb.append(prefix);
-            sb.append(i).append(" (").append(line[i]).append(") :");
+            sb.append(i).append(" (").append(line[i]).append("): ");
             if (op[i] < 0) sb.append(bytecodeToString[-op[i]]);
             else sb.append(codeToString[op[i]]);
             sb.append(" ");
             if (op[i] < 0) sb.append(bytecodeToString[-op[i]]);
             else sb.append(codeToString[op[i]]);
             sb.append(" ");
@@ -119,6 +105,9 @@ class JSFunction extends JS implements ByteCodes, Tokens, Task {
                 sb.append(" finally: ").append(jmps[1] < 0 ? "No finally block" : ""+(i+jmps[1]));
             } else if(op[i] == NEWFUNCTION) {
                 sb.append(((JSFunction) arg[i]).dump(prefix + "     "));
                 sb.append(" finally: ").append(jmps[1] < 0 ? "No finally block" : ""+(i+jmps[1]));
             } else if(op[i] == NEWFUNCTION) {
                 sb.append(((JSFunction) arg[i]).dump(prefix + "     "));
+            } else if(op[i] == NEWSCOPE) {
+                int n = ((JSNumber)arg[i]).toInt();
+                sb.append(" base: " + (n>>>16) + " size: " + (n&0xffff));
             }
             sb.append("\n");
         }
             }
             sb.append("\n");
         }
index d352ce6..75503ac 100644 (file)
@@ -4,7 +4,7 @@ package org.ibex.js;
 import gnu.regexp.*;
 
 /** A JavaScript regular expression object */
 import gnu.regexp.*;
 
 /** A JavaScript regular expression object */
-class JSRegexp extends JS {
+public class JSRegexp extends JS {
     private boolean global;
     private RE re;
     private int lastIndex;
     private boolean global;
     private RE re;
     private int lastIndex;
index 9fbd3e2..9b9a3b1 100644 (file)
@@ -1,11 +1,51 @@
 // Copyright 2004 Adam Megacz, see the COPYING file for licensing [GPL] 
 package org.ibex.js; 
 
 // Copyright 2004 Adam Megacz, see the COPYING file for licensing [GPL] 
 package org.ibex.js; 
 
-// FIXME: should allow parentScope to be a JS, not a JSScope
 /** Implementation of a JavaScript Scope */
 /** Implementation of a JavaScript Scope */
-public class JSScope extends JS.O { 
+class JSScope {
 
 
-    private JSScope parentScope;
+    private final int base;
+    private final JS[] vars;
+    final JSScope parent;
+
+    public static class Top extends JSScope {
+        private final JS global;
+        public Top(JS global) { super(null,0,0); this.global = global; }
+        JS get(int i) throws JSExn { throw new JSExn("scope index out of range"); }
+        void put(int i, JS o) throws JSExn { throw new JSExn("scope index out of range"); }
+        JS getGlobal() { return global; }
+    };
+        
+    // NOTE: We can't just set base to parent.base + parent.vars.length
+    // sometimes we only access part of a parent's scope
+    public JSScope(JSScope parent, int base, int size) {
+        this.parent = parent;
+        this.base = base;
+        this.vars = new JS[size];
+    }
+    
+    final JS get(JS i) throws JSExn {
+        if(i==null) throw new NullPointerException();
+        try {
+            return get(JS.toInt(i));
+        } catch(ArrayIndexOutOfBoundsException e) { 
+            throw new JSExn("scope index out of range");
+        }
+    }
+    final void put(JS i, JS o) throws JSExn {
+        if(i==null) throw new NullPointerException();
+        try {
+            put(JS.toInt(i),o);
+        } catch(ArrayIndexOutOfBoundsException e) { 
+            throw new JSExn("scope index out of range");
+        }
+    }
+    JS get(int i) throws JSExn { return i < base ? parent.get(i) : vars[i-base]; }
+    void put(int i, JS o) throws JSExn { if(i < base) parent.put(i,o); else vars[i-base] = o; }
+    
+    JS getGlobal() { return parent.getGlobal(); }
+    
+    /*private JSScope parentScope;
 
     private static final JS NULL_PLACEHOLDER = new JS() { };
 
 
     private static final JS NULL_PLACEHOLDER = new JS() { };
 
@@ -142,6 +182,6 @@ public class JSScope extends JS.O {
             }
             return NaN;
         }
             }
             return NaN;
         }
-    }
+    }*/
 }
 
 }
 
index 3012415..42ec2c7 100644 (file)
@@ -137,6 +137,7 @@ class Lexer implements Tokens {
         case "implements": return RESERVED;
         case "instanceof": return RESERVED;
         case "synchronized": return RESERVED;
         case "implements": return RESERVED;
         case "instanceof": return RESERVED;
         case "synchronized": return RESERVED;
+        case "cascade": return CASCADE;
         //#end
         return -1;
     }
         //#end
         return -1;
     }
index dce9a24..6293107 100644 (file)
@@ -69,7 +69,7 @@ class Parser extends Lexer implements ByteCodes {
 
     // Constructors //////////////////////////////////////////////////////
 
 
     // Constructors //////////////////////////////////////////////////////
 
-    public Parser(Reader r, String sourceName, int line) throws IOException { super(r, sourceName, line); }
+    private Parser(Reader r, String sourceName, int line) throws IOException { super(r, sourceName, line); }
 
     /** for debugging */
     public static void main(String[] s) throws IOException {
 
     /** for debugging */
     public static void main(String[] s) throws IOException {
@@ -78,7 +78,6 @@ class Parser extends Lexer implements ByteCodes {
         System.out.println(block);
     }
 
         System.out.println(block);
     }
 
-
     // Statics ////////////////////////////////////////////////////////////
 
     static byte[] precedence = new byte[MAX_TOKEN + 1];
     // Statics ////////////////////////////////////////////////////////////
 
     static byte[] precedence = new byte[MAX_TOKEN + 1];
@@ -133,8 +132,68 @@ class Parser extends Lexer implements ByteCodes {
         precedence[DOT] = precedence[LB] = precedence[LP] =  precedence[INC] = precedence[DEC] = 15;
     }
 
         precedence[DOT] = precedence[LB] = precedence[LP] =  precedence[INC] = precedence[DEC] = 15;
     }
 
-
+    // Local variable management
+    Vec scopeStack = new Vec();
+    static class ScopeInfo {
+        int base;
+        int end;
+        int newScopeInsn;
+        Hash mapping = new Hash();
+    }
+    Hash globalCache = new Hash();
+    JS scopeKey(String name) {
+        if(globalCache.get(name) != null) return null;
+        for(int i=scopeStack.size()-1;i>=0;i--) {
+            JS key = (JS)((ScopeInfo) scopeStack.elementAt(i)).mapping.get(name);
+            if(key != null) return key;
+        }
+        globalCache.put(name,Boolean.TRUE);
+        return null;
+    }
+    void scopeDeclare(String name) throws IOException {
+        ScopeInfo si = (ScopeInfo) scopeStack.lastElement();
+        if(si.mapping.get(name) != null) throw pe("" + name + " already declared in this scope");
+        si.mapping.put(name,JS.N(si.end++));
+        globalCache.put(name,null);
+    }
+    void scopePush(JSFunction b) {
+        ScopeInfo prev = (ScopeInfo) scopeStack.lastElement();
+        ScopeInfo si = new ScopeInfo();
+        si.base = prev.end;
+        si.end = si.base;
+        si.newScopeInsn = b.size;
+        scopeStack.push(si);
+        b.add(parserLine, NEWSCOPE);
+    }
+    void scopePop(JSFunction b) {
+        ScopeInfo si = (ScopeInfo) scopeStack.pop();
+        b.add(parserLine, OLDSCOPE);
+        b.set(si.newScopeInsn,JS.N((si.base<<16)|((si.end-si.base)<<0))); 
+    }
+    
+    
     // Parsing Logic /////////////////////////////////////////////////////////
     // Parsing Logic /////////////////////////////////////////////////////////
+    
+    /** parse and compile a function */
+    public static JSFunction fromReader(String sourceName, int firstLine, Reader sourceCode) throws IOException {
+        JSFunction ret = new JSFunction(sourceName, firstLine, null);
+        if (sourceCode == null) return ret;
+        Parser p = new Parser(sourceCode, sourceName, firstLine);
+        p.scopeStack.setSize(0);
+        p.scopeStack.push(new ScopeInfo());
+        p.scopePush(ret);
+        while(true) {
+            //int s = ret.size;
+            if(p.peekToken() == -1) break; // FIXME: Check this logic one more time
+            p.parseStatement(ret, null);
+            //if (s == ret.size) break;
+        }
+        p.scopePop(ret);
+        if(p.scopeStack.size() != 1) throw new Error("scopeStack height mismatch");
+        ret.add(-1, LITERAL, null); 
+        ret.add(-1, RETURN);
+        return ret;
+    }
 
     /** gets a token and throws an exception if it is not <tt>code</tt> */
     private void consume(int code) throws IOException {
 
     /** gets a token and throws an exception if it is not <tt>code</tt> */
     private void consume(int code) throws IOException {
@@ -177,12 +236,10 @@ class Parser extends Lexer implements ByteCodes {
         // (.foo) syntax
         case DOT: {
             consume(NAME);
         // (.foo) syntax
         case DOT: {
             consume(NAME);
-            b.add(parserLine, TOPSCOPE);
-            b.add(parserLine, LITERAL, JSString.intern(""));
-            b.add(parserLine, GET);
-            b.add(parserLine, LITERAL, JSString.intern(string));
-            b.add(parserLine, GET);
-            continueExpr(b, minPrecedence);
+            b.add(parserLine, GLOBALSCOPE);
+            b.add(parserLine, GET, JS.S("",true));
+            b.add(parserLine, LITERAL, JS.S(string,true));
+            continueExprAfterAssignable(b,minPrecedence,null);
             break;
         }
 
             break;
         }
 
@@ -227,18 +284,23 @@ class Parser extends Lexer implements ByteCodes {
         case INC: case DEC: {  // prefix (not postfix)
             startExpr(b, precedence[tok]);
             int prev = b.size - 1;
         case INC: case DEC: {  // prefix (not postfix)
             startExpr(b, precedence[tok]);
             int prev = b.size - 1;
+            boolean sg = b.get(prev) == SCOPEGET;
             if (b.get(prev) == GET && b.getArg(prev) != null)
                 b.set(prev, LITERAL, b.getArg(prev));
             else if(b.get(prev) == GET)
                 b.pop();
             if (b.get(prev) == GET && b.getArg(prev) != null)
                 b.set(prev, LITERAL, b.getArg(prev));
             else if(b.get(prev) == GET)
                 b.pop();
-            else
+            else if(!sg)
                 throw pe("prefixed increment/decrement can only be performed on a valid assignment target");
                 throw pe("prefixed increment/decrement can only be performed on a valid assignment target");
-            b.add(parserLine, GET_PRESERVE, Boolean.TRUE);
+            if(!sg) b.add(parserLine, GET_PRESERVE, Boolean.TRUE);
             b.add(parserLine, LITERAL, JS.N(1));
             b.add(parserLine, tok == INC ? ADD : SUB, JS.N(2));
             b.add(parserLine, LITERAL, JS.N(1));
             b.add(parserLine, tok == INC ? ADD : SUB, JS.N(2));
-            b.add(parserLine, PUT, null);
-            b.add(parserLine, SWAP, null);
-            b.add(parserLine, POP, null);
+            if(sg) {
+                b.add(parserLine, SCOPEPUT, b.getArg(prev));
+            } else {
+                b.add(parserLine, PUT, null);
+                b.add(parserLine, SWAP, null);
+                b.add(parserLine, POP, null);
+            }
             break;
         }
         case BANG: case BITNOT: case TYPEOF: {
             break;
         }
         case BANG: case BITNOT: case TYPEOF: {
@@ -266,11 +328,25 @@ class Parser extends Lexer implements ByteCodes {
             break;
         }
         case NAME: {
             break;
         }
         case NAME: {
-            b.add(parserLine, TOPSCOPE);
-            b.add(parserLine, LITERAL, JSString.intern(string));
-            continueExprAfterAssignable(b,minPrecedence);
+            JS varKey = scopeKey(string);
+            if(varKey == null) {
+                b.add(parserLine, GLOBALSCOPE);
+                b.add(parserLine, LITERAL, JSString.intern(string));
+            }
+            continueExprAfterAssignable(b,minPrecedence,varKey);
+            break;
+        }
+        case CASCADE: {
+            if(peekToken() == ASSIGN) {
+                consume(ASSIGN);
+                startExpr(b, precedence[ASSIGN]);
+                b.add(parserLine, CASCADE, JS.T);
+            } else {
+                b.add(parserLine, CASCADE, JS.F);
+            }
             break;
         }
             break;
         }
+                
         case FUNCTION: {
             consume(LP);
             int numArgs = 0;
         case FUNCTION: {
             consume(LP);
             int numArgs = 0;
@@ -278,27 +354,20 @@ class Parser extends Lexer implements ByteCodes {
             b.add(parserLine, NEWFUNCTION, b2);
 
             // function prelude; arguments array is already on the stack
             b.add(parserLine, NEWFUNCTION, b2);
 
             // function prelude; arguments array is already on the stack
-            b2.add(parserLine, TOPSCOPE);
-            b2.add(parserLine, SWAP);
-            b2.add(parserLine, DECLARE, JSString.intern("arguments"));    // declare arguments (equivalent to 'var arguments;')
-            b2.add(parserLine, SWAP);                                     // set this.arguments and leave the value on the stack
-            b2.add(parserLine, PUT);
+            scopePush(b2);
+            scopeDeclare("arguments");
+            b2.add(parserLine, SCOPEPUT,scopeKey("arguments"));
 
             while(peekToken() != RP) {                                    // run through the list of argument names
                 numArgs++;
                 if (peekToken() == NAME) {
                     consume(NAME);                                        // a named argument
 
             while(peekToken() != RP) {                                    // run through the list of argument names
                 numArgs++;
                 if (peekToken() == NAME) {
                     consume(NAME);                                        // a named argument
-                    JS varName = JSString.intern(string);
                     
                     b2.add(parserLine, DUP);                              // dup the args array 
                     b2.add(parserLine, GET, JS.N(numArgs - 1));   // retrieve it from the arguments array
                     
                     b2.add(parserLine, DUP);                              // dup the args array 
                     b2.add(parserLine, GET, JS.N(numArgs - 1));   // retrieve it from the arguments array
-                    b2.add(parserLine, TOPSCOPE);
-                    b2.add(parserLine, SWAP);
-                    b2.add(parserLine, DECLARE, varName);                  // declare the name
-                    b2.add(parserLine, SWAP);
-                    b2.add(parserLine, PUT); 
-                    b2.add(parserLine, POP);                               // pop the value
-                    b2.add(parserLine, POP);                               // pop the scope                  
+                    scopeDeclare(string);
+                    b2.add(parserLine, SCOPEPUT, scopeKey(string));
+                    b2.add(parserLine, POP);
                 }
                 if (peekToken() == RP) break;
                 consume(COMMA);
                 }
                 if (peekToken() == RP) break;
                 consume(COMMA);
@@ -307,13 +376,13 @@ class Parser extends Lexer implements ByteCodes {
 
             b2.numFormalArgs = numArgs;
             b2.add(parserLine, POP);                                      // pop off the arguments array
 
             b2.numFormalArgs = numArgs;
             b2.add(parserLine, POP);                                      // pop off the arguments array
-            b2.add(parserLine, POP);                                      // pop off TOPSCOPE
             
            if(peekToken() != LC)
                 throw pe("JSFunctions must have a block surrounded by curly brackets");
                 
             parseBlock(b2, null);                                   // the function body
             
            if(peekToken() != LC)
                 throw pe("JSFunctions must have a block surrounded by curly brackets");
                 
             parseBlock(b2, null);                                   // the function body
-
+            
+            scopePop(b2);
             b2.add(parserLine, LITERAL, null);                        // in case we "fall out the bottom", return NULL
             b2.add(parserLine, RETURN);
 
             b2.add(parserLine, LITERAL, null);                        // in case we "fall out the bottom", return NULL
             b2.add(parserLine, RETURN);
 
@@ -364,12 +433,12 @@ class Parser extends Lexer implements ByteCodes {
      *  expression that modifies the assignable.  This method always
      *  decreases the stack depth by exactly one element.
      */
      *  expression that modifies the assignable.  This method always
      *  decreases the stack depth by exactly one element.
      */
-    private void continueExprAfterAssignable(JSFunction b,int minPrecedence) throws IOException {
+    private void continueExprAfterAssignable(JSFunction b,int minPrecedence, JS varKey) throws IOException {
         int saveParserLine = parserLine;
         int saveParserLine = parserLine;
-        _continueExprAfterAssignable(b,minPrecedence);
+        _continueExprAfterAssignable(b,minPrecedence,varKey);
         parserLine = saveParserLine;
     }
         parserLine = saveParserLine;
     }
-    private void _continueExprAfterAssignable(JSFunction b,int minPrecedence) throws IOException {
+    private void _continueExprAfterAssignable(JSFunction b,int minPrecedence, JS varKey) throws IOException {
         if (b == null) throw new Error("got null b; this should never happen");
         int tok = getToken();
         if (minPrecedence != -1 && (precedence[tok] < minPrecedence || (precedence[tok] == minPrecedence && !isRightAssociative[tok])))
         if (b == null) throw new Error("got null b; this should never happen");
         int tok = getToken();
         if (minPrecedence != -1 && (precedence[tok] < minPrecedence || (precedence[tok] == minPrecedence && !isRightAssociative[tok])))
@@ -393,52 +462,71 @@ class Parser extends Lexer implements ByteCodes {
             */
         case ASSIGN_BITOR: case ASSIGN_BITXOR: case ASSIGN_BITAND: case ASSIGN_LSH: case ASSIGN_RSH: case ASSIGN_URSH:
         case ASSIGN_MUL: case ASSIGN_DIV: case ASSIGN_MOD: case ASSIGN_ADD: case ASSIGN_SUB: case ADD_TRAP: case DEL_TRAP: {
             */
         case ASSIGN_BITOR: case ASSIGN_BITXOR: case ASSIGN_BITAND: case ASSIGN_LSH: case ASSIGN_RSH: case ASSIGN_URSH:
         case ASSIGN_MUL: case ASSIGN_DIV: case ASSIGN_MOD: case ASSIGN_ADD: case ASSIGN_SUB: case ADD_TRAP: case DEL_TRAP: {
-            if (tok != ADD_TRAP && tok != DEL_TRAP) b.add(parserLine, GET_PRESERVE);
+            if (tok != ADD_TRAP && tok != DEL_TRAP) 
+                b.add(parserLine, varKey == null ? GET_PRESERVE : SCOPEGET, varKey);
             
             startExpr(b,  precedence[tok]);
             
             if (tok != ADD_TRAP && tok != DEL_TRAP) {
                 // tok-1 is always s/^ASSIGN_// (0 is BITOR, 1 is ASSIGN_BITOR, etc) 
                 b.add(parserLine, tok - 1, tok-1==ADD ? JS.N(2) : null);
             
             startExpr(b,  precedence[tok]);
             
             if (tok != ADD_TRAP && tok != DEL_TRAP) {
                 // tok-1 is always s/^ASSIGN_// (0 is BITOR, 1 is ASSIGN_BITOR, etc) 
                 b.add(parserLine, tok - 1, tok-1==ADD ? JS.N(2) : null);
-                b.add(parserLine, PUT);
-                b.add(parserLine, SWAP);
-                b.add(parserLine, POP);
+                if(varKey == null) {
+                    b.add(parserLine, PUT);
+                    b.add(parserLine, SWAP);
+                    b.add(parserLine, POP);
+                } else {
+                    b.add(parserLine, SCOPEPUT, varKey);
+                }
             } else {
             } else {
+                if(varKey != null) throw pe("cannot place traps on local variables");
                 b.add(parserLine, tok);
             }
             break;
         }
         case INC: case DEC: { // postfix
                 b.add(parserLine, tok);
             }
             break;
         }
         case INC: case DEC: { // postfix
-            b.add(parserLine, GET_PRESERVE, Boolean.TRUE);
-            b.add(parserLine, LITERAL, JS.N(1));
-            b.add(parserLine, tok == INC ? ADD : SUB, JS.N(2));
-            b.add(parserLine, PUT, null);
-            b.add(parserLine, SWAP, null);
-            b.add(parserLine, POP, null);
-            b.add(parserLine, LITERAL, JS.N(1));
-            b.add(parserLine, tok == INC ? SUB : ADD, JS.N(2));   // undo what we just did, since this is postfix
+            if(varKey == null) {
+                b.add(parserLine, GET_PRESERVE, Boolean.TRUE);
+                b.add(parserLine, LITERAL, JS.N(1));
+                b.add(parserLine, tok == INC ? ADD : SUB, JS.N(2));
+                b.add(parserLine, PUT, null);
+                b.add(parserLine, SWAP, null);
+                b.add(parserLine, POP, null);
+                b.add(parserLine, LITERAL, JS.N(1));
+                b.add(parserLine, tok == INC ? SUB : ADD, JS.N(2));   // undo what we just did, since this is postfix
+            } else {
+                b.add(parserLine, SCOPEGET, varKey);
+                b.add(parserLine, DUP);
+                b.add(parserLine, LITERAL, JS.ONE);
+                b.add(parserLine, tok == INC ? ADD : SUB, JS.N(2));
+                b.add(parserLine, SCOPEPUT, varKey);
+            }
             break;
         }
         case ASSIGN: {
             startExpr(b, precedence[tok]);
             break;
         }
         case ASSIGN: {
             startExpr(b, precedence[tok]);
-            b.add(parserLine, PUT);
-            b.add(parserLine, SWAP);
-            b.add(parserLine, POP);
+            if(varKey == null) {
+                b.add(parserLine, PUT);
+                b.add(parserLine, SWAP);
+                b.add(parserLine, POP);
+            } else {
+                b.add(parserLine, SCOPEPUT, varKey);
+            }
             break;
         }
         case LP: {
             break;
         }
         case LP: {
-
             // Method calls are implemented by doing a GET_PRESERVE
             // first.  If the object supports method calls, it will
             // return JS.METHOD
             // Method calls are implemented by doing a GET_PRESERVE
             // first.  If the object supports method calls, it will
             // return JS.METHOD
-            b.add(parserLine, GET_PRESERVE);
+            b.add(parserLine, varKey == null ? GET_PRESERVE : SCOPEGET, varKey);
             int n = parseArgs(b);
             int n = parseArgs(b);
-            b.add(parserLine, CALLMETHOD, JS.N(n));
+            b.add(parserLine, varKey == null ? CALLMETHOD : CALL, JS.N(n));
             break;
         }
         default: {
             pushBackToken();
             break;
         }
         default: {
             pushBackToken();
-            if(b.get(b.size-1) == LITERAL && b.getArg(b.size-1) != null)
+            if(varKey != null)
+                b.add(parserLine, SCOPEGET, varKey);
+            else if(b.get(b.size-1) == LITERAL && b.getArg(b.size-1) != null)
                 b.set(b.size-1,GET,b.getArg(b.size-1));
             else
                 b.add(parserLine, GET);
                 b.set(b.size-1,GET,b.getArg(b.size-1));
             else
                 b.add(parserLine, GET);
@@ -516,13 +604,13 @@ class Parser extends Lexer implements ByteCodes {
                 consume(NAME);
             }
             b.add(parserLine, LITERAL, JSString.intern(string));
                 consume(NAME);
             }
             b.add(parserLine, LITERAL, JSString.intern(string));
-            continueExprAfterAssignable(b,minPrecedence);
+            continueExprAfterAssignable(b,minPrecedence,null);
             break;
         }
         case LB: { // subscripting (not array constructor)
             startExpr(b, -1);
             consume(RB);
             break;
         }
         case LB: { // subscripting (not array constructor)
             startExpr(b, -1);
             consume(RB);
-            continueExprAfterAssignable(b,minPrecedence);
+            continueExprAfterAssignable(b,minPrecedence,null);
             break;
         }
         case HOOK: {
             break;
         }
         case HOOK: {
@@ -611,22 +699,19 @@ class Parser extends Lexer implements ByteCodes {
             break;
         }
         case VAR: {
             break;
         }
         case VAR: {
-            b.add(parserLine, TOPSCOPE);                         // push the current scope
             while(true) {
                 consume(NAME);
             while(true) {
                 consume(NAME);
-                b.add(parserLine, DECLARE, JSString.intern(string)); // declare it
+                String var = string;
+                scopeDeclare(var);
                 if (peekToken() == ASSIGN) {                     // if there is an '=' after the variable name
                     consume(ASSIGN);
                     startExpr(b, NO_COMMA);
                 if (peekToken() == ASSIGN) {                     // if there is an '=' after the variable name
                     consume(ASSIGN);
                     startExpr(b, NO_COMMA);
-                    b.add(parserLine, PUT);                      // assign it
+                    b.add(parserLine, SCOPEPUT, scopeKey(var)); // assign it
                     b.add(parserLine, POP);                      // clean the stack
                     b.add(parserLine, POP);                      // clean the stack
-                } else {
-                    b.add(parserLine, POP);                      // pop the string pushed by declare
-                }   
+                }
                 if (peekToken() != COMMA) break;
                 consume(COMMA);
             }
                 if (peekToken() != COMMA) break;
                 consume(COMMA);
             }
-            b.add(parserLine, POP);                              // pop off the topscope
             if ((mostRecentlyReadToken != RC || peekToken() == SEMI) && peekToken() != -1 && mostRecentlyReadToken != SEMI) consume(SEMI);
             break;
         }
             if ((mostRecentlyReadToken != RC || peekToken() == SEMI) && peekToken() != -1 && mostRecentlyReadToken != SEMI) consume(SEMI);
             break;
         }
@@ -780,17 +865,12 @@ class Parser extends Lexer implements ByteCodes {
                     }
                     consume(RP);
                     // the exception is on top of the stack; put it to the chosen name
                     }
                     consume(RP);
                     // the exception is on top of the stack; put it to the chosen name
-                    b.add(parserLine, NEWSCOPE);
-                    b.add(parserLine, TOPSCOPE);
-                    b.add(parserLine, SWAP);
-                    b.add(parserLine, LITERAL,JSString.intern(exceptionVar));
-                    b.add(parserLine, DECLARE);
-                    b.add(parserLine, SWAP);
-                    b.add(parserLine, PUT);
-                    b.add(parserLine, POP);
+                    scopePush(b);
+                    scopeDeclare(exceptionVar);
+                    b.add(parserLine, SCOPEPUT, scopeKey(exceptionVar));
                     b.add(parserLine, POP);
                     parseBlock(b, null);
                     b.add(parserLine, POP);
                     parseBlock(b, null);
-                    b.add(parserLine, OLDSCOPE);
+                    scopePop(b);
                     
                     b.add(parserLine, JMP);
                     catchEnds.addElement(new Integer(b.size-1));
                     
                     b.add(parserLine, JMP);
                     catchEnds.addElement(new Integer(b.size-1));
@@ -863,20 +943,27 @@ class Parser extends Lexer implements ByteCodes {
                 b.add(parserLine,DUP);
                 b.add(parserLine,GET,JS.S("nextElement"));
 
                 b.add(parserLine,DUP);
                 b.add(parserLine,GET,JS.S("nextElement"));
 
-                b.add(parserLine, NEWSCOPE);
+                scopePush(b);
                 
                 
-                b.add(parserLine,TOPSCOPE);
-                b.add(parserLine,SWAP);
-                b.add(parserLine, hadVar ? DECLARE : LITERAL, JSString.intern(varName));
-                b.add(parserLine,SWAP);
-                b.add(parserLine,PUT);
-                b.add(parserLine,POP);
-                b.add(parserLine,POP);
-                b.add(parserLine,SWAP);
+                if(hadVar) scopeDeclare(varName);
+                JS varKey = scopeKey(varName);
+                
+                if(varKey == null) {
+                    b.add(parserLine,GLOBALSCOPE);
+                    b.add(parserLine,SWAP);
+                    b.add(parserLine, LITERAL, JSString.intern(varName));
+                    b.add(parserLine,SWAP);
+                    b.add(parserLine,PUT);
+                    b.add(parserLine,POP);
+                } else {
+                    b.add(parserLine, SCOPEPUT, varKey);
+                }
+                b.add(parserLine,POP);  // pop the put'ed value
+                b.add(parserLine,SWAP); // put CallMarker back into place
                 
                 parseStatement(b, null);
                 
                 
                 parseStatement(b, null);
                 
-                b.add(parserLine, OLDSCOPE);
+                scopePop(b);
                 b.add(parserLine, CONTINUE);
                 // jump here on break
                 b.set(size, JS.N(b.size - size));
                 b.add(parserLine, CONTINUE);
                 // jump here on break
                 b.set(size, JS.N(b.size - size));
@@ -884,7 +971,7 @@ class Parser extends Lexer implements ByteCodes {
                 b.add(parserLine, POP);
             } else {
                 if (hadVar) pushBackToken(VAR, null);                    // yeah, this actually matters
                 b.add(parserLine, POP);
             } else {
                 if (hadVar) pushBackToken(VAR, null);                    // yeah, this actually matters
-                b.add(parserLine, NEWSCOPE);                             // grab a fresh scope
+                scopePush(b);                             // grab a fresh scope
                     
                 parseStatement(b, null);                                 // initializer
                 JSFunction e2 =                                    // we need to put the incrementor before the test
                     
                 parseStatement(b, null);                                 // initializer
                 JSFunction e2 =                                    // we need to put the incrementor before the test
@@ -914,7 +1001,7 @@ class Parser extends Lexer implements ByteCodes {
                 b.add(parserLine, CONTINUE);                             // if we fall out the bottom, CONTINUE
                 b.set(size2 - 1, JS.N(b.size - size2 + 1));     // end of the loop
                     
                 b.add(parserLine, CONTINUE);                             // if we fall out the bottom, CONTINUE
                 b.set(size2 - 1, JS.N(b.size - size2 + 1));     // end of the loop
                     
-                b.add(parserLine, OLDSCOPE);                             // get our scope back
+                scopePop(b);                            // get our scope back
             }
             break;
         }
             }
             break;
         }
@@ -938,9 +1025,9 @@ class Parser extends Lexer implements ByteCodes {
 
         case LC: {  // blocks are statements too
             pushBackToken();
 
         case LC: {  // blocks are statements too
             pushBackToken();
-            b.add(parserLine, NEWSCOPE);
+            scopePush(b);
             parseBlock(b, label);
             parseBlock(b, label);
-            b.add(parserLine, OLDSCOPE);
+            scopePop(b);
             break;
         }
 
             break;
         }
 
index c432a6a..1bf62e1 100644 (file)
@@ -10,9 +10,9 @@ public class Test extends JS {
         if(args.length == 0) { System.err.println("Usage Test filename"); System.exit(1); }
         JS f = JS.fromReader(args[0],1,new FileReader(args[0]));
         System.out.println(((JSFunction)f).dump());
         if(args.length == 0) { System.err.println("Usage Test filename"); System.exit(1); }
         JS f = JS.fromReader(args[0],1,new FileReader(args[0]));
         System.out.println(((JSFunction)f).dump());
-        JSScope s = new JSScope(new JSScope.Global());
+        JS s = new JS.O();
         s.put(JS.S("sys"),new Test());
         s.put(JS.S("sys"),new Test());
-        f = JS.cloneWithNewParentScope(f,s);
+        f = JS.cloneWithNewGlobalScope(f,s);
         //JS ret = f.call(null,null,null,null,0);
         Interpreter i = new Interpreter((JSFunction)f, true, new Interpreter.JSArgs(f));
         JS ret = i.resume();
         //JS ret = f.call(null,null,null,null,0);
         Interpreter i = new Interpreter((JSFunction)f, true, new Interpreter.JSArgs(f));
         JS ret = i.resume();
index 8970e14..126a10b 100644 (file)
@@ -96,6 +96,7 @@ interface Tokens {
     public static final int GRAMMAR       = 78;  // the grammar-definition operator (::=)
     public static final int ADD_TRAP      = 79;  // the add-trap operator (++=)
     public static final int DEL_TRAP      = 80;  // the del-trap operator (--=)
     public static final int GRAMMAR       = 78;  // the grammar-definition operator (::=)
     public static final int ADD_TRAP      = 79;  // the add-trap operator (++=)
     public static final int DEL_TRAP      = 80;  // the del-trap operator (--=)
+    public static final int CASCADE       = 81;  // cascade expression - arg==true for write cascade
  
     public static final int MAX_TOKEN = DEL_TRAP;
 
  
     public static final int MAX_TOKEN = DEL_TRAP;
 
@@ -112,7 +113,7 @@ interface Tokens {
         "HOOK", "COLON", "INC", "DEC", "DOT", "FUNCTION", "IF",
         "ELSE", "SWITCH", "CASE", "DEFAULT", "WHILE", "DO", "FOR",
         "VAR", "WITH", "CATCH", "FINALLY", "RESERVED", "GRAMMAR",
         "HOOK", "COLON", "INC", "DEC", "DOT", "FUNCTION", "IF",
         "ELSE", "SWITCH", "CASE", "DEFAULT", "WHILE", "DO", "FOR",
         "VAR", "WITH", "CATCH", "FINALLY", "RESERVED", "GRAMMAR",
-        "ADD_TRAP", "DEL_TRAP"
+        "ADD_TRAP", "DEL_TRAP", "CASCADE"
     };
 
 }
     };
 
 }