Skip to content

Instantly share code, notes, and snippets.

@cviebig
Last active October 8, 2015 14:52
Show Gist options
  • Save cviebig/b30c3c96e997492b9acf to your computer and use it in GitHub Desktop.
Save cviebig/b30c3c96e997492b9acf to your computer and use it in GitHub Desktop.
The compilation will fail if the parser select_stmt_def is added in line 192 and the uncommented part in l. 83 added to the fusion adapt it will fail
/*=============================================================================
Copyright (c) 2001-2015 Joel de Guzman
Distributed under the Boost Software License, Version 1.0. (See accompanying
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
=============================================================================*/
///////////////////////////////////////////////////////////////////////////////
//
// A simple parser for X3 intended as a minimal starting point.
// 'rexpr' is a parser for a language resembling a minimal subset
// of json, but limited to a dictionary (composed of key=value pairs)
// where the value can itself be a string or a recursive dictionary.
//
// Example:
//
// {
// "color" = "blue"
// "size" = "29 cm."
// "position" = {
// "x" = "123"
// "y" = "456"
// }
// }
//
///////////////////////////////////////////////////////////////////////////////
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/ast/position_tagged.hpp>
#include <boost/spirit/home/x3/support/ast/variant.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/std_pair.hpp>
#include <boost/fusion/include/io.hpp>
#include <iostream>
#include <fstream>
#include <string>
#include <map>
///////////////////////////////////////////////////////////////////////////////
// Our AST
///////////////////////////////////////////////////////////////////////////////
namespace sql { namespace ast
{
namespace fusion = boost::fusion;
namespace x3 = boost::spirit::x3;
struct select_stmt : x3::position_tagged
{
std::string stmt;
};
struct explain_clause : x3::position_tagged {
bool explain;
bool query_plan;
};
struct sql_stmt : x3::position_tagged
{
explain_clause explain;
select_stmt stmt;
};
struct sql_stmt_list : x3::position_tagged
{
std::vector<sql_stmt> sql_stmts;
};
typedef sql_stmt_list sql;
}}
// We need to tell fusion about our rexpr struct
// to make it a first-class fusion citizen
BOOST_FUSION_ADAPT_STRUCT(sql::ast::select_stmt,
stmt
)
BOOST_FUSION_ADAPT_STRUCT(sql::ast::explain_clause,
(bool, explain)
(bool, query_plan)
)
BOOST_FUSION_ADAPT_STRUCT(sql::ast::sql_stmt,
(sql::ast::explain_clause, explain)
)
// (sql::ast::select_stmt, stmt)
BOOST_FUSION_ADAPT_STRUCT(sql::ast::sql_stmt_list,
sql_stmts
)
///////////////////////////////////////////////////////////////////////////////
// AST processing
///////////////////////////////////////////////////////////////////////////////
namespace sql { namespace ast
{
///////////////////////////////////////////////////////////////////////////
// Print out the sql tree
///////////////////////////////////////////////////////////////////////////
struct sql_printer
{
sql_printer(std::ostream& out)
: out(out) {}
void operator()(ast::sql const& ast) const
{
for (auto const& stmt : ast.sql_stmts)
{
sql_printer{out}(stmt);
out << ";";
}
}
void operator()(ast::explain_clause const& ast) const
{
if(ast.explain) {
out << "EXPLAIN ";
if(ast.query_plan) {
out << "QUERY PLAN ";
}
}
}
void operator()(ast::sql_stmt const& ast) const
{
sql_printer{out}(ast.explain);
}
void operator()(ast::select_stmt const& ast) const
{
//out << ast.stmt;
}
std::ostream& out;
};
}}
///////////////////////////////////////////////////////////////////////////////
// Our rexpr grammar
///////////////////////////////////////////////////////////////////////////////
namespace sql { namespace parser
{
namespace x3 = boost::spirit::x3;
namespace ascii = boost::spirit::x3::ascii;
using x3::attr;
using x3::lit;
using x3::lexeme;
using x3::matches;
using x3::omit;
using ascii::alnum;
using ascii::alpha;
using ascii::char_;
using x3::int_;
using x3::string;
struct select_stmt_class;
struct explain_clause_class;
struct sql_stmt_class;
struct sql_stmt_list_class;
struct sql_inner_class;
struct sql_class;
x3::rule<select_stmt_class, ast::select_stmt> const
select_stmt = "select_stmt";
x3::rule<explain_clause_class, ast::explain_clause> const
explain_clause = "explain_clause";
x3::rule<sql_stmt_class, ast::sql_stmt> const
sql_stmt = "sql_stmt";
x3::rule<sql_stmt_list_class, ast::sql_stmt_list> const
sql_stmt_list = "sql_stmt_list";
x3::rule<sql_inner_class, ast::sql> const
sql_inner = "sql";
x3::rule<sql_class, ast::sql> const sql = "sql";
// https://www.sqlite.org/syntax/factored-select-stmt.html
//
auto const select_stmt_def = lit("SELECT") >> +(char_ - ';');
auto const explain_clause_def = (omit[lit("EXPLAIN")] >> attr(true) >> matches[lit("QUERY") >> lit("PLAN")])
| (attr(false) >> attr(false));
// https://www.sqlite.org/syntax/sql-stmt.html
// NOTE: not implemented, directly refers to select_stmt
auto const sql_stmt_def = explain_clause_def; // >> select_stmt_def;
// https://www.sqlite.org/syntax/sql-stmt-list.html
//
auto const sql_stmt_list_def = sql_stmt_def % ';';
//
// by convention
auto const sql_inner_def = sql_stmt_list_def;
//
// top
auto const sql_def = sql_inner_def;
BOOST_SPIRIT_DEFINE(select_stmt, explain_clause, sql_stmt, sql_stmt_list, sql_inner, sql);
}}
///////////////////////////////////////////////////////////////////////////////
// Main program
///////////////////////////////////////////////////////////////////////////////
int main(int argc, char **argv)
{
std::cout << "Give input to parse or type [q or Q] to quit\n\n";
std::string str;
while (getline(std::cin, str))
{
if (str.empty() || str[0] == 'q' || str[0] == 'Q')
break;
using sql::parser::sql; // Our grammar
sql::ast::sql ast; // Our tree
using boost::spirit::x3::ascii::space;
auto iter(str.begin());
auto const end(str.end());
if (phrase_parse(iter, end, sql, space, ast)) {
if(iter == end) {
std::cout << "Parsing succeeded and the whole string was consumed\n";
} else {
std::cout << "Parsing succeeded but only a part of the string was consumed\n";
}
sql::ast::sql_printer printer(std::cout);
printer(ast);
std::cout << "\n";
} else {
std::cout << "Parsing failed\n";
}
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment