Skip to main content

oak_zig/builder/
mod.rs

1#![doc = include_str!("readme.md")]
2use crate::{
3    ast::*,
4    language::ZigLanguage,
5    parser::{ZigParser, element_type::ZigElementType},
6};
7use oak_core::{Builder, BuilderCache, GreenNode, OakDiagnostics, Parser, RedNode, RedTree, SourceText, TextEdit, source::Source};
8use std::sync::Arc;
9
10/// AST builder for the Zig language
11#[derive(Clone)]
12pub struct ZigBuilder<'config> {
13    /// Language configuration
14    config: &'config ZigLanguage,
15}
16
17impl<'config> ZigBuilder<'config> {
18    /// Creates a new Zig builder
19    pub fn new(config: &'config ZigLanguage) -> Self {
20        Self { config }
21    }
22
23    /// Builds the AST root node from the syntax tree
24    pub fn build_root(&self, green: &GreenNode<ZigLanguage>, source: &SourceText) -> Result<ZigRoot, oak_core::OakError> {
25        let mut items = Vec::new();
26        let red = RedNode::new(green, 0);
27
28        for child in red.children() {
29            if let RedTree::Node(n) = child {
30                match n.green.kind {
31                    ZigElementType::Comptime => {
32                        // In Zig, comptime can be followed by a block or an expression.
33                        // For simplicity, we check if any child is a Block.
34                        let mut is_block = false;
35                        for child in n.children() {
36                            if let RedTree::Node(cn) = child {
37                                if cn.green.kind == ZigElementType::Block {
38                                    items.push(Item::Comptime(self.build_block(cn, source)?));
39                                    is_block = true;
40                                    break;
41                                }
42                            }
43                        }
44                        if !is_block {
45                            // If not a block, it might be an expression.
46                            // In a real implementation, we'd build a Statement or Expression node.
47                            // For Item::Comptime, we currently only support Arc<Block>.
48                        }
49                    }
50                    ZigElementType::Var | ZigElementType::Const => {
51                        let decl = self.build_declaration(n, source)?;
52                        items.push(Item::Declaration(decl));
53                    }
54                    ZigElementType::ContainerField => {
55                        let field = self.build_container_field(n, source)?;
56                        items.push(Item::ContainerField(field));
57                    }
58                    _ => {}
59                }
60            }
61        }
62
63        Ok(ZigRoot { items })
64    }
65
66    fn build_block(&self, node: RedNode<ZigLanguage>, source: &SourceText) -> Result<Arc<Block>, oak_core::OakError> {
67        let mut statements = Vec::new();
68
69        for child in node.children() {
70            if let RedTree::Node(n) = child {
71                let stmt = self.build_statement(n, source)?;
72                statements.push(stmt);
73            }
74        }
75
76        Ok(Arc::new(Block { statements, span: node.span().into() }))
77    }
78
79    fn build_statement(&self, node: RedNode<ZigLanguage>, source: &SourceText) -> Result<Statement, oak_core::OakError> {
80        let kind = node.green.kind;
81        match kind {
82            ZigElementType::Block => Ok(Statement::Block(self.build_block(node, source)?)),
83            ZigElementType::IfStatement => {
84                // Simplified
85                Ok(Statement::Expression(Expression::Identifier("if".to_string())))
86            }
87            ZigElementType::WhileStatement => {
88                // Simplified
89                Ok(Statement::Expression(Expression::Identifier("while".to_string())))
90            }
91            ZigElementType::ForStatement => {
92                // Simplified
93                Ok(Statement::Expression(Expression::Identifier("for".to_string())))
94            }
95            ZigElementType::ReturnStatement => {
96                // Simplified
97                Ok(Statement::Expression(Expression::Identifier("return".to_string())))
98            }
99            ZigElementType::BreakStatement => {
100                // Simplified
101                Ok(Statement::Expression(Expression::Identifier("break".to_string())))
102            }
103            ZigElementType::ContinueStatement => {
104                // Simplified
105                Ok(Statement::Expression(Expression::Identifier("continue".to_string())))
106            }
107            ZigElementType::DeferStatement => {
108                // Simplified
109                Ok(Statement::Expression(Expression::Identifier("defer".to_string())))
110            }
111            ZigElementType::Comptime => {
112                // Check if it's a block
113                for child in node.children() {
114                    if let RedTree::Node(n) = child {
115                        if n.green.kind == ZigElementType::Block {
116                            return Ok(Statement::Comptime(self.build_block(n, source)?));
117                        }
118                    }
119                }
120                // Fallback or handle expression-based comptime
121                Ok(Statement::Expression(Expression::Identifier("comptime_expr".to_string())))
122            }
123            ZigElementType::Var | ZigElementType::Const => {
124                let decl = self.build_declaration(node, source)?;
125                Ok(Statement::Declaration(decl))
126            }
127            _ => {
128                // Simplified
129                Ok(Statement::Expression(Expression::Identifier("stmt".to_string())))
130            }
131        }
132    }
133
134    fn build_declaration(&self, node: RedNode<ZigLanguage>, _source: &SourceText) -> Result<Arc<Declaration>, oak_core::OakError> {
135        let span = node.span();
136        let decl = Arc::new(Declaration { name: "decl".to_string(), is_comptime: false, span: span.into() });
137        Ok(decl)
138    }
139
140    fn build_container_field(&self, node: RedNode<ZigLanguage>, _source: &SourceText) -> Result<Arc<ContainerField>, oak_core::OakError> {
141        let span = node.span();
142        let field = Arc::new(ContainerField { name: "field".to_string(), span: span.into() });
143        Ok(field)
144    }
145}
146
147impl<'config> Builder<ZigLanguage> for ZigBuilder<'config> {
148    fn build<'a, S: Source + ?Sized>(&self, source: &'a S, edits: &[TextEdit], cache: &'a mut impl BuilderCache<ZigLanguage>) -> oak_core::builder::BuildOutput<ZigLanguage> {
149        let parser = ZigParser::new(self.config);
150        let parse_result = parser.parse(source, edits, cache);
151
152        match parse_result.result {
153            Ok(green_tree) => {
154                let source_text = SourceText::new(source.get_text_in((0..source.length()).into()).into_owned());
155                match self.build_root(green_tree, &source_text) {
156                    Ok(ast_root) => OakDiagnostics { result: Ok(ast_root), diagnostics: parse_result.diagnostics },
157                    Err(build_error) => {
158                        let mut diagnostics = parse_result.diagnostics;
159                        diagnostics.push(build_error.clone());
160                        OakDiagnostics { result: Err(build_error), diagnostics }
161                    }
162                }
163            }
164            Err(parse_error) => OakDiagnostics { result: Err(parse_error), diagnostics: parse_result.diagnostics },
165        }
166    }
167}