rust_expression/
ast.rs

1pub type Number = f64;
2
3#[derive(Debug, PartialEq, Clone)]
4pub struct FunCall {
5    pub name: String,
6    pub params: Vec<Operand>,
7}
8
9#[derive(Debug, PartialEq, Clone)]
10pub enum Operand {
11    Number(Number),
12    Symbol(String),
13    Term(Box<Term>),
14    FunCall(FunCall),
15}
16
17impl Operand {
18    pub fn is_symbol(&self, sym: &str) -> bool {
19        matches!(self, Operand::Symbol(s) if s == sym)
20    }
21}
22
23#[derive(Debug, PartialEq, Eq, Clone, Copy)]
24pub enum Operation {
25    Add,
26    Sub,
27    Mul,
28    Div,
29    Rem,
30    Pow,
31}
32
33#[derive(Debug, PartialEq, Clone)]
34pub struct Term {
35    pub op: Operation,
36    pub lhs: Operand,
37    pub rhs: Operand,
38}
39
40#[derive(Debug, PartialEq, Clone)]
41pub struct CustomFunction {
42    pub args: Vec<String>,
43    pub body: Operand,
44}
45
46#[derive(Clone)]
47pub struct BuildInFunction {
48    pub name: String,
49    pub arg: String,
50    pub body: &'static dyn Fn(Number) -> Number,
51}
52
53impl PartialEq for BuildInFunction {
54    fn eq(&self, other: &Self) -> bool {
55        self.name == other.name && self.arg == other.arg
56    }
57}
58
59impl std::fmt::Debug for BuildInFunction {
60    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
61        f.debug_struct("BuildInFunction")
62            .field("name", &self.name)
63            .field("arg", &self.arg)
64            .finish()
65    }
66}
67
68#[derive(Debug, PartialEq, Clone)]
69pub enum Function {
70    Custom(CustomFunction),
71    BuildIn(BuildInFunction),
72}
73
74impl Default for Function {
75    fn default() -> Self {
76        Function::Custom(CustomFunction {
77            args: Vec::new(),
78            body: Operand::Number(1.0),
79        })
80    }
81}
82
83#[derive(Debug, PartialEq, Clone)]
84pub enum Statement {
85    Expression {
86        op: Operand,
87    },
88    Assignment {
89        sym: String,
90        op: Operand,
91    },
92    SolveFor {
93        lhs: Operand,
94        rhs: Operand,
95        sym: String,
96    },
97    Function {
98        name: String,
99        fun: Function,
100    },
101    Plot {
102        name: String,
103    },
104}
105
106#[cfg(test)]
107mod tests {
108    use super::*;
109    fn create_term() -> Term {
110        let lhs = Operand::Number(1.0);
111        let rhs = Operand::Number(1.0);
112        let op = Operation::Add;
113        Term { op, lhs, rhs }
114    }
115
116    #[test]
117    fn operand_is_symbol() {
118        assert!(Operand::Symbol("x".to_string()).is_symbol("x"));
119    }
120
121    #[test]
122    fn operand_is_not_symbol() {
123        assert!(!Operand::Symbol("y".to_string()).is_symbol("x"));
124        assert!(!Operand::Number(1.0).is_symbol("x"));
125        assert!(!Operand::Term(Box::new(create_term())).is_symbol("x"));
126    }
127}