aoc23_parser 0.1.1

A basic LL(1) parser used for the 2023 Advent of Code
Documentation
use std::{cmp, mem};

use aoc23_parser::{
    tokenizer::{TokenStream, Token, ParseTokenError},
};

#[derive(Debug)]
struct Game {
    index: u32,
    matches: Vec<(u32, u32, u32)>,
}
impl Game {
    fn new() -> Game { 
        Game{ index: 0, matches: vec!((0,0,0)) } 
    }
}
impl Default for Game {
    fn default() -> Self { Game{ index: 0, matches: vec!((0,0,0)) } }
}

#[allow(dead_code)]
pub fn get_answer_part_1(input: Vec<String>) -> Result<u32, Box<dyn std::error::Error>>{
    let mut sum = 0;
    for Game{ index, matches } in get_result(input.join("\n"))? {
        if matches.iter().all(|&(reds, greens, blues)|  reds <= 12 && greens <= 13 && blues <= 14) { 
            sum += index;
        }
    }

    Ok(sum)
}

pub fn get_answer(input: String) -> Result<u32, Box<dyn std::error::Error>> {
    let sum = get_result(input)?.iter()
        .fold(0, |sum, Game{ matches, .. }| {
            let (reds, greens, blues) = matches.iter()
                .fold((0,0,0), |(min_reds, min_greens, min_blues), &(reds, greens, blues)| {
                    (cmp::max(min_reds, reds), cmp::max(min_greens, greens), cmp::max(min_blues, blues))
                });
            sum + reds * greens * blues
        });

    Ok(sum)
}

fn get_result(input: String) -> Result<Vec<Game>, Box<dyn std::error::Error>> {
    let tokens = TokenStream::tokenize(input.as_bytes())?;
    let instructions = tokens.parse(parse_tokens)?;
    let result = evaluate_stack(instructions)?;

    Ok(result)
}

#[derive(Debug, Clone)]
pub enum Instruction { Index(u32), Red(u32), Green(u32), Blue(u32), Round, Game}
fn parse_tokens(pattern: &mut Vec<Token>) -> Result<Option<Vec<Instruction>>, ParseTokenError> { 
    Ok( match &pattern[..] {
        [Token::Keyword("Game") , Token::Count(num), Token::Seperator(":")] => Some(vec!(Instruction::Index(*num))),
        [Token::Count(num), Token::Identifier(color)] => match color.as_str() 
        {
            "red"   => Some(vec!(Instruction::Red(*num))),
            "green" => Some(vec!(Instruction::Green(*num))),
            "blue"  => Some(vec!(Instruction::Blue(*num))),
            _ => return Err(ParseTokenError::InvalidToken(color.to_string())),
        },
        [Token::Seperator(",")] => Some(vec!()),
        [Token::Seperator(";")] => Some(vec!(Instruction::Round)),
        [Token::Seperator("\n") | Token::EndOfInput] => Some(vec!(Instruction::Game)),
        _ => None,
    })
}
fn evaluate_stack(stack: Vec<Instruction>) -> Result<Vec<Game>, Box<dyn std::error::Error>> {
    //println!("{stack:?}");
    //Evaluate stack
    let ref mut curr_game = Game::new();
    let results = stack.iter().flat_map(|instruction| match instruction { 
        Instruction::Index(num) => { 
            curr_game.index = *num; 
            vec!()
        }, 
        Instruction::Red(num) => { 
            curr_game.matches.last_mut().unwrap().0 = *num;
            vec!()
        },
        Instruction::Green(num) => { 
            curr_game.matches.last_mut().unwrap().1 = *num;
            vec!()
        }, 
        Instruction::Blue(num) => { 
            curr_game.matches.last_mut().unwrap().2 = *num; 
            vec!()
        }, 
        Instruction::Round => { 
            curr_game.matches.push((0,0,0));
            vec!()
        }
        Instruction::Game => {
            vec!(mem::take(curr_game))
        }
    }).collect();

    Ok(results)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn part1() -> Result<(), Box<dyn std::error::Error>> {
        let test_input = vec!(
            "Game 1: 3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green",
            "Game 2: 1 blue, 2 green; 3 green, 4 blue, 1 red; 1 green, 1 blue",
            "Game 3: 8 green, 6 blue, 20 red; 5 blue, 4 red, 13 green; 5 green, 1 red",
            "Game 4: 1 green, 3 red, 6 blue; 3 green, 6 red; 3 green, 15 blue, 14 red",
            "Game 5: 6 red, 1 blue, 3 green; 2 blue, 1 red, 2 green",
        );
        let result = get_answer_part_1(test_input.iter().map(|s| s.to_string()).collect())?;
        assert_eq!(result, 8, "Expected {:?}, got {:?}", 8, result); 
        Ok(())
    }

    #[test]
    fn part2() -> Result<(), Box<dyn std::error::Error>> {
        let test_input = vec!(
            "Game 1: 3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green",
            "Game 2: 1 blue, 2 green; 3 green, 4 blue, 1 red; 1 green, 1 blue",
            "Game 3: 8 green, 6 blue, 20 red; 5 blue, 4 red, 13 green; 5 green, 1 red",
            "Game 4: 1 green, 3 red, 6 blue; 3 green, 6 red; 3 green, 15 blue, 14 red",
            "Game 5: 6 red, 1 blue, 3 green; 2 blue, 1 red, 2 green",
        );

        let result = get_answer(test_input.join("\n"))?;
        assert_eq!(result, 2286, "Expected {:?}, got {:?}", 2286, result); 
        Ok(())
    }
}