use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum BinOp {
Add,
Sub,
Mul,
Div,
Rem,
Pow,
BitAnd,
BitOr,
BitXor,
Shl,
Shr,
And,
Or,
Eq,
Ne,
Lt,
Le,
Gt,
Ge,
Range,
RangeInclusive,
Pipe,
Compose,
}
impl BinOp {
pub fn precedence(&self) -> u8 {
match self {
BinOp::Range | BinOp::RangeInclusive => 1,
BinOp::Or => 2,
BinOp::And => 3,
BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge => 4,
BinOp::BitOr => 5,
BinOp::BitXor => 6,
BinOp::BitAnd => 7,
BinOp::Shl | BinOp::Shr => 8,
BinOp::Pipe | BinOp::Compose => 9,
BinOp::Add | BinOp::Sub => 10,
BinOp::Mul | BinOp::Div | BinOp::Rem => 11,
BinOp::Pow => 12,
}
}
pub fn associativity(&self) -> Associativity {
match self {
BinOp::Pow => Associativity::Right,
BinOp::Pipe | BinOp::Compose => Associativity::Left,
_ => Associativity::Left,
}
}
pub fn binding_power(&self) -> (u8, u8) {
let prec = self.precedence() * 2;
match self.associativity() {
Associativity::Left => (prec, prec + 1),
Associativity::Right => (prec + 1, prec),
Associativity::None => (prec, prec),
}
}
pub fn as_str(&self) -> &'static str {
match self {
BinOp::Add => "+",
BinOp::Sub => "-",
BinOp::Mul => "*",
BinOp::Div => "/",
BinOp::Rem => "%",
BinOp::Pow => "**",
BinOp::BitAnd => "&",
BinOp::BitOr => "|",
BinOp::BitXor => "^",
BinOp::Shl => "<<",
BinOp::Shr => ">>",
BinOp::And => "&&",
BinOp::Or => "||",
BinOp::Eq => "==",
BinOp::Ne => "!=",
BinOp::Lt => "<",
BinOp::Le => "<=",
BinOp::Gt => ">",
BinOp::Ge => ">=",
BinOp::Range => "..",
BinOp::RangeInclusive => "..=",
BinOp::Pipe => "|>",
BinOp::Compose => ">>",
}
}
pub fn is_comparison(&self) -> bool {
matches!(
self,
BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge
)
}
pub fn is_logical(&self) -> bool {
matches!(self, BinOp::And | BinOp::Or)
}
pub fn is_arithmetic(&self) -> bool {
matches!(
self,
BinOp::Add | BinOp::Sub | BinOp::Mul | BinOp::Div | BinOp::Rem | BinOp::Pow
)
}
pub fn is_bitwise(&self) -> bool {
matches!(
self,
BinOp::BitAnd | BinOp::BitOr | BinOp::BitXor | BinOp::Shl | BinOp::Shr
)
}
}
impl fmt::Display for BinOp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.as_str())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum AssignOp {
Assign,
AddAssign,
SubAssign,
MulAssign,
DivAssign,
RemAssign,
BitAndAssign,
BitOrAssign,
BitXorAssign,
ShlAssign,
ShrAssign,
}
impl AssignOp {
pub fn to_bin_op(&self) -> Option<BinOp> {
match self {
AssignOp::Assign => None,
AssignOp::AddAssign => Some(BinOp::Add),
AssignOp::SubAssign => Some(BinOp::Sub),
AssignOp::MulAssign => Some(BinOp::Mul),
AssignOp::DivAssign => Some(BinOp::Div),
AssignOp::RemAssign => Some(BinOp::Rem),
AssignOp::BitAndAssign => Some(BinOp::BitAnd),
AssignOp::BitOrAssign => Some(BinOp::BitOr),
AssignOp::BitXorAssign => Some(BinOp::BitXor),
AssignOp::ShlAssign => Some(BinOp::Shl),
AssignOp::ShrAssign => Some(BinOp::Shr),
}
}
pub fn as_str(&self) -> &'static str {
match self {
AssignOp::Assign => "=",
AssignOp::AddAssign => "+=",
AssignOp::SubAssign => "-=",
AssignOp::MulAssign => "*=",
AssignOp::DivAssign => "/=",
AssignOp::RemAssign => "%=",
AssignOp::BitAndAssign => "&=",
AssignOp::BitOrAssign => "|=",
AssignOp::BitXorAssign => "^=",
AssignOp::ShlAssign => "<<=",
AssignOp::ShrAssign => ">>=",
}
}
}
impl fmt::Display for AssignOp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.as_str())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum UnaryOp {
Neg,
Not,
BitNot,
Deref,
Ref,
RefMut,
}
impl UnaryOp {
pub fn prefix_binding_power(&self) -> u8 {
25
}
pub fn as_str(&self) -> &'static str {
match self {
UnaryOp::Neg => "-",
UnaryOp::Not => "!",
UnaryOp::BitNot => "~",
UnaryOp::Deref => "*",
UnaryOp::Ref => "&",
UnaryOp::RefMut => "&mut",
}
}
}
impl fmt::Display for UnaryOp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.as_str())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Associativity {
Left,
Right,
None,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PostfixOp {
Call,
MethodCall,
Field,
Index,
Try,
Await,
}
impl PostfixOp {
pub fn binding_power(&self) -> u8 {
27 }
}
pub mod precedence {
pub const ASSIGN: u8 = 0;
pub const RANGE: u8 = 2;
pub const OR: u8 = 4;
pub const AND: u8 = 6;
pub const COMPARE: u8 = 8;
pub const BIT_OR: u8 = 10;
pub const BIT_XOR: u8 = 12;
pub const BIT_AND: u8 = 14;
pub const SHIFT: u8 = 16;
pub const PIPE: u8 = 18;
pub const SUM: u8 = 20;
pub const PRODUCT: u8 = 22;
pub const POWER: u8 = 24;
pub const PREFIX: u8 = 25;
pub const POSTFIX: u8 = 27;
pub const AS: u8 = 26;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_precedence_ordering() {
assert!(BinOp::Mul.precedence() > BinOp::Add.precedence());
assert!(BinOp::And.precedence() > BinOp::Or.precedence());
assert!(BinOp::Eq.precedence() > BinOp::Or.precedence());
assert!(BinOp::Eq.precedence() < BinOp::Add.precedence());
}
#[test]
fn test_binding_power() {
let (l, r) = BinOp::Add.binding_power();
assert!(l < r);
let (l, r) = BinOp::Pow.binding_power();
assert!(l > r);
}
#[test]
fn test_assign_op_conversion() {
assert_eq!(AssignOp::AddAssign.to_bin_op(), Some(BinOp::Add));
assert_eq!(AssignOp::Assign.to_bin_op(), None);
}
}