@grain/stdlib 0.5.2 → 0.5.4

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 (54) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/array.gr +61 -1
  3. package/array.md +113 -0
  4. package/bigint.md +30 -30
  5. package/buffer.gr +24 -22
  6. package/char.gr +2 -2
  7. package/float32.md +3 -3
  8. package/float64.md +3 -3
  9. package/immutablemap.gr +493 -0
  10. package/immutablemap.md +479 -0
  11. package/immutablepriorityqueue.gr +360 -0
  12. package/immutablepriorityqueue.md +291 -0
  13. package/immutableset.gr +498 -0
  14. package/immutableset.md +449 -0
  15. package/list.gr +75 -2
  16. package/list.md +110 -0
  17. package/map.gr +1 -2
  18. package/marshal.gr +1058 -0
  19. package/marshal.md +76 -0
  20. package/number.gr +689 -23
  21. package/number.md +362 -27
  22. package/package.json +1 -1
  23. package/pervasives.gr +16 -5
  24. package/pervasives.md +28 -0
  25. package/priorityqueue.gr +261 -0
  26. package/priorityqueue.md +309 -0
  27. package/queue.gr +14 -1
  28. package/queue.md +16 -1
  29. package/regex.gr +90 -67
  30. package/runtime/bigint.gr +4 -4
  31. package/runtime/compare.gr +179 -0
  32. package/runtime/compare.md +6 -0
  33. package/runtime/equal.gr +3 -3
  34. package/runtime/exception.gr +9 -5
  35. package/runtime/exception.md +8 -2
  36. package/runtime/gc.gr +2 -1
  37. package/runtime/malloc.gr +1 -3
  38. package/runtime/numberUtils.gr +11 -11
  39. package/runtime/numbers.gr +423 -100
  40. package/runtime/numbers.md +50 -0
  41. package/runtime/string.gr +4 -2
  42. package/set.gr +26 -27
  43. package/stack.gr +12 -0
  44. package/stack.md +15 -0
  45. package/string.gr +409 -53
  46. package/string.md +164 -1
  47. package/sys/file.gr +4 -4
  48. package/sys/file.md +3 -3
  49. package/sys/process.gr +3 -3
  50. package/sys/process.md +3 -3
  51. package/sys/random.gr +2 -2
  52. package/sys/random.md +2 -2
  53. package/sys/time.gr +2 -2
  54. package/sys/time.md +2 -2
