Skip to content

Commit 397ac5c

Browse files
committed
Add parser.with_consumed()
1 parent c0f3b2d commit 397ac5c

File tree

5 files changed

+47
-9
lines changed

5 files changed

+47
-9
lines changed

crates/utils/src/parser/base.rs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use crate::input::{InputError, MapWithInputExt};
22
use crate::parser::combinator::{
3-
Map, MapResult, Optional, Or, RepeatArrayVec, RepeatN, RepeatVec, WithPrefix, WithSuffix,
3+
Map, MapResult, Optional, Or, RepeatArrayVec, RepeatN, RepeatVec, WithConsumed, WithPrefix,
4+
WithSuffix,
45
};
56
use crate::parser::error::{ParseError, WithErrorMsg};
67
use crate::parser::iterator::{ParserIterator, ParserMatchesIterator};
@@ -233,6 +234,23 @@ pub trait Parser: Sized {
233234
}
234235
}
235236

237+
/// Return the output of this parser as well as the bytes consumed.
238+
///
239+
/// This can be used to map any errors that occur while processing the parsed input back to the
240+
/// problematic item's position in the input.
241+
///
242+
/// # Examples
243+
/// ```
244+
/// # use utils::parser::{self, Parser};
245+
/// assert_eq!(
246+
/// parser::u32().with_consumed().parse(b"012,345,678"),
247+
/// Ok(((12, &b"012"[..]), &b",345,678"[..]))
248+
/// );
249+
/// ```
250+
fn with_consumed(self) -> WithConsumed<Self> {
251+
WithConsumed { parser: self }
252+
}
253+
236254
/// Parse a prefix (normally a string literal) before this parser.
237255
///
238256
/// The result of the prefix parser is discarded.

crates/utils/src/parser/combinator.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,23 @@ impl<A: Parser, B: for<'i> Parser<Output<'i> = A::Output<'i>>> Parser for Or<A,
239239
}
240240
}
241241

242+
#[derive(Copy, Clone)]
243+
pub struct WithConsumed<P> {
244+
pub(super) parser: P,
245+
}
246+
impl<P: Parser> Parser for WithConsumed<P> {
247+
type Output<'i> = (P::Output<'i>, &'i [u8]);
248+
type Then<T: Parser> = Then2<Self, T>;
249+
250+
#[inline]
251+
fn parse<'i>(&self, input: &'i [u8]) -> ParseResult<'i, Self::Output<'i>> {
252+
match self.parser.parse(input) {
253+
Ok((v, remaining)) => Ok(((v, &input[..input.len() - remaining.len()]), remaining)),
254+
Err(e) => Err(e),
255+
}
256+
}
257+
}
258+
242259
#[derive(Copy, Clone)]
243260
pub struct WithPrefix<A, B> {
244261
pub(super) parser: A,

crates/year2024/src/day18.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,16 @@ impl Day18 {
3535
for item in parser::number_range(0..=max_coord)
3636
.then(parser::number_range(0..=max_coord).with_prefix(b','))
3737
.map(|(x, y)| Point2D { x, y })
38+
.with_consumed()
3839
.with_suffix(parser::eol())
3940
.parse_iterator(input)
4041
{
41-
let pos = item?;
42+
let (pos, line) = item?;
4243
if blocked_at[(pos.y + 1) * size + (pos.x + 1)] == u16::MAX {
4344
blocked_at[(pos.y + 1) * size + (pos.x + 1)] = fallen;
4445
fallen += 1;
4546
} else {
46-
return Err(InputError::new(input, 0, "duplicate position in input"));
47+
return Err(InputError::new(input, line, "duplicate position in input"));
4748
}
4849
}
4950

crates/year2024/src/day24.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,17 +40,18 @@ impl Day24 {
4040
.then(parser::byte_range(b'0'..=b'9'))
4141
.with_suffix(": ")
4242
.then(parser::byte_range(b'0'..=b'1'))
43+
.with_consumed()
4344
.with_suffix(parser::eol())
4445
.parse_iterator(initial_str)
4546
{
46-
let (wire, b) = item?;
47+
let ((wire, b), line) = item?;
4748
let n = ((wire.1 - b'0') * 10 + (wire.2 - b'0')) as usize;
4849

4950
if (wire.0, n) != next {
5051
if next.0 == b'x' && wire == (b'y', b'0', b'0') {
5152
input_bits = next.1;
5253
} else {
53-
return Err(InputError::new(input, 0, "unexpected initial value"));
54+
return Err(InputError::new(input, line, "unexpected initial value"));
5455
}
5556
}
5657

crates/year2024/src/day25.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,24 +9,25 @@ pub struct Day25 {
99

1010
impl Day25 {
1111
pub fn new(input: &str, _: InputType) -> Result<Self, InputError> {
12-
let mut locks = Vec::new();
13-
let mut keys = Vec::new();
12+
let mut locks = Vec::with_capacity(250);
13+
let mut keys = Vec::with_capacity(250);
1414

1515
for item in parser::one_of((b'.'.map(|_| false), b'#'.map(|_| true)))
1616
.repeat_n::<5, _>(parser::noop())
1717
.repeat_n::<7, _>(parser::eol())
18+
.with_consumed()
1819
.with_suffix(parser::eol().then(parser::eol()))
1920
.parse_iterator(input)
2021
{
21-
let grid = item?;
22+
let (grid, grid_str) = item?;
2223

2324
let top = grid[0][0];
2425
let bottom = grid[6][0];
2526
if top == bottom
2627
|| grid[0][1..].iter().any(|&x| x != top)
2728
|| grid[6][1..].iter().any(|&x| x != bottom)
2829
{
29-
return Err(InputError::new(input, 0, "expected lock or key"));
30+
return Err(InputError::new(input, grid_str, "expected lock or key"));
3031
}
3132

3233
let mut mask = 0u32;

0 commit comments

Comments
 (0)