/* * This file is part of Code::Blocks Studio, an open-source cross-platform IDE * Copyright (C) 2003 Yiannis An. Mandravellos * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Contact e-mail: Yiannis An. Mandravellos * Program URL : http://www.codeblocks.org * * $Id: parserthread.cpp,v 1.22.2.1 2005/10/25 07:59:01 mandrav Exp $ * $Date: 2005/10/25 07:59:01 $ */ #include #include "parserthread.h" #include #include #include #include #include int THREAD_START = wxNewId(); int THREAD_END = wxNewId(); int NEW_TOKEN = wxNewId(); int FILE_NEEDS_PARSING = wxNewId(); ParserThread::ParserThread(wxEvtHandler* parent,bool* abortflag, const wxString& bufferOrFilename, bool isLocal, ParserThreadOptions& options, TokensArray* tokens) : m_pParent(parent), m_pTokens(tokens), m_pLastParent(0L), m_IsLocal(isLocal), m_StartBlockIndex(0), m_Options(options) { // m_pAbort=abortflag; //ctor m_Tokens.m_Options.wantPreprocessor = options.wantPreprocessor; if (!bufferOrFilename.IsEmpty()) { if (!options.useBuffer) { m_Filename = bufferOrFilename; m_Tokens.Init(m_Filename); } else m_Tokens.InitFromBuffer(bufferOrFilename); } m_LastScope = tsUndefined; } ParserThread::~ParserThread() { //dtor } void ParserThread::Log(const wxString& log) { wxCommandEvent event(wxEVT_COMMAND_MENU_SELECTED, NEW_TOKEN); event.SetString(log); event.SetInt(m_Tokens.GetLineNumber()); wxPostEvent(m_pParent, event); wxYield(); } void ParserThread::SetTokens(TokensArray* tokens) { m_pTokens = tokens; } void* ParserThread::DoRun() { // wxCommandEvent event(wxEVT_COMMAND_MENU_SELECTED, THREAD_START); // event.SetString(m_Filename); // event.SetInt((int)this); // wxPostEvent(m_pParent, event); // //// Log("ParserThread running for " + m_Filename); // Parse(); // // wxCommandEvent event1(wxEVT_COMMAND_MENU_SELECTED, THREAD_END); // event1.SetString(m_Filename); // event1.SetInt((int)this); // wxPostEvent(m_pParent, event1); return 0L; } wxChar ParserThread::SkipToOneOfChars(const wxString& chars, bool supportNesting) { unsigned int level = m_Tokens.GetNestingLevel(); while (1) { wxString token = m_Tokens.GetToken(); if (token.IsEmpty()) return '\0'; // eof if (!supportNesting || (supportNesting && m_Tokens.GetNestingLevel() == level)) { wxChar ch = token.GetChar(0); if (chars.Find(ch) != wxNOT_FOUND) return ch; } } } void ParserThread::SkipBlock() { // skip tokens until we reach } // block nesting is taken into consideration too ;) // this is the nesting level we start at // we subtract 1 because we 're already inside the block // (since we 've read the {) unsigned int level = m_Tokens.GetNestingLevel() - 1; while (1) { wxString token = m_Tokens.GetToken(); if (token.IsEmpty()) break; // eof // if we reach the initial nesting level, we are done if (level == m_Tokens.GetNestingLevel()) break; } } void ParserThread::SkipAngleBraces() { int nestLvl = 0; while (true) { wxString tmp = m_Tokens.GetToken(); if (tmp.Matches(_T("<"))) ++nestLvl; else if (tmp.Matches(_T(">"))) --nestLvl; else if (tmp.Matches(_T(";"))) { // unget token - leave ; on the stack m_Tokens.UngetToken(); break; } else if (tmp.IsEmpty()) break; if (nestLvl <= 0) break; } } bool ParserThread::ParseBufferForFunctions(const wxString& buffer) { if (!m_pTokens) return false; m_pTokens->Clear(); m_Tokens.InitFromBuffer(buffer); if (!m_Tokens.IsOK()) return false; m_Str.Clear(); m_EncounteredNamespaces.Clear(); while (1) { if (!m_pTokens || TestDestroy()) return false; wxString token = m_Tokens.GetToken(); if (token.IsEmpty()) break; #if 0 if (!m_Str.IsEmpty()) Log(m_Str); #endif #if 0 if (!token.IsEmpty()) Log(token); #endif if (token.Matches(_T(";"))) { m_Str.Clear(); } else if (token.Matches(_T("{"))) { SkipBlock(); m_Str.Clear(); } else if (token.Matches(_T("}"))) { m_Str.Clear(); } // else if (token.Matches("::")) // { // m_Str.Clear(); // } else if (token.Matches(_T("typedef")) || token.Matches(_T(":"))) { SkipToOneOfChars(_T(";}"), true); m_Str.Clear(); } else if (token.Matches(_T("extern")) || token.StartsWith(_T("__asm"))) { SkipToOneOfChars(_T(";")); //m_Str.Clear(); } else if (token.Matches(_T("#"))) { m_Tokens.GetToken(); m_Tokens.GetToken(); m_Str.Clear(); } else { wxString peek = m_Tokens.PeekToken(); if (!peek.IsEmpty()) { if (peek.GetChar(0) == '(') { // function // ignore some well-known wxWindows macros if (token.Matches(_T("BEGIN_EVENT_TABLE"))) { // skip till after END_EVENT_TABLE while (!token.IsEmpty() && !token.Matches(_T("END_EVENT_TABLE"))) token = m_Tokens.GetToken(); // skip args m_Tokens.GetToken(); // skip args } else if (!token.Matches(_T("*_EVENT_TABLE")) && !token.Matches(_T("IMPLEMENT_APP")) && !token.Matches(_T("WX_DECLARE_*")) && !token.Matches(_T("WX_DEFINE_*"))) { if (m_Str.GetChar(0) == '~') { token = _T('~') + token; m_Str.Clear(); } HandleFunction(token); } else m_Tokens.GetToken(); // skip args } else { m_Str << token << _T(" "); } } } } return true; } bool ParserThread::Parse() { if (!m_pTokens) return false; #if 0 if (!m_Options.useBuffer) Log("Parsing " + m_Filename); #endif if (!m_Tokens.IsOK()) { //Log("Cannot parse " + m_Filename); return false; } if (m_Options.useBuffer) m_StartBlockIndex = m_pTokens->GetCount(); else m_StartBlockIndex = 0; m_Str.Clear(); m_LastToken.Clear(); m_EncounteredNamespaces.Clear(); while (1) { if (!m_pTokens || TestDestroy()) break; wxString token = m_Tokens.GetToken(); if (token.IsEmpty()) break; #if 0 if (!m_Str.IsEmpty()) Log(m_Str); #endif #if 0 if (!token.IsEmpty()) Log(token); #endif if (token.Matches(_T(";"))) { m_Str.Clear(); } else if (token.Matches(_T("delete")) || token.Matches(_T(".")) || (token.Matches(_T(">")) && m_LastToken.Matches(_T("-")))) { m_Str.Clear(); SkipToOneOfChars(_T(";}")); } else if (token.Matches(_T("{"))) { if (!m_Options.useBuffer || m_Options.bufferSkipBlocks) SkipBlock(); m_Str.Clear(); } else if (token.Matches(_T("}"))) { m_pLastParent = 0L; m_LastScope = tsUndefined; m_Str.Clear(); // the only time we get to find a } is when recursively called by e.g. HandleClass // we have to return now... if (!m_Options.useBuffer) break; } else if (token.Matches(_T(":"))) { if (m_LastToken.Matches(_T("public"))) m_LastScope = tsPublic; else if (m_LastToken.Matches(_T("protected"))) m_LastScope = tsProtected; else if (m_LastToken.Matches(_T("private"))) m_LastScope = tsPrivate; m_Str.Clear(); } else if (token.Matches(_T("while")) || token.Matches(_T("if")) || token.Matches(_T("do")) || token.Matches(_T("else")) || token.Matches(_T("for")) || token.Matches(_T("switch"))) { if (!m_Options.useBuffer || m_Options.bufferSkipBlocks) SkipToOneOfChars(_T(";}"), true); else m_Tokens.GetToken(); //skip args m_Str.Clear(); } else if (token.Matches(_T("typedef")) || token.Matches(_T("return")) || token.Matches(_T(":"))) { SkipToOneOfChars(_T(";}"), true); m_Str.Clear(); } else if (token.Matches(_T("extern"))) { // check for "C" m_Str = m_Tokens.GetToken(); if (m_Str.Matches(_T("\"C\""))) { m_Tokens.GetToken(); // "eat" { Parse(); // time for recursion ;) } else SkipToOneOfChars(_T(";")); // skip externs // m_Tokens.UngetToken(); // nope, return the token back... m_Str.Clear(); } else if (token.StartsWith(_T("__asm"))) { SkipToOneOfChars(_T(";"), true); //m_Str.Clear(); } else if (token.Matches(_T("static")) || token.Matches(_T("virtual")) || token.Matches(_T("inline"))) { // do nothing (skip it) //m_Str.Clear(); } else if (token.Matches(_T("#"))) { token = m_Tokens.GetToken(); if (token.Matches(_T("include"))) HandleIncludes(); else if (token.Matches(_T("define"))) HandleDefines(); m_Str.Clear(); } else if (token.Matches(_T("using"))) // using namespace ? { SkipToOneOfChars(_T(";}"), true); m_Str.Clear(); } else if (token.Matches(_T("namespace"))) { m_Str.Clear(); HandleNamespace(); } else if (token.Matches(_T("template"))) { m_Str.Clear(); SkipToOneOfChars(_T(">;"), true); } else if (token.Matches(_T("class"))) { m_Str.Clear(); HandleClass(); } else if (token.Matches(_T("struct"))) { m_Str.Clear(); HandleClass(false); } else if (token.Matches(_T("enum"))) { m_Str.Clear(); HandleEnum(); } else if (token.Matches(_T("union"))) { SkipToOneOfChars(_T("{;")); // if (m_Tokens.GetToken() == "{") { Token* oldparent = m_pLastParent; Parse(); m_Str.Clear(); m_pLastParent = oldparent; } } #if 1 else if (token.Matches(_T("operator"))) { wxString func = token; while (1) { token = m_Tokens.GetToken(); if (!token.IsEmpty()) { if (token.GetChar(0) == '(') { // check for operator()() wxString peek = m_Tokens.PeekToken(); if (!peek.IsEmpty() && peek.GetChar(0) != '(') m_Tokens.UngetToken(); else func << token; break; } else func << token; } else break; } HandleFunction(func, true); m_Str.Clear(); } #endif else { wxString peek = m_Tokens.PeekToken(); if (!peek.IsEmpty()) { if (peek.GetChar(0) == '(' && !m_Options.useBuffer) { if (!m_Options.useBuffer || m_Options.bufferSkipBlocks) { // function // ignore some well-known wxWindows macros if (token.Matches(_T("BEGIN_EVENT_TABLE"))) { // skip till after END_EVENT_TABLE while (!token.IsEmpty() && !token.Matches(_T("END_EVENT_TABLE"))) token = m_Tokens.GetToken(); // skip args m_Tokens.GetToken(); // skip args } else if (!token.Matches(_T("*_EVENT_TABLE*")) && !token.Matches(_T("IMPLEMENT_APP")) && !token.Matches(_T("IMPLEMENT_DYNAMIC_CLASS")) && !token.Matches(_T("WX_DECLARE_*")) && !token.Matches(_T("WX_DEFINE_*"))) { // Log("m_Str='"+m_Str+"'"); // Log("token='"+token+"'"); // Log("peek='"+peek+"'"); HandleFunction(token); } else m_Tokens.GetToken(); // skip args } else m_Tokens.GetToken(); // eat args when parsing block m_Str.Clear(); } else if (peek.Matches(_T(","))) { // example decl to encounter a comma: int x,y,z; // token should hold the var (x/y/z) // m_Str should hold the type (int) DoAddToken(tkVariable, token); // skip comma (we had peeked it) m_Tokens.GetToken(); } else if (peek.Matches(_T("<"))) { // a template, e.g. someclass::memberfunc // we have to skip <>, so we 're left with someclass::memberfunc SkipAngleBraces(); peek = m_Tokens.PeekToken(); if (peek.Matches(_T("::"))) { // 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(); }