Commit | Line | Data |
---|---|---|
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 | ) |