Skip to content

Commit

Permalink
fix(char)!: Merge u8, etc parsers into dec_uint / dec_int
Browse files Browse the repository at this point in the history
The exact type only has overflow properties and does not otherwise
affect the parsing of the grammar.

BREAKING CHANGE: Compared to `nom8`, `character::u8` and friends are
replaced by `character::dec_uint` and `character::dec_int`.
  • Loading branch information
epage committed Feb 6, 2023
1 parent eb42ad9 commit f8df6fa
Show file tree
Hide file tree
Showing 5 changed files with 258 additions and 53 deletions.
14 changes: 14 additions & 0 deletions src/character/complete.rs
Original file line number Diff line number Diff line change
Expand Up @@ -831,6 +831,13 @@ macro_rules! ints {
/// will parse a number in text form to a number
///
/// *Complete version*: can parse until the end of input.
///
/// **WARNING:** Deprecated, replaced with
/// [`winnow::character::dec_uint`][crate::character::dec_int]
#[deprecated(
since = "0.1.0",
note = "Replaced with `winnow::character::dec_int`"
)]
pub fn $t<T, E: ParseError<T>>(input: T) -> IResult<T, $t, E>
where
T: Input,
Expand Down Expand Up @@ -880,6 +887,13 @@ macro_rules! uints {
/// will parse a number in text form to a number
///
/// *Complete version*: can parse until the end of input.
///
/// **WARNING:** Deprecated, replaced with
/// [`winnow::character::dec_uint`][crate::character::dec_uint]
#[deprecated(
since = "0.1.0",
note = "Replaced with `winnow::character::dec_uint`"
)]
pub fn $t<T, E: ParseError<T>>(input: T) -> IResult<T, $t, E>
where
T: Input,
Expand Down
285 changes: 237 additions & 48 deletions src/character/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ mod tests;

use crate::lib::std::ops::{Add, Shl};

use crate::combinator::opt;
use crate::error::ParseError;
use crate::error::{ErrMode, ErrorKind, Needed};
use crate::input::Compare;
Expand Down Expand Up @@ -888,61 +889,249 @@ where
}
}

#[doc(hidden)]
macro_rules! ints {
($($t:tt)+) => {
$(
/// will parse a number in text form to a number
///
/// *Complete version*: can parse until the end of input.
///
/// *Streaming version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data.
#[inline(always)]
pub fn $t<I, E: ParseError<I>, const STREAMING: bool>(input: I) -> IResult<I, $t, E>
where
I: InputIsStreaming<STREAMING>,
I: Input,
<I as Input>::Token: AsChar + Copy,
{
if STREAMING {
streaming::$t(input)
} else {
complete::$t(input)
}
}
)+
/// Decode a decimal unsigned integer
///
/// *Complete version*: can parse until the end of input.
///
/// *Streaming version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data.
pub fn dec_uint<I, O, E: ParseError<I>, const STREAMING: bool>(input: I) -> IResult<I, O, E>
where
I: InputIsStreaming<STREAMING>,
I: Input,
<I as Input>::Token: AsChar + Copy,
O: Uint,
{
let i = input.clone();

if i.input_len() == 0 {
if STREAMING {
return Err(ErrMode::Incomplete(Needed::new(1)));
} else {
return Err(ErrMode::from_error_kind(input, ErrorKind::Digit));
}
}

let mut value = O::default();
for (offset, c) in i.iter_offsets() {
match c.as_char().to_digit(10) {
Some(d) => match value.checked_mul(10, sealed::SealedMarker).and_then(|v| {
let d = d as u8;
v.checked_add(d, sealed::SealedMarker)
}) {
None => return Err(ErrMode::from_error_kind(input, ErrorKind::Digit)),
Some(v) => value = v,
},
None => {
if offset == 0 {
return Err(ErrMode::from_error_kind(input, ErrorKind::Digit));
} else {
return Ok((i.next_slice(offset).0, value));
}
}
}
}

if STREAMING {
Err(ErrMode::Incomplete(Needed::new(1)))
} else {
Ok((i.next_slice(i.input_len()).0, value))
}
}

ints! { i8 i16 i32 i64 i128 }
/// Metadata for parsing unsigned integers
pub trait Uint: Default {
#[doc(hidden)]
fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self>;
#[doc(hidden)]
fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self>;
}

#[doc(hidden)]
macro_rules! uints {
($($t:tt)+) => {
$(
/// will parse a number in text form to a number
///
/// *Complete version*: can parse until the end of input.
///
/// *Streaming version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data.
#[inline(always)]
pub fn $t<I, E: ParseError<I>, const STREAMING: bool>(input: I) -> IResult<I, $t, E>
where
I: InputIsStreaming<STREAMING>,
I: Input,
<I as Input>::Token: AsChar,
{
if STREAMING {
streaming::$t(input)
} else {
complete::$t(input)
}
}
)+
impl Uint for u8 {
fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
self.checked_mul(by as Self)
}
fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
self.checked_add(by as Self)
}
}

impl Uint for u16 {
fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
self.checked_mul(by as Self)
}
fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
self.checked_add(by as Self)
}
}

impl Uint for u32 {
fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
self.checked_mul(by as Self)
}
fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
self.checked_add(by as Self)
}
}

impl Uint for u64 {
fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
self.checked_mul(by as Self)
}
fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
self.checked_add(by as Self)
}
}

impl Uint for u128 {
fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
self.checked_mul(by as Self)
}
fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
self.checked_add(by as Self)
}
}

impl Uint for i8 {
fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
self.checked_mul(by as Self)
}
fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
self.checked_add(by as Self)
}
}

