Add ExpressionPrettyPrinter
Reviewers: teon.banek, mtomic Reviewed By: teon.banek Subscribers: pullbot Differential Revision: https://phabricator.memgraph.io/D1719
This commit is contained in:
parent
7ba8228c46
commit
06d4568950
@ -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
|
||||
|
@ -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);
|
||||
|
320
src/query/frontend/ast/pretty_print.cpp
Normal file
320
src/query/frontend/ast/pretty_print.cpp
Normal 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
|
12
src/query/frontend/ast/pretty_print.hpp
Normal file
12
src/query/frontend/ast/pretty_print.hpp
Normal 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
|
@ -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)
|
||||
|
||||
|
@ -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))
|
||||
|
139
tests/unit/query_pretty_print.cpp
Normal file
139
tests/unit/query_pretty_print.cpp
Normal 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
|
Loading…
Reference in New Issue
Block a user