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