--- /dev/null
+package jrw;
+
+import java.util.*;
+import java.util.function.*;
+
+public class Environment {
+ public static final Environment root = new Environment();
+ 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());
+ }
+}