Author Topic: Is it possible for the parser to support newlib prototypes?  (Read 64403 times)

Offline ollydbg

  • Developer
  • Lives here!
  • *****
  • Posts: 5910
  • OpenCV and Robotics
    • Chinese OpenCV forum moderator
Re: Is it possible for the parser to support newlib prototypes?
« Reply #30 on: September 14, 2014, 02:28:32 pm »
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
Does the trunk fix your but report here Re: The 06 July 2014 build (9844) is out.?
If some piece of memory should be reused, turn them to variables (or const variables).
If some piece of operations should be reused, turn them to functions.
If they happened together, then turn them to classes.

Offline White-Tiger

  • Multiple posting newcomer
  • *
  • Posts: 83
Re: Is it possible for the parser to support newlib prototypes?
« Reply #31 on: September 14, 2014, 04:30:30 pm »
yes an no.. the reported case works, same for the real world code I based it on... (at least it looks like that, didn't miss any function)
But another case I've used to work with, does not work.

I'll post the almost non-stripped code just in case there's another "bug" (had to strip it a bit because of 20k character limit for posts^^)
Code: c++
#include <stdint.h>
typedef uint64_t uint64;
typedef unsigned short anyID;
#define TS3_VECTOR anyID
enum LogLevel{a,b,c,};
#define PluginMessageTarget LogLevel
#define PluginItemType LogLevel
#define PluginGuiProfile LogLevel
#define PluginConnectTab LogLevel


struct TS3Functions {
unsigned int (*getClientLibVersion)(char** result);
unsigned int (*getClientLibVersionNumber)(uint64* result);
unsigned int (*spawnNewServerConnectionHandler)(int port, uint64* result);
unsigned int (*destroyServerConnectionHandler)(uint64 serverConnectionHandlerID);

/* Error handling */
unsigned int (*getErrorMessage)(unsigned int errorCode, char** error);

/* Memory management */
unsigned int (*freeMemory)(void* pointer);

/* Logging */
unsigned int (*logMessage)(const char* logMessage, enum LogLevel severity, const char* channel, uint64 logID);

/* Sound */
unsigned int (*getPlaybackDeviceList)(const char* modeID, char**** result);
unsigned int (*getPlaybackModeList)(char*** result);
unsigned int (*getCaptureDeviceList)(const char* modeID, char**** result);
unsigned int (*getCaptureModeList)(char*** result);
unsigned int (*getDefaultPlaybackDevice)(const char* modeID, char*** result);
unsigned int (*getDefaultPlayBackMode)(char** result);
unsigned int (*getDefaultCaptureDevice)(const char* modeID, char*** result);
unsigned int (*getDefaultCaptureMode)(char** result);
unsigned int (*openPlaybackDevice)(uint64 serverConnectionHandlerID, const char* modeID, const char* playbackDevice);
unsigned int (*openCaptureDevice)(uint64 serverConnectionHandlerID, const char* modeID, const char* captureDevice);
unsigned int (*getCurrentPlaybackDeviceName)(uint64 serverConnectionHandlerID, char** result, int* isDefault);
unsigned int (*getCurrentPlayBackMode)(uint64 serverConnectionHandlerID, char** result);
unsigned int (*getCurrentCaptureDeviceName)(uint64 serverConnectionHandlerID, char** result, int* isDefault);
unsigned int (*getCurrentCaptureMode)(uint64 serverConnectionHandlerID, char** result);
unsigned int (*initiateGracefulPlaybackShutdown)(uint64 serverConnectionHandlerID);
unsigned int (*closePlaybackDevice)(uint64 serverConnectionHandlerID);
unsigned int (*closeCaptureDevice)(uint64 serverConnectionHandlerID);
unsigned int (*activateCaptureDevice)(uint64 serverConnectionHandlerID);
unsigned int (*playWaveFileHandle)(uint64 serverConnectionHandlerID, const char* path, int loop, uint64* waveHandle);
unsigned int (*pauseWaveFileHandle)(uint64 serverConnectionHandlerID, uint64 waveHandle, int pause);
unsigned int (*closeWaveFileHandle)(uint64 serverConnectionHandlerID, uint64 waveHandle);
unsigned int (*playWaveFile)(uint64 serverConnectionHandlerID, const char* path);
unsigned int (*registerCustomDevice)(const char* deviceID, const char* deviceDisplayName, int capFrequency, int capChannels, int playFrequency, int playChannels);
unsigned int (*unregisterCustomDevice)(const char* deviceID);
unsigned int (*processCustomCaptureData)(const char* deviceName, const short* buffer, int samples);
unsigned int (*acquireCustomPlaybackData)(const char* deviceName, short* buffer, int samples);

/* Preprocessor */
unsigned int (*getPreProcessorInfoValueFloat)(uint64 serverConnectionHandlerID, const char* ident, float* result);
unsigned int (*getPreProcessorConfigValue)(uint64 serverConnectionHandlerID, const char* ident, char** result);
unsigned int (*setPreProcessorConfigValue)(uint64 serverConnectionHandlerID, const char* ident, const char* value);

/* Encoder */
unsigned int (*getEncodeConfigValue)(uint64 serverConnectionHandlerID, const char* ident, char** result);

/* Playback */
unsigned int (*getPlaybackConfigValueAsFloat)(uint64 serverConnectionHandlerID, const char* ident, float* result);
unsigned int (*setPlaybackConfigValue)(uint64 serverConnectionHandlerID, const char* ident, const char* value);
unsigned int (*setClientVolumeModifier)(uint64 serverConnectionHandlerID, anyID clientID, float value);

/* Recording */
unsigned int (*startVoiceRecording)(uint64 serverConnectionHandlerID);
unsigned int (*stopVoiceRecording)(uint64 serverConnectionHandlerID);

/* 3d sound positioning */
unsigned int (*systemset3DListenerAttributes) (uint64 serverConnectionHandlerID, const TS3_VECTOR* position, const TS3_VECTOR* forward, const TS3_VECTOR* up);
unsigned int (*set3DWaveAttributes) (uint64 serverConnectionHandlerID, uint64 waveHandle, const TS3_VECTOR* position);
unsigned int (*systemset3DSettings) (uint64 serverConnectionHandlerID, float distanceFactor, float rolloffScale);
unsigned int (*channelset3DAttributes) (uint64 serverConnectionHandlerID, anyID clientID, const TS3_VECTOR* position);

/* Interaction with the server */
unsigned int (*startConnection)(uint64 serverConnectionHandlerID, const char* identity, const char* ip, unsigned int port, const char* nickname,
                               const char** defaultChannelArray, const char* defaultChannelPassword, const char* serverPassword);
unsigned int (*stopConnection)(uint64 serverConnectionHandlerID, const char* quitMessage);
unsigned int (*requestClientMove)(uint64 serverConnectionHandlerID, anyID clientID, uint64 newChannelID, const char* password, const char* returnCode);
unsigned int (*requestClientVariables)(uint64 serverConnectionHandlerID, anyID clientID, const char* returnCode);
unsigned int (*requestClientKickFromChannel)(uint64 serverConnectionHandlerID, anyID clientID, const char* kickReason, const char* returnCode);
unsigned int (*requestClientKickFromServer)(uint64 serverConnectionHandlerID, anyID clientID, const char* kickReason, const char* returnCode);
unsigned int (*requestChannelDelete)(uint64 serverConnectionHandlerID, uint64 channelID, int force, const char* returnCode);
unsigned int (*requestChannelMove)(uint64 serverConnectionHandlerID, uint64 channelID, uint64 newChannelParentID, uint64 newChannelOrder, const char* returnCode);
unsigned int (*requestSendPrivateTextMsg)(uint64 serverConnectionHandlerID, const char* message, anyID targetClientID, const char* returnCode);
unsigned int (*requestSendChannelTextMsg)(uint64 serverConnectionHandlerID, const char* message, uint64 targetChannelID, const char* returnCode);
unsigned int (*requestSendServerTextMsg)(uint64 serverConnectionHandlerID, const char* message, const char* returnCode);
unsigned int (*requestConnectionInfo)(uint64 serverConnectionHandlerID, anyID clientID, const char* returnCode);
unsigned int (*requestClientSetWhisperList)(uint64 serverConnectionHandlerID, anyID clientID, const uint64* targetChannelIDArray, const anyID* targetClientIDArray, const char* returnCode);
unsigned int (*requestChannelSubscribe)(uint64 serverConnectionHandlerID, const uint64* channelIDArray, const char* returnCode);
unsigned int (*requestChannelSubscribeAll)(uint64 serverConnectionHandlerID, const char* returnCode);
unsigned int (*requestChannelUnsubscribe)(uint64 serverConnectionHandlerID, const uint64* channelIDArray, const char* returnCode);
unsigned int (*requestChannelUnsubscribeAll)(uint64 serverConnectionHandlerID, const char* returnCode);
unsigned int (*requestChannelDescription)(uint64 serverConnectionHandlerID, uint64 channelID, const char* returnCode);
unsigned int (*requestMuteClients)(uint64 serverConnectionHandlerID, const anyID* clientIDArray, const char* returnCode);
unsigned int (*requestUnmuteClients)(uint64 serverConnectionHandlerID, const anyID* clientIDArray, const char* returnCode);
unsigned int (*requestClientPoke)(uint64 serverConnectionHandlerID, anyID clientID, const char* message, const char* returnCode);
unsigned int (*requestClientIDs)(uint64 serverConnectionHandlerID, const char* clientUniqueIdentifier, const char* returnCode);
unsigned int (*clientChatClosed)(uint64 serverConnectionHandlerID, const char* clientUniqueIdentifier, anyID clientID, const char* returnCode);
unsigned int (*clientChatComposing)(uint64 serverConnectionHandlerID, anyID clientID, const char* returnCode);
unsigned int (*requestServerTemporaryPasswordAdd)(uint64 serverConnectionHandlerID, const char* password, const char* description, uint64 duration, uint64 targetChannelID, const char* targetChannelPW, const char* returnCode);
unsigned int (*requestServerTemporaryPasswordDel)(uint64 serverConnectionHandlerID, const char* password, const char* returnCode);
unsigned int (*requestServerTemporaryPasswordList)(uint64 serverConnectionHandlerID, const char* returnCode);

/* Access clientlib information */

/* Query own client ID */
unsigned int (*getClientID)(uint64 serverConnectionHandlerID, anyID* result);

/* Client info */
unsigned int (*getClientSelfVariableAsInt)(uint64 serverConnectionHandlerID, size_t flag, int* result);
unsigned int (*getClientSelfVariableAsString)(uint64 serverConnectionHandlerID, size_t flag, char** result);
unsigned int (*setClientSelfVariableAsInt)(uint64 serverConnectionHandlerID, size_t flag, int value);
unsigned int (*setClientSelfVariableAsString)(uint64 serverConnectionHandlerID, size_t flag, const char* value);
unsigned int (*flushClientSelfUpdates)(uint64 serverConnectionHandlerID, const char* returnCode);
unsigned int (*getClientVariableAsInt)(uint64 serverConnectionHandlerID, anyID clientID, size_t flag, int* result);
unsigned int (*getClientVariableAsUInt64)(uint64 serverConnectionHandlerID, anyID clientID, size_t flag, uint64* result);
unsigned int (*getClientVariableAsString)(uint64 serverConnectionHandlerID, anyID clientID, size_t flag, char** result);
unsigned int (*getClientList)(uint64 serverConnectionHandlerID, anyID** result);
unsigned int (*getChannelOfClient)(uint64 serverConnectionHandlerID, anyID clientID, uint64* result);

/* Channel info */
unsigned int (*getChannelVariableAsInt)(uint64 serverConnectionHandlerID, uint64 channelID, size_t flag, int* result);
unsigned int (*getChannelVariableAsUInt64)(uint64 serverConnectionHandlerID, uint64 channelID, size_t flag, uint64* result);
unsigned int (*getChannelVariableAsString)(uint64 serverConnectionHandlerID, uint64 channelID, size_t flag, char** result);
unsigned int (*getChannelIDFromChannelNames)(uint64 serverConnectionHandlerID, char** channelNameArray, uint64* result);
unsigned int (*setChannelVariableAsInt)(uint64 serverConnectionHandlerID, uint64 channelID, size_t flag, int value);
unsigned int (*setChannelVariableAsUInt64)(uint64 serverConnectionHandlerID, uint64 channelID, size_t flag, uint64 value);
unsigned int (*setChannelVariableAsString)(uint64 serverConnectionHandlerID, uint64 channelID, size_t flag, const char* value);
unsigned int (*flushChannelUpdates)(uint64 serverConnectionHandlerID, uint64 channelID, const char* returnCode);
unsigned int (*flushChannelCreation)(uint64 serverConnectionHandlerID, uint64 channelParentID, const char* returnCode);
unsigned int (*getChannelList)(uint64 serverConnectionHandlerID, uint64** result);
unsigned int (*getChannelClientList)(uint64 serverConnectionHandlerID, uint64 channelID,  anyID** result);
unsigned int (*getParentChannelOfChannel)(uint64 serverConnectionHandlerID, uint64 channelID, uint64* result);

/* Server info */
unsigned int (*getServerConnectionHandlerList)(uint64** result);
unsigned int (*getServerVariableAsInt)(uint64 serverConnectionHandlerID, size_t flag, int* result);
unsigned int (*getServerVariableAsUInt64)(uint64 serverConnectionHandlerID, size_t flag, uint64* result);
unsigned int (*getServerVariableAsString)(uint64 serverConnectionHandlerID, size_t flag, char** result);
unsigned int (*requestServerVariables)(uint64 serverConnectionHandlerID);

/* Connection info */
unsigned int (*getConnectionStatus)(uint64 serverConnectionHandlerID, int* result);
unsigned int (*getConnectionVariableAsUInt64)(uint64 serverConnectionHandlerID, anyID clientID, size_t flag, uint64* result);
unsigned int (*getConnectionVariableAsDouble)(uint64 serverConnectionHandlerID, anyID clientID, size_t flag, double* result);
unsigned int (*getConnectionVariableAsString)(uint64 serverConnectionHandlerID, anyID clientID, size_t flag, char** result);
unsigned int (*cleanUpConnectionInfo)(uint64 serverConnectionHandlerID, anyID clientID);

/* Client related */
unsigned int (*requestClientDBIDfromUID)(uint64 serverConnectionHandlerID, const char* clientUniqueIdentifier, const char* returnCode);
unsigned int (*requestClientNamefromUID)(uint64 serverConnectionHandlerID, const char* clientUniqueIdentifier, const char* returnCode);
unsigned int (*requestClientNamefromDBID)(uint64 serverConnectionHandlerID, uint64 clientDatabaseID, const char* returnCode);
unsigned int (*requestClientEditDescription)(uint64 serverConnectionHandlerID, anyID clientID, const char* clientDescription, const char* returnCode);
unsigned int (*requestClientSetIsTalker)(uint64 serverConnectionHandlerID, anyID clientID, int isTalker, const char* returnCode);
unsigned int (*requestIsTalker)(uint64 serverConnectionHandlerID, int isTalkerRequest, const char* isTalkerRequestMessage, const char* returnCode);

/* Plugin related */
unsigned int (*requestSendClientQueryCommand)(uint64 serverConnectionHandlerID, const char* command, const char* returnCode);

/* Filetransfer */
unsigned int (*getTransferFileName)(anyID transferID, char** result);
unsigned int (*getTransferFilePath)(anyID transferID, char** result);
unsigned int (*getTransferFileSize)(anyID transferID, uint64* result);
unsigned int (*getTransferFileSizeDone)(anyID transferID, uint64* result);
unsigned int (*isTransferSender)(anyID transferID, int* result);  /* 1 == upload, 0 == download */
unsigned int (*getTransferStatus)(anyID transferID, int* result);
unsigned int (*getCurrentTransferSpeed)(anyID transferID, float* result);
unsigned int (*getAverageTransferSpeed)(anyID transferID, float* result);
unsigned int (*getTransferRunTime)(anyID transferID, uint64* result);
unsigned int (*sendFile)(uint64 serverConnectionHandlerID, uint64 channelID, const char* channelPW, const char* file, int overwrite, int resume, const char* sourceDirectory, anyID* result, const char* returnCode);
unsigned int (*requestFile)(uint64 serverConnectionHandlerID, uint64 channelID, const char* channelPW, const char* file, int overwrite, int resume, const char* destinationDirectory, anyID* result, const char* returnCode);
unsigned int (*haltTransfer)(uint64 serverConnectionHandlerID, anyID transferID, int deleteUnfinishedFile, const char* returnCode);
unsigned int (*requestFileList)(uint64 serverConnectionHandlerID, uint64 channelID, const char* channelPW, const char* path, const char* returnCode);
unsigned int (*requestFileInfo)(uint64 serverConnectionHandlerID, uint64 channelID, const char* channelPW, const char* file, const char* returnCode);
unsigned int (*requestDeleteFile)(uint64 serverConnectionHandlerID, uint64 channelID, const char* channelPW, const char** file, const char* returnCode);
unsigned int (*requestCreateDirectory)(uint64 serverConnectionHandlerID, uint64 channelID, const char* channelPW, const char* directoryPath, const char* returnCode);
unsigned int (*requestRenameFile)(uint64 serverConnectionHandlerID, uint64 fromChannelID, const char* channelPW, uint64 toChannelID, const char* toChannelPW, const char* oldFile, const char* newFile, const char* returnCode);

/** [...] **/

/* Client functions */
void         (*getAppPath)(char* path, size_t maxLen);
void         (*getResourcesPath)(char* path, size_t maxLen);
void         (*getConfigPath)(char* path, size_t maxLen);
void         (*getPluginPath)(char* path, size_t maxLen);
uint64       (*getCurrentServerConnectionHandlerID)();
void         (*printMessage)(uint64 serverConnectionHandlerID, const char* message, enum PluginMessageTarget messageTarget);
void         (*printMessageToCurrentTab)(const char* message);
void         (*urlsToBB)(const char* text, char* result, size_t maxLen);
void         (*sendPluginCommand)(uint64 serverConnectionHandlerID, const char* pluginID, const char* command, int targetMode, const anyID* targetIDs, const char* returnCode);
void         (*getDirectories)(const char* path, char* result, size_t maxLen);
unsigned int (*getServerConnectInfo)(uint64 scHandlerID, char* host, unsigned short* port, char* password, size_t maxLen);
unsigned int (*getChannelConnectInfo)(uint64 scHandlerID, uint64 channelID, char* path, char* password, size_t maxLen);
void         (*createReturnCode)(const char* pluginID, char* returnCode, size_t maxLen);
unsigned int (*requestInfoUpdate)(uint64 scHandlerID, enum PluginItemType itemType, uint64 itemID);
uint64       (*getServerVersion)(uint64 scHandlerID);
unsigned int (*isWhispering)(uint64 scHandlerID, anyID clientID, int* result);
unsigned int (*isReceivingWhisper)(uint64 scHandlerID, anyID clientID, int* result);
unsigned int (*getAvatar)(uint64 scHandlerID, anyID clientID, char* result, size_t maxLen);
void         (*setPluginMenuEnabled)(const char* pluginID, int menuID, int enabled);
void         (*showHotkeySetup)();
void         (*requestHotkeyInputDialog)(const char* pluginID, const char* keyword, int isDown, void* qParentWindow);
unsigned int (*getHotkeyFromKeyword)(const char* pluginID, const char** keywords, char** hotkeys, size_t arrayLen, size_t hotkeyBufSize);
unsigned int (*getClientDisplayName)(uint64 scHandlerID, anyID clientID, char* result, size_t maxLen);
unsigned int (*getBookmarkList)(struct PluginBookmarkList** list);
unsigned int (*getProfileList)(enum PluginGuiProfile profile, int* defaultProfileIdx, char*** result);
unsigned int (*guiConnect)(enum PluginConnectTab connectTab, const char* serverLabel, const char* serverAddress, const char* serverPassword, const char* nickname, const char* channel, const char* channelPassword, const char* captureProfile, const char* playbackProfile, const char* hotkeyProfile, const char* soundProfile, const char* userIdentity, const char* oneTimeKey, const char* phoneticName, uint64* scHandlerID);
unsigned int (*guiConnectBookmark)(enum PluginConnectTab connectTab, const char* bookmarkuuid, uint64* scHandlerID);
unsigned int (*createBookmark)(const char* bookmarkuuid, const char* serverLabel, const char* serverAddress, const char* serverPassword, const char* nickname, const char* channel, const char* channelPassword, const char* captureProfile, const char* playbackProfile, const char* hotkeyProfile, const char* soundProfile, const char* uniqueUserId, const char* oneTimeKey, const char* phoneticName);
unsigned int (*getPermissionIDByName)(uint64 serverConnectionHandlerID, const char* permissionName, unsigned int* result);
unsigned int (*getClientNeededPermission)(uint64 serverConnectionHandlerID, const char* permissionName, int* result);
};
TS3Functions.

Basically the problem already appears at the first function... at "getClientLibVersion"... it's detected... but only as
public int(* getClientLibVersion): unsigned
So there's a problem with unsigned int as a return because it only picks up the first identifier...

It also corrupts the code-completion popup... sometimes you'll get a different declaration to the right then, the selected function on the left...
Looks like this:
« Last Edit: September 14, 2014, 04:36:06 pm by White-Tiger »
Windoze 8.1 x86_64 16GiB RAM, wxWidgets-2.8x (latest,trunk), MinGW-builds (latest, posix-threads)
Code::Blocks (x86 , latest , selection length patch , build option fixes/additions , toggle comments)

Offline ollydbg

  • Developer
  • Lives here!
  • *****
  • Posts: 5910
  • OpenCV and Robotics
    • Chinese OpenCV forum moderator
Re: Is it possible for the parser to support newlib prototypes?
« Reply #32 on: September 15, 2014, 07:00:24 am »
Quote
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
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;
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));
Those can be handled.. and also variable-like macros..
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.

