oak_actionscript/builder/
mod.rs1use 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#[derive(Clone)]
10pub struct ActionScriptBuilder<'config> {
11 config: &'config ActionScriptLanguage,
13}
14
15impl<'config> ActionScriptBuilder<'config> {
16 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 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 }
134 ActionScriptElementType::Implements => {
135 }
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, });
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}