Skip to main content

cell_sheet_core/formula/
parser.rs

1use crate::formula::ast::*;
2use crate::formula::token::{tokenize, Token};
3use crate::model::CellError;
4
5struct Parser {
6    tokens: Vec<Token>,
7    pos: usize,
8}
9
10impl Parser {
11    fn new(tokens: Vec<Token>) -> Self {
12        Parser { tokens, pos: 0 }
13    }
14
15    fn peek(&self) -> Option<&Token> {
16        self.tokens.get(self.pos)
17    }
18
19    fn advance(&mut self) -> Option<&Token> {
20        let tok = self.tokens.get(self.pos);
21        self.pos += 1;
22        tok
23    }
24
25    fn expect(&mut self, expected: &Token) -> Result<(), CellError> {
26        match self.advance() {
27            Some(tok) if tok == expected => Ok(()),
28            _ => Err(CellError::Parse),
29        }
30    }
31
32    fn expression(&mut self) -> Result<Expr, CellError> {
33        self.comparison()
34    }
35
36    fn comparison(&mut self) -> Result<Expr, CellError> {
37        let mut left = self.addition()?;
38        if let Some(op) = self.peek().and_then(|t| match t {
39            Token::Gt => Some(Op::Gt),
40            Token::Gte => Some(Op::Gte),
41            Token::Lt => Some(Op::Lt),
42            Token::Lte => Some(Op::Lte),
43            Token::Eq => Some(Op::Eq),
44            Token::Neq => Some(Op::Neq),
45            _ => None,
46        }) {
47            self.advance();
48            let right = self.addition()?;
49            left = Expr::BinaryOp {
50                op,
51                left: Box::new(left),
52                right: Box::new(right),
53            };
54        }
55        Ok(left)
56    }
57
58    fn addition(&mut self) -> Result<Expr, CellError> {
59        let mut left = self.multiplication()?;
60        loop {
61            let op = match self.peek() {
62                Some(Token::Plus) => Op::Add,
63                Some(Token::Minus) => Op::Sub,
64                _ => break,
65            };
66            self.advance();
67            let right = self.multiplication()?;
68            left = Expr::BinaryOp {
69                op,
70                left: Box::new(left),
71                right: Box::new(right),
72            };
73        }
74        Ok(left)
75    }
76
77    fn multiplication(&mut self) -> Result<Expr, CellError> {
78        let mut left = self.unary()?;
79        loop {
80            let op = match self.peek() {
81                Some(Token::Star) => Op::Mul,
82                Some(Token::Slash) => Op::Div,
83                _ => break,
84            };
85            self.advance();
86            let right = self.unary()?;
87            left = Expr::BinaryOp {
88                op,
89                left: Box::new(left),
90                right: Box::new(right),
91            };
92        }
93        Ok(left)
94    }
95
96    fn unary(&mut self) -> Result<Expr, CellError> {
97        if let Some(Token::Minus) = self.peek() {
98            self.advance();
99            let expr = self.unary()?;
100            return Ok(Expr::UnaryNeg(Box::new(expr)));
101        }
102        self.primary()
103    }
104
105    fn primary(&mut self) -> Result<Expr, CellError> {
106        let tok = self.advance().ok_or(CellError::Parse)?.clone();
107        match tok {
108            Token::Number(n) => Ok(Expr::Number(n)),
109            Token::StringLit(s) => Ok(Expr::Text(s)),
110            Token::Bool(b) => Ok(Expr::Bool(b)),
111            Token::CellRef {
112                col,
113                row,
114                abs_col,
115                abs_row,
116            } => {
117                let cell_ref =
118                    CellRef::from_display(&col, &row, abs_col, abs_row).ok_or(CellError::Ref)?;
119
120                if let Some(Token::Colon) = self.peek() {
121                    self.advance();
122                    let end_tok = self.advance().ok_or(CellError::Parse)?.clone();
123                    if let Token::CellRef {
124                        col: col2,
125                        row: row2,
126                        abs_col: ac2,
127                        abs_row: ar2,
128                    } = end_tok
129                    {
130                        let end_ref =
131                            CellRef::from_display(&col2, &row2, ac2, ar2).ok_or(CellError::Ref)?;
132                        Ok(Expr::Range {
133                            start: cell_ref,
134                            end: end_ref,
135                        })
136                    } else {
137                        Err(CellError::Parse)
138                    }
139                } else {
140                    Ok(Expr::CellRef(cell_ref))
141                }
142            }
143            Token::Ident(name) => {
144                self.expect(&Token::LParen)?;
145                let mut args = Vec::new();
146                if self.peek() != Some(&Token::RParen) {
147                    args.push(self.expression()?);
148                    while let Some(Token::Comma) = self.peek() {
149                        self.advance();
150                        args.push(self.expression()?);
151                    }
152                }
153                self.expect(&Token::RParen)?;
154                Ok(Expr::FnCall { name, args })
155            }
156            Token::LParen => {
157                let expr = self.expression()?;
158                self.expect(&Token::RParen)?;
159                Ok(expr)
160            }
161            _ => Err(CellError::Parse),
162        }
163    }
164}
165
166pub fn parse(input: &str) -> Result<Expr, CellError> {
167    let tokens = tokenize(input)?;
168    let mut parser = Parser::new(tokens);
169    let expr = parser.expression()?;
170    if parser.pos != parser.tokens.len() {
171        return Err(CellError::Parse);
172    }
173    Ok(expr)
174}
175
176#[cfg(test)]
177mod tests {
178    use super::*;
179
180    #[test]
181    fn parse_number() {
182        let expr = parse("42").unwrap();
183        assert_eq!(expr, Expr::Number(42.0));
184    }
185
186    #[test]
187    fn parse_string() {
188        let expr = parse("\"hello\"").unwrap();
189        assert_eq!(expr, Expr::Text("hello".into()));
190    }
191
192    #[test]
193    fn parse_cell_ref() {
194        let expr = parse("A1").unwrap();
195        assert_eq!(
196            expr,
197            Expr::CellRef(CellRef {
198                col: 0,
199                row: 0,
200                abs_col: false,
201                abs_row: false
202            })
203        );
204    }
205
206    #[test]
207    fn parse_addition() {
208        let expr = parse("1+2").unwrap();
209        assert_eq!(
210            expr,
211            Expr::BinaryOp {
212                op: Op::Add,
213                left: Box::new(Expr::Number(1.0)),
214                right: Box::new(Expr::Number(2.0)),
215            }
216        );
217    }
218
219    #[test]
220    fn parse_precedence_mul_before_add() {
221        let expr = parse("1+2*3").unwrap();
222        assert_eq!(
223            expr,
224            Expr::BinaryOp {
225                op: Op::Add,
226                left: Box::new(Expr::Number(1.0)),
227                right: Box::new(Expr::BinaryOp {
228                    op: Op::Mul,
229                    left: Box::new(Expr::Number(2.0)),
230                    right: Box::new(Expr::Number(3.0)),
231                }),
232            }
233        );
234    }
235
236    #[test]
237    fn parse_parentheses() {
238        let expr = parse("(1+2)*3").unwrap();
239        assert_eq!(
240            expr,
241            Expr::BinaryOp {
242                op: Op::Mul,
243                left: Box::new(Expr::BinaryOp {
244                    op: Op::Add,
245                    left: Box::new(Expr::Number(1.0)),
246                    right: Box::new(Expr::Number(2.0)),
247                }),
248                right: Box::new(Expr::Number(3.0)),
249            }
250        );
251    }
252
253    #[test]
254    fn parse_function_call() {
255        let expr = parse("SUM(A1:A3)").unwrap();
256        assert_eq!(
257            expr,
258            Expr::FnCall {
259                name: "SUM".into(),
260                args: vec![Expr::Range {
261                    start: CellRef {
262                        col: 0,
263                        row: 0,
264                        abs_col: false,
265                        abs_row: false
266                    },
267                    end: CellRef {
268                        col: 0,
269                        row: 2,
270                        abs_col: false,
271                        abs_row: false
272                    },
273                }],
274            }
275        );
276    }
277
278    #[test]
279    fn parse_function_multiple_args() {
280        let expr = parse("IF(A1>0,A1,0)").unwrap();
281        assert_eq!(
282            expr,
283            Expr::FnCall {
284                name: "IF".into(),
285                args: vec![
286                    Expr::BinaryOp {
287                        op: Op::Gt,
288                        left: Box::new(Expr::CellRef(CellRef {
289                            col: 0,
290                            row: 0,
291                            abs_col: false,
292                            abs_row: false
293                        })),
294                        right: Box::new(Expr::Number(0.0)),
295                    },
296                    Expr::CellRef(CellRef {
297                        col: 0,
298                        row: 0,
299                        abs_col: false,
300                        abs_row: false
301                    }),
302                    Expr::Number(0.0),
303                ],
304            }
305        );
306    }
307
308    #[test]
309    fn parse_unary_negation() {
310        let expr = parse("-A1").unwrap();
311        assert_eq!(
312            expr,
313            Expr::UnaryNeg(Box::new(Expr::CellRef(CellRef {
314                col: 0,
315                row: 0,
316                abs_col: false,
317                abs_row: false
318            })))
319        );
320    }
321
322    #[test]
323    fn parse_comparison() {
324        let expr = parse("A1>=10").unwrap();
325        assert_eq!(
326            expr,
327            Expr::BinaryOp {
328                op: Op::Gte,
329                left: Box::new(Expr::CellRef(CellRef {
330                    col: 0,
331                    row: 0,
332                    abs_col: false,
333                    abs_row: false
334                })),
335                right: Box::new(Expr::Number(10.0)),
336            }
337        );
338    }
339
340    #[test]
341    fn parse_range() {
342        let expr = parse("A1:B3").unwrap();
343        assert_eq!(
344            expr,
345            Expr::Range {
346                start: CellRef {
347                    col: 0,
348                    row: 0,
349                    abs_col: false,
350                    abs_row: false
351                },
352                end: CellRef {
353                    col: 1,
354                    row: 2,
355                    abs_col: false,
356                    abs_row: false
357                },
358            }
359        );
360    }
361
362    #[test]
363    fn parse_bool() {
364        assert_eq!(parse("TRUE").unwrap(), Expr::Bool(true));
365        assert_eq!(parse("FALSE").unwrap(), Expr::Bool(false));
366    }
367
368    #[test]
369    fn parse_complex_formula() {
370        let expr = parse("SUM(A1:A3)+1").unwrap();
371        assert_eq!(
372            expr,
373            Expr::BinaryOp {
374                op: Op::Add,
375                left: Box::new(Expr::FnCall {
376                    name: "SUM".into(),
377                    args: vec![Expr::Range {
378                        start: CellRef {
379                            col: 0,
380                            row: 0,
381                            abs_col: false,
382                            abs_row: false
383                        },
384                        end: CellRef {
385                            col: 0,
386                            row: 2,
387                            abs_col: false,
388                            abs_row: false
389                        },
390                    }],
391                }),
392                right: Box::new(Expr::Number(1.0)),
393            }
394        );
395    }
396}