Maybe, a better method is to try expand every identifier like token if possible, I remembered you have a patch named "cc_parser_general.patch", but when I looked at that patch in my PC, I see that patch contains too many things. (a lot of them is already in trunk)

To enable this feature, we can just do this patch:
Code
 src/plugins/codecompletion/parser/tokenizer.cpp | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/src/plugins/codecompletion/parser/tokenizer.cpp b/src/plugins/codecompletion/parser/tokenizer.cpp
index f47e8dd..31f0546 100644
--- a/src/plugins/codecompletion/parser/tokenizer.cpp
+++ b/src/plugins/codecompletion/parser/tokenizer.cpp
@@ -1246,8 +1246,8 @@ wxString Tokenizer::DoGetToken()
 void Tokenizer::ReplaceMacro(wxString& str)
 {
     // this indicates we are already in macro replacement mode
-    if (m_RepeatReplaceCount > 0)
-    {
+//    if (m_RepeatReplaceCount > 0)
+//    {
         const int id = m_TokenTree->TokenExists(str, -1, tkMacroDef);
         if (id != -1)
         {
@@ -1270,8 +1270,9 @@ void Tokenizer::ReplaceMacro(wxString& str)
         // if in macro expansion mode, we don't want to let the user replacement rule executed
         // again, so just returned
         return;
-    }
+//    }
 
+#if 0
     wxStringHashMap::const_iterator it = s_Replacements.find(str);
     if (it == s_Replacements.end())
         return;
@@ -1344,6 +1345,7 @@ void Tokenizer::ReplaceMacro(wxString& str)
         if (it->second != str && ReplaceBufferText(it->second, false))
             str = DoGetToken();
     }
+#endif
 }
 
 bool Tokenizer::CalcConditionExpression()

