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
32using namespace boost::spirit;
33using namespace boost::spirit::ascii;
34
35namespace fusion = boost::fusion;
36namespace phoenix = boost::phoenix;
37
38using phoenix::at_c;
39using phoenix::push_back;
40
41///////////////////////////////////////////////////////////////////////////////
42// Our mini XML tree representation
43///////////////////////////////////////////////////////////////////////////////
44struct mini_xml;
45
46typedef
47 boost::variant<
48 boost::recursive_wrapper<mini_xml>
49 , std::string
50 >
51mini_xml_node;
52
53struct 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
61BOOST_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///////////////////////////////////////////////////////////////////////////////
70template <typename Iterator>
71struct 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///////////////////////////////////////////////////////////////////////////////
110template <typename T>
111struct 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
122phoenix::function<get_element<std::string> > _string;
123phoenix::function<get_element<mini_xml> > _xml;
124
125///////////////////////////////////////////////////////////////////////////////
126// The output grammar defining the format of the generated data
127///////////////////////////////////////////////////////////////////////////////
128template <typename OutputIterator>
129struct 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///////////////////////////////////////////////////////////////////////////////
149int 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

source code of boost/libs/spirit/example/karma/mini_xml_karma.cpp