use php_ast::*;
use php_lexer::TokenKind;
use crate::diagnostics::{ParseError, ERROR_PLACEHOLDER};
use crate::parser::Parser;
pub(super) fn parse_trait_adaptations<'arena, 'src>(
parser: &'_ mut Parser<'arena, 'src>,
) -> ArenaVec<'arena, TraitAdaptation<'arena, 'src>> {
let mut adaptations = parser.alloc_vec();
while !parser.check(TokenKind::RightBrace) && !parser.check(TokenKind::Eof) {
let start = parser.start_span();
let first_name = parser.parse_name();
if parser.eat(TokenKind::DoubleColon).is_some() {
let method = if let Some((text, span)) = parser.eat_identifier_or_keyword() {
Name::Simple { value: text, span }
} else {
let span = parser.current_span();
parser.error(ParseError::Expected {
expected: "method name".into(),
found: parser.current_kind(),
span,
});
Name::Simple {
value: ERROR_PLACEHOLDER,
span,
}
};
if parser.check(TokenKind::Identifier) && parser.current_text() == "insteadof" {
parser.advance(); let mut insteadof = {
let mut _v = parser.alloc_vec_with_capacity(1);
_v.push(parser.parse_name());
_v
};
while parser.eat(TokenKind::Comma).is_some() {
if parser.check(TokenKind::Semicolon) {
break;
} insteadof.push(parser.parse_name());
}
parser.expect(TokenKind::Semicolon);
let span = Span::new(start, parser.previous_end());
adaptations.push(TraitAdaptation {
kind: TraitAdaptationKind::Precedence {
trait_name: first_name,
method,
insteadof,
},
span,
});
} else if parser.eat(TokenKind::As).is_some() {
let (new_modifier, new_name) = parse_alias_rhs(parser);
parser.expect(TokenKind::Semicolon);
let span = Span::new(start, parser.previous_end());
adaptations.push(TraitAdaptation {
kind: TraitAdaptationKind::Alias {
trait_name: Some(first_name),
method,
new_modifier,
new_name,
},
span,
});
} else {
let span = parser.current_span();
parser.error(ParseError::Expected {
expected: "'insteadof' or 'as'".into(),
found: parser.current_kind(),
span,
});
parser.advance();
}
} else if parser.eat(TokenKind::As).is_some() {
let (new_modifier, new_name) = parse_alias_rhs(parser);
parser.expect(TokenKind::Semicolon);
let span = Span::new(start, parser.previous_end());
adaptations.push(TraitAdaptation {
kind: TraitAdaptationKind::Alias {
trait_name: None,
method: first_name,
new_modifier,
new_name,
},
span,
});
} else {
let span = parser.current_span();
parser.error(ParseError::Expected {
expected: "'::' or 'as'".into(),
found: parser.current_kind(),
span,
});
parser.advance();
}
}
parser.expect(TokenKind::RightBrace);
adaptations
}
fn parse_alias_rhs<'arena, 'src>(
parser: &'_ mut Parser<'arena, 'src>,
) -> (Option<Visibility>, Option<Name<'arena, 'src>>) {
let new_modifier = match parser.current_kind() {
TokenKind::Public => {
parser.advance();
Some(Visibility::Public)
}
TokenKind::Protected => {
parser.advance();
Some(Visibility::Protected)
}
TokenKind::Private => {
parser.advance();
Some(Visibility::Private)
}
_ => None,
};
let new_name = parser
.eat_identifier_or_keyword()
.map(|(text, span)| Name::Simple { value: text, span });
(new_modifier, new_name)
}