Implement ast generation by cypher visitor

This commit is contained in:
Mislav Bradac 2017-03-11 21:19:57 +01:00
parent cfd2eae9d0
commit 026e0e6fbd
10 changed files with 648 additions and 600 deletions

View File

@ -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
)

View File

@ -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.
*/

11
src/query/context.cpp Normal file
View File

@ -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);
}
}

View File

@ -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);
};
}

View File

@ -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)

View File

@ -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);
}
}

View File

@ -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_;
};
}

View File

@ -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;
}

View File

@ -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_;
};
}
}

View File

@ -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);
}