impl Uint for i16 {
fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
self.checked_mul(by as Self)
}
fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
self.checked_add(by as Self)
}
}

impl Uint for i32 {
fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
self.checked_mul(by as Self)
}
fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
self.checked_add(by as Self)
}
}

impl Uint for i64 {
fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
self.checked_mul(by as Self)
}
fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
self.checked_add(by as Self)
}
}

impl Uint for i128 {
fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
self.checked_mul(by as Self)
}
fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
self.checked_add(by as Self)
}
}

/// Decode a decimal signed integer
///
/// *Complete version*: can parse until the end of input.
///
/// *Streaming version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data.
pub fn dec_int<I, O, E: ParseError<I>, const STREAMING: bool>(input: I) -> IResult<I, O, E>
where
I: InputIsStreaming<STREAMING>,
I: Input,
<I as Input>::Token: AsChar + Copy,
O: Int,
{
let i = input.clone();

fn sign(token: impl AsChar) -> bool {
let token = token.as_char();
token == '+' || token == '-'
}
let (i, sign) = opt(crate::bytes::one_of(sign).map(AsChar::as_char))
.map(|c| c != Some('-'))
.parse_next(i)?;

if i.input_len() == 0 {
if STREAMING {
return Err(ErrMode::Incomplete(Needed::new(1)));
} else {
return Err(ErrMode::from_error_kind(input, ErrorKind::Digit));
}
}

let mut value = O::default();
for (offset, c) in i.iter_offsets() {
match c.as_char().to_digit(10) {
Some(d) => match value.checked_mul(10, sealed::SealedMarker).and_then(|v| {
let d = d as u8;
if sign {
v.checked_add(d, sealed::SealedMarker)
} else {
v.checked_sub(d, sealed::SealedMarker)
}
}) {
None => return Err(ErrMode::from_error_kind(input, ErrorKind::Digit)),
Some(v) => value = v,
},
None => {
if offset == 0 {
return Err(ErrMode::from_error_kind(input, ErrorKind::Digit));
} else {
return Ok((i.next_slice(offset).0, value));
}
}
}
}

if STREAMING {
Err(ErrMode::Incomplete(Needed::new(1)))
} else {
Ok((i.next_slice(i.input_len()).0, value))
}
}

/// Metadata for parsing signed integers
pub trait Int: Uint {
#[doc(hidden)]
fn checked_sub(self, by: u8, _: sealed::SealedMarker) -> Option<Self>;
}

impl Int for i8 {
fn checked_sub(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
self.checked_sub(by as Self)
}
}

uints! { u8 u16 u32 u64 u128 }
impl Int for i16 {
fn checked_sub(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
self.checked_sub(by as Self)
}
}

impl Int for i32 {
fn checked_sub(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
self.checked_sub(by as Self)
}
}

impl Int for i64 {
fn checked_sub(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
self.checked_sub(by as Self)
}
}

impl Int for i128 {
fn checked_sub(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
self.checked_sub(by as Self)
}
}

/// Decode a variable-width hexadecimal integer.
///
Expand Down
8 changes: 4 additions & 4 deletions src/character/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -424,14 +424,14 @@ mod complete {
#[test]
fn ints(s in "\\PC*") {
let res1 = digit_to_i16(&s);
let res2 = i16(s.as_str());
let res2 = dec_int(s.as_str());
assert_eq!(res1, res2);
}

#[test]
fn uints(s in "\\PC*") {
let res1 = digit_to_u32(&s);
let res2 = u32(s.as_str());
let res2 = dec_uint(s.as_str());
assert_eq!(res1, res2);
}
}
Expand Down Expand Up @@ -1431,14 +1431,14 @@ mod streaming {
#[test]
fn ints(s in "\\PC*") {
let res1 = digit_to_i16(Streaming(&s));
let res2 = i16(Streaming(s.as_str()));
let res2 = dec_int(Streaming(s.as_str()));
assert_eq!(res1, res2);
}

#[test]
fn uints(s in "\\PC*") {
let res1 = digit_to_u32(Streaming(&s));
let res2 = u32(Streaming(s.as_str()));
let res2 = dec_uint(Streaming(s.as_str()));
assert_eq!(res1, res2);
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/combinator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,8 @@
//! - [`digit0`][crate::character::digit0]: Recognizes zero or more numerical characters: `[0-9]`. [`digit1`][crate::character::digit1] does the same but returns at least one character
//! - [`float`][crate::character::float]: Parse a floating point number in a byte string
//! - [`hex_digit0`][crate::character::hex_digit0]: Recognizes zero or more hexadecimal numerical characters: `[0-9A-Fa-f]`. [`hex_digit1`][crate::character::hex_digit1] does the same but returns at least one character
//! - [`dec_int`][crate::character::dec_uint]: Decode a variable-width, decimal signed integer
//! - [`dec_uint`][crate::character::dec_uint]: Decode a variable-width, decimal unsigned integer
//! - [`hex_uint`][crate::character::hex_uint]: Decode a variable-width, hexadecimal integer
//! - [`line_ending`][crate::character::line_ending]: Recognizes an end of line (both `\n` and `\r\n`)
//! - [`multispace0`][crate::character::multispace0]: Recognizes zero or more spaces, tabs, carriage returns and line feeds. [`multispace1`][crate::character::multispace1] does the same but returns at least one character
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -492,7 +492,7 @@ pub mod _tutorial;
///
/// fn parse_data(input: &str) -> IResult<&str, u64> {
/// // ...
/// # winnow::character::u64(input)
/// # winnow::character::dec_uint(input)
/// }
///
/// fn main() {
Expand Down

0 comments on commit f8df6fa

Please sign in to comment.