Thus, we totally remove all the user defined replacement rules.
I just test the patch, the parsing time is a bit longer, but not too much.  ;D
Another smart method is that we can only check the macro usage on the identifier like token which has all capital characters or underscore.
If some piece of memory should be reused, turn them to variables (or const variables).
If some piece of operations should be reused, turn them to functions.
If they happened together, then turn them to classes.

Offline ollydbg

  • Developer
  • Lives here!
  • *****
  • Posts: 5910
  • OpenCV and Robotics
    • Chinese OpenCV forum moderator
Re: Is it possible for the parser to support newlib prototypes?
« Reply #33 on: September 15, 2014, 07:34:39 am »
yes an no.. the reported case works, same for the real world code I based it on... (at least it looks like that, didn't miss any function)
But another case I've used to work with, does not work.

I'll post the almost non-stripped code just in case there's another "bug" (had to strip it a bit because of 20k character limit for posts^^)
...

Basically the problem already appears at the first function... at "getClientLibVersion"... it's detected... but only as
public int(* getClientLibVersion): unsigned
So there's a problem with unsigned int as a return because it only picks up the first identifier...

Thanks for the sample code.
OK, I can strip the test code (can be run in cctest project)
Code

struct TS3Functions {
unsigned int (*getClientLibVersion)(char** result);
unsigned int (*getClientLibVersionNumber)(uint64* result);
};

TS3Functions a;


//a.    //getClientLibVersion

The result is:
Code
000001. --------------M-a-i-n--L-o-g--------------


000002. -----------I-n-t-e-r-i-m--L-o-g-----------
000003. InitTokenizer() : m_Filename='F:\cb_sf_git\trunk\src\plugins\codecompletion\testing\ccc_function_ptr.cpp', m_FileSize=255.
000004. Init() : m_Filename='F:\cb_sf_git\trunk\src\plugins\codecompletion\testing\ccc_function_ptr.cpp'
000005. F:\cb_sf_git\trunk\src\plugins\codecompletion\testing\ccc_function_ptr.cpp
000006. Parse() : Parsing 'F:\cb_sf_git\trunk\src\plugins\codecompletion\testing\ccc_function_ptr.cpp'
000007. SkipComment() : Start from line = 1
000008. DoParse() : Loop:m_Str='', token='struct'
000009. HandleClass() : Found class 'TS3Functions', next='{'
000010. GetRealTypeIfTokenIsMacro() : tokenIsMacro=no -> tokenName='TS3Functions'.
000011. DoAddToken() : Created token='TS3Functions', file_idx=1, line=5, ticket=256
000012. GetTokenBaseType() : Searching within m_Str=''
000013. GetTokenBaseType() : Compensated m_Str=''
000014. GetTokenBaseType() : Returning ''
000015. DoAddToken() : Prepending ''
000016. DoAddToken() : Added/updated token 'TS3Functions' (0), kind 'class', type '', actual ''. Parent is  (-1)
000017. DoParse() : Loop:m_Str='', token='unsigned'
000018. DoParse() : Loop:m_Str='unsigned ', token='int'
000019. ReadParentheses(): (* getClientLibVersion), line=6
000020. HandleFunction() : Adding function 'int': m_Str='unsigned '
000021. ReadParentheses(): (char** result), line=6
000022. HandleFunction() : name='int', args='(* getClientLibVersion)', peek='(char** result)'
000023. HandleFunction() : !(Ctor/Dtor) 'int', m_Str='unsigned ', localParent='<none>'
000024. HandleFunction() : Adding function 'int', ': m_Str='unsigned ', enc_ns='nil'.
000025. HandleFunction() : Possible macro '(char** result)' in function 'int' (file name='F:\cb_sf_git\trunk\src\plugins\codecompletion\testing\ccc_function_ptr.cpp', line numer 6).
000026. HandleFunction() : Add token name='int', args='(* getClientLibVersion)', return type='unsigned '
000027. GetBaseArgs() : args='(* getClientLibVersion)'.
000028. GetBaseArgs() : baseArgs='(*)'.
000029. DoAddToken() : Created token='int', file_idx=1, line=6, ticket=257
000030. GetTokenBaseType() : Searching within m_Str='unsigned'
000031. GetTokenBaseType() : Compensated m_Str='unsigned'
000032. GetTokenBaseType() : Found 'unsigned'
000033. DoAddToken() : Prepending ''
000034. DoAddToken() : Added/updated token 'int' (1), kind 'function', type 'unsigned', actual 'unsigned'. Parent is TS3Functions (0)
000035. DoParse() : Loop:m_Str='', token='(char** result)'
000036. DoParse() : Loop:m_Str='', token=';'
000037. DoParse() : Loop:m_Str='', token='unsigned'
000038. DoParse() : Loop:m_Str='unsigned ', token='int'
000039. ReadParentheses(): (* getClientLibVersionNumber), line=7
000040. HandleFunction() : Adding function 'int': m_Str='unsigned '
000041. ReadParentheses(): (uint64* result), line=7
000042. HandleFunction() : name='int', args='(* getClientLibVersionNumber)', peek='(uint64* result)'
000043. HandleFunction() : !(Ctor/Dtor) 'int', m_Str='unsigned ', localParent='<none>'
000044. HandleFunction() : Adding function 'int', ': m_Str='unsigned ', enc_ns='nil'.
000045. HandleFunction() : Possible macro '(uint64* result)' in function 'int' (file name='F:\cb_sf_git\trunk\src\plugins\codecompletion\testing\ccc_function_ptr.cpp', line numer 7).
000046. HandleFunction() : Add token name='int', args='(* getClientLibVersionNumber)', return type='unsigned '
000047. GetBaseArgs() : args='(* getClientLibVersionNumber)'.
000048. GetBaseArgs() : baseArgs='(*)'.
000049. DoAddToken() : Found token (parent).
000050. GetTokenBaseType() : Searching within m_Str='unsigned'
000051. GetTokenBaseType() : Compensated m_Str='unsigned'
000052. GetTokenBaseType() : Found 'unsigned'
000053. DoAddToken() : Prepending ''
000054. DoAddToken() : Added/updated token 'int' (1), kind 'function', type 'unsigned', actual 'unsigned'. Parent is TS3Functions (0)
000055. DoParse() : Loop:m_Str='', token='(uint64* result)'
000056. DoParse() : Loop:m_Str='', token=';'
000057. DoParse() : Loop:m_Str='', token='}'
000058. DoParse() : Loop:m_Str='', token='TS3Functions'
000059. DoParse() : Loop:m_Str='TS3Functions ', token='a'
000060. DoAddToken() : Created token='a', file_idx=1, line=10, ticket=258
000061. GetTokenBaseType() : Searching within m_Str='TS3Functions'
000062. GetTokenBaseType() : Compensated m_Str='TS3Functions'
000063. GetTokenBaseType() : Found 'TS3Functions'
000064. DoAddToken() : Prepending ''
000065. DoAddToken() : Added/updated token 'a' (2), kind 'variable', type 'TS3Functions', actual 'TS3Functions'. Parent is  (-1)
000066. DoParse() : Loop:m_Str='TS3Functions', token=';'
000067. SkipComment() : Start from line = 13
000068. SkipComment() : Need to call SkipToInlineCommentEnd() here at line = 13
000069. bool Tokenizer::SkipToInlineCommentEnd() : line=13, CurrentChar='a', PreviousChar='/', NextChar='.'
000070. SkipToInlineCommentEnd(): (END) We are now at line 13, CurrentChar='
********************************************************
  Testing in file: F:\cb_sf_git\trunk\src\plugins\codecompletion\testing\ccc_function_ptr.cpp
********************************************************
NativeParserBase::BreakUpComponents()
FindCCTokenStart() : Starting at 0 "a."
GetNextCCToken() : at 0 (a): res=
GetNextCCToken() : Done nest: at 1 (.): res=a
GetNextCCToken() : Return at 1 (.): res=a
GetCCToken() : FindCCTokenStart returned 1 "a."
GetCCToken() : GetNextCCToken returned 1 "a"
GetCCToken() : Left ""
NativeParserBase::GenerateResultSet_2()
Find 1 valid text matched tokens from the tree.
NativeParserBase::BreakUpComponents()
FindCCTokenStart() : Starting at 0 "TS3Functions"
GetNextCCToken() : at 0 (T): res=
GetNextCCToken() : Done nest: at 12 (
GetNextCCToken() : Return at 12 (
GetCCToken() : FindCCTokenStart returned 12 "TS3Functions"
GetCCToken() : GetNextCCToken returned 12 "TS3Functions"
GetCCToken() : Left ""
NativeParserBase::GenerateResultSet_2()
Find 1 valid text matched tokens from the tree.
NativeParserBase::GenerateResultSet_2()
*FAIL: a.  getClientLibVersion
--------------------------------------------------------
Total 1 tests, 0 PASS, 1 FAIL
--------------------------------------------------------

Look: Adding function 'int': m_Str='unsigned ', the reason is we have such source code, the function name is "int".  ;D
Code
                    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
                    }

and
Code
unsigned int (*getClientLibVersion)(char** result);
Satisfy "AAA BBB (...)" pattern. Maybe, we need to check whether there is a parenthesis after parenthesis.
« Last Edit: September 15, 2014, 08:19:31 am by ollydbg »
If some piece of memory should be reused, turn them to variables (or const variables).
If some piece of operations should be reused, turn them to functions.
If they happened together, then turn them to classes.

Offline ollydbg

  • Developer
  • Lives here!
  • *****
  • Posts: 5910
  • OpenCV and Robotics
    • Chinese OpenCV forum moderator
Re: Is it possible for the parser to support newlib prototypes?
« Reply #34 on: September 15, 2014, 08:29:24 am »
Candidate patch to fix your problem
Code
8ee75a7f4ee217a48380bcee26ad83d674cfd489
 src/plugins/codecompletion/parser/parserthread.cpp | 23 +++++++++++++++++++++-
 src/plugins/codecompletion/parser/parserthread.h   |  2 +-
 2 files changed, 23 insertions(+), 2 deletions(-)

diff --git a/src/plugins/codecompletion/parser/parserthread.cpp b/src/plugins/codecompletion/parser/parserthread.cpp
index 9cb2690..96ba685 100644
--- a/src/plugins/codecompletion/parser/parserthread.cpp
+++ b/src/plugins/codecompletion/parser/parserthread.cpp
@@ -2148,18 +2148,39 @@ void ParserThread::HandleClass(EClassType ct)
     m_Tokenizer.SetState(oldState);
 }
 
-void ParserThread::HandleFunction(const wxString& name, bool isOperator, bool isPointer)
+void ParserThread::HandleFunction(wxString name, bool isOperator, bool isPointer)
 {
     TRACE(_T("HandleFunction() : Adding function '")+name+_T("': m_Str='")+m_Str+_T("'"));
     int lineNr = m_Tokenizer.GetLineNumber();
     wxString args = m_Tokenizer.GetToken();
     wxString peek = m_Tokenizer.PeekToken();
+    if (peek[0] == ParserConsts::opbracket_chr)
+    {
+        // pattern unsigned int (*getClientLibVersion)(char** result);
+        // currently, m_Str = unsigned, function name = int
+        // args = (*getClientLibVersion), peek = (char** result)
+        // this may be a function pointer declaration
+        m_Str<<name;
+        name = args;    // this is in-fact the function name "getClientLibVersion"
+        int pos = name.find(ParserConsts::ptr);
+        if (pos != wxNOT_FOUND)
+        {
+            args = peek; // peek becomes the function args
+            name.Trim(true).RemoveLast();
+            name.Remove(0, pos+1);
+            name.Trim(true).Trim(false);
+            isPointer = true;
+            m_Tokenizer.GetToken(); //consume the peek
+            peek = m_Tokenizer.PeekToken(); // update the peek value
+        }
+    }
     TRACE(_T("HandleFunction() : name='")+name+_T("', args='")+args+_T("', peek='")+peek+_T("'"));
 
     // special case for function pointers
     if (isPointer)
     {
         // pattern: m_Str AAA (*BBB) (...);
+        // name = *BBB, args = (...)
         if (peek == ParserConsts::semicolon)
         {
             TRACE(_T("HandleFunction() : Add token name='")+name+_T("', args='")+args+_T("', return type='") + m_Str+ _T("'"));
diff --git a/src/plugins/codecompletion/parser/parserthread.h b/src/plugins/codecompletion/parser/parserthread.h
index 7421177..3e26189 100644
--- a/src/plugins/codecompletion/parser/parserthread.h
+++ b/src/plugins/codecompletion/parser/parserthread.h
@@ -202,7 +202,7 @@ protected:
       * @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, bool isPointer = false);
+    void HandleFunction(wxString name, bool isOperator = false, bool isPointer = false);
 
     /** parse for loop arguments:
       * for(int X; ... ; ...)


But I have some code suggestion issue.

Code
struct TS3Functions {
unsigned int (*getClientLibVersion)(char** result);
unsigned int (*getClientLibVersionNumber)(uint64* result);
};

TS3Functions a;
a.
Code suggest list shows Ok, but if I continue entering "get", then code suggesting list disappears, not sure why........(maybe some other local patches ;))

EDIT: update the patch, so no such issue. :)
« Last Edit: September 15, 2014, 09:31:41 am by ollydbg »
If some piece of memory should be reused, turn them to variables (or const variables).
If some piece of operations should be reused, turn them to functions.
If they happened together, then turn them to classes.

Offline Teybeo

  • Multiple posting newcomer
  • *
  • Posts: 14
Re: Is it possible for the parser to support newlib prototypes?
« Reply #35 on: September 16, 2014, 11:05:20 pm »
I remembered this thread when I saw the 9916 changelog about macros and functions stuff.
So I just download and tested the 9916 build but the post-OpenGL 1.x functions are still not detected as functions but as macros, so the calltip don't show the function parameters. OpenGL 1.x function dates back 20 years ago, we are now at OpenGL 4.5 and there are now TONS of functions which we can't see the parameters types and names.

Example:


Basically all the detected functions are the 20 years old and sometimes deprecated ones, and the macros are the modern ones.

I've read some of you guys posts but I didn't understand a lot. Do you think it can be supported ?

Offline Huki

  • Multiple posting newcomer
  • *
  • Posts: 95
Re: Is it possible for the parser to support newlib prototypes?
« Reply #36 on: September 17, 2014, 12:10:32 am »
yes an no.. the reported case works, same for the real world code I based it on... (at least it looks like that, didn't miss any function)
But another case I've used to work with, does not work.

I'll post the almost non-stripped code just in case there's another "bug" (had to strip it a bit because of 20k character limit for posts^^)
...

Basically the problem already appears at the first function... at "getClientLibVersion"... it's detected... but only as
public int(* getClientLibVersion): unsigned
So there's a problem with unsigned int as a return because it only picks up the first identifier...

Candidate patch to fix your problem
[...]
Hi, yes, we need to handle return types with more than one token, like "unsigned int". Thanks for the patch, I'll review it and the macros handling part tomorrow.

Offline Huki

  • Multiple posting newcomer
  • *
  • Posts: 95
Re: Is it possible for the parser to support newlib prototypes?
« Reply #37 on: September 17, 2014, 12:18:27 am »
I remembered this thread when I saw the 9916 changelog about macros and functions stuff.
So I just download and tested the 9916 build but the post-OpenGL 1.x functions are still not detected as functions but as macros, so the calltip don't show the function parameters. OpenGL 1.x function dates back 20 years ago, we are now at OpenGL 4.5 and there are now TONS of functions which we can't see the parameters types and names.
The support added in build 9916 is for function calltips. Just select the function from the code completion list and start typing the opening bracket, you should see the calltip showing the arguments and return type. See the screenshot below:


However the code completion list itself will only show it as a macro definition as you can see.

Offline Teybeo

  • Multiple posting newcomer
  • *
  • Posts: 14
Re: Is it possible for the parser to support newlib prototypes?
« Reply #38 on: September 17, 2014, 02:52:54 pm »
Oops yeah, I confirm, it works like in your screenshot, good job :) !

(Minor UI detail, it would be nice if the function calltip was showing the true name of the function and not PFNGLDRAWARRAYSINSTANCEDPROC)

Offline Huki

  • Multiple posting newcomer
  • *
  • Posts: 95
Re: Is it possible for the parser to support newlib prototypes?
« Reply #39 on: September 18, 2014, 01:16:09 pm »
Oops yeah, I confirm, it works like in your screenshot, good job :) !

(Minor UI detail, it would be nice if the function calltip was showing the true name of the function and not PFNGLDRAWARRAYSINSTANCEDPROC)
Yes, I think it would be better to show the variable name rather than the typedef name. See here:


I'll submit the patch soon...
« Last Edit: September 18, 2014, 01:19:50 pm by Huki »

Offline Huki

  • Multiple posting newcomer
  • *
  • Posts: 95
Re: Is it possible for the parser to support newlib prototypes?
« Reply #40 on: September 18, 2014, 02:15:14 pm »
yes an no.. the reported case works, same for the real world code I based it on... (at least it looks like that, didn't miss any function)
But another case I've used to work with, does not work.

I'll post the almost non-stripped code just in case there's another "bug" (had to strip it a bit because of 20k character limit for posts^^)
...

Basically the problem already appears at the first function... at "getClientLibVersion"... it's detected... but only as
public int(* getClientLibVersion): unsigned
So there's a problem with unsigned int as a return because it only picks up the first identifier...

Candidate patch to fix your problem
[...]
Hi, yes, we need to handle return types with more than one token, like "unsigned int". Thanks for the patch, I'll review it and the macros handling part tomorrow.
I think your patch will fix the problem, but maybe it's better to have all function pointers checking in one place (in DoParse()), then we just send the result to HandleFunction(). The problem is that we have a pattern like: AAA BBB (*name) (arg), where m_Str = AAA, token = BBB, peek = (*name), and we can't know if this is a function declaration or function ptr without reading the next token after peek.
But I think we can use another trick: strip the '(' in peek, and see if the next char is '*'. If it is, then it should be a function pointer. See this code:
Code
// pattern unsigned int (*getClientLibVersion)(char** result);
// currently, m_Str = unsigned, token = int, peek = (*getClientLibVersion)
// this may be a function pointer declaration, we can guess without
// reading the next token, if "peek" has a ptr char and only 1 argument
// in it.

// see what is inside the (...)
// try to see whether the peek pattern is (* BBB)
wxString arg = peek;
arg.Remove(0,1); // remove '('
if (arg.GetChar(0) == ParserConsts::ptr)
{
    arg.RemoveLast();
    arg.Remove(0,1).Trim(false); // remove '*'
    m_Str << token;
    token = m_Tokenizer.GetToken(); //consume the peek
    // BBB is now the function ptr's name
    HandleFunction(/*function name*/ arg,
                   /*isOperator*/    false,
                   /*isPointer*/     true);
}
else if (!m_Options.useBuffer || m_Options.bufferSkipBlocks)
    // function declaration
else
    // local variable initialized with ctor

Btw, some notes about the function pointer handling:
1) I saw comments like: // *BBB is now the function ptr's name
In fact we strip the '*', so the function name is just BBB.

