Test uncaught throw, catchless try* . Fix 46 impls.
[jackhill/mal.git] / ada / step5_tco.adb
CommitLineData
a3c4ba44
CM
1with Ada.Command_Line;
2with Ada.Exceptions;
3with Ada.Text_IO;
a3c4ba44
CM
4with Core;
5with Envs;
18e21187 6with Eval_Callback;
a3c4ba44
CM
7with Printer;
8with Reader;
18e21187 9with Smart_Pointers;
a3c4ba44
CM
10with Types;
11
c3244bcf 12procedure Step5_TCO is
a3c4ba44 13
18e21187
CM
14 use Types;
15
b5bad5ea
CM
16 -- Forward declaration of Eval.
17 function Eval (AParam : Mal_Handle; AnEnv : Envs.Env_Handle) return Mal_Handle;
18
19 Debug : Boolean := False;
20
21
a3c4ba44
CM
22 function Read (Param : String) return Types.Mal_Handle is
23 begin
24 return Reader.Read_Str (Param);
25 end Read;
26
27
18e21187
CM
28 function Def_Fn (Args : List_Mal_Type; Env : Envs.Env_Handle)
29 return Mal_Handle is
30 Name, Fn_Body, Res : Mal_Handle;
31 begin
32 Name := Car (Args);
33 pragma Assert (Deref (Name).Sym_Type = Sym,
34 "Def_Fn: expected atom as name");
b5bad5ea 35 Fn_Body := Nth (Args, 1);
18e21187 36 Res := Eval (Fn_Body, Env);
b5bad5ea 37 Envs.Set (Env, Deref_Sym (Name).Get_Sym, Res);
18e21187
CM
38 return Res;
39 end Def_Fn;
40
41
42 function Eval_As_Boolean (MH : Mal_Handle) return Boolean is
43 Res : Boolean;
44 begin
45 case Deref (MH).Sym_Type is
311cbfc0 46 when Bool =>
18e21187 47 Res := Deref_Bool (MH).Get_Bool;
8083b525
CM
48 when Nil =>
49 return False;
18e21187
CM
50-- when List =>
51-- declare
52-- L : List_Mal_Type;
53-- begin
54-- L := Deref_List (MH).all;
55-- Res := not Is_Null (L);
56-- end;
57 when others => -- Everything else
58 Res := True;
59 end case;
60 return Res;
61 end Eval_As_Boolean;
62
63
64 function Eval_Ast
65 (Ast : Mal_Handle; Env : Envs.Env_Handle)
66 return Mal_Handle is
67
68 function Call_Eval (A : Mal_Handle) return Mal_Handle is
69 begin
70 return Eval (A, Env);
71 end Call_Eval;
72
73 begin
74
75 case Deref (Ast).Sym_Type is
76
77 when Sym =>
78
79 declare
80 Sym : Mal_String := Deref_Sym (Ast).Get_Sym;
81 begin
82 -- if keyword, return it. Otherwise look it up in the environment.
83 if Sym(1) = ':' then
84 return Ast;
85 else
86 return Envs.Get (Env, Sym);
87 end if;
88 exception
89 when Envs.Not_Found =>
564a4525 90 raise Envs.Not_Found with ("'" & Sym & "' not found");
18e21187
CM
91 end;
92
93 when List =>
94
95 return Map (Call_Eval'Unrestricted_Access, Deref_List_Class (Ast).all);
96
18e21187
CM
97 when others => return Ast;
98
99 end case;
100
101 end Eval_Ast;
102
103
104 function Eval (AParam : Mal_Handle; AnEnv : Envs.Env_Handle)
105 return Mal_Handle is
106 Param : Mal_Handle;
107 Env : Envs.Env_Handle;
108 First_Param, Rest_Params : Mal_Handle;
109 Rest_List, Param_List : List_Mal_Type;
110 begin
111
112 Param := AParam;
113 Env := AnEnv;
114
b5bad5ea 115 <<Tail_Call_Opt>>
18e21187
CM
116
117 if Debug then
118 Ada.Text_IO.Put_Line ("Evaling " & Deref (Param).To_String);
119 end if;
120
121 if Deref (Param).Sym_Type = List and then
122 Deref_List (Param).Get_List_Type = List_List then
123
124 Param_List := Deref_List (Param).all;
74a87bd5
CM
125
126 -- Deal with empty list..
127 if Param_List.Length = 0 then
128 return Param;
129 end if;
130
18e21187
CM
131 First_Param := Car (Param_List);
132 Rest_Params := Cdr (Param_List);
133 Rest_List := Deref_List (Rest_Params).all;
134
135 if Deref (First_Param).Sym_Type = Sym and then
136 Deref_Sym (First_Param).Get_Sym = "def!" then
137 return Def_Fn (Rest_List, Env);
138 elsif Deref (First_Param).Sym_Type = Sym and then
139 Deref_Sym (First_Param).Get_Sym = "let*" then
140 declare
141 Defs, Expr, Res : Mal_Handle;
142 E : Envs.Env_Handle;
143 begin
144 E := Envs.New_Env (Env);
145 Defs := Car (Rest_List);
146 Deref_List_Class (Defs).Add_Defs (E);
147 Expr := Car (Deref_List (Cdr (Rest_List)).all);
148 Param := Expr;
149 Env := E;
150 goto Tail_Call_Opt;
151 -- was:
152 -- Res := Eval (Expr, E);
153 -- return Res;
154 end;
155 elsif Deref (First_Param).Sym_Type = Sym and then
156 Deref_Sym (First_Param).Get_Sym = "do" then
157 declare
158 D : List_Mal_Type;
159 E : Mal_Handle;
160 begin
161
162 if Debug then
163 Ada.Text_IO.Put_Line ("Do-ing " & To_String (Rest_List));
164 end if;
165
166 if Is_Null (Rest_List) then
167 return Rest_Params;
168 end if;
169
170 -- Loop processes Evals all but last entry
171 D := Rest_List;
172 loop
173 E := Car (D);
174 D := Deref_List (Cdr (D)).all;
175 exit when Is_Null (D);
176 E := Eval (E, Env);
177 end loop;
178
179 Param := E;
180 goto Tail_Call_Opt;
181
182 end;
183 elsif Deref (First_Param).Sym_Type = Sym and then
184 Deref_Sym (First_Param).Get_Sym = "if" then
185 declare
186 Args : List_Mal_Type := Rest_List;
187
188 Cond, True_Part, False_Part : Mal_Handle;
189 Cond_Bool : Boolean;
190 pragma Assert (Length (Args) = 2 or Length (Args) = 3,
191 "If_Processing: not 2 or 3 parameters");
192 L : List_Mal_Type;
193 begin
194
195 Cond := Eval (Car (Args), Env);
196
197 Cond_Bool := Eval_As_Boolean (Cond);
198
199 if Cond_Bool then
200 L := Deref_List (Cdr (Args)).all;
201
202 Param := Car (L);
203 goto Tail_Call_Opt;
204 -- was: return Eval (Car (L), Env);
205 else
206 if Length (Args) = 3 then
207 L := Deref_List (Cdr (Args)).all;
208 L := Deref_List (Cdr (L)).all;
209
210 Param := Car (L);
211 goto Tail_Call_Opt;
212 -- was: return Eval (Car (L), Env);
213 else
8083b525 214 return New_Nil_Mal_Type;
18e21187
CM
215 end if;
216 end if;
217 end;
218
219 elsif Deref (First_Param).Sym_Type = Sym and then
220 Deref_Sym (First_Param).Get_Sym = "fn*" then
221
222 return New_Lambda_Mal_Type
223 (Params => Car (Rest_List),
224 Expr => Nth (Rest_List, 1),
225 Env => Env);
226
227 else
228
229 -- The APPLY section.
230 declare
231 Evaled_H : Mal_Handle;
232 begin
233 Evaled_H := Eval_Ast (Param, Env);
234
235 Param_List := Deref_List (Evaled_H).all;
236
237 First_Param := Car (Param_List);
238 Rest_Params := Cdr (Param_List);
239 Rest_List := Deref_List (Rest_Params).all;
240
241 if Deref (First_Param).Sym_Type = Func then
1c28e560 242 return Call_Func (Deref_Func (First_Param).all, Rest_Params);
18e21187
CM
243 elsif Deref (First_Param).Sym_Type = Lambda then
244 declare
245
246 L : Lambda_Mal_Type;
247 E : Envs.Env_Handle;
248 Param_Names : List_Mal_Type;
249 Res : Mal_Handle;
250
251 begin
252
253 L := Deref_Lambda (First_Param).all;
254 E := Envs.New_Env (L.Get_Env);
255
256 Param_Names := Deref_List (L.Get_Params).all;
257
258 if Envs.Bind (E, Param_Names, Deref_List (Rest_Params).all) then
259
260 Param := L.Get_Expr;
261 Env := E;
262 goto Tail_Call_Opt;
311cbfc0 263 -- was: return Eval (L.Get_Expr, E);
18e21187
CM
264
265 else
266
267 raise Mal_Exception with "Bind failed in Apply";
268
269 end if;
270
271 end;
272
273 else -- neither a Lambda or a Func
274 raise Mal_Exception;
275 end if;
276
277 end;
278
279 end if;
280
dd7a4f55 281 else -- Not a List_List
18e21187
CM
282
283 return Eval_Ast (Param, Env);
284
285 end if;
286
287 end Eval;
a3c4ba44
CM
288
289
290 function Print (Param : Types.Mal_Handle) return String is
291 begin
292 return Printer.Pr_Str (Param);
293 end Print;
294
b5bad5ea 295 function Rep (Param : String; Env : Envs.Env_Handle) return String is
dd7a4f55 296 AST, Evaluated_AST : Types.Mal_Handle;
a3c4ba44
CM
297 begin
298
dd7a4f55 299 AST := Read (Param);
a3c4ba44 300
dd7a4f55
JM
301 if Types.Is_Null (AST) then
302 return "";
303 else
304 Evaluated_AST := Eval (AST, Env);
305 return Print (Evaluated_AST);
306 end if;
a3c4ba44 307
311cbfc0 308 end Rep;
a3c4ba44 309
dd7a4f55 310
b5bad5ea
CM
311 Repl_Env : Envs.Env_Handle;
312
313
314 -- These two ops use Repl_Env directly.
315
316
317 procedure RE (Str : Mal_String) is
318 Discarded : Mal_Handle;
319 begin
320 Discarded := Eval (Read (Str), Repl_Env);
321 end RE;
322
323
324 function Do_Eval (Rest_Handle : Mal_Handle; Env : Envs.Env_Handle)
325 return Types.Mal_Handle is
326 First_Param : Mal_Handle;
327 Rest_List : Types.List_Mal_Type;
328 begin
329 Rest_List := Deref_List (Rest_Handle).all;
330 First_Param := Car (Rest_List);
331 return Eval_Callback.Eval.all (First_Param, Repl_Env);
332 end Do_Eval;
333
a3c4ba44
CM
334 Cmd_Args : Natural;
335
336begin
337
365f0253
CM
338 -- Save a function pointer back to the Eval function.
339 -- Can't use 'Access here because of Ada rules but 'Unrestricted_Access is OK
340 -- as we know Eval will be in scope for the lifetime of the program.
341 Eval_Callback.Eval := Eval'Unrestricted_Access;
342
a3c4ba44
CM
343 Cmd_Args := 0;
344 while Ada.Command_Line.Argument_Count > Cmd_Args loop
345 Cmd_Args := Cmd_Args + 1;
346 if Ada.Command_Line.Argument (Cmd_Args) = "-d" then
18e21187 347 Debug := True;
a3c4ba44
CM
348 elsif Ada.Command_Line.Argument (Cmd_Args) = "-e" then
349 Envs.Debug := True;
350 end if;
351 end loop;
352
b5bad5ea
CM
353 Repl_Env := Envs.New_Env;
354
355 Core.Init (Repl_Env);
a3c4ba44 356
b5bad5ea 357 RE ("(def! not (fn* (a) (if a false true)))");
a3c4ba44
CM
358
359 loop
360 begin
361 Ada.Text_IO.Put ("user> ");
311cbfc0
CM
362 exit when Ada.Text_IO.End_Of_File;
363 Ada.Text_IO.Put_Line (Rep (Ada.Text_IO.Get_Line, Repl_Env));
a3c4ba44 364 exception
a3c4ba44
CM
365 when E : others =>
366 Ada.Text_IO.Put_Line
367 (Ada.Text_IO.Standard_Error,
dd7a4f55
JM
368 "Error: " & Ada.Exceptions.Exception_Information (E));
369 if Types.Mal_Exception_Value /= Smart_Pointers.Null_Smart_Pointer then
370 Ada.Text_IO.Put_Line
371 (Ada.Text_IO.Standard_Error,
372 Printer.Pr_Str (Types.Mal_Exception_Value));
373 Types.Mal_Exception_Value := Smart_Pointers.Null_Smart_Pointer;
374 end if;
a3c4ba44
CM
375 end;
376 end loop;
c3244bcf 377end Step5_TCO;