Skip to main content

oak_php/parser/
mod.rs

1pub mod element_type;
2
3use crate::{
4    language::PhpLanguage,
5    lexer::{PhpLexer, PhpTokenType},
6    parser::element_type::PhpElementType,
7};
8use oak_core::{
9    GreenNode, OakError,
10    parser::{
11        ParseCache, ParseOutput, Parser, ParserState, parse_with_lexer,
12        pratt::{Associativity, Pratt, PrattParser, binary},
13    },
14    source::{Source, TextEdit},
15};
16
17pub(crate) type State<'a, S> = ParserState<'a, PhpLanguage, S>;
18
19/// Parser for the PHP language.
20///
21/// This parser transforms a stream of tokens into a green tree of [`PhpTokenType`] nodes.
22pub struct PhpParser<'config> {
23    pub(crate) config: &'config PhpLanguage,
24}
25
26impl<'config> PhpParser<'config> {
27    /// Creates a new `PhpParser` with the given language configuration.
28    pub fn new(config: &'config PhpLanguage) -> Self {
29        Self { config }
30    }
31}
32
33impl<'config> Pratt<PhpLanguage> for PhpParser<'config> {
34    fn primary<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> &'a GreenNode<'a, PhpLanguage> {
35        use crate::lexer::token_type::PhpTokenType::*;
36        let cp = state.checkpoint();
37        match state.peek_kind() {
38            Some(Identifier) | Some(Variable) => {
39                state.bump();
40                state.finish_at(cp, PhpElementType::Identifier)
41            }
42            Some(NumberLiteral) | Some(StringLiteral) | Some(BooleanLiteral) | Some(NullLiteral) => {
43                state.bump();
44                state.finish_at(cp, PhpElementType::Literal)
45            }
46            Some(LeftParen) => {
47                state.bump();
48                PrattParser::parse(state, 0, self);
49                state.expect(RightParen).ok();
50                state.finish_at(cp, PhpElementType::ParenthesizedExpression)
51            }
52            _ => {
53                state.bump();
54                state.finish_at(cp, PhpElementType::Error)
55            }
56        }
57    }
58
59    fn prefix<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> &'a GreenNode<'a, PhpLanguage> {
60        self.primary(state)
61    }
62
63    fn infix<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>, left: &'a GreenNode<'a, PhpLanguage>, min_precedence: u8) -> Option<&'a GreenNode<'a, PhpLanguage>> {
64        use crate::lexer::PhpTokenType::*;
65        let kind = state.peek_kind()?;
66
67        let (prec, assoc) = match kind {
68            Assign | PlusAssign | MinusAssign | MultiplyAssign | DivideAssign | ModuloAssign | PowerAssign | ConcatAssign | BitwiseAndAssign | BitwiseOrAssign | BitwiseXorAssign | LeftShiftAssign | RightShiftAssign | NullCoalesceAssign => {
69                (1, Associativity::Right)
70            }
71            LogicalOr => (2, Associativity::Left),
72            LogicalAnd => (3, Associativity::Left),
73            Equal | Identical | NotEqual | NotIdentical | Less | Greater | LessEqual | GreaterEqual | Spaceship => (4, Associativity::Left),
74            Plus | Minus | Concat => (10, Associativity::Left),
75            Multiply | Divide | Modulo => (11, Associativity::Left),
76            LeftParen | LeftBracket | Arrow => (15, Associativity::Left),
77            _ => return None,
78        };
79
80        if prec < min_precedence {
81            return None;
82        }
83
84        match kind {
85            LeftParen => {
86                let cp = state.checkpoint();
87                state.push_child(left);
88                state.expect(LeftParen).ok();
89                while state.not_at_end() && !state.at(RightParen) {
90                    state.advance();
91                }
92                state.expect(RightParen).ok();
93                Some(state.finish_at(cp, PhpElementType::CallExpression))
94            }
95            LeftBracket => {
96                let cp = state.checkpoint();
97                state.push_child(left);
98                state.expect(LeftBracket).ok();
99                while state.not_at_end() && !state.at(RightBracket) {
100                    state.advance();
101                }
102                state.expect(RightBracket).ok();
103                Some(state.finish_at(cp, PhpElementType::ArrayAccessExpression))
104            }
105            Arrow => {
106                let cp = state.checkpoint();
107                state.push_child(left);
108                state.expect(Arrow).ok();
109                state.expect(Identifier).ok();
110                Some(state.finish_at(cp, PhpElementType::MemberAccessExpression))
111            }
112            _ => Some(binary(state, left, kind, prec, assoc, PhpElementType::BinaryExpression, |s, p| PrattParser::parse(s, p, self))),
113        }
114    }
115}
116
117impl<'config> PhpParser<'config> {
118    fn parse_statement<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
119        use crate::lexer::PhpTokenType::*;
120        match state.peek_kind() {
121            Some(Namespace) => self.parse_namespace_def(state),
122            Some(Use) => self.parse_use_statement(state),
123            Some(Class) | Some(Interface) | Some(Trait) | Some(Abstract) | Some(Final) | Some(Public) | Some(Private) | Some(Protected) | Some(Static) => self.parse_declaration(state),
124            Some(Function) => self.parse_function_def(state),
125            Some(If) => self.parse_if_statement(state),
126            Some(While) => self.parse_while_statement(state),
127            Some(For) => self.parse_for_statement(state),
128            Some(Foreach) => self.parse_foreach_statement(state),
129            Some(Return) => self.parse_return_statement(state),
130            Some(Echo) => self.parse_echo_statement(state),
131            Some(LeftBrace) => self.parse_compound_statement(state),
132            _ => self.parse_expression_statement(state),
133        }
134    }
135
136    fn parse_namespace_def<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
137        let cp = state.checkpoint();
138        state.expect(crate::lexer::PhpTokenType::Namespace).ok();
139        state.advance_until_any(&[crate::lexer::PhpTokenType::Semicolon, crate::lexer::PhpTokenType::LeftBrace]);
140        if state.eat(crate::lexer::PhpTokenType::LeftBrace) {
141            while state.not_at_end() && !state.at(crate::lexer::PhpTokenType::RightBrace) {
142                self.parse_statement(state)?;
143            }
144            state.expect(crate::lexer::PhpTokenType::RightBrace).ok();
145        }
146        else {
147            state.eat(crate::lexer::PhpTokenType::Semicolon);
148        }
149        state.finish_at(cp, PhpElementType::NamespaceDef);
150        Ok(())
151    }
152
153    fn parse_use_statement<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
154        let cp = state.checkpoint();
155        state.expect(crate::lexer::PhpTokenType::Use).ok();
156        state.advance_until(crate::lexer::PhpTokenType::Semicolon);
157        state.eat(crate::lexer::PhpTokenType::Semicolon);
158        state.finish_at(cp, PhpElementType::UseStatement);
159        Ok(())
160    }
161
162    fn parse_function_def<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
163        let cp = state.checkpoint();
164        state.expect(crate::lexer::PhpTokenType::Function).ok();
165        state.expect(crate::lexer::PhpTokenType::Identifier).ok();
166        state.expect(crate::lexer::PhpTokenType::LeftParen).ok();
167        while state.not_at_end() && !state.at(crate::lexer::PhpTokenType::RightParen) {
168            state.advance();
169        }
170        state.expect(crate::lexer::PhpTokenType::RightParen).ok();
171        self.parse_compound_statement(state)?;
172        state.finish_at(cp, PhpElementType::FunctionDef);
173        Ok(())
174    }
175
176    fn parse_declaration<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
177        use crate::lexer::PhpTokenType::*;
178        let cp = state.checkpoint();
179        while state.not_at_end() && matches!(state.peek_kind(), Some(Public) | Some(Private) | Some(Protected) | Some(Static) | Some(Abstract) | Some(Final)) {
180            state.bump();
181        }
182
183        match state.peek_kind() {
184            Some(Class) => {
185                state.bump();
186                state.expect(Identifier).ok();
187                state.advance_until(LeftBrace);
188                self.parse_compound_statement(state)?;
189                state.finish_at(cp, PhpElementType::ClassDef);
190            }
191            Some(Interface) => {
192                state.bump();
193                state.expect(Identifier).ok();
194                self.parse_compound_statement(state)?;
195                state.finish_at(cp, PhpElementType::InterfaceDef);
196            }
197            Some(Function) => self.parse_function_def(state)?,
198            _ => {
199                state.advance_until_any(&[Semicolon, LeftBrace]);
200                if state.eat(LeftBrace) {
201                    while state.not_at_end() && !state.at(RightBrace) {
202                        self.parse_statement(state)?;
203                    }
204                    state.expect(RightBrace).ok();
205                }
206                else {
207                    state.eat(Semicolon);
208                }
209            }
210        }
211        Ok(())
212    }
213
214    fn parse_if_statement<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
215        let cp = state.checkpoint();
216        state.expect(crate::lexer::PhpTokenType::If).ok();
217        state.expect(crate::lexer::PhpTokenType::LeftParen).ok();
218        PrattParser::parse(state, 0, self);
219        state.expect(crate::lexer::PhpTokenType::RightParen).ok();
220        self.parse_statement(state)?;
221        state.finish_at(cp, PhpElementType::IfStatement);
222        Ok(())
223    }
224
225    fn parse_while_statement<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
226        let cp = state.checkpoint();
227        state.expect(crate::lexer::PhpTokenType::While).ok();
228        state.expect(crate::lexer::PhpTokenType::LeftParen).ok();
229        PrattParser::parse(state, 0, self);
230        state.expect(crate::lexer::PhpTokenType::RightParen).ok();
231        self.parse_statement(state)?;
232        state.finish_at(cp, PhpElementType::WhileStatement);
233        Ok(())
234    }
235
236    fn parse_for_statement<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
237        let cp = state.checkpoint();
238        state.expect(crate::lexer::PhpTokenType::For).ok();
239        state.expect(crate::lexer::PhpTokenType::LeftParen).ok();
240        state.expect(crate::lexer::PhpTokenType::RightParen).ok();
241        self.parse_statement(state)?;
242        state.finish_at(cp, PhpElementType::ForStatement);
243        Ok(())
244    }
245
246    fn parse_foreach_statement<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
247        let cp = state.checkpoint();
248        state.expect(crate::lexer::PhpTokenType::Foreach).ok();
249        state.expect(crate::lexer::PhpTokenType::LeftParen).ok();
250        state.expect(crate::lexer::PhpTokenType::RightParen).ok();
251        self.parse_statement(state)?;
252        state.finish_at(cp, PhpElementType::ForeachStatement);
253        Ok(())
254    }
255
256    fn parse_compound_statement<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
257        let cp = state.checkpoint();
258        state.expect(crate::lexer::PhpTokenType::LeftBrace).ok();
259        while state.not_at_end() && !state.at(crate::lexer::PhpTokenType::RightBrace) {
260            self.parse_statement(state)?;
261        }
262        state.expect(crate::lexer::PhpTokenType::RightBrace).ok();
263        state.finish_at(cp, PhpElementType::CompoundStatement);
264        Ok(())
265    }
266
267    fn parse_return_statement<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
268        let cp = state.checkpoint();
269        state.expect(crate::lexer::PhpTokenType::Return).ok();
270        PrattParser::parse(state, 0, self);
271        state.eat(crate::lexer::PhpTokenType::Semicolon);
272        state.finish_at(cp, PhpElementType::ReturnStatement);
273        Ok(())
274    }
275
276    fn parse_echo_statement<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
277        let cp = state.checkpoint();
278        state.expect(crate::lexer::PhpTokenType::Echo).ok();
279        PrattParser::parse(state, 0, self);
280        state.eat(crate::lexer::PhpTokenType::Semicolon);
281        state.finish_at(cp, PhpElementType::EchoStatement);
282        Ok(())
283    }
284
285    fn parse_expression_statement<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
286        PrattParser::parse(state, 0, self);
287        state.eat(crate::lexer::PhpTokenType::Semicolon);
288        Ok(())
289    }
290}
291
292impl<'config> Parser<PhpLanguage> for PhpParser<'config> {
293    fn parse<'a, S: Source + ?Sized>(&self, text: &'a S, edits: &[TextEdit], cache: &'a mut impl ParseCache<PhpLanguage>) -> ParseOutput<'a, PhpLanguage> {
294        let lexer = crate::lexer::PhpLexer::new(&self.config);
295        parse_with_lexer(&lexer, text, edits, cache, |state| {
296            let cp = state.checkpoint();
297            while state.not_at_end() {
298                if let Err(_) = self.parse_statement(state) {
299                    state.advance();
300                }
301            }
302            Ok(state.finish_at(cp, PhpElementType::Root))
303        })
304    }
305}