-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfbtype.ml
More file actions
280 lines (243 loc) · 8.51 KB
/
fbtype.ml
File metadata and controls
280 lines (243 loc) · 8.51 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
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
open Fbast;;
(*
* If you would like typechecking to be enabled by your interpreter by default,
* then change the following value to true. Whether or not typechecking is
* enabled by default, you can explicitly enable it or disable it using
* command-line arguments.
*)
let typecheck_default_enabled = true;;
(*
* Replace this with your typechecker code. Your code should not throw the
* following exception; if you need to raise an exception, create your own
* exception type here.
*)
(* let typecheck e = raise Fbdk.TypecheckerNotImplementedException;; *)
exception TypeMismatch;;
exception TypeError;;
exception CycleException;;
let rec union lst1 lst2 =
match lst1 with
| [] -> lst2
| (t1, t2) :: tl -> if (List.mem (t1, t2) lst2) || (List.mem (t2, t1) lst2) then
union tl lst2
else
union tl ((t1, t2) :: lst2)
;;
let rec createFreshTypeVar nameList code =
if List.mem (Char.escaped (Char.chr code)) nameList then
createFreshTypeVar nameList (code + 1)
else
Char.escaped (Char.chr code)
;;
let rec merge (type1, type2) lst =
match lst with
| [] -> []
| (type3, type4) :: tl ->
if (type1 <> type3 || type2 <> type4) then
( if type2 = type3 then
union [(type1, type4)] (merge (type1, type2) tl)
else if type2 = type4 then
union [(type1, type3)] (merge (type1, type2) tl)
else if type1 = type4 then
union [(type2, type3)] (merge (type1, type2) tl)
else if type1 = type3 then
union [(type2, type4)] (merge (type1, type2) tl)
else
merge (type1, type2) tl )
else
merge (type1, type2) tl
;;
let rec addEquation lst =
match lst with
| [] -> []
| hd :: tl ->
let lst1 =
( match hd with
| (TArrow (type1, type2), TArrow (type3, type4)) ->
[(type1, type3); (type2, type4)]
| _ -> [] )
in
let lst2 = merge hd tl in
union (union [hd] (union lst1 lst2)) (addEquation tl)
;;
let isConsistent lst =
let rec isSameType pair =
match pair with
| (TVar _, _) | (_, TVar _) -> true
| (TInt, TInt) | (TBool, TBool) -> true
| (TInt, _) | (_, TInt) | (TBool, _) | (_, TBool) -> false
| (TArrow (type1, type2), TArrow (type3, type4)) ->
(isSameType (type1, type3)) && (isSameType (type2, type4))
in
List.for_all isSameType lst
;;
let rec closure lst =
let lst1 = addEquation lst in
let lst2 = addEquation lst1 in
if (List.length lst1) = (List.length lst2) then
lst1
else
closure lst2
;;
let rec replace alpha eq =
match alpha with
| TInt | TBool -> alpha
| TArrow (type1, type2) ->
TArrow ((replace type1 eq), (replace type2 eq))
| TVar t ->
( match eq with
| (TVar a, TVar b) ->
if (alpha = TVar a) && a < b then
TVar b
else if (alpha = TVar b) && a > b then
TVar a
else
alpha
| (TVar a, b) | (b, TVar a) ->
if (alpha = TVar a) then
replace b eq
else
alpha
| _ -> alpha )
;;
let rec solveEquation alpha lst =
match lst with
| [] -> alpha
| hd :: tl ->
solveEquation (replace alpha hd) tl
;;
let rec solve alpha lst =
let a = solveEquation alpha lst in
let b = solveEquation a lst in
if a <> b then
solve a lst
else
a
;;
let rec collectVar e =
match e with
| TInt | TBool -> []
| TArrow (a, b) ->
(collectVar a) @ (collectVar b)
| TVar a ->
[a]
;;
let rec findAdj node lst =
match lst with
| [] -> []
| (a, b) :: tl ->
if (TVar node) = a then
(match b with
| TArrow(_, _) ->
(collectVar b) @ (findAdj node tl)
| _ ->
findAdj node tl )
else
findAdj node tl
;;
let rec dfs node eq visitedList =
if (List.mem node visitedList) then
false
else
(let adjList = findAdj node eq in
if (List.exists (fun x -> List.mem x (node::visitedList)) adjList) then
false
else
dfsList adjList eq (node :: visitedList) )
and
dfsList lst eq visitedList =
match lst with
| [] -> true
| hd :: tl ->
(dfs hd eq visitedList) && (dfsList tl eq visitedList)
;;
let rec checkCycle eq nameList =
match nameList with
| [] -> true
| hd :: tl ->
(dfs hd eq []) && (checkCycle eq tl)
;;
let rec recTypeCheck gamma e nameList =
match e with
| Int _ -> (TInt, [], nameList)
| Bool _ -> (TBool, [], nameList)
| Var ident ->
if (List.mem_assoc ident gamma) then
((List.assoc ident gamma), [], nameList)
else
raise TypeError
| Plus (e1, e2) | Minus (e1, e2) ->
let (type1, eq1, nameList1) = recTypeCheck gamma e1 nameList in
let (type2, eq2, nameList2) = recTypeCheck gamma e2 nameList1 in
(TInt, (union (union eq1 eq2) [(type1, TInt); (type2, TInt)]), nameList2)
| Equal (e1, e2) ->
let (type1, eq1, nameList1) = recTypeCheck gamma e1 nameList in
let (type2, eq2, nameList2) = recTypeCheck gamma e2 nameList1 in
(TBool, (union (union eq1 eq2) [(type1, TInt); (type2, TInt)]), nameList2)
| And (e1, e2) | Or (e1, e2) ->
let (type1, eq1, nameList1) = recTypeCheck gamma e1 nameList in
let (type2, eq2, nameList2) = recTypeCheck gamma e2 nameList1 in
(TBool, (union (union eq1 eq2) [(type1, TBool); (type2, TBool)]), nameList2)
| Not e ->
let (type1, eq1, nameList1) = recTypeCheck gamma e nameList in
(TBool, (union eq1 [(type1, TBool)]), nameList1 )
| If (e1, e2, e3) ->
let newName = createFreshTypeVar nameList 97 in
let alpha = TVar newName in
let (type1, eq1, nameList1) = recTypeCheck gamma e1 (newName :: nameList) in
let (type2, eq2, nameList2) = recTypeCheck gamma e2 nameList1 in
let (type3, eq3, nameList3) = recTypeCheck gamma e3 nameList2 in
(alpha,
(union (union (union eq1 eq2) eq3)
[(type1, TBool); (type2, alpha); (type3, alpha)] ),
nameList3
)
| Function (Ident i, e) ->
let newName = createFreshTypeVar (i :: nameList) 97 in
let alpha = TVar newName in
let newGamma = if (List.mem (Ident i, alpha) gamma) then gamma
else ((Ident i, alpha) :: gamma) in
let (type1, eq1, nameList1) = recTypeCheck newGamma e (newName :: i :: nameList) in
(TArrow (alpha, type1), eq1, nameList1)
| Appl (e1, e2) ->
let newName = createFreshTypeVar nameList 97 in
let alpha = TVar newName in
let (type1, eq1, nameList1) = recTypeCheck gamma e1 (newName :: nameList) in
let (type2, eq2, nameList2) = recTypeCheck gamma e2 nameList1 in
(alpha, (union (union eq1 eq2) [(type1, TArrow (type2, alpha))]), nameList2 )
| _ -> raise TypeError
;;
let rec recordName e nameList =
match e with
| TInt | TBool -> nameList
| TArrow (a, b) ->
recordName b (recordName a nameList)
| TVar a ->
if (List.mem_assoc a nameList) then
nameList
else
(a, Char.escaped (Char.chr (List.length nameList + 97 ))) :: nameList
;;
let rec changeName e nameList =
match e with
| TInt | TBool -> e
| TArrow (a, b) ->
TArrow (changeName a nameList, changeName b nameList)
| TVar a ->
if (List.mem_assoc a nameList) then
TVar (List.assoc a nameList)
else
e
;;
let typecheck e =
let (t, eq, nameList) = recTypeCheck [] e [] in
let lst = closure eq in
if isConsistent lst then
if checkCycle lst nameList then
let ttype = solve t lst in
changeName ttype (recordName ttype [])
else
raise CycleException
else
raise TypeMismatch
;;