diff --git a/src/ast.rs b/src/ast.rs index 440d012..a3c84b7 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -249,6 +249,26 @@ impl From<&TokenKind> for IncludeKind { } } +#[derive(Debug, Clone, PartialEq)] +pub enum TraitAdaptation { + Alias { + r#trait: Option, + method: Identifier, + alias: Identifier, + visibility: Option, + }, + Visibility { + r#trait: Option, + method: Identifier, + visibility: MethodFlag, + }, + Precedence { + r#trait: Option, + method: Identifier, + insteadof: Vec, + }, +} + #[derive(Debug, PartialEq, Clone)] pub enum Statement { InlineHtml(ByteString), @@ -328,6 +348,7 @@ pub enum Statement { }, TraitUse { traits: Vec, + adaptations: Vec, }, Interface { name: Identifier, diff --git a/src/lexer/lexer.rs b/src/lexer/lexer.rs index a69967a..85aa7e8 100644 --- a/src/lexer/lexer.rs +++ b/src/lexer/lexer.rs @@ -1157,6 +1157,7 @@ fn identifier_to_keyword(ident: &[u8]) -> Option { b"yield" => TokenKind::Yield, b"__DIR__" => TokenKind::DirConstant, b"while" => TokenKind::While, + b"insteadof" => TokenKind::Insteadof, _ => return None, }) } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 51bdce0..312f924 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -6,7 +6,7 @@ use crate::{ }, Block, Case, Catch, Expression, Identifier, MatchArm, Program, Statement, Type, }; -use crate::{ByteString, TryBlockCaughtType}; +use crate::{ByteString, TraitAdaptation, TryBlockCaughtType}; use std::{fmt::Display, vec::IntoIter}; use self::precedence::{Associativity, Precedence}; @@ -1406,16 +1406,106 @@ impl Parser { let mut traits = Vec::new(); - while self.current.kind != TokenKind::SemiColon { + while self.current.kind != TokenKind::SemiColon + && self.current.kind != TokenKind::LeftBrace + { self.optional_comma()?; let t = self.full_name()?; traits.push(t.into()); } - self.semi()?; + let mut adaptations = Vec::new(); + if self.current.kind == TokenKind::LeftBrace { + self.lbrace()?; - Ok(Statement::TraitUse { traits }) + while self.current.kind != TokenKind::RightBrace { + let (r#trait, method): (Option, Identifier) = + match self.peek.kind { + TokenKind::DoubleColon => { + let r#trait = self.full_name()?; + self.next(); + let method = self.ident()?; + (Some(r#trait.into()), method.into()) + } + _ => (None, self.ident()?.into()), + }; + + match self.current.kind { + TokenKind::As => { + self.next(); + + match self.current.kind { + TokenKind::Public + | TokenKind::Protected + | TokenKind::Private => { + let visibility: MethodFlag = + self.current.kind.clone().into(); + self.next(); + + if self.current.kind == TokenKind::SemiColon { + adaptations.push(TraitAdaptation::Visibility { + r#trait, + method, + visibility, + }); + } else { + let alias: Identifier = self.name()?.into(); + adaptations.push(TraitAdaptation::Alias { + r#trait, + method, + alias, + visibility: Some(visibility), + }); + } + } + _ => { + let alias: Identifier = self.name()?.into(); + adaptations.push(TraitAdaptation::Alias { + r#trait, + method, + alias, + visibility: None, + }); + } + } + } + TokenKind::Insteadof => { + self.next(); + + let mut insteadof = Vec::new(); + insteadof.push(self.full_name()?.into()); + while self.current.kind != TokenKind::SemiColon { + self.optional_comma()?; + insteadof.push(self.full_name()?.into()); + } + + adaptations.push(TraitAdaptation::Precedence { + r#trait, + method, + insteadof, + }); + } + _ => { + return Err(ParseError::UnexpectedToken( + self.current.kind.to_string(), + self.current.span, + )) + } + }; + + self.semi()?; + } + + self.rbrace()?; + } else { + self.semi()?; + } + + Ok(Statement::TraitUse { + traits, + adaptations, + }) } TokenKind::Const => { self.next(); @@ -2759,7 +2849,7 @@ mod tests { Arg, ArrayItem, BackedEnumType, Case, ClassFlag, Constant, DeclareItem, ElseIf, IncludeKind, InfixOp, MethodFlag, PropertyFlag, StringPart, }, - Catch, Expression, Identifier, Param, Statement, TryBlockCaughtType, Type, + Catch, Expression, Identifier, Param, Statement, TraitAdaptation, TryBlockCaughtType, Type, }; use crate::{Lexer, Use}; use pretty_assertions::assert_eq; @@ -5514,6 +5604,114 @@ mod tests { ); } + #[test] + fn trait_simple_alias() { + assert_ast( + "