1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
|
package coffee.liz.lambda.eval;
import coffee.liz.lambda.ast.Expression;
import coffee.liz.lambda.ast.Macro;
import coffee.liz.lambda.bind.ExternalBinding;
import jakarta.annotation.Nullable;
import lombok.Builder;
import lombok.RequiredArgsConstructor;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
/**
* Runtime environment for variable bindings, macros, and external bindings.
*/
@RequiredArgsConstructor
@Builder(toBuilder = true)
public final class Environment {
/** Named expansions */
private final Map<String, Expression> macros;
/** "FFI" */
private final Map<String, ExternalBinding> externalBindings;
/** Variable name bound at this scope level. Null for root. */
@Nullable
private final String boundName;
/** Lazily-evaluated value for boundName, or null for root. */
@Nullable
private final Supplier<Value> boundValue;
/** Enclosing scope, or null for root. Forms a linked list of bindings. */
@Nullable
private final Environment parent;
/**
* Creates an environment from macro and external binding lists.
*
* @param macros
* program macro definitions
* @param externalBindings
* external Java bindings for FFI
* @return the new environment
*/
public static Environment from(final List<Macro> macros, final List<ExternalBinding> externalBindings) {
return new Environment(macros.stream().collect(Collectors.toMap(Macro::name, Macro::expression)),
externalBindings.stream().collect(Collectors.toMap(ExternalBinding::getName, Function.identity())),
null, null, null);
}
/**
* Creates a child scope.
*
* @param name
* the variable name
* @param value
* the value supplier (thunk)
* @return a new environment with the binding added
*/
public Environment extend(final String name, final Supplier<Value> value) {
return new Environment(macros, externalBindings, name, value, this);
}
public Environment extend(final Environment other) {
return other.toBuilder().parent(this).build();
}
/**
* Looks up a name, checking bindings, then macros, then external bindings.
*
* @param name
* the name to look up
* @return the lookup result, or empty if not found
*/
public Optional<LookupResult> lookup(final String name) {
for (Environment env = this; env != null; env = env.parent) {
if (!name.equals(env.boundName)) {
continue;
}
return Optional.of(new LookupResult.Binding(env.boundValue));
}
final Expression macro = macros.get(name);
if (macro != null) {
return Optional.of(new LookupResult.Macro(macro));
}
final ExternalBinding external = externalBindings.get(name);
if (external != null) {
return Optional.of(new LookupResult.External(external));
}
return Optional.empty();
}
/**
* Returns local binding frames from nearest scope to farthest.
*/
public List<BindingFrame> bindingFrames() {
final List<BindingFrame> frames = new ArrayList<>();
for (Environment env = this; env != null; env = env.parent) {
if (env.boundName == null || env.boundValue == null) {
continue;
}
frames.add(new BindingFrame(env.boundName, env.boundValue));
}
return List.copyOf(frames);
}
/**
* Result of looking up a name in the environment.
*/
public sealed interface LookupResult {
/** A local variable binding. */
record Binding(Supplier<Value> value) implements LookupResult {
}
/** A macro definition. */
record Macro(Expression expression) implements LookupResult {
}
/** An external Java binding. */
record External(ExternalBinding binding) implements LookupResult {
}
}
public record BindingFrame(String name, Supplier<Value> value) {
}
public static Environment root() {
return new Environment(Map.of(), Map.of(), null, null, null);
}
}
|