-
Notifications
You must be signed in to change notification settings - Fork 0
/
MH_Typechecker.java
executable file
·222 lines (195 loc) · 7.33 KB
/
MH_Typechecker.java
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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
// File: MH_Typechecker.java
// Date: October 2015
// Java template file for typechecker component of Informatics 2A Assignment 1.
// Provides infrastructure for Micro-Haskell typechecking:
// the core typechecking operation is to be implemented by students.
import java.util.* ;
import java.io.* ;
class MH_Typechecker {
static MH_Parser MH_Parser = MH_Type_Impl.MH_Parser ;
// The core of the typechecker:
// Computing the MH_TYPE of a given MH_EXP in a given TYPE_ENV.
// Should raise TypeError if MH_EXP isn't well-typed
static MH_TYPE IntegerType = MH_Type_Impl.IntegerType ;
static MH_TYPE BoolType = MH_Type_Impl.BoolType ;
static MH_TYPE computeType (MH_EXP exp, TYPE_ENV env)
throws TypeError, UnknownVariable {
if (exp.isVAR()){
return env.typeOf(exp.value());
}
else if (exp.isNUM()){
String s = exp.value();
for (char c : s.toCharArray())
if (c<'0' || c>'9')
throw new TypeError("Non-digit character " + c + " not expected in " + s + ".");
return IntegerType;
}
else if (exp.isBOOLEAN()){
String s = exp.value();
if (s!="True" && s!="False")
throw new TypeError("BoolType " + s + " does not match \"True\" or \"False\". ");
return BoolType;
}
else if (exp.isAPP()){
MH_TYPE fst = computeType(exp.first(), env);
MH_TYPE snd = computeType(exp.second(), env);
if (!fst.isArrow())
throw new TypeError("-> expected where " + fst + " found.");
if (!fst.left().equals(snd))
throw new TypeError("Argument not matching expected type.");
return fst.right();
}
else if (exp.isINFIX()){
String infix = exp.infixOp();
boolean fst = computeType(exp.first(), env).equals(IntegerType);
boolean snd = computeType(exp.second(), env).equals(IntegerType);
if (!fst)
throw new TypeError("First argument is non IntegerType.");
else if (!snd)
throw new TypeError("Second argument is non IntegerType.");
switch (infix){
case "==": return BoolType;
case "<=": return BoolType;
case "+": return IntegerType;
case "-": return IntegerType;
default: throw new TypeError("Infix operator: " + infix + " not in language.");
}
}
else if (exp.isIF()){
MH_TYPE fst = computeType(exp.first(), env);
MH_TYPE snd = computeType(exp.second(), env);
MH_TYPE trd = computeType(exp.third(), env);
if (!fst.equals(BoolType))
throw new TypeError("Boolean condition expected where " + fst + " found.");
if (!snd.equals(trd))
throw new TypeError("Types of \"then\" and \"else\" in if-then-else expression differ");
return trd;
}
else
throw new TypeError("Expression not in language.");
}
// Type environments:
interface TYPE_ENV {
MH_TYPE typeOf (String var) throws UnknownVariable ;
}
static class MH_Type_Env implements TYPE_ENV {
TreeMap env ;
public MH_TYPE typeOf (String var) throws UnknownVariable {
MH_TYPE t = (MH_TYPE)(env.get(var)) ;
if (t == null) throw new UnknownVariable(var) ;
else return t ;
}
// Constructor for cloning a type env
MH_Type_Env (MH_Type_Env given) {
this.env = (TreeMap)given.env.clone() ;
}
// Constructor for building a type env from the type decls
// appearing in a program
MH_Type_Env (TREE prog) throws DuplicatedVariable {
this.env = new TreeMap() ;
TREE prog1 = prog ;
while (prog1.getRhs() != MH_Parser.epsilon) {
TREE typeDecl = prog1.getChildren()[0].getChildren()[0] ;
String var = typeDecl.getChildren()[0].getValue() ;
MH_TYPE theType = MH_Type_Impl.convertType
(typeDecl.getChildren()[2]);
if (env.containsKey(var))
throw new DuplicatedVariable(var) ;
else env.put(var,theType) ;
prog1 = prog1.getChildren()[1] ;
}
System.out.println ("Type conversions successful.") ;
}
// Augmenting a type env with a list of function arguments.
// Takes the type of the function, returns the result type.
MH_TYPE addArgBindings (TREE args, MH_TYPE theType)
throws DuplicatedVariable, TypeError {
TREE args1=args ;
MH_TYPE theType1 = theType ;
while (args1.getRhs() != MH_Parser.epsilon) {
if (theType1.isArrow()) {
String var = args1.getChildren()[0].getValue() ;
if (env.containsKey(var)) {
throw new DuplicatedVariable(var) ;
} else {
this.env.put(var, theType1.left()) ;
theType1 = theType1.right() ;
args1 = args1.getChildren()[1] ;
}
} else throw new TypeError ("Too many function arguments");
} ;
return theType1 ;
}
}
static MH_Type_Env compileTypeEnv (TREE prog)
throws DuplicatedVariable{
return new MH_Type_Env (prog) ;
}
// Building a closure (using lambda) from argument list and body
static MH_EXP buildClosure (TREE args, MH_EXP exp) {
if (args.getRhs() == MH_Parser.epsilon)
return exp ;
else {
MH_EXP exp1 = buildClosure (args.getChildren()[1], exp) ;
String var = args.getChildren()[0].getValue() ;
return new MH_Exp_Impl (var, exp1) ;
}
}
// Name-closure pairs (result of processing a TermDecl).
static class Named_MH_EXP {
String name ; MH_EXP exp ;
Named_MH_EXP (String name, MH_EXP exp) {
this.name = name; this.exp = exp ;
}
}
static Named_MH_EXP typecheckDecl (TREE decl, MH_Type_Env env)
throws TypeError, UnknownVariable, DuplicatedVariable,
NameMismatchError {
// typechecks the given decl against the env,
// and returns a name-closure pair for the entity declared.
String theVar = decl.getChildren()[0].getChildren()[0].getValue();
String theVar1= decl.getChildren()[1].getChildren()[0].getValue();
if (!theVar.equals(theVar1))
throw new NameMismatchError(theVar,theVar1) ;
MH_TYPE theType =
MH_Type_Impl.convertType (decl.getChildren()[0].getChildren()[2]) ;
MH_EXP theExp =
MH_Exp_Impl.convertExp (decl.getChildren()[1].getChildren()[3]) ;
TREE theArgs = decl.getChildren()[1].getChildren()[1] ;
MH_Type_Env theEnv = new MH_Type_Env (env) ;
MH_TYPE resultType = theEnv.addArgBindings (theArgs, theType) ;
MH_TYPE expType = computeType (theExp, theEnv) ;
if (expType.equals(resultType)) {
return new Named_MH_EXP (theVar,buildClosure(theArgs,theExp));
}
else throw new TypeError ("RHS of declaration of " +
theVar + " has wrong type") ;
}
static MH_Exp_Env typecheckProg (TREE prog, MH_Type_Env env)
throws TypeError, UnknownVariable, DuplicatedVariable,
NameMismatchError {
TREE prog1 = prog ;
TreeMap treeMap = new TreeMap() ;
while (prog1.getRhs() != MH_Parser.epsilon) {
TREE theDecl = prog1.getChildren()[0] ;
Named_MH_EXP binding = typecheckDecl (theDecl, env) ;
treeMap.put (binding.name, binding.exp) ;
prog1 = prog1.getChildren()[1] ;
}
System.out.println ("Typecheck successful.") ;
return new MH_Exp_Env (treeMap) ;
}
// For testing:
public static void main (String[] args) throws Exception {
Reader reader = new BufferedReader (new FileReader (args[0])) ;
// try {
LEX_TOKEN_STREAM MH_Lexer =
new CheckedSymbolLexer (new MH_Lexer (reader)) ;
TREE prog = MH_Parser.parseTokenStream (MH_Lexer) ;
MH_Type_Env typeEnv = compileTypeEnv (prog) ;
MH_Exp_Env runEnv = typecheckProg (prog, typeEnv) ;
// } catch (Exception x) {
// System.out.println ("MH Error: " + x.getMessage()) ;
// }
}
}