use crate::ast;
use crate::{
OptionSpanned as _, Parse, ParseError, ParseErrorKind, Parser, Peek, Peeker, Spanned, ToTokens,
};
use std::mem::take;
use std::ops;
#[derive(Debug, Clone, Copy, Default)]
pub(crate) struct EagerBrace(pub(crate) bool);
impl ops::Deref for EagerBrace {
type Target = bool;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct EagerBinary(pub(crate) bool);
impl ops::Deref for EagerBinary {
type Target = bool;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct Callable(pub(crate) bool);
impl ops::Deref for Callable {
type Target = bool;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Default for Callable {
fn default() -> Self {
Self(true)
}
}
#[derive(Debug, Clone, PartialEq, Eq, ToTokens, Spanned)]
pub enum Expr {
Path(Box<ast::Path>),
Item(Box<ast::Item>),
Assign(Box<ast::ExprAssign>),
While(Box<ast::ExprWhile>),
Loop(Box<ast::ExprLoop>),
For(Box<ast::ExprFor>),
Let(Box<ast::ExprLet>),
If(Box<ast::ExprIf>),
Match(Box<ast::ExprMatch>),
Call(Box<ast::ExprCall>),
FieldAccess(Box<ast::ExprFieldAccess>),
Group(Box<ast::ExprGroup>),
Binary(Box<ast::ExprBinary>),
Unary(Box<ast::ExprUnary>),
Index(Box<ast::ExprIndex>),
Break(Box<ast::ExprBreak>),
Continue(Box<ast::ExprContinue>),
Yield(Box<ast::ExprYield>),
Block(Box<ast::ExprBlock>),
Return(Box<ast::ExprReturn>),
Await(Box<ast::ExprAwait>),
Try(Box<ast::ExprTry>),
Select(Box<ast::ExprSelect>),
Closure(Box<ast::ExprClosure>),
Lit(Box<ast::ExprLit>),
ForceSemi(Box<ast::ForceSemi>),
MacroCall(Box<ast::MacroCall>),
Object(Box<ast::ExprObject>),
Tuple(Box<ast::ExprTuple>),
Vec(Box<ast::ExprVec>),
Range(Box<ast::ExprRange>),
}
impl Expr {
pub fn needs_semi(&self) -> bool {
match self {
Self::While(_) => false,
Self::Loop(_) => false,
Self::For(_) => false,
Self::If(_) => false,
Self::Match(_) => false,
Self::Block(_) => false,
Self::Select(_) => false,
Self::MacroCall(macro_call) => macro_call.needs_semi(),
Self::ForceSemi(force_semi) => force_semi.needs_semi,
_ => true,
}
}
pub fn is_callable(&self, callable: bool) -> bool {
match self {
Self::While(_) => false,
Self::Loop(_) => callable,
Self::For(_) => false,
Self::If(_) => callable,
Self::Match(_) => callable,
Self::Select(_) => callable,
Self::ForceSemi(expr) => expr.expr.is_callable(callable),
_ => true,
}
}
pub fn take_attributes(&mut self) -> Vec<ast::Attribute> {
match self {
Self::Path(_) => Vec::new(),
Self::Item(item) => item.take_attributes(),
Self::Break(expr) => take(&mut expr.attributes),
Self::Continue(expr) => take(&mut expr.attributes),
Self::Yield(expr) => take(&mut expr.attributes),
Self::Block(expr) => take(&mut expr.attributes),
Self::Return(expr) => take(&mut expr.attributes),
Self::Closure(expr) => take(&mut expr.attributes),
Self::Match(expr) => take(&mut expr.attributes),
Self::While(expr) => take(&mut expr.attributes),
Self::Loop(expr) => take(&mut expr.attributes),
Self::For(expr) => take(&mut expr.attributes),
Self::Let(expr) => take(&mut expr.attributes),
Self::If(expr) => take(&mut expr.attributes),
Self::Select(expr) => take(&mut expr.attributes),
Self::Lit(expr) => take(&mut expr.attributes),
Self::Assign(expr) => take(&mut expr.attributes),
Self::Binary(expr) => take(&mut expr.attributes),
Self::Call(expr) => take(&mut expr.attributes),
Self::FieldAccess(expr) => take(&mut expr.attributes),
Self::Group(expr) => take(&mut expr.attributes),
Self::Unary(expr) => take(&mut expr.attributes),
Self::Index(expr) => take(&mut expr.attributes),
Self::Await(expr) => take(&mut expr.attributes),
Self::Try(expr) => take(&mut expr.attributes),
Self::ForceSemi(expr) => expr.expr.take_attributes(),
Self::Object(expr) => take(&mut expr.attributes),
Self::Range(expr) => take(&mut expr.attributes),
Self::Vec(expr) => take(&mut expr.attributes),
Self::Tuple(expr) => take(&mut expr.attributes),
Self::MacroCall(expr) => take(&mut expr.attributes),
}
}
pub fn attributes(&self) -> &[ast::Attribute] {
match self {
Self::Path(_) => &[],
Self::Item(expr) => expr.attributes(),
Self::Break(expr) => &expr.attributes,
Self::Continue(expr) => &expr.attributes,
Self::Yield(expr) => &expr.attributes,
Self::Block(expr) => &expr.attributes,
Self::Return(expr) => &expr.attributes,
Self::Closure(expr) => &expr.attributes,
Self::Match(expr) => &expr.attributes,
Self::While(expr) => &expr.attributes,
Self::Loop(expr) => &expr.attributes,
Self::For(expr) => &expr.attributes,
Self::Let(expr) => &expr.attributes,
Self::If(expr) => &expr.attributes,
Self::Select(expr) => &expr.attributes,
Self::Lit(expr) => &expr.attributes,
Self::Assign(expr) => &expr.attributes,
Self::Binary(expr) => &expr.attributes,
Self::Call(expr) => &expr.attributes,
Self::FieldAccess(expr) => &expr.attributes,
Self::Group(expr) => &expr.attributes,
Self::Unary(expr) => &expr.attributes,
Self::Index(expr) => &expr.attributes,
Self::Await(expr) => &expr.attributes,
Self::Try(expr) => &expr.attributes,
Self::ForceSemi(expr) => expr.expr.attributes(),
Self::MacroCall(expr) => &expr.attributes,
Self::Object(expr) => &expr.attributes,
Self::Range(expr) => &expr.attributes,
Self::Tuple(expr) => &expr.attributes,
Self::Vec(expr) => &expr.attributes,
}
}
pub fn is_lit(&self) -> bool {
match self {
Self::Lit(..) => return true,
Self::Unary(expr_unary) => {
if let ast::ExprUnary {
op: ast::UnOp::Neg,
expr,
..
} = &**expr_unary
{
if let Self::Lit(expr) = expr {
return matches!(
&**expr,
ast::ExprLit {
lit: ast::Lit::Number(..),
..
}
);
}
}
}
_ => (),
}
false
}
pub(crate) fn parse_without_eager_brace(p: &mut Parser<'_>) -> Result<Self, ParseError> {
Self::parse_with(p, EagerBrace(false), EagerBinary(true), Callable(true))
}
pub(crate) fn parse_with(
p: &mut Parser<'_>,
eager_brace: EagerBrace,
eager_binary: EagerBinary,
callable: Callable,
) -> Result<Self, ParseError> {
let mut attributes = p.parse()?;
let expr = Self::parse_base(p, &mut attributes, eager_brace)?;
let expr = Self::parse_chain(p, expr, callable)?;
let expr = if *eager_binary {
Self::parse_binary(p, expr, 0, eager_brace)?
} else {
expr
};
if let Some(span) = attributes.option_span() {
return Err(ParseError::unsupported(span, "attributes"));
}
Ok(expr)
}
pub(crate) fn parse_with_meta_path(
p: &mut Parser<'_>,
attributes: &mut Vec<ast::Attribute>,
path: ast::Path,
eager_brace: EagerBrace,
) -> Result<Self, ParseError> {
if *eager_brace && p.peek::<T!['{']>()? {
let ident = ast::ObjectIdent::Named(path);
return Ok(Self::Object(Box::new(ast::ExprObject::parse_with_meta(
p,
take(attributes),
ident,
)?)));
}
if p.peek::<T![!]>()? {
return Ok(Self::MacroCall(Box::new(
ast::MacroCall::parse_with_meta_path(p, std::mem::take(attributes), path)?,
)));
}
Ok(Self::Path(Box::new(path)))
}
pub fn parse_open_paren(
p: &mut Parser<'_>,
attributes: Vec<ast::Attribute>,
) -> Result<Self, ParseError> {
if let (K!['('], K![')']) = (p.nth(0)?, p.nth(1)?) {
return Ok(Self::Tuple(Box::new(ast::ExprTuple::parse_with_meta(
p, attributes,
)?)));
}
let open = p.parse::<T!['(']>()?;
let expr = p.parse::<Self>()?;
if p.peek::<T![')']>()? {
return Ok(Self::Group(Box::new(ast::ExprGroup {
attributes,
open,
expr,
close: p.parse()?,
})));
}
Ok(Self::Tuple(Box::new(
ast::ExprTuple::parse_from_first_expr(p, attributes, open, expr)?,
)))
}
pub(crate) fn parse_with_meta(
p: &mut Parser<'_>,
attributes: &mut Vec<ast::Attribute>,
path: Option<ast::Path>,
callable: Callable,
) -> Result<Self, ParseError> {
let lhs = if let Some(path) = path {
Self::parse_with_meta_path(p, attributes, path, EagerBrace(true))?
} else {
Self::parse_base(p, attributes, EagerBrace(true))?
};
let lhs = Self::parse_chain(p, lhs, callable)?;
Ok(Self::parse_binary(p, lhs, 0, EagerBrace(true))?)
}
fn parse_base(
p: &mut Parser<'_>,
attributes: &mut Vec<ast::Attribute>,
eager_brace: EagerBrace,
) -> Result<Self, ParseError> {
if let Some(path) = p.parse::<Option<ast::Path>>()? {
return Ok(Self::parse_with_meta_path(
p,
attributes,
path,
eager_brace,
)?);
}
if ast::Lit::peek_in_expr(p.peeker()) {
return Ok(Self::Lit(Box::new(ast::ExprLit::parse_with_meta(
p,
take(attributes),
)?)));
}
let mut label = p.parse::<Option<(ast::Label, T![:])>>()?;
let mut async_token = p.parse::<Option<T![async]>>()?;
let mut const_token = p.parse::<Option<T![const]>>()?;
let mut move_token = p.parse::<Option<T![move]>>()?;
let expr = match p.nth(0)? {
K![..] => {
let limits = ast::ExprRangeLimits::HalfOpen(p.parse()?);
Self::parse_range(p, take(attributes), None, limits)?
}
K![..=] => {
let limits = ast::ExprRangeLimits::Closed(p.parse()?);
Self::parse_range(p, take(attributes), None, limits)?
}
K![#] => {
let ident = ast::ObjectIdent::Anonymous(p.parse()?);
Self::Object(Box::new(ast::ExprObject::parse_with_meta(
p,
take(attributes),
ident,
)?))
}
K![||] | K![|] => Self::Closure(Box::new(ast::ExprClosure::parse_with_meta(
p,
take(attributes),
take(&mut async_token),
take(&mut move_token),
)?)),
K![select] => Self::Select(Box::new(ast::ExprSelect::parse_with_attributes(
p,
take(attributes),
)?)),
K![!] | K![-] | K![&] | K![*] => Self::Unary(Box::new(
ast::ExprUnary::parse_with_meta(p, take(attributes), eager_brace)?,
)),
K![while] => Self::While(Box::new(ast::ExprWhile::parse_with_meta(
p,
take(attributes),
take(&mut label),
)?)),
K![loop] => Self::Loop(Box::new(ast::ExprLoop::parse_with_meta(
p,
take(attributes),
take(&mut label),
)?)),
K![for] => Self::For(Box::new(ast::ExprFor::parse_with_meta(
p,
take(attributes),
take(&mut label),
)?)),
K![let] => Self::Let(Box::new(ast::ExprLet::parse_with_meta(
p,
take(attributes),
)?)),
K![if] => Self::If(Box::new(ast::ExprIf::parse_with_meta(p, take(attributes))?)),
K![match] => Self::Match(Box::new(ast::ExprMatch::parse_with_attributes(
p,
take(attributes),
)?)),
K!['['] => Self::Vec(Box::new(ast::ExprVec::parse_with_meta(
p,
take(attributes),
)?)),
K!['('] => Self::parse_open_paren(p, take(attributes))?,
K!['{'] => Self::Block(Box::new(ast::ExprBlock::parse_with_meta(
p,
take(attributes),
take(&mut async_token),
take(&mut const_token),
take(&mut move_token),
)?)),
K![break] => Self::Break(Box::new(ast::ExprBreak::parse_with_meta(
p,
take(attributes),
)?)),
K![continue] => Self::Continue(Box::new(ast::ExprContinue::parse_with_meta(
p,
take(attributes),
)?)),
K![yield] => Self::Yield(Box::new(ast::ExprYield::parse_with_meta(
p,
take(attributes),
)?)),
K![return] => Self::Return(Box::new(ast::ExprReturn::parse_with_meta(
p,
take(attributes),
)?)),
_ => {
return Err(ParseError::expected(&p.tok_at(0)?, "expression"));
}
};
if let Some(span) = label.option_span() {
return Err(ParseError::unsupported(span, "label"));
}
if let Some(span) = async_token.option_span() {
return Err(ParseError::unsupported(span, "async modifier"));
}
if let Some(span) = const_token.option_span() {
return Err(ParseError::unsupported(span, "const modifier"));
}
if let Some(span) = move_token.option_span() {
return Err(ParseError::unsupported(span, "move modifier"));
}
Ok(expr)
}
fn parse_range(
p: &mut Parser<'_>,
attributes: Vec<ast::Attribute>,
from: Option<Self>,
limits: ast::ExprRangeLimits,
) -> Result<Self, ParseError> {
let to = if Self::peek(p.peeker()) {
Some(Self::parse_with(
p,
EagerBrace(true),
EagerBinary(true),
Callable(true),
)?)
} else {
None
};
Ok(Self::Range(Box::new(ast::ExprRange {
attributes,
from,
limits,
to,
})))
}
fn parse_chain(
p: &mut Parser<'_>,
mut expr: Self,
callable: Callable,
) -> Result<Self, ParseError> {
while !p.is_eof()? {
let is_callable = expr.is_callable(*callable);
match p.nth(0)? {
K!['['] if is_callable => {
expr = Self::Index(Box::new(ast::ExprIndex {
attributes: expr.take_attributes(),
target: expr,
open: p.parse()?,
index: p.parse()?,
close: p.parse()?,
}));
}
K!['('] if is_callable => {
let args = p.parse::<ast::Parenthesized<Self, T![,]>>()?;
expr = Self::Call(Box::new(ast::ExprCall {
id: Default::default(),
attributes: expr.take_attributes(),
expr,
args,
}));
}
K![?] => {
expr = Self::Try(Box::new(ast::ExprTry {
attributes: expr.take_attributes(),
expr,
try_token: p.parse()?,
}));
}
K![=] => {
let eq = p.parse()?;
let rhs =
Self::parse_with(p, EagerBrace(true), EagerBinary(true), Callable(true))?;
expr = Self::Assign(Box::new(ast::ExprAssign {
attributes: expr.take_attributes(),
lhs: expr,
eq,
rhs,
}));
}
K![.] => {
match p.nth(1)? {
K![await] => {
expr = Self::Await(Box::new(ast::ExprAwait {
attributes: expr.take_attributes(),
expr,
dot: p.parse()?,
await_token: p.parse()?,
}));
}
K![ident] => {
expr = Self::FieldAccess(Box::new(ast::ExprFieldAccess {
attributes: expr.take_attributes(),
expr,
dot: p.parse()?,
expr_field: ast::ExprField::Path(p.parse()?),
}));
}
K![number] => {
expr = Self::FieldAccess(Box::new(ast::ExprFieldAccess {
attributes: expr.take_attributes(),
expr,
dot: p.parse()?,
expr_field: ast::ExprField::LitNumber(p.parse()?),
}));
}
_ => {
return Err(ParseError::new(
p.span(0..1),
ParseErrorKind::BadFieldAccess,
));
}
}
}
_ => break,
}
}
Ok(expr)
}
fn parse_binary_rhs(
p: &mut Parser<'_>,
lhs: &Self,
eager_brace: EagerBrace,
op: ast::BinOp,
lookahead_tok: &mut Option<ast::BinOp>,
) -> Result<Self, ParseError> {
let rhs = Self::parse_base(p, &mut vec![], eager_brace)?;
let mut rhs = Self::parse_chain(p, rhs, Callable(true))?;
*lookahead_tok = ast::BinOp::from_peeker(p.peeker());
loop {
let lh = match *lookahead_tok {
Some(lh) if lh.precedence() > op.precedence() => lh,
Some(lh) if lh.precedence() == op.precedence() && !op.is_assoc() => {
return Err(ParseError::new(
lhs.span().join(rhs.span()),
ParseErrorKind::PrecedenceGroupRequired,
));
}
_ => break,
};
rhs = Self::parse_binary(p, rhs, lh.precedence(), eager_brace)?;
*lookahead_tok = ast::BinOp::from_peeker(p.peeker());
}
Ok(rhs)
}
fn try_parse_binary_rhs(
p: &mut Parser<'_>,
lhs: &Self,
eager_brace: EagerBrace,
op: ast::BinOp,
lookahead_tok: &mut Option<ast::BinOp>,
) -> Result<Option<Self>, ParseError> {
Ok(if Self::peek(p.peeker()) {
Some(Self::parse_binary_rhs(
p,
lhs,
eager_brace,
op,
lookahead_tok,
)?)
} else {
*lookahead_tok = None;
None
})
}
fn parse_binary(
p: &mut Parser<'_>,
mut lhs: Self,
min_precedence: usize,
eager_brace: EagerBrace,
) -> Result<Self, ParseError> {
let mut lookahead_tok = ast::BinOp::from_peeker(p.peeker());
loop {
let op = match lookahead_tok {
Some(op) if op.precedence() >= min_precedence => op,
_ => break,
};
let (t1, t2) = op.advance(p)?;
lhs = match t1.kind {
K![..] => {
let to =
Self::try_parse_binary_rhs(p, &lhs, eager_brace, op, &mut lookahead_tok)?;
Self::Range(Box::new(ast::ExprRange {
attributes: lhs.take_attributes(),
from: Some(lhs),
limits: ast::ExprRangeLimits::HalfOpen(ast::generated::DotDot {
token: t1,
}),
to,
}))
}
K![..=] => {
let to =
Self::try_parse_binary_rhs(p, &lhs, eager_brace, op, &mut lookahead_tok)?;
Self::Range(Box::new(ast::ExprRange {
attributes: lhs.take_attributes(),
from: Some(lhs),
limits: ast::ExprRangeLimits::Closed(ast::generated::DotDotEq {
token: t1,
}),
to,
}))
}
_ => {
let rhs = Self::parse_binary_rhs(p, &lhs, eager_brace, op, &mut lookahead_tok)?;
Self::Binary(Box::new(ast::ExprBinary {
attributes: Vec::new(),
lhs,
t1,
t2,
rhs,
op,
}))
}
};
}
Ok(lhs)
}
pub(crate) fn from_lit(lit: ast::Lit) -> Self {
Self::Lit(Box::new(ast::ExprLit {
attributes: Vec::new(),
lit,
}))
}
}
impl Parse for Expr {
fn parse(p: &mut Parser<'_>) -> Result<Self, ParseError> {
Self::parse_with(p, EagerBrace(true), EagerBinary(true), Callable(true))
}
}
impl Peek for Expr {
fn peek(p: &mut Peeker<'_>) -> bool {
match p.nth(0) {
K![async] => true,
K![self] => true,
K![select] => true,
K![#] => true,
K![-] => true,
K![!] => true,
K![&] => true,
K![*] => true,
K![while] => true,
K![loop] => true,
K![for] => true,
K![let] => true,
K![if] => true,
K![break] => true,
K![continue] => true,
K![return] => true,
K![true] => true,
K![false] => true,
K![ident] => true,
K![::] => true,
K!['('] => true,
K!['['] => true,
K!['{'] => true,
K![number] => true,
K![char] => true,
K![byte] => true,
K![str] => true,
K![bytestr] => true,
K!['label] => matches!(p.nth(1), K![:]),
K![..] => true,
_ => false,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, ToTokens, Spanned)]
pub struct ExprWithoutBinary {
expr: ast::Expr,
}
impl ExprWithoutBinary {
pub fn into_expr(self) -> Expr {
self.expr
}
}
impl Parse for ExprWithoutBinary {
fn parse(p: &mut Parser) -> Result<Self, ParseError> {
let expr = Expr::parse_with(
p,
Default::default(),
EagerBinary(false),
Default::default(),
)?;
Ok(Self { expr })
}
}
impl Peek for ExprWithoutBinary {
fn peek(p: &mut Peeker<'_>) -> bool {
Expr::peek(p)
}
}
#[cfg(test)]
mod tests {
use crate::{ast, testing};
#[test]
fn test_expr_if() {
testing::roundtrip::<ast::Expr>(r#"if true {} else {}"#);
}
#[test]
fn test_expr_while() {
testing::roundtrip::<ast::ExprWhile>(r#"while true {}"#);
}
}