2) About the spaces trimming: I checked the ReadParantheses() function (used in DoGetToken()), and I see it guarantees that there is no space immediately after the '(' or before the ')'. So the only place we might have to trim spaces is after removing the '*' (eg, in (* BBB) there is a space before BBB).
So I think all other Trim() calls can be safely removed, like this:
Old code:
Code
arg.Trim(true).RemoveLast();
arg.Remove(0, pos+1);
arg.Trim(true).Trim(false);
New code:
Code
arg.RemoveLast();
arg.Remove(0, pos+1).Trim(false);

I quickly tested it with some spaces and here is the result.. :)



Offline Huki

  • Multiple posting newcomer
  • *
  • Posts: 95
Re: Is it possible for the parser to support newlib prototypes?
« Reply #41 on: September 18, 2014, 02:28:31 pm »
Quote
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
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,
I have finally gotten around to doing it. See the result below.. :)
I'm pasting the entire code for opbracket_chr and macro defines, in DoParse(). Also, now all macros are expanded in DoParse(), but only if we reached "if (!switchHandled)".

Code
[...]

        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())
            {
                // pattern: AAA or 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);
                }
                // any function like pattern
                else if (   (peek.GetChar(0) == ParserConsts::opbracket_chr)
                         && m_Options.handleFunctions )
                {
                    if (   m_Str.IsEmpty()
                        && m_EncounteredNamespaces.empty()
                        && m_EncounteredTypeNamespaces.empty()
                        && (!m_LastParent || m_LastParent->m_Name != token) ) // if func has same name as current scope (class)
                    {
                        // 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.RemoveLast();
                                arg.Remove(0, pos+1).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 ()
                            m_Str = token + arg;
                    }
                    // 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)
                    else if (   (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.RemoveLast();
                            arg.Remove(0, pos+1).Trim(false);
                            HandleFunction(/*function name*/ arg,
                                           /*isOperator*/    false,
                                           /*isPointer*/     true);
                            m_Str.Clear();
                        }
                    }
                    else
                    {
                        // pattern unsigned int (*getClientLibVersion)(char** result);
                        // currently, m_Str = unsigned, token = int, peek = (*getClientLibVersion)
                        // this may be a function pointer declaration, we can guess without
                        // reading the next token, if "peek" has a ptr char and only 1 argument
                        // in it.

                        // see what is inside the (...)
                        // try to see whether the peek pattern is (* BBB)
                        wxString arg = peek;
                        arg.Remove(0,1); // remove '('
                        if (arg.GetChar(0) == ParserConsts::ptr)
                        {
                            arg.RemoveLast();
                            arg.Remove(0,1).Trim(false); // remove '*'
                            m_Str << token;
                            token = m_Tokenizer.GetToken(); //consume the peek
                            // BBB is now the function ptr's name
                            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 [...]

EDIT: and here is the patch:

Code
From d3f194380a8cdc0eeae06708ece5cf06dd61dd32 Mon Sep 17 00:00:00 2001
From: huki <gk7huki@gmail.com>
Date: Thu, 18 Sep 2014 18:26:08 +0530
Subject: CC: merge function handling and update macro handling

---
 src/plugins/codecompletion/parser/parserthread.cpp | 104 ++++++++++++---------
 1 file changed, 61 insertions(+), 43 deletions(-)

diff --git a/src/plugins/codecompletion/parser/parserthread.cpp b/src/plugins/codecompletion/parser/parserthread.cpp
index e1f258d..fc3c409 100644
--- a/src/plugins/codecompletion/parser/parserthread.cpp
+++ b/src/plugins/codecompletion/parser/parserthread.cpp
@@ -1009,22 +1009,21 @@ void ParserThread::DoParse()
             wxString peek = m_Tokenizer.PeekToken();
             if (!peek.IsEmpty())
             {
-                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) ) // if func has same name as current scope (class)
+                // pattern: AAA or AAA (...)
+                int id = m_TokenTree->TokenExists(token, -1, tkMacroDef);
+                // if AAA is a macro definition, then expand this macro
+                if (id != -1)
                 {
-                    // 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);
-                        m_Str.Clear();
-                    }
-                    else
+                    HandleMacroExpansion(id, peek);
+                }
+                // any function like pattern
+                else if (   (peek.GetChar(0) == ParserConsts::opbracket_chr)
+                         && m_Options.handleFunctions )
+                {
+                    if (   m_Str.IsEmpty()
+                        && m_EncounteredNamespaces.empty()
+                        && m_EncounteredTypeNamespaces.empty()
+                        && (!m_LastParent || m_LastParent->m_Name != token) ) // if func has same name as current scope (class)
                     {
                         // see what is inside the (...)
                         wxString arg = m_Tokenizer.GetToken(); // eat args ()
@@ -1037,16 +1036,15 @@ void ParserThread::DoParse()
                             {
                                 // pattern: AAA (* BBB) (...)
                                 // where peek is (...) and arg is (* BBB)
-                                arg.Trim(true).RemoveLast();
-                                arg.Remove(0, pos+1);
-                                arg.Trim(true).Trim(false);
+                                arg.RemoveLast();
+                                arg.Remove(0, pos+1).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
+                                // BBB is now the function ptr's name
                                 HandleFunction(/*function name*/ arg,
                                                /*isOperator*/    false,
                                                /*isPointer*/     true);
@@ -1056,9 +1054,6 @@ void ParserThread::DoParse()
                         else // wxString arg = m_Tokenizer.GetToken(); // eat args ()
                             m_Str = token + arg;
                     }
-                }
-                else if (peek.GetChar(0) == ParserConsts::opbracket_chr && m_Options.handleFunctions)
-                {
                     // NOTE: support some more cases..., such as m_Str is not empty
                     // if (!m_Options.useBuffer || m_Options.bufferSkipBlocks)
                     //     HandleFunction(token); // function
@@ -1068,42 +1063,65 @@ void ParserThread::DoParse()
                     // 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))
+                    else if (   (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);
+                            arg.RemoveLast();
+                            arg.Remove(0, pos+1).Trim(false);
                             HandleFunction(/*function name*/ arg,
                                            /*isOperator*/    false,
                                            /*isPointer*/     true);
+                            m_Str.Clear();
                         }
                     }
-                    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)
+                        // pattern unsigned int (*getClientLibVersion)(char** result);
+                        // currently, m_Str = unsigned, token = int, peek = (*getClientLibVersion)
+                        // this may be a function pointer declaration, we can guess without
+                        // reading the next token, if "peek" has a ptr char and only 1 argument
+                        // in it.
+
+                        // see what is inside the (...)
+                        // try to see whether the peek pattern is (* BBB)
+                        wxString arg = peek;
+                        arg.Remove(0,1); // remove '('
+                        if (arg.GetChar(0) == ParserConsts::ptr)
                         {
-                            Token* newToken = DoAddToken(tkVariable, token, m_Tokenizer.GetLineNumber());
-                            if (newToken && !m_TemplateArgument.IsEmpty())
-                                ResolveTemplateArgs(newToken);
+                            arg.RemoveLast();
+                            arg.Remove(0,1).Trim(false); // remove '*'
+                            m_Str << token;
+                            token = m_Tokenizer.GetToken(); //consume the peek
+                            // BBB is now the function ptr's name
+                            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
                         }
-                        m_Tokenizer.GetToken(); // eat args when parsing block
+                        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();
                     }
