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