/home/noah/src/ruchy/src/backend/transpiler/mod.rs
Line | Count | Source |
1 | | //! Modular transpiler for Ruchy language |
2 | | //! |
3 | | //! This module is responsible for converting Ruchy AST into Rust code using `proc_macro2` `TokenStream`. |
4 | | |
5 | | #![allow(clippy::missing_errors_doc)] |
6 | | #![allow(clippy::too_many_lines)] |
7 | | |
8 | | mod actors; |
9 | | mod dataframe; |
10 | | mod dataframe_builder; |
11 | | mod dataframe_helpers; |
12 | | mod dispatcher; |
13 | | mod expressions; |
14 | | mod method_call_refactored; |
15 | | mod patterns; |
16 | | mod result_type; |
17 | | mod statements; |
18 | | mod type_conversion_refactored; |
19 | | mod type_inference; |
20 | | mod types; |
21 | | pub mod codegen_minimal; |
22 | | |
23 | | use crate::frontend::ast::{Attribute, Expr, ExprKind, Span, Type}; |
24 | | use crate::backend::module_resolver::ModuleResolver; |
25 | | use anyhow::Result; |
26 | | use proc_macro2::TokenStream; |
27 | | use quote::{format_ident, quote}; |
28 | | |
29 | | // Module exports are handled by the impl blocks in each module |
30 | | |
31 | | /// Block categorization result: (functions, statements, modules, `has_main`, `main_expr`) |
32 | | type BlockCategorization<'a> = (Vec<TokenStream>, Vec<TokenStream>, Vec<TokenStream>, bool, Option<&'a Expr>); |
33 | | |
34 | | /// Function signature information for type coercion |
35 | | #[derive(Debug, Clone)] |
36 | | pub struct FunctionSignature { |
37 | | pub name: String, |
38 | | pub param_types: Vec<String>, // Simplified: just the type name as string |
39 | | } |
40 | | |
41 | | /// The main transpiler struct |
42 | | pub struct Transpiler { |
43 | | /// Track whether we're in an async context |
44 | | pub in_async_context: bool, |
45 | | /// Track variables that need to be mutable (for auto-mutability) |
46 | | pub mutable_vars: std::collections::HashSet<String>, |
47 | | /// Track function signatures for type coercion |
48 | | pub function_signatures: std::collections::HashMap<String, FunctionSignature>, |
49 | | } |
50 | | |
51 | | impl Default for Transpiler { |
52 | 0 | fn default() -> Self { |
53 | 0 | Self::new() |
54 | 0 | } |
55 | | } |
56 | | |
57 | | impl Transpiler { |
58 | | /// Creates a new transpiler instance without module loader |
59 | | /// |
60 | | /// # Examples |
61 | | /// |
62 | | /// ``` |
63 | | /// use ruchy::Transpiler; |
64 | | /// |
65 | | /// let transpiler = Transpiler::new(); |
66 | | /// assert!(!transpiler.in_async_context); |
67 | | /// ``` |
68 | 0 | pub fn new() -> Self { |
69 | 0 | Self { |
70 | 0 | in_async_context: false, |
71 | 0 | mutable_vars: std::collections::HashSet::new(), |
72 | 0 | function_signatures: std::collections::HashMap::new(), |
73 | 0 | } |
74 | 0 | } |
75 | | |
76 | | /// Centralized result printing logic - ONE PLACE FOR ALL RESULT PRINTING |
77 | | /// This eliminates code duplication and ensures consistent Unit type handling |
78 | 0 | fn generate_result_printing_tokens(&self) -> TokenStream { |
79 | 0 | quote! { |
80 | | // Check the type name first to avoid Unit type Display error |
81 | | if std::any::type_name_of_val(&result) == "()" { |
82 | | // Don't print Unit type |
83 | | } else if std::any::type_name_of_val(&result).contains("String") || |
84 | | std::any::type_name_of_val(&result).contains("&str") { |
85 | | println!("{}", result); |
86 | | } else { |
87 | | println!("{:?}", result); |
88 | | } |
89 | | } |
90 | 0 | } |
91 | | |
92 | | /// Centralized value printing logic for functions like println |
93 | 0 | fn generate_value_printing_tokens(&self, value_expr: TokenStream, func_tokens: TokenStream) -> TokenStream { |
94 | 0 | quote! { |
95 | | { |
96 | | use std::any::Any; |
97 | | let value = #value_expr; |
98 | | // Special handling for String and &str types to avoid quotes |
99 | | if let Some(s) = (&value as &dyn Any).downcast_ref::<String>() { |
100 | | #func_tokens!("{}", s) |
101 | | } else if let Some(s) = (&value as &dyn Any).downcast_ref::<&str>() { |
102 | | #func_tokens!("{}", s) |
103 | | } else { |
104 | | #func_tokens!("{:?}", value) |
105 | | } |
106 | | } |
107 | | } |
108 | 0 | } |
109 | | |
110 | | /// Analyze expressions to determine which variables need to be mutable |
111 | 0 | pub fn analyze_mutability(&mut self, exprs: &[Expr]) { |
112 | 0 | for expr in exprs { |
113 | 0 | self.analyze_expr_mutability(expr); |
114 | 0 | } |
115 | 0 | } |
116 | | |
117 | | /// Collect function signatures for type coercion |
118 | 0 | pub fn collect_function_signatures(&mut self, exprs: &[Expr]) { |
119 | 0 | for expr in exprs { |
120 | 0 | self.collect_signatures_from_expr(expr); |
121 | 0 | } |
122 | 0 | } |
123 | | |
124 | 0 | fn collect_signatures_from_expr(&mut self, expr: &Expr) { |
125 | | use crate::frontend::ast::ExprKind; |
126 | | |
127 | 0 | match &expr.kind { |
128 | 0 | ExprKind::Function { name, params, .. } => { |
129 | 0 | let param_types: Vec<String> = params.iter() |
130 | 0 | .map(|param| self.type_to_string(¶m.ty)) |
131 | 0 | .collect(); |
132 | | |
133 | 0 | let signature = FunctionSignature { |
134 | 0 | name: name.clone(), |
135 | 0 | param_types, |
136 | 0 | }; |
137 | | |
138 | 0 | self.function_signatures.insert(name.clone(), signature); |
139 | | } |
140 | 0 | ExprKind::Block(exprs) => { |
141 | 0 | for e in exprs { |
142 | 0 | self.collect_signatures_from_expr(e); |
143 | 0 | } |
144 | | } |
145 | 0 | ExprKind::Let { body, .. } => { |
146 | 0 | self.collect_signatures_from_expr(body); |
147 | 0 | } |
148 | 0 | _ => {} |
149 | | } |
150 | 0 | } |
151 | | |
152 | 0 | fn type_to_string(&self, ty: &crate::frontend::ast::Type) -> String { |
153 | | use crate::frontend::ast::TypeKind; |
154 | | |
155 | 0 | match &ty.kind { |
156 | 0 | TypeKind::Named(name) => name.clone(), |
157 | 0 | TypeKind::Reference { inner, .. } => format!("&{}", self.type_to_string(inner)), |
158 | 0 | _ => "Unknown".to_string(), |
159 | | } |
160 | 0 | } |
161 | | |
162 | 0 | fn analyze_expr_mutability(&mut self, expr: &Expr) { |
163 | | use crate::frontend::ast::ExprKind; |
164 | | |
165 | 0 | match &expr.kind { |
166 | | // Direct assignment marks the target as mutable |
167 | 0 | ExprKind::Assign { target, value } => { |
168 | 0 | if let ExprKind::Identifier(name) = &target.kind { |
169 | 0 | self.mutable_vars.insert(name.clone()); |
170 | 0 | } |
171 | 0 | self.analyze_expr_mutability(value); |
172 | | } |
173 | | // Compound assignment marks the target as mutable |
174 | 0 | ExprKind::CompoundAssign { target, value, .. } => { |
175 | 0 | if let ExprKind::Identifier(name) = &target.kind { |
176 | 0 | self.mutable_vars.insert(name.clone()); |
177 | 0 | } |
178 | 0 | self.analyze_expr_mutability(value); |
179 | | } |
180 | | // Pre/Post increment/decrement mark the target as mutable |
181 | 0 | ExprKind::PreIncrement { target } | |
182 | 0 | ExprKind::PostIncrement { target } | |
183 | 0 | ExprKind::PreDecrement { target } | |
184 | 0 | ExprKind::PostDecrement { target } => { |
185 | 0 | if let ExprKind::Identifier(name) = &target.kind { |
186 | 0 | self.mutable_vars.insert(name.clone()); |
187 | 0 | } |
188 | | } |
189 | | // Recursively analyze blocks |
190 | 0 | ExprKind::Block(exprs) => { |
191 | 0 | for e in exprs { |
192 | 0 | self.analyze_expr_mutability(e); |
193 | 0 | } |
194 | | } |
195 | | // Analyze control flow |
196 | 0 | ExprKind::If { condition, then_branch, else_branch } => { |
197 | 0 | self.analyze_expr_mutability(condition); |
198 | 0 | self.analyze_expr_mutability(then_branch); |
199 | 0 | if let Some(else_expr) = else_branch { |
200 | 0 | self.analyze_expr_mutability(else_expr); |
201 | 0 | } |
202 | | } |
203 | 0 | ExprKind::While { condition, body } => { |
204 | 0 | self.analyze_expr_mutability(condition); |
205 | 0 | self.analyze_expr_mutability(body); |
206 | 0 | } |
207 | 0 | ExprKind::For { body, iter, .. } => { |
208 | 0 | self.analyze_expr_mutability(iter); |
209 | 0 | self.analyze_expr_mutability(body); |
210 | 0 | } |
211 | | // Analyze match arms |
212 | 0 | ExprKind::Match { expr, arms } => { |
213 | 0 | self.analyze_expr_mutability(expr); |
214 | 0 | for arm in arms { |
215 | 0 | self.analyze_expr_mutability(&arm.body); |
216 | 0 | } |
217 | | } |
218 | | // Analyze let bodies |
219 | 0 | ExprKind::Let { body, value, .. } | ExprKind::LetPattern { body, value, .. } => { |
220 | 0 | self.analyze_expr_mutability(value); |
221 | 0 | self.analyze_expr_mutability(body); |
222 | 0 | } |
223 | | // Analyze function bodies |
224 | 0 | ExprKind::Function { body, .. } => { |
225 | 0 | self.analyze_expr_mutability(body); |
226 | 0 | } |
227 | 0 | ExprKind::Lambda { body, .. } => { |
228 | 0 | self.analyze_expr_mutability(body); |
229 | 0 | } |
230 | | // Analyze binary/unary operations |
231 | 0 | ExprKind::Binary { left, right, .. } => { |
232 | 0 | self.analyze_expr_mutability(left); |
233 | 0 | self.analyze_expr_mutability(right); |
234 | 0 | } |
235 | 0 | ExprKind::Unary { operand, .. } => { |
236 | 0 | self.analyze_expr_mutability(operand); |
237 | 0 | } |
238 | | // Analyze calls |
239 | 0 | ExprKind::Call { func, args } => { |
240 | 0 | self.analyze_expr_mutability(func); |
241 | 0 | for arg in args { |
242 | 0 | self.analyze_expr_mutability(arg); |
243 | 0 | } |
244 | | } |
245 | 0 | ExprKind::MethodCall { receiver, args, .. } => { |
246 | 0 | self.analyze_expr_mutability(receiver); |
247 | 0 | for arg in args { |
248 | 0 | self.analyze_expr_mutability(arg); |
249 | 0 | } |
250 | | } |
251 | 0 | _ => {} |
252 | | } |
253 | 0 | } |
254 | | |
255 | | /// Resolves file imports in the AST using `ModuleResolver` |
256 | | #[allow(dead_code)] |
257 | 0 | fn resolve_imports(&self, expr: &Expr) -> Result<Expr> { |
258 | | // For now, just use default search paths since we don't have file context here |
259 | 0 | let mut resolver = ModuleResolver::new(); |
260 | 0 | resolver.resolve_imports(expr.clone()) |
261 | 0 | } |
262 | | |
263 | | /// Resolves file imports with a specific file context for search paths |
264 | 0 | fn resolve_imports_with_context(&self, expr: &Expr, file_path: Option<&std::path::Path>) -> Result<Expr> { |
265 | 0 | let mut resolver = ModuleResolver::new(); |
266 | | |
267 | | // Add the file's directory to search paths if provided |
268 | 0 | if let Some(path) = file_path { |
269 | 0 | if let Some(dir) = path.parent() { |
270 | 0 | resolver.add_search_path(dir); |
271 | 0 | } |
272 | 0 | } |
273 | | |
274 | 0 | resolver.resolve_imports(expr.clone()) |
275 | 0 | } |
276 | | |
277 | | /// Transpiles an expression to a `TokenStream` |
278 | | /// |
279 | | /// # Examples |
280 | | /// |
281 | | /// ``` |
282 | | /// use ruchy::{Transpiler, Parser}; |
283 | | /// |
284 | | /// let mut parser = Parser::new("42"); |
285 | | /// let ast = parser.parse().unwrap(); |
286 | | /// |
287 | | /// let transpiler = Transpiler::new(); |
288 | | /// let result = transpiler.transpile(&ast); |
289 | | /// assert!(result.is_ok()); |
290 | | /// ``` |
291 | | /// |
292 | | /// # Errors |
293 | | /// |
294 | | /// Returns an error if the AST cannot be transpiled to valid Rust code. |
295 | 0 | pub fn transpile(&self, expr: &Expr) -> Result<TokenStream> { |
296 | 0 | self.transpile_expr(expr) |
297 | 0 | } |
298 | | |
299 | | /// Check if AST contains `HashMap` operations requiring `std::collections::HashMap` import |
300 | 0 | fn contains_hashmap(expr: &Expr) -> bool { |
301 | | use crate::frontend::ast::{ExprKind, Literal}; |
302 | | |
303 | 0 | match &expr.kind { |
304 | 0 | ExprKind::ObjectLiteral { .. } => true, |
305 | 0 | ExprKind::Call { func, .. } => { |
306 | | // Check for HashMap methods like .get(), .insert(), etc. |
307 | 0 | if let ExprKind::FieldAccess { field, .. } = &func.kind { |
308 | 0 | matches!(field.as_str(), "get" | "insert" | "remove" | "contains_key" | "keys" | "values") |
309 | | } else { |
310 | 0 | false |
311 | | } |
312 | | } |
313 | 0 | ExprKind::IndexAccess { object: _, index } => { |
314 | | // String literal index access suggests HashMap |
315 | 0 | matches!(&index.kind, ExprKind::Literal(Literal::String(_))) |
316 | | } |
317 | 0 | ExprKind::Block(exprs) => exprs.iter().any(Self::contains_hashmap), |
318 | 0 | ExprKind::Function { body, .. } => Self::contains_hashmap(body), |
319 | 0 | ExprKind::If { condition, then_branch, else_branch } => { |
320 | 0 | Self::contains_hashmap(condition) || |
321 | 0 | Self::contains_hashmap(then_branch) || |
322 | 0 | else_branch.as_ref().is_some_and(|e| Self::contains_hashmap(e)) |
323 | | } |
324 | 0 | ExprKind::Binary { left, right, .. } => { |
325 | 0 | Self::contains_hashmap(left) || Self::contains_hashmap(right) |
326 | | } |
327 | 0 | _ => false, |
328 | | } |
329 | 0 | } |
330 | | |
331 | | /// Checks if an expression contains `DataFrame` operations (simplified for complexity) |
332 | 0 | fn contains_dataframe(expr: &Expr) -> bool { |
333 | 0 | matches!( |
334 | 0 | expr.kind, |
335 | | ExprKind::DataFrame { .. } | ExprKind::DataFrameOperation { .. } |
336 | | ) |
337 | 0 | } |
338 | | |
339 | | /// Wraps transpiled code in a complete Rust program with necessary imports |
340 | | /// |
341 | | /// # Examples |
342 | | /// |
343 | | /// ``` |
344 | | /// use ruchy::{Transpiler, Parser}; |
345 | | /// |
346 | | /// let mut parser = Parser::new("42"); |
347 | | /// let ast = parser.parse().unwrap(); |
348 | | /// |
349 | | /// let transpiler = Transpiler::new(); |
350 | | /// let result = transpiler.transpile_to_program(&ast); |
351 | | /// assert!(result.is_ok()); |
352 | | /// |
353 | | /// let code = result.unwrap().to_string(); |
354 | | /// assert!(code.contains("fn main")); |
355 | | /// assert!(code.contains("42")); |
356 | | /// ``` |
357 | | /// |
358 | | /// # Errors |
359 | | /// |
360 | | /// Returns an error if the AST cannot be transpiled to a valid Rust program. |
361 | 0 | pub fn transpile_to_program(&mut self, expr: &Expr) -> Result<TokenStream> { |
362 | | // First analyze the entire program to detect mutable variables and function signatures |
363 | 0 | if let ExprKind::Block(exprs) = &expr.kind { |
364 | 0 | self.analyze_mutability(exprs); |
365 | 0 | self.collect_function_signatures(exprs); |
366 | 0 | } else { |
367 | 0 | self.analyze_expr_mutability(expr); |
368 | 0 | self.collect_signatures_from_expr(expr); |
369 | 0 | } |
370 | | |
371 | 0 | let result = self.transpile_to_program_with_context(expr, None); |
372 | 0 | if let Ok(ref token_stream) = result { |
373 | 0 | // Debug: Write the generated Rust code to a debug file |
374 | 0 | let rust_code = token_stream.to_string(); |
375 | 0 | std::fs::write("/tmp/debug_transpiler_output.rs", &rust_code).ok(); |
376 | 0 | } |
377 | 0 | result |
378 | 0 | } |
379 | | |
380 | | /// Transpile with file context for module resolution |
381 | 0 | pub fn transpile_to_program_with_context(&self, expr: &Expr, file_path: Option<&std::path::Path>) -> Result<TokenStream> { |
382 | | // First, resolve any file imports using the module resolver |
383 | 0 | let resolved_expr = self.resolve_imports_with_context(expr, file_path)?; |
384 | 0 | let needs_polars = Self::contains_dataframe(&resolved_expr); |
385 | 0 | let needs_hashmap = Self::contains_hashmap(&resolved_expr); |
386 | | |
387 | 0 | match &resolved_expr.kind { |
388 | 0 | ExprKind::Function { name, .. } => { |
389 | 0 | self.transpile_single_function(&resolved_expr, name, needs_polars, needs_hashmap) |
390 | | } |
391 | 0 | ExprKind::Block(exprs) => { |
392 | 0 | self.transpile_program_block(exprs, needs_polars, needs_hashmap) |
393 | | } |
394 | | _ => { |
395 | 0 | self.transpile_expression_program(&resolved_expr, needs_polars, needs_hashmap) |
396 | | } |
397 | | } |
398 | 0 | } |
399 | | |
400 | 0 | fn transpile_single_function(&self, expr: &Expr, name: &str, needs_polars: bool, needs_hashmap: bool) -> Result<TokenStream> { |
401 | | // Use the proper function expression transpiler to handle attributes correctly |
402 | 0 | let func = match &expr.kind { |
403 | 0 | crate::frontend::ast::ExprKind::Function { .. } => self.transpile_function_expr(expr)?, |
404 | 0 | _ => self.transpile_expr(expr)?, |
405 | | }; |
406 | 0 | let needs_main = name != "main"; |
407 | | |
408 | 0 | match (needs_polars, needs_hashmap, needs_main) { |
409 | 0 | (true, true, true) => Ok(quote! { |
410 | 0 | use polars::prelude::*; |
411 | 0 | use std::collections::HashMap; |
412 | 0 | #func |
413 | 0 | fn main() { /* Function defined but not called */ } |
414 | 0 | }), |
415 | 0 | (true, true, false) => Ok(quote! { |
416 | 0 | use polars::prelude::*; |
417 | 0 | use std::collections::HashMap; |
418 | 0 | #func |
419 | 0 | }), |
420 | 0 | (true, false, true) => Ok(quote! { |
421 | 0 | use polars::prelude::*; |
422 | 0 | #func |
423 | 0 | fn main() { /* Function defined but not called */ } |
424 | 0 | }), |
425 | 0 | (true, false, false) => Ok(quote! { |
426 | 0 | use polars::prelude::*; |
427 | 0 | #func |
428 | 0 | }), |
429 | 0 | (false, true, true) => Ok(quote! { |
430 | 0 | use std::collections::HashMap; |
431 | 0 | #func |
432 | 0 | fn main() { /* Function defined but not called */ } |
433 | 0 | }), |
434 | 0 | (false, true, false) => Ok(quote! { |
435 | 0 | use std::collections::HashMap; |
436 | 0 | #func |
437 | 0 | }), |
438 | 0 | (false, false, true) => Ok(quote! { |
439 | 0 | #func |
440 | 0 | fn main() { /* Function defined but not called */ } |
441 | 0 | }), |
442 | 0 | (false, false, false) => Ok(quote! { |
443 | 0 | #func |
444 | 0 | }) |
445 | | } |
446 | 0 | } |
447 | | |
448 | 0 | fn transpile_program_block(&self, exprs: &[Expr], needs_polars: bool, needs_hashmap: bool) -> Result<TokenStream> { |
449 | 0 | let (functions, statements, modules, has_main, main_expr) = self.categorize_block_expressions(exprs)?; |
450 | | |
451 | 0 | if functions.is_empty() && !has_main && modules.is_empty() { |
452 | 0 | self.transpile_statement_only_block(exprs, needs_polars, needs_hashmap) |
453 | 0 | } else if has_main || !modules.is_empty() { |
454 | 0 | self.transpile_block_with_main_function(&functions, &statements, &modules, main_expr, needs_polars, needs_hashmap) |
455 | | } else { |
456 | 0 | self.transpile_block_with_functions(&functions, &statements, needs_polars, needs_hashmap) |
457 | | } |
458 | 0 | } |
459 | | |
460 | 0 | fn categorize_block_expressions<'a>(&self, exprs: &'a [Expr]) -> Result<BlockCategorization<'a>> { |
461 | 0 | let mut functions = Vec::new(); |
462 | 0 | let mut statements = Vec::new(); |
463 | 0 | let mut modules = Vec::new(); |
464 | 0 | let mut has_main_function = false; |
465 | 0 | let mut main_function_expr = None; |
466 | | |
467 | | |
468 | 0 | for expr in exprs { |
469 | 0 | match &expr.kind { |
470 | 0 | ExprKind::Function { name, .. } => { |
471 | 0 | if name == "main" { |
472 | 0 | has_main_function = true; |
473 | 0 | main_function_expr = Some(expr); |
474 | 0 | } else { |
475 | | // Use proper function transpiler to handle attributes correctly |
476 | 0 | functions.push(self.transpile_function_expr(expr)?); |
477 | | } |
478 | | }, |
479 | 0 | ExprKind::Module { name, body } => { |
480 | | // Extract module declarations for top-level placement |
481 | 0 | modules.push(self.transpile_module_declaration(name, body)?); |
482 | | }, |
483 | 0 | ExprKind::Block(block_exprs) => { |
484 | | // Check if this is a module-containing block from the resolver |
485 | 0 | if block_exprs.len() == 2 |
486 | 0 | && matches!(block_exprs[0].kind, ExprKind::Module { .. }) |
487 | 0 | && matches!(block_exprs[1].kind, ExprKind::Import { .. }) { |
488 | | // This is a module resolver block: extract the module and keep the import as statement |
489 | 0 | if let ExprKind::Module { name, body } = &block_exprs[0].kind { |
490 | 0 | modules.push(self.transpile_module_declaration(name, body)?); |
491 | 0 | } |
492 | 0 | statements.push(self.transpile_expr(&block_exprs[1])?); |
493 | | } else { |
494 | | // Regular block, treat as statement |
495 | 0 | statements.push(self.transpile_expr(expr)?); |
496 | | } |
497 | | }, |
498 | | _ => { |
499 | 0 | let stmt = self.transpile_expr(expr)?; |
500 | | // Ensure statements have semicolons for proper separation |
501 | 0 | let stmt_str = stmt.to_string(); |
502 | 0 | if !stmt_str.trim().ends_with(';') && !stmt_str.trim().ends_with('}') { |
503 | 0 | statements.push(quote! { #stmt; }); |
504 | 0 | } else { |
505 | 0 | statements.push(stmt); |
506 | 0 | } |
507 | | } |
508 | | } |
509 | | } |
510 | | |
511 | 0 | Ok((functions, statements, modules, has_main_function, main_function_expr)) |
512 | 0 | } |
513 | | |
514 | 0 | fn transpile_module_declaration(&self, name: &str, body: &Expr) -> Result<TokenStream> { |
515 | 0 | let module_name = format_ident!("{}", name); |
516 | | |
517 | | // Handle module body - if it's a block, transpile its contents as module items |
518 | 0 | let body_tokens = if let ExprKind::Block(exprs) = &body.kind { |
519 | | // Separate functions from other items in the module |
520 | 0 | let mut module_items = Vec::new(); |
521 | | |
522 | 0 | for expr in exprs { |
523 | 0 | match &expr.kind { |
524 | | ExprKind::Function { .. } => { |
525 | | // Transpile functions as module items |
526 | 0 | module_items.push(self.transpile_function_expr(expr)?); |
527 | | } |
528 | | _ => { |
529 | | // Other items (constants, etc.) |
530 | 0 | module_items.push(self.transpile_expr(expr)?); |
531 | | } |
532 | | } |
533 | | } |
534 | | |
535 | 0 | quote! { #(#module_items)* } |
536 | | } else { |
537 | | // Single expression - transpile normally |
538 | 0 | self.transpile_expr(body)? |
539 | | }; |
540 | | |
541 | 0 | Ok(quote! { |
542 | 0 | mod #module_name { |
543 | 0 | #body_tokens |
544 | 0 | } |
545 | 0 | }) |
546 | 0 | } |
547 | | |
548 | 0 | fn transpile_statement_only_block(&self, exprs: &[Expr], needs_polars: bool, needs_hashmap: bool) -> Result<TokenStream> { |
549 | | // Check if this is a statement sequence (contains let, assignments, etc.) or an expression sequence |
550 | 0 | let has_statements = exprs.iter().any(|expr| self.is_statement_expr(expr)); |
551 | | |
552 | 0 | if has_statements { |
553 | | // Split into statements and possible final expression |
554 | 0 | let (statements, final_expr) = if !exprs.is_empty() && !self.is_statement_expr(exprs.last().unwrap()) { |
555 | | // Last item is an expression, not a statement |
556 | 0 | (&exprs[..exprs.len() - 1], Some(exprs.last().unwrap())) |
557 | | } else { |
558 | | // All are statements |
559 | 0 | (exprs, None) |
560 | | }; |
561 | | |
562 | | // Transpile all statements and add semicolons intelligently |
563 | 0 | let statement_results: Result<Vec<_>> = statements.iter().map(|expr| { |
564 | 0 | let tokens = self.transpile_expr(expr)?; |
565 | 0 | let tokens_str = tokens.to_string(); |
566 | | // If the statement already ends with a semicolon, don't add another |
567 | 0 | if tokens_str.trim().ends_with(';') { |
568 | 0 | Ok(tokens) |
569 | | } else { |
570 | | // Add semicolon for statements that need them |
571 | 0 | Ok(quote! { #tokens; }) |
572 | | } |
573 | 0 | }).collect(); |
574 | 0 | let statement_tokens = statement_results?; |
575 | | |
576 | | // Handle final expression if present |
577 | 0 | let main_body = if let Some(final_expr) = final_expr { |
578 | 0 | let final_tokens = self.transpile_expr(final_expr)?; |
579 | 0 | let result_printing_logic = self.generate_result_printing_tokens(); |
580 | 0 | quote! { |
581 | | #(#statement_tokens)* |
582 | | let result = #final_tokens; |
583 | | #result_printing_logic |
584 | | } |
585 | | } else { |
586 | 0 | quote! { |
587 | | #(#statement_tokens)* |
588 | | } |
589 | | }; |
590 | | |
591 | 0 | match (needs_polars, needs_hashmap) { |
592 | 0 | (true, true) => Ok(quote! { |
593 | 0 | use polars::prelude::*; |
594 | 0 | use std::collections::HashMap; |
595 | 0 | fn main() { |
596 | 0 | #main_body |
597 | 0 | } |
598 | 0 | }), |
599 | 0 | (true, false) => Ok(quote! { |
600 | 0 | use polars::prelude::*; |
601 | 0 | fn main() { |
602 | 0 | #main_body |
603 | 0 | } |
604 | 0 | }), |
605 | 0 | (false, true) => Ok(quote! { |
606 | 0 | use std::collections::HashMap; |
607 | 0 | fn main() { |
608 | 0 | #main_body |
609 | 0 | } |
610 | 0 | }), |
611 | 0 | (false, false) => Ok(quote! { |
612 | 0 | fn main() { |
613 | 0 | #main_body |
614 | 0 | } |
615 | 0 | }) |
616 | | } |
617 | | } else { |
618 | | // Pure expression sequence - use existing result printing approach |
619 | 0 | let block_expr = Expr::new(ExprKind::Block(exprs.to_vec()), Span::new(0, 0)); |
620 | 0 | let body = self.transpile_expr(&block_expr)?; |
621 | 0 | self.wrap_in_main_with_result_printing(body, needs_polars, needs_hashmap) |
622 | | } |
623 | 0 | } |
624 | | |
625 | 0 | fn is_statement_expr(&self, expr: &Expr) -> bool { |
626 | 0 | match &expr.kind { |
627 | | // Let bindings are statements |
628 | 0 | ExprKind::Let { .. } | ExprKind::LetPattern { .. } => true, |
629 | | // Assignment operations are statements |
630 | 0 | ExprKind::Assign { .. } | ExprKind::CompoundAssign { .. } => true, |
631 | | // Loops are statements (void/unit type) |
632 | 0 | ExprKind::While { .. } | ExprKind::For { .. } | ExprKind::Loop { .. } => true, |
633 | | // Function calls that don't return meaningful values (like println) |
634 | 0 | ExprKind::Call { func, .. } => { |
635 | 0 | if let ExprKind::Identifier(name) = &func.kind { |
636 | 0 | matches!(name.as_str(), "println" | "print" | "dbg") |
637 | | } else { |
638 | 0 | false |
639 | | } |
640 | | } |
641 | | // Blocks containing statements |
642 | 0 | ExprKind::Block(exprs) => exprs.iter().any(|e| self.is_statement_expr(e)), |
643 | | // Most other expressions are not statements |
644 | 0 | _ => false, |
645 | | } |
646 | 0 | } |
647 | | |
648 | 0 | fn transpile_block_with_main_function(&self, functions: &[TokenStream], statements: &[TokenStream], modules: &[TokenStream], main_expr: Option<&Expr>, needs_polars: bool, needs_hashmap: bool) -> Result<TokenStream> { |
649 | 0 | if statements.is_empty() && main_expr.is_some() { |
650 | | // Only functions, just emit them normally (includes user's main) |
651 | 0 | let main_tokens = if let Some(main) = main_expr { |
652 | 0 | self.transpile_expr(main)? |
653 | | } else { |
654 | 0 | return Err(anyhow::anyhow!("Expected main function expression")); |
655 | | }; |
656 | | |
657 | 0 | match (needs_polars, needs_hashmap) { |
658 | 0 | (true, true) => Ok(quote! { |
659 | | use polars::prelude::*; |
660 | | use std::collections::HashMap; |
661 | | #(#modules)* |
662 | | #(#functions)* |
663 | | #main_tokens |
664 | | }), |
665 | 0 | (true, false) => Ok(quote! { |
666 | | use polars::prelude::*; |
667 | | #(#modules)* |
668 | | #(#functions)* |
669 | | #main_tokens |
670 | | }), |
671 | 0 | (false, true) => Ok(quote! { |
672 | | use std::collections::HashMap; |
673 | | #(#modules)* |
674 | | #(#functions)* |
675 | | #main_tokens |
676 | | }), |
677 | 0 | (false, false) => Ok(quote! { |
678 | | #(#modules)* |
679 | | #(#functions)* |
680 | | #main_tokens |
681 | | }) |
682 | | } |
683 | | } else { |
684 | | // TOP-LEVEL STATEMENTS: Extract main body and combine with statements |
685 | 0 | let main_body = if let Some(main) = main_expr { |
686 | 0 | self.extract_main_function_body(main)? |
687 | | } else { |
688 | | // No user main function, just use empty body |
689 | 0 | quote! {} |
690 | | }; |
691 | | |
692 | 0 | match (needs_polars, needs_hashmap) { |
693 | 0 | (true, true) => Ok(quote! { |
694 | | use polars::prelude::*; |
695 | | use std::collections::HashMap; |
696 | | #(#modules)* |
697 | | #(#functions)* |
698 | | fn main() { |
699 | | // Top-level statements execute first |
700 | | #(#statements)* |
701 | | |
702 | | // Then user's main function body |
703 | | #main_body |
704 | | } |
705 | | }), |
706 | 0 | (true, false) => Ok(quote! { |
707 | | use polars::prelude::*; |
708 | | #(#modules)* |
709 | | #(#functions)* |
710 | | fn main() { |
711 | | // Top-level statements execute first |
712 | | #(#statements)* |
713 | | |
714 | | // Then user's main function body |
715 | | #main_body |
716 | | } |
717 | | }), |
718 | 0 | (false, true) => Ok(quote! { |
719 | | use std::collections::HashMap; |
720 | | #(#modules)* |
721 | | #(#functions)* |
722 | | fn main() { |
723 | | // Top-level statements execute first |
724 | | #(#statements)* |
725 | | |
726 | | // Then user's main function body |
727 | | #main_body |
728 | | } |
729 | | }), |
730 | 0 | (false, false) => Ok(quote! { |
731 | | #(#modules)* |
732 | | #(#functions)* |
733 | | fn main() { |
734 | | // Top-level statements execute first |
735 | | #(#statements)* |
736 | | |
737 | | // Then user's main function body |
738 | | #main_body |
739 | | } |
740 | | }) |
741 | | } |
742 | | } |
743 | 0 | } |
744 | | |
745 | | /// Extracts the body of a main function for inlining with top-level statements |
746 | 0 | fn extract_main_function_body(&self, main_expr: &Expr) -> Result<TokenStream> { |
747 | 0 | if let ExprKind::Function { body, .. } = &main_expr.kind { |
748 | | // Transpile just the body, not the entire function definition |
749 | 0 | self.transpile_expr(body) |
750 | | } else { |
751 | 0 | Err(anyhow::anyhow!("Expected function expression for main body extraction")) |
752 | | } |
753 | 0 | } |
754 | | |
755 | 0 | fn transpile_block_with_functions(&self, functions: &[TokenStream], statements: &[TokenStream], needs_polars: bool, needs_hashmap: bool) -> Result<TokenStream> { |
756 | | // No main function among extracted functions - create one for statements |
757 | 0 | match (needs_polars, needs_hashmap) { |
758 | 0 | (true, true) => Ok(quote! { |
759 | | use polars::prelude::*; |
760 | | use std::collections::HashMap; |
761 | | #(#functions)* |
762 | | fn main() { #(#statements)* } |
763 | | }), |
764 | 0 | (true, false) => Ok(quote! { |
765 | | use polars::prelude::*; |
766 | | #(#functions)* |
767 | | fn main() { #(#statements)* } |
768 | | }), |
769 | 0 | (false, true) => Ok(quote! { |
770 | | use std::collections::HashMap; |
771 | | #(#functions)* |
772 | | fn main() { #(#statements)* } |
773 | | }), |
774 | 0 | (false, false) => Ok(quote! { |
775 | | #(#functions)* |
776 | | fn main() { #(#statements)* } |
777 | | }) |
778 | | } |
779 | 0 | } |
780 | | |
781 | | |
782 | 0 | fn transpile_expression_program(&self, expr: &Expr, needs_polars: bool, needs_hashmap: bool) -> Result<TokenStream> { |
783 | 0 | let body = self.transpile_expr(expr)?; |
784 | | |
785 | | // Check if this is a statement vs expression |
786 | 0 | if self.is_statement_expr(expr) { |
787 | | // For statements, execute directly without result wrapping |
788 | 0 | self.wrap_statement_in_main(body, needs_polars, needs_hashmap) |
789 | | } else { |
790 | | // For expressions, wrap with result printing |
791 | 0 | self.wrap_in_main_with_result_printing(body, needs_polars, needs_hashmap) |
792 | | } |
793 | 0 | } |
794 | | |
795 | 0 | fn wrap_statement_in_main(&self, body: TokenStream, needs_polars: bool, needs_hashmap: bool) -> Result<TokenStream> { |
796 | | // For statements, execute directly without result capture |
797 | 0 | match (needs_polars, needs_hashmap) { |
798 | 0 | (true, true) => Ok(quote! { |
799 | 0 | use polars::prelude::*; |
800 | 0 | use std::collections::HashMap; |
801 | 0 | fn main() { |
802 | 0 | #body; |
803 | 0 | } |
804 | 0 | }), |
805 | 0 | (true, false) => Ok(quote! { |
806 | 0 | use polars::prelude::*; |
807 | 0 | fn main() { |
808 | 0 | #body; |
809 | 0 | } |
810 | 0 | }), |
811 | 0 | (false, true) => Ok(quote! { |
812 | 0 | use std::collections::HashMap; |
813 | 0 | fn main() { |
814 | 0 | #body; |
815 | 0 | } |
816 | 0 | }), |
817 | 0 | (false, false) => Ok(quote! { |
818 | 0 | fn main() { |
819 | 0 | #body; |
820 | 0 | } |
821 | 0 | }) |
822 | | } |
823 | 0 | } |
824 | | |
825 | 0 | fn wrap_in_main_with_result_printing(&self, body: TokenStream, needs_polars: bool, needs_hashmap: bool) -> Result<TokenStream> { |
826 | 0 | let result_printing_logic = self.generate_result_printing_tokens(); |
827 | 0 | match (needs_polars, needs_hashmap) { |
828 | 0 | (true, true) => Ok(quote! { |
829 | 0 | use polars::prelude::*; |
830 | 0 | use std::collections::HashMap; |
831 | 0 | fn main() { |
832 | 0 | let result = #body; |
833 | 0 | #result_printing_logic |
834 | 0 | } |
835 | 0 | }), |
836 | 0 | (true, false) => Ok(quote! { |
837 | 0 | use polars::prelude::*; |
838 | 0 | fn main() { |
839 | 0 | let result = #body; |
840 | 0 | #result_printing_logic |
841 | 0 | } |
842 | 0 | }), |
843 | | (false, true) => { |
844 | 0 | Ok(quote! { |
845 | 0 | use std::collections::HashMap; |
846 | 0 | fn main() { |
847 | 0 | let result = #body; |
848 | 0 | #result_printing_logic |
849 | 0 | } |
850 | 0 | }) |
851 | | }, |
852 | | (false, false) => { |
853 | 0 | Ok(quote! { |
854 | 0 | fn main() { |
855 | 0 | let result = #body; |
856 | 0 | #result_printing_logic |
857 | 0 | } |
858 | 0 | }) |
859 | | } |
860 | | } |
861 | 0 | } |
862 | | |
863 | | /// Transpiles an expression to a String |
864 | 0 | pub fn transpile_to_string(&self, expr: &Expr) -> Result<String> { |
865 | 0 | let tokens = self.transpile(expr)?; |
866 | | |
867 | | // Format the tokens with rustfmt-like style |
868 | 0 | let mut result = String::new(); |
869 | 0 | let token_str = tokens.to_string(); |
870 | | |
871 | | // Basic formatting: add newlines after semicolons and braces |
872 | 0 | for ch in token_str.chars() { |
873 | 0 | result.push(ch); |
874 | 0 | if ch == ';' || ch == '{' { |
875 | 0 | result.push('\n'); |
876 | 0 | } |
877 | | } |
878 | | |
879 | 0 | Ok(result) |
880 | 0 | } |
881 | | |
882 | | /// Generate minimal code for self-hosting (direct Rust mapping, no optimization) |
883 | 0 | pub fn transpile_minimal(&self, expr: &Expr) -> Result<String> { |
884 | 0 | codegen_minimal::MinimalCodeGen::gen_program(expr) |
885 | 0 | } |
886 | | |
887 | | /// Check if a name is a Rust reserved keyword |
888 | 0 | pub(crate) fn is_rust_reserved_keyword(name: &str) -> bool { |
889 | | // List of Rust reserved keywords that would conflict |
890 | 0 | matches!(name, |
891 | 0 | "as" | "break" | "const" | "continue" | "crate" | "else" | "enum" | "extern" | |
892 | 0 | "false" | "fn" | "for" | "if" | "impl" | "in" | "let" | "loop" | "match" | |
893 | 0 | "mod" | "move" | "mut" | "pub" | "ref" | "return" | "self" | "Self" | |
894 | 0 | "static" | "struct" | "super" | "trait" | "true" | "type" | "unsafe" | |
895 | 0 | "use" | "where" | "while" | "async" | "await" | "dyn" | "final" | "try" | |
896 | 0 | "abstract" | "become" | "box" | "do" | "override" | "priv" | "typeof" | |
897 | 0 | "unsized" | "virtual" | "yield" |
898 | | ) |
899 | 0 | } |
900 | | |
901 | | /// Main expression transpilation dispatcher |
902 | | /// |
903 | | /// # Panics |
904 | | /// |
905 | | /// Panics if label names cannot be parsed as valid Rust tokens |
906 | 0 | pub fn transpile_expr(&self, expr: &Expr) -> Result<TokenStream> { |
907 | | use ExprKind::{ |
908 | | Actor, ActorQuery, ActorSend, Ask, Assign, AsyncBlock, Await, Binary, Call, Command, CompoundAssign, DataFrame, |
909 | | DataFrameOperation, Err, FieldAccess, For, Function, Identifier, If, IfLet, IndexAccess, Lambda, |
910 | | List, ListComprehension, Literal, Loop, Macro, Match, MethodCall, None, ObjectLiteral, Ok, QualifiedName, |
911 | | Range, Send, Slice, Some, StringInterpolation, Struct, StructLiteral, Throw, Try, TryCatch, TypeCast, |
912 | | Tuple, Unary, While, WhileLet, |
913 | | }; |
914 | | |
915 | | // Dispatch to specialized handlers to keep complexity below 10 |
916 | 0 | match &expr.kind { |
917 | | // Basic expressions |
918 | | Literal(_) | Identifier(_) | QualifiedName { .. } | StringInterpolation { .. } | TypeCast { .. } => { |
919 | 0 | self.transpile_basic_expr(expr) |
920 | | } |
921 | | |
922 | | // Operators and control flow |
923 | | Binary { .. } |
924 | | | Unary { .. } |
925 | | | Assign { .. } |
926 | | | CompoundAssign { .. } |
927 | | | Await { .. } |
928 | | | AsyncBlock { .. } |
929 | | | If { .. } |
930 | | | IfLet { .. } |
931 | | | Match { .. } |
932 | | | For { .. } |
933 | | | While { .. } |
934 | | | WhileLet { .. } |
935 | | | Loop { .. } |
936 | 0 | | TryCatch { .. } => self.transpile_operator_control_expr(expr), |
937 | | |
938 | | // Functions |
939 | | Function { .. } | Lambda { .. } | Call { .. } | MethodCall { .. } | Macro { .. } => { |
940 | 0 | self.transpile_function_expr(expr) |
941 | | } |
942 | | |
943 | | // Structures |
944 | | Struct { .. } | StructLiteral { .. } | ObjectLiteral { .. } | FieldAccess { .. } |
945 | | | IndexAccess { .. } | Slice { .. } => { |
946 | 0 | self.transpile_struct_expr(expr) |
947 | | } |
948 | | |
949 | | // Data and error handling |
950 | | DataFrame { .. } |
951 | | | DataFrameOperation { .. } |
952 | | | List(_) |
953 | | | Tuple(_) |
954 | | | ListComprehension { .. } |
955 | | | Range { .. } |
956 | | | Throw { .. } |
957 | | | Ok { .. } |
958 | | | Err { .. } |
959 | | | Some { .. } |
960 | | | None |
961 | 0 | | Try { .. } => self.transpile_data_error_expr(expr), |
962 | | |
963 | | // Actor system and process execution |
964 | 0 | Actor { .. } | Send { .. } | Ask { .. } | ActorSend { .. } | ActorQuery { .. } | Command { .. } => self.transpile_actor_expr(expr), |
965 | | |
966 | | // Everything else |
967 | 0 | _ => self.transpile_misc_expr(expr), |
968 | | } |
969 | 0 | } |
970 | | } |