solar_parse/parser/
expr.rs

1use crate::{PResult, Parser};
2use solar_ast::{token::*, *};
3use solar_interface::kw;
4
5impl<'sess, 'ast> Parser<'sess, 'ast> {
6    /// Parses an expression.
7    #[inline]
8    pub fn parse_expr(&mut self) -> PResult<'sess, Box<'ast, Expr<'ast>>> {
9        self.parse_expr_with(None)
10    }
11
12    #[instrument(name = "parse_expr", level = "debug", skip_all)]
13    pub(super) fn parse_expr_with(
14        &mut self,
15        with: Option<Box<'ast, Expr<'ast>>>,
16    ) -> PResult<'sess, Box<'ast, Expr<'ast>>> {
17        let expr = self.parse_binary_expr(4, with)?;
18        if self.eat(TokenKind::Question) {
19            let then = self.parse_expr()?;
20            self.expect(TokenKind::Colon)?;
21            let else_ = self.parse_expr()?;
22            let span = expr.span.to(self.prev_token.span);
23            Ok(self.alloc(Expr { span, kind: ExprKind::Ternary(expr, then, else_) }))
24        } else {
25            let kind = if let Some(binop_eq) = self.token.as_binop_eq() {
26                Some(binop_eq)
27            } else if self.token.kind == TokenKind::Eq {
28                None
29            } else {
30                return Ok(expr);
31            };
32            self.bump(); // binop token
33            let rhs = self.parse_expr()?;
34            let span = expr.span.to(self.prev_token.span);
35            Ok(self.alloc(Expr { span, kind: ExprKind::Assign(expr, kind, rhs) }))
36        }
37    }
38
39    /// Parses a binary expression.
40    fn parse_binary_expr(
41        &mut self,
42        min_precedence: usize,
43        with: Option<Box<'ast, Expr<'ast>>>,
44    ) -> PResult<'sess, Box<'ast, Expr<'ast>>> {
45        let mut expr = self.parse_unary_expr(with)?;
46        let mut precedence = token_precedence(self.token);
47        while precedence >= min_precedence {
48            while token_precedence(self.token) == precedence {
49                // Parse a**b**c as a**(b**c)
50                let next_precedence = if self.token.kind == TokenKind::BinOp(BinOpToken::Star) {
51                    precedence + 1
52                } else {
53                    precedence
54                };
55
56                let token = self.token;
57                self.bump(); // binop token
58
59                let rhs = self.parse_binary_expr(next_precedence, None)?;
60
61                let span = expr.span.to(self.prev_token.span);
62
63                let kind = if let Some(binop) = token.as_binop() {
64                    ExprKind::Binary(expr, binop, rhs)
65                } else if let Some(binop_eq) = token.as_binop_eq() {
66                    ExprKind::Assign(expr, Some(binop_eq), rhs)
67                } else if token.kind == TokenKind::Eq {
68                    ExprKind::Assign(expr, None, rhs)
69                } else {
70                    let msg = format!("unknown binop token: {token:?}");
71                    self.dcx().bug(msg).span(span).emit();
72                };
73                expr = self.alloc(Expr { span, kind });
74            }
75            precedence -= 1;
76        }
77        Ok(expr)
78    }
79
80    /// Parses a unary expression.
81    fn parse_unary_expr(
82        &mut self,
83        with: Option<Box<'ast, Expr<'ast>>>,
84    ) -> PResult<'sess, Box<'ast, Expr<'ast>>> {
85        if with.is_none() && self.eat(TokenKind::BinOp(BinOpToken::Plus)) {
86            self.dcx().err("unary plus is not supported").span(self.prev_token.span).emit();
87        }
88
89        let lo = with.as_ref().map(|e| e.span).unwrap_or(self.token.span);
90        let parse_lhs = |this: &mut Self, with| {
91            this.parse_lhs_expr(with, lo).map(|expr| {
92                if let Some(unop) = this.token.as_unop(true) {
93                    this.bump(); // unop
94                    let span = lo.to(this.prev_token.span);
95                    this.alloc(Expr { span, kind: ExprKind::Unary(unop, expr) })
96                } else {
97                    expr
98                }
99            })
100        };
101        if let Some(with) = with {
102            parse_lhs(self, Some(with))
103        } else if self.eat_keyword(kw::Delete) {
104            self.parse_unary_expr(None).map(|expr| {
105                let span = lo.to(self.prev_token.span);
106                self.alloc(Expr { span, kind: ExprKind::Delete(expr) })
107            })
108        } else if let Some(unop) = self.token.as_unop(false) {
109            self.bump(); // unop
110            self.parse_unary_expr(None).map(|expr| {
111                let span = lo.to(self.prev_token.span);
112                self.alloc(Expr { span, kind: ExprKind::Unary(unop, expr) })
113            })
114        } else {
115            parse_lhs(self, None)
116        }
117    }
118
119    /// Parses a primary left-hand-side expression.
120    fn parse_lhs_expr(
121        &mut self,
122        with: Option<Box<'ast, Expr<'ast>>>,
123        lo: Span,
124    ) -> PResult<'sess, Box<'ast, Expr<'ast>>> {
125        let mut expr = if let Some(with) = with {
126            Ok(with)
127        } else if self.eat_keyword(kw::New) {
128            self.parse_type().map(|ty| {
129                let span = lo.to(self.prev_token.span);
130                self.alloc(Expr { span, kind: ExprKind::New(ty) })
131            })
132        } else if self.eat_keyword(kw::Payable) {
133            self.parse_call_args().map(|args| {
134                let span = lo.to(self.prev_token.span);
135                self.alloc(Expr { span, kind: ExprKind::Payable(args) })
136            })
137        } else {
138            self.parse_primary_expr()
139        }?;
140        loop {
141            let kind = if self.eat(TokenKind::Dot) {
142                // expr.member
143                let member = self.parse_ident_any()?;
144                ExprKind::Member(expr, member)
145            } else if self.check(TokenKind::OpenDelim(Delimiter::Parenthesis)) {
146                // expr(args)
147                let args = self.parse_call_args()?;
148                ExprKind::Call(expr, args)
149            } else if self.check(TokenKind::OpenDelim(Delimiter::Bracket)) {
150                let kind = self.parse_expr_index_kind()?;
151                ExprKind::Index(expr, kind)
152            } else if self.check(TokenKind::OpenDelim(Delimiter::Brace)) {
153                // This may be `try` statement block.
154                if !self.look_ahead(1).is_ident() || self.look_ahead(2).kind != TokenKind::Colon {
155                    break;
156                }
157
158                // expr{args}
159                let args = self.parse_named_args(false)?;
160                ExprKind::CallOptions(expr, args)
161            } else {
162                break;
163            };
164            let span = lo.to(self.prev_token.span);
165            expr = self.alloc(Expr { span, kind });
166        }
167        Ok(expr)
168    }
169
170    /// Parses a primary expression.
171    fn parse_primary_expr(&mut self) -> PResult<'sess, Box<'ast, Expr<'ast>>> {
172        let lo = self.token.span;
173        let kind = if self.check_lit() {
174            let (lit, sub) = self.parse_lit_with_subdenomination()?;
175            ExprKind::Lit(lit, sub)
176        } else if self.eat_keyword(kw::Type) {
177            self.expect(TokenKind::OpenDelim(Delimiter::Parenthesis))?;
178            let ty = self.parse_type()?;
179            self.expect(TokenKind::CloseDelim(Delimiter::Parenthesis))?;
180            ExprKind::TypeCall(ty)
181        } else if self.check_elementary_type() {
182            let mut ty = self.parse_type()?;
183            if let TypeKind::Elementary(ElementaryType::Address(payable)) = &mut ty.kind {
184                if *payable {
185                    let msg = "`address payable` cannot be used in an expression";
186                    self.dcx().err(msg).span(ty.span).emit();
187                    *payable = false;
188                }
189            }
190            ExprKind::Type(ty)
191        } else if self.check_nr_ident() {
192            let ident = self.parse_ident()?;
193            ExprKind::Ident(ident)
194        } else if self.check(TokenKind::OpenDelim(Delimiter::Parenthesis))
195            || self.check(TokenKind::OpenDelim(Delimiter::Bracket))
196        {
197            // Array or tuple expression.
198            let TokenKind::OpenDelim(close_delim) = self.token.kind else { unreachable!() };
199            let is_array = close_delim == Delimiter::Bracket;
200            let list = self.parse_optional_items_seq(close_delim, Self::parse_expr)?;
201            if is_array {
202                if !list.iter().all(Option::is_some) {
203                    let msg = "array expression components cannot be empty";
204                    let span = lo.to(self.prev_token.span);
205                    return Err(self.dcx().err(msg).span(span));
206                }
207                // SAFETY: All elements are checked to be `Some` above.
208                ExprKind::Array(unsafe { option_boxes_unwrap_unchecked(list) })
209            } else {
210                ExprKind::Tuple(list)
211            }
212        } else {
213            return self.unexpected();
214        };
215        let span = lo.to(self.prev_token.span);
216        Ok(self.alloc(Expr { span, kind }))
217    }
218
219    /// Parses a list of function call arguments.
220    #[track_caller]
221    pub(super) fn parse_call_args(&mut self) -> PResult<'sess, CallArgs<'ast>> {
222        self.parse_spanned(Self::parse_call_args_kind).map(|(span, kind)| CallArgs { span, kind })
223    }
224
225    #[track_caller]
226    fn parse_call_args_kind(&mut self) -> PResult<'sess, CallArgsKind<'ast>> {
227        if self.look_ahead(1).kind == TokenKind::OpenDelim(Delimiter::Brace) {
228            self.expect(TokenKind::OpenDelim(Delimiter::Parenthesis))?;
229            let args = self.parse_named_args(true).map(CallArgsKind::Named)?;
230            self.expect(TokenKind::CloseDelim(Delimiter::Parenthesis))?;
231            Ok(args)
232        } else {
233            self.parse_unnamed_args().map(CallArgsKind::Unnamed)
234        }
235    }
236
237    /// Parses a `[]` indexing expression.
238    pub(super) fn parse_expr_index_kind(&mut self) -> PResult<'sess, IndexKind<'ast>> {
239        self.expect(TokenKind::OpenDelim(Delimiter::Bracket))?;
240        let kind = if self.check(TokenKind::CloseDelim(Delimiter::Bracket)) {
241            // expr[]
242            IndexKind::Index(None)
243        } else {
244            let start = if self.check(TokenKind::Colon) { None } else { Some(self.parse_expr()?) };
245            if self.eat_noexpect(TokenKind::Colon) {
246                // expr[start?:end?]
247                let end = if self.check(TokenKind::CloseDelim(Delimiter::Bracket)) {
248                    None
249                } else {
250                    Some(self.parse_expr()?)
251                };
252                IndexKind::Range(start, end)
253            } else {
254                // expr[start?]
255                IndexKind::Index(start)
256            }
257        };
258        self.expect(TokenKind::CloseDelim(Delimiter::Bracket))?;
259        Ok(kind)
260    }
261
262    /// Parses a list of named arguments: `{a: b, c: d, ...}`
263    #[track_caller]
264    fn parse_named_args(&mut self, allow_empty: bool) -> PResult<'sess, NamedArgList<'ast>> {
265        self.parse_delim_comma_seq(Delimiter::Brace, allow_empty, Self::parse_named_arg)
266    }
267
268    /// Parses a single named argument: `a: b`.
269    #[track_caller]
270    fn parse_named_arg(&mut self) -> PResult<'sess, NamedArg<'ast>> {
271        let name = self.parse_ident()?;
272        self.expect(TokenKind::Colon)?;
273        let value = self.parse_expr()?;
274        Ok(NamedArg { name, value })
275    }
276
277    /// Parses a list of expressions: `(a, b, c, ...)`.
278    #[allow(clippy::vec_box)]
279    #[track_caller]
280    fn parse_unnamed_args(&mut self) -> PResult<'sess, Box<'ast, [Box<'ast, Expr<'ast>>]>> {
281        self.parse_paren_comma_seq(true, Self::parse_expr)
282    }
283}
284
285fn token_precedence(t: Token) -> usize {
286    // https://github.com/ethereum/solidity/blob/78ec8dd6f93bf5a5b4ca7582f9d491a4f66c3610/liblangutil/Token.h#L68
287    use BinOpToken::*;
288    use TokenKind::*;
289    match t.kind {
290        Question => 3,
291        Eq => 2,
292        BinOpEq(_) => 2,
293        Comma => 1,
294        OrOr => 4,
295        AndAnd => 5,
296        BinOp(Or) => 8,
297        BinOp(Caret) => 9,
298        BinOp(And) => 10,
299        BinOp(Shl) => 11,
300        BinOp(Sar) => 11,
301        BinOp(Shr) => 11,
302        BinOp(Plus) => 12,
303        BinOp(Minus) => 12,
304        BinOp(Star) => 13,
305        BinOp(Slash) => 13,
306        BinOp(Percent) => 13,
307        StarStar => 14,
308        EqEq => 6,
309        Ne => 6,
310        Lt => 7,
311        Gt => 7,
312        Le => 7,
313        Ge => 7,
314        Walrus => 2,
315        _ => 0,
316    }
317}
318
319/// Converts a list of `Option<Box<'ast, T>>` into a list of `Box<'ast, T>`.
320///
321/// This only works because `Option<Box<'ast, T>>` is guaranteed to be a valid `Box<'ast, T>` when
322/// `Some` when `T: Sized`.
323///
324/// # Safety
325///
326/// All elements of the list must be `Some`.
327#[inline]
328unsafe fn option_boxes_unwrap_unchecked<'a, 'b, T>(
329    list: Box<'a, [Option<Box<'b, T>>]>,
330) -> Box<'a, [Box<'b, T>]> {
331    debug_assert!(list.iter().all(Option::is_some));
332    // SAFETY: Caller must ensure that all elements are `Some`.
333    unsafe { std::mem::transmute(list) }
334}