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 // // [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