use alloc::vec::Vec;
use core::fmt;
use crate::entity::{Block, Value};
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum BinOp {
Add,
Sub,
Mul,
Div,
Eq,
Ne,
Lt,
Le,
Gt,
Ge,
And,
Or,
}
impl BinOp {
#[must_use]
pub const fn is_comparison(self) -> bool {
matches!(
self,
BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge
)
}
#[must_use]
pub const fn is_logical(self) -> bool {
matches!(self, BinOp::And | BinOp::Or)
}
}
impl fmt::Display for BinOp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let name = match self {
BinOp::Add => "add",
BinOp::Sub => "sub",
BinOp::Mul => "mul",
BinOp::Div => "div",
BinOp::Eq => "eq",
BinOp::Ne => "ne",
BinOp::Lt => "lt",
BinOp::Le => "le",
BinOp::Gt => "gt",
BinOp::Ge => "ge",
BinOp::And => "and",
BinOp::Or => "or",
};
f.write_str(name)
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum UnOp {
Neg,
Not,
}
impl fmt::Display for UnOp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let name = match self {
UnOp::Neg => "neg",
UnOp::Not => "not",
};
f.write_str(name)
}
}
#[derive(Clone, PartialEq, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Inst {
Iconst(i64),
Fconst(f64),
Bconst(bool),
Bin(BinOp, Value, Value),
Un(UnOp, Value),
}
#[derive(Clone, PartialEq, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Terminator {
Return(Option<Value>),
Jump(Block, Vec<Value>),
Branch {
cond: Value,
then_block: Block,
then_args: Vec<Value>,
else_block: Block,
else_args: Vec<Value>,
},
}
impl Terminator {
pub fn each_successor(&self, mut f: impl FnMut(Block)) {
match self {
Terminator::Return(_) => {}
Terminator::Jump(target, _) => f(*target),
Terminator::Branch {
then_block,
else_block,
..
} => {
f(*then_block);
f(*else_block);
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::vec;
#[test]
fn test_binop_classification_partitions_operations() {
for op in [BinOp::Add, BinOp::Sub, BinOp::Mul, BinOp::Div] {
assert!(!op.is_comparison() && !op.is_logical());
}
for op in [
BinOp::Eq,
BinOp::Ne,
BinOp::Lt,
BinOp::Le,
BinOp::Gt,
BinOp::Ge,
] {
assert!(op.is_comparison() && !op.is_logical());
}
for op in [BinOp::And, BinOp::Or] {
assert!(op.is_logical() && !op.is_comparison());
}
}
#[test]
fn test_each_successor_reports_targets_in_order() {
let mut got = Vec::new();
Terminator::Return(None).each_successor(|b| got.push(b));
assert!(got.is_empty());
let mut got = Vec::new();
Terminator::Jump(Block::from_raw(2), vec![]).each_successor(|b| got.push(b));
assert_eq!(got, vec![Block::from_raw(2)]);
let mut got = Vec::new();
Terminator::Branch {
cond: Value::from_raw(0),
then_block: Block::from_raw(1),
then_args: vec![],
else_block: Block::from_raw(2),
else_args: vec![],
}
.each_successor(|b| got.push(b));
assert_eq!(got, vec![Block::from_raw(1), Block::from_raw(2)]);
}
}