Fixed root environment bug.
[jrw.git] / src / jrw / Environment.java
1 package jrw;
2
3 import java.util.*;
4 import java.util.function.*;
5
6 public class Environment {
7     public static final Environment root = new Environment(null);
8     private static final ThreadLocal<Environment> current = new ThreadLocal<>();
9     public final Environment parent;
10     private Map<Variable<?>, Object> data = Collections.emptyMap();
11
12     public static class Variable<T> {
13         private final Supplier<? extends T> ival;
14         private T rval;
15         private boolean inited;
16
17         public Variable(Supplier<? extends T> ival) {
18             this.ival = ival;
19         }
20
21         @SuppressWarnings("unchecked")
22         public T get() {
23             for(Environment env = current(); env != null; env = env.parent) {
24                 Map<Variable<?>, Object> data = env.data;
25                 if(data.containsKey(this))
26                     return((T)data.get(this));
27             }
28             if(!inited) {
29                 synchronized(this) {
30                     if(!inited) {
31                         rval = (this.ival == null) ? null : this.ival.get();
32                         inited = true;
33                     }
34                 }
35             }
36             return(rval);
37         }
38     }
39
40     public Environment(Environment parent) {
41         this.parent = parent;
42     }
43
44     public Environment() {
45         this(current());
46     }
47
48     public <T> void set(Variable<T> var, T val) {
49         synchronized(this) {
50             Map<Variable<?>, Object> data = new IdentityHashMap<>(this.data);
51             data.put(var, val);
52             this.data = data;
53         }
54     }
55
56     public <T> void clear(Variable<T> var, T val) {
57         synchronized(this) {
58             Map<Variable<?>, Object> data = new IdentityHashMap<>(this.data);
59             data.remove(var);
60             this.data = data;
61         }
62     }
63
64     public static Environment current() {
65         Environment ret = current.get();
66         return((ret == null) ? root : ret);
67     }
68
69     public class Frame implements AutoCloseable {
70         private final Environment prev;
71
72         private Frame() {
73             this.prev = current.get();
74             current.set(Environment.this);
75         }
76
77         public void close() {
78             current.set(prev);
79         }
80     }
81
82     public Frame frame() {
83         return(new Frame());
84     }
85 }