LibJS: Implement ConditionalExpression (ternary "?:" operator)

This commit is contained in:
Andreas Kling 2020-04-03 12:14:28 +02:00
parent 522d8c5d71
commit 0622181d1f
Notes: sideshowbarker 2024-07-19 07:58:43 +09:00
5 changed files with 83 additions and 1 deletions

View file

@ -970,4 +970,34 @@ void SwitchCase::dump(int indent) const
}
}
Value ConditionalExpression::execute(Interpreter& interpreter) const
{
auto test_result = m_test->execute(interpreter);
if (interpreter.exception())
return {};
Value result;
if (test_result.to_boolean()) {
result = m_consequent->execute(interpreter);
} else {
result = m_alternate->execute(interpreter);
}
if (interpreter.exception())
return {};
return result;
}
void ConditionalExpression::dump(int indent) const
{
ASTNode::dump(indent);
print_indent(indent);
printf("(Test)\n");
m_test->dump(indent + 1);
print_indent(indent);
printf("(Consequent)\n");
m_test->dump(indent + 1);
print_indent(indent);
printf("(Alternate)\n");
m_test->dump(indent + 1);
}
}

View file

@ -639,6 +639,26 @@ private:
bool m_computed { false };
};
class ConditionalExpression final : public Expression {
public:
ConditionalExpression(NonnullRefPtr<Expression> test, NonnullRefPtr<Expression> consequent, NonnullRefPtr<Expression> alternate)
: m_test(move(test))
, m_consequent(move(consequent))
, m_alternate(move(alternate))
{
}
virtual void dump(int indent) const override;
virtual Value execute(Interpreter&) const override;
private:
virtual const char* class_name() const override { return "ConditionalExpression"; }
NonnullRefPtr<Expression> m_test;
NonnullRefPtr<Expression> m_consequent;
NonnullRefPtr<Expression> m_alternate;
};
class CatchClause final : public ASTNode {
public:
CatchClause(const FlyString& parameter, NonnullRefPtr<BlockStatement> body)

View file

@ -516,6 +516,8 @@ 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::QuestionMark:
return parse_conditional_expression(move(lhs));
default:
m_parser_state.m_has_errors = true;
expected("secondary expression (missing switch case)");
@ -660,6 +662,15 @@ NonnullRefPtr<BreakStatement> Parser::parse_break_statement()
return create_ast_node<BreakStatement>();
}
NonnullRefPtr<ConditionalExpression> Parser::parse_conditional_expression(NonnullRefPtr<Expression> test)
{
consume(TokenType::QuestionMark);
auto consequent = parse_expression(0);
consume(TokenType::Colon);
auto alternate = parse_expression(0);
return create_ast_node<ConditionalExpression>(move(test), move(consequent), move(alternate));
}
NonnullRefPtr<TryStatement> Parser::parse_try_statement()
{
consume(TokenType::Try);
@ -865,7 +876,8 @@ bool Parser::match_secondary_expression() const
|| type == TokenType::BracketOpen
|| type == TokenType::PlusPlus
|| type == TokenType::MinusMinus
|| type == TokenType::Instanceof;
|| type == TokenType::Instanceof
|| type == TokenType::QuestionMark;
}
bool Parser::match_statement() const

View file

@ -58,6 +58,7 @@ public:
NonnullRefPtr<SwitchStatement> parse_switch_statement();
NonnullRefPtr<SwitchCase> parse_switch_case();
NonnullRefPtr<BreakStatement> parse_break_statement();
NonnullRefPtr<ConditionalExpression> parse_conditional_expression(NonnullRefPtr<Expression> test);
NonnullRefPtr<Expression> parse_expression(int min_precedence, Associativity associate = Associativity::Right);
NonnullRefPtr<Expression> parse_primary_expression();

View file

@ -0,0 +1,19 @@
function assert(x) { if (!x) throw 1; }
try {
var x = 1;
assert(x === 1 ? true : false);
assert(x ? x : 0);
assert(1 < 2 ? (true) : (false));
var o = {};
o.f = true;
assert(o.f ? true : false);
assert(1 ? o.f : null);
console.log("PASS");
} catch (e) {
console.log("FAIL: " + e);
}