Skip to main content

oak_go/builder/
mod.rs

1//! Go language builder
2
3use crate::{
4    ast::{self, Declaration, GoRoot},
5    language::GoLanguage,
6    lexer::{GoLexer, token_type::GoTokenType},
7    parser::{GoParser, element_type::GoElementType},
8};
9use oak_core::{
10    Builder, BuilderCache, GreenNode, Lexer, OakDiagnostics, OakError, ParseSession, Parser, SourceText, TextEdit, TokenType,
11    builder::BuildOutput,
12    source::Source,
13    tree::{RedNode, RedTree},
14};
15
16mod build_signatures;
17
18/// Go language builder
19pub struct GoBuilder<'config> {
20    pub(crate) config: &'config GoLanguage,
21}
22
23impl<'config> Builder<GoLanguage> for GoBuilder<'config> {
24    fn build<'a, S: Source + ?Sized>(&self, text: &S, edits: &[TextEdit], cache: &'a mut impl BuilderCache<GoLanguage>) -> BuildOutput<GoLanguage> {
25        let parser = GoParser::new(self.config);
26        let cache_ptr = cache as *mut _;
27        let output = parser.parse(text, edits, unsafe { &mut *cache_ptr });
28        let result = output.result.map(|green| {
29            let source = SourceText::new(text.get_text_in((0..text.length()).into()).into_owned());
30            self.build_root(green, &source, unsafe { &mut *cache_ptr })
31        });
32        OakDiagnostics { result, diagnostics: output.diagnostics }
33    }
34}
35
36impl<'config> GoBuilder<'config> {
37    pub fn new(config: &'config GoLanguage) -> Self {
38        Self { config }
39    }
40
41    fn build_root<'a>(&self, green: &'a GreenNode<'a, GoLanguage>, source: &SourceText, cache: &mut impl BuilderCache<GoLanguage>) -> GoRoot {
42        if let Some(cached) = cache.get_typed_node::<std::sync::Arc<GoRoot>>(green) {
43            return (*cached).clone();
44        }
45
46        let red = RedNode::new(green, 0);
47        let mut package = None;
48        let mut imports = vec![];
49        let mut declarations = vec![];
50
51        for child in red.children() {
52            if let RedTree::Node(node) = child {
53                match node.green.kind {
54                    GoElementType::PackageClause => {
55                        package = self.extract_package(node, source);
56                    }
57                    GoElementType::ImportDeclaration => {
58                        imports.extend(self.extract_imports(node, source));
59                    }
60                    GoElementType::FunctionDeclaration => {
61                        if let Ok(func) = self.extract_function(node, source, cache) {
62                            declarations.push(Declaration::Function(func));
63                        }
64                    }
65                    GoElementType::VariableDeclaration => {
66                        if let Ok(vars) = self.extract_variables(node, source, cache) {
67                            declarations.extend(vars);
68                        }
69                    }
70                    GoElementType::ConstDeclaration => {
71                        if let Ok(consts) = self.extract_consts(node, source, cache) {
72                            declarations.extend(consts);
73                        }
74                    }
75                    GoElementType::TypeDeclaration => {
76                        if let Ok(types) = self.extract_types(node, source, cache) {
77                            declarations.extend(types);
78                        }
79                    }
80                    _ => {}
81                }
82            }
83        }
84
85        let root = GoRoot { package, imports, declarations };
86        // cache.set_typed_node(green, std::sync::Arc::new(root.clone()));
87        root
88    }
89
90    fn extract_package(&self, node: RedNode<GoLanguage>, source: &SourceText) -> Option<String> {
91        for child in node.children() {
92            if let RedTree::Leaf(leaf) = child {
93                if leaf.kind == GoTokenType::Identifier {
94                    return Some(source.get_text_in(leaf.span).trim().to_string());
95                }
96            }
97        }
98        None
99    }
100
101    fn extract_imports(&self, node: RedNode<GoLanguage>, source: &SourceText) -> Vec<ast::Import> {
102        let mut imports = vec![];
103        for child in node.children() {
104            if let RedTree::Node(n) = child {
105                match n.green.kind {
106                    GoElementType::ImportSpec => {
107                        let mut path = String::new();
108                        let mut alias = None;
109                        for spec_child in n.children() {
110                            match spec_child {
111                                RedTree::Leaf(leaf) if leaf.kind == GoTokenType::Identifier => {
112                                    alias = Some(source.get_text_in(leaf.span).trim().to_string());
113                                }
114                                RedTree::Leaf(leaf) if leaf.kind == GoTokenType::StringLiteral => {
115                                    path = source.get_text_in(leaf.span).trim_matches('"').to_string();
116                                }
117                                _ => {}
118                            }
119                        }
120                        imports.push(ast::Import { path, alias, span: n.span() });
121                    }
122                    _ => {
123                        // Handle import ( ... )
124                        imports.extend(self.extract_imports(n, source));
125                    }
126                }
127            }
128        }
129        imports
130    }
131
132    fn extract_block(&self, node: RedNode<GoLanguage>, source: &SourceText) -> Result<ast::Block, OakError> {
133        let mut statements = vec![];
134        let span = node.span();
135
136        for child in node.children() {
137            if let RedTree::Node(n) = child {
138                if let Some(stmt) = self.extract_statement(n, source)? {
139                    statements.push(stmt);
140                }
141            }
142        }
143
144        Ok(ast::Block { statements, span })
145    }
146
147    fn extract_statement(&self, node: RedNode<GoLanguage>, source: &SourceText) -> Result<Option<ast::Statement>, OakError> {
148        match node.green.kind {
149            GoElementType::ReturnStatement => {
150                let mut values = vec![];
151                for child in node.children() {
152                    if let RedTree::Node(n) = child {
153                        values.push(self.extract_expression(n, source)?);
154                    }
155                }
156                Ok(Some(ast::Statement::Return { values, span: node.span() }))
157            }
158            GoElementType::IfStatement => {
159                let mut condition = ast::Expression::Literal { value: "true".to_string(), span: node.span() };
160                let mut then_block = ast::Block { statements: vec![], span: node.span() };
161                let mut else_block = None;
162
163                for child in node.children() {
164                    if let RedTree::Node(n) = child {
165                        match n.green.kind {
166                            GoElementType::BinaryExpression | GoElementType::CallExpression | GoElementType::Identifier => {
167                                condition = self.extract_expression(n, source)?;
168                            }
169                            GoElementType::Block => {
170                                if then_block.statements.is_empty() {
171                                    then_block = self.extract_block(n, source)?;
172                                }
173                                else {
174                                    else_block = Some(self.extract_block(n, source)?);
175                                }
176                            }
177                            GoElementType::IfStatement => {
178                                // Handle else if
179                                let inner_if = self.extract_statement(n, source)?;
180                                if let Some(ast::Statement::If { condition, then_block, else_block: inner_else, span }) = inner_if {
181                                    else_block = Some(ast::Block { statements: vec![ast::Statement::If { condition, then_block, else_block: inner_else, span }], span });
182                                }
183                            }
184                            _ => {}
185                        }
186                    }
187                }
188                Ok(Some(ast::Statement::If { condition, then_block, else_block, span: node.span() }))
189            }
190            GoElementType::ForStatement => {
191                let mut init = None;
192                let mut condition = None;
193                let mut post = None;
194                let mut body = ast::Block { statements: vec![], span: node.span() };
195
196                for child in node.children() {
197                    if let RedTree::Node(n) = child {
198                        match n.green.kind {
199                            GoElementType::ShortVarDecl | GoElementType::AssignmentStatement | GoElementType::VariableDeclaration => {
200                                if init.is_none() {
201                                    init = self.extract_statement(n, source)?.map(Box::new);
202                                }
203                                else {
204                                    post = self.extract_statement(n, source)?.map(Box::new);
205                                }
206                            }
207                            GoElementType::BinaryExpression | GoElementType::CallExpression | GoElementType::Identifier => {
208                                condition = Some(self.extract_expression(n, source)?);
209                            }
210                            GoElementType::Block => {
211                                body = self.extract_block(n, source)?;
212                            }
213                            _ => {}
214                        }
215                    }
216                }
217                Ok(Some(ast::Statement::For { init, condition, post, body, span: node.span() }))
218            }
219            GoElementType::AssignmentStatement | GoElementType::ShortVarDecl | GoElementType::VariableDeclaration | GoElementType::VariableSpec => {
220                // Support multiple assignment
221                let mut targets = vec![];
222                let mut values = vec![];
223
224                for child in node.children() {
225                    match child {
226                        RedTree::Leaf(leaf) if leaf.kind == GoTokenType::Identifier => {
227                            targets.push(source.get_text_in(leaf.span).trim().to_string());
228                        }
229                        RedTree::Node(n) => {
230                            if n.green.kind == GoElementType::VariableSpec || n.green.kind == GoElementType::VariableDeclaration {
231                                // Recursive extraction
232                                if let Some(ast::Statement::Assignment { targets: t, values: v, .. }) = self.extract_statement(n, source)? {
233                                    targets.extend(t);
234                                    values.extend(v);
235                                }
236                            }
237                            else if n.green.kind != GoElementType::Identifier && !n.green.kind.is_keyword() {
238                                values.push(self.extract_expression(n, source)?);
239                            }
240                            else if n.green.kind == GoElementType::Identifier {
241                                targets.push(source.get_text_in(n.span()).trim().to_string());
242                            }
243                        }
244                        _ => {}
245                    }
246                }
247
248                if targets.is_empty() {
249                    return Ok(None);
250                }
251
252                // If no value (e.g., variable declaration), fill with default value
253                if values.is_empty() {
254                    for _ in &targets {
255                        values.push(ast::Expression::Literal { value: "0".to_string(), span: node.span() });
256                    }
257                }
258
259                Ok(Some(ast::Statement::Assignment { targets, values, span: node.span() }))
260            }
261            _ => {
262                // Could be an expression statement
263                if let Ok(expr) = self.extract_expression(node, source) { Ok(Some(ast::Statement::Expression(expr))) } else { Ok(None) }
264            }
265        }
266    }
267
268    fn extract_expression(&self, node: RedNode<GoLanguage>, source: &SourceText) -> Result<ast::Expression, OakError> {
269        self.extract_expression_internal(RedTree::Node(node), source)
270    }
271
272    fn extract_expression_internal(&self, tree: RedTree<GoLanguage>, source: &SourceText) -> Result<ast::Expression, OakError> {
273        match tree {
274            RedTree::Leaf(leaf) => {
275                if leaf.kind == GoTokenType::Identifier {
276                    let name = source.get_text_in(leaf.span).trim().to_string();
277                    if name.is_empty() {
278                        return Err(OakError::parse_error("Empty identifier leaf"));
279                    }
280                    Ok(ast::Expression::Identifier { name, span: leaf.span })
281                }
282                else if leaf.kind == GoTokenType::IntLiteral || leaf.kind == GoTokenType::StringLiteral || leaf.kind == GoTokenType::BoolLiteral {
283                    Ok(ast::Expression::Literal { value: source.get_text_in(leaf.span).to_string(), span: leaf.span })
284                }
285                else {
286                    Err(OakError::parse_error(format!("Unexpected leaf in expression: {:?}", leaf.kind)))
287                }
288            }
289            RedTree::Node(node) => match node.green.kind {
290                GoElementType::Identifier => {
291                    let mut name = String::new();
292                    for child in node.children() {
293                        match child {
294                            RedTree::Leaf(leaf) if leaf.kind == GoTokenType::Identifier => {
295                                name = source.get_text_in(leaf.span).trim().to_string();
296                                if !name.is_empty() {
297                                    break;
298                                }
299                            }
300                            RedTree::Node(n) => {
301                                println!("DEBUG: Identifier node has a Node child: kind={:?}, span={:?}", n.green.kind, n.span());
302                            }
303                            _ => {}
304                        }
305                    }
306                    if name.is_empty() {
307                        name = source.get_text_in(node.span()).trim().to_string();
308                    }
309                    if name.is_empty() {
310                        println!("DEBUG: Final empty identifier node details:");
311                        println!("  Node kind: {:?}", node.green.kind);
312                        println!("  Node span: {:?}", node.span());
313                        println!("  Children count: {}", node.children().count());
314                        for (i, child) in node.children().enumerate() {
315                            match child {
316                                RedTree::Node(n) => println!("    child {}: Node kind={:?}, span={:?}", i, n.green.kind, n.span()),
317                                RedTree::Leaf(l) => println!("    child {}: Leaf kind={:?}, span={:?}, text={:?}", i, l.kind, l.span, source.get_text_in(l.span)),
318                            }
319                        }
320                        return Err(OakError::parse_error(format!("Empty identifier at {:?}", node.span())));
321                    }
322                    Ok(ast::Expression::Identifier { name, span: node.span() })
323                }
324                GoElementType::IntLiteral | GoElementType::StringLiteral | GoElementType::BoolLiteral => Ok(ast::Expression::Literal { value: source.get_text_in(node.span()).trim().to_string(), span: node.span() }),
325                GoElementType::BinaryExpression => {
326                    let mut left = None;
327                    let mut op = String::new();
328                    let mut right = None;
329
330                    for child in node.children() {
331                        match child {
332                            RedTree::Node(n) => {
333                                if left.is_none() {
334                                    left = Some(Box::new(self.extract_expression(n, source)?));
335                                }
336                                else {
337                                    right = Some(Box::new(self.extract_expression(n, source)?));
338                                }
339                            }
340                            RedTree::Leaf(leaf) => {
341                                if leaf.kind == GoTokenType::Identifier {
342                                    if left.is_none() {
343                                        left = Some(Box::new(ast::Expression::Identifier { name: source.get_text_in(leaf.span).trim().to_string(), span: leaf.span }));
344                                    }
345                                    else {
346                                        right = Some(Box::new(ast::Expression::Identifier { name: source.get_text_in(leaf.span).trim().to_string(), span: leaf.span }));
347                                    }
348                                }
349                                else if TokenType::role(&leaf.kind) == oak_core::UniversalTokenRole::Operator {
350                                    op = source.get_text_in(leaf.span).to_string();
351                                }
352                            }
353                        }
354                    }
355
356                    if let (Some(left), Some(right)) = (left, right) { Ok(ast::Expression::Binary { left, op, right, span: node.span() }) } else { Ok(ast::Expression::Literal { value: source.get_text_in(node.span()).to_string(), span: node.span() }) }
357                }
358                GoElementType::CallExpression => {
359                    let mut func = None;
360                    let mut args = vec![];
361
362                    for child in node.children() {
363                        match child {
364                            RedTree::Node(n) => {
365                                if func.is_none() {
366                                    func = Some(Box::new(self.extract_expression(n, source)?));
367                                }
368                                else if n.green.kind == GoElementType::ExpressionList {
369                                    for list_child in n.children() {
370                                        if let RedTree::Node(ln) = list_child {
371                                            args.push(self.extract_expression(ln, source)?);
372                                        }
373                                        else if let RedTree::Leaf(leaf) = list_child {
374                                            if let Ok(expr) = self.extract_expression_internal(RedTree::Leaf(leaf), source) {
375                                                args.push(expr);
376                                            }
377                                        }
378                                    }
379                                }
380                                else {
381                                    args.push(self.extract_expression(n, source)?);
382                                }
383                            }
384                            RedTree::Leaf(leaf) if leaf.kind == GoTokenType::Identifier => {
385                                if func.is_none() {
386                                    func = Some(Box::new(ast::Expression::Identifier { name: source.get_text_in(leaf.span).trim().to_string(), span: leaf.span }));
387                                }
388                                else {
389                                    args.push(ast::Expression::Identifier { name: source.get_text_in(leaf.span).trim().to_string(), span: leaf.span });
390                                }
391                            }
392                            _ => {}
393                        }
394                    }
395
396                    if let Some(func) = func { Ok(ast::Expression::Call { func, args, span: node.span() }) } else { Ok(ast::Expression::Literal { value: source.get_text_in(node.span()).to_string(), span: node.span() }) }
397                }
398                _ => Ok(ast::Expression::Literal { value: source.get_text_in(node.span()).to_string(), span: node.span() }),
399            },
400        }
401    }
402
403    fn extract_variables(&self, node: RedNode<GoLanguage>, source: &SourceText, cache: &mut impl BuilderCache<GoLanguage>) -> Result<Vec<Declaration>, OakError> {
404        if let Some(cached) = cache.get_typed_node::<Vec<Declaration>>(&node.green) {
405            return Ok(cached);
406        }
407        let mut vars = vec![];
408        for child in node.children() {
409            if let RedTree::Node(n) = child {
410                match n.green.kind {
411                    GoElementType::VariableSpec => {
412                        let mut name = String::new();
413                        let mut var_type = None;
414                        let mut value = None;
415                        for spec_child in n.children() {
416                            match spec_child {
417                                RedTree::Leaf(leaf) if leaf.kind == GoTokenType::Identifier => {
418                                    if name.is_empty() {
419                                        name = source.get_text_in(leaf.span).trim().to_string();
420                                    }
421                                    else if var_type.is_none() {
422                                        var_type = Some(source.get_text_in(leaf.span).trim().to_string());
423                                    }
424                                }
425                                RedTree::Node(en) => {
426                                    value = Some(self.extract_expression(en, source)?);
427                                }
428                                _ => {}
429                            }
430                        }
431                        vars.push(Declaration::Variable(ast::Variable { name, var_type, value, span: n.span() }));
432                    }
433                    _ => {
434                        // Handle var ( ... )
435                        vars.extend(self.extract_variables(n, source, cache)?);
436                    }
437                }
438            }
439        }
440        cache.set_typed_node(&node.green, std::sync::Arc::new(vars.clone()));
441        Ok(vars)
442    }
443
444    fn extract_consts(&self, node: RedNode<GoLanguage>, source: &SourceText, cache: &mut impl BuilderCache<GoLanguage>) -> Result<Vec<Declaration>, OakError> {
445        if let Some(cached) = cache.get_typed_node::<Vec<Declaration>>(&node.green) {
446            return Ok(cached);
447        }
448        let mut consts = vec![];
449        for child in node.children() {
450            if let RedTree::Node(n) = child {
451                match n.green.kind {
452                    GoElementType::ConstSpec => {
453                        let mut name = String::new();
454                        let mut const_type = None;
455                        let mut value = ast::Expression::Literal { value: "0".to_string(), span: n.span() };
456                        for spec_child in n.children() {
457                            match spec_child {
458                                RedTree::Leaf(leaf) if leaf.kind == GoTokenType::Identifier => {
459                                    if name.is_empty() {
460                                        name = source.get_text_in(leaf.span).trim().to_string();
461                                    }
462                                    else if const_type.is_none() {
463                                        const_type = Some(source.get_text_in(leaf.span).trim().to_string());
464                                    }
465                                }
466                                RedTree::Node(en) => {
467                                    value = self.extract_expression(en, source)?;
468                                }
469                                _ => {}
470                            }
471                        }
472                        consts.push(Declaration::Const(ast::Const { name, const_type, value, span: n.span() }));
473                    }
474                    _ => {
475                        // Handle const ( ... )
476                        consts.extend(self.extract_consts(n, source, cache)?);
477                    }
478                }
479            }
480        }
481        cache.set_typed_node(&node.green, std::sync::Arc::new(consts.clone()));
482        Ok(consts)
483    }
484
485    fn extract_types(&self, node: RedNode<GoLanguage>, source: &SourceText, cache: &mut impl BuilderCache<GoLanguage>) -> Result<Vec<Declaration>, OakError> {
486        if let Some(cached) = cache.get_typed_node::<Vec<Declaration>>(&node.green) {
487            return Ok(cached);
488        }
489        let mut types = vec![];
490        for child in node.children() {
491            if let RedTree::Node(n) = child {
492                match n.green.kind {
493                    GoElementType::TypeSpec => {
494                        let mut name = String::new();
495                        let mut definition = String::new();
496                        for spec_child in n.children() {
497                            match spec_child {
498                                RedTree::Leaf(leaf) if leaf.kind == GoTokenType::Identifier => {
499                                    if name.is_empty() {
500                                        name = source.get_text_in(leaf.span).trim().to_string();
501                                    }
502                                    else {
503                                        definition = source.get_text_in(leaf.span).trim().to_string();
504                                    }
505                                }
506                                RedTree::Node(tn) => {
507                                    definition = source.get_text_in(tn.span()).to_string();
508                                }
509                                _ => {}
510                            }
511                        }
512                        types.push(Declaration::Type(ast::TypeDecl { name, definition, span: n.span() }));
513                    }
514                    _ => {
515                        // Handle type ( ... )
516                        types.extend(self.extract_types(n, source, cache)?);
517                    }
518                }
519            }
520        }
521        cache.set_typed_node(&node.green, std::sync::Arc::new(types.clone()));
522        Ok(types)
523    }
524}