DISABLE FDs (REMOVE ME).
[jackhill/mal.git] / wasm / types.wam
CommitLineData
33309c6a
JM
1;; Mal value memory layout
2;; type words
3;; ---------- ----------
4;; nil ref/ 0 | 0 | |
5;; false ref/ 1 | 0 | |
6;; true ref/ 1 | 1 | |
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 | |
21
22(module $types
23
24 (global $NIL_T i32 0)
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)
40
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)
45
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)
52
53 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
54 ;; General functions
55
56 (func $INC_REF (param $mv i32) (result i32)
57 (i32.store $mv (i32.add (i32.load $mv) 32))
3ea09886
JM
58 $mv
59 )
60
61 (func $TRUE_FALSE (param $val i32) (result i32)
0a19c2f1 62 ($INC_REF (if (result i32) $val (global.get $TRUE) (global.get $FALSE)))
3ea09886 63 )
33309c6a
JM
64
65 (func $THROW_STR_0 (param $fmt i32)
0a19c2f1
JM
66 (drop ($sprintf_1 (global.get $error_str) $fmt ""))
67 (global.set $error_type 1)
3ea09886 68 )
33309c6a
JM
69
70 (func $THROW_STR_1 (param $fmt i32) (param $v0 i32)
0a19c2f1
JM
71 (drop ($sprintf_1 (global.get $error_str) $fmt $v0))
72 (global.set $error_type 1)
3ea09886
JM
73 )
74
75 (func $EQUAL_Q (param $a i32 $b i32) (result i32)
349faa83
JM
76 (LET $ta ($TYPE $a)
77 $tb ($TYPE $b))
3ea09886 78
0a19c2f1
JM
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))))
3ea09886
JM
83 (then
84 ;; EQUAL_Q_SEQ
85 (block $done
86 (loop $loop
87 (if (OR (i32.eq ($VAL0 $a) 0) (i32.eq ($VAL0 $b) 0))
88 (br $done))
89 (if ($EQUAL_Q ($MEM_VAL1_ptr $a) ($MEM_VAL1_ptr $b))
90 (then
0a19c2f1
JM
91 (local.set $a ($MEM_VAL0_ptr $a))
92 (local.set $b ($MEM_VAL0_ptr $b)))
3ea09886
JM
93 (else
94 (return 0)))
95 (br $loop)
96 )
97 )
98 (return (AND (i32.eq ($VAL0 $a) 0) (i32.eq ($VAL0 $b) 0))))
0a19c2f1
JM
99 (else (if (AND (i32.eq $ta (global.get $HASHMAP_T))
100 (i32.eq $tb (global.get $HASHMAP_T)))
3ea09886 101 ;; EQUAL_Q_HM
77bf4e61 102 (then (return 1))
3ea09886 103 ;; TODO: remove this once strings are interned
0a19c2f1
JM
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))))
77bf4e61 108 (then (return (i32.eqz ($strcmp ($to_String $a) ($to_String $b)))))
3ea09886
JM
109 (else
110 (return (AND (i32.eq $ta $tb)
77bf4e61
JM
111 (i32.eq ($VAL0 $a) ($VAL0 $b))))))))))
112 0 ;; not reachable
3ea09886
JM
113 )
114
115 (func $DEREF_META (param $mv i32) (result i32)
116 (loop $loop
0a19c2f1 117 (if (i32.eq ($TYPE $mv) (global.get $METADATA_T))
3ea09886 118 (then
0a19c2f1 119 (local.set $mv ($MEM_VAL0_ptr $mv))
3ea09886
JM
120 (br $loop)))
121 )
122 $mv
123 )
124
125 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
126 ;; string functions
127
50eea9ad
JM
128 (func $to_MalString (param $mv i32) (result i32)
129 ;; TODO: assert mv is a string/keyword/symbol
0a19c2f1 130 (i32.add (global.get $string_mem) ($VAL0 $mv))
50eea9ad
JM
131 )
132
3ea09886 133 (func $to_String (param $mv i32) (result i32)
50eea9ad
JM
134 ;; skip string refcnt and size
135 (i32.add 4 ($to_MalString $mv))
136 )
137
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)
349faa83 141 (LET $ms ($ALLOC_STRING $str ($strlen $str) 1))
0a19c2f1 142 ($ALLOC_SCALAR $type (i32.sub $ms (global.get $string_mem)))
50eea9ad
JM
143 )
144
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,
147 ;; return NULL.
148 (func $INTERN_STRING (param $mv i32) (result i32)
349faa83
JM
149 (LET $res 0
150 $ms ($to_MalString $mv)
151 $existing_ms ($FIND_STRING (i32.add $ms 4))
152 $tmp 0)
50eea9ad
JM
153 (if (AND $existing_ms (i32.lt_s $existing_ms $ms))
154 (then
0a19c2f1
JM
155 (local.set $tmp $mv)
156 (local.set $res ($ALLOC_SCALAR (global.get $STRING_T)
0c62f14e 157 (i32.sub $existing_ms
0a19c2f1 158 (global.get $string_mem))))
0c62f14e 159 (i32.store16 $existing_ms (i32.add (i32.load16_u $existing_ms) 1))
50eea9ad
JM
160 ($RELEASE $tmp)))
161 $res
162 )
163
164 (func $STRING_INIT (param $type i32) (result i32)
349faa83 165 (LET $ms ($ALLOC_STRING "" 0 0))
0a19c2f1 166 ($ALLOC_SCALAR $type (i32.sub $ms (global.get $string_mem)))
50eea9ad
JM
167 )
168
169 (func $STRING_FINALIZE (param $mv i32 $size i32) (result i32)
50eea9ad 170 ;; Check if the new string can be interned.
349faa83
JM
171 (LET $tmp ($INTERN_STRING $mv)
172 $ms ($to_MalString $mv))
50eea9ad
JM
173 (if $tmp
174 (then
0a19c2f1 175 (local.set $mv $tmp))
50eea9ad
JM
176 (else
177 ;;; ms->size = sizeof(MalString) + size + 1
0c62f14e 178 (i32.store16 (i32.add $ms 2)
50eea9ad
JM
179 (i32.add (i32.add 4 $size) 1))
180 ;;; string_mem_next = (void *)ms + ms->size
0a19c2f1 181 (global.set $string_mem_next
50eea9ad
JM
182 (i32.add $ms (i32.load16_u (i32.add $ms 2))))))
183 $mv
3ea09886 184 )
33309c6a
JM
185
186 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
187 ;; numeric functions
188
189 (func $INTEGER (param $val i32) (result i32)
0a19c2f1 190 ($ALLOC_SCALAR (global.get $INTEGER_T) $val)
3ea09886 191 )
33309c6a
JM
192
193 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
194 ;; sequence functions
195
196 (func $MAP_LOOP_START (param $type i32) (result i32)
0a19c2f1
JM
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))
349faa83
JM
203 (else
204 ($THROW_STR_1 "read_seq invalid type %d" $type)
205 0)))))))
33309c6a
JM
206
207 ($INC_REF $res)
208 )
209
210 (func $MAP_LOOP_UPDATE (param $type i32) (param $empty i32)
211 (param $current i32) (param $val2 i32) (param $val3 i32)
212 (result i32)
349faa83 213 (LET $res ($ALLOC $type $empty $val2 $val3))
33309c6a 214
33309c6a
JM
215 ;; sequence took ownership
216 ($RELEASE $empty)
217 ($RELEASE $val2)
0a19c2f1 218 (if (i32.eq $type (global.get $HASHMAP_T))
33309c6a 219 ($RELEASE $val3))
0a19c2f1 220 (if (i32.gt_u $current (global.get $EMPTY_HASHMAP))
33309c6a 221 ;; if not first element, set current next to point to new element
3ea09886
JM
222 (i32.store ($VAL0_ptr $current) ($IDX $res)))
223
224 $res
225 )
226
227 (func $FORCE_SEQ_TYPE (param $type i32) (param $mv i32) (result i32)
349faa83 228 (LET $res 0)
3ea09886
JM
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
0a19c2f1 232 (if (i32.le_u $mv (global.get $EMPTY_HASHMAP))
3ea09886
JM
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)
236 )
237
238 (func $LIST (param $seq i32 $first i32) (result i32)
0a19c2f1 239 ($ALLOC (global.get $LIST_T) $seq $first 0)
3ea09886 240 )
33309c6a 241
3ea09886
JM
242 (func $LIST2 (param $first i32 $second i32) (result i32)
243 ;; last element is empty list
0a19c2f1 244 (LET $tmp ($LIST (global.get $EMPTY_LIST) $second)
349faa83 245 $res ($LIST $tmp $first))
3ea09886 246 ($RELEASE $tmp) ;; new list takes ownership of previous
33309c6a
JM
247 $res
248 )
249
3ea09886 250 (func $LIST3 (param $first i32 $second i32 $third i32) (result i32)
349faa83
JM
251 (LET $tmp ($LIST2 $second $third)
252 $res ($LIST $tmp $first))
3ea09886
JM
253 ($RELEASE $tmp) ;; new list takes ownership of previous
254 $res
255 )
256
257 (func $LIST_Q (param $mv i32) (result i32)
0a19c2f1 258 (i32.eq ($TYPE $mv) (global.get $LIST_T))
3ea09886
JM
259 )
260
33309c6a
JM
261 (func $EMPTY_Q (param $mv i32) (result i32)
262 (i32.eq ($VAL0 $mv) 0)
263 )
264
3ea09886 265 (func $COUNT (param $mv i32) (result i32)
349faa83 266 (LET $cnt 0)
3ea09886
JM
267 (block $done
268 (loop $loop
269 (if (i32.eq ($VAL0 $mv) 0) (br $done))
0a19c2f1
JM
270 (local.set $cnt (i32.add $cnt 1))
271 (local.set $mv ($MEM_VAL0_ptr $mv))
3ea09886
JM
272 (br $loop)
273 )
274 )
275 $cnt
276 )
277
278 (func $LAST (param $mv i32) (result i32)
349faa83 279 (LET $cur 0)
3ea09886
JM
280 ;; TODO: check that actually a list/vector
281 (if (i32.eq ($VAL0 $mv) 0)
282 ;; empty seq, return nil
0a19c2f1 283 (return ($INC_REF (global.get $NIL))))
3ea09886
JM
284 (block $done
285 (loop $loop
286 ;; end, return previous value
287 (if (i32.eq ($VAL0 $mv) 0) (br $done))
288 ;; current becomes previous entry
0a19c2f1 289 (local.set $cur $mv)
3ea09886 290 ;; next entry
0a19c2f1 291 (local.set $mv ($MEM_VAL0_ptr $mv))
3ea09886
JM
292 (br $loop)
293 )
294 )
295 ($INC_REF ($MEM_VAL1_ptr $cur))
296 )
297
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)
302 (result i64)
349faa83 303 (LET $idx 0
0a19c2f1 304 $res ($INC_REF (global.get $EMPTY_LIST))
349faa83
JM
305 $last 0
306 $tmp $res)
3ea09886
JM
307 ;; advance seq to start
308 (block $done
309 (loop $loop
310 (if (OR (i32.ge_s $idx $start)
311 (i32.eqz ($VAL0 $seq)))
312 (br $done))
0a19c2f1
JM
313 (local.set $seq ($MEM_VAL0_ptr $seq))
314 (local.set $idx (i32.add $idx 1))
3ea09886
JM
315 (br $loop)
316 )
317 )
318 (block $done
319 (loop $loop
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)))
325 (then
0a19c2f1 326 (local.set $res $tmp)
3ea09886
JM
327 (br $done)))
328 ;; allocate new list element with copied value
0a19c2f1 329 (local.set $res ($LIST (global.get $EMPTY_LIST)
3ea09886
JM
330 ($MEM_VAL1_ptr $seq)))
331 ;; sequence took ownership
0a19c2f1 332 ($RELEASE (global.get $EMPTY_LIST))
3ea09886
JM
333 (if (i32.eqz $last)
334 (then
335 ;; if first element, set return value to new element
0a19c2f1 336 (local.set $tmp $res))
3ea09886
JM
337 (else
338 ;; if not the first element, set return value to new element
339 (i32.store ($VAL0_ptr $last) ($IDX $res))))
0a19c2f1 340 (local.set $last $res) ;; update last list element
3ea09886 341 ;; advance to next element of seq
0a19c2f1
JM
342 (local.set $seq ($MEM_VAL0_ptr $seq))
343 (local.set $idx (i32.add $idx 1))
3ea09886
JM
344 (br $loop)
345 )
346 )
347
348 ;; combine last/res as hi 32/low 32 of i64
349 (i64.or
0a19c2f1
JM
350 (i64.shl (i64.extend_i32_u $last) (i64.const 32))
351 (i64.extend_i32_u $res))
3ea09886
JM
352 )
353
33309c6a
JM
354 (func $HASHMAP (result i32)
355 ;; just point to static empty hash-map
0a19c2f1 356 ($INC_REF (global.get $EMPTY_HASHMAP))
33309c6a
JM
357 )
358
3ea09886 359 (func $ASSOC1 (param $hm i32 $k i32 $v i32) (result i32)
0a19c2f1 360 (LET $res ($ALLOC (global.get $HASHMAP_T) $hm $k $v))
33309c6a
JM
361 ;; we took ownership of previous release
362 ($RELEASE $hm)
363 $res
364 )
365
3ea09886 366 (func $ASSOC1_S (param $hm i32 $k i32 $v i32) (result i32)
0a19c2f1 367 (LET $kmv ($STRING (global.get $STRING_T) $k)
349faa83 368 $res ($ASSOC1 $hm $kmv $v))
33309c6a
JM
369 ;; map took ownership of key
370 ($RELEASE $kmv)
371 $res
372 )
373
374 (func $HASHMAP_GET (param $hm i32) (param $key_mv i32) (result i64)
349faa83
JM
375 (LET $key ($to_String $key_mv)
376 $found 0
377 $res 0
378 $test_key_mv 0)
33309c6a
JM
379
380 (block $done
381 (loop $loop
382 ;;; if (VAL0(hm) == 0)
383 (if (i32.eq ($VAL0 $hm) 0)
384 (then
0a19c2f1 385 (local.set $res (global.get $NIL))
33309c6a
JM
386 (br $done)))
387 ;;; test_key_mv = MEM_VAL1(hm)
0a19c2f1 388 (local.set $test_key_mv ($MEM_VAL1_ptr $hm))
33309c6a
JM
389 ;;; if (strcmp(key, to_String(test_key_mv)) == 0)
390 (if (i32.eq ($strcmp $key ($to_String $test_key_mv)) 0)
391 (then
0a19c2f1
JM
392 (local.set $found 1)
393 (local.set $res ($MEM_VAL2_ptr $hm))
33309c6a 394 (br $done)))
0a19c2f1 395 (local.set $hm ($MEM_VAL0_ptr $hm))
33309c6a
JM
396
397 (br $loop)
398 )
399 )
400
401 ;; combine found/res as hi 32/low 32 of i64
0a19c2f1
JM
402 (i64.or (i64.shl (i64.extend_i32_u $found) (i64.const 32))
403 (i64.extend_i32_u $res))
33309c6a
JM
404 )
405
406 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
407 ;; function functions
408
409 (func $FUNCTION (param $index i32) (result i32)
0a19c2f1 410 ($ALLOC_SCALAR (global.get $FUNCTION_T) $index)
33309c6a
JM
411 )
412
3ea09886 413 (func $MALFUNC (param $ast i32 $params i32 $env i32) (result i32)
0a19c2f1 414 ($ALLOC (global.get $MALFUNC_T) $ast $params $env)
3ea09886 415 )
33309c6a 416
33309c6a 417)