102 lines
3.2 KiB
Rust
102 lines
3.2 KiB
Rust
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::<u16>() {
|
|
Value::Literal(num)
|
|
} else {
|
|
Value::Identifier(value.into())
|
|
}
|
|
}
|
|
|
|
fn solve_value(value: &Value, expressions: &HashMap<String, Expression>, cache: &mut HashMap<String, u16>) -> 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<String, Expression>, cache: &mut HashMap<String, u16>) -> 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<String, u16> = HashMap::new();
|
|
let expressions = input.lines().map(|line| {
|
|
let (expr, into) = line.split_once(" -> ").unwrap();
|
|
let parts = expr.split(" ").collect::<Vec<_>>();
|
|
|
|
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::<HashMap<_, _>>();
|
|
|
|
println!("solve for a: {}", calculate_wire(&"a".into(), &expressions, &mut value_cache));
|
|
}
|