mini_xml2_sr.cpp 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. /*=============================================================================
  2. Copyright (c) 2001-2010 Joel de Guzman
  3. Copyright (c) 2009 Francois Barel
  4. Distributed under the Boost Software License, Version 1.0. (See accompanying
  5. file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  6. =============================================================================*/
  7. ///////////////////////////////////////////////////////////////////////////////
  8. //
  9. // A mini XML-like parser
  10. //
  11. // [ JDG March 25, 2007 ] spirit2
  12. //
  13. ///////////////////////////////////////////////////////////////////////////////
  14. #include <boost/config/warning_disable.hpp>
  15. //[mini_xml2_sr_includes
  16. #include <boost/spirit/include/qi.hpp>
  17. #include <boost/spirit/repository/include/qi_subrule.hpp>
  18. #include <boost/spirit/include/phoenix_core.hpp>
  19. #include <boost/spirit/include/phoenix_operator.hpp>
  20. //]
  21. #include <boost/spirit/include/phoenix_fusion.hpp>
  22. #include <boost/spirit/include/phoenix_stl.hpp>
  23. #include <boost/fusion/include/adapt_struct.hpp>
  24. #include <boost/variant/recursive_variant.hpp>
  25. #include <boost/foreach.hpp>
  26. #include <iostream>
  27. #include <fstream>
  28. #include <string>
  29. #include <vector>
  30. namespace client
  31. {
  32. namespace fusion = boost::fusion;
  33. namespace phoenix = boost::phoenix;
  34. //[mini_xml2_sr_using
  35. namespace qi = boost::spirit::qi;
  36. namespace repo = boost::spirit::repository;
  37. namespace ascii = boost::spirit::ascii;
  38. //]
  39. ///////////////////////////////////////////////////////////////////////////
  40. // Our mini XML tree representation
  41. ///////////////////////////////////////////////////////////////////////////
  42. struct mini_xml;
  43. typedef
  44. boost::variant<
  45. boost::recursive_wrapper<mini_xml>
  46. , std::string
  47. >
  48. mini_xml_node;
  49. struct mini_xml
  50. {
  51. std::string name; // tag name
  52. std::vector<mini_xml_node> children; // children
  53. };
  54. }
  55. // We need to tell fusion about our mini_xml struct
  56. // to make it a first-class fusion citizen
  57. BOOST_FUSION_ADAPT_STRUCT(
  58. client::mini_xml,
  59. (std::string, name)
  60. (std::vector<client::mini_xml_node>, children)
  61. )
  62. namespace client
  63. {
  64. ///////////////////////////////////////////////////////////////////////////
  65. // Print out the mini xml tree
  66. ///////////////////////////////////////////////////////////////////////////
  67. int const tabsize = 4;
  68. void tab(int indent)
  69. {
  70. for (int i = 0; i < indent; ++i)
  71. std::cout << ' ';
  72. }
  73. struct mini_xml_printer
  74. {
  75. mini_xml_printer(int indent = 0)
  76. : indent(indent)
  77. {
  78. }
  79. void operator()(mini_xml const& xml) const;
  80. int indent;
  81. };
  82. struct mini_xml_node_printer : boost::static_visitor<>
  83. {
  84. mini_xml_node_printer(int indent = 0)
  85. : indent(indent)
  86. {
  87. }
  88. void operator()(mini_xml const& xml) const
  89. {
  90. mini_xml_printer(indent+tabsize)(xml);
  91. }
  92. void operator()(std::string const& text) const
  93. {
  94. tab(indent+tabsize);
  95. std::cout << "text: \"" << text << '"' << std::endl;
  96. }
  97. int indent;
  98. };
  99. void mini_xml_printer::operator()(mini_xml const& xml) const
  100. {
  101. tab(indent);
  102. std::cout << "tag: " << xml.name << std::endl;
  103. tab(indent);
  104. std::cout << '{' << std::endl;
  105. BOOST_FOREACH(mini_xml_node const& node, xml.children)
  106. {
  107. boost::apply_visitor(mini_xml_node_printer(indent), node);
  108. }
  109. tab(indent);
  110. std::cout << '}' << std::endl;
  111. }
  112. ///////////////////////////////////////////////////////////////////////////
  113. // Our mini XML grammar definition
  114. ///////////////////////////////////////////////////////////////////////////
  115. //[mini_xml2_sr_grammar
  116. template <typename Iterator>
  117. struct mini_xml_grammar
  118. : qi::grammar<Iterator, mini_xml(), ascii::space_type>
  119. {
  120. mini_xml_grammar()
  121. : mini_xml_grammar::base_type(entry)
  122. {
  123. using qi::lit;
  124. using qi::lexeme;
  125. using ascii::char_;
  126. using ascii::string;
  127. using namespace qi::labels;
  128. entry %= (
  129. xml %=
  130. start_tag[_a = _1]
  131. >> *node
  132. >> end_tag(_a)
  133. , node %= xml | text
  134. , text %= lexeme[+(char_ - '<')]
  135. , start_tag %=
  136. '<'
  137. >> !lit('/')
  138. >> lexeme[+(char_ - '>')]
  139. >> '>'
  140. , end_tag %=
  141. "</"
  142. >> lit(_r1)
  143. >> '>'
  144. );
  145. }
  146. qi::rule<Iterator, mini_xml(), ascii::space_type> entry;
  147. repo::qi::subrule<0, mini_xml(), qi::locals<std::string> > xml;
  148. repo::qi::subrule<1, mini_xml_node()> node;
  149. repo::qi::subrule<2, std::string()> text;
  150. repo::qi::subrule<3, std::string()> start_tag;
  151. repo::qi::subrule<4, void(std::string)> end_tag;
  152. };
  153. //]
  154. }
  155. ///////////////////////////////////////////////////////////////////////////////
  156. // Main program
  157. ///////////////////////////////////////////////////////////////////////////////
  158. int main(int argc, char **argv)
  159. {
  160. char const* filename;
  161. if (argc > 1)
  162. {
  163. filename = argv[1];
  164. }
  165. else
  166. {
  167. std::cerr << "Error: No input file provided." << std::endl;
  168. return 1;
  169. }
  170. std::ifstream in(filename, std::ios_base::in);
  171. if (!in)
  172. {
  173. std::cerr << "Error: Could not open input file: "
  174. << filename << std::endl;
  175. return 1;
  176. }
  177. std::string storage; // We will read the contents here.
  178. in.unsetf(std::ios::skipws); // No white space skipping!
  179. std::copy(
  180. std::istream_iterator<char>(in),
  181. std::istream_iterator<char>(),
  182. std::back_inserter(storage));
  183. typedef client::mini_xml_grammar<std::string::const_iterator> mini_xml_grammar;
  184. mini_xml_grammar xml; // Our grammar
  185. client::mini_xml ast; // Our tree
  186. using boost::spirit::ascii::space;
  187. std::string::const_iterator iter = storage.begin();
  188. std::string::const_iterator end = storage.end();
  189. bool r = phrase_parse(iter, end, xml, space, ast);
  190. if (r && iter == end)
  191. {
  192. std::cout << "-------------------------\n";
  193. std::cout << "Parsing succeeded\n";
  194. std::cout << "-------------------------\n";
  195. client::mini_xml_printer printer;
  196. printer(ast);
  197. return 0;
  198. }
  199. else
  200. {
  201. std::string::const_iterator some = iter + std::min(30, int(end - iter));
  202. std::string context(iter, (some>end)?end:some);
  203. std::cout << "-------------------------\n";
  204. std::cout << "Parsing failed\n";
  205. std::cout << "stopped at: \"" << context << "...\"\n";
  206. std::cout << "-------------------------\n";
  207. return 1;
  208. }
  209. }