use crate::{PResult, Parser};
use smallvec::SmallVec;
use solar_ast::{token::*, *};
use solar_interface::kw;
impl<'sess, 'ast> Parser<'sess, 'ast> {
#[inline]
pub fn parse_expr(&mut self) -> PResult<'sess, Box<'ast, Expr<'ast>>> {
self.with_recursion_limit("expression", |this| this.parse_expr_with(None))
}
#[instrument(name = "parse_expr", level = "trace", skip_all)]
pub(super) fn parse_expr_with(
&mut self,
with: Option<Box<'ast, Expr<'ast>>>,
) -> PResult<'sess, Box<'ast, Expr<'ast>>> {
let expr = self.parse_binary_expr(4, with)?;
if self.eat(TokenKind::Question) {
let then = self.parse_expr()?;
self.expect(TokenKind::Colon)?;
let else_ = self.parse_expr()?;
let span = expr.span.to(self.prev_token.span);
Ok(self.alloc(Expr { span, kind: ExprKind::Ternary(expr, then, else_) }))
} else {
let kind = if let Some(binop_eq) = self.token.as_binop_eq() {
Some(binop_eq)
} else if self.token.kind == TokenKind::Eq {
None
} else {
return Ok(expr);
};
self.bump(); let rhs = self.parse_expr()?;
let span = expr.span.to(self.prev_token.span);
Ok(self.alloc(Expr { span, kind: ExprKind::Assign(expr, kind, rhs) }))
}
}
fn parse_binary_expr(
&mut self,
min_precedence: usize,
with: Option<Box<'ast, Expr<'ast>>>,
) -> PResult<'sess, Box<'ast, Expr<'ast>>> {
let mut expr = self.parse_unary_expr(with)?;
let mut precedence = token_precedence(self.token);
while precedence >= min_precedence {
while token_precedence(self.token) == precedence {
let next_precedence = if self.token.kind == TokenKind::BinOp(BinOpToken::Star) {
precedence + 1
} else {
precedence
};
let token = self.token;
self.bump();
let rhs = self.parse_binary_expr(next_precedence, None)?;
let span = expr.span.to(self.prev_token.span);
let kind = if let Some(binop) = token.as_binop() {
ExprKind::Binary(expr, binop, rhs)
} else if let Some(binop_eq) = token.as_binop_eq() {
ExprKind::Assign(expr, Some(binop_eq), rhs)
} else if token.kind == TokenKind::Eq {
ExprKind::Assign(expr, None, rhs)
} else {
let msg = format!("unknown binop token: {token:?}");
self.dcx().bug(msg).span(span).emit();
};
expr = self.alloc(Expr { span, kind });
}
precedence -= 1;
}
Ok(expr)
}
fn parse_unary_expr(
&mut self,
with: Option<Box<'ast, Expr<'ast>>>,
) -> PResult<'sess, Box<'ast, Expr<'ast>>> {
if with.is_none() && self.eat(TokenKind::BinOp(BinOpToken::Plus)) {
self.dcx().err("unary plus is not supported").span(self.prev_token.span).emit();
}
let lo = with.as_ref().map(|e| e.span).unwrap_or(self.token.span);
let parse_lhs = |this: &mut Self, with| {
this.parse_lhs_expr(with, lo).map(|expr| {
if let Some(unop) = this.token.as_unop(true) {
this.bump(); let span = lo.to(this.prev_token.span);
this.alloc(Expr { span, kind: ExprKind::Unary(unop, expr) })
} else {
expr
}
})
};
if let Some(with) = with {
parse_lhs(self, Some(with))
} else if self.eat_keyword(kw::Delete) {
self.parse_unary_expr(None).map(|expr| {
let span = lo.to(self.prev_token.span);
self.alloc(Expr { span, kind: ExprKind::Delete(expr) })
})
} else if let Some(unop) = self.token.as_unop(false) {
self.bump(); self.parse_unary_expr(None).map(|expr| {
let span = lo.to(self.prev_token.span);
self.alloc(Expr { span, kind: ExprKind::Unary(unop, expr) })
})
} else {
parse_lhs(self, None)
}
}
fn parse_lhs_expr(
&mut self,
with: Option<Box<'ast, Expr<'ast>>>,
lo: Span,
) -> PResult<'sess, Box<'ast, Expr<'ast>>> {
let mut expr = if let Some(with) = with {
Ok(with)
} else if self.eat_keyword(kw::New) {
self.parse_type().map(|ty| {
let span = lo.to(self.prev_token.span);
self.alloc(Expr { span, kind: ExprKind::New(ty) })
})
} else if self.eat_keyword(kw::Payable) {
self.parse_call_args().map(|args| {
let span = lo.to(self.prev_token.span);
self.alloc(Expr { span, kind: ExprKind::Payable(args) })
})
} else {
self.parse_primary_expr()
}?;
loop {
let kind = if self.eat(TokenKind::Dot) {
let member = self.parse_ident_any()?;
ExprKind::Member(expr, member)
} else if self.check(TokenKind::OpenDelim(Delimiter::Parenthesis)) {
let args = self.parse_call_args()?;
ExprKind::Call(expr, args)
} else if self.check(TokenKind::OpenDelim(Delimiter::Bracket)) {
let kind = self.parse_expr_index_kind()?;
ExprKind::Index(expr, kind)
} else if self.check(TokenKind::OpenDelim(Delimiter::Brace)) {
if !self.look_ahead(1).is_ident() || self.look_ahead(2).kind != TokenKind::Colon {
break;
}
let args = self.parse_named_args(false)?;
ExprKind::CallOptions(expr, args)
} else {
break;
};
let span = lo.to(self.prev_token.span);
expr = self.alloc(Expr { span, kind });
}
Ok(expr)
}
fn parse_primary_expr(&mut self) -> PResult<'sess, Box<'ast, Expr<'ast>>> {
let lo = self.token.span;
let kind = if self.check_lit() {
let (lit, sub) = self.parse_lit(true)?;
ExprKind::Lit(self.alloc(lit), sub)
} else if self.eat_keyword(kw::Type) {
self.expect(TokenKind::OpenDelim(Delimiter::Parenthesis))?;
let ty = self.parse_type()?;
self.expect(TokenKind::CloseDelim(Delimiter::Parenthesis))?;
ExprKind::TypeCall(ty)
} else if self.check_elementary_type() {
let mut ty = self.parse_type()?;
if let TypeKind::Elementary(ElementaryType::Address(payable)) = &mut ty.kind
&& *payable
{
let msg = "`address payable` cannot be used in an expression";
self.dcx().err(msg).span(ty.span).emit();
*payable = false;
}
ExprKind::Type(ty)
} else if self.check_nr_ident() {
let ident = self.parse_ident()?;
ExprKind::Ident(ident)
} else if self.check(TokenKind::OpenDelim(Delimiter::Parenthesis))
|| self.check(TokenKind::OpenDelim(Delimiter::Bracket))
{
let TokenKind::OpenDelim(close_delim) = self.token.kind else { unreachable!() };
let is_array = close_delim == Delimiter::Bracket;
let list = self.parse_optional_items_seq(close_delim, Self::parse_expr)?;
if is_array {
let list = list
.into_iter()
.map(|item| match item.into() {
Some(expr) => Ok(Some(expr)),
None => {
let msg = "array expression components cannot be empty";
let span = lo.to(self.prev_token.span);
Err(self.dcx().err(msg).span(span))
}
})
.collect::<Result<SmallVec<[Option<_>; 8]>, _>>()?;
ExprKind::Array(unsafe { option_boxes_unwrap_unchecked(self.alloc_smallvec(list)) })
} else {
ExprKind::Tuple(self.alloc_smallvec(list))
}
} else {
return self.unexpected();
};
let span = lo.to(self.prev_token.span);
Ok(self.alloc(Expr { span, kind }))
}
#[track_caller]
pub(super) fn parse_call_args(&mut self) -> PResult<'sess, CallArgs<'ast>> {
self.parse_spanned(Self::parse_call_args_kind).map(|(span, kind)| CallArgs { span, kind })
}
#[track_caller]
fn parse_call_args_kind(&mut self) -> PResult<'sess, CallArgsKind<'ast>> {
if self.look_ahead(1).kind == TokenKind::OpenDelim(Delimiter::Brace) {
self.expect(TokenKind::OpenDelim(Delimiter::Parenthesis))?;
let args = self.parse_named_args(true).map(CallArgsKind::Named)?;
self.expect(TokenKind::CloseDelim(Delimiter::Parenthesis))?;
Ok(args)
} else {
self.parse_unnamed_args().map(CallArgsKind::Unnamed)
}
}
pub(super) fn parse_expr_index_kind(&mut self) -> PResult<'sess, IndexKind<'ast>> {
self.expect(TokenKind::OpenDelim(Delimiter::Bracket))?;
let kind = if self.check(TokenKind::CloseDelim(Delimiter::Bracket)) {
IndexKind::Index(None)
} else {
let start = if self.check(TokenKind::Colon) { None } else { Some(self.parse_expr()?) };
if self.eat_noexpect(TokenKind::Colon) {
let end = if self.check(TokenKind::CloseDelim(Delimiter::Bracket)) {
None
} else {
Some(self.parse_expr()?)
};
IndexKind::Range(start, end)
} else {
IndexKind::Index(start)
}
};
self.expect(TokenKind::CloseDelim(Delimiter::Bracket))?;
Ok(kind)
}
#[track_caller]
fn parse_named_args(&mut self, allow_empty: bool) -> PResult<'sess, NamedArgList<'ast>> {
self.parse_delim_comma_seq(Delimiter::Brace, allow_empty, Self::parse_named_arg)
}
#[track_caller]
fn parse_named_arg(&mut self) -> PResult<'sess, NamedArg<'ast>> {
let name = self.parse_ident()?;
self.expect(TokenKind::Colon)?;
let value = self.parse_expr()?;
Ok(NamedArg { name, value })
}
#[allow(clippy::vec_box)]
#[track_caller]
fn parse_unnamed_args(&mut self) -> PResult<'sess, BoxSlice<'ast, Box<'ast, Expr<'ast>>>> {
self.parse_paren_comma_seq(true, Self::parse_expr)
}
}
fn token_precedence(t: Token) -> usize {
use BinOpToken::*;
use TokenKind::*;
match t.kind {
Question => 3,
Eq => 2,
BinOpEq(_) => 2,
Comma => 1,
OrOr => 4,
AndAnd => 5,
BinOp(Or) => 8,
BinOp(Caret) => 9,
BinOp(And) => 10,
BinOp(Shl) => 11,
BinOp(Sar) => 11,
BinOp(Shr) => 11,
BinOp(Plus) => 12,
BinOp(Minus) => 12,
BinOp(Star) => 13,
BinOp(Slash) => 13,
BinOp(Percent) => 13,
StarStar => 14,
EqEq => 6,
Ne => 6,
Lt => 7,
Gt => 7,
Le => 7,
Ge => 7,
Walrus => 2,
_ => 0,
}
}
#[inline]
unsafe fn option_boxes_unwrap_unchecked<'a, 'b, T>(
list: BoxSlice<'a, Option<Box<'b, T>>>,
) -> BoxSlice<'a, Box<'b, T>> {
debug_assert!(list.iter().all(Option::is_some));
unsafe { std::mem::transmute(list) }
}