Skip to content
This repository was archived by the owner on Jul 24, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/parser/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ pub enum ParseError {
CannotMixKeyedAndUnkeyedEntries(Span),
CannotUsePositionalArgumentAfterNamedArgument(Span),
CannotUseReservedKeywordAsATypeName(String, Span),
CannotUseReservedKeywordAsAGoToLabel(String, Span),
}

impl From<SyntaxError> for ParseError {
Expand Down Expand Up @@ -129,7 +130,8 @@ impl Display for ParseError {
Self::CannotAssignReferenceToNonReferencableValue(span) => write!(f, "Parse Error: cannot assign reference to non-referencable value on line {} column {}", span.0, span.1),
Self::CannotMixKeyedAndUnkeyedEntries(span) => write!(f, "Parse Error: cannot mix keyed and un-keyed entries on line {}", span.0),
Self::CannotUsePositionalArgumentAfterNamedArgument(span) => write!(f, "Parse Error: cannot use positional argument after named argument on line {} column {}", span.0, span.1),
Self::CannotUseReservedKeywordAsATypeName(ty, span) => write!(f, "Parse Error: cannot use `{}` as a type name as it is reserved on line {} column {}", ty, span.0, span.1)
Self::CannotUseReservedKeywordAsATypeName(ty, span) => write!(f, "Parse Error: cannot use `{}` as a type name as it is reserved on line {} column {}", ty, span.0, span.1),
Self::CannotUseReservedKeywordAsAGoToLabel(ty, span) => write!(f, "Parse Error: cannot use `{}` as a goto label as it is reserved on line {} column {}", ty, span.0, span.1),
}
}
}
58 changes: 55 additions & 3 deletions src/parser/expressions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ fn for_precedence(state: &mut State, precedence: Precedence) -> ParseResult<Expr
Expression::Instanceof {
left: Box::new(left),
span,
right: Box::new(Expression::Self_),
right: Box::new(Expression::Parent),
}
}
TokenKind::Instanceof if state.current.kind == TokenKind::Static => {
Expand All @@ -186,7 +186,37 @@ fn for_precedence(state: &mut State, precedence: Precedence) -> ParseResult<Expr
Expression::Instanceof {
left: Box::new(left),
span,
right: Box::new(Expression::Self_),
right: Box::new(Expression::Static),
}
}
TokenKind::Instanceof if state.current.kind == TokenKind::Enum => {
let enum_span = state.current.span;
state.next();

Expression::Instanceof {
left: Box::new(left),
span,
right: Box::new(Expression::Identifier(Identifier::SimpleIdentifier(
SimpleIdentifier {
span: enum_span,
name: "enum".into(),
},
))),
}
}
TokenKind::Instanceof if state.current.kind == TokenKind::From => {
let from_span = state.current.span;
state.next();

Expression::Instanceof {
left: Box::new(left),
span,
right: Box::new(Expression::Identifier(Identifier::SimpleIdentifier(
SimpleIdentifier {
span: from_span,
name: "from".into(),
},
))),
}
}
_ => {
Expand Down Expand Up @@ -530,7 +560,7 @@ expressions! {
functions::anonymous_function(state)
})

#[before(list), current(
#[before(reserved_identifier_static_call), current(
| TokenKind::True | TokenKind::False | TokenKind::Null
| TokenKind::Readonly | TokenKind::Self_ | TokenKind::Parent
| TokenKind::Enum | TokenKind::From
Expand All @@ -542,6 +572,14 @@ expressions! {
postfix(state, lhs, &TokenKind::LeftParen)
})

#[before(list), current(TokenKind::Enum | TokenKind::From), peek(TokenKind::DoubleColon)]
reserved_identifier_static_call(|state: &mut State| {
let ident = identifiers::type_identifier(state)?;
let lhs = Expression::Identifier(Identifier::SimpleIdentifier(ident));

postfix(state, lhs, &TokenKind::DoubleColon)
})

#[before(anonymous_class), current(TokenKind::List)]
list(|state: &mut State| {
arrays::list_expression(state)
Expand Down Expand Up @@ -776,6 +814,20 @@ expressions! {

Expression::Parent
}
TokenKind::Enum => {
let span = state.current.span;

state.next();

Expression::Identifier(Identifier::SimpleIdentifier(SimpleIdentifier { span, name: "enum".into() }))
}
TokenKind::From => {
let span = state.current.span;

state.next();

Expression::Identifier(Identifier::SimpleIdentifier(SimpleIdentifier { span, name: "from".into() }))
}
_ => clone_or_new_precedence(state)?,
};

Expand Down
4 changes: 2 additions & 2 deletions src/parser/internal/goto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::parser::internal::utils;
use crate::parser::state::State;

pub fn label_statement(state: &mut State) -> ParseResult<Statement> {
let label = identifiers::identifier(state)?;
let label = identifiers::label_identifier(state)?;

utils::skip_colon(state)?;

Expand All @@ -16,7 +16,7 @@ pub fn label_statement(state: &mut State) -> ParseResult<Statement> {
pub fn goto_statement(state: &mut State) -> ParseResult<Statement> {
utils::skip(state, TokenKind::Goto)?;

let label = identifiers::identifier(state)?;
let label = identifiers::label_identifier(state)?;

utils::skip_semicolon(state)?;

Expand Down
30 changes: 30 additions & 0 deletions src/parser/internal/identifiers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,36 @@ pub fn type_identifier(state: &mut State) -> ParseResult<SimpleIdentifier> {
}
}

/// Expect an unqualified identifier such as Foo or Bar for a class, interface, trait, or an enum name.
pub fn label_identifier(state: &mut State) -> ParseResult<SimpleIdentifier> {
match state.current.kind.clone() {
TokenKind::Identifier(name) => {
let span = state.current.span;

state.next();

Ok(SimpleIdentifier { span, name })
}
TokenKind::Enum | TokenKind::From => {
let span = state.current.span;
let name = state.current.kind.to_string().into();

state.next();

Ok(SimpleIdentifier { span, name })
}
t if is_reserved_identifier(&t) => Err(ParseError::CannotUseReservedKeywordAsAGoToLabel(
state.current.kind.to_string(),
state.current.span,
)),
_ => Err(ParseError::ExpectedToken(
vec!["an identifier".to_owned()],
Some(state.current.kind.to_string()),
state.current.span,
)),
}
}

/// Expect an unqualified identifier such as Foo or Bar.
pub fn identifier(state: &mut State) -> ParseResult<SimpleIdentifier> {
if let TokenKind::Identifier(name) = state.current.kind.clone() {
Expand Down
21 changes: 18 additions & 3 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,13 @@ fn statement(state: &mut State) -> ParseResult<Statement> {
TokenKind::Class => classes::parse(state)?,
TokenKind::Interface => interfaces::parse(state)?,
TokenKind::Trait => traits::parse(state)?,
TokenKind::Enum if state.peek.kind != TokenKind::LeftParen => enums::parse(state)?,
TokenKind::Enum
if state.peek.kind != TokenKind::LeftParen
&& state.peek.kind != TokenKind::DoubleColon
&& state.peek.kind != TokenKind::Colon =>
{
Comment on lines +119 to +122
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

guard for enum(), enum::class, and enum: ( label )

enums::parse(state)?
}
TokenKind::Function
if identifiers::is_identifier_maybe_soft_reserved(&state.peek.kind)
|| state.peek.kind == TokenKind::Ampersand =>
Expand Down Expand Up @@ -163,7 +169,13 @@ fn statement(state: &mut State) -> ParseResult<Statement> {
TokenKind::Class => classes::parse(state)?,
TokenKind::Interface => interfaces::parse(state)?,
TokenKind::Trait => traits::parse(state)?,
TokenKind::Enum if state.peek.kind != TokenKind::LeftParen => enums::parse(state)?,
TokenKind::Enum
if state.peek.kind != TokenKind::LeftParen
&& state.peek.kind != TokenKind::DoubleColon
&& state.peek.kind != TokenKind::Colon =>
{
enums::parse(state)?
}
TokenKind::Function
if identifiers::is_identifier_maybe_soft_reserved(&state.peek.kind)
|| state.peek.kind == TokenKind::Ampersand =>
Expand Down Expand Up @@ -194,7 +206,10 @@ fn statement(state: &mut State) -> ParseResult<Statement> {
}
}
TokenKind::Goto => goto::goto_statement(state)?,
TokenKind::Identifier(_) if state.peek.kind == TokenKind::Colon => {
token
if identifiers::is_identifier_maybe_reserved(token)
&& state.peek.kind == TokenKind::Colon =>
{
goto::label_statement(state)?
}
TokenKind::Declare => {
Expand Down
Loading