gbf_core/decompiler/ast/
bin_op.rs#![deny(missing_docs)]
use gbf_macros::AstNodeTransform;
use serde::{Deserialize, Serialize};
use super::AstKind;
use super::{expr::ExprKind, AstNodeError};
use crate::decompiler::ast::literal::LiteralNode;
use crate::decompiler::ast::AstVisitable;
use crate::define_ast_enum_type;
define_ast_enum_type! {
BinOpType {
Add => "+",
Sub => "-",
Mul => "*",
Div => "/",
Mod => "%",
And => "&",
Or => "|",
Xor => "xor",
LogicalAnd => "&&",
LogicalOr => "||",
Equal => "==",
NotEqual => "!=",
Greater => ">",
Less => "<",
GreaterOrEqual => ">=",
LessOrEqual => "<=",
ShiftLeft => "<<",
ShiftRight => ">>",
In => "in",
Join => "@",
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Eq, AstNodeTransform)]
#[convert_to(ExprKind::BinOp, AstKind::Expression)]
pub struct BinaryOperationNode {
pub lhs: Box<ExprKind>,
pub rhs: Box<ExprKind>,
pub op_type: BinOpType,
}
impl BinaryOperationNode {
pub fn new(
lhs: Box<ExprKind>,
rhs: Box<ExprKind>,
op_type: BinOpType,
) -> Result<Self, AstNodeError> {
Self::validate_operand(&lhs)?;
Self::validate_operand(&rhs)?;
Ok(Self { lhs, rhs, op_type })
}
fn validate_operand(expr: &ExprKind) -> Result<(), AstNodeError> {
if let ExprKind::Literal(LiteralNode::String(_)) = expr {
return Err(AstNodeError::InvalidOperand(
"BinaryOperationNode".to_string(),
"Unsupported operand type".to_string(),
vec!["LiteralNode".to_string()],
format!("{:?}", expr),
));
}
Ok(())
}
}
impl PartialEq for BinaryOperationNode {
fn eq(&self, other: &Self) -> bool {
self.lhs == other.lhs && self.rhs == other.rhs && self.op_type == other.op_type
}
}
impl AstVisitable for BinaryOperationNode {
fn accept(&self, visitor: &mut dyn super::visitors::AstVisitor) {
visitor.visit_bin_op(self);
}
}
#[cfg(test)]
mod tests {
use crate::decompiler::ast::{emit, new_bin_op, new_id, new_str};
use super::*;
#[test]
fn test_bin_op_emit() -> Result<(), AstNodeError> {
for op_type in BinOpType::all_variants() {
let expr = new_bin_op(new_id("a"), new_id("b"), op_type.clone())?;
assert_eq!(emit(expr), format!("a {} b", op_type.as_str()));
}
Ok(())
}
#[test]
fn test_nested_bin_op_emit() -> Result<(), AstNodeError> {
let expr = new_bin_op(
new_bin_op(new_id("a"), new_id("b"), BinOpType::Add)?,
new_id("c"),
BinOpType::Mul,
)?;
assert_eq!(emit(expr), "(a + b) * c");
Ok(())
}
#[test]
fn test_bin_op_eq() -> Result<(), AstNodeError> {
let a = new_bin_op(new_id("a"), new_id("b"), BinOpType::Add)?;
let b = new_bin_op(new_id("a"), new_id("b"), BinOpType::Add)?;
let c = new_bin_op(new_id("a"), new_id("b"), BinOpType::Sub)?;
let d = new_bin_op(new_id("a"), new_id("c"), BinOpType::Add)?;
assert_eq!(a, b);
assert_ne!(a, c);
assert_ne!(a, d);
Ok(())
}
#[test]
fn test_bin_op_validate_operand() -> Result<(), AstNodeError> {
let a = new_bin_op(new_id("a"), new_id("b"), BinOpType::Add);
let b = new_bin_op(new_id("a"), new_str("string"), BinOpType::Add);
let c = new_bin_op(new_str("string"), new_id("b"), BinOpType::Add);
assert!(a.is_ok());
assert!(b.is_err());
assert!(c.is_err());
Ok(())
}
}