Fixed root environment bug.
[jrw.git] / src / jrw / Environment.java
index f0f1347..57642e6 100644 (file)
@@ -1,15 +1,85 @@
 package jrw;
 
 import java.util.*;
+import java.util.function.*;
 
 public class Environment {
-    public static Map<Object, Object> dispatch(Handler handler, Request req) {
-       while(true) {
-           try {
-               return(handler.handle(req));
-           } catch(Restart r) {
-               handler = r;
+    public static final Environment root = new Environment(null);
+    private static final ThreadLocal<Environment> current = new ThreadLocal<>();
+    public final Environment parent;
+    private Map<Variable<?>, Object> data = Collections.emptyMap();
+
+    public static class Variable<T> {
+       private final Supplier<? extends T> ival;
+       private T rval;
+       private boolean inited;
+
+       public Variable(Supplier<? extends T> ival) {
+           this.ival = ival;
+       }
+
+       @SuppressWarnings("unchecked")
+       public T get() {
+           for(Environment env = current(); env != null; env = env.parent) {
+               Map<Variable<?>, Object> data = env.data;
+               if(data.containsKey(this))
+                   return((T)data.get(this));
            }
+           if(!inited) {
+               synchronized(this) {
+                   if(!inited) {
+                       rval = (this.ival == null) ? null : this.ival.get();
+                       inited = true;
+                   }
+               }
+           }
+           return(rval);
        }
     }
+
+    public Environment(Environment parent) {
+       this.parent = parent;
+    }
+
+    public Environment() {
+       this(current());
+    }
+
+    public <T> void set(Variable<T> var, T val) {
+       synchronized(this) {
+           Map<Variable<?>, Object> data = new IdentityHashMap<>(this.data);
+           data.put(var, val);
+           this.data = data;
+       }
+    }
+
+    public <T> void clear(Variable<T> var, T val) {
+       synchronized(this) {
+           Map<Variable<?>, Object> data = new IdentityHashMap<>(this.data);
+           data.remove(var);
+           this.data = data;
+       }
+    }
+
+    public static Environment current() {
+       Environment ret = current.get();
+       return((ret == null) ? root : ret);
+    }
+
+    public class Frame implements AutoCloseable {
+       private final Environment prev;
+
+       private Frame() {
+           this.prev = current.get();
+           current.set(Environment.this);
+       }
+
+       public void close() {
+           current.set(prev);
+       }
+    }
+
+    public Frame frame() {
+       return(new Frame());
+    }
 }