| /*============================================================================= |
| Copyright (c) 2001-2010 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 mini XML-like parser |
| // |
| // [ JDG March 25, 2007 ] spirit2 |
| // |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| #include <boost/config/warning_disable.hpp> |
| #include <boost/spirit/include/qi.hpp> |
| #include <boost/spirit/include/phoenix_core.hpp> |
| #include <boost/spirit/include/phoenix_operator.hpp> |
| #include <boost/spirit/include/phoenix_fusion.hpp> |
| #include <boost/spirit/include/phoenix_stl.hpp> |
| #include <boost/fusion/include/adapt_struct.hpp> |
| #include <boost/variant/recursive_variant.hpp> |
| #include <boost/foreach.hpp> |
| |
| #include <iostream> |
| #include <fstream> |
| #include <string> |
| #include <vector> |
| |
| namespace client |
| { |
| namespace fusion = boost::fusion; |
| namespace phoenix = boost::phoenix; |
| namespace qi = boost::spirit::qi; |
| namespace ascii = boost::spirit::ascii; |
| |
| /////////////////////////////////////////////////////////////////////////// |
| // Our mini XML tree representation |
| /////////////////////////////////////////////////////////////////////////// |
| struct mini_xml; |
| |
| typedef |
| boost::variant< |
| boost::recursive_wrapper<mini_xml> |
| , std::string |
| > |
| mini_xml_node; |
| |
| struct mini_xml |
| { |
| std::string name; // tag name |
| std::vector<mini_xml_node> children; // children |
| }; |
| } |
| |
| // We need to tell fusion about our mini_xml struct |
| // to make it a first-class fusion citizen |
| BOOST_FUSION_ADAPT_STRUCT( |
| client::mini_xml, |
| (std::string, name) |
| (std::vector<client::mini_xml_node>, children) |
| ) |
| |
| namespace client |
| { |
| /////////////////////////////////////////////////////////////////////////// |
| // Print out the mini xml tree |
| /////////////////////////////////////////////////////////////////////////// |
| int const 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; |
| |
| BOOST_FOREACH(mini_xml_node const& node, xml.children) |
| { |
| boost::apply_visitor(mini_xml_node_printer(indent), node); |
| } |
| |
| tab(indent); |
| std::cout << '}' << std::endl; |
| } |
| |
| /////////////////////////////////////////////////////////////////////////// |
| // Our mini XML grammar definition |
| /////////////////////////////////////////////////////////////////////////// |
| //[tutorial_xml2_grammar |
| 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) |
| { |
| using qi::lit; |
| using qi::lexeme; |
| using ascii::char_; |
| using ascii::string; |
| using namespace qi::labels; |
| |
| text %= lexeme[+(char_ - '<')]; |
| node %= xml | text; |
| |
| start_tag %= |
| '<' |
| >> !lit('/') |
| >> lexeme[+(char_ - '>')] |
| >> '>' |
| ; |
| |
| end_tag = |
| "</" |
| >> string(_r1) |
| >> '>' |
| ; |
| |
| xml %= |
| start_tag[_a = _1] |
| >> *node |
| >> end_tag(_a) |
| ; |
| } |
| |
| 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; |
| }; |
| //] |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // Main program |
| /////////////////////////////////////////////////////////////////////////////// |
| int main(int argc, char **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; // We will read the contents here. |
| in.unsetf(std::ios::skipws); // No white space skipping! |
| std::copy( |
| std::istream_iterator<char>(in), |
| std::istream_iterator<char>(), |
| std::back_inserter(storage)); |
| |
| typedef client::mini_xml_grammar<std::string::const_iterator> mini_xml_grammar; |
| mini_xml_grammar xml; // Our grammar |
| client::mini_xml ast; // Our tree |
| |
| using boost::spirit::ascii::space; |
| std::string::const_iterator iter = storage.begin(); |
| std::string::const_iterator end = storage.end(); |
| bool r = phrase_parse(iter, end, xml, space, ast); |
| |
| if (r && iter == end) |
| { |
| std::cout << "-------------------------\n"; |
| std::cout << "Parsing succeeded\n"; |
| std::cout << "-------------------------\n"; |
| client::mini_xml_printer printer; |
| printer(ast); |
| return 0; |
| } |
| else |
| { |
| std::string::const_iterator some = iter+30; |
| std::string context(iter, (some>end)?end:some); |
| std::cout << "-------------------------\n"; |
| std::cout << "Parsing failed\n"; |
| std::cout << "stopped at: \": " << context << "...\"\n"; |
| std::cout << "-------------------------\n"; |
| return 1; |
| } |
| } |
| |
| |