backyard-parser 0.1.10

Parse PHP code to AST node.
Documentation
use bumpalo::vec;
use backyard_lexer::token::TokenType;
use backyard_nodes::{
  Location,
  Modifier,
  Node,
  PropertyHookNode,
  PropertyItemNode,
  PropertyNode,
  Visibility,
  utils::{ IntoBoxedNode, IntoBoxedOptionNode },
};

use crate::{
  error::ParserError,
  parser::{ LocationHelper, LoopArgument, OptionNodeOrInternal, Parser, TokenTypeArrayCombine },
  utils::{ match_pattern, Lookup, LookupResult, ModifierLookup },
};

use super::{
  block::BlockParser,
  comment::CommentParser,
  function::FunctionParser,
  identifier::IdentifierParser,
  types::TypesParser,
};

#[derive(Debug, Clone)]
pub struct PropertyParser;

impl PropertyParser {
  pub fn test<'arena, 'a>(
    parser: &mut Parser<'arena, 'a>,
    _: &mut LoopArgument
  ) -> Option<std::vec::Vec<LookupResult<'arena>>> {
    match_pattern(
      parser,
      &[
        Lookup::Modifiers(
          &[
            ModifierLookup::Visibility,
            ModifierLookup::Custom(&[TokenType::Static, TokenType::Readonly]),
          ]
        ),
        Lookup::Optional(&[TokenType::Var]),
        Lookup::OptionalType,
        Lookup::Equal(&[TokenType::Variable]),
      ]
    )
  }

  pub fn parse<'arena, 'a, 'b>(
    parser: &mut Parser<'arena, 'a>,
    mut matched: std::vec::Vec<LookupResult<'arena>>,
    start_loc: Location,
    _: &mut LoopArgument<'arena, 'b>
  ) -> Result<Node<'arena>, ParserError> {
    if let [modifiers, has_var, prop_type, name] = matched.as_mut_slice() {
      let next_token = parser.get_token(parser.position)?;
      let prop_type = prop_type.as_optional_type().into_boxed(parser.arena);
      let first_prop = if next_token.token_type == TokenType::Assignment {
        parser.position += 1;
        if
          let Some(value) = parser.get_statement(
            &mut LoopArgument::with_tokens(
              parser.arena,
              "property",
              &[TokenType::Comma, TokenType::Semicolon, TokenType::LeftCurlyBracket],
              &[]
            )
          )?
        {
          let name = name.as_equal(parser)?;
          let item_start_loc = name.get_location().unwrap();
          PropertyItemNode::loc(
            IdentifierParser::from_token(name).into_boxed(parser.arena),
            prop_type,
            Some(value.into_boxed(parser.arena)),
            parser.gen_loc(item_start_loc)
          )
        } else {
          return Err(ParserError::Internal);
        }
      } else {
        let name = name.as_equal(parser)?;
        let item_start_loc = name.get_location().unwrap();
        PropertyItemNode::loc(
          IdentifierParser::from_token(name).into_boxed(parser.arena),
          prop_type,
          None,
          parser.gen_loc(item_start_loc)
        )
      };
      let mut items = vec![in parser.arena; first_prop];
      let mut hooks = vec![in parser.arena];
      let next_token = parser.get_token(parser.position)?;
      if next_token.token_type == TokenType::Comma {
        let next_items = parser.get_children(
          &mut LoopArgument::new(
            parser.arena,
            "property",
            &[TokenType::Comma],
            &[TokenType::Semicolon, TokenType::LeftCurlyBracket],
            &[
              (CommentParser::test, CommentParser::parse),
              (TypesParser::test, TypesParser::parse),
              (PropertyItemParser::test, PropertyItemParser::parse),
            ]
          )
        )?;
        items.extend(next_items);
      } else if next_token.token_type == TokenType::LeftCurlyBracket {
        parser.position += 1;
        hooks = parser.get_children(
          &mut LoopArgument::new(
            parser.arena,
            "property",
            &[],
            &[TokenType::RightCurlyBracket],
            &[
              (CommentParser::test, CommentParser::parse),
              (HookParser::test, HookParser::parse),
            ]
          )
        )?;
      }
      let mut visibilities = std::vec![];
      let mut modifier = None;
      if let Some([m0, m1]) = modifiers.as_modifier() {
        visibilities = m0.as_visibilities(parser);
        modifier = m1.as_custom(parser, |x| Modifier::try_from(x));
      }
      if visibilities.is_empty() && !has_var.is_empty() {
        visibilities.push(Visibility::Public);
      }
      return Ok(PropertyNode::loc(visibilities, modifier, hooks, items, parser.gen_loc(start_loc)));
    }
    Err(ParserError::Internal)
  }
}

#[derive(Debug, Clone)]
pub struct PropertyItemParser;

impl PropertyItemParser {
  pub fn test<'arena, 'a>(
    parser: &mut Parser<'arena, 'a>,
    _: &mut LoopArgument
  ) -> Option<std::vec::Vec<LookupResult<'arena>>> {
    match_pattern(
      parser,
      &[Lookup::Equal(&[TokenType::Variable]), Lookup::Optional(&[TokenType::Assignment])]
    )
  }

  pub fn parse<'arena, 'a, 'b>(
    parser: &mut Parser<'arena, 'a>,
    matched: std::vec::Vec<LookupResult<'arena>>,
    start_loc: Location,
    args: &mut LoopArgument<'arena, 'b>
  ) -> Result<Node<'arena>, ParserError> {
    if let [name, has_value] = matched.as_slice() {
      let name = IdentifierParser::from_token(name.as_equal(parser)?);
      let value = if !has_value.is_empty() {
        parser.get_statement(
          &mut LoopArgument::with_tokens(
            parser.arena,
            "property_item",
            &args.separators.combine(&[TokenType::Comma, TokenType::Semicolon]),
            args.breakers
          )
        )?
      } else {
        None
      };
      return Ok(
        PropertyItemNode::loc(
          name.into_boxed(parser.arena),
          args.last_expr.take().into_boxed(parser.arena),
          value.into_boxed(parser.arena),
          parser.gen_loc(start_loc)
        )
      );
    }
    Err(ParserError::Internal)
  }
}

#[derive(Debug, Clone)]
pub struct HookParser;

impl HookParser {
  pub fn test<'arena, 'a>(
    parser: &mut Parser<'arena, 'a>,
    _: &mut LoopArgument
  ) -> Option<std::vec::Vec<LookupResult<'arena>>> {
    match_pattern(
      parser,
      &[
        Lookup::Optional(&[TokenType::BitwiseAnd]),
        Lookup::Equal(&[TokenType::Get, TokenType::Set]),
        Lookup::Optional(&[TokenType::LeftParenthesis]),
      ]
    )
  }

  pub fn parse<'arena, 'a, 'b>(
    parser: &mut Parser<'arena, 'a>,
    matched: std::vec::Vec<LookupResult<'arena>>,
    start_loc: Location,
    _: &mut LoopArgument<'arena, 'b>
  ) -> Result<Node<'arena>, ParserError> {
    if let [is_ref, name, has_param] = matched.as_slice() {
      let is_get = name.as_equal(parser)?.token_type == TokenType::Get;
      let params = if !is_get && !has_param.is_empty() {
        FunctionParser::get_parameters(parser)?
      } else {
        vec![in parser.arena]
      };
      let next_token = parser.get_token(parser.position)?;
      let body = if next_token.token_type == TokenType::LeftCurlyBracket {
        BlockParser::new_block(parser)?
      } else if next_token.token_type == TokenType::Arrow {
        parser.position += 1;
        let expr = parser
          .get_statement(
            &mut LoopArgument::with_tokens(parser.arena, "set_hook", &[], &[TokenType::Semicolon])
          )?
          .ok_internal()?;
        parser.position += 1;
        expr
      } else {
        return Err(ParserError::Internal);
      };
      return Ok(
        PropertyHookNode::loc(
          is_get,
          !is_ref.is_empty(),
          params,
          body.into_boxed(parser.arena),
          parser.gen_loc(start_loc)
        )
      );
    }
    Err(ParserError::Internal)
  }
}