Developer forums (C::B DEVELOPMENT STRICTLY!) > CodeCompletion redesign

Is it possible for the parser to support newlib prototypes?

<< < (6/12) > >>

Huki:
I have looked at the patch for function pointers that I mentioned earlier:

--- Quote from: Huki on September 06, 2014, 08:56:12 am ---
--- Quote from: ollydbg on September 06, 2014, 08:02:14 am ---I try to test this code with your patch:

--- Code: ---void my_f(int b);

void (*foo)(int a);

foo = &my_f;

foo(5);

--- End code ---

But I don't have tip info if the mouse hover on the "foo(5)".
Find declaration of "foo(5)" gives nothing.


--- End quote ---
I think normal function pointer variables parsing require another patch (I will send it next, but it requires some cleanup before).
--- End quote ---

Function pointers are handled just like normal functions, but of course, they only have a declaration, not definition. So I've updated HandleFunctions() to handle this as a special case.

This patch adds support for func ptr both in global and local block. Also, all the cases involving opbracket_chr are handled in one place, in this order:

--- Code: ---void(*foo)(int a); // func ptr
void *(*foo)(int a); // func ptr with ptr return type
void foo(int a); // function decl
AAA foo(5); // var initialized with ctor (only supported for local block)
--- End code ---

You can use your earlier test code (quoted above) to test this patch. Let me know if it's ok.

ollydbg:

--- Quote from: Huki on September 11, 2014, 11:32:17 am ---I have looked at the patch for function pointers that I mentioned earlier:

--- Quote from: Huki on September 06, 2014, 08:56:12 am ---
--- Quote from: ollydbg on September 06, 2014, 08:02:14 am ---I try to test this code with your patch:

--- Code: ---void my_f(int b);

void (*foo)(int a);

foo = &my_f;

foo(5);

--- End code ---

But I don't have tip info if the mouse hover on the "foo(5)".
Find declaration of "foo(5)" gives nothing.


--- End quote ---
I think normal function pointer variables parsing require another patch (I will send it next, but it requires some cleanup before).
--- End quote ---

Function pointers are handled just like normal functions, but of course, they only have a declaration, not definition. So I've updated HandleFunctions() to handle this as a special case.

This patch adds support for func ptr both in global and local block. Also, all the cases involving opbracket_chr are handled in one place, in this order:

--- Code: ---void(*foo)(int a); // func ptr
void *(*foo)(int a); // func ptr with ptr return type
void foo(int a); // function decl
AAA foo(5); // var initialized with ctor (only supported for local block)
--- End code ---

You can use your earlier test code (quoted above) to test this patch. Let me know if it's ok.


--- End quote ---
Thanks, I reviewed and tested the patch, works fine, thanks! Here is my modified patch. (I just add some comments)

--- Code: --- src/plugins/codecompletion/parser/parser.cpp       |   2 +-
 src/plugins/codecompletion/parser/parserthread.cpp | 101 +++++++++++++++++++--
 src/plugins/codecompletion/parser/parserthread.h   |   3 +-
 3 files changed, 94 insertions(+), 12 deletions(-)

diff --git a/src/plugins/codecompletion/parser/parser.cpp b/src/plugins/codecompletion/parser/parser.cpp
index feeec27..a7886bd 100644
--- a/src/plugins/codecompletion/parser/parser.cpp
+++ b/src/plugins/codecompletion/parser/parser.cpp
@@ -631,7 +631,7 @@ bool Parser::ParseBuffer(const wxString& buffer,   bool isLocal,
     opts.wantPreprocessor     = m_Options.wantPreprocessor;
     opts.parseComplexMacros   = false;
 
-    opts.handleFunctions      = false;
+    opts.handleFunctions      = true;   // enabled to support function ptr in local block
 
     opts.storeDocumentation   = m_Options.storeDocumentation;
 
diff --git a/src/plugins/codecompletion/parser/parserthread.cpp b/src/plugins/codecompletion/parser/parserthread.cpp
index d408270..5d5ab6c 100644
--- a/src/plugins/codecompletion/parser/parserthread.cpp
+++ b/src/plugins/codecompletion/parser/parserthread.cpp
@@ -1004,6 +1004,8 @@ void ParserThread::DoParse()
         }
         else if (!switchHandled)
         {
+            // since we can't recognize the pattern by token, then the token
+            // is normally an identifier style lexme, now we try to peek the next token
             wxString peek = m_Tokenizer.PeekToken();
             if (!peek.IsEmpty())
             {
@@ -1014,8 +1016,9 @@ void ParserThread::DoParse()
                     && m_EncounteredTypeNamespaces.empty()
                     && (!m_LastParent || m_LastParent->m_Name != token) ) // if func has same name as current scope (class)
                 {
+                    // pattern: AAA (...)
                     int id = m_TokenTree->TokenExists(token, -1, tkMacroDef);
-
+                    // if AAA is a macro definition, then expand this macro
                     if (id != -1)
                     {
                         HandleMacroExpansion(id, peek);
@@ -1023,17 +1026,31 @@ void ParserThread::DoParse()
                     }
                     else
                     {
+                        // see what is inside the (...)
                         wxString arg = m_Tokenizer.GetToken(); // eat args ()
+                        // try to see whether the peek pattern is (* BBB)
                         int pos = peek.find(ParserConsts::ptr);
                         if (pos != wxNOT_FOUND)
                         {
                             peek = m_Tokenizer.PeekToken();
                             if (peek.GetChar(0) == ParserConsts::opbracket_chr)
                             {
+                                // pattern: AAA (* BBB) (...)
+                                // where peek is (...) and arg is (* BBB)
                                 arg.Trim(true).RemoveLast();
                                 arg.Remove(0, pos+1);
-                                if (!m_Options.useBuffer || m_Options.bufferSkipBlocks)
-                                    HandleFunction(arg); // function
+                                arg.Trim(true).Trim(false);
+                                // NOTE: support func ptr in local block, show return type.
+                                // if (!m_Options.useBuffer || m_Options.bufferSkipBlocks)
+                                //     HandleFunction(arg); // function
+                                // AAA now becomes the last element of stacked type string
+                                // which is the return type of function ptr
+                                m_Str << token << ParserConsts::space_chr;
+                                // * BBB is now the function ptr's name
+                                HandleFunction(/*function name*/ arg,
+                                               /*isOperator*/    false,
+                                               /*isPointer*/     true);
+                                m_Str.Clear();
                             }
                         }
                         else // wxString arg = m_Tokenizer.GetToken(); // eat args ()
@@ -1042,10 +1059,50 @@ void ParserThread::DoParse()
                 }
                 else if (peek.GetChar(0) == ParserConsts::opbracket_chr && m_Options.handleFunctions)
                 {
-                    if (!m_Options.useBuffer || m_Options.bufferSkipBlocks)
+                    // NOTE: support some more cases..., such as m_Str is not empty
+                    // if (!m_Options.useBuffer || m_Options.bufferSkipBlocks)
+                    //     HandleFunction(token); // function
+                    // else
+                    //     m_Tokenizer.GetToken(); // eat args when parsing block
+
+                    // function ptr with pointer return type
+                    // eg: void *(*Alloc)(void *p, size_t size);
+                    // where, m_Str=void, token=(*Alloc), peek=(void *p, size_t size)
+                    if (   (m_LastToken == ParserConsts::ref_chr || m_LastToken == ParserConsts::ptr_chr) // (m_PointerOrRef)
+                        && (token.GetChar(0) == ParserConsts::opbracket_chr))
+                    {
+                        int pos = token.find(ParserConsts::ptr);
+                        if (pos != wxNOT_FOUND)
+                        {
+                            wxString arg = token;
+                            arg.Trim(true).RemoveLast();
+                            arg.Remove(0, pos+1);
+                            arg.Trim(true).Trim(false);
+                            HandleFunction(/*function name*/ arg,
+                                           /*isOperator*/    false,
+                                           /*isPointer*/     true);
+                        }
+                    }
+                    else if (!m_Options.useBuffer || m_Options.bufferSkipBlocks)
+                    {
+                        // pattern AAA BBB (...) in global namespace (not in local block)
+                        // so, this is mostly like a function declaration, but in-fact this
+                        // can also be a global variable initializized with ctor, but for
+                        // simplicity, we drop the later case
                         HandleFunction(token); // function
+                    }
                     else
+                    {
+                        // local variables initialized with ctor
+                        if (!m_Str.IsEmpty() && m_Options.handleVars)
+                        {
+                            Token* newToken = DoAddToken(tkVariable, token, m_Tokenizer.GetLineNumber());
+                            if (newToken && !m_TemplateArgument.IsEmpty())
+                                ResolveTemplateArgs(newToken);
+                        }
                         m_Tokenizer.GetToken(); // eat args when parsing block
+                    }
+
                     m_Str.Clear();
                 }
                 else if (   (peek  == ParserConsts::colon)
@@ -1113,15 +1170,15 @@ void ParserThread::DoParse()
                         m_EncounteredNamespaces.push(token);
                     m_Tokenizer.GetToken(); // eat ::
                 }
-                else if (   (peek == ParserConsts::semicolon)
-                         || (   (   m_Options.useBuffer
-                                 && (peek.GetChar(0) == ParserConsts::opbracket_chr) )
-                             && (!m_Str.Contains(ParserConsts::dcolon)) ) )
+                // NOTE: opbracket_chr already handled above
+                else if (peek==ParserConsts::semicolon)
                 {
                     if (   !m_Str.IsEmpty()
                         && (    wxIsalpha(token.GetChar(0))
                             || (token.GetChar(0) == ParserConsts::underscore_chr) ) )
                     {
+                        // pattern: m_Str AAA;
+                        // where AAA is the variable name, m_Str contains type string
                         if (m_Options.handleVars)
                         {
                             Token* newToken = DoAddToken(tkVariable, token, m_Tokenizer.GetLineNumber());
@@ -1597,7 +1654,7 @@ void ParserThread::HandleDefines()
     wxString para; // function-like macro's args
     if (!readToEOL.IsEmpty())
     {
-        // a '(' char follow the macro name (without space between them) is regard as a
+        // a '(' char follow the macro name (without space between them) is regard as a
         // function like macro definition
         if (readToEOL[0] == ParserConsts::opbracket_chr) // function-like macro definition
         {
@@ -2091,7 +2148,7 @@ void ParserThread::HandleClass(EClassType ct)
     m_Tokenizer.SetState(oldState);
 }
 
-void ParserThread::HandleFunction(const wxString& name, bool isOperator)
+void ParserThread::HandleFunction(const wxString& name, bool isOperator, bool isPointer)
 {
     TRACE(_T("HandleFunction() : Adding function '")+name+_T("': m_Str='")+m_Str+_T("'"));
     int lineNr = m_Tokenizer.GetLineNumber();
@@ -2099,6 +2156,30 @@ void ParserThread::HandleFunction(const wxString& name, bool isOperator)
     wxString peek = m_Tokenizer.PeekToken();
     TRACE(_T("HandleFunction() : name='")+name+_T("', args='")+args+_T("', peek='")+peek+_T("'"));
 
+    // special case for function pointers
+    if (isPointer)
+    {
+        // pattern: m_Str AAA (*BBB) (...);
+        if (peek == ParserConsts::semicolon)
+        {
+            TRACE(_T("HandleFunction() : Add token name='")+name+_T("', args='")+args+_T("', return type='") + m_Str+ _T("'"));
+            Token* newToken =  DoAddToken(tkFunction, name, lineNr, 0, 0, args);
+            if (newToken)
+            {
+                newToken->m_IsConst = false;
+                newToken->m_TemplateArgument = m_TemplateArgument;
+                if (!m_TemplateArgument.IsEmpty() && newToken->m_TemplateMap.empty())
+                    ResolveTemplateArgs(newToken);
+            }
+            else
+            {
+                TRACE(_T("HandleFunction() : Unable to create/add new token: ") + name);
+            }
+            m_TemplateArgument.Clear();
+        }
+        return;
+    }
+
     if (!m_Str.StartsWith(ParserConsts::kw_friend))
     {
         int lineStart = 0;
diff --git a/src/plugins/codecompletion/parser/parserthread.h b/src/plugins/codecompletion/parser/parserthread.h
index d81b752..7421177 100644
--- a/src/plugins/codecompletion/parser/parserthread.h
+++ b/src/plugins/codecompletion/parser/parserthread.h
@@ -200,8 +200,9 @@ protected:
     /** handle function declaration or definition
       * @param name function name
       * @param isOperator if true, means it is an operator overload function
+      * @param isPointer if true, means it is a function pointer
       */
-    void HandleFunction(const wxString& name, bool isOperator = false);
+    void HandleFunction(const wxString& name, bool isOperator = false, bool isPointer = false);
 
     /** parse for loop arguments:
       * for(int X; ... ; ...)


--- End code ---

If you are OK,  I'm going to commit.

BTW: by the way, maybe, the two condition:


--- Code: ---                if (   (peek.GetChar(0) == ParserConsts::opbracket_chr)
                    && m_Options.handleFunctions
                    && m_Str.IsEmpty()
                    && m_EncounteredNamespaces.empty()
                    && m_EncounteredTypeNamespaces.empty()
                    && (!m_LastParent || m_LastParent->m_Name != token) )
                {
                       Handle pattern AAA(...)
                       -> maybe, it is a macro usage
                       -> maybe, it is a function ptr
                }

--- End code ---


--- Code: ---                else if (peek.GetChar(0) == ParserConsts::opbracket_chr && m_Options.handleFunctions)
                {
                       Handle pattern CCC::DDD  EEE::AAA(...)
                       or                  CCC::DDD* EEE::AAA(...)
                       -> maybe, it is a function declaration
                       -> maybe, it is a function ptr
                       -> maybe, it is a local variable initialization with ctor
                }

--- End code ---
Those two conditions can be merged or some refactored, but I'm not quite sure. E.g. extract the handling macro usage, and merge handling of function decl or function ptr in one condition. This can be a new commit.  :D

BTW2: coding style: when adding comment, I think "// NOTE" is better, there is a space after "//".


Huki:
Ok about the patch, so feel free to commit. :)


--- Quote ---BTW2: coding style: when adding comment, I think "// NOTE" is better, there is a space after "//".
--- End quote ---
Ok about that too.


--- Quote from: ollydbg on September 12, 2014, 04:15:54 am ---BTW: by the way, maybe, the two condition:
[...]
Those two conditions can be merged or some refactored, but I'm not quite sure. E.g. extract the handling macro usage, and merge handling of function decl or function ptr in one condition. This can be a new commit.  :D

--- End quote ---
I agree we can separate the macro handling and merge the function handling.
We can think about supporting more cases for macro handling too. We currently handle function-like macros, and only when m_Str is empty, eg:

--- Code: ---some code;
MACRO(); // expand this
more code;
--- End code ---
Sometimes the macro is not the first token in the line, so m_Str is non-empty, just like the bug reported by the original poster in this thread:

--- Code: ---FILE * _EXFUN(fopen, (const char *__restrict _name, const char *__restrict _type));
--- End code ---
Those can be handled.. and also variable-like macros..

ollydbg:

--- Quote from: Huki on September 12, 2014, 03:01:55 pm ---Ok about the patch, so feel free to commit. :)

--- End quote ---
Done. rev 9916, thanks!



--- Quote ---
--- Quote from: ollydbg on September 12, 2014, 04:15:54 am ---BTW: by the way, maybe, the two condition:
[...]
Those two conditions can be merged or some refactored, but I'm not quite sure. E.g. extract the handling macro usage, and merge handling of function decl or function ptr in one condition. This can be a new commit.  :D

--- End quote ---
I agree we can separate the macro handling and merge the function handling.
We can think about supporting more cases for macro handling too. We currently handle function-like macros, and only when m_Str is empty, eg:

--- Code: ---some code;
MACRO(); // expand this
more code;
--- End code ---
Sometimes the macro is not the first token in the line, so m_Str is non-empty, just like the bug reported by the original poster in this thread:

--- Code: ---FILE * _EXFUN(fopen, (const char *__restrict _name, const char *__restrict _type));
--- End code ---
Those can be handled.. and also variable-like macros..

--- End quote ---
Good catch, though it will increase the parsing time, but I think it is worthy. This can remove my own hack on the user defined macro replacement rule.

White-Tiger:
Yay! for the function pointer patch xD It finally works^^ (ok didn't test it, but I'm using them in different projects and they didn't work most of the time..)
It's weird though.. I've thought Code::Blocks was able to handle them as they sometimes seemed to work.. just didn't most of the time :P

Navigation

[0] Message Index

[#] Next page

[*] Previous page

Go to full version