Skip to main content

oak_javascript/parser/
mod.rs

1//! JavaScript parser implementation.
2
3pub mod element_type;
4
5use crate::{language::JavaScriptLanguage, parser::element_type::JavaScriptElementType};
6use oak_core::{
7    GreenNode, OakError, ParseCache, TextEdit,
8    parser::{Associativity, Parser, ParserState, Pratt, PrattParser, binary},
9    source::Source,
10};
11
12pub(crate) type State<'a, S> = ParserState<'a, JavaScriptLanguage, S>;
13
14/// JavaScript parser.
15pub struct JavaScriptParser<'config> {
16    /// Configuration for the JavaScript language.
17    pub(crate) config: &'config JavaScriptLanguage,
18}
19
20impl<'config> JavaScriptParser<'config> {
21    /// Creates a new JavaScript parser.
22    pub fn new(config: &'config JavaScriptLanguage) -> Self {
23        Self { config }
24    }
25
26    fn parse_statement<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
27        use crate::lexer::token_type::JavaScriptTokenType::*;
28        match state.peek_kind() {
29            Some(Function) => self.parse_function_declaration(state)?,
30            Some(Var) | Some(Let) | Some(Const) => self.parse_variable_declaration(state)?,
31            Some(If) => self.parse_if_statement(state)?,
32            Some(While) => self.parse_while_statement(state)?,
33            Some(For) => self.parse_for_statement(state)?,
34            Some(Return) => self.parse_return_statement(state)?,
35            Some(LeftBrace) => self.parse_block_statement(state)?,
36            _ => {
37                PrattParser::parse(state, 0, self);
38                state.eat(Semicolon);
39            }
40        }
41        Ok(())
42    }
43
44    fn parse_function_declaration<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
45        use crate::lexer::token_type::JavaScriptTokenType::*;
46        let cp = state.checkpoint();
47        state.expect(Function).ok();
48        state.eat(IdentifierName);
49        state.expect(LeftParen).ok();
50        while state.not_at_end() && !state.at(RightParen) {
51            state.advance()
52        }
53        state.expect(RightParen).ok();
54        self.parse_block_statement(state)?;
55        state.finish_at(cp, crate::parser::element_type::JavaScriptElementType::FunctionDeclaration);
56        Ok(())
57    }
58
59    fn parse_variable_declaration<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
60        use crate::lexer::token_type::JavaScriptTokenType::*;
61        let cp = state.checkpoint();
62        state.bump(); // var/let/const
63        state.expect(IdentifierName).ok();
64        if state.eat(Equal) {
65            PrattParser::parse(state, 0, self);
66        }
67        state.eat(Semicolon);
68        state.finish_at(cp, crate::parser::element_type::JavaScriptElementType::VariableDeclaration);
69        Ok(())
70    }
71
72    fn parse_if_statement<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
73        use crate::lexer::token_type::JavaScriptTokenType::*;
74        let cp = state.checkpoint();
75        state.expect(If).ok();
76        state.expect(LeftParen).ok();
77        PrattParser::parse(state, 0, self);
78        state.expect(RightParen).ok();
79        self.parse_statement(state)?;
80        if state.eat(Else) {
81            self.parse_statement(state)?
82        }
83        state.finish_at(cp, crate::parser::element_type::JavaScriptElementType::IfStatement);
84        Ok(())
85    }
86
87    fn parse_while_statement<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
88        use crate::lexer::token_type::JavaScriptTokenType::*;
89        let cp = state.checkpoint();
90        state.expect(While).ok();
91        state.expect(LeftParen).ok();
92        PrattParser::parse(state, 0, self);
93        state.expect(RightParen).ok();
94        self.parse_statement(state)?;
95        state.finish_at(cp, crate::parser::element_type::JavaScriptElementType::WhileStatement);
96        Ok(())
97    }
98
99    fn parse_for_statement<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
100        use crate::lexer::token_type::JavaScriptTokenType::*;
101        let cp = state.checkpoint();
102        state.expect(For).ok();
103        state.expect(LeftParen).ok();
104        // Simplified handling
105        while state.not_at_end() && !state.at(RightParen) {
106            state.advance()
107        }
108        state.expect(RightParen).ok();
109        self.parse_statement(state)?;
110        state.finish_at(cp, crate::parser::element_type::JavaScriptElementType::ForStatement);
111        Ok(())
112    }
113
114    fn parse_return_statement<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
115        use crate::lexer::token_type::JavaScriptTokenType::*;
116        let cp = state.checkpoint();
117        state.expect(Return).ok();
118        if !state.at(Semicolon) {
119            PrattParser::parse(state, 0, self);
120        }
121        state.eat(Semicolon);
122        state.finish_at(cp, crate::parser::element_type::JavaScriptElementType::ReturnStatement);
123        Ok(())
124    }
125
126    fn parse_block_statement<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
127        use crate::lexer::token_type::JavaScriptTokenType::*;
128        let cp = state.checkpoint();
129        state.expect(LeftBrace).ok();
130        while state.not_at_end() && !state.at(RightBrace) {
131            self.parse_statement(state)?
132        }
133        state.expect(RightBrace).ok();
134        state.finish_at(cp, crate::parser::element_type::JavaScriptElementType::BlockStatement);
135        Ok(())
136    }
137}
138
139impl<'config> Pratt<JavaScriptLanguage> for JavaScriptParser<'config> {
140    fn primary<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> &'a GreenNode<'a, JavaScriptLanguage> {
141        use crate::lexer::token_type::JavaScriptTokenType::*;
142        let cp = state.checkpoint();
143        match state.peek_kind() {
144            Some(IdentifierName) => {
145                state.bump();
146                state.finish_at(cp, crate::parser::element_type::JavaScriptElementType::Identifier)
147            }
148            Some(NumericLiteral) | Some(StringLiteral) | Some(True) | Some(False) | Some(Null) => {
149                state.bump();
150                state.finish_at(cp, crate::parser::element_type::JavaScriptElementType::Literal)
151            }
152            Some(LeftParen) => {
153                state.bump();
154                PrattParser::parse(state, 0, self);
155                state.expect(RightParen).ok();
156                state.finish_at(cp, crate::parser::element_type::JavaScriptElementType::Expression)
157            }
158            _ => {
159                state.bump();
160                state.finish_at(cp, crate::parser::element_type::JavaScriptElementType::Error)
161            }
162        }
163    }
164
165    fn infix<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>, left: &'a GreenNode<'a, JavaScriptLanguage>, min_precedence: u8) -> Option<&'a GreenNode<'a, JavaScriptLanguage>> {
166        use crate::lexer::token_type::JavaScriptTokenType::*;
167        let kind = state.peek_kind()?;
168
169        let (prec, assoc) = match kind {
170            Equal
171            | PlusEqual
172            | MinusEqual
173            | StarEqual
174            | SlashEqual
175            | PercentEqual
176            | StarStarEqual
177            | LeftShiftEqual
178            | RightShiftEqual
179            | UnsignedRightShiftEqual
180            | AmpersandEqual
181            | PipeEqual
182            | CaretEqual
183            | AmpersandAmpersandEqual
184            | PipePipeEqual
185            | QuestionQuestionEqual => (1, Associativity::Right),
186            PipePipe => (2, Associativity::Left),
187            AmpersandAmpersand => (3, Associativity::Left),
188            EqualEqual | NotEqual | EqualEqualEqual | NotEqualEqual => (4, Associativity::Left),
189            Plus | Minus => (10, Associativity::Left),
190            Star | Slash | Percent => (11, Associativity::Left),
191            LeftParen | Dot => (15, Associativity::Left),
192            _ => return None,
193        };
194
195        if prec < min_precedence {
196            return None;
197        }
198
199        match kind {
200            LeftParen => {
201                let cp = state.checkpoint();
202                state.push_child(left);
203                state.expect(LeftParen).ok();
204                while state.not_at_end() && !state.at(RightParen) {
205                    state.advance()
206                }
207                state.expect(RightParen).ok();
208                Some(state.finish_at(cp, crate::parser::element_type::JavaScriptElementType::CallExpression))
209            }
210            Dot => {
211                let cp = state.checkpoint();
212                state.push_child(left);
213                state.expect(Dot).ok();
214                state.expect(IdentifierName).ok();
215                Some(state.finish_at(cp, crate::parser::element_type::JavaScriptElementType::MemberExpression))
216            }
217            _ => {
218                let result_kind = match kind {
219                    Equal
220                    | PlusEqual
221                    | MinusEqual
222                    | StarEqual
223                    | SlashEqual
224                    | PercentEqual
225                    | StarStarEqual
226                    | LeftShiftEqual
227                    | RightShiftEqual
228                    | UnsignedRightShiftEqual
229                    | AmpersandEqual
230                    | PipeEqual
231                    | CaretEqual
232                    | AmpersandAmpersandEqual
233                    | PipePipeEqual
234                    | QuestionQuestionEqual => JavaScriptElementType::AssignmentExpression,
235                    PipePipe | AmpersandAmpersand => JavaScriptElementType::LogicalExpression,
236                    _ => JavaScriptElementType::BinaryExpression,
237                };
238                Some(binary(state, left, kind, prec, assoc, result_kind.into(), |s, p| PrattParser::parse(s, p, self)))
239            }
240        }
241    }
242}
243
244impl<'config> Parser<JavaScriptLanguage> for JavaScriptParser<'config> {
245    fn parse<'a, S: Source + ?Sized>(&self, text: &'a S, edits: &[TextEdit], cache: &'a mut impl ParseCache<JavaScriptLanguage>) -> oak_core::parser::ParseOutput<'a, JavaScriptLanguage> {
246        let lexer = crate::lexer::JavaScriptLexer::new(&self.config);
247        oak_core::parser::parse_with_lexer(&lexer, text, edits, cache, |state| {
248            let cp = state.checkpoint();
249            while state.not_at_end() {
250                self.parse_statement(state).ok();
251            }
252            Ok(state.finish_at(cp, crate::parser::element_type::JavaScriptElementType::Root))
253        })
254    }
255}