mirror of
https://github.com/danog/parser.git
synced 2024-12-02 09:27:50 +01:00
chore: refactor expression parser to avoid stack overflow issue (#177)
Signed-off-by: azjezz <azjezz@protonmail.com>
This commit is contained in:
parent
ede98233c5
commit
e74d0ec18e
@ -424,14 +424,28 @@ impl Lexer {
|
|||||||
state.source.next();
|
state.source.next();
|
||||||
DocStringKind::Nowdoc
|
DocStringKind::Nowdoc
|
||||||
}
|
}
|
||||||
_ => DocStringKind::Heredoc,
|
[b'"'] => {
|
||||||
|
state.source.next();
|
||||||
|
DocStringKind::Heredoc
|
||||||
|
}
|
||||||
|
[_, ..] => DocStringKind::Heredoc,
|
||||||
|
[] => {
|
||||||
|
return Err(SyntaxError::UnexpectedEndOfFile(state.source.span()));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// FIXME: Add support for nowdocs too by checking if a `'`
|
// FIXME: Add support for nowdocs too by checking if a `'`
|
||||||
// character is present before and after the identifier.
|
// character is present before and after the identifier.
|
||||||
let label: ByteString = match self.peek_identifier(state) {
|
let label: ByteString = match self.peek_identifier(state) {
|
||||||
Some(_) => self.consume_identifier(state).into(),
|
Some(_) => self.consume_identifier(state).into(),
|
||||||
None => unreachable!(),
|
None => match state.source.current() {
|
||||||
|
Some(c) => {
|
||||||
|
return Err(SyntaxError::UnexpectedCharacter(*c, state.source.span()))
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
return Err(SyntaxError::UnexpectedEndOfFile(state.source.span()));
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
if doc_string_kind == DocStringKind::Nowdoc {
|
if doc_string_kind == DocStringKind::Nowdoc {
|
||||||
@ -445,6 +459,8 @@ impl Lexer {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
} else if let Some(b'"') = state.source.current() {
|
||||||
|
state.source.next();
|
||||||
}
|
}
|
||||||
|
|
||||||
if !matches!(state.source.current(), Some(b'\n')) {
|
if !matches!(state.source.current(), Some(b'\n')) {
|
||||||
|
1153
src/parser/expressions.rs
Normal file
1153
src/parser/expressions.rs
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,11 +1,10 @@
|
|||||||
use crate::lexer::token::TokenKind;
|
use crate::lexer::token::TokenKind;
|
||||||
use crate::parser;
|
|
||||||
use crate::parser::ast::ArrayItem;
|
use crate::parser::ast::ArrayItem;
|
||||||
use crate::parser::ast::Expression;
|
use crate::parser::ast::Expression;
|
||||||
use crate::parser::ast::ListItem;
|
use crate::parser::ast::ListItem;
|
||||||
use crate::parser::error::ParseError;
|
use crate::parser::error::ParseError;
|
||||||
use crate::parser::error::ParseResult;
|
use crate::parser::error::ParseResult;
|
||||||
use crate::parser::internal::precedences::Precedence;
|
use crate::parser::expressions;
|
||||||
use crate::parser::internal::utils;
|
use crate::parser::internal::utils;
|
||||||
use crate::parser::state::State;
|
use crate::parser::state::State;
|
||||||
|
|
||||||
@ -38,7 +37,7 @@ pub fn list_expression(state: &mut State) -> ParseResult<Expression> {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut value = parser::expression(state, Precedence::Lowest)?;
|
let mut value = expressions::lowest_precedence(state)?;
|
||||||
|
|
||||||
if state.current.kind == TokenKind::DoubleArrow {
|
if state.current.kind == TokenKind::DoubleArrow {
|
||||||
if !has_atleast_one_key && !items.is_empty() {
|
if !has_atleast_one_key && !items.is_empty() {
|
||||||
@ -62,7 +61,7 @@ pub fn list_expression(state: &mut State) -> ParseResult<Expression> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
has_atleast_one_key = true;
|
has_atleast_one_key = true;
|
||||||
value = parser::expression(state, Precedence::Lowest)?;
|
value = expressions::lowest_precedence(state)?;
|
||||||
} else if has_atleast_one_key {
|
} else if has_atleast_one_key {
|
||||||
return Err(ParseError::CannotMixKeyedAndUnkeyedEntries(
|
return Err(ParseError::CannotMixKeyedAndUnkeyedEntries(
|
||||||
state.current.span,
|
state.current.span,
|
||||||
@ -148,7 +147,7 @@ pub fn legacy_array_expression(state: &mut State) -> ParseResult<Expression> {
|
|||||||
(false, (0, 0))
|
(false, (0, 0))
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut value = parser::expression(state, Precedence::Lowest)?;
|
let mut value = expressions::lowest_precedence(state)?;
|
||||||
|
|
||||||
// TODO: return error for `[...$a => $b]`.
|
// TODO: return error for `[...$a => $b]`.
|
||||||
if state.current.kind == TokenKind::DoubleArrow {
|
if state.current.kind == TokenKind::DoubleArrow {
|
||||||
@ -170,7 +169,7 @@ pub fn legacy_array_expression(state: &mut State) -> ParseResult<Expression> {
|
|||||||
false
|
false
|
||||||
};
|
};
|
||||||
|
|
||||||
value = parser::expression(state, Precedence::Lowest)?;
|
value = expressions::lowest_precedence(state)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
items.push(ArrayItem {
|
items.push(ArrayItem {
|
||||||
@ -211,7 +210,7 @@ fn array_pair(state: &mut State) -> ParseResult<ArrayItem> {
|
|||||||
(false, (0, 0))
|
(false, (0, 0))
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut value = parser::expression(state, Precedence::Lowest)?;
|
let mut value = expressions::lowest_precedence(state)?;
|
||||||
if state.current.kind == TokenKind::DoubleArrow {
|
if state.current.kind == TokenKind::DoubleArrow {
|
||||||
state.next();
|
state.next();
|
||||||
|
|
||||||
@ -229,7 +228,7 @@ fn array_pair(state: &mut State) -> ParseResult<ArrayItem> {
|
|||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
};
|
};
|
||||||
value = parser::expression(state, Precedence::Lowest)?;
|
value = expressions::lowest_precedence(state)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(ArrayItem {
|
Ok(ArrayItem {
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
use crate::lexer::token::TokenKind;
|
use crate::lexer::token::TokenKind;
|
||||||
use crate::parser;
|
|
||||||
use crate::parser::ast::attributes::Attribute;
|
use crate::parser::ast::attributes::Attribute;
|
||||||
use crate::parser::ast::attributes::AttributeGroup;
|
use crate::parser::ast::attributes::AttributeGroup;
|
||||||
use crate::parser::error::ParseResult;
|
use crate::parser::error::ParseResult;
|
||||||
use crate::parser::internal::precedences::Precedence;
|
use crate::parser::expressions;
|
||||||
use crate::parser::internal::utils;
|
use crate::parser::internal::utils;
|
||||||
use crate::parser::state::State;
|
use crate::parser::state::State;
|
||||||
|
|
||||||
@ -21,7 +20,7 @@ pub fn gather_attributes(state: &mut State) -> ParseResult<bool> {
|
|||||||
|
|
||||||
while state.current.kind != TokenKind::RightBracket {
|
while state.current.kind != TokenKind::RightBracket {
|
||||||
let start = state.current.span;
|
let start = state.current.span;
|
||||||
let expression = parser::expression(state, Precedence::Lowest)?;
|
let expression = expressions::lowest_precedence(state)?;
|
||||||
let end = state.current.span;
|
let end = state.current.span;
|
||||||
|
|
||||||
members.push(Attribute {
|
members.push(Attribute {
|
||||||
|
@ -2,7 +2,6 @@ use crate::expect_token;
|
|||||||
use crate::expected_scope;
|
use crate::expected_scope;
|
||||||
use crate::lexer::token::Span;
|
use crate::lexer::token::Span;
|
||||||
use crate::lexer::token::TokenKind;
|
use crate::lexer::token::TokenKind;
|
||||||
use crate::parser;
|
|
||||||
use crate::parser::ast::classish::ClassishConstant;
|
use crate::parser::ast::classish::ClassishConstant;
|
||||||
use crate::parser::ast::enums::BackedEnumCase;
|
use crate::parser::ast::enums::BackedEnumCase;
|
||||||
use crate::parser::ast::enums::BackedEnumMember;
|
use crate::parser::ast::enums::BackedEnumMember;
|
||||||
@ -15,12 +14,12 @@ use crate::parser::ast::Statement;
|
|||||||
use crate::parser::ast::TraitAdaptation;
|
use crate::parser::ast::TraitAdaptation;
|
||||||
use crate::parser::error::ParseError;
|
use crate::parser::error::ParseError;
|
||||||
use crate::parser::error::ParseResult;
|
use crate::parser::error::ParseResult;
|
||||||
|
use crate::parser::expressions;
|
||||||
use crate::parser::internal::attributes;
|
use crate::parser::internal::attributes;
|
||||||
use crate::parser::internal::data_type;
|
use crate::parser::internal::data_type;
|
||||||
use crate::parser::internal::functions;
|
use crate::parser::internal::functions;
|
||||||
use crate::parser::internal::identifiers;
|
use crate::parser::internal::identifiers;
|
||||||
use crate::parser::internal::modifiers;
|
use crate::parser::internal::modifiers;
|
||||||
use crate::parser::internal::precedences::Precedence;
|
|
||||||
use crate::parser::internal::utils;
|
use crate::parser::internal::utils;
|
||||||
use crate::parser::state::Scope;
|
use crate::parser::state::Scope;
|
||||||
use crate::parser::state::State;
|
use crate::parser::state::State;
|
||||||
@ -115,7 +114,7 @@ pub fn backed_enum_member(state: &mut State) -> ParseResult<BackedEnumMember> {
|
|||||||
|
|
||||||
utils::skip(state, TokenKind::Equals)?;
|
utils::skip(state, TokenKind::Equals)?;
|
||||||
|
|
||||||
let value = parser::expression(state, Precedence::Lowest)?;
|
let value = expressions::lowest_precedence(state)?;
|
||||||
|
|
||||||
let end = utils::skip_semicolon(state)?;
|
let end = utils::skip_semicolon(state)?;
|
||||||
|
|
||||||
@ -152,8 +151,7 @@ pub fn class_like_statement(state: &mut State) -> ParseResult<Statement> {
|
|||||||
let start = state.current.span;
|
let start = state.current.span;
|
||||||
let modifiers = modifiers::collect(state)?;
|
let modifiers = modifiers::collect(state)?;
|
||||||
|
|
||||||
if !has_attributes {
|
if !has_attributes && state.current.kind == TokenKind::Use {
|
||||||
if state.current.kind == TokenKind::Use {
|
|
||||||
return parse_classish_uses(state);
|
return parse_classish_uses(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,7 +162,6 @@ pub fn class_like_statement(state: &mut State) -> ParseResult<Statement> {
|
|||||||
start,
|
start,
|
||||||
)?));
|
)?));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if state.current.kind == TokenKind::Function {
|
if state.current.kind == TokenKind::Function {
|
||||||
return Ok(Statement::Method(functions::method(
|
return Ok(Statement::Method(functions::method(
|
||||||
@ -185,7 +182,7 @@ pub fn class_like_statement(state: &mut State) -> ParseResult<Statement> {
|
|||||||
// e.g: = "foo";
|
// e.g: = "foo";
|
||||||
if state.current.kind == TokenKind::Equals {
|
if state.current.kind == TokenKind::Equals {
|
||||||
state.next();
|
state.next();
|
||||||
value = Some(parser::expression(state, Precedence::Lowest)?);
|
value = Some(expressions::lowest_precedence(state)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
let class_name: String = expected_scope!([
|
let class_name: String = expected_scope!([
|
||||||
@ -395,7 +392,7 @@ fn constant(
|
|||||||
|
|
||||||
utils::skip(state, TokenKind::Equals)?;
|
utils::skip(state, TokenKind::Equals)?;
|
||||||
|
|
||||||
let value = parser::expression(state, Precedence::Lowest)?;
|
let value = expressions::lowest_precedence(state)?;
|
||||||
|
|
||||||
let end = utils::skip_semicolon(state)?;
|
let end = utils::skip_semicolon(state)?;
|
||||||
|
|
||||||
|
@ -10,8 +10,8 @@ use crate::parser::ast::MatchArm;
|
|||||||
use crate::parser::ast::Statement;
|
use crate::parser::ast::Statement;
|
||||||
use crate::parser::error::ParseError;
|
use crate::parser::error::ParseError;
|
||||||
use crate::parser::error::ParseResult;
|
use crate::parser::error::ParseResult;
|
||||||
|
use crate::parser::expressions;
|
||||||
use crate::parser::internal::blocks;
|
use crate::parser::internal::blocks;
|
||||||
use crate::parser::internal::precedences::Precedence;
|
|
||||||
use crate::parser::internal::utils;
|
use crate::parser::internal::utils;
|
||||||
use crate::parser::state::State;
|
use crate::parser::state::State;
|
||||||
|
|
||||||
@ -20,7 +20,7 @@ pub fn match_expression(state: &mut State) -> ParseResult<Expression> {
|
|||||||
|
|
||||||
utils::skip_left_parenthesis(state)?;
|
utils::skip_left_parenthesis(state)?;
|
||||||
|
|
||||||
let condition = Box::new(parser::expression(state, Precedence::Lowest)?);
|
let condition = Box::new(expressions::lowest_precedence(state)?);
|
||||||
|
|
||||||
utils::skip_right_parenthesis(state)?;
|
utils::skip_right_parenthesis(state)?;
|
||||||
utils::skip_left_brace(state)?;
|
utils::skip_left_brace(state)?;
|
||||||
@ -46,13 +46,13 @@ pub fn match_expression(state: &mut State) -> ParseResult<Expression> {
|
|||||||
|
|
||||||
utils::skip_double_arrow(state)?;
|
utils::skip_double_arrow(state)?;
|
||||||
|
|
||||||
let body = parser::expression(state, Precedence::Lowest)?;
|
let body = expressions::lowest_precedence(state)?;
|
||||||
|
|
||||||
default = Some(Box::new(DefaultMatchArm { body }));
|
default = Some(Box::new(DefaultMatchArm { body }));
|
||||||
} else {
|
} else {
|
||||||
let mut conditions = Vec::new();
|
let mut conditions = Vec::new();
|
||||||
while state.current.kind != TokenKind::DoubleArrow {
|
while state.current.kind != TokenKind::DoubleArrow {
|
||||||
conditions.push(parser::expression(state, Precedence::Lowest)?);
|
conditions.push(expressions::lowest_precedence(state)?);
|
||||||
|
|
||||||
if state.current.kind == TokenKind::Comma {
|
if state.current.kind == TokenKind::Comma {
|
||||||
state.next();
|
state.next();
|
||||||
@ -67,7 +67,7 @@ pub fn match_expression(state: &mut State) -> ParseResult<Expression> {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
let body = parser::expression(state, Precedence::Lowest)?;
|
let body = expressions::lowest_precedence(state)?;
|
||||||
|
|
||||||
arms.push(MatchArm { conditions, body });
|
arms.push(MatchArm { conditions, body });
|
||||||
}
|
}
|
||||||
@ -93,7 +93,7 @@ pub fn switch_statement(state: &mut State) -> ParseResult<Statement> {
|
|||||||
|
|
||||||
utils::skip_left_parenthesis(state)?;
|
utils::skip_left_parenthesis(state)?;
|
||||||
|
|
||||||
let condition = parser::expression(state, Precedence::Lowest)?;
|
let condition = expressions::lowest_precedence(state)?;
|
||||||
|
|
||||||
utils::skip_right_parenthesis(state)?;
|
utils::skip_right_parenthesis(state)?;
|
||||||
|
|
||||||
@ -111,7 +111,7 @@ pub fn switch_statement(state: &mut State) -> ParseResult<Statement> {
|
|||||||
TokenKind::Case => {
|
TokenKind::Case => {
|
||||||
state.next();
|
state.next();
|
||||||
|
|
||||||
let condition = parser::expression(state, Precedence::Lowest)?;
|
let condition = expressions::lowest_precedence(state)?;
|
||||||
|
|
||||||
utils::skip_any_of(state, &[TokenKind::Colon, TokenKind::SemiColon])?;
|
utils::skip_any_of(state, &[TokenKind::Colon, TokenKind::SemiColon])?;
|
||||||
|
|
||||||
@ -170,7 +170,7 @@ pub fn if_statement(state: &mut State) -> ParseResult<Statement> {
|
|||||||
|
|
||||||
utils::skip_left_parenthesis(state)?;
|
utils::skip_left_parenthesis(state)?;
|
||||||
|
|
||||||
let condition = parser::expression(state, Precedence::Lowest)?;
|
let condition = expressions::lowest_precedence(state)?;
|
||||||
|
|
||||||
utils::skip_right_parenthesis(state)?;
|
utils::skip_right_parenthesis(state)?;
|
||||||
|
|
||||||
@ -201,7 +201,7 @@ pub fn if_statement(state: &mut State) -> ParseResult<Statement> {
|
|||||||
state.next();
|
state.next();
|
||||||
|
|
||||||
utils::skip_left_parenthesis(state)?;
|
utils::skip_left_parenthesis(state)?;
|
||||||
let condition = parser::expression(state, Precedence::Lowest)?;
|
let condition = expressions::lowest_precedence(state)?;
|
||||||
utils::skip_right_parenthesis(state)?;
|
utils::skip_right_parenthesis(state)?;
|
||||||
|
|
||||||
utils::skip_colon(state)?;
|
utils::skip_colon(state)?;
|
||||||
@ -260,7 +260,7 @@ pub fn if_statement(state: &mut State) -> ParseResult<Statement> {
|
|||||||
|
|
||||||
utils::skip_left_parenthesis(state)?;
|
utils::skip_left_parenthesis(state)?;
|
||||||
|
|
||||||
let condition = parser::expression(state, Precedence::Lowest)?;
|
let condition = expressions::lowest_precedence(state)?;
|
||||||
|
|
||||||
utils::skip_right_parenthesis(state)?;
|
utils::skip_right_parenthesis(state)?;
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
use crate::expected_scope;
|
use crate::expected_scope;
|
||||||
use crate::lexer::token::Span;
|
use crate::lexer::token::Span;
|
||||||
use crate::lexer::token::TokenKind;
|
use crate::lexer::token::TokenKind;
|
||||||
use crate::parser;
|
|
||||||
use crate::parser::ast::functions::ArrowFunction;
|
use crate::parser::ast::functions::ArrowFunction;
|
||||||
use crate::parser::ast::functions::Closure;
|
use crate::parser::ast::functions::Closure;
|
||||||
use crate::parser::ast::functions::ClosureUse;
|
use crate::parser::ast::functions::ClosureUse;
|
||||||
@ -13,11 +12,11 @@ use crate::parser::ast::Expression;
|
|||||||
use crate::parser::ast::Statement;
|
use crate::parser::ast::Statement;
|
||||||
use crate::parser::error::ParseError;
|
use crate::parser::error::ParseError;
|
||||||
use crate::parser::error::ParseResult;
|
use crate::parser::error::ParseResult;
|
||||||
|
use crate::parser::expressions;
|
||||||
use crate::parser::internal::blocks;
|
use crate::parser::internal::blocks;
|
||||||
use crate::parser::internal::data_type;
|
use crate::parser::internal::data_type;
|
||||||
use crate::parser::internal::identifiers;
|
use crate::parser::internal::identifiers;
|
||||||
use crate::parser::internal::parameters;
|
use crate::parser::internal::parameters;
|
||||||
use crate::parser::internal::precedences::Precedence;
|
|
||||||
use crate::parser::internal::utils;
|
use crate::parser::internal::utils;
|
||||||
use crate::parser::state::Scope;
|
use crate::parser::state::Scope;
|
||||||
use crate::parser::state::State;
|
use crate::parser::state::State;
|
||||||
@ -62,7 +61,7 @@ pub fn anonymous_function(state: &mut State) -> ParseResult<Expression> {
|
|||||||
|
|
||||||
// TODO(azjezz): this shouldn't call expr, we should have a function
|
// TODO(azjezz): this shouldn't call expr, we should have a function
|
||||||
// just for variables, so we don't have to go through the whole `match` in `expression(...)`
|
// just for variables, so we don't have to go through the whole `match` in `expression(...)`
|
||||||
let var = match parser::expression(state, Precedence::Lowest)? {
|
let var = match expressions::lowest_precedence(state)? {
|
||||||
s @ Expression::Variable { .. } => ClosureUse { var: s, by_ref },
|
s @ Expression::Variable { .. } => ClosureUse { var: s, by_ref },
|
||||||
_ => {
|
_ => {
|
||||||
return Err(ParseError::UnexpectedToken(
|
return Err(ParseError::UnexpectedToken(
|
||||||
@ -146,7 +145,7 @@ pub fn arrow_function(state: &mut State) -> ParseResult<Expression> {
|
|||||||
utils::skip(state, TokenKind::DoubleArrow)?;
|
utils::skip(state, TokenKind::DoubleArrow)?;
|
||||||
|
|
||||||
let body = scoped!(state, Scope::ArrowFunction(is_static), {
|
let body = scoped!(state, Scope::ArrowFunction(is_static), {
|
||||||
Box::new(parser::expression(state, Precedence::Lowest)?)
|
Box::new(expressions::lowest_precedence(state)?)
|
||||||
});
|
});
|
||||||
|
|
||||||
let end = state.current.span;
|
let end = state.current.span;
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
use crate::lexer::token::TokenKind;
|
use crate::lexer::token::TokenKind;
|
||||||
use crate::parser;
|
|
||||||
use crate::parser::ast::Statement;
|
use crate::parser::ast::Statement;
|
||||||
use crate::parser::error::ParseResult;
|
use crate::parser::error::ParseResult;
|
||||||
|
use crate::parser::expressions;
|
||||||
use crate::parser::internal::blocks;
|
use crate::parser::internal::blocks;
|
||||||
use crate::parser::internal::precedences::Precedence;
|
|
||||||
use crate::parser::internal::utils;
|
use crate::parser::internal::utils;
|
||||||
use crate::parser::state::State;
|
use crate::parser::state::State;
|
||||||
|
|
||||||
@ -12,7 +11,7 @@ pub fn foreach_loop(state: &mut State) -> ParseResult<Statement> {
|
|||||||
|
|
||||||
utils::skip_left_parenthesis(state)?;
|
utils::skip_left_parenthesis(state)?;
|
||||||
|
|
||||||
let expr = parser::expression(state, Precedence::Lowest)?;
|
let expr = expressions::lowest_precedence(state)?;
|
||||||
|
|
||||||
utils::skip(state, TokenKind::As)?;
|
utils::skip(state, TokenKind::As)?;
|
||||||
|
|
||||||
@ -22,7 +21,7 @@ pub fn foreach_loop(state: &mut State) -> ParseResult<Statement> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut key_var = None;
|
let mut key_var = None;
|
||||||
let mut value_var = parser::expression(state, Precedence::Lowest)?;
|
let mut value_var = expressions::lowest_precedence(state)?;
|
||||||
|
|
||||||
if state.current.kind == TokenKind::DoubleArrow {
|
if state.current.kind == TokenKind::DoubleArrow {
|
||||||
state.next();
|
state.next();
|
||||||
@ -34,7 +33,7 @@ pub fn foreach_loop(state: &mut State) -> ParseResult<Statement> {
|
|||||||
state.next();
|
state.next();
|
||||||
}
|
}
|
||||||
|
|
||||||
value_var = parser::expression(state, Precedence::Lowest)?;
|
value_var = expressions::lowest_precedence(state)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
utils::skip_right_parenthesis(state)?;
|
utils::skip_right_parenthesis(state)?;
|
||||||
@ -76,7 +75,7 @@ pub fn for_loop(state: &mut State) -> ParseResult<Statement> {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
init.push(parser::expression(state, Precedence::Lowest)?);
|
init.push(expressions::lowest_precedence(state)?);
|
||||||
|
|
||||||
if state.current.kind == TokenKind::Comma {
|
if state.current.kind == TokenKind::Comma {
|
||||||
state.next();
|
state.next();
|
||||||
@ -93,7 +92,7 @@ pub fn for_loop(state: &mut State) -> ParseResult<Statement> {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
condition.push(parser::expression(state, Precedence::Lowest)?);
|
condition.push(expressions::lowest_precedence(state)?);
|
||||||
|
|
||||||
if state.current.kind == TokenKind::Comma {
|
if state.current.kind == TokenKind::Comma {
|
||||||
state.next();
|
state.next();
|
||||||
@ -109,7 +108,7 @@ pub fn for_loop(state: &mut State) -> ParseResult<Statement> {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
r#loop.push(parser::expression(state, Precedence::Lowest)?);
|
r#loop.push(expressions::lowest_precedence(state)?);
|
||||||
|
|
||||||
if state.current.kind == TokenKind::Comma {
|
if state.current.kind == TokenKind::Comma {
|
||||||
state.next();
|
state.next();
|
||||||
@ -155,7 +154,7 @@ pub fn do_loop(state: &mut State) -> ParseResult<Statement> {
|
|||||||
utils::skip(state, TokenKind::While)?;
|
utils::skip(state, TokenKind::While)?;
|
||||||
|
|
||||||
utils::skip_left_parenthesis(state)?;
|
utils::skip_left_parenthesis(state)?;
|
||||||
let condition = parser::expression(state, Precedence::Lowest)?;
|
let condition = expressions::lowest_precedence(state)?;
|
||||||
utils::skip_right_parenthesis(state)?;
|
utils::skip_right_parenthesis(state)?;
|
||||||
utils::skip_semicolon(state)?;
|
utils::skip_semicolon(state)?;
|
||||||
|
|
||||||
@ -167,7 +166,7 @@ pub fn while_loop(state: &mut State) -> ParseResult<Statement> {
|
|||||||
|
|
||||||
utils::skip_left_parenthesis(state)?;
|
utils::skip_left_parenthesis(state)?;
|
||||||
|
|
||||||
let condition = parser::expression(state, Precedence::Lowest)?;
|
let condition = expressions::lowest_precedence(state)?;
|
||||||
|
|
||||||
utils::skip_right_parenthesis(state)?;
|
utils::skip_right_parenthesis(state)?;
|
||||||
|
|
||||||
@ -203,7 +202,7 @@ pub fn continue_statement(state: &mut State) -> ParseResult<Statement> {
|
|||||||
|
|
||||||
let mut num = None;
|
let mut num = None;
|
||||||
if state.current.kind != TokenKind::SemiColon {
|
if state.current.kind != TokenKind::SemiColon {
|
||||||
num = Some(parser::expression(state, Precedence::Lowest)?);
|
num = Some(expressions::lowest_precedence(state)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
utils::skip_semicolon(state)?;
|
utils::skip_semicolon(state)?;
|
||||||
@ -216,7 +215,7 @@ pub fn break_statement(state: &mut State) -> ParseResult<Statement> {
|
|||||||
|
|
||||||
let mut num = None;
|
let mut num = None;
|
||||||
if state.current.kind != TokenKind::SemiColon {
|
if state.current.kind != TokenKind::SemiColon {
|
||||||
num = Some(parser::expression(state, Precedence::Lowest)?);
|
num = Some(expressions::lowest_precedence(state)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
utils::skip_semicolon(state)?;
|
utils::skip_semicolon(state)?;
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
use super::identifiers;
|
use super::identifiers;
|
||||||
use crate::lexer::token::TokenKind;
|
use crate::lexer::token::TokenKind;
|
||||||
use crate::parser;
|
|
||||||
use crate::parser::ast::functions::FunctionParameter;
|
use crate::parser::ast::functions::FunctionParameter;
|
||||||
use crate::parser::ast::functions::FunctionParameterList;
|
use crate::parser::ast::functions::FunctionParameterList;
|
||||||
use crate::parser::ast::functions::MethodParameter;
|
use crate::parser::ast::functions::MethodParameter;
|
||||||
@ -9,10 +8,10 @@ use crate::parser::ast::Arg;
|
|||||||
use crate::parser::ast::Expression;
|
use crate::parser::ast::Expression;
|
||||||
use crate::parser::error::ParseError;
|
use crate::parser::error::ParseError;
|
||||||
use crate::parser::error::ParseResult;
|
use crate::parser::error::ParseResult;
|
||||||
|
use crate::parser::expressions;
|
||||||
use crate::parser::internal::attributes;
|
use crate::parser::internal::attributes;
|
||||||
use crate::parser::internal::data_type;
|
use crate::parser::internal::data_type;
|
||||||
use crate::parser::internal::modifiers;
|
use crate::parser::internal::modifiers;
|
||||||
use crate::parser::internal::precedences::Precedence;
|
|
||||||
use crate::parser::internal::utils;
|
use crate::parser::internal::utils;
|
||||||
use crate::parser::state::Scope;
|
use crate::parser::state::Scope;
|
||||||
use crate::parser::state::State;
|
use crate::parser::state::State;
|
||||||
@ -52,7 +51,7 @@ pub fn function_parameter_list(state: &mut State) -> Result<FunctionParameterLis
|
|||||||
let mut default = None;
|
let mut default = None;
|
||||||
if state.current.kind == TokenKind::Equals {
|
if state.current.kind == TokenKind::Equals {
|
||||||
state.next();
|
state.next();
|
||||||
default = Some(parser::expression(state, Precedence::Lowest)?);
|
default = Some(expressions::lowest_precedence(state)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
let end = state.current.span;
|
let end = state.current.span;
|
||||||
@ -202,7 +201,7 @@ pub fn method_parameter_list(state: &mut State) -> Result<MethodParameterList, P
|
|||||||
let mut default = None;
|
let mut default = None;
|
||||||
if state.current.kind == TokenKind::Equals {
|
if state.current.kind == TokenKind::Equals {
|
||||||
state.next();
|
state.next();
|
||||||
default = Some(parser::expression(state, Precedence::Lowest)?);
|
default = Some(expressions::lowest_precedence(state)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
let end = state.current.span;
|
let end = state.current.span;
|
||||||
@ -277,7 +276,7 @@ pub fn args_list(state: &mut State) -> ParseResult<Vec<Arg>> {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
let value = parser::expression(state, Precedence::Lowest)?;
|
let value = expressions::lowest_precedence(state)?;
|
||||||
|
|
||||||
args.push(Arg {
|
args.push(Arg {
|
||||||
name,
|
name,
|
||||||
|
@ -7,7 +7,7 @@ pub enum Associativity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
|
||||||
pub enum Precedence {
|
pub enum Precedence {
|
||||||
Lowest,
|
Lowest,
|
||||||
IncDec,
|
IncDec,
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
use crate::lexer::token::TokenKind;
|
use crate::lexer::token::TokenKind;
|
||||||
use crate::parser;
|
|
||||||
use crate::parser::ast::try_block::CatchBlock;
|
use crate::parser::ast::try_block::CatchBlock;
|
||||||
use crate::parser::ast::try_block::CatchType;
|
use crate::parser::ast::try_block::CatchType;
|
||||||
use crate::parser::ast::try_block::FinallyBlock;
|
use crate::parser::ast::try_block::FinallyBlock;
|
||||||
@ -7,9 +6,9 @@ use crate::parser::ast::try_block::TryBlock;
|
|||||||
use crate::parser::ast::Statement;
|
use crate::parser::ast::Statement;
|
||||||
use crate::parser::error::ParseError;
|
use crate::parser::error::ParseError;
|
||||||
use crate::parser::error::ParseResult;
|
use crate::parser::error::ParseResult;
|
||||||
|
use crate::parser::expressions;
|
||||||
use crate::parser::internal::blocks;
|
use crate::parser::internal::blocks;
|
||||||
use crate::parser::internal::identifiers;
|
use crate::parser::internal::identifiers;
|
||||||
use crate::parser::internal::precedences::Precedence;
|
|
||||||
use crate::parser::internal::utils;
|
use crate::parser::internal::utils;
|
||||||
use crate::parser::state::State;
|
use crate::parser::state::State;
|
||||||
|
|
||||||
@ -39,7 +38,7 @@ pub fn try_block(state: &mut State) -> ParseResult<Statement> {
|
|||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
// TODO(azjezz): this is a variable, no an expression?
|
// TODO(azjezz): this is a variable, no an expression?
|
||||||
Some(parser::expression(state, Precedence::Lowest)?)
|
Some(expressions::lowest_precedence(state)?)
|
||||||
};
|
};
|
||||||
|
|
||||||
utils::skip_right_parenthesis(state)?;
|
utils::skip_right_parenthesis(state)?;
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
use crate::lexer::token::TokenKind;
|
use crate::lexer::token::TokenKind;
|
||||||
use crate::parser;
|
|
||||||
use crate::parser::ast::Expression;
|
use crate::parser::ast::Expression;
|
||||||
use crate::parser::error::ParseResult;
|
use crate::parser::error::ParseResult;
|
||||||
|
use crate::parser::expressions;
|
||||||
use crate::parser::internal::identifiers;
|
use crate::parser::internal::identifiers;
|
||||||
use crate::parser::internal::precedences::Precedence;
|
|
||||||
use crate::parser::internal::utils;
|
use crate::parser::internal::utils;
|
||||||
use crate::parser::state::State;
|
use crate::parser::state::State;
|
||||||
use crate::peek_token;
|
use crate::peek_token;
|
||||||
@ -16,7 +15,7 @@ pub fn dynamic_variable(state: &mut State) -> ParseResult<Expression> {
|
|||||||
state.next();
|
state.next();
|
||||||
|
|
||||||
// TODO(azjezz): this is not an expression! it's a constant expression
|
// TODO(azjezz): this is not an expression! it's a constant expression
|
||||||
let name = parser::expression(state, Precedence::Lowest)?;
|
let name = expressions::lowest_precedence(state)?;
|
||||||
|
|
||||||
utils::skip_right_brace(state)?;
|
utils::skip_right_brace(state)?;
|
||||||
|
|
||||||
|
1026
src/parser/mod.rs
1026
src/parser/mod.rs
File diff suppressed because it is too large
Load Diff
@ -2,9 +2,15 @@ use std::env;
|
|||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
use std::thread;
|
||||||
|
|
||||||
use php_parser_rs::lexer::Lexer;
|
use php_parser_rs::lexer::Lexer;
|
||||||
|
|
||||||
|
enum TestResult {
|
||||||
|
Success,
|
||||||
|
Error(String),
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn third_party_1_php_standard_library() {
|
fn third_party_1_php_standard_library() {
|
||||||
test_repository(
|
test_repository(
|
||||||
@ -33,9 +39,11 @@ fn third_party_3_symfony_framework() {
|
|||||||
"symfony-framework",
|
"symfony-framework",
|
||||||
"https://github.com/symfony/symfony",
|
"https://github.com/symfony/symfony",
|
||||||
"6.3",
|
"6.3",
|
||||||
&["src/Symfony"],
|
&["src/Symfony/"],
|
||||||
&[
|
&[
|
||||||
|
// stub
|
||||||
"src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/Fixtures/proxy-implem.php",
|
"src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/Fixtures/proxy-implem.php",
|
||||||
|
// file contains syntax error used for testing.
|
||||||
"src/Symfony/Component/Config/Tests/Fixtures/ParseError.php",
|
"src/Symfony/Component/Config/Tests/Fixtures/ParseError.php",
|
||||||
// FIXME: Remove this one once I've found the energy to sort out heredocs / nowdocs.
|
// FIXME: Remove this one once I've found the energy to sort out heredocs / nowdocs.
|
||||||
"src/Symfony/Component/DependencyInjection/LazyProxy/PhpDumper/LazyServiceDumper.php",
|
"src/Symfony/Component/DependencyInjection/LazyProxy/PhpDumper/LazyServiceDumper.php",
|
||||||
@ -98,12 +106,86 @@ fn test_repository(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut entries = vec![];
|
||||||
for dir in directories {
|
for dir in directories {
|
||||||
test_directory(out_path.clone(), out_path.join(dir), ignore);
|
entries.append(&mut read_directory(
|
||||||
|
out_path.clone(),
|
||||||
|
out_path.join(dir),
|
||||||
|
ignore,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut threads = vec![];
|
||||||
|
for (index, chunk) in entries.chunks(entries.len() / 4).enumerate() {
|
||||||
|
let chunk = chunk.to_vec();
|
||||||
|
let thread = thread::Builder::new()
|
||||||
|
.stack_size(16 * 1024 * 1024)
|
||||||
|
.name(format!("{name}:{index}"))
|
||||||
|
.spawn(move || {
|
||||||
|
let thread = thread::current();
|
||||||
|
let thread_name = thread.name().unwrap();
|
||||||
|
|
||||||
|
let mut results = vec![];
|
||||||
|
for (name, filename) in chunk {
|
||||||
|
let code = std::fs::read(&filename).unwrap();
|
||||||
|
|
||||||
|
match Lexer::new().tokenize(&code) {
|
||||||
|
Ok(tokens) => match php_parser_rs::parse(tokens) {
|
||||||
|
Ok(ast) => {
|
||||||
|
println!("✅ [{thread_name}][{name}]: {} statement(s).", ast.len());
|
||||||
|
|
||||||
|
results.push(TestResult::Success);
|
||||||
|
}
|
||||||
|
Err(error) => {
|
||||||
|
results.push(TestResult::Error(format!(
|
||||||
|
"❌ [{thread_name}][{name}]: {error:?}"
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(error) => {
|
||||||
|
results.push(TestResult::Error(format!(
|
||||||
|
"❌ [{thread_name}][{name}]: {error:?}"
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
results
|
||||||
|
});
|
||||||
|
|
||||||
|
threads.push(thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut results = vec![];
|
||||||
|
for thread in threads {
|
||||||
|
let mut result = thread
|
||||||
|
.unwrap_or_else(|e| panic!("failed to spawn thread: {:#?}", e))
|
||||||
|
.join()
|
||||||
|
.unwrap_or_else(|e| panic!("failed to join thread: {:#?}", e));
|
||||||
|
|
||||||
|
results.append(&mut result);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut fail = false;
|
||||||
|
results
|
||||||
|
.iter()
|
||||||
|
.map(|result| match result {
|
||||||
|
TestResult::Error(message) => {
|
||||||
|
fail = true;
|
||||||
|
|
||||||
|
println!("{}", message);
|
||||||
|
}
|
||||||
|
TestResult::Success => {}
|
||||||
|
})
|
||||||
|
.for_each(drop);
|
||||||
|
|
||||||
|
if fail {
|
||||||
|
panic!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_directory(root: PathBuf, directory: PathBuf, ignore: &[&str]) {
|
fn read_directory(root: PathBuf, directory: PathBuf, ignore: &[&str]) -> Vec<(String, PathBuf)> {
|
||||||
|
let mut results = vec![];
|
||||||
let mut entries = fs::read_dir(&directory)
|
let mut entries = fs::read_dir(&directory)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.flatten()
|
.flatten()
|
||||||
@ -114,7 +196,7 @@ fn test_directory(root: PathBuf, directory: PathBuf, ignore: &[&str]) {
|
|||||||
|
|
||||||
for entry in entries {
|
for entry in entries {
|
||||||
if entry.is_dir() {
|
if entry.is_dir() {
|
||||||
test_directory(root.clone(), entry, ignore);
|
results.append(&mut read_directory(root.clone(), entry, ignore));
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -136,26 +218,9 @@ fn test_directory(root: PathBuf, directory: PathBuf, ignore: &[&str]) {
|
|||||||
.strip_prefix(root.to_str().unwrap())
|
.strip_prefix(root.to_str().unwrap())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
test_file(name, entry);
|
results.push((name.to_string(), entry));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fn test_file(name: &str, filename: PathBuf) {
|
results
|
||||||
let code = std::fs::read(&filename).unwrap();
|
|
||||||
|
|
||||||
Lexer::new()
|
|
||||||
.tokenize(&code)
|
|
||||||
.map(|tokens| {
|
|
||||||
php_parser_rs::parse(tokens)
|
|
||||||
.map(|_| {
|
|
||||||
println!("✅ successfully parsed file: `\"{}\"`.", name);
|
|
||||||
})
|
|
||||||
.unwrap_or_else(|error| {
|
|
||||||
panic!("❌ failed to parse file: `\"{name}\"`, error: {error:?}")
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.unwrap_or_else(|error| {
|
|
||||||
panic!("❌ failed to tokenize file: `\"{name}\"`, error: {error:?}")
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user