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