mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 20:47:45 +00:00
LibJS: Add support for "continue" inside "for" statements :^)
This commit is contained in:
parent
e3b92caa6d
commit
9ebd066ac8
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);
|
last_value = interpreter.run(*m_body);
|
||||||
if (interpreter.exception())
|
if (interpreter.exception())
|
||||||
return {};
|
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) {
|
if (m_update) {
|
||||||
m_update->execute(interpreter);
|
m_update->execute(interpreter);
|
||||||
if (interpreter.exception())
|
if (interpreter.exception())
|
||||||
|
@ -226,6 +236,16 @@ Value ForStatement::execute(Interpreter& interpreter) const
|
||||||
last_value = interpreter.run(*m_body);
|
last_value = interpreter.run(*m_body);
|
||||||
if (interpreter.exception())
|
if (interpreter.exception())
|
||||||
return {};
|
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) {
|
if (m_update) {
|
||||||
m_update->execute(interpreter);
|
m_update->execute(interpreter);
|
||||||
if (interpreter.exception())
|
if (interpreter.exception())
|
||||||
|
@ -759,7 +779,7 @@ Value VariableDeclaration::execute(Interpreter& interpreter) const
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
Value VariableDeclarator::execute(Interpreter &) const
|
Value VariableDeclarator::execute(Interpreter&) const
|
||||||
{
|
{
|
||||||
// NOTE: This node is handled by VariableDeclaration.
|
// NOTE: This node is handled by VariableDeclaration.
|
||||||
ASSERT_NOT_REACHED();
|
ASSERT_NOT_REACHED();
|
||||||
|
@ -1005,6 +1025,12 @@ Value BreakStatement::execute(Interpreter& interpreter) const
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Value ContinueStatement::execute(Interpreter& interpreter) const
|
||||||
|
{
|
||||||
|
interpreter.unwind(ScopeType::Continuable);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
void SwitchStatement::dump(int indent) const
|
void SwitchStatement::dump(int indent) const
|
||||||
{
|
{
|
||||||
ASTNode::dump(indent);
|
ASTNode::dump(indent);
|
||||||
|
|
|
@ -814,4 +814,14 @@ private:
|
||||||
virtual const char* class_name() const override { return "BreakStatement"; }
|
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,
|
Block,
|
||||||
Try,
|
Try,
|
||||||
Breakable,
|
Breakable,
|
||||||
|
Continuable,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Variable {
|
struct Variable {
|
||||||
|
|
|
@ -48,6 +48,7 @@ Lexer::Lexer(StringView source)
|
||||||
s_keywords.set("catch", TokenType::Catch);
|
s_keywords.set("catch", TokenType::Catch);
|
||||||
s_keywords.set("class", TokenType::Class);
|
s_keywords.set("class", TokenType::Class);
|
||||||
s_keywords.set("const", TokenType::Const);
|
s_keywords.set("const", TokenType::Const);
|
||||||
|
s_keywords.set("continue", TokenType::Continue);
|
||||||
s_keywords.set("default", TokenType::Default);
|
s_keywords.set("default", TokenType::Default);
|
||||||
s_keywords.set("delete", TokenType::Delete);
|
s_keywords.set("delete", TokenType::Delete);
|
||||||
s_keywords.set("do", TokenType::Do);
|
s_keywords.set("do", TokenType::Do);
|
||||||
|
|
|
@ -208,6 +208,8 @@ NonnullRefPtr<Statement> Parser::parse_statement()
|
||||||
return parse_try_statement();
|
return parse_try_statement();
|
||||||
case TokenType::Break:
|
case TokenType::Break:
|
||||||
return parse_break_statement();
|
return parse_break_statement();
|
||||||
|
case TokenType::Continue:
|
||||||
|
return parse_continue_statement();
|
||||||
case TokenType::Switch:
|
case TokenType::Switch:
|
||||||
return parse_switch_statement();
|
return parse_switch_statement();
|
||||||
case TokenType::Do:
|
case TokenType::Do:
|
||||||
|
@ -686,6 +688,13 @@ NonnullRefPtr<BreakStatement> Parser::parse_break_statement()
|
||||||
return create_ast_node<BreakStatement>();
|
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)
|
NonnullRefPtr<ConditionalExpression> Parser::parse_conditional_expression(NonnullRefPtr<Expression> test)
|
||||||
{
|
{
|
||||||
consume(TokenType::QuestionMark);
|
consume(TokenType::QuestionMark);
|
||||||
|
@ -938,6 +947,7 @@ bool Parser::match_statement() const
|
||||||
|| type == TokenType::CurlyOpen
|
|| type == TokenType::CurlyOpen
|
||||||
|| type == TokenType::Switch
|
|| type == TokenType::Switch
|
||||||
|| type == TokenType::Break
|
|| type == TokenType::Break
|
||||||
|
|| type == TokenType::Continue
|
||||||
|| type == TokenType::Var;
|
|| type == TokenType::Var;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -58,6 +58,7 @@ public:
|
||||||
NonnullRefPtr<SwitchStatement> parse_switch_statement();
|
NonnullRefPtr<SwitchStatement> parse_switch_statement();
|
||||||
NonnullRefPtr<SwitchCase> parse_switch_case();
|
NonnullRefPtr<SwitchCase> parse_switch_case();
|
||||||
NonnullRefPtr<BreakStatement> parse_break_statement();
|
NonnullRefPtr<BreakStatement> parse_break_statement();
|
||||||
|
NonnullRefPtr<ContinueStatement> parse_continue_statement();
|
||||||
NonnullRefPtr<DoWhileStatement> parse_do_while_statement();
|
NonnullRefPtr<DoWhileStatement> parse_do_while_statement();
|
||||||
NonnullRefPtr<ConditionalExpression> parse_conditional_expression(NonnullRefPtr<Expression> test);
|
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(Colon) \
|
||||||
__ENUMERATE_JS_TOKEN(Comma) \
|
__ENUMERATE_JS_TOKEN(Comma) \
|
||||||
__ENUMERATE_JS_TOKEN(Const) \
|
__ENUMERATE_JS_TOKEN(Const) \
|
||||||
|
__ENUMERATE_JS_TOKEN(Continue) \
|
||||||
__ENUMERATE_JS_TOKEN(CurlyClose) \
|
__ENUMERATE_JS_TOKEN(CurlyClose) \
|
||||||
__ENUMERATE_JS_TOKEN(CurlyOpen) \
|
__ENUMERATE_JS_TOKEN(CurlyOpen) \
|
||||||
__ENUMERATE_JS_TOKEN(Default) \
|
__ENUMERATE_JS_TOKEN(Default) \
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue