exercises

Log | Files | Refs | README

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 }