Add ExpressionPrettyPrinter

Reviewers: teon.banek, mtomic

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D1719
This commit is contained in:
Lovro Lugovic 2018-11-07 10:54:35 +01:00
parent 7ba8228c46
commit 06d4568950
7 changed files with 520 additions and 2 deletions

View File

@ -28,6 +28,7 @@ set(mg_single_node_sources
glue/communication.cpp
query/common.cpp
query/frontend/ast/ast.cpp
query/frontend/ast/pretty_print.cpp
query/frontend/ast/cypher_main_visitor.cpp
query/frontend/semantic/required_privileges.cpp
query/frontend/semantic/symbol_generator.cpp
@ -132,6 +133,7 @@ set(mg_distributed_sources
glue/communication.cpp
query/common.cpp
query/frontend/ast/ast.cpp
query/frontend/ast/pretty_print.cpp
query/frontend/ast/cypher_main_visitor.cpp
query/frontend/semantic/required_privileges.cpp
query/frontend/semantic/symbol_generator.cpp

View File

@ -189,12 +189,14 @@ cpp<#
;;; Expressions
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(lcp:define-class expression (tree "::utils::Visitable<ExpressionVisitor<TypedValue>>")
(lcp:define-class expression (tree "::utils::Visitable<ExpressionVisitor<TypedValue>>"
"::utils::Visitable<ExpressionVisitor<void>>")
()
(:abstractp t)
(:public
#>cpp
using ::utils::Visitable<ExpressionVisitor<TypedValue>>::Accept;
using ::utils::Visitable<ExpressionVisitor<void>>::Accept;
using Tree::Accept;
Expression() = default;
@ -309,6 +311,7 @@ cpp<#
(let ((cpp-name (lcp::cpp-type-name ',op)))
#>cpp
DEFVISITABLE(ExpressionVisitor<TypedValue>);
DEFVISITABLE(ExpressionVisitor<void>);
bool Accept(HierarchicalTreeVisitor &visitor) override {
if (visitor.PreVisit(*this)) {
expression1_->Accept(visitor) && expression2_->Accept(visitor);
@ -340,6 +343,7 @@ cpp<#
(let ((cpp-name (lcp::cpp-type-name ',op)))
#>cpp
DEFVISITABLE(ExpressionVisitor<TypedValue>);
DEFVISITABLE(ExpressionVisitor<void>);
bool Accept(HierarchicalTreeVisitor &visitor) override {
if (visitor.PreVisit(*this)) {
expression_->Accept(visitor);
@ -382,6 +386,7 @@ cpp<#
}
DEFVISITABLE(ExpressionVisitor<TypedValue>);
DEFVISITABLE(ExpressionVisitor<void>);
bool Accept(HierarchicalTreeVisitor &visitor) override {
if (visitor.PreVisit(*this)) {
if (expression1_) expression1_->Accept(visitor);
@ -437,6 +442,7 @@ cpp<#
ListSlicingOperator() = default;
DEFVISITABLE(ExpressionVisitor<TypedValue>);
DEFVISITABLE(ExpressionVisitor<void>);
bool Accept(HierarchicalTreeVisitor &visitor) override {
if (visitor.PreVisit(*this)) {
bool cont = list_->Accept(visitor);
@ -491,6 +497,7 @@ cpp<#
IfOperator() = default;
DEFVISITABLE(ExpressionVisitor<TypedValue>);
DEFVISITABLE(ExpressionVisitor<void>);
bool Accept(HierarchicalTreeVisitor &visitor) override {
if (visitor.PreVisit(*this)) {
condition_->Accept(visitor) && then_expression_->Accept(visitor) &&
@ -556,6 +563,7 @@ cpp<#
PrimitiveLiteral() = default;
DEFVISITABLE(ExpressionVisitor<TypedValue>);
DEFVISITABLE(ExpressionVisitor<void>);
DEFVISITABLE(HierarchicalTreeVisitor);
PrimitiveLiteral *Clone(AstStorage &storage) const override {
@ -588,6 +596,7 @@ cpp<#
ListLiteral() = default;
DEFVISITABLE(ExpressionVisitor<TypedValue>);
DEFVISITABLE(ExpressionVisitor<void>);
bool Accept(HierarchicalTreeVisitor &visitor) override {
if (visitor.PreVisit(*this)) {
for (auto expr_ptr : elements_)
@ -627,6 +636,7 @@ cpp<#
MapLiteral() = default;
DEFVISITABLE(ExpressionVisitor<TypedValue>);
DEFVISITABLE(ExpressionVisitor<void>);
bool Accept(HierarchicalTreeVisitor &visitor) override {
if (visitor.PreVisit(*this)) {
for (auto pair : elements_)
@ -664,6 +674,7 @@ cpp<#
Identifier() = default;
DEFVISITABLE(ExpressionVisitor<TypedValue>);
DEFVISITABLE(ExpressionVisitor<void>);
DEFVISITABLE(HierarchicalTreeVisitor);
Identifier *Clone(AstStorage &storage) const override {
@ -694,6 +705,7 @@ cpp<#
PropertyLookup() = default;
DEFVISITABLE(ExpressionVisitor<TypedValue>);
DEFVISITABLE(ExpressionVisitor<void>);
bool Accept(HierarchicalTreeVisitor &visitor) override {
if (visitor.PreVisit(*this)) {
expression_->Accept(visitor);
@ -744,6 +756,7 @@ cpp<#
LabelsTest() = default;
DEFVISITABLE(ExpressionVisitor<TypedValue>);
DEFVISITABLE(ExpressionVisitor<void>);
bool Accept(HierarchicalTreeVisitor &visitor) override {
if (visitor.PreVisit(*this)) {
expression_->Accept(visitor);
@ -786,6 +799,7 @@ cpp<#
Function() = default;
DEFVISITABLE(ExpressionVisitor<TypedValue>);
DEFVISITABLE(ExpressionVisitor<void>);
bool Accept(HierarchicalTreeVisitor &visitor) override {
if (visitor.PreVisit(*this)) {
for (auto *argument : arguments_) {
@ -853,6 +867,7 @@ cpp<#
Reduce() = default;
DEFVISITABLE(ExpressionVisitor<TypedValue>);
DEFVISITABLE(ExpressionVisitor<void>);
bool Accept(HierarchicalTreeVisitor &visitor) override {
if (visitor.PreVisit(*this)) {
accumulator_->Accept(visitor) && initializer_->Accept(visitor) &&
@ -898,6 +913,7 @@ cpp<#
Coalesce() = default;
DEFVISITABLE(ExpressionVisitor<TypedValue>);
DEFVISITABLE(ExpressionVisitor<void>);
bool Accept(HierarchicalTreeVisitor &visitor) override {
if (visitor.PreVisit(*this)) {
for (auto *expr : expressions_) {
@ -947,6 +963,7 @@ cpp<#
Extract() = default;
DEFVISITABLE(ExpressionVisitor<TypedValue>);
DEFVISITABLE(ExpressionVisitor<void>);
bool Accept(HierarchicalTreeVisitor &visitor) override {
if (visitor.PreVisit(*this)) {
identifier_->Accept(visitor) && list_->Accept(visitor) &&
@ -994,6 +1011,7 @@ cpp<#
All() = default;
DEFVISITABLE(ExpressionVisitor<TypedValue>);
DEFVISITABLE(ExpressionVisitor<void>);
bool Accept(HierarchicalTreeVisitor &visitor) override {
if (visitor.PreVisit(*this)) {
identifier_->Accept(visitor) && list_expression_->Accept(visitor) &&
@ -1044,6 +1062,7 @@ cpp<#
Single() = default;
DEFVISITABLE(ExpressionVisitor<TypedValue>);
DEFVISITABLE(ExpressionVisitor<void>);
bool Accept(HierarchicalTreeVisitor &visitor) override {
if (visitor.PreVisit(*this)) {
identifier_->Accept(visitor) && list_expression_->Accept(visitor) &&
@ -1081,6 +1100,7 @@ cpp<#
ParameterLookup() = default;
DEFVISITABLE(ExpressionVisitor<TypedValue>);
DEFVISITABLE(ExpressionVisitor<void>);
DEFVISITABLE(HierarchicalTreeVisitor);
ParameterLookup *Clone(AstStorage &storage) const override {
@ -1099,7 +1119,8 @@ cpp<#
cpp<#)
(:serialize :capnp))
(lcp:define-class named-expression (tree "::utils::Visitable<ExpressionVisitor<TypedValue>>")
(lcp:define-class named-expression (tree "::utils::Visitable<ExpressionVisitor<TypedValue>>"
"::utils::Visitable<ExpressionVisitor<void>>")
((name "std::string" :scope :public)
(expression "Expression *" :initval "nullptr" :scope :public
:capnp-type "Tree" :capnp-init nil
@ -1110,10 +1131,12 @@ cpp<#
(:public
#>cpp
using ::utils::Visitable<ExpressionVisitor<TypedValue>>::Accept;
using ::utils::Visitable<ExpressionVisitor<void>>::Accept;
NamedExpression() = default;
DEFVISITABLE(ExpressionVisitor<TypedValue>);
DEFVISITABLE(ExpressionVisitor<void>);
bool Accept(HierarchicalTreeVisitor &visitor) override {
if (visitor.PreVisit(*this)) {
expression_->Accept(visitor);

View File

@ -0,0 +1,320 @@
#include "query/frontend/ast/pretty_print.hpp"
#include "query/frontend/ast/ast.hpp"
#include "utils/algorithm.hpp"
#include "utils/string.hpp"
namespace query {
namespace {
class ExpressionPrettyPrinter : public ExpressionVisitor<void> {
public:
explicit ExpressionPrettyPrinter(std::ostream *out);
// Unary operators
void Visit(NotOperator &op) override;
void Visit(UnaryPlusOperator &op) override;
void Visit(UnaryMinusOperator &op) override;
void Visit(IsNullOperator &op) override;
// Binary operators
void Visit(OrOperator &op) override;
void Visit(XorOperator &op) override;
void Visit(AndOperator &op) override;
void Visit(AdditionOperator &op) override;
void Visit(SubtractionOperator &op) override;
void Visit(MultiplicationOperator &op) override;
void Visit(DivisionOperator &op) override;
void Visit(ModOperator &op) override;
void Visit(NotEqualOperator &op) override;
void Visit(EqualOperator &op) override;
void Visit(LessOperator &op) override;
void Visit(GreaterOperator &op) override;
void Visit(LessEqualOperator &op) override;
void Visit(GreaterEqualOperator &op) override;
void Visit(InListOperator &op) override;
void Visit(SubscriptOperator &op) override;
// Other
void Visit(ListSlicingOperator &op) override;
void Visit(IfOperator &op) override;
void Visit(ListLiteral &op) override;
void Visit(MapLiteral &op) override;
void Visit(LabelsTest &op) override;
void Visit(Aggregation &op) override;
void Visit(Function &op) override;
void Visit(Reduce &op) override;
void Visit(Coalesce &op) override;
void Visit(Extract &op) override;
void Visit(All &op) override;
void Visit(Single &op) override;
void Visit(Identifier &op) override;
void Visit(PrimitiveLiteral &op) override;
void Visit(PropertyLookup &op) override;
void Visit(ParameterLookup &op) override;
void Visit(NamedExpression &op) override;
private:
std::ostream *out_;
};
// Declare all of the different `PrintObject` overloads upfront since they're
// mutually recursive. Without this, overload resolution depends on the ordering
// of the overloads within the source, which is quite fragile.
template <typename T>
void PrintObject(std::ostream *out, const T &arg);
void PrintObject(std::ostream *out, const std::string &str);
void PrintObject(std::ostream *out, Aggregation::Op op);
void PrintObject(std::ostream *out, Expression *expr);
void PrintObject(std::ostream *out, const PropertyValue &value);
template <typename T>
void PrintObject(std::ostream *out, const std::vector<T> &vec);
template <typename K, typename V>
void PrintObject(std::ostream *out, const std::map<K, V> &map);
void PrintObject(
std::ostream *out,
const std::unordered_map<std::pair<std::string, storage::Property>,
Expression *> &map);
template <typename T>
void PrintObject(std::ostream *out, const T &arg) {
*out << arg;
}
void PrintObject(std::ostream *out, const std::string &str) {
*out << utils::Escape(str);
}
void PrintObject(std::ostream *out, Aggregation::Op op) {
*out << Aggregation::OpToString(op);
}
void PrintObject(std::ostream *out, Expression *expr) {
if (expr) {
ExpressionPrettyPrinter printer{out};
expr->Accept(printer);
} else {
*out << "<null>";
}
}
void PrintObject(std::ostream *out, const PropertyValue &value) {
switch (value.type()) {
case PropertyValue::Type::Null:
*out << "null";
break;
case PropertyValue::Type::String:
PrintObject(out, value.Value<std::string>());
break;
case PropertyValue::Type::Bool:
*out << (value.Value<bool>() ? "true" : "false");
break;
case PropertyValue::Type::Int:
PrintObject(out, value.Value<std::int64_t>());
break;
case PropertyValue::Type::Double:
PrintObject(out, value.Value<double>());
break;
case PropertyValue::Type::List:
PrintObject(out, value.Value<std::vector<PropertyValue>>());
break;
case PropertyValue::Type::Map:
PrintObject(out, value.Value<std::map<std::string, PropertyValue>>());
break;
}
}
template <typename T>
void PrintObject(std::ostream *out, const std::vector<T> &vec) {
*out << "[";
utils::PrintIterable(*out, vec, ", ", [](auto &stream, const auto &item) {
PrintObject(&stream, item);
});
*out << "]";
}
template <typename K, typename V>
void PrintObject(std::ostream *out, const std::map<K, V> &map) {
*out << "{";
utils::PrintIterable(*out, map, ", ", [](auto &stream, const auto &item) {
PrintObject(&stream, item.first);
stream << ": ";
PrintObject(&stream, item.second);
});
*out << "}";
}
void PrintObject(
std::ostream *out,
const std::unordered_map<std::pair<std::string, storage::Property>,
Expression *> &map) {
*out << "{";
utils::PrintIterable(*out, map, ", ", [](auto &stream, const auto &item) {
PrintObject(&stream, item.first.first);
stream << ": ";
PrintObject(&stream, item.second);
});
*out << "}";
}
template <typename T>
void PrintOperatorArgs(std::ostream *out, const T &arg) {
*out << " ";
PrintObject(out, arg);
*out << ")";
}
template <typename T, typename... Ts>
void PrintOperatorArgs(std::ostream *out, const T &arg, const Ts &... args) {
*out << " ";
PrintObject(out, arg);
PrintOperatorArgs(out, args...);
}
template <typename... Ts>
void PrintOperator(std::ostream *out, const std::string &name,
const Ts &... args) {
*out << "(" << name;
PrintOperatorArgs(out, args...);
}
ExpressionPrettyPrinter::ExpressionPrettyPrinter(std::ostream *out)
: out_(out) {}
#define UNARY_OPERATOR_VISIT(OP_NODE, OP_STR) \
void ExpressionPrettyPrinter::Visit(OP_NODE &op) { \
PrintOperator(out_, OP_STR, op.expression_); \
}
UNARY_OPERATOR_VISIT(NotOperator, "Not");
UNARY_OPERATOR_VISIT(UnaryPlusOperator, "+");
UNARY_OPERATOR_VISIT(UnaryMinusOperator, "-");
UNARY_OPERATOR_VISIT(IsNullOperator, "IsNull");
#undef UNARY_OPERATOR_VISIT
#define BINARY_OPERATOR_VISIT(OP_NODE, OP_STR) \
void ExpressionPrettyPrinter::Visit(OP_NODE &op) { \
PrintOperator(out_, OP_STR, op.expression1_, op.expression2_); \
}
BINARY_OPERATOR_VISIT(OrOperator, "Or");
BINARY_OPERATOR_VISIT(XorOperator, "Xor");
BINARY_OPERATOR_VISIT(AndOperator, "And");
BINARY_OPERATOR_VISIT(AdditionOperator, "+");
BINARY_OPERATOR_VISIT(SubtractionOperator, "-");
BINARY_OPERATOR_VISIT(MultiplicationOperator, "*");
BINARY_OPERATOR_VISIT(DivisionOperator, "/");
BINARY_OPERATOR_VISIT(ModOperator, "%");
BINARY_OPERATOR_VISIT(NotEqualOperator, "!=");
BINARY_OPERATOR_VISIT(EqualOperator, "==");
BINARY_OPERATOR_VISIT(LessOperator, "<");
BINARY_OPERATOR_VISIT(GreaterOperator, ">");
BINARY_OPERATOR_VISIT(LessEqualOperator, "<=");
BINARY_OPERATOR_VISIT(GreaterEqualOperator, ">=");
BINARY_OPERATOR_VISIT(InListOperator, "In");
BINARY_OPERATOR_VISIT(SubscriptOperator, "Subscript");
#undef BINARY_OPERATOR_VISIT
void ExpressionPrettyPrinter::Visit(ListSlicingOperator &op) {
PrintOperator(out_, "ListSlicing", op.list_, op.lower_bound_,
op.upper_bound_);
}
void ExpressionPrettyPrinter::Visit(IfOperator &op) {
PrintOperator(out_, "If", op.condition_, op.then_expression_,
op.else_expression_);
}
void ExpressionPrettyPrinter::Visit(ListLiteral &op) {
PrintOperator(out_, "ListLiteral", op.elements_);
}
void ExpressionPrettyPrinter::Visit(MapLiteral &op) {
PrintObject(out_, op.elements_);
}
void ExpressionPrettyPrinter::Visit(LabelsTest &op) {
PrintOperator(out_, "LabelsTest", op.expression_);
}
void ExpressionPrettyPrinter::Visit(Aggregation &op) {
PrintOperator(out_, "Aggregation", op.op_);
}
void ExpressionPrettyPrinter::Visit(Function &op) {
PrintOperator(out_, "Function", op.function_name_, op.arguments_);
}
void ExpressionPrettyPrinter::Visit(Reduce &op) {
PrintOperator(out_, "Reduce", op.accumulator_, op.initializer_,
op.identifier_, op.list_, op.expression_);
}
void ExpressionPrettyPrinter::Visit(Coalesce &op) {
PrintOperator(out_, "Coalesce", op.expressions_);
}
void ExpressionPrettyPrinter::Visit(Extract &op) {
PrintOperator(out_, "Extract", op.identifier_, op.list_, op.expression_);
}
void ExpressionPrettyPrinter::Visit(All &op) {
PrintOperator(out_, "All", op.identifier_, op.list_expression_,
op.where_->expression_);
}
void ExpressionPrettyPrinter::Visit(Single &op) {
PrintOperator(out_, "Single", op.identifier_, op.list_expression_,
op.where_->expression_);
}
void ExpressionPrettyPrinter::Visit(Identifier &op) {
PrintOperator(out_, "Identifier", op.name_);
}
void ExpressionPrettyPrinter::Visit(PrimitiveLiteral &op) {
PrintObject(out_, op.value_);
}
void ExpressionPrettyPrinter::Visit(PropertyLookup &op) {
PrintOperator(out_, "PropertyLookup", op.expression_, op.property_name_);
}
void ExpressionPrettyPrinter::Visit(ParameterLookup &op) {
PrintOperator(out_, "ParameterLookup", op.token_position_);
}
void ExpressionPrettyPrinter::Visit(NamedExpression &op) {
PrintOperator(out_, "NamedExpression", op.name_, op.expression_);
}
} // namespace
void PrintExpression(Expression *expr, std::ostream *out) {
ExpressionPrettyPrinter printer{out};
expr->Accept(printer);
}
void PrintExpression(NamedExpression *expr, std::ostream *out) {
ExpressionPrettyPrinter printer{out};
expr->Accept(printer);
}
} // namespace query

View File

@ -0,0 +1,12 @@
#pragma once
#include <iostream>
#include "query/frontend/ast/ast.hpp"
namespace query {
void PrintExpression(Expression *expr, std::ostream *out);
void PrintExpression(NamedExpression *expr, std::ostream *out);
} // namespace query

View File

@ -165,6 +165,9 @@ target_link_libraries(${test_prefix}query_cost_estimator mg-single-node kvstore_
add_unit_test(query_expression_evaluator.cpp)
target_link_libraries(${test_prefix}query_expression_evaluator mg-single-node kvstore_dummy_lib)
add_unit_test(query_pretty_print.cpp)
target_link_libraries(${test_prefix}query_pretty_print mg-single-node kvstore_dummy_lib)
add_unit_test(query_plan_accumulate_aggregate.cpp)
target_link_libraries(${test_prefix}query_plan_accumulate_aggregate mg-single-node kvstore_dummy_lib)

View File

@ -24,12 +24,15 @@
#pragma once
#include <map>
#include <sstream>
#include <sstream>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
#include "query/frontend/ast/ast.hpp"
#include "query/frontend/ast/pretty_print.hpp"
#include "storage/common/types/types.hpp"
#include "utils/string.hpp"
@ -54,6 +57,18 @@ auto ToMap(const TypedValue &t) {
return map;
};
std::string ToString(Expression *expr) {
std::ostringstream ss;
PrintExpression(expr, &ss);
return ss.str();
}
std::string ToString(NamedExpression *expr) {
std::ostringstream ss;
PrintExpression(expr, &ss);
return ss.str();
}
// Custom types for ORDER BY, SKIP, LIMIT, ON MATCH and ON CREATE expressions,
// so that they can be used to resolve function calls.
struct OrderBy {
@ -553,6 +568,10 @@ auto GetMerge(AstStorage &storage, Pattern *pattern, OnMatch on_match,
query::test_common::GetCypherUnion(storage.Create<CypherUnion>(false), \
__VA_ARGS__)
// Various operators
#define NOT(expr) storage.Create<query::NotOperator>((expr))
#define UPLUS(expr) storage.Create<query::UnaryPlusOperator>((expr))
#define UMINUS(expr) storage.Create<query::UnaryMinusOperator>((expr))
#define IS_NULL(expr) storage.Create<query::IsNullOperator>((expr))
#define ADD(expr1, expr2) \
storage.Create<query::AdditionOperator>((expr1), (expr2))
#define LESS(expr1, expr2) storage.Create<query::LessOperator>((expr1), (expr2))

View File

@ -0,0 +1,139 @@
#include <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "database/single_node/graph_db_accessor.hpp"
#include "query/frontend/ast/ast.hpp"
#include "query/frontend/ast/pretty_print.hpp"
#include "query_common.hpp"
#include "utils/string.hpp"
using namespace query;
using query::test_common::ToList;
using query::test_common::ToString;
using testing::ElementsAre;
using testing::UnorderedElementsAre;
namespace {
struct ExpressionPrettyPrinterTest : public ::testing::Test {
ExpressionPrettyPrinterTest() : pdba{db.Access()}, dba{*pdba} {}
database::GraphDb db;
std::unique_ptr<database::GraphDbAccessor> pdba;
database::GraphDbAccessor &dba;
AstStorage storage;
};
TEST_F(ExpressionPrettyPrinterTest, Literals) {
// 1
EXPECT_EQ(ToString(LITERAL(1)), "1");
// "hello"
EXPECT_EQ(ToString(LITERAL("hello")), "\"hello\"");
// null
EXPECT_EQ(ToString(LITERAL(TypedValue::Null)), "null");
// true
EXPECT_EQ(ToString(LITERAL(true)), "true");
// false
EXPECT_EQ(ToString(LITERAL(false)), "false");
// [1 null "hello"]
EXPECT_EQ(ToString(LITERAL(
(std::vector<PropertyValue>{1, PropertyValue::Null, "hello"}))),
"[1, null, \"hello\"]");
// {hello: 1, there: 2}
EXPECT_EQ(ToString(LITERAL((std::map<std::string, PropertyValue>{
{"hello", 1}, {"there", 2}}))),
"{\"hello\": 1, \"there\": 2}");
}
TEST_F(ExpressionPrettyPrinterTest, UnaryOperators) {
// not(false)
EXPECT_EQ(ToString(NOT(LITERAL(false))), "(Not false)");
// +1
EXPECT_EQ(ToString(UPLUS(LITERAL(1))), "(+ 1)");
// -1
EXPECT_EQ(ToString(UMINUS(LITERAL(1))), "(- 1)");
// null IS NULL
EXPECT_EQ(ToString(IS_NULL(LITERAL(TypedValue::Null))), "(IsNull null)");
}
TEST_F(ExpressionPrettyPrinterTest, BinaryOperators) {
// and(null, 5)
EXPECT_EQ(ToString(AND(LITERAL(TypedValue::Null), LITERAL(5))),
"(And null 5)");
// or(5, {hello: "there"}["hello"])
EXPECT_EQ(
ToString(OR(LITERAL(5),
PROPERTY_LOOKUP(MAP(std::make_pair(PROPERTY_PAIR("hello"),
LITERAL("there"))),
PROPERTY_PAIR("hello")))),
"(Or 5 (PropertyLookup {\"hello\": \"there\"} \"hello\"))");
// and(coalesce(null, 1), {hello: "there"})
EXPECT_EQ(ToString(AND(
COALESCE(LITERAL(TypedValue::Null), LITERAL(1)),
MAP(std::make_pair(PROPERTY_PAIR("hello"), LITERAL("there"))))),
"(And (Coalesce [null, 1]) {\"hello\": \"there\"})");
}
TEST_F(ExpressionPrettyPrinterTest, Coalesce) {
// coalesce()
EXPECT_EQ(ToString(COALESCE()), "(Coalesce [])");
// coalesce(null, null)
EXPECT_EQ(
ToString(COALESCE(LITERAL(TypedValue::Null), LITERAL(TypedValue::Null))),
"(Coalesce [null, null])");
// coalesce(null, 2, 3)
EXPECT_EQ(
ToString(COALESCE(LITERAL(TypedValue::Null), LITERAL(2), LITERAL(3))),
"(Coalesce [null, 2, 3])");
// coalesce(null, 2, assert(false), 3)
EXPECT_EQ(ToString(COALESCE(LITERAL(TypedValue::Null), LITERAL(2),
FN("ASSERT", LITERAL(false)), LITERAL(3))),
"(Coalesce [null, 2, (Function \"ASSERT\" [false]), 3])");
// coalesce(null, assert(false))
EXPECT_EQ(ToString(COALESCE(LITERAL(TypedValue::Null),
FN("ASSERT", LITERAL(false)))),
"(Coalesce [null, (Function \"ASSERT\" [false])])");
// coalesce([null, null])
EXPECT_EQ(ToString(COALESCE(LITERAL(TypedValue(
std::vector<TypedValue>{TypedValue::Null, TypedValue::Null})))),
"(Coalesce [[null, null]])");
}
TEST_F(ExpressionPrettyPrinterTest, ParameterLookup) {
// and($hello, $there)
EXPECT_EQ(ToString(AND(PARAMETER_LOOKUP(1), PARAMETER_LOOKUP(2))),
"(And (ParameterLookup 1) (ParameterLookup 2))");
}
TEST_F(ExpressionPrettyPrinterTest, PropertyLookup) {
// {hello: "there"}["hello"]
EXPECT_EQ(ToString(PROPERTY_LOOKUP(
MAP(std::make_pair(PROPERTY_PAIR("hello"), LITERAL("there"))),
PROPERTY_PAIR("hello"))),
"(PropertyLookup {\"hello\": \"there\"} \"hello\")");
}
TEST_F(ExpressionPrettyPrinterTest, NamedExpression) {
// n AS 1
EXPECT_EQ(ToString(NEXPR("n", LITERAL(1))), "(NamedExpression \"n\" 1)");
}
} // namespace