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;