| /*============================================================================= |
| Copyright (c) 2001-2008 Hartmut Kaiser |
| Copyright (c) 2001-2003 Daniel Nuffer |
| http://spirit.sourceforge.net/ |
| |
| Use, modification and distribution is subject to the Boost Software |
| License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at |
| http://www.boost.org/LICENSE_1_0.txt) |
| =============================================================================*/ |
| |
| #if !defined(TREE_TO_XML_IPP) |
| #define TREE_TO_XML_IPP |
| |
| #include <cstdio> |
| #include <cstdarg> |
| #include <locale> |
| #include <string> |
| #include <cstring> |
| |
| #include <map> |
| #include <iostream> |
| #include <boost/config.hpp> |
| #include <boost/assert.hpp> |
| |
| #ifdef BOOST_NO_STRINGSTREAM |
| #include <strstream> |
| #define BOOST_SPIRIT_OSSTREAM std::ostrstream |
| inline |
| std::string BOOST_SPIRIT_GETSTRING(std::ostrstream& ss) |
| { |
| ss << std::ends; |
| std::string rval = ss.str(); |
| ss.freeze(false); |
| return rval; |
| } |
| #else |
| #include <sstream> |
| #define BOOST_SPIRIT_GETSTRING(ss) ss.str() |
| #define BOOST_SPIRIT_OSSTREAM std::basic_ostringstream<CharT> |
| #endif |
| |
| namespace boost { namespace spirit { |
| |
| BOOST_SPIRIT_CLASSIC_NAMESPACE_BEGIN |
| |
| namespace impl { |
| |
| /////////////////////////////////////////////////////////////////////////// |
| template <typename CharT> |
| struct string_lit; |
| |
| template <> |
| struct string_lit<char> |
| { |
| static char get(char c) { return c; } |
| static std::string get(char const* str = "") { return str; } |
| }; |
| |
| template <> |
| struct string_lit<wchar_t> |
| { |
| static wchar_t get(char c) |
| { |
| typedef std::ctype<wchar_t> ctype_t; |
| return std::use_facet<ctype_t>(std::locale()).widen(c); |
| } |
| static std::basic_string<wchar_t> get(char const* source = "") |
| { |
| using namespace std; // some systems have size_t in ns std |
| size_t len = strlen(source); |
| std::auto_ptr<wchar_t> result (new wchar_t[len+1]); |
| result.get()[len] = '\0'; |
| |
| // working with wide character streams is supported only if the |
| // platform provides the std::ctype<wchar_t> facet |
| BOOST_ASSERT(std::has_facet<std::ctype<wchar_t> >(std::locale())); |
| |
| std::use_facet<std::ctype<wchar_t> >(std::locale()) |
| .widen(source, source + len, result.get()); |
| return result.get(); |
| } |
| }; |
| } |
| |
| // xml formatting helper classes |
| namespace xml { |
| |
| template <typename CharT> |
| inline void |
| encode (std::basic_string<CharT> &str, char s, char const *r, int len) |
| { |
| typedef typename std::basic_string<CharT>::size_type size_type; |
| |
| size_type pos = 0; |
| while ((pos = str.find_first_of (impl::string_lit<CharT>::get(s), pos)) != |
| size_type(std::basic_string<CharT>::npos)) |
| { |
| str.replace (pos, 1, impl::string_lit<CharT>::get(r)); |
| pos += len; |
| } |
| } |
| |
| template <typename CharT> |
| inline std::basic_string<CharT> |
| encode (std::basic_string<CharT> str) |
| { |
| encode(str, '&', "&", 3); |
| encode(str, '<', "<", 2); |
| encode(str, '>', ">", 2); |
| encode(str, '\r', "\\r", 1); |
| encode(str, '\n', "\\n", 1); |
| return str; |
| } |
| |
| template <typename CharT> |
| inline std::basic_string<CharT> |
| encode (CharT const *text) |
| { |
| return encode (std::basic_string<CharT>(text)); |
| } |
| |
| // format a xml attribute |
| template <typename CharT> |
| struct attribute |
| { |
| attribute() |
| { |
| } |
| |
| attribute (std::basic_string<CharT> const& key_, |
| std::basic_string<CharT> const& value_) |
| : key (key_), value(value_) |
| { |
| } |
| |
| bool has_value() |
| { |
| return value.size() > 0; |
| } |
| |
| std::basic_string<CharT> key; |
| std::basic_string<CharT> value; |
| }; |
| |
| template <typename CharT> |
| inline std::basic_ostream<CharT>& |
| operator<< (std::basic_ostream<CharT> &ostrm, attribute<CharT> const &attr) |
| { |
| if (0 == attr.key.size()) |
| return ostrm; |
| ostrm << impl::string_lit<CharT>::get(" ") << encode(attr.key) |
| << impl::string_lit<CharT>::get("=\"") << encode(attr.value) |
| << impl::string_lit<CharT>::get("\""); |
| return ostrm; |
| } |
| |
| // output a xml element (base class, not used directly) |
| template <typename CharT> |
| class element |
| { |
| protected: |
| element(std::basic_ostream<CharT> &ostrm_, bool incr_indent_ = true) |
| : ostrm(ostrm_), incr_indent(incr_indent_) |
| { |
| if (incr_indent) ++get_indent(); |
| } |
| ~element() |
| { |
| if (incr_indent) --get_indent(); |
| } |
| |
| public: |
| void output_space () |
| { |
| for (int i = 0; i < get_indent(); i++) |
| ostrm << impl::string_lit<CharT>::get(" "); |
| } |
| |
| protected: |
| int &get_indent() |
| { |
| static int indent; |
| |
| return indent; |
| } |
| |
| std::basic_ostream<CharT> &ostrm; |
| bool incr_indent; |
| }; |
| |
| // a xml node |
| template <typename CharT> |
| class node : public element<CharT> |
| { |
| public: |
| node (std::basic_ostream<CharT> &ostrm_, |
| std::basic_string<CharT> const& tag_, attribute<CharT> &attr) |
| : element<CharT>(ostrm_), tag(tag_) |
| { |
| this->output_space(); |
| this->ostrm |
| << impl::string_lit<CharT>::get("<") << tag_ << attr |
| << impl::string_lit<CharT>::get(">\n"); |
| } |
| node (std::basic_ostream<CharT> &ostrm_, |
| std::basic_string<CharT> const& tag_) |
| : element<CharT>(ostrm_), tag(tag_) |
| { |
| this->output_space(); |
| this->ostrm |
| << impl::string_lit<CharT>::get("<") << tag_ |
| << impl::string_lit<CharT>::get(">\n"); |
| } |
| ~node() |
| { |
| this->output_space(); |
| this->ostrm |
| << impl::string_lit<CharT>::get("</") << tag |
| << impl::string_lit<CharT>::get(">\n"); |
| } |
| |
| private: |
| std::basic_string<CharT> tag; |
| }; |
| |
| template <typename CharT> |
| class text : public element<CharT> |
| { |
| public: |
| text (std::basic_ostream<CharT> &ostrm_, |
| std::basic_string<CharT> const& tag, |
| std::basic_string<CharT> const& textlit) |
| : element<CharT>(ostrm_) |
| { |
| this->output_space(); |
| this->ostrm |
| << impl::string_lit<CharT>::get("<") << tag |
| << impl::string_lit<CharT>::get(">") << encode(textlit) |
| << impl::string_lit<CharT>::get("</") << tag |
| << impl::string_lit<CharT>::get(">\n"); |
| } |
| |
| text (std::basic_ostream<CharT> &ostrm_, |
| std::basic_string<CharT> const& tag, |
| std::basic_string<CharT> const& textlit, |
| attribute<CharT> &attr) |
| : element<CharT>(ostrm_) |
| { |
| this->output_space(); |
| this->ostrm |
| << impl::string_lit<CharT>::get("<") << tag << attr |
| << impl::string_lit<CharT>::get(">") << encode(textlit) |
| << impl::string_lit<CharT>::get("</") << tag |
| << impl::string_lit<CharT>::get(">\n"); |
| } |
| |
| text (std::basic_ostream<CharT> &ostrm_, |
| std::basic_string<CharT> const& tag, |
| std::basic_string<CharT> const& textlit, |
| attribute<CharT> &attr1, attribute<CharT> &attr2) |
| : element<CharT>(ostrm_) |
| { |
| this->output_space(); |
| this->ostrm |
| << impl::string_lit<CharT>::get("<") << tag << attr1 << attr2 |
| << impl::string_lit<CharT>::get(">") << encode(textlit) |
| << impl::string_lit<CharT>::get("</") << tag |
| << impl::string_lit<CharT>::get(">\n"); |
| } |
| }; |
| |
| // a xml comment |
| template <typename CharT> |
| class comment : public element<CharT> |
| { |
| public: |
| comment (std::basic_ostream<CharT> &ostrm_, |
| std::basic_string<CharT> const& commentlit) |
| : element<CharT>(ostrm_, false) |
| { |
| if ('\0' != commentlit[0]) |
| { |
| this->output_space(); |
| this->ostrm << impl::string_lit<CharT>::get("<!-- ") |
| << encode(commentlit) |
| << impl::string_lit<CharT>::get(" -->\n"); |
| } |
| } |
| }; |
| |
| // a xml document |
| template <typename CharT> |
| class document : public element<CharT> |
| { |
| public: |
| document (std::basic_ostream<CharT> &ostrm_) |
| : element<CharT>(ostrm_) |
| { |
| this->get_indent() = -1; |
| this->ostrm << impl::string_lit<CharT>::get( |
| "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n"); |
| } |
| |
| document (std::basic_ostream<CharT> &ostrm_, |
| std::basic_string<CharT> const& mainnode, |
| std::basic_string<CharT> const& dtd) |
| : element<CharT>(ostrm_) |
| { |
| this->get_indent() = -1; |
| this->ostrm << impl::string_lit<CharT>::get( |
| "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n"); |
| |
| this->output_space(); |
| this->ostrm << impl::string_lit<CharT>::get("<!DOCTYPE ") << mainnode |
| << impl::string_lit<CharT>::get(" SYSTEM \"") << dtd |
| << impl::string_lit<CharT>::get("\">\n"); |
| } |
| ~document() |
| { |
| BOOST_SPIRIT_ASSERT(-1 == this->get_indent()); |
| } |
| }; |
| |
| } // end of namespace xml |
| |
| namespace impl { |
| |
| /////////////////////////////////////////////////////////////////////////// |
| // look up the rule name from the given parser_id |
| template <typename AssocContainerT> |
| inline typename AssocContainerT::value_type::second_type |
| get_rulename (AssocContainerT const &id_to_name_map, |
| BOOST_SPIRIT_CLASSIC_NS::parser_id const &id) |
| { |
| typename AssocContainerT::const_iterator it = id_to_name_map.find(id); |
| if (it != id_to_name_map.end()) |
| return (*it).second; |
| typedef typename AssocContainerT::value_type::second_type second_t; |
| return second_t(); |
| } |
| |
| // dump a parse tree as xml |
| template < |
| typename CharT, typename IteratorT, typename GetIdT, typename GetValueT |
| > |
| inline void |
| token_to_xml (std::basic_ostream<CharT> &ostrm, IteratorT const &it, |
| bool is_root, GetIdT const &get_token_id, GetValueT const &get_token_value) |
| { |
| BOOST_SPIRIT_OSSTREAM stream; |
| |
| stream << get_token_id(*it) << std::ends; |
| xml::attribute<CharT> token_id ( |
| impl::string_lit<CharT>::get("id"), |
| BOOST_SPIRIT_GETSTRING(stream).c_str()); |
| xml::attribute<CharT> is_root_attr ( |
| impl::string_lit<CharT>::get("is_root"), |
| impl::string_lit<CharT>::get(is_root ? "1" : "")); |
| xml::attribute<CharT> nil; |
| xml::text<CharT>(ostrm, |
| impl::string_lit<CharT>::get("token"), |
| get_token_value(*it).c_str(), |
| token_id, |
| is_root_attr.has_value() ? is_root_attr : nil); |
| } |
| |
| template < |
| typename CharT, typename TreeNodeT, typename AssocContainerT, |
| typename GetIdT, typename GetValueT |
| > |
| inline void |
| tree_node_to_xml (std::basic_ostream<CharT> &ostrm, TreeNodeT const &node, |
| AssocContainerT const& id_to_name_map, GetIdT const &get_token_id, |
| GetValueT const &get_token_value) |
| { |
| typedef typename TreeNodeT::const_iterator node_iter_t; |
| typedef |
| typename TreeNodeT::value_type::parse_node_t::const_iterator_t |
| value_iter_t; |
| |
| xml::attribute<CharT> nil; |
| node_iter_t end = node.end(); |
| for (node_iter_t it = node.begin(); it != end; ++it) |
| { |
| // output a node |
| xml::attribute<CharT> id ( |
| impl::string_lit<CharT>::get("rule"), |
| get_rulename(id_to_name_map, (*it).value.id()).c_str()); |
| xml::node<CharT> currnode (ostrm, |
| impl::string_lit<CharT>::get("parsenode"), |
| (*it).value.id() != 0 && id.has_value() ? id : nil); |
| |
| // first dump the value |
| std::size_t cnt = std::distance((*it).value.begin(), (*it).value.end()); |
| |
| if (1 == cnt) |
| { |
| token_to_xml (ostrm, (*it).value.begin(), |
| (*it).value.is_root(), get_token_id, get_token_value); |
| } |
| else if (cnt > 1) |
| { |
| xml::node<CharT> value (ostrm, |
| impl::string_lit<CharT>::get("value")); |
| bool is_root = (*it).value.is_root(); |
| |
| value_iter_t val_end = (*it).value.end(); |
| for (value_iter_t val_it = (*it).value.begin(); |
| val_it != val_end; ++val_it) |
| { |
| token_to_xml (ostrm, val_it, is_root, get_token_id, |
| get_token_value); |
| } |
| } |
| tree_node_to_xml(ostrm, (*it).children, id_to_name_map, |
| get_token_id, get_token_value); // dump all subnodes |
| } |
| } |
| |
| template <typename CharT, typename TreeNodeT, typename AssocContainerT> |
| inline void |
| tree_node_to_xml (std::basic_ostream<CharT> &ostrm, TreeNodeT const &node, |
| AssocContainerT const& id_to_name_map) |
| { |
| typedef typename TreeNodeT::const_iterator node_iter_t; |
| |
| xml::attribute<CharT> nil; |
| node_iter_t end = node.end(); |
| for (node_iter_t it = node.begin(); it != end; ++it) |
| { |
| // output a node |
| xml::attribute<CharT> id ( |
| impl::string_lit<CharT>::get("rule"), |
| get_rulename(id_to_name_map, (*it).value.id()).c_str()); |
| xml::node<CharT> currnode (ostrm, |
| impl::string_lit<CharT>::get("parsenode"), |
| (*it).value.id() != parser_id() && id.has_value() ? id : nil); |
| |
| // first dump the value |
| if ((*it).value.begin() != (*it).value.end()) |
| { |
| std::basic_string<CharT> tokens ((*it).value.begin(), (*it).value.end()); |
| |
| if (tokens.size() > 0) |
| { |
| // output all subtokens as one string (for better readability) |
| xml::attribute<CharT> is_root ( |
| impl::string_lit<CharT>::get("is_root"), |
| impl::string_lit<CharT>::get((*it).value.is_root() ? "1" : "")); |
| xml::text<CharT>(ostrm, |
| impl::string_lit<CharT>::get("value"), tokens.c_str(), |
| is_root.has_value() ? is_root : nil); |
| } |
| |
| } |
| // dump all subnodes |
| tree_node_to_xml(ostrm, (*it).children, id_to_name_map); |
| } |
| } |
| |
| } // namespace impl |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // dump a parse tree as a xml stream (generic variant) |
| template < |
| typename CharT, typename TreeNodeT, typename AssocContainerT, |
| typename GetIdT, typename GetValueT |
| > |
| inline void |
| basic_tree_to_xml (std::basic_ostream<CharT> &ostrm, TreeNodeT const &tree, |
| std::basic_string<CharT> const &input_line, AssocContainerT const& id_to_name, |
| GetIdT const &get_token_id, GetValueT const &get_token_value) |
| { |
| // generate xml dump |
| xml::document<CharT> doc (ostrm, |
| impl::string_lit<CharT>::get("parsetree"), |
| impl::string_lit<CharT>::get("parsetree.dtd")); |
| xml::comment<CharT> input (ostrm, input_line.c_str()); |
| xml::attribute<CharT> ver ( |
| impl::string_lit<CharT>::get("version"), |
| impl::string_lit<CharT>::get("1.0")); |
| xml::node<CharT> mainnode (ostrm, |
| impl::string_lit<CharT>::get("parsetree"), ver); |
| |
| impl::tree_node_to_xml (ostrm, tree, id_to_name, get_token_id, |
| get_token_value); |
| } |
| |
| // dump a parse tree as a xml steam (for character based parsers) |
| template <typename CharT, typename TreeNodeT, typename AssocContainerT> |
| inline void |
| basic_tree_to_xml (std::basic_ostream<CharT> &ostrm, TreeNodeT const &tree, |
| std::basic_string<CharT> const &input_line, |
| AssocContainerT const& id_to_name) |
| { |
| // generate xml dump |
| xml::document<CharT> doc (ostrm, |
| impl::string_lit<CharT>::get("parsetree"), |
| impl::string_lit<CharT>::get("parsetree.dtd")); |
| xml::comment<CharT> input (ostrm, input_line.c_str()); |
| xml::attribute<CharT> ver ( |
| impl::string_lit<CharT>::get("version"), |
| impl::string_lit<CharT>::get("1.0")); |
| xml::node<CharT> mainnode (ostrm, |
| impl::string_lit<CharT>::get("parsetree"), ver); |
| |
| impl::tree_node_to_xml(ostrm, tree, id_to_name); |
| } |
| |
| template <typename CharT, typename TreeNodeT> |
| inline void |
| basic_tree_to_xml (std::basic_ostream<CharT> &ostrm, TreeNodeT const &tree, |
| std::basic_string<CharT> const &input_line) |
| { |
| return basic_tree_to_xml<CharT>(ostrm, tree, input_line, |
| std::map<BOOST_SPIRIT_CLASSIC_NS::parser_id, std::basic_string<CharT> >()); |
| } |
| |
| BOOST_SPIRIT_CLASSIC_NAMESPACE_END |
| |
| }} // namespace boost::spirit |
| |
| #undef BOOST_SPIRIT_OSSTREAM |
| #undef BOOST_SPIRIT_GETSTRING |
| |
| #endif // !defined(PARSE_TREE_XML_HPP) |