1pub mod ast;
2pub mod tokens;
3
4pub use ast::Expr;
5use ast::{BinaryOp, Span, UnaryOp};
6use crate::types::ParseError;
7use nom::{IResult, character::complete::multispace0};
8use tokens::{bool_literal, identifier, number_literal, offset, string_literal};
9
10struct Parser<'a> {
11 full: &'a str,
12}
13
14impl<'a> Parser<'a> {
15 fn new(full: &'a str) -> Self {
16 Self { full }
17 }
18
19 fn span(&self, before: &str, after: &str) -> Span {
20 let start = offset(self.full, before);
21 let end = offset(self.full, after);
22 Span::new(start, end - start)
23 }
24
25 fn parse_primary(&self, i: &'a str) -> IResult<&'a str, Expr> {
28 let i = multispace0(i)?.0;
29
30 if let Ok((rest, n)) = number_literal(i) {
32 return Ok((rest, Expr::Number(n, self.span(i, rest))));
33 }
34
35 if let Ok((rest, text)) = string_literal(i) {
37 return Ok((rest, Expr::Text(text, self.span(i, rest))));
38 }
39
40 if let Some(inner) = i.strip_prefix('(') {
42 let (rest, expr) = self.parse_comparison(inner)?;
43 let rest = multispace0(rest)?.0;
44 if let Some(after) = rest.strip_prefix(')') {
45 return Ok((after, expr));
46 }
47 return Err(nom::Err::Error(nom::error::Error::new(
48 rest,
49 nom::error::ErrorKind::Char,
50 )));
51 }
52
53 if let Ok((rest, b)) = bool_literal(i) {
55 return Ok((rest, Expr::Bool(b, self.span(i, rest))));
56 }
57
58 if let Ok((rest, name)) = identifier(i) {
60 let rest_ws = multispace0(rest)?.0;
61 if let Some(args_input) = rest_ws.strip_prefix('(') {
62 let (rest2, args) = self.parse_arg_list(args_input)?;
64 let rest2 = multispace0(rest2)?.0;
65 if let Some(after) = rest2.strip_prefix(')') {
66 return Ok((after, Expr::FunctionCall {
67 name: name.to_uppercase(),
68 args,
69 span: self.span(i, after),
70 }));
71 }
72 return Err(nom::Err::Error(nom::error::Error::new(
73 rest2,
74 nom::error::ErrorKind::Char,
75 )));
76 }
77 return Ok((rest, Expr::Variable(name.to_string(), self.span(i, rest))));
78 }
79
80 Err(nom::Err::Error(nom::error::Error::new(i, nom::error::ErrorKind::Alt)))
81 }
82
83 fn parse_arg_list(&self, i: &'a str) -> IResult<&'a str, Vec<Expr>> {
84 let mut args = Vec::new();
85 let mut rest = multispace0(i)?.0;
86
87 if rest.starts_with(')') {
88 return Ok((rest, args));
89 }
90
91 let (r, first) = self.parse_comparison(rest)?;
92 args.push(first);
93 rest = r;
94
95 loop {
96 rest = multispace0(rest)?.0;
97 if let Some(after_comma) = rest.strip_prefix(',') {
98 let (r, arg) = self.parse_comparison(after_comma)?;
99 args.push(arg);
100 rest = r;
101 } else {
102 break;
103 }
104 }
105
106 Ok((rest, args))
107 }
108
109 fn parse_postfix(&self, i: &'a str) -> IResult<&'a str, Expr> {
112 let (rest, expr) = self.parse_primary(i)?;
113 let rest_ws = multispace0(rest)?.0;
114 if let Some(after) = rest_ws.strip_prefix('%') {
115 return Ok((after, Expr::UnaryOp {
116 op: UnaryOp::Percent,
117 operand: Box::new(expr),
118 span: self.span(i, after),
119 }));
120 }
121 Ok((rest, expr))
122 }
123
124 fn parse_unary(&self, i: &'a str) -> IResult<&'a str, Expr> {
127 let i_ws = multispace0(i)?.0;
128 if let Some(after_minus) = i_ws.strip_prefix('-') {
129 let (rest, operand) = self.parse_unary(after_minus)?;
130 return Ok((rest, Expr::UnaryOp {
131 op: UnaryOp::Neg,
132 operand: Box::new(operand),
133 span: self.span(i_ws, rest),
134 }));
135 }
136 self.parse_postfix(i)
137 }
138
139 fn parse_power(&self, i: &'a str) -> IResult<&'a str, Expr> {
142 let (rest, left) = self.parse_unary(i)?;
143 let rest_ws = multispace0(rest)?.0;
144 if let Some(after_op) = rest_ws.strip_prefix('^') {
145 let (rest2, right) = self.parse_power(after_op)?;
146 return Ok((rest2, Expr::BinaryOp {
147 op: BinaryOp::Pow,
148 left: Box::new(left),
149 right: Box::new(right),
150 span: self.span(i, rest2),
151 }));
152 }
153 Ok((rest, left))
154 }
155
156 fn parse_multiplicative(&self, i: &'a str) -> IResult<&'a str, Expr> {
159 let (mut rest, mut left) = self.parse_power(i)?;
160 loop {
161 let ws = multispace0(rest)?.0;
162 let op = ws.strip_prefix('*').map(|after| (BinaryOp::Mul, after))
163 .or_else(|| ws.strip_prefix('/').map(|after| (BinaryOp::Div, after)));
164 match op {
165 None => break,
166 Some((op, after)) => {
167 let (r, right) = self.parse_power(after)?;
168 left = Expr::BinaryOp {
169 op,
170 span: self.span(i, r),
171 left: Box::new(left),
172 right: Box::new(right),
173 };
174 rest = r;
175 }
176 }
177 }
178 Ok((rest, left))
179 }
180
181 fn parse_additive(&self, i: &'a str) -> IResult<&'a str, Expr> {
184 let (mut rest, mut left) = self.parse_multiplicative(i)?;
185 loop {
186 let ws = multispace0(rest)?.0;
187 let op = ws.strip_prefix('+').map(|after| (BinaryOp::Add, after))
188 .or_else(|| ws.strip_prefix('-').map(|after| (BinaryOp::Sub, after)));
189 match op {
190 None => break,
191 Some((op, after)) => {
192 let (r, right) = self.parse_multiplicative(after)?;
193 left = Expr::BinaryOp {
194 op,
195 span: self.span(i, r),
196 left: Box::new(left),
197 right: Box::new(right),
198 };
199 rest = r;
200 }
201 }
202 }
203 Ok((rest, left))
204 }
205
206 fn parse_concat(&self, i: &'a str) -> IResult<&'a str, Expr> {
209 let (mut rest, mut left) = self.parse_additive(i)?;
210 loop {
211 let ws = multispace0(rest)?.0;
212 if let Some(after) = ws.strip_prefix('&') {
213 let (r, right) = self.parse_additive(after)?;
214 left = Expr::BinaryOp {
215 op: BinaryOp::Concat,
216 span: self.span(i, r),
217 left: Box::new(left),
218 right: Box::new(right),
219 };
220 rest = r;
221 } else {
222 break;
223 }
224 }
225 Ok((rest, left))
226 }
227
228 fn parse_comparison(&self, i: &'a str) -> IResult<&'a str, Expr> {
231 let (rest, left) = self.parse_concat(i)?;
232 let ws = multispace0(rest)?.0;
233
234 let op_result: Option<(BinaryOp, &'a str)> = if let Some(after) = ws.strip_prefix("<>") {
236 Some((BinaryOp::Ne, after))
237 } else if let Some(after) = ws.strip_prefix("<=") {
238 Some((BinaryOp::Le, after))
239 } else if let Some(after) = ws.strip_prefix(">=") {
240 Some((BinaryOp::Ge, after))
241 } else if let Some(after) = ws.strip_prefix('<') {
242 Some((BinaryOp::Lt, after))
243 } else if let Some(after) = ws.strip_prefix('>') {
244 Some((BinaryOp::Gt, after))
245 } else if let Some(after) = ws.strip_prefix('=') {
246 Some((BinaryOp::Eq, after))
247 } else {
248 None
249 };
250
251 if let Some((op, after)) = op_result {
252 let (r, right) = self.parse_concat(after)?;
253 return Ok((r, Expr::BinaryOp {
254 op,
255 span: self.span(i, r),
256 left: Box::new(left),
257 right: Box::new(right),
258 }));
259 }
260
261 Ok((rest, left))
262 }
263}
264
265pub fn parse(formula: &str) -> Result<Expr, ParseError> {
272 let input = formula.strip_prefix('=').unwrap_or(formula).trim();
273 let p = Parser::new(formula);
274 match p.parse_comparison(input) {
275 Ok((rest, expr)) => {
276 let rest = rest.trim();
277 if rest.is_empty() {
278 Ok(expr)
279 } else {
280 Err(ParseError {
281 message: format!("Unexpected input '{}'", rest),
282 position: offset(formula, rest),
283 })
284 }
285 }
286 Err(nom::Err::Error(e)) | Err(nom::Err::Failure(e)) => Err(ParseError {
287 message: "Parse error".into(),
288 position: offset(formula, e.input),
289 }),
290 Err(nom::Err::Incomplete(_)) => Err(ParseError {
291 message: "Incomplete input".into(),
292 position: formula.len(),
293 }),
294 }
295}
296
297pub fn validate(formula: &str) -> Result<(), ParseError> {
299 parse(formula).map(|_| ())
300}
301
302#[cfg(test)]
303mod tests {
304 use super::*;
305 use crate::parser::ast::{BinaryOp, Expr, UnaryOp};
306
307 #[test]
308 fn parse_number_literal() {
309 let expr = parse("=42").unwrap();
310 assert!(matches!(expr, Expr::Number(n, _) if n == 42.0));
311 }
312
313 #[test]
314 fn parse_binary_add() {
315 let expr = parse("=1+2").unwrap();
316 assert!(matches!(expr, Expr::BinaryOp { op: BinaryOp::Add, .. }));
317 }
318
319 #[test]
320 fn parse_precedence() {
321 let expr = parse("=2+3*4").unwrap();
323 match expr {
324 Expr::BinaryOp { op: BinaryOp::Add, right, .. } => {
325 assert!(matches!(*right, Expr::BinaryOp { op: BinaryOp::Mul, .. }));
326 }
327 _ => panic!("Expected Add at top"),
328 }
329 }
330
331 #[test]
332 fn parse_function_call() {
333 let expr = parse("=SUM(1,2,3)").unwrap();
334 match expr {
335 Expr::FunctionCall { name, args, .. } => {
336 assert_eq!(name, "SUM");
337 assert_eq!(args.len(), 3);
338 }
339 _ => panic!("Expected FunctionCall"),
340 }
341 }
342
343 #[test]
344 fn parse_percent() {
345 let expr = parse("=50%").unwrap();
346 assert!(matches!(expr, Expr::UnaryOp { op: UnaryOp::Percent, .. }));
347 }
348
349 #[test]
350 fn parse_string_literal() {
351 let expr = parse("=\"hello\"").unwrap();
352 assert!(matches!(expr, Expr::Text(ref s, _) if s == "hello"));
353 }
354
355 #[test]
356 fn parse_concat_op() {
357 let expr = parse("=\"a\"&\"b\"").unwrap();
358 assert!(matches!(expr, Expr::BinaryOp { op: BinaryOp::Concat, .. }));
359 }
360
361 #[test]
362 fn validate_incomplete_fails() {
363 let err = validate("=SUM(1,").unwrap_err();
364 assert!(!err.message.is_empty());
365 }
366
367 #[test]
368 fn parse_nested() {
369 assert!(parse("=ROUND(SUM(1,2)*1.1, 1)").is_ok());
370 }
371
372 #[test]
373 fn parse_boolean() {
374 let expr = parse("=TRUE").unwrap();
375 assert!(matches!(expr, Expr::Bool(true, _)));
376 }
377
378 #[test]
379 fn parse_variable() {
380 let expr = parse("=myVar").unwrap();
381 assert!(matches!(expr, Expr::Variable(ref n, _) if n == "myVar"));
382 }
383
384 #[test]
385 fn parse_power_right_assoc() {
386 let expr = parse("=2^3^2").unwrap();
388 match expr {
389 Expr::BinaryOp { op: BinaryOp::Pow, right, .. } => {
390 assert!(matches!(*right, Expr::BinaryOp { op: BinaryOp::Pow, .. }));
391 }
392 _ => panic!("Expected Pow at top"),
393 }
394 }
395}