use nom::{ branch::alt, character::complete::char as nom_char, combinator::map, multi::many0, IResult, }; use std::fs; fn main() -> Result<(), Box> { let input = fs::read_to_string("inputs/day_10.txt")?; let mut syntax_error_score = 0; let mut autocomplete_scores = Vec::new(); for line in input.split("\n") { match parse_lisp(line) { Ok(_) => { // boring } Err(nom::Err::Failure(ParseError::MismatchedExpectation(_, actual))) => { syntax_error_score += match actual { ')' => 3, ']' => 57, '}' => 1197, '>' => 25137, _ => 0, } } Err(nom::Err::Failure(ParseError::EndOfInput(_))) => { let mut line = line.to_owned(); let mut autocomplete_score = 0u64; while let Err(nom::Err::Failure(ParseError::EndOfInput(expected))) = parse_lisp(&line) { autocomplete_score *= 5; autocomplete_score += match expected { ')' => 1, ']' => 2, '}' => 3, '>' => 4, _ => 0, }; line.push(expected); } autocomplete_scores.push(autocomplete_score); } Err(_) => panic!("Unexpected nom error type"), } } dbg!(syntax_error_score); autocomplete_scores.sort(); dbg!(autocomplete_scores[autocomplete_scores.len() / 2]); Ok(()) } #[derive(Debug)] enum ParseError<'a> { MismatchedExpectation(char, char), EndOfInput(char), Other(nom::error::Error<&'a str>), } impl<'a> From> for ParseError<'a> { fn from(e: nom::error::Error<&'a str>) -> Self { ParseError::Other(e) } } impl<'a> nom::error::ParseError<&'a str> for ParseError<'a> { fn from_error_kind(input: &'a str, kind: nom::error::ErrorKind) -> Self { nom::error::Error::from_error_kind(input, kind).into() } fn append(_input: &'a str, _kind: nom::error::ErrorKind, other: Self) -> Self { other } fn from_char(input: &'a str, c: char) -> Self { nom::error::Error::from_char(input, c).into() } } #[derive(Debug)] struct Lisp { blocks: Vec, } #[derive(Debug)] struct Block { opening: char, blocks: Vec, } fn parse_lisp(input: &str) -> IResult<&str, Lisp, ParseError> { map(parse_blocks, |blocks| Lisp { blocks })(input) } fn parse_blocks(input: &str) -> IResult<&str, Vec, ParseError> { many0(parse_block)(input) } fn parse_block(input: &str) -> IResult<&str, Block, ParseError> { alt(( block('{', '}'), block('[', ']'), block('(', ')'), block('<', '>'), ))(input) } fn block(opening: char, closing: char) -> impl Fn(&str) -> IResult<&str, Block, ParseError> { move |input: &str| { let (input, _) = nom_char(opening)(input)?; let (input, blocks) = parse_blocks(input)?; let (input, _) = match nom_char(closing)(input) { Ok((input, closing)) => (input, closing), Err(nom::Err::Error(_)) => { return Err(nom::Err::Failure(match input.chars().next() { Some(actual) => ParseError::MismatchedExpectation(closing, actual), None => ParseError::EndOfInput(closing), })) } Err(e) => return Err(e), }; Ok((input, Block { opening, blocks })) } }