@grain/stdlib 0.4.4 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/CHANGELOG.md +87 -0
  2. package/LICENSE +1 -1
  3. package/array.gr +92 -73
  4. package/array.md +18 -18
  5. package/bigint.gr +497 -0
  6. package/bigint.md +811 -0
  7. package/buffer.gr +56 -217
  8. package/buffer.md +24 -17
  9. package/bytes.gr +103 -205
  10. package/bytes.md +19 -0
  11. package/char.gr +152 -166
  12. package/char.md +200 -0
  13. package/exception.md +6 -0
  14. package/float32.gr +159 -82
  15. package/float32.md +315 -0
  16. package/float64.gr +163 -82
  17. package/float64.md +315 -0
  18. package/hash.gr +53 -49
  19. package/int32.gr +479 -230
  20. package/int32.md +937 -0
  21. package/int64.gr +479 -230
  22. package/int64.md +937 -0
  23. package/list.gr +530 -116
  24. package/list.md +1141 -0
  25. package/map.gr +302 -121
  26. package/map.md +525 -0
  27. package/number.gr +51 -57
  28. package/number.md +37 -3
  29. package/option.gr +25 -25
  30. package/option.md +1 -1
  31. package/package.json +3 -3
  32. package/pervasives.gr +504 -52
  33. package/pervasives.md +1116 -0
  34. package/queue.gr +8 -1
  35. package/queue.md +10 -0
  36. package/random.gr +196 -0
  37. package/random.md +179 -0
  38. package/range.gr +26 -26
  39. package/regex.gr +1833 -842
  40. package/regex.md +11 -11
  41. package/result.md +1 -1
  42. package/runtime/bigint.gr +2045 -0
  43. package/runtime/bigint.md +326 -0
  44. package/runtime/dataStructures.gr +99 -279
  45. package/runtime/dataStructures.md +391 -0
  46. package/runtime/debug.gr +0 -1
  47. package/runtime/debug.md +6 -0
  48. package/runtime/equal.gr +40 -37
  49. package/runtime/equal.md +6 -0
  50. package/runtime/exception.gr +28 -15
  51. package/runtime/exception.md +30 -0
  52. package/runtime/gc.gr +50 -20
  53. package/runtime/gc.md +36 -0
  54. package/runtime/malloc.gr +32 -22
  55. package/runtime/malloc.md +55 -0
  56. package/runtime/numberUtils.gr +297 -142
  57. package/runtime/numberUtils.md +54 -0
  58. package/runtime/numbers.gr +1204 -453
  59. package/runtime/numbers.md +300 -0
  60. package/runtime/string.gr +193 -228
  61. package/runtime/string.md +24 -0
  62. package/runtime/stringUtils.gr +62 -38
  63. package/runtime/stringUtils.md +6 -0
  64. package/runtime/unsafe/constants.gr +17 -0
  65. package/runtime/unsafe/constants.md +72 -0
  66. package/runtime/unsafe/conv.gr +10 -10
  67. package/runtime/unsafe/conv.md +71 -0
  68. package/runtime/unsafe/errors.md +204 -0
  69. package/runtime/unsafe/memory.gr +14 -3
  70. package/runtime/unsafe/memory.md +54 -0
  71. package/runtime/unsafe/printWasm.gr +4 -4
  72. package/runtime/unsafe/printWasm.md +24 -0
  73. package/runtime/unsafe/tags.gr +11 -10
  74. package/runtime/unsafe/tags.md +120 -0
  75. package/runtime/unsafe/wasmf32.gr +9 -2
  76. package/runtime/unsafe/wasmf32.md +168 -0
  77. package/runtime/unsafe/wasmf64.gr +9 -2
  78. package/runtime/unsafe/wasmf64.md +168 -0
  79. package/runtime/unsafe/wasmi32.gr +65 -47
  80. package/runtime/unsafe/wasmi32.md +282 -0
  81. package/runtime/unsafe/wasmi64.gr +78 -50
  82. package/runtime/unsafe/wasmi64.md +300 -0
  83. package/runtime/utils/printing.gr +62 -0
  84. package/runtime/utils/printing.md +18 -0
  85. package/runtime/wasi.gr +200 -46
  86. package/runtime/wasi.md +839 -0
  87. package/set.gr +125 -121
  88. package/set.md +24 -21
  89. package/stack.gr +29 -29
  90. package/stack.md +4 -6
  91. package/string.gr +434 -415
  92. package/string.md +3 -3
  93. package/sys/file.gr +477 -482
  94. package/sys/process.gr +33 -47
  95. package/sys/random.gr +48 -20
  96. package/sys/random.md +38 -0
  97. package/sys/time.gr +12 -28
