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
39 static char* _gSrcStart = 0;
40 static char* _gSrcEnd   = 0;
41 static wxChar* _gLastSuppresedComment = 0;
42 static int   _gLineNo      = 0;
43 
44 // FOR NOW:: comments queue is static
45 #define MAX_CQ_ENTRIES 128
46 static char* _gCommentsQueue[MAX_CQ_ENTRIES];
47 static int    _gCQSize = 0;
48 
49 /***** keyword map related structures *****/
50 
51 struct less_c_str
52 {
operator ()less_c_str53     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 
71 static KeywordMapT __gMultiLangMap;
72 static int         __gMapReady = 0;
73 
74 static 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 
check_keyword_map()110 static 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 
skip_to_eol(char * & cur)134 static inline void skip_to_eol( char*& cur )
135 {
136     while( *(cur) != 10 && *cur != 13 && cur < _gSrcEnd) ++cur;
137 }
138 
skip_eol(char * & cur)139 static 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 
skip_to_next_comment_in_the_line(char * & cur)150 static 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 
store_line_no(int & toVar)178 inline static void store_line_no( int& toVar )
179 {
180     toVar = _gLineNo;
181 }
182 
restore_line_no(int storedLineNo)183 inline static void restore_line_no( int storedLineNo )
184 {
185     _gLineNo = storedLineNo;
186 }
187 
get_line_no()188 inline static int get_line_no()
189 {
190     return _gLineNo;
191 }
192 
skip_to_prev_line(char * & cur)193 static 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 
skip_comments(char * & cur)224 static 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 
clear_commets_queue()321 static inline void clear_commets_queue()
322 {
323     _gCQSize = 0;
324 }
325 
skip_quoted_string(char * & cur)326 static 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 
get_next_token(char * & cur)357 static 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 
skip_preprocessor_dir(wxChar * & cur)385 static 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 
skip_token(char * & cur)402 static 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 
get_token_len(char * tok)472 static 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 
cmp_tokens(char * tok1,char * tok2)483 static 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 
cmp_tokens_fast(char * tok1,char * tok2,size_t len)504 static 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 
skip_tempalate_statement(char * & cur)516 static 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 
skip_statement(char * & cur)551 static 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 
skip_token_back(char * & cur)576 static 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 
skip_next_token_back(char * & cur)610 static 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 
get_token_str(char * cur)643 static 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 
skip_block(char * & cur)655 static 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
skip_imp_block(char * & cur)734 static 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 
is_class_token(char * & cur)753 static 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 
is_forward_decl(char * cur)780 inline 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 
is_function(char * cur,bool & isAMacro)802 inline 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 
skip_scope_block(char * & cur)863 static 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 
arrange_indirection_tokens_between(wxString & type,wxString & identifier)897 static 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 
is_keyword(char * cur)916 static 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 
get_string_between(wxChar * start,wxChar * end,wxString * pStr)934 static 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 
set_comment_text(wxString & text,wxChar * start)944 static 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 
CJSourceParser(bool collectCommnets,bool collectMacros)969 CJSourceParser::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 
Parse(char * start,char * end)979 spFile* 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 
AttachComments(spContext & ctx,wxChar * cur)1189 void 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 
AddMacroNode(wxChar * & cur)1264 void 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 
ParseKeyword(char * & cur)1367 void 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 
ParseNameAndRetVal(char * & cur,bool & isAMacro)1516 bool 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 
ParseArguments(char * & cur)1637 bool 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 
ParseMemberVar(char * & cur)1827 void 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 
SkipFunction(char * & cur)1928 void 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 
SkipFunctionBody(char * & cur)1945 void 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 
CheckVisibilty(char * & cur)1996 bool 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 
AddClassNode(char * & cur)2021 void 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 
AddEnumNode(wxChar * & cur)2192 void 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 
AddTypeDefNode(wxChar * & cur)2229 void 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