1 // Hannibal: partial C++ grammar to parse C++ type information
2 // Copyright (c) 2005-2006 Danny Havenith
3 //
4 // Boost.Wave: A Standard compliant C++ preprocessor
5 // Copyright (c) 2001-2010 Hartmut Kaiser
6 //
7 // http://www.boost.org/
8 //
9 // Distributed under the Boost Software License, Version 1.0. (See accompanying
10 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
11
12 #define HANNIBAL_DUMP_PARSE_TREE 1
13 //#define HANNIBAL_TRACE_DECLARATIONS
14 //#define BOOST_SPIRIT_DEBUG
15
16 #include <iostream>
17 #include <fstream>
18 #include <string>
19 #include <vector>
20
21 #include <boost/spirit/include/classic_ast.hpp>
22 #include <boost/spirit/include/classic_tree_to_xml.hpp>
23 #include <boost/program_options.hpp>
24
25 ///////////////////////////////////////////////////////////////////////////////
26 // Include Wave itself
27 #include <boost/wave.hpp>
28
29 ///////////////////////////////////////////////////////////////////////////////
30 // Include the lexer stuff
31 #include <boost/wave/cpplexer/cpp_lex_token.hpp> // token class
32 #include <boost/wave/cpplexer/cpp_lex_iterator.hpp> // lexer class
33
34 ///////////////////////////////////////////////////////////////////////////////
35 // Include the Hannibal grammar
36 #include "translation_unit_parser.h"
37 #include "translation_unit_skipper.h"
38
39 using std::vector;
40 using std::string;
41 namespace po = boost::program_options;
42
43 #if HANNIBAL_DUMP_PARSE_TREE != 0
44 ///////////////////////////////////////////////////////////////////////////////
45 namespace {
46
47 ///////////////////////////////////////////////////////////////////////////
48 // helper routines needed to generate the parse tree XML dump
49 typedef boost::wave::cpplexer::lex_token<> token_type;
50
get_token_id(token_type const & t)51 token_type::string_type get_token_id(token_type const &t)
52 {
53 using namespace boost::wave;
54 return get_token_name(token_id(t)); // boost::wave::token_id(t);
55 }
56
get_token_value(token_type const & t)57 token_type::string_type get_token_value(token_type const &t)
58 {
59 return t.get_value();
60 }
61
62 ///////////////////////////////////////////////////////////////////////////////
63 } // unnamed namespace
64 #endif
65
66 ///////////////////////////////////////////////////////////////////////////////
67 namespace {
68
69 ///////////////////////////////////////////////////////////////////////////
70 // Parse the command line for input files and (system-) include paths
71 // Prints usage info if needed.
72 // returns true if program should continue, false if program must stop
parse_command_line(int argc,char * argv[],po::variables_map & vm)73 bool parse_command_line( int argc, char *argv[], po::variables_map &vm)
74 {
75 //
76 // Setup command line structure
77 po::options_description visible("Usage: hannibal [options] file");
78 visible.add_options()
79 ("help,h", "show this help message")
80 ("include,I", po::value<vector<string> >(),
81 "specify additional include directory")
82 ("sysinclude,S", po::value<vector<string> >(),
83 "specify additional system include directory")
84 ("define,D", po::value<vector<string> >()->composing(),
85 "specify a macro to define (as macro[=[value]])")
86 ("predefine,P", po::value<vector<string> >()->composing(),
87 "specify a macro to predefine (as macro[=[value]])")
88 ("undefine,U", po::value<vector<string> >()->composing(),
89 "specify a macro to undefine")
90 ;
91
92 po::options_description hidden;
93 hidden.add_options()
94 ("input-file", "input file");
95
96 po::options_description desc;
97 desc.add( visible).add( hidden);
98
99 po::positional_options_description p;
100 p.add("input-file", 1);
101
102 //
103 // Parse
104 po::store(po::command_line_parser(argc, argv).
105 options(desc).positional(p).run(), vm);
106 po::notify(vm);
107
108 //
109 // Print usage, if necessary
110 if (!vm.count( "input-file") || vm.count( "help"))
111 {
112 std::cout << visible << std::endl;
113 return false;
114 }
115 else
116 {
117 return true;
118 }
119 }
120
121 ///////////////////////////////////////////////////////////////////////////////
122 } // unnamed namespace
123
124 ///////////////////////////////////////////////////////////////////////////////
125 // main entry point
main(int argc,char * argv[])126 int main(int argc, char *argv[])
127 {
128 po::variables_map vm;
129
130 if (!parse_command_line( argc, argv, vm))
131 {
132 return -1;
133 }
134
135 string inputfile = vm["input-file"].as< string>();
136
137 // current file position is saved for exception handling
138 boost::wave::util::file_position_type current_position;
139
140 try {
141 // Open and read in the specified input file.
142 std::ifstream instream( inputfile.c_str());
143 std::string instring;
144
145 if (!instream.is_open()) {
146 std::cerr << "Hannibal: could not open input file: " << inputfile
147 << std::endl;
148 return -2;
149 }
150 instream.unsetf(std::ios::skipws);
151 instring = std::string(std::istreambuf_iterator<char>(instream.rdbuf()),
152 std::istreambuf_iterator<char>());
153
154 // The template boost::wave::cpplexer::lex_token<> is the token type to be
155 // used by the Wave library.
156 typedef boost::wave::cpplexer::lex_token<> token_type;
157
158 // The template boost::wave::cpplexer::lex_iterator<> is the lexer type to
159 // be used by the Wave library.
160 typedef boost::wave::cpplexer::lex_iterator<token_type> lex_iterator_type;
161
162 // This is the resulting context type to use. The first template parameter
163 // should match the iterator type to be used during construction of the
164 // corresponding context object (see below).
165 typedef boost::wave::context<
166 std::string::iterator,
167 lex_iterator_type,
168 boost::wave::iteration_context_policies::load_file_to_string
169 > context_type;
170
171 // The preprocessor iterator shouldn't be constructed directly. It is
172 // to be generated through a wave::context<> object. This wave:context<>
173 // object is to be used additionally to initialize and define different
174 // parameters of the actual preprocessing (not done here).
175 //
176 // The preprocessing of the input stream is done on the fly behind the
177 // scenes during iteration over the context_type::iterator_type stream.
178 context_type ctx (instring.begin(), instring.end(), inputfile.c_str());
179
180 // add include directories to the include path
181 if (vm.count("include")) {
182 vector<string> const &paths =
183 vm["include"].as<vector<string> >();
184 vector<string>::const_iterator end = paths.end();
185 for (vector<string>::const_iterator cit = paths.begin();
186 cit != end; ++cit)
187 {
188 ctx.add_include_path((*cit).c_str());
189 }
190 }
191
192 // add system include directories to the include path
193 if (vm.count("sysinclude")) {
194 vector<string> const &syspaths =
195 vm["sysinclude"].as<vector<string> >();
196 vector<string>::const_iterator end = syspaths.end();
197 for (vector<string>::const_iterator cit = syspaths.begin();
198 cit != end; ++cit)
199 {
200 ctx.add_sysinclude_path((*cit).c_str());
201 }
202 }
203
204 // add additional defined macros
205 if (vm.count("define")) {
206 vector<string> const ¯os = vm["define"].as<vector<string> >();
207 vector<string>::const_iterator end = macros.end();
208 for (vector<string>::const_iterator cit = macros.begin();
209 cit != end; ++cit)
210 {
211 ctx.add_macro_definition(*cit);
212 }
213 }
214
215 // add additional predefined macros
216 if (vm.count("predefine")) {
217 vector<string> const &predefmacros =
218 vm["predefine"].as<vector<string> >();
219 vector<string>::const_iterator end = predefmacros.end();
220 for (vector<string>::const_iterator cit = predefmacros.begin();
221 cit != end; ++cit)
222 {
223 ctx.add_macro_definition(*cit, true);
224 }
225 }
226
227 // undefine specified macros
228 if (vm.count("undefine")) {
229 vector<string> const &undefmacros =
230 vm["undefine"].as<vector<string> >();
231 vector<string>::const_iterator end = undefmacros.end();
232 for (vector<string>::const_iterator cit = undefmacros.begin();
233 cit != end; ++cit)
234 {
235 ctx.remove_macro_definition((*cit).c_str(), true);
236 }
237 }
238
239 // analyze the input file
240 context_type::iterator_type first = ctx.begin();
241 context_type::iterator_type last = ctx.end();
242
243 translation_unit_skipper s;
244
245 #if HANNIBAL_DUMP_PARSE_TREE != 0
246 typedef boost::spirit::classic::tree_parse_info<context_type::iterator_type>
247 result_type;
248 translation_unit_grammar::rule_map_type rule_map;
249 translation_unit_grammar g(&rule_map);
250
251 // parse the input file
252 result_type pi = boost::spirit::classic::ast_parse(first, last, g, s);
253 #else
254 typedef boost::spirit::classic::parse_info<context_type::iterator_type>
255 result_type;
256 translation_unit_grammar g;
257
258 // parse the input file
259 result_type pi = boost::spirit::classic::parse(first, last, g, s);
260 #endif
261
262 if (pi.full) {
263 std::cout
264 << "Hannibal: parsed sucessfully: " << inputfile << std::endl;
265
266 #if HANNIBAL_DUMP_PARSE_TREE != 0
267 // generate xml dump from parse tree, if requested
268 boost::spirit::classic::tree_to_xml(std::cerr, pi.trees, "", rule_map,
269 &get_token_id, &get_token_value);
270 #endif
271 }
272 else {
273 std::cerr
274 << "Hannibal: parsing failed: " << inputfile << std::endl;
275 std::cerr
276 << "Hannibal: last recognized token was: "
277 << get_token_id(*pi.stop) << std::endl;
278
279 using boost::wave::util::file_position_type;
280 file_position_type const& pos(pi.stop->get_position());
281 std::cerr
282 << "Hannibal: at: " << pos.get_file() << "(" << pos.get_line()
283 << "," << pos.get_column() << ")" << std::endl;
284 return 1;
285 }
286 }
287 catch (boost::wave::cpp_exception const& e) {
288 // some preprocessing error
289 std::cerr
290 << e.file_name() << ":" << e.line_no() << ":" << e.column_no()
291 << ": " << e.description() << std::endl;
292 return 2;
293 }
294 catch (boost::wave::cpplexer::lexing_exception const& e) {
295 // some lexing error
296 std::cerr
297 << e.file_name() << ":" << e.line_no() << ":" << e.column_no()
298 << ": " << e.description() << std::endl;
299 return 2;
300 }
301 catch (std::exception const& e) {
302 // use last recognized token to retrieve the error position
303 std::cerr
304 << current_position.get_file()
305 << "(" << current_position.get_line() << "): "
306 << "exception caught: " << e.what()
307 << std::endl;
308 return 3;
309 }
310 catch (...) {
311 // use last recognized token to retrieve the error position
312 std::cerr
313 << current_position.get_file()
314 << "(" << current_position.get_line() << "): "
315 << "unexpected exception caught." << std::endl;
316 return 4;
317 }
318 return 0;
319 }
320