mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 15:47:44 +00:00
LibJS: Implement automatic semicolon insertion
This commit is contained in:
parent
b77ceecb02
commit
07f838dc4e
2 changed files with 67 additions and 24 deletions
|
@ -151,7 +151,7 @@ int Parser::operator_precedence(TokenType type) const
|
||||||
{
|
{
|
||||||
auto it = g_operator_precedence.find(type);
|
auto it = g_operator_precedence.find(type);
|
||||||
if (it == g_operator_precedence.end()) {
|
if (it == g_operator_precedence.end()) {
|
||||||
fprintf(stderr, "No precedence for operator %s\n", Token::name(type));
|
fprintf(stderr, "Internal Error: No precedence for operator %s\n", Token::name(type));
|
||||||
ASSERT_NOT_REACHED();
|
ASSERT_NOT_REACHED();
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -250,15 +250,17 @@ NonnullRefPtr<Statement> Parser::parse_statement()
|
||||||
case TokenType::Do:
|
case TokenType::Do:
|
||||||
return parse_do_while_statement();
|
return parse_do_while_statement();
|
||||||
default:
|
default:
|
||||||
if (match_expression())
|
if (match_expression()) {
|
||||||
return adopt(*new ExpressionStatement(parse_expression(0)));
|
auto expr = parse_expression(0);
|
||||||
|
consume_or_insert_semicolon();
|
||||||
|
return create_ast_node<ExpressionStatement>(move(expr));
|
||||||
|
}
|
||||||
m_parser_state.m_has_errors = true;
|
m_parser_state.m_has_errors = true;
|
||||||
expected("statement (missing switch case)");
|
expected("statement (missing switch case)");
|
||||||
consume();
|
consume();
|
||||||
return create_ast_node<ErrorStatement>();
|
return create_ast_node<ErrorStatement>();
|
||||||
} }();
|
} }();
|
||||||
if (match(TokenType::Semicolon))
|
|
||||||
consume();
|
|
||||||
return statement;
|
return statement;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -444,7 +446,7 @@ NonnullRefPtr<ObjectExpression> Parser::parse_object_expression()
|
||||||
} else {
|
} else {
|
||||||
m_parser_state.m_has_errors = true;
|
m_parser_state.m_has_errors = true;
|
||||||
auto& current_token = m_parser_state.m_current_token;
|
auto& current_token = m_parser_state.m_current_token;
|
||||||
fprintf(stderr, "Error: Unexpected token %s as member in object initialization. Expected a numeric literal, string literal or identifier (line: %zu, column: %zu))\n",
|
fprintf(stderr, "Syntax Error: Unexpected token %s as member in object initialization. Expected a numeric literal, string literal or identifier (line: %zu, column: %zu))\n",
|
||||||
current_token.name(),
|
current_token.name(),
|
||||||
current_token.line_number(),
|
current_token.line_number(),
|
||||||
current_token.line_column());
|
current_token.line_column());
|
||||||
|
@ -653,9 +655,15 @@ NonnullRefPtr<NewExpression> Parser::parse_new_expression()
|
||||||
NonnullRefPtr<ReturnStatement> Parser::parse_return_statement()
|
NonnullRefPtr<ReturnStatement> Parser::parse_return_statement()
|
||||||
{
|
{
|
||||||
consume(TokenType::Return);
|
consume(TokenType::Return);
|
||||||
|
|
||||||
|
// Automatic semicolon insertion: terminate statement when return is followed by newline
|
||||||
|
if (m_parser_state.m_current_token.trivia().contains('\n'))
|
||||||
|
return create_ast_node<ReturnStatement>(nullptr);
|
||||||
|
|
||||||
if (match_expression()) {
|
if (match_expression()) {
|
||||||
return create_ast_node<ReturnStatement>(parse_expression(0));
|
return create_ast_node<ReturnStatement>(parse_expression(0));
|
||||||
}
|
}
|
||||||
|
consume_or_insert_semicolon();
|
||||||
return create_ast_node<ReturnStatement>(nullptr);
|
return create_ast_node<ReturnStatement>(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -744,6 +752,8 @@ NonnullRefPtr<VariableDeclaration> Parser::parse_variable_declaration()
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
consume_or_insert_semicolon();
|
||||||
|
|
||||||
auto declaration = create_ast_node<VariableDeclaration>(declaration_kind, move(declarations));
|
auto declaration = create_ast_node<VariableDeclaration>(declaration_kind, move(declarations));
|
||||||
if (declaration->declaration_kind() == DeclarationKind::Var)
|
if (declaration->declaration_kind() == DeclarationKind::Var)
|
||||||
m_parser_state.m_var_scopes.last().append(declaration);
|
m_parser_state.m_var_scopes.last().append(declaration);
|
||||||
|
@ -755,20 +765,32 @@ NonnullRefPtr<VariableDeclaration> Parser::parse_variable_declaration()
|
||||||
NonnullRefPtr<ThrowStatement> Parser::parse_throw_statement()
|
NonnullRefPtr<ThrowStatement> Parser::parse_throw_statement()
|
||||||
{
|
{
|
||||||
consume(TokenType::Throw);
|
consume(TokenType::Throw);
|
||||||
return create_ast_node<ThrowStatement>(parse_expression(0));
|
|
||||||
|
// Automatic semicolon insertion: terminate statement when throw is followed by newline
|
||||||
|
if (m_parser_state.m_current_token.trivia().contains('\n')) {
|
||||||
|
m_parser_state.m_has_errors = true;
|
||||||
|
fprintf(stderr, "Syntax Error: no line break is allowed between 'throw' and its expression\n");
|
||||||
|
return create_ast_node<ThrowStatement>(create_ast_node<ErrorExpression>());
|
||||||
|
}
|
||||||
|
|
||||||
|
auto expression = parse_expression(0);
|
||||||
|
consume_or_insert_semicolon();
|
||||||
|
return create_ast_node<ThrowStatement>(move(expression));
|
||||||
}
|
}
|
||||||
|
|
||||||
NonnullRefPtr<BreakStatement> Parser::parse_break_statement()
|
NonnullRefPtr<BreakStatement> Parser::parse_break_statement()
|
||||||
{
|
{
|
||||||
consume(TokenType::Break);
|
consume(TokenType::Break);
|
||||||
// FIXME: Handle labels.
|
consume_or_insert_semicolon();
|
||||||
|
// FIXME: Handle labels. When fixing this, take care to correctly implement semicolon insertion
|
||||||
return create_ast_node<BreakStatement>();
|
return create_ast_node<BreakStatement>();
|
||||||
}
|
}
|
||||||
|
|
||||||
NonnullRefPtr<ContinueStatement> Parser::parse_continue_statement()
|
NonnullRefPtr<ContinueStatement> Parser::parse_continue_statement()
|
||||||
{
|
{
|
||||||
consume(TokenType::Continue);
|
consume(TokenType::Continue);
|
||||||
// FIXME: Handle labels.
|
consume_or_insert_semicolon();
|
||||||
|
// FIXME: Handle labels. When fixing this, take care to correctly implement semicolon insertion
|
||||||
return create_ast_node<ContinueStatement>();
|
return create_ast_node<ContinueStatement>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -812,6 +834,7 @@ NonnullRefPtr<DoWhileStatement> Parser::parse_do_while_statement()
|
||||||
auto test = parse_expression(0);
|
auto test = parse_expression(0);
|
||||||
|
|
||||||
consume(TokenType::ParenClose);
|
consume(TokenType::ParenClose);
|
||||||
|
consume_or_insert_semicolon();
|
||||||
|
|
||||||
return create_ast_node<DoWhileStatement>(move(test), move(body));
|
return create_ast_node<DoWhileStatement>(move(test), move(body));
|
||||||
}
|
}
|
||||||
|
@ -889,21 +912,25 @@ NonnullRefPtr<ForStatement> Parser::parse_for_statement()
|
||||||
|
|
||||||
consume(TokenType::ParenOpen);
|
consume(TokenType::ParenOpen);
|
||||||
|
|
||||||
|
bool first_semicolon_consumed = false;
|
||||||
RefPtr<ASTNode> init;
|
RefPtr<ASTNode> init;
|
||||||
switch (m_parser_state.m_current_token.type()) {
|
switch (m_parser_state.m_current_token.type()) {
|
||||||
case TokenType::Semicolon:
|
case TokenType::Semicolon:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (match_expression())
|
if (match_expression()) {
|
||||||
init = parse_expression(0);
|
init = parse_expression(0);
|
||||||
else if (match_variable_declaration())
|
} else if (match_variable_declaration()) {
|
||||||
init = parse_variable_declaration();
|
init = parse_variable_declaration();
|
||||||
else
|
first_semicolon_consumed = true;
|
||||||
|
} else {
|
||||||
ASSERT_NOT_REACHED();
|
ASSERT_NOT_REACHED();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
consume(TokenType::Semicolon);
|
if (!first_semicolon_consumed)
|
||||||
|
consume(TokenType::Semicolon);
|
||||||
|
|
||||||
RefPtr<Expression> test;
|
RefPtr<Expression> test;
|
||||||
switch (m_parser_state.m_current_token.type()) {
|
switch (m_parser_state.m_current_token.type()) {
|
||||||
|
@ -1050,16 +1077,32 @@ Token Parser::consume()
|
||||||
return old_token;
|
return old_token;
|
||||||
}
|
}
|
||||||
|
|
||||||
Token Parser::consume(TokenType type)
|
void Parser::consume_or_insert_semicolon()
|
||||||
{
|
{
|
||||||
if (m_parser_state.m_current_token.type() != type) {
|
// Semicolon was found and will be consumed
|
||||||
m_parser_state.m_has_errors = true;
|
if (match(TokenType::Semicolon)) {
|
||||||
auto& current_token = m_parser_state.m_current_token;
|
consume();
|
||||||
fprintf(stderr, "Error: Unexpected token %s. Expected %s (line: %zu, column: %zu))\n",
|
return;
|
||||||
current_token.name(),
|
}
|
||||||
Token::name(type),
|
// Insert semicolon if...
|
||||||
current_token.line_number(),
|
// ...token is preceeded by one or more newlines
|
||||||
current_token.line_column());
|
if (m_parser_state.m_current_token.trivia().contains('\n'))
|
||||||
|
return;
|
||||||
|
// ...token is a closing paren
|
||||||
|
if (match(TokenType::ParenClose))
|
||||||
|
return;
|
||||||
|
// ...token is eof
|
||||||
|
if (match(TokenType::Eof))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// No rule for semicolon insertion applies -> syntax error
|
||||||
|
expected("Semicolon");
|
||||||
|
}
|
||||||
|
|
||||||
|
Token Parser::consume(TokenType expected_type)
|
||||||
|
{
|
||||||
|
if (m_parser_state.m_current_token.type() != expected_type) {
|
||||||
|
expected(Token::name(expected_type));
|
||||||
}
|
}
|
||||||
return consume();
|
return consume();
|
||||||
}
|
}
|
||||||
|
@ -1068,7 +1111,7 @@ void Parser::expected(const char* what)
|
||||||
{
|
{
|
||||||
m_parser_state.m_has_errors = true;
|
m_parser_state.m_has_errors = true;
|
||||||
auto& current_token = m_parser_state.m_current_token;
|
auto& current_token = m_parser_state.m_current_token;
|
||||||
fprintf(stderr, "Error: Unexpected token %s. Expected %s (line: %zu, column: %zu)\n",
|
fprintf(stderr, "Syntax Error: Unexpected token %s. Expected %s (line: %zu, column: %zu)\n",
|
||||||
current_token.name(),
|
current_token.name(),
|
||||||
what,
|
what,
|
||||||
current_token.line_number(),
|
current_token.line_number(),
|
||||||
|
|
|
@ -89,6 +89,7 @@ private:
|
||||||
void expected(const char* what);
|
void expected(const char* what);
|
||||||
Token consume();
|
Token consume();
|
||||||
Token consume(TokenType type);
|
Token consume(TokenType type);
|
||||||
|
void consume_or_insert_semicolon();
|
||||||
void save_state();
|
void save_state();
|
||||||
void load_state();
|
void load_state();
|
||||||
|
|
||||||
|
@ -104,6 +105,5 @@ private:
|
||||||
|
|
||||||
ParserState m_parser_state;
|
ParserState m_parser_state;
|
||||||
Optional<ParserState> m_saved_state;
|
Optional<ParserState> m_saved_state;
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue