Packages

Binary and string parser combinator library for Gleam

Current section

Files

Jump to
bitty src bitty@string.erl
Raw

src/bitty@string.erl

-module(bitty@string).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/bitty/string.gleam").
-export([utf8/1, literal/1, newline/0, tab/0, crlf/0, line_ending/0, fixed/1, grapheme_if/1, grapheme/0, take_graphemes/1, take_while/1, not_line_ending/0, take_while1/1, take_until/1, null_terminated/0, alpha/0, alpha1/0, digit/0, digit1/0, integer/0, alphanumeric/0, alphanumeric1/0, hex_digit/0, hex_digit1/0, space/0, space1/0, multispace/0, multispace1/0]).
-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(
" String-level parsers for reading, matching, and scanning UTF-8 text.\n"
" These parsers decode graphemes on the fly.\n"
" Use `bitty/bytes` for raw byte operations and `bitty/bits` for sub-byte\n"
" access before returning to string parsing.\n"
).
-file("src/bitty/string.gleam", 237).
?DOC(
" Parse exactly `byte_len` bytes and decode them as a UTF-8 string.\n"
" Fails if the bytes are not valid UTF-8 or if there is insufficient input.\n"
"\n"
" ```gleam\n"
" let assert Ok(s) = bitty.run(string.utf8(5), on: <<\"hello\">>)\n"
" assert s == \"hello\"\n"
" ```\n"
).
-spec utf8(integer()) -> bitty:parser(binary()).
utf8(Byte_len) ->
bitty:then(
bitty@bytes:take(Byte_len),
fun(Raw) -> case gleam@bit_array:to_string(Raw) of
{ok, S} ->
bitty:success(S);
{error, _} ->
bitty:fail(<<"expected valid utf-8"/utf8>>)
end end
).
-file("src/bitty/string.gleam", 251).
?DOC(
" Match an exact string literal at the current position and consume it.\n"
" Returns `Nil` on success; fails if the bytes don't match.\n"
"\n"
" ```gleam\n"
" let assert Ok(Nil) = bitty.run(string.literal(\"GET\"), on: <<\"GET\">>)\n"
" ```\n"
).
-spec literal(binary()) -> bitty:parser(nil).
literal(Expected) ->
bitty@bytes:tag(gleam_stdlib:identity(Expected)).
-file("src/bitty/string.gleam", 181).
?DOC(
" Match a newline character (`\\n`).\n"
"\n"
" ```gleam\n"
" let assert Ok(#(_, rest)) = bitty.run_partial(string.newline(), on: <<\"\\nhello\">>)\n"
" assert rest == <<\"hello\">>\n"
" ```\n"
).
-spec newline() -> bitty:parser(nil).
newline() ->
literal(<<"\n"/utf8>>).
-file("src/bitty/string.gleam", 191).
?DOC(
" Match a tab character (`\\t`).\n"
"\n"
" ```gleam\n"
" let assert Ok(#(_, rest)) = bitty.run_partial(string.tab(), on: <<\"\\thello\">>)\n"
" assert rest == <<\"hello\">>\n"
" ```\n"
).
-spec tab() -> bitty:parser(nil).
tab() ->
literal(<<"\t"/utf8>>).
-file("src/bitty/string.gleam", 201).
?DOC(
" Match a carriage return + line feed sequence (`\\r\\n`).\n"
"\n"
" ```gleam\n"
" let assert Ok(#(_, rest)) = bitty.run_partial(string.crlf(), on: <<\"\\r\\nhello\">>)\n"
" assert rest == <<\"hello\">>\n"
" ```\n"
).
-spec crlf() -> bitty:parser(nil).
crlf() ->
literal(<<"\r\n"/utf8>>).
-file("src/bitty/string.gleam", 212).
?DOC(
" Match a line ending: either `\\r\\n` or `\\n`.\n"
" Tries `\\r\\n` first to avoid partial matches.\n"
"\n"
" ```gleam\n"
" let assert Ok(#(_, rest)) = bitty.run_partial(string.line_ending(), on: <<\"\\r\\nhello\">>)\n"
" assert rest == <<\"hello\">>\n"
" ```\n"
).
-spec line_ending() -> bitty:parser(nil).
line_ending() ->
bitty:one_of([crlf(), newline()]).
-file("src/bitty/string.gleam", 279).
-spec trim_padding(binary()) -> binary().
trim_padding(S) ->
_pipe = S,
_pipe@1 = gleam@string:to_graphemes(_pipe),
_pipe@2 = lists:reverse(_pipe@1),
_pipe@3 = gleam@list:drop_while(
_pipe@2,
fun(G) -> (G =:= <<" "/utf8>>) orelse (G =:= <<"\x{0000}"/utf8>>) end
),
_pipe@4 = lists:reverse(_pipe@3),
gleam@string:join(_pipe@4, <<""/utf8>>).
-file("src/bitty/string.gleam", 275).
?DOC(
" Parse a fixed-width string field, trimming trailing NUL bytes and spaces.\n"
" Useful for binary formats that pad string fields to a fixed size.\n"
"\n"
" ```gleam\n"
" let assert Ok(s) = bitty.run(string.fixed(5), on: <<\"hi\", 0x00, 0x00, 0x00>>)\n"
" assert s == \"hi\"\n"
" ```\n"
).
-spec fixed(integer()) -> bitty:parser(binary()).
fixed(Byte_len) ->
_pipe = utf8(Byte_len),
bitty:map(_pipe, fun trim_padding/1).
-file("src/bitty/string.gleam", 326).
-spec peek_lead_byte(bitstring(), integer()) -> {ok, integer()} | {error, nil}.
peek_lead_byte(Input, Offset) ->
case gleam_stdlib:bit_array_slice(Input, Offset, 1) of
{ok, <<Byte>>} ->
{ok, Byte};
_ ->
{error, nil}
end.
-file("src/bitty/string.gleam", 333).
-spec utf8_byte_len(integer()) -> {ok, integer()} | {error, nil}.
utf8_byte_len(Lead_byte) ->
case Lead_byte of
B when B < 16#80 ->
{ok, 1};
B@1 when (B@1 >= 16#C0) andalso (B@1 < 16#E0) ->
{ok, 2};
B@2 when (B@2 >= 16#E0) andalso (B@2 < 16#F0) ->
{ok, 3};
B@3 when (B@3 >= 16#F0) andalso (B@3 < 16#F8) ->
{ok, 4};
_ ->
{error, nil}
end.
-file("src/bitty/string.gleam", 343).
-spec decode_codepoint(bitstring(), integer()) -> {ok, {binary(), integer()}} |
{error, nil}.
decode_codepoint(Input, Offset) ->
gleam@result:'try'(
peek_lead_byte(Input, Offset),
fun(Lead) ->
gleam@result:'try'(
utf8_byte_len(Lead),
fun(Len) ->
gleam@result:'try'(
gleam_stdlib:bit_array_slice(Input, Offset, Len),
fun(Bytes) ->
gleam@result:'try'(
gleam@bit_array:to_string(Bytes),
fun(S) -> {ok, {S, Len}} end
)
end
)
end
)
end
).
-file("src/bitty/string.gleam", 371).
-spec try_extend_grapheme(bitstring(), integer(), integer()) -> {ok, integer()} |
{error, nil}.
try_extend_grapheme(Input, Start, End) ->
gleam@result:'try'(
decode_codepoint(Input, End),
fun(_use0) ->
{_, Next_len} = _use0,
New_end = End + Next_len,
Total_len = New_end - Start,
gleam@result:'try'(
gleam_stdlib:bit_array_slice(Input, Start, Total_len),
fun(Ext_bytes) ->
gleam@result:'try'(
gleam@bit_array:to_string(Ext_bytes),
fun(Ext_str) ->
case gleam@string:to_graphemes(Ext_str) of
[_] ->
{ok, New_end};
_ ->
{error, nil}
end
end
)
end
)
end
).
-file("src/bitty/string.gleam", 356).
-spec extend_grapheme(bitstring(), integer(), integer()) -> {ok,
{binary(), integer()}} |
{error, nil}.
extend_grapheme(Input, Start, End) ->
Len = End - Start,
gleam@result:'try'(
gleam_stdlib:bit_array_slice(Input, Start, Len),
fun(Bytes) ->
gleam@result:'try'(
gleam@bit_array:to_string(Bytes),
fun(Current) -> case try_extend_grapheme(Input, Start, End) of
{ok, New_end} ->
extend_grapheme(Input, Start, New_end);
{error, _} ->
{ok, {Current, Len}}
end end
)
end
).
-file("src/bitty/string.gleam", 351).
-spec decode_grapheme(bitstring(), integer()) -> {ok, {binary(), integer()}} |
{error, nil}.
decode_grapheme(Input, Offset) ->
gleam@result:'try'(
decode_codepoint(Input, Offset),
fun(_use0) ->
{_, First_len} = _use0,
extend_grapheme(Input, Offset, Offset + First_len)
end
).
-file("src/bitty/string.gleam", 307).
?DOC(
" Parse a single UTF-8 grapheme that satisfies the given predicate.\n"
" Fails if the grapheme doesn't match or if the input is not valid UTF-8.\n"
"\n"
" ```gleam\n"
" let parser = string.grapheme_if(fn(c) { c == \"A\" })\n"
" let assert Ok(c) = bitty.run(parser, on: <<\"A\">>)\n"
" assert c == \"A\"\n"
" ```\n"
).
-spec grapheme_if(fun((binary()) -> boolean())) -> bitty:parser(binary()).
grapheme_if(Predicate) ->
bitty:make_parser(
fun(State) ->
bitty:require_aligned(
State,
fun() ->
case decode_grapheme(
erlang:element(2, State),
erlang:element(3, State)
) of
{ok, {S, Len}} ->
case Predicate(S) of
true ->
{continue,
S,
{state,
erlang:element(2, State),
erlang:element(3, State) + Len,
erlang:element(4, State),
erlang:element(5, State)},
true};
false ->
bitty:stop_expected(
State,
<<"matching grapheme"/utf8>>
)
end;
{error, _} ->
bitty:stop_expected(
State,
<<"valid utf-8 grapheme"/utf8>>
)
end
end
)
end
).
-file("src/bitty/string.gleam", 295).
?DOC(
" Parse a single UTF-8 grapheme and return it as a one-character string.\n"
" Handles multi-byte and multi-codepoint grapheme clusters.\n"
"\n"
" ```gleam\n"
" let assert Ok(c) = bitty.run(string.grapheme(), on: <<\"A\">>)\n"
" assert c == \"A\"\n"
" ```\n"
).
-spec grapheme() -> bitty:parser(binary()).
grapheme() ->
grapheme_if(fun(_) -> true end).
-file("src/bitty/string.gleam", 449).
-spec decode_slice(bitstring(), integer(), integer()) -> {ok, binary()} |
{error, nil}.
decode_slice(Input, Start, Len) ->
gleam@result:'try'(
gleam_stdlib:bit_array_slice(Input, Start, Len),
fun(Bytes) -> gleam@bit_array:to_string(Bytes) end
).
-file("src/bitty/string.gleam", 437).
-spec finish_slice(bitty:state(), integer()) -> bitty:step(binary()).
finish_slice(State, Start) ->
Len = erlang:element(3, State) - Start,
case Len of
0 ->
{continue, <<""/utf8>>, State, false};
_ ->
case decode_slice(erlang:element(2, State), Start, Len) of
{ok, S} ->
{continue, S, State, true};
{error, _} ->
bitty:stop_expected(State, <<"valid utf-8"/utf8>>)
end
end.
-file("src/bitty/string.gleam", 405).
-spec take_graphemes_loop(bitty:state(), integer(), integer()) -> bitty:step(binary()).
take_graphemes_loop(State, Start, Remaining) ->
case Remaining =< 0 of
true ->
finish_slice(State, Start);
false ->
case decode_grapheme(
erlang:element(2, State),
erlang:element(3, State)
) of
{ok, {_, Len}} ->
take_graphemes_loop(
{state,
erlang:element(2, State),
erlang:element(3, State) + Len,
erlang:element(4, State),
erlang:element(5, State)},
Start,
Remaining - 1
);
{error, _} ->
Consumed = erlang:element(3, State) > Start,
{stop,
{bitty_error,
{location,
erlang:element(3, State),
erlang:element(4, State)},
[<<(erlang:integer_to_binary(Remaining))/binary,
" more graphemes"/utf8>>],
[],
none},
Consumed,
erlang:element(5, State)}
end
end.
-file("src/bitty/string.gleam", 394).
?DOC(
" Parse exactly `count` UTF-8 graphemes and return them as a string.\n"
" Unlike `utf8` which counts bytes, this counts logical characters.\n"
"\n"
" ```gleam\n"
" let assert Ok(s) = bitty.run(string.take_graphemes(2), on: <<\"café\">>)\n"
" assert s == \"ca\"\n"
" ```\n"
).
-spec take_graphemes(integer()) -> bitty:parser(binary()).
take_graphemes(Count) ->
bitty:make_parser(
fun(State) ->
gleam@bool:guard(
Count < 0,
bitty:stop_expected(
State,
<<"non-negative grapheme count"/utf8>>
),
fun() ->
bitty:require_aligned(
State,
fun() ->
take_graphemes_loop(
State,
erlang:element(3, State),
Count
)
end
)
end
)
end
).
-file("src/bitty/string.gleam", 470).
-spec take_while_loop(bitty:state(), integer(), fun((binary()) -> boolean())) -> bitty:step(binary()).
take_while_loop(State, Start, Predicate) ->
case decode_grapheme(erlang:element(2, State), erlang:element(3, State)) of
{ok, {S, Len}} ->
case Predicate(S) of
true ->
take_while_loop(
{state,
erlang:element(2, State),
erlang:element(3, State) + Len,
erlang:element(4, State),
erlang:element(5, State)},
Start,
Predicate
);
false ->
finish_slice(State, Start)
end;
{error, _} ->
finish_slice(State, Start)
end.
-file("src/bitty/string.gleam", 463).
?DOC(
" Consume graphemes while the predicate holds, returning them as a string.\n"
" Returns `\"\"` when zero graphemes match.\n"
"\n"
" ```gleam\n"
" let parser = string.take_while(fn(c) { c != \" \" })\n"
" let assert Ok(#(s, rest)) = bitty.run_partial(parser, on: <<\"hello world\">>)\n"
" assert s == \"hello\"\n"
" assert rest == <<\" world\">>\n"
" ```\n"
).
-spec take_while(fun((binary()) -> boolean())) -> bitty:parser(binary()).
take_while(Predicate) ->
bitty:make_parser(
fun(State) ->
bitty:require_aligned(
State,
fun() ->
take_while_loop(State, erlang:element(3, State), Predicate)
end
)
end
).
-file("src/bitty/string.gleam", 224).
?DOC(
" Consume characters until a `\\r` or `\\n` is found.\n"
" The line ending itself is not consumed.\n"
"\n"
" ```gleam\n"
" let assert Ok(#(s, rest)) = bitty.run_partial(string.not_line_ending(), on: <<\"hello\\nworld\">>)\n"
" assert s == \"hello\"\n"
" assert rest == <<\"\\nworld\">>\n"
" ```\n"
).
-spec not_line_ending() -> bitty:parser(binary()).
not_line_ending() ->
take_while(
fun(C) ->
not gleam_stdlib:contains_string(C, <<"\n"/utf8>>) andalso not gleam_stdlib:contains_string(
C,
<<"\r"/utf8>>
)
end
).
-file("src/bitty/string.gleam", 498).
?DOC(
" Like `take_while` but requires at least one matching grapheme.\n"
" Fails if the first grapheme doesn't satisfy the predicate.\n"
"\n"
" ```gleam\n"
" let parser = string.take_while1(fn(c) { c == \"a\" })\n"
" let assert Ok(#(s, _rest)) = bitty.run_partial(parser, on: <<\"aab\">>)\n"
" assert s == \"aa\"\n"
" ```\n"
).
-spec take_while1(fun((binary()) -> boolean())) -> bitty:parser(binary()).
take_while1(Predicate) ->
bitty:make_parser(
fun(State) ->
bitty:require_aligned(
State,
fun() ->
case decode_grapheme(
erlang:element(2, State),
erlang:element(3, State)
) of
{ok, {S, Len}} ->
case Predicate(S) of
true ->
take_while_loop(
{state,
erlang:element(2, State),
erlang:element(3, State) + Len,
erlang:element(4, State),
erlang:element(5, State)},
erlang:element(3, State),
Predicate
);
false ->
bitty:stop_expected(
State,
<<"matching grapheme"/utf8>>
)
end;
{error, _} ->
bitty:stop_expected(
State,
<<"valid utf-8 grapheme"/utf8>>
)
end
end
)
end
).
-file("src/bitty/string.gleam", 527).
?DOC(
" Consume graphemes until the predicate matches, returning everything\n"
" before the match. The matching grapheme is not consumed.\n"
" Returns `\"\"` when the first grapheme matches.\n"
"\n"
" ```gleam\n"
" let parser = string.take_until(fn(c) { c == \" \" })\n"
" let assert Ok(#(s, rest)) = bitty.run_partial(parser, on: <<\"hello world\">>)\n"
" assert s == \"hello\"\n"
" assert rest == <<\" world\">>\n"
" ```\n"
).
-spec take_until(fun((binary()) -> boolean())) -> bitty:parser(binary()).
take_until(Predicate) ->
take_while(fun(C) -> not Predicate(C) end).
-file("src/bitty/string.gleam", 262).
?DOC(
" Parse a null-terminated (C-style) string. Consumes graphemes until a\n"
" NUL byte (`0x00`) is found, then skips the NUL. Fails if no NUL is present.\n"
"\n"
" ```gleam\n"
" let assert Ok(s) = bitty.run(string.null_terminated(), on: <<\"hi\", 0x00>>)\n"
" assert s == \"hi\"\n"
" ```\n"
).
-spec null_terminated() -> bitty:parser(binary()).
null_terminated() ->
bitty:then(
take_until(fun(C) -> C =:= <<"\x{0000}"/utf8>> end),
fun(S) ->
bitty:then(bitty@bytes:tag(<<0>>), fun(_) -> bitty:success(S) end)
end
).
-file("src/bitty/string.gleam", 531).
-spec byte_of(binary()) -> {ok, integer()} | {error, nil}.
byte_of(S) ->
case gleam_stdlib:identity(S) of
<<B>> ->
{ok, B};
_ ->
{error, nil}
end.
-file("src/bitty/string.gleam", 538).
-spec is_alpha(binary()) -> boolean().
is_alpha(C) ->
case byte_of(C) of
{ok, B} ->
((B >= 16#41) andalso (B =< 16#5A)) orelse ((B >= 16#61) andalso (B
=< 16#7A));
{error, _} ->
false
end.
-file("src/bitty/string.gleam", 24).
?DOC(
" Parse zero or more ASCII alphabetic characters (`a-zA-Z`).\n"
" Returns `\"\"` when no characters match.\n"
"\n"
" ```gleam\n"
" let assert Ok(#(s, rest)) = bitty.run_partial(string.alpha(), on: <<\"abcXYZ123\">>)\n"
" assert s == \"abcXYZ\"\n"
" assert rest == <<\"123\">>\n"
" ```\n"
).
-spec alpha() -> bitty:parser(binary()).
alpha() ->
take_while(fun is_alpha/1).
-file("src/bitty/string.gleam", 36).
?DOC(
" Parse one or more ASCII alphabetic characters (`a-zA-Z`).\n"
" Fails if the first character is not alphabetic.\n"
"\n"
" ```gleam\n"
" let assert Ok(#(s, rest)) = bitty.run_partial(string.alpha1(), on: <<\"abcXYZ123\">>)\n"
" assert s == \"abcXYZ\"\n"
" assert rest == <<\"123\">>\n"
" ```\n"
).
-spec alpha1() -> bitty:parser(binary()).
alpha1() ->
take_while1(fun is_alpha/1).
-file("src/bitty/string.gleam", 545).
-spec is_digit(binary()) -> boolean().
is_digit(C) ->
case byte_of(C) of
{ok, B} ->
(B >= 16#30) andalso (B =< 16#39);
{error, _} ->
false
end.
-file("src/bitty/string.gleam", 72).
?DOC(
" Parse zero or more ASCII decimal digits (`0-9`).\n"
" Returns `\"\"` when no characters match.\n"
"\n"
" ```gleam\n"
" let assert Ok(#(s, rest)) = bitty.run_partial(string.digit(), on: <<\"42abc\">>)\n"
" assert s == \"42\"\n"
" assert rest == <<\"abc\">>\n"
" ```\n"
).
-spec digit() -> bitty:parser(binary()).
digit() ->
take_while(fun is_digit/1).
-file("src/bitty/string.gleam", 84).
?DOC(
" Parse one or more ASCII decimal digits (`0-9`).\n"
" Fails if the first character is not a digit.\n"
"\n"
" ```gleam\n"
" let assert Ok(#(s, rest)) = bitty.run_partial(string.digit1(), on: <<\"42abc\">>)\n"
" assert s == \"42\"\n"
" assert rest == <<\"abc\">>\n"
" ```\n"
).
-spec digit1() -> bitty:parser(binary()).
digit1() ->
take_while1(fun is_digit/1).
-file("src/bitty/string.gleam", 96).
?DOC(
" Parse one or more ASCII digits and convert them to an `Int`.\n"
" Fails if no digits are found or if parsing fails.\n"
"\n"
" ```gleam\n"
" let assert Ok(#(n, rest)) = bitty.run_partial(string.integer(), on: <<\"42abc\">>)\n"
" assert n == 42\n"
" assert rest == <<\"abc\">>\n"
" ```\n"
).
-spec integer() -> bitty:parser(integer()).
integer() ->
bitty:then(digit1(), fun(Digits) -> case gleam_stdlib:parse_int(Digits) of
{ok, N} ->
bitty:success(N);
{error, _} ->
bitty:fail(<<"expected valid integer"/utf8>>)
end end).
-file("src/bitty/string.gleam", 552).
-spec is_alphanumeric(binary()) -> boolean().
is_alphanumeric(C) ->
is_alpha(C) orelse is_digit(C).
-file("src/bitty/string.gleam", 48).
?DOC(
" Parse zero or more ASCII alphanumeric characters (`a-zA-Z0-9`).\n"
" Returns `\"\"` when no characters match.\n"
"\n"
" ```gleam\n"
" let assert Ok(#(s, rest)) = bitty.run_partial(string.alphanumeric(), on: <<\"abc123!!\">>)\n"
" assert s == \"abc123\"\n"
" assert rest == <<\"!!\">>\n"
" ```\n"
).
-spec alphanumeric() -> bitty:parser(binary()).
alphanumeric() ->
take_while(fun is_alphanumeric/1).
-file("src/bitty/string.gleam", 60).
?DOC(
" Parse one or more ASCII alphanumeric characters (`a-zA-Z0-9`).\n"
" Fails if the first character is not alphanumeric.\n"
"\n"
" ```gleam\n"
" let assert Ok(#(s, rest)) = bitty.run_partial(string.alphanumeric1(), on: <<\"abc123!!\">>)\n"
" assert s == \"abc123\"\n"
" assert rest == <<\"!!\">>\n"
" ```\n"
).
-spec alphanumeric1() -> bitty:parser(binary()).
alphanumeric1() ->
take_while1(fun is_alphanumeric/1).
-file("src/bitty/string.gleam", 556).
-spec is_hex_digit(binary()) -> boolean().
is_hex_digit(C) ->
case byte_of(C) of
{ok, B} ->
(((B >= 16#30) andalso (B =< 16#39)) orelse ((B >= 16#41) andalso (B
=< 16#46)))
orelse ((B >= 16#61) andalso (B =< 16#66));
{error, _} ->
false
end.
-file("src/bitty/string.gleam", 112).
?DOC(
" Parse zero or more ASCII hexadecimal digits (`0-9a-fA-F`).\n"
" Returns `\"\"` when no characters match.\n"
"\n"
" ```gleam\n"
" let assert Ok(#(s, rest)) = bitty.run_partial(string.hex_digit(), on: <<\"deadBEEF!\">>)\n"
" assert s == \"deadBEEF\"\n"
" assert rest == <<\"!\">>\n"
" ```\n"
).
-spec hex_digit() -> bitty:parser(binary()).
hex_digit() ->
take_while(fun is_hex_digit/1).
-file("src/bitty/string.gleam", 124).
?DOC(
" Parse one or more ASCII hexadecimal digits (`0-9a-fA-F`).\n"
" Fails if the first character is not a hex digit.\n"
"\n"
" ```gleam\n"
" let assert Ok(#(s, rest)) = bitty.run_partial(string.hex_digit1(), on: <<\"deadBEEF!\">>)\n"
" assert s == \"deadBEEF\"\n"
" assert rest == <<\"!\">>\n"
" ```\n"
).
-spec hex_digit1() -> bitty:parser(binary()).
hex_digit1() ->
take_while1(fun is_hex_digit/1).
-file("src/bitty/string.gleam", 566).
-spec is_space(binary()) -> boolean().
is_space(C) ->
(C =:= <<" "/utf8>>) orelse (C =:= <<"\t"/utf8>>).
-file("src/bitty/string.gleam", 136).
?DOC(
" Parse zero or more spaces and tabs.\n"
" Returns `\"\"` when no characters match.\n"
"\n"
" ```gleam\n"
" let assert Ok(#(s, rest)) = bitty.run_partial(string.space(), on: <<\" \\thello\">>)\n"
" assert s == \" \\t\"\n"
" assert rest == <<\"hello\">>\n"
" ```\n"
).
-spec space() -> bitty:parser(binary()).
space() ->
take_while(fun is_space/1).
-file("src/bitty/string.gleam", 148).
?DOC(
" Parse one or more spaces and tabs.\n"
" Fails if the first character is not a space or tab.\n"
"\n"
" ```gleam\n"
" let assert Ok(#(s, rest)) = bitty.run_partial(string.space1(), on: <<\" \\thello\">>)\n"
" assert s == \" \\t\"\n"
" assert rest == <<\"hello\">>\n"
" ```\n"
).
-spec space1() -> bitty:parser(binary()).
space1() ->
take_while1(fun is_space/1).
-file("src/bitty/string.gleam", 570).
-spec is_multispace(binary()) -> boolean().
is_multispace(C) ->
((((C =:= <<" "/utf8>>) orelse (C =:= <<"\t"/utf8>>)) orelse (C =:= <<"\n"/utf8>>))
orelse (C =:= <<"\r"/utf8>>))
orelse (C =:= <<"\r\n"/utf8>>).
-file("src/bitty/string.gleam", 159).
?DOC(
" Parse zero or more whitespace characters (space, tab, `\\r`, `\\n`).\n"
" Returns `\"\"` when no characters match.\n"
"\n"
" ```gleam\n"
" let assert Ok(s) = bitty.run(string.multispace(), on: <<\" \\t\\n\">>)\n"
" assert s == \" \\t\\n\"\n"
" ```\n"
).
-spec multispace() -> bitty:parser(binary()).
multispace() ->
take_while(fun is_multispace/1).
-file("src/bitty/string.gleam", 171).
?DOC(
" Parse one or more whitespace characters (space, tab, `\\r`, `\\n`).\n"
" Fails if the first character is not whitespace.\n"
"\n"
" ```gleam\n"
" let assert Ok(#(s, rest)) = bitty.run_partial(string.multispace1(), on: <<\" \\t\\nhello\">>)\n"
" assert s == \" \\t\\n\"\n"
" assert rest == <<\"hello\">>\n"
" ```\n"
).
-spec multispace1() -> bitty:parser(binary()).
multispace1() ->
take_while1(fun is_multispace/1).