-module(bitty). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/bitty.gleam"). -export([make_parser/1, run_parser/2, success/1, fail/1, from_result/1, map/2, replace/2, then/2, one_of/1, attempt/1, cut/1, label/2, context/2, many_until/2, fold/3, many/1, many1/1, fold1/3, repeat/2, length_repeat/2, preceded/2, terminated/2, delimited/3, pair/2, separated_pair/3, separated1/2, separated/2, optional/1, verify/2, 'not'/1, 'cond'/2, location/0, align/0, 'end'/0, advance_bits/3, read_uint/4, stop_expected/2, read_n_bytes/2, length_take/1, require_aligned/2, within_bytes/2, within_bytes_partial/2, capture/1, length_within/2, run/2, run_partial/2, run_with_location/2]). -export_type([location/0, bitty_error/0, parser/1, state/0, step/1]). -if(?OTP_RELEASE >= 27). -define(MODULEDOC(Str), -moduledoc(Str)). -define(DOC(Str), -doc(Str)). -else. -define(MODULEDOC(Str), -compile([])). -define(DOC(Str), -compile([])). -endif. ?MODULEDOC( " Zero-copy binary parser combinators for Gleam, targeting both BEAM and\n" " JavaScript. Build parsers by composing primitives from `bitty/bytes`,\n" " `bitty/bits`, and `bitty/num` with the combinators in this module.\n" " Parsers are designed for Gleam's `use` syntax.\n" ). -type location() :: {location, integer(), integer()}. -type bitty_error() :: {bitty_error, location(), list(binary()), list(binary()), gleam@option:option(binary())}. -opaque parser(OZP) :: {parser, fun((state()) -> step(OZP))}. -type state() :: {state, bitstring(), integer(), integer(), boolean()}. -type step(OZQ) :: {continue, OZQ, state(), boolean()} | {stop, bitty_error(), boolean(), boolean()}. -file("src/bitty.gleam", 51). ?DOC(false). -spec make_parser(fun((state()) -> step(OZR))) -> parser(OZR). make_parser(F) -> {parser, F}. -file("src/bitty.gleam", 56). ?DOC(false). -spec run_parser(parser(OZU), state()) -> step(OZU). run_parser(Parser, State) -> (erlang:element(2, Parser))(State). -file("src/bitty.gleam", 62). ?DOC( " Create a parser that always succeeds with the given value without\n" " consuming any input.\n" ). -spec success(OZX) -> parser(OZX). success(Value) -> {parser, fun(State) -> {continue, Value, State, false} end}. -file("src/bitty.gleam", 68). ?DOC( " Create a parser that always fails with the given message without\n" " consuming any input.\n" ). -spec fail(binary()) -> parser(any()). fail(Message) -> {parser, fun(State) -> {stop, {bitty_error, {location, erlang:element(3, State), erlang:element(4, State)}, [], [], {some, Message}}, false, erlang:element(5, State)} end}. -file("src/bitty.gleam", 87). ?DOC( " Convert a `Result(a, e)` into a parser: `Ok(value)` succeeds with the\n" " value, `Error(e)` fails with the inspected error as the message.\n" " Useful for lifting fallible conversions (e.g. `bit_array.to_string`)\n" " into a parser pipeline with `use` syntax.\n" ). -spec from_result({ok, PAB} | {error, any()}) -> parser(PAB). from_result(Result) -> case Result of {ok, Value} -> success(Value); {error, E} -> fail(gleam@string:inspect(E)) end. -file("src/bitty.gleam", 158). ?DOC(" Transform the result of a parser by applying `f` to the parsed value.\n"). -spec map(parser(PAW), fun((PAW) -> PAY)) -> parser(PAY). map(Parser, F) -> {parser, fun(State) -> case (erlang:element(2, Parser))(State) of {continue, Value, State1, Consumed} -> {continue, F(Value), State1, Consumed}; {stop, Error, Consumed@1, Committed} -> {stop, Error, Consumed@1, Committed} end end}. -file("src/bitty.gleam", 174). ?DOC( " Replace the result of a parser with a fixed value, discarding the\n" " original result. Useful with `tag` patterns.\n" "\n" " ```gleam\n" " let parser = bytes.tag(<<0x01>>) |> bitty.replace(with: \"one\")\n" " let assert Ok(\"one\") = bitty.run(parser, on: <<0x01>>)\n" " ```\n" ). -spec replace(parser(any()), PBC) -> parser(PBC). replace(Parser, Value) -> _pipe = Parser, map(_pipe, fun(_) -> Value end). -file("src/bitty.gleam", 186). ?DOC( " Sequence two parsers: run `parser`, then pass its result to `next` to\n" " get the second parser. Designed for Gleam's `use` syntax:\n" "\n" " ```gleam\n" " use length <- bitty.then(num.u8())\n" " use data <- bitty.then(bytes.take(length))\n" " bitty.success(data)\n" " ```\n" ). -spec then(parser(PBE), fun((PBE) -> parser(PBG))) -> parser(PBG). then(Parser, Next) -> {parser, fun(State) -> case (erlang:element(2, Parser))(State) of {continue, Value, State1, Consumed1} -> case (erlang:element(2, (Next(Value))))(State1) of {continue, Value2, State2, Consumed2} -> {continue, Value2, State2, Consumed1 orelse Consumed2}; {stop, Error, Consumed2@1, Committed2} -> {stop, Error, Consumed1 orelse Consumed2@1, Committed2 orelse erlang:element(5, State1)} end; {stop, Error@1, Consumed, Committed} -> {stop, Error@1, Consumed, Committed} end end}. -file("src/bitty.gleam", 247). -spec merge_errors(bitty_error(), bitty_error()) -> bitty_error(). merge_errors(E1, E2) -> Loc1 = (erlang:element(2, erlang:element(2, E1)) * 8) + erlang:element( 3, erlang:element(2, E1) ), Loc2 = (erlang:element(2, erlang:element(2, E2)) * 8) + erlang:element( 3, erlang:element(2, E2) ), case gleam@int:compare(Loc1, Loc2) of gt -> E1; lt -> E2; eq -> {bitty_error, erlang:element(2, E1), lists:append(erlang:element(3, E1), erlang:element(3, E2)), erlang:element(4, E1), erlang:element(5, E1)} end. -file("src/bitty.gleam", 228). -spec try_parsers(list(parser(PBN)), state(), bitty_error()) -> step(PBN). try_parsers(Parsers, State, Last_error) -> case Parsers of [] -> {stop, Last_error, false, false}; [Parser | Rest] -> case (erlang:element(2, Parser))(State) of {continue, _, _, _} = Success -> Success; {stop, Error, Consumed, Committed} -> case Consumed orelse Committed of true -> {stop, Error, Consumed, Committed}; false -> try_parsers( Rest, State, merge_errors(Last_error, Error) ) end end end. -file("src/bitty.gleam", 213). ?DOC( " Try each parser in order, returning the first success. A parser that\n" " consumes input before failing will **not** backtrack — wrap alternatives\n" " in `attempt` if backtracking is needed. Errors from non-consuming\n" " failures at the same position are merged.\n" "\n" " ```gleam\n" " let parser = bitty.one_of([\n" " bitty.attempt(bytes.tag(<<0x01>>) |> bitty.map(fn(_) { \"one\" })),\n" " bytes.tag(<<0x02>>) |> bitty.map(fn(_) { \"two\" }),\n" " ])\n" " let assert Ok(\"two\") = bitty.run(parser, on: <<0x02>>)\n" " ```\n" ). -spec one_of(list(parser(PBJ))) -> parser(PBJ). one_of(Parsers) -> {parser, fun(State) -> try_parsers( Parsers, State, {bitty_error, {location, erlang:element(3, State), erlang:element(4, State)}, [], [], none} ) end}. -file("src/bitty.gleam", 269). ?DOC( " Wrap a parser to allow backtracking on failure. If the inner parser\n" " fails after consuming input, `attempt` resets the consumed flag so\n" " that `one_of` can try the next alternative.\n" "\n" " ```gleam\n" " let parser = bitty.one_of([\n" " bitty.attempt(bytes.tag(<<0xCA, 0xFE>>)),\n" " bytes.tag(<<0xCA, 0x11>>),\n" " ])\n" " let assert Ok(Nil) = bitty.run(parser, on: <<0xCA, 0x11>>)\n" " ```\n" ). -spec attempt(parser(PBR)) -> parser(PBR). attempt(Parser) -> {parser, fun(State) -> case (erlang:element(2, Parser))(State) of {continue, _, _, _} = Success -> Success; {stop, Error, _, Committed} -> {stop, Error, false, Committed} end end}. -file("src/bitty.gleam", 287). ?DOC( " Commit to the current parse path on success. After `cut`, a later\n" " failure will not backtrack past this point, producing better error\n" " messages. Typically used after matching a tag or discriminator.\n" "\n" " ```gleam\n" " use _ <- bitty.then(bitty.cut(bytes.tag(<<0x01>>)))\n" " use value <- bitty.then(num.u8())\n" " bitty.success(value)\n" " ```\n" ). -spec cut(parser(PBU)) -> parser(PBU). cut(Parser) -> {parser, fun(State) -> case (erlang:element(2, Parser))(State) of {continue, Value, State1, Consumed} -> {continue, Value, {state, erlang:element(2, State1), erlang:element(3, State1), erlang:element(4, State1), true}, Consumed}; {stop, Error, Consumed@1, Committed} -> {stop, Error, Consumed@1, Committed} end end}. -file("src/bitty.gleam", 305). ?DOC( " Replace the `expected` list in a parse error with `name`.\n" " Useful for giving user-friendly names to complex parsers.\n" "\n" " ```gleam\n" " let parser = num.u8() |> bitty.label(named: \"message type\")\n" " let assert Error(e) = bitty.run(parser, on: <<>>)\n" " assert e.expected == [\"message type\"]\n" " ```\n" ). -spec label(parser(PBX), binary()) -> parser(PBX). label(Parser, Name) -> {parser, fun(State) -> case (erlang:element(2, Parser))(State) of {continue, _, _, _} = Success -> Success; {stop, Error, Consumed, Committed} -> {stop, {bitty_error, erlang:element(2, Error), [Name], erlang:element(4, Error), erlang:element(5, Error)}, Consumed, Committed} end end}. -file("src/bitty.gleam", 323). ?DOC( " Push `name` onto the error context stack on failure, creating a\n" " breadcrumb trail (e.g. `[\"TLS record\", \"handshake\", \"certificate\"]`).\n" "\n" " ```gleam\n" " let parser = num.u8() |> bitty.context(in: \"header\")\n" " let assert Error(e) = bitty.run(parser, on: <<>>)\n" " assert e.context == [\"header\"]\n" " ```\n" ). -spec context(parser(PCA), binary()) -> parser(PCA). context(Parser, Name) -> {parser, fun(State) -> case (erlang:element(2, Parser))(State) of {continue, _, _, _} = Success -> Success; {stop, Error, Consumed, Committed} -> {stop, {bitty_error, erlang:element(2, Error), erlang:element(3, Error), [Name | erlang:element(4, Error)], erlang:element(5, Error)}, Consumed, Committed} end end}. -file("src/bitty.gleam", 382). -spec many_until_loop(parser(PCR), parser(PCT), state(), list(PCR), boolean()) -> step({list(PCR), PCT}). many_until_loop(Parser, Terminator, State, Acc, Any_consumed) -> case (erlang:element(2, Terminator))( {state, erlang:element(2, State), erlang:element(3, State), erlang:element(4, State), false} ) of {continue, Term_value, State1, Consumed} -> {continue, {lists:reverse(Acc), Term_value}, {state, erlang:element(2, State1), erlang:element(3, State1), erlang:element(4, State1), erlang:element(5, State1) orelse erlang:element(5, State)}, Any_consumed orelse Consumed}; {stop, _, false, false} -> case (erlang:element(2, Parser))(State) of {continue, Value, State1@1, true} -> many_until_loop( Parser, Terminator, {state, erlang:element(2, State1@1), erlang:element(3, State1@1), erlang:element(4, State1@1), erlang:element(5, State1@1) orelse erlang:element( 5, State )}, [Value | Acc], true ); {continue, _, _, false} -> {stop, {bitty_error, {location, erlang:element(3, State), erlang:element(4, State)}, [<<"consuming parser"/utf8>>], [], none}, Any_consumed, erlang:element(5, State)}; {stop, Error, Consumed@1, Committed} -> {stop, Error, Any_consumed orelse Consumed@1, Committed orelse erlang:element(5, State)} end; {stop, Error@1, Consumed@2, Committed@1} -> {stop, Error@1, Any_consumed orelse Consumed@2, Committed@1 orelse erlang:element(5, State)} end. -file("src/bitty.gleam", 375). ?DOC( " Repeat a parser until a terminator succeeds, returning the collected\n" " values and the terminator's result as a tuple. The terminator is tried\n" " first each iteration; on failure the item parser runs. The item parser\n" " must consume input on each iteration to prevent infinite loops.\n" "\n" " ```gleam\n" " let parser = bitty.many_until(num.u8(), until: bytes.tag(<<0x00>>))\n" " let assert Ok(#(values, Nil)) =\n" " bitty.run(parser, on: <<1, 2, 3, 0x00>>)\n" " assert values == [1, 2, 3]\n" " ```\n" ). -spec many_until(parser(PCL), parser(PCN)) -> parser({list(PCL), PCN}). many_until(Parser, Terminator) -> {parser, fun(State) -> many_until_loop(Parser, Terminator, State, [], false) end}. -file("src/bitty.gleam", 440). -spec fold_loop(parser(PDC), fun((PDE, PDC) -> PDE), state(), PDE, boolean()) -> step(PDE). fold_loop(Parser, F, State, Acc, Any_consumed) -> case (erlang:element(2, Parser))( {state, erlang:element(2, State), erlang:element(3, State), erlang:element(4, State), false} ) of {continue, Value, State1, true} -> fold_loop( Parser, F, {state, erlang:element(2, State1), erlang:element(3, State1), erlang:element(4, State1), erlang:element(5, State1) orelse erlang:element(5, State)}, F(Acc, Value), true ); {continue, _, _, false} -> {stop, {bitty_error, {location, erlang:element(3, State), erlang:element(4, State)}, [<<"consuming parser"/utf8>>], [], none}, Any_consumed, erlang:element(5, State)}; {stop, _, false, false} -> {continue, Acc, State, Any_consumed}; {stop, Error, Consumed, Committed} -> {stop, Error, Any_consumed orelse Consumed, Committed orelse erlang:element(5, State)} end. -file("src/bitty.gleam", 432). ?DOC( " Like `many` but accumulates results with a function instead of\n" " building a list. Runs the parser zero or more times.\n" "\n" " ```gleam\n" " let parser = bitty.fold(num.u8(), from: 0, with: fn(acc, x) { acc + x })\n" " let assert Ok(6) = bitty.run(parser, on: <<1, 2, 3>>)\n" " ```\n" ). -spec fold(parser(PCY), PDA, fun((PDA, PCY) -> PDA)) -> parser(PDA). fold(Parser, Initial, F) -> {parser, fun(State) -> fold_loop(Parser, F, State, Initial, false) end}. -file("src/bitty.gleam", 347). ?DOC( " Repeat a parser zero or more times, collecting results into a list.\n" " The inner parser **must** consume input on each iteration; a\n" " non-consuming parser causes an immediate error to prevent infinite loops.\n" " Stops when the parser fails without consuming input.\n" "\n" " ```gleam\n" " let assert Ok(values) =\n" " bitty.run(bitty.many(num.u8()), on: <<1, 2, 3>>)\n" " assert values == [1, 2, 3]\n" " ```\n" ). -spec many(parser(PCD)) -> parser(list(PCD)). many(Parser) -> _pipe = fold(Parser, [], fun(Acc, X) -> [X | Acc] end), map(_pipe, fun lists:reverse/1). -file("src/bitty.gleam", 359). ?DOC( " Like `many`, but requires at least one successful match.\n" "\n" " ```gleam\n" " let assert Ok(values) =\n" " bitty.run(bitty.many1(num.u8()), on: <<1, 2, 3>>)\n" " assert values == [1, 2, 3]\n" " ```\n" ). -spec many1(parser(PCH)) -> parser(list(PCH)). many1(Parser) -> _pipe = Parser, then(_pipe, fun(First) -> _pipe@1 = many(Parser), map(_pipe@1, fun(Rest) -> [First | Rest] end) end). -file("src/bitty.gleam", 479). ?DOC( " Like `fold` but requires at least one successful match.\n" "\n" " ```gleam\n" " let parser = bitty.fold1(num.u8(), from: 0, with: fn(acc, x) { acc + x })\n" " let assert Ok(6) = bitty.run(parser, on: <<1, 2, 3>>)\n" " ```\n" ). -spec fold1(parser(PDG), PDI, fun((PDI, PDG) -> PDI)) -> parser(PDI). fold1(Parser, Initial, F) -> _pipe = Parser, then(_pipe, fun(First) -> fold(Parser, F(Initial, First), F) end). -file("src/bitty.gleam", 498). -spec repeat_loop(parser(PDO), state(), list(PDO), integer(), boolean()) -> step(list(PDO)). repeat_loop(Parser, State, Acc, Remaining, Any_consumed) -> case Remaining =< 0 of true -> {continue, lists:reverse(Acc), State, Any_consumed}; false -> case (erlang:element(2, Parser))(State) of {continue, Value, State1, Consumed} -> repeat_loop( Parser, State1, [Value | Acc], Remaining - 1, Any_consumed orelse Consumed ); {stop, Error, Consumed@1, Committed} -> {stop, Error, Any_consumed orelse Consumed@1, Committed} end end. -file("src/bitty.gleam", 494). ?DOC( " Run a parser exactly `count` times, collecting results into a list.\n" "\n" " ```gleam\n" " let assert Ok(values) =\n" " bitty.run(bitty.repeat(num.u8(), times: 2), on: <<1, 2, 3>>)\n" " assert values == [1, 2]\n" " ```\n" ). -spec repeat(parser(PDK), integer()) -> parser(list(PDK)). repeat(Parser, Count) -> {parser, fun(State) -> repeat_loop(Parser, State, [], Count, false) end}. -file("src/bitty.gleam", 531). ?DOC( " Parse a count, then run a parser that many times. Dynamic version\n" " of `repeat`.\n" "\n" " ```gleam\n" " let parser = bitty.length_repeat(num.u8(), run: num.u8())\n" " let assert Ok(values) = bitty.run(parser, on: <<3, 10, 20, 30>>)\n" " assert values == [10, 20, 30]\n" " ```\n" ). -spec length_repeat(parser(integer()), parser(PDU)) -> parser(list(PDU)). length_repeat(Count, Parser) -> _pipe = Count, then(_pipe, fun(N) -> repeat(Parser, N) end). -file("src/bitty.gleam", 545). ?DOC( " Run `prefix` then `parser`, discarding the prefix result and returning\n" " only the parser's value.\n" "\n" " ```gleam\n" " use value <- bitty.then(bitty.preceded(bytes.tag(<<0x00>>), num.u8()))\n" " bitty.success(value)\n" " ```\n" ). -spec preceded(parser(any()), parser(PEA)) -> parser(PEA). preceded(Prefix, Parser) -> _pipe = Prefix, then(_pipe, fun(_) -> Parser end). -file("src/bitty.gleam", 556). ?DOC( " Run `parser` then `suffix`, discarding the suffix result and returning\n" " only the parser's value.\n" "\n" " ```gleam\n" " use value <- bitty.then(bitty.terminated(num.u8(), bytes.tag(<<0x00>>)))\n" " bitty.success(value)\n" " ```\n" ). -spec terminated(parser(PED), parser(any())) -> parser(PED). terminated(Parser, Suffix) -> _pipe = Parser, then(_pipe, fun(Value) -> _pipe@1 = Suffix, replace(_pipe@1, Value) end). -file("src/bitty.gleam", 571). ?DOC( " Run `open`, `parser`, then `close`, returning only the parser's value.\n" "\n" " ```gleam\n" " let parser = bitty.delimited(\n" " bytes.tag(<<0x28>>),\n" " num.u8(),\n" " bytes.tag(<<0x29>>),\n" " )\n" " let assert Ok(value) = bitty.run(parser, on: <<0x28, 42, 0x29>>)\n" " assert value == 42\n" " ```\n" ). -spec delimited(parser(any()), parser(PEK), parser(any())) -> parser(PEK). delimited(Open, Parser, Close) -> preceded(Open, terminated(Parser, Close)). -file("src/bitty.gleam", 586). ?DOC( " Run two parsers in sequence and return their results as a tuple.\n" "\n" " ```gleam\n" " let parser = bitty.pair(num.u8(), num.u8())\n" " let assert Ok(value) = bitty.run(parser, on: <<1, 2>>)\n" " assert value == #(1, 2)\n" " ```\n" ). -spec pair(parser(PEP), parser(PER)) -> parser({PEP, PER}). pair(First, Second) -> _pipe = First, then(_pipe, fun(A) -> _pipe@1 = Second, map(_pipe@1, fun(B) -> {A, B} end) end). -file("src/bitty.gleam", 601). ?DOC( " Run two parsers separated by a third, discarding the separator's result.\n" "\n" " ```gleam\n" " let parser = bitty.separated_pair(\n" " num.u8(),\n" " by: bytes.tag(<<0x2C>>),\n" " then: num.u8(),\n" " )\n" " let assert Ok(value) = bitty.run(parser, on: <<1, 0x2C, 2>>)\n" " assert value == #(1, 2)\n" " ```\n" ). -spec separated_pair(parser(PEU), parser(any()), parser(PEY)) -> parser({PEU, PEY}). separated_pair(First, Separator, Second) -> _pipe = First, then(_pipe, fun(A) -> _pipe@1 = preceded(Separator, Second), map(_pipe@1, fun(C) -> {A, C} end) end). -file("src/bitty.gleam", 641). ?DOC( " Like `separated`, but requires at least one item.\n" "\n" " A trailing separator (one not followed by a valid item) is left\n" " unconsumed. Compose with `end()` if you need to ensure all input\n" " is consumed.\n" "\n" " ```gleam\n" " let parser = bitty.separated1(num.u8(), by: bytes.tag(<<0x2C>>))\n" " let assert Ok(values) = bitty.run(parser, on: <<1, 0x2C, 2, 0x2C, 3>>)\n" " assert values == [1, 2, 3]\n" " ```\n" ). -spec separated1(parser(PFH), parser(any())) -> parser(list(PFH)). separated1(Parser, Separator) -> _pipe = Parser, then( _pipe, fun(First) -> _pipe@1 = many(attempt(preceded(Separator, Parser))), map(_pipe@1, fun(Rest) -> [First | Rest] end) end ). -file("src/bitty.gleam", 623). ?DOC( " Parse zero or more occurrences of `parser` separated by `separator`.\n" " The separator parser's result is discarded. Returns a list of the\n" " parsed values. Succeeds with an empty list if the first item fails\n" " without consuming input.\n" "\n" " A trailing separator (one not followed by a valid item) is left\n" " unconsumed. Compose with `end()` if you need to ensure all input\n" " is consumed.\n" "\n" " ```gleam\n" " let parser = bitty.separated(num.u8(), by: bytes.tag(<<0x2C>>))\n" " let assert Ok(values) = bitty.run(parser, on: <<1, 0x2C, 2, 0x2C, 3>>)\n" " assert values == [1, 2, 3]\n" " ```\n" ). -spec separated(parser(PFB), parser(any())) -> parser(list(PFB)). separated(Parser, Separator) -> one_of([separated1(Parser, Separator), success([])]). -file("src/bitty.gleam", 657). ?DOC( " Try a parser, returning `Some(value)` on success or `None` if it fails\n" " without consuming input. A consuming failure still propagates.\n" "\n" " ```gleam\n" " let assert Ok(value) =\n" " bitty.run(bitty.optional(num.u8()), on: <<42>>)\n" " assert value == Some(42)\n" " ```\n" ). -spec optional(parser(PFN)) -> parser(gleam@option:option(PFN)). optional(Parser) -> {parser, fun(State) -> case (erlang:element(2, Parser))(State) of {continue, Value, State1, Consumed} -> {continue, {some, Value}, State1, Consumed}; {stop, _, false, false} -> {continue, none, State, false}; {stop, Error, Consumed@1, Committed} -> {stop, Error, Consumed@1, Committed} end end}. -file("src/bitty.gleam", 676). ?DOC( " Run a parser and then check the result against a predicate. If the\n" " predicate returns `False`, the parse fails with `\"verify\"` expected.\n" "\n" " ```gleam\n" " let parser = num.u8() |> bitty.verify(with: fn(x) { x > 0 })\n" " let assert Ok(42) = bitty.run(parser, on: <<42>>)\n" " let assert Error(_) = bitty.run(parser, on: <<0>>)\n" " ```\n" ). -spec verify(parser(PFR), fun((PFR) -> boolean())) -> parser(PFR). verify(Parser, Predicate) -> {parser, fun(State) -> case (erlang:element(2, Parser))(State) of {continue, Value, State1, Consumed} -> case Predicate(Value) of true -> {continue, Value, State1, Consumed}; false -> {stop, {bitty_error, {location, erlang:element(3, State), erlang:element(4, State)}, [<<"verify"/utf8>>], [], none}, Consumed, erlang:element(5, State1)} end; {stop, Error, Consumed@1, Committed} -> {stop, Error, Consumed@1, Committed} end end}. -file("src/bitty.gleam", 710). ?DOC( " Negative lookahead: succeeds with `Nil` if the child parser fails,\n" " fails if the child parser succeeds. Never consumes input.\n" "\n" " ```gleam\n" " let parser = {\n" " use _ <- bitty.then(bitty.not(bytes.tag(<<0x00>>)))\n" " num.u8()\n" " }\n" " let assert Ok(0x42) = bitty.run(parser, on: <<0x42>>)\n" " let assert Error(_) = bitty.run(parser, on: <<0x00>>)\n" " ```\n" ). -spec 'not'(parser(any())) -> parser(nil). 'not'(Parser) -> {parser, fun(State) -> case (erlang:element(2, Parser))(State) of {continue, _, _, _} -> {stop, {bitty_error, {location, erlang:element(3, State), erlang:element(4, State)}, [], [], none}, false, erlang:element(5, State)}; {stop, _, _, _} -> {continue, nil, State, false} end end}. -file("src/bitty.gleam", 737). ?DOC( " Conditionally run a parser. If `condition` is `True`, run the parser\n" " and wrap the result in `Some`. If `False`, return `None` without\n" " consuming input.\n" "\n" " ```gleam\n" " let parser = bitty.cond(when: True, run: num.u8())\n" " let assert Ok(Some(42)) = bitty.run(parser, on: <<42>>)\n" " ```\n" ). -spec 'cond'(boolean(), parser(PFX)) -> parser(gleam@option:option(PFX)). 'cond'(Condition, Parser) -> case Condition of true -> _pipe = Parser, map(_pipe, fun(Field@0) -> {some, Field@0} end); false -> success(none) end. -file("src/bitty.gleam", 745). ?DOC(" Return the current `Location` in the input without consuming anything.\n"). -spec location() -> parser(location()). location() -> {parser, fun(State) -> {continue, {location, erlang:element(3, State), erlang:element(4, State)}, State, false} end}. -file("src/bitty.gleam", 759). ?DOC( " Skip remaining bits in the current byte to reach the next byte boundary.\n" " If already aligned, this is a no-op. Use after bit-level parsing to\n" " resume byte-aligned operations.\n" ). -spec align() -> parser(nil). align() -> {parser, fun(State) -> case erlang:element(4, State) =:= 0 of true -> {continue, nil, State, false}; false -> {continue, nil, {state, erlang:element(2, State), erlang:element(3, State) + 1, 0, erlang:element(5, State)}, true} end end}. -file("src/bitty.gleam", 779). ?DOC( " Succeed only if all input has been consumed. Fails with\n" " `\"end of input\"` expected if bytes remain.\n" "\n" " ```gleam\n" " let assert Ok(Nil) = bitty.run(bitty.end(), on: <<>>)\n" " ```\n" ). -spec 'end'() -> parser(nil). 'end'() -> {parser, fun(State) -> Remaining = erlang:byte_size(erlang:element(2, State)) - erlang:element( 3, State ), case (Remaining =:= 0) andalso (erlang:element(4, State) =:= 0) of true -> {continue, nil, State, false}; false -> {stop, {bitty_error, {location, erlang:element(3, State), erlang:element(4, State)}, [<<"end of input"/utf8>>], [], none}, false, erlang:element(5, State)} end end}. -file("src/bitty.gleam", 857). -spec within_bytes_check(state(), integer(), PGQ, state()) -> step(PGQ). within_bytes_check(State, Byte_len, Value, Inner_end) -> case (erlang:element(3, Inner_end) =:= Byte_len) andalso (erlang:element( 4, Inner_end ) =:= 0) of true -> {continue, Value, {state, erlang:element(2, State), erlang:element(3, State) + Byte_len, erlang:element(4, State), erlang:element(5, State)}, Byte_len > 0}; false -> {stop, {bitty_error, {location, erlang:element(3, State) + erlang:element(3, Inner_end), erlang:element(4, Inner_end)}, [<<<<"complete consumption of "/utf8, (erlang:integer_to_binary(Byte_len))/binary>>/binary, "-byte window"/utf8>>], [], none}, true, erlang:element(5, State)} end. -file("src/bitty.gleam", 891). -spec within_bytes_partial_check(state(), integer(), PGS, state(), bitstring()) -> step({PGS, bitstring()}). within_bytes_partial_check(State, Byte_len, Value, Inner_end, Window) -> Leftover = case erlang:element(4, Inner_end) /= 0 of true -> Available = 8 - erlang:element(4, Inner_end), Partial = case gleam_stdlib:bit_array_slice( Window, erlang:element(3, Inner_end), 1 ) of {ok, <>} -> Mask = erlang:'bsl'(1, Available) - 1, Bits_val = erlang:'band'(Byte, Mask), <>; _ -> <<>> end, Full_offset = erlang:element(3, Inner_end) + 1, Remaining = Byte_len - Full_offset, case gleam_stdlib:bit_array_slice(Window, Full_offset, Remaining) of {ok, Rest} -> <>; _ -> Partial end; false -> Unconsumed = Byte_len - erlang:element(3, Inner_end), case gleam_stdlib:bit_array_slice( Window, erlang:element(3, Inner_end), Unconsumed ) of {ok, Rest@1} -> Rest@1; _ -> <<>> end end, {continue, {Value, Leftover}, {state, erlang:element(2, State), erlang:element(3, State) + Byte_len, erlang:element(4, State), erlang:element(5, State)}, Byte_len > 0}. -file("src/bitty.gleam", 931). -spec within_bytes_stop(state(), bitty_error(), boolean(), boolean()) -> step(any()). within_bytes_stop(State, Error, Consumed, Committed) -> {stop, {bitty_error, {location, erlang:element(3, State) + erlang:element( 2, erlang:element(2, Error) ), erlang:element(3, erlang:element(2, Error))}, erlang:element(3, Error), erlang:element(4, Error), erlang:element(5, Error)}, Consumed, Committed orelse erlang:element(5, State)}. -file("src/bitty.gleam", 1087). ?DOC(false). -spec advance_bits(integer(), integer(), integer()) -> {integer(), integer()}. advance_bits(Byte_offset, Bit_offset, Count) -> Total_bits = Bit_offset + Count, {Byte_offset + (Total_bits div 8), Total_bits rem 8}. -file("src/bitty.gleam", 1046). ?DOC(false). -spec read_uint(state(), integer(), integer(), boolean()) -> step(integer()). read_uint(State, Remaining, Acc, Consumed) -> case Remaining =< 0 of true -> {continue, Acc, State, Consumed}; false -> case gleam_stdlib:bit_array_slice( erlang:element(2, State), erlang:element(3, State), 1 ) of {ok, <>} -> Available = 8 - erlang:element(4, State), To_read = gleam@int:min(Available, Remaining), Shift = Available - To_read, Mask = erlang:'bsl'(1, To_read) - 1, Bits_val = erlang:'band'(erlang:'bsr'(Byte, Shift), Mask), New_acc = erlang:'bor'(erlang:'bsl'(Acc, To_read), Bits_val), {New_byte, New_bit} = advance_bits( erlang:element(3, State), erlang:element(4, State), To_read ), New_state = {state, erlang:element(2, State), New_byte, New_bit, erlang:element(5, State)}, read_uint(New_state, Remaining - To_read, New_acc, Consumed); _ -> {stop, {bitty_error, {location, erlang:element(3, State), erlang:element(4, State)}, [<<(erlang:integer_to_binary(Remaining))/binary, " more bits"/utf8>>], [], none}, Consumed, erlang:element(5, State)} end end. -file("src/bitty.gleam", 1121). -spec read_n_bytes_unaligned(state(), integer(), bitstring(), boolean()) -> step(bitstring()). read_n_bytes_unaligned(State, Remaining, Acc, Consumed) -> case Remaining =< 0 of true -> {continue, Acc, State, Consumed}; false -> case read_uint(State, 8, 0, Consumed) of {continue, Byte_val, New_state, _} -> read_n_bytes_unaligned( New_state, Remaining - 1, <>, Consumed ); {stop, Error, Consumed2, Committed} -> {stop, Error, Consumed2, Committed} end end. -file("src/bitty.gleam", 1144). ?DOC(false). -spec stop_expected(state(), binary()) -> step(any()). stop_expected(State, Expected) -> {stop, {bitty_error, {location, erlang:element(3, State), erlang:element(4, State)}, [Expected], [], none}, false, erlang:element(5, State)}. -file("src/bitty.gleam", 819). -spec within_bytes_setup( state(), integer(), parser(PGH), fun((state(), bitstring(), step(PGH)) -> step(PGK)) ) -> step(PGK). within_bytes_setup(State, Byte_len, Inner, On_window) -> Remaining = erlang:byte_size(erlang:element(2, State)) - erlang:element( 3, State ), case Remaining < Byte_len of true -> stop_expected( State, <<<<"at least "/utf8, (erlang:integer_to_binary(Byte_len))/binary>>/binary, " bytes"/utf8>> ); false -> case gleam_stdlib:bit_array_slice( erlang:element(2, State), erlang:element(3, State), Byte_len ) of {ok, Window} -> Inner_state = {state, Window, 0, 0, false}, On_window( State, Window, (erlang:element(2, Inner))(Inner_state) ); _ -> stop_expected(State, <<"valid slice"/utf8>>) end end. -file("src/bitty.gleam", 846). -spec within_bytes_run(state(), integer(), parser(PGN)) -> step(PGN). within_bytes_run(State, Byte_len, Inner) -> within_bytes_setup( State, Byte_len, Inner, fun(St, _, Result) -> case Result of {continue, Value, Inner_end, _} -> within_bytes_check(St, Byte_len, Value, Inner_end); {stop, Error, Consumed, Committed} -> within_bytes_stop(St, Error, Consumed, Committed) end end ). -file("src/bitty.gleam", 965). -spec within_bytes_partial_run(state(), integer(), parser(PGZ)) -> step({PGZ, bitstring()}). within_bytes_partial_run(State, Byte_len, Inner) -> within_bytes_setup( State, Byte_len, Inner, fun(St, Window, Result) -> case Result of {continue, Value, Inner_end, _} -> within_bytes_partial_check( St, Byte_len, Value, Inner_end, Window ); {stop, Error, Consumed, Committed} -> within_bytes_stop(St, Error, Consumed, Committed) end end ). -file("src/bitty.gleam", 1109). -spec read_n_bytes_aligned(state(), integer()) -> step(bitstring()). read_n_bytes_aligned(State, Count) -> case gleam_stdlib:bit_array_slice( erlang:element(2, State), erlang:element(3, State), Count ) of {ok, Bytes} -> {continue, Bytes, {state, erlang:element(2, State), erlang:element(3, State) + Count, erlang:element(4, State), erlang:element(5, State)}, Count > 0}; _ -> stop_expected( State, <<(erlang:integer_to_binary(Count))/binary, " bytes"/utf8>> ) end. -file("src/bitty.gleam", 1097). ?DOC(false). -spec read_n_bytes(state(), integer()) -> step(bitstring()). read_n_bytes(State, Count) -> gleam@bool:guard( Count < 0, stop_expected(State, <<"non-negative byte count"/utf8>>), fun() -> case erlang:element(4, State) =:= 0 of true -> read_n_bytes_aligned(State, Count); false -> read_n_bytes_unaligned(State, Count, <<>>, Count > 0) end end ). -file("src/bitty.gleam", 1041). ?DOC( " Parse a byte count, then take that many bytes as a `BitArray`.\n" " Dynamic version of `bytes.take`.\n" "\n" " ```gleam\n" " let parser = bitty.length_take(num.u8())\n" " let assert Ok(data) = bitty.run(parser, on: <<3, 0xAA, 0xBB, 0xCC>>)\n" " assert data == <<0xAA, 0xBB, 0xCC>>\n" " ```\n" ). -spec length_take(parser(integer())) -> parser(bitstring()). length_take(Count) -> _pipe = Count, then( _pipe, fun(N) -> make_parser(fun(_capture) -> read_n_bytes(_capture, N) end) end ). -file("src/bitty.gleam", 1158). ?DOC(false). -spec require_aligned(state(), fun(() -> step(PHR))) -> step(PHR). require_aligned(State, Continue) -> case erlang:element(4, State) /= 0 of true -> stop_expected(State, <<"byte alignment"/utf8>>); false -> Continue() end. -file("src/bitty.gleam", 808). ?DOC( " Run `inner` on a zero-copy window of exactly `byte_len` bytes.\n" " The inner parser must consume the entire window or the parse fails.\n" " Requires byte alignment.\n" "\n" " ```gleam\n" " use length <- bitty.then(num.u8())\n" " use value <- bitty.then(bitty.within_bytes(length, run: bytes.rest()))\n" " bitty.success(value)\n" " ```\n" ). -spec within_bytes(integer(), parser(PGE)) -> parser(PGE). within_bytes(Byte_len, Inner) -> {parser, fun(State) -> gleam@bool:guard( Byte_len < 0, stop_expected(State, <<"non-negative byte length"/utf8>>), fun() -> require_aligned( State, fun() -> within_bytes_run(State, Byte_len, Inner) end ) end ) end}. -file("src/bitty.gleam", 951). ?DOC( " Like `within_bytes`, but the inner parser may stop early. Returns the\n" " parsed value and any unconsumed input from the window as a tuple.\n" " Requires byte alignment. The returned `BitArray` may not be byte-aligned\n" " when bit-level parsers are used.\n" ). -spec within_bytes_partial(integer(), parser(PGW)) -> parser({PGW, bitstring()}). within_bytes_partial(Byte_len, Inner) -> {parser, fun(State) -> gleam@bool:guard( Byte_len < 0, stop_expected(State, <<"non-negative byte length"/utf8>>), fun() -> require_aligned( State, fun() -> within_bytes_partial_run(State, Byte_len, Inner) end ) end ) end}. -file("src/bitty.gleam", 992). ?DOC( " Run a parser and return the raw bytes it consumed, discarding the\n" " parsed value. Requires byte alignment at both start and end.\n" "\n" " ```gleam\n" " let inner = {\n" " use _ <- bitty.then(num.u8())\n" " use _ <- bitty.then(num.u8())\n" " bitty.success(Nil)\n" " }\n" " let assert Ok(raw) = bitty.run(bitty.capture(inner), on: <<0xCA, 0xFE>>)\n" " assert raw == <<0xCA, 0xFE>>\n" " ```\n" ). -spec capture(parser(any())) -> parser(bitstring()). capture(Parser) -> {parser, fun(State) -> require_aligned( State, fun() -> case (erlang:element(2, Parser))(State) of {continue, _, State1, Consumed} -> gleam@bool:guard( erlang:element(4, State1) /= 0, {stop, {bitty_error, {location, erlang:element(3, State1), erlang:element(4, State1)}, [<<"byte alignment after capture"/utf8>>], [], none}, Consumed, erlang:element(5, State1)}, fun() -> Byte_count = erlang:element(3, State1) - erlang:element( 3, State ), case gleam_stdlib:bit_array_slice( erlang:element(2, State), erlang:element(3, State), Byte_count ) of {ok, Captured} -> {continue, Captured, State1, Consumed}; _ -> stop_expected( State, <<"valid slice"/utf8>> ) end end ); {stop, Error, Consumed@1, Committed} -> {stop, Error, Consumed@1, Committed} end end ) end}. -file("src/bitty.gleam", 1029). ?DOC( " Parse a byte count, then run a parser within a window of that many\n" " bytes. Dynamic version of `within_bytes`.\n" "\n" " ```gleam\n" " let parser = bitty.length_within(num.u8(), run: bytes.rest())\n" " let assert Ok(data) = bitty.run(parser, on: <<3, 0xAA, 0xBB, 0xCC>>)\n" " assert data == <<0xAA, 0xBB, 0xCC>>\n" " ```\n" ). -spec length_within(parser(integer()), parser(PHG)) -> parser(PHG). length_within(Count, Parser) -> _pipe = Count, then(_pipe, fun(N) -> within_bytes(N, Parser) end). -file("src/bitty.gleam", 1165). -spec remaining_bits_of_byte(state()) -> bitstring(). remaining_bits_of_byte(State) -> Available = 8 - erlang:element(4, State), case gleam_stdlib:bit_array_slice( erlang:element(2, State), erlang:element(3, State), 1 ) of {ok, <>} -> Mask = erlang:'bsl'(1, Available) - 1, Bits_val = erlang:'band'(Byte, Mask), <>; _ -> <<>> end. -file("src/bitty.gleam", 1177). -spec remaining_slice(state()) -> bitstring(). remaining_slice(State) -> case erlang:element(4, State) /= 0 of true -> Partial = remaining_bits_of_byte(State), Full_offset = erlang:element(3, State) + 1, Remaining = erlang:byte_size(erlang:element(2, State)) - Full_offset, case gleam_stdlib:bit_array_slice( erlang:element(2, State), Full_offset, Remaining ) of {ok, Rest} -> <>; _ -> Partial end; false -> Remaining@1 = erlang:byte_size(erlang:element(2, State)) - erlang:element( 3, State ), case gleam_stdlib:bit_array_slice( erlang:element(2, State), erlang:element(3, State), Remaining@1 ) of {ok, Rest@1} -> Rest@1; _ -> <<>> end end. -file("src/bitty.gleam", 1198). -spec new_state(bitstring()) -> state(). new_state(Input) -> {state, Input, 0, 0, false}. -file("src/bitty.gleam", 149). -spec do_run(parser(PAS), bitstring()) -> {ok, {PAS, state()}} | {error, bitty_error()}. do_run(Parser, Input) -> State = new_state(Input), case (erlang:element(2, Parser))(State) of {continue, Value, Final_state, _} -> {ok, {Value, Final_state}}; {stop, Error, _, _} -> {error, Error} end. -file("src/bitty.gleam", 101). ?DOC( " Run a parser on the given input, requiring it to consume all bytes.\n" " Returns `Error` if parsing fails or if unconsumed input remains.\n" "\n" " ```gleam\n" " let assert Ok(byte) = bitty.run(num.u8(), on: <<0xFF>>)\n" " assert byte == 255\n" " ```\n" ). -spec run(parser(PAG), bitstring()) -> {ok, PAG} | {error, bitty_error()}. run(Parser, Input) -> gleam@result:'try'( do_run(Parser, Input), fun(_use0) -> {Value, Final_state} = _use0, Remaining = erlang:byte_size(erlang:element(2, Final_state)) - erlang:element( 3, Final_state ), case (Remaining =:= 0) andalso (erlang:element(4, Final_state) =:= 0) of true -> {ok, Value}; false -> {error, {bitty_error, {location, erlang:element(3, Final_state), erlang:element(4, Final_state)}, [<<"end of input"/utf8>>], [], none}} end end ). -file("src/bitty.gleam", 128). ?DOC( " Run a parser on the given input, returning the parsed value and any\n" " unconsumed input. Does not require all input to be consumed.\n" " The returned `BitArray` may not be byte-aligned when bit-level parsers\n" " are used.\n" "\n" " ```gleam\n" " let assert Ok(#(byte, rest)) =\n" " bitty.run_partial(num.u8(), on: <<0xAA, 0xBB>>)\n" " assert byte == 0xAA\n" " assert rest == <<0xBB>>\n" " ```\n" ). -spec run_partial(parser(PAK), bitstring()) -> {ok, {PAK, bitstring()}} | {error, bitty_error()}. run_partial(Parser, Input) -> gleam@result:'try'( do_run(Parser, Input), fun(_use0) -> {Value, Final_state} = _use0, {ok, {Value, remaining_slice(Final_state)}} end ). -file("src/bitty.gleam", 140). ?DOC( " Like `run_partial`, but also returns the `Location` where the parser\n" " stopped. Useful for incremental or streaming parsing.\n" " The returned `BitArray` may not be byte-aligned when bit-level parsers\n" " are used.\n" ). -spec run_with_location(parser(PAO), bitstring()) -> {ok, {PAO, location(), bitstring()}} | {error, bitty_error()}. run_with_location(Parser, Input) -> gleam@result:'try'( do_run(Parser, Input), fun(_use0) -> {Value, Final_state} = _use0, Loc = {location, erlang:element(3, Final_state), erlang:element(4, Final_state)}, {ok, {Value, Loc, remaining_slice(Final_state)}} end ).