Skip to content

Commit d93ff20

Browse files
committed
Add ParserMatchesIterator
1 parent c541182 commit d93ff20

File tree

4 files changed

+73
-23
lines changed

4 files changed

+73
-23
lines changed

crates/utils/src/parser/base.rs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use crate::parser::combinator::{
33
Map, MapResult, Optional, Or, RepeatArrayVec, RepeatN, RepeatVec, WithPrefix, WithSuffix,
44
};
55
use crate::parser::error::{ParseError, WithErrorMsg};
6-
use crate::parser::iterator::ParserIterator;
6+
use crate::parser::iterator::{ParserIterator, ParserMatchesIterator};
77
use crate::parser::simple::{Constant, Eol};
88
use crate::parser::then::{Then, Then2, Unimplemented};
99

@@ -414,6 +414,29 @@ pub trait Parser: Sized {
414414
parser: self,
415415
}
416416
}
417+
418+
/// Create an iterator which returns matches only and skips over errors.
419+
///
420+
/// This is intended for cases that require extracting matches out of the input.
421+
/// Otherwise, [`parse_iterator`](Self::parse_iterator) should be used with a parser that can
422+
/// match the entire input structure.
423+
///
424+
/// # Examples
425+
/// ```
426+
/// # use utils::parser::{self, Parser};
427+
/// assert_eq!(
428+
/// parser::u32()
429+
/// .matches_iterator("abc123d456efg7hi8jk9lmnop")
430+
/// .collect::<Vec<_>>(),
431+
/// vec![123, 456, 7, 8, 9]
432+
/// );
433+
/// ```
434+
fn matches_iterator(self, input: &str) -> ParserMatchesIterator<Self> {
435+
ParserMatchesIterator {
436+
remaining: input.as_bytes(),
437+
parser: self,
438+
}
439+
}
417440
}
418441

419442
// Workaround to allow using methods which consume a parser in methods which take references.

crates/utils/src/parser/iterator.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,31 @@ impl<'a, P: Parser> Iterator for ParserIterator<'a, P> {
3636
}
3737

3838
impl<P: Parser> FusedIterator for ParserIterator<'_, P> {}
39+
40+
/// An iterator which returns successful parse outputs only, skipping over errors.
41+
///
42+
/// See [`Parser::matches_iterator`].
43+
#[derive(Copy, Clone)]
44+
#[must_use = "iterators are lazy and do nothing unless consumed"]
45+
pub struct ParserMatchesIterator<'a, P> {
46+
pub(super) remaining: &'a [u8],
47+
pub(super) parser: P,
48+
}
49+
50+
impl<'a, P: Parser> Iterator for ParserMatchesIterator<'a, P> {
51+
type Item = P::Output<'a>;
52+
53+
#[inline]
54+
fn next(&mut self) -> Option<Self::Item> {
55+
while !self.remaining.is_empty() {
56+
if let Ok((v, remaining)) = self.parser.parse(self.remaining) {
57+
self.remaining = remaining;
58+
return Some(v);
59+
}
60+
self.remaining = &self.remaining[1..];
61+
}
62+
None
63+
}
64+
}
65+
66+
impl<P: Parser> FusedIterator for ParserMatchesIterator<'_, P> {}

crates/utils/src/parser/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ mod then;
1212

1313
pub use base::*;
1414
pub use error::ParseError;
15-
pub use iterator::ParserIterator;
15+
pub use iterator::{ParserIterator, ParserMatchesIterator};
1616
pub use number::{i128, i16, i32, i64, i8, number_range, u128, u16, u32, u64, u8};
1717
pub use one_of::one_of;
1818
pub use simple::{byte, byte_range, constant, eof, eol, noop, take_while, take_while1};

crates/year2024/src/day03.rs

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,32 +7,31 @@ pub struct Day03 {
77
part2: u32,
88
}
99

10+
enum Instruction {
11+
Mul(u32, u32),
12+
Do,
13+
Dont,
14+
}
15+
1016
impl Day03 {
1117
pub fn new(input: &str, _: InputType) -> Result<Self, InputError> {
18+
let matches = parser::parse_tree!(
19+
("mul(", a @ parser::u32(), ",", b @ parser::u32(), ")") => Instruction::Mul(a, b),
20+
("don't()") => Instruction::Dont,
21+
("do()") => Instruction::Do,
22+
)
23+
.matches_iterator(input);
24+
1225
let (mut part1, mut part2) = (0, 0);
1326
let mut enabled = true;
14-
15-
let mut input = input.as_bytes();
16-
while !input.is_empty() {
17-
if let Ok(([a, b], remaining)) = parser::u32()
18-
.repeat_n(b',')
19-
.with_prefix("mul(")
20-
.with_suffix(")")
21-
.parse(input)
22-
{
23-
part1 += a * b;
24-
if enabled {
25-
part2 += a * b;
27+
for instruction in matches {
28+
match instruction {
29+
Instruction::Mul(a, b) => {
30+
part1 += a * b;
31+
part2 += if enabled { a * b } else { 0 };
2632
}
27-
input = remaining;
28-
} else if let Some(remaining) = input.strip_prefix(b"do()") {
29-
enabled = true;
30-
input = remaining;
31-
} else if let Some(remaining) = input.strip_prefix(b"don't()") {
32-
enabled = false;
33-
input = remaining;
34-
} else {
35-
input = &input[1..];
33+
Instruction::Do => enabled = true,
34+
Instruction::Dont => enabled = false,
3635
}
3736
}
3837

0 commit comments

Comments
 (0)