Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/boost_1_34_1/libs/wave/samples/waveidl/idl.cpp @ 29

Last change on this file since 29 was 29, checked in by landauf, 17 years ago

updated boost from 1_33_1 to 1_34_1

File size: 19.0 KB
Line 
1/*=============================================================================
2    Boost.Wave: A Standard compliant C++ preprocessor library
3
4    Sample: IDL oriented preprocessor
5   
6    http://www.boost.org/
7
8    Copyright (c) 2001-2007 Hartmut Kaiser. Distributed under the Boost
9    Software License, Version 1.0. (See accompanying file
10    LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
11=============================================================================*/
12
13#include "idl.hpp"                  // global configuration
14
15#include <boost/assert.hpp>
16#include <boost/program_options.hpp>
17#include <boost/filesystem/path.hpp>
18
19///////////////////////////////////////////////////////////////////////////////
20//  Include Wave itself
21#include <boost/wave.hpp>
22
23///////////////////////////////////////////////////////////////////////////////
24//  Include the lexer related stuff
25#include <boost/wave/cpplexer/cpp_lex_token.hpp>  // token type
26#include "idllexer/idl_lex_iterator.hpp"          // lexer type
27
28///////////////////////////////////////////////////////////////////////////////
29//  include lexer specifics, import lexer names
30//
31#if BOOST_WAVE_SEPARATE_LEXER_INSTANTIATION == 0
32#include "idllexer/idl_re2c_lexer.hpp"
33#endif
34
35///////////////////////////////////////////////////////////////////////////////
36//  include the grammar definitions, if these shouldn't be compiled separately
37//  (ATTENTION: _very_ large compilation times!)
38//
39#if BOOST_WAVE_SEPARATE_GRAMMAR_INSTANTIATION == 0
40#include <boost/wave/grammars/cpp_intlit_grammar.hpp>
41#include <boost/wave/grammars/cpp_chlit_grammar.hpp>
42#include <boost/wave/grammars/cpp_grammar.hpp>
43#include <boost/wave/grammars/cpp_expression_grammar.hpp>
44#include <boost/wave/grammars/cpp_predef_macros_grammar.hpp>
45#include <boost/wave/grammars/cpp_defined_grammar.hpp>
46#endif
47
48///////////////////////////////////////////////////////////////////////////////
49//  import required names
50using namespace boost::spirit;
51
52using std::string;
53using std::pair;
54using std::vector;
55using std::getline;
56using std::ifstream;
57using std::cout;
58using std::cerr;
59using std::endl;
60using std::ostream;
61using std::istreambuf_iterator;
62
63namespace po = boost::program_options;
64namespace fs = boost::filesystem;
65
66///////////////////////////////////////////////////////////////////////////////
67// print the current version
68int print_version()
69{
70    typedef boost::wave::idllexer::lex_iterator<
71            boost::wave::cpplexer::lex_token<> >
72        lex_iterator_type;
73    typedef boost::wave::context<std::string::iterator, lex_iterator_type>
74        context_type;
75       
76    string version (context_type::get_version_string());
77    cout
78        << version.substr(1, version.size()-2)  // strip quotes
79        << " (" << IDL_VERSION_DATE << ")"      // add date
80        << endl;
81    return 0;                       // exit app
82}
83
84///////////////////////////////////////////////////////////////////////////////
85// print the copyright statement
86int print_copyright()
87{
88    char const *copyright[] = {
89        "",
90        "Sample: IDL oriented preprocessor",
91        "Based on: Wave, A Standard conformant C++ preprocessor library",
92        "It is hosted by http://www.boost.org/.", 
93        "",
94        "Copyright (c) 2001-2006 Hartmut Kaiser, Distributed under the Boost",
95        "Software License, Version 1.0. (See accompanying file",
96        "LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)",
97        0
98    };
99   
100    for (int i = 0; 0 != copyright[i]; ++i)
101        cout << copyright[i] << endl;
102       
103    return 0;                       // exit app
104}
105
106///////////////////////////////////////////////////////////////////////////////
107namespace cmd_line_util {
108
109    // Additional command line parser which interprets '@something' as an
110    // option "config-file" with the value "something".
111    pair<string, string> at_option_parser(string const&s)
112    {
113        if ('@' == s[0]) 
114            return std::make_pair(string("config-file"), s.substr(1));
115        else
116            return pair<string, string>();
117    }
118
119    // class, which keeps include file information read from the command line
120    class include_paths {
121    public:
122        include_paths() : seen_separator(false) {}
123
124        vector<string> paths;       // stores user paths
125        vector<string> syspaths;    // stores system paths
126        bool seen_separator;        // command line contains a '-I-' option
127
128        // Function which validates additional tokens from command line.
129        static void 
130        validate(boost::any &v, vector<string> const &tokens)
131        {
132            if (v.empty())
133                v = boost::any(include_paths());
134
135            include_paths *p = boost::any_cast<include_paths>(&v);
136
137            BOOST_ASSERT(p);
138            // Assume only one path per '-I' occurrence.
139            string t = tokens[0];
140            if (t == "-") {
141            // found -I- option, so switch behaviour
142                p->seen_separator = true;
143            } 
144            else if (p->seen_separator) {
145            // store this path as a system path
146                p->syspaths.push_back(t); 
147            } 
148            else {
149            // store this path as an user path
150                p->paths.push_back(t);
151            }           
152        }
153    };
154
155    // Read all options from a given config file, parse and add them to the
156    // given variables_map
157    void read_config_file_options(string const &filename, 
158        po::options_description const &desc, po::variables_map &vm,
159        bool may_fail = false)
160    {
161    ifstream ifs(filename.c_str());
162
163        if (!ifs.is_open()) {
164            if (!may_fail) {
165                cerr << filename
166                    << ": command line warning: config file not found"
167                    << endl;
168            }
169            return;
170        }
171       
172    vector<string> options;
173    string line;
174
175        while (std::getline(ifs, line)) {
176        // skip empty lines
177            string::size_type pos = line.find_first_not_of(" \t");
178            if (pos == string::npos) 
179                continue;
180
181        // skip comment lines
182            if ('#' != line[pos])
183                options.push_back(line);
184        }
185
186        if (options.size() > 0) {
187            using namespace boost::program_options::command_line_style;
188            po::store(po::command_line_parser(options)
189                .options(desc).style(unix_style).run(), vm);
190            po::notify(vm);
191        }
192    }
193
194    // predicate to extract all positional arguments from the command line
195    struct is_argument {
196        bool operator()(po::option const &opt)
197        {
198          return (opt.position_key == -1) ? true : false;
199        }
200    };
201
202///////////////////////////////////////////////////////////////////////////////
203}
204
205///////////////////////////////////////////////////////////////////////////////
206//
207//  Special validator overload, which allows to handle the -I- syntax for
208//  switching the semantics of an -I option.
209//
210///////////////////////////////////////////////////////////////////////////////
211namespace boost { namespace program_options {
212
213  void validate(boost::any &v, std::vector<std::string> const &s,
214      cmd_line_util::include_paths *, int) 
215  {
216      cmd_line_util::include_paths::validate(v, s);
217  }
218
219}}  // namespace boost::program_options
220
221///////////////////////////////////////////////////////////////////////////////
222//  do the actual preprocessing
223int 
224do_actual_work (std::string file_name, po::variables_map const &vm)
225{
226// current file position is saved for exception handling
227boost::wave::util::file_position_type current_position;
228
229    try {
230    // process the given file
231    ifstream instream(file_name.c_str());
232    string instring;
233
234        if (!instream.is_open()) {
235            cerr << "waveidl: could not open input file: " << file_name << endl;
236            return -1;
237        }
238        instream.unsetf(std::ios::skipws);
239       
240#if defined(BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS)
241        // this is known to be very slow for large files on some systems
242        copy (istream_iterator<char>(instream),
243              istream_iterator<char>(), 
244              inserter(instring, instring.end()));
245#else
246        instring = string(istreambuf_iterator<char>(instream.rdbuf()),
247                          istreambuf_iterator<char>());
248#endif
249
250    //  This sample uses the lex_token type predefined in the Wave library, but
251    //  but uses a custom lexer type.
252        typedef boost::wave::idllexer::lex_iterator<
253                boost::wave::cpplexer::lex_token<> >
254            lex_iterator_type;
255        typedef boost::wave::context<std::string::iterator, lex_iterator_type> 
256            context_type;
257
258    // The C++ preprocessor iterators shouldn't be constructed directly. They
259    // are to be generated through a boost::wave::context<> object. This
260    // boost::wave::context object is additionally to be used to initialize and
261    // define different parameters of the actual preprocessing.
262    // The preprocessing of the input stream is done on the fly behind the
263    // scenes during iteration over the context_type::iterator_type stream.
264    context_type ctx (instring.begin(), instring.end(), file_name.c_str());
265
266    // add include directories to the system include search paths
267        if (vm.count("sysinclude")) {
268            vector<string> const &syspaths = 
269                vm["sysinclude"].as<vector<string> >();
270            vector<string>::const_iterator end = syspaths.end();
271            for (vector<string>::const_iterator cit = syspaths.begin(); 
272                 cit != end; ++cit)
273            {
274                ctx.add_sysinclude_path((*cit).c_str());
275            }
276        }
277       
278    // add include directories to the include search paths
279        if (vm.count("include")) {
280            cmd_line_util::include_paths const &ip = 
281                vm["include"].as<cmd_line_util::include_paths>();
282            vector<string>::const_iterator end = ip.paths.end();
283
284            for (vector<string>::const_iterator cit = ip.paths.begin(); 
285                 cit != end; ++cit)
286            {
287                ctx.add_include_path((*cit).c_str());
288            }
289
290        // if on the command line was given -I- , this has to be propagated
291            if (ip.seen_separator) 
292                ctx.set_sysinclude_delimiter();
293                 
294        // add system include directories to the include path
295            vector<string>::const_iterator sysend = ip.syspaths.end();
296            for (vector<string>::const_iterator syscit = ip.syspaths.begin(); 
297                 syscit != sysend; ++syscit)
298            {
299                ctx.add_sysinclude_path((*syscit).c_str());
300            }
301        }
302   
303    // add additional defined macros
304        if (vm.count("define")) {
305            vector<string> const &macros = vm["define"].as<vector<string> >();
306            vector<string>::const_iterator end = macros.end();
307            for (vector<string>::const_iterator cit = macros.begin(); 
308                 cit != end; ++cit)
309            {
310                ctx.add_macro_definition(*cit);
311            }
312        }
313
314    // add additional predefined macros
315        if (vm.count("predefine")) {
316            vector<string> const &predefmacros = 
317                vm["predefine"].as<vector<string> >();
318            vector<string>::const_iterator end = predefmacros.end();
319            for (vector<string>::const_iterator cit = predefmacros.begin(); 
320                 cit != end; ++cit)
321            {
322                ctx.add_macro_definition(*cit, true);
323            }
324        }
325
326    // undefine specified macros
327        if (vm.count("undefine")) {
328            vector<string> const &undefmacros = 
329                vm["undefine"].as<vector<string> >();
330            vector<string>::const_iterator end = undefmacros.end();
331            for (vector<string>::const_iterator cit = undefmacros.begin(); 
332                 cit != end; ++cit)
333            {
334                ctx.remove_macro_definition((*cit).c_str(), true);
335            }
336        }
337
338    // open the output file
339    std::ofstream output;
340   
341        if (vm.count("output")) {
342        // try to open the file, where to put the preprocessed output
343        string out_file (vm["output"].as<string>());
344       
345            output.open(out_file.c_str());
346            if (!output.is_open()) {
347                cerr << "waveidl: could not open output file: " << out_file
348                     << endl;
349                return -1;
350            }
351        }
352        else {
353        // output the preprocessed result to std::cout
354            output.copyfmt(cout);
355            output.clear(cout.rdstate());
356            static_cast<std::basic_ios<char> &>(output).rdbuf(cout.rdbuf());
357        }
358
359    // analyze the input file
360    context_type::iterator_type first = ctx.begin();
361    context_type::iterator_type last = ctx.end();
362   
363    // loop over all generated tokens outputing the generated text
364        while (first != last) {
365        // print out the string representation of this token (skip comments)
366            using namespace boost::wave;
367
368        // store the last known good token position
369            current_position = (*first).get_position();
370
371        token_id id = token_id(*first);
372
373            if (T_CPPCOMMENT == id || T_NEWLINE == id) {
374            // C++ comment tokens contain the trailing newline
375                output << endl;
376            }
377            else if (id != T_CCOMMENT) {
378            // print out the current token value
379                output << (*first).get_value();
380            }
381            ++first;        // advance to the next token
382        }
383    }
384    catch (boost::wave::cpp_exception const& e) {
385    // some preprocessing error
386        cerr
387            << e.file_name() << "(" << e.line_no() << "): "
388            << e.description() << endl;
389        return 1;
390    }
391    catch (boost::wave::cpplexer::lexing_exception const& e) {
392    // some lexing error
393        cerr
394            << e.file_name() << "(" << e.line_no() << "): "
395            << e.description() << endl;
396        return 2;
397    }
398    catch (std::exception const& e) {
399    // use last recognized token to retrieve the error position
400        cerr
401            << current_position.get_file() 
402            << "(" << current_position.get_line() << "): "
403            << "exception caught: " << e.what()
404            << endl;
405        return 3;
406    }
407    catch (...) {
408    // use last recognized token to retrieve the error position
409        cerr
410            << current_position.get_file() 
411            << "(" << current_position.get_line() << "): "
412            << "unexpected exception caught." << endl;
413        return 4;
414    }
415    return 0;
416}
417
418///////////////////////////////////////////////////////////////////////////////
419//  main entry point
420int
421main (int argc, char *argv[])
422{
423    try {
424    // analyze the command line options and arguments
425   
426    // declare the options allowed from the command line only
427    po::options_description desc_cmdline ("Options allowed on the command line only");
428       
429        desc_cmdline.add_options()
430            ("help,h", "print out program usage (this message)")
431            ("version,v", "print the version number")
432            ("copyright,c", "print out the copyright statement")
433            ("config-file", po::value<vector<string> >(), 
434                "specify a config file (alternatively: @filepath)")
435        ;
436
437    // declare the options allowed on command line and in config files
438    po::options_description desc_generic ("Options allowed additionally in a config file");
439
440        desc_generic.add_options()
441            ("output,o", "specify a file to use for output instead of stdout")
442            ("include,I", po::value<cmd_line_util::include_paths>()->composing(), 
443                "specify an additional include directory")
444            ("sysinclude,S", po::value<vector<string> >()->composing(), 
445                "specify an additional system include directory")
446            ("define,D", po::value<vector<string> >()->composing(), 
447                "specify a macro to define (as macro[=[value]])")
448            ("predefine,P", po::value<vector<string> >()->composing(), 
449                "specify a macro to predefine (as macro[=[value]])")
450            ("undefine,U", po::value<vector<string> >()->composing(), 
451                "specify a macro to undefine")
452        ;
453       
454    // combine the options for the different usage schemes
455    po::options_description desc_overall_cmdline;
456    po::options_description desc_overall_cfgfile;
457
458        desc_overall_cmdline.add(desc_cmdline).add(desc_generic);
459        desc_overall_cfgfile.add(desc_generic);
460       
461    // parse command line and store results
462        using namespace boost::program_options::command_line_style;
463
464    po::parsed_options opts = po::parse_command_line(argc, argv, 
465        desc_overall_cmdline, unix_style, cmd_line_util::at_option_parser);
466    po::variables_map vm;
467   
468        po::store(opts, vm);
469        po::notify(vm);
470       
471    // Try to find a waveidl.cfg in the same directory as the executable was
472    // started from. If this exists, treat it as a wave config file
473    fs::path filename(argv[0], fs::native);
474
475        filename = filename.branch_path() / "waveidl.cfg";
476        cmd_line_util::read_config_file_options(filename.string(), 
477            desc_overall_cfgfile, vm, true);
478
479    // if there is specified at least one config file, parse it and add the
480    // options to the main variables_map
481        if (vm.count("config-file")) {
482            vector<string> const &cfg_files = 
483                vm["config-file"].as<vector<string> >();
484            vector<string>::const_iterator end = cfg_files.end();
485            for (vector<string>::const_iterator cit = cfg_files.begin(); 
486                 cit != end; ++cit)
487            {
488            // parse a single config file and store the results
489                cmd_line_util::read_config_file_options(*cit, 
490                    desc_overall_cfgfile, vm);
491            }
492        }
493
494    // ... act as required
495        if (vm.count("help")) {
496        po::options_description desc_help (
497            "Usage: waveidl [options] [@config-file(s)] file");
498
499            desc_help.add(desc_cmdline).add(desc_generic);
500            cout << desc_help << endl;
501            return 1;
502        }
503       
504        if (vm.count("version")) {
505            return print_version();
506        }
507
508        if (vm.count("copyright")) {
509            return print_copyright();
510        }
511       
512    // extract the arguments from the parsed command line
513    vector<po::option> arguments;
514   
515        std::remove_copy_if(opts.options.begin(), opts.options.end(), 
516            inserter(arguments, arguments.end()), cmd_line_util::is_argument());
517           
518    // if there is no input file given, then exit
519        if (0 == arguments.size() || 0 == arguments[0].value.size()) {
520            cerr << "waveidl: no input file given, "
521                 << "use --help to get a hint." << endl;
522            return 5;
523        }
524
525    // preprocess the given input file
526        return do_actual_work(arguments[0].value[0], vm);
527    }
528    catch (std::exception const& e) {
529        cout << "waveidl: exception caught: " << e.what() << endl;
530        return 6;
531    }
532    catch (...) {
533        cerr << "waveidl: unexpected exception caught." << endl;
534        return 7;
535    }
536}
537
Note: See TracBrowser for help on using the repository browser.