Skip to content

Commit

Permalink
Add is_bin_digit method (#1658)
Browse files Browse the repository at this point in the history
  • Loading branch information
Chasing1020 authored Oct 21, 2023
1 parent 95d9926 commit 0c5d5b9
Show file tree
Hide file tree
Showing 7 changed files with 228 additions and 1 deletion.
2 changes: 2 additions & 0 deletions doc/choosing_a_combinator.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ Use these functions with a combinator like `take_while`:
- [`is_digit`](https://docs.rs/nom/latest/nom/character/fn.is_digit.html): Tests if byte is ASCII digit: `[0-9]`
- [`is_hex_digit`](https://docs.rs/nom/latest/nom/character/fn.is_hex_digit.html): Tests if byte is ASCII hex digit: `[0-9A-Fa-f]`
- [`is_oct_digit`](https://docs.rs/nom/latest/nom/character/fn.is_oct_digit.html): Tests if byte is ASCII octal digit: `[0-7]`
- [`is_bin_digit`](https://docs.rs/nom/latest/nom/character/fn.is_bin_digit.html): Tests if byte is ASCII binary digit: `[0-1]`
- [`is_space`](https://docs.rs/nom/latest/nom/character/fn.is_space.html): Tests if byte is ASCII space or tab: `[ \t]`
- [`is_newline`](https://docs.rs/nom/latest/nom/character/fn.is_newline.html): Tests if byte is ASCII newline: `[\n]`

Expand All @@ -150,6 +151,7 @@ Alternatively there are ready to use functions:
- [`newline`](https://docs.rs/nom/latest/nom/character/complete/fn.newline.html): Matches a newline character `\n`
- [`not_line_ending`](https://docs.rs/nom/latest/nom/character/complete/fn.not_line_ending.html): Recognizes a string of any char except `\r` or `\n`
- [`oct_digit0`](https://docs.rs/nom/latest/nom/character/complete/fn.oct_digit0.html): Recognizes zero or more octal characters: `[0-7]`. [`oct_digit1`](https://docs.rs/nom/latest/nom/character/complete/fn.oct_digit1.html) does the same but returns at least one character
- [`bin_digit0`](https://docs.rs/nom/latest/nom/character/complete/fn.bin_digit0.html): Recognizes zero or more binary characters: `[0-1]`. [`bin_digit1`](https://docs.rs/nom/latest/nom/character/complete/fn.bin_digit1.html) does the same but returns at least one character
- [`rest`](https://docs.rs/nom/latest/nom/combinator/fn.rest.html): Return the remaining input
- [`rest_len`](https://docs.rs/nom/latest/nom/combinator/fn.rest_len.html): Return the length of the remaining input
- [`space0`](https://docs.rs/nom/latest/nom/character/complete/fn.space0.html): Recognizes zero or more spaces and tabs. [`space1`](https://docs.rs/nom/latest/nom/character/complete/fn.space1.html) does the same but returns at least one character
Expand Down
8 changes: 7 additions & 1 deletion src/bytes/tests.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::character::is_alphabetic;
use crate::character::streaming::{
alpha1 as alpha, alphanumeric1 as alphanumeric, digit1 as digit, hex_digit1 as hex_digit,
multispace1 as multispace, oct_digit1 as oct_digit, space1 as space,
multispace1 as multispace, oct_digit1 as oct_digit, bin_digit1 as bin_digit, space1 as space,
};
use crate::error::ErrorKind;
use crate::internal::{Err, IResult, Needed};
Expand Down Expand Up @@ -336,6 +336,12 @@ fn recognize() {
let rod = yod(&b"1234567;"[..]);
assert_eq!(rod, Ok((semicolon, &b"1234567"[..])));

fn ybd(i: &[u8]) -> IResult<&[u8], &[u8]> {
recognize(bin_digit)(i)

Check failure on line 340 in src/bytes/tests.rs

View workflow job for this annotation

GitHub Actions / Bench

expected function, found `impl internal::Parser<_, Output = _, Error = _>`

Check failure on line 340 in src/bytes/tests.rs

View workflow job for this annotation

GitHub Actions / Test (nightly)

expected function, found `impl internal::Parser<_, Output = _, Error = _>`

Check failure on line 340 in src/bytes/tests.rs

View workflow job for this annotation

GitHub Actions / Test (stable, --no-default-features --features "alloc")

expected function, found `impl internal::Parser<_, Output = _, Error = _>`

Check failure on line 340 in src/bytes/tests.rs

View workflow job for this annotation

GitHub Actions / Test (beta)

expected function, found `impl internal::Parser<_, Output = _, Error = _>`

Check failure on line 340 in src/bytes/tests.rs

View workflow job for this annotation

GitHub Actions / Coverage

expected function, found `impl internal::Parser<_, Output = _, Error = _>`
}
let rbd = ybd(&b"101010;"[..]);
assert_eq!(rbd, Ok((semicolon, &b"101010"[..])));

fn yan(i: &[u8]) -> IResult<&[u8], &[u8]> {
recognize(alphanumeric).parse(i)
}
Expand Down
93 changes: 93 additions & 0 deletions src/character/complete.rs
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,56 @@ where
input.split_at_position1_complete(|item| !item.is_oct_digit(), ErrorKind::OctDigit)
}

/// Recognizes zero or more binary characters: 0-1
///
/// *Complete version*: Will return the whole input if no terminating token is found (a non binary
/// digit character).
/// # Example
///
/// ```
/// # use nom::{Err, error::ErrorKind, IResult, Needed};
/// # use nom::character::complete::bin_digit0;
/// fn parser(input: &str) -> IResult<&str, &str> {
/// bin_digit0(input)
/// }
///
/// assert_eq!(parser("013a"), Ok(("3a", "01")));
/// assert_eq!(parser("a013"), Ok(("a013", "")));
/// assert_eq!(parser(""), Ok(("", "")));
/// ```
pub fn bin_digit0<T, E: ParseError<T>>(input: T) -> IResult<T, T, E>
where
T: Input,
<T as Input>::Item: AsChar,
{
input.split_at_position_complete(|item| !item.is_bin_digit())
}

/// Recognizes one or more binary characters: 0-1
///
/// *Complete version*: Will return an error if there's not enough input data,
/// or the whole input if no terminating token is found (a non binary digit character).
/// # Example
///
/// ```
/// # use nom::{Err, error::{Error, ErrorKind}, IResult, Needed};
/// # use nom::character::complete::bin_digit1;
/// fn parser(input: &str) -> IResult<&str, &str> {
/// bin_digit1(input)
/// }
///
/// assert_eq!(parser("013a"), Ok(("3a", "01")));
/// assert_eq!(parser("a013"), Err(Err::Error(Error::new("a013", ErrorKind::BinDigit))));
/// assert_eq!(parser(""), Err(Err::Error(Error::new("", ErrorKind::BinDigit))));
/// ```
pub fn bin_digit1<T, E: ParseError<T>>(input: T) -> IResult<T, T, E>
where
T: Input,
<T as Input>::Item: AsChar,
{
input.split_at_position1_complete(|item| !item.is_bin_digit(), ErrorKind::BinDigit)
}

/// Recognizes zero or more ASCII numerical and alphabetic characters: 0-9, a-z, A-Z
///
/// *Complete version*: Will return the whole input if no terminating token is found (a non
Expand Down Expand Up @@ -864,6 +914,13 @@ mod tests {
assert_eq!(oct_digit1::<_, (_, ErrorKind)>(b), Ok((empty, b)));
assert_eq!(oct_digit1(c), Err(Err::Error((c, ErrorKind::OctDigit))));
assert_eq!(oct_digit1(d), Err(Err::Error((d, ErrorKind::OctDigit))));
assert_eq!(bin_digit1(a), Err(Err::Error((a, ErrorKind::BinDigit))));
assert_eq!(
bin_digit1::<_, (_, ErrorKind)>(b),
Ok((&b"234"[..], &b"1"[..]))
);
assert_eq!(bin_digit1(c), Err(Err::Error((c, ErrorKind::BinDigit))));
assert_eq!(bin_digit1(d), Err(Err::Error((d, ErrorKind::BinDigit))));
assert_eq!(alphanumeric1::<_, (_, ErrorKind)>(a), Ok((empty, a)));
//assert_eq!(fix_error!(b,(), alphanumeric), Ok((empty, b)));
assert_eq!(alphanumeric1::<_, (_, ErrorKind)>(c), Ok((empty, c)));
Expand Down Expand Up @@ -901,6 +958,13 @@ mod tests {
assert_eq!(oct_digit1::<_, (_, ErrorKind)>(b), Ok((empty, b)));
assert_eq!(oct_digit1(c), Err(Err::Error((c, ErrorKind::OctDigit))));
assert_eq!(oct_digit1(d), Err(Err::Error((d, ErrorKind::OctDigit))));
assert_eq!(bin_digit1(a), Err(Err::Error((a, ErrorKind::BinDigit))));
assert_eq!(
bin_digit1::<_, (_, ErrorKind)>(b),
Ok(("234", "1"))
);
assert_eq!(bin_digit1(c), Err(Err::Error((c, ErrorKind::BinDigit))));
assert_eq!(bin_digit1(d), Err(Err::Error((d, ErrorKind::BinDigit))));
assert_eq!(alphanumeric1::<_, (_, ErrorKind)>(a), Ok((empty, a)));
//assert_eq!(fix_error!(b,(), alphanumeric), Ok((empty, b)));
assert_eq!(alphanumeric1::<_, (_, ErrorKind)>(c), Ok((empty, c)));
Expand Down Expand Up @@ -960,6 +1024,12 @@ mod tests {
}
_ => panic!("wrong return type in offset test for oct_digit"),
}
match bin_digit1::<_, (_, ErrorKind)>(f) {
Ok((i, _)) => {
assert_eq!(f.offset(i) + i.len(), f.len());
}
_ => panic!("wrong return type in offset test for bin_digit"),
}
}

#[test]
Expand Down Expand Up @@ -1066,6 +1136,29 @@ mod tests {
assert!(!crate::character::is_oct_digit(b'\x60'));
}

#[test]
fn bin_digit_test() {
let i = &b"101010;"[..];
assert_parse!(bin_digit1(i), Ok((&b";"[..], &i[..i.len() - 1])));

let i = &b"2"[..];
assert_parse!(
bin_digit1(i),
Err(Err::Error(error_position!(i, ErrorKind::BinDigit)))
);

assert!(crate::character::is_bin_digit(b'0'));
assert!(crate::character::is_bin_digit(b'1'));
assert!(!crate::character::is_bin_digit(b'8'));
assert!(!crate::character::is_bin_digit(b'9'));
assert!(!crate::character::is_bin_digit(b'a'));
assert!(!crate::character::is_bin_digit(b'A'));
assert!(!crate::character::is_bin_digit(b'/'));
assert!(!crate::character::is_bin_digit(b':'));
assert!(!crate::character::is_bin_digit(b'@'));
assert!(!crate::character::is_bin_digit(b'\x60'));
}

#[test]
fn full_line_windows() {
use crate::sequence::pair;
Expand Down
16 changes: 16 additions & 0 deletions src/character/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,22 @@ pub fn is_oct_digit(chr: u8) -> bool {
matches!(chr, 0x30..=0x37)
}

/// Tests if byte is ASCII binary digit: 0-1
///
/// # Example
///
/// ```
/// # use nom::character::is_bin_digit;
/// assert_eq!(is_bin_digit(b'a'), false);
/// assert_eq!(is_bin_digit(b'2'), false);
/// assert_eq!(is_bin_digit(b'0'), true);
/// assert_eq!(is_bin_digit(b'1'), true);
/// ```
#[inline]
pub fn is_bin_digit(chr: u8) -> bool {
matches!(chr, 0x30..=0x31)
}

/// Tests if byte is ASCII alphanumeric: A-Z, a-z, 0-9
///
/// # Example
Expand Down
89 changes: 89 additions & 0 deletions src/character/streaming.rs
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,48 @@ where
input.split_at_position1(|item| !item.is_oct_digit(), ErrorKind::OctDigit)
}

/// Recognizes zero or more binary characters: 0-1
///
/// *Streaming version*: Will return `Err(nom::Err::Incomplete(_))` if there's not enough input data,
/// or if no terminating token is found (a non binary digit character).
/// # Example
///
/// ```
/// # use nom::{Err, error::ErrorKind, IResult, Needed};
/// # use nom::character::streaming::bin_digit0;
/// assert_eq!(bin_digit0::<_, (_, ErrorKind)>("013a"), Ok(("3a", "01")));
/// assert_eq!(bin_digit0::<_, (_, ErrorKind)>("a013"), Ok(("a013", "")));
/// assert_eq!(bin_digit0::<_, (_, ErrorKind)>(""), Err(Err::Incomplete(Needed::new(1))));
/// ```
pub fn bin_digit0<T, E: ParseError<T>>(input: T) -> IResult<T, T, E>
where
T: Input,
<T as Input>::Item: AsChar,
{
input.split_at_position(|item| !item.is_bin_digit())
}

/// Recognizes one or more binary characters: 0-1
///
/// *Streaming version*: Will return `Err(nom::Err::Incomplete(_))` if there's not enough input data,
/// or if no terminating token is found (a non binary digit character).
/// # Example
///
/// ```
/// # use nom::{Err, error::ErrorKind, IResult, Needed};
/// # use nom::character::streaming::bin_digit1;
/// assert_eq!(bin_digit1::<_, (_, ErrorKind)>("013a"), Ok(("3a", "01")));
/// assert_eq!(bin_digit1::<_, (_, ErrorKind)>("a013"), Err(Err::Error(("a013", ErrorKind::BinDigit))));
/// assert_eq!(bin_digit1::<_, (_, ErrorKind)>(""), Err(Err::Incomplete(Needed::new(1))));
/// ```
pub fn bin_digit1<T, E: ParseError<T>>(input: T) -> IResult<T, T, E>
where
T: Input,
<T as Input>::Item: AsChar,
{
input.split_at_position1(|item| !item.is_bin_digit(), ErrorKind::BinDigit)
}

/// Recognizes zero or more ASCII numerical and alphabetic characters: 0-9, a-z, A-Z
///
/// *Streaming version*: Will return `Err(nom::Err::Incomplete(_))` if there's not enough input data,
Expand Down Expand Up @@ -795,6 +837,17 @@ mod tests {
alphanumeric1::<_, (_, ErrorKind)>(a),
Err(Err::Incomplete(Needed::new(1)))
);
assert_eq!(bin_digit1(a), Err(Err::Error((a, ErrorKind::BinDigit))));
assert_eq!(
bin_digit1::<_, (_, ErrorKind)>(b),
Ok((&b"234"[..], &b"1"[..]))
);
assert_eq!(bin_digit1(c), Err(Err::Error((c, ErrorKind::BinDigit))));
assert_eq!(bin_digit1(d), Err(Err::Error((d, ErrorKind::BinDigit))));
assert_eq!(
alphanumeric1::<_, (_, ErrorKind)>(a),
Err(Err::Incomplete(Needed::new(1)))
);
//assert_eq!(fix_error!(b,(), alphanumeric1), Ok((empty, b)));
assert_eq!(
alphanumeric1::<_, (_, ErrorKind)>(c),
Expand Down Expand Up @@ -854,6 +907,13 @@ mod tests {
);
assert_eq!(oct_digit1(c), Err(Err::Error((c, ErrorKind::OctDigit))));
assert_eq!(oct_digit1(d), Err(Err::Error((d, ErrorKind::OctDigit))));
assert_eq!(bin_digit1(a), Err(Err::Error((a, ErrorKind::BinDigit))));
assert_eq!(
bin_digit1::<_, (_, ErrorKind)>(b),
Ok(("234", "1"))
);
assert_eq!(bin_digit1(c), Err(Err::Error((c, ErrorKind::BinDigit))));
assert_eq!(bin_digit1(d), Err(Err::Error((d, ErrorKind::BinDigit))));
assert_eq!(
alphanumeric1::<_, (_, ErrorKind)>(a),
Err(Err::Incomplete(Needed::new(1)))
Expand Down Expand Up @@ -922,6 +982,12 @@ mod tests {
}
_ => panic!("wrong return type in offset test for oct_digit"),
}
match bin_digit1::<_, (_, ErrorKind)>(f) {
Ok((i, _)) => {
assert_eq!(f.offset(i) + i.len(), f.len());
}
_ => panic!("wrong return type in offset test for bin_digit"),
}
}

#[test]
Expand Down Expand Up @@ -1034,6 +1100,29 @@ mod tests {
assert!(!crate::character::is_oct_digit(b'\x60'));
}

#[test]
fn bin_digit_test() {
let i = &b"01;"[..];
assert_parse!(bin_digit1(i), Ok((&b";"[..], &i[..i.len() - 1])));

let i = &b"8"[..];
assert_parse!(
bin_digit1(i),
Err(Err::Error(error_position!(i, ErrorKind::BinDigit)))
);

assert!(crate::character::is_bin_digit(b'0'));
assert!(crate::character::is_bin_digit(b'1'));
assert!(!crate::character::is_bin_digit(b'8'));
assert!(!crate::character::is_bin_digit(b'9'));
assert!(!crate::character::is_bin_digit(b'a'));
assert!(!crate::character::is_bin_digit(b'A'));
assert!(!crate::character::is_bin_digit(b'/'));
assert!(!crate::character::is_bin_digit(b':'));
assert!(!crate::character::is_bin_digit(b'@'));
assert!(!crate::character::is_bin_digit(b'\x60'));
}

#[test]
fn full_line_windows() {
fn take_full_line(i: &[u8]) -> IResult<&[u8], (&[u8], &[u8])> {
Expand Down
3 changes: 3 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,7 @@ pub enum ErrorKind {
Digit,
HexDigit,
OctDigit,
BinDigit,
AlphaNumeric,
Space,
MultiSpace,
Expand Down Expand Up @@ -534,6 +535,7 @@ pub fn error_to_u32(e: &ErrorKind) -> u32 {
ErrorKind::Fail => 75,
ErrorKind::Many => 76,
ErrorKind::Fold => 77,
ErrorKind::BinDigit => 78,
}
}

Expand Down Expand Up @@ -584,6 +586,7 @@ impl ErrorKind {
ErrorKind::ManyMN => "Many(m, n)",
ErrorKind::HexDigit => "Hexadecimal Digit",
ErrorKind::OctDigit => "Octal digit",
ErrorKind::BinDigit => "Binary digit",
ErrorKind::Not => "Negation",
ErrorKind::Permutation => "Permutation",
ErrorKind::ManyTill => "ManyTill",
Expand Down
18 changes: 18 additions & 0 deletions src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -701,6 +701,8 @@ pub trait AsChar: Copy {
fn is_hex_digit(self) -> bool;
/// Tests that self is an octal digit
fn is_oct_digit(self) -> bool;
/// Tests that self is a binary digit
fn is_bin_digit(self) -> bool;
/// Gets the len in bytes for self
fn len(self) -> usize;
}
Expand Down Expand Up @@ -731,6 +733,10 @@ impl AsChar for u8 {
matches!(self, 0x30..=0x37)
}
#[inline]
fn is_bin_digit(self) -> bool {
matches!(self, 0x30..=0x31)
}
#[inline]
fn len(self) -> usize {
1
}
Expand Down Expand Up @@ -761,6 +767,10 @@ impl<'a> AsChar for &'a u8 {
matches!(*self, 0x30..=0x37)
}
#[inline]
fn is_bin_digit(self) -> bool {
matches!(*self, 0x30..=0x31)
}
#[inline]
fn len(self) -> usize {
1
}
Expand Down Expand Up @@ -792,6 +802,10 @@ impl AsChar for char {
self.is_digit(8)
}
#[inline]
fn is_bin_digit(self) -> bool {
self.is_digit(2)
}
#[inline]
fn len(self) -> usize {
self.len_utf8()
}
Expand Down Expand Up @@ -823,6 +837,10 @@ impl<'a> AsChar for &'a char {
self.is_digit(8)
}
#[inline]
fn is_bin_digit(self) -> bool {
self.is_digit(2)
}
#[inline]
fn len(self) -> usize {
self.len_utf8()
}
Expand Down

0 comments on commit 0c5d5b9

Please sign in to comment.