1///////////////////////////////////////////////////////////////////////////// 2// Name: No names yet. 3// Purpose: Contrib. demo 4// Author: Aleksandras Gluchovas 5// Modified by: 6// Created: 22/09/98 7// RCS-ID: $Id: cjparser.cpp 35650 2005-09-23 12:56:45Z MR $ 8// Copyright: (c) Aleskandars Gluchovas 9// Licence: wxWindows licence 10///////////////////////////////////////////////////////////////////////////// 11 12// For compilers that support precompilation, includes "wx/wx.h". 13#include "wx/wxprec.h" 14 15#ifdef __BORLANDC__ 16#pragma hdrstop 17#endif 18 19#ifndef WX_PRECOMP 20#include "wx/wx.h" 21#endif 22 23#include "cjparser.h" 24 25#if defined( wxUSE_TEMPLATE_STL ) 26 27 #include <map> 28 29#else 30 31 #include "wxstlac.h" 32 33#endif 34 35 36/***** Implementation for class SJParser *****/ 37 38// statics used by inline'ed C helper-functions 39static char* _gSrcStart = 0; 40static char* _gSrcEnd = 0; 41static wxChar* _gLastSuppresedComment = 0; 42static int _gLineNo = 0; 43 44// FOR NOW:: comments queue is static 45#define MAX_CQ_ENTRIES 128 46static char* _gCommentsQueue[MAX_CQ_ENTRIES]; 47static int _gCQSize = 0; 48 49/***** keyword map related structures *****/ 50 51struct less_c_str 52{ 53 inline bool operator()( char* x, char* y) const 54 { return ( strcmp( x,y ) < 0 ); 55 } 56}; 57 58//WXSTL_MAP(CharPtrT,CharPtrT, LESS_THEN_FUNCTOR(CharPtrT)); 59 60#if defined( wxUSE_TEMPLATE_STL ) 61 62 typedef map< char*, char*, less_c_str > KeywordMapT; 63 64#else 65 66 typedef char* CharPtrT; 67 typedef WXSTL_MAP( CharPtrT, CharPtrT ,less_c_str) KeywordMapT; 68 69#endif 70 71static KeywordMapT __gMultiLangMap; 72static int __gMapReady = 0; 73 74static char* __gKeyWords[] = 75{ 76 "public", 77 "protected", 78 "private", 79 80 "class", 81 "struct", 82 "union", 83 "enum", 84 "interface", 85 86 "package", 87 "import", 88 89 "typedef", 90 "template", 91 "friend", 92 "const", 93 "volatile", 94 "mutable", 95 "virtual", 96 "inline", 97 "static", 98 "register", 99 100 "final", 101 "abstract", 102 "native", 103 104 "__stdcall", 105 "extern", 106 107 0 108}; 109 110static void check_keyword_map() 111{ 112 if ( !__gMapReady ) 113 { 114 __gMapReady = 1; 115 116 // "make sure" the address of the first member of non-polimorphic class 117 // coinsides with the address of the instance 118 119 char** keyword = __gKeyWords; 120 121 while ( (*keyword) != 0 ) 122 { 123 __gMultiLangMap.insert( 124 KeywordMapT::value_type( *keyword, *keyword ) 125 ); 126 127 ++keyword; 128 } 129 } 130} 131 132/***** helper functions *****/ 133 134static inline void skip_to_eol( char*& cur ) 135{ 136 while( *(cur) != 10 && *cur != 13 && cur < _gSrcEnd) ++cur; 137} 138 139static inline void skip_eol( char*& cur ) 140{ 141 if ( *cur == 13 ) 142 143 cur += 2; 144 else 145 cur += 1; 146 147 ++_gLineNo; 148} 149 150static inline bool skip_to_next_comment_in_the_line( char*& cur ) 151{ 152 do 153 { 154 while( cur < _gSrcEnd && 155 *cur != 10 && 156 *cur != 13 && 157 *cur != '/' 158 ) ++cur; 159 160 if ( cur == _gSrcEnd ) return false; 161 162 if ( *cur == '/' ) 163 { 164 if ( (*(cur+1) == '*') || 165 (*(cur+1) == '/') ) return true; 166 else 167 { 168 ++cur; 169 continue; 170 } 171 } 172 173 return false; 174 175 } while(1); 176} 177 178inline static void store_line_no( int& toVar ) 179{ 180 toVar = _gLineNo; 181} 182 183inline static void restore_line_no( int storedLineNo ) 184{ 185 _gLineNo = storedLineNo; 186} 187 188inline static int get_line_no() 189{ 190 return _gLineNo; 191} 192 193static void skip_to_prev_line( char*& cur ) 194{ 195 while( cur >= _gSrcStart && 196 *cur != 10 && 197 *cur != 13 198 ) --cur; 199 200 // NOTE:: '\n' is 13,10 for DOS 201 // '\n' is 10 for UNIX 202 203 // NOTE1: '\n' symbol is not used here, 204 // to provide possibility of loading 205 // file as binary 206 207 --cur; 208 if ( *cur == 10 ) 209 { 210 ++cur; 211 return; 212 } 213 214 if ( *cur == 13 ) --cur; 215 216 while( cur >= _gSrcStart && 217 *cur != 10 && 218 *cur != 13 219 ) --cur; 220 221 ++cur; // move to the first character in the line 222} 223 224static inline void skip_comments( char*& cur ) 225{ 226 ++cur; // skip '/' token 227 228 if ( *cur != '/' && *cur != '*' ) return; 229 230 // first, store position of the comment into the queue 231 // (which further will be attached to the next context 232 // found) 233 234 if ( cur-1 != _gLastSuppresedComment ) 235 { 236 if ( _gCQSize == MAX_CQ_ENTRIES ) 237 { 238 size_t i = MAX_CQ_ENTRIES-1; 239 240 while( i != 0 ) 241 { 242 _gCommentsQueue[i-1] = _gCommentsQueue[i]; 243 --i; 244 } 245 246 --_gCQSize ; 247 } 248 249 _gCommentsQueue[_gCQSize++] = cur-1; 250 } 251 252 // if signle-line comment, skip it now 253 if ( *cur == '/' ) 254 { 255 skip_to_eol( cur ); 256 skip_eol( cur ); 257 return; 258 } 259 260 size_t level = 1; 261 262 // check for multiline comment (handle nested multiline comments!) 263 264 int line_len = 0; 265 266 ++cur; 267 ++cur; 268 do 269 { 270 // TBD:: check eof cond. 271 272 // detect and remove vertical columns of '*''s 273 274 while ( *cur != '/' && cur < _gSrcEnd ) 275 { 276 switch (*cur) 277 { 278 case '*' : 279 { 280 if ( *(cur+1) != '/' ) 281 { 282 if ( line_len == 1 ) 283 284 *cur = ' '; 285 } 286 287 break; 288 } 289 290 case 13 : line_len = 0; break; 291 case 10 : { line_len = 0; ++_gLineNo; } break; 292 293 default : ++line_len; 294 } 295 296 ++cur; 297 } 298 299 if ( cur >= _gSrcEnd ) return; 300 301 ++cur; 302 303 if ( *(cur-2) == '*' ) 304 { 305 --level; 306 if ( level == 0 ) 307 break; 308 } 309 else 310 if ( *cur == '*' ) 311 { 312 ++cur; 313 ++cur; 314 315 ++level; 316 } 317 318 } while(1); 319} 320 321static inline void clear_commets_queue() 322{ 323 _gCQSize = 0; 324} 325 326static inline void skip_quoted_string( char*& cur ) 327{ 328 ++cur; // skip first quote '"' 329 330 // check if quote wasn't prefixed 331 if ( *(cur-2) == '\\' ) 332 return; 333 334 do 335 { 336 while ( *cur != '"' && cur < _gSrcEnd ) 337 { 338 if ( *cur == 10 ) ++_gLineNo; 339 ++cur; 340 } 341 342 if ( cur >= _gSrcEnd ) return; 343 344 ++cur; // skip the last quote 345 346 // check if it wasn't prefixed 347 348 if ( *(cur-2) != '\\' ) 349 break; 350 351 } while (1); 352} 353 354// skips subsequent white space and comments 355// (return false if the end of source code reached) 356 357static inline bool get_next_token( char*& cur ) 358{ 359 for( ; cur < _gSrcEnd; ++cur ) 360 { 361 switch( *(cur) ) 362 { 363 case ' ' : continue; 364 case '\t': continue; 365 case 13 : continue; 366 367 case 10 : { ++_gLineNo;continue; } 368 369 case '/' : skip_comments( cur ); 370 --cur; 371 continue; 372 373 default : break; 374 }; 375 376 break; 377 } 378 379 if ( cur >= _gSrcEnd ) 380 return false; 381 else 382 return true; 383} 384 385static inline void skip_preprocessor_dir( wxChar*& cur ) 386{ 387 do 388 { 389 skip_to_eol(cur); 390 391 if ( *(cur-1) != _T('\\') ) 392 break; 393 394 if ( cur < _gSrcEnd ) 395 skip_eol( cur ); 396 else 397 break; 398 399 } while(1); 400} 401 402static void skip_token( char*& cur ) 403{ 404 if ( *cur == '"' ) 405 { 406 skip_quoted_string( cur ); 407 return; 408 } 409 410 if ( *cur == ',' || 411 *cur == ';' || 412 *cur == ')' || 413 *cur == '(' 414 ) 415 { 416 ++cur; 417 return; 418 } 419 420 // special case of "!=", "<=", ... 2 character composite tokens 421 if ( *cur == '<' || 422 *cur == '>' || 423 *cur == '=' || 424 *cur == '!' 425 ) 426 { 427 cur++; 428 if ( *cur == '=' ) 429 cur++; 430 431 return; 432 } 433 434 ++cur; // leading character is always skipped 435 436 for( ; cur < _gSrcEnd ; ++cur ) 437 { 438 switch ( *cur ) 439 { 440 case ' ' : break; 441 case '\t': break; 442 case 13 : break; 443 case 10 : break; 444 case ',' : break; 445 case ';' : break; 446 case '<' : break; 447 case '>' : break; 448 449 // FIXME:: QUICK-HACK:: to treat scope resolution 450 // tokens are a part of the string - e.g. SomeSpace::SubName would 451 // become one token 452 453 case ':' : if ( *(cur+1) == ':' ) 454 { 455 ++cur; 456 continue; 457 } 458 459 break; 460 case '=' : break; 461 case '(' : break; 462 case ')' : break; 463 case '{' : break; 464 case '}' : break; 465 466 default : continue; 467 }; 468 break; 469 } 470} 471 472static inline size_t get_token_len( char* tok ) 473{ 474 char* start = tok; 475 476 skip_token( tok ); 477 478 return size_t( tok - start ); 479} 480 481// returns true, if given tokens are equel 482 483static inline bool cmp_tokens( char* tok1, char* tok2 ) 484{ 485 // NOTE:: the case one token includes 486 // other in it's entirely is not handled 487 488 size_t len = get_token_len( tok1 ); 489 490 // assuming that tokens are non-zero length 491 492 do 493 { 494 if ( *(tok1++) != *(tok2++) ) 495 return false; 496 497 --len; 498 499 } while ( --len ); 500 501 return true; 502} 503 504static inline bool cmp_tokens_fast( char* tok1, char* tok2, size_t len ) 505{ 506 do 507 { 508 if ( *(tok1++) != *(tok2++) ) 509 return false; 510 511 } while ( --len ); 512 513 return true; 514} 515 516static inline void skip_tempalate_statement( char*& cur ) 517{ 518 size_t level = 0; 519 520 // go one level deeper 521 while( *cur != '<' && cur < _gSrcEnd ) 522 { 523 if (*cur == 10 ) ++_gLineNo; 524 ++cur; 525 } 526 527 // FIXME:: template should be checked statement for 528 // comments inside of it 529 530 do 531 { 532 if ( *cur == '<' ) 533 ++level; 534 else 535 --level; 536 537 ++cur; // skip '<' or '>' token 538 539 if ( level == 0 ) 540 return; 541 542 while( *cur != '<' && *cur != '>' && cur < _gSrcEnd ) 543 { 544 if (*cur == 10 ) ++_gLineNo; 545 ++cur; 546 } 547 548 } while (1); 549} 550 551static inline void skip_statement( char*& cur ) 552{ 553 for( ; cur < _gSrcEnd; ++cur ) 554 555 switch (*cur) 556 { 557 case ';' : ++cur; // skip statement-terminator token 558 return; 559 560 case '"' : skip_quoted_string(cur); 561 --cur; 562 continue; 563 564 case 10 : ++_gLineNo; 565 566 continue; 567 case '/' : skip_comments( cur ); 568 --cur; 569 continue; 570 default : continue; 571 } 572} 573 574// "reversed" versions of skip_token() and get_next_token() 575 576static inline void skip_token_back( char*& cur ) 577{ 578 // FIXME:: now, when moving backwards, neither strings nor 579 // comment blocks are checked 580 581 --cur; // skip to the trailing character 582 583 if ( *cur == ',' || 584 *cur == ')' || 585 *cur == '(' 586 ) 587 return; 588 589 590 for( ; cur < _gSrcEnd ; --cur ) 591 { 592 switch ( *cur ) 593 { 594 case ' ' : break; 595 case '\t': break; 596 case 13 : break; 597 case 10 : break; 598 case ',' : break; 599 case '(' : break; 600 601 default : continue; 602 }; 603 604 break; 605 } 606 607 ++cur; // get to the leading character of the token 608} 609 610static inline void skip_next_token_back( char*& cur ) 611{ 612 --cur; // skip leading character of the current token 613 614 if ( *cur == ',' || 615 *cur == ')' || 616 *cur == '(' 617 ) 618 { 619 ++cur; 620 return; 621 } 622 623 for( ; cur < _gSrcEnd; --cur ) 624 { 625 switch ( *cur ) 626 { 627 case ' ' : continue; 628 case '\t': continue; 629 case 13 : continue; 630 case 10 : continue; 631 case ',' : continue; 632 case '(' : continue; 633 634 default : break; 635 }; 636 637 break; 638 } 639 640 ++cur; // position after the trailing charcter of the prev token 641} 642 643static wxString get_token_str( char* cur ) 644{ 645 return wxString( cur, get_token_len( cur ) ); 646} 647 648// skips token or whole expression which may have 649// nested expressions between '(' ')' brackets. 650// 651// Upon return, the cursor points to the terminating bracket ')', 652// 653// Return value is the size of the block 654 655static size_t skip_block( char*& cur ) 656{ 657 size_t level = 0; // nesting level 658 659 char* start = cur; 660 661 // NOTE:: assumed that block not necessarely starts 662 // with bracket rightaway 663 664 if ( *cur == '(' ) 665 { 666 ++level; 667 } 668 669 do 670 { 671 skip_token( cur ); 672 673 char* savedPos = cur; 674 int tmpLnNo; 675 store_line_no( tmpLnNo ); 676 677 get_next_token( cur ); 678 679 if ( cur >= _gSrcEnd ) return 0; 680 681 if ( *cur == '(' ) 682 { 683 ++level; 684 } 685 else 686 if ( *cur == ')' ) 687 { 688 if ( level == 0 ) 689 { 690 cur = savedPos; 691 restore_line_no( tmpLnNo ); 692 693 return size_t(cur-start); 694 } 695 696 --level; 697 698 if ( level == 0 ) 699 { 700 ++cur; 701 702 // QUICK-HACK::to easily handle function prototypes , 703 // it works, besause theoretically there should 704 // be no cast-expressions in non-implementation 705 // scope (e.g. "time( (long*)(ptr+1) )" should not 706 // appear in the declarations, thus it is most likelly 707 // for the ")(" fragment to be within a function 708 // prototype in the declarations scope 709 710 if ( *cur == '(' ) 711 { 712 ++level; 713 continue; 714 } 715 716 else return size_t(cur-start); 717 } 718 } 719 else 720 { 721 if ( level == 0 ) 722 { 723 cur = savedPos; 724 restore_line_no( tmpLnNo ); 725 726 return size_t(cur-start); 727 } 728 } 729 730 } while(1); 731} 732 733// returns 0, if end of source reached 734static inline bool skip_imp_block( char*& cur ) 735{ 736 while( *cur != '{' && cur < _gSrcEnd ) 737 { 738 skip_token( cur ); 739 if ( !get_next_token( cur ) ) return false; 740 } 741 742 while( *cur != '}' && cur < _gSrcEnd ) 743 { 744 skip_token( cur ); 745 if ( !get_next_token( cur ) ) return false; 746 } 747 748 ++cur; 749 750 return true; 751} 752 753static bool is_class_token( char*& cur ) 754{ 755 // FIXME:: the below mess should be cleaned in it's entirely 756 757 if ( *cur == 'i' ) 758 if ( *(cur+1) == 'n' ) 759 760 return cmp_tokens_fast( cur, "interface", 9 ); 761 762 if ( *cur == 'c' ) 763 if ( *(cur+1) == 'l' ) 764 765 return cmp_tokens_fast( cur, "class", 5 ); 766 767 if ( *cur == 's' ) 768 if ( *(cur+1) == 't' ) 769 770 return cmp_tokens_fast( cur, "struct", 6 ); 771 772 if ( *cur == 'u' ) 773 if ( *(cur+1) == 'n' ) 774 775 return cmp_tokens_fast( cur, "union", 5 ); 776 777 return false; 778} 779 780inline static bool is_forward_decl( char* cur ) 781{ 782 do 783 { 784 switch( *cur ) 785 { 786 case ':' : return false; 787 case '{' : return false; 788 case '(' : return false; 789 790 case ';' : return true; 791 792 default : break; 793 }; 794 795 ++cur; 796 797 } while (cur < _gSrcEnd); // prevent running out of bounds 798 799 return false; 800} 801 802inline static bool is_function( char* cur, bool& isAMacro ) 803{ 804 isAMacro = false; 805 806 int tmpLnNo; 807 store_line_no( tmpLnNo ); 808 809 // NOTE:: comments and quoted strings are not checked here 810 811 // first,check for "single-line hanginging macros" like: 812 // ___UNICODE 813 // 814 815 char* eol = cur; 816 skip_to_eol( eol ); 817 818 skip_token( cur ); 819 get_next_token( cur ); 820 821 if ( cur > eol ) 822 { 823 isAMacro = true; 824 restore_line_no( tmpLnNo ); 825 826 return true; 827 } 828 829 // it's not a macro, go to the begining of arg. list 830 831 do 832 { 833 // if bracket found, it's a function or a begining 834 // of some macro 835 if ( *cur == '(' ) 836 { 837 restore_line_no( tmpLnNo ); 838 return true; 839 } 840 841 // end of statement found without any brackets in it 842 // - it cannot be a function 843 844 if ( *cur == ';' ) 845 { 846 restore_line_no( tmpLnNo ); 847 return false; 848 } 849 850 ++cur; 851 852 } while( cur < _gSrcEnd); 853 854 isAMacro = 1; 855 restore_line_no( tmpLnNo ); 856 857 return false; 858} 859 860// upon return the cursor is positioned after the 861// terminating curly brace 862 863static inline void skip_scope_block( char*& cur ) 864{ 865 size_t level = 0; 866 867 for( ; cur < _gSrcEnd ; ++cur ) 868 869 switch( *cur ) 870 { 871 case '/' : skip_comments( cur ); 872 --cur; 873 continue; 874 case '"' : skip_quoted_string( cur ); 875 --cur; 876 continue; 877 878 case '{' : ++level; 879 continue; 880 881 case '}' :--level; 882 if ( level == 0 ) 883 { 884 ++cur; // skip final closing curly brace 885 return; 886 } 887 888 case 10 : ++_gLineNo; continue; 889 890 default : continue; 891 }; 892} 893 894// moves tokens like '*' '**', '***', '&' from the name 895// to the type 896 897static void arrange_indirection_tokens_between( wxString& type, 898 wxString& identifier ) 899{ 900 // TBD:: FIXME:: return value of operators ! 901 902 while ( identifier[0u] == _T('*') || 903 identifier[0u] == _T('&') 904 ) 905 { 906 type += identifier[0u]; 907 identifier.erase(0,1); 908 909 if ( !identifier.length() ) return; 910 } 911} 912 913 914// the only function where multi-lang keyword map is accessed 915 916static bool is_keyword( char* cur ) 917{ 918 size_t len = get_token_len( cur ); 919 920 // put a terminating zero after the given token 921 char tmp = *(cur + len); 922 *(cur+len) = '\0'; 923 924 KeywordMapT::iterator i; 925 926 i = __gMultiLangMap.find( cur ); 927 928 // restore original character suppresed by terminating zero 929 *(cur + len) = tmp; 930 931 return i == __gMultiLangMap.end() ? false : true; 932} 933 934static inline void get_string_between( wxChar* start, wxChar* end, 935 wxString* pStr ) 936{ 937 char saved = *end; 938 939 *end = _T('\0'); 940 *pStr = start; 941 *end = saved; 942} 943 944static wxChar* set_comment_text( wxString& text, wxChar* start ) 945{ 946 wxChar* end = start; 947 948 // to avoid poluting the queue with this comment 949 _gLastSuppresedComment = start; 950 951 skip_comments( end ); 952 953 if ( *(end-1) == _T('/') ) 954 end -= 2; 955 956 start += 2; 957 958 // skip multiple leading '/''s or '*''s 959 while( *start == _T('/') && start < end ) ++start; 960 while( *start == _T('*') && start < end ) ++start; 961 962 get_string_between( start, end, &text ); 963 964 return end; 965} 966 967/***** Implementation for class CJSourceParser *****/ 968 969CJSourceParser::CJSourceParser( bool collectCommnets, bool collectMacros ) 970 : mpStart(0), 971 mpEnd(0), 972 mpCurCtx( 0 ), 973 mCommentsOn( collectCommnets ), 974 mMacrosOn ( collectMacros ) 975{ 976 check_keyword_map(); 977} 978 979spFile* CJSourceParser::Parse( char* start, char* end ) 980{ 981 // set up state variables 982 mCurVis = SP_VIS_PRIVATE; 983 984 spFile* pTopCtx = new spFile(); 985 mpCurCtx = pTopCtx; 986 987 mIsVirtual = 0; 988 mIsTemplate = 0; 989 mNestingLevel = 0; 990 991 m_cur = start; 992 993 mpStart = start; 994 mpEnd = end; 995 996 _gSrcEnd = mpEnd; // let all the C-functions "smell" the end of file 997 _gSrcStart = start; 998 999 _gLineNo = 0; 1000 1001 clear_commets_queue(); 1002 1003 // main parsing loop 1004 1005 do 1006 { 1007 if ( !get_next_token( m_cur ) ) 1008 // end of source reached 1009 return pTopCtx; 1010 1011 if ( memcmp( m_cur, "ScriptSection( const string&", 1012 strlen( "ScriptSection( const string&" ) 1013 ) == 0 1014 ) 1015 { 1016 // int o = 0; 1017 // ++o; 1018 } 1019 1020 switch (*m_cur) 1021 { 1022 case '#' : 1023 { 1024 AddMacroNode( m_cur ); 1025 continue; 1026 } 1027 1028 case ':' : 1029 { 1030 skip_token( m_cur ); 1031 continue; 1032 } 1033 1034 case ';' : 1035 { 1036 skip_token( m_cur ); 1037 continue; 1038 } 1039 1040 case ')' : 1041 { 1042 skip_token( m_cur ); 1043 continue; 1044 } 1045 1046 case '=' : 1047 { 1048 skip_token( m_cur ); 1049 continue; 1050 } 1051 1052 default: break; 1053 } 1054 1055 // 'const' is a part of the return type, not a keyword here 1056 if ( strncmp(m_cur, "const", 5) != 0 && is_keyword( m_cur ) ) 1057 { 1058 // parses, token, if token identifies 1059 // the container context (e.g. class/namespace) 1060 // the corresponding context object is created 1061 // and set as current context 1062 1063 ParseKeyword( m_cur ); 1064 continue; 1065 } 1066 1067 if ( *m_cur >= _T('0') && *m_cur <= _T('9') ) 1068 { 1069 skip_token( m_cur ); 1070 continue; 1071 } 1072 1073 if ( *m_cur == _T('}') ) 1074 { 1075 if ( mCurCtxType != SP_CTX_CLASS ) 1076 { 1077 // FOR NOW:: disable the below assertion 1078 1079 // DBG:: unexpected closing-bracket found 1080 //ASSERT(0); 1081 1082 skip_token( m_cur ); // just skip it 1083 continue; 1084 } 1085 1086 if ( mpCurCtx->GetType() == SP_CTX_CLASS ) 1087 { 1088 int curOfs = ( (m_cur+1) - _gSrcStart ); 1089 1090 mpCurCtx->mContextLength = ( curOfs - mpCurCtx->mSrcOffset ); 1091 } 1092 1093 --mNestingLevel; 1094 1095 // terminate operation/class/namespace context 1096 // TBD:: check if it's really this type of context 1097 1098 wxASSERT( mpCurCtx ); 1099 mpCurCtx = mpCurCtx->GetOutterContext(); 1100 wxASSERT( mpCurCtx ); 1101 1102 if ( mNestingLevel == 0 ) 1103 { 1104 1105 mCurCtxType = SP_CTX_FILE; 1106 1107 // not-nested class delclaration finished, 1108 // rest template flag in any case 1109 mIsTemplate = 0; 1110 } 1111 1112 skip_token( m_cur ); 1113 continue; 1114 } 1115 1116 bool isAMacro = false; 1117 1118 if ( is_function( m_cur, isAMacro ) ) 1119 { 1120 if ( isAMacro ) 1121 { 1122 skip_token( m_cur ); 1123 continue; 1124 } 1125 1126 char* savedPos = m_cur; 1127 1128 int tmpLnNo; 1129 store_line_no( tmpLnNo ); 1130 wxUnusedVar( tmpLnNo ); 1131 1132 isAMacro = false; 1133 1134 if ( !ParseNameAndRetVal( m_cur, isAMacro ) ) 1135 { 1136 if ( !isAMacro ) 1137 { 1138 m_cur = savedPos; 1139 SkipFunction( m_cur ); 1140 } 1141 continue; 1142 } 1143 1144 if ( !ParseArguments( m_cur ) ) 1145 { 1146 // failure while parsing arguments, 1147 // remove enclosing operation context 1148 1149 spContext* pFailed = mpCurCtx; 1150 mpCurCtx = mpCurCtx->GetOutterContext(); 1151 mpCurCtx->RemoveChild( pFailed ); 1152 1153 skip_to_eol( m_cur ); 1154 //m_cur = savedPos; 1155 } 1156 else 1157 { 1158 // otherwise, successfully close operation context: 1159 1160 clear_commets_queue(); 1161 1162 SkipFunctionBody( m_cur ); 1163 1164 mpCurCtx = mpCurCtx->GetOutterContext(); 1165 1166 // DBG:: 1167 wxASSERT( mpCurCtx ); 1168 1169 } 1170 } 1171 else // otherwise it's declaration of a variable; 1172 { 1173 // now, the cursor point to the end of statement (';' token) 1174 1175 if ( mCurCtxType != SP_CTX_CLASS ) 1176 { 1177 // non-class members are ignored 1178 1179 skip_token( m_cur ); // skip the end of statement 1180 continue; 1181 } 1182 1183 ParseMemberVar( m_cur ); 1184 } 1185 1186 } while( 1 ); 1187} 1188 1189void CJSourceParser::AttachComments( spContext& ctx, wxChar* cur ) 1190{ 1191 if ( !mCommentsOn ) return; 1192 1193 MCommentListT& lst = ctx.GetCommentList(); 1194 1195 wxChar* prevComEnd = 0; 1196 1197 int tmpLnNo; 1198 store_line_no( tmpLnNo ); 1199 1200 // attach comments which were found before the given context 1201 1202 for( int i = 0; i != _gCQSize; ++i ) 1203 { 1204 spComment* pComment = new spComment(); 1205 lst.push_back( pComment ); 1206 1207 // find the end of comment 1208 wxChar* start = _gCommentsQueue[i]; 1209 1210 pComment->mIsMultiline = ( *(start+1) == _T('*') ); 1211 1212 // first comment in the queue and multiline 1213 // comments are always treated as a begining 1214 // of the new paragraph in the comment text 1215 1216 if ( i == 0 ) 1217 { 1218 pComment->mStartsPar = true; 1219 } 1220 else if ( pComment->mIsMultiline ) 1221 { 1222 pComment->mStartsPar = true; 1223 } 1224 else 1225 { 1226 // find out wheather there is a new-line 1227 // between to adjecent comments 1228 1229 wxChar* prevLine = start; 1230 skip_to_prev_line(prevLine); 1231 1232 if ( prevLine >= prevComEnd ) 1233 pComment->mStartsPar = true; 1234 else 1235 pComment->mStartsPar = false; 1236 } 1237 1238 prevComEnd = set_comment_text( pComment->m_Text, start ); 1239 } 1240 1241 // attach comments which are at the end of the line 1242 // of the given context (if any) 1243 1244 if ( skip_to_next_comment_in_the_line( cur ) ) 1245 { 1246 spComment* pComment = new spComment(); 1247 lst.push_back( pComment ); 1248 1249 set_comment_text( pComment->m_Text, cur ); 1250 1251 pComment->mStartsPar = 1; 1252 pComment->mIsMultiline = ( *(cur+1) == _T('*') ); 1253 1254 // mark this comment, so that it would not 1255 // get in the comments list of the next context 1256 _gLastSuppresedComment = cur; 1257 } 1258 1259 restore_line_no( tmpLnNo ); 1260 1261 clear_commets_queue(); 1262} 1263 1264void CJSourceParser::AddMacroNode( wxChar*& cur ) 1265{ 1266 wxChar* start = cur; 1267 1268 int lineNo = get_line_no(); 1269 1270 skip_preprocessor_dir( cur ); 1271 1272 int tmpLnNo; 1273 store_line_no( tmpLnNo ); 1274 1275 if ( !mMacrosOn ) return; 1276 1277 spPreprocessorLine* pPL = new spPreprocessorLine(); 1278 pPL->mSrcLineNo = lineNo; 1279 1280 AttachComments( *pPL, cur ); 1281 1282 get_string_between( start, cur, &pPL->m_Line ); 1283 1284 ++start; // skip '#' 1285 get_next_token( start ); 1286 1287 pPL->mDefType = SP_PREP_DEF_OTHER; 1288 1289 // if we found a definition or redefinition, 1290 // determine the type exactly and assign 1291 // a name to the context 1292 1293 if ( *start == _T('d') ) 1294 { 1295 if ( cmp_tokens_fast( start, _T("define"), 6 ) ) 1296 { 1297 char* tok = start+6; 1298 1299 get_next_token( tok ); 1300 1301 pPL->m_Name = get_token_str( tok ); 1302 1303 skip_token( tok ); 1304 get_next_token( tok); 1305 1306 1307 if ( tok > cur ) 1308 pPL->mDefType = SP_PREP_DEF_DEFINE_SYMBOL; 1309 else 1310 pPL->mDefType = SP_PREP_DEF_REDEFINE_SYMBOL; 1311 } 1312 } 1313 else if ( *start == _T('i') ) 1314 { 1315 if ( cmp_tokens_fast( start, _T("include"), 7 ) ) 1316 { 1317 pPL->mDefType = SP_PREP_DEF_INCLUDE_FILE; 1318 } 1319 else if ( *++start == _T('f') ) 1320 { 1321 // either "#if" or "#ifdef" 1322 cur = start; 1323 skip_token( cur ); 1324 get_next_token( cur ); 1325 1326 wxString condition = get_token_str( cur ); 1327 1328 // currently, everything except '0' is true 1329 if ( condition == _T("0") ) { 1330 // skip until the following else or enif 1331 while ( cur < _gSrcEnd ) { 1332 skip_to_eol( cur ); 1333 skip_eol( cur ); 1334 1335 get_next_token( cur ); 1336 if ( *cur++ == _T('#') && *cur == _T('e') ) 1337 break; 1338 } 1339 } 1340 1341 // TODO parse the condition... 1342 } 1343 } 1344 else if ( cmp_tokens_fast( start, _T("else"), 4 ) ) 1345 { 1346 // skip until "#endif" 1347 while ( cur < _gSrcEnd ) { 1348 skip_to_eol( cur ); 1349 skip_eol( cur ); 1350 1351 get_next_token( cur ); 1352 if ( *cur++ == _T('#') && cmp_tokens_fast( cur, "endif", 5 ) ) 1353 break; 1354 } 1355 } 1356 1357 mpCurCtx->AddMember( pPL ); 1358 1359 skip_to_eol( cur ); 1360 skip_eol( cur ); 1361 1362 restore_line_no( tmpLnNo ); 1363 1364 clear_commets_queue(); 1365} 1366 1367void CJSourceParser::ParseKeyword( char*& cur ) 1368{ 1369 // analyze token, which identifies the begining of a new context 1370 1371 if ( CheckVisibilty( cur ) ) 1372 { 1373 skip_token( cur ); 1374 return; 1375 } 1376 1377 if ( is_class_token( cur ) ) 1378 { 1379 if ( is_forward_decl( cur ) ) 1380 { 1381 // forward declarations are ignored; 1382 skip_token( cur ); 1383 return; 1384 } 1385 1386 if ( mNestingLevel == 0 ) 1387 { 1388 // change context form global class context 1389 mCurCtxType = SP_CTX_CLASS; 1390 } 1391 1392 ++mNestingLevel; 1393 1394 // add information about new class (name, inheritance, etc) 1395 AddClassNode( cur ); 1396 1397 // the default visiblity for class members is 'private' 1398 mCurVis = SP_VIS_PRIVATE; 1399 1400 return; 1401 } 1402 1403 size_t len = get_token_len( cur ); 1404 1405 if ( cmp_tokens_fast( cur, "typedef", len ) ) 1406 { 1407 skip_token(cur); 1408 get_next_token(cur); 1409 1410 if ( cmp_tokens_fast( cur, "struct", len ) || 1411 cmp_tokens_fast( cur, "union", len ) || 1412 cmp_tokens_fast( cur, "class", len ) 1413 ) 1414 { 1415 if ( mNestingLevel == 0 ) 1416 { 1417 // change context form global class context 1418 mCurCtxType = SP_CTX_CLASS; 1419 } 1420 1421 ++mNestingLevel; 1422 1423 // add information about new class (name, inheritance, etc) 1424 AddClassNode( cur ); 1425 1426 // the default visiblity for class members is 'private' 1427 mCurVis = SP_VIS_PRIVATE; 1428 1429 return; 1430 1431 // FOR NOW:: typedef struct, etc are also ignored 1432 //skip_scope_block( cur ); 1433 } 1434 1435 if ( cmp_tokens_fast( cur, "enum", len ) ) 1436 { 1437 AddEnumNode( cur ); 1438 return; 1439 } 1440 1441 AddTypeDefNode( cur ); 1442 1443 return; 1444 } 1445 1446 if ( cmp_tokens_fast( cur, "enum", len ) ) 1447 { 1448 AddEnumNode( cur ); 1449 return; 1450 } 1451 1452 if ( cmp_tokens_fast( cur, "extern", len ) ) 1453 { 1454 // extern's are ignored (both extern "C" and extern vars) 1455 while ( *cur != '{' && 1456 *cur != ';' ) 1457 { 1458 skip_token( cur ); 1459 get_next_token( cur ); 1460 } 1461 return; 1462 1463 } 1464 if ( cmp_tokens_fast( cur, "enum", len ) ) 1465 { 1466 // enumeration blocks are ignored 1467 1468 skip_scope_block( cur ); 1469 1470 get_next_token( cur ); 1471 skip_token( cur ); // skip ';' token; 1472 return; 1473 } 1474 1475 if ( cmp_tokens_fast( cur, "package", len ) ) 1476 { 1477 // packages are ignored 1478 skip_statement( cur ); 1479 return; 1480 }; 1481 1482 if ( cmp_tokens_fast( cur, "import", len ) ) 1483 { 1484 // import statements are ignored 1485 skip_statement( cur ); 1486 return; 1487 } 1488 1489 if ( cmp_tokens_fast( cur, "virtual", len ) ) 1490 { 1491 // probably the virtual method is in front of us; 1492 mIsVirtual = 1; 1493 skip_token( cur ); 1494 return; 1495 } 1496 1497 if ( cmp_tokens_fast( cur, "template", len ) ) 1498 { 1499 mIsTemplate = 1; 1500 skip_tempalate_statement( cur ); 1501 return; 1502 } 1503 1504 if ( cmp_tokens_fast( cur, "friend", len ) ) 1505 { 1506 skip_statement( cur ); 1507 return; 1508 } 1509 1510 // ingnore "unsigificant" tokens (i.e. which do not 1511 // affect the current parsing context) 1512 1513 skip_token( cur ); 1514} 1515 1516bool CJSourceParser::ParseNameAndRetVal( char*& cur, bool& isAMacro ) 1517{ 1518 isAMacro = false; 1519 1520 // FOR NOW:: all functions in the global 1521 // scope are ignored 1522 1523 int lineNo = get_line_no(); 1524 1525 char* start = cur; 1526 1527 bool isVirtual = false; 1528 while( *cur != '(' ) 1529 { 1530 if ( get_token_str( cur ) == "virtual" ) 1531 isVirtual = true; 1532 1533 skip_token( cur ); 1534 if ( !get_next_token( cur ) ) return false; 1535 } 1536 1537 char* bracketPos = cur; 1538 char* savedPos = cur + 1; 1539 1540 int tmpLnNo; 1541 store_line_no( tmpLnNo ); 1542 1543 // skip gap between function name and start of paramters list 1544 while ( *(cur-1) == ' ' ) 1545 --cur; 1546 1547 // check if it's not a macro, and let plugin handle it, if so 1548 1549 if ( mpPlugin ) 1550 { 1551 skip_token_back( cur ); 1552 1553 char* tmp = cur; 1554 1555 if ( mpPlugin->CanUnderstandContext( tmp, _gSrcEnd, mpCurCtx ) ) 1556 { 1557 cur = tmp; 1558 1559 mpPlugin->ParseContext( _gSrcStart, cur, _gSrcEnd, mpCurCtx ); 1560 1561 isAMacro = true; 1562 1563 return false; 1564 } 1565 } 1566 1567 spOperation* pOp = new spOperation(); 1568 1569 pOp->mSrcLineNo = lineNo; 1570 pOp->mSrcOffset = int( start - _gSrcStart ); 1571 pOp->mHeaderLength = int( bracketPos - start ); 1572 if ( mpCurCtx->GetContextType() == SP_CTX_CLASS ) 1573 pOp->mScope = mpCurCtx->m_Name; 1574 1575 mpCurCtx->AddMember( pOp ); 1576 pOp->mVisibility = mCurVis; 1577 pOp->mIsVirtual = isVirtual; 1578 1579 // add comments about operation 1580 AttachComments( *pOp, cur ); 1581 1582 // go backwards to method name 1583 skip_token_back( cur ); 1584 1585 pOp->m_Name = get_token_str( cur ); 1586 1587 // checker whether it's not an operator 1588 char chFirst = *pOp->m_Name.c_str(); 1589 if ( !isalpha(chFirst) && chFirst != '_' && chFirst != '~' ) { 1590 // skip 'operator' 1591 skip_next_token_back( cur ); 1592 skip_token_back( cur ); 1593 1594 wxString lastToken = get_token_str( cur ); 1595 if ( lastToken == "operator" ) { 1596 lastToken += pOp->m_Name; 1597 pOp->m_Name = lastToken; 1598 } 1599 else { 1600 // ok, it wasn't an operator after all 1601 skip_token( cur ); 1602 } 1603 } 1604 else if ( pOp->m_Name == "operator" ) { 1605 skip_token( cur ); 1606 get_next_token( cur ); 1607 wxString oper = get_token_str( cur ); 1608 1609 pOp->m_Name += oper; 1610 } 1611 1612 // go backwards to method return type 1613 skip_next_token_back( cur ); 1614 1615 if ( cur >= start ) 1616 { 1617 wxString rettype = wxString( start, size_t( cur-start ) ); 1618 // FIXME just for now... 1619 wxString::size_type pos = 0; 1620 wxString toerase("WXDLLEXPORT "); 1621 while((pos = rettype.find(toerase, pos)) != wxString::npos) 1622 rettype.erase(pos, toerase.length()); 1623 pOp->m_RetType = rettype; 1624 } 1625 1626 arrange_indirection_tokens_between( pOp->m_RetType, pOp->m_Name ); 1627 1628 cur = savedPos; 1629 restore_line_no( tmpLnNo ); 1630 1631 // now, enter operation context 1632 mpCurCtx = pOp; 1633 1634 return true; 1635} 1636 1637bool CJSourceParser::ParseArguments( char*& cur ) 1638{ 1639 // DANGER-MACROS:: 1640 1641 // now cursor position is right after the first opening bracket 1642 // of the function declaration 1643 1644 char* blocks [16]; // used exclusivelly for iterative "lean out" 1645 // of macros and misc. not-obviouse grammar 1646 // (dirty,, but we cannot do it very nice, 1647 // we're not preprocessor-free C/C++ code) 1648 int blockSizes[16]; 1649 1650 do 1651 { 1652 size_t blocksSkipped = 0; 1653 1654 get_next_token( cur ); 1655 1656 bool first_blk = true; 1657 1658 while( *cur != ')' && *cur != ',' ) 1659 { 1660 blocks[blocksSkipped] = cur; 1661 1662 if ( first_blk ) 1663 { 1664 char* prev = cur; 1665 skip_token( cur ); 1666 1667 blockSizes[blocksSkipped] = size_t(cur-prev); 1668 1669 first_blk = 0; 1670 } 1671 else 1672 blockSizes[blocksSkipped] = skip_block( cur ); 1673 1674 get_next_token( cur ); 1675 ++blocksSkipped; 1676 } 1677 1678 1679 if ( blocksSkipped == 1 ) 1680 { 1681 // check if the empty arg. list stressed with "void" inside 1682 if ( cmp_tokens_fast( blocks[0] , "void", 4 ) ) 1683 { 1684 cur++; // skip ')' 1685 1686 break; 1687 } 1688 1689 // FIXME:: TBD:: K&R-style function declarations! 1690 1691 // if only one block enclosed, than it's probably 1692 // some macro, there should be at least two blocks, 1693 // one for argument type and another for it's identifier 1694 return false; 1695 } 1696 1697 if ( blocksSkipped == 0 ) 1698 { 1699 if ( *cur == 10 ) ++_gLineNo; 1700 ++cur; // skip ')' 1701 1702 break; // function without paramters 1703 } 1704 1705 // we should be in the operation context now 1706 spOperation* pOp = (spOperation*)mpCurCtx; 1707 1708 spParameter* pPar = new spParameter(); 1709 1710 pOp->AddMember( pPar ); 1711 // FOR NOW:: line number is not exact if argument list is mutiline 1712 pPar->mSrcLineNo = get_line_no(); 1713 1714 size_t nameBlock = blocksSkipped - 1; 1715 size_t typeBlock = nameBlock - 1; 1716 1717 // check if default values present 1718 if ( *blocks[typeBlock] == '=' ) 1719 { 1720 // expressions like "int = 5" are ignored, 1721 // since name for paramters is required 1722 if ( blocksSkipped == 3 ) 1723 { 1724 if ( *cur == ')' ) 1725 { 1726 ++cur; 1727 break; 1728 } 1729 else 1730 continue; 1731 } 1732 1733 pPar->m_InitVal = wxString( blocks[nameBlock], blockSizes[nameBlock] ); 1734 1735 nameBlock = nameBlock - 2; // skip '=' token and default value block 1736 typeBlock = nameBlock - 1; 1737 } 1738 1739 // attach comments about the parameter 1740 AttachComments( *pPar, blocks[nameBlock] ); 1741 1742 // retrieve argument name 1743 pPar->m_Name = wxString( blocks[nameBlock], blockSizes[nameBlock] ); 1744 1745 // retreive argument type 1746 1747 size_t len = blockSizes[ typeBlock ]; 1748 len = size_t ( (blocks[ typeBlock ] + len) - blocks[ 0 ] ); 1749 1750 pPar->m_Type = wxString( blocks[0], len ); 1751 1752 arrange_indirection_tokens_between( pPar->m_Type, pPar->m_Name ); 1753 1754 if ( *cur == ')' ) 1755 { 1756 ++cur; 1757 break; 1758 } 1759 1760 ++cur; // skip comma 1761 get_next_token(cur); 1762 1763 } while(1); 1764 1765 // skip possible whitespace between ')' and following "const" 1766 while ( isspace(*cur) ) 1767 cur++; 1768 1769 // check if it was really a function not a macro, 1770 // if so, than it should be terminated with semicolon ';' 1771 // or opening implemenetaton bracket '{' 1772 1773 char* tok = cur; 1774 1775 int tmpLnNo; 1776 store_line_no( tmpLnNo ); 1777 1778 bool result = true; 1779 1780 do 1781 { 1782 if ( *tok == '{' || *tok == ';' ) 1783 { 1784 restore_line_no(tmpLnNo); 1785 break; 1786 } 1787 1788 // check for unexpected tokens 1789 if ( *tok == '=' || *tok == '0' ) 1790 { 1791 skip_token(tok); 1792 if ( !get_next_token(tok) ) return false; 1793 continue; 1794 } 1795 1796 if ( *tok == '}' ) return false; 1797 1798 // if initialization list found 1799 if ( *tok == ':' ) 1800 { 1801 restore_line_no(tmpLnNo); 1802 break; 1803 } 1804 1805 if ( cmp_tokens_fast( tok, "const", 5 ) ) 1806 { 1807 ((spOperation*)mpCurCtx)->mIsConstant = true; 1808 1809 skip_token(tok); 1810 if ( !get_next_token(tok) ) return false; 1811 continue; 1812 } 1813 1814 if ( CheckVisibilty( tok ) ) return false; 1815 1816 // if next context found 1817 if ( is_keyword( tok ) ) return false; 1818 1819 skip_token(tok); 1820 if ( !get_next_token(tok) ) return false; 1821 1822 } while(1); 1823 1824 return result; 1825} 1826 1827void CJSourceParser::ParseMemberVar( char*& cur ) 1828{ 1829 MMemberListT& members = mpCurCtx->GetMembers(); 1830 1831 bool firstMember = true; 1832 1833 wxString type; 1834 1835 // jump to the end of statement 1836 // and start collecting same-type varibles 1837 // back-to-front towards the type identifier 1838 1839 skip_statement( cur ); 1840 char* savedPos = cur; 1841 1842 int tmpLnNo; 1843 store_line_no( tmpLnNo ); 1844 1845 --cur; // rewind back to ';' 1846 1847 do 1848 { 1849 spAttribute* pAttr = new spAttribute(); 1850 // FOR NOW:: line not is not exact, if member declaration is multiline 1851 pAttr->mSrcLineNo = get_line_no(); 1852 1853 mpCurCtx->AddMember( pAttr ); 1854 pAttr->mVisibility = mCurVis; 1855 1856 pAttr->mIsConstant = 0; 1857 1858 if ( firstMember ) 1859 { 1860 firstMember = 0; 1861 } 1862 1863 skip_token_back( cur ); 1864 1865 // attach comments about the attribute 1866 AttachComments( *pAttr, cur ); 1867 1868 pAttr->m_Name = get_token_str( cur ); 1869 1870 // guessing that this going to be variable type 1871 skip_next_token_back( cur ); 1872 skip_token_back( cur ); 1873 1874 pAttr->m_Type = get_token_str( cur ); 1875 1876 // if comma, than variable list continues 1877 // otherwise the variable type reached - stop 1878 1879 if ( *cur == _T('=') ) 1880 { 1881 // yes, we've mistaken, it was not a identifier, 1882 // but it's default value 1883 pAttr->m_InitVal = pAttr->m_Name; 1884 1885 // skip default value and '=' symbol 1886 skip_next_token_back( cur ); 1887 skip_token_back( cur ); 1888 1889 pAttr->m_Name = get_token_str( cur ); 1890 1891 skip_next_token_back( cur ); 1892 skip_token_back( cur ); 1893 } 1894 1895 if ( *cur != ',' ) 1896 { 1897 type = get_token_str( cur ); 1898 break; 1899 } 1900 1901 } while(1); 1902 1903 size_t first = 0; 1904 1905 // set up types for all collected (same-type) attributes; 1906 while ( first != members.size() - 1 ) 1907 { 1908 spAttribute* pAttr = members[first++]->CastToAttribute(); 1909 if ( !pAttr ) 1910 continue; 1911 1912 if ( pAttr->m_Type.empty() ) 1913 pAttr->m_Type = type; 1914 pAttr->mVisibility = mCurVis; 1915 1916 if ( !pAttr->m_Name.empty() ) 1917 arrange_indirection_tokens_between( pAttr->m_Type, pAttr->m_Name ); 1918 } 1919 1920 cur = savedPos; 1921 restore_line_no( tmpLnNo ); 1922 1923 clear_commets_queue(); 1924 1925 1926} 1927 1928void CJSourceParser::SkipFunction( char*& cur ) 1929{ 1930 while ( *cur != '(' && cur < _gSrcEnd ) 1931 { 1932 if (*cur == 10 ) ++_gLineNo; 1933 ++cur; 1934 } 1935 1936 skip_next_token_back( cur ); // go back and skip function identifier 1937 skip_token_back( cur ); // go back and skip return type 1938 1939 skip_block( cur ); // now, go ahead and skip whole declaration 1940 1941 SkipFunctionBody( cur ); 1942 1943} 1944 1945void CJSourceParser::SkipFunctionBody( char*& cur ) 1946{ 1947 // FIXME:: check for comments and quoted stirngs here 1948 1949 bool hasDefinition = false; 1950 1951 while( *cur != '{' && *cur != ';' ) 1952 { 1953 if (*cur == 10 ) ++_gLineNo; 1954 ++cur; 1955 } 1956 1957 if ( *cur == ';' ) 1958 { 1959 ++cur; 1960 } 1961 else 1962 { 1963 hasDefinition = true; 1964 1965 skip_scope_block( cur ); // skip the whole imp. 1966 } 1967 1968 if ( mpCurCtx->GetType() == SP_CTX_OPERATION ) 1969 { 1970 spOperation& op = *((spOperation*)mpCurCtx); 1971 1972 int curOfs = int ( cur - _gSrcStart ); 1973 1974 op.mContextLength = curOfs - mpCurCtx->mSrcOffset; 1975 1976 op.mHasDefinition = hasDefinition; 1977 1978 // separate scope resolution token from the name of operation 1979 1980 for( size_t i = 0; i != op.m_Name.length(); ++i ) 1981 { 1982 if ( op.m_Name[i] == ':' && op.m_Name[i+1] == ':' ) 1983 { 1984 wxString unscoped( op.m_Name, i+2, op.m_Name.length() - ( i + 2 ) ); 1985 1986 op.mScope = wxString( op.m_Name, 0, i ); 1987 1988 op.m_Name = unscoped; 1989 1990 break; 1991 } 1992 } 1993 } 1994} 1995 1996bool CJSourceParser::CheckVisibilty( char*& cur ) 1997{ 1998 size_t len = get_token_len( cur ); 1999 2000 if ( cmp_tokens_fast( cur, "public:", len ) ) 2001 { 2002 mCurVis = SP_VIS_PUBLIC; 2003 return true; 2004 } 2005 2006 if ( cmp_tokens_fast( cur, "protected:", len ) ) 2007 { 2008 mCurVis = SP_VIS_PROTECTED; 2009 return true; 2010 } 2011 2012 if ( cmp_tokens_fast( cur, "private:", len ) ) 2013 { 2014 mCurVis = SP_VIS_PRIVATE; 2015 return true; 2016 } 2017 2018 return false; 2019} 2020 2021void CJSourceParser::AddClassNode( char*& cur ) 2022{ 2023 char* ctxStart = cur; 2024 2025 wxString classkeyword = get_token_str( cur ); 2026 2027 skip_token( cur ); // skip 'class' keyword 2028 if ( !get_next_token( cur ) ) return; 2029 2030 // in C++ 2031 if ( *cur == ':' ) 2032 { 2033 skip_token( cur ); 2034 get_next_token( cur ); 2035 } 2036 2037 // by default all class members are private 2038 mCurVis = SP_VIS_PRIVATE; 2039 2040 spClass* pClass = new spClass(); 2041 if ( classkeyword == "class" ) 2042 pClass->mClassSubType = SP_CLTYPE_CLASS; 2043 else if ( classkeyword == "struct" ) { 2044 pClass->mClassSubType = SP_CLTYPE_STRUCTURE; 2045 2046 mCurVis = SP_VIS_PUBLIC; 2047 } 2048 else if ( classkeyword == "union" ) { 2049 pClass->mClassSubType = SP_CLTYPE_UNION; 2050 2051 mCurVis = SP_VIS_PUBLIC; 2052 } 2053 else if ( classkeyword == "interface" ) 2054 pClass->mClassSubType = SP_CLTYPE_INTERFACE; 2055 else { 2056 pClass->mClassSubType = SP_CLTYPE_INVALID; 2057 2058 wxFAIL_MSG("unknown class keyword"); 2059 } 2060 2061 mpCurCtx->AddMember( pClass ); 2062 2063 // attach comments about the class 2064 AttachComments( *pClass, cur ); 2065 2066 pClass->mSrcLineNo = get_line_no(); 2067 2068 pClass->mSrcOffset = int( ctxStart - _gSrcStart ); 2069 2070 char* nameTok = cur; 2071 pClass->m_Name = get_token_str( cur ); 2072 2073 bool isDerived = 0; 2074 2075 // DANGER-MACROS:: 2076 2077 do 2078 { 2079 skip_token( cur ); 2080 if ( !get_next_token( cur ) ) return; 2081 2082 if ( *cur == ':' ) 2083 { 2084 isDerived = 1; 2085 2086 char* tok = cur; 2087 2088 int tmpLn; 2089 store_line_no( tmpLn ); 2090 2091 skip_next_token_back( tok ); 2092 skip_token_back( tok ); 2093 2094 restore_line_no( tmpLn ); 2095 2096 // class name should precend ':' colon, thus 2097 // the one which was captured before was 2098 // proablty something else (like __dllexport MyClass : ... ) 2099 2100 if ( nameTok != tok ) 2101 { 2102 pClass->m_Name = get_token_str( tok ); 2103 } 2104 2105 } 2106 2107 if ( *cur == '{' ) 2108 break; 2109 2110 if ( *cur == ',' ) 2111 continue; 2112 2113 size_t len = get_token_len( cur ); 2114 2115 // skip neglectable C++ modifieres 2116 if ( cmp_tokens_fast( cur, "public", len ) ) 2117 continue; 2118 2119 if ( cmp_tokens_fast( cur, "protected", len ) ) 2120 continue; 2121 2122 if ( cmp_tokens_fast( cur, "private", len ) ) 2123 continue; 2124 2125 if ( cmp_tokens_fast( cur, "virtual", len ) ) 2126 continue; 2127 2128 // skip neglectable JAVA modifieres 2129 2130 if ( cmp_tokens_fast( cur, "extends", len ) ) 2131 { 2132 isDerived = 1; 2133 continue; 2134 } 2135 2136 if ( cmp_tokens_fast( cur, "implements", len ) ) 2137 { 2138 isDerived = 1; 2139 continue; 2140 } 2141 2142 // all we need to know is superclass or interface 2143 2144 char* tok = cur; 2145 int tmpLn; 2146 store_line_no( tmpLn ); 2147 2148 skip_token(tok); 2149 get_next_token(tok); 2150 2151 restore_line_no( tmpLn ); 2152 2153 if ( *tok != ':' && *cur != ':' ) 2154 2155 pClass->m_SuperClassNames.push_back( wxString( cur, len ) ); 2156 2157 } while(1); 2158 2159 if ( !isDerived ) 2160 { 2161 int tmpLn; 2162 store_line_no( tmpLn ); 2163 2164 while ( pClass->m_SuperClassNames.size() ) 2165 2166 pClass->m_SuperClassNames.erase( &pClass->m_SuperClassNames[0] ); 2167 2168 char* tok = cur; 2169 2170 // some non-obviouse token was following "class" keyword - 2171 // we've confused it with class name - thus now we're reverting this mistake 2172 2173 skip_next_token_back( tok ); 2174 skip_token_back( tok ); 2175 2176 pClass->m_Name = get_token_str( tok ); 2177 2178 restore_line_no( tmpLn ); 2179 } 2180 2181 2182 ++cur; // skip opening curly brace 2183 2184 pClass->mHeaderLength = ( cur - ctxStart ); 2185 2186 // now, enter the class context 2187 mpCurCtx = pClass; 2188 2189 clear_commets_queue(); 2190} 2191 2192void CJSourceParser::AddEnumNode( wxChar*& cur ) 2193{ 2194 // now the cursor is at "enum" keyword 2195 wxChar* start = cur; 2196 2197 spEnumeration* pEnum = new spEnumeration(); 2198 mpCurCtx->AddMember( pEnum ); 2199 2200 pEnum->mSrcLineNo = get_line_no(); 2201 2202 2203 AttachComments( *pEnum, cur ); 2204 2205 skip_token( cur ); 2206 if ( !get_next_token( cur ) ) return; 2207 2208 // check if enumeration has got it's identifier 2209 if ( *cur != '{' ) 2210 { 2211 pEnum->m_Name = get_token_str( cur ); 2212 } 2213 2214 if ( !skip_imp_block( cur ) ) return; 2215 2216 get_string_between( start, cur, &pEnum->m_EnumContent ); 2217 2218 if ( get_next_token(cur) ) 2219 { 2220 // check if the identifier if after the {...} block 2221 if ( *cur != ';' ) 2222 2223 pEnum->m_Name = get_token_str( cur ); 2224 } 2225 2226 clear_commets_queue(); 2227} 2228 2229void CJSourceParser::AddTypeDefNode( wxChar*& cur ) 2230{ 2231 // now the cursor at the token next to "typedef" keyword 2232 2233 if ( !get_next_token(cur) ) return; 2234 2235 wxChar* start = cur; 2236 2237 spTypeDef* pTDef = new spTypeDef(); 2238 mpCurCtx->AddMember( pTDef ); 2239 2240 pTDef->mSrcLineNo = get_line_no(); 2241 2242 AttachComments( *pTDef, cur ); 2243 2244 skip_statement( cur ); 2245 2246 int tmpLnNo; 2247 store_line_no( tmpLnNo ); 2248 2249 wxChar* tok = cur-1; 2250 skip_next_token_back( tok ); 2251 2252 wxChar* nameEnd = tok; 2253 2254 skip_token_back( tok ); 2255 2256 wxChar* nameStart = tok; 2257 2258 skip_next_token_back( tok ); 2259 2260 wxChar* typeEnd = tok; 2261 2262 // check if it's function prototype 2263 if ( *nameStart == ')' ) 2264 { 2265 typeEnd = nameStart+1; 2266 2267 // skip argument list 2268 while ( *nameStart != '(' ) --nameStart; 2269 2270 // skip to function type definition 2271 while ( *nameStart != ')' ) --nameStart; 2272 2273 skip_next_token_back( nameStart ); 2274 2275 nameEnd = nameStart; 2276 2277 skip_token_back( nameStart ); 2278 2279 if ( *nameStart == '*' ) ++nameStart; 2280 } 2281 2282 get_string_between( start, typeEnd, &pTDef->m_OriginalType ); 2283 2284 get_string_between( nameStart, nameEnd, &pTDef->m_Name ); 2285 2286 clear_commets_queue(); 2287 2288 restore_line_no( tmpLnNo ); 2289} 2290