258 lines
6.2 KiB
Rust
258 lines
6.2 KiB
Rust
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();
|
|
}
|
|
}
|