-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMH_Typechecker.java
More file actions
236 lines (203 loc) · 8.4 KB
/
Copy pathMH_Typechecker.java
File metadata and controls
236 lines (203 loc) · 8.4 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
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
223
224
225
226
227
228
229
230
231
232
233
234
235
236
// File: MH_Typechecker.java
// 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_Parser1 = MH_Type_Impl.MH_Parser1 ;
// 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 {
// add code here
if (exp.isVAR()) {
return env.typeOf(exp.value());
}
else if (exp.isNUM()){
return IntegerType;
}
else if (exp.isBOOLEAN()){
return BoolType;
}
else if (exp.isAPP()) {
//Check if it is a function
if (computeType(exp.first(), env).isFun()) {
// Then compare if the argument type matches the declared type
if (computeType(exp.first(), env).left().equals(computeType(exp.second(), env))) {
return computeType(exp.first(), env).right();
} else {
throw new TypeError("Argument type does not match declared type.");
}
} else {
throw new TypeError("Not a function.");
}
}
else if (exp.isINFIX()) {
// valid operands: + - == <=
// inputs have to be integers
// for + - output is integer
// for == <= output is boolean
// do it recursively (until you reach a base case)
if (computeType(exp.first(), env).equals(IntegerType) && computeType(exp.first(), env).equals(IntegerType)) {
if (exp.infixOp().equals("+") || exp.infixOp().equals("-")) {
return IntegerType;
} else if (exp.infixOp().equals("==") || exp.infixOp().equals("<=")) {
return BoolType;
} else {
throw new TypeError("Wrong infix operator.");
}
} else {
throw new TypeError("Operation parameters should only be Integers.");
}
}
else if (exp.isIF()) {
// for if the computeType(exp.first(), env) has to be boolean
if (computeType(exp.first(), env).isBool()) {
// exp.second() and exp.third() must be of the same type
MH_TYPE then_type = computeType(exp.second(), env);
MH_TYPE else_type = computeType(exp.third(), env);
if (then_type.equals(else_type)) {
return then_type;
} else {
throw new TypeError("Type of then and else should be the same.");
}
} else {
throw new TypeError("If condition should be a boolean.");
}
}
else {
return null;
}
}
// Type environments:
interface TYPE_ENV {
MH_TYPE typeOf (String var) throws UnknownVariable ;
}
static class MH_Type_Env implements TYPE_ENV {
TreeMap<String,MH_TYPE> 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 = new TreeMap<String,MH_TYPE>() ;
this.env.putAll(given.env) ;
// Old version (causes unchecked typecast warning):
// this.env = (TreeMap<String,MH_TYPE>)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<String,MH_TYPE>() ;
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.isFun()) {
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<String,MH_EXP> treeMap = new TreeMap<String,MH_EXP>() ;
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_Parser1.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()) ;
// }
}
}