소스 검색

LibJS: Implement unary plus / minus

Linus Groh 5 년 전
부모
커밋
a62230770b

+ 10 - 0
Libraries/LibJS/AST.cpp

@@ -303,6 +303,10 @@ Value UnaryExpression::execute(Interpreter& interpreter) const
         return bitwise_not(lhs_result);
     case UnaryOp::Not:
         return Value(!lhs_result.to_boolean());
+    case UnaryOp::Plus:
+        return unary_plus(lhs_result);
+    case UnaryOp::Minus:
+        return unary_minus(lhs_result);
     case UnaryOp::Typeof:
         switch (lhs_result.type()) {
         case Value::Type::Undefined:
@@ -442,6 +446,12 @@ void UnaryExpression::dump(int indent) const
     case UnaryOp::Not:
         op_string = "!";
         break;
+    case UnaryOp::Plus:
+        op_string = "+";
+        break;
+    case UnaryOp::Minus:
+        op_string = "-";
+        break;
     case UnaryOp::Typeof:
         op_string = "typeof ";
         break;

+ 2 - 0
Libraries/LibJS/AST.h

@@ -353,6 +353,8 @@ private:
 enum class UnaryOp {
     BitwiseNot,
     Not,
+    Plus,
+    Minus,
     Typeof,
 };
 

+ 8 - 0
Libraries/LibJS/Parser.cpp

@@ -365,6 +365,12 @@ NonnullRefPtr<Expression> Parser::parse_unary_prefixed_expression()
     case TokenType::Tilde:
         consume();
         return create_ast_node<UnaryExpression>(UnaryOp::BitwiseNot, parse_expression(precedence, associativity));
+    case TokenType::Plus:
+        consume();
+        return create_ast_node<UnaryExpression>(UnaryOp::Plus, parse_expression(precedence, associativity));
+    case TokenType::Minus:
+        consume();
+        return create_ast_node<UnaryExpression>(UnaryOp::Minus, parse_expression(precedence, associativity));
     case TokenType::Typeof:
         consume();
         return create_ast_node<UnaryExpression>(UnaryOp::Typeof, parse_expression(precedence, associativity));
@@ -833,6 +839,8 @@ bool Parser::match_unary_prefixed_expression() const
         || type == TokenType::MinusMinus
         || type == TokenType::ExclamationMark
         || type == TokenType::Tilde
+        || type == TokenType::Plus
+        || type == TokenType::Minus
         || type == TokenType::Typeof;
 }
 

+ 12 - 0
Libraries/LibJS/Runtime/Value.cpp

@@ -191,6 +191,18 @@ Value bitwise_not(Value lhs)
     return Value(~(i32)lhs.to_number().as_double());
 }
 
