text: /*=============================================================================
      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 <boost/config/warning_disable.hpp>
  
  #include <boost/spirit/include/qi.hpp>
  #include <boost/spirit/include/karma.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_function.hpp>
  #include <boost/spirit/include/phoenix_stl.hpp>
  #include <boost/fusion/include/adapt_struct.hpp>
  #include <boost/variant/recursive_variant.hpp>
  
  #include <iostream>
  #include <fstream>
  #include <string>
  #include <vector>
  
  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<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(
      mini_xml,
      (std::string, name)
      (std::vector<mini_xml_node>, children)
  )
  
  ///////////////////////////////////////////////////////////////////////////////
  //  Our mini XML grammar definition
  ///////////////////////////////////////////////////////////////////////////////
  template <typename Iterator>
  struct mini_xml_parser :
      qi::grammar<Iterator, mini_xml(), space_type>
  {
      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<Iterator, mini_xml(), space_type> xml;
      qi::rule<Iterator, mini_xml_node(), space_type> node;
      qi::rule<Iterator, std::string(), space_type> text;
      qi::rule<Iterator, std::string(), space_type> start_tag;
      qi::rule<Iterator, void(std::string), space_type> end_tag;
  };
  
  ///////////////////////////////////////////////////////////////////////////////
  //  A couple of phoenix functions helping to access the elements of the
  //  generated AST
  ///////////////////////////////////////////////////////////////////////////////
  template <typename T>
  struct get_element
  {
      template <typename T1>
      struct result { typedef T const& type; };
  
      T const& operator()(mini_xml_node const& node) const
      {
          return boost::get<T>(node);
      }
  };
  
  phoenix::function<get_element<std::string> > _string;
  phoenix::function<get_element<mini_xml> > _xml;
  
  ///////////////////////////////////////////////////////////////////////////////
  //  The output grammar defining the format of the generated data
  ///////////////////////////////////////////////////////////////////////////////
  template <typename OutputIterator>
  struct mini_xml_generator
    : karma::grammar<OutputIterator, mini_xml()>
  {
      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)]
              <<  "</" << string[_1 = at_c<0>(_val)] << '>'
              ;
      }
  
      karma::rule<OutputIterator, mini_xml()> xml;
      karma::rule<OutputIterator, mini_xml_node()> 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<char>(in),
          std::istream_iterator<char>(),
          std::back_inserter(storage));
  
      typedef mini_xml_parser<std::string::const_iterator> 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<std::string> outiter_type;
          typedef mini_xml_generator<outiter_type> 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;
      }
  }
  