@grain/stdlib 0.6.6 → 0.7.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.
- package/CHANGELOG.md +57 -0
- package/LICENSE +1 -1
- package/README.md +2 -2
- package/array.gr +55 -7
- package/array.md +123 -77
- package/bigint.md +30 -30
- package/buffer.gr +20 -53
- package/buffer.md +47 -47
- package/bytes.gr +111 -35
- package/bytes.md +111 -32
- package/char.gr +201 -99
- package/char.md +361 -34
- package/exception.gr +11 -11
- package/exception.md +26 -1
- package/float32.gr +327 -3
- package/float32.md +606 -19
- package/float64.gr +320 -3
- package/float64.md +606 -19
- package/fs.gr +1082 -0
- package/fs.md +630 -0
- package/hash.gr +142 -88
- package/hash.md +102 -14
- package/int16.md +23 -23
- package/int32.gr +25 -4
- package/int32.md +65 -30
- package/int64.gr +26 -1
- package/int64.md +65 -30
- package/int8.md +23 -23
- package/json.gr +366 -51
- package/json.md +418 -2
- package/list.gr +328 -31
- package/list.md +492 -69
- package/map.gr +20 -12
- package/map.md +44 -38
- package/marshal.gr +41 -40
- package/marshal.md +2 -2
- package/number.gr +159 -30
- package/number.md +215 -38
- package/option.md +21 -21
- package/package.json +5 -3
- package/path.gr +48 -0
- package/path.md +103 -12
- package/pervasives.gr +2 -2
- package/pervasives.md +37 -37
- package/priorityqueue.gr +7 -7
- package/priorityqueue.md +19 -19
- package/queue.gr +183 -29
- package/queue.md +296 -40
- package/random.md +6 -6
- package/range.gr +4 -4
- package/range.md +6 -6
- package/rational.md +16 -16
- package/regex.gr +52 -51
- package/regex.md +11 -11
- package/result.md +16 -16
- package/runtime/atof/common.md +39 -39
- package/runtime/atof/decimal.gr +6 -6
- package/runtime/atof/decimal.md +8 -8
- package/runtime/atof/lemire.gr +5 -5
- package/runtime/atof/lemire.md +1 -1
- package/runtime/atof/parse.gr +16 -16
- package/runtime/atof/parse.md +2 -2
- package/runtime/atof/slow.md +1 -1
- package/runtime/atof/table.md +2 -2
- package/runtime/atoi/parse.gr +3 -3
- package/runtime/atoi/parse.md +1 -1
- package/runtime/bigint.gr +15 -47
- package/runtime/bigint.md +54 -60
- package/runtime/compare.gr +2 -2
- package/runtime/compare.md +1 -1
- package/runtime/dataStructures.md +33 -33
- package/runtime/debugPrint.gr +4 -1
- package/runtime/debugPrint.md +9 -9
- package/runtime/equal.gr +99 -77
- package/runtime/equal.md +1 -1
- package/runtime/exception.gr +62 -82
- package/runtime/exception.md +62 -11
- package/runtime/gc.gr +39 -45
- package/runtime/gc.md +4 -4
- package/runtime/malloc.gr +7 -7
- package/runtime/malloc.md +4 -4
- package/runtime/math/kernel/cos.gr +70 -0
- package/runtime/math/kernel/cos.md +14 -0
- package/runtime/math/kernel/sin.gr +65 -0
- package/runtime/math/kernel/sin.md +14 -0
- package/runtime/math/kernel/tan.gr +136 -0
- package/runtime/math/kernel/tan.md +14 -0
- package/runtime/math/rempio2.gr +244 -0
- package/runtime/math/rempio2.md +14 -0
- package/runtime/math/trig.gr +130 -0
- package/runtime/math/trig.md +28 -0
- package/runtime/math/umuldi.gr +26 -0
- package/runtime/math/umuldi.md +14 -0
- package/runtime/numberUtils.gr +29 -29
- package/runtime/numberUtils.md +12 -12
- package/runtime/numbers.gr +373 -381
- package/runtime/numbers.md +79 -73
- package/runtime/string.gr +37 -105
- package/runtime/string.md +3 -9
- package/runtime/unsafe/constants.md +24 -24
- package/runtime/unsafe/conv.md +13 -13
- package/runtime/unsafe/memory.gr +24 -20
- package/runtime/unsafe/memory.md +27 -7
- package/runtime/unsafe/offsets.gr +36 -0
- package/runtime/unsafe/offsets.md +88 -0
- package/runtime/unsafe/panic.gr +28 -0
- package/runtime/unsafe/panic.md +14 -0
- package/runtime/unsafe/tags.md +32 -32
- package/runtime/unsafe/wasmf32.md +28 -28
- package/runtime/unsafe/wasmf64.md +28 -28
- package/runtime/unsafe/wasmi32.md +47 -47
- package/runtime/unsafe/wasmi64.md +50 -50
- package/runtime/utf8.gr +189 -0
- package/runtime/utf8.md +117 -0
- package/runtime/wasi.gr +4 -2
- package/runtime/wasi.md +138 -138
- package/set.gr +18 -11
- package/set.md +42 -36
- package/stack.gr +171 -2
- package/stack.md +297 -15
- package/string.gr +352 -557
- package/string.md +77 -34
- package/uint16.md +22 -22
- package/uint32.gr +25 -4
- package/uint32.md +63 -28
- package/uint64.gr +25 -5
- package/uint64.md +63 -28
- package/uint8.md +22 -22
- package/uri.gr +57 -53
- package/uri.md +11 -12
- package/wasi/file.gr +67 -59
- package/wasi/file.md +39 -39
- package/wasi/process.md +5 -5
- package/wasi/random.md +3 -3
- package/wasi/time.md +4 -4
- package/runtime/utils/printing.gr +0 -60
- package/runtime/utils/printing.md +0 -26
package/json.gr
CHANGED
|
@@ -17,6 +17,8 @@ from "runtime/bigint" include Bigint as BI
|
|
|
17
17
|
from "runtime/dataStructures" include DataStructures
|
|
18
18
|
from "runtime/numbers" include Numbers
|
|
19
19
|
from "runtime/numberUtils" include NumberUtils
|
|
20
|
+
from "runtime/utf8" include Utf8
|
|
21
|
+
use Utf8.{ getCodePoint }
|
|
20
22
|
from "runtime/string" include String as RuntimeString
|
|
21
23
|
from "runtime/unsafe/tags" include Tags
|
|
22
24
|
from "runtime/unsafe/wasmi32" include WasmI32
|
|
@@ -28,7 +30,7 @@ from "char" include Char
|
|
|
28
30
|
from "string" include String
|
|
29
31
|
from "list" include List
|
|
30
32
|
from "uint8" include Uint8
|
|
31
|
-
use RuntimeString.{ toString as runtimeToString
|
|
33
|
+
use RuntimeString.{ toString as runtimeToString }
|
|
32
34
|
use Numbers.{ coerceNumberToWasmI32 }
|
|
33
35
|
use DataStructures.{ tagSimpleNumber, untagSimpleNumber }
|
|
34
36
|
|
|
@@ -1026,8 +1028,8 @@ let makeJsonWriter = (format: FormattingSettings, buffer: Buffer.Buffer) => {
|
|
|
1026
1028
|
// escapes it when needed, but requires to keep track of the previous code
|
|
1027
1029
|
// point in the iteration so it's more complicated and handled separately.
|
|
1028
1030
|
let emitCodePoint = if (
|
|
1029
|
-
!format.escapeAllControlPoints
|
|
1030
|
-
!format.escapeNonASCII
|
|
1031
|
+
!format.escapeAllControlPoints
|
|
1032
|
+
&& !format.escapeNonASCII
|
|
1031
1033
|
) {
|
|
1032
1034
|
(codePoint: Number) => {
|
|
1033
1035
|
if (codePoint > 31 && codePoint != 0x0022 && codePoint != 0x005C) {
|
|
@@ -1042,10 +1044,10 @@ let makeJsonWriter = (format: FormattingSettings, buffer: Buffer.Buffer) => {
|
|
|
1042
1044
|
// from 31 to 127.
|
|
1043
1045
|
(codePoint: Number) => {
|
|
1044
1046
|
if (
|
|
1045
|
-
codePoint > 31
|
|
1046
|
-
codePoint != 0x0022
|
|
1047
|
-
codePoint != 0x005C
|
|
1048
|
-
codePoint < 128
|
|
1047
|
+
codePoint > 31
|
|
1048
|
+
&& codePoint != 0x0022
|
|
1049
|
+
&& codePoint != 0x005C
|
|
1050
|
+
&& codePoint < 128
|
|
1049
1051
|
) {
|
|
1050
1052
|
Buffer.addCharFromCodePoint(codePoint, buffer)
|
|
1051
1053
|
} else {
|
|
@@ -1059,11 +1061,11 @@ let makeJsonWriter = (format: FormattingSettings, buffer: Buffer.Buffer) => {
|
|
|
1059
1061
|
// codepoints, but covering that would be overkill.
|
|
1060
1062
|
(codePoint: Number) => {
|
|
1061
1063
|
if (
|
|
1062
|
-
codePoint > 31
|
|
1063
|
-
codePoint != 0x0022
|
|
1064
|
-
codePoint != 0x005C
|
|
1065
|
-
codePoint < 127
|
|
1066
|
-
codePoint > 159
|
|
1064
|
+
codePoint > 31
|
|
1065
|
+
&& codePoint != 0x0022
|
|
1066
|
+
&& codePoint != 0x005C
|
|
1067
|
+
&& codePoint < 127
|
|
1068
|
+
|| codePoint > 159
|
|
1067
1069
|
) {
|
|
1068
1070
|
Buffer.addCharFromCodePoint(codePoint, buffer)
|
|
1069
1071
|
} else {
|
|
@@ -1077,10 +1079,10 @@ let makeJsonWriter = (format: FormattingSettings, buffer: Buffer.Buffer) => {
|
|
|
1077
1079
|
// 127 (Delete).
|
|
1078
1080
|
(codePoint: Number) => {
|
|
1079
1081
|
if (
|
|
1080
|
-
codePoint > 31
|
|
1081
|
-
codePoint != 0x0022
|
|
1082
|
-
codePoint != 0x005C
|
|
1083
|
-
codePoint < 127
|
|
1082
|
+
codePoint > 31
|
|
1083
|
+
&& codePoint != 0x0022
|
|
1084
|
+
&& codePoint != 0x005C
|
|
1085
|
+
&& codePoint < 127
|
|
1084
1086
|
) {
|
|
1085
1087
|
// fast path for chars that never need any escaping
|
|
1086
1088
|
Buffer.addCharFromCodePoint(codePoint, buffer)
|
|
@@ -1321,6 +1323,7 @@ let rec readCodePoint = (bytePosition: Number, string: String) => {
|
|
|
1321
1323
|
|
|
1322
1324
|
if (bytePositionW32 < byteSize) {
|
|
1323
1325
|
let codePoint = getCodePoint(ptr)
|
|
1326
|
+
|
|
1324
1327
|
tagSimpleNumber(codePoint)
|
|
1325
1328
|
} else {
|
|
1326
1329
|
_END_OF_INPUT
|
|
@@ -1364,8 +1367,8 @@ let skipWhiteSpace = (parserState: JsonParserState) => {
|
|
|
1364
1367
|
// isAtEndOfInput is not strictly necessary here
|
|
1365
1368
|
// could remove as an optimization
|
|
1366
1369
|
while (
|
|
1367
|
-
isInterTokenWhiteSpace(parserState.currentCodePoint)
|
|
1368
|
-
!isAtEndOfInput(parserState)
|
|
1370
|
+
isInterTokenWhiteSpace(parserState.currentCodePoint)
|
|
1371
|
+
&& !isAtEndOfInput(parserState)
|
|
1369
1372
|
) {
|
|
1370
1373
|
next(parserState)
|
|
1371
1374
|
void
|
|
@@ -1426,10 +1429,10 @@ let expectCodePointAndAdvance = (
|
|
|
1426
1429
|
next(parserState)
|
|
1427
1430
|
None
|
|
1428
1431
|
} else {
|
|
1429
|
-
let detail = "expected "
|
|
1430
|
-
formatCodePointOrEOF(expectedCodePoint)
|
|
1431
|
-
", found "
|
|
1432
|
-
formatCodePointOrEOF(c)
|
|
1432
|
+
let detail = "expected "
|
|
1433
|
+
++ formatCodePointOrEOF(expectedCodePoint)
|
|
1434
|
+
++ ", found "
|
|
1435
|
+
++ formatCodePointOrEOF(c)
|
|
1433
1436
|
Some(buildUnexpectedTokenError(parserState, detail))
|
|
1434
1437
|
}
|
|
1435
1438
|
}
|
|
@@ -1438,9 +1441,9 @@ let atoiFast = buffer => {
|
|
|
1438
1441
|
let mut result = 0
|
|
1439
1442
|
for (let mut i = 0; i < bufLen; i += 1) {
|
|
1440
1443
|
use Uint8.{ (-) }
|
|
1441
|
-
result = (result << 1)
|
|
1442
|
-
(result << 3)
|
|
1443
|
-
Uint8.toNumber(Buffer.getUint8(i, buffer) - 48us)
|
|
1444
|
+
result = (result << 1)
|
|
1445
|
+
+ (result << 3)
|
|
1446
|
+
+ Uint8.toNumber(Buffer.getUint8(i, buffer) - 48us)
|
|
1444
1447
|
}
|
|
1445
1448
|
result
|
|
1446
1449
|
}
|
|
@@ -1467,8 +1470,8 @@ let rec parseValue = (parserState: JsonParserState) => {
|
|
|
1467
1470
|
0x39 => parseNumberValue(parserState), // '9'
|
|
1468
1471
|
0x2D => parseNumberValue(parserState), // '-'
|
|
1469
1472
|
c => {
|
|
1470
|
-
let detail = "expected start of a JSON value, found "
|
|
1471
|
-
formatCodePointOrEOF(c)
|
|
1473
|
+
let detail = "expected start of a JSON value, found "
|
|
1474
|
+
++ formatCodePointOrEOF(c)
|
|
1472
1475
|
Err(buildUnexpectedTokenError(parserState, detail))
|
|
1473
1476
|
},
|
|
1474
1477
|
}
|
|
@@ -1655,20 +1658,20 @@ and parseString = (parserState: JsonParserState) => {
|
|
|
1655
1658
|
if (hexDigitCodePoint >= 48 && hexDigitCodePoint <= 57) { // 0..9
|
|
1656
1659
|
digit -= 48
|
|
1657
1660
|
} else if (
|
|
1658
|
-
hexDigitCodePoint >= 65
|
|
1659
|
-
hexDigitCodePoint <= 70
|
|
1661
|
+
hexDigitCodePoint >= 65
|
|
1662
|
+
&& hexDigitCodePoint <= 70
|
|
1660
1663
|
) { // A..F
|
|
1661
1664
|
digit -= 55 // (65 - 10)
|
|
1662
1665
|
} else if (
|
|
1663
|
-
hexDigitCodePoint >= 97
|
|
1664
|
-
hexDigitCodePoint <= 102
|
|
1666
|
+
hexDigitCodePoint >= 97
|
|
1667
|
+
&& hexDigitCodePoint <= 102
|
|
1665
1668
|
) { // a..f
|
|
1666
1669
|
digit -= 87 // (97 - 10)
|
|
1667
1670
|
} else {
|
|
1668
1671
|
let digitsSoFar = 3 - digitIndex
|
|
1669
1672
|
let detail =
|
|
1670
|
-
"expected exactly 4 hexadecimal digits in the UTF-16 escape sequence, found only "
|
|
1671
|
-
runtimeToString(digitsSoFar)
|
|
1673
|
+
"expected exactly 4 hexadecimal digits in the UTF-16 escape sequence, found only "
|
|
1674
|
+
++ runtimeToString(digitsSoFar)
|
|
1672
1675
|
return Err(buildUnexpectedTokenError(parserState, detail))
|
|
1673
1676
|
}
|
|
1674
1677
|
|
|
@@ -1700,19 +1703,19 @@ and parseString = (parserState: JsonParserState) => {
|
|
|
1700
1703
|
// iteration of the loop.
|
|
1701
1704
|
highSurrogate = codeUnit
|
|
1702
1705
|
} else if (
|
|
1703
|
-
isCodePointInBasicMultilingualPlane(codeUnit)
|
|
1704
|
-
!isLowSurrogate(codeUnit)
|
|
1706
|
+
isCodePointInBasicMultilingualPlane(codeUnit)
|
|
1707
|
+
&& !isLowSurrogate(codeUnit)
|
|
1705
1708
|
) {
|
|
1706
1709
|
let codePoint = codeUnit
|
|
1707
1710
|
Buffer.addCharFromCodePoint(codePoint, buffer)
|
|
1708
1711
|
break
|
|
1709
1712
|
} else {
|
|
1710
1713
|
let message =
|
|
1711
|
-
"Invalid character escape sequence at position "
|
|
1712
|
-
runtimeToString(escapeStartPos)
|
|
1713
|
-
": expected a Unicode code point in Basic Multilingual Plane (U+0000..U+FFFF) or a high surrogate (0xD800..0xDBFF) of a UTF-16 surrogate pair, found "
|
|
1714
|
-
"0x"
|
|
1715
|
-
toHexWithZeroPadding(codeUnit, 4)
|
|
1714
|
+
"Invalid character escape sequence at position "
|
|
1715
|
+
++ runtimeToString(escapeStartPos)
|
|
1716
|
+
++ ": expected a Unicode code point in Basic Multilingual Plane (U+0000..U+FFFF) or a high surrogate (0xD800..0xDBFF) of a UTF-16 surrogate pair, found "
|
|
1717
|
+
++ "0x"
|
|
1718
|
+
++ toHexWithZeroPadding(codeUnit, 4)
|
|
1716
1719
|
return Err(InvalidUTF16SurrogatePair(message))
|
|
1717
1720
|
}
|
|
1718
1721
|
} else {
|
|
@@ -1728,11 +1731,11 @@ and parseString = (parserState: JsonParserState) => {
|
|
|
1728
1731
|
break
|
|
1729
1732
|
} else {
|
|
1730
1733
|
let message =
|
|
1731
|
-
"Invalid character escape sequence at position "
|
|
1732
|
-
runtimeToString(escapeStartPos)
|
|
1733
|
-
": expected a low surrogate (0xDC00..0xDFFF) in the second code unit of the UTF-16 sequence, found "
|
|
1734
|
-
"0x"
|
|
1735
|
-
toHexWithZeroPadding(codeUnit, 4)
|
|
1734
|
+
"Invalid character escape sequence at position "
|
|
1735
|
+
++ runtimeToString(escapeStartPos)
|
|
1736
|
+
++ ": expected a low surrogate (0xDC00..0xDFFF) in the second code unit of the UTF-16 sequence, found "
|
|
1737
|
+
++ "0x"
|
|
1738
|
+
++ toHexWithZeroPadding(codeUnit, 4)
|
|
1736
1739
|
return Err(InvalidUTF16SurrogatePair(message))
|
|
1737
1740
|
}
|
|
1738
1741
|
}
|
|
@@ -1742,8 +1745,8 @@ and parseString = (parserState: JsonParserState) => {
|
|
|
1742
1745
|
// JSON doesn't allow arbitrary characters to be preceded by backslash escape.
|
|
1743
1746
|
// Only the ones above.
|
|
1744
1747
|
let detail =
|
|
1745
|
-
"expected a valid escape sequence or the end of string, found "
|
|
1746
|
-
formatCodePointOrEOF(unexpectedCodePoint)
|
|
1748
|
+
"expected a valid escape sequence or the end of string, found "
|
|
1749
|
+
++ formatCodePointOrEOF(unexpectedCodePoint)
|
|
1747
1750
|
return Err(buildUnexpectedTokenError(parserState, detail))
|
|
1748
1751
|
},
|
|
1749
1752
|
}
|
|
@@ -1816,8 +1819,8 @@ and parseNumberValue = (parserState: JsonParserState) => {
|
|
|
1816
1819
|
unexpectedCodePoint => {
|
|
1817
1820
|
// The integer part of the number has to have at least one digit.
|
|
1818
1821
|
// JSON doesn't allow numbers starting with decimal separator like ".1".
|
|
1819
|
-
let detail = "expected a decimal digit, found "
|
|
1820
|
-
formatCodePointOrEOF(unexpectedCodePoint)
|
|
1822
|
+
let detail = "expected a decimal digit, found "
|
|
1823
|
+
++ formatCodePointOrEOF(unexpectedCodePoint)
|
|
1821
1824
|
return Err(buildUnexpectedTokenError(parserState, detail))
|
|
1822
1825
|
},
|
|
1823
1826
|
}
|
|
@@ -2061,11 +2064,323 @@ provide let parse: (str: String) => Result<Json, JsonParseError> = (str: String)
|
|
|
2061
2064
|
} else {
|
|
2062
2065
|
match (root) {
|
|
2063
2066
|
Ok(_) => {
|
|
2064
|
-
let detail = "expected end of input, found "
|
|
2065
|
-
formatCodePointOrEOF(parserState.currentCodePoint)
|
|
2067
|
+
let detail = "expected end of input, found "
|
|
2068
|
+
++ formatCodePointOrEOF(parserState.currentCodePoint)
|
|
2066
2069
|
Err(buildUnexpectedTokenError(parserState, detail))
|
|
2067
2070
|
},
|
|
2068
2071
|
e => e,
|
|
2069
2072
|
}
|
|
2070
2073
|
}
|
|
2071
2074
|
}
|
|
2075
|
+
|
|
2076
|
+
/**
|
|
2077
|
+
* Utilities for accessing and updating JSON data.
|
|
2078
|
+
*
|
|
2079
|
+
* @example
|
|
2080
|
+
* let obj = JsonObject([("x", JsonNumber(123))])
|
|
2081
|
+
* assert get(property("x") ||> number, obj) == Some(123)
|
|
2082
|
+
* @example
|
|
2083
|
+
* let obj = JsonObject([("x", JsonNumber(123))])
|
|
2084
|
+
* assert set(property("x") ||> number, 321, obj) ==
|
|
2085
|
+
* Some(JsonObject([("x", JsonNumber(321))]))
|
|
2086
|
+
*
|
|
2087
|
+
* @since v0.7.0
|
|
2088
|
+
*/
|
|
2089
|
+
provide module Lenses {
|
|
2090
|
+
/**
|
|
2091
|
+
* A structure which provides functionality for accessing and setting JSON
|
|
2092
|
+
* data.
|
|
2093
|
+
*
|
|
2094
|
+
* @since v0.7.0
|
|
2095
|
+
*/
|
|
2096
|
+
provide record Lens<a, b> {
|
|
2097
|
+
/**
|
|
2098
|
+
* A function which reads a value from the subject.
|
|
2099
|
+
*/
|
|
2100
|
+
get: (subject: a) => Option<b>,
|
|
2101
|
+
/**
|
|
2102
|
+
* A function which immutably updates a value in the subject.
|
|
2103
|
+
*/
|
|
2104
|
+
set: (newValue: b, subject: a) => Option<a>,
|
|
2105
|
+
}
|
|
2106
|
+
|
|
2107
|
+
/**
|
|
2108
|
+
* Reads the value focused on by the given lens from the input data.
|
|
2109
|
+
*
|
|
2110
|
+
* @param lens: The lens to apply to the subject data
|
|
2111
|
+
* @param subject: The data which will have the lens applied to it
|
|
2112
|
+
* @returns `Some(data)` containing the data read by the lens if the lens matches the given data, or `None` if the data cannot be matched to the lens
|
|
2113
|
+
*
|
|
2114
|
+
* @example assert get(number, JsonNumber(123)) == Some(123)
|
|
2115
|
+
* @example assert get(string, JsonString("abc")) == Some("abc")
|
|
2116
|
+
* @example assert get(number, JsonString("abc")) == None
|
|
2117
|
+
*/
|
|
2118
|
+
provide let get = (lens, subject) => lens.get(subject)
|
|
2119
|
+
|
|
2120
|
+
/**
|
|
2121
|
+
* Sets the value focused on by the given lens from the input data to the
|
|
2122
|
+
* desired new value.
|
|
2123
|
+
*
|
|
2124
|
+
* @param lens: The lens to apply to the subject data
|
|
2125
|
+
* @param newValue: The new value to set at the focus of the lens
|
|
2126
|
+
* @param subject: The data which will have the lens applied to it
|
|
2127
|
+
* @returns `Some(data)` containing the new data after the lens substitution if the lens matches the given data, or `None` if the data cannot be matched to the lens
|
|
2128
|
+
*
|
|
2129
|
+
* @example assert set(number, 123, JsonBoolean(true)) == Some(JsonNumber(123))
|
|
2130
|
+
* @example assert set(property("a"), JsonNumber(123), JsonObject([("a", JsonNull)])) == Some(JsonObject([("a", JsonNumber(123))]))
|
|
2131
|
+
* @example assert set(property("a"), JsonNumber(123), JsonBoolean(true)) == None
|
|
2132
|
+
*/
|
|
2133
|
+
provide let set = (lens, newValue, subject) => lens.set(newValue, subject)
|
|
2134
|
+
|
|
2135
|
+
/**
|
|
2136
|
+
* Updates the value focused on by the given lens from the input data by
|
|
2137
|
+
* applying a function to it and setting the focus to the result of the function
|
|
2138
|
+
*
|
|
2139
|
+
* @param lens: The lens to apply to the subject data
|
|
2140
|
+
* @param fn: The function to apply to the matched data at the lens if matched
|
|
2141
|
+
* @param subject: The data which will have the lens applied to it
|
|
2142
|
+
* @returns `Some(data)` containing the new data after the lens mapping has been applied if the lens matches the given data, or `None` if the data cannot be matched to the lens
|
|
2143
|
+
*
|
|
2144
|
+
* @example assert map(number, x => x * 2, JsonNumber(5)) == Some(JsonNumber(10))
|
|
2145
|
+
* @example
|
|
2146
|
+
* assert map(property("x"), x => JsonArray([x, x]), JsonObject([("x", JsonNumber(1))])) ==
|
|
2147
|
+
* Some(JsonObject([("x", JsonArray([JsonNumber(1), JsonNumber(1)]))]))
|
|
2148
|
+
* @example assert map(number, x => x * 2, JsonString("abc")) == None
|
|
2149
|
+
*/
|
|
2150
|
+
provide let map = (lens, fn, subject) => {
|
|
2151
|
+
match (lens.get(subject)) {
|
|
2152
|
+
Some(lensVal) => lens.set(fn(lensVal), subject),
|
|
2153
|
+
None => None,
|
|
2154
|
+
}
|
|
2155
|
+
}
|
|
2156
|
+
|
|
2157
|
+
/**
|
|
2158
|
+
* A lens whose focus is a JSON value.
|
|
2159
|
+
*
|
|
2160
|
+
* @example assert get(json, JsonString("abc")) == Some(JsonString("abc"))
|
|
2161
|
+
*
|
|
2162
|
+
* @since v0.7.0
|
|
2163
|
+
*/
|
|
2164
|
+
provide let json = {
|
|
2165
|
+
get: json => Some(json),
|
|
2166
|
+
set: (newValue, _) => Some(newValue),
|
|
2167
|
+
}
|
|
2168
|
+
|
|
2169
|
+
/**
|
|
2170
|
+
* A lens whose focus is a JSON boolean value.
|
|
2171
|
+
*
|
|
2172
|
+
* @example assert get(boolean, JsonBoolean(true)) == Some(true)
|
|
2173
|
+
*
|
|
2174
|
+
* @since v0.7.0
|
|
2175
|
+
*/
|
|
2176
|
+
provide let boolean = {
|
|
2177
|
+
get: json => {
|
|
2178
|
+
match (json) {
|
|
2179
|
+
JsonBoolean(val) => Some(val),
|
|
2180
|
+
_ => None,
|
|
2181
|
+
}
|
|
2182
|
+
},
|
|
2183
|
+
set: (newValue, _) => Some(JsonBoolean(newValue)),
|
|
2184
|
+
}
|
|
2185
|
+
|
|
2186
|
+
/**
|
|
2187
|
+
* A lens whose focus is a JSON string value.
|
|
2188
|
+
*
|
|
2189
|
+
* @example assert get(string, JsonString("abc")) == Some("abc")
|
|
2190
|
+
*
|
|
2191
|
+
* @since v0.7.0
|
|
2192
|
+
*/
|
|
2193
|
+
provide let string = {
|
|
2194
|
+
get: json => {
|
|
2195
|
+
match (json) {
|
|
2196
|
+
JsonString(val) => Some(val),
|
|
2197
|
+
_ => None,
|
|
2198
|
+
}
|
|
2199
|
+
},
|
|
2200
|
+
set: (newValue, _) => Some(JsonString(newValue)),
|
|
2201
|
+
}
|
|
2202
|
+
|
|
2203
|
+
/**
|
|
2204
|
+
* A lens whose focus is a JSON number value.
|
|
2205
|
+
*
|
|
2206
|
+
* @example assert get(number, JsonNumber(123)) == Some(123)
|
|
2207
|
+
*
|
|
2208
|
+
* @since v0.7.0
|
|
2209
|
+
*/
|
|
2210
|
+
provide let number = {
|
|
2211
|
+
get: json => {
|
|
2212
|
+
match (json) {
|
|
2213
|
+
JsonNumber(val) => Some(val),
|
|
2214
|
+
_ => None,
|
|
2215
|
+
}
|
|
2216
|
+
},
|
|
2217
|
+
set: (newValue, _) => Some(JsonNumber(newValue)),
|
|
2218
|
+
}
|
|
2219
|
+
|
|
2220
|
+
/**
|
|
2221
|
+
* A lens whose focus is a JSON array.
|
|
2222
|
+
*
|
|
2223
|
+
* @example assert get(array, JsonArray([JsonNumber(123)])) == Some([JsonNumber(123)])
|
|
2224
|
+
*
|
|
2225
|
+
* @since v0.7.0
|
|
2226
|
+
*/
|
|
2227
|
+
provide let array = {
|
|
2228
|
+
get: json => {
|
|
2229
|
+
match (json) {
|
|
2230
|
+
JsonArray(val) => Some(val),
|
|
2231
|
+
_ => None,
|
|
2232
|
+
}
|
|
2233
|
+
},
|
|
2234
|
+
set: (newValue, _) => Some(JsonArray(newValue)),
|
|
2235
|
+
}
|
|
2236
|
+
|
|
2237
|
+
/**
|
|
2238
|
+
* A lens whose focus is the property pair list of a JSON object.
|
|
2239
|
+
*
|
|
2240
|
+
* @example assert get(objectProperties, JsonObject([("a", JsonNumber(123))])) == Some([("a", JsonNumber(123))])
|
|
2241
|
+
*
|
|
2242
|
+
* @since v0.7.0
|
|
2243
|
+
*/
|
|
2244
|
+
provide let objectProperties = {
|
|
2245
|
+
get: json => {
|
|
2246
|
+
match (json) {
|
|
2247
|
+
JsonObject(val) => Some(val),
|
|
2248
|
+
_ => None,
|
|
2249
|
+
}
|
|
2250
|
+
},
|
|
2251
|
+
set: (newValue, _) => Some(JsonObject(newValue)),
|
|
2252
|
+
}
|
|
2253
|
+
|
|
2254
|
+
let rec replaceFirst = (acc, list, propertyName, newValue) => {
|
|
2255
|
+
match (list) {
|
|
2256
|
+
[first, ...rest] => {
|
|
2257
|
+
let (firstKey, firstVal) = first
|
|
2258
|
+
if (firstKey == propertyName) {
|
|
2259
|
+
List.append(List.reverse([(firstKey, newValue), ...acc]), rest)
|
|
2260
|
+
} else {
|
|
2261
|
+
replaceFirst([first, ...acc], rest, propertyName, newValue)
|
|
2262
|
+
}
|
|
2263
|
+
},
|
|
2264
|
+
[] => List.reverse([(propertyName, newValue), ...acc]),
|
|
2265
|
+
}
|
|
2266
|
+
}
|
|
2267
|
+
|
|
2268
|
+
/**
|
|
2269
|
+
* Creates a lens whose focus is a given property of a JSON object.
|
|
2270
|
+
*
|
|
2271
|
+
* @param propertyName: The property name of the JSON object to focus on
|
|
2272
|
+
* @returns A lens whose focus is the given property of a JSON object
|
|
2273
|
+
*
|
|
2274
|
+
* @example assert get(property("x"), JsonObject([("x", JsonNumber(123))])) == Some(JsonNumber(123))
|
|
2275
|
+
* @example
|
|
2276
|
+
* assert set(property("x"), JsonString("new"), JsonObject([("x", JsonNumber(123))])) ==
|
|
2277
|
+
* Some(JsonObject([("x", JsonString("new"))]))
|
|
2278
|
+
*
|
|
2279
|
+
* @since v0.7.0
|
|
2280
|
+
*/
|
|
2281
|
+
provide let property = propertyName =>
|
|
2282
|
+
{ get: json => match (json) {
|
|
2283
|
+
JsonObject(props) => {
|
|
2284
|
+
match (List.find(((k, _)) => k == propertyName, props)) {
|
|
2285
|
+
Some((_, v)) => Some(v),
|
|
2286
|
+
None => None,
|
|
2287
|
+
}
|
|
2288
|
+
},
|
|
2289
|
+
_ => None,
|
|
2290
|
+
}, set: (newValue, json) => {
|
|
2291
|
+
match (json) {
|
|
2292
|
+
JsonObject(props) =>
|
|
2293
|
+
Some(JsonObject(replaceFirst([], props, propertyName, newValue))),
|
|
2294
|
+
_ => None,
|
|
2295
|
+
}
|
|
2296
|
+
} }
|
|
2297
|
+
|
|
2298
|
+
/**
|
|
2299
|
+
* Wraps a lens to permit nullable values in addition to the original value
|
|
2300
|
+
* type of the given lens. During a `get` operation if the lens matches then
|
|
2301
|
+
* the result will be enclosed in `Some`; if the lens does not match but the
|
|
2302
|
+
* value focused is null, then the lens will still successfully match and
|
|
2303
|
+
* `None` will be returned.
|
|
2304
|
+
*
|
|
2305
|
+
* @example assert get(nullable(number), JsonNumber(123)) == Some(Some(123))
|
|
2306
|
+
* @example assert get(nullable(number), JsonNull) == Some(None)
|
|
2307
|
+
* @example assert get(nullable(number), JsonString("abc")) == None
|
|
2308
|
+
* @example assert set(nullable(number), Some(123), JsonString("abc")) == Some(JsonNumber(123))
|
|
2309
|
+
*
|
|
2310
|
+
* @since v0.7.0
|
|
2311
|
+
*/
|
|
2312
|
+
provide let nullable = lens =>
|
|
2313
|
+
{ get: json => {
|
|
2314
|
+
match (get(lens, json)) {
|
|
2315
|
+
Some(x) => Some(Some(x)),
|
|
2316
|
+
None => {
|
|
2317
|
+
match (json) {
|
|
2318
|
+
JsonNull => Some(None),
|
|
2319
|
+
_ => {
|
|
2320
|
+
match (get(lens, json)) {
|
|
2321
|
+
Some(x) => Some(Some(x)),
|
|
2322
|
+
None => None,
|
|
2323
|
+
}
|
|
2324
|
+
},
|
|
2325
|
+
}
|
|
2326
|
+
},
|
|
2327
|
+
}
|
|
2328
|
+
}, set: (newValue, json) => {
|
|
2329
|
+
match (newValue) {
|
|
2330
|
+
Some(x) => lens.set(x, json),
|
|
2331
|
+
None => Some(JsonNull),
|
|
2332
|
+
}
|
|
2333
|
+
} }
|
|
2334
|
+
|
|
2335
|
+
/**
|
|
2336
|
+
* Reverse lens composition.
|
|
2337
|
+
*
|
|
2338
|
+
* @param lens1: The lens which will be applied first
|
|
2339
|
+
* @param lens2: The lens which will be applied second
|
|
2340
|
+
* @returns A lens which combines the two given lenses, passing through the first and then the second
|
|
2341
|
+
*
|
|
2342
|
+
* @example assert get(property("x") ||> number, JsonObject([("x", JsonNumber(123))])) == Some(123)
|
|
2343
|
+
* @example
|
|
2344
|
+
* assert set(property("x") ||> string, "new", JsonObject([("x", JsonNumber(123))])) ==
|
|
2345
|
+
* Some(JsonObject([("x", JsonString("new"))]))
|
|
2346
|
+
*
|
|
2347
|
+
* @since v0.7.0
|
|
2348
|
+
*/
|
|
2349
|
+
let (||>) = (lens1, lens2) =>
|
|
2350
|
+
{ get: json => {
|
|
2351
|
+
match (lens1.get(json)) {
|
|
2352
|
+
Some(x) => lens2.get(x),
|
|
2353
|
+
None => None,
|
|
2354
|
+
}
|
|
2355
|
+
}, set: (newValue, target) => {
|
|
2356
|
+
match (lens1.get(target)) {
|
|
2357
|
+
Some(x) => match (lens2.set(newValue, x)) {
|
|
2358
|
+
Some(y) => lens1.set(y, target),
|
|
2359
|
+
None => None,
|
|
2360
|
+
},
|
|
2361
|
+
None => None,
|
|
2362
|
+
}
|
|
2363
|
+
} }
|
|
2364
|
+
|
|
2365
|
+
/**
|
|
2366
|
+
* Creates a lens whose focus is a given property path within a JSON object tree.
|
|
2367
|
+
*
|
|
2368
|
+
* @param propertyNames: The property path of the JSON object to create a focus on
|
|
2369
|
+
* @returns A lens whose focus is the given property path of a JSON object
|
|
2370
|
+
*
|
|
2371
|
+
* @example
|
|
2372
|
+
* let nestedObj = JsonObject([("a", JsonObject([("b", JsonNumber(123))]))])
|
|
2373
|
+
* assert get(propertyPath(["a", "b"]), nestedObj) == Some(JsonNumber(123))
|
|
2374
|
+
*
|
|
2375
|
+
* @since v0.7.0
|
|
2376
|
+
*/
|
|
2377
|
+
provide let propertyPath = propertyNames => {
|
|
2378
|
+
List.reduce(
|
|
2379
|
+
(lensAcc, propertyName) => lensAcc ||> property(propertyName),
|
|
2380
|
+
json,
|
|
2381
|
+
propertyNames
|
|
2382
|
+
)
|
|
2383
|
+
}
|
|
2384
|
+
|
|
2385
|
+
provide { (||>) }
|
|
2386
|
+
}
|