Skip to main content

oak_groovy/builder/
mod.rs

1#![doc = include_str!("readme.md")]
2use crate::{ast::*, language::GroovyLanguage, parser::GroovyParser};
3use oak_core::{Builder, BuilderCache, GreenNode, OakDiagnostics, Parser, SourceText, TextEdit, source::Source};
4
5/// AST builder for the Groovy language
6#[derive(Clone)]
7pub struct GroovyBuilder<'config> {
8    /// Language configuration
9    config: &'config GroovyLanguage,
10}
11
12impl<'config> GroovyBuilder<'config> {
13    /// Creates a new Groovy builder
14    pub fn new(config: &'config GroovyLanguage) -> Self {
15        Self { config }
16    }
17
18    /// Builds the AST root node from the syntax tree
19    pub fn build_root(&self, green: &GreenNode<GroovyLanguage>, source: &SourceText) -> Result<GroovyRoot, oak_core::OakError> {
20        let mut declarations = Vec::new();
21        let mut offset = 0;
22
23        for child in green.children() {
24            if let oak_core::GreenTree::Node(node) = child {
25                if let Some(decl) = self.build_declaration(node, source, offset) {
26                    declarations.push(decl)
27                }
28            }
29            offset += child.len() as usize
30        }
31
32        Ok(GroovyRoot { declarations, span: (0..source.length()).into() })
33    }
34
35    fn build_declaration(&self, node: &oak_core::GreenNode<GroovyLanguage>, source: &SourceText, offset: usize) -> Option<crate::ast::Declaration> {
36        use crate::parser::element_type::GroovyElementType;
37        let kind: GroovyElementType = node.kind;
38        let start = offset;
39
40        match kind {
41            GroovyElementType::ClassKeyword => {
42                let mut name = "GroovyClass".to_string();
43                let mut inner_offset = offset;
44                for child in node.children() {
45                    if let oak_core::GreenTree::Leaf(leaf) = child {
46                        if leaf.kind == crate::lexer::token_type::GroovyTokenType::Identifier {
47                            name = source.get_text_in((inner_offset..inner_offset + leaf.length as usize).into()).to_string();
48                            break;
49                        }
50                    }
51                    inner_offset += child.len() as usize;
52                }
53                Some(crate::ast::Declaration::Class { name, members: vec![], span: (start..start + node.byte_length as usize).into() })
54            }
55            GroovyElementType::DefKeyword => {
56                let mut name = "groovyMethod".to_string();
57                let mut inner_offset = offset;
58                for child in node.children() {
59                    if let oak_core::GreenTree::Leaf(leaf) = child {
60                        if leaf.kind == crate::lexer::token_type::GroovyTokenType::Identifier {
61                            name = source.get_text_in((inner_offset..inner_offset + leaf.length as usize).into()).to_string();
62                            break;
63                        }
64                    }
65                    inner_offset += child.len() as usize;
66                }
67                Some(crate::ast::Declaration::Method { name, params: vec![], span: (start..start + node.byte_length as usize).into() })
68            }
69            _ => {
70                // Check if it's a SourceFile or Root and recurse
71                if kind == GroovyElementType::Root || kind == GroovyElementType::SourceFile {
72                    // Logic to handle root children is already in build_root
73                }
74                None
75            }
76        }
77    }
78}
79
80impl<'config> Builder<GroovyLanguage> for GroovyBuilder<'config> {
81    fn build<'a, S: Source + ?Sized>(&self, source: &'a S, edits: &[TextEdit], _cache: &'a mut impl BuilderCache<GroovyLanguage>) -> oak_core::builder::BuildOutput<GroovyLanguage> {
82        let parser = GroovyParser::new(self.config);
83        let mut cache = oak_core::parser::ParseSession::<GroovyLanguage>::default();
84        let parse_result = parser.parse(source, edits, &mut cache);
85
86        match parse_result.result {
87            Ok(green_tree) => {
88                let source_text = SourceText::new(source.get_text_in((0..source.length()).into()).into_owned());
89                match self.build_root(green_tree, &source_text) {
90                    Ok(ast_root) => OakDiagnostics { result: Ok(ast_root), diagnostics: parse_result.diagnostics },
91                    Err(build_error) => {
92                        let mut diagnostics = parse_result.diagnostics;
93                        diagnostics.push(build_error.clone());
94                        OakDiagnostics { result: Err(build_error), diagnostics }
95                    }
96                }
97            }
98            Err(parse_error) => OakDiagnostics { result: Err(parse_error), diagnostics: parse_result.diagnostics },
99        }
100    }
101}