Skip to main content

oak_wit_component/builder/
mod.rs

1use crate::{
2    ast::*,
3    language::WitLanguage,
4    lexer::token_type::WitTokenType,
5    parser::{WitParser, element_type::WitElementType},
6};
7use oak_core::{Builder, BuilderCache, GreenNode, OakDiagnostics, OakError, Parser, RedNode, RedTree, Source, SourceText, TextEdit, builder::BuildOutput};
8
9/// AST builder for the WIT Component format.
10#[derive(Clone, Copy)]
11pub struct WitBuilder<'config> {
12    /// Language configuration
13    config: &'config WitLanguage,
14}
15
16impl<'config> WitBuilder<'config> {
17    /// Creates a new `WitBuilder` with the given configuration.
18    pub const fn new(config: &'config WitLanguage) -> Self {
19        Self { config }
20    }
21}
22
23impl<'config> Builder<WitLanguage> for WitBuilder<'config> {
24    fn build<'a, S: oak_core::source::Source + ?Sized>(&self, source: &S, edits: &[TextEdit], _cache: &'a mut impl BuilderCache<WitLanguage>) -> BuildOutput<WitLanguage> {
25        let parser = WitParser::new(self.config);
26        let mut session = oak_core::parser::session::ParseSession::<WitLanguage>::default();
27        let parse_result = parser.parse(source, edits, &mut session);
28
29        match parse_result.result {
30            Ok(green_tree) => {
31                let source_text = SourceText::new(source.get_text_in((0..source.length()).into()).into_owned());
32                match self.build_root(green_tree, &source_text) {
33                    Ok(ast_root) => OakDiagnostics { result: Ok(ast_root), diagnostics: parse_result.diagnostics },
34                    Err(build_error) => {
35                        let mut diagnostics = parse_result.diagnostics;
36                        diagnostics.push(build_error.clone());
37                        OakDiagnostics { result: Err(build_error), diagnostics }
38                    }
39                }
40            }
41            Err(parse_error) => OakDiagnostics { result: Err(parse_error), diagnostics: parse_result.diagnostics },
42        }
43    }
44}
45
46impl<'config> WitBuilder<'config> {
47    fn build_root<'a>(&self, green_tree: &'a GreenNode<'a, WitLanguage>, source: &SourceText) -> Result<WitRoot, OakError> {
48        let red_root = RedNode::new(green_tree, 0);
49        let mut items = Vec::new();
50
51        for child in red_root.children() {
52            if let RedTree::Node(n) = child {
53                match n.green.kind {
54                    WitElementType::PackageKw => {
55                        items.push(WitItem::Package(self.build_package(n, source)?));
56                    }
57                    WitElementType::WorldKw => {
58                        items.push(WitItem::World(self.build_world(n, source)?));
59                    }
60                    WitElementType::InterfaceKw => {
61                        items.push(WitItem::Interface(self.build_interface(n, source)?));
62                    }
63                    _ => {}
64                }
65            }
66        }
67
68        Ok(WitRoot { items })
69    }
70
71    fn build_package(&self, node: RedNode<WitLanguage>, source: &SourceText) -> Result<WitPackage, OakError> {
72        let mut name = String::new();
73
74        for child in node.children() {
75            if let RedTree::Leaf(t) = child {
76                if t.kind == WitTokenType::Identifier {
77                    name = source.get_text_in(t.span.clone().into()).to_string();
78                }
79            }
80        }
81
82        Ok(WitPackage { name })
83    }
84
85    fn build_world(&self, node: RedNode<WitLanguage>, source: &SourceText) -> Result<WitWorld, OakError> {
86        let mut name = String::new();
87        let mut items = Vec::new();
88
89        for child in node.children() {
90            match child {
91                RedTree::Leaf(t) => {
92                    if t.kind == WitTokenType::Identifier {
93                        name = source.get_text_in(t.span.clone().into()).to_string();
94                    }
95                }
96                RedTree::Node(n) => match n.green.kind {
97                    WitElementType::ImportKw => {
98                        items.push(WitWorldItem::Import(self.build_import(n, source)?));
99                    }
100                    WitElementType::ExportKw => {
101                        items.push(WitWorldItem::Export(self.build_export(n, source)?));
102                    }
103                    WitElementType::IncludeKw => {
104                        items.push(WitWorldItem::Include(self.build_include(n, source)?));
105                    }
106                    _ => {}
107                },
108            }
109        }
110
111        Ok(WitWorld { name, items })
112    }
113
114    fn build_interface(&self, node: RedNode<WitLanguage>, source: &SourceText) -> Result<WitInterface, OakError> {
115        let mut name = String::new();
116        let mut items = Vec::new();
117
118        for child in node.children() {
119            match child {
120                RedTree::Leaf(t) => {
121                    if t.kind == WitTokenType::Identifier {
122                        name = source.get_text_in(t.span.clone().into()).to_string();
123                    }
124                }
125                RedTree::Node(n) => match n.green.kind {
126                    WitElementType::TypeKw => {
127                        items.push(WitInterfaceItem::Type(self.build_type(n, source)?));
128                    }
129                    WitElementType::FuncKw => {
130                        items.push(WitInterfaceItem::Func(self.build_func(n, source)?));
131                    }
132                    _ => {}
133                },
134            }
135        }
136
137        Ok(WitInterface { name, items })
138    }
139
140    fn build_import(&self, node: RedNode<WitLanguage>, source: &SourceText) -> Result<WitImport, OakError> {
141        let mut name = String::new();
142        for child in node.children() {
143            if let RedTree::Leaf(t) = child {
144                if t.kind == WitTokenType::Identifier {
145                    name = source.get_text_in(t.span.clone().into()).to_string();
146                }
147            }
148        }
149        Ok(WitImport { name })
150    }
151
152    fn build_export(&self, node: RedNode<WitLanguage>, source: &SourceText) -> Result<WitExport, OakError> {
153        let mut name = String::new();
154        for child in node.children() {
155            if let RedTree::Leaf(t) = child {
156                if t.kind == WitTokenType::Identifier {
157                    name = source.get_text_in(t.span.clone().into()).to_string();
158                }
159            }
160        }
161        Ok(WitExport { name })
162    }
163
164    fn build_include(&self, node: RedNode<WitLanguage>, source: &SourceText) -> Result<WitInclude, OakError> {
165        let mut name = String::new();
166        for child in node.children() {
167            if let RedTree::Leaf(t) = child {
168                if t.kind == WitTokenType::Identifier {
169                    name = source.get_text_in(t.span.clone().into()).to_string();
170                }
171            }
172        }
173        Ok(WitInclude { name })
174    }
175
176    fn build_type(&self, node: RedNode<WitLanguage>, source: &SourceText) -> Result<WitType, OakError> {
177        let mut name = String::new();
178        let mut kind = WitTypeKind::Bool; // Default
179
180        for child in node.children() {
181            if let RedTree::Leaf(t) = child {
182                if t.kind == WitTokenType::Identifier {
183                    name = source.get_text_in(t.span.clone().into()).to_string();
184                }
185                else if let Some(t_kind) = self.parse_type_kind(&source.get_text_in(t.span.clone().into())) {
186                    kind = t_kind;
187                }
188            }
189        }
190
191        Ok(WitType { name, kind })
192    }
193
194    fn build_func(&self, node: RedNode<WitLanguage>, source: &SourceText) -> Result<WitFunc, OakError> {
195        let mut name = String::new();
196        let mut params = Vec::new();
197        let mut result = None;
198
199        for child in node.children() {
200            match child {
201                RedTree::Leaf(t) => {
202                    if t.kind == WitTokenType::Identifier {
203                        name = source.get_text_in(t.span.clone().into()).to_string();
204                    }
205                }
206                _ => {}
207            }
208        }
209
210        Ok(WitFunc { name, params, result })
211    }
212
213    fn parse_type_kind(&self, text: &str) -> Option<WitTypeKind> {
214        match text {
215            "bool" => Some(WitTypeKind::Bool),
216            "u32" => Some(WitTypeKind::U32),
217            "string" => Some(WitTypeKind::String),
218            _ => None,
219        }
220    }
221}