gbf_core/decompiler/ast/
assignment.rs

1#![deny(missing_docs)]
2
3use gbf_macros::AstNodeTransform;
4use serde::{Deserialize, Serialize};
5
6use super::{
7    AstKind, AstNodeError, expr::ExprKind, ptr::P, statement::StatementKind, visitors::AstVisitor,
8};
9use crate::decompiler::ast::AstVisitable;
10
11/// Represents a statement node in the AST, such as `variable = value`.
12#[derive(Debug, Clone, Serialize, Deserialize, Eq, AstNodeTransform)]
13#[convert_to(StatementKind::Assignment, AstKind::Statement)]
14pub struct AssignmentNode {
15    /// The left-hand side of the statement, usually a variable.
16    pub lhs: ExprKind,
17    /// The right-hand side of the statement, the value to assign.
18    pub rhs: ExprKind,
19}
20
21impl AssignmentNode {
22    /// Creates a new `StatementNode` after validating `lhs` and `rhs` types.
23    ///
24    /// # Arguments
25    /// - `lhs` - The left-hand side of the statement.
26    /// - `rhs` - The right-hand side of the statement.
27    ///
28    /// # Returns
29    /// A new `StatementNode`.
30    ///
31    /// # Errors
32    /// Returns an `AstNodeError` if `lhs` or `rhs` is of an unsupported type.
33    pub fn new(lhs: ExprKind, rhs: ExprKind) -> Result<Self, AstNodeError> {
34        Ok(Self { lhs, rhs })
35    }
36}
37
38impl AstVisitable for P<AssignmentNode> {
39    /// Accepts the visitor and calls the appropriate visit method.
40    fn accept<V: AstVisitor>(&self, visitor: &mut V) -> V::Output {
41        visitor.visit_assignment(self)
42    }
43}
44
45// == Other implementations for statement ==
46impl PartialEq for AssignmentNode {
47    fn eq(&self, other: &Self) -> bool {
48        self.lhs == other.lhs && self.rhs == other.rhs
49    }
50}
51
52#[cfg(test)]
53mod tests {
54    use crate::decompiler::ast::{
55        AstNodeError, emit, new_assignment, new_id, new_member_access, new_str,
56    };
57
58    #[test]
59    fn test_statement_emit() -> Result<(), AstNodeError> {
60        let stmt = new_assignment(new_id("test1"), new_id("test2"));
61        // this becomes a statement because of the `into`, so it should end with a semicolon
62        assert_eq!(emit(stmt), "test1 = test2;");
63
64        // player.chat = "Hello, world!";
65        let stmt = new_assignment(
66            new_member_access(new_id("player"), new_id("chat"))?,
67            new_str("Hello, world!"),
68        );
69        assert_eq!(emit(stmt), "player.chat = \"Hello, world!\";");
70        Ok(())
71    }
72}