Skip to content

Commit

Permalink
WIP: feat(seq): 'seq' macro for easier skipping
Browse files Browse the repository at this point in the history
Fixes #82
  • Loading branch information
epage committed Apr 27, 2023
1 parent 681bea8 commit 6bdf559
Show file tree
Hide file tree
Showing 4 changed files with 142 additions and 40 deletions.
36 changes: 16 additions & 20 deletions examples/http/parser.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use winnow::sequence::seq;
use winnow::{bytes::take_while1, character::line_ending, multi::many1, IResult, Parser};

pub type Stream<'i> = &'i [u8];
Expand Down Expand Up @@ -51,21 +52,15 @@ fn request(input: Stream<'_>) -> IResult<Stream<'_>, (Request<'_>, Vec<Header<'_
}

fn request_line(input: Stream<'_>) -> IResult<Stream<'_>, Request<'_>> {
let (input, method) = take_while1(is_token).parse_next(input)?;
let (input, _) = take_while1(is_space).parse_next(input)?;
let (input, uri) = take_while1(is_not_space).parse_next(input)?;
let (input, _) = take_while1(is_space).parse_next(input)?;
let (input, version) = http_version(input)?;
let (input, _) = line_ending(input)?;

Ok((
input,
Request {
method,
uri,
version,
},
))
seq!( Request {
method: take_while1(is_token),
_: take_while1(is_space),
uri: take_while1(is_not_space),
_: take_while1(is_space),
version: http_version,
_: line_ending,
})
.parse_next(input)
}

fn http_version(input: Stream<'_>) -> IResult<Stream<'_>, &[u8]> {
Expand All @@ -84,11 +79,12 @@ fn message_header_value(input: Stream<'_>) -> IResult<Stream<'_>, &[u8]> {
}

fn message_header(input: Stream<'_>) -> IResult<Stream<'_>, Header<'_>> {
let (input, name) = take_while1(is_token).parse_next(input)?;
let (input, _) = ':'.parse_next(input)?;
let (input, value) = many1(message_header_value).parse_next(input)?;

Ok((input, Header { name, value }))
seq!(Header {
name: take_while1(is_token),
_: ':',
value: many1(message_header_value),
})
.parse_next(input)
}

#[rustfmt::skip]
Expand Down
35 changes: 15 additions & 20 deletions examples/http/parser_streaming.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,21 +53,15 @@ fn request(input: Stream<'_>) -> IResult<Stream<'_>, (Request<'_>, Vec<Header<'_
}

fn request_line(input: Stream<'_>) -> IResult<Stream<'_>, Request<'_>> {
let (input, method) = take_while1(is_token).parse_next(input)?;
let (input, _) = take_while1(is_space).parse_next(input)?;
let (input, uri) = take_while1(is_not_space).parse_next(input)?;
let (input, _) = take_while1(is_space).parse_next(input)?;
let (input, version) = http_version(input)?;
let (input, _) = line_ending(input)?;

Ok((
input,
Request {
method,
uri,
version,
},
))
seq!( Request {
method: take_while1(is_token),
_: take_while1(is_space),
uri: take_while1(is_not_space),
_: take_while1(is_space),
version: http_version,
_: line_ending,
})
.parse_next(input)
}

fn http_version(input: Stream<'_>) -> IResult<Stream<'_>, &[u8]> {
Expand All @@ -86,11 +80,12 @@ fn message_header_value(input: Stream<'_>) -> IResult<Stream<'_>, &[u8]> {
}

fn message_header(input: Stream<'_>) -> IResult<Stream<'_>, Header<'_>> {
let (input, name) = take_while1(is_token).parse_next(input)?;
let (input, _) = ':'.parse_next(input)?;
let (input, value) = many1(message_header_value).parse_next(input)?;

Ok((input, Header { name, value }))
seq!(Header {
name: take_while1(is_token),
_: ':',
value: many1(message_header_value),
})
.parse_next(input)
}

#[rustfmt::skip]
Expand Down
108 changes: 108 additions & 0 deletions src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,111 @@ macro_rules! succ (
(19, $submac:ident ! ($($rest:tt)*)) => ($submac!(20, $($rest)*));
(20, $submac:ident ! ($($rest:tt)*)) => ($submac!(21, $($rest)*));
);
/// Sequences multiple parsers and builds a struct out of them.
///
///# Example
///
/// ```
/// # use winnow::prelude::*;
/// # use winnow::bytes::take_while1;
/// # use winnow::character::{alphanumeric1, dec_uint, space0};
/// # use winnow::sequence::delimited;
/// # use winnow::multi::many0;
/// use winnow::sequence::seq;
///
/// #[derive(Debug, PartialEq)]
/// struct Point(u32, u32);
///
/// #[derive(Debug, PartialEq)]
/// struct Field {
/// name: Vec<u8>,
/// value: Vec<u8>,
/// point: Point,
/// }
///
/// let num = dec_uint::<_, u32, _>;
/// let spaced = |b| delimited(space0, b, space0);
/// let mut parser = seq!{
/// Field {
/// name: many0(alphanumeric1),
/// // `_` fields are ignored when building the struct
/// _: spaced(b':'),
/// value: many0(alphanumeric1),
/// _: spaced(b':'),
/// point: (num(), spaced(b','), num()).map(|(x, _, y)| Point(x, y),)
/// }
/// };
/// assert_eq!(
/// parser.parse_next(&b"test: data: 123 , 4"[..]),
/// Ok((
/// &b""[..],
/// Field {
/// name: b"test"[..].to_owned(),
/// value: b"data"[..].to_owned(),
/// point: Point(123, 4),
/// },
/// )),
/// );
/// ```
#[macro_export]
#[doc(alias = "tuple")]
#[doc(alias = "preceded")]
#[doc(alias = "terminated")]
#[doc(alias = "pair")]
#[doc(alias = "struct_parser")]
macro_rules! seq {
($name: ident { $($fields: tt)* }) => {
$crate::trace::trace(stringify!($name), move |input|
{
use $crate::Parser;
$crate::seq_parse_fields!($($fields)*);
Ok((
input,
$crate::seq_fields!( ($($fields)*); $name),
))
})
};
}

#[macro_export]
#[doc(hidden)]
macro_rules! seq_parse_fields {
(_ : $parser: expr, $($remaining: tt)*) => {
let (input, _) = $parser.parse_next(input)?;
$crate::seq_parse_fields!($($remaining)*)
};
($field: ident : $parser: expr, $($remaining: tt)*) => {
let (input, $field) = $parser.parse_next(input)?;
$crate::seq_parse_fields!($($remaining)*)
};
(,) => {};
() => {};
}

#[macro_export]
#[doc(hidden)]
macro_rules! seq_fields {
(; $name: ident $($tt: tt)*) => {
$name { $($tt)* }
};
( (_ : $first_parser: expr, $($remaining: tt)+ ); $name: ident $($tt: tt)*) => {
$crate::seq_fields!( ( $($remaining)+ ) ; $name $($tt)* )
};
( ($first_field: ident : $first_parser: expr, $($remaining: tt)+ );
$name: ident $($tt: tt)*) =>
{
$crate::seq_fields!( ( $($remaining)+ ) ; $name $($tt)* $first_field: $first_field, )
};
( ( _ : $first_parser: expr ); $name: ident $($tt: tt)*) => {
$crate::seq_fields!( ; $name $($tt)* )
};
( ($first_field: ident : $first_parser: expr ); $name: ident $($tt: tt)*) => {
$crate::seq_fields!(; $name $($tt)* $first_field: $first_field,)
};
( ( _ : $first_parser: expr, ); $name: ident $($tt: tt)*) => {
$crate::seq_fields!(; $name $($tt)*)
};
( ($first_field: ident : $first_parser: expr, ); $name: ident $($tt: tt)*) => {
$crate::seq_fields!(; $name $($tt)* $first_field: $first_field,)
};
}
3 changes: 3 additions & 0 deletions src/sequence/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ use crate::stream::Stream;
use crate::trace::trace;
use crate::Parser;

#[doc(inline)]
pub use crate::seq;

/// Apply two parsers, only returning the output from the second.
///
/// # Arguments
Expand Down

0 comments on commit 6bdf559

Please sign in to comment.