Skip to main content

oak_elixir/builder/
mod.rs

1use crate::{
2    ast::*,
3    language::ElixirLanguage,
4    lexer::token_type::ElixirTokenType,
5    parser::{ElixirParser, element_type::ElixirElementType},
6};
7use core::range::Range;
8use oak_core::{Builder, BuilderCache, GreenNode, OakDiagnostics, OakError, Parser, RedNode, RedTree, SourceText, TextEdit, builder::BuildOutput, source::Source};
9
10/// Elixir AST builder.
11#[derive(Clone, Copy)]
12pub struct ElixirBuilder<'config> {
13    /// Language configuration.
14    config: &'config ElixirLanguage,
15}
16
17impl<'config> ElixirBuilder<'config> {
18    /// Creates a new Elixir builder.
19    pub fn new(config: &'config ElixirLanguage) -> Self {
20        Self { config }
21    }
22}
23
24impl<'config> Builder<ElixirLanguage> for ElixirBuilder<'config> {
25    fn build<'a, S: Source + ?Sized>(&self, source: &S, edits: &[TextEdit], _cache: &'a mut impl BuilderCache<ElixirLanguage>) -> BuildOutput<ElixirLanguage> {
26        let parser = ElixirParser::new(self.config);
27
28        let mut cache = oak_core::parser::session::ParseSession::<ElixirLanguage>::default();
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> ElixirBuilder<'config> {
49    /// Builds root node
50    pub(crate) fn build_root(&self, green_tree: GreenNode<ElixirLanguage>, source: &SourceText) -> Result<ElixirRoot, 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            match child {
56                RedTree::Node(n) => {
57                    if let Some(item) = self.build_item(n, source)? {
58                        items.push(item);
59                    }
60                }
61                RedTree::Leaf(_) => {}
62            }
63        }
64        Ok(ElixirRoot { items })
65    }
66
67    fn build_item(&self, node: RedNode<ElixirLanguage>, source: &SourceText) -> Result<Option<Item>, OakError> {
68        match node.green.kind {
69            ElixirElementType::ModuleDefinition => {
70                let module = self.build_module(node, source)?;
71                Ok(Some(Item::Module(module)))
72            }
73            ElixirElementType::FunctionDefinition => {
74                let func = self.build_function(node, source)?;
75                Ok(Some(Item::Function(func)))
76            }
77            _ => {
78                if let Some(expr) = self.build_expr_opt(node, source)? {
79                    Ok(Some(Item::Statement(Statement::ExprStmt { span: expr_span(&expr), expr })))
80                }
81                else {
82                    Ok(None)
83                }
84            }
85        }
86    }
87
88    fn build_module(&self, node: RedNode<ElixirLanguage>, source: &SourceText) -> Result<Module, OakError> {
89        let span = node.span();
90        let mut name = Identifier { name: String::new(), span: (0..0).into() };
91        let mut items = Vec::new();
92
93        for child in node.children() {
94            match child {
95                RedTree::Node(n) => match n.green.kind {
96                    ElixirElementType::IdentifierExpression => {
97                        name = self.build_identifier(n, source)?;
98                    }
99                    _ => {
100                        if let Some(item) = self.build_item(n, source)? {
101                            items.push(item);
102                        }
103                    }
104                },
105                RedTree::Leaf(t) => {
106                    if t.kind == ElixirTokenType::Identifier {
107                        name = Identifier { name: text(source, t.span.clone().into()), span: t.span.clone().into() };
108                    }
109                }
110            }
111        }
112
113        Ok(Module { name, items, span: span.into() })
114    }
115
116    fn build_function(&self, node: RedNode<ElixirLanguage>, source: &SourceText) -> Result<Function, OakError> {
117        let span = node.span();
118        let mut name = Identifier { name: String::new(), span: (0..0).into() };
119        let mut params = Vec::new();
120        let mut body = Block { statements: Vec::new(), span: (0..0).into() };
121
122        for child in node.children() {
123            match child {
124                RedTree::Node(n) => match n.green.kind {
125                    ElixirElementType::IdentifierExpression => {
126                        name = self.build_identifier(n, source)?;
127                    }
128                    ElixirElementType::CallExpression => {
129                        // In Elixir, function name and params are often parsed as a call expression
130                        // e.g., def hello(name) do ... end
131                        for grand_child in n.children() {
132                            match grand_child {
133                                RedTree::Node(gn) => {
134                                    if let Some(expr) = self.build_expr_opt(gn, source)? {
135                                        let span = expr_span(&expr);
136                                        match expr {
137                                            Expr::Ident(id) if name.name.is_empty() => name = id,
138                                            _ => params.push(Param {
139                                                name: match expr {
140                                                    Expr::Ident(id) => id,
141                                                    _ => Identifier { name: "_".to_string(), span: (0..0).into() }, // simplified
142                                                },
143                                                ty: None,
144                                                span,
145                                            }),
146                                        }
147                                    }
148                                }
149                                RedTree::Leaf(gt) if gt.kind == ElixirTokenType::Identifier => {
150                                    if name.name.is_empty() {
151                                        name = Identifier { name: text(source, gt.span.clone().into()), span: gt.span.clone().into() };
152                                    }
153                                }
154                                _ => {}
155                            }
156                        }
157                    }
158                    _ => {
159                        if let Some(expr) = self.build_expr_opt(n, source)? {
160                            body.statements.push(Statement::ExprStmt { span: expr_span(&expr), expr });
161                        }
162                    }
163                },
164                RedTree::Leaf(t) if t.kind == ElixirTokenType::Identifier => {
165                    if name.name.is_empty() {
166                        name = Identifier { name: text(source, t.span.clone().into()), span: t.span.clone().into() };
167                    }
168                }
169                _ => {}
170            }
171        }
172
173        body.span = span.into();
174        Ok(Function { name, params, body, span: span.into() })
175    }
176
177    fn build_expr_opt(&self, node: RedNode<ElixirLanguage>, source: &SourceText) -> Result<Option<Expr>, OakError> {
178        match node.green.kind {
179            ElixirElementType::IdentifierExpression => Ok(Some(Expr::Ident(self.build_identifier(node, source)?))),
180            ElixirElementType::LiteralExpression => {
181                for child in node.children() {
182                    if let RedTree::Leaf(t) = child {
183                        match t.kind {
184                            ElixirTokenType::Number | ElixirTokenType::Float => {
185                                return Ok(Some(Expr::Number { value: text(source, t.span.clone().into()), span: t.span.clone().into() }));
186                            }
187                            ElixirTokenType::String => {
188                                return Ok(Some(Expr::String { value: text(source, t.span.clone().into()), span: t.span.clone().into() }));
189                            }
190                            ElixirTokenType::Atom => {
191                                return Ok(Some(Expr::Atom { value: text(source, t.span.clone().into()), span: t.span.clone().into() }));
192                            }
193                            ElixirTokenType::True => {
194                                return Ok(Some(Expr::Bool { value: true, span: t.span.clone().into() }));
195                            }
196                            ElixirTokenType::False => {
197                                return Ok(Some(Expr::Bool { value: false, span: t.span.clone().into() }));
198                            }
199                            _ => {}
200                        }
201                    }
202                }
203                Ok(None)
204            }
205            ElixirElementType::BinaryExpression => {
206                let mut left = None;
207                let mut op = None;
208                let mut right = None;
209                let span = node.span();
210
211                for child in node.children() {
212                    match child {
213                        RedTree::Node(n) => {
214                            let expr = self.build_expr_opt(n, source)?;
215                            if left.is_none() {
216                                left = expr;
217                            }
218                            else {
219                                right = expr;
220                            }
221                        }
222                        RedTree::Leaf(t) => {
223                            op = Some(t.kind);
224                        }
225                    }
226                }
227
228                if let (Some(l), Some(o), Some(r)) = (left, op, right) { Ok(Some(Expr::Binary { left: Box::new(l), op: o, right: Box::new(r), span: span.into() })) } else { Ok(None) }
229            }
230            ElixirElementType::MatchExpression => {
231                let mut left = None;
232                let mut right = None;
233                let span = node.span();
234
235                for child in node.children() {
236                    if let RedTree::Node(n) = child {
237                        let expr = self.build_expr_opt(n, source)?;
238                        if left.is_none() {
239                            left = expr;
240                        }
241                        else {
242                            right = expr;
243                        }
244                    }
245                }
246
247                if let (Some(l), Some(r)) = (left, right) { Ok(Some(Expr::Match { left: Box::new(l), right: Box::new(r), span: span.into() })) } else { Ok(None) }
248            }
249            ElixirElementType::UnaryExpression => {
250                let mut op = None;
251                let mut expr = None;
252                let span = node.span();
253
254                for child in node.children() {
255                    match child {
256                        RedTree::Node(n) => {
257                            expr = self.build_expr_opt(n, source)?;
258                        }
259                        RedTree::Leaf(t) => {
260                            op = Some(t.kind);
261                        }
262                    }
263                }
264
265                if let (Some(o), Some(e)) = (op, expr) {
266                    if o == ElixirTokenType::At {
267                        if let Expr::Ident(id) = e {
268                            return Ok(Some(Expr::Attribute { name: id, span: span.into() }));
269                        }
270                    }
271                    Ok(Some(Expr::Unary { op: o, expr: Box::new(e), span: span.into() }))
272                }
273                else {
274                    Ok(None)
275                }
276            }
277            ElixirElementType::CallExpression => {
278                let mut callee = None;
279                let mut args = Vec::new();
280                let span = node.span();
281
282                for child in node.children() {
283                    match child {
284                        RedTree::Node(n) => {
285                            if let Some(expr) = self.build_expr_opt(n, source)? {
286                                if callee.is_none() {
287                                    callee = Some(Box::new(expr));
288                                }
289                                else {
290                                    args.push(expr);
291                                }
292                            }
293                        }
294                        RedTree::Leaf(t) if t.kind == ElixirTokenType::Identifier => {
295                            if callee.is_none() {
296                                callee = Some(Box::new(Expr::Ident(Identifier { name: text(source, t.span.clone().into()), span: t.span.clone().into() })));
297                            }
298                        }
299                        _ => {}
300                    }
301                }
302
303                if let Some(c) = callee { Ok(Some(Expr::Call { callee: c, args, span: span.into() })) } else { Ok(None) }
304            }
305            ElixirElementType::AccessExpression => {
306                let mut receiver = None;
307                let mut field = None;
308                let span = node.span();
309
310                for child in node.children() {
311                    match child {
312                        RedTree::Node(n) => {
313                            if let Some(expr) = self.build_expr_opt(n, source)? {
314                                if receiver.is_none() {
315                                    receiver = Some(Box::new(expr));
316                                }
317                                else if let Expr::Ident(id) = expr {
318                                    field = Some(id);
319                                }
320                            }
321                        }
322                        RedTree::Leaf(t) if t.kind == ElixirTokenType::Identifier => {
323                            if receiver.is_none() {
324                                receiver = Some(Box::new(Expr::Ident(Identifier { name: text(source, t.span.clone().into()), span: t.span.clone().into() })));
325                            }
326                            else {
327                                field = Some(Identifier { name: text(source, t.span.clone().into()), span: t.span.clone().into() });
328                            }
329                        }
330                        _ => {}
331                    }
332                }
333
334                if let (Some(r), Some(f)) = (receiver, field) { Ok(Some(Expr::Field { receiver: r, field: f, span: span.into() })) } else { Ok(None) }
335            }
336            ElixirElementType::BlockExpression => {
337                let mut statements = Vec::new();
338                let span = node.span();
339
340                for child in node.children() {
341                    if let RedTree::Node(n) = child {
342                        if let Some(expr) = self.build_expr_opt(n, source)? {
343                            statements.push(Statement::ExprStmt { span: expr_span(&expr), expr });
344                        }
345                    }
346                }
347                Ok(Some(Expr::Block(Block { statements, span: span.into() })))
348            }
349            ElixirElementType::ListLiteral => {
350                let mut items = Vec::new();
351                let span = node.span();
352                for child in node.children() {
353                    if let RedTree::Node(n) = child {
354                        if let Some(expr) = self.build_expr_opt(n, source)? {
355                            items.push(expr);
356                        }
357                    }
358                }
359                Ok(Some(Expr::List { items, span: span.into() }))
360            }
361            ElixirElementType::TupleLiteral => {
362                let mut items = Vec::new();
363                let span = node.span();
364                for child in node.children() {
365                    if let RedTree::Node(n) = child {
366                        if let Some(expr) = self.build_expr_opt(n, source)? {
367                            items.push(expr);
368                        }
369                    }
370                }
371                Ok(Some(Expr::Tuple { items, span: span.into() }))
372            }
373            _ => Ok(None),
374        }
375    }
376
377    fn build_identifier(&self, node: RedNode<ElixirLanguage>, source: &SourceText) -> Result<Identifier, OakError> {
378        let span = node.span();
379        for child in node.children() {
380            if let RedTree::Leaf(t) = child {
381                if t.kind == ElixirTokenType::Identifier {
382                    return Ok(Identifier { name: text(source, t.span.clone().into()), span: t.span.clone().into() });
383                }
384            }
385        }
386        Ok(Identifier { name: String::new(), span: span.into() })
387    }
388}
389
390fn text(source: &SourceText, span: Range<usize>) -> String {
391    source.get_text_in(span).into_owned()
392}
393
394fn expr_span(expr: &Expr) -> Range<usize> {
395    match expr {
396        Expr::Ident(id) => id.span.clone(),
397        Expr::Atom { span, .. } => span.clone(),
398        Expr::Number { span, .. } => span.clone(),
399        Expr::String { span, .. } => span.clone(),
400        Expr::Bool { span, .. } => span.clone(),
401        Expr::Unary { span, .. } => span.clone(),
402        Expr::Binary { span, .. } => span.clone(),
403        Expr::Match { span, .. } => span.clone(),
404        Expr::Call { span, .. } => span.clone(),
405        Expr::Field { span, .. } => span.clone(),
406        Expr::Attribute { span, .. } => span.clone(),
407        Expr::Index { span, .. } => span.clone(),
408        Expr::Paren { span, .. } => span.clone(),
409        Expr::List { span, .. } => span.clone(),
410        Expr::Tuple { span, .. } => span.clone(),
411        Expr::Map { span, .. } => span.clone(),
412        Expr::Block(b) => b.span.clone(),
413    }
414}