1 /* Copyright (c) 2002 Hewlett-Packard under GPL version 2 or later */
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <assert.h>
5 #include "pmccabe.h"
6 #include "dmain.h"
7
8 /* $Id: cparse.c,v 1.24 2001/01/26 23:00:30 bame Exp $ */
9
10 int
fancygettoken(char * buf,int classflag,int * line,int * nLine)11 fancygettoken(char *buf, int classflag, int *line, int *nLine)
12 {
13 int c;
14 char tmpbuf[256];
15
16 if ((c = gettoken(buf, line, nLine)) == T_IDENT)
17 {
18 if ((c = gettoken(tmpbuf, NULL, NULL)) == ':')
19 {
20 if ((c = ncss_Getchar()) == ':')
21 {
22 buf += strlen(buf); *buf++ = ':'; *buf++ = ':';
23 *buf = '\0';
24 switch (c = gettoken(tmpbuf, NULL, NULL))
25 {
26 case T_OPERATOR:
27 strcat(buf, tmpbuf);
28 buf += strlen(buf);
29 getoverloadedop(buf);
30 break;
31 case T_IDENT:
32 /* ident::ident - could be recursive */
33 ncss_Ungets(tmpbuf);
34 if ((c = fancygettoken(buf, 1, NULL, NULL)) != T_IDENT)
35 ncss_Ungetc(c);
36 break;
37 case '~': /* destructor, collect the identifier */
38 *buf++ = c;
39 gettoken(buf, NULL, NULL);
40 break;
41 default:
42 ncss_Ungetc(c);
43 *buf++ = '\0';
44 break;
45 }
46 }
47 else
48 {
49 /* only got ':', who knows what this is */
50 ncss_Ungets("::");
51 }
52 }
53 else
54 {
55 ungettoken(c, tmpbuf);
56 }
57
58 c = T_IDENT;
59 }
60 else if (classflag && c == T_OPERATOR)
61 {
62 /* strcat(buf, tmpbuf); */
63 buf += strlen(buf);
64 getoverloadedop(buf);
65 }
66 else if (classflag && c == '~')
67 {
68 *buf++ = c;
69 c = gettoken(buf, NULL, NULL);
70 if (c != T_IDENT)
71 {
72 fprintf(stderr, "fatal error file %s line %d\n",
73 __FILE__, __LINE__);
74 exit(3);
75 }
76 }
77
78 return c;
79 }
80
81 int
toplevelstatement(stats_t * stats)82 toplevelstatement(stats_t *stats)
83 /*
84 * At the top level of a C file, the statements are blocks of
85 * tokens ending in either ; or are function definitions which
86 * end in }. Interesting types of statements include class
87 * and struct definitions - because they may contain inline functions,
88 * and function definitions. All others are merely counted.
89 */
90 {
91 int endofstatement = FALSE;
92 int c;
93 char buf[1024];
94 int functionFirstLine = -1;
95 int functionFirstNLine = -1;
96 int functionDefLine;
97 int line, nLine;
98
99 buf[0] = '\0';
100
101 c = skipws();
102 ncss_Ungetc(c);
103
104 /* gettoken eats whitespace */
105 while (!endofstatement &&
106 (c = fancygettoken(buf, stats->type == STATS_CLASS, &line, &nLine)) != EOF)
107 {
108 if (functionFirstLine == -1)
109 {
110 functionFirstLine = line;
111 functionFirstNLine = nLine;
112 }
113 switch (c)
114 {
115 case T_CLASS:
116 case T_STRUCT:
117 case T_UNION:
118 if (maybeclass())
119 {
120 stats->nsemicolons--;
121 endofstatement = TRUE;
122 }
123 break;
124
125 case T_NAMESPACE:
126 if (maybenamespace())
127 {
128 /* no trailing semicolon for namespaces */
129 endofstatement = TRUE;
130 }
131 break;
132
133 case '(':
134 /* possible start of function */
135 functionDefLine = Line;
136 possiblefn(stats, buf, functionFirstLine, functionDefLine, functionFirstNLine);
137 endofstatement = TRUE;
138 break;
139
140 case '}':
141 case ')':
142 Exit = 2;
143 {
144 char _buf[100];
145 sprintf(_buf, "too many %c's", c);
146 fileerror(_buf);
147 }
148 break;
149
150 case '{':
151 c = matchcurly();
152 break;
153
154 case ':': /* This should catch C++ "class foo { public: } */
155 case ';':
156 /* end of statement */
157 endofstatement = TRUE;
158 break;
159
160 default:
161 break;
162 }
163 }
164
165 return c;
166 }
167
168 int
findchar(char fc)169 findchar(char fc)
170 {
171 int c;
172
173 while ((c = ncss_Getchar()) != EOF && c != fc)
174 {
175 }
176
177 return c;
178 }
179
180 int
maybeclass()181 maybeclass()
182 /*
183 * We've just seen "class" at the top level in a file so
184 * we may be entering a definition of same. If so, we want to be
185 * on the lookout for inline functions. Return 1 if this is a
186 * class definition else restore the function name and return 0.
187 */
188 {
189 char classname[256], dummy[256];
190 int isclass = 0;
191 int c;
192
193 if ((c =gettoken(classname, NULL, NULL)) == T_IDENT)
194 {
195 /* "class name" */
196 switch(c = gettoken(dummy, NULL, NULL))
197 {
198 case '{': /* "class name {" */
199 break;
200
201 case ':': /* "class name :" */
202 c = gettoken(dummy, NULL, NULL);
203 if (c == ':')
204 {
205 /* "class name :: */
206 /* this is a namespace-qualified declaration since cannot
207 have a definition like this. */
208 break;
209 }
210 else
211 {
212 /* "clas name : [one or more initializers]" */
213 while (c != '{')
214 {
215 c = gettoken(dummy, NULL, NULL);
216 }
217 }
218 break;
219
220 default:
221 /* if we fail to get "class name [:.*] {" */
222 ungettoken(c, dummy);
223 }
224 }
225 else if (c == '{') /* Unnamed class */
226 {
227 /* "class {" */
228 strcpy(classname, "unnamed");
229 }
230 else
231 {
232 /* "class BOGUS" -- perhaps this is C code using a variable "class" */
233 ungettoken(c, dummy);
234 }
235
236 if (isclass = (c == '{'))
237 {
238 stats_t *class = stats_push(classname, STATS_CLASS);
239
240 while ((c = gettoken(dummy, NULL, NULL)) != '}')
241 {
242 if (c == EOF)
243 {
244 fileerror("unexpected EOF");
245 break;
246 }
247 else
248 ungettoken(c, dummy);
249
250 toplevelstatement(class);
251 }
252
253 stats_pop(class);
254 }
255
256 return isclass;
257 }
258
259 int
maybenamespace()260 maybenamespace()
261 /*
262 * We've just seen "namespace" at the top level in a file so
263 * we may be entering a definition of same (if we next find "token {").
264 * Return 1 if this is a
265 * namespace definition else restore the name and return 0.
266 */
267 {
268 char nsname[256], dummy[256];
269 int isns = 0;
270 int c;
271
272 if ((c = gettoken(nsname, NULL, NULL)) == T_IDENT)
273 {
274 /* "namespace name" */
275 switch(c = gettoken(dummy, NULL, NULL))
276 {
277 case '{': /* "namespace name {" */
278 break;
279
280 default:
281 /* if we fail to get "namespace name {" */
282 ungettoken(c, dummy);
283 }
284 }
285 else if (c == '{') /* Unnamed namespace */
286 {
287 /* "namespace {" */
288 strcpy(nsname, "anonymous_namespace");
289 }
290 else
291 {
292 /* "namespace BOGUS" -- is C code using a variable "namespace"? */
293 ungettoken(c, dummy);
294 }
295
296 if (isns = (c == '{'))
297 {
298 stats_t *ns = stats_push(nsname, STATS_NAMESPACE);
299
300 while ((c = gettoken(dummy, NULL, NULL)) != '}')
301 {
302 if (c == EOF)
303 {
304 fileerror("unexpected EOF");
305 break;
306 }
307 else
308 ungettoken(c, dummy);
309
310 toplevelstatement(ns);
311 }
312
313 stats_pop(ns);
314 }
315
316 return isns;
317 }
318
319
320 void
findsemicolon()321 findsemicolon()
322 {
323 int c;
324
325 while ((c = ncss_Getchar()) != EOF && c != ';')
326 {
327 switch (c)
328 {
329 case '(':
330 c = matchparen();
331 break;
332 case '{':
333 c = matchcurly();
334 break;
335 }
336 }
337
338 if (c == EOF)
339 {
340 Exit = 5;
341 fileerror("expected ';' got EOF");
342 }
343 }
344
345 int
getoverloadedop(char * buf)346 getoverloadedop(char *buf)
347 /*
348 * Having just read ident::operator, try to read the operator into buf.
349 * If the first non-WS character is a '(', the overloaded thing is a
350 * function call. Otherwise it's some type of real operator and we
351 * terminate normally on '('. If we read ; or { we probably should
352 * print a warning and bail out.
353 */
354 {
355 char *savebuf = buf;
356 char tmpbuf[256];
357 int c = gettoken(tmpbuf, NULL, NULL);
358
359 if (c == '(')
360 {
361 /* overloaded function call syntax */
362 *buf++ = c;
363 /* Match the paren */
364 while(c != ')')
365 {
366 if ((c = skipws()) == EOF)
367 break;
368
369 *buf++ = c;
370 }
371 }
372 else if (c == T_IDENT)
373 {
374 /* class::operator int() */
375 /* Overloaded typecast */
376 *buf++ = '_';
377 *buf = '\0';
378 strcat(buf, tmpbuf);
379 buf += strlen(buf);
380 *buf++ = '(';
381 *buf++ = ')';
382 *buf = '\0';
383 }
384 else if (c != EOF)
385 {
386 *buf++ = c;
387 while ((c = ncss_Getchar()) != EOF)
388 {
389 if (!ISSPACE(c))
390 {
391 if (c == '(' || c == ';')
392 {
393 ncss_Ungetc(c);
394 break;
395 }
396 else
397 *buf++ = c;
398 }
399 }
400 }
401
402 *buf = '\0';
403
404 return c;
405 }
406
407 void
possiblefn(stats_t * stats,const char * name,int line1,int defline,int nLine1)408 possiblefn(stats_t *stats, const char *name, int line1, int defline, int nLine1)
409 /*
410 * We've just read an open parenthesis. If there's a legal identifier
411 * in name we may be within a function definition.
412 */
413 {
414 char dummy[257];
415 int nstatements = 0; /* in case there's code prior to the { */
416 int c;
417
418 if (strlen(name) == 0)
419 {
420 /* no function name - must not be a function - return */
421 findsemicolon();
422 }
423 else
424 {
425 if ((c = matchparen()) != EOF)
426 {
427 c = gettoken(dummy, NULL, NULL);
428
429 switch (c)
430 {
431 case T_CONST:
432 if (strchr(name, ':') != NULL || stats->type == STATS_CLASS)
433 {
434 /* foo::foo() const ^ [;] { */
435 /* This'll either be a ; for a declaration or a { */
436 c = gettoken(dummy, NULL, NULL);
437 break;
438 }
439 /* foo() const ^ char *a; { */
440 /*** FALL THROUGH ***/
441
442 case T_IDENT:
443 case T_STRUCT:
444 case T_UNION:
445 /* if (strchr(name, ':') == NULL && stats->type != STATS_CLASS) */
446 {
447 /* K&R function, T_IDENT is part of first parm defn */
448 /* Read up to that first '{' */
449 /* function foo(a, b, c) int a; */
450 /* ^ */
451 while ((c = ncss_Getchar()) != EOF && c != '{')
452 {
453 }
454 }
455 break;
456 case '{':
457 /* open { of the function */
458 break;
459 case '(':
460 /* wierd possibility in C++ - what we thought was the */
461 /* parameter list was really part of an overloaded */
462 /* operator overloading of an odd typecast or something. */
463 /* The function name will be wrong but who cares :-> */
464
465 c = matchparen();
466 if (c != EOF)
467 c = gettoken(dummy, NULL, NULL);
468 break;
469 case ':':
470 /* Another C++-ism: main(args): ident(args), ident(args) */
471 c = prefunction(&nstatements);
472 break;
473 }
474
475 if (c == '{')
476 {
477 /* This really is a function */
478 stats_t *fn = stats_push(name, STATS_FUNCTION);
479 fn->nfunctions = 1;
480 fn->firstline = line1;
481 fn->defline = defline;
482 fn->nsemicolons = nstatements;
483
484 c = countfunction(fn);
485 fn->nLines = ncss_Line - nLine1;
486 stats->nLines -= fn->nLines;
487 if (!Totalsonly && !Filesonly)
488 printstats(fn);
489 stats_pop(fn);
490 }
491 }
492 }
493 }
494
495 int
prefunction(int * nstatements)496 prefunction(int *nstatements)
497 /*
498 * Handle C++ ident(args) : ident(args), ident(args). Count each
499 * ident(args) after function declaration as a statement.
500 */
501 {
502 int c;
503
504 (*nstatements)++;
505
506 while((c = ncss_Getchar()) != EOF)
507 {
508 switch(c)
509 {
510 case '(':
511 c = matchparen();
512 break;
513 case ',':
514 (*nstatements)++;
515 break;
516 case '{':
517 return c;
518 }
519 }
520 Exit = 9;
521 fileerror("expected { got EOF");
522
523 return c;
524 }
525
526 int
countfunction(stats_t * fn)527 countfunction(stats_t *fn)
528 {
529 int nest = 1;
530 int c;
531 char id[257];
532
533 while (nest > 0 && (c = gettoken2(id, NULL, NULL)) != EOF)
534 {
535 switch (c)
536 {
537 case ';':
538 fn->nsemicolons++;
539 break;
540
541 case '{':
542 nest++;
543 break;
544
545 case '}':
546 nest--;
547 break;
548
549 case '?':
550 fn->nq++;
551 break;
552
553 case T_LOGICAL:
554 fn->nor++;
555 break;
556
557 case '^':
558 fn->nor++; /* This is XOR but I'm lazy */
559 break;
560
561 case T_CLASS:
562 case T_UNION:
563 case T_STRUCT:
564 if (maybeclass())
565 fn->nsemicolons--;
566 break;
567
568 default:
569 countword(fn, c);
570 break;
571 }
572 }
573
574 fn->lastline = Line;
575
576 if (nest > 0 /* && c == EOF */ )
577 {
578 Exit = 6;
579 fileerror("not enough }'s");
580 }
581
582 return c;
583 }
584
585 void
countword(stats_t * fn,int id)586 countword(stats_t * fn, int id)
587 {
588 switch (id)
589 {
590 case T_IF:
591 fn->nif++;
592 break;
593 case T_FOR:
594 fn->nfor++;
595 break;
596 case T_WHILE:
597 fn->nwhile++;
598 break;
599 case T_SWITCH:
600 fn->nswitch++;
601 break;
602 case T_CASE:
603 fn->ncase++;
604 break;
605 }
606 }
607