use crate::{CalcError, CalcErrorType};
mod builtins;
#[cfg(test)]
mod tests;
pub struct Context {
var_table: Vec<VarTableEntry>,
pub prev_ans: Option<f64>,
function_table: Vec<Function>
}
impl Context {
pub fn new() -> Self {
Self {
var_table: builtins::get_consts(),
function_table: builtins::get_functions(),
prev_ans: None,
}
}
pub fn lookup_var(&self, query: &String) -> Option<Result<f64, CalcError>> {
if query.eq("ans") {
if let Some(ans) = self.prev_ans {
return Some(Ok(ans));
} else {
return Some(Err(CalcError {
error_type: CalcErrorType::CalculationError,
msg: "Cannot use \'ans\' without a previous evaluated equation".to_string(),
}));
}
}
for entry in &self.var_table {
if entry.name.eq(query) {
return Some(Ok(entry.value));
}
}
return None;
}
pub fn try_function(&self, name: &String, args: Vec<f64>) -> Option<Result<f64, CalcError>> {
for f in self.function_table.iter() {
if !f.name.eq(name) { continue; }
if f.num_args != 0 && f.num_args != args.len() {
return Some(Err(CalcError {
error_type: CalcErrorType::ArgumentError,
msg: "Invalid number of arguments".to_string(),
}));
}
return Some((f.closure)(args));
}
return None;
}
pub fn assign_var(&mut self, query: &String, val: f64) -> Result<(), CalcError> {
for entry in &mut self.var_table {
if entry.name.eq(query) {
if entry.constant {
return Err(CalcError {
error_type: CalcErrorType::AssignmentError,
msg: format!("Can't assign value to constant \'{}\'", entry.name).to_string()
});
}
entry.value = val;
return Ok(());
}
}
self.var_table.push(VarTableEntry {
name: query.clone(),
value: val,
constant: false,
});
return Ok(());
}
}
struct VarTableEntry {
pub name: String,
pub value: f64,
pub constant: bool,
}
struct Function {
name: String,
num_args: usize,
closure: Box<dyn Fn(Vec<f64>) -> Result<f64, CalcError>>
}