Skip to main content

oak_elm/parser/
mod.rs

1/// Element type definitions for Elm.
2pub mod element_type;
3
4use crate::{
5    language::ElmLanguage,
6    lexer::{ElmLexer, token_type::ElmTokenType},
7    parser::element_type::ElmElementType,
8};
9use oak_core::{
10    GreenNode, OakError,
11    parser::{Associativity, ParseCache, ParseOutput, Parser, ParserState, Pratt, PrattParser, binary, parse_with_lexer, unary},
12    source::{Source, TextEdit},
13};
14
15pub(crate) type State<'a, S> = ParserState<'a, ElmLanguage, S>;
16
17/// A parser for Elm source files.
18pub struct ElmParser<'config> {
19    pub(crate) config: &'config ElmLanguage,
20}
21
22impl<'config> ElmParser<'config> {
23    /// Creates a new Elm parser with the given configuration.
24    pub fn new(config: &'config ElmLanguage) -> Self {
25        Self { config }
26    }
27
28    fn parse_item<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
29        match state.peek_kind() {
30            Some(ElmTokenType::Module) => self.parse_module(state),
31            Some(ElmTokenType::Import) => self.parse_import(state),
32            Some(ElmTokenType::Type) => {
33                if state.peek_at(1).map(|t| t.kind) == Some(ElmTokenType::Alias) {
34                    self.parse_type_alias(state)
35                }
36                else {
37                    self.parse_type_declaration(state)
38                }
39            }
40            Some(ElmTokenType::Port) => self.parse_port(state),
41            Some(ElmTokenType::Identifier) => self.parse_value_declaration(state),
42            Some(_) => {
43                state.bump();
44                Ok(())
45            }
46            None => Ok(()),
47        }
48    }
49
50    fn parse_module<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
51        let cp = state.checkpoint();
52        state.expect(ElmTokenType::Module).ok();
53        state.expect(ElmTokenType::Identifier).ok();
54        if state.eat(ElmTokenType::Exposing) {
55            state.expect(ElmTokenType::LeftParen).ok();
56            while state.not_at_end() && !state.at(ElmTokenType::RightParen) {
57                state.bump();
58            }
59            state.expect(ElmTokenType::RightParen).ok();
60        }
61        state.expect(ElmTokenType::Where).ok();
62        state.finish_at(cp, ElmElementType::Module);
63        Ok(())
64    }
65
66    fn parse_import<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
67        let cp = state.checkpoint();
68        state.expect(ElmTokenType::Import).ok();
69        state.expect(ElmTokenType::Identifier).ok();
70        if state.eat(ElmTokenType::As) {
71            state.expect(ElmTokenType::Identifier).ok();
72        }
73        if state.eat(ElmTokenType::Exposing) {
74            state.expect(ElmTokenType::LeftParen).ok();
75            while state.not_at_end() && !state.at(ElmTokenType::RightParen) {
76                state.bump();
77            }
78            state.expect(ElmTokenType::RightParen).ok();
79        }
80        state.finish_at(cp, ElmElementType::Import);
81        Ok(())
82    }
83
84    fn parse_type_declaration<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
85        let cp = state.checkpoint();
86        state.expect(ElmTokenType::Type).ok();
87        state.expect(ElmTokenType::Identifier).ok();
88        while state.not_at_end() && !state.at(ElmTokenType::Equal) {
89            state.bump();
90        }
91        if state.eat(ElmTokenType::Equal) {
92            while state.not_at_end() && !state.at(ElmTokenType::Newline) {
93                state.bump();
94            }
95        }
96        state.finish_at(cp, ElmElementType::TypeDeclaration);
97        Ok(())
98    }
99
100    fn parse_type_alias<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
101        let cp = state.checkpoint();
102        state.expect(ElmTokenType::Type).ok();
103        state.expect(ElmTokenType::Alias).ok();
104        state.expect(ElmTokenType::Identifier).ok();
105        while state.not_at_end() && !state.at(ElmTokenType::Equal) {
106            state.bump();
107        }
108        if state.eat(ElmTokenType::Equal) {
109            while state.not_at_end() && !state.at(ElmTokenType::Newline) {
110                state.bump();
111            }
112        }
113        state.finish_at(cp, ElmElementType::TypeAlias);
114        Ok(())
115    }
116
117    fn parse_port<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
118        let cp = state.checkpoint();
119        state.expect(ElmTokenType::Port).ok();
120        state.expect(ElmTokenType::Identifier).ok();
121        state.expect(ElmTokenType::Colon).ok();
122        while state.not_at_end() && !state.at(ElmTokenType::Newline) {
123            state.bump();
124        }
125        state.finish_at(cp, ElmElementType::FunctionDeclaration);
126        Ok(())
127    }
128
129    fn parse_value_declaration<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
130        let cp = state.checkpoint();
131        if state.at(ElmTokenType::Identifier) {
132            let next = state.peek_at(1);
133            if matches!(next, Some(t) if t.kind == ElmTokenType::Colon) {
134                state.bump(); // ident
135                state.bump(); // :
136                while state.not_at_end() && !state.at(ElmTokenType::Newline) {
137                    state.bump();
138                }
139                state.finish_at(cp, ElmElementType::TypeSignature);
140                return Ok(());
141            }
142        }
143
144        while state.not_at_end() && !state.at(ElmTokenType::Equal) {
145            self.parse_pattern(state)?;
146        }
147        if state.eat(ElmTokenType::Equal) {
148            PrattParser::parse(state, 0, self);
149        }
150        state.finish_at(cp, ElmElementType::ValueDeclaration);
151        Ok(())
152    }
153
154    fn parse_pattern<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
155        let cp = state.checkpoint();
156        match state.peek_kind() {
157            Some(ElmTokenType::Identifier) | Some(ElmTokenType::Number) | Some(ElmTokenType::String) | Some(ElmTokenType::Char) => {
158                state.bump();
159            }
160            Some(ElmTokenType::LeftParen) => {
161                state.bump();
162                while state.not_at_end() && !state.at(ElmTokenType::RightParen) {
163                    self.parse_pattern(state)?;
164                }
165                state.expect(ElmTokenType::RightParen).ok();
166            }
167            Some(ElmTokenType::LeftBracket) => {
168                state.bump();
169                while state.not_at_end() && !state.at(ElmTokenType::RightBracket) {
170                    self.parse_pattern(state)?;
171                    state.eat(ElmTokenType::Comma);
172                }
173                state.expect(ElmTokenType::RightBracket).ok();
174            }
175            _ => {
176                state.bump();
177            }
178        }
179        state.finish_at(cp, ElmElementType::Pattern);
180        Ok(())
181    }
182}
183
184impl<'config> Parser<ElmLanguage> for ElmParser<'config> {
185    fn parse<'a, S: Source + ?Sized>(&self, text: &'a S, edits: &[TextEdit], cache: &'a mut impl ParseCache<ElmLanguage>) -> ParseOutput<'a, ElmLanguage> {
186        let lexer = ElmLexer::new(self.config);
187        parse_with_lexer(&lexer, text, edits, cache, |state| {
188            let checkpoint = state.checkpoint();
189
190            while state.not_at_end() {
191                self.parse_item(state)?;
192            }
193
194            Ok(state.finish_at(checkpoint, ElmElementType::Root))
195        })
196    }
197}
198
199impl<'config> Pratt<ElmLanguage> for ElmParser<'config> {
200    fn primary<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> &'a GreenNode<'a, ElmLanguage> {
201        let cp = state.checkpoint();
202        match state.peek_kind() {
203            Some(ElmTokenType::Identifier) => {
204                state.bump();
205                state.finish_at(cp, ElmElementType::Identifier)
206            }
207            Some(ElmTokenType::Number) | Some(ElmTokenType::Float) | Some(ElmTokenType::String) | Some(ElmTokenType::Char) => {
208                state.bump();
209                state.finish_at(cp, ElmElementType::Literal)
210            }
211            Some(ElmTokenType::LeftParen) => {
212                state.bump();
213                PrattParser::parse(state, 0, self);
214                state.expect(ElmTokenType::RightParen).ok();
215                state.finish_at(cp, ElmElementType::Expression)
216            }
217            Some(ElmTokenType::LeftBracket) => {
218                state.bump();
219                while state.not_at_end() && !state.at(ElmTokenType::RightBracket) {
220                    PrattParser::parse(state, 0, self);
221                    state.eat(ElmTokenType::Comma);
222                }
223                state.expect(ElmTokenType::RightBracket).ok();
224                state.finish_at(cp, ElmElementType::ListExpression)
225            }
226            _ => {
227                state.bump();
228                state.finish_at(cp, ElmElementType::Error)
229            }
230        }
231    }
232
233    fn prefix<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> &'a GreenNode<'a, ElmLanguage> {
234        let kind = match state.peek_kind() {
235            Some(k) => k,
236            None => return self.primary(state),
237        };
238
239        match kind {
240            ElmTokenType::Minus => unary(state, kind, 12, ElmElementType::UnaryExpression.into(), |s, p| PrattParser::parse(s, p, self)),
241            _ => self.primary(state),
242        }
243    }
244
245    fn infix<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>, left: &'a GreenNode<'a, ElmLanguage>, min_precedence: u8) -> Option<&'a GreenNode<'a, ElmLanguage>> {
246        let kind = state.peek_kind()?;
247
248        let (prec, assoc) = match kind {
249            ElmTokenType::Dot => (13, Associativity::Left),
250            ElmTokenType::Star | ElmTokenType::Slash | ElmTokenType::DoubleSlash | ElmTokenType::Percent => (11, Associativity::Left),
251            ElmTokenType::Plus | ElmTokenType::Minus => (10, Associativity::Left),
252            ElmTokenType::DoublePlus => (9, Associativity::Right),
253            ElmTokenType::EqualEqual | ElmTokenType::NotEqual | ElmTokenType::Less | ElmTokenType::Greater | ElmTokenType::LessEqual | ElmTokenType::GreaterEqual => (8, Associativity::Left),
254            ElmTokenType::DoubleAmpersand => (7, Associativity::Right),
255            ElmTokenType::DoublePipe => (6, Associativity::Right),
256            ElmTokenType::DoubleLess | ElmTokenType::DoubleGreater => (5, Associativity::Left),
257            _ => return None,
258        };
259
260        if prec < min_precedence {
261            return None;
262        }
263
264        Some(binary(state, left, kind, prec, assoc, ElmElementType::BinaryExpression.into(), |s, p| PrattParser::parse(s, p, self)))
265    }
266}