diff --git a/implement/Pine.Core/ElmInteractive/ElmValue.cs b/implement/Pine.Core/ElmInteractive/ElmValue.cs
index 2ca33a31..910f6a8b 100644
--- a/implement/Pine.Core/ElmInteractive/ElmValue.cs
+++ b/implement/Pine.Core/ElmInteractive/ElmValue.cs
@@ -21,6 +21,7 @@ type ElmValue
| ElmString String
| ElmTag String (List ElmValue)
| ElmRecord (List ( String, ElmValue ))
+ | ElmBytes Bytes
| ElmFloat BigInt.BigInt BigInt.BigInt
| ElmInternal String
@@ -37,6 +38,8 @@ override public string ToString() =>
public const string ElmRecordTypeTagName = "Elm_Record";
+ public const string ElmBytesTypeTagName = "Elm_Bytes";
+
public const string ElmStringTypeTagName = "String";
public const string ElmSetTypeTagName = "Set_elm_builtin";
@@ -50,6 +53,9 @@ override public string ToString() =>
public static readonly PineValue ElmRecordTypeTagNameAsValue =
PineValueAsString.ValueFromString(ElmRecordTypeTagName);
+ public static readonly PineValue ElmBytesTypeTagNameAsValue =
+ PineValueAsString.ValueFromString(ElmBytesTypeTagName);
+
public static readonly PineValue ElmStringTypeTagNameAsValue =
PineValueAsString.ValueFromString(ElmStringTypeTagName);
@@ -364,6 +370,35 @@ public override string ToString() =>
Fields.FirstOrDefault(field => field.FieldName == fieldName).Value;
}
+ ///
+ /// Elm Bytes type from https://package.elm-lang.org/packages/elm/bytes/latest/
+ ///
+ public record ElmBytes(ReadOnlyMemory Value)
+ : ElmValue
+ {
+ ///
+ /// The number of contained nodes is always zero for the 'Bytes' variant.
+ ///
+ public override int ContainedNodesCount { get; } = 0;
+
+
+ ///
+ public virtual bool Equals(ElmBytes? otherBytes)
+ {
+ if (otherBytes is null)
+ return false;
+
+ if (Value.Length != otherBytes.Value.Length)
+ return false;
+
+ return Value.Span.SequenceEqual(otherBytes.Value.Span);
+ }
+
+ ///
+ public override int GetHashCode() =>
+ Value.GetHashCode();
+ }
+
///
/// The Elm compiler included with Pine models the 'Float' type from Elm as a rational number,
/// expressed as the quotient or fraction โ of two integers, a numerator and a denominator.
@@ -503,6 +538,9 @@ public static (string expressionString, bool needsParens) RenderAsElmExpression(
ElmTag tag =>
ElmTagAsExpression(tag.TagName, tag.Arguments),
+ ElmBytes bytes =>
+ ("<" + bytes.Value.Length + " bytes>", needsParens: false),
+
ElmFloat elmFloat =>
(Convert.ToString(
(double)elmFloat.Numerator / (double)elmFloat.Denominator,
diff --git a/implement/Pine.Core/ElmInteractive/ElmValueEncoding.cs b/implement/Pine.Core/ElmInteractive/ElmValueEncoding.cs
index 32755aa6..1add5dad 100644
--- a/implement/Pine.Core/ElmInteractive/ElmValueEncoding.cs
+++ b/implement/Pine.Core/ElmInteractive/ElmValueEncoding.cs
@@ -143,6 +143,26 @@ public static Result PineListValueAsElmValue(PineValue.ListVal
}
}
+ {
+ // case of Bytes.Bytes
+
+ if (listValue.Elements[0] == ElmValue.ElmBytesTypeTagNameAsValue)
+ {
+ var tagArgumentsValue = listValue.Elements[1];
+
+ if (tagArgumentsValue is not PineValue.ListValue tagArgumentsList)
+ return "Failed to convert value under Bytes tag: Expected a list of tag arguments";
+
+ if (tagArgumentsList.Elements.Count is not 1)
+ return "Failed to convert value under Bytes tag: Expected a list of tag arguments with single item";
+
+ if (tagArgumentsList.Elements[0] is not PineValue.BlobValue blobValue)
+ return "Failed to convert value under Bytes tag: Expected blob value in tag argument";
+
+ return new ElmValue.ElmBytes(blobValue.Bytes);
+ }
+ }
+
{
// Optimize for the case of an Elm Float.
@@ -332,6 +352,7 @@ public static Result PineListValueAsElmValue(PineValue.ListVal
recordFields[i] = (fieldName.Value, fieldValueOk.Value);
continue;
}
+
return "Failed decoding field value: " + fieldValueResult;
}
@@ -450,6 +471,11 @@ [.. elmList.Elements.Select(item => ElmValueAsPineValue(item, reusableEncoding))
ElmRecordAsPineValue(
[.. elmRecord.Fields.Select(field => (field.FieldName, ElmValueAsPineValue(field.Value, reusableEncoding)))]),
+ ElmValue.ElmBytes elmBytes =>
+ PineValue.List(
+ [ElmValue.ElmBytesTypeTagNameAsValue,
+ PineValue.List([PineValue.Blob(elmBytes.Value)])]),
+
ElmValue.ElmFloat elmFloat =>
PineValue.List(
[ElmValue.ElmFloatTypeTagNameAsValue,
diff --git a/implement/Pine.Core/PopularValues.cs b/implement/Pine.Core/PopularValues.cs
index 6b09cf59..586fbc6b 100644
--- a/implement/Pine.Core/PopularValues.cs
+++ b/implement/Pine.Core/PopularValues.cs
@@ -24,6 +24,7 @@ from c2 in Enumerable.Range(0, 128)
private static IEnumerable PopularStringsSource =>
[
"Elm_Record",
+ "Elm_Bytes",
"Basics",
"List",
diff --git a/implement/pine/ElmTime/compile-elm-program/src/ElmCompiler.elm b/implement/pine/ElmTime/compile-elm-program/src/ElmCompiler.elm
index 31f23860..4f6c70c1 100644
--- a/implement/pine/ElmTime/compile-elm-program/src/ElmCompiler.elm
+++ b/implement/pine/ElmTime/compile-elm-program/src/ElmCompiler.elm
@@ -9,6 +9,7 @@ module ElmCompiler exposing
, compilationAndEmitStackFromModulesInCompilation
, compileElmSyntaxExpression
, compileElmSyntaxFunction
+ , elmBytesTypeTagNameAsValue
, elmFloatTypeTagName
, elmRecordTypeTagName
, elmRecordTypeTagNameAsValue
@@ -142,6 +143,16 @@ elmRecordTypeTagNameAsValue =
Pine.valueFromString elmRecordTypeTagName
+elmBytesTypeTagName : String
+elmBytesTypeTagName =
+ "Elm_Bytes"
+
+
+elmBytesTypeTagNameAsValue : Pine.Value
+elmBytesTypeTagNameAsValue =
+ Pine.valueFromString elmBytesTypeTagName
+
+
elmFloatTypeTagName : String
elmFloatTypeTagName =
"Elm_Float"
diff --git a/implement/pine/ElmTime/compile-elm-program/src/ElmInteractive.elm b/implement/pine/ElmTime/compile-elm-program/src/ElmInteractive.elm
index 9cd54c88..fe66b931 100644
--- a/implement/pine/ElmTime/compile-elm-program/src/ElmInteractive.elm
+++ b/implement/pine/ElmTime/compile-elm-program/src/ElmInteractive.elm
@@ -13,6 +13,7 @@ import ElmCompiler
, ElmModuleInCompilation
, ProjectParsedElmFile
, compilationAndEmitStackFromModulesInCompilation
+ , elmBytesTypeTagNameAsValue
, elmFloatTypeTagName
, elmRecordTypeTagName
, elmStringTypeTagName
@@ -58,6 +59,7 @@ type ElmValue
| ElmString String
| ElmTag String (List ElmValue)
| ElmRecord (List ( String, ElmValue ))
+ | ElmBytes (List Int)
| ElmFloat BigInt.BigInt BigInt.BigInt
| ElmInternal String
@@ -185,6 +187,11 @@ renderAsElmExpression elmValue =
, { needsParens = True }
)
+ ElmBytes blob ->
+ ( "<" ++ String.fromInt (List.length blob) ++ " bytes>"
+ , { needsParens = False }
+ )
+
ElmFloat numerator denominator ->
( case elmFloatToFloat numerator denominator of
Nothing ->
@@ -219,10 +226,24 @@ elmValueAsJson elmValue =
Json.Encode.list elmValueAsJson list
ElmRecord fields ->
- Json.Encode.list (\( fieldName, fieldValue ) -> Json.Encode.list identity [ Json.Encode.string fieldName, elmValueAsJson fieldValue ]) fields
+ Json.Encode.list
+ (\( fieldName, fieldValue ) ->
+ Json.Encode.list identity [ Json.Encode.string fieldName, elmValueAsJson fieldValue ]
+ )
+ fields
ElmTag tagName tagArguments ->
- Json.Encode.list identity [ Json.Encode.string tagName, Json.Encode.list elmValueAsJson tagArguments ]
+ Json.Encode.list identity
+ [ Json.Encode.string tagName
+ , Json.Encode.list elmValueAsJson tagArguments
+ ]
+
+ ElmBytes blob ->
+ Json.Encode.object
+ [ ( "Elm_Bytes"
+ , Json.Encode.list Json.Encode.int blob
+ )
+ ]
ElmFloat numerator denominator ->
case elmFloatToFloat numerator denominator of
@@ -291,70 +312,88 @@ pineValueAsElmValue pineValue =
|> Ok
Pine.ListValue list ->
- case list |> List.map pineValueAsElmValue |> Result.Extra.combine of
- Err error ->
- Err ("Failed to combine list: " ++ error)
+ let
+ genericList () =
+ case list |> List.map pineValueAsElmValue |> Result.Extra.combine of
+ Err error ->
+ Err ("Failed to combine list: " ++ error)
- Ok listValues ->
- let
- resultAsList =
- Ok (ElmList listValues)
- in
- if listValues == [] then
- resultAsList
-
- else
- case listValues of
- [ ElmList tagNameChars, ElmList tagArguments ] ->
- case tryMapElmValueToString tagNameChars of
- Just tagName ->
- if stringStartsWithUpper tagName then
- if tagName == elmRecordTypeTagName then
- (case tagArguments of
- [ recordValue ] ->
- elmValueAsElmRecord recordValue
-
- _ ->
- Err ("Wrong number of tag arguments: " ++ String.fromInt (List.length tagArguments))
- )
- |> Result.mapError ((++) "Failed to extract value under record tag: ")
-
- else if tagName == elmStringTypeTagName then
- (case tagArguments of
- [ ElmList charsList ] ->
- charsList
- |> tryMapElmValueToString
- |> Maybe.map (ElmString >> Ok)
- |> Maybe.withDefault (Err "Failed to map chars")
-
- _ ->
- Err
- ("Unexpected shape of tag arguments ("
- ++ String.fromInt (List.length tagArguments)
- ++ "): "
- )
- )
- |> Result.mapError ((++) "Failed to extract value under String tag: ")
-
- else if tagName == elmFloatTypeTagName then
- case tagArguments of
- [ ElmInteger numerator, ElmInteger denominator ] ->
- Ok (ElmFloat numerator denominator)
-
- _ ->
- Err "Unexpected shape under Float tag"
-
- else
- Ok (ElmTag tagName tagArguments)
-
- else
- resultAsList
+ Ok listValues ->
+ let
+ resultAsList =
+ Ok (ElmList listValues)
+ in
+ if listValues == [] then
+ resultAsList
- Nothing ->
+ else
+ case listValues of
+ [ ElmList tagNameChars, ElmList tagArguments ] ->
+ case tryMapElmValueToString tagNameChars of
+ Just tagName ->
+ if stringStartsWithUpper tagName then
+ if tagName == elmRecordTypeTagName then
+ (case tagArguments of
+ [ recordValue ] ->
+ elmValueAsElmRecord recordValue
+
+ _ ->
+ Err ("Wrong number of tag arguments: " ++ String.fromInt (List.length tagArguments))
+ )
+ |> Result.mapError ((++) "Failed to extract value under record tag: ")
+
+ else if tagName == elmStringTypeTagName then
+ (case tagArguments of
+ [ ElmList charsList ] ->
+ charsList
+ |> tryMapElmValueToString
+ |> Maybe.map (ElmString >> Ok)
+ |> Maybe.withDefault (Err "Failed to map chars")
+
+ _ ->
+ Err
+ ("Unexpected shape of tag arguments ("
+ ++ String.fromInt (List.length tagArguments)
+ ++ "): "
+ )
+ )
+ |> Result.mapError ((++) "Failed to extract value under String tag: ")
+
+ else if tagName == elmFloatTypeTagName then
+ case tagArguments of
+ [ ElmInteger numerator, ElmInteger denominator ] ->
+ Ok (ElmFloat numerator denominator)
+
+ _ ->
+ Err "Unexpected shape under Float tag"
+
+ else
+ Ok (ElmTag tagName tagArguments)
+
+ else
+ resultAsList
+
+ Nothing ->
+ resultAsList
+
+ _ ->
resultAsList
+ in
+ case list of
+ [ tagValue, Pine.ListValue tagArguments ] ->
+ if tagValue == elmBytesTypeTagNameAsValue then
+ case tagArguments of
+ [ Pine.BlobValue blob ] ->
+ Ok (ElmBytes blob)
_ ->
- resultAsList
+ genericList ()
+
+ else
+ genericList ()
+
+ _ ->
+ genericList ()
elmValueAsElmRecord : ElmValue -> Result String ElmValue
@@ -613,13 +652,13 @@ json_encode_pineValue dictionary value =
(\entryName entryValue aggregate ->
case entryValue of
Pine.BlobValue blob ->
- if List.length blob < 3 then
- aggregate
+ if List.length blob < 3 then
+ aggregate
- else
- { aggregate
- | blobDict = Dict.insert blob entryName aggregate.blobDict
- }
+ else
+ { aggregate
+ | blobDict = Dict.insert blob entryName aggregate.blobDict
+ }
Pine.ListValue list ->
if list == [] then
@@ -726,78 +765,78 @@ json_encode_pineValue_Internal dictionary value =
jsonEncodeEmptyBlob
_ ->
- if List.length blob < 3 then
- case intFromBlobValueStrict blob of
- Err _ ->
- defaultBlobEncoding ()
+ if List.length blob < 3 then
+ case intFromBlobValueStrict blob of
+ Err _ ->
+ defaultBlobEncoding ()
- Ok asInt ->
- Json.Encode.object
- [ ( "BlobAsInt"
- , Json.Encode.int asInt
- )
- ]
+ Ok asInt ->
+ Json.Encode.object
+ [ ( "BlobAsInt"
+ , Json.Encode.int asInt
+ )
+ ]
- else
- tryFindReference ()
+ else
+ tryFindReference ()
intFromBlobValueStrict : List Int -> Result String Int
intFromBlobValueStrict blobBytes =
- case blobBytes of
- [] ->
- Err "Empty blob does not encode an integer."
+ case blobBytes of
+ [] ->
+ Err "Empty blob does not encode an integer."
- [ _ ] ->
- Err "Blob with only one byte does not encode an integer."
+ [ _ ] ->
+ Err "Blob with only one byte does not encode an integer."
- sign :: absFirst :: following ->
- let
- computeAbsValue () =
- if following == [] then
- Ok absFirst
+ sign :: absFirst :: following ->
+ let
+ computeAbsValue () =
+ if following == [] then
+ Ok absFirst
- else if absFirst == 0 then
- Err "Avoid ambiguous leading zero."
+ else if absFirst == 0 then
+ Err "Avoid ambiguous leading zero."
- else
- case following of
- [ b1 ] ->
- Ok ((absFirst * 256) + b1)
+ else
+ case following of
+ [ b1 ] ->
+ Ok ((absFirst * 256) + b1)
- [ b1, b2 ] ->
- Ok ((absFirst * 65536) + (b1 * 256) + b2)
+ [ b1, b2 ] ->
+ Ok ((absFirst * 65536) + (b1 * 256) + b2)
- [ b1, b2, b3 ] ->
- Ok ((absFirst * 16777216) + (b1 * 65536) + (b2 * 256) + b3)
+ [ b1, b2, b3 ] ->
+ Ok ((absFirst * 16777216) + (b1 * 65536) + (b2 * 256) + b3)
- [ b1, b2, b3, b4 ] ->
- Ok ((absFirst * 4294967296) + (b1 * 16777216) + (b2 * 65536) + (b3 * 256) + b4)
+ [ b1, b2, b3, b4 ] ->
+ Ok ((absFirst * 4294967296) + (b1 * 16777216) + (b2 * 65536) + (b3 * 256) + b4)
- [ b1, b2, b3, b4, b5 ] ->
- Ok ((absFirst * 1099511627776) + (b1 * 4294967296) + (b2 * 16777216) + (b3 * 65536) + (b4 * 256) + b5)
+ [ b1, b2, b3, b4, b5 ] ->
+ Ok ((absFirst * 1099511627776) + (b1 * 4294967296) + (b2 * 16777216) + (b3 * 65536) + (b4 * 256) + b5)
- _ ->
- Err "Failed to map to int - unsupported number of bytes"
- in
- case sign of
- 4 ->
- computeAbsValue ()
+ _ ->
+ Err "Failed to map to int - unsupported number of bytes"
+ in
+ case sign of
+ 4 ->
+ computeAbsValue ()
- 2 ->
- case computeAbsValue () of
- Err err ->
- Err err
+ 2 ->
+ case computeAbsValue () of
+ Err err ->
+ Err err
- Ok absValue ->
- if absValue == 0 then
- Err "Avoid ambiguous negative zero."
+ Ok absValue ->
+ if absValue == 0 then
+ Err "Avoid ambiguous negative zero."
- else
- Ok -absValue
+ else
+ Ok -absValue
- _ ->
- Err ("Unexpected value for sign byte: " ++ String.fromInt sign)
+ _ ->
+ Err ("Unexpected value for sign byte: " ++ String.fromInt sign)
jsonEncodeEmptyList : Json.Encode.Value
diff --git a/implement/pine/ElmTime/compile-elm-program/src/ElmInteractiveKernelModules.elm b/implement/pine/ElmTime/compile-elm-program/src/ElmInteractiveKernelModules.elm
index c98df94f..04a37932 100644
--- a/implement/pine/ElmTime/compile-elm-program/src/ElmInteractiveKernelModules.elm
+++ b/implement/pine/ElmTime/compile-elm-program/src/ElmInteractiveKernelModules.elm
@@ -8,16 +8,19 @@ module Bytes exposing (..)
type Bytes
- = Bytes (List Int)
+ = Elm_Bytes Int
width : Bytes -> Int
width bytes =
case bytes of
- Bytes list -> Pine_kernel.length list
+ Elm_Bytes list ->
+ Pine_kernel.length list
-type Endianness = LE | BE
+type Endianness
+ = LE
+ | BE
"""
, """
@@ -37,10 +40,10 @@ type Encoder
encode : Encoder -> Bytes.Bytes
encode builder =
- Bytes.Bytes (encodeBlob builder)
+ Bytes.Elm_Bytes (encodeBlob builder)
-encodeBlob : Encoder -> List Int
+encodeBlob : Encoder -> Int
encodeBlob builder =
case builder of
U8 n ->
@@ -61,15 +64,20 @@ encodeBlob builder =
Pine_kernel.take [ 4, (Pine_kernel.reverse (Pine_kernel.skip [ 1, n ])) ]
in
if (e == Bytes.LE)
- then littleEndian
- else Pine_kernel.reverse littleEndian
+ then
+ littleEndian
+ else
+ Pine_kernel.reverse littleEndian
SequenceEncoder bs ->
- Pine_kernel.concat (List.map encodeBlob bs)
+ if bs == []
+ then
+ Pine_kernel.take [ 0, 0 ]
+ else
+ Pine_kernel.concat (List.map encodeBlob bs)
- BytesEncoder bs ->
- case bs of
- Bytes.Bytes blob -> blob
+ BytesEncoder (Bytes.Elm_Bytes blob) ->
+ blob
-- INTEGERS
@@ -78,8 +86,8 @@ encodeBlob builder =
{-| Encode integers from `0` to `255` in one byte.
-}
unsignedInt8 : Int -> Encoder
-unsignedInt8 =
- U8
+unsignedInt8 int =
+ U8 int
{-| Encode integers from `0` to `65535` in two bytes.
@@ -97,13 +105,221 @@ unsignedInt32 =
bytes : Bytes.Bytes -> Encoder
-bytes =
- Bytes.Bytes
+bytes bytes =
+ BytesEncoder bytes
sequence : List Encoder -> Encoder
sequence builders =
- SequenceEncoder builders
+ SequenceEncoder builders
+
+
+string : String -> Encoder
+string (String chars)=
+ let
+ blob =
+ encodeCharsAsBlob chars
+ in
+ BytesEncoder (Bytes.Elm_Bytes blob)
+
+
+encodeCharsAsBlob : List Char -> Int
+encodeCharsAsBlob chars =
+ encodeCharsAsBlobRec
+ (Pine_kernel.take [ 0, 0 ])
+ chars
+
+
+encodeCharsAsBlobRec : Int -> List Char -> Int
+encodeCharsAsBlobRec prefix chars =
+ case chars of
+ [] ->
+ prefix
+
+ char :: rest ->
+ let
+ code =
+ Char.toCode char
+
+ charUtf8 =
+ if Pine_kernel.is_sorted_ascending_int [ code, 0x7f ] then
+ -- 1-byte encoding
+ char
+
+ else if Pine_kernel.is_sorted_ascending_int [ code, 0x7ff ] then
+ -- 2-byte encoding
+ let
+ byte1 =
+ 0xC0 + (code // 64)
+
+ byte2 =
+ 0x80 + modBy 64 code
+ in
+ Pine_kernel.concat
+ [ Pine_kernel.take [ 1, Pine_kernel.reverse byte1 ]
+ , Pine_kernel.take [ 1, Pine_kernel.reverse byte2 ]
+ ]
+
+ else if Pine_kernel.is_sorted_ascending_int [ code, 0xffff ] then
+ -- 3-byte encoding
+ let
+ byte1 =
+ 0xE0 + (code // 4096)
+
+ byte2 =
+ 0x80 + modBy 64 (code // 64)
+
+ byte3 =
+ 0x80 + modBy 64 code
+ in
+ Pine_kernel.concat
+ [ Pine_kernel.take [ 1, Pine_kernel.reverse byte1 ]
+ , Pine_kernel.take [ 1, Pine_kernel.reverse byte2 ]
+ , Pine_kernel.take [ 1, Pine_kernel.reverse byte3 ]
+ ]
+
+ else
+ -- 4-byte encoding for code points >= 0x10000
+ let
+ byte1 =
+ 0xF0 + (code // 262144)
+
+ byte2 =
+ 0x80 + modBy 64 (code // 4096)
+
+ byte3 =
+ 0x80 + modBy 64 (code // 64)
+
+ byte4 =
+ 0x80 + modBy 64 code
+ in
+ Pine_kernel.concat
+ [ Pine_kernel.take [ 1, Pine_kernel.reverse byte1 ]
+ , Pine_kernel.take [ 1, Pine_kernel.reverse byte2 ]
+ , Pine_kernel.take [ 1, Pine_kernel.reverse byte3 ]
+ , Pine_kernel.take [ 1, Pine_kernel.reverse byte4 ]
+ ]
+ in
+ encodeCharsAsBlobRec
+ (Pine_kernel.concat [ prefix, charUtf8 ])
+ rest
+
+
+getStringWidth : String -> Int
+getStringWidth (String chars) =
+ Pine_kernel.length
+ (encodeCharsAsBlob chars)
+
+
+"""
+ , """
+module Bytes.Decode exposing (..)
+
+
+import Bytes
+
+
+-- PARSER
+
+
+{-| Describes how to turn a sequence of bytes into a nice Elm value.
+-}
+type Decoder a
+ = Decoder (Bytes -> Int -> ( Int, a ))
+
+
+{-| Turn a sequence of bytes into a nice Elm value.
+
+ -- decode (unsignedInt16 BE) <0007> == Just 7
+ -- decode (unsignedInt16 LE) <0700> == Just 7
+ -- decode (unsignedInt16 BE) <0700> == Just 1792
+ -- decode (unsignedInt32 BE) <0700> == Nothing
+
+
+
+The `Decoder` specifies exactly how this should happen. This process may fail
+if the sequence of bytes is corrupted or unexpected somehow. The examples above
+show a case where there are not enough bytes.
+
+-}
+decode : Decoder a -> Bytes -> Maybe a
+decode (Decoder decoder) bytes =
+ let
+ (Bytes.Elm_Bytes blob) =
+ bytes
+
+ ( offset, result ) =
+ decoder bytes 0
+
+ blobLength =
+ Pine_kernel.length blob
+ in
+ if Pine_kernel.is_sorted_ascending_int [ 0, offset, blobLength ]
+ then
+ Just result
+ else
+ Nothing
+
+
+{-| Decode one byte into an integer from `0` to `255`.
+-}
+unsignedInt8 : Decoder Int
+unsignedInt8 =
+ Decoder
+ (\\(Bytes.Elm_Bytes blob) offset ->
+ let
+ byte =
+ Pine_kernel.take [ 1, Pine_kernel.skip [ offset, blob ] ]
+ in
+ ( Pine_kernel.add_int [ offset, 1 ]
+ , Pine_kernel.concat [ Pine_kernel.take [ 1, 0 ], byte ]
+ )
+ )
+
+
+succeed : a -> Decoder a
+succeed a =
+ Decoder (\\_ offset -> ( offset, a ))
+
+
+map : (a -> b) -> Decoder a -> Decoder b
+map func (Decoder decodeA) =
+ Decoder
+ (\\bites offset ->
+ let
+ ( aOffset, a ) =
+ decodeA bites offset
+ in
+ ( aOffset, func a )
+ )
+
+
+type Step state a
+ = Loop state
+ | Done a
+
+
+loop : state -> (state -> Decoder (Step state a)) -> Decoder a
+loop state callback =
+ Decoder (loopHelp state callback)
+
+
+loopHelp : state -> (state -> Decoder (Step state a)) -> Bytes -> Int -> ( Int, a )
+loopHelp state callback bites offset =
+ let
+ (Decoder decoder) =
+ callback state
+
+ ( newOffset, step ) =
+ decoder bites offset
+ in
+ case step of
+ Loop newState ->
+ loopHelp newState callback bites newOffset
+
+ Done result ->
+ ( newOffset, result )
+
"""
, """
diff --git a/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/context-app/.gitignore b/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/context-app/.gitignore
new file mode 100644
index 00000000..aee98106
--- /dev/null
+++ b/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/context-app/.gitignore
@@ -0,0 +1 @@
+/elm-stuff/
diff --git a/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/context-app/elm.json b/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/context-app/elm.json
new file mode 100644
index 00000000..c175df83
--- /dev/null
+++ b/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/context-app/elm.json
@@ -0,0 +1,18 @@
+{
+ "type": "application",
+ "source-directories": [
+ "src"
+ ],
+ "elm-version": "0.19.1",
+ "dependencies": {
+ "direct": {
+ "elm/bytes": "1.0.8",
+ "elm/core": "1.0.5"
+ },
+ "indirect": {}
+ },
+ "test-dependencies": {
+ "direct": {},
+ "indirect": {}
+ }
+}
diff --git a/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/context-app/src/DecodeExtra.elm b/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/context-app/src/DecodeExtra.elm
new file mode 100644
index 00000000..81012dc3
--- /dev/null
+++ b/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/context-app/src/DecodeExtra.elm
@@ -0,0 +1,25 @@
+module DecodeExtra exposing (..)
+
+import Bytes.Decode
+
+
+list : Int -> Bytes.Decode.Decoder a -> Bytes.Decode.Decoder (List a)
+list length aDecoder =
+ Bytes.Decode.loop
+ ( length, [] )
+ (listStep aDecoder)
+
+
+listStep :
+ Bytes.Decode.Decoder a
+ -> ( Int, List a )
+ -> Bytes.Decode.Decoder (Bytes.Decode.Step ( Int, List a ) (List a))
+listStep elementDecoder ( n, elements ) =
+ if n <= 0 then
+ Bytes.Decode.succeed
+ (Bytes.Decode.Done (List.reverse elements))
+
+ else
+ Bytes.Decode.map
+ (\element -> Bytes.Decode.Loop ( n - 1, element :: elements ))
+ elementDecoder
diff --git a/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/110/expected-value.txt b/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/110/expected-value.txt
new file mode 100644
index 00000000..4020c25f
--- /dev/null
+++ b/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/110/expected-value.txt
@@ -0,0 +1 @@
+<1 bytes>
\ No newline at end of file
diff --git a/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/110/submission.txt b/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/110/submission.txt
new file mode 100644
index 00000000..d884ef68
--- /dev/null
+++ b/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/110/submission.txt
@@ -0,0 +1 @@
+Bytes.Encode.encode (Bytes.Encode.unsignedInt8 17)
\ No newline at end of file
diff --git a/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/210/expected-value.txt b/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/210/expected-value.txt
new file mode 100644
index 00000000..29bdeeba
--- /dev/null
+++ b/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/210/expected-value.txt
@@ -0,0 +1 @@
+<0 bytes>
\ No newline at end of file
diff --git a/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/210/submission.txt b/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/210/submission.txt
new file mode 100644
index 00000000..d490adc4
--- /dev/null
+++ b/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/210/submission.txt
@@ -0,0 +1 @@
+Bytes.Encode.encode (Bytes.Encode.sequence [])
\ No newline at end of file
diff --git a/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/211/expected-value.txt b/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/211/expected-value.txt
new file mode 100644
index 00000000..4020c25f
--- /dev/null
+++ b/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/211/expected-value.txt
@@ -0,0 +1 @@
+<1 bytes>
\ No newline at end of file
diff --git a/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/211/submission.txt b/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/211/submission.txt
new file mode 100644
index 00000000..2167159a
--- /dev/null
+++ b/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/211/submission.txt
@@ -0,0 +1 @@
+Bytes.Encode.encode (Bytes.Encode.sequence [ Bytes.Encode.unsignedInt8 17 ])
\ No newline at end of file
diff --git a/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/212/expected-value.txt b/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/212/expected-value.txt
new file mode 100644
index 00000000..498204f1
--- /dev/null
+++ b/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/212/expected-value.txt
@@ -0,0 +1 @@
+<2 bytes>
\ No newline at end of file
diff --git a/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/212/submission.txt b/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/212/submission.txt
new file mode 100644
index 00000000..878d4182
--- /dev/null
+++ b/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/212/submission.txt
@@ -0,0 +1 @@
+Bytes.Encode.encode (Bytes.Encode.sequence [ Bytes.Encode.unsignedInt8 0, Bytes.Encode.unsignedInt8 17 ])
\ No newline at end of file
diff --git a/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/310/expected-value.txt b/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/310/expected-value.txt
new file mode 100644
index 00000000..29bdeeba
--- /dev/null
+++ b/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/310/expected-value.txt
@@ -0,0 +1 @@
+<0 bytes>
\ No newline at end of file
diff --git a/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/310/submission.txt b/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/310/submission.txt
new file mode 100644
index 00000000..12f5bd36
--- /dev/null
+++ b/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/310/submission.txt
@@ -0,0 +1 @@
+Bytes.Encode.encode (Bytes.Encode.string "")
\ No newline at end of file
diff --git a/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/311/expected-value.txt b/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/311/expected-value.txt
new file mode 100644
index 00000000..4a0eaef0
--- /dev/null
+++ b/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/311/expected-value.txt
@@ -0,0 +1 @@
+<11 bytes>
\ No newline at end of file
diff --git a/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/311/submission.txt b/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/311/submission.txt
new file mode 100644
index 00000000..daa4ba2d
--- /dev/null
+++ b/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/311/submission.txt
@@ -0,0 +1 @@
+Bytes.Encode.encode (Bytes.Encode.string "Hello World")
\ No newline at end of file
diff --git a/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/334/expected-value.txt b/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/334/expected-value.txt
new file mode 100644
index 00000000..c10e6afe
--- /dev/null
+++ b/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/334/expected-value.txt
@@ -0,0 +1 @@
+<4 bytes>
\ No newline at end of file
diff --git a/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/334/submission.txt b/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/334/submission.txt
new file mode 100644
index 00000000..695ccc00
--- /dev/null
+++ b/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/334/submission.txt
@@ -0,0 +1 @@
+Bytes.Encode.encode (Bytes.Encode.string "๐ฒ")
\ No newline at end of file
diff --git a/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/340/expected-value.txt b/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/340/expected-value.txt
new file mode 100644
index 00000000..c2270834
--- /dev/null
+++ b/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/340/expected-value.txt
@@ -0,0 +1 @@
+0
\ No newline at end of file
diff --git a/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/340/submission.txt b/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/340/submission.txt
new file mode 100644
index 00000000..98dcbb84
--- /dev/null
+++ b/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/340/submission.txt
@@ -0,0 +1 @@
+Bytes.Encode.getStringWidth ""
\ No newline at end of file
diff --git a/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/344/expected-value.txt b/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/344/expected-value.txt
new file mode 100644
index 00000000..bf0d87ab
--- /dev/null
+++ b/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/344/expected-value.txt
@@ -0,0 +1 @@
+4
\ No newline at end of file
diff --git a/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/344/submission.txt b/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/344/submission.txt
new file mode 100644
index 00000000..d6b551c9
--- /dev/null
+++ b/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/344/submission.txt
@@ -0,0 +1 @@
+Bytes.Encode.getStringWidth "๐ฒ"
\ No newline at end of file
diff --git a/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/411/submission.txt b/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/411/submission.txt
new file mode 100644
index 00000000..8ac8e9d6
--- /dev/null
+++ b/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/411/submission.txt
@@ -0,0 +1 @@
+encodedBytes = Bytes.Encode.encode (Bytes.Encode.string "test")
\ No newline at end of file
diff --git a/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/412/expected-value.txt b/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/412/expected-value.txt
new file mode 100644
index 00000000..667cddf1
--- /dev/null
+++ b/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/412/expected-value.txt
@@ -0,0 +1 @@
+Just [116,101,115,116]
\ No newline at end of file
diff --git a/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/412/submission.txt b/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/412/submission.txt
new file mode 100644
index 00000000..d8e9b3fb
--- /dev/null
+++ b/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/412/submission.txt
@@ -0,0 +1 @@
+Bytes.Decode.decode (DecodeExtra.list 4 Bytes.Decode.unsignedInt8) encodedBytes
\ No newline at end of file
diff --git a/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/415/submission.txt b/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/415/submission.txt
new file mode 100644
index 00000000..9349c604
--- /dev/null
+++ b/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/415/submission.txt
@@ -0,0 +1 @@
+encodedBytes = Bytes.Encode.encode (Bytes.Encode.string "๐ฒ")
\ No newline at end of file
diff --git a/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/416/expected-value.txt b/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/416/expected-value.txt
new file mode 100644
index 00000000..97dc219c
--- /dev/null
+++ b/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/416/expected-value.txt
@@ -0,0 +1 @@
+Just [240,159,140,178]
\ No newline at end of file
diff --git a/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/416/submission.txt b/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/416/submission.txt
new file mode 100644
index 00000000..d8e9b3fb
--- /dev/null
+++ b/implement/test-and-train/elm-interactive-scenarios-kernel/elm-bytes/steps/416/submission.txt
@@ -0,0 +1 @@
+Bytes.Decode.decode (DecodeExtra.list 4 Bytes.Decode.unsignedInt8) encodedBytes
\ No newline at end of file
diff --git a/implement/test-elm-time/ElmValueTests.cs b/implement/test-elm-time/ElmValueTests.cs
index 022c1263..b69ff146 100644
--- a/implement/test-elm-time/ElmValueTests.cs
+++ b/implement/test-elm-time/ElmValueTests.cs
@@ -40,6 +40,11 @@ public void Elm_value_encoding_roundtrips()
ElmValue.Integer(47)]),
]),
+ new ElmValue.ElmBytes(System.ReadOnlyMemory.Empty),
+ new ElmValue.ElmBytes((byte[])[0]),
+ new ElmValue.ElmBytes((byte[])[11, 13, 17]),
+ new ElmValue.ElmBytes((byte[])[0, 13, 17]),
+
ElmValue.ElmFloat.Convert(0),
ElmValue.ElmFloat.Convert(0.3),
ElmValue.ElmFloat.Convert(1.7),
@@ -94,6 +99,18 @@ public void Elm_value_display_as_expression()
(ElmValue.TagInstance("Just", [ElmValue.TagInstance("Just", [ElmValue.Integer(47)])]),
"Just (Just 47)"),
+ (new ElmValue.ElmBytes(System.ReadOnlyMemory.Empty),
+ "<0 bytes>"),
+
+ (new ElmValue.ElmBytes((byte[])[0]),
+ "<1 bytes>"),
+
+ (new ElmValue.ElmBytes((byte[])[11, 13, 17]),
+ "<3 bytes>"),
+
+ (new ElmValue.ElmBytes((byte[])[0, 13, 17]),
+ "<3 bytes>"),
+
(ElmValue.ElmFloat.Convert(0),
"0"),