Day 3 complete

This commit is contained in:
2025-05-26 12:46:12 -05:00
parent 1dc412f341
commit 4eaf6948e9
2 changed files with 188 additions and 41 deletions

View File

@@ -8,7 +8,9 @@ use std::env;
use std::error::Error;
use std::fs::File;
use std::io::{BufReader, prelude::*};
use std::iter::Peekable;
use std::str::FromStr;
#[derive(Eq, PartialEq, Clone, Debug)]
enum Token {
Text(String),
@@ -21,14 +23,6 @@ enum Token {
NewLine,
}
fn is_mul_text_token(text_token: &str) -> bool {
if text_token.ends_with("mul") {
true
} else {
false
}
}
fn tokenize(input_str: String) -> Vec<Token> {
let mut tokens: Vec<Token> = Vec::new();
let mut chars = input_str.chars().peekable();
@@ -37,7 +31,9 @@ fn tokenize(input_str: String) -> Vec<Token> {
// Text Numeric
_ if char::is_alphabetic(c) => {
let mut text = c.to_string();
while let Some(alphabetic_char) = chars.next_if(|p| p.is_alphabetic()) {
// HACK: including ' as part of alphabetic to implent "don't" expressions for part 2
while let Some(alphabetic_char) = chars.next_if(|p| p.is_alphabetic() || *p == '\'')
{
text.push(alphabetic_char)
}
Token::Text(text)
@@ -73,6 +69,10 @@ struct MulOp {
}
impl MulOp {
pub fn new(lhs: String, rhs: String) -> MulOp {
MulOp { lhs, rhs }
}
pub fn multiple<T>(&self) -> Result<<T as Mul>::Output, <T as FromStr>::Err>
where
T: FromStr + Mul,
@@ -83,42 +83,131 @@ impl MulOp {
}
}
fn parse_tokens(tokens: &Vec<Token>) -> Vec<MulOp> {
fn is_mul_text_token(text_token: &str) -> bool {
if text_token.ends_with("mul") {
true
} else {
false
}
}
fn parse_mul<'a>(token_iter: &mut Peekable<impl Iterator<Item = &'a Token>>) -> Option<MulOp> {
// Take OpenParenthesis or return None
if !token_iter.next_if_eq(&&Token::OpenParenthesis).is_some() {
return None;
}
// Take Number or return None
let lhs =
if let Some(Token::Number(lhs)) = token_iter.next_if(|v| matches!(v, Token::Number(_))) {
lhs.to_owned()
} else {
return None;
};
// Take Comma or return None
if !token_iter.next_if_eq(&&Token::Comma).is_some() {
return None;
}
// Take Number or return None
let rhs = if let Some(Token::Number(rhs)) =
token_iter.next_if(|v: &&Token| matches!(v, Token::Number(_)))
{
rhs.to_owned()
} else {
return None;
};
// Take CloseParenthesis or return None
if !token_iter.next_if_eq(&&Token::CloseParenthesis).is_some() {
return None;
}
Some(MulOp::new(lhs, rhs))
}
#[derive(Debug)]
struct DoOp {}
impl DoOp {
pub fn new() -> DoOp {
DoOp {}
}
}
fn is_do_text_token(text_token: &str) -> bool {
if text_token.ends_with("do") {
true
} else {
false
}
}
fn parse_do<'a>(token_iter: &mut Peekable<impl Iterator<Item = &'a Token>>) -> Option<DoOp> {
// Take OpenParenthesis or return None
if !token_iter.next_if_eq(&&Token::OpenParenthesis).is_some() {
return None;
}
// Take CloseParenthesis or return None
if !token_iter.next_if_eq(&&Token::CloseParenthesis).is_some() {
return None;
}
Some(DoOp::new())
}
#[derive(Debug)]
struct DontOp {}
impl DontOp {
pub fn new() -> DontOp {
DontOp {}
}
}
fn is_dont_text_token(text_token: &str) -> bool {
if text_token.ends_with("don't") {
true
} else {
false
}
}
fn parse_dont<'a>(token_iter: &mut Peekable<impl Iterator<Item = &'a Token>>) -> Option<DontOp> {
// Take OpenParenthesis or return None
if !token_iter.next_if_eq(&&Token::OpenParenthesis).is_some() {
return None;
}
// Take CloseParenthesis or return None
if !token_iter.next_if_eq(&&Token::CloseParenthesis).is_some() {
return None;
}
Some(DontOp::new())
}
#[derive(Debug)]
enum Ops {
Mul(MulOp),
Do(DoOp),
Dont(DontOp),
}
fn parse_tokens(tokens: &Vec<Token>) -> Vec<Ops> {
let mut tokens = tokens.iter().peekable();
let mut muls: Vec<MulOp> = Vec::new();
let mut muls: Vec<Ops> = Vec::new();
while let Some(token) = tokens.next() {
match token {
Token::Text(t) => {
if is_mul_text_token(t) {
// Take OpenParenthesis or continue
if !tokens.next_if_eq(&&Token::OpenParenthesis).is_some() {
continue;
let op = parse_mul(tokens.by_ref());
if let Some(op) = op {
muls.push(Ops::Mul(op));
}
// Take Number or continue
let lhs = if let Some(Token::Number(lhs)) =
tokens.next_if(|v| matches!(v, Token::Number(_)))
{
lhs.to_owned()
} else {
continue;
};
// Take Comma or continue
if !tokens.next_if_eq(&&Token::Comma).is_some() {
continue;
} else if is_do_text_token(t) {
let op = parse_do(tokens.by_ref());
if let Some(op) = op {
muls.push(Ops::Do(op));
}
// Take Number or continue
let rhs = if let Some(Token::Number(rhs)) =
tokens.next_if(|v: &&Token| matches!(v, Token::Number(_)))
{
rhs.to_owned()
} else {
continue;
};
// Take CloseParenthesis or continue
if !tokens.next_if_eq(&&Token::CloseParenthesis).is_some() {
continue;
} else if is_dont_text_token(t) {
let op = parse_dont(tokens.by_ref());
if let Some(op) = op {
muls.push(Ops::Dont(op));
}
muls.push(MulOp { lhs, rhs });
}
}
_ => (),
@@ -149,12 +238,25 @@ fn main() -> Result<(), Box<dyn Error>> {
let muls = parse_tokens(&tokens);
//println!("{:?}", muls);
let mut accumulator: i64 = 0;
for mul in muls {
accumulator += mul.multiple::<i64>()?;
let mut accumulator_part1: i64 = 0;
let mut accumulator_part2: i64 = 0;
let mut enabled: bool = true;
for op in muls {
match op {
Ops::Mul(mul_op) => {
let value = mul_op.multiple::<i64>()?;
accumulator_part1 += value;
if enabled {
accumulator_part2 += value;
}
}
Ops::Do(_) => enabled = true,
Ops::Dont(_) => enabled = false,
}
}
println!("Answer: {}", accumulator);
println!("Answer part 1: {}", accumulator_part1);
println!("Answer part 2: {}", accumulator_part2);
Ok(())
}