Skip to main content

oak_csharp/builder/
mod.rs

1//! Builder implementation for C# that converts the green tree into a high-level AST.
2
3use crate::{ast::*, language::CSharpLanguage, lexer::token_type::CSharpTokenType, parser::CSharpElementType};
4use core::range::Range;
5use oak_core::{
6    GreenNode, Parser,
7    builder::{BuildOutput, Builder, BuilderCache},
8    source::{Source, TextEdit},
9    tree::red_tree::{RedNode, RedTree},
10};
11
12/// A builder that constructs a C# high-level AST from a green tree.
13///
14/// The `CSharpBuilder` traverses the red tree (which provides absolute spans)
15/// and maps its nodes to structured `ast` types.
16pub struct CSharpBuilder<'config> {
17    /// The language configuration.
18    language: &'config CSharpLanguage,
19}
20
21impl<'config> CSharpBuilder<'config> {
22    /// Creates a new `CSharpBuilder` with the given language configuration.
23    pub fn new(language: &'config CSharpLanguage) -> Self {
24        Self { language }
25    }
26
27    /// Builds a `CSharpRoot` from a green node.
28    fn build_root(&self, green: &GreenNode<CSharpLanguage>, source: &str) -> CSharpRoot {
29        let red = RedNode::new(green, 0);
30        let mut items = Vec::new();
31
32        for child in red.children() {
33            if let RedTree::Node(node) = child {
34                if let Some(item) = self.build_item(node, source) {
35                    items.push(item);
36                }
37            }
38        }
39
40        CSharpRoot { items }
41    }
42
43    /// Builds a top-level `Item` from a red node.
44    fn build_item(&self, node: RedNode<CSharpLanguage>, source: &str) -> Option<Item> {
45        match node.green.kind {
46            CSharpElementType::NamespaceDeclaration => Some(Item::Namespace(self.build_namespace(node, source))),
47            CSharpElementType::UsingDirective => Some(Item::Using(self.build_using(node, source))),
48            CSharpElementType::ClassDeclaration => Some(Item::Class(self.build_class(node, source))),
49            CSharpElementType::InterfaceDeclaration => Some(Item::Interface(self.build_interface(node, source))),
50            CSharpElementType::StructDeclaration => Some(Item::Struct(self.build_struct(node, source))),
51            CSharpElementType::EnumDeclaration => Some(Item::Enum(self.build_enum(node, source))),
52            CSharpElementType::RecordDeclaration => Some(Item::Record(self.build_record(node, source))),
53            CSharpElementType::DelegateDeclaration => Some(Item::Delegate(self.build_delegate(node, source))),
54            _ => None,
55        }
56    }
57
58    /// Gets a substring from the source text safely.
59    fn get_text<'a>(&self, span: Range<usize>, source: &'a str) -> &'a str {
60        let start = span.start;
61        let end = span.end;
62        if start > source.len() || end > source.len() || start > end {
63            return "";
64        }
65        &source[start..end]
66    }
67
68    /// Extracts an identifier string from a red node.
69    fn extract_identifier(&self, node: RedNode<CSharpLanguage>, source: &str) -> String {
70        for child in node.children() {
71            match child {
72                RedTree::Leaf(leaf) => {
73                    if leaf.kind == CSharpTokenType::Identifier {
74                        return self.get_text(leaf.span, source).trim().to_string();
75                    }
76                }
77                RedTree::Node(sub_node) => {
78                    if sub_node.kind::<CSharpElementType>() == CSharpElementType::IdentifierName {
79                        return self.get_text(sub_node.span(), source).trim().to_string();
80                    }
81                    let id = self.extract_identifier(sub_node, source);
82                    if !id.is_empty() {
83                        return id;
84                    }
85                }
86            }
87        }
88        String::new()
89    }
90
91    /// Extracts a list of attributes from a red node.
92    fn extract_attributes(&self, node: RedNode<CSharpLanguage>, source: &str) -> Vec<Attribute> {
93        let mut attributes = Vec::new();
94        for child in node.children() {
95            if let RedTree::Node(attr_node) = child {
96                if attr_node.green.kind == CSharpElementType::Attribute {
97                    let name = self.extract_identifier(attr_node.clone(), source);
98                    let span = attr_node.span();
99                    // TODO: Extract arguments
100                    attributes.push(Attribute { name, arguments: Vec::new(), span })
101                }
102            }
103        }
104        attributes
105    }
106
107    /// Builds a `NamespaceDeclaration` from a red node.
108    fn build_namespace(&self, node: RedNode<CSharpLanguage>, source: &str) -> NamespaceDeclaration {
109        let name = self.extract_identifier(node.clone(), source);
110        let attributes = self.extract_attributes(node.clone(), source);
111        let mut items = Vec::new();
112
113        for child in node.children() {
114            if let RedTree::Node(sub_node) = child {
115                if let Some(item) = self.build_item(sub_node, source) {
116                    items.push(item)
117                }
118            }
119        }
120
121        NamespaceDeclaration { name, attributes, items, span: node.span() }
122    }
123
124    fn build_using(&self, node: RedNode<CSharpLanguage>, source: &str) -> UsingDirective {
125        let path = self.extract_identifier(node.clone(), source);
126        let is_static = self.get_text(node.span(), source).contains("static");
127        let is_global = self.get_text(node.span(), source).contains("global");
128        UsingDirective {
129            path,
130            is_static,
131            alias: None, // TODO
132            is_global,
133            span: node.span(),
134        }
135    }
136
137    fn build_class(&self, node: RedNode<CSharpLanguage>, source: &str) -> ClassDeclaration {
138        let name = self.extract_identifier(node.clone(), source);
139        let mut members = Vec::new();
140        let modifiers = self.extract_modifiers(node.clone(), source);
141        let attributes = self.extract_attributes(node.clone(), source);
142
143        for child in node.children() {
144            if let RedTree::Node(sub_node) = child {
145                self.collect_members(sub_node, source, &mut members)
146            }
147        }
148
149        ClassDeclaration { name, attributes, modifiers, base_types: Vec::new(), type_parameters: Vec::new(), constraints: Vec::new(), members, span: node.span() }
150    }
151
152    fn build_interface(&self, node: RedNode<CSharpLanguage>, source: &str) -> InterfaceDeclaration {
153        let name = self.extract_identifier(node.clone(), source);
154        let modifiers = self.extract_modifiers(node.clone(), source);
155        let attributes = self.extract_attributes(node.clone(), source);
156        let mut members = Vec::new();
157        for child in node.children() {
158            if let RedTree::Node(sub_node) = child {
159                self.collect_members(sub_node, source, &mut members)
160            }
161        }
162        InterfaceDeclaration { name, attributes, modifiers, members, type_parameters: Vec::new(), span: node.span() }
163    }
164
165    fn build_struct(&self, node: RedNode<CSharpLanguage>, source: &str) -> StructDeclaration {
166        let name = self.extract_identifier(node.clone(), source);
167        let modifiers = self.extract_modifiers(node.clone(), source);
168        let attributes = self.extract_attributes(node.clone(), source);
169        let mut members = Vec::new();
170        for child in node.children() {
171            if let RedTree::Node(sub_node) = child {
172                self.collect_members(sub_node, source, &mut members)
173            }
174        }
175        StructDeclaration { name, attributes, modifiers, members, type_parameters: Vec::new(), span: node.span() }
176    }
177
178    fn build_enum(&self, node: RedNode<CSharpLanguage>, source: &str) -> EnumDeclaration {
179        let name = self.extract_identifier(node.clone(), source);
180        let modifiers = self.extract_modifiers(node.clone(), source);
181        let attributes = self.extract_attributes(node.clone(), source);
182        let mut members = Vec::new();
183        // Handle enum members
184        for child in node.children() {
185            if let RedTree::Node(sub_node) = child {
186                if sub_node.kind::<CSharpElementType>() == CSharpElementType::IdentifierName {
187                    members.push(EnumMember { name: self.get_text(sub_node.span(), source).to_string(), attributes: Vec::new(), value: None })
188                }
189            }
190        }
191        EnumDeclaration { name, attributes, modifiers, members, span: node.span() }
192    }
193
194    fn build_record(&self, node: RedNode<CSharpLanguage>, source: &str) -> RecordDeclaration {
195        let name = self.extract_identifier(node.clone(), source);
196        let modifiers = self.extract_modifiers(node.clone(), source);
197        let attributes = self.extract_attributes(node.clone(), source);
198        let mut members = Vec::new();
199        for child in node.children() {
200            if let RedTree::Node(sub_node) = child {
201                self.collect_members(sub_node, source, &mut members)
202            }
203        }
204        RecordDeclaration { name, attributes, modifiers, members, type_parameters: Vec::new(), span: node.span() }
205    }
206
207    fn build_delegate(&self, node: RedNode<CSharpLanguage>, source: &str) -> DelegateDeclaration {
208        let name = self.extract_identifier(node.clone(), source);
209        let modifiers = self.extract_modifiers(node.clone(), source);
210        let attributes = self.extract_attributes(node.clone(), source);
211        DelegateDeclaration { name, attributes, modifiers, return_type: "void".to_string(), type_parameters: Vec::new(), parameters: Vec::new(), span: node.span() }
212    }
213
214    fn extract_modifiers(&self, node: RedNode<CSharpLanguage>, source: &str) -> Vec<String> {
215        let mut modifiers = Vec::new();
216        for child in node.children() {
217            if let RedTree::Leaf(leaf) = child {
218                if leaf.kind.is_keyword() {
219                    let text = self.get_text(leaf.span, source).trim();
220                    match text {
221                        "public" | "private" | "protected" | "internal" | "static" | "readonly" | "abstract" | "virtual" | "override" | "async" | "volatile" | "sealed" | "extern" | "partial" | "new" | "unsafe" => modifiers.push(text.to_string()),
222                        _ => {}
223                    }
224                }
225            }
226        }
227        modifiers
228    }
229
230    fn collect_members(&self, node: RedNode<CSharpLanguage>, source: &str, members: &mut Vec<Member>) {
231        match node.green.kind {
232            CSharpElementType::MethodDeclaration => members.push(Member::Method(self.build_method(node, source))),
233            CSharpElementType::FieldDeclaration => members.push(Member::Field(self.build_field(node, source))),
234            CSharpElementType::PropertyDeclaration => members.push(Member::Property(self.build_property(node, source))),
235            CSharpElementType::IndexerDeclaration => members.push(Member::Indexer(self.build_indexer(node, source))),
236            CSharpElementType::EventDeclaration => members.push(Member::Event(self.build_event(node, source))),
237            _ => {
238                for child in node.children() {
239                    if let RedTree::Node(sub_node) = child {
240                        self.collect_members(sub_node, source, members)
241                    }
242                }
243            }
244        }
245    }
246
247    fn build_method(&self, node: RedNode<CSharpLanguage>, source: &str) -> MethodDeclaration {
248        let name = self.extract_identifier(node.clone(), source);
249        let modifiers = self.extract_modifiers(node.clone(), source);
250        let attributes = self.extract_attributes(node.clone(), source);
251        let is_async = modifiers.contains(&"async".to_string());
252
253        MethodDeclaration {
254            name,
255            attributes,
256            modifiers,
257            return_type: "void".to_string(), // TODO
258            type_parameters: Vec::new(),     // TODO
259            parameters: Vec::new(),          // TODO
260            body: self.build_body(node.clone(), source),
261            is_async,
262            span: node.span(),
263        }
264    }
265
266    fn build_field(&self, node: RedNode<CSharpLanguage>, source: &str) -> FieldDeclaration {
267        let name = self.extract_identifier(node.clone(), source);
268        let attributes = self.extract_attributes(node.clone(), source);
269        FieldDeclaration {
270            name,
271            attributes,
272            r#type: "object".to_string(), // TODO
273            modifiers: self.extract_modifiers(node, source),
274            initializer: None, // TODO
275            span: Range::default(),
276        }
277    }
278
279    fn build_property(&self, node: RedNode<CSharpLanguage>, source: &str) -> PropertyDeclaration {
280        let name = self.extract_identifier(node.clone(), source);
281        let attributes = self.extract_attributes(node.clone(), source);
282        PropertyDeclaration {
283            name,
284            attributes,
285            r#type: "object".to_string(), // TODO
286            modifiers: self.extract_modifiers(node, source),
287            get_accessor: None, // TODO
288            set_accessor: None, // TODO
289            span: node.span(),
290        }
291    }
292
293    fn build_indexer(&self, node: RedNode<CSharpLanguage>, source: &str) -> IndexerDeclaration {
294        let attributes = self.extract_attributes(node.clone(), source);
295        IndexerDeclaration {
296            attributes,
297            r#type: "object".to_string(), // TODO
298            parameters: Vec::new(),       // TODO
299            get_accessor: None,           // TODO
300            set_accessor: None,           // TODO
301            span: node.span(),
302        }
303    }
304
305    fn build_event(&self, node: RedNode<CSharpLanguage>, source: &str) -> EventDeclaration {
306        let name = self.extract_identifier(node.clone(), source);
307        let attributes = self.extract_attributes(node.clone(), source);
308        EventDeclaration {
309            name,
310            attributes,
311            r#type: "object".to_string(), // TODO
312            modifiers: self.extract_modifiers(node, source),
313            span: node.span(),
314        }
315    }
316
317    fn build_body(&self, node: RedNode<CSharpLanguage>, source: &str) -> Option<Vec<Statement>> {
318        for child in node.children() {
319            if let RedTree::Node(sub_node) = child {
320                if sub_node.green.kind == CSharpElementType::Block {
321                    let mut statements = Vec::new();
322                    for grandchild in sub_node.children() {
323                        if let RedTree::Node(grandchild_node) = grandchild {
324                            if let Some(stmt) = self.build_statement(grandchild_node, source) {
325                                statements.push(stmt)
326                            }
327                        }
328                    }
329                    return Some(statements);
330                }
331            }
332        }
333        None
334    }
335
336    fn build_statement(&self, node: RedNode<CSharpLanguage>, source: &str) -> Option<Statement> {
337        match node.green.kind {
338            CSharpElementType::ExpressionStatement => {
339                for child in node.children() {
340                    if let RedTree::Node(sub_node) = child {
341                        if let Some(expr) = self.build_expression(sub_node, source) {
342                            return Some(Statement::Expression(expr));
343                        }
344                    }
345                }
346                None
347            }
348            CSharpElementType::ReturnStatement => {
349                for child in node.children() {
350                    if let RedTree::Node(sub_node) = child {
351                        if let Some(expr) = self.build_expression(sub_node, source) {
352                            return Some(Statement::Return(Some(expr)));
353                        }
354                    }
355                }
356                Some(Statement::Return(None))
357            }
358            CSharpElementType::Block => {
359                let mut statements = Vec::new();
360                for child in node.children() {
361                    if let RedTree::Node(sub_node) = child {
362                        if let Some(stmt) = self.build_statement(sub_node, source) {
363                            statements.push(stmt)
364                        }
365                    }
366                }
367                Some(Statement::Block(statements))
368            }
369            CSharpElementType::IfStatement => {
370                // Simplified
371                Some(Statement::If { condition: Expression::Literal(Literal::Boolean(true)), then_branch: Box::new(Statement::Block(Vec::new())), else_branch: None })
372            }
373            CSharpElementType::BreakStatement => Some(Statement::Break),
374            CSharpElementType::ContinueStatement => Some(Statement::Continue),
375            _ => None,
376        }
377    }
378
379    fn build_expression(&self, node: RedNode<CSharpLanguage>, source: &str) -> Option<Expression> {
380        match node.green.kind {
381            CSharpElementType::LiteralExpression => {
382                let text = self.get_text(node.span(), source).trim();
383                if text == "true" {
384                    Some(Expression::Literal(Literal::Boolean(true)))
385                }
386                else if text == "false" {
387                    Some(Expression::Literal(Literal::Boolean(false)))
388                }
389                else if text == "null" {
390                    Some(Expression::Literal(Literal::Null))
391                }
392                else if let Ok(n) = text.parse::<i64>() {
393                    Some(Expression::Literal(Literal::Integer(n)))
394                }
395                else {
396                    Some(Expression::Literal(Literal::String(text.to_string())))
397                }
398            }
399            CSharpElementType::IdentifierName => Some(Expression::Identifier(self.get_text(node.span(), source).trim().to_string())),
400            CSharpElementType::AwaitExpression => {
401                for child in node.children() {
402                    if let RedTree::Node(sub_node) = child {
403                        if let Some(expr) = self.build_expression(sub_node, source) {
404                            return Some(Expression::Await(Box::new(expr)));
405                        }
406                    }
407                }
408                None
409            }
410            _ => None,
411        }
412    }
413}
414
415impl<'config> Builder<CSharpLanguage> for CSharpBuilder<'config> {
416    fn build<'a, S: Source + ?Sized>(&self, source: &'a S, edits: &[TextEdit], _cache: &'a mut impl BuilderCache<CSharpLanguage>) -> BuildOutput<CSharpLanguage> {
417        let mut session = oak_core::parser::ParseSession::<CSharpLanguage>::default();
418        let parser = crate::parser::CSharpParser::new(self.language);
419        let output = parser.parse(source, edits, &mut session);
420
421        let mut result = Err(oak_core::OakError::custom_error("Build failed"));
422        if let Ok(green) = &output.result {
423            let root = self.build_root(green, source.get_text_in((0..source.length()).into()).as_ref());
424            result = Ok(root)
425        }
426
427        oak_core::errors::OakDiagnostics { result, diagnostics: output.diagnostics }
428    }
429}