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}