Created
June 14, 2015 13:19
-
-
Save agatan/062060978386aa09c50c to your computer and use it in GitHub Desktop.
qi error handling sample
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
#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