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 &macros = 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