Skip to content

Instantly share code, notes, and snippets.

@agatan
Created June 14, 2015 13:19
Show Gist options
  • Save agatan/062060978386aa09c50c to your computer and use it in GitHub Desktop.
Save agatan/062060978386aa09c50c to your computer and use it in GitHub Desktop.
qi error handling sample
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/variant/recursive_variant.hpp>
#include <boost/spirit/home/support/iterators/line_pos_iterator.hpp>
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <boost/format.hpp>
namespace client {
namespace fusion = boost::fusion;
namespace phx = boost::phoenix;
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
struct mini_xml;
typedef boost::variant<
boost::recursive_wrapper<mini_xml>
, std::string
>
mini_xml_node;
struct mini_xml {
std::string name;
std::vector<mini_xml_node> children;
};
} // namespace client
BOOST_FUSION_ADAPT_STRUCT(
client::mini_xml,
(std::string, name),
(std::vector<client::mini_xml_node>, children)
)
namespace client {
int constexpr tabsize = 4;
void tab(int indent)
{
for (int i = 0; i < indent; ++i)
std::cout << ' ';
}
struct mini_xml_printer
{
mini_xml_printer(int indent = 0) : indent(indent) {}
void operator()(mini_xml const& xml) const;
int indent;
};
struct mini_xml_node_printer : boost::static_visitor<>
{
mini_xml_node_printer(int indent = 0) : indent(indent) {}
void operator()(mini_xml const& xml) const
{
mini_xml_printer(indent+tabsize)(xml);
}
void operator()(std::string const& text) const
{
tab(indent + tabsize);
std::cout << "text: \"" << text << '"' << std::endl;
}
int indent;
};
void mini_xml_printer::operator()(mini_xml const& xml) const
{
tab(indent);
std::cout << "tag: " << xml.name << std::endl;
tab(indent);
std::cout << '{' << std::endl;
for (mini_xml_node const& node : xml.children) {
boost::apply_visitor(mini_xml_node_printer(indent), node);
}
tab(indent);
std::cout << '}' << std::endl;
}
template<typename Iterator>
struct mini_xml_grammar
: qi::grammar<Iterator, mini_xml(), qi::locals<std::string>, ascii::space_type>
{
mini_xml_grammar()
: mini_xml_grammar::base_type(xml, "xml")
{
using qi::lit;
using qi::lexeme;
using qi::on_error;
using qi::fail;
using ascii::char_;
using ascii::string;
using namespace qi::labels;
using phx::construct;
using phx::val;
text %= lexeme[+(char_ - '<')];
node %= xml | text;
start_tag %=
'<'
>> !lit('/')
> lexeme[+(char_ - '>')]
> '>'
;
end_tag =
"</"
> string(_r1)
> '>'
;
xml %=
start_tag[_a = _1]
> *node
> end_tag(_a)
;
xml.name("xml");
node.name("node");
text.name("text");
start_tag.name("start_tag");
end_tag.name("end_tag");
on_error<fail> (
xml
, std::cerr << phx::val("Error in ")
<< phx::bind([](auto const begin, auto const err_pos) {
return (boost::format("line:%1%, col:%2%") % boost::spirit::get_line(err_pos) % boost::spirit::get_column(begin, err_pos)).str();
}, _1, _3) << '\n'
<< "Expected " << _4 << "\n\n"
<< phx::bind([this](auto const begin, auto const end, auto const err_itr) {
return std::string {
boost::spirit::get_line_start(begin, err_itr),
std::find_if(err_itr, end, [](auto c) { return c == '\r' || c == '\n'; })
} + '\n' + std::string(boost::spirit::get_column(begin, err_itr)-1, ' ') + "^ here";
}, _1, _2, _3)
<< std::endl);
}
qi::rule<Iterator, mini_xml(), qi::locals<std::string>, ascii::space_type> xml;
qi::rule<Iterator, mini_xml_node(), ascii::space_type> node;
qi::rule<Iterator, std::string(), ascii::space_type> text;
qi::rule<Iterator, std::string(), ascii::space_type> start_tag;
qi::rule<Iterator, void(std::string), ascii::space_type> end_tag;
};
} // namespace client
int main(int argc, char const* argv[])
{
char const* filename;
if (argc > 1) filename = argv[1];
else {
std::cerr << "Error: No input file provided." << std::endl;
return 1;
}
std::ifstream in(filename, std::ios_base::in);
if (!in)
{
std::cerr << "Error: Could not open input file: " << filename << std::endl;
return 1;
}
std::string storage;
in.unsetf(std::ios::skipws);
std::copy(
std::istream_iterator<char>(in),
std::istream_iterator<char>(),
std::back_inserter(storage));
typedef client::mini_xml_grammar<boost::spirit::line_pos_iterator<std::string::const_iterator>> mini_xml_grammar;
mini_xml_grammar xml;
client::mini_xml ast;
using boost::spirit::ascii::space;
auto iter = boost::spirit::line_pos_iterator<std::string::const_iterator>(storage.cbegin());
auto end = boost::spirit::line_pos_iterator<std::string::const_iterator>(storage.end());
bool r = boost::spirit::qi::phrase_parse(iter, end, xml, space, ast);
if (r && iter == end)
{
std::cout << "Succeeded!" << std::endl;
client::mini_xml_printer printer;
printer(ast);
}
else
{
std::cout << "failed" << std::endl;
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment