token_statistics.cpp 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. /*=============================================================================
  2. Boost.Wave: A Standard compliant C++ preprocessor library
  3. http://www.boost.org/
  4. Copyright (c) 2001-2010 Hartmut Kaiser. Distributed under the Boost
  5. Software License, Version 1.0. (See accompanying file
  6. LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  7. =============================================================================*/
  8. #include "token_statistics.hpp" // config data
  9. ///////////////////////////////////////////////////////////////////////////////
  10. // include required boost libraries
  11. #include <boost/assert.hpp>
  12. #include <boost/program_options.hpp>
  13. ///////////////////////////////////////////////////////////////////////////////
  14. // Include Wave itself
  15. #include <boost/wave.hpp>
  16. ///////////////////////////////////////////////////////////////////////////////
  17. // Include the lexer stuff
  18. #include <boost/wave/cpplexer/cpp_lex_token.hpp> // token class
  19. #include "xlex_iterator.hpp" // lexer class
  20. #include "collect_token_statistics.hpp"
  21. ///////////////////////////////////////////////////////////////////////////////
  22. // import required names
  23. using namespace boost::spirit::classic;
  24. using std::string;
  25. using std::vector;
  26. using std::cout;
  27. using std::cerr;
  28. using std::endl;
  29. using std::ifstream;
  30. using std::ostream;
  31. using std::istreambuf_iterator;
  32. namespace po = boost::program_options;
  33. ///////////////////////////////////////////////////////////////////////////////
  34. namespace cmd_line_util {
  35. // predicate to extract all positional arguments from the command line
  36. struct is_argument {
  37. bool operator()(po::option const &opt)
  38. {
  39. return (opt.position_key == -1) ? true : false;
  40. }
  41. };
  42. ///////////////////////////////////////////////////////////////////////////////
  43. }
  44. ///////////////////////////////////////////////////////////////////////////////
  45. // print the current version
  46. int print_version()
  47. {
  48. // get time of last compilation of this file
  49. boost::wave::util::time_conversion_helper compilation_time(__DATE__ " " __TIME__);
  50. // calculate the number of days since May 9 2005
  51. // (the day the token_statistics project was started)
  52. std::tm first_day;
  53. std::memset (&first_day, 0, sizeof(std::tm));
  54. first_day.tm_mon = 4; // May
  55. first_day.tm_mday = 9; // 09
  56. first_day.tm_year = 105; // 2005
  57. long seconds = long(std::difftime(compilation_time.get_time(),
  58. std::mktime(&first_day)));
  59. cout
  60. << TOKEN_STATISTICS_VERSION_MAJOR << '.'
  61. << TOKEN_STATISTICS_VERSION_MINOR << '.'
  62. << TOKEN_STATISTICS_VERSION_SUBMINOR << '.'
  63. << seconds/(3600*24); // get number of days from seconds
  64. return 1; // exit app
  65. }
  66. ///////////////////////////////////////////////////////////////////////////////
  67. //
  68. int
  69. do_actual_work(vector<string> const &arguments, po::variables_map const &vm)
  70. {
  71. // current file position is saved for exception handling
  72. boost::wave::util::file_position_type current_position;
  73. try {
  74. // this object keeps track of all the statistics
  75. collect_token_statistics stats;
  76. // collect the token statistics for all arguments given
  77. vector<string>::const_iterator lastfile = arguments.end();
  78. for (vector<string>::const_iterator file_it = arguments.begin();
  79. file_it != lastfile; ++file_it)
  80. {
  81. ifstream instream((*file_it).c_str());
  82. string instring;
  83. if (!instream.is_open()) {
  84. cerr << "token_statistics: could not open input file: "
  85. << *file_it << endl;
  86. continue;
  87. }
  88. instream.unsetf(std::ios::skipws);
  89. instring = string(istreambuf_iterator<char>(instream.rdbuf()),
  90. istreambuf_iterator<char>());
  91. // The template boost::wave::cpplexer::lex_token<> is the token type to be
  92. // used by the Wave library.
  93. typedef boost::wave::cpplexer::xlex::xlex_iterator<
  94. boost::wave::cpplexer::lex_token<> >
  95. lexer_type;
  96. typedef boost::wave::context<
  97. std::string::iterator, lexer_type
  98. > context_type;
  99. // The preprocessor iterator shouldn't be constructed directly. It is
  100. // to be generated through a wave::context<> object. This wave:context<>
  101. // object is additionally to be used to initialize and define different
  102. // parameters of the actual preprocessing.
  103. // The preprocessing of the input stream is done on the fly behind the
  104. // scenes during iteration over the context_type::iterator_type stream.
  105. context_type ctx (instring.begin(), instring.end(), (*file_it).c_str());
  106. // add include directories to the include path
  107. if (vm.count("include")) {
  108. vector<string> const &paths =
  109. vm["include"].as<vector<string> >();
  110. vector<string>::const_iterator end = paths.end();
  111. for (vector<string>::const_iterator cit = paths.begin();
  112. cit != end; ++cit)
  113. {
  114. ctx.add_include_path((*cit).c_str());
  115. }
  116. }
  117. // add system include directories to the include path
  118. if (vm.count("sysinclude")) {
  119. vector<string> const &syspaths =
  120. vm["sysinclude"].as<vector<string> >();
  121. vector<string>::const_iterator end = syspaths.end();
  122. for (vector<string>::const_iterator cit = syspaths.begin();
  123. cit != end; ++cit)
  124. {
  125. ctx.add_sysinclude_path((*cit).c_str());
  126. }
  127. }
  128. // analyze the actual file
  129. context_type::iterator_type first = ctx.begin();
  130. context_type::iterator_type last = ctx.end();
  131. while (first != last) {
  132. current_position = (*first).get_position();
  133. stats(*first);
  134. ++first;
  135. }
  136. }
  137. // print out the collected statistics
  138. stats.print();
  139. }
  140. catch (boost::wave::cpp_exception const& e) {
  141. // some preprocessing error
  142. cerr
  143. << e.file_name() << "(" << e.line_no() << "): "
  144. << e.description() << endl;
  145. return 2;
  146. }
  147. catch (std::exception const& e) {
  148. // use last recognized token to retrieve the error position
  149. cerr
  150. << current_position.get_file()
  151. << "(" << current_position.get_line() << "): "
  152. << "exception caught: " << e.what()
  153. << endl;
  154. return 3;
  155. }
  156. catch (...) {
  157. // use last recognized token to retrieve the error position
  158. cerr
  159. << current_position.get_file()
  160. << "(" << current_position.get_line() << "): "
  161. << "unexpected exception caught." << endl;
  162. return 4;
  163. }
  164. return 0;
  165. }
  166. ///////////////////////////////////////////////////////////////////////////////
  167. // here we go!
  168. int
  169. main (int argc, char *argv[])
  170. {
  171. try {
  172. // analyze the command line options and arguments
  173. vector<string> syspathes;
  174. po::options_description desc("Usage: token_statistics [options] file ...");
  175. desc.add_options()
  176. ("help,h", "print out program usage (this message)")
  177. ("version,v", "print the version number")
  178. ("include,I", po::value<vector<string> >(),
  179. "specify additional include directory")
  180. ("sysinclude,S", po::value<vector<string> >(),
  181. "specify additional system include directory")
  182. ;
  183. using namespace boost::program_options::command_line_style;
  184. po::parsed_options opts = po::parse_command_line(argc, argv, desc, unix_style);
  185. po::variables_map vm;
  186. po::store(opts, vm);
  187. po::notify(vm);
  188. if (vm.count("help")) {
  189. cout << desc << endl;
  190. return 1;
  191. }
  192. if (vm.count("version")) {
  193. return print_version();
  194. }
  195. // extract the arguments from the parsed command line
  196. vector<po::option> arguments;
  197. std::remove_copy_if(opts.options.begin(), opts.options.end(),
  198. inserter(arguments, arguments.end()), cmd_line_util::is_argument());
  199. // if there is no input file given, then exit
  200. if (0 == arguments.size() || 0 == arguments[0].value.size()) {
  201. cerr << "token_statistics: No input file given. "
  202. << "Use --help to get a hint." << endl;
  203. return 5;
  204. }
  205. // iterate over all given input files
  206. return do_actual_work(arguments[0].value , vm);
  207. }
  208. catch (std::exception const& e) {
  209. cout << "token_statistics: exception caught: " << e.what() << endl;
  210. return 6;
  211. }
  212. catch (...) {
  213. cerr << "token_statistics: unexpected exception caught." << endl;
  214. return 7;
  215. }
  216. }