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