package/regex.gr CHANGED
@@ -121,10 +121,10 @@ let withConfig = (buf: RegExBuf, config: RegExParserConfig) => {
121
121
 
122
122
  let parseErr = (buf: RegExBuf, msg: String, posShift) => {
123
123
  "Invalid Regular Expression: " ++
124
- msg ++
125
- " (position " ++
126
- toString(unbox(buf.cursor) + posShift) ++
127
- ")"
124
+ msg ++
125
+ " (position " ++
126
+ toString(unbox(buf.cursor) + posShift) ++
127
+ ")"
128
128
  }
129
129
 
130
130
  let next = (buf: RegExBuf) => {
@@ -241,7 +241,8 @@ let rec rangeAdd = (rng: CharRange, v: CharRangeElt) => {
241
241
  _ when rangeContains(rng, v) => rng,
242
242
  _ => rangeUnion(rng, [(v, v)]),
243
243
  }
244
- }, rangeUnion = (rng1, rng2) => {
244
+ },
245
+ rangeUnion = (rng1, rng2) => {
245
246
  match ((rng1, rng2)) {
246
247
  ([], _) => rng2,
247
248
  (_, []) => rng1,
@@ -487,7 +488,7 @@ enum MergeMode {
487
488
  }
488
489
 
489
490
  let mergeAdjacent = lst => {
490
- // see [TODO] below
491
+ // TODO: see below
491
492
  let readyForAccum = (l, mode) => {
492
493
  match (l) {
493
494
  [] => true,
@@ -512,7 +513,7 @@ let mergeAdjacent = lst => {
512
513
  // drop empty elements
513
514
  [REEmpty, ...tl] => loop(mode, accum, tl),
514
515
  [RELiteralString(""), ...tl] => loop(mode, accum, tl),
515
- // [TODO] Clean up with or-patterns (grain-lang/grain#696)
516
+ // TODO(#696): Clean up with or-patterns
516
517
  _ when readyForAccum(l, mode) => {
517
518
  match (accum) {
518
519
  [] => [],
@@ -592,7 +593,7 @@ REGEX PARSING DEFINITIONS
592
593
 
593
594
  // Range parsing ("[a-z]")
594
595
 
595
- // [TODO] (#769) When byte-based regexes are supported, we'll need another limit of 255 for those.
596
+ // TODO(#769): When byte-based regexes are supported, we'll need another limit of 255 for those.
596
597
  let rangeLimit = 0x10FFFF
597
598
 
598
599
  // These are snake-cased to avoid confusion with their capitalized counterparts
@@ -636,7 +637,8 @@ let rec parseRangeNot = (buf: RegExBuf) => {
636
637
  Ok(_) => parseRange(buf),
637
638
  }
638
639
  }
639
- }, parseRange = (buf: RegExBuf) => {
640
+ },
641
+ parseRange = (buf: RegExBuf) => {
640
642
  if (!more(buf)) {
641
643
  Err(parseErr(buf, "Missing closing `]`", 0))
642
644
  } else {
@@ -659,7 +661,8 @@ let rec parseRangeNot = (buf: RegExBuf) => {
659
661
  Ok(_) => parseRangeRest(buf, [], None, None),
660
662
  }
661
663
  }
662
- }, parseClass = (buf: RegExBuf) => {
664
+ },
665
+ parseClass = (buf: RegExBuf) => {
663
666
  if (!more(buf)) {
664
667
  Err(
665
668
  "no chars"
@@ -694,7 +697,8 @@ let rec parseRangeNot = (buf: RegExBuf) => {
694
697
  Ok(c) => Err("unknown class: " ++ toString(c)),
695
698
  }
696
699
  }
697
- }, parsePosixCharClass = (buf: RegExBuf) => {
700
+ },
701
+ parsePosixCharClass = (buf: RegExBuf) => {
698
702
  if (!more(buf)) {
699
703
  Err(parseErr(buf, "Missing POSIX character class after `[`", 0))
700
704
  } else {
@@ -810,7 +814,8 @@ let rec parseRangeNot = (buf: RegExBuf) => {
810
814
  ),
811
815
  }
812
816
  }
813
- }, parseRangeRest =
817
+ },
818
+ parseRangeRest =
814
819
  (
815
820
  buf: RegExBuf,
816
821
  rng: CharRange,
@@ -962,7 +967,8 @@ let rec parseRangeNot = (buf: RegExBuf) => {
962
967
  },
963
968
  }
964
969
  }
965
- }, parseRangeRestSpan =
970
+ },
971
+ parseRangeRestSpan =
966
972
  (
967
973
  buf: RegExBuf,
968
974
  c,
@@ -1213,7 +1219,8 @@ let rec parseAtom = (buf: RegExBuf) => {
1213
1219
  _ => parseLiteral(buf),
1214
1220
  },
1215
1221
  }
1216
- }, parseLook = (buf: RegExBuf) => {
1222
+ },
1223
+ parseLook = (buf: RegExBuf) => {
1217
1224
  let preNumGroups = unbox(buf.config.groupNumber)
1218
1225
  let spanNumGroups = () => unbox(buf.config.groupNumber) - preNumGroups
1219
1226
  // (isMatch, isAhead)
@@ -1279,7 +1286,8 @@ let rec parseAtom = (buf: RegExBuf) => {
1279
1286
  }
1280
1287
  },
1281
1288
  }
1282
- }, parseTest = (buf: RegExBuf) => {
1289
+ },
1290
+ parseTest = (buf: RegExBuf) => {
1283
1291
  if (!more(buf)) {
1284
1292
  Err(parseErr(buf, "Expected test", 0))
1285
1293
  } else {
@@ -1316,7 +1324,8 @@ let rec parseAtom = (buf: RegExBuf) => {
1316
1324
  ),
1317
1325
  }
1318
1326
  }
