roblox_rs_core/ast/
transformer.rs1use 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#[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
30pub 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
41fn 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 }
52 Item::Mod(_) => {
53 }
56 _ => {
57 let comment = format!("-- Unsupported Rust item: {:?}", item);
59 ast.add_stmt(LuauStmt::Comment(comment));
60 }
61 }
62
63 Ok(())
64}
65
66fn 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 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 luau_func.set_body(vec![
92 LuauStmt::Comment(format!("-- Body of function {}", name)),
93 LuauStmt::Return(Some(LuauExpr::Nil)),
94 ]);
95
96 ast.add_stmt(LuauStmt::FunctionDecl(luau_func));
98
99 Ok(())
100}
101
102fn 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 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 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 constructor_fn.set_body(vec![
126 LuauStmt::Return(Some(LuauExpr::Table(table))),
127 ]);
128
129 ast.add_stmt(LuauStmt::FunctionDecl(constructor_fn));
131
132 Ok(())
133}
134
135fn transform_enum(enum_item: &ItemEnum, ast: &mut LuauAst) -> Result<()> {
137 let enum_name = enum_item.ident.to_string();
138
139 let mut enum_table = LuauTable::new();
141
142 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 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 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 enum_table.add_field(variant_name, LuauExpr::Number(i as f64));
160 }
161 }
162 }
163
164 ast.add_stmt(LuauStmt::LocalAssign(
166 vec![enum_name],
167 vec![LuauExpr::Table(enum_table)],
168 ));
169
170 Ok(())
171}
172
173fn 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 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
200fn 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 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 if has_self {
226 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 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 ast.add_stmt(LuauStmt::FunctionDecl(luau_method));
242
243 Ok(())
244}