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(); 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(); 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(); 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(); 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(); 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(); 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 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}