use crate::ast;
use crate::{ParseError, Parser, Peek, Peeker, Spanned, ToTokens};
use runestick::Span;
use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq, ToTokens, Spanned)]
pub struct ExprBinary {
#[rune(iter)]
pub attributes: Vec<ast::Attribute>,
pub lhs: ast::Expr,
pub t1: ast::Token,
pub t2: Option<ast::Token>,
pub rhs: ast::Expr,
#[rune(skip)]
pub op: BinOp,
}
impl ExprBinary {
pub fn op_span(&self) -> Span {
if let Some(t2) = self.t2 {
self.t1.span().join(t2.span())
} else {
self.t1.span()
}
}
}
expr_parse!(Binary, ExprBinary, "binary expression");
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum BinOp {
Add,
Sub,
Div,
Mul,
Rem,
Eq,
Neq,
Gt,
Lt,
Gte,
Lte,
Is,
IsNot,
And,
Or,
Shl,
Shr,
BitAnd,
BitXor,
BitOr,
AddAssign,
SubAssign,
MulAssign,
DivAssign,
RemAssign,
BitAndAssign,
BitXorAssign,
BitOrAssign,
ShlAssign,
ShrAssign,
DotDot,
DotDotEq,
}
impl BinOp {
pub 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 fn is_conditional(self) -> bool {
match self {
Self::And => true,
Self::Or => true,
_ => false,
}
}
pub(super) fn precedence(self) -> usize {
match self {
Self::Is | Self::IsNot => 12,
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(super) 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(super) fn from_peeker(p: &mut Peeker<'_>) -> Option<BinOp> {
Some(match p.nth(0) {
K![+] => Self::Add,
K![-] => Self::Sub,
K![*] => Self::Mul,
K![/] => Self::Div,
K![%] => Self::Rem,
K![==] => Self::Eq,
K![!=] => Self::Neq,
K![<] => Self::Lt,
K![>] => Self::Gt,
K![<=] => Self::Lte,
K![>=] => Self::Gte,
ast::Kind::Is => match p.nth(1) {
K![not] => Self::IsNot,
_ => Self::Is,
},
K![&&] => Self::And,
K![||] => Self::Or,
K![<<] => Self::Shl,
K![>>] => Self::Shr,
K![&] => Self::BitAnd,
K![^] => Self::BitXor,
K![|] => Self::BitOr,
K![+=] => Self::AddAssign,
K![-=] => Self::SubAssign,
K![*=] => Self::MulAssign,
K![/=] => Self::DivAssign,
K![%=] => Self::RemAssign,
K![&=] => Self::BitAndAssign,
K![^=] => Self::BitXorAssign,
K![|=] => Self::BitOrAssign,
K![<<=] => Self::ShlAssign,
K![>>=] => Self::ShrAssign,
K![..] => Self::DotDot,
K![..=] => Self::DotDotEq,
_ => return None,
})
}
pub(crate) fn advance(
&self,
p: &mut Parser<'_>,
) -> Result<(ast::Token, Option<ast::Token>), ParseError> {
Ok(match self {
Self::IsNot => (p.next()?, Some(p.next()?)),
_ => (p.next()?, None),
})
}
}
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::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 {
Self::from_peeker(p).is_some()
}
}