1319
- }, parseInteger = (buf: RegExBuf, n) => {
1327
+ },
1328
+ parseInteger = (buf: RegExBuf, n) => {
1320
1329
  if (!more(buf)) {
1321
1330
  Ok(n)
1322
1331
  } else {
@@ -1331,7 +1340,8 @@ let rec parseAtom = (buf: RegExBuf) => {
1331
1340
  Ok(_) => Ok(n),
1332
1341
  }
1333
1342
  }
1334
- }, parseMode = (buf: RegExBuf) => {
1343
+ },
1344
+ parseMode = (buf: RegExBuf) => {
1335
1345
  let processState = ((cs, ml)) => {
1336
1346
  let withCs = match (cs) {
1337
1347
  None => buf.config,
@@ -1390,7 +1400,8 @@ let rec parseAtom = (buf: RegExBuf) => {
1390
1400
  }
1391
1401
  }
1392
1402
  help((None, None))
1393
- }, parseUnicodeCategories = (buf: RegExBuf, pC: String) => {
1403
+ },
1404
+ parseUnicodeCategories = (buf: RegExBuf, pC: String) => {
1394
1405
  if (!more(buf)) {
1395
1406
  Err(parseErr(buf, "Expected unicode category", 0))
1396
1407
  } else {
@@ -1558,7 +1569,8 @@ let rec parseAtom = (buf: RegExBuf) => {
1558
1569
  Ok(_) => Err(parseErr(buf, "Expected `{` after `\\" ++ pC ++ "`", 0)),
1559
1570
  }
1560
1571
  }
1561
- }, parseLiteral = (buf: RegExBuf) => {
1572
+ },
1573
+ parseLiteral = (buf: RegExBuf) => {
1562
1574
  if (!more(buf)) {
1563
1575
  Err(parseErr(buf, "Expected literal", 0))
1564
1576
  } else {
@@ -1578,7 +1590,7 @@ let rec parseAtom = (buf: RegExBuf) => {
1578
1590
  Err(
1579
1591
  parseErr(buf, "unmatched `" ++ Char.toString(c) ++ "` in pattern", 0)
1580
1592
  ),
1581
- // [TODO] case-insensitive (#691)
1593
+ // TODO(#691): Enable case-insensitive regular expression matching
1582
1594
  Ok(c) when buf.config.caseSensitive => {
1583
1595
  ignore(next(buf))
1584
1596
  Ok(RELiteral(c))
@@ -1592,7 +1604,8 @@ let rec parseAtom = (buf: RegExBuf) => {
1592
1604
  },
1593
1605
  }
1594
1606
  }
1595
- }, parseBackslashLiteral = (buf: RegExBuf) => {
1607
+ },
1608
+ parseBackslashLiteral = (buf: RegExBuf) => {
1596
1609
  if (!more(buf)) {
1597
1610
  // Special case: EOS after backslash matches null
1598
1611
  Err(parseErr(buf, "Expected to find escaped value after backslash", 0))
@@ -1655,7 +1668,8 @@ let rec parseAtom = (buf: RegExBuf) => {
1655
1668
  },
1656
1669
  }
1657
1670
  }
1658
- }, parseNonGreedy = (buf: RegExBuf) => {
1671
+ },
1672
+ parseNonGreedy = (buf: RegExBuf) => {
1659
1673
  let checkNotNested = res => {
1660
1674
  if (!more(buf)) {
1661
1675
  res
@@ -1681,7 +1695,8 @@ let rec parseAtom = (buf: RegExBuf) => {
1681
1695
  Ok(_) => checkNotNested(Ok(false)),
1682
1696
  }
1683
1697
  }
1684
- }, parsePCE = (buf: RegExBuf) => {
1698
+ },
1699
+ parsePCE = (buf: RegExBuf) => {
1685
1700
  match (parseAtom(buf)) {
1686
1701
  Err(e) => Err(e),
1687
1702
  Ok(atom) => {
@@ -1775,7 +1790,8 @@ let rec parseAtom = (buf: RegExBuf) => {
1775
1790
  }
1776
1791
  },
1777
1792
  }
1778
- }, parsePCEs = (buf: RegExBuf, toplevel: Bool) => {
1793
+ },
1794
+ parsePCEs = (buf: RegExBuf, toplevel: Bool) => {
1779
1795
  if (!more(buf)) {
1780
1796
  Ok([])
1781
1797
  } else {
@@ -1801,7 +1817,8 @@ let rec parseAtom = (buf: RegExBuf) => {
1801
1817
  },
1802
1818
  }
1803
1819
  }
1804
- }, parseRegex = (buf: RegExBuf) => {
1820
+ },
1821
+ parseRegex = (buf: RegExBuf) => {
1805
1822
  if (!more(buf)) {
1806
1823
  Ok(REEmpty)
1807
1824
  } else {
@@ -1836,7 +1853,8 @@ let rec parseAtom = (buf: RegExBuf) => {
1836
1853
  },
1837
1854
  }
1838
1855
  }
1839
- }, parseRegexNonEmpty = (buf: RegExBuf) => {
1856
+ },
1857
+ parseRegexNonEmpty = (buf: RegExBuf) => {
1840
1858
  match (parsePCEs(buf, false)) {
1841
1859
  Err(e) => Err(e),
1842
1860
  Ok(pces) => {
@@ -1922,7 +1940,7 @@ let rec isAnchored = (re: ParsedRegularExpression) => {
1922
1940
  REAlts(a, b) => isAnchored(a) && isAnchored(b),
1923
1941
  REConditional(_, rx1, rx2, _, _, _) =>
1924
1942
  isAnchored(rx1) &&
1925
- Option.mapWithDefault(isAnchored, false, rx2),
1943
+ Option.mapWithDefault(isAnchored, false, rx2),
1926
1944
  REGroup(rx, _) => isAnchored(rx),
1927
1945
  RECut(rx, _, _, _) => isAnchored(rx),
1928
1946
  _ => false,
@@ -2305,10 +2323,10 @@ let charMatcher = (toMatch, next_m) =>
2305
2323
  if (
2306
2324
  {
2307
2325
  pos < limit &&
2308
- match (matchBufChar(buf, pos)) {
2309
- Err(_) => false,
2310
- Ok(c) => toMatch == c,
2311
- }
2326
+ match (matchBufChar(buf, pos)) {
2327
+ Err(_) => false,
2328
+ Ok(c) => toMatch == c,
2329
+ }
2312
2330
  }
2313
2331
  ) next_m(buf, pos + 1, start, limit, end, state, stack) else None
2314
2332
  }
@@ -2326,10 +2344,10 @@ let charTailMatcher = toMatch =>
2326
2344
  if (
2327
2345
  {
2328
2346
  pos < limit &&
2329
- match (matchBufChar(buf, pos)) {
2330
- Err(_) => false,
2331
- Ok(c) => toMatch == c,
2332
- }
2347
+ match (matchBufChar(buf, pos)) {
2348
+ Err(_) => false,
2349
+ Ok(c) => toMatch == c,
2350
+ }
2333
2351
  }
2334
2352
  ) Some(pos + 1) else None
2335
2353
  }
@@ -2382,13 +2400,13 @@ let stringMatcher = (toMatch, len, next_m) =>
2382
2400
  if (
2383
2401
  {
2384
2402
  pos + len <= limit &&
2385
- subArraysEqual(
2386
- buf.matchInputExploded,
2387
- pos,
2388
- String.explode(toMatch),
2389
- 0,
2390
- len
2391
- )
2403
+ subArraysEqual(
2404
+ buf.matchInputExploded,
2405
+ pos,
2406
+ String.explode(toMatch),
2407
+ 0,
2408
+ len
2409
+ )
2392
2410
  }
2393
2411
  ) next_m(buf, pos + len, start, limit, end, state, stack) else None
2394
2412
  }
@@ -2406,13 +2424,13 @@ let stringTailMatcher = (toMatch, len) =>
2406
2424
  if (
2407
2425
  {
2408
2426
  pos + len <= limit &&
2409
- subArraysEqual(
2410
- buf.matchInputExploded,
2411
- pos,
2412
- String.explode(toMatch),
2413
- 0,
2414
- len
2415
- )
2427
+ subArraysEqual(
2428
+ buf.matchInputExploded,
2429
+ pos,
2430
+ String.explode(toMatch),
2431
+ 0,
2432
+ len
2433
+ )
2416
2434
  }
2417
2435
  ) Some(pos + len) else None
2418
2436
  }
