gbf_core/decompiler/ast/visitors/
emit_context.rs

1#![deny(missing_docs)]
2
3use thiserror::Error;
4
5/// Represents an error that occurred while converting an AST node.
6#[derive(Debug, Error)]
7pub enum EmitError {}
8
9/// Represents the verbosity mode in which the AST should be emitted.
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum EmitVerbosity {
12    /// Emit the AST in a format that is readable by humans.
13    Pretty,
14    /// Emit the AST in a format that is readable by the parser. This format ensures that no comments or extra whitespace is emitted.
15    Minified,
16    /// Debug mode, which emits the AST in a format that is useful for debugging.
17    Debug,
18}
19
20/// Represents the formatting indentation style for blocks of code.
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22pub enum IndentStyle {
23    /// Allman style indentation.
24    Allman,
25    /// K&R style indentation.
26    KAndR,
27}
28
29/// Contains the emitting context for the AST.
30#[derive(Debug, Clone, Copy)]
31pub struct EmitContext {
32    /// The current indentation level.
33    pub indent: usize,
34    /// The number of spaces to indent by.
35    pub indent_step: usize,
36    /// Whether to format numbers in hexadecimal.
37    pub format_number_hex: bool,
38    /// The mode in which to emit the AST.
39    pub verbosity: EmitVerbosity,
40    /// The style of indentation to use.
41    pub indent_style: IndentStyle,
42    /// The root of the expression tree.
43    pub expr_root: bool,
44    /// If we should include SSA versions in the emitted code.
45    pub include_ssa_versions: bool,
46}
47
48impl EmitContext {
49    /// Allow temporarily changing the EmitContext for a block of code.
50    ///
51    /// # Arguments
52    /// - `f` - The function to call with the new EmitContext.
53    ///
54    /// # Returns
55    /// The EmitContext after the function has been called.
56    ///
57    /// # Example
58    /// ```
59    /// use gbf_core::decompiler::ast::visitors::emit_context::EmitContext;
60    ///
61    /// let mut context = EmitContext::default();
62    /// let body_context = context.scoped(|ctx| ctx.with_indent());
63    /// ```
64    pub fn scoped<F>(&mut self, action: F) -> Self
65    where
66        F: FnOnce(&mut Self) -> EmitContext,
67    {
68        action(self)
69    }
70
71    /// Returns a new EmitContext with the indent increased by the indent step.
72    ///
73    /// # Returns
74    /// A new EmitContext with the indent increased by the indent step.
75    ///
76    /// # Example
77    /// ```
78    /// use gbf_core::decompiler::ast::visitors::emit_context::EmitContext;
79    ///
80    /// let mut context = EmitContext::default();
81    /// let body_context = context.with_indent();
82    /// ```
83    pub fn with_indent(&self) -> EmitContext {
84        let mut new_context = *self;
85        new_context.indent += self.indent_step;
86        new_context
87    }
88
89    /// Returns a new EmitContext with expr_root set to the given value.
90    ///
91    /// # Arguments
92    /// - `expr_root` - The value to set expr_root to.
93    ///
94    /// # Returns
95    /// A new EmitContext with expr_root set to the given value.
96    ///
97    /// # Example
98    /// ```
99    /// use gbf_core::decompiler::ast::visitors::emit_context::EmitContext;
100    ///
101    /// let mut context = EmitContext::default();
102    /// let body_context = context.with_expr_root(true);
103    /// ```
104    pub fn with_expr_root(&self, expr_root: bool) -> EmitContext {
105        let mut new_context = *self;
106        new_context.expr_root = expr_root;
107        new_context
108    }
109
110    /// Creates a builder for `EmitContext`.
111    ///
112    /// # Returns
113    /// A new instance of `EmitContextBuilder`.
114    pub fn builder() -> EmitContextBuilder {
115        EmitContextBuilder::default()
116    }
117}
118
119/// Builder for `EmitContext` to provide a fluent API for customization.
120#[derive(Debug, Clone)]
121pub struct EmitContextBuilder {
122    indent: usize,
123    indent_step: usize,
124    format_number_hex: bool,
125    verbosity: EmitVerbosity,
126    indent_style: IndentStyle,
127    expr_root: bool,
128    include_ssa_versions: bool,
129}
130
131impl EmitContextBuilder {
132    /// Sets the initial indentation level.
133    pub fn indent(mut self, indent: usize) -> Self {
134        self.indent = indent;
135        self
136    }
137
138    /// Sets the number of spaces per indentation step.
139    pub fn indent_step(mut self, indent_step: usize) -> Self {
140        self.indent_step = indent_step;
141        self
142    }
143
144    /// Configures whether to format numbers in hexadecimal.
145    pub fn format_number_hex(mut self, format_number_hex: bool) -> Self {
146        self.format_number_hex = format_number_hex;
147        self
148    }
149
150    /// Sets the verbosity mode.
151    pub fn verbosity(mut self, verbosity: EmitVerbosity) -> Self {
152        self.verbosity = verbosity;
153        self
154    }
155
156    /// Sets the indentation style.
157    pub fn indent_style(mut self, indent_style: IndentStyle) -> Self {
158        self.indent_style = indent_style;
159        self
160    }
161
162    /// Sets the `expr_root` flag.
163    pub fn expr_root(mut self, expr_root: bool) -> Self {
164        self.expr_root = expr_root;
165        self
166    }
167
168    /// Sets the `include_ssa_versions` flag.
169    pub fn include_ssa_versions(mut self, include_ssa_versions: bool) -> Self {
170        self.include_ssa_versions = include_ssa_versions;
171        self
172    }
173
174    /// Builds the `EmitContext` with the specified parameters.
175    pub fn build(self) -> EmitContext {
176        EmitContext {
177            indent: self.indent,
178            indent_step: self.indent_step,
179            format_number_hex: self.format_number_hex,
180            verbosity: self.verbosity,
181            indent_style: self.indent_style,
182            expr_root: self.expr_root,
183            include_ssa_versions: self.include_ssa_versions,
184        }
185    }
186}
187
188// == Other Implementations for EmitContext ==
189
190impl Default for EmitContextBuilder {
191    fn default() -> Self {
192        Self {
193            indent: 0,
194            indent_step: 4,
195            format_number_hex: false,
196            verbosity: EmitVerbosity::Pretty,
197            indent_style: IndentStyle::Allman,
198            expr_root: true,
199            include_ssa_versions: false,
200        }
201    }
202}
203
204impl Default for EmitContext {
205    fn default() -> Self {
206        EmitContextBuilder::default().build()
207    }
208}
209
210#[cfg(test)]
211mod tests {
212    use super::*;
213
214    #[test]
215    fn test_emit_context_with_indent() {
216        let context = EmitContext::default();
217        let new_context = context.with_indent();
218        assert_eq!(new_context.indent, 4);
219    }
220
221    #[test]
222    fn test_emit_context_scoped() {
223        let mut context = EmitContext::default();
224        let body_context = context.scoped(|ctx| ctx.with_indent());
225        assert_eq!(body_context.indent, 4);
226    }
227
228    #[test]
229    fn test_builder_default_values() {
230        let builder = EmitContext::builder();
231        let context = builder.build();
232        assert_eq!(context.indent, 0);
233        assert_eq!(context.indent_step, 4);
234        assert!(!context.format_number_hex);
235        assert_eq!(context.verbosity, EmitVerbosity::Pretty);
236        assert_eq!(context.indent_style, IndentStyle::Allman);
237    }
238
239    #[test]
240    fn test_builder_custom_values() {
241        let context = EmitContext::builder()
242            .indent(2)
243            .indent_step(8)
244            .format_number_hex(true)
245            .verbosity(EmitVerbosity::Debug)
246            .indent_style(IndentStyle::KAndR)
247            .expr_root(true)
248            .include_ssa_versions(true)
249            .build();
250        assert_eq!(context.indent, 2);
251        assert_eq!(context.indent_step, 8);
252        assert!(context.format_number_hex);
253        assert_eq!(context.verbosity, EmitVerbosity::Debug);
254        assert_eq!(context.indent_style, IndentStyle::KAndR);
255        assert!(context.expr_root);
256        assert!(context.include_ssa_versions);
257    }
258}