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 >> lit(_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 + std::min(30, int(end - iter));
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