mago-syntax 1.0.0-alpha.5

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::*;
use crate::ast::sequence::Sequence;
use crate::ast::sequence::TokenSeparatedSequence;
use crate::error::ParseError;
use crate::parser::internal::attribute;
use crate::parser::internal::block::parse_block;
use crate::parser::internal::expression;
use crate::parser::internal::expression::parse_expression;
use crate::parser::internal::function_like::parameter;
use crate::parser::internal::identifier;
use crate::parser::internal::modifier::parse_modifier_sequence;
use crate::parser::internal::terminator::parse_terminator;
use crate::parser::internal::token_stream::TokenStream;
use crate::parser::internal::type_hint::parse_optional_type_hint;
use crate::parser::internal::utils;
use crate::parser::internal::variable::parse_direct_variable;

pub fn parse_property_with_attributes_and_modifiers(
    stream: &mut TokenStream<'_, '_>,
    attributes: Sequence<AttributeList>,
    modifiers: Sequence<Modifier>,
) -> Result<Property, ParseError> {
    let var = utils::maybe_expect_keyword(stream, T!["var"])?;
    let hint = parse_optional_type_hint(stream)?;
    let item = parse_property_item(stream)?;

    let next = utils::peek(stream)?.kind;
    if matches!(next, T!["{"]) {
        return Ok(Property::Hooked(HookedProperty {
            attribute_lists: attributes,
            modifiers,
            var,
            hint,
            item,
            hook_list: parse_property_hook_list(stream)?,
        }));
    }

    Ok(Property::Plain(PlainProperty {
        attribute_lists: attributes,
        modifiers,
        var,
        hint,
        items: {
            let mut items = vec![item];
            let mut commas = Vec::new();
            if matches!(next, T![","]) {
                commas.push(utils::expect_any(stream)?);

                loop {
                    let item = parse_property_item(stream)?;
                    items.push(item);

                    match utils::maybe_expect(stream, T![","])? {
                        Some(comma) => {
                            commas.push(comma);
                        }
                        None => {
                            break;
                        }
                    }
                }
            }

            TokenSeparatedSequence::new(items, commas)
        },
        terminator: parse_terminator(stream)?,
    }))
}

pub fn parse_property_item(stream: &mut TokenStream<'_, '_>) -> Result<PropertyItem, ParseError> {
    let next = utils::maybe_peek_nth(stream, 1)?;

    Ok(match next.map(|t| t.kind) {
        Some(T!["="]) => PropertyItem::Concrete(parse_property_concrete_item(stream)?),
        _ => PropertyItem::Abstract(parse_property_abstract_item(stream)?),
    })
}

pub fn parse_property_abstract_item(stream: &mut TokenStream<'_, '_>) -> Result<PropertyAbstractItem, ParseError> {
    Ok(PropertyAbstractItem { variable: parse_direct_variable(stream)? })
}

pub fn parse_property_concrete_item(stream: &mut TokenStream<'_, '_>) -> Result<PropertyConcreteItem, ParseError> {
    Ok(PropertyConcreteItem {
        variable: parse_direct_variable(stream)?,
        equals: utils::expect_span(stream, T!["="])?,
        value: parse_expression(stream)?,
    })
}

pub fn parse_optional_property_hook_list(
    stream: &mut TokenStream<'_, '_>,
) -> Result<Option<PropertyHookList>, ParseError> {
    Ok(match utils::maybe_peek(stream)?.map(|t| t.kind) {
        Some(T!["{"]) => Some(parse_property_hook_list(stream)?),
        _ => None,
    })
}

pub fn parse_property_hook_list(stream: &mut TokenStream<'_, '_>) -> Result<PropertyHookList, ParseError> {
    Ok(PropertyHookList {
        left_brace: utils::expect_span(stream, T!["{"])?,
        hooks: {
            let mut hooks = Vec::new();
            loop {
                let token = utils::peek(stream)?;
                if T!["}"] == token.kind {
                    break;
                }

                let hook = parse_property_hook(stream)?;
                hooks.push(hook);
            }

            Sequence::new(hooks)
        },
        right_brace: utils::expect_span(stream, T!["}"])?,
    })
}

pub fn parse_property_hook(stream: &mut TokenStream<'_, '_>) -> Result<PropertyHook, ParseError> {
    Ok(PropertyHook {
        attribute_lists: attribute::parse_attribute_list_sequence(stream)?,
        ampersand: utils::maybe_expect(stream, T!["&"])?.map(|t| t.span),
        modifiers: parse_modifier_sequence(stream)?,
        name: identifier::parse_local_identifier(stream)?,
        parameters: parameter::parse_optional_function_like_parameter_list(stream)?,
        body: parse_property_hook_body(stream)?,
    })
}

pub fn parse_property_hook_body(stream: &mut TokenStream<'_, '_>) -> Result<PropertyHookBody, ParseError> {
    let next = utils::peek(stream)?;

    Ok(match next.kind {
        T![";"] => PropertyHookBody::Abstract(parse_property_hook_abstract_body(stream)?),
        T!["{"] | T!["=>"] => PropertyHookBody::Concrete(parse_property_hook_concrete_body(stream)?),
        _ => return Err(utils::unexpected(stream, Some(next), T![";", "{", "=>"])),
    })
}

pub fn parse_property_hook_abstract_body(
    stream: &mut TokenStream<'_, '_>,
) -> Result<PropertyHookAbstractBody, ParseError> {
    Ok(PropertyHookAbstractBody { semicolon: utils::expect_span(stream, T![";"])? })
}

pub fn parse_property_hook_concrete_body(
    stream: &mut TokenStream<'_, '_>,
) -> Result<PropertyHookConcreteBody, ParseError> {
    let next = utils::peek(stream)?;

    Ok(match next.kind {
        T!["{"] => PropertyHookConcreteBody::Block(parse_block(stream)?),
        T!["=>"] => PropertyHookConcreteBody::Expression(parse_property_hook_concrete_expression_body(stream)?),
        _ => return Err(utils::unexpected(stream, Some(next), T!["{", "=>"])),
    })
}

pub fn parse_property_hook_concrete_expression_body(
    stream: &mut TokenStream<'_, '_>,
) -> Result<PropertyHookConcreteExpressionBody, ParseError> {
    Ok(PropertyHookConcreteExpressionBody {
        arrow: utils::expect_span(stream, T!["=>"])?,
        expression: expression::parse_expression(stream)?,
        semicolon: utils::expect_span(stream, T![";"])?,
    })
}