Implement expression code generator
Summary: Work in progress, this is not usable yet Reviewers: florijan Reviewed By: florijan Subscribers: pullbot Differential Revision: https://phabricator.memgraph.io/D84
This commit is contained in:
parent
47ed127086
commit
00d818c762
29
src/query/backend/cpp/code_generator.cpp
Normal file
29
src/query/backend/cpp/code_generator.cpp
Normal file
@ -0,0 +1,29 @@
|
||||
#include "query/backend/cpp/code_generator.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "query/backend/cpp/named_antlr_tokens.hpp"
|
||||
#include "utils/assert.hpp"
|
||||
|
||||
namespace {
|
||||
std::string kLogicalOr = "||";
|
||||
// OpenCypher supports xor operator only for booleans, so we can use binary xor
|
||||
// instead.
|
||||
std::string kLogicalXor = "^";
|
||||
std::string kLogicalAnd = "&&";
|
||||
std::string kLogicalNot = "!";
|
||||
std::string kEq = "=";
|
||||
std::string kNe = "!=";
|
||||
std::string kLt = "<";
|
||||
std::string kGt = ">";
|
||||
std::string kLe = "<=";
|
||||
std::string kGe = ">=";
|
||||
std::string kPlus = "+";
|
||||
std::string kMinus = "-";
|
||||
std::string kMult = "*";
|
||||
std::string kDiv = "/";
|
||||
std::string kMod = "%";
|
||||
std::string kUnaryMinus = "-";
|
||||
std::string kUnaryPlus = ""; // No need to generate anything.
|
||||
}
|
15
src/query/backend/cpp/code_generator.hpp
Normal file
15
src/query/backend/cpp/code_generator.hpp
Normal file
@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "query/frontend/opencypher/generated/CypherBaseVisitor.h"
|
||||
#include "antlr4-runtime.h"
|
||||
|
||||
using antlropencypher::CypherParser;
|
||||
|
||||
class CodeGenerator {
|
||||
void GenerateExpresssion();
|
||||
|
||||
private:
|
||||
std::string code_;
|
||||
};
|
@ -5,6 +5,9 @@
|
||||
#include "query/frontend/opencypher/generated/CypherParser.h"
|
||||
#include "utils/exceptions/basic_exception.hpp"
|
||||
|
||||
namespace backend {
|
||||
namespace cpp {
|
||||
|
||||
// TODO: Figure out what information to put in exception.
|
||||
// Error reporting is tricky since we get stripped query and position of error
|
||||
// in original query is not same as position of error in stripped query. Most
|
||||
@ -22,21 +25,17 @@ class SemanticException : BasicException {
|
||||
// enum VariableType { TYPED_VALUE, LIST, MAP, NODE, RELATIONSHIP, PATH };
|
||||
|
||||
struct Node {
|
||||
std::string output_identifier;
|
||||
std::string output_id;
|
||||
std::vector<std::string> labels;
|
||||
std::unordered_map<std::string,
|
||||
antlropencypher::CypherParser::ExpressionContext*>
|
||||
properties;
|
||||
std::unordered_map<std::string, std::string> properties;
|
||||
};
|
||||
|
||||
struct Relationship {
|
||||
enum Direction { LEFT, RIGHT, BOTH };
|
||||
std::string output_identifier;
|
||||
std::string output_id;
|
||||
Direction direction = Direction::BOTH;
|
||||
std::vector<std::string> types;
|
||||
std::unordered_map<std::string,
|
||||
antlropencypher::CypherParser::ExpressionContext*>
|
||||
properties;
|
||||
std::unordered_map<std::string, std::string> properties;
|
||||
bool has_range = false;
|
||||
// If has_range is false, lower and upper bound values are not important.
|
||||
// lower_bound can be larger than upper_bound and in that case there is no
|
||||
@ -46,7 +45,37 @@ struct Relationship {
|
||||
};
|
||||
|
||||
struct PatternPart {
|
||||
std::string output_identifier;
|
||||
std::vector<Node> nodes;
|
||||
std::vector<Relationship> relationships;
|
||||
std::string output_id;
|
||||
std::vector<std::string> nodes;
|
||||
std::vector<std::string> relationships;
|
||||
};
|
||||
|
||||
enum class Function {
|
||||
LOGICAL_OR,
|
||||
LOGICAL_XOR,
|
||||
LOGICAL_AND,
|
||||
LOGICAL_NOT,
|
||||
EQ,
|
||||
NE,
|
||||
LT,
|
||||
GT,
|
||||
LE,
|
||||
GE,
|
||||
ADDITION,
|
||||
SUBTRACTION,
|
||||
MULTIPLICATION,
|
||||
DIVISION,
|
||||
MODULO,
|
||||
UNARY_MINUS,
|
||||
UNARY_PLUS,
|
||||
PROPERTY_GETTER,
|
||||
LITERAL,
|
||||
PARAMETER
|
||||
};
|
||||
|
||||
struct SimpleExpression {
|
||||
Function function;
|
||||
std::vector<std::string> arguments;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -7,36 +7,79 @@
|
||||
#include <vector>
|
||||
|
||||
#include "query/backend/cpp/compiler_structures.hpp"
|
||||
#include "query/backend/cpp/named_antlr_tokens.hpp"
|
||||
#include "utils/assert.hpp"
|
||||
|
||||
namespace backend {
|
||||
namespace cpp {
|
||||
|
||||
namespace {
|
||||
// List of unnamed tokens visitor needs to use. This should be reviewed on every
|
||||
// grammar change since even changes in ordering of rules will cause antlr to
|
||||
// generate different constants for unnamed tokens.
|
||||
const auto kDotsTokenId = CypherParser::T__12; // ..
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
antlrcpp::Any CypherMainVisitor::visitNodePattern(
|
||||
CypherParser::NodePatternContext *ctx) {
|
||||
bool new_node = true;
|
||||
Node node;
|
||||
node.output_identifier = new_identifier();
|
||||
if (ctx->variable()) {
|
||||
identifiers_map_[ctx->variable()->accept(this).as<std::string>()] =
|
||||
node.output_identifier;
|
||||
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();
|
||||
}
|
||||
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();
|
||||
}
|
||||
if (ctx->nodeLabels()) {
|
||||
node.labels =
|
||||
ctx->nodeLabels()->accept(this).as<std::vector<std::string>>();
|
||||
}
|
||||
if (ctx->properties()) {
|
||||
node.properties =
|
||||
ctx->properties()
|
||||
->accept(this)
|
||||
.as<std::unordered_map<std::string,
|
||||
CypherParser::ExpressionContext *>>();
|
||||
node.properties = ctx->properties()
|
||||
->accept(this)
|
||||
.as<std::unordered_map<std::string, std::string>>();
|
||||
}
|
||||
symbol_table_[node.output_identifier] = node;
|
||||
return node;
|
||||
symbol_table_[node.output_id] = node;
|
||||
return node.output_id;
|
||||
}
|
||||
|
||||
antlrcpp::Any CypherMainVisitor::visitNodeLabels(
|
||||
@ -60,32 +103,30 @@ antlrcpp::Any CypherMainVisitor::visitProperties(
|
||||
|
||||
antlrcpp::Any CypherMainVisitor::visitMapLiteral(
|
||||
CypherParser::MapLiteralContext *ctx) {
|
||||
std::unordered_map<std::string, CypherParser::ExpressionContext *> map;
|
||||
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>()] =
|
||||
ctx->expression()[i];
|
||||
ctx->expression()[i]->accept(this).as<std::string>();
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
antlrcpp::Any CypherMainVisitor::visitSymbolicName(
|
||||
CypherParser::SymbolicNameContext *ctx) {
|
||||
if (!ctx->UnescapedSymbolicName()) {
|
||||
// SymbolicName can only be UnescapedSymbolicName. At this moment we want to
|
||||
// avoid openCypher crazyness that allows variables to be named as keywords
|
||||
// and escaped sequences. To allow all possible variable names allowed by
|
||||
// openCypher grammar we need to figure out escaping rules so we can
|
||||
// reference same variable as unescaped and escaped string.
|
||||
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();
|
||||
}
|
||||
return ctx->getText();
|
||||
return std::string(ctx->getText());
|
||||
}
|
||||
|
||||
antlrcpp::Any CypherMainVisitor::visitPattern(
|
||||
CypherParser::PatternContext *ctx) {
|
||||
std::vector<PatternPart> pattern;
|
||||
std::vector<std::string> pattern;
|
||||
for (auto *pattern_part : ctx->patternPart()) {
|
||||
pattern.push_back(pattern_part->accept(this).as<PatternPart>());
|
||||
pattern.push_back(pattern_part->accept(this).as<std::string>());
|
||||
}
|
||||
return pattern;
|
||||
}
|
||||
@ -95,11 +136,15 @@ antlrcpp::Any CypherMainVisitor::visitPatternPart(
|
||||
PatternPart pattern_part =
|
||||
ctx->anonymousPatternPart()->accept(this).as<PatternPart>();
|
||||
if (ctx->variable()) {
|
||||
identifiers_map_[ctx->variable()->accept(this).as<std::string>()] =
|
||||
pattern_part.output_identifier;
|
||||
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;
|
||||
}
|
||||
symbol_table_[pattern_part.output_identifier] = pattern_part;
|
||||
return pattern_part;
|
||||
symbol_table_[pattern_part.output_id] = pattern_part;
|
||||
return pattern_part.output_id;
|
||||
}
|
||||
|
||||
antlrcpp::Any CypherMainVisitor::visitPatternElement(
|
||||
@ -108,11 +153,12 @@ antlrcpp::Any CypherMainVisitor::visitPatternElement(
|
||||
return ctx->patternElement()->accept(this);
|
||||
}
|
||||
PatternPart pattern_part;
|
||||
pattern_part.output_identifier = new_identifier();
|
||||
pattern_part.nodes.push_back(ctx->nodePattern()->accept(this).as<Node>());
|
||||
pattern_part.output_id = new_id();
|
||||
pattern_part.nodes.push_back(
|
||||
ctx->nodePattern()->accept(this).as<std::string>());
|
||||
for (auto *pattern_element_chain : ctx->patternElementChain()) {
|
||||
auto element =
|
||||
pattern_element_chain->accept(this).as<std::pair<Relationship, Node>>();
|
||||
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);
|
||||
}
|
||||
@ -121,17 +167,61 @@ antlrcpp::Any CypherMainVisitor::visitPatternElement(
|
||||
|
||||
antlrcpp::Any CypherMainVisitor::visitPatternElementChain(
|
||||
CypherParser::PatternElementChainContext *ctx) {
|
||||
return std::pair<Relationship, Node>(
|
||||
ctx->relationshipPattern()->accept(this).as<Relationship>(),
|
||||
ctx->nodePattern()->accept(this).as<Node>());
|
||||
return std::pair<std::string, std::string>(
|
||||
ctx->relationshipPattern()->accept(this).as<std::string>(),
|
||||
ctx->nodePattern()->accept(this).as<std::string>());
|
||||
}
|
||||
|
||||
antlrcpp::Any CypherMainVisitor::visitRelationshipPattern(
|
||||
CypherParser::RelationshipPatternContext *ctx) {
|
||||
bool new_relationship = true;
|
||||
Relationship relationship;
|
||||
relationship.output_identifier = new_identifier();
|
||||
if (ctx->relationshipDetail()) {
|
||||
VisitRelationshipDetail(ctx->relationshipDetail(), relationship);
|
||||
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;
|
||||
}
|
||||
}
|
||||
if (ctx->leftArrowHead() && !ctx->rightArrowHead()) {
|
||||
relationship.direction = Relationship::Direction::LEFT;
|
||||
@ -142,42 +232,16 @@ antlrcpp::Any CypherMainVisitor::visitRelationshipPattern(
|
||||
// grammar.
|
||||
relationship.direction = Relationship::Direction::BOTH;
|
||||
}
|
||||
symbol_table_[relationship.output_identifier] = relationship;
|
||||
return relationship;
|
||||
symbol_table_[relationship.output_id] = relationship;
|
||||
return relationship.output_id;
|
||||
}
|
||||
|
||||
antlrcpp::Any CypherMainVisitor::visitRelationshipDetail(
|
||||
CypherParser::RelationshipDetailContext *) {
|
||||
debug_assert(false, "Unimplemented.");
|
||||
debug_assert(false, "Should never be called. See documentation in hpp.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void CypherMainVisitor::VisitRelationshipDetail(
|
||||
CypherParser::RelationshipDetailContext *ctx, Relationship &relationship) {
|
||||
if (ctx->variable()) {
|
||||
identifiers_map_[ctx->variable()->accept(this).as<std::string>()] =
|
||||
relationship.output_identifier;
|
||||
}
|
||||
if (ctx->relationshipTypes()) {
|
||||
relationship.types =
|
||||
ctx->relationshipTypes()->accept(this).as<std::vector<std::string>>();
|
||||
}
|
||||
if (ctx->properties()) {
|
||||
relationship.properties =
|
||||
ctx->properties()
|
||||
->accept(this)
|
||||
.as<std::unordered_map<std::string,
|
||||
CypherParser::ExpressionContext *>>();
|
||||
}
|
||||
if (ctx->rangeLiteral()) {
|
||||
relationship.has_range = true;
|
||||
auto range =
|
||||
ctx->rangeLiteral()->accept(this).as<std::pair<int64_t, int64_t>>();
|
||||
relationship.lower_bound = range.first;
|
||||
relationship.upper_bound = range.second;
|
||||
}
|
||||
}
|
||||
|
||||
antlrcpp::Any CypherMainVisitor::visitRelationshipTypes(
|
||||
CypherParser::RelationshipTypesContext *ctx) {
|
||||
std::vector<std::string> types;
|
||||
@ -215,6 +279,238 @@ antlrcpp::Any CypherMainVisitor::visitRangeLiteral(
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
antlrcpp::Any CypherMainVisitor::visitAtom(CypherParser::AtomContext *ctx) {
|
||||
if (ctx->literal()) {
|
||||
// This is not very nice since we didn't parse text given in query, but we
|
||||
// left that job to the code generator. Correct approach would be to parse
|
||||
// it and store it in a structure of appropriate type, int, string... And
|
||||
// then code generator would generate its own text based on structure. This
|
||||
// is also a security risk if code generator doesn't parse and escape
|
||||
// text appropriately. At the moment we don;t care much since literal will
|
||||
// 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;
|
||||
} 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
|
||||
// should either replace user's parameter name with generic one or we should
|
||||
// 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;
|
||||
} 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];
|
||||
}
|
||||
// TODO: Implement this. We don't support comprehensions, functions,
|
||||
// filtering... at the moment.
|
||||
throw SemanticException();
|
||||
}
|
||||
|
||||
antlrcpp::Any CypherMainVisitor::visitIntegerLiteral(
|
||||
CypherParser::IntegerLiteralContext *ctx) {
|
||||
int64_t t = 0LL;
|
||||
@ -225,3 +521,5 @@ antlrcpp::Any CypherMainVisitor::visitIntegerLiteral(
|
||||
}
|
||||
return t;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,136 +5,288 @@
|
||||
#include "antlr4-runtime.h"
|
||||
#include "query/backend/cpp/compiler_structures.hpp"
|
||||
|
||||
namespace backend {
|
||||
namespace cpp {
|
||||
|
||||
using antlropencypher::CypherParser;
|
||||
|
||||
class CypherMainVisitor : public antlropencypher::CypherBaseVisitor {
|
||||
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));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates Node and stores it in symbol_table_. If variable is defined it is
|
||||
* stored in identifiers_map_.
|
||||
*
|
||||
* @return Node.
|
||||
*/
|
||||
* Creates Node and stores it in symbol_table_. If variable is defined it is
|
||||
* stored in ids_map_.
|
||||
*
|
||||
* @return string - node id.
|
||||
*/
|
||||
antlrcpp::Any visitNodePattern(
|
||||
CypherParser::NodePatternContext *ctx) override;
|
||||
|
||||
/**
|
||||
* @return vector<string> labels.
|
||||
*/
|
||||
* @return vector<string> labels.
|
||||
*/
|
||||
antlrcpp::Any visitNodeLabels(CypherParser::NodeLabelsContext *ctx) override;
|
||||
|
||||
/**
|
||||
* @return unordered_map<string, ExpressionContext*> properties.
|
||||
*/
|
||||
* @return unordered_map<string, string> properties - property key to
|
||||
* expression id.
|
||||
*/
|
||||
antlrcpp::Any visitProperties(CypherParser::PropertiesContext *ctx) override;
|
||||
|
||||
/**
|
||||
* @return unordered_map<string, ExpressionContext*> map.
|
||||
*/
|
||||
* @return unordered_map<string, string> map - key to expression id.
|
||||
*/
|
||||
antlrcpp::Any visitMapLiteral(CypherParser::MapLiteralContext *ctx) override;
|
||||
|
||||
/**
|
||||
* @return string.
|
||||
*/
|
||||
* @return string.
|
||||
*/
|
||||
antlrcpp::Any visitSymbolicName(
|
||||
CypherParser::SymbolicNameContext *ctx) override;
|
||||
|
||||
/**
|
||||
* @return vector<PatternPart> pattern.
|
||||
*/
|
||||
* @return vector<PatternPart> pattern.
|
||||
*/
|
||||
antlrcpp::Any visitPattern(CypherParser::PatternContext *ctx) override;
|
||||
|
||||
/**
|
||||
* Stores PatternPart in symbol_table_. If variable is defined it is stored in
|
||||
* identifiers_map_.
|
||||
*
|
||||
* @return PatternPart.
|
||||
*/
|
||||
* Stores PatternPart in symbol_table_. If variable is defined it is stored
|
||||
*in
|
||||
* ids_map_.
|
||||
*
|
||||
* @return string - pattern part id.
|
||||
*/
|
||||
antlrcpp::Any visitPatternPart(
|
||||
CypherParser::PatternPartContext *ctx) override;
|
||||
|
||||
/**
|
||||
* Creates PatternPart.
|
||||
*
|
||||
* @return PatternPart.
|
||||
*/
|
||||
* Creates PatternPart.
|
||||
*
|
||||
* @return PatternPart.
|
||||
*/
|
||||
antlrcpp::Any visitPatternElement(
|
||||
CypherParser::PatternElementContext *ctx) override;
|
||||
|
||||
/**
|
||||
* @return pair<Relationship, Node>
|
||||
*/
|
||||
* @return pair<string, string> - node and relationship ids.
|
||||
*/
|
||||
antlrcpp::Any visitPatternElementChain(
|
||||
CypherParser::PatternElementChainContext *ctx) override;
|
||||
|
||||
/**
|
||||
* Creates Relationship and stores it in symbol_table_.
|
||||
*
|
||||
*/
|
||||
* Creates Relationship and stores it in symbol_table_. If variable is defined
|
||||
* it is stored in symbol_table_.
|
||||
*
|
||||
* @return string - relationship id.
|
||||
*/
|
||||
antlrcpp::Any visitRelationshipPattern(
|
||||
CypherParser::RelationshipPatternContext *ctx) override;
|
||||
|
||||
/**
|
||||
* This should never be called. Call VisitRelationshipDetail with already
|
||||
* created Relationship instead.
|
||||
*/
|
||||
* This should never be called. Everything is done directly in
|
||||
* visitRelationshipPattern.
|
||||
*/
|
||||
antlrcpp::Any visitRelationshipDetail(
|
||||
CypherParser::RelationshipDetailContext *ctx) override;
|
||||
|
||||
/**
|
||||
* If variable is defined it is stored in symbol_table_. Relationship is
|
||||
* filled with properties, types and range if provided.
|
||||
* Use this instead of antlr generated visitRelationshipDetail with already
|
||||
* created Relationship. If we should have used visitRelationshipDetail
|
||||
* (relationshipDetail is optional production in relationshipPattern) then we
|
||||
* would have needed to return not completely initialised Relationship.
|
||||
*/
|
||||
void VisitRelationshipDetail(CypherParser::RelationshipDetailContext *ctx,
|
||||
Relationship &relationship);
|
||||
|
||||
/**
|
||||
* @return vector<string>.
|
||||
*/
|
||||
* @return vector<string>.
|
||||
*/
|
||||
antlrcpp::Any visitRelationshipTypes(
|
||||
CypherParser::RelationshipTypesContext *ctx) override;
|
||||
|
||||
/**
|
||||
* @return int64_t.
|
||||
*/
|
||||
antlrcpp::Any visitIntegerLiteral(
|
||||
CypherParser::IntegerLiteralContext *ctx) override;
|
||||
|
||||
/**
|
||||
* @return pair<int64_t, int64_t>.
|
||||
*/
|
||||
* @return pair<int64_t, int64_t>.
|
||||
*/
|
||||
antlrcpp::Any visitRangeLiteral(
|
||||
CypherParser::RangeLiteralContext *ctx) override;
|
||||
|
||||
/**
|
||||
* Top level expression.
|
||||
*
|
||||
* @return string - expression id.
|
||||
*/
|
||||
antlrcpp::Any visitExpression(CypherParser::ExpressionContext *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;
|
||||
|
||||
/**
|
||||
* AND.
|
||||
*
|
||||
* @return string - expression id.
|
||||
*/
|
||||
antlrcpp::Any visitExpression10(
|
||||
CypherParser::Expression10Context *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;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Property lookup, test for node labels existence...
|
||||
*
|
||||
* @return string - expression id.
|
||||
*/
|
||||
antlrcpp::Any visitExpression2(
|
||||
CypherParser::Expression2Context *ctx) override;
|
||||
|
||||
/**
|
||||
* Literals, params, list comprehension...
|
||||
*
|
||||
* @return string - expression id.
|
||||
*/
|
||||
antlrcpp::Any visitAtom(CypherParser::AtomContext *ctx) override;
|
||||
|
||||
// antlrcpp::Any visitLiteral(CypherParser::LiteralContext *ctx) override {
|
||||
// return visitChildren(ctx);
|
||||
// }
|
||||
//
|
||||
// antlrcpp::Any visitBooleanLiteral(
|
||||
// CypherParser::BooleanLiteralContext *ctx) override {
|
||||
// return visitChildren(ctx);
|
||||
// }
|
||||
//
|
||||
// antlrcpp::Any visitListLiteral(
|
||||
// CypherParser::ListLiteralContext *ctx) override {
|
||||
// return visitChildren(ctx);
|
||||
// }
|
||||
//
|
||||
// antlrcpp::Any visitParenthesizedExpression(
|
||||
// CypherParser::ParenthesizedExpressionContext *ctx) override {
|
||||
// return visitChildren(ctx);
|
||||
// }
|
||||
|
||||
/**
|
||||
* @return int64_t.
|
||||
*/
|
||||
antlrcpp::Any visitIntegerLiteral(
|
||||
CypherParser::IntegerLiteralContext *ctx) override;
|
||||
|
||||
public:
|
||||
// TODO: These temporary getters should eventually be replaced with something
|
||||
// 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
|
||||
// Currently there are needed for testing. cypher_main_visitor test should
|
||||
// be
|
||||
// refactored once these getters are deleted.
|
||||
const std::unordered_map<std::string, std::string> &identifiers_map() const {
|
||||
return identifiers_map_;
|
||||
}
|
||||
const std::unordered_map<std::string, antlrcpp::Any> &symbol_table() const {
|
||||
return symbol_table_;
|
||||
}
|
||||
const auto &ids_map() const { return ids_map_; }
|
||||
const auto &symbol_table() const { return symbol_table_; }
|
||||
|
||||
private:
|
||||
// Return new output code identifier.
|
||||
// TODO: Should we generate identifiers with more readable names: node_1,
|
||||
// relationship_5, ...?
|
||||
std::string new_identifier() const {
|
||||
static int next_identifier = 0;
|
||||
return "id" + std::to_string(next_identifier++);
|
||||
}
|
||||
// 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 identifiers (nodes, relationships, values, lists ...) from query
|
||||
// code to identifier that is used in generated code;
|
||||
std::unordered_map<std::string, std::string> identifiers_map_;
|
||||
|
||||
// Mapping of output (generated) code identifiers to appropriate parser
|
||||
// Mapping of output (generated) code ids to appropriate parser
|
||||
// structure.
|
||||
std::unordered_map<std::string, antlrcpp::Any> symbol_table_;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
24
src/query/backend/cpp/named_antlr_tokens.hpp
Normal file
24
src/query/backend/cpp/named_antlr_tokens.hpp
Normal file
@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "query/frontend/opencypher/generated/CypherBaseVisitor.h"
|
||||
|
||||
using antlropencypher::CypherParser;
|
||||
|
||||
// List of unnamed tokens visitor needs to use. This should be reviewed on every
|
||||
// grammar change since even changes in ordering of rules will cause antlr to
|
||||
// generate different constants for unnamed tokens.
|
||||
const auto kDotsTokenId = CypherParser::T__11; // ..
|
||||
const auto kEqTokenId = CypherParser::T__2; // =
|
||||
const auto kNeTokenId1 = CypherParser::T__18; // <>
|
||||
const auto kNeTokenId2 = CypherParser::T__19; // !=
|
||||
const auto kLtTokenId = CypherParser::T__20; // <
|
||||
const auto kGtTokenId = CypherParser::T__21; // >
|
||||
const auto kLeTokenId = CypherParser::T__22; // <=
|
||||
const auto kGeTokenId = CypherParser::T__23; // >=
|
||||
const auto kPlusTokenId = CypherParser::T__12; // +
|
||||
const auto kMinusTokenId = CypherParser::T__13; // -
|
||||
const auto kMultTokenId = CypherParser::T__4; // *
|
||||
const auto kDivTokenId = CypherParser::T__14; // /
|
||||
const auto kModTokenId = CypherParser::T__15; // %
|
||||
const auto kUnaryPlusTokenId = CypherParser::T__12; // +
|
||||
const auto kUnaryMinusTokenId = CypherParser::T__13; // -
|
20
src/query/backend/cpp/runtime_functions.cpp
Normal file
20
src/query/backend/cpp/runtime_functions.cpp
Normal file
@ -0,0 +1,20 @@
|
||||
#include "query/backend/cpp/runtime_functions.hpp"
|
||||
|
||||
#include "storage/record_accessor.hpp"
|
||||
#include "storage/vertex_accessor.hpp"
|
||||
#include "storage/edge_accessor.hpp"
|
||||
|
||||
namespace backend {
|
||||
namespace cpp {
|
||||
|
||||
TypedValue GetProperty(const TypedValue &t, const std::string &key) {
|
||||
if (t.type() == TypedValue::Type::Vertex) {
|
||||
return t.Value<VertexAccessor>().PropsAt(key);
|
||||
} else if (t.type() == TypedValue::Type::Edge) {
|
||||
return t.Value<EdgeAccessor>().PropsAt(key);
|
||||
} else {
|
||||
throw TypedValueException("Unsupported type.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
10
src/query/backend/cpp/runtime_functions.hpp
Normal file
10
src/query/backend/cpp/runtime_functions.hpp
Normal file
@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "query/backend/cpp/typed_value.hpp"
|
||||
|
||||
namespace backend {
|
||||
namespace cpp {
|
||||
|
||||
TypedValue GetProperty(const TypedValue &t, const std::string &key);
|
||||
}
|
||||
}
|
@ -32,7 +32,7 @@ TypedValue::TypedValue(const PropertyValue& value) {
|
||||
new (&list_v) std::vector<TypedValue>(vec.begin(), vec.end());
|
||||
return;
|
||||
}
|
||||
permanent_fail("Unsupported PropertyValue::Type");
|
||||
permanent_fail("Unsupported type");
|
||||
}
|
||||
|
||||
TypedValue::operator PropertyValue() const {
|
||||
@ -51,73 +51,86 @@ TypedValue::operator PropertyValue() const {
|
||||
return PropertyValue(
|
||||
std::vector<PropertyValue>(list_v.begin(), list_v.end()));
|
||||
default:
|
||||
permanent_fail("Unsupported PropertyValue::Type");
|
||||
throw TypedValueException(
|
||||
"Unsupported conversion from TypedValue to PropertyValue");
|
||||
}
|
||||
permanent_fail("Unsupported PropertyValue::Type");
|
||||
}
|
||||
|
||||
// TODO: Refactor this. Value<bool> should be ValueBool. If we do it in that way
|
||||
// we could return reference for complex types and value for primitive types.
|
||||
// Other solution would be to add additional overloads for references, for
|
||||
// example Value<string&>.
|
||||
// Value extraction template instantiations
|
||||
template <>
|
||||
bool TypedValue::Value<bool>() const {
|
||||
debug_assert(type_ == TypedValue::Type::Bool,
|
||||
"Incompatible template param and type");
|
||||
if (type_ != TypedValue::Type::Bool) {
|
||||
throw TypedValueException("Incompatible template param and type");
|
||||
}
|
||||
return bool_v;
|
||||
}
|
||||
|
||||
template <>
|
||||
int TypedValue::Value<int>() const {
|
||||
debug_assert(type_ == TypedValue::Type::Int,
|
||||
"Incompatible template param and type");
|
||||
if (type_ != TypedValue::Type::Int) {
|
||||
throw TypedValueException("Incompatible template param and type");
|
||||
}
|
||||
return int_v;
|
||||
}
|
||||
|
||||
template <>
|
||||
double TypedValue::Value<double>() const {
|
||||
debug_assert(type_ == TypedValue::Type::Double,
|
||||
"Incompatible template param and type");
|
||||
if (type_ != TypedValue::Type::Double) {
|
||||
throw TypedValueException("Incompatible template param and type");
|
||||
}
|
||||
return double_v;
|
||||
}
|
||||
|
||||
template <>
|
||||
std::string TypedValue::Value<std::string>() const {
|
||||
debug_assert(type_ == TypedValue::Type::String,
|
||||
"Incompatible template param and type");
|
||||
if (type_ != TypedValue::Type::String) {
|
||||
throw TypedValueException("Incompatible template param and type");
|
||||
}
|
||||
return string_v;
|
||||
}
|
||||
|
||||
template <>
|
||||
std::vector<TypedValue> TypedValue::Value<std::vector<TypedValue>>() const {
|
||||
debug_assert(type_ == TypedValue::Type::List,
|
||||
"Incompatible template param and type");
|
||||
if (type_ != TypedValue::Type::List) {
|
||||
throw TypedValueException("Incompatible template param and type");
|
||||
}
|
||||
return list_v;
|
||||
}
|
||||
|
||||
template <>
|
||||
std::map<std::string, TypedValue>
|
||||
TypedValue::Value<std::map<std::string, TypedValue>>() const {
|
||||
debug_assert(type_ == TypedValue::Type::Map,
|
||||
"Incompatible template param and type");
|
||||
if (type_ != TypedValue::Type::Map) {
|
||||
throw TypedValueException("Incompatible template param and type");
|
||||
}
|
||||
return map_v;
|
||||
}
|
||||
|
||||
template <>
|
||||
VertexAccessor TypedValue::Value<VertexAccessor>() const {
|
||||
debug_assert(type_ == TypedValue::Type::Vertex,
|
||||
"Incompatible template param and type");
|
||||
if (type_ != TypedValue::Type::Vertex) {
|
||||
throw TypedValueException("Incompatible template param and type");
|
||||
}
|
||||
return vertex_v;
|
||||
}
|
||||
|
||||
template <>
|
||||
EdgeAccessor TypedValue::Value<EdgeAccessor>() const {
|
||||
debug_assert(type_ == TypedValue::Type::Edge,
|
||||
"Incompatible template param and type");
|
||||
if (type_ != TypedValue::Type::Edge) {
|
||||
throw TypedValueException("Incompatible template param and type");
|
||||
}
|
||||
return edge_v;
|
||||
}
|
||||
|
||||
template <>
|
||||
Path TypedValue::Value<Path>() const {
|
||||
debug_assert(type_ == TypedValue::Type::Path,
|
||||
"Incompatible template param and type");
|
||||
if (type_ != TypedValue::Type::Path) {
|
||||
throw TypedValueException("Incompatible template param and type");
|
||||
}
|
||||
return path_v;
|
||||
}
|
||||
|
||||
@ -308,9 +321,9 @@ double ToDouble(const TypedValue& value) {
|
||||
return (double)value.Value<int>();
|
||||
case TypedValue::Type::Double:
|
||||
return value.Value<double>();
|
||||
|
||||
default:
|
||||
permanent_fail("Unsupported TypedValue::Type");
|
||||
throw TypedValueException(
|
||||
"Unsupported TypedValue::Type conversion to double");
|
||||
}
|
||||
}
|
||||
|
||||
@ -324,22 +337,25 @@ TypedValue operator<(const TypedValue& a, const TypedValue& b) {
|
||||
|
||||
if (a.type() == TypedValue::Type::String ||
|
||||
b.type() == TypedValue::Type::String) {
|
||||
if (a.type() != b.type())
|
||||
if (a.type() != b.type()) {
|
||||
throw TypedValueException("Invalid equality operand types({} + {})",
|
||||
a.type(), b.type());
|
||||
else
|
||||
} else {
|
||||
return a.Value<std::string>() < b.Value<std::string>();
|
||||
}
|
||||
}
|
||||
|
||||
// at this point we only have int and double
|
||||
if (a.type() == TypedValue::Type::Double ||
|
||||
b.type() == TypedValue::Type::Double)
|
||||
b.type() == TypedValue::Type::Double) {
|
||||
return ToDouble(a) < ToDouble(b);
|
||||
else
|
||||
} else {
|
||||
return a.Value<int>() < b.Value<int>();
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: 2 = "2" -> false, I don't this is handled correctly at the moment.
|
||||
// TODO: 2 = "2" -> false, I don't think this is handled correctly at the
|
||||
// moment.
|
||||
TypedValue operator==(const TypedValue& a, const TypedValue& b) {
|
||||
if (a.type() == TypedValue::Type::Null || b.type() == TypedValue::Type::Null)
|
||||
return TypedValue::Null;
|
||||
@ -349,7 +365,8 @@ TypedValue operator==(const TypedValue& a, const TypedValue& b) {
|
||||
if (a.type() == TypedValue::Type::List &&
|
||||
b.type() == TypedValue::Type::List) {
|
||||
// Potential optimisation: There is no need to copies of both lists to
|
||||
// compare them. If operator becomes a friend of TypedValue class then we
|
||||
// compare them. If operator becomes a friend of TypedValue class then
|
||||
// we
|
||||
// can compare list_v-s directly.
|
||||
auto list1 = a.Value<std::vector<TypedValue>>();
|
||||
auto list2 = b.Value<std::vector<TypedValue>>();
|
||||
@ -361,7 +378,8 @@ TypedValue operator==(const TypedValue& a, const TypedValue& b) {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// We are not compatible with neo4j at this point. In neo4j 2 = [2] compares
|
||||
// We are not compatible with neo4j at this point. In neo4j 2 = [2]
|
||||
// compares
|
||||
// to true. That is not the end of unselfishness of developers at neo4j so
|
||||
// they allow us to use as many braces as we want to get to the truth in
|
||||
// list comparison, so [[2]] = [[[[[[2]]]]]] compares to true in neo4j as
|
||||
@ -373,27 +391,30 @@ TypedValue operator==(const TypedValue& a, const TypedValue& b) {
|
||||
|
||||
if (a.type() == TypedValue::Type::String ||
|
||||
b.type() == TypedValue::Type::String) {
|
||||
if (a.type() != b.type())
|
||||
if (a.type() != b.type()) {
|
||||
throw TypedValueException("Invalid equality operand types({} + {})",
|
||||
a.type(), b.type());
|
||||
else
|
||||
} else {
|
||||
return a.Value<std::string>() == b.Value<std::string>();
|
||||
}
|
||||
}
|
||||
|
||||
if (a.type() == TypedValue::Type::Bool ||
|
||||
b.type() == TypedValue::Type::Bool) {
|
||||
if (a.type() != b.type())
|
||||
if (a.type() != b.type()) {
|
||||
throw TypedValueException("Invalid equality operand types({} + {})",
|
||||
a.type(), b.type());
|
||||
else
|
||||
} else {
|
||||
return a.Value<bool>() == b.Value<bool>();
|
||||
}
|
||||
}
|
||||
// at this point we only have int and double
|
||||
if (a.type() == TypedValue::Type::Double ||
|
||||
b.type() == TypedValue::Type::Double) {
|
||||
return ToDouble(a) == ToDouble(b);
|
||||
} else
|
||||
} else {
|
||||
return a.Value<int>() == b.Value<int>();
|
||||
}
|
||||
}
|
||||
|
||||
TypedValue operator!(const TypedValue& a) {
|
||||
@ -402,7 +423,6 @@ TypedValue operator!(const TypedValue& a) {
|
||||
return TypedValue::Null;
|
||||
case TypedValue::Type::Bool:
|
||||
return TypedValue(!a.Value<bool>());
|
||||
|
||||
default:
|
||||
throw TypedValueException("Invalid logical not operand type (!{})",
|
||||
a.type());
|
||||
@ -423,10 +443,10 @@ std::string ValueToString(const TypedValue& value) {
|
||||
return std::to_string(value.Value<int>());
|
||||
case TypedValue::Type::Double:
|
||||
return fmt::format("{}", value.Value<double>());
|
||||
|
||||
// unsupported situations
|
||||
default:
|
||||
permanent_fail("Unsupported TypedValue::Type");
|
||||
throw TypedValueException(
|
||||
"Unsupported TypedValue::Type conversion to string");
|
||||
}
|
||||
}
|
||||
|
||||
@ -438,7 +458,6 @@ TypedValue operator-(const TypedValue& a) {
|
||||
return -a.Value<int>();
|
||||
case TypedValue::Type::Double:
|
||||
return -a.Value<double>();
|
||||
|
||||
default:
|
||||
throw TypedValueException("Invalid unary minus operand type (-{})",
|
||||
a.type());
|
||||
@ -501,8 +520,9 @@ TypedValue operator+(const TypedValue& a, const TypedValue& b) {
|
||||
if (a.type() == TypedValue::Type::Double ||
|
||||
b.type() == TypedValue::Type::Double) {
|
||||
return ToDouble(a) + ToDouble(b);
|
||||
} else
|
||||
} else {
|
||||
return a.Value<int>() + b.Value<int>();
|
||||
}
|
||||
}
|
||||
|
||||
TypedValue operator-(const TypedValue& a, const TypedValue& b) {
|
||||
@ -515,8 +535,9 @@ TypedValue operator-(const TypedValue& a, const TypedValue& b) {
|
||||
if (a.type() == TypedValue::Type::Double ||
|
||||
b.type() == TypedValue::Type::Double) {
|
||||
return ToDouble(a) - ToDouble(b);
|
||||
} else
|
||||
} else {
|
||||
return a.Value<int>() - b.Value<int>();
|
||||
}
|
||||
}
|
||||
|
||||
TypedValue operator/(const TypedValue& a, const TypedValue& b) {
|
||||
@ -529,8 +550,9 @@ TypedValue operator/(const TypedValue& a, const TypedValue& b) {
|
||||
if (a.type() == TypedValue::Type::Double ||
|
||||
b.type() == TypedValue::Type::Double) {
|
||||
return ToDouble(a) / ToDouble(b);
|
||||
} else
|
||||
} else {
|
||||
return a.Value<int>() / b.Value<int>();
|
||||
}
|
||||
}
|
||||
|
||||
TypedValue operator*(const TypedValue& a, const TypedValue& b) {
|
||||
@ -543,8 +565,9 @@ TypedValue operator*(const TypedValue& a, const TypedValue& b) {
|
||||
if (a.type() == TypedValue::Type::Double ||
|
||||
b.type() == TypedValue::Type::Double) {
|
||||
return ToDouble(a) * ToDouble(b);
|
||||
} else
|
||||
} else {
|
||||
return a.Value<int>() * b.Value<int>();
|
||||
}
|
||||
}
|
||||
|
||||
TypedValue operator%(const TypedValue& a, const TypedValue& b) {
|
||||
@ -557,8 +580,9 @@ TypedValue operator%(const TypedValue& a, const TypedValue& b) {
|
||||
if (a.type() == TypedValue::Type::Double ||
|
||||
b.type() == TypedValue::Type::Double) {
|
||||
return (double)fmod(ToDouble(a), ToDouble(b));
|
||||
} else
|
||||
} else {
|
||||
return a.Value<int>() % b.Value<int>();
|
||||
}
|
||||
}
|
||||
|
||||
inline bool IsLogicallyOk(const TypedValue& a) {
|
||||
@ -570,23 +594,41 @@ inline bool IsLogicallyOk(const TypedValue& a) {
|
||||
TypedValue operator&&(const TypedValue& a, const TypedValue& b) {
|
||||
if (IsLogicallyOk(a) && IsLogicallyOk(b)) {
|
||||
if (a.type() == TypedValue::Type::Null ||
|
||||
b.type() == TypedValue::Type::Null)
|
||||
b.type() == TypedValue::Type::Null) {
|
||||
return TypedValue::Null;
|
||||
else
|
||||
} else {
|
||||
return a.Value<bool>() && b.Value<bool>();
|
||||
} else
|
||||
}
|
||||
} else {
|
||||
throw TypedValueException("Invalid logical and operand types({} && {})",
|
||||
a.type(), b.type());
|
||||
}
|
||||
}
|
||||
|
||||
TypedValue operator||(const TypedValue& a, const TypedValue& b) {
|
||||
if (IsLogicallyOk(a) && IsLogicallyOk(b)) {
|
||||
if (a.type() == TypedValue::Type::Null ||
|
||||
b.type() == TypedValue::Type::Null)
|
||||
b.type() == TypedValue::Type::Null) {
|
||||
return TypedValue::Null;
|
||||
else
|
||||
} else {
|
||||
return a.Value<bool>() || b.Value<bool>();
|
||||
} else
|
||||
}
|
||||
} else {
|
||||
throw TypedValueException("Invalid logical and operand types({} && {})",
|
||||
a.type(), b.type());
|
||||
}
|
||||
}
|
||||
|
||||
TypedValue operator^(const TypedValue& a, const TypedValue& b) {
|
||||
if (IsLogicallyOk(a) && IsLogicallyOk(b)) {
|
||||
if (a.type() == TypedValue::Type::Null ||
|
||||
b.type() == TypedValue::Type::Null) {
|
||||
return TypedValue::Null;
|
||||
} else {
|
||||
return static_cast<bool>(a.Value<bool>() ^ b.Value<bool>());
|
||||
}
|
||||
} else {
|
||||
throw TypedValueException("Invalid logical and operand types({} && {})",
|
||||
a.type(), b.type());
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
typedef traversal_template::Path<VertexAccessor, EdgeAccessor> Path;
|
||||
|
||||
// TODO: Neo4j does overflow checking. Should we also implement it?
|
||||
/**
|
||||
* Encapsulation of a value and it's type encapsulated in a class that has no
|
||||
* compiled-time info about that type.
|
||||
@ -151,6 +152,10 @@ TypedValue operator%(const TypedValue& a, const TypedValue& b);
|
||||
// binary bool operators
|
||||
TypedValue operator&&(const TypedValue& a, const TypedValue& b);
|
||||
TypedValue operator||(const TypedValue& a, const TypedValue& b);
|
||||
// binary bool xor, not power operator
|
||||
// Be careful: since ^ is binary operator and || and && are logical operators
|
||||
// they have different priority in c++.
|
||||
TypedValue operator^(const TypedValue& a, const TypedValue& b);
|
||||
|
||||
// stream output
|
||||
std::ostream& operator<<(std::ostream& os, const TypedValue::Type type);
|
||||
|
@ -45,73 +45,70 @@ T__43=44
|
||||
T__44=45
|
||||
T__45=46
|
||||
T__46=47
|
||||
T__47=48
|
||||
T__48=49
|
||||
StringLiteral=50
|
||||
EscapedChar=51
|
||||
HexInteger=52
|
||||
DecimalInteger=53
|
||||
OctalInteger=54
|
||||
HexLetter=55
|
||||
HexDigit=56
|
||||
Digit=57
|
||||
NonZeroDigit=58
|
||||
NonZeroOctDigit=59
|
||||
OctDigit=60
|
||||
ZeroDigit=61
|
||||
ExponentDecimalReal=62
|
||||
RegularDecimalReal=63
|
||||
UNION=64
|
||||
ALL=65
|
||||
OPTIONAL=66
|
||||
MATCH=67
|
||||
UNWIND=68
|
||||
AS=69
|
||||
MERGE=70
|
||||
ON=71
|
||||
CREATE=72
|
||||
SET=73
|
||||
DETACH=74
|
||||
DELETE=75
|
||||
REMOVE=76
|
||||
WITH=77
|
||||
DISTINCT=78
|
||||
RETURN=79
|
||||
ORDER=80
|
||||
BY=81
|
||||
L_SKIP=82
|
||||
LIMIT=83
|
||||
ASCENDING=84
|
||||
ASC=85
|
||||
DESCENDING=86
|
||||
DESC=87
|
||||
WHERE=88
|
||||
OR=89
|
||||
XOR=90
|
||||
AND=91
|
||||
NOT=92
|
||||
IN=93
|
||||
STARTS=94
|
||||
ENDS=95
|
||||
CONTAINS=96
|
||||
IS=97
|
||||
CYPHERNULL=98
|
||||
COUNT=99
|
||||
FILTER=100
|
||||
EXTRACT=101
|
||||
ANY=102
|
||||
NONE=103
|
||||
SINGLE=104
|
||||
TRUE=105
|
||||
FALSE=106
|
||||
UnescapedSymbolicName=107
|
||||
IdentifierStart=108
|
||||
IdentifierPart=109
|
||||
EscapedSymbolicName=110
|
||||
SP=111
|
||||
WHITESPACE=112
|
||||
Comment=113
|
||||
L_0X=114
|
||||
StringLiteral=48
|
||||
EscapedChar=49
|
||||
HexInteger=50
|
||||
DecimalInteger=51
|
||||
OctalInteger=52
|
||||
HexLetter=53
|
||||
HexDigit=54
|
||||
Digit=55
|
||||
NonZeroDigit=56
|
||||
NonZeroOctDigit=57
|
||||
OctDigit=58
|
||||
ZeroDigit=59
|
||||
ExponentDecimalReal=60
|
||||
RegularDecimalReal=61
|
||||
UNION=62
|
||||
ALL=63
|
||||
OPTIONAL=64
|
||||
MATCH=65
|
||||
UNWIND=66
|
||||
AS=67
|
||||
MERGE=68
|
||||
ON=69
|
||||
CREATE=70
|
||||
SET=71
|
||||
DETACH=72
|
||||
DELETE=73
|
||||
REMOVE=74
|
||||
WITH=75
|
||||
DISTINCT=76
|
||||
RETURN=77
|
||||
ORDER=78
|
||||
BY=79
|
||||
L_SKIP=80
|
||||
LIMIT=81
|
||||
ASCENDING=82
|
||||
ASC=83
|
||||
DESCENDING=84
|
||||
DESC=85
|
||||
WHERE=86
|
||||
OR=87
|
||||
XOR=88
|
||||
AND=89
|
||||
NOT=90
|
||||
IN=91
|
||||
STARTS=92
|
||||
ENDS=93
|
||||
CONTAINS=94
|
||||
IS=95
|
||||
CYPHERNULL=96
|
||||
COUNT=97
|
||||
FILTER=98
|
||||
EXTRACT=99
|
||||
ANY=100
|
||||
NONE=101
|
||||
SINGLE=102
|
||||
TRUE=103
|
||||
FALSE=104
|
||||
UnescapedSymbolicName=105
|
||||
IdentifierStart=106
|
||||
IdentifierPart=107
|
||||
EscapedSymbolicName=108
|
||||
SP=109
|
||||
WHITESPACE=110
|
||||
Comment=111
|
||||
';'=1
|
||||
','=2
|
||||
'='=3
|
||||
@ -120,45 +117,43 @@ L_0X=114
|
||||
'('=6
|
||||
')'=7
|
||||
'['=8
|
||||
'?'=9
|
||||
']'=10
|
||||
':'=11
|
||||
'|'=12
|
||||
'..'=13
|
||||
'+'=14
|
||||
'-'=15
|
||||
'/'=16
|
||||
'%'=17
|
||||
'^'=18
|
||||
'=~'=19
|
||||
'<>'=20
|
||||
'!='=21
|
||||
'<'=22
|
||||
'>'=23
|
||||
'<='=24
|
||||
'>='=25
|
||||
'.'=26
|
||||
'!'=27
|
||||
'{'=28
|
||||
'}'=29
|
||||
'$'=30
|
||||
'⟨'=31
|
||||
'〈'=32
|
||||
'﹤'=33
|
||||
'<'=34
|
||||
'⟩'=35
|
||||
'〉'=36
|
||||
'﹥'=37
|
||||
'>'=38
|
||||
''=39
|
||||
'‐'=40
|
||||
'‑'=41
|
||||
'‒'=42
|
||||
'–'=43
|
||||
'—'=44
|
||||
'―'=45
|
||||
'−'=46
|
||||
'﹘'=47
|
||||
'﹣'=48
|
||||
'-'=49
|
||||
'0'=61
|
||||
']'=9
|
||||
':'=10
|
||||
'|'=11
|
||||
'..'=12
|
||||
'+'=13
|
||||
'-'=14
|
||||
'/'=15
|
||||
'%'=16
|
||||
'^'=17
|
||||
'=~'=18
|
||||
'<>'=19
|
||||
'!='=20
|
||||
'<'=21
|
||||
'>'=22
|
||||
'<='=23
|
||||
'>='=24
|
||||
'.'=25
|
||||
'{'=26
|
||||
'}'=27
|
||||
'$'=28
|
||||
'⟨'=29
|
||||
'〈'=30
|
||||
'﹤'=31
|
||||
'<'=32
|
||||
'⟩'=33
|
||||
'〉'=34
|
||||
'﹥'=35
|
||||
'>'=36
|
||||
''=37
|
||||
'‐'=38
|
||||
'‑'=39
|
||||
'‒'=40
|
||||
'–'=41
|
||||
'—'=42
|
||||
'―'=43
|
||||
'−'=44
|
||||
'﹘'=45
|
||||
'﹣'=46
|
||||
'-'=47
|
||||
'0'=59
|
||||
|
@ -1,8 +1,9 @@
|
||||
|
||||
// Generated from
|
||||
// /home/buda/Workspace/code/memgraph/memgraph/src/query/frontend/opencypher/grammar/Cypher.g4
|
||||
// by ANTLR 4.6
|
||||
// Generated from /home/mislav/code/memgraph/memgraph/src/query/frontend/opencypher/grammar/Cypher.g4 by ANTLR 4.6
|
||||
|
||||
|
||||
#include "CypherBaseListener.h"
|
||||
|
||||
|
||||
using namespace antlropencypher;
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
|
||||
// Generated from /home/buda/Workspace/code/memgraph/memgraph/src/query/frontend/opencypher/grammar/Cypher.g4 by ANTLR 4.6
|
||||
// Generated from /home/mislav/code/memgraph/memgraph/src/query/frontend/opencypher/grammar/Cypher.g4 by ANTLR 4.6
|
||||
|
||||
#pragma once
|
||||
|
||||
@ -216,6 +216,9 @@ public:
|
||||
virtual void enterListComprehension(CypherParser::ListComprehensionContext * /*ctx*/) override { }
|
||||
virtual void exitListComprehension(CypherParser::ListComprehensionContext * /*ctx*/) override { }
|
||||
|
||||
virtual void enterPatternComprehension(CypherParser::PatternComprehensionContext * /*ctx*/) override { }
|
||||
virtual void exitPatternComprehension(CypherParser::PatternComprehensionContext * /*ctx*/) override { }
|
||||
|
||||
virtual void enterPropertyLookup(CypherParser::PropertyLookupContext * /*ctx*/) override { }
|
||||
virtual void exitPropertyLookup(CypherParser::PropertyLookupContext * /*ctx*/) override { }
|
||||
|
||||
|
@ -1,8 +1,9 @@
|
||||
|
||||
// Generated from
|
||||
// /home/buda/Workspace/code/memgraph/memgraph/src/query/frontend/opencypher/grammar/Cypher.g4
|
||||
// by ANTLR 4.6
|
||||
// Generated from /home/mislav/code/memgraph/memgraph/src/query/frontend/opencypher/grammar/Cypher.g4 by ANTLR 4.6
|
||||
|
||||
|
||||
#include "CypherBaseVisitor.h"
|
||||
|
||||
|
||||
using namespace antlropencypher;
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
|
||||
// Generated from /home/buda/Workspace/code/memgraph/memgraph/src/query/frontend/opencypher/grammar/Cypher.g4 by ANTLR 4.6
|
||||
// Generated from /home/mislav/code/memgraph/memgraph/src/query/frontend/opencypher/grammar/Cypher.g4 by ANTLR 4.6
|
||||
|
||||
#pragma once
|
||||
|
||||
@ -281,6 +281,10 @@ public:
|
||||
return visitChildren(ctx);
|
||||
}
|
||||
|
||||
virtual antlrcpp::Any visitPatternComprehension(CypherParser::PatternComprehensionContext *ctx) override {
|
||||
return visitChildren(ctx);
|
||||
}
|
||||
|
||||
virtual antlrcpp::Any visitPropertyLookup(CypherParser::PropertyLookupContext *ctx) override {
|
||||
return visitChildren(ctx);
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,5 @@
|
||||
|
||||
// Generated from /home/buda/Workspace/code/memgraph/memgraph/src/query/frontend/opencypher/grammar/Cypher.g4 by ANTLR 4.6
|
||||
// Generated from /home/mislav/code/memgraph/memgraph/src/query/frontend/opencypher/grammar/Cypher.g4 by ANTLR 4.6
|
||||
|
||||
#pragma once
|
||||
|
||||
@ -20,20 +20,19 @@ public:
|
||||
T__26 = 27, T__27 = 28, T__28 = 29, T__29 = 30, T__30 = 31, T__31 = 32,
|
||||
T__32 = 33, T__33 = 34, T__34 = 35, T__35 = 36, T__36 = 37, T__37 = 38,
|
||||
T__38 = 39, T__39 = 40, T__40 = 41, T__41 = 42, T__42 = 43, T__43 = 44,
|
||||
T__44 = 45, T__45 = 46, T__46 = 47, T__47 = 48, T__48 = 49, StringLiteral = 50,
|
||||
EscapedChar = 51, HexInteger = 52, DecimalInteger = 53, OctalInteger = 54,
|
||||
HexLetter = 55, HexDigit = 56, Digit = 57, NonZeroDigit = 58, NonZeroOctDigit = 59,
|
||||
OctDigit = 60, ZeroDigit = 61, ExponentDecimalReal = 62, RegularDecimalReal = 63,
|
||||
UNION = 64, ALL = 65, OPTIONAL = 66, MATCH = 67, UNWIND = 68, AS = 69,
|
||||
MERGE = 70, ON = 71, CREATE = 72, SET = 73, DETACH = 74, DELETE = 75,
|
||||
REMOVE = 76, WITH = 77, DISTINCT = 78, RETURN = 79, ORDER = 80, BY = 81,
|
||||
L_SKIP = 82, LIMIT = 83, ASCENDING = 84, ASC = 85, DESCENDING = 86,
|
||||
DESC = 87, WHERE = 88, OR = 89, XOR = 90, AND = 91, NOT = 92, IN = 93,
|
||||
STARTS = 94, ENDS = 95, CONTAINS = 96, IS = 97, CYPHERNULL = 98, COUNT = 99,
|
||||
FILTER = 100, EXTRACT = 101, ANY = 102, NONE = 103, SINGLE = 104, TRUE = 105,
|
||||
FALSE = 106, UnescapedSymbolicName = 107, IdentifierStart = 108, IdentifierPart = 109,
|
||||
EscapedSymbolicName = 110, SP = 111, WHITESPACE = 112, Comment = 113,
|
||||
L_0X = 114
|
||||
T__44 = 45, T__45 = 46, T__46 = 47, StringLiteral = 48, EscapedChar = 49,
|
||||
HexInteger = 50, DecimalInteger = 51, OctalInteger = 52, HexLetter = 53,
|
||||
HexDigit = 54, Digit = 55, NonZeroDigit = 56, NonZeroOctDigit = 57,
|
||||
OctDigit = 58, ZeroDigit = 59, ExponentDecimalReal = 60, RegularDecimalReal = 61,
|
||||
UNION = 62, ALL = 63, OPTIONAL = 64, MATCH = 65, UNWIND = 66, AS = 67,
|
||||
MERGE = 68, ON = 69, CREATE = 70, SET = 71, DETACH = 72, DELETE = 73,
|
||||
REMOVE = 74, WITH = 75, DISTINCT = 76, RETURN = 77, ORDER = 78, BY = 79,
|
||||
L_SKIP = 80, LIMIT = 81, ASCENDING = 82, ASC = 83, DESCENDING = 84,
|
||||
DESC = 85, WHERE = 86, OR = 87, XOR = 88, AND = 89, NOT = 90, IN = 91,
|
||||
STARTS = 92, ENDS = 93, CONTAINS = 94, IS = 95, CYPHERNULL = 96, COUNT = 97,
|
||||
FILTER = 98, EXTRACT = 99, ANY = 100, NONE = 101, SINGLE = 102, TRUE = 103,
|
||||
FALSE = 104, UnescapedSymbolicName = 105, IdentifierStart = 106, IdentifierPart = 107,
|
||||
EscapedSymbolicName = 108, SP = 109, WHITESPACE = 110, Comment = 111
|
||||
};
|
||||
|
||||
CypherLexer(antlr4::CharStream *input);
|
||||
|
@ -45,73 +45,70 @@ T__43=44
|
||||
T__44=45
|
||||
T__45=46
|
||||
T__46=47
|
||||
T__47=48
|
||||
T__48=49
|
||||
StringLiteral=50
|
||||
EscapedChar=51
|
||||
HexInteger=52
|
||||
DecimalInteger=53
|
||||
OctalInteger=54
|
||||
HexLetter=55
|
||||
HexDigit=56
|
||||
Digit=57
|
||||
NonZeroDigit=58
|
||||
NonZeroOctDigit=59
|
||||
OctDigit=60
|
||||
ZeroDigit=61
|
||||
ExponentDecimalReal=62
|
||||
RegularDecimalReal=63
|
||||
UNION=64
|
||||
ALL=65
|
||||
OPTIONAL=66
|
||||
MATCH=67
|
||||
UNWIND=68
|
||||
AS=69
|
||||
MERGE=70
|
||||
ON=71
|
||||
CREATE=72
|
||||
SET=73
|
||||
DETACH=74
|
||||
DELETE=75
|
||||
REMOVE=76
|
||||
WITH=77
|
||||
DISTINCT=78
|
||||
RETURN=79
|
||||
ORDER=80
|
||||
BY=81
|
||||
L_SKIP=82
|
||||
LIMIT=83
|
||||
ASCENDING=84
|
||||
ASC=85
|
||||
DESCENDING=86
|
||||
DESC=87
|
||||
WHERE=88
|
||||
OR=89
|
||||
XOR=90
|
||||
AND=91
|
||||
NOT=92
|
||||
IN=93
|
||||
STARTS=94
|
||||
ENDS=95
|
||||
CONTAINS=96
|
||||
IS=97
|
||||
CYPHERNULL=98
|
||||
COUNT=99
|
||||
FILTER=100
|
||||
EXTRACT=101
|
||||
ANY=102
|
||||
NONE=103
|
||||
SINGLE=104
|
||||
TRUE=105
|
||||
FALSE=106
|
||||
UnescapedSymbolicName=107
|
||||
IdentifierStart=108
|
||||
IdentifierPart=109
|
||||
EscapedSymbolicName=110
|
||||
SP=111
|
||||
WHITESPACE=112
|
||||
Comment=113
|
||||
L_0X=114
|
||||
StringLiteral=48
|
||||
EscapedChar=49
|
||||
HexInteger=50
|
||||
DecimalInteger=51
|
||||
OctalInteger=52
|
||||
HexLetter=53
|
||||
HexDigit=54
|
||||
Digit=55
|
||||
NonZeroDigit=56
|
||||
NonZeroOctDigit=57
|
||||
OctDigit=58
|
||||
ZeroDigit=59
|
||||
ExponentDecimalReal=60
|
||||
RegularDecimalReal=61
|
||||
UNION=62
|
||||
ALL=63
|
||||
OPTIONAL=64
|
||||
MATCH=65
|
||||
UNWIND=66
|
||||
AS=67
|
||||
MERGE=68
|
||||
ON=69
|
||||
CREATE=70
|
||||
SET=71
|
||||
DETACH=72
|
||||
DELETE=73
|
||||
REMOVE=74
|
||||
WITH=75
|
||||
DISTINCT=76
|
||||
RETURN=77
|
||||
ORDER=78
|
||||
BY=79
|
||||
L_SKIP=80
|
||||
LIMIT=81
|
||||
ASCENDING=82
|
||||
ASC=83
|
||||
DESCENDING=84
|
||||
DESC=85
|
||||
WHERE=86
|
||||
OR=87
|
||||
XOR=88
|
||||
AND=89
|
||||
NOT=90
|
||||
IN=91
|
||||
STARTS=92
|
||||
ENDS=93
|
||||
CONTAINS=94
|
||||
IS=95
|
||||
CYPHERNULL=96
|
||||
COUNT=97
|
||||
FILTER=98
|
||||
EXTRACT=99
|
||||
ANY=100
|
||||
NONE=101
|
||||
SINGLE=102
|
||||
TRUE=103
|
||||
FALSE=104
|
||||
UnescapedSymbolicName=105
|
||||
IdentifierStart=106
|
||||
IdentifierPart=107
|
||||
EscapedSymbolicName=108
|
||||
SP=109
|
||||
WHITESPACE=110
|
||||
Comment=111
|
||||
';'=1
|
||||
','=2
|
||||
'='=3
|
||||
@ -120,45 +117,43 @@ L_0X=114
|
||||
'('=6
|
||||
')'=7
|
||||
'['=8
|
||||
'?'=9
|
||||
']'=10
|
||||
':'=11
|
||||
'|'=12
|
||||
'..'=13
|
||||
'+'=14
|
||||
'-'=15
|
||||
'/'=16
|
||||
'%'=17
|
||||
'^'=18
|
||||
'=~'=19
|
||||
'<>'=20
|
||||
'!='=21
|
||||
'<'=22
|
||||
'>'=23
|
||||
'<='=24
|
||||
'>='=25
|
||||
'.'=26
|
||||
'!'=27
|
||||
'{'=28
|
||||
'}'=29
|
||||
'$'=30
|
||||
'⟨'=31
|
||||
'〈'=32
|
||||
'﹤'=33
|
||||
'<'=34
|
||||
'⟩'=35
|
||||
'〉'=36
|
||||
'﹥'=37
|
||||
'>'=38
|
||||
''=39
|
||||
'‐'=40
|
||||
'‑'=41
|
||||
'‒'=42
|
||||
'–'=43
|
||||
'—'=44
|
||||
'―'=45
|
||||
'−'=46
|
||||
'﹘'=47
|
||||
'﹣'=48
|
||||
'-'=49
|
||||
'0'=61
|
||||
']'=9
|
||||
':'=10
|
||||
'|'=11
|
||||
'..'=12
|
||||
'+'=13
|
||||
'-'=14
|
||||
'/'=15
|
||||
'%'=16
|
||||
'^'=17
|
||||
'=~'=18
|
||||
'<>'=19
|
||||
'!='=20
|
||||
'<'=21
|
||||
'>'=22
|
||||
'<='=23
|
||||
'>='=24
|
||||
'.'=25
|
||||
'{'=26
|
||||
'}'=27
|
||||
'$'=28
|
||||
'⟨'=29
|
||||
'〈'=30
|
||||
'﹤'=31
|
||||
'<'=32
|
||||
'⟩'=33
|
||||
'〉'=34
|
||||
'﹥'=35
|
||||
'>'=36
|
||||
''=37
|
||||
'‐'=38
|
||||
'‑'=39
|
||||
'‒'=40
|
||||
'–'=41
|
||||
'—'=42
|
||||
'―'=43
|
||||
'−'=44
|
||||
'﹘'=45
|
||||
'﹣'=46
|
||||
'-'=47
|
||||
'0'=59
|
||||
|
@ -1,8 +1,9 @@
|
||||
|
||||
// Generated from
|
||||
// /home/buda/Workspace/code/memgraph/memgraph/src/query/frontend/opencypher/grammar/Cypher.g4
|
||||
// by ANTLR 4.6
|
||||
// Generated from /home/mislav/code/memgraph/memgraph/src/query/frontend/opencypher/grammar/Cypher.g4 by ANTLR 4.6
|
||||
|
||||
|
||||
#include "CypherListener.h"
|
||||
|
||||
|
||||
using namespace antlropencypher;
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
|
||||
// Generated from /home/buda/Workspace/code/memgraph/memgraph/src/query/frontend/opencypher/grammar/Cypher.g4 by ANTLR 4.6
|
||||
// Generated from /home/mislav/code/memgraph/memgraph/src/query/frontend/opencypher/grammar/Cypher.g4 by ANTLR 4.6
|
||||
|
||||
#pragma once
|
||||
|
||||
@ -214,6 +214,9 @@ public:
|
||||
virtual void enterListComprehension(CypherParser::ListComprehensionContext *ctx) = 0;
|
||||
virtual void exitListComprehension(CypherParser::ListComprehensionContext *ctx) = 0;
|
||||
|
||||
virtual void enterPatternComprehension(CypherParser::PatternComprehensionContext *ctx) = 0;
|
||||
virtual void exitPatternComprehension(CypherParser::PatternComprehensionContext *ctx) = 0;
|
||||
|
||||
virtual void enterPropertyLookup(CypherParser::PropertyLookupContext *ctx) = 0;
|
||||
virtual void exitPropertyLookup(CypherParser::PropertyLookupContext *ctx) = 0;
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,5 @@
|
||||
|
||||
// Generated from /home/buda/Workspace/code/memgraph/memgraph/src/query/frontend/opencypher/grammar/Cypher.g4 by ANTLR 4.6
|
||||
// Generated from /home/mislav/code/memgraph/memgraph/src/query/frontend/opencypher/grammar/Cypher.g4 by ANTLR 4.6
|
||||
|
||||
#pragma once
|
||||
|
||||
@ -20,20 +20,19 @@ public:
|
||||
T__26 = 27, T__27 = 28, T__28 = 29, T__29 = 30, T__30 = 31, T__31 = 32,
|
||||
T__32 = 33, T__33 = 34, T__34 = 35, T__35 = 36, T__36 = 37, T__37 = 38,
|
||||
T__38 = 39, T__39 = 40, T__40 = 41, T__41 = 42, T__42 = 43, T__43 = 44,
|
||||
T__44 = 45, T__45 = 46, T__46 = 47, T__47 = 48, T__48 = 49, StringLiteral = 50,
|
||||
EscapedChar = 51, HexInteger = 52, DecimalInteger = 53, OctalInteger = 54,
|
||||
HexLetter = 55, HexDigit = 56, Digit = 57, NonZeroDigit = 58, NonZeroOctDigit = 59,
|
||||
OctDigit = 60, ZeroDigit = 61, ExponentDecimalReal = 62, RegularDecimalReal = 63,
|
||||
UNION = 64, ALL = 65, OPTIONAL = 66, MATCH = 67, UNWIND = 68, AS = 69,
|
||||
MERGE = 70, ON = 71, CREATE = 72, SET = 73, DETACH = 74, DELETE = 75,
|
||||
REMOVE = 76, WITH = 77, DISTINCT = 78, RETURN = 79, ORDER = 80, BY = 81,
|
||||
L_SKIP = 82, LIMIT = 83, ASCENDING = 84, ASC = 85, DESCENDING = 86,
|
||||
DESC = 87, WHERE = 88, OR = 89, XOR = 90, AND = 91, NOT = 92, IN = 93,
|
||||
STARTS = 94, ENDS = 95, CONTAINS = 96, IS = 97, CYPHERNULL = 98, COUNT = 99,
|
||||
FILTER = 100, EXTRACT = 101, ANY = 102, NONE = 103, SINGLE = 104, TRUE = 105,
|
||||
FALSE = 106, UnescapedSymbolicName = 107, IdentifierStart = 108, IdentifierPart = 109,
|
||||
EscapedSymbolicName = 110, SP = 111, WHITESPACE = 112, Comment = 113,
|
||||
L_0X = 114
|
||||
T__44 = 45, T__45 = 46, T__46 = 47, StringLiteral = 48, EscapedChar = 49,
|
||||
HexInteger = 50, DecimalInteger = 51, OctalInteger = 52, HexLetter = 53,
|
||||
HexDigit = 54, Digit = 55, NonZeroDigit = 56, NonZeroOctDigit = 57,
|
||||
OctDigit = 58, ZeroDigit = 59, ExponentDecimalReal = 60, RegularDecimalReal = 61,
|
||||
UNION = 62, ALL = 63, OPTIONAL = 64, MATCH = 65, UNWIND = 66, AS = 67,
|
||||
MERGE = 68, ON = 69, CREATE = 70, SET = 71, DETACH = 72, DELETE = 73,
|
||||
REMOVE = 74, WITH = 75, DISTINCT = 76, RETURN = 77, ORDER = 78, BY = 79,
|
||||
L_SKIP = 80, LIMIT = 81, ASCENDING = 82, ASC = 83, DESCENDING = 84,
|
||||
DESC = 85, WHERE = 86, OR = 87, XOR = 88, AND = 89, NOT = 90, IN = 91,
|
||||
STARTS = 92, ENDS = 93, CONTAINS = 94, IS = 95, CYPHERNULL = 96, COUNT = 97,
|
||||
FILTER = 98, EXTRACT = 99, ANY = 100, NONE = 101, SINGLE = 102, TRUE = 103,
|
||||
FALSE = 104, UnescapedSymbolicName = 105, IdentifierStart = 106, IdentifierPart = 107,
|
||||
EscapedSymbolicName = 108, SP = 109, WHITESPACE = 110, Comment = 111
|
||||
};
|
||||
|
||||
enum {
|
||||
@ -55,11 +54,11 @@ public:
|
||||
RuleListLiteral = 57, RulePartialComparisonExpression = 58, RuleParenthesizedExpression = 59,
|
||||
RuleRelationshipsPattern = 60, RuleFilterExpression = 61, RuleIdInColl = 62,
|
||||
RuleFunctionInvocation = 63, RuleFunctionName = 64, RuleListComprehension = 65,
|
||||
RulePropertyLookup = 66, RuleVariable = 67, RuleNumberLiteral = 68,
|
||||
RuleMapLiteral = 69, RuleParameter = 70, RulePropertyExpression = 71,
|
||||
RulePropertyKeyName = 72, RuleIntegerLiteral = 73, RuleDoubleLiteral = 74,
|
||||
RuleSymbolicName = 75, RuleLeftArrowHead = 76, RuleRightArrowHead = 77,
|
||||
RuleDash = 78
|
||||
RulePatternComprehension = 66, RulePropertyLookup = 67, RuleVariable = 68,
|
||||
RuleNumberLiteral = 69, RuleMapLiteral = 70, RuleParameter = 71, RulePropertyExpression = 72,
|
||||
RulePropertyKeyName = 73, RuleIntegerLiteral = 74, RuleDoubleLiteral = 75,
|
||||
RuleSymbolicName = 76, RuleLeftArrowHead = 77, RuleRightArrowHead = 78,
|
||||
RuleDash = 79
|
||||
};
|
||||
|
||||
CypherParser(antlr4::TokenStream *input);
|
||||
@ -138,6 +137,7 @@ public:
|
||||
class FunctionInvocationContext;
|
||||
class FunctionNameContext;
|
||||
class ListComprehensionContext;
|
||||
class PatternComprehensionContext;
|
||||
class PropertyLookupContext;
|
||||
class VariableContext;
|
||||
class NumberLiteralContext;
|
||||
@ -382,6 +382,7 @@ public:
|
||||
antlr4::tree::TerminalNode *SET();
|
||||
std::vector<SetItemContext *> setItem();
|
||||
SetItemContext* setItem(size_t i);
|
||||
antlr4::tree::TerminalNode *SP();
|
||||
|
||||
virtual void enterRule(antlr4::tree::ParseTreeListener *listener) override;
|
||||
virtual void exitRule(antlr4::tree::ParseTreeListener *listener) override;
|
||||
@ -398,6 +399,8 @@ public:
|
||||
virtual size_t getRuleIndex() const override;
|
||||
PropertyExpressionContext *propertyExpression();
|
||||
ExpressionContext *expression();
|
||||
std::vector<antlr4::tree::TerminalNode *> SP();
|
||||
antlr4::tree::TerminalNode* SP(size_t i);
|
||||
VariableContext *variable();
|
||||
NodeLabelsContext *nodeLabels();
|
||||
|
||||
@ -785,6 +788,8 @@ public:
|
||||
public:
|
||||
RelationshipDetailContext(antlr4::ParserRuleContext *parent, size_t invokingState);
|
||||
virtual size_t getRuleIndex() const override;
|
||||
std::vector<antlr4::tree::TerminalNode *> SP();
|
||||
antlr4::tree::TerminalNode* SP(size_t i);
|
||||
VariableContext *variable();
|
||||
RelationshipTypesContext *relationshipTypes();
|
||||
RangeLiteralContext *rangeLiteral();
|
||||
@ -856,6 +861,7 @@ public:
|
||||
NodeLabelContext(antlr4::ParserRuleContext *parent, size_t invokingState);
|
||||
virtual size_t getRuleIndex() const override;
|
||||
LabelNameContext *labelName();
|
||||
antlr4::tree::TerminalNode *SP();
|
||||
|
||||
virtual void enterRule(antlr4::tree::ParseTreeListener *listener) override;
|
||||
virtual void exitRule(antlr4::tree::ParseTreeListener *listener) override;
|
||||
@ -1143,6 +1149,8 @@ public:
|
||||
PropertyLookupContext* propertyLookup(size_t i);
|
||||
std::vector<NodeLabelsContext *> nodeLabels();
|
||||
NodeLabelsContext* nodeLabels(size_t i);
|
||||
std::vector<antlr4::tree::TerminalNode *> SP();
|
||||
antlr4::tree::TerminalNode* SP(size_t i);
|
||||
|
||||
virtual void enterRule(antlr4::tree::ParseTreeListener *listener) override;
|
||||
virtual void exitRule(antlr4::tree::ParseTreeListener *listener) override;
|
||||
@ -1163,6 +1171,7 @@ public:
|
||||
std::vector<antlr4::tree::TerminalNode *> SP();
|
||||
antlr4::tree::TerminalNode* SP(size_t i);
|
||||
ListComprehensionContext *listComprehension();
|
||||
PatternComprehensionContext *patternComprehension();
|
||||
antlr4::tree::TerminalNode *FILTER();
|
||||
FilterExpressionContext *filterExpression();
|
||||
antlr4::tree::TerminalNode *EXTRACT();
|
||||
@ -1382,13 +1391,33 @@ public:
|
||||
|
||||
ListComprehensionContext* listComprehension();
|
||||
|
||||
class PatternComprehensionContext : public antlr4::ParserRuleContext {
|
||||
public:
|
||||
PatternComprehensionContext(antlr4::ParserRuleContext *parent, size_t invokingState);
|
||||
virtual size_t getRuleIndex() const override;
|
||||
RelationshipsPatternContext *relationshipsPattern();
|
||||
std::vector<ExpressionContext *> expression();
|
||||
ExpressionContext* expression(size_t i);
|
||||
std::vector<antlr4::tree::TerminalNode *> SP();
|
||||
antlr4::tree::TerminalNode* SP(size_t i);
|
||||
VariableContext *variable();
|
||||
antlr4::tree::TerminalNode *WHERE();
|
||||
|
||||
virtual void enterRule(antlr4::tree::ParseTreeListener *listener) override;
|
||||
virtual void exitRule(antlr4::tree::ParseTreeListener *listener) override;
|
||||
|
||||
virtual antlrcpp::Any accept(antlr4::tree::ParseTreeVisitor *visitor) override;
|
||||
|
||||
};
|
||||
|
||||
PatternComprehensionContext* patternComprehension();
|
||||
|
||||
class PropertyLookupContext : public antlr4::ParserRuleContext {
|
||||
public:
|
||||
PropertyLookupContext(antlr4::ParserRuleContext *parent, size_t invokingState);
|
||||
virtual size_t getRuleIndex() const override;
|
||||
PropertyKeyNameContext *propertyKeyName();
|
||||
std::vector<antlr4::tree::TerminalNode *> SP();
|
||||
antlr4::tree::TerminalNode* SP(size_t i);
|
||||
antlr4::tree::TerminalNode *SP();
|
||||
|
||||
virtual void enterRule(antlr4::tree::ParseTreeListener *listener) override;
|
||||
virtual void exitRule(antlr4::tree::ParseTreeListener *listener) override;
|
||||
|
@ -1,8 +1,9 @@
|
||||
|
||||
// Generated from
|
||||
// /home/buda/Workspace/code/memgraph/memgraph/src/query/frontend/opencypher/grammar/Cypher.g4
|
||||
// by ANTLR 4.6
|
||||
// Generated from /home/mislav/code/memgraph/memgraph/src/query/frontend/opencypher/grammar/Cypher.g4 by ANTLR 4.6
|
||||
|
||||
|
||||
#include "CypherVisitor.h"
|
||||
|
||||
|
||||
using namespace antlropencypher;
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
|
||||
// Generated from /home/buda/Workspace/code/memgraph/memgraph/src/query/frontend/opencypher/grammar/Cypher.g4 by ANTLR 4.6
|
||||
// Generated from /home/mislav/code/memgraph/memgraph/src/query/frontend/opencypher/grammar/Cypher.g4 by ANTLR 4.6
|
||||
|
||||
#pragma once
|
||||
|
||||
@ -152,6 +152,8 @@ public:
|
||||
|
||||
virtual antlrcpp::Any visitListComprehension(CypherParser::ListComprehensionContext *context) = 0;
|
||||
|
||||
virtual antlrcpp::Any visitPatternComprehension(CypherParser::PatternComprehensionContext *context) = 0;
|
||||
|
||||
virtual antlrcpp::Any visitPropertyLookup(CypherParser::PropertyLookupContext *context) = 0;
|
||||
|
||||
virtual antlrcpp::Any visitVariable(CypherParser::VariableContext *context) = 0;
|
||||
|
@ -53,12 +53,12 @@ mergeAction : ( ON SP MATCH SP set )
|
||||
|
||||
create : CREATE SP? pattern ;
|
||||
|
||||
set : SET setItem ( ',' setItem )* ;
|
||||
set : SET SP? setItem ( ',' setItem )* ;
|
||||
|
||||
setItem : ( propertyExpression '=' expression )
|
||||
| ( variable '=' expression )
|
||||
| ( variable '+=' expression )
|
||||
| ( variable nodeLabels )
|
||||
setItem : ( propertyExpression SP? '=' SP? expression )
|
||||
| ( variable SP? '=' SP? expression )
|
||||
| ( variable SP? '+=' SP? expression )
|
||||
| ( variable SP? nodeLabels )
|
||||
;
|
||||
|
||||
cypherDelete : ( DETACH SP )? DELETE SP? expression ( SP? ',' SP? expression )* ;
|
||||
@ -115,17 +115,17 @@ relationshipPattern : ( leftArrowHead SP? dash SP? relationshipDetail? SP? dash
|
||||
| ( dash SP? relationshipDetail? SP? dash )
|
||||
;
|
||||
|
||||
relationshipDetail : '[' variable? '?'? relationshipTypes? rangeLiteral? properties? ']' ;
|
||||
relationshipDetail : '[' SP? ( variable SP? )? ( relationshipTypes SP? )? rangeLiteral? ( properties SP? )? ']' ;
|
||||
|
||||
properties : mapLiteral
|
||||
| parameter
|
||||
;
|
||||
|
||||
relationshipTypes : ':' relTypeName ( SP? '|' ':'? SP? relTypeName )* ;
|
||||
relationshipTypes : ':' SP? relTypeName ( SP? '|' ':'? SP? relTypeName )* ;
|
||||
|
||||
nodeLabels : nodeLabel ( SP? nodeLabel )* ;
|
||||
|
||||
nodeLabel : ':' labelName ;
|
||||
nodeLabel : ':' SP? labelName ;
|
||||
|
||||
rangeLiteral : '*' SP? ( integerLiteral SP? )? ( '..' SP? ( integerLiteral SP? )? )? ;
|
||||
|
||||
@ -155,12 +155,13 @@ expression4 : ( ( '+' | '-' ) SP? )* expression3 ;
|
||||
|
||||
expression3 : expression2 ( ( SP? '[' expression ']' ) | ( SP? '[' expression? '..' expression? ']' ) | ( ( ( SP? '=~' ) | ( SP IN ) | ( SP STARTS SP WITH ) | ( SP ENDS SP WITH ) | ( SP CONTAINS ) ) SP? expression2 ) | ( SP IS SP CYPHERNULL ) | ( SP IS SP NOT SP CYPHERNULL ) )* ;
|
||||
|
||||
expression2 : atom ( propertyLookup | nodeLabels )* ;
|
||||
expression2 : atom ( SP? ( propertyLookup | nodeLabels ) )* ;
|
||||
|
||||
atom : literal
|
||||
| parameter
|
||||
| ( COUNT SP? '(' SP? '*' SP? ')' )
|
||||
| listComprehension
|
||||
| patternComprehension
|
||||
| ( FILTER SP? '(' SP? filterExpression SP? ')' )
|
||||
| ( EXTRACT SP? '(' SP? filterExpression SP? ( SP? '|' expression )? ')' )
|
||||
| ( ALL SP? '(' SP? filterExpression SP? ')' )
|
||||
@ -212,7 +213,9 @@ functionName : UnescapedSymbolicName
|
||||
|
||||
listComprehension : '[' SP? filterExpression ( SP? '|' SP? expression )? SP? ']' ;
|
||||
|
||||
propertyLookup : SP? '.' SP? ( ( propertyKeyName ( '?' | '!' ) ) | propertyKeyName ) ;
|
||||
patternComprehension : '[' SP? ( variable SP? '=' SP? )? relationshipsPattern SP? ( WHERE SP? expression SP? )? '|' SP? expression SP? ']' ;
|
||||
|
||||
propertyLookup : '.' SP? ( propertyKeyName ) ;
|
||||
|
||||
variable : symbolicName ;
|
||||
|
||||
@ -220,7 +223,7 @@ StringLiteral : ( '"' ( StringLiteral_0 | EscapedChar )* '"' )
|
||||
| ( '\'' ( StringLiteral_1 | EscapedChar )* '\'' )
|
||||
;
|
||||
|
||||
EscapedChar : '\\' ( '\\' | '\'' | '"' | ( 'B' | 'b' ) | ( 'F' | 'f' ) | ( 'N' | 'n' ) | ( 'R' | 'r' ) | ( 'T' | 't' ) | '_' | '%' | ( ( 'U' | 'u' ) ( HexDigit HexDigit HexDigit HexDigit ) ) | ( ( 'U' | 'u' ) ( HexDigit HexDigit HexDigit HexDigit HexDigit HexDigit HexDigit HexDigit ) ) ) ;
|
||||
EscapedChar : '\\' ( '\\' | '\'' | '"' | ( 'B' | 'b' ) | ( 'F' | 'f' ) | ( 'N' | 'n' ) | ( 'R' | 'r' ) | ( 'T' | 't' ) | ( ( 'U' | 'u' ) ( HexDigit HexDigit HexDigit HexDigit ) ) | ( ( 'U' | 'u' ) ( HexDigit HexDigit HexDigit HexDigit HexDigit HexDigit HexDigit HexDigit ) ) ) ;
|
||||
|
||||
numberLiteral : doubleLiteral
|
||||
| integerLiteral
|
||||
@ -239,7 +242,7 @@ integerLiteral : HexInteger
|
||||
| DecimalInteger
|
||||
;
|
||||
|
||||
HexInteger : L_0X ( HexDigit )+ ;
|
||||
HexInteger : '0x' ( HexDigit )+ ;
|
||||
|
||||
DecimalInteger : ZeroDigit
|
||||
| ( NonZeroDigit ( Digit )* )
|
||||
@ -493,7 +496,7 @@ WHITESPACE : SPACE
|
||||
;
|
||||
|
||||
Comment : ( '/*' ( Comment_1 | ( '*' Comment_2 ) )* '*/' )
|
||||
| ( '//' Comment_3 CR? ( LF | EOF ) )
|
||||
| ( '//' ( Comment_3 )* CR? ( LF | EOF ) )
|
||||
;
|
||||
|
||||
leftArrowHead : '<'
|
||||
@ -524,8 +527,6 @@ dash : '-'
|
||||
| '-'
|
||||
;
|
||||
|
||||
L_0X : ( '0' | '0' ) ( 'X' | 'x' ) ;
|
||||
|
||||
fragment FF : [\f] ;
|
||||
|
||||
fragment EscapedSymbolicName_0 : [\u0000-_a-\uFFFF] ;
|
||||
|
@ -10,37 +10,42 @@
|
||||
// Value extraction template instantiations
|
||||
template <>
|
||||
bool PropertyValue::Value<bool>() const {
|
||||
debug_assert(type_ == PropertyValue::Type::Bool,
|
||||
"Incompatible template param and type");
|
||||
if (type_ != PropertyValue::Type::Bool) {
|
||||
throw PropertyValueException("Incompatible template param and type");
|
||||
}
|
||||
return bool_v;
|
||||
}
|
||||
|
||||
template <>
|
||||
std::string PropertyValue::Value<std::string>() const {
|
||||
debug_assert(type_ == PropertyValue::Type::String,
|
||||
"Incompatible template param and type");
|
||||
if (type_ != PropertyValue::Type::String) {
|
||||
throw PropertyValueException("Incompatible template param and type");
|
||||
}
|
||||
return *string_v;
|
||||
}
|
||||
|
||||
template <>
|
||||
int PropertyValue::Value<int>() const {
|
||||
debug_assert(type_ == PropertyValue::Type::Int,
|
||||
"Incompatible template param and type");
|
||||
if (type_ != PropertyValue::Type::Int) {
|
||||
throw PropertyValueException("Incompatible template param and type");
|
||||
}
|
||||
return int_v;
|
||||
}
|
||||
|
||||
template <>
|
||||
double PropertyValue::Value<double>() const {
|
||||
debug_assert(type_ == PropertyValue::Type::Double,
|
||||
"Incompatible template param and type");
|
||||
if (type_ != PropertyValue::Type::Double) {
|
||||
throw PropertyValueException("Incompatible template param and type");
|
||||
}
|
||||
return double_v;
|
||||
}
|
||||
|
||||
template <>
|
||||
std::vector<PropertyValue> PropertyValue::Value<std::vector<PropertyValue>>()
|
||||
const {
|
||||
debug_assert(type_ == PropertyValue::Type::List,
|
||||
"Incompatible template param and type");
|
||||
if (type_ != PropertyValue::Type::List) {
|
||||
throw PropertyValueException("Incompatible template param and type");
|
||||
}
|
||||
return *list_v;
|
||||
}
|
||||
|
||||
|
62
tests/manual/antlr_tree_pretty_print.cpp
Normal file
62
tests/manual/antlr_tree_pretty_print.cpp
Normal file
@ -0,0 +1,62 @@
|
||||
#include <iostream>
|
||||
#include <istream>
|
||||
#include <iterator>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
|
||||
#include "antlr4-runtime.h"
|
||||
#include "query/frontend/opencypher/generated/CypherLexer.h"
|
||||
#include "query/frontend/opencypher/generated/CypherParser.h"
|
||||
|
||||
using namespace antlropencypher;
|
||||
using namespace antlr4;
|
||||
|
||||
std::string ReadAllInput() {
|
||||
// don't skip the whitespace while reading
|
||||
std::cin >> std::noskipws;
|
||||
|
||||
// use stream iterators to copy the stream to a string
|
||||
std::istream_iterator<char> it(std::cin);
|
||||
std::istream_iterator<char> end;
|
||||
std::string results(it, end);
|
||||
return results;
|
||||
}
|
||||
|
||||
int main(int, const char**) {
|
||||
std::string input_string = ReadAllInput();
|
||||
ANTLRInputStream input(input_string);
|
||||
CypherLexer lexer(&input);
|
||||
CommonTokenStream tokens(&lexer);
|
||||
|
||||
tokens.fill();
|
||||
for (auto token : tokens.getTokens()) {
|
||||
std::cout << token->toString() << std::endl;
|
||||
}
|
||||
|
||||
CypherParser parser(&tokens);
|
||||
tree::ParseTree* tree = parser.cypher();
|
||||
|
||||
// Print tree indented. This is a hacky implementation and not very correct.
|
||||
std::string indent;
|
||||
std::string string_tree = tree->toStringTree(&parser);
|
||||
for (int i = 0; i < (int)string_tree.size(); ++i) {
|
||||
char c = string_tree[i];
|
||||
char next_c = i + 1 != (int)string_tree.size() ? string_tree[i + 1] : '\0';
|
||||
char prev_c = i - 1 != (int)string_tree.size() ? string_tree[i - 1] : '\0';
|
||||
if (c == '(' && next_c != ' ') {
|
||||
indent.push_back(' ');
|
||||
std::cout << "(";
|
||||
} else if (c == ')' && prev_c != ' ') {
|
||||
indent.pop_back();
|
||||
std::cout << ")";
|
||||
} else {
|
||||
if (c == ' ' && prev_c != ' ') {
|
||||
std::cout << "\n" << indent;
|
||||
} else if (c != ' ') {
|
||||
std::cout << c;
|
||||
}
|
||||
}
|
||||
}
|
||||
std::cout << std::endl;
|
||||
return 0;
|
||||
}
|
@ -13,6 +13,8 @@ using namespace ::testing;
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace backend::cpp;
|
||||
|
||||
class ParserTables {
|
||||
template <typename T>
|
||||
auto FilterAnies(std::unordered_map<std::string, antlrcpp::Any> map) {
|
||||
@ -31,7 +33,7 @@ class ParserTables {
|
||||
auto *tree = parser.tree();
|
||||
CypherMainVisitor visitor;
|
||||
visitor.visit(tree);
|
||||
identifiers_map_ = visitor.identifiers_map();
|
||||
identifiers_map_ = visitor.ids_map().back();
|
||||
symbol_table_ = visitor.symbol_table();
|
||||
pattern_parts_ = FilterAnies<PatternPart>(symbol_table_);
|
||||
nodes_ = FilterAnies<Node>(symbol_table_);
|
||||
@ -51,7 +53,7 @@ void CompareNodes(std::pair<std::string, Node> node_entry,
|
||||
std::vector<std::string> labels,
|
||||
std::vector<std::string> property_keys) {
|
||||
auto node = node_entry.second;
|
||||
ASSERT_EQ(node_entry.first, node.output_identifier);
|
||||
ASSERT_EQ(node_entry.first, node.output_id);
|
||||
ASSERT_THAT(node.labels,
|
||||
UnorderedElementsAreArray(labels.begin(), labels.end()));
|
||||
std::vector<std::string> node_property_keys;
|
||||
@ -72,7 +74,7 @@ void CompareRelationships(
|
||||
std::vector<std::string> property_keys, bool has_range,
|
||||
int64_t lower_bound = 1LL, int64_t upper_bound = LLONG_MAX) {
|
||||
auto relationship = relationship_entry.second;
|
||||
ASSERT_EQ(relationship_entry.first, relationship.output_identifier);
|
||||
ASSERT_EQ(relationship_entry.first, relationship.output_id);
|
||||
ASSERT_EQ(relationship.direction, direction);
|
||||
ASSERT_THAT(relationship.types,
|
||||
UnorderedElementsAreArray(types.begin(), types.end()));
|
||||
@ -299,6 +301,55 @@ TEST(CompilerStructuresTest, PatternPartVariable) {
|
||||
ASSERT_NE(parser.pattern_parts_.find(output_identifier),
|
||||
parser.pattern_parts_.end());
|
||||
}
|
||||
|
||||
// Multiple nodes with same variable and properties.
|
||||
TEST(CompilerStructuresTest, MultipleNodesWithVariableAndProperties) {
|
||||
ASSERT_THROW(ParserTables parser("CREATE (a {b: 5})-[]-(a {c: 5})"),
|
||||
SemanticException);
|
||||
}
|
||||
|
||||
// Multiple nodes with same variable name.
|
||||
TEST(CompilerStructuresTest, MultipleNodesWithVariable) {
|
||||
ParserTables parser("CREATE (a {b: 5, c: 5})-[]-(a)");
|
||||
ASSERT_EQ(parser.identifiers_map_.size(), 1U);
|
||||
ASSERT_EQ(parser.pattern_parts_.size(), 1U);
|
||||
ASSERT_EQ(parser.relationships_.size(), 1U);
|
||||
ASSERT_EQ(parser.nodes_.size(), 1U);
|
||||
auto pattern_part = parser.pattern_parts_.begin()->second;
|
||||
ASSERT_EQ(pattern_part.nodes.size(), 2U);
|
||||
ASSERT_EQ(pattern_part.relationships.size(), 1U);
|
||||
ASSERT_EQ(pattern_part.nodes[0], pattern_part.nodes[1]);
|
||||
}
|
||||
|
||||
// Multiple relationships with same variable name and properties.
|
||||
TEST(CompilerStructuresTest, MultipleRelationshipsWithVariableAndProperties) {
|
||||
ASSERT_THROW(ParserTables parser("CREATE ()-[e {a: 5}]-()-[e {c: 5}]-()"),
|
||||
SemanticException);
|
||||
}
|
||||
|
||||
// Multiple relationships with same variable name.
|
||||
TEST(CompilerStructuresTest, MultipleRelationshipsWithVariable) {
|
||||
ParserTables parser("CREATE ()-[a {a: 5}]-()-[a]-()");
|
||||
ASSERT_EQ(parser.identifiers_map_.size(), 1U);
|
||||
ASSERT_EQ(parser.pattern_parts_.size(), 1U);
|
||||
ASSERT_EQ(parser.relationships_.size(), 1U);
|
||||
ASSERT_EQ(parser.nodes_.size(), 3U);
|
||||
auto pattern_part = parser.pattern_parts_.begin()->second;
|
||||
ASSERT_EQ(pattern_part.nodes.size(), 3U);
|
||||
ASSERT_EQ(pattern_part.relationships.size(), 2U);
|
||||
ASSERT_NE(pattern_part.nodes[0], pattern_part.nodes[1]);
|
||||
ASSERT_NE(pattern_part.nodes[1], pattern_part.nodes[2]);
|
||||
ASSERT_NE(pattern_part.nodes[0], pattern_part.nodes[2]);
|
||||
ASSERT_EQ(pattern_part.relationships[0], pattern_part.relationships[1]);
|
||||
}
|
||||
|
||||
// Different structures (nodes, realtionships, patterns) with same variable
|
||||
// name.
|
||||
TEST(CompilerStructuresTest, DifferentTypesWithVariable) {
|
||||
ASSERT_THROW(ParserTables parser("CREATE a=(a)"), SemanticException);
|
||||
ASSERT_THROW(ParserTables parser("CREATE (a)-[a]-()"), SemanticException);
|
||||
ASSERT_THROW(ParserTables parser("CREATE a=()-[a]-()"), SemanticException);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
|
@ -210,7 +210,6 @@ TEST(TypedValue, Sum) {
|
||||
std::vector<TypedValue> out3 = {1, 2, true, "a", 1, 2, true, "a"};
|
||||
EXPECT_PROP_EQ(
|
||||
(TypedValue(2) + TypedValue(in)).Value<std::vector<TypedValue>>(), out1);
|
||||
std::cerr << (TypedValue(2) + TypedValue(in)) << "\n";
|
||||
EXPECT_PROP_EQ(
|
||||
(TypedValue(in) + TypedValue(2)).Value<std::vector<TypedValue>>(), out2);
|
||||
EXPECT_PROP_EQ(
|
||||
@ -322,3 +321,13 @@ TEST(TypedValue, LogicalOr) {
|
||||
EXPECT_PROP_EQ(TypedValue(true) || TypedValue(true), TypedValue(true));
|
||||
EXPECT_PROP_EQ(TypedValue(false) || TypedValue(true), TypedValue(true));
|
||||
}
|
||||
|
||||
TEST(TypedValue, LogicalXor) {
|
||||
TestLogicalThrows(
|
||||
[](const TypedValue& p1, const TypedValue& p2) { return p1 ^ p2; });
|
||||
EXPECT_PROP_ISNULL(TypedValue::Null && TypedValue(true));
|
||||
EXPECT_PROP_EQ(TypedValue(true) ^ TypedValue(true), TypedValue(false));
|
||||
EXPECT_PROP_EQ(TypedValue(false) ^ TypedValue(true), TypedValue(true));
|
||||
EXPECT_PROP_EQ(TypedValue(true) ^ TypedValue(false), TypedValue(true));
|
||||
EXPECT_PROP_EQ(TypedValue(false) ^ TypedValue(false), TypedValue(false));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user