mirror of
https://github.com/danog/parser.git
synced 2024-11-26 20:04:57 +01:00
chore: allow soft reserved keywords to be used as function names (#185)
Signed-off-by: azjezz <azjezz@protonmail.com>
This commit is contained in:
parent
d3612edc5c
commit
89866d952c
@ -244,11 +244,23 @@ expressions! {
|
||||
functions::arrow_function(state)
|
||||
})
|
||||
|
||||
#[before(list), current(TokenKind::Function)]
|
||||
#[before(reserved_identifier_function_call), current(TokenKind::Function)]
|
||||
anonymous_function(|state: &mut State| {
|
||||
functions::anonymous_function(state)
|
||||
})
|
||||
|
||||
#[before(list), current(
|
||||
| TokenKind::True | TokenKind::False | TokenKind::Null
|
||||
| TokenKind::Readonly | TokenKind::Self_ | TokenKind::Parent
|
||||
| TokenKind::Enum | TokenKind::From
|
||||
), peek(TokenKind::LeftParen)]
|
||||
reserved_identifier_function_call(|state: &mut State| {
|
||||
let ident = identifiers::ident_maybe_soft_reserved(state)?;
|
||||
let lhs = Expression::Identifier(ident);
|
||||
|
||||
postfix(state, lhs, &TokenKind::LeftParen)
|
||||
})
|
||||
|
||||
#[before(anonymous_class), current(TokenKind::List)]
|
||||
list(|state: &mut State| {
|
||||
arrays::list_expression(state)
|
||||
@ -814,7 +826,9 @@ fn postfix(state: &mut State, lhs: Expression, op: &TokenKind) -> Result<Express
|
||||
let property = match state.current.kind.clone() {
|
||||
TokenKind::Dollar => variables::dynamic_variable(state)?,
|
||||
TokenKind::Variable(_) => Expression::Variable(identifiers::var(state)?),
|
||||
TokenKind::Identifier(_) => Expression::Identifier(identifiers::ident(state)?),
|
||||
_ if identifiers::is_ident_maybe_reserved(&state.current.kind) => {
|
||||
Expression::Identifier(identifiers::ident_maybe_reserved(state)?)
|
||||
}
|
||||
TokenKind::LeftBrace => {
|
||||
must_be_method_call = true;
|
||||
state.next();
|
||||
@ -838,9 +852,6 @@ fn postfix(state: &mut State, lhs: Expression, op: &TokenKind) -> Result<Express
|
||||
end,
|
||||
})
|
||||
}
|
||||
_ if identifiers::is_reserved_ident(&state.current.kind) => {
|
||||
Expression::Identifier(identifiers::ident_maybe_reserved(state)?)
|
||||
}
|
||||
_ => {
|
||||
return expected_token_err!(["`{`", "`$`", "an identifier"], state);
|
||||
}
|
||||
|
@ -6,7 +6,6 @@ use crate::parser::ast::functions::Closure;
|
||||
use crate::parser::ast::functions::ClosureUse;
|
||||
use crate::parser::ast::functions::Function;
|
||||
use crate::parser::ast::functions::Method;
|
||||
use crate::parser::ast::identifiers::Identifier;
|
||||
use crate::parser::ast::modifiers::MethodModifierGroup;
|
||||
use crate::parser::ast::Expression;
|
||||
use crate::parser::ast::Statement;
|
||||
@ -174,20 +173,7 @@ pub fn function(state: &mut State) -> ParseResult<Statement> {
|
||||
false
|
||||
};
|
||||
|
||||
let name = if state.current.kind == TokenKind::Null {
|
||||
let start = state.current.span;
|
||||
let end = (start.0, start.1 + 4);
|
||||
|
||||
state.next();
|
||||
|
||||
Identifier {
|
||||
start,
|
||||
name: "null".into(),
|
||||
end,
|
||||
}
|
||||
} else {
|
||||
identifiers::ident(state)?
|
||||
};
|
||||
let name = identifiers::ident_maybe_soft_reserved(state)?;
|
||||
|
||||
// get attributes before processing parameters, otherwise
|
||||
// parameters will steal attributes of this function.
|
||||
|
@ -9,10 +9,10 @@ use crate::peek_token;
|
||||
/// Expect an unqualified identifier such as Foo or Bar.
|
||||
pub fn ident(state: &mut State) -> ParseResult<Identifier> {
|
||||
let name = peek_token!([
|
||||
TokenKind::Identifier(identifier) => {
|
||||
identifier.clone()
|
||||
},
|
||||
], state, "an identifier");
|
||||
TokenKind::Identifier(identifier) => {
|
||||
identifier.clone()
|
||||
},
|
||||
], state, "an identifier");
|
||||
|
||||
let start = state.current.span;
|
||||
state.next();
|
||||
@ -96,16 +96,56 @@ pub fn ident_maybe_reserved(state: &mut State) -> ParseResult<Identifier> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ident_maybe_soft_reserved(state: &mut State) -> ParseResult<Identifier> {
|
||||
match state.current.kind {
|
||||
_ if is_soft_reserved_ident(&state.current.kind) => {
|
||||
let name = state.current.kind.to_string().into();
|
||||
|
||||
let start = state.current.span;
|
||||
state.next();
|
||||
let end = state.current.span;
|
||||
|
||||
Ok(Identifier { start, name, end })
|
||||
}
|
||||
_ => ident(state),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_ident_maybe_soft_reserved(kind: &TokenKind) -> bool {
|
||||
if let TokenKind::Identifier(_) = kind {
|
||||
return true;
|
||||
}
|
||||
|
||||
is_soft_reserved_ident(kind)
|
||||
}
|
||||
|
||||
pub fn is_ident_maybe_reserved(kind: &TokenKind) -> bool {
|
||||
if let TokenKind::Identifier(_) = kind {
|
||||
return true;
|
||||
}
|
||||
|
||||
is_reserved_ident(kind)
|
||||
}
|
||||
|
||||
pub fn is_soft_reserved_ident(kind: &TokenKind) -> bool {
|
||||
matches!(kind, |TokenKind::Parent| TokenKind::Self_
|
||||
| TokenKind::True
|
||||
| TokenKind::False
|
||||
| TokenKind::Null
|
||||
| TokenKind::Enum
|
||||
| TokenKind::From
|
||||
| TokenKind::Readonly)
|
||||
}
|
||||
|
||||
pub fn is_reserved_ident(kind: &TokenKind) -> bool {
|
||||
if is_soft_reserved_ident(kind) {
|
||||
return true;
|
||||
}
|
||||
|
||||
matches!(
|
||||
kind,
|
||||
TokenKind::Static
|
||||
| TokenKind::Parent
|
||||
| TokenKind::Self_
|
||||
| TokenKind::Abstract
|
||||
| TokenKind::True
|
||||
| TokenKind::False
|
||||
| TokenKind::Null
|
||||
| TokenKind::Final
|
||||
| TokenKind::For
|
||||
| TokenKind::Private
|
||||
@ -180,7 +220,5 @@ pub fn is_reserved_ident(kind: &TokenKind) -> bool {
|
||||
| TokenKind::HaltCompiler
|
||||
| TokenKind::Fn
|
||||
| TokenKind::Match
|
||||
| TokenKind::Enum
|
||||
| TokenKind::From
|
||||
)
|
||||
}
|
||||
|
@ -247,8 +247,7 @@ pub fn args_list(state: &mut State) -> ParseResult<Vec<Arg>> {
|
||||
while !state.is_eof() && state.current.kind != TokenKind::RightParen {
|
||||
let mut name = None;
|
||||
let mut unpack = false;
|
||||
if (matches!(state.current.kind, TokenKind::Identifier(_))
|
||||
|| identifiers::is_reserved_ident(&state.current.kind))
|
||||
if identifiers::is_ident_maybe_reserved(&state.current.kind)
|
||||
&& state.peek.kind == TokenKind::Colon
|
||||
{
|
||||
name = Some(identifiers::ident_maybe_reserved(state)?);
|
||||
|
@ -124,30 +124,31 @@ fn statement(state: &mut State) -> ParseResult<Statement> {
|
||||
let statement = if has_attributes {
|
||||
match &state.current.kind {
|
||||
TokenKind::Abstract => classish::class_definition(state)?,
|
||||
TokenKind::Readonly => classish::class_definition(state)?,
|
||||
TokenKind::Readonly if state.peek.kind != TokenKind::LeftParen => {
|
||||
classish::class_definition(state)?
|
||||
}
|
||||
TokenKind::Final => classish::class_definition(state)?,
|
||||
TokenKind::Class => classish::class_definition(state)?,
|
||||
TokenKind::Interface => classish::interface_definition(state)?,
|
||||
TokenKind::Trait => classish::trait_definition(state)?,
|
||||
TokenKind::Enum => classish::enum_definition(state)?,
|
||||
TokenKind::Enum if state.peek.kind != TokenKind::LeftParen => {
|
||||
classish::enum_definition(state)?
|
||||
}
|
||||
TokenKind::Function
|
||||
if matches!(
|
||||
state.peek.kind,
|
||||
TokenKind::Identifier(_) | TokenKind::Null | TokenKind::Ampersand
|
||||
) =>
|
||||
if identifiers::is_ident_maybe_soft_reserved(&state.peek.kind)
|
||||
|| state.peek.kind == TokenKind::Ampersand =>
|
||||
{
|
||||
// FIXME: This is incredibly hacky but we don't have a way to look at
|
||||
// the next N tokens right now. We could probably do with a `peek_buf()`
|
||||
// method like the Lexer has.
|
||||
if state.peek.kind == TokenKind::Ampersand {
|
||||
let mut cloned = state.iter.clone();
|
||||
if let Some((index, _)) = state.iter.clone().enumerate().next() {
|
||||
if let Some((_, token)) = state.iter.clone().enumerate().next() {
|
||||
if !matches!(
|
||||
cloned.nth(index),
|
||||
Some(Token {
|
||||
token,
|
||||
Token {
|
||||
kind: TokenKind::Identifier(_),
|
||||
..
|
||||
})
|
||||
}
|
||||
) {
|
||||
let expr = expressions::lowest_precedence(state)?;
|
||||
|
||||
@ -173,17 +174,19 @@ fn statement(state: &mut State) -> ParseResult<Statement> {
|
||||
} else {
|
||||
match &state.current.kind {
|
||||
TokenKind::Abstract => classish::class_definition(state)?,
|
||||
TokenKind::Readonly => classish::class_definition(state)?,
|
||||
TokenKind::Readonly if state.peek.kind != TokenKind::LeftParen => {
|
||||
classish::class_definition(state)?
|
||||
}
|
||||
TokenKind::Final => classish::class_definition(state)?,
|
||||
TokenKind::Class => classish::class_definition(state)?,
|
||||
TokenKind::Interface => classish::interface_definition(state)?,
|
||||
TokenKind::Trait => classish::trait_definition(state)?,
|
||||
TokenKind::Enum => classish::enum_definition(state)?,
|
||||
TokenKind::Enum if state.peek.kind != TokenKind::LeftParen => {
|
||||
classish::enum_definition(state)?
|
||||
}
|
||||
TokenKind::Function
|
||||
if matches!(
|
||||
state.peek.kind,
|
||||
TokenKind::Identifier(_) | TokenKind::Null | TokenKind::Ampersand
|
||||
) =>
|
||||
if identifiers::is_ident_maybe_soft_reserved(&state.peek.kind)
|
||||
|| state.peek.kind == TokenKind::Ampersand =>
|
||||
{
|
||||
// FIXME: This is incredibly hacky but we don't have a way to look at
|
||||
// the next N tokens right now. We could probably do with a `peek_buf()`
|
||||
|
466
tests/fixtures/0278/ast.txt
vendored
Normal file
466
tests/fixtures/0278/ast.txt
vendored
Normal file
@ -0,0 +1,466 @@
|
||||
[
|
||||
Function(
|
||||
Function {
|
||||
start: (
|
||||
3,
|
||||
1,
|
||||
),
|
||||
end: (
|
||||
3,
|
||||
24,
|
||||
),
|
||||
name: Identifier {
|
||||
start: (
|
||||
3,
|
||||
10,
|
||||
),
|
||||
name: "true",
|
||||
end: (
|
||||
3,
|
||||
14,
|
||||
),
|
||||
},
|
||||
attributes: [],
|
||||
parameters: FunctionParameterList {
|
||||
start: (
|
||||
3,
|
||||
14,
|
||||
),
|
||||
end: (
|
||||
3,
|
||||
16,
|
||||
),
|
||||
members: [],
|
||||
},
|
||||
return_type: Some(
|
||||
Void,
|
||||
),
|
||||
by_ref: false,
|
||||
body: [],
|
||||
},
|
||||
),
|
||||
Function(
|
||||
Function {
|
||||
start: (
|
||||
4,
|
||||
1,
|
||||
),
|
||||
end: (
|
||||
4,
|
||||
25,
|
||||
),
|
||||
name: Identifier {
|
||||
start: (
|
||||
4,
|
||||
10,
|
||||
),
|
||||
name: "false",
|
||||
end: (
|
||||
4,
|
||||
15,
|
||||
),
|
||||
},
|
||||
attributes: [],
|
||||
parameters: FunctionParameterList {
|
||||
start: (
|
||||
4,
|
||||
15,
|
||||
),
|
||||
end: (
|
||||
4,
|
||||
17,
|
||||
),
|
||||
members: [],
|
||||
},
|
||||
return_type: Some(
|
||||
Void,
|
||||
),
|
||||
by_ref: false,
|
||||
body: [],
|
||||
},
|
||||
),
|
||||
Function(
|
||||
Function {
|
||||
start: (
|
||||
5,
|
||||
1,
|
||||
),
|
||||
end: (
|
||||
5,
|
||||
24,
|
||||
),
|
||||
name: Identifier {
|
||||
start: (
|
||||
5,
|
||||
10,
|
||||
),
|
||||
name: "null",
|
||||
end: (
|
||||
5,
|
||||
14,
|
||||
),
|
||||
},
|
||||
attributes: [],
|
||||
parameters: FunctionParameterList {
|
||||
start: (
|
||||
5,
|
||||
14,
|
||||
),
|
||||
end: (
|
||||
5,
|
||||
16,
|
||||
),
|
||||
members: [],
|
||||
},
|
||||
return_type: Some(
|
||||
Void,
|
||||
),
|
||||
by_ref: false,
|
||||
body: [],
|
||||
},
|
||||
),
|
||||
Function(
|
||||
Function {
|
||||
start: (
|
||||
6,
|
||||
1,
|
||||
),
|
||||
end: (
|
||||
6,
|
||||
28,
|
||||
),
|
||||
name: Identifier {
|
||||
start: (
|
||||
6,
|
||||
10,
|
||||
),
|
||||
name: "readonly",
|
||||
end: (
|
||||
6,
|
||||
18,
|
||||
),
|
||||
},
|
||||
attributes: [],
|
||||
parameters: FunctionParameterList {
|
||||
start: (
|
||||
6,
|
||||
18,
|
||||
),
|
||||
end: (
|
||||
6,
|
||||
20,
|
||||
),
|
||||
members: [],
|
||||
},
|
||||
return_type: Some(
|
||||
Void,
|
||||
),
|
||||
by_ref: false,
|
||||
body: [],
|
||||
},
|
||||
),
|
||||
Function(
|
||||
Function {
|
||||
start: (
|
||||
7,
|
||||
1,
|
||||
),
|
||||
end: (
|
||||
7,
|
||||
24,
|
||||
),
|
||||
name: Identifier {
|
||||
start: (
|
||||
7,
|
||||
10,
|
||||
),
|
||||
name: "self",
|
||||
end: (
|
||||
7,
|
||||
14,
|
||||
),
|
||||
},
|
||||
attributes: [],
|
||||
parameters: FunctionParameterList {
|
||||
start: (
|
||||
7,
|
||||
14,
|
||||
),
|
||||
end: (
|
||||
7,
|
||||
16,
|
||||
),
|
||||
members: [],
|
||||
},
|
||||
return_type: Some(
|
||||
Void,
|
||||
),
|
||||
by_ref: false,
|
||||
body: [],
|
||||
},
|
||||
),
|
||||
Function(
|
||||
Function {
|
||||
start: (
|
||||
8,
|
||||
1,
|
||||
),
|
||||
end: (
|
||||
8,
|
||||
26,
|
||||
),
|
||||
name: Identifier {
|
||||
start: (
|
||||
8,
|
||||
10,
|
||||
),
|
||||
name: "parent",
|
||||
end: (
|
||||
8,
|
||||
16,
|
||||
),
|
||||
},
|
||||
attributes: [],
|
||||
parameters: FunctionParameterList {
|
||||
start: (
|
||||
8,
|
||||
16,
|
||||
),
|
||||
end: (
|
||||
8,
|
||||
18,
|
||||
),
|
||||
members: [],
|
||||
},
|
||||
return_type: Some(
|
||||
Void,
|
||||
),
|
||||
by_ref: false,
|
||||
body: [],
|
||||
},
|
||||
),
|
||||
Function(
|
||||
Function {
|
||||
start: (
|
||||
9,
|
||||
1,
|
||||
),
|
||||
end: (
|
||||
9,
|
||||
24,
|
||||
),
|
||||
name: Identifier {
|
||||
start: (
|
||||
9,
|
||||
10,
|
||||
),
|
||||
name: "enum",
|
||||
end: (
|
||||
9,
|
||||
14,
|
||||
),
|
||||
},
|
||||
attributes: [],
|
||||
parameters: FunctionParameterList {
|
||||
start: (
|
||||
9,
|
||||
14,
|
||||
),
|
||||
end: (
|
||||
9,
|
||||
16,
|
||||
),
|
||||
members: [],
|
||||
},
|
||||
return_type: Some(
|
||||
Void,
|
||||
),
|
||||
by_ref: false,
|
||||
body: [],
|
||||
},
|
||||
),
|
||||
Function(
|
||||
Function {
|
||||
start: (
|
||||
10,
|
||||
1,
|
||||
),
|
||||
end: (
|
||||
10,
|
||||
24,
|
||||
),
|
||||
name: Identifier {
|
||||
start: (
|
||||
10,
|
||||
10,
|
||||
),
|
||||
name: "from",
|
||||
end: (
|
||||
10,
|
||||
14,
|
||||
),
|
||||
},
|
||||
attributes: [],
|
||||
parameters: FunctionParameterList {
|
||||
start: (
|
||||
10,
|
||||
14,
|
||||
),
|
||||
end: (
|
||||
10,
|
||||
16,
|
||||
),
|
||||
members: [],
|
||||
},
|
||||
return_type: Some(
|
||||
Void,
|
||||
),
|
||||
by_ref: false,
|
||||
body: [],
|
||||
},
|
||||
),
|
||||
Expression {
|
||||
expr: Call {
|
||||
target: Identifier(
|
||||
Identifier {
|
||||
start: (
|
||||
12,
|
||||
1,
|
||||
),
|
||||
name: "true",
|
||||
end: (
|
||||
12,
|
||||
5,
|
||||
),
|
||||
},
|
||||
),
|
||||
args: [],
|
||||
},
|
||||
},
|
||||
Expression {
|
||||
expr: Call {
|
||||
target: Identifier(
|
||||
Identifier {
|
||||
start: (
|
||||
13,
|
||||
1,
|
||||
),
|
||||
name: "false",
|
||||
end: (
|
||||
13,
|
||||
6,
|
||||
),
|
||||
},
|
||||
),
|
||||
args: [],
|
||||
},
|
||||
},
|
||||
Expression {
|
||||
expr: Call {
|
||||
target: Identifier(
|
||||
Identifier {
|
||||
start: (
|
||||
14,
|
||||
1,
|
||||
),
|
||||
name: "null",
|
||||
end: (
|
||||
14,
|
||||
5,
|
||||
),
|
||||
},
|
||||
),
|
||||
args: [],
|
||||
},
|
||||
},
|
||||
Expression {
|
||||
expr: Call {
|
||||
target: Identifier(
|
||||
Identifier {
|
||||
start: (
|
||||
15,
|
||||
1,
|
||||
),
|
||||
name: "readonly",
|
||||
end: (
|
||||
15,
|
||||
9,
|
||||
),
|
||||
},
|
||||
),
|
||||
args: [],
|
||||
},
|
||||
},
|
||||
Expression {
|
||||
expr: Call {
|
||||
target: Identifier(
|
||||
Identifier {
|
||||
start: (
|
||||
16,
|
||||
1,
|
||||
),
|
||||
name: "self",
|
||||
end: (
|
||||
16,
|
||||
5,
|
||||
),
|
||||
},
|
||||
),
|
||||
args: [],
|
||||
},
|
||||
},
|
||||
Expression {
|
||||
expr: Call {
|
||||
target: Identifier(
|
||||
Identifier {
|
||||
start: (
|
||||
17,
|
||||
1,
|
||||
),
|
||||
name: "parent",
|
||||
end: (
|
||||
17,
|
||||
7,
|
||||
),
|
||||
},
|
||||
),
|
||||
args: [],
|
||||
},
|
||||
},
|
||||
Expression {
|
||||
expr: Call {
|
||||
target: Identifier(
|
||||
Identifier {
|
||||
start: (
|
||||
18,
|
||||
1,
|
||||
),
|
||||
name: "enum",
|
||||
end: (
|
||||
18,
|
||||
5,
|
||||
),
|
||||
},
|
||||
),
|
||||
args: [],
|
||||
},
|
||||
},
|
||||
Expression {
|
||||
expr: Call {
|
||||
target: Identifier(
|
||||
Identifier {
|
||||
start: (
|
||||
19,
|
||||
1,
|
||||
),
|
||||
name: "from",
|
||||
end: (
|
||||
19,
|
||||
5,
|
||||
),
|
||||
},
|
||||
),
|
||||
args: [],
|
||||
},
|
||||
},
|
||||
]
|
19
tests/fixtures/0278/code.php
vendored
Normal file
19
tests/fixtures/0278/code.php
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
function true(): void {}
|
||||
function false(): void {}
|
||||
function null(): void {}
|
||||
function readonly(): void {}
|
||||
function self(): void {}
|
||||
function parent(): void {}
|
||||
function enum(): void {}
|
||||
function from(): void {}
|
||||
|
||||
true();
|
||||
false();
|
||||
null();
|
||||
readonly();
|
||||
self();
|
||||
parent();
|
||||
enum();
|
||||
from();
|
Loading…
Reference in New Issue
Block a user