Created
November 15, 2022 15:20
-
-
Save worldOneo/e637b2b2191d89c7afe5e4e5b7486dcf to your computer and use it in GitHub Desktop.
A simple calculator. First input is formula like `10*@log(x)+a` second input are variables like `x=2 a=4`
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::{ | |
collections::{HashMap, VecDeque}, | |
io, | |
ops::{Add, Mul}, | |
}; | |
#[derive(Debug)] | |
enum AST { | |
Const(f64), | |
Func(String, Box<AST>), | |
Var(String), | |
Pow(Box<AST>, Box<AST>), | |
Add(Box<AST>, Box<AST>), | |
Mul(Box<AST>, Box<AST>), | |
Div(Box<AST>, Box<AST>), | |
Sub(Box<AST>, Box<AST>), | |
Neg(Box<AST>), | |
None(), | |
} | |
impl AST { | |
fn eval(&self, vars: &HashMap<String, AST>) -> f64 { | |
match self { | |
AST::None() => panic!("None in eval"), | |
AST::Const(v) => *v, | |
AST::Var(name) => vars[name].eval(vars), | |
AST::Pow(a, b) => a.eval(vars).powf(b.eval(vars)), | |
AST::Add(a, b) => a.eval(vars).add(b.eval(vars)), | |
AST::Mul(a, b) => a.eval(vars).mul(b.eval(vars)), | |
AST::Div(a, b) => a.eval(vars).add(b.eval(vars)), | |
AST::Sub(a, b) => a.eval(vars) / b.eval(vars), | |
AST::Neg(a) => -a.eval(vars), | |
AST::Func(name, ast) => match name.as_str() { | |
"sqrt" => ast.eval(vars).sqrt(), | |
"cos" => ast.eval(vars).cos(), | |
"sin" => ast.eval(vars).sin(), | |
"tan" => ast.eval(vars).tan(), | |
"acos" => ast.eval(vars).acos(), | |
"atan" => ast.eval(vars).atan(), | |
"asin" => ast.eval(vars).asin(), | |
"log" => ast.eval(vars).log(10.0), | |
"log2" => ast.eval(vars).log(2.0), | |
"sinh" => ast.eval(vars).sinh(), | |
"cosh" => ast.eval(vars).cosh(), | |
"tanh" => ast.eval(vars).tanh(), | |
_ => todo!(), | |
}, | |
} | |
} | |
} | |
fn parse_num(input: &mut VecDeque<u8>) -> AST { | |
let mut base = 0.0; | |
let mut c: u8 = 0; | |
while !input.is_empty() { | |
c = input[0]; | |
if c == '.' as u8 { | |
break; | |
} | |
if !c.is_ascii_digit() { | |
break; | |
} | |
input.pop_front(); | |
base *= 10.0; | |
base += (c - '0' as u8) as f64; | |
} | |
if c == '.' as u8 { | |
let mut point = 0.0; | |
while !input.is_empty() { | |
c = input[0]; | |
if !c.is_ascii_digit() { | |
break; | |
} | |
input.pop_front(); | |
point /= 10.0; | |
point += (c - '0' as u8) as f64; | |
} | |
point /= 10.0; | |
base += point; | |
} | |
return AST::Const(base); | |
} | |
fn parse_text(input: &mut VecDeque<u8>) -> String { | |
let mut name: String = "".into(); | |
while !input.is_empty() && !is_operator(input[0]) { | |
name.push(input.pop_front().unwrap() as char) | |
} | |
return name; | |
} | |
fn parse_var(input: &mut VecDeque<u8>) -> AST { | |
return AST::Var(parse_text(input)); | |
} | |
fn is_operator(c: u8) -> bool { | |
match c as char { | |
'+' | '-' | '*' | '/' | '@' | '^' | ':' | '(' | ')' | ' ' | '=' => true, | |
_ => false, | |
} | |
} | |
fn parse_single(input: &mut VecDeque<u8>) -> AST { | |
let c = input[0]; | |
let ast = match c { | |
c if c.is_ascii_digit() => parse_num(input), | |
c if c == '-' as u8 => { | |
input.pop_front(); | |
AST::Neg(Box::new(parse(input))) | |
} | |
c if c == '@' as u8 => { | |
input.pop_front(); | |
let name = parse_text(input); | |
AST::Func(name, Box::new(parse_single(input))) | |
} | |
c if c == '(' as u8 => { | |
input.pop_front(); | |
let brace = parse_single(input); | |
if input.pop_front().unwrap() != ')' as u8 { | |
panic!("Expected ')'") | |
} | |
return brace; | |
} | |
_ => parse_var(input), | |
}; | |
check_extension(input, ast) | |
} | |
fn check_extension(input: &mut VecDeque<u8>, ast: AST) -> AST { | |
if input.is_empty() { | |
return ast; | |
} | |
let c = input[0]; | |
let ast = match c as char { | |
'+' => { | |
input.pop_front(); | |
AST::Add(Box::new(ast), Box::new(parse_single(input))) | |
} | |
'-' => { | |
input.pop_front(); | |
AST::Sub(Box::new(ast), Box::new(parse_single(input))) | |
} | |
'*' => { | |
input.pop_front(); | |
AST::Mul(Box::new(ast), Box::new(parse_single(input))) | |
} | |
'/' => { | |
input.pop_front(); | |
AST::Div(Box::new(ast), Box::new(parse_single(input))) | |
} | |
'^' => { | |
input.pop_front(); | |
AST::Pow(Box::new(ast), Box::new(parse_single(input))) | |
} | |
' ' => { | |
input.pop_front(); | |
ast | |
} | |
_ => return ast, | |
}; | |
return check_extension(input, ast); | |
} | |
fn parse(input: &mut VecDeque<u8>) -> AST { | |
if input.is_empty() { | |
return AST::None(); | |
} | |
let next = parse_single(input); | |
return check_extension(input, next); | |
} | |
fn parse_vars(input: &mut VecDeque<u8>) -> HashMap<String, AST> { | |
let mut map: HashMap<String, AST> = HashMap::new(); | |
while !input.is_empty() { | |
let name = parse_text(input); | |
if input.pop_front().unwrap() != '=' as u8 { | |
panic!("Expected '=' ") | |
} | |
let val = parse(input); | |
map.insert(name, val); | |
} | |
map | |
} | |
fn main() -> io::Result<()> { | |
let mut buffer: String = String::new(); | |
io::stdin().read_line(&mut buffer)?; | |
buffer = buffer.trim().into(); | |
let mut input = buffer.chars().map(|c| c as u8).collect::<VecDeque<u8>>(); | |
let ast = parse(&mut input); | |
loop { | |
let mut buffer: String = String::new(); | |
io::stdin().read_line(&mut buffer)?; | |
buffer = buffer.trim().into(); | |
let mut input = buffer.chars().map(|c| c as u8).collect::<VecDeque<u8>>(); | |
let vars = parse_vars(&mut input); | |
println!("= {}", ast.eval(&vars)); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment