/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; |