Created
October 21, 2015 13:47
-
-
Save cviebig/8d9f152afa3bfc792796 to your computer and use it in GitHub Desktop.
compiles but segfaults on parser invocation
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) | |
=============================================================================*/ | |
#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/spirit/home/x3/support/utility/annotate_on_success.hpp> | |
#include <boost/spirit/home/x3/support/utility/error_reporting.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 <vector> | |
namespace sql { namespace ast | |
{ | |
// forward declaration | |
struct binary_operation; | |
struct expr : boost::spirit::x3::variant< | |
int | |
, float | |
, boost::spirit::x3::forward_ast<binary_operation> | |
> | |
{ | |
using base_type::base_type; | |
using base_type::operator=; | |
}; | |
struct binary_operation : boost::spirit::x3::position_tagged { | |
expr first; | |
char op; | |
expr second; | |
}; | |
}} | |
BOOST_FUSION_ADAPT_STRUCT(sql::ast::binary_operation, | |
(sql::ast::expr, first) | |
(char, op) | |
(sql::ast::expr, second) | |
) | |
namespace sql { namespace parser | |
{ | |
namespace x3 = boost::spirit::x3; | |
//////////////////////////////////////////////////////////////////////////// | |
// Our error handler | |
//////////////////////////////////////////////////////////////////////////// | |
// X3 Error Handler Utility | |
template <typename Iterator> | |
using error_handler = x3::error_handler<Iterator>; | |
// tag used to get our error handler from the context | |
using error_handler_tag = x3::error_handler_tag; | |
struct error_handler_base | |
{ | |
error_handler_base(); | |
template <typename Iterator, typename Exception, typename Context> | |
x3::error_handler_result on_error( | |
Iterator& first, Iterator const& last | |
, Exception const& x, Context const& context); | |
std::map<std::string, std::string> id_map; | |
}; | |
//////////////////////////////////////////////////////////////////////////// | |
// Implementation | |
//////////////////////////////////////////////////////////////////////////// | |
inline error_handler_base::error_handler_base() | |
{ | |
id_map["sql"] = "SQL"; | |
} | |
template <typename Iterator, typename Exception, typename Context> | |
inline x3::error_handler_result | |
error_handler_base::on_error( | |
Iterator& first, Iterator const& last | |
, Exception const& x, Context const& context) | |
{ | |
std::string which = x.which(); | |
auto iter = id_map.find(which); | |
if (iter != id_map.end()) | |
which = iter->second; | |
std::string message = "Error! Expecting: " + which + " here:"; | |
auto& error_handler = x3::get<error_handler_tag>(context).get(); | |
error_handler(x.where(), message); | |
return x3::error_handler_result::fail; | |
} | |
}} | |
namespace sql { namespace parser | |
{ | |
namespace x3 = boost::spirit::x3; | |
// Our Iterator Type | |
typedef std::string::const_iterator iterator_type; | |
// The Phrase Parse Context | |
typedef | |
x3::phrase_parse_context<x3::ascii::space_type>::type | |
phrase_context_type; | |
// Our Error Handler | |
typedef error_handler<iterator_type> error_handler_type; | |
// Combined Error Handler and Phrase Parse Context | |
typedef x3::with_context< | |
error_handler_tag | |
, std::reference_wrapper<error_handler_type> const | |
, phrase_context_type>::type | |
context_type; | |
}} | |
namespace sql { namespace parser | |
{ | |
namespace x3 = boost::spirit::x3; | |
namespace ascii = boost::spirit::x3::ascii; | |
using x3::char_; | |
using x3::int_; | |
using x3::float_; | |
struct binary_operation_class; | |
struct expr_class; | |
x3::rule<binary_operation_class, ast::binary_operation> const | |
binary_operation = "binary_operation"; | |
x3::rule<expr_class, ast::expr> const | |
expr = "expr"; | |
auto const binary_operation_def = expr >> char_ >> expr; | |
// https://www.sqlite.org/syntax/expr.html | |
// | |
auto const expr_def = | |
( | |
int_ | |
| float_ | |
| binary_operation | |
); | |
BOOST_SPIRIT_DEFINE(binary_operation, expr); | |
struct binary_operation_class : x3::annotate_on_success {}; | |
struct expr_class : x3::annotate_on_success, error_handler_base {}; | |
}} | |
namespace sql { namespace ast | |
{ | |
struct sql_printer | |
{ | |
sql_printer(std::ostream& out) | |
: out(out) {} | |
void operator()(char const& ast) const | |
{ | |
out << ast; | |
} | |
void operator()(int const& ast) const | |
{ | |
out << ast; | |
} | |
void operator()(float const& ast) const | |
{ | |
out << ast; | |
} | |
void operator()(ast::binary_operation const& ast) const | |
{ | |
sql_printer{out}(ast.first); | |
sql_printer{out}(ast.op); | |
sql_printer{out}(ast.second); | |
} | |
void operator()(ast::expr const& ast) const | |
{ | |
boost::apply_visitor(*this, ast); | |
} | |
std::ostream& out; | |
}; | |
}} | |
namespace sql { namespace parser { | |
template<typename Attribute> | |
struct parse_result { | |
parse_result(bool success, bool finished, | |
unsigned int remainder, Attribute ast, | |
std::string messages, | |
error_handler_type errors): | |
success(success), finished(finished), | |
remainder(remainder), ast(ast), messages(messages), | |
errors(errors) {} | |
bool success; | |
// finished indicates whether the whole input string was consumed | |
bool finished; | |
// remainder contains the number of characters that remain unparsed | |
unsigned int remainder; | |
Attribute ast; | |
std::string messages; | |
error_handler_type errors; | |
}; | |
template<typename ID, typename Attribute> | |
parse_result<Attribute> parse(x3::rule<ID, Attribute> s, std::string const& source, | |
std::string input_path = ""); | |
// Def | |
template<typename ID, typename Attribute> | |
parse_result<Attribute> parse(x3::rule<ID, Attribute> s, std::string const& source, | |
std::string input_path) { | |
std::stringstream out; | |
using sql::parser::iterator_type; | |
iterator_type iter(source.begin()); | |
iterator_type const end(source.end()); | |
// Our AST | |
Attribute ast; | |
// Our error handler | |
using boost::spirit::x3::with; | |
using sql::parser::error_handler_type; | |
using sql::parser::error_handler_tag; | |
error_handler_type error_handler(iter, end, out, input_path.c_str()); // Our error handler | |
// Our parser | |
auto const parser = | |
// we pass our error handler to the parser so we can access | |
// it later on in our on_error and on_sucess handlers | |
with<error_handler_tag>(std::ref(error_handler)) | |
[ | |
s | |
]; | |
// Go forth and parse! | |
using boost::spirit::x3::ascii::space; | |
bool success = phrase_parse(iter, end, parser, space, ast); | |
bool finished = true; | |
unsigned int remainder = 0; | |
if(iter != end) { | |
if(success) { | |
error_handler(iter, "Error! Expecting end of input here: "); | |
} | |
finished = false; | |
remainder = end - iter; | |
} | |
return parse_result<Attribute>(success, finished, remainder, ast, out.str(), error_handler); | |
}; | |
}} | |
/////////////////////////////////////////////////////////////////////////////// | |
// 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::expr; // Our grammar | |
sql::ast::expr ast; // Our tree | |
using boost::spirit::x3::ascii::space; | |
auto p = sql::parser::parse(expr, str); | |
std::cout << "Successful: " << (p.success ? "yes" : "no") << std::endl; | |
std::cout << "Parsed text: " << std::endl; | |
sql::ast::sql_printer{std::cout}(p.ast); | |
std::cout << std::endl; | |
std::cout << "Messages: " << std::endl << p.messages; | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment