diff options
author | Ali Mohammad Pur <ali.mpfard@gmail.com> | 2021-07-11 01:16:17 +0430 |
---|---|---|
committer | Linus Groh <mail@linusgroh.de> | 2021-07-11 21:41:54 +0100 |
commit | 1a9518ebe313f10091da92e73de76b4ab2f2d875 (patch) | |
tree | 0bcf7429be2941e314df5209f5ed8f7983f328fb /Userland | |
parent | 7fc6cd6b20ea0f9252c733de5c3249605a3bd130 (diff) | |
download | serenity-1a9518ebe313f10091da92e73de76b4ab2f2d875.zip |
LibJS: Implement parsing and evaluation for AssignmentPatterns
e.g. `[...foo] = bar` can now be evaluated :^)
Diffstat (limited to 'Userland')
-rw-r--r-- | Userland/Libraries/LibJS/AST.cpp | 76 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/AST.h | 10 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp | 12 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Lexer.cpp | 5 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Parser.cpp | 33 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/SourceRange.h | 3 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Token.h | 5 |
7 files changed, 104 insertions, 40 deletions
diff --git a/Userland/Libraries/LibJS/AST.cpp b/Userland/Libraries/LibJS/AST.cpp index 06f548bb07..6abd3e48dd 100644 --- a/Userland/Libraries/LibJS/AST.cpp +++ b/Userland/Libraries/LibJS/AST.cpp @@ -1484,11 +1484,18 @@ Value AssignmentExpression::execute(Interpreter& interpreter, GlobalObject& glob { InterpreterNodeScope node_scope { interpreter, *this }; +#define EXECUTE_LHS() \ + do { \ + if (auto* ptr = m_lhs.get_pointer<NonnullRefPtr<Expression>>()) { \ + lhs_result = (*ptr)->execute(interpreter, global_object); \ + if (interpreter.exception()) \ + return {}; \ + } \ + } while (0) + #define EXECUTE_LHS_AND_RHS() \ do { \ - lhs_result = m_lhs->execute(interpreter, global_object); \ - if (interpreter.exception()) \ - return {}; \ + EXECUTE_LHS(); \ rhs_result = m_rhs->execute(interpreter, global_object); \ if (interpreter.exception()) \ return {}; \ @@ -1548,25 +1555,19 @@ Value AssignmentExpression::execute(Interpreter& interpreter, GlobalObject& glob rhs_result = unsigned_right_shift(global_object, lhs_result, rhs_result); break; case AssignmentOp::AndAssignment: - lhs_result = m_lhs->execute(interpreter, global_object); - if (interpreter.exception()) - return {}; + EXECUTE_LHS(); if (!lhs_result.to_boolean()) return lhs_result; rhs_result = m_rhs->execute(interpreter, global_object); break; case AssignmentOp::OrAssignment: - lhs_result = m_lhs->execute(interpreter, global_object); - if (interpreter.exception()) - return {}; + EXECUTE_LHS(); if (lhs_result.to_boolean()) return lhs_result; rhs_result = m_rhs->execute(interpreter, global_object); break; case AssignmentOp::NullishAssignment: - lhs_result = m_lhs->execute(interpreter, global_object); - if (interpreter.exception()) - return {}; + EXECUTE_LHS(); if (!lhs_result.is_nullish()) return lhs_result; rhs_result = m_rhs->execute(interpreter, global_object); @@ -1575,26 +1576,42 @@ Value AssignmentExpression::execute(Interpreter& interpreter, GlobalObject& glob if (interpreter.exception()) return {}; - auto reference = m_lhs->to_reference(interpreter, global_object); - if (interpreter.exception()) - return {}; + return m_lhs.visit( + [&](NonnullRefPtr<Expression>& lhs) -> JS::Value { + auto reference = lhs->to_reference(interpreter, global_object); + if (interpreter.exception()) + return {}; - if (m_op == AssignmentOp::Assignment) { - rhs_result = m_rhs->execute(interpreter, global_object); - if (interpreter.exception()) - return {}; - } + if (m_op == AssignmentOp::Assignment) { + rhs_result = m_rhs->execute(interpreter, global_object); + if (interpreter.exception()) + return {}; + } - if (reference.is_unresolvable()) { - interpreter.vm().throw_exception<ReferenceError>(global_object, ErrorType::InvalidLeftHandAssignment); - return {}; - } + if (reference.is_unresolvable()) { + interpreter.vm().throw_exception<ReferenceError>(global_object, ErrorType::InvalidLeftHandAssignment); + return {}; + } - reference.put_value(global_object, rhs_result); - if (interpreter.exception()) - return {}; + reference.put_value(global_object, rhs_result); + if (interpreter.exception()) + return {}; - return rhs_result; + return rhs_result; + }, + [&](NonnullRefPtr<BindingPattern>& pattern) -> JS::Value { + VERIFY(m_op == AssignmentOp::Assignment); + + rhs_result = m_rhs->execute(interpreter, global_object); + if (interpreter.exception()) + return {}; + + interpreter.vm().assign(pattern, rhs_result, global_object); + if (interpreter.exception()) + return {}; + + return rhs_result; + }); } Value UpdateExpression::execute(Interpreter& interpreter, GlobalObject& global_object) const @@ -1693,7 +1710,7 @@ void AssignmentExpression::dump(int indent) const ASTNode::dump(indent); print_indent(indent + 1); outln("{}", op_string); - m_lhs->dump(indent + 1); + m_lhs.visit([&](auto& lhs) { lhs->dump(indent + 1); }); m_rhs->dump(indent + 1); } @@ -2395,5 +2412,4 @@ void ScopeNode::add_hoisted_function(NonnullRefPtr<FunctionDeclaration> hoisted_ { m_hoisted_functions.append(hoisted_function); } - } diff --git a/Userland/Libraries/LibJS/AST.h b/Userland/Libraries/LibJS/AST.h index 27e3a6ff19..f3f3ad0673 100644 --- a/Userland/Libraries/LibJS/AST.h +++ b/Userland/Libraries/LibJS/AST.h @@ -998,13 +998,21 @@ public: { } + AssignmentExpression(SourceRange source_range, AssignmentOp op, NonnullRefPtr<BindingPattern> lhs, NonnullRefPtr<Expression> rhs) + : Expression(source_range) + , m_op(op) + , m_lhs(move(lhs)) + , m_rhs(move(rhs)) + { + } + virtual Value execute(Interpreter&, GlobalObject&) const override; virtual void dump(int indent) const override; virtual void generate_bytecode(Bytecode::Generator&) const override; private: AssignmentOp m_op; - NonnullRefPtr<Expression> m_lhs; + Variant<NonnullRefPtr<Expression>, NonnullRefPtr<BindingPattern>> m_lhs; NonnullRefPtr<Expression> m_rhs; }; diff --git a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp index 6418a56a5d..768df2214c 100644 --- a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp +++ b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp @@ -272,8 +272,10 @@ void Identifier::generate_bytecode(Bytecode::Generator& generator) const void AssignmentExpression::generate_bytecode(Bytecode::Generator& generator) const { - if (is<Identifier>(*m_lhs)) { - auto& identifier = static_cast<Identifier const&>(*m_lhs); + // FIXME: Implement this for BindingPatterns too. + auto& lhs = m_lhs.get<NonnullRefPtr<Expression>>(); + if (is<Identifier>(*lhs)) { + auto& identifier = static_cast<Identifier const&>(*lhs); if (m_op == AssignmentOp::Assignment) { m_rhs->generate_bytecode(generator); @@ -281,7 +283,7 @@ void AssignmentExpression::generate_bytecode(Bytecode::Generator& generator) con return; } - m_lhs->generate_bytecode(generator); + lhs->generate_bytecode(generator); Bytecode::BasicBlock* rhs_block_ptr { nullptr }; Bytecode::BasicBlock* end_block_ptr { nullptr }; @@ -377,8 +379,8 @@ void AssignmentExpression::generate_bytecode(Bytecode::Generator& generator) con return; } - if (is<MemberExpression>(*m_lhs)) { - auto& expression = static_cast<MemberExpression const&>(*m_lhs); + if (is<MemberExpression>(*lhs)) { + auto& expression = static_cast<MemberExpression const&>(*lhs); expression.object().generate_bytecode(generator); auto object_reg = generator.allocate_register(); generator.emit<Bytecode::Op::Store>(object_reg); diff --git a/Userland/Libraries/LibJS/Lexer.cpp b/Userland/Libraries/LibJS/Lexer.cpp index 0263231065..daeffc53d6 100644 --- a/Userland/Libraries/LibJS/Lexer.cpp +++ b/Userland/Libraries/LibJS/Lexer.cpp @@ -20,7 +20,7 @@ HashMap<char, TokenType> Lexer::s_single_char_tokens; Lexer::Lexer(StringView source, StringView filename, size_t line_number, size_t line_column) : m_source(source) - , m_current_token(TokenType::Eof, {}, StringView(nullptr), StringView(nullptr), filename, 0, 0) + , m_current_token(TokenType::Eof, {}, StringView(nullptr), StringView(nullptr), filename, 0, 0, 0) , m_filename(filename) , m_line_number(line_number) , m_line_column(line_column) @@ -659,7 +659,8 @@ Token Lexer::next() m_source.substring_view(value_start - 1, m_position - value_start), m_filename, value_start_line_number, - value_start_column_number); + value_start_column_number, + m_position); if constexpr (LEXER_DEBUG) { dbgln("------------------------------"); diff --git a/Userland/Libraries/LibJS/Parser.cpp b/Userland/Libraries/LibJS/Parser.cpp index feaf0ec9bc..09d652b56b 100644 --- a/Userland/Libraries/LibJS/Parser.cpp +++ b/Userland/Libraries/LibJS/Parser.cpp @@ -1305,6 +1305,36 @@ NonnullRefPtr<AssignmentExpression> Parser::parse_assignment_expression(Assignme || match(TokenType::DoublePipeEquals) || match(TokenType::DoubleQuestionMarkEquals)); consume(); + + if (assignment_op == AssignmentOp::Assignment) { + auto synthesize_binding_pattern = [this](Expression const& expression) -> RefPtr<BindingPattern> { + // Clear any syntax error that has occurred in the range that 'expression' spans. + m_state.errors.remove_all_matching([range = expression.source_range()](auto const& error) { + return error.position.has_value() && range.contains(*error.position); + }); + // Make a parser and parse the source for this expression as a binding pattern. + auto source = m_state.lexer.source().substring_view(expression.source_range().start.offset - 2, expression.source_range().end.offset - expression.source_range().start.offset); + Lexer lexer { source, m_state.lexer.filename(), expression.source_range().start.line, expression.source_range().start.column }; + Parser parser { lexer }; + auto result = parser.parse_binding_pattern(); + if (parser.has_errors()) { + for (auto& error : parser.errors()) + syntax_error(move(error.message), move(error.position)); + } + return result; + }; + if (is<ArrayExpression>(*lhs) || is<ObjectExpression>(*lhs)) { + auto binding_pattern = synthesize_binding_pattern(*lhs); + if (binding_pattern) { + auto rhs = parse_expression(min_precedence, associativity); + return create_ast_node<AssignmentExpression>( + { m_state.current_token.filename(), rule_start.position(), position() }, + assignment_op, + binding_pattern.release_nonnull(), + move(rhs)); + } + } + } if (!is<Identifier>(*lhs) && !is<MemberExpression>(*lhs) && !is<CallExpression>(*lhs)) { syntax_error("Invalid left-hand side in assignment"); } else if (m_state.strict_mode && is<Identifier>(*lhs)) { @@ -2398,7 +2428,8 @@ Position Parser::position() const { return { m_state.current_token.line_number(), - m_state.current_token.line_column() + m_state.current_token.line_column(), + m_state.current_token.offset(), }; } diff --git a/Userland/Libraries/LibJS/SourceRange.h b/Userland/Libraries/LibJS/SourceRange.h index 936863ecc7..b92208dc32 100644 --- a/Userland/Libraries/LibJS/SourceRange.h +++ b/Userland/Libraries/LibJS/SourceRange.h @@ -13,9 +13,12 @@ namespace JS { struct Position { size_t line { 0 }; size_t column { 0 }; + size_t offset { 0 }; }; struct SourceRange { + [[nodiscard]] bool contains(Position const& position) const { return position.offset <= end.offset && position.offset >= start.offset; } + StringView filename; Position start; Position end; diff --git a/Userland/Libraries/LibJS/Token.h b/Userland/Libraries/LibJS/Token.h index bb3c2bb324..1a9033c8f1 100644 --- a/Userland/Libraries/LibJS/Token.h +++ b/Userland/Libraries/LibJS/Token.h @@ -161,7 +161,7 @@ enum class TokenCategory { class Token { public: - Token(TokenType type, String message, StringView trivia, StringView value, StringView filename, size_t line_number, size_t line_column) + Token(TokenType type, String message, StringView trivia, StringView value, StringView filename, size_t line_number, size_t line_column, size_t offset) : m_type(type) , m_message(message) , m_trivia(trivia) @@ -169,6 +169,7 @@ public: , m_filename(filename) , m_line_number(line_number) , m_line_column(line_column) + , m_offset(offset) { } @@ -184,6 +185,7 @@ public: const StringView& filename() const { return m_filename; } size_t line_number() const { return m_line_number; } size_t line_column() const { return m_line_column; } + size_t offset() const { return m_offset; } double double_value() const; bool bool_value() const; @@ -207,6 +209,7 @@ private: StringView m_filename; size_t m_line_number; size_t m_line_column; + size_t m_offset; }; } |