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