#byteparser
Parser combinators over Bytes.
A ByteParser a reads a Bytes value from a position and produces a
value or a positioned error. The single-byte primitives hand out a U8;
every element of Bytes is a byte by construction, so there is nothing
for a single-byte read to reject. Build a parser from the
primitives (byte, satisfy, takeBytes, the integer and float
readers) and the combinators (many, orElse, choice, between),
sequence parsers with defer notation, and run the result with
runByteParser, or runByteParserWithin to parse a sub-range
[start, end) of a larger Bytes value without copying it.
Parsers backtrack: a failed parser never advances the position, and
orElse p q runs q from the position where p started. The integer
readers name their byte order and width, as in beUint 4 for a four-byte
big-endian unsigned integer and leSint 2 for a two-byte little-endian
signed one. An Int reader fails rather than wrap when the value does not
fit Int, which only an eight-byte value can do. beU16, beU32,
beU64, leU16, leU32 and leU64 read a fixed-width unsigned value as
its own type, U64 included. bytebuilder's emit functions write the same
encodings.
#Results and parsers
#BResult
data BResult a
= BOk a Int
| BErr String IntThe outcome of running a parser from a position.
BOk carries the value and the position just past the bytes consumed.
BErr carries a message and the position where parsing failed. Match on
these directly when a decoder needs position-level control beyond what
the combinators give.
Instances: Mappable
#ByteParserE
data ByteParserE (e : Effect) a
= ByteParserE (Bytes -> Int -> Int -> <e> BResult a)A parser indexed by the effect row e its steps may perform.
The wrapped function takes the input, a start position and an exclusive
end position, and returns a BResult. ByteParser fixes e to the empty
row, and every parser in this module has that type.
Instances: DeferredMappable, DeferredApplicative, DeferredThenable
#ByteParser
type ByteParser a = ByteParserE <> aA parser whose steps perform no effects. Every parser this module exports has this type.
#runBP
runBP : ByteParserE e a -> Bytes -> Int -> Int -> <e> BResult a
runBP _ input pos endRuns p on input from position pos, bounded to [pos, end), and
returns the raw BResult.
A primitive that delegates to another parser (rather than composing with
the combinators above) must call runBP with the SAME end it was
itself handed, never length input — otherwise it escapes a bound set by
runByteParserWithin. runByteParser/runByteParserWithin are the forms
that supply end themselves and return a Result.
> runByteParserWithin 0 1 (ByteParserE (input pos end => runBP (takeBytes 2) input pos end)) (fromU8Array [|1, 2, 3|])
Err "unexpected end of input at byte 1"#onOk
onOk : BResult a -> (a -> Int -> <e> BResult b) -> <e> BResult b
onOk _ kContinues from a successful result.
Applies k to the value and position of a BOk, and passes a BErr
through unchanged.
#Alternatives
#noMatch
noMatch : ByteParserE e aA parser that always fails, consuming nothing.
#orElse
orElse : ByteParserE e a -> ByteParserE e a -> ByteParserE e a
orElse p qTries p, and when it fails, runs q from the same starting position.
> runByteParser (orElse (byte 1) (byte 2)) (fromU8Array [|2|])
Ok 2#Primitives
#failWith
failWith : String -> ByteParser a
failWith msgA parser that always fails with msg, consuming nothing.
#satisfy
satisfy : (U8 -> Bool) -> ByteParser U8
satisfy predOne byte that satisfies pred.
> runByteParser (satisfy (b => b == 65)) (fromU8Array [|65, 66, 67|])
Ok 65
> runByteParser (satisfy (b => b == 65)) (fromU8Array [|99|])
Err "unexpected byte at byte 0"#anyByte
anyByte : ByteParser U8Any one byte.
> runByteParser anyByte (fromU8Array [|42|])
Ok 42#byte
byte : U8 -> ByteParser U8
byte bExactly the byte b.
> runByteParser (byte 0xFF) (fromU8Array [|255, 0|])
Ok 255
> runByteParser (byte 0x00) (fromU8Array [|1|])
Err "unexpected byte at byte 0"#eof
eof : ByteParser UnitSucceeds at the end of the input, consuming nothing.
> runByteParser eof (fromU8Array [||])
Ok ()
> runByteParser eof (fromU8Array [|1|])
Err "expected end of input at byte 0"#peek
peek : ByteParser U8The byte at the current position, without consuming it. Fails at the end of the input.
#Combinators
#many
many : ByteParser a -> ByteParser (List a)
many pZero or more p, until it fails.
Also stops when p succeeds without consuming anything, so many of
such a parser terminates.
> runByteParser (many (byte 1)) (fromU8Array [|1, 1, 1, 2|])
Ok [1, 1, 1]#some
some : ByteParser a -> ByteParser (List a)
some pOne or more p.
> runByteParser (some (byte 2)) (fromU8Array [|2, 2, 3|])
Ok [2, 2]
> runByteParser (some (byte 2)) (fromU8Array [|3|])
Err "unexpected byte at byte 0"#sepBy1
sepBy1 : ByteParser a -> ByteParser b -> ByteParser (List a)
sepBy1 p sepOne or more p, separated by sep.
#sepBy
sepBy : ByteParser a -> ByteParser b -> ByteParser (List a)
sepBy p sepZero or more p, separated by sep.
#optional
optional : ByteParser a -> ByteParser (Option a)
optional pSome the result of p, or None when p fails, consuming nothing.
> runByteParser (optional (byte 5)) (fromU8Array [|5|])
Ok (Some 5)
> runByteParser (optional (byte 5)) (fromU8Array [|9|])
Ok None#between
between : ByteParser open -> ByteParser close -> ByteParser a -> ByteParser a
between open close pThe result of p parsed between open and close.
#choice
choice : List (ByteParser a) -> ByteParser aThe result of the first parser in the list that succeeds. Fails when the list is empty or every parser fails.
#chainl1
chainl1 : ByteParser a -> ByteParser (a -> a -> a) -> ByteParser a
chainl1 p opOne or more p separated by op, combined from the left.
op yields a binary function, and each one is applied to the value so
far and the next p.
#takeBytes
takeBytes : Int -> ByteParser Bytes
takeBytes nExactly n bytes, as a Bytes.
Fails when fewer than n bytes remain, even when n is far larger than
any real input could hold — the bound check never overflows.
> runByteParser (takeBytes 3) (fromU8Array [|10, 20, 30, 40|])
Ok (Bytes "0a141e")
> runByteParser (deferThen anyByte (_ => takeBytes 4611686018427387903)) (fromU8Array [|10, 20, 30, 40|])
Err "unexpected end of input at byte 4"#Integers and floats
#beUint
beUint : Int -> ByteParser Int
beUint nAn unsigned integer of n bytes, most significant byte first.
Fails when fewer than n bytes remain, and when the value is larger than
Int holds, which needs eight bytes or more; beU64 reads any eight.
> runByteParser (beUint 2) (fromU8Array [|1, 2|])
Ok 258
> runByteParser (beUint 1) (fromU8Array [|255|])
Ok 255
> runByteParser (beUint 4) (fromU8Array [|0, 0, 1, 0|])
Ok 256
> runByteParser (beUint 8) (fromU8Array [|64, 0, 0, 0, 0, 0, 0, 0|])
Err "integer does not fit Int (read a U64 with beU64 or leU64) at byte 7"#beSint
beSint : Int -> ByteParser Int
beSint nA signed two's-complement integer of n bytes, most significant byte
first.
> runByteParser (beSint 1) (fromU8Array [|255|])
Ok (-1)
> runByteParser (beSint 1) (fromU8Array [|127|])
Ok 127
> runByteParser (beSint 2) (fromU8Array [|255, 255|])
Ok (-1)
> runByteParser (beSint 2) (fromU8Array [|0, 1|])
Ok 1
> runByteParser (beSint 9) (fromU8Array [|255, 255, 255, 255, 255, 255, 255, 255, 254|])
Ok (-2)#beFloat64
beFloat64 : ByteParser FloatA 64-bit IEEE 754 float from eight bytes, most significant byte first.
> runByteParser beFloat64 (fromU8Array [|63, 248, 0, 0, 0, 0, 0, 0|])
Ok 1.5
> runByteParser beFloat64 (fromU8Array [|192, 0, 0, 0, 0, 0, 0, 0|])
Ok (-2.0)#leUint
leUint : Int -> ByteParser Int
leUint nAn unsigned integer of n bytes, least significant byte first.
Fails when fewer than n bytes remain, and when the value is larger than
Int holds, which needs eight bytes or more; leU64 reads any eight.
> runByteParser (leUint 2) (fromU8Array [|2, 1|])
Ok 258
> runByteParser (leUint 1) (fromU8Array [|255|])
Ok 255
> runByteParser (leUint 4) (fromU8Array [|0, 1, 0, 0|])
Ok 256#leSint
leSint : Int -> ByteParser Int
leSint nA signed two's-complement integer of n bytes, least significant byte
first.
> runByteParser (leSint 1) (fromU8Array [|255|])
Ok (-1)
> runByteParser (leSint 1) (fromU8Array [|127|])
Ok 127
> runByteParser (leSint 2) (fromU8Array [|255, 255|])
Ok (-1)
> runByteParser (leSint 2) (fromU8Array [|1, 0|])
Ok 1#leFloat64
leFloat64 : ByteParser FloatA 64-bit IEEE 754 float from eight bytes, least significant byte first.
> runByteParser leFloat64 (fromU8Array [|0, 0, 0, 0, 0, 0, 248, 63|])
Ok 1.5
> runByteParser leFloat64 (fromU8Array [|0, 0, 0, 0, 0, 0, 0, 192|])
Ok (-2.0)#Fixed-width unsigned readers
#beU16
beU16 : ByteParser U16A U16 from two bytes, most significant byte first. The inverse of
bytebuilder.emitU16BE.
> runByteParser beU16 (fromU8Array [|1, 2|])
Ok 258#beU32
beU32 : ByteParser U32A U32 from four bytes, most significant byte first. The inverse of
bytebuilder.emitU32BE.
> runByteParser beU32 (fromU8Array [|255, 255, 255, 255|])
Ok 4294967295#beU64
beU64 : ByteParser U64A U64 from eight bytes, most significant byte first. The inverse of
bytebuilder.emitU64BE. Every eight-byte value fits, so this never
fails for want of range.
> runByteParser beU64 (fromU8Array [|255, 255, 255, 255, 255, 255, 255, 255|])
Ok 18446744073709551615#leU16
leU16 : ByteParser U16A U16 from two bytes, least significant byte first. The inverse of
bytebuilder.emitU16LE.
> runByteParser leU16 (fromU8Array [|2, 1|])
Ok 258#leU32
leU32 : ByteParser U32A U32 from four bytes, least significant byte first. The inverse of
bytebuilder.emitU32LE.
> runByteParser leU32 (fromU8Array [|4, 3, 2, 1|])
Ok 16909060#leU64
leU64 : ByteParser U64A U64 from eight bytes, least significant byte first. The inverse of
bytebuilder.emitU64LE.
> runByteParser leU64 (fromU8Array [|21, 124, 74, 127, 185, 121, 55, 158|])
Ok 11400714819323198485#Running a parser
#runByteParser
runByteParser : ByteParser a -> Bytes -> Result String a
runByteParser p bytesThe result of running p on bytes from position 0.
Err carries the failure message and the byte position where it
happened. Bytes left over after p succeeds are not an error; sequence
p with eof to require that the whole input is consumed.
> runByteParser (byte 42) (fromU8Array [|42|])
Ok 42
> runByteParser (byte 42) (fromU8Array [|7|])
Err "unexpected byte at byte 0"#runByteParserWithin
runByteParserWithin : Int -> Int -> ByteParser a -> Bytes -> Result String a
runByteParserWithin start end p bytesThe result of running p on the half-open sub-range [start, end) of
bytes, without copying it.
Behaves as running p on bytes.[start..end] would, but every length
check inside p sees end rather than bytes' own length, so a parser
that reads to the end of its input stops at end, not at the end of the
larger bytes value it was carved from. A position in a returned Err
is always an absolute offset into bytes itself, never relative to
start. start < 0, end < start or end > length bytes is a
malformed range and fails without running p at all, rather than
parsing an empty or truncated input.
> runByteParserWithin 1 3 (many anyByte) (fromU8Array [|10, 20, 30, 40|])
Ok [20, 30]
> runByteParserWithin 0 1 beU16 (fromU8Array [|1, 2|])
Err "unexpected end of input at byte 1"
> runByteParserWithin 2 1 anyByte (fromU8Array [|1, 2, 3|])
Err "invalid range [2, 1) for input of length 3"