complete 2006.rs

This commit is contained in:
2026-04-09 23:52:51 +09:00
parent ef49375bfb
commit ac9a4eb888

View File

@@ -0,0 +1,257 @@
use std::{
collections::HashMap,
io::{read_to_string, stdin},
};
#[derive(Debug)]
enum Token {
Atom { symbol: String },
AddOp,
EqOp,
Num { num: u64 },
Undef,
}
struct Atom {
symbol: String,
}
struct Molecular {
composites: Vec<(Atom, u64)>,
}
struct Expr {
composites: Vec<(Molecular, u64)>,
}
// stmt := Expr = Expr
struct Stmt {
lhs: Expr,
rhs: Expr,
}
/*
Lex
Tokenizing
*/
fn lex(line: &mut String) -> Vec<Token> {
let mut tokens = vec![];
let mut chars = line.chars();
let mut symb = String::new();
let mut tk = Token::Undef;
let mut pull_tk = |tk: &mut Token, tokens: &mut Vec<Token>, symb: &mut String| {
if symb.len() == 0 {
return;
}
let mut current_tk = std::mem::replace(tk, Token::Undef);
match current_tk {
Token::Num { ref mut num } => {
if let Ok(parsed) = symb.parse::<u64>() {
*num = parsed;
}
symb.clear();
}
Token::Atom { ref mut symbol } => {
*symbol = std::mem::take(symb);
}
_ => {}
}
tokens.push(current_tk);
};
let mut flg = false;
while let Some(curr) = chars.next() {
if curr.is_whitespace() {
pull_tk(&mut tk, &mut tokens, &mut symb);
continue;
}
if curr.is_uppercase() {
if flg {
pull_tk(&mut tk, &mut tokens, &mut symb);
}
flg = true;
symb.push(curr);
tk = Token::Atom {
symbol: String::new(),
};
} else if curr.is_lowercase() {
symb.push(curr);
} else if curr.is_numeric() {
if flg {
if !matches!(tk, Token::Num { .. }) {
pull_tk(&mut tk, &mut tokens, &mut symb);
}
}
flg = true;
symb.push(curr);
tk = Token::Num { num: 0 };
} else if curr == '+' {
if flg {
pull_tk(&mut tk, &mut tokens, &mut symb);
}
flg = false;
tk = Token::Undef;
tokens.push(Token::AddOp);
} else if curr == '=' {
if flg {
pull_tk(&mut tk, &mut tokens, &mut symb);
}
flg = false;
tk = Token::Undef;
tokens.push(Token::EqOp);
}
}
pull_tk(&mut tk, &mut tokens, &mut symb);
tokens
}
/*
Parse
*/
fn parse_molecular(tokens: &Vec<Token>, ptr: &mut usize) -> Molecular {
let mut comps = vec![];
while *ptr < tokens.len() {
let Token::Atom { symbol } = &tokens[*ptr] else {
break;
};
let atom = Atom {
symbol: symbol.clone(),
};
*ptr += 1;
if *ptr < tokens.len() {
if let Token::Num { num } = &tokens[*ptr] {
*ptr += 1;
comps.push((atom, *num));
} else {
comps.push((atom, 1));
}
} else {
comps.push((atom, 1));
}
}
Molecular { composites: comps }
}
fn parse_expr(tokens: &Vec<Token>, ptr: &mut usize) -> Expr {
let mut comps = vec![];
while *ptr < tokens.len() {
if let Token::EqOp = &tokens[*ptr] {
break;
}
if let Token::AddOp = &tokens[*ptr] {
*ptr += 1;
}
let mut cnt = 1;
if let Token::Num { num } = &tokens[*ptr] {
*ptr += 1;
cnt = *num;
}
let molecular = parse_molecular(tokens, ptr);
comps.push((molecular, cnt));
}
Expr { composites: comps }
}
fn parse_stmt(tokens: &Vec<Token>, ptr: &mut usize) -> Stmt {
let lhs = parse_expr(tokens, ptr);
*ptr += 1;
let rhs = parse_expr(tokens, ptr);
Stmt { lhs, rhs }
}
fn count_amount(expr: &Expr) -> HashMap<String, u64> {
let mut counter = HashMap::new();
for (mol, cnt_mol) in expr.composites.iter() {
for (atom, cnt_atom) in mol.composites.iter() {
counter
.entry(atom.symbol.clone())
.and_modify(|cnt| *cnt += cnt_atom * cnt_mol)
.or_insert(cnt_atom * cnt_mol);
}
}
counter
}
fn main() {
let mut line = String::new();
stdin().read_line(&mut line).unwrap();
let mut case = 0;
while !line.starts_with('#') {
case += 1;
line = line.trim().to_string();
let tks = lex(&mut line);
let stmt = parse_stmt(&tks, &mut 0);
let lhs_counter = count_amount(&stmt.lhs);
let rhs_counter = count_amount(&stmt.rhs);
// calculate destoryed and created atoms
let mut calcs = HashMap::new();
rhs_counter.iter().for_each(|(symb, cnt)| {
calcs
.entry(symb.clone())
.and_modify(|c| *c += *cnt as i64)
.or_insert(*cnt as i64);
});
lhs_counter.iter().for_each(|(symb, cnt)| {
calcs
.entry(symb.clone())
.and_modify(|c| *c -= *cnt as i64)
.or_insert(-(*cnt as i64));
});
let flg = calcs.values().any(|cnt| *cnt != 0);
if !flg {
println!("Equation {} is balanced.", case);
} else {
println!("Equation {} is unbalanced.", case);
let mut calcs = calcs
.into_iter()
.map(|(symb, cnt)| (symb, cnt))
.collect::<Vec<_>>();
calcs.sort();
for (symb, cnt) in calcs.iter() {
if *cnt == 1 {
println!("You have created {} atom of {}.", cnt, symb);
} else if *cnt > 1 {
println!("You have created {} atoms of {}.", cnt, symb);
} else if *cnt == -1 {
println!("You have destroyed {} atom of {}.", -cnt, symb);
} else if *cnt < -1 {
println!("You have destroyed {} atoms of {}.", -cnt, symb);
}
}
println!();
}
line.clear();
stdin().read_line(&mut line).unwrap();
}
}