LibJS: Add support for "continue" inside "for" statements :^)
This commit is contained in:
parent
e3b92caa6d
commit
9ebd066ac8
Notes:
sideshowbarker
2024-07-19 07:55:21 +09:00
Author: https://github.com/awesomekling Commit: https://github.com/SerenityOS/serenity/commit/9ebd066ac84
8 changed files with 65 additions and 1 deletions
|
@ -215,6 +215,16 @@ Value ForStatement::execute(Interpreter& interpreter) const
|
|||
last_value = interpreter.run(*m_body);
|
||||
if (interpreter.exception())
|
||||
return {};
|
||||
if (interpreter.should_unwind()) {
|
||||
if (interpreter.should_unwind_until(ScopeType::Continuable)) {
|
||||
interpreter.stop_unwind();
|
||||
} else if (interpreter.should_unwind_until(ScopeType::Breakable)) {
|
||||
interpreter.stop_unwind();
|
||||
break;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
if (m_update) {
|
||||
m_update->execute(interpreter);
|
||||
if (interpreter.exception())
|
||||
|
@ -226,6 +236,16 @@ Value ForStatement::execute(Interpreter& interpreter) const
|
|||
last_value = interpreter.run(*m_body);
|
||||
if (interpreter.exception())
|
||||
return {};
|
||||
if (interpreter.should_unwind()) {
|
||||
if (interpreter.should_unwind_until(ScopeType::Continuable)) {
|
||||
interpreter.stop_unwind();
|
||||
} else if (interpreter.should_unwind_until(ScopeType::Breakable)) {
|
||||
interpreter.stop_unwind();
|
||||
break;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
if (m_update) {
|
||||
m_update->execute(interpreter);
|
||||
if (interpreter.exception())
|
||||
|
@ -759,7 +779,7 @@ Value VariableDeclaration::execute(Interpreter& interpreter) const
|
|||
return {};
|
||||
}
|
||||
|
||||
Value VariableDeclarator::execute(Interpreter &) const
|
||||
Value VariableDeclarator::execute(Interpreter&) const
|
||||
{
|
||||
// NOTE: This node is handled by VariableDeclaration.
|
||||
ASSERT_NOT_REACHED();
|
||||
|
@ -1005,6 +1025,12 @@ Value BreakStatement::execute(Interpreter& interpreter) const
|
|||
return {};
|
||||
}
|
||||
|
||||
Value ContinueStatement::execute(Interpreter& interpreter) const
|
||||
{
|
||||
interpreter.unwind(ScopeType::Continuable);
|
||||
return {};
|
||||
}
|
||||
|
||||
void SwitchStatement::dump(int indent) const
|
||||
{
|
||||
ASTNode::dump(indent);
|
||||
|
|
|
@ -814,4 +814,14 @@ private:
|
|||
virtual const char* class_name() const override { return "BreakStatement"; }
|
||||
};
|
||||
|
||||
class ContinueStatement final : public Statement {
|
||||
public:
|
||||
ContinueStatement() {}
|
||||
|
||||
virtual Value execute(Interpreter&) const override;
|
||||
|
||||
private:
|
||||
virtual const char* class_name() const override { return "ContinueStatement"; }
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@ enum class ScopeType {
|
|||
Block,
|
||||
Try,
|
||||
Breakable,
|
||||
Continuable,
|
||||
};
|
||||
|
||||
struct Variable {
|
||||
|
|
|
@ -48,6 +48,7 @@ Lexer::Lexer(StringView source)
|
|||
s_keywords.set("catch", TokenType::Catch);
|
||||
s_keywords.set("class", TokenType::Class);
|
||||
s_keywords.set("const", TokenType::Const);
|
||||
s_keywords.set("continue", TokenType::Continue);
|
||||
s_keywords.set("default", TokenType::Default);
|
||||
s_keywords.set("delete", TokenType::Delete);
|
||||
s_keywords.set("do", TokenType::Do);
|
||||
|
|
|
@ -208,6 +208,8 @@ NonnullRefPtr<Statement> Parser::parse_statement()
|
|||
return parse_try_statement();
|
||||
case TokenType::Break:
|
||||
return parse_break_statement();
|
||||
case TokenType::Continue:
|
||||
return parse_continue_statement();
|
||||
case TokenType::Switch:
|
||||
return parse_switch_statement();
|
||||
case TokenType::Do:
|
||||
|
@ -686,6 +688,13 @@ NonnullRefPtr<BreakStatement> Parser::parse_break_statement()
|
|||
return create_ast_node<BreakStatement>();
|
||||
}
|
||||
|
||||
NonnullRefPtr<ContinueStatement> Parser::parse_continue_statement()
|
||||
{
|
||||
consume(TokenType::Continue);
|
||||
// FIXME: Handle labels.
|
||||
return create_ast_node<ContinueStatement>();
|
||||
}
|
||||
|
||||
NonnullRefPtr<ConditionalExpression> Parser::parse_conditional_expression(NonnullRefPtr<Expression> test)
|
||||
{
|
||||
consume(TokenType::QuestionMark);
|
||||
|
@ -938,6 +947,7 @@ bool Parser::match_statement() const
|
|||
|| type == TokenType::CurlyOpen
|
||||
|| type == TokenType::Switch
|
||||
|| type == TokenType::Break
|
||||
|| type == TokenType::Continue
|
||||
|| type == TokenType::Var;
|
||||
}
|
||||
|
||||
|
|
|
@ -58,6 +58,7 @@ public:
|
|||
NonnullRefPtr<SwitchStatement> parse_switch_statement();
|
||||
NonnullRefPtr<SwitchCase> parse_switch_case();
|
||||
NonnullRefPtr<BreakStatement> parse_break_statement();
|
||||
NonnullRefPtr<ContinueStatement> parse_continue_statement();
|
||||
NonnullRefPtr<DoWhileStatement> parse_do_while_statement();
|
||||
NonnullRefPtr<ConditionalExpression> parse_conditional_expression(NonnullRefPtr<Expression> test);
|
||||
|
||||
|
|
14
Libraries/LibJS/Tests/continue-basic.js
Normal file
14
Libraries/LibJS/Tests/continue-basic.js
Normal file
|
@ -0,0 +1,14 @@
|
|||
function assert(x) { if (!x) throw 1; }
|
||||
|
||||
try {
|
||||
var j = 0;
|
||||
for (var i = 0; i < 9; ++i) {
|
||||
if (i == 3)
|
||||
continue;
|
||||
++j;
|
||||
}
|
||||
assert(j == 8);
|
||||
console.log("PASS");
|
||||
} catch {
|
||||
}
|
||||
|
|
@ -50,6 +50,7 @@ namespace JS {
|
|||
__ENUMERATE_JS_TOKEN(Colon) \
|
||||
__ENUMERATE_JS_TOKEN(Comma) \
|
||||
__ENUMERATE_JS_TOKEN(Const) \
|
||||
__ENUMERATE_JS_TOKEN(Continue) \
|
||||
__ENUMERATE_JS_TOKEN(CurlyClose) \
|
||||
__ENUMERATE_JS_TOKEN(CurlyOpen) \
|
||||
__ENUMERATE_JS_TOKEN(Default) \
|
||||
|
|
Loading…
Add table
Reference in a new issue