Skip to main content

oak_actionscript/parser/
mod.rs

1/// Element types for the ActionScript language.
2pub mod element_type;
3
4pub use element_type::ActionScriptElementType;
5
6use crate::{language::ActionScriptLanguage, lexer::ActionScriptLexer};
7use oak_core::{
8    GreenNode, TokenType,
9    parser::{Associativity, ParseCache, ParseOutput, Parser, ParserState, Pratt, PrattParser, binary, parse_with_lexer, unary},
10    source::{Source, TextEdit},
11};
12
13pub(crate) type State<'a, S> = ParserState<'a, ActionScriptLanguage, S>;
14
15/// A parser for the ActionScript language.
16pub struct ActionScriptParser<'config> {
17    pub(crate) config: &'config ActionScriptLanguage,
18}
19
20impl<'config> Pratt<ActionScriptLanguage> for ActionScriptParser<'config> {
21    fn primary<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> &'a GreenNode<'a, ActionScriptLanguage> {
22        let cp = state.checkpoint();
23        match state.peek_kind() {
24            Some(crate::lexer::ActionScriptTokenType::Identifier) => {
25                state.bump();
26                state.finish_at(cp, crate::parser::element_type::ActionScriptElementType::IdentifierExpression)
27            }
28            Some(k) if k.is_literal() => {
29                state.bump();
30                state.finish_at(cp, crate::parser::element_type::ActionScriptElementType::LiteralExpression)
31            }
32            Some(crate::lexer::ActionScriptTokenType::LeftParen) => {
33                state.bump();
34                PrattParser::parse(state, 0, self);
35                state.expect(crate::lexer::ActionScriptTokenType::RightParen).ok();
36                state.finish_at(cp, crate::parser::element_type::ActionScriptElementType::ParenthesizedExpression)
37            }
38            _ => {
39                state.bump();
40                state.finish_at(cp, crate::parser::element_type::ActionScriptElementType::Error)
41            }
42        }
43    }
44
45    fn prefix<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> &'a GreenNode<'a, ActionScriptLanguage> {
46        use crate::{lexer::ActionScriptTokenType as TT, parser::element_type::ActionScriptElementType as ET};
47        let kind = match state.peek_kind() {
48            Some(k) => k,
49            None => return self.primary(state),
50        };
51
52        match kind {
53            TT::Minus | TT::LogicalNot | TT::BitwiseNot => unary(state, kind, 13, ET::UnaryExpression.into(), |s, p| PrattParser::parse(s, p, self)),
54            _ => self.primary(state),
55        }
56    }
57
58    fn infix<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>, left: &'a GreenNode<'a, ActionScriptLanguage>, min_precedence: u8) -> Option<&'a GreenNode<'a, ActionScriptLanguage>> {
59        use crate::{lexer::ActionScriptTokenType as TT, parser::element_type::ActionScriptElementType as ET};
60        let kind = state.peek_kind()?;
61
62        let (prec, assoc) = match kind {
63            TT::Equal
64            | TT::PlusAssign
65            | TT::MinusAssign
66            | TT::StarAssign
67            | TT::SlashAssign
68            | TT::PercentAssign
69            | TT::LeftShiftAssign
70            | TT::RightShiftAssign
71            | TT::UnsignedRightShiftAssign
72            | TT::BitwiseAndAssign
73            | TT::BitwiseOrAssign
74            | TT::BitwiseXorAssign => (1, Associativity::Right),
75            TT::LogicalOr => (3, Associativity::Left),
76            TT::LogicalAnd => (4, Associativity::Left),
77            TT::EqualEqual | TT::NotEqual | TT::EqualEqualEqual | TT::NotEqualEqual => (5, Associativity::Left),
78            TT::LessThan | TT::LessEqual | TT::GreaterThan | TT::GreaterEqual | TT::Is | TT::Instanceof => (6, Associativity::Left),
79            TT::BitwiseOr => (7, Associativity::Left),
80            TT::BitwiseXor => (8, Associativity::Left),
81            TT::BitwiseAnd => (9, Associativity::Left),
82            TT::LeftShift | TT::RightShift | TT::UnsignedRightShift => (10, Associativity::Left),
83            TT::Plus | TT::Minus => (11, Associativity::Left),
84            TT::Star | TT::Slash | TT::Percent => (12, Associativity::Left),
85            TT::LeftParen | TT::LeftBracket | TT::Dot => (14, Associativity::Left),
86            _ => return None,
87        };
88
89        if prec < min_precedence {
90            return None;
91        }
92
93        match kind {
94            TT::LeftParen => {
95                let cp = state.checkpoint();
96                state.push_child(left);
97                state.expect(TT::LeftParen).ok();
98                if !state.at(TT::RightParen) {
99                    loop {
100                        PrattParser::parse(state, 0, self);
101                        if !state.eat(TT::Comma) {
102                            break;
103                        }
104                    }
105                }
106                state.expect(TT::RightParen).ok();
107                Some(state.finish_at(cp, ET::CallExpression))
108            }
109            TT::LeftBracket => {
110                let cp = state.checkpoint();
111                state.push_child(left);
112                state.expect(TT::LeftBracket).ok();
113                PrattParser::parse(state, 0, self);
114                state.expect(TT::RightBracket).ok();
115                Some(state.finish_at(cp, ET::IndexExpression))
116            }
117            TT::Dot => {
118                let cp = state.checkpoint();
119                state.push_child(left);
120                state.expect(TT::Dot).ok();
121                state.expect(TT::Identifier).ok();
122                Some(state.finish_at(cp, ET::FieldExpression))
123            }
124            _ => Some(binary(state, left, kind, prec, assoc, ET::BinaryExpression.into(), |s, p| PrattParser::parse(s, p, self))),
125        }
126    }
127}
128
129impl<'config> ActionScriptParser<'config> {
130    /// Creates a new ActionScript parser with the given configuration.
131    pub fn new(config: &'config ActionScriptLanguage) -> Self {
132        Self { config }
133    }
134
135    pub(crate) fn parse_statement<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), oak_core::OakError> {
136        use crate::lexer::ActionScriptTokenType;
137        match state.peek_kind() {
138            Some(ActionScriptTokenType::Function) => self.parse_function(state)?,
139            Some(ActionScriptTokenType::Import) => self.parse_import_statement(state)?,
140            Some(ActionScriptTokenType::Package) => self.parse_package_declaration(state)?,
141            Some(ActionScriptTokenType::Class) => self.parse_class_declaration(state)?,
142            Some(ActionScriptTokenType::Interface) => self.parse_interface_declaration(state)?,
143            Some(ActionScriptTokenType::Var) | Some(ActionScriptTokenType::Const) => self.parse_variable_declaration(state)?,
144            Some(ActionScriptTokenType::If) => self.parse_if_statement(state)?,
145            Some(ActionScriptTokenType::While) => self.parse_while_statement(state)?,
146            Some(ActionScriptTokenType::For) => self.parse_for_statement(state)?,
147            Some(ActionScriptTokenType::Return) => self.parse_return_statement(state)?,
148            Some(ActionScriptTokenType::LeftBrace) => self.parse_block(state)?,
149            _ => {
150                PrattParser::parse(state, 0, self);
151                state.eat(ActionScriptTokenType::Semicolon);
152            }
153        }
154        Ok(())
155    }
156
157    fn parse_function<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), oak_core::OakError> {
158        use crate::lexer::ActionScriptTokenType;
159        let cp = state.checkpoint();
160        state.expect(ActionScriptTokenType::Function).ok();
161        state.expect(ActionScriptTokenType::Identifier).ok();
162        self.parse_param_list(state)?;
163        if state.eat(ActionScriptTokenType::Colon) {
164            state.expect(ActionScriptTokenType::Identifier).ok();
165        }
166        self.parse_block(state)?;
167        state.finish_at(cp, ActionScriptElementType::Function);
168        Ok(())
169    }
170
171    fn parse_param_list<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), oak_core::OakError> {
172        use crate::lexer::ActionScriptTokenType::*;
173        let cp = state.checkpoint();
174        state.expect(LeftParen).ok();
175        while state.not_at_end() && !state.at(RightParen) {
176            state.advance()
177        }
178        state.expect(RightParen).ok();
179        state.finish_at(cp, ActionScriptElementType::ParameterList);
180        Ok(())
181    }
182
183    fn parse_block<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), oak_core::OakError> {
184        use crate::lexer::ActionScriptTokenType::*;
185        let cp = state.checkpoint();
186        state.expect(LeftBrace).ok();
187        while state.not_at_end() && !state.at(RightBrace) {
188            self.parse_statement(state)?
189        }
190        state.expect(RightBrace).ok();
191        state.finish_at(cp, ActionScriptElementType::BlockExpression);
192        Ok(())
193    }
194
195    fn parse_import_statement<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), oak_core::OakError> {
196        let cp = state.checkpoint();
197        state.expect(crate::lexer::ActionScriptTokenType::Import).ok();
198        while !state.at(crate::lexer::ActionScriptTokenType::Semicolon) && state.not_at_end() {
199            state.bump()
200        }
201        state.eat(crate::lexer::ActionScriptTokenType::Semicolon);
202        state.finish_at(cp, ActionScriptElementType::Import);
203        Ok(())
204    }
205
206    fn parse_package_declaration<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), oak_core::OakError> {
207        let cp = state.checkpoint();
208        state.expect(crate::lexer::ActionScriptTokenType::Package).ok();
209        if state.at(crate::lexer::ActionScriptTokenType::Identifier) {
210            state.bump()
211        }
212        self.parse_block(state)?;
213        state.finish_at(cp, ActionScriptElementType::Package);
214        Ok(())
215    }
216
217    fn parse_class_declaration<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), oak_core::OakError> {
218        let cp = state.checkpoint();
219        state.expect(crate::lexer::ActionScriptTokenType::Class).ok();
220        state.expect(crate::lexer::ActionScriptTokenType::Identifier).ok();
221        if state.eat(crate::lexer::ActionScriptTokenType::Extends) {
222            state.expect(crate::lexer::ActionScriptTokenType::Identifier).ok();
223        }
224        self.parse_block(state)?;
225        state.finish_at(cp, ActionScriptElementType::Class);
226        Ok(())
227    }
228
229    fn parse_interface_declaration<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), oak_core::OakError> {
230        let cp = state.checkpoint();
231        state.expect(crate::lexer::ActionScriptTokenType::Interface).ok();
232        state.expect(crate::lexer::ActionScriptTokenType::Identifier).ok();
233        self.parse_block(state)?;
234        state.finish_at(cp, ActionScriptElementType::Interface);
235        Ok(())
236    }
237
238    fn parse_variable_declaration<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), oak_core::OakError> {
239        let cp = state.checkpoint();
240        if state.eat(crate::lexer::ActionScriptTokenType::Var) || state.eat(crate::lexer::ActionScriptTokenType::Const) {
241            state.expect(crate::lexer::ActionScriptTokenType::Identifier).ok();
242            if state.eat(crate::lexer::ActionScriptTokenType::Colon) {
243                state.expect(crate::lexer::ActionScriptTokenType::Identifier).ok();
244            }
245            if state.eat(crate::lexer::ActionScriptTokenType::Equal) {
246                PrattParser::parse(state, 0, self);
247            }
248            state.eat(crate::lexer::ActionScriptTokenType::Semicolon);
249        }
250        state.finish_at(cp, ActionScriptElementType::Variable);
251        Ok(())
252    }
253
254    fn parse_if_statement<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), oak_core::OakError> {
255        let cp = state.checkpoint();
256        state.expect(crate::lexer::ActionScriptTokenType::If).ok();
257        state.expect(crate::lexer::ActionScriptTokenType::LeftParen).ok();
258        PrattParser::parse(state, 0, self);
259        state.expect(crate::lexer::ActionScriptTokenType::RightParen).ok();
260        self.parse_statement(state)?;
261        if state.eat(crate::lexer::ActionScriptTokenType::Else) {
262            self.parse_statement(state)?;
263        }
264        state.finish_at(cp, ActionScriptElementType::IfStatement);
265        Ok(())
266    }
267
268    fn parse_while_statement<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), oak_core::OakError> {
269        let cp = state.checkpoint();
270        state.expect(crate::lexer::ActionScriptTokenType::While).ok();
271        state.expect(crate::lexer::ActionScriptTokenType::LeftParen).ok();
272        PrattParser::parse(state, 0, self);
273        state.expect(crate::lexer::ActionScriptTokenType::RightParen).ok();
274        self.parse_statement(state)?;
275        state.finish_at(cp, ActionScriptElementType::WhileStatement);
276        Ok(())
277    }
278
279    fn parse_for_statement<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), oak_core::OakError> {
280        let cp = state.checkpoint();
281        state.expect(crate::lexer::ActionScriptTokenType::For).ok();
282        state.expect(crate::lexer::ActionScriptTokenType::LeftParen).ok();
283        while state.not_at_end() && !state.at(crate::lexer::ActionScriptTokenType::RightParen) {
284            state.advance();
285        }
286        state.expect(crate::lexer::ActionScriptTokenType::RightParen).ok();
287        self.parse_statement(state)?;
288        state.finish_at(cp, ActionScriptElementType::ForStatement);
289        Ok(())
290    }
291
292    fn parse_return_statement<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), oak_core::OakError> {
293        let cp = state.checkpoint();
294        state.expect(crate::lexer::ActionScriptTokenType::Return).ok();
295        if !state.at(crate::lexer::ActionScriptTokenType::Semicolon) {
296            PrattParser::parse(state, 0, self);
297        }
298        state.eat(crate::lexer::ActionScriptTokenType::Semicolon);
299        state.finish_at(cp, ActionScriptElementType::ReturnStatement);
300        Ok(())
301    }
302}
303
304impl<'config> Parser<ActionScriptLanguage> for ActionScriptParser<'config> {
305    fn parse<'a, S: Source + ?Sized>(&self, text: &'a S, edits: &[TextEdit], cache: &'a mut impl ParseCache<ActionScriptLanguage>) -> ParseOutput<'a, ActionScriptLanguage> {
306        let lexer = ActionScriptLexer::new(&self.config);
307        parse_with_lexer(&lexer, text, edits, cache, |state| {
308            let cp = state.checkpoint();
309            while state.not_at_end() {
310                if state.current().map(|t| t.kind.is_ignored()).unwrap_or(false) {
311                    state.advance();
312                    continue;
313                }
314                self.parse_statement(state)?
315            }
316            Ok(state.finish_at(cp, ActionScriptElementType::SourceFile))
317        })
318    }
319}