day18 without nom

This commit is contained in:
Glenn Griffin 2020-12-21 15:11:43 -08:00
parent ee5ee9447a
commit 00ec9c2867
2 changed files with 107 additions and 40 deletions

View File

@ -9,4 +9,3 @@ edition = "2018"
[dependencies] [dependencies]
aoc-runner = "0.3.0" aoc-runner = "0.3.0"
aoc-runner-derive = "0.3.0" aoc-runner-derive = "0.3.0"
nom = "6.0.1"

View File

@ -1,45 +1,78 @@
use aoc_runner_derive::aoc; 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> { fn num(i: &str) -> Option<(&str, usize)> {
map_res(delimited(space0, digit1, space0), FromStr::from_str)(i) 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)] #[aoc(day18, part1)]
fn solve_d18_p1(input: &str) -> usize { fn solve_d18_p1(input: &str) -> usize {
fn paren(i: &str) -> IResult<&str, usize> { #[derive(Debug, Copy, Clone)]
delimited(space0, delimited(tag("("), expr, tag(")")), space0)(i) enum Operator {
Add,
Mul,
} }
fn num_or_paren(i: &str) -> IResult<&str, usize> { fn operator(i: &str) -> Option<(&str, Operator)> {
alt((num, paren))(i) 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> { fn paren(i: &str) -> Option<(&str, usize)> {
let (i, lhs) = num_or_paren(i)?; let i = i.trim_start();
if i.is_empty() || i.as_bytes()[0] != b'(' {
fold_many0(preceded(char('+'), num_or_paren), lhs, |lhs, rhs| lhs + rhs)(i) 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> { fn num_or_paren(i: &str) -> Option<(&str, usize)> {
let (i, lhs) = add_or_paren(i)?; num(i).or_else(|| paren(i))
}
fold_many0( fn operator_and_rhs(i: &str) -> Option<(&str, (Operator, usize))> {
pair(alt((char('+'), char('*'))), num_or_paren), let (i, op) = operator(i)?;
lhs, let (i, rhs) = num_or_paren(i)?;
|lhs, (op, rhs)| match op { Some((i, (op, rhs)))
'+' => lhs + rhs, }
'*' => lhs * rhs,
_ => unreachable!(), fn expr(i: &str) -> Option<(&str, usize)> {
}, let (mut rem, mut lhs) = num_or_paren(i)?;
)(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() input.split('\n').map(|line| expr(line).unwrap().1).sum()
@ -47,24 +80,59 @@ fn solve_d18_p1(input: &str) -> usize {
#[aoc(day18, part2)] #[aoc(day18, part2)]
fn solve_d18_p2(input: &str) -> usize { fn solve_d18_p2(input: &str) -> usize {
fn paren(i: &str) -> IResult<&str, usize> { fn paren(i: &str) -> Option<(&str, usize)> {
delimited(space0, delimited(tag("("), expr, tag(")")), space0)(i) 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> { fn num_or_paren(i: &str) -> Option<(&str, usize)> {
alt((num, paren))(i) num(i).or_else(|| paren(i))
} }
fn add_or_paren(i: &str) -> IResult<&str, usize> { fn add_or_paren(i: &str) -> Option<(&str, usize)> {
let (i, lhs) = num_or_paren(i)?; 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> { fn expr(i: &str) -> Option<(&str, usize)> {
let (i, lhs) = add_or_paren(i)?; 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() input.split('\n').map(|line| expr(line).unwrap().1).sum()