Skip to main content

oak_elixir/parser/
mod.rs

1use crate::{
2    language::ElixirLanguage,
3    lexer::{ElixirLexer, token_type::ElixirTokenType},
4};
5use oak_core::{
6    GreenNode, OakError, TokenType,
7    parser::{Associativity, ParseCache, ParseOutput, Parser, ParserState, Pratt, PrattParser, binary, parse_with_lexer, unary},
8    source::{Source, TextEdit},
9};
10
11/// Element type module for Elixir.
12pub mod element_type;
13pub use self::element_type::ElixirElementType;
14
15pub(crate) type State<'a, S> = ParserState<'a, ElixirLanguage, S>;
16
17/// Elixir parser implementation.
18pub struct ElixirParser<'config> {
19    pub(crate) config: &'config ElixirLanguage,
20}
21
22impl<'config> Parser<ElixirLanguage> for ElixirParser<'config> {
23    fn parse<'a, S: Source + ?Sized>(&self, text: &'a S, edits: &[TextEdit], cache: &'a mut impl ParseCache<ElixirLanguage>) -> ParseOutput<'a, ElixirLanguage> {
24        let lexer = ElixirLexer::new(self.config);
25        parse_with_lexer(&lexer, text, edits, cache, |state| {
26            let checkpoint = state.checkpoint();
27
28            while state.not_at_end() {
29                match state.peek_kind() {
30                    Some(ElixirTokenType::Defmodule) => {
31                        self.parse_module(state);
32                    }
33                    Some(ElixirTokenType::Def) | Some(ElixirTokenType::Defp) => {
34                        self.parse_function(state);
35                    }
36                    Some(_) => {
37                        PrattParser::parse(state, 0, self);
38                    }
39                    None => break,
40                }
41                state.eat(ElixirTokenType::Semicolon);
42                state.eat(ElixirTokenType::Newline);
43            }
44
45            Ok(state.finish_at(checkpoint, ElixirElementType::Root))
46        })
47    }
48}
49
50impl<'config> ElixirParser<'config> {
51    /// Creates a new `ElixirParser`.
52    pub fn new(config: &'config ElixirLanguage) -> Self {
53        Self { config }
54    }
55
56    fn parse_module<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> &'a GreenNode<'a, ElixirLanguage> {
57        let cp = state.checkpoint();
58        state.expect(ElixirTokenType::Defmodule).ok();
59        PrattParser::parse(state, 0, self); // Module name
60        state.expect(ElixirTokenType::Do).ok();
61        while state.not_at_end() && !state.at(ElixirTokenType::End) {
62            match state.peek_kind() {
63                Some(ElixirTokenType::Def) | Some(ElixirTokenType::Defp) => {
64                    self.parse_function(state);
65                }
66                Some(_) => {
67                    PrattParser::parse(state, 0, self);
68                }
69                None => break,
70            }
71            state.eat(ElixirTokenType::Semicolon);
72            state.eat(ElixirTokenType::Newline);
73        }
74        state.expect(ElixirTokenType::End).ok();
75        state.finish_at(cp, ElixirElementType::ModuleDefinition)
76    }
77
78    fn parse_function<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> &'a GreenNode<'a, ElixirLanguage> {
79        let cp = state.checkpoint();
80        state.bump(); // def or defp
81        PrattParser::parse(state, 0, self); // Function name and params
82        if state.eat(ElixirTokenType::Do) {
83            while state.not_at_end() && !state.at(ElixirTokenType::End) {
84                PrattParser::parse(state, 0, self);
85                state.eat(ElixirTokenType::Semicolon);
86                state.eat(ElixirTokenType::Newline);
87            }
88            state.expect(ElixirTokenType::End).ok();
89        }
90        state.finish_at(cp, ElixirElementType::FunctionDefinition)
91    }
92}
93
94impl<'config> Pratt<ElixirLanguage> for ElixirParser<'config> {
95    fn primary<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> &'a GreenNode<'a, ElixirLanguage> {
96        let cp = state.checkpoint();
97        match state.peek_kind() {
98            Some(ElixirTokenType::Identifier) => {
99                state.bump();
100                state.finish_at(cp, ElixirElementType::IdentifierExpression)
101            }
102            Some(ElixirTokenType::Atom) => {
103                state.bump();
104                state.finish_at(cp, ElixirElementType::LiteralExpression)
105            }
106            Some(ElixirTokenType::Number) | Some(ElixirTokenType::Float) => {
107                state.bump();
108                state.finish_at(cp, ElixirElementType::LiteralExpression)
109            }
110            Some(ElixirTokenType::String) | Some(ElixirTokenType::Character) => {
111                state.bump();
112                state.finish_at(cp, ElixirElementType::LiteralExpression)
113            }
114            Some(ElixirTokenType::LeftParen) => {
115                state.bump();
116                PrattParser::parse(state, 0, self);
117                state.expect(ElixirTokenType::RightParen).ok();
118                state.finish_at(cp, ElixirElementType::BlockExpression)
119            }
120            Some(ElixirTokenType::LeftBracket) => {
121                state.bump();
122                if !state.at(ElixirTokenType::RightBracket) {
123                    loop {
124                        PrattParser::parse(state, 0, self);
125                        if !state.eat(ElixirTokenType::Comma) {
126                            break;
127                        }
128                    }
129                }
130                state.expect(ElixirTokenType::RightBracket).ok();
131                state.finish_at(cp, ElixirElementType::ListLiteral)
132            }
133            Some(ElixirTokenType::LeftBrace) => {
134                state.bump();
135                if !state.at(ElixirTokenType::RightBrace) {
136                    loop {
137                        PrattParser::parse(state, 0, self);
138                        if !state.eat(ElixirTokenType::Comma) {
139                            break;
140                        }
141                    }
142                }
143                state.expect(ElixirTokenType::RightBrace).ok();
144                state.finish_at(cp, ElixirElementType::TupleLiteral)
145            }
146            Some(ElixirTokenType::Percent) => {
147                state.bump();
148                if state.eat(ElixirTokenType::LeftBrace) {
149                    if !state.at(ElixirTokenType::RightBrace) {
150                        loop {
151                            PrattParser::parse(state, 0, self);
152                            if !state.eat(ElixirTokenType::Comma) {
153                                break;
154                            }
155                        }
156                    }
157                    state.expect(ElixirTokenType::RightBrace).ok();
158                    state.finish_at(cp, ElixirElementType::MapLiteral)
159                }
160                else {
161                    state.finish_at(cp, ElixirElementType::Error)
162                }
163            }
164            _ => {
165                state.bump();
166                state.finish_at(cp, ElixirElementType::Error)
167            }
168        }
169    }
170
171    fn prefix<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> &'a GreenNode<'a, ElixirLanguage> {
172        let kind = match state.peek_kind() {
173            Some(k) => k,
174            None => return self.primary(state),
175        };
176
177        match kind {
178            ElixirTokenType::Plus | ElixirTokenType::Minus | ElixirTokenType::Bang | ElixirTokenType::At => unary(state, kind, 13, ElixirElementType::UnaryExpression.into(), |s, p| PrattParser::parse(s, p, self)),
179            _ => self.primary(state),
180        }
181    }
182
183    fn infix<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>, left: &'a GreenNode<'a, ElixirLanguage>, min_precedence: u8) -> Option<&'a GreenNode<'a, ElixirLanguage>> {
184        use ElixirTokenType::*;
185        let kind = state.peek_kind()?;
186
187        let (prec, assoc) = match kind {
188            Eq => (1, Associativity::Right),
189            OrOr => (2, Associativity::Left),
190            AndAnd => (3, Associativity::Left),
191            EqEq | Ne => (4, Associativity::Left),
192            Lt | Le | Gt | Ge => (5, Associativity::Left),
193            Pipeline => (6, Associativity::Left),
194            Concat | PlusPlus | MinusMinus => (7, Associativity::Left),
195            Plus | Minus => (8, Associativity::Left),
196            Mul | Div => (9, Associativity::Left),
197            Dot => (11, Associativity::Left),
198            LeftParen => (12, Associativity::Left),
199            _ => return None,
200        };
201
202        if prec < min_precedence {
203            return None;
204        }
205
206        match kind {
207            LeftParen => {
208                let cp = state.checkpoint();
209                state.push_child(left);
210                state.bump();
211                if !state.at(RightParen) {
212                    loop {
213                        PrattParser::parse(state, 0, self);
214                        if !state.eat(Comma) {
215                            break;
216                        }
217                    }
218                }
219                state.expect(RightParen).ok();
220                Some(state.finish_at(cp, ElixirElementType::CallExpression))
221            }
222            Dot => {
223                let cp = state.checkpoint();
224                state.push_child(left);
225                state.bump();
226                PrattParser::parse(state, prec + 1, self);
227                Some(state.finish_at(cp, ElixirElementType::AccessExpression))
228            }
229            Eq => Some(binary(state, left, kind, prec, assoc, ElixirElementType::MatchExpression.into(), |s, p| PrattParser::parse(s, p, self))),
230            _ => Some(binary(state, left, kind, prec, assoc, ElixirElementType::BinaryExpression.into(), |s, p| PrattParser::parse(s, p, self))),
231        }
232    }
233}