Skip to main content

oak_d/parser/
mod.rs

1#![doc = include_str!("readme.md")]
2
3pub mod element_type;
4
5use crate::{
6    language::DLanguage,
7    lexer::{DLexer, token_type::DTokenType},
8};
9use oak_core::{
10    GreenNode, TextEdit,
11    parser::{Associativity, Parser, ParserState, Pratt, PrattParser},
12    source::Source,
13};
14
15/// The state of the D parser.
16pub(crate) type State<'a, S> = ParserState<'a, DLanguage, S>;
17
18/// A parser for the D language.
19pub struct DParser<'config> {
20    pub(crate) config: &'config DLanguage,
21}
22
23impl<'config> DParser<'config> {
24    /// Creates a new D parser.
25    pub fn new(config: &'config DLanguage) -> Self {
26        Self { config }
27    }
28
29    /// Skips trivia tokens (whitespace, comments, newlines).
30    fn skip_trivia<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) {
31        while let Some(kind) = state.peek_kind() {
32            if matches!(kind, DTokenType::Whitespace | DTokenType::Newline | DTokenType::LineComment | DTokenType::BlockComment | DTokenType::NestedComment) {
33                state.bump();
34            }
35            else {
36                break;
37            }
38        }
39    }
40
41    /// Parses a module declaration.
42    fn parse_module<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), oak_core::OakError> {
43        let cp = state.checkpoint();
44        state.bump(); // module
45        self.skip_trivia(state);
46        // Parse module name
47        while let Some(kind) = state.peek_kind() {
48            if matches!(kind, DTokenType::Identifier | DTokenType::Dot) {
49                state.bump();
50            }
51            else {
52                break;
53            }
54        }
55        state.eat(DTokenType::Semicolon);
56        state.finish_at(cp, crate::parser::element_type::DElementType::Module);
57        Ok(())
58    }
59
60    /// Parses an import declaration.
61    fn parse_import<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), oak_core::OakError> {
62        let cp = state.checkpoint();
63        state.bump(); // import
64        self.skip_trivia(state);
65        // Parse import path
66        while let Some(kind) = state.peek_kind() {
67            if matches!(kind, DTokenType::Identifier | DTokenType::Dot) {
68                state.bump();
69            }
70            else {
71                break;
72            }
73        }
74        state.eat(DTokenType::Semicolon);
75        state.finish_at(cp, crate::parser::element_type::DElementType::Import);
76        Ok(())
77    }
78
79    /// Parses a class definition.
80    fn parse_class<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), oak_core::OakError> {
81        let cp = state.checkpoint();
82        state.bump(); // class
83        self.skip_trivia(state);
84        // Parse class name
85        if state.at(DTokenType::Identifier) {
86            state.bump();
87        }
88        // Parse base classes
89        self.skip_trivia(state);
90        if state.at(DTokenType::Colon) {
91            state.bump();
92            while let Some(kind) = state.peek_kind() {
93                if !matches!(kind, DTokenType::Semicolon | DTokenType::LeftBrace) {
94                    state.bump();
95                }
96                else {
97                    break;
98                }
99            }
100        }
101        // Parse class body
102        self.skip_trivia(state);
103        if state.at(DTokenType::LeftBrace) {
104            state.bump();
105            while let Some(kind) = state.peek_kind() {
106                if !matches!(kind, DTokenType::RightBrace) {
107                    self.parse_statement(state)?;
108                }
109                else {
110                    break;
111                }
112            }
113            state.eat(DTokenType::RightBrace);
114        }
115        state.eat(DTokenType::Semicolon);
116        state.finish_at(cp, crate::parser::element_type::DElementType::Class);
117        Ok(())
118    }
119
120    /// Parses a struct definition.
121    fn parse_struct<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), oak_core::OakError> {
122        let cp = state.checkpoint();
123        state.bump(); // struct
124        self.skip_trivia(state);
125        // Parse struct name
126        if state.at(DTokenType::Identifier) {
127            state.bump();
128        }
129        // Parse struct body
130        self.skip_trivia(state);
131        if state.at(DTokenType::LeftBrace) {
132            state.bump();
133            while let Some(kind) = state.peek_kind() {
134                if !matches!(kind, DTokenType::RightBrace) {
135                    self.parse_statement(state)?;
136                }
137                else {
138                    break;
139                }
140            }
141            state.eat(DTokenType::RightBrace);
142        }
143        state.eat(DTokenType::Semicolon);
144        state.finish_at(cp, crate::parser::element_type::DElementType::Struct);
145        Ok(())
146    }
147
148    /// Parses an interface definition.
149    fn parse_interface<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), oak_core::OakError> {
150        let cp = state.checkpoint();
151        state.bump(); // interface
152        self.skip_trivia(state);
153        // Parse interface name
154        if state.at(DTokenType::Identifier) {
155            state.bump();
156        }
157        // Parse base interfaces
158        self.skip_trivia(state);
159        if state.at(DTokenType::Colon) {
160            state.bump();
161            while let Some(kind) = state.peek_kind() {
162                if !matches!(kind, DTokenType::Semicolon | DTokenType::LeftBrace) {
163                    state.bump();
164                }
165                else {
166                    break;
167                }
168            }
169        }
170        // Parse interface body
171        self.skip_trivia(state);
172        if state.at(DTokenType::LeftBrace) {
173            state.bump();
174            while let Some(kind) = state.peek_kind() {
175                if !matches!(kind, DTokenType::RightBrace) {
176                    self.parse_statement(state)?;
177                }
178                else {
179                    break;
180                }
181            }
182            state.eat(DTokenType::RightBrace);
183        }
184        state.eat(DTokenType::Semicolon);
185        state.finish_at(cp, crate::parser::element_type::DElementType::Interface);
186        Ok(())
187    }
188
189    /// Parses a function definition.
190    fn parse_function<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), oak_core::OakError> {
191        let cp = state.checkpoint();
192        state.bump(); // function
193        self.skip_trivia(state);
194        // Parse return type
195        while let Some(kind) = state.peek_kind() {
196            if !matches!(kind, DTokenType::Identifier) {
197                break;
198            }
199            state.bump();
200        }
201        // Parse function name
202        if state.at(DTokenType::Identifier) {
203            state.bump();
204        }
205        // Parse parameters
206        self.skip_trivia(state);
207        if state.at(DTokenType::LeftParen) {
208            state.bump();
209            while let Some(kind) = state.peek_kind() {
210                if !matches!(kind, DTokenType::RightParen) {
211                    self.skip_trivia(state);
212                    // Parse parameter
213                    while let Some(kind) = state.peek_kind() {
214                        if !matches!(kind, DTokenType::Comma | DTokenType::RightParen) {
215                            state.bump();
216                        }
217                        else {
218                            break;
219                        }
220                    }
221                    if state.at(DTokenType::Comma) {
222                        state.bump();
223                    }
224                }
225                else {
226                    break;
227                }
228            }
229            state.eat(DTokenType::RightParen);
230        }
231        // Parse function body
232        self.skip_trivia(state);
233        if state.at(DTokenType::LeftBrace) {
234            state.bump();
235            while let Some(kind) = state.peek_kind() {
236                if !matches!(kind, DTokenType::RightBrace) {
237                    self.parse_statement(state)?;
238                }
239                else {
240                    break;
241                }
242            }
243            state.eat(DTokenType::RightBrace);
244        }
245        state.eat(DTokenType::Semicolon);
246        state.finish_at(cp, crate::parser::element_type::DElementType::Function);
247        Ok(())
248    }
249
250    /// Parses a statement.
251    fn parse_statement<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), oak_core::OakError> {
252        self.skip_trivia(state);
253        match state.peek_kind() {
254            Some(DTokenType::ModuleKeyword) => self.parse_module(state)?,
255            Some(DTokenType::ImportKeyword) => self.parse_import(state)?,
256            Some(DTokenType::ClassKeyword) => self.parse_class(state)?,
257            Some(DTokenType::StructKeyword) => self.parse_struct(state)?,
258            Some(DTokenType::InterfaceKeyword) => self.parse_interface(state)?,
259            Some(DTokenType::FunctionKeyword) => self.parse_function(state)?,
260            Some(DTokenType::LeftBrace) => {
261                // Compound statement
262                let cp = state.checkpoint();
263                state.bump();
264                while let Some(kind) = state.peek_kind() {
265                    if !matches!(kind, DTokenType::RightBrace) {
266                        self.parse_statement(state)?;
267                    }
268                    else {
269                        break;
270                    }
271                }
272                state.eat(DTokenType::RightBrace);
273                state.finish_at(cp, crate::parser::element_type::DElementType::Block);
274            }
275            Some(DTokenType::IfKeyword) => {
276                // If statement
277                let cp = state.checkpoint();
278                state.bump();
279                self.skip_trivia(state);
280                if state.at(DTokenType::LeftParen) {
281                    state.bump();
282                    while let Some(kind) = state.peek_kind() {
283                        if !matches!(kind, DTokenType::RightParen) {
284                            state.bump();
285                        }
286                        else {
287                            break;
288                        }
289                    }
290                    state.eat(DTokenType::RightParen);
291                }
292                self.parse_statement(state)?;
293                self.skip_trivia(state);
294                if state.at(DTokenType::ElseKeyword) {
295                    state.bump();
296                    self.parse_statement(state)?;
297                }
298                state.finish_at(cp, crate::parser::element_type::DElementType::IfStatement);
299            }
300            Some(DTokenType::WhileKeyword) => {
301                // While statement
302                let cp = state.checkpoint();
303                state.bump();
304                self.skip_trivia(state);
305                if state.at(DTokenType::LeftParen) {
306                    state.bump();
307                    while let Some(kind) = state.peek_kind() {
308                        if !matches!(kind, DTokenType::RightParen) {
309                            state.bump();
310                        }
311                        else {
312                            break;
313                        }
314                    }
315                    state.eat(DTokenType::RightParen);
316                }
317                self.parse_statement(state)?;
318                state.finish_at(cp, crate::parser::element_type::DElementType::WhileStatement);
319            }
320            Some(DTokenType::ForKeyword) => {
321                // For statement
322                let cp = state.checkpoint();
323                state.bump();
324                self.skip_trivia(state);
325                if state.at(DTokenType::LeftParen) {
326                    state.bump();
327                    // Initialization
328                    while let Some(kind) = state.peek_kind() {
329                        if !matches!(kind, DTokenType::Semicolon) {
330                            state.bump();
331                        }
332                        else {
333                            break;
334                        }
335                    }
336                    state.eat(DTokenType::Semicolon);
337                    // Condition
338                    while let Some(kind) = state.peek_kind() {
339                        if !matches!(kind, DTokenType::Semicolon) {
340                            state.bump();
341                        }
342                        else {
343                            break;
344                        }
345                    }
346                    state.eat(DTokenType::Semicolon);
347                    // Increment
348                    while let Some(kind) = state.peek_kind() {
349                        if !matches!(kind, DTokenType::RightParen) {
350                            state.bump();
351                        }
352                        else {
353                            break;
354                        }
355                    }
356                    state.eat(DTokenType::RightParen);
357                }
358                self.parse_statement(state)?;
359                state.finish_at(cp, crate::parser::element_type::DElementType::ForStatement);
360            }
361            Some(DTokenType::ReturnKeyword) => {
362                // Return statement
363                let cp = state.checkpoint();
364                state.bump();
365                self.skip_trivia(state);
366                while let Some(kind) = state.peek_kind() {
367                    if !matches!(kind, DTokenType::Semicolon) {
368                        state.bump();
369                    }
370                    else {
371                        break;
372                    }
373                }
374                state.eat(DTokenType::Semicolon);
375                state.finish_at(cp, crate::parser::element_type::DElementType::ReturnStatement);
376            }
377            _ => {
378                // Expression statement
379                let cp = state.checkpoint();
380                while let Some(kind) = state.peek_kind() {
381                    if !matches!(kind, DTokenType::Semicolon) {
382                        state.bump();
383                    }
384                    else {
385                        break;
386                    }
387                }
388                state.eat(DTokenType::Semicolon);
389                state.finish_at(cp, crate::parser::element_type::DElementType::ExpressionStatement);
390            }
391        }
392        Ok(())
393    }
394}
395
396impl<'config> Parser<DLanguage> for DParser<'config> {
397    fn parse<'a, S: Source + ?Sized>(&self, text: &'a S, edits: &[TextEdit], cache: &'a mut impl oak_core::ParseCache<DLanguage>) -> oak_core::ParseOutput<'a, DLanguage> {
398        let lexer = DLexer::new(self.config);
399        oak_core::parser::parse_with_lexer(&lexer, text, edits, cache, |state| {
400            let checkpoint = state.checkpoint();
401
402            while state.not_at_end() {
403                self.parse_statement(state)?;
404            }
405
406            Ok(state.finish_at(checkpoint, crate::parser::element_type::DElementType::Root))
407        })
408    }
409}
410
411impl<'config> Pratt<DLanguage> for DParser<'config> {
412    fn primary<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> &'a GreenNode<'a, DLanguage> {
413        self.skip_trivia(state);
414        let cp = state.checkpoint();
415        match state.peek_kind() {
416            Some(DTokenType::Identifier) => {
417                state.bump();
418                state.finish_at(cp, crate::parser::element_type::DElementType::Identifier)
419            }
420            Some(DTokenType::IntegerLiteral) | Some(DTokenType::FloatLiteral) | Some(DTokenType::StringLiteral) | Some(DTokenType::CharLiteral) => {
421                state.bump();
422                state.finish_at(cp, crate::parser::element_type::DElementType::Literal)
423            }
424            Some(DTokenType::LeftParen) => {
425                state.bump();
426                let expr = PrattParser::parse(state, 0, self);
427                state.push_child(expr);
428                self.skip_trivia(state);
429                state.eat(DTokenType::RightParen);
430                state.finish_at(cp, crate::parser::element_type::DElementType::ParenthesizedExpression)
431            }
432            _ => {
433                state.bump();
434                state.finish_at(cp, crate::parser::element_type::DElementType::Error)
435            }
436        }
437    }
438
439    fn infix<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>, left: &'a GreenNode<'a, DLanguage>, min_precedence: u8) -> Option<&'a GreenNode<'a, DLanguage>> {
440        self.skip_trivia(state);
441        let kind = state.peek_kind()?;
442
443        let (prec, assoc) = match kind {
444            DTokenType::Assign => (1, Associativity::Right),
445            DTokenType::PlusAssign | DTokenType::MinusAssign | DTokenType::MultiplyAssign | DTokenType::DivideAssign | DTokenType::ModuloAssign => (1, Associativity::Right),
446            DTokenType::LogicalOr => (2, Associativity::Left),
447            DTokenType::LogicalAnd => (3, Associativity::Left),
448            DTokenType::Equal | DTokenType::NotEqual | DTokenType::Less | DTokenType::LessEqual | DTokenType::Greater | DTokenType::GreaterEqual => (4, Associativity::Left),
449            DTokenType::Plus | DTokenType::Minus => (5, Associativity::Left),
450            DTokenType::Multiply | DTokenType::Divide | DTokenType::Modulo => (6, Associativity::Left),
451            DTokenType::Dot => (7, Associativity::Left),
452            _ => return None,
453        };
454
455        if prec < min_precedence {
456            return None;
457        }
458
459        let cp = state.checkpoint();
460        state.push_child(left);
461        state.bump();
462        self.skip_trivia(state);
463        let right = PrattParser::parse(state, prec + (assoc as u8), self);
464        state.push_child(right);
465        Some(state.finish_at(cp, crate::parser::element_type::DElementType::BinaryExpression))
466    }
467}