/****************************************************************************** demi-cpp demi-cpp ソースファイル Copyright(C) 2007 Wraith. All rights reserved. Coded by Wraith in May 5, 2007. ******************************************************************************/ /////////////////////////////////////////////////////////////////////////////// // // ■ demicpp.cpp // http://tricklib.com/cxx/ex/demicpp/demicpp.cpp // // □ リファレンス・サポートページ // http://tricklib.com/cxx/ex/demicpp/ // // □ ライセンス情報 // http://tricklib.com/license.htm // // □ コンパイル方法 // case Borland C++ // bcc32 demicpp.cpp // case Visual C++ // cl /GX demicpp.cpp // case CodeWarrior // mwcc demicpp.cpp // case gcc // g++ demicpp.cpp // // ////////////////////////////////////////////////////////////////////////////// // // Includes // #include #include #include #include #include #include #include #include ////////////////////////////////////////////////////////////////////////////// // // nullable_bool // #ifndef NULLABLE_BOOL_H #define NULLABLE_BOOL_H // // この nullable_bool は http://tricklib.com/cxx/ex/boolean/ を元にしています。 // boolean では Windows API 中で使用されている型と衝突するので改名しています。 // #ifdef NULLABLE_BOOL_WITH_EXCEPTION #ifndef NULLABLE_BOOL_EXCEPTION_OBJ #define NULLABLE_BOOL_EXCEPTION_OBJ this #endif #endif class nullable_bool { enum value_type { null_value = -1, false_value = 0, // == (int)false true_value = 1, // == (int)true }; static value_type translate(bool X) { return (value_type)(int)X; // == X? true_value: false_value; } value_type value; public: typedef nullable_bool this_type; bool is_null() const { return null_value == value; } bool is_not_null() const { return !is_null(); } this_type & set_null() { value = null_value; return *this; } nullable_bool() :value(null_value) { } nullable_bool(bool X_value) :value(translate(X_value)) { } nullable_bool(const this_type &X) :value(X.value) { } operator bool () const { #ifdef NULLABLE_BOOL_WITH_EXCEPTION if (is_null()) { throw NULLABLE_BOOL_EXCEPTION_OBJ; } #endif return true_value == value; } this_type operator ! () const { return is_null() ? *this: this_type(false_value == value); } this_type & operator=(bool X_value) { value = translate(X_value); return *this; } this_type & operator=(const this_type &X) { value = X.value; return *this; } this_type & operator|=(bool X) { return *this = *this |X; } this_type & operator|=(const this_type &X) { return *this = *this |X; } this_type & operator&=(bool X) { return *this = *this &X; } this_type & operator&=(const this_type &X) { return *this = *this &X; } this_type & operator^=(bool X) { return *this = *this ^X; } this_type & operator^=(const this_type &X) { return *this = *this ^X; } #ifdef _MSC_VER #pragma warning( push ) #pragma warning( disable : 4927 ) #endif friend inline const nullable_bool operator==(const nullable_bool &A, const nullable_bool &B) { if (nullable_bool::null_value == (A.value |B.value)) { return nullable_bool(); } else { return nullable_bool(A.value == B.value); } } friend inline const nullable_bool operator==(const nullable_bool &A, const bool &B) { if (A.is_null()) { return nullable_bool(); } else { return nullable_bool(A.value == nullable_bool::translate(B)); } } friend inline const nullable_bool operator==(const bool &A, const nullable_bool &B) { if (B.is_null()) { return nullable_bool(); } else { return nullable_bool(nullable_bool::translate(A) == B.value); } } friend inline const nullable_bool operator!=(const nullable_bool &A, const nullable_bool &B) { if (nullable_bool::null_value == (A.value |B.value)) { return nullable_bool(); } else { return nullable_bool(A.value != B.value); } } friend inline const nullable_bool operator!=(const nullable_bool &A, const bool &B) { if (A.is_null()) { return nullable_bool(); } else { return nullable_bool(A.value != nullable_bool::translate(B)); } } friend inline const nullable_bool operator!=(const bool &A, const nullable_bool &B) { if (B.is_null()) { return nullable_bool(); } else { return nullable_bool(nullable_bool::translate(A) != B.value); } } friend inline const nullable_bool operator|(const nullable_bool &A, const nullable_bool &B) { if (nullable_bool::null_value == (A.value |B.value)) { return nullable_bool(); } else { return nullable_bool(A.value |B.value); } } friend inline const nullable_bool operator|(const nullable_bool &A, const bool &B) { if (A.is_null()) { return nullable_bool(); } else { return nullable_bool(A.value |nullable_bool::translate(B)); } } friend inline const nullable_bool operator|(const bool &A, const nullable_bool &B) { if (B.is_null()) { return nullable_bool(); } else { return nullable_bool(nullable_bool::translate(A) |B.value); } } friend inline const nullable_bool operator&(const nullable_bool &A, const nullable_bool &B) { if (nullable_bool::null_value == (A.value |B.value)) { return nullable_bool(); } else { return nullable_bool(A.value &B.value); } } friend inline const nullable_bool operator&(const nullable_bool &A, const bool &B) { if (A.is_null()) { return nullable_bool(); } else { return nullable_bool(A.value &nullable_bool::translate(B)); } } friend inline const nullable_bool operator&(const bool &A, const nullable_bool &B) { if (B.is_null()) { return nullable_bool(); } else { return nullable_bool(nullable_bool::translate(A) &B.value); } } friend inline const nullable_bool operator^(const nullable_bool &A, const nullable_bool &B) { return A != B; } friend inline const nullable_bool operator^(const nullable_bool &A, const bool &B) { return A != B; } friend inline const nullable_bool operator^(const bool &A, const nullable_bool &B) { return A != B; } #ifdef _MSC_VER #pragma warning( pop ) #endif }; // 三態 bool 補助関数 inline const bool nullable_bool_is_null(const nullable_bool &X) { return X.is_null(); } inline const bool nullable_bool_is_null(const bool &) { return false; } inline const bool nullable_bool_is_not_null(const nullable_bool &X) { return X.is_not_null(); } inline const bool nullable_bool_is_not_null(const bool &X) { return true; } #endif // ndef NULLABLE_BOOL_H ////////////////////////////////////////////////////////////////////////////// // // kernel // template class open_stream :public target_stream_T { public: open_stream(const std::string &filename) :target_stream_T(filename.c_str()) { if (!*this) { std::cerr << "ファイル " << filename << " を開けませんでした。"<< std::endl; exit(EXIT_FAILURE); } } operator target_stream_T * () { return this; } }; std::string trim_line(const std::string &original) { std::string result; const char *white_spaces = " \t\r\n\f"; // 便宜上改行コード等もホワイトスペース扱いにする const int buffer_size = 1024; char buffer[buffer_size]; int buffer_i = 0; bool is_first_white_space = false; for(std::string::const_iterator o_i = original.begin(); o_i != original.end(); ++o_i) { if (strchr(white_spaces, *o_i)) { if (is_first_white_space) { is_first_white_space = false; buffer[buffer_i++] = ' '; } } else { is_first_white_space = true; buffer[buffer_i++] = *o_i; } if (buffer_size <= buffer_i) { result.append(buffer, buffer_i); buffer_i = 0; } } if (0 < buffer_i) { result.append(buffer, buffer_i); //buffer_i = 0; } if (1 < result.size() && '#' == result[0] && ' ' == result[1]) { result = "#" +result.substr(2); } if (1 < result.size() && ' ' == *(result.rbegin())) { result = result.substr(0, result.size() -1); } return result; } nullable_bool test_condition(const std::string &a_condition_str, const std::vector &a_extract_macros, const std::vector &a_extract_inv_macros) { nullable_bool result; std::string trimed_condition = trim_line(a_condition_str); // // サポートしていない演算 // const char *unsupported_operators[] = { "~",/*"!",*/"+","-", "*","/","%", "+","-", "<<",">>", "<",">","<=",">=", "==","!=", "&", "^", "|", "&&", "||", "?:", NULL, }; for(const char **unsupported = unsupported_operators; NULL != *unsupported; ++unsupported) { if (std::string::npos != trimed_condition.find(*unsupported)) { return result; } } // // invert(!)の処理 // bool invert = false; std::string::size_type pre_length, now_length = trimed_condition.length(); do { pre_length = now_length; while('!' == trimed_condition[0]) { invert = !invert; trimed_condition = trim_line(trimed_condition.substr(1)); } while('(' == trimed_condition[0] && ')' == *(trimed_condition.rbegin())) { trimed_condition = trim_line(trimed_condition.substr(1, trimed_condition.length()-2)); } now_length = trimed_condition.length(); } while(pre_length != now_length); // // defined の処理 // static const int defined_length = sizeof("defined") -1; // == strlen("defined") if (0 == trimed_condition.find("defined")) { trimed_condition = trim_line(trimed_condition.substr(defined_length)); while('(' == trimed_condition[0] && ')' == *(trimed_condition.rbegin())) { trimed_condition = trim_line(trimed_condition.substr(1, trimed_condition.length()-2)); } if (a_extract_macros.end() != std::find(a_extract_macros.begin(), a_extract_macros.end(), trimed_condition)) { result = !invert; } else if (a_extract_inv_macros.end() != std::find(a_extract_inv_macros.begin(), a_extract_inv_macros.end(), trimed_condition)) { result = invert; } } return result; } nullable_bool match_test(const std::string ¤t_macro, const std::vector &extract_macros, const std::vector &extract_inv_macros) { if (extract_macros.end() != std::find(extract_macros.begin(), extract_macros.end(), current_macro)) { return true; } if (extract_inv_macros.end() != std::find(extract_inv_macros.begin(), extract_inv_macros.end(), current_macro)) { return false; } return nullable_bool(); } namespace directive { const std::string null_directive = "#"; const std::string pragma_directive = "#pragma"; const std::string if_directive = "#if"; const std::string ifdef_directive = "#ifdef"; const std::string ifndef_directive = "#ifndef"; const std::string else_directive = "#else"; const std::string elif_directive = "#elif"; const std::string endif_directive = "#endif"; const std::string include_directive = "#include"; // これより下のディレクティブはこの demicpp.cpp では実際には対応していません。 const std::string define_directive = "#define"; const std::string undef_directive = "#undef"; const std::string line_directive = "#line"; const std::string error_directive = "#error"; } #if defined(__BORLANDC__) #pragma warn -8027 #endif class demicpp { private: nullable_bool is_extract_area; bool is_active_scope; std::istream *ifile; std::ostream *exfile; std::ostream *subfile; const std::vector &extract_macros; const std::vector &extract_inv_macros; public: demicpp ( std::istream *a_ifile, std::ostream *a_exfile, std::ostream *a_subfile, const std::vector &a_extract_macros, const std::vector &a_extract_inv_macros, nullable_bool a_is_extract_area = nullable_bool(), bool a_is_active_scope = false ) :ifile(a_ifile) ,exfile(a_exfile) ,subfile(a_subfile) ,extract_macros(a_extract_macros) ,extract_inv_macros(a_extract_inv_macros) ,is_extract_area(a_is_extract_area) ,is_active_scope(a_is_active_scope) { assert(NULL != a_ifile); bool is_hitted_in_current_level = is_active_scope && is_extract_area; while(!(a_ifile->eof())) { const int buffer_size = 4096; char buffer[buffer_size]; a_ifile->getline(buffer, buffer_size); std::string current_line(buffer, a_ifile->gcount()); std::string trimed_line = trim_line(current_line); // // 各種ディレクティブの処理 // if (0 == trimed_line.find(directive::null_directive)) { // // 分岐ディレクティブ // if (0 == trimed_line.find(directive::if_directive)) { // #if* nullable_bool condition_result; if (0 == trimed_line.find(directive::ifdef_directive)) { // #ifdef const std::string ¤t_macro = trim_line(trimed_line.substr(directive::ifdef_directive.length())); condition_result = match_test(current_macro, extract_macros, extract_inv_macros); } else if (0 == trimed_line.find(directive::ifndef_directive)) { // #ifndef const std::string ¤t_macro = trim_line(trimed_line.substr(directive::ifndef_directive.length())); condition_result = !match_test(current_macro, extract_macros, extract_inv_macros); } else { // #if const std::string & condition_str = trim_line(trimed_line.substr(directive::if_directive.length())); condition_result = test_condition(condition_str, extract_macros, extract_inv_macros); } bool local_is_active_scope = condition_result.is_not_null(); bool local_is_extract_area = local_is_active_scope ? condition_result :is_extract_area; if (!local_is_active_scope) { output_line(current_line); } demicpp(a_ifile, a_exfile, a_subfile, extract_macros, extract_inv_macros, local_is_extract_area, local_is_active_scope); continue; } else if (0 == trimed_line.find(directive::else_directive) || 0 == trimed_line.find(directive::elif_directive)) { // #else if (is_active_scope) { is_extract_area = !is_hitted_in_current_level; is_hitted_in_current_level = true; continue; } } else if (0 == trimed_line.find(directive::endif_directive)) { // #endif if (!is_active_scope) { output_line(current_line); } return; } } // // ... // output_line(current_line); } } void output_line(const std::string ¤t_line) { if (is_extract_area) { if (exfile) { *exfile << current_line.c_str(); if (!(ifile->eof())) { *exfile << std::endl; } } } else { if (subfile) { *subfile << current_line.c_str(); if (!(ifile->eof())) { *subfile << std::endl; } } } } }; #if defined(__BORLANDC__) #pragma warn .8027 #endif const std::string get_filename_from_include_directive(const std::string &line) { assert(line == trim_line(line)); assert(0 == line.find(directive::include_directive)); std::string result; std::string::size_type target_pos = directive::include_directive.length() +1; if (target_pos < line.length()) { result = line.substr(target_pos); std::string::size_type result_length = result.length(); if ( 2 <= result_length && NULL != strchr("\"<", result[0]) && NULL != strchr("\">", result[result_length -1]) ) { result = result.substr(1, result_length -2); } } return result; } void copy_file_contents ( std::istream *a_ifile, std::ostream *a_ofile ) { assert(NULL != a_ifile); assert(NULL != a_ofile); while(!(a_ifile->eof())) { const int buffer_size = 4096; char buffer[buffer_size]; a_ifile->getline(buffer, buffer_size); std::string current_line(buffer, a_ifile->gcount()); *a_ofile << current_line.c_str(); if (!(a_ifile->eof())) { *a_ofile << std::endl; } } } void include ( std::istream *a_ifile, std::ostream *a_ofile, const std::vector &develop_files ) { assert(NULL != a_ifile); assert(NULL != a_ofile); while(!(a_ifile->eof())) { const int buffer_size = 4096; char buffer[buffer_size]; a_ifile->getline(buffer, buffer_size); std::string current_line(buffer, a_ifile->gcount()); std::string trimed_line = trim_line(current_line); if (0 == trimed_line.find(directive::include_directive)) { const std::string filename = get_filename_from_include_directive(trimed_line); if (develop_files.end() != std::find(develop_files.begin(), develop_files.end(), filename)) { copy_file_contents(open_stream(filename), a_ofile); continue; } } *a_ofile << current_line.c_str(); if (!(a_ifile->eof())) { *a_ofile << std::endl; } } } void pragma(std::istream *a_ifile, std::ostream *a_ofile, bool trim_option) { assert(NULL != a_ifile); assert(NULL != a_ofile); while(!(a_ifile->eof())) { const int buffer_size = 4096; char buffer[buffer_size]; a_ifile->getline(buffer, buffer_size); std::string current_line(buffer, a_ifile->gcount()); std::string trimed_line = trim_line(current_line); const std::string &output_line = (trim_option) ? trimed_line: current_line; if (0 == trimed_line.find(directive::pragma_directive)) { *a_ofile << output_line.c_str() << std::endl; } } } ////////////////////////////////////////////////////////////////////////////// // // Command-line Interface // #if defined(__BORLANDC__) #pragma warn -8027 #endif class argument_bank { public: std::string sub_command; std::vector options; std::vector defined_macros; std::vector defined_inv_macros; std::vector target_files; argument_bank(int argc, char *args[]) { if (2 <= argc) { sub_command = args[1]; for(int i = 2; i < argc; ++i) { if (NULL != strchr("/-", args[i][0])) { switch(args[i][1]) { case 'D': switch(args[i][2]) { case 0: if (++i < argc) { switch(args[i][0]) { case '!': if (args[i][1]) { defined_inv_macros.push_back(args[i] +1); } else if (++i < argc) { defined_inv_macros.push_back(args[i]); } break; default: defined_macros.push_back(args[i]); break; } } break; case '!': if (args[i][3]) { defined_inv_macros.push_back(args[i] +3); } else if (++i < argc) { defined_inv_macros.push_back(args[i]); } break; default: defined_macros.push_back(args[i] +2); } break; default: options.push_back(args[i] +1); break; } } else { target_files.push_back(args[i]); } } } } }; #if defined(__BORLANDC__) #pragma warn .8027 #endif int main(int argc, char *args[]) { argument_bank arg_bank(argc, args); if (0 == strcmp("extract", arg_bank.sub_command.c_str())) { if (2 != arg_bank.target_files.size()) { std::cerr << "入力ファイルと出力ファイルを指定してください。" << std::endl; return EXIT_FAILURE; } demicpp ( open_stream(arg_bank.target_files[0]), open_stream(arg_bank.target_files[1]), NULL, arg_bank.defined_macros, arg_bank.defined_inv_macros ); } else if (0 == strcmp("subtract", arg_bank.sub_command.c_str())) { if (2 != arg_bank.target_files.size()) { std::cerr << "入力ファイルと出力ファイルを指定してください。" << std::endl; return EXIT_FAILURE; } demicpp ( open_stream(arg_bank.target_files[0]), NULL, open_stream(arg_bank.target_files[1]), arg_bank.defined_macros, arg_bank.defined_inv_macros ); } else if (0 == strcmp("include", arg_bank.sub_command.c_str())) { if (2 != arg_bank.target_files.size()) { std::cerr << "入力ファイルと出力ファイルを指定してください。" << std::endl; return EXIT_FAILURE; } include ( open_stream(arg_bank.target_files[0]), open_stream(arg_bank.target_files[1]), arg_bank.defined_macros ); } else if (0 == strcmp("pragma", arg_bank.sub_command.c_str())) { if (2 != arg_bank.target_files.size()) { std::cerr << "入力ファイルと出力ファイルを指定してください。" << std::endl; return EXIT_FAILURE; } pragma ( open_stream(arg_bank.target_files[0]), open_stream(arg_bank.target_files[1]), arg_bank.options.end() != std::find(arg_bank.options.begin(), arg_bank.options.end(), "trim") ); } else { if (2 <= argc) { std::cout << std::endl; std::cout << args[1] << " は不明なサブコマンドです。" << std::endl; } std::cout << std::endl; std::cout << "usage : demicpp sub-commnad options" << std::endl; std::cout << std::endl; std::cout << " demicpp extract [-D[!]マクロ1][-D[!]マクロ2][...] 入力ファイル名 出力ファイル名" << std::endl; std::cout << " demicpp subtract [-D[!]マクロ1][-D[!]マクロ2][...] 入力ファイル名 出力ファイル名" << std::endl; std::cout << " demicpp include [-Dファイル名1][-Dファイル名2][...] 入力ファイル名 出力ファイル名" << std::endl; std::cout << " demicpp pragma [trim] 入力ファイル名 出力ファイル名" << std::endl; std::cout << std::endl; std::cout << " extract サブコマンドは指定されたマクロの条件で有効となるブロックを抽出します。" << std::endl; std::cout << std::endl; std::cout << " subtract サブコマンドは指定されたマクロの条件で有効となるブロックを除去します。" << std::endl; std::cout << std::endl; std::cout << " include サブコマンドは指定されたファイルの #include 指定を展開します。" << std::endl; std::cout << std::endl; std::cout << " pragma サブコマンドは条件ディレクティブを無視して全 pragma ディレクティブ行を抽出します。" << std::endl; std::cout << " trim オプションを指定すると余分な余白をトリムした状態で出力します。" << std::endl; std::cout << std::endl; std::cout << "cf. http://tricklib.com/cxx/ex/demicpp/" << std::endl; } return EXIT_SUCCESS; } /****************************************************************************** □■□■ Wraith the Trickster □■□■ ■□■□ 〜I'll go with heaven's advantage and fool's wisdom.〜 ■□■□ ******************************************************************************/