@grain/stdlib 0.4.6 → 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 (82) hide show
  1. package/CHANGELOG.md +73 -0
  2. package/array.gr +18 -18
  3. package/array.md +18 -18
  4. package/bigint.gr +497 -0
  5. package/bigint.md +811 -0
  6. package/buffer.gr +49 -213
  7. package/buffer.md +24 -17
  8. package/bytes.gr +100 -202
  9. package/bytes.md +19 -0
  10. package/char.gr +63 -133
  11. package/exception.md +6 -0
  12. package/float32.gr +39 -78
  13. package/float64.gr +43 -78
  14. package/hash.gr +37 -37
  15. package/int32.gr +152 -198
  16. package/int32.md +104 -0
  17. package/int64.gr +151 -197
  18. package/int64.md +104 -0
  19. package/list.gr +467 -70
  20. package/list.md +1141 -0
  21. package/map.gr +192 -7
  22. package/map.md +525 -0
  23. package/number.gr +30 -54
  24. package/number.md +3 -3
  25. package/option.md +1 -1
  26. package/package.json +3 -3
  27. package/pervasives.gr +499 -59
  28. package/pervasives.md +1116 -0
  29. package/queue.gr +4 -0
  30. package/queue.md +10 -0
  31. package/random.gr +196 -0
  32. package/random.md +179 -0
  33. package/regex.gr +1833 -842
  34. package/regex.md +11 -11
  35. package/result.md +1 -1
  36. package/runtime/bigint.gr +2045 -0
  37. package/runtime/bigint.md +326 -0
  38. package/runtime/dataStructures.gr +99 -278
  39. package/runtime/dataStructures.md +391 -0
  40. package/runtime/debug.md +6 -0
  41. package/runtime/equal.gr +5 -23
  42. package/runtime/equal.md +6 -0
  43. package/runtime/exception.md +30 -0
  44. package/runtime/gc.gr +20 -3
  45. package/runtime/gc.md +36 -0
  46. package/runtime/malloc.gr +13 -11
  47. package/runtime/malloc.md +55 -0
  48. package/runtime/numberUtils.gr +91 -41
  49. package/runtime/numberUtils.md +54 -0
  50. package/runtime/numbers.gr +1043 -391
  51. package/runtime/numbers.md +300 -0
  52. package/runtime/string.gr +136 -230
  53. package/runtime/string.md +24 -0
  54. package/runtime/stringUtils.gr +58 -38
  55. package/runtime/stringUtils.md +6 -0
  56. package/runtime/unsafe/constants.gr +17 -0
  57. package/runtime/unsafe/constants.md +72 -0
  58. package/runtime/unsafe/conv.md +71 -0
  59. package/runtime/unsafe/errors.md +204 -0
  60. package/runtime/unsafe/memory.md +54 -0
  61. package/runtime/unsafe/printWasm.md +24 -0
  62. package/runtime/unsafe/tags.gr +9 -8
  63. package/runtime/unsafe/tags.md +120 -0
  64. package/runtime/unsafe/wasmf32.md +168 -0
  65. package/runtime/unsafe/wasmf64.md +168 -0
  66. package/runtime/unsafe/wasmi32.md +282 -0
  67. package/runtime/unsafe/wasmi64.md +300 -0
  68. package/runtime/utils/printing.gr +62 -0
  69. package/runtime/utils/printing.md +18 -0
  70. package/runtime/wasi.gr +1 -1
  71. package/runtime/wasi.md +839 -0
  72. package/set.gr +17 -8
  73. package/set.md +24 -21
  74. package/stack.gr +3 -3
  75. package/stack.md +4 -6
  76. package/string.gr +194 -329
  77. package/string.md +3 -3
  78. package/sys/file.gr +245 -429
  79. package/sys/process.gr +27 -45
  80. package/sys/random.gr +47 -16
  81. package/sys/random.md +38 -0
  82. package/sys/time.gr +11 -27
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 (==),
@@ -19,6 +20,7 @@ import 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"
@@ -37,29 +39,13 @@ primitive (&&): (Bool, Bool) -> Bool = "@and"
37
39
  primitive (||): (Bool, Bool) -> Bool = "@or"
38
40
 
39
41
  enum StringList {
40
- SLEmpty,
41
- SLCons(String, StringList),
42
+ [],
43
+ [...](String, StringList),
42
44
  }
43
45
 
44
- @disableGC
45
- let slConsDisableGc = (a, b) => {
46
- Memory.incRef(WasmI32.fromGrain(SLCons))
47
- Memory.incRef(WasmI32.fromGrain(a))
48
- // for easier chaining, we don't incRef `b`
49
- SLCons(a, b)
50
- }
51
-
52
- @disableGC
53
- let mut _RUNTIME_TYPE_METADATA_PTR = 0x01n
46
+ primitive _RUNTIME_TYPE_METADATA_PTR: WasmI32 = "@heap.type_metadata"
54
47
 
55
- @disableGC
56
- let initPtr = () => {
57
- // hack to avoid incRef on this pointer
58
- _RUNTIME_TYPE_METADATA_PTR = 0x408n
59
- }
60
- initPtr()
61
-
62
- @disableGC
48
+ @unsafe
63
49
  let findTypeMetadata = (moduleId, typeId) => {
64
50
  let mut metadataPtr = WasmI32.load(_RUNTIME_TYPE_METADATA_PTR, 0n)
65
51
  let mut modData = -1n
@@ -89,7 +75,7 @@ let findTypeMetadata = (moduleId, typeId) => {
89
75
  }
90
76
  }
91
77
 
92
- @disableGC
78
+ @unsafe
93
79
  let getVariantName = variant => {
94
80
  let moduleId = WasmI32.load(variant, 4n) >> 1n
95
81
  let typeId = WasmI32.load(variant, 8n) >> 1n
@@ -120,7 +106,7 @@ let getVariantName = variant => {
120
106
  }
121
107
  }
122
108
 
