diff --git a/Libraries/LibJS/AST.cpp b/Libraries/LibJS/AST.cpp index 407ecd8caa..a06dc9b04e 100644 --- a/Libraries/LibJS/AST.cpp +++ b/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; diff --git a/Libraries/LibJS/AST.h b/Libraries/LibJS/AST.h index f03b04b111..1eef238e00 100644 --- a/Libraries/LibJS/AST.h +++ b/Libraries/LibJS/AST.h @@ -353,6 +353,8 @@ private: enum class UnaryOp { BitwiseNot, Not, + Plus, + Minus, Typeof, }; diff --git a/Libraries/LibJS/Parser.cpp b/Libraries/LibJS/Parser.cpp index 59db60e5ee..60102e37ee 100644 --- a/Libraries/LibJS/Parser.cpp +++ b/Libraries/LibJS/Parser.cpp @@ -365,6 +365,12 @@ NonnullRefPtr Parser::parse_unary_prefixed_expression() case TokenType::Tilde: consume(); return create_ast_node(UnaryOp::BitwiseNot, parse_expression(precedence, associativity)); + case TokenType::Plus: + consume(); + return create_ast_node(UnaryOp::Plus, parse_expression(precedence, associativity)); + case TokenType::Minus: + consume(); + return create_ast_node(UnaryOp::Minus, parse_expression(precedence, associativity)); case TokenType::Typeof: consume(); return create_ast_node(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; } diff --git a/Libraries/LibJS/Runtime/Value.cpp b/Libraries/LibJS/Runtime/Value.cpp index dfff936a88..6746aa621c 100644 --- a/Libraries/LibJS/Runtime/Value.cpp +++ b/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()); diff --git a/Libraries/LibJS/Runtime/Value.h b/Libraries/LibJS/Runtime/Value.h index d6517ffbb3..a341490976 100644 --- a/Libraries/LibJS/Runtime/Value.h +++ b/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); diff --git a/Libraries/LibJS/Tests/Math.abs.js b/Libraries/LibJS/Tests/Math.abs.js index 27ba82b35e..5224f168cb 100644 --- a/Libraries/LibJS/Tests/Math.abs.js +++ b/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); diff --git a/Libraries/LibJS/Tests/String.prototype.startsWith.js b/Libraries/LibJS/Tests/String.prototype.startsWith.js index b194816e43..38ac98d4a2 100644 --- a/Libraries/LibJS/Tests/String.prototype.startsWith.js +++ b/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"); diff --git a/Libraries/LibJS/Tests/parser-unary-associativity.js b/Libraries/LibJS/Tests/parser-unary-associativity.js index 240b20f61c..baa38e0850 100644 --- a/Libraries/LibJS/Tests/parser-unary-associativity.js +++ b/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); diff --git a/Libraries/LibJS/Tests/to-number-basic.js b/Libraries/LibJS/Tests/to-number-basic.js index e2482e8671..a2e87ce527 100644 --- a/Libraries/LibJS/Tests/to-number-basic.js +++ b/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) {