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