use std::fmt;
use lalrpop_util::{lalrpop_mod, ParseError};
use libm::tgamma;
pub mod context;
use context::*;
mod ast;
use ast::*;
lalrpop_mod!(grammar);
pub fn calculate(input_str: &str, ctx: &mut Context) -> Result<f64, CalcError> {
let input_str = if let Some(stripped) = input_str.strip_suffix('\n') { stripped } else { input_str };
let parser = grammar::targetParser::new();
let (tree, assignment) = match parser.parse(input_str) {
Ok(res) => { res }
Err(e) => {
let msg = match &e {
ParseError::InvalidToken { location } => {
let pad = std::iter::repeat(" ").take(*location).collect::<String>();
format!("Invalid token\n| {input_str}\n| {pad}└── here")
},
ParseError::UnrecognizedEof { location: _, expected: _ } => {
String::from("Unexpected EOI")
},
ParseError::UnrecognizedToken { token, expected: _ } => {
let pad = std::iter::repeat(" ").take(token.0).collect::<String>();
format!("Unexpected token\n| {input_str}\n| {pad}└── here")
},
ParseError::ExtraToken { token} => {
let pad = std::iter::repeat(" ").take(token.0).collect::<String>();
format!("Extra token\n| {input_str}\n| {pad}└── here")
},
_ => String::from("Parser error"),
};
return Err(CalcError {
error_type: CalcErrorType::ParserError,
msg,
});
}
};
let res = evaluate_ast(*tree, ctx);
if res.is_ok() {
let solution = res.clone().unwrap();
if assignment.is_some() {
let assign_var = assignment.unwrap();
if let Err(e) = ctx.assign_var(&assign_var, solution) {
return Err(e);
}
} else {
ctx.prev_ans = Some(solution);
}
}
return res;
}
fn evaluate_ast(root: Expr, ctx: &Context) -> Result<f64, CalcError> {
match root {
Expr::Num(n) => {
Ok(n)
}
Expr::Op(left_e, op, right_e) => {
let lhs = match evaluate_ast(*left_e, ctx) {
Ok(n) => n,
Err(e) => { return Err(e) },
};
let rhs = match evaluate_ast(*right_e, ctx) {
Ok(n) => n,
Err(e) => { return Err(e) },
};
let res = match op {
Operation::Add => { lhs + rhs }
Operation::Sub => { lhs - rhs }
Operation::Mul => { lhs * rhs }
Operation::Div => { lhs / rhs }
Operation::FloorDiv => { f64::floor(lhs / rhs) }
Operation::Mod => { lhs % rhs }
Operation::Exp => { lhs.powf(rhs) }
};
Ok(res)
}
Expr::Func(name, arg_list) => {
let mut args: Vec<f64> = Vec::new();
for arg in arg_list {
let val = match evaluate_ast(*arg, ctx) {
Ok(n) => n,
Err(e) => { return Err(e) },
};
args.push(val);
}
if let Some(res) = ctx.try_function(&name, args) {
return res;
}
return Err(CalcError {
error_type: CalcErrorType::UndefinedIdentifier,
msg: format!("Unknown function \"{name}()\""),
})
}
Expr::Var(name) => {
if let Some(res) = ctx.lookup_var(&name) {
return res;
}
return Err(CalcError {
error_type: CalcErrorType::UndefinedIdentifier,
msg: format!("Unknown variable \"{name}\""),
})
}
Expr::Fac(e) => {
let num = match evaluate_ast(*e, ctx) {
Ok(n) => n,
Err(e) => { return Err(e) },
};
return Ok(tgamma(num + 1.0));
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct CalcError {
pub error_type: CalcErrorType,
pub msg: String,
}
impl fmt::Display for CalcError {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "{}: {}\n", self.error_type, self.msg)
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum CalcErrorType {
ParserError,
UndefinedIdentifier,
AssignmentError,
ArgumentError,
CalculationError,
}
impl fmt::Display for CalcErrorType {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "{}", match *self {
Self::ParserError => { "Parser error" },
Self::UndefinedIdentifier => { "Undefined identifier" },
Self::AssignmentError => { "Assignment error" },
Self::ArgumentError => { "Argument error" },
Self::CalculationError => { "Calculation error" },
})
}
}