|
|
//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 2000.
//
// File: CXX.CXX
//
// Contents: C and C++ Filter
//
// Classes: CxxFilter
//
// History: 26-Jun-92 BartoszM Created
// 17-Oct-94 BartoszM Rewrote
//
//----------------------------------------------------------------------------
#include <pch.cxx>
#pragma hdrstop
//+---------------------------------------------------------------------------
//
// Member: CxxScanner::CxxScanner, public
//
// History: 26-Jun-92 BartoszM Created
//
//----------------------------------------------------------------------------
CxxScanner::CxxScanner () : _pStream(0), _fIgnorePreamble(FALSE), _fScanningPrepro(FALSE), _fIdFound(FALSE), _cLines( 0 ) { _buf[0] = L'\0'; }
//+---------------------------------------------------------------------------
//
// Member: CxxScanner::Init, public
//
// Arguments: [pStream] -- stream for text
//
// History: 24-Nov-93 AmyA Created
//
//----------------------------------------------------------------------------
void CxxScanner::Init ( CFilterTextStream * pStream ) { _pStream = pStream; // Position scanner on a token
Accept(); }
//+---------------------------------------------------------------------------
//
// Member: CxxScanner::NextToken, public
//
// Arguments: [c] -- lookahead character
//
// Returns: Recognized token
//
// History: 26-Jun-92 BartoszM Created
//
//----------------------------------------------------------------------------
CToken CxxScanner::NextToken( int c ) { BOOL fFirstTime = TRUE;
// Loop until a token is recognized
for(;;) { switch (c) { case -1: // UNICODE EOF
_token = tEnd; return _token;
case L'\n': _cLines++; _fScanningPrepro = FALSE; c = _pStream->GetChar(); break;
case L'{': _token = tLBrace; return _token;
case L'}': _token = tRBrace; return _token;
case L';': _token = tSemi; return _token;
case L',': if ( _fIgnorePreamble ) { // skip comma in the preamble
c = _pStream->GetChar(); break; } _token = tComma; return _token;
case L'*': if ( _fIgnorePreamble ) { // skip star in the preamble
c = _pStream->GetChar(); break; } _token = tStar; return _token;
case L'#': // not a token!
// consume preprocessor command
_fScanningPrepro = TRUE; c = _pStream->GetChar(); break;
case L'(': if ( _fIgnorePreamble ) { // skip parentheses in the preamble
c = _pStream->GetChar(); break; } _token = tLParen; return _token;
case L')': if ( _fIgnorePreamble ) { // skip parentheses in the preamble
c = _pStream->GetChar(); break; } _token = tRParen; return _token;
case L':': c = _pStream->GetChar(); // ignore colons in the preamble
if ( !_fIgnorePreamble && c == L':') { _token = tDoubleColon; return _token; } break;
case L'/': // not a token!
// consume comment
c = EatComment(); break;
case L'"': // not a token!
// consume string literal
c = EatString(); break;
case L'\'': // not a token!
// consume character literal
c = EatCharLiteral(); break;
default:
// We don't really care about indentifiers.
// We store them in the buffer so that when
// we recognize a real token like :: or (
// we can retrieve them.
// Look out for 'class' 'struct' and 'union' though.
if ( iswalpha((wint_t)c) || (c == L'_') || (c == L'~') ) { _fIdFound = TRUE; // in preamble skip names except for the first
// one, which is the name of the procedure
if ( _fIgnorePreamble && !fFirstTime ) { c = SkipName(c); continue; } else { c = LoadName (c); fFirstTime = FALSE; }
if (!_fIgnorePreamble) { // look for class/struct/union keywords
if ( wcscmp(_buf, L"class" ) == 0 ) { _token = tClass; return _token; } else if ( wcscmp(_buf, L"struct") == 0 ) { _token = tStruct; return _token; } else if ( wcscmp(_buf, L"union" ) == 0 ) { _token = tUnion; return _token; } else if ( wcscmp(_buf, L"interface" ) == 0 ) { _token = tInterface; return _token; } else if ( wcscmp(_buf, L"typedef" ) == 0 ) { _token = tTypedef; return _token; } else if ( wcscmp(_buf, L"enum" ) == 0 ) { _token = tEnum; return _token; } }
if ( _fScanningPrepro ) { if ( wcscmp(_buf, L"define" ) == 0 ) { _token = tDefine; c = LoadName(c); return _token; } else if ( wcscmp(_buf, L"include" ) == 0 ) { _token = tInclude; c = LoadIncludeFileName(c); return _token; } else { c = EatPrepro(); _fScanningPrepro = FALSE; } } } else // not recognized, continue scanning
{ c = _pStream->GetChar(); } break; } // end of switch
} // end of infinite loop
return _token; }
//+---------------------------------------------------------------------------
//
// Member: CxxScanner::SkipName, public
//
// Returns: Next character after identifier
//
// History: 26-Jun-92 BartoszM Created
//
//----------------------------------------------------------------------------
int CxxScanner::SkipName(int c) { int i = 0;
do { c = _pStream->GetChar(); i++; } while ( (iswalnum((wint_t)c) || (c == L'_')) && (i < MAXIDENTIFIER) );
return c; }
//+---------------------------------------------------------------------------
//
// Member: CxxScanner::LoadName, public
//
// Synopsis: Scans and copies identifier into scanner's buffer
//
// Arguments: [c] -- GetChar character
//
// Returns: Next character after identifier
//
// History: 26-Jun-92 BartoszM Created
//
//----------------------------------------------------------------------------
int CxxScanner::LoadName(int c) { WCHAR * pCur = _buf; _pStream->GetRegion ( _region, -1, 0 ); int i = 0; do { _buf[i++] = (WCHAR)c; c = _pStream->GetChar(); } while ( (iswalnum((wint_t)c) || (c == L'_')) && (i < MAXIDENTIFIER));
_region.cwcExtent = i; // c is not a symbol character
_buf[i] = L'\0';
//DbgPrint("LoadName: =================> %ws\n", _buf);
return c; }
//+---------------------------------------------------------------------------
//
// Member: CxxScanner::LoadIncludeFileName, public
//
// Synopsis: Scans and copies a file name following a
// #include statement to internal buffer
// If a path exists, it is ignored.
// A '.' is converted to '_' because searching an id
// with a '.' does not seem to work with ci.
// For example,
// #include <\foo\bar\fname.h> --> fname_h
//
// Arguments: [c] -- GetChar character
//
// Returns: Next character after the #include stmt
//
// History: 10-June-2000 kumarp Created
//
//----------------------------------------------------------------------------
int CxxScanner::LoadIncludeFileName(int c) { WCHAR * pCur = _buf; int i = 0;
// skip chars preceeding the file name
do { c = _pStream->GetChar(); } while ((c == L'\t') || (c == L' ') || (c == L'"') || (c == L'<')); _pStream->GetRegion ( _region, -1, 0 );
do { _buf[i++] = (WCHAR)c; if ((c == L'\\') || (c == L'/')) { // ignore path
i = 0; _pStream->GetRegion ( _region, 0, 0 ); }
c = _pStream->GetChar(); // if (c == L'.')
// {
// c = L'_';
// }
} while ((iswalnum((wint_t)c) || ( c == L'.' ) || (c == L'_') || (c == L'\\') || (c == L'/')) && (i < MAXIDENTIFIER)); _region.cwcExtent = i; _buf[i] = L'\0';
c = EatPrepro();
return c; }
//+---------------------------------------------------------------------------
//
// Member: CxxScanner::EatComment, public
//
// Synopsis: Eats comments
//
// Returns: First non-comment character
//
// Requires: Leading '/' found and scanned
//
// History: 26-Jun-92 BartoszM Created
//
//----------------------------------------------------------------------------
int CxxScanner::EatComment() { int c = _pStream->GetChar();
if ( c == L'*') { // C style comment
while ((c = _pStream->GetChar()) != EOF ) { while ( c == L'*' ) { c = _pStream->GetChar();
if ( c == EOF ) return EOF;
if ( c == L'/' ) return _pStream->GetChar();
} } } else if ( c == L'/' ) { // C++ style comment
while ((c = _pStream->GetChar()) != EOF ) { if ( c == L'\n' ) break; } }
return c; } //+---------------------------------------------------------------------------
//
// Member: CxxScanner::EatString, public
//
// Synopsis: Eats string literal
//
// Returns: First non-string character
//
// Requires: Leading '"' found and scanned
//
// History: 26-Jun-92 BartoszM Created
//
//----------------------------------------------------------------------------
int CxxScanner::EatString() { int c;
while ((c = _pStream->GetChar()) != EOF ) { if ( c == L'"' ) { c = _pStream->GetChar(); break; }
// eat backslashes
// skip escaped quotes
if ( c == L'\\' ) { c = _pStream->GetChar(); if ( c == EOF ) return EOF; } } return c; }
//+---------------------------------------------------------------------------
//
// Member: CxxScanner::EatCharLiteral, public
//
// Synopsis: Eats character literal
//
// Returns: First non-char-literal character
//
// Requires: Leading apostrophe ' found and scanned
//
// History: 26-Jun-92 BartoszM Created
//
//----------------------------------------------------------------------------
int CxxScanner::EatCharLiteral() { int c;
while ((c = _pStream->GetChar()) != EOF ) { if ( c == L'\'' ) { c = _pStream->GetChar(); break; }
// eat backslashes
// skip escaped quotes
if ( c == L'\\' ) { c = _pStream->GetChar(); if ( c == EOF ) return EOF; } } return c; }
//+---------------------------------------------------------------------------
//
// Member: CxxScanner::EatPrepro, public
//
// Synopsis: Eats preprocessor commands. Possibly multi-line.
//
// Returns: First non-preprocessor character
//
// Requires: Leading # found and scanned
//
// History: 26-Jun-92 BartoszM Created
//
//----------------------------------------------------------------------------
int CxxScanner::EatPrepro() { int c;
_fScanningPrepro = FALSE;
while ((c = _pStream->GetChar()) != EOF && (c != L'\n')) { if ( c == L'\\' ) // skip whatever follows backslash
{ c = _pStream->GetChar(); if (c == L'\r') c = _pStream->GetChar(); if ( c == EOF ) return EOF; } } return c; }
//+---------------------------------------------------------------------------
//
// Member: CxxParser::CxxParser, public
//
// Synopsis: Initialize parser
//
// Arguments: [pStm] -- stream
// [drep] -- data repository
//
// History: 26-Jun-92 BartoszM Created
//
//----------------------------------------------------------------------------
CxxParser::CxxParser () : _scope(0), _inClass(0), _fParsingTypedef(FALSE), _fParsingFnPtrTypedef(FALSE), _iVal(0) { _strClass[0] = L'\0'; _strName[0] = L'\0'; _attribute.ulKind = PRSPEC_LPWSTR; _attribute.lpwstr = PROP_CLASS;
_psVal[Function].ulKind = PRSPEC_LPWSTR; _psVal[Function].lpwstr = PROP_FUNC;
_psVal[Class].ulKind = PRSPEC_LPWSTR; _psVal[Class].lpwstr = PROP_CLASS;
_psVal[Lines].ulKind = PRSPEC_LPWSTR; _psVal[Lines].lpwstr = PROP_LINES;
_aVal[Function] = 0; _aVal[Class] = 0; _aVal[Lines] = 0; }
CxxParser::~CxxParser() { delete _aVal[Function]; delete _aVal[Class]; delete _aVal[Lines]; }
//+---------------------------------------------------------------------------
//
// Member: CxxParser::Init, public
//
// Synopsis: Initialize parser
//
// Arguments: [pStream] -- stream
//
// History: 24-Nov-93 AmyA Created
//
//----------------------------------------------------------------------------
void CxxParser::Init ( CFilterTextStream * pStream ) { _scan.Init(pStream); _token = _scan.Token(); }
//+---------------------------------------------------------------------------
//
// Member: CxxParser::Parse, public
//
// Synopsis: Parse the file
//
// History: 26-Jun-92 BartoszM Created
//
//----------------------------------------------------------------------------
BOOL CxxParser::Parse() { _cwcCopiedClass = 0; _cwcCopiedName = 0;
while ( _token != tEnd) { switch ( _token ) { case tTypedef: if ( !_fParsingTypedef ) { _fParsingTypedef = TRUE; _typedefScope = _scope; } _token = _scan.Accept(); break;
case tSemi: if ( _fParsingTypedef && ( _scope == _typedefScope )) { ASSERT(_fParsingFnPtrTypedef == FALSE); SetName(); //DbgPrint("tSemi: name: %ws, scope: %d\n", _strName, _scope);
PutFunction(); _fParsingTypedef = FALSE; _token = _scan.Accept(); return TRUE; } _token = _scan.Accept(); break;
case tComma: if ( _fParsingTypedef && ( _scope == _typedefScope )) { ASSERT(_fParsingFnPtrTypedef == FALSE); SetName(); //DbgPrint("tComma: name: %ws, scope: %d\n", _strName, _scope);
PutFunction(); _token = _scan.Accept(); return TRUE; } _token = _scan.Accept(); break;
case tEnum: //DbgPrint("tEnum\n");
//_scan.IgnorePreamble(TRUE);
_token = _scan.Accept(); //_scan.IgnorePreamble(FALSE);
if ( _token == tLBrace ) { // Good, we're inside a enum definition
_scope++; SetName(); //DbgPrint("tEnum: %ws\n", _strName);
PutFunction(); _token = _scan.Accept(); return TRUE; } // otherwise it was a false alarm
break; case tClass: case tStruct: case tUnion: case tInterface:
// We have to recognize stuff like this:
// class FOO : public bar:a, private b {
// ----- --
// text between 'class' and left brace is
// a preamble that the scanner will skip
// If it's only a forward declaration, we
// will stop at a semicolon and ignore the
// whole business.
#if CIDBG == 1
_classToken = _token; #endif // CIDBG == 1
// scan through stuff like
// : public foo, private bar
_scan.IgnorePreamble(TRUE); _token = _scan.Accept(); _scan.IgnorePreamble(FALSE);
// Ignore embedded classes
if ( _inClass == 0 ) SetClass(); // record class name for later
if ( _token == tLBrace ) { // Good, we're inside a class definition
_inClass++; _scope++; PutClass (); _token = _scan.Accept(); return TRUE; } // otherwise it was a false alarm
break;
case tDoubleColon:
// Here we deal with constructs like
// FOO::FOO ( int x ) : bar(state::ok), (true) {
// -- - --
// Text between left paren and left brace is preamble
// and the scanner skips it. If we hit a semicolon
// rather than left brace, we ignore the whole
// construct (it was an invocation or something)
SetClass(); // record class name just in case
_token = _scan.Accept(); if ( _token == tLParen ) { SetName(); // record method name just in case
_scan.IgnorePreamble(TRUE); _token = _scan.Accept(); _scan.IgnorePreamble(FALSE);
if ( _token == tLBrace ) { // Yes, we have method definition
_scope++; _token = _scan.Accept(); PutMethod(); return TRUE; } // otherwise it was a false alarm
} break;
case tLParen: if ( _fParsingTypedef && ( _scope == _typedefScope )) { //
// at present we only support fn-ptr typedefs
// of the following type:
//
// typedef void (*FnPtr1) ( int i, float f );
//
//SetName();
//DbgPrint("tLParen: name: %ws, scope: %d\n", _strName, _scope);
_scan.SetIdFound(FALSE); _token = _scan.Accept(); if ( ( _token == tStar ) && !_scan.IdFound() ) { _fParsingFnPtrTypedef = TRUE; } else { //PutFunction();
_fParsingTypedef = FALSE; _fParsingFnPtrTypedef = FALSE; } _token = _scan.Accept(); } else { SetName(); // record procedure name just in case
// It may be an inline constructor
// skip argument list and constructor stuff like
// : Parent(blah), member(blah)
_scan.IgnorePreamble(TRUE); _token = _scan.Accept(); _scan.IgnorePreamble(FALSE);
if ( _token == tLBrace ) { // Yes, it's a definition
if ( _inClass ) { // inline method definition inside class definition
_scope++; _token = _scan.Accept(); PutInlineMethod(); return TRUE; } else if ( _scope == 0 ) { // function definitions
// in outer scope
_scope++; PutFunction(); _token = _scan.Accept(); return TRUE; } // else continue--false alarm
} } break;
case tRParen: if ( _fParsingFnPtrTypedef && ( _scope == _typedefScope )) { SetName(); //DbgPrint("tRParen: name: %ws, scope: %d\n", _strName, _scope);
PutFunction(); _fParsingTypedef = FALSE; _fParsingFnPtrTypedef = FALSE; _token = _scan.Accept(); return TRUE; } _token = _scan.Accept(); break; case tEnd: return FALSE;
case tLBrace: // keep track of scope
_scope++; _token = _scan.Accept(); break;
case tRBrace: // keep track of scope and (nested) class scope
_scope--; if ( _inClass > _scope ) { _inClass--; } _token = _scan.Accept(); break;
case tDefine: SetName(); PutFunction(); _scan.EatPrepro(); _token = _scan.Accept(); return TRUE;
case tInclude: SetName(); PutFunction(); _token = _scan.Accept(); return TRUE; default: _token = _scan.Accept(); } }
if ( _aVal[Lines] == 0 ) { _aVal[Lines] = new CPropVar; if ( 0 == _aVal[Lines] ) THROW( CException( E_OUTOFMEMORY ) ); } _aVal[Lines]->SetUI4( _scan.Lines() );
return FALSE; // we only end up here if _token == tEnd
}
void CxxParser::PutClass () { _tokenType = ttClass; _attribute.lpwstr = PROP_CLASS; _strName[0] = L'\0';
#if 0
if ( _aVal[Class] == 0 ) { _aVal[Class] = new CPropVar; if ( 0 == _aVal[Class] ) THROW( CException( E_OUTOFMEMORY ) ); }
_aVal[Class]->SetLPWSTR( _strClass, _aVal[Class]->Count() );
#endif
// PROP_CLASS, _strClass
//DbgPrint("PutClass: class: %ws\n", _strClass);
#if CIDBG == 1
if ( _classToken == tClass ) {
cxxDebugOut((DEB_ITRACE,"class %ws\n", _strClass )); } else if ( _classToken == tStruct ) {
cxxDebugOut((DEB_ITRACE, "struct %ws\n", _strClass )); } else if ( _classToken == tUnion ) {
cxxDebugOut((DEB_ITRACE, "union %ws\n", _strClass )); } else if ( _classToken == tInterface ) {
cxxDebugOut((DEB_ITRACE, "interface %ws\n", _strClass )); } #endif // CIDBG == 1
}
void CxxParser::PutMethod () { _tokenType = ttMethod; _attribute.lpwstr = PROP_FUNC;
#if 0
if ( _aVal[Function] == 0 ) { _aVal[Function] = new CPropVar; if ( 0 == _aVal[Function] ) THROW( CException( E_OUTOFMEMORY ) ); }
_aVal[Function]->SetLPWSTR( _strName, _aVal[Function]->Count() );
#endif
cxxDebugOut((DEB_ITRACE, "%ws::%ws\n", _strClass, _strName )); }
void CxxParser::PutInlineMethod () { _tokenType = ttInlineMethod; _attribute.lpwstr = PROP_FUNC;
#if 0
if ( _aVal[Function] == 0 ) { _aVal[Function] = new CPropVar; if ( 0 == _aVal[Function] ) THROW( CException( E_OUTOFMEMORY ) ); }
_aVal[Function]->SetLPWSTR( _strName, _aVal[Function]->Count() );
#endif
cxxDebugOut((DEB_ITRACE, "%ws::%ws\n", _strClass, _strName )); }
void CxxParser::PutFunction () { _tokenType = ttFunction; _attribute.lpwstr = PROP_FUNC; _strClass[0] = L'\0';
#if 0
if ( _aVal[Function] == 0 ) { _aVal[Function] = new CPropVar; if ( 0 == _aVal[Function] ) THROW( CException( E_OUTOFMEMORY ) ); }
_aVal[Function]->SetLPWSTR( _strName, _aVal[Function]->Count() ); #endif
//DbgPrint("PutFunction: func: %ws\n", _strName);
cxxDebugOut((DEB_ITRACE, "function %ws\n", _strName )); }
void CxxParser::GetRegion ( FILTERREGION& region ) { switch (_tokenType) { case ttClass: region = _regionClass; break; case ttFunction: case ttInlineMethod: case ttMethod: region = _regionName; break; } }
BOOL CxxParser::GetTokens ( ULONG * pcwcBuffer, WCHAR * awcBuffer ) { ULONG cwc = *pcwcBuffer; *pcwcBuffer = 0;
if (_strClass[0] != L'\0') { // We have a class name
WCHAR * strClass = _strClass + _cwcCopiedClass;
ULONG cwcClass = wcslen( strClass ); if ( cwcClass > cwc ) { wcsncpy( awcBuffer, strClass, cwc ); _cwcCopiedClass += cwc; return FALSE; } wcscpy( awcBuffer, strClass ); *pcwcBuffer = cwcClass; _cwcCopiedClass += cwcClass; awcBuffer[(*pcwcBuffer)++] = L' '; }
if (_strName[0] == L'\0') { // it was only a class name
awcBuffer[*pcwcBuffer] = L'\0'; return TRUE; }
cwc -= *pcwcBuffer; WCHAR * awc = awcBuffer + *pcwcBuffer; WCHAR * strName = _strName + _cwcCopiedName; ULONG cwcName = wcslen( strName );
if ( cwcName > cwc ) { wcsncpy( awc, strName, cwc ); _cwcCopiedName += cwc; return FALSE; } wcscpy( awc, strName ); *pcwcBuffer += cwcName; _cwcCopiedName += cwcName; return TRUE; }
BOOL CxxParser::GetValueAttribute( PROPSPEC & ps ) { for ( ; _iVal <= Lines && 0 == _aVal[_iVal]; _iVal++ ) continue;
if ( _iVal > Lines ) return FALSE; else { ps = _psVal[_iVal];
return TRUE; } }
PROPVARIANT * CxxParser::GetValue() { if ( _iVal > Lines ) return 0;
CPropVar * pTemp = _aVal[_iVal]; _aVal[_iVal] = 0; _iVal++;
return (PROPVARIANT *)(void *)pTemp; }
|