/**************************************************************
 * 
 * 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
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_basic.hxx"
#include <basic/sbx.hxx>
#include "sbcomp.hxx"

SbxObject* cloneTypeObjectImpl( const SbxObject& rTypeObj );

// Deklaration einer Variablen
// Bei Fehlern wird bis zum Komma oder Newline geparst.
// Returnwert: eine neue Instanz, die eingefuegt und dann geloescht wird.
// Array-Indexe werden als SbiDimList zurueckgegeben

SbiSymDef* SbiParser::VarDecl( SbiDimList** ppDim, sal_Bool bStatic, sal_Bool bConst )
{
	bool bWithEvents = false;
	if( Peek() == WITHEVENTS )
	{
		Next();
		bWithEvents = true;
	}
	if( !TestSymbol() ) return NULL;
	SbxDataType t = eScanType;
	SbiSymDef* pDef = bConst ? new SbiConstDef( aSym ) : new SbiSymDef( aSym );
	SbiDimList* pDim = NULL;
	// Klammern?
	if( Peek() == LPAREN )
	{
		pDim = new SbiDimList( 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( SbERR_EXPECTED, "()" );
		delete pDim;
	}
	else if( ppDim )
		*ppDim = pDim;
	return pDef;
}

// Aufloesen einer AS-Typdeklaration
// Der Datentyp wird in die uebergebene Variable eingetragen

void SbiParser::TypeDecl( SbiSymDef& rDef, sal_Bool bAsNewAlreadyParsed )
{
	SbxDataType eType = rDef.GetType();
	short nSize = 0;
	if( bAsNewAlreadyParsed || Peek() == AS )
	{
		if( !bAsNewAlreadyParsed )
			Next();
		rDef.SetDefinedAs();
		String aType;
		SbiToken eTok = Next();
		if( !bAsNewAlreadyParsed && eTok == NEW )
		{
			rDef.SetNew();
			eTok = Next();
		}
		switch( eTok )
		{
			case ANY:
				if( rDef.IsNew() )
					Error( SbERR_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( SbERR_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( SbERR_OUT_OF_RANGE );
						else
							rDef.SetFixedStringLength( nSize );
					}
				}
				break;
			case SYMBOL: // kann nur ein TYPE oder eine Objektklasse sein!
				if( eScanType != SbxVARIANT )
					Error( SbERR_SYNTAX );
				else
				{
					String aCompleteName = aSym;

					// #52709 DIM AS NEW fuer Uno mit voll-qualifizierten Namen
					if( Peek() == DOT )
					{
						String aDotStr( '.' );
						while( Peek() == DOT )
						{
							aCompleteName += aDotStr;
							Next();
                            SbiToken ePeekTok = Peek();
							if( ePeekTok == SYMBOL || IsKwd( ePeekTok ) )
							{
								Next();
								aCompleteName += aSym;
							}
							else
							{
								Next();
								Error( SbERR_UNEXPECTED, SYMBOL );
								break;
							}
						}
					}
					else if( rEnumArray->Find( aCompleteName, SbxCLASS_OBJECT ) )
					{
						eType = SbxLONG;
						break;
					}

					// In den String-Pool uebernehmen
					rDef.SetTypeId( aGblStrings.Add( aCompleteName ) );

					if( rDef.IsNew() && pProc == NULL )
						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( SbERR_UNEXPECTED, eTok );
				Next();
		}
		// Die Variable koennte mit Suffix deklariert sein
		if( rDef.GetType() != SbxVARIANT )
		{
			if( rDef.GetType() != eType )
				Error( SbERR_VAR_DEFINED, rDef.GetName() );
			else if( eType == SbxSTRING && rDef.GetLen() != nSize )
				Error( SbERR_VAR_DEFINED, rDef.GetName() );
		}
		rDef.SetType( eType );
		rDef.SetLen( nSize );
	}
}

// Hier werden Variable, Arrays und Strukturen definiert.
// DIM/PRIVATE/PUBLIC/GLOBAL

void SbiParser::Dim()
{
	DefVar( _DIM, ( pProc && bVBASupportOn ) ? pProc->IsStatic() : sal_False );
}

void SbiParser::DefVar( SbiOpcode eOp, sal_Bool bStatic )
{
	SbiSymPool* pOldPool = pPool;
	sal_Bool bSwitchPool = sal_False;
	sal_Bool bPersistantGlobal = sal_False;
	SbiToken eFirstTok = eCurTok;
	if( pProc && ( eCurTok == GLOBAL || eCurTok == PUBLIC || eCurTok == PRIVATE ) )
		Error( SbERR_NOT_IN_SUBR, eCurTok );
	if( eCurTok == PUBLIC || eCurTok == GLOBAL )
    {
		bSwitchPool = sal_True;		// im richtigen Moment auf globalen Pool schalten
    	if( eCurTok == GLOBAL )
            bPersistantGlobal = sal_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 ) 
		bPersistantGlobal = sal_True;
	// PRIVATE ist Synonym fuer DIM
	// _CONST_?
	sal_Bool bConst = sal_False;
	if( eCurTok == _CONST_ )
		bConst = sal_True;
	else if( Peek() == _CONST_ )
		Next(), bConst = sal_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( _JUMP, 0 );
				bNewGblDefs = sal_False;
			}
			Next();
			DefProc( sal_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( bPrivate );
			return;
		}
	}

#ifdef SHARED
#define tmpSHARED
#undef SHARED
#endif
	// SHARED wird ignoriert
	if( Peek() == SHARED ) Next();
#ifdef tmpSHARED
#define SHARED
#undef tmpSHARED
#endif
	// PRESERVE nur bei REDIM
	if( Peek() == PRESERVE )
	{
		Next();
		if( eOp == _REDIM )
			eOp = _REDIMP;
		else
			Error( SbERR_UNEXPECTED, eCurTok );
	}
	SbiSymDef* pDef;
	SbiDimList* pDim;

	// AB 9.7.97, #40689, Statics -> Modul-Initialisierung, in Sub ueberspringen
	sal_uInt32 nEndOfStaticLbl = 0;
	if( !bVBASupportOn && bStatic )
	{
		nEndOfStaticLbl = aGen.Gen( _JUMP, 0 );
		aGen.Statement();	// bei static hier nachholen
	}

	sal_Bool bDefined = sal_False;
	while( ( pDef = VarDecl( &pDim, bStatic, bConst ) ) != NULL )
	{
		EnableErrors();
		// Variable suchen:
		if( bSwitchPool )
			pPool = &aGlobals;
		SbiSymDef* pOld = pPool->Find( pDef->GetName() );
		// AB 31.3.1996, #25651#, auch in Runtime-Library suchen
		sal_Bool bRtlSym = sal_False;
		if( !pOld )
		{
			pOld = CheckRTLForSym( pDef->GetName(), SbxVARIANT );
			if( pOld )
				bRtlSym = sal_True;
		}
		if( pOld && !(eOp == _REDIM || eOp == _REDIMP) )
		{
			if( pDef->GetScope() == SbLOCAL && pOld->GetScope() != SbLOCAL )
				pOld = NULL;
		}
		if( pOld )
		{
			bDefined = sal_True;
			// Bei RTL-Symbol immer Fehler
			if( !bRtlSym && (eOp == _REDIM || eOp == _REDIMP) )
			{
				// Bei REDIM die Attribute vergleichen
				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( SbERR_VAR_DEFINED, pDef->GetName() );
			}
			else
				Error( SbERR_VAR_DEFINED, pDef->GetName() );
			delete pDef; pDef = pOld;
		}
		else
			pPool->Add( pDef );

		// #36374: Variable vor Unterscheidung IsNew() anlegen
		// Sonst Error bei Dim Identifier As New Type und option explicit
		if( !bDefined && !(eOp == _REDIM || eOp == _REDIMP)
                      && ( !bConst || pDef->GetScope() == SbGLOBAL ) )
		{
			// Variable oder globale Konstante deklarieren
			SbiOpcode eOp2;
			switch ( pDef->GetScope() )
			{
				case SbGLOBAL:	eOp2 = bPersistantGlobal ? _GLOBAL_P : _GLOBAL; 
                                goto global;
				case SbPUBLIC:	eOp2 = bPersistantGlobal ? _PUBLIC_P : _PUBLIC;
								// AB 9.7.97, #40689, kein eigener Opcode mehr
								if( bVBASupportOn && bStatic )
								{
									eOp2 = _STATIC;
									break;
								}
				global:			aGen.BackChain( nGblChain );
								nGblChain = 0;
								bGblDefs = bNewGblDefs = sal_True;
								break;
				default:		eOp2 = _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 != NULL && pDim->GetDims() > 0 )
				nOpnd2 |= SBX_TYPE_VAR_TO_DIM_FLAG;

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

		// Initialisierung fuer selbstdefinierte Datentypen
		// und per NEW angelegte Variable
		if( pDef->GetType() == SbxOBJECT
		 && pDef->GetTypeId() )
		{
			if( !bCompatible && !pDef->IsNew() )
			{
				String aTypeName( aGblStrings.Find( pDef->GetTypeId() ) );
				if( rTypeArray->Find( aTypeName, SbxCLASS_OBJECT ) == NULL )
					Error( SbERR_UNDEF_TYPE, aTypeName );
			}

			if( bConst )
			{
				Error( SbERR_SYNTAX );
			}

			if( pDim )
			{
                if( eOp == _REDIMP )
                {
					SbiExpression aExpr( this, *pDef, NULL );
					aExpr.Gen();
					aGen.Gen( _REDIMP_ERASE );

				    pDef->SetDims( pDim->GetDims() );
				    SbiExpression aExpr2( this, *pDef, pDim );
				    aExpr2.Gen();
    				aGen.Gen( _DCREATE_REDIMP, pDef->GetId(), pDef->GetTypeId() );
                }
                else
                {
				    pDef->SetDims( pDim->GetDims() );
				    SbiExpression aExpr( this, *pDef, pDim );
				    aExpr.Gen();
	    			aGen.Gen( _DCREATE, pDef->GetId(), pDef->GetTypeId() );
                }
			}
			else
			{
				SbiExpression aExpr( this, *pDef );
				aExpr.Gen();
				SbiOpcode eOp_ = pDef->IsNew() ? _CREATE : _TCREATE;
				aGen.Gen( eOp_, pDef->GetId(), pDef->GetTypeId() );
				aGen.Gen( _SET );
			}
		}
		else
		{
			if( bConst )
			{
				// Konstanten-Definition
				if( pDim )
				{
					Error( SbERR_SYNTAX );
					delete pDim;
				}
				SbiExpression aVar( this, *pDef );
				if( !TestToken( EQ ) )
					goto MyBreak;	// AB 24.6.1996 (s.u.)
				SbiConstExpression aExpr( this );
				if( !bDefined && aExpr.IsValid() )
				{
					if( pDef->GetScope() == SbGLOBAL )
					{
						// Nur Code fuer globale Konstante erzeugen!
						aVar.Gen();
						aExpr.Gen();
						aGen.Gen( _PUTC );
					}
					SbiConstDef* pConst = pDef->GetConstDef();
					if( aExpr.GetType() == SbxSTRING )
						pConst->Set( aExpr.GetString() );
					else
						pConst->Set( aExpr.GetValue(), aExpr.GetType() );
				}
			}
			else if( pDim )
			{
				// Die Variable dimensionieren
				// Bei REDIM die Var vorher loeschen
				if( eOp == _REDIM )
				{
					SbiExpression aExpr( this, *pDef, NULL );
					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( _ERASE_CLEAR );
					else
						aGen.Gen( _ERASE );
				}
				else if( eOp == _REDIMP )
				{
					SbiExpression aExpr( this, *pDef, NULL );
					aExpr.Gen();
					aGen.Gen( _REDIMP_ERASE );
				}
				pDef->SetDims( pDim->GetDims() );
                if( bPersistantGlobal )
                    pDef->SetGlobal( sal_True );
				SbiExpression aExpr( this, *pDef, pDim );
				aExpr.Gen();
                pDef->SetGlobal( sal_False );
				aGen.Gen( (eOp == _STATIC) ? _DIM : eOp );
			}
		}
		if( !TestComma() )
			goto MyBreak;	// AB 24.6.1996 (s.u.)

		// #27963# AB, 24.6.1996
		// Einfuehrung bSwitchPool (s.o.): pPool darf beim VarDecl-Aufruf
		// noch nicht auf &aGlobals gesetzt sein.
		// Ansonsten soll das Verhalten aber absolut identisch bleiben,
		// d.h. pPool muss immer am Schleifen-Ende zurueckgesetzt werden.
		// auch bei break
		pPool = pOldPool;
		continue;		// MyBreak überspingen
	MyBreak:
		pPool = pOldPool;
		break;
	}

	// AB 9.7.97, #40689, Sprung ueber Statics-Deklaration abschliessen
	if( !bVBASupportOn && bStatic )
	{
		// globalen Chain pflegen
		nGblChain = aGen.Gen( _JUMP, 0 );
		bGblDefs = bNewGblDefs = sal_True;

		// fuer Sub Sprung auf Ende der statics eintragen
		aGen.BackChain( nEndOfStaticLbl );
	}

	//pPool = pOldPool;
}

// Hier werden Arrays redimensioniert.

void SbiParser::ReDim()
{
	DefVar( _REDIM, (  pProc && bVBASupportOn ) ? pProc->IsStatic() : sal_False );
}

// ERASE array, ...

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

// Deklaration eines Datentyps

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

void SbiParser::DefType( sal_Bool bPrivate )
{
	// TODO: Use bPrivate
    (void)bPrivate;

	// Neues Token lesen, es muss ein Symbol sein
	if (!TestSymbol())
		return;

	if (rTypeArray->Find(aSym,SbxCLASS_OBJECT))
	{
		Error( SbERR_VAR_DEFINED, aSym );
		return;
	}

	SbxObject *pType = new SbxObject(aSym);

	SbiSymDef* pElem;
	SbiDimList* pDim = NULL;
	sal_Bool bDone = sal_False;

	while( !bDone && !IsEof() )
	{
		switch( Peek() )
		{
			case ENDTYPE :
				pElem = NULL;
				bDone = sal_True;
				Next();
			break;

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

			default:
				pDim = NULL;
				pElem = VarDecl(&pDim,sal_False,sal_False);
				if( !pElem )
					bDone = sal_True;	// Error occured
		}
		if( pElem )
		{
			SbxArray *pTypeMembers = pType->GetProperties();
			String aElemName = pElem->GetName();
			if( pTypeMembers->Find( aElemName, SbxCLASS_DONTCARE) )
				Error (SbERR_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 ub = -1;
							sal_Int32 lb = nBase;
							SbiExprNode* pNode =  pDim->Get(i)->GetExprNode();
							ub = pNode->GetNumber();
							if ( !pDim->Get( i )->IsBased() ) // each dim is low/up
							{
								if (  ++i >= pDim->GetSize() ) // trouble
									StarBASIC::FatalError( SbERR_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
					sal_uInt16 nSavFlags = pTypeElem->GetFlags();
					// need to reset the FIXED flag 
					// when calling PutObject ( because the type will not match Object ) 	
					pTypeElem->ResetFlag( SBX_FIXED );
					pTypeElem->PutObject( pArray );
					pTypeElem->SetFlags( nSavFlags );
				}
				// Nested user type?
				if( eElemType == SbxOBJECT )
				{
					sal_uInt16 nElemTypeId = pElem->GetTypeId();
					if( nElemTypeId != 0 )
					{
						String aTypeName( aGblStrings.Find( nElemTypeId ) );
						SbxObject* pTypeObj = static_cast< SbxObject* >( rTypeArray->Find( aTypeName, SbxCLASS_OBJECT ) );
						if( pTypeObj != NULL )
						{
							SbxObject* pCloneObj = cloneTypeObjectImpl( *pTypeObj );
							pTypeElem->PutObject( pCloneObj );
						}
					}
				}
				delete pDim;
				pTypeMembers->Insert( pTypeElem, pTypeMembers->Count() );
			}
			delete pElem;
		}
	}

	pType->Remove( XubString( RTL_CONSTASCII_USTRINGPARAM("Name") ), SbxCLASS_DONTCARE );
	pType->Remove( XubString( RTL_CONSTASCII_USTRINGPARAM("Parent") ), SbxCLASS_DONTCARE );

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


// Declaration of Enum type

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

void SbiParser::DefEnum( sal_Bool bPrivate )
{
	// Neues Token lesen, es muss ein Symbol sein
	if (!TestSymbol())
		return;

	String aEnumName = aSym;
	if( rEnumArray->Find(aEnumName,SbxCLASS_OBJECT) )
	{
		Error( SbERR_VAR_DEFINED, aSym );
		return;
	}

	SbxObject *pEnum = new SbxObject( aEnumName );
	if( bPrivate )
		pEnum->SetFlag( SBX_PRIVATE );

	SbiSymDef* pElem;
	SbiDimList* pDim;
	sal_Bool bDone = sal_False;

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

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

			default:
			{
				// TODO: Check existing!
				sal_Bool bDefined = sal_False;

				pDim = NULL;
				pElem = VarDecl( &pDim, sal_False, sal_True );
				if( !pElem )
				{
					bDone = sal_True;	// Error occured
					break;
				}
				else if( pDim )
				{
					delete pDim;
					Error( SbERR_SYNTAX );
					bDone = sal_True;	// Error occured
					break;
				}

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

					SbiConstExpression aExpr( this );
					if( !bDefined && 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( SbERR_VAR_DEFINED, pElem->GetName() );
					bDone = sal_True;	// Error occured
					break;
				}

				pPool->Add( pElem );

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

					aVar.Gen();
					sal_uInt16 nStringId = aGen.GetParser()->aGblStrings.Add( nCurrentEnumValue, SbxLONG );
					aGen.Gen( _NUMBER, nStringId );
					aGen.Gen( _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( SBX_WRITE );
			pEnumElem->SetFlag( SBX_CONST );
			pEnumMembers->Insert( pEnumElem, pEnumMembers->Count() );
		}
	}

	pEnum->Remove( XubString( RTL_CONSTASCII_USTRINGPARAM("Name") ), SbxCLASS_DONTCARE );
	pEnum->Remove( XubString( RTL_CONSTASCII_USTRINGPARAM("Parent") ), SbxCLASS_DONTCARE );

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


// Prozedur-Deklaration
// das erste Token ist bereits eingelesen (SUB/FUNCTION)
// xxx Name [LIB "name"[ALIAS "name"]][(Parameter)][AS TYPE]

SbiProcDef* SbiParser::ProcDecl( sal_Bool bDecl )
{
	sal_Bool bFunc = sal_Bool( eCurTok == FUNCTION );
	sal_Bool bProp = sal_Bool( eCurTok == GET || eCurTok == SET || eCurTok == LET );
	if( !TestSymbol() ) return NULL;
	String aName( aSym );
	SbxDataType eType = eScanType;
	SbiProcDef* pDef = new SbiProcDef( this, aName, true );
	pDef->SetType( eType );
	if( Peek() == _CDECL_ )
	{
		Next(); pDef->SetCdecl();
	}
	if( Peek() == LIB )
	{
		Next();
		if( Next() == FIXSTRING )
			pDef->GetLib() = aSym;
		else
			Error( SbERR_SYNTAX );
	}
	if( Peek() == ALIAS )
	{
		Next();
		if( Next() == FIXSTRING )
			pDef->GetAlias() = aSym;
		else
			Error( SbERR_SYNTAX );
	}
	if( !bDecl )
	{
		// CDECL, LIB und ALIAS sind unzulaessig
		if( pDef->GetLib().Len() )
			Error( SbERR_UNEXPECTED, LIB );
		if( pDef->GetAlias().Len() )
			Error( SbERR_UNEXPECTED, ALIAS );
		if( pDef->IsCdecl() )
			Error( SbERR_UNEXPECTED, _CDECL_ );
		pDef->SetCdecl( sal_False );
		pDef->GetLib().Erase();
		pDef->GetAlias().Erase();
	}
	else if( !pDef->GetLib().Len() )
	{
		// ALIAS und CDECL nur zusammen mit LIB
		if( pDef->GetAlias().Len() )
			Error( SbERR_UNEXPECTED, ALIAS );
		if( pDef->IsCdecl() )
			Error( SbERR_UNEXPECTED, _CDECL_ );
		pDef->SetCdecl( sal_False );
		pDef->GetAlias().Erase();
	}
	// Klammern?
	if( Peek() == LPAREN )
	{
		Next();
		if( Peek() == RPAREN )
			Next();
		else
		  for(;;) {
			sal_Bool bByVal = sal_False;
			sal_Bool bOptional = sal_False;
			sal_Bool bParamArray = sal_False;
			while( Peek() == BYVAL || Peek() == BYREF || Peek() == _OPTIONAL_ )
			{
				if		( Peek() == BYVAL )		Next(), bByVal = sal_True;
				else if	( Peek() == BYREF )		Next(), bByVal = sal_False;
				else if	( Peek() == _OPTIONAL_ )	Next(), bOptional = sal_True;
			}
			if( bCompatible && Peek() == PARAMARRAY )
			{
				if( bByVal || bOptional )
					Error( SbERR_UNEXPECTED, PARAMARRAY );
				Next();
				bParamArray = sal_True;
			}
			SbiSymDef* pPar = VarDecl( NULL, sal_False, sal_False );
			if( !pPar )
				break;
			if( bByVal )
				pPar->SetByVal();
			if( bOptional )
				pPar->SetOptional();
			if( bParamArray )
				pPar->SetParamArray();
			pDef->GetParams().Add( pPar );
			SbiToken eTok = Next();
			if( eTok != COMMA && eTok != RPAREN )
			{
				sal_Bool bError2 = sal_True;
				if( bOptional && bCompatible && eTok == EQ )
				{
					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 );
					delete pDefaultExpr;

					eTok = Next();
					if( eTok == COMMA || eTok == RPAREN )
						bError2 = sal_False;
				}
				if( bError2 )
				{
					Error( SbERR_EXPECTED, RPAREN );
					break;
				}
			}
			if( eTok == RPAREN )
				break;
		}
	}
	TypeDecl( *pDef );
	if( eType != SbxVARIANT && pDef->GetType() != eType )
		Error( SbERR_BAD_DECLARATION, aName );
//	if( pDef->GetType() == SbxOBJECT )
//		pDef->SetType( SbxVARIANT ),
//		Error( SbERR_SYNTAX );
	if( pDef->GetType() == SbxVARIANT && !( bFunc || bProp ) )
		pDef->SetType( SbxEMPTY );
	return pDef;
}

// DECLARE

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

void SbiParser::DefDeclare( sal_Bool bPrivate )
{
	Next();
	if( eCurTok != SUB && eCurTok != FUNCTION )
	  Error( SbERR_UNEXPECTED, eCurTok );
	else
	{
		bool bFunction = (eCurTok == FUNCTION);

		SbiProcDef* pDef = ProcDecl( sal_True );
		if( pDef )
		{
			if( !pDef->GetLib().Len() )
				Error( SbERR_EXPECTED, LIB );
			// gibts den schon?
			SbiSymDef* pOld = aPublics.Find( pDef->GetName() );
			if( pOld )
			{
				SbiProcDef* p = pOld->GetProcDef();
				if( !p )
				{
					// Als Variable deklariert
					Error( SbERR_BAD_DECLARATION, pDef->GetName() );
					delete pDef;
					pDef = NULL;
				}
				else
					pDef->Match( p );
			}
			else
				aPublics.Add( pDef );

			if ( pDef )
			{
				pDef->SetPublic( !bPrivate );

				// New declare handling
				if( pDef->GetLib().Len() > 0 )
				{
					if( bNewGblDefs && nGblChain == 0 )
					{
						nGblChain = aGen.Gen( _JUMP, 0 );
						bNewGblDefs = sal_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( _PARAM, 0, sal::static_int_cast< sal_uInt16 >( eType ) );

					if( nParCount > 1 )
					{
						aGen.Gen( _ARGC );

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

							aGen.Gen( _PARAM, i, sal::static_int_cast< sal_uInt16 >( eParType ) );
							aGen.Gen( _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( sal_False );
								nTyp |= 0x8000;
							}
							aGen.Gen( _ARGTYP, nTyp );
						}
					}

					aGen.Gen( _LIB, aGblStrings.Add( pDef->GetLib() ) );

					SbiOpcode eOp = pDef->IsCdecl() ? _CALLC : _CALL;
					sal_uInt16 nId = pDef->GetId();
					if( pDef->GetAlias().Len() )
						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( _PUT );

					aGen.Gen( _LEAVE );
				}
			}
		}
	}
}

// Aufruf einer SUB oder FUNCTION

void SbiParser::Call()
{
	String aName( aSym );
	SbiExpression aVar( this, SbSYMBOL );
	aVar.Gen( FORCE_CALL );
	aGen.Gen( _GET );
}

// SUB/FUNCTION

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

// Einlesen einer Prozedur

sal_Bool runsInSetup( void );

void SbiParser::DefProc( sal_Bool bStatic, sal_Bool bPrivate )
{
	sal_uInt16 l1 = nLine, l2 = nLine;
	sal_Bool bSub = sal_Bool( eCurTok == SUB );
	sal_Bool bProperty = sal_Bool( eCurTok == PROPERTY );
	PropertyMode ePropertyMode = PROPERTY_MODE_NONE;
	if( bProperty )
	{
		Next();
		if( eCurTok == GET )
			ePropertyMode = PROPERTY_MODE_GET;
		else if( eCurTok == LET )
			ePropertyMode = PROPERTY_MODE_LET;
		else if( eCurTok == SET )
			ePropertyMode = PROPERTY_MODE_SET;
		else
			Error( SbERR_EXPECTED, "Get or Let or Set" );
	}

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

	// Ist die Proc bereits deklariert?
	SbiSymDef* pOld = aPublics.Find( pDef->GetName() );
	if( pOld )
	{
		bool bError_ = false;

		pProc = pOld->GetProcDef();
		if( !pProc )
		{
			// Als Variable deklariert
			Error( SbERR_BAD_DECLARATION, pDef->GetName() );
			delete pDef;
			pProc = NULL;
			bError_ = true;
		}
		// #100027: Multiple declaration -> Error
		// #112787: Not for setup, REMOVE for 8
		else if( !runsInSetup() && pProc->IsUsedForProcDecl() )
		{
			PropertyMode ePropMode = pDef->getPropertyMode();
			if( ePropMode == PROPERTY_MODE_NONE || ePropMode == pProc->getPropertyMode() )
			{
				Error( SbERR_PROC_DEFINED, pDef->GetName() );
				delete pDef;
				pProc = NULL;
				bError_ = true;
			}
		}

		if( !bError_ )
		{
			pDef->Match( pProc );
			pProc = pDef;
		}
	}
	else
		aPublics.Add( pDef ), pProc = pDef;

	if( !pProc )
		return;
	pProc->SetPublic( !bPrivate );

	// Nun setzen wir die Suchhierarchie fuer Symbole sowie die aktuelle
	// Prozedur.
	aPublics.SetProcId( pProc->GetId() );
	pProc->GetParams().SetParent( &aPublics );
	if( bStatic )
        {
		if ( bVBASupportOn )
			pProc->SetStatic( sal_True );
		else
			Error( SbERR_NOT_IMPLEMENTED ); // STATIC SUB ...
        }
 	else
	{
		pProc->SetStatic( sal_False );
        }
	// Normalfall: Lokale Variable->Parameter->Globale Variable
	pProc->GetLocals().SetParent( &pProc->GetParams() );
	pPool = &pProc->GetLocals();

	pProc->Define();
	OpenBlock( eExit );
	StmntBlock( bSub ? ENDSUB : (bProperty ? ENDPROPERTY : ENDFUNC) );
	l2 = nLine;
	pProc->SetLine1( l1 );
	pProc->SetLine2( l2 );
	pPool = &aPublics;
	aPublics.SetProcId( 0 );
	// Offene Labels?
	pProc->GetLabels().CheckRefs();
	CloseBlock();
	aGen.Gen( _LEAVE );
	pProc = NULL;
}

// STATIC variable|procedure

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

void SbiParser::DefStatic( sal_Bool bPrivate )
{
	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( _JUMP, 0 );
				bNewGblDefs = sal_False;
			}
			Next();
			DefProc( sal_True, bPrivate );
			break;
		default: {
			if( !pProc )
				Error( SbERR_NOT_IN_SUBR );
			// Pool umsetzen, damit STATIC-Deklarationen im globalen
			// Pool landen
			SbiSymPool* p = pPool; pPool = &aPublics;
			DefVar( _STATIC, sal_True );
			pPool = p;
			} break;
	}
}

