Skip to main content

oak_jasm/ast/
mod.rs

1//! Abstract Syntax Tree for the JASM language.
2
3#![doc = include_str!("readme.md")]
4use oak_core::source::{SourceBuffer, ToSource};
5#[cfg(feature = "oak-pretty-print")]
6use oak_pretty_print::{AsDocument, Document};
7#[cfg(feature = "serde")]
8use serde::{Deserialize, Serialize};
9use std::{string::String, vec::Vec};
10
11/// JASM root node.
12#[derive(Clone, Debug)]
13#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
14pub struct JasmRoot {
15    /// The class definition.
16    pub class: JasmClass,
17}
18
19impl ToSource for JasmRoot {
20    fn to_source(&self, buffer: &mut SourceBuffer) {
21        self.class.to_source(buffer)
22    }
23}
24
25#[cfg(feature = "oak-pretty-print")]
26impl AsDocument for JasmRoot {
27    fn as_document(&self) -> Document<'_> {
28        self.class.as_document()
29    }
30}
31
32/// AST node for a JASM class declaration.
33#[derive(Clone, Debug)]
34#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
35pub struct JasmClass {
36    /// Access modifiers (public, private, etc.).
37    pub modifiers: Vec<String>,
38    /// Class name.
39    pub name: String,
40    /// Version information (e.g., 65:0).
41    pub version: Option<String>,
42    /// List of methods.
43    pub methods: Vec<JasmMethod>,
44    /// List of fields.
45    pub fields: Vec<JasmField>,
46    /// Source file information.
47    pub source_file: Option<String>,
48}
49
50impl ToSource for JasmClass {
51    fn to_source(&self, buffer: &mut SourceBuffer) {
52        if let Some(source) = &self.source_file {
53            buffer.push(".source ");
54            buffer.push(source);
55            buffer.push("\n")
56        }
57        buffer.push(".class ");
58        for modifier in &self.modifiers {
59            buffer.push(modifier);
60            buffer.push(" ")
61        }
62        buffer.push(&self.name);
63        buffer.push("\n");
64        if let Some(version) = &self.version {
65            buffer.push(".version ");
66            buffer.push(version);
67            buffer.push("\n")
68        }
69        buffer.push("\n");
70        for field in &self.fields {
71            field.to_source(buffer);
72            buffer.push("\n")
73        }
74        for method in &self.methods {
75            method.to_source(buffer);
76            buffer.push("\n")
77        }
78    }
79}
80
81#[cfg(feature = "oak-pretty-print")]
82impl AsDocument for JasmClass {
83    fn as_document(&self) -> Document<'_> {
84        let mut docs = Vec::new();
85        if let Some(source) = &self.source_file {
86            docs.push(Document::Text(format!(".source {}\n", source).into()))
87        }
88        let mut class_line = vec![Document::Text(".class ".into())];
89        for modifier in &self.modifiers {
90            class_line.push(Document::Text(modifier.clone().into()));
91            class_line.push(Document::Text(" ".into()))
92        }
93        class_line.push(Document::Text(self.name.clone().into()));
94        docs.push(Document::Concat(class_line));
95        docs.push(Document::Line);
96
97        if let Some(version) = &self.version {
98            docs.push(Document::Text(format!(".version {}\n", version).into()))
99        }
100        docs.push(Document::Line);
101
102        for field in &self.fields {
103            docs.push(field.as_document());
104            docs.push(Document::Line)
105        }
106        for method in &self.methods {
107            docs.push(method.as_document());
108            docs.push(Document::Line)
109        }
110        Document::Concat(docs)
111    }
112}
113
114/// AST node for a JASM method declaration.
115#[derive(Clone, Debug)]
116#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
117pub struct JasmMethod {
118    /// Access modifiers (public, static, etc.).
119    pub modifiers: Vec<String>,
120    /// Method name and type descriptor (e.g., "main":"([Ljava/lang/String)V").
121    pub name_and_descriptor: String,
122    /// Stack size.
123    pub stack_size: Option<u32>,
124    /// Number of local variables.
125    pub locals_count: Option<u32>,
126    /// List of instructions.
127    pub instructions: Vec<JasmInstruction>,
128}
129
130impl ToSource for JasmMethod {
131    fn to_source(&self, buffer: &mut SourceBuffer) {
132        buffer.push(".method ");
133        for modifier in &self.modifiers {
134            buffer.push(modifier);
135            buffer.push(" ")
136        }
137        buffer.push(&self.name_and_descriptor);
138        buffer.push("\n");
139        if let Some(stack) = self.stack_size {
140            buffer.push("    .limit stack ");
141            buffer.push(&stack.to_string());
142            buffer.push("\n")
143        }
144        if let Some(locals) = self.locals_count {
145            buffer.push("    .limit locals ");
146            buffer.push(&locals.to_string());
147            buffer.push("\n")
148        }
149        for inst in &self.instructions {
150            buffer.push("    ");
151            inst.to_source(buffer);
152            buffer.push("\n")
153        }
154        buffer.push(".end method")
155    }
156}
157
158#[cfg(feature = "oak-pretty-print")]
159impl AsDocument for JasmMethod {
160    fn as_document(&self) -> Document<'_> {
161        let mut docs = Vec::new();
162        let mut method_line = vec![Document::Text(".method ".into())];
163        for modifier in &self.modifiers {
164            method_line.push(Document::Text(modifier.clone().into()));
165            method_line.push(Document::Text(" ".into()))
166        }
167        method_line.push(Document::Text(self.name_and_descriptor.clone().into()));
168        docs.push(Document::Concat(method_line));
169        docs.push(Document::Line);
170
171        let mut body = Vec::new();
172        if let Some(stack) = self.stack_size {
173            body.push(Document::Text(format!(".limit stack {}\n", stack).into()))
174        }
175        if let Some(locals) = self.locals_count {
176            body.push(Document::Text(format!(".limit locals {}\n", locals).into()))
177        }
178        for inst in &self.instructions {
179            body.push(inst.as_document());
180            body.push(Document::Line)
181        }
182
183        docs.push(Document::indent(Document::Concat(body)));
184        docs.push(Document::Text(".end method".into()));
185        Document::Concat(docs)
186    }
187}
188
189/// AST node for a JASM field declaration.
190#[derive(Clone, Debug)]
191#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
192pub struct JasmField {
193    /// Access modifiers (public, static, etc.).
194    pub modifiers: Vec<String>,
195    /// Field name and type descriptor (e.g., "value":"I").
196    pub name_and_descriptor: String,
197}
198
199impl ToSource for JasmField {
200    fn to_source(&self, buffer: &mut SourceBuffer) {
201        buffer.push(".field ");
202        for modifier in &self.modifiers {
203            buffer.push(modifier);
204            buffer.push(" ")
205        }
206        buffer.push(&self.name_and_descriptor)
207    }
208}
209
210#[cfg(feature = "oak-pretty-print")]
211impl AsDocument for JasmField {
212    fn as_document(&self) -> Document<'_> {
213        let mut docs = vec![Document::Text(".field ".into())];
214        for modifier in &self.modifiers {
215            docs.push(Document::Text(modifier.clone().into()));
216            docs.push(Document::Text(" ".into()))
217        }
218        docs.push(Document::Text(self.name_and_descriptor.clone().into()));
219        Document::Concat(docs)
220    }
221}
222
223/// AST node for a JASM instruction.
224#[derive(Clone, Debug)]
225#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
226pub enum JasmInstruction {
227    /// Simple instruction (e.g., aload_0, return).
228    Simple(String),
229    /// Instruction with an argument (e.g., ldc "Hello").
230    WithArgument {
231        /// The instruction name.
232        instruction: String,
233        /// The instruction argument.
234        argument: String,
235    },
236    /// Method call instruction (e.g., invokespecial Method java/lang/Object."<init>":"()V").
237    MethodCall {
238        /// The instruction name.
239        instruction: String,
240        /// The method reference.
241        method_ref: String,
242    },
243    /// Field access instruction (e.g., getstatic Field java/lang/System.out:"Ljava/io/PrintStream;").
244    FieldAccess {
245        /// The instruction name.
246        instruction: String,
247        /// The field reference.
248        field_ref: String,
249    },
250}
251
252impl ToSource for JasmInstruction {
253    fn to_source(&self, buffer: &mut SourceBuffer) {
254        match self {
255            JasmInstruction::Simple(s) => buffer.push(s),
256            JasmInstruction::WithArgument { instruction, argument } => {
257                buffer.push(instruction);
258                buffer.push(" ");
259                buffer.push(argument)
260            }
261            JasmInstruction::MethodCall { instruction, method_ref } => {
262                buffer.push(instruction);
263                buffer.push(" ");
264                buffer.push(method_ref)
265            }
266            JasmInstruction::FieldAccess { instruction, field_ref } => {
267                buffer.push(instruction);
268                buffer.push(" ");
269                buffer.push(field_ref)
270            }
271        }
272    }
273}
274
275#[cfg(feature = "oak-pretty-print")]
276impl AsDocument for JasmInstruction {
277    fn as_document(&self) -> Document<'_> {
278        match self {
279            JasmInstruction::Simple(s) => Document::Text(s.clone().into()),
280            JasmInstruction::WithArgument { instruction, argument } => Document::Concat(vec![Document::Text(instruction.clone().into()), Document::Text(" ".into()), Document::Text(argument.clone().into())]),
281            JasmInstruction::MethodCall { instruction, method_ref } => Document::Concat(vec![Document::Text(instruction.clone().into()), Document::Text(" ".into()), Document::Text(method_ref.clone().into())]),
282            JasmInstruction::FieldAccess { instruction, field_ref } => Document::Concat(vec![Document::Text(instruction.clone().into()), Document::Text(" ".into()), Document::Text(field_ref.clone().into())]),
283        }
284    }
285}