Skip to content

Commit 86b63db

Browse files
committed
make lenient parser report errors
1 parent d24ddf0 commit 86b63db

File tree

2 files changed

+189
-94
lines changed

2 files changed

+189
-94
lines changed

query-grammar/src/infallible.rs

Lines changed: 65 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,20 @@
22
33
use std::convert::Infallible;
44

5-
use nom::{IResult, InputLength};
5+
use nom::{AsChar, IResult, InputLength, InputTakeAtPosition};
66

77
pub(crate) type ErrorList = Vec<LenientErrorInternal>;
88
pub(crate) type JResult<I, O> = IResult<I, (O, ErrorList), Infallible>;
99

1010
/// An error, with an end-of-string based offset
11+
#[derive(Debug)]
1112
pub(crate) struct LenientErrorInternal {
12-
pos: usize,
13-
message: String,
13+
pub pos: usize,
14+
pub message: String,
1415
}
1516

1617
/// A recoverable error and the position it happened at
18+
#[derive(Debug, PartialEq)]
1719
pub struct LenientError {
1820
pub pos: usize,
1921
pub message: String,
@@ -38,8 +40,6 @@ fn unwrap_infallible<T>(res: Result<T, nom::Err<Infallible>>) -> T {
3840
// when rfcs#1733 get stabilized, this can make things clearer
3941
// trait InfallibleParser<I, O> = nom::Parser<I, (O, ErrorList), std::convert::Infallible>;
4042

41-
// TODO space0 and space1: space0 can't fail, space1 can parse nothing but reports missing space
42-
4343
/// A variant of the classical `opt` parser, except it returns an infallible error type.
4444
///
4545
/// It's less generic than the original to ease type resolution in the rest of the code.
@@ -54,6 +54,53 @@ where F: nom::Parser<I, O, nom::error::Error<I>> {
5454
}
5555
}
5656

57+
pub(crate) fn opt_i_err<'a, I: Clone + InputLength, O, F>(
58+
mut f: F,
59+
message: impl ToString + 'a,
60+
) -> impl FnMut(I) -> JResult<I, Option<O>> + 'a
61+
where
62+
F: nom::Parser<I, O, nom::error::Error<I>> + 'a,
63+
{
64+
move |input: I| {
65+
let i = input.clone();
66+
match f.parse(input) {
67+
Ok((i, o)) => Ok((i, (Some(o), Vec::new()))),
68+
Err(_) => {
69+
let errs = vec![LenientErrorInternal {
70+
pos: i.input_len(),
71+
message: message.to_string(),
72+
}];
73+
Ok((i, (None, errs)))
74+
}
75+
}
76+
}
77+
}
78+
79+
pub(crate) fn space0_infallible<T>(input: T) -> JResult<T, T>
80+
where
81+
T: InputTakeAtPosition + Clone,
82+
<T as InputTakeAtPosition>::Item: AsChar + Clone,
83+
{
84+
opt_i(nom::character::complete::space0)(input)
85+
.map(|(left, (spaces, errors))| (left, (spaces.expect("space0 can't fail"), errors)))
86+
}
87+
88+
pub(crate) fn space1_infallible<T>(input: T) -> JResult<T, Option<T>>
89+
where
90+
T: InputTakeAtPosition + Clone + InputLength,
91+
<T as InputTakeAtPosition>::Item: AsChar + Clone,
92+
{
93+
opt_i(nom::character::complete::space1)(input).map(|(left, (spaces, mut errors))| {
94+
if spaces.is_none() {
95+
errors.push(LenientErrorInternal {
96+
pos: left.input_len(),
97+
message: "missing space".to_string(),
98+
})
99+
}
100+
(left, (spaces, errors))
101+
})
102+
}
103+
57104
pub(crate) fn fallible<I, O, E: nom::error::ParseError<I>, F>(
58105
mut f: F,
59106
) -> impl FnMut(I) -> IResult<I, O, E>
@@ -206,30 +253,30 @@ where
206253
F: nom::Parser<I, (O, ErrorList), Infallible>,
207254
G: nom::Parser<I, (O2, ErrorList), Infallible>,
208255
{
209-
move |mut i: I| {
256+
move |i: I| {
210257
let mut res: Vec<O> = Vec::new();
211258
let mut errors: ErrorList = Vec::new();
212259

213-
let (i1, (o, mut err)) = unwrap_infallible(f.parse(i.clone()));
260+
let (mut i, (o, mut err)) = unwrap_infallible(f.parse(i.clone()));
214261
errors.append(&mut err);
215262
res.push(o);
216-
i = i1;
217263

218264
loop {
219-
let len = i.input_len();
220-
let (i1, (_, mut err)) = unwrap_infallible(sep.parse(i.clone()));
221-
errors.append(&mut err);
265+
let (i_sep_parsed, (_, mut err_sep)) = unwrap_infallible(sep.parse(i.clone()));
266+
let len_before = i_sep_parsed.input_len();
222267

223-
let (i2, (o, mut err)) = unwrap_infallible(f.parse(i1.clone()));
268+
let (i_elem_parsed, (o, mut err_elem)) =
269+
unwrap_infallible(f.parse(i_sep_parsed.clone()));
224270

225271
// infinite loop check: the parser must always consume
226272
// if we consumed nothing here, don't produce an element.
227-
if i2.input_len() == len {
228-
return Ok((i1, (res, errors)));
273+
if i_elem_parsed.input_len() == len_before {
274+
return Ok((i, (res, errors)));
229275
}
230276
res.push(o);
231-
errors.append(&mut err);
232-
i = i2;
277+
errors.append(&mut err_sep);
278+
errors.append(&mut err_elem);
279+
i = i_elem_parsed;
233280
}
234281
}
235282
}
@@ -269,7 +316,7 @@ macro_rules! alt_trait_impl(
269316
fn choice(&mut self, input: Input) -> Option<JResult<Input, Output>> {
270317
match self.0.0.parse(input.clone()) {
271318
Err(_) => alt_trait_inner!(1, self, input, $($id_cond $id),+),
272-
Ok((i1, _)) => Some(self.0.1.parse(i1)),
319+
Ok((input_left, _)) => Some(self.0.1.parse(input_left)),
273320
}
274321
}
275322
}
@@ -280,7 +327,7 @@ macro_rules! alt_trait_inner(
280327
($it:tt, $self:expr, $input:expr, $head_cond:ident $head:ident, $($id_cond:ident $id:ident),+) => (
281328
match $self.$it.0.parse($input.clone()) {
282329
Err(_) => succ!($it, alt_trait_inner!($self, $input, $($id_cond $id),+)),
283-
Ok((i1, _)) => Some($self.$it.1.parse(i1)),
330+
Ok((input_left, _)) => Some($self.$it.1.parse(input_left)),
284331
}
285332
);
286333
($it:tt, $self:expr, $input:expr, $head_cond:ident $head:ident) => (

0 commit comments

Comments
 (0)