mago-syntax 1.27.1

A correct, fast, and memory-efficient PHP syntax implementation, including Lexer, Parser, AST, and utilities for Mago.
Documentation
use crate::T;
use crate::ast::Expression;
use crate::ast::FunctionPartialApplication;
use crate::ast::Identifier;
use crate::ast::LocalIdentifier;
use crate::ast::PartialApplication;
use crate::ast::ast::Construct;
use crate::ast::ast::DieConstruct;
use crate::ast::ast::EmptyConstruct;
use crate::ast::ast::EvalConstruct;
use crate::ast::ast::ExitConstruct;
use crate::ast::ast::IncludeConstruct;
use crate::ast::ast::IncludeOnceConstruct;
use crate::ast::ast::IssetConstruct;
use crate::ast::ast::PrintConstruct;
use crate::ast::ast::RequireConstruct;
use crate::ast::ast::RequireOnceConstruct;
use crate::error::ParseError;
use crate::parser::Parser;
use crate::token::Precedence;

impl<'arena> Parser<'_, 'arena> {
    pub(crate) fn parse_construct(&mut self) -> Result<Expression<'arena>, ParseError> {
        let token = self.stream.lookahead(0)?.ok_or_else(|| self.stream.unexpected(None, &[]))?;

        Ok(Expression::Construct(match token.kind {
            T!["isset"] => {
                let isset = self.expect_keyword(T!["isset"])?;
                let result = self.parse_comma_separated_sequence(T!["("], T![")"], |p| p.parse_expression())?;

                Construct::Isset(IssetConstruct {
                    isset,
                    left_parenthesis: result.open,
                    values: result.sequence,
                    right_parenthesis: result.close,
                })
            }
            T!["empty"] => Construct::Empty(EmptyConstruct {
                empty: self.expect_keyword(T!["empty"])?,
                left_parenthesis: self.stream.eat_span(T!["("])?,
                value: self.arena.alloc(self.parse_expression()?),
                right_parenthesis: self.stream.eat_span(T![")"])?,
            }),
            T!["eval"] => Construct::Eval(EvalConstruct {
                eval: self.expect_keyword(T!["eval"])?,
                left_parenthesis: self.stream.eat_span(T!["("])?,
                value: self.arena.alloc(self.parse_expression()?),
                right_parenthesis: self.stream.eat_span(T![")"])?,
            }),
            T!["print"] => Construct::Print(PrintConstruct {
                print: self.expect_keyword(T!["print"])?,
                value: self.arena.alloc(self.parse_expression_with_precedence(Precedence::Print)?),
            }),
            T!["require"] => Construct::Require(RequireConstruct {
                require: self.expect_any_keyword()?,
                value: self.arena.alloc(self.parse_expression()?),
            }),
            T!["require_once"] => Construct::RequireOnce(RequireOnceConstruct {
                require_once: self.expect_any_keyword()?,
                value: self.arena.alloc(self.parse_expression()?),
            }),
            T!["include"] => Construct::Include(IncludeConstruct {
                include: self.expect_any_keyword()?,
                value: self.arena.alloc(self.parse_expression()?),
            }),
            T!["include_once"] => Construct::IncludeOnce(IncludeOnceConstruct {
                include_once: self.expect_any_keyword()?,
                value: self.arena.alloc(self.parse_expression()?),
            }),
            T!["exit"] => {
                let exit = self.expect_any_keyword()?;
                if let Some(next) = self.stream.lookahead(0)?
                    && matches!(next.kind, T!["("])
                {
                    let partial_args = self.parse_partial_argument_list()?;

                    if partial_args.has_placeholders() {
                        return Ok(Expression::PartialApplication(PartialApplication::Function(
                            FunctionPartialApplication {
                                function: self.arena.alloc(Expression::Identifier(Identifier::Local(
                                    LocalIdentifier { span: exit.span, value: exit.value },
                                ))),
                                argument_list: partial_args,
                            },
                        )));
                    }

                    Construct::Exit(ExitConstruct {
                        exit,
                        arguments: Some(partial_args.into_argument_list(self.arena)),
                    })
                } else {
                    Construct::Exit(ExitConstruct { exit, arguments: None })
                }
            }
            T!["die"] => {
                let die = self.expect_any_keyword()?;
                if let Some(next) = self.stream.lookahead(0)?
                    && matches!(next.kind, T!["("])
                {
                    let partial_args = self.parse_partial_argument_list()?;

                    if partial_args.has_placeholders() {
                        return Ok(Expression::PartialApplication(PartialApplication::Function(
                            FunctionPartialApplication {
                                function: self.arena.alloc(Expression::Identifier(Identifier::Local(
                                    LocalIdentifier { span: die.span, value: die.value },
                                ))),
                                argument_list: partial_args,
                            },
                        )));
                    }

                    Construct::Die(DieConstruct { die, arguments: Some(partial_args.into_argument_list(self.arena)) })
                } else {
                    Construct::Die(DieConstruct { die, arguments: None })
                }
            }
            _ => {
                return Err(self.stream.unexpected(
                    Some(token),
                    T![
                        "isset",
                        "empty",
                        "eval",
                        "include",
                        "include_once",
                        "require",
                        "require_once",
                        "print",
                        "exit",
                        "die"
                    ],
                ));
            }
        }))
    }
}