home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / wxos2233.zip / wxOS2-2_3_3.zip / wxWindows-2.3.3 / utils / HelpGen / src / cjparser.cpp next >
C/C++ Source or Header  |  2001-11-19  |  53KB  |  2,294 lines

  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,v 1.6 2001/11/18 12:25:11 GD Exp $
  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.  
  385.         return FALSE;
  386.     else
  387.         return TRUE;
  388. }
  389.  
  390. static inline void skip_preprocessor_dir( char*& cur )
  391. {
  392.     do
  393.     {
  394.         skip_to_eol(cur);
  395.  
  396.         if ( *(cur-1) != '\\' )
  397.             break;
  398.  
  399.         if ( cur < _gSrcEnd )
  400.             skip_eol( cur );
  401.         else
  402.             break;
  403.  
  404.     } while(1);
  405. }
  406.  
  407. static void skip_token( char*& cur )
  408. {
  409.     if ( *cur == '"' )
  410.     {
  411.         skip_quoted_string( cur );
  412.         return;
  413.     }
  414.  
  415.     if ( *cur == ',' ||
  416.          *cur == ';' ||
  417.          *cur == ')' ||
  418.          *cur == '('
  419.        )
  420.     {
  421.         ++cur;
  422.         return;
  423.     }
  424.  
  425.     // special case of "!=", "<=", ... 2 character composite tokens
  426.     if ( *cur == '<' ||
  427.          *cur == '>' ||
  428.          *cur == '=' ||
  429.          *cur == '!'
  430.        )
  431.     {
  432.         cur++;
  433.         if ( *cur == '=' )
  434.             cur++;
  435.  
  436.         return;
  437.     }
  438.  
  439.     ++cur; // leading character is always skipped
  440.  
  441.     for( ; cur < _gSrcEnd ; ++cur )
  442.     {
  443.         switch ( *cur )
  444.         {
  445.             case ' ' : break;
  446.             case '\t': break;
  447.             case 13  : break;
  448.             case 10  : break;
  449.             case ',' : break;
  450.             case ';' : break;
  451.             case '<' : break;
  452.             case '>' : break;
  453.  
  454.             // FIXME:: QUICK-HACK:: to treat scope resolution
  455.             //         tokens are a part of the string - e.g. SomeSpace::SubName would
  456.             //         become one token
  457.  
  458.             case ':' : if ( *(cur+1) == ':' )
  459.                        {
  460.                            ++cur;
  461.                            continue;
  462.                        }
  463.  
  464.                        break;
  465.             case '=' : break;
  466.             case '(' : break;
  467.             case ')' : break;
  468.             case '{' : break;
  469.             case '}' : break;
  470.  
  471.             default : continue;
  472.         };
  473.         break;
  474.     }
  475. }
  476.  
  477. static inline size_t get_token_len( char* tok )
  478. {
  479.     char* start = tok;
  480.  
  481.     skip_token( tok );
  482.  
  483.     return size_t( tok - start );
  484. }
  485.  
  486. // returns true, if given tokens are equel
  487.  
  488. static inline bool cmp_tokens( char* tok1, char* tok2 )
  489. {
  490.     // NOTE:: the case one token includes
  491.     //        other in it's entirely is not handled
  492.  
  493.     size_t len = get_token_len( tok1 );
  494.  
  495.     // assuming that tokens are non-zero length
  496.  
  497.     do
  498.     {
  499.         if ( *(tok1++) != *(tok2++) )
  500.             return FALSE;
  501.  
  502.         --len;
  503.  
  504.     } while ( --len );
  505.  
  506.     return TRUE;
  507. }
  508.  
  509. static inline bool cmp_tokens_fast( char* tok1, char* tok2, size_t len )
  510. {
  511.     do
  512.     {
  513.         if ( *(tok1++) != *(tok2++) )
  514.             return FALSE;
  515.  
  516.     } while ( --len );
  517.  
  518.     return TRUE;
  519. }
  520.  
  521. static inline void skip_tempalate_statement( char*& cur )
  522. {
  523.     size_t level = 0;
  524.  
  525.     // go one level deeper
  526.     while( *cur != '<' && cur < _gSrcEnd )
  527.     {
  528.         if (*cur == 10 ) ++_gLineNo;
  529.         ++cur;
  530.     }
  531.  
  532.     // FIXME:: template should be checked statement for
  533.     //         comments inside of it
  534.  
  535.     do
  536.     {
  537.         if ( *cur == '<' )
  538.             ++level;
  539.         else
  540.             --level;
  541.  
  542.         ++cur; // skip '<' or '>' token
  543.  
  544.         if ( level == 0 )
  545.             return;
  546.  
  547.         while( *cur != '<' && *cur != '>' && cur < _gSrcEnd )
  548.         {
  549.             if (*cur == 10 ) ++_gLineNo;
  550.             ++cur;
  551.         }
  552.  
  553.     } while (1);
  554. }
  555.  
  556. static inline void skip_statement( char*& cur )
  557. {
  558.     for( ; cur < _gSrcEnd; ++cur )
  559.  
  560.         switch (*cur)
  561.         {
  562.             case  ';' : ++cur; // skip statement-terminator token
  563.                         return;
  564.  
  565.             case  '"' : skip_quoted_string(cur);
  566.                         --cur;
  567.                         continue;
  568.  
  569.             case  10  : ++_gLineNo;
  570.  
  571.                         continue;
  572.             case  '/' : skip_comments( cur );
  573.                         --cur;
  574.                         continue;
  575.             default : continue;
  576.         }
  577. }
  578.  
  579. // "reversed" versions of skip_token() and get_next_token()
  580.  
  581. static inline void skip_token_back( char*& cur )
  582. {
  583.     // FIXME:: now, when moving backwards, neither strings nor
  584.     //         comment blocks are checked
  585.  
  586.     --cur; // skip to the trailing character
  587.  
  588.     if ( *cur == ',' ||
  589.          *cur == ')' ||
  590.          *cur == '('
  591.        )
  592.        return;
  593.  
  594.  
  595.     for( ; cur < _gSrcEnd ; --cur )
  596.     {
  597.         switch ( *cur )
  598.         {
  599.             case ' ' : break;
  600.             case '\t': break;
  601.             case 13  : break;
  602.             case 10  : break;
  603.             case ',' : break;
  604.             case '(' : break;
  605.  
  606.             default : continue;
  607.         };
  608.  
  609.         break;
  610.     }
  611.  
  612.     ++cur; // get to the leading character of the token
  613. }
  614.  
  615. static inline void skip_next_token_back( char*& cur )
  616. {
  617.     --cur; // skip leading character of the current token
  618.  
  619.     if ( *cur == ',' ||
  620.          *cur == ')' ||
  621.          *cur == '('
  622.        )
  623.     {
  624.        ++cur;
  625.        return;
  626.     }
  627.  
  628.     for( ; cur < _gSrcEnd; --cur )
  629.     {
  630.         switch ( *cur )
  631.         {
  632.             case ' ' : continue;
  633.             case '\t': continue;
  634.             case 13  : continue;
  635.             case 10  : continue;
  636.             case ',' : continue;
  637.             case '(' : continue;
  638.  
  639.             default : break;
  640.         };
  641.  
  642.         break;
  643.     }
  644.  
  645.     ++cur; // position after the trailing charcter of the prev token
  646. }
  647.  
  648. static string get_token_str( char* cur )
  649. {
  650.     return string( cur, get_token_len( cur ) );
  651. }
  652.  
  653. // skips token or whole expression which may have
  654. // nested  expressions between '(' ')' brackets.
  655. //
  656. // Upon return, the cursor points to the terminating bracket ')',
  657. //
  658. // Return value is the size of the block
  659.  
  660. static size_t skip_block( char*& cur )
  661. {
  662.     size_t level = 0; // nesting level
  663.  
  664.     char* start = cur;
  665.  
  666.     // NOTE:: assumed that block not necessarely starts
  667.     //        with bracket rightaway
  668.  
  669.     if ( *cur == '(' )
  670.     {
  671.         ++level;
  672.     }
  673.  
  674.     do
  675.     {
  676.         skip_token( cur );
  677.  
  678.         char* savedPos = cur;
  679.         int tmpLnNo;
  680.         store_line_no( tmpLnNo );
  681.  
  682.         get_next_token( cur );
  683.  
  684.         if ( cur >= _gSrcEnd ) return 0;
  685.  
  686.         if ( *cur == '(' )
  687.         {
  688.             ++level;
  689.         }
  690.         else
  691.         if ( *cur == ')' )
  692.         {
  693.             if ( level == 0 )
  694.             {
  695.                 cur = savedPos;
  696.                 restore_line_no( tmpLnNo );
  697.  
  698.                 return size_t(cur-start);
  699.             }
  700.  
  701.             --level;
  702.  
  703.             if ( level == 0 )
  704.             {
  705.                 ++cur;
  706.  
  707.                 // QUICK-HACK::to easily handle function prototypes ,
  708.                 // it works, besause theoretically there should
  709.                 // be no cast-expressions in non-implementation
  710.                 // scope (e.g. "time( (long*)(ptr+1) )" should not
  711.                 // appear in the declarations, thus it is most likelly
  712.                 // for the ")(" fragment to be within a function
  713.                 // prototype in the declarations scope
  714.  
  715.                 if ( *cur == '(' )
  716.                 {
  717.                     ++level;
  718.                     continue;
  719.                 }
  720.  
  721.                 else return size_t(cur-start);
  722.             }
  723.         }
  724.         else
  725.         {
  726.             if ( level == 0 )
  727.             {
  728.                 cur = savedPos;
  729.                 restore_line_no( tmpLnNo );
  730.  
  731.                 return size_t(cur-start);
  732.             }
  733.         }
  734.  
  735.     } while(1);
  736. }
  737.  
  738. // returns 0, if end of source reached
  739. static inline bool skip_imp_block( char*& cur )
  740. {
  741.     while( *cur != '{' && cur < _gSrcEnd )
  742.     {
  743.         skip_token( cur );
  744.         if ( !get_next_token( cur ) ) return FALSE;
  745.     }
  746.  
  747.     while( *cur != '}' && cur < _gSrcEnd )
  748.     {
  749.         skip_token( cur );
  750.         if ( !get_next_token( cur ) ) return FALSE;
  751.     }
  752.  
  753.     ++cur;
  754.  
  755.     return TRUE;
  756. }
  757.  
  758. static bool is_class_token( char*& cur )
  759. {
  760.     // FIXME:: the below mess should be cleaned in it's entirely
  761.  
  762.     if ( *cur == 'i' )
  763.         if ( *(cur+1) == 'n' )
  764.  
  765.             return cmp_tokens_fast( cur, "interface", 9 );
  766.  
  767.     if ( *cur == 'c' )
  768.         if ( *(cur+1) == 'l' )
  769.  
  770.             return cmp_tokens_fast( cur, "class", 5 );
  771.  
  772.     if ( *cur == 's' )
  773.         if ( *(cur+1) == 't' )
  774.  
  775.             return cmp_tokens_fast( cur, "struct", 6 );
  776.  
  777.     if ( *cur == 'u' )
  778.         if ( *(cur+1) == 'n' )
  779.  
  780.             return cmp_tokens_fast( cur, "union", 5 );
  781.  
  782.     return FALSE;
  783. }
  784.  
  785. inline static bool is_forward_decl( char* cur )
  786. {
  787.     do
  788.     {
  789.         switch( *cur )
  790.         {
  791.             case ':' : return FALSE;
  792.             case '{' : return FALSE;
  793.             case '(' : return FALSE;
  794.  
  795.             case ';' : return TRUE;
  796.  
  797.             default : break;
  798.         };
  799.  
  800.         ++cur;
  801.  
  802.     } while (cur < _gSrcEnd); // prevent running out of bounds
  803.  
  804.     return FALSE;
  805. }
  806.  
  807. inline static bool is_function( char* cur, bool& isAMacro )
  808. {
  809.     isAMacro = FALSE;
  810.  
  811.     int tmpLnNo;
  812.     store_line_no( tmpLnNo );
  813.  
  814.     // NOTE:: comments and quoted strings are not checked here
  815.  
  816.     // first,check for "single-line hanginging macros" like:
  817.     // ___UNICODE
  818.     //
  819.  
  820.     char* eol = cur;
  821.     skip_to_eol( eol );
  822.  
  823.     skip_token( cur );
  824.     get_next_token( cur );
  825.  
  826.     if ( cur > eol )
  827.     {
  828.         isAMacro = TRUE;
  829.         restore_line_no( tmpLnNo );
  830.  
  831.         return TRUE;
  832.     }
  833.  
  834.     // it's not a macro, go to the begining of arg. list
  835.  
  836.     do
  837.     {
  838.         // if bracket found, it's a function or a begining
  839.         // of some macro
  840.         if ( *cur == '(' )
  841.         {
  842.             restore_line_no( tmpLnNo );
  843.             return TRUE;
  844.         }
  845.  
  846.         // end of statement found without any brackets in it
  847.         // - it cannot be a function
  848.  
  849.         if ( *cur == ';' )
  850.         {
  851.             restore_line_no( tmpLnNo );
  852.             return FALSE;
  853.         }
  854.  
  855.         ++cur;
  856.  
  857.     } while( cur < _gSrcEnd);
  858.  
  859.     isAMacro = 1;
  860.     restore_line_no( tmpLnNo );
  861.  
  862.     return FALSE;
  863. }
  864.  
  865. // upon return the cursor is positioned after the
  866. // terminating curly brace
  867.  
  868. static inline void skip_scope_block( char*& cur )
  869. {
  870.     size_t level = 0;
  871.  
  872.     for( ; cur < _gSrcEnd ; ++cur )
  873.  
  874.         switch( *cur )
  875.         {
  876.             case '/' : skip_comments( cur );
  877.                        --cur;
  878.                        continue;
  879.             case '"' : skip_quoted_string( cur );
  880.                        --cur;
  881.                        continue;
  882.  
  883.             case '{' : ++level;
  884.                        continue;
  885.  
  886.             case '}'  :--level;
  887.                        if ( level == 0 )
  888.                        {
  889.                            ++cur; // skip final closing curly brace
  890.                            return;
  891.                        }
  892.  
  893.             case 10 : ++_gLineNo; continue;
  894.  
  895.             default : continue;
  896.         };
  897. }
  898.  
  899. // moves tokens like '*' '**', '***', '&' from the name
  900. // to the type
  901.  
  902. static void arrange_indirection_tokens_between( string& type,
  903.                                                 string& identifier )
  904. {
  905.     // TBD:: FIXME:: return value of operators !
  906.  
  907.     while ( identifier[0] == '*' ||
  908.             identifier[0] == '&'
  909.           )
  910.     {
  911.         type += identifier[0];
  912.         identifier.erase(0,1);
  913.  
  914.         if ( !identifier.length() ) return;
  915.     }
  916. }
  917.  
  918.  
  919. // the only function where multi-lang keyword map is accessed
  920.  
  921. static bool is_keyword( char* cur )
  922. {
  923.     size_t len = get_token_len( cur );
  924.  
  925.     // put a terminating zero after the given token
  926.     char tmp = *(cur + len);
  927.     *(cur+len) = '\0';
  928.  
  929.     KeywordMapT::iterator i;
  930.  
  931.     i = __gMultiLangMap.find( cur );
  932.  
  933.     // restore original character suppresed by terminating zero
  934.     *(cur + len) = tmp;
  935.  
  936.     return i == __gMultiLangMap.end() ? false : true;
  937. }
  938.  
  939. static inline void get_string_between( char* start, char* end,
  940.                                        string* pStr )
  941. {
  942.     char saved = *end;
  943.  
  944.     *end  = '\0';
  945.     *pStr = start;
  946.     *end  = saved;
  947. }
  948.  
  949. static char* set_comment_text( string& text, char* start )
  950. {
  951.     char* end = start;
  952.  
  953.     // to avoid poluting the queue with this comment
  954.     _gLastSuppresedComment = start;
  955.  
  956.     skip_comments( end );
  957.  
  958.     if ( *(end-1) == '/' )
  959.         end -= 2;
  960.  
  961.     start += 2;
  962.  
  963.     // skip multiple leading '/''s or '*''s
  964.     while( *start == '/' && start < end ) ++start;
  965.     while( *start == '*' && start < end ) ++start;
  966.  
  967.     get_string_between( start, end, &text );
  968.  
  969.     return end;
  970. }
  971.  
  972. /***** Implementation for class CJSourceParser *****/
  973.  
  974. CJSourceParser::CJSourceParser( bool collectCommnets, bool collectMacros )
  975.     : mpStart(0),
  976.       mpEnd(0),
  977.       mpCurCtx( 0 ),
  978.       mCommentsOn( collectCommnets ),
  979.       mMacrosOn  ( collectMacros )
  980. {
  981.     check_keyword_map();
  982. }
  983.  
  984. spFile* CJSourceParser::Parse( char* start, char* end )
  985. {
  986.     // set up state variables
  987.     mCurVis       = SP_VIS_PRIVATE;
  988.  
  989.     spFile* pTopCtx = new spFile();
  990.     mpCurCtx        = pTopCtx;
  991.  
  992.     mIsVirtual    = 0;
  993.     mIsTemplate   = 0;
  994.     mNestingLevel = 0;
  995.  
  996.     cur = start;
  997.  
  998.     mpStart = start;
  999.     mpEnd   = end;
  1000.  
  1001.     _gSrcEnd   = mpEnd; // let all the C-functions "smell" the end of file
  1002.     _gSrcStart = start;
  1003.  
  1004.     _gLineNo   = 0;
  1005.  
  1006.     clear_commets_queue();
  1007.  
  1008.     // main parsing loop
  1009.  
  1010.     do
  1011.     {
  1012.         if ( !get_next_token( cur ) )
  1013.             // end of source reached
  1014.             return pTopCtx;
  1015.  
  1016.         if ( memcmp( cur, "ScriptSection( const string&",
  1017.                      strlen( "ScriptSection( const string&" )
  1018.                    ) == 0
  1019.             )
  1020.         {
  1021.             int o = 0;
  1022.             ++o;
  1023.         }
  1024.  
  1025.         switch (*cur)
  1026.         {
  1027.             case '#' :
  1028.                 {
  1029.                     AddMacroNode( cur );
  1030.                     continue;
  1031.                 }
  1032.  
  1033.             case ':' :
  1034.                 {
  1035.                     skip_token( cur );
  1036.                     continue;
  1037.                 }
  1038.  
  1039.             case ';' :
  1040.                 {
  1041.                     skip_token( cur );
  1042.                     continue;
  1043.                 }
  1044.  
  1045.             case ')' :
  1046.                 {
  1047.                     skip_token( cur );
  1048.                     continue;
  1049.                 }
  1050.  
  1051.             case '=' :
  1052.                 {
  1053.                     skip_token( cur );
  1054.                     continue;
  1055.                 }
  1056.  
  1057.             default: break;
  1058.         }
  1059.  
  1060.         // 'const' is a part of the return type, not a keyword here
  1061.         if ( strncmp(cur, "const", 5) != 0 && is_keyword( cur ) )
  1062.         {
  1063.             // parses, token, if token identifies
  1064.             // the container context (e.g. class/namespace)
  1065.             // the corresponding context object is created
  1066.             // and set as current context
  1067.  
  1068.             ParseKeyword( cur );
  1069.             continue;
  1070.         }
  1071.  
  1072.         if ( *cur >= '0' && *cur <= '9' )
  1073.         {
  1074.             skip_token( cur );
  1075.             continue;
  1076.         }
  1077.  
  1078.         if ( *cur == '}' )
  1079.         {
  1080.             if ( mCurCtxType != SP_CTX_CLASS )
  1081.             {
  1082.                 // FOR NOW:: disable the below assertion
  1083.  
  1084.                 // DBG:: unexpected closing-bracket found
  1085.                 //ASSERT(0);
  1086.  
  1087.                 skip_token( cur ); // just skip it
  1088.                 continue;
  1089.             }
  1090.  
  1091.             if ( mpCurCtx->GetType() == SP_CTX_CLASS )
  1092.             {
  1093.                 int curOfs = ( (cur+1) - _gSrcStart );
  1094.  
  1095.                 mpCurCtx->mContextLength = ( curOfs - mpCurCtx->mSrcOffset );
  1096.             }
  1097.  
  1098.             --mNestingLevel;
  1099.  
  1100.             // terminate operation/class/namespace context
  1101.             // TBD:: check if it's really this type of context
  1102.  
  1103.             wxASSERT( mpCurCtx );
  1104.             mpCurCtx = mpCurCtx->GetOutterContext();
  1105.             wxASSERT( mpCurCtx );
  1106.  
  1107.             if ( mNestingLevel == 0 )
  1108.             {
  1109.  
  1110.                 mCurCtxType = SP_CTX_FILE;
  1111.  
  1112.                 // not-nested class delclaration finished,
  1113.                 // rest template flag in any case
  1114.                 mIsTemplate = 0;
  1115.             }
  1116.  
  1117.             skip_token( cur );
  1118.             continue;
  1119.         }
  1120.  
  1121.         bool isAMacro = 0;
  1122.  
  1123.         if ( is_function( cur, isAMacro ) )
  1124.         {
  1125.             if ( isAMacro )
  1126.             {
  1127.                 skip_token( cur );
  1128.                 continue;
  1129.             }
  1130.  
  1131.             char* savedPos = cur;
  1132.  
  1133.             int tmpLnNo;
  1134.             store_line_no( 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.         rettype.Replace("WXDLLEXPORT ", ""); // FIXME just for now...
  1625.         pOp->mRetType = rettype;
  1626.     }
  1627.  
  1628.     arrange_indirection_tokens_between( pOp->mRetType, pOp->mName );
  1629.  
  1630.     cur = savedPos;
  1631.     restore_line_no( tmpLnNo );
  1632.  
  1633.     // now, enter operation context
  1634.     mpCurCtx = pOp;
  1635.  
  1636.     return TRUE;
  1637. }
  1638.  
  1639. bool CJSourceParser::ParseArguments( char*& cur )
  1640. {
  1641.     // DANGER-MACROS::
  1642.  
  1643.     // now cursor position is right after the first opening bracket
  1644.     // of the function declaration
  1645.  
  1646.     char* blocks    [16]; // used exclusivelly for iterative "lean out"
  1647.                           // of macros and misc. not-obviouse grammar
  1648.                           // (dirty,, but we cannot do it very nice,
  1649.                           //  we're not preprocessor-free C/C++ code)
  1650.     int   blockSizes[16];
  1651.  
  1652.     do
  1653.     {
  1654.         size_t blocksSkipped = 0;
  1655.  
  1656.         get_next_token( cur );
  1657.  
  1658.         bool first_blk = 1;
  1659.  
  1660.         while( *cur != ')' && *cur != ',' )
  1661.         {
  1662.             blocks[blocksSkipped] = cur;
  1663.  
  1664.             if ( first_blk )
  1665.             {
  1666.                 char* prev = cur;
  1667.                 skip_token( cur );
  1668.  
  1669.                 blockSizes[blocksSkipped] = size_t(cur-prev);
  1670.  
  1671.                 first_blk = 0;
  1672.             }
  1673.             else
  1674.                 blockSizes[blocksSkipped] = skip_block( cur );
  1675.  
  1676.             get_next_token( cur );
  1677.             ++blocksSkipped;
  1678.         }
  1679.  
  1680.  
  1681.         if ( blocksSkipped == 1 )
  1682.         {
  1683.             // check if the empty arg. list stressed with "void" inside
  1684.             if ( cmp_tokens_fast( blocks[0] , "void", 4 ) )
  1685.             {
  1686.                 cur++;  // skip ')'
  1687.  
  1688.                 break;
  1689.             }
  1690.  
  1691.             // FIXME:: TBD:: K&R-style function declarations!
  1692.  
  1693.             // if only one block enclosed, than it's probably
  1694.             // some macro, there should be at least two blocks,
  1695.             // one for argument type and another for it's identifier
  1696.             return FALSE;
  1697.         }
  1698.  
  1699.         if ( blocksSkipped == 0 )
  1700.         {
  1701.             if ( *cur == 10 ) ++_gLineNo;
  1702.             ++cur; // skip ')'
  1703.  
  1704.             break; // function without paramters
  1705.         }
  1706.  
  1707.         // we should be in the operation context now
  1708.         spOperation* pOp = (spOperation*)mpCurCtx;
  1709.  
  1710.         spParameter* pPar = new spParameter();
  1711.  
  1712.         pOp->AddMember( pPar );
  1713.         // FOR NOW:: line number is not exact if argument list is mutiline
  1714.         pPar->mSrcLineNo = get_line_no();
  1715.  
  1716.         size_t nameBlock = blocksSkipped - 1;
  1717.         size_t typeBlock = nameBlock - 1;
  1718.  
  1719.         // check if default values present
  1720.         if ( *blocks[typeBlock] == '=' )
  1721.         {
  1722.             // expressions like "int = 5" are ignored,
  1723.             // since name for paramters is required
  1724.             if ( blocksSkipped == 3 )
  1725.             {
  1726.                 if ( *cur == ')' )
  1727.                 {
  1728.                     ++cur;
  1729.                     break;
  1730.                 }
  1731.             else
  1732.                 continue;
  1733.             }
  1734.  
  1735.             pPar->mInitVal = string( blocks[nameBlock], blockSizes[nameBlock] );
  1736.  
  1737.             nameBlock = nameBlock - 2; // skip '=' token and default value block
  1738.             typeBlock = nameBlock - 1;
  1739.         }
  1740.  
  1741.         // attach comments about the parameter
  1742.         AttachComments( *pPar, blocks[nameBlock] );
  1743.  
  1744.         // retrieve argument name
  1745.         pPar->mName = string( blocks[nameBlock], blockSizes[nameBlock] );
  1746.  
  1747.         // retreive argument type
  1748.  
  1749.         size_t len = blockSizes[ typeBlock ];
  1750.         len = size_t ( (blocks[ typeBlock ] + len) - blocks[ 0 ] );
  1751.  
  1752.         pPar->mType = string( blocks[0], len );
  1753.  
  1754.         arrange_indirection_tokens_between( pPar->mType, pPar->mName );
  1755.  
  1756.         if ( *cur == ')' )
  1757.         {
  1758.             ++cur;
  1759.             break;
  1760.         }
  1761.  
  1762.         ++cur; // skip comma
  1763.         get_next_token(cur);
  1764.  
  1765.     } while(1);
  1766.  
  1767.     // skip possible whitespace between ')' and following "const"
  1768.     while ( isspace(*cur) )
  1769.         cur++;
  1770.  
  1771.     // check if it was really a function not a macro,
  1772.     // if so, than it should be terminated with semicolon ';'
  1773.     // or opening implemenetaton bracket '{'
  1774.  
  1775.     char* tok = cur;
  1776.  
  1777.     int tmpLnNo;
  1778.     store_line_no( tmpLnNo );
  1779.  
  1780.     do
  1781.     {
  1782.         if ( *tok == '{' || *tok == ';' )
  1783.         {
  1784.             restore_line_no(tmpLnNo);
  1785.             return TRUE;
  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.             return TRUE;
  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 TRUE;
  1825. }
  1826.  
  1827. void CJSourceParser::ParseMemberVar( char*& cur )
  1828. {
  1829.     MMemberListT& members = mpCurCtx->GetMembers();
  1830.  
  1831.     bool firstMember = 1;
  1832.  
  1833.     size_t first = 0;
  1834.  
  1835.     string type;
  1836.  
  1837.     // jump to the end of statement
  1838.     // and start collecting same-type varibles
  1839.     // back-to-front towards the type identifier
  1840.  
  1841.     skip_statement( cur );
  1842.     char* savedPos = cur;
  1843.  
  1844.     int tmpLnNo;
  1845.     store_line_no( tmpLnNo );
  1846.  
  1847.     --cur; // rewind back to ';'
  1848.  
  1849.     do
  1850.     {
  1851.         spAttribute* pAttr = new spAttribute();
  1852.         // FOR NOW:: line not is not exact, if member declaration is multiline
  1853.         pAttr->mSrcLineNo = get_line_no();
  1854.  
  1855.         mpCurCtx->AddMember( pAttr );
  1856.         pAttr->mVisibility = mCurVis;
  1857.  
  1858.         pAttr->mIsConstant = 0;
  1859.  
  1860.         if ( firstMember )
  1861.         {
  1862.             firstMember = 0;
  1863.             first = members.size() - 1;;
  1864.         }
  1865.  
  1866.         skip_token_back( cur );
  1867.  
  1868.         // attach comments about the attribute
  1869.         AttachComments( *pAttr, cur );
  1870.  
  1871.         pAttr->mName = get_token_str( cur );
  1872.  
  1873.         // guessing that this going to be variable type
  1874.         skip_next_token_back( cur );
  1875.         skip_token_back( cur );
  1876.  
  1877.         pAttr->mType = get_token_str( cur );
  1878.  
  1879.         // if comma, than variable list continues
  1880.         // otherwise the variable type reached - stop
  1881.  
  1882.         if ( *cur == '=' )
  1883.         {
  1884.             // yes, we've mistaken, it was not a identifier,
  1885.             // but it's default value
  1886.             pAttr->mInitVal =
  1887.                 pAttr->mName;
  1888.  
  1889.             // skip default value and '=' symbol
  1890.             skip_next_token_back( cur );
  1891.             skip_token_back( cur );
  1892.  
  1893.             pAttr->mName = get_token_str( cur );
  1894.  
  1895.             skip_next_token_back( cur );
  1896.             skip_token_back( cur );
  1897.         }
  1898.  
  1899.         if ( *cur != ',' )
  1900.         {
  1901.             type = get_token_str( cur );
  1902.             break;
  1903.         }
  1904.  
  1905.     } while(1);
  1906.  
  1907.     first = 0;
  1908.  
  1909.     // set up types for all collected (same-type) attributes;
  1910.     while ( first != members.size() - 1 )
  1911.     {
  1912.         spAttribute* pAttr = members[first++]->CastToAttribute();
  1913.         if ( !pAttr )
  1914.             continue;
  1915.  
  1916.         if ( !pAttr->mType )
  1917.             pAttr->mType = type;
  1918.         pAttr->mVisibility = mCurVis;
  1919.  
  1920.         if ( !!pAttr->mName )
  1921.             arrange_indirection_tokens_between( pAttr->mType, pAttr->mName );
  1922.     }
  1923.  
  1924.     cur = savedPos;
  1925.     restore_line_no( tmpLnNo );
  1926.  
  1927.     clear_commets_queue();
  1928.  
  1929.  
  1930. }
  1931.  
  1932. void CJSourceParser::SkipFunction( char*& cur )
  1933. {
  1934.     while ( *cur != '(' && cur < _gSrcEnd )
  1935.     {
  1936.         if (*cur == 10 ) ++_gLineNo;
  1937.         ++cur;
  1938.     }
  1939.  
  1940.     skip_next_token_back( cur ); // go back and skip function identifier
  1941.     skip_token_back( cur );      // go back and skip return type
  1942.  
  1943.     skip_block( cur );           // now, go ahead and skip whole declaration
  1944.  
  1945.     SkipFunctionBody( cur );
  1946.  
  1947. }
  1948.  
  1949. void CJSourceParser::SkipFunctionBody( char*& cur )
  1950. {
  1951.     // FIXME:: check for comments and quoted stirngs here
  1952.  
  1953.     bool hasDefinition = FALSE;
  1954.  
  1955.     while( *cur != '{' && *cur != ';' )
  1956.     {
  1957.         if (*cur == 10 ) ++_gLineNo;
  1958.         ++cur;
  1959.     }
  1960.  
  1961.     if ( *cur == ';' )
  1962.     {
  1963.         ++cur;
  1964.     }
  1965.     else
  1966.     {
  1967.         hasDefinition = TRUE;
  1968.  
  1969.         skip_scope_block( cur ); // skip the whole imp.
  1970.     }
  1971.  
  1972.     if ( mpCurCtx->GetType() == SP_CTX_OPERATION )
  1973.     {
  1974.         spOperation& op = *((spOperation*)mpCurCtx);
  1975.  
  1976.         int curOfs = int ( cur - _gSrcStart );
  1977.  
  1978.         op.mContextLength = curOfs - mpCurCtx->mSrcOffset;
  1979.  
  1980.         op.mHasDefinition = hasDefinition;
  1981.  
  1982.         // separate scope resolution token from the name of operation
  1983.  
  1984.         for( size_t i = 0; i != op.mName.length(); ++i )
  1985.         {
  1986.             if ( op.mName[i] == ':' && op.mName[i+1] == ':' )
  1987.             {
  1988.                 string unscoped( op.mName, i+2, op.mName.length() - ( i + 2 ) );
  1989.  
  1990.                 op.mScope = string( op.mName, 0, i );
  1991.  
  1992.                 op.mName = unscoped;
  1993.  
  1994.                 break;
  1995.             }
  1996.         }
  1997.     }
  1998. }
  1999.  
  2000. bool CJSourceParser::CheckVisibilty( char*& cur )
  2001. {
  2002.     size_t len = get_token_len( cur );
  2003.  
  2004.     if ( cmp_tokens_fast( cur, "public:", len ) )
  2005.     {
  2006.         mCurVis = SP_VIS_PUBLIC;
  2007.         return TRUE;
  2008.     }
  2009.  
  2010.     if ( cmp_tokens_fast( cur, "protected:", len ) )
  2011.     {
  2012.         mCurVis = SP_VIS_PROTECTED;
  2013.         return TRUE;
  2014.     }
  2015.  
  2016.     if ( cmp_tokens_fast( cur, "private:", len ) )
  2017.     {
  2018.         mCurVis = SP_VIS_PRIVATE;
  2019.         return TRUE;
  2020.     }
  2021.  
  2022.     return FALSE;
  2023. }
  2024.  
  2025. void CJSourceParser::AddClassNode( char*& cur )
  2026. {
  2027.     char* ctxStart = cur;
  2028.  
  2029.     string classkeyword = get_token_str( cur );
  2030.  
  2031.     skip_token( cur ); // skip 'class' keyword
  2032.     if ( !get_next_token( cur ) ) return;
  2033.  
  2034.     // in C++
  2035.     if ( *cur == ':' )
  2036.     {
  2037.         skip_token( cur );
  2038.         get_next_token( cur );
  2039.     }
  2040.  
  2041.     // by default all class members are private
  2042.     mCurVis = SP_VIS_PRIVATE;
  2043.  
  2044.     spClass* pClass = new spClass();
  2045.     if ( classkeyword == "class" )
  2046.         pClass->mClassSubType = SP_CLTYPE_CLASS;
  2047.     else if ( classkeyword == "struct" ) {
  2048.         pClass->mClassSubType = SP_CLTYPE_STRUCTURE;
  2049.  
  2050.         mCurVis = SP_VIS_PUBLIC;
  2051.     }
  2052.     else if ( classkeyword == "union" ) {
  2053.         pClass->mClassSubType = SP_CLTYPE_UNION;
  2054.  
  2055.         mCurVis = SP_VIS_PUBLIC;
  2056.     }
  2057.     else if ( classkeyword == "interface" )
  2058.         pClass->mClassSubType = SP_CLTYPE_INTERFACE;
  2059.     else {
  2060.         pClass->mClassSubType = SP_CLTYPE_INVALID;
  2061.  
  2062.         wxFAIL_MSG("unknown class keyword");
  2063.     }
  2064.  
  2065.     mpCurCtx->AddMember( pClass );
  2066.  
  2067.     // attach comments about the class
  2068.     AttachComments( *pClass, cur );
  2069.  
  2070.     pClass->mSrcLineNo = get_line_no();
  2071.  
  2072.     pClass->mSrcOffset = int( ctxStart - _gSrcStart );
  2073.  
  2074.     char* nameTok = cur;
  2075.     pClass->mName = get_token_str( cur );
  2076.  
  2077.     bool isDerived = 0;
  2078.  
  2079.     // DANGER-MACROS::
  2080.  
  2081.     do
  2082.     {
  2083.         skip_token( cur );
  2084.         if ( !get_next_token( cur ) ) return;
  2085.  
  2086.         if ( *cur == ':' )
  2087.         {
  2088.             isDerived = 1;
  2089.  
  2090.             char* tok = cur;
  2091.  
  2092.             int tmpLn;
  2093.             store_line_no( tmpLn );
  2094.  
  2095.             skip_next_token_back( tok );
  2096.             skip_token_back( tok );
  2097.  
  2098.             restore_line_no( tmpLn );
  2099.  
  2100.             // class name should precend ':' colon, thus
  2101.             // the one which was captured before was
  2102.             // proablty something else (like __dllexport MyClass : ... )
  2103.  
  2104.             if ( nameTok != tok )
  2105.             {
  2106.                 pClass->mName = get_token_str( tok );
  2107.             }
  2108.  
  2109.         }
  2110.  
  2111.         if ( *cur == '{' )
  2112.             break;
  2113.  
  2114.         if ( *cur == ',' )
  2115.             continue;
  2116.  
  2117.         size_t len = get_token_len( cur );
  2118.  
  2119.         // skip neglectable C++ modifieres
  2120.         if ( cmp_tokens_fast( cur, "public", len ) )
  2121.             continue;
  2122.  
  2123.         if ( cmp_tokens_fast( cur, "protected", len ) )
  2124.             continue;
  2125.  
  2126.         if ( cmp_tokens_fast( cur, "private", len ) )
  2127.             continue;
  2128.  
  2129.         if ( cmp_tokens_fast( cur, "virtual", len ) )
  2130.             continue;
  2131.  
  2132.         // skip neglectable JAVA modifieres
  2133.  
  2134.         if ( cmp_tokens_fast( cur, "extends", len ) )
  2135.         {
  2136.             isDerived = 1;
  2137.             continue;
  2138.         }
  2139.  
  2140.         if ( cmp_tokens_fast( cur, "implements", len ) )
  2141.         {
  2142.             isDerived = 1;
  2143.             continue;
  2144.         }
  2145.  
  2146.         // all we need to know is superclass or interface
  2147.  
  2148.         char* tok = cur;
  2149.         int tmpLn;
  2150.         store_line_no( tmpLn );
  2151.  
  2152.         skip_token(tok);
  2153.         get_next_token(tok);
  2154.  
  2155.         restore_line_no( tmpLn );
  2156.  
  2157.         if ( *tok != ':' && *cur != ':' )
  2158.  
  2159.             pClass->mSuperClassNames.push_back( string( cur, len ) );
  2160.  
  2161.     } while(1);
  2162.  
  2163.     if ( !isDerived )
  2164.     {
  2165.         int tmpLn;
  2166.         store_line_no( tmpLn );
  2167.  
  2168.         while ( pClass->mSuperClassNames.size() )
  2169.  
  2170.             pClass->mSuperClassNames.erase( &pClass->mSuperClassNames[0] );
  2171.  
  2172.         char* tok = cur;
  2173.  
  2174.         // some non-obviouse token was following "class" keyword -
  2175.         // we've confused it with class name - thus now we're reverting this mistake
  2176.  
  2177.         skip_next_token_back( tok );
  2178.         skip_token_back( tok );
  2179.  
  2180.         pClass->mName = get_token_str( tok );
  2181.  
  2182.         restore_line_no( tmpLn );
  2183.     }
  2184.  
  2185.  
  2186.     ++cur; // skip opening curly brace
  2187.  
  2188.     pClass->mHeaderLength = ( cur - ctxStart );
  2189.  
  2190.     // now, enter the class context
  2191.     mpCurCtx = pClass;
  2192.  
  2193.     clear_commets_queue();
  2194. }
  2195.  
  2196. void CJSourceParser::AddEnumNode( char*& cur )
  2197. {
  2198.     // now the cursor is at "enum" keyword
  2199.     char* start = cur;
  2200.  
  2201.     spEnumeration* pEnum = new spEnumeration();
  2202.     mpCurCtx->AddMember( pEnum );
  2203.  
  2204.     pEnum->mSrcLineNo = get_line_no();
  2205.  
  2206.  
  2207.     AttachComments( *pEnum, cur );
  2208.  
  2209.     skip_token( cur );
  2210.     if ( !get_next_token( cur ) ) return;
  2211.  
  2212.     // check if enumeration has got it's identifier
  2213.     if ( *cur != '{' )
  2214.     {
  2215.         pEnum->mName = get_token_str( cur );
  2216.     }
  2217.  
  2218.     if ( !skip_imp_block( cur ) ) return;
  2219.  
  2220.     get_string_between( start, cur, &pEnum->mEnumContent );
  2221.  
  2222.     if ( get_next_token(cur) )
  2223.     {
  2224.         // check if the identifier if after the {...} block
  2225.         if ( *cur != ';' )
  2226.  
  2227.             pEnum->mName = get_token_str( cur );
  2228.     }
  2229.  
  2230.     clear_commets_queue();
  2231. }
  2232.  
  2233. void CJSourceParser::AddTypeDefNode( char*& cur )
  2234. {
  2235.     // now the cursor at the token next to "typedef" keyword
  2236.  
  2237.     if ( !get_next_token(cur) ) return;
  2238.  
  2239.     char* start = cur;
  2240.  
  2241.     spTypeDef* pTDef = new spTypeDef();
  2242.     mpCurCtx->AddMember( pTDef );
  2243.  
  2244.     pTDef->mSrcLineNo = get_line_no();
  2245.  
  2246.     AttachComments( *pTDef, cur );
  2247.  
  2248.     skip_statement( cur );
  2249.  
  2250.     int tmpLnNo;
  2251.     store_line_no( tmpLnNo );
  2252.  
  2253.     char* tok = cur-1;
  2254.     skip_next_token_back( tok );
  2255.  
  2256.     char* nameEnd = tok;
  2257.  
  2258.     skip_token_back( tok );
  2259.  
  2260.     char* nameStart = tok;
  2261.  
  2262.     skip_next_token_back( tok );
  2263.  
  2264.     char* typeEnd = tok;
  2265.  
  2266.     // check if it's function prototype
  2267.     if ( *nameStart == ')' )
  2268.     {
  2269.         typeEnd = nameStart+1;
  2270.  
  2271.         // skip argument list
  2272.         while ( *nameStart != '(' ) --nameStart;
  2273.  
  2274.         // skip to function type definition
  2275.         while ( *nameStart != ')' ) --nameStart;
  2276.  
  2277.         skip_next_token_back( nameStart );
  2278.  
  2279.         nameEnd = nameStart;
  2280.  
  2281.         skip_token_back( nameStart );
  2282.  
  2283.         if ( *nameStart == '*' ) ++nameStart;
  2284.     }
  2285.  
  2286.     get_string_between( start, typeEnd, &pTDef->mOriginalType );
  2287.  
  2288.     get_string_between( nameStart, nameEnd, &pTDef->mName );
  2289.  
  2290.     clear_commets_queue();
  2291.  
  2292.     restore_line_no( tmpLnNo );
  2293. }
  2294.