| 1 | /*============================================================================= |
| 2 | Copyright (c) 2001-2010 Joel de Guzman |
| 3 | Copyright (c) 2001-2010 Hartmut Kaiser |
| 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, Karma is used to print out the generated AST |
| 11 | // |
| 12 | // [ JDG March 25, 2007 ] spirit2 |
| 13 | // [ HK April 02, 2007 ] spirit2 |
| 14 | // |
| 15 | /////////////////////////////////////////////////////////////////////////////// |
| 16 | |
| 17 | #include <boost/spirit/include/qi.hpp> |
| 18 | #include <boost/spirit/include/karma.hpp> |
| 19 | #include <boost/phoenix/core.hpp> |
| 20 | #include <boost/phoenix/operator.hpp> |
| 21 | #include <boost/phoenix/fusion.hpp> |
| 22 | #include <boost/phoenix/function.hpp> |
| 23 | #include <boost/phoenix/stl.hpp> |
| 24 | #include <boost/fusion/include/adapt_struct.hpp> |
| 25 | #include <boost/variant/recursive_variant.hpp> |
| 26 | |
| 27 | #include <iostream> |
| 28 | #include <fstream> |
| 29 | #include <string> |
| 30 | #include <vector> |
| 31 | |
| 32 | using namespace boost::spirit; |
| 33 | using namespace boost::spirit::ascii; |
| 34 | |
| 35 | namespace fusion = boost::fusion; |
| 36 | namespace phoenix = boost::phoenix; |
| 37 | |
| 38 | using phoenix::at_c; |
| 39 | using phoenix::push_back; |
| 40 | |
| 41 | /////////////////////////////////////////////////////////////////////////////// |
| 42 | // Our mini XML tree representation |
| 43 | /////////////////////////////////////////////////////////////////////////////// |
| 44 | struct mini_xml; |
| 45 | |
| 46 | typedef |
| 47 | boost::variant< |
| 48 | boost::recursive_wrapper<mini_xml> |
| 49 | , std::string |
| 50 | > |
| 51 | mini_xml_node; |
| 52 | |
| 53 | struct mini_xml |
| 54 | { |
| 55 | std::string name; // tag name |
| 56 | std::vector<mini_xml_node> children; // children |
| 57 | }; |
| 58 | |
| 59 | // We need to tell fusion about our mini_xml struct |
| 60 | // to make it a first-class fusion citizen |
| 61 | BOOST_FUSION_ADAPT_STRUCT( |
| 62 | mini_xml, |
| 63 | (std::string, name) |
| 64 | (std::vector<mini_xml_node>, children) |
| 65 | ) |
| 66 | |
| 67 | /////////////////////////////////////////////////////////////////////////////// |
| 68 | // Our mini XML grammar definition |
| 69 | /////////////////////////////////////////////////////////////////////////////// |
| 70 | template <typename Iterator> |
| 71 | struct mini_xml_parser : |
| 72 | qi::grammar<Iterator, mini_xml(), space_type> |
| 73 | { |
| 74 | mini_xml_parser() : mini_xml_parser::base_type(xml) |
| 75 | { |
| 76 | text = lexeme[+(char_ - '<') [_val += _1]]; |
| 77 | node = (xml | text) [_val = _1]; |
| 78 | |
| 79 | start_tag = |
| 80 | '<' |
| 81 | >> !lit('/') |
| 82 | >> lexeme[+(char_ - '>') [_val += _1]] |
| 83 | >> '>' |
| 84 | ; |
| 85 | |
| 86 | end_tag = |
| 87 | "</" |
| 88 | >> lit(_r1) |
| 89 | >> '>' |
| 90 | ; |
| 91 | |
| 92 | xml = |
| 93 | start_tag [at_c<0>(tuple: _val) = _1] |
| 94 | >> *node [push_back(a0: at_c<1>(tuple: _val), a1: _1)] |
| 95 | >> end_tag(at_c<0>(tuple: _val)) |
| 96 | ; |
| 97 | } |
| 98 | |
| 99 | qi::rule<Iterator, mini_xml(), space_type> xml; |
| 100 | qi::rule<Iterator, mini_xml_node(), space_type> node; |
| 101 | qi::rule<Iterator, std::string(), space_type> text; |
| 102 | qi::rule<Iterator, std::string(), space_type> start_tag; |
| 103 | qi::rule<Iterator, void(std::string), space_type> end_tag; |
| 104 | }; |
| 105 | |
| 106 | /////////////////////////////////////////////////////////////////////////////// |
| 107 | // A couple of phoenix functions helping to access the elements of the |
| 108 | // generated AST |
| 109 | /////////////////////////////////////////////////////////////////////////////// |
| 110 | template <typename T> |
| 111 | struct get_element |
| 112 | { |
| 113 | template <typename T1> |
| 114 | struct result { typedef T const& type; }; |
| 115 | |
| 116 | T const& operator()(mini_xml_node const& node) const |
| 117 | { |
| 118 | return boost::get<T>(node); |
| 119 | } |
| 120 | }; |
| 121 | |
| 122 | phoenix::function<get_element<std::string> > _string; |
| 123 | phoenix::function<get_element<mini_xml> > _xml; |
| 124 | |
| 125 | /////////////////////////////////////////////////////////////////////////////// |
| 126 | // The output grammar defining the format of the generated data |
| 127 | /////////////////////////////////////////////////////////////////////////////// |
| 128 | template <typename OutputIterator> |
| 129 | struct mini_xml_generator |
| 130 | : karma::grammar<OutputIterator, mini_xml()> |
| 131 | { |
| 132 | mini_xml_generator() : mini_xml_generator::base_type(xml) |
| 133 | { |
| 134 | node %= string | xml; |
| 135 | xml = |
| 136 | '<' << string[_1 = at_c<0>(tuple: _val)] << '>' |
| 137 | << (*node)[_1 = at_c<1>(tuple: _val)] |
| 138 | << "</" << string[_1 = at_c<0>(tuple: _val)] << '>' |
| 139 | ; |
| 140 | } |
| 141 | |
| 142 | karma::rule<OutputIterator, mini_xml()> xml; |
| 143 | karma::rule<OutputIterator, mini_xml_node()> node; |
| 144 | }; |
| 145 | |
| 146 | /////////////////////////////////////////////////////////////////////////////// |
| 147 | // Main program |
| 148 | /////////////////////////////////////////////////////////////////////////////// |
| 149 | int main(int argc, char **argv) |
| 150 | { |
| 151 | char const* filename; |
| 152 | if (argc > 1) |
| 153 | { |
| 154 | filename = argv[1]; |
| 155 | } |
| 156 | else |
| 157 | { |
| 158 | std::cerr << "Error: No input file provided." << std::endl; |
| 159 | return 1; |
| 160 | } |
| 161 | |
| 162 | std::ifstream in(filename, std::ios_base::in); |
| 163 | |
| 164 | if (!in) |
| 165 | { |
| 166 | std::cerr << "Error: Could not open input file: " |
| 167 | << filename << std::endl; |
| 168 | return 1; |
| 169 | } |
| 170 | |
| 171 | std::string storage; // We will read the contents here. |
| 172 | in.unsetf(mask: std::ios::skipws); // No white space skipping! |
| 173 | std::copy( |
| 174 | first: std::istream_iterator<char>(in), |
| 175 | last: std::istream_iterator<char>(), |
| 176 | result: std::back_inserter(x&: storage)); |
| 177 | |
| 178 | typedef mini_xml_parser<std::string::const_iterator> mini_xml_parser; |
| 179 | mini_xml_parser xmlin; // Our grammar definition |
| 180 | mini_xml ast; // our tree |
| 181 | |
| 182 | std::string::const_iterator iter = storage.begin(); |
| 183 | std::string::const_iterator end = storage.end(); |
| 184 | bool r = qi::phrase_parse(first&: iter, last: end, expr: xmlin, skipper: space, attr&: ast); |
| 185 | |
| 186 | if (r && iter == end) |
| 187 | { |
| 188 | std::cout << "-------------------------\n" ; |
| 189 | std::cout << "Parsing succeeded\n" ; |
| 190 | std::cout << "-------------------------\n" ; |
| 191 | |
| 192 | typedef std::back_insert_iterator<std::string> outiter_type; |
| 193 | typedef mini_xml_generator<outiter_type> mini_xml_generator; |
| 194 | |
| 195 | mini_xml_generator xmlout; // Our grammar definition |
| 196 | |
| 197 | std::string generated; |
| 198 | outiter_type outit(generated); |
| 199 | bool r = karma::generate(sink_&: outit, expr: xmlout, attr: ast); |
| 200 | |
| 201 | if (r) |
| 202 | std::cout << generated << std::endl; |
| 203 | return 0; |
| 204 | } |
| 205 | else |
| 206 | { |
| 207 | std::string::const_iterator begin = storage.begin(); |
| 208 | std::size_t dist = std::distance(first: begin, last: iter); |
| 209 | std::string::const_iterator some = |
| 210 | iter + (std::min)(a: storage.size()-dist, b: std::size_t(30)); |
| 211 | std::string context(iter, some); |
| 212 | std::cout << "-------------------------\n" ; |
| 213 | std::cout << "Parsing failed\n" ; |
| 214 | std::cout << "stopped at: \": " << context << "...\"\n" ; |
| 215 | std::cout << "-------------------------\n" ; |
| 216 | return 1; |
| 217 | } |
| 218 | } |
| 219 | |
| 220 | |
| 221 | |