Skip to content

Commit

Permalink
Refactor dispatch!; fixes winnow-rs#344
Browse files Browse the repository at this point in the history
  • Loading branch information
silvanshade committed May 28, 2024
1 parent f349369 commit 49f6bd2
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 8 deletions.
2 changes: 2 additions & 0 deletions src/combinator/branch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ use crate::error::{ErrMode, ErrorKind, ParserError};
use crate::stream::Stream;
use crate::*;

#[doc(hidden)]
pub use crate::__dispatch_cases;
#[doc(inline)]
pub use crate::dispatch;

Expand Down
82 changes: 74 additions & 8 deletions src/macros/dispatch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,19 +37,85 @@
///
/// assert_eq!(escaped.parse_peek("\\nHello"), Ok(("Hello", '\n')));
/// ```
#[macro_export]
#[doc(hidden)] // forced to be visible in intended location
macro_rules! __dispatch_cases {
([ ] [ $i:tt $initial:tt $($acc:tt)* ]) => {
match $initial {
$($acc)*
}
};
([ $pat:pat $(if $pred:expr)? => $expr:expr $(,)? ] [ $i:tt $($acc:tt)* ]) => {
$crate::combinator::__dispatch_cases!([ ] [ $i $($acc)* $pat $(if $pred)? => $expr.parse_next($i), ])
};
([ $pat:pat $(if $pred:expr)? => $expr:expr, $($rest:tt)+ ] [ $i:tt $($acc:tt)* ]) => {
$crate::combinator::__dispatch_cases!([ $($rest)+ ] [ $i $($acc)* $pat $(if $pred)? => $expr.parse_next($i), ])
};
// NOTE: Must be the last rule otherwise the `{ ... },` (trailing comma) will fail to parse.
([ $pat:pat $(if $pred:expr)? => { $($body:tt)* } $($rest:tt)+ ] [ $i:tt $($acc:tt)* ]) => {
$crate::combinator::__dispatch_cases!([ $($rest)+ ] [ $i $($acc)* $pat $(if $pred)? => { $($body)* }.parse_next($i), ])
};
}

#[macro_export]
#[doc(hidden)] // forced to be visible in intended location
macro_rules! dispatch {
($match_parser: expr; $( $pat:pat $(if $pred:expr)? => $expr: expr ),+ $(,)? ) => {
$crate::combinator::trace("dispatch", move |i: &mut _|
{
($match_parser:expr; $($cases:tt)+) => {
$crate::combinator::trace("dispatch", move |i: &mut _| {
use $crate::Parser;
let initial = $match_parser.parse_next(i)?;
match initial {
$(
$pat $(if $pred)? => $expr.parse_next(i),
)*
}
$crate::combinator::__dispatch_cases!([ $($cases)+ ] [ i initial ])
})
};
}

#[cfg(test)]
mod test {
#[test]
fn parse_block_with_trailing_comma() {
use crate::combinator::{dispatch, empty, fail, preceded};
use crate::prelude::*;
use crate::token::any;

#[allow(unused)]
fn escaped(input: &mut &str) -> PResult<char> {
preceded('\\', escape_seq_char).parse_next(input)
}

#[allow(unused)]
fn escape_seq_char(input: &mut &str) -> PResult<char> {
dispatch! {any;
'b' => {
empty.value('\u{8}')
},
'f' => empty.value('\u{c}'),
_ => fail::<_, char, _>,
}
.parse_next(input)
}
}

#[test]
fn parse_block_sans_trailing_comma() {
use crate::combinator::{dispatch, empty, fail, preceded};
use crate::prelude::*;
use crate::token::any;

#[allow(unused)]
fn escaped(input: &mut &str) -> PResult<char> {
preceded('\\', escape_seq_char).parse_next(input)
}

#[allow(unused)]
fn escape_seq_char(input: &mut &str) -> PResult<char> {
dispatch! {any;
'b' => {
empty.value('\u{8}')
}
'f' => empty.value('\u{c}'),
_ => fail::<_, char, _>,
}
.parse_next(input)
}
}
}

0 comments on commit 49f6bd2

Please sign in to comment.