use core::fmt;
use crate::ast::prelude::*;
use crate::parse::Advance;
#[test]
#[cfg(not(miri))]
fn ast_parse() {
rt::<ast::ExprBinary>("42 + b");
rt::<ast::ExprBinary>("b << 10");
}
#[derive(Debug, TryClone, PartialEq, Eq, ToTokens, Spanned)]
#[non_exhaustive]
pub struct ExprBinary {
#[rune(iter)]
pub attributes: Vec<ast::Attribute>,
pub lhs: Box<ast::Expr>,
pub op: BinOp,
pub rhs: Box<ast::Expr>,
}
expr_parse!(Binary, ExprBinary, "binary expression");
#[derive(Debug, TryClone, Clone, Copy, PartialEq, Eq, Hash, ToTokens, Spanned)]
#[try_clone(copy)]
#[non_exhaustive]
pub enum BinOp {
Add(T![+]),
Sub(T![-]),
Div(T![/]),
Mul(T![*]),
Rem(T![%]),
Eq(T![==]),
Neq(T![!=]),
Gt(T![>]),
Lt(T![<]),
Gte(T![>=]),
Lte(T![<=]),
As(T![as]),
Is(T![is]),
IsNot(T![is not]),
And(T![&&]),
Or(T![||]),
Shl(T![<<]),
Shr(T![>>]),
BitAnd(T![&]),
BitXor(T![^]),
BitOr(T![|]),
AddAssign(T![+=]),
SubAssign(T![-=]),
MulAssign(T![*=]),
DivAssign(T![/=]),
RemAssign(T![%=]),
BitAndAssign(T![&=]),
BitXorAssign(T![^=]),
BitOrAssign(T![|=]),
ShlAssign(T![<<=]),
ShrAssign(T![>>=]),
DotDot(T![..]),
DotDotEq(T![..=]),
}
impl BinOp {
pub(crate) fn is_assign(&self) -> bool {
match self {
Self::AddAssign(..) => true,
Self::SubAssign(..) => true,
Self::MulAssign(..) => true,
Self::DivAssign(..) => true,
Self::RemAssign(..) => true,
Self::BitAndAssign(..) => true,
Self::BitXorAssign(..) => true,
Self::BitOrAssign(..) => true,
Self::ShlAssign(..) => true,
Self::ShrAssign(..) => true,
_ => false,
}
}
pub(crate) fn is_conditional(self) -> bool {
match self {
Self::And(..) => true,
Self::Or(..) => true,
_ => false,
}
}
pub(crate) fn precedence(&self) -> usize {
match self {
Self::Is(..) | Self::IsNot(..) => 13,
Self::As(..) => 13,
Self::Mul(..) | Self::Div(..) | Self::Rem(..) => 11,
Self::Add(..) | Self::Sub(..) => 10,
Self::Shl(..) | Self::Shr(..) => 9,
Self::BitAnd(..) => 8,
Self::BitXor(..) => 7,
Self::BitOr(..) => 6,
Self::Eq(..)
| Self::Neq(..)
| Self::Lt(..)
| Self::Gt(..)
| Self::Lte(..)
| Self::Gte(..) => 5,
Self::And(..) => 4,
Self::Or(..) => 3,
Self::DotDot(..) | Self::DotDotEq(..) => 2,
_ => 1,
}
}
pub(crate) fn is_assoc(&self) -> bool {
match self {
Self::Mul(..) => true,
Self::Div(..) => true,
Self::Add(..) => true,
Self::Sub(..) => true,
Self::Or(..) => true,
Self::And(..) => true,
Self::Rem(..) => true,
Self::Shl(..) => true,
Self::Shr(..) => true,
Self::BitAnd(..) => true,
Self::BitOr(..) => true,
Self::BitXor(..) => true,
_ => false,
}
}
pub(crate) fn from_slice_with_range(out: &[ast::Token]) -> Option<Self> {
Self::from_slice_with(out, true)
}
pub(crate) fn from_slice(out: &[ast::Token]) -> Option<Self> {
Self::from_slice_with(out, false)
}
fn from_slice_with(out: &[ast::Token], range: bool) -> Option<Self> {
let &ast::Token { span, kind } = out.first()?;
let out = match kind {
K![+] => Self::Add(ast::Plus { span }),
K![-] => Self::Sub(ast::Dash { span }),
K![*] => Self::Mul(ast::Star { span }),
K![/] => Self::Div(ast::Div { span }),
K![%] => Self::Rem(ast::Perc { span }),
K![==] => Self::Eq(ast::EqEq { span }),
K![!=] => Self::Neq(ast::BangEq { span }),
K![<] => Self::Lt(ast::Lt { span }),
K![>] => Self::Gt(ast::Gt { span }),
K![<=] => Self::Lte(ast::LtEq { span }),
K![>=] => Self::Gte(ast::GtEq { span }),
K![as] => Self::As(ast::As { span }),
K![is] => {
let is = ast::Is { span };
if let Some(&ast::Token {
kind: K![not],
span,
}) = out.get(1)
{
Self::IsNot(ast::IsNot {
is,
not: ast::Not { span },
})
} else {
Self::Is(is)
}
}
K![&&] => Self::And(ast::AmpAmp { span }),
K![||] => Self::Or(ast::PipePipe { span }),
K![<<] => Self::Shl(ast::LtLt { span }),
K![>>] => Self::Shr(ast::GtGt { span }),
K![&] => Self::BitAnd(ast::Amp { span }),
K![^] => Self::BitXor(ast::Caret { span }),
K![|] => Self::BitOr(ast::Pipe { span }),
K![+=] => Self::AddAssign(ast::PlusEq { span }),
K![-=] => Self::SubAssign(ast::DashEq { span }),
K![*=] => Self::MulAssign(ast::StarEq { span }),
K![/=] => Self::DivAssign(ast::SlashEq { span }),
K![%=] => Self::RemAssign(ast::PercEq { span }),
K![&=] => Self::BitAndAssign(ast::AmpEq { span }),
K![^=] => Self::BitXorAssign(ast::CaretEq { span }),
K![|=] => Self::BitOrAssign(ast::PipeEq { span }),
K![<<=] => Self::ShlAssign(ast::LtLtEq { span }),
K![>>=] => Self::ShrAssign(ast::GtGtEq { span }),
K![..] if range => Self::DotDot(ast::DotDot { span }),
K![..=] if range => Self::DotDotEq(ast::DotDotEq { span }),
_ => return None,
};
Some(out)
}
pub(crate) fn from_peeker(p: &mut Peeker<'_>) -> Option<Self> {
let array = p.array::<2>();
Self::from_slice_with_range(&array)
}
pub(crate) fn advance<A>(&self, p: &mut A) -> Result<(), A::Error>
where
A: ?Sized + Advance,
{
match self {
Self::IsNot(..) => {
p.advance(2)?;
}
_ => {
p.advance(1)?;
}
}
Ok(())
}
}
impl fmt::Display for BinOp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Self::Add(..) => write!(f, "+"),
Self::Sub(..) => write!(f, "-"),
Self::Div(..) => write!(f, "/"),
Self::Mul(..) => write!(f, "*"),
Self::Rem(..) => write!(f, "%"),
Self::Eq(..) => write!(f, "=="),
Self::Neq(..) => write!(f, "!="),
Self::Gt(..) => write!(f, ">"),
Self::Lt(..) => write!(f, "<"),
Self::Gte(..) => write!(f, ">="),
Self::Lte(..) => write!(f, "<="),
Self::As(..) => write!(f, "as"),
Self::Is(..) => write!(f, "is"),
Self::IsNot(..) => write!(f, "is not"),
Self::And(..) => write!(f, "&&"),
Self::Or(..) => write!(f, "||"),
Self::Shl(..) => write!(f, "<<"),
Self::Shr(..) => write!(f, ">>"),
Self::BitAnd(..) => write!(f, "&"),
Self::BitXor(..) => write!(f, "^"),
Self::BitOr(..) => write!(f, "|"),
Self::AddAssign(..) => write!(f, "+="),
Self::SubAssign(..) => write!(f, "-="),
Self::DivAssign(..) => write!(f, "/="),
Self::MulAssign(..) => write!(f, "*="),
Self::BitAndAssign(..) => write!(f, "&="),
Self::BitXorAssign(..) => write!(f, "^="),
Self::BitOrAssign(..) => write!(f, "|="),
Self::RemAssign(..) => write!(f, "%="),
Self::ShlAssign(..) => write!(f, "<<="),
Self::ShrAssign(..) => write!(f, ">>="),
Self::DotDot(..) => write!(f, ".."),
Self::DotDotEq(..) => write!(f, "..="),
}
}
}
impl Peek for BinOp {
fn peek(p: &mut Peeker<'_>) -> bool {
let slice = p.array::<2>();
Self::from_slice_with_range(&slice).is_some()
}
}