use std::collections::HashMap; #[derive(Debug, Clone)] pub enum Value { Literal(u16), Identifier(String), } #[derive(Debug, Clone, Copy)] pub enum Operator { And, Or, RShift, LShift, Not } #[derive(Debug, Clone)] pub enum Expression { Assign(Value), // 1 token before arrow Binary(Operator, Value, Value), // 3 tokens before arrow Unary(Value), // always not // 2 tokens before arrow } fn parse_operator(name: &str) -> Operator { match name { "AND" => Operator::And, "OR" => Operator::Or, "RSHIFT" => Operator::RShift, "LSHIFT" => Operator::LShift, _ => panic!("unexpected operator: {}", name) } } fn parse_value(value: &str) -> Value { if let Ok(num) = value.parse::() { Value::Literal(num) } else { Value::Identifier(value.into()) } } fn solve_value(value: &Value, expressions: &HashMap, cache: &mut HashMap) -> u16 { match value { Value::Literal(num) => *num, Value::Identifier(name) => { let result = calculate_wire(name, expressions, cache); cache.insert(name.clone(), result); result }, } } fn calculate_wire(ident: &String, expressions: &HashMap, cache: &mut HashMap) -> u16 { if let Some(num) = cache.get(ident) { return *num; } match expressions.get(ident).unwrap() { Expression::Assign(value) => { solve_value(value, expressions, cache) }, Expression::Binary(operator, lhs, rhs) => { match operator { Operator::And => solve_value(lhs, expressions, cache) & solve_value(rhs, expressions, cache), Operator::Or => solve_value(lhs, expressions, cache) | solve_value(rhs, expressions, cache), Operator::RShift => solve_value(lhs, expressions, cache) >> solve_value(rhs, expressions, cache), Operator::LShift => solve_value(lhs, expressions, cache) << solve_value(rhs, expressions, cache), Operator::Not => todo!(), } }, Expression::Unary(value) => { !solve_value(value, expressions, cache) }, } } pub fn solve(input: &str) { let mut value_cache: HashMap = HashMap::new(); let expressions = input.lines().map(|line| { let (expr, into) = line.split_once(" -> ").unwrap(); let parts = expr.split(" ").collect::>(); let parsed = match parts.len() { 1 => { // assignment Expression::Assign(parse_value(expr)) }, 2 => { Expression::Unary(parse_value(parts[1])) }, 3 => { Expression::Binary(parse_operator(parts[1]), parse_value(parts[0]), parse_value(parts[2])) }, _ => panic!("unexpected number of parts: {line}") }; // println!("parsed: {into} -> {parsed:?}"); (into.to_string(), parsed) }).collect::>(); println!("solve for a: {}", calculate_wire(&"a".into(), &expressions, &mut value_cache)); }