@grain/stdlib 0.6.0 → 0.6.1

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.6.1](https://github.com/grain-lang/grain/compare/stdlib-v0.6.0...stdlib-v0.6.1) (2024-03-29)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * **stdlib:** Avoid WASI random_get in Hash stdlib during module startup ([#2078](https://github.com/grain-lang/grain/issues/2078)) ([7eadfb0](https://github.com/grain-lang/grain/commit/7eadfb097e64b043c860d27d21d36d4bfea1ea96))
9
+ * **stdlib:** Implement `print` using a single element io vec ([#2066](https://github.com/grain-lang/grain/issues/2066)) ([9eeb0f2](https://github.com/grain-lang/grain/commit/9eeb0f2edb14facc619c1ede27a5700a27e64e3f))
10
+
3
11
  ## [0.6.0](https://github.com/grain-lang/grain/compare/stdlib-v0.5.13...stdlib-v0.6.0) (2024-03-04)
4
12
 
5
13
 
package/hash.gr CHANGED
@@ -43,9 +43,15 @@ from "wasi/random" include Random
43
43
  from "result" include Result
44
44
 
45
45
  @unsafe
46
- let seed = {
46
+ let mut seed = 0n
47
+
48
+ @unsafe
49
+ let initalize = () => {
50
+ // Delay initialization to the first call to `hash` to prevent WASI calls
51
+ // during startup
47
52
  let random = Random.random()
48
- coerceNumberToWasmI32(Result.unwrap(random))
53
+ seed = coerceNumberToWasmI32(Result.unwrap(random))
54
+ seed
49
55
  }
50
56
 
51
57
  @unsafe
@@ -239,6 +245,8 @@ let rec hashOne = (val, depth) => {
239
245
  *
240
246
  * @param anything: The value to hash
241
247
  * @returns A hash for the given value
248
+ *
249
+ * @throws Failure(String): If WASI random_get fails
242
250
  *
243
251
  * @example assert Hash.hash(1) == Hash.hash(1)
244
252
  * @example assert Hash.hash("Hello World") == Hash.hash("Hello World")
@@ -247,7 +255,11 @@ let rec hashOne = (val, depth) => {
247
255
  */
248
256
  @unsafe
249
257
  provide let hash = anything => {
250
- h = seed
258
+ h = if (WasmI32.eqz(seed)) {
259
+ initalize()
260
+ } else {
261
+ seed
262
+ }
251
263
 
252
264
  hashOne(WasmI32.fromGrain(anything), 0n)
253
265
  finalize(0n)
package/hash.md CHANGED
@@ -50,6 +50,12 @@ Returns:
50
50
  |----|-----------|
51
51
  |`Number`|A hash for the given value|
52
52
 
53
+ Throws:
54
+
55
+ `Failure(String)`
56
+
57
+ * If WASI random_get fails
58
+
53
59
  Examples:
54
60
 
55
61
  ```grain
package/json.gr CHANGED
@@ -55,10 +55,25 @@ let _Float64_BOXED_VALUE_OFFSET = 8n
55
55
  * ])
56
56
  */
57
57
  provide enum rec Json {
58
+ /**
59
+ * Represents the JSON `null` value.
60
+ */
58
61
  JsonNull,
62
+ /**
63
+ * Represents a JSON boolean value.
64
+ */
59
65
  JsonBoolean(Bool),
66
+ /**
67
+ * Represents a JSON number value.
68
+ */
60
69
  JsonNumber(Number),
70
+ /**
71
+ * Represents a JSON string value.
72
+ */
61
73
  JsonString(String),
74
+ /**
75
+ * Represents a JSON array value.
76
+ */
62
77
  JsonArray(List<Json>),
63
78
  // Note that JsonObject here is deliberately defined as a simple list of key value pair tuples as opposed
64
79
  // to for example a Map in order to accommodate the fact that the ECMA-404 standard doesn't prohibit
@@ -67,6 +82,9 @@ provide enum rec Json {
67
82
  // has the benefit of List's immutability. It's a conscious decision that sacrifices ease of use of the
68
83
  // API for lossless handing of these edge cases with intention of later building more ergonomic APIs on a
69
84
  // higher level of abstraction.
85
+ /**
86
+ * Represents a JSON object value, as a list of (key, value).
87
+ */
70
88
  JsonObject(List<(String, Json)>),
71
89
  }
72
90
 
@@ -494,7 +512,6 @@ let emitEscapedUnicodeSequence = (codePoint: Number, buffer: Buffer.Buffer) => {
494
512
  // High surrogate
495
513
  let lowSurrogate = (uPrime & 0b00000000001111111111) + 0xDC00
496
514
  // Low surrogate
497
-
498
515
  emitUTF16EscapeSequence(highSurrogate, buffer)
499
516
  emitUTF16EscapeSequence(lowSurrogate, buffer)
500
517
  }
@@ -558,21 +575,9 @@ let printNumberWasmI64 = (value: WasmI64, buffer: Buffer.Buffer) => {
558
575
  Buffer.addString(s, buffer)
559
576
  }
560
577
 
561
- @unsafe
562
- let isFinite = (value: WasmF64) => {
563
- use WasmF64.{ (==), (-) }
564
- value - value == 0.0W
565
- }
566
-
567
- @unsafe
568
- let isNaN = (value: WasmF64) => {
569
- use WasmF64.{ (!=) }
570
- value != value
571
- }
572
-
573
578
  @unsafe
574
579
  let printNumberWasmF64 = (value: WasmF64, buffer: Buffer.Buffer) => {
575
- if (isFinite(value)) {
580
+ if (NumberUtils.isFinite(value)) {
576
581
  let s = NumberUtils.dtoa(value)
577
582
  Buffer.addString(s, buffer)
578
583
  None
@@ -585,7 +590,7 @@ let printNumberWasmF64 = (value: WasmF64, buffer: Buffer.Buffer) => {
585
590
  // directly. Other possible choices were to throw exceptions or to
586
591
  // continue formatting without representing these values correctly
587
592
  // (like JavaScript's JSON.stringify).
588
- if (isNaN(value)) {
593
+ if (NumberUtils.isNaN(value)) {
589
594
  Some(InvalidNumber("NaN is not allowed in JsonNumber"))
590
595
  } else if (value < 0.0W) {
591
596
  Some(InvalidNumber("-Infinity is not allowed in JsonNumber"))
@@ -1558,20 +1563,22 @@ and parseString = (parserState: JsonParserState) => {
1558
1563
  // '"'
1559
1564
  Some(e) => return Err(e),
1560
1565
  None => {
1561
- let mut done = false
1562
1566
  let buffer = parserState.bufferParse
1563
1567
  Buffer.clear(buffer)
1564
1568
 
1565
- while (!done) {
1569
+ while (true) {
1566
1570
  match (parserState.currentCodePoint) {
1567
1571
  0x22 => { // '"'
1568
1572
  next(parserState)
1569
- done = true
1570
1573
  break
1571
1574
  },
1572
- -1 => {
1573
- // just end the loop without setting done to true
1574
- break
1575
+ -1 => { // EOF
1576
+ return Err(
1577
+ buildUnexpectedTokenError(
1578
+ parserState,
1579
+ "unexpected end of string value"
1580
+ ),
1581
+ )
1575
1582
  },
1576
1583
  0x5C => { // '\'
1577
1584
  // Keep the starting position for better error reporting.
@@ -1757,17 +1764,8 @@ and parseString = (parserState: JsonParserState) => {
1757
1764
  }
1758
1765
  }
1759
1766
 
1760
- if (done) {
1761
- let s = Buffer.toString(buffer)
1762
- return Ok(s)
1763
- } else {
1764
- return Err(
1765
- buildUnexpectedTokenError(
1766
- parserState,
1767
- "unexpected end of string value"
1768
- ),
1769
- )
1770
- }
1767
+ let s = Buffer.toString(buffer)
1768
+ return Ok(s)
1771
1769
  },
1772
1770
  }
1773
1771
  }
@@ -1887,10 +1885,14 @@ and parseNumberValue = (parserState: JsonParserState) => {
1887
1885
  }
1888
1886
  },
1889
1887
  }
1890
- if (result == 0 && isNegative)
1891
- return Ok(JsonNumber(-0.0))
1892
- else
1893
- return Ok(JsonNumber(if (isNegative) result * -1 else result))
1888
+
1889
+ let result = if (result == 0 && isNegative) {
1890
+ -0.0
1891
+ } else {
1892
+ if (isNegative) result * -1 else result
1893
+ }
1894
+
1895
+ return Ok(JsonNumber(result))
1894
1896
  }
1895
1897
  and parseArray = (parserState: JsonParserState) => {
1896
1898
  match (expectCodePointAndAdvance(0x5B, parserState)) {
@@ -1901,10 +1903,9 @@ and parseArray = (parserState: JsonParserState) => {
1901
1903
 
1902
1904
  let mut elems = []: List<Json>
1903
1905
 
1904
- let mut done = false
1905
1906
  let mut first = true
1906
1907
  let mut trailingComma = false
1907
- while (!done) {
1908
+ while (true) {
1908
1909
  let c = parserState.currentCodePoint
1909
1910
  match (c) {
1910
1911
  0x2C => { // ','
@@ -1922,12 +1923,12 @@ and parseArray = (parserState: JsonParserState) => {
1922
1923
  },
1923
1924
  0x5D => { // ']'
1924
1925
  next(parserState)
1925
- done = true
1926
1926
  break
1927
1927
  },
1928
- -1 => {
1929
- // just end the loop without setting done to true
1930
- break
1928
+ -1 => { // EOF
1929
+ return Err(
1930
+ buildUnexpectedTokenError(parserState, "unexpected end of array"),
1931
+ )
1931
1932
  },
1932
1933
  _ => {
1933
1934
  // note that parseValue skips initial and final whitespace
@@ -1947,12 +1948,8 @@ and parseArray = (parserState: JsonParserState) => {
1947
1948
  return Err(
1948
1949
  buildUnexpectedTokenError(parserState, "unexpected end of array"),
1949
1950
  )
1950
- } else if (done) {
1951
- return Ok(JsonArray(List.reverse(elems)))
1952
1951
  } else {
1953
- return Err(
1954
- buildUnexpectedTokenError(parserState, "unexpected end of array"),
1955
- )
1952
+ return Ok(JsonArray(List.reverse(elems)))
1956
1953
  }
1957
1954
  },
1958
1955
  }
@@ -1964,12 +1961,11 @@ and parseObject = (parserState: JsonParserState) => {
1964
1961
  None => {
1965
1962
  let mut entries = []: List<(String, Json)>
1966
1963
 
1967
- let mut done = false
1968
1964
  let mut first = true
1969
1965
 
1970
1966
  // one iteration of this loop should correspond to a key-value pair
1971
1967
  let mut trailingComma = false
1972
- while (!done) {
1968
+ while (true) {
1973
1969
  skipWhiteSpace(parserState)
1974
1970
 
1975
1971
  let c = parserState.currentCodePoint
@@ -1994,7 +1990,6 @@ and parseObject = (parserState: JsonParserState) => {
1994
1990
  return Err(buildUnexpectedTokenError(parserState, detail))
1995
1991
  }
1996
1992
  next(parserState)
1997
- done = true
1998
1993
  break
1999
1994
  },
2000
1995
  _ => {
@@ -2027,15 +2022,7 @@ and parseObject = (parserState: JsonParserState) => {
2027
2022
  }
2028
2023
  // end of entry loop
2029
2024
 
2030
- if (done) {
2031
- return Ok(JsonObject(List.reverse(entries)))
2032
- } else {
2033
- // This branch is not expected to actually execute,
2034
- // but in case it does, may just as well do the right thing.
2035
- return Err(
2036
- buildUnexpectedTokenError(parserState, "unexpected end of object"),
2037
- )
2038
- }
2025
+ return Ok(JsonObject(List.reverse(entries)))
2039
2026
  },
2040
2027
  }
2041
2028
  }
package/json.md CHANGED
@@ -40,6 +40,44 @@ enum Json {
40
40
 
41
41
  Data structure representing JSON in Grain.
42
42
 
43
+ Variants:
44
+
45
+ ```grain
46
+ JsonNull
47
+ ```
48
+
49
+ Represents the JSON `null` value.
50
+
51
+ ```grain
52
+ JsonBoolean(Bool)
53
+ ```
54
+
55
+ Represents a JSON boolean value.
56
+
57
+ ```grain
58
+ JsonNumber(Number)
59
+ ```
60
+
61
+ Represents a JSON number value.
62
+
63
+ ```grain
64
+ JsonString(String)
65
+ ```
66
+
67
+ Represents a JSON string value.
68
+
69
+ ```grain
70
+ JsonArray(List<Json>)
71
+ ```
72
+
73
+ Represents a JSON array value.
74
+
75
+ ```grain
76
+ JsonObject(List<(String, Json)>)
77
+ ```
78
+
79
+ Represents a JSON object value, as a list of (key, value).
80
+
43
81
  Examples:
44
82
 
45
83
  ```grain
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@grain/stdlib",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "description": "The standard library for the Grain language.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://grain-lang.org",
@@ -1452,13 +1452,13 @@ let get_dtoa_buf = () => {
1452
1452
  }
1453
1453
 
1454
1454
  @unsafe
1455
- let isFinite = value => {
1455
+ provide let isFinite = value => {
1456
1456
  use WasmF64.{ (==), (-) }
1457
1457
  value - value == 0.0W
1458
1458
  }
1459
1459
 
1460
1460
  @unsafe
1461
- let isNaN = value => {
1461
+ provide let isNaN = value => {
1462
1462
  use WasmF64.{ (!=) }
1463
1463
  value != value
1464
1464
  }
@@ -60,6 +60,18 @@ utoa64 : (value: WasmI64, radix: WasmI32) => String
60
60
  itoa64 : (value: WasmI64, radix: WasmI32) => String
61
61
  ```
62
62
 
63
+ ### NumberUtils.**isFinite**
64
+
65
+ ```grain
66
+ isFinite : (value: WasmF64) => Bool
67
+ ```
68
+
69
+ ### NumberUtils.**isNaN**
70
+
71
+ ```grain
72
+ isNaN : (value: WasmF64) => Bool
73
+ ```
74
+
63
75
  ### NumberUtils.**dtoa**
64
76
 
65
77
  ```grain
package/runtime/string.gr CHANGED
@@ -861,20 +861,20 @@ provide let print = (value, suffix="\n") => {
861
861
  // First convert the value to string, if it isn't one already.
862
862
  let valuePtr = WasmI32.fromGrain(value)
863
863
  let s = toString(value)
864
- let ptr = WasmI32.fromGrain(s)
865
- let suffixPtr = WasmI32.fromGrain(suffix)
866
- // iov: [<ptr to string> <nbytes of string> <ptr to end sequence> <nbytes of end sequence>] (32 bytes)
864
+ let combined = concat(s, suffix)
865
+ let ptr = WasmI32.fromGrain(combined)
866
+ // iov: [<ptr to string> <nbytes of string>]
867
867
  // buf: <iov> <written>
868
868
  // fd_write(STDOUT (1), iov, len(iov), written)
869
- let buf = Memory.malloc(36n)
869
+ let buf = Memory.malloc(20n)
870
870
  let iov = buf
871
- let written = buf + 32n
871
+ let written = buf + 16n
872
872
  WasmI32.store(iov, ptr + 8n, 0n)
873
873
  WasmI32.store(iov, WasmI32.load(ptr, 4n), 4n)
874
- WasmI32.store(iov, suffixPtr + 8n, 8n)
875
- WasmI32.store(iov, WasmI32.load(suffixPtr, 4n), 12n)
876
- fd_write(1n, iov, 2n, written)
874
+ fd_write(1n, iov, 1n, written)
877
875
  Memory.free(buf)
876
+ ignore(value)
877
+ ignore(suffix)
878
878
  void
879
879
  }
880
880