+Value unary_plus(Value lhs)
+{
+    return lhs.to_number();
+}
+
+Value unary_minus(Value lhs)
+{
+    if (lhs.to_number().is_nan())
+        return js_nan();
+    return Value(-lhs.to_number().as_double());
+}
+
 Value left_shift(Value lhs, Value rhs)
 {
     return Value((i32)lhs.to_number().as_double() << (i32)rhs.to_number().as_double());

+ 2 - 0
Libraries/LibJS/Runtime/Value.h

@@ -181,6 +181,8 @@ Value bitwise_and(Value lhs, Value rhs);
 Value bitwise_or(Value lhs, Value rhs);
 Value bitwise_xor(Value lhs, Value rhs);
 Value bitwise_not(Value);
+Value unary_plus(Value);
+Value unary_minus(Value);
 Value left_shift(Value lhs, Value rhs);
 Value right_shift(Value lhs, Value rhs);
 Value add(Value lhs, Value rhs);

+ 1 - 1
Libraries/LibJS/Tests/Math.abs.js

@@ -2,7 +2,7 @@ function assert(x) { if (!x) throw 1; }
 
 try {
     assert(Math.abs('-1') === 1);
-    assert(Math.abs(0 - 2) === 2);
+    assert(Math.abs(-2) === 2);
     assert(Math.abs(null) === 0);
     assert(Math.abs('') === 0);
     assert(Math.abs([]) === 0);

+ 2 - 2
Libraries/LibJS/Tests/String.prototype.startsWith.js

@@ -22,7 +22,7 @@ try {
     assert(s.startsWith("foo", false) === true);
     assert(s.startsWith("foo", true) === false);
     assert(s.startsWith("foo", "foo") === true);
-    assert(s.startsWith("foo", 0 - 1) === true);
+    assert(s.startsWith("foo", -1) === true);
     assert(s.startsWith("foo", 42) === false);
     assert(s.startsWith("bar", 3) === true);
     assert(s.startsWith("bar", "3") === true);
@@ -31,7 +31,7 @@ try {
     assert(s.startsWith("") === true);
     assert(s.startsWith("", 0) === true);
     assert(s.startsWith("", 1) === true);
-    assert(s.startsWith("", 0 - 1) === true);
+    assert(s.startsWith("", -1) === true);
     assert(s.startsWith("", 42) === true);
 
     console.log("PASS");

+ 2 - 0
Libraries/LibJS/Tests/parser-unary-associativity.js

@@ -8,6 +8,8 @@ try {
     assert(!o.a === false);
     assert(!o.a === !(o.a));
     assert(~o.a === ~(o.a));
+    assert(+o.a === +(o.a));
+    assert(-o.a === -(o.a));
 
     assert((typeof "x" === "string") === true);
     assert(!(typeof "x" === "string") === false);

+ 34 - 22
Libraries/LibJS/Tests/to-number-basic.js

@@ -1,31 +1,43 @@
 function assert(x) { if (!x) throw 1; }
 
-// FIXME: Just "+x" doesn't parse currently,
-// so we use "x - 0", which is effectively the same.
-// "0 + x" would _not_ work in all cases.
-function n(x) { return x - 0; }
-
 try {
-    assert(n(false) === 0);
-    assert(n(true) === 1);
-    assert(n(null) === 0);
-    assert(n([]) === 0);
-    assert(n([[[[[]]]]]) === 0);
-    assert(n([[[[[42]]]]]) === 42);
-    assert(n("") === 0);
-    assert(n("42") === 42);
-    assert(n(42) === 42);
+    assert(+false === 0);
+    assert(-false === 0);
+    assert(+true === 1);
+    assert(-true === -1);
+    assert(+null === 0);
+    assert(-null === 0);
+    assert(+[] === 0);
+    assert(-[] === 0);
+    assert(+[[[[[]]]]] === 0);
+    assert(-[[[[[]]]]] === 0);
+    assert(+[[[[[42]]]]] === 42);
+    assert(-[[[[[42]]]]] === -42);
+    assert(+"" === 0);
+    assert(-"" === 0);
+    assert(+"42" === 42);
+    assert(-"42" === -42);
+    assert(+42 === 42);
+    assert(-42 === -42);
     // FIXME: returns NaN
-    // assert(n("1.23") === 1.23)
+    // assert(+"1.23" === 1.23)
+    // assert(-"1.23" === -1.23)
     // FIXME: chokes on ASSERT
-    // assert(n(1.23) === 1.23);
+    // assert(+1.23 === 1.23);
+    // assert(-1.23 === -1.23);
 
-    assert(isNaN(n(undefined)));
-    assert(isNaN(n({})));
-    assert(isNaN(n({a: 1})));
-    assert(isNaN(n([1, 2, 3])));
-    assert(isNaN(n([[[["foo"]]]])));
-    assert(isNaN(n("foo")));
+    assert(isNaN(+undefined));
+    assert(isNaN(-undefined));
+    assert(isNaN(+{}));
+    assert(isNaN(-{}));
+    assert(isNaN(+{a: 1}));
+    assert(isNaN(-{a: 1}));
+    assert(isNaN(+[1, 2, 3]));
+    assert(isNaN(-[1, 2, 3]));
+    assert(isNaN(+[[[["foo"]]]]));
+    assert(isNaN(-[[[["foo"]]]]));
+    assert(isNaN(+"foo"));
+    assert(isNaN(-"foo"));
 
     console.log("PASS");
 } catch (e) {