operator.rs (5733B)
1 use std::{collections::HashMap, f32::consts::FRAC_PI_2}; 2 3 use crate::{Expression, OperatorToken, token::Function}; 4 5 #[derive(Debug, Clone, PartialEq)] 6 pub enum Operator { 7 Add, 8 Subtract, 9 Multiply, 10 Divide, 11 Power, 12 Root, 13 Factorial, 14 } 15 16 #[derive(Clone)] 17 pub struct StandardOperator(pub Operator); 18 19 impl OperatorToken for StandardOperator { 20 fn precedence(&self) -> u8 { 21 match self.0 { 22 Operator::Add | Operator::Subtract => 1, 23 Operator::Multiply | Operator::Divide => 2, 24 Operator::Power => 3, 25 Operator::Root | Operator::Factorial => 4, 26 } 27 } 28 29 fn evaluate(&self, args: &[f64]) -> Result<f64, String> { 30 match self.0 { 31 Operator::Add => Ok(args[0] + args[1]), 32 Operator::Subtract => Ok(args[0] - args[1]), 33 Operator::Multiply => Ok(args[0] * args[1]), 34 Operator::Divide => { 35 if args[1] == 0.0 { 36 Err("Division by zero".to_string()) 37 } else { 38 Ok(args[0] / args[1]) 39 } 40 } 41 // ... other operators 42 _ => Err("Operation not supported in standard mode".to_string()), 43 } 44 } 45 } 46 47 #[derive(Clone)] 48 pub enum ScientificOperator { 49 Basic(Operator), 50 Function(Function), 51 } 52 53 impl OperatorToken for ScientificOperator { 54 fn precedence(&self) -> u8 { 55 todo!() 56 } 57 58 fn evaluate(&self, args: &[f64]) -> Result<f64, String> { 59 todo!() 60 } 61 } 62 63 /// Composite Pattern: this is a composite node, it holds two child expressions 64 /// and an operator. The children are `Box<dyn Expression>` trait objects, which 65 /// means each child can be any expression type: a number, a variable, another 66 /// binary operation, or even a decorated expression. 67 /// 68 /// We use `Box<dyn Expression>` rather than generic type parameters because 69 /// generic would make each `BinaryOperation` monomorphic (specialized to one 70 /// concrete type) over its children's types. 71 pub struct BinaryOperation { 72 pub left: Box<dyn Expression>, 73 pub right: Box<dyn Expression>, 74 pub operator: Operator, 75 } 76 77 impl BinaryOperation { 78 pub fn new(left: Box<dyn Expression>, right: Box<dyn Expression>, operator: Operator) -> Self { 79 Self { 80 left, 81 right, 82 operator, 83 } 84 } 85 86 fn operator_symbol(&self) -> &'static str { 87 match self.operator { 88 Operator::Add => "+", 89 Operator::Subtract => "-", 90 Operator::Multiply => "*", 91 Operator::Divide => "/", 92 Operator::Power => "^", 93 Operator::Root => todo!(), 94 Operator::Factorial => todo!(), 95 } 96 } 97 } 98 99 impl Expression for BinaryOperation { 100 fn evaluate(&self, variables: &std::collections::HashMap<String, f64>) -> Result<f64, String> { 101 // Evaluates both children recursively 102 let l = self.left.evaluate(variables)?; 103 let r = self.right.evaluate(variables)?; 104 105 match self.operator { 106 Operator::Add => Ok(l + r), 107 Operator::Subtract => Ok(l - r), 108 Operator::Multiply => Ok(l * r), 109 Operator::Divide if r == 0.0 => Err("Division by zero".to_string()), 110 Operator::Divide => Ok(l / r), 111 Operator::Power => Ok(l.powf(r)), 112 _ => Ok(l + r), 113 } 114 } 115 116 fn to_string(&self) -> String { 117 let left_str = if self.left.precedence() < self.precedence() { 118 format!("({})", self.left.to_string()) 119 } else { 120 self.left.to_string() 121 }; 122 123 let right_str = if self.right.precedence() < self.precedence() { 124 format!("({})", self.right.to_string()) 125 } else { 126 self.right.to_string() 127 }; 128 129 format!("{} {} {}", left_str, self.operator_symbol(), right_str) 130 } 131 132 fn precedence(&self) -> u8 { 133 match self.operator { 134 Operator::Add | Operator::Subtract => 1, 135 Operator::Multiply | Operator::Divide => 2, 136 Operator::Power => 3, 137 Operator::Root => todo!(), 138 Operator::Factorial => todo!(), 139 } 140 } 141 } 142 143 /// Like BinaryOperation, FunctionCall demonstrates the recursive nature of 144 /// the Composite pattern: the argument can itself be an arbitrarily complex 145 /// expression tree, and the uniform Expression interface handles any depth 146 /// of nesting. 147 pub struct FunctionCall { 148 pub function: Function, 149 pub argument: Box<dyn Expression>, 150 } 151 152 impl Expression for FunctionCall { 153 fn evaluate(&self, variables: &HashMap<String, f64>) -> Result<f64, String> { 154 let val = self.argument.evaluate(variables)?; 155 156 match self.function { 157 Function::Sin => Ok(val.sin()), 158 Function::Cos => Ok(val.cos()), 159 Function::Tan => { 160 let hp = std::f64::consts::FRAC_PI_2; 161 if (val - hp).abs() % std::f64::consts::PI < 1e-10 { 162 Err("Targent undefined at this value".into()) 163 } else { 164 Ok(val.tan()) 165 } 166 } 167 Function::Sqrt if val < 0.0 => Err("Cannot take square root of negative number".into()), 168 Function::Sqrt => Ok(val.sqrt()), 169 } 170 } 171 172 /// This method reconstructs the function call syntax for display purpose 173 fn to_string(&self) -> String { 174 let func_name = match self.function { 175 Function::Sqrt => "sqrt", 176 Function::Sin => "sin", 177 Function::Cos => "cos", 178 Function::Tan => "tan", 179 }; 180 format!("{}({})", func_name, self.argument.to_string()) 181 } 182 183 fn precedence(&self) -> u8 { 184 4 185 } 186 }