Skip to main content

oak_rust/builder/
build_generics.rs

1use crate::{ast::*, language::RustLanguage, lexer::RustTokenType, parser::RustElementType};
2use core::range::Range;
3use oak_core::{BuilderCache, RedNode, RedTree, SourceText};
4
5impl<'config> super::RustBuilder<'config> {
6    /// Builds generic parameters and where clauses from a node's children.
7    pub fn build_generics_and_where(&self, node: RedNode<RustLanguage>, source: &SourceText) -> Result<Option<Generics>, oak_core::OakError> {
8        let mut params = Vec::new();
9        let mut where_clause = None;
10        let mut generics_span: Option<Range<usize>> = None;
11
12        let children: Vec<_> = node.children().collect();
13        let mut i = 0;
14        while i < children.len() {
15            match &children[i] {
16                RedTree::Leaf(t) if t.kind == RustTokenType::Lt => {
17                    let start = t.span.start;
18                    let mut j = i + 1;
19                    while j < children.len() {
20                        if let RedTree::Leaf(t_end) = &children[j] {
21                            if t_end.kind == RustTokenType::Gt {
22                                generics_span = Some(Range { start, end: t_end.span.end });
23                                // Parse parameters inside < >
24                                params = self.parse_generic_params(&children[i + 1..j], source)?;
25                                i = j;
26                                break;
27                            }
28                        }
29                        j += 1;
30                    }
31                }
32                RedTree::Leaf(t) if t.kind == RustTokenType::Where => {
33                    let start = t.span.start;
34                    // Find where the where clause ends (usually before a { or ;)
35                    let mut j = i + 1;
36                    while j < children.len() {
37                        match &children[j] {
38                            RedTree::Leaf(t_end) if t_end.kind == RustTokenType::LeftBrace || t_end.kind == RustTokenType::Semicolon => {
39                                let end = t_end.span.start;
40                                where_clause = Some(self.parse_where_clause(&children[i + 1..j], Range { start, end }, source)?);
41                                i = j - 1;
42                                break;
43                            }
44                            _ => j += 1,
45                        }
46                    }
47                }
48                _ => {}
49            }
50            i += 1;
51        }
52
53        if params.is_empty() && where_clause.is_none() {
54            Ok(None)
55        }
56        else {
57            let span = match (generics_span, &where_clause) {
58                (Some(g), Some(w)) => Range { start: g.start, end: w.span.end },
59                (Some(g), None) => g,
60                (None, Some(w)) => w.span.clone(),
61                (None, None) => Range { start: 0, end: 0 },
62            };
63            Ok(Some(Generics { params, where_clause, span }))
64        }
65    }
66
67    fn parse_generic_params(&self, children: &[RedTree<RustLanguage>], source: &SourceText) -> Result<Vec<GenericParam>, oak_core::OakError> {
68        let mut params = Vec::new();
69        let mut current_start = 0;
70
71        while current_start < children.len() {
72            // Find next comma or end
73            let mut current_end = current_start;
74            while current_end < children.len() {
75                if let RedTree::Leaf(t) = &children[current_end] {
76                    if t.kind == RustTokenType::Comma {
77                        break;
78                    }
79                }
80                current_end += 1;
81            }
82
83            if current_start < current_end {
84                params.push(self.parse_single_generic_param(&children[current_start..current_end], source)?);
85            }
86
87            current_start = current_end + 1;
88        }
89
90        Ok(params)
91    }
92
93    fn parse_single_generic_param(&self, children: &[RedTree<RustLanguage>], source: &SourceText) -> Result<GenericParam, oak_core::OakError> {
94        if children.is_empty() {
95            return Err(oak_core::OakError::syntax_error("Empty generic parameter".to_string(), 0, None));
96        }
97
98        // Handle attributes (skipped for now but structure is here)
99        let mut i = 0;
100        while i < children.len() {
101            if let RedTree::Leaf(t) = &children[i] {
102                if t.kind == RustTokenType::Hash {
103                    // Skip attribute for now
104                    let mut j = i + 1;
105                    let mut bracket_count = 0;
106                    while j < children.len() {
107                        if let RedTree::Leaf(t_inner) = &children[j] {
108                            if t_inner.kind == RustTokenType::LeftBracket {
109                                bracket_count += 1;
110                            }
111                            else if t_inner.kind == RustTokenType::RightBracket {
112                                bracket_count -= 1;
113                                if bracket_count == 0 {
114                                    i = j;
115                                    break;
116                                }
117                            }
118                        }
119                        j += 1;
120                    }
121                }
122                else {
123                    break;
124                }
125            }
126            else {
127                break;
128            }
129            i += 1;
130        }
131
132        let remaining = &children[i..];
133        if remaining.is_empty() {
134            return Err(oak_core::OakError::syntax_error("Generic parameter name missing after attributes".to_string(), children[0].span().start, None));
135        }
136
137        // Lifetime parameter: 'a: 'b + 'c
138        if let RedTree::Leaf(t) = &remaining[0] {
139            if t.kind == RustTokenType::Lifetime {
140                let lifetime = Lifetime { name: crate::builder::text(source, t.span.clone().into()), span: t.span.clone().into() };
141
142                let mut bounds = Vec::new();
143                if remaining.len() > 1 {
144                    if let RedTree::Leaf(t_colon) = &remaining[1] {
145                        if t_colon.kind == RustTokenType::Colon {
146                            for child in &remaining[2..] {
147                                if let RedTree::Leaf(t_bound) = child {
148                                    if t_bound.kind == RustTokenType::Lifetime {
149                                        bounds.push(Lifetime { name: crate::builder::text(source, t_bound.span.clone().into()), span: t_bound.span.clone().into() });
150                                    }
151                                }
152                            }
153                        }
154                    }
155                }
156                // Update GenericParam::Lifetime to include bounds if AST supports it,
157                // but currently AST GenericParam::Lifetime(Lifetime) only takes one Lifetime.
158                // We'll stick to the current AST but noted the limitation.
159                return Ok(GenericParam::Lifetime(lifetime));
160            }
161        }
162
163        // Const parameter: const N: usize = 10
164        if let RedTree::Leaf(t) = &remaining[0] {
165            if t.kind == RustTokenType::Const {
166                let mut name = Identifier { name: String::new(), span: Range { start: 0, end: 0 } };
167                let mut ty = Type::Path("usize".to_string()); // Default fallback
168                let mut default = None;
169
170                let mut j = 1;
171                while j < remaining.len() {
172                    match &remaining[j] {
173                        RedTree::Leaf(t_id) if t_id.kind == RustTokenType::Identifier => {
174                            name.name = crate::builder::text(source, t_id.span.clone().into());
175                            name.span = t_id.span.clone().into();
176                        }
177                        RedTree::Leaf(t_colon) if t_colon.kind == RustTokenType::Colon => {
178                            if j + 1 < remaining.len() {
179                                if let RedTree::Node(n_ty) = &remaining[j + 1] {
180                                    ty = self.build_type(n_ty.clone(), source)?;
181                                    j += 1;
182                                }
183                            }
184                        }
185                        RedTree::Leaf(t_eq) if t_eq.kind == RustTokenType::Eq => {
186                            if j + 1 < remaining.len() {
187                                if let RedTree::Node(n_expr) = &remaining[j + 1] {
188                                    default = Some(self.build_expr(n_expr.clone(), source)?);
189                                    j += 1;
190                                }
191                            }
192                        }
193                        _ => {}
194                    }
195                    j += 1;
196                }
197                return Ok(GenericParam::Const { name, ty, default });
198            }
199        }
200
201        // Type parameter: T: Bound1 + Bound2 = DefaultType
202        let mut name = Identifier { name: String::new(), span: Range { start: 0, end: 0 } };
203        let mut bounds = Vec::new();
204        let mut default = None;
205
206        let mut j = 0;
207        while j < remaining.len() {
208            match &remaining[j] {
209                RedTree::Leaf(t_id) if t_id.kind == RustTokenType::Identifier => {
210                    name.name = crate::builder::text(source, t_id.span.clone().into());
211                    name.span = t_id.span.clone().into();
212                }
213                RedTree::Leaf(t_colon) if t_colon.kind == RustTokenType::Colon => {
214                    let mut k = j + 1;
215                    while k < remaining.len() {
216                        if let RedTree::Leaf(t_eq) = &remaining[k] {
217                            if t_eq.kind == RustTokenType::Eq {
218                                break;
219                            }
220                        }
221                        k += 1;
222                    }
223                    bounds = self.parse_type_param_bounds(&remaining[j + 1..k], source)?;
224                    j = k - 1;
225                }
226                RedTree::Leaf(t_eq) if t_eq.kind == RustTokenType::Eq => {
227                    if j + 1 < remaining.len() {
228                        if let RedTree::Node(n_ty) = &remaining[j + 1] {
229                            default = Some(self.build_type(n_ty.clone(), source)?);
230                            j += 1;
231                        }
232                    }
233                }
234                _ => {}
235            }
236            j += 1;
237        }
238
239        Ok(GenericParam::Type { name, bounds, default })
240    }
241
242    fn parse_type_param_bounds(&self, children: &[RedTree<RustLanguage>], source: &SourceText) -> Result<Vec<TypeParamBound>, oak_core::OakError> {
243        let mut bounds = Vec::new();
244        for child in children {
245            match child {
246                RedTree::Leaf(t) if t.kind == RustTokenType::Lifetime => {
247                    bounds.push(TypeParamBound::Lifetime(Lifetime { name: crate::builder::text(source, t.span.clone().into()), span: t.span.clone().into() }));
248                }
249                RedTree::Node(n) => {
250                    bounds.push(TypeParamBound::Trait(self.build_type(n.clone(), source)?));
251                }
252                _ => {}
253            }
254        }
255        Ok(bounds)
256    }
257
258    fn parse_where_clause(&self, children: &[RedTree<RustLanguage>], span: Range<usize>, source: &SourceText) -> Result<WhereClause, oak_core::OakError> {
259        let mut predicates = Vec::new();
260        let mut current_start = 0;
261
262        while current_start < children.len() {
263            let mut current_end = current_start;
264            while current_end < children.len() {
265                if let RedTree::Leaf(t) = &children[current_end] {
266                    if t.kind == RustTokenType::Comma {
267                        break;
268                    }
269                }
270                current_end += 1;
271            }
272
273            if current_start < current_end {
274                predicates.push(self.parse_where_predicate(&children[current_start..current_end], source)?);
275            }
276
277            current_start = current_end + 1;
278        }
279
280        Ok(WhereClause { predicates, span })
281    }
282
283    fn parse_where_predicate(&self, children: &[RedTree<RustLanguage>], source: &SourceText) -> Result<WherePredicate, oak_core::OakError> {
284        let mut colon_idx = None;
285        for (i, child) in children.iter().enumerate() {
286            if let RedTree::Leaf(t) = child {
287                if t.kind == RustTokenType::Colon {
288                    colon_idx = Some(i);
289                    break;
290                }
291            }
292        }
293
294        if let Some(idx) = colon_idx {
295            let left = &children[..idx];
296            let right = &children[idx + 1..];
297
298            if left.len() == 1 {
299                if let RedTree::Leaf(t) = &left[0] {
300                    if t.kind == RustTokenType::Lifetime {
301                        let lifetime = Lifetime { name: crate::builder::text(source, t.span.clone().into()), span: t.span.clone().into() };
302                        let mut bounds = Vec::new();
303                        for child in right {
304                            if let RedTree::Leaf(t_bound) = child {
305                                if t_bound.kind == RustTokenType::Lifetime {
306                                    bounds.push(Lifetime { name: crate::builder::text(source, t_bound.span.clone().into()), span: t_bound.span.clone().into() });
307                                }
308                            }
309                        }
310                        return Ok(WherePredicate::Lifetime { lifetime, bounds });
311                    }
312                }
313            }
314
315            let bounded_ty = if left.len() == 1 {
316                if let RedTree::Node(n) = &left[0] { self.build_type(n.clone(), source)? } else { Type::Path(crate::builder::text(source, left[0].span().into())) }
317            }
318            else {
319                Type::Path(crate::builder::text(source, Range { start: left[0].span().start, end: left.last().unwrap().span().end }))
320            };
321
322            let bounds = self.parse_type_param_bounds(right, source)?;
323            Ok(WherePredicate::Type { bounded_ty, bounds })
324        }
325        else {
326            Err(oak_core::OakError::syntax_error("Missing colon in where predicate".to_string(), children[0].span().start, None))
327        }
328    }
329}