php_parser_rs/parser/
mod.rs

1use crate::expect_literal;
2use crate::lexer::token::OpenTagKind;
3use crate::lexer::token::Token;
4use crate::lexer::token::TokenKind;
5use crate::lexer::Lexer;
6use crate::parser::ast::declares::DeclareBody;
7use crate::parser::ast::declares::DeclareEntry;
8use crate::parser::ast::declares::DeclareEntryGroup;
9use crate::parser::ast::declares::DeclareStatement;
10use crate::parser::ast::variables::Variable;
11use crate::parser::ast::{Program, Statement, StaticVar};
12use crate::parser::error::ParseErrorStack;
13use crate::parser::error::ParseResult;
14use crate::parser::internal::attributes;
15use crate::parser::internal::blocks;
16use crate::parser::internal::classes;
17use crate::parser::internal::constants;
18use crate::parser::internal::control_flow;
19use crate::parser::internal::enums;
20use crate::parser::internal::functions;
21use crate::parser::internal::goto;
22use crate::parser::internal::identifiers;
23use crate::parser::internal::interfaces;
24use crate::parser::internal::loops;
25use crate::parser::internal::namespaces;
26use crate::parser::internal::traits;
27use crate::parser::internal::try_block;
28use crate::parser::internal::uses;
29use crate::parser::internal::utils;
30use crate::parser::internal::variables;
31use crate::parser::state::State;
32
33pub use crate::lexer::stream::TokenStream;
34
35use self::ast::ClosingTagStatement;
36use self::ast::EchoOpeningTagStatement;
37use self::ast::EchoStatement;
38use self::ast::ExpressionStatement;
39use self::ast::FullOpeningTagStatement;
40use self::ast::GlobalStatement;
41use self::ast::HaltCompilerStatement;
42use self::ast::InlineHtmlStatement;
43use self::ast::ReturnStatement;
44use self::ast::ShortOpeningTagStatement;
45use self::ast::StaticStatement;
46use self::internal::precedences::Precedence;
47
48pub mod ast;
49pub mod error;
50
51mod expressions;
52mod internal;
53mod macros;
54mod state;
55
56pub fn parse<B: ?Sized + AsRef<[u8]>>(input: &B) -> Result<Program, ParseErrorStack> {
57    let lexer = Lexer::new();
58    let tokens = match lexer.tokenize(input) {
59        Ok(tokens) => tokens,
60        Err(error) => {
61            return Err(ParseErrorStack {
62                errors: vec![error.into()],
63                partial: Vec::new(),
64            })
65        }
66    };
67
68    construct(&tokens)
69}
70
71pub fn construct(tokens: &[Token]) -> Result<Program, ParseErrorStack> {
72    let mut stream = TokenStream::new(tokens);
73    let mut state = State::new(&mut stream);
74
75    let mut program = Program::new();
76
77    while !state.stream.is_eof() {
78        let statement = match top_level_statement(&mut state) {
79            Ok(statement) => statement,
80            Err(error) => {
81                let mut previous = state.errors;
82                previous.push(error);
83
84                return Err(ParseErrorStack {
85                    errors: previous,
86                    partial: program,
87                });
88            }
89        };
90
91        program.push(statement);
92    }
93
94    let errors = state.errors;
95    if !errors.is_empty() {
96        return Err(ParseErrorStack {
97            errors,
98            partial: program,
99        });
100    }
101
102    Ok(program.to_vec())
103}
104
105fn top_level_statement(state: &mut State) -> ParseResult<Statement> {
106    let statement = match &state.stream.current().kind {
107        TokenKind::Namespace => namespaces::namespace(state)?,
108        TokenKind::Use => uses::use_statement(state)?,
109        TokenKind::Const => Statement::Constant(constants::parse(state)?),
110        TokenKind::HaltCompiler => {
111            state.stream.next();
112
113            let content = if let TokenKind::InlineHtml = state.stream.current().kind.clone() {
114                let content = state.stream.current().value.clone();
115                state.stream.next();
116                Some(content)
117            } else {
118                None
119            };
120
121            Statement::HaltCompiler(HaltCompilerStatement { content })
122        }
123        _ => statement(state)?,
124    };
125
126    Ok(statement)
127}
128
129fn statement(state: &mut State) -> ParseResult<Statement> {
130    let has_attributes = attributes::gather_attributes(state)?;
131
132    let current = state.stream.current();
133    let peek = state.stream.peek();
134    let statement = if has_attributes {
135        match &current.kind {
136            TokenKind::Abstract => classes::parse(state)?,
137            TokenKind::Readonly if peek.kind != TokenKind::LeftParen => classes::parse(state)?,
138            TokenKind::Final => classes::parse(state)?,
139            TokenKind::Class => classes::parse(state)?,
140            TokenKind::Interface => interfaces::parse(state)?,
141            TokenKind::Trait => traits::parse(state)?,
142            TokenKind::Enum
143                if !matches!(
144                    peek.kind,
145                    TokenKind::LeftParen | TokenKind::DoubleColon | TokenKind::Colon,
146                ) =>
147            {
148                enums::parse(state)?
149            }
150            TokenKind::Function
151                if identifiers::is_identifier_maybe_soft_reserved(&peek.kind)
152                    || peek.kind == TokenKind::Ampersand =>
153            {
154                if peek.kind == TokenKind::Ampersand {
155                    if !identifiers::is_identifier_maybe_soft_reserved(
156                        &state.stream.lookahead(1).kind,
157                    ) {
158                        return Ok(Statement::Expression(ExpressionStatement {
159                            expression: expressions::attributes(state, &Precedence::Lowest)?,
160                            ending: utils::skip_ending(state)?,
161                        }));
162                    }
163
164                    functions::function(state)?
165                } else {
166                    functions::function(state)?
167                }
168            }
169            _ => Statement::Expression(ExpressionStatement {
170                expression: expressions::attributes(state, &Precedence::Lowest)?,
171                ending: utils::skip_ending(state)?,
172            }),
173        }
174    } else {
175        match &current.kind {
176            TokenKind::OpenTag(OpenTagKind::Echo) => {
177                let span = current.span;
178                state.stream.next();
179
180                Statement::EchoOpeningTag(EchoOpeningTagStatement { span })
181            }
182            TokenKind::OpenTag(OpenTagKind::Full) => {
183                let span = current.span;
184                state.stream.next();
185
186                Statement::FullOpeningTag(FullOpeningTagStatement { span })
187            }
188            TokenKind::OpenTag(OpenTagKind::Short) => {
189                let span = current.span;
190                state.stream.next();
191
192                Statement::ShortOpeningTag(ShortOpeningTagStatement { span })
193            }
194            TokenKind::CloseTag => {
195                let span = current.span;
196                state.stream.next();
197
198                Statement::ClosingTag(ClosingTagStatement { span })
199            }
200            TokenKind::Abstract => classes::parse(state)?,
201            TokenKind::Readonly if peek.kind != TokenKind::LeftParen => classes::parse(state)?,
202            TokenKind::Final => classes::parse(state)?,
203            TokenKind::Class => classes::parse(state)?,
204            TokenKind::Interface => interfaces::parse(state)?,
205            TokenKind::Trait => traits::parse(state)?,
206            TokenKind::Enum
207                if !matches!(
208                    peek.kind,
209                    TokenKind::LeftParen | TokenKind::DoubleColon | TokenKind::Colon,
210                ) =>
211            {
212                enums::parse(state)?
213            }
214            TokenKind::Function
215                if identifiers::is_identifier_maybe_soft_reserved(&peek.kind)
216                    || peek.kind == TokenKind::Ampersand =>
217            {
218                if peek.kind == TokenKind::Ampersand {
219                    if !identifiers::is_identifier_maybe_soft_reserved(
220                        &state.stream.lookahead(1).kind,
221                    ) {
222                        return Ok(Statement::Expression(ExpressionStatement {
223                            expression: expressions::attributes(state, &Precedence::Lowest)?,
224                            ending: utils::skip_ending(state)?,
225                        }));
226                    }
227
228                    functions::function(state)?
229                } else {
230                    functions::function(state)?
231                }
232            }
233            TokenKind::Goto => goto::goto_statement(state)?,
234            token
235                if identifiers::is_identifier_maybe_reserved(token)
236                    && peek.kind == TokenKind::Colon =>
237            {
238                goto::label_statement(state)?
239            }
240            TokenKind::Declare => {
241                let span = utils::skip(state, TokenKind::Declare)?;
242
243                let entries = {
244                    let start = utils::skip_left_parenthesis(state)?;
245                    let mut entries = Vec::new();
246                    loop {
247                        let key = identifiers::identifier(state)?;
248                        let span = utils::skip(state, TokenKind::Equals)?;
249                        let value = expect_literal!(state);
250
251                        entries.push(DeclareEntry {
252                            key,
253                            equals: span,
254                            value,
255                        });
256
257                        if state.stream.current().kind == TokenKind::Comma {
258                            state.stream.next();
259                        } else {
260                            break;
261                        }
262                    }
263                    let end = utils::skip_right_parenthesis(state)?;
264
265                    DeclareEntryGroup {
266                        left_parenthesis: start,
267                        entries,
268                        right_parenthesis: end,
269                    }
270                };
271
272                let body = match state.stream.current().kind.clone() {
273                    TokenKind::SemiColon => {
274                        let span = utils::skip_semicolon(state)?;
275
276                        DeclareBody::Noop { semicolon: span }
277                    }
278                    TokenKind::LeftBrace => {
279                        let start = utils::skip_left_brace(state)?;
280                        let statements =
281                            blocks::multiple_statements_until(state, &TokenKind::RightBrace)?;
282                        let end = utils::skip_right_brace(state)?;
283
284                        DeclareBody::Braced {
285                            left_brace: start,
286                            statements,
287                            right_brace: end,
288                        }
289                    }
290                    TokenKind::Colon => {
291                        let start = utils::skip_colon(state)?;
292                        let statements =
293                            blocks::multiple_statements_until(state, &TokenKind::EndDeclare)?;
294                        let end = (
295                            utils::skip(state, TokenKind::EndDeclare)?,
296                            utils::skip_semicolon(state)?,
297                        );
298
299                        DeclareBody::Block {
300                            colon: start,
301                            statements,
302                            end,
303                        }
304                    }
305                    _ => {
306                        let expression = expressions::create(state)?;
307                        let end = utils::skip_semicolon(state)?;
308
309                        DeclareBody::Expression {
310                            expression,
311                            semicolon: end,
312                        }
313                    }
314                };
315
316                Statement::Declare(DeclareStatement {
317                    declare: span,
318                    entries,
319                    body,
320                })
321            }
322            TokenKind::Global => {
323                let span = current.span;
324                state.stream.next();
325
326                let mut variables = vec![];
327                // `loop` instead of `while` as we don't allow for extra commas.
328                loop {
329                    variables.push(variables::dynamic_variable(state)?);
330
331                    if state.stream.current().kind == TokenKind::Comma {
332                        state.stream.next();
333                    } else {
334                        break;
335                    }
336                }
337
338                utils::skip_semicolon(state)?;
339                Statement::Global(GlobalStatement {
340                    global: span,
341                    variables,
342                })
343            }
344            TokenKind::Static if matches!(peek.kind, TokenKind::Variable) => {
345                state.stream.next();
346
347                let mut vars = vec![];
348
349                // `loop` instead of `while` as we don't allow for extra commas.
350                loop {
351                    let var = variables::simple_variable(state)?;
352                    let mut default = None;
353
354                    if state.stream.current().kind == TokenKind::Equals {
355                        state.stream.next();
356
357                        default = Some(expressions::create(state)?);
358                    }
359
360                    vars.push(StaticVar {
361                        var: Variable::SimpleVariable(var),
362                        default,
363                    });
364
365                    if state.stream.current().kind == TokenKind::Comma {
366                        state.stream.next();
367                    } else {
368                        break;
369                    }
370                }
371
372                utils::skip_semicolon(state)?;
373
374                Statement::Static(StaticStatement { vars })
375            }
376            TokenKind::InlineHtml => {
377                let html = state.stream.current().value.clone();
378                state.stream.next();
379
380                Statement::InlineHtml(InlineHtmlStatement { html })
381            }
382            TokenKind::Do => loops::do_while_statement(state)?,
383            TokenKind::While => loops::while_statement(state)?,
384            TokenKind::For => loops::for_statement(state)?,
385            TokenKind::Foreach => loops::foreach_statement(state)?,
386            TokenKind::Continue => loops::continue_statement(state)?,
387            TokenKind::Break => loops::break_statement(state)?,
388            TokenKind::Switch => control_flow::switch_statement(state)?,
389            TokenKind::If => control_flow::if_statement(state)?,
390            TokenKind::Try => try_block::try_block(state)?,
391            TokenKind::LeftBrace => blocks::block_statement(state)?,
392            TokenKind::SemiColon => {
393                let start = current.span;
394
395                state.stream.next();
396
397                Statement::Noop(start)
398            }
399            TokenKind::Echo => {
400                state.stream.next();
401
402                let mut values = Vec::new();
403                loop {
404                    values.push(expressions::create(state)?);
405
406                    if state.stream.current().kind == TokenKind::Comma {
407                        state.stream.next();
408                    } else {
409                        break;
410                    }
411                }
412
413                Statement::Echo(EchoStatement {
414                    echo: current.span,
415                    values,
416                    ending: utils::skip_ending(state)?,
417                })
418            }
419            TokenKind::Return => {
420                state.stream.next();
421
422                let value = if matches!(
423                    state.stream.current().kind,
424                    TokenKind::SemiColon | TokenKind::CloseTag
425                ) {
426                    None
427                } else {
428                    expressions::create(state).map(Some)?
429                };
430
431                Statement::Return(ReturnStatement {
432                    r#return: current.span,
433                    value,
434                    ending: utils::skip_ending(state)?,
435                })
436            }
437            _ => Statement::Expression(ExpressionStatement {
438                expression: expressions::create(state)?,
439                ending: utils::skip_ending(state)?,
440            }),
441        }
442    };
443
444    Ok(statement)
445}