diff --git a/crates/utils/src/parser/base.rs b/crates/utils/src/parser/base.rs index 9042ffe..862ccc3 100644 --- a/crates/utils/src/parser/base.rs +++ b/crates/utils/src/parser/base.rs @@ -3,7 +3,7 @@ use crate::parser::combinator::{ Map, MapResult, Optional, Or, RepeatArrayVec, RepeatN, RepeatVec, WithPrefix, WithSuffix, }; use crate::parser::error::{ParseError, WithErrorMsg}; -use crate::parser::iterator::ParserIterator; +use crate::parser::iterator::{ParserIterator, ParserMatchesIterator}; use crate::parser::simple::{Constant, Eol}; use crate::parser::then::{Then, Then2, Unimplemented}; @@ -414,6 +414,29 @@ pub trait Parser: Sized { parser: self, } } + + /// Create an iterator which returns matches only and skips over errors. + /// + /// This is intended for cases that require extracting matches out of the input. + /// Otherwise, [`parse_iterator`](Self::parse_iterator) should be used with a parser that can + /// match the entire input structure. + /// + /// # Examples + /// ``` + /// # use utils::parser::{self, Parser}; + /// assert_eq!( + /// parser::u32() + /// .matches_iterator("abc123d456efg7hi8jk9lmnop") + /// .collect::>(), + /// vec![123, 456, 7, 8, 9] + /// ); + /// ``` + fn matches_iterator(self, input: &str) -> ParserMatchesIterator { + ParserMatchesIterator { + remaining: input.as_bytes(), + parser: self, + } + } } // Workaround to allow using methods which consume a parser in methods which take references. diff --git a/crates/utils/src/parser/iterator.rs b/crates/utils/src/parser/iterator.rs index b8eb0ac..6160820 100644 --- a/crates/utils/src/parser/iterator.rs +++ b/crates/utils/src/parser/iterator.rs @@ -36,3 +36,31 @@ impl<'a, P: Parser> Iterator for ParserIterator<'a, P> { } impl FusedIterator for ParserIterator<'_, P> {} + +/// An iterator which returns successful parse outputs only, skipping over errors. +/// +/// See [`Parser::matches_iterator`]. +#[derive(Copy, Clone)] +#[must_use = "iterators are lazy and do nothing unless consumed"] +pub struct ParserMatchesIterator<'a, P> { + pub(super) remaining: &'a [u8], + pub(super) parser: P, +} + +impl<'a, P: Parser> Iterator for ParserMatchesIterator<'a, P> { + type Item = P::Output<'a>; + + #[inline] + fn next(&mut self) -> Option { + while !self.remaining.is_empty() { + if let Ok((v, remaining)) = self.parser.parse(self.remaining) { + self.remaining = remaining; + return Some(v); + } + self.remaining = &self.remaining[1..]; + } + None + } +} + +impl FusedIterator for ParserMatchesIterator<'_, P> {} diff --git a/crates/utils/src/parser/mod.rs b/crates/utils/src/parser/mod.rs index 71ffee4..231fa37 100644 --- a/crates/utils/src/parser/mod.rs +++ b/crates/utils/src/parser/mod.rs @@ -12,7 +12,7 @@ mod then; pub use base::*; pub use error::ParseError; -pub use iterator::ParserIterator; +pub use iterator::{ParserIterator, ParserMatchesIterator}; pub use number::{i128, i16, i32, i64, i8, number_range, u128, u16, u32, u64, u8}; pub use one_of::one_of; pub use simple::{byte, byte_range, constant, eof, eol, noop, take_while, take_while1}; diff --git a/crates/year2024/src/day03.rs b/crates/year2024/src/day03.rs index a9e1893..fabd979 100644 --- a/crates/year2024/src/day03.rs +++ b/crates/year2024/src/day03.rs @@ -7,32 +7,31 @@ pub struct Day03 { part2: u32, } +enum Instruction { + Mul(u32, u32), + Do, + Dont, +} + impl Day03 { pub fn new(input: &str, _: InputType) -> Result { + let matches = parser::parse_tree!( + ("mul(", a @ parser::u32(), ",", b @ parser::u32(), ")") => Instruction::Mul(a, b), + ("don't()") => Instruction::Dont, + ("do()") => Instruction::Do, + ) + .matches_iterator(input); + let (mut part1, mut part2) = (0, 0); let mut enabled = true; - - let mut input = input.as_bytes(); - while !input.is_empty() { - if let Ok(([a, b], remaining)) = parser::u32() - .repeat_n(b',') - .with_prefix("mul(") - .with_suffix(")") - .parse(input) - { - part1 += a * b; - if enabled { - part2 += a * b; + for instruction in matches { + match instruction { + Instruction::Mul(a, b) => { + part1 += a * b; + part2 += if enabled { a * b } else { 0 }; } - input = remaining; - } else if let Some(remaining) = input.strip_prefix(b"do()") { - enabled = true; - input = remaining; - } else if let Some(remaining) = input.strip_prefix(b"don't()") { - enabled = false; - input = remaining; - } else { - input = &input[1..]; + Instruction::Do => enabled = true, + Instruction::Dont => enabled = false, } }