ソースを参照

LibJS: Implement bitwise right shift operator (>>)

Linus Groh 5 年 前
コミット
502d1f5165

+ 9 - 0
Libraries/LibJS/AST.cpp

@@ -755,6 +755,12 @@ Value AssignmentExpression::execute(Interpreter& interpreter) const
             return {};
         rhs_result = left_shift(interpreter, lhs_result, rhs_result);
         break;
+    case AssignmentOp::RightShiftAssignment:
+        lhs_result = m_lhs->execute(interpreter);
+        if (interpreter.exception())
+            return {};
+        rhs_result = right_shift(interpreter, lhs_result, rhs_result);
+        break;
     }
     if (interpreter.exception())
         return {};
@@ -825,6 +831,9 @@ void AssignmentExpression::dump(int indent) const
     case AssignmentOp::LeftShiftAssignment:
         op_string = "<<=";
         break;
+    case AssignmentOp::RightShiftAssignment:
+        op_string = ">>=";
+        break;
     }
 
     ASTNode::dump(indent);

+ 1 - 0
Libraries/LibJS/AST.h

@@ -568,6 +568,7 @@ enum class AssignmentOp {
     MultiplicationAssignment,
     DivisionAssignment,
     LeftShiftAssignment,
+    RightShiftAssignment,
 };
 
 class AssignmentExpression : public Expression {

+ 8 - 0
Libraries/LibJS/Parser.cpp

@@ -583,6 +583,12 @@ NonnullRefPtr<Expression> Parser::parse_secondary_expression(NonnullRefPtr<Expre
     case TokenType::ShiftLeftEquals:
         consume();
         return create_ast_node<AssignmentExpression>(AssignmentOp::LeftShiftAssignment, move(lhs), parse_expression(min_precedence, associativity));
+    case TokenType::ShiftRight:
+        consume();
+        return create_ast_node<BinaryExpression>(BinaryOp::RightShift, move(lhs), parse_expression(min_precedence, associativity));
+    case TokenType::ShiftRightEquals:
+        consume();
+        return create_ast_node<AssignmentExpression>(AssignmentOp::RightShiftAssignment, move(lhs), parse_expression(min_precedence, associativity));
     case TokenType::ParenOpen:
         return parse_call_expression(move(lhs));
     case TokenType::Equals:
@@ -1070,6 +1076,8 @@ bool Parser::match_secondary_expression() const
         || type == TokenType::Caret
         || type == TokenType::ShiftLeft
         || type == TokenType::ShiftLeftEquals
+        || type == TokenType::ShiftRight
+        || type == TokenType::ShiftRightEquals
         || type == TokenType::DoubleAmpersand
         || type == TokenType::DoublePipe
         || type == TokenType::DoubleQuestionMark;

+ 7 - 1
Libraries/LibJS/Runtime/Value.cpp

@@ -264,7 +264,13 @@ Value left_shift(Interpreter&, Value lhs, Value rhs)
 
 Value right_shift(Interpreter&, Value lhs, Value rhs)
 {
-    return Value((i32)lhs.to_number().as_double() >> (i32)rhs.to_number().as_double());
+    auto lhs_number = lhs.to_number();
+    if (!lhs_number.is_finite_number())
+        return Value(0);
+    auto rhs_number = rhs.to_number();
+    if (!rhs_number.is_finite_number())
+        return lhs_number;
+    return Value((i32)lhs_number.as_double() >> (i32)rhs_number.as_double());
 }
 
 Value add(Interpreter& interpreter, Value lhs, Value rhs)

+ 63 - 0
Libraries/LibJS/Tests/binary-bitwise-right-shift.js

@@ -0,0 +1,63 @@
+load("test-common.js");
+
+try {
+    assert((0 >> 0) === 0);
+    assert((0 >> 1) === 0);
+    assert((0 >> 2) === 0);
+    assert((0 >> 3) === 0);
+    assert((0 >> 4) === 0);
+    assert((0 >> 5) === 0);
+
+    assert((1 >> 0) === 1);
+    assert((1 >> 1) === 0);
+    assert((1 >> 2) === 0);
+    assert((1 >> 3) === 0);
+    assert((1 >> 4) === 0);
+    assert((1 >> 5) === 0);
+
+    assert((5 >> 0) === 5);
+    assert((5 >> 1) === 2);
+    assert((5 >> 2) === 1);
+    assert((5 >> 3) === 0);
+    assert((5 >> 4) === 0);
+    assert((5 >> 5) === 0);
+
+    assert((42 >> 0) === 42);
+    assert((42 >> 1) === 21);
+    assert((42 >> 2) === 10);
+    assert((42 >> 3) === 5);
+    assert((42 >> 4) === 2);
+    assert((42 >> 5) === 1);
+
+    assert((-1 >> 0) === -1);
+    assert((-1 >> 1) === -1);
+    assert((-1 >> 2) === -1);
+    assert((-1 >> 3) === -1);
+    assert((-1 >> 4) === -1);
+    assert((-1 >> 5) === -1);
+
+    assert((-5 >> 0) === -5);
+    assert((-5 >> 1) === -3);
+    assert((-5 >> 2) === -2);
+    assert((-5 >> 3) === -1);
+    assert((-5 >> 4) === -1);
+    assert((-5 >> 5) === -1);
+
+    var x = 67;
+    var y = 4;
+    assert(("42" >> 3) === 5);
+    assert((x >> y) === 4);
+    assert((x >> [[[[5]]]]) === 2);
+    assert((undefined >> y) === 0);
+    assert(("a" >> "b") === 0);
+    assert((null >> null) === 0);
+    assert((undefined >> undefined) === 0);
+    assert((NaN >> NaN) === 0);
+    assert((6 >> NaN) === 6);
+    assert((Infinity >> Infinity) === 0);
+    assert((-Infinity >> Infinity) === 0);
+
+    console.log("PASS");
+} catch (e) {
+    console.log("FAIL: " + e);
+}