diff --git a/crates/utils/src/parser/base.rs b/crates/utils/src/parser/base.rs index 530c9e1..81ad181 100644 --- a/crates/utils/src/parser/base.rs +++ b/crates/utils/src/parser/base.rs @@ -425,6 +425,20 @@ impl Parser for u8 { } } +/// Allow custom functions and closures to be used as parsers. +impl ParseResult> Parser for F { + type Output<'i> = O; + type Then = Then2; + + fn parse<'i>(&self, input: &'i [u8]) -> ParseResult<'i, Self::Output<'i>> { + self(input) + } + + fn then(self, next: T) -> Self::Then { + Then2::new(self, next) + } +} + /// Trait for types that have a canonical parser. pub trait Parseable { type Parser: for<'i> Parser = Self>; diff --git a/crates/year2015/src/day12.rs b/crates/year2015/src/day12.rs index a21ab4f..77f72a7 100644 --- a/crates/year2015/src/day12.rs +++ b/crates/year2015/src/day12.rs @@ -10,11 +10,7 @@ pub struct Day12 { impl Day12 { pub fn new(input: &str, _: InputType) -> Result { - let (part1, part2) = match Self::parse(input.as_bytes()) { - Ok(((part1, part2), &[])) => Ok((part1, part2)), - Ok((_, remaining)) => Err(InputError::new(input, remaining, "expected end of input")), - Err((err, position)) => Err(InputError::new(input, position, err)), - }?; + let (part1, part2) = Self::parse.parse_complete(input)?; Ok(Self { part1, part2 }) } diff --git a/crates/year2017/src/day09.rs b/crates/year2017/src/day09.rs new file mode 100644 index 0000000..7ae7f33 --- /dev/null +++ b/crates/year2017/src/day09.rs @@ -0,0 +1,97 @@ +use utils::parser::{ParseError, ParseResult}; +use utils::prelude::*; + +/// Parsing a nested structure. +#[derive(Clone, Debug)] +pub struct Day09 { + part1: u32, + part2: u32, +} + +impl Day09 { + pub fn new(input: &str, _: InputType) -> Result { + let (part1, part2) = Self::parse.parse_complete(input)?; + Ok(Self { part1, part2 }) + } + + // Parses either a (nested) group or a single piece of garbage. + fn parse(mut input: &[u8]) -> ParseResult<(u32, u32)> { + let mut group_depth = 0; + let mut in_garbage = false; + + let mut group_score = 0; + let mut garbage_count = 0; + + loop { + input = if in_garbage { + match input { + [b'!', _, rest @ ..] => rest, + [b'>', rest @ ..] => { + in_garbage = false; + if group_depth == 0 { + return Ok(((group_score, garbage_count), rest)); + } + rest + } + [_, rest @ ..] => { + garbage_count += 1; + rest + } + [] => return Err((ParseError::ExpectedByte(b'>'), input)), + } + } else { + match input { + [b'{', rest @ ..] => { + group_depth += 1; + group_score += group_depth; + rest + } + [b'}', rest @ ..] if group_depth > 0 => { + group_depth -= 1; + if group_depth == 0 { + return Ok(((group_score, garbage_count), rest)); + } + rest + } + [b'<', rest @ ..] => { + in_garbage = true; + rest + } + [b',', rest @ ..] if group_depth > 0 => rest, + _ if group_depth > 0 => { + return Err((ParseError::Custom("expected '{', '}', ',' or '<'"), input)) + } + _ => return Err((ParseError::Custom("expected '{' or '<'"), input)), + } + } + } + } + + #[must_use] + pub fn part1(&self) -> u32 { + self.part1 + } + + #[must_use] + pub fn part2(&self) -> u32 { + self.part2 + } +} + +examples!(Day09 -> (u32, u32) [ + {input: "{}", part1: 1}, + {input: "{{{}}}", part1: 6}, + {input: "{{},{}}", part1: 5}, + {input: "{{{},{},{{}}}}", part1: 16}, + {input: "{,,,}", part1: 1}, + {input: "{{},{},{},{}}", part1: 9}, + {input: "{{},{},{},{}}", part1: 9}, + {input: "{{},{},{},{}}", part1: 3}, + {input: "<>", part2: 0}, + {input: "", part2: 17}, + {input: "<<<<>", part2: 3}, + {input: "<{!>}>", part2: 2}, + {input: "", part2: 0}, + {input: ">", part2: 0}, + {input: "<{o\"i!a,<{i", part2: 10}, +]); diff --git a/crates/year2017/src/lib.rs b/crates/year2017/src/lib.rs index 82e6277..24ef742 100644 --- a/crates/year2017/src/lib.rs +++ b/crates/year2017/src/lib.rs @@ -10,4 +10,5 @@ utils::year!(2017 => year2017, ${ 6 => day06::Day06, 7 => day07::Day07<'_>, 8 => day08::Day08, + 9 => day09::Day09, });