📄 parserthread.cpp
字号:
// Log("peek='::', token='" + token + "', m_LastToken='" + m_LastToken + "', m_Str='" + m_Str + "'");
m_EncounteredNamespaces.Add(token);
m_Tokens.GetToken(); // eat ::
}
if (m_Str.IsEmpty())
{
m_Str = GetStringFromArray(m_EncounteredNamespaces, _T("::")) + token;
m_EncounteredNamespaces.Clear();
}
}
else if (peek.Matches(_T("::")))
{
// Log("peek='::', token='" + token + "', m_LastToken='" + m_LastToken + "', m_Str='" + m_Str + "'");
m_EncounteredNamespaces.Add(token);
m_Tokens.GetToken(); // eat ::
}
else if ((peek.Matches(_T(";")) || (m_Options.useBuffer && peek.GetChar(0) == _T('(')) && !m_Str.Contains(_T("::"))) && m_pTokens)
{
// Log("m_Str='"+m_Str+"'");
// Log("token='"+token+"'");
// Log("peek='"+peek+"'");
if (!m_Str.IsEmpty() && (isalpha(token.GetChar(0)) || token.GetChar(0) == '_'))
{
DoAddToken(tkVariable, token);
}
//m_Str.Clear();
}
else
{
m_Str << token << _T(" ");
}
}
}
m_LastToken = token;
}
return true;
}
Token* ParserThread::TokenExists(const wxString& name, Token* parent, short int kindMask)
{
if (!m_pTokens)
return 0;
if (!parent)
{
// when parsing a block, we must make sure the token does not already exist...
for (unsigned int i = m_StartBlockIndex; i < m_pTokens->GetCount(); ++i)
{
Token* token = m_pTokens->Item(i);
if ((token->m_TokenKind & kindMask) && token->m_Name.Matches(name))
return token;
}
}
else
{
// search only under the parent token
for (unsigned int i = 0; i < parent->m_Children.GetCount(); ++i)
{
Token* token = parent->m_Children.Item(i);
if ((token->m_TokenKind & kindMask) && token->m_Name.Matches(name))
return token;
}
}
return 0L;
}
wxString ParserThread::GetActualTokenType()
{
// we will compensate for spaces between
// namespaces (e.g. NAMESPACE :: SomeType) wich is valid C++ construct
// we 'll remove spaces that follow a semicolon
int pos = 0;
while (pos < (int)m_Str.Length())
{
if (m_Str.GetChar(pos) == ' ' &&
(
(pos > 0 && m_Str.GetChar(pos - 1) == ':') ||
(pos < (int)m_Str.Length() - 1 && m_Str.GetChar(pos + 1) == ':')
)
)
{
m_Str.Remove(pos, 1);
}
else
++pos;
}
// m_Str contains the full text before the token's declaration
// an example m_Str value would be: const wxString&
// what we do here is locate the actual return value (wxString in this example)
// it will be needed by code completion code ;)
pos = m_Str.Length() - 1;
// we walk m_Str backwards until we find a non-space character which also is
// not * or &
// const wxString&
// in this example, we would stop here ^
while (pos >= 0 &&
(isspace(m_Str.GetChar(pos)) ||
m_Str.GetChar(pos) == '*' ||
m_Str.GetChar(pos) == '&'))
--pos;
if (pos >= 0)
{
// we have the end of the word we 're interested in
int end = pos;
// continue walking backwards until we find the start of the word
// const wxString&
// in this example, we would stop here ^
while (pos >= 0 && (isalnum(m_Str.GetChar(pos)) || m_Str.GetChar(pos) == '_' || m_Str.GetChar(pos) == ':'))
--pos;
return m_Str.Mid(pos + 1, end - pos);
}
return wxEmptyString;
}
Token* ParserThread::DoAddToken(TokenKind kind, const wxString& name, const wxString& args, bool isOperator)
{
wxMutexLocker lock(s_mutexProtection);
if (m_Options.useBuffer && TokenExists(name))
return 0L;
Token* newToken = new Token;
m_Str.Trim();
if (kind == tkDestructor)
{
// special class destructors case
newToken->m_Name = _T("~") + name;
m_Str.Clear();
}
else
newToken->m_Name = name;
// check for implementation member function
Token* localParent = 0;
if (m_EncounteredNamespaces.GetCount())
{
unsigned int count = m_EncounteredNamespaces.GetCount();
for (unsigned int i = 0; i < count; ++i)
{
// Log("NS: '" + m_EncounteredNamespaces[i] + "' for " + newToken->m_Name);
localParent = TokenExists(m_EncounteredNamespaces[i], localParent, tkClass | tkNamespace);
if (!localParent)
break;
}
m_EncounteredNamespaces.Clear();
}
if (localParent)
{
// Log("Parent found for " + m_Str + " " + newToken->m_Name + ": " + localParent->m_DisplayName);
Token* existing = TokenExists(newToken->m_Name, localParent);
if (existing)
{
// Log("Existing found for " + newToken->m_Name);
// if the token exists, all we have to do is adjust the
// implementation file/line
existing->m_ImplFilename = m_Tokens.GetFilename();
existing->m_ImplLine = m_Tokens.GetLineNumber();
delete newToken;
return existing;
}
}
newToken->m_Type = m_Str;
newToken->m_ActualType = GetActualTokenType();
newToken->m_Args = args;
newToken->m_Scope = m_LastScope;
newToken->m_TokenKind = kind;
newToken->m_IsLocal = m_IsLocal;
newToken->m_pParent = m_pLastParent;
newToken->m_Filename = m_Tokens.GetFilename();
newToken->m_Line = m_Tokens.GetLineNumber();
newToken->m_ImplLine = 0;
newToken->m_IsOperator = isOperator;
newToken->m_IsTemporary = m_Options.useBuffer;
// Log("Added token " +name+ ", type '" +newToken->m_Type+ "', actual '" +newToken->m_ActualType+ "'");
if (m_pLastParent)
newToken->m_DisplayName << m_pLastParent->m_Name << _T("::");
newToken->m_DisplayName << newToken->m_Name << args;
if (!newToken->m_Type.IsEmpty())
newToken->m_DisplayName << _T(" : ") << newToken->m_Type;
if (m_pTokens)
m_pTokens->Add(newToken);
if (m_pLastParent)
m_pLastParent->AddChild(newToken);
return newToken;
}
void ParserThread::HandleIncludes()
{
wxString filename;
bool isGlobal = !m_IsLocal;
wxString token = m_Tokens.GetToken();
// now token holds something like:
// "someheader.h"
// < and will follow iostream.h, >
if (!token.IsEmpty())
{
if (token.GetChar(0) == '"')
{
// "someheader.h"
token.Replace(_T("\""), _T(""));
filename = token;
}
else if (token.GetChar(0) == '<')
{
isGlobal = true;
// next token is filename, next is . (dot), next is extension
// basically we 'll loop until >
while (1)
{
token = m_Tokens.GetToken();
if (token.IsEmpty())
break;
if (token.GetChar(0) != '>')
filename << token;
else
break;
}
}
}
if (!filename.IsEmpty())
{
wxCommandEvent event(wxEVT_COMMAND_MENU_SELECTED, FILE_NEEDS_PARSING);
event.SetString(m_Filename + _T("+") + filename);
// setting all #includes as global
// it's amazing how many projects use #include "..." for global headers (MSVC mainly - booh)
event.SetInt(1);//isGlobal);
// wxPostEvent(m_pParent, event);
// since we 'll be calling directly the parser's method, let's make it thread-safe
static wxMutex lock;
wxMutexLocker l(lock);
m_pParent->ProcessEvent(event);
}
}
void ParserThread::HandleDefines()
{
wxString filename;
wxString token = m_Tokens.GetToken();
m_Str.Clear();
// now token holds something like:
// BLAH_BLAH
if (!token.IsEmpty())
{
// make sure preprocessor definitions are not going under namespaces or classes!
Token* oldParent = m_pLastParent;
m_pLastParent = 0L;
Token* newToken = DoAddToken(tkPreprocessor, token);
if (newToken)
newToken->m_Line -= 1; // preprocessor definitions need correction for the line number
if (m_Tokens.PeekToken().GetChar(0) == '(') // TODO: find better way...
m_Tokens.GetToken(); // eat args
m_pLastParent = oldParent;
}
}
void ParserThread::HandleNamespace()
{
wxString ns = m_Tokens.GetToken();
wxString next = m_Tokens.PeekToken();
if (next.Matches(_T("{")))
{
// use the existing copy (if any)
Token* newToken = TokenExists(ns, 0, tkNamespace);
if (!newToken)
newToken = DoAddToken(tkNamespace, ns);
if (!newToken)
return;
m_Tokens.GetToken(); // eat {
Token* lastParent = m_pLastParent;
TokenScope lastScope = m_LastScope;
m_pLastParent = newToken;
// default scope is: public for namespaces (actually no, but emulate it)
m_LastScope = tsPublic;
Parse();
m_pLastParent = lastParent;
m_LastScope = lastScope;
}
else
SkipToOneOfChars(_T(";{")); // some kind of error in code ?
}
void ParserThread::HandleClass(bool isClass)
{
int lineNr = m_Tokens.GetLineNumber();
wxString ancestors;
while (1)
{
wxString current = m_Tokens.GetToken();
wxString next = m_Tokens.PeekToken();
if (!current.IsEmpty() && !next.IsEmpty())
{
if (next.Matches(_T("<"))) // template specialization
{
SkipAngleBraces();
next = m_Tokens.PeekToken();
}
if (next.Matches(_T(":"))) // has ancestor(s)
{
//Log("Class " + current + " has ancestors");
m_Tokens.GetToken(); // eat ":"
while (1)
{
wxString tmp = m_Tokens.GetToken();
next = m_Tokens.PeekToken();
if (!tmp.Matches(_T("public")) &&
!tmp.Matches(_T("protected")) &&
!tmp.Matches(_T("private")) &&
!tmp.Matches(_T(">")) &&
!tmp.Matches(_T(",")))
{
// fix for namespace usage in ancestors
if (tmp.Matches(_T("::")) || next.Matches(_T("::")))
ancestors << tmp;
else
ancestors << tmp << _T(',');
//Log("Adding ancestor " + tmp);
}
if (next.IsEmpty() ||
next.Matches(_T("{")) ||
next.Matches(_T(";")))
break;
else if (next.Matches(_T("<")))
{
// template class
int nest = 0;
m_Tokens.GetToken(); // eat "<"
while (1)
{
wxString tmp1 = m_Tokens.GetToken();
if (tmp1.Matches(_T("<")))
++nest;
else if (tmp1.Matches(_T(">")))
--nest;
if (tmp1.IsEmpty() ||
tmp1.Matches(_T("{")) ||
tmp1.Matches(_T(";")) ||
(tmp1.Matches(_T(">")) && nest <= 0))
{
m_Tokens.UngetToken(); // put it back before exiting
break;
}
}
}
}
//Log("Ancestors: " + ancestors);
}
if (current.Matches(_T("{"))) // unnamed class/struct
{
Token* lastParent = m_pLastParent;
TokenScope lastScope = m_LastScope;
// default scope is: private for classes, public for structs
m_LastScope = isClass ? tsPrivate : tsPublic;
Parse();
m_pLastParent = lastParent;
m_LastScope = lastScope;
break;
}
else if (next.Matches(_T("{"))) // no ancestor(s)
{
Token* newToken = DoAddToken(tkClass, current);
if (!newToken)
return;
newToken->m_Line = lineNr; // correct line number (might be messed if class has ancestors)
newToken->m_AncestorsString = ancestors;
m_Tokens.GetToken(); // eat {
Token* lastParent = m_pLastParent;
TokenScope lastScope = m_LastScope;
m_pLastParent = newToken;
// default scope is: private for classes, public for structs
m_LastScope = isClass ? tsPrivate : tsPublic;
Parse();
m_pLastParent = lastParent;
m_LastScope = lastScope;
break;
}
else if (next.Matches(_T(";"))) // forward decl; we don't care
break;
else if (next.GetChar(0) == '(') // function: struct xyz& DoSomething()...
{
HandleFunction(current);
break;
}
}
else
break;
}
}
void ParserThread::HandleFunction(const wxString& name, bool isOperator)
{
//Log("Adding function '"+name+"': m_Str='"+m_Str+"'");
wxString args = m_Tokens.GetToken();
if (!m_Str.StartsWith(_T("friend")))
{
TokenKind kind = tkFunction;
bool CtorDtor = m_pLastParent && name.Matches(m_pLastParent->m_Name);
if (!CtorDtor)
{
// check for m_EncounteredNamespaces
unsigned int count = m_EncounteredNamespaces.GetCount();
if (count)
{
Token* localParent = 0;
for (unsigned int i = 0; i < count; ++i)
{
localParent = TokenExists(m_EncounteredNamespaces[i], localParent, tkClass | tkNamespace);
if (!localParent)
break;
}
CtorDtor = localParent && name.Matches(localParent->m_Name);
}
}
if (CtorDtor)
{
m_Str.Trim();
if (m_Str.IsEmpty())
kind = tkConstructor;
else if (m_Str.Matches(_T("~")))
kind = tkDestructor;
}
// Log("Adding function '"+name+"': m_Str='"+m_Str+"'"+", enc_ns="+(m_EncounteredNamespaces.GetCount()?m_EncounteredNamespaces[0]:"nil"));
DoAddToken(kind, name, args, isOperator);
}
if (!m_Tokens.PeekToken().Matches(_T("}")))
SkipToOneOfChars(_T(";}"), true);
}
void ParserThread::HandleEnum()
{
// enums have the following rough definition:
// enum [xxx] { type1 name1 [= 1][, [type2 name2 [= 2]]] };
bool isUnnamed = false;
wxString token = m_Tokens.GetToken();
if (token.IsEmpty())
return;
else if (token.Matches(_T("{")))
{
// we have an un-named enum
token = _T("Un-named");
m_Tokens.UngetToken(); // return '{' back
isUnnamed = true;
}
Token* newEnum = 0L;
unsigned int level = 0;
if (isalpha(token.GetChar(0)))
{
if (m_Tokens.PeekToken().GetChar(0) != '{')
return;
if (isUnnamed)
{
// for unnamed enums, look if we already have "Unnamed", so we don't
// add a new one for every unnamed enum we encounter, in this scope...
newEnum = TokenExists(token, m_pLastParent, tkEnum);
}
if (!newEnum) // either named or first unnamed enum
newEnum = DoAddToken(tkEnum, token);
level = m_Tokens.GetNestingLevel();
m_Tokens.GetToken(); // skip {
}
else
{
if (token.GetChar(0) != '{')
return;
level = m_Tokens.GetNestingLevel() - 1; // we 've already entered the { block
}
while (1)
{
// process enumerators
token = m_Tokens.GetToken();
wxString peek = m_Tokens.PeekToken();
if (token.IsEmpty() || peek.IsEmpty())
return; //eof
if (token.Matches(_T("}")) && level == m_Tokens.GetNestingLevel())
break;
// assignments (=xxx) are ignored by the tokenizer,
// so we don't have to worry about them here ;)
if (peek.Matches(_T(",")) || peek.Matches(_T("}")) || peek.Matches(_T(":")))
{
// this "if", avoids non-valid enumerators
// like a comma (if no enumerators follow)
if (isalpha(token.GetChar(0)))
{
Token* lastParent = m_pLastParent;
m_pLastParent = newEnum;
DoAddToken(tkEnumerator, token);
m_pLastParent = lastParent;
}
if (peek.Matches(_T(":")))
{
// bit specifier (eg, xxx:1)
//walk to , or }
SkipToOneOfChars(_T(",}"));
}
}
}
// skip to ;
token = m_Tokens.GetToken();
while (!token.IsEmpty() && !token.Matches(_T(";")))
token = m_Tokens.GetToken();
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -