mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 04:07:44 +00:00
LibJS: Allow multiple labels on the same statement
Since there are only a number of statements where labels can actually be used we now also only store labels when necessary. Also now tracks the first continue usage of a label since this might not be valid but that can only be determined after we have parsed the statement. Also ensures the correct error does not get wiped by load_state.
This commit is contained in:
parent
bfc1b4ba61
commit
79caca8ca2
7 changed files with 194 additions and 47 deletions
|
@ -586,7 +586,12 @@ RefPtr<Statement> Parser::try_parse_labelled_statement(AllowLabelledFunction all
|
|||
return {};
|
||||
}
|
||||
|
||||
auto identifier = consume_identifier_reference().value();
|
||||
auto identifier = [&] {
|
||||
if (m_state.current_token.value() == "await"sv) {
|
||||
return consume().value();
|
||||
}
|
||||
return consume_identifier_reference().value();
|
||||
}();
|
||||
if (!match(TokenType::Colon))
|
||||
return {};
|
||||
consume(TokenType::Colon);
|
||||
|
@ -594,6 +599,14 @@ RefPtr<Statement> Parser::try_parse_labelled_statement(AllowLabelledFunction all
|
|||
if (!match_statement())
|
||||
return {};
|
||||
|
||||
state_rollback_guard.disarm();
|
||||
discard_saved_state();
|
||||
|
||||
if (m_state.strict_mode && identifier == "let"sv) {
|
||||
syntax_error("Strict mode reserved word 'let' is not allowed in label", rule_start.position());
|
||||
return {};
|
||||
}
|
||||
|
||||
if (match(TokenType::Function) && (allow_function == AllowLabelledFunction::No || m_state.strict_mode)) {
|
||||
syntax_error("Not allowed to declare a function here");
|
||||
return {};
|
||||
|
@ -604,8 +617,10 @@ RefPtr<Statement> Parser::try_parse_labelled_statement(AllowLabelledFunction all
|
|||
|
||||
RefPtr<Statement> labelled_statement;
|
||||
|
||||
auto is_iteration_statement = false;
|
||||
|
||||
if (match(TokenType::Function)) {
|
||||
m_state.labels_in_scope.set(identifier, false);
|
||||
m_state.labels_in_scope.set(identifier, {});
|
||||
auto function_declaration = parse_function_node<FunctionDeclaration>();
|
||||
m_state.current_scope->function_declarations.append(function_declaration);
|
||||
auto hoisting_target = m_state.current_scope->get_current_function_scope();
|
||||
|
@ -615,16 +630,23 @@ RefPtr<Statement> Parser::try_parse_labelled_statement(AllowLabelledFunction all
|
|||
|
||||
labelled_statement = move(function_declaration);
|
||||
} else {
|
||||
auto is_iteration_statement = match(TokenType::For) || match(TokenType::Do) || match(TokenType::While);
|
||||
m_state.labels_in_scope.set(identifier, is_iteration_statement);
|
||||
labelled_statement = parse_statement();
|
||||
m_state.labels_in_scope.set(identifier, {});
|
||||
labelled_statement = parse_statement(allow_function);
|
||||
if (is<IterationStatement>(*labelled_statement)) {
|
||||
is_iteration_statement = true;
|
||||
static_cast<IterationStatement&>(*labelled_statement).add_label(identifier);
|
||||
} else if (is<LabelableStatement>(*labelled_statement)) {
|
||||
static_cast<LabelableStatement&>(*labelled_statement).add_label(identifier);
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_iteration_statement) {
|
||||
if (auto entry = m_state.labels_in_scope.find(identifier); entry != m_state.labels_in_scope.end() && entry->value.has_value())
|
||||
syntax_error("labelled continue statement cannot use non iterating statement", m_state.labels_in_scope.get(identifier).value());
|
||||
}
|
||||
|
||||
m_state.labels_in_scope.remove(identifier);
|
||||
|
||||
labelled_statement->add_label(identifier);
|
||||
state_rollback_guard.disarm();
|
||||
discard_saved_state();
|
||||
return labelled_statement.release_nonnull();
|
||||
}
|
||||
|
||||
|
@ -2376,7 +2398,7 @@ NonnullRefPtr<BreakStatement> Parser::parse_break_statement()
|
|||
if (match(TokenType::Semicolon)) {
|
||||
consume();
|
||||
} else {
|
||||
if (match(TokenType::Identifier) && !m_state.current_token.trivia_contains_line_terminator()) {
|
||||
if (!m_state.current_token.trivia_contains_line_terminator() && match_identifier()) {
|
||||
target_label = consume().value();
|
||||
|
||||
auto label = m_state.labels_in_scope.find(target_label);
|
||||
|
@ -2404,12 +2426,15 @@ NonnullRefPtr<ContinueStatement> Parser::parse_continue_statement()
|
|||
consume();
|
||||
return create_ast_node<ContinueStatement>({ m_state.current_token.filename(), rule_start.position(), position() }, target_label);
|
||||
}
|
||||
if (match(TokenType::Identifier) && !m_state.current_token.trivia_contains_line_terminator()) {
|
||||
if (!m_state.current_token.trivia_contains_line_terminator() && match_identifier()) {
|
||||
auto label_position = position();
|
||||
target_label = consume().value();
|
||||
|
||||
auto label = m_state.labels_in_scope.find(target_label);
|
||||
if (label == m_state.labels_in_scope.end() || !label->value)
|
||||
if (label == m_state.labels_in_scope.end())
|
||||
syntax_error(String::formatted("Label '{}' not found or invalid", target_label));
|
||||
else
|
||||
label->value = label_position;
|
||||
}
|
||||
consume_or_insert_semicolon();
|
||||
return create_ast_node<ContinueStatement>({ m_state.current_token.filename(), rule_start.position(), position() }, target_label);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue