## 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
    }