]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
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 | ||
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 | { | |
80 | 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 | { | |
92 | mini_xml_node_printer(int indent = 0) | |
93 | : indent(indent) | |
94 | { | |
95 | } | |
96 | ||
97 | void operator()(mini_xml const& xml) const | |
98 | { | |
99 | mini_xml_printer(indent+tabsize)(xml); | |
100 | } | |
101 | ||
102 | 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 | ||
111 | 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 | { | |
135 | 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 | /////////////////////////////////////////////////////////////////////////////// | |
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 |