bitty/string

String-level parsers for reading, matching, and scanning UTF-8 text. These parsers decode graphemes on the fly. Use bitty/bytes for raw byte operations and bitty/bits for sub-byte access before returning to string parsing.

Values

pub fn alpha() -> bitty.Parser(String)

Parse zero or more ASCII alphabetic characters (a-zA-Z). Returns "" when no characters match.

let assert Ok(#(s, rest)) = bitty.run_partial(string.alpha(), on: <<"abcXYZ123">>)
assert s == "abcXYZ"
assert rest == <<"123">>
pub fn alpha1() -> bitty.Parser(String)

Parse one or more ASCII alphabetic characters (a-zA-Z). Fails if the first character is not alphabetic.

let assert Ok(#(s, rest)) = bitty.run_partial(string.alpha1(), on: <<"abcXYZ123">>)
assert s == "abcXYZ"
assert rest == <<"123">>
pub fn alphanumeric() -> bitty.Parser(String)

Parse zero or more ASCII alphanumeric characters (a-zA-Z0-9). Returns "" when no characters match.

let assert Ok(#(s, rest)) = bitty.run_partial(string.alphanumeric(), on: <<"abc123!!">>)
assert s == "abc123"
assert rest == <<"!!">>
pub fn alphanumeric1() -> bitty.Parser(String)

Parse one or more ASCII alphanumeric characters (a-zA-Z0-9). Fails if the first character is not alphanumeric.

let assert Ok(#(s, rest)) = bitty.run_partial(string.alphanumeric1(), on: <<"abc123!!">>)
assert s == "abc123"
assert rest == <<"!!">>
pub fn crlf() -> bitty.Parser(Nil)

Match a carriage return + line feed sequence (\r\n).

let assert Ok(#(_, rest)) = bitty.run_partial(string.crlf(), on: <<"\r\nhello">>)
assert rest == <<"hello">>
pub fn digit() -> bitty.Parser(String)

Parse zero or more ASCII decimal digits (0-9). Returns "" when no characters match.

let assert Ok(#(s, rest)) = bitty.run_partial(string.digit(), on: <<"42abc">>)
assert s == "42"
assert rest == <<"abc">>
pub fn digit1() -> bitty.Parser(String)

Parse one or more ASCII decimal digits (0-9). Fails if the first character is not a digit.

let assert Ok(#(s, rest)) = bitty.run_partial(string.digit1(), on: <<"42abc">>)
assert s == "42"
assert rest == <<"abc">>
pub fn fixed(byte_len: Int) -> bitty.Parser(String)

Parse a fixed-width string field, trimming trailing NUL bytes and spaces. Useful for binary formats that pad string fields to a fixed size.

let assert Ok(s) = bitty.run(string.fixed(5), on: <<"hi", 0x00, 0x00, 0x00>>)
assert s == "hi"
pub fn grapheme() -> bitty.Parser(String)

Parse a single UTF-8 grapheme and return it as a one-character string. Handles multi-byte and multi-codepoint grapheme clusters.

let assert Ok(c) = bitty.run(string.grapheme(), on: <<"A">>)
assert c == "A"
pub fn grapheme_if(
  predicate: fn(String) -> Bool,
) -> bitty.Parser(String)

Parse a single UTF-8 grapheme that satisfies the given predicate. Fails if the grapheme doesn’t match or if the input is not valid UTF-8.

let parser = string.grapheme_if(fn(c) { c == "A" })
let assert Ok(c) = bitty.run(parser, on: <<"A">>)
assert c == "A"
pub fn hex_digit() -> bitty.Parser(String)

Parse zero or more ASCII hexadecimal digits (0-9a-fA-F). Returns "" when no characters match.

let assert Ok(#(s, rest)) = bitty.run_partial(string.hex_digit(), on: <<"deadBEEF!">>)
assert s == "deadBEEF"
assert rest == <<"!">>
pub fn hex_digit1() -> bitty.Parser(String)

Parse one or more ASCII hexadecimal digits (0-9a-fA-F). Fails if the first character is not a hex digit.

let assert Ok(#(s, rest)) = bitty.run_partial(string.hex_digit1(), on: <<"deadBEEF!">>)
assert s == "deadBEEF"
assert rest == <<"!">>
pub fn integer() -> bitty.Parser(Int)

Parse one or more ASCII digits and convert them to an Int. Fails if no digits are found or if parsing fails.

let assert Ok(#(n, rest)) = bitty.run_partial(string.integer(), on: <<"42abc">>)
assert n == 42
assert rest == <<"abc">>
pub fn line_ending() -> bitty.Parser(Nil)

Match a line ending: either \r\n or \n. Tries \r\n first to avoid partial matches.

let assert Ok(#(_, rest)) = bitty.run_partial(string.line_ending(), on: <<"\r\nhello">>)
assert rest == <<"hello">>
pub fn literal(expected: String) -> bitty.Parser(Nil)

Match an exact string literal at the current position and consume it. Returns Nil on success; fails if the bytes don’t match.

let assert Ok(Nil) = bitty.run(string.literal("GET"), on: <<"GET">>)
pub fn multispace() -> bitty.Parser(String)

Parse zero or more whitespace characters (space, tab, \r, \n). Returns "" when no characters match.

let assert Ok(s) = bitty.run(string.multispace(), on: <<" \t\n">>)
assert s == " \t\n"
pub fn multispace1() -> bitty.Parser(String)

Parse one or more whitespace characters (space, tab, \r, \n). Fails if the first character is not whitespace.

let assert Ok(#(s, rest)) = bitty.run_partial(string.multispace1(), on: <<" \t\nhello">>)
assert s == " \t\n"
assert rest == <<"hello">>
pub fn newline() -> bitty.Parser(Nil)

Match a newline character (\n).

let assert Ok(#(_, rest)) = bitty.run_partial(string.newline(), on: <<"\nhello">>)
assert rest == <<"hello">>
pub fn not_line_ending() -> bitty.Parser(String)

Consume characters until a \r or \n is found. The line ending itself is not consumed.

let assert Ok(#(s, rest)) = bitty.run_partial(string.not_line_ending(), on: <<"hello\nworld">>)
assert s == "hello"
assert rest == <<"\nworld">>
pub fn null_terminated() -> bitty.Parser(String)

Parse a null-terminated (C-style) string. Consumes graphemes until a NUL byte (0x00) is found, then skips the NUL. Fails if no NUL is present.

let assert Ok(s) = bitty.run(string.null_terminated(), on: <<"hi", 0x00>>)
assert s == "hi"
pub fn space() -> bitty.Parser(String)

Parse zero or more spaces and tabs. Returns "" when no characters match.

let assert Ok(#(s, rest)) = bitty.run_partial(string.space(), on: <<"  \thello">>)
assert s == "  \t"
assert rest == <<"hello">>
pub fn space1() -> bitty.Parser(String)

Parse one or more spaces and tabs. Fails if the first character is not a space or tab.

let assert Ok(#(s, rest)) = bitty.run_partial(string.space1(), on: <<"  \thello">>)
assert s == "  \t"
assert rest == <<"hello">>
pub fn tab() -> bitty.Parser(Nil)

Match a tab character (\t).

let assert Ok(#(_, rest)) = bitty.run_partial(string.tab(), on: <<"\thello">>)
assert rest == <<"hello">>
pub fn take_graphemes(count: Int) -> bitty.Parser(String)

Parse exactly count UTF-8 graphemes and return them as a string. Unlike utf8 which counts bytes, this counts logical characters.

let assert Ok(s) = bitty.run(string.take_graphemes(2), on: <<"café">>)
assert s == "ca"
pub fn take_until(
  predicate: fn(String) -> Bool,
) -> bitty.Parser(String)

Consume graphemes until the predicate matches, returning everything before the match. The matching grapheme is not consumed. Returns "" when the first grapheme matches.

let parser = string.take_until(fn(c) { c == " " })
let assert Ok(#(s, rest)) = bitty.run_partial(parser, on: <<"hello world">>)
assert s == "hello"
assert rest == <<" world">>
pub fn take_while(
  predicate: fn(String) -> Bool,
) -> bitty.Parser(String)

Consume graphemes while the predicate holds, returning them as a string. Returns "" when zero graphemes match.

let parser = string.take_while(fn(c) { c != " " })
let assert Ok(#(s, rest)) = bitty.run_partial(parser, on: <<"hello world">>)
assert s == "hello"
assert rest == <<" world">>
pub fn take_while1(
  predicate: fn(String) -> Bool,
) -> bitty.Parser(String)

Like take_while but requires at least one matching grapheme. Fails if the first grapheme doesn’t satisfy the predicate.

let parser = string.take_while1(fn(c) { c == "a" })
let assert Ok(#(s, _rest)) = bitty.run_partial(parser, on: <<"aab">>)
assert s == "aa"
pub fn utf8(byte_len: Int) -> bitty.Parser(String)

Parse exactly byte_len bytes and decode them as a UTF-8 string. Fails if the bytes are not valid UTF-8 or if there is insufficient input.

let assert Ok(s) = bitty.run(string.utf8(5), on: <<"hello">>)
assert s == "hello"
Search Document