mini_xml2.cpp 6.7 KB

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