├── .flake8 ├── README.md └── c-comments-to-cpp.py /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | max-line-length = 100 3 | exclude = .git 4 | 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## ⚠️ This repository has moved to: https://gitlab.com/mbitsnbites/c-comments-to-cpp 2 | 3 | # C comments to C++ 4 | 5 | This tool converts C style comments to C++ style comments. 6 | 7 | By default the program expects the source file on STDIN, and writes the result 8 | to STDOUT (it is also possible to pass the paths of the input and ouput files 9 | as command line options). 10 | 11 | You can also alter the behavior of the conversion by passing command line 12 | options. 13 | 14 | For more information, run `c-comments-to-cpp.py --help` 15 | 16 | For example, if this is `test.cpp`: 17 | 18 | ```c++ 19 | /***************************************************************************** 20 | * This is a big comment. 21 | *****************************************************************************/ 22 | 23 | /** 24 | * @brief Return value for all library functions. 25 | */ 26 | typedef enum { 27 | STATUS_FAIL = 0, /*!< Failure (zero). */ 28 | STATUS_OK = 1 /*!< Success 29 | (non-zero). */ 30 | } status_t; 31 | 32 | void foo() { 33 | /* Print a message. */ 34 | printf("Hello! \"/* This is not a comment! */\"\n"); /* ...but this is. */ 35 | } 36 | ``` 37 | 38 | Then `c-comments-to-cpp.py test.cpp` outputs: 39 | 40 | ```c++ 41 | //**************************************************************************** 42 | // This is a big comment. 43 | //**************************************************************************** 44 | 45 | /// @brief Return value for all library functions. 46 | typedef enum { 47 | STATUS_FAIL = 0, ///< Failure (zero). 48 | STATUS_OK = 1 ///< Success 49 | ///< (non-zero). 50 | } status_t; 51 | 52 | void foo() { 53 | // Print a message. 54 | printf("Hello! \"/* This is not a comment! */\"\n"); // ...but this is. 55 | } 56 | ``` 57 | 58 | -------------------------------------------------------------------------------- /c-comments-to-cpp.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- mode: Python; tab-width: 4; indent-tabs-mode: nil; -*- 3 | """ 4 | Copyright (C) 2017-2020 Marcus Geelnard 5 | 6 | This software is provided 'as-is', without any express or implied 7 | warranty. In no event will the authors be held liable for any damages 8 | arising from the use of this software. 9 | 10 | Permission is granted to anyone to use this software for any purpose, 11 | including commercial applications, and to alter it and redistribute it 12 | freely, subject to the following restrictions: 13 | 14 | 1. The origin of this software must not be misrepresented; you must not 15 | claim that you wrote the original software. If you use this software 16 | in a product, an acknowledgment in the product documentation would be 17 | appreciated but is not required. 18 | 2. Altered source versions must be plainly marked as such, and must not be 19 | misrepresented as being the original software. 20 | 3. This notice may not be removed or altered from any source distribution. 21 | """ 22 | 23 | import argparse 24 | import sys 25 | 26 | 27 | def convert(in_file, out_file, keep_empty_start_end, drop_empty_lines): 28 | inside_c_comment = False 29 | comment_style = "//" 30 | comment_indent = 0 31 | 32 | for line in in_file: 33 | # Start by dropping trailing whitespace from the input. 34 | line = line.rstrip() 35 | out_line = "" 36 | 37 | # Cater for empty lines in the middle of comments 38 | # which we want to be empty comment lines to make 39 | # it a consistent block. 40 | if (not drop_empty_lines) and len(line) == 0 and inside_c_comment: 41 | out_line += comment_style 42 | 43 | inside_string = False 44 | inside_indentation = True 45 | start_or_end_line = False 46 | k = 0 47 | while k < len(line): 48 | if inside_string: 49 | assert k > 0 50 | if line[k] == '"' and line[k - 1] != "\\": 51 | inside_string = False 52 | out_line += line[k] 53 | k += 1 54 | else: 55 | if inside_c_comment: 56 | if inside_indentation: 57 | if (k >= comment_indent) or not (line[k] in [" ", "\t"]): 58 | inside_indentation = False 59 | out_line += comment_style 60 | # Consume up to len(comment_style) chars from the line. 61 | for m in range(k, min(k + len(comment_style), len(line))): 62 | if line[m] in [" ", "\t"]: 63 | k += 1 64 | elif line[m] == "*" and ( 65 | (m + 1) == len(line) or line[m + 1] != "/" 66 | ): 67 | k += 1 68 | else: 69 | break 70 | if k < len(line) and not (line[k] in [" ", "\t", "*"]): 71 | out_line += " " 72 | if k < len(line): 73 | if ( 74 | line[k] == "*" 75 | and (k + 1) < len(line) 76 | and line[k + 1] == "/" 77 | ): 78 | inside_c_comment = False 79 | start_or_end_line = True 80 | if k > 0 and line[k - 1] == "*": 81 | # Replace '*/' with '**' in case this is a '...*****/'-style line. 82 | out_line += "**" 83 | out_line += "\n" 84 | k += 2 85 | else: 86 | out_line += line[k] 87 | k += 1 88 | else: 89 | if line[k] == "/" and (k + 1) < len(line) and line[k + 1] == "*": 90 | # Start of C style comment. 91 | comment_indent = k 92 | inside_c_comment = True 93 | start_or_end_line = True 94 | comment_style = "//" 95 | k += 2 96 | if ( 97 | (k + 1) < len(line) 98 | and line[k] == "*" 99 | and line[k + 1] == "/" 100 | ): 101 | # Ridiculous case of a completely empty, single line C-style comment 102 | inside_c_comment = False 103 | start_or_end_line = True 104 | break 105 | if k < len(line) and (line[k] == "*" or line[k] == "!"): 106 | if (k + 1) >= len(line) or line[k + 1] != "*": 107 | # Start of Doxygen comment. 108 | comment_style = "///" 109 | k += 1 110 | if k < len(line) and line[k] == "<": 111 | # Start of Doxygen after-member comment. 112 | comment_style = "///<" 113 | k += 1 114 | out_line += comment_style 115 | inside_indentation = False 116 | elif line[k] == "/" and (k + 1) < len(line) and line[k + 1] == "/": 117 | # Start of C++ style comment. 118 | out_line += line[k:] 119 | k = len(line) 120 | else: 121 | if line[k] == '"': 122 | inside_string = True 123 | out_line += line[k] 124 | k += 1 125 | 126 | # Strip trailing whitespace (including newline chars). 127 | out_line = out_line.rstrip() 128 | 129 | # Print output if options and circumstances permit. 130 | empty_comment_line = len(out_line.lstrip()) == len(comment_style) 131 | if ( 132 | (not empty_comment_line) 133 | or (start_or_end_line and keep_empty_start_end) 134 | or (not start_or_end_line and not drop_empty_lines) 135 | ): 136 | out_file.write(f"{out_line}\n") 137 | 138 | 139 | def main(): 140 | # Handle the program arguments. 141 | parser = argparse.ArgumentParser( 142 | description="Convert C-style comments to C++-style." 143 | ) 144 | parser.add_argument( 145 | "--keep-empty-start-end", 146 | action="store_true", 147 | help="preserve empty start/end comment lines", 148 | ) 149 | parser.add_argument( 150 | "--drop-empty-lines", 151 | action="store_true", 152 | help="drop empty lines in comment blocks", 153 | ) 154 | parser.add_argument("infile", nargs="?", help="input file (default: stdin)") 155 | parser.add_argument("outfile", nargs="?", help="output file (default: stdout)") 156 | args = parser.parse_args() 157 | 158 | # Select input and output files. 159 | in_file = sys.stdin 160 | out_file = sys.stdout 161 | if args.infile: 162 | in_file = open(args.infile, "r", encoding="utf8") 163 | if args.outfile: 164 | out_file = open(args.outfile, "w", encoding="utf8") 165 | 166 | convert( 167 | in_file=in_file, 168 | out_file=out_file, 169 | keep_empty_start_end=args.keep_empty_start_end, 170 | drop_empty_lines=args.drop_empty_lines, 171 | ) 172 | 173 | 174 | if __name__ == "__main__": 175 | main() 176 | --------------------------------------------------------------------------------