3 (global $repl_env (mut i32) (i32.const 0))
6 (func $READ (param $str i32) (result i32)
11 (func $is_pair (param $ast i32) (result i32)
13 (set_local $type ($TYPE $ast))
14 (AND (OR (i32.eq $type (get_global $LIST_T))
15 (i32.eq $type (get_global $VECTOR_T)))
16 (i32.ne ($VAL0 $ast) 0))
19 (func $QUASIQUOTE (param $ast i32) (result i32)
20 (local $sym i32 $res i32 $second i32 $third i32)
22 (if (i32.eqz ($is_pair $ast)) ;; QQ_QUOTE
24 (set_local $sym ($STRING (get_global $SYMBOL_T) "quote"))
26 (set_local $res ($LIST2 $sym $ast))
29 (set_local $res ($MEM_VAL1_ptr $ast))
30 (if (AND (i32.eq ($TYPE $res) (get_global $SYMBOL_T))
31 (i32.eqz ($strcmp "unquote" ($to_String $res))))
34 (set_local $res ($INC_REF ($MEM_VAL1_ptr ($MEM_VAL0_ptr $ast)))))
35 (else (if (AND ($is_pair $res)
36 (i32.eq ($TYPE ($MEM_VAL1_ptr $res))
37 (get_global $SYMBOL_T))
38 (i32.eqz ($strcmp "splice-unquote"
39 ($to_String ($MEM_VAL1_ptr $res)))))
41 ;; ['concat, ast[0][1], quasiquote(ast[1..])]
42 (set_local $sym ($STRING (get_global $SYMBOL_T) "concat"))
44 ($MEM_VAL1_ptr ($MEM_VAL0_ptr ($MEM_VAL1_ptr $ast))))
45 (set_local $third ($QUASIQUOTE ($MEM_VAL0_ptr $ast)))
46 (set_local $res ($LIST3 $sym $second $third))
47 ;; release inner quasiquoted since outer list take ownership
51 ;; ['cons, quasiquote(ast[0]), quasiquote(ast[1..])]
52 (set_local $sym ($STRING (get_global $SYMBOL_T) "cons"))
53 (set_local $second ($QUASIQUOTE ($MEM_VAL1_ptr $ast)))
54 (set_local $third ($QUASIQUOTE ($MEM_VAL0_ptr $ast)))
55 (set_local $res ($LIST3 $sym $second $third))
56 ;; release inner quasiquoted since outer list takes ownership
63 (global $mac_ast_stack (mut i32) (i32.const 0))
64 (global $mac_ast_stack_top (mut i32) (i32.const -1))
66 (func $MACROEXPAND (param $orig_ast i32 $env i32) (result i32)
67 (local $ast i32 $mac i32 $mac_env i64)
68 (set_global $mac_ast_stack (STATIC_ARRAY 128))
69 (set_local $ast $orig_ast)
73 (if (OR (i32.ne ($TYPE $ast) (get_global $LIST_T)) ;; a list
74 (i32.eqz ($VAL0 $ast)) ;; non-empty
75 (i32.ne ($TYPE ($MEM_VAL1_ptr $ast)) ;; leading symbol
76 (get_global $SYMBOL_T)))
78 (set_local $mac_env ($ENV_FIND $env ($MEM_VAL1_ptr $ast)))
79 (set_local $mac (i32.wrap/i64 (i64.shr_u $mac_env (i64.const 32))))
80 (if (OR (i32.eqz (i32.wrap/i64 $mac_env)) ;; defined in env
81 (i32.ne ($TYPE $mac) ;; a macro
82 (get_global $MACRO_T)))
86 (set_local $ast ($APPLY $mac ($MEM_VAL0_ptr $ast)))
88 ;; if ast is not the first ast that was passed in, then add it
89 ;; to the pending release list.
90 (if (i32.ne $ast $orig_ast)
92 (set_global $mac_ast_stack_top
93 (i32.add (get_global $mac_ast_stack_top) 1))
95 (get_global $mac_ast_stack)
96 (i32.mul_s (get_global $mac_ast_stack_top) 4))
98 (if (get_global $error_type)
107 (func $EVAL_AST (param $ast i32 $env i32 $skiplast i32) (result i32)
108 (local $res i32 $val2 i32 $val3 i32 $type i32 $found i32)
109 (local $ret i32 $empty i32 $current i32)
111 (if (get_global $error_type) (return 0))
112 (set_local $type ($TYPE $ast))
114 ;;($PR_VALUE ">>> EVAL_AST ast: '%s'\n" $ast)
118 (block $default (block (block
119 (br_table 2 2 2 2 2 0 1 1 1 2 2 2 2 2 2 2 $type))
121 ;; found/res returned as hi 32/lo 32 of i64
122 (set_local $res ($ENV_GET $env $ast))
124 ;; list, vector, hashmap
126 (set_local $res ($MAP_LOOP_START $type))
127 ;; push MAP_LOOP stack
128 ;;; empty = current = ret = res
129 (set_local $ret $res)
130 (set_local $current $res)
131 (set_local $empty $res)
135 ;; check if we are done evaluating the source sequence
136 (if (i32.eq ($VAL0 $ast) 0) (br $done))
139 (if (i32.eqz ($VAL0 ($MEM_VAL0_ptr $ast)) 0) (br $done)))
141 (if (i32.eq $type (get_global $HASHMAP_T))
143 (set_local $res ($EVAL ($MEM_VAL2_ptr $ast) $env)))
145 (set_local $res ($EVAL ($MEM_VAL1_ptr $ast) $env))))
146 (set_local $val2 $res)
148 ;; if error, release the unattached element
149 (if (get_global $error_type)
155 ;; for hash-maps, copy the key (inc ref since we are going
156 ;; to release it below)
157 (if (i32.eq $type (get_global $HASHMAP_T))
159 (set_local $val3 $val2)
160 (set_local $val2 ($MEM_VAL1_ptr $ast))
161 (drop ($INC_REF $val2))))
164 (set_local $res ($MAP_LOOP_UPDATE $type $empty $current $val2 $val3))
165 (if (i32.le_u $current (get_global $EMPTY_HASHMAP))
166 ;; if first element, set return to new element
167 (set_local $ret $res))
168 ;; update current to point to new element
169 (set_local $current $res)
171 (set_local $ast ($MEM_VAL0_ptr $ast))
177 (set_local $res $ret)
178 ;; EVAL_AST_RETURN: nothing to do
181 (set_local $res ($INC_REF $ast))
187 (func $MAL_GET_A1 (param $ast i32) (result i32)
188 ($MEM_VAL1_ptr ($MEM_VAL0_ptr $ast)))
189 (func $MAL_GET_A2 (param $ast i32) (result i32)
190 ($MEM_VAL1_ptr ($MEM_VAL0_ptr ($MEM_VAL0_ptr $ast))))
191 (func $MAL_GET_A3 (param $ast i32) (result i32)
192 ($MEM_VAL1_ptr ($MEM_VAL0_ptr ($MEM_VAL0_ptr ($MEM_VAL0_ptr $ast)))))
194 (func $EVAL (param $orig_ast i32 $orig_env i32) (result i32)
195 (local $ast i32 $env i32 $prev_ast i32 $prev_env i32 $res i32 $el i32)
196 (local $ftype i32 $f_args i32 $f i32 $args i32)
197 (local $a0 i32 $a0sym i32 $a1 i32 $a2 i32)
200 (set_local $ast $orig_ast)
201 (set_local $env $orig_env)
202 (set_local $prev_ast 0)
203 (set_local $prev_env 0)
209 (set_local $f_args 0)
213 ;;($PR_VALUE ">>> EVAL ast: '%s'\n" $ast)
215 (if (get_global $error_type)
220 (if (i32.ne ($TYPE $ast) (get_global $LIST_T))
222 (set_local $res ($EVAL_AST $ast $env 0))
226 (set_local $ast ($MACROEXPAND $ast $env))
227 ;;($PR_VALUE ">>> >>> EVAL ast: '%s'\n" $ast)
229 (if (i32.ne ($TYPE $ast) (get_global $LIST_T))
231 (set_local $res ($EVAL_AST $ast $env 0))
236 (set_local $res ($INC_REF $ast))
239 (set_local $a0 ($MEM_VAL1_ptr $ast))
240 (set_local $a0sym "")
241 (if (i32.eq ($TYPE $a0) (get_global $SYMBOL_T))
242 (set_local $a0sym ($to_String $a0)))
244 (if (i32.eqz ($strcmp "def!" $a0sym))
246 (set_local $a1 ($MAL_GET_A1 $ast))
247 (set_local $a2 ($MAL_GET_A2 $ast))
248 (set_local $res ($EVAL $a2 $env))
249 (if (get_global $error_type) (br $EVAL_return))
251 ;; set a1 in env to a2
252 (set_local $res ($ENV_SET $env $a1 $res))
254 (else (if (i32.eqz ($strcmp "let*" $a0sym))
256 (set_local $a1 ($MAL_GET_A1 $ast))
257 (set_local $a2 ($MAL_GET_A2 $ast))
259 ;; create new environment with outer as current environment
260 (set_local $prev_env $env) ;; save env for later release
261 (set_local $env ($ENV_NEW $env))
265 (if (i32.eqz ($VAL0 $a1))
267 ;; eval current A1 odd element
268 (set_local $res ($EVAL ($MEM_VAL1_ptr ($MEM_VAL0_ptr $a1)) $env))
270 (if (get_global $error_type) (br $done))
272 ;; set key/value in the let environment
273 (set_local $res ($ENV_SET $env ($MEM_VAL1_ptr $a1) $res))
274 ;; release our use, ENV_SET took ownership
277 ;; skip to the next pair of a1 elements
278 (set_local $a1 ($MEM_VAL0_ptr ($MEM_VAL0_ptr $a1)))
283 ;; release previous environment if not the current EVAL env
284 (if (i32.ne $prev_env $orig_env)
287 (set_local $prev_env 0)))
291 (else (if (i32.eqz ($strcmp "do" $a0sym))
293 ;; EVAL the rest through second to last
294 (set_local $el ($EVAL_AST ($MEM_VAL0_ptr $ast) $env 1))
295 (set_local $ast ($LAST $ast))
298 (else (if (i32.eqz ($strcmp "quote" $a0sym))
300 (set_local $res ($INC_REF ($MEM_VAL1_ptr ($MEM_VAL0_ptr $ast))))
302 (else (if (i32.eqz ($strcmp "quasiquote" $a0sym))
304 (set_local $ast ($QUASIQUOTE ($MEM_VAL1_ptr ($MEM_VAL0_ptr $ast))))
306 ;; if we have already been here via TCO, release previous ast
307 (if $prev_ast ($RELEASE $prev_ast))
308 (set_local $prev_ast $ast)
310 (else (if (i32.eqz ($strcmp "defmacro!" $a0sym))
312 (set_local $a1 ($MAL_GET_A1 $ast))
313 (set_local $a2 ($MAL_GET_A2 $ast))
314 (set_local $res ($EVAL $a2 $env))
315 ($SET_TYPE $res (get_global $MACRO_T))
316 (if (get_global $error_type)
319 ;; set a1 in env to a2
320 (set_local $res ($ENV_SET $env $a1 $res))
322 (else (if (i32.eqz ($strcmp "macroexpand" $a0sym))
324 ;; since we are returning it unevaluated, inc the ref cnt
325 (set_local $res ($INC_REF ($MACROEXPAND
326 ($MEM_VAL1_ptr ($MEM_VAL0_ptr $ast))
328 (else (if (i32.eqz ($strcmp "try*" $a0sym))
330 (set_local $a1 ($MAL_GET_A1 $ast))
331 (set_local $res ($EVAL $a1 $env))
333 ;; if there is no error, return
334 (if (i32.eqz (get_global $error_type))
336 ;; if there is an error and res is set, we need to free it
337 ($printf_1 "res value: %d\n" $res)
339 ;; if there is no catch block then return
340 (if (i32.eqz ($VAL0 ($MEM_VAL0_ptr ($MEM_VAL0_ptr $ast))))
343 ;; save the current environment for release
344 (set_local $prev_env $env)
345 ;; create environment for the catch block eval
346 (set_local $env ($ENV_NEW $env))
348 ;; set a1 and a2 from the catch block
349 (set_local $a1 ($MAL_GET_A1 ($MAL_GET_A2 $ast)))
350 (set_local $a2 ($MAL_GET_A2 ($MAL_GET_A2 $ast)))
352 ;; create object for string errors
353 (if (i32.eq (get_global $error_type) 1)
355 (set_local $err ($STRING (get_global $STRING_T)
356 (get_global $error_str))))
358 (set_local $err (get_global $error_val))))
359 ;; bind the catch symbol to the error object
360 (drop ($ENV_SET $env $a1 $err))
361 ;; release our use, env took ownership
364 ;; unset error for catch eval
365 (set_global $error_type 0)
366 (i32.store (get_global $error_str) (CHR "\x00"))
368 ;; release previous environment if not the current EVAL env
369 (if (i32.ne $prev_env $orig_env)
372 (set_local $prev_env 0)))
376 (else (if (i32.eqz ($strcmp "if" $a0sym))
378 (set_local $a1 ($MAL_GET_A1 $ast))
379 (set_local $res ($EVAL $a1 $env))
381 (if (get_global $error_type)
383 (else (if (OR (i32.eq $res (get_global $NIL))
384 (i32.eq $res (get_global $FALSE)))
387 ;; if no false case (A3), return nil
388 (if (i32.lt_u ($COUNT $ast) 4)
390 (set_local $res ($INC_REF (get_global $NIL)))
393 (set_local $ast ($MAL_GET_A3 $ast)))))
396 (set_local $ast ($MAL_GET_A2 $ast))))))
398 (else (if (i32.eqz ($strcmp "fn*" $a0sym))
400 (set_local $a1 ($MAL_GET_A1 $ast))
401 (set_local $a2 ($MAL_GET_A2 $ast))
402 (set_local $res ($MALFUNC $a2 $a1 $env))
406 (set_local $res ($EVAL_AST $ast $env 0))
407 (set_local $f_args $res)
409 ;; if error, return f/args for release by caller
410 (if (get_global $error_type)
412 (set_local $res $f_args)
415 (set_local $args ($MEM_VAL0_ptr $f_args)) ;; rest
416 (set_local $f ($DEREF_META ($MEM_VAL1_ptr $f_args))) ;; value
418 (set_local $ftype ($TYPE $f))
419 (if (i32.eq $ftype (get_global $FUNCTION_T))
421 (if (i32.eq ($VAL0 $f) 0) ;; eval
423 (set_local $res ($EVAL ($MEM_VAL1_ptr $args)
424 (get_global $repl_env))))
426 (set_local $res (call_indirect (type $fnT) $args ($VAL0 $f)))))
430 (else (if (i32.eq $ftype (get_global $MALFUNC_T))
432 ;; save the current environment for release
433 (set_local $prev_env $env)
434 ;; create new environment using env and params stored in function
435 (set_local $env ($ENV_NEW_BINDS ($MEM_VAL2_ptr $f)
436 ($MEM_VAL1_ptr $f) $args))
438 ;; release previous environment if not the current EVAL env
439 ;; because our new env refers to it and we no longer need to
440 ;; track it (since we are TCO recurring)
441 (if (i32.ne $prev_env $orig_env)
444 (set_local $prev_env 0)))
446 ;; claim the AST before releasing the list containing it
447 (set_local $ast ($MEM_VAL0_ptr $f))
448 (drop ($INC_REF $ast))
450 ;; if we have already been here via TCO, release previous
453 (if $prev_ast ($RELEASE $prev_ast))
454 (set_local $prev_ast $ast)
461 ($THROW_STR_1 "apply of non-function type: %d\n" $ftype)
464 (br $EVAL_return)))))))))))))))))))))))))
467 ) ;; end of EVAL_return
470 (if (i32.ne $env $orig_env) ($RELEASE $env))
471 (if $prev_ast ($RELEASE $prev_ast))
477 (func $PRINT (param $ast i32) (result i32)
482 (func $RE (param $line i32 $env i32) (result i32)
483 (local $mv1 i32 $res i32)
485 (set_local $mv1 ($READ $line))
486 (if (get_global $error_type) (br $rep_done))
488 (set_local $res ($EVAL $mv1 $env))
491 ;; release memory from MAL_READ
493 ;; release memory from MACROEXPAND
494 ;; TODO: needs to happen in EVAL
497 (if (i32.lt_s (get_global $mac_ast_stack_top) 0)
499 ($RELEASE (i32.load (i32.add
500 (get_global $mac_ast_stack)
501 (i32.mul_s (get_global $mac_ast_stack_top) 4))))
502 (set_global $mac_ast_stack_top
503 (i32.sub_s (get_global $mac_ast_stack_top) 1))
510 (func $REP (param $line i32 $env i32) (result i32)
511 (local $mv2 i32 $ms i32)
513 (set_local $mv2 ($RE $line $env))
514 (if (get_global $error_type) (br $rep_done))
516 ;; ($PR_MEMORY -1 -1)
517 (set_local $ms ($PRINT $mv2))
520 ;; release memory from RE
525 (func $main (param $argc i32 $argv i32) (result i32)
526 (local $line i32 $res i32 $repl_env i32)
527 ;; argument processing
528 (local $i i32 $ret i32 $empty i32 $current i32 $tmp i32 $val2 i32)
531 ;; ($printf_1 "argc: 0x%x\n" $argc)
532 ;; ($printf_1 "memoryBase: 0x%x\n" (get_global $memoryBase))
533 ;; ($printf_1 "heap_start: 0x%x\n" (get_global $heap_start))
534 ;; ($printf_1 "heap_end: 0x%x\n" (get_global $heap_end))
535 ;; ($printf_1 "mem: 0x%x\n" (get_global $mem))
536 ;; ($printf_1 "string_mem: %d\n" (get_global $string_mem))
538 (set_global $repl_env ($ENV_NEW (get_global $NIL)))
539 (set_local $repl_env (get_global $repl_env))
541 ;; core.EXT: defined in wasm
542 ($add_core_ns $repl_env)
543 (drop ($ENV_SET_S $repl_env "eval" ($FUNCTION 0)))
546 ;; core.mal: defined using the language itself
547 ($RELEASE ($RE "(def! *host-language* \"WebAssembly\")" $repl_env))
548 ($RELEASE ($RE "(def! not (fn* (a) (if a false true)))" $repl_env))
549 ($RELEASE ($RE "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))" $repl_env))
550 ($RELEASE ($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))
551 ($RELEASE ($RE "(def! *gensym-counter* (atom 0))" $repl_env))
552 ($RELEASE ($RE "(def! gensym (fn* [] (symbol (str \"G__\" (swap! *gensym-counter* (fn* [x] (+ 1 x)))))))" $repl_env))
553 ($RELEASE ($RE "(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (c (gensym)) `(let* (~c ~(first xs)) (if ~c ~c (or ~@(rest xs)))))))))" $repl_env))
555 ;; Command line arguments
556 (set_local $res ($MAP_LOOP_START (get_global $LIST_T)))
557 ;; push MAP_LOP stack
558 ;; empty = current = ret = res
559 (set_local $ret $res)
560 (set_local $current $res)
561 (set_local $empty $res)
566 (if (i32.ge_u $i $argc) (br $done))
568 (set_local $val2 ($STRING (get_global $STRING_T)
569 (i32.load (i32.add $argv (i32.mul_u $i 4)))))
572 (set_local $res ($MAP_LOOP_UPDATE
573 (get_global $LIST_T) $empty $current $val2 0))
574 (if (i32.le_u $current (get_global $EMPTY_HASHMAP))
575 ;; if first element, set return to new element
576 (set_local $ret $res))
577 ;; update current to point to new element
578 (set_local $current $res)
580 (set_local $i (i32.add $i 1))
584 (drop ($ENV_SET_S $repl_env "*ARGV*" $ret))
589 (if (i32.gt_u $argc 1)
591 (drop ($ENV_SET_S $repl_env
592 "*FILE*" ($STRING (get_global $STRING_T)
593 (i32.load (i32.add $argv 4)))))
594 ($RELEASE ($RE "(load-file *FILE*)" $repl_env))
595 (if (get_global $error_type)
597 ($printf_1 "Error: %s\n" (get_global $error_str))
602 ($RELEASE ($RE "(println (str \"Mal [\" *host-language* \"]\"))" $repl_env))
607 (set_local $line ($readline "user> "))
608 (if (i32.eqz $line) (br $repl_done))
609 (if (i32.eq (i32.load8_u $line) 0)
613 (set_local $res ($REP $line $repl_env))
614 (if (get_global $error_type)
616 ($printf_1 "Error: %s\n" (get_global $error_str))
617 (set_global $error_type 0))
619 ($printf_1 "%s\n" ($to_String $res))))
622 ;;($PR_MEMORY_SUMMARY_SMALL)
631 (export "_main" (func $main))
632 (export "__post_instantiate" (func $init_memory))