From 5e40aa182b9507559f20d94902a2ae868afe7393 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Sat, 4 Apr 2020 21:46:25 +0200 Subject: [PATCH] LibJS: Support VariableDeclaration with multiple declarators This patch adds support in the parser and interpreter for this: var a = 1, b = 2, c = a + b; VariableDeclaration is now a sequence of VariableDeclarators. :^) --- Libraries/LibJS/AST.cpp | 35 +++++++++++++------ Libraries/LibJS/AST.h | 30 ++++++++++++---- Libraries/LibJS/Parser.cpp | 22 ++++++++---- .../LibJS/Tests/var-multiple-declarator.js | 11 ++++++ 4 files changed, 76 insertions(+), 22 deletions(-) create mode 100644 Libraries/LibJS/Tests/var-multiple-declarator.js diff --git a/Libraries/LibJS/AST.cpp b/Libraries/LibJS/AST.cpp index 978ede3922..36ebb8b8c8 100644 --- a/Libraries/LibJS/AST.cpp +++ b/Libraries/LibJS/AST.cpp @@ -747,17 +747,24 @@ void UpdateExpression::dump(int indent) const Value VariableDeclaration::execute(Interpreter& interpreter) const { - interpreter.declare_variable(name().string(), m_declaration_type); - if (m_initializer) { - auto initalizer_result = m_initializer->execute(interpreter); - if (interpreter.exception()) - return {}; - interpreter.set_variable(name().string(), initalizer_result, true); + for (auto& declarator : m_declarations) { + interpreter.declare_variable(declarator.id().string(), m_declaration_type); + if (auto* init = declarator.init()) { + auto initalizer_result = init->execute(interpreter); + if (interpreter.exception()) + return {}; + interpreter.set_variable(declarator.id().string(), initalizer_result, true); + } } - return {}; } +Value VariableDeclarator::execute(Interpreter &) const +{ + // NOTE: This node is handled by VariableDeclaration. + ASSERT_NOT_REACHED(); +} + void VariableDeclaration::dump(int indent) const { const char* declaration_type_string = nullptr; @@ -776,9 +783,17 @@ void VariableDeclaration::dump(int indent) const ASTNode::dump(indent); print_indent(indent + 1); printf("%s\n", declaration_type_string); - m_name->dump(indent + 1); - if (m_initializer) - m_initializer->dump(indent + 1); + + for (auto& declarator : m_declarations) + declarator.dump(indent + 1); +} + +void VariableDeclarator::dump(int indent) const +{ + ASTNode::dump(indent); + m_id->dump(indent + 1); + if (m_init) + m_init->dump(indent + 1); } void ObjectExpression::dump(int indent) const diff --git a/Libraries/LibJS/AST.h b/Libraries/LibJS/AST.h index cfd6d06aa8..56f4e355f5 100644 --- a/Libraries/LibJS/AST.h +++ b/Libraries/LibJS/AST.h @@ -579,17 +579,36 @@ enum class DeclarationType { Const, }; +class VariableDeclarator final : public ASTNode { +public: + VariableDeclarator(NonnullRefPtr id, RefPtr init) + : m_id(move(id)) + , m_init(move(init)) + { + } + + const Identifier& id() const { return m_id; } + const Expression* init() const { return m_init; } + + virtual Value execute(Interpreter&) const override; + virtual void dump(int indent) const override; + +private: + virtual const char* class_name() const override { return "VariableDeclarator"; } + + NonnullRefPtr m_id; + RefPtr m_init; +}; + class VariableDeclaration : public Declaration { public: - VariableDeclaration(NonnullRefPtr name, RefPtr initializer, DeclarationType declaration_type) + VariableDeclaration(DeclarationType declaration_type, NonnullRefPtrVector declarations) : m_declaration_type(declaration_type) - , m_name(move(name)) - , m_initializer(move(initializer)) + , m_declarations(move(declarations)) { } virtual bool is_variable_declaration() const override { return true; } - const Identifier& name() const { return *m_name; } DeclarationType declaration_type() const { return m_declaration_type; } virtual Value execute(Interpreter&) const override; @@ -599,8 +618,7 @@ private: virtual const char* class_name() const override { return "VariableDeclaration"; } DeclarationType m_declaration_type; - NonnullRefPtr m_name; - RefPtr m_initializer; + NonnullRefPtrVector m_declarations; }; class ObjectExpression : public Expression { diff --git a/Libraries/LibJS/Parser.cpp b/Libraries/LibJS/Parser.cpp index 50617f9e2c..682039ad51 100644 --- a/Libraries/LibJS/Parser.cpp +++ b/Libraries/LibJS/Parser.cpp @@ -654,13 +654,23 @@ NonnullRefPtr Parser::parse_variable_declaration() default: ASSERT_NOT_REACHED(); } - auto name = consume(TokenType::Identifier).value(); - RefPtr initializer; - if (match(TokenType::Equals)) { - consume(); - initializer = parse_expression(0); + + NonnullRefPtrVector declarations; + for (;;) { + auto id = consume(TokenType::Identifier).value(); + RefPtr init; + if (match(TokenType::Equals)) { + consume(); + init = parse_expression(0); + } + declarations.append(create_ast_node(create_ast_node(move(id)), move(init))); + if (match(TokenType::Comma)) { + consume(); + continue; + } + break; } - return create_ast_node(create_ast_node(name), move(initializer), declaration_type); + return create_ast_node(declaration_type, move(declarations)); } NonnullRefPtr Parser::parse_throw_statement() diff --git a/Libraries/LibJS/Tests/var-multiple-declarator.js b/Libraries/LibJS/Tests/var-multiple-declarator.js new file mode 100644 index 0000000000..79098eb68e --- /dev/null +++ b/Libraries/LibJS/Tests/var-multiple-declarator.js @@ -0,0 +1,11 @@ +function assert(x) { if (!x) throw 1; } + +try { + var a = 1, b = 2, c = a + b; + assert(a === 1); + assert(b === 2); + assert(c === 3); + console.log("PASS"); +} catch (e) { + console.log("FAIL: " + e); +}