1use crate::kinds::Kind;
4
5pub const MAX_EXPR_DEPTH: usize = 64;
7
8pub const MAX_EXPR_SOURCE: usize = 4096;
10
11#[derive(Debug, Clone, PartialEq)]
13pub enum BinOp {
14 Add,
15 Sub,
16 Mul,
17 Div,
18 Mod,
19}
20
21#[derive(Debug, Clone, PartialEq)]
23pub enum UnOp {
24 Neg,
25 Not,
26}
27
28#[derive(Debug, Clone, PartialEq)]
30pub enum CmpOp {
31 Eq,
32 Ne,
33 Lt,
34 Le,
35 Gt,
36 Ge,
37}
38
39#[derive(Debug, Clone, PartialEq)]
41pub enum LogicOp {
42 And,
43 Or,
44}
45
46#[derive(Debug, Clone)]
48pub enum ExprNode {
49 Literal(Kind),
51 Variable(String),
53 BinaryOp {
55 left: Box<ExprNode>,
56 op: BinOp,
57 right: Box<ExprNode>,
58 },
59 UnaryOp { op: UnOp, operand: Box<ExprNode> },
61 Comparison {
63 left: Box<ExprNode>,
64 op: CmpOp,
65 right: Box<ExprNode>,
66 },
67 Logical {
69 left: Box<ExprNode>,
70 op: LogicOp,
71 right: Box<ExprNode>,
72 },
73 FnCall { name: String, args: Vec<ExprNode> },
75 Conditional {
77 cond: Box<ExprNode>,
78 then_expr: Box<ExprNode>,
79 else_expr: Box<ExprNode>,
80 },
81}
82
83#[cfg(test)]
84mod tests {
85 use super::*;
86 use crate::kinds::Number;
87
88 #[test]
89 fn construct_literal() {
90 let node = ExprNode::Literal(Kind::Number(Number::unitless(42.0)));
91 assert!(matches!(node, ExprNode::Literal(Kind::Number(_))));
92 }
93
94 #[test]
95 fn construct_variable() {
96 let node = ExprNode::Variable("temp".into());
97 if let ExprNode::Variable(name) = &node {
98 assert_eq!(name, "temp");
99 } else {
100 panic!("expected Variable");
101 }
102 }
103
104 #[test]
105 fn construct_binary_op() {
106 let node = ExprNode::BinaryOp {
107 left: Box::new(ExprNode::Literal(Kind::Number(Number::unitless(1.0)))),
108 op: BinOp::Add,
109 right: Box::new(ExprNode::Literal(Kind::Number(Number::unitless(2.0)))),
110 };
111 assert!(matches!(node, ExprNode::BinaryOp { op: BinOp::Add, .. }));
112 }
113
114 #[test]
115 fn construct_unary_op() {
116 let node = ExprNode::UnaryOp {
117 op: UnOp::Neg,
118 operand: Box::new(ExprNode::Literal(Kind::Number(Number::unitless(5.0)))),
119 };
120 assert!(matches!(node, ExprNode::UnaryOp { op: UnOp::Neg, .. }));
121 }
122
123 #[test]
124 fn construct_comparison() {
125 let node = ExprNode::Comparison {
126 left: Box::new(ExprNode::Variable("x".into())),
127 op: CmpOp::Gt,
128 right: Box::new(ExprNode::Literal(Kind::Number(Number::unitless(10.0)))),
129 };
130 assert!(matches!(node, ExprNode::Comparison { op: CmpOp::Gt, .. }));
131 }
132
133 #[test]
134 fn construct_logical() {
135 let node = ExprNode::Logical {
136 left: Box::new(ExprNode::Literal(Kind::Bool(true))),
137 op: LogicOp::And,
138 right: Box::new(ExprNode::Literal(Kind::Bool(false))),
139 };
140 assert!(matches!(
141 node,
142 ExprNode::Logical {
143 op: LogicOp::And,
144 ..
145 }
146 ));
147 }
148
149 #[test]
150 fn construct_fn_call() {
151 let node = ExprNode::FnCall {
152 name: "abs".into(),
153 args: vec![ExprNode::Literal(Kind::Number(Number::unitless(-3.0)))],
154 };
155 if let ExprNode::FnCall { name, args } = &node {
156 assert_eq!(name, "abs");
157 assert_eq!(args.len(), 1);
158 } else {
159 panic!("expected FnCall");
160 }
161 }
162
163 #[test]
164 fn construct_conditional() {
165 let node = ExprNode::Conditional {
166 cond: Box::new(ExprNode::Literal(Kind::Bool(true))),
167 then_expr: Box::new(ExprNode::Literal(Kind::Number(Number::unitless(1.0)))),
168 else_expr: Box::new(ExprNode::Literal(Kind::Number(Number::unitless(0.0)))),
169 };
170 assert!(matches!(node, ExprNode::Conditional { .. }));
171 }
172
173 #[test]
174 fn clone_and_debug() {
175 let node = ExprNode::Literal(Kind::Str("test".into()));
176 let cloned = node.clone();
177 let debug = format!("{:?}", cloned);
178 assert!(debug.contains("Literal"));
179 }
180
181 #[test]
182 fn binop_variants() {
183 let ops = [BinOp::Add, BinOp::Sub, BinOp::Mul, BinOp::Div, BinOp::Mod];
184 for op in &ops {
185 assert_eq!(op.clone(), op.clone());
186 }
187 }
188
189 #[test]
190 fn cmpop_variants() {
191 let ops = [
192 CmpOp::Eq,
193 CmpOp::Ne,
194 CmpOp::Lt,
195 CmpOp::Le,
196 CmpOp::Gt,
197 CmpOp::Ge,
198 ];
199 for op in &ops {
200 assert_eq!(op.clone(), op.clone());
201 }
202 }
203}