-
-                    m_Str.Clear();
                 }
                 else if (   (peek  == ParserConsts::colon)
                          && (token != ParserConsts::kw_private)
--
1.9.4.msysgit.0

« Last Edit: September 18, 2014, 04:26:26 pm by Huki »

Offline Huki

  • Multiple posting newcomer
  • *
  • Posts: 95
Re: Is it possible for the parser to support newlib prototypes?
« Reply #42 on: September 18, 2014, 03:01:53 pm »
Maybe, a better method is to try expand every identifier like token if possible, I remembered you have a patch named "cc_parser_general.patch", but when I looked at that patch in my PC, I see that patch contains too many things. (a lot of them is already in trunk)

To enable this feature, we can just do this patch:
Code
 src/plugins/codecompletion/parser/tokenizer.cpp | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/src/plugins/codecompletion/parser/tokenizer.cpp b/src/plugins/codecompletion/parser/tokenizer.cpp
index f47e8dd..31f0546 100644
--- a/src/plugins/codecompletion/parser/tokenizer.cpp
+++ b/src/plugins/codecompletion/parser/tokenizer.cpp
@@ -1246,8 +1246,8 @@ wxString Tokenizer::DoGetToken()
 void Tokenizer::ReplaceMacro(wxString& str)
 {
     // this indicates we are already in macro replacement mode
-    if (m_RepeatReplaceCount > 0)
-    {
+//    if (m_RepeatReplaceCount > 0)
+//    {
         const int id = m_TokenTree->TokenExists(str, -1, tkMacroDef);
         if (id != -1)
         {
@@ -1270,8 +1270,9 @@ void Tokenizer::ReplaceMacro(wxString& str)
         // if in macro expansion mode, we don't want to let the user replacement rule executed
         // again, so just returned
         return;
-    }
+//    }
 
+#if 0
     wxStringHashMap::const_iterator it = s_Replacements.find(str);
     if (it == s_Replacements.end())
         return;
@@ -1344,6 +1345,7 @@ void Tokenizer::ReplaceMacro(wxString& str)
         if (it->second != str && ReplaceBufferText(it->second, false))
             str = DoGetToken();
     }
+#endif
 }
 
 bool Tokenizer::CalcConditionExpression()

Thus, we totally remove all the user defined replacement rules.
I just test the patch, the parsing time is a bit longer, but not too much.  ;D
Sure, I think we can try it, but better to keep it for another commit. ;)

