Change quasiquote algorithm
[jackhill/mal.git] / impls / vhdl / step7_quote.vhdl
CommitLineData
36e91db4
DM
1entity step7_quote is
2end entity step7_quote;
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 step7_quote 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 fn_eval(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is
101 begin
102 EVAL(args.seq_val(0), repl_env, result, err);
103 end procedure fn_eval;
104
105 procedure fn_swap(args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is
106 variable atom: mal_val_ptr := args.seq_val(0);
107 variable fn: mal_val_ptr := args.seq_val(1);
108 variable call_args_seq: mal_seq_ptr;
109 variable call_args, eval_res, sub_err: mal_val_ptr;
110 begin
111 call_args_seq := new mal_seq(0 to args.seq_val'length - 2);
112 call_args_seq(0) := atom.seq_val(0);
113 call_args_seq(1 to call_args_seq'length - 1) := args.seq_val(2 to args.seq_val'length - 1);
114 new_seq_obj(mal_list, call_args_seq, call_args);
115 apply_func(fn, call_args, eval_res, sub_err);
116 if sub_err /= null then
117 err := sub_err;
118 return;
119 end if;
120 atom.seq_val(0) := eval_res;
121 result := eval_res;
122 end procedure fn_swap;
123
124 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
125 begin
126 if func_sym.string_val.all = "eval" then
127 fn_eval(args, result, err);
128 elsif func_sym.string_val.all = "swap!" then
129 fn_swap(args, result, err);
130 else
131 eval_native_func(func_sym, args, result, err);
132 end if;
133 end procedure apply_native_func;
134
135 procedure apply_func(fn: inout mal_val_ptr; args: inout mal_val_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is
136 variable fn_env: env_ptr;
137 begin
138 case fn.val_type is
139 when mal_nativefn =>
140 apply_native_func(fn, args, result, err);
141 when mal_fn =>
142 new_env(fn_env, fn.func_val.f_env, fn.func_val.f_args, args);
143 EVAL(fn.func_val.f_body, fn_env, result, err);
144 when others =>
145 new_string("not a function", err);
146 return;
147 end case;
148 end procedure apply_func;
149
150 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
151 variable eval_err: mal_val_ptr;
152 begin
153 result := new mal_seq(0 to ast_seq'length - 1);
154 for i in result'range loop
155 EVAL(ast_seq(i), env, result(i), eval_err);
156 if eval_err /= null then
157 err := eval_err;
158 return;
159 end if;
160 end loop;
161 end procedure eval_ast_seq;
162
163 procedure eval_ast(ast: inout mal_val_ptr; env: inout env_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is
164 variable key, val, eval_err, env_err: mal_val_ptr;
165 variable new_seq: mal_seq_ptr;
166 variable i: integer;
167 begin
168 case ast.val_type is
169 when mal_symbol =>
170 env_get(env, ast, val, env_err);
171 if env_err /= null then
172 err := env_err;
173 return;
174 end if;
175 result := val;
176 return;
177 when mal_list | mal_vector | mal_hashmap =>
178 eval_ast_seq(ast.seq_val, env, new_seq, eval_err);
179 if eval_err /= null then
180 err := eval_err;
181 return;
182 end if;
183 new_seq_obj(ast.val_type, new_seq, result);
184 return;
185 when others =>
186 result := ast;
187 return;
188 end case;
189 end procedure eval_ast;
190
191 procedure EVAL(in_ast: inout mal_val_ptr; in_env: inout env_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is
192 variable i: integer;
193 variable ast, evaled_ast, a0, call_args, val, vars, sub_err, fn: mal_val_ptr;
194 variable env, let_env, fn_env: env_ptr;
195 begin
196 ast := in_ast;
197 env := in_env;
198 loop
199 if ast.val_type /= mal_list then
200 eval_ast(ast, env, result, err);
201 return;
202 end if;
203
204 if ast.seq_val'length = 0 then
205 result := ast;
206 return;
207 end if;
208
209 a0 := ast.seq_val(0);
210 if a0.val_type = mal_symbol then
211 if a0.string_val.all = "def!" then
212 EVAL(ast.seq_val(2), env, val, sub_err);
213 if sub_err /= null then
214 err := sub_err;
215 return;
216 end if;
217 env_set(env, ast.seq_val(1), val);
218 result := val;
219 return;
220
221 elsif a0.string_val.all = "let*" then
222 vars := ast.seq_val(1);
223 new_env(let_env, env);
224 i := 0;
225 while i < vars.seq_val'length loop
226 EVAL(vars.seq_val(i + 1), let_env, val, sub_err);
227 if sub_err /= null then
228 err := sub_err;
229 return;
230 end if;
231 env_set(let_env, vars.seq_val(i), val);
232 i := i + 2;
233 end loop;
234 env := let_env;
235 ast := ast.seq_val(2);
236 next; -- TCO
237
238 elsif a0.string_val.all = "quote" then
239 result := ast.seq_val(1);
240 return;
241
fbfe6784
NB
242 elsif a0.string_val.all = "quasiquoteexpand" then
243 quasiquote(ast.seq_val(1), result);
244 return;
245
36e91db4
DM
246 elsif a0.string_val.all = "quasiquote" then
247 quasiquote(ast.seq_val(1), ast);
248 next; -- TCO
249
250 elsif a0.string_val.all = "do" then
251 for i in 1 to ast.seq_val'high - 1 loop
252 EVAL(ast.seq_val(i), env, result, sub_err);
253 if sub_err /= null then
254 err := sub_err;
255 return;
256 end if;
257 end loop;
258 ast := ast.seq_val(ast.seq_val'high);
259 next; -- TCO
260
261 elsif a0.string_val.all = "if" then
262 EVAL(ast.seq_val(1), env, val, sub_err);
263 if sub_err /= null then
264 err := sub_err;
265 return;
266 end if;
267 if val.val_type = mal_nil or val.val_type = mal_false then
268 if ast.seq_val'length > 3 then
269 ast := ast.seq_val(3);
270 else
271 new_nil(result);
272 return;
273 end if;
274 else
275 ast := ast.seq_val(2);
276 end if;
277 next; -- TCO
278
279 elsif a0.string_val.all = "fn*" then
280 new_fn(ast.seq_val(2), ast.seq_val(1), env, result);
281 return;
282
283 end if;
284 end if;
285
286 eval_ast(ast, env, evaled_ast, sub_err);
287 if sub_err /= null then
288 err := sub_err;
289 return;
290 end if;
291 seq_drop_prefix(evaled_ast, 1, call_args);
292 fn := evaled_ast.seq_val(0);
293 case fn.val_type is
294 when mal_nativefn =>
295 apply_native_func(fn, call_args, result, err);
296 return;
297 when mal_fn =>
298 new_env(fn_env, fn.func_val.f_env, fn.func_val.f_args, call_args);
299 env := fn_env;
300 ast := fn.func_val.f_body;
301 next; -- TCO
302 when others =>
303 new_string("not a function", err);
304 return;
305 end case;
306 end loop;
307 end procedure EVAL;
308
309 procedure mal_PRINT(exp: inout mal_val_ptr; result: out line) is
310 begin
311 pr_str(exp, true, result);
312 end procedure mal_PRINT;
313
314 procedure RE(str: in string; env: inout env_ptr; result: out mal_val_ptr; err: out mal_val_ptr) is
315 variable ast, read_err: mal_val_ptr;
316 begin
317 mal_READ(str, ast, read_err);
318 if read_err /= null then
319 err := read_err;
320 result := null;
321 return;
322 end if;
323 if ast = null then
324 result := null;
325 return;
326 end if;
327 EVAL(ast, env, result, err);
328 end procedure RE;
329
330 procedure REP(str: in string; env: inout env_ptr; result: out line; err: out mal_val_ptr) is
331 variable eval_res, eval_err: mal_val_ptr;
332 begin
333 RE(str, env, eval_res, eval_err);
334 if eval_err /= null then
335 err := eval_err;
336 result := null;
337 return;
338 end if;
339 mal_PRINT(eval_res, result);
340 end procedure REP;
341
342 procedure set_argv(e: inout env_ptr; program_file: inout line) is
343 variable argv_var_name: string(1 to 6) := "*ARGV*";
344 variable argv_sym, argv_list: mal_val_ptr;
345 file f: text;
346 variable status: file_open_status;
347 variable one_line: line;
348 variable seq: mal_seq_ptr;
349 variable element: mal_val_ptr;
350 begin
351 program_file := null;
352 seq := new mal_seq(0 to -1);
353 file_open(status, f, external_name => "vhdl_argv.tmp", open_kind => read_mode);
354 if status = open_ok then
355 if not endfile(f) then
356 readline(f, program_file);
357 while not endfile(f) loop
358 readline(f, one_line);
359 new_string(one_line.all, element);
360 seq := new mal_seq'(seq.all & element);
361 end loop;
362 end if;
363 file_close(f);
364 end if;
365 new_seq_obj(mal_list, seq, argv_list);
366 new_symbol(argv_var_name, argv_sym);
367 env_set(e, argv_sym, argv_list);
368 end procedure set_argv;
369
370 procedure repl is
371 variable is_eof: boolean;
372 variable program_file, input_line, result: line;
373 variable eval_sym, eval_fn, dummy_val, err: mal_val_ptr;
374 variable outer: env_ptr;
375 variable eval_func_name: string(1 to 4) := "eval";
376 begin
377 outer := null;
378 new_env(repl_env, outer);
379
380 -- core.EXT: defined using VHDL (see core.vhdl)
381 define_core_functions(repl_env);
382 new_symbol(eval_func_name, eval_sym);
383 new_nativefn(eval_func_name, eval_fn);
384 env_set(repl_env, eval_sym, eval_fn);
385 set_argv(repl_env, program_file);
386
387 -- core.mal: defined using the language itself
388 RE("(def! not (fn* (a) (if a false true)))", repl_env, dummy_val, err);
e6d41de4 389 RE("(def! load-file (fn* (f) (eval (read-string (str " & '"' & "(do " & '"' & " (slurp f) " & '"' & "\nnil)" & '"' & ")))))", repl_env, dummy_val, err);
36e91db4
DM
390
391 if program_file /= null then
392 REP("(load-file " & '"' & program_file.all & '"' & ")", repl_env, result, err);
393 return;
394 end if;
395
396 loop
397 mal_readline("user> ", is_eof, input_line);
398 exit when is_eof;
399 next when input_line'length = 0;
400 REP(input_line.all, repl_env, result, err);
401 if err /= null then
402 pr_str(err, false, result);
403 result := new string'("Error: " & result.all);
404 end if;
405 if result /= null then
406 mal_printline(result.all);
407 end if;
408 deallocate(result);
409 deallocate(err);
410 end loop;
411 mal_printline("");
412 end procedure repl;
413
414begin
415 repl;
416end architecture test;