コード例 #1
0
ファイル: CParser.cpp プロジェクト: yuan-/cminusminus
void CParser::Run()
{
    // Loop through the entire token list
    for(size_t i = 0; i < m_lTokenList.size(); i++)
    {
        // Get the current token
        CToken CurrentToken = m_lTokenList[i];
        // Get the previous token on the list
        CToken PreviousToken = (i > 0) ? m_lTokenList[i - 1] : CToken();
        // Get the token before the previous token on the list (used to check in the 'something = somethingelse' kind of checks)
        CToken SecondPreviousToken = (i > 1) ? m_lTokenList[i - 2] : CToken();

        // Check if the iterator is currently at the start of the list
        // If it is, we need to perform some seperate checks
        if(i == 0)
        {
            // The only things allowed at the start of the script is a { or type
            if(CurrentToken.m_iTokenType != OPEN_CURLY_BRACKET_TOKEN && CurrentToken.m_iTokenType != FLOAT_TYPE_TOKEN && CurrentToken.m_iTokenType != INTEGER_TYPE_TOKEN && CurrentToken.m_iTokenType != STRING_TYPE_TOKEN)
                PushBackError(CurrentToken.m_iLine, "Unexpected '" + CurrentToken.m_sValue + "' at start of the script found.");

            // We don't need to execute the rest of the checks, call continue
            continue;
        }

        // If the previous token was an equal sign, and we have a token before that, we're in an assignement statement
        if(PreviousToken.m_iTokenType == EQUALSIGN_TOKEN && SecondPreviousToken.m_iTokenType != INVALID_TOKEN_TYPE)
        {
            // First check if we're assigning to anything valid
            // It cannot be a value constant, string literal or non-existing variable
            if(!VariableExists(SecondPreviousToken.m_sValue))
            {
                // The user is trying to assign something to a constant value (for example: int 5 = 3;)
                if(IsFloatOrInteger(SecondPreviousToken.m_sValue))
                    PushBackError(CurrentToken.m_iLine, "Cannot assign to a value constant (" + SecondPreviousToken.m_sValue + ").");

                // It's a string literal
                else if(SecondPreviousToken.m_iTokenType == STRING_LITERAL_TOKEN)
                    PushBackError(CurrentToken.m_iLine, "Cannot assign anything to a string literal.");

                // Variable simply doesn't exist
                else
                    PushBackError(CurrentToken.m_iLine, "Cannot assign anything to " + SecondPreviousToken.m_sValue + ", variable does not exist.");
                continue;
            }

            // Get the iterator in the VariableList that represents the variable we're assigning to
            VariableList::iterator LeftHandSide = GetVariableListIteratorFromVariableName(SecondPreviousToken.m_sValue);

            // Now check if we're trying to assign something valid to the variable
            if(!VariableExists(CurrentToken.m_sValue))
            {
                // Check if the current token is a value constant
                // We handle 'var = constants' type of statements here

                // Check if it's a string literal
                if(CurrentToken.m_iTokenType == STRING_LITERAL_TOKEN)
                {
                    // Make sure we're assigning it to a string
                    if((*LeftHandSide).m_eType != VARIABLE_TYPE_STRING)
                    {
                        PushBackError(CurrentToken.m_iLine, "Cannot assign \"" + CurrentToken.m_sValue + "\" to '" + SecondPreviousToken.m_sValue + "', the types differ.");
                        continue;
                    }

                    // Set the hasBeenAssignedAnything flag for this variable to true
                    // This flags the variable as been defined
                    (*LeftHandSide).m_sValue = CurrentToken.m_sValue;

                    // Set the value for this variable
                    (*LeftHandSide).m_bHasBeenAssignedAnything = true;
                }

                // It's a float or integer
                else if(IsFloatOrInteger(CurrentToken.m_sValue))
                {
                    // Is it an integer constant?
                    if(IsInteger(CurrentToken.m_sValue))
                    {
                        // Type checking
                        if((*LeftHandSide).m_eType != VARIABLE_TYPE_INTEGER)
                        {
                            PushBackError(CurrentToken.m_iLine, "Cannot assign '" + CurrentToken.m_sValue + "' to '" + SecondPreviousToken.m_sValue + "', the types differ.");
                            continue;
                        }

                        // Set the hasBeenAssignedAnything flag for this variable to true
                        // This flags the variable as been defined
                        (*LeftHandSide).m_bHasBeenAssignedAnything = true;

                        // Set the value for this variable
                        (*LeftHandSide).m_iValue = atoi(CurrentToken.m_sValue.c_str());
                    }

                    // Or a float constant
                    else
                    {
                        // Add some typechecking
                        if((*LeftHandSide).m_eType != PARAMETER_TYPE_FLOAT)
                        {
                            PushBackError(CurrentToken.m_iLine, "Cannot assign '" + CurrentToken.m_sValue + "' to '" + SecondPreviousToken.m_sValue + "', the types differ.");
                            continue;
                        }

                        // Set the hasBeenAssignedAnything flag for this variable to true
                        // This flags the variable as been defined
                        (*LeftHandSide).m_bHasBeenAssignedAnything = true;

                        // Set the value for this variable
                        (*LeftHandSide).m_fValue = atof(CurrentToken.m_sValue.c_str());
                    }
                }

                // It's not a string literal, not a float or integer, this means the user is trying to assign a variable to another variable
                // While the right hand side variable doesn't exist
                else
                {
                    // Wait, it might be a function call, make sure the next token isn't a OPEN_BRACKET_TOKEN
                    if((i + 1) != m_lTokenList.size() && m_lTokenList[i + 1].m_iTokenType != OPEN_BRACKET_TOKEN)
                    {
                        // It wasn't, the rhs doesn't exist
                        PushBackError(CurrentToken.m_iLine, "Cannot assign '" + CurrentToken.m_sValue + "' to '" + SecondPreviousToken.m_sValue + "', '" + CurrentToken.m_sValue + "' does not exist.");
                        continue;
                    }
                }
            }

            // The current token isn't a value constant, it's another variable
            // We handle 'var = var' type of statements here
            else
            {
                // Get the iterator for the right hand side variable
                VariableList::iterator RightHandSide = GetVariableListIteratorFromVariableName(CurrentToken.m_sValue);

                // Check if the variable is allowed the other variable
                if(!HasCorrectIndentationLevel((*RightHandSide).m_oIndentation, SecondPreviousToken.m_oIndentation))
                {
                    PushBackError(CurrentToken.m_iLine, "Cannot access " + (*RightHandSide).m_sValueName + ", that variable is declared on another level.");
                    continue;
                }

                // Type checking: make sure the variables have the same types
                if((*LeftHandSide).m_eType != (*RightHandSide).m_eType)
                {
                    PushBackError(CurrentToken.m_iLine, "Cannot assign '" + CurrentToken.m_sValue + "' to '" + SecondPreviousToken.m_sValue + "', the types differ.");
                    continue;
                }

                // Set the hasBeenAssignedAnything flag to true
                // This flags the variable as been defined
                (*LeftHandSide).m_bHasBeenAssignedAnything = true;

                // Set the value
                if((*LeftHandSide).m_eType == VARIABLE_TYPE_INTEGER)
                    (*LeftHandSide).m_iValue = (*RightHandSide).m_iValue;

                if((*LeftHandSide).m_eType == PARAMETER_TYPE_FLOAT)
                    (*LeftHandSide).m_fValue = (*RightHandSide).m_fValue;

                if((*LeftHandSide).m_eType == VARIABLE_TYPE_STRING)
                    (*LeftHandSide).m_sValue = (*RightHandSide).m_sValue;
            }
        }

        // The previous token was either a + or -
        if(PreviousToken.m_iTokenType == PLUS_OPERATOR_TOKEN || PreviousToken.m_iTokenType == MINUS_OPERATOR_TOKEN)
        {
            // Get the token for the assignment variable (the variable we're assigning to)
            CToken VariableWhichIsBeingAssignedTo = CToken();

            // This variable holds the amounts of times we had to decrement the iterator in order
            // to find the variable we're assigning to. We need to save the amount of times we've
            // decremented the iterator in order to increment it the same amount of times again,
            // otherwise this loop will run infinitely
            int iAmountOfDecrements = i;

            // Infinite loop
            while(true)
            {
                // Wait, if the iterator is already at the start of the list, break out of the loop
                // We can't decrement an iterator which is already at the start of the loop
                if(iAmountOfDecrements == m_lTokenList.size())
                    break;

                // Save the current iterator position
                VariableWhichIsBeingAssignedTo = m_lTokenList[iAmountOfDecrements--];

                // If we the token we're processing is a VALUE_TOKEN and it's not a constant value (eg not a float or int)
                if(VariableWhichIsBeingAssignedTo.m_iTokenType == VALUE_TOKEN && !IsFloatOrInteger(VariableWhichIsBeingAssignedTo.m_sValue))
                    break;
            }

            // Now we get the VariableList iterator which is pointing at the correct variable we want to assign to
            VariableList::iterator LeftHandSide = GetVariableListIteratorFromVariableName(VariableWhichIsBeingAssignedTo.m_sValue);
            // Get the variable type of the current token (eg what we're trying to assign to our variable)
            eVariableTypes eType = GetVariableType(CurrentToken.m_sValue);

            // Make sure the iterator is correct (it's not correct if the example we're trying to assign to doesn't exist)
            if(LeftHandSide != m_lVariableList.end())
            {
                // Wait, is the type of what we're trying to assign to the variable the same as the variable?
                if(eType != (*LeftHandSide).m_eType)
                {
                    PushBackError(CurrentToken.m_iLine, "Cannot concatenate '" + CurrentToken.m_sValue + "' and '" + (*LeftHandSide).m_sValueName + "', the types differ.");
                    continue;
                }

                // Is this the plus operator?
                if(PreviousToken.m_iTokenType == PLUS_OPERATOR_TOKEN)
                {
                    // int + int
                    if(eType == VARIABLE_TYPE_INTEGER)
                        (*LeftHandSide).m_iValue += atoi(CurrentToken.m_sValue.c_str());
                    // float + float
                    if(eType == PARAMETER_TYPE_FLOAT)
                        (*LeftHandSide).m_fValue += atof(CurrentToken.m_sValue.c_str());
                    // string + string
                    if(eType == VARIABLE_TYPE_STRING)
                    {
                        // Remove the double quotes from the string
                        std::string sStringLiteral = CurrentToken.m_sValue;

                        // Concat the strings
                        (*LeftHandSide).m_sValue += sStringLiteral;
                    }
                }

                // Is this the minus operator?
                if(PreviousToken.m_iTokenType == MINUS_OPERATOR_TOKEN)
                {
                    // int - int
                    if(eType == VARIABLE_TYPE_INTEGER)
                        (*LeftHandSide).m_iValue -= atoi(CurrentToken.m_sValue.c_str());
                    // float - float
                    if(eType == PARAMETER_TYPE_FLOAT)
                        (*LeftHandSide).m_fValue -= atof(CurrentToken.m_sValue.c_str());
                    // String doesn't support operator-
                    if(eType == VARIABLE_TYPE_STRING)
                        PushBackError(CurrentToken.m_iLine, "The string type does not define the minus operator.");
                }
            }
            continue;
        }

        // Convert the line number of the previous token to a string
        std::stringstream sLineNumberOfPreviousToken;
        sLineNumberOfPreviousToken << PreviousToken.m_iLine;
        std::string sPreviousTokensLine = sLineNumberOfPreviousToken.str();

        if(CurrentToken.m_iTokenType == OPEN_CURLY_BRACKET_TOKEN)
        {
            // Allowed previous tokens: {, }, ;
            // Not allowed previous tokens: {, float, string, int, =, VALUE_TOKEN
            if(PreviousToken.m_iTokenType != OPEN_CURLY_BRACKET_TOKEN && PreviousToken.m_iTokenType != CLOSE_CURLY_BRACKET_TOKEN && PreviousToken.m_iTokenType != SEMICOLON_TOKEN)
                PushBackError(CurrentToken.m_iLine, "Finish the statement at line " + sPreviousTokensLine + " first.");

            continue;
        }

        if(CurrentToken.m_iTokenType == CLOSE_CURLY_BRACKET_TOKEN)
        {
            // Allowed previous tokens: {, }, ;
            // Not allowed previous tokens: }, float, string, int, =, VALUE_TOKEN
            if(PreviousToken.m_iTokenType != OPEN_CURLY_BRACKET_TOKEN && PreviousToken.m_iTokenType != CLOSE_CURLY_BRACKET_TOKEN && PreviousToken.m_iTokenType != SEMICOLON_TOKEN)
                PushBackError(CurrentToken.m_iLine, "Finish the statement at line " + sPreviousTokensLine + " first.");

            continue;
        }

        if(CurrentToken.m_iTokenType == SEMICOLON_TOKEN)
        {
            // Allowed previous tokens: {, ;, }, VALUE_TOKEN
            // Not allowed previous tokens: =, float, string, int
            if(PreviousToken.m_iTokenType != CLOSE_CURLY_BRACKET_TOKEN && PreviousToken.m_iTokenType != OPEN_CURLY_BRACKET_TOKEN && PreviousToken.m_iTokenType != VALUE_TOKEN && PreviousToken.m_iTokenType != SEMICOLON_TOKEN)
            {
                if(PreviousToken.m_iTokenType == STRING_TYPE_TOKEN || PreviousToken.m_iTokenType == INTEGER_TYPE_TOKEN || PreviousToken.m_iTokenType == FLOAT_TYPE_TOKEN)
                    PushBackError(CurrentToken.m_iLine, "Expected an equal sign followed by a value or variable on line " + sPreviousTokensLine);

                if(PreviousToken.m_iTokenType == EQUALSIGN_TOKEN)
                    PushBackError(CurrentToken.m_iLine, "Expected a value or variable after the equal sign on line " + sPreviousTokensLine);
            }

            continue;
        }

        if(CurrentToken.m_iTokenType == EQUALSIGN_TOKEN)
        {
            // Allowed previous tokens: VALUE_TOKEN
            // Not allowed previous tokens: =, float, string, int, {, ;, }
            if(PreviousToken.m_iTokenType != VALUE_TOKEN)
                PushBackError(CurrentToken.m_iLine, PreviousToken.m_sValue + " cannot be followed by an equal sign.");

            continue;
        }

        if(CurrentToken.m_iTokenType == INTEGER_TYPE_TOKEN || CurrentToken.m_iTokenType == FLOAT_TYPE_TOKEN || CurrentToken.m_iTokenType == STRING_TYPE_TOKEN)
        {
            // Allowed previous tokens: {, }, ;
            // Not allowed previous tokens: =, float, string, int, VALUE_TOKEN
            if(PreviousToken.m_iTokenType != CLOSE_CURLY_BRACKET_TOKEN && PreviousToken.m_iTokenType != OPEN_CURLY_BRACKET_TOKEN && PreviousToken.m_iTokenType != SEMICOLON_TOKEN)
                PushBackError(CurrentToken.m_iLine, PreviousToken.m_sValue + " cannot be followed by a type.");

            continue;
        }

        if(CurrentToken.m_iTokenType == VALUE_TOKEN)
        {
            // If the current token is a value token and the next one is an open bracket token, the user is trying to call a function
            if(i != m_lTokenList.size() && m_lTokenList[i + 1].m_iTokenType == OPEN_BRACKET_TOKEN)
            {
                // Get the function name
                std::string FunctionName = CurrentToken.m_sValue;

                // The LoopToken is the token we'll be using in the loop.
                CToken LoopToken = m_lTokenList[i + 1];
                // We also save the token before the current token, this is used to get a parameter list
                CToken TokenBeforeCurrentLoopToken = m_lTokenList[i];

                // This variable holds the amounts of times we've executed the loop
                int iAmountOfIncrements = i;
                // The parameter list for the function
                ParameterList lParameterList;

                // Infinite loop
                while(true)
                {
                    // If the iAmountOfIncrements variable is equal to the token size, we can't get the next token on the list
                    // Break out of the loop
                    if(iAmountOfIncrements == m_lTokenList.size())
                        break;

                    // We found a close bracket token, exit the loop
                    if(LoopToken.m_iTokenType == CLOSE_BRACKET_TOKEN)
                        break;

                    // The current token is a value token or a string literal, and the previous token was either a ( or a ,
                    // This is a parameter for the function, push it back onto the list
                    if(LoopToken.m_iTokenType == VALUE_TOKEN || LoopToken.m_iTokenType == STRING_LITERAL_TOKEN && (TokenBeforeCurrentLoopToken.m_iTokenType == OPEN_BRACKET_TOKEN || TokenBeforeCurrentLoopToken.m_iTokenType == COMMA_TOKEN))
                    {
                        // It might be a variable, does it exist?
                        if(!VariableExists(LoopToken.m_sValue))
                        {
                            // Doesn't exist
                            // It's a float or integer
                            if(IsFloatOrInteger(LoopToken.m_sValue))
                            {
                                // It's an integer
                                if(IsInteger(LoopToken.m_sValue))
                                    lParameterList.push_back(CParameter(PARAMETER_TYPE_INTEGER, atoi(LoopToken.m_sValue.c_str())));
                                // It's a float
                                else
                                    lParameterList.push_back(CParameter(PARAMETER_TYPE_FLOAT, (float) atof(LoopToken.m_sValue.c_str())));
                            }
                            // It's not a float or integer, must be a string
                            else lParameterList.push_back(CParameter(PARAMETER_TYPE_STRING, LoopToken.m_sValue));
                        }

                        // It's a variable, this parameter was a variable
                        else
                        {
                            // Get the iterator on the VariableList
                            VariableList::iterator variableIterator = GetVariableListIteratorFromVariableName(LoopToken.m_sValue);

                            // Get the type of the variable and push it back onto the parameter list
                            if((*variableIterator).m_eType == VARIABLE_TYPE_INTEGER)
                                lParameterList.push_back(CParameter(PARAMETER_TYPE_INTEGER, (*variableIterator).m_iValue));
                            if((*variableIterator).m_eType == VARIABLE_TYPE_FLOAT)
                                lParameterList.push_back(CParameter(PARAMETER_TYPE_FLOAT, (float) (*variableIterator).m_fValue));
                            if((*variableIterator).m_eType == VARIABLE_TYPE_STRING)
                                lParameterList.push_back(CParameter(PARAMETER_TYPE_STRING, (*variableIterator).m_sValue));
                        }
                    }

                    // Save the current token and the token before the previous one
                    TokenBeforeCurrentLoopToken = m_lTokenList[iAmountOfIncrements];
                    LoopToken = m_lTokenList[++iAmountOfIncrements];
                }

                // Wait, does the function exist?
                if(!CFunctionWrapper::FunctionExists(FunctionName))
                {
                    PushBackError(CurrentToken.m_iLine, "Could not call " + FunctionName + ", function does not exist.");
                    continue;
                }

                // Call the function
                CFunctionCallAttempt oAttempt = CFunctionWrapper::CallFunction(FunctionName, lParameterList);

                // Did an error occur while calling the function?
                if(oAttempt.m_bErrorOccured)
                {
                    PushBackError(CurrentToken.m_iLine, oAttempt.m_sErrorMessage);
                    continue;
                }

                // If the token before the function name token is an equal sign token, this isn't a regular call
                // It's an assignment statement.
                if(PreviousToken.m_iTokenType == EQUALSIGN_TOKEN)
                {
                    // Get the variable iterator pointing to the variable we're trying to assign to
                    VariableList::iterator VariableAssignmentIterator = GetVariableListIteratorFromVariableName(SecondPreviousToken.m_sValue);

                    // Does the return type of the function match the variable's type?
                    if((*VariableAssignmentIterator).m_eType != oAttempt.m_oReturnValue.m_eType)
                    {
                        PushBackError(CurrentToken.m_iLine, "Could not assign the return value of " + FunctionName + " to " + (*VariableAssignmentIterator).m_sValueName + ", the types differ.");
                        continue;
                    }

                    // Something has been assigned to this value
                    (*VariableAssignmentIterator).m_bHasBeenAssignedAnything = true;

                    // Save the value
                    if(oAttempt.m_oReturnValue.m_eType == VARIABLE_TYPE_INTEGER)
                        (*VariableAssignmentIterator).m_iValue = oAttempt.m_oReturnValue.m_iValue;

                    if(oAttempt.m_oReturnValue.m_eType == VARIABLE_TYPE_FLOAT)
                        (*VariableAssignmentIterator).m_fValue = oAttempt.m_oReturnValue.m_fValue;

                    if(oAttempt.m_oReturnValue.m_eType == VARIABLE_TYPE_STRING)
                        (*VariableAssignmentIterator).m_sValue = oAttempt.m_oReturnValue.m_sValue;
                }
            }
            // End of function checking

            // Allowed previous tokens: {, }, ;, =, float, string, int, =
            // Not allowed previous tokens: VALUE_TOKEN
            if(PreviousToken.m_iTokenType == VALUE_TOKEN)
            {
                PushBackError(CurrentToken.m_iLine, "'" + PreviousToken.m_sValue + "' cannot be followed by '" + CurrentToken.m_sValue + "'.");
            }

            else
            {
                // If the current token is a value token
                // and the previous token was either a float, string or int type, the user is trying to declare a variable
                if(PreviousToken.m_iTokenType == FLOAT_TYPE_TOKEN || PreviousToken.m_iTokenType == INTEGER_TYPE_TOKEN || PreviousToken.m_iTokenType == STRING_TYPE_TOKEN)
                {
                    // Check if the current token is a valid variable name
                    if(!IsFloatOrInteger(CurrentToken.m_sValue))
                    {
                        if(VariableExists(CurrentToken.m_sValue))
                        {
                            PushBackError(CurrentToken.m_iLine, "'" + CurrentToken.m_sValue + "' already exists. Cannot re-declare a variable.");
                        }
                        else
                        {
                            // Setup a CVariable object
                            CVariable oVariable;
                            oVariable.m_sValueName = CurrentToken.m_sValue;

                            // Set the type of the CVariable object according to the type of token the previous token object had
                            if(PreviousToken.m_iTokenType == INTEGER_TYPE_TOKEN)
                                oVariable.m_eType = VARIABLE_TYPE_INTEGER;

                            if(PreviousToken.m_iTokenType == FLOAT_TYPE_TOKEN)
                                oVariable.m_eType = VARIABLE_TYPE_FLOAT;

                            if(PreviousToken.m_iTokenType == STRING_TYPE_TOKEN)
                                oVariable.m_eType = VARIABLE_TYPE_STRING;

                            // Save the indentation level for this variable
                            oVariable.m_oIndentation = CurrentToken.m_oIndentation;

                            // Push it onto the variable list
                            m_lVariableList.push_back(oVariable);
                        }
                    }
                }
            }

            continue;
        }
    }

    // Now loop through the error list
#if _DEBUG
    if(m_lErrorList.size() > 0) CLogger::Write("\n* Errors found:");
#endif

    // Log every error
    for(ErrorList::iterator iterator = m_lErrorList.begin(); iterator != m_lErrorList.end(); iterator++)
        CLogger::Write("Line %d: %s", (*iterator).m_iLine, (*iterator).m_sMessage.c_str());

    // If we're compiling in debug mode we show the variables we've found in the scripts
#if _DEBUG
    CLogger::Write("\n* Variables found:");
    for(VariableList::iterator iterator = m_lVariableList.begin(); iterator != m_lVariableList.end(); iterator++)
    {
        // Check if the variable has been assigned anything
        if((*iterator).m_bHasBeenAssignedAnything)
        {
            // Output the variable name and type
            if((*iterator).m_eType == VARIABLE_TYPE_INTEGER)
                CLogger::Write("Variable %s (integer) has value %d (tab level: %d, tab id: %d)", (*iterator).m_sValueName.c_str(), (*iterator).m_iValue, (*iterator).m_oIndentation.m_iLevel, (*iterator).m_oIndentation.m_iLevelID);

            if((*iterator).m_eType == PARAMETER_TYPE_FLOAT)
                CLogger::Write("Variable %s (float) has value %.2f (tab level: %d, tab id: %d)", (*iterator).m_sValueName.c_str(), (*iterator).m_fValue, (*iterator).m_oIndentation.m_iLevel, (*iterator).m_oIndentation.m_iLevelID);

            if((*iterator).m_eType == VARIABLE_TYPE_STRING)
                CLogger::Write("Variable %s (string) has value %s (tab level: %d, tab id: %d)", (*iterator).m_sValueName.c_str(), (*iterator).m_sValue.c_str(), (*iterator).m_oIndentation.m_iLevel, (*iterator).m_oIndentation.m_iLevelID);
        }

        else CLogger::Write("Variable %s has been declared but not yet defined. (tab level: %d, tab id: %d)", (*iterator).m_sValueName.c_str(), (*iterator).m_oIndentation.m_iLevel, (*iterator).m_oIndentation.m_iLevelID);
    }
#endif
}
コード例 #2
0
ファイル: CToken.hpp プロジェクト: KalDragon/urde
 TToken& operator=(T* obj) {*this = CToken(GetIObjObjectFor(obj)); return this;}