Skip to main content

oak_actionscript/builder/
mod.rs

1use crate::{
2    ast::*,
3    language::ActionScriptLanguage,
4    parser::{ActionScriptElementType, ActionScriptParser},
5};
6use oak_core::{Builder, BuilderCache, GreenNode, Lexer, OakDiagnostics, OakError, Parser, RedNode, RedTree, SourceText, TextEdit, source::Source};
7
8/// AST builder for the ActionScript language.
9#[derive(Clone)]
10pub struct ActionScriptBuilder<'config> {
11    /// Language configuration
12    config: &'config ActionScriptLanguage,
13}
14
15impl<'config> ActionScriptBuilder<'config> {
16    /// Creates a new ActionScript builder.
17    pub fn new(config: &'config ActionScriptLanguage) -> Self {
18        Self { config }
19    }
20}
21
22impl<'config> Builder<ActionScriptLanguage> for ActionScriptBuilder<'config> {
23    fn build<'a, S: Source + ?Sized>(&self, source: &S, edits: &[TextEdit], _cache: &'a mut impl BuilderCache<ActionScriptLanguage>) -> oak_core::builder::BuildOutput<ActionScriptLanguage> {
24        let parser = ActionScriptParser::new(self.config);
25        let lexer = crate::lexer::ActionScriptLexer::new(&self.config);
26
27        let mut cache = oak_core::parser::session::ParseSession::<ActionScriptLanguage>::default();
28        lexer.lex(source, edits, &mut cache);
29        let parse_result = parser.parse(source, edits, &mut cache);
30
31        match parse_result.result {
32            Ok(green_tree) => {
33                let source_text = SourceText::new(source.get_text_in((0..source.length()).into()).into_owned());
34                match self.build_root(green_tree.clone(), &source_text) {
35                    Ok(ast_root) => OakDiagnostics { result: Ok(ast_root), diagnostics: parse_result.diagnostics },
36                    Err(build_error) => {
37                        let mut diagnostics = parse_result.diagnostics;
38                        diagnostics.push(build_error.clone());
39                        OakDiagnostics { result: Err(build_error), diagnostics }
40                    }
41                }
42            }
43            Err(parse_error) => OakDiagnostics { result: Err(parse_error), diagnostics: parse_result.diagnostics },
44        }
45    }
46}
47
48impl<'config> ActionScriptBuilder<'config> {
49    /// Builds the root node.
50    pub(crate) fn build_root(&self, green_tree: GreenNode<ActionScriptLanguage>, source: &SourceText) -> Result<ActionScriptRoot, OakError> {
51        let red_root = RedNode::new(&green_tree, 0);
52        let mut items = Vec::new();
53
54        for child in red_root.children() {
55            if let RedTree::Node(n) = child {
56                if let Some(item) = self.build_item(&n, source) {
57                    items.push(item);
58                }
59            }
60        }
61        Ok(ActionScriptRoot { items })
62    }
63
64    fn build_item(&self, node: &RedNode<ActionScriptLanguage>, source: &SourceText) -> Option<ActionScriptItem> {
65        match node.green.kind {
66            ActionScriptElementType::Package => self.build_package(node, source).map(ActionScriptItem::Package),
67            ActionScriptElementType::Class => self.build_class(node, source).map(ActionScriptItem::Class),
68            ActionScriptElementType::Interface => self.build_interface(node, source).map(ActionScriptItem::Interface),
69            ActionScriptElementType::Function => self.build_function(node, source).map(ActionScriptItem::Function),
70            ActionScriptElementType::Variable => self.build_variable(node, source).map(ActionScriptItem::Variable),
71            ActionScriptElementType::Import => self.build_import(node, source).map(ActionScriptItem::Import),
72            _ => None,
73        }
74    }
75
76    fn build_package(&self, node: &RedNode<ActionScriptLanguage>, source: &SourceText) -> Option<PackageDeclaration> {
77        let mut name = None;
78        let mut items = Vec::new();
79
80        for child in node.children() {
81            if let RedTree::Node(n) = child {
82                match n.green.kind {
83                    ActionScriptElementType::Identifier => {
84                        name = Some(Identifier { name: source.get_text_in(n.span()).to_string(), span: n.span() });
85                    }
86                    ActionScriptElementType::Block => {
87                        for block_child in n.children() {
88                            if let RedTree::Node(bn) = block_child {
89                                if let Some(item) = self.build_item(&bn, source) {
90                                    items.push(item);
91                                }
92                            }
93                        }
94                    }
95                    _ => {
96                        if let Some(item) = self.build_item(&n, source) {
97                            items.push(item);
98                        }
99                    }
100                }
101            }
102        }
103
104        Some(PackageDeclaration { name, items, span: node.span() })
105    }
106
107    fn build_class(&self, node: &RedNode<ActionScriptLanguage>, source: &SourceText) -> Option<ClassDeclaration> {
108        let mut name = None;
109        let mut modifiers = Vec::new();
110        let extends = None;
111        let implements = Vec::new();
112        let mut items = Vec::new();
113
114        for child in node.children() {
115            if let RedTree::Node(n) = child {
116                match n.green.kind {
117                    ActionScriptElementType::Public
118                    | ActionScriptElementType::Private
119                    | ActionScriptElementType::Internal
120                    | ActionScriptElementType::Protected
121                    | ActionScriptElementType::Static
122                    | ActionScriptElementType::Final
123                    | ActionScriptElementType::Dynamic => {
124                        modifiers.push(source.get_text_in(n.span()).to_string());
125                    }
126                    ActionScriptElementType::Identifier => {
127                        if name.is_none() {
128                            name = Some(Identifier { name: source.get_text_in(n.span()).to_string(), span: n.span() });
129                        }
130                    }
131                    ActionScriptElementType::Extends => {
132                        // The next identifier is the base class
133                    }
134                    ActionScriptElementType::Implements => {
135                        // Subsequent identifiers are interfaces
136                    }
137                    ActionScriptElementType::Block => {
138                        for block_child in n.children() {
139                            if let RedTree::Node(bn) = block_child {
140                                if let Some(item) = self.build_item(&bn, source) {
141                                    items.push(item);
142                                }
143                            }
144                        }
145                    }
146                    _ => {
147                        if let Some(item) = self.build_item(&n, source) {
148                            items.push(item);
149                        }
150                    }
151                }
152            }
153        }
154
155        name.map(|name| ClassDeclaration { name, modifiers, extends, implements, items, span: node.span() })
156    }
157
158    fn build_interface(&self, node: &RedNode<ActionScriptLanguage>, source: &SourceText) -> Option<InterfaceDeclaration> {
159        let mut name = None;
160        let mut extends = Vec::new();
161        let mut items = Vec::new();
162
163        for child in node.children() {
164            if let RedTree::Node(n) = child {
165                match n.green.kind {
166                    ActionScriptElementType::Identifier => {
167                        if name.is_none() {
168                            name = Some(Identifier { name: source.get_text_in(n.span()).to_string(), span: n.span() });
169                        }
170                        else {
171                            extends.push(Identifier { name: source.get_text_in(n.span()).to_string(), span: n.span() });
172                        }
173                    }
174                    ActionScriptElementType::Block => {
175                        for block_child in n.children() {
176                            if let RedTree::Node(bn) = block_child {
177                                if let Some(item) = self.build_item(&bn, source) {
178                                    items.push(item);
179                                }
180                            }
181                        }
182                    }
183                    _ => {
184                        if let Some(item) = self.build_item(&n, source) {
185                            items.push(item);
186                        }
187                    }
188                }
189            }
190        }
191
192        name.map(|name| InterfaceDeclaration { name, extends, items, span: node.span() })
193    }
194
195    fn build_function(&self, node: &RedNode<ActionScriptLanguage>, source: &SourceText) -> Option<FunctionDeclaration> {
196        let mut name = None;
197        let mut parameters = Vec::new();
198        let mut return_type = None;
199        let mut found_colon = false;
200
201        for child in node.children() {
202            if let RedTree::Node(n) = child {
203                match n.green.kind {
204                    ActionScriptElementType::Identifier => {
205                        if name.is_none() {
206                            name = Some(Identifier { name: source.get_text_in(n.span()).to_string(), span: n.span() });
207                        }
208                        else if found_colon {
209                            return_type = Some(Identifier { name: source.get_text_in(n.span()).to_string(), span: n.span() });
210                        }
211                    }
212                    ActionScriptElementType::ParameterList => {
213                        parameters = self.build_parameters(&n, source);
214                    }
215                    ActionScriptElementType::Colon => {
216                        found_colon = true;
217                    }
218                    _ => {}
219                }
220            }
221        }
222
223        name.map(|name| FunctionDeclaration { name, parameters, return_type, span: node.span() })
224    }
225
226    fn build_parameters(&self, node: &RedNode<ActionScriptLanguage>, source: &SourceText) -> Vec<Parameter> {
227        let mut params = Vec::new();
228        for child in node.children() {
229            if let RedTree::Node(n) = child {
230                if n.green.kind == ActionScriptElementType::Identifier {
231                    params.push(Parameter {
232                        name: Identifier { name: source.get_text_in(n.span()).to_string(), span: n.span() },
233                        type_annotation: None, // Simplified
234                    });
235                }
236            }
237        }
238        params
239    }
240
241    fn build_variable(&self, node: &RedNode<ActionScriptLanguage>, source: &SourceText) -> Option<VariableDeclaration> {
242        let mut name = None;
243        let mut type_annotation = None;
244        let mut is_const = false;
245        let mut found_colon = false;
246
247        for child in node.children() {
248            if let RedTree::Node(n) = child {
249                match n.green.kind {
250                    ActionScriptElementType::Const => {
251                        is_const = true;
252                    }
253                    ActionScriptElementType::Identifier => {
254                        if name.is_none() {
255                            name = Some(Identifier { name: source.get_text_in(n.span()).to_string(), span: n.span() });
256                        }
257                        else if found_colon {
258                            type_annotation = Some(Identifier { name: source.get_text_in(n.span()).to_string(), span: n.span() });
259                        }
260                    }
261                    ActionScriptElementType::Colon => {
262                        found_colon = true;
263                    }
264                    _ => {}
265                }
266            }
267        }
268
269        name.map(|name| VariableDeclaration { name, type_annotation, is_const, span: node.span() })
270    }
271
272    fn build_import(&self, node: &RedNode<ActionScriptLanguage>, source: &SourceText) -> Option<ImportDeclaration> {
273        let mut path = String::new();
274
275        for child in node.children() {
276            if let RedTree::Node(n) = child {
277                if n.green.kind == ActionScriptElementType::Identifier || n.green.kind == ActionScriptElementType::Dot || n.green.kind == ActionScriptElementType::Star {
278                    path.push_str(&source.get_text_in(n.span()));
279                }
280            }
281        }
282
283        if path.is_empty() { None } else { Some(ImportDeclaration { path, span: node.span() }) }
284    }
285}