Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions src/lexer/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ impl Display for TokenKind {
Self::EndSwitch => "endswitch",
Self::EndWhile => "endwhile",
Self::Enum => "enum",
Self::Eof => "",
Self::Eof => "[end of file]",
Self::Equals => "=",
Self::Extends => "extends",
Self::False => "false",
Expand Down Expand Up @@ -382,8 +382,10 @@ impl Display for TokenKind {
Self::Interface => "interface",
Self::NamespaceConstant => "__NAMESPACE__",
Self::PowEquals => "**=",
Self::Variable(v) => {
return write!(f, "${}", v);
}
Self::StringPart(v)
| Self::Variable(v)
| Self::QualifiedIdentifier(v)
| Self::Identifier(v)
| Self::FullyQualifiedIdentifier(v)
Expand Down
30 changes: 30 additions & 0 deletions src/parser/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,39 @@ pub enum Type {
Mixed,
Callable,
Iterable,
StaticReference,
SelfReference,
ParentReference,
}

impl Type {
pub fn standalone(&self) -> bool {
matches!(self, Type::Mixed | Type::Never | Type::Void)
}

pub fn nullable(&self) -> bool {
matches!(self, Type::Nullable(_))
}

pub fn includes_callable(&self) -> bool {
match &self {
Self::Callable => true,
Self::Union(types) | Self::Intersection(types) => {
types.iter().any(|x| x.includes_callable())
}
_ => false,
}
}

pub fn includes_class_scoped(&self) -> bool {
match &self {
Self::StaticReference | Self::SelfReference | Self::ParentReference => true,
Self::Union(types) | Self::Intersection(types) => {
types.iter().any(|x| x.includes_class_scoped())
}
_ => false,
}
}
}

impl Display for Type {
Expand Down Expand Up @@ -77,6 +104,9 @@ impl Display for Type {
Type::Mixed => write!(f, "mixed"),
Type::Callable => write!(f, "callable"),
Type::Iterable => write!(f, "iterable"),
Type::StaticReference => write!(f, "static"),
Type::SelfReference => write!(f, "self"),
Type::ParentReference => write!(f, "parent"),
}
}
}
Expand Down
10 changes: 8 additions & 2 deletions src/parser/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ pub enum ParseError {
StandaloneTypeUsedInCombination(Type, Span),
TryWithoutCatchOrFinally(Span),
VariadicPromotedProperty(Span),
MissingTypeForReadonlyProperty(String, String, Span),
PromotedPropertyOutsideConstructor(Span),
PromotedPropertyOnAbstractConstructor(Span),
AbstractModifierOnNonAbstractClassMethod(Span),
Expand All @@ -27,6 +28,8 @@ pub enum ParseError {
FinalModifierOnPrivateConstant(Span),
FinalModifierOnAbstractClass(Span),
UnpredictableState(Span),
StaticPropertyUsingReadonlyModifier(String, String, Span),
ReadonlyPropertyHasDefaultValue(String, String, Span),
}

impl Display for ParseError {
Expand All @@ -47,11 +50,12 @@ impl Display for ParseError {
None => write!(f, "Parse Error: unexpected end of file, expecting {} on line {} column {}", expected, span.0, span.1),
}
},
Self::MissingTypeForReadonlyProperty(class, prop, span) => write!(f, "Parse Error: Readonly property {}::${} must have type on line {} column {}", class, prop, span.0, span.1),
Self::MultipleModifiers(modifier, span) => write!(f, "Parse Error: Multiple {} modifiers are not allowed on line {} column {}", modifier, span.0, span.1),
Self::MultipleAccessModifiers( span) => write!(f, "Parse Error: Multiple access type modifiers are not allowed on line {} column {}", span.0, span.1),
Self::UnexpectedToken(message, span) => write!(f, "Parse Error: Unexpected token {} on line {} column {}", message, span.0, span.1),
Self::UnexpectedEndOfFile => write!(f, "Parse Error: unexpected end of file."),
Self::FinalModifierOnAbstractClassMember(span) => write!(f, "Parse Error: Cannot use the final modifier on an abstract class member on line {} column {}", span.0, span.1),
Self::FinalModifierOnAbstractClassMember(span) => write!(f, "Parse Error: Cannot use 'final' as an abstract class member modifier on line {} column {}", span.0, span.1),
Self::StaticModifierOnConstant(span) => write!(f, "Parse Error: Cannot use 'static' as constant modifier on line {} column {}", span.0, span.1),
Self::ReadonlyModifierOnConstant(span) => write!(f, "Parse Error: Cannot use 'readonly' as constant modifier on line {} column {}", span.0, span.1),
Self::FinalModifierOnPrivateConstant(span) => write!(f, "Parse Error: Private constant cannot be final as it is not visible to other classes on line {} column {}", span.0, span.1),
Expand All @@ -65,7 +69,9 @@ impl Display for ParseError {
Self::ConstructorInEnum(name, span) => write!(f, "Parse Error: Enum '{}' cannot have a constructor on line {} column {}", name, span.0, span.1),
Self::MissingCaseValueForBackedEnum(case, name, span) => write!(f, "Parse Error: Case `{}` of backed enum `{}` must have a value on line {} column {}", case, name, span.0, span.1),
Self::CaseValueForUnitEnum(case, name, span) => write!(f, "Parse Error: Case `{}` of unit enum `{}` must not have a value on line {} column {}", case, name, span.0, span.1),
Self::UnpredictableState(span) => write!(f, "Parse Error: Reached an unpredictable state on line {} column {}", span.0, span.1)
Self::UnpredictableState(span) => write!(f, "Parse Error: Reached an unpredictable state on line {} column {}", span.0, span.1),
Self::StaticPropertyUsingReadonlyModifier(class, prop, span) => write!(f, "Parse Error: Static property {}:${} cannot be readonly on line {} column {}", class, prop, span.0, span.1),
Self::ReadonlyPropertyHasDefaultValue(class, prop, span) => write!(f, "Parse Error: Readonly property {}:${} cannot have a default value on line {} column {}", class, prop, span.0, span.1),
}
}
}
101 changes: 42 additions & 59 deletions src/parser/internal/classish_statement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::expected_scope;
use crate::lexer::token::TokenKind;
use crate::parser::ast::Identifier;
use crate::parser::ast::MethodFlag;
use crate::parser::ast::PropertyFlag;
use crate::parser::ast::Statement;
use crate::parser::ast::TraitAdaptation;
use crate::parser::error::ParseError;
Expand All @@ -12,7 +13,6 @@ use crate::parser::state::State;
use crate::parser::Parser;

use crate::expect_token;
use crate::expected_token_err;
use crate::peek_token;

impl Parser {
Expand Down Expand Up @@ -123,15 +123,22 @@ impl Parser {

let member_flags = self.class_members_flags(state)?;

match &state.current.kind {
TokenKind::Const => self.parse_classish_const(state, member_flags),
TokenKind::Function => self.method(
if state.current.kind == TokenKind::Const {
return self.parse_classish_const(state, member_flags);
}

if state.current.kind == TokenKind::Function {
return self.method(
state,
member_flags.iter().map(|t| t.clone().into()).collect(),
),
// TODO
TokenKind::Variable(_) => {
let var = self.var(state)?;
);
}

let ty = self.get_optional_type(state)?;

expect_token!([
TokenKind::Variable(var) => {
let flags: Vec<PropertyFlag> = member_flags.into_iter().map(|f| f.into()).collect();
let mut value = None;

if state.current.kind == TokenKind::Equals {
Expand All @@ -141,56 +148,42 @@ impl Parser {

self.semi(state)?;

Ok(Statement::Property {
var,
value,
r#type: None,
flags: member_flags.into_iter().map(|f| f.into()).collect(),
})
}
TokenKind::Question
| TokenKind::Identifier(_)
| TokenKind::QualifiedIdentifier(_)
| TokenKind::FullyQualifiedIdentifier(_)
| TokenKind::Array
| TokenKind::Null => {
let prop_type = self.type_string(state)?;
let var = self.var(state)?;
let mut value = None;
if flags.contains(&PropertyFlag::Readonly) {
if flags.contains(&PropertyFlag::Static) {
let class_name: String = expected_scope!([
Scope::Class(name, _) => state.named(&name),
Scope::Trait(name) => state.named(&name),
Scope::AnonymousClass => state.named(&"class@anonymous".into()),
], state);

if state.current.kind == TokenKind::Equals {
state.next();
value = Some(self.expression(state, Precedence::Lowest)?);
}
return Err(ParseError::StaticPropertyUsingReadonlyModifier(class_name, var.to_string(), state.current.span));
}

// TODO: Support comma-separated property declarations.
// nikic/php-parser does this with a single Property statement
// that is capable of holding multiple property declarations.
self.semi(state)?;
if value.is_some() {
let class_name: String = expected_scope!([
Scope::Class(name, _) => state.named(&name),
Scope::Trait(name) => state.named(&name),
Scope::AnonymousClass => state.named(&"class@anonymous".into()),
], state);

return Err(ParseError::ReadonlyPropertyHasDefaultValue(class_name, var.to_string(), state.current.span));
}
}

Ok(Statement::Property {
var,
value,
r#type: Some(prop_type),
flags: member_flags.into_iter().map(|f| f.into()).collect(),
r#type: ty,
flags,
})
}
_ => expected_token_err!(
["`const`", "`function`", "an identifier", "a varaible"],
state
),
}
], state, ["a varaible"])
}

fn parse_classish_var(&self, state: &mut State) -> ParseResult<Statement> {
state.next();

let mut var_type = None;

if !matches!(state.current.kind, TokenKind::Variable(_)) {
var_type = Some(self.type_string(state)?);
}

let ty = self.get_optional_type(state)?;
let var = self.var(state)?;
let mut value = None;

Expand All @@ -205,7 +198,7 @@ impl Parser {
Ok(Statement::Var {
var,
value,
r#type: var_type,
r#type: ty,
})
}

Expand Down Expand Up @@ -238,10 +231,8 @@ impl Parser {
_ => (None, self.ident(state)?.into()),
};

match state.current.kind {
expect_token!([
TokenKind::As => {
state.next();

match state.current.kind {
TokenKind::Public | TokenKind::Protected | TokenKind::Private => {
let visibility: MethodFlag = state.current.kind.clone().into();
Expand Down Expand Up @@ -273,10 +264,8 @@ impl Parser {
});
}
}
}
},
TokenKind::Insteadof => {
state.next();

let mut insteadof = Vec::new();
insteadof.push(self.full_name(state)?.into());
while state.current.kind != TokenKind::SemiColon {
Expand All @@ -290,13 +279,7 @@ impl Parser {
insteadof,
});
}
_ => {
return Err(ParseError::UnexpectedToken(
state.current.kind.to_string(),
state.current.span,
))
}
};
], state, ["`as`", "`insteadof`"]);

self.semi(state)?;
}
Expand Down
8 changes: 4 additions & 4 deletions src/parser/internal/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ impl Parser {
if state.current.kind == TokenKind::Colon {
self.colon(state)?;

return_type = Some(self.type_string(state)?);
return_type = Some(self.get_type(state)?);
}

self.lbrace(state)?;
Expand Down Expand Up @@ -128,7 +128,7 @@ impl Parser {
if state.current.kind == TokenKind::Colon {
self.colon(state)?;

return_type = Some(self.type_string(state)?);
return_type = Some(self.get_type(state)?);
}

expect_token!([TokenKind::DoubleArrow], state, ["`=>`"]);
Expand Down Expand Up @@ -169,7 +169,7 @@ impl Parser {
if state.current.kind == TokenKind::Colon {
self.colon(state)?;

return_type = Some(self.type_string(state)?);
return_type = Some(self.get_type(state)?);
}

self.lbrace(state)?;
Expand Down Expand Up @@ -241,7 +241,7 @@ impl Parser {
if state.current.kind == TokenKind::Colon {
self.colon(state)?;

return_type = Some(self.type_string(state)?);
return_type = Some(self.get_type(state)?);
}

if !has_body {
Expand Down
25 changes: 0 additions & 25 deletions src/parser/internal/ident.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,31 +37,6 @@ impl Parser {
], state, "a variable"))
}

pub(in crate::parser) fn full_name_maybe_type_keyword(
&self,
state: &mut State,
) -> ParseResult<ByteString> {
match state.current.kind {
TokenKind::Array | TokenKind::Callable => {
let r = Ok(state.current.kind.to_string().into());
state.next();
r
}
_ => self.full_name(state),
}
}

pub(in crate::parser) fn type_with_static(&self, state: &mut State) -> ParseResult<ByteString> {
Ok(match state.current.kind {
TokenKind::Static | TokenKind::Null | TokenKind::True | TokenKind::False => {
let str = state.current.kind.to_string();
state.next();
str.into()
}
_ => self.full_name_maybe_type_keyword(state)?,
})
}

pub(in crate::parser) fn ident_maybe_reserved(
&self,
state: &mut State,
Expand Down
Loading