Quote
Another smart method is that we can only check the macro usage on the identifier like token which has all capital characters or underscore.
Maybe it won't be required unless the parsing speed is too bad, but we will see..

Offline ollydbg

  • Developer
  • Lives here!
  • *****
  • Posts: 5910
  • OpenCV and Robotics
    • Chinese OpenCV forum moderator
Re: Is it possible for the parser to support newlib prototypes?
« Reply #43 on: September 21, 2014, 09:32:48 am »
I think your patch will fix the problem, but maybe it's better to have all function pointers checking in one place (in DoParse()), then we just send the result to HandleFunction(). The problem is that we have a pattern like: AAA BBB (*name) (arg), where m_Str = AAA, token = BBB, peek = (*name), and we can't know if this is a function declaration or function ptr without reading the next token after peek.
But I think we can use another trick: strip the '(' in peek, and see if the next char is '*'. If it is, then it should be a function pointer. See this code:
Code
// pattern unsigned int (*getClientLibVersion)(char** result);
// currently, m_Str = unsigned, token = int, peek = (*getClientLibVersion)
// this may be a function pointer declaration, we can guess without
// reading the next token, if "peek" has a ptr char and only 1 argument
// in it.

// see what is inside the (...)
// try to see whether the peek pattern is (* BBB)
wxString arg = peek;
arg.Remove(0,1); // remove '('
if (arg.GetChar(0) == ParserConsts::ptr)
{
    arg.RemoveLast();
    arg.Remove(0,1).Trim(false); // remove '*'
    m_Str << token;
    token = m_Tokenizer.GetToken(); //consume the peek
    // BBB is now the function ptr's name
    HandleFunction(/*function name*/ arg,
                   /*isOperator*/    false,
                   /*isPointer*/     true);
}
else if (!m_Options.useBuffer || m_Options.bufferSkipBlocks)
    // function declaration
