## 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/patterns.rs

    Line
    Count
    Source
    1
    //! Pattern matching transpilation
    2
    3
    #![allow(clippy::missing_errors_doc)]
    4
    #![allow(clippy::only_used_in_recursion)]
    5
    6
    use super::Transpiler;
    7
    use crate::frontend::ast::{Expr, MatchArm, Pattern};
    8
    use anyhow::Result;
    9
    use proc_macro2::TokenStream;
    10
    use quote::{format_ident, quote};
    11
    12
    impl Transpiler {
    13
        /// Transpiles match expressions
    14
        ///
    15
        /// # Examples
    16
        ///
    17
        /// ```
    18
        /// use ruchy::{Transpiler, Parser};
    19
        ///
    20
        /// let transpiler = Transpiler::new();
    21
        /// let mut parser = Parser::new(r#"match x { 1 => "one", _ => "other" }"#);
    22
        /// let ast = parser.parse().unwrap();
    23
        ///
    24
        /// let result = transpiler.transpile(&ast).unwrap();
    25
        /// let code = result.to_string();
    26
        /// assert!(code.contains("match"));
    27
        /// assert!(code.contains("1 =>"));
    28
        /// assert!(code.contains("_ =>"));
    29
        /// ```
    30
    0
        pub fn transpile_match(&self, expr: &Expr, arms: &[MatchArm]) -> Result<TokenStream> {
    31
    0
            let expr_tokens = self.transpile_expr(expr)?;
    32
    33
    0
            let mut arm_tokens = Vec::new();
    34
    0
            for arm in arms {
    35
    0
                let pattern_tokens = self.transpile_pattern(&arm.pattern)?;
    36
    0
                let body_tokens = self.transpile_expr(&arm.body)?;
    37
    38
                // Handle pattern guards if present
    39
    0
                if let Some(guard_expr) = &arm.guard {
    40
    0
                    let guard_tokens = self.transpile_expr(guard_expr)?;
    41
    0
                    arm_tokens.push(quote! {
    42
                        #pattern_tokens if #guard_tokens => #body_tokens
    43
                    });
    44
    0
                } else {
    45
    0
                    arm_tokens.push(quote! {
    46
    0
                        #pattern_tokens => #body_tokens
    47
    0
                    });
    48
    0
                }
    49
            }
    50
    51
    0
            Ok(quote! {
    52
                match #expr_tokens {
    53
                    #(#arm_tokens,)*
    54
                }
    55
            })
    56
    0
        }
    57
    58
        /// Transpiles patterns
    59
        ///
    60
        /// # Examples
    61
        ///
    62
        /// ```
    63
        /// use ruchy::{Transpiler, Parser};
    64
        ///
    65
        /// // Wildcard pattern
    66
        /// let transpiler = Transpiler::new();
    67
        /// let mut parser = Parser::new("match x { _ => 42 }");
    68
        /// let ast = parser.parse().unwrap();
    69
        /// let result = transpiler.transpile(&ast).unwrap();
    70
        /// assert!(result.to_string().contains("_ =>"));
    71
        /// ```
    72
        ///
    73
        /// ```
    74
        /// use ruchy::{Transpiler, Parser};
    75
        ///
    76
        /// // Identifier pattern
    77
        /// let transpiler = Transpiler::new();
    78
        /// let mut parser = Parser::new("match x { y => y + 1 }");
    79
        /// let ast = parser.parse().unwrap();
    80
        /// let result = transpiler.transpile(&ast).unwrap();
    81
        /// assert!(result.to_string().contains("y =>"));
    82
        /// ```
    83
        ///
    84
        /// ```
    85
        /// use ruchy::{Transpiler, Parser};
    86
        ///
    87
        /// // Tuple pattern
    88
        /// let transpiler = Transpiler::new();
    89
        /// let mut parser = Parser::new("match pair { (a, b) => a + b }");
    90
        /// let ast = parser.parse().unwrap();
    91
        /// let result = transpiler.transpile(&ast).unwrap();
    92
        /// assert!(result.to_string().contains("("));
    93
        /// assert!(result.to_string().contains("a"));
    94
        /// assert!(result.to_string().contains("b"));
    95
        /// ```
    96
    0
        pub fn transpile_pattern(&self, pattern: &Pattern) -> Result<TokenStream> {
    97
    0
            match pattern {
    98
    0
                Pattern::Wildcard => Ok(quote! { _ }),
    99
    0
                Pattern::Literal(lit) => Ok(Self::transpile_literal(lit)),
    100
    0
                Pattern::Identifier(name) => {
    101
    0
                    let ident = format_ident!("{}", name);
    102
    0
                    Ok(quote! { #ident })
    103
                }
    104
    0
                Pattern::QualifiedName(path) => {
    105
                    // Generate qualified path like Ordering::Less
    106
    0
                    let segments: Vec<_> = path.iter().map(|s| format_ident!("{}", s)).collect();
    107
    0
                    Ok(quote! { #(#segments)::* })
    108
                }
    109
    0
                Pattern::Tuple(patterns) => {
    110
    0
                    let pattern_tokens: Result<Vec<_>> =
    111
    0
                        patterns.iter().map(|p| self.transpile_pattern(p)).collect();
    112
    0
                    let pattern_tokens = pattern_tokens?;
    113
    0
                    Ok(quote! { (#(#pattern_tokens),*) })
    114
                }
    115
    0
                Pattern::List(patterns) => {
    116
    0
                    if patterns.is_empty() {
    117
    0
                        Ok(quote! { [] })
    118
                    } else {
    119
                        // Check for rest pattern
    120
    0
                        let has_rest = patterns.iter().any(|p| matches!(p, Pattern::Rest | Pattern::RestNamed(_)));
    121
    122
    0
                        if has_rest {
    123
                            // Handle patterns with rest
    124
    0
                            let mut pattern_tokens = Vec::new();
    125
    0
                            for p in patterns {
    126
    0
                                match p {
    127
    0
                                    Pattern::Rest => pattern_tokens.push(quote! { .. }),
    128
    0
                                    Pattern::RestNamed(name) => {
    129
    0
                                        let name_ident = format_ident!("{}", name);
    130
    0
                                        pattern_tokens.push(quote! { ..#name_ident });
    131
    0
                                    }
    132
    0
                                    _ => pattern_tokens.push(self.transpile_pattern(p)?),
    133
                                }
    134
                            }
    135
    0
                            Ok(quote! { [#(#pattern_tokens),*] })
    136
                        } else {
    137
                            // Simple list pattern
    138
    0
                            let pattern_tokens: Result<Vec<_>> =
    139
    0
                                patterns.iter().map(|p| self.transpile_pattern(p)).collect();
    140
    0
                            let pattern_tokens = pattern_tokens?;
    141
    0
                            Ok(quote! { [#(#pattern_tokens),*] })
    142
                        }
    143
                    }
    144
                }
    145
    0
                Pattern::Struct { name, fields, has_rest } => {
    146
                    // Handle anonymous struct destructuring for objects
    147
    0
                    if name.is_empty() {
    148
                        // For nested patterns within tuples/arrays, we need to generate a wildcard
    149
                        // The actual field extraction will be handled by special transpilation logic
    150
    0
                        return Ok(quote! { _ });
    151
    0
                    }
    152
    153
    0
                    let struct_name = format_ident!("{}", name);
    154
    155
    0
                    if fields.is_empty() {
    156
    0
                        Ok(quote! { #struct_name {} })
    157
                    } else {
    158
    0
                        let field_patterns: Result<Vec<_>> = fields
    159
    0
                            .iter()
    160
    0
                            .map(|field| {
    161
    0
                                let field_ident = format_ident!("{}", field.name);
    162
    0
                                if let Some(ref pattern) = field.pattern {
    163
    0
                                    let pattern_tokens = self.transpile_pattern(pattern)?;
    164
    0
                                    Ok(quote! { #field_ident: #pattern_tokens })
    165
                                } else {
    166
                                    // Shorthand field pattern
    167
    0
                                    Ok(quote! { #field_ident })
    168
                                }
    169
    0
                            })
    170
    0
                            .collect();
    171
    0
                        let field_patterns = field_patterns?;
    172
    0
                        if *has_rest {
    173
    0
                            Ok(quote! { #struct_name { #(#field_patterns),*, .. } })
    174
                        } else {
    175
    0
                            Ok(quote! { #struct_name { #(#field_patterns),* } })
    176
                        }
    177
                    }
    178
                }
    179
    0
                Pattern::Or(patterns) => {
    180
    0
                    let pattern_tokens: Result<Vec<_>> =
    181
    0
                        patterns.iter().map(|p| self.transpile_pattern(p)).collect();
    182
    0
                    let pattern_tokens = pattern_tokens?;
    183
    184
                    // Join patterns with |
    185
    0
                    let mut result = TokenStream::new();
    186
    0
                    for (i, tokens) in pattern_tokens.iter().enumerate() {
    187
    0
                        if i > 0 {
    188
    0
                            result.extend(quote! { | });
    189
    0
                        }
    190
    0
                        result.extend(tokens.clone());
    191
                    }
    192
    0
                    Ok(result)
    193
                }
    194
                Pattern::Range {
    195
    0
                    start,
    196
    0
                    end,
    197
    0
                    inclusive,
    198
                } => {
    199
    0
                    let start_tokens = self.transpile_pattern(start)?;
    200
    0
                    let end_tokens = self.transpile_pattern(end)?;
    201
    202
    0
                    if *inclusive {
    203
    0
                        Ok(quote! { #start_tokens..=#end_tokens })
    204
                    } else {
    205
    0
                        Ok(quote! { #start_tokens..#end_tokens })
    206
                    }
    207
                }
    208
    0
                Pattern::Rest => Ok(quote! { .. }),
    209
    0
                Pattern::RestNamed(name) => {
    210
    0
                    let name_ident = format_ident!("{}", name);
    211
    0
                    Ok(quote! { ..#name_ident })
    212
                }
    213
    0
                Pattern::Ok(pattern) => {
    214
    0
                    let inner = self.transpile_pattern(pattern)?;
    215
    0
                    Ok(quote! { Ok(#inner) })
    216
                }
    217
    0
                Pattern::Err(pattern) => {
    218
    0
                    let inner = self.transpile_pattern(pattern)?;
    219
    0
                    Ok(quote! { Err(#inner) })
    220
                }
    221
    0
                Pattern::Some(pattern) => {
    222
    0
                    let inner = self.transpile_pattern(pattern)?;
    223
    0
                    Ok(quote! { Some(#inner) })
    224
                }
    225
    0
                Pattern::None => Ok(quote! { None }),
    226
    0
                Pattern::WithDefault { pattern, .. } => {
    227
                    // For pattern transpilation, we just transpile the inner pattern
    228
                    // The default handling is done in the destructuring code
    229
    0
                    self.transpile_pattern(pattern)
    230
                }
    231
            }
    232
    0
        }
    233
    }
    234
    235
    #[cfg(test)]
    236
    #[path = "patterns_tests.rs"]
    237
    mod tests;