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
11pub mod element_type;
12mod parse_top_level;
13pub use self::element_type::ElixirElementType;
14
15pub(crate) type State<'a, S> = ParserState<'a, ElixirLanguage, S>;
16
17pub struct ElixirParser<'config> {
18    pub(crate) _config: &'config ElixirLanguage,
19}
20
21impl<'config> Parser<ElixirLanguage> for ElixirParser<'config> {
22    fn parse<'a, S: Source + ?Sized>(&self, text: &'a S, edits: &[TextEdit], cache: &'a mut impl ParseCache<ElixirLanguage>) -> ParseOutput<'a, ElixirLanguage> {
23        let lexer = ElixirLexer::new(self._config);
24        parse_with_lexer(&lexer, text, edits, cache, |state| self.parse_root_internal(state))
25    }
26}
27
28impl<'config> ElixirParser<'config> {
29    pub fn new(config: &'config ElixirLanguage) -> Self {
30        Self { _config: config }
31    }
32}
33
34impl<'config> Pratt<ElixirLanguage> for ElixirParser<'config> {
35    fn primary<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> &'a GreenNode<'a, ElixirLanguage> {
36        let cp = state.checkpoint();
37        match state.peek_kind() {
38            Some(ElixirTokenType::Identifier) => {
39                state.bump();
40                state.finish_at(cp, ElixirElementType::IdentifierExpression)
41            }
42            Some(ElixirTokenType::Atom) => {
43                state.bump();
44                state.finish_at(cp, ElixirElementType::LiteralExpression)
45            }
46            Some(ElixirTokenType::Number) | Some(ElixirTokenType::Float) => {
47                state.bump();
48                state.finish_at(cp, ElixirElementType::LiteralExpression)
49            }
50            Some(ElixirTokenType::String) | Some(ElixirTokenType::Character) => {
51                state.bump();
52                state.finish_at(cp, ElixirElementType::LiteralExpression)
53            }
54            Some(ElixirTokenType::LeftParen) => {
55                state.bump();
56                PrattParser::parse(state, 0, self);
57                state.expect(ElixirTokenType::RightParen).ok();
58                state.finish_at(cp, ElixirElementType::BlockExpression)
59            }
60            Some(ElixirTokenType::LeftBracket) => {
61                state.bump();
62                if !state.at(ElixirTokenType::RightBracket) {
63                    loop {
64                        PrattParser::parse(state, 0, self);
65                        if !state.eat(ElixirTokenType::Comma) {
66                            break;
67                        }
68                    }
69                }
70                state.expect(ElixirTokenType::RightBracket).ok();
71                state.finish_at(cp, ElixirElementType::ListLiteral)
72            }
73            Some(ElixirTokenType::LeftBrace) => {
74                state.bump();
75                if !state.at(ElixirTokenType::RightBrace) {
76                    loop {
77                        PrattParser::parse(state, 0, self);
78                        if !state.eat(ElixirTokenType::Comma) {
79                            break;
80                        }
81                    }
82                }
83                state.expect(ElixirTokenType::RightBrace).ok();
84                state.finish_at(cp, ElixirElementType::TupleLiteral)
85            }
86            Some(ElixirTokenType::Percent) => {
87                state.bump();
88                if state.eat(ElixirTokenType::LeftBrace) {
89                    if !state.at(ElixirTokenType::RightBrace) {
90                        loop {
91                            PrattParser::parse(state, 0, self);
92                            if !state.eat(ElixirTokenType::Comma) {
93                                break;
94                            }
95                        }
96                    }
97                    state.expect(ElixirTokenType::RightBrace).ok();
98                    state.finish_at(cp, ElixirElementType::MapLiteral)
99                }
100                else {
101                    state.finish_at(cp, ElixirElementType::Error)
102                }
103            }
104            _ => {
105                state.bump();
106                state.finish_at(cp, ElixirElementType::Error)
107            }
108        }
109    }
110
111    fn prefix<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> &'a GreenNode<'a, ElixirLanguage> {
112        let kind = match state.peek_kind() {
113            Some(k) => k,
114            None => return self.primary(state),
115        };
116
117        match kind {
118            ElixirTokenType::Plus | ElixirTokenType::Minus | ElixirTokenType::Bang | ElixirTokenType::At => unary(state, kind, 13, ElixirElementType::UnaryExpression.into(), |s, p| PrattParser::parse(s, p, self)),
119            _ => self.primary(state),
120        }
121    }
122
123    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>> {
124        use ElixirTokenType::*;
125        let kind = state.peek_kind()?;
126
127        let (prec, assoc) = match kind {
128            Eq => (1, Associativity::Right),
129            OrOr => (2, Associativity::Left),
130            AndAnd => (3, Associativity::Left),
131            EqEq | Ne => (4, Associativity::Left),
132            Lt | Le | Gt | Ge => (5, Associativity::Left),
133            Pipeline => (6, Associativity::Left),
134            Concat | PlusPlus | MinusMinus => (7, Associativity::Left),
135            Plus | Minus => (8, Associativity::Left),
136            Mul | Div => (9, Associativity::Left),
137            Dot => (11, Associativity::Left),
138            LeftParen => (12, Associativity::Left),
139            _ => return None,
140        };
141
142        if prec < min_precedence {
143            return None;
144        }
145
146        match kind {
147            LeftParen => {
148                let cp = state.checkpoint();
149                state.push_child(left);
150                state.bump();
151                if !state.at(RightParen) {
152                    loop {
153                        PrattParser::parse(state, 0, self);
154                        if !state.eat(Comma) {
155                            break;
156                        }
157                    }
158                }
159                state.expect(RightParen).ok();
160                Some(state.finish_at(cp, ElixirElementType::CallExpression))
161            }
162            Dot => {
163                let cp = state.checkpoint();
164                state.push_child(left);
165                state.bump();
166                PrattParser::parse(state, prec + 1, self);
167                Some(state.finish_at(cp, ElixirElementType::AccessExpression))
168            }
169            Eq => Some(binary(state, left, kind, prec, assoc, ElixirElementType::MatchExpression.into(), |s, p| PrattParser::parse(s, p, self))),
170            _ => Some(binary(state, left, kind, prec, assoc, ElixirElementType::BinaryExpression.into(), |s, p| PrattParser::parse(s, p, self))),
171        }
172    }
173}