terbium_grammar/
ast.rs

1use super::token::{Literal, Operator, StringLiteral, Token};
2use crate::token::{get_lexer, Bracket};
3use crate::Error;
4
5use chumsky::prelude::*;
6use chumsky::primitive::FilterMap;
7
8#[derive(Clone, Debug, PartialEq)]
9pub enum Expr {
10    Integer(u128),
11    Float(String), // See token.rs for why this is a String
12    String(String),
13    Bool(bool),
14    Ident(String),
15    Array(Vec<Expr>),
16    UnaryExpr {
17        operator: Operator,
18        value: Box<Expr>,
19    },
20    BinaryExpr {
21        operator: Operator,
22        lhs: Box<Expr>,
23        rhs: Box<Expr>,
24    },
25    Attr(Box<Expr>, String),
26    Call {
27        value: Box<Expr>,
28        args: Vec<Expr>,
29        kwargs: Vec<(String, Expr)>,
30    },
31}
32
33impl Expr {
34    pub fn from_tokens(tokens: Vec<Token>) -> (Self, Vec<Error>) {
35        let (expr, errors) = get_expr_parser().parse_recovery(tokens);
36
37        (expr.unwrap(), errors)
38    }
39
40    pub fn from_string(s: String) -> (Self, Vec<Error>) {
41        Self::from_tokens(get_lexer().parse(s.as_str()).unwrap())
42    }
43}
44
45#[derive(Clone, Debug, PartialEq)]
46pub enum Target {
47    // Could represent a variable or a parameter. Supports destructuring.
48    Ident(String),
49    Array(Vec<Target>),
50    Attr(Box<Target>, String), // Invalid as a parameter or when let/immut is used.
51}
52
53#[derive(Clone, Debug, PartialEq)]
54pub enum Node {
55    Module(Vec<Node>),
56    Func {
57        name: String,
58        params: Vec<Node>,
59        body: Vec<Node>,
60    },
61    Expr(Expr),
62    // e.g. x.y = z becomes Assign { target: Attr(Ident("x"), "y"), value: Ident("z"), .. }
63    Assign {
64        target: Target,
65        value: Expr,
66        r#let: bool,
67        immut: bool,
68    },
69    Return(Option<Expr>),
70}
71
72pub trait CommonParser<T> = Parser<Token, T, Error = Error> + Clone;
73
74pub fn get_expr_parser() -> impl CommonParser<Expr> {
75    recursive(|e: Recursive<Token, Expr, Error>| {
76        let literal: FilterMap<_, Error> = select! {
77            Token::Literal(lit) => match lit {
78                Literal::Integer(i) => Expr::Integer(i),
79                Literal::Float(f) => Expr::Float(f),
80                Literal::String(s) => match s {
81                    StringLiteral::String(s) => Expr::String(s),
82                    _ => unreachable!(),
83                },
84            }
85        };
86
87        let ident = select! {
88            Token::Identifier(s) => match s.as_str() {
89                "true" => Expr::Bool(true),
90                "false" => Expr::Bool(false),
91                _ => Expr::Ident(s),
92            }
93        };
94
95        let array = e
96            .clone()
97            .separated_by(just::<_, Token, _>(Token::Comma))
98            .allow_trailing()
99            .delimited_by(
100                just(Token::StartBracket(Bracket::Bracket)),
101                just(Token::EndBracket(Bracket::Bracket)),
102            )
103            .map(Expr::Array);
104
105        let atom = choice((
106            literal,
107            ident,
108            e.clone()
109                .delimited_by(
110                    just(Token::StartBracket(Bracket::Paren)),
111                    just(Token::EndBracket(Bracket::Paren)),
112                )
113                .boxed(),
114            array,
115        ))
116        .boxed();
117
118        let attr = atom
119            .clone()
120            .then(
121                just::<_, Token, _>(Token::Dot)
122                    .ignore_then(ident)
123                    .repeated(),
124            )
125            .foldl(|a, b| {
126                Expr::Attr(
127                    Box::new(a),
128                    match b {
129                        Expr::Ident(s) => s,
130                        Expr::Bool(b) => b.to_string(),
131                        _ => unreachable!(),
132                    },
133                )
134            })
135            .boxed();
136
137        let call = attr
138            .clone()
139            .then(
140                e.clone()
141                    .separated_by(just::<_, Token, _>(Token::Comma))
142                    .allow_trailing()
143                    .delimited_by(
144                        just(Token::StartBracket(Bracket::Paren)),
145                        just(Token::EndBracket(Bracket::Paren)),
146                    )
147                    .or_not(),
148            )
149            .map(|(expr, args)| match args {
150                Some(args) => Expr::Call {
151                    value: Box::new(expr),
152                    args,
153                    kwargs: vec![],
154                },
155                None => expr,
156            })
157            .boxed();
158
159        let unary = just(Token::Operator(Operator::Sub))
160            .or(just(Token::Operator(Operator::Add)))
161            .or(just(Token::Operator(Operator::Not)))
162            .or(just(Token::Operator(Operator::BitNot)))
163            .repeated()
164            .then(call.clone())
165            .foldr(|operator, expr| match operator {
166                Token::Operator(operator) => Expr::UnaryExpr {
167                    operator,
168                    value: Box::new(expr),
169                },
170                _ => unreachable!(),
171            })
172            .boxed();
173
174        let binary_pow = unary
175            .clone()
176            .then(
177                just(Token::Operator(Operator::Pow))
178                    .map(|o| match o {
179                        Token::Operator(op) => op,
180                        _ => unreachable!(),
181                    })
182                    .then(unary)
183                    .repeated(),
184            )
185            .foldl(|lhs, (operator, rhs)| Expr::BinaryExpr {
186                operator,
187                lhs: Box::new(lhs),
188                rhs: Box::new(rhs),
189            })
190            .boxed();
191
192        let op = just(Token::Operator(Operator::Mul))
193            .or(just(Token::Operator(Operator::Div)))
194            .or(just(Token::Operator(Operator::Mod)))
195            .map(|o| match o {
196                Token::Operator(op) => op,
197                _ => unreachable!(),
198            });
199        let binary_product = binary_pow
200            .clone()
201            .then(op.then(binary_pow).repeated())
202            .foldl(|lhs, (operator, rhs)| Expr::BinaryExpr {
203                operator,
204                lhs: Box::new(lhs),
205                rhs: Box::new(rhs),
206            })
207            .boxed();
208
209        let op = just(Token::Operator(Operator::Add))
210            .or(just(Token::Operator(Operator::Sub)))
211            .map(|o| match o {
212                Token::Operator(op) => op,
213                _ => unreachable!(),
214            });
215        let binary_sum = binary_product
216            .clone()
217            .then(op.then(binary_product).repeated())
218            .foldl(|lhs, (operator, rhs)| Expr::BinaryExpr {
219                operator,
220                lhs: Box::new(lhs),
221                rhs: Box::new(rhs),
222            })
223            .boxed();
224
225        let op = just(Token::Operator(Operator::Eq))
226            .or(just(Token::Operator(Operator::Ne)))
227            .or(just(Token::Operator(Operator::Lt)))
228            .or(just(Token::Operator(Operator::Gt)))
229            .or(just(Token::Operator(Operator::Le)))
230            .or(just(Token::Operator(Operator::Ge)))
231            .map(|o| match o {
232                Token::Operator(op) => op,
233                _ => unreachable!(),
234            });
235        let binary_cmp = binary_sum
236            .clone()
237            .then(op.then(binary_sum).repeated())
238            .foldl(|lhs, (operator, rhs)| Expr::BinaryExpr {
239                operator,
240                lhs: Box::new(lhs),
241                rhs: Box::new(rhs),
242            })
243            .boxed();
244
245        let binary_logical_and = binary_cmp
246            .clone()
247            .then(
248                just(Token::Operator(Operator::And))
249                    .map(|o| match o {
250                        Token::Operator(op) => op,
251                        _ => unreachable!(),
252                    })
253                    .then(binary_cmp)
254                    .repeated(),
255            )
256            .foldl(|lhs, (operator, rhs)| Expr::BinaryExpr {
257                operator,
258                lhs: Box::new(lhs),
259                rhs: Box::new(rhs),
260            })
261            .boxed();
262
263        let binary_logical_or = binary_logical_and
264            .clone()
265            .then(
266                just(Token::Operator(Operator::Or))
267                    .map(|o| match o {
268                        Token::Operator(op) => op,
269                        _ => unreachable!(),
270                    })
271                    .then(binary_logical_and)
272                    .repeated(),
273            )
274            .foldl(|lhs, (operator, rhs)| Expr::BinaryExpr {
275                operator,
276                lhs: Box::new(lhs),
277                rhs: Box::new(rhs),
278            })
279            .boxed();
280
281        let op = just(Token::Operator(Operator::BitAnd))
282            .or(just(Token::Operator(Operator::BitOr)))
283            .or(just(Token::Operator(Operator::BitXor)))
284            .map(|o| match o {
285                Token::Operator(op) => op,
286                _ => unreachable!(),
287            });
288        binary_logical_or
289            .clone()
290            .then(op.then(binary_logical_or).repeated())
291            .foldl(|lhs, (operator, rhs)| Expr::BinaryExpr {
292                operator,
293                lhs: Box::new(lhs),
294                rhs: Box::new(rhs),
295            })
296            .boxed()
297    })
298}
299
300#[cfg(test)]
301mod tests {
302    use super::*;
303    use crate::ast::Expr::*;
304
305    #[test]
306    fn test_expr_parser() {
307        let code = "-1 + 2 * (5 - [2, a.b() - (c + -d), e(5, f())])";
308        let (tree, errors) = Expr::from_string(code.to_string());
309
310        assert_eq!(
311            tree,
312            BinaryExpr {
313                operator: Operator::Add,
314                lhs: Box::new(UnaryExpr {
315                    operator: Operator::Sub,
316                    value: Box::new(Integer(1)),
317                }),
318                rhs: Box::new(BinaryExpr {
319                    operator: Operator::Mul,
320                    lhs: Box::new(Integer(2)),
321                    rhs: Box::new(BinaryExpr {
322                        operator: Operator::Sub,
323                        lhs: Box::new(Integer(5)),
324                        rhs: Box::new(Array(vec![
325                            Integer(2),
326                            BinaryExpr {
327                                operator: Operator::Sub,
328                                lhs: Box::new(Call {
329                                    value: Box::new(Attr(
330                                        Box::new(Ident("a".to_string())),
331                                        "b".to_string()
332                                    )),
333                                    args: vec![],
334                                    kwargs: vec![],
335                                }),
336                                rhs: Box::new(BinaryExpr {
337                                    operator: Operator::Add,
338                                    lhs: Box::new(Ident("c".to_string())),
339                                    rhs: Box::new(UnaryExpr {
340                                        operator: Operator::Sub,
341                                        value: Box::new(Ident("d".to_string())),
342                                    }),
343                                }),
344                            },
345                            Call {
346                                value: Box::new(Ident("e".to_string())),
347                                args: vec![
348                                    Integer(5),
349                                    Call {
350                                        value: Box::new(Ident("f".to_string())),
351                                        args: vec![],
352                                        kwargs: vec![],
353                                    },
354                                ],
355                                kwargs: vec![],
356                            },
357                        ])),
358                    }),
359                }),
360            }
361        );
362        assert_eq!(errors.len(), 0);
363    }
364}