1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: Contrib. demo
4 // Author: Aleksandras Gluchovas
8 // Copyright: (c) Aleskandars Gluchovas
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
13 #pragma implementation "acell.h"
17 // For compilers that support precompilation, includes "wx/wx.h".
18 #include "wx/wxprec.h"
30 #if defined( wxUSE_TEMPLATE_STL )
41 /***** Implementation for class SJParser *****/
43 // statics used by inline'ed C helper-functions
44 static char* _gSrcStart
= 0;
45 static char* _gSrcEnd
= 0;
46 static char* _gLastSuppresedComment
= 0;
47 static int _gLineNo
= 0;
49 // FOR NOW:: comments queue is static
50 #define MAX_CQ_ENTRIES 128
51 static char* _gCommentsQueue
[MAX_CQ_ENTRIES
];
52 static int _gCQSize
= 0;
54 /***** keyword map related structures *****/
58 inline bool operator()( char* x
, char* y
) const
59 { return ( strcmp( x
,y
) < 0 );
63 //WXSTL_MAP(CharPtrT,CharPtrT, LESS_THEN_FUNCTOR(CharPtrT));
65 #if defined( wxUSE_TEMPLATE_STL )
67 typedef map
< char*, char*, less_c_str
> KeywordMapT
;
71 typedef char* CharPtrT
;
72 typedef WXSTL_MAP( CharPtrT
, CharPtrT
,less_c_str
) KeywordMapT
;
76 static KeywordMapT __gMultiLangMap
;
77 static int __gMapReady
= 0;
79 static char* __gKeyWords
[] =
115 static void check_keyword_map()
121 // "make sure" the address of the first member of non-polimorphic class
122 // coinsides with the address of the instance
124 char** keyword
= __gKeyWords
;
126 while ( (*keyword
) != 0 )
128 __gMultiLangMap
.insert(
129 KeywordMapT::value_type( *keyword
, *keyword
)
137 /***** helper functions *****/
139 static inline void skip_to_eol( char*& cur
)
141 while( *(cur
) != 10 && *cur
!= 13 && cur
< _gSrcEnd
) ++cur
;
144 static inline void skip_eol( char*& cur
)
155 static inline bool skip_to_next_comment_in_the_line( char*& cur
)
159 while( cur
< _gSrcEnd
&&
165 if ( cur
== _gSrcEnd
) return FALSE
;
169 if ( (*(cur
+1) == '*') ||
170 (*(cur
+1) == '/') ) return TRUE
;
183 inline static void store_line_no( int& toVar
)
188 inline static void restore_line_no( int storedLineNo
)
190 _gLineNo
= storedLineNo
;
193 inline static int get_line_no()
198 static void skip_to_prev_line( char*& cur
)
200 while( cur
>= _gSrcStart
&&
205 // NOTE:: '\n' is 13,10 for DOS
206 // '\n' is 10 for UNIX
208 // NOTE1: '\n' symbol is not used here,
209 // to provide possibility of loading
219 if ( *cur
== 13 ) --cur
;
221 while( cur
>= _gSrcStart
&&
226 ++cur
; // move to the first character in the line
229 static inline void skip_comments( char*& cur
)
231 ++cur
; // skip '/' token
233 if ( *cur
!= '/' && *cur
!= '*' ) return;
235 // first, store position of the comment into the queue
236 // (which further will be attached to the next context
239 if ( cur
-1 != _gLastSuppresedComment
)
241 if ( _gCQSize
== MAX_CQ_ENTRIES
)
243 size_t i
= MAX_CQ_ENTRIES
-1;
247 _gCommentsQueue
[i
-1] = _gCommentsQueue
[i
];
254 _gCommentsQueue
[_gCQSize
++] = cur
-1;
257 // if signle-line comment, skip it now
267 // check for multiline comment (handle nested multiline comments!)
275 // TBD:: check eof cond.
277 // detect and remove vertical columns of '*''s
279 while ( *cur
!= '/' && cur
< _gSrcEnd
)
285 if ( *(cur
+1) != '/' )
295 case 13 : line_len
= 0; break;
296 case 10 : { line_len
= 0; ++_gLineNo
; } break;
298 default : ++line_len
;
304 if ( cur
>= _gSrcEnd
) return;
308 if ( *(cur
-2) == '*' )
326 static inline void clear_commets_queue()
331 static inline void skip_quoted_string( char*& cur
)
333 ++cur
; // skip first quote '"'
335 // check if quote wasn't prefixed
336 if ( *(cur
-2) == '\\' )
341 while ( *cur
!= '"' && cur
< _gSrcEnd
)
343 if ( *cur
== 10 ) ++_gLineNo
;
347 if ( cur
>= _gSrcEnd
) return;
349 ++cur
; // skip the last quote
351 // check if it wasn't prefixed
353 if ( *(cur
-2) != '\\' )
359 // skips subsequent white space and comments
360 // (return false if the end of source code reached)
362 static inline bool get_next_token( char*& cur
)
364 for( ; cur
< _gSrcEnd
; ++cur
)
372 case 10 : { ++_gLineNo
;continue; }
374 case '/' : skip_comments( cur
);
384 if ( cur
>= _gSrcEnd
)
391 static inline void skip_preprocessor_dir( char*& cur
)
397 if ( *(cur
-1) != '\\' )
400 if ( cur
< _gSrcEnd
)
408 static void skip_token( char*& cur
)
412 skip_quoted_string( cur
);
426 // special case of "!=", "<=", ... 2 character composite tokens
440 ++cur
; // leading character is always skipped
442 for( ; cur
< _gSrcEnd
; ++cur
)
455 // FIXME:: QUICK-HACK:: to treat scope resolution
456 // tokens are a part of the string - e.g. SomeSpace::SubName would
459 case ':' : if ( *(cur
+1) == ':' )
478 static inline size_t get_token_len( char* tok
)
484 return size_t( tok
- start
);
487 // returns true, if given tokens are equel
489 static inline bool cmp_tokens( char* tok1
, char* tok2
)
491 // NOTE:: the case one token includes
492 // other in it's entirely is not handled
494 size_t len
= get_token_len( tok1
);
496 // assuming that tokens are non-zero length
500 if ( *(tok1
++) != *(tok2
++) )
510 static inline bool cmp_tokens_fast( char* tok1
, char* tok2
, size_t len
)
514 if ( *(tok1
++) != *(tok2
++) )
522 static inline void skip_tempalate_statement( char*& cur
)
526 // go one level deeper
527 while( *cur
!= '<' && cur
< _gSrcEnd
)
529 if (*cur
== 10 ) ++_gLineNo
;
533 // FIXME:: template should be checked statement for
534 // comments inside of it
543 ++cur
; // skip '<' or '>' token
548 while( *cur
!= '<' && *cur
!= '>' && cur
< _gSrcEnd
)
550 if (*cur
== 10 ) ++_gLineNo
;
557 static inline void skip_statement( char*& cur
)
559 for( ; cur
< _gSrcEnd
; ++cur
)
563 case ';' : ++cur
; // skip statement-terminator token
566 case '"' : skip_quoted_string(cur
);
570 case 10 : ++_gLineNo
;
573 case '/' : skip_comments( cur
);
580 // "reversed" versions of skip_token() and get_next_token()
582 static inline void skip_token_back( char*& cur
)
584 // FIXME:: now, when moving backwards, neither strings nor
585 // comment blocks are checked
587 --cur
; // skip to the trailing character
596 for( ; cur
< _gSrcEnd
; --cur
)
613 ++cur
; // get to the leading character of the token
616 static inline void skip_next_token_back( char*& cur
)
618 --cur
; // skip leading character of the current token
629 for( ; cur
< _gSrcEnd
; --cur
)
646 ++cur
; // position after the trailing charcter of the prev token
649 static string
get_token_str( char* cur
)
651 return string( cur
, get_token_len( cur
) );
654 // skips token or whole expression which may have
655 // nested expressions between '(' ')' brackets.
657 // Upon return, the cursor points to the terminating bracket ')',
659 // Return value is the size of the block
661 static size_t skip_block( char*& cur
)
663 size_t level
= 0; // nesting level
667 // NOTE:: assumed that block not necessarely starts
668 // with bracket rightaway
679 char* savedPos
= cur
;
681 store_line_no( tmpLnNo
);
683 get_next_token( cur
);
685 if ( cur
>= _gSrcEnd
) return 0;
697 restore_line_no( tmpLnNo
);
699 return size_t(cur
-start
);
708 // QUICK-HACK::to easily handle function prototypes ,
709 // it works, besause theoretically there should
710 // be no cast-expressions in non-implementation
711 // scope (e.g. "time( (long*)(ptr+1) )" should not
712 // appear in the declarations, thus it is most likelly
713 // for the ")(" fragment to be within a function
714 // prototype in the declarations scope
722 else return size_t(cur
-start
);
730 restore_line_no( tmpLnNo
);
732 return size_t(cur
-start
);
739 // returns 0, if end of source reached
740 static inline bool skip_imp_block( char*& cur
)
742 while( *cur
!= '{' && cur
< _gSrcEnd
)
745 if ( !get_next_token( cur
) ) return FALSE
;
748 while( *cur
!= '}' && cur
< _gSrcEnd
)
751 if ( !get_next_token( cur
) ) return FALSE
;
759 static bool is_class_token( char*& cur
)
761 // FIXME:: the below mess should be cleaned in it's entirely
764 if ( *(cur
+1) == 'n' )
766 return cmp_tokens_fast( cur
, "interface", 9 );
769 if ( *(cur
+1) == 'l' )
771 return cmp_tokens_fast( cur
, "class", 5 );
774 if ( *(cur
+1) == 't' )
776 return cmp_tokens_fast( cur
, "struct", 6 );
779 if ( *(cur
+1) == 'n' )
781 return cmp_tokens_fast( cur
, "union", 5 );
786 inline static bool is_forward_decl( char* cur
)
792 case ':' : return FALSE
;
793 case '{' : return FALSE
;
794 case '(' : return FALSE
;
796 case ';' : return TRUE
;
803 } while (cur
< _gSrcEnd
); // prevent running out of bounds
808 inline static bool is_function( char* cur
, bool& isAMacro
)
813 store_line_no( tmpLnNo
);
815 // NOTE:: comments and quoted strings are not checked here
817 // first,check for "single-line hanginging macros" like:
825 get_next_token( cur
);
830 restore_line_no( tmpLnNo
);
835 // it's not a macro, go to the begining of arg. list
839 // if bracket found, it's a function or a begining
843 restore_line_no( tmpLnNo
);
847 // end of statement found without any brackets in it
848 // - it cannot be a function
852 restore_line_no( tmpLnNo
);
858 } while( cur
< _gSrcEnd
);
861 restore_line_no( tmpLnNo
);
866 // upon return the cursor is positioned after the
867 // terminating curly brace
869 static inline void skip_scope_block( char*& cur
)
873 for( ; cur
< _gSrcEnd
; ++cur
)
877 case '/' : skip_comments( cur
);
880 case '"' : skip_quoted_string( cur
);
890 ++cur
; // skip final closing curly brace
894 case 10 : ++_gLineNo
; continue;
900 // moves tokens like '*' '**', '***', '&' from the name
903 static void arrange_indirection_tokens_between( string
& type
,
906 // TBD:: FIXME:: return value of operators !
908 while ( identifier
[0] == '*' ||
912 type
+= identifier
[0];
913 identifier
.erase(0,1);
915 if ( !identifier
.length() ) return;
920 // the only function where multi-lang keyword map is accessed
922 static bool is_keyword( char* cur
)
924 size_t len
= get_token_len( cur
);
926 // put a terminating zero after the given token
927 char tmp
= *(cur
+ len
);
930 KeywordMapT::iterator i
;
932 i
= __gMultiLangMap
.find( cur
);
934 // restore original character suppresed by terminating zero
937 return i
== __gMultiLangMap
.end() ? false : true;
940 static inline void get_string_between( char* start
, char* end
,
950 static char* set_comment_text( string
& text
, char* start
)
954 // to avoid poluting the queue with this comment
955 _gLastSuppresedComment
= start
;
957 skip_comments( end
);
959 if ( *(end
-1) == '/' )
964 // skip multiple leading '/''s or '*''s
965 while( *start
== '/' && start
< end
) ++start
;
966 while( *start
== '*' && start
< end
) ++start
;
968 get_string_between( start
, end
, &text
);
973 /***** Implementation for class CJSourceParser *****/
975 CJSourceParser::CJSourceParser( bool collectCommnets
, bool collectMacros
)
979 mCommentsOn( collectCommnets
),
980 mMacrosOn ( collectMacros
)
985 spFile
* CJSourceParser::Parse( char* start
, char* end
)
987 // set up state variables
988 mCurVis
= SP_VIS_PRIVATE
;
990 spFile
* pTopCtx
= new spFile();
1002 _gSrcEnd
= mpEnd
; // let all the C-functions "smell" the end of file
1007 clear_commets_queue();
1009 // main parsing loop
1013 if ( !get_next_token( cur
) )
1014 // end of source reached
1017 if ( memcmp( cur
, "ScriptSection( const string&",
1018 strlen( "ScriptSection( const string&" )
1030 AddMacroNode( cur
);
1061 // 'const' is a part of the return type, not a keyword here
1062 if ( strncmp(cur
, "const", 5) != 0 && is_keyword( cur
) )
1064 // parses, token, if token identifies
1065 // the container context (e.g. class/namespace)
1066 // the corresponding context object is created
1067 // and set as current context
1069 ParseKeyword( cur
);
1073 if ( *cur
>= '0' && *cur
<= '9' )
1081 if ( mCurCtxType
!= SP_CTX_CLASS
)
1083 // FOR NOW:: disable the below assertion
1085 // DBG:: unexpected closing-bracket found
1088 skip_token( cur
); // just skip it
1092 if ( mpCurCtx
->GetType() == SP_CTX_CLASS
)
1094 int curOfs
= ( (cur
+1) - _gSrcStart
);
1096 mpCurCtx
->mContextLength
= ( curOfs
- mpCurCtx
->mSrcOffset
);
1101 // terminate operation/class/namespace context
1102 // TBD:: check if it's really this type of context
1104 wxASSERT( mpCurCtx
);
1105 mpCurCtx
= mpCurCtx
->GetOutterContext();
1106 wxASSERT( mpCurCtx
);
1108 if ( mNestingLevel
== 0 )
1111 mCurCtxType
= SP_CTX_FILE
;
1113 // not-nested class delclaration finished,
1114 // rest template flag in any case
1124 if ( is_function( cur
, isAMacro
) )
1132 char* savedPos
= cur
;
1135 store_line_no( tmpLnNo
);
1139 if ( !ParseNameAndRetVal( cur
, isAMacro
) )
1144 SkipFunction( cur
);
1149 if ( !ParseArguments( cur
) )
1151 // failure while parsing arguments,
1152 // remove enclosing operation context
1154 spContext
* pFailed
= mpCurCtx
;
1155 mpCurCtx
= mpCurCtx
->GetOutterContext();
1156 mpCurCtx
->RemoveChild( pFailed
);
1163 // otherwise, successfully close operation context:
1165 clear_commets_queue();
1167 SkipFunctionBody( cur
);
1169 mpCurCtx
= mpCurCtx
->GetOutterContext();
1172 wxASSERT( mpCurCtx
);
1176 else // otherwise it's declaration of a variable;
1178 // now, the cursor point to the end of statement (';' token)
1180 if ( mCurCtxType
!= SP_CTX_CLASS
)
1182 // non-class members are ignored
1184 skip_token( cur
); // skip the end of statement
1188 ParseMemberVar( cur
);
1194 void CJSourceParser::AttachComments( spContext
& ctx
, char* cur
)
1196 if ( !mCommentsOn
) return;
1198 MCommentListT
& lst
= ctx
.GetCommentList();
1200 char* prevComEnd
= 0;
1203 store_line_no( tmpLnNo
);
1205 // attach comments which were found before the given context
1207 for( int i
= 0; i
!= _gCQSize
; ++i
)
1209 spComment
* pComment
= new spComment();
1210 lst
.push_back( pComment
);
1212 // find the end of comment
1213 char* start
= _gCommentsQueue
[i
];
1215 pComment
->mIsMultiline
= ( *(start
+1) == '*' );
1217 // first comment in the queue and multiline
1218 // comments are always treated as a begining
1219 // of the new paragraph in the comment text
1223 pComment
->mStartsPar
= TRUE
;
1225 if ( pComment
->mIsMultiline
)
1227 pComment
->mStartsPar
= TRUE
;
1230 // find out wheather there is a new-line
1231 // between to adjecent comments
1234 char* prevLine
= start
;
1235 skip_to_prev_line(prevLine
);
1237 if ( prevLine
>= prevComEnd
)
1239 pComment
->mStartsPar
= TRUE
;
1241 pComment
->mStartsPar
= FALSE
;
1244 prevComEnd
= set_comment_text( pComment
->mText
, start
);
1248 // attach comments which are at the end of the line
1249 // of the given context (if any)
1251 if ( skip_to_next_comment_in_the_line( cur
) )
1253 spComment
* pComment
= new spComment();
1254 lst
.push_back( pComment
);
1256 set_comment_text( pComment
->mText
, cur
);
1258 pComment
->mStartsPar
= 1;
1259 pComment
->mIsMultiline
= ( *(cur
+1) == '*' );
1261 // mark this comment, so that it would not
1262 // get in the comments list of the next context
1263 _gLastSuppresedComment
= cur
;
1266 restore_line_no( tmpLnNo
);
1268 clear_commets_queue();
1271 void CJSourceParser::AddMacroNode( char*& cur
)
1275 int lineNo
= get_line_no();
1277 skip_preprocessor_dir( cur
);
1280 store_line_no( tmpLnNo
);
1282 if ( !mMacrosOn
) return;
1284 spPreprocessorLine
* pPL
= new spPreprocessorLine();
1285 pPL
->mSrcLineNo
= lineNo
;
1287 AttachComments( *pPL
, cur
);
1289 get_string_between( start
, cur
, &pPL
->mLine
);
1291 ++start
; // skip '#'
1292 get_next_token( start
);
1294 pPL
->mDefType
= SP_PREP_DEF_OTHER
;
1296 // if we found a definition or redefinition,
1297 // determine the type exactly and assign
1298 // a name to the context
1300 if ( *start
== 'd' )
1302 if ( cmp_tokens_fast( start
, "define", 6 ) )
1304 char* tok
= start
+6;
1306 get_next_token( tok
);
1308 pPL
->mName
= get_token_str( tok
);
1311 get_next_token( tok
);
1315 pPL
->mDefType
= SP_PREP_DEF_DEFINE_SYMBOL
;
1317 pPL
->mDefType
= SP_PREP_DEF_REDEFINE_SYMBOL
;
1320 else if ( *start
== 'i' )
1322 if ( cmp_tokens_fast( start
, "include", 7 ) )
1324 pPL
->mDefType
= SP_PREP_DEF_INCLUDE_FILE
;
1326 else if ( *++start
== 'f' )
1328 // either "#if" or "#ifdef"
1331 get_next_token( cur
);
1333 string condition
= get_token_str( cur
);
1335 // currently, everything except '0' is true
1336 if ( condition
== "0" ) {
1337 // skip until the following else or enif
1338 while ( cur
< _gSrcEnd
) {
1342 get_next_token( cur
);
1343 if ( *cur
++ == '#' && *cur
== 'e' )
1348 // TODO parse the condition...
1351 else if ( cmp_tokens_fast( start
, "else", 4 ) )
1353 // skip until "#endif"
1354 while ( cur
< _gSrcEnd
) {
1358 get_next_token( cur
);
1359 if ( *cur
++ == '#' && cmp_tokens_fast( cur
, "endif", 5 ) )
1364 mpCurCtx
->AddMember( pPL
);
1369 restore_line_no( tmpLnNo
);
1371 clear_commets_queue();
1374 void CJSourceParser::ParseKeyword( char*& cur
)
1376 // analyze token, which identifies the begining of a new context
1378 if ( CheckVisibilty( cur
) )
1384 if ( is_class_token( cur
) )
1386 if ( is_forward_decl( cur
) )
1388 // forward declarations are ignored;
1393 if ( mNestingLevel
== 0 )
1395 // change context form global class context
1396 mCurCtxType
= SP_CTX_CLASS
;
1401 // add information about new class (name, inheritance, etc)
1402 AddClassNode( cur
);
1404 // the default visiblity for class members is 'private'
1405 mCurVis
= SP_VIS_PRIVATE
;
1410 size_t len
= get_token_len( cur
);
1412 if ( cmp_tokens_fast( cur
, "typedef", len
) )
1415 get_next_token(cur
);
1417 if ( cmp_tokens_fast( cur
, "struct", len
) ||
1418 cmp_tokens_fast( cur
, "union", len
) ||
1419 cmp_tokens_fast( cur
, "class", len
)
1422 if ( mNestingLevel
== 0 )
1424 // change context form global class context
1425 mCurCtxType
= SP_CTX_CLASS
;
1430 // add information about new class (name, inheritance, etc)
1431 AddClassNode( cur
);
1433 // the default visiblity for class members is 'private'
1434 mCurVis
= SP_VIS_PRIVATE
;
1438 // FOR NOW:: typedef struct, etc are also ignored
1439 //skip_scope_block( cur );
1442 if ( cmp_tokens_fast( cur
, "enum", len
) )
1448 AddTypeDefNode( cur
);
1453 if ( cmp_tokens_fast( cur
, "enum", len
) )
1459 if ( cmp_tokens_fast( cur
, "extern", len
) )
1461 // extern's are ignored (both extern "C" and extern vars)
1462 while ( *cur
!= '{' &&
1466 get_next_token( cur
);
1471 if ( cmp_tokens_fast( cur
, "enum", len
) )
1473 // enumeration blocks are ignored
1475 skip_scope_block( cur
);
1477 get_next_token( cur
);
1478 skip_token( cur
); // skip ';' token;
1482 if ( cmp_tokens_fast( cur
, "package", len
) )
1484 // packages are ignored
1485 skip_statement( cur
);
1489 if ( cmp_tokens_fast( cur
, "import", len
) )
1491 // import statements are ignored
1492 skip_statement( cur
);
1496 if ( cmp_tokens_fast( cur
, "virtual", len
) )
1498 // probably the virtual method is in front of us;
1504 if ( cmp_tokens_fast( cur
, "template", len
) )
1507 skip_tempalate_statement( cur
);
1511 if ( cmp_tokens_fast( cur
, "friend", len
) )
1513 skip_statement( cur
);
1517 // ingnore "unsigificant" tokens (i.e. which do not
1518 // affect the current parsing context)
1523 bool CJSourceParser::ParseNameAndRetVal( char*& cur
, bool& isAMacro
)
1527 // FOR NOW:: all functions in the global
1528 // scope are ignored
1530 int lineNo
= get_line_no();
1534 bool isVirtual
= false;
1535 while( *cur
!= '(' )
1537 if ( get_token_str( cur
) == "virtual" )
1541 if ( !get_next_token( cur
) ) return FALSE
;
1544 char* bracketPos
= cur
;
1545 char* savedPos
= cur
+ 1;
1548 store_line_no( tmpLnNo
);
1550 // skip gap between function name and start of paramters list
1551 while ( *(cur
-1) == ' ' )
1554 // check if it's not a macro, and let plugin handle it, if so
1558 skip_token_back( cur
);
1562 if ( mpPlugin
->CanUnderstandContext( tmp
, _gSrcEnd
, mpCurCtx
) )
1566 mpPlugin
->ParseContext( _gSrcStart
, cur
, _gSrcEnd
, mpCurCtx
);
1574 spOperation
* pOp
= new spOperation();
1576 pOp
->mSrcLineNo
= lineNo
;
1577 pOp
->mSrcOffset
= int( start
- _gSrcStart
);
1578 pOp
->mHeaderLength
= int( bracketPos
- start
);
1579 if ( mpCurCtx
->GetContextType() == SP_CTX_CLASS
)
1580 pOp
->mScope
= mpCurCtx
->mName
;
1582 mpCurCtx
->AddMember( pOp
);
1583 pOp
->mVisibility
= mCurVis
;
1584 pOp
->mIsVirtual
= isVirtual
;
1586 // add comments about operation
1587 AttachComments( *pOp
, cur
);
1589 // go backwards to method name
1590 skip_token_back( cur
);
1592 pOp
->mName
= get_token_str( cur
);
1594 // checker whether it's not an operator
1595 char chFirst
= *pOp
->mName
.c_str();
1596 if ( !isalpha(chFirst
) && chFirst
!= '_' && chFirst
!= '~' ) {
1598 skip_next_token_back( cur
);
1599 skip_token_back( cur
);
1601 string lastToken
= get_token_str( cur
);
1602 if ( lastToken
== "operator" ) {
1603 lastToken
+= pOp
->mName
;
1604 pOp
->mName
= lastToken
;
1607 // ok, it wasn't an operator after all
1611 else if ( pOp
->mName
== "operator" ) {
1613 get_next_token( cur
);
1614 string oper
= get_token_str( cur
);
1619 // go backwards to method return type
1620 skip_next_token_back( cur
);
1624 string rettype
= string( start
, size_t( cur
-start
) );
1625 rettype
.Replace("WXDLLEXPORT ", ""); // FIXME just for now...
1626 pOp
->mRetType
= rettype
;
1629 arrange_indirection_tokens_between( pOp
->mRetType
, pOp
->mName
);
1632 restore_line_no( tmpLnNo
);
1634 // now, enter operation context
1640 bool CJSourceParser::ParseArguments( char*& cur
)
1644 // now cursor position is right after the first opening bracket
1645 // of the function declaration
1647 char* blocks
[16]; // used exclusivelly for iterative "lean out"
1648 // of macros and misc. not-obviouse grammar
1649 // (dirty,, but we cannot do it very nice,
1650 // we're not preprocessor-free C/C++ code)
1655 size_t blocksSkipped
= 0;
1657 get_next_token( cur
);
1661 while( *cur
!= ')' && *cur
!= ',' )
1663 blocks
[blocksSkipped
] = cur
;
1670 blockSizes
[blocksSkipped
] = size_t(cur
-prev
);
1675 blockSizes
[blocksSkipped
] = skip_block( cur
);
1677 get_next_token( cur
);
1682 if ( blocksSkipped
== 1 )
1684 // check if the empty arg. list stressed with "void" inside
1685 if ( cmp_tokens_fast( blocks
[0] , "void", 4 ) )
1692 // FIXME:: TBD:: K&R-style function declarations!
1694 // if only one block enclosed, than it's probably
1695 // some macro, there should be at least two blocks,
1696 // one for argument type and another for it's identifier
1700 if ( blocksSkipped
== 0 )
1702 if ( *cur
== 10 ) ++_gLineNo
;
1705 break; // function without paramters
1708 // we should be in the operation context now
1709 spOperation
* pOp
= (spOperation
*)mpCurCtx
;
1711 spParameter
* pPar
= new spParameter();
1713 pOp
->AddMember( pPar
);
1714 // FOR NOW:: line number is not exact if argument list is mutiline
1715 pPar
->mSrcLineNo
= get_line_no();
1717 size_t nameBlock
= blocksSkipped
- 1;
1718 size_t typeBlock
= nameBlock
- 1;
1720 // check if default values present
1721 if ( *blocks
[typeBlock
] == '=' )
1723 // expressions like "int = 5" are ignored,
1724 // since name for paramters is required
1725 if ( blocksSkipped
== 3 )
1736 pPar
->mInitVal
= string( blocks
[nameBlock
], blockSizes
[nameBlock
] );
1738 nameBlock
= nameBlock
- 2; // skip '=' token and default value block
1739 typeBlock
= nameBlock
- 1;
1742 // attach comments about the parameter
1743 AttachComments( *pPar
, blocks
[nameBlock
] );
1745 // retrieve argument name
1746 pPar
->mName
= string( blocks
[nameBlock
], blockSizes
[nameBlock
] );
1748 // retreive argument type
1750 size_t len
= blockSizes
[ typeBlock
];
1751 len
= size_t ( (blocks
[ typeBlock
] + len
) - blocks
[ 0 ] );
1753 pPar
->mType
= string( blocks
[0], len
);
1755 arrange_indirection_tokens_between( pPar
->mType
, pPar
->mName
);
1763 ++cur
; // skip comma
1764 get_next_token(cur
);
1768 // skip possible whitespace between ')' and following "const"
1769 while ( isspace(*cur
) )
1772 // check if it was really a function not a macro,
1773 // if so, than it should be terminated with semicolon ';'
1774 // or opening implemenetaton bracket '{'
1779 store_line_no( tmpLnNo
);
1783 if ( *tok
== '{' || *tok
== ';' )
1785 restore_line_no(tmpLnNo
);
1789 // check for unexpected tokens
1790 if ( *tok
== '=' || *tok
== '0' )
1793 if ( !get_next_token(tok
) ) return FALSE
;
1797 if ( *tok
== '}' ) return FALSE
;
1799 // if initialization list found
1802 restore_line_no(tmpLnNo
);
1806 if ( cmp_tokens_fast( tok
, "const", 5 ) )
1808 ((spOperation
*)mpCurCtx
)->mIsConstant
= true;
1811 if ( !get_next_token(tok
) ) return FALSE
;
1815 if ( CheckVisibilty( tok
) ) return FALSE
;
1817 // if next context found
1818 if ( is_keyword( tok
) ) return FALSE
;
1821 if ( !get_next_token(tok
) ) return FALSE
;
1828 void CJSourceParser::ParseMemberVar( char*& cur
)
1830 MMemberListT
& members
= mpCurCtx
->GetMembers();
1832 bool firstMember
= 1;
1838 // jump to the end of statement
1839 // and start collecting same-type varibles
1840 // back-to-front towards the type identifier
1842 skip_statement( cur
);
1843 char* savedPos
= cur
;
1846 store_line_no( tmpLnNo
);
1848 --cur
; // rewind back to ';'
1852 spAttribute
* pAttr
= new spAttribute();
1853 // FOR NOW:: line not is not exact, if member declaration is multiline
1854 pAttr
->mSrcLineNo
= get_line_no();
1856 mpCurCtx
->AddMember( pAttr
);
1857 pAttr
->mVisibility
= mCurVis
;
1859 pAttr
->mIsConstant
= 0;
1864 first
= members
.size() - 1;;
1867 skip_token_back( cur
);
1869 // attach comments about the attribute
1870 AttachComments( *pAttr
, cur
);
1872 pAttr
->mName
= get_token_str( cur
);
1874 // guessing that this going to be variable type
1875 skip_next_token_back( cur
);
1876 skip_token_back( cur
);
1878 pAttr
->mType
= get_token_str( cur
);
1880 // if comma, than variable list continues
1881 // otherwise the variable type reached - stop
1885 // yes, we've mistaken, it was not a identifier,
1886 // but it's default value
1890 // skip default value and '=' symbol
1891 skip_next_token_back( cur
);
1892 skip_token_back( cur
);
1894 pAttr
->mName
= get_token_str( cur
);
1896 skip_next_token_back( cur
);
1897 skip_token_back( cur
);
1902 type
= get_token_str( cur
);
1910 // set up types for all collected (same-type) attributes;
1911 while ( first
!= members
.size() - 1 )
1913 spAttribute
* pAttr
= members
[first
++]->CastToAttribute();
1917 if ( !pAttr
->mType
)
1918 pAttr
->mType
= type
;
1919 pAttr
->mVisibility
= mCurVis
;
1921 if ( !!pAttr
->mName
)
1922 arrange_indirection_tokens_between( pAttr
->mType
, pAttr
->mName
);
1926 restore_line_no( tmpLnNo
);
1928 clear_commets_queue();
1933 void CJSourceParser::SkipFunction( char*& cur
)
1935 while ( *cur
!= '(' && cur
< _gSrcEnd
)
1937 if (*cur
== 10 ) ++_gLineNo
;
1941 skip_next_token_back( cur
); // go back and skip function identifier
1942 skip_token_back( cur
); // go back and skip return type
1944 skip_block( cur
); // now, go ahead and skip whole declaration
1946 SkipFunctionBody( cur
);
1950 void CJSourceParser::SkipFunctionBody( char*& cur
)
1952 // FIXME:: check for comments and quoted stirngs here
1954 bool hasDefinition
= FALSE
;
1956 while( *cur
!= '{' && *cur
!= ';' )
1958 if (*cur
== 10 ) ++_gLineNo
;
1968 hasDefinition
= TRUE
;
1970 skip_scope_block( cur
); // skip the whole imp.
1973 if ( mpCurCtx
->GetType() == SP_CTX_OPERATION
)
1975 spOperation
& op
= *((spOperation
*)mpCurCtx
);
1977 int curOfs
= int ( cur
- _gSrcStart
);
1979 op
.mContextLength
= curOfs
- mpCurCtx
->mSrcOffset
;
1981 op
.mHasDefinition
= hasDefinition
;
1983 // separate scope resolution token from the name of operation
1985 for( size_t i
= 0; i
!= op
.mName
.length(); ++i
)
1987 if ( op
.mName
[i
] == ':' && op
.mName
[i
+1] == ':' )
1989 string
unscoped( op
.mName
, i
+2, op
.mName
.length() - ( i
+ 2 ) );
1991 op
.mScope
= string( op
.mName
, 0, i
);
1993 op
.mName
= unscoped
;
2001 bool CJSourceParser::CheckVisibilty( char*& cur
)
2003 size_t len
= get_token_len( cur
);
2005 if ( cmp_tokens_fast( cur
, "public:", len
) )
2007 mCurVis
= SP_VIS_PUBLIC
;
2011 if ( cmp_tokens_fast( cur
, "protected:", len
) )
2013 mCurVis
= SP_VIS_PROTECTED
;
2017 if ( cmp_tokens_fast( cur
, "private:", len
) )
2019 mCurVis
= SP_VIS_PRIVATE
;
2026 void CJSourceParser::AddClassNode( char*& cur
)
2028 char* ctxStart
= cur
;
2030 string classkeyword
= get_token_str( cur
);
2032 skip_token( cur
); // skip 'class' keyword
2033 if ( !get_next_token( cur
) ) return;
2039 get_next_token( cur
);
2042 // by default all class members are private
2043 mCurVis
= SP_VIS_PRIVATE
;
2045 spClass
* pClass
= new spClass();
2046 if ( classkeyword
== "class" )
2047 pClass
->mClassSubType
= SP_CLTYPE_CLASS
;
2048 else if ( classkeyword
== "struct" ) {
2049 pClass
->mClassSubType
= SP_CLTYPE_STRUCTURE
;
2051 mCurVis
= SP_VIS_PUBLIC
;
2053 else if ( classkeyword
== "union" ) {
2054 pClass
->mClassSubType
= SP_CLTYPE_UNION
;
2056 mCurVis
= SP_VIS_PUBLIC
;
2058 else if ( classkeyword
== "interface" )
2059 pClass
->mClassSubType
= SP_CLTYPE_INTERFACE
;
2061 pClass
->mClassSubType
= SP_CLTYPE_INVALID
;
2063 wxFAIL_MSG("unknown class keyword");
2066 mpCurCtx
->AddMember( pClass
);
2068 // attach comments about the class
2069 AttachComments( *pClass
, cur
);
2071 pClass
->mSrcLineNo
= get_line_no();
2073 pClass
->mSrcOffset
= int( ctxStart
- _gSrcStart
);
2075 char* nameTok
= cur
;
2076 pClass
->mName
= get_token_str( cur
);
2085 if ( !get_next_token( cur
) ) return;
2094 store_line_no( tmpLn
);
2096 skip_next_token_back( tok
);
2097 skip_token_back( tok
);
2099 restore_line_no( tmpLn
);
2101 // class name should precend ':' colon, thus
2102 // the one which was captured before was
2103 // proablty something else (like __dllexport MyClass : ... )
2105 if ( nameTok
!= tok
)
2107 pClass
->mName
= get_token_str( tok
);
2118 size_t len
= get_token_len( cur
);
2120 // skip neglectable C++ modifieres
2121 if ( cmp_tokens_fast( cur
, "public", len
) )
2124 if ( cmp_tokens_fast( cur
, "protected", len
) )
2127 if ( cmp_tokens_fast( cur
, "private", len
) )
2130 if ( cmp_tokens_fast( cur
, "virtual", len
) )
2133 // skip neglectable JAVA modifieres
2135 if ( cmp_tokens_fast( cur
, "extends", len
) )
2141 if ( cmp_tokens_fast( cur
, "implements", len
) )
2147 // all we need to know is superclass or interface
2151 store_line_no( tmpLn
);
2154 get_next_token(tok
);
2156 restore_line_no( tmpLn
);
2158 if ( *tok
!= ':' && *cur
!= ':' )
2160 pClass
->mSuperClassNames
.push_back( string( cur
, len
) );
2167 store_line_no( tmpLn
);
2169 while ( pClass
->mSuperClassNames
.size() )
2171 pClass
->mSuperClassNames
.erase( &pClass
->mSuperClassNames
[0] );
2175 // some non-obviouse token was following "class" keyword -
2176 // we've confused it with class name - thus now we're reverting this mistake
2178 skip_next_token_back( tok
);
2179 skip_token_back( tok
);
2181 pClass
->mName
= get_token_str( tok
);
2183 restore_line_no( tmpLn
);
2187 ++cur
; // skip opening curly brace
2189 pClass
->mHeaderLength
= ( cur
- ctxStart
);
2191 // now, enter the class context
2194 clear_commets_queue();
2197 void CJSourceParser::AddEnumNode( char*& cur
)
2199 // now the cursor is at "enum" keyword
2202 spEnumeration
* pEnum
= new spEnumeration();
2203 mpCurCtx
->AddMember( pEnum
);
2205 pEnum
->mSrcLineNo
= get_line_no();
2208 AttachComments( *pEnum
, cur
);
2211 if ( !get_next_token( cur
) ) return;
2213 // check if enumeration has got it's identifier
2216 pEnum
->mName
= get_token_str( cur
);
2219 if ( !skip_imp_block( cur
) ) return;
2221 get_string_between( start
, cur
, &pEnum
->mEnumContent
);
2223 if ( get_next_token(cur
) )
2225 // check if the identifier if after the {...} block
2228 pEnum
->mName
= get_token_str( cur
);
2231 clear_commets_queue();
2234 void CJSourceParser::AddTypeDefNode( char*& cur
)
2236 // now the cursor at the token next to "typedef" keyword
2238 if ( !get_next_token(cur
) ) return;
2242 spTypeDef
* pTDef
= new spTypeDef();
2243 mpCurCtx
->AddMember( pTDef
);
2245 pTDef
->mSrcLineNo
= get_line_no();
2247 AttachComments( *pTDef
, cur
);
2249 skip_statement( cur
);
2252 store_line_no( tmpLnNo
);
2255 skip_next_token_back( tok
);
2257 char* nameEnd
= tok
;
2259 skip_token_back( tok
);
2261 char* nameStart
= tok
;
2263 skip_next_token_back( tok
);
2265 char* typeEnd
= tok
;
2267 // check if it's function prototype
2268 if ( *nameStart
== ')' )
2270 typeEnd
= nameStart
+1;
2272 // skip argument list
2273 while ( *nameStart
!= '(' ) --nameStart
;
2275 // skip to function type definition
2276 while ( *nameStart
!= ')' ) --nameStart
;
2278 skip_next_token_back( nameStart
);
2280 nameEnd
= nameStart
;
2282 skip_token_back( nameStart
);
2284 if ( *nameStart
== '*' ) ++nameStart
;
2287 get_string_between( start
, typeEnd
, &pTDef
->mOriginalType
);
2289 get_string_between( nameStart
, nameEnd
, &pTDef
->mName
);
2291 clear_commets_queue();
2293 restore_line_no( tmpLnNo
);