else
    // local variable initialized with ctor
Correct, we should recognize the function patter in DoParse(), not HandleFunction.

Quote
Btw, some notes about the function pointer handling:
1) I saw comments like: // *BBB is now the function ptr's name
In fact we strip the '*', so the function name is just BBB.
Correct, thanks.


Quote
2) About the spaces trimming: I checked the ReadParantheses() function (used in DoGetToken()), and I see it guarantees that there is no space immediately after the '(' or before the ')'. So the only place we might have to trim spaces is after removing the '*' (eg, in (* BBB) there is a space before BBB).
So I think all other Trim() calls can be safely removed, like this:
Old code:
Code
arg.Trim(true).RemoveLast();
arg.Remove(0, pos+1);
arg.Trim(true).Trim(false);
New code:
Code
arg.RemoveLast();
arg.Remove(0, pos+1).Trim(false);
...
You are right, thanks.

Quote
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
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,
I have finally gotten around to doing it. See the result below.. :)
...
EDIT: and here is the patch:
...
I'm testing your patch now. It looks like macro expansion is enabled on every identifier like tokens.
If some piece of memory should be reused, turn them to variables (or const variables).
If some piece of operations should be reused, turn them to functions.
If they happened together, then turn them to classes.

Offline Huki

  • Multiple posting newcomer
  • *
  • Posts: 95
