Created
October 9, 2015 12:39
-
-
Save cviebig/4922ca72893034d4232b to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*============================================================================= | |
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; | |
}; | |
typedef std::vector<sql_stmt> sql_stmt_list; | |
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) | |
) | |
/////////////////////////////////////////////////////////////////////////////// | |
// 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_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