Skip to main content

silkworm_syn/parse/
expr.rs

1use crate::ast;
2use crate::ptr::P;
3use crate::token::{BinOp, Delim, Keyword, Kind as T, Token};
4use crate::Span;
5
6use super::{PResult, Parser};
7
8impl<'a, I> Parser<'a, I>
9where
10    I: Iterator<Item = Token>,
11{
12    pub fn parse_expr(&mut self) -> PResult<'a, ast::Expr> {
13        self.parse_expr_with_precedence(0)
14    }
15
16    pub fn parse_expr_with_precedence(&mut self, min_precedence: u32) -> PResult<'a, ast::Expr> {
17        let mut left = self.parse_unary_expr_or_higher()?;
18
19        while let Some(bin_op_kind) = self.peek_bin_op()? {
20            let precedence = binary_precedence(bin_op_kind);
21            if precedence <= min_precedence {
22                break;
23            }
24
25            let bin_op = ast::BinOp {
26                kind: bin_op_kind,
27                span: self.bump().span,
28            };
29
30            let right = self.parse_expr_with_precedence(precedence)?;
31
32            left = ast::Expr {
33                span: left.span.union(right.span),
34                kind: ast::ExprKind::Binary(bin_op, P(left), P(right)),
35            };
36        }
37
38        Ok(left)
39    }
40
41    fn peek_bin_op(&mut self) -> PResult<'a, Option<ast::BinOpKind>> {
42        use ast::BinOpKind as O;
43
44        let bin_op = match self.token.kind {
45            T::EqEq | T::Keyword(Keyword::Is) | T::Keyword(Keyword::Eq) => Some(O::Eq),
46            T::Neq | T::Keyword(Keyword::Neq) => Some(O::Neq),
47            T::Lt | T::Keyword(Keyword::Lt) => Some(O::Lt),
48            T::Lte | T::Keyword(Keyword::Lte) => Some(O::Lte),
49            T::Gt | T::Keyword(Keyword::Gt) => Some(O::Gt),
50            T::Gte | T::Keyword(Keyword::Gte) => Some(O::Gte),
51
52            T::AndAnd | T::Keyword(Keyword::And) => Some(O::And),
53            T::OrOr | T::Keyword(Keyword::Or) => Some(O::Or),
54            T::Xor | T::Keyword(Keyword::Xor) => Some(O::Xor),
55
56            T::BinOp(BinOp::Plus) => Some(O::Add),
57            T::BinOp(BinOp::Minus) => Some(O::Sub),
58            T::BinOp(BinOp::Star) => Some(O::Mul),
59            T::BinOp(BinOp::Slash) => Some(O::Div),
60            T::BinOp(BinOp::Percent) => Some(O::Mod),
61
62            T::Eq | T::Keyword(Keyword::To) => Some(O::Assign),
63            T::BinOpEq(BinOp::Plus) => Some(O::AddAssign),
64            T::BinOpEq(BinOp::Minus) => Some(O::SubAssign),
65            T::BinOpEq(BinOp::Star) => Some(O::MulAssign),
66            T::BinOpEq(BinOp::Slash) => Some(O::DivAssign),
67            T::BinOpEq(BinOp::Percent) => Some(O::ModAssign),
68
69            _ => None,
70        };
71
72        Ok(bin_op)
73    }
74
75    pub fn parse_unary_expr_or_higher(&mut self) -> PResult<'a, ast::Expr> {
76        let (kind, span) = match self.token.kind {
77            T::BinOp(BinOp::Minus) => (ast::UnOpKind::Neg, self.bump().span),
78            T::Not | T::Keyword(Keyword::Not) => (ast::UnOpKind::Not, self.bump().span),
79            _ => {
80                return {
81                    if let Ok(expr) = self.parse_call_expr_or_atom() {
82                        Ok(expr)
83                    } else {
84                        Err(self.expect_one_of(&[T::Not, T::BinOp(BinOp::Minus)]))
85                    }
86                }
87            }
88        };
89
90        let op = ast::UnOp { kind, span };
91        let operand = self.parse_call_expr_or_atom()?;
92        let span = op.span.union(operand.span);
93
94        Ok(ast::Expr {
95            kind: ast::ExprKind::Unary(op, P(operand)),
96            span,
97        })
98    }
99
100    pub fn parse_call_expr_or_atom(&mut self) -> PResult<'a, ast::Expr> {
101        let receiver = self.parse_atom()?;
102
103        let (args, span) = match self.parse_call_arg_list() {
104            Some(tup) => tup,
105            None => return Ok(receiver),
106        };
107
108        Ok(ast::Expr {
109            span: receiver.span.union(span),
110            kind: ast::ExprKind::Call(P(receiver), args),
111        })
112    }
113
114    pub fn parse_call_arg_list(&mut self) -> Option<(Vec<ast::Expr>, Span)> {
115        self.eat(T::OpenDelim(Delim::Paren))?;
116
117        Some(self.parse_list_with(
118            true,
119            super::list::parse_list_sep_with_term(true, T::Comma, T::CloseDelim(Delim::Paren)),
120            |p, span| {
121                p.expect_one_of(&[T::Comma, T::CloseDelim(Delim::Paren)])
122                    .span(span);
123            },
124            |p| p.parse_expr().ok(),
125        ))
126    }
127
128    pub fn parse_atom(&mut self) -> PResult<'a, ast::Expr> {
129        match self.token.kind {
130            T::OpenDelim(Delim::Paren) => {
131                let span = self.bump().span;
132                let mut expr = self.parse_expr()?;
133                if let Some(paren) = self.eat(T::CloseDelim(Delim::Paren)) {
134                    expr.span = span.union(paren.span);
135                    Ok(expr)
136                } else {
137                    Err(self.expect_one_of(&[]))
138                }
139            }
140            T::Number
141            | T::Keyword(Keyword::True)
142            | T::Keyword(Keyword::False)
143            | T::Keyword(Keyword::Null)
144            | T::OpenDelim(Delim::DoubleQuote)
145            | T::OpenDelim(Delim::Backtick) => {
146                let lit = self.parse_lit()?;
147                Ok(ast::Expr {
148                    span: lit.span,
149                    kind: ast::ExprKind::Lit(lit),
150                })
151            }
152            T::Dollar | T::At | T::AtAt | T::Ident | T::Keyword(_) => {
153                let var = self.parse_var()?;
154                Ok(ast::Expr {
155                    span: var.span,
156                    kind: ast::ExprKind::Var(var),
157                })
158            }
159            _ => Err(self.expect_one_of(&[
160                T::OpenDelim(Delim::Paren),
161                T::Number,
162                T::Keyword(Keyword::True),
163                T::Keyword(Keyword::False),
164                T::Keyword(Keyword::Null),
165                T::OpenDelim(Delim::DoubleQuote),
166                T::OpenDelim(Delim::Backtick),
167                T::Dollar,
168                T::At,
169                T::AtAt,
170                T::Ident,
171            ])),
172        }
173    }
174
175    pub fn parse_var(&mut self) -> PResult<'a, ast::Var> {
176        let span = self.token.span;
177
178        let sigil = if self.eat(T::Dollar).is_some() {
179            ast::Sigil::Global
180        } else if self.eat(T::At).is_some() {
181            ast::Sigil::Node
182        } else if self.eat(T::AtAt).is_some() {
183            ast::Sigil::File
184        } else {
185            ast::Sigil::Local
186        };
187
188        let (symbol, keyword, ident_span) =
189            self.eat_symbol().ok_or_else(|| self.expect(T::Ident))?;
190
191        Ok(ast::Var {
192            sigil,
193            symbol,
194            keyword,
195            span: span.union(ident_span),
196        })
197    }
198
199    pub fn parse_lit(&mut self) -> PResult<'a, ast::Lit> {
200        let (kind, span) = match self.token.kind {
201            T::Number => (ast::LitKind::Number, self.bump().span),
202            T::Keyword(Keyword::True) => (ast::LitKind::True, self.bump().span),
203            T::Keyword(Keyword::False) => (ast::LitKind::False, self.bump().span),
204            T::Keyword(Keyword::Null) => (ast::LitKind::Null, self.bump().span),
205            T::OpenDelim(Delim::DoubleQuote) => {
206                self.bump();
207                let body =
208                    self.parse_str_body_with_terminator(T::CloseDelim(Delim::DoubleQuote))?;
209                let span = body.span;
210                (ast::LitKind::Str(body), span)
211            }
212            T::OpenDelim(Delim::Backtick) => {
213                self.bump();
214                let body = self.parse_str_body_with_terminator(T::CloseDelim(Delim::Backtick))?;
215                let span = body.span;
216                (ast::LitKind::Str(body), span)
217            }
218            _ => {
219                return Err(self.expect_one_of(&[
220                    T::Number,
221                    T::Keyword(Keyword::True),
222                    T::Keyword(Keyword::False),
223                    T::Keyword(Keyword::Null),
224                    T::OpenDelim(Delim::DoubleQuote),
225                    T::OpenDelim(Delim::Backtick),
226                ]))
227            }
228        };
229
230        Ok(ast::Lit { kind, span })
231    }
232}
233
234fn binary_precedence(op: ast::BinOpKind) -> u32 {
235    use ast::BinOpKind as O;
236    match op {
237        O::Mul => 8,
238        O::Div => 8,
239        O::Mod => 8,
240        O::Add => 7,
241        O::Sub => 7,
242        O::Lt => 6,
243        O::Lte => 6,
244        O::Gt => 6,
245        O::Gte => 6,
246        O::Eq => 5,
247        O::Neq => 5,
248        O::MulAssign => 4,
249        O::DivAssign => 4,
250        O::ModAssign => 4,
251        O::AddAssign => 3,
252        O::SubAssign => 3,
253        O::And => 2,
254        O::Or => 2,
255        O::Xor => 2,
256        O::Assign => 1,
257    }
258}
259
260#[cfg(test)]
261mod tests {
262    use super::*;
263
264    use crate::parse::test_utils::assert_parse;
265    use crate::parse::Parse;
266    use crate::Span;
267
268    #[test]
269    fn can_parse_lit() {
270        assert_parse("123", |_itn| ast::Lit {
271            kind: ast::LitKind::Number,
272            span: Span::new(0, 3),
273        });
274
275        assert_parse("true", |_itn| ast::Lit {
276            kind: ast::LitKind::True,
277            span: Span::new(0, 4),
278        });
279
280        assert_parse("false", |_itn| ast::Lit {
281            kind: ast::LitKind::False,
282            span: Span::new(0, 5),
283        });
284
285        assert_parse("null", |_itn| ast::Lit {
286            kind: ast::LitKind::Null,
287            span: Span::new(0, 4),
288        });
289    }
290
291    #[test]
292    fn can_parse_var() {
293        assert_parse("$foo", |itn| ast::Var {
294            sigil: ast::Sigil::Global,
295            symbol: itn.intern("foo"),
296            keyword: None,
297            span: Span::new(0, 4),
298        });
299
300        assert_parse("if", |itn| ast::Var {
301            sigil: ast::Sigil::Local,
302            symbol: itn.intern("if"),
303            keyword: Some(Keyword::If),
304            span: Span::new(0, 2),
305        });
306
307        assert_parse("@@bar", |itn| ast::Var {
308            sigil: ast::Sigil::File,
309            symbol: itn.intern("bar"),
310            keyword: None,
311            span: Span::new(0, 5),
312        });
313
314        assert_parse("@baz", |itn| ast::Var {
315            sigil: ast::Sigil::Node,
316            symbol: itn.intern("baz"),
317            keyword: None,
318            span: Span::new(0, 4),
319        });
320    }
321
322    #[test]
323    fn can_parse_expr() {
324        use ast::BinOpKind as O;
325
326        assert_parse("1 + 2 * 3", |itn| ast::Expr {
327            kind: ast::ExprKind::Binary(
328                ast::BinOp {
329                    kind: O::Add,
330                    span: Span::new(2, 1),
331                },
332                P(ast::Expr {
333                    kind: ast::ExprKind::Lit(ast::Lit::parse_with_interner("1", 0, itn).unwrap()),
334                    span: Span::new(0, 1),
335                }),
336                P(ast::Expr {
337                    kind: ast::ExprKind::Binary(
338                        ast::BinOp {
339                            kind: O::Mul,
340                            span: Span::new(6, 1),
341                        },
342                        P(ast::Expr {
343                            kind: ast::ExprKind::Lit(
344                                ast::Lit::parse_with_interner("2", 4, itn).unwrap(),
345                            ),
346                            span: Span::new(4, 1),
347                        }),
348                        P(ast::Expr {
349                            kind: ast::ExprKind::Lit(
350                                ast::Lit::parse_with_interner("3", 8, itn).unwrap(),
351                            ),
352                            span: Span::new(8, 1),
353                        }),
354                    ),
355                    span: Span::new(4, 5),
356                }),
357            ),
358            span: Span::new(0, 9),
359        });
360
361        assert_parse("(1 + 2) * 3", |itn| ast::Expr {
362            kind: ast::ExprKind::Binary(
363                ast::BinOp {
364                    kind: O::Mul,
365                    span: Span::new(8, 1),
366                },
367                P(ast::Expr {
368                    kind: ast::ExprKind::Binary(
369                        ast::BinOp {
370                            kind: O::Add,
371                            span: Span::new(3, 1),
372                        },
373                        P(ast::Expr {
374                            kind: ast::ExprKind::Lit(
375                                ast::Lit::parse_with_interner("1", 1, itn).unwrap(),
376                            ),
377                            span: Span::new(1, 1),
378                        }),
379                        P(ast::Expr {
380                            kind: ast::ExprKind::Lit(
381                                ast::Lit::parse_with_interner("2", 5, itn).unwrap(),
382                            ),
383                            span: Span::new(5, 1),
384                        }),
385                    ),
386                    span: Span::new(0, 7),
387                }),
388                P(ast::Expr {
389                    kind: ast::ExprKind::Lit(ast::Lit::parse_with_interner("3", 10, itn).unwrap()),
390                    span: Span::new(10, 1),
391                }),
392            ),
393            span: Span::new(0, 11),
394        });
395
396        assert_parse("bar()", |itn| ast::Expr {
397            kind: ast::ExprKind::Call(
398                P(ast::Expr {
399                    kind: ast::ExprKind::Var(ast::Var::parse_with_interner("bar", 0, itn).unwrap()),
400                    span: Span::new(0, 3),
401                }),
402                Vec::new(),
403            ),
404            span: Span::new(0, 5),
405        });
406
407        assert_parse("$foo = bar(42, 1 + 2 + @@baz(quux, )) or false", |itn| {
408            ast::Expr {
409                kind: ast::ExprKind::Binary(
410                    ast::BinOp {
411                        kind: O::Assign,
412                        span: Span::new(5, 1),
413                    },
414                    P(ast::Expr {
415                        kind: ast::ExprKind::Var(
416                            ast::Var::parse_with_interner("$foo", 0, itn).unwrap(),
417                        ),
418                        span: Span::new(0, 4),
419                    }),
420                    P(ast::Expr {
421                        kind: ast::ExprKind::Binary(
422                            ast::BinOp {
423                                kind: O::Or,
424                                span: Span::new(38, 2),
425                            },
426                            P(ast::Expr {
427                                kind: ast::ExprKind::Call(
428                                    P(ast::Expr {
429                                        kind: ast::ExprKind::Var(
430                                            ast::Var::parse_with_interner("bar", 7, itn).unwrap(),
431                                        ),
432                                        span: Span::new(7, 3),
433                                    }),
434                                    vec![
435                                        ast::Expr::parse_with_interner("42", 11, itn).unwrap(),
436                                        ast::Expr::parse_with_interner(
437                                            "1 + 2 + @@baz(quux, )",
438                                            15,
439                                            itn,
440                                        )
441                                        .unwrap(),
442                                    ],
443                                ),
444                                span: Span::new(7, 31),
445                            }),
446                            P(ast::Expr {
447                                kind: ast::ExprKind::Lit(
448                                    ast::Lit::parse_with_interner("false", 41, itn).unwrap(),
449                                ),
450                                span: Span::new(41, 5),
451                            }),
452                        ),
453                        span: Span::new(7, 39),
454                    }),
455                ),
456                span: Span::new(0, 46),
457            }
458        });
459    }
460}