summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Libraries/LibJS/AST.cpp30
-rw-r--r--Libraries/LibJS/AST.h20
-rw-r--r--Libraries/LibJS/Parser.cpp14
-rw-r--r--Libraries/LibJS/Parser.h1
-rw-r--r--Libraries/LibJS/Tests/ternary-basic.js19
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);
+}