Re: Is it possible for the parser to support newlib prototypes?
« Reply #44 on: September 21, 2014, 07:44:47 pm »
I think your patch will fix the problem, but maybe it's better to have all function pointers checking in one place (in DoParse()), then we just send the result to HandleFunction(). The problem is that we have a pattern like: AAA BBB (*name) (arg), where m_Str = AAA, token = BBB, peek = (*name), and we can't know if this is a function declaration or function ptr without reading the next token after peek.
But I think we can use another trick: strip the '(' in peek, and see if the next char is '*'. If it is, then it should be a function pointer. See this code:
Code
// pattern unsigned int (*getClientLibVersion)(char** result);
// currently, m_Str = unsigned, token = int, peek = (*getClientLibVersion)
// this may be a function pointer declaration, we can guess without
// reading the next token, if "peek" has a ptr char and only 1 argument
// in it.

// see what is inside the (...)
// try to see whether the peek pattern is (* BBB)
wxString arg = peek;
arg.Remove(0,1); // remove '('
if (arg.GetChar(0) == ParserConsts::ptr)
{
    arg.RemoveLast();
    arg.Remove(0,1).Trim(false); // remove '*'
    m_Str << token;
    token = m_Tokenizer.GetToken(); //consume the peek
    // BBB is now the function ptr's name
    HandleFunction(/*function name*/ arg,
                   /*isOperator*/    false,
                   /*isPointer*/     true);
}
else if (!m_Options.useBuffer || m_Options.bufferSkipBlocks)
    // function declaration
else
    // local variable initialized with ctor
Correct, we should recognize the function patter in DoParse(), not HandleFunction.

On second look I noticed we can do a little optimization. This code:
Code
wxString arg = peek;
arg.Remove(0,1); // remove '('
if (arg.GetChar(0) == ParserConsts::ptr)
{
    arg.Remove(0,1).Trim(false); // remove '*'
    [...]
}
can be changed to:
Code
if (peek.GetChar(1) == ParserConsts::ptr)
{
    wxString arg = peek;
    arg.Remove(0,2).Trim(false); // remove "(*"
    [...]
}

So the entire code for this case:
Code
// see what is inside the (...)
// try to see whether the peek pattern is (* BBB)
if (peek.GetChar(1) == ParserConsts::ptr)
{
    wxString arg = peek;
    arg.RemoveLast(); // remove ")"
    arg.Remove(0,2).Trim(false); // remove "(* "
    m_Str << token;
    token = m_Tokenizer.GetToken(); //consume the peek
    // BBB is now the function ptr's name
    HandleFunction(/*function name*/ arg,
                   /*isOperator*/    false,
                   /*isPointer*/     true);
}
else if [...]
That way we don't have to create a temporary variable 'arg' every time when we see a function declaration-like pattern, but only when we know for sure it can be a function pointer.


Quote
I'm testing your patch now. It looks like macro expansion is enabled on every identifier like tokens.
Yes, when the (!switchHandled) case is reached in DoParse(), it now expands all macros, including variable-like ones.
« Last Edit: September 21, 2014, 07:46:56 pm by Huki »