1 // parse_code_comment.hpp
2
3 #pragma once
4
5 #include <map>
6 #include <fstream>
7 #include <utility>
8
9
10 namespace Comment
11 {
12
13 // source file Line types
14 enum LineType { Blank = 0, Code = 1, CComment = 2, CppComment = 3,
15 CCodeComment = 4, CppCodeComment = 5 };
16
17 // source file line type in string literal style
18 static std::string LineArray[CppCodeComment + 1U] =
19 { "Blank line", "Code only", "C Comment", "C++ Comment",
20 "Code and C Comment", "Code and C++ Comment" };
21
22 // read all characters of a txt style file into buffer and figure out the number of
23 // code lines and the number of comment lines
24 class file_code_comment
25 {
26 public:
27 typedef std::map<int, LineType>::size_type size_type;
28
29 public:
30
31 file_code_comment(const std::string&);
32
33 virtual ~file_code_comment();
34
35 size_type size() const;
36
37 std::pair<int, int> code_comment() const;
38
39 // list line number and type
40 const std::map<int, LineType>& line_types() const;
41
42 private:
43
44 // in case of comment marks in string literals
45 size_type skip_double_quotes(const std::string&, const std::string&, size_type) const;
46
47 std::pair<LineType, bool> cppcode_comment(const std::string&, size_type, size_type) const;
48
49 std::pair<LineType, bool> multicomment(const std::string&, size_type, size_type, size_type) const;
50
51 // [Blank]
52 // if(a == b) [Code]
53 // //
[CppComment]
54 // /*
*/ [CComment]
55 // /*
[CComment]
56 // */ [CComment]
57 //
*/ [CComment]
58 // if(a == b) /*
*/ [CCodeComment]
59 // if(a == b) /*
[CCodeComment]
60 // */ [CCodeComment]
61 //
[CCodeComment]
62 // /*
*/ if(a == b) [CCodeComment]
63 // if(a == b) //
[CppCodeComment]
64
65 // deduce what line type is for a string literal
66 std::pair<LineType, bool> line_type(const std::string&, size_type&) const;
67
68 void read_ccomment(const std::string&, std::ifstream&, int&);
69
70 // Read source and figure out code lines, blank lines, comment lines
71 void read_file(const std::string&);
72
73 size_type skip_leading_space(const std::string&, size_type = 0) const;
74
75
76 private:
77
78 void initialize(const std::string&);
79
80 static const std::string cppcmt; // C++ comment //
81 static const std::string ccmt_start; // C comment start /*
82 static const std::string ccmt_end; // C comment end */
83
84 std::map<int, LineType> m_code_comment; // <line number, LineType>
85 };
86
87 file_code_comment::file_code_comment(const std::string& file) :
88 m_code_comment()
89 {
90 initialize(file);
91 }
92
93 file_code_comment::~file_code_comment() { m_code_comment.clear(); }
94
95 file_code_comment::size_type file_code_comment::
96 skip_leading_space(const std::string& str, size_type idx) const
97 {
98 for(; idx < str.size() && (' ' == str[idx] || '\t' == str[idx]); ++idx);
99 return idx;
100 }
101
102 file_code_comment::size_type file_code_comment::size() const
103 {
104 return m_code_comment.size();
105 }
106
107 std::pair<int, int> file_code_comment::code_comment() const
108 {
109 int code = 0; // the number of code lines
110 int cmt = 0; // the number of comment lines
111 for(std::map<int, LineType>::const_iterator cit(m_code_comment.begin());
112 cit != m_code_comment.end(); ++cit)
113 switch(cit->second)
114 {
115 case Code: ++code; break;
116 case CComment:
117 case CppComment: ++cmt; break;
118 case CCodeComment:
119 case CppCodeComment: ++code; ++cmt; break;
120 case Blank:
121 default: break;
122 }
123 return std::make_pair(code, cmt);
124 }
125
126 // list line number and type
127 const std::map<int, LineType>& file_code_comment::line_types() const
128 {
129 return m_code_comment;
130 }
131
132 // ignore comment marks in double quotes
133 // "
/*
" "
*/
"
134 // "
//
"
135 file_code_comment::size_type file_code_comment::skip_double_quotes
136 (const std::string& str, const std::string& cmt, size_type idx) const
137 {
138 for(; idx < str.size(); ++idx)
139 {
140 idx = str.find(cmt, idx);
141 if(str.size() < idx)
142 {
143 idx = str.size();
144 break;
145 }
146 size_type ridx = str.find('"', idx);
147 if(str.size() < ridx)
148 break;
149 size_type lidx = str.rfind('"', idx);
150 if(str.size() < lidx)
151 break;
152 if(lidx < idx && idx < ridx)
153 idx = ridx;
154 else
155 break;
156 }
157 return idx;
158 }
159
160 // many C style comment blocks in one single line match?
161 std::pair<LineType, bool> file_code_comment::cppcode_comment(const std::string& str,
162 size_type lidx,
163 size_type idx) const
164 {
165 if(lidx == idx)
166 return std::make_pair(CppComment, false);
167 else
168 {
169 idx = str.size();
170 if(lidx < str.size())
171 return std::make_pair(CppCodeComment, false);
172 else
173 return std::make_pair(Code, false);
174 }
175 }
176
177 // /* */ while /* */ // /*
178 // /* ************** */ while {} /* ** */ //
179 // /* ******* */
180
181 std::pair<LineType, bool> file_code_comment::multicomment(const std::string& str,
182 size_type eidx,
183 size_type ridx,
184 size_type idx) const
185 {
186 size_type cnts = 0;
187 size_type cnte = 0;
188 LineType type = CComment;
189 if(idx < ridx)
190 type = CCodeComment;
191 bool code_found = false;
192 while(ridx < eidx)
193 {
194 ++cnts;
195 ridx = skip_double_quotes(str, ccmt_end, ridx + 2U); // +2 skip /*
196 if(ridx < eidx)
197 {
198 ridx += 2U; // +2 skip */
199 idx = ridx;
200 ++cnte;
201 ridx = skip_double_quotes(str, ccmt_start, ridx);
202 if(skip_leading_space(str, idx) < ridx)
203 code_found = true;
204 }
205 }
206 if(code_found)
207 type = CCodeComment;
208 return std::make_pair(type, cnte < cnts);
209 }
210
211 // deduce what line type is for a string literal
212 std::pair<LineType, bool> file_code_comment::line_type(const std::string& str,
213 size_type& idx) const
214 {
215 idx = skip_leading_space(str);
216 if(str.size() == idx)
217 return std::make_pair(Blank, false);
218 else
219 {
220 size_type lidx = skip_double_quotes(str, cppcmt, idx);
221 size_type ridx = skip_double_quotes(str, ccmt_start, idx);
222 /* Somettimes C comments and C++ comments (or Code) are mixed in a single line */
223 if(lidx < ridx) // C++ comment first then CppComment or CppCodeComment
224 return cppcode_comment(str, lidx, idx);
225 else if(ridx < lidx) // C comment first then CComment or CCodeComment
226 return multicomment(str, lidx, ridx, idx);
227 else // no comment
228 return std::make_pair(Code, false);
229 }
230 }
231
232
233 void file_code_comment::read_ccomment(const std::string& str, std::ifstream& fin,
234 int& line)
235 {
236 std::string tstr;
237 while(std::getline(fin, tstr))
238 {
239 ++line;
240 size_type idx = skip_leading_space(tstr);
241 if(tstr.size() == idx)
242 {
243 m_code_comment.insert(std::make_pair(line, Blank));
244 }
245 else
246 {
247 m_code_comment.insert(std::make_pair(line, CComment));
248 if(skip_double_quotes(tstr, ccmt_end, 0) < tstr.size()) // found */
249 break;
250 }
251 }
252 }
253
254 // Read source and figure out code lines, blank lines, comment lines
255 void file_code_comment::read_file(const std::string& file)
256 {
257 std::ifstream fin(file.c_str());
258 if(!fin.is_open())
259 {
260 std::cerr << "Failed to open file '" << file
261 << "' @ Comment::file_code_comment\n";
262 return;
263 }
264 std::string str;
265 size_type idx = 0;
266 int line = 0;
267 while(std::getline(fin, str))
268 {
269 ++line;
270 std::pair<LineType, bool> tp = line_type(str, idx);
271 m_code_comment.insert(std::make_pair(line, tp.first));
272 switch(tp.first)
273 {
274 case Blank:
275 case Code:
276 case CppComment:
277 case CppCodeComment: break;
278 case CComment:
279 case CCodeComment: if(tp.second)
280 read_ccomment(str, fin, line);
281 break;
282 default: break;
283 }
284 }
285 }
286
287 void file_code_comment::initialize(const std::string& file)
288 {
289 read_file(file);
290 }
291
292 const std::string file_code_comment::cppcmt("//"); // C++ comment //
293 const std::string file_code_comment::ccmt_start("/*"); // C comment start /*
294 const std::string file_code_comment::ccmt_end("*/"); // C Comment end */
295
296 } // namespace Comment
297
2
3 #pragma once
4
5 #include <map>
6 #include <fstream>
7 #include <utility>
8
9
10 namespace Comment
11 {
12
13 // source file Line types
14 enum LineType { Blank = 0, Code = 1, CComment = 2, CppComment = 3,
15 CCodeComment = 4, CppCodeComment = 5 };
16
17 // source file line type in string literal style
18 static std::string LineArray[CppCodeComment + 1U] =
19 { "Blank line", "Code only", "C Comment", "C++ Comment",
20 "Code and C Comment", "Code and C++ Comment" };
21
22 // read all characters of a txt style file into buffer and figure out the number of
23 // code lines and the number of comment lines
24 class file_code_comment
25 {
26 public:
27 typedef std::map<int, LineType>::size_type size_type;
28
29 public:
30
31 file_code_comment(const std::string&);
32
33 virtual ~file_code_comment();
34
35 size_type size() const;
36
37 std::pair<int, int> code_comment() const;
38
39 // list line number and type
40 const std::map<int, LineType>& line_types() const;
41
42 private:
43
44 // in case of comment marks in string literals
45 size_type skip_double_quotes(const std::string&, const std::string&, size_type) const;
46
47 std::pair<LineType, bool> cppcode_comment(const std::string&, size_type, size_type) const;
48
49 std::pair<LineType, bool> multicomment(const std::string&, size_type, size_type, size_type) const;
50
51 // [Blank]
52 // if(a == b) [Code]
53 // //

54 // /*

55 // /*

56 // */ [CComment]
57 //

58 // if(a == b) /*

59 // if(a == b) /*

60 // */ [CCodeComment]
61 //

62 // /*

63 // if(a == b) //

64
65 // deduce what line type is for a string literal
66 std::pair<LineType, bool> line_type(const std::string&, size_type&) const;
67
68 void read_ccomment(const std::string&, std::ifstream&, int&);
69
70 // Read source and figure out code lines, blank lines, comment lines
71 void read_file(const std::string&);
72
73 size_type skip_leading_space(const std::string&, size_type = 0) const;
74
75
76 private:
77
78 void initialize(const std::string&);
79
80 static const std::string cppcmt; // C++ comment //
81 static const std::string ccmt_start; // C comment start /*
82 static const std::string ccmt_end; // C comment end */
83
84 std::map<int, LineType> m_code_comment; // <line number, LineType>
85 };
86
87 file_code_comment::file_code_comment(const std::string& file) :
88 m_code_comment()
89 {
90 initialize(file);
91 }
92
93 file_code_comment::~file_code_comment() { m_code_comment.clear(); }
94
95 file_code_comment::size_type file_code_comment::
96 skip_leading_space(const std::string& str, size_type idx) const
97 {
98 for(; idx < str.size() && (' ' == str[idx] || '\t' == str[idx]); ++idx);
99 return idx;
100 }
101
102 file_code_comment::size_type file_code_comment::size() const
103 {
104 return m_code_comment.size();
105 }
106
107 std::pair<int, int> file_code_comment::code_comment() const
108 {
109 int code = 0; // the number of code lines
110 int cmt = 0; // the number of comment lines
111 for(std::map<int, LineType>::const_iterator cit(m_code_comment.begin());
112 cit != m_code_comment.end(); ++cit)
113 switch(cit->second)
114 {
115 case Code: ++code; break;
116 case CComment:
117 case CppComment: ++cmt; break;
118 case CCodeComment:
119 case CppCodeComment: ++code; ++cmt; break;
120 case Blank:
121 default: break;
122 }
123 return std::make_pair(code, cmt);
124 }
125
126 // list line number and type
127 const std::map<int, LineType>& file_code_comment::line_types() const
128 {
129 return m_code_comment;
130 }
131
132 // ignore comment marks in double quotes
133 // "




134 // "


135 file_code_comment::size_type file_code_comment::skip_double_quotes
136 (const std::string& str, const std::string& cmt, size_type idx) const
137 {
138 for(; idx < str.size(); ++idx)
139 {
140 idx = str.find(cmt, idx);
141 if(str.size() < idx)
142 {
143 idx = str.size();
144 break;
145 }
146 size_type ridx = str.find('"', idx);
147 if(str.size() < ridx)
148 break;
149 size_type lidx = str.rfind('"', idx);
150 if(str.size() < lidx)
151 break;
152 if(lidx < idx && idx < ridx)
153 idx = ridx;
154 else
155 break;
156 }
157 return idx;
158 }
159
160 // many C style comment blocks in one single line match?
161 std::pair<LineType, bool> file_code_comment::cppcode_comment(const std::string& str,
162 size_type lidx,
163 size_type idx) const
164 {
165 if(lidx == idx)
166 return std::make_pair(CppComment, false);
167 else
168 {
169 idx = str.size();
170 if(lidx < str.size())
171 return std::make_pair(CppCodeComment, false);
172 else
173 return std::make_pair(Code, false);
174 }
175 }
176
177 // /* */ while /* */ // /*
178 // /* ************** */ while {} /* ** */ //

179 // /* ******* */
180
181 std::pair<LineType, bool> file_code_comment::multicomment(const std::string& str,
182 size_type eidx,
183 size_type ridx,
184 size_type idx) const
185 {
186 size_type cnts = 0;
187 size_type cnte = 0;
188 LineType type = CComment;
189 if(idx < ridx)
190 type = CCodeComment;
191 bool code_found = false;
192 while(ridx < eidx)
193 {
194 ++cnts;
195 ridx = skip_double_quotes(str, ccmt_end, ridx + 2U); // +2 skip /*
196 if(ridx < eidx)
197 {
198 ridx += 2U; // +2 skip */
199 idx = ridx;
200 ++cnte;
201 ridx = skip_double_quotes(str, ccmt_start, ridx);
202 if(skip_leading_space(str, idx) < ridx)
203 code_found = true;
204 }
205 }
206 if(code_found)
207 type = CCodeComment;
208 return std::make_pair(type, cnte < cnts);
209 }
210
211 // deduce what line type is for a string literal
212 std::pair<LineType, bool> file_code_comment::line_type(const std::string& str,
213 size_type& idx) const
214 {
215 idx = skip_leading_space(str);
216 if(str.size() == idx)
217 return std::make_pair(Blank, false);
218 else
219 {
220 size_type lidx = skip_double_quotes(str, cppcmt, idx);
221 size_type ridx = skip_double_quotes(str, ccmt_start, idx);
222 /* Somettimes C comments and C++ comments (or Code) are mixed in a single line */
223 if(lidx < ridx) // C++ comment first then CppComment or CppCodeComment
224 return cppcode_comment(str, lidx, idx);
225 else if(ridx < lidx) // C comment first then CComment or CCodeComment
226 return multicomment(str, lidx, ridx, idx);
227 else // no comment
228 return std::make_pair(Code, false);
229 }
230 }
231
232
233 void file_code_comment::read_ccomment(const std::string& str, std::ifstream& fin,
234 int& line)
235 {
236 std::string tstr;
237 while(std::getline(fin, tstr))
238 {
239 ++line;
240 size_type idx = skip_leading_space(tstr);
241 if(tstr.size() == idx)
242 {
243 m_code_comment.insert(std::make_pair(line, Blank));
244 }
245 else
246 {
247 m_code_comment.insert(std::make_pair(line, CComment));
248 if(skip_double_quotes(tstr, ccmt_end, 0) < tstr.size()) // found */
249 break;
250 }
251 }
252 }
253
254 // Read source and figure out code lines, blank lines, comment lines
255 void file_code_comment::read_file(const std::string& file)
256 {
257 std::ifstream fin(file.c_str());
258 if(!fin.is_open())
259 {
260 std::cerr << "Failed to open file '" << file
261 << "' @ Comment::file_code_comment\n";
262 return;
263 }
264 std::string str;
265 size_type idx = 0;
266 int line = 0;
267 while(std::getline(fin, str))
268 {
269 ++line;
270 std::pair<LineType, bool> tp = line_type(str, idx);
271 m_code_comment.insert(std::make_pair(line, tp.first));
272 switch(tp.first)
273 {
274 case Blank:
275 case Code:
276 case CppComment:
277 case CppCodeComment: break;
278 case CComment:
279 case CCodeComment: if(tp.second)
280 read_ccomment(str, fin, line);
281 break;
282 default: break;
283 }
284 }
285 }
286
287 void file_code_comment::initialize(const std::string& file)
288 {
289 read_file(file);
290 }
291
292 const std::string file_code_comment::cppcmt("//"); // C++ comment //
293 const std::string file_code_comment::ccmt_start("/*"); // C comment start /*
294 const std::string file_code_comment::ccmt_end("*/"); // C Comment end */
295
296 } // namespace Comment
297