1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-20 11:45:06 +00:00

LibJS: Implement nullish coalescing operator (??)

This commit is contained in:
Linus Groh 2020-04-18 00:49:11 +01:00 committed by Andreas Kling
parent 1806592d58
commit d14ddb6461
4 changed files with 60 additions and 12 deletions

View file

@ -338,20 +338,25 @@ Value LogicalExpression::execute(Interpreter& interpreter) const
auto rhs_result = m_rhs->execute(interpreter);
if (interpreter.exception())
return {};
return Value(rhs_result);
return rhs_result;
}
return Value(lhs_result);
case LogicalOp::Or:
return lhs_result;
case LogicalOp::Or: {
if (lhs_result.to_boolean())
return Value(lhs_result);
return lhs_result;
auto rhs_result = m_rhs->execute(interpreter);
if (interpreter.exception())
return {};
return Value(rhs_result);
return rhs_result;
}
case LogicalOp::NullishCoalescing:
if (lhs_result.is_null() || lhs_result.is_undefined()) {
auto rhs_result = m_rhs->execute(interpreter);
if (interpreter.exception())
return {};
return rhs_result;
}
return lhs_result;
}
ASSERT_NOT_REACHED();
@ -515,6 +520,9 @@ void LogicalExpression::dump(int indent) const
case LogicalOp::Or:
op_string = "||";
break;
case LogicalOp::NullishCoalescing:
op_string = "??";
break;
}
print_indent(indent);

View file

@ -367,6 +367,7 @@ private:
enum class LogicalOp {
And,
Or,
NullishCoalescing,
};
class LogicalExpression : public Expression {

View file

@ -601,6 +601,9 @@ NonnullRefPtr<Expression> Parser::parse_secondary_expression(NonnullRefPtr<Expre
case TokenType::DoublePipe:
consume();
return create_ast_node<LogicalExpression>(LogicalOp::Or, move(lhs), parse_expression(min_precedence, associativity));
case TokenType::DoubleQuestionMark:
consume();
return create_ast_node<LogicalExpression>(LogicalOp::NullishCoalescing, move(lhs), parse_expression(min_precedence, associativity));
case TokenType::QuestionMark:
return parse_conditional_expression(move(lhs));
default:
@ -1039,7 +1042,8 @@ bool Parser::match_secondary_expression() const
|| type == TokenType::Pipe
|| type == TokenType::Caret
|| type == TokenType::DoubleAmpersand
|| type == TokenType::DoublePipe;
|| type == TokenType::DoublePipe
|| type == TokenType::DoubleQuestionMark;
}
bool Parser::match_statement() const

View file

@ -17,7 +17,6 @@ try {
assert(("foo" && true) === true);
assert((false && "bar") === false);
assert((true && "bar") === "bar");
assert((null && true) === null);
assert((0 && false) === 0);
assert((0 && true) === 0);
assert((42 && false) === false);
@ -55,7 +54,6 @@ try {
assert(("foo" || true) === "foo");
assert((false || "bar") === "bar");
assert((true || "bar") === true);
assert((null || true) === true);
assert((0 || false) === false);
assert((0 || true) === true);
assert((42 || false) === 42);
@ -77,6 +75,43 @@ try {
assert((false || undefined) === undefined);
assert((true || undefined) === true);
assert((true ?? true) === true);
assert((false ?? false) === false);
assert((true ?? false) === true);
assert((false ?? true) === false);
assert((false ?? (1 === 2)) === false);
assert((true ?? (1 === 2)) === true);
assert(("" ?? "") === "");
assert(("" ?? false) === "");
assert(("" ?? true) === "");
assert((false ?? "") === false);
assert((true ?? "") === true);
assert(("foo" ?? "bar") === "foo");
assert(("foo" ?? false) === "foo");
assert(("foo" ?? true) === "foo");
assert((false ?? "bar") === false);
assert((true ?? "bar") === true);
assert((0 ?? false) === 0);
assert((0 ?? true) === 0);
assert((42 ?? false) === 42);
assert((42 ?? true) === 42);
assert((false ?? 0) === false);
assert((true ?? 0) === true);
assert((false ?? 42) === false);
assert((true ?? 42) === true);
assert(([] ?? false).length === 0);
assert(([] ?? true).length === 0);
assert((false ?? []) === false);
assert((true ?? []) === true);
assert((null ?? false) === false);
assert((null ?? true) === true);
assert((false ?? null) === false);
assert((true ?? null) === true);
assert((undefined ?? false) === false);
assert((undefined ?? true) === true);
assert((false ?? undefined) === false);
assert((true ?? undefined) === true);
console.log("PASS");
} catch (e) {
console.log("FAIL: " + e);