1 ;; Mal value memory layout
3 ;; ---------- ----------
5 ;; false ref/ 1 | 0 | |
7 ;; integer ref/ 2 | int | |
8 ;; float ref/ 3 | ??? | |
9 ;; string/kw ref/ 4 | string ptr | |
10 ;; symbol ref/ 5 | string ptr | |
11 ;; list ref/ 6 | next mem idx | val mem idx |
12 ;; vector ref/ 7 | next mem idx | val mem idx |
13 ;; hashmap ref/ 8 | next mem idx | key mem idx | val mem idx
14 ;; function ref/ 9 | fn idx | |
15 ;; mal function ref/10 | body mem idx | param mem idx | env mem idx
16 ;; macro fn ref/11 | body mem idx | param mem idx | env mem idx
17 ;; atom ref/12 | val mem idx | |
18 ;; environment ref/13 | hmap mem idx | outer mem idx |
19 ;; metadata ref/14 | obj mem idx | meta mem idx |
20 ;; FREE sz/15 | next mem idx | |
25 (global $BOOLEAN_T i32 1)
26 (global $INTEGER_T i32 2)
27 (global $FLOAT_T i32 3)
28 (global $STRING_T i32 4)
29 (global $SYMBOL_T i32 5)
30 (global $LIST_T i32 6)
31 (global $VECTOR_T i32 7)
32 (global $HASHMAP_T i32 8)
33 (global $FUNCTION_T i32 9)
34 (global $MALFUNC_T i32 10)
35 (global $MACRO_T i32 11)
36 (global $ATOM_T i32 12)
37 (global $ENVIRONMENT_T i32 13)
38 (global $METADATA_T i32 14)
39 (global $FREE_T i32 15)
41 (global $error_type (mut i32) 0)
42 (global $error_val (mut i32) 0)
43 ;; Index into static string memory (static.wast)
44 (global $error_str (mut i32) 0)
46 (global $NIL (mut i32) 0)
47 (global $FALSE (mut i32) 0)
48 (global $TRUE (mut i32) 0)
49 (global $EMPTY_LIST (mut i32) 0)
50 (global $EMPTY_VECTOR (mut i32) 0)
51 (global $EMPTY_HASHMAP (mut i32) 0)
53 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
56 (func $INC_REF (param $mv i32) (result i32)
57 (i32.store $mv (i32.add (i32.load $mv) 32))
61 (func $TRUE_FALSE (param $val i32) (result i32)
62 ($INC_REF (if (result i32) $val (global.get $TRUE) (global.get $FALSE)))
65 (func $THROW_STR_0 (param $fmt i32)
66 (drop ($sprintf_1 (global.get $error_str) $fmt ""))
67 (global.set $error_type 1)
70 (func $THROW_STR_1 (param $fmt i32) (param $v0 i32)
71 (drop ($sprintf_1 (global.get $error_str) $fmt $v0))
72 (global.set $error_type 1)
75 (func $EQUAL_Q (param $a i32 $b i32) (result i32)
79 (if (AND (OR (i32.eq $ta (global.get $LIST_T))
80 (i32.eq $ta (global.get $VECTOR_T)))
81 (OR (i32.eq $tb (global.get $LIST_T))
82 (i32.eq $tb (global.get $VECTOR_T))))
87 (if (OR (i32.eq ($VAL0 $a) 0) (i32.eq ($VAL0 $b) 0))
89 (if ($EQUAL_Q ($MEM_VAL1_ptr $a) ($MEM_VAL1_ptr $b))
91 (local.set $a ($MEM_VAL0_ptr $a))
92 (local.set $b ($MEM_VAL0_ptr $b)))
98 (return (AND (i32.eq ($VAL0 $a) 0) (i32.eq ($VAL0 $b) 0))))
99 (else (if (AND (i32.eq $ta (global.get $HASHMAP_T))
100 (i32.eq $tb (global.get $HASHMAP_T)))
103 ;; TODO: remove this once strings are interned
104 (else (if (OR (AND (i32.eq $ta (global.get $STRING_T))
105 (i32.eq $tb (global.get $STRING_T)))
106 (AND (i32.eq $ta (global.get $SYMBOL_T))
107 (i32.eq $tb (global.get $SYMBOL_T))))
108 (then (return (i32.eqz ($strcmp ($to_String $a) ($to_String $b)))))
110 (return (AND (i32.eq $ta $tb)
111 (i32.eq ($VAL0 $a) ($VAL0 $b))))))))))
115 (func $DEREF_META (param $mv i32) (result i32)
117 (if (i32.eq ($TYPE $mv) (global.get $METADATA_T))
119 (local.set $mv ($MEM_VAL0_ptr $mv))
125 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
128 (func $to_MalString (param $mv i32) (result i32)
129 ;; TODO: assert mv is a string/keyword/symbol
130 (i32.add (global.get $string_mem) ($VAL0 $mv))
133 (func $to_String (param $mv i32) (result i32)
134 ;; skip string refcnt and size
135 (i32.add 4 ($to_MalString $mv))
138 ;; Duplicate regular character array string into a Mal string and
139 ;; return the MalVal pointer
140 (func $STRING (param $type i32 $str i32) (result i32)
141 (LET $ms ($ALLOC_STRING $str ($strlen $str) 1))
142 ($ALLOC_SCALAR $type (i32.sub $ms (global.get $string_mem)))
145 ;; Find first duplicate (internet) of mv. If one is found, free up
146 ;; mv and return the interned version. If no duplicate is found,
148 (func $INTERN_STRING (param $mv i32) (result i32)
150 $ms ($to_MalString $mv)
151 $existing_ms ($FIND_STRING (i32.add $ms 4))
153 (if (AND $existing_ms (i32.lt_s $existing_ms $ms))
156 (local.set $res ($ALLOC_SCALAR (global.get $STRING_T)
157 (i32.sub $existing_ms
158 (global.get $string_mem))))
159 (i32.store16 $existing_ms (i32.add (i32.load16_u $existing_ms) 1))
164 (func $STRING_INIT (param $type i32) (result i32)
165 (LET $ms ($ALLOC_STRING "" 0 0))
166 ($ALLOC_SCALAR $type (i32.sub $ms (global.get $string_mem)))
169 (func $STRING_FINALIZE (param $mv i32 $size i32) (result i32)
170 ;; Check if the new string can be interned.
171 (LET $tmp ($INTERN_STRING $mv)
172 $ms ($to_MalString $mv))
175 (local.set $mv $tmp))
177 ;;; ms->size = sizeof(MalString) + size + 1
178 (i32.store16 (i32.add $ms 2)
179 (i32.add (i32.add 4 $size) 1))
180 ;;; string_mem_next = (void *)ms + ms->size
181 (global.set $string_mem_next
182 (i32.add $ms (i32.load16_u (i32.add $ms 2))))))
186 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
189 (func $INTEGER (param $val i32) (result i32)
190 ($ALLOC_SCALAR (global.get $INTEGER_T) $val)
193 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
194 ;; sequence functions
196 (func $MAP_LOOP_START (param $type i32) (result i32)
197 (LET $res (if (result i32) (i32.eq $type (global.get $LIST_T))
198 (then (global.get $EMPTY_LIST))
199 (else (if (result i32) (i32.eq $type (global.get $VECTOR_T))
200 (then (global.get $EMPTY_VECTOR))
201 (else (if (result i32) (i32.eq $type (global.get $HASHMAP_T))
202 (then (global.get $EMPTY_HASHMAP))
204 ($THROW_STR_1 "read_seq invalid type %d" $type)
210 (func $MAP_LOOP_UPDATE (param $type i32) (param $empty i32)
211 (param $current i32) (param $val2 i32) (param $val3 i32)
213 (LET $res ($ALLOC $type $empty $val2 $val3))
215 ;; sequence took ownership
218 (if (i32.eq $type (global.get $HASHMAP_T))
220 (if (i32.gt_u $current (global.get $EMPTY_HASHMAP))
221 ;; if not first element, set current next to point to new element
222 (i32.store ($VAL0_ptr $current) ($IDX $res)))
227 (func $FORCE_SEQ_TYPE (param $type i32) (param $mv i32) (result i32)
229 ;; if it's already the right type, inc ref cnt and return it
230 (if (i32.eq $type ($TYPE $mv)) (return ($INC_REF $mv)))
231 ;; if it's empty, return the sequence match
232 (if (i32.le_u $mv (global.get $EMPTY_HASHMAP))
233 (return ($MAP_LOOP_START $type)))
234 ;; otherwise, copy first element to turn it into correct type
235 ($ALLOC $type ($MEM_VAL0_ptr $mv) ($MEM_VAL1_ptr $mv) 0)
238 (func $LIST (param $seq i32 $first i32) (result i32)
239 ($ALLOC (global.get $LIST_T) $seq $first 0)
242 (func $LIST2 (param $first i32 $second i32) (result i32)
243 ;; last element is empty list
244 (LET $tmp ($LIST (global.get $EMPTY_LIST) $second)
245 $res ($LIST $tmp $first))
246 ($RELEASE $tmp) ;; new list takes ownership of previous
250 (func $LIST3 (param $first i32 $second i32 $third i32) (result i32)
251 (LET $tmp ($LIST2 $second $third)
252 $res ($LIST $tmp $first))
253 ($RELEASE $tmp) ;; new list takes ownership of previous
257 (func $LIST_Q (param $mv i32) (result i32)
258 (i32.eq ($TYPE $mv) (global.get $LIST_T))
261 (func $EMPTY_Q (param $mv i32) (result i32)
262 (i32.eq ($VAL0 $mv) 0)
265 (func $COUNT (param $mv i32) (result i32)
269 (if (i32.eq ($VAL0 $mv) 0) (br $done))
270 (local.set $cnt (i32.add $cnt 1))
271 (local.set $mv ($MEM_VAL0_ptr $mv))
278 (func $LAST (param $mv i32) (result i32)
280 ;; TODO: check that actually a list/vector
281 (if (i32.eq ($VAL0 $mv) 0)
282 ;; empty seq, return nil
283 (return ($INC_REF (global.get $NIL))))
286 ;; end, return previous value
287 (if (i32.eq ($VAL0 $mv) 0) (br $done))
288 ;; current becomes previous entry
291 (local.set $mv ($MEM_VAL0_ptr $mv))
295 ($INC_REF ($MEM_VAL1_ptr $cur))
298 ;; make a copy of sequence seq from index start to end
299 ;; set last to last element of slice before the empty
300 ;; set after to element following slice (or original)
301 (func $SLICE (param $seq i32) (param $start i32) (param $end i32)
304 $res ($INC_REF (global.get $EMPTY_LIST))
307 ;; advance seq to start
310 (if (OR (i32.ge_s $idx $start)
311 (i32.eqz ($VAL0 $seq)))
313 (local.set $seq ($MEM_VAL0_ptr $seq))
314 (local.set $idx (i32.add $idx 1))
320 ;; if current position is at end, then return or if we reached
321 ;; end seq, then return
322 (if (OR (AND (i32.ne $end -1)
323 (i32.ge_s $idx $end))
324 (i32.eqz ($VAL0 $seq)))
326 (local.set $res $tmp)
328 ;; allocate new list element with copied value
329 (local.set $res ($LIST (global.get $EMPTY_LIST)
330 ($MEM_VAL1_ptr $seq)))
331 ;; sequence took ownership
332 ($RELEASE (global.get $EMPTY_LIST))
335 ;; if first element, set return value to new element
336 (local.set $tmp $res))
338 ;; if not the first element, set return value to new element
339 (i32.store ($VAL0_ptr $last) ($IDX $res))))
340 (local.set $last $res) ;; update last list element
341 ;; advance to next element of seq
342 (local.set $seq ($MEM_VAL0_ptr $seq))
343 (local.set $idx (i32.add $idx 1))
348 ;; combine last/res as hi 32/low 32 of i64
350 (i64.shl (i64.extend_i32_u $last) (i64.const 32))
351 (i64.extend_i32_u $res))
354 (func $HASHMAP (result i32)
355 ;; just point to static empty hash-map
356 ($INC_REF (global.get $EMPTY_HASHMAP))
359 (func $ASSOC1 (param $hm i32 $k i32 $v i32) (result i32)
360 (LET $res ($ALLOC (global.get $HASHMAP_T) $hm $k $v))
361 ;; we took ownership of previous release
366 (func $ASSOC1_S (param $hm i32 $k i32 $v i32) (result i32)
367 (LET $kmv ($STRING (global.get $STRING_T) $k)
368 $res ($ASSOC1 $hm $kmv $v))
369 ;; map took ownership of key
374 (func $HASHMAP_GET (param $hm i32) (param $key_mv i32) (result i64)
375 (LET $key ($to_String $key_mv)
382 ;;; if (VAL0(hm) == 0)
383 (if (i32.eq ($VAL0 $hm) 0)
385 (local.set $res (global.get $NIL))
387 ;;; test_key_mv = MEM_VAL1(hm)
388 (local.set $test_key_mv ($MEM_VAL1_ptr $hm))
389 ;;; if (strcmp(key, to_String(test_key_mv)) == 0)
390 (if (i32.eq ($strcmp $key ($to_String $test_key_mv)) 0)
393 (local.set $res ($MEM_VAL2_ptr $hm))
395 (local.set $hm ($MEM_VAL0_ptr $hm))
401 ;; combine found/res as hi 32/low 32 of i64
402 (i64.or (i64.shl (i64.extend_i32_u $found) (i64.const 32))
403 (i64.extend_i32_u $res))
406 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
407 ;; function functions
409 (func $FUNCTION (param $index i32) (result i32)
410 ($ALLOC_SCALAR (global.get $FUNCTION_T) $index)
413 (func $MALFUNC (param $ast i32 $params i32 $env i32) (result i32)
414 ($ALLOC (global.get $MALFUNC_T) $ast $params $env)