/*============================================================================= Copyright (c) 2001-2010 Joel de Guzman Copyright (c) 2001-2010 Hartmut Kaiser 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, Karma is used to print out the generated AST // // [ JDG March 25, 2007 ] spirit2 // [ HK April 02, 2007 ] spirit2 // /////////////////////////////////////////////////////////////////////////////// #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace boost::spirit; using namespace boost::spirit::ascii; namespace fusion = boost::fusion; namespace phoenix = boost::phoenix; using phoenix::at_c; using phoenix::push_back; /////////////////////////////////////////////////////////////////////////////// // Our mini XML tree representation /////////////////////////////////////////////////////////////////////////////// struct mini_xml; typedef boost::variant< boost::recursive_wrapper , std::string > mini_xml_node; struct mini_xml { std::string name; // tag name std::vector children; // children }; // We need to tell fusion about our mini_xml struct // to make it a first-class fusion citizen BOOST_FUSION_ADAPT_STRUCT( mini_xml, (std::string, name) (std::vector, children) ) /////////////////////////////////////////////////////////////////////////////// // Our mini XML grammar definition /////////////////////////////////////////////////////////////////////////////// template struct mini_xml_parser : qi::grammar { mini_xml_parser() : mini_xml_parser::base_type(xml) { text = lexeme[+(char_ - '<') [_val += _1]]; node = (xml | text) [_val = _1]; start_tag = '<' >> !lit('/') >> lexeme[+(char_ - '>') [_val += _1]] >> '>' ; end_tag = "> lit(_r1) >> '>' ; xml = start_tag [at_c<0>(_val) = _1] >> *node [push_back(at_c<1>(_val), _1)] >> end_tag(at_c<0>(_val)) ; } qi::rule xml; qi::rule node; qi::rule text; qi::rule start_tag; qi::rule end_tag; }; /////////////////////////////////////////////////////////////////////////////// // A couple of phoenix functions helping to access the elements of the // generated AST /////////////////////////////////////////////////////////////////////////////// template struct get_element { template struct result { typedef T const& type; }; T const& operator()(mini_xml_node const& node) const { return boost::get(node); } }; phoenix::function > _string; phoenix::function > _xml; /////////////////////////////////////////////////////////////////////////////// // The output grammar defining the format of the generated data /////////////////////////////////////////////////////////////////////////////// template struct mini_xml_generator : karma::grammar { mini_xml_generator() : mini_xml_generator::base_type(xml) { node %= string | xml; xml = '<' << string[_1 = at_c<0>(_val)] << '>' << (*node)[_1 = at_c<1>(_val)] << "(_val)] << '>' ; } karma::rule xml; karma::rule node; }; /////////////////////////////////////////////////////////////////////////////// // 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(in), std::istream_iterator(), std::back_inserter(storage)); typedef mini_xml_parser mini_xml_parser; mini_xml_parser xmlin; // Our grammar definition mini_xml ast; // our tree std::string::const_iterator iter = storage.begin(); std::string::const_iterator end = storage.end(); bool r = qi::phrase_parse(iter, end, xmlin, space, ast); if (r && iter == end) { std::cout << "-------------------------\n"; std::cout << "Parsing succeeded\n"; std::cout << "-------------------------\n"; typedef std::back_insert_iterator outiter_type; typedef mini_xml_generator mini_xml_generator; mini_xml_generator xmlout; // Our grammar definition std::string generated; outiter_type outit(generated); bool r = karma::generate(outit, xmlout, ast); if (r) std::cout << generated << std::endl; return 0; } else { std::string::const_iterator begin = storage.begin(); std::size_t dist = std::distance(begin, iter); std::string::const_iterator some = iter + (std::min)(storage.size()-dist, std::size_t(30)); std::string context(iter, some); std::cout << "-------------------------\n"; std::cout << "Parsing failed\n"; std::cout << "stopped at: \": " << context << "...\"\n"; std::cout << "-------------------------\n"; return 1; } }