roblox_rs_core/ast/
transformer.rs

1//! AST transformation module.
2//!
3//! This module provides functionality for transforming a Rust AST into a Luau AST.
4
5use syn::{File, Item, ItemFn, ItemStruct, ItemEnum, ItemImpl, ImplItem};
6use thiserror::Error;
7
8use crate::luau::{LuauAst, LuauStmt, LuauExpr, LuauFunction, LuauTable};
9use crate::error::Result;
10
11/// Error that can occur during AST transformation.
12#[derive(Error, Debug)]
13pub enum TransformError {
14    #[error("unsupported feature: {0}")]
15    UnsupportedFeature(String),
16    
17    #[error("type error: {0}")]
18    Type(String),
19    
20    #[error("transformation error: {0}")]
21    Other(String),
22}
23
24impl From<TransformError> for crate::error::Error {
25    fn from(err: TransformError) -> Self {
26        crate::error::Error::Transform(err.to_string())
27    }
28}
29
30/// Transform a Rust AST into a Luau AST.
31pub fn transform_ast(ast: &File) -> Result<LuauAst> {
32    let mut luau_ast = LuauAst::new();
33    
34    for item in &ast.items {
35        transform_item(item, &mut luau_ast)?;
36    }
37    
38    Ok(luau_ast)
39}
40
41/// Transform a top-level Rust item into Luau.
42fn transform_item(item: &Item, ast: &mut LuauAst) -> Result<()> {
43    match item {
44        Item::Fn(item_fn) => transform_function(item_fn, ast)?,
45        Item::Struct(item_struct) => transform_struct(item_struct, ast)?,
46        Item::Enum(item_enum) => transform_enum(item_enum, ast)?,
47        Item::Impl(item_impl) => transform_impl(item_impl, ast)?,
48        Item::Use(_) => {
49            // Imports are handled differently in Luau - they'll be resolved
50            // during a separate pass
51        }
52        Item::Mod(_) => {
53            // Modules will be translated into separate files or tables
54            // depending on configuration
55        }
56        _ => {
57            // For items we don't yet support, add a comment to the output
58            let comment = format!("-- Unsupported Rust item: {:?}", item);
59            ast.add_stmt(LuauStmt::Comment(comment));
60        }
61    }
62    
63    Ok(())
64}
65
66/// Transform a Rust function into a Luau function.
67fn transform_function(func: &ItemFn, ast: &mut LuauAst) -> Result<()> {
68    let name = func.sig.ident.to_string();
69    let mut luau_func = LuauFunction::new(name.clone());
70    
71    // Add parameters
72    for param in &func.sig.inputs {
73        match param {
74            syn::FnArg::Typed(pat_type) => {
75                if let syn::Pat::Ident(pat_ident) = &*pat_type.pat {
76                    luau_func.add_param(pat_ident.ident.to_string());
77                }
78            }
79            _ => {
80                return Err(TransformError::UnsupportedFeature(
81                    "Self parameter not supported yet".to_string()
82                ).into());
83            }
84        }
85    }
86    
87    // Transform function body
88    // In a real implementation, we would recursively transform each statement
89    // For now, just add a placeholder
90    
91    luau_func.set_body(vec![
92        LuauStmt::Comment(format!("-- Body of function {}", name)),
93        LuauStmt::Return(Some(LuauExpr::Nil)),
94    ]);
95    
96    // Add the function to the AST
97    ast.add_stmt(LuauStmt::FunctionDecl(luau_func));
98    
99    Ok(())
100}
101
102/// Transform a Rust struct into a Luau table constructor function.
103fn transform_struct(struct_item: &ItemStruct, ast: &mut LuauAst) -> Result<()> {
104    let struct_name = struct_item.ident.to_string();
105    let mut constructor_fn = LuauFunction::new(format!("create_{}", struct_name.to_lowercase()));
106    
107    // Add parameters for each field
108    for field in &struct_item.fields {
109        if let Some(ident) = &field.ident {
110            constructor_fn.add_param(ident.to_string());
111        }
112    }
113    
114    // Create a table with the struct fields
115    let mut table = LuauTable::new();
116    
117    for field in &struct_item.fields {
118        if let Some(ident) = &field.ident {
119            let field_name = ident.to_string();
120            table.add_field(field_name.clone(), LuauExpr::Variable(field_name));
121        }
122    }
123    
124    // Return the table
125    constructor_fn.set_body(vec![
126        LuauStmt::Return(Some(LuauExpr::Table(table))),
127    ]);
128    
129    // Add the constructor function to the AST
130    ast.add_stmt(LuauStmt::FunctionDecl(constructor_fn));
131    
132    Ok(())
133}
134
135/// Transform a Rust enum into a Luau module with constants.
136fn transform_enum(enum_item: &ItemEnum, ast: &mut LuauAst) -> Result<()> {
137    let enum_name = enum_item.ident.to_string();
138    
139    // Create a table for the enum
140    let mut enum_table = LuauTable::new();
141    
142    // Add a field for each variant
143    for (i, variant) in enum_item.variants.iter().enumerate() {
144        let variant_name = variant.ident.to_string();
145        
146        match &variant.fields {
147            syn::Fields::Named(_) => {
148                // For named fields, create a function that constructs the variant
149                let function_name = format!("create_{}", variant_name.to_lowercase());
150                enum_table.add_field(variant_name.clone(), LuauExpr::Variable(function_name));
151            }
152            syn::Fields::Unnamed(_) => {
153                // For tuple variants, create a constructor function
154                let function_name = format!("create_{}", variant_name.to_lowercase());
155                enum_table.add_field(variant_name.clone(), LuauExpr::Variable(function_name));
156            }
157            syn::Fields::Unit => {
158                // For unit variants, just use the index as a value
159                enum_table.add_field(variant_name, LuauExpr::Number(i as f64));
160            }
161        }
162    }
163    
164    // Create an assignment for the enum table
165    ast.add_stmt(LuauStmt::LocalAssign(
166        vec![enum_name],
167        vec![LuauExpr::Table(enum_table)],
168    ));
169    
170    Ok(())
171}
172
173/// Transform a Rust impl block into Luau methods.
174fn transform_impl(impl_item: &ItemImpl, ast: &mut LuauAst) -> Result<()> {
175    let type_name = match &*impl_item.self_ty {
176        syn::Type::Path(type_path) => {
177            if let Some(segment) = type_path.path.segments.last() {
178                segment.ident.to_string()
179            } else {
180                return Err(TransformError::Other("Empty type path".to_string()).into());
181            }
182        }
183        _ => {
184            return Err(TransformError::UnsupportedFeature(
185                "Complex impl self type not supported yet".to_string()
186            ).into());
187        }
188    };
189    
190    // Add methods
191    for item in &impl_item.items {
192        if let ImplItem::Fn(method) = item {
193            transform_method(&type_name, method, ast)?;
194        }
195    }
196    
197    Ok(())
198}
199
200/// Transform a Rust impl method into a Luau method.
201fn transform_method(type_name: &str, method: &syn::ImplItemFn, ast: &mut LuauAst) -> Result<()> {
202    let method_name = method.sig.ident.to_string();
203    let full_method_name = format!("{}_{}", type_name.to_lowercase(), method_name);
204    
205    let mut luau_method = LuauFunction::new(full_method_name);
206    
207    // Handle self parameter
208    let mut has_self = false;
209    
210    for param in &method.sig.inputs {
211        match param {
212            syn::FnArg::Receiver(_) => {
213                has_self = true;
214                luau_method.add_param("self".to_string());
215            }
216            syn::FnArg::Typed(pat_type) => {
217                if let syn::Pat::Ident(pat_ident) = &*pat_type.pat {
218                    luau_method.add_param(pat_ident.ident.to_string());
219                }
220            }
221        }
222    }
223    
224    // Add the method to the type
225    if has_self {
226        // In a real implementation, we would add the method to the type's metatable
227        // For now, just add a function with self parameter
228        luau_method.set_body(vec![
229            LuauStmt::Comment(format!("-- Body of method {}.{}", type_name, method_name)),
230            LuauStmt::Return(Some(LuauExpr::Nil)),
231        ]);
232    } else {
233        // Static method
234        luau_method.set_body(vec![
235            LuauStmt::Comment(format!("-- Body of static method {}.{}", type_name, method_name)),
236            LuauStmt::Return(Some(LuauExpr::Nil)),
237        ]);
238    }
239    
240    // Add the method to the AST
241    ast.add_stmt(LuauStmt::FunctionDecl(luau_method));
242    
243    Ok(())
244}