Ada: fix Makefile and re-write early steps to remove some dependencies
[jackhill/mal.git] / ada / step7_quote.adb
CommitLineData
ebb6e9d3
CM
1with Ada.Command_Line;
2with Ada.Exceptions;
3with Ada.Text_IO;
4with Ada.IO_Exceptions;
5with Core;
6with Envs;
18e21187 7with Eval_Callback;
ebb6e9d3
CM
8with Printer;
9with Reader;
18e21187 10with Smart_Pointers;
ebb6e9d3
CM
11with Types;
12
13procedure Step7_Quote is
14
18e21187
CM
15 use Types;
16
18e21187
CM
17 function Eval (AParam : Types.Mal_Handle; AnEnv : Envs.Env_Handle)
18 return Types.Mal_Handle;
19
20 Debug : Boolean := False;
21
22
b5bad5ea
CM
23 function Read (Param : String) return Types.Mal_Handle is
24 begin
25 return Reader.Read_Str (Param);
26 end Read;
27
18e21187
CM
28
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 =>
91 raise Envs.Not_Found with (" '" & Sym & "' not found ");
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
106
107 function Quasi_Quote_Processing (Param : Mal_Handle) return Mal_Handle is
108 Res, First_Elem, FE_0 : Mal_Handle;
109 L : List_Ptr;
110 D_Ptr, Ast_P : List_Class_Ptr;
111 begin
112
113 if Debug then
114 Ada.Text_IO.Put_Line ("QuasiQt " & Deref (Param).To_String);
115 end if;
116
117 -- Create a New List for the result...
118 Res := New_List_Mal_Type (List_List);
119 L := Deref_List (Res);
120
121 -- This is the equivalent of Is_Pair
122 if Deref (Param).Sym_Type /= List or else
123 Is_Null (Deref_List_Class (Param).all) then
124
125 -- return a new list containing: a symbol named "quote" and ast.
126 L.Append (New_Symbol_Mal_Type ("quote"));
127 L.Append (Param);
128 return Res;
129
130 end if;
131
132 -- Ast is a non-empty list at this point.
133
134 Ast_P := Deref_List_Class (Param);
135
136 First_Elem := Car (Ast_P.all);
137
138 -- if the first element of ast is a symbol named "unquote":
139 if Deref (First_Elem).Sym_Type = Sym and then
140 Deref_Sym (First_Elem).Get_Sym = "unquote" then
141
142 -- return the second element of ast.`
143 D_Ptr := Deref_List_Class (Cdr (Ast_P.all));
144 return Car (D_Ptr.all);
145
146 end if;
147
148 -- if the first element of first element of `ast` (`ast[0][0]`)
149 -- is a symbol named "splice-unquote"
150 if Deref (First_Elem).Sym_Type = List and then
151 not Is_Null (Deref_List_Class (First_Elem).all) then
152
153 D_Ptr := Deref_List_Class (First_Elem);
154 FE_0 := Car (D_Ptr.all);
155
156 if Deref (FE_0).Sym_Type = Sym and then
157 Deref_Sym (FE_0).Get_Sym = "splice-unquote" then
158
159 -- return a new list containing: a symbol named "concat",
160 L.Append (New_Symbol_Mal_Type ("concat"));
161
162 -- the second element of first element of ast (ast[0][1]),
163 D_Ptr := Deref_List_Class (Cdr (D_Ptr.all));
164 L.Append (Car (D_Ptr.all));
165
166 -- and the result of calling quasiquote with
167 -- the second through last element of ast.
168 L.Append (Quasi_Quote_Processing (Cdr (Ast_P.all)));
169
170 return Res;
171
172 end if;
173
174 end if;
175
176 -- otherwise: return a new list containing: a symbol named "cons",
177 L.Append (New_Symbol_Mal_Type ("cons"));
178
179 -- the result of calling quasiquote on first element of ast (ast[0]),
180 L.Append (Quasi_Quote_Processing (Car (Ast_P.all)));
181
182 -- and result of calling quasiquote with the second through last element of ast.
183 L.Append (Quasi_Quote_Processing (Cdr (Ast_P.all)));
184
185 return Res;
186
187 end Quasi_Quote_Processing;
188
189
190 function Eval (AParam : Mal_Handle; AnEnv : Envs.Env_Handle)
191 return Mal_Handle is
192 Param : Mal_Handle;
193 Env : Envs.Env_Handle;
194 First_Param, Rest_Params : Mal_Handle;
195 Rest_List, Param_List : List_Mal_Type;
196 begin
197
198 Param := AParam;
199 Env := AnEnv;
200
b5bad5ea 201 <<Tail_Call_Opt>>
18e21187
CM
202
203 if Debug then
204 Ada.Text_IO.Put_Line ("Evaling " & Deref (Param).To_String);
205 end if;
206
207 if Deref (Param).Sym_Type = List and then
208 Deref_List (Param).Get_List_Type = List_List then
209
210 Param_List := Deref_List (Param).all;
211 First_Param := Car (Param_List);
212 Rest_Params := Cdr (Param_List);
213 Rest_List := Deref_List (Rest_Params).all;
214
215 if Deref (First_Param).Sym_Type = Sym and then
216 Deref_Sym (First_Param).Get_Sym = "def!" then
217 return Def_Fn (Rest_List, Env);
218 elsif Deref (First_Param).Sym_Type = Sym and then
219 Deref_Sym (First_Param).Get_Sym = "let*" then
220 declare
221 Defs, Expr, Res : Mal_Handle;
222 E : Envs.Env_Handle;
223 begin
224 E := Envs.New_Env (Env);
225 Defs := Car (Rest_List);
226 Deref_List_Class (Defs).Add_Defs (E);
227 Expr := Car (Deref_List (Cdr (Rest_List)).all);
228 Param := Expr;
229 Env := E;
230 goto Tail_Call_Opt;
231 -- was:
232 -- Res := Eval (Expr, E);
233 -- return Res;
234 end;
235 elsif Deref (First_Param).Sym_Type = Sym and then
236 Deref_Sym (First_Param).Get_Sym = "do" then
237 declare
238 D : List_Mal_Type;
239 E : Mal_Handle;
240 begin
241
242 if Debug then
243 Ada.Text_IO.Put_Line ("Do-ing " & To_String (Rest_List));
244 end if;
245
246 if Is_Null (Rest_List) then
247 return Rest_Params;
248 end if;
249
250 -- Loop processes Evals all but last entry
251 D := Rest_List;
252 loop
253 E := Car (D);
254 D := Deref_List (Cdr (D)).all;
255 exit when Is_Null (D);
256 E := Eval (E, Env);
257 end loop;
258
259 Param := E;
260 goto Tail_Call_Opt;
261
262 end;
263 elsif Deref (First_Param).Sym_Type = Sym and then
264 Deref_Sym (First_Param).Get_Sym = "if" then
265 declare
266 Args : List_Mal_Type := Rest_List;
267
268 Cond, True_Part, False_Part : Mal_Handle;
269 Cond_Bool : Boolean;
270 pragma Assert (Length (Args) = 2 or Length (Args) = 3,
271 "If_Processing: not 2 or 3 parameters");
272 L : List_Mal_Type;
273 begin
274
275 Cond := Eval (Car (Args), Env);
276
277 Cond_Bool := Eval_As_Boolean (Cond);
278
279 if Cond_Bool then
280 L := Deref_List (Cdr (Args)).all;
281
282 Param := Car (L);
283 goto Tail_Call_Opt;
284 -- was: return Eval (Car (L), Env);
285 else
286 if Length (Args) = 3 then
287 L := Deref_List (Cdr (Args)).all;
288 L := Deref_List (Cdr (L)).all;
289
290 Param := Car (L);
291 goto Tail_Call_Opt;
292 -- was: return Eval (Car (L), Env);
293 else
8083b525 294 return New_Nil_Mal_Type;
18e21187
CM
295 end if;
296 end if;
297 end;
298
299 elsif Deref (First_Param).Sym_Type = Sym and then
300 Deref_Sym (First_Param).Get_Sym = "fn*" then
301
302 return New_Lambda_Mal_Type
303 (Params => Car (Rest_List),
304 Expr => Nth (Rest_List, 1),
305 Env => Env);
306
307 elsif Deref (First_Param).Sym_Type = Sym and then
308 Deref_Sym (First_Param).Get_Sym = "quote" then
309
310 return Car (Rest_List);
311
312 elsif Deref (First_Param).Sym_Type = Sym and then
313 Deref_Sym (First_Param).Get_Sym = "quasiquote" then
314
315 Param := Quasi_Quote_Processing (Car (Rest_List));
316 goto Tail_Call_Opt;
317
318 else
319
320 -- The APPLY section.
321 declare
322 Evaled_H : Mal_Handle;
323 begin
324 Evaled_H := Eval_Ast (Param, Env);
325
326 Param_List := Deref_List (Evaled_H).all;
327
328 First_Param := Car (Param_List);
329 Rest_Params := Cdr (Param_List);
330 Rest_List := Deref_List (Rest_Params).all;
331
332 if Deref (First_Param).Sym_Type = Func then
1c28e560 333 return Call_Func (Deref_Func (First_Param).all, Rest_Params);
18e21187
CM
334 elsif Deref (First_Param).Sym_Type = Lambda then
335 declare
336
337 L : Lambda_Mal_Type;
338 E : Envs.Env_Handle;
339 Param_Names : List_Mal_Type;
340 Res : Mal_Handle;
341
342 begin
343
344 L := Deref_Lambda (First_Param).all;
345 E := Envs.New_Env (L.Get_Env);
346
347 Param_Names := Deref_List (L.Get_Params).all;
348
349 if Envs.Bind (E, Param_Names, Deref_List (Rest_Params).all) then
350
351 Param := L.Get_Expr;
352 Env := E;
353 goto Tail_Call_Opt;
354 -- was: return Eval (L.Get_Expr, E);
355
356 else
357
358 raise Mal_Exception with "Bind failed in Apply";
359
360 end if;
361
362 end;
363
364 else -- neither a Lambda or a Func
365 raise Mal_Exception;
366 end if;
367
368 end;
369
370 end if;
371
372 else
373
374 return Eval_Ast (Param, Env);
375
376 end if;
377
378 end Eval;
ebb6e9d3
CM
379
380
381 function Print (Param : Types.Mal_Handle) return String is
382 begin
383 return Printer.Pr_Str (Param);
384 end Print;
385
b5bad5ea
CM
386 function Rep (Param : String; Env : Envs.Env_Handle) return String is
387 AST, Evaluated_AST : Types.Mal_Handle;
ebb6e9d3
CM
388 begin
389
b5bad5ea 390 AST := Read (Param);
ebb6e9d3 391
b5bad5ea
CM
392 if Types.Is_Null (AST) then
393 return "";
394 else
395 Evaluated_AST := Eval (AST, Env);
396 return Print (Evaluated_AST);
397 end if;
ebb6e9d3
CM
398
399 end Rep;
400
b5bad5ea
CM
401
402 Repl_Env : Envs.Env_Handle;
403
404
405 -- These two ops use Repl_Env directly.
406
407
408 procedure RE (Str : Mal_String) is
409 Discarded : Mal_Handle;
410 begin
411 Discarded := Eval (Read (Str), Repl_Env);
412 end RE;
413
414
1c28e560 415 function Do_Eval (Rest_Handle : Mal_Handle)
b5bad5ea
CM
416 return Types.Mal_Handle is
417 First_Param : Mal_Handle;
418 Rest_List : Types.List_Mal_Type;
419 begin
420 Rest_List := Deref_List (Rest_Handle).all;
421 First_Param := Car (Rest_List);
422 return Eval_Callback.Eval.all (First_Param, Repl_Env);
423 end Do_Eval;
424
425
ebb6e9d3
CM
426 S : String (1..Reader.Max_Line_Len);
427 Last : Natural;
f049dc3a 428 Cmd_Args, File_Param : Natural;
ebb6e9d3
CM
429 Command_Args : Types.Mal_Handle;
430 Command_List : Types.List_Ptr;
431 File_Processed : Boolean := False;
432
433begin
434
18e21187
CM
435 -- Save a function pointer back to the Eval function.
436 -- Can't use 'Access here because of Ada rules but 'Unrestricted_Access is OK
437 -- as we know Eval will be in scope for the lifetime of the program.
438 Eval_Callback.Eval := Eval'Unrestricted_Access;
439
b5bad5ea
CM
440 Repl_Env := Envs.New_Env;
441
ebb6e9d3
CM
442 -- Core init also creates the first environment.
443 -- This is needed for the def!'s below.
b5bad5ea
CM
444 Core.Init (Repl_Env);
445
446 -- Register the eval command. This needs to be done here rather than Core.Init
447 -- as it requires direct access to Repl_Env.
448 Envs.Set (Repl_Env, "eval", New_Func_Mal_Type ("eval", Do_Eval'Unrestricted_Access));
449
450 RE ("(def! not (fn* (a) (if a false true)))");
451
452 RE ("(def! load-file (fn* (f) (eval (read-string (str ""(do "" (slurp f) "")"")))))");
ebb6e9d3
CM
453
454 Cmd_Args := 0;
455 Command_Args := Types.New_List_Mal_Type (Types.List_List);
456 Command_List := Types.Deref_List (Command_Args);
457
458 while Ada.Command_Line.Argument_Count > Cmd_Args loop
459
460 Cmd_Args := Cmd_Args + 1;
461 if Ada.Command_Line.Argument (Cmd_Args) = "-d" then
18e21187 462 Debug := True;
ebb6e9d3
CM
463 elsif Ada.Command_Line.Argument (Cmd_Args) = "-e" then
464 Envs.Debug := True;
f049dc3a
CM
465 elsif not File_Processed then
466 File_Param := Cmd_Args;
467 File_Processed := True;
ebb6e9d3 468 else
f049dc3a
CM
469 Command_List.Append
470 (Types.New_Symbol_Mal_Type (Ada.Command_Line.Argument (Cmd_Args)));
ebb6e9d3
CM
471 end if;
472
473 end loop;
474
b5bad5ea 475 Envs.Set (Repl_Env, "*ARGV*", Command_Args);
ebb6e9d3 476
f049dc3a 477 if File_Processed then
b5bad5ea 478 RE ("(load-file """ & Ada.Command_Line.Argument (File_Param) & """)");
453a89a0
CM
479 else
480 loop
481 begin
482 Ada.Text_IO.Put ("user> ");
483 Ada.Text_IO.Get_Line (S, Last);
484 Ada.Text_IO.Put_Line (Rep (S (1..Last), Repl_Env));
485 exception
486 when Ada.IO_Exceptions.End_Error => raise;
487 when E : others =>
488 Ada.Text_IO.Put_Line
489 (Ada.Text_IO.Standard_Error,
490 Ada.Exceptions.Exception_Information (E));
491 end;
492 end loop;
f049dc3a
CM
493 end if;
494
ebb6e9d3
CM
495exception
496 when Ada.IO_Exceptions.End_Error => null;
497 -- i.e. exit without textual output
498end Step7_Quote;