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