Skip to content

Commit c712560

Browse files
committed
Add transform_till and recognize_till parsers
Upstream: winnow-rs/winnow#541
1 parent 910c4c3 commit c712560

File tree

2 files changed

+84
-0
lines changed

2 files changed

+84
-0
lines changed

src/ghci/parse/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ mod module_set;
99
mod show_paths;
1010
mod show_targets;
1111
mod target_kind;
12+
mod transform_till;
1213

1314
use haskell_grammar::module_name;
1415
use lines::rest_of_line;
@@ -28,3 +29,5 @@ pub use show_paths::parse_show_paths;
2829
pub use show_paths::ShowPaths;
2930
pub use show_targets::parse_show_targets;
3031
pub use target_kind::TargetKind;
32+
pub use transform_till::recognize_till;
33+
pub use transform_till::transform_till;

src/ghci/parse/transform_till.rs

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
use winnow::combinator::eof;
2+
use winnow::combinator::terminated;
3+
use winnow::error::ErrMode;
4+
use winnow::error::ErrorKind;
5+
use winnow::error::ParserError;
6+
use winnow::stream::Offset;
7+
use winnow::stream::Stream;
8+
use winnow::stream::StreamIsPartial;
9+
use winnow::Parser;
10+
11+
/// Call the `repeat` parser until the `end` parser produces a result.
12+
///
13+
/// Then, return the input consumed until the `end` parser was called, and the result of the `end`
14+
/// parser.
15+
///
16+
/// See: <https://github.com/winnow-rs/winnow/pull/541>
17+
pub fn recognize_till<I, Discard, O, E>(
18+
mut repeat: impl Parser<I, Discard, E>,
19+
mut end: impl Parser<I, O, E>,
20+
) -> impl Parser<I, (<I as Stream>::Slice, O), E>
21+
where
22+
I: Stream,
23+
E: ParserError<I>,
24+
{
25+
move |input: &mut I| {
26+
let start = input.checkpoint();
27+
28+
loop {
29+
let before_end = input.checkpoint();
30+
match end.parse_next(input) {
31+
Ok(end_parsed) => {
32+
let after_end = input.checkpoint();
33+
34+
let offset_to_before_end = before_end.offset_from(&start);
35+
input.reset(start);
36+
let input_until_end = input.next_slice(offset_to_before_end);
37+
input.reset(after_end);
38+
39+
return Ok((input_until_end, end_parsed));
40+
}
41+
Err(ErrMode::Backtrack(_)) => {
42+
input.reset(before_end);
43+
match repeat.parse_next(input) {
44+
Ok(_) => {}
45+
Err(e) => return Err(e.append(input, ErrorKind::Many)),
46+
}
47+
}
48+
Err(e) => return Err(e),
49+
}
50+
}
51+
}
52+
}
53+
54+
/// Like [`recognize_till`], but it also applies a `transform` parser to the recognized input.
55+
pub fn transform_till<I, O1, O2, Discard, E>(
56+
mut repeat: impl Parser<I, Discard, E>,
57+
mut transform: impl Parser<<I as Stream>::Slice, O1, E>,
58+
mut end: impl Parser<I, O2, E>,
59+
) -> impl Parser<I, (O1, O2), E>
60+
where
61+
I: Stream,
62+
E: ParserError<I>,
63+
E: ParserError<<I as Stream>::Slice>,
64+
<I as Stream>::Slice: Stream + StreamIsPartial,
65+
{
66+
move |input: &mut I| {
67+
let (mut until_end, end_parsed) =
68+
recognize_till(repeat.by_ref(), end.by_ref()).parse_next(input)?;
69+
70+
let inner_parsed = terminated(transform.by_ref(), eof)
71+
.parse_next(&mut until_end)
72+
.map_err(|err_mode| match err_mode {
73+
ErrMode::Incomplete(_) => {
74+
panic!("complete parsers should not report `ErrMode::Incomplete(_)`")
75+
}
76+
ErrMode::Backtrack(inner) | ErrMode::Cut(inner) => ErrMode::Cut(inner),
77+
})?;
78+
79+
Ok((inner_parsed, end_parsed))
80+
}
81+
}

0 commit comments

Comments
 (0)