@@ -2513,10 +2531,10 @@ let rangeMatcher = (rng: CharRange, next_m) =>
2513
2531
  if (
2514
2532
  {
2515
2533
  pos < limit &&
2516
- match (matchBufChar(buf, pos)) {
2517
- Err(_) => false,
2518
- Ok(c) => rangeContains(rng, Char.code(c)),
2519
- }
2534
+ match (matchBufChar(buf, pos)) {
2535
+ Err(_) => false,
2536
+ Ok(c) => rangeContains(rng, Char.code(c)),
2537
+ }
2520
2538
  }
2521
2539
  ) next_m(buf, pos + 1, start, limit, end, state, stack) else None
2522
2540
  }
@@ -2534,10 +2552,10 @@ let rangeTailMatcher = (rng: CharRange) =>
2534
2552
  if (
2535
2553
  {
2536
2554
  pos < limit &&
2537
- match (matchBufChar(buf, pos)) {
2538
- Err(_) => false,
2539
- Ok(c) => rangeContains(rng, Char.code(c)),
2540
- }
2555
+ match (matchBufChar(buf, pos)) {
2556
+ Err(_) => false,
2557
+ Ok(c) => rangeContains(rng, Char.code(c)),
2558
+ }
2541
2559
  }
2542
2560
  ) Some(pos + 1) else None
