summaryrefslogtreecommitdiff
path: root/Userland
diff options
context:
space:
mode:
authorAli Mohammad Pur <ali.mpfard@gmail.com>2021-07-11 01:16:17 +0430
committerLinus Groh <mail@linusgroh.de>2021-07-11 21:41:54 +0100
commit1a9518ebe313f10091da92e73de76b4ab2f2d875 (patch)
tree0bcf7429be2941e314df5209f5ed8f7983f328fb /Userland
parent7fc6cd6b20ea0f9252c733de5c3249605a3bd130 (diff)
downloadserenity-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.cpp76
-rw-r--r--Userland/Libraries/LibJS/AST.h10
-rw-r--r--Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp12
-rw-r--r--Userland/Libraries/LibJS/Lexer.cpp5
-rw-r--r--Userland/Libraries/LibJS/Parser.cpp33
-rw-r--r--Userland/Libraries/LibJS/SourceRange.h3
-rw-r--r--Userland/Libraries/LibJS/Token.h5
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;
};
}