Skip to main content

oak_php/parser/
mod.rs

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