-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathenv.ml
298 lines (273 loc) · 9.73 KB
/
env.ml
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
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
(*
This file contains all types and helper functions
for the scope and environement
*)
open Ast
type scope = {
(* Types declared in this scope: (type name, underlying type, used, line num) *)
mutable t: (string * typeT * bool * int) list;
(* Vars declared in this scope: (var name, type, used, line num) *)
mutable v: (string * typeT * bool * int) list;
(* funcs declared in this scope: (func name, inputs types, return type, used, line num) *)
mutable f: (string * typeT list * typeT option * bool * int) list;
}
type env = scope list
let empty_scope () = {
t = [];
v = [];
f = [];
}
;;
let list_to_string l f =
List.fold_left (fun acc e -> acc ^ f e ^ ", ") "" l
;;
let print_scope s =
let t_str = list_to_string s.t (fun (id, t, u, l) -> "(" ^ id ^ ", " ^ Prettyp.typeT_str t 0 ^ ", " ^ string_of_bool u ^ ", " ^ string_of_int l ^ ")") in
let v_str = list_to_string s.v (fun (id, t, u, l) -> "(" ^ id ^ ", " ^ Prettyp.typeT_str t 0 ^ ", " ^ string_of_bool u ^ ", " ^ string_of_int l ^ ")") in
let f_str = list_to_string s.f (fun (id, t_l, t_r, u, l) -> "(" ^ id ^ ", [" ^ (list_to_string t_l (fun t -> Prettyp.typeT_str t 0)) ^ "], " ^ (match t_r with None -> "void" | Some t -> Prettyp.typeT_str t 0) ^ ", " ^ string_of_bool u ^ ", " ^ string_of_int l ^ ")") in
print_string ("{\nT: " ^ t_str ^ "\nV: " ^ v_str ^ "\nF: " ^ f_str ^ "\n}\n")
;;
let copy_scope s =
let copied_s = empty_scope () in
copied_s.t <- s.t;
copied_s.v <- s.v;
copied_s.f <- s.f;
copied_s
;;
let copy_env env =
let rec copy_env' env' =
match env' with
| [] -> []
| s::env'' -> (copy_scope s)::(copy_env' env'')
in
copy_env' env
;;
let rec print_env env =
print_string "Printing env\n";
let rec print_env' env =
match env with
| s::env' -> print_scope s; print_env' env'
| [] -> print_string "\n"
in
print_env' env
;;
let empty_env () = [empty_scope ()]
;;
let push_scope env = (empty_scope ()) :: env
;;
(* Go through all variables, types and func of the scope and checks if any declarations were unused *)
let warn_unused s =
let rec warn_v vs =
match vs with
| [] -> ()
| (id, _, used, l)::vs' ->
if not (used) then
Exceptions.new_warning (Exceptions.Warning ("The variable " ^ id ^ " was declared and never referenced", l));
warn_v vs'
in
let rec warn_t ts =
match ts with
| [] -> ()
| (id, _, used, l)::ts' ->
if not (used) then
Exceptions.new_warning (Exceptions.Warning ("The type " ^ id ^ " was declared and never referenced", l));
warn_t ts'
in
let rec warn_f fs =
match fs with
| [] -> ()
| (id, _, _, used, l)::fs' ->
if not(id = "main") && not (used) then
Exceptions.new_warning (Exceptions.Warning ("The function " ^ id ^ " was declared and never referenced", l));
warn_f fs'
in
warn_v s.v;
warn_t s.t;
warn_f s.f
;;
let pop_scope env =
if List.length env < 2 then
failwith ("Cannot pop scope from env of lenght: " ^ string_of_int (List.length env))
else
warn_unused (List.hd env)
;;
(*
Get the type of this variable id
This will also mark the var as used
*)
let get_var_s id s =
let v = ref None in
let rec get_var' vars =
match vars with
| [] -> []
| (id', t, used, l) :: vars' when id' = id ->
v := Some t;
(id', t, true, l) :: vars'
| v'::vars' -> v'::(get_var' vars')
in
s.v <- get_var' s.v;
!v
;;
(*
Get the type signature of this function
This will mark the func as used
*)
let get_func_s id s =
let f = ref None in
let rec get_func' funcs =
match funcs with
| [] -> []
| (id', in_t, r_t, _, l) :: funcs' when id' = id ->
f := Some (in_t, r_t);
(id', in_t, r_t, true, l) :: funcs'
| v'::funcs' -> v'::(get_func' funcs')
in
s.f <- get_func' s.f;
!f
;;
(* Get the type from an id in the scope *)
let get_type_s id s =
let t = ref None in
let rec get_type' types =
match types with
| [] -> []
| (id', t', _, l) :: types' when id' = id ->
t := Some t';
(id', t', true, l) :: types'
| v'::types' -> v'::(get_type' types')
in
s.t <- get_type' s.t;
!t
;;
(* Throw if the id exists in the scop and was not what we thought it was *)
let check_others s id expected l =
let def_l = ref 0 in
let var_exist = List.exists (fun (id', _, _, l') -> if id' = id then (def_l := l'; true) else false) s.v in
let type_exists = List.exists (fun (id', _, _, l') -> if id' = id then (def_l := l'; true) else false) s.t in
let func_exists = List.exists (fun (id', _, _, _, l') -> if id' = id then (def_l := l'; true) else false) s.f in
if var_exist then
raise (Exceptions.SyntaxError (id ^ " is not a " ^ expected ^ ", it is a variable declared on line " ^ string_of_int (!def_l), Some l))
else if type_exists then
raise (Exceptions.SyntaxError (id ^ " is not a " ^ expected ^ ", it is a type declared on line " ^ string_of_int (!def_l), Some l))
else if func_exists then
raise (Exceptions.SyntaxError (id ^ " is not a " ^ expected ^ ", it is a function declared on line " ^ string_of_int (!def_l), Some l))
else ()
;;
(* Get the type from an id in the env *)
let rec get_type id env l =
match env with
| [] -> raise (Exceptions.SyntaxError ("The type '" ^ id ^ "' was never defined", Some l))
| s::env' ->
let t_opt = get_type_s id s in
begin match t_opt with
| None -> check_others s id "type" l; get_type id env' l
| Some t -> t
end
;;
(* Get the type of a variable in the environement *)
let rec get_var id env l =
match env with
| [] -> raise (Exceptions.SyntaxError ("The variable '" ^ id ^ "' was never defined", Some l))
| s::env' ->
let v_opt = get_var_s id s in
begin match v_opt with
| None -> check_others s id "variable" l; get_var id env' l
| Some t -> t
end
;;
(* Get the function input and output types from the environement *)
let rec get_func id env l =
match env with
| [] -> raise (Exceptions.SyntaxError ("The function '" ^ id ^ "' was never defined", Some l))
| s::env' ->
let f_opt = get_func_s id s in
begin match f_opt with
| None -> check_others s id "function" l; get_func id env' l
| Some t -> t
end
;;
(* Check if an ID already exists in this scope, if so thow an exception *)
let check_exists s id l =
let def_l = ref 0 in
let var_exist = List.exists (fun (id', _, _, l') -> if id' = id then (def_l := l'; true) else false) s.v in
let type_exists = List.exists (fun (id', _, _, l') -> if id' = id then (def_l := l'; true) else false) s.t in
let func_exists = List.exists (fun (id', _, _, _, l') -> if id' = id then (def_l := l'; true) else false) s.f in
if var_exist then
raise (Exceptions.SyntaxError ("A variable with name '" ^ id ^ "' was already defined in this scope on line " ^ string_of_int (!def_l), Some l))
else if type_exists then
raise (Exceptions.SyntaxError ("A type with name '" ^ id ^ "' was already defined in this scope on line " ^ string_of_int (!def_l), Some l))
else if func_exists then
raise (Exceptions.SyntaxError ("A function with name '" ^ id ^ "' was already defined in this scope on line " ^ string_of_int (!def_l), Some l))
else ()
;;
(*
Create a new scope for the function body, add the arguments to the function in the scope
and return the new extended environemnt
*)
let open_function_scope env f =
match f with
| FuncDecl (name, inputs, _, _, l) ->
let func_scope = empty_scope () in
let rec build_inputs_scope acc inputs =
match inputs with
| [] -> acc
| (id, t)::inputs' ->
if List.exists (fun (id', _, _, _) -> id' = id) acc then
raise (Exceptions.SyntaxError ("Multiple function arguments with the same name '" ^ id ^ "' in declaration of '" ^ name ^ "'", Some l))
else
build_inputs_scope ((id, t, false, l)::acc) inputs'
in
func_scope.v <- build_inputs_scope [] inputs;
func_scope :: env
;;
let var_decl env v_decl =
let v_tup = match v_decl with
| VarDeclTypeInit (t, id, _, l) -> (id, t, false, l)
| VarDeclTypeNoInit (t, id, l) -> (id, t, false, l)
| VarDeclNoTypeInit (id, e, l) -> failwith ("Line " ^ string_of_int l ^ "\nCannot add variable to env without knowing type")
in
let (id, _, _, l) = v_tup in
if List.length env = 0 then
failwith ("Line " ^ string_of_int l ^ "\nThe environement is empty so we cannot declare variable '" ^ id ^ "'")
else
let s = List.hd env in
check_exists s id l;
s.v <- (v_tup :: s.v)
;;
let type_decl env t_decl =
let t_tup = match t_decl with
| TypeDecl (t, id, l) ->
let underlying_type = begin match t with
| DefinedType (id', _, _) -> get_type id' env l
| _ -> t
end in
(id, DefinedType (id, Some underlying_type, l), false, l)
in
let (id, _, _, l) = t_tup in
if List.length env = 0 then
failwith ("Line " ^ string_of_int l ^ "\nThe environement is empty so we cannot declare type '" ^ id ^ "'")
else
let s = List.hd env in
check_exists s id l;
s.t <- (t_tup :: s.t)
;;
let func_decl env f_decl =
let f_tup = match f_decl with
| FuncDecl (id, inputs, out_opt, _, l) ->
match out_opt with
| Some (DefinedType (_, None, l)) -> failwith ("Line " ^ string_of_int l ^ "\nDefined type in function sig output not resolved at weeding time")
| _ ->
(id, List.map (fun (_, t) ->
match t with
| DefinedType (_, None, l) -> failwith ("Line " ^ string_of_int l ^ "\nDefined type in function sig inputs not resolved at weeding time")
| _ -> t
) inputs, out_opt, false, l)
in
let (id, _, _, _, l) = f_tup in
if List.length env = 0 then
failwith ("Line " ^ string_of_int l ^ "\nThe environement is empty so we cannot declare function '" ^ id ^ "'")
else
let s = List.hd env in
check_exists s id l;
s.f <- (f_tup :: s.f)
;;