1pub 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
16pub struct PhpParser<'config> {
20 pub(crate) config: &'config PhpLanguage,
21}
22
23impl<'config> PhpParser<'config> {
24 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}