Skip to main content

oak_php/parser/
mod.rs

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