]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | /*============================================================================= |
2 | Copyright (c) 2001-2008 Hartmut Kaiser | |
3 | Copyright (c) 2001-2003 Daniel Nuffer | |
4 | http://spirit.sourceforge.net/ | |
5 | ||
6 | Use, modification and distribution is subject to the Boost Software | |
7 | License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at | |
8 | http://www.boost.org/LICENSE_1_0.txt) | |
9 | =============================================================================*/ | |
10 | ||
11 | #if !defined(TREE_TO_XML_IPP) | |
12 | #define TREE_TO_XML_IPP | |
13 | ||
14 | #include <cstdio> | |
15 | #include <cstdarg> | |
16 | #include <locale> | |
17 | #include <string> | |
18 | #include <cstring> | |
19 | ||
20 | #include <map> | |
21 | #include <iostream> | |
22 | #include <boost/config.hpp> | |
23 | #include <boost/assert.hpp> | |
24 | #include <boost/scoped_array.hpp> | |
25 | ||
26 | #ifdef BOOST_NO_STRINGSTREAM | |
27 | #include <strstream> | |
28 | #define BOOST_SPIRIT_OSSTREAM std::ostrstream | |
29 | inline | |
30 | std::string BOOST_SPIRIT_GETSTRING(std::ostrstream& ss) | |
31 | { | |
32 | ss << std::ends; | |
33 | std::string rval = ss.str(); | |
34 | ss.freeze(false); | |
35 | return rval; | |
36 | } | |
37 | #else | |
38 | #include <sstream> | |
39 | #define BOOST_SPIRIT_GETSTRING(ss) ss.str() | |
40 | #define BOOST_SPIRIT_OSSTREAM std::basic_ostringstream<CharT> | |
41 | #endif | |
42 | ||
43 | namespace boost { namespace spirit { | |
44 | ||
45 | BOOST_SPIRIT_CLASSIC_NAMESPACE_BEGIN | |
46 | ||
47 | namespace impl { | |
48 | ||
49 | /////////////////////////////////////////////////////////////////////////// | |
50 | template <typename CharT> | |
51 | struct string_lit; | |
52 | ||
53 | template <> | |
54 | struct string_lit<char> | |
55 | { | |
56 | static char get(char c) { return c; } | |
57 | static std::string get(char const* str = "") { return str; } | |
58 | }; | |
59 | ||
60 | template <> | |
61 | struct string_lit<wchar_t> | |
62 | { | |
63 | static wchar_t get(char c) | |
64 | { | |
65 | typedef std::ctype<wchar_t> ctype_t; | |
66 | return std::use_facet<ctype_t>(std::locale()).widen(c); | |
67 | } | |
68 | static std::basic_string<wchar_t> get(char const* source = "") | |
69 | { | |
70 | using namespace std; // some systems have size_t in ns std | |
71 | size_t len = strlen(source); | |
72 | boost::scoped_array<wchar_t> result (new wchar_t[len+1]); | |
73 | result.get()[len] = '\0'; | |
74 | ||
75 | // working with wide character streams is supported only if the | |
76 | // platform provides the std::ctype<wchar_t> facet | |
77 | BOOST_ASSERT(std::has_facet<std::ctype<wchar_t> >(std::locale())); | |
78 | ||
79 | std::use_facet<std::ctype<wchar_t> >(std::locale()) | |
80 | .widen(source, source + len, result.get()); | |
81 | return result.get(); | |
82 | } | |
83 | }; | |
84 | } | |
85 | ||
86 | // xml formatting helper classes | |
87 | namespace xml { | |
88 | ||
89 | template <typename CharT> | |
90 | inline void | |
91 | encode (std::basic_string<CharT> &str, char s, char const *r, int len) | |
92 | { | |
93 | typedef typename std::basic_string<CharT>::size_type size_type; | |
94 | ||
95 | size_type pos = 0; | |
96 | while ((pos = str.find_first_of (impl::string_lit<CharT>::get(s), pos)) != | |
97 | size_type(std::basic_string<CharT>::npos)) | |
98 | { | |
99 | str.replace (pos, 1, impl::string_lit<CharT>::get(r)); | |
100 | pos += len; | |
101 | } | |
102 | } | |
103 | ||
104 | template <typename CharT> | |
105 | inline std::basic_string<CharT> | |
106 | encode (std::basic_string<CharT> str) | |
107 | { | |
108 | encode(str, '&', "&", 3); | |
109 | encode(str, '<', "<", 2); | |
110 | encode(str, '>', ">", 2); | |
111 | encode(str, '\r', "\\r", 1); | |
112 | encode(str, '\n', "\\n", 1); | |
113 | return str; | |
114 | } | |
115 | ||
116 | template <typename CharT> | |
117 | inline std::basic_string<CharT> | |
118 | encode (CharT const *text) | |
119 | { | |
120 | return encode (std::basic_string<CharT>(text)); | |
121 | } | |
122 | ||
123 | // format a xml attribute | |
124 | template <typename CharT> | |
125 | struct attribute | |
126 | { | |
127 | attribute() | |
128 | { | |
129 | } | |
130 | ||
131 | attribute (std::basic_string<CharT> const& key_, | |
132 | std::basic_string<CharT> const& value_) | |
133 | : key (key_), value(value_) | |
134 | { | |
135 | } | |
136 | ||
137 | bool has_value() | |
138 | { | |
139 | return value.size() > 0; | |
140 | } | |
141 | ||
142 | std::basic_string<CharT> key; | |
143 | std::basic_string<CharT> value; | |
144 | }; | |
145 | ||
146 | template <typename CharT> | |
147 | inline std::basic_ostream<CharT>& | |
148 | operator<< (std::basic_ostream<CharT> &ostrm, attribute<CharT> const &attr) | |
149 | { | |
150 | if (0 == attr.key.size()) | |
151 | return ostrm; | |
152 | ostrm << impl::string_lit<CharT>::get(" ") << encode(attr.key) | |
153 | << impl::string_lit<CharT>::get("=\"") << encode(attr.value) | |
154 | << impl::string_lit<CharT>::get("\""); | |
155 | return ostrm; | |
156 | } | |
157 | ||
158 | // output a xml element (base class, not used directly) | |
159 | template <typename CharT> | |
160 | class element | |
161 | { | |
162 | protected: | |
163 | element(std::basic_ostream<CharT> &ostrm_, bool incr_indent_ = true) | |
164 | : ostrm(ostrm_), incr_indent(incr_indent_) | |
165 | { | |
166 | if (incr_indent) ++get_indent(); | |
167 | } | |
168 | ~element() | |
169 | { | |
170 | if (incr_indent) --get_indent(); | |
171 | } | |
172 | ||
173 | public: | |
174 | void output_space () | |
175 | { | |
176 | for (int i = 0; i < get_indent(); i++) | |
177 | ostrm << impl::string_lit<CharT>::get(" "); | |
178 | } | |
179 | ||
180 | protected: | |
181 | int &get_indent() | |
182 | { | |
183 | static int indent; | |
184 | ||
185 | return indent; | |
186 | } | |
187 | ||
188 | std::basic_ostream<CharT> &ostrm; | |
189 | bool incr_indent; | |
190 | }; | |
191 | ||
192 | // a xml node | |
193 | template <typename CharT> | |
194 | class node : public element<CharT> | |
195 | { | |
196 | public: | |
197 | node (std::basic_ostream<CharT> &ostrm_, | |
198 | std::basic_string<CharT> const& tag_, attribute<CharT> &attr) | |
199 | : element<CharT>(ostrm_), tag(tag_) | |
200 | { | |
201 | this->output_space(); | |
202 | this->ostrm | |
203 | << impl::string_lit<CharT>::get("<") << tag_ << attr | |
204 | << impl::string_lit<CharT>::get(">\n"); | |
205 | } | |
206 | node (std::basic_ostream<CharT> &ostrm_, | |
207 | std::basic_string<CharT> const& tag_) | |
208 | : element<CharT>(ostrm_), tag(tag_) | |
209 | { | |
210 | this->output_space(); | |
211 | this->ostrm | |
212 | << impl::string_lit<CharT>::get("<") << tag_ | |
213 | << impl::string_lit<CharT>::get(">\n"); | |
214 | } | |
215 | ~node() | |
216 | { | |
217 | this->output_space(); | |
218 | this->ostrm | |
219 | << impl::string_lit<CharT>::get("</") << tag | |
220 | << impl::string_lit<CharT>::get(">\n"); | |
221 | } | |
222 | ||
223 | private: | |
224 | std::basic_string<CharT> tag; | |
225 | }; | |
226 | ||
227 | template <typename CharT> | |
228 | class text : public element<CharT> | |
229 | { | |
230 | public: | |
231 | text (std::basic_ostream<CharT> &ostrm_, | |
232 | std::basic_string<CharT> const& tag, | |
233 | std::basic_string<CharT> const& textlit) | |
234 | : element<CharT>(ostrm_) | |
235 | { | |
236 | this->output_space(); | |
237 | this->ostrm | |
238 | << impl::string_lit<CharT>::get("<") << tag | |
239 | << impl::string_lit<CharT>::get(">") << encode(textlit) | |
240 | << impl::string_lit<CharT>::get("</") << tag | |
241 | << impl::string_lit<CharT>::get(">\n"); | |
242 | } | |
243 | ||
244 | text (std::basic_ostream<CharT> &ostrm_, | |
245 | std::basic_string<CharT> const& tag, | |
246 | std::basic_string<CharT> const& textlit, | |
247 | attribute<CharT> &attr) | |
248 | : element<CharT>(ostrm_) | |
249 | { | |
250 | this->output_space(); | |
251 | this->ostrm | |
252 | << impl::string_lit<CharT>::get("<") << tag << attr | |
253 | << impl::string_lit<CharT>::get(">") << encode(textlit) | |
254 | << impl::string_lit<CharT>::get("</") << tag | |
255 | << impl::string_lit<CharT>::get(">\n"); | |
256 | } | |
257 | ||
258 | text (std::basic_ostream<CharT> &ostrm_, | |
259 | std::basic_string<CharT> const& tag, | |
260 | std::basic_string<CharT> const& textlit, | |
261 | attribute<CharT> &attr1, attribute<CharT> &attr2) | |
262 | : element<CharT>(ostrm_) | |
263 | { | |
264 | this->output_space(); | |
265 | this->ostrm | |
266 | << impl::string_lit<CharT>::get("<") << tag << attr1 << attr2 | |
267 | << impl::string_lit<CharT>::get(">") << encode(textlit) | |
268 | << impl::string_lit<CharT>::get("</") << tag | |
269 | << impl::string_lit<CharT>::get(">\n"); | |
270 | } | |
271 | }; | |
272 | ||
273 | // a xml comment | |
274 | template <typename CharT> | |
275 | class comment : public element<CharT> | |
276 | { | |
277 | public: | |
278 | comment (std::basic_ostream<CharT> &ostrm_, | |
279 | std::basic_string<CharT> const& commentlit) | |
280 | : element<CharT>(ostrm_, false) | |
281 | { | |
282 | if ('\0' != commentlit[0]) | |
283 | { | |
284 | this->output_space(); | |
285 | this->ostrm << impl::string_lit<CharT>::get("<!-- ") | |
286 | << encode(commentlit) | |
287 | << impl::string_lit<CharT>::get(" -->\n"); | |
288 | } | |
289 | } | |
290 | }; | |
291 | ||
292 | // a xml document | |
293 | template <typename CharT> | |
294 | class document : public element<CharT> | |
295 | { | |
296 | public: | |
297 | document (std::basic_ostream<CharT> &ostrm_) | |
298 | : element<CharT>(ostrm_) | |
299 | { | |
300 | this->get_indent() = -1; | |
301 | this->ostrm << impl::string_lit<CharT>::get( | |
302 | "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n"); | |
303 | } | |
304 | ||
305 | document (std::basic_ostream<CharT> &ostrm_, | |
306 | std::basic_string<CharT> const& mainnode, | |
307 | std::basic_string<CharT> const& dtd) | |
308 | : element<CharT>(ostrm_) | |
309 | { | |
310 | this->get_indent() = -1; | |
311 | this->ostrm << impl::string_lit<CharT>::get( | |
312 | "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n"); | |
313 | ||
314 | this->output_space(); | |
315 | this->ostrm << impl::string_lit<CharT>::get("<!DOCTYPE ") << mainnode | |
316 | << impl::string_lit<CharT>::get(" SYSTEM \"") << dtd | |
317 | << impl::string_lit<CharT>::get("\">\n"); | |
318 | } | |
319 | ~document() | |
320 | { | |
321 | BOOST_SPIRIT_ASSERT(-1 == this->get_indent()); | |
322 | } | |
323 | }; | |
324 | ||
325 | } // end of namespace xml | |
326 | ||
327 | namespace impl { | |
328 | ||
329 | /////////////////////////////////////////////////////////////////////////// | |
330 | // look up the rule name from the given parser_id | |
331 | template <typename AssocContainerT> | |
332 | inline typename AssocContainerT::value_type::second_type | |
333 | get_rulename (AssocContainerT const &id_to_name_map, | |
334 | BOOST_SPIRIT_CLASSIC_NS::parser_id const &id) | |
335 | { | |
336 | typename AssocContainerT::const_iterator it = id_to_name_map.find(id); | |
337 | if (it != id_to_name_map.end()) | |
338 | return (*it).second; | |
339 | typedef typename AssocContainerT::value_type::second_type second_t; | |
340 | return second_t(); | |
341 | } | |
342 | ||
343 | // dump a parse tree as xml | |
344 | template < | |
345 | typename CharT, typename IteratorT, typename GetIdT, typename GetValueT | |
346 | > | |
347 | inline void | |
348 | token_to_xml (std::basic_ostream<CharT> &ostrm, IteratorT const &it, | |
349 | bool is_root, GetIdT const &get_token_id, GetValueT const &get_token_value) | |
350 | { | |
351 | BOOST_SPIRIT_OSSTREAM stream; | |
352 | ||
353 | stream << get_token_id(*it) << std::ends; | |
354 | xml::attribute<CharT> token_id ( | |
355 | impl::string_lit<CharT>::get("id"), | |
356 | BOOST_SPIRIT_GETSTRING(stream).c_str()); | |
357 | xml::attribute<CharT> is_root_attr ( | |
358 | impl::string_lit<CharT>::get("is_root"), | |
359 | impl::string_lit<CharT>::get(is_root ? "1" : "")); | |
360 | xml::attribute<CharT> nil; | |
361 | xml::text<CharT>(ostrm, | |
362 | impl::string_lit<CharT>::get("token"), | |
363 | get_token_value(*it).c_str(), | |
364 | token_id, | |
365 | is_root_attr.has_value() ? is_root_attr : nil); | |
366 | } | |
367 | ||
368 | template < | |
369 | typename CharT, typename TreeNodeT, typename AssocContainerT, | |
370 | typename GetIdT, typename GetValueT | |
371 | > | |
372 | inline void | |
373 | tree_node_to_xml (std::basic_ostream<CharT> &ostrm, TreeNodeT const &node, | |
374 | AssocContainerT const& id_to_name_map, GetIdT const &get_token_id, | |
375 | GetValueT const &get_token_value) | |
376 | { | |
377 | typedef typename TreeNodeT::const_iterator node_iter_t; | |
378 | typedef | |
379 | typename TreeNodeT::value_type::parse_node_t::const_iterator_t | |
380 | value_iter_t; | |
381 | ||
382 | xml::attribute<CharT> nil; | |
383 | node_iter_t end = node.end(); | |
384 | for (node_iter_t it = node.begin(); it != end; ++it) | |
385 | { | |
386 | // output a node | |
387 | xml::attribute<CharT> id ( | |
388 | impl::string_lit<CharT>::get("rule"), | |
389 | get_rulename(id_to_name_map, (*it).value.id()).c_str()); | |
390 | xml::node<CharT> currnode (ostrm, | |
391 | impl::string_lit<CharT>::get("parsenode"), | |
392 | (*it).value.id() != 0 && id.has_value() ? id : nil); | |
393 | ||
394 | // first dump the value | |
395 | std::size_t cnt = std::distance((*it).value.begin(), (*it).value.end()); | |
396 | ||
397 | if (1 == cnt) | |
398 | { | |
399 | token_to_xml (ostrm, (*it).value.begin(), | |
400 | (*it).value.is_root(), get_token_id, get_token_value); | |
401 | } | |
402 | else if (cnt > 1) | |
403 | { | |
404 | xml::node<CharT> value (ostrm, | |
405 | impl::string_lit<CharT>::get("value")); | |
406 | bool is_root = (*it).value.is_root(); | |
407 | ||
408 | value_iter_t val_end = (*it).value.end(); | |
409 | for (value_iter_t val_it = (*it).value.begin(); | |
410 | val_it != val_end; ++val_it) | |
411 | { | |
412 | token_to_xml (ostrm, val_it, is_root, get_token_id, | |
413 | get_token_value); | |
414 | } | |
415 | } | |
416 | tree_node_to_xml(ostrm, (*it).children, id_to_name_map, | |
417 | get_token_id, get_token_value); // dump all subnodes | |
418 | } | |
419 | } | |
420 | ||
421 | template <typename CharT, typename TreeNodeT, typename AssocContainerT> | |
422 | inline void | |
423 | tree_node_to_xml (std::basic_ostream<CharT> &ostrm, TreeNodeT const &node, | |
424 | AssocContainerT const& id_to_name_map) | |
425 | { | |
426 | typedef typename TreeNodeT::const_iterator node_iter_t; | |
427 | ||
428 | xml::attribute<CharT> nil; | |
429 | node_iter_t end = node.end(); | |
430 | for (node_iter_t it = node.begin(); it != end; ++it) | |
431 | { | |
432 | // output a node | |
433 | xml::attribute<CharT> id ( | |
434 | impl::string_lit<CharT>::get("rule"), | |
435 | get_rulename(id_to_name_map, (*it).value.id()).c_str()); | |
436 | xml::node<CharT> currnode (ostrm, | |
437 | impl::string_lit<CharT>::get("parsenode"), | |
438 | (*it).value.id() != parser_id() && id.has_value() ? id : nil); | |
439 | ||
440 | // first dump the value | |
441 | if ((*it).value.begin() != (*it).value.end()) | |
442 | { | |
443 | std::basic_string<CharT> tokens ((*it).value.begin(), (*it).value.end()); | |
444 | ||
445 | if (tokens.size() > 0) | |
446 | { | |
447 | // output all subtokens as one string (for better readability) | |
448 | xml::attribute<CharT> is_root ( | |
449 | impl::string_lit<CharT>::get("is_root"), | |
450 | impl::string_lit<CharT>::get((*it).value.is_root() ? "1" : "")); | |
451 | xml::text<CharT>(ostrm, | |
452 | impl::string_lit<CharT>::get("value"), tokens.c_str(), | |
453 | is_root.has_value() ? is_root : nil); | |
454 | } | |
455 | ||
456 | } | |
457 | // dump all subnodes | |
458 | tree_node_to_xml(ostrm, (*it).children, id_to_name_map); | |
459 | } | |
460 | } | |
461 | ||
462 | } // namespace impl | |
463 | ||
464 | /////////////////////////////////////////////////////////////////////////////// | |
465 | // dump a parse tree as a xml stream (generic variant) | |
466 | template < | |
467 | typename CharT, typename TreeNodeT, typename AssocContainerT, | |
468 | typename GetIdT, typename GetValueT | |
469 | > | |
470 | inline void | |
471 | basic_tree_to_xml (std::basic_ostream<CharT> &ostrm, TreeNodeT const &tree, | |
472 | std::basic_string<CharT> const &input_line, AssocContainerT const& id_to_name, | |
473 | GetIdT const &get_token_id, GetValueT const &get_token_value) | |
474 | { | |
475 | // generate xml dump | |
476 | xml::document<CharT> doc (ostrm, | |
477 | impl::string_lit<CharT>::get("parsetree"), | |
478 | impl::string_lit<CharT>::get("parsetree.dtd")); | |
479 | xml::comment<CharT> input (ostrm, input_line.c_str()); | |
480 | xml::attribute<CharT> ver ( | |
481 | impl::string_lit<CharT>::get("version"), | |
482 | impl::string_lit<CharT>::get("1.0")); | |
483 | xml::node<CharT> mainnode (ostrm, | |
484 | impl::string_lit<CharT>::get("parsetree"), ver); | |
485 | ||
486 | impl::tree_node_to_xml (ostrm, tree, id_to_name, get_token_id, | |
487 | get_token_value); | |
488 | } | |
489 | ||
490 | // dump a parse tree as a xml steam (for character based parsers) | |
491 | template <typename CharT, typename TreeNodeT, typename AssocContainerT> | |
492 | inline void | |
493 | basic_tree_to_xml (std::basic_ostream<CharT> &ostrm, TreeNodeT const &tree, | |
494 | std::basic_string<CharT> const &input_line, | |
495 | AssocContainerT const& id_to_name) | |
496 | { | |
497 | // generate xml dump | |
498 | xml::document<CharT> doc (ostrm, | |
499 | impl::string_lit<CharT>::get("parsetree"), | |
500 | impl::string_lit<CharT>::get("parsetree.dtd")); | |
501 | xml::comment<CharT> input (ostrm, input_line.c_str()); | |
502 | xml::attribute<CharT> ver ( | |
503 | impl::string_lit<CharT>::get("version"), | |
504 | impl::string_lit<CharT>::get("1.0")); | |
505 | xml::node<CharT> mainnode (ostrm, | |
506 | impl::string_lit<CharT>::get("parsetree"), ver); | |
507 | ||
508 | impl::tree_node_to_xml(ostrm, tree, id_to_name); | |
509 | } | |
510 | ||
511 | template <typename CharT, typename TreeNodeT> | |
512 | inline void | |
513 | basic_tree_to_xml (std::basic_ostream<CharT> &ostrm, TreeNodeT const &tree, | |
514 | std::basic_string<CharT> const &input_line) | |
515 | { | |
516 | return basic_tree_to_xml<CharT>(ostrm, tree, input_line, | |
517 | std::map<BOOST_SPIRIT_CLASSIC_NS::parser_id, std::basic_string<CharT> >()); | |
518 | } | |
519 | ||
520 | BOOST_SPIRIT_CLASSIC_NAMESPACE_END | |
521 | ||
522 | }} // namespace boost::spirit | |
523 | ||
524 | #undef BOOST_SPIRIT_OSSTREAM | |
525 | #undef BOOST_SPIRIT_GETSTRING | |
526 | ||
527 | #endif // !defined(PARSE_TREE_XML_HPP) |