mago-syntax 1.20.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::ast::Argument;
use crate::ast::ast::ArgumentList;
use crate::ast::ast::NamedArgument;
use crate::ast::ast::NamedPlaceholderArgument;
use crate::ast::ast::PartialArgument;
use crate::ast::ast::PartialArgumentList;
use crate::ast::ast::PlaceholderArgument;
use crate::ast::ast::PositionalArgument;
use crate::ast::ast::VariadicPlaceholderArgument;
use crate::error::ParseError;
use crate::parser::Parser;

impl<'input, 'arena> Parser<'input, 'arena> {
    pub(crate) fn parse_optional_argument_list(&mut self) -> Result<Option<ArgumentList<'arena>>, ParseError> {
        if let Some(T!["("]) = self.stream.peek_kind(0)? { Ok(Some(self.parse_argument_list()?)) } else { Ok(None) }
    }

    pub(crate) fn parse_argument_list(&mut self) -> Result<ArgumentList<'arena>, ParseError> {
        let result = self.parse_comma_separated_sequence(T!["("], T![")"], |p| p.parse_argument())?;

        Ok(ArgumentList { left_parenthesis: result.open, arguments: result.sequence, right_parenthesis: result.close })
    }

    pub(crate) fn parse_argument(&mut self) -> Result<Argument<'arena>, ParseError> {
        let current = self.stream.lookahead(0)?.ok_or_else(|| self.stream.unexpected(None, &[]))?;
        if current.kind.is_identifier_maybe_reserved() && matches!(self.stream.peek_kind(1)?, Some(T![":"])) {
            return Ok(Argument::Named(NamedArgument {
                name: self.parse_local_identifier()?,
                colon: self.stream.consume_span()?,
                value: self.parse_expression()?,
            }));
        }

        Ok(Argument::Positional(PositionalArgument {
            ellipsis: if self.stream.is_at(T!["..."])? { Some(self.stream.eat_span(T!["..."])?) } else { None },
            value: self.parse_expression()?,
        }))
    }

    pub(crate) fn parse_partial_argument_list(&mut self) -> Result<PartialArgumentList<'arena>, ParseError> {
        let result = self.parse_comma_separated_sequence(T!["("], T![")"], |p| p.parse_partial_argument())?;

        Ok(PartialArgumentList {
            left_parenthesis: result.open,
            arguments: result.sequence,
            right_parenthesis: result.close,
        })
    }

    pub(crate) fn parse_partial_argument(&mut self) -> Result<PartialArgument<'arena>, ParseError> {
        let current = self.stream.lookahead(0)?.ok_or_else(|| self.stream.unexpected(None, &[]))?;

        if current.kind == T!["?"] {
            return Ok(PartialArgument::Placeholder(PlaceholderArgument { span: self.stream.consume_span()? }));
        }

        if current.kind == T!["..."] {
            let next = self.stream.lookahead(1)?;
            match next.map(|t| t.kind) {
                Some(crate::token::TokenKind::Comma | crate::token::TokenKind::RightParenthesis) | None => {
                    return Ok(PartialArgument::VariadicPlaceholder(VariadicPlaceholderArgument {
                        span: self.stream.consume_span()?,
                    }));
                }
                _ => {}
            }
        }

        if current.kind.is_identifier_maybe_reserved() && matches!(self.stream.peek_kind(1)?, Some(T![":"])) {
            let name = self.parse_local_identifier()?;
            let colon = self.stream.consume_span()?;

            if self.stream.is_at(T!["?"])? {
                let question_mark = self.stream.eat_span(T!["?"])?;
                return Ok(PartialArgument::NamedPlaceholder(NamedPlaceholderArgument { name, colon, question_mark }));
            }

            return Ok(PartialArgument::Named(NamedArgument { name, colon, value: self.parse_expression()? }));
        }

        Ok(PartialArgument::Positional(PositionalArgument {
            ellipsis: if self.stream.is_at(T!["..."])? { Some(self.stream.eat_span(T!["..."])?) } else { None },
            value: self.parse_expression()?,
        }))
    }
}