-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathASTMatch.java
More file actions
112 lines (93 loc) · 5.14 KB
/
Copy pathASTMatch.java
File metadata and controls
112 lines (93 loc) · 5.14 KB
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
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class ASTMatch implements ASTNode {
ASTNode exp;
List<ASTPattern> patterns;
List<ASTNode> bodies;
public ASTMatch(ASTNode e, List<ASTPattern> patterns, List<ASTNode> bodies) {
this.exp = e;
this.patterns = patterns;
this.bodies = bodies;
}
@Override
public IValue eval(Environment<IValue> e) throws InterpreterError {
IValue v = exp.eval(e);
for (int i = 0; i < patterns.size(); i++) {
ASTPattern pat = patterns.get(i);
ASTNode body = bodies.get(i);
if (v instanceof VStructTuple st && st.getTag().equals(pat.getTag()) && st.getValues().size() >= pat.getPvs().size()) {
Environment<IValue> en = e.beginScope();
List<String> patVars = pat.getPvs();
List<IValue> values = st.getValues();
for (int j = 0; j < patVars.size(); j++) {
if (!patVars.get(j).equals("_")) { // _ is the #Nil() placeholder
en.assoc(patVars.get(j), values.get(j));
}
}
return body.eval(en);
}
}
throw new InterpreterError("match: no pattern matched for " + v.toStr());
}
@Override
public ASTType typecheck(Environment<ASTType> e) throws TypeCheckError, InterpreterError {
ASTType exprType = exp.typecheck(e);
if (!(exprType instanceof ASTTEnum enumType)) {
throw new TypeCheckError("match: expression must be an enum type, got " + exprType.toStr());
}
// Checks exhaustiveness
boolean hasWildcard = patterns.stream().anyMatch(p -> p.getTag() == null || p.getTag().equals("_"));
if (!hasWildcard) {
for (Map.Entry<String, List<ASTType>> variant : enumType.getOptions()) {
String tag = variant.getKey();
boolean covered = patterns.stream().anyMatch(p -> p.getTag() != null && p.getTag().equals(tag));
if (!covered)
throw new TypeCheckError("match: non-exhaustive patterns, missing tag " + tag);
}
}
ASTType resultType = null;
for (int i = 0; i < patterns.size(); i++) {
ASTPattern pat = patterns.get(i);
ASTNode body = bodies.get(i);
String tag = pat.getTag();
List<String> patVars = pat.getPvs();
boolean isWildcard = (tag == null || tag.equals("_"));
List<ASTType> fieldTypes = new ArrayList<>();
if (!isWildcard) {
fieldTypes = enumType.getOptions().stream().filter(entry -> entry.getKey().equals(tag))
.map(Map.Entry::getValue).findFirst()
.orElseThrow(() -> new TypeCheckError("match: tag " + tag + " not in enum"));
boolean isNilCase = patVars.size() == 1 && patVars.get(0).equals("_") && fieldTypes.isEmpty();
if (!isNilCase && patVars.size() > fieldTypes.size()) {
throw new TypeCheckError("match: pattern has more variables than enum fields for " + tag);
}
}
Environment<ASTType> patEnv = e.beginScope();
if (!isWildcard) {
for (int j = 0; j < patVars.size(); j++) {
if (!patVars.get(j).equals("_")) {
patEnv.assoc(patVars.get(j), fieldTypes.get(j));
}
}
}
ASTType branchType = body.typecheck(patEnv);
patEnv.endScope();
branchType = ASTTypeDef.unfold(branchType, e);
if (resultType == null) {
resultType = branchType;
} else {
resultType = resultType.computeLUB(branchType, e);
}
}
return ASTTypeDef.unfold(resultType, e);
}
@Override
public boolean isFreeOutsideLambda(String id) {
if (exp.isFreeOutsideLambda(id)) return true;
for (ASTNode body : bodies)
if (body.isFreeOutsideLambda(id))
return true;
return false;
}
}