summaryrefslogtreecommitdiffstats
path: root/src/boost/libs/wave/samples/hannibal/hannibal.cpp
blob: fa9e1f291568208f4ae80e7eb63f656d1e7a0b87 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
//  Hannibal: partial C++ grammar to parse C++ type information
//  Copyright (c) 2005-2006 Danny Havenith
// 
//  Boost.Wave: A Standard compliant C++ preprocessor
//  Copyright (c) 2001-2010 Hartmut Kaiser
// 
//  http://www.boost.org/
//
//  Distributed under the Boost Software License, Version 1.0. (See accompanying 
//  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

#define HANNIBAL_DUMP_PARSE_TREE 1
//#define HANNIBAL_TRACE_DECLARATIONS
//#define BOOST_SPIRIT_DEBUG

#include <iostream>
#include <fstream>
#include <string>
#include <vector>

#include <boost/spirit/include/classic_ast.hpp>
#include <boost/spirit/include/classic_tree_to_xml.hpp>
#include <boost/program_options.hpp>

///////////////////////////////////////////////////////////////////////////////
//  Include Wave itself
#include <boost/wave.hpp>

///////////////////////////////////////////////////////////////////////////////
// Include the lexer stuff
#include <boost/wave/cpplexer/cpp_lex_token.hpp>    // token class
#include <boost/wave/cpplexer/cpp_lex_iterator.hpp> // lexer class

///////////////////////////////////////////////////////////////////////////////
//  Include the Hannibal grammar
#include "translation_unit_parser.h"
#include "translation_unit_skipper.h"

using std::vector;
using std::string;
namespace po = boost::program_options;

#if HANNIBAL_DUMP_PARSE_TREE != 0
///////////////////////////////////////////////////////////////////////////////
namespace {

    ///////////////////////////////////////////////////////////////////////////
    //  helper routines needed to generate the parse tree XML dump
    typedef boost::wave::cpplexer::lex_token<> token_type;
    
    token_type::string_type  get_token_id(token_type const &t) 
    { 
        using namespace boost::wave;
        return get_token_name(token_id(t)); // boost::wave::token_id(t); 
    }
    
    token_type::string_type get_token_value(token_type const &t) 
    { 
        return t.get_value(); 
    }

///////////////////////////////////////////////////////////////////////////////
}   // unnamed namespace 
#endif

///////////////////////////////////////////////////////////////////////////////
namespace {
    
    ///////////////////////////////////////////////////////////////////////////
    // Parse the command line for input files and  (system-) include paths
    // Prints usage info if needed.
    // returns true if program should continue, false if program must stop
    bool parse_command_line( int argc, char *argv[], po::variables_map &vm)
    {
        //
        // Setup command line structure
        po::options_description visible("Usage: hannibal [options] file");
        visible.add_options()
                ("help,h", "show this help message")
                ("include,I", po::value<vector<string> >(), 
                    "specify additional include directory")
                ("sysinclude,S", po::value<vector<string> >(), 
                    "specify additional system include directory")
                ("define,D", po::value<vector<string> >()->composing(), 
                    "specify a macro to define (as macro[=[value]])")
                ("predefine,P", po::value<vector<string> >()->composing(), 
                    "specify a macro to predefine (as macro[=[value]])")
                ("undefine,U", po::value<vector<string> >()->composing(), 
                    "specify a macro to undefine")
            ;
            
        po::options_description hidden;
        hidden.add_options()
            ("input-file", "input file");

        po::options_description desc;
        desc.add( visible).add( hidden);

        po::positional_options_description p;
        p.add("input-file", 1);

        //
        // Parse
        po::store(po::command_line_parser(argc, argv).
                options(desc).positional(p).run(), vm);
        po::notify(vm);

        //
        // Print usage, if necessary
        if (!vm.count( "input-file") ||  vm.count( "help"))
        {
            std::cout << visible << std::endl;
            return false;
        }
        else 
        {
            return true;
        }
    }

///////////////////////////////////////////////////////////////////////////////
}   // unnamed namespace

