vhdl: defmacro! doesn't modify existing functions
[jackhill/mal.git] / impls / vhdl / step8_macros.vhdl
CommitLineData
36e91db4
DM
1entity step8_macros is
2end entity step8_macros;
3
4library STD;
5use STD.textio.all;
6library WORK;
7use WORK.pkg_readline.all;
8use WORK.types.all;
9use WORK.printer.all;
10use WORK.reader.all;
11use WORK.env.all;
12use WORK.core.all;
13
14architecture test of step8_macros is
15
16 shared variable repl_env: env_ptr;
17
18 procedure mal_READ(str: in string; ast: out mal_val_ptr; err: out mal_val_ptr) is
19 begin
20 read_str(str, ast, err);
21 end procedure mal_READ;
22
fbfe6784
NB
23 procedure starts_with(lst : inout mal_val_ptr;
24 sym : in string;
25 res : out boolean) is
36e91db4 26 begin
fbfe6784
NB
27 res := lst.seq_val.all'length = 2
28 and lst.seq_val.all (lst.seq_val.all'low).val_type = mal_symbol
29 and lst.seq_val.all (lst.seq_val.all'low).string_val.all = sym;
30 end starts_with;
36e91db4 31
fbfe6784
NB
32 -- Forward declaration
33 procedure quasiquote(ast: inout mal_val_ptr;
34 result: out mal_val_ptr);
35
36 procedure qq_loop(elt : inout mal_val_ptr;
37 acc : inout mal_val_ptr) is
38 variable sw : boolean := elt.val_type = mal_list;
39 variable seq : mal_seq_ptr := new mal_seq(0 to 2);
36e91db4 40 begin
fbfe6784
NB
41 if sw then
42 starts_with(elt, "splice-unquote", sw);
36e91db4 43 end if;
fbfe6784
NB
44 if sw then
45 new_symbol("concat", seq(0));
46 seq(1) := elt.seq_val(1);
36e91db4 47 else
fbfe6784
NB
48 new_symbol("cons", seq(0));
49 quasiquote(elt, seq(1));
50 end if;
51 seq(2) := acc;
52 new_seq_obj(mal_list, seq, acc);
53 end qq_loop;
54
55 procedure qq_foldr (xs : inout mal_seq_ptr;
56 res : out mal_val_ptr) is
57 variable seq : mal_seq_ptr := new mal_seq(0 to -1);
58 variable acc : mal_val_ptr;
59 begin
60 new_seq_obj(mal_list, seq, acc);
61 for i in xs'reverse_range loop
62 qq_loop (xs(i), acc);
63 end loop;
64 res := acc;
65 end procedure qq_foldr;
66
67 procedure quasiquote(ast: inout mal_val_ptr;
68 result: out mal_val_ptr) is
69 variable sw : boolean;
70 variable seq : mal_seq_ptr;
71 begin
72 case ast.val_type is
73 when mal_list =>
74 starts_with(ast, "unquote", sw);
75 if sw then
76 result := ast.seq_val(1);
77 else
78 qq_foldr(ast.seq_val, result);
79 end if;
80 when mal_vector =>
81 seq := new mal_seq(0 to 1);
82 new_symbol("vec", seq(0));
83 qq_foldr(ast.seq_val, seq(1));
36e91db4 84 new_seq_obj(mal_list, seq, result);
fbfe6784
NB
85 when mal_symbol | mal_hashmap =>
86 seq := new mal_seq(0 to 1);
87 new_symbol("quote", seq(0));
88 seq(1) := ast;
36e91db4 89 new_seq_obj(mal_list, seq, result);
fbfe6784
NB
90 when others =>
91 result := ast;
92 end case;
36e91db4
DM
93 end procedure quasiquote;
94
95 -- Forward declaration
96 procedure EVAL(in_ast: inout mal_val_ptr; in_env: inout env_ptr; result: out mal_val_ptr; err: out mal_val_ptr);
97
98 procedure apply_func(fn: inout mal_val_ptr; args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr);
99
100 procedure is_macro_call(ast: inout mal_val_ptr; env: inout env_ptr; is_macro: out boolean) is
101 variable f, env_err: mal_val_ptr;
102 begin
103 is_macro := false;
104 if ast.val_type = mal_list and
105 ast.seq_val'length > 0 and
106 ast.seq_val(0).val_type = mal_symbol then
107 env_get(env, ast.seq_val(0), f, env_err);
108 if env_err = null and f /= null and
109 f.val_type = mal_fn and f.func_val.f_is_macro then
110 is_macro := true;
111 end if;
112 end if;
113 end procedure is_macro_call;
114
115 procedure macroexpand(in_ast: inout mal_val_ptr; env: inout env_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is
116 variable ast, macro_fn, call_args, macro_err: mal_val_ptr;
117 variable is_macro: boolean;
118 begin
119 ast := in_ast;
120 is_macro_call(ast, env, is_macro);
121 while is_macro loop
122 env_get(env, ast.seq_val(0), macro_fn, macro_err);
123 seq_drop_prefix(ast, 1, call_args);
124 apply_func(macro_fn, call_args, ast, macro_err);
125 if macro_err /= null then
126 err := macro_err;
127 return;
128 end if;
129 is_macro_call(ast, env, is_macro);
130 end loop;
131 result := ast;
132 end procedure macroexpand;
133
134 procedure fn_eval(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is
135 begin
136 EVAL(args.seq_val(0), repl_env, result, err);
137 end procedure fn_eval;
138
139 procedure fn_swap(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is
140 variable atom: mal_val_ptr := args.seq_val(0);
141 variable fn: mal_val_ptr := args.seq_val(1);
142 variable call_args_seq: mal_seq_ptr;
143 variable call_args, eval_res, sub_err: mal_val_ptr;
144 begin
145 call_args_seq := new mal_seq(0 to args.seq_val'length - 2);
146 call_args_seq(0) := atom.seq_val(0);
147 call_args_seq(1 to call_args_seq'length - 1) := args.seq_val(2 to args.seq_val'length - 1);
148 new_seq_obj(mal_list, call_args_seq, call_args);
149 apply_func(fn, call_args, eval_res, sub_err);
150 if sub_err /= null then
151 err := sub_err;
152 return;
153 end if;
154 atom.seq_val(0) := eval_res;
155 result := eval_res;
156 end procedure fn_swap;
157
158 procedure apply_native_func(func_sym: inout mal_val_ptr; args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is
159 begin
160 if func_sym.string_val.all = "eval" then
161 fn_eval(args, result, err);
162 elsif func_sym.string_val.all = "swap!" then
163 fn_swap(args, result, err);
164 else
165 eval_native_func(func_sym, args, result, err);
166 end if;
167 end procedure apply_native_func;
168
169 procedure apply_func(fn: inout mal_val_ptr; args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is
170 variable fn_env: env_ptr;
171 begin
172 case fn.val_type is
173 when mal_nativefn =>
174 apply_native_func(fn, args, result, err);
175 when mal_fn =>
176 new_env(fn_env, fn.func_val.f_env, fn.func_val.f_args, args);
177 EVAL(fn.func_val.f_body, fn_env, result, err);
178 when others =>
179 new_string("not a function", err);
180 return;
181 end case;
182 end procedure apply_func;
183
184 procedure eval_ast_seq(ast_seq: inout mal_seq_ptr; env: inout env_ptr; result: inout mal_seq_ptr; err: out mal_val_ptr) is
185 variable eval_err: mal_val_ptr;
186 begin
187 result := new mal_seq(0 to ast_seq'length - 1);
188 for i in result'range loop
189 EVAL(ast_seq(i), env, result(i), eval_err);
190 if eval_err /= null then
191 err := eval_err;
192 return;
193 end if;
194 end loop;
195 end procedure eval_ast_seq;
196
197 procedure eval_ast(ast: inout mal_val_ptr; env: inout env_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is
198 variable key, val, eval_err, env_err: mal_val_ptr;
199 variable new_seq: mal_seq_ptr;
200 variable i: integer;
201 begin
202 case ast.val_type is
203 when mal_symbol =>
204 env_get(env, ast, val, env_err);
205 if env_err /= null then
206 err := env_err;
207 return;
208 end if;
209 result := val;
210 return;
211 when mal_list | mal_vector | mal_hashmap =>
212 eval_ast_seq(ast.seq_val, env, new_seq, eval_err);
213 if eval_err /= null then
214 err := eval_err;
215 return;
216 end if;
217 new_seq_obj(ast.val_type, new_seq, result);
218 return;
219 when others =>
220 result := ast;
221 return;
222 end case;
223 end procedure eval_ast;
224
225 procedure EVAL(in_ast: inout mal_val_ptr; in_env: inout env_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is
226 variable i: integer;
227 variable ast, evaled_ast, a0, call_args, val, vars, sub_err, fn: mal_val_ptr;
228 variable env, let_env, fn_env: env_ptr;
229 begin
230 ast := in_ast;
231 env := in_env;
232 loop
233 if ast.val_type /= mal_list then
234 eval_ast(ast, env, result, err);
235 return;
236 end if;
237
238 macroexpand(ast, env, ast, sub_err);
239 if sub_err /= null then
240 err := sub_err;
241 return;
242 end if;
243 if ast.val_type /= mal_list then
244 eval_ast(ast, env, result, err);
245 return;
246 end if;
247 if ast.seq_val'length = 0 then
248 result := ast;
249 return;
250 end if;
251
252 a0 := ast.seq_val(0);
253 if a0.val_type = mal_symbol then
254 if a0.string_val.all = "def!" then
255 EVAL(ast.seq_val(2), env, val, sub_err);
256 if sub_err /= null then
257 err := sub_err;
258 return;
259 end if;
260 env_set(env, ast.seq_val(1), val);
261 result := val;
262 return;
263
264 elsif a0.string_val.all = "let*" then
265 vars := ast.seq_val(1);
266 new_env(let_env, env);
267 i := 0;
268 while i < vars.seq_val'length loop
269 EVAL(vars.seq_val(i + 1), let_env, val, sub_err);
270 if sub_err /= null then
271 err := sub_err;
272 return;
273 end if;
274 env_set(let_env, vars.seq_val(i), val);
275 i := i + 2;
276 end loop;
277 env := let_env;
278 ast := ast.seq_val(2);
279 next; -- TCO
280
281 elsif a0.string_val.all = "quote" then
282 result := ast.seq_val(1);
283 return;
284
fbfe6784
NB
285 elsif a0.string_val.all = "quasiquoteexpand" then
286 quasiquote(ast.seq_val(1), result);
287 return;
288
36e91db4
DM
289 elsif a0.string_val.all = "quasiquote" then
290 quasiquote(ast.seq_val(1), ast);
291 next; -- TCO
292
293 elsif a0.string_val.all = "defmacro!" then
fdd4e53f 294 EVAL(ast.seq_val(2), env, fn, sub_err);
36e91db4
DM
295 if sub_err /= null then
296 err := sub_err;
297 return;
298 end if;
fdd4e53f 299 new_fn(fn.func_val.f_body, fn.func_val.f_args, fn.func_val.f_env, val);
36e91db4
DM
300 val.func_val.f_is_macro := true;
301 env_set(env, ast.seq_val(1), val);
302 result := val;
303 return;
304
305 elsif a0.string_val.all = "macroexpand" then
306 macroexpand(ast.seq_val(1), env, result, err);
307 return;
308
309 elsif a0.string_val.all = "do" then
310 for i in 1 to ast.seq_val'high - 1 loop
311 EVAL(ast.seq_val(i), env, result, sub_err);
312 if sub_err /= null then
313 err := sub_err;
314 return;
315 end if;
316 end loop;
317 ast := ast.seq_val(ast.seq_val'high);
318 next; -- TCO
319
320 elsif a0.string_val.all = "if" then
321 EVAL(ast.seq_val(1), env, val, sub_err);
322 if sub_err /= null then
323 err := sub_err;
324 return;
325 end if;
326 if val.val_type = mal_nil or val.val_type = mal_false then
327 if ast.seq_val'length > 3 then
328 ast := ast.seq_val(3);
329 else
330 new_nil(result);
331 return;
332 end if;
333 else
334 ast := ast.seq_val(2);
335 end if;
336 next; -- TCO
337
338 elsif a0.string_val.all = "fn*" then
339 new_fn(ast.seq_val(2), ast.seq_val(1), env, result);
340 return;
341
342 end if;
343 end if;
344
345 eval_ast(ast, env, evaled_ast, sub_err);
346 if sub_err /= null then
347 err := sub_err;
348 return;
349 end if;
350 seq_drop_prefix(evaled_ast, 1, call_args);
351 fn := evaled_ast.seq_val(0);
352 case fn.val_type is
353 when mal_nativefn =>
354 apply_native_func(fn, call_args, result, err);
355 return;
356 when mal_fn =>
357 new_env(fn_env, fn.func_val.f_env, fn.func_val.f_args, call_args);
358 env := fn_env;
359 ast := fn.func_val.f_body;
360 next; -- TCO
361 when others =>
362 new_string("not a function", err);
363 return;
364 end case;
365 end loop;
366 end procedure EVAL;
367
368 procedure mal_PRINT(exp: inout mal_val_ptr; result: out line) is
369 begin
370 pr_str(exp, true, result);
371 end procedure mal_PRINT;
372
373 procedure RE(str: in string; env: inout env_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is
374 variable ast, read_err: mal_val_ptr;
375 begin
376 mal_READ(str, ast, read_err);
377 if read_err /= null then
378 err := read_err;
379 result := null;
380 return;
381 end if;
382 if ast = null then
383 result := null;
384 return;
385 end if;
386 EVAL(ast, env, result, err);
387 end procedure RE;
388
389 procedure REP(str: in string; env: inout env_ptr; result: out line; err: out mal_val_ptr) is
390 variable eval_res, eval_err: mal_val_ptr;
391 begin
392 RE(str, env, eval_res, eval_err);
393 if eval_err /= null then
394 err := eval_err;
395 result := null;
396 return;
397 end if;
398 mal_PRINT(eval_res, result);
399 end procedure REP;
400
401 procedure set_argv(e: inout env_ptr; program_file: inout line) is
402 variable argv_var_name: string(1 to 6) := "*ARGV*";
403 variable argv_sym, argv_list: mal_val_ptr;
404 file f: text;
405 variable status: file_open_status;
406 variable one_line: line;
407 variable seq: mal_seq_ptr;
408 variable element: mal_val_ptr;
409 begin
410 program_file := null;
411 seq := new mal_seq(0 to -1);
412 file_open(status, f, external_name => "vhdl_argv.tmp", open_kind => read_mode);
413 if status = open_ok then
414 if not endfile(f) then
415 readline(f, program_file);
416 while not endfile(f) loop
417 readline(f, one_line);
418 new_string(one_line.all, element);
419 seq := new mal_seq'(seq.all & element);
420 end loop;
421 end if;
422 file_close(f);
423 end if;
424 new_seq_obj(mal_list, seq, argv_list);
425 new_symbol(argv_var_name, argv_sym);
426 env_set(e, argv_sym, argv_list);
427 end procedure set_argv;
428
429 procedure repl is
430 variable is_eof: boolean;
431 variable program_file, input_line, result: line;
432 variable eval_sym, eval_fn, dummy_val, err: mal_val_ptr;
433 variable outer: env_ptr;
434 variable eval_func_name: string(1 to 4) := "eval";
435 begin
436 outer := null;
437 new_env(repl_env, outer);
438
439 -- core.EXT: defined using VHDL (see core.vhdl)
440 define_core_functions(repl_env);
441 new_symbol(eval_func_name, eval_sym);
442 new_nativefn(eval_func_name, eval_fn);
443 env_set(repl_env, eval_sym, eval_fn);
444 set_argv(repl_env, program_file);
445
446 -- core.mal: defined using the language itself
447 RE("(def! not (fn* (a) (if a false true)))", repl_env, dummy_val, err);
e6d41de4 448 RE("(def! load-file (fn* (f) (eval (read-string (str " & '"' & "(do " & '"' & " (slurp f) " & '"' & "\nnil)" & '"' & ")))))", repl_env, dummy_val, err);
36e91db4 449 RE("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw " & '"' & "odd number of forms to cond" & '"' & ")) (cons 'cond (rest (rest xs)))))))", repl_env, dummy_val, err);
36e91db4
DM
450
451 if program_file /= null then
452 REP("(load-file " & '"' & program_file.all & '"' & ")", repl_env, result, err);
453 return;
454 end if;
455
456 loop
457 mal_readline("user> ", is_eof, input_line);
458 exit when is_eof;
459 next when input_line'length = 0;
460 REP(input_line.all, repl_env, result, err);
461 if err /= null then
462 pr_str(err, false, result);
463 result := new string'("Error: " & result.all);
464 end if;
465 if result /= null then
466 mal_printline(result.all);
467 end if;
468 deallocate(result);
469 deallocate(err);
470 end loop;
471 mal_printline("");
472 end procedure repl;
473
474begin
475 repl;
476end architecture test;