Author Topic: Code completion doesnt follow #include in struct  (Read 34692 times)

Offline JGM

  • Lives here!
  • ****
  • Posts: 518
  • Got to practice :)
Re: Code completion doesnt follow #include in struct
« Reply #30 on: March 28, 2011, 01:38:23 am »
Yay, I started working on the expression parser using ceniza code, what I'm struggling is on the macro expansion and recursivness of macro evaluations I'm still thinking whats the best way to do it.

@ollydbg
I copied the quex generated code on your repo to eliminate my custom tokenizer and use something that would simplify the process as improve it. Also I was studying the modifications you did on the tokenizer class. Is there a simpler example to follow? If not I guess I will have to study it more and search the documentation :D

Offline ollydbg

  • Developer
  • Lives here!
  • *****
  • Posts: 6034
  • OpenCV and Robotics
    • Chinese OpenCV forum moderator
Re: Code completion doesnt follow #include in struct
« Reply #31 on: March 28, 2011, 02:37:49 am »
@ollydbg
I copied the quex generated code on your repo to eliminate my custom tokenizer and use something that would simplify the process as improve it. Also I was studying the modifications you did on the tokenizer class. Is there a simpler example to follow? If not I guess I will have to study it more and search the documentation :D
I'm grad that you use quex generated parser now. :D
1, all the lexer code was under the folder: /cppparser/lexer, they were generated by the file cpp.qx(this is called lexer grammar file), and the running command to generate is "cpp.bat" (windows command to call quex library). If you just want to use the generated lexer, you do not need to install python and quex, because the generated code has contains all the files necessary to compile. If you want to modify the cpp.qx file, then you need to install python and quex.

2, I use the mode "pointing to a buffer" in the generated lexer. which is, when it initialized, I set a NULL pointer to the lexer.
Code
m_Quex((QUEX_TYPE_CHARACTER*)s_QuexBuffer,4,(QUEX_TYPE_CHARACTER*)s_QuexBuffer+1)
this code is the initialization of the lexer, and s_QuexBuffer is infact a buffer of NULL.
Code
QUEX_TYPE_CHARACTER Tokenizer::s_QuexBuffer[4] = {0,0,0,0};

3, When you own buffer is ready, I just "point to" it, see:
Code
bool Tokenizer::ReadFile()
{
    bool success = false;
    cc_string fileName = cc_text("");
    if (m_pLoader)
    {
        fileName = m_pLoader->fileName();

        const char * pBuffer = m_pLoader->data();
        m_BufferLen = m_pLoader->length();

        if( m_BufferLen != 0)
            success = true;


        m_Quex.reset_buffer((QUEX_TYPE_CHARACTER*)pBuffer,
                                       m_BufferLen+2,
                                       (QUEX_TYPE_CHARACTER*)pBuffer+m_BufferLen+1);

        (void)m_Quex.token_p_switch(&m_TokenBuffer[0]);

        cout<< "set buffer size" << (int)QUEX_SETTING_BUFFER_SIZE <<endl;

        return true;

}
the char buffer is loaded by "m_pLoader" which is a file loader. so, it contains two info, one is its buffer start address, the other is the length
Code
        const char * pBuffer = m_pLoader->data();
        m_BufferLen = m_pLoader->length();

Code
&m_TokenBuffer[0]
this is the Token address set by the user, so when you call
Code
(void)m_Quex.token_p_switch(&m_TokenBuffer[0]);
this will let the lexer go one step and fill the Token.

4, my Tokenizer is modified from the Tokenizer class in CC's current implementation, but has a lot of things changed. The normal way to receive the Token is like below:

Code
bool Tokenizer::FetchToken(RawToken * pToken)
{
    (void)m_Quex.token_p_switch(pToken);

    QUEX_TYPE_TOKEN_ID id = m_Quex.receive();

    if( id == TKN_TERMINATION )
    {
       m_IsEOF = true;
       return false;
    }
    return true;
}
you supply a token address, and the lexer fill the token. then, you can get the Token's Id and Token's text(if it is an identifier), also it's line and column information.

If you have some problems using quex, feel free to ask me.

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 JGM

  • Lives here!
  • ****
  • Posts: 518
  • Got to practice :)
Re: Code completion doesnt follow #include in struct
« Reply #32 on: March 28, 2011, 06:39:21 am »
after writing a simple function for evaluation of expressions I got stuck  :P

This is the test case:

Code
#ifndef MAX_VALUE
#define MAX_VALUE 10000
#endif

#ifndef MAX_VALUE
#define MAX_VALUE ShouldNotOccurre
#endif

#if MAX_VALUE > 100 //This is the simple test I was to evaluate
int testy;
#endif

