Split symbol_generator.hpp to .cpp
Reviewers: florijan, mislav.bradac Reviewed By: florijan Subscribers: pullbot Differential Revision: https://phabricator.memgraph.io/D172
This commit is contained in:
parent
11eb643a5e
commit
c2c34336c3
@ -357,6 +357,7 @@ set(memgraph_src_files
|
|||||||
${src_dir}/query/frontend/ast/cypher_main_visitor.cpp
|
${src_dir}/query/frontend/ast/cypher_main_visitor.cpp
|
||||||
${src_dir}/query/backend/cpp/typed_value.cpp
|
${src_dir}/query/backend/cpp/typed_value.cpp
|
||||||
${src_dir}/query/frontend/logical/planner.cpp
|
${src_dir}/query/frontend/logical/planner.cpp
|
||||||
|
${src_dir}/query/frontend/semantic/symbol_generator.cpp
|
||||||
)
|
)
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
135
src/query/frontend/semantic/symbol_generator.cpp
Normal file
135
src/query/frontend/semantic/symbol_generator.cpp
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
// Copyright 2017 Memgraph
|
||||||
|
//
|
||||||
|
// Created by Teon Banek on 24-03-2017
|
||||||
|
|
||||||
|
#include "query/frontend/semantic/symbol_generator.hpp"
|
||||||
|
|
||||||
|
namespace query {
|
||||||
|
|
||||||
|
auto SymbolGenerator::CreateVariable(const std::string &name,
|
||||||
|
SymbolGenerator::Variable::Type type) {
|
||||||
|
auto symbol = symbol_table_.CreateSymbol(name);
|
||||||
|
auto variable = SymbolGenerator::Variable{symbol, type};
|
||||||
|
scope_.variables[name] = variable;
|
||||||
|
return variable;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto SymbolGenerator::GetOrCreateVariable(
|
||||||
|
const std::string &name, SymbolGenerator::Variable::Type type) {
|
||||||
|
auto search = scope_.variables.find(name);
|
||||||
|
if (search != scope_.variables.end()) {
|
||||||
|
auto variable = search->second;
|
||||||
|
if (type != SymbolGenerator::Variable::Type::Any && type != variable.type) {
|
||||||
|
throw TypeMismatchError(name, TypeToString(variable.type),
|
||||||
|
TypeToString(type));
|
||||||
|
}
|
||||||
|
return search->second;
|
||||||
|
}
|
||||||
|
return CreateVariable(name, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clauses
|
||||||
|
|
||||||
|
void SymbolGenerator::Visit(Create &create) { scope_.in_create = true; }
|
||||||
|
void SymbolGenerator::PostVisit(Create &create) { scope_.in_create = false; }
|
||||||
|
|
||||||
|
void SymbolGenerator::PostVisit(Return &ret) {
|
||||||
|
for (auto &named_expr : ret.named_expressions_) {
|
||||||
|
// Named expressions establish bindings for expressions which come after
|
||||||
|
// return, but not for the expressions contained inside.
|
||||||
|
symbol_table_[*named_expr] = CreateVariable(named_expr->name_).symbol;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expressions
|
||||||
|
|
||||||
|
void SymbolGenerator::Visit(Identifier &ident) {
|
||||||
|
Symbol symbol;
|
||||||
|
if (scope_.in_pattern) {
|
||||||
|
// Patterns can bind new symbols or reference already bound. But there
|
||||||
|
// are the following special cases:
|
||||||
|
// 1) Expressions in property maps `{prop_name: expr}` can only reference
|
||||||
|
// bound variables.
|
||||||
|
// 2) Patterns used to create nodes and edges cannot redeclare already
|
||||||
|
// established bindings. Declaration only happens in single node
|
||||||
|
// patterns and in edge patterns. OpenCypher example,
|
||||||
|
// `MATCH (n) CREATE (n)` should throw an error that `n` is already
|
||||||
|
// declared. While `MATCH (n) CREATE (n) -[:R]-> (n)` is allowed,
|
||||||
|
// since `n` now references the bound node instead of declaring it.
|
||||||
|
// Additionally, we will support edge referencing in pattern:
|
||||||
|
// `MATCH (n) - [r] -> (n) - [r] -> (n) RETURN r`, which would
|
||||||
|
// usually raise redeclaration of `r`.
|
||||||
|
if (scope_.in_property_map && !HasVariable(ident.name_)) {
|
||||||
|
// Case 1)
|
||||||
|
throw UnboundVariableError(ident.name_);
|
||||||
|
} else if ((scope_.in_create_node || scope_.in_create_edge) &&
|
||||||
|
HasVariable(ident.name_)) {
|
||||||
|
// Case 2)
|
||||||
|
throw RedeclareVariableError(ident.name_);
|
||||||
|
}
|
||||||
|
auto type = Variable::Type::Vertex;
|
||||||
|
if (scope_.in_edge_atom) {
|
||||||
|
type = Variable::Type::Edge;
|
||||||
|
}
|
||||||
|
symbol = GetOrCreateVariable(ident.name_, type).symbol;
|
||||||
|
} else {
|
||||||
|
// Everything else references a bound symbol.
|
||||||
|
if (!HasVariable(ident.name_)) throw UnboundVariableError(ident.name_);
|
||||||
|
symbol = scope_.variables[ident.name_].symbol;
|
||||||
|
}
|
||||||
|
symbol_table_[ident] = symbol;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pattern and its subparts.
|
||||||
|
|
||||||
|
void SymbolGenerator::Visit(Pattern &pattern) {
|
||||||
|
scope_.in_pattern = true;
|
||||||
|
if (scope_.in_create && pattern.atoms_.size() == 1) {
|
||||||
|
debug_assert(dynamic_cast<NodeAtom *>(pattern.atoms_[0]),
|
||||||
|
"Expected a single NodeAtom in Pattern");
|
||||||
|
scope_.in_create_node = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SymbolGenerator::PostVisit(Pattern &pattern) { scope_.in_pattern = false; }
|
||||||
|
|
||||||
|
void SymbolGenerator::Visit(NodeAtom &node_atom) {
|
||||||
|
scope_.in_node_atom = true;
|
||||||
|
scope_.in_property_map = true;
|
||||||
|
for (auto kv : node_atom.properties_) {
|
||||||
|
kv.second->Accept(*this);
|
||||||
|
}
|
||||||
|
scope_.in_property_map = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SymbolGenerator::PostVisit(NodeAtom &node_atom) {
|
||||||
|
scope_.in_node_atom = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SymbolGenerator::Visit(EdgeAtom &edge_atom) {
|
||||||
|
scope_.in_edge_atom = true;
|
||||||
|
if (scope_.in_create) {
|
||||||
|
scope_.in_create_edge = true;
|
||||||
|
if (edge_atom.edge_types_.size() != 1) {
|
||||||
|
throw SemanticException(
|
||||||
|
"A single relationship type must be specified "
|
||||||
|
"when creating an edge.");
|
||||||
|
}
|
||||||
|
if (edge_atom.direction_ == EdgeAtom::Direction::BOTH) {
|
||||||
|
throw SemanticException(
|
||||||
|
"Bidirectional relationship are not supported "
|
||||||
|
"when creating an edge");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SymbolGenerator::PostVisit(EdgeAtom &edge_atom) {
|
||||||
|
scope_.in_edge_atom = false;
|
||||||
|
scope_.in_create_edge = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SymbolGenerator::HasVariable(const std::string &name) {
|
||||||
|
return scope_.variables.find(name) != scope_.variables.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace query
|
@ -1,3 +1,7 @@
|
|||||||
|
// Copyright 2017 Memgraph
|
||||||
|
//
|
||||||
|
// Created by Teon Banek on 11-03-2017
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "query/exceptions.hpp"
|
#include "query/exceptions.hpp"
|
||||||
@ -6,6 +10,12 @@
|
|||||||
|
|
||||||
namespace query {
|
namespace query {
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Visits the AST and generates symbols for variables.
|
||||||
|
///
|
||||||
|
/// During the process of symbol generation, simple semantic checks are
|
||||||
|
/// performed. Such as, redeclaring a variable or conflicting expectations of
|
||||||
|
/// variable types.
|
||||||
class SymbolGenerator : public TreeVisitorBase {
|
class SymbolGenerator : public TreeVisitorBase {
|
||||||
public:
|
public:
|
||||||
SymbolGenerator(SymbolTable &symbol_table) : symbol_table_(symbol_table) {}
|
SymbolGenerator(SymbolTable &symbol_table) : symbol_table_(symbol_table) {}
|
||||||
@ -14,103 +24,20 @@ class SymbolGenerator : public TreeVisitorBase {
|
|||||||
using TreeVisitorBase::PostVisit;
|
using TreeVisitorBase::PostVisit;
|
||||||
|
|
||||||
// Clauses
|
// Clauses
|
||||||
void Visit(Create &create) override {
|
void Visit(Create &create) override;
|
||||||
scope_.in_create = true;
|
void PostVisit(Create &create) override;
|
||||||
}
|
void PostVisit(Return &ret) override;
|
||||||
void PostVisit(Create &create) override {
|
|
||||||
scope_.in_create = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PostVisit(Return &ret) override {
|
|
||||||
for (auto &named_expr : ret.named_expressions_) {
|
|
||||||
// Named expressions establish bindings for expressions which come after
|
|
||||||
// return, but not for the expressions contained inside.
|
|
||||||
symbol_table_[*named_expr] = CreateVariable(named_expr->name_).symbol;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Expressions
|
// Expressions
|
||||||
void Visit(Identifier &ident) override {
|
void Visit(Identifier &ident) override;
|
||||||
Symbol symbol;
|
|
||||||
if (scope_.in_pattern) {
|
|
||||||
// Patterns can bind new symbols or reference already bound. But there
|
|
||||||
// are the following special cases:
|
|
||||||
// 1) Expressions in property maps `{prop_name: expr}` can only reference
|
|
||||||
// bound variables.
|
|
||||||
// 2) Patterns used to create nodes and edges cannot redeclare already
|
|
||||||
// established bindings. Declaration only happens in single node
|
|
||||||
// patterns and in edge patterns. OpenCypher example,
|
|
||||||
// `MATCH (n) CREATE (n)` should throw an error that `n` is already
|
|
||||||
// declared. While `MATCH (n) CREATE (n) -[:R]-> (n)` is allowed,
|
|
||||||
// since `n` now references the bound node instead of declaring it.
|
|
||||||
// Additionally, we will support edge referencing in pattern:
|
|
||||||
// `MATCH (n) - [r] -> (n) - [r] -> (n) RETURN r`, which would
|
|
||||||
// usually raise redeclaration of `r`.
|
|
||||||
if (scope_.in_property_map && !HasVariable(ident.name_)) {
|
|
||||||
// Case 1)
|
|
||||||
throw UnboundVariableError(ident.name_);
|
|
||||||
} else if ((scope_.in_create_node || scope_.in_create_edge) &&
|
|
||||||
HasVariable(ident.name_)) {
|
|
||||||
// Case 2)
|
|
||||||
throw RedeclareVariableError(ident.name_);
|
|
||||||
}
|
|
||||||
auto type = Variable::Type::Vertex;
|
|
||||||
if (scope_.in_edge_atom) {
|
|
||||||
type = Variable::Type::Edge;
|
|
||||||
}
|
|
||||||
symbol = GetOrCreateVariable(ident.name_, type).symbol;
|
|
||||||
} else {
|
|
||||||
// Everything else references a bound symbol.
|
|
||||||
if (!HasVariable(ident.name_))
|
|
||||||
throw UnboundVariableError(ident.name_);
|
|
||||||
symbol = scope_.variables[ident.name_].symbol;
|
|
||||||
}
|
|
||||||
symbol_table_[ident] = symbol;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pattern and its subparts.
|
// Pattern and its subparts.
|
||||||
void Visit(Pattern &pattern) override {
|
void Visit(Pattern &pattern) override;
|
||||||
scope_.in_pattern = true;
|
void PostVisit(Pattern &pattern) override;
|
||||||
if (scope_.in_create && pattern.atoms_.size() == 1) {
|
void Visit(NodeAtom &node_atom) override;
|
||||||
debug_assert(dynamic_cast<NodeAtom*>(pattern.atoms_[0]),
|
void PostVisit(NodeAtom &node_atom) override;
|
||||||
"Expected a single NodeAtom in Pattern");
|
void Visit(EdgeAtom &edge_atom) override;
|
||||||
scope_.in_create_node = true;
|
void PostVisit(EdgeAtom &edge_atom) override;
|
||||||
}
|
|
||||||
}
|
|
||||||
void PostVisit(Pattern &pattern) override {
|
|
||||||
scope_.in_pattern = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Visit(NodeAtom &node_atom) override {
|
|
||||||
scope_.in_node_atom = true;
|
|
||||||
scope_.in_property_map = true;
|
|
||||||
for (auto kv : node_atom.properties_) {
|
|
||||||
kv.second->Accept(*this);
|
|
||||||
}
|
|
||||||
scope_.in_property_map = false;
|
|
||||||
}
|
|
||||||
void PostVisit(NodeAtom &node_atom) override {
|
|
||||||
scope_.in_node_atom = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Visit(EdgeAtom &edge_atom) override {
|
|
||||||
scope_.in_edge_atom = true;
|
|
||||||
if (scope_.in_create) {
|
|
||||||
scope_.in_create_edge = true;
|
|
||||||
if (edge_atom.edge_types_.size() != 1) {
|
|
||||||
throw SemanticException("A single relationship type must be specified "
|
|
||||||
"when creating an edge.");
|
|
||||||
}
|
|
||||||
if (edge_atom.direction_ == EdgeAtom::Direction::BOTH) {
|
|
||||||
throw SemanticException("Bidirectional relationship are not supported "
|
|
||||||
"when creating an edge");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void PostVisit(EdgeAtom &edge_atom) override {
|
|
||||||
scope_.in_edge_atom = false;
|
|
||||||
scope_.in_create_edge = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// A variable stores the associated symbol and its type.
|
// A variable stores the associated symbol and its type.
|
||||||
@ -143,38 +70,20 @@ class SymbolGenerator : public TreeVisitorBase {
|
|||||||
std::map<std::string, Variable> variables;
|
std::map<std::string, Variable> variables;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool HasVariable(const std::string &name) {
|
bool HasVariable(const std::string &name);
|
||||||
return scope_.variables.find(name) != scope_.variables.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns a new variable with a freshly generated symbol. Previous mapping
|
// Returns a new variable with a freshly generated symbol. Previous mapping
|
||||||
// of the same name to a different variable is replaced with the new one.
|
// of the same name to a different variable is replaced with the new one.
|
||||||
Variable CreateVariable(
|
auto CreateVariable(const std::string &name,
|
||||||
const std::string &name, Variable::Type type = Variable::Type::Any) {
|
Variable::Type type = Variable::Type::Any);
|
||||||
auto symbol = symbol_table_.CreateSymbol(name);
|
|
||||||
auto variable = Variable{symbol, type};
|
|
||||||
scope_.variables[name] = variable;
|
|
||||||
return variable;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the variable by name. If the mapping already exists, checks if the
|
// Returns the variable by name. If the mapping already exists, checks if the
|
||||||
// types match. Otherwise, returns a new variable.
|
// types match. Otherwise, returns a new variable.
|
||||||
Variable GetOrCreateVariable(
|
auto GetOrCreateVariable(const std::string &name,
|
||||||
const std::string &name, Variable::Type type = Variable::Type::Any) {
|
Variable::Type type = Variable::Type::Any);
|
||||||
auto search = scope_.variables.find(name);
|
|
||||||
if (search != scope_.variables.end()) {
|
|
||||||
auto variable = search->second;
|
|
||||||
if (type != Variable::Type::Any && type != variable.type) {
|
|
||||||
throw TypeMismatchError(name, TypeToString(variable.type),
|
|
||||||
TypeToString(type));
|
|
||||||
}
|
|
||||||
return search->second;
|
|
||||||
}
|
|
||||||
return CreateVariable(name, type);
|
|
||||||
}
|
|
||||||
|
|
||||||
SymbolTable &symbol_table_;
|
SymbolTable &symbol_table_;
|
||||||
Scope scope_;
|
Scope scope_;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
} // namespace query
|
||||||
|
Loading…
Reference in New Issue
Block a user