diff --git a/Cargo.toml b/Cargo.toml index f936d74..1fd5041 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,4 +9,3 @@ edition = "2018" [dependencies] aoc-runner = "0.3.0" aoc-runner-derive = "0.3.0" -nom = "6.0.1" diff --git a/src/day18.rs b/src/day18.rs index dde2757..1ab21fb 100644 --- a/src/day18.rs +++ b/src/day18.rs @@ -1,45 +1,78 @@ use aoc_runner_derive::aoc; -use nom::branch::alt; -use nom::bytes::complete::tag; -use nom::character::complete::{char, digit1, space0}; -use nom::combinator::map_res; -use nom::multi::fold_many0; -use nom::sequence::{delimited, pair, preceded}; -use nom::IResult; -use std::str::FromStr; -fn num(i: &str) -> IResult<&str, usize> { - map_res(delimited(space0, digit1, space0), FromStr::from_str)(i) +fn num(i: &str) -> Option<(&str, usize)> { + let i = i.trim_start(); + let end_idx = i + .as_bytes() + .iter() + .copied() + .position(|b| !(b'0'..=b'9').contains(&b)) + .unwrap_or(i.len()); + let n = (&i[..end_idx]).parse().ok()?; + let rem = &i[end_idx..]; + Some((rem, n)) } #[aoc(day18, part1)] fn solve_d18_p1(input: &str) -> usize { - fn paren(i: &str) -> IResult<&str, usize> { - delimited(space0, delimited(tag("("), expr, tag(")")), space0)(i) + #[derive(Debug, Copy, Clone)] + enum Operator { + Add, + Mul, } - fn num_or_paren(i: &str) -> IResult<&str, usize> { - alt((num, paren))(i) + fn operator(i: &str) -> Option<(&str, Operator)> { + let i = i.trim_start(); + let op = match i.as_bytes()[0] { + b'+' => Operator::Add, + b'*' => Operator::Mul, + _ => return None, + }; + Some((&i[1..], op)) } - fn add_or_paren(i: &str) -> IResult<&str, usize> { - let (i, lhs) = num_or_paren(i)?; - - fold_many0(preceded(char('+'), num_or_paren), lhs, |lhs, rhs| lhs + rhs)(i) + fn paren(i: &str) -> Option<(&str, usize)> { + let i = i.trim_start(); + if i.is_empty() || i.as_bytes()[0] != b'(' { + return None; + } + let (rem, n) = expr(&i[1..])?; + if rem.is_empty() || rem.as_bytes()[0] != b')' { + return None; + } + let rem = &rem[1..]; + Some((rem, n)) } - fn expr(i: &str) -> IResult<&str, usize> { - let (i, lhs) = add_or_paren(i)?; + fn num_or_paren(i: &str) -> Option<(&str, usize)> { + num(i).or_else(|| paren(i)) + } - fold_many0( - pair(alt((char('+'), char('*'))), num_or_paren), - lhs, - |lhs, (op, rhs)| match op { - '+' => lhs + rhs, - '*' => lhs * rhs, - _ => unreachable!(), - }, - )(i) + fn operator_and_rhs(i: &str) -> Option<(&str, (Operator, usize))> { + let (i, op) = operator(i)?; + let (i, rhs) = num_or_paren(i)?; + Some((i, (op, rhs))) + } + + fn expr(i: &str) -> Option<(&str, usize)> { + let (mut rem, mut lhs) = num_or_paren(i)?; + + loop { + if rem.is_empty() { + break; + } + + if let Some((irem, (op, rhs))) = operator_and_rhs(rem) { + rem = irem; + lhs = match op { + Operator::Add => lhs + rhs, + Operator::Mul => lhs * rhs, + }; + } else { + break; + } + } + Some((rem, lhs)) } input.split('\n').map(|line| expr(line).unwrap().1).sum() @@ -47,24 +80,59 @@ fn solve_d18_p1(input: &str) -> usize { #[aoc(day18, part2)] fn solve_d18_p2(input: &str) -> usize { - fn paren(i: &str) -> IResult<&str, usize> { - delimited(space0, delimited(tag("("), expr, tag(")")), space0)(i) + fn paren(i: &str) -> Option<(&str, usize)> { + let i = i.trim_start(); + if i.is_empty() || i.as_bytes()[0] != b'(' { + return None; + } + let (rem, n) = expr(&i[1..])?; + if rem.is_empty() || rem.as_bytes()[0] != b')' { + return None; + } + let rem = &rem[1..]; + Some((rem, n)) } - fn num_or_paren(i: &str) -> IResult<&str, usize> { - alt((num, paren))(i) + fn num_or_paren(i: &str) -> Option<(&str, usize)> { + num(i).or_else(|| paren(i)) } - fn add_or_paren(i: &str) -> IResult<&str, usize> { - let (i, lhs) = num_or_paren(i)?; + fn add_or_paren(i: &str) -> Option<(&str, usize)> { + let (mut i, mut lhs) = num_or_paren(i)?; - fold_many0(preceded(char('+'), num_or_paren), lhs, |lhs, rhs| lhs + rhs)(i) + loop { + i = i.trim_start(); + if i.is_empty() || i.as_bytes()[0] != b'+' { + break; + } + i = &i[1..]; + if let Some((rem, rhs)) = num_or_paren(i) { + lhs += rhs; + i = rem; + } else { + break; + } + } + Some((i, lhs)) } - fn expr(i: &str) -> IResult<&str, usize> { - let (i, lhs) = add_or_paren(i)?; + fn expr(i: &str) -> Option<(&str, usize)> { + let (mut i, mut lhs) = add_or_paren(i)?; - fold_many0(preceded(char('*'), add_or_paren), lhs, |lhs, rhs| lhs * rhs)(i) + loop { + i = i.trim_start(); + if i.is_empty() || i.as_bytes()[0] != b'*' { + break; + } + i = &i[1..]; + if let Some((rem, rhs)) = add_or_paren(i) { + lhs *= rhs; + i = rem; + } else { + break; + } + } + Some((i, lhs)) } input.split('\n').map(|line| expr(line).unwrap().1).sum()