2543
2561
  }
@@ -2636,7 +2654,7 @@ let isWordChar = c => {
2636
2654
 
2637
2655
  let isWordBoundary = (buf, pos, start, limit, end) => {
2638
2656
  !((pos == start || !isWordChar(matchBufChar(buf, pos - 1))) ==
2639
- (pos == end || !isWordChar(matchBufChar(buf, pos))))
2657
+ (pos == end || !isWordChar(matchBufChar(buf, pos))))
2640
2658
  }
2641
2659
 
2642
2660
  let wordBoundaryMatcher = next_m =>
@@ -3320,7 +3338,7 @@ record RegularExpression {
3320
3338
  * @section Values: Functions for working with regular expressions.
3321
3339
  */
3322
3340
 
3323
- // [TODO] When #661 is resolved, re-add the following pieces of documentation:
3341
+ // TODO(#661): re-add the following pieces of documentation:
3324
3342
  /*
3325
3343
  [Under POSIX character classes]
3326
3344
 
@@ -3672,10 +3690,12 @@ let makeMatchResult = (origString, start, end, state) => {
3672
3690
  // Helpers for user-facing match functionality
3673
3691
 
3674
3692
  let fastDriveRegexIsMatch = (rx, string, startOffset, endOffset) => {
3675
- let state = if (rx.reReferences) Array.make(rx.reNumGroups, None)
3693
+ let state =
3694
+ if (rx.reReferences) Array.make(rx.reNumGroups, None)
3676
3695
  else Array.make(0, None)
3677
- let toWrap = if (startOffset == 0 && endOffset == String.length(string))
3678
- string else String.slice(startOffset, endOffset, string)
3696
+ let toWrap =
3697
+ if (startOffset == 0 && endOffset == String.length(string)) string
3698
+ else String.slice(startOffset, endOffset, string)
3679
3699
  let buf = makeMatchBuffer(toWrap)
3680
3700
  Option.isSome(
3681
3701
  searchMatch(rx, buf, 0, 0, Array.length(buf.matchInputExploded), state)
@@ -3687,8 +3707,9 @@ let rec fastDriveRegexMatchAll = (rx, string, startOffset, endOffset) => {
3687
3707
  []
3688
3708
  } else {
3689
3709
  let state = Array.make(rx.reNumGroups, None)
3690
- let toWrap = if (startOffset == 0 && endOffset == String.length(string))
3691
- string else String.slice(startOffset, endOffset, string)
3710
+ let toWrap =
3711
+ if (startOffset == 0 && endOffset == String.length(string)) string
3712
+ else String.slice(startOffset, endOffset, string)
3692
3713
  let buf = makeMatchBuffer(toWrap)
3693
3714
  match (searchMatch(
3694
3715
  rx,
@@ -3726,8 +3747,9 @@ let rec fastDriveRegexMatchAll = (rx, string, startOffset, endOffset) => {
3726
3747
 
3727
3748
  let fastDriveRegexMatch = (rx, string, startOffset, endOffset) => {
3728
3749
  let state = Array.make(rx.reNumGroups, None)
3729
- let toWrap = if (startOffset == 0 && endOffset == String.length(string))
3730
- string else String.slice(startOffset, endOffset, string)
3750
+ let toWrap =
3751
+ if (startOffset == 0 && endOffset == String.length(string)) string
3752
+ else String.slice(startOffset, endOffset, string)
3731
3753
  let buf = makeMatchBuffer(toWrap)
3732
3754
  match (searchMatch(
3733
3755
  rx,
@@ -3954,11 +3976,12 @@ let regexReplaceHelp =
3954
3976
  let mut out = []
3955
3977
  let rec loop = searchPos => {
3956
3978
  let state = Array.make(rx.reNumGroups, None)
3979
+ let inStart = max(0, searchPos - rx.reMaxLookbehind)
3957
3980
  let poss = searchMatch(
3958
3981
  rx,
3959
3982
  buf,
3960
3983
  searchPos,
3961
- searchPos,
3984
+ inStart,
3962
3985
  Array.length(buf.matchInputExploded),
3963
3986
  state
3964
3987
  )
package/runtime/bigint.gr CHANGED
@@ -1827,10 +1827,10 @@ let baseCaseDivRem = (a: WasmI32, b: WasmI32, result: WasmI32) => {
1827
1827
  let n = getHalfSize(b)
1828
1828
  let m = getHalfSize(a) - n
1829
1829
  let qsize = (if (WasmI32.eqz(WasmI32.and(m + 1n, 1n))) {
1830
- m + 1n
1831
- } else {
1832
- m + 2n
1833
- }) >>
1830
+ m + 1n
1831
+ } else {
1832
+ m + 2n
1833
+ }) >>
1834
1834
  1n
1835
1835
  let mut q = init(qsize)
1836
1836
  let mut a = 0n
@@ -0,0 +1,179 @@
1
+ /* grainc-flags --no-pervasives */
2
+
3
+ import WasmI32, {
4
+ eq as (==),
5
+ ne as (!=),
6
+ and as (&),
7
+ xor as (^),
8
+ or as (|),
9
+ add as (+),
10
+ sub as (-),
11
+ mul as (*),
12
+ ltS as (<),
13
+ gtS as (>),
14
+ remS as (%),
15
+ shl as (<<),
16
+ shrU as (>>>),
17
+ } from "runtime/unsafe/wasmi32"
18
+ import WasmI64 from "runtime/unsafe/wasmi64"
19
+ import Memory from "runtime/unsafe/memory"
20
+ import Tags from "runtime/unsafe/tags"
21
+ import { tagSimpleNumber } from "runtime/dataStructures"
22
+ import { isNumber, cmp as numberCompare } from "runtime/numbers"
23
+
24
+ primitive (!): Bool -> Bool = "@not"
25
+ primitive (||): (Bool, Bool) -> Bool = "@or"
26
+ primitive (&&): (Bool, Bool) -> Bool = "@and"
27
+
28
+ @unsafe
29
+ let zero = WasmI32.fromGrain(0)
30
+
31
+ @unsafe
32
+ let rec heapCompareHelp = (heapTag, xptr, yptr) => {
33
+ match (heapTag) {
34
+ t when t == Tags._GRAIN_ADT_HEAP_TAG => {
35
+ // Check if the same constructor variant
36
+ let xvariant = WasmI32.load(xptr, 12n)
37
+ let yvariant = WasmI32.load(yptr, 12n)
38
+ if (xvariant != yvariant) {
39
+ tagSimpleNumber(xvariant - yvariant)
40
+ } else {
41
+ let xarity = WasmI32.load(xptr, 16n)
42
+ let yarity = WasmI32.load(yptr, 16n)
43
+
44
+ let mut result = 0
45
+
46
+ let bytes = xarity * 4n
47
+ for (let mut i = 0n; i < bytes; i += 4n) {
48
+ let sub = compareHelp(
49
+ WasmI32.load(xptr + i, 20n),
50
+ WasmI32.load(yptr + i, 20n)
51
+ )
52
+ if (WasmI32.fromGrain(sub) != zero) {
53
+ result = sub
54
+ break
55
+ }
56
+ }
57
+
58
+ result
59
+ }
60
+ },
61
+ t when t == Tags._GRAIN_RECORD_HEAP_TAG => {
62
+ let xlength = WasmI32.load(xptr, 12n)
63
+ let ylength = WasmI32.load(yptr, 12n)
64
+
65
+ let mut result = 0
66
+
67
+ let bytes = xlength * 4n
68
+ for (let mut i = 0n; i < bytes; i += 4n) {
69
+ let sub = compareHelp(
70
+ WasmI32.load(xptr + i, 16n),
71
+ WasmI32.load(yptr + i, 16n)
72
+ )
73
+ if (WasmI32.fromGrain(sub) != zero) {
74
+ result = sub
75
+ break
76
+ }
77
+ }
78
+
79
+ result
80
+ },
81
+ t when t == Tags._GRAIN_ARRAY_HEAP_TAG => {
82
+ let xlength = WasmI32.load(xptr, 4n)
83
+ let ylength = WasmI32.load(yptr, 4n)
84
+
85
+ // Check if the same length
86
+ if (xlength != ylength) {
87
+ tagSimpleNumber(xlength - ylength)
88
+ } else {
89
+ let mut result = 0
90
+ let bytes = xlength * 4n
91
+ for (let mut i = 0n; i < bytes; i += 4n) {
92
+ let sub = compareHelp(
93
+ WasmI32.load(xptr + i, 8n),
94
+ WasmI32.load(yptr + i, 8n)
95
+ )
96
+ if (WasmI32.fromGrain(sub) != zero) {
97
+ result = sub
98
+ break
99
+ }
100
+ }
101
+
102
+ result
103
+ }
104
+ },
105
+ t when (
106
+ t == Tags._GRAIN_STRING_HEAP_TAG || t == Tags._GRAIN_BYTES_HEAP_TAG
107
+ ) => {
108
+ let xlength = WasmI32.load(xptr, 4n)
109
+ let ylength = WasmI32.load(yptr, 4n)
110
+
111
+ if (xlength == ylength) {
112
+ tagSimpleNumber(Memory.compare(xptr + 8n, yptr + 8n, xlength))
113
+ } else {
114
+ if (xlength < ylength) {
115
+ let sub = Memory.compare(xptr + 8n, yptr + 8n, xlength)
116
+ // The shorter one comes first
117
+ if (sub == 0n) -1 else tagSimpleNumber(sub)
118
+ } else {
119
+ let sub = Memory.compare(xptr + 8n, yptr + 8n, ylength)
120
+ // The shorter one comes first
121
+ if (sub == 0n) 1 else tagSimpleNumber(sub)
122
+ }
123
+ }
124
+ },
125
+ t when t == Tags._GRAIN_TUPLE_HEAP_TAG => {
126
+ let xsize = WasmI32.load(xptr, 4n)
127
+ let ysize = WasmI32.load(yptr, 4n)
128
+
129
+ let mut result = 0
130
+ let bytes = xsize * 4n
131
+ for (let mut i = 0n; i < bytes; i += 4n) {
132
+ let sub = compareHelp(
133
+ WasmI32.load(xptr + i, 8n),
134
+ WasmI32.load(yptr + i, 8n)
135
+ )
136
+ if (WasmI32.fromGrain(sub) != zero) {
137
+ result = sub
138
+ break
139
+ }
140
+ }
141
+
142
+ result
143
+ },
144
+ _ => {
145
+ // No other implementation
146
+ tagSimpleNumber(xptr - yptr)
147
+ },
148
+ }
149
+ },
150
+ compareHelp = (x, y) => {
151
+ let xtag = x & Tags._GRAIN_GENERIC_TAG_MASK
152
+ let ytag = y & Tags._GRAIN_GENERIC_TAG_MASK
153
+ if ((xtag & ytag) != Tags._GRAIN_GENERIC_HEAP_TAG_TYPE) {
154
+ // Short circuit for non-pointer values
155
+ if ((xtag & Tags._GRAIN_NUMBER_TAG_MASK) == Tags._GRAIN_NUMBER_TAG_TYPE) {
156
+ // Signed comparisons are necessary for numbers
157
+ if (x < y) -1 else if (x > y) 1 else 0
158
+ } else {
159
+ // Unsigned comparisons are necessary for other stack-allocated values
160
+ if (WasmI32.ltU(x, y)) -1 else if (WasmI32.gtU(x, y)) 1 else 0
161
+ }
162
+ } else if (isNumber(x)) {
163
+ // Numbers have special comparison rules, e.g. NaN == NaN
164
+ tagSimpleNumber(numberCompare(x, y, true))
165
+ } else {
166
+ // Handle all other heap allocated things
167
+ // Can short circuit if pointers are the same
168
+ if (x == y) {
169
+ 0
170
+ } else {
171
+ heapCompareHelp(WasmI32.load(x, 0n), x, y)
172
+ }
173
+ }
174
+ }
175
+
176
+ @unsafe
177
+ export let compare = (x: a, y: a) => {
178
+ compareHelp(WasmI32.fromGrain(x), WasmI32.fromGrain(y))
179
+ }
@@ -0,0 +1,6 @@
1
+ ### Compare.**compare**
2
+
3
+ ```grain
4
+ compare : (a, a) -> Number
5
+ ```
6
+
package/runtime/equal.gr CHANGED
@@ -14,13 +14,12 @@ import WasmI32, {
14
14
  } from "runtime/unsafe/wasmi32"
15
15
  import WasmI64 from "runtime/unsafe/wasmi64"
16
16
  import Tags from "runtime/unsafe/tags"
17
+ import { isNumber, numberEqual } from "runtime/numbers"
17
18
 
18
19
  primitive (!): Bool -> Bool = "@not"
19
20
  primitive (||): (Bool, Bool) -> Bool = "@or"
20
21
  primitive (&&): (Bool, Bool) -> Bool = "@and"
21
22
 
22
- import { isNumber, numberEqual } from "runtime/numbers"
23
-
24
23
  @unsafe
25
24
  let cycleMarker = 0x80000000n
26
25
 
@@ -190,7 +189,8 @@ let rec heapEqualHelp = (heapTag, xptr, yptr) => {
190
189
  xptr == yptr
191
190
  },
192
191
  }
