Coverage Report

Created: 2025-09-08 21:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}