    /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
    /*
     * This file is part of the LibreOffice project.
     *
     * This Source Code Form is subject to the terms of the Mozilla Public
     * License, v. 2.0. If a copy of the MPL was not distributed with this
     * file, You can obtain one at http://mozilla.org/MPL/2.0/.
     *
     * This file incorporates work covered by the following license notice:
     *
     *   Licensed to the Apache Software Foundation (ASF) under one or more
     *   contributor license agreements. See the NOTICE file distributed
     *   with this work for additional information regarding copyright
     *   ownership. The ASF licenses this file to you under the Apache
     *   License, Version 2.0 (the "License"); you may not use this file
     *   except in compliance with the License. You may obtain a copy of
     *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
     */

    #include <basic/sbx.hxx>
    #include <sbunoobj.hxx>
    #include <parser.hxx>
    #include <svtools/miscopt.hxx>
    #include <osl/diagnose.h>
    #include <com/sun/star/reflection/theCoreReflection.hpp>
    #include <comphelper/processfactory.hxx>
    #include <com/sun/star/reflection/XInterfaceMemberTypeDescription.hpp>
    #include <com/sun/star/reflection/XIdlMethod.hpp>
    #include <com/sun/star/uno/Exception.hpp>
    #include <basic/codecompletecache.hxx>
    #include <memory>

    using namespace ::com::sun::star;
    using namespace ::com::sun::star::uno;

    // Declaration of a variable
    // If there are errors it will be parsed up to the comma or the newline.
    // Return-value: a new instance, which were inserted and then deleted.
    // Array-Index were returned as SbiExprList

    SbiSymDef* SbiParser::VarDecl( SbiExprListPtr* ppDim, bool bStatic, bool bConst )
    {
        bool bWithEvents = false;
        if( Peek() == WITHEVENTS )
        {
            Next();
            bWithEvents = true;
        }
        if( !TestSymbol() ) return nullptr;
        SbxDataType t = eScanType;
        SbiSymDef* pDef = bConst ? new SbiConstDef( aSym ) : new SbiSymDef( aSym );
        SbiExprListPtr pDim;
        // Brackets?
        if( Peek() == LPAREN )
        {
            pDim = SbiExprList::ParseDimList( this );
            if( !pDim->GetDims() )
                pDef->SetWithBrackets();
        }
        pDef->SetType( t );
        if( bStatic )
            pDef->SetStatic();
        if( bWithEvents )
            pDef->SetWithEvents();
        TypeDecl( *pDef );
        if( !ppDim && pDim )
        {
            if(pDim->GetDims() )
                Error( ERRCODE_BASIC_EXPECTED, "()" );
        }
        else if( ppDim )
            *ppDim = std::move(pDim);
        return pDef;
    }

    // Resolving of a AS-Type-Declaration
    // The data type were inserted into the handed over variable

    void SbiParser::TypeDecl( SbiSymDef& rDef, bool bAsNewAlreadyParsed )
    {
        SbxDataType eType = rDef.GetType();
        if( bAsNewAlreadyParsed || Peek() == AS )
        {
            short nSize = 0;
            if( !bAsNewAlreadyParsed )
                Next();
            rDef.SetDefinedAs();
            SbiToken eTok = Next();
            if( !bAsNewAlreadyParsed && eTok == NEW )
            {
                rDef.SetNew();
                eTok = Next();
            }
            switch( eTok )
            {
                case ANY:
                    if( rDef.IsNew() )
                        Error( ERRCODE_BASIC_SYNTAX );
                    eType = SbxVARIANT; break;
                case TINTEGER:
                case TLONG:
                case TSINGLE:
                case TDOUBLE:
                case TCURRENCY:
                case TDATE:
                case TSTRING:
                case TOBJECT:
                case ERROR_:
                case TBOOLEAN:
                case TVARIANT:
                case TBYTE:
                    if( rDef.IsNew() )
                        Error( ERRCODE_BASIC_SYNTAX );
                    eType = (eTok==TBYTE) ? SbxBYTE : SbxDataType( eTok - TINTEGER + SbxINTEGER );
                    if( eType == SbxSTRING )
                    {
                        // STRING*n ?
                        if( Peek() == MUL )
                        {       // fixed size!
                            Next();
                            SbiConstExpression aSize( this );
                            nSize = aSize.GetShortValue();
                            if( nSize < 0 || (bVBASupportOn && nSize <= 0) )
                                Error( ERRCODE_BASIC_OUT_OF_RANGE );
                            else
                                rDef.SetFixedStringLength( nSize );
                        }
                    }
                    break;
                case SYMBOL: // can only be a TYPE or a object class!
                    if( eScanType != SbxVARIANT )
                        Error( ERRCODE_BASIC_SYNTAX );
                    else
                    {
                        OUString aCompleteName = aSym;

                        // #52709 DIM AS NEW for Uno with full-qualified name
                        if( Peek() == DOT )
                        {
                            OUString aDotStr( '.' );
                            while( Peek() == DOT )
                            {
                                aCompleteName += aDotStr;
                                Next();
                                SbiToken ePeekTok = Peek();
                                if( ePeekTok == SYMBOL || IsKwd( ePeekTok ) )
                                {
                                    Next();
                                    aCompleteName += aSym;
                                }
                                else
                                {
                                    Next();
                                    Error( ERRCODE_BASIC_UNEXPECTED, SYMBOL );
                                    break;
                                }
                            }
                        }
                        else if( rEnumArray->Find( aCompleteName, SbxClassType::Object ) || ( IsVBASupportOn() && VBAConstantHelper::instance().isVBAConstantType( aCompleteName ) ) )
                        {
                            eType = SbxLONG;
                            break;
                        }

                        // Take over in the string pool
                        rDef.SetTypeId( aGblStrings.Add( aCompleteName ) );

                        if( rDef.IsNew() && pProc == nullptr )
                            aRequiredTypes.push_back( aCompleteName );
                    }
                    eType = SbxOBJECT;
                    break;
                case FIXSTRING: // new syntax for complex UNO types
                    rDef.SetTypeId( aGblStrings.Add( aSym ) );
                    eType = SbxOBJECT;
                    break;
                default:
                    Error( ERRCODE_BASIC_UNEXPECTED, eTok );
                    Next();
            }
            // The variable could have been declared with a suffix
            if( rDef.GetType() != SbxVARIANT )
            {
                if( rDef.GetType() != eType )
                    Error( ERRCODE_BASIC_VAR_DEFINED, rDef.GetName() );
                else if( eType == SbxSTRING && rDef.GetLen() != nSize )
                    Error( ERRCODE_BASIC_VAR_DEFINED, rDef.GetName() );
            }
            rDef.SetType( eType );
            rDef.SetLen( nSize );
        }
    }

    // Here variables, arrays and structures were defined.
    // DIM/PRIVATE/PUBLIC/GLOBAL

    void SbiParser::Dim()
    {
        DefVar( SbiOpcode::DIM_, pProc && bVBASupportOn && pProc->IsStatic() );
    }

    void SbiParser::DefVar( SbiOpcode eOp, bool bStatic )
    {
        SbiSymPool* pOldPool = pPool;
        bool bSwitchPool = false;
        bool bPersistentGlobal = false;
        SbiToken eFirstTok = eCurTok;

        if( pProc && ( eCurTok == GLOBAL || eCurTok == PUBLIC || eCurTok == PRIVATE ) )
            Error( ERRCODE_BASIC_NOT_IN_SUBR, eCurTok );
        if( eCurTok == PUBLIC || eCurTok == GLOBAL )
        {
            bSwitchPool = true;     // at the right moment switch to the global pool
            if( eCurTok == GLOBAL )
                bPersistentGlobal = true;
        }
        // behavior in VBA is that a module scope variable's lifetime is
        // tied to the document. e.g. a module scope variable is global
           if(  GetBasic()->IsDocBasic() && bVBASupportOn && !pProc )
            bPersistentGlobal = true;
        // PRIVATE is a synonymous for DIM
        // _CONST_?
        bool bConst = false;
        if( eCurTok == CONST_ )
            bConst = true;
        else if( Peek() == CONST_ )
        {
            Next();
            bConst = true;
        }

        // #110004 It can also be a sub/function
        if( !bConst && (eCurTok == SUB || eCurTok == FUNCTION || eCurTok == PROPERTY ||
                        eCurTok == STATIC || eCurTok == ENUM || eCurTok == DECLARE || eCurTok == TYPE) )
        {
            // Next token is read here, because !bConst
            bool bPrivate = ( eFirstTok == PRIVATE );

            if( eCurTok == STATIC )
            {
                Next();
                DefStatic( bPrivate );
            }
            else if( eCurTok == SUB || eCurTok == FUNCTION || eCurTok == PROPERTY )
            {
                // End global chain if necessary (not done in
                // SbiParser::Parse() under these conditions
                if( bNewGblDefs && nGblChain == 0 )
                {
                    nGblChain = aGen.Gen( SbiOpcode::JUMP_, 0 );
                    bNewGblDefs = false;
                }
                Next();
                DefProc( false, bPrivate );
                return;
            }
            else if( eCurTok == ENUM )
            {
                Next();
                DefEnum( bPrivate );
                return;
            }
            else if( eCurTok == DECLARE )
            {
                Next();
                DefDeclare( bPrivate );
                return;
            }
            // #i109049
            else if( eCurTok == TYPE )
            {
                Next();
                DefType(); // TODO: Use bPrivate in DefType()
                return;
            }
        }

        // SHARED were ignored
        if( Peek() == SHARED ) Next();

        // PRESERVE only at REDIM
        if( Peek() == PRESERVE )
        {
            Next();
            if( eOp == SbiOpcode::REDIM_ )
                eOp = SbiOpcode::REDIMP_;
            else
                Error( ERRCODE_BASIC_UNEXPECTED, eCurTok );
        }
        SbiSymDef* pDef;
        SbiExprListPtr pDim;

        // #40689, Statics -> Module-Initialising, skip in Sub
        sal_uInt32 nEndOfStaticLbl = 0;
        if( !bVBASupportOn && bStatic )
        {
            nEndOfStaticLbl = aGen.Gen( SbiOpcode::JUMP_, 0 );
            aGen.Statement();   // catch up on static here
        }

        bool bDefined = false;
        while( ( pDef = VarDecl( &pDim, bStatic, bConst ) ) != nullptr )
        {
            /*fprintf(stderr, "Actual sub: \n");
            fprintf(stderr, "Symbol name: %s\n",OUStringToOString(pDef->GetName(),RTL_TEXTENCODING_UTF8).getStr());*/
            EnableErrors();
            // search variable:
            if( bSwitchPool )
                pPool = &aGlobals;
            SbiSymDef* pOld = pPool->Find( pDef->GetName() );
            // search also in the Runtime-Library
            bool bRtlSym = false;
            if( !pOld )
            {
                pOld = CheckRTLForSym( pDef->GetName(), SbxVARIANT );
                if( pOld )
                    bRtlSym = true;
            }
            if( pOld && !(eOp == SbiOpcode::REDIM_ || eOp == SbiOpcode::REDIMP_) )
            {
                if( pDef->GetScope() == SbLOCAL && pOld->GetScope() != SbLOCAL )
                    pOld = nullptr;
            }
            if( pOld )
            {
                bDefined = true;
                // always an error at a RTL-S
                if( !bRtlSym && (eOp == SbiOpcode::REDIM_ || eOp == SbiOpcode::REDIMP_) )
                {
                    // compare the attributes at a REDIM
                    SbxDataType eDefType;
                    bool bError_ = false;
                    if( pOld->IsStatic() )
                    {
                        bError_ = true;
                    }
                    else if( pOld->GetType() != ( eDefType = pDef->GetType() ) )
                    {
                        if( !( eDefType == SbxVARIANT && !pDef->IsDefinedAs() ) )
                            bError_ = true;
                    }
                    if( bError_ )
                        Error( ERRCODE_BASIC_VAR_DEFINED, pDef->GetName() );
                }
                else
                    Error( ERRCODE_BASIC_VAR_DEFINED, pDef->GetName() );
                delete pDef; pDef = pOld;
            }
            else
                pPool->Add( pDef );

            // #36374: Create the variable in front of the distinction IsNew()
            // Otherwise error at Dim Identifier As New Type and option explicit
            if( !bDefined && !(eOp == SbiOpcode::REDIM_ || eOp == SbiOpcode::REDIMP_)
                          && ( !bConst || pDef->GetScope() == SbGLOBAL ) )
            {
                // Declare variable or global constant
                SbiOpcode eOp2;
                switch ( pDef->GetScope() )
                {
                    case SbGLOBAL:  eOp2 = bPersistentGlobal ? SbiOpcode::GLOBAL_P_ : SbiOpcode::GLOBAL_;
                                    goto global;
                    case SbPUBLIC:  eOp2 = bPersistentGlobal ? SbiOpcode::PUBLIC_P_ : SbiOpcode::PUBLIC_;
                                    // #40689, no own Opcode anymore
                                    if( bVBASupportOn && bStatic )
                                    {
                                        eOp2 = SbiOpcode::STATIC_;
                                        break;
                                    }
                    global:         aGen.BackChain( nGblChain );
                                    nGblChain = 0;
                                    bGblDefs = bNewGblDefs = true;
                                    break;
                    default:        eOp2 = SbiOpcode::LOCAL_;
                }
                sal_uInt32 nOpnd2 = sal::static_int_cast< sal_uInt16 >( pDef->GetType() );
                if( pDef->IsWithEvents() )
                    nOpnd2 |= SBX_TYPE_WITH_EVENTS_FLAG;

                if( bCompatible && pDef->IsNew() )
                    nOpnd2 |= SBX_TYPE_DIM_AS_NEW_FLAG;

                short nFixedStringLength = pDef->GetFixedStringLength();
                if( nFixedStringLength >= 0 )
                    nOpnd2 |= (SBX_FIXED_LEN_STRING_FLAG + (sal_uInt32(nFixedStringLength) << 17));     // len = all bits above 0x10000

                if( pDim != nullptr && pDim->GetDims() > 0 )
                    nOpnd2 |= SBX_TYPE_VAR_TO_DIM_FLAG;

                aGen.Gen( eOp2, pDef->GetId(), nOpnd2 );
            }

            // Initialising for self-defined data types
            // and per NEW created variable
            if( pDef->GetType() == SbxOBJECT
             && pDef->GetTypeId() )
            {
                if( !bCompatible && !pDef->IsNew() )
                {
                    OUString aTypeName( aGblStrings.Find( pDef->GetTypeId() ) );
                    if( rTypeArray->Find( aTypeName, SbxClassType::Object ) == nullptr )
                    {
                        if( CodeCompleteOptions::IsExtendedTypeDeclaration() )
                        {
                            if(!IsUnoInterface(aTypeName))
                                Error( ERRCODE_BASIC_UNDEF_TYPE, aTypeName );
                        }
                        else
                            Error( ERRCODE_BASIC_UNDEF_TYPE, aTypeName );
                    }
                }

                if( bConst )
                {
                    Error( ERRCODE_BASIC_SYNTAX );
                }

                if( pDim )
                {
                    if( eOp == SbiOpcode::REDIMP_ )
                    {
                        SbiExpression aExpr( this, *pDef, nullptr );
                        aExpr.Gen();
                        aGen.Gen( SbiOpcode::REDIMP_ERASE_ );

                        pDef->SetDims( pDim->GetDims() );
                        SbiExpression aExpr2( this, *pDef, std::move(pDim) );
                        aExpr2.Gen();
                        aGen.Gen( SbiOpcode::DCREATE_REDIMP_, pDef->GetId(), pDef->GetTypeId() );
                    }
                    else
                    {
                        pDef->SetDims( pDim->GetDims() );
                        SbiExpression aExpr( this, *pDef, std::move(pDim) );
                        aExpr.Gen();
                        aGen.Gen( SbiOpcode::DCREATE_, pDef->GetId(), pDef->GetTypeId() );
                    }
                }
                else
                {
                    SbiExpression aExpr( this, *pDef );
                    aExpr.Gen();
                    SbiOpcode eOp_ = pDef->IsNew() ? SbiOpcode::CREATE_ : SbiOpcode::TCREATE_;
                    aGen.Gen( eOp_, pDef->GetId(), pDef->GetTypeId() );
                    if ( bVBASupportOn )
                        aGen.Gen( SbiOpcode::VBASET_ );
                    else
                        aGen.Gen( SbiOpcode::SET_ );
                }
            }
            else
            {
                if( bConst )
                {
                    // Definition of the constants
                    if( pDim )
                    {
                        Error( ERRCODE_BASIC_SYNTAX );
                    }
                    SbiExpression aVar( this, *pDef );
                    if( !TestToken( EQ ) )
                        goto MyBreak;   // (see below)
                    SbiConstExpression aExpr( this );
                    if( !bDefined && aExpr.IsValid() )
                    {
                        if( pDef->GetScope() == SbGLOBAL )
                        {
                            // Create code only for the global constant!
                            aVar.Gen();
                            aExpr.Gen();
                            aGen.Gen( SbiOpcode::PUTC_ );
                        }
                        SbiConstDef* pConst = pDef->GetConstDef();
                        if( aExpr.GetType() == SbxSTRING )
                            pConst->Set( aExpr.GetString() );
                        else
                            pConst->Set( aExpr.GetValue(), aExpr.GetType() );
                    }
                }
                else if( pDim )
                {
                    // Dimension the variable
                    // Delete the var at REDIM beforehand
                    if( eOp == SbiOpcode::REDIM_ )
                    {
                        SbiExpression aExpr( this, *pDef, nullptr );
                        aExpr.Gen();
                        if ( bVBASupportOn )
                            // delete the array but
                            // clear the variable ( this
                            // allows the processing of
                            // the param to happen as normal without errors ( ordinary ERASE just clears the array )
                            aGen.Gen( SbiOpcode::ERASE_CLEAR_ );
                        else
                            aGen.Gen( SbiOpcode::ERASE_ );
                    }
                    else if( eOp == SbiOpcode::REDIMP_ )
                    {
                        SbiExpression aExpr( this, *pDef, nullptr );
                        aExpr.Gen();
                        aGen.Gen( SbiOpcode::REDIMP_ERASE_ );
                    }
                    pDef->SetDims( pDim->GetDims() );
                    if( bPersistentGlobal )
                        pDef->SetGlobal( true );
                    SbiExpression aExpr( this, *pDef, std::move(pDim) );
                    aExpr.Gen();
                    pDef->SetGlobal( false );
                    aGen.Gen( (eOp == SbiOpcode::STATIC_) ? SbiOpcode::DIM_ : eOp );
                }
            }
            if( !TestComma() )
                goto MyBreak;

            // Implementation of bSwitchPool (see above): pPool must not be set to &aGlobals
            // at the VarDecl-Call.
            // Apart from that the behavior should be absolutely identical,
            // i.e., pPool had to be reset always at the end of the loop.
            // also at a break
            pPool = pOldPool;
            continue;       // Skip MyBreak
        MyBreak:
            pPool = pOldPool;
            break;
        }

        // #40689, finalize the jump over statics declarations
        if( !bVBASupportOn && bStatic )
        {
            // maintain the global chain
            nGblChain = aGen.Gen( SbiOpcode::JUMP_, 0 );
            bGblDefs = bNewGblDefs = true;

            // Register for Sub a jump to the end of statics
            aGen.BackChain( nEndOfStaticLbl );
        }

    }

    // Here were Arrays redimensioned.

    void SbiParser::ReDim()
    {
        DefVar( SbiOpcode::REDIM_, pProc && bVBASupportOn && pProc->IsStatic() );
    }

    // ERASE array, ...

    void SbiParser::Erase()
    {
        while( !bAbort )
        {
            SbiExpression aExpr( this, SbLVALUE );
            aExpr.Gen();
            aGen.Gen( SbiOpcode::ERASE_ );
            if( !TestComma() ) break;
        }
    }

    // Declaration of a data type

    void SbiParser::Type()
    {
        DefType();
    }

    void SbiParser::DefType()
    {
        // Read the new Token lesen. It had to be a symbol
        if (!TestSymbol())
            return;

        if (rTypeArray->Find(aSym,SbxClassType::Object))
        {
            Error( ERRCODE_BASIC_VAR_DEFINED, aSym );
            return;
        }

        SbxObject *pType = new SbxObject(aSym);

        bool bDone = false;

        while( !bDone && !IsEof() )
        {
            std::unique_ptr<SbiSymDef> pElem;
            SbiExprListPtr pDim;
            switch( Peek() )
            {
                case ENDTYPE :
                    bDone = true;
                    Next();
                break;

                case EOLN :
                case REM :
                    Next();
                break;

                default:
                    pElem.reset(VarDecl(&pDim, false, false));
                    if( !pElem )
                        bDone = true;   // Error occurred
            }
            if( pElem )
            {
                SbxArray *pTypeMembers = pType->GetProperties();
                OUString aElemName = pElem->GetName();
                if( pTypeMembers->Find( aElemName, SbxClassType::DontCare) )
                {
                    Error (ERRCODE_BASIC_VAR_DEFINED);
                }
                else
                {
                    SbxDataType eElemType = pElem->GetType();
                    SbxProperty *pTypeElem = new SbxProperty( aElemName, eElemType );
                    if( pDim )
                    {
                        SbxDimArray* pArray = new SbxDimArray( pElem->GetType() );
                        if ( pDim->GetSize() )
                        {
                            // Dimension the target array

                            for ( short i=0; i<pDim->GetSize();++i )
                            {
                                sal_Int32 lb = nBase;
                                SbiExprNode* pNode =  pDim->Get(i)->GetExprNode();
                                sal_Int32 ub = pNode->GetNumber();
                                if ( !pDim->Get( i )->IsBased() ) // each dim is low/up
                                {
                                    if (  ++i >= pDim->GetSize() ) // trouble
                                        StarBASIC::FatalError( ERRCODE_BASIC_INTERNAL_ERROR );
                                    pNode =  pDim->Get(i)->GetExprNode();
                                    lb = ub;
                                    ub = pNode->GetNumber();
                                }
                                else if ( !bCompatible )
                                    ub += nBase;
                                pArray->AddDim32( lb, ub );
                            }
                            pArray->setHasFixedSize( true );
                        }
                        else
                            pArray->unoAddDim( 0, -1 ); // variant array
                        SbxFlagBits nSavFlags = pTypeElem->GetFlags();
                        // need to reset the FIXED flag
                        // when calling PutObject ( because the type will not match Object )
                        pTypeElem->ResetFlag( SbxFlagBits::Fixed );
                        pTypeElem->PutObject( pArray );
                        pTypeElem->SetFlags( nSavFlags );
                    }
                    // Nested user type?
                    if( eElemType == SbxOBJECT )
                    {
                        sal_uInt16 nElemTypeId = pElem->GetTypeId();
                        if( nElemTypeId != 0 )
                        {
                            OUString aTypeName( aGblStrings.Find( nElemTypeId ) );
                            SbxObject* pTypeObj = static_cast< SbxObject* >( rTypeArray->Find( aTypeName, SbxClassType::Object ) );
                            if( pTypeObj != nullptr )
                            {
                                SbxObject* pCloneObj = cloneTypeObjectImpl( *pTypeObj );
                                pTypeElem->PutObject( pCloneObj );
                            }
                        }
                    }
                    pTypeMembers->Insert( pTypeElem, pTypeMembers->Count() );
                }
            }
        }

        pType->Remove( "Name", SbxClassType::DontCare );
        pType->Remove( "Parent", SbxClassType::DontCare );

        rTypeArray->Insert (pType,rTypeArray->Count());
    }

    // Declaration of Enum type

    void SbiParser::Enum()
    {
        DefEnum( false );
    }

    void SbiParser::DefEnum( bool bPrivate )
    {
        // Read a the new Token. It had to be a symbol
        if (!TestSymbol())
            return;

        OUString aEnumName = aSym;
        if( rEnumArray->Find(aEnumName,SbxClassType::Object) )
        {
            Error( ERRCODE_BASIC_VAR_DEFINED, aSym );
            return;
        }

        SbxObject *pEnum = new SbxObject( aEnumName );
        if( bPrivate )
        {
            pEnum->SetFlag( SbxFlagBits::Private );
        }
        SbiSymDef* pElem;
        bool bDone = false;

        // Starting with -1 to make first default value 0 after ++
        sal_Int32 nCurrentEnumValue = -1;
        while( !bDone && !IsEof() )
        {
            switch( Peek() )
            {
                case ENDENUM :
                    pElem = nullptr;
                    bDone = true;
                    Next();
                break;

                case EOLN :
                case REM :
                    pElem = nullptr;
                    Next();
                break;

                default:
                {
                    SbiExprListPtr pDim;
                    pElem = VarDecl( &pDim, false, true );
                    if( !pElem )
                    {
                        bDone = true;   // Error occurred
                        break;
                    }
                    else if( pDim )
                    {
                        Error( ERRCODE_BASIC_SYNTAX );
                        bDone = true;   // Error occurred
                        break;
                    }

                    SbiExpression aVar( this, *pElem );
                    if( Peek() == EQ )
                    {
                        Next();

                        SbiConstExpression aExpr( this );
                        if( aExpr.IsValid() )
                        {
                            SbxVariableRef xConvertVar = new SbxVariable();
                            if( aExpr.GetType() == SbxSTRING )
                                xConvertVar->PutString( aExpr.GetString() );
                            else
                                xConvertVar->PutDouble( aExpr.GetValue() );

                            nCurrentEnumValue = xConvertVar->GetLong();
                        }
                    }
                    else
                        nCurrentEnumValue++;

                    SbiSymPool* pPoolToUse = bPrivate ? pPool : &aGlobals;

                    SbiSymDef* pOld = pPoolToUse->Find( pElem->GetName() );
                    if( pOld )
                    {
                        Error( ERRCODE_BASIC_VAR_DEFINED, pElem->GetName() );
                        bDone = true;   // Error occurred
                        break;
                    }

                    pPool->Add( pElem );

                    if( !bPrivate )
                    {
                        aGen.BackChain( nGblChain );
                        nGblChain = 0;
                        bGblDefs = bNewGblDefs = true;
                        aGen.Gen(
                            SbiOpcode::GLOBAL_, pElem->GetId(),
                            sal::static_int_cast< sal_uInt16 >( pElem->GetType() ) );

                        aVar.Gen();
                        sal_uInt16 nStringId = aGen.GetParser()->aGblStrings.Add( nCurrentEnumValue, SbxLONG );
                        aGen.Gen( SbiOpcode::NUMBER_, nStringId );
                        aGen.Gen( SbiOpcode::PUTC_ );
                    }

                    SbiConstDef* pConst = pElem->GetConstDef();
                    pConst->Set( nCurrentEnumValue, SbxLONG );
                }
            }
            if( pElem )
            {
                SbxArray *pEnumMembers = pEnum->GetProperties();
                SbxProperty *pEnumElem = new SbxProperty( pElem->GetName(), SbxLONG );
                pEnumElem->PutLong( nCurrentEnumValue );
                pEnumElem->ResetFlag( SbxFlagBits::Write );
                pEnumElem->SetFlag( SbxFlagBits::Const );
                pEnumMembers->Insert( pEnumElem, pEnumMembers->Count() );
            }
        }

        pEnum->Remove( "Name", SbxClassType::DontCare );
        pEnum->Remove( "Parent", SbxClassType::DontCare );

        rEnumArray->Insert( pEnum, rEnumArray->Count() );
    }

    // Procedure-Declaration
    // the first Token is already read in (SUB/FUNCTION)
    // xxx Name [LIB "name"[ALIAS "name"]][(Parameter)][AS TYPE]

    SbiProcDef* SbiParser::ProcDecl( bool bDecl )
    {
        bool bFunc = ( eCurTok == FUNCTION );
        bool bProp = ( eCurTok == GET || eCurTok == SET || eCurTok == LET );
        if( !TestSymbol() ) return nullptr;
        OUString aName( aSym );
        SbxDataType eType = eScanType;
        SbiProcDef* pDef = new SbiProcDef( this, aName, true );
        pDef->SetType( eType );
        if( Peek() == CDECL_ )
        {
            Next(); pDef->SetCdecl(true);
        }
        if( Peek() == LIB )
        {
            Next();
            if( Next() == FIXSTRING )
            {
                pDef->GetLib() = aSym;
            }
            else
            {
                Error( ERRCODE_BASIC_SYNTAX );
            }
        }
        if( Peek() == ALIAS )
        {
            Next();
            if( Next() == FIXSTRING )
            {
                pDef->GetAlias() = aSym;
            }
            else
            {
                Error( ERRCODE_BASIC_SYNTAX );
            }
        }
        if( !bDecl )
        {
            // CDECL, LIB and ALIAS are invalid
            if( !pDef->GetLib().isEmpty() )
            {
                Error( ERRCODE_BASIC_UNEXPECTED, LIB );
            }
            if( !pDef->GetAlias().isEmpty() )
            {
                Error( ERRCODE_BASIC_UNEXPECTED, ALIAS );
            }
            if( pDef->IsCdecl() )
            {
                Error( ERRCODE_BASIC_UNEXPECTED, CDECL_ );
            }
            pDef->SetCdecl( false );
            pDef->GetLib().clear();
            pDef->GetAlias().clear();
        }
        else if( pDef->GetLib().isEmpty() )
        {
            // ALIAS and CDECL only together with LIB
            if( !pDef->GetAlias().isEmpty() )
            {
                Error( ERRCODE_BASIC_UNEXPECTED, ALIAS );
            }
            if( pDef->IsCdecl() )
            {
                Error( ERRCODE_BASIC_UNEXPECTED, CDECL_ );
            }
            pDef->SetCdecl( false );
            pDef->GetAlias().clear();
        }
        // Brackets?
        if( Peek() == LPAREN )
        {
            Next();
            if( Peek() == RPAREN )
            {
                Next();
            }
            else
            {
                for(;;)
                {
                    bool bByVal = false;
                    bool bOptional = false;
                    bool bParamArray = false;
                    while( Peek() == BYVAL || Peek() == BYREF || Peek() == OPTIONAL_ )
                    {
                        if( Peek() == BYVAL )
                        {
                            bByVal = true;
                        }
                        else if ( Peek() == BYREF )
                        {
                            bByVal = false;
                        }
                        else if ( Peek() == OPTIONAL_ )
                        {
                            bOptional = true;
                        }
                        Next();
                    }
                    if( bCompatible && Peek() == PARAMARRAY )
                    {
                        if( bByVal || bOptional )
                        {
                            Error( ERRCODE_BASIC_UNEXPECTED, PARAMARRAY );
                        }
                        Next();
                        bParamArray = true;
                    }
                    SbiSymDef* pPar = VarDecl( nullptr, false, false );
                    if( !pPar )
                    {
                        break;
                    }
                    if( bByVal )
                    {
                        pPar->SetByVal(true);
                    }
                    if( bOptional )
                    {
                        pPar->SetOptional();
                    }
                    if( bParamArray )
                    {
                        pPar->SetParamArray();
                    }
                    pDef->GetParams().Add( pPar );
                    SbiToken eTok = Next();
                    if( eTok != COMMA && eTok != RPAREN )
                    {
                        bool bError2 = true;
                        if( bOptional && bCompatible && eTok == EQ )
                        {
                            std::unique_ptr<SbiConstExpression> pDefaultExpr(new SbiConstExpression( this ));
                            SbxDataType eType2 = pDefaultExpr->GetType();

                            sal_uInt16 nStringId;
                            if( eType2 == SbxSTRING )
                            {
                                nStringId = aGblStrings.Add( pDefaultExpr->GetString() );
                            }
                            else
                            {
                                nStringId = aGblStrings.Add( pDefaultExpr->GetValue(), eType2 );
                            }
                            pPar->SetDefaultId( nStringId );
                            pDefaultExpr.reset();

                            eTok = Next();
                            if( eTok == COMMA || eTok == RPAREN )
                            {
                                bError2 = false;
                            }
                        }
                        if( bError2 )
                        {
                            Error( ERRCODE_BASIC_EXPECTED, RPAREN );
                            break;
                        }
                    }
                    if( eTok == RPAREN )
                    {
                        break;
                    }
                }
            }
        }
        TypeDecl( *pDef );
        if( eType != SbxVARIANT && pDef->GetType() != eType )
        {
            Error( ERRCODE_BASIC_BAD_DECLARATION, aName );
        }
        if( pDef->GetType() == SbxVARIANT && !( bFunc || bProp ) )
        {
            pDef->SetType( SbxEMPTY );
        }
        return pDef;
    }

    // DECLARE

    void SbiParser::Declare()
    {
        DefDeclare( false );
    }

    void SbiParser::DefDeclare( bool bPrivate )
    {
        Next();
        if( eCurTok == PTRSAFE )
            Next();

        if( eCurTok != SUB && eCurTok != FUNCTION )
        {
          Error( ERRCODE_BASIC_UNEXPECTED, eCurTok );
        }
        else
        {
            bool bFunction = (eCurTok == FUNCTION);

            SbiProcDef* pDef = ProcDecl( true );
            if( pDef )
            {
                if( pDef->GetLib().isEmpty() )
                {
                    Error( ERRCODE_BASIC_EXPECTED, LIB );
                }
                // Is it already there?
                SbiSymDef* pOld = aPublics.Find( pDef->GetName() );
                if( pOld )
                {
                    SbiProcDef* p = pOld->GetProcDef();
                    if( !p )
                    {
                        // Declared as a variable
                        Error( ERRCODE_BASIC_BAD_DECLARATION, pDef->GetName() );
                        delete pDef;
                        pDef = nullptr;
                    }
                    else
                    {
                        pDef->Match( std::unique_ptr<SbiProcDef>(p) );
                    }
                }
                else
                {
                    aPublics.Add( pDef );
                }
                if ( pDef )
                {
                    pDef->SetPublic( !bPrivate );

                    // New declare handling
                    if( !pDef->GetLib().isEmpty())
                    {
                        if( bNewGblDefs && nGblChain == 0 )
                        {
                            nGblChain = aGen.Gen( SbiOpcode::JUMP_, 0 );
                            bNewGblDefs = false;
                        }

                        sal_uInt16 nSavLine = nLine;
                        aGen.Statement();
                        pDef->Define();
                        pDef->SetLine1( nSavLine );
                        pDef->SetLine2( nSavLine );

                        SbiSymPool& rPool = pDef->GetParams();
                        sal_uInt16 nParCount = rPool.GetSize();

                        SbxDataType eType = pDef->GetType();
                        if( bFunction )
                        {
                            aGen.Gen( SbiOpcode::PARAM_, 0, sal::static_int_cast< sal_uInt16 >( eType ) );
                        }
                        if( nParCount > 1 )
                        {
                            aGen.Gen( SbiOpcode::ARGC_ );

                            for( sal_uInt16 i = 1 ; i < nParCount ; ++i )
                            {
                                SbiSymDef* pParDef = rPool.Get( i );
                                SbxDataType eParType = pParDef->GetType();

                                aGen.Gen( SbiOpcode::PARAM_, i, sal::static_int_cast< sal_uInt16 >( eParType ) );
                                aGen.Gen( SbiOpcode::ARGV_ );

                                sal_uInt16 nTyp = sal::static_int_cast< sal_uInt16 >( pParDef->GetType() );
                                if( pParDef->IsByVal() )
                                {
                                    // Reset to avoid additional byval in call to wrapper function
                                    pParDef->SetByVal( false );
                                    nTyp |= 0x8000;
                                }
                                aGen.Gen( SbiOpcode::ARGTYP_, nTyp );
                            }
                        }

                        aGen.Gen( SbiOpcode::LIB_, aGblStrings.Add( pDef->GetLib() ) );

                        SbiOpcode eOp = pDef->IsCdecl() ? SbiOpcode::CALLC_ : SbiOpcode::CALL_;
                        sal_uInt16 nId = pDef->GetId();
                        if( !pDef->GetAlias().isEmpty() )
                        {
                            nId = ( nId & 0x8000 ) | aGblStrings.Add( pDef->GetAlias() );
                        }
                        if( nParCount > 1 )
                        {
                            nId |= 0x8000;
                        }
                        aGen.Gen( eOp, nId, sal::static_int_cast< sal_uInt16 >( eType ) );

                        if( bFunction )
                        {
                            aGen.Gen( SbiOpcode::PUT_ );
                        }
                        aGen.Gen( SbiOpcode::LEAVE_ );
                    }
                }
            }
        }
    }

    void SbiParser::Attribute()
    {
        // TODO: Need to implement the method as an attributed object.
        while( Next() != EQ )
        {
            if( Next() != DOT)
            {
                break;
            }
        }

        if( eCurTok != EQ )
        {
            Error( ERRCODE_BASIC_SYNTAX );
        }
        else
        {
            SbiExpression aValue( this );
        }
        // Don't generate any code - just discard it.
    }

    // Call of a SUB or a FUNCTION

    void SbiParser::Call()
    {
        SbiExpression aVar( this, SbSYMBOL );
        aVar.Gen( FORCE_CALL );
        aGen.Gen( SbiOpcode::GET_ );
    }

    // SUB/FUNCTION

    void SbiParser::SubFunc()
    {
        DefProc( false, false );
    }

    // Read in of a procedure

    void SbiParser::DefProc( bool bStatic, bool bPrivate )
    {
        sal_uInt16 l1 = nLine;
        bool bSub = ( eCurTok == SUB );
        bool bProperty = ( eCurTok == PROPERTY );
        PropertyMode ePropertyMode = PropertyMode::NONE;
        if( bProperty )
        {
            Next();
            if( eCurTok == GET )
            {
                ePropertyMode = PropertyMode::Get;
            }
            else if( eCurTok == LET )
            {
                ePropertyMode = PropertyMode::Let;
            }
            else if( eCurTok == SET )
            {
                ePropertyMode = PropertyMode::Set;
            }
            else
            {
                Error( ERRCODE_BASIC_EXPECTED, "Get or Let or Set" );
            }
        }

        SbiToken eExit = eCurTok;
        SbiProcDef* pDef = ProcDecl( false );
        if( !pDef )
        {
            return;
        }
        pDef->setPropertyMode( ePropertyMode );

        // Is the Proc already declared?
        SbiSymDef* pOld = aPublics.Find( pDef->GetName() );
        if( pOld )
        {
            pProc = pOld->GetProcDef();
            if( !pProc )
            {
                // Declared as a variable
                Error( ERRCODE_BASIC_BAD_DECLARATION, pDef->GetName() );
                delete pDef;
                return;
            }
            // #100027: Multiple declaration -> Error
            // #112787: Not for setup, REMOVE for 8
            else if( pProc->IsUsedForProcDecl() )
            {
                PropertyMode ePropMode = pDef->getPropertyMode();
                if( ePropMode == PropertyMode::NONE || ePropMode == pProc->getPropertyMode() )
                {
                    Error( ERRCODE_BASIC_PROC_DEFINED, pDef->GetName() );
                    delete pDef;
                    return;
                }
            }

            pDef->Match( std::unique_ptr<SbiProcDef>(pProc) );
            pProc = pDef;
        }
        else
        {
            aPublics.Add( pDef );
            pProc = pDef;
        }
        if( !pProc )
        {
            return;
        }
        pProc->SetPublic( !bPrivate );

        // Now we set the search hierarchy for symbols as well as the
        // current procedure.
        aPublics.SetProcId( pProc->GetId() );
        pProc->GetParams().SetParent( &aPublics );
        if( bStatic )
        {
            if ( bVBASupportOn )
            {
                pProc->SetStatic();
            }
            else
            {
                Error( ERRCODE_BASIC_NOT_IMPLEMENTED ); // STATIC SUB ...
            }
        }
        else
        {
            pProc->SetStatic( false );
        }
        // Normal case: Local variable->parameter->global variable
        pProc->GetLocals().SetParent( &pProc->GetParams() );
        pPool = &pProc->GetLocals();

        pProc->Define();
        OpenBlock( eExit );
        StmntBlock( bSub ? ENDSUB : (bProperty ? ENDPROPERTY : ENDFUNC) );
        sal_uInt16 l2 = nLine;
        pProc->SetLine1( l1 );
        pProc->SetLine2( l2 );
        pPool = &aPublics;
        aPublics.SetProcId( 0 );
        // Open labels?
        pProc->GetLabels().CheckRefs();
        CloseBlock();
        aGen.Gen( SbiOpcode::LEAVE_ );
        pProc = nullptr;
    }

    // STATIC variable|procedure

    void SbiParser::Static()
    {
        DefStatic( false );
    }

    void SbiParser::DefStatic( bool bPrivate )
    {
        SbiSymPool* p;

        switch( Peek() )
        {
        case SUB:
        case FUNCTION:
        case PROPERTY:
            // End global chain if necessary (not done in
            // SbiParser::Parse() under these conditions
            if( bNewGblDefs && nGblChain == 0 )
            {
                nGblChain = aGen.Gen( SbiOpcode::JUMP_, 0 );
                bNewGblDefs = false;
            }
            Next();
            DefProc( true, bPrivate );
            break;
        default:
            if( !pProc )
            {
                Error( ERRCODE_BASIC_NOT_IN_SUBR );
            }
            // Reset the Pool, so that STATIC-Declarations go into the
            // global Pool
            p = pPool;
            pPool = &aPublics;
            DefVar( SbiOpcode::STATIC_, true );
            pPool = p;
            break;
        }
    }

    bool SbiParser::IsUnoInterface(const OUString& sTypeName)
    {
        try
        {
            return css::reflection::theCoreReflection::get(
                comphelper::getProcessComponentContext())->forName(sTypeName).is();
        }
        catch(const Exception&)
        {
            OSL_FAIL("Could not create reflection.CoreReflection.");
        }
        return false;
    }

    /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

↑ V668 There is no sense in testing the 'pProc' pointer against null, as the memory was allocated using the 'new' operator. The exception will be generated in the case of memory allocation error.