193
- }, equalHelp = (x, y) => {
192
+ },
193
+ equalHelp = (x, y) => {
194
194
  if (
195
195
  (x & Tags._GRAIN_GENERIC_TAG_MASK) != 0n &&
196
196
  (y & Tags._GRAIN_GENERIC_TAG_MASK) != 0n
@@ -9,6 +9,8 @@ import foreign wasm fd_write: (
9
9
  WasmI32,
10
10
  ) -> WasmI32 from "wasi_snapshot_preview1"
11
11
 
12
+ primitive unreachable: () -> a = "@unreachable"
13
+
12
14
  enum Option<a> {
13
15
  Some(a),
14
16
  None,
@@ -79,8 +81,8 @@ let exceptionToString = (e: Exception) => {
79
81
  // the runtime heap, but this is the only module that needs to do it
80
82
  let iov = WasmI32.fromGrain([> 0n, 0n, 0n, 0n, 0n, 0n, 0n, 0n, 0n, 0n])
81
83
 
82
- export let printException = (e: Exception) => {
83
- let ptr = WasmI32.fromGrain(exceptionToString(e))
84
+ export let panic = (msg: String) => {
85
+ let ptr = WasmI32.fromGrain(msg)
84
86
  let written = iov + 32n
85
87
  let lf = iov + 36n
86
88
  WasmI32.store(iov, ptr + 8n, 0n)
@@ -89,7 +91,11 @@ export let printException = (e: Exception) => {
89
91
  WasmI32.store(iov, lf, 8n)
90
92
  WasmI32.store(iov, 1n, 12n)
91
93
  fd_write(2n, iov, 2n, written)
92
- void
94
+ unreachable()
95
+ }
96
+
97
+ export let panicWithException = (e: Exception) => {
98
+ panic(exceptionToString(e))
93
99
  }
94
100
 
95
101
  export exception IndexOutOfBounds
@@ -109,7 +115,6 @@ export exception MatchFailure
109
115
  */
110
116
  export exception AssertionError(String)
111
117
  export exception InvalidArgument(String)
112
- export exception OutOfMemory
113
118
 
114
119
  let runtimeErrorPrinter = e => {
115
120
  match (e) {
@@ -123,7 +128,6 @@ let runtimeErrorPrinter = e => {
123
128
  Some("NumberNotRational: Can't coerce number to rational"),
124
129
  MatchFailure => Some("MatchFailure: No matching pattern"),
125
130
  AssertionError(s) => Some(s),
126
- OutOfMemory => Some("OutOfMemory: Maximum memory size exceeded"),
127
131
  InvalidArgument(msg) => Some(msg),
128
132
  _ => None,
129
133
  }
@@ -22,9 +22,15 @@ dangerouslyRegisterBasePrinter : a -> Void
22
22
  dangerouslyRegisterPrinter : a -> Void
23
23
  ```
24
24
 
25
- ### Exception.**printException**
25
+ ### Exception.**panic**
26
26
 
27
27
  ```grain
28
- printException : Exception -> Void
28
+ panic : String -> a
29
+ ```
30
+
31
+ ### Exception.**panicWithException**
32
+
33
+ ```grain
34
+ panicWithException : Exception -> a
29
35
  ```
30
36