Here the function I wrote to prepare a list of Token for the constexprevaluator class
Code
/**
         * Converts an expression from a #if, #else, etc to an array of elements with macros expanded
         * @return Vector with tokens that can be used to evalulate the expression by the ConstExprEvaluator class.
         */
const vector<Token> preprocessor::expand_macro_expression(const vector<preprocessor_token> &expression)
{
   vector<Token> tokens;

   for(unsigned int i=0; i<expression.size(); i++)
        {
            Token token_to_add;

            if(expression[i].type == identifier)
            {
                token_to_add.type = ttNumber;

                if(is_defined(expression[i].token))
                {
                    define macro = get_define(expression[i].token);

                    //For macro definitions
                    if(macro.parameters.size() <= 0)
                    {
                        if(macro.value == "")
                        {
                            //The macro is defined but without a predifined value for it so we default to 1
                            token_to_add.value = "1";
                        }
                        else
                        {
                            //Tha macro has a predifined value (We should check if it's a valid number)
                            token_to_add.value = macro.value;
                        }
                    }

                    //For macro functions, we need to parse the parameters and then evaluate with recursive function
                    else
                    {
                        //for now we just return 1 but this is totally wrong
                        token_to_add.value = "1";
                    }
                }
                else
                {
                    token_to_add.value = "0";
                }
            }

            //Adds any numbers found
            else if(expression[i].type == number)
            {
                token_to_add.type = ttNumber;
                token_to_add.value = expression[i].token;
            }

            //Handle any other operator found
            else
            {
                if(expression[i].token == "?")
                {
                    token_to_add.type = ttQuestion;
                    token_to_add.value = "?";
                }
                else if(expression[i].token == ":")
                {
                    token_to_add.type = ttColon;
                    token_to_add.value = ":";
                }
                else if(expression[i].token == "||")
                {
                    token_to_add.type = ttOr;
                    token_to_add.value = "||";
                }
                else if(expression[i].token == "&&")
                {
                    token_to_add.type = ttAnd;
                    token_to_add.value = "&&";
                }
                else if(expression[i].token == "|")
                {
                    token_to_add.type = ttBitOr;
                    token_to_add.value = "|";
                }
                else if(expression[i].token == "^")
                {
                    token_to_add.type = ttBitXOr;
                    token_to_add.value = "^";
                }
                else if(expression[i].token == "&")
                {
                    token_to_add.type = ttBitAnd;
                    token_to_add.value = "&";
                }
                else if(expression[i].token == "==")
                {
                    token_to_add.type = ttEqual;
                    token_to_add.value = "==";
                }
                else if(expression[i].token == "!=")
                {
                    token_to_add.type = ttNotEqual;
                    token_to_add.value = "!=";
                }
                else if(expression[i].token == "<")
                {
                    token_to_add.type = ttLess;
                    token_to_add.value = "<";
                }
                else if(expression[i].token == ">")
                {
                    token_to_add.type = ttGreater;
                    token_to_add.value = ">";
                }
                else if(expression[i].token == "<=")
                {
                    token_to_add.type = ttLessEqual;
                    token_to_add.value = "<=";
                }
                else if(expression[i].token == ">=")
                {
                    token_to_add.type = ttGreaterEqual;
                    token_to_add.value = ">=";
                }
                else if(expression[i].token == "<<")
                {
                    token_to_add.type = ttLShift;
                    token_to_add.value = "<<";
                }
                else if(expression[i].token == ">>")
                {
                    token_to_add.type = ttRShift;
                    token_to_add.value = ">>";
                }
                else if(expression[i].token == "+")
                {
                    token_to_add.type = ttPlus;
                    token_to_add.value = "+";
                }
                else if(expression[i].token == "-")
                {
                    token_to_add.type = ttMinus;
                    token_to_add.value = "-";
                }
                else if(expression[i].token == "*")
                {
                    token_to_add.type = ttTimes;
                    token_to_add.value = "*";
                }
                else if(expression[i].token == "/")
                {
                    token_to_add.type = ttDivide;
                    token_to_add.value = "/";
                }
                else if(expression[i].token == "%")
                {
                    token_to_add.type = ttModulo;
                    token_to_add.value = "%";
                }
                else if(expression[i].token == "!")
                {
                    token_to_add.type = ttNot;
                    token_to_add.value = "?";
                }
                else if(expression[i].token == "~")
                {
                    token_to_add.type = ttBitNeg;
                    token_to_add.value = "~";
                }
                else if(expression[i].token == "(")
                {
                    token_to_add.type = ttLParen;
                    token_to_add.value = "(";
                }
                else if(expression[i].token == ")")
                {
                    token_to_add.type = ttRParen;
                    token_to_add.value = ")";
                }
            }

            tokens.push_back(token_to_add);
        }

        Token endToken = {ttEndOfTokens, ""};
        tokens.push_back(endToken);


        return tokens;
}

And this is the function that catches the tokens and pass them to the evaluator:

Code
/**
         * Evaluates a macro expression/condition
         * @param define_declaration A macro object
         * @return true if condition is true (duh!) false otherwise
         */
const bool preprocessor::parse_expression(const vector<preprocessor_token> &expression)
{
   bool return_value = false;

   const vector<Token> tokens_vector = expand_macro_expression(expression);

        Token tokens[tokens_vector.size()];

   for(unsigned int i=0; i<tokens_vector.size(); i++)
        {
            tokens[i] = tokens_vector[i];

            cout << tokens_vector[i].value; //To check the tokens are correct (debugging)
        }

        try
        {
            PCToken pcToken = &tokens[0];

            if(ConstExprEvaluator::eval(&pcToken) > 0)
            {
                return_value = true;
            }
        }
        catch (const PreprocessorError &prepError)
        {
            return_value = false;

            //TODO add this exception to m_error
            std::cerr << "Exception: " << prepError.getMessage() << "\n";
        }

        return return_value;
}

and I'm getting the following exception: Exception: Error parsing constant-expression at token

@ceniza
whats the exact meaning of that exception? and sorry for my noobness  :oops:

Edit: The expression to evalualte is 10000 > 100

Edit #2: After drinking a glass of water I was thinking on the code I wrote and I figured out I was adding empty tokens on the for loop sorry for that one!
« Last Edit: March 28, 2011, 08:50:03 am by JGM »

Offline JGM

  • Lives here!
  • ****
  • Posts: 518
  • Got to practice :)
Re: Code completion doesnt follow #include in struct
« Reply #33 on: March 28, 2011, 06:50:06 am »
If you have some problems using quex, feel free to ask me.

Thanks ollydbg!, everything is clearer now :D I copied the right files as you mentioned from the lexer dir, but I didn't understand the logic, now it's clearer :D

Offline Ceniza

  • Developer
  • Lives here!
  • *****
  • Posts: 1441
    • CenizaSOFT
Re: Code completion doesnt follow #include in struct
« Reply #34 on: March 28, 2011, 05:25:20 pm »
@ceniza
whats the exact meaning of that exception? and sorry for my noobness  :oops:

Edit: The expression to evalualte is 10000 > 100

Edit #2: After drinking a glass of water I was thinking on the code I wrote and I figured out I was adding empty tokens on the for loop sorry for that one!

I guess Edit #2 means that you have solved the issue already.

A few comments:

* If a macro is defined as nothing (empty), the replacement is empty, not "1". I hope it is a placeholder.
* There is no need to adapt your code to use the ConstExprEvaluator's interface. Instead, adapt ConstExprEvaluator's to your code (the current interface was a quick hack to remove the one that used TokenProvider).
* A constant expression is true when it is not 0 (!= 0), not when it is positive (> 0).
* The code of the ConstExprEvaluator unnecessarily calls the peek function over and over again. It was a quick replacement to get it working. Calling skipWhiteSpace on eval() once and using the current token's value instead of calling peek is more optimal. Additionally, replace the usage of operator ++ by a call to a function that advances the 'iterator' (using operator ++) followed by a call to skipWhiteSpace (call it nextToken).

Offline JGM

  • Lives here!
  • ****
  • Posts: 518
  • Got to practice :)
Re: Code completion doesnt follow #include in struct
« Reply #35 on: March 28, 2011, 07:25:30 pm »
I guess Edit #2 means that you have solved the issue already.

Yep it is working now  :wink: Your expression evaluator code is really cool!

* If a macro is defined as nothing (empty), the replacement is empty, not "1". I hope it is a placeholder.

ohhh

* A constant expression is true when it is not 0 (!= 0), not when it is positive (> 0).

In other words, negative numbers evaluate to true, didn't supposed that

* The code of the ConstExprEvaluator unnecessarily calls the peek function over and over again. It was a quick replacement to get it working. Calling skipWhiteSpace on eval() once and using the current token's value instead of calling peek is more optimal. Additionally, replace the usage of operator ++ by a call to a function that advances the 'iterator' (using operator ++) followed by a call to skipWhiteSpace (call it nextToken).

There are also many things on my code that need optimization

Thanks for all your feedback, great source of reference  :D

Offline ollydbg

  • Developer
  • Lives here!
  • *****
  • Posts: 6034
  • OpenCV and Robotics
    • Chinese OpenCV forum moderator
Re: Code completion doesnt follow #include in struct
« Reply #36 on: April 11, 2011, 07:29:12 am »
find some preprocessor source links
https://sourceforge.net/apps/trac/cppcheck/ticket/516
PS: cppcheck has a preprocessor too.
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.