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