Skip to main content

oak_actionscript/parser/
mod.rs

1pub mod element_type;
2
3pub use element_type::ActionScriptElementType;
4
5use crate::{language::ActionScriptLanguage, lexer::ActionScriptLexer};
6use oak_core::{
7    GreenNode,
8    parser::{Associativity, ParseCache, ParseOutput, Parser, ParserState, Pratt, PrattParser, binary, parse_with_lexer, unary},
9    source::{Source, TextEdit},
10};
11
12mod parse_top_level;
13
14pub(crate) type State<'a, S> = ParserState<'a, ActionScriptLanguage, S>;
15
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    pub fn new(config: &'config ActionScriptLanguage) -> Self {
131        Self { config }
132    }
133}
134
135impl<'config> Parser<ActionScriptLanguage> for ActionScriptParser<'config> {
136    fn parse<'a, S: Source + ?Sized>(&self, text: &'a S, edits: &[TextEdit], cache: &'a mut impl ParseCache<ActionScriptLanguage>) -> ParseOutput<'a, ActionScriptLanguage> {
137        let lexer = ActionScriptLexer::new(&self.config);
138        parse_with_lexer(&lexer, text, edits, cache, |state| self.parse_source_file(state))
139    }
140}