Skip to content

Instantly share code, notes, and snippets.

@cviebig
Created October 21, 2015 13:47
Show Gist options
  • Save cviebig/8d9f152afa3bfc792796 to your computer and use it in GitHub Desktop.
Save cviebig/8d9f152afa3bfc792796 to your computer and use it in GitHub Desktop.
compiles but segfaults on parser invocation
/*=============================================================================
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