]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
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 | ||
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 | { | |
86 | 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 | { | |
98 | mini_xml_node_printer(int indent = 0) | |
99 | : indent(indent) | |
100 | { | |
101 | } | |
102 | ||
103 | void operator()(mini_xml const& xml) const | |
104 | { | |
105 | mini_xml_printer(indent+tabsize)(xml); | |
106 | } | |
107 | ||
108 | 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 | ||
117 | 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 | { | |
141 | 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 | "</" | |
92f5a8d4 | 168 | >> lit(_r1) |
7c673cae FG |
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 | /////////////////////////////////////////////////////////////////////////////// | |
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 | { | |
92f5a8d4 | 236 | std::string::const_iterator some = iter + std::min(30, int(end - iter)); |
7c673cae FG |
237 | std::string context(iter, (some>end)?end:some); |
238 | std::cout << "-------------------------\n"; | |
239 | std::cout << "Parsing failed\n"; | |
92f5a8d4 | 240 | std::cout << "stopped at: \"" << context << "...\"\n"; |
7c673cae FG |
241 | std::cout << "-------------------------\n"; |
242 | return 1; | |
243 | } | |
244 | } | |
245 | ||
246 |