Change quasiquote algorithm
[jackhill/mal.git] / impls / awk / step9_try.awk
CommitLineData
8c7587af
MK
1@include "types.awk"
2@include "reader.awk"
3@include "printer.awk"
4@include "env.awk"
5@include "core.awk"
6
7function READ(str)
8{
9 return reader_read_str(str)
10}
11
fbfe6784
NB
12# Return 0, an error or the unquote argument (second element of ast).
13function starts_with(ast, sym, idx, len)
8c7587af 14{
fbfe6784
NB
15 if (ast !~ /^\(/)
16 return 0
17 idx = substr(ast, 2)
18 len = types_heap[idx]["len"]
19 if (!len || types_heap[idx][0] != sym)
20 return 0
21 if (len != 2)
22 return "!\"'" sym "' expects 1 argument, not " (len - 1) "."
23 return types_heap[idx][1]
8c7587af
MK
24}
25
fbfe6784 26function quasiquote(ast, new_idx, ret, ast_idx, elt_i, elt, previous)
8c7587af 27{
fbfe6784
NB
28 if (ast !~ /^[(['{]/) {
29 return ast
30 }
31 if (ast ~ /['\{]/) {
8c7587af
MK
32 new_idx = types_allocate()
33 types_heap[new_idx][0] = "'quote"
34 types_heap[new_idx][1] = ast
35 types_heap[new_idx]["len"] = 2
36 return "(" new_idx
37 }
fbfe6784
NB
38 ret = starts_with(ast, "'unquote")
39 if (ret ~ /^!/) {
8c7587af
MK
40 types_release(ast)
41 return ret
42 }
fbfe6784
NB
43 if (ret) {
44 types_addref(ret)
45 types_release(ast)
46 return ret
47 }
48 new_idx = types_allocate()
49 types_heap[new_idx]["len"] = 0
50 ast_idx = substr(ast, 2)
51 for (elt_i=types_heap[ast_idx]["len"]-1; 0<=elt_i; elt_i--) {
52 elt = types_heap[ast_idx][elt_i]
53 ret = starts_with(elt, "'splice-unquote")
54 if (ret ~ /^!/) {
55 types_release("(" new_idx)
8c7587af 56 types_release(ast)
fbfe6784 57 return ret
8c7587af 58 }
fbfe6784
NB
59 if (ret) {
60 previous = "(" new_idx
61 new_idx = types_allocate()
62 types_heap[new_idx][0] = "'concat"
63 types_heap[new_idx][1] = types_addref(ret)
64 types_heap[new_idx][2] = previous
65 types_heap[new_idx]["len"] = 3
66 } else {
67 ret = quasiquote(types_addref(elt))
68 if (ret ~ /^!/) {
69 types_release(ast)
70 return ret
71 }
72 previous = "(" new_idx
73 new_idx = types_allocate()
74 types_heap[new_idx][0] = "'cons"
75 types_heap[new_idx][1] = ret
76 types_heap[new_idx][2] = previous
77 types_heap[new_idx]["len"] = 3
8c7587af 78 }
8c7587af 79 }
fbfe6784
NB
80 if (ast ~ /^\[/) {
81 previous = "(" new_idx
82 new_idx = types_allocate()
83 types_heap[new_idx][0] = "'vec"
84 types_heap[new_idx][1] = previous
85 types_heap[new_idx]["len"] = 2
8c7587af 86 }
8c7587af 87 types_release(ast)
8c7587af
MK
88 return "(" new_idx
89}
90
fbfe6784 91function is_macro_call(ast, env, idx, len, sym, f)
8c7587af 92{
fbfe6784
NB
93 if (ast !~ /^\(/) return 0
94 idx = substr(ast, 2)
95 len = types_heap[idx]["len"]
96 if (len == 0) return 0
97 sym = types_heap[idx][0]
98 if (sym !~ /^'/) return 0
8c7587af
MK
99 f = env_get(env, sym)
100 return f ~ /^\$/ && types_heap[substr(f, 2)]["is_macro"]
101}
102
103function macroexpand(ast, env, idx, f_idx, new_env)
104{
105 while (is_macro_call(ast, env)) {
106 idx = substr(ast, 2)
107 f_idx = substr(env_get(env, types_heap[idx][0]), 2)
108 new_env = env_new(types_heap[f_idx]["env"], types_heap[f_idx]["params"], idx)
109 types_release(ast)
110 if (new_env ~ /^!/) {
111 return new_env
112 }
113 types_addref(ast = types_heap[f_idx]["body"])
114 ast = EVAL(ast, new_env)
115 env_release(new_env)
116 if (ast ~ /^!/) {
117 return ast
118 }
119 }
120 return ast
121}
122
123function eval_ast(ast, env, i, idx, len, new_idx, ret)
124{
125 switch (ast) {
126 case /^'/:
127 ret = env_get(env, ast)
128 if (ret !~ /^!/) {
129 types_addref(ret)
130 }
131 return ret
132 case /^[([]/:
133 idx = substr(ast, 2)
134 len = types_heap[idx]["len"]
135 new_idx = types_allocate()
136 for (i = 0; i < len; ++i) {
137 ret = EVAL(types_addref(types_heap[idx][i]), env)
138 if (ret ~ /^!/) {
139 types_heap[new_idx]["len"] = i
140 types_release(substr(ast, 1, 1) new_idx)
141 return ret
142 }
143 types_heap[new_idx][i] = ret
144 }
145 types_heap[new_idx]["len"] = len
146 return substr(ast, 1, 1) new_idx
147 case /^\{/:
148 idx = substr(ast, 2)
149 new_idx = types_allocate()
150 for (i in types_heap[idx]) {
151 if (i ~ /^[":]/) {
152 ret = EVAL(types_addref(types_heap[idx][i]), env)
153 if (ret ~ /^!/) {
154 types_release("{" new_idx)
155 return ret
156 }
157 types_heap[new_idx][i] = ret
158 }
159 }
160 return "{" new_idx
161 default:
162 return ast
163 }
164}
165
166function EVAL_def(ast, env, idx, sym, ret, len)
167{
168 idx = substr(ast, 2)
169 if (types_heap[idx]["len"] != 3) {
170 len = types_heap[idx]["len"]
171 types_release(ast)
172 env_release(env)
173 return "!\"Invalid argument length for 'def!'. Expects exactly 2 arguments, supplied" (len - 1) "."
174 }
175 sym = types_heap[idx][1]
176 if (sym !~ /^'/) {
177 types_release(ast)
178 env_release(env)
179 return "!\"Incompatible type for argument 1 of 'def!'. Expects symbol, supplied " types_typename(sym) "."
180 }
181 ret = EVAL(types_addref(types_heap[idx][2]), env)
182 if (ret !~ /^!/) {
183 env_set(env, sym, ret)
184 types_addref(ret)
185 }
186 types_release(ast)
187 env_release(env)
188 return ret
189}
190
191function EVAL_let(ast, env, ret_env, idx, params, params_idx, params_len, new_env, i, sym, ret, body, len)
192{
193 idx = substr(ast, 2)
194 if (types_heap[idx]["len"] != 3) {
195 len = types_heap[idx]["len"]
196 types_release(ast)
197 env_release(env)
198 return "!\"Invalid argument length for 'let*'. Expects exactly 2 arguments, supplied " (len - 1) "."
199 }
200 params = types_heap[idx][1]
201 if (params !~ /^[([]/) {
202 types_release(ast)
203 env_release(env)
204 return "!\"Incompatible type for argument 1 of 'let*'. Expects list or vector, supplied " types_typename(params) "."
205 }
206 params_idx = substr(params, 2)
207 params_len = types_heap[params_idx]["len"]
208 if (params_len % 2 != 0) {
209 types_release(ast)
210 env_release(env)
211 return "!\"Invalid elements count for argument 1 of 'let*'. Expects even number of elements, supplied " params_len "."
212 }
213 new_env = env_new(env)
214 env_release(env)
215 for (i = 0; i < params_len; i += 2) {
216 sym = types_heap[params_idx][i]
217 if (sym !~ /^'/) {
218 types_release(ast)
219 env_release(new_env)
220 return "!\"Incompatible type for odd element of argument 1 of 'let*'. Expects symbol, supplied " types_typename(sym) "."
221 }
222 ret = EVAL(types_addref(types_heap[params_idx][i + 1]), new_env)
223 if (ret ~ /^!/) {
224 types_release(ast)
225 env_release(new_env)
226 return ret
227 }
228 env_set(new_env, sym, ret)
229 }
230 types_addref(body = types_heap[idx][2])
231 types_release(ast)
232 ret_env[0] = new_env
233 return body
234}
235
236function EVAL_defmacro(ast, env, idx, sym, ret, len)
237{
238 idx = substr(ast, 2)
239 if (types_heap[idx]["len"] != 3) {
240 len = types_heap[idx]["len"]
241 types_release(ast)
242 env_release(env)
243 return "!\"Invalid argument length for 'defmacro!'. Expects exactly 2 arguments, supplied" (len - 1) "."
244 }
245 sym = types_heap[idx][1]
246 if (sym !~ /^'/) {
247 types_release(ast)
248 env_release(env)
249 return "!\"Incompatible type for argument 1 of 'defmacro!'. Expects symbol, supplied " types_typename(sym) "."
250 }
251 ret = EVAL(types_addref(types_heap[idx][2]), env)
252 types_release(ast)
253 if (ret ~ /^!/) {
254 env_release(env)
255 return ret
256 }
257 if (ret !~ /^\$/) {
258 types_release(ret)
259 env_release(env)
260 return "!\"Incompatible type for argument 2 of 'defmacro!'. Expects function, supplied " types_typename(ret) "."
261 }
262 types_heap[substr(ret, 2)]["is_macro"] = 1
263 env_set(env, sym, ret)
264 types_addref(ret)
265 env_release(env)
266 return ret
267}
268
269function EVAL_try(ast, env, catch_body, catch_env, idx, catch, catch_idx, catch_sym, ret, len, str)
270{
271 idx = substr(ast, 2)
c738a39f
DM
272 len = types_heap[idx]["len"]
273 if (len != 2 && len != 3) {
8c7587af
MK
274 types_release(ast)
275 env_release(env)
c738a39f
DM
276 return "!\"Invalid argument length for 'try*'. Expects 1 or 2 arguments, supplied" (len - 1) "."
277 }
278 if (len == 2) {
279 ret = EVAL(types_addref(types_heap[idx][1]), env)
280 types_release(ast)
281 env_release(env)
282 return ret
8c7587af
MK
283 }
284 catch = types_heap[idx][2]
285 if (catch !~ /^\(/) {
286 types_release(ast)
287 env_release(env)
288 return "!\"Incompatible type for argument 2 of 'try*'. Expects list, supplied " types_typename(catch) "."
289 }
290 catch_idx = substr(catch, 2)
291 if (types_heap[catch_idx]["len"] != 3) {
292 len = types_heap[catch_idx]["len"]
293 types_release(ast)
294 env_release(env)
295 return "!\"Invalid elements count for argument 2 of 'try*'. Expects exactly 3 elements, supplied " len "."
296 }
297 if (types_heap[catch_idx][0] != "'catch*") {
298 str = printer_pr_str(types_heap[catch_idx][0])
299 types_release(ast)
300 env_release(env)
301 return "!\"Invalid first element of argument 2 of 'try*'. Expects symbol 'catch*', supplied '" str "'."
302 }
303 catch_sym = types_heap[catch_idx][1]
304 if (catch_sym !~ /^'/) {
305 types_release(ast)
306 env_release(env)
307 return "!\"Incompatible type for second element of argument 2 of 'try*'. Expects symbol, supplied " types_typename(catch_sym) "."
308 }
309 ret = EVAL(types_addref(types_heap[idx][1]), env)
310 if (ret !~ /^!/) {
311 types_release(ast)
312 env_release(env)
313 return ret
314 }
315 types_addref(catch_body[0] = types_heap[catch_idx][2])
316 catch_env[0] = env_new(env)
317 env_release(env)
318 env_set(catch_env[0], catch_sym, substr(ret, 2))
319 types_release(ast)
320 return ""
321}
322
323function EVAL_do(ast, env, idx, len, i, body, ret)
324{
325 idx = substr(ast, 2)
326 len = types_heap[idx]["len"]
327 if (len == 1) {
328 types_release(ast)
329 env_release(env)
330 return "!\"Invalid argument length for 'do'. Expects at least 1 argument, supplied" (len - 1) "."
331 }
332 for (i = 1; i < len - 1; ++i) {
333 ret = EVAL(types_addref(types_heap[idx][i]), env)
334 if (ret ~ /^!/) {
335 types_release(ast)
336 env_release(env)
337 return ret
338 }
339 types_release(ret)
340 }
341 types_addref(body = types_heap[idx][len - 1])
342 types_release(ast)
343 return body
344}
345
346function EVAL_if(ast, env, idx, len, ret, body)
347{
348 idx = substr(ast, 2)
349 len = types_heap[idx]["len"]
350 if (len != 3 && len != 4) {
351 types_release(ast)
352 return "!\"Invalid argument length for 'if'. Expects 2 or 3 arguments, supplied " (len - 1) "."
353 }
354 ret = EVAL(types_addref(types_heap[idx][1]), env)
355 if (ret ~ /^!/) {
356 types_release(ast)
357 return ret
358 }
359 types_release(ret)
360 switch (ret) {
361 case "#nil":
362 case "#false":
363 if (len == 3) {
364 body = "#nil"
365 } else {
366 types_addref(body = types_heap[idx][3])
367 }
368 break
369 default:
370 types_addref(body = types_heap[idx][2])
371 break
372 }
373 types_release(ast)
374 return body
375}
376
377function EVAL_fn(ast, env, idx, params, params_idx, params_len, i, sym, f_idx, len)
378{
379 idx = substr(ast, 2)
380 if (types_heap[idx]["len"] != 3) {
381 len = types_heap[idx]["len"]
382 types_release(ast)
383 env_release(env)
384 return "!\"Invalid argument length for 'fn*'. Expects exactly 2 arguments, supplied " (len - 1) "."
385 }
386 params = types_heap[idx][1]
387 if (params !~ /^[([]/) {
388 types_release(ast)
389 env_release(env)
390 return "!\"Incompatible type for argument 1 of 'fn*'. Expects list or vector, supplied " types_typename(params) "."
391 }
392 params_idx = substr(params, 2)
393 params_len = types_heap[params_idx]["len"]
394 for (i = 0; i < params_len; ++i) {
395 sym = types_heap[params_idx][i]
396 if (sym !~ /^'/) {
397 types_release(ast)
398 env_release(env)
399 return "!\"Incompatible type for element of argument 1 of 'fn*'. Expects symbol, supplied " types_typename(sym) "."
400 }
401 if (sym == "'&" && i + 2 != params_len) {
402 types_release(ast)
403 env_release(env)
404 return "!\"Symbol '&' should be followed by last parameter. Parameter list length is " params_len ", position of symbol '&' is " (i + 1) "."
405 }
406 }
407 f_idx = types_allocate()
408 types_addref(types_heap[f_idx]["params"] = types_heap[idx][1])
409 types_addref(types_heap[f_idx]["body"] = types_heap[idx][2])
410 types_heap[f_idx]["env"] = env
411 types_release(ast)
412 return "$" f_idx
413}
414
415function EVAL(ast, env, body, new_ast, ret, idx, len, f, f_idx, ret_body, ret_env)
416{
417 env_addref(env)
418 for (;;) {
419 if (ast !~ /^\(/) {
420 ret = eval_ast(ast, env)
421 types_release(ast)
422 env_release(env)
423 return ret
424 }
425 if (types_heap[substr(ast, 2)]["len"] == 0) {
426 env_release(env)
427 return ast
428 }
429 ast = macroexpand(ast, env)
430 if (ast ~ /^!/) {
431 env_release(env)
432 return ast
433 }
434 if (ast !~ /^\(/) {
435 ret = eval_ast(ast, env)
436 types_release(ast)
437 env_release(env)
438 return ret
439 }
440 idx = substr(ast, 2)
441 len = types_heap[idx]["len"]
442 switch (types_heap[idx][0]) {
443 case "'def!":
444 return EVAL_def(ast, env)
445 case "'let*":
446 ast = EVAL_let(ast, env, ret_env)
447 if (ast ~ /^!/) {
448 return ast
449 }
450 env = ret_env[0]
451 continue
452 case "'quote":
453 if (len != 2) {
454 types_release(ast)
455 env_release(env)
456 return "!\"Invalid argument length for 'quote'. Expects exactly 1 argument, supplied " (len - 1) "."
457 }
458 types_addref(body = types_heap[idx][1])
459 types_release(ast)
460 env_release(env)
461 return body
fbfe6784
NB
462 case "'quasiquoteexpand":
463 env_release(env)
464 if (len != 2) {
465 types_release(ast)
466 return "!\"Invalid argument length for 'quasiquoteexpand'. Expects exactly 1 argument, supplied " (len - 1) "."
467 }
468 types_addref(body = types_heap[idx][1])
469 types_release(ast)
470 return quasiquote(body)
8c7587af
MK
471 case "'quasiquote":
472 if (len != 2) {
473 types_release(ast)
474 env_release(env)
475 return "!\"Invalid argument length for 'quasiquote'. Expects exactly 1 argument, supplied " (len - 1) "."
476 }
477 types_addref(body = types_heap[idx][1])
478 types_release(ast)
479 ast = quasiquote(body)
480 if (ast ~ /^!/) {
481 env_release(env)
482 return ast
483 }
484 continue
485 case "'defmacro!":
486 return EVAL_defmacro(ast, env)
487 case "'macroexpand":
488 if (len != 2) {
489 types_release(ast)
490 env_release(env)
491 return "!\"Invalid argument length for 'macroexpand'. Expects exactly 1 argument, supplied " (len - 1) "."
492 }
493 types_addref(body = types_heap[idx][1])
494 types_release(ast)
495 ret = macroexpand(body, env)
496 env_release(env)
497 return ret
498 case "'try*":
499 ret = EVAL_try(ast, env, ret_body, ret_env)
500 if (ret != "") {
501 return ret
502 }
503 ast = ret_body[0]
504 env = ret_env[0]
505 continue
506 case "'do":
507 ast = EVAL_do(ast, env)
508 if (ast ~ /^!/) {
509 return ast
510 }
511 continue
512 case "'if":
513 ast = EVAL_if(ast, env)
514 if (ast !~ /^['([{]/) {
515 env_release(env)
516 return ast
517 }
518 continue
519 case "'fn*":
520 return EVAL_fn(ast, env)
521 default:
522 new_ast = eval_ast(ast, env)
523 types_release(ast)
524 env_release(env)
525 if (new_ast ~ /^!/) {
526 return new_ast
527 }
528 idx = substr(new_ast, 2)
529 f = types_heap[idx][0]
530 f_idx = substr(f, 2)
531 switch (f) {
532 case /^\$/:
533 env = env_new(types_heap[f_idx]["env"], types_heap[f_idx]["params"], idx)
534 if (env ~ /^!/) {
535 types_release(new_ast)
536 return env
537 }
538 types_addref(ast = types_heap[f_idx]["body"])
539 types_release(new_ast)
540 continue
541 case /^&/:
542 ret = @f_idx(idx)
543 types_release(new_ast)
544 return ret
545 default:
546 types_release(new_ast)
547 return "!\"First element of list must be function, supplied " types_typename(f) "."
548 }
549 }
550 }
551}
552
553function PRINT(expr, str)
554{
555 str = printer_pr_str(expr, 1)
556 types_release(expr)
557 return str
558}
559
560function rep(str, ast, expr)
561{
562 ast = READ(str)
563 if (ast ~ /^!/) {
564 return ast
565 }
566 expr = EVAL(ast, repl_env)
567 if (expr ~ /^!/) {
568 return expr
569 }
570 return PRINT(expr)
571}
572
573function eval(idx)
574{
575 if (types_heap[idx]["len"] != 2) {
576 return "!\"Invalid argument length for builtin function 'eval'. Expects exactly 1 argument, supplied " (types_heap[idx]["len"] - 1) "."
577 }
578 return EVAL(types_addref(types_heap[idx][1]), repl_env)
579}
580
581function main(str, ret, i, idx)
582{
583 repl_env = env_new()
584 for (i in core_ns) {
585 env_set(repl_env, i, core_ns[i])
586 }
587
588 env_set(repl_env, "'eval", "&eval")
589
590 rep("(def! not (fn* (a) (if a false true)))")
e6d41de4 591 rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))")
8c7587af 592 rep("(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)))))))")
8c7587af
MK
593
594 idx = types_allocate()
595 env_set(repl_env, "'*ARGV*", "(" idx)
596 if (ARGC > 1) {
597 for (i = 2; i < ARGC; ++i) {
598 types_heap[idx][i - 2] = "\"" ARGV[i]
599 }
600 types_heap[idx]["len"] = ARGC - 2
601 ARGC = 1
602 rep("(load-file \"" ARGV[1] "\")")
603 return
604 }
605 types_heap[idx]["len"] = 0
606
607 while (1) {
608 printf("user> ")
609 if (getline str <= 0) {
610 break
611 }
612 ret = rep(str)
613 if (ret ~ /^!/) {
614 print "ERROR: " printer_pr_str(substr(ret, 2))
615 } else {
616 print ret
617 }
618 }
619}
620
621BEGIN {
622 main()
623 env_check(0)
624 env_dump()
625 types_dump()
626 exit(0)
627}