diff --git a/CMakeLists.txt b/CMakeLists.txt index 72999f286..4327311e1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -349,6 +349,7 @@ set(memgraph_src_files ${src_dir}/database/graph_db_accessor.cpp ${src_dir}/query/stripper.cpp ${src_dir}/query/frontend/ast/cypher_main_visitor.cpp + ${src_dir}/query/context.cpp ${src_dir}/query/backend/cpp/typed_value.cpp ${src_dir}/query/frontend/ast/ast.cpp ) diff --git a/src/query/backend/cpp/generator.hpp b/src/query/backend/cpp/generator.hpp index d823cf4ac..df3f08236 100644 --- a/src/query/backend/cpp/generator.hpp +++ b/src/query/backend/cpp/generator.hpp @@ -1,8 +1,8 @@ #pragma once -#include <experimental/filesystem> #include "antlr4-runtime.h" -#include "query/backend/cpp/cypher_main_visitor.hpp" +#include "query/frontend/ast/cypher_main_visitor.hpp" +#include <experimental/filesystem> namespace fs = std::experimental::filesystem; @@ -13,8 +13,8 @@ namespace cpp { using namespace antlr4; class GeneratorException : public BasicException { - public: - using BasicException::BasicException; +public: + using BasicException::BasicException; GeneratorException() : BasicException("") {} }; @@ -23,7 +23,7 @@ class GeneratorException : public BasicException { * C++. */ class Generator { - public: +public: /** * Generates cpp code inside file on the path. */ diff --git a/src/query/context.cpp b/src/query/context.cpp new file mode 100644 index 000000000..177a37cb7 --- /dev/null +++ b/src/query/context.cpp @@ -0,0 +1,11 @@ +#include "query/context.hpp" +#include "query/frontend/ast/cypher_main_visitor.hpp" + +namespace query { + +void HighLevelAstConversion::Apply(Context &ctx, + antlr4::tree::ParseTree *tree) { + query::frontend::CypherMainVisitor visitor(ctx); + visitor.visit(tree); +} +} diff --git a/src/query/context.hpp b/src/query/context.hpp index 92e4b609e..4c73f17ad 100644 --- a/src/query/context.hpp +++ b/src/query/context.hpp @@ -2,7 +2,8 @@ #include "antlr4-runtime.h" #include "database/graph_db_accessor.hpp" -#include "query/frontend/ast/cypher_main_visitor.hpp" + +namespace query { class TypedcheckedTree {}; @@ -11,8 +12,8 @@ class LogicalPlan {}; class Context; class LogicalPlanGenerator { - public: - std::vector<LogicalPlan> Generate(TypedcheckedTree&, Context&) { +public: + std::vector<LogicalPlan> Generate(TypedcheckedTree &, Context &) { return {LogicalPlan()}; } }; @@ -22,32 +23,31 @@ struct Config { }; class Context { - public: - int uid_counter; - Context(Config config, GraphDbAccessor& db_accessor) - : config(config), db_accessor(db_accessor) {} +public: + Context(Config config, GraphDbAccessor &db_accessor) + : config_(config), db_accessor_(db_accessor) {} + int next_uid() { return uid_counter_++; } - Config config; - GraphDbAccessor& db_accessor; + Config config_; + GraphDbAccessor &db_accessor_; + int uid_counter_ = 0; }; class LogicalPlanner { - public: +public: LogicalPlanner(Context ctx) : ctx_(ctx) {} LogicalPlan Apply(TypedcheckedTree typedchecked_tree) { - return ctx_.config.logical_plan_generator.Generate(typedchecked_tree, - ctx_)[0]; + return ctx_.config_.logical_plan_generator.Generate(typedchecked_tree, + ctx_)[0]; } - private: +private: Context ctx_; }; class HighLevelAstConversion { - public: - void Apply(const Context& ctx, antlr4::tree::ParseTree* tree) { - query::frontend::CypherMainVisitor visitor(ctx); - visitor.visit(tree); - } +public: + void Apply(Context &ctx, antlr4::tree::ParseTree *tree); }; +} diff --git a/src/query/engine.hpp b/src/query/engine.hpp index 1f1fba69a..d7ae8445c 100644 --- a/src/query/engine.hpp +++ b/src/query/engine.hpp @@ -26,12 +26,11 @@ namespace fs = std::experimental::filesystem; * the results should be returned (more optimal then just return * the whole result set) */ -template <typename Stream> -class QueryEngine : public Loggable { - private: +template <typename Stream> class QueryEngine : public Loggable { +private: using QueryPlanLib = DynamicLib<QueryPlanTrait<Stream>>; - public: +public: QueryEngine() : Loggable("QueryEngine") {} /** @@ -104,12 +103,12 @@ class QueryEngine : public Loggable { * * @return size_t the number of loaded query plans */ - auto Size() { // TODO: const once whan ConcurrentMap::Accessor becomes const + auto Size() { // TODO: const once whan ConcurrentMap::Accessor becomes const return query_plans.access().size(); } // return query_plans.access().size(); } - private: +private: /** * Loads query plan eather from hardcoded folder or from the file that is * generated in this method. @@ -176,7 +175,7 @@ class QueryEngine : public Loggable { // TODO: underlying object has to be live during query execution // fix that when Antler will be introduced into the database - auto query_plan_instance = query_plan->instance(); // because of move + auto query_plan_instance = query_plan->instance(); // because of move plans_accessor.insert(hash, std::move(query_plan)); // return an instance of runnable code (PlanInterface) diff --git a/src/query/frontend/ast/ast.cpp b/src/query/frontend/ast/ast.cpp index a6a32fbeb..0b961d6c1 100644 --- a/src/query/frontend/ast/ast.cpp +++ b/src/query/frontend/ast/ast.cpp @@ -3,11 +3,11 @@ namespace query { -TypedValue Ident::Evaluate(Frame& frame, SymbolTable& symbol_table) { +TypedValue Ident::Evaluate(Frame &frame, SymbolTable &symbol_table) { return frame[symbol_table[*this].position_]; } -void NamedExpr::Evaluate(Frame& frame, SymbolTable& symbol_table) { +void NamedExpr::Evaluate(Frame &frame, SymbolTable &symbol_table) { frame[symbol_table[*ident_].position_] = expr_->Evaluate(frame, symbol_table); } } diff --git a/src/query/frontend/ast/ast.hpp b/src/query/frontend/ast/ast.hpp index 1022b0ed9..b7617a746 100644 --- a/src/query/frontend/ast/ast.hpp +++ b/src/query/frontend/ast/ast.hpp @@ -22,139 +22,161 @@ class NodePart; class EdgePart; class TreeVisitorBase { - public: +public: // Start of the tree is a Query. - virtual void PreVisit(Query& query) {} - virtual void Visit(Query& query) = 0; - virtual void PostVisit(Query& query) {} + virtual void PreVisit(Query &) {} + virtual void Visit(Query &query) = 0; + virtual void PostVisit(Query &) {} // Expressions - virtual void PreVisit(NamedExpr&) {} - virtual void Visit(NamedExpr&) = 0; - virtual void PostVisit(NamedExpr&) {} - virtual void PreVisit(Ident& ident) {} - virtual void Visit(Ident& ident) = 0; - virtual void PostVisit(Ident& ident) {} + virtual void PreVisit(NamedExpr &) {} + virtual void Visit(NamedExpr &) = 0; + virtual void PostVisit(NamedExpr &) {} + virtual void PreVisit(Ident &) {} + virtual void Visit(Ident &ident) = 0; + virtual void PostVisit(Ident &) {} // Clauses - virtual void PreVisit(Match& match) {} - virtual void Visit(Match& match) = 0; - virtual void PostVisit(Match& match) {} - virtual void PreVisit(Return& ret) {} - virtual void Visit(Return& ret) = 0; - virtual void PostVisit(Return& ret) {} + virtual void PreVisit(Match &) {} + virtual void Visit(Match &match) = 0; + virtual void PostVisit(Match &) {} + virtual void PreVisit(Return &) {} + virtual void Visit(Return &ret) = 0; + virtual void PostVisit(Return &) {} // Pattern and its subparts. - virtual void PreVisit(Pattern& pattern) {} - virtual void Visit(Pattern& pattern) = 0; - virtual void PostVisit(Pattern& pattern) {} - virtual void PreVisit(NodePart& node_part) {} - virtual void Visit(NodePart& node_part) = 0; - virtual void PostVisit(NodePart& node_part) {} - virtual void PreVisit(EdgePart& edge_part) {} - virtual void Visit(EdgePart& edge_part) = 0; - virtual void PostVisit(EdgePart& edge_part) {} + virtual void PreVisit(Pattern &) {} + virtual void Visit(Pattern &pattern) = 0; + virtual void PostVisit(Pattern &) {} + virtual void PreVisit(NodePart &) {} + virtual void Visit(NodePart &node_part) = 0; + virtual void PostVisit(NodePart &) {} + virtual void PreVisit(EdgePart &) {} + virtual void Visit(EdgePart &edge_part) = 0; + virtual void PostVisit(EdgePart &) {} }; class Tree { - public: - Tree(const int uid) : uid_(uid) {} +public: + Tree(int uid) : uid_(uid) {} int uid() const { return uid_; } - virtual void Accept(TreeVisitorBase& visitor) = 0; + virtual void Accept(TreeVisitorBase &visitor) = 0; - private: +private: const int uid_; }; class Expr : public Tree { - public: - virtual TypedValue Evaluate(Frame&, SymbolTable&) = 0; +public: + Expr(int uid) : Tree(uid) {} + virtual TypedValue Evaluate(Frame &, SymbolTable &) = 0; }; class Ident : public Expr { - public: - std::string identifier_; - TypedValue Evaluate(Frame& frame, SymbolTable& symbol_table) override; - void Accept(TreeVisitorBase& visitor) override { +public: + Ident(int) = delete; + Ident(int uid, const std::string &identifier) + : Expr(uid), identifier_(identifier) {} + + TypedValue Evaluate(Frame &frame, SymbolTable &symbol_table) override; + void Accept(TreeVisitorBase &visitor) override { visitor.PreVisit(*this); visitor.Visit(*this); visitor.PostVisit(*this); } + + std::string identifier_; }; -class Part : public Tree {}; +class Part : public Tree { +public: + Part(int uid) : Tree(uid) {} +}; class NamedExpr : public Tree { - public: - std::shared_ptr<Ident> ident_; - std::shared_ptr<Expr> expr_; - void Evaluate(Frame& frame, SymbolTable& symbol_table); - void Accept(TreeVisitorBase& visitor) override { +public: + NamedExpr(int uid) : Tree(uid) {} + void Evaluate(Frame &frame, SymbolTable &symbol_table); + void Accept(TreeVisitorBase &visitor) override { visitor.PreVisit(*this); ident_->Accept(visitor); expr_->Accept(visitor); visitor.Visit(*this); visitor.PostVisit(*this); } + + std::shared_ptr<Ident> ident_; + std::shared_ptr<Expr> expr_; }; class NodePart : public Part { - public: - Ident identifier_; - // TODO: Mislav call GraphDb::label(label_name) to populate labels_! - std::vector<GraphDb::Label> labels_; - // TODO: properties - void Accept(TreeVisitorBase& visitor) override { +public: + NodePart(int uid) : Part(uid) {} + void Accept(TreeVisitorBase &visitor) override { visitor.PreVisit(*this); - identifier_.Accept(visitor); + identifier_->Accept(visitor); visitor.Visit(*this); visitor.PostVisit(*this); } + + std::shared_ptr<Ident> identifier_; + std::vector<GraphDb::Label> labels_; }; class EdgePart : public Part { - public: - Ident identifier_; - // TODO: finish this: properties, types... - void Accept(TreeVisitorBase& visitor) override { +public: + enum class Direction { LEFT, RIGHT, BOTH }; + + EdgePart(int uid) : Part(uid) {} + void Accept(TreeVisitorBase &visitor) override { visitor.PreVisit(*this); - identifier_.Accept(visitor); + identifier_->Accept(visitor); visitor.Visit(*this); visitor.PostVisit(*this); } + + Direction direction = Direction::BOTH; + std::shared_ptr<Ident> identifier_; }; -class Clause : public Tree {}; +class Clause : public Tree { +public: + Clause(int uid) : Tree(uid) {} +}; class Pattern : public Tree { - public: - std::vector<std::shared_ptr<NodePart>> node_parts_; - void Accept(TreeVisitorBase& visitor) override { +public: + Pattern(int uid) : Tree(uid) {} + void Accept(TreeVisitorBase &visitor) override { visitor.PreVisit(*this); - for (auto& node_part : node_parts_) { - node_part->Accept(visitor); + for (auto &part : parts_) { + part->Accept(visitor); } visitor.Visit(*this); visitor.PostVisit(*this); } + std::shared_ptr<Ident> identifier_; + std::vector<std::shared_ptr<Part>> parts_; }; class Query : public Tree { - public: - std::vector<std::unique_ptr<Clause>> clauses_; - void Accept(TreeVisitorBase& visitor) override { +public: + Query(int uid) : Tree(uid) {} + void Accept(TreeVisitorBase &visitor) override { visitor.PreVisit(*this); - for (auto& clause : clauses_) { + for (auto &clause : clauses_) { clause->Accept(visitor); } visitor.Visit(*this); visitor.PostVisit(*this); } + std::vector<std::shared_ptr<Clause>> clauses_; }; class Match : public Clause { - public: - std::vector<std::unique_ptr<Pattern>> patterns_; - void Accept(TreeVisitorBase& visitor) override { +public: + Match(int uid) : Clause(uid) {} + std::vector<std::shared_ptr<Pattern>> patterns_; + void Accept(TreeVisitorBase &visitor) override { visitor.PreVisit(*this); - for (auto& pattern : patterns_) { + for (auto &pattern : patterns_) { pattern->Accept(visitor); } visitor.Visit(*this); @@ -163,15 +185,19 @@ class Match : public Clause { }; class Return : public Clause { - public: +public: + Return(int uid) : Clause(uid) {} std::vector<std::shared_ptr<NamedExpr>> exprs_; - void Accept(TreeVisitorBase& visitor) override { + void Accept(TreeVisitorBase &visitor) override { visitor.PreVisit(*this); - for (auto& expr : exprs_) { + for (auto &expr : exprs_) { expr->Accept(visitor); } visitor.Visit(*this); visitor.PostVisit(*this); } + + std::shared_ptr<Ident> identifier_; + std::vector<std::shared_ptr<NamedExpr>> named_exprs_; }; } diff --git a/src/query/frontend/ast/cypher_main_visitor.cpp b/src/query/frontend/ast/cypher_main_visitor.cpp index c59e9e1d0..cd94df5cb 100644 --- a/src/query/frontend/ast/cypher_main_visitor.cpp +++ b/src/query/frontend/ast/cypher_main_visitor.cpp @@ -6,103 +6,132 @@ #include <utility> #include <vector> -#include "query/backend/cpp/compiler_structures.hpp" +#include "database/graph_db.hpp" #include "query/frontend/ast/named_antlr_tokens.hpp" #include "utils/assert.hpp" -namespace backend { -namespace cpp { +namespace query { +namespace frontend { namespace { // Map children tokens of antlr node to Function enum. -std::vector<Function> MapTokensToOperators( - antlr4::ParserRuleContext *node, - const std::unordered_map<size_t, Function> token_to_operator) { - std::vector<antlr4::tree::TerminalNode *> tokens; - for (const auto &x : token_to_operator) { - tokens.insert(tokens.end(), node->getTokens(x.first).begin(), - node->getTokens(x.first).end()); - } - sort(tokens.begin(), tokens.end(), [](antlr4::tree::TerminalNode *a, - antlr4::tree::TerminalNode *b) { - return a->getSourceInterval().startsBeforeDisjoint(b->getSourceInterval()); - }); - std::vector<Function> ops; - for (auto *token : tokens) { - auto it = token_to_operator.find(token->getSymbol()->getType()); - debug_assert(it != token_to_operator.end(), - "Wrong mapping sent to function."); - ops.push_back(it->second); - } - return ops; -} +// std::vector<Function> MapTokensToOperators( +// antlr4::ParserRuleContext *node, +// const std::unordered_map<size_t, Function> token_to_operator) { +// std::vector<antlr4::tree::TerminalNode *> tokens; +// for (const auto &x : token_to_operator) { +// tokens.insert(tokens.end(), node->getTokens(x.first).begin(), +// node->getTokens(x.first).end()); +// } +// sort(tokens.begin(), tokens.end(), [](antlr4::tree::TerminalNode *a, +// antlr4::tree::TerminalNode *b) { +// return +// a->getSourceInterval().startsBeforeDisjoint(b->getSourceInterval()); +// }); +// std::vector<Function> ops; +// for (auto *token : tokens) { +// auto it = token_to_operator.find(token->getSymbol()->getType()); +// debug_assert(it != token_to_operator.end(), +// "Wrong mapping sent to function."); +// ops.push_back(it->second); +// } +// return ops; +//} } -antlrcpp::Any CypherMainVisitor::visitNodePattern( - CypherParser::NodePatternContext *ctx) { - bool new_node = true; - Node node; - if (ctx->variable()) { - auto variable = ctx->variable()->accept(this).as<std::string>(); - auto &curr_id_map = ids_map_.back(); - if (curr_id_map.find(variable) != curr_id_map.end()) { - if (!symbol_table_[curr_id_map[variable]].is<Node>()) { - throw SemanticException(); - } - new_node = false; - node = symbol_table_[curr_id_map[variable]].as<Node>(); - } else { - node.output_id = new_id(); - curr_id_map[variable] = node.output_id; - } - } else { - node.output_id = new_id(); +antlrcpp::Any +CypherMainVisitor::visitSingleQuery(CypherParser::SingleQueryContext *ctx) { + auto children = ctx->children; + query_ = std::make_shared<Query>(ctx_.next_uid()); + for (auto *child : children) { + query_->clauses_.push_back(child->accept(this)); } - if (!new_node && (ctx->nodeLabels() || ctx->properties())) { - // If variable is already declared, we cannot list properties or labels. - // This is slightly incompatible with neo4j. In neo4j it is valid to write - // MATCH (n {a: 5})--(n {b: 10}) which is equivalent to MATCH(n {a:5, b: - // 10})--(n). Neo4j also allows MATCH (n) RETURN (n {x: 5}) which is - // equivalent to MATCH (n) RETURN ({x: 5}). - // TODO: The way in which we are storing nodes is not suitable for optional - // match. For example: MATCH (n {a: 5}) OPTIONAL MATCH (n {b: 10}) RETURN - // n.a, n.b. would not work. Think more about that case. - throw SemanticException(); + return query_; +} + +antlrcpp::Any +CypherMainVisitor::visitCypherMatch(CypherParser::CypherMatchContext *ctx) { + auto match = std::make_shared<Match>(ctx_.next_uid()); + if (ctx->OPTIONAL() || ctx->where()) { + throw std::exception(); + } + match->patterns_ = + ctx->pattern()->accept(this).as<std::vector<std::shared_ptr<Pattern>>>(); + return match; +} + +antlrcpp::Any +CypherMainVisitor::visitReturnItems(CypherParser::ReturnItemsContext *ctx) { + auto return_clause = std::make_shared<Return>(ctx_.next_uid()); + for (auto *item : ctx->returnItem()) { + return_clause->named_exprs_.push_back(item->accept(this)); + } + return return_clause; +} + +antlrcpp::Any +CypherMainVisitor::visitReturnItem(CypherParser::ReturnItemContext *ctx) { + auto named_expr = std::make_shared<NamedExpr>(ctx_.next_uid()); + if (ctx->variable()) { + named_expr->ident_ = + std::make_shared<Ident>(ctx_.next_uid(), ctx->variable()->accept(this)); + } else { + named_expr->ident_ = + std::make_shared<Ident>(ctx_.next_uid(), ctx->getText()); + } + named_expr->expr_ = ctx->expression()->accept(this); + return named_expr; +} + +antlrcpp::Any +CypherMainVisitor::visitNodePattern(CypherParser::NodePatternContext *ctx) { + auto node = new NodePart(ctx_.next_uid()); + if (ctx->variable()) { + std::string variable = ctx->variable()->accept(this); + node->identifier_ = + std::make_shared<Ident>(ctx_.next_uid(), kUserIdentPrefix + variable); + } else { + node->identifier_ = std::make_shared<Ident>( + ctx_.next_uid(), kAnonIdentPrefix + std::to_string(next_ident_id_++)); } if (ctx->nodeLabels()) { - node.labels = - ctx->nodeLabels()->accept(this).as<std::vector<std::string>>(); + std::vector<std::string> labels = ctx->nodeLabels()->accept(this); + for (const auto &label : labels) { + node->labels_.push_back(ctx_.db_accessor_.label(label)); + } } if (ctx->properties()) { - node.properties = ctx->properties() - ->accept(this) - .as<std::unordered_map<std::string, std::string>>(); + throw std::exception(); + // node.properties = ctx->properties() + // ->accept(this) + // .as<std::unordered_map<std::string, + // std::string>>(); } - symbol_table_[node.output_id] = node; - return node.output_id; + return (Part *)node; } -antlrcpp::Any CypherMainVisitor::visitNodeLabels( - CypherParser::NodeLabelsContext *ctx) { +antlrcpp::Any +CypherMainVisitor::visitNodeLabels(CypherParser::NodeLabelsContext *ctx) { std::vector<std::string> labels; for (auto *node_label : ctx->nodeLabel()) { - labels.push_back(node_label->accept(this).as<std::string>()); + labels.push_back(node_label->accept(this)); } return labels; } -antlrcpp::Any CypherMainVisitor::visitProperties( - CypherParser::PropertiesContext *ctx) { +antlrcpp::Any +CypherMainVisitor::visitProperties(CypherParser::PropertiesContext *ctx) { if (!ctx->mapLiteral()) { // If child is not mapLiteral that means child is params. At the moment // memgraph doesn't support params. - throw SemanticException(); + throw std::exception(); } return ctx->mapLiteral()->accept(this); } -antlrcpp::Any CypherMainVisitor::visitMapLiteral( - CypherParser::MapLiteralContext *ctx) { +antlrcpp::Any +CypherMainVisitor::visitMapLiteral(CypherParser::MapLiteralContext *ctx) { + throw std::exception(); std::unordered_map<std::string, std::string> map; for (int i = 0; i < (int)ctx->propertyKeyName().size(); ++i) { map[ctx->propertyKeyName()[i]->accept(this).as<std::string>()] = @@ -111,40 +140,38 @@ antlrcpp::Any CypherMainVisitor::visitMapLiteral( return map; } -antlrcpp::Any CypherMainVisitor::visitSymbolicName( - CypherParser::SymbolicNameContext *ctx) { +antlrcpp::Any +CypherMainVisitor::visitSymbolicName(CypherParser::SymbolicNameContext *ctx) { if (ctx->EscapedSymbolicName()) { // We don't allow at this point for variable to be EscapedSymbolicName // because we would have t ofigure out how escaping works since same // variable can be referenced in two ways: escaped and unescaped. - throw SemanticException(); + throw std::exception(); } return std::string(ctx->getText()); } -antlrcpp::Any CypherMainVisitor::visitPattern( - CypherParser::PatternContext *ctx) { - std::vector<std::string> pattern; +antlrcpp::Any +CypherMainVisitor::visitPattern(CypherParser::PatternContext *ctx) { + std::vector<std::shared_ptr<Pattern>> patterns; for (auto *pattern_part : ctx->patternPart()) { - pattern.push_back(pattern_part->accept(this).as<std::string>()); + patterns.push_back(pattern_part->accept(this)); } - return pattern; + return patterns; } -antlrcpp::Any CypherMainVisitor::visitPatternPart( - CypherParser::PatternPartContext *ctx) { - PatternPart pattern_part = - ctx->anonymousPatternPart()->accept(this).as<PatternPart>(); +antlrcpp::Any +CypherMainVisitor::visitPatternPart(CypherParser::PatternPartContext *ctx) { + std::shared_ptr<Pattern> pattern = ctx->anonymousPatternPart()->accept(this); if (ctx->variable()) { - std::string variable = ctx->variable()->accept(this).as<std::string>(); - auto &curr_id_map = ids_map_.back(); - if (curr_id_map.find(variable) != curr_id_map.end()) { - throw SemanticException(); - } - curr_id_map[variable] = pattern_part.output_id; + std::string variable = ctx->variable()->accept(this); + pattern->identifier_ = + std::make_shared<Ident>(ctx_.next_uid(), kUserIdentPrefix + variable); + } else { + pattern->identifier_ = std::make_shared<Ident>( + ctx_.next_uid(), kAnonIdentPrefix + std::to_string(next_ident_id_++)); } - symbol_table_[pattern_part.output_id] = pattern_part; - return pattern_part.output_id; + return pattern; } antlrcpp::Any CypherMainVisitor::visitPatternElement( @@ -152,88 +179,67 @@ antlrcpp::Any CypherMainVisitor::visitPatternElement( if (ctx->patternElement()) { return ctx->patternElement()->accept(this); } - PatternPart pattern_part; - pattern_part.output_id = new_id(); - pattern_part.nodes.push_back( - ctx->nodePattern()->accept(this).as<std::string>()); + auto pattern = std::shared_ptr<Pattern>(); + pattern->parts_.push_back( + ctx->nodePattern()->accept(this).as<std::shared_ptr<Part>>()); for (auto *pattern_element_chain : ctx->patternElementChain()) { - auto element = pattern_element_chain->accept(this) - .as<std::pair<std::string, std::string>>(); - pattern_part.relationships.push_back(element.first); - pattern_part.nodes.push_back(element.second); + auto element = + pattern_element_chain->accept(this) + .as<std::pair<std::shared_ptr<Part>, std::shared_ptr<Part>>>(); + pattern->parts_.push_back(element.first); + pattern->parts_.push_back(element.second); } - return pattern_part; + return pattern; } antlrcpp::Any CypherMainVisitor::visitPatternElementChain( CypherParser::PatternElementChainContext *ctx) { - return std::pair<std::string, std::string>( - ctx->relationshipPattern()->accept(this).as<std::string>(), - ctx->nodePattern()->accept(this).as<std::string>()); + return std::pair<std::shared_ptr<Part>, std::shared_ptr<Part>>( + ctx->relationshipPattern()->accept(this).as<std::shared_ptr<Part>>(), + ctx->nodePattern()->accept(this).as<std::shared_ptr<Part>>()); } antlrcpp::Any CypherMainVisitor::visitRelationshipPattern( CypherParser::RelationshipPatternContext *ctx) { - bool new_relationship = true; - Relationship relationship; + auto edge = std::make_shared<EdgePart>(ctx_.next_uid()); if (ctx->relationshipDetail()) { if (ctx->relationshipDetail()->variable()) { - auto variable = - ctx->relationshipDetail()->variable()->accept(this).as<std::string>(); - auto &curr_id_map = ids_map_.back(); - if (curr_id_map.find(variable) != curr_id_map.end()) { - if (!symbol_table_[curr_id_map[variable]].is<Relationship>()) { - throw SemanticException(); - } - new_relationship = false; - relationship = symbol_table_[curr_id_map[variable]].as<Relationship>(); - } else { - relationship.output_id = new_id(); - curr_id_map[variable] = relationship.output_id; - } - } - if (!new_relationship && (ctx->relationshipDetail()->relationshipTypes() || - ctx->relationshipDetail()->properties() || - ctx->relationshipDetail()->rangeLiteral())) { - // Neo4j doesn't allow multiple edges with same variable name, but if we - // are going to support different types of morphisms then there is no - // reason to disallow that. - throw SemanticException(); - } - if (ctx->relationshipDetail()->relationshipTypes()) { - relationship.types = ctx->relationshipDetail() - ->relationshipTypes() - ->accept(this) - .as<std::vector<std::string>>(); - } - if (ctx->relationshipDetail()->properties()) { - relationship.properties = - ctx->relationshipDetail() - ->properties() - ->accept(this) - .as<std::unordered_map<std::string, std::string>>(); - } - if (ctx->relationshipDetail()->rangeLiteral()) { - relationship.has_range = true; - auto range = ctx->relationshipDetail() - ->rangeLiteral() - ->accept(this) - .as<std::pair<int64_t, int64_t>>(); - relationship.lower_bound = range.first; - relationship.upper_bound = range.second; + std::string variable = + ctx->relationshipDetail()->variable()->accept(this); + edge->identifier_ = + std::make_shared<Ident>(ctx_.next_uid(), kUserIdentPrefix + variable); + } else { + edge->identifier_ = std::make_shared<Ident>( + ctx_.next_uid(), kAnonIdentPrefix + std::to_string(next_ident_id_++)); } } + if (ctx->relationshipDetail()->relationshipTypes()) { + throw std::exception(); + } + if (ctx->relationshipDetail()->properties()) { + throw std::exception(); + } + if (ctx->relationshipDetail()->rangeLiteral()) { + throw std::exception(); + // relationship.has_range = true; + // auto range = ctx->relationshipDetail() + // ->rangeLiteral() + // ->accept(this) + // .as<std::pair<int64_t, int64_t>>(); + // relationship.lower_bound = range.first; + // relationship.upper_bound = range.second; + } + if (ctx->leftArrowHead() && !ctx->rightArrowHead()) { - relationship.direction = Relationship::Direction::LEFT; + edge->direction = EdgePart::Direction::LEFT; } else if (!ctx->leftArrowHead() && ctx->rightArrowHead()) { - relationship.direction = Relationship::Direction::RIGHT; + edge->direction = EdgePart::Direction::RIGHT; } else { // <-[]-> and -[]- is the same thing as far as we understand openCypher // grammar. - relationship.direction = Relationship::Direction::BOTH; + edge->direction = EdgePart::Direction::BOTH; } - symbol_table_[relationship.output_id] = relationship; - return relationship.output_id; + return std::shared_ptr<Part>(edge); } antlrcpp::Any CypherMainVisitor::visitRelationshipDetail( @@ -251,8 +257,8 @@ antlrcpp::Any CypherMainVisitor::visitRelationshipTypes( return types; } -antlrcpp::Any CypherMainVisitor::visitRangeLiteral( - CypherParser::RangeLiteralContext *ctx) { +antlrcpp::Any +CypherMainVisitor::visitRangeLiteral(CypherParser::RangeLiteralContext *ctx) { if (ctx->integerLiteral().size() == 0U) { // -[*]- return std::pair<int64_t, int64_t>(1LL, LLONG_MAX); @@ -279,194 +285,201 @@ antlrcpp::Any CypherMainVisitor::visitRangeLiteral( } } -antlrcpp::Any CypherMainVisitor::visitExpression( - CypherParser::ExpressionContext *ctx) { +antlrcpp::Any +CypherMainVisitor::visitExpression(CypherParser::ExpressionContext *ctx) { return visitChildren(ctx); } -// OR. -antlrcpp::Any CypherMainVisitor::visitExpression12( - CypherParser::Expression12Context *ctx) { - return LeftAssociativeOperatorExpression(ctx->expression11(), - Function::LOGICAL_OR); -} - -// XOR. -antlrcpp::Any CypherMainVisitor::visitExpression11( - CypherParser::Expression11Context *ctx) { - return LeftAssociativeOperatorExpression(ctx->expression10(), - Function::LOGICAL_XOR); -} - -// AND. -antlrcpp::Any CypherMainVisitor::visitExpression10( - CypherParser::Expression10Context *ctx) { - return LeftAssociativeOperatorExpression(ctx->expression9(), - Function::LOGICAL_AND); -} - -// NOT. -antlrcpp::Any CypherMainVisitor::visitExpression9( - CypherParser::Expression9Context *ctx) { - // TODO: make template similar to LeftAssociativeOperatorExpression for unary - // expresssions. - auto operand = ctx->expression8()->accept(this).as<std::string>(); - for (int i = 0; i < (int)ctx->NOT().size(); ++i) { - auto lhs_id = new_id(); - symbol_table_[lhs_id] = SimpleExpression{Function::LOGICAL_NOT, {operand}}; - operand = lhs_id; - } - return operand; -} - -// Comparisons. -antlrcpp::Any CypherMainVisitor::visitExpression8( - CypherParser::Expression8Context *ctx) { - if (!ctx->partialComparisonExpression().size()) { - // There is no comparison operators. We generate expression7. - return ctx->expression7()->accept(this); - } - - // There is at least one comparison. We need to generate code for each of - // them. We don't call visitPartialComparisonExpression but do everything in - // this function and call expression7-s directly. Since every expression7 - // can be generated twice (because it can appear in two comparisons) code - // generated by whole subtree of expression7 must not have any sideeffects. - // We handle chained comparisons as defined by mathematics, neo4j handles - // them in a very interesting, illogical and incomprehensible way. For - // example in neo4j: - // 1 < 2 < 3 -> true, - // 1 < 2 < 3 < 4 -> false, - // 5 > 3 < 5 > 3 -> true, - // 4 <= 5 < 7 > 6 -> false - // All of those comparisons evaluate to true in memgraph. - std::vector<std::string> children_ids; - children_ids.push_back(ctx->expression7()->accept(this).as<std::string>()); - auto partial_comparison_expressions = ctx->partialComparisonExpression(); - for (auto *child : partial_comparison_expressions) { - children_ids.push_back(child->accept(this).as<std::string>()); - } - - // Make all comparisons. - std::string first_operand = children_ids[0]; - std::vector<std::string> comparison_ids; - for (int i = 0; i < (int)partial_comparison_expressions.size(); ++i) { - auto *expr = partial_comparison_expressions[i]; - auto op = [](CypherParser::PartialComparisonExpressionContext *expr) { - if (expr->getToken(kEqTokenId, 0)) { - return Function::EQ; - } else if (expr->getToken(kNeTokenId1, 0) || - expr->getToken(kNeTokenId2, 0)) { - return Function::NE; - } else if (expr->getToken(kLtTokenId, 0)) { - return Function::LT; - } else if (expr->getToken(kGtTokenId, 0)) { - return Function::GT; - } else if (expr->getToken(kLeTokenId, 0)) { - return Function::LE; - } else if (expr->getToken(kGeTokenId, 0)) { - return Function::GE; - } - assert(false); - return Function::GE; - }(expr); - auto lhs_id = new_id(); - symbol_table_[lhs_id] = - SimpleExpression{op, {first_operand, children_ids[i + 1]}}; - first_operand = lhs_id; - comparison_ids.push_back(lhs_id); - } - - first_operand = comparison_ids[0]; - // Calculate logical and of results of comparisons. - for (int i = 1; i < (int)comparison_ids.size(); ++i) { - auto lhs_id = new_id(); - symbol_table_[lhs_id] = SimpleExpression{ - Function::LOGICAL_AND, {first_operand, comparison_ids[i]}}; - first_operand = lhs_id; - } - return first_operand; -} - -antlrcpp::Any CypherMainVisitor::visitPartialComparisonExpression( - CypherParser::PartialComparisonExpressionContext *) { - debug_assert(false, "Should never be called. See documentation in hpp."); - return 0; -} - -// Addition and subtraction. -antlrcpp::Any CypherMainVisitor::visitExpression7( - CypherParser::Expression7Context *ctx) { - return LeftAssociativeOperatorExpression( - ctx->expression6(), - MapTokensToOperators(ctx, {{kPlusTokenId, Function::ADDITION}, - {kMinusTokenId, Function::SUBTRACTION}})); -} - -// Multiplication, division, modding. -antlrcpp::Any CypherMainVisitor::visitExpression6( - CypherParser::Expression6Context *ctx) { - return LeftAssociativeOperatorExpression( - ctx->expression5(), - MapTokensToOperators(ctx, {{kMultTokenId, Function::MULTIPLICATION}, - {kDivTokenId, Function::DIVISION}, - {kModTokenId, Function::MODULO}})); -} - -// Power. -antlrcpp::Any CypherMainVisitor::visitExpression5( - CypherParser::Expression5Context *ctx) { - if (ctx->expression4().size() > 1u) { - // TODO: implement power operator. In neo4j power is right associative and - // int^int -> float. - throw SemanticException(); - } - return visitChildren(ctx); -} - -// Unary minus and plus. -antlrcpp::Any CypherMainVisitor::visitExpression4( - CypherParser::Expression4Context *ctx) { - auto ops = - MapTokensToOperators(ctx, {{kUnaryPlusTokenId, Function::UNARY_PLUS}, - {kUnaryMinusTokenId, Function::UNARY_MINUS}}); - auto operand = ctx->expression3()->accept(this).as<std::string>(); - for (int i = 0; i < (int)ops.size(); ++i) { - auto lhs_id = new_id(); - symbol_table_[lhs_id] = SimpleExpression{ops[i], {operand}}; - operand = lhs_id; - } - return operand; -} - -antlrcpp::Any CypherMainVisitor::visitExpression3( - CypherParser::Expression3Context *ctx) { - // If there is only one child we don't need to generate any code in this since - // that child is expression2. Other operations are not implemented at the - // moment. - // TODO: implement this. - if (ctx->children.size() > 1u) { - throw SemanticException(); - } - return visitChildren(ctx); -} - -antlrcpp::Any CypherMainVisitor::visitExpression2( - CypherParser::Expression2Context *ctx) { - if (ctx->nodeLabels().size()) { - // TODO: Implement this. We don't currently support label checking in - // expresssion. - throw SemanticException(); - } - auto operand = ctx->atom()->accept(this).as<std::string>(); - for (int i = 0; i < (int)ctx->propertyLookup().size(); ++i) { - auto lhs_id = new_id(); - symbol_table_[lhs_id] = - SimpleExpression{Function::PROPERTY_GETTER, {operand}}; - operand = lhs_id; - } - return operand; -} +//// OR. +// antlrcpp::Any +// CypherMainVisitor::visitExpression12(CypherParser::Expression12Context *ctx) +// { +// return LeftAssociativeOperatorExpression(ctx->expression11(), +// Function::LOGICAL_OR); +//} +// +//// XOR. +// antlrcpp::Any +// CypherMainVisitor::visitExpression11(CypherParser::Expression11Context *ctx) +// { +// return LeftAssociativeOperatorExpression(ctx->expression10(), +// Function::LOGICAL_XOR); +//} +// +//// AND. +// antlrcpp::Any +// CypherMainVisitor::visitExpression10(CypherParser::Expression10Context *ctx) +// { +// return LeftAssociativeOperatorExpression(ctx->expression9(), +// Function::LOGICAL_AND); +//} +// +//// NOT. +// antlrcpp::Any +// CypherMainVisitor::visitExpression9(CypherParser::Expression9Context *ctx) { +// // TODO: make template similar to LeftAssociativeOperatorExpression for +// unary +// // expresssions. +// auto operand = ctx->expression8()->accept(this).as<std::string>(); +// for (int i = 0; i < (int)ctx->NOT().size(); ++i) { +// auto lhs_id = new_id(); +// symbol_table_[lhs_id] = SimpleExpression{Function::LOGICAL_NOT, +// {operand}}; +// operand = lhs_id; +// } +// return operand; +//} +// +//// Comparisons. +// antlrcpp::Any +// CypherMainVisitor::visitExpression8(CypherParser::Expression8Context *ctx) { +// if (!ctx->partialComparisonExpression().size()) { +// // There is no comparison operators. We generate expression7. +// return ctx->expression7()->accept(this); +// } +// +// // There is at least one comparison. We need to generate code for each of +// // them. We don't call visitPartialComparisonExpression but do everything in +// // this function and call expression7-s directly. Since every expression7 +// // can be generated twice (because it can appear in two comparisons) code +// // generated by whole subtree of expression7 must not have any sideeffects. +// // We handle chained comparisons as defined by mathematics, neo4j handles +// // them in a very interesting, illogical and incomprehensible way. For +// // example in neo4j: +// // 1 < 2 < 3 -> true, +// // 1 < 2 < 3 < 4 -> false, +// // 5 > 3 < 5 > 3 -> true, +// // 4 <= 5 < 7 > 6 -> false +// // All of those comparisons evaluate to true in memgraph. +// std::vector<std::string> children_ids; +// children_ids.push_back(ctx->expression7()->accept(this).as<std::string>()); +// auto partial_comparison_expressions = ctx->partialComparisonExpression(); +// for (auto *child : partial_comparison_expressions) { +// children_ids.push_back(child->accept(this).as<std::string>()); +// } +// +// // Make all comparisons. +// std::string first_operand = children_ids[0]; +// std::vector<std::string> comparison_ids; +// for (int i = 0; i < (int)partial_comparison_expressions.size(); ++i) { +// auto *expr = partial_comparison_expressions[i]; +// auto op = [](CypherParser::PartialComparisonExpressionContext *expr) { +// if (expr->getToken(kEqTokenId, 0)) { +// return Function::EQ; +// } else if (expr->getToken(kNeTokenId1, 0) || +// expr->getToken(kNeTokenId2, 0)) { +// return Function::NE; +// } else if (expr->getToken(kLtTokenId, 0)) { +// return Function::LT; +// } else if (expr->getToken(kGtTokenId, 0)) { +// return Function::GT; +// } else if (expr->getToken(kLeTokenId, 0)) { +// return Function::LE; +// } else if (expr->getToken(kGeTokenId, 0)) { +// return Function::GE; +// } +// assert(false); +// return Function::GE; +// }(expr); +// auto lhs_id = new_id(); +// symbol_table_[lhs_id] = +// SimpleExpression{op, {first_operand, children_ids[i + 1]}}; +// first_operand = lhs_id; +// comparison_ids.push_back(lhs_id); +// } +// +// first_operand = comparison_ids[0]; +// // Calculate logical and of results of comparisons. +// for (int i = 1; i < (int)comparison_ids.size(); ++i) { +// auto lhs_id = new_id(); +// symbol_table_[lhs_id] = SimpleExpression{ +// Function::LOGICAL_AND, {first_operand, comparison_ids[i]}}; +// first_operand = lhs_id; +// } +// return first_operand; +//} +// +// antlrcpp::Any CypherMainVisitor::visitPartialComparisonExpression( +// CypherParser::PartialComparisonExpressionContext *) { +// debug_assert(false, "Should never be called. See documentation in hpp."); +// return 0; +//} +// +//// Addition and subtraction. +// antlrcpp::Any +// CypherMainVisitor::visitExpression7(CypherParser::Expression7Context *ctx) { +// return LeftAssociativeOperatorExpression( +// ctx->expression6(), +// MapTokensToOperators(ctx, {{kPlusTokenId, Function::ADDITION}, +// {kMinusTokenId, Function::SUBTRACTION}})); +//} +// +//// Multiplication, division, modding. +// antlrcpp::Any +// CypherMainVisitor::visitExpression6(CypherParser::Expression6Context *ctx) { +// return LeftAssociativeOperatorExpression( +// ctx->expression5(), +// MapTokensToOperators(ctx, {{kMultTokenId, Function::MULTIPLICATION}, +// {kDivTokenId, Function::DIVISION}, +// {kModTokenId, Function::MODULO}})); +//} +// +//// Power. +// antlrcpp::Any +// CypherMainVisitor::visitExpression5(CypherParser::Expression5Context *ctx) { +// if (ctx->expression4().size() > 1u) { +// // TODO: implement power operator. In neo4j power is right associative and +// // int^int -> float. +// throw SemanticException(); +// } +// return visitChildren(ctx); +//} +// +//// Unary minus and plus. +// antlrcpp::Any +// CypherMainVisitor::visitExpression4(CypherParser::Expression4Context *ctx) { +// auto ops = +// MapTokensToOperators(ctx, {{kUnaryPlusTokenId, Function::UNARY_PLUS}, +// {kUnaryMinusTokenId, +// Function::UNARY_MINUS}}); +// auto operand = ctx->expression3()->accept(this).as<std::string>(); +// for (int i = 0; i < (int)ops.size(); ++i) { +// auto lhs_id = new_id(); +// symbol_table_[lhs_id] = SimpleExpression{ops[i], {operand}}; +// operand = lhs_id; +// } +// return operand; +//} +// +// antlrcpp::Any +// CypherMainVisitor::visitExpression3(CypherParser::Expression3Context *ctx) { +// // If there is only one child we don't need to generate any code in this +// since +// // that child is expression2. Other operations are not implemented at the +// // moment. +// // TODO: implement this. +// if (ctx->children.size() > 1u) { +// throw SemanticException(); +// } +// return visitChildren(ctx); +//} +// +// antlrcpp::Any +// CypherMainVisitor::visitExpression2(CypherParser::Expression2Context *ctx) { +// if (ctx->nodeLabels().size()) { +// // TODO: Implement this. We don't currently support label checking in +// // expresssion. +// throw SemanticException(); +// } +// auto operand = ctx->atom()->accept(this).as<std::string>(); +// for (int i = 0; i < (int)ctx->propertyLookup().size(); ++i) { +// auto lhs_id = new_id(); +// symbol_table_[lhs_id] = +// SimpleExpression{Function::PROPERTY_GETTER, {operand}}; +// operand = lhs_id; +// } +// return operand; +//} antlrcpp::Any CypherMainVisitor::visitAtom(CypherParser::AtomContext *ctx) { if (ctx->literal()) { @@ -479,10 +492,11 @@ antlrcpp::Any CypherMainVisitor::visitAtom(CypherParser::AtomContext *ctx) { // appear only in tests and real queries will be stripped. // TODO: Either parse it correctly or raise exception. If exception is // raised it tests should also use params instead of literals. - auto text = ctx->literal()->getText(); - auto lhs_id = new_id(); - symbol_table_[lhs_id] = SimpleExpression{Function::LITERAL, {text}}; - return lhs_id; + // auto text = ctx->literal()->getText(); + // auto lhs_id = new_id(); + // symbol_table_[lhs_id] = SimpleExpression{Function::LITERAL, {text}}; + // return lhs_id; + throw std::exception(); } else if (ctx->parameter()) { // This is once again potential security risk. We shouldn't output text // given in user query as parameter name directly to the code. Stripper @@ -490,25 +504,20 @@ antlrcpp::Any CypherMainVisitor::visitAtom(CypherParser::AtomContext *ctx) { // allow only parameters with numeric names. At the moment this is not a // problem since we don't accept user's parameters but only ours. // TODO: revise this. - auto text = ctx->literal()->getText(); - auto lhs_id = new_id(); - symbol_table_[lhs_id] = SimpleExpression{Function::PARAMETER, {text}}; - return lhs_id; + // auto text = ctx->literal()->getText(); + // auto lhs_id = new_id(); + // symbol_table_[lhs_id] = SimpleExpression{Function::PARAMETER, {text}}; + throw std::exception(); + // return lhs_id; } else if (ctx->parenthesizedExpression()) { return ctx->parenthesizedExpression()->accept(this); } else if (ctx->variable()) { - // TODO: revise this. Is it possible in some atom to use not declared - // variable. Is it correct to always use last ids_map? - auto &curr_id_map = ids_map_.back(); - auto variable = ctx->variable()->accept(this).as<std::string>(); - if (curr_id_map.find(variable) == curr_id_map.end()) { - throw SemanticException(); - } - return curr_id_map[variable]; + std::string variable = ctx->variable()->accept(this); + return std::make_shared<Ident>(ctx_.next_uid(), variable); } // TODO: Implement this. We don't support comprehensions, functions, // filtering... at the moment. - throw SemanticException(); + throw std::exception(); } antlrcpp::Any CypherMainVisitor::visitIntegerLiteral( @@ -517,7 +526,7 @@ antlrcpp::Any CypherMainVisitor::visitIntegerLiteral( try { t = std::stoll(ctx->getText(), 0, 0); } catch (std::out_of_range) { - throw SemanticException(); + throw std::exception(); } return t; } diff --git a/src/query/frontend/ast/cypher_main_visitor.hpp b/src/query/frontend/ast/cypher_main_visitor.hpp index 70101c05a..b5dd3acbb 100644 --- a/src/query/frontend/ast/cypher_main_visitor.hpp +++ b/src/query/frontend/ast/cypher_main_visitor.hpp @@ -1,53 +1,64 @@ #pragma once -#include "antlr4-runtime.h" -#include "query/backend/cpp/compiler_structures.hpp" -#include "query/frontend/opencypher/generated/CypherBaseVisitor.h" #include <string> +#include <unordered_set> + +#include "antlr4-runtime.h" +#include "query/context.hpp" +#include "query/frontend/ast/ast.hpp" +#include "query/frontend/opencypher/generated/CypherBaseVisitor.h" namespace query { namespace frontend { +using query::Context; using antlropencypher::CypherParser; class CypherMainVisitor : public antlropencypher::CypherBaseVisitor { +public: + CypherMainVisitor(Context &ctx) : ctx_(ctx) {} + private: - // Return new output code id. - // TODO: Should we generate ids with more readable names: node_1, - // relationship_5, temporary_2...? - std::string new_id() const { - static int next_id = 0; - return "id" + std::to_string(next_id++); - } + // template <typename TExpression> + // antlrcpp::Any + // LeftAssociativeOperatorExpression(std::vector<TExpression *> children, + // std::vector<Function> ops) { + // assert(children.size()); + // std::vector<std::string> children_ids; + // + // for (auto *child : children) { + // children_ids.push_back(child->accept(this).template + // as<std::string>()); + // } + // + // std::string first_operand = children_ids[0]; + // for (int i = 0; i < (int)ops.size(); ++i) { + // auto lhs_id = new_id(); + // symbol_table_[lhs_id] = + // SimpleExpression{ops[i], {first_operand, children_ids[i + 1]}}; + // first_operand = lhs_id; + // } + // return first_operand; + // } + // + // template <typename TExpression> + // antlrcpp::Any + // LeftAssociativeOperatorExpression(std::vector<TExpression *> children, + // Function op) { + // return LeftAssociativeOperatorExpression( + // children, std::vector<Function>((int)children.size() - 1, op)); + // } - template <typename TExpression> antlrcpp::Any - LeftAssociativeOperatorExpression(std::vector<TExpression *> children, - std::vector<Function> ops) { - assert(children.size()); - std::vector<std::string> children_ids; + visitSingleQuery(CypherParser::SingleQueryContext *ctx) override; - for (auto *child : children) { - children_ids.push_back(child->accept(this).template as<std::string>()); - } - - std::string first_operand = children_ids[0]; - for (int i = 0; i < (int)ops.size(); ++i) { - auto lhs_id = new_id(); - symbol_table_[lhs_id] = - SimpleExpression{ops[i], {first_operand, children_ids[i + 1]}}; - first_operand = lhs_id; - } - return first_operand; - } - - template <typename TExpression> antlrcpp::Any - LeftAssociativeOperatorExpression(std::vector<TExpression *> children, - Function op) { - return LeftAssociativeOperatorExpression( - children, std::vector<Function>((int)children.size() - 1, op)); - } + visitCypherMatch(CypherParser::CypherMatchContext *ctx) override; + + antlrcpp::Any + visitReturnItems(CypherParser::ReturnItemsContext *ctx) override; + + antlrcpp::Any visitReturnItem(CypherParser::ReturnItemContext *ctx) override; /** * Creates Node and stores it in symbol_table_. If variable is defined it is @@ -143,100 +154,100 @@ private: */ antlrcpp::Any visitExpression(CypherParser::ExpressionContext *ctx) override; - /** - * OR. - * - * @return string - expression id. - */ - antlrcpp::Any - visitExpression12(CypherParser::Expression12Context *ctx) override; + ///** + //* OR. + //* + //* @return string - expression id. + //*/ + // antlrcpp::Any + // visitExpression12(CypherParser::Expression12Context *ctx) override; - /** - * XOR. - * - * @return string - expression id. - */ - antlrcpp::Any - visitExpression11(CypherParser::Expression11Context *ctx) override; + ///** + //* XOR. + //* + //* @return string - expression id. + //*/ + // antlrcpp::Any + // visitExpression11(CypherParser::Expression11Context *ctx) override; - /** - * AND. - * - * @return string - expression id. - */ - antlrcpp::Any - visitExpression10(CypherParser::Expression10Context *ctx) override; + ///** + //* AND. + //* + //* @return string - expression id. + //*/ + // antlrcpp::Any + // visitExpression10(CypherParser::Expression10Context *ctx) override; - /** - * NOT. - * - * @return string - expression id. - */ - antlrcpp::Any - visitExpression9(CypherParser::Expression9Context *ctx) override; + ///** + //* NOT. + //* + //* @return string - expression id. + //*/ + // antlrcpp::Any + // visitExpression9(CypherParser::Expression9Context *ctx) override; - /** - * Comparisons. - * - * @return string - expression id. - */ - antlrcpp::Any - visitExpression8(CypherParser::Expression8Context *ctx) override; + ///** + //* Comparisons. + //* + //* @return string - expression id. + //*/ + // antlrcpp::Any + // visitExpression8(CypherParser::Expression8Context *ctx) override; - /** - * Never call this. Everything related to generating code for comparison - * operators should be done in visitExpression8. - */ - antlrcpp::Any visitPartialComparisonExpression( - CypherParser::PartialComparisonExpressionContext *ctx) override; + ///** + //* Never call this. Everything related to generating code for comparison + //* operators should be done in visitExpression8. + //*/ + // antlrcpp::Any visitPartialComparisonExpression( + // CypherParser::PartialComparisonExpressionContext *ctx) override; - /** - * Addition and subtraction. - * - * @return string - expression id. - */ - antlrcpp::Any - visitExpression7(CypherParser::Expression7Context *ctx) override; + ///** + //* Addition and subtraction. + //* + //* @return string - expression id. + //*/ + // antlrcpp::Any + // visitExpression7(CypherParser::Expression7Context *ctx) override; - /** - * Multiplication, division, modding. - * - * @return string - expression id. - */ - antlrcpp::Any - visitExpression6(CypherParser::Expression6Context *ctx) override; + ///** + //* Multiplication, division, modding. + //* + //* @return string - expression id. + //*/ + // antlrcpp::Any + // visitExpression6(CypherParser::Expression6Context *ctx) override; - /** - * Power. - * - * @return string - expression id. - */ - antlrcpp::Any - visitExpression5(CypherParser::Expression5Context *ctx) override; + ///** + //* Power. + //* + //* @return string - expression id. + //*/ + // antlrcpp::Any + // visitExpression5(CypherParser::Expression5Context *ctx) override; - /** - * Unary minus and plus. - * - * @return string - expression id. - */ - antlrcpp::Any - visitExpression4(CypherParser::Expression4Context *ctx) override; + ///** + //* Unary minus and plus. + //* + //* @return string - expression id. + //*/ + // antlrcpp::Any + // visitExpression4(CypherParser::Expression4Context *ctx) override; - /** - * Element of a list, range of a list... - * - * @return string - expression id. - */ - antlrcpp::Any - visitExpression3(CypherParser::Expression3Context *ctx) override; + ///** + //* Element of a list, range of a list... + //* + //* @return string - expression id. + //*/ + // antlrcpp::Any + // visitExpression3(CypherParser::Expression3Context *ctx) override; - /** - * Property lookup, test for node labels existence... - * - * @return string - expression id. - */ - antlrcpp::Any - visitExpression2(CypherParser::Expression2Context *ctx) override; + ///** + //* Property lookup, test for node labels existence... + //* + //* @return string - expression id. + //*/ + // antlrcpp::Any + // visitExpression2(CypherParser::Expression2Context *ctx) override; /** * Literals, params, list comprehension... @@ -270,25 +281,12 @@ private: antlrcpp::Any visitIntegerLiteral(CypherParser::IntegerLiteralContext *ctx) override; -public: - // TODO: These temporary getters should eventually be replaced with - // something - // else once we figure out where and how those strctures will be used. - // Currently there are needed for testing. cypher_main_visitor test should - // be - // refactored once these getters are deleted. - const auto &ids_map() const { return ids_map_; } - const auto &symbol_table() const { return symbol_table_; } - private: - // Mapping of ids (nodes, relationships, values, lists ...) from - // query - // code to id that is used in generated code; - std::vector<std::unordered_map<std::string, std::string>> ids_map_{1}; - - // Mapping of output (generated) code ids to appropriate parser - // structure. - std::unordered_map<std::string, antlrcpp::Any> symbol_table_; + Context &ctx_; + int next_ident_id_; + const std::string kUserIdentPrefix = "u_"; + const std::string kAnonIdentPrefix = "a_"; + std::shared_ptr<Query> query_; }; } } diff --git a/tests/unit/cypher_main_visitor.cpp b/tests/unit/cypher_main_visitor.cpp index f1b6682b3..0c8bcb7b5 100644 --- a/tests/unit/cypher_main_visitor.cpp +++ b/tests/unit/cypher_main_visitor.cpp @@ -1,19 +1,22 @@ +#include <algorithm> #include <climits> #include <string> -#include <vector> #include <unordered_map> -#include <algorithm> +#include <vector> + #include "antlr4-runtime.h" -#include "gtest/gtest.h" #include "gmock/gmock.h" -#include "query/backend/cpp/cypher_main_visitor.cpp" +#include "query/context.hpp" +#include "query/frontend/ast/cypher_main_visitor.hpp" #include "query/frontend/opencypher/parser.hpp" +#include "gtest/gtest.h" using namespace ::testing; namespace { -using namespace backend::cpp; +using query::Context; +using namespace query::frontend; class ParserTables { template <typename T> @@ -27,7 +30,7 @@ class ParserTables { return filtered; } - public: +public: ParserTables(const std::string &query) { frontend::opencypher::Parser parser(query); auto *tree = parser.tree(); @@ -86,7 +89,8 @@ void CompareRelationships( relationship_property_keys, UnorderedElementsAreArray(property_keys.begin(), property_keys.end())); ASSERT_EQ(relationship.has_range, has_range); - if (!has_range) return; + if (!has_range) + return; ASSERT_EQ(relationship.lower_bound, lower_bound); ASSERT_EQ(relationship.upper_bound, upper_bound); }