• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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