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, elems) = self.parse_array_elements(inner)?;
43 let rest = multispace0(rest)?.0;
44 if let Some(after) = rest.strip_prefix('}') {
45 return Ok((after, Expr::Array(elems, self.span(i, after))));
46 }
47 return Err(nom::Err::Error(nom::error::Error::new(
48 rest,
49 nom::error::ErrorKind::Char,
50 )));
51 }
52
53 if let Some(inner) = i.strip_prefix('(') {
55 let (rest, expr) = self.parse_comparison(inner)?;
56 let rest = multispace0(rest)?.0;
57 if let Some(after) = rest.strip_prefix(')') {
58 return Ok((after, expr));
59 }
60 return Err(nom::Err::Error(nom::error::Error::new(
61 rest,
62 nom::error::ErrorKind::Char,
63 )));
64 }
65
66 if let Ok((rest, b)) = bool_literal(i) {
68 return Ok((rest, Expr::Bool(b, self.span(i, rest))));
69 }
70
71 if let Ok((rest, name)) = identifier(i) {
73 let rest_ws = multispace0(rest)?.0;
74 if let Some(args_input) = rest_ws.strip_prefix('(') {
75 let (rest2, args) = self.parse_arg_list(args_input)?;
77 let rest2 = multispace0(rest2)?.0;
78 if let Some(after) = rest2.strip_prefix(')') {
79 return Ok((after, Expr::FunctionCall {
80 name: name.to_uppercase(),
81 args,
82 span: self.span(i, after),
83 }));
84 }
85 return Err(nom::Err::Error(nom::error::Error::new(
86 rest2,
87 nom::error::ErrorKind::Char,
88 )));
89 }
90 return Ok((rest, Expr::Variable(name.to_string(), self.span(i, rest))));
91 }
92
93 Err(nom::Err::Error(nom::error::Error::new(i, nom::error::ErrorKind::Alt)))
94 }
95
96 fn parse_arg_list(&self, i: &'a str) -> IResult<&'a str, Vec<Expr>> {
97 let mut args = Vec::new();
98 let mut rest = multispace0(i)?.0;
99
100 if rest.starts_with(')') {
101 return Ok((rest, args));
102 }
103
104 let (r, first) = self.parse_comparison(rest)?;
105 args.push(first);
106 rest = r;
107
108 loop {
109 rest = multispace0(rest)?.0;
110 if let Some(after_comma) = rest.strip_prefix(',') {
111 let (r, arg) = self.parse_comparison(after_comma)?;
112 args.push(arg);
113 rest = r;
114 } else {
115 break;
116 }
117 }
118
119 Ok((rest, args))
120 }
121
122 fn parse_array_elements(&self, i: &'a str) -> IResult<&'a str, Vec<Expr>> {
123 let mut elems = Vec::new();
124 let mut rest = multispace0(i)?.0;
125 if rest.starts_with('}') {
126 return Ok((rest, elems)); }
128 let (r, first) = self.parse_comparison(rest)?;
129 elems.push(first);
130 rest = r;
131 loop {
132 rest = multispace0(rest)?.0;
133 if let Some(after_comma) = rest.strip_prefix(',') {
134 let (r, elem) = self.parse_comparison(after_comma)?;
135 elems.push(elem);
136 rest = r;
137 } else {
138 break;
139 }
140 }
141 Ok((rest, elems))
142 }
143
144 fn parse_postfix(&self, i: &'a str) -> IResult<&'a str, Expr> {
147 let (rest, expr) = self.parse_primary(i)?;
148 let rest_ws = multispace0(rest)?.0;
149 if let Some(after) = rest_ws.strip_prefix('%') {
150 return Ok((after, Expr::UnaryOp {
151 op: UnaryOp::Percent,
152 operand: Box::new(expr),
153 span: self.span(i, after),
154 }));
155 }
156 Ok((rest, expr))
157 }
158
159 fn parse_unary(&self, i: &'a str) -> IResult<&'a str, Expr> {
162 let i_ws = multispace0(i)?.0;
163 if let Some(after_minus) = i_ws.strip_prefix('-') {
164 let (rest, operand) = self.parse_unary(after_minus)?;
165 return Ok((rest, Expr::UnaryOp {
166 op: UnaryOp::Neg,
167 operand: Box::new(operand),
168 span: self.span(i_ws, rest),
169 }));
170 }
171 self.parse_postfix(i)
172 }
173
174 fn parse_power(&self, i: &'a str) -> IResult<&'a str, Expr> {
177 let (rest, left) = self.parse_unary(i)?;
178 let rest_ws = multispace0(rest)?.0;
179 if let Some(after_op) = rest_ws.strip_prefix('^') {
180 let (rest2, right) = self.parse_power(after_op)?;
181 return Ok((rest2, Expr::BinaryOp {
182 op: BinaryOp::Pow,
183 left: Box::new(left),
184 right: Box::new(right),
185 span: self.span(i, rest2),
186 }));
187 }
188 Ok((rest, left))
189 }
190
191 fn parse_multiplicative(&self, i: &'a str) -> IResult<&'a str, Expr> {
194 let (mut rest, mut left) = self.parse_power(i)?;
195 loop {
196 let ws = multispace0(rest)?.0;
197 let op = ws.strip_prefix('*').map(|after| (BinaryOp::Mul, after))
198 .or_else(|| ws.strip_prefix('/').map(|after| (BinaryOp::Div, after)));
199 match op {
200 None => break,
201 Some((op, after)) => {
202 let (r, right) = self.parse_power(after)?;
203 left = Expr::BinaryOp {
204 op,
205 span: self.span(i, r),
206 left: Box::new(left),
207 right: Box::new(right),
208 };
209 rest = r;
210 }
211 }
212 }
213 Ok((rest, left))
214 }
215
216 fn parse_additive(&self, i: &'a str) -> IResult<&'a str, Expr> {
219 let (mut rest, mut left) = self.parse_multiplicative(i)?;
220 loop {
221 let ws = multispace0(rest)?.0;
222 let op = ws.strip_prefix('+').map(|after| (BinaryOp::Add, after))
223 .or_else(|| ws.strip_prefix('-').map(|after| (BinaryOp::Sub, after)));
224 match op {
225 None => break,
226 Some((op, after)) => {
227 let (r, right) = self.parse_multiplicative(after)?;
228 left = Expr::BinaryOp {
229 op,
230 span: self.span(i, r),
231 left: Box::new(left),
232 right: Box::new(right),
233 };
234 rest = r;
235 }
236 }
237 }
238 Ok((rest, left))
239 }
240
241 fn parse_concat(&self, i: &'a str) -> IResult<&'a str, Expr> {
244 let (mut rest, mut left) = self.parse_additive(i)?;
245 loop {
246 let ws = multispace0(rest)?.0;
247 if let Some(after) = ws.strip_prefix('&') {
248 let (r, right) = self.parse_additive(after)?;
249 left = Expr::BinaryOp {
250 op: BinaryOp::Concat,
251 span: self.span(i, r),
252 left: Box::new(left),
253 right: Box::new(right),
254 };
255 rest = r;
256 } else {
257 break;
258 }
259 }
260 Ok((rest, left))
261 }
262
263 fn parse_comparison(&self, i: &'a str) -> IResult<&'a str, Expr> {
266 let (rest, left) = self.parse_concat(i)?;
267 let ws = multispace0(rest)?.0;
268
269 let op_result: Option<(BinaryOp, &'a str)> = if let Some(after) = ws.strip_prefix("<>") {
271 Some((BinaryOp::Ne, after))
272 } else if let Some(after) = ws.strip_prefix("<=") {
273 Some((BinaryOp::Le, after))
274 } else if let Some(after) = ws.strip_prefix(">=") {
275 Some((BinaryOp::Ge, after))
276 } else if let Some(after) = ws.strip_prefix('<') {
277 Some((BinaryOp::Lt, after))
278 } else if let Some(after) = ws.strip_prefix('>') {
279 Some((BinaryOp::Gt, after))
280 } else if let Some(after) = ws.strip_prefix('=') {
281 Some((BinaryOp::Eq, after))
282 } else {
283 None
284 };
285
286 if let Some((op, after)) = op_result {
287 let (r, right) = self.parse_concat(after)?;
288 return Ok((r, Expr::BinaryOp {
289 op,
290 span: self.span(i, r),
291 left: Box::new(left),
292 right: Box::new(right),
293 }));
294 }
295
296 Ok((rest, left))
297 }
298}
299
300pub fn parse(formula: &str) -> Result<Expr, ParseError> {
307 let input = formula.strip_prefix('=').unwrap_or(formula).trim();
308 let p = Parser::new(formula);
309 match p.parse_comparison(input) {
310 Ok((rest, expr)) => {
311 let rest = rest.trim();
312 if rest.is_empty() {
313 Ok(expr)
314 } else {
315 Err(ParseError {
316 message: format!("Unexpected input '{}'", rest),
317 position: offset(formula, rest),
318 })
319 }
320 }
321 Err(nom::Err::Error(e)) | Err(nom::Err::Failure(e)) => Err(ParseError {
322 message: "Parse error".into(),
323 position: offset(formula, e.input),
324 }),
325 Err(nom::Err::Incomplete(_)) => Err(ParseError {
326 message: "Incomplete input".into(),
327 position: formula.len(),
328 }),
329 }
330}
331
332pub fn validate(formula: &str) -> Result<(), ParseError> {
334 parse(formula).map(|_| ())
335}
336
337#[cfg(test)]
338mod tests {
339 use super::*;
340 use crate::parser::ast::{BinaryOp, Expr, UnaryOp};
341
342 #[test]
343 fn parse_number_literal() {
344 let expr = parse("=42").unwrap();
345 assert!(matches!(expr, Expr::Number(n, _) if n == 42.0));
346 }
347
348 #[test]
349 fn parse_binary_add() {
350 let expr = parse("=1+2").unwrap();
351 assert!(matches!(expr, Expr::BinaryOp { op: BinaryOp::Add, .. }));
352 }
353
354 #[test]
355 fn parse_precedence() {
356 let expr = parse("=2+3*4").unwrap();
358 match expr {
359 Expr::BinaryOp { op: BinaryOp::Add, right, .. } => {
360 assert!(matches!(*right, Expr::BinaryOp { op: BinaryOp::Mul, .. }));
361 }
362 _ => panic!("Expected Add at top"),
363 }
364 }
365
366 #[test]
367 fn parse_function_call() {
368 let expr = parse("=SUM(1,2,3)").unwrap();
369 match expr {
370 Expr::FunctionCall { name, args, .. } => {
371 assert_eq!(name, "SUM");
372 assert_eq!(args.len(), 3);
373 }
374 _ => panic!("Expected FunctionCall"),
375 }
376 }
377
378 #[test]
379 fn parse_percent() {
380 let expr = parse("=50%").unwrap();
381 assert!(matches!(expr, Expr::UnaryOp { op: UnaryOp::Percent, .. }));
382 }
383
384 #[test]
385 fn parse_string_literal() {
386 let expr = parse("=\"hello\"").unwrap();
387 assert!(matches!(expr, Expr::Text(ref s, _) if s == "hello"));
388 }
389
390 #[test]
391 fn parse_concat_op() {
392 let expr = parse("=\"a\"&\"b\"").unwrap();
393 assert!(matches!(expr, Expr::BinaryOp { op: BinaryOp::Concat, .. }));
394 }
395
396 #[test]
397 fn validate_incomplete_fails() {
398 let err = validate("=SUM(1,").unwrap_err();
399 assert!(!err.message.is_empty());
400 }
401
402 #[test]
403 fn parse_nested() {
404 assert!(parse("=ROUND(SUM(1,2)*1.1, 1)").is_ok());
405 }
406
407 #[test]
408 fn parse_boolean() {
409 let expr = parse("=TRUE").unwrap();
410 assert!(matches!(expr, Expr::Bool(true, _)));
411 }
412
413 #[test]
414 fn parse_variable() {
415 let expr = parse("=myVar").unwrap();
416 assert!(matches!(expr, Expr::Variable(ref n, _) if n == "myVar"));
417 }
418
419 #[test]
420 fn parse_array_literal_numbers() {
421 let expr = parse("={1,2,3}").unwrap();
422 match expr {
423 Expr::Array(elems, _) => assert_eq!(elems.len(), 3),
424 _ => panic!("Expected Array"),
425 }
426 }
427
428 #[test]
429 fn parse_array_literal_mixed() {
430 let expr = parse("={1,\"hello\",TRUE}").unwrap();
431 assert!(matches!(expr, Expr::Array(_, _)));
432 }
433
434 #[test]
435 fn parse_array_literal_empty() {
436 let expr = parse("={}").unwrap();
437 assert!(matches!(expr, Expr::Array(ref e, _) if e.is_empty()));
438 }
439
440 #[test]
441 fn parse_array_in_function_call() {
442 let expr = parse("=SUM({1,2,3})").unwrap();
443 match expr {
444 Expr::FunctionCall { name, args, .. } => {
445 assert_eq!(name, "SUM");
446 assert_eq!(args.len(), 1);
447 assert!(matches!(args[0], Expr::Array(_, _)));
448 }
449 _ => panic!("Expected FunctionCall"),
450 }
451 }
452
453 #[test]
454 fn parse_power_right_assoc() {
455 let expr = parse("=2^3^2").unwrap();
457 match expr {
458 Expr::BinaryOp { op: BinaryOp::Pow, right, .. } => {
459 assert!(matches!(*right, Expr::BinaryOp { op: BinaryOp::Pow, .. }));
460 }
461 _ => panic!("Expected Pow at top"),
462 }
463 }
464}