Skip to main content

oak_lua/parser/
mod.rs

1/// Element types for Lua.
2pub mod element_type;
3pub use element_type::LuaElementType;
4
5use crate::{language::LuaLanguage, lexer::LuaLexer};
6use oak_core::{
7    GreenNode, OakError, TextEdit,
8    parser::{
9        ParseCache, ParseOutput, Parser, ParserState, parse_with_lexer,
10        pratt::{Associativity, Pratt, PrattParser, binary, unary},
11    },
12    source::Source,
13};
14
15pub(crate) type State<'a, S> = ParserState<'a, LuaLanguage, S>;
16
17/// A parser for Lua source files.
18pub struct LuaParser<'config> {
19    pub(crate) config: &'config LuaLanguage,
20}
21
22impl<'config> LuaParser<'config> {
23    /// Creates a new `LuaParser` with the given language configuration.
24    pub fn new(config: &'config LuaLanguage) -> Self {
25        Self { config }
26    }
27
28    fn parse_statement<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
29        use crate::lexer::LuaTokenType::*;
30        match state.peek_kind() {
31            Some(Local) => self.parse_local_statement(state)?,
32            Some(If) => self.parse_if_statement(state)?,
33            Some(While) => self.parse_while_statement(state)?,
34            Some(For) => self.parse_for_statement(state)?,
35            Some(Repeat) => self.parse_repeat_statement(state)?,
36            Some(Function) => self.parse_function_declaration(state)?,
37            Some(Return) => self.parse_return_statement(state)?,
38            Some(Break) => self.parse_break_statement(state)?,
39            Some(Do) => self.parse_do_statement(state)?,
40            Some(Goto) => self.parse_goto_statement(state)?,
41            Some(ColonColon) => self.parse_label_statement(state)?,
42            _ => {
43                let cp = state.checkpoint();
44                PrattParser::parse(state, 0, self);
45                if state.at(Comma) || state.at(Eq) {
46                    while state.eat(Comma) {
47                        PrattParser::parse(state, 0, self);
48                    }
49                    state.expect(Eq).ok();
50                    PrattParser::parse(state, 0, self);
51                    while state.eat(Comma) {
52                        PrattParser::parse(state, 0, self);
53                    }
54                    state.finish_at(cp, crate::parser::element_type::LuaElementType::AssignmentStatement);
55                }
56                else {
57                    state.finish_at(cp, crate::parser::element_type::LuaElementType::ExpressionStatement);
58                }
59                state.eat(Semicolon);
60            }
61        }
62        Ok(())
63    }
64
65    fn parse_local_statement<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
66        use crate::lexer::LuaTokenType::*;
67        let cp = state.checkpoint();
68        state.expect(Local).ok();
69        if state.eat(Function) {
70            state.expect(Identifier).ok();
71            self.parse_function_body(state)?;
72        }
73        else {
74            state.expect(Identifier).ok();
75            while state.eat(Comma) {
76                state.expect(Identifier).ok();
77            }
78            if state.eat(Eq) {
79                PrattParser::parse(state, 0, self);
80                while state.eat(Comma) {
81                    PrattParser::parse(state, 0, self);
82                }
83            }
84        }
85        state.eat(Semicolon);
86        state.finish_at(cp, crate::parser::element_type::LuaElementType::LocalStatement);
87        Ok(())
88    }
89
90    fn parse_if_statement<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
91        use crate::lexer::LuaTokenType::*;
92        let cp = state.checkpoint();
93        state.expect(If).ok();
94        PrattParser::parse(state, 0, self);
95        state.expect(Then).ok();
96        self.parse_block(state)?;
97        while state.eat(Elseif) {
98            PrattParser::parse(state, 0, self);
99            state.expect(Then).ok();
100            self.parse_block(state)?;
101        }
102        if state.eat(Else) {
103            self.parse_block(state)?;
104        }
105        state.expect(End).ok();
106        state.finish_at(cp, crate::parser::element_type::LuaElementType::IfStatement);
107        Ok(())
108    }
109
110    fn parse_while_statement<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
111        use crate::lexer::LuaTokenType::*;
112        let cp = state.checkpoint();
113        state.expect(While).ok();
114        PrattParser::parse(state, 0, self);
115        state.expect(Do).ok();
116        self.parse_block(state)?;
117        state.expect(End).ok();
118        state.finish_at(cp, crate::parser::element_type::LuaElementType::WhileStatement);
119        Ok(())
120    }
121
122    fn parse_for_statement<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
123        use crate::lexer::LuaTokenType::*;
124        let cp = state.checkpoint();
125        state.expect(For).ok();
126        state.expect(Identifier).ok();
127        if state.eat(Eq) {
128            PrattParser::parse(state, 0, self);
129            state.expect(Comma).ok();
130            PrattParser::parse(state, 0, self);
131            if state.eat(Comma) {
132                PrattParser::parse(state, 0, self);
133            }
134        }
135        else {
136            while state.eat(Comma) {
137                state.expect(Identifier).ok();
138            }
139            state.expect(In).ok();
140            PrattParser::parse(state, 0, self);
141            while state.eat(Comma) {
142                PrattParser::parse(state, 0, self);
143            }
144        }
145        state.expect(Do).ok();
146        self.parse_block(state)?;
147        state.expect(End).ok();
148        state.finish_at(cp, crate::parser::element_type::LuaElementType::ForStatement);
149        Ok(())
150    }
151
152    fn parse_repeat_statement<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
153        use crate::lexer::LuaTokenType::*;
154        let cp = state.checkpoint();
155        state.expect(Repeat).ok();
156        self.parse_block(state)?;
157        state.expect(Until).ok();
158        PrattParser::parse(state, 0, self);
159        state.finish_at(cp, crate::parser::element_type::LuaElementType::RepeatStatement);
160        Ok(())
161    }
162
163    fn parse_function_declaration<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
164        use crate::lexer::LuaTokenType::*;
165        let cp = state.checkpoint();
166        state.expect(Function).ok();
167        state.expect(Identifier).ok();
168        while state.eat(Dot) {
169            state.expect(Identifier).ok();
170        }
171        if state.eat(Colon) {
172            state.expect(Identifier).ok();
173        }
174        self.parse_function_body(state)?;
175        state.finish_at(cp, crate::parser::element_type::LuaElementType::FunctionDeclaration);
176        Ok(())
177    }
178
179    fn parse_function_body<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
180        use crate::lexer::LuaTokenType::*;
181        state.expect(LeftParen).ok();
182        if !state.at(RightParen) {
183            if state.eat(DotDotDot) {
184                // vararg
185            }
186            else {
187                state.expect(Identifier).ok();
188                while state.eat(Comma) {
189                    if state.eat(DotDotDot) {
190                        break;
191                    }
192                    state.expect(Identifier).ok();
193                }
194            }
195        }
196        state.expect(RightParen).ok();
197        self.parse_block(state)?;
198        state.expect(End).ok();
199        Ok(())
200    }
201
202    fn parse_return_statement<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
203        use crate::lexer::LuaTokenType::*;
204        let cp = state.checkpoint();
205        state.expect(Return).ok();
206        if !state.at(End) && !state.at(Else) && !state.at(Elseif) && !state.at(Until) && !state.at(Semicolon) {
207            PrattParser::parse(state, 0, self);
208            while state.eat(Comma) {
209                PrattParser::parse(state, 0, self);
210            }
211        }
212        state.eat(Semicolon);
213        state.finish_at(cp, crate::parser::element_type::LuaElementType::ReturnStatement);
214        Ok(())
215    }
216
217    fn parse_break_statement<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
218        use crate::lexer::LuaTokenType::*;
219        let cp = state.checkpoint();
220        state.expect(Break).ok();
221        state.finish_at(cp, crate::parser::element_type::LuaElementType::BreakStatement);
222        Ok(())
223    }
224
225    fn parse_do_statement<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
226        use crate::lexer::LuaTokenType::*;
227        let cp = state.checkpoint();
228        state.expect(Do).ok();
229        self.parse_block(state)?;
230        state.expect(End).ok();
231        state.finish_at(cp, crate::parser::element_type::LuaElementType::DoStatement);
232        Ok(())
233    }
234
235    fn parse_goto_statement<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
236        use crate::lexer::LuaTokenType::*;
237        let cp = state.checkpoint();
238        state.expect(Goto).ok();
239        state.expect(Identifier).ok();
240        state.finish_at(cp, crate::parser::element_type::LuaElementType::GotoStatement);
241        Ok(())
242    }
243
244    fn parse_label_statement<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
245        use crate::lexer::LuaTokenType::*;
246        let cp = state.checkpoint();
247        state.expect(ColonColon).ok();
248        state.expect(Identifier).ok();
249        state.expect(ColonColon).ok();
250        state.finish_at(cp, crate::parser::element_type::LuaElementType::LabelStatement);
251        Ok(())
252    }
253
254    fn parse_block<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
255        use crate::lexer::LuaTokenType::*;
256        while state.not_at_end() && !state.at(End) && !state.at(Else) && !state.at(Elseif) && !state.at(Until) {
257            self.parse_statement(state)?;
258        }
259        Ok(())
260    }
261
262    fn parse_table_constructor<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> &'a GreenNode<'a, LuaLanguage> {
263        use crate::lexer::LuaTokenType::*;
264        let cp = state.checkpoint();
265        state.expect(LeftBrace).ok();
266        while state.not_at_end() && !state.at(RightBrace) {
267            let field_cp = state.checkpoint();
268            if state.eat(LeftBracket) {
269                PrattParser::parse(state, 0, self);
270                state.expect(RightBracket).ok();
271                state.expect(Eq).ok();
272                PrattParser::parse(state, 0, self);
273            }
274            else if state.at(Identifier) && state.peek_non_trivia_kind_at(1) == Some(Eq) {
275                state.bump();
276                state.expect(Eq).ok();
277                PrattParser::parse(state, 0, self);
278            }
279            else {
280                PrattParser::parse(state, 0, self);
281            }
282            state.finish_at(field_cp, crate::parser::element_type::LuaElementType::TableField);
283            if !state.eat(Comma) && !state.eat(Semicolon) {
284                break;
285            }
286        }
287        state.expect(RightBrace).ok();
288        state.finish_at(cp, crate::parser::element_type::LuaElementType::TableConstructorExpression)
289    }
290}
291
292impl<'config> Pratt<LuaLanguage> for LuaParser<'config> {
293    fn primary<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> &'a GreenNode<'a, LuaLanguage> {
294        use crate::lexer::LuaTokenType::*;
295        let cp = state.checkpoint();
296        match state.peek_kind() {
297            Some(Nil) | Some(False) | Some(True) | Some(Number) | Some(String) | Some(DotDotDot) => {
298                state.bump();
299                state.finish_at(cp, crate::parser::element_type::LuaElementType::LiteralExpression)
300            }
301            Some(Identifier) => {
302                state.bump();
303                state.finish_at(cp, crate::parser::element_type::LuaElementType::IdentifierExpression)
304            }
305            Some(Function) => {
306                state.bump();
307                self.parse_function_body(state).ok();
308                state.finish_at(cp, crate::parser::element_type::LuaElementType::FunctionExpression)
309            }
310            Some(LeftParen) => {
311                state.bump();
312                PrattParser::parse(state, 0, self);
313                state.expect(RightParen).ok();
314                state.finish_at(cp, crate::parser::element_type::LuaElementType::ParenthesizedExpression)
315            }
316            Some(LeftBrace) => self.parse_table_constructor(state),
317            _ => {
318                use crate::lexer::LuaTokenType;
319                let current_kind = state.peek_kind();
320                state.record_expected("expression");
321                if state.not_at_end() {
322                    // Don't bump if we are at a token that could be part of the following structure
323                    match current_kind {
324                        Some(LuaTokenType::RightParen)
325                        | Some(LuaTokenType::RightBrace)
326                        | Some(LuaTokenType::RightBracket)
327                        | Some(LuaTokenType::Comma)
328                        | Some(LuaTokenType::Semicolon)
329                        | Some(LuaTokenType::End)
330                        | Some(LuaTokenType::Else)
331                        | Some(LuaTokenType::Elseif)
332                        | Some(LuaTokenType::Until) => {
333                            // Don't bump, let the caller handle it
334                        }
335                        _ => {
336                            state.bump();
337                        }
338                    }
339                }
340                state.finish_at(cp, crate::parser::element_type::LuaElementType::Error)
341            }
342        }
343    }
344
345    fn prefix<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> &'a GreenNode<'a, LuaLanguage> {
346        use crate::{lexer::LuaTokenType as TT, parser::element_type::LuaElementType as ET};
347        let kind = state.peek_kind();
348        match kind {
349            Some(TT::Minus) | Some(TT::Not) | Some(TT::Hash) | Some(TT::Tilde) => {
350                let op_kind = kind.unwrap();
351                unary(state, op_kind, 11, ET::UnaryExpression, |s, p| PrattParser::parse(s, p, self))
352            }
353            _ => self.primary(state),
354        }
355    }
356
357    fn infix<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>, left: &'a GreenNode<'a, LuaLanguage>, min_precedence: u8) -> Option<&'a GreenNode<'a, LuaLanguage>> {
358        use crate::{lexer::LuaTokenType as TT, parser::element_type::LuaElementType as ET};
359        let kind = state.peek_kind()?;
360
361        let (prec, assoc) = match kind {
362            TT::Or => (1, Associativity::Left),
363            TT::And => (2, Associativity::Left),
364            TT::Lt | TT::Gt | TT::LtEq | TT::GtEq | TT::TildeEq | TT::EqEq => (3, Associativity::Left),
365            TT::Pipe => (4, Associativity::Left),
366            TT::Tilde => (5, Associativity::Left),
367            TT::Ampersand => (6, Associativity::Left),
368            TT::LtLt | TT::GtGt => (7, Associativity::Left),
369            TT::DotDot => (8, Associativity::Right),
370            TT::Plus | TT::Minus => (9, Associativity::Left),
371            TT::Star | TT::Slash | TT::SlashSlash | TT::Percent => (10, Associativity::Left),
372            TT::Caret => (12, Associativity::Right),
373            TT::Dot | TT::Colon | TT::LeftParen | TT::LeftBrace | TT::String | TT::LeftBracket => (13, Associativity::Left),
374            _ => return None,
375        };
376
377        if prec < min_precedence {
378            return None;
379        }
380
381        match kind {
382            TT::Dot => {
383                let cp = state.checkpoint_before(left);
384                state.bump();
385                state.expect(TT::Identifier).ok();
386                Some(state.finish_at(cp, ET::MemberExpression))
387            }
388            TT::Colon => {
389                let cp = state.checkpoint_before(left);
390                state.bump();
391                state.expect(TT::Identifier).ok();
392                Some(state.finish_at(cp, ET::MemberExpression))
393            }
394            TT::LeftBracket => {
395                let cp = state.checkpoint_before(left);
396                state.bump();
397                PrattParser::parse(state, 0, self);
398                state.expect(TT::RightBracket).ok();
399                Some(state.finish_at(cp, ET::IndexExpression))
400            }
401            TT::LeftParen | TT::LeftBrace | TT::String => {
402                let cp = state.checkpoint_before(left);
403                if state.eat(TT::LeftParen) {
404                    if !state.at(TT::RightParen) {
405                        PrattParser::parse(state, 0, self);
406                        while state.eat(TT::Comma) {
407                            PrattParser::parse(state, 0, self);
408                        }
409                    }
410                    state.expect(TT::RightParen).ok();
411                }
412                else if state.at(TT::LeftBrace) {
413                    self.parse_table_constructor(state);
414                }
415                else if state.at(TT::String) {
416                    state.bump();
417                }
418                else {
419                    state.record_expected("arguments");
420                }
421                Some(state.finish_at(cp, ET::CallExpression))
422            }
423            _ => Some(binary(state, left, kind, prec, assoc, ET::BinaryExpression, |s, p| PrattParser::parse(s, p, self))),
424        }
425    }
426}
427
428impl<'config> Parser<LuaLanguage> for LuaParser<'config> {
429    fn parse<'a, S: Source + ?Sized>(&self, text: &'a S, edits: &[TextEdit], cache: &'a mut impl ParseCache<LuaLanguage>) -> ParseOutput<'a, LuaLanguage> {
430        let lexer = LuaLexer::new(self.config);
431        parse_with_lexer(&lexer, text, edits, cache, |state| {
432            use crate::lexer::LuaTokenType::*;
433            let checkpoint = state.checkpoint();
434
435            while state.not_at_end() {
436                self.parse_statement(state).ok();
437            }
438
439            Ok(state.finish_at(checkpoint, crate::parser::element_type::LuaElementType::SourceFile))
440        })
441    }
442}