1 // COVTOOL -- test coverage analysis tool. 2 // Copyright (C) 2002, Lowell Boggs Jr. 3 // mailto:lowell.boggs@attbi.com 4 5 // This file contains free software. You can redistribute it 6 // and/or modify it under the terms of the GNU General Public License 7 // as published by the Free Software Foundation; either version 2, or 8 // (at your option) any later version. 9 10 // This source code is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU General Public License for more details. 14 15 // Write to the Free Software Foundation, 59 Temple Place - Suite 330, 16 // Boston, MA 02111-1307, USA for a copy of the GNU General Public License. 17 // 18 19 20 // 21 // This is the instrumentor for the covtool.exe program. It parses a 22 // C++ source file that has already been through the C preprocessor and 23 // adds additional function calls and data declarations such that when your 24 // program runs it will make function calls that will create a database 25 // indicating which lines of code in your program were executed and which 26 // were not. See covtoolhelper.c for implementations of the datastructures 27 // and functions injected by this program. 28 // 29 // The parser only recognizes the big picture parts of the C++ language. 30 // It is a recursive descent parser that knows about namespaces, classes, 31 // structs, unions, enums, variable declarations, functions, statements, etc. 32 // It does not know about expressions -- a statement is either one of 33 // the structured statements (if, while, do, for, switch, case) or it is 34 // a sequence of tokens ending in ;. 35 // 36 // The parser injects a call to CvT_record_line() before the beginning of 37 // every executable statement -- and one extra at the beginning of every 38 // function. See function 'instrument_line()' below. 39 // 40 // Also, you can instruct the parser to skip all files beginning with a 41 // given prefix. Normally the prefix will be just '/' because you will 42 // probably want to ignore the C++ source header files -- since they will 43 // detract from your code test coverage numbers and you don't want to have to 44 // write full tests for them. See the -skip option. 45 // 46 #include <fstream> 47 #include <covstream.h> 48 #include <ctype.h> 49 #include <map> 50 #include <set> 51 #include <string> 52 #include <list> 53 #include <vector> 54 #include <stdlib.h> 55 56 // the following block must not be separated, it defines the 57 // version string 58 char const* 59 #include "covtool_version" 60 ; 61 // do not separate this from the #include of covtool_version! 62 63 typedef CovStream::iterator iterator; 64 typedef CovStream::token token; 65 66 int line_number=1; 67 int depth=0; 68 69 char const *parse_state[1000]; 70 71 72 #ifdef DEBUGGING 73 static char buffer[256]; 74 static char *current_token = buffer; 75 #define DEBUG_FLUSH() cout.flush() 76 #else 77 #define DEBUG_FLUSH() 78 #endif 79 80 token get_token(iterator start, iterator end); 81 82 // the names of runtime objects injected during instrumentation 83 84 char const *rt_file_name_table = "__CvT__file_name____"; 85 char const *rt_record_line = "__CvT__record_line____"; 86 char const *rt_logged_lines_table = "__CvT__logged_lines___"; 87 char const *rt_instrumented_lines = "__CvT__instrumented_lines___"; 88 89 char const *rt_module_info = 90 "struct __CvT__module_info____ \n" 91 "{ \n" 92 " char const **file; \n" 93 " int files; \n" 94 " void const **str; // string version of file names above \n" 95 " int * line; // pairs of ints stored here \n" 96 " // the first is the index into file \n" 97 " // the second is the line number in that file \n" 98 " int lines; // count of PAIRS not ints at line \n" 99 " \n" 100 " int struct_size; // validates this struct \n" 101 " int * inst; // counts of instrumented lines per file \n" 102 " \n" 103 " void construct(); \n" 104 " \n" 105 " __CvT__module_info____(char const **f, int fs, \n" 106 " int *l, int ls, \n" 107 " int *i \n" 108 " ) \n" 109 " : file(f), files(fs), str(0), line(l), lines(ls), \n" 110 " struct_size(sizeof(__CvT__module_info____)), inst(i) \n" 111 " { \n" 112 " construct(); \n" 113 " } \n" 114 " \n" 115 " ~__CvT__module_info____(); \n" 116 " \n" 117 "};\n"; 118 119 120 // The instrument time variables keeping track of files, lines, etc 121 122 typedef map<string,int> Files_t; 123 typedef Files_t::iterator Files_Iterator_t; 124 typedef list<Files_t::iterator> Ordered_Files_t; 125 typedef set< pair<int,int> > Logged_Lines_t; 126 127 bool instrumenting=false; 128 bool skipping=false; 129 string skip_prefix; // one for the moment 130 list<string> skip_prefixes; 131 Files_t files; 132 Ordered_Files_t files_in_order; 133 token current_file_name; 134 string current_file_string; 135 int current_file_number; 136 Logged_Lines_t logged_lines; 137 138 bool is_skipped(token filename) 139 +{ 140 + list<string>::iterator il; 141 142 + for(il = skip_prefixes.begin(); il != skip_prefixes.end(); ++il) 143 { 144 + string const &skipped_path = *il; 145 146 + iterator ifl = filename.first; 147 + string::const_iterator ipn = skipped_path.begin(); 148 149 + while(*ifl == *ipn && 150 ifl != filename.second && 151 ipn != skipped_path.end() 152 ) 153 { 154 + ++ifl; 155 + ++ipn; 156 } 157 158 + if(ipn == skipped_path.end()) 159 { 160 + return true; 161 } 162 } 163 164 + return false; 165 166 } 167 168 169 void record_current_file_name() // records 'current_file_name' 170 +{ 171 172 + iterator i = current_file_name.first; 173 174 + current_file_string.resize(0); 175 176 + while(i != current_file_name.second) 177 { 178 + current_file_string += *i++; 179 } 180 181 // cerr << "filename string is '" << current_file_string << endl; 182 183 + Files_Iterator_t tmp = files.find(current_file_string); 184 185 + if(tmp == files.end()) 186 { 187 // file not already added 188 189 //cerr << "it is new!" << endl; 190 191 + current_file_number = files.size(); 192 193 + map<string,int>::value_type v(current_file_string, current_file_number); 194 195 + Files_Iterator_t tmp = files.insert(v).first; 196 197 + files_in_order.push_back(tmp); 198 199 } 200 else 201 { 202 // cerr << "it is old!" << endl; 203 + current_file_number = tmp->second; 204 } 205 206 207 } 208 209 210 void instrument_beginning() 211 +{ 212 + if(!instrumenting) 213 + return; 214 215 + cout << "# 1 \"Instrumentation code beginning\"" << endl; 216 217 + cout << rt_module_info << endl; 218 219 + cout << "static __CvT__module_info____* __CvT_miT;" << endl; 220 221 + cout << "extern void " 222 << rt_record_line 223 << "(__CvT__module_info____ const*, int,int);" 224 << endl; 225 226 + cout.flush(); 227 } 228 229 void instrument_ending() 230 +{ 231 + if(!instrumenting) 232 + return; 233 234 + cout << "# 1 \"Instrumentation code ending\"" << endl; 235 236 + cout << "static char const *" << rt_file_name_table << "[]={" << endl; 237 238 + int instrumented_files = files_in_order.size(); 239 240 + if(instrumented_files == 0) 241 - instrumented_files=1; 242 243 + vector<int> instrumented_lines(instrumented_files,0); 244 245 + Ordered_Files_t::iterator first, last; 246 247 + for(first = files_in_order.begin(), 248 last = files_in_order.end(); 249 first != last; 250 ++first 251 ) 252 { 253 + Files_Iterator_t i = *first; 254 255 + cout << CovStream::fullname(i->first) << "," << endl; 256 } 257 258 + cout << "0};" << endl; 259 260 + Logged_Lines_t::iterator ll_first, ll_last; 261 262 + ll_first = logged_lines.begin(); 263 + ll_last = logged_lines.end(); 264 265 + cout << "static int " << rt_logged_lines_table << "[] = {" << endl; 266 267 + while(ll_first != ll_last) 268 { 269 + Logged_Lines_t::value_type const &r = *ll_first++; 270 271 + ++instrumented_lines[r.first]; // for each ref, increment the 272 // count of instrumented lines 273 // associated with the corresponding 274 // file 275 276 + cout << r.first << "," << r.second << "," << endl; 277 278 } 279 280 + cout << "0};" << endl; 281 282 + cout << "static int " << rt_instrumented_lines << "[]={" << endl; 283 { 284 + vector<int>::iterator i; 285 286 + for(i=instrumented_lines.begin(); 287 i != instrumented_lines.end(); 288 ++i 289 ) 290 { 291 + cout << *i << "," << endl; 292 } 293 } 294 + cout << "0};" << endl; 295 296 + cout << "static __CvT__module_info____" 297 << endl 298 << "__CvT_miTBODY(" 299 << endl 300 << " " 301 << rt_file_name_table 302 << "," 303 << endl 304 << " sizeof(" 305 << rt_file_name_table 306 << ")/sizeof(char*) -1," 307 << endl << " " 308 << rt_logged_lines_table 309 << "," 310 << endl 311 << " (sizeof(" 312 << rt_logged_lines_table 313 << ")/sizeof(int) -1) / 2" 314 << endl 315 << " ," << rt_instrumented_lines 316 << ");" 317 << endl; 318 319 + cout << 320 "static __CvT__module_info____* __CvT_miTINIT = __CvT_miT = &__CvT_miTBODY;" 321 << endl; 322 323 324 + cerr.flush(); // not sure why this needed but without these 325 + cout.flush(); // too lines, icov.exe crashes in static destruction. 326 // Could be a bug in my code that will get fixed 327 // later, or could be a subtle issues in the 328 // destruction of objects in the covtoolhelper.c 329 330 } 331 332 void instrument_line() 333 +{ 334 + if(!instrumenting) 335 + return; 336 337 + if(skipping) 338 - return; 339 340 + pair<int,int> line_ref(current_file_number, line_number); 341 342 + Logged_Lines_t::iterator i = logged_lines.find(line_ref); 343 344 + if(i == logged_lines.end()) 345 { 346 + logged_lines.insert(line_ref); 347 348 + cout << rt_record_line 349 << "(__CvT_miT," 350 << current_file_number 351 << "," 352 << line_number 353 << ");" ; 354 } 355 356 } 357 358 token process_declaration(token t, iterator end); 359 360 int main(int argc, char **argv) 361 -{ 362 #ifdef COVTOOL_INST 363 - extern void CvT_StartRecording____(); 364 - CvT_StartRecording____(); 365 366 #endif 367 368 + --argc; 369 + ++argv; 370 371 + string dash_instrument("-instrument"); 372 + string dash_skip("-skip"); 373 + string dash_v("-v"); 374 375 + while(argc) 376 { 377 + if(dash_v == *argv) 378 { 379 - cout << covtool_version << endl; 380 - exit(1); 381 } 382 else 383 + if(dash_instrument == *argv) 384 { 385 + instrumenting = true; 386 } 387 else 388 + if(dash_skip == *argv) 389 { 390 + --argc; 391 + ++argv; 392 393 + skip_prefixes.push_back(string("\"") + *argv); 394 395 } 396 else 397 { 398 - cerr << "covtool: invalid command line parameter '" << *argv << endl; 399 - exit(1); 400 } 401 402 + if(argc) 403 { 404 + --argc; 405 + ++argv; 406 } 407 } 408 409 + instrument_beginning(); 410 411 + CovStream s(stdin); 412 413 + iterator end = s.end(); 414 415 + parse_state[0] = "file"; 416 417 + token t = get_token(s.begin(), s.end()); 418 419 + do 420 { 421 + t = process_declaration(t, end); 422 } 423 while(t.first != end); 424 425 426 + cout << endl; 427 428 + instrument_ending(); 429 430 431 } 432 433 void output_token(iterator start, iterator end); 434 435 token get_token(iterator start, iterator end) 436 +{ 437 // 438 // parse tokens and handle default behaviors thereof. 439 // All parsing algorithms, except main(), assume that 440 // the first token to be parsed has already been gathered 441 // and is presented as the first thing to look at. It will 442 // not have been copied to stdout yet though. 443 // 444 445 + token t(start,start); 446 447 + static int first_time=1; 448 449 + if(first_time) 450 { 451 + first_time = 0; 452 453 + if(!instrumenting) 454 { 455 + cout << "/*" 456 << parse_state[depth] 457 << "\t" 458 << 1 459 << "\t:" 460 << depth 461 << "\t*/"; 462 463 DEBUG_FLUSH(); 464 } 465 } 466 467 + for(;;) 468 { 469 470 + t = CovStream::parse_token(t.second,end); 471 472 473 #ifdef DEBUGGING 474 475 // so I can the stupid token in the debugger! 476 477 478 { 479 char *scan = buffer; 480 iterator input = t.first; 481 482 while(input != t.second) 483 *scan++ = *input++; 484 485 *scan = 0; 486 487 } 488 489 490 #endif 491 492 493 + if(t.first == end) 494 + break; 495 496 + if(*t.first == '\n') 497 { 498 + ++line_number; 499 500 + if(!instrumenting) 501 { 502 + cout << endl 503 << "/*" 504 << parse_state[depth] 505 << "\t" 506 << line_number 507 << "\t:" 508 << depth 509 << "\t*/"; 510 511 DEBUG_FLUSH(); 512 } 513 else 514 + cout << endl; 515 } 516 else 517 + if(*t.first == '#') 518 { 519 + output_token(t.first, t.second); // the # 520 521 + t = CovStream::parse_token(t.second,end); // the line number 522 523 + if(t.first == end) 524 - break; // wierd stuff at end of file 525 526 527 528 + output_token(t.first, t.second); 529 530 + if( isdigit(*t.first) ) 531 { 532 // this is a line number pragma 533 534 + char buffer[256]; 535 536 + iterator scan = t.first; 537 538 + char *cp = buffer; 539 540 + while(scan != t.second) 541 + *cp++ = *scan++; 542 543 + *cp=0; 544 545 + sscanf(buffer, "%d", &line_number); 546 547 + t = CovStream::parse_token(t.second, end); // the filename 548 549 + if(t.first != end && *t.first != '\n') 550 + output_token(t.first, t.second); 551 552 + current_file_name = t; 553 554 + record_current_file_name(); 555 556 + skipping = is_skipped(t); 557 558 559 + while(t.first != end && *t.first != '\n') 560 { 561 + t = CovStream::parse_token(t.second, end); 562 563 + if(t.first != end && *t.first != '\n') 564 + output_token(t.first, t.second); 565 } 566 567 + if(!instrumenting) 568 { 569 + cout << endl 570 << "/*" 571 << parse_state[depth] 572 << "\t" 573 << line_number 574 << "\t:" 575 << depth 576 << "\t*/"; 577 DEBUG_FLUSH(); 578 } 579 else 580 + cout << endl; 581 582 } 583 else 584 { 585 // #pragma or other such nonesene 586 587 + do 588 { 589 + t = get_token(t.second, end); 590 + output_token(t.first, t.second); 591 } 592 while(t.first != end && *t.first != ';'); 593 594 } 595 596 } 597 else 598 { 599 + break; 600 } 601 602 } 603 604 605 + return t; 606 607 } 608 609 void output_token(iterator start, iterator end) 610 +{ 611 + while(start != end) cout << *start++; 612 + cout << " "; 613 614 DEBUG_FLUSH(); 615 616 } 617 618 bool compare_token(iterator first, iterator last, char const *s) 619 +{ 620 + while(first != last && *s && *first == *s) 621 { 622 + ++first; 623 + ++s; 624 } 625 626 + if(first == last && *s == 0) 627 + return true; 628 629 + return false; 630 631 } 632 633 token process_class (token start, iterator end, char const *scope="class"); 634 token process_enum (token start, iterator end); 635 token process_template (token start, iterator end); 636 token process_typedef (token start, iterator end); 637 token process_function (token start, iterator end); 638 639 token process_parenthetical(token t, iterator end); 640 token process_statement (token t, iterator end); 641 642 token process_declaration(token t, iterator end) 643 +{ 644 // 645 // process C++ declarations by copying them to stdout 646 // and add instrumentation as needed. 647 648 + if(t.first == end || *t.first == ';' || *t.first == '}') 649 + output_token(t.first,t.second); 650 else 651 { 652 653 + if(compare_token(t.first, t.second, "extern")) 654 { 655 + token save = t; 656 657 + t = get_token(t.second, end); 658 659 + if(t.first != end && *t.first == '"') 660 { 661 + output_token(save.first, save.second); 662 + output_token(t.first, t.second); 663 664 + t = get_token(t.second, end); 665 666 + if(t.first != end) 667 { 668 + if(*t.first == '{') 669 { 670 + ++depth; parse_state[depth]="extrn"; 671 672 + output_token(t.first, t.second); 673 674 + t = get_token(t.second, end); 675 + do 676 { 677 + t = process_declaration(t, end); 678 } 679 while(t.first != end && *t.first != '}'); 680 681 + output_token(t.first, t.second); 682 683 + --depth; 684 685 + t = get_token(t.second, end); 686 687 + return t; 688 689 690 } 691 else 692 { 693 + return process_declaration(t,end); 694 } 695 } 696 } 697 else 698 { 699 + output_token(save.first, save.second); 700 + return process_declaration(t,end); 701 } 702 703 704 } 705 else 706 + if(compare_token(t.first, t.second, "struct") || 707 compare_token(t.first, t.second, "class") || 708 compare_token(t.first, t.second, "union") || 709 compare_token(t.first, t.second, "namespace") 710 ) 711 { 712 + char const *id="class"; 713 714 + if(*t.first == 's') 715 + id = "strct"; 716 else 717 + if(*t.first == 'u') 718 + id = "union"; 719 else 720 + if(*t.first == 'n') 721 + id = "namsp"; 722 723 + return process_class(t, end,id); 724 } 725 else 726 + if(compare_token(t.first, t.second, "enum") ) 727 { 728 + return process_enum(t, end); 729 } 730 else 731 + if( 732 compare_token(t.first, t.second, "public") || 733 compare_token(t.first, t.second, "private") || 734 compare_token(t.first, t.second, "protected") 735 ) 736 { 737 // eat the class section separators 738 739 + output_token(t.first, t.second); 740 + t = get_token(t.second, end); 741 + output_token(t.first, t.second); 742 + return get_token(t.second, end); 743 } 744 else 745 + if(compare_token(t.first, t.second, "template") ) 746 { 747 + return process_template(t, end); 748 } 749 else 750 + if(compare_token(t.first, t.second, "namespace") ) 751 { 752 - return process_class(t, end, "namsp"); 753 } 754 else 755 + if(compare_token(t.first, t.second, "catch") ) 756 { 757 + output_token(t.first, t.second); 758 + t = get_token(t.second, end); 759 760 + t = process_parenthetical(t, end); 761 762 + return process_statement(t,end); 763 764 } 765 else 766 + if(compare_token(t.first, t.second, "typedef") || 767 compare_token(t.first, t.second, "using") 768 ) 769 { 770 + return process_typedef(t, end); 771 } 772 else 773 + if(isalnum(*t.first) || *t.first == '_') 774 { 775 + return process_function(t,end); 776 } 777 else 778 { 779 + output_token(t.first,t.second); 780 } 781 } 782 783 + return get_token(t.second, end); 784 } 785 786 token process_class(token t, iterator end, char const *scope) 787 +{ 788 + output_token(t.first, t.second); // class, struct, union, etc 789 790 // scan until the '{' or the ';' 791 792 + while(t.first != end) 793 { 794 + t = get_token(t.second,end); 795 796 + if(t.first == end || *t.first == ';' || *t.first == '{') 797 + break; 798 799 + output_token(t.first, t.second); 800 } 801 802 + if(t.first != end && *t.first != ';') 803 { 804 // process class body. Output the { and get the next token 805 806 + output_token(t.first, t.second); 807 + ++depth; parse_state[depth] = scope; // usually class but maybe namespace 808 809 + t = get_token(t.second,end); 810 811 + while(t.first != end && *t.first != '}') 812 { 813 814 + t = process_declaration(t, end); // prints the trailing '}' if any 815 816 } 817 818 + output_token(t.first, t.second); 819 + --depth; 820 + t = get_token(t.second, end); 821 822 823 } 824 825 826 + return t; 827 828 } 829 830 831 token process_enum(token t, iterator end) 832 +{ 833 + output_token(t.first, t.second); 834 835 + while(t.first != end) 836 { 837 + t = get_token(t.second,end); 838 839 + output_token(t.first, t.second); 840 841 + if(t.first == end) 842 - break; 843 844 + if(*t.first == ';' ) 845 - return get_token(t.second, end); 846 847 + if(*t.first == '{' ) 848 + break; 849 850 } 851 852 + if(t.first != end) 853 { 854 + ++depth; parse_state[depth]="enum"; 855 856 + while(t.first != end) 857 { 858 + t = get_token(t.second,end); 859 860 + output_token(t.first, t.second); 861 862 + if(t.first == end || *t.first == '}') 863 { 864 + t = get_token(t.second, end); 865 + break; 866 } 867 } 868 + --depth; 869 } 870 871 872 + return t; 873 874 } 875 876 877 token process_template(token t, iterator end) 878 +{ 879 + output_token(t.first, t.second); 880 881 + int angledepth = 0; 882 883 + while(t.first != end) 884 { 885 + t = get_token(t.second,end); 886 887 + output_token(t.first, t.second); 888 889 + if(t.first == end) 890 - break; 891 892 + if(compare_token(t.first, t.second, "<>")) 893 + return process_declaration(get_token(t.second, end), end); 894 895 + if(*t.first == '<') 896 + ++angledepth; 897 else 898 + if(*t.first == '>') 899 { 900 + if(angledepth == 1) 901 { 902 + token t = process_declaration(get_token(t.second, end), end); 903 904 + return t; 905 } 906 + --angledepth; 907 } 908 909 } 910 911 - return token(end,end); 912 913 } 914 915 token process_till_semicolon(token t, iterator end) 916 // 917 // consume tokens until a semicolon is encountered -- return the 918 // token after the semicolon. Do not walk past '}' -- I forgeet 919 // why -- but handle nested {} pairs. 920 // 921 +{ 922 + int braces=0; 923 924 + do 925 { 926 + t = get_token(t.second, end); 927 928 + if(t.first == end) 929 - return t; 930 931 + if(*t.first == '{') 932 + ++braces; 933 else 934 + if(*t.first == '}') 935 + if(braces == 0) 936 - return t; 937 else 938 + --braces; 939 940 + output_token(t.first, t.second); 941 } 942 while(t.first != end && *t.first != ';'); 943 944 + return get_token(t.second, end); 945 946 } 947 948 token process_typedef(token t, iterator end) 949 +{ 950 // process typedef, namespace, and using statements at file scope 951 952 + output_token(t.first, t.second); 953 954 + t = get_token(t.second, end); 955 956 + if( compare_token(t.first, t.second, "struct") || 957 compare_token(t.first, t.second, "class") || 958 compare_token(t.first, t.second, "union") 959 ) 960 { 961 + return process_class(t,end); 962 } 963 964 + output_token(t.first, t.second); 965 966 + return process_till_semicolon(t, end); 967 968 } 969 970 971 972 token process_function(token t, iterator end) 973 +{ 974 // process function and variable declarations 975 976 // copy the type info out and scarf the variable name 977 // up to the ()'s. 978 979 + output_token(t.first, t.second); 980 981 + do 982 { 983 // handle various wierd syntax cases including a pointer to a 984 // function declaration type (*name)(parms) 985 // so we have to handle multiple () groups before the 'const' or '=' 986 // or { that defines what this darn thing is. 987 988 + t = get_token(t.second, end); 989 990 + if(t.first != end && compare_token(t.first, t.second, "operator") ) 991 { 992 // oops, operators can be '=' and that will screw up the 993 // logic of detecting that this is a variable assignment not a 994 // function body. 995 996 + output_token(t.first, t.second); 997 998 + t = get_token(t.second, end); 999 1000 // we now have the operator being defined but we don't 1001 // want it to be processed either 1002 1003 + output_token(t.first, t.second); 1004 1005 + t = get_token(t.second, end); // get the paren after the operator 1006 1007 1008 } 1009 1010 1011 + if(t.first != end && *t.first == '(') 1012 { 1013 + t = process_parenthetical(t, end); 1014 1015 + if(t.first != end && 1016 compare_token(t.first, t.second, "const") 1017 ) 1018 { 1019 + output_token(t.first, t.second); 1020 + t = get_token(t.second, end); 1021 } 1022 1023 + if(t.first != end && 1024 compare_token(t.first, t.second, "try") 1025 ) 1026 { 1027 + output_token(t.first, t.second); 1028 + t = get_token(t.second, end); 1029 } 1030 1031 1032 } 1033 1034 + output_token(t.first, t.second); 1035 } 1036 while(t.first != end && 1037 *t.first != ';' && 1038 *t.first != '=' && 1039 *t.first != '{' 1040 ); 1041 1042 + if(t.first == end || *t.first == ';') 1043 + return get_token(t.second, end); 1044 1045 + if(*t.first == '=' ) 1046 + return process_till_semicolon(t, end); 1047 1048 // must be a '{' 1049 1050 1051 + ++depth; parse_state[depth] = "func"; 1052 1053 + instrument_line(); 1054 1055 1056 + t = get_token(t.second, end); 1057 1058 + while(t.first != end && *t.first != '}') 1059 { 1060 + t = process_statement(t, end); 1061 } 1062 1063 + output_token(t.first, t.second); 1064 1065 + --depth; 1066 1067 + return get_token(t.second, end); 1068 1069 } 1070 1071 token process_if(token t, iterator end); 1072 token process_while(token t, iterator end); 1073 token process_do(token t, iterator end); 1074 token process_for(token t, iterator end); 1075 token process_switch(token t, iterator end); 1076 token process_try(token t, iterator end); 1077 1078 token process_statement(token t, iterator end) 1079 +{ 1080 + if(t.first == end) 1081 { 1082 - return t; 1083 } 1084 1085 + if(*t.first == ';') 1086 { 1087 + output_token(t.first, t.second); 1088 + return get_token(t.second, end); 1089 } 1090 else 1091 + if(*t.first == '{') 1092 { 1093 + ++depth; parse_state[depth] = "block"; 1094 1095 + output_token(t.first, t.second); 1096 + t = get_token(t.second,end); 1097 1098 + while( t.first != end && *t.first != '}' ) 1099 { 1100 + t = process_statement(t,end); 1101 } 1102 1103 + output_token(t.first, t.second); 1104 1105 + --depth; 1106 1107 + return get_token(t.second, end); 1108 1109 } 1110 else 1111 + if(compare_token(t.first, t.second, "if") ) 1112 { 1113 + instrument_line(); 1114 + return process_if(t, end); 1115 } 1116 else 1117 + if(compare_token(t.first, t.second, "case") ) 1118 { 1119 + output_token(t.first, t.second); // case 1120 1121 + do 1122 { 1123 + t = get_token(t.second, end); 1124 1125 + output_token(t.first, t.second); // label 1126 } 1127 while(t.first != end && !compare_token(t.first,t.second,":")); 1128 1129 + return process_statement(get_token(t.second, end), end); 1130 } 1131 else 1132 + if(compare_token(t.first, t.second, "default") ) 1133 { 1134 + output_token(t.first, t.second); // default 1135 1136 + t = get_token(t.second, end); 1137 1138 + output_token(t.first, t.second); // : 1139 1140 + return process_statement(get_token(t.second, end), end); 1141 } 1142 else 1143 + if(compare_token(t.first, t.second, "while") ) 1144 { 1145 + instrument_line(); 1146 + return process_while(t, end); 1147 } 1148 else 1149 + if(compare_token(t.first, t.second, "do") ) 1150 { 1151 + instrument_line(); 1152 + return process_do(t, end); 1153 } 1154 else 1155 + if(compare_token(t.first, t.second, "for") ) 1156 { 1157 + instrument_line(); 1158 + return process_for(t, end); 1159 } 1160 else 1161 + if(compare_token(t.first, t.second, "switch") ) 1162 { 1163 + instrument_line(); 1164 + return process_switch(t, end); 1165 } 1166 else 1167 + if(compare_token(t.first, t.second, "enum") ) 1168 { 1169 - instrument_line(); 1170 - return process_enum(t, end); 1171 } 1172 else 1173 + if(compare_token(t.first, t.second, "try") ) 1174 { 1175 + instrument_line(); 1176 + return process_try(t, end); 1177 } 1178 else 1179 + if(compare_token(t.first, t.second, "typedef") || 1180 compare_token(t.first, t.second, "using") || 1181 compare_token(t.first, t.second, "namespace") 1182 ) 1183 { 1184 + return process_typedef(t, end); 1185 } 1186 else 1187 + if(compare_token(t.first, t.second, "class") || 1188 compare_token(t.first, t.second, "struct")|| 1189 compare_token(t.first, t.second, "union") 1190 ) 1191 { 1192 - instrument_line(); 1193 - return process_declaration(t, end); 1194 } 1195 else 1196 { 1197 + instrument_line(); 1198 1199 1200 #if 0 1201 t = process_typedef(t, end); 1202 #else 1203 + output_token(t.first, t.second); 1204 + t = process_till_semicolon(t,end); 1205 1206 #endif 1207 1208 + return t; 1209 } 1210 } 1211 1212 1213 token process_parenthetical(token t, iterator end) 1214 +{ 1215 // scoop up a parenthesized expression and return the first 1216 // token after the trailing ). 1217 1218 + output_token(t.first, t.second); 1219 1220 + t = get_token(t.second, end); 1221 1222 + int pdepth=1; 1223 1224 + for(;;) 1225 { 1226 + if(t.first == end) 1227 - break; 1228 1229 1230 + if(*t.first == ')') 1231 { 1232 + --pdepth; 1233 1234 + if(pdepth == 0) 1235 + break; 1236 1237 } 1238 else 1239 + if(*t.first == '(') 1240 { 1241 + ++pdepth; 1242 } 1243 1244 + output_token(t.first, t.second); 1245 + t = get_token(t.second, end); 1246 1247 } 1248 1249 + output_token(t.first, t.second); 1250 + return get_token(t.second, end); 1251 1252 } 1253 1254 1255 token process_if(token t, iterator end) 1256 +{ 1257 //handle 'if' statements and the else clause 1258 1259 + output_token(t.first, t.second); 1260 1261 + t = process_parenthetical(get_token(t.second, end),end); 1262 1263 + if(instrumenting) cout << "{"; 1264 1265 + t = process_statement(t, end); 1266 1267 + if(instrumenting) cout << "}"; 1268 1269 + if(compare_token(t.first, t.second, "else")) 1270 { 1271 + output_token(t.first, t.second); 1272 1273 + if(instrumenting) cout << "{"; 1274 + t = process_statement(get_token(t.second, end), end); 1275 + if(instrumenting) cout << "}"; 1276 } 1277 1278 + return t; 1279 1280 } 1281 1282 token process_while(token t, iterator end) 1283 +{ 1284 //handle 'while' statements 1285 1286 + output_token(t.first, t.second); 1287 1288 + t = process_parenthetical(get_token(t.second, end),end); 1289 1290 + if(instrumenting) cout << "{"; 1291 + t = process_statement(t, end); 1292 + if(instrumenting) cout << "}"; 1293 1294 + return t; 1295 1296 } 1297 1298 token process_do(token t, iterator end) 1299 +{ 1300 //handle 'do' statements 1301 1302 + output_token(t.first, t.second); // print do, get first token of statement 1303 1304 + if(instrumenting) cout << "{"; 1305 + t = process_statement(get_token(t.second,end), end); 1306 + if(instrumenting) cout << "}"; 1307 1308 // t should be 'while' here 1309 1310 + output_token(t.first, t.second); 1311 1312 + t = process_parenthetical(get_token(t.second, end),end); 1313 + return t; 1314 1315 } 1316 1317 token process_for(token t, iterator end) 1318 +{ 1319 //handle for statements 1320 1321 + output_token(t.first, t.second); 1322 1323 + t = process_parenthetical( get_token(t.second, end), end); 1324 1325 + if(instrumenting) cout << "{"; 1326 1327 + t = process_statement(t, end); 1328 1329 + if(instrumenting) cout << "}"; 1330 1331 + return t; 1332 1333 1334 1335 } 1336 1337 token process_switch(token t, iterator end) 1338 +{ 1339 //handle switch statements 1340 1341 + output_token(t.first, t.second); 1342 1343 + t = process_parenthetical( get_token(t.second, end), end); 1344 1345 + return process_statement(t, end); 1346 1347 } 1348 1349 1350 token process_try(token t, iterator end) 1351 +{ 1352 + output_token(t.first, t.second); 1353 1354 + t = process_statement(get_token(t.second, end), end); 1355 1356 + while(t.first != end && compare_token(t.first, t.second, "catch")) 1357 { 1358 + output_token(t.first, t.second); // catch 1359 1360 + t = process_parenthetical(get_token(t.second, end), end); 1361 1362 + t = process_statement(t, end); 1363 1364 } 1365 1366 + return t; 1367 1368 } 1369 1370