diff options
author | Andreas Kling <kling@serenityos.org> | 2020-04-03 12:14:28 +0200 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2020-04-03 12:15:14 +0200 |
commit | 0622181d1f883e787222bdbbbbd5f7ebf58c7a5a (patch) | |
tree | e23f767f8593954e03759e726d3205c1caec2f6a | |
parent | 522d8c5d7190bb97916c04dd1bcf0a1d71403191 (diff) | |
download | serenity-0622181d1f883e787222bdbbbbd5f7ebf58c7a5a.zip |
LibJS: Implement ConditionalExpression (ternary "?:" operator)
-rw-r--r-- | Libraries/LibJS/AST.cpp | 30 | ||||
-rw-r--r-- | Libraries/LibJS/AST.h | 20 | ||||
-rw-r--r-- | Libraries/LibJS/Parser.cpp | 14 | ||||
-rw-r--r-- | Libraries/LibJS/Parser.h | 1 | ||||
-rw-r--r-- | Libraries/LibJS/Tests/ternary-basic.js | 19 |
5 files changed, 83 insertions, 1 deletions
diff --git a/Libraries/LibJS/AST.cpp b/Libraries/LibJS/AST.cpp index f5fd51e366..38e44e1edb 100644 --- a/Libraries/LibJS/AST.cpp +++ b/Libraries/LibJS/AST.cpp @@ -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); +} + } diff --git a/Libraries/LibJS/AST.h b/Libraries/LibJS/AST.h index 5bc59aeca8..2c1d13b386 100644 --- a/Libraries/LibJS/AST.h +++ b/Libraries/LibJS/AST.h @@ -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) diff --git a/Libraries/LibJS/Parser.cpp b/Libraries/LibJS/Parser.cpp index 31da30916b..da735be71e 100644 --- a/Libraries/LibJS/Parser.cpp +++ b/Libraries/LibJS/Parser.cpp @@ -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 diff --git a/Libraries/LibJS/Parser.h b/Libraries/LibJS/Parser.h index c929672fbe..f4e759d4f5 100644 --- a/Libraries/LibJS/Parser.h +++ b/Libraries/LibJS/Parser.h @@ -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(); diff --git a/Libraries/LibJS/Tests/ternary-basic.js b/Libraries/LibJS/Tests/ternary-basic.js new file mode 100644 index 0000000000..62de660275 --- /dev/null +++ b/Libraries/LibJS/Tests/ternary-basic.js @@ -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); +} |