merge builtin-json

This commit is contained in:
Luke Boswell 2023-03-15 18:07:01 +11:00
commit ef0de4c72f
No known key found for this signature in database
GPG key ID: F6DB3C9DB47377B0
48 changed files with 1175 additions and 977 deletions

View file

@ -188,6 +188,23 @@ pub const RocList = extern struct {
};
}
pub fn allocateExact(
alignment: u32,
length: usize,
element_width: usize,
) RocList {
if (length == 0) {
return empty();
}
const data_bytes = length * element_width;
return RocList{
.bytes = utils.allocateWithRefcount(data_bytes, alignment),
.length = length,
.capacity_or_ref_ptr = length,
};
}
pub fn reallocate(
self: RocList,
alignment: u32,
@ -474,6 +491,30 @@ pub fn listReserve(
}
}
pub fn listReleaseExcessCapacity(
list: RocList,
alignment: u32,
element_width: usize,
update_mode: UpdateMode,
) callconv(.C) RocList {
const old_length = list.len();
// We use the direct list.capacity_or_ref_ptr to make sure both that there is no extra capacity and that it isn't a seamless slice.
if ((update_mode == .InPlace or list.isUnique()) and list.capacity_or_ref_ptr == old_length) {
return list;
} else if (old_length == 0) {
list.decref(alignment);
return RocList.empty();
} else {
var output = RocList.allocateExact(alignment, old_length, element_width);
if (list.bytes) |source_ptr| {
const dest_ptr = output.bytes orelse unreachable;
@memcpy(dest_ptr, source_ptr, old_length * element_width);
}
return output;
}
}
pub fn listAppendUnsafe(
list: RocList,
element: Opaque,

View file

@ -56,6 +56,7 @@ comptime {
exportListFn(list.listIsUnique, "is_unique");
exportListFn(list.listCapacity, "capacity");
exportListFn(list.listRefcountPtr, "refcount_ptr");
exportListFn(list.listReleaseExcessCapacity, "release_excess_capacity");
}
// Num Module

View file

@ -1,3 +1,40 @@
## JSON is a data format that is easy for humans to read and write. It is
## commonly used to exhange data between two systems such as a server and a
## client (e.g. web browser).
##
## This module implements functionality to serialise and de-serialise Roc types
## to and from JSON data. Using the `Encode` and `Decode` builtins this process
## can be achieved without the need to write custom encoder and decoder functions
## to parse UTF-8 strings.
##
## Here is a basic example which shows how to parse a JSON record into a Roc
## type named `Language` which includes a `name` field. The JSON string is
## decoded and then the field is encoded back into a UTF-8 string.
##
## ```
## Language : {
## name : Str,
## }
##
## jsonStr = Str.toUtf8 "{\"name\":\"Röc Lang\"}"
##
## result : Result Language _
## result =
## jsonStr
## |> Decode.fromBytes fromUtf8 # returns `Ok {name : "Röc Lang"}`
##
## name =
## decodedValue <- Result.map result
##
## Encode.toBytes decodedValue.name toUtf8
##
## expect name == Ok (Str.toUtf8 "\"Röc Lang\"")
## ```
##
## **Note:** This module is likely to be moved out of the builtins in future.
## It is currently located here to facilitate development of the Abilities
## language feature and testing. You are welcome to use this module, just note
## that it will be moved into a package in a future update.
interface Json
exposes [
Json,
@ -7,6 +44,7 @@ interface Json
imports [
List,
Str,
Result.{ Result },
Encode,
Encode.{
Encoder,
@ -37,10 +75,8 @@ interface Json
Result,
]
## **Note:** This module is likely to be moved out of the builtins in future.
## It is currently located here to facilitate development of the Abilities
## language feature and testing. You are welcome to use this module, just note
## that it will be moved into a package in a future update.
## An opaque type with the `EncoderFormatting` and
## `DecoderFormatting` abilities.
Json := {} has [
EncoderFormatting {
u8: encodeU8,
@ -83,8 +119,10 @@ Json := {} has [
},
]
## Returns a JSON `Decoder`
toUtf8 = @Json {}
## Returns a JSON `Encoder`
fromUtf8 = @Json {}
numToBytes = \n ->
@ -402,12 +440,18 @@ decodeList = \decodeElem -> Decode.custom \bytes, @Json {} ->
when bytes is
['[', ']'] -> { result: Ok [], rest: List.drop bytes 2 }
['[', ..] ->
when decodeElems (eatWhitespace (List.dropFirst bytes)) [] is
Errored e rest -> { result: Err e, rest }
Done vals rest ->
when rest is
[']', ..] -> { result: Ok vals, rest: List.dropFirst rest }
_ -> { result: Err TooShort, rest }
bytesWithoutWhitespace = eatWhitespace (List.dropFirst bytes)
when bytesWithoutWhitespace is
[']', ..] ->
{ result: Ok [], rest: List.dropFirst bytesWithoutWhitespace }
_ ->
when decodeElems bytesWithoutWhitespace [] is
Errored e rest ->
{ result: Err e, rest }
Done vals rest ->
when rest is
[']', ..] -> { result: Ok vals, rest: List.dropFirst rest }
_ -> { result: Err TooShort, rest }
_ ->
{ result: Err TooShort, rest: bytes }
@ -495,6 +539,7 @@ expect
input = Str.toUtf8 " \n\r\tabc"
actual = eatWhitespace input
expected = Str.toUtf8 "abc"
actual == expected
# Test json string decoding with escapes
@ -502,6 +547,7 @@ expect
input = Str.toUtf8 "\"a\r\nbc\\\"xz\""
expected = Ok "a\r\nbc\\\"xz"
actual = Decode.fromBytes input fromUtf8
actual == expected
# Test json string encoding with escapes
@ -509,53 +555,31 @@ expect
input = "a\r\nbc\\\"xz"
expected = Str.toUtf8 "\"a\r\nbc\\\"xz\""
actual = Encode.toBytes input toUtf8
actual == expected
# Test json array decode empty list
expect
input = Str.toUtf8 "[ ]"
expected = []
actual : List U8
actual = Decode.fromBytes input fromUtf8 |> Result.withDefault []
actual : Result (List U8) _
actual = Decode.fromBytes input fromUtf8
expected = Ok []
actual == expected
# Test json array decoding into integers
expect
input = Str.toUtf8 "[ 1,\n2,\t3]"
expected = [1, 2, 3]
actual : List U8
actual = Decode.fromBytes input fromUtf8 |> Result.withDefault []
actual : Result (List U8) _
actual = Decode.fromBytes input fromUtf8
expected = Ok [1, 2, 3]
actual == expected
# Test json array decoding into strings ignoring whitespace around values
expect
input = Str.toUtf8 "[\r\"one\" ,\t\"two\"\n,\n\"3\"\t]"
expected = ["one", "two", "3"]
actual : List Str
actual =
Decode.fromBytes input fromUtf8
|> Result.onErr handleJsonDecodeError
|> Result.withDefault []
actual = Decode.fromBytes input fromUtf8
expected = Ok ["one", "two", "3"]
actual == expected
# Helper for tests to handle Json decoding errors
handleJsonDecodeError = \err ->
when err is
Leftover bytes ->
when Str.fromUtf8 bytes is
Ok bs -> crash "ERROR: bytes left \(bs)"
Err _ ->
ls =
bytes
|> List.map Num.toStr
|> Str.joinWith ","
crash "ERROR: bytes left \(ls)"
TooShort -> crash "ERROR: input too short"

View file

@ -62,6 +62,7 @@ interface List
sortAsc,
sortDesc,
reserve,
releaseExcessCapacity,
walkBackwardsUntil,
countIf,
]
@ -291,6 +292,10 @@ withCapacity : Nat -> List a
## Enlarge the list for at least capacity additional elements
reserve : List a, Nat -> List a
## Shrink the memory footprint of a list such that it's capacity and length are equal.
## Note: This will also convert seamless slices to regular lists.
releaseExcessCapacity : List a -> List a
## Put two lists together.
## ```
## List.concat [1, 2, 3] [4, 5]

View file

@ -352,6 +352,7 @@ pub const LIST_APPEND_UNSAFE: &str = "roc_builtins.list.append_unsafe";
pub const LIST_RESERVE: &str = "roc_builtins.list.reserve";
pub const LIST_CAPACITY: &str = "roc_builtins.list.capacity";
pub const LIST_REFCOUNT_PTR: &str = "roc_builtins.list.refcount_ptr";
pub const LIST_RELEASE_EXCESS_CAPACITY: &str = "roc_builtins.list.release_excess_capacity";
pub const DEC_FROM_STR: &str = "roc_builtins.dec.from_str";
pub const DEC_TO_STR: &str = "roc_builtins.dec.to_str";