hannibal.cpp 11 KB

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