mago-syntax 1.20.1

A correct, fast, and memory-efficient PHP syntax implementation, including Lexer, Parser, AST, and utilities for Mago.
Documentation
use mago_database::file::HasFileId;

use crate::T;
use crate::ast::ast::ClosingTag;
use crate::ast::ast::Terminator;
use crate::error::ParseError;
use crate::parser::Parser;
use crate::token::TokenKind;

impl<'input, 'arena> Parser<'input, 'arena> {
    pub(crate) fn parse_optional_terminator(&mut self) -> Result<Option<Terminator<'arena>>, ParseError> {
        let next = self.stream.lookahead(0)?;
        Ok(match next.map(|t| t.kind) {
            Some(T![";" | "?>"]) => Some(self.parse_terminator()?),
            _ => None,
        })
    }

    pub(crate) fn parse_terminator(&mut self) -> Result<Terminator<'arena>, ParseError> {
        if self.stream.is_at(TokenKind::Semicolon)? {
            let token = self.stream.consume()?;

            return Ok(Terminator::Semicolon(token.span_for(self.stream.file_id())));
        }

        if self.stream.is_at(TokenKind::CloseTag)? {
            let closing_tag_token = self.stream.consume()?;
            let closing_tag = ClosingTag { span: closing_tag_token.span_for(self.stream.file_id()) };

            let next = self.stream.lookahead(0)?;
            if matches!(next.map(|t| t.kind), Some(T!["<?php" | "<?"])) {
                return Ok(Terminator::TagPair(closing_tag, self.parse_opening_tag()?));
            } else {
                return Ok(Terminator::ClosingTag(closing_tag));
            }
        }

        let Some(next) = self.stream.lookahead(0)? else {
            return Err(self.stream.unexpected(None, T![";", "?>"]));
        };

        self.errors.push(self.stream.unexpected(Some(next), T![";", "?>"]));

        Ok(Terminator::Missing(next.span_for(self.stream.file_id())))
    }
}