1#[derive(Debug, Clone, PartialEq)]
3pub struct Span {
4 pub offset: usize, pub length: usize,
6}
7
8impl Span {
9 pub fn new(offset: usize, length: usize) -> Self {
10 Self { offset, length }
11 }
12}
13
14#[derive(Debug, Clone, PartialEq)]
15pub enum UnaryOp {
16 Neg, Percent, }
19
20#[derive(Debug, Clone, PartialEq)]
21pub enum BinaryOp {
22 Add, Sub, Mul, Div, Pow,
23 Concat, Eq, Ne, Lt, Gt, Le, Ge,
25}
26
27#[derive(Debug, Clone, PartialEq)]
28pub enum Expr {
29 Number(f64, Span),
30 Text(String, Span),
31 Bool(bool, Span),
32 Variable(String, Span),
33 UnaryOp {
34 op: UnaryOp,
35 operand: Box<Expr>,
36 span: Span,
37 },
38 BinaryOp {
39 op: BinaryOp,
40 left: Box<Expr>,
41 right: Box<Expr>,
42 span: Span,
43 },
44 FunctionCall {
45 name: String, args: Vec<Expr>,
47 span: Span,
48 },
49 Array(Vec<Expr>, Span),
50 Apply {
53 func: Box<Expr>,
54 call_args: Vec<Expr>,
55 span: Span,
56 },
57}
58
59impl Expr {
60 pub fn span(&self) -> &Span {
61 match self {
62 Expr::Number(_, s) | Expr::Text(_, s) | Expr::Bool(_, s) | Expr::Variable(_, s) => s,
63 Expr::UnaryOp { span, .. }
64 | Expr::BinaryOp { span, .. }
65 | Expr::FunctionCall { span, .. }
66 | Expr::Apply { span, .. } => span,
67 Expr::Array(_, span) => span,
68 }
69 }
70}
71
72#[cfg(test)]
73mod tests {
74 use super::*;
75
76 #[test]
77 fn span_stores_offset_and_length() {
78 let s = Span::new(5, 10);
79 assert_eq!(s.offset, 5);
80 assert_eq!(s.length, 10);
81 }
82
83 #[test]
84 fn expr_number_span() {
85 let e = Expr::Number(1.0, Span::new(0, 3));
86 assert_eq!(e.span().offset, 0);
87 assert_eq!(e.span().length, 3);
88 }
89
90 #[test]
91 fn expr_text_span() {
92 let e = Expr::Text("hello".into(), Span::new(2, 7));
93 assert_eq!(e.span().offset, 2);
94 }
95
96 #[test]
97 fn expr_bool_span() {
98 let e = Expr::Bool(true, Span::new(1, 4));
99 assert_eq!(e.span().offset, 1);
100 }
101
102 #[test]
103 fn expr_function_call_span() {
104 let e = Expr::FunctionCall {
105 name: "SUM".into(),
106 args: vec![],
107 span: Span::new(0, 5),
108 };
109 assert_eq!(e.span().offset, 0);
110 assert_eq!(e.span().length, 5);
111 }
112
113 #[test]
114 fn unary_op_debug() {
115 assert_eq!(format!("{:?}", UnaryOp::Neg), "Neg");
116 assert_eq!(format!("{:?}", UnaryOp::Percent), "Percent");
117 }
118
119 #[test]
120 fn binary_op_debug() {
121 assert_eq!(format!("{:?}", BinaryOp::Add), "Add");
122 assert_eq!(format!("{:?}", BinaryOp::Eq), "Eq");
123 }
124
125 #[test]
126 fn expr_variable_span() {
127 let e = Expr::Variable("x".into(), Span::new(0, 1));
128 assert_eq!(e.span().offset, 0);
129 assert_eq!(e.span().length, 1);
130 }
131
132 #[test]
133 fn expr_unary_op_span() {
134 let e = Expr::UnaryOp {
135 op: UnaryOp::Neg,
136 operand: Box::new(Expr::Number(1.0, Span::new(1, 1))),
137 span: Span::new(0, 2),
138 };
139 assert_eq!(e.span().offset, 0);
140 assert_eq!(e.span().length, 2);
141 }
142
143 #[test]
144 fn expr_binary_op_span() {
145 let e = Expr::BinaryOp {
146 op: BinaryOp::Add,
147 left: Box::new(Expr::Number(1.0, Span::new(0, 1))),
148 right: Box::new(Expr::Number(2.0, Span::new(2, 1))),
149 span: Span::new(0, 3),
150 };
151 assert_eq!(e.span().offset, 0);
152 assert_eq!(e.span().length, 3);
153 }
154
155 #[test]
156 fn expr_apply_span() {
157 let e = Expr::Apply {
158 func: Box::new(Expr::Variable("f".into(), Span::new(0, 1))),
159 call_args: vec![],
160 span: Span::new(0, 4),
161 };
162 assert_eq!(e.span().offset, 0);
163 assert_eq!(e.span().length, 4);
164 }
165}