gbf_core/decompiler/ast/
unary_op.rs1#![deny(missing_docs)]
2
3use gbf_macros::AstNodeTransform;
4use serde::{Deserialize, Serialize};
5
6use crate::define_ast_enum_type;
7
8use super::{
9 AstKind, AstNodeError, AstVisitable, expr::ExprKind, literal::LiteralNode, ptr::P,
10 visitors::AstVisitor,
11};
12
13define_ast_enum_type!(
14 UnaryOpType {
15 LogicalNot => "!",
16 BitwiseNot => "~",
17 Negate => "-",
18 }
19);
20
21#[derive(Debug, Clone, Serialize, Deserialize, Eq, AstNodeTransform)]
23#[convert_to(ExprKind::UnaryOp, AstKind::Expression)]
24pub struct UnaryOperationNode {
25 pub operand: ExprKind,
27 pub op_type: UnaryOpType,
29}
30
31impl UnaryOperationNode {
32 pub fn new(operand: ExprKind, op_type: UnaryOpType) -> Result<Self, AstNodeError> {
44 Self::validate_operand(&operand)?;
45
46 Ok(Self { operand, op_type })
47 }
48
49 fn validate_operand(expr: &ExprKind) -> Result<(), AstNodeError> {
50 if let ExprKind::Literal(lit) = expr {
52 if let LiteralNode::String(_) = lit.as_ref() {
53 return Err(AstNodeError::InvalidOperand);
54 }
55 }
56 Ok(())
57 }
58}
59
60impl AstVisitable for P<UnaryOperationNode> {
61 fn accept<V: AstVisitor>(&self, visitor: &mut V) -> V::Output {
62 visitor.visit_unary_op(self)
63 }
64}
65
66impl PartialEq for UnaryOperationNode {
68 fn eq(&self, other: &Self) -> bool {
69 self.operand == other.operand && self.op_type == other.op_type
70 }
71}
72
73#[cfg(test)]
74mod tests {
75 use crate::decompiler::ast::{
76 AstNodeError, bin_op::BinOpType, emit, new_bin_op, new_id, new_str, new_unary_op,
77 };
78
79 use super::UnaryOpType;
80
81 #[test]
82 fn test_unary_op_emit() -> Result<(), AstNodeError> {
83 for op_type in UnaryOpType::all_variants() {
84 let expr = new_unary_op(new_id("a"), op_type.clone())?;
85 assert_eq!(emit(expr), format!("{}a", op_type.as_str()));
86 }
87 Ok(())
88 }
89
90 #[test]
91 fn test_nested_unary_op_emit() -> Result<(), AstNodeError> {
92 for op_type in UnaryOpType::all_variants() {
93 let expr = new_unary_op(new_unary_op(new_id("a"), op_type.clone())?, op_type.clone())?;
94 assert_eq!(
95 emit(expr),
96 format!("{}({}a)", op_type.as_str(), op_type.as_str())
97 );
98 }
99 Ok(())
100 }
101
102 #[test]
103 fn test_unary_op_binary_operand() -> Result<(), AstNodeError> {
104 let result = new_unary_op(
105 new_bin_op(new_id("a"), new_id("b"), BinOpType::Add)?,
106 UnaryOpType::Negate,
107 )?;
108
109 assert_eq!(emit(result), "-(a + b)");
110
111 Ok(())
112 }
113
114 #[test]
115 fn test_unary_op_invalid_operand() {
116 let result = new_unary_op(new_str("a"), UnaryOpType::Negate);
117 assert!(result.is_err());
118 }
119
120 #[test]
121 fn test_unary_op_equality() -> Result<(), AstNodeError> {
122 let unary1 = new_unary_op(new_id("a"), UnaryOpType::Negate)?;
123 let unary2 = new_unary_op(new_id("a"), UnaryOpType::Negate)?;
124 assert_eq!(unary1, unary2);
125
126 let unary3 = new_unary_op(new_id("b"), UnaryOpType::Negate)?;
127 assert_ne!(unary1, unary3);
128 Ok(())
129 }
130}