package/runtime/string.gr CHANGED
@@ -7,6 +7,7 @@ import WasmI32, {
7
7
  divS as (/),
8
8
  shl as (<<),
9
9
  shrS as (>>),
10
+ shrU as (>>>),
10
11
  and as (&),
11
12
  or as (|),
12
13
  eq as (==),
@@ -14,44 +15,37 @@ import WasmI32, {
14
15
  geS as (>=),
15
16
  gtS as (>),
16
17
  leS as (<=),
17
- ltS as (<)
18
+ ltS as (<),
18
19
  } from "runtime/unsafe/wasmi32"
19
20
  import WasmI64 from "runtime/unsafe/wasmi64"
20
21
  import WasmF32 from "runtime/unsafe/wasmf32"
21
22
  import WasmF64 from "runtime/unsafe/wasmf64"
23
+ import BI from "runtime/bigint"
22
24
  import Memory from "runtime/unsafe/memory"
23
25
  import Tags from "runtime/unsafe/tags"
24
26
  import NumberUtils from "runtime/numberUtils"
25
27
 
26
28
  import { allocateString, allocateArray } from "runtime/dataStructures"
27
29
 
28
- import foreign wasm fd_write: (WasmI32, WasmI32, WasmI32, WasmI32) -> WasmI32 from "wasi_snapshot_preview1"
30
+ import foreign wasm fd_write: (
31
+ WasmI32,
32
+ WasmI32,
33
+ WasmI32,
34
+ WasmI32,
35
+ ) -> WasmI32 from "wasi_snapshot_preview1"
29
36
 
30
- primitive (!) : Bool -> Bool = "@not"
31
- primitive (&&) : (Bool, Bool) -> Bool = "@and"
32
- primitive (||) : (Bool, Bool) -> Bool = "@or"
37
+ primitive (!): Bool -> Bool = "@not"
38
+ primitive (&&): (Bool, Bool) -> Bool = "@and"
39
+ primitive (||): (Bool, Bool) -> Bool = "@or"
33
40
 
34
- enum StringList { SLEmpty, SLCons(String, StringList) }
35
-
36
- @disableGC
37
- let slConsDisableGc = (a, b) => {
38
- Memory.incRef(WasmI32.fromGrain(SLCons))
39
- Memory.incRef(WasmI32.fromGrain(a))
40
- // for easier chaining, we don't incRef `b`
41
- SLCons(a, b)
41
+ enum StringList {
42
+ [],
43
+ [...](String, StringList),
42
44
  }
43
45
 
44
- @disableGC
45
- let mut _RUNTIME_TYPE_METADATA_PTR = 0x01n;
46
-
47
- @disableGC
48
- let initPtr = () => {
49
- // hack to avoid incRef on this pointer
50
- _RUNTIME_TYPE_METADATA_PTR = 0x408n
51
- }
52
- initPtr();
46
+ primitive _RUNTIME_TYPE_METADATA_PTR: WasmI32 = "@heap.type_metadata"
53
47
 
54
- @disableGC
48
+ @unsafe
55
49
  let findTypeMetadata = (moduleId, typeId) => {
56
50
  let mut metadataPtr = WasmI32.load(_RUNTIME_TYPE_METADATA_PTR, 0n)
57
51
  let mut modData = -1n
@@ -81,8 +75,8 @@ let findTypeMetadata = (moduleId, typeId) => {
81
75
  }
82
76
  }
83
77
 
84
- @disableGC
85
- let getVariantName = (variant) => {
78
+ @unsafe
79
+ let getVariantName = variant => {
86
80
  let moduleId = WasmI32.load(variant, 4n) >> 1n
87
81
  let typeId = WasmI32.load(variant, 8n) >> 1n
88
82
  let variantId = WasmI32.load(variant, 12n) >> 1n
@@ -112,8 +106,8 @@ let getVariantName = (variant) => {
112
106
  }
113
107
  }
114
108
 
115
- @disableGC
116
- let getRecordFieldNames = (record_) => {
109
+ @unsafe
110
+ let getRecordFieldNames = record_ => {
117
111
  let moduleId = WasmI32.load(record_, 4n) >> 1n
118
112
  let typeId = WasmI32.load(record_, 8n) >> 1n
119
113
  let arity = WasmI32.load(record_, 12n)
@@ -131,8 +125,9 @@ let getRecordFieldNames = (record_) => {
131
125
  for (let mut i = 0n; i < arity; i += 1n) {
132
126
  let fieldLength = WasmI32.load(fields + fieldOffset, 4n)
133
127
  let fieldName = allocateString(fieldLength)
128
+ Memory.incRef(fieldName)
134
129
  Memory.copy(fieldName + 8n, fields + fieldOffset + 8n, fieldLength)
135
- WasmI32.store(fieldArray + (i * 4n), fieldName, 8n)
130
+ WasmI32.store(fieldArray + i * 4n, fieldName, 8n)
136
131
 
137
132
  fieldOffset += WasmI32.load(fields + fieldOffset, 0n)
138
133
  }
@@ -141,50 +136,50 @@ let getRecordFieldNames = (record_) => {
141
136
  }
142
137
  }
143
138
 
144
- @disableGC
139
+ @unsafe
145
140
  let rec totalBytes = (acc, list) => {
146
141
  match (list) {
147
- SLCons(hd, tl) => totalBytes(acc + WasmI32.load(WasmI32.fromGrain(hd), 4n), tl),
148
- SLEmpty => acc
142
+ [hd, ...tl] =>
143
+ totalBytes(acc + WasmI32.load(WasmI32.fromGrain(hd), 4n), tl),
144
+ [] => acc,
149
145
  }
150
146
  }
151
147
 
152
- @disableGC
148
+ @unsafe
153
149
  let rec writeStrings = (buf, list) => {
154
150
  match (list) {
155
- SLCons(hd, tl) => {
151
+ [hd, ...tl] => {
156
152
  let hd = WasmI32.fromGrain(hd)
157
153
  let hdSize = WasmI32.load(hd, 4n)
158
154
  Memory.copy(buf, hd + 8n, hdSize)
159
155
  writeStrings(buf + hdSize, tl)
160
156
  },
161
- SLEmpty => void
157
+ [] => void,
162
158
  }
163
159
  }
164
160
 
165
- @disableGC
166
- let join = (list) => {
161
+ @unsafe
162
+ let join = list => {
167
163
  let len = totalBytes(0n, list)
168
164
  let str = allocateString(len)
169
165
  writeStrings(str + 8n, list)
170
166
  WasmI32.toGrain(str): String
171
167
  }
172
168
 
173
- @disableGC
174
- let reverse = (list) => {
175
- @disableGC
169
+ @unsafe
170
+ let reverse = list => {
171
+ @unsafe
176
172
  let rec iter = (list, acc) => {
177
173
  match (list) {
178
- SLEmpty => acc,
179
- SLCons(first, rest) => iter(rest, slConsDisableGc(first, acc))
174
+ [] => acc,
175
+ [first, ...rest] => iter(rest, [first, ...acc]),
180
176
  }
181
177
  }
182
- Memory.incRef(WasmI32.fromGrain(SLEmpty))
183
- iter(list, SLEmpty)
178
+ iter(list, [])
184
179
  }
185
180
 
186
- @disableGC
187
- export let rec concat = (s1: String, s2: String) => {
181
+ @unsafe
182
+ export let concat = (s1: String, s2: String) => {
188
183
  let ptr1 = WasmI32.fromGrain(s1)
189
184
  let ptr2 = WasmI32.fromGrain(s2)
190
185
 
@@ -196,14 +191,10 @@ export let rec concat = (s1: String, s2: String) => {
196
191
  Memory.copy(newString + 8n, ptr1 + 8n, size1)
197
192
  Memory.copy(newString + 8n + size1, ptr2 + 8n, size2)
198
193
 
199
- let ret = WasmI32.toGrain(newString): String
200
- Memory.decRef(ptr1)
201
- Memory.decRef(ptr2)
202
- Memory.decRef(WasmI32.fromGrain(concat))
203
- ret
194
+ WasmI32.toGrain(newString): String
204
195
  }
205
196
 
206
- @disableGC
197
+ @unsafe
207
198
  let escape = (ptr, isString) => {
208
199
  let _SEQ_B = 0x08n
209
200
  let _SEQ_F = 0x0Cn
@@ -217,30 +208,20 @@ let escape = (ptr, isString) => {
217
208
 
218
209
  let _SEQ_QUOTE = if (isString) _SEQ_DQUOTE else _SEQ_SQUOTE
219
210
 
220
- let size = if (isString) {
221
- WasmI32.load(ptr, 4n)
222
- } else {
223
- let byte = WasmI32.load8U(ptr, 4n)
224
-
225
- // Calculate number of bytes via utf-8 encoding
226
- if ((byte & 0x80n) == 0x00n) {
227
- 1n
228
- } else if ((byte & 0xF0n) == 0xF0n) {
229
- 4n
230
- } else if ((byte & 0xE0n) == 0xE0n) {
231
- 3n
232
- } else {
233
- 2n
234
- }
235
- }
211
+ let size = WasmI32.load(ptr, 4n)
236
212
 
237
- let startOffset = if (isString) 8n else 4n
213
+ let startOffset = 8n
238
214
  let stringOffset = 8n
239
215
 
240
216
  let mut newSize = 2n // extra space for quote characters
241
217
  for (let mut i = 0n; i < size; i += 1n) {
242
218
  let byte = WasmI32.load8U(ptr + i, startOffset)
243
- if (byte >= _SEQ_B && byte <= _SEQ_R /* b, f, n, r, t, v */ || byte == _SEQ_V || byte == _SEQ_SLASH || byte == _SEQ_QUOTE) {
219
+ if (
220
+ byte >= _SEQ_B && byte <= _SEQ_R || /* b, f, n, r, t, v */
221
+ byte == _SEQ_V ||
222
+ byte == _SEQ_SLASH ||
223
+ byte == _SEQ_QUOTE
224
+ ) {
244
225
  newSize += 2n
245
226
  } else {
246
227
  newSize += 1n
@@ -254,7 +235,12 @@ let escape = (ptr, isString) => {
254
235
 
255
236
  for (let mut i = 0n; i < size; i += 1n) {
256
237
  let byte = WasmI32.load8U(ptr + i, startOffset)
257
- if (byte >= _SEQ_B && byte <= _SEQ_R /* b, f, n, r, t, v */ || byte == _SEQ_V || byte == _SEQ_SLASH || byte == _SEQ_QUOTE) {
238
+ if (
239
+ byte >= _SEQ_B && byte <= _SEQ_R || /* b, f, n, r, t, v */
240
+ byte == _SEQ_V ||
241
+ byte == _SEQ_SLASH ||
242
+ byte == _SEQ_QUOTE
243
+ ) {
258
244
  WasmI32.store8(escapedString + j, _SEQ_SLASH, stringOffset)
259
245
  j += 1n
260
246
  let seq = match (byte) {
@@ -264,7 +250,7 @@ let escape = (ptr, isString) => {
264
250
  r when r == _SEQ_R => 0x72n,
265
251
  t when t == _SEQ_T => 0x74n,
266
252
  v when v == _SEQ_V => 0x76n,
267
- _ => byte
253
+ _ => byte,
268
254
  }
269
255
  WasmI32.store8(escapedString + j, seq, stringOffset)
270
256
  j += 1n
@@ -280,33 +266,65 @@ let escape = (ptr, isString) => {
280
266
  WasmI32.toGrain(escapedString): String
281
267
  }
282
268
 
283
- @disableGC
269
+ @unsafe
284
270
  let escapeString = (s: String) => {
285
271
  escape(WasmI32.fromGrain(s), true)
286
272
  }
287
273
 
288
- @disableGC
289
- let escapeChar = (c: Char) => {
290
- escape(WasmI32.fromGrain(c), false)
274
+ @unsafe
275
+ let escapeChar = (s: String) => {
276
+ escape(WasmI32.fromGrain(s), false)
277
+ }
278
+
279
+ @unsafe
280
+ let usvToString = usv => {
281
+ if (usv < 0x80n) {
282
+ let string = allocateString(1n)
283
+ WasmI32.store8(string, usv, 8n)
284
+ WasmI32.toGrain(string): String
285
+ } else {
286
+ let mut count = 0n
287
+ let mut offset = 0n
288
+ if (usv <= 0x07FFn) {
289
+ count = 1n
290
+ offset = 0xC0n
291
+ } else if (usv <= 0xFFFFn) {
292
+ count = 2n
293
+ offset = 0xE0n
294
+ } else {
295
+ count = 3n
296
+ offset = 0xF0n
297
+ }
298
+ let string = allocateString(count + 1n)
299
+ WasmI32.store8(string, (usv >>> 6n * count) + offset, 8n)
300
+
301
+ let mut n = 0n
302
+ while (count > 0n) {
303
+ n += 1n
304
+ let temp = usv >>> 6n * (count - 1n)
305
+ WasmI32.store8(string + n, 0x80n | temp & 0x3Fn, 8n)
306
+ count -= 1n
307
+ }
308
+
309
+ WasmI32.toGrain(string): String
310
+ }
291
311
  }
292
312
 
293
- @disableGC
313
+ @unsafe
294
314
  let isListVariant = (variant: String) => {
295
315
  // Only lists can start with `[`, so only need to check for that
296
316
  // Sort of a hack until we have a better solution
297
317
  WasmI32.load8U(WasmI32.fromGrain(variant), 8n) == 0x5Bn
298
318
  }
299
319
 
300
- @disableGC
320
+ @unsafe
301
321
  let rec heapValueToString = (ptr, extraIndents, toplevel) => {
302
322
  let tag = WasmI32.load(ptr, 0n)
303
323
  match (tag) {
304
324
  t when t == Tags._GRAIN_STRING_HEAP_TAG => {
325
+ // In both cases, we incRef ptr so we can use it again via WasmI32.toGrain
326
+ Memory.incRef(ptr)
305
327
  if (toplevel) {
306
- // We can just return the pointer since its already a string and
307
- // doesn't need escaping in this case, but by convention its reference
308
- // count needs to be incremented.
309
- Memory.incRef(ptr)
310
328
  WasmI32.toGrain(ptr): String
311
329
  } else {
312
330
  escapeString(WasmI32.toGrain(ptr))
@@ -350,25 +368,6 @@ let rec heapValueToString = (ptr, extraIndents, toplevel) => {
350
368
 
351
369
  WasmI32.toGrain(str): String
352
370
  },
353
- t when t == Tags._GRAIN_CHAR_HEAP_TAG => {
354
- if (toplevel) {
355
- let byte = WasmI32.load8U(ptr, 4n)
356
- let numBytes = if ((byte & 0x80n) == 0x00n) {
357
- 1n
358
- } else if ((byte & 0xF0n) == 0xF0n) {
359
- 4n
360
- } else if ((byte & 0xE0n) == 0xE0n) {
361
- 3n
362
- } else {
363
- 2n
364
- }
365
- let str = allocateString(numBytes)
366
- Memory.copy(str + 8n, ptr + 4n, numBytes)
367
- WasmI32.toGrain(str): String
368
- } else {
369
- escapeChar(WasmI32.toGrain(ptr))
370
- }
371
- },
372
371
  t when t == Tags._GRAIN_ADT_HEAP_TAG => {
373
372
  // [ <value type tag>, <module_tag>, <type_tag>, <variant_tag>, <arity>, elts ... ]
374
373
  let variantName = getVariantName(ptr)
@@ -387,26 +386,22 @@ let rec heapValueToString = (ptr, extraIndents, toplevel) => {
387
386
  } else {
388
387
  let comspace = ", "
389
388
  let rparen = ")"
390
- Memory.incRef(WasmI32.fromGrain(SLEmpty))
391
- let mut strings = slConsDisableGc(rparen, SLEmpty)
389
+ let mut strings = [rparen]
392
390
  for (let mut i = variantArity * 4n - 4n; i >= 0n; i -= 4n) {
393
- let tmp = toStringHelp(WasmI32.load(ptr + i, 20n), extraIndents, false)
394
- strings = slConsDisableGc(tmp, strings)
395
- Memory.decRef(WasmI32.fromGrain(tmp))
391
+ let tmp = toStringHelp(
392
+ WasmI32.load(ptr + i, 20n),
393
+ extraIndents,
394
+ false
395
+ )
396
+ strings = [tmp, ...strings]
396
397
  if (i > 0n) {
397
- strings = slConsDisableGc(comspace, strings)
398
+ strings = [comspace, ...strings]
398
399
  }
399
400
  }
400
- Memory.incRef(WasmI32.fromGrain(variantName))
401
401
  let lparen = "("
402
- strings = slConsDisableGc(variantName, slConsDisableGc(lparen, strings))
403
- let string = join(strings)
404
- Memory.decRef(WasmI32.fromGrain(variantName))
405
- Memory.decRef(WasmI32.fromGrain(lparen))
406
- Memory.decRef(WasmI32.fromGrain(rparen))
407
- Memory.decRef(WasmI32.fromGrain(strings))
408
- Memory.decRef(WasmI32.fromGrain(comspace))
409
- string
402
+ strings = [variantName, lparen, ...strings]
403
+
404
+ join(strings)
410
405
  }
411
406
  }
412
407
  }
@@ -418,70 +413,60 @@ let rec heapValueToString = (ptr, extraIndents, toplevel) => {
418
413
  "<record value>"
419
414
  } else {
420
415
  let prevPadAmount = extraIndents * 2n
421
- let prevSpacePadding = if (prevPadAmount == 0n) "" else {
416
+ let prevSpacePadding = if (prevPadAmount == 0n) {
417
+ ""
418
+ } else {
422
419
  let v = allocateString(prevPadAmount)
423
- Memory.fill(v + 8n, 0x20n, prevPadAmount) // create indentation for closing brace
424
- WasmI32.toGrain(v) : String
420
+ Memory.fill(
421
+ v + 8n,
422
+ 0x20n,
423
+ prevPadAmount
424
+ ) // create indentation for closing brace
425
+ WasmI32.toGrain(v): String
425
426
  }
426
427
  let padAmount = (extraIndents + 1n) * 2n
427
428
  let spacePadding = allocateString(padAmount)
428
429
  Memory.fill(spacePadding + 8n, 0x20n, padAmount) // create indentation
429
430
  let spacePadding = WasmI32.toGrain(spacePadding): String
430
- Memory.incRef(WasmI32.fromGrain(SLEmpty))
431
431
  let newline = "\n"
432
432
  let rbrace = "}"
433
- let mut strings = slConsDisableGc(newline, slConsDisableGc(prevSpacePadding, slConsDisableGc(rbrace, SLEmpty)))
433
+ let mut strings = [newline, prevSpacePadding, rbrace]
434
434
  let colspace = ": "
435
435
  let comlf = ",\n"
436
- for (let mut i = recordArity * 4n - 4n; i >= 0n; i -= 4n) {
436
+ for (let mut i = recordArity * 4n - 4n; i >= 0n; i -= 4n) {
437
437
  let fieldName = WasmI32.toGrain(WasmI32.load(fields + i, 8n)): String
438
- let fieldValue = toStringHelp(WasmI32.load(ptr + i, 16n), extraIndents + 1n, false)
439
- strings = slConsDisableGc(spacePadding, slConsDisableGc(fieldName, slConsDisableGc(colspace, slConsDisableGc(fieldValue, strings))))
440
- Memory.decRef(WasmI32.fromGrain(fieldValue))
438
+ let fieldValue = toStringHelp(
439
+ WasmI32.load(ptr + i, 16n),
440
+ extraIndents + 1n,
441
+ false
442
+ )
443
+ strings = [spacePadding, fieldName, colspace, fieldValue, ...strings]
441
444
  if (i > 0n) {
442
- strings = slConsDisableGc(comlf, strings)
445
+ strings = [comlf, ...strings]
443
446
  }
444
447
  }
445
448
  let lbrace = "{\n"
446
- strings = slConsDisableGc(lbrace, strings)
447
- let string = join(strings)
448
-
449
- Memory.decRef(WasmI32.fromGrain(strings))
450
- Memory.decRef(WasmI32.fromGrain(spacePadding))
451
- Memory.decRef(WasmI32.fromGrain(colspace))
452
- Memory.decRef(WasmI32.fromGrain(comlf))
453
- Memory.decRef(WasmI32.fromGrain(lbrace))
454
- Memory.decRef(WasmI32.fromGrain(rbrace))
455
- Memory.decRef(WasmI32.fromGrain(newline))
456
-
457
- Memory.free(fields) // Avoid double-free of record field names
449
+ strings = [lbrace, ...strings]
458
450
 
459
- string
451
+ join(strings)
460
452
  }
461
453
  },
462
454
  t when t == Tags._GRAIN_ARRAY_HEAP_TAG => {
463
455
  let arity = WasmI32.load(ptr, 4n)
464
- Memory.incRef(WasmI32.fromGrain(SLEmpty))
465
456
  let rbrack = "]"
466
- let mut strings = slConsDisableGc(rbrack, SLEmpty)
457
+ let mut strings = [rbrack]
467
458
  let comspace = ", "
468
459
  for (let mut i = arity * 4n - 4n; i >= 0n; i -= 4n) {
469
460
  let item = toStringHelp(WasmI32.load(ptr + i, 8n), extraIndents, false)
470
- strings = slConsDisableGc(item, strings)
471
- Memory.decRef(WasmI32.fromGrain(item))
461
+ strings = [item, ...strings]
472
462
  if (i > 0n) {
473
- strings = slConsDisableGc(comspace, strings)
463
+ strings = [comspace, ...strings]
474
464
  }
475
465
  }
476
466
  let lbrack = "[> "
477
- strings = slConsDisableGc(lbrack, strings)
478
- let string = join(strings)
479
- Memory.decRef(WasmI32.fromGrain(strings))
480
- Memory.decRef(WasmI32.fromGrain(comspace))
481
- Memory.decRef(WasmI32.fromGrain(lbrack))
482
- Memory.decRef(WasmI32.fromGrain(rbrack))
483
-
484
- string
467
+ strings = [lbrack, ...strings]
468
+
469
+ join(strings)
485
470
  },
486
471
  t when t == Tags._GRAIN_BOXED_NUM_HEAP_TAG => {
487
472
  let numberTag = WasmI32.load(ptr, 4n)
@@ -492,18 +477,15 @@ let rec heapValueToString = (ptr, extraIndents, toplevel) => {
492
477
  t when t == Tags._GRAIN_INT64_BOXED_NUM_TAG => {
493
478
  NumberUtils.itoa64(WasmI64.load(ptr, 8n), 10n)
494
479
  },
480
+ t when t == Tags._GRAIN_BIGINT_BOXED_NUM_TAG => {
481
+ BI.bigIntToString10(ptr)
482
+ },
495
483
  t when t == Tags._GRAIN_RATIONAL_BOXED_NUM_TAG => {
496
- let numerator = NumberUtils.itoa32(WasmI32.load(ptr, 8n), 10n)
497
- let denominator = NumberUtils.itoa32(WasmI32.load(ptr, 12n), 10n)
498
- Memory.incRef(WasmI32.fromGrain(SLEmpty))
484
+ let numerator = BI.bigIntToString10(WasmI32.load(ptr, 8n))
485
+ let denominator = BI.bigIntToString10(WasmI32.load(ptr, 12n))
499
486
  let slash = "/"
500
- let strings = slConsDisableGc(numerator, slConsDisableGc(slash, slConsDisableGc(denominator, SLEmpty)))
501
- let string = join(strings)
502
- Memory.decRef(WasmI32.fromGrain(strings))
503
- Memory.decRef(WasmI32.fromGrain(numerator))
504
- Memory.decRef(WasmI32.fromGrain(denominator))
505
- Memory.decRef(WasmI32.fromGrain(slash))
506
- string
487
+ let strings = [numerator, slash, denominator]
488
+ join(strings)
507
489
  },
508
490
  t when t == Tags._GRAIN_FLOAT32_BOXED_NUM_TAG => {
509
491
  NumberUtils.dtoa(WasmF64.promoteF32(WasmF32.load(ptr, 8n)))
@@ -513,7 +495,7 @@ let rec heapValueToString = (ptr, extraIndents, toplevel) => {
513
495
  },
514
496
  _ => {
515
497
  "<unknown boxed number>"
516
- }
498
+ },
517
499
  }
518
500
  },
519
501
  t when t == Tags._GRAIN_TUPLE_HEAP_TAG => {
@@ -523,78 +505,80 @@ let rec heapValueToString = (ptr, extraIndents, toplevel) => {
523
505
  } else {
524
506
  WasmI32.store(ptr, 0x80000000n | tupleLength, 4n)
525
507
  let comspace = ", "
526
- Memory.incRef(WasmI32.fromGrain(SLEmpty))
527
508
  let rparen = ")"
528
- let mut strings = slConsDisableGc(rparen, SLEmpty)
509
+ let mut strings = [rparen]
529
510
  if (tupleLength <= 1n) {
530
511
  // Special case: unary tuple
531
512
  let comma = ","
532
- strings = slConsDisableGc(comma, strings)
533
- Memory.decRef(WasmI32.fromGrain(comma))
513
+ strings = [comma, ...strings]
534
514
  void
535
515
  }
536
516
  for (let mut i = tupleLength * 4n - 4n; i >= 0n; i -= 4n) {
537
- let item = toStringHelp(WasmI32.load(ptr + i, 8n), extraIndents, false)
538
- strings = slConsDisableGc(item, strings)
539
- Memory.decRef(WasmI32.fromGrain(item))
517
+ let item = toStringHelp(
518
+ WasmI32.load(ptr + i, 8n),
519
+ extraIndents,
520
+ false
521
+ )
522
+ strings = [item, ...strings]
540
523
  if (i > 0n) {
541
- strings = slConsDisableGc(comspace, strings)
524
+ strings = [comspace, ...strings]
542
525
  }
543
526
  }
544
527
  WasmI32.store(ptr, tupleLength, 4n)
545
528
 
546
529
  Memory.incRef(WasmI32.fromGrain(strings))
547
530
  let lparen = "("
548
- strings = slConsDisableGc(lparen, strings)
531
+ strings = [lparen, ...strings]
549
532
 
550
- let string = join(strings)
551
- Memory.decRef(WasmI32.fromGrain(strings))
552
- Memory.decRef(WasmI32.fromGrain(comspace))
553
- Memory.decRef(WasmI32.fromGrain(rparen))
554
- Memory.decRef(WasmI32.fromGrain(lparen))
555
-
556
- string
533
+ join(strings)
557
534
  }
558
535
  },
559
536
  t when t == Tags._GRAIN_LAMBDA_HEAP_TAG => {
560
537
  "<lambda>"
561
538
  },
562
539
  _ => {
563
- Memory.incRef(WasmI32.fromGrain(SLEmpty))
564
- let strings = slConsDisableGc(
540
+ let strings = [
565
541
  "<unknown heap tag type: 0x",
566
- slConsDisableGc(NumberUtils.itoa32(tag, 16n),
567
- slConsDisableGc(" | value: 0x",
568
- slConsDisableGc(NumberUtils.itoa32(ptr, 16n),
569
- slConsDisableGc(">", SLEmpty)))))
570
- let string = join(strings)
571
- Memory.decRef(WasmI32.fromGrain(strings))
572
- string
573
- }
542
+ NumberUtils.itoa32(tag, 16n),
543
+ " | value: 0x",
544
+ NumberUtils.itoa32(ptr, 16n),
545
+ ">",
546
+ ]
547
+ join(strings)
548
+ },
574
549
  }
575
550
  }, toStringHelp = (grainValue, extraIndents, toplevel) => {
576
551
  if ((grainValue & 1n) != 0n) {
577
552
  // Simple (unboxed) numbers
578
553
  NumberUtils.itoa32(grainValue >> 1n, 10n)
579
- } else if ((grainValue & 7n) == Tags._GRAIN_GENERIC_HEAP_TAG_TYPE) {
580
- heapValueToString(grainValue, extraIndents, toplevel)
581
- } else if (grainValue == WasmI32.fromGrain(true)) {
582
- "true"
583
- } else if (grainValue == WasmI32.fromGrain(false)) {
584
- "false"
585
- } else if (grainValue == WasmI32.fromGrain(void)) {
586
- "void"
587
554
  } else {
588
- "<unknown value>"
555
+ let tag = grainValue & 7n
556
+ if (tag == Tags._GRAIN_GENERIC_HEAP_TAG_TYPE) {
557
+ heapValueToString(grainValue, extraIndents, toplevel)
558
+ } else if (tag == Tags._GRAIN_CHAR_TAG_TYPE) {
559
+ let string = usvToString(grainValue >> 3n)
560
+ if (toplevel) {
561
+ string
562
+ } else {
563
+ escapeChar(string)
564
+ }
565
+ } else if (grainValue == WasmI32.fromGrain(true)) {
566
+ "true"
567
+ } else if (grainValue == WasmI32.fromGrain(false)) {
568
+ "false"
569
+ } else if (grainValue == WasmI32.fromGrain(void)) {
570
+ "void"
571
+ } else {
572
+ "<unknown value>"
573
+ }
589
574
  }
590
575
  }, listToString = (ptr, extraIndents) => {
591
576
  let mut cur = ptr
592
577
  let mut isFirst = true
593
578
 
594
- Memory.incRef(WasmI32.fromGrain(SLEmpty))
595
579
  let lbrack = "["
596
580
  let commaspace = ", "
597
- let mut strings = slConsDisableGc(lbrack, SLEmpty)
581
+ let mut strings = [lbrack]
598
582
 
599
583
  while (true) {
600
584
  let variantId = WasmI32.load(cur, 12n) >> 1n // tagged number
@@ -602,46 +586,30 @@ let rec heapValueToString = (ptr, extraIndents, toplevel) => {
602
586
  break
603
587
  } else {
604
588
  if (!isFirst) {
605
- strings = slConsDisableGc(commaspace, strings)
589
+ strings = [commaspace, ...strings]
606
590
  }
607
591
  isFirst = false
608
592
  let item = toStringHelp(WasmI32.load(cur, 20n), extraIndents, false)
609
- strings = slConsDisableGc(item, strings)
610
- Memory.decRef(WasmI32.fromGrain(item))
593
+ strings = [item, ...strings]
611
594
  cur = WasmI32.load(cur, 24n)
612
595
  }
613
596
  }
614
597
  let rbrack = "]"
615
- strings = slConsDisableGc(rbrack, strings)
598
+ strings = [rbrack, ...strings]
616
599
  let reversed = reverse(strings)
617
- let string = join(reversed)
618
- Memory.decRef(WasmI32.fromGrain(strings))
619
- Memory.decRef(WasmI32.fromGrain(reversed))
620
- Memory.decRef(WasmI32.fromGrain(lbrack))
621
- Memory.decRef(WasmI32.fromGrain(rbrack))
622
- Memory.decRef(WasmI32.fromGrain(commaspace))
623
- string
600
+ join(reversed)
624
601
  }
625
602
 
626
- @disableGC
627
- export let rec toString = (value) => {
603
+ @unsafe
604
+ export let toString = value => {
628
605
  let value = WasmI32.fromGrain(value)
629
- let ret = toStringHelp(value, 0n, true)
630
- Memory.decRef(value)
631
- Memory.decRef(WasmI32.fromGrain(toString))
632
- ret
606
+ toStringHelp(value, 0n, true)
633
607
  }
634
608
 
635
- @disableGC
636
- export let rec print = (value) => {
637
- // First convert the value to string, if it isn't one already. Calling
638
- // toString will either return value instance itself if it's already a
639
- // string, or a new string with the content of value object, which will need
640
- // to be deallocated. In either case it's sufficient to force decrement its
641
- // reference count.
609
+ @unsafe
610
+ export let print = value => {
611
+ // First convert the value to string, if it isn't one already.
642
612
  let valuePtr = WasmI32.fromGrain(value)
643
- Memory.incRef(WasmI32.fromGrain(toString))
644
- Memory.incRef(valuePtr) // for function call
645
613
  let s = toString(value)
646
614
  let ptr = WasmI32.fromGrain(s)
647
615
  // iov: [<ptr to string> <nbytes of string> <ptr to newline> <nbytes of newline>] (32 bytes)
@@ -658,8 +626,5 @@ export let rec print = (value) => {
658
626
  WasmI32.store(iov, 1n, 12n)
659
627
  fd_write(1n, iov, 2n, written)
660
628
  Memory.free(buf)
661
- Memory.decRef(ptr)
662
- Memory.decRef(WasmI32.fromGrain(print))
663
- Memory.decRef(WasmI32.fromGrain(value))
664
629
  void
665
630
  }