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:
Mislav Bradac 2017-03-08 14:09:25 +01:00
parent 47ed127086
commit 00d818c762
29 changed files with 8604 additions and 8336 deletions

View 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.
}

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

View File

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

View File

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

View File

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

View 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; // -

View 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.");
}
}
}
}

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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