///////////////////////////////////////////////////////////////////////////////
// main entry point
int main(int argc, char *argv[])
{
    po::variables_map vm;

    if (!parse_command_line( argc, argv, vm))
    {
        return -1;
    }

   string inputfile = vm["input-file"].as< string>();

// current file position is saved for exception handling
boost::wave::util::file_position_type current_position;

    try {
    //  Open and read in the specified input file.
        std::ifstream instream( inputfile.c_str());
        std::string instring;

        if (!instream.is_open()) {
            std::cerr << "Hannibal: could not open input file: " << inputfile
                      << std::endl;
            return -2;
        }
        instream.unsetf(std::ios::skipws);
        instring = std::string(std::istreambuf_iterator<char>(instream.rdbuf()),
                               std::istreambuf_iterator<char>());

    //  The template boost::wave::cpplexer::lex_token<> is the token type to be 
    //  used by the Wave library.
        typedef boost::wave::cpplexer::lex_token<> token_type;
    
    //  The template boost::wave::cpplexer::lex_iterator<> is the lexer type to
    //  be used by the Wave library.
        typedef boost::wave::cpplexer::lex_iterator<token_type> lex_iterator_type;

    //  This is the resulting context type to use. The first template parameter
    //  should match the iterator type to be used during construction of the
    //  corresponding context object (see below).
        typedef boost::wave::context<
                std::string::iterator, 
                lex_iterator_type,
                boost::wave::iteration_context_policies::load_file_to_string
            > context_type;

    // The preprocessor iterator shouldn't be constructed directly. It is 
    // to be generated through a wave::context<> object. This wave:context<> 
    // object is to be used additionally to initialize and define different 
    // parameters of the actual preprocessing (not done here).
    //
    // The preprocessing of the input stream is done on the fly behind the 
    // scenes during iteration over the context_type::iterator_type stream.
    context_type ctx (instring.begin(), instring.end(), inputfile.c_str());

    // add include directories to the include path
        if (vm.count("include")) {
            vector<string> const &paths = 
                vm["include"].as<vector<string> >();
            vector<string>::const_iterator end = paths.end();
            for (vector<string>::const_iterator cit = paths.begin(); 
                cit != end; ++cit)
            {
                ctx.add_include_path((*cit).c_str());
            }
        }

    // add system include directories to the include path
        if (vm.count("sysinclude")) {
            vector<string> const &syspaths = 
                vm["sysinclude"].as<vector<string> >();
            vector<string>::const_iterator end = syspaths.end();
            for (vector<string>::const_iterator cit = syspaths.begin(); 
                cit != end; ++cit)
            {
                ctx.add_sysinclude_path((*cit).c_str());
            }
        }

    // add additional defined macros 
        if (vm.count("define")) {
            vector<string> const &macros = vm["define"].as<vector<string> >();
            vector<string>::const_iterator end = macros.end();
            for (vector<string>::const_iterator cit = macros.begin(); 
                 cit != end; ++cit)
            {
                ctx.add_macro_definition(*cit);
            }
        }

    // add additional predefined macros 
        if (vm.count("predefine")) {
            vector<string> const &predefmacros = 
                vm["predefine"].as<vector<string> >();
            vector<string>::const_iterator end = predefmacros.end();
            for (vector<string>::const_iterator cit = predefmacros.begin(); 
                 cit != end; ++cit)
            {
                ctx.add_macro_definition(*cit, true);
            }
        }

    // undefine specified macros
        if (vm.count("undefine")) {
            vector<string> const &undefmacros = 
                vm["undefine"].as<vector<string> >();
            vector<string>::const_iterator end = undefmacros.end();
            for (vector<string>::const_iterator cit = undefmacros.begin(); 
                 cit != end; ++cit)
            {
                ctx.remove_macro_definition((*cit).c_str(), true);
            }
        }

    // analyze the input file
    context_type::iterator_type first = ctx.begin();
    context_type::iterator_type last = ctx.end();

    translation_unit_skipper s; 

#if HANNIBAL_DUMP_PARSE_TREE != 0
    typedef boost::spirit::classic::tree_parse_info<context_type::iterator_type> 
        result_type;
    translation_unit_grammar::rule_map_type rule_map;
    translation_unit_grammar g(&rule_map);

    // parse the input file
    result_type pi = boost::spirit::classic::ast_parse(first, last, g, s);
#else
    typedef boost::spirit::classic::parse_info<context_type::iterator_type> 
        result_type;
    translation_unit_grammar g;

    // parse the input file
    result_type pi = boost::spirit::classic::parse(first, last, g, s);
#endif

        if (pi.full) {
            std::cout 
                << "Hannibal: parsed sucessfully: " << inputfile << std::endl;

#if HANNIBAL_DUMP_PARSE_TREE != 0
            // generate xml dump from parse tree, if requested
            boost::spirit::classic::tree_to_xml(std::cerr, pi.trees, "", rule_map, 
                &get_token_id, &get_token_value);
#endif
        }
        else {
            std::cerr 
                << "Hannibal: parsing failed: " << inputfile << std::endl;
            std::cerr 
                << "Hannibal: last recognized token was: " 
                << get_token_id(*pi.stop) << std::endl;

            using boost::wave::util::file_position_type;
            file_position_type const& pos(pi.stop->get_position());
            std::cerr 
                << "Hannibal: at: " << pos.get_file() << "(" << pos.get_line() 
                << "," << pos.get_column() << ")" << std::endl;
            return 1;
        }
    }
    catch (boost::wave::cpp_exception const& e) {
    // some preprocessing error
        std::cerr 
            << e.file_name() << ":" << e.line_no() << ":" << e.column_no() 
            << ": " << e.description() << std::endl;
        return 2;
    }
    catch (boost::wave::cpplexer::lexing_exception const& e) {
    // some lexing error
        std::cerr 
            << e.file_name() << ":" << e.line_no() << ":" << e.column_no() 
            << ": " << e.description() << std::endl;
        return 2;
    }
    catch (std::exception const& e) {
    // use last recognized token to retrieve the error position
        std::cerr 
            << current_position.get_file() 
            << "(" << current_position.get_line() << "): "
            << "exception caught: " << e.what()
            << std::endl;
        return 3;
    }
    catch (...) {
    // use last recognized token to retrieve the error position
        std::cerr 
            << current_position.get_file() 
            << "(" << current_position.get_line() << "): "
            << "unexpected exception caught." << std::endl;
        return 4;
    }
    return 0;
}