123
- @disableGC
109
+ @unsafe
124
110
  let getRecordFieldNames = record_ => {
125
111
  let moduleId = WasmI32.load(record_, 4n) >> 1n
126
112
  let typeId = WasmI32.load(record_, 8n) >> 1n
@@ -139,6 +125,7 @@ let getRecordFieldNames = record_ => {
139
125
  for (let mut i = 0n; i < arity; i += 1n) {
140
126
  let fieldLength = WasmI32.load(fields + fieldOffset, 4n)
141
127
  let fieldName = allocateString(fieldLength)
128
+ Memory.incRef(fieldName)
142
129
  Memory.copy(fieldName + 8n, fields + fieldOffset + 8n, fieldLength)
143
130
  WasmI32.store(fieldArray + i * 4n, fieldName, 8n)
144
131
 
@@ -149,29 +136,29 @@ let getRecordFieldNames = record_ => {
149
136
  }
150
137
  }
151
138
 
152
- @disableGC
139
+ @unsafe
153
140
  let rec totalBytes = (acc, list) => {
154
141
  match (list) {
155
- SLCons(hd, tl) =>
142
+ [hd, ...tl] =>
156
143
  totalBytes(acc + WasmI32.load(WasmI32.fromGrain(hd), 4n), tl),
157
- SLEmpty => acc,
144
+ [] => acc,
158
145
  }
159
146
  }
160
147
 
161
- @disableGC
148
+ @unsafe
162
149
  let rec writeStrings = (buf, list) => {
163
150
  match (list) {
164
- SLCons(hd, tl) => {
151
+ [hd, ...tl] => {
165
152
  let hd = WasmI32.fromGrain(hd)
166
153
  let hdSize = WasmI32.load(hd, 4n)
167
154
  Memory.copy(buf, hd + 8n, hdSize)
168
155
  writeStrings(buf + hdSize, tl)
169
156
  },
170
- SLEmpty => void,
157
+ [] => void,
171
158
  }
172
159
  }
173
160
 
174
- @disableGC
161
+ @unsafe
175
162
  let join = list => {
176
163
  let len = totalBytes(0n, list)
177
164
  let str = allocateString(len)
@@ -179,21 +166,20 @@ let join = list => {
179
166
  WasmI32.toGrain(str): String
180
167
  }
181
168
 
182
- @disableGC
169
+ @unsafe
183
170
  let reverse = list => {
184
- @disableGC
171
+ @unsafe
185
172
  let rec iter = (list, acc) => {
186
173
  match (list) {
187
- SLEmpty => acc,
188
- SLCons(first, rest) => iter(rest, slConsDisableGc(first, acc)),
174
+ [] => acc,
175
+ [first, ...rest] => iter(rest, [first, ...acc]),
189
176
  }
190
177
  }
191
- Memory.incRef(WasmI32.fromGrain(SLEmpty))
192
- iter(list, SLEmpty)
178
+ iter(list, [])
193
179
  }
194
180
 
195
- @disableGC
196
- export let rec concat = (s1: String, s2: String) => {
181
+ @unsafe
182
+ export let concat = (s1: String, s2: String) => {
197
183
  let ptr1 = WasmI32.fromGrain(s1)
198
184
  let ptr2 = WasmI32.fromGrain(s2)
199
185
 
@@ -205,14 +191,10 @@ export let rec concat = (s1: String, s2: String) => {
205
191
  Memory.copy(newString + 8n, ptr1 + 8n, size1)
206
192
  Memory.copy(newString + 8n + size1, ptr2 + 8n, size2)
207
193
 
208
- let ret = WasmI32.toGrain(newString): String
209
- Memory.decRef(ptr1)
210
- Memory.decRef(ptr2)
211
- Memory.decRef(WasmI32.fromGrain(concat))
212
- ret
194
+ WasmI32.toGrain(newString): String
213
195
  }
214
196
 
215
- @disableGC
197
+ @unsafe
216
198
  let escape = (ptr, isString) => {
217
199
  let _SEQ_B = 0x08n
218
200
  let _SEQ_F = 0x0Cn
@@ -226,24 +208,9 @@ let escape = (ptr, isString) => {
226
208
 
227
209
  let _SEQ_QUOTE = if (isString) _SEQ_DQUOTE else _SEQ_SQUOTE
228
210
 
229
- let size = if (isString) {
230
- WasmI32.load(ptr, 4n)
231
- } else {
232
- let byte = WasmI32.load8U(ptr, 4n)
233
-
234
- // Calculate number of bytes via utf-8 encoding
235
- if ((byte & 0x80n) == 0x00n) {
236
- 1n
237
- } else if ((byte & 0xF0n) == 0xF0n) {
238
- 4n
239
- } else if ((byte & 0xE0n) == 0xE0n) {
240
- 3n
241
- } else {
242
- 2n
243
- }
244
- }
211
+ let size = WasmI32.load(ptr, 4n)
245
212
 
246
- let startOffset = if (isString) 8n else 4n
213
+ let startOffset = 8n
247
214
  let stringOffset = 8n
248
215
 
249
216
  let mut newSize = 2n // extra space for quote characters
@@ -299,33 +266,65 @@ let escape = (ptr, isString) => {
299
266
  WasmI32.toGrain(escapedString): String
300
267
  }
301
268
 
302
- @disableGC
269
+ @unsafe
303
270
  let escapeString = (s: String) => {
304
271
  escape(WasmI32.fromGrain(s), true)
305
272
  }
306
273
 
307
- @disableGC
308
- let escapeChar = (c: Char) => {
309
- 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
+ }
310
311
  }
311
312
 
312
- @disableGC
313
+ @unsafe
313
314
  let isListVariant = (variant: String) => {
314
315
  // Only lists can start with `[`, so only need to check for that
315
316
  // Sort of a hack until we have a better solution
316
317
  WasmI32.load8U(WasmI32.fromGrain(variant), 8n) == 0x5Bn
317
318
  }
318
319
 
319
- @disableGC
320
+ @unsafe
320
321
  let rec heapValueToString = (ptr, extraIndents, toplevel) => {
321
322
  let tag = WasmI32.load(ptr, 0n)
322
323
  match (tag) {
323
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)
324
327
  if (toplevel) {
325
- // We can just return the pointer since its already a string and
326
- // doesn't need escaping in this case, but by convention its reference
327
- // count needs to be incremented.
328
- Memory.incRef(ptr)
329
328
  WasmI32.toGrain(ptr): String
330
329
  } else {
331
330
  escapeString(WasmI32.toGrain(ptr))
@@ -369,25 +368,6 @@ let rec heapValueToString = (ptr, extraIndents, toplevel) => {
369
368
 
370
369
  WasmI32.toGrain(str): String
371
370
  },
372
- t when t == Tags._GRAIN_CHAR_HEAP_TAG => {
373
- if (toplevel) {
374
- let byte = WasmI32.load8U(ptr, 4n)
375
- let numBytes = if ((byte & 0x80n) == 0x00n) {
376
- 1n
377
- } else if ((byte & 0xF0n) == 0xF0n) {
378
- 4n
379
- } else if ((byte & 0xE0n) == 0xE0n) {
380
- 3n
381
- } else {
382
- 2n
383
- }
384
- let str = allocateString(numBytes)
385
- Memory.copy(str + 8n, ptr + 4n, numBytes)
386
- WasmI32.toGrain(str): String
387
- } else {
388
- escapeChar(WasmI32.toGrain(ptr))
389
- }
390
- },
391
371
  t when t == Tags._GRAIN_ADT_HEAP_TAG => {
392
372
  // [ <value type tag>, <module_tag>, <type_tag>, <variant_tag>, <arity>, elts ... ]
393
373
  let variantName = getVariantName(ptr)
@@ -406,33 +386,22 @@ let rec heapValueToString = (ptr, extraIndents, toplevel) => {
406
386
  } else {
407
387
  let comspace = ", "
408
388
  let rparen = ")"
409
- Memory.incRef(WasmI32.fromGrain(SLEmpty))
410
- let mut strings = slConsDisableGc(rparen, SLEmpty)
389
+ let mut strings = [rparen]
411
390
  for (let mut i = variantArity * 4n - 4n; i >= 0n; i -= 4n) {
412
391
  let tmp = toStringHelp(
413
392
  WasmI32.load(ptr + i, 20n),
414
393
  extraIndents,
415
394
  false
416
395
  )
417
- strings = slConsDisableGc(tmp, strings)
418
- Memory.decRef(WasmI32.fromGrain(tmp))
396
+ strings = [tmp, ...strings]
419
397
  if (i > 0n) {
420
- strings = slConsDisableGc(comspace, strings)
398
+ strings = [comspace, ...strings]
421
399
  }
422
400
  }
423
- Memory.incRef(WasmI32.fromGrain(variantName))
424
401
  let lparen = "("
425
- strings = slConsDisableGc(
426
- variantName,
427
- slConsDisableGc(lparen, strings)
428
- )
429
- let string = join(strings)
430
- Memory.decRef(WasmI32.fromGrain(variantName))
431
- Memory.decRef(WasmI32.fromGrain(lparen))
432
- Memory.decRef(WasmI32.fromGrain(rparen))
433
- Memory.decRef(WasmI32.fromGrain(strings))
434
- Memory.decRef(WasmI32.fromGrain(comspace))
435
- string
402
+ strings = [variantName, lparen, ...strings]
403
+
404
+ join(strings)
436
405
  }
437
406
  }
438
407
  }
@@ -459,13 +428,9 @@ let rec heapValueToString = (ptr, extraIndents, toplevel) => {
459
428
  let spacePadding = allocateString(padAmount)
460
429
  Memory.fill(spacePadding + 8n, 0x20n, padAmount) // create indentation
461
430
  let spacePadding = WasmI32.toGrain(spacePadding): String
462
- Memory.incRef(WasmI32.fromGrain(SLEmpty))
463
431
  let newline = "\n"
464
432
  let rbrace = "}"
465
- let mut strings = slConsDisableGc(
466
- newline,
467
- slConsDisableGc(prevSpacePadding, slConsDisableGc(rbrace, SLEmpty))
468
- )
433
+ let mut strings = [newline, prevSpacePadding, rbrace]
469
434
  let colspace = ": "
470
435
  let comlf = ",\n"
471
436
  for (let mut i = recordArity * 4n - 4n; i >= 0n; i -= 4n) {
@@ -475,58 +440,33 @@ let rec heapValueToString = (ptr, extraIndents, toplevel) => {
475
440
  extraIndents + 1n,
476
441
  false
477
442
  )
478
- strings = slConsDisableGc(
479
- spacePadding,
480
- slConsDisableGc(
481
- fieldName,
482
- slConsDisableGc(colspace, slConsDisableGc(fieldValue, strings))
483
- )
484
- )
485
- Memory.decRef(WasmI32.fromGrain(fieldValue))
443
+ strings = [spacePadding, fieldName, colspace, fieldValue, ...strings]
486
444
  if (i > 0n) {
487
- strings = slConsDisableGc(comlf, strings)
445
+ strings = [comlf, ...strings]
488
446
  }
489
447
  }
490
448
  let lbrace = "{\n"
491
- strings = slConsDisableGc(lbrace, strings)
492
- let string = join(strings)
493
-
494
- Memory.decRef(WasmI32.fromGrain(strings))
495
- Memory.decRef(WasmI32.fromGrain(spacePadding))
496
- Memory.decRef(WasmI32.fromGrain(colspace))
497
- Memory.decRef(WasmI32.fromGrain(comlf))
498
- Memory.decRef(WasmI32.fromGrain(lbrace))
499
- Memory.decRef(WasmI32.fromGrain(rbrace))
500
- Memory.decRef(WasmI32.fromGrain(newline))
449
+ strings = [lbrace, ...strings]
501
450
 
502
- Memory.free(fields) // Avoid double-free of record field names
503
-
504
- string
451
+ join(strings)
505
452
  }
506
453
  },
507
454
  t when t == Tags._GRAIN_ARRAY_HEAP_TAG => {
508
455
  let arity = WasmI32.load(ptr, 4n)
509
- Memory.incRef(WasmI32.fromGrain(SLEmpty))
510
456
  let rbrack = "]"
511
- let mut strings = slConsDisableGc(rbrack, SLEmpty)
457
+ let mut strings = [rbrack]
512
458
  let comspace = ", "
513
459
  for (let mut i = arity * 4n - 4n; i >= 0n; i -= 4n) {
514
460
  let item = toStringHelp(WasmI32.load(ptr + i, 8n), extraIndents, false)
515
- strings = slConsDisableGc(item, strings)
516
- Memory.decRef(WasmI32.fromGrain(item))
461
+ strings = [item, ...strings]
517
462
  if (i > 0n) {
518
- strings = slConsDisableGc(comspace, strings)
463
+ strings = [comspace, ...strings]
519
464
  }
520
465
  }
521
466
  let lbrack = "[> "
522
- strings = slConsDisableGc(lbrack, strings)
523
- let string = join(strings)
524
- Memory.decRef(WasmI32.fromGrain(strings))
525
- Memory.decRef(WasmI32.fromGrain(comspace))
526
- Memory.decRef(WasmI32.fromGrain(lbrack))
527
- Memory.decRef(WasmI32.fromGrain(rbrack))
528
-
529
- string
467
+ strings = [lbrack, ...strings]
468
+
469
+ join(strings)
530
470
  },
531
471
  t when t == Tags._GRAIN_BOXED_NUM_HEAP_TAG => {
532
472
  let numberTag = WasmI32.load(ptr, 4n)
@@ -537,21 +477,15 @@ let rec heapValueToString = (ptr, extraIndents, toplevel) => {
537
477
  t when t == Tags._GRAIN_INT64_BOXED_NUM_TAG => {
538
478
  NumberUtils.itoa64(WasmI64.load(ptr, 8n), 10n)
539
479
  },
480
+ t when t == Tags._GRAIN_BIGINT_BOXED_NUM_TAG => {
481
+ BI.bigIntToString10(ptr)
482
+ },
540
483
  t when t == Tags._GRAIN_RATIONAL_BOXED_NUM_TAG => {
541
- let numerator = NumberUtils.itoa32(WasmI32.load(ptr, 8n), 10n)
542
- let denominator = NumberUtils.itoa32(WasmI32.load(ptr, 12n), 10n)
543
- Memory.incRef(WasmI32.fromGrain(SLEmpty))
484
+ let numerator = BI.bigIntToString10(WasmI32.load(ptr, 8n))
485
+ let denominator = BI.bigIntToString10(WasmI32.load(ptr, 12n))
544
486
  let slash = "/"
545
- let strings = slConsDisableGc(
546
- numerator,
547
- slConsDisableGc(slash, slConsDisableGc(denominator, SLEmpty))
548
- )
549
- let string = join(strings)
550
- Memory.decRef(WasmI32.fromGrain(strings))
551
- Memory.decRef(WasmI32.fromGrain(numerator))
552
- Memory.decRef(WasmI32.fromGrain(denominator))
553
- Memory.decRef(WasmI32.fromGrain(slash))
554
- string
487
+ let strings = [numerator, slash, denominator]
488
+ join(strings)
555
489
  },
556
490
  t when t == Tags._GRAIN_FLOAT32_BOXED_NUM_TAG => {
557
491
  NumberUtils.dtoa(WasmF64.promoteF32(WasmF32.load(ptr, 8n)))
@@ -571,14 +505,12 @@ let rec heapValueToString = (ptr, extraIndents, toplevel) => {
571
505
  } else {
572
506
  WasmI32.store(ptr, 0x80000000n | tupleLength, 4n)
573
507
  let comspace = ", "
574
- Memory.incRef(WasmI32.fromGrain(SLEmpty))
575
508
  let rparen = ")"
576
- let mut strings = slConsDisableGc(rparen, SLEmpty)
509
+ let mut strings = [rparen]
577
510
  if (tupleLength <= 1n) {
578
511
  // Special case: unary tuple
579
512
  let comma = ","
580
- strings = slConsDisableGc(comma, strings)
581
- Memory.decRef(WasmI32.fromGrain(comma))
513
+ strings = [comma, ...strings]
582
514
  void
583
515
  }
584
516
  for (let mut i = tupleLength * 4n - 4n; i >= 0n; i -= 4n) {
@@ -587,73 +519,66 @@ let rec heapValueToString = (ptr, extraIndents, toplevel) => {
587
519
  extraIndents,
588
520
  false
589
521
  )
590
- strings = slConsDisableGc(item, strings)
591
- Memory.decRef(WasmI32.fromGrain(item))
522
+ strings = [item, ...strings]
592
523
  if (i > 0n) {
593
- strings = slConsDisableGc(comspace, strings)
524
+ strings = [comspace, ...strings]
594
525
  }
595
526
  }
596
527
  WasmI32.store(ptr, tupleLength, 4n)
597
528
 
598
529
  Memory.incRef(WasmI32.fromGrain(strings))
599
530
  let lparen = "("
600
- strings = slConsDisableGc(lparen, strings)
531
+ strings = [lparen, ...strings]
601
532
 
602
- let string = join(strings)
603
- Memory.decRef(WasmI32.fromGrain(strings))
604
- Memory.decRef(WasmI32.fromGrain(comspace))
605
- Memory.decRef(WasmI32.fromGrain(rparen))
606
- Memory.decRef(WasmI32.fromGrain(lparen))
607
-
608
- string
533
+ join(strings)
609
534
  }
610
535
  },
611
536
  t when t == Tags._GRAIN_LAMBDA_HEAP_TAG => {
612
537
  "<lambda>"
613
538
  },
614
539
  _ => {
615
- Memory.incRef(WasmI32.fromGrain(SLEmpty))
616
- let strings = slConsDisableGc(
540
+ let strings = [
617
541
  "<unknown heap tag type: 0x",
618
- slConsDisableGc(
619
- NumberUtils.itoa32(tag, 16n),
620
- slConsDisableGc(
621
- " | value: 0x",
622
- slConsDisableGc(
623
- NumberUtils.itoa32(ptr, 16n),
624
- slConsDisableGc(">", SLEmpty)
625
- )
626
- )
627
- )
628
- )
629
- let string = join(strings)
630
- Memory.decRef(WasmI32.fromGrain(strings))
631
- string
542
+ NumberUtils.itoa32(tag, 16n),
543
+ " | value: 0x",
544
+ NumberUtils.itoa32(ptr, 16n),
545
+ ">",
546
+ ]
547
+ join(strings)
632
548
  },
633
549
  }
634
550
  }, toStringHelp = (grainValue, extraIndents, toplevel) => {
635
551
  if ((grainValue & 1n) != 0n) {
636
552
  // Simple (unboxed) numbers
637
553
  NumberUtils.itoa32(grainValue >> 1n, 10n)
638
- } else if ((grainValue & 7n) == Tags._GRAIN_GENERIC_HEAP_TAG_TYPE) {
639
- heapValueToString(grainValue, extraIndents, toplevel)
640
- } else if (grainValue == WasmI32.fromGrain(true)) {
641
- "true"
642
- } else if (grainValue == WasmI32.fromGrain(false)) {
643
- "false"
644
- } else if (grainValue == WasmI32.fromGrain(void)) {
645
- "void"
646
554
  } else {
647
- "<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
+ }
648
574
  }
649
575
  }, listToString = (ptr, extraIndents) => {
650
576
  let mut cur = ptr
651
577
  let mut isFirst = true
652
578
 
653
- Memory.incRef(WasmI32.fromGrain(SLEmpty))
654
579
  let lbrack = "["
655
580
  let commaspace = ", "
656
- let mut strings = slConsDisableGc(lbrack, SLEmpty)
581
+ let mut strings = [lbrack]
657
582
 
658
583
  while (true) {
659
584
  let variantId = WasmI32.load(cur, 12n) >> 1n // tagged number
@@ -661,46 +586,30 @@ let rec heapValueToString = (ptr, extraIndents, toplevel) => {
661
586
  break
662
587
  } else {
663
588
  if (!isFirst) {
664
- strings = slConsDisableGc(commaspace, strings)
589
+ strings = [commaspace, ...strings]
665
590
  }
666
591
  isFirst = false
667
592
  let item = toStringHelp(WasmI32.load(cur, 20n), extraIndents, false)
668
- strings = slConsDisableGc(item, strings)
669
- Memory.decRef(WasmI32.fromGrain(item))
593
+ strings = [item, ...strings]
670
594
  cur = WasmI32.load(cur, 24n)
671
595
  }
672
596
  }
673
597
  let rbrack = "]"
674
- strings = slConsDisableGc(rbrack, strings)
598
+ strings = [rbrack, ...strings]
675
599
  let reversed = reverse(strings)
676
- let string = join(reversed)
677
- Memory.decRef(WasmI32.fromGrain(strings))
678
- Memory.decRef(WasmI32.fromGrain(reversed))
679
- Memory.decRef(WasmI32.fromGrain(lbrack))
680
- Memory.decRef(WasmI32.fromGrain(rbrack))
681
- Memory.decRef(WasmI32.fromGrain(commaspace))
682
- string
600
+ join(reversed)
683
601
  }
684
602
 
685
- @disableGC
686
- export let rec toString = value => {
603
+ @unsafe
604
+ export let toString = value => {
687
605
  let value = WasmI32.fromGrain(value)
688
- let ret = toStringHelp(value, 0n, true)
689
- Memory.decRef(value)
690
- Memory.decRef(WasmI32.fromGrain(toString))
691
- ret
606
+ toStringHelp(value, 0n, true)
692
607
  }
693
608
 
694
- @disableGC
695
- export let rec print = value => {
696
- // First convert the value to string, if it isn't one already. Calling
697
- // toString will either return value instance itself if it's already a
698
- // string, or a new string with the content of value object, which will need
699
- // to be deallocated. In either case it's sufficient to force decrement its
700
- // reference count.
609
+ @unsafe
610
+ export let print = value => {
611
+ // First convert the value to string, if it isn't one already.
701
612
  let valuePtr = WasmI32.fromGrain(value)
702
- Memory.incRef(WasmI32.fromGrain(toString))
703
- Memory.incRef(valuePtr) // for function call
704
613
  let s = toString(value)
705
614
  let ptr = WasmI32.fromGrain(s)
706
615
  // iov: [<ptr to string> <nbytes of string> <ptr to newline> <nbytes of newline>] (32 bytes)
@@ -717,8 +626,5 @@ export let rec print = value => {
717
626
  WasmI32.store(iov, 1n, 12n)
718
627
  fd_write(1n, iov, 2n, written)
719
628
  Memory.free(buf)
720
- Memory.decRef(ptr)
721
- Memory.decRef(WasmI32.fromGrain(print))
722
- Memory.decRef(WasmI32.fromGrain(value))
723
629
  void
724
630
  }
@@ -0,0 +1,24 @@
1
+ ### String.**StringList**
2
+
3
+ ```grain
4
+ type StringList
5
+ ```
6
+
7
+ ### String.**concat**
8
+
9
+ ```grain
10
+ concat : (String, String) -> String
11
+ ```
12
+
13
+ ### String.**toString**
14
+
15
+ ```grain
16
+ toString : a -> String
17
+ ```
18
+
19
+ ### String.**print**
20
+
21
+ ```grain
22
+ print : a -> Void
23
+ ```
24
+