/home/noah/src/ruchy/src/backend/transpiler/dataframe_builder.rs
Line | Count | Source |
1 | | //! `DataFrame` builder pattern transpilation for correct Polars API |
2 | | //! |
3 | | //! Transforms Ruchy's builder pattern into valid Polars code |
4 | | |
5 | | use super::Transpiler; |
6 | | use anyhow::Result; |
7 | | use proc_macro2::TokenStream; |
8 | | use quote::quote; |
9 | | use crate::frontend::ast::{Expr, ExprKind}; |
10 | | |
11 | | impl Transpiler { |
12 | | /// Transpile `DataFrame` builder pattern chains |
13 | | /// Transforms: `DataFrame::new().column("a", [1,2]).column("b", [3,4]).build()` |
14 | | /// Into: `DataFrame::new(vec![Series::new("a", &[1,2]), Series::new("b", &[3,4])])` |
15 | 0 | pub fn transpile_dataframe_builder(&self, expr: &Expr) -> Result<Option<TokenStream>> { |
16 | | // Check if this is a DataFrame builder pattern |
17 | 0 | if let Some((columns, _base)) = self.extract_dataframe_builder_chain(expr) { |
18 | | // Generate Series for each column |
19 | 0 | let mut series_tokens = Vec::new(); |
20 | 0 | for (name, data) in columns { |
21 | 0 | let name_tokens = self.transpile_expr(&name)?; |
22 | 0 | let data_tokens = self.transpile_expr(&data)?; |
23 | 0 | series_tokens.push(quote! { |
24 | | polars::prelude::Series::new(#name_tokens, &#data_tokens) |
25 | | }); |
26 | | } |
27 | | |
28 | | // Generate the DataFrame constructor |
29 | 0 | if series_tokens.is_empty() { |
30 | 0 | Ok(Some(quote! { polars::prelude::DataFrame::empty() })) |
31 | | } else { |
32 | 0 | Ok(Some(quote! { |
33 | | polars::prelude::DataFrame::new(vec![#(#series_tokens),*]).unwrap() |
34 | | })) |
35 | | } |
36 | | } else { |
37 | 0 | Ok(None) |
38 | | } |
39 | 0 | } |
40 | | |
41 | | /// Extract `DataFrame` builder chain pattern |
42 | | /// Returns columns and base expression if it's a builder pattern |
43 | 0 | fn extract_dataframe_builder_chain(&self, expr: &Expr) -> Option<(Vec<(Expr, Expr)>, Expr)> { |
44 | 0 | match &expr.kind { |
45 | | // .build() at the end |
46 | 0 | ExprKind::MethodCall { receiver, method, args } if method == "build" && args.is_empty() => { |
47 | 0 | self.extract_column_chain(receiver) |
48 | | } |
49 | | // Just column chains without .build() |
50 | 0 | ExprKind::MethodCall { receiver, method, args } if method == "column" && args.len() == 2 => { |
51 | 0 | if let Some((mut cols, base)) = self.extract_column_chain(receiver) { |
52 | 0 | cols.push((args[0].clone(), args[1].clone())); |
53 | 0 | Some((cols, base)) |
54 | | } else { |
55 | 0 | Some((vec![(args[0].clone(), args[1].clone())], receiver.as_ref().clone())) |
56 | | } |
57 | | } |
58 | 0 | _ => None |
59 | | } |
60 | 0 | } |
61 | | |
62 | | /// Extract column method calls recursively |
63 | 0 | fn extract_column_chain(&self, expr: &Expr) -> Option<(Vec<(Expr, Expr)>, Expr)> { |
64 | 0 | match &expr.kind { |
65 | 0 | ExprKind::MethodCall { receiver, method, args } if method == "column" && args.len() == 2 => { |
66 | 0 | if let Some((mut cols, base)) = self.extract_column_chain(receiver) { |
67 | 0 | cols.push((args[0].clone(), args[1].clone())); |
68 | 0 | Some((cols, base)) |
69 | | } else { |
70 | | // Base case: reached the DataFrame::new() |
71 | 0 | Some((vec![(args[0].clone(), args[1].clone())], receiver.as_ref().clone())) |
72 | | } |
73 | | } |
74 | 0 | ExprKind::Call { func, args } => { |
75 | | // Check if it's DataFrame::new() |
76 | 0 | if let ExprKind::QualifiedName { module, name } = &func.kind { |
77 | 0 | if module == "DataFrame" && name == "new" && args.is_empty() { |
78 | 0 | return Some((Vec::new(), expr.clone())); |
79 | 0 | } |
80 | 0 | } |
81 | 0 | None |
82 | | } |
83 | 0 | _ => None |
84 | | } |
85 | 0 | } |
86 | | |
87 | | /// Check if expression is a `DataFrame` builder pattern |
88 | 0 | pub fn is_dataframe_builder(&self, expr: &Expr) -> bool { |
89 | 0 | match &expr.kind { |
90 | 0 | ExprKind::MethodCall { method, .. } => { |
91 | 0 | matches!(method.as_str(), "column" | "build") |
92 | | } |
93 | 0 | ExprKind::Call { func, .. } => { |
94 | 0 | if let ExprKind::QualifiedName { module, name } = &func.kind { |
95 | 0 | module == "DataFrame" && name == "new" |
96 | | } else { |
97 | 0 | false |
98 | | } |
99 | | } |
100 | 0 | _ => false |
101 | | } |
102 | 0 | } |
103 | | } |