oak_php/parser/
mod.rs

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