roc/crates/compiler/builtins/roc/Decode.roc
Agus Zubiaga 710d62f754
Load and can new top-level imports
Previously, all imports were available in the header, so we could start
processing dependencies as soon as we parsed it. However, the new imports
are treated as defs, so we have to parse the whole module to find them.

This commit essentially moves the dependency resolution from the `LoadHeader`
phase to the `Parse` phase, and it updates canonicalization to introduce
module symbols into scope when a `ValueDef::ModuleImport` is encountered.

NOTE:
- The `imports` header still parses, but it's no longer wired up. I will remove
it in an upcoming commit.
- Ingested files and imports that appear in nested expressions are not
yet supported by load
2024-01-20 08:39:31 -03:00

171 lines
6 KiB
Text

interface Decode
exposes [
DecodeError,
DecodeResult,
Decoder,
Decoding,
DecoderFormatting,
decoder,
u8,
u16,
u32,
u64,
u128,
i8,
i16,
i32,
i64,
i128,
f32,
f64,
dec,
bool,
string,
list,
record,
tuple,
custom,
decodeWith,
fromBytesPartial,
fromBytes,
mapResult,
]
imports []
import List
import Result exposing [Result]
import Num exposing [
U8,
U16,
U32,
U64,
U128,
I8,
I16,
I32,
I64,
I128,
Nat,
F32,
F64,
Dec,
]
import Bool exposing [Bool]
## Error types when decoding a `List U8` of utf-8 bytes using a [Decoder]
DecodeError : [TooShort]
## Return type of a [Decoder].
##
## This can be useful when creating a [custom](#custom) decoder or when
## using [fromBytesPartial](#fromBytesPartial). For example writing unit tests,
## such as;
## ```
## expect
## input = "\"hello\", " |> Str.toUtf8
## actual = Decode.fromBytesPartial input Json.json
## expected = Ok "hello"
##
## actual.result == expected
## ```
DecodeResult val : { result : Result val DecodeError, rest : List U8 }
## Decodes a `List U8` of utf-8 bytes where `val` is the type of the decoded
## value, and `fmt` is a [Decoder] which implements the [DecoderFormatting]
## ability
Decoder val fmt := List U8, fmt -> DecodeResult val where fmt implements DecoderFormatting
## Definition of the [Decoding] ability
Decoding implements
decoder : Decoder val fmt where val implements Decoding, fmt implements DecoderFormatting
## Definition of the [DecoderFormatting] ability
DecoderFormatting implements
u8 : Decoder U8 fmt where fmt implements DecoderFormatting
u16 : Decoder U16 fmt where fmt implements DecoderFormatting
u32 : Decoder U32 fmt where fmt implements DecoderFormatting
u64 : Decoder U64 fmt where fmt implements DecoderFormatting
u128 : Decoder U128 fmt where fmt implements DecoderFormatting
i8 : Decoder I8 fmt where fmt implements DecoderFormatting
i16 : Decoder I16 fmt where fmt implements DecoderFormatting
i32 : Decoder I32 fmt where fmt implements DecoderFormatting
i64 : Decoder I64 fmt where fmt implements DecoderFormatting
i128 : Decoder I128 fmt where fmt implements DecoderFormatting
f32 : Decoder F32 fmt where fmt implements DecoderFormatting
f64 : Decoder F64 fmt where fmt implements DecoderFormatting
dec : Decoder Dec fmt where fmt implements DecoderFormatting
bool : Decoder Bool fmt where fmt implements DecoderFormatting
string : Decoder Str fmt where fmt implements DecoderFormatting
list : Decoder elem fmt -> Decoder (List elem) fmt where fmt implements DecoderFormatting
## `record state stepField finalizer` decodes a record field-by-field.
##
## `stepField` returns a decoder for the given field in the record, or
## `Skip` if the field is not a part of the decoded record.
##
## `finalizer` should produce the record value from the decoded `state`.
record : state, (state, Str -> [Keep (Decoder state fmt), Skip]), (state -> Result val DecodeError) -> Decoder val fmt where fmt implements DecoderFormatting
## `tuple state stepElem finalizer` decodes a tuple element-by-element.
##
## `stepElem` returns a decoder for the nth index in the tuple, or
## `TooLong` if the index is larger than the expected size of the tuple. The
## index passed to `stepElem` is 0-indexed.
##
## `finalizer` should produce the tuple value from the decoded `state`.
tuple : state, (state, Nat -> [Next (Decoder state fmt), TooLong]), (state -> Result val DecodeError) -> Decoder val fmt where fmt implements DecoderFormatting
## Build a custom [Decoder] function. For example the implementation of
## `decodeBool` could be defined as follows;
##
## ```
## decodeBool = Decode.custom \bytes, @Json {} ->
## when bytes is
## ['f', 'a', 'l', 's', 'e', ..] -> { result: Ok Bool.false, rest: List.dropFirst bytes 5 }
## ['t', 'r', 'u', 'e', ..] -> { result: Ok Bool.true, rest: List.dropFirst bytes 4 }
## _ -> { result: Err TooShort, rest: bytes }
## ```
custom : (List U8, fmt -> DecodeResult val) -> Decoder val fmt where fmt implements DecoderFormatting
custom = \decode -> @Decoder decode
## Decode a `List U8` utf-8 bytes using a specific [Decoder] function
decodeWith : List U8, Decoder val fmt, fmt -> DecodeResult val where fmt implements DecoderFormatting
decodeWith = \bytes, @Decoder decode, fmt -> decode bytes fmt
## Decode a `List U8` utf-8 bytes and return a [DecodeResult](#DecodeResult)
## ```
## expect
## input = "\"hello\", " |> Str.toUtf8
## actual = Decode.fromBytesPartial input Json.json
## expected = Ok "hello"
##
## actual.result == expected
## ```
fromBytesPartial : List U8, fmt -> DecodeResult val where val implements Decoding, fmt implements DecoderFormatting
fromBytesPartial = \bytes, fmt -> decodeWith bytes decoder fmt
## Decode a `List U8` utf-8 bytes and return a [Result] with no leftover bytes
## expected. If successful returns `Ok val`, however, if there are bytes
## remaining returns `Err Leftover (List U8)`.
## ```
## expect
## input = "\"hello\", " |> Str.toUtf8
## actual = Decode.fromBytes input Json.json
## expected = Ok "hello"
##
## actual == expected
## ```
fromBytes : List U8, fmt -> Result val [Leftover (List U8)]DecodeError where val implements Decoding, fmt implements DecoderFormatting
fromBytes = \bytes, fmt ->
when fromBytesPartial bytes fmt is
{ result, rest } ->
if List.isEmpty rest then
when result is
Ok val -> Ok val
Err TooShort -> Err TooShort
else
Err (Leftover rest)
## Transform the `val` of a [DecodeResult]
mapResult : DecodeResult a, (a -> b) -> DecodeResult b
mapResult = \{ result, rest }, mapper -> { result: Result.map result mapper, rest }