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