#include <gqlite.h>
#include <gqlite-c.h>
//BEGIN format.h




#include <string>

namespace std
{
  const char* to_string(gqlite::value_type _type);
}


#ifdef GQLITE_HAVE_CLOG

#include <fmt>

namespace gqlite
{
  template<typename... _Targs_>
  std::string format_string(const std::string& _format, const _Targs_&... _args)
  {
    return fmt::format(_format, _args...);
  }
}

#else

namespace gqlite
{
  /**
   * @internal
   * @return \p _integer converted to a string, with a minimum size of \p _width, filled with 0.
   */
  inline std::string to_string_fixed_width(int _integer, int _width)
  {
    std::string s = std::to_string(_integer);
    while(s.size() < _width)
    {
      s = '0' + s;
    }
    return s;
  }
  inline std::string format_string(const std::string& _format)
  {
    return _format;
  }
  namespace details
  {
    template<typename _T_>
    inline std::string to_string(const _T_& _v)
    {
      return std::to_string(_v);
    }
    template<>
    inline std::string to_string<std::string>(const std::string& _v)
    {
      return _v;
    }
    template<>
    inline std::string to_string<const char*>(const char* const& _v)
    {
      return _v;
    }
    template<>
    inline std::string to_string<char>(const char& _v)
    {
      std::string v;
      return v += _v;
    }
  }
  template<typename _T_, typename... _Targs_>
  inline std::string format_string(const std::string& _format, const _T_& _a, const _Targs_&... _args)
  {
    int start_pos = _format.find("{}");
    if(start_pos == std::string::npos)
    {
      return _format;
    }
    std::string f = _format;
    f.replace(start_pos, 2, details::to_string(_a));
    return format_string(f, _args...);
  }
}


#endif
//END format.h
//BEGIN logging.h
/**
 * logging file allowing optional support of clog
 */



#ifdef GQLITE_HAVE_CLOG

#include <clog>

#define gqlite_error(...) clog_error(__VA_ARGS__)
#define gqlite_warning(...) clog_warning(__VA_ARGS__)
#define gqlite_debug(...) clog_debug(__VA_ARGS__)
#define gqlite_info(...) clog_info(__VA_ARGS__)
#define gqlite_fatal(...) clog_fatal(__VA_ARGS__)

#define gqlite_debug_vn(...) clog_debug_vn(__VA_ARGS__)

#define gqlite_assert(...) clog_assert(__VA_ARGS__)
#define gqlite_assert_msg(...) clog_assert_msg(__VA_ARGS__)


#else

#include <iostream>

#define gqlite_error(msg, ...) std::cerr << "ERROR: " __FILE__ ":" << __LINE__ << ": " << msg << std::endl;
#define gqlite_warning(msg, ...) std::cerr << "WARNING: " __FILE__ ":" << __LINE__ << ": " << msg << std::endl;
#define gqlite_debug(msg, ...) std::cerr << "DEBUG: " __FILE__ ":" << __LINE__ << ": " << msg << std::endl;
#define gqlite_info(msg, ...) std::cerr << "INFO: " __FILE__ ":" << __LINE__ << ": " << msg << std::endl;
#define gqlite_fatal(msg, ...) std::cerr << "FATAL ERROR" __FILE__ ":" << __LINE__ << ": " << msg << std::endl; std::abort()

#define gqlite_debug_vn(...) clog_debug_vn(__VA_ARGS__)

#if NDEBUG

#define gqlite_assert(assrt) do { } while ((false) && (assrt))
#define gqlite_assert_msg(assrt, msg, ...) gqlite_assert(assrt)
#define gqlite_near_equal_assert(v1, v2, tol) (void)(v1); (void)(v2); (void)(tol); do { } while (false)

#else

#define gqlite_assert(assrt)                                     \
  if( not (assrt ) )                                                \
  {                                                                 \
    std::cerr << "assertion failed: " << #assrt << std::endl;       \
    std::abort();                                                   \
  }

#define gqlite_assert_msg(assrt, msg, ...)                                    \
  if( not (assrt ) )                                                        \
  {                                                                         \
    std::cerr << "assertion failed: " << msg << " " << #assrt << std::endl; \
    std::abort();                                                           \
  }

#define gqlite_near_equal_assert(v1, v2, tol)                                                          \
  {                                                                                                       \
    auto __clog__nea_ev1_ = (v1);                                                                         \
    auto __clog__nea_ev2_ = (v2);                                                                         \
    auto __clog__nea_tol_ = (tol);                                                                        \
    auto __clog__nea_error_ = std::abs(__clog__nea_ev1_ - __clog__nea_ev2_);                              \
    if(__clog__nea_error_ > __clog__nea_tol_)                                                             \
    {                                                                                                     \
      std::cerr << "Expected near equal: '" << __clog__nea_ev1_ << "' != '" << __clog__nea_ev2_           \
                << "' error: '" << __clog__nea_error_ << "', tolerance: '" << __clog__nea_tol_ << "'");   \
    }                                                                                                     \
  }


#endif

#endif
//END logging.h
//BEGIN string.h
#include <numeric>
#include <sstream>

namespace gqlite::string
{
  /**
   * @internal
   * @return the result of joining the element of a vector with delimiter
   */
  inline std::string join(const std::vector<std::string>& _v, const std::string& _delimiter)
  {
    return std::accumulate(
      std::next(_v.begin()), 
      _v.end(), 
      _v[0], 
      [_delimiter](std::string a, std::string b) {
          return a + _delimiter + b;
      }
    );
  }
  /**
   * @internal
   * @return the result of spliting \p s with delimiter \p delim
   */
  inline std::vector<std::string> split (const std::string &s, char delim)
  {
    std::vector<std::string> result;
    std::stringstream ss (s);
    std::string item;

    while (std::getline (ss, item, delim))
    {
      if(not item.empty()) result.push_back (item);
    }

    return result;
  }
}
//END string.h
//BEGIN gqlite_p.h


#include <gqlite.h>



namespace gqlite
{
  template<typename _T_, typename... _TOther_>
  inline exception::exception(const char* _format, const _T_& _value, const _TOther_&... _other) : exception(format_string(_format, _value, _other...))
  {
  }
  template<typename _T_, typename... _TOther_>
  inline exception::exception(const std::string& _format, const _T_& _value, const _TOther_&... _other) : exception(format_string(_format, _value, _other...))
  {
  }

  template<typename _T_>
  inline value::value(const std::initializer_list<typename std::unordered_map<std::string, _T_>::value_type>& _v) : value()
  {
    std::unordered_map<std::string, _T_> m{_v};
    std::unordered_map<std::string, _T_> mo;
    for(auto const&[k, v] : m)
    {
      mo[k] = v;
    }
    *this = value(mo);
  }
  template<typename _T_>
  inline value::value(const std::vector<_T_>& _v) : value()
  {
    value_vector vo;
    for(const _T_& t : _v)
    {
      vo.push_back(t);
    }
    *this = value(vo);
  }
}
//END gqlite_p.h
//BEGIN global.h


#define GQLITE_UNUSED (void*)

//BEGIN gqlite Foreach

// The following macros are for internal use by gqlite, no warranty of behavior is guaranteed

#define __GQLITE_INC(I) __GQLITE_INC_## I
#define __GQLITE_INC_0 1
#define __GQLITE_INC_1 2
#define __GQLITE_INC_2 3
#define __GQLITE_INC_3 4
#define __GQLITE_INC_4 5
#define __GQLITE_INC_5 6
#define __GQLITE_INC_6 7
#define __GQLITE_INC_7 8
#define __GQLITE_INC_8 9
#define __GQLITE_INC_9 10
#define __GQLITE_INC_10 11
#define __GQLITE_INC_11 12
#define __GQLITE_INC_12 13
#define __GQLITE_INC_13 14
#define __GQLITE_INC_14 15
#define __GQLITE_INC_15 16
#define __GQLITE_INC_16 17
#define __GQLITE_INC_17 18
#define __GQLITE_INC_18 19
#define __GQLITE_INC_19 20
#define __GQLITE_INC_20 21

#define __GQLITE_FOREACH__0(WHAT, SEP, I, ...)
#define __GQLITE_FOREACH__1(WHAT, SEP, I, X, ...) WHAT(X, I)
#define __GQLITE_FOREACH__2(WHAT, SEP, I, X, ...) WHAT(X, I) GQLITE_UNPACK SEP __GQLITE_FOREACH__1(WHAT, SEP, __GQLITE_INC(I), __VA_ARGS__)
#define __GQLITE_FOREACH__3(WHAT, SEP, I, X, ...) WHAT(X, I) GQLITE_UNPACK SEP __GQLITE_FOREACH__2(WHAT, SEP, __GQLITE_INC(I), __VA_ARGS__)
#define __GQLITE_FOREACH__4(WHAT, SEP, I, X, ...) WHAT(X, I) GQLITE_UNPACK SEP __GQLITE_FOREACH__3(WHAT, SEP, __GQLITE_INC(I), __VA_ARGS__)
#define __GQLITE_FOREACH__5(WHAT, SEP, I, X, ...) WHAT(X, I) GQLITE_UNPACK SEP __GQLITE_FOREACH__4(WHAT, SEP, __GQLITE_INC(I), __VA_ARGS__)
#define __GQLITE_FOREACH__6(WHAT, SEP, I, X, ...) WHAT(X, I) GQLITE_UNPACK SEP __GQLITE_FOREACH__5(WHAT, SEP, __GQLITE_INC(I), __VA_ARGS__)
#define __GQLITE_FOREACH__7(WHAT, SEP, I, X, ...) WHAT(X, I) GQLITE_UNPACK SEP __GQLITE_FOREACH__6(WHAT, SEP, __GQLITE_INC(I), __VA_ARGS__)
#define __GQLITE_FOREACH__8(WHAT, SEP, I, X, ...) WHAT(X, I) GQLITE_UNPACK SEP __GQLITE_FOREACH__7(WHAT, SEP, __GQLITE_INC(I), __VA_ARGS__)
#define __GQLITE_FOREACH__9(WHAT, SEP, I, X, ...) WHAT(X, I) GQLITE_UNPACK SEP __GQLITE_FOREACH__8(WHAT, SEP, __GQLITE_INC(I), __VA_ARGS__)
#define __GQLITE_FOREACH__10(WHAT, SEP, I, X, ...) WHAT(X, I) GQLITE_UNPACK SEP __GQLITE_FOREACH__9(WHAT, SEP, __GQLITE_INC(I), __VA_ARGS__)
#define __GQLITE_FOREACH__11(WHAT, SEP, I, X, ...) WHAT(X, I) GQLITE_UNPACK SEP __GQLITE_FOREACH__10(WHAT, SEP, __GQLITE_INC(I), __VA_ARGS__)
#define __GQLITE_FOREACH__12(WHAT, SEP, I, X, ...) WHAT(X, I) GQLITE_UNPACK SEP __GQLITE_FOREACH__11(WHAT, SEP, __GQLITE_INC(I), __VA_ARGS__)
#define __GQLITE_FOREACH__13(WHAT, SEP, I, X, ...) WHAT(X, I) GQLITE_UNPACK SEP __GQLITE_FOREACH__12(WHAT, SEP, __GQLITE_INC(I), __VA_ARGS__)
#define __GQLITE_FOREACH__14(WHAT, SEP, I, X, ...) WHAT(X, I) GQLITE_UNPACK SEP __GQLITE_FOREACH__13(WHAT, SEP, __GQLITE_INC(I), __VA_ARGS__)
#define __GQLITE_FOREACH__15(WHAT, SEP, I, X, ...) WHAT(X, I) GQLITE_UNPACK SEP __GQLITE_FOREACH__14(WHAT, SEP, __GQLITE_INC(I), __VA_ARGS__)
#define __GQLITE_FOREACH__16(WHAT, SEP, I, X, ...) WHAT(X, I) GQLITE_UNPACK SEP __GQLITE_FOREACH__15(WHAT, SEP, __GQLITE_INC(I), __VA_ARGS__)
#define __GQLITE_FOREACH__17(WHAT, SEP, I, X, ...) WHAT(X, I) GQLITE_UNPACK SEP __GQLITE_FOREACH__16(WHAT, SEP, __GQLITE_INC(I), __VA_ARGS__)
#define __GQLITE_FOREACH__18(WHAT, SEP, I, X, ...) WHAT(X, I) GQLITE_UNPACK SEP __GQLITE_FOREACH__17(WHAT, SEP, __GQLITE_INC(I), __VA_ARGS__)
#define __GQLITE_FOREACH__19(WHAT, SEP, I, X, ...) WHAT(X, I) GQLITE_UNPACK SEP __GQLITE_FOREACH__18(WHAT, SEP, __GQLITE_INC(I), __VA_ARGS__)
#define __GQLITE_FOREACH__20(WHAT, SEP, I, X, ...) WHAT(X, I) GQLITE_UNPACK SEP __GQLITE_FOREACH__19(WHAT, SEP, __GQLITE_INC(I), __VA_ARGS__)
#define __GQLITE_FOREACH__21(WHAT, SEP, I, X, ...) WHAT(X, I) GQLITE_UNPACK SEP __GQLITE_FOREACH__20(WHAT, SEP, __GQLITE_INC(I), __VA_ARGS__)
#define __GQLITE_FOREACH__22(WHAT, SEP, I, X, ...) WHAT(X, I) GQLITE_UNPACK SEP __GQLITE_FOREACH__21(WHAT, SEP, __GQLITE_INC(I), __VA_ARGS__)
#define __GQLITE_FOREACH__23(WHAT, SEP, I, X, ...) WHAT(X, I) GQLITE_UNPACK SEP __GQLITE_FOREACH__22(WHAT, SEP, __GQLITE_INC(I), __VA_ARGS__)
#define __GQLITE_FOREACH__24(WHAT, SEP, I, X, ...) WHAT(X, I) GQLITE_UNPACK SEP __GQLITE_FOREACH__23(WHAT, SEP, __GQLITE_INC(I), __VA_ARGS__)
#define __GQLITE_FOREACH__25(WHAT, SEP, I, X, ...) WHAT(X, I) GQLITE_UNPACK SEP __GQLITE_FOREACH__24(WHAT, SEP, __GQLITE_INC(I), __VA_ARGS__)
#define __GQLITE_FOREACH__26(WHAT, SEP, I, X, ...) WHAT(X, I) GQLITE_UNPACK SEP __GQLITE_FOREACH__25(WHAT, SEP, __GQLITE_INC(I), __VA_ARGS__)
#define __GQLITE_FOREACH__27(WHAT, SEP, I, X, ...) WHAT(X, I) GQLITE_UNPACK SEP __GQLITE_FOREACH__26(WHAT, SEP, __GQLITE_INC(I), __VA_ARGS__)
#define __GQLITE_FOREACH__28(WHAT, SEP, I, X, ...) WHAT(X, I) GQLITE_UNPACK SEP __GQLITE_FOREACH__27(WHAT, SEP, __GQLITE_INC(I), __VA_ARGS__)
#define __GQLITE_FOREACH__29(WHAT, SEP, I, X, ...) WHAT(X, I) GQLITE_UNPACK SEP __GQLITE_FOREACH__28(WHAT, SEP, __GQLITE_INC(I), __VA_ARGS__)
#define __GQLITE_FOREACH__30(WHAT, SEP, I, X, ...) WHAT(X, I) GQLITE_UNPACK SEP __GQLITE_FOREACH__29(WHAT, SEP, __GQLITE_INC(I), __VA_ARGS__)

#define __GQLITE_GET_MACRO(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, NAME, ...) NAME

/**
 * Execute action(X, I) on each variadic arguments.
 * X will contains the argument and I the index.
 * And add sep between each element
 * 
 * @code
 * #define PRINT(X, I) std::cout << " " # I ": " << X;
 * GQLITE_FOREACH(PRINT, do();, a, b, c)
 * @endcode
 * 
 * Will expand into:
 * 
 * @code
 * std::cout << " " "0" ": " << a; do();
 * std::cout << " " "1" ": " << b; do();
 * std::cout << " " "2" ": " << c;
 * @endcode
 */
#define GQLITE_FOREACH_SEP(action, sep, ...) \
  __GQLITE_GET_MACRO(void, __VA_ARGS__ __VA_OPT__(,) \
  __GQLITE_FOREACH__29, __GQLITE_FOREACH__28, __GQLITE_FOREACH__27, __GQLITE_FOREACH__26, __GQLITE_FOREACH__25, __GQLITE_FOREACH__24, __GQLITE_FOREACH__23, __GQLITE_FOREACH__22, __GQLITE_FOREACH__21, __GQLITE_FOREACH__20, \
  __GQLITE_FOREACH__19, __GQLITE_FOREACH__18, __GQLITE_FOREACH__17, __GQLITE_FOREACH__16, __GQLITE_FOREACH__15, __GQLITE_FOREACH__14, __GQLITE_FOREACH__13, __GQLITE_FOREACH__12, __GQLITE_FOREACH__11, __GQLITE_FOREACH__10, \
  __GQLITE_FOREACH__9, __GQLITE_FOREACH__8, __GQLITE_FOREACH__7, __GQLITE_FOREACH__6, __GQLITE_FOREACH__5, __GQLITE_FOREACH__4, __GQLITE_FOREACH__3, __GQLITE_FOREACH__2, __GQLITE_FOREACH__1, __GQLITE_FOREACH__0)(action, (sep), 0, __VA_ARGS__)

/**
 * Execute action(X, I) on each variadic arguments.
 * X will contains the argument and I the index.
 * 
 * @code
 * #define PRINT(X, I) std::cout << " " # I ": " << X;
 * GQLITE_FOREACH(PRINT, a, b, c)
 * @endcode
 * 
 * Will expand into:
 * 
 * @code
 * std::cout << " " "0" ": " << a;
 * std::cout << " " "1" ": " << b;
 * std::cout << " " "2" ": " << c;
 * @endcode
 */
#define GQLITE_FOREACH(action, ...) GQLITE_FOREACH_SEP(action, , __VA_ARGS__)

//END gqlite Foreach

//BEGIN gqlite List

#define __GQLITE_LIST_ACTION(__X__, __I__) , __X__

/**
 * Concatenate the arguments of the macro in a comma seperated list.
 * Usefull to enable multiple template parameters in a macro.
 * 
 * @code
 * SOME_MACRO(some_template<GQLITE_LIST(A,B)>)
 * @endcode
 */
#define GQLITE_LIST(__A__, ...) __A__ GQLITE_FOREACH(__GQLITE_LIST_ACTION, __VA_ARGS__)

//END gqlite List

namespace gqlite::traits
{
  /**
   * Define a trait that test if \ref T is in the set \ref Ts.
   * 
   * \code
   * contains_v<int, std::string, double> == false;
   * contains_v<std::string, std::string, double> == true;
   * \endcode
   */
  template<typename T, typename... Ts>
  struct contains : std::disjunction<std::is_same<T, Ts>...>
  {};
  template<typename T, typename... Ts>
  inline constexpr bool contains_v = contains<T, Ts...>::value;
}
//END global.h
//BEGIN errors.h
#include <gqlite.h>

namespace gqlite::errors
{
  template<typename... _TOther_>
  inline void check_condition(bool _condition, const char* _format, const _TOther_&... _other)
  {
    if(not _condition)
    {
      throw exception(_format, _other...);
    }
  }
  template<typename _T_>
  inline void check_arguments_size(const char* _fname, const std::vector<_T_>& _arguments, int _size)
  {
    check_condition(_arguments.size() == _size, "'{}' function expect {} arguments, got {}", _fname, _size, _arguments.size());
  }
  template<typename _T_>
  inline void check_argument_type(const char* _fname, _T_ _got, _T_ _expected)
  {
    check_condition(_got == _expected, "Invalid argument type for function '{}'", _fname);
  }
}
//END errors.h
//BEGIN table.h
#include <gqlite.h>

#include <span>
#include <string>
#include <unordered_map>
#include <vector>




namespace gqlite
{
  /**
   * @internal
   * 
   * This class allow to store data as a table, during the execution of the query.
   */
  template<typename _T_>
  class table
  {
  public:
    class row_view : public std::span<const _T_>
    {
      using std::span<const _T_>::span;
    public:
      operator std::vector<_T_>() const
      {
        return std::vector<_T_>(this->begin(), this->end());
      }
      template<template<typename T> class C>
      bool operator==(const C<_T_>& _v) const
      {
        return this->size() == _v.size() and std::equal(this->begin(), this->end(), _v.begin(), _v.end());
      }
    };
  public:
    table();
    ~table();
    /**
     * Add a single column. Adding column to a table with data is a costly operation.
     */
    void add_column(const std::string& _column, const _T_& _val = _T_());
    /**
     * Add a set of columns. Adding column to a table with data is a costly operation.
     */
    void add_columns(const std::vector<std::string>& _columns, const _T_& _val = _T_());
    /**
     * Add a row to the table.
     */
    void add_row(const std::vector<_T_>& _row);
    bool has_column(const std::string& _column) const;
    std::size_t get_column_index(const std::string& _column) const;
    /**
     * @return the name of the columns
     */
    std::vector<std::string> get_columns_names() const;
    std::size_t get_columns_count() const;
    std::size_t get_rows_count() const;
    /**
     * @return the row at index \p _j
     */
    row_view get_row(std::size_t _j) const;
    /**
     * @return the value at column \p _i and row \p _j
     */
    const _T_& get_value(std::size_t _i, std::size_t _j) const;
    /**
     * @return the value at column \p _column and row \p _j
     */
    const _T_& get_value(const std::string& _column, std::size_t _j) const;
  private:
    void add_columns(std::size_t _count, const _T_& _val);
    std::size_t m_columns = 0, m_rows = 0;
    std::unordered_map<std::string, int> m_labels;
    std::vector<_T_> m_values;
  };

  template<typename _T_>
  inline table<_T_>::table() {}
  template<typename _T_>
  inline table<_T_>::~table() {}
  template<typename _T_>
  inline void table<_T_>::add_column(const std::string& _column, const _T_& _val)
  {
    add_columns(1, _val);
    m_labels[_column] = m_labels.size();
  }
  template<typename _T_>
  inline void table<_T_>::add_columns(const std::vector<std::string>& _columns, const _T_& _val)
  {
    add_columns(_columns.size(), _val);
    for(const std::string& s : _columns)
    {
      m_labels[s] = m_labels.size();
    }
  }
  template<typename _T_>
  inline void table<_T_>::add_row(const std::vector<_T_>& _row)
  {
    errors::check_condition(_row.size() == get_columns_count(), "Invalid row size, got {}, expected {}", _row.size(), get_columns_count());
    m_values.insert(m_values.end(), _row.begin(), _row.end());
    ++m_rows;
  }
  template<typename _T_>
  bool table<_T_>::has_column(const std::string& _column) const
  {
    return m_labels.find(_column) != m_labels.end();
  }
  template<typename _T_>
  std::size_t table<_T_>::get_column_index(const std::string& _column) const
  {
    auto it = m_labels.find(_column);
    if(it != m_labels.end()) return it->second;
    throw exception("Unknown column {}");
  }
  template<typename _T_>
  inline std::vector<std::string> table<_T_>::get_columns_names() const
  {
    std::vector<std::string> col;
    col.resize(m_columns);
    for(auto const& [k, v] : m_labels)
    {
      col[v] = k;
    }
    return col;
  }
  template<typename _T_>
  inline std::size_t table<_T_>::get_columns_count() const
  {
    return m_columns;
  }
  template<typename _T_>
  inline std::size_t table<_T_>::get_rows_count() const
  {
    return m_rows;
  }
  template<typename _T_>
  inline table<_T_>::row_view table<_T_>::get_row(std::size_t _j) const
  {
    if(_j >= m_rows)
    {
      throw exception("Invalid index {}, size is {}", _j, m_rows);
    }
    return row_view(m_values.begin() + _j * m_columns, m_values.begin() + (_j+1) * m_columns);
  }
  template<typename _T_>
  inline const _T_& table<_T_>::get_value(std::size_t _i, std::size_t _j) const
  {
    return m_values[_i + _j * m_columns];
  }
  template<typename _T_>
  inline const _T_& table<_T_>::get_value(const std::string& _column, std::size_t _j) const
  {
    auto it = m_labels.find(_column);
    if(it == m_labels.end()) throw gqlite::exception("Unknown column {}", _column);
    return get_value(it->second, _j);
  }
  template<typename _T_>
  inline void table<_T_>::add_columns(std::size_t _count, const _T_& _val)
  {
    std::size_t new_columns = m_columns + _count;
    if(m_rows > 0)
    {
      std::vector<_T_> new_values;
      new_values.reserve(new_columns * m_rows);
      auto new_value_bi = std::back_inserter(new_values);
      for(std::size_t j = 0; j < m_rows; ++j)
      {
        std::copy(m_values.begin() + m_columns * j, m_values.begin()  + m_columns * (j+1), new_value_bi);
        std::fill_n(new_value_bi, _count, _val);
      }
      m_values = new_values;
    }
    m_columns = new_columns;
  }
}
//END table.h
//BEGIN oc/token.h
#include <string>

namespace gqlite::oc
{
  enum class token_type
  {
  // Not really token
    UNFINISHED_STRING = -4,
    UNFINISHED_COMMENT = -3,
    END_OF_FILE = -2,
    UNKNOWN = - 1,
  // Special characters
    SEMI = 0, ///< ;
    STARTBRACE, ///< {
    ENDBRACE, ///< }
    STARTBRACKET, ///< (
    ENDBRACKET, ///< )
    STARTBOXBRACKET, ///< [
    ENDBOXBRACKET, ///< ]
    PIPE, ///< |
    MINUS, ///< -
    COMMA, ///< ,
    COLON, ///< :
    COLONCOLON, ///< :
    EQUAL, ///< =
    DIFFERENT, ///< !=
    SUPERIOR, ///< >
    INFERIOR, ///< <
    SUPERIOR_EQUAL, ///< >=
    INFERIOR_EQUAL, ///< <=
    DOT, ///< .
    LEFT_ARROW, ///< ->
    RIGHT_ARROW, ///< <-
    PLUS, ///< +
    STAR, ///< *
    DIVIDE, ///< /
    EXCLAMATION, ///< !
    PLUS_EQUAL, ///< +=
  // Constants
    IDENTIFIER,
    STRING,
    INTEGER,
    FLOATING_POINT,
  // Keywords
#define TOKEN_KEYWORD(_K_) _K_,
#define TOKEN_KEYWORD2(_K_, _S_) _K_,
    TOKEN_KEYWORD(AND)
TOKEN_KEYWORD(AS)
TOKEN_KEYWORD(CREATE)
TOKEN_KEYWORD(DELETE)
TOKEN_KEYWORD(DETACH)
TOKEN_KEYWORD2(FALSE, "false")
TOKEN_KEYWORD(IN)
TOKEN_KEYWORD(IS)
TOKEN_KEYWORD(NOT)
TOKEN_KEYWORD(MATCH)
TOKEN_KEYWORD2(NULL_CAPS, "NULL")
TOKEN_KEYWORD2(NULL_TOKEN, "null")
TOKEN_KEYWORD(OR)
TOKEN_KEYWORD(REMOVE)
TOKEN_KEYWORD(RETURN)
TOKEN_KEYWORD(SET)
TOKEN_KEYWORD2(TRUE, "true")
TOKEN_KEYWORD(XOR)
TOKEN_KEYWORD(WITH)
TOKEN_KEYWORD(WHERE)

#undef TOKEN_KEYWORD
#undef TOKEN_KEYWORD2
  };
  const char* token_type_to_string(token_type _type );
  struct token
  {
    /// type of the token
    token_type type;
    /// line of the token
    int line;
    /// Column of the token
    int column;
    /// String or identifier name
    std::string string;
    
    token();
    /**
      * Creates a token of the given type
      */
    token(token_type _type, int _line, int _column);
    /**
      * Creates an identifier or a string constant
      */
    token(token_type _type, const std::string& _string, int _line, int _column);
  };
} // namespace gqlite::oc
//END oc/token.h
//BEGIN oc/lexer.h
#include <istream>

namespace gqlite::oc
{

class token;
class lexer
{
  public:
    lexer(std::istream* sstream);
    ~lexer();
  public:
    token next_token();
    void unget(const token& _token);
  protected:
    bool ignore_comment(token& _token, int _lastChar);
    /**
     * @return the next char and increment the column counter.
     */
    int get_next_char();
    /**
     * @return the next char that is not a separator (space, tab, return...)
     */
    int get_next_non_separator_char();
    /**
     * Cancel the reading of the previous char.
     */
    void unget();
    bool eof() const;
    int line() const;
    int column() const;
    /**
     * Get an identifier (or keyword) in the current flow of character.
     */
    std::string get_identifier(int lastChar);
    token get_string(int lastChar);
  private:
    struct data;
    data* const d;
};

} // namespace gqlite::oc
//END oc/lexer.h
//BEGIN oc/algebra/node_type.h
namespace gqlite::oc::algebra
{
  enum class node_type
  {
#define OC_ALGEBRA_GENERATE(_KLASS_NAME_, _MEMBER_DEF_)                                                                                             \
  _KLASS_NAME_,
// Queries

#define OC_ALGEBRA_CREATE_NODES_MEMBERS(_KLASS_NAME_, F)                          \
  F(_KLASS_NAME_, std::vector<alternative<GQLITE_LIST(graph_node, graph_edge)>>, patterns)

OC_ALGEBRA_GENERATE(create, OC_ALGEBRA_CREATE_NODES_MEMBERS)

#define OC_ALGEBRA_MATCH_NODES_MEMBERS(_KLASS_NAME_, F)                                     \
  F(_KLASS_NAME_, std::vector<alternative<GQLITE_LIST(graph_node, graph_edge)>>, patterns)  \
  F(_KLASS_NAME_, node_csp, where)

OC_ALGEBRA_GENERATE(match, OC_ALGEBRA_MATCH_NODES_MEMBERS)

// Statements

#define OC_ALGEBRA_NODES_LIST_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::vector<node_csp>, nodes)

OC_ALGEBRA_GENERATE(statements, OC_ALGEBRA_NODES_LIST_MEMBERS)

#define OC_ALGEBRA_RETURN_MEMBERS(_KLASS_NAME_, F)                \
  F(_KLASS_NAME_, bool, all)                                      \
  F(_KLASS_NAME_, std::vector<named_expression_csp>, expressions)

OC_ALGEBRA_GENERATE(return_statement, OC_ALGEBRA_RETURN_MEMBERS)

#define OC_ALGEBRA_WITH_MEMBERS(_KLASS_NAME_, F)                  \
  F(_KLASS_NAME_, bool, all)                                      \
  F(_KLASS_NAME_, std::vector<named_expression_csp>, expressions)

OC_ALGEBRA_GENERATE(with, OC_ALGEBRA_WITH_MEMBERS)

#define OC_ALGEBRA_DELETE_MEMBERS(_KLASS_NAME_, F)  \
  F(_KLASS_NAME_, bool, detach)                     \
  F(_KLASS_NAME_, std::vector<node_csp>, expressions)

OC_ALGEBRA_GENERATE(delete_statement, OC_ALGEBRA_DELETE_MEMBERS)

OC_ALGEBRA_GENERATE(set, OC_ALGEBRA_NODES_LIST_MEMBERS)
OC_ALGEBRA_GENERATE(remove, OC_ALGEBRA_NODES_LIST_MEMBERS)

// Set/remove Expressions

#define OC_ALGEBRA_SET_ADD_VALUE_MEMBERS(_KLASS_NAME_, F)                 \
  F(_KLASS_NAME_, member_access_csp, target)                              \
  F(_KLASS_NAME_, node_csp, expression)

OC_ALGEBRA_GENERATE(set_property, OC_ALGEBRA_SET_ADD_VALUE_MEMBERS)


#define OC_ALGEBRA_SET_EQUAL_MEMBERS(_KLASS_NAME_, F)                     \
  F(_KLASS_NAME_, member_access_csp, target)                              \
  F(_KLASS_NAME_, nocde_csp, expression)

OC_ALGEBRA_GENERATE(add_property, OC_ALGEBRA_SET_ADD_VALUE_MEMBERS)

#define OC_ALGEBRA_REMOVE_PROPERTY_MEMBERS(_KLASS_NAME_, F)                   \
  F(_KLASS_NAME_, member_access_csp, target)

OC_ALGEBRA_GENERATE(remove_property, OC_ALGEBRA_REMOVE_PROPERTY_MEMBERS)

#define OC_ALGEBRA_EDIT_LABELS_MEMBERS(_KLASS_NAME_, F)                   \
  F(_KLASS_NAME_, std::string, target)                                    \
  F(_KLASS_NAME_, std::vector<std::string>, labels)

OC_ALGEBRA_GENERATE(edit_labels, OC_ALGEBRA_EDIT_LABELS_MEMBERS)


// Values

#define OC_ALGEBRA_GRAPH_NODE_MEMBERS(_KLASS_NAME_, F)          \
  F(_KLASS_NAME_, std::string, variable)                        \
  F(_KLASS_NAME_, std::vector<std::string>, labels)             \
  F(_KLASS_NAME_, map_csp, properties)

OC_ALGEBRA_GENERATE(graph_node, OC_ALGEBRA_GRAPH_NODE_MEMBERS)

#define OC_ALGEBRA_GRAPH_EDGE_MEMBERS(_KLASS_NAME_, F)          \
  F(_KLASS_NAME_, std::string, variable)                        \
  F(_KLASS_NAME_, graph_node_csp, source)                       \
  F(_KLASS_NAME_, graph_node_csp, destination)                  \
  F(_KLASS_NAME_, edge_directivity, directivity)                \
  F(_KLASS_NAME_, std::vector<std::string>, labels)             \
  F(_KLASS_NAME_, map_csp, properties)

OC_ALGEBRA_GENERATE(graph_edge, OC_ALGEBRA_GRAPH_EDGE_MEMBERS)

#define OC_ALGEBRA_VALUE_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, gqlite::value, value)

OC_ALGEBRA_GENERATE(value, OC_ALGEBRA_VALUE_MEMBERS)


#define OC_ALGEBRA_MAP_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::unordered_map<GQLITE_LIST(std::string, node_csp)>, map)

OC_ALGEBRA_GENERATE(map, OC_ALGEBRA_MAP_MEMBERS)

#define OC_ALGEBRA_ARRAY_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::vector<node_csp>, array)

OC_ALGEBRA_GENERATE(array, OC_ALGEBRA_ARRAY_MEMBERS)


// Expressions

#define OC_ALGEBRA_NAMED_EXPRESSION(_KLASS_NAME_, F)      \
  F(_KLASS_NAME_, std::string, name)                      \
  F(_KLASS_NAME_, node_csp, expression)

OC_ALGEBRA_GENERATE(named_expression, OC_ALGEBRA_NAMED_EXPRESSION)

#define OC_ALGEBRA_VARIABLE_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::string, identifier)

OC_ALGEBRA_GENERATE(variable, OC_ALGEBRA_VARIABLE_MEMBERS)

#define OC_ALGEBRA_MEMBER_ACCESS_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::string, left)                      \
  F(_KLASS_NAME_, std::vector<std::string>, path)

OC_ALGEBRA_GENERATE(member_access, OC_ALGEBRA_MEMBER_ACCESS_MEMBERS)

#define OC_ALGEBRA_HAS_LABELS_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::string, left)                   \
  F(_KLASS_NAME_, std::vector<std::string>, labels)

OC_ALGEBRA_GENERATE(has_labels, OC_ALGEBRA_HAS_LABELS_MEMBERS)

#define OC_ALGEBRA_FUNCTION_CALL_EXPRESSION(_KLASS_NAME_, F)  \
  F(_KLASS_NAME_, std::string, name)                          \
  F(_KLASS_NAME_, std::vector<node_csp>, arguments)

OC_ALGEBRA_GENERATE(function_call, OC_ALGEBRA_FUNCTION_CALL_EXPRESSION)

#define OC_ALGEBRA_BINARY_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, node_csp, left)                  \
  F(_KLASS_NAME_, node_csp, right)

OC_ALGEBRA_GENERATE(logical_and, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(logical_or, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_equal, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_different, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_inferior, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_superior, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_inferior_equal, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_superior_equal, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_in, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_not_in, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(addition, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(substraction, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(multiplication, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(division, OC_ALGEBRA_BINARY_MEMBERS)

#define OC_ALGEBRA_UNARY_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, node_csp, value)

OC_ALGEBRA_GENERATE(logical_negation, OC_ALGEBRA_UNARY_MEMBERS)
OC_ALGEBRA_GENERATE(negation, OC_ALGEBRA_UNARY_MEMBERS)
OC_ALGEBRA_GENERATE(is_null, OC_ALGEBRA_UNARY_MEMBERS)
OC_ALGEBRA_GENERATE(is_not_null, OC_ALGEBRA_UNARY_MEMBERS)

#undef OC_ALGEBRA_GENERATE
    __Invalid__
  };
  const char* node_type_name(node_type _nt);
}
//END oc/algebra/node_type.h
//BEGIN oc/algebra/node.h
#ifndef _OC_ALGEBRA_ABSTRACT_NODE_H_
#define _OC_ALGEBRA_ABSTRACT_NODE_H_

#include <functional>
#include <memory>

#include <gqlite.h>



namespace gqlite::oc::algebra
{
  enum class edge_directivity
  {
    directed, undirected
  };
  template<typename _TR_, typename... _TArgs_>
  class abstract_node_visitor;
  namespace details
  {
    class abstract_node_visitor_adaptor;
  }
  
  class node : public std::enable_shared_from_this<node>
  {
    template<typename _TR_, typename... _TArgs_>
    friend class abstract_node_visitor;
  protected:
    struct data;
    data* d;
    node(node_type _type, data* );
  public:
    virtual ~node();
  public:
    node_type get_type() const;
    virtual bool equals(const std::shared_ptr<const node>& _node) const = 0;
  protected:
    virtual void accept(details::abstract_node_visitor_adaptor* _node, void* _r, void* _parameter) const = 0;
  };
  typedef std::shared_ptr<const node> node_csp;
  template<typename... _T_>
  class alternative
  {
    static_assert(std::conjunction_v<std::is_base_of<node, _T_>...> , "All types should subclass node");
  public:
    alternative() : m_type(node_type::__Invalid__)
    {
    }
    template<typename _To_, std::enable_if_t<traits::contains_v<_To_, _T_...>, int> = 0>
    alternative(std::shared_ptr<const _To_> _v) : m_node(_v), m_type(_v->get_type())
    {}
    template<typename _To_, std::enable_if_t<traits::contains_v<_To_, _T_...>, int> = 0>
    alternative(const _To_* _v) : m_node(_v), m_type(_v->get_type())
    {}
    node_type get_type() const { return m_type; }
    template<typename _To_, std::enable_if_t<traits::contains_v<_To_, _T_...>, int> = 0>
    std::shared_ptr<const _To_> get_value() const
    {
      return std::static_pointer_cast<const _To_>(m_node);
    }
    bool is_valid() const { return m_type != node_type::__Invalid__; }
    node_csp get_node() const { return m_node; }
    template<typename _TRet_>
    _TRet_ visit(std::function<_TRet_(std::shared_ptr<const _T_>)>... _functors) const
    {
      return visit_impl(_functors...);
    }
  private:
    template<typename _TRet_, typename _T1_, typename... _TOther_>
    _TRet_ visit_impl(std::function<_TRet_(std::shared_ptr<const _T1_>)> _f1, std::function<_TRet_(std::shared_ptr<const _TOther_>)>... _others) const
    {
      std::shared_ptr<const _T1_> t = std::dynamic_pointer_cast<const _T1_>(m_node);
      if(t) return _f1(t);
      return visit_impl<_TRet_>(_others...);
    }
    template<typename _TRet_>
    _TRet_ visit_impl() const { throw gqlite::exception("Internal error: no alternative found."); }
    node_csp m_node;
    node_type m_type;
  };
}

#endif
//END oc/algebra/node.h
//BEGIN oc/algebra/node_p.h


namespace gqlite::oc::algebra
{
  struct node::data
  {
    virtual ~data() {}
    node_type type;
  };
}
//END oc/algebra/node_p.h
//BEGIN oc/algebra/nodes.h
#ifndef _OC_ALGEBRA_NODES_H_
#define _OC_ALGEBRA_NODES_H_

#include <gqlite.h>



namespace gqlite::oc::algebra
{
  enum class match_modifier
  {
    none, distinct
  };
}

#define OC_ALGEBRA_GENERATE(_KLASS_NAME_, _MEMBER_DEF_)                                                                                             \
  class _KLASS_NAME_;                                                                                                                                       \
  typedef std::shared_ptr<const _KLASS_NAME_> _KLASS_NAME_ ## _csp;


namespace gqlite::oc::algebra
{
// Queries

#define OC_ALGEBRA_CREATE_NODES_MEMBERS(_KLASS_NAME_, F)                          \
  F(_KLASS_NAME_, std::vector<alternative<GQLITE_LIST(graph_node, graph_edge)>>, patterns)

OC_ALGEBRA_GENERATE(create, OC_ALGEBRA_CREATE_NODES_MEMBERS)

#define OC_ALGEBRA_MATCH_NODES_MEMBERS(_KLASS_NAME_, F)                                     \
  F(_KLASS_NAME_, std::vector<alternative<GQLITE_LIST(graph_node, graph_edge)>>, patterns)  \
  F(_KLASS_NAME_, node_csp, where)

OC_ALGEBRA_GENERATE(match, OC_ALGEBRA_MATCH_NODES_MEMBERS)

// Statements

#define OC_ALGEBRA_NODES_LIST_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::vector<node_csp>, nodes)

OC_ALGEBRA_GENERATE(statements, OC_ALGEBRA_NODES_LIST_MEMBERS)

#define OC_ALGEBRA_RETURN_MEMBERS(_KLASS_NAME_, F)                \
  F(_KLASS_NAME_, bool, all)                                      \
  F(_KLASS_NAME_, std::vector<named_expression_csp>, expressions)

OC_ALGEBRA_GENERATE(return_statement, OC_ALGEBRA_RETURN_MEMBERS)

#define OC_ALGEBRA_WITH_MEMBERS(_KLASS_NAME_, F)                  \
  F(_KLASS_NAME_, bool, all)                                      \
  F(_KLASS_NAME_, std::vector<named_expression_csp>, expressions)

OC_ALGEBRA_GENERATE(with, OC_ALGEBRA_WITH_MEMBERS)

#define OC_ALGEBRA_DELETE_MEMBERS(_KLASS_NAME_, F)  \
  F(_KLASS_NAME_, bool, detach)                     \
  F(_KLASS_NAME_, std::vector<node_csp>, expressions)

OC_ALGEBRA_GENERATE(delete_statement, OC_ALGEBRA_DELETE_MEMBERS)

OC_ALGEBRA_GENERATE(set, OC_ALGEBRA_NODES_LIST_MEMBERS)
OC_ALGEBRA_GENERATE(remove, OC_ALGEBRA_NODES_LIST_MEMBERS)

// Set/remove Expressions

#define OC_ALGEBRA_SET_ADD_VALUE_MEMBERS(_KLASS_NAME_, F)                 \
  F(_KLASS_NAME_, member_access_csp, target)                              \
  F(_KLASS_NAME_, node_csp, expression)

OC_ALGEBRA_GENERATE(set_property, OC_ALGEBRA_SET_ADD_VALUE_MEMBERS)


#define OC_ALGEBRA_SET_EQUAL_MEMBERS(_KLASS_NAME_, F)                     \
  F(_KLASS_NAME_, member_access_csp, target)                              \
  F(_KLASS_NAME_, nocde_csp, expression)

OC_ALGEBRA_GENERATE(add_property, OC_ALGEBRA_SET_ADD_VALUE_MEMBERS)

#define OC_ALGEBRA_REMOVE_PROPERTY_MEMBERS(_KLASS_NAME_, F)                   \
  F(_KLASS_NAME_, member_access_csp, target)

OC_ALGEBRA_GENERATE(remove_property, OC_ALGEBRA_REMOVE_PROPERTY_MEMBERS)

#define OC_ALGEBRA_EDIT_LABELS_MEMBERS(_KLASS_NAME_, F)                   \
  F(_KLASS_NAME_, std::string, target)                                    \
  F(_KLASS_NAME_, std::vector<std::string>, labels)

OC_ALGEBRA_GENERATE(edit_labels, OC_ALGEBRA_EDIT_LABELS_MEMBERS)


// Values

#define OC_ALGEBRA_GRAPH_NODE_MEMBERS(_KLASS_NAME_, F)          \
  F(_KLASS_NAME_, std::string, variable)                        \
  F(_KLASS_NAME_, std::vector<std::string>, labels)             \
  F(_KLASS_NAME_, map_csp, properties)

OC_ALGEBRA_GENERATE(graph_node, OC_ALGEBRA_GRAPH_NODE_MEMBERS)

#define OC_ALGEBRA_GRAPH_EDGE_MEMBERS(_KLASS_NAME_, F)          \
  F(_KLASS_NAME_, std::string, variable)                        \
  F(_KLASS_NAME_, graph_node_csp, source)                       \
  F(_KLASS_NAME_, graph_node_csp, destination)                  \
  F(_KLASS_NAME_, edge_directivity, directivity)                \
  F(_KLASS_NAME_, std::vector<std::string>, labels)             \
  F(_KLASS_NAME_, map_csp, properties)

OC_ALGEBRA_GENERATE(graph_edge, OC_ALGEBRA_GRAPH_EDGE_MEMBERS)

#define OC_ALGEBRA_VALUE_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, gqlite::value, value)

OC_ALGEBRA_GENERATE(value, OC_ALGEBRA_VALUE_MEMBERS)


#define OC_ALGEBRA_MAP_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::unordered_map<GQLITE_LIST(std::string, node_csp)>, map)

OC_ALGEBRA_GENERATE(map, OC_ALGEBRA_MAP_MEMBERS)

#define OC_ALGEBRA_ARRAY_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::vector<node_csp>, array)

OC_ALGEBRA_GENERATE(array, OC_ALGEBRA_ARRAY_MEMBERS)


// Expressions

#define OC_ALGEBRA_NAMED_EXPRESSION(_KLASS_NAME_, F)      \
  F(_KLASS_NAME_, std::string, name)                      \
  F(_KLASS_NAME_, node_csp, expression)

OC_ALGEBRA_GENERATE(named_expression, OC_ALGEBRA_NAMED_EXPRESSION)

#define OC_ALGEBRA_VARIABLE_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::string, identifier)

OC_ALGEBRA_GENERATE(variable, OC_ALGEBRA_VARIABLE_MEMBERS)

#define OC_ALGEBRA_MEMBER_ACCESS_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::string, left)                      \
  F(_KLASS_NAME_, std::vector<std::string>, path)

OC_ALGEBRA_GENERATE(member_access, OC_ALGEBRA_MEMBER_ACCESS_MEMBERS)

#define OC_ALGEBRA_HAS_LABELS_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::string, left)                   \
  F(_KLASS_NAME_, std::vector<std::string>, labels)

OC_ALGEBRA_GENERATE(has_labels, OC_ALGEBRA_HAS_LABELS_MEMBERS)

#define OC_ALGEBRA_FUNCTION_CALL_EXPRESSION(_KLASS_NAME_, F)  \
  F(_KLASS_NAME_, std::string, name)                          \
  F(_KLASS_NAME_, std::vector<node_csp>, arguments)

OC_ALGEBRA_GENERATE(function_call, OC_ALGEBRA_FUNCTION_CALL_EXPRESSION)

#define OC_ALGEBRA_BINARY_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, node_csp, left)                  \
  F(_KLASS_NAME_, node_csp, right)

OC_ALGEBRA_GENERATE(logical_and, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(logical_or, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_equal, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_different, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_inferior, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_superior, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_inferior_equal, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_superior_equal, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_in, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_not_in, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(addition, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(substraction, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(multiplication, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(division, OC_ALGEBRA_BINARY_MEMBERS)

#define OC_ALGEBRA_UNARY_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, node_csp, value)

OC_ALGEBRA_GENERATE(logical_negation, OC_ALGEBRA_UNARY_MEMBERS)
OC_ALGEBRA_GENERATE(negation, OC_ALGEBRA_UNARY_MEMBERS)
OC_ALGEBRA_GENERATE(is_null, OC_ALGEBRA_UNARY_MEMBERS)
OC_ALGEBRA_GENERATE(is_not_null, OC_ALGEBRA_UNARY_MEMBERS)

}

#undef OC_ALGEBRA_GENERATE

#define OC_ALGEBRA_GENERATE_CONSTRUCTOR_ARGUMENT(_KLASS_NAME_, _TYPE_, _NAME_)      \
  _TYPE_ _ ## _NAME_,

#define OC_ALGEBRA_GENERATE_ACCESSOR_DECLARATION(_KLASS_NAME_, _TYPE_, _NAME_,...)  \
  _TYPE_ get_ ## _NAME_() const;

#define OC_ALGEBRA_GENERATE(_KLASS_NAME_, _MEMBER_DEF_)                                                                                         \
  class _KLASS_NAME_ : public node                                                                                                              \
  {                                                                                                                                             \
  public:                                                                                                                                       \
    _KLASS_NAME_(_MEMBER_DEF_(_KLASS_NAME_, OC_ALGEBRA_GENERATE_CONSTRUCTOR_ARGUMENT) void* _ = nullptr);                                       \
    ~_KLASS_NAME_();                                                                                                                            \
  public:                                                                                                                                       \
    _MEMBER_DEF_(_KLASS_NAME_, OC_ALGEBRA_GENERATE_ACCESSOR_DECLARATION);                                                                       \
    using node::accept;                                                                                                                         \
    bool equals(const std::shared_ptr<const node>& _node) const override;                                                                       \
  private:                                                                                                                                      \
    void accept(details::abstract_node_visitor_adaptor* _node, void* _r, void* _parameter) const override;                                      \
  private:                                                                                                                                      \
    struct data;                                                                                                                                \
  };

namespace gqlite::oc::algebra
{
// Queries

#define OC_ALGEBRA_CREATE_NODES_MEMBERS(_KLASS_NAME_, F)                          \
  F(_KLASS_NAME_, std::vector<alternative<GQLITE_LIST(graph_node, graph_edge)>>, patterns)

OC_ALGEBRA_GENERATE(create, OC_ALGEBRA_CREATE_NODES_MEMBERS)

#define OC_ALGEBRA_MATCH_NODES_MEMBERS(_KLASS_NAME_, F)                                     \
  F(_KLASS_NAME_, std::vector<alternative<GQLITE_LIST(graph_node, graph_edge)>>, patterns)  \
  F(_KLASS_NAME_, node_csp, where)

OC_ALGEBRA_GENERATE(match, OC_ALGEBRA_MATCH_NODES_MEMBERS)

// Statements

#define OC_ALGEBRA_NODES_LIST_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::vector<node_csp>, nodes)

OC_ALGEBRA_GENERATE(statements, OC_ALGEBRA_NODES_LIST_MEMBERS)

#define OC_ALGEBRA_RETURN_MEMBERS(_KLASS_NAME_, F)                \
  F(_KLASS_NAME_, bool, all)                                      \
  F(_KLASS_NAME_, std::vector<named_expression_csp>, expressions)

OC_ALGEBRA_GENERATE(return_statement, OC_ALGEBRA_RETURN_MEMBERS)

#define OC_ALGEBRA_WITH_MEMBERS(_KLASS_NAME_, F)                  \
  F(_KLASS_NAME_, bool, all)                                      \
  F(_KLASS_NAME_, std::vector<named_expression_csp>, expressions)

OC_ALGEBRA_GENERATE(with, OC_ALGEBRA_WITH_MEMBERS)

#define OC_ALGEBRA_DELETE_MEMBERS(_KLASS_NAME_, F)  \
  F(_KLASS_NAME_, bool, detach)                     \
  F(_KLASS_NAME_, std::vector<node_csp>, expressions)

OC_ALGEBRA_GENERATE(delete_statement, OC_ALGEBRA_DELETE_MEMBERS)

OC_ALGEBRA_GENERATE(set, OC_ALGEBRA_NODES_LIST_MEMBERS)
OC_ALGEBRA_GENERATE(remove, OC_ALGEBRA_NODES_LIST_MEMBERS)

// Set/remove Expressions

#define OC_ALGEBRA_SET_ADD_VALUE_MEMBERS(_KLASS_NAME_, F)                 \
  F(_KLASS_NAME_, member_access_csp, target)                              \
  F(_KLASS_NAME_, node_csp, expression)

OC_ALGEBRA_GENERATE(set_property, OC_ALGEBRA_SET_ADD_VALUE_MEMBERS)


#define OC_ALGEBRA_SET_EQUAL_MEMBERS(_KLASS_NAME_, F)                     \
  F(_KLASS_NAME_, member_access_csp, target)                              \
  F(_KLASS_NAME_, nocde_csp, expression)

OC_ALGEBRA_GENERATE(add_property, OC_ALGEBRA_SET_ADD_VALUE_MEMBERS)

#define OC_ALGEBRA_REMOVE_PROPERTY_MEMBERS(_KLASS_NAME_, F)                   \
  F(_KLASS_NAME_, member_access_csp, target)

OC_ALGEBRA_GENERATE(remove_property, OC_ALGEBRA_REMOVE_PROPERTY_MEMBERS)

#define OC_ALGEBRA_EDIT_LABELS_MEMBERS(_KLASS_NAME_, F)                   \
  F(_KLASS_NAME_, std::string, target)                                    \
  F(_KLASS_NAME_, std::vector<std::string>, labels)

OC_ALGEBRA_GENERATE(edit_labels, OC_ALGEBRA_EDIT_LABELS_MEMBERS)


// Values

#define OC_ALGEBRA_GRAPH_NODE_MEMBERS(_KLASS_NAME_, F)          \
  F(_KLASS_NAME_, std::string, variable)                        \
  F(_KLASS_NAME_, std::vector<std::string>, labels)             \
  F(_KLASS_NAME_, map_csp, properties)

OC_ALGEBRA_GENERATE(graph_node, OC_ALGEBRA_GRAPH_NODE_MEMBERS)

#define OC_ALGEBRA_GRAPH_EDGE_MEMBERS(_KLASS_NAME_, F)          \
  F(_KLASS_NAME_, std::string, variable)                        \
  F(_KLASS_NAME_, graph_node_csp, source)                       \
  F(_KLASS_NAME_, graph_node_csp, destination)                  \
  F(_KLASS_NAME_, edge_directivity, directivity)                \
  F(_KLASS_NAME_, std::vector<std::string>, labels)             \
  F(_KLASS_NAME_, map_csp, properties)

OC_ALGEBRA_GENERATE(graph_edge, OC_ALGEBRA_GRAPH_EDGE_MEMBERS)

#define OC_ALGEBRA_VALUE_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, gqlite::value, value)

OC_ALGEBRA_GENERATE(value, OC_ALGEBRA_VALUE_MEMBERS)


#define OC_ALGEBRA_MAP_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::unordered_map<GQLITE_LIST(std::string, node_csp)>, map)

OC_ALGEBRA_GENERATE(map, OC_ALGEBRA_MAP_MEMBERS)

#define OC_ALGEBRA_ARRAY_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::vector<node_csp>, array)

OC_ALGEBRA_GENERATE(array, OC_ALGEBRA_ARRAY_MEMBERS)


// Expressions

#define OC_ALGEBRA_NAMED_EXPRESSION(_KLASS_NAME_, F)      \
  F(_KLASS_NAME_, std::string, name)                      \
  F(_KLASS_NAME_, node_csp, expression)

OC_ALGEBRA_GENERATE(named_expression, OC_ALGEBRA_NAMED_EXPRESSION)

#define OC_ALGEBRA_VARIABLE_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::string, identifier)

OC_ALGEBRA_GENERATE(variable, OC_ALGEBRA_VARIABLE_MEMBERS)

#define OC_ALGEBRA_MEMBER_ACCESS_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::string, left)                      \
  F(_KLASS_NAME_, std::vector<std::string>, path)

OC_ALGEBRA_GENERATE(member_access, OC_ALGEBRA_MEMBER_ACCESS_MEMBERS)

#define OC_ALGEBRA_HAS_LABELS_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::string, left)                   \
  F(_KLASS_NAME_, std::vector<std::string>, labels)

OC_ALGEBRA_GENERATE(has_labels, OC_ALGEBRA_HAS_LABELS_MEMBERS)

#define OC_ALGEBRA_FUNCTION_CALL_EXPRESSION(_KLASS_NAME_, F)  \
  F(_KLASS_NAME_, std::string, name)                          \
  F(_KLASS_NAME_, std::vector<node_csp>, arguments)

OC_ALGEBRA_GENERATE(function_call, OC_ALGEBRA_FUNCTION_CALL_EXPRESSION)

#define OC_ALGEBRA_BINARY_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, node_csp, left)                  \
  F(_KLASS_NAME_, node_csp, right)

OC_ALGEBRA_GENERATE(logical_and, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(logical_or, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_equal, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_different, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_inferior, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_superior, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_inferior_equal, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_superior_equal, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_in, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_not_in, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(addition, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(substraction, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(multiplication, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(division, OC_ALGEBRA_BINARY_MEMBERS)

#define OC_ALGEBRA_UNARY_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, node_csp, value)

OC_ALGEBRA_GENERATE(logical_negation, OC_ALGEBRA_UNARY_MEMBERS)
OC_ALGEBRA_GENERATE(negation, OC_ALGEBRA_UNARY_MEMBERS)
OC_ALGEBRA_GENERATE(is_null, OC_ALGEBRA_UNARY_MEMBERS)
OC_ALGEBRA_GENERATE(is_not_null, OC_ALGEBRA_UNARY_MEMBERS)

}

#undef OC_ALGEBRA_GENERATE

#endif
//END oc/algebra/nodes.h
//BEGIN oc/algebra/abstract_node_visitor.h
#ifndef _OC_ALGEBRA_ABSTRACT_NODE_VISITOR_H_
#define _OC_ALGEBRA_ABSTRACT_NODE_VISITOR_H_

#include <any>
#include <vector>
#include <unordered_map>



namespace gqlite::oc::algebra
{
  namespace details
  {
    class abstract_node_visitor_adaptor
    {
    public:
      abstract_node_visitor_adaptor();
      ~abstract_node_visitor_adaptor();
  #define OC_ALGEBRA_GENERATE(_KLASS_NAME_, _MEMBER_DEF_)                         \
      virtual void call_visit(_KLASS_NAME_ ## _csp _node, void* _r, void* _parameter) = 0;
  // Queries

#define OC_ALGEBRA_CREATE_NODES_MEMBERS(_KLASS_NAME_, F)                          \
  F(_KLASS_NAME_, std::vector<alternative<GQLITE_LIST(graph_node, graph_edge)>>, patterns)

OC_ALGEBRA_GENERATE(create, OC_ALGEBRA_CREATE_NODES_MEMBERS)

#define OC_ALGEBRA_MATCH_NODES_MEMBERS(_KLASS_NAME_, F)                                     \
  F(_KLASS_NAME_, std::vector<alternative<GQLITE_LIST(graph_node, graph_edge)>>, patterns)  \
  F(_KLASS_NAME_, node_csp, where)

OC_ALGEBRA_GENERATE(match, OC_ALGEBRA_MATCH_NODES_MEMBERS)

// Statements

#define OC_ALGEBRA_NODES_LIST_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::vector<node_csp>, nodes)

OC_ALGEBRA_GENERATE(statements, OC_ALGEBRA_NODES_LIST_MEMBERS)

#define OC_ALGEBRA_RETURN_MEMBERS(_KLASS_NAME_, F)                \
  F(_KLASS_NAME_, bool, all)                                      \
  F(_KLASS_NAME_, std::vector<named_expression_csp>, expressions)

OC_ALGEBRA_GENERATE(return_statement, OC_ALGEBRA_RETURN_MEMBERS)

#define OC_ALGEBRA_WITH_MEMBERS(_KLASS_NAME_, F)                  \
  F(_KLASS_NAME_, bool, all)                                      \
  F(_KLASS_NAME_, std::vector<named_expression_csp>, expressions)

OC_ALGEBRA_GENERATE(with, OC_ALGEBRA_WITH_MEMBERS)

#define OC_ALGEBRA_DELETE_MEMBERS(_KLASS_NAME_, F)  \
  F(_KLASS_NAME_, bool, detach)                     \
  F(_KLASS_NAME_, std::vector<node_csp>, expressions)

OC_ALGEBRA_GENERATE(delete_statement, OC_ALGEBRA_DELETE_MEMBERS)

OC_ALGEBRA_GENERATE(set, OC_ALGEBRA_NODES_LIST_MEMBERS)
OC_ALGEBRA_GENERATE(remove, OC_ALGEBRA_NODES_LIST_MEMBERS)

// Set/remove Expressions

#define OC_ALGEBRA_SET_ADD_VALUE_MEMBERS(_KLASS_NAME_, F)                 \
  F(_KLASS_NAME_, member_access_csp, target)                              \
  F(_KLASS_NAME_, node_csp, expression)

OC_ALGEBRA_GENERATE(set_property, OC_ALGEBRA_SET_ADD_VALUE_MEMBERS)


#define OC_ALGEBRA_SET_EQUAL_MEMBERS(_KLASS_NAME_, F)                     \
  F(_KLASS_NAME_, member_access_csp, target)                              \
  F(_KLASS_NAME_, nocde_csp, expression)

OC_ALGEBRA_GENERATE(add_property, OC_ALGEBRA_SET_ADD_VALUE_MEMBERS)

#define OC_ALGEBRA_REMOVE_PROPERTY_MEMBERS(_KLASS_NAME_, F)                   \
  F(_KLASS_NAME_, member_access_csp, target)

OC_ALGEBRA_GENERATE(remove_property, OC_ALGEBRA_REMOVE_PROPERTY_MEMBERS)

#define OC_ALGEBRA_EDIT_LABELS_MEMBERS(_KLASS_NAME_, F)                   \
  F(_KLASS_NAME_, std::string, target)                                    \
  F(_KLASS_NAME_, std::vector<std::string>, labels)

OC_ALGEBRA_GENERATE(edit_labels, OC_ALGEBRA_EDIT_LABELS_MEMBERS)


// Values

#define OC_ALGEBRA_GRAPH_NODE_MEMBERS(_KLASS_NAME_, F)          \
  F(_KLASS_NAME_, std::string, variable)                        \
  F(_KLASS_NAME_, std::vector<std::string>, labels)             \
  F(_KLASS_NAME_, map_csp, properties)

OC_ALGEBRA_GENERATE(graph_node, OC_ALGEBRA_GRAPH_NODE_MEMBERS)

#define OC_ALGEBRA_GRAPH_EDGE_MEMBERS(_KLASS_NAME_, F)          \
  F(_KLASS_NAME_, std::string, variable)                        \
  F(_KLASS_NAME_, graph_node_csp, source)                       \
  F(_KLASS_NAME_, graph_node_csp, destination)                  \
  F(_KLASS_NAME_, edge_directivity, directivity)                \
  F(_KLASS_NAME_, std::vector<std::string>, labels)             \
  F(_KLASS_NAME_, map_csp, properties)

OC_ALGEBRA_GENERATE(graph_edge, OC_ALGEBRA_GRAPH_EDGE_MEMBERS)

#define OC_ALGEBRA_VALUE_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, gqlite::value, value)

OC_ALGEBRA_GENERATE(value, OC_ALGEBRA_VALUE_MEMBERS)


#define OC_ALGEBRA_MAP_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::unordered_map<GQLITE_LIST(std::string, node_csp)>, map)

OC_ALGEBRA_GENERATE(map, OC_ALGEBRA_MAP_MEMBERS)

#define OC_ALGEBRA_ARRAY_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::vector<node_csp>, array)

OC_ALGEBRA_GENERATE(array, OC_ALGEBRA_ARRAY_MEMBERS)


// Expressions

#define OC_ALGEBRA_NAMED_EXPRESSION(_KLASS_NAME_, F)      \
  F(_KLASS_NAME_, std::string, name)                      \
  F(_KLASS_NAME_, node_csp, expression)

OC_ALGEBRA_GENERATE(named_expression, OC_ALGEBRA_NAMED_EXPRESSION)

#define OC_ALGEBRA_VARIABLE_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::string, identifier)

OC_ALGEBRA_GENERATE(variable, OC_ALGEBRA_VARIABLE_MEMBERS)

#define OC_ALGEBRA_MEMBER_ACCESS_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::string, left)                      \
  F(_KLASS_NAME_, std::vector<std::string>, path)

OC_ALGEBRA_GENERATE(member_access, OC_ALGEBRA_MEMBER_ACCESS_MEMBERS)

#define OC_ALGEBRA_HAS_LABELS_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::string, left)                   \
  F(_KLASS_NAME_, std::vector<std::string>, labels)

OC_ALGEBRA_GENERATE(has_labels, OC_ALGEBRA_HAS_LABELS_MEMBERS)

#define OC_ALGEBRA_FUNCTION_CALL_EXPRESSION(_KLASS_NAME_, F)  \
  F(_KLASS_NAME_, std::string, name)                          \
  F(_KLASS_NAME_, std::vector<node_csp>, arguments)

OC_ALGEBRA_GENERATE(function_call, OC_ALGEBRA_FUNCTION_CALL_EXPRESSION)

#define OC_ALGEBRA_BINARY_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, node_csp, left)                  \
  F(_KLASS_NAME_, node_csp, right)

OC_ALGEBRA_GENERATE(logical_and, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(logical_or, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_equal, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_different, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_inferior, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_superior, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_inferior_equal, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_superior_equal, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_in, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_not_in, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(addition, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(substraction, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(multiplication, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(division, OC_ALGEBRA_BINARY_MEMBERS)

#define OC_ALGEBRA_UNARY_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, node_csp, value)

OC_ALGEBRA_GENERATE(logical_negation, OC_ALGEBRA_UNARY_MEMBERS)
OC_ALGEBRA_GENERATE(negation, OC_ALGEBRA_UNARY_MEMBERS)
OC_ALGEBRA_GENERATE(is_null, OC_ALGEBRA_UNARY_MEMBERS)
OC_ALGEBRA_GENERATE(is_not_null, OC_ALGEBRA_UNARY_MEMBERS)

  #undef OC_ALGEBRA_GENERATE
    // protected:
      // void accept(node_csp _node, void* _r, void* _parameter)
      // {
      //   return accept(_r, _node.ge(), _parameter);
      // }
      // void accept(const node* _node, void* _r, void* _parameter)
      // {
      //   if(_node)
      //   {
      //     _node->accept(this, _r, _parameter);
      //   }
      // }
    };    
  }
  
  template<typename _TR_, typename... _TArgs_>
  class abstract_node_visitor : details::abstract_node_visitor_adaptor
  {
    friend class node;
  public:
    using ParametersTuple = std::tuple<_TArgs_...>;
    static constexpr std::size_t ParametersCount = sizeof...(_TArgs_);
    using ReturnType = _TR_;
  public:
    abstract_node_visitor() {}
    ~abstract_node_visitor() {}
  public:
    /**
     * Call this function to start visiting
     */
    _TR_ start(node_csp _node, const _TArgs_&... _args)
    {
      return accept(_node, _args...);
    }
#define OC_ALGEBRA_GENERATE(_KLASS_NAME_, _MEMBER_DEF_)                                           \
  private:                                                                                                \
    virtual _TR_ visit(_KLASS_NAME_ ## _csp _node, const _TArgs_&... _parameter) = 0;                      \
    virtual void call_visit(_KLASS_NAME_ ## _csp _node, void* _r, void* _parameter) override               \
    {                                                                                                     \
      typedef _TR_ (abstract_node_visitor::* F)(_KLASS_NAME_ ## _csp _node, const _TArgs_&...);              \
      *reinterpret_cast<_TR_*>(_r) = std::apply(F(&abstract_node_visitor::visit), std::tuple_cat(std::make_tuple(this, _node),   \
                                                *reinterpret_cast<ParametersTuple*>(_parameter)));        \
    }
// Queries

#define OC_ALGEBRA_CREATE_NODES_MEMBERS(_KLASS_NAME_, F)                          \
  F(_KLASS_NAME_, std::vector<alternative<GQLITE_LIST(graph_node, graph_edge)>>, patterns)

OC_ALGEBRA_GENERATE(create, OC_ALGEBRA_CREATE_NODES_MEMBERS)

#define OC_ALGEBRA_MATCH_NODES_MEMBERS(_KLASS_NAME_, F)                                     \
  F(_KLASS_NAME_, std::vector<alternative<GQLITE_LIST(graph_node, graph_edge)>>, patterns)  \
  F(_KLASS_NAME_, node_csp, where)

OC_ALGEBRA_GENERATE(match, OC_ALGEBRA_MATCH_NODES_MEMBERS)

// Statements

#define OC_ALGEBRA_NODES_LIST_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::vector<node_csp>, nodes)

OC_ALGEBRA_GENERATE(statements, OC_ALGEBRA_NODES_LIST_MEMBERS)

#define OC_ALGEBRA_RETURN_MEMBERS(_KLASS_NAME_, F)                \
  F(_KLASS_NAME_, bool, all)                                      \
  F(_KLASS_NAME_, std::vector<named_expression_csp>, expressions)

OC_ALGEBRA_GENERATE(return_statement, OC_ALGEBRA_RETURN_MEMBERS)

#define OC_ALGEBRA_WITH_MEMBERS(_KLASS_NAME_, F)                  \
  F(_KLASS_NAME_, bool, all)                                      \
  F(_KLASS_NAME_, std::vector<named_expression_csp>, expressions)

OC_ALGEBRA_GENERATE(with, OC_ALGEBRA_WITH_MEMBERS)

#define OC_ALGEBRA_DELETE_MEMBERS(_KLASS_NAME_, F)  \
  F(_KLASS_NAME_, bool, detach)                     \
  F(_KLASS_NAME_, std::vector<node_csp>, expressions)

OC_ALGEBRA_GENERATE(delete_statement, OC_ALGEBRA_DELETE_MEMBERS)

OC_ALGEBRA_GENERATE(set, OC_ALGEBRA_NODES_LIST_MEMBERS)
OC_ALGEBRA_GENERATE(remove, OC_ALGEBRA_NODES_LIST_MEMBERS)

// Set/remove Expressions

#define OC_ALGEBRA_SET_ADD_VALUE_MEMBERS(_KLASS_NAME_, F)                 \
  F(_KLASS_NAME_, member_access_csp, target)                              \
  F(_KLASS_NAME_, node_csp, expression)

OC_ALGEBRA_GENERATE(set_property, OC_ALGEBRA_SET_ADD_VALUE_MEMBERS)


#define OC_ALGEBRA_SET_EQUAL_MEMBERS(_KLASS_NAME_, F)                     \
  F(_KLASS_NAME_, member_access_csp, target)                              \
  F(_KLASS_NAME_, nocde_csp, expression)

OC_ALGEBRA_GENERATE(add_property, OC_ALGEBRA_SET_ADD_VALUE_MEMBERS)

#define OC_ALGEBRA_REMOVE_PROPERTY_MEMBERS(_KLASS_NAME_, F)                   \
  F(_KLASS_NAME_, member_access_csp, target)

OC_ALGEBRA_GENERATE(remove_property, OC_ALGEBRA_REMOVE_PROPERTY_MEMBERS)

#define OC_ALGEBRA_EDIT_LABELS_MEMBERS(_KLASS_NAME_, F)                   \
  F(_KLASS_NAME_, std::string, target)                                    \
  F(_KLASS_NAME_, std::vector<std::string>, labels)

OC_ALGEBRA_GENERATE(edit_labels, OC_ALGEBRA_EDIT_LABELS_MEMBERS)


// Values

#define OC_ALGEBRA_GRAPH_NODE_MEMBERS(_KLASS_NAME_, F)          \
  F(_KLASS_NAME_, std::string, variable)                        \
  F(_KLASS_NAME_, std::vector<std::string>, labels)             \
  F(_KLASS_NAME_, map_csp, properties)

OC_ALGEBRA_GENERATE(graph_node, OC_ALGEBRA_GRAPH_NODE_MEMBERS)

#define OC_ALGEBRA_GRAPH_EDGE_MEMBERS(_KLASS_NAME_, F)          \
  F(_KLASS_NAME_, std::string, variable)                        \
  F(_KLASS_NAME_, graph_node_csp, source)                       \
  F(_KLASS_NAME_, graph_node_csp, destination)                  \
  F(_KLASS_NAME_, edge_directivity, directivity)                \
  F(_KLASS_NAME_, std::vector<std::string>, labels)             \
  F(_KLASS_NAME_, map_csp, properties)

OC_ALGEBRA_GENERATE(graph_edge, OC_ALGEBRA_GRAPH_EDGE_MEMBERS)

#define OC_ALGEBRA_VALUE_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, gqlite::value, value)

OC_ALGEBRA_GENERATE(value, OC_ALGEBRA_VALUE_MEMBERS)


#define OC_ALGEBRA_MAP_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::unordered_map<GQLITE_LIST(std::string, node_csp)>, map)

OC_ALGEBRA_GENERATE(map, OC_ALGEBRA_MAP_MEMBERS)

#define OC_ALGEBRA_ARRAY_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::vector<node_csp>, array)

OC_ALGEBRA_GENERATE(array, OC_ALGEBRA_ARRAY_MEMBERS)


// Expressions

#define OC_ALGEBRA_NAMED_EXPRESSION(_KLASS_NAME_, F)      \
  F(_KLASS_NAME_, std::string, name)                      \
  F(_KLASS_NAME_, node_csp, expression)

OC_ALGEBRA_GENERATE(named_expression, OC_ALGEBRA_NAMED_EXPRESSION)

#define OC_ALGEBRA_VARIABLE_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::string, identifier)

OC_ALGEBRA_GENERATE(variable, OC_ALGEBRA_VARIABLE_MEMBERS)

#define OC_ALGEBRA_MEMBER_ACCESS_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::string, left)                      \
  F(_KLASS_NAME_, std::vector<std::string>, path)

OC_ALGEBRA_GENERATE(member_access, OC_ALGEBRA_MEMBER_ACCESS_MEMBERS)

#define OC_ALGEBRA_HAS_LABELS_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::string, left)                   \
  F(_KLASS_NAME_, std::vector<std::string>, labels)

OC_ALGEBRA_GENERATE(has_labels, OC_ALGEBRA_HAS_LABELS_MEMBERS)

#define OC_ALGEBRA_FUNCTION_CALL_EXPRESSION(_KLASS_NAME_, F)  \
  F(_KLASS_NAME_, std::string, name)                          \
  F(_KLASS_NAME_, std::vector<node_csp>, arguments)

OC_ALGEBRA_GENERATE(function_call, OC_ALGEBRA_FUNCTION_CALL_EXPRESSION)

#define OC_ALGEBRA_BINARY_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, node_csp, left)                  \
  F(_KLASS_NAME_, node_csp, right)

OC_ALGEBRA_GENERATE(logical_and, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(logical_or, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_equal, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_different, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_inferior, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_superior, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_inferior_equal, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_superior_equal, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_in, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_not_in, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(addition, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(substraction, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(multiplication, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(division, OC_ALGEBRA_BINARY_MEMBERS)

#define OC_ALGEBRA_UNARY_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, node_csp, value)

OC_ALGEBRA_GENERATE(logical_negation, OC_ALGEBRA_UNARY_MEMBERS)
OC_ALGEBRA_GENERATE(negation, OC_ALGEBRA_UNARY_MEMBERS)
OC_ALGEBRA_GENERATE(is_null, OC_ALGEBRA_UNARY_MEMBERS)
OC_ALGEBRA_GENERATE(is_not_null, OC_ALGEBRA_UNARY_MEMBERS)

#undef OC_ALGEBRA_GENERATE
  protected:
    _TR_ accept(node_csp _node, const _TArgs_... _arguments)
    {
      return accept(_node.get(), _arguments...);
    }
    _TR_ accept(const node* _node, const _TArgs_... _arguments)
    {
      _TR_ r;
      if(_node)
      {
        ParametersTuple pt = std::make_tuple(_arguments...);
        _node->accept(this, &r, &pt);
      }
      return r;
    }
  };
  
  template<typename... _TArgs_>
  class abstract_node_visitor<void, _TArgs_...> : details::abstract_node_visitor_adaptor
  {
    friend class node;
  public:
    using ParametersTuple = std::tuple<_TArgs_...>;
    static constexpr std::size_t ParametersCount = sizeof...(_TArgs_);
    using ReturnType = void;
  public:
    abstract_node_visitor() {}
    ~abstract_node_visitor() {}
  public:
    /**
     * Call this function to start visiting
     */
    void start(node_csp _node, const _TArgs_&... _args)
    {
      accept(_node, _args...);
    }
#define OC_ALGEBRA_GENERATE(_KLASS_NAME_, _MEMBER_DEF_)                                                                 \
  private:                                                                                                                      \
    virtual void visit(_KLASS_NAME_ ## _csp _node, const _TArgs_&... _parameter) = 0;                                            \
    virtual void call_visit(_KLASS_NAME_ ## _csp _node, void*, void* _parameter) override                                        \
    {                                                                                                                           \
      typedef void (abstract_node_visitor::* F)(_KLASS_NAME_ ## _csp _node, const _TArgs_&...);                                    \
      std::apply(F(&abstract_node_visitor::visit), std::tuple_cat(std::make_tuple(this, _node), *reinterpret_cast<ParametersTuple*>(_parameter) ));    \
    }
// Queries

#define OC_ALGEBRA_CREATE_NODES_MEMBERS(_KLASS_NAME_, F)                          \
  F(_KLASS_NAME_, std::vector<alternative<GQLITE_LIST(graph_node, graph_edge)>>, patterns)

OC_ALGEBRA_GENERATE(create, OC_ALGEBRA_CREATE_NODES_MEMBERS)

#define OC_ALGEBRA_MATCH_NODES_MEMBERS(_KLASS_NAME_, F)                                     \
  F(_KLASS_NAME_, std::vector<alternative<GQLITE_LIST(graph_node, graph_edge)>>, patterns)  \
  F(_KLASS_NAME_, node_csp, where)

OC_ALGEBRA_GENERATE(match, OC_ALGEBRA_MATCH_NODES_MEMBERS)

// Statements

#define OC_ALGEBRA_NODES_LIST_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::vector<node_csp>, nodes)

OC_ALGEBRA_GENERATE(statements, OC_ALGEBRA_NODES_LIST_MEMBERS)

#define OC_ALGEBRA_RETURN_MEMBERS(_KLASS_NAME_, F)                \
  F(_KLASS_NAME_, bool, all)                                      \
  F(_KLASS_NAME_, std::vector<named_expression_csp>, expressions)

OC_ALGEBRA_GENERATE(return_statement, OC_ALGEBRA_RETURN_MEMBERS)

#define OC_ALGEBRA_WITH_MEMBERS(_KLASS_NAME_, F)                  \
  F(_KLASS_NAME_, bool, all)                                      \
  F(_KLASS_NAME_, std::vector<named_expression_csp>, expressions)

OC_ALGEBRA_GENERATE(with, OC_ALGEBRA_WITH_MEMBERS)

#define OC_ALGEBRA_DELETE_MEMBERS(_KLASS_NAME_, F)  \
  F(_KLASS_NAME_, bool, detach)                     \
  F(_KLASS_NAME_, std::vector<node_csp>, expressions)

OC_ALGEBRA_GENERATE(delete_statement, OC_ALGEBRA_DELETE_MEMBERS)

OC_ALGEBRA_GENERATE(set, OC_ALGEBRA_NODES_LIST_MEMBERS)
OC_ALGEBRA_GENERATE(remove, OC_ALGEBRA_NODES_LIST_MEMBERS)

// Set/remove Expressions

#define OC_ALGEBRA_SET_ADD_VALUE_MEMBERS(_KLASS_NAME_, F)                 \
  F(_KLASS_NAME_, member_access_csp, target)                              \
  F(_KLASS_NAME_, node_csp, expression)

OC_ALGEBRA_GENERATE(set_property, OC_ALGEBRA_SET_ADD_VALUE_MEMBERS)


#define OC_ALGEBRA_SET_EQUAL_MEMBERS(_KLASS_NAME_, F)                     \
  F(_KLASS_NAME_, member_access_csp, target)                              \
  F(_KLASS_NAME_, nocde_csp, expression)

OC_ALGEBRA_GENERATE(add_property, OC_ALGEBRA_SET_ADD_VALUE_MEMBERS)

#define OC_ALGEBRA_REMOVE_PROPERTY_MEMBERS(_KLASS_NAME_, F)                   \
  F(_KLASS_NAME_, member_access_csp, target)

OC_ALGEBRA_GENERATE(remove_property, OC_ALGEBRA_REMOVE_PROPERTY_MEMBERS)

#define OC_ALGEBRA_EDIT_LABELS_MEMBERS(_KLASS_NAME_, F)                   \
  F(_KLASS_NAME_, std::string, target)                                    \
  F(_KLASS_NAME_, std::vector<std::string>, labels)

OC_ALGEBRA_GENERATE(edit_labels, OC_ALGEBRA_EDIT_LABELS_MEMBERS)


// Values

#define OC_ALGEBRA_GRAPH_NODE_MEMBERS(_KLASS_NAME_, F)          \
  F(_KLASS_NAME_, std::string, variable)                        \
  F(_KLASS_NAME_, std::vector<std::string>, labels)             \
  F(_KLASS_NAME_, map_csp, properties)

OC_ALGEBRA_GENERATE(graph_node, OC_ALGEBRA_GRAPH_NODE_MEMBERS)

#define OC_ALGEBRA_GRAPH_EDGE_MEMBERS(_KLASS_NAME_, F)          \
  F(_KLASS_NAME_, std::string, variable)                        \
  F(_KLASS_NAME_, graph_node_csp, source)                       \
  F(_KLASS_NAME_, graph_node_csp, destination)                  \
  F(_KLASS_NAME_, edge_directivity, directivity)                \
  F(_KLASS_NAME_, std::vector<std::string>, labels)             \
  F(_KLASS_NAME_, map_csp, properties)

OC_ALGEBRA_GENERATE(graph_edge, OC_ALGEBRA_GRAPH_EDGE_MEMBERS)

#define OC_ALGEBRA_VALUE_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, gqlite::value, value)

OC_ALGEBRA_GENERATE(value, OC_ALGEBRA_VALUE_MEMBERS)


#define OC_ALGEBRA_MAP_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::unordered_map<GQLITE_LIST(std::string, node_csp)>, map)

OC_ALGEBRA_GENERATE(map, OC_ALGEBRA_MAP_MEMBERS)

#define OC_ALGEBRA_ARRAY_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::vector<node_csp>, array)

OC_ALGEBRA_GENERATE(array, OC_ALGEBRA_ARRAY_MEMBERS)


// Expressions

#define OC_ALGEBRA_NAMED_EXPRESSION(_KLASS_NAME_, F)      \
  F(_KLASS_NAME_, std::string, name)                      \
  F(_KLASS_NAME_, node_csp, expression)

OC_ALGEBRA_GENERATE(named_expression, OC_ALGEBRA_NAMED_EXPRESSION)

#define OC_ALGEBRA_VARIABLE_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::string, identifier)

OC_ALGEBRA_GENERATE(variable, OC_ALGEBRA_VARIABLE_MEMBERS)

#define OC_ALGEBRA_MEMBER_ACCESS_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::string, left)                      \
  F(_KLASS_NAME_, std::vector<std::string>, path)

OC_ALGEBRA_GENERATE(member_access, OC_ALGEBRA_MEMBER_ACCESS_MEMBERS)

#define OC_ALGEBRA_HAS_LABELS_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::string, left)                   \
  F(_KLASS_NAME_, std::vector<std::string>, labels)

OC_ALGEBRA_GENERATE(has_labels, OC_ALGEBRA_HAS_LABELS_MEMBERS)

#define OC_ALGEBRA_FUNCTION_CALL_EXPRESSION(_KLASS_NAME_, F)  \
  F(_KLASS_NAME_, std::string, name)                          \
  F(_KLASS_NAME_, std::vector<node_csp>, arguments)

OC_ALGEBRA_GENERATE(function_call, OC_ALGEBRA_FUNCTION_CALL_EXPRESSION)

#define OC_ALGEBRA_BINARY_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, node_csp, left)                  \
  F(_KLASS_NAME_, node_csp, right)

OC_ALGEBRA_GENERATE(logical_and, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(logical_or, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_equal, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_different, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_inferior, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_superior, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_inferior_equal, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_superior_equal, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_in, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_not_in, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(addition, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(substraction, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(multiplication, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(division, OC_ALGEBRA_BINARY_MEMBERS)

#define OC_ALGEBRA_UNARY_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, node_csp, value)

OC_ALGEBRA_GENERATE(logical_negation, OC_ALGEBRA_UNARY_MEMBERS)
OC_ALGEBRA_GENERATE(negation, OC_ALGEBRA_UNARY_MEMBERS)
OC_ALGEBRA_GENERATE(is_null, OC_ALGEBRA_UNARY_MEMBERS)
OC_ALGEBRA_GENERATE(is_not_null, OC_ALGEBRA_UNARY_MEMBERS)

#undef OC_ALGEBRA_GENERATE
  protected:
    void accept(node_csp _node, const _TArgs_&... _arguments)
    {
      accept(_node.get(), _arguments...);
    }
    void accept(const node* _node, const _TArgs_&... _arguments)
    {
      if(_node)
      {
        ParametersTuple pt = std::make_tuple(_arguments...);
        _node->accept(this, nullptr, &pt);
      }
    }
  };
}

#endif
//END oc/algebra/abstract_node_visitor.h
//BEGIN oc/algebra/default_node_visitor.h






#define __OC_ALGEBRA_NODE_VISITOR_OVERLOAD(_NAME_, _I_)             \
  Return visit(_NAME_ ## CSP _node, const Parameter& _parameter);

#define OC_ALGEBRA_NODE_VISITOR_OVERLOAD(...)                       \
  GQLITE_FOREACH(__OC_ALGEBRA_NODE_VISITOR_OVERLOAD, __VA_ARGS__)

namespace gqlite::oc::algebra
{
  /**
   * @ingroup GQL_Algebra
   * Base abstract_node_visitor with empty implementation
   */
  template<typename _TR_, typename... _TArgs_>
  class default_node_visitor : public abstract_node_visitor<_TR_, _TArgs_...>
  {
  protected:
    virtual _TR_ visit_default(node_csp _node, const _TArgs_&...) = 0;
#define OC_ALGEBRA_GENERATE(_KLASS_NAME_, _MEMBER_DEF_)                               \
        _TR_ visit(_KLASS_NAME_ ## _csp _node, const _TArgs_&... _args) override       \
        {                                                                             \
          return visit_default(_node, _args...);                                      \
        }
// Queries

#define OC_ALGEBRA_CREATE_NODES_MEMBERS(_KLASS_NAME_, F)                          \
  F(_KLASS_NAME_, std::vector<alternative<GQLITE_LIST(graph_node, graph_edge)>>, patterns)

OC_ALGEBRA_GENERATE(create, OC_ALGEBRA_CREATE_NODES_MEMBERS)

#define OC_ALGEBRA_MATCH_NODES_MEMBERS(_KLASS_NAME_, F)                                     \
  F(_KLASS_NAME_, std::vector<alternative<GQLITE_LIST(graph_node, graph_edge)>>, patterns)  \
  F(_KLASS_NAME_, node_csp, where)

OC_ALGEBRA_GENERATE(match, OC_ALGEBRA_MATCH_NODES_MEMBERS)

// Statements

#define OC_ALGEBRA_NODES_LIST_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::vector<node_csp>, nodes)

OC_ALGEBRA_GENERATE(statements, OC_ALGEBRA_NODES_LIST_MEMBERS)

#define OC_ALGEBRA_RETURN_MEMBERS(_KLASS_NAME_, F)                \
  F(_KLASS_NAME_, bool, all)                                      \
  F(_KLASS_NAME_, std::vector<named_expression_csp>, expressions)

OC_ALGEBRA_GENERATE(return_statement, OC_ALGEBRA_RETURN_MEMBERS)

#define OC_ALGEBRA_WITH_MEMBERS(_KLASS_NAME_, F)                  \
  F(_KLASS_NAME_, bool, all)                                      \
  F(_KLASS_NAME_, std::vector<named_expression_csp>, expressions)

OC_ALGEBRA_GENERATE(with, OC_ALGEBRA_WITH_MEMBERS)

#define OC_ALGEBRA_DELETE_MEMBERS(_KLASS_NAME_, F)  \
  F(_KLASS_NAME_, bool, detach)                     \
  F(_KLASS_NAME_, std::vector<node_csp>, expressions)

OC_ALGEBRA_GENERATE(delete_statement, OC_ALGEBRA_DELETE_MEMBERS)

OC_ALGEBRA_GENERATE(set, OC_ALGEBRA_NODES_LIST_MEMBERS)
OC_ALGEBRA_GENERATE(remove, OC_ALGEBRA_NODES_LIST_MEMBERS)

// Set/remove Expressions

#define OC_ALGEBRA_SET_ADD_VALUE_MEMBERS(_KLASS_NAME_, F)                 \
  F(_KLASS_NAME_, member_access_csp, target)                              \
  F(_KLASS_NAME_, node_csp, expression)

OC_ALGEBRA_GENERATE(set_property, OC_ALGEBRA_SET_ADD_VALUE_MEMBERS)


#define OC_ALGEBRA_SET_EQUAL_MEMBERS(_KLASS_NAME_, F)                     \
  F(_KLASS_NAME_, member_access_csp, target)                              \
  F(_KLASS_NAME_, nocde_csp, expression)

OC_ALGEBRA_GENERATE(add_property, OC_ALGEBRA_SET_ADD_VALUE_MEMBERS)

#define OC_ALGEBRA_REMOVE_PROPERTY_MEMBERS(_KLASS_NAME_, F)                   \
  F(_KLASS_NAME_, member_access_csp, target)

OC_ALGEBRA_GENERATE(remove_property, OC_ALGEBRA_REMOVE_PROPERTY_MEMBERS)

#define OC_ALGEBRA_EDIT_LABELS_MEMBERS(_KLASS_NAME_, F)                   \
  F(_KLASS_NAME_, std::string, target)                                    \
  F(_KLASS_NAME_, std::vector<std::string>, labels)

OC_ALGEBRA_GENERATE(edit_labels, OC_ALGEBRA_EDIT_LABELS_MEMBERS)


// Values

#define OC_ALGEBRA_GRAPH_NODE_MEMBERS(_KLASS_NAME_, F)          \
  F(_KLASS_NAME_, std::string, variable)                        \
  F(_KLASS_NAME_, std::vector<std::string>, labels)             \
  F(_KLASS_NAME_, map_csp, properties)

OC_ALGEBRA_GENERATE(graph_node, OC_ALGEBRA_GRAPH_NODE_MEMBERS)

#define OC_ALGEBRA_GRAPH_EDGE_MEMBERS(_KLASS_NAME_, F)          \
  F(_KLASS_NAME_, std::string, variable)                        \
  F(_KLASS_NAME_, graph_node_csp, source)                       \
  F(_KLASS_NAME_, graph_node_csp, destination)                  \
  F(_KLASS_NAME_, edge_directivity, directivity)                \
  F(_KLASS_NAME_, std::vector<std::string>, labels)             \
  F(_KLASS_NAME_, map_csp, properties)

OC_ALGEBRA_GENERATE(graph_edge, OC_ALGEBRA_GRAPH_EDGE_MEMBERS)

#define OC_ALGEBRA_VALUE_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, gqlite::value, value)

OC_ALGEBRA_GENERATE(value, OC_ALGEBRA_VALUE_MEMBERS)


#define OC_ALGEBRA_MAP_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::unordered_map<GQLITE_LIST(std::string, node_csp)>, map)

OC_ALGEBRA_GENERATE(map, OC_ALGEBRA_MAP_MEMBERS)

#define OC_ALGEBRA_ARRAY_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::vector<node_csp>, array)

OC_ALGEBRA_GENERATE(array, OC_ALGEBRA_ARRAY_MEMBERS)


// Expressions

#define OC_ALGEBRA_NAMED_EXPRESSION(_KLASS_NAME_, F)      \
  F(_KLASS_NAME_, std::string, name)                      \
  F(_KLASS_NAME_, node_csp, expression)

OC_ALGEBRA_GENERATE(named_expression, OC_ALGEBRA_NAMED_EXPRESSION)

#define OC_ALGEBRA_VARIABLE_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::string, identifier)

OC_ALGEBRA_GENERATE(variable, OC_ALGEBRA_VARIABLE_MEMBERS)

#define OC_ALGEBRA_MEMBER_ACCESS_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::string, left)                      \
  F(_KLASS_NAME_, std::vector<std::string>, path)

OC_ALGEBRA_GENERATE(member_access, OC_ALGEBRA_MEMBER_ACCESS_MEMBERS)

#define OC_ALGEBRA_HAS_LABELS_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::string, left)                   \
  F(_KLASS_NAME_, std::vector<std::string>, labels)

OC_ALGEBRA_GENERATE(has_labels, OC_ALGEBRA_HAS_LABELS_MEMBERS)

#define OC_ALGEBRA_FUNCTION_CALL_EXPRESSION(_KLASS_NAME_, F)  \
  F(_KLASS_NAME_, std::string, name)                          \
  F(_KLASS_NAME_, std::vector<node_csp>, arguments)

OC_ALGEBRA_GENERATE(function_call, OC_ALGEBRA_FUNCTION_CALL_EXPRESSION)

#define OC_ALGEBRA_BINARY_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, node_csp, left)                  \
  F(_KLASS_NAME_, node_csp, right)

OC_ALGEBRA_GENERATE(logical_and, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(logical_or, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_equal, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_different, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_inferior, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_superior, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_inferior_equal, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_superior_equal, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_in, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_not_in, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(addition, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(substraction, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(multiplication, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(division, OC_ALGEBRA_BINARY_MEMBERS)

#define OC_ALGEBRA_UNARY_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, node_csp, value)

OC_ALGEBRA_GENERATE(logical_negation, OC_ALGEBRA_UNARY_MEMBERS)
OC_ALGEBRA_GENERATE(negation, OC_ALGEBRA_UNARY_MEMBERS)
OC_ALGEBRA_GENERATE(is_null, OC_ALGEBRA_UNARY_MEMBERS)
OC_ALGEBRA_GENERATE(is_not_null, OC_ALGEBRA_UNARY_MEMBERS)

#undef OC_ALGEBRA_GENERATE
  };
}

//END oc/algebra/default_node_visitor.h
//BEGIN oc/algebra/visitors/print.h


namespace gqlite::oc::algebra::visitors
{
  namespace details
  {
    template<typename _T_, typename = void>
    struct print_helper;
  }
  class print : public abstract_node_visitor<void>
  {
    template<typename _T_, typename = void>
    friend class details::print_helper;
  public:
    print();
    ~print();
#define OC_ALGEBRA_GENERATE(_KLASS_NAME_, _MEMBER_DEF_) \
  virtual void visit(_KLASS_NAME_ ## _csp _node) override;

#undef OC_ALGEBRA_GENERATE
  private:
    struct data;
    data* const d;
  };
}
//END oc/algebra/visitors/print.h
//BEGIN oc/algebra/visitors/stringify.h





namespace gqlite::oc::algebra::visitors
{
  struct stringify : public gqlite::oc::algebra::default_node_visitor<std::string>
  {
    std::string visit_default(algebra::node_csp _node) override
    {
      throw gqlite::exception("Unimplemented statement node {} in stringify", oc::algebra::node_type_name(_node->get_type()));
    }
    std::string visit(algebra::member_access_csp _var) override
    {
      return format_string("{}.{}", _var->get_left(), string::join(_var->get_path(), ","));
    }
    std::string visit(algebra::variable_csp _var) override
    {
      return _var->get_identifier();
    }
    std::string visit(algebra::function_call_csp _var) override
    {
      std::string args;
      for(algebra::node_csp node : _var->get_arguments())
      {
        if(not args.empty()) args += ",";
        args += start(node);
      }
      return format_string("{}({})", _var->get_name(), args);
    }
  };
}
//END oc/algebra/visitors/stringify.h
//BEGIN oc/parser.h
#include <tuple>



namespace gqlite::oc
{
  class lexer;
  class parser
  {
  public:
    parser(lexer* _lexer);
    ~parser();
    algebra::node_csp parse();
  private:
    struct data;
    data* const d;
  };
} // namespace gqlite::oc
//END oc/parser.h
//BEGIN backend.h




namespace gqlite
{
  class backend
  {
  public:
    virtual ~backend();
  public:
    virtual value execute_oc_query(oc::algebra::node_csp _node, const value_map& _bindings) = 0;
    virtual value get_debug_stats() const = 0;
  };
}
//END backend.h
//BEGIN backends/sqlite_queries.h
//To update this file, run 'queries_gen.rb'
#include <sstream>

namespace gqlite::backends::sqlite_queries
{
  std::string edge_add_properties(const std::string& _graph_name)
  {
    std::stringstream stream;
    
stream << "UPDATE gqlite_";

stream << ( _graph_name );

stream << "_edges SET properties=json_set(properties, ?002, json_patch(json_extract(properties, ?002) , ?003) ) WHERE id=?001\n"
"";

    return stream.str();
  }
  std::string edge_count_by_node(const std::string& _graph_name)
  {
    std::stringstream stream;
    
stream << "SELECT count(*) FROM gqlite_";

stream << ( _graph_name );

stream << "_edges WHERE left=?001 or right=?001";

    return stream.str();
  }
  std::string edge_create(const std::string& _graph_name)
  {
    std::stringstream stream;
    
stream << "INSERT INTO gqlite_";

stream << ( _graph_name );

stream << "_edges (label, properties, left, right) VALUES (?001, ?002, ?003, ?004)";

    return stream.str();
  }
  std::string edge_delete(const std::string& _graph_name)
  {
    std::stringstream stream;
    
stream << "DELETE FROM gqlite_";

stream << ( _graph_name );

stream << "_edges WHERE id=?001";

    return stream.str();
  }
  std::string edge_delete_by_node(const std::string& _graph_name)
  {
    std::stringstream stream;
    
stream << "DELETE FROM gqlite_";

stream << ( _graph_name );

stream << "_edges WHERE left=?001 or right=?001";

    return stream.str();
  }
  std::string edge_get_label_properties(const std::string& _graph_name)
  {
    std::stringstream stream;
    
stream << "SELECT label, properties FROM gqlite_";

stream << ( _graph_name );

stream << "_edges WHERE id = ?001";

    return stream.str();
  }
  std::string edge_set_property(const std::string& _graph_name)
  {
    std::stringstream stream;
    
stream << "UPDATE gqlite_";

stream << ( _graph_name );

stream << "_edges SET properties=json_set(properties, ?002, ?003) WHERE id=?001";

    return stream.str();
  }
  std::string edge_remove_property(const std::string& _graph_name)
  {
    std::stringstream stream;
    
stream << "UPDATE gqlite_";

stream << ( _graph_name );

stream << "_edges SET properties=json_remove(properties, ?002) WHERE id=?001";

    return stream.str();
  }
  std::string get_debug_stats(const std::string& _graph_name)
  {
    std::stringstream stream;
    
stream << "SELECT COUNT(*) FROM gqlite_";

stream << ( _graph_name );

stream << "_nodes\n"
"UNION ALL SELECT COUNT(*) FROM gqlite_";

stream << ( _graph_name );

stream << "_edges\n"
"UNION ALL SELECT COUNT(*) FROM gqlite_";

stream << ( _graph_name );

stream << "_labels\n"
"UNION ALL SELECT COUNT(j.value) FROM gqlite_";

stream << ( _graph_name );

stream << "_nodes t, json_each(properties) j\n"
"UNION ALL SELECT COUNT(j.value) FROM gqlite_";

stream << ( _graph_name );

stream << "_edges t, json_each(properties) j\n"
"UNION ALL SELECT COUNT(*) FROM gqlite_labels\n"
"UNION ALL SELECT COUNT(DISTINCT label) FROM gqlite_";

stream << ( _graph_name );

stream << "_labels";

    return stream.str();
  }
  std::string graph_create(const std::string& _graph_name)
  {
    std::stringstream stream;
    
stream << "CREATE TABLE gqlite_";

stream << ( _graph_name );

stream << "_nodes(id INTEGER PRIMARY KEY AUTOINCREMENT, properties TEXT NOT NULL);\n"
"CREATE TABLE gqlite_";

stream << ( _graph_name );

stream << "_edges(id INTEGER PRIMARY KEY AUTOINCREMENT,\n"
"       label INTEGER,\n"
"       properties TEXT NOT NULL,\n"
"       left INTEGER,\n"
"       right INTEGER,\n"
"       FOREIGN KEY(label) REFERENCES gqlite_labels(id),\n"
"       FOREIGN KEY(left) REFERENCES gqlite_";

stream << ( _graph_name );

stream << "_nodes(id), FOREIGN KEY(right) REFERENCES gqlite_";

stream << ( _graph_name );

stream << "_nodes(id));\n"
"CREATE TABLE gqlite_";

stream << ( _graph_name );

stream << "_labels(label INTEGER, node_id INTEGER,\n"
"        FOREIGN KEY(label) REFERENCES gqlite_labels(id), FOREIGN KEY(node_id) REFERENCES gqlite_";

stream << ( _graph_name );

stream << "_nodes(id));\n"
"CREATE VIEW gqlite_";

stream << ( _graph_name );

stream << "_edges_undirected (id, label, properties, left, right) AS\n"
"        SELECT id, label, properties, left, right FROM gqlite_";

stream << ( _graph_name );

stream << "_edges\n"
"        UNION SELECT id, label, properties, right, left FROM gqlite_";

stream << ( _graph_name );

stream << "_edges;";

    return stream.str();
  }
  std::string graph_has(const std::string& _graph_name)
  {
    std::stringstream stream;
    
stream << "SELECT count(*) FROM sqlite_master\n"
"  WHERE type='table'\n"
"    AND (name='gqlite_";

stream << ( _graph_name );

stream << "_nodes' or name='gqlite_";

stream << ( _graph_name );

stream << "_edges' or name='gqlite_";

stream << ( _graph_name );

stream << "_labels')";

    return stream.str();
  }
  std::string label_create_table()
  {
    std::stringstream stream;
    
stream << "CREATE TABLE gqlite_labels(id INTEGER PRIMARY KEY AUTOINCREMENT, label TEXT NOT NULL UNIQUE);\n"
"INSERT INTO gqlite_labels(id, label) VALUES (0, \"\")";

    return stream.str();
  }
  std::string label_get_from_id()
  {
    std::stringstream stream;
    
stream << "SELECT label FROM gqlite_labels WHERE id = ?001\n"
"";

    return stream.str();
  }
  std::string label_get_from_name()
  {
    std::stringstream stream;
    
stream << "SELECT id FROM gqlite_labels WHERE label = ?001\n"
"";

    return stream.str();
  }
  std::string label_insert()
  {
    std::stringstream stream;
    
stream << "INSERT INTO gqlite_labels(label) VALUES (?001)";

    return stream.str();
  }
  std::string node_add_label(const std::string& _graph_name)
  {
    std::stringstream stream;
    
stream << "INSERT INTO gqlite_";

stream << ( _graph_name );

stream << "_labels   (label, node_id) VALUES (?001, ?002)";

    return stream.str();
  }
  std::string node_add_properties(const std::string& _graph_name)
  {
    std::stringstream stream;
    
stream << "UPDATE gqlite_";

stream << ( _graph_name );

stream << "_nodes SET properties=json_set(properties, ?002, json_patch(json_extract(properties, ?002) , ?003) ) WHERE id=?001\n"
"";

    return stream.str();
  }
  std::string node_create(const std::string& _graph_name)
  {
    std::stringstream stream;
    
stream << "INSERT INTO gqlite_";

stream << ( _graph_name );

stream << "_nodes (properties) VALUES (?001)";

    return stream.str();
  }
  std::string node_delete(const std::string& _graph_name)
  {
    std::stringstream stream;
    
stream << "DELETE FROM gqlite_";

stream << ( _graph_name );

stream << "_labels WHERE node_id=?001;\n"
"DELETE FROM gqlite_";

stream << ( _graph_name );

stream << "_nodes WHERE id=?001";

    return stream.str();
  }
  std::string node_get_labels(const std::string& _graph_name)
  {
    std::stringstream stream;
    
stream << "SELECT label FROM gqlite_";

stream << ( _graph_name );

stream << "_labels WHERE node_id = ?001";

    return stream.str();
  }
  std::string node_get_properties(const std::string& _graph_name)
  {
    std::stringstream stream;
    
stream << "SELECT properties FROM gqlite_";

stream << ( _graph_name );

stream << "_nodes WHERE id = ?001";

    return stream.str();
  }
  std::string node_remove_label(const std::string& _graph_name)
  {
    std::stringstream stream;
    
stream << "DELETE FROM gqlite_";

stream << ( _graph_name );

stream << "_labels WHERE label = ?001 AND node_id = ?002";

    return stream.str();
  }
  std::string node_remove_property(const std::string& _graph_name)
  {
    std::stringstream stream;
    
stream << "UPDATE gqlite_";

stream << ( _graph_name );

stream << "_nodes SET properties=json_remove(properties, ?002) WHERE id=?001";

    return stream.str();
  }
  std::string node_set_property(const std::string& _graph_name)
  {
    std::stringstream stream;
    
stream << "UPDATE gqlite_";

stream << ( _graph_name );

stream << "_nodes SET properties=json_set(properties, ?002, ?003) WHERE id=?001";

    return stream.str();
  }
  std::string table_has()
  {
    std::stringstream stream;
    
stream << "SELECT count(*) FROM sqlite_master WHERE type='table' AND (name=?001)";

    return stream.str();
  }
}
//END backends/sqlite_queries.h
//BEGIN backends/functions.h
#include <gqlite.h>

namespace gqlite::backends::functions
{
  value call(const std::string& _name, const std::vector<value>& _arguments);
}
//END backends/functions.h
//BEGIN backends/sqlite.h


namespace gqlite::backends
{
  class sqlite : public backend
  {
  public:
    sqlite(void* _db);
    virtual ~sqlite();
    static sqlite* from_file(const std::string& _filename);
  public:
    value execute_oc_query(oc::algebra::node_csp _node, const value_map& _variant) override;
    value get_debug_stats() const override;
  private:
    struct data;
    data* const d;
  };
}
//END backends/sqlite.h
//BEGIN backend.cpp


using namespace gqlite;

backend::~backend()
{}
//END backend.cpp
//BEGIN connection.cpp


#include <sstream>





using namespace gqlite;

struct connection::data
{
  ~data()
  {
    delete backend_;
  }
  backend* backend_ = nullptr;
};

connection::connection()
{}

connection::connection(const connection& _rhs) : d(_rhs.d)
{
}

connection::connection(backend* _backend) : d(new data{_backend})
{
}

connection& connection::operator=(const connection& _rhs)
{
  d = _rhs.d;
  return *this;
}

connection::~connection()
{
}

connection connection::create_from_sqlite(void* _sqlite_connection, const gqlite::value&)
{
  return connection(new backends::sqlite(_sqlite_connection));
}

connection connection::create_from_sqlite_file(const std::string& _filename, const gqlite::value&)
{
  return connection(backends::sqlite::from_file(_filename));
}

value connection::execute_oc_query(const std::string& _string, const value_map& _bindings)
{
  std::stringstream ss(_string);
  oc::lexer l(&ss);
  oc::parser parser(&l);
  oc::algebra::node_csp node = parser.parse();
  return d->backend_->execute_oc_query(node, _bindings);
}

gqlite::value connection::get_debug_stats() const
{
  return d->backend_->get_debug_stats();
}
//END connection.cpp
//BEGIN exception.cpp
#include <gqlite.h>

using namespace gqlite;

exception::exception(const exception& _rhs)
{
  operator=(_rhs);
}

exception& exception::operator=(const exception& _rhs)
{
  if(_rhs.m_error.empty())
  {
    m_c_error = _rhs.m_c_error;
  } else {
    m_error = _rhs.m_error;
    m_c_error = m_error.c_str();
  }
  return *this;
}
//END exception.cpp
//BEGIN gqlite-c.cpp






#define BEGIN_CHECK                       \
  if(not _context->error_message.empty()) \
  {                                       \
    gqlite_error("Error message '{}' was not cleared from context!", _context->error_message);  \
  }                                       \
  try                                     \
  {
#define END_CHECK                                 \
  } catch(const gqlite::exception& _ex)           \
  {                                               \
    _context->error_message = _ex.what();         \
    if(_context->error_message.empty())           \
    {                                             \
      _context->error_message = "Unknown error";  \
    }                                             \
  }

extern "C"
{

  struct gqlite_api_context
  {
    std::string error_message;
    ~gqlite_api_context()
    {
      release_memory();
    }
    char* str = nullptr;
    char* set_string(const std::string& _string)
    {
      release_memory();
      str = new char[_string.size() + 1];
      std::copy(_string.begin(), _string.end() + 1, str);
      return str;
    }
    void release_memory()
    {
      delete[] str;
      str = nullptr;
    }
  };

  gqlite_api_context_t gqlite_api_context_create()
  {
    return new gqlite_api_context;
  }
  void gqlite_api_context_destroy(gqlite_api_context_t _context)
  {
    delete _context;
  }
  const char* gqlite_api_context_get_message(gqlite_api_context_t _context)
  {
    return _context->error_message.c_str();
  }
  bool gqlite_api_context_has_error(gqlite_api_context_t _context)
  {
    return not _context->error_message.empty();
  }
  void gqlite_api_context_clear_error(gqlite_api_context_t _context)
  {
    _context->error_message.clear();
  }

  struct gqlite_connection
  {
    gqlite::connection db;
  };
  struct gqlite_value
  {
    gqlite::value value;
  };

  gqlite_connection_t gqlite_connection_create_from_sqlite(gqlite_api_context_t _context, void* _handle, gqlite_value_t _options)
  {
    BEGIN_CHECK
    gqlite::value options;
    if(_options) options = _options->value;
    return new gqlite_connection{gqlite::connection::create_from_sqlite(_handle, options)};
    END_CHECK
    return nullptr;
  }
  gqlite_connection_t gqlite_connection_create_from_sqlite_file(gqlite_api_context_t _context, const char* _filename, gqlite_value_t _options)
  {
    BEGIN_CHECK
    gqlite::value options;
    if(_options) options = _options->value;
    return new gqlite_connection{gqlite::connection::create_from_sqlite_file(_filename, options)};
    END_CHECK
    return nullptr;
  }

  void gqlite_connection_destroy(gqlite_api_context_t _context, gqlite_connection_t _connection)
  {
    BEGIN_CHECK
    delete _connection;
    END_CHECK
  }

  gqlite_value_t gqlite_connection_oc_query(gqlite_api_context_t _context, gqlite_connection_t _connection, const char* _query, gqlite_value_t _bindings)
  {
    BEGIN_CHECK
    gqlite::value_map bindings;
    if(_bindings)
    {
      bindings = _bindings->value.to_map();
    }
    return new gqlite_value{ _connection->db.execute_oc_query(_query, bindings) };
    END_CHECK
    return nullptr;
  }

  gqlite_value_t gqlite_value_create(gqlite_api_context_t)
  {
    return new gqlite_value;
  }
  void gqlite_value_destroy(gqlite_api_context_t, gqlite_value_t _value)
  {
    delete _value;
  }
  const char* gqlite_value_to_json(gqlite_api_context_t _context, gqlite_value_t _value)
  {
    BEGIN_CHECK
    return _context->set_string(_value->value.to_json());
    END_CHECK
    return nullptr;
  }
  bool gqlite_value_is_valid(gqlite_api_context_t, gqlite_value_t _value)
  {
    return _value and _value->value.get_type() != gqlite::value_type::invalid;
  }

  gqlite_value_t gqlite_private_test_stats(gqlite_connection_t _connection)
  {
    return new gqlite_value { _connection->db.get_debug_stats() };
  }

}
//END gqlite-c.cpp
//BEGIN value.cpp


#include <sstream>
#include <variant>



using namespace gqlite;

const char* std::to_string(value_type _type)
{
  switch(_type)
  {
    using enum value_type;
    case invalid: return "invalid";
    case boolean: return "boolean";
    case integer: return "integer";
    case number: return "number";
    case string: return "string";
    case map: return "map";
    case vector: return "vector";
  }
  return "unknown value type";
}

struct value::data
{
  value_type type;
  std::variant<bool, int, double, std::string, value_map, value_vector> value_container;

  void to_json(std::stringstream& _stream);
};

namespace {
  /**
   * @internal
   * class use to read json files
   */
  struct json_reader
  {
    std::stringstream stream;
    int last_char;
    /// @brief get the next char
    void fetch_next_char()
    {
      while(std::isspace(last_char = stream.get()))
      {
        if(last_char == std::stringstream::traits_type::eof())
        {
          return;
        } else if(stream.fail())
        {
          throw gqlite::exception("Failure in getting json data from stream");
        }
      }
    };
    /// @brief start parsing
    /// @throw gqlite::exception
    /// @return the parsed value
    gqlite::value start()
    {
      fetch_next_char();
      return read_value();
    }
    /// @brief read a string
    /// @return the string without quotation
    std::string read_string()
    {
      is_char('"');
      fetch_next_char();
      std::string string;
      bool keep_nc = false; // this is used to indicate if the last character was a '\' or not
      while(keep_nc or last_char != '"')
      {
        if(not keep_nc and last_char == '\\')
        {
          keep_nc = true; // make sure to ignore the next character if it is a '"' or to properly handle a '\n'
        } else {
          if(keep_nc)
          {
            keep_nc = false;
            if(last_char == 'n')
            {
              string += '\n';
            } else {
              string += char(last_char);
            }
          } else {
            string += char(last_char);
          }
        }
        if(not stream.good())
        {
          throw gqlite::exception("Unifinished string: {}", string);
        }
        fetch_next_char();
      }
      fetch_next_char(); // eat the '"'
      return string;
    }
    /// @brief throw an exception if last_char different from @param _c 
    void is_char(int _c)
    {
      if(last_char != _c)
      {
        throw gqlite::exception("Expected '{}' but got '{}'", char(_c), char(last_char));
      }
    }
    /// @brief read the next value
    /// @return and return it
    gqlite::value read_value()
    {
      switch (last_char)
      {
      case '{':
      {
        // Parse object
        value_map map;
        fetch_next_char();
        while(last_char != '}')
        {
          std::string str = read_string();
          is_char(':');
          fetch_next_char();
          gqlite::value val = read_value();
          map[str] = val;
          if(last_char == ',')
          {
            fetch_next_char();
          } else {
            break;
          }
        }
        is_char('}');
        fetch_next_char();
        return map;
      }
      case '[':
      {
        // Parse array
        value_vector vec;
        fetch_next_char();
        while(last_char != ']')
        {
          gqlite::value val = read_value();
          vec.push_back(val);
          if(last_char == ',')
          {
            fetch_next_char();
          } else {
            break;
          }
        }
        is_char(']');
        fetch_next_char();
        return vec;
      }
      case '"':
        return read_string();
      case 't':
      {
        // Parse true
        fetch_next_char(); is_char('r');
        fetch_next_char(); is_char('u');
        fetch_next_char(); is_char('e');
        fetch_next_char();
        return true;
      }
      case 'f':
      {
        // Parse false
        fetch_next_char(); is_char('a');
        fetch_next_char(); is_char('l');
        fetch_next_char(); is_char('s');
        fetch_next_char(); is_char('e');
        fetch_next_char();
        return false;
      }
      case 'n':
      {
        // Parse null
        fetch_next_char(); is_char('u');
        fetch_next_char(); is_char('l');
        fetch_next_char(); is_char('l');
        fetch_next_char();
        return value();
      }
      // Parse number
      case '-':
      case '0':
      case '1':
      case '2':
      case '3':
      case '4':
      case '5':
      case '6':
      case '7':
      case '8':
      case '9':
      {
        std::string number;
        number += char(last_char);
        fetch_next_char();
        bool is_integer = true;
        while((last_char >= '0' and last_char <= '9') or last_char == '.' or last_char == 'e' or last_char == '+' or last_char == '-')
        {
          is_integer = is_integer and (last_char != '.' and last_char != 'e');
          number += char(last_char);
          fetch_next_char();
        }
        if(is_integer)
        {
          return std::stoi(number);
        } else {
          return std::stod(number);
        }
      }
      default:
        throw gqlite::exception("Unexpected {}", char(last_char));
      }
    }
  };
}

void value::data::to_json(std::stringstream& _stream)
{
  bool first = true;
  switch(type)
  {
  case value_type::integer:
    _stream << std::get<int>(value_container);
    break;
  case value_type::number:
    _stream << std::get<double>(value_container);
    break;
  case value_type::string:
    _stream << '"' << std::get<std::string>(value_container) << '"';
    break;
  case value_type::map:
    _stream << '{';
    for(auto const& [k, v] : std::get<value_map>(value_container))
    {
      if(first)
      {
        first = false;
      } else {
        _stream << ',';
      }
      _stream << '"' << k << "\":";
      v.d->to_json(_stream);
    }
    _stream << '}';
    break;
  case value_type::vector:
    _stream << '[';
    for(const value& v : std::get<value_vector>(value_container))
    {
      if(first)
      {
        first = false;
      } else {
        _stream << ',';
      }
      v.d->to_json(_stream);
    }
    _stream << ']';
    break;
  case value_type::invalid:
    _stream << "null";
  }
}

value::value() : d(new data{value_type::invalid})
{}

value::value(const value& _rhs) : d(_rhs.d)
{
}

value& value::operator=(const value& _rhs)
{
  d = _rhs.d;
  return *this;
}

value::~value()
{}

value::value(int _v) : d(new data{value_type::integer, _v})
{}

value::value(double _v) : d(new data{value_type::number, _v})
{}

value::value(const std::string& _v) : d(new data{value_type::string, _v})
{}

value::value(const value_map& _v) : d(new data{value_type::map, _v})
{}

value::value(const value_vector& _v) : d(new data{value_type::vector, _v})
{}

value_type value::get_type() const
{
  return d->type;
}

bool value::operator==(const value& _rhs) const
{
  return d->type == _rhs.d->type and d->value_container == _rhs.d->value_container;
}

bool value::to_bool() const
{
  switch(d->type)
  {
    case value_type::boolean:
      return std::get<bool>(d->value_container);
    case value_type::integer:
      return std::get<int>(d->value_container) != 0;
    case value_type::number:
      return std::get<double>(d->value_container) != 0.0;
    default:
      throw exception(format_string("Value is not a bool, it is {}", d->type));
  }
}

int value::to_integer() const
{
  switch(d->type)
  {
    case value_type::integer:
      return std::get<int>(d->value_container);
    case value_type::number:
      return std::get<double>(d->value_container);
    default:
      throw exception(format_string("Value is not a number, it is {}", d->type));
  }
}

double value::to_double() const
{
  switch(d->type)
  {
    case value_type::integer:
      return std::get<int>(d->value_container);
    case value_type::number:
      return std::get<double>(d->value_container);
    default:
      throw exception(format_string("Value is not a number, it is {}", d->type));
  }
}

std::string value::to_string() const
{
  if(d->type != value_type::string) throw exception(format_string("Value is not a string, it is {}", d->type));
  return std::get<std::string>(d->value_container);
}

value_map value::to_map() const
{
  if(d->type != value_type::map) throw exception(format_string("Value is not a map, it is {}", d->type));
  return std::get<value_map>(d->value_container);
}

value_vector value::to_vector() const
{
  if(d->type != value_type::vector) throw exception(format_string("Value is not a vector, it is {}", d->type));
  return std::get<value_vector>(d->value_container);
}

std::string value::to_json() const
{
  std::stringstream ss;
  d->to_json(ss);
  return ss.str();
}

value value::from_json(const std::string& _json)
{
  json_reader reader{std::stringstream(_json)};
  return reader.start();
}
//END value.cpp
//BEGIN backends/functions.cpp


#include <functional>




namespace gqlite::backends::functions
{
  struct static_data
  {
    static_data();

    static void check_value_is_map(const char* _fname, const value& _value);

    static gqlite::value type(const std::vector<value>& _arguments);
    static gqlite::value labels(const std::vector<value>& _arguments);
    static gqlite::value size(const std::vector<value>& _arguments);
    static gqlite::value keys(const std::vector<value>& _arguments);
    std::unordered_map<std::string, std::function<value(const std::vector<value>&)>> functions;
  };

  void static_data::check_value_is_map(const char* _fname, const value& _value)
  {
    if(_value.get_type() != value_type::map)
    {
      throw exception("'{}' expected a map.", _fname);
    }
  }

  /**
   * @return the type of a relationship, i.e., the label
   */
  value static_data::type(const std::vector<value>& _arguments)
  {
    errors::check_arguments_size("type", _arguments, 1);
    value arg0 = _arguments[0];
    if(arg0.get_type() == value_type::map)
    {
      value_map map = arg0.to_map();
      auto it = map.find("label");
      if(it == map.end())
      {
        return value();
      } else {
        return it->second;
      }
    } else {
      return value();
    }
  }
  /**
   * @return the labels of a node
   */
  value static_data::labels(const std::vector<value>& _arguments)
  {
    errors::check_arguments_size("labels", _arguments, 1);
    value arg0 = _arguments[0];
    if(arg0.get_type() == value_type::map)
    {
      value_map map = arg0.to_map();
      auto it = map.find("labels");
      if(it == map.end())
      {
        return value();
      } else {
        return it->second;
      }
    } else {
      return value();
    }
  }
  /**
   * @return the keys of a map
   */
  value static_data::keys(const std::vector<value>& _arguments)
  {
    errors::check_arguments_size("labels", _arguments, 1);
    value arg0 = _arguments[0];
    if(arg0.get_type() == value_type::map)
    {
      value_map map = arg0.to_map();
      value_vector out;
      for(auto it = map.begin(); it != map.end(); ++it)
      {
        out.push_back(it->first);
      }
      return out;
    } else {
      return value();
    }
  }
  /**
   * @return the keys of a vector
   */
  value static_data::size(const std::vector<value>& _arguments)
  {
    errors::check_arguments_size("labels", _arguments, 1);
    value arg0 = _arguments[0];
    if(arg0.get_type() == value_type::vector)
    {
      return arg0.to_vector();
    } else {
      return value();
    }
  }
  static_data::static_data()
  {
    functions["type"] = &static_data::type;
    functions["labels"] = &static_data::labels;
    functions["size"] = &static_data::size;
    functions["keys"] = &static_data::keys;
  }

  value call(const std::string& _name, const std::vector<value>& _arguments)
  {
    static static_data sd;
    auto it = sd.functions.find(_name);
    if(it == sd.functions.end())
    {
      throw exception("No function {}.", _name);
    } else {
      return it->second(_arguments);
    }
  }
}

//END backends/functions.cpp
//BEGIN backends/sqlite.cpp


#include <list>
#include <map>
#include <sqlite3.h>
#include <variant>









using namespace gqlite::backends;

namespace gqlite::backends
{
  struct sqlite_data
  {
    sqlite3* handle;
    std::unordered_map<int, std::string> id_to_label = {{0, std::string()}};
    std::unordered_map<std::string, int> label_to_id = {{std::string(), 0}};

    void throwLastError(const std::string& _query);
    void graph_create(const std::string& _name);
    bool graph_has(const std::string& _name);
    bool table_has(const std::string& _name);

    value execute_sql(const std::string& _query, const std::map<int, value>& _bindings = {});
    uint64_t last_row_id();
    int id_for_label(const std::string& _string);
    std::string label_for_id(int _id);
  };
}

struct sqlite::data : public sqlite_data
{};

void sqlite_data::throwLastError(const std::string& _query)
{
  throw gqlite::exception("Error {}, while executing {}", sqlite3_errmsg(handle), _query);
}

gqlite::value sqlite_data::execute_sql(const std::string& _query, const std::map<int, value>& _bindings)
{
#if 0
  std::cout << "Executing query: '" << _query << "' with bindings:" << std::endl;
  for(auto const& [k,v] : _bindings)
  {
    std::cout << " [" << k << "] = " << v.to_json() << std::endl;
  }
#endif
  sqlite3_stmt* ps;
  const char* ptr = _query.data();
  const char* ptr_end = _query.data() + _query.size();
  value_vector q_rs;
  while(ptr != ptr_end)
  {
    sqlite3_stmt* ps;
    if(sqlite3_prepare_v2(handle, ptr, ptr_end - ptr, &ps, &ptr) != SQLITE_OK)
    {
      sqlite3_finalize(ps);
      throwLastError(_query);
    }
    for(const auto& [key, value] : _bindings)
    {
      switch(value.get_type())
      {
        case value_type::invalid:
          sqlite3_bind_null(ps, key);
          break;
        case value_type::integer:
          sqlite3_bind_int(ps, key, value.to_integer());
          break;
        case value_type::number:
          sqlite3_bind_double(ps, key, value.to_double());
          break;
        default:
        {
          std::string js;
          if(value.get_type() == value_type::string)
          {
            js = value.to_string();
          } else {
            js = value.to_json();
          }
          char* arr = new char[js.size()];
          std::copy(js.begin(), js.end(), arr);
          sqlite3_bind_text(ps, key, arr, js.size(), [](void* _ptr) { delete[] static_cast<char*>(_ptr); });
          break;
        }
      }
    }
    value_vector rows;
    int code = sqlite3_step(ps);
    while(code == SQLITE_ROW)
    {
      value_vector row;
      for(int i = 0; i < sqlite3_data_count(ps); ++i)
      {
        switch(sqlite3_column_type(ps, i))
        {
          case SQLITE_INTEGER:
            row.push_back(sqlite3_column_int(ps, i));
            break;
          case SQLITE_FLOAT:
            row.push_back(sqlite3_column_double(ps, i));
            break;
          case SQLITE_BLOB:
            sqlite3_finalize(ps);
            throw gqlite::exception("Blobs are not supported.");
          case SQLITE_NULL:
            row.push_back(value());
            break;
          case SQLITE3_TEXT:
            row.push_back(std::string(reinterpret_cast<const char*>(sqlite3_column_text(ps, i))));
            break;
        }
      }
      rows.push_back(row);
      code = sqlite3_step(ps);
    }
    // Finalize the execution of the statement
    sqlite3_finalize(ps);
    switch(code)
    {
    case SQLITE_DONE:
      q_rs.push_back(rows);
      break;
    default:
      throwLastError(_query);
    }
  }
  if(q_rs.size() == 1)
  {
    return q_rs[0];
  } else {
    return q_rs;
  }
}

void sqlite_data::graph_create(const std::string& _name)
{
  execute_sql(sqlite_queries::graph_create(_name));
}

bool sqlite_data::graph_has(const std::string& _name)
{
  value r= execute_sql(sqlite_queries::graph_has(_name));
  value_vector v = r.to_vector();
  errors::check_condition(v.size() == 1, "Should have gotten only one result");
  v = v.begin()->to_vector();
  errors::check_condition(v.size() == 1, "Should have gotten only one result");
  return v.begin()->to_bool();
}

bool sqlite_data::table_has(const std::string& _name)
{
  value r = execute_sql(sqlite_queries::table_has(), {{1, _name}});
  value_vector v = r.to_vector();
  errors::check_condition(v.size() == 1, "Should have gotten only one result for table_has.");
  v = v.begin()->to_vector();
  errors::check_condition(v.size() == 1, "Should have gotten only one column for table_has.");
  return v.begin()->to_bool();
}


uint64_t sqlite_data::last_row_id()
{
  return sqlite3_last_insert_rowid(handle);
}

int sqlite_data::id_for_label(const std::string& _string)
{
  auto it = label_to_id.find(_string);
  if(it == label_to_id.end())
  {
    value r = execute_sql(sqlite_queries::label_get_from_name(), {{1, _string}});
    value_vector v = r.to_vector();
    if(v.size() == 0)
    {
      execute_sql(sqlite_queries::label_insert(), {{1, _string}});
      int id = last_row_id();
      id_to_label[id] = _string;
      label_to_id[_string] = id;
      return id;
    } else {
      v = v.begin()->to_vector();
      errors::check_condition(v.size() == 1, "Should have gotten only one column for label_get_from_id.");
      return v.begin()->to_integer();
    }
  } else {
    return it->second;
  }
}

std::string sqlite_data::label_for_id(int _id)
{
  auto it = id_to_label.find(_id);
  if(it == id_to_label.end())
  {
    value r = execute_sql(sqlite_queries::label_get_from_id(), {{1, _id}});
    value_vector v = r.to_vector();
    if(v.size() == 0)
    {
      throw gqlite::exception("Internal error: unknown label id {}", _id);
    } else {
      v = v.begin()->to_vector();
      errors::check_condition(v.size() == 1, "Should have gotten only one column for label_get_from_id.");
      return v.begin()->to_string();
    }
  } else {
    return it->second;
  }
}

namespace gqlite::backends::sqlite_oc_executor
{
  namespace algebra = gqlite::oc::algebra;

  /**
   * @internal
   * Represent a reference to a node
   */
  struct node_ref
  {
    int id; ///< database id for the node
    value cache; ///< cache of the labels/properties
  };

  /**
   * @internal
   * Represent a relationship between nodes
   */
  struct edge_ref
  {
    int id; ///< database id for the edge
    value cache; ///< cache of the label/properties
  };
  struct empty {};
  using node_ref_sp = std::shared_ptr<node_ref>;
  using edge_ref_sp = std::shared_ptr<edge_ref>;
  using exec_value = std::variant<empty, node_ref_sp, edge_ref_sp, gqlite::value>;
  using exec_value_table = table<exec_value>;

  /**
   * Global context for the execution of a query.
   */
  struct execution_context
  {
    sqlite_data* data;
    std::string graph_name = "default";

    /**
     * @return a value representing the executed value from @p _value
     */
    gqlite::value get_value(const exec_value& _value)
    {
      struct value_getter
      {
        execution_context* exec_c;
        gqlite::value operator()(const node_ref_sp& _node_ref)
        {
          if(_node_ref->cache.get_type() == value_type::invalid)
          {
            // Retrieve properties
            gqlite::value properties;
            {
              // Query
              value_vector properties_val_list_vector = exec_c->data->execute_sql(sqlite_queries::node_get_properties(exec_c->graph_name), {{1, _node_ref->id}}).to_vector();
              errors::check_condition(properties_val_list_vector.size() == 1, "When getting a node, should have received only one node");
              value_vector properties_row = properties_val_list_vector.front().to_vector();
              errors::check_condition(properties_row.size() == 1, "When getting a node, properties get should only have given one column");
              properties = gqlite::value::from_json(properties_row.front().to_string());
            }

            // Retrieve labels
            value_vector labels;
            {
              // Query
              value_vector labels_val_list_vector = exec_c->data->execute_sql(sqlite_queries::node_get_labels(exec_c->graph_name), {{1, _node_ref->id}}).to_vector();
              for(const gqlite::value& label_row_value : labels_val_list_vector)
              {
                value_vector label_row = label_row_value.to_vector();
                errors::check_condition(label_row.size() == 1, "When getting a node, labels get should only have given one column");
                labels.push_back(exec_c->data->label_for_id(label_row.front().to_integer()));
              }
            }
            // Generate the cache
            _node_ref->cache = gqlite::value{{
              {"type", gqlite::value("node")}, {"labels", gqlite::value(labels)}, {"id", gqlite::value(_node_ref->id)}, {"properties", gqlite::value(properties)}
            }};
          }
          return _node_ref->cache;
        }
        gqlite::value operator()(const edge_ref_sp& _edge_ref)
        {
          if(_edge_ref->cache.get_type() == value_type::invalid)
          {
            // Retrieve label/properties
            std::string label;
            gqlite::value properties;
            {
              // Query
              value_vector properties_val_list_vector = exec_c->data->execute_sql(sqlite_queries::edge_get_label_properties(exec_c->graph_name), {{1, _edge_ref->id}}).to_vector();
              errors::check_condition(properties_val_list_vector.size() == 1, "When getting an edge, should have received only one edge");
              value_vector properties_row = properties_val_list_vector.front().to_vector();
              errors::check_condition(properties_row.size() == 2, "When getting an edge, properties get should only have given two column");
              label = exec_c->data->label_for_id(properties_row[0].to_integer());
              properties = gqlite::value::from_json(properties_row[1].to_string());
            }
            // Generate the cache
            _edge_ref->cache = gqlite::value{{
              {"type", gqlite::value("edge")}, {"label", gqlite::value(label)}, {"id", gqlite::value(_edge_ref->id)}, {"properties", gqlite::value(properties)}
            }};
          }
          return _edge_ref->cache;
        }
        gqlite::value operator()(const gqlite::value& _value)
        {
          return _value;
        }
        gqlite::value operator()(const empty&)
        {
          return gqlite::value();
        }
      };
      return std::visit(value_getter{this}, _value);
    }
    /**
     * @return a value representing the executed value from @p _value to be used in sql query.
     * A.k.a. nodes/edges id or the value
     */
    gqlite::value get_sql_value(const exec_value& _value)
    {
      struct value_getter
      {
        execution_context* exec_c;
        gqlite::value operator()(const node_ref_sp& _node_ref)
        {
          return _node_ref->id;
        }
        gqlite::value operator()(const edge_ref_sp& _edge_ref)
        {
          return _edge_ref->id;
        }
        gqlite::value operator()(const gqlite::value& _value)
        {
          return _value;
        }
        gqlite::value operator()(const empty&)
        {
          return gqlite::value();
        }
      };
      return std::visit(value_getter{this}, _value);
    }
  };

  /**
   * Context for the evaluation of one row.
   */
  struct evaluation_context
  {
    exec_value_table table;
    std::vector<exec_value> current_row;
    std::vector<std::string> new_vars; ///< i.e. the new variables introduced in this match

    void prepare_current_row(const exec_value_table& _table, std::size_t _i, bool _first_statement)
    {
      if(current_row.size() < table.get_columns_count())
      {
        current_row.resize(table.get_columns_count());
      }
      if(_first_statement)
      {
        std::fill_n(current_row.begin(), current_row.size(), empty{});
      } else {
        exec_value_table::row_view row_init = _table.get_row(_i);
        std::copy(row_init.begin(), row_init.end(), current_row.begin());
        std::fill_n(current_row.begin() + row_init.size(), current_row.size() - row_init.size(), empty{});
      }
    }
    bool has_variable(const std::string& _variable)
    {
      return table.has_column(_variable);
    }
    bool is_variable_set(const std::string& _variable)
    {
      return table.has_column(_variable) and not std::holds_alternative<empty>(current_row[table.get_column_index(_variable)]);
    }
    bool is_new_variable(const std::string& _variable)
    {
      return std::find(new_vars.begin(), new_vars.end(), _variable) != new_vars.end();
    }
    std::size_t get_new_variable_index(const std::string& _variable)
    {
      return std::find(new_vars.begin(), new_vars.end(), _variable) - new_vars.begin();
    }
    void define_variable(const std::string& _variable)
    {
      if(_variable.empty()) return;
      if(table.has_column(_variable))
      {
        throw exception("Variable {} is already bound.", _variable);
      }
      table.add_column(_variable);
      new_vars.push_back(_variable);
    }
    void define_variable_if_needed(const std::string& _variable)
    {
      if(_variable.empty()) return;
      if(not table.has_column(_variable))
      {
        table.add_column(_variable);
        new_vars.push_back(_variable);
      }
    }
    exec_value get_variable(const std::string& _variable)
    {
      if(table.has_column(_variable))
      {
        return current_row[table.get_column_index(_variable)];
      } else {
        throw exception("Variable {} is not defined.", _variable);
      }
    }
    void set_variable(const std::string& _variable, const exec_value& _ev)
    {
      current_row[table.get_column_index(_variable)] = _ev;
    }

    /**
     * @return a node ref or an exception if not a node ref
     */
    node_ref_sp get_node_ref(const exec_value& _value)
    {
      if(std::holds_alternative<node_ref_sp>(_value))
      {
        return std::get<node_ref_sp>(_value);
      }
      throw exception("Expected a reference to a node.");
    }
    void define_variables(const std::vector<algebra::alternative<algebra::graph_node, algebra::graph_edge>>& _patterns)
    {
      for(const algebra::alternative<algebra::graph_node, algebra::graph_edge>& pattern : _patterns)
      {
        pattern.visit<void>([this](const algebra::graph_node_csp _node)
        {
          define_variable(_node->get_variable());
        }, [this](const algebra::graph_edge_csp _edge)
        {
          define_variable(_edge->get_variable());
          define_variable_if_needed(_edge->get_source()->get_variable());
          define_variable_if_needed(_edge->get_destination()->get_variable());
        });
      }
    }
  };

  /**
   * Context for the generation of the sql match query
   */
  struct match_context
  {
    execution_context* exec_c;
    evaluation_context* eval_c;
    int count = 0;
    std::string sql_variables;
    std::size_t sql_variables_count = 0;
    std::string sql_tables;
    std::string sql_conditions;
    std::map<int, value> bindings;
    std::vector<std::pair<std::string, std::string>> edge_sql_var_to_oc_var;
    int label_count = 0;
    struct var_info
    {
      std::string sql_var;
      std::string sql_properties_var;
      std::string sql_label_var;
      std::size_t sql_column;
      bool is_node;
    };
    std::map<std::string, var_info> oc_var_to_sql_var;
    /**
     * Generate the SQL join operation needed for matchin labels
     */
    std::string generate_labels_match(const std::vector<std::string>& _labels, const std::string& _node_variable, bool _start_with_and)
    {
      std::string cond;
      for(const std::string& label : _labels)
      {
        sql_tables += format_string(" JOIN gqlite_{}_labels AS tb_lab{}", exec_c->graph_name, label_count);
        if(not cond.empty() or _start_with_and) cond += " AND ";
        std::size_t idx = bind_value(exec_c->data->id_for_label(label));
        cond += format_string(" {} = tb_lab{}.node_id AND ?{} = tb_lab{}.label ",
                      _node_variable, label_count, to_string_fixed_width(idx, 3), label_count);
        ++label_count;
      }
      return cond;
    }

    var_info& get_variable_info(const std::string& _oc_variable)
    {
      auto it = oc_var_to_sql_var.find(_oc_variable);
      if(it == oc_var_to_sql_var.end())
      {
        throw gqlite::exception("Unknown variable '{}'", _oc_variable);
      }
      return it->second;
    }

    std::string get_sql_variable(const std::string& _oc_variable)
    {
      return get_variable_info(_oc_variable).sql_var;
    }
    std::string get_sql_properties_variable(const std::string& _oc_variable)
    {
      var_info& info = get_variable_info(_oc_variable);
      std::string properties_var = info.sql_properties_var;
      if(properties_var.empty())
      {
        properties_var = retrieve_sql_properties(info.sql_var);
        info.sql_properties_var = properties_var;
      }
      return properties_var;
    }
    std::string retrieve_sql_properties(const std::string& _sql_var)
    {
      std::string count_s = std::to_string(count++);
      std::string properties_var = format_string("tbp{}.properties", count_s);
      sql_tables += format_string(" JOIN gqlite_{}_nodes AS tbp{} ", exec_c->graph_name, count_s);
      sql_conditions += format_string(" AND tbp{}.id = {}", count_s, _sql_var);
      return properties_var;
    }
    std::size_t bind_value(const value& _value)
    {
      std::size_t binding_id = bindings.size() + 1;
      bindings[binding_id] = _value;
      return binding_id;
    }

  };

  /**
   * Visitor to build a sql query
   */
  struct sql_filter_visitor : public gqlite::oc::algebra::default_node_visitor<std::string>
  {
    match_context* mc;
    std::string visit_default(algebra::node_csp _node) override
    {
      throw gqlite::exception("Unimplemented statement node {} in sql_filter_visitor", oc::algebra::node_type_name(_node->get_type()));
    }
    std::string visit(algebra::variable_csp _node) override
    {
      return mc->get_sql_variable(_node->get_identifier());
    }
    std::string visit(algebra::has_labels_csp _node) override
    {
      return mc->generate_labels_match(_node->get_labels(), mc->get_sql_variable(_node->get_left()), false);
    }
    std::string visit(algebra::value_csp _node) override
    {
      std::size_t idx = mc->bind_value(_node->get_value());
      return "?" + to_string_fixed_width(idx, 3);
    }
    std::string visit(algebra::member_access_csp _node) override
    {
      return format_string("json_extract({}, '$.{}')", mc->get_sql_properties_variable(_node->get_left()), string::join(_node->get_path(), "."));
    }
    std::string visit(algebra::function_call_csp _node) override
    {
      if(_node->get_name() == "type")
      {
        errors::check_arguments_size("type", _node->get_arguments(), 1);
        algebra::node_csp arg0 = _node->get_arguments().front();
        errors::check_argument_type("type", arg0->get_type(), algebra::node_type::variable);
        algebra::variable_csp arg0_var = std::static_pointer_cast<const algebra::variable>(arg0);
        match_context::var_info& info = mc->get_variable_info(arg0_var->get_identifier());
        if(info.is_node)
        {
          throw gqlite::exception("type expect an edge for variable {}", arg0_var->get_identifier());
        }
        return gqlite::format_string("(SELECT label FROM gqlite_labels WHERE id = {})", info.sql_label_var);
      } else {
        throw gqlite::exception("unknown function {}", _node->get_name());
      }
    }
#define FILTER_VISITOR_BINARY_OP(_AL_, _OP_)                                                          \
    std::string visit(algebra::_AL_ ## _csp _node) override                                           \
    {                                                                                                 \
      return format_string("({} " _OP_ " {})", start(_node->get_left()), start(_node->get_right()));  \
    }
    FILTER_VISITOR_BINARY_OP(logical_and, "AND")
    FILTER_VISITOR_BINARY_OP(logical_or, "OR")
    FILTER_VISITOR_BINARY_OP(relational_equal, "=")
    FILTER_VISITOR_BINARY_OP(relational_different, "!=")
    FILTER_VISITOR_BINARY_OP(relational_inferior, "<")
    FILTER_VISITOR_BINARY_OP(relational_superior, ">")
    FILTER_VISITOR_BINARY_OP(relational_inferior_equal, "<")
    FILTER_VISITOR_BINARY_OP(relational_superior_equal, ">")
    FILTER_VISITOR_BINARY_OP(relational_in, "IN")
    FILTER_VISITOR_BINARY_OP(relational_not_in, "NOT IN")
    FILTER_VISITOR_BINARY_OP(addition, "+")
    FILTER_VISITOR_BINARY_OP(substraction, "-")
    FILTER_VISITOR_BINARY_OP(multiplication, "*")
    FILTER_VISITOR_BINARY_OP(division, "/")
#define FILTER_VISITOR_UNARY_OP(_AL_, _OP_)                             \
    std::string visit(algebra::_AL_ ## _csp _node) override             \
    {                                                                   \
      return format_string("(" _OP_ " {})", start(_node->get_value())); \
    }
    FILTER_VISITOR_UNARY_OP(logical_negation, "!")
    FILTER_VISITOR_UNARY_OP(negation, "-")
    std::string visit(algebra::is_not_null_csp _node) override
    {
      return format_string("({} IS NOT NULL)", start(_node->get_value()));
    }
    std::string visit(algebra::is_null_csp _node) override
    {
      return format_string("({} IS NULL)", start(_node->get_value()));
    }
  };
  /**
   * Visitor to evaluate expressions.
   */
  struct evaluator_visitor : public gqlite::oc::algebra::default_node_visitor<exec_value>
  {
    execution_context* exec_c;
    evaluation_context* eval_c;
    exec_value visit_default(algebra::node_csp _node) override
    {
      throw gqlite::exception("Unimplemented statement node {} in evaluator_visitor", oc::algebra::node_type_name(_node->get_type()));
    }
    value get_properties(const std::unordered_map<std::string, algebra::node_csp>& _properties)
    {
      value_map props;
      for(auto const& [k,v] : _properties)
      {
        props[k] = exec_c->get_value(start(v));
      }
      return props;
    }
    value_map get_properties(const exec_value& _value)
    {
      return exec_c->get_value(_value).to_map()["properties"].to_map();
    }
    value get_property(const value& _value, const std::vector<std::string>& _path)
    {
      value cval = _value;
      for(const std::string& pn : _path)
      {
        cval = cval.to_map()[pn];
     }
      return cval;
    }
    exec_value visit(algebra::value_csp _node) override
    {
      return _node->get_value();
    }
    exec_value visit(algebra::variable_csp _node) override
    {
      return eval_c->get_variable(_node->get_identifier());
    }
    exec_value visit(algebra::array_csp _node) override
    {
      std::vector<value> values;
      for(const algebra::node_csp& v : _node->get_array())
      {
        values.push_back(exec_c->get_value(start(v)));
      }
      return values;
    }
    exec_value visit(algebra::map_csp _node) override
    {
      return get_properties(_node->get_map());
    }
    exec_value visit(algebra::member_access_csp _node) override
    {
      exec_value value = eval_c->get_variable(_node->get_left());

      struct member_access
      {
        evaluator_visitor* self;
        algebra::member_access_csp node;
        gqlite::value operator()(const node_ref_sp& _v)
        {
          return self->get_property(self->get_properties(_v), node->get_path());
        }
        gqlite::value operator()(const edge_ref_sp& _v)
        {
          return self->get_property(self->get_properties(_v), node->get_path());
        }
        gqlite::value operator()(const gqlite::value& _v, bool _allow_array = true)
        {
          switch(_v.get_type())
          {
            case gqlite::value_type::map:
            {
              return self->get_property(_v.to_map(), node->get_path());
            }
            default:
              throw gqlite::exception("Invalid value type, expected a map, got {}.", _v.to_json());
          }
        }
        gqlite::value operator()(const empty&)
        {
          return gqlite::value();
        }
      };
      return std::visit(member_access{this, _node}, value);
    }
    bool is_numeric(value_type _vt)
    {
      return _vt == value_type::integer or _vt == value_type::number;
    }
    exec_value visit(algebra::addition_csp _node) override
    {
      exec_value left_ev = start(_node->get_left());
      exec_value right_ev = start(_node->get_right());
      errors::check_condition(std::holds_alternative<value>(left_ev), "Binary operations must be done on null values.");
      errors::check_condition(std::holds_alternative<value>(right_ev), "Binary operations must be done on null values.");
      value left_val = std::get<value>(left_ev);
      value right_val = std::get<value>(right_ev);
      if(left_val.get_type() == value_type::string and right_val.get_type() == value_type::string)
      {
        return left_val.to_string() + right_val.to_string();
      } else if(is_numeric(left_val.get_type()) and is_numeric(right_val.get_type()))
      {
        if(left_val.get_type() == value_type::integer and right_val.get_type() == value_type::integer)
        {
          return left_val.to_integer() + right_val.to_integer();
        } else {
          return left_val.to_double() + right_val.to_double();
        }
      } else {
        throw exception("Cannot add {} with {}.", left_val.to_json(), right_val.to_json());
      }
    }
    exec_value visit(algebra::is_not_null_csp _node) override
    {
      exec_value left_ev = start(_node->get_value());
      return not std::holds_alternative<empty>(left_ev)
            and (not std::holds_alternative<value>(left_ev) or std::get<value>(left_ev).get_type() != value_type::invalid);
    }
    exec_value visit(algebra::is_null_csp _node) override
    {
      exec_value left_ev = start(_node->get_value());
      return std::holds_alternative<empty>(left_ev)
            or (std::holds_alternative<value>(left_ev) and std::get<value>(left_ev).get_type() == value_type::invalid);;
    }
    exec_value visit(algebra::function_call_csp _node) override
    {
      std::vector<value> args;
      for(const algebra::node_csp& arg : _node->get_arguments())
      {
        args.push_back(exec_c->get_value(start(arg)));
      }
      return functions::call(_node->get_name(), args);
    }
  };
  /**
   * Visitor to execute statements: CREATE, MATCHES, RETURN...
   */
  struct statement_visitor : public gqlite::oc::algebra::default_node_visitor<value>
  {
    execution_context exec_c;
    exec_value_table table;
    bool first_statement = true;
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // Common functions
    value visit_default(algebra::node_csp _node) override
    {
      throw gqlite::exception("Unimplemented statement node {} in statement_visitor", oc::algebra::node_type_name(_node->get_type()));
    }
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // Creation functions
    bool has_node_ref(evaluator_visitor* _ev, algebra::graph_node_csp _node)
    {
      return not _node->get_variable().empty() and _ev->eval_c->is_variable_set(_node->get_variable());
    }
    node_ref_sp create_node(evaluator_visitor* _ev, algebra::graph_node_csp _node)
    {
      value props;
      if(_node->get_properties())
      {
        props = _ev->get_properties(_node->get_properties()->get_map());
      } else {
        props = value_map();
      }
      std::string json_properties = props.to_json();
      exec_c.data->execute_sql(sqlite_queries::node_create(exec_c.graph_name), {{1, json_properties}});
      int row_id = exec_c.data->last_row_id();
      for(const std::string& label : _node->get_labels())
      {
        exec_c.data->execute_sql(sqlite_queries::node_add_label(exec_c.graph_name), {{1, exec_c.data->id_for_label(label)}, {2, row_id}});
      }
      node_ref_sp nr = std::make_shared<node_ref>(node_ref{
          row_id,
          gqlite::value{
          {
            {"type", gqlite::value("node")}, {"labels", gqlite::value(_node->get_labels())}, {"id", gqlite::value(row_id)}, {"properties", props}            
          }}
        });
      if(not _node->get_variable().empty())
      {
        _ev->eval_c->set_variable(_node->get_variable(), nr);
      }
      
      return nr;
    }
    void create_edge(evaluator_visitor* _ev, algebra::graph_edge_csp _edge)
    {
      node_ref_sp source = has_node_ref(_ev, _edge->get_source()) ? _ev->eval_c->get_node_ref(_ev->eval_c->get_variable(_edge->get_source()->get_variable())) : create_node(_ev, _edge->get_source());
      node_ref_sp destination = has_node_ref(_ev, _edge->get_destination()) ? _ev->eval_c->get_node_ref(_ev->eval_c->get_variable(_edge->get_destination()->get_variable())) : create_node(_ev, _edge->get_destination());
      std::string label = _edge->get_labels().empty() ? std::string() : _edge->get_labels().front();
      int label_id = exec_c.data->id_for_label(label);
      
      value props;
      if(_edge->get_properties())
      {
        props = _ev->get_properties(_edge->get_properties()->get_map());
      } else {
        props = value_map();
      }
      exec_c.data->execute_sql(sqlite_queries::edge_create(exec_c.graph_name), {{1, label_id}, {2, props.to_json()}, {3, source->id}, {4, destination->id}});
      int row_id = exec_c.data->last_row_id();
      edge_ref_sp nr = std::make_shared<edge_ref>(edge_ref{
          row_id,
          value{
            {{"type", value("edge")}, {"label", value(label)}, {"id", value(row_id)}, {"properties", props}}
          }
        });
      if(not _edge->get_variable().empty())
      {
        _ev->eval_c->set_variable(_edge->get_variable(), nr);
      }
    }
    /**
     * Execute the create statement
     */
    value visit(algebra::create_csp _node) override
    {
      evaluation_context eval_c;
      eval_c.table.add_columns(table.get_columns_names());
      evaluator_visitor eval_v;
      eval_v.exec_c = &exec_c;
      eval_v.eval_c = &eval_c;
      std::size_t max_iter = first_statement ? 1 : table.get_rows_count();
      // Set variables
      eval_c.define_variables(_node->get_patterns());
      // Create nodes
      for(std::size_t i = 0; i < max_iter; ++i)
      {
        eval_c.prepare_current_row(table, i, first_statement);
        for(const algebra::alternative<algebra::graph_node, algebra::graph_edge>& pattern : _node->get_patterns())
        {
          pattern.visit<void>([this, &eval_v](const algebra::graph_node_csp _node)
          {
            create_node(&eval_v, _node);
          },
          [this, &eval_v](const algebra::graph_edge_csp _edge)
          {
            create_edge(&eval_v, _edge);
          });
        }
        eval_c.table.add_row(eval_c.current_row);
      }
      table = eval_c.table;
      return value{};
    }
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // Match
    /**
     * Generate a filter expression for a map
     */
    std::string generate_filter(const algebra::map_csp& _map, const std::string& _path, sql_filter_visitor* _filter_visitor)
    {
      std::string r;
      for(const auto& [k, v] : _map->get_map())
      {
        std::string path = _path + "." + k;
        if(v->get_type() == algebra::node_type::map)
        {
          r += generate_filter(std::static_pointer_cast<const algebra::map>(_map), path, _filter_visitor);
        } else {
          r += format_string(" AND json_extract({}') = {} ", path, _filter_visitor->start(v));
        }
      }
      return r;
    }
    void generate_var_match(match_context* _mc, const std::string& _oc_var, const std::string& _sql_var, const std::string& _sql_properties_var, const std::string& _label_var, bool _is_node)
    {
      if(not _oc_var.empty())
      {
        auto it = _mc->oc_var_to_sql_var.find(_oc_var);
        if(it == _mc->oc_var_to_sql_var.end())
        {
          // Check if it is a new variable and need to be extracted in the SQL statement
          errors::check_condition(_mc->eval_c->is_new_variable(_oc_var), "only new variable should be defined.");
          if(not _mc->sql_variables.empty()) _mc->sql_variables += ", ";
          _mc->sql_variables += _sql_var;
          _mc->oc_var_to_sql_var[_oc_var] = {_sql_var, _sql_properties_var, _label_var, _mc->sql_variables_count, _is_node};
          ++_mc->sql_variables_count;
        } else {
          // Generate match
          if(it->second.is_node != _is_node) throw exception("{} is redefined as a variable of a different type", _oc_var);
          _mc->sql_conditions += format_string(" AND {} = {} ", _sql_var, it->second.sql_var);
        }
      }
    }
    value visit(algebra::match_csp _node) override
    {
      evaluation_context eval_c;
      eval_c.table.add_columns(table.get_columns_names());
      evaluator_visitor eval_v;
      eval_v.exec_c = &exec_c;
      eval_v.eval_c = &eval_c;

      match_context mc;
      mc.exec_c = &exec_c;
      mc.eval_c = &eval_c;
      sql_filter_visitor fil_vis;
      fil_vis.mc = &mc;

      std::size_t max_iter = first_statement ? 1 : table.get_rows_count();
      // Set variables
      eval_c.define_variables(_node->get_patterns());

      for(std::size_t i = 0; i < max_iter; ++i)
      {
        eval_c.prepare_current_row(table, i, first_statement);

        // 0) Bind the values
        {
          std::size_t column_index = 0;
          for(const std::string& column : table.get_columns_names())
          {
            // It is a value, bind it.
            int idx = mc.bind_value(mc.exec_c->get_sql_value(mc.eval_c->get_variable(column)));
            std::string binding = format_string("?{}", to_string_fixed_width(idx, 3));
            mc.oc_var_to_sql_var[column] = {binding, std::string(), std::string(), std::size_t(-1), std::holds_alternative<node_ref_sp>(eval_c.current_row[column_index])};
          }
          ++column_index;
        }

        // 1) Go through the patterns
        for(const algebra::alternative<algebra::graph_node, algebra::graph_edge>& pattern : _node->get_patterns())
        {
          if(mc.count != 0)
          {
            mc.sql_tables += " JOIN ";
          }
          pattern.visit<void>([this, &mc, &fil_vis](const algebra::graph_node_csp _node)
          {
            std::string count_s = std::to_string(mc.count++);
            std::string sql_var = format_string("tb{}.id", count_s);
            std::string sql_properties_var = format_string("tb{}.properties", count_s);
            mc.sql_tables += format_string("gqlite_{}_nodes AS tb{}", exec_c.graph_name, count_s);
            mc.sql_conditions += mc.generate_labels_match(_node->get_labels(), format_string("tb{}.id", count_s), true);
            if(_node->get_properties())
            {
              mc.sql_conditions += generate_filter(_node->get_properties(), sql_properties_var + ", '$", &fil_vis);
            }
            generate_var_match(&mc, _node->get_variable(), sql_var, sql_properties_var, std::string(), true);
          },
          [this, &mc, &fil_vis](const algebra::graph_edge_csp _edge)
          {
            std::string count_s = std::to_string(mc.count++);
            std::string sql_source_var = format_string("tb{}.left", count_s);
            std::string sql_edge_var = format_string("tb{}.id", count_s);
            std::string sql_properties_edge_var = format_string("tb{}.properties", count_s);
            std::string sql_label_edge_var = format_string("tb{}.label", count_s);
            std::string sql_source_properties_var;
            std::string sql_destination_properties_var;
            std::string sql_destination_var = format_string("tb{}.right", count_s);
            if(_edge->get_directivity() == algebra::edge_directivity::undirected)
            {
              mc.sql_tables += format_string("gqlite_{}_edges_undirected AS tb{}", exec_c.graph_name, count_s);
            } else {
              mc.sql_tables += format_string("gqlite_{}_edges AS tb{}", exec_c.graph_name, count_s);
            }
            mc.sql_conditions += mc.generate_labels_match(_edge->get_source()->get_labels(), "tb" + count_s + ".left", true);
            mc.sql_conditions += mc.generate_labels_match(_edge->get_destination()->get_labels(), "tb" + count_s + ".right", true);
            if(not _edge->get_labels().empty())
            {
              mc.sql_conditions += " AND (FALSE ";
              for(const std::string& label : _edge->get_labels())
              {
                mc.sql_conditions += format_string(" OR {} = {}", sql_label_edge_var
                , exec_c.data->id_for_label(label));
              }
              mc.sql_conditions += ")";
            }
            if(_edge->get_properties())
            {
              mc.sql_conditions += generate_filter(_edge->get_properties(), sql_properties_edge_var + ", '$", &fil_vis);
            }
            if(_edge->get_source()->get_properties())
            {
              sql_source_properties_var = mc.retrieve_sql_properties(sql_source_var);
              mc.sql_conditions += generate_filter(_edge->get_source()->get_properties(), sql_source_properties_var + ", '$", &fil_vis);
            }
            if(_edge->get_destination()->get_properties())
            {
              sql_destination_properties_var = mc.retrieve_sql_properties(sql_destination_var);
              mc.sql_conditions += generate_filter(_edge->get_destination()->get_properties(), sql_destination_properties_var + ", '$", &fil_vis);
            }
            // Ensure identical oc variablle are joined in SQL
            generate_var_match(&mc, _edge->get_source()->get_variable(), sql_source_var, sql_source_properties_var, std::string(), true);
            generate_var_match(&mc, _edge->get_variable(), sql_edge_var, sql_properties_edge_var, sql_label_edge_var, false);
            generate_var_match(&mc, _edge->get_destination()->get_variable(), sql_destination_var, sql_destination_properties_var, std::string(), true);
            // ensure edge isomorphism, i.e., if the variable names are different, the edges must be different
            for(const std::pair<std::string, std::string>& sql_var_to_oc_var : mc.edge_sql_var_to_oc_var)
            {
              if(sql_var_to_oc_var.second != _edge->get_variable() and not _edge->get_variable().empty())
              {
                mc.sql_conditions += format_string(" AND {} != {}", sql_edge_var, sql_var_to_oc_var.first);
              }
            }
            mc.edge_sql_var_to_oc_var.push_back({sql_edge_var, _edge->get_variable()});
            // Increase variable counter
          });
        }
        // 2) Handle where
        if(_node->get_where())
        {
          mc.sql_conditions += " AND " + fil_vis.start(_node->get_where());
        }
        // 3) Assemble SQL query for execution
        if(not mc.sql_conditions.empty())
        {
          mc.sql_conditions = (mc.count == 1 ? " WHERE TRUE " : " ON TRUE ") + mc.sql_conditions;
        }
        std::string sql_query = "SELECT " + mc.sql_variables + " FROM " + mc.sql_tables + mc.sql_conditions;
        gqlite::value r = exec_c.data->execute_sql(sql_query, mc.bindings);

        // 4) Store the results
        std::vector<exec_value> values;
        values.resize(eval_c.table.get_columns_count());
        std::size_t start_new_values = 0;
        if(not first_statement)
        {
          exec_value_table::row_view row_init = table.get_row(i);
          std::copy(row_init.begin(), row_init.end(), values.begin());
          start_new_values = row_init.size();
        }
        for(const gqlite::value& row_value : r.to_vector())
        {
          value_vector row = row_value.to_vector();
          errors::check_condition(row.size() == mc.eval_c->new_vars.size(), "Wrong number of column return by SQL Query.");
          for(const std::string& k : mc.eval_c->new_vars)
          {
            match_context::var_info vi = mc.oc_var_to_sql_var[k];
            int id = row[vi.sql_column].to_integer();
            if(vi.is_node)
            {
              values[vi.sql_column + start_new_values] = std::make_shared<node_ref>(id);
            } else {
              values[vi.sql_column + start_new_values] = std::make_shared<edge_ref>(id);
            }
          }
          eval_c.table.add_row(values);
        }
      }
      table = eval_c.table;
      return value{};
    }
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // delete
    value visit(algebra::delete_statement_csp _ds) override
    {
      evaluation_context eval_c;
      eval_c.table = table;
      evaluator_visitor eval_v;
      eval_v.exec_c = &exec_c;
      eval_v.eval_c = &eval_c;

      struct deleter
      {
        statement_visitor* self;
        bool detach;
        void operator()(const node_ref_sp& _node)
        {
          if(detach)
          {
            self->exec_c.data->execute_sql(sqlite_queries::edge_delete_by_node(self->exec_c.graph_name), {{1, _node->id}});
          } else {
            value result = self->exec_c.data->execute_sql(sqlite_queries::edge_count_by_node(self->exec_c.graph_name), {{1, _node->id}});
            value_vector rows = result.to_vector();
            errors::check_condition(rows.size() == 1, "Invalid number of rows for counting edges got {} expected 1.", rows.size());
            value_vector row = rows.front().to_vector();
            errors::check_condition(row.size() == 1, "Invalid number of columns for counting edges got {} expected 1.", rows.size());
            int count = row.front().to_integer();
            errors::check_condition(count == 0, "Cannot delete node with {} relationships.", count);
          }
          self->exec_c.data->execute_sql(sqlite_queries::node_delete(self->exec_c.graph_name), {{1, _node->id}});
        }
        void operator()(const edge_ref_sp& _edge)
        {
          self->exec_c.data->execute_sql(sqlite_queries::edge_delete(self->exec_c.graph_name), {{1, _edge->id}});
        }
        void operator()(const value& _value)
        {
          throw exception("Cannot delete a value.");
        }
        void operator()(const empty&)
        {
          throw exception("Cannot delete an empty value.");
        }
      };

      for(int i = 0; i < table.get_rows_count(); ++i)
      {
        value_vector row;
        eval_c.prepare_current_row(table, i, false);
        for(const algebra::node_csp& rv : _ds->get_expressions())
        {
          exec_value ev =eval_v.start(rv);
          std::visit(deleter{this, _ds->get_detach()}, ev);
        }
      }
      return value();
    }
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // set
    value visit(algebra::set_csp _set) override
    {
      evaluation_context eval_c;
      eval_c.table = table;
      evaluator_visitor eval_v;
      eval_v.exec_c = &exec_c;
      eval_v.eval_c = &eval_c;

      struct property_setter_adder_base
      {
        statement_visitor* self;
        std::vector<std::string> path;
        exec_value new_value;
        void execute(const std::string& _query, int _node_id)
        {
          std::string path_string = "$";
          for(const std::string& pe : path)
          {
            path_string += "." + pe;
          }
          self->exec_c.data->execute_sql(_query, {{1, _node_id}, {2, path_string}, {3, self->exec_c.get_value(new_value)}});
        }
        void operator()(const value& _value)
        {
          std::cout << _value.to_json() << std::endl;
          throw exception("Only node/edge can be set.");
        }
        void operator()(const empty&)
        {
          throw exception("Try to set a null value.");
        }

      };

      struct property_setter : property_setter_adder_base
      {
        using property_setter_adder_base::operator();
        void operator()(const node_ref_sp& _node)
        {
          _node->cache = gqlite::value(); // Invalidate cache
          execute(sqlite_queries::node_set_property(self->exec_c.graph_name), _node->id);
        }
        void operator()(const edge_ref_sp& _edge)
        {
          _edge->cache = gqlite::value(); // Invalidate cache
          execute(sqlite_queries::edge_set_property(self->exec_c.graph_name), _edge->id);
        }
      };
      struct property_adder : property_setter_adder_base
      {
        using property_setter_adder_base::operator();
        void operator()(const node_ref_sp& _node)
        {
          _node->cache = gqlite::value(); // Invalidate cache
          execute(sqlite_queries::node_add_properties(self->exec_c.graph_name), _node->id);
        }
        void operator()(const edge_ref_sp& _edge)
        {
          _edge->cache = gqlite::value(); // Invalidate cache
          execute(sqlite_queries::node_add_properties(self->exec_c.graph_name), _edge->id);
        }
      };

      struct set_visitor : public gqlite::oc::algebra::default_node_visitor<void>
      {
        statement_visitor* self;
        evaluation_context* eval_c;
        evaluator_visitor* eval_v;
        void visit_default(algebra::node_csp _node) override
        {
          throw gqlite::exception("Unimplemented statement node {} in set_visitor", oc::algebra::node_type_name(_node->get_type()));
        }
        void visit(algebra::set_property_csp _property)
        {
          exec_value target = eval_c->get_variable(_property->get_target()->get_left());
          exec_value ev = eval_v->start(_property->get_expression());
          std::visit(property_setter{self, _property->get_target()->get_path(), ev}, target);
          
        }
        void visit(algebra::add_property_csp _property)
        {
          exec_value target = eval_c->get_variable(_property->get_target()->get_left());
          exec_value ev = eval_v->start(_property->get_expression());
          std::visit(property_adder{self, _property->get_target()->get_path(), ev}, target);
          
        }
        void visit(algebra::edit_labels_csp _property)
        {
          exec_value target = eval_c->get_variable(_property->get_target());
          errors::check_condition(std::holds_alternative<node_ref_sp>(target), "Can only add labels to nodes.");
          node_ref_sp target_nd = std::get<node_ref_sp>(target);
          for(const std::string& label : _property->get_labels())
          {
            self->exec_c.data->execute_sql(sqlite_queries::node_add_label(self->exec_c.graph_name), {{2, target_nd->id}, {1, self->exec_c.data->id_for_label(label)}});
          }
        }
      };

      set_visitor set_v;
      set_v.self = this;
      set_v.eval_c = &eval_c;
      set_v.eval_v = &eval_v;
      for(int i = 0; i < table.get_rows_count(); ++i)
      {
        value_vector row;
        eval_c.prepare_current_row(table, i, false);
        for(const algebra::node_csp& node : _set->get_nodes())
        {
          set_v.start(node);
        }
      }
      return value();
    }
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // remove
    value visit(algebra::remove_csp _set) override
    {
      evaluation_context eval_c;
      eval_c.table = table;
      evaluator_visitor eval_v;
      eval_v.exec_c = &exec_c;
      eval_v.eval_c = &eval_c;

      struct property_remover
      {
        statement_visitor* self;
        std::vector<std::string> path;
        void execute(const std::string& _query, int _node_id)
        {
          std::string path_string = "$";
          for(const std::string& pe : path)
          {
            path_string += "." + pe;
          }
          self->exec_c.data->execute_sql(_query, {{1, _node_id}, {2, path_string}});
        }
        void operator()(const value& _value)
        {
          std::cout << _value.to_json() << std::endl;
          throw exception("Only node/edge can be set.");
        }
        void operator()(const empty&)
        {
          throw exception("Try to set a null value.");
        }
        void operator()(const node_ref_sp& _node)
        {
          _node->cache = gqlite::value(); // Invalidate cache
          execute(sqlite_queries::node_remove_property(self->exec_c.graph_name), _node->id);
        }
        void operator()(const edge_ref_sp& _edge)
        {
          _edge->cache = gqlite::value(); // Invalidate cache
          execute(sqlite_queries::edge_remove_property(self->exec_c.graph_name), _edge->id);
        }
      };

      struct remove_visitor : public gqlite::oc::algebra::default_node_visitor<void>
      {
        statement_visitor* self;
        evaluation_context* eval_c;
        evaluator_visitor* eval_v;
        void visit_default(algebra::node_csp _node) override
        {
          throw gqlite::exception("Unimplemented statement node {} in set_visitor", oc::algebra::node_type_name(_node->get_type()));
        }
        void visit(algebra::remove_property_csp _property)
        {
          exec_value target = eval_c->get_variable(_property->get_target()->get_left());
          std::visit(property_remover{self, _property->get_target()->get_path()}, target);
          
        }
        void visit(algebra::edit_labels_csp _property)
        {
          exec_value target = eval_c->get_variable(_property->get_target());
          errors::check_condition(std::holds_alternative<node_ref_sp>(target), "Can only add labels to nodes.");
          node_ref_sp target_nd = std::get<node_ref_sp>(target);
          for(const std::string& label : _property->get_labels())
          {
            self->exec_c.data->execute_sql(sqlite_queries::node_remove_label(self->exec_c.graph_name), {{2, target_nd->id}, {1, self->exec_c.data->id_for_label(label)}});
          }
        }

      };

      remove_visitor set_v;
      set_v.self = this;
      set_v.eval_c = &eval_c;
      set_v.eval_v = &eval_v;
      for(int i = 0; i < table.get_rows_count(); ++i)
      {
        value_vector row;
        eval_c.prepare_current_row(table, i, false);
        for(const algebra::node_csp& node : _set->get_nodes())
        {
          set_v.start(node);
        }
      }
      return value();
    }
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // with
    value visit(algebra::with_csp w) override
    {
      if(w->get_all() and w->get_expressions().empty())
      {
        return value{};
      }
      if(w->get_all() and not w->get_expressions().empty())
      {
        throw exception("Unimplemented WITH *, expressions");
      }
      evaluation_context eval_c;
      eval_c.table = table;
      evaluator_visitor eval_v;
      eval_v.exec_c = &exec_c;
      eval_v.eval_c = &eval_c;

      exec_value_table out_table;
      value_vector labels;
      for(const algebra::named_expression_csp& rv : w->get_expressions())
      {
        out_table.add_column(rv->get_name());
      }
      std::size_t max_iter = first_statement ? 1 : table.get_rows_count();
      for(int i = 0; i < max_iter; ++i)
      {
        std::vector<exec_value> row;
        eval_c.prepare_current_row(table, i, first_statement);
        for(const algebra::named_expression_csp& rv : w->get_expressions())
        {
          row.push_back(eval_v.start(rv->get_expression()));
        }
        out_table.add_row(row);
      }
      table = out_table;
      return value();
    }
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // Return
    value visit(algebra::return_statement_csp rs) override
    {
      if(rs->get_all())
      {
        errors::check_condition(rs->get_expressions().empty(), "Unimplemented RETURN *, expressions");
        value_vector labels;
        for(const std::string&  c : table.get_columns_names()) { labels.push_back(c); }
        value_vector results_rows;
        results_rows.push_back(labels);
        for(int i = 0; i < table.get_rows_count(); ++i)
        {
          value_vector row;
          for(const exec_value& ev : table.get_row(i))
          {
            row.push_back(exec_c.get_value(ev));
          }
          results_rows.push_back(row);
        }
        return results_rows;

      } else {
        evaluation_context eval_c;
        eval_c.table = table;
        evaluator_visitor eval_v;
        eval_v.exec_c = &exec_c;
        eval_v.eval_c = &eval_c;

        value_vector labels;
        for(const algebra::named_expression_csp& rv : rs->get_expressions())
        {
          labels.push_back(rv->get_name());
        }
        value_vector results_rows;
        results_rows.push_back(labels);
        for(int i = 0; i < table.get_rows_count(); ++i)
        {
          value_vector row;
          eval_c.prepare_current_row(table, i, false);
          for(const algebra::named_expression_csp& rv : rs->get_expressions())
          {
            row.push_back(exec_c.get_value(eval_v.start(rv->get_expression())));
          }
          results_rows.push_back(row);
        }
        return value(results_rows);
      }
    }
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // Statements
    value visit(algebra::statements_csp _node) override
    {
      for(algebra::node_csp node : _node->get_nodes())
      {
        value val = start(node);
        first_statement = false;
        if(node->get_type() == algebra::node_type::return_statement)
        {
          return val;
        }
      }
      return value{};
    }
  };
}

sqlite::sqlite(void* _db) : d(new data)
{
  d->handle = reinterpret_cast<sqlite3*>(_db);
  if(not d->table_has("gqlite_labels"))
  {
    d->execute_sql(sqlite_queries::label_create_table());
  }
  if(not d->graph_has("default"))
  {
    d->graph_create("default");
  }
}

sqlite::~sqlite()
{}

sqlite* sqlite::from_file(const std::string& _filename)
{
  sqlite3* handle;

  if(sqlite3_open(_filename.c_str(), &handle) != SQLITE_OK)
  {
    gqlite_error(sqlite3_errmsg(handle));
    sqlite3_close(handle);
    return nullptr;
  }
  return new sqlite(handle);
}

gqlite::value sqlite::execute_oc_query(oc::algebra::node_csp _node, const value_map& _bindings)
{
  d->execute_sql("BEGIN");
  try
  {
    sqlite_oc_executor::statement_visitor executor;
    executor.exec_c.data = d;
    value val = executor.start(_node);
    d->execute_sql("COMMIT");
    return val;
  } catch(const exception& _ex)
  {
    d->execute_sql("ROLLBACK");
    throw _ex;
  }
}

gqlite::value sqlite::get_debug_stats() const
{
  gqlite::value result = d->execute_sql(sqlite_queries::get_debug_stats("default"));
  value_vector rows = result.to_vector();
  errors::check_condition(rows.size() == 7, "Invalid number of debug stats got {} expected 7.", rows.size());
  value_map stats;
  stats["nodes_count"] = rows[0].to_vector()[0].to_integer();
  stats["edges_count"] = rows[1].to_vector()[0].to_integer();
  stats["labels_assignment_count"] = rows[2].to_vector()[0].to_integer();
  stats["properties_count"] = rows[3].to_vector()[0].to_integer() + rows[4].to_vector()[0].to_integer();
  stats["labels_count"] = rows[5].to_vector()[0].to_integer();
  stats["labels_assignment_nodes_count"] = rows[6].to_vector()[0].to_integer();
  return stats;
}
//END backends/sqlite.cpp
//BEGIN oc/lexer.cpp


#include <algorithm>
#include <iostream>
#include <vector>



using namespace gqlite::oc;

struct lexer::data {
  std::istream* stream;
  int lastChar;
  int col;
  int line;
  int followingnewline;
  std::vector<token> ungotten;
};

lexer::lexer(std::istream* sstream) : d(new data)
{
  d->col = 1;
  d->line = 1;
  d->followingnewline = 1;
  d->stream = sstream;
}

lexer::~lexer()
{
  delete d;
}

#define IDENTIFIER_IS_KEYWORD( tokenname, tokenid) \
  if( identifierStr == tokenname) \
  { \
    return token(token_type::tokenid, line(), initial_col); \
  }

#define CHAR_IS_TOKEN( tokenchar, tokenid ) \
  if( lastChar == tokenchar ) \
  { \
    return token(token_type::tokenid, line(), initial_col); \
  }

#define CHAR_IS_TOKEN_OR_TOKEN( tokenchar, tokendecidechar, tokenid_1, tokenid_2 ) \
  if( lastChar == tokenchar  ) \
  { \
    if( get_next_char() == tokendecidechar ) \
    { \
      return token(token_type::tokenid_2, line(), initial_col); \
    } else { \
      unget(); \
      return token(token_type::tokenid_1, line(), initial_col); \
    } \
  }

#define CHAR_IS_TOKEN_OR_TOKEN_OR_TOKEN( tokenchar_1, tokenchar_2, tokenchar_3, tokenid_1, tokenid_2, tokenid_3 ) \
  if( lastChar == tokenchar_1 ) \
  { \
    int nextChar = get_next_char(); \
    if( nextChar == tokenchar_2 ) \
    { \
      return token(token_type::tokenid_2, line(), initial_col); \
    } else if( nextChar  == tokenchar_3 ) \
    { \
      return token(token_type::tokenid_3, line(), initial_col); \
    } else { \
      unget(); \
      return token(token_type::tokenid_1, line(), initial_col); \
    } \
  }

#define CHAR_IS_TOKEN_OR_TOKEN_OR_TOKEN_OR_TOKEN( tokenchar_1, tokenchar_2, tokenchar_3, tokenchar_4, tokenid_1, tokenid_2, tokenid_3, tokenid_4 ) \
  if( lastChar == tokenchar_1 ) \
  { \
    int nextChar = get_next_char(); \
    if( nextChar == tokenchar_2 ) \
    { \
      return token(token_type::tokenid_2, line(), initial_col); \
    } else if( nextChar  == tokenchar_3 ) \
    { \
      return token(token_type::tokenid_3, line(), initial_col); \
    } else if( nextChar  == tokenchar_4 ) \
    { \
      return token(token_type::tokenid_4, line(), initial_col); \
    } else { \
      unget(); \
      return token(token_type::tokenid_1, line(), initial_col); \
    } \
  }

void lexer::unget(const token& _token)
{
  d->ungotten.push_back(_token);
}

token lexer::next_token()
{
  if(not d->ungotten.empty())
  {
    token t = d->ungotten.back();
    d->ungotten.pop_back();
    return t;
  }
  int lastChar = get_next_non_separator_char();
  int initial_line = line() - 1;
  int initial_col = column() - 1;
  if( eof() ) return token(token_type::END_OF_FILE, line(), initial_col);
  std::string identifierStr;
  // Test for comment
  token commenttoken;
  if( ignore_comment( commenttoken, lastChar ) )
  {
    return commenttoken;
  }
  // if it is alpha, it's an identifier or a keyword
  if(std::isalpha(lastChar) or lastChar == '_')
  {
    identifierStr = get_identifier(lastChar);
#define TOKEN_KEYWORD(_K_) IDENTIFIER_IS_KEYWORD(# _K_, _K_)
#define TOKEN_KEYWORD2(_K_, _S_) IDENTIFIER_IS_KEYWORD(_S_, _K_)
    TOKEN_KEYWORD(AND)
TOKEN_KEYWORD(AS)
TOKEN_KEYWORD(CREATE)
TOKEN_KEYWORD(DELETE)
TOKEN_KEYWORD(DETACH)
TOKEN_KEYWORD2(FALSE, "false")
TOKEN_KEYWORD(IN)
TOKEN_KEYWORD(IS)
TOKEN_KEYWORD(NOT)
TOKEN_KEYWORD(MATCH)
TOKEN_KEYWORD2(NULL_CAPS, "NULL")
TOKEN_KEYWORD2(NULL_TOKEN, "null")
TOKEN_KEYWORD(OR)
TOKEN_KEYWORD(REMOVE)
TOKEN_KEYWORD(RETURN)
TOKEN_KEYWORD(SET)
TOKEN_KEYWORD2(TRUE, "true")
TOKEN_KEYWORD(XOR)
TOKEN_KEYWORD(WITH)
TOKEN_KEYWORD(WHERE)

#undef TOKEN_KEYWORD
#undef TOKEN_KEYWORD2

    return token(token_type::IDENTIFIER, identifierStr, line(), initial_col);
  } else if(lastChar == '"' or lastChar == '\'' ) {
    return get_string(lastChar);
  } else if(lastChar >= '0' and lastChar <= '9') {
    std::string number;
    number += char(lastChar);
    lastChar = get_next_char();
    bool is_integer = true;
    while((lastChar >= '0' and lastChar <= '9') or lastChar == '.' or lastChar == 'e' or lastChar == '+' or lastChar == '-')
    {
      is_integer = is_integer and (lastChar != '.' and lastChar != 'e');
      number += char(lastChar);
      lastChar = get_next_char();
    }
    unget();
    if(is_integer)
    {
      return token(token_type::INTEGER, number, line(), initial_col);
    } else {
      return token(token_type::FLOATING_POINT, number, line(), initial_col);
    }
  } else {
    CHAR_IS_TOKEN(';', SEMI );
    CHAR_IS_TOKEN('.', DOT );
    CHAR_IS_TOKEN( '{', STARTBRACE );
    CHAR_IS_TOKEN( '}', ENDBRACE );
    CHAR_IS_TOKEN( '(', STARTBRACKET );
    CHAR_IS_TOKEN( ')', ENDBRACKET );
    CHAR_IS_TOKEN( '[', STARTBOXBRACKET );
    CHAR_IS_TOKEN( ']', ENDBOXBRACKET );
    CHAR_IS_TOKEN( '|', PIPE );
    CHAR_IS_TOKEN( ',', COMMA );
    CHAR_IS_TOKEN( '=', EQUAL );
    CHAR_IS_TOKEN_OR_TOKEN( '+', '=', PLUS, PLUS_EQUAL );
    CHAR_IS_TOKEN( '*', STAR );
    CHAR_IS_TOKEN( '/', DIVIDE );
    CHAR_IS_TOKEN_OR_TOKEN( '!', '=', EXCLAMATION, DIFFERENT)
    CHAR_IS_TOKEN_OR_TOKEN( ':', ':', COLON, COLONCOLON);
    CHAR_IS_TOKEN_OR_TOKEN( '-', '>', MINUS, RIGHT_ARROW);
    CHAR_IS_TOKEN_OR_TOKEN( '>', '=', SUPERIOR, SUPERIOR_EQUAL);
    CHAR_IS_TOKEN_OR_TOKEN_OR_TOKEN_OR_TOKEN( '<', '-', '=', '>', INFERIOR, LEFT_ARROW, INFERIOR_EQUAL, DIFFERENT);
  }
  if( lastChar > 128 ) return next_token();
  identifierStr = lastChar;
  std::cerr << "Unknown token : " << lastChar << " '" << identifierStr << "' at " << initial_line << "," << initial_col << " eof is" << eof() << std::endl;
  return token(token_type::UNKNOWN, initial_line, initial_col);
}

int lexer::get_next_non_separator_char()
{
  int lastChar = ' ';
  while( not eof() and isspace(lastChar = get_next_char() )  )
  { // Ignore space
  }
  return lastChar;
}

int lexer::get_next_char()
{
  d->lastChar = d->stream->get();
  if( d->lastChar == '\n' )
  {
    ++d->line;
    ++d->followingnewline;
    d->col = 1;
  } else {
    ++d->col;
    d->followingnewline = 0;
  }
  return d->lastChar;
}

void lexer::unget()
{
  --d->col;
  d->stream->unget();
  if( d->followingnewline > 0 )
  {
    --d->followingnewline;
    --d->line;
  }
}

bool lexer::eof() const
{
  return !d->stream->good();
}

int lexer::line() const
{
  return d->line;
}
int lexer::column() const
{
  return d->col;
}

bool lexer::ignore_comment(token& _token, int _lastChar )
{
  if( _lastChar == '/' )
  {
    int initial_line = line();
    int initial_col = column();
    int nextChar = get_next_char();
    if( nextChar == '/' )
    { // Mono line comment
      while( not eof() and get_next_char() != '\n' )
      {
      }
      _token = next_token();
      return true;
    } else {
      unget();
    }
  }
  return false;
}

std::string lexer::get_identifier(int lastChar)
{
  std::string identifierStr;
  if( lastChar != 0) {
    identifierStr = lastChar;
  }
  while (not eof() )
  {
    lastChar = get_next_char();
    if( std::isalnum(lastChar) or lastChar == '_')
    {
      identifierStr += lastChar;
    } else {
      unget();
      break;
    }
  }
  return identifierStr;
}

token lexer::get_string(int lastChar)
{
  int initial_col = column();
  std::string identifierStr = "";
  while( not eof() )
  {
    int nextChar = get_next_char();
    if(nextChar == '\\')
    {
      nextChar = get_next_char();
      identifierStr += nextChar;
    } else if( nextChar == lastChar)
    {
      return token(token_type::STRING, identifierStr, line(), initial_col );
    } else {
      identifierStr += nextChar;
    }
  }
  return token(token_type::UNFINISHED_STRING, line(), initial_col);
}
//END oc/lexer.cpp
//BEGIN oc/parser.cpp











using namespace gqlite::oc;

struct parser::data
{
  lexer* lex;
  token tok;

  algebra::node_csp parse_create();
  algebra::node_csp parse_match();
  algebra::node_csp parse_return();
  algebra::node_csp parse_with();
  algebra::node_csp parse_delete();
  algebra::node_csp parse_set();
  algebra::node_csp parse_remove();
  std::vector<algebra::alternative<algebra::graph_node, algebra::graph_edge>> parse_patterns(bool _allow_undirected_edge, bool _allow_multiple_edge_labels, bool _require_one_label);
  std::unordered_map<std::string, algebra::node_csp> parse_properties();
  algebra::node_csp parse_expression();
  algebra::node_csp parse_unary_expression();
  algebra::node_csp parse_multiplicative_expression();
  algebra::node_csp parse_additive_expression();
  algebra::node_csp parse_relational_expression();
  algebra::node_csp parse_conditional_and_expression();
  algebra::node_csp parse_member_expression();
  algebra::node_csp parse_terminal_expression();
  algebra::node_csp parse_expression_list();
  std::vector<std::string> parse_path();
  std::vector<algebra::named_expression_csp> parse_named_expressions();
  void validate(algebra::graph_node_csp);
  void validate(algebra::graph_edge_csp);
  void get_next_token();
  template<typename... _T_>
  [[noreturn]] void report_error(const token& _token, const std::string& _errorMsg, const _T_&... _values);
  [[noreturn]] void report_unexpected(const token& _token);
  bool is_of_type(const token& _token, token_type _type);
  bool is_of_type(token_type _type);

  std::unordered_map<std::string, algebra::node_csp> bounded_variables;
};

namespace gqlite::details
{
  template<>
  inline std::string to_string<token_type>(const token_type& _v)
  {
    return token_type_to_string(_v);
  }
}

void parser::data::validate(algebra::graph_node_csp _node)
{
  if(_node->get_variable().empty()) return;
  auto it = bounded_variables.find(_node->get_variable());
  if(it == bounded_variables.end())
  {
    bounded_variables[_node->get_variable()] = _node;
    return;
  }
  if(_node->get_labels().empty() and not _node->get_properties()) return;
  if(_node->equals(it->second)) return;
  report_error(tok, "Variable {} is already bound.", _node->get_variable());
}

void parser::data::validate(algebra::graph_edge_csp _node)
{
  validate(_node->get_source());
  validate(_node->get_destination());
  if(_node->get_variable().empty()) return;
  auto it = bounded_variables.find(_node->get_variable());
  if(it == bounded_variables.end())
  {
    bounded_variables[_node->get_variable()] = _node;
    return;
  }
  if(_node->get_labels().empty() and not _node->get_properties()) return;
  if(_node->equals(it->second)) return;
  report_error(tok, "Variable {} is already bound.", _node->get_variable());
}

algebra::node_csp parser::data::parse_create()
{
  get_next_token();
  return std::make_shared<algebra::create>(parse_patterns(false, false, true));
}

algebra::node_csp parser::data::parse_match()
{
  get_next_token();
  std::vector<algebra::alternative<algebra::graph_node, algebra::graph_edge>> patterns = parse_patterns(true, true, false);
  algebra::node_csp where;
  if(tok.type == token_type::WHERE)
  {
    get_next_token();
    where = parse_expression();
  }
  return std::make_shared<algebra::match>(patterns, where);
}

std::vector<algebra::named_expression_csp> parser::data::parse_named_expressions()
{
  std::vector<algebra::named_expression_csp> expressions;
  std::vector<std::string> labels;
  do
  {
    std::string name;
    if(tok.type == token_type::IDENTIFIER)
    {
      name = tok.string;
    }
    algebra::node_csp node = parse_expression();
    if(tok.type == token_type::AS)
    {
      get_next_token();
      is_of_type(token_type::IDENTIFIER);
      name = tok.string;
      get_next_token();
    } else if(node->get_type() != algebra::node_type::variable)
    {
      name = algebra::visitors::stringify().start(node);
    }
    if(std::find(labels.begin(), labels.end(), name) != labels.end())
    {
      report_error(tok, "Duplicate column name {} is not allowed.");
    }
    labels.push_back(name);
    expressions.push_back(std::make_shared<algebra::named_expression>(name, node));
    if(tok.type == token_type::COMMA)
    {
      get_next_token();
    } else {
      break;
    }
  } while(true);
  return expressions;
}

algebra::node_csp parser::data::parse_with()
{
  get_next_token();
  if(tok.type == token_type::STAR)
  {
    get_next_token();
    return std::make_shared<algebra::with>(true, std::vector<algebra::named_expression_csp>());
  }
  return std::make_shared<algebra::with>(false, parse_named_expressions());
}

algebra::node_csp parser::data::parse_delete()
{
  bool detach = (tok.type == token_type::DETACH);
  if(detach) get_next_token();
  is_of_type(token_type::DELETE);
  get_next_token();
  std::vector<algebra::node_csp> expressions;
  while(tok.type != token_type::END_OF_FILE)
  {
    expressions.push_back(parse_expression());
    if(tok.type == token_type::COMMA)
    {
      get_next_token();
    } else
    {
      break;
    }
  }
  return std::make_shared<algebra::delete_statement>(detach, expressions);
}

algebra::node_csp parser::data::parse_set()
{
  std::vector<algebra::node_csp> nodes;
  get_next_token();
  while(tok.type != token_type::END_OF_FILE)
  {
    std::string name;
    if(tok.type == token_type::STARTBRACKET)
    {
      get_next_token();
      is_of_type(token_type::IDENTIFIER);
      name = tok.string;
      get_next_token();
      is_of_type(token_type::ENDBRACKET);
    } else {
      is_of_type(token_type::IDENTIFIER);
      name = tok.string;
    }
    get_next_token();
    std::vector<std::string> path = parse_path();

    switch(tok.type)
    {
      case token_type::EQUAL:
      {
        get_next_token();
        algebra::node_csp value = parse_expression();
        nodes.push_back(std::make_shared<algebra::set_property>(std::make_shared<algebra::member_access>(name, path), value));
        break;
      }
      case token_type::PLUS_EQUAL:
      {
        get_next_token();
        algebra::node_csp value = parse_expression();
        nodes.push_back(std::make_shared<algebra::add_property>(std::make_shared<algebra::member_access>(name, path), value));
        break;
      }
      case token_type::COLON:
      {
        if(not path.empty())
        {
          report_unexpected(tok);
        }
        std::vector<std::string> labels;
        while(tok.type == token_type::COLON)
        {
          get_next_token();
          is_of_type(token_type::IDENTIFIER);
          labels.push_back(tok.string);
          get_next_token();
        }
        nodes.push_back(std::make_shared<algebra::edit_labels>(name, labels));
        break;
      }
      default:
        report_unexpected(tok);
    }

    if(tok.type == token_type::COMMA)
    {
      get_next_token();
    } else {
      break;
    }
  }
  return std::make_shared<algebra::set>(nodes);
}

algebra::node_csp parser::data::parse_remove()
{
  std::vector<algebra::node_csp> nodes;
  get_next_token();
  while(tok.type != token_type::END_OF_FILE)
  {
    std::string name;
    if(tok.type == token_type::STARTBRACKET)
    {
      get_next_token();
      is_of_type(token_type::IDENTIFIER);
      name = tok.string;
      get_next_token();
      is_of_type(token_type::ENDBRACKET);
    } else {
      is_of_type(token_type::IDENTIFIER);
      name = tok.string;
    }
    get_next_token();
    std::vector<std::string> path = parse_path();

    switch(tok.type)
    {
      case token_type::COLON:
      {
        if(not path.empty())
        {
          report_unexpected(tok);
        }
        std::vector<std::string> labels;
        while(tok.type == token_type::COLON)
        {
          get_next_token();
          is_of_type(token_type::IDENTIFIER);
          labels.push_back(tok.string);
          get_next_token();
        }
        nodes.push_back(std::make_shared<algebra::edit_labels>(name, labels));
        break;
      }
      default:
        nodes.push_back(std::make_shared<algebra::remove_property>(std::make_shared<algebra::member_access>(name, path)));
        break;
    }

    if(tok.type == token_type::COMMA)
    {
      get_next_token();
    } else {
      break;
    }
  }
  return std::make_shared<algebra::remove>(nodes);
}

algebra::node_csp parser::data::parse_return()
{
  get_next_token();
  if(tok.type == token_type::STAR)
  {
    get_next_token();
    return std::make_shared<algebra::return_statement>(true, std::vector<algebra::named_expression_csp>());
  }
  return std::make_shared<algebra::return_statement>(false, parse_named_expressions());
}

namespace
{
  struct edge
  {
    bool active = false;
    algebra::edge_directivity directivity;
    algebra::graph_node_csp source, destination;
    std::string variable;
    std::vector<std::string> labels;
    algebra::map_csp properties;
  };
}

std::vector<algebra::alternative<algebra::graph_node, algebra::graph_edge>> parser::data::parse_patterns(bool _allow_undirected_edge, bool _allow_multiple_edge_labels, bool _require_one_label)
{
  std::vector<algebra::alternative<algebra::graph_node, algebra::graph_edge>> patterns;
  edge current_edge;
  do
  {
    is_of_type(token_type::STARTBRACKET);
    std::vector<algebra::graph_node_csp> nodes;
    get_next_token();
    // identifier
    std::string variable;
    if(tok.type == token_type::IDENTIFIER)
    { 
      variable = tok.string;
      get_next_token();
    }
    // Labels
    std::vector<std::string> labels;
    while(tok.type == token_type::COLON)
    {
      get_next_token();
      is_of_type(token_type::IDENTIFIER);
      labels.push_back(tok.string);
      get_next_token();
    }
    // Properties
    algebra::map_csp properties;
    if(tok.type == token_type::STARTBRACE)
    {
      properties = std::make_shared<algebra::map>(parse_properties());
    }
    // End of node
    is_of_type(token_type::ENDBRACKET);
    get_next_token();
    algebra::graph_node_csp gnode = std::make_shared<algebra::graph_node>(variable, labels, properties);
    // Handle, check if edge is active
    bool add_to_patterns = true;
    if(current_edge.active)
    {
      if(current_edge.source)
      {
        current_edge.destination = gnode;
      } else {
        current_edge.source = gnode;
      }
      algebra::graph_edge_csp ge = std::make_shared<algebra::graph_edge>(current_edge.variable, current_edge.source, current_edge.destination, current_edge.directivity, current_edge.labels, current_edge.properties);
      validate(ge);
      patterns.push_back(ge);
      current_edge.active = false;
      add_to_patterns = false;
    }
    // If comma, an other node statement comes after
    if(tok.type == token_type::COMMA)
    {
      get_next_token();
    }
    else if(tok.type == token_type::MINUS or tok.type == token_type::LEFT_ARROW)
    {
      // Reset current edge
      current_edge = edge{};
      current_edge.active = true;
      // Handle directivity
      if(tok.type == token_type::MINUS)
      {
        current_edge.source = gnode;
        current_edge.directivity = algebra::edge_directivity::undirected;
      } else {
        current_edge.destination = gnode;
        current_edge.directivity = algebra::edge_directivity::directed;
      }
      // Parse
      get_next_token();
      if(tok.type == token_type::RIGHT_ARROW or tok.type == token_type::MINUS)
      {
        if(_require_one_label)
        {
          is_of_type(token_type::STARTBOXBRACKET);
        }
      } else {
        is_of_type(token_type::STARTBOXBRACKET);
        get_next_token();
        // parse edge
        if(tok.type == token_type::IDENTIFIER)
        { // identifier
          current_edge.variable = tok.string;
          get_next_token();
        }
        if(tok.type == token_type::COLON)
        { // label
          get_next_token();
          is_of_type(token_type::IDENTIFIER);
          current_edge.labels.push_back(tok.string);
          get_next_token();
          if(_allow_multiple_edge_labels)
          {
            while(tok.type != token_type::END_OF_FILE)
            {
              if(tok.type == token_type::PIPE)
              {
                get_next_token();
                if(tok.type == token_type::COLON)
                {
                  get_next_token();
                }
                is_of_type(token_type::IDENTIFIER);
                if(std::find(current_edge.labels.begin(), current_edge.labels.end(), tok.string) == current_edge.labels.end())
                {
                  current_edge.labels.push_back(tok.string);
                }
                get_next_token();
              } else {
                break;
              }
            }
          }
        } else if(_require_one_label) {
          is_of_type(token_type::COLON); //
        }
        if(tok.type == token_type::STARTBRACE)
        {
          current_edge.properties = std::make_shared<algebra::map>(parse_properties());
        }
        is_of_type(token_type::ENDBOXBRACKET);
        get_next_token();
      }
      if(tok.type == token_type::MINUS)
      {
        if(current_edge.directivity == algebra::edge_directivity::undirected)
        {
          if(not _allow_undirected_edge)
          {
            report_error(tok, "Edge must be directed during creation,");
          }
        }
      } else if(tok.type == token_type::RIGHT_ARROW)
      {
        if(current_edge.directivity == algebra::edge_directivity::directed)
        {
          report_error(tok, "Edge cannot have both direction.");
        }
        current_edge.directivity = algebra::edge_directivity::directed;
      } else {
        report_unexpected(tok);
      }
      get_next_token();
      is_of_type(token_type::STARTBRACKET);
      add_to_patterns = false;
    } else {
      if(add_to_patterns) { validate(gnode); patterns.push_back(gnode); }
      break;
    }
    if(add_to_patterns) { validate(gnode); patterns.push_back(gnode); }
  } while(true);
  if(current_edge.active)
  {
    report_error(tok, "Unfinished edge");
  }
  return patterns;
}

std::unordered_map<std::string, algebra::node_csp> parser::data::parse_properties()
{
  std::unordered_map<std::string, algebra::node_csp> p;
  is_of_type(token_type::STARTBRACE);
  get_next_token();
  while(tok.type != token_type::ENDBRACE)
  {
    if(tok.type != token_type::STRING and tok.type != token_type::IDENTIFIER)
    {
      report_unexpected(tok);
    }
    std::string key = tok.string;
    get_next_token();
    is_of_type(token_type::COLON);
    get_next_token();
    p[key] = parse_expression();
    if(tok.type == token_type::COMMA)
    {
      get_next_token();
    } else {
      break;
    }
  }
  is_of_type(token_type::ENDBRACE);
  get_next_token();
  return p;
}

algebra::node_csp parser::data::parse_terminal_expression()
{
  token t = tok;
  switch (tok.type)
  {
  case token_type::NULL_TOKEN:
    get_next_token();
    return std::make_shared<algebra::value>(gqlite::value());
  case token_type::IDENTIFIER:
    get_next_token();
    switch(tok.type)
    {
      case token_type::STARTBRACKET:
      {
        std::vector<algebra::node_csp> arguments;
        get_next_token();
        if(tok.type != token_type::ENDBRACKET)
        {
          while(tok.type != token_type::END_OF_FILE)
          {
            arguments.push_back(parse_expression());
            if(tok.type == token_type::COMMA)
            {
              get_next_token();
            } else {
              break;
            }
          }
          is_of_type(token_type::ENDBRACKET);
        }
        get_next_token();
        return std::make_shared<algebra::function_call>(t.string, arguments);
      }
      case token_type::COLON:
      {
        std::vector<std::string> labels;
        get_next_token();
        while(tok.type == token_type::IDENTIFIER)
        {
          labels.push_back(tok.string);
          get_next_token();
          if(tok.type == token_type::COLON)
          {
            get_next_token();
          } else {
            break;
          }
        }
        return std::make_shared<algebra::has_labels>(t.string, labels);
      }
      default:
        return std::make_shared<algebra::variable>(t.string);
    }
  case token_type::STRING:
    get_next_token();
    return std::make_shared<algebra::value>(t.string);
  case token_type::INTEGER:
    get_next_token();
    try
    {
      return std::make_shared<algebra::value>(std::stoi(t.string));
    } catch(std::out_of_range)
    {
      throw gqlite::exception("Integer {} is too large.", t.string);
    }
  case token_type::FLOATING_POINT:
    get_next_token();
    return std::make_shared<algebra::value>(std::stod(t.string));
  case token_type::TRUE:
    get_next_token();
    return std::make_shared<algebra::value>(true);
  case token_type::FALSE:
    get_next_token();
    return std::make_shared<algebra::value>(false);
  case token_type::STARTBRACE:
  {
    return std::make_shared<algebra::map>(parse_properties());
  }
  case token_type::STARTBOXBRACKET:
  {
    return parse_expression_list();
  }
  default:
    report_unexpected(tok);
  }
}

algebra::node_csp parser::data::parse_expression_list()
{
  std::vector<algebra::node_csp> nodes;
  get_next_token();
  while(tok.type != token_type::END_OF_FILE)
  {
    nodes.push_back(parse_expression());
    if(tok.type == token_type::COMMA)
    {
      get_next_token();
    } else {
      is_of_type(token_type::ENDBOXBRACKET);
      get_next_token();
      return std::make_shared<algebra::array>(nodes);
    }
  }
  report_unexpected(tok);
}

algebra::node_csp parser::data::parse_conditional_and_expression()
{
  algebra::node_csp node = parse_relational_expression();
  if(tok.type == token_type::AND)
  {
    get_next_token();
    return std::make_shared<algebra::logical_and>(node, parse_expression());
  } else {
    return node;
  }
}

algebra::node_csp parser::data::parse_relational_expression()
{
  algebra::node_csp node = parse_additive_expression();
  switch(tok.type)
  {
    case token_type::IS:
    {
      get_next_token();
      if(tok.type == token_type::NOT)
      {
        get_next_token();
        is_of_type(token_type::NULL_CAPS);
        get_next_token();
        return std::make_shared<algebra::is_not_null>(node);
      } else {
        is_of_type(token_type::NULL_CAPS);
        get_next_token();
        return std::make_shared<algebra::is_null>(node);
      }
    }
    case token_type::EQUAL:
      get_next_token();
      return std::make_shared<algebra::relational_equal>(node, parse_additive_expression());
    case token_type::DIFFERENT:
      get_next_token();
      return std::make_shared<algebra::relational_different>(node, parse_additive_expression());
    case token_type::INFERIOR:
      get_next_token();
      return std::make_shared<algebra::relational_inferior>(node, parse_additive_expression());
    case token_type::SUPERIOR:
      get_next_token();
      return std::make_shared<algebra::relational_superior>(node, parse_additive_expression());
    case token_type::INFERIOR_EQUAL:
      get_next_token();
      return std::make_shared<algebra::relational_inferior_equal>(node, parse_additive_expression());
    case token_type::SUPERIOR_EQUAL:
      get_next_token();
      return std::make_shared<algebra::relational_superior_equal>(node, parse_additive_expression());
    case token_type::IN:
      get_next_token();
      return std::make_shared<algebra::relational_in>(node, parse_expression_list());
    case token_type::NOT:
      get_next_token();
      if(is_of_type(tok, token_type::IN))
      {
        get_next_token();
      }
      return std::make_shared<algebra::relational_not_in>(node, parse_expression_list());
    default:
      return node;
  }
}

algebra::node_csp parser::data::parse_additive_expression()
{
  algebra::node_csp node = parse_multiplicative_expression();
  switch(tok.type)
  {
    case token_type::PLUS:
      get_next_token();
      return std::make_shared<algebra::addition>(node, parse_multiplicative_expression());
    case token_type::MINUS:
      get_next_token();
      return std::make_shared<algebra::substraction>(node, parse_multiplicative_expression());
    default:
      return node;
  }
}

algebra::node_csp parser::data::parse_multiplicative_expression()
{
  algebra::node_csp node = parse_unary_expression();
  switch(tok.type)
  {
    case token_type::STAR:
      get_next_token();
      return std::make_shared<algebra::multiplication>(node, parse_unary_expression());
    case token_type::DIVIDE:
      get_next_token();
      return std::make_shared<algebra::division>(node, parse_unary_expression());
    default:
      return node;
  }
}

algebra::node_csp parser::data::parse_unary_expression()
{
  switch(tok.type)
  {
    case token_type::EXCLAMATION:
      get_next_token();
      return std::make_shared<algebra::logical_negation>(parse_member_expression());
    case token_type::MINUS:
      get_next_token();
      return std::make_shared<algebra::negation>(parse_member_expression());
    case token_type::PLUS:
      get_next_token();
      [[fallthrough]];
    default:
      return parse_member_expression();
  }
}

std::vector<std::string> parser::data::parse_path()
{
  std::vector<std::string> path;
  while (tok.type == token_type::DOT)
  {
    get_next_token();
    is_of_type(token_type::IDENTIFIER);
    path.push_back(tok.string);
    get_next_token();
  }
  return path;
}

algebra::node_csp parser::data::parse_member_expression()
{
  algebra::node_csp left = parse_terminal_expression();
  if(tok.type == token_type::DOT)
  {
    if(left->get_type() != algebra::node_type::variable)
    {
      report_unexpected(tok);
    }
    std::string left_identif = std::static_pointer_cast<const algebra::variable>(left)->get_identifier();
    return std::make_shared<algebra::member_access>(left_identif, parse_path());
  }
  return left;
}

algebra::node_csp parser::data::parse_expression()
{
  algebra::node_csp node = parse_conditional_and_expression();
  if(tok.type == token_type::OR)
  {
    get_next_token();
    return std::make_shared<algebra::logical_or>(node, parse_expression());
  } else {
    return node;
  }
}

void parser::data::get_next_token()
{
  tok = lex->next_token();
}

template<typename... _T_>
void parser::data::report_error(const token& _token, const std::string& _errorMsg, const _T_&... _values)
{
  throw gqlite::exception("{}:{}:" + _errorMsg, _token.line, _token.column, _values...);
}

void parser::data::report_unexpected(const token& _token) 
{
  if(_token.string.empty())
  {
    report_error(_token, "Unexpected token {}", _token.type);
  } else {
    report_error(_token, "Unexpected token {} ({})", _token.type, _token.string);
  }
}

bool parser::data::is_of_type(const token& _token, token_type _type)
{
  if(_token.type == _type) return true;
  if(_token.string.empty())
  {
    report_error(_token, "Expected token {} got {}", _type, _token.type);
  } else {
    report_error(_token, "Expected token {} got {} ({})", _type, _token.type, _token.string);
  }
  return false;
}

bool parser::data::is_of_type(token_type _type)
{
  return is_of_type(tok, _type);
}

parser::parser(lexer* _lexer) : d(new data)
{
  d->lex = _lexer;
}

parser::~parser()
{
  delete d;
}

algebra::node_csp parser::parse()
{
  std::vector<algebra::node_csp> nodes;
  d->get_next_token();
  while(d->tok.type != token_type::END_OF_FILE)
  {
    switch(d->tok.type)
    {
    case token_type::CREATE:
      nodes.push_back(d->parse_create());
      break;
    case token_type::MATCH:
      nodes.push_back(d->parse_match());
      break;
    case token_type::WITH:
      nodes.push_back(d->parse_with());
      break;
    case token_type::DELETE:
    case token_type::DETACH:
      nodes.push_back(d->parse_delete());
      break;
    case token_type::SET:
      nodes.push_back(d->parse_set());
      break;
    case token_type::REMOVE:
      nodes.push_back(d->parse_remove());
      break;
    case token_type::RETURN:
      nodes.push_back(d->parse_return());
      d->is_of_type(token_type::END_OF_FILE);
      break;
    default:
      d->report_unexpected(d->tok);
    }
  }
  switch(nodes.size())
  {
  case 0:
    d->report_error(token(), "Empty query.");
  case 1:
    return nodes.front();
  default:
    return std::make_shared<algebra::statements>(nodes);
  }
}

//END oc/parser.cpp
//BEGIN oc/token.cpp


#include <assert.h>
#include <iostream>

using namespace gqlite::oc;

const char* gqlite::oc::token_type_to_string(token_type _type)
{
  switch(_type)
  {
    using enum token_type;
    case UNFINISHED_STRING:
      return "unfinished string";
    case UNFINISHED_COMMENT:
      return "unfinished comment";
    case END_OF_FILE:
      return "end of file";
    case UNKNOWN:
      return "unknown";
    case SEMI:
      return ";";
    case STARTBRACE:
      return "{";
    case ENDBRACE:
      return "}";
    case STARTBRACKET:
      return "(";
    case ENDBRACKET:
      return ")";
    case STARTBOXBRACKET:
      return "[";
    case ENDBOXBRACKET:
      return "]";
    case PIPE:
      return "|";
    case MINUS:
      return "-";
    case COMMA:
      return ",";
    case DOT:
      return ".";
    case LEFT_ARROW:
      return "<-";
    case RIGHT_ARROW:
      return "->";
    case COLON:
      return ":";
    case COLONCOLON:
      return "::";
    case EQUAL:
      return "=";
    case DIFFERENT:
      return "!=";
    case SUPERIOR:
      return ">";
    case INFERIOR:
      return "<";
    case SUPERIOR_EQUAL:
      return ">=";
    case INFERIOR_EQUAL:
      return "<=";
    case PLUS:
      return "+";
    case STAR:
      return "*";
    case DIVIDE:
      return "/";
    case EXCLAMATION:
      return "!";
    case PLUS_EQUAL:
      return "+=";
    case IDENTIFIER:
      return "identifier";
    case STRING:
      return "string";
#define TOKEN_KEYWORD(_K_)  \
    case _K_:               \
      return # _K_;
#define TOKEN_KEYWORD2(_K_, _S_)  \
    case _K_:                     \
      return _S_;
    TOKEN_KEYWORD(AND)
TOKEN_KEYWORD(AS)
TOKEN_KEYWORD(CREATE)
TOKEN_KEYWORD(DELETE)
TOKEN_KEYWORD(DETACH)
TOKEN_KEYWORD2(FALSE, "false")
TOKEN_KEYWORD(IN)
TOKEN_KEYWORD(IS)
TOKEN_KEYWORD(NOT)
TOKEN_KEYWORD(MATCH)
TOKEN_KEYWORD2(NULL_CAPS, "NULL")
TOKEN_KEYWORD2(NULL_TOKEN, "null")
TOKEN_KEYWORD(OR)
TOKEN_KEYWORD(REMOVE)
TOKEN_KEYWORD(RETURN)
TOKEN_KEYWORD(SET)
TOKEN_KEYWORD2(TRUE, "true")
TOKEN_KEYWORD(XOR)
TOKEN_KEYWORD(WITH)
TOKEN_KEYWORD(WHERE)

#undef TOKEN_KEYWORD
#undef TOKEN_KEYWORD2
  }
  std::cerr << "add the token" << (int)_type << std::endl;
  std::abort();
}

token::token() : type(token_type::UNKNOWN)
{

}

token::token(token_type _type, int _line, int _column) : type(_type), line(_line), column(_column)
{
}

token::token(token_type _type, const std::string& _string, int _line, int _column) : type(_type), line(_line), column(_column), string(_string)
{
}
//END oc/token.cpp
//BEGIN oc/algebra/abstract_node_visitor.cpp


using namespace gqlite::oc::algebra::details;

abstract_node_visitor_adaptor::abstract_node_visitor_adaptor()
{

}

abstract_node_visitor_adaptor::~abstract_node_visitor_adaptor()
{

}
//END oc/algebra/abstract_node_visitor.cpp
//BEGIN oc/algebra/node_type.cpp




namespace gqlite::oc::algebra
{
  const char * node_type_name(node_type _nt)
  {
    switch(_nt)
    {
#define OC_ALGEBRA_GENERATE(_KLASS_NAME_, _MEMBER_DEF_) \
      case node_type::_KLASS_NAME_:                              \
        return #_KLASS_NAME_;
// Queries

#define OC_ALGEBRA_CREATE_NODES_MEMBERS(_KLASS_NAME_, F)                          \
  F(_KLASS_NAME_, std::vector<alternative<GQLITE_LIST(graph_node, graph_edge)>>, patterns)

OC_ALGEBRA_GENERATE(create, OC_ALGEBRA_CREATE_NODES_MEMBERS)

#define OC_ALGEBRA_MATCH_NODES_MEMBERS(_KLASS_NAME_, F)                                     \
  F(_KLASS_NAME_, std::vector<alternative<GQLITE_LIST(graph_node, graph_edge)>>, patterns)  \
  F(_KLASS_NAME_, node_csp, where)

OC_ALGEBRA_GENERATE(match, OC_ALGEBRA_MATCH_NODES_MEMBERS)

// Statements

#define OC_ALGEBRA_NODES_LIST_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::vector<node_csp>, nodes)

OC_ALGEBRA_GENERATE(statements, OC_ALGEBRA_NODES_LIST_MEMBERS)

#define OC_ALGEBRA_RETURN_MEMBERS(_KLASS_NAME_, F)                \
  F(_KLASS_NAME_, bool, all)                                      \
  F(_KLASS_NAME_, std::vector<named_expression_csp>, expressions)

OC_ALGEBRA_GENERATE(return_statement, OC_ALGEBRA_RETURN_MEMBERS)

#define OC_ALGEBRA_WITH_MEMBERS(_KLASS_NAME_, F)                  \
  F(_KLASS_NAME_, bool, all)                                      \
  F(_KLASS_NAME_, std::vector<named_expression_csp>, expressions)

OC_ALGEBRA_GENERATE(with, OC_ALGEBRA_WITH_MEMBERS)

#define OC_ALGEBRA_DELETE_MEMBERS(_KLASS_NAME_, F)  \
  F(_KLASS_NAME_, bool, detach)                     \
  F(_KLASS_NAME_, std::vector<node_csp>, expressions)

OC_ALGEBRA_GENERATE(delete_statement, OC_ALGEBRA_DELETE_MEMBERS)

OC_ALGEBRA_GENERATE(set, OC_ALGEBRA_NODES_LIST_MEMBERS)
OC_ALGEBRA_GENERATE(remove, OC_ALGEBRA_NODES_LIST_MEMBERS)

// Set/remove Expressions

#define OC_ALGEBRA_SET_ADD_VALUE_MEMBERS(_KLASS_NAME_, F)                 \
  F(_KLASS_NAME_, member_access_csp, target)                              \
  F(_KLASS_NAME_, node_csp, expression)

OC_ALGEBRA_GENERATE(set_property, OC_ALGEBRA_SET_ADD_VALUE_MEMBERS)


#define OC_ALGEBRA_SET_EQUAL_MEMBERS(_KLASS_NAME_, F)                     \
  F(_KLASS_NAME_, member_access_csp, target)                              \
  F(_KLASS_NAME_, nocde_csp, expression)

OC_ALGEBRA_GENERATE(add_property, OC_ALGEBRA_SET_ADD_VALUE_MEMBERS)

#define OC_ALGEBRA_REMOVE_PROPERTY_MEMBERS(_KLASS_NAME_, F)                   \
  F(_KLASS_NAME_, member_access_csp, target)

OC_ALGEBRA_GENERATE(remove_property, OC_ALGEBRA_REMOVE_PROPERTY_MEMBERS)

#define OC_ALGEBRA_EDIT_LABELS_MEMBERS(_KLASS_NAME_, F)                   \
  F(_KLASS_NAME_, std::string, target)                                    \
  F(_KLASS_NAME_, std::vector<std::string>, labels)

OC_ALGEBRA_GENERATE(edit_labels, OC_ALGEBRA_EDIT_LABELS_MEMBERS)


// Values

#define OC_ALGEBRA_GRAPH_NODE_MEMBERS(_KLASS_NAME_, F)          \
  F(_KLASS_NAME_, std::string, variable)                        \
  F(_KLASS_NAME_, std::vector<std::string>, labels)             \
  F(_KLASS_NAME_, map_csp, properties)

OC_ALGEBRA_GENERATE(graph_node, OC_ALGEBRA_GRAPH_NODE_MEMBERS)

#define OC_ALGEBRA_GRAPH_EDGE_MEMBERS(_KLASS_NAME_, F)          \
  F(_KLASS_NAME_, std::string, variable)                        \
  F(_KLASS_NAME_, graph_node_csp, source)                       \
  F(_KLASS_NAME_, graph_node_csp, destination)                  \
  F(_KLASS_NAME_, edge_directivity, directivity)                \
  F(_KLASS_NAME_, std::vector<std::string>, labels)             \
  F(_KLASS_NAME_, map_csp, properties)

OC_ALGEBRA_GENERATE(graph_edge, OC_ALGEBRA_GRAPH_EDGE_MEMBERS)

#define OC_ALGEBRA_VALUE_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, gqlite::value, value)

OC_ALGEBRA_GENERATE(value, OC_ALGEBRA_VALUE_MEMBERS)


#define OC_ALGEBRA_MAP_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::unordered_map<GQLITE_LIST(std::string, node_csp)>, map)

OC_ALGEBRA_GENERATE(map, OC_ALGEBRA_MAP_MEMBERS)

#define OC_ALGEBRA_ARRAY_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::vector<node_csp>, array)

OC_ALGEBRA_GENERATE(array, OC_ALGEBRA_ARRAY_MEMBERS)


// Expressions

#define OC_ALGEBRA_NAMED_EXPRESSION(_KLASS_NAME_, F)      \
  F(_KLASS_NAME_, std::string, name)                      \
  F(_KLASS_NAME_, node_csp, expression)

OC_ALGEBRA_GENERATE(named_expression, OC_ALGEBRA_NAMED_EXPRESSION)

#define OC_ALGEBRA_VARIABLE_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::string, identifier)

OC_ALGEBRA_GENERATE(variable, OC_ALGEBRA_VARIABLE_MEMBERS)

#define OC_ALGEBRA_MEMBER_ACCESS_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::string, left)                      \
  F(_KLASS_NAME_, std::vector<std::string>, path)

OC_ALGEBRA_GENERATE(member_access, OC_ALGEBRA_MEMBER_ACCESS_MEMBERS)

#define OC_ALGEBRA_HAS_LABELS_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::string, left)                   \
  F(_KLASS_NAME_, std::vector<std::string>, labels)

OC_ALGEBRA_GENERATE(has_labels, OC_ALGEBRA_HAS_LABELS_MEMBERS)

#define OC_ALGEBRA_FUNCTION_CALL_EXPRESSION(_KLASS_NAME_, F)  \
  F(_KLASS_NAME_, std::string, name)                          \
  F(_KLASS_NAME_, std::vector<node_csp>, arguments)

OC_ALGEBRA_GENERATE(function_call, OC_ALGEBRA_FUNCTION_CALL_EXPRESSION)

#define OC_ALGEBRA_BINARY_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, node_csp, left)                  \
  F(_KLASS_NAME_, node_csp, right)

OC_ALGEBRA_GENERATE(logical_and, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(logical_or, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_equal, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_different, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_inferior, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_superior, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_inferior_equal, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_superior_equal, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_in, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_not_in, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(addition, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(substraction, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(multiplication, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(division, OC_ALGEBRA_BINARY_MEMBERS)

#define OC_ALGEBRA_UNARY_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, node_csp, value)

OC_ALGEBRA_GENERATE(logical_negation, OC_ALGEBRA_UNARY_MEMBERS)
OC_ALGEBRA_GENERATE(negation, OC_ALGEBRA_UNARY_MEMBERS)
OC_ALGEBRA_GENERATE(is_null, OC_ALGEBRA_UNARY_MEMBERS)
OC_ALGEBRA_GENERATE(is_not_null, OC_ALGEBRA_UNARY_MEMBERS)

#undef OC_ALGEBRA_GENERATE
      case node_type::__Invalid__:
        return "__Invalid__";
    }
    gqlite_fatal("impossible");
  }
}

//END oc/algebra/node_type.cpp
//BEGIN oc/algebra/node.cpp


using namespace gqlite::oc::algebra;

node::node(node_type _type, node::data* _d) : d(_d)
{
  d->type = _type;
}

node::~node()
{
  delete d;
}

node_type node::get_type() const
{
  return d->type;
}
//END oc/algebra/node.cpp
//BEGIN oc/algebra/nodes.cpp






namespace gqlite::oc::algebra::details
{
  template<typename _T_>
  bool equals(const _T_& _a, const _T_& _b) requires std::same_as<_T_, gqlite::value> or std::same_as<_T_, std::string> or std::same_as<_T_, edge_directivity> or std::is_integral_v<_T_> or std::is_floating_point_v<_T_>
  {
    return _a == _b;
  }
  template<typename _T_>
  bool equals(const std::shared_ptr<const _T_>& _a, const std::shared_ptr<const _T_>& _b) requires std::derived_from<_T_, node>
  {
    return (_a == _b) or (_a and _b and _a->equals(_b));
  }
  template<typename... _T_>
  bool equals(const alternative<_T_...>& _a, const alternative<_T_...>& _b)
  {
    return _a.get_node()->equals(_b.get_node());
  }
  template<typename _T1_, typename _T2_>
  bool equals(const std::pair<_T1_, _T2_>& _a, const std::pair<_T1_, _T2_>& _b)
  {
    return equals(_a.first, _b.first) and equals(_a.second, _b.second);
  }
  template<typename _T_>
  bool equals(const std::vector<_T_>& _a, const std::vector<_T_>& _b)
  {
    if(_a.size() != _b.size()) return false;
    for(int i = 0; i < _a.size(); ++i)
    {
      if(not equals(_a[i], _b[i])) return false;
    }
    return true;
  }
  template<typename _T_>
  bool equals(const std::unordered_map<std::string, _T_>& _a, const std::unordered_map<std::string, _T_>& _b)
  {
    if(_a.size() != _b.size()) return false;
    for(auto const& [k,v] : _a)
    {
      auto it = _b.find(k);
      if(it == _b.end() or not equals(v, it->second)) return false;
    }
    return true;
  }
}

namespace gqlite::oc::algebra {

#define OC_ALGEBRA_GENERATE_PRIVATE_MEMBER(_KLASS_NAME_, _TYPE_, _NAME_) \
  _TYPE_ m_ ## _NAME_;

#define OC_ALGEBRA_GENERATE_ACCESSOR_DEFINITION(_KLASS_NAME_, _TYPE_, _NAME_)   \
  _TYPE_ _KLASS_NAME_::get_ ## _NAME_() const                                   \
  {                                                                             \
    return static_cast<data*>(d)->m_ ## _NAME_;                                 \
  }

#define GQL_GENERATE_DESTRUCTOR(_KLASS_NAME_, _TYPE_, _NAME_)
  
#define OC_ALGEBRA_GENERATE_ASSIGNMENT(_KLASS_NAME_, _TYPE_, _NAME_)  \
  static_cast<data*>(d)->m_ ## _NAME_ = _ ## _NAME_;

#define OC_ALGEBRA_GENERATE_COMPARISON(_KLASS_NAME_, _TYPE_, _NAME_)  \
  and details::equals(static_cast<data*>(d)->m_ ## _NAME_, static_cast<data*>(rhs->d)->m_ ## _NAME_)

#define OC_ALGEBRA_GENERATE(_KLASS_NAME_, _MEMBER_DEF_)                                                                                                 \
struct _KLASS_NAME_::data : public node::data                                                                                                           \
{                                                                                                                                                       \
  _MEMBER_DEF_(_KLASS_NAME_, OC_ALGEBRA_GENERATE_PRIVATE_MEMBER)                                                                                        \
};                                                                                                                                                      \
                                                                                                                                                        \
_KLASS_NAME_::_KLASS_NAME_(_MEMBER_DEF_(_KLASS_NAME_, OC_ALGEBRA_GENERATE_CONSTRUCTOR_ARGUMENT) void* _)                                                \
  : node(node_type::_KLASS_NAME_, new data)                                                                                                             \
{                                                                                                                                                       \
  GQLITE_UNUSED(_);                                                                                                                                     \
  _MEMBER_DEF_(_KLASS_NAME_, OC_ALGEBRA_GENERATE_ASSIGNMENT)                                                                                            \
}                                                                                                                                                       \
                                                                                                                                                        \
_KLASS_NAME_::~_KLASS_NAME_()                                                                                                                           \
{                                                                                                                                                       \
  _MEMBER_DEF_(_KLASS_NAME_, GQL_GENERATE_DESTRUCTOR)                                                                                                   \
}                                                                                                                                                       \
                                                                                                                                                        \
bool _KLASS_NAME_::equals(const std::shared_ptr<const node>& _node) const                                                                               \
{                                                                                                                                                       \
  if(_node->get_type() != node_type::_KLASS_NAME_) return false;                                                                                        \
  _KLASS_NAME_ ## _csp rhs = std::static_pointer_cast<const _KLASS_NAME_>(_node);                                                                       \
  return true _MEMBER_DEF_(_KLASS_NAME_, OC_ALGEBRA_GENERATE_COMPARISON);                                                                               \
}                                                                                                                                                       \
                                                                                                                                                        \
_MEMBER_DEF_(_KLASS_NAME_, OC_ALGEBRA_GENERATE_ACCESSOR_DEFINITION)                                                                                     \
                                                                                                                                                        \
void _KLASS_NAME_::accept(details::abstract_node_visitor_adaptor* _visitor_adaptor, void* _r, void* _parameter) const                                   \
{                                                                                                                                                       \
  return _visitor_adaptor->call_visit(std::static_pointer_cast<const _KLASS_NAME_>(shared_from_this()), _r, _parameter);                                \
}

// Queries

#define OC_ALGEBRA_CREATE_NODES_MEMBERS(_KLASS_NAME_, F)                          \
  F(_KLASS_NAME_, std::vector<alternative<GQLITE_LIST(graph_node, graph_edge)>>, patterns)

OC_ALGEBRA_GENERATE(create, OC_ALGEBRA_CREATE_NODES_MEMBERS)

#define OC_ALGEBRA_MATCH_NODES_MEMBERS(_KLASS_NAME_, F)                                     \
  F(_KLASS_NAME_, std::vector<alternative<GQLITE_LIST(graph_node, graph_edge)>>, patterns)  \
  F(_KLASS_NAME_, node_csp, where)

OC_ALGEBRA_GENERATE(match, OC_ALGEBRA_MATCH_NODES_MEMBERS)

// Statements

#define OC_ALGEBRA_NODES_LIST_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::vector<node_csp>, nodes)

OC_ALGEBRA_GENERATE(statements, OC_ALGEBRA_NODES_LIST_MEMBERS)

#define OC_ALGEBRA_RETURN_MEMBERS(_KLASS_NAME_, F)                \
  F(_KLASS_NAME_, bool, all)                                      \
  F(_KLASS_NAME_, std::vector<named_expression_csp>, expressions)

OC_ALGEBRA_GENERATE(return_statement, OC_ALGEBRA_RETURN_MEMBERS)

#define OC_ALGEBRA_WITH_MEMBERS(_KLASS_NAME_, F)                  \
  F(_KLASS_NAME_, bool, all)                                      \
  F(_KLASS_NAME_, std::vector<named_expression_csp>, expressions)

OC_ALGEBRA_GENERATE(with, OC_ALGEBRA_WITH_MEMBERS)

#define OC_ALGEBRA_DELETE_MEMBERS(_KLASS_NAME_, F)  \
  F(_KLASS_NAME_, bool, detach)                     \
  F(_KLASS_NAME_, std::vector<node_csp>, expressions)

OC_ALGEBRA_GENERATE(delete_statement, OC_ALGEBRA_DELETE_MEMBERS)

OC_ALGEBRA_GENERATE(set, OC_ALGEBRA_NODES_LIST_MEMBERS)
OC_ALGEBRA_GENERATE(remove, OC_ALGEBRA_NODES_LIST_MEMBERS)

// Set/remove Expressions

#define OC_ALGEBRA_SET_ADD_VALUE_MEMBERS(_KLASS_NAME_, F)                 \
  F(_KLASS_NAME_, member_access_csp, target)                              \
  F(_KLASS_NAME_, node_csp, expression)

OC_ALGEBRA_GENERATE(set_property, OC_ALGEBRA_SET_ADD_VALUE_MEMBERS)


#define OC_ALGEBRA_SET_EQUAL_MEMBERS(_KLASS_NAME_, F)                     \
  F(_KLASS_NAME_, member_access_csp, target)                              \
  F(_KLASS_NAME_, nocde_csp, expression)

OC_ALGEBRA_GENERATE(add_property, OC_ALGEBRA_SET_ADD_VALUE_MEMBERS)

#define OC_ALGEBRA_REMOVE_PROPERTY_MEMBERS(_KLASS_NAME_, F)                   \
  F(_KLASS_NAME_, member_access_csp, target)

OC_ALGEBRA_GENERATE(remove_property, OC_ALGEBRA_REMOVE_PROPERTY_MEMBERS)

#define OC_ALGEBRA_EDIT_LABELS_MEMBERS(_KLASS_NAME_, F)                   \
  F(_KLASS_NAME_, std::string, target)                                    \
  F(_KLASS_NAME_, std::vector<std::string>, labels)

OC_ALGEBRA_GENERATE(edit_labels, OC_ALGEBRA_EDIT_LABELS_MEMBERS)


// Values

#define OC_ALGEBRA_GRAPH_NODE_MEMBERS(_KLASS_NAME_, F)          \
  F(_KLASS_NAME_, std::string, variable)                        \
  F(_KLASS_NAME_, std::vector<std::string>, labels)             \
  F(_KLASS_NAME_, map_csp, properties)

OC_ALGEBRA_GENERATE(graph_node, OC_ALGEBRA_GRAPH_NODE_MEMBERS)

#define OC_ALGEBRA_GRAPH_EDGE_MEMBERS(_KLASS_NAME_, F)          \
  F(_KLASS_NAME_, std::string, variable)                        \
  F(_KLASS_NAME_, graph_node_csp, source)                       \
  F(_KLASS_NAME_, graph_node_csp, destination)                  \
  F(_KLASS_NAME_, edge_directivity, directivity)                \
  F(_KLASS_NAME_, std::vector<std::string>, labels)             \
  F(_KLASS_NAME_, map_csp, properties)

OC_ALGEBRA_GENERATE(graph_edge, OC_ALGEBRA_GRAPH_EDGE_MEMBERS)

#define OC_ALGEBRA_VALUE_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, gqlite::value, value)

OC_ALGEBRA_GENERATE(value, OC_ALGEBRA_VALUE_MEMBERS)


#define OC_ALGEBRA_MAP_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::unordered_map<GQLITE_LIST(std::string, node_csp)>, map)

OC_ALGEBRA_GENERATE(map, OC_ALGEBRA_MAP_MEMBERS)

#define OC_ALGEBRA_ARRAY_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::vector<node_csp>, array)

OC_ALGEBRA_GENERATE(array, OC_ALGEBRA_ARRAY_MEMBERS)


// Expressions

#define OC_ALGEBRA_NAMED_EXPRESSION(_KLASS_NAME_, F)      \
  F(_KLASS_NAME_, std::string, name)                      \
  F(_KLASS_NAME_, node_csp, expression)

OC_ALGEBRA_GENERATE(named_expression, OC_ALGEBRA_NAMED_EXPRESSION)

#define OC_ALGEBRA_VARIABLE_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::string, identifier)

OC_ALGEBRA_GENERATE(variable, OC_ALGEBRA_VARIABLE_MEMBERS)

#define OC_ALGEBRA_MEMBER_ACCESS_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::string, left)                      \
  F(_KLASS_NAME_, std::vector<std::string>, path)

OC_ALGEBRA_GENERATE(member_access, OC_ALGEBRA_MEMBER_ACCESS_MEMBERS)

#define OC_ALGEBRA_HAS_LABELS_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, std::string, left)                   \
  F(_KLASS_NAME_, std::vector<std::string>, labels)

OC_ALGEBRA_GENERATE(has_labels, OC_ALGEBRA_HAS_LABELS_MEMBERS)

#define OC_ALGEBRA_FUNCTION_CALL_EXPRESSION(_KLASS_NAME_, F)  \
  F(_KLASS_NAME_, std::string, name)                          \
  F(_KLASS_NAME_, std::vector<node_csp>, arguments)

OC_ALGEBRA_GENERATE(function_call, OC_ALGEBRA_FUNCTION_CALL_EXPRESSION)

#define OC_ALGEBRA_BINARY_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, node_csp, left)                  \
  F(_KLASS_NAME_, node_csp, right)

OC_ALGEBRA_GENERATE(logical_and, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(logical_or, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_equal, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_different, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_inferior, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_superior, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_inferior_equal, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_superior_equal, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_in, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(relational_not_in, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(addition, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(substraction, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(multiplication, OC_ALGEBRA_BINARY_MEMBERS)
OC_ALGEBRA_GENERATE(division, OC_ALGEBRA_BINARY_MEMBERS)

#define OC_ALGEBRA_UNARY_MEMBERS(_KLASS_NAME_, F) \
  F(_KLASS_NAME_, node_csp, value)

OC_ALGEBRA_GENERATE(logical_negation, OC_ALGEBRA_UNARY_MEMBERS)
OC_ALGEBRA_GENERATE(negation, OC_ALGEBRA_UNARY_MEMBERS)
OC_ALGEBRA_GENERATE(is_null, OC_ALGEBRA_UNARY_MEMBERS)
OC_ALGEBRA_GENERATE(is_not_null, OC_ALGEBRA_UNARY_MEMBERS)


#undef OC_ALGEBRA_GENERATE
}
//END oc/algebra/nodes.cpp
//BEGIN oc/algebra/visitors/print.cpp


#include <iostream>
#include <type_traits>



using namespace gqlite::oc::algebra::visitors;

namespace
{
  static void print_h(const std::string& _indentation, const std::string& _message)
  {
    std::cerr << _indentation << _message << std::endl;;
  }
  template<typename _T_, typename... _TOther_>
  static void print_h(const std::string& _indentation, const std::string& _message, _T_ const& _v, _TOther_... _other)
  {
#ifdef GQLITE_HAVE_CLOG
    print_h(_indentation, fmt::format(_message, _v, _other...));
#else
    print_h(_indentation, "Missing clog.");
#endif
  }
}

  
namespace gqlite::oc::algebra::visitors::details
{
  
  template < template <typename...> class Template, typename T >
  struct is_specialisation_of : std::false_type {};

  template < template <typename...> class Template, typename... Args >
  struct is_specialisation_of< Template, Template<Args...> > : std::true_type {};
  
  template<typename _T_>
  struct remove_const_explicitly_shared_data_pointer { typedef _T_ type; };
  template<typename _T_>
  struct remove_const_explicitly_shared_data_pointer<std::shared_ptr<const _T_>> { typedef _T_ type; };
  
  template<typename... _T_>
  struct print_helper<gqlite::oc::algebra::alternative<_T_...>>
  {
    static void call(print* _visitor, std::string& _indentation, const char* _name, const gqlite::oc::algebra::alternative<_T_...>& _t)
    {
      if(_t.is_valid())
      {
        if(_name != 0)
        {
          print_h(_indentation, "{}:", _name);
        }
        std::string indentation = _indentation;
        _indentation += "  ";
        _visitor->accept(_t.get_node());
        _indentation = indentation;
      }
    }
  };
  template<typename _T_>
  struct print_helper<std::shared_ptr<const _T_>, typename std::enable_if<std::is_base_of<gqlite::oc::algebra::node, _T_>::value>::type>
  {
    static void call(print* _visitor, std::string& _indentation, const char* _name, std::shared_ptr<const _T_> _t)
    {
      if(_t)
      {
        if(_name != 0)
        {
          print_h(_indentation, "{}:", _name);
        }
        std::string indentation = _indentation;
        _indentation += "  ";
        _visitor->accept(_t);
        _indentation = indentation;
      }
    }
  };
  template<typename _T_, typename>
  struct print_helper
  {
    static void call(print* _visitor, std::string& _indentation, const char* _name, const _T_& _t)
    {
      GQLITE_UNUSED(_visitor);
      print_h(_indentation, "{}: {}", _name, _t);
    }
  };
  template<typename _T_>
  struct print_helper<std::vector<_T_>, void>
  {
    static void call(print* _visitor, std::string& _indentation, const char* _name, const std::vector<_T_>& _t)
    {
      print_h(_indentation, "{} ({}): [", _name, _t.size());
      for(const _T_& t : _t)
      {
        print_helper<_T_>::call(_visitor, _indentation, "null", t);
      }
      print_h(_indentation, "]");
    }
  };
}

struct print::data
{
  std::string indentation;
};

print::print() : d(new data)
{

}

print::~print()
{
  delete d;
}

#define GQL_GENERATE_VISIT_CALL(_KLASS_NAME_, _TYPE_, _NAME_)          \
  details::print_helper<_TYPE_>::call(this, d->indentation, # _NAME_, _node->get_ ## _NAME_());

#define OC_ALGEBRA_GENERATE(_KLASS_NAME_, _MEMBER_DEF_)                 \
void print::visit(_KLASS_NAME_ ## _csp _node)                           \
{                                                                       \
  print_h(d->indentation, # _KLASS_NAME_);                              \
  std::string oldindentation = d->indentation;                          \
  d->indentation += "  ";                                               \
  _MEMBER_DEF_(_KLASS_NAME_, GQL_GENERATE_VISIT_CALL)                   \
  d->indentation = oldindentation;                                      \
}



#undef OC_ALGEBRA_GENERATE
//END oc/algebra/visitors/print.cpp
