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