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