Skip to main content

oak_vbnet/builder/
mod.rs

1#![doc = include_str!("readme.md")]
2
3use crate::{ast::*, language::VbNetLanguage, lexer::token_type::VbNetTokenType, parser::element_type::VbNetElementType};
4use oak_core::{
5    builder::Builder,
6    parser::Parser,
7    source::Source,
8    tree::{GreenNode, GreenTree},
9};
10
11/// VB.NET AST builder
12pub struct VbNetBuilder<'config> {
13    config: &'config VbNetLanguage,
14}
15
16impl<'config> VbNetBuilder<'config> {
17    /// Creates a new VB.NET builder
18    pub fn new(config: &'config VbNetLanguage) -> Self {
19        Self { config }
20    }
21}
22
23impl<'config> Builder<VbNetLanguage> for VbNetBuilder<'config> {
24    fn build<'a, S: Source + ?Sized>(&self, source: &'a S, edits: &[oak_core::TextEdit], cache: &'a mut impl oak_core::builder::BuilderCache<VbNetLanguage>) -> oak_core::builder::BuildOutput<VbNetLanguage> {
25        let parser = crate::parser::VbNetParser::new(self.config);
26        let parse_output = parser.parse(source, edits, cache);
27
28        match parse_output.result {
29            Ok(green_node) => {
30                let ast = self.build_ast(&green_node, source);
31                oak_core::errors::OakDiagnostics { result: Ok(ast), diagnostics: parse_output.diagnostics }
32            }
33            Err(err) => oak_core::errors::OakDiagnostics { result: Err(err), diagnostics: parse_output.diagnostics },
34        }
35    }
36}
37
38impl<'config> VbNetBuilder<'config> {
39    fn build_ast<'a, S: Source + ?Sized>(&self, node: &GreenNode<'a, VbNetLanguage>, source: &'a S) -> VbNetRoot {
40        let mut items = Vec::new();
41        self.build_items(node, source, &mut items);
42        VbNetRoot { items }
43    }
44
45    fn build_items<'a, S: Source + ?Sized>(&self, node: &GreenNode<'a, VbNetLanguage>, source: &'a S, items: &mut Vec<Item>) {
46        for child in node.children() {
47            match child {
48                GreenTree::Node(child_node) => {
49                    match child_node.kind() {
50                        VbNetElementType::Namespace => {
51                            if let Some(namespace) = self.build_namespace(child_node, source) {
52                                items.push(Item::Namespace(namespace));
53                            }
54                        }
55                        VbNetElementType::Imports => {
56                            if let Some(imports) = self.build_imports(child_node, source) {
57                                items.push(Item::Imports(imports));
58                            }
59                        }
60                        VbNetElementType::Class => {
61                            if let Some(class) = self.build_class(child_node, source) {
62                                items.push(Item::Class(class));
63                            }
64                        }
65                        VbNetElementType::Function => {
66                            if let Some(function) = self.build_function(child_node, source) {
67                                items.push(Item::Function(function));
68                            }
69                        }
70                        VbNetElementType::Sub => {
71                            if let Some(sub) = self.build_sub(child_node, source) {
72                                items.push(Item::Sub(sub));
73                            }
74                        }
75                        VbNetElementType::Property => {
76                            if let Some(property) = self.build_property(child_node, source) {
77                                items.push(Item::Property(property));
78                            }
79                        }
80                        VbNetElementType::Dim => {
81                            if let Some(variable) = self.build_variable(child_node, source) {
82                                items.push(Item::Variable(variable));
83                            }
84                        }
85                        _ => {
86                            // Recursively process child nodes
87                            self.build_items(child_node, source, items);
88                        }
89                    }
90                }
91                GreenTree::Leaf(_) => {
92                    // Skip leaf nodes
93                }
94            }
95        }
96    }
97
98    fn build_namespace<'a, S: Source + ?Sized>(&self, node: &GreenNode<'a, VbNetLanguage>, source: &'a S) -> Option<NamespaceDeclaration> {
99        let mut name = String::new();
100        let mut items = Vec::new();
101        let span = (0..node.text_len() as usize).into();
102        let mut current_offset = 0;
103
104        for child in node.children() {
105            match child {
106                GreenTree::Leaf(leaf) => {
107                    if leaf.kind() == VbNetTokenType::Identifier {
108                        let leaf_start = current_offset;
109                        let leaf_end = current_offset + leaf.length() as usize;
110                        name = source.get_text_in((leaf_start..leaf_end).into()).to_string();
111                    }
112                }
113                GreenTree::Node(child_node) => {
114                    self.build_items(child_node, source, &mut items);
115                }
116            }
117            current_offset += child.len() as usize;
118        }
119
120        Some(NamespaceDeclaration { name, items, span })
121    }
122
123    fn build_imports<'a, S: Source + ?Sized>(&self, node: &GreenNode<'a, VbNetLanguage>, source: &'a S) -> Option<ImportsDirective> {
124        let mut path = String::new();
125        let span = (0..node.text_len() as usize).into();
126        let mut current_offset = 0;
127        let mut found_imports = false;
128
129        for child in node.children() {
130            match child {
131                GreenTree::Leaf(leaf) => {
132                    if leaf.kind() == VbNetTokenType::Imports {
133                        found_imports = true;
134                    }
135                    else if found_imports && leaf.kind() == VbNetTokenType::Identifier {
136                        if !path.is_empty() {
137                            path.push('.');
138                        }
139                        let leaf_start = current_offset;
140                        let leaf_end = current_offset + leaf.length() as usize;
141                        path.push_str(&source.get_text_in((leaf_start..leaf_end).into()));
142                    }
143                    else if found_imports && leaf.kind() == VbNetTokenType::Dot {
144                        path.push('.');
145                    }
146                }
147                GreenTree::Node(_) => {}
148            }
149            current_offset += child.len() as usize;
150        }
151
152        Some(ImportsDirective { path, alias: None, span })
153    }
154
155    fn build_class<'a, S: Source + ?Sized>(&self, node: &GreenNode<'a, VbNetLanguage>, source: &'a S) -> Option<ClassDeclaration> {
156        let mut name = String::new();
157        let mut attributes = Vec::new();
158        let mut modifiers = Vec::new();
159        let mut base_types = Vec::new();
160        let mut members = Vec::new();
161        let span = (0..node.text_len() as usize).into();
162        let mut current_offset = 0;
163        let mut skipped_class = false;
164
165        for child in node.children() {
166            match child {
167                GreenTree::Leaf(leaf) => match leaf.kind() {
168                    VbNetTokenType::Class => {
169                        skipped_class = true;
170                    }
171                    VbNetTokenType::Identifier if skipped_class => {
172                        if name.is_empty() {
173                            let leaf_start = current_offset;
174                            let leaf_end = current_offset + leaf.length() as usize;
175                            name = source.get_text_in((leaf_start..leaf_end).into()).to_string();
176                        }
177                    }
178                    VbNetTokenType::Public | VbNetTokenType::Private | VbNetTokenType::Protected | VbNetTokenType::Friend | VbNetTokenType::Shared | VbNetTokenType::MustInherit | VbNetTokenType::NotInheritable | VbNetTokenType::Partial => {
179                        let leaf_start = current_offset;
180                        let leaf_end = current_offset + leaf.length() as usize;
181                        modifiers.push(source.get_text_in((leaf_start..leaf_end).into()).to_string());
182                    }
183                    _ => {}
184                },
185                GreenTree::Node(child_node) => {
186                    self.build_members(child_node, source, &mut members);
187                }
188            }
189            current_offset += child.len() as usize;
190        }
191
192        Some(ClassDeclaration { name, attributes, modifiers, base_types, members, span })
193    }
194
195    fn build_function<'a, S: Source + ?Sized>(&self, node: &GreenNode<'a, VbNetLanguage>, source: &'a S) -> Option<FunctionDeclaration> {
196        let mut name = String::new();
197        let mut attributes = Vec::new();
198        let mut modifiers = Vec::new();
199        let mut return_type = String::new();
200        let mut parameters = Vec::new();
201        let mut body = None;
202        let span = (0..node.text_len() as usize).into();
203        let mut current_offset = 0;
204        let mut skipped_function = false;
205
206        for child in node.children() {
207            match child {
208                GreenTree::Leaf(leaf) => match leaf.kind() {
209                    VbNetTokenType::Function => {
210                        skipped_function = true;
211                    }
212                    VbNetTokenType::Identifier if skipped_function => {
213                        if name.is_empty() {
214                            let leaf_start = current_offset;
215                            let leaf_end = current_offset + leaf.length() as usize;
216                            name = source.get_text_in((leaf_start..leaf_end).into()).to_string();
217                        }
218                    }
219                    VbNetTokenType::Public
220                    | VbNetTokenType::Private
221                    | VbNetTokenType::Protected
222                    | VbNetTokenType::Friend
223                    | VbNetTokenType::Shared
224                    | VbNetTokenType::Overrides
225                    | VbNetTokenType::Overloads
226                    | VbNetTokenType::Overridable
227                    | VbNetTokenType::NotOverridable
228                    | VbNetTokenType::MustOverride
229                    | VbNetTokenType::ReadOnly
230                    | VbNetTokenType::WriteOnly
231                    | VbNetTokenType::Static
232                    | VbNetTokenType::Partial
233                    | VbNetTokenType::Async => {
234                        let leaf_start = current_offset;
235                        let leaf_end = current_offset + leaf.length() as usize;
236                        modifiers.push(source.get_text_in((leaf_start..leaf_end).into()).to_string());
237                    }
238                    _ => {}
239                },
240                GreenTree::Node(child_node) => {
241                    if child_node.kind() == VbNetElementType::Statement {
242                        let mut statements = Vec::new();
243                        self.build_statements(child_node, source, &mut statements);
244                        body = Some(statements);
245                    }
246                }
247            }
248            current_offset += child.len() as usize;
249        }
250
251        Some(FunctionDeclaration { name, attributes, modifiers, return_type, parameters, body, span })
252    }
253
254    fn build_sub<'a, S: Source + ?Sized>(&self, node: &GreenNode<'a, VbNetLanguage>, source: &'a S) -> Option<SubDeclaration> {
255        let mut name = String::new();
256        let mut attributes = Vec::new();
257        let mut modifiers = Vec::new();
258        let mut parameters = Vec::new();
259        let mut body = None;
260        let span = (0..node.text_len() as usize).into();
261        let mut current_offset = 0;
262        let mut skipped_sub = false;
263
264        for child in node.children() {
265            match child {
266                GreenTree::Leaf(leaf) => match leaf.kind() {
267                    VbNetTokenType::Sub => {
268                        skipped_sub = true;
269                    }
270                    VbNetTokenType::Identifier if skipped_sub => {
271                        if name.is_empty() {
272                            let leaf_start = current_offset;
273                            let leaf_end = current_offset + leaf.length() as usize;
274                            name = source.get_text_in((leaf_start..leaf_end).into()).to_string();
275                        }
276                    }
277                    VbNetTokenType::Public
278                    | VbNetTokenType::Private
279                    | VbNetTokenType::Protected
280                    | VbNetTokenType::Friend
281                    | VbNetTokenType::Shared
282                    | VbNetTokenType::Overrides
283                    | VbNetTokenType::Overloads
284                    | VbNetTokenType::Overridable
285                    | VbNetTokenType::NotOverridable
286                    | VbNetTokenType::MustOverride
287                    | VbNetTokenType::Static
288                    | VbNetTokenType::Partial
289                    | VbNetTokenType::Async => {
290                        let leaf_start = current_offset;
291                        let leaf_end = current_offset + leaf.length() as usize;
292                        modifiers.push(source.get_text_in((leaf_start..leaf_end).into()).to_string());
293                    }
294                    _ => {}
295                },
296                GreenTree::Node(child_node) => {
297                    if child_node.kind() == VbNetElementType::Statement {
298                        let mut statements = Vec::new();
299                        self.build_statements(child_node, source, &mut statements);
300                        body = Some(statements);
301                    }
302                }
303            }
304            current_offset += child.len() as usize;
305        }
306
307        Some(SubDeclaration { name, attributes, modifiers, parameters, body, span })
308    }
309
310    fn build_property<'a, S: Source + ?Sized>(&self, node: &GreenNode<'a, VbNetLanguage>, source: &'a S) -> Option<PropertyDeclaration> {
311        let mut name = String::new();
312        let mut attributes = Vec::new();
313        let mut property_type = String::new();
314        let mut modifiers = Vec::new();
315        let mut get_accessor = None;
316        let mut set_accessor = None;
317        let span = (0..node.text_len() as usize).into();
318        let mut current_offset = 0;
319        let mut skipped_property = false;
320
321        for child in node.children() {
322            match child {
323                GreenTree::Leaf(leaf) => match leaf.kind() {
324                    VbNetTokenType::Property => {
325                        skipped_property = true;
326                    }
327                    VbNetTokenType::Identifier if skipped_property => {
328                        if name.is_empty() {
329                            let leaf_start = current_offset;
330                            let leaf_end = current_offset + leaf.length() as usize;
331                            name = source.get_text_in((leaf_start..leaf_end).into()).to_string();
332                        }
333                    }
334                    VbNetTokenType::Public
335                    | VbNetTokenType::Private
336                    | VbNetTokenType::Protected
337                    | VbNetTokenType::Friend
338                    | VbNetTokenType::Shared
339                    | VbNetTokenType::Overrides
340                    | VbNetTokenType::Overloads
341                    | VbNetTokenType::Overridable
342                    | VbNetTokenType::NotOverridable
343                    | VbNetTokenType::MustOverride
344                    | VbNetTokenType::ReadOnly
345                    | VbNetTokenType::WriteOnly
346                    | VbNetTokenType::Static
347                    | VbNetTokenType::Partial => {
348                        let leaf_start = current_offset;
349                        let leaf_end = current_offset + leaf.length() as usize;
350                        modifiers.push(source.get_text_in((leaf_start..leaf_end).into()).to_string());
351                    }
352                    _ => {}
353                },
354                GreenTree::Node(_) => {
355                    // Handle get/set accessors
356                }
357            }
358            current_offset += child.len() as usize;
359        }
360
361        Some(PropertyDeclaration { name, attributes, property_type, modifiers, get_accessor, set_accessor, span })
362    }
363
364    fn build_variable<'a, S: Source + ?Sized>(&self, node: &GreenNode<'a, VbNetLanguage>, source: &'a S) -> Option<VariableDeclaration> {
365        let mut name = String::new();
366        let mut attributes = Vec::new();
367        let mut variable_type = String::new();
368        let mut modifiers = Vec::new();
369        let mut initializer = None;
370        let span = (0..node.text_len() as usize).into();
371        let mut current_offset = 0;
372        let mut skipped_dim = false;
373
374        for child in node.children() {
375            match child {
376                GreenTree::Leaf(leaf) => {
377                    if leaf.kind() == VbNetTokenType::Dim {
378                        skipped_dim = true;
379                    }
380                    if leaf.kind() == VbNetTokenType::Identifier && name.is_empty() && skipped_dim {
381                        let leaf_start = current_offset;
382                        let leaf_end = current_offset + leaf.length() as usize;
383                        name = source.get_text_in((leaf_start..leaf_end).into()).to_string();
384                    }
385                }
386                GreenTree::Node(child_node) => {
387                    if child_node.kind() == VbNetElementType::Expression {
388                        if let Some(expr) = self.build_expression(child_node, source) {
389                            initializer = Some(expr);
390                        }
391                    }
392                }
393            }
394            current_offset += child.len() as usize;
395        }
396
397        Some(VariableDeclaration { name, attributes, variable_type, modifiers, initializer, span })
398    }
399
400    fn build_members<'a, S: Source + ?Sized>(&self, node: &GreenNode<'a, VbNetLanguage>, source: &'a S, members: &mut Vec<Member>) {
401        for child in node.children() {
402            match child {
403                GreenTree::Node(child_node) => match child_node.kind() {
404                    VbNetElementType::Function => {
405                        if let Some(function) = self.build_function(child_node, source) {
406                            members.push(Member::Function(function));
407                        }
408                    }
409                    VbNetElementType::Sub => {
410                        if let Some(sub) = self.build_sub(child_node, source) {
411                            members.push(Member::Sub(sub));
412                        }
413                    }
414                    VbNetElementType::Property => {
415                        if let Some(property) = self.build_property(child_node, source) {
416                            members.push(Member::Property(property));
417                        }
418                    }
419                    VbNetElementType::Dim => {
420                        if let Some(variable) = self.build_variable(child_node, source) {
421                            members.push(Member::Variable(variable));
422                        }
423                    }
424                    _ => {
425                        self.build_members(child_node, source, members);
426                    }
427                },
428                GreenTree::Leaf(_) => {}
429            }
430        }
431    }
432
433    fn build_statements<'a, S: Source + ?Sized>(&self, node: &GreenNode<'a, VbNetLanguage>, source: &'a S, statements: &mut Vec<Statement>) {
434        for child in node.children() {
435            match child {
436                GreenTree::Node(child_node) => {
437                    match child_node.kind() {
438                        VbNetElementType::Expression => {
439                            if let Some(expr) = self.build_expression(child_node, source) {
440                                statements.push(Statement::Expression(expr));
441                            }
442                        }
443                        VbNetElementType::Return => {
444                            let mut return_expr = None;
445                            for grandchild in child_node.children() {
446                                if let GreenTree::Node(grandchild_node) = grandchild {
447                                    if grandchild_node.kind() == VbNetElementType::Expression {
448                                        return_expr = self.build_expression(grandchild_node, source);
449                                    }
450                                }
451                            }
452                            statements.push(Statement::Return(return_expr));
453                        }
454                        VbNetElementType::If => {
455                            // TODO: Implement If statement parsing
456                        }
457                        VbNetElementType::For => {
458                            // TODO: Implement For loop parsing
459                        }
460                        VbNetElementType::While => {
461                            // TODO: Implement While loop parsing
462                        }
463                        VbNetElementType::DoWhile => {
464                            // TODO: Implement Do loop parsing
465                        }
466                        VbNetElementType::SelectCase => {
467                            // TODO: Implement Select statement parsing
468                        }
469                        VbNetElementType::Try => {
470                            // TODO: Implement Try statement parsing
471                        }
472                        VbNetElementType::Exit => {
473                            // TODO: Implement Exit statement parsing
474                        }
475                        VbNetElementType::Continue => {
476                            // TODO: Implement Continue statement parsing
477                        }
478                        VbNetElementType::Throw => {
479                            // TODO: Implement Throw statement parsing
480                        }
481                        VbNetElementType::With => {
482                            // TODO: Implement With statement parsing
483                        }
484                        VbNetElementType::Dim => {
485                            if let Some(variable) = self.build_variable(child_node, source) {
486                                statements.push(Statement::Dim(variable));
487                            }
488                        }
489                        VbNetElementType::Const => {
490                            // TODO: Implement Const statement parsing
491                        }
492                        _ => {
493                            self.build_statements(child_node, source, statements);
494                        }
495                    }
496                }
497                GreenTree::Leaf(_) => {}
498            }
499        }
500    }
501
502    fn build_expression<'a, S: Source + ?Sized>(&self, node: &GreenNode<'a, VbNetLanguage>, source: &'a S) -> Option<Expression> {
503        let mut current_offset = 0;
504        for child in node.children() {
505            match child {
506                GreenTree::Leaf(leaf) => {
507                    match leaf.kind() {
508                        VbNetTokenType::Identifier => {
509                            let leaf_start = current_offset;
510                            let leaf_end = current_offset + leaf.length() as usize;
511                            return Some(Expression::Identifier(source.get_text_in((leaf_start..leaf_end).into()).to_string()));
512                        }
513                        VbNetTokenType::IntegerLiteral => {
514                            let leaf_start = current_offset;
515                            let leaf_end = current_offset + leaf.length() as usize;
516                            if let Ok(value) = source.get_text_in((leaf_start..leaf_end).into()).parse::<i64>() {
517                                return Some(Expression::Literal(Literal::Integer(value)));
518                            }
519                        }
520                        VbNetTokenType::FloatLiteral => {
521                            let leaf_start = current_offset;
522                            let leaf_end = current_offset + leaf.length() as usize;
523                            if let Ok(value) = source.get_text_in((leaf_start..leaf_end).into()).parse::<f64>() {
524                                return Some(Expression::Literal(Literal::Double(value)));
525                            }
526                        }
527                        VbNetTokenType::StringLiteral => {
528                            let leaf_start = current_offset;
529                            let leaf_end = current_offset + leaf.length() as usize;
530                            let text = source.get_text_in((leaf_start..leaf_end).into());
531                            // Remove quotes
532                            let value = text.trim_matches('"').replace("\"", "\"");
533                            return Some(Expression::Literal(Literal::String(value)));
534                        }
535                        VbNetTokenType::BooleanLiteral => {
536                            let leaf_start = current_offset;
537                            let leaf_end = current_offset + leaf.length() as usize;
538                            let text = source.get_text_in((leaf_start..leaf_end).into()).to_lowercase();
539                            if text == "true" {
540                                return Some(Expression::Literal(Literal::Boolean(true)));
541                            }
542                            else if text == "false" {
543                                return Some(Expression::Literal(Literal::Boolean(false)));
544                            }
545                        }
546                        VbNetTokenType::NothingLiteral => {
547                            return Some(Expression::Literal(Literal::Nothing));
548                        }
549                        VbNetTokenType::Me => {
550                            return Some(Expression::Me);
551                        }
552                        VbNetTokenType::MyBase => {
553                            return Some(Expression::MyBase);
554                        }
555                        VbNetTokenType::MyClass => {
556                            return Some(Expression::MyClass);
557                        }
558                        _ => {}
559                    }
560                }
561                GreenTree::Node(child_node) => {
562                    if child_node.kind() == VbNetElementType::ParenthesizedExpression {
563                        // Parenthesized expression
564                        for grandchild in child_node.children() {
565                            if let GreenTree::Node(grandchild_node) = grandchild {
566                                if let Some(expr) = self.build_expression(grandchild_node, source) {
567                                    return Some(Expression::Parenthesized(Box::new(expr)));
568                                }
569                            }
570                        }
571                    }
572                    else {
573                        if let Some(expr) = self.build_expression(child_node, source) {
574                            return Some(expr);
575                        }
576                    }
577                }
578            }
579            current_offset += child.len() as usize;
580        }
581        None
582    }
583}