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
|
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.RequiredArgsConstructor;
import java.util.List;
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
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);
}
/**
* 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();
}
/**
* 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 {
}
}
}
|