diff --git a/Libraries/LibGUI/JSSyntaxHighlighter.cpp b/Libraries/LibGUI/JSSyntaxHighlighter.cpp index 08769b2a56..30aa6d7e13 100644 --- a/Libraries/LibGUI/JSSyntaxHighlighter.cpp +++ b/Libraries/LibGUI/JSSyntaxHighlighter.cpp @@ -50,7 +50,6 @@ static TextStyle style_for_token_type(Gfx::Palette palette, JS::TokenType type) return { palette.syntax_string() }; case JS::TokenType::BracketClose: case JS::TokenType::BracketOpen: - case JS::TokenType::Caret: case JS::TokenType::Comma: case JS::TokenType::CurlyClose: case JS::TokenType::CurlyOpen: @@ -65,6 +64,8 @@ static TextStyle style_for_token_type(Gfx::Palette palette, JS::TokenType type) case JS::TokenType::Asterisk: case JS::TokenType::AsteriskAsteriskEquals: case JS::TokenType::AsteriskEquals: + case JS::TokenType::Caret: + case JS::TokenType::CaretEquals: case JS::TokenType::DoubleAmpersand: case JS::TokenType::DoubleAsterisk: case JS::TokenType::DoublePipe: diff --git a/Libraries/LibJS/AST.cpp b/Libraries/LibJS/AST.cpp index a153f7b70d..25b22d154a 100644 --- a/Libraries/LibJS/AST.cpp +++ b/Libraries/LibJS/AST.cpp @@ -850,6 +850,24 @@ Value AssignmentExpression::execute(Interpreter& interpreter) const return {}; rhs_result = div(interpreter, lhs_result, rhs_result); break; + case AssignmentOp::BitwiseAndAssignment: + lhs_result = m_lhs->execute(interpreter); + if (interpreter.exception()) + return {}; + rhs_result = bitwise_and(interpreter, lhs_result, rhs_result); + break; + case AssignmentOp::BitwiseOrAssignment: + lhs_result = m_lhs->execute(interpreter); + if (interpreter.exception()) + return {}; + rhs_result = bitwise_or(interpreter, lhs_result, rhs_result); + break; + case AssignmentOp::BitwiseXorAssignment: + lhs_result = m_lhs->execute(interpreter); + if (interpreter.exception()) + return {}; + rhs_result = bitwise_xor(interpreter, lhs_result, rhs_result); + break; case AssignmentOp::LeftShiftAssignment: lhs_result = m_lhs->execute(interpreter); if (interpreter.exception()) @@ -936,6 +954,15 @@ void AssignmentExpression::dump(int indent) const case AssignmentOp::DivisionAssignment: op_string = "/="; break; + case AssignmentOp::BitwiseAndAssignment: + op_string = "&="; + break; + case AssignmentOp::BitwiseOrAssignment: + op_string = "|="; + break; + case AssignmentOp::BitwiseXorAssignment: + op_string = "^="; + break; case AssignmentOp::LeftShiftAssignment: op_string = "<<="; break; diff --git a/Libraries/LibJS/AST.h b/Libraries/LibJS/AST.h index a07e79550f..a1c2c9960f 100644 --- a/Libraries/LibJS/AST.h +++ b/Libraries/LibJS/AST.h @@ -604,6 +604,9 @@ enum class AssignmentOp { SubtractionAssignment, MultiplicationAssignment, DivisionAssignment, + BitwiseAndAssignment, + BitwiseOrAssignment, + BitwiseXorAssignment, LeftShiftAssignment, RightShiftAssignment, UnsignedRightShiftAssignment, diff --git a/Libraries/LibJS/Lexer.cpp b/Libraries/LibJS/Lexer.cpp index 1c5c17046c..388ec424da 100644 --- a/Libraries/LibJS/Lexer.cpp +++ b/Libraries/LibJS/Lexer.cpp @@ -97,6 +97,7 @@ Lexer::Lexer(StringView source) s_two_char_tokens.set("%=", TokenType::PercentEquals); s_two_char_tokens.set("&=", TokenType::AmpersandEquals); s_two_char_tokens.set("|=", TokenType::PipeEquals); + s_two_char_tokens.set("^=", TokenType::CaretEquals); s_two_char_tokens.set("&&", TokenType::DoubleAmpersand); s_two_char_tokens.set("||", TokenType::DoublePipe); s_two_char_tokens.set("??", TokenType::DoubleQuestionMark); diff --git a/Libraries/LibJS/Parser.cpp b/Libraries/LibJS/Parser.cpp index e9d258bd53..64eb68a4c9 100644 --- a/Libraries/LibJS/Parser.cpp +++ b/Libraries/LibJS/Parser.cpp @@ -140,7 +140,9 @@ Parser::Parser(Lexer lexer) g_operator_precedence.set(TokenType::ShiftLeftEquals, 3); g_operator_precedence.set(TokenType::ShiftRightEquals, 3); g_operator_precedence.set(TokenType::UnsignedShiftRightEquals, 3); + g_operator_precedence.set(TokenType::AmpersandEquals, 3); g_operator_precedence.set(TokenType::PipeEquals, 3); + g_operator_precedence.set(TokenType::CaretEquals, 3); g_operator_precedence.set(TokenType::Yield, 2); @@ -668,12 +670,21 @@ NonnullRefPtr Parser::parse_secondary_expression(NonnullRefPtr(BinaryOp::BitwiseAnd, move(lhs), parse_expression(min_precedence, associativity)); + case TokenType::AmpersandEquals: + consume(); + return create_ast_node(AssignmentOp::BitwiseAndAssignment, move(lhs), parse_expression(min_precedence, associativity)); case TokenType::Pipe: consume(); return create_ast_node(BinaryOp::BitwiseOr, move(lhs), parse_expression(min_precedence, associativity)); + case TokenType::PipeEquals: + consume(); + return create_ast_node(AssignmentOp::BitwiseOrAssignment, move(lhs), parse_expression(min_precedence, associativity)); case TokenType::Caret: consume(); return create_ast_node(BinaryOp::BitwiseXor, move(lhs), parse_expression(min_precedence, associativity)); + case TokenType::CaretEquals: + consume(); + return create_ast_node(AssignmentOp::BitwiseXorAssignment, move(lhs), parse_expression(min_precedence, associativity)); case TokenType::ShiftLeft: consume(); return create_ast_node(BinaryOp::LeftShift, move(lhs), parse_expression(min_precedence, associativity)); @@ -1199,8 +1210,11 @@ bool Parser::match_secondary_expression() const || type == TokenType::Instanceof || type == TokenType::QuestionMark || type == TokenType::Ampersand + || type == TokenType::AmpersandEquals || type == TokenType::Pipe + || type == TokenType::PipeEquals || type == TokenType::Caret + || type == TokenType::CaretEquals || type == TokenType::ShiftLeft || type == TokenType::ShiftLeftEquals || type == TokenType::ShiftRight diff --git a/Libraries/LibJS/Tests/assignment-operators.js b/Libraries/LibJS/Tests/assignment-operators.js index 9f02675f46..23443186e4 100644 --- a/Libraries/LibJS/Tests/assignment-operators.js +++ b/Libraries/LibJS/Tests/assignment-operators.js @@ -23,6 +23,18 @@ try { assert((x /= 2) === 3); assert(x === 3); + x = 3; + assert((x &= 2) === 2); + assert(x === 2); + + x = 3; + assert((x |= 4) === 7); + assert(x === 7); + + x = 6; + assert((x ^= 2) === 4); + assert(x === 4); + x = 2; assert((x <<= 2) === 8); assert(x === 8); diff --git a/Libraries/LibJS/Token.h b/Libraries/LibJS/Token.h index b3242703d8..db0109c50d 100644 --- a/Libraries/LibJS/Token.h +++ b/Libraries/LibJS/Token.h @@ -44,6 +44,7 @@ namespace JS { __ENUMERATE_JS_TOKEN(BracketOpen) \ __ENUMERATE_JS_TOKEN(Break) \ __ENUMERATE_JS_TOKEN(Caret) \ + __ENUMERATE_JS_TOKEN(CaretEquals) \ __ENUMERATE_JS_TOKEN(Case) \ __ENUMERATE_JS_TOKEN(Catch) \ __ENUMERATE_JS_TOKEN(Class) \ diff --git a/Userland/js.cpp b/Userland/js.cpp index faf7ec8e01..36b71bcf91 100644 --- a/Userland/js.cpp +++ b/Userland/js.cpp @@ -556,7 +556,6 @@ int main(int argc, char** argv) break; case JS::TokenType::BracketClose: case JS::TokenType::BracketOpen: - case JS::TokenType::Caret: case JS::TokenType::Comma: case JS::TokenType::CurlyClose: case JS::TokenType::CurlyOpen: @@ -570,6 +569,8 @@ int main(int argc, char** argv) case JS::TokenType::Asterisk: case JS::TokenType::AsteriskAsteriskEquals: case JS::TokenType::AsteriskEquals: + case JS::TokenType::Caret: + case JS::TokenType::CaretEquals: case JS::TokenType::DoubleAmpersand: case JS::TokenType::DoubleAsterisk: case JS::TokenType::DoublePipe: