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