Skip to content
This repository was archived by the owner on Jul 24, 2024. It is now read-only.

Commit 51b885c

Browse files
authored
chore: improve properties parser (#6)
Signed-off-by: azjezz <azjezz@protonmail.com>
1 parent 737d485 commit 51b885c

40 files changed

+1828
-173
lines changed

src/lexer/token.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,7 @@ impl Display for TokenKind {
282282
Self::EndSwitch => "endswitch",
283283
Self::EndWhile => "endwhile",
284284
Self::Enum => "enum",
285-
Self::Eof => "",
285+
Self::Eof => "[end of file]",
286286
Self::Equals => "=",
287287
Self::Extends => "extends",
288288
Self::False => "false",
@@ -382,8 +382,10 @@ impl Display for TokenKind {
382382
Self::Interface => "interface",
383383
Self::NamespaceConstant => "__NAMESPACE__",
384384
Self::PowEquals => "**=",
385+
Self::Variable(v) => {
386+
return write!(f, "${}", v);
387+
}
385388
Self::StringPart(v)
386-
| Self::Variable(v)
387389
| Self::QualifiedIdentifier(v)
388390
| Self::Identifier(v)
389391
| Self::FullyQualifiedIdentifier(v)

src/parser/ast.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,39 @@ pub enum Type {
3232
Mixed,
3333
Callable,
3434
Iterable,
35+
StaticReference,
36+
SelfReference,
37+
ParentReference,
3538
}
3639

3740
impl Type {
3841
pub fn standalone(&self) -> bool {
3942
matches!(self, Type::Mixed | Type::Never | Type::Void)
4043
}
44+
45+
pub fn nullable(&self) -> bool {
46+
matches!(self, Type::Nullable(_))
47+
}
48+
49+
pub fn includes_callable(&self) -> bool {
50+
match &self {
51+
Self::Callable => true,
52+
Self::Union(types) | Self::Intersection(types) => {
53+
types.iter().any(|x| x.includes_callable())
54+
}
55+
_ => false,
56+
}
57+
}
58+
59+
pub fn includes_class_scoped(&self) -> bool {
60+
match &self {
61+
Self::StaticReference | Self::SelfReference | Self::ParentReference => true,
62+
Self::Union(types) | Self::Intersection(types) => {
63+
types.iter().any(|x| x.includes_class_scoped())
64+
}
65+
_ => false,
66+
}
67+
}
4168
}
4269

4370
impl Display for Type {
@@ -77,6 +104,9 @@ impl Display for Type {
77104
Type::Mixed => write!(f, "mixed"),
78105
Type::Callable => write!(f, "callable"),
79106
Type::Iterable => write!(f, "iterable"),
107+
Type::StaticReference => write!(f, "static"),
108+
Type::SelfReference => write!(f, "self"),
109+
Type::ParentReference => write!(f, "parent"),
80110
}
81111
}
82112
}

src/parser/error.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ pub enum ParseError {
1515
StandaloneTypeUsedInCombination(Type, Span),
1616
TryWithoutCatchOrFinally(Span),
1717
VariadicPromotedProperty(Span),
18+
MissingTypeForReadonlyProperty(String, String, Span),
1819
PromotedPropertyOutsideConstructor(Span),
1920
PromotedPropertyOnAbstractConstructor(Span),
2021
AbstractModifierOnNonAbstractClassMethod(Span),
@@ -27,6 +28,8 @@ pub enum ParseError {
2728
FinalModifierOnPrivateConstant(Span),
2829
FinalModifierOnAbstractClass(Span),
2930
UnpredictableState(Span),
31+
StaticPropertyUsingReadonlyModifier(String, String, Span),
32+
ReadonlyPropertyHasDefaultValue(String, String, Span),
3033
}
3134

3235
impl Display for ParseError {
@@ -47,11 +50,12 @@ impl Display for ParseError {
4750
None => write!(f, "Parse Error: unexpected end of file, expecting {} on line {} column {}", expected, span.0, span.1),
4851
}
4952
},
53+
Self::MissingTypeForReadonlyProperty(class, prop, span) => write!(f, "Parse Error: Readonly property {}::${} must have type on line {} column {}", class, prop, span.0, span.1),
5054
Self::MultipleModifiers(modifier, span) => write!(f, "Parse Error: Multiple {} modifiers are not allowed on line {} column {}", modifier, span.0, span.1),
5155
Self::MultipleAccessModifiers( span) => write!(f, "Parse Error: Multiple access type modifiers are not allowed on line {} column {}", span.0, span.1),
5256
Self::UnexpectedToken(message, span) => write!(f, "Parse Error: Unexpected token {} on line {} column {}", message, span.0, span.1),
5357
Self::UnexpectedEndOfFile => write!(f, "Parse Error: unexpected end of file."),
54-
Self::FinalModifierOnAbstractClassMember(span) => write!(f, "Parse Error: Cannot use the final modifier on an abstract class member on line {} column {}", span.0, span.1),
58+
Self::FinalModifierOnAbstractClassMember(span) => write!(f, "Parse Error: Cannot use 'final' as an abstract class member modifier on line {} column {}", span.0, span.1),
5559
Self::StaticModifierOnConstant(span) => write!(f, "Parse Error: Cannot use 'static' as constant modifier on line {} column {}", span.0, span.1),
5660
Self::ReadonlyModifierOnConstant(span) => write!(f, "Parse Error: Cannot use 'readonly' as constant modifier on line {} column {}", span.0, span.1),
5761
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),
@@ -65,7 +69,9 @@ impl Display for ParseError {
6569
Self::ConstructorInEnum(name, span) => write!(f, "Parse Error: Enum '{}' cannot have a constructor on line {} column {}", name, span.0, span.1),
6670
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),
6771
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),
68-
Self::UnpredictableState(span) => write!(f, "Parse Error: Reached an unpredictable state on line {} column {}", span.0, span.1)
72+
Self::UnpredictableState(span) => write!(f, "Parse Error: Reached an unpredictable state on line {} column {}", span.0, span.1),
73+
Self::StaticPropertyUsingReadonlyModifier(class, prop, span) => write!(f, "Parse Error: Static property {}:${} cannot be readonly on line {} column {}", class, prop, span.0, span.1),
74+
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),
6975
}
7076
}
7177
}

src/parser/internal/classish_statement.rs

Lines changed: 42 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use crate::expected_scope;
22
use crate::lexer::token::TokenKind;
33
use crate::parser::ast::Identifier;
44
use crate::parser::ast::MethodFlag;
5+
use crate::parser::ast::PropertyFlag;
56
use crate::parser::ast::Statement;
67
use crate::parser::ast::TraitAdaptation;
78
use crate::parser::error::ParseError;
@@ -12,7 +13,6 @@ use crate::parser::state::State;
1213
use crate::parser::Parser;
1314

1415
use crate::expect_token;
15-
use crate::expected_token_err;
1616
use crate::peek_token;
1717

1818
impl Parser {
@@ -123,15 +123,22 @@ impl Parser {
123123

124124
let member_flags = self.class_members_flags(state)?;
125125

126-
match &state.current.kind {
127-
TokenKind::Const => self.parse_classish_const(state, member_flags),
128-
TokenKind::Function => self.method(
126+
if state.current.kind == TokenKind::Const {
127+
return self.parse_classish_const(state, member_flags);
128+
}
129+
130+
if state.current.kind == TokenKind::Function {
131+
return self.method(
129132
state,
130133
member_flags.iter().map(|t| t.clone().into()).collect(),
131-
),
132-
// TODO
133-
TokenKind::Variable(_) => {
134-
let var = self.var(state)?;
134+
);
135+
}
136+
137+
let ty = self.get_optional_type(state)?;
138+
139+
expect_token!([
140+
TokenKind::Variable(var) => {
141+
let flags: Vec<PropertyFlag> = member_flags.into_iter().map(|f| f.into()).collect();
135142
let mut value = None;
136143

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

142149
self.semi(state)?;
143150

144-
Ok(Statement::Property {
145-
var,
146-
value,
147-
r#type: None,
148-
flags: member_flags.into_iter().map(|f| f.into()).collect(),
149-
})
150-
}
151-
TokenKind::Question
152-
| TokenKind::Identifier(_)
153-
| TokenKind::QualifiedIdentifier(_)
154-
| TokenKind::FullyQualifiedIdentifier(_)
155-
| TokenKind::Array
156-
| TokenKind::Null => {
157-
let prop_type = self.type_string(state)?;
158-
let var = self.var(state)?;
159-
let mut value = None;
151+
if flags.contains(&PropertyFlag::Readonly) {
152+
if flags.contains(&PropertyFlag::Static) {
153+
let class_name: String = expected_scope!([
154+
Scope::Class(name, _) => state.named(&name),
155+
Scope::Trait(name) => state.named(&name),
156+
Scope::AnonymousClass => state.named(&"class@anonymous".into()),
157+
], state);
160158

161-
if state.current.kind == TokenKind::Equals {
162-
state.next();
163-
value = Some(self.expression(state, Precedence::Lowest)?);
164-
}
159+
return Err(ParseError::StaticPropertyUsingReadonlyModifier(class_name, var.to_string(), state.current.span));
160+
}
165161

166-
// TODO: Support comma-separated property declarations.
167-
// nikic/php-parser does this with a single Property statement
168-
// that is capable of holding multiple property declarations.
169-
self.semi(state)?;
162+
if value.is_some() {
163+
let class_name: String = expected_scope!([
164+
Scope::Class(name, _) => state.named(&name),
165+
Scope::Trait(name) => state.named(&name),
166+
Scope::AnonymousClass => state.named(&"class@anonymous".into()),
167+
], state);
168+
169+
return Err(ParseError::ReadonlyPropertyHasDefaultValue(class_name, var.to_string(), state.current.span));
170+
}
171+
}
170172

171173
Ok(Statement::Property {
172174
var,
173175
value,
174-
r#type: Some(prop_type),
175-
flags: member_flags.into_iter().map(|f| f.into()).collect(),
176+
r#type: ty,
177+
flags,
176178
})
177179
}
178-
_ => expected_token_err!(
179-
["`const`", "`function`", "an identifier", "a varaible"],
180-
state
181-
),
182-
}
180+
], state, ["a varaible"])
183181
}
184182

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

188-
let mut var_type = None;
189-
190-
if !matches!(state.current.kind, TokenKind::Variable(_)) {
191-
var_type = Some(self.type_string(state)?);
192-
}
193-
186+
let ty = self.get_optional_type(state)?;
194187
let var = self.var(state)?;
195188
let mut value = None;
196189

@@ -205,7 +198,7 @@ impl Parser {
205198
Ok(Statement::Var {
206199
var,
207200
value,
208-
r#type: var_type,
201+
r#type: ty,
209202
})
210203
}
211204

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

241-
match state.current.kind {
234+
expect_token!([
242235
TokenKind::As => {
243-
state.next();
244-
245236
match state.current.kind {
246237
TokenKind::Public | TokenKind::Protected | TokenKind::Private => {
247238
let visibility: MethodFlag = state.current.kind.clone().into();
@@ -273,10 +264,8 @@ impl Parser {
273264
});
274265
}
275266
}
276-
}
267+
},
277268
TokenKind::Insteadof => {
278-
state.next();
279-
280269
let mut insteadof = Vec::new();
281270
insteadof.push(self.full_name(state)?.into());
282271
while state.current.kind != TokenKind::SemiColon {
@@ -290,13 +279,7 @@ impl Parser {
290279
insteadof,
291280
});
292281
}
293-
_ => {
294-
return Err(ParseError::UnexpectedToken(
295-
state.current.kind.to_string(),
296-
state.current.span,
297-
))
298-
}
299-
};
282+
], state, ["`as`", "`insteadof`"]);
300283

301284
self.semi(state)?;
302285
}

src/parser/internal/functions.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ impl Parser {
7979
if state.current.kind == TokenKind::Colon {
8080
self.colon(state)?;
8181

82-
return_type = Some(self.type_string(state)?);
82+
return_type = Some(self.get_type(state)?);
8383
}
8484

8585
self.lbrace(state)?;
@@ -128,7 +128,7 @@ impl Parser {
128128
if state.current.kind == TokenKind::Colon {
129129
self.colon(state)?;
130130

131-
return_type = Some(self.type_string(state)?);
131+
return_type = Some(self.get_type(state)?);
132132
}
133133

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

172-
return_type = Some(self.type_string(state)?);
172+
return_type = Some(self.get_type(state)?);
173173
}
174174

175175
self.lbrace(state)?;
@@ -241,7 +241,7 @@ impl Parser {
241241
if state.current.kind == TokenKind::Colon {
242242
self.colon(state)?;
243243

244-
return_type = Some(self.type_string(state)?);
244+
return_type = Some(self.get_type(state)?);
245245
}
246246

247247
if !has_body {

src/parser/internal/ident.rs

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -37,31 +37,6 @@ impl Parser {
3737
], state, "a variable"))
3838
}
3939

40-
pub(in crate::parser) fn full_name_maybe_type_keyword(
41-
&self,
42-
state: &mut State,
43-
) -> ParseResult<ByteString> {
44-
match state.current.kind {
45-
TokenKind::Array | TokenKind::Callable => {
46-
let r = Ok(state.current.kind.to_string().into());
47-
state.next();
48-
r
49-
}
50-
_ => self.full_name(state),
51-
}
52-
}
53-
54-
pub(in crate::parser) fn type_with_static(&self, state: &mut State) -> ParseResult<ByteString> {
55-
Ok(match state.current.kind {
56-
TokenKind::Static | TokenKind::Null | TokenKind::True | TokenKind::False => {
57-
let str = state.current.kind.to_string();
58-
state.next();
59-
str.into()
60-
}
61-
_ => self.full_name_maybe_type_keyword(state)?,
62-
})
63-
}
64-
6540
pub(in crate::parser) fn ident_maybe_reserved(
6641
&self,
6742
state: &mut State,

0 commit comments

Comments
 (0)