diff options
Diffstat (limited to 'basic/source/comp')
-rw-r--r-- | basic/source/comp/buffer.cxx | 250 | ||||
-rw-r--r-- | basic/source/comp/codegen.cxx | 528 | ||||
-rw-r--r-- | basic/source/comp/dim.cxx | 1104 | ||||
-rw-r--r-- | basic/source/comp/exprgen.cxx | 297 | ||||
-rw-r--r-- | basic/source/comp/exprnode.cxx | 478 | ||||
-rw-r--r-- | basic/source/comp/exprtree.cxx | 1101 | ||||
-rw-r--r-- | basic/source/comp/io.cxx | 321 | ||||
-rw-r--r-- | basic/source/comp/loops.cxx | 558 | ||||
-rw-r--r-- | basic/source/comp/makefile.mk | 73 | ||||
-rw-r--r-- | basic/source/comp/parser.cxx | 816 | ||||
-rw-r--r-- | basic/source/comp/sbcomp.cxx | 171 | ||||
-rw-r--r-- | basic/source/comp/scanner.cxx | 582 | ||||
-rw-r--r-- | basic/source/comp/symtbl.cxx | 534 | ||||
-rw-r--r-- | basic/source/comp/token.cxx | 698 |
14 files changed, 7511 insertions, 0 deletions
diff --git a/basic/source/comp/buffer.cxx b/basic/source/comp/buffer.cxx new file mode 100644 index 000000000000..74559bf0e6c4 --- /dev/null +++ b/basic/source/comp/buffer.cxx @@ -0,0 +1,250 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_basic.hxx" + +#include "sbcomp.hxx" +#include "buffer.hxx" +#include <string.h> + +const static UINT32 UP_LIMIT=0xFFFFFF00L; + +// Der SbiBuffer wird in Inkrements von mindestens 16 Bytes erweitert. +// Dies ist notwendig, da viele Klassen von einer Pufferlaenge +// von x*16 Bytes ausgehen. + +SbiBuffer::SbiBuffer( SbiParser* p, short n ) +{ + pParser = p; + n = ( (n + 15 ) / 16 ) * 16; + if( !n ) n = 16; + pBuf = NULL; + pCur = NULL; + nInc = n; + nSize = + nOff = 0; +} + +SbiBuffer::~SbiBuffer() +{ + delete[] pBuf; +} + +// Rausreichen des Puffers +// Dies fuehrt zur Loeschung des Puffers! + +char* SbiBuffer::GetBuffer() +{ + char* p = pBuf; + pBuf = NULL; + pCur = NULL; + return p; +} + +// Test, ob der Puffer n Bytes aufnehmen kann. +// Im Zweifelsfall wird er vergroessert + +BOOL SbiBuffer::Check( USHORT n ) +{ + if( !n ) return TRUE; + if( ( static_cast<UINT32>( nOff )+ n ) > static_cast<UINT32>( nSize ) ) + { + if( nInc == 0 ) + return FALSE; + USHORT nn = 0; + while( nn < n ) nn = nn + nInc; + char* p; + if( ( static_cast<UINT32>( nSize ) + nn ) > UP_LIMIT ) p = NULL; + else p = new char [nSize + nn]; + if( !p ) + { + pParser->Error( SbERR_PROG_TOO_LARGE ); + nInc = 0; + delete[] pBuf; pBuf = NULL; + return FALSE; + } + else + { + if( nSize ) memcpy( p, pBuf, nSize ); + delete[] pBuf; + pBuf = p; + pCur = pBuf + nOff; + nSize = nSize + nn; + } + } + return TRUE; +} + +// Angleich des Puffers auf die uebergebene Byte-Grenze + +void SbiBuffer::Align( INT32 n ) +{ + if( nOff % n ) { + UINT32 nn =( ( nOff + n ) / n ) * n; + if( nn <= UP_LIMIT ) + { + nn = nn - nOff; + if( Check( static_cast<USHORT>(nn) ) ) + { + memset( pCur, 0, nn ); + pCur += nn; + nOff = nOff + nn; + } + } + } +} + +// Patch einer Location + +void SbiBuffer::Patch( UINT32 off, UINT32 val ) +{ + if( ( off + sizeof( UINT32 ) ) < nOff ) + { + UINT16 val1 = static_cast<UINT16>( val & 0xFFFF ); + UINT16 val2 = static_cast<UINT16>( val >> 16 ); + BYTE* p = (BYTE*) pBuf + off; + *p++ = (char) ( val1 & 0xFF ); + *p++ = (char) ( val1 >> 8 ); + *p++ = (char) ( val2 & 0xFF ); + *p = (char) ( val2 >> 8 ); + } +} + +// Forward References auf Labels und Prozeduren +// bauen eine Kette auf. Der Anfang der Kette ist beim uebergebenen +// Parameter, das Ende der Kette ist 0. + +void SbiBuffer::Chain( UINT32 off ) +{ + if( off && pBuf ) + { + BYTE *ip; + UINT32 i = off; + UINT32 val1 = (nOff & 0xFFFF); + UINT32 val2 = (nOff >> 16); + do + { + ip = (BYTE*) pBuf + i; + BYTE* pTmp = ip; + i = *pTmp++; i |= *pTmp++ << 8; i |= *pTmp++ << 16; i |= *pTmp++ << 24; + + if( i >= nOff ) + { + pParser->Error( SbERR_INTERNAL_ERROR, "BACKCHAIN" ); + break; + } + *ip++ = (char) ( val1 & 0xFF ); + *ip++ = (char) ( val1 >> 8 ); + *ip++ = (char) ( val2 & 0xFF ); + *ip = (char) ( val2 >> 8 ); + } while( i ); + } +} + +BOOL SbiBuffer::operator +=( INT8 n ) +{ + if( Check( 1 ) ) + { + *pCur++ = (char) n; nOff++; return TRUE; + } else return FALSE; +} + +BOOL SbiBuffer::operator +=( UINT8 n ) +{ + if( Check( 1 ) ) + { + *pCur++ = (char) n; nOff++; return TRUE; + } else return FALSE; +} + +BOOL SbiBuffer::operator +=( INT16 n ) +{ + if( Check( 2 ) ) + { + *pCur++ = (char) ( n & 0xFF ); + *pCur++ = (char) ( n >> 8 ); + nOff += 2; return TRUE; + } else return FALSE; +} + +BOOL SbiBuffer::operator +=( UINT16 n ) +{ + if( Check( 2 ) ) + { + *pCur++ = (char) ( n & 0xFF ); + *pCur++ = (char) ( n >> 8 ); + nOff += 2; return TRUE; + } else return FALSE; +} + +BOOL SbiBuffer::operator +=( UINT32 n ) +{ + if( Check( 4 ) ) + { + UINT16 n1 = static_cast<UINT16>( n & 0xFFFF ); + UINT16 n2 = static_cast<UINT16>( n >> 16 ); + if ( operator +=( n1 ) && operator +=( n2 ) ) + return TRUE; + return TRUE; + } + return FALSE; +} + +BOOL SbiBuffer::operator +=( INT32 n ) +{ + return operator +=( (UINT32) n ); +} + + +BOOL SbiBuffer::operator +=( const String& n ) +{ + USHORT l = n.Len() + 1; + if( Check( l ) ) + { + ByteString aByteStr( n, gsl_getSystemTextEncoding() ); + memcpy( pCur, aByteStr.GetBuffer(), l ); + pCur += l; + nOff = nOff + l; + return TRUE; + } + else return FALSE; +} + +BOOL SbiBuffer::Add( const void* p, USHORT len ) +{ + if( Check( len ) ) + { + memcpy( pCur, p, len ); + pCur += len; + nOff = nOff + len; + return TRUE; + } else return FALSE; +} + + + diff --git a/basic/source/comp/codegen.cxx b/basic/source/comp/codegen.cxx new file mode 100644 index 000000000000..c7a63b6d7fbb --- /dev/null +++ b/basic/source/comp/codegen.cxx @@ -0,0 +1,528 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_basic.hxx" + +#include <basic/sbx.hxx> +#include "sbcomp.hxx" +#include "image.hxx" +#include <limits> + +// nInc ist die Inkrementgroesse der Puffer + +SbiCodeGen::SbiCodeGen( SbModule& r, SbiParser* p, short nInc ) + : rMod( r ), aCode( p, nInc ) +{ + pParser = p; + bStmnt = FALSE; + nLine = 0; + nCol = 0; + nForLevel = 0; +} + +UINT32 SbiCodeGen::GetPC() +{ + return aCode.GetSize(); +} + +// Statement merken + +void SbiCodeGen::Statement() +{ + bStmnt = TRUE; + + nLine = pParser->GetLine(); + nCol = pParser->GetCol1(); + + // #29955 Information der for-Schleifen-Ebene + // in oberen Byte der Spalte speichern + nCol = (nCol & 0xff) + 0x100 * nForLevel; +} + +// Anfang eines Statements markieren + +void SbiCodeGen::GenStmnt() +{ + if( bStmnt ) + { + bStmnt = FALSE; + Gen( _STMNT, nLine, nCol ); + } +} + +// Die Gen-Routinen returnen den Offset des 1. Operanden, +// damit Jumps dort ihr Backchain versenken koennen + +UINT32 SbiCodeGen::Gen( SbiOpcode eOpcode ) +{ +#ifdef DBG_UTIL + if( eOpcode < SbOP0_START || eOpcode > SbOP0_END ) + pParser->Error( SbERR_INTERNAL_ERROR, "OPCODE1" ); +#endif + GenStmnt(); + aCode += (UINT8) eOpcode; + return GetPC(); +} + +UINT32 SbiCodeGen::Gen( SbiOpcode eOpcode, UINT32 nOpnd ) +{ +#ifdef DBG_UTIL + if( eOpcode < SbOP1_START || eOpcode > SbOP1_END ) + pParser->Error( SbERR_INTERNAL_ERROR, "OPCODE2" ); +#endif + GenStmnt(); + aCode += (UINT8) eOpcode; + UINT32 n = GetPC(); + aCode += nOpnd; + return n; +} + +UINT32 SbiCodeGen::Gen( SbiOpcode eOpcode, UINT32 nOpnd1, UINT32 nOpnd2 ) +{ +#ifdef DBG_UTIL + if( eOpcode < SbOP2_START || eOpcode > SbOP2_END ) + pParser->Error( SbERR_INTERNAL_ERROR, "OPCODE3" ); +#endif + GenStmnt(); + aCode += (UINT8) eOpcode; + UINT32 n = GetPC(); + aCode += nOpnd1; + aCode += nOpnd2; + return n; +} + +// Abspeichern des erzeugten Images im Modul + +void SbiCodeGen::Save() +{ + SbiImage* p = new SbiImage; + rMod.StartDefinitions(); + // OPTION BASE-Wert: + p->nDimBase = pParser->nBase; + // OPTION EXPLICIT-Flag uebernehmen + if( pParser->bExplicit ) + p->SetFlag( SBIMG_EXPLICIT ); + if( pParser->IsVBASupportOn() ) + p->SetFlag( SBIMG_VBASUPPORT ); + + int nIfaceCount = 0; + if( pParser->bClassModule ) + { + p->SetFlag( SBIMG_CLASSMODULE ); + pCLASSFAC->AddClassModule( &rMod ); + + nIfaceCount = pParser->aIfaceVector.size(); + if( nIfaceCount ) + { + if( !rMod.pClassData ) + rMod.pClassData = new SbClassData; + + for( int i = 0 ; i < nIfaceCount ; i++ ) + { + const String& rIfaceName = pParser->aIfaceVector[i]; + SbxVariable* pIfaceVar = new SbxVariable( SbxVARIANT ); + pIfaceVar->SetName( rIfaceName ); + SbxArray* pIfaces = rMod.pClassData->mxIfaces; + pIfaces->Insert( pIfaceVar, pIfaces->Count() ); + } + } + } + else + { + pCLASSFAC->RemoveClassModule( &rMod ); + } + if( pParser->bText ) + p->SetFlag( SBIMG_COMPARETEXT ); + // GlobalCode-Flag + if( pParser->HasGlobalCode() ) + p->SetFlag( SBIMG_INITCODE ); + // Die Entrypoints: + for( SbiSymDef* pDef = pParser->aPublics.First(); pDef; + pDef = pParser->aPublics.Next() ) + { + SbiProcDef* pProc = pDef->GetProcDef(); + if( pProc && pProc->IsDefined() ) + { + String aProcName = pProc->GetName(); + String aIfaceProcName; + String aIfaceName; + USHORT nPassCount = 1; + if( nIfaceCount ) + { + int nPropPrefixFound = + aProcName.Search( String( RTL_CONSTASCII_USTRINGPARAM("Property ") ) ); + String aPureProcName = aProcName; + String aPropPrefix; + if( nPropPrefixFound == 0 ) + { + aPropPrefix = aProcName.Copy( 0, 13 ); // 13 == Len( "Property ?et " ) + aPureProcName = aProcName.Copy( 13 ); + } + for( int i = 0 ; i < nIfaceCount ; i++ ) + { + const String& rIfaceName = pParser->aIfaceVector[i]; + int nFound = aPureProcName.Search( rIfaceName ); + if( nFound == 0 && '_' == aPureProcName.GetChar( rIfaceName.Len() ) ) + { + if( nPropPrefixFound == 0 ) + aIfaceProcName += aPropPrefix; + aIfaceProcName += aPureProcName.Copy( rIfaceName.Len() + 1 ); + aIfaceName = rIfaceName; + nPassCount = 2; + break; + } + } + } + SbMethod* pMeth = NULL; + for( USHORT nPass = 0 ; nPass < nPassCount ; nPass++ ) + { + if( nPass == 1 ) + aProcName = aIfaceProcName; + + PropertyMode ePropMode = pProc->getPropertyMode(); + if( ePropMode != PROPERTY_MODE_NONE ) + { + SbxDataType ePropType = SbxEMPTY; + switch( ePropMode ) + { + case PROPERTY_MODE_GET: + ePropType = pProc->GetType(); + break; + case PROPERTY_MODE_LET: + { + // type == type of first parameter + ePropType = SbxVARIANT; // Default + SbiSymPool* pPool = &pProc->GetParams(); + if( pPool->GetSize() > 1 ) + { + SbiSymDef* pPar = pPool->Get( 1 ); + if( pPar ) + ePropType = pPar->GetType(); + } + break; + } + case PROPERTY_MODE_SET: + ePropType = SbxOBJECT; + break; + case PROPERTY_MODE_NONE: + DBG_ERROR( "Illegal PropertyMode PROPERTY_MODE_NONE" ); + break; + } + String aPropName = pProc->GetPropName(); + if( nPass == 1 ) + aPropName = aPropName.Copy( aIfaceName.Len() + 1 ); + SbProcedureProperty* pProcedureProperty = NULL; + pProcedureProperty = rMod.GetProcedureProperty( aPropName, ePropType ); + } + if( nPass == 1 ) + { + SbIfaceMapperMethod* pMapperMeth = NULL; + pMapperMeth = rMod.GetIfaceMapperMethod( aProcName, pMeth ); + } + else + { + pMeth = rMod.GetMethod( aProcName, pProc->GetType() ); + + // #110004 + if( !pProc->IsPublic() ) + pMeth->SetFlag( SBX_PRIVATE ); + + pMeth->nStart = pProc->GetAddr(); + pMeth->nLine1 = pProc->GetLine1(); + pMeth->nLine2 = pProc->GetLine2(); + // Die Parameter: + SbxInfo* pInfo = pMeth->GetInfo(); + String aHelpFile, aComment; + ULONG nHelpId = 0; + if( pInfo ) + { + // Die Zusatzdaten retten + aHelpFile = pInfo->GetHelpFile(); + aComment = pInfo->GetComment(); + nHelpId = pInfo->GetHelpId(); + } + // Und die Parameterliste neu aufbauen + pInfo = new SbxInfo( aHelpFile, nHelpId ); + pInfo->SetComment( aComment ); + SbiSymPool* pPool = &pProc->GetParams(); + // Das erste Element ist immer der Funktionswert! + for( USHORT i = 1; i < pPool->GetSize(); i++ ) + { + SbiSymDef* pPar = pPool->Get( i ); + SbxDataType t = pPar->GetType(); + if( !pPar->IsByVal() ) + t = (SbxDataType) ( t | SbxBYREF ); + if( pPar->GetDims() ) + t = (SbxDataType) ( t | SbxARRAY ); + // #33677 Optional-Info durchreichen + USHORT nFlags = SBX_READ; + if( pPar->IsOptional() ) + nFlags |= SBX_OPTIONAL; + + pInfo->AddParam( pPar->GetName(), t, nFlags ); + + UINT32 nUserData = 0; + USHORT nDefaultId = pPar->GetDefaultId(); + if( nDefaultId ) + nUserData |= nDefaultId; + if( pPar->IsParamArray() ) + nUserData |= PARAM_INFO_PARAMARRAY; + if( nUserData ) + { + SbxParamInfo* pParam = (SbxParamInfo*)pInfo->GetParam( i ); + pParam->nUserData = nUserData; + } + } + pMeth->SetInfo( pInfo ); + } + + } // for( iPass... + } + } + // Der Code + p->AddCode( aCode.GetBuffer(), aCode.GetSize() ); + + // Der globale StringPool. 0 ist nicht belegt. + SbiStringPool* pPool = &pParser->aGblStrings; + USHORT nSize = pPool->GetSize(); + p->MakeStrings( nSize ); + USHORT i; + for( i = 1; i <= nSize; i++ ) + p->AddString( pPool->Find( i ) ); + + // Typen einfuegen + USHORT nCount = pParser->rTypeArray->Count(); + for (i = 0; i < nCount; i++) + p->AddType((SbxObject *)pParser->rTypeArray->Get(i)); + + // Insert enum objects + nCount = pParser->rEnumArray->Count(); + for (i = 0; i < nCount; i++) + p->AddEnum((SbxObject *)pParser->rEnumArray->Get(i)); + + if( !p->IsError() ) + rMod.pImage = p; + else + delete p; + + rMod.EndDefinitions(); +} + +template < class T > +class PCodeVisitor +{ +public: + virtual ~PCodeVisitor(); + + virtual void start( BYTE* pStart ) = 0; + virtual void processOpCode0( SbiOpcode eOp ) = 0; + virtual void processOpCode1( SbiOpcode eOp, T nOp1 ) = 0; + virtual void processOpCode2( SbiOpcode eOp, T nOp1, T nOp2 ) = 0; + virtual bool processParams() = 0; + virtual void end() = 0; +}; + +template <class T> PCodeVisitor< T >::~PCodeVisitor() +{} + +template <class T> +class PCodeBufferWalker +{ +private: + T m_nBytes; + BYTE* m_pCode; + T readParam( BYTE*& pCode ) + { + short nBytes = sizeof( T ); + T nOp1=0; + for ( int i=0; i<nBytes; ++i ) + nOp1 |= *pCode++ << ( i * 8); + return nOp1; + } +public: + PCodeBufferWalker( BYTE* pCode, T nBytes ): m_nBytes( nBytes ), m_pCode( pCode ) + { + } + void visitBuffer( PCodeVisitor< T >& visitor ) + { + BYTE* pCode = m_pCode; + if ( !pCode ) + return; + BYTE* pEnd = pCode + m_nBytes; + visitor.start( m_pCode ); + T nOp1 = 0, nOp2 = 0; + for( ; pCode < pEnd; ) + { + SbiOpcode eOp = (SbiOpcode)(*pCode++); + + if ( eOp <= SbOP0_END ) + visitor.processOpCode0( eOp ); + else if( eOp >= SbOP1_START && eOp <= SbOP1_END ) + { + if ( visitor.processParams() ) + nOp1 = readParam( pCode ); + else + pCode += sizeof( T ); + visitor.processOpCode1( eOp, nOp1 ); + } + else if( eOp >= SbOP2_START && eOp <= SbOP2_END ) + { + if ( visitor.processParams() ) + { + nOp1 = readParam( pCode ); + nOp2 = readParam( pCode ); + } + else + pCode += ( sizeof( T ) * 2 ); + visitor.processOpCode2( eOp, nOp1, nOp2 ); + } + } + visitor.end(); + } +}; + +template < class T, class S > +class OffSetAccumulator : public PCodeVisitor< T > +{ + T m_nNumOp0; + T m_nNumSingleParams; + T m_nNumDoubleParams; +public: + + OffSetAccumulator() : m_nNumOp0(0), m_nNumSingleParams(0), m_nNumDoubleParams(0){} + virtual void start( BYTE* /*pStart*/ ){} + virtual void processOpCode0( SbiOpcode /*eOp*/ ){ ++m_nNumOp0; } + virtual void processOpCode1( SbiOpcode /*eOp*/, T /*nOp1*/ ){ ++m_nNumSingleParams; } + virtual void processOpCode2( SbiOpcode /*eOp*/, T /*nOp1*/, T /*nOp2*/ ) { ++m_nNumDoubleParams; } + virtual void end(){} + S offset() + { + T result = 0 ; + static const S max = std::numeric_limits< S >::max(); + result = m_nNumOp0 + ( ( sizeof(S) + 1 ) * m_nNumSingleParams ) + ( (( sizeof(S) * 2 )+ 1 ) * m_nNumDoubleParams ); + if ( result > max ) + return max; + + return static_cast<S>(result); + } + virtual bool processParams(){ return false; } +}; + + + +template < class T, class S > + +class BufferTransformer : public PCodeVisitor< T > +{ + BYTE* m_pStart; + SbiBuffer m_ConvertedBuf; +public: + BufferTransformer():m_pStart(NULL), m_ConvertedBuf( NULL, 1024 ) {} + virtual void start( BYTE* pStart ){ m_pStart = pStart; } + virtual void processOpCode0( SbiOpcode eOp ) + { + m_ConvertedBuf += (UINT8)eOp; + } + virtual void processOpCode1( SbiOpcode eOp, T nOp1 ) + { + m_ConvertedBuf += (UINT8)eOp; + switch( eOp ) + { + case _JUMP: + case _JUMPT: + case _JUMPF: + case _GOSUB: + case _CASEIS: + case _RETURN: + case _ERRHDL: + case _TESTFOR: + nOp1 = static_cast<T>( convertBufferOffSet(m_pStart, nOp1) ); + break; + case _RESUME: + if ( nOp1 > 1 ) + nOp1 = static_cast<T>( convertBufferOffSet(m_pStart, nOp1) ); + break; + default: + break; // + + } + m_ConvertedBuf += (S)nOp1; + } + virtual void processOpCode2( SbiOpcode eOp, T nOp1, T nOp2 ) + { + m_ConvertedBuf += (UINT8)eOp; + if ( eOp == _CASEIS ) + if ( nOp1 ) + nOp1 = static_cast<T>( convertBufferOffSet(m_pStart, nOp1) ); + m_ConvertedBuf += (S)nOp1; + m_ConvertedBuf += (S)nOp2; + + } + virtual bool processParams(){ return true; } + virtual void end() {} + // yeuch, careful here, you can only call + // GetBuffer on the returned SbiBuffer once, also + // you (as the caller) get to own the memory + SbiBuffer& buffer() + { + return m_ConvertedBuf; + } + static S convertBufferOffSet( BYTE* pStart, T nOp1 ) + { + PCodeBufferWalker< T > aBuff( pStart, nOp1); + OffSetAccumulator< T, S > aVisitor; + aBuff.visitBuffer( aVisitor ); + return aVisitor.offset(); + } +}; + +UINT32 +SbiCodeGen::calcNewOffSet( BYTE* pCode, UINT16 nOffset ) +{ + return BufferTransformer< UINT16, UINT32 >::convertBufferOffSet( pCode, nOffset ); +} + +UINT16 +SbiCodeGen::calcLegacyOffSet( BYTE* pCode, UINT32 nOffset ) +{ + return BufferTransformer< UINT32, UINT16 >::convertBufferOffSet( pCode, nOffset ); +} + +template <class T, class S> +void +PCodeBuffConvertor<T,S>::convert() +{ + PCodeBufferWalker< T > aBuf( m_pStart, m_nSize ); + BufferTransformer< T, S > aTrnsfrmer; + aBuf.visitBuffer( aTrnsfrmer ); + m_pCnvtdBuf = (BYTE*)aTrnsfrmer.buffer().GetBuffer(); + m_nCnvtdSize = static_cast<S>( aTrnsfrmer.buffer().GetSize() ); +} + +template class PCodeBuffConvertor< UINT16, UINT32 >; +template class PCodeBuffConvertor< UINT32, UINT16 >; diff --git a/basic/source/comp/dim.cxx b/basic/source/comp/dim.cxx new file mode 100644 index 000000000000..fb4071bcc2a2 --- /dev/null +++ b/basic/source/comp/dim.cxx @@ -0,0 +1,1104 @@ + /************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_basic.hxx" +#include <basic/sbx.hxx> +#include "sbcomp.hxx" + +// 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, BOOL bStatic, BOOL bConst ) +{ + 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 ); + pDef->SetType( t ); + if( bStatic ) + pDef->SetStatic(); + 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, 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 ) + Error( SbERR_OUT_OF_RANGE ); + } + } + 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 ) ); + } + 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() : FALSE ); +} + +void SbiParser::DefVar( SbiOpcode eOp, BOOL bStatic ) +{ + SbiSymPool* pOldPool = pPool; + BOOL bSwitchPool = FALSE; + BOOL bPersistantGlobal = FALSE; + SbiToken eFirstTok = eCurTok; + if( pProc && ( eCurTok == GLOBAL || eCurTok == PUBLIC || eCurTok == PRIVATE ) ) + Error( SbERR_NOT_IN_SUBR, eCurTok ); + if( eCurTok == PUBLIC || eCurTok == GLOBAL ) + { + bSwitchPool = TRUE; // im richtigen Moment auf globalen Pool schalten + if( eCurTok == GLOBAL ) + bPersistantGlobal = 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 = TRUE; + // PRIVATE ist Synonym fuer 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) ) + { + // 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 = FALSE; + } + Next(); + DefProc( FALSE, bPrivate ); + return; + } + else if( eCurTok == ENUM ) + { + Next(); + DefEnum( bPrivate ); + return; + } + else if( eCurTok == DECLARE ) + { + Next(); + DefDeclare( 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 + UINT32 nEndOfStaticLbl = 0; + if( !bVBASupportOn && bStatic ) + { + nEndOfStaticLbl = aGen.Gen( _JUMP, 0 ); + aGen.Statement(); // bei static hier nachholen + } + + BOOL bDefined = 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 + BOOL bRtlSym = FALSE; + if( !pOld ) + { + pOld = CheckRTLForSym( pDef->GetName(), SbxVARIANT ); + if( pOld ) + bRtlSym = TRUE; + } + if( pOld && !(eOp == _REDIM || eOp == _REDIMP) ) + { + if( pDef->GetScope() == SbLOCAL && pOld->GetScope() != SbLOCAL ) + pOld = NULL; + } + if( pOld ) + { + bDefined = 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 = TRUE; + break; + default: eOp2 = _LOCAL; + } + aGen.Gen( + eOp2, pDef->GetId(), + sal::static_int_cast< UINT16 >( pDef->GetType() ) ); + } + + // 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( TRUE ); + SbiExpression aExpr( this, *pDef, pDim ); + aExpr.Gen(); + pDef->SetGlobal( 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 = 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() : FALSE ); +} + +// ERASE array, ... + +void SbiParser::Erase() +{ + while( !bAbort ) + { + if( !TestSymbol() ) return; + String aName( aSym ); + SbxDataType eType = eScanType; + SbiSymDef* pDef = pPool->Find( aName ); + if( !pDef ) + { + if( bExplicit ) + Error( SbERR_UNDEF_VAR, aName ); + pDef = pPool->AddSym( aName ); + pDef->SetType( eType ); + } + SbiExpression aExpr( this, *pDef ); + aExpr.Gen(); + aGen.Gen( _ERASE ); + if( !TestComma() ) break; + } +} + +// Deklaration eines Datentyps + +void SbiParser::Type() +{ + DefType( FALSE ); +} + +void SbiParser::DefType( 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; + BOOL bDone = FALSE; + + while( !bDone && !IsEof() ) + { + switch( Peek() ) + { + case ENDTYPE : + pElem = NULL; + bDone = TRUE; + Next(); + break; + + case EOLN : + case REM : + pElem = NULL; + Next(); + break; + + default: + pDim = NULL; + pElem = VarDecl(&pDim,FALSE,FALSE); + if( !pElem ) + bDone = TRUE; // Error occured + } + if( pElem ) + { + SbxArray *pTypeMembers = pType -> GetProperties(); + if (pTypeMembers -> Find (pElem->GetName(),SbxCLASS_DONTCARE)) + Error (SbERR_VAR_DEFINED); + else + { + SbxProperty *pTypeElem = new SbxProperty (pElem->GetName(),pElem->GetType()); + if( pDim ) + { + SbxDimArray* pArray = new SbxDimArray( pElem->GetType() ); + if ( pDim->GetSize() ) + { + // Dimension the target array + + for ( short i=0; i<pDim->GetSize();++i ) + { + INT32 ub = -1; + 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 + USHORT 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 ); + } + 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( FALSE ); +} + +void SbiParser::DefEnum( 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; + 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 = NULL; + bDone = TRUE; + Next(); + break; + + case EOLN : + case REM : + pElem = NULL; + Next(); + break; + + default: + { + // TODO: Check existing! + BOOL bDefined = FALSE; + + pDim = NULL; + pElem = VarDecl( &pDim, FALSE, TRUE ); + if( !pElem ) + { + bDone = TRUE; // Error occured + break; + } + else if( pDim ) + { + delete pDim; + Error( SbERR_SYNTAX ); + bDone = 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 = TRUE; // Error occured + break; + } + + pPool->Add( pElem ); + + if( !bPrivate ) + { + SbiOpcode eOp = _GLOBAL; + aGen.BackChain( nGblChain ); + nGblChain = 0; + bGblDefs = bNewGblDefs = TRUE; + aGen.Gen( + eOp, pElem->GetId(), + sal::static_int_cast< UINT16 >( pElem->GetType() ) ); + + aVar.Gen(); + USHORT 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( BOOL bDecl ) +{ + BOOL bFunc = BOOL( eCurTok == FUNCTION ); + BOOL bProp = 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( 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( FALSE ); + pDef->GetAlias().Erase(); + } + // Klammern? + 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 ) Next(), bByVal = TRUE; + else if ( Peek() == BYREF ) Next(), bByVal = FALSE; + else if ( Peek() == _OPTIONAL_ ) Next(), bOptional = TRUE; + } + if( bCompatible && Peek() == PARAMARRAY ) + { + if( bByVal || bByVal || bOptional ) + Error( SbERR_UNEXPECTED, PARAMARRAY ); + Next(); + bParamArray = TRUE; + } + SbiSymDef* pPar = VarDecl( NULL, FALSE, 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 ) + { + BOOL bError2 = TRUE; + if( bOptional && bCompatible && eTok == EQ ) + { + SbiConstExpression* pDefaultExpr = new SbiConstExpression( this ); + SbxDataType eType2 = pDefaultExpr->GetType(); + + USHORT 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 = 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( FALSE ); +} + +void SbiParser::DefDeclare( BOOL bPrivate ) +{ + Next(); + if( eCurTok != SUB && eCurTok != FUNCTION ) + Error( SbERR_UNEXPECTED, eCurTok ); + else + { + SbiProcDef* pDef = ProcDecl( 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 ); + } + } +} + +// 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( FALSE, FALSE ); +} + +// Einlesen einer Prozedur + +BOOL runsInSetup( void ); + +void SbiParser::DefProc( BOOL bStatic, BOOL bPrivate ) +{ + USHORT l1 = nLine, l2 = nLine; + BOOL bSub = BOOL( eCurTok == SUB ); + BOOL bProperty = 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( 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( TRUE ); + else + Error( SbERR_NOT_IMPLEMENTED ); // STATIC SUB ... + } + else + { + pProc->SetStatic( 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( FALSE ); +} + +void SbiParser::DefStatic( 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 = FALSE; + } + Next(); + DefProc( 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, TRUE ); + pPool = p; + } break; + } +} + diff --git a/basic/source/comp/exprgen.cxx b/basic/source/comp/exprgen.cxx new file mode 100644 index 000000000000..89520832ff67 --- /dev/null +++ b/basic/source/comp/exprgen.cxx @@ -0,0 +1,297 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_basic.hxx" + +#include "sbcomp.hxx" +#include "expr.hxx" + +// Umsetztabelle fuer Token-Operatoren und Opcodes + +typedef struct { + SbiToken eTok; // Token + SbiOpcode eOp; // Opcode +} OpTable; + +static OpTable aOpTable [] = { + { EXPON,_EXP }, + { MUL, _MUL }, + { DIV, _DIV }, + { IDIV, _IDIV }, + { MOD, _MOD }, + { PLUS, _PLUS }, + { MINUS,_MINUS }, + { EQ, _EQ }, + { NE, _NE }, + { LE, _LE }, + { GE, _GE }, + { LT, _LT }, + { GT, _GT }, + { AND, _AND }, + { OR, _OR }, + { XOR, _XOR }, + { EQV, _EQV }, + { IMP, _IMP }, + { NOT, _NOT }, + { NEG, _NEG }, + { CAT, _CAT }, + { LIKE, _LIKE }, + { IS, _IS }, + { NIL, _NOP }}; + +// Ausgabe eines Elements +void SbiExprNode::Gen( RecursiveMode eRecMode ) +{ + if( IsConstant() ) + { + switch( GetType() ) + { + case SbxEMPTY: pGen->Gen( _EMPTY ); break; + case SbxINTEGER: pGen->Gen( _CONST, (short) nVal ); break; + case SbxSTRING: + { + USHORT nStringId = pGen->GetParser()->aGblStrings.Add( aStrVal, TRUE ); + pGen->Gen( _SCONST, nStringId ); break; + } + default: + { + USHORT nStringId = pGen->GetParser()->aGblStrings.Add( nVal, eType ); + pGen->Gen( _NUMBER, nStringId ); + } + } + } + else if( IsOperand() ) + { + SbiExprNode* pWithParent_ = NULL; + SbiOpcode eOp; + if( aVar.pDef->GetScope() == SbPARAM ) + { + eOp = _PARAM; + if( 0 == aVar.pDef->GetPos() ) + { + bool bTreatFunctionAsParam = true; + if( eRecMode == FORCE_CALL ) + { + bTreatFunctionAsParam = false; + } + else if( eRecMode == UNDEFINED ) + { + if( aVar.pPar && aVar.pPar->IsBracket() ) + bTreatFunctionAsParam = false; + } + if( !bTreatFunctionAsParam ) + eOp = aVar.pDef->IsGlobal() ? _FIND_G : _FIND; + } + } + // AB: 17.12.1995, Spezialbehandlung fuer WITH + else if( (pWithParent_ = GetWithParent()) != NULL ) + { + eOp = _ELEM; // .-Ausdruck in WITH + } + else + { + SbiProcDef* pProc = aVar.pDef->GetProcDef(); + // per DECLARE definiert? + if( pProc && pProc->GetLib().Len() ) + eOp = pProc->IsCdecl() ? _CALLC : _CALL; + else + eOp = ( aVar.pDef->GetScope() == SbRTL ) ? _RTL : + (aVar.pDef->IsGlobal() ? _FIND_G : _FIND); + } + + if( eOp == _FIND ) + { + + SbiProcDef* pProc = aVar.pDef->GetProcDef(); + if ( pGen->GetParser()->bClassModule ) + eOp = _FIND_CM; + else if ( aVar.pDef->IsStatic() || (pProc && pProc->IsStatic()) ) + { + eOp = _FIND_STATIC; + } + } + for( SbiExprNode* p = this; p; p = p->aVar.pNext ) + { + if( p == this && pWithParent_ != NULL ) + pWithParent_->Gen(); + p->GenElement( eOp ); + eOp = _ELEM; + } + } + else if( IsTypeOf() ) + { + pLeft->Gen(); + pGen->Gen( _TESTCLASS, nTypeStrId ); + } + else + { + pLeft->Gen(); + if( pRight ) + pRight->Gen(); + for( OpTable* p = aOpTable; p->eTok != NIL; p++ ) + { + if( p->eTok == eTok ) + { + pGen->Gen( p->eOp ); break; + } + } + } +} + +// Ausgabe eines Operanden-Elements + +void SbiExprNode::GenElement( SbiOpcode eOp ) +{ +#ifdef DBG_UTIL + if( (eOp < _RTL || eOp > _CALLC) && eOp != _FIND_G && eOp != _FIND_CM ) + pGen->GetParser()->Error( SbERR_INTERNAL_ERROR, "Opcode" ); +#endif + SbiSymDef* pDef = aVar.pDef; + // Das ID ist entweder die Position oder das String-ID + // Falls das Bit 0x8000 gesetzt ist, hat die Variable + // eine Parameterliste. + USHORT nId = ( eOp == _PARAM ) ? pDef->GetPos() : pDef->GetId(); + // Parameterliste aufbauen + if( aVar.pPar && aVar.pPar->GetSize() ) + { + nId |= 0x8000; + aVar.pPar->Gen(); + } + + SbiProcDef* pProc = aVar.pDef->GetProcDef(); + // per DECLARE definiert? + if( pProc ) + { + // Dann evtl. einen LIB-Befehl erzeugen + if( pProc->GetLib().Len() ) + pGen->Gen( _LIB, pGen->GetParser()->aGblStrings.Add( pProc->GetLib() ) ); + // und den Aliasnamen nehmen + if( pProc->GetAlias().Len() ) + nId = ( nId & 0x8000 ) | pGen->GetParser()->aGblStrings.Add( pProc->GetAlias() ); + } + pGen->Gen( eOp, nId, sal::static_int_cast< UINT16 >( GetType() ) ); + + if( aVar.pvMorePar ) + { + SbiExprListVector* pvMorePar = aVar.pvMorePar; + SbiExprListVector::iterator it; + for( it = pvMorePar->begin() ; it != pvMorePar->end() ; ++it ) + { + SbiExprList* pExprList = *it; + pExprList->Gen(); + pGen->Gen( _ARRAYACCESS ); + } + } +} + +// Erzeugen einer Argv-Tabelle +// Das erste Element bleibt immer frei fuer Returnwerte etc. +// Siehe auch SbiProcDef::SbiProcDef() in symtbl.cxx + +void SbiExprList::Gen() +{ + if( pFirst ) + { + pParser->aGen.Gen( _ARGC ); + // AB 10.1.96: Typ-Anpassung bei DECLARE + USHORT nCount = 1, nParAnz = 0; + SbiSymPool* pPool = NULL; + if( pProc ) + { + pPool = &pProc->GetParams(); + nParAnz = pPool->GetSize(); + } + for( SbiExpression* pExpr = pFirst; pExpr; pExpr = pExpr->pNext,nCount++ ) + { + pExpr->Gen(); + if( pExpr->GetName().Len() ) + { + // named arg + USHORT nSid = pParser->aGblStrings.Add( pExpr->GetName() ); + pParser->aGen.Gen( _ARGN, nSid ); + + // AB 10.1.96: Typanpassung bei named -> passenden Parameter suchen + if( pProc ) + { + // Vorerst: Error ausloesen + pParser->Error( SbERR_NO_NAMED_ARGS ); + + // Spaeter, wenn Named Args bei DECLARE moeglich + /* + for( USHORT i = 1 ; i < nParAnz ; i++ ) + { + SbiSymDef* pDef = pPool->Get( i ); + const String& rName = pDef->GetName(); + if( rName.Len() ) + { + if( pExpr->GetName().ICompare( rName ) + == COMPARE_EQUAL ) + { + pParser->aGen.Gen( _ARGTYP, pDef->GetType() ); + break; + } + } + } + */ + } + } + else + { + pParser->aGen.Gen( _ARGV ); + + // Funktion mit DECLARE -> Typ-Anpassung + if( pProc && nCount < nParAnz ) + { + SbiSymDef* pDef = pPool->Get( nCount ); + USHORT nTyp = sal::static_int_cast< USHORT >( + pDef->GetType() ); + // Zusätzliches Flag für BYVAL einbauen + if( pDef->IsByVal() ) + nTyp |= 0x8000; + pParser->aGen.Gen( _ARGTYP, nTyp ); + } + } + } + } +} + +void SbiExpression::Gen( RecursiveMode eRecMode ) +{ + // AB: 17.12.1995, Spezialbehandlung fuer WITH + // Wenn pExpr == .-Ausdruck in With, zunaechst Gen fuer Basis-Objekt + pExpr->Gen( eRecMode ); + if( bBased ) + { + USHORT uBase = pParser->nBase; + if( pParser->IsCompatible() ) + uBase |= 0x8000; // #109275 Flag compatiblity + pParser->aGen.Gen( _BASED, uBase ); + pParser->aGen.Gen( _ARGV ); + } +} + diff --git a/basic/source/comp/exprnode.cxx b/basic/source/comp/exprnode.cxx new file mode 100644 index 000000000000..a77cf32abbaa --- /dev/null +++ b/basic/source/comp/exprnode.cxx @@ -0,0 +1,478 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_basic.hxx" + +#include <math.h> + +#include <rtl/math.hxx> +#include "sbcomp.hxx" +#include "expr.hxx" + +////////////////////////////////////////////////////////////////////////// + +SbiExprNode::SbiExprNode( void ) +{ + pLeft = NULL; + pRight = NULL; + eNodeType = SbxDUMMY; +} + +SbiExprNode::SbiExprNode( SbiParser* p, SbiExprNode* l, SbiToken t, SbiExprNode* r ) +{ + BaseInit( p ); + + pLeft = l; + pRight = r; + eTok = t; + nVal = 0; + eType = SbxVARIANT; // Nodes sind immer Variant + eNodeType = SbxNODE; + bComposite= TRUE; +} + +SbiExprNode::SbiExprNode( SbiParser* p, double n, SbxDataType t ) +{ + BaseInit( p ); + + eType = t; + eNodeType = SbxNUMVAL; + nVal = n; +} + +SbiExprNode::SbiExprNode( SbiParser* p, const String& rVal ) +{ + BaseInit( p ); + + eType = SbxSTRING; + eNodeType = SbxSTRVAL; + aStrVal = rVal; +} + +SbiExprNode::SbiExprNode( SbiParser* p, const SbiSymDef& r, SbxDataType t, SbiExprList* l ) +{ + BaseInit( p ); + + eType = ( t == SbxVARIANT ) ? r.GetType() : t; + eNodeType = SbxVARVAL; + aVar.pDef = (SbiSymDef*) &r; + aVar.pPar = l; + aVar.pvMorePar = NULL; + aVar.pNext= NULL; + + // Funktionsergebnisse sind nie starr + bComposite= BOOL( aVar.pDef->GetProcDef() != NULL ); +} + +// #120061 TypeOf +SbiExprNode::SbiExprNode( SbiParser* p, SbiExprNode* l, USHORT nId ) +{ + BaseInit( p ); + + pLeft = l; + eType = SbxBOOL; + eNodeType = SbxTYPEOF; + nTypeStrId = nId; +} + + +// AB: 17.12.95, Hilfsfunktion fuer Ctor fuer einheitliche Initialisierung +void SbiExprNode::BaseInit( SbiParser* p ) +{ + pGen = &p->aGen; + eTok = NIL; + pLeft = NULL; + pRight = NULL; + pWithParent = NULL; + bComposite = FALSE; + bError = FALSE; +} + +SbiExprNode::~SbiExprNode() +{ + delete pLeft; + delete pRight; + if( IsVariable() ) + { + delete aVar.pPar; + delete aVar.pNext; + SbiExprListVector* pvMorePar = aVar.pvMorePar; + if( pvMorePar ) + { + SbiExprListVector::iterator it; + for( it = pvMorePar->begin() ; it != pvMorePar->end() ; ++it ) + delete *it; + delete pvMorePar; + } + } +} + +SbiSymDef* SbiExprNode::GetVar() +{ + if( eNodeType == SbxVARVAL ) + return aVar.pDef; + else + return NULL; +} + +SbiSymDef* SbiExprNode::GetRealVar() +{ + SbiExprNode* p = GetRealNode(); + if( p ) + return p->GetVar(); + else + return NULL; +} + +// AB: 18.12.95 +SbiExprNode* SbiExprNode::GetRealNode() +{ + if( eNodeType == SbxVARVAL ) + { + SbiExprNode* p = this; + while( p->aVar.pNext ) + p = p->aVar.pNext; + return p; + } + else + return NULL; +} + +// Diese Methode setzt den Typ um, falls er in den Integer-Bereich hineinpasst + +BOOL SbiExprNode::IsIntConst() +{ + if( eNodeType == SbxNUMVAL ) + { + if( eType >= SbxINTEGER && eType <= SbxDOUBLE ) + { + double n; + if( nVal >= SbxMININT && nVal <= SbxMAXINT && modf( nVal, &n ) == 0 ) + { + nVal = (double) (short) nVal; + eType = SbxINTEGER; + return TRUE; + } + } + } + return FALSE; +} + +BOOL SbiExprNode::IsNumber() +{ + return BOOL( eNodeType == SbxNUMVAL ); +} + +BOOL SbiExprNode::IsString() +{ + return BOOL( eNodeType == SbxSTRVAL ); +} + +BOOL SbiExprNode::IsVariable() +{ + return BOOL( eNodeType == SbxVARVAL ); +} + +BOOL SbiExprNode::IsLvalue() +{ + return IsVariable(); +} + +// Ermitteln der Tiefe eines Baumes + +short SbiExprNode::GetDepth() +{ + if( IsOperand() ) return 0; + else + { + short d1 = pLeft->GetDepth(); + short d2 = pRight->GetDepth(); + return( (d1 < d2 ) ? d2 : d1 ) + 1; + } +} + + +// Abgleich eines Baumes: +// 1. Constant Folding +// 2. Typabgleich +// 3. Umwandlung der Operanden in Strings +// 4. Hochziehen der Composite- und Error-Bits + +void SbiExprNode::Optimize() +{ + FoldConstants(); + CollectBits(); +} + +// Hochziehen der Composite- und Fehlerbits + +void SbiExprNode::CollectBits() +{ + if( pLeft ) + { + pLeft->CollectBits(); + bError |= pLeft->bError; + bComposite |= pLeft->bComposite; + } + if( pRight ) + { + pRight->CollectBits(); + bError |= pRight->bError; + bComposite |= pRight->bComposite; + } +} + +// Kann ein Zweig umgeformt werden, wird TRUE zurueckgeliefert. In diesem +// Fall ist das Ergebnis im linken Zweig. + +void SbiExprNode::FoldConstants() +{ + if( IsOperand() || eTok == LIKE ) return; + pLeft->FoldConstants(); + if( pRight ) + { + pRight->FoldConstants(); + if( pLeft->IsConstant() && pRight->IsConstant() + && pLeft->eNodeType == pRight->eNodeType ) + { + CollectBits(); + if( eTok == CAT ) + // CAT verbindet auch zwei Zahlen miteinander! + eType = SbxSTRING; + if( pLeft->eType == SbxSTRING ) + // Kein Type Mismatch! + eType = SbxSTRING; + if( eType == SbxSTRING ) + { + String rl( pLeft->GetString() ); + String rr( pRight->GetString() ); + delete pLeft; pLeft = NULL; + delete pRight; pRight = NULL; + bComposite = FALSE; + if( eTok == PLUS || eTok == CAT ) + { + eTok = CAT; + // Verkettung: + aStrVal = rl; + aStrVal += rr; + eType = SbxSTRING; + eNodeType = SbxSTRVAL; + } + else + { + eType = SbxDOUBLE; + eNodeType = SbxNUMVAL; + StringCompare eRes = rr.CompareTo( rl ); + switch( eTok ) + { + case EQ: + nVal = ( eRes == COMPARE_EQUAL ) ? SbxTRUE : SbxFALSE; + break; + case NE: + nVal = ( eRes != COMPARE_EQUAL ) ? SbxTRUE : SbxFALSE; + break; + case LT: + nVal = ( eRes == COMPARE_LESS ) ? SbxTRUE : SbxFALSE; + break; + case GT: + nVal = ( eRes == COMPARE_GREATER ) ? SbxTRUE : SbxFALSE; + break; + case LE: + nVal = ( eRes != COMPARE_GREATER ) ? SbxTRUE : SbxFALSE; + break; + case GE: + nVal = ( eRes != COMPARE_LESS ) ? SbxTRUE : SbxFALSE; + break; + default: + pGen->GetParser()->Error( SbERR_CONVERSION ); + bError = TRUE; + } + } + } + else + { + double nl = pLeft->nVal; + double nr = pRight->nVal; + long ll = 0, lr = 0; + long llMod = 0, lrMod = 0; + if( ( eTok >= AND && eTok <= IMP ) + || eTok == IDIV || eTok == MOD ) + { + // Integer-Operationen + BOOL err = FALSE; + if( nl > SbxMAXLNG ) err = TRUE, nl = SbxMAXLNG; + else + if( nl < SbxMINLNG ) err = TRUE, nl = SbxMINLNG; + if( nr > SbxMAXLNG ) err = TRUE, nr = SbxMAXLNG; + else + if( nr < SbxMINLNG ) err = TRUE, nr = SbxMINLNG; + ll = (long) nl; lr = (long) nr; + llMod = (long) (nl < 0 ? nl - 0.5 : nl + 0.5); + lrMod = (long) (nr < 0 ? nr - 0.5 : nr + 0.5); + if( err ) + { + pGen->GetParser()->Error( SbERR_MATH_OVERFLOW ); + bError = TRUE; + } + } + BOOL bBothInt = BOOL( pLeft->eType < SbxSINGLE + && pRight->eType < SbxSINGLE ); + delete pLeft; pLeft = NULL; + delete pRight; pRight = NULL; + nVal = 0; + eType = SbxDOUBLE; + eNodeType = SbxNUMVAL; + bComposite = FALSE; + BOOL bCheckType = FALSE; + switch( eTok ) + { + case EXPON: + nVal = pow( nl, nr ); break; + case MUL: + bCheckType = TRUE; + nVal = nl * nr; break; + case DIV: + if( !nr ) + { + pGen->GetParser()->Error( SbERR_ZERODIV ); nVal = HUGE_VAL; + bError = TRUE; + } else nVal = nl / nr; + break; + case PLUS: + bCheckType = TRUE; + nVal = nl + nr; break; + case MINUS: + bCheckType = TRUE; + nVal = nl - nr; break; + case EQ: + nVal = ( nl == nr ) ? SbxTRUE : SbxFALSE; + eType = SbxINTEGER; break; + case NE: + nVal = ( nl != nr ) ? SbxTRUE : SbxFALSE; + eType = SbxINTEGER; break; + case LT: + nVal = ( nl < nr ) ? SbxTRUE : SbxFALSE; + eType = SbxINTEGER; break; + case GT: + nVal = ( nl > nr ) ? SbxTRUE : SbxFALSE; + eType = SbxINTEGER; break; + case LE: + nVal = ( nl <= nr ) ? SbxTRUE : SbxFALSE; + eType = SbxINTEGER; break; + case GE: + nVal = ( nl >= nr ) ? SbxTRUE : SbxFALSE; + eType = SbxINTEGER; break; + case IDIV: + if( !lr ) + { + pGen->GetParser()->Error( SbERR_ZERODIV ); nVal = HUGE_VAL; + bError = TRUE; + } else nVal = ll / lr; + eType = SbxLONG; break; + case MOD: + if( !lr ) + { + pGen->GetParser()->Error( SbERR_ZERODIV ); nVal = HUGE_VAL; + bError = TRUE; + } else nVal = llMod % lrMod; + eType = SbxLONG; break; + case AND: + nVal = (double) ( ll & lr ); eType = SbxLONG; break; + case OR: + nVal = (double) ( ll | lr ); eType = SbxLONG; break; + case XOR: + nVal = (double) ( ll ^ lr ); eType = SbxLONG; break; + case EQV: + nVal = (double) ( ~ll ^ lr ); eType = SbxLONG; break; + case IMP: + nVal = (double) ( ~ll | lr ); eType = SbxLONG; break; + default: break; + } + + if( !::rtl::math::isFinite( nVal ) ) + pGen->GetParser()->Error( SbERR_MATH_OVERFLOW ); + + // Den Datentyp wiederherstellen, um Rundungsfehler + // zu killen + if( bCheckType && bBothInt + && nVal >= SbxMINLNG && nVal <= SbxMAXLNG ) + { + // NK-Stellen weg + long n = (long) nVal; + nVal = n; + eType = ( n >= SbxMININT && n <= SbxMAXINT ) + ? SbxINTEGER : SbxLONG; + } + } + } + } + else if( pLeft->IsNumber() ) + { + nVal = pLeft->nVal; + delete pLeft; + pLeft = NULL; + eType = SbxDOUBLE; + eNodeType = SbxNUMVAL; + bComposite = FALSE; + switch( eTok ) + { + case NEG: + nVal = -nVal; break; + case NOT: { + // Integer-Operation! + BOOL err = FALSE; + if( nVal > SbxMAXLNG ) err = TRUE, nVal = SbxMAXLNG; + else + if( nVal < SbxMINLNG ) err = TRUE, nVal = SbxMINLNG; + if( err ) + { + pGen->GetParser()->Error( SbERR_MATH_OVERFLOW ); + bError = TRUE; + } + nVal = (double) ~((long) nVal); + eType = SbxLONG; + } break; + default: break; + } + } + if( eNodeType == SbxNUMVAL ) + { + // Evtl auf INTEGER falten (wg. besserem Opcode)? + if( eType == SbxSINGLE || eType == SbxDOUBLE ) + { + double x; + if( nVal >= SbxMINLNG && nVal <= SbxMAXLNG + && !modf( nVal, &x ) ) + eType = SbxLONG; + } + if( eType == SbxLONG && nVal >= SbxMININT && nVal <= SbxMAXINT ) + eType = SbxINTEGER; + } +} + + diff --git a/basic/source/comp/exprtree.cxx b/basic/source/comp/exprtree.cxx new file mode 100644 index 000000000000..2f5c0b967a2e --- /dev/null +++ b/basic/source/comp/exprtree.cxx @@ -0,0 +1,1101 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_basic.hxx" + +#include "sbcomp.hxx" +#include <basic/sbx.hxx> // w.g. ...IMPL_REF(...sbxvariable) +#include "expr.hxx" + +/*************************************************************************** +|* +|* SbiExpression +|* +***************************************************************************/ + +SbiExpression::SbiExpression( SbiParser* p, SbiExprType t, SbiExprMode eMode ) +{ + pParser = p; + bError = bByVal = bBased = bBracket = FALSE; + nParenLevel = 0; + eCurExpr = t; + m_eMode = eMode; + pNext = NULL; + pExpr = (t != SbSTDEXPR ) ? Term() : Boolean(); + if( t != SbSYMBOL ) + pExpr->Optimize(); + if( t == SbLVALUE && !pExpr->IsLvalue() ) + p->Error( SbERR_LVALUE_EXPECTED ); + if( t == SbOPERAND && !IsVariable() ) + p->Error( SbERR_VAR_EXPECTED ); +} + +SbiExpression::SbiExpression( SbiParser* p, double n, SbxDataType t ) +{ + pParser = p; + eCurExpr = SbOPERAND; + pNext = NULL; + bError = bByVal = bBased = bBracket = FALSE; + pExpr = new SbiExprNode( pParser, n, t ); + pExpr->Optimize(); +} + +SbiExpression::SbiExpression( SbiParser* p, const String& r ) +{ + pParser = p; + pNext = NULL; + bError = bByVal = bBased = bBracket = FALSE; + eCurExpr = SbOPERAND; + pExpr = new SbiExprNode( pParser, r ); +} + +SbiExpression::SbiExpression( SbiParser* p, const SbiSymDef& r, SbiExprList* pPar ) +{ + pParser = p; + pNext = NULL; + bError = bByVal = bBased = bBracket = FALSE; + eCurExpr = SbOPERAND; + pExpr = new SbiExprNode( pParser, r, SbxVARIANT, pPar ); +} + +SbiExpression::SbiExpression( SbiParser* p, SbiToken t ) +{ + pParser = p; + pNext = NULL; + bError = bByVal = bBased = bBracket = FALSE; + eCurExpr = SbOPERAND; + pExpr = new SbiExprNode( pParser, NULL, t, NULL ); +} + +SbiExpression::~SbiExpression() +{ + delete pExpr; +} + +// Einlesen eines kompletten Bezeichners +// Ein Bezeichner hat folgende Form: +// name[(Parameter)][.Name[(parameter)]]... +// Strukturelemente werden ueber das Element pNext verkoppelt, +// damit sie nicht im Baum stehen. + +// Folgen Parameter ohne Klammer? Dies kann eine Zahl, ein String, +// ein Symbol oder auch ein Komma sein (wenn der 1. Parameter fehlt) + +static BOOL DoParametersFollow( SbiParser* p, SbiExprType eCurExpr, SbiToken eTok ) +{ + if( eTok == LPAREN ) + return TRUE; + // Aber nur, wenn CALL-aehnlich! + if( !p->WhiteSpace() || eCurExpr != SbSYMBOL ) + return FALSE; + if ( eTok == NUMBER || eTok == MINUS || eTok == FIXSTRING + || eTok == SYMBOL || eTok == COMMA || eTok == DOT || eTok == NOT ) + { + return TRUE; + } + else // check for default params with reserved names ( e.g. names of tokens ) + { + SbiTokenizer tokens( *(SbiTokenizer*)p ); + // Urk the Next() / Peek() symantics are... weird + tokens.Next(); + if ( tokens.Peek() == ASSIGN ) + return TRUE; + } + return FALSE; +} + +// Definition eines neuen Symbols + +static SbiSymDef* AddSym + ( SbiToken eTok, SbiSymPool& rPool, SbiExprType eCurExpr, + const String& rName, SbxDataType eType, SbiParameters* pPar ) +{ + SbiSymDef* pDef; + // A= ist keine Prozedur + BOOL bHasType = BOOL( eTok == EQ || eTok == DOT ); + if( ( !bHasType && eCurExpr == SbSYMBOL ) || pPar ) + { + // Dies ist also eine Prozedur + // da suche man doch den richtigen Pool raus, da Procs + // immer in einem Public-Pool landen muessen + SbiSymPool* pPool = &rPool; + if( pPool->GetScope() != SbPUBLIC ) + pPool = &rPool.GetParser()->aPublics; + SbiProcDef* pProc = pPool->AddProc( rName ); + + // Sonderbehandlung fuer Colls wie Documents(1) + if( eCurExpr == SbSTDEXPR ) + bHasType = TRUE; + + pDef = pProc; + pDef->SetType( bHasType ? eType : SbxEMPTY ); + if( pPar ) + { + // Dummy-Parameter generieren + USHORT n = 1; + for( short i = 0; i < pPar->GetSize(); i++ ) + { + String aPar = String::CreateFromAscii( "PAR" ); + aPar += ++n; + pProc->GetParams().AddSym( aPar ); + } + } + } + else + { + // oder ein normales Symbol + pDef = rPool.AddSym( rName ); + pDef->SetType( eType ); + } + return pDef; +} + +// Zur Zeit sind sogar Keywords zugelassen (wg. gleichnamiger Dflt-Properties) + +SbiExprNode* SbiExpression::Term( void ) +{ + if( pParser->Peek() == DOT ) + { + // eine WITH-Variable + SbiExprNode* pWithVar = pParser->GetWithVar(); + // #26608: Ans Ende der Node-Kette gehen, um richtiges Objekt zu uebergeben + SbiSymDef* pDef = pWithVar ? pWithVar->GetRealVar() : NULL; + SbiExprNode* pNd = NULL; + if( !pDef ) + { + pParser->Next(); + } + else + { + pNd = ObjTerm( *pDef ); + if( pNd ) + pNd->SetWithParent( pWithVar ); + } + if( !pNd ) + { + pParser->Error( SbERR_UNEXPECTED, DOT ); + pNd = new SbiExprNode( pParser, 1.0, SbxDOUBLE ); + } + return pNd; + } + + SbiToken eTok = pParser->Next(); + // Anfang des Parsings merken + pParser->LockColumn(); + String aSym( pParser->GetSym() ); + SbxDataType eType = pParser->GetType(); + SbiParameters* pPar = NULL; + SbiExprListVector* pvMoreParLcl = NULL; + // Folgen Parameter? + SbiToken eNextTok = pParser->Peek(); + // Ist es ein benannter Parameter? + // Dann einfach eine Stringkonstante erzeugen. Diese wird + // im SbiParameters-ctor erkannt und weiterverarbeitet + if( eNextTok == ASSIGN ) + { + pParser->UnlockColumn(); + return new SbiExprNode( pParser, aSym ); + } + // ab hier sind keine Keywords zugelassen! + if( pParser->IsKwd( eTok ) ) + { + if( pParser->IsCompatible() && eTok == INPUT ) + { + eTok = SYMBOL; + } + else + { + pParser->Error( SbERR_SYNTAX ); + bError = TRUE; + } + } + + if( DoParametersFollow( pParser, eCurExpr, eTok = eNextTok ) ) + { + bool bStandaloneExpression = (m_eMode == EXPRMODE_STANDALONE); + pPar = new SbiParameters( pParser, bStandaloneExpression ); + bError |= !pPar->IsValid(); + if( !bError ) + bBracket = pPar->IsBracket(); + eTok = pParser->Peek(); + + // i75443 check for additional sets of parameters + while( eTok == LPAREN ) + { + if( pvMoreParLcl == NULL ) + pvMoreParLcl = new SbiExprListVector(); + SbiParameters* pAddPar = new SbiParameters( pParser ); + pvMoreParLcl->push_back( pAddPar ); + bError |= !pPar->IsValid(); + eTok = pParser->Peek(); + } + } + // Es koennte ein Objektteil sein, wenn . oder ! folgt + // Bei . muss aber die Variable bereits definiert sein; wenn pDef + // nach der Suche NULL ist, isses ein Objekt! + BOOL bObj = BOOL( ( eTok == DOT || eTok == EXCLAM ) + && !pParser->WhiteSpace() ); + if( bObj ) + { + bBracket = FALSE; // Now the bracket for the first term is obsolete + if( eType == SbxVARIANT ) + eType = SbxOBJECT; + else + { + // Name%. geht wirklich nicht! + pParser->Error( SbERR_BAD_DECLARATION, aSym ); + bError = TRUE; + } + } + // Suche: + SbiSymDef* pDef = pParser->pPool->Find( aSym ); + if( !pDef ) + { + // Teil der Runtime-Library? + // AB 31.3.1996: In Parser-Methode ausgelagert + // (wird auch in SbiParser::DefVar() in DIM.CXX benoetigt) + pDef = pParser->CheckRTLForSym( aSym, eType ); + } + if( !pDef ) + { + // Falls ein Punkt angegeben war, isses Teil eines Objekts, + // also muss der Returnwert ein Objekt sein + if( bObj ) + eType = SbxOBJECT; + pDef = AddSym( eTok, *pParser->pPool, eCurExpr, aSym, eType, pPar ); + // Looks like this is a local ( but undefined variable ) + // if it is in a static procedure then make this Symbol + // static + if ( !bObj && pParser->pProc && pParser->pProc->IsStatic() ) + pDef->SetStatic(); + } + else + { + + // Symbol ist bereits definiert. + // Ist es eine Konstante? + SbiConstDef* pConst = pDef->GetConstDef(); + if( pConst ) + { + if( pConst->GetType() == SbxSTRING ) + return new SbiExprNode( pParser, pConst->GetString() ); + else + return new SbiExprNode( pParser, pConst->GetValue(), pConst->GetType() ); + } + // Hat es Dimensionen, + // und sind auch Parameter angegeben? + // (Wobei 0 Parameter () entsprechen) + if( pDef->GetDims() ) + { + if( pPar && pPar->GetSize() && pPar->GetSize() != pDef->GetDims() ) + pParser->Error( SbERR_WRONG_DIMS ); + } + if( pDef->IsDefinedAs() ) + { + SbxDataType eDefType = pDef->GetType(); + // #119187 Only error if types conflict + if( eType >= SbxINTEGER && eType <= SbxSTRING && eType != eDefType ) + { + // Wie? Erst mit AS definieren und dann einen Suffix nehmen? + pParser->Error( SbERR_BAD_DECLARATION, aSym ); + bError = TRUE; + } + else if ( eType == SbxVARIANT ) + // Falls nix angegeben, den Typ des Eintrags nehmen + // aber nur, wenn die Var nicht mit AS XXX definiert ist + // damit erwischen wir n% = 5 : print n + eType = eDefType; + } + // Funktion? + if( pDef->GetProcDef() ) + { + SbiProcDef* pProc = pDef->GetProcDef(); + if( pPar && pProc->GetLib().Len() ) // DECLARE benutzt? + pPar->SetProc( pProc ); + // Wenn keine Pars, vorerst nichts machen + // Pruefung auf Typ-Anzahl waere denkbar + } + // Typcheck bei Variablen: + // ist explizit im Scanner etwas anderes angegeben? + // Bei Methoden ist dies OK! + if( eType != SbxVARIANT && // Variant nimmt alles + eType != pDef->GetType() && + !pDef->GetProcDef() ) + { + // Es kann sein, dass pDef ein Objekt beschreibt, das bisher + // nur als SbxVARIANT erkannt wurde, dann Typ von pDef aendern + // AB, 16.12.95 (Vielleicht noch aehnliche Faelle moeglich ?!?) + if( eType == SbxOBJECT && pDef->GetType() == SbxVARIANT ) + { + pDef->SetType( SbxOBJECT ); + } + else + { + pParser->Error( SbERR_BAD_DECLARATION, aSym ); + bError = TRUE; + } + } + } + SbiExprNode* pNd = new SbiExprNode( pParser, *pDef, eType ); + if( !pPar ) + pPar = new SbiParameters( pParser,FALSE,FALSE ); + pNd->aVar.pPar = pPar; + pNd->aVar.pvMorePar = pvMoreParLcl; + if( bObj ) + { + // AB, 8.1.95: Objekt kann auch vom Typ SbxVARIANT sein + if( pDef->GetType() == SbxVARIANT ) + pDef->SetType( SbxOBJECT ); + // Falls wir etwas mit Punkt einscannen, muss der + // Typ SbxOBJECT sein + if( pDef->GetType() != SbxOBJECT && pDef->GetType() != SbxVARIANT ) + { + pParser->Error( SbERR_BAD_DECLARATION, aSym ); + bError = TRUE; + } + if( !bError ) + pNd->aVar.pNext = ObjTerm( *pDef ); + } + // Merken der Spalte 1 wieder freigeben + pParser->UnlockColumn(); + return pNd; +} + +// Aufbau eines Objekt-Terms. Ein derartiger Term ist Teil +// eines Ausdrucks, der mit einer Objektvariablen beginnt. + +SbiExprNode* SbiExpression::ObjTerm( SbiSymDef& rObj ) +{ + pParser->Next(); + SbiToken eTok = pParser->Next(); + if( eTok != SYMBOL && !pParser->IsKwd( eTok ) && !pParser->IsExtra( eTok ) ) + { + // #66745 Einige Operatoren koennen in diesem Kontext auch + // als Identifier zugelassen werden, wichtig fuer StarOne + if( eTok != MOD && eTok != NOT && eTok != AND && eTok != OR && + eTok != XOR && eTok != EQV && eTok != IMP && eTok != IS ) + { + pParser->Error( SbERR_VAR_EXPECTED ); + bError = TRUE; + } + } + /* #118410 Allow type for Class methods and RTL object, e.g. RTL.Chr$(97) + else + { + if( pParser->GetType() != SbxVARIANT ) + pParser->Error( SbERR_SYNTAX ), bError = TRUE; + } + */ + if( bError ) + return NULL; + + String aSym( pParser->GetSym() ); + SbxDataType eType = pParser->GetType(); + SbiParameters* pPar = NULL; + eTok = pParser->Peek(); + // Parameter? + if( DoParametersFollow( pParser, eCurExpr, eTok ) ) + { + pPar = new SbiParameters( pParser ); + bError |= !pPar->IsValid(); + eTok = pParser->Peek(); + } + BOOL bObj = BOOL( ( eTok == DOT || eTok == EXCLAM ) && !pParser->WhiteSpace() ); + if( bObj ) + { + if( eType == SbxVARIANT ) + eType = SbxOBJECT; + else + { + // Name%. geht wirklich nicht! + pParser->Error( SbERR_BAD_DECLARATION, aSym ); + bError = TRUE; + } + } + + // Der Symbol-Pool eines Objekts ist immer PUBLIC + SbiSymPool& rPool = rObj.GetPool(); + rPool.SetScope( SbPUBLIC ); + SbiSymDef* pDef = rPool.Find( aSym ); + if( !pDef ) + { + pDef = AddSym( eTok, rPool, eCurExpr, aSym, eType, pPar ); + pDef->SetType( eType ); + } + + SbiExprNode* pNd = new SbiExprNode( pParser, *pDef, eType ); + pNd->aVar.pPar = pPar; + if( bObj ) + { + // Falls wir etwas mit Punkt einscannen, muss der + // Typ SbxOBJECT sein + + // AB, 3.1.96 + // Es kann sein, dass pDef ein Objekt beschreibt, das bisher + // nur als SbxVARIANT erkannt wurde, dann Typ von pDef aendern + if( pDef->GetType() == SbxVARIANT ) + pDef->SetType( SbxOBJECT ); + + if( pDef->GetType() != SbxOBJECT ) + { + pParser->Error( SbERR_BAD_DECLARATION, aSym ); + bError = TRUE; + } + if( !bError ) + { + pNd->aVar.pNext = ObjTerm( *pDef ); + pNd->eType = eType; + } + } + return pNd; +} + +// Als Operanden kommen in Betracht: +// Konstante +// skalare Variable +// Strukturelemente +// Array-Elemente +// Funktionen +// geklammerte Ausdruecke + +SbiExprNode* SbiExpression::Operand() +{ + SbiExprNode *pRes; + SbiToken eTok; + + // Operand testen: + switch( eTok = pParser->Peek() ) + { + case SYMBOL: + pRes = Term(); + // process something like "IF Not r Is Nothing Then .." + if( pParser->IsVBASupportOn() && pParser->Peek() == IS ) + { + eTok = pParser->Next(); + pRes = new SbiExprNode( pParser, pRes, eTok, Like() ); + } + break; + case DOT: // .with + pRes = Term(); break; + case NUMBER: + pParser->Next(); + pRes = new SbiExprNode( pParser, pParser->GetDbl(), pParser->GetType() ); + break; + case FIXSTRING: + pParser->Next(); + pRes = new SbiExprNode( pParser, pParser->GetSym() ); break; + case LPAREN: + pParser->Next(); + if( nParenLevel == 0 && m_eMode == EXPRMODE_LPAREN_PENDING && pParser->Peek() == RPAREN ) + { + m_eMode = EXPRMODE_EMPTY_PAREN; + pRes = new SbiExprNode(); // Dummy node + pParser->Next(); + break; + } + nParenLevel++; + pRes = Boolean(); + if( pParser->Peek() != RPAREN ) + { + // If there was a LPARAM, it does not belong to the expression + if( nParenLevel == 1 && m_eMode == EXPRMODE_LPAREN_PENDING ) + m_eMode = EXPRMODE_LPAREN_NOT_NEEDED; + else + pParser->Error( SbERR_BAD_BRACKETS ); + } + else + { + pParser->Next(); + if( nParenLevel == 1 && m_eMode == EXPRMODE_LPAREN_PENDING ) + { + SbiToken eTokAfterRParen = pParser->Peek(); + if( eTokAfterRParen == EQ || eTokAfterRParen == LPAREN || eTokAfterRParen == DOT ) + m_eMode = EXPRMODE_ARRAY_OR_OBJECT; + else + m_eMode = EXPRMODE_STANDARD; + } + } + nParenLevel--; + pRes->bComposite = TRUE; + break; + default: + // Zur Zeit sind Keywords hier OK! + if( pParser->IsKwd( eTok ) ) + pRes = Term(); + else + { + pParser->Next(); + pRes = new SbiExprNode( pParser, 1.0, SbxDOUBLE ); // bei Fehlern + pParser->Error( SbERR_UNEXPECTED, eTok ); + } + } + return pRes; +} + +SbiExprNode* SbiExpression::Unary() +{ + SbiExprNode* pNd; + SbiToken eTok = pParser->Peek(); + switch( eTok ) + { + case MINUS: + eTok = NEG; + case NOT: + pParser->Next(); + pNd = new SbiExprNode( pParser, Unary(), eTok, NULL ); + break; + case PLUS: + pParser->Next(); + pNd = Unary(); + break; + case TYPEOF: + { + pParser->Next(); + SbiExprNode* pObjNode = Operand(); + pParser->TestToken( IS ); + String aDummy; + SbiSymDef* pTypeDef = new SbiSymDef( aDummy ); + pParser->TypeDecl( *pTypeDef, TRUE ); + pNd = new SbiExprNode( pParser, pObjNode, pTypeDef->GetTypeId() ); + break; + } + default: + pNd = Operand(); + } + return pNd; +} + +SbiExprNode* SbiExpression::Exp() +{ + SbiExprNode* pNd = Unary(); + if( m_eMode != EXPRMODE_EMPTY_PAREN ) + { + while( pParser->Peek() == EXPON ) { + SbiToken eTok = pParser->Next(); + pNd = new SbiExprNode( pParser, pNd, eTok, Unary() ); + } + } + return pNd; +} + +SbiExprNode* SbiExpression::MulDiv() +{ + SbiExprNode* pNd = Exp(); + if( m_eMode != EXPRMODE_EMPTY_PAREN ) + { + for( ;; ) + { + SbiToken eTok = pParser->Peek(); + if( eTok != MUL && eTok != DIV ) + break; + eTok = pParser->Next(); + pNd = new SbiExprNode( pParser, pNd, eTok, Exp() ); + } + } + return pNd; +} + +SbiExprNode* SbiExpression::IntDiv() +{ + SbiExprNode* pNd = MulDiv(); + if( m_eMode != EXPRMODE_EMPTY_PAREN ) + { + while( pParser->Peek() == IDIV ) { + SbiToken eTok = pParser->Next(); + pNd = new SbiExprNode( pParser, pNd, eTok, MulDiv() ); + } + } + return pNd; +} + +SbiExprNode* SbiExpression::Mod() +{ + SbiExprNode* pNd = IntDiv(); + if( m_eMode != EXPRMODE_EMPTY_PAREN ) + { + while( pParser->Peek() == MOD ) { + SbiToken eTok = pParser->Next(); + pNd = new SbiExprNode( pParser, pNd, eTok, IntDiv() ); + } + } + return pNd; +} + +SbiExprNode* SbiExpression::AddSub() +{ + SbiExprNode* pNd = Mod(); + if( m_eMode != EXPRMODE_EMPTY_PAREN ) + { + for( ;; ) + { + SbiToken eTok = pParser->Peek(); + if( eTok != PLUS && eTok != MINUS ) + break; + eTok = pParser->Next(); + pNd = new SbiExprNode( pParser, pNd, eTok, Mod() ); + } + } + return pNd; +} + +SbiExprNode* SbiExpression::Cat() +{ + SbiExprNode* pNd = AddSub(); + if( m_eMode != EXPRMODE_EMPTY_PAREN ) + { + for( ;; ) + { + SbiToken eTok = pParser->Peek(); + if( eTok != CAT ) + break; + eTok = pParser->Next(); + pNd = new SbiExprNode( pParser, pNd, eTok, AddSub() ); + } + } + return pNd; +} + +SbiExprNode* SbiExpression::Comp() +{ + SbiExprNode* pNd = Cat(); + if( m_eMode != EXPRMODE_EMPTY_PAREN ) + { + short nCount = 0; + for( ;; ) + { + SbiToken eTok = pParser->Peek(); + if( m_eMode == EXPRMODE_ARRAY_OR_OBJECT ) + break; + if( eTok != EQ && eTok != NE && eTok != LT + && eTok != GT && eTok != LE && eTok != GE ) + break; + eTok = pParser->Next(); + pNd = new SbiExprNode( pParser, pNd, eTok, Cat() ); + nCount++; + } + } + return pNd; +} + +SbiExprNode* SbiExpression::Like() +{ + SbiExprNode* pNd = Comp(); + if( m_eMode != EXPRMODE_EMPTY_PAREN ) + { + short nCount = 0; + while( pParser->Peek() == LIKE ) { + SbiToken eTok = pParser->Next(); + pNd = new SbiExprNode( pParser, pNd, eTok, Comp() ), nCount++; + } + // Mehrere Operatoren hintereinander gehen nicht + if( nCount > 1 ) + { + pParser->Error( SbERR_SYNTAX ); + bError = TRUE; + } + } + return pNd; +} + +SbiExprNode* SbiExpression::Boolean() +{ + SbiExprNode* pNd = Like(); + if( m_eMode != EXPRMODE_EMPTY_PAREN ) + { + for( ;; ) + { + SbiToken eTok = pParser->Peek(); + if( eTok != AND && eTok != OR && eTok != XOR + && eTok != EQV && eTok != IMP && eTok != IS ) + break; + eTok = pParser->Next(); + pNd = new SbiExprNode( pParser, pNd, eTok, Like() ); + } + } + return pNd; +} + +/*************************************************************************** +|* +|* SbiConstExpression +|* +***************************************************************************/ + +// Parsing einer Expression, die sich zu einer numerischen +// Konstanten verarbeiten laesst. + +SbiConstExpression::SbiConstExpression( SbiParser* p ) : SbiExpression( p ) +{ + if( pExpr->IsConstant() ) + { + eType = pExpr->GetType(); + if( pExpr->IsNumber() ) + { + nVal = pExpr->nVal; + } + else + { + nVal = 0; + aVal = pExpr->aStrVal; + } + } + else + { + // #40204 Spezialbehandlung fuer BOOL-Konstanten + BOOL bIsBool = FALSE; + if( pExpr->eNodeType == SbxVARVAL ) + { + SbiSymDef* pVarDef = pExpr->GetVar(); + + // Ist es eine BOOL-Konstante? + BOOL bBoolVal = FALSE; + if( pVarDef->GetName().EqualsIgnoreCaseAscii( "true" ) ) + //if( pVarDef->GetName().ICompare( "true" ) == COMPARE_EQUAL ) + { + bIsBool = TRUE; + bBoolVal = TRUE; + } + else if( pVarDef->GetName().EqualsIgnoreCaseAscii( "false" ) ) + //else if( pVarDef->GetName().ICompare( "false" ) == COMPARE_EQUAL ) + { + bIsBool = TRUE; + bBoolVal = FALSE; + } + + // Wenn es ein BOOL ist, Node austauschen + if( bIsBool ) + { + delete pExpr; + pExpr = new SbiExprNode( pParser, (bBoolVal ? SbxTRUE : SbxFALSE), SbxINTEGER ); + eType = pExpr->GetType(); + nVal = pExpr->nVal; + } + } + + if( !bIsBool ) + { + pParser->Error( SbERR_SYNTAX ); + eType = SbxDOUBLE; + nVal = 0; + } + } +} + +short SbiConstExpression::GetShortValue() +{ + if( eType == SbxSTRING ) + { + SbxVariableRef refConv = new SbxVariable; + refConv->PutString( aVal ); + return refConv->GetInteger(); + } + else + { + double n = nVal; + if( n > 0 ) n += .5; else n -= .5; + if( n > SbxMAXINT ) n = SbxMAXINT, pParser->Error( SbERR_OUT_OF_RANGE ); + else + if( n < SbxMININT ) n = SbxMININT, pParser->Error( SbERR_OUT_OF_RANGE ); + return (short) n; + } +} + + +/*************************************************************************** +|* +|* SbiExprList +|* +***************************************************************************/ + +SbiExprList::SbiExprList( SbiParser* p ) +{ + pParser = p; + pFirst = NULL; + pProc = NULL; + nExpr = + nDim = 0; + bError = + bBracket = FALSE; +} + +SbiExprList::~SbiExprList() +{ + SbiExpression* p = pFirst; + while( p ) + { + SbiExpression* q = p->pNext; + delete p; + p = q; + } +} + +// Parameter anfordern (ab 0) + +SbiExpression* SbiExprList::Get( short n ) +{ + SbiExpression* p = pFirst; + while( n-- && p ) + p = p->pNext; + return p; +} + +void SbiExprList::addExpression( SbiExpression* pExpr ) +{ + SbiExpression* p = pFirst; + while( p && p->pNext ) + p = p->pNext; + + p->pNext = pExpr; +} + + +/*************************************************************************** +|* +|* SbiParameters +|* +***************************************************************************/ + +// Parsender Konstruktor: +// Die Parameterliste wird komplett geparst. +// "Prozedurname()" ist OK. +// Dann handelt es sich um eine Funktion ohne Parameter +// respektive um die Angabe eines Arrays als Prozedurparameter. + +// #i79918/#i80532: bConst has never been set to true +// -> reused as bStandaloneExpression +//SbiParameters::SbiParameters( SbiParser* p, BOOL bConst, BOOL bPar) : +SbiParameters::SbiParameters( SbiParser* p, BOOL bStandaloneExpression, BOOL bPar) : + SbiExprList( p ) +{ + if( !bPar ) + return; + + SbiExpression *pExpr; + SbiToken eTok = pParser->Peek(); + + // evtl. Klammer auf weg: + bool bAssumeExprLParenMode = false; + bool bAssumeArrayMode = false; + if( eTok == LPAREN ) + { + if( bStandaloneExpression ) + { + bAssumeExprLParenMode = true; + } + else + { + bBracket = TRUE; + pParser->Next(); + eTok = pParser->Peek(); + } + } + + // Ende-Test + if( ( bBracket && eTok == RPAREN ) || pParser->IsEoln( eTok ) ) + { + if( eTok == RPAREN ) + pParser->Next(); + return; + } + // Parametertabelle einlesen und in richtiger Folge ablegen! + SbiExpression* pLast = NULL; + String aName; + while( !bError ) + { + aName.Erase(); + // Fehlendes Argument + if( eTok == COMMA ) + { + pExpr = new SbiExpression( pParser, 0, SbxEMPTY ); + //if( bConst ) + // pParser->Error( SbERR_SYNTAX ), bError = TRUE; + } + // Benannte Argumente: entweder .name= oder name:= + else + { + if( bAssumeExprLParenMode ) + { + pExpr = new SbiExpression( pParser, SbSTDEXPR, EXPRMODE_LPAREN_PENDING ); + bAssumeExprLParenMode = FALSE; + + SbiExprMode eModeAfter = pExpr->m_eMode; + if( eModeAfter == EXPRMODE_LPAREN_NOT_NEEDED ) + { + bBracket = TRUE; + } + else if( eModeAfter == EXPRMODE_ARRAY_OR_OBJECT ) + { + // Expression "looks" like an array assignment + // a(...)[(...)] = ? or a(...).b(...) + // RPAREN is already parsed + bBracket = TRUE; + bAssumeArrayMode = true; + eTok = NIL; + } + else if( eModeAfter == EXPRMODE_EMPTY_PAREN ) + { + bBracket = TRUE; + delete pExpr; + return; + } + } + else + pExpr = new SbiExpression( pParser ); + + //pExpr = bConst ? new SbiConstExpression( pParser ) + // : new SbiExpression( pParser ); + if( !bAssumeArrayMode ) + { + if( pParser->Peek() == ASSIGN ) + { + // VBA mode: name:= + // SbiExpression::Term() hat einen String daraus gemacht + aName = pExpr->GetString(); + delete pExpr; + pParser->Next(); + pExpr = new SbiExpression( pParser ); + //if( bConst ) + // pParser->Error( SbERR_SYNTAX ), bError = TRUE; + } + pExpr->GetName() = aName; + } + } + pExpr->pNext = NULL; + if( !pLast ) + pFirst = pLast = pExpr; + else + pLast->pNext = pExpr, pLast = pExpr; + nExpr++; + bError |= !pExpr->IsValid(); + + if( bAssumeArrayMode ) + break; + + // Naechstes Element? + eTok = pParser->Peek(); + if( eTok != COMMA ) + { + if( ( bBracket && eTok == RPAREN ) || pParser->IsEoln( eTok ) ) + break; + pParser->Error( bBracket + ? SbERR_BAD_BRACKETS + : SbERR_EXPECTED, COMMA ); + bError = TRUE; + } + else + { + pParser->Next(); + eTok = pParser->Peek(); + if( ( bBracket && eTok == RPAREN ) || pParser->IsEoln( eTok ) ) + break; + } + } + // Schliessende Klammer + if( eTok == RPAREN ) + { + pParser->Next(); + pParser->Peek(); + if( !bBracket ) + { + pParser->Error( SbERR_BAD_BRACKETS ); + bError = TRUE; + } + } + nDim = nExpr; +} + +/*************************************************************************** +|* +|* SbiDimList +|* +***************************************************************************/ + +// Parsender Konstruktor: +// Eine Liste von Array-Dimensionen wird geparst. Die Ausdruecke werden +// auf numerisch getestet. Das bCONST-Bit wird gesetzt, wenn alle Ausdruecke +// Integer-Konstanten sind. + +SbiDimList::SbiDimList( SbiParser* p ) : SbiExprList( p ) +{ + bConst = TRUE; + + if( pParser->Next() != LPAREN ) + { + pParser->Error( SbERR_EXPECTED, LPAREN ); + bError = TRUE; return; + } + + if( pParser->Peek() != RPAREN ) + { + SbiExpression *pExpr1, *pExpr2, *pLast = NULL; + SbiToken eTok; + for( ;; ) + { + pExpr1 = new SbiExpression( pParser ); + eTok = pParser->Next(); + if( eTok == TO ) + { + pExpr2 = new SbiExpression( pParser ); + eTok = pParser->Next(); + bConst &= pExpr1->IsIntConstant() & pExpr2->IsIntConstant(); + bError |= !pExpr1->IsValid(); + bError |= !pExpr2->IsValid(); + pExpr1->pNext = pExpr2; + if( !pLast ) + pFirst = pExpr1; + else + pLast->pNext = pExpr1; + pLast = pExpr2; + nExpr += 2; + } + else + { + // Nur eine Dim-Angabe + pExpr1->SetBased(); + pExpr1->pNext = NULL; + bConst &= pExpr1->IsIntConstant(); + bError |= !pExpr1->IsValid(); + if( !pLast ) + pFirst = pLast = pExpr1; + else + pLast->pNext = pExpr1, pLast = pExpr1; + nExpr++; + } + nDim++; + if( eTok == RPAREN ) break; + if( eTok != COMMA ) + { + pParser->Error( SbERR_BAD_BRACKETS ); + pParser->Next(); + break; + } + } + } + else pParser->Next(); +} + diff --git a/basic/source/comp/io.cxx b/basic/source/comp/io.cxx new file mode 100644 index 000000000000..1ed551994c92 --- /dev/null +++ b/basic/source/comp/io.cxx @@ -0,0 +1,321 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_basic.hxx" + + +#include <tools/stream.hxx> +#include "sbcomp.hxx" +#include "iosys.hxx" + +// Test, ob ein I/O-Channel angegeben wurde + +BOOL SbiParser::Channel( BOOL bAlways ) +{ + BOOL bRes = FALSE; + Peek(); + if( IsHash() ) + { + SbiExpression aExpr( this ); + while( Peek() == COMMA || Peek() == SEMICOLON ) + Next(); + aExpr.Gen(); + aGen.Gen( _CHANNEL ); + bRes = TRUE; + } + else if( bAlways ) + Error( SbERR_EXPECTED, "#" ); + return bRes; +} + +// Fuer PRINT und WRITE wird bei Objektvariablen versucht, +// die Default-Property anzusprechen. + +// PRINT + +void SbiParser::Print() +{ + BOOL bChan = Channel(); + // Die Ausdruecke zum Drucken: + while( !bAbort ) + { + if( !IsEoln( Peek() ) ) + { + SbiExpression* pExpr = new SbiExpression( this ); + pExpr->Gen(); + delete pExpr; + Peek(); + aGen.Gen( eCurTok == COMMA ? _PRINTF : _BPRINT ); + } + if( eCurTok == COMMA || eCurTok == SEMICOLON ) + { + Next(); + if( IsEoln( Peek() ) ) break; + } + else + { + aGen.Gen( _PRCHAR, '\n' ); + break; + } + } + if( bChan ) + aGen.Gen( _CHAN0 ); +} + +// WRITE #chan, expr, ... + +void SbiParser::Write() +{ + BOOL bChan = Channel(); + // Die Ausdruecke zum Drucken: + while( !bAbort ) + { + SbiExpression* pExpr = new SbiExpression( this ); + pExpr->Gen(); + delete pExpr; + aGen.Gen( _BWRITE ); + if( Peek() == COMMA ) + { + aGen.Gen( _PRCHAR, ',' ); + Next(); + if( IsEoln( Peek() ) ) break; + } + else + { + aGen.Gen( _PRCHAR, '\n' ); + break; + } + } + if( bChan ) + aGen.Gen( _CHAN0 ); +} + +// LINE INPUT [prompt], var$ + +void SbiParser::LineInput() +{ + Channel( TRUE ); + // BOOL bChan = Channel( TRUE ); + SbiExpression* pExpr = new SbiExpression( this, SbOPERAND ); + /* AB 15.1.96: Keinen allgemeinen Ausdruck mehr zulassen + SbiExpression* pExpr = new SbiExpression( this ); + if( !pExpr->IsVariable() ) + { + SbiToken eTok = Peek(); + if( eTok == COMMA || eTok == SEMICOLON ) Next(); + else Error( SbERR_EXPECTED, COMMA ); + // mit Prompt + if( !bChan ) + { + pExpr->Gen(); + aGen.Gen( _PROMPT ); + } + else + Error( SbERR_VAR_EXPECTED ); + delete pExpr; + pExpr = new SbiExpression( this, SbOPERAND ); + } + */ + if( !pExpr->IsVariable() ) + Error( SbERR_VAR_EXPECTED ); + if( pExpr->GetType() != SbxVARIANT && pExpr->GetType() != SbxSTRING ) + Error( SbERR_CONVERSION ); + pExpr->Gen(); + aGen.Gen( _LINPUT ); + delete pExpr; + aGen.Gen( _CHAN0 ); // ResetChannel() nicht mehr in StepLINPUT() +} + +// INPUT + +void SbiParser::Input() +{ + aGen.Gen( _RESTART ); + Channel( TRUE ); + // BOOL bChan = Channel( TRUE ); + SbiExpression* pExpr = new SbiExpression( this, SbOPERAND ); + /* ALT: Jetzt keinen allgemeinen Ausdruck mehr zulassen + SbiExpression* pExpr = new SbiExpression( this ); + ... + siehe LineInput + */ + while( !bAbort ) + { + if( !pExpr->IsVariable() ) + Error( SbERR_VAR_EXPECTED ); + pExpr->Gen(); + aGen.Gen( _INPUT ); + if( Peek() == COMMA ) + { + Next(); + delete pExpr; + pExpr = new SbiExpression( this, SbOPERAND ); + } + else break; + } + delete pExpr; + aGen.Gen( _CHAN0 ); // ResetChannel() nicht mehr in StepINPUT() +} + +// OPEN stringexpr FOR mode ACCCESS access mode AS Channel [Len=n] + +void SbiParser::Open() +{ + SbiExpression aFileName( this ); + SbiToken eTok; + TestToken( FOR ); + short nMode = 0; + short nFlags = 0; + switch( Next() ) + { + case INPUT: + nMode = STREAM_READ; nFlags |= SBSTRM_INPUT; break; + case OUTPUT: + nMode = STREAM_WRITE | STREAM_TRUNC; nFlags |= SBSTRM_OUTPUT; break; + case APPEND: + nMode = STREAM_WRITE; nFlags |= SBSTRM_APPEND; break; + case RANDOM: + nMode = STREAM_READ | STREAM_WRITE; nFlags |= SBSTRM_RANDOM; break; + case BINARY: + nMode = STREAM_READ | STREAM_WRITE; nFlags |= SBSTRM_BINARY; break; + default: + Error( SbERR_SYNTAX ); + } + if( Peek() == ACCESS ) + { + Next(); + eTok = Next(); + // #27964# Nur STREAM_READ,STREAM_WRITE-Flags in nMode beeinflussen + nMode &= ~(STREAM_READ | STREAM_WRITE); // loeschen + if( eTok == READ ) + { + if( Peek() == WRITE ) + { + Next(); + nMode |= (STREAM_READ | STREAM_WRITE); + } + else + nMode |= STREAM_READ; + } + else if( eTok == WRITE ) + nMode |= STREAM_WRITE; + else + Error( SbERR_SYNTAX ); + } + switch( Peek() ) + { +#ifdef SHARED +#undef SHARED +#define tmpSHARED +#endif + case SHARED: + Next(); nMode |= STREAM_SHARE_DENYNONE; break; +#ifdef tmpSHARED +#define SHARED +#undef tmpSHARED +#endif + case LOCK: + Next(); + eTok = Next(); + if( eTok == READ ) + { + if( Peek() == WRITE ) Next(), nMode |= STREAM_SHARE_DENYALL; + else nMode |= STREAM_SHARE_DENYREAD; + } + else if( eTok == WRITE ) + nMode |= STREAM_SHARE_DENYWRITE; + else + Error( SbERR_SYNTAX ); + break; + default: break; + } + TestToken( AS ); + // Die Kanalnummer + SbiExpression* pChan = new SbiExpression( this ); + if( !pChan ) + Error( SbERR_SYNTAX ); + SbiExpression* pLen = NULL; + if( Peek() == SYMBOL ) + { + Next(); + String aLen( aSym ); + if( aLen.EqualsIgnoreCaseAscii( "LEN" ) ) + { + TestToken( EQ ); + pLen = new SbiExpression( this ); + } + } + if( !pLen ) pLen = new SbiExpression( this, 128, SbxINTEGER ); + // Der Stack fuer den OPEN-Befehl sieht wie folgt aus: + // Blocklaenge + // Kanalnummer + // Dateiname + pLen->Gen(); + if( pChan ) + pChan->Gen(); + aFileName.Gen(); + aGen.Gen( _OPEN, nMode, nFlags ); + delete pLen; + delete pChan; +} + +// NAME file AS file + +void SbiParser::Name() +{ + SbiExpression aExpr1( this ); + TestToken( AS ); + SbiExpression aExpr2( this ); + aExpr1.Gen(); + aExpr2.Gen(); + aGen.Gen( _RENAME ); +} + +// CLOSE [n,...] + +void SbiParser::Close() +{ + Peek(); + if( IsEoln( eCurTok ) ) + aGen.Gen( _CLOSE, 0 ); + else + for( ;; ) + { + SbiExpression aExpr( this ); + while( Peek() == COMMA || Peek() == SEMICOLON ) + Next(); + aExpr.Gen(); + aGen.Gen( _CHANNEL ); + aGen.Gen( _CLOSE, 1 ); + + if( IsEoln( Peek() ) ) + break; + } +} + + diff --git a/basic/source/comp/loops.cxx b/basic/source/comp/loops.cxx new file mode 100644 index 000000000000..bd4540a8ffde --- /dev/null +++ b/basic/source/comp/loops.cxx @@ -0,0 +1,558 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_basic.hxx" + +#include "sbcomp.hxx" + +// Single-line IF und Multiline IF + +void SbiParser::If() +{ + UINT32 nEndLbl; + SbiToken eTok = NIL; + // Ende-Tokens ignorieren: + SbiExpression aCond( this ); + aCond.Gen(); + TestToken( THEN ); + if( IsEoln( Next() ) ) + { + // AB 13.5.1996: #27720# Am Ende jeden Blocks muss ein Jump zu ENDIF + // eingefuegt werden, damit bei ELSEIF nicht erneut die Bedingung + // ausgewertet wird. Die Tabelle nimmt alle Absprungstellen auf. +#define JMP_TABLE_SIZE 100 + UINT32 pnJmpToEndLbl[JMP_TABLE_SIZE]; // 100 ELSEIFs zulaessig + USHORT iJmp = 0; // aktueller Tabellen-Index + + // multiline IF + nEndLbl = aGen.Gen( _JUMPF, 0 ); + eTok = Peek(); + while( !( eTok == ELSEIF || eTok == ELSE || eTok == ENDIF ) && + !bAbort && Parse() ) + { + eTok = Peek(); + if( IsEof() ) + { + Error( SbERR_BAD_BLOCK, IF ); bAbort = TRUE; return; + } + } + // ELSEIF? + while( eTok == ELSEIF ) + { + // #27720# Bei erfolgreichem IF/ELSEIF auf ENDIF springen + if( iJmp >= JMP_TABLE_SIZE ) + { + Error( SbERR_PROG_TOO_LARGE ); bAbort = TRUE; return; + } + pnJmpToEndLbl[iJmp++] = aGen.Gen( _JUMP, 0 ); + + Next(); + aGen.BackChain( nEndLbl ); + + aGen.Statement(); + SbiExpression* pCond = new SbiExpression( this ); + pCond->Gen(); + nEndLbl = aGen.Gen( _JUMPF, 0 ); + delete pCond; + TestToken( THEN ); + eTok = Peek(); + while( !( eTok == ELSEIF || eTok == ELSE || eTok == ENDIF ) && + !bAbort && Parse() ) + { + eTok = Peek(); + if( IsEof() ) + { + Error( SbERR_BAD_BLOCK, ELSEIF ); bAbort = TRUE; return; + } + } + } + if( eTok == ELSE ) + { + Next(); + UINT32 nElseLbl = nEndLbl; + nEndLbl = aGen.Gen( _JUMP, 0 ); + aGen.BackChain( nElseLbl ); + + aGen.Statement(); + StmntBlock( ENDIF ); + } + else if( eTok == ENDIF ) + Next(); + + // #27720# Jmp-Tabelle abarbeiten + while( iJmp > 0 ) + { + iJmp--; + aGen.BackChain( pnJmpToEndLbl[iJmp] ); + } + } + else + { + // single line IF + bSingleLineIf = TRUE; + nEndLbl = aGen.Gen( _JUMPF, 0 ); + Push( eCurTok ); + while( !bAbort ) + { + if( !Parse() ) break; + eTok = Peek(); + if( eTok == ELSE || eTok == EOLN || eTok == REM ) + break; + } + if( eTok == ELSE ) + { + Next(); + UINT32 nElseLbl = nEndLbl; + nEndLbl = aGen.Gen( _JUMP, 0 ); + aGen.BackChain( nElseLbl ); + while( !bAbort ) + { + if( !Parse() ) break; + eTok = Peek(); + if( eTok == EOLN ) + break; + } + } + bSingleLineIf = FALSE; + } + aGen.BackChain( nEndLbl ); +} + +// ELSE/ELSEIF/ENDIF ohne IF + +void SbiParser::NoIf() +{ + Error( SbERR_NO_IF ); + StmntBlock( ENDIF ); +} + +// DO WHILE...LOOP +// DO ... LOOP WHILE + +void SbiParser::DoLoop() +{ + UINT32 nStartLbl = aGen.GetPC(); + OpenBlock( DO ); + SbiToken eTok = Next(); + if( IsEoln( eTok ) ) + { + // DO ... LOOP [WHILE|UNTIL expr] + StmntBlock( LOOP ); + eTok = Next(); + if( eTok == UNTIL || eTok == WHILE ) + { + SbiExpression aExpr( this ); + aExpr.Gen(); + aGen.Gen( eTok == UNTIL ? _JUMPF : _JUMPT, nStartLbl ); + } else + if (eTok == EOLN || eTok == REM) + aGen.Gen (_JUMP, nStartLbl); + else + Error( SbERR_EXPECTED, WHILE ); + } + else + { + // DO [WHILE|UNTIL expr] ... LOOP + if( eTok == UNTIL || eTok == WHILE ) + { + SbiExpression aCond( this ); + aCond.Gen(); + } + UINT32 nEndLbl = aGen.Gen( eTok == UNTIL ? _JUMPT : _JUMPF, 0 ); + StmntBlock( LOOP ); + TestEoln(); + aGen.Gen( _JUMP, nStartLbl ); + aGen.BackChain( nEndLbl ); + } + CloseBlock(); +} + +// WHILE ... WEND + +void SbiParser::While() +{ + SbiExpression aCond( this ); + UINT32 nStartLbl = aGen.GetPC(); + aCond.Gen(); + UINT32 nEndLbl = aGen.Gen( _JUMPF, 0 ); + StmntBlock( WEND ); + aGen.Gen( _JUMP, nStartLbl ); + aGen.BackChain( nEndLbl ); +} + +// FOR var = expr TO expr STEP + +void SbiParser::For() +{ + bool bForEach = ( Peek() == EACH ); + if( bForEach ) + Next(); + SbiExpression aLvalue( this, SbOPERAND ); + aLvalue.Gen(); // Variable auf dem Stack + + if( bForEach ) + { + TestToken( _IN_ ); + SbiExpression aCollExpr( this, SbOPERAND ); + aCollExpr.Gen(); // Colletion var to for stack + TestEoln(); + aGen.Gen( _INITFOREACH ); + } + else + { + TestToken( EQ ); + SbiExpression aStartExpr( this ); + aStartExpr.Gen(); // Startausdruck auf dem Stack + TestToken( TO ); + SbiExpression aStopExpr( this ); + aStopExpr.Gen(); // Endausdruck auf dem Stack + if( Peek() == STEP ) + { + Next(); + SbiExpression aStepExpr( this ); + aStepExpr.Gen(); + } + else + { + SbiExpression aOne( this, 1, SbxINTEGER ); + aOne.Gen(); + } + TestEoln(); + // Der Stack hat jetzt 4 Elemente: Variable, Start, Ende, Inkrement + // Startwert binden + aGen.Gen( _INITFOR ); + } + + UINT32 nLoop = aGen.GetPC(); + // Test durchfuehren, evtl. Stack freigeben + UINT32 nEndTarget = aGen.Gen( _TESTFOR, 0 ); + OpenBlock( FOR ); + StmntBlock( NEXT ); + aGen.Gen( _NEXT ); + aGen.Gen( _JUMP, nLoop ); + // Kommen Variable nach NEXT? + if( Peek() == SYMBOL ) + { + SbiExpression aVar( this, SbOPERAND ); + if( aVar.GetRealVar() != aLvalue.GetRealVar() ) + Error( SbERR_EXPECTED, aLvalue.GetRealVar()->GetName() ); + } + aGen.BackChain( nEndTarget ); + CloseBlock(); +} + +// WITH .. END WITH + +void SbiParser::With() +{ + SbiExpression aVar( this, SbOPERAND ); + + // Letzten Knoten in der Objekt-Kette ueberpruefen + SbiExprNode *pNode = aVar.GetExprNode()->GetRealNode(); + SbiSymDef* pDef = pNode->GetVar(); + // Variant, AB 27.6.1997, #41090: bzw. empty -> muß Object sein + if( pDef->GetType() == SbxVARIANT || pDef->GetType() == SbxEMPTY ) + pDef->SetType( SbxOBJECT ); + else if( pDef->GetType() != SbxOBJECT ) + Error( SbERR_NEEDS_OBJECT ); + + // Knoten auch auf SbxOBJECT setzen, damit spaeter Gen() klappt + pNode->SetType( SbxOBJECT ); + + OpenBlock( NIL, aVar.GetExprNode() ); + StmntBlock( ENDWITH ); + CloseBlock(); +} + +// LOOP/NEXT/WEND ohne Konstrukt + +void SbiParser::BadBlock() +{ + if( eEndTok ) + Error( SbERR_BAD_BLOCK, eEndTok ); + else + Error( SbERR_BAD_BLOCK, "Loop/Next/Wend" ); +} + +// On expr Goto/Gosub n,n,n... + +void SbiParser::OnGoto() +{ + SbiExpression aCond( this ); + aCond.Gen(); + UINT32 nLabelsTarget = aGen.Gen( _ONJUMP, 0 ); + SbiToken eTok = Next(); + if( eTok != GOTO && eTok != GOSUB ) + { + Error( SbERR_EXPECTED, "GoTo/GoSub" ); + eTok = GOTO; + } + // Label-Tabelle einlesen: + UINT32 nLbl = 0; + do + { + SbiToken eTok2 = NIL; + eTok2 = Next(); // Label holen + if( MayBeLabel() ) + { + UINT32 nOff = pProc->GetLabels().Reference( aSym ); + aGen.Gen( _JUMP, nOff ); + nLbl++; + } + else Error( SbERR_LABEL_EXPECTED ); + } + while( !bAbort && TestComma() ); + if( eTok == GOSUB ) + nLbl |= 0x8000; + aGen.Patch( nLabelsTarget, nLbl ); +} + +// GOTO/GOSUB + +void SbiParser::Goto() +{ + SbiOpcode eOp = eCurTok == GOTO ? _JUMP : _GOSUB; + Next(); + if( MayBeLabel() ) + { + UINT32 nOff = pProc->GetLabels().Reference( aSym ); + aGen.Gen( eOp, nOff ); + } + else Error( SbERR_LABEL_EXPECTED ); +} + +// RETURN [label] + +void SbiParser::Return() +{ + Next(); + if( MayBeLabel() ) + { + UINT32 nOff = pProc->GetLabels().Reference( aSym ); + aGen.Gen( _RETURN, nOff ); + } + else aGen.Gen( _RETURN, 0 ); +} + +// SELECT CASE + +void SbiParser::Select() +{ + TestToken( CASE ); + SbiExpression aCase( this ); + SbiToken eTok = NIL; + aCase.Gen(); + aGen.Gen( _CASE ); + TestEoln(); + UINT32 nNextTarget = 0; + UINT32 nDoneTarget = 0; + BOOL bElse = FALSE; + // Die Cases einlesen: + while( !bAbort ) + { + eTok = Next(); + if( eTok == CASE ) + { + if( nNextTarget ) + aGen.BackChain( nNextTarget ), nNextTarget = 0; + aGen.Statement(); + // Jeden Case einlesen + BOOL bDone = FALSE; + UINT32 nTrueTarget = 0; + if( Peek() == ELSE ) + { + // CASE ELSE + Next(); + bElse = TRUE; + } + else while( !bDone ) + { + if( bElse ) + Error( SbERR_SYNTAX ); + SbiToken eTok2 = Peek(); + if( eTok2 == IS || ( eTok2 >= EQ && eTok2 <= GE ) ) + { // CASE [IS] operator expr + if( eTok2 == IS ) + Next(); + eTok2 = Peek(); + if( eTok2 < EQ || eTok2 > GE ) + Error( SbERR_SYNTAX ); + else Next(); + SbiExpression aCompare( this ); + aCompare.Gen(); + nTrueTarget = aGen.Gen( + _CASEIS, nTrueTarget, + sal::static_int_cast< UINT16 >( + SbxEQ + ( eTok2 - EQ ) ) ); + } + else + { // CASE expr | expr TO expr + SbiExpression aCase1( this ); + aCase1.Gen(); + if( Peek() == TO ) + { + // CASE a TO b + Next(); + SbiExpression aCase2( this ); + aCase2.Gen(); + nTrueTarget = aGen.Gen( _CASETO, nTrueTarget ); + } + else + // CASE a + nTrueTarget = aGen.Gen( _CASEIS, nTrueTarget, SbxEQ ); + + } + if( Peek() == COMMA ) Next(); + else TestEoln(), bDone = TRUE; + } + // Alle Cases abgearbeitet + if( !bElse ) + { + nNextTarget = aGen.Gen( _JUMP, nNextTarget ); + aGen.BackChain( nTrueTarget ); + } + // den Statement-Rumpf bauen + while( !bAbort ) + { + eTok = Peek(); + if( eTok == CASE || eTok == ENDSELECT ) + break; + if( !Parse() ) goto done; + eTok = Peek(); + if( eTok == CASE || eTok == ENDSELECT ) + break; + } + if( !bElse ) + nDoneTarget = aGen.Gen( _JUMP, nDoneTarget ); + } + else if( !IsEoln( eTok ) ) + break; + } +done: + if( eTok != ENDSELECT ) + Error( SbERR_EXPECTED, ENDSELECT ); + if( nNextTarget ) + aGen.BackChain( nNextTarget ); + aGen.BackChain( nDoneTarget ); + aGen.Gen( _ENDCASE ); +} + +// ON Error/Variable + +#ifdef _MSC_VER +#pragma optimize("",off) +#endif + +void SbiParser::On() +{ + SbiToken eTok = Peek(); + String aString = SbiTokenizer::Symbol(eTok); + if (aString.EqualsIgnoreCaseAscii("ERROR")) + //if (!aString.ICompare("ERROR")) + eTok = _ERROR_; // Error kommt als SYMBOL + if( eTok != _ERROR_ && eTok != LOCAL ) OnGoto(); + else + { + if( eTok == LOCAL ) Next(); + Next (); // Kein TestToken mehr, da es sonst einen Fehler gibt + + Next(); // Token nach Error holen + if( eCurTok == GOTO ) + { + // ON ERROR GOTO label|0 + Next(); + bool bError_ = false; + if( MayBeLabel() ) + { + if( eCurTok == NUMBER && !nVal ) + aGen.Gen( _STDERROR ); + else + { + UINT32 nOff = pProc->GetLabels().Reference( aSym ); + aGen.Gen( _ERRHDL, nOff ); + } + } + else if( eCurTok == MINUS ) + { + Next(); + if( eCurTok == NUMBER && nVal == 1 ) + aGen.Gen( _STDERROR ); + else + bError_ = true; + } + if( bError_ ) + Error( SbERR_LABEL_EXPECTED ); + } + else if( eCurTok == RESUME ) + { + TestToken( NEXT ); + aGen.Gen( _NOERROR ); + } + else Error( SbERR_EXPECTED, "GoTo/Resume" ); + } +} + +#ifdef _MSC_VER +#pragma optimize("",off) +#endif + +// RESUME [0]|NEXT|label + +void SbiParser::Resume() +{ + UINT32 nLbl; + + switch( Next() ) + { + case EOS: + case EOLN: + aGen.Gen( _RESUME, 0 ); + break; + case NEXT: + aGen.Gen( _RESUME, 1 ); + Next(); + break; + case NUMBER: + if( !nVal ) + { + aGen.Gen( _RESUME, 0 ); + break; + } // fall thru + case SYMBOL: + if( MayBeLabel() ) + { + nLbl = pProc->GetLabels().Reference( aSym ); + aGen.Gen( _RESUME, nLbl ); + Next(); + break; + } // fall thru + default: + Error( SbERR_LABEL_EXPECTED ); + } +} + diff --git a/basic/source/comp/makefile.mk b/basic/source/comp/makefile.mk new file mode 100644 index 000000000000..bd3c750df355 --- /dev/null +++ b/basic/source/comp/makefile.mk @@ -0,0 +1,73 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2000, 2010 Oracle and/or its affiliates. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +PRJ=..$/.. + +PRJNAME=basic +TARGET=comp + +# --- Settings ------------------------------------------------------------ + +.INCLUDE : settings.mk + +CXXFILES= \ + sbcomp.cxx \ + dim.cxx \ + exprtree.cxx \ + exprnode.cxx \ + exprgen.cxx \ + codegen.cxx \ + io.cxx \ + loops.cxx \ + parser.cxx \ + scanner.cxx \ + token.cxx \ + symtbl.cxx \ + buffer.cxx + +SLOFILES= \ + $(SLO)$/sbcomp.obj \ + $(SLO)$/dim.obj \ + $(SLO)$/exprtree.obj \ + $(SLO)$/exprnode.obj \ + $(SLO)$/exprgen.obj \ + $(SLO)$/codegen.obj \ + $(SLO)$/io.obj \ + $(SLO)$/loops.obj \ + $(SLO)$/parser.obj \ + $(SLO)$/scanner.obj \ + $(SLO)$/token.obj \ + $(SLO)$/symtbl.obj \ + $(SLO)$/buffer.obj + +EXCEPTIONSFILES= \ + $(SLO)$/parser.obj \ + $(SLO)$/exprtree.obj + +# --- Targets -------------------------------------------------------------- + +.INCLUDE : target.mk diff --git a/basic/source/comp/parser.cxx b/basic/source/comp/parser.cxx new file mode 100644 index 000000000000..400e77a94b16 --- /dev/null +++ b/basic/source/comp/parser.cxx @@ -0,0 +1,816 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_basic.hxx" +#include <basic/sbx.hxx> +#include "sbcomp.hxx" + +struct SbiParseStack { // "Stack" fuer Statement-Blocks + SbiParseStack* pNext; // Chain + SbiExprNode* pWithVar; // Variable fuer WITH + SbiToken eExitTok; // Exit-Token + UINT32 nChain; // JUMP-Chain +}; + +struct SbiStatement { + SbiToken eTok; + void( SbiParser::*Func )(); // Verarbeitungsroutine + BOOL bMain; // TRUE: ausserhalb SUBs OK + BOOL bSubr; // TRUE: in SUBs OK +}; + +#define Y TRUE +#define N FALSE + +static SbiStatement StmntTable [] = { +{ CALL, &SbiParser::Call, N, Y, }, // CALL +{ CLOSE, &SbiParser::Close, N, Y, }, // CLOSE +{ _CONST_, &SbiParser::Dim, Y, Y, }, // CONST +{ DECLARE, &SbiParser::Declare, Y, N, }, // DECLARE +{ DEFBOOL, &SbiParser::DefXXX, Y, N, }, // DEFBOOL +{ DEFCUR, &SbiParser::DefXXX, Y, N, }, // DEFCUR +{ DEFDATE, &SbiParser::DefXXX, Y, N, }, // DEFDATE +{ DEFDBL, &SbiParser::DefXXX, Y, N, }, // DEFDBL +{ DEFERR, &SbiParser::DefXXX, Y, N, }, // DEFERR +{ DEFINT, &SbiParser::DefXXX, Y, N, }, // DEFINT +{ DEFLNG, &SbiParser::DefXXX, Y, N, }, // DEFLNG +{ DEFOBJ, &SbiParser::DefXXX, Y, N, }, // DEFOBJ +{ DEFSNG, &SbiParser::DefXXX, Y, N, }, // DEFSNG +{ DEFSTR, &SbiParser::DefXXX, Y, N, }, // DEFSTR +{ DEFVAR, &SbiParser::DefXXX, Y, N, }, // DEFVAR +{ DIM, &SbiParser::Dim, Y, Y, }, // DIM +{ DO, &SbiParser::DoLoop, N, Y, }, // DO +{ ELSE, &SbiParser::NoIf, N, Y, }, // ELSE +{ ELSEIF, &SbiParser::NoIf, N, Y, }, // ELSEIF +{ ENDIF, &SbiParser::NoIf, N, Y, }, // ENDIF +{ END, &SbiParser::Stop, N, Y, }, // END +{ ENUM, &SbiParser::Enum, Y, N, }, // TYPE +{ ERASE, &SbiParser::Erase, N, Y, }, // ERASE +{ _ERROR_, &SbiParser::ErrorStmnt, N, Y, }, // ERROR +{ EXIT, &SbiParser::Exit, N, Y, }, // EXIT +{ FOR, &SbiParser::For, N, Y, }, // FOR +{ FUNCTION, &SbiParser::SubFunc, Y, N, }, // FUNCTION +{ GOSUB, &SbiParser::Goto, N, Y, }, // GOSUB +{ GLOBAL, &SbiParser::Dim, Y, N, }, // GLOBAL +{ GOTO, &SbiParser::Goto, N, Y, }, // GOTO +{ IF, &SbiParser::If, N, Y, }, // IF +{ IMPLEMENTS, &SbiParser::Implements, Y, N, }, // IMPLEMENTS +{ INPUT, &SbiParser::Input, N, Y, }, // INPUT +{ LET, &SbiParser::Assign, N, Y, }, // LET +{ LINEINPUT,&SbiParser::LineInput, N, Y, }, // LINE INPUT +{ LOOP, &SbiParser::BadBlock, N, Y, }, // LOOP +{ LSET, &SbiParser::LSet, N, Y, }, // LSET +{ NAME, &SbiParser::Name, N, Y, }, // NAME +{ NEXT, &SbiParser::BadBlock, N, Y, }, // NEXT +{ ON, &SbiParser::On, N, Y, }, // ON +{ OPEN, &SbiParser::Open, N, Y, }, // OPEN +{ OPTION, &SbiParser::Option, Y, N, }, // OPTION +{ PRINT, &SbiParser::Print, N, Y, }, // PRINT +{ PRIVATE, &SbiParser::Dim, Y, N, }, // PRIVATE +{ PROPERTY, &SbiParser::SubFunc, Y, N, }, // FUNCTION +{ PUBLIC, &SbiParser::Dim, Y, N, }, // PUBLIC +{ REDIM, &SbiParser::ReDim, N, Y, }, // DIM +{ RESUME, &SbiParser::Resume, N, Y, }, // RESUME +{ RETURN, &SbiParser::Return, N, Y, }, // RETURN +{ RSET, &SbiParser::RSet, N, Y, }, // RSET +{ SELECT, &SbiParser::Select, N, Y, }, // SELECT +{ SET, &SbiParser::Set, N, Y, }, // SET +{ STATIC, &SbiParser::Static, Y, Y, }, // STATIC +{ STOP, &SbiParser::Stop, N, Y, }, // STOP +{ SUB, &SbiParser::SubFunc, Y, N, }, // SUB +{ TYPE, &SbiParser::Type, Y, N, }, // TYPE +{ UNTIL, &SbiParser::BadBlock, N, Y, }, // UNTIL +{ WHILE, &SbiParser::While, N, Y, }, // WHILE +{ WEND, &SbiParser::BadBlock, N, Y, }, // WEND +{ WITH, &SbiParser::With, N, Y, }, // WITH +{ WRITE, &SbiParser::Write, N, Y, }, // WRITE + +{ NIL, NULL, N, N } +}; + + +#ifdef _MSC_VER +// 'this' : used in base member initializer list +#pragma warning( disable: 4355 ) +#endif + +SbiParser::SbiParser( StarBASIC* pb, SbModule* pm ) + : SbiTokenizer( pm->GetSource32(), pb ), + aGblStrings( this ), + aLclStrings( this ), + aGlobals( aGblStrings, SbGLOBAL ), + aPublics( aGblStrings, SbPUBLIC ), + aRtlSyms( aGblStrings, SbRTL ), + aGen( *pm, this, 1024 ) +{ + pBasic = pb; + eCurExpr = SbSYMBOL; + eEndTok = NIL; + pProc = NULL; + pStack = NULL; + pWithVar = NULL; + nBase = 0; + bText = + bGblDefs = + bNewGblDefs = + bSingleLineIf = + bExplicit = FALSE; + bClassModule = FALSE; + pPool = &aPublics; + for( short i = 0; i < 26; i++ ) + eDefTypes[ i ] = SbxVARIANT; // Kein expliziter Defaulttyp + + aPublics.SetParent( &aGlobals ); + aGlobals.SetParent( &aRtlSyms ); + + // Die globale Chainkette faengt bei Adresse 0 an: + nGblChain = aGen.Gen( _JUMP, 0 ); + + rTypeArray = new SbxArray; // Array fuer Benutzerdefinierte Typen + rEnumArray = new SbxArray; // Array for Enum types +} + + +// Ist Teil der Runtime-Library? +SbiSymDef* SbiParser::CheckRTLForSym( const String& rSym, SbxDataType eType ) +{ + SbxVariable* pVar = GetBasic()->GetRtl()->Find( rSym, SbxCLASS_DONTCARE ); + SbiSymDef* pDef = NULL; + if( pVar ) + { + if( pVar->IsA( TYPE(SbxMethod) ) ) + { + SbiProcDef* pProc_ = aRtlSyms.AddProc( rSym ); + pProc_->SetType( pVar->GetType() ); + pDef = pProc_; + } + else + { + pDef = aRtlSyms.AddSym( rSym ); + pDef->SetType( eType ); + } + } + return pDef; +} + +// Globale Chainkette schliessen + +BOOL SbiParser::HasGlobalCode() +{ + if( bGblDefs && nGblChain ) + { + aGen.BackChain( nGblChain ); + aGen.Gen( _LEAVE ); + // aGen.Gen( _STOP ); + nGblChain = 0; + } + return bGblDefs; +} + +void SbiParser::OpenBlock( SbiToken eTok, SbiExprNode* pVar ) +{ + SbiParseStack* p = new SbiParseStack; + p->eExitTok = eTok; + p->nChain = 0; + p->pWithVar = pWithVar; + p->pNext = pStack; + pStack = p; + pWithVar = pVar; + + // #29955 for-Schleifen-Ebene pflegen + if( eTok == FOR ) + aGen.IncForLevel(); +} + +void SbiParser::CloseBlock() +{ + if( pStack ) + { + SbiParseStack* p = pStack; + + // #29955 for-Schleifen-Ebene pflegen + if( p->eExitTok == FOR ) + aGen.DecForLevel(); + + aGen.BackChain( p->nChain ); + pStack = p->pNext; + pWithVar = p->pWithVar; + delete p; + } +} + +// EXIT ... + +void SbiParser::Exit() +{ + SbiToken eTok = Next(); + for( SbiParseStack* p = pStack; p; p = p->pNext ) + { + if( eTok == p->eExitTok ) + { + p->nChain = aGen.Gen( _JUMP, p->nChain ); + return; + } + } + if( pStack ) + Error( SbERR_EXPECTED, pStack->eExitTok ); + else + Error( SbERR_BAD_EXIT ); +} + +BOOL SbiParser::TestSymbol( BOOL bKwdOk ) +{ + Peek(); + if( eCurTok == SYMBOL || ( bKwdOk && IsKwd( eCurTok ) ) ) + { + Next(); return TRUE; + } + Error( SbERR_SYMBOL_EXPECTED ); + return FALSE; +} + +// Testen auf ein bestimmtes Token + +BOOL SbiParser::TestToken( SbiToken t ) +{ + if( Peek() == t ) + { + Next(); return TRUE; + } + else + { + Error( SbERR_EXPECTED, t ); + return FALSE; + } +} + +// Testen auf Komma oder EOLN + +BOOL SbiParser::TestComma() +{ + SbiToken eTok = Peek(); + if( IsEoln( eTok ) ) + { + Next(); + return FALSE; + } + else if( eTok != COMMA ) + { + Error( SbERR_EXPECTED, COMMA ); + return FALSE; + } + Next(); + return TRUE; +} + +// Testen, ob EOLN vorliegt + +void SbiParser::TestEoln() +{ + if( !IsEoln( Next() ) ) + { + Error( SbERR_EXPECTED, EOLN ); + while( !IsEoln( Next() ) ) {} + } +} + +// Parsing eines Statement-Blocks +// Das Parsing laeuft bis zum Ende-Token. + +void SbiParser::StmntBlock( SbiToken eEnd ) +{ + SbiToken xe = eEndTok; + eEndTok = eEnd; + while( !bAbort && Parse() ) {} + eEndTok = xe; + if( IsEof() ) + { + Error( SbERR_BAD_BLOCK, eEnd ); + bAbort = TRUE; + } +} + +// Die Hauptroutine. Durch wiederholten Aufrufs dieser Routine wird +// die Quelle geparst. Returnwert FALSE bei Ende/Fehlern. + +BOOL SbiParser::Parse() +{ + if( bAbort ) return FALSE; + + EnableErrors(); + + bErrorIsSymbol = false; + Peek(); + bErrorIsSymbol = true; + // Dateiende? + if( IsEof() ) + { + // AB #33133: Falls keine Sub angelegt wurde, muss hier + // der globale Chain abgeschlossen werden! + // AB #40689: Durch die neue static-Behandlung kann noch + // ein nGblChain vorhanden sein, daher vorher abfragen + if( bNewGblDefs && nGblChain == 0 ) + nGblChain = aGen.Gen( _JUMP, 0 ); + return FALSE; + } + + // Leerstatement? + if( IsEoln( eCurTok ) ) + { + Next(); return TRUE; + } + + if( !bSingleLineIf && MayBeLabel( TRUE ) ) + { + // Ist ein Label + if( !pProc ) + Error( SbERR_NOT_IN_MAIN, aSym ); + else + pProc->GetLabels().Define( aSym ); + Next(); Peek(); + // Leerstatement? + if( IsEoln( eCurTok ) ) + { + Next(); return TRUE; + } + } + + // Ende des Parsings? + if( eCurTok == eEndTok ) + { + Next(); + if( eCurTok != NIL ) + aGen.Statement(); + return FALSE; + } + + // Kommentar? + if( eCurTok == REM ) + { + Next(); return TRUE; + } + + // Kommt ein Symbol, ist es entweder eine Variable( LET ) + // oder eine SUB-Prozedur( CALL ohne Klammern ) + // DOT fuer Zuweisungen im WITH-Block: .A=5 + if( eCurTok == SYMBOL || eCurTok == DOT ) + { + if( !pProc ) + Error( SbERR_EXPECTED, SUB ); + else + { + // Damit Zeile & Spalte stimmen... + Next(); + Push( eCurTok ); + aGen.Statement(); + Symbol(); + } + } + else + { + Next(); + + // Hier folgen nun die Statement-Parser. + + SbiStatement* p; + for( p = StmntTable; p->eTok != NIL; p++ ) + if( p->eTok == eCurTok ) + break; + if( p->eTok != NIL ) + { + if( !pProc && !p->bMain ) + Error( SbERR_NOT_IN_MAIN, eCurTok ); + else if( pProc && !p->bSubr ) + Error( SbERR_NOT_IN_SUBR, eCurTok ); + else + { + // globalen Chain pflegen + // AB #41606/#40689: Durch die neue static-Behandlung kann noch + // ein nGblChain vorhanden sein, daher vorher abfragen + if( bNewGblDefs && nGblChain == 0 && + ( eCurTok == SUB || eCurTok == FUNCTION || eCurTok == PROPERTY ) ) + { + nGblChain = aGen.Gen( _JUMP, 0 ); + bNewGblDefs = FALSE; + } + // Statement-Opcode bitte auch am Anfang einer Sub + if( ( p->bSubr && (eCurTok != STATIC || Peek() == SUB || Peek() == FUNCTION ) ) || + eCurTok == SUB || eCurTok == FUNCTION ) + aGen.Statement(); + (this->*( p->Func ) )(); + SbxError nSbxErr = SbxBase::GetError(); + if( nSbxErr ) + SbxBase::ResetError(), Error( (SbError)nSbxErr ); + } + } + else + Error( SbERR_UNEXPECTED, eCurTok ); + } + + // Test auf Ende des Statements: + // Kann auch ein ELSE sein, da vor dem ELSE kein : stehen muss! + + if( !IsEos() ) + { + Peek(); + if( !IsEos() && eCurTok != ELSE ) + { + // falls das Parsing abgebrochen wurde, bis zum ":" vorgehen: + Error( SbERR_UNEXPECTED, eCurTok ); + while( !IsEos() ) Next(); + } + } + // Der Parser bricht am Ende ab, das naechste Token ist noch nicht + // geholt! + return TRUE; +} + +// Innerste With-Variable liefern +SbiExprNode* SbiParser::GetWithVar() +{ + if( pWithVar ) + return pWithVar; + + // Sonst im Stack suchen + SbiParseStack* p = pStack; + while( p ) + { + // LoopVar kann zur Zeit nur fuer with sein + if( p->pWithVar ) + return p->pWithVar; + p = p->pNext; + } + return NULL; +} + + +// Zuweisung oder Subroutine Call + +void SbiParser::Symbol() +{ + SbiExprMode eMode = bVBASupportOn ? EXPRMODE_STANDALONE : EXPRMODE_STANDARD; + SbiExpression aVar( this, SbSYMBOL, eMode ); + + bool bEQ = ( Peek() == EQ ); + if( !bEQ && bVBASupportOn && aVar.IsBracket() ) + Error( SbERR_EXPECTED, "=" ); + + RecursiveMode eRecMode = ( bEQ ? PREVENT_CALL : FORCE_CALL ); + bool bSpecialMidHandling = false; + SbiSymDef* pDef = aVar.GetRealVar(); + if( bEQ && pDef && pDef->GetScope() == SbRTL ) + { + String aRtlName = pDef->GetName(); + if( aRtlName.EqualsIgnoreCaseAscii("Mid") ) + { + SbiExprNode* pExprNode = aVar.GetExprNode(); + // SbiNodeType eNodeType; + if( pExprNode && pExprNode->GetNodeType() == SbxVARVAL ) + { + SbiExprList* pPar = pExprNode->GetParameters(); + short nParCount = pPar ? pPar->GetSize() : 0; + if( nParCount == 2 || nParCount == 3 ) + { + if( nParCount == 2 ) + pPar->addExpression( new SbiExpression( this, -1, SbxLONG ) ); + + TestToken( EQ ); + pPar->addExpression( new SbiExpression( this ) ); + + bSpecialMidHandling = true; + } + } + } + } + aVar.Gen( eRecMode ); + if( !bSpecialMidHandling ) + { + if( !bEQ ) + { + aGen.Gen( _GET ); + } + else + { + // Dann muss es eine Zuweisung sein. Was anderes gibts nicht! + if( !aVar.IsLvalue() ) + Error( SbERR_LVALUE_EXPECTED ); + TestToken( EQ ); + SbiExpression aExpr( this ); + aExpr.Gen(); + SbiOpcode eOp = _PUT; + // SbiSymDef* pDef = aVar.GetRealVar(); + if( pDef ) + { + if( pDef->GetConstDef() ) + Error( SbERR_DUPLICATE_DEF, pDef->GetName() ); + if( pDef->GetType() == SbxOBJECT ) + { + eOp = _SET; + if( pDef->GetTypeId() ) + { + aGen.Gen( _SETCLASS, pDef->GetTypeId() ); + return; + } + } + } + aGen.Gen( eOp ); + } + } +} + +// Zuweisungen + +void SbiParser::Assign() +{ + SbiExpression aLvalue( this, SbLVALUE ); + TestToken( EQ ); + SbiExpression aExpr( this ); + aLvalue.Gen(); + aExpr.Gen(); + USHORT nLen = 0; + SbiSymDef* pDef = aLvalue.GetRealVar(); + { + if( pDef->GetConstDef() ) + Error( SbERR_DUPLICATE_DEF, pDef->GetName() ); + nLen = aLvalue.GetRealVar()->GetLen(); + } + if( nLen ) + aGen.Gen( _PAD, nLen ); + aGen.Gen( _PUT ); +} + +// Zuweisungen einer Objektvariablen + +void SbiParser::Set() +{ + SbiExpression aLvalue( this, SbLVALUE ); + SbxDataType eType = aLvalue.GetType(); + if( eType != SbxOBJECT && eType != SbxEMPTY && eType != SbxVARIANT ) + Error( SbERR_INVALID_OBJECT ); + TestToken( EQ ); + SbiSymDef* pDef = aLvalue.GetRealVar(); + if( pDef && pDef->GetConstDef() ) + Error( SbERR_DUPLICATE_DEF, pDef->GetName() ); + + SbiToken eTok = Peek(); + if( eTok == NEW ) + { + Next(); + String aStr; + SbiSymDef* pTypeDef = new SbiSymDef( aStr ); + TypeDecl( *pTypeDef, TRUE ); + + aLvalue.Gen(); + // aGen.Gen( _CLASS, pDef->GetTypeId() | 0x8000 ); + aGen.Gen( _CREATE, pDef->GetId(), pTypeDef->GetTypeId() ); + aGen.Gen( _SETCLASS, pDef->GetTypeId() ); + } + else + { + SbiExpression aExpr( this ); + aLvalue.Gen(); + aExpr.Gen(); + // Its a good idea to distinguish between + // set someting = another & + // someting = another + // ( its necessary for vba objects where set is object + // specific and also doesn't involve processing default params ) + if( pDef->GetTypeId() ) + { + if ( bVBASupportOn ) + aGen.Gen( _VBASETCLASS, pDef->GetTypeId() ); + else + aGen.Gen( _SETCLASS, pDef->GetTypeId() ); + } + else + { + if ( bVBASupportOn ) + aGen.Gen( _VBASET ); + else + aGen.Gen( _SET ); + } + } + // aGen.Gen( _SET ); +} + +// JSM 07.10.95 +void SbiParser::LSet() +{ + SbiExpression aLvalue( this, SbLVALUE ); + if( aLvalue.GetType() != SbxSTRING ) + Error( SbERR_INVALID_OBJECT ); + TestToken( EQ ); + SbiSymDef* pDef = aLvalue.GetRealVar(); + if( pDef && pDef->GetConstDef() ) + Error( SbERR_DUPLICATE_DEF, pDef->GetName() ); + SbiExpression aExpr( this ); + aLvalue.Gen(); + aExpr.Gen(); + aGen.Gen( _LSET ); +} + +// JSM 07.10.95 +void SbiParser::RSet() +{ + SbiExpression aLvalue( this, SbLVALUE ); + if( aLvalue.GetType() != SbxSTRING ) + Error( SbERR_INVALID_OBJECT ); + TestToken( EQ ); + SbiSymDef* pDef = aLvalue.GetRealVar(); + if( pDef && pDef->GetConstDef() ) + Error( SbERR_DUPLICATE_DEF, pDef->GetName() ); + SbiExpression aExpr( this ); + aLvalue.Gen(); + aExpr.Gen(); + aGen.Gen( _RSET ); +} + +// DEFINT, DEFLNG, DEFSNG, DEFDBL, DEFSTR und so weiter + +void SbiParser::DefXXX() +{ + sal_Unicode ch1, ch2; + SbxDataType t = SbxDataType( eCurTok - DEFINT + SbxINTEGER ); + + while( !bAbort ) + { + if( Next() != SYMBOL ) break; + ch1 = aSym.ToUpperAscii().GetBuffer()[0]; + ch2 = 0; + if( Peek() == MINUS ) + { + Next(); + if( Next() != SYMBOL ) Error( SbERR_SYMBOL_EXPECTED ); + else + { + ch2 = aSym.ToUpperAscii().GetBuffer()[0]; + //ch2 = aSym.Upper(); + if( ch2 < ch1 ) Error( SbERR_SYNTAX ), ch2 = 0; + } + } + if (!ch2) ch2 = ch1; + ch1 -= 'A'; ch2 -= 'A'; + for (; ch1 <= ch2; ch1++) eDefTypes[ ch1 ] = t; + if( !TestComma() ) break; + } +} + +// STOP/SYSTEM + +void SbiParser::Stop() +{ + aGen.Gen( _STOP ); + Peek(); // #35694: Nur Peek(), damit EOL in Single-Line-If erkannt wird +} + +// IMPLEMENTS + +void SbiParser::Implements() +{ + if( !bClassModule ) + { + Error( SbERR_UNEXPECTED, IMPLEMENTS ); + return; + } + + if( TestSymbol() ) + { + String aImplementedIface = GetSym(); + aIfaceVector.push_back( aImplementedIface ); + } +} + +void SbiParser::EnableCompatibility() +{ + if( !bCompatible ) + AddConstants(); + bCompatible = TRUE; +} + +// OPTION + +void SbiParser::Option() +{ + switch( Next() ) + { + case EXPLICIT: + bExplicit = TRUE; break; + case BASE: + if( Next() == NUMBER ) + { + if( nVal == 0 || nVal == 1 ) + { + nBase = (short) nVal; + break; + } + } + Error( SbERR_EXPECTED, "0/1" ); + break; + case PRIVATE: + { + String aString = SbiTokenizer::Symbol(Next()); + if( !aString.EqualsIgnoreCaseAscii("Module") ) + Error( SbERR_EXPECTED, "Module" ); + break; + } + case COMPARE: + switch( Next() ) + { + case TEXT: bText = TRUE; return; + case BINARY: bText = FALSE; return; + default:; + } // Fall thru! + case COMPATIBLE: + EnableCompatibility(); + break; + + case CLASSMODULE: + bClassModule = TRUE; + break; + case VBASUPPORT: + if( Next() == NUMBER ) + { + if ( nVal == 1 || nVal == 0 ) + { + bVBASupportOn = ( nVal == 1 ); + if ( bVBASupportOn ) + EnableCompatibility(); + break; + } + } + Error( SbERR_EXPECTED, "0/1" ); + break; + default: + Error( SbERR_BAD_OPTION, eCurTok ); + } +} + +void addStringConst( SbiSymPool& rPool, const char* pSym, const String& rStr ) +{ + SbiConstDef* pConst = new SbiConstDef( String::CreateFromAscii( pSym ) ); + pConst->SetType( SbxSTRING ); + pConst->Set( rStr ); + rPool.Add( pConst ); +} + +inline void addStringConst( SbiSymPool& rPool, const char* pSym, const char* pStr ) +{ + addStringConst( rPool, pSym, String::CreateFromAscii( pStr ) ); +} + +void SbiParser::AddConstants( void ) +{ + // #113063 Create constant RTL symbols + addStringConst( aPublics, "vbCr", "\x0D" ); + addStringConst( aPublics, "vbCrLf", "\x0D\x0A" ); + addStringConst( aPublics, "vbFormFeed", "\x0C" ); + addStringConst( aPublics, "vbLf", "\x0A" ); +#if defined(UNX) + addStringConst( aPublics, "vbNewLine", "\x0A" ); +#else + addStringConst( aPublics, "vbNewLine", "\x0D\x0A" ); +#endif + addStringConst( aPublics, "vbNullString", "" ); + addStringConst( aPublics, "vbTab", "\x09" ); + addStringConst( aPublics, "vbVerticalTab", "\x0B" ); + + // Force length 1 and make char 0 afterwards + String aNullCharStr( String::CreateFromAscii( " " ) ); + aNullCharStr.SetChar( 0, 0 ); + addStringConst( aPublics, "vbNullChar", aNullCharStr ); +} + +// ERROR n + +void SbiParser::ErrorStmnt() +{ + SbiExpression aPar( this ); + aPar.Gen(); + aGen.Gen( _ERROR ); +} + diff --git a/basic/source/comp/sbcomp.cxx b/basic/source/comp/sbcomp.cxx new file mode 100644 index 000000000000..6e4f7ddf126e --- /dev/null +++ b/basic/source/comp/sbcomp.cxx @@ -0,0 +1,171 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_basic.hxx" + +#include <basic/sbx.hxx> +#include "sbcomp.hxx" +#include "image.hxx" + + +// For debugging only +// #define DBG_SAVE_DISASSEMBLY + +#ifdef DBG_SAVE_DISASSEMBLY +static bool dbg_bDisassemble = true; +#include <comphelper/processfactory.hxx> + +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/ucb/XSimpleFileAccess3.hpp> +#include <com/sun/star/io/XTextOutputStream.hpp> +#include <com/sun/star/io/XActiveDataSource.hpp> + +using namespace comphelper; +using namespace rtl; +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::ucb; +using namespace com::sun::star::io; + +void dbg_SaveDisassembly( SbModule* pModule ) +{ + bool bDisassemble = dbg_bDisassemble; + if( bDisassemble ) + { + Reference< XSimpleFileAccess3 > xSFI; + Reference< XTextOutputStream > xTextOut; + Reference< XOutputStream > xOut; + Reference< XMultiServiceFactory > xSMgr = getProcessServiceFactory(); + if( xSMgr.is() ) + { + Reference< XSimpleFileAccess3 > xSFI = Reference< XSimpleFileAccess3 >( xSMgr->createInstance + ( OUString::createFromAscii( "com.sun.star.ucb.SimpleFileAccess" ) ), UNO_QUERY ); + if( xSFI.is() ) + { + String aFile( RTL_CONSTASCII_USTRINGPARAM("file:///d:/BasicAsm_") ); + StarBASIC* pBasic = (StarBASIC*)pModule->GetParent(); + if( pBasic ) + { + aFile += pBasic->GetName(); + aFile.AppendAscii( "_" ); + } + aFile += pModule->GetName(); + aFile.AppendAscii( ".txt" ); + + // String aFile( RTL_CONSTASCII_USTRINGPARAM("file:///d:/BasicAsm.txt") ); + if( xSFI->exists( aFile ) ) + xSFI->kill( aFile ); + xOut = xSFI->openFileWrite( aFile ); + Reference< XInterface > x = xSMgr->createInstance( OUString::createFromAscii( "com.sun.star.io.TextOutputStream" ) ); + Reference< XActiveDataSource > xADS( x, UNO_QUERY ); + xADS->setOutputStream( xOut ); + xTextOut = Reference< XTextOutputStream >( x, UNO_QUERY ); + } + } + + if( xTextOut.is() ) + { + String aDisassemblyStr; + pModule->Disassemble( aDisassemblyStr ); + xTextOut->writeString( aDisassemblyStr ); + } + xOut->closeOutput(); + } +} +#endif + +// Diese Routine ist hier definiert, damit der Compiler als eigenes Segment +// geladen werden kann. + +BOOL SbModule::Compile() +{ + if( pImage ) + return TRUE; + StarBASIC* pBasic = PTR_CAST(StarBASIC,GetParent()); + if( !pBasic ) + return FALSE; + SbxBase::ResetError(); + // Aktuelles Modul! + SbModule* pOld = pCMOD; + pCMOD = this; + + SbiParser* pParser = new SbiParser( (StarBASIC*) GetParent(), this ); + while( pParser->Parse() ) {} + if( !pParser->GetErrors() ) + pParser->aGen.Save(); + delete pParser; + // fuer den Disassembler + if( pImage ) + pImage->aOUSource = aOUSource; + + pCMOD = pOld; + + // Beim Compilieren eines Moduls werden die Modul-globalen + // Variablen aller Module ungueltig + BOOL bRet = IsCompiled(); + if( bRet ) + { + pBasic->ClearAllModuleVars(); + // clear all method statics + for( USHORT i = 0; i < pMethods->Count(); i++ ) + { + SbMethod* p = PTR_CAST(SbMethod,pMethods->Get( i ) ); + if( p ) + p->ClearStatics(); + } + + // #i31510 Init other libs only if Basic isn't running + if( pINST == NULL ) + { + SbxObject* pParent_ = pBasic->GetParent(); + if( pParent_ ) + pBasic = PTR_CAST(StarBASIC,pParent_); + if( pBasic ) + pBasic->ClearAllModuleVars(); + } + } + +#ifdef DBG_SAVE_DISASSEMBLY + dbg_SaveDisassembly( this ); +#endif + + return bRet; +} + +/************************************************************************** +* +* Syntax-Highlighting +* +**************************************************************************/ + +void StarBASIC::Highlight( const String& rSrc, SbTextPortions& rList ) +{ + SbiTokenizer aTok( rSrc ); + aTok.Hilite( rList ); +} + diff --git a/basic/source/comp/scanner.cxx b/basic/source/comp/scanner.cxx new file mode 100644 index 000000000000..dd68f20893f5 --- /dev/null +++ b/basic/source/comp/scanner.cxx @@ -0,0 +1,582 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_basic.hxx" + +#include "sbcomp.hxx" +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#if defined UNX +#include <stdlib.h> +#else +#include <math.h> // atof() +#endif +#include <rtl/math.hxx> +#include <vcl/svapp.hxx> +#include <unotools/charclass.hxx> + +#include <runtime.hxx> + +SbiScanner::SbiScanner( const ::rtl::OUString& rBuf, StarBASIC* p ) : aBuf( rBuf ) +{ + pBasic = p; + pLine = NULL; + nVal = 0; + eScanType = SbxVARIANT; + nErrors = 0; + nBufPos = 0; + nCurCol1 = 0; + nSavedCol1 = 0; + nColLock = 0; + nLine = 0; + nCol1 = 0; + nCol2 = 0; + nCol = 0; + bError = + bAbort = + bSpaces = + bNumber = + bSymbol = + bUsedForHilite = + bCompatible = + bVBASupportOn = + bPrevLineExtentsComment = FALSE; + bHash = + bErrors = TRUE; +} + +SbiScanner::~SbiScanner() +{} + +void SbiScanner::LockColumn() +{ + if( !nColLock++ ) + nSavedCol1 = nCol1; +} + +void SbiScanner::UnlockColumn() +{ + if( nColLock ) + nColLock--; +} + +void SbiScanner::GenError( SbError code ) +{ + if( GetSbData()->bBlockCompilerError ) + { + bAbort = TRUE; + return; + } + if( !bError && bErrors ) + { + BOOL bRes = TRUE; + // Nur einen Fehler pro Statement reporten + bError = TRUE; + if( pBasic ) + { + // Falls EXPECTED oder UNEXPECTED kommen sollte, bezieht es sich + // immer auf das letzte Token, also die Col1 uebernehmen + USHORT nc = nColLock ? nSavedCol1 : nCol1; + switch( code ) + { + case SbERR_EXPECTED: + case SbERR_UNEXPECTED: + case SbERR_SYMBOL_EXPECTED: + case SbERR_LABEL_EXPECTED: + nc = nCol1; + if( nc > nCol2 ) nCol2 = nc; + break; + } + bRes = pBasic->CError( code, aError, nLine, nc, nCol2 ); + } + bAbort |= !bRes | + ( code == SbERR_NO_MEMORY || code == SbERR_PROG_TOO_LARGE ); + } + if( bErrors ) + nErrors++; +} + +// Falls sofort ein Doppelpunkt folgt, wird TRUE zurueckgeliefert. +// Wird von SbiTokenizer::MayBeLabel() verwendet, um einen Label zu erkennen + +BOOL SbiScanner::DoesColonFollow() +{ + if( pLine && *pLine == ':' ) + { + pLine++; nCol++; return TRUE; + } + else return FALSE; +} + +// Testen auf ein legales Suffix + +static SbxDataType GetSuffixType( sal_Unicode c ) +{ + static String aSuffixesStr = String::CreateFromAscii( "%&!#@ $" ); + if( c ) + { + sal_uInt32 n = aSuffixesStr.Search( c ); + if( STRING_NOTFOUND != n && c != ' ' ) + return SbxDataType( (USHORT) n + SbxINTEGER ); + } + return SbxVARIANT; +} + +// Einlesen des naechsten Symbols in die Variablen aSym, nVal und eType +// Returnwert ist FALSE bei EOF oder Fehlern +#define BUF_SIZE 80 + +BOOL SbiScanner::NextSym() +{ + // Fuer den EOLN-Fall merken + USHORT nOldLine = nLine; + USHORT nOldCol1 = nCol1; + USHORT nOldCol2 = nCol2; + sal_Unicode buf[ BUF_SIZE ], *p = buf; + bHash = FALSE; + + eScanType = SbxVARIANT; + aSym.Erase(); + bSymbol = + bNumber = bSpaces = FALSE; + + // Zeile einlesen? + if( !pLine ) + { + INT32 n = nBufPos; + INT32 nLen = aBuf.getLength(); + if( nBufPos >= nLen ) + return FALSE; + const sal_Unicode* p2 = aBuf.getStr(); + p2 += n; + while( ( n < nLen ) && ( *p2 != '\n' ) && ( *p2 != '\r' ) ) + p2++, n++; + aLine = aBuf.copy( nBufPos, n - nBufPos ); + if( n < nLen ) + { + if( *p2 == '\r' && *( p2+1 ) == '\n' ) + n += 2; + else + n++; + } + nBufPos = n; + pLine = aLine.getStr(); + nOldLine = ++nLine; + nCol = nCol1 = nCol2 = nOldCol1 = nOldCol2 = 0; + nColLock = 0; + } + + // Leerstellen weg: + while( *pLine && (( *pLine == ' ' ) || ( *pLine == '\t' ) || ( *pLine == '\f' )) ) + pLine++, nCol++, bSpaces = TRUE; + + nCol1 = nCol; + + // nur Leerzeile? + if( !*pLine ) + goto eoln; + + if( bPrevLineExtentsComment ) + goto PrevLineCommentLbl; + + if( *pLine == '#' ) + { + pLine++; + nCol++; + bHash = TRUE; + } + + // Symbol? Dann Zeichen kopieren. + if( BasicSimpleCharClass::isAlpha( *pLine, bCompatible ) || *pLine == '_' ) + { + // Wenn nach '_' nichts kommt, ist es ein Zeilenabschluss! + if( *pLine == '_' && !*(pLine+1) ) + { pLine++; + goto eoln; } + bSymbol = TRUE; + short n = nCol; + for ( ; (BasicSimpleCharClass::isAlphaNumeric( *pLine, bCompatible ) || ( *pLine == '_' ) ); pLine++ ) + nCol++; + aSym = aLine.copy( n, nCol - n ); + // Abschliessendes '_' durch Space ersetzen, wenn Zeilenende folgt + // (sonst falsche Zeilenfortsetzung) + if( !bUsedForHilite && !*pLine && *(pLine-1) == '_' ) + { + aSym.GetBufferAccess(); // #109693 force copy if necessary + *((sal_Unicode*)(pLine-1)) = ' '; // cast wegen const + } + // Typkennung? + // Das Ausrufezeichen bitte nicht testen, wenn + // danach noch ein Symbol anschliesst + else if( *pLine != '!' || !BasicSimpleCharClass::isAlpha( pLine[ 1 ], bCompatible ) ) + { + SbxDataType t = GetSuffixType( *pLine ); + if( t != SbxVARIANT ) + { + eScanType = t; + pLine++; + nCol++; + } + } + } + + // Zahl? Dann einlesen und konvertieren. + else if( BasicSimpleCharClass::isDigit( *pLine & 0xFF ) + || ( *pLine == '.' && BasicSimpleCharClass::isDigit( *(pLine+1) & 0xFF ) ) ) + { + short exp = 0; + short comma = 0; + short ndig = 0; + short ncdig = 0; + eScanType = SbxDOUBLE; + BOOL bBufOverflow = FALSE; + while( strchr( "0123456789.DEde", *pLine ) && *pLine ) + { + // AB 4.1.1996: Buffer voll? -> leer weiter scannen + if( (p-buf) == (BUF_SIZE-1) ) + { + bBufOverflow = TRUE; + pLine++, nCol++; + continue; + } + // Komma oder Exponent? + if( *pLine == '.' ) + { + if( ++comma > 1 ) + { + pLine++; nCol++; continue; + } + else *p++ = *pLine++, nCol++; + } + else if( strchr( "DdEe", *pLine ) ) + { + if (++exp > 1) + { + pLine++; nCol++; continue; + } +// if( toupper( *pLine ) == 'D' ) +// eScanType = SbxDOUBLE; + *p++ = 'E'; pLine++; nCol++; + // Vorzeichen hinter Exponent? + if( *pLine == '+' ) + pLine++, nCol++; + else + if( *pLine == '-' ) + *p++ = *pLine++, nCol++; + } + else + { + *p++ = *pLine++, nCol++; + if( comma && !exp ) ncdig++; + } + if (!exp) ndig++; + } + *p = 0; + aSym = p; bNumber = TRUE; + // Komma, Exponent mehrfach vorhanden? + if( comma > 1 || exp > 1 ) + { aError = '.'; + GenError( SbERR_BAD_CHAR_IN_NUMBER ); } + + // #57844 Lokalisierte Funktion benutzen + nVal = rtl_math_uStringToDouble( buf, buf+(p-buf), '.', ',', NULL, NULL ); + // ALT: nVal = atof( buf ); + + ndig = ndig - comma; + if( !comma && !exp ) + { + if( nVal >= SbxMININT && nVal <= SbxMAXINT ) + eScanType = SbxINTEGER; + else + if( nVal >= SbxMINLNG && nVal <= SbxMAXLNG ) + eScanType = SbxLONG; + } + if( bBufOverflow ) + GenError( SbERR_MATH_OVERFLOW ); + // zu viele Zahlen fuer SINGLE? +// if (ndig > 15 || ncdig > 6) +// eScanType = SbxDOUBLE; +// else +// if( nVal > SbxMAXSNG || nVal < SbxMINSNG ) +// eScanType = SbxDOUBLE; + + // Typkennung? + SbxDataType t = GetSuffixType( *pLine ); + if( t != SbxVARIANT ) + { + eScanType = t; + pLine++; + nCol++; + } + } + + // Hex/Oktalzahl? Einlesen und konvertieren: + else if( *pLine == '&' ) + { + pLine++; nCol++; + sal_Unicode cmp1[] = { '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F', 0 }; + sal_Unicode cmp2[] = { '0', '1', '2', '3', '4', '5', '6', '7', 0 }; + sal_Unicode *cmp = cmp1; + //char *cmp = "0123456789ABCDEF"; + sal_Unicode base = 16; + sal_Unicode ndig = 8; + sal_Unicode xch = *pLine++ & 0xFF; nCol++; + switch( toupper( xch ) ) + { + case 'O': + cmp = cmp2; base = 8; ndig = 11; break; + //cmp = "01234567"; base = 8; ndig = 11; break; + case 'H': + break; + default : + // Wird als Operator angesehen + pLine--; nCol--; nCol1 = nCol-1; aSym = '&'; return SYMBOL; + } + bNumber = TRUE; + long l = 0; + int i; + BOOL bBufOverflow = FALSE; + while( BasicSimpleCharClass::isAlphaNumeric( *pLine & 0xFF, bCompatible ) ) + { + sal_Unicode ch = sal::static_int_cast< sal_Unicode >( + toupper( *pLine & 0xFF ) ); + pLine++; nCol++; + // AB 4.1.1996: Buffer voll, leer weiter scannen + if( (p-buf) == (BUF_SIZE-1) ) + bBufOverflow = TRUE; + else if( String( cmp ).Search( ch ) != STRING_NOTFOUND ) + //else if( strchr( cmp, ch ) ) + *p++ = ch; + else + { + aError = ch; + GenError( SbERR_BAD_CHAR_IN_NUMBER ); + } + } + *p = 0; + for( p = buf; *p; p++ ) + { + i = (*p & 0xFF) - '0'; + if( i > 9 ) i -= 7; + l = ( l * base ) + i; + if( !ndig-- ) + { + GenError( SbERR_MATH_OVERFLOW ); break; + } + } + if( *pLine == '&' ) pLine++, nCol++; + nVal = (double) l; + eScanType = ( l >= SbxMININT && l <= SbxMAXINT ) ? SbxINTEGER : SbxLONG; + if( bBufOverflow ) + GenError( SbERR_MATH_OVERFLOW ); + } + + // Strings: + else if( *pLine == '"' || *pLine == '[' ) + { + sal_Unicode cSep = *pLine; + if( cSep == '[' ) + bSymbol = TRUE, cSep = ']'; + short n = nCol+1; + while( *pLine ) + { + do pLine++, nCol++; + while( *pLine && ( *pLine != cSep ) ); + if( *pLine == cSep ) + { + pLine++; nCol++; + if( *pLine != cSep || cSep == ']' ) break; + } else aError = cSep, GenError( SbERR_EXPECTED ); + } + // If VBA Interop then doen't eat the [] chars + if ( cSep == ']' && bVBASupportOn ) + aSym = aLine.copy( n - 1, nCol - n + 1); + else + aSym = aLine.copy( n, nCol - n - 1 ); + // Doppelte Stringbegrenzer raus + String s( cSep ); + s += cSep; + USHORT nIdx = 0; + do + { + nIdx = aSym.Search( s, nIdx ); + if( nIdx == STRING_NOTFOUND ) + break; + aSym.Erase( nIdx, 1 ); + nIdx++; + } + while( true ); + if( cSep != ']' ) + eScanType = ( cSep == '#' ) ? SbxDATE : SbxSTRING; + } + // ungueltige Zeichen: + else if( ( *pLine & 0xFF ) >= 0x7F ) + { + GenError( SbERR_SYNTAX ); pLine++; nCol++; + } + // andere Gruppen: + else + { + short n = 1; + switch( *pLine++ ) + { + case '<': if( *pLine == '>' || *pLine == '=' ) n = 2; break; + case '>': if( *pLine == '=' ) n = 2; break; + case ':': if( *pLine == '=' ) n = 2; break; + } + aSym = aLine.copy( nCol, n ); + pLine += n-1; nCol = nCol + n; + } + + nCol2 = nCol-1; + +PrevLineCommentLbl: + // Kommentar? + if( bPrevLineExtentsComment || (eScanType != SbxSTRING && + ( aSym.GetBuffer()[0] == '\'' || aSym.EqualsIgnoreCaseAscii( "REM" ) ) ) ) + { + bPrevLineExtentsComment = FALSE; + aSym = String::CreateFromAscii( "REM" ); + USHORT nLen = String( pLine ).Len(); + if( bCompatible && pLine[ nLen - 1 ] == '_' && pLine[ nLen - 2 ] == ' ' ) + bPrevLineExtentsComment = TRUE; + nCol2 = nCol2 + nLen; + pLine = NULL; + } + return TRUE; + + // Sonst Zeilen-Ende: aber bitte auf '_' testen, ob die + // Zeile nicht weitergeht! +eoln: + if( nCol && *--pLine == '_' ) + { + pLine = NULL; + bool bRes = NextSym(); + if( bVBASupportOn && aSym.GetBuffer()[0] == '.' ) + { + // object _ + // .Method + // ^^^ <- spaces is legal in MSO VBA + OSL_TRACE("*** resetting bSpaces***"); + bSpaces = FALSE; + } + return bRes; + } + else + { + pLine = NULL; + nLine = nOldLine; + nCol1 = nOldCol1; + nCol2 = nOldCol2; + aSym = '\n'; + nColLock = 0; + return TRUE; + } +} + +LetterTable BasicSimpleCharClass::aLetterTable; + +LetterTable::LetterTable( void ) +{ + for( int i = 0 ; i < 256 ; ++i ) + IsLetterTab[i] = false; + + IsLetterTab[0xC0] = true; // À , CAPITAL LETTER A WITH GRAVE ACCENT + IsLetterTab[0xC1] = true; // Á , CAPITAL LETTER A WITH ACUTE ACCENT + IsLetterTab[0xC2] = true; //  , CAPITAL LETTER A WITH CIRCUMFLEX ACCENT + IsLetterTab[0xC3] = true; // à , CAPITAL LETTER A WITH TILDE + IsLetterTab[0xC4] = true; // Ä , CAPITAL LETTER A WITH DIAERESIS + IsLetterTab[0xC5] = true; // Å , CAPITAL LETTER A WITH RING ABOVE + IsLetterTab[0xC6] = true; // Æ , CAPITAL LIGATURE AE + IsLetterTab[0xC7] = true; // Ç , CAPITAL LETTER C WITH CEDILLA + IsLetterTab[0xC8] = true; // È , CAPITAL LETTER E WITH GRAVE ACCENT + IsLetterTab[0xC9] = true; // É , CAPITAL LETTER E WITH ACUTE ACCENT + IsLetterTab[0xCA] = true; // Ê , CAPITAL LETTER E WITH CIRCUMFLEX ACCENT + IsLetterTab[0xCB] = true; // Ë , CAPITAL LETTER E WITH DIAERESIS + IsLetterTab[0xCC] = true; // Ì , CAPITAL LETTER I WITH GRAVE ACCENT + IsLetterTab[0xCD] = true; // Í , CAPITAL LETTER I WITH ACUTE ACCENT + IsLetterTab[0xCE] = true; // Î , CAPITAL LETTER I WITH CIRCUMFLEX ACCENT + IsLetterTab[0xCF] = true; // Ï , CAPITAL LETTER I WITH DIAERESIS + IsLetterTab[0xD0] = true; // Ð , CAPITAL LETTER ETH + IsLetterTab[0xD1] = true; // Ñ , CAPITAL LETTER N WITH TILDE + IsLetterTab[0xD2] = true; // Ò , CAPITAL LETTER O WITH GRAVE ACCENT + IsLetterTab[0xD3] = true; // Ó , CAPITAL LETTER O WITH ACUTE ACCENT + IsLetterTab[0xD4] = true; // Ô , CAPITAL LETTER O WITH CIRCUMFLEX ACCENT + IsLetterTab[0xD5] = true; // Õ , CAPITAL LETTER O WITH TILDE + IsLetterTab[0xD6] = true; // Ö , CAPITAL LETTER O WITH DIAERESIS + IsLetterTab[0xD8] = true; // Ø , CAPITAL LETTER O WITH STROKE + IsLetterTab[0xD9] = true; // Ù , CAPITAL LETTER U WITH GRAVE ACCENT + IsLetterTab[0xDA] = true; // Ú , CAPITAL LETTER U WITH ACUTE ACCENT + IsLetterTab[0xDB] = true; // Û , CAPITAL LETTER U WITH CIRCUMFLEX ACCENT + IsLetterTab[0xDC] = true; // Ü , CAPITAL LETTER U WITH DIAERESIS + IsLetterTab[0xDD] = true; // Ý , CAPITAL LETTER Y WITH ACUTE ACCENT + IsLetterTab[0xDE] = true; // Þ , CAPITAL LETTER THORN + IsLetterTab[0xDF] = true; // ß , SMALL LETTER SHARP S + IsLetterTab[0xE0] = true; // à , SMALL LETTER A WITH GRAVE ACCENT + IsLetterTab[0xE1] = true; // á , SMALL LETTER A WITH ACUTE ACCENT + IsLetterTab[0xE2] = true; // â , SMALL LETTER A WITH CIRCUMFLEX ACCENT + IsLetterTab[0xE3] = true; // ã , SMALL LETTER A WITH TILDE + IsLetterTab[0xE4] = true; // ä , SMALL LETTER A WITH DIAERESIS + IsLetterTab[0xE5] = true; // å , SMALL LETTER A WITH RING ABOVE + IsLetterTab[0xE6] = true; // æ , SMALL LIGATURE AE + IsLetterTab[0xE7] = true; // ç , SMALL LETTER C WITH CEDILLA + IsLetterTab[0xE8] = true; // è , SMALL LETTER E WITH GRAVE ACCENT + IsLetterTab[0xE9] = true; // é , SMALL LETTER E WITH ACUTE ACCENT + IsLetterTab[0xEA] = true; // ê , SMALL LETTER E WITH CIRCUMFLEX ACCENT + IsLetterTab[0xEB] = true; // ë , SMALL LETTER E WITH DIAERESIS + IsLetterTab[0xEC] = true; // ì , SMALL LETTER I WITH GRAVE ACCENT + IsLetterTab[0xED] = true; // í , SMALL LETTER I WITH ACUTE ACCENT + IsLetterTab[0xEE] = true; // î , SMALL LETTER I WITH CIRCUMFLEX ACCENT + IsLetterTab[0xEF] = true; // ï , SMALL LETTER I WITH DIAERESIS + IsLetterTab[0xF0] = true; // ð , SMALL LETTER ETH + IsLetterTab[0xF1] = true; // ñ , SMALL LETTER N WITH TILDE + IsLetterTab[0xF2] = true; // ò , SMALL LETTER O WITH GRAVE ACCENT + IsLetterTab[0xF3] = true; // ó , SMALL LETTER O WITH ACUTE ACCENT + IsLetterTab[0xF4] = true; // ô , SMALL LETTER O WITH CIRCUMFLEX ACCENT + IsLetterTab[0xF5] = true; // õ , SMALL LETTER O WITH TILDE + IsLetterTab[0xF6] = true; // ö , SMALL LETTER O WITH DIAERESIS + IsLetterTab[0xF8] = true; // ø , SMALL LETTER O WITH OBLIQUE BAR + IsLetterTab[0xF9] = true; // ù , SMALL LETTER U WITH GRAVE ACCENT + IsLetterTab[0xFA] = true; // ú , SMALL LETTER U WITH ACUTE ACCENT + IsLetterTab[0xFB] = true; // û , SMALL LETTER U WITH CIRCUMFLEX ACCENT + IsLetterTab[0xFC] = true; // ü , SMALL LETTER U WITH DIAERESIS + IsLetterTab[0xFD] = true; // ý , SMALL LETTER Y WITH ACUTE ACCENT + IsLetterTab[0xFE] = true; // þ , SMALL LETTER THORN + IsLetterTab[0xFF] = true; // ÿ , SMALL LETTER Y WITH DIAERESIS +} + +bool LetterTable::isLetterUnicode( sal_Unicode c ) +{ + static CharClass* pCharClass = NULL; + if( pCharClass == NULL ) + pCharClass = new CharClass( Application::GetSettings().GetLocale() ); + String aStr( c ); + bool bRet = pCharClass->isLetter( aStr, 0 ); + return bRet; +} diff --git a/basic/source/comp/symtbl.cxx b/basic/source/comp/symtbl.cxx new file mode 100644 index 000000000000..24f0f890eebb --- /dev/null +++ b/basic/source/comp/symtbl.cxx @@ -0,0 +1,534 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_basic.hxx" + +#include "sbcomp.hxx" +#include <stdio.h> +#include <string.h> +#include <ctype.h> + +SV_IMPL_PTRARR(SbiStrings,String*) +SV_IMPL_PTRARR(SbiSymbols,SbiSymDef*) + +// Alle Symbolnamen werden im Stringpool des Symbol-Pools abgelegt, damit +// alle Symbole im gleichen Case verarbeitet werden. Beim Speichern des +// Code-Images wird der globale Stringpool mit den entsprechenden Sympools +// gespeichert. Der lokale Stringpool nimmt alle Symbole auf, die nicht +// ins Image wandern (Labels, Konstantennamen etc). + +/*************************************************************************** +|* +|* SbiStringPool +|* +***************************************************************************/ + +SbiStringPool::SbiStringPool( SbiParser* p ) +{ + pParser = p; +} + +SbiStringPool::~SbiStringPool() +{} + +// Suchen + +const String& SbiStringPool::Find( USHORT n ) const +{ + if( !n || n > aData.Count() ) + return aEmpty; + else + return *aData.GetObject( n-1 ); +} + +// Hinzufuegen eines Strings. Der String wird Case-Insensitiv +// verglichen. + +short SbiStringPool::Add( const String& rVal, BOOL bNoCase ) +{ + USHORT n = aData.Count(); + for( USHORT i = 0; i < n; i++ ) + { + String* p = aData.GetObject( i ); + if( ( bNoCase && p->Equals( rVal ) ) + || ( !bNoCase && p->EqualsIgnoreCaseAscii( rVal ) ) ) + return i+1; + } + const String* pNew = new String( rVal ); + aData.Insert( pNew, n++ ); + return (short) n; +} + +short SbiStringPool::Add( double n, SbxDataType t ) +{ + char buf[ 40 ]; + switch( t ) + { + case SbxINTEGER: snprintf( buf, sizeof(buf), "%d", (short) n ); break; + case SbxLONG: snprintf( buf, sizeof(buf), "%ld", (long) n ); break; + case SbxSINGLE: snprintf( buf, sizeof(buf), "%.6g", (float) n ); break; + case SbxDOUBLE: snprintf( buf, sizeof(buf), "%.16g", n ); break; + default: break; + } + return Add( String::CreateFromAscii( buf ) ); +} + +/*************************************************************************** +|* +|* SbiSymPool +|* +***************************************************************************/ + +SbiSymPool::SbiSymPool( SbiStringPool& r, SbiSymScope s ) : rStrings( r ) +{ + pParser = r.GetParser(); + eScope = s; + pParent = NULL; + nCur = + nProcId = 0; +} + +SbiSymPool::~SbiSymPool() +{} + +// Inhalt loeschen + +void SbiSymPool::Clear() +{ + aData.DeleteAndDestroy( 0, aData.Count() ); +} + +SbiSymDef* SbiSymPool::First() +{ + nCur = (USHORT) -1; + return Next(); +} + +SbiSymDef* SbiSymPool::Next() +{ + if( ++nCur >= aData.Count() ) + return NULL; + else + return aData.GetObject( nCur ); +} + +// Hinzufuegen eines Symbols + +SbiSymDef* SbiSymPool::AddSym( const String& rName ) +{ + SbiSymDef* p = new SbiSymDef( rName ); + p->nPos = aData.Count(); + p->nId = rStrings.Add( rName ); + p->nProcId = nProcId; + p->pIn = this; + const SbiSymDef* q = p; + aData.Insert( q, q->nPos ); + return p; +} + +SbiProcDef* SbiSymPool::AddProc( const String& rName ) +{ + SbiProcDef* p = new SbiProcDef( pParser, rName ); + p->nPos = aData.Count(); + p->nId = rStrings.Add( rName ); + // Procs sind immer global + p->nProcId = 0; + p->pIn = this; + const SbiSymDef* q = p; + aData.Insert( q, q->nPos ); + return p; +} + +// Hinzufuegen einer extern aufgebauten Symboldefinition + +void SbiSymPool::Add( SbiSymDef* pDef ) +{ + if( pDef && pDef->pIn != this ) + { + if( pDef->pIn ) + { +#ifdef DBG_UTIL + // schon in einem anderen Pool drin! + pParser->Error( SbERR_INTERNAL_ERROR, "Dbl Pool" ); +#endif + return; + } + + pDef->nPos = aData.Count(); + if( !pDef->nId ) + { + // Bei statischen Variablen muss ein eindeutiger Name + // im Stringpool erzeugt werden (Form ProcName:VarName) + String aName( pDef->aName ); + if( pDef->IsStatic() ) + { + aName = pParser->aGblStrings.Find( nProcId ); + aName += ':'; + aName += pDef->aName; + } + pDef->nId = rStrings.Add( aName ); + } + // Procs sind immer global + if( !pDef->GetProcDef() ) + pDef->nProcId = nProcId; + pDef->pIn = this; + const SbiSymDef* q = pDef; + aData.Insert( q, q->nPos ); + } +} + +// Suchen eines Eintrags ueber den Namen. Es wird auch im Parent gesucht. + +SbiSymDef* SbiSymPool::Find( const String& rName ) const +{ + for( USHORT i = 0; i < aData.Count(); i++ ) + { + SbiSymDef* p = aData.GetObject( i ); + if( ( !p->nProcId || ( p->nProcId == nProcId ) ) + && ( p->aName.EqualsIgnoreCaseAscii( rName ) ) ) + return p; + } + if( pParent ) + return pParent->Find( rName ); + else + return NULL; +} + +// Suchen ueber ID-Nummer + +SbiSymDef* SbiSymPool::FindId( USHORT n ) const +{ + for( USHORT i = 0; i < aData.Count(); i++ ) + { + SbiSymDef* p = aData.GetObject( i ); + if( p->nId == n && ( !p->nProcId || ( p->nProcId == nProcId ) ) ) + return p; + } + if( pParent ) + return pParent->FindId( n ); + else + return NULL; +} + +// Suchen ueber Position (ab 0) + +SbiSymDef* SbiSymPool::Get( USHORT n ) const +{ + if( n >= aData.Count() ) + return NULL; + else + return aData.GetObject( n ); +} + +UINT32 SbiSymPool::Define( const String& rName ) +{ + SbiSymDef* p = Find( rName ); + if( p ) + { if( p->IsDefined() ) + pParser->Error( SbERR_LABEL_DEFINED, rName ); + } + else + p = AddSym( rName ); + return p->Define(); +} + +UINT32 SbiSymPool::Reference( const String& rName ) +{ + SbiSymDef* p = Find( rName ); + if( !p ) + p = AddSym( rName ); + //Sicherheitshalber + pParser->aGen.GenStmnt(); + return p->Reference(); +} + +// Alle offenen Referenzen anmaulen + +void SbiSymPool::CheckRefs() +{ + for( USHORT i = 0; i < aData.Count(); i++ ) + { + SbiSymDef* p = aData.GetObject( i ); + if( !p->IsDefined() ) + pParser->Error( SbERR_UNDEF_LABEL, p->GetName() ); + } +} + +/*************************************************************************** +|* +|* Symbol-Definitionen +|* +***************************************************************************/ + +SbiSymDef::SbiSymDef( const String& rName ) : aName( rName ) +{ + eType = SbxEMPTY; + nDims = 0; + nTypeId = 0; + nProcId = 0; + nId = 0; + nPos = 0; + nLen = 0; + nChain = 0; + bAs = + bNew = + bStatic = + bOpt = + bParamArray = + bByVal = + bChained = + bGlobal = FALSE; + pIn = + pPool = NULL; + nDefaultId = 0; +} + +SbiSymDef::~SbiSymDef() +{ + delete pPool; +} + +SbiProcDef* SbiSymDef::GetProcDef() +{ + return NULL; +} + +SbiConstDef* SbiSymDef::GetConstDef() +{ + return NULL; +} + +// Wenn der Name benoetigt wird, den aktuellen Namen +// aus dem Stringpool nehmen + +const String& SbiSymDef::GetName() +{ + if( pIn ) + aName = pIn->rStrings.Find( nId ); + return aName; +} + +// Eintragen eines Datentyps + +void SbiSymDef::SetType( SbxDataType t ) +{ + if( t == SbxVARIANT && pIn ) + { + sal_Unicode cu = aName.GetBuffer()[0]; + if( cu < 256 ) + { + char ch = (char)aName.GetBuffer()[0]; + if( ch == '_' ) ch = 'Z'; + int ch2 = toupper( ch ); + unsigned char c = (unsigned char)ch2; + if( c > 0 && c < 128 ) + t = pIn->pParser->eDefTypes[ ch2 - 'A' ]; + } + } + eType = t; +} + +// Aufbau einer Backchain, falls noch nicht definiert +// Es wird der Wert zurueckgeliefert, der als Operand gespeichert +// werden soll. + +UINT32 SbiSymDef::Reference() +{ + if( !bChained ) + { + UINT32 n = nChain; + nChain = pIn->pParser->aGen.GetOffset(); + return n; + } + else return nChain; +} + +// Definition eines Symbols. +// Hier wird der Backchain aufgeloest, falls vorhanden + +UINT32 SbiSymDef::Define() +{ + UINT32 n = pIn->pParser->aGen.GetPC(); + pIn->pParser->aGen.GenStmnt(); + if( nChain ) pIn->pParser->aGen.BackChain( nChain ); + nChain = n; + bChained = TRUE; + return nChain; +} + +// Eine Symboldefinition kann einen eigenen Pool haben. Dies ist +// der Fall bei Objekten und Prozeduren (lokale Variable) + +SbiSymPool& SbiSymDef::GetPool() +{ + if( !pPool ) + pPool = new SbiSymPool( pIn->pParser->aGblStrings, SbLOCAL ); // wird gedumpt + return *pPool; +} + +SbiSymScope SbiSymDef::GetScope() const +{ + return pIn ? pIn->GetScope() : SbLOCAL; +} + +//////////////////////////////////////////////////////////////////////////// + +// Die Prozedur-Definition hat drei Pools: +// 1) aParams: wird durch die Definition gefuellt. Enthaelt die Namen +// der Parameter, wie sie innerhalb des Rumpfes verwendet werden. +// Das erste Element ist der Returnwert. +// 2) pPool: saemtliche lokale Variable +// 3) aLabels: Labels + +SbiProcDef::SbiProcDef( SbiParser* pParser, const String& rName, + BOOL bProcDecl ) + : SbiSymDef( rName ) + , aParams( pParser->aGblStrings, SbPARAM ) // wird gedumpt + , aLabels( pParser->aLclStrings, SbLOCAL ) // wird nicht gedumpt + , mbProcDecl( bProcDecl ) +{ + aParams.SetParent( &pParser->aPublics ); + pPool = new SbiSymPool( pParser->aGblStrings, SbLOCAL ); // Locals + pPool->SetParent( &aParams ); + nLine1 = + nLine2 = 0; + mePropMode = PROPERTY_MODE_NONE; + bPublic = TRUE; + bCdecl = FALSE; + bStatic = FALSE; + // Fuer Returnwerte ist das erste Element der Parameterliste + // immer mit dem Namen und dem Typ der Proc definiert + aParams.AddSym( aName ); +} + +SbiProcDef::~SbiProcDef() +{} + +SbiProcDef* SbiProcDef::GetProcDef() +{ + return this; +} + +void SbiProcDef::SetType( SbxDataType t ) +{ + SbiSymDef::SetType( t ); + aParams.Get( 0 )->SetType( eType ); +} + +// Match mit einer Forward-Deklaration +// Falls der Match OK ist, wird pOld durch this im Pool ersetzt +// pOld wird immer geloescht! + +void SbiProcDef::Match( SbiProcDef* pOld ) +{ + SbiSymDef* po, *pn=NULL; + // Parameter 0 ist der Funktionsname + USHORT i; + for( i = 1; i < aParams.GetSize(); i++ ) + { + po = pOld->aParams.Get( i ); + pn = aParams.Get( i ); + // Kein Typabgleich; das wird beim Laufen erledigt + // aber ist sie evtl. mit zu wenigen Parametern aufgerufen + // worden? + if( !po && !pn->IsOptional() && !pn->IsParamArray() ) + break; + po = pOld->aParams.Next(); + } + // Wurden zu viele Parameter angegeben? + if( pn && i < aParams.GetSize() && pOld->pIn ) + { + // Die ganze Zeile markieren + pOld->pIn->GetParser()->SetCol1( 0 ); + pOld->pIn->GetParser()->Error( SbERR_BAD_DECLARATION, aName ); + } + if( !pIn && pOld->pIn ) + { + // Alten Eintrag durch neuen ersetzen + SbiSymDef** pData = (SbiSymDef**) pOld->pIn->aData.GetData(); + pData[ pOld->nPos ] = this; + nPos = pOld->nPos; + nId = pOld->nId; + pIn = pOld->pIn; + } + delete pOld; +} + +void SbiProcDef::setPropertyMode( PropertyMode ePropMode ) +{ + mePropMode = ePropMode; + if( mePropMode != PROPERTY_MODE_NONE ) + { + // Prop name = original scanned procedure name + maPropName = aName; + + // CompleteProcName includes "Property xxx " + // to avoid conflicts with other symbols + String aCompleteProcName; + aCompleteProcName.AppendAscii( "Property " ); + switch( mePropMode ) + { + case PROPERTY_MODE_GET: aCompleteProcName.AppendAscii( "Get " ); break; + case PROPERTY_MODE_LET: aCompleteProcName.AppendAscii( "Let " ); break; + case PROPERTY_MODE_SET: aCompleteProcName.AppendAscii( "Set " ); break; + case PROPERTY_MODE_NONE: + DBG_ERROR( "Illegal PropertyMode PROPERTY_MODE_NONE" ); + break; + } + aCompleteProcName += aName; + aName = aCompleteProcName; + } +} + + +////////////////////////////////////////////////////////////////////////// + +SbiConstDef::SbiConstDef( const String& rName ) + : SbiSymDef( rName ) +{ + nVal = 0; eType = SbxINTEGER; +} + +void SbiConstDef::Set( double n, SbxDataType t ) +{ + aVal.Erase(); nVal = n; eType = t; +} + +void SbiConstDef::Set( const String& n ) +{ + aVal = n; nVal = 0; eType = SbxSTRING; +} + +SbiConstDef::~SbiConstDef() +{} + +SbiConstDef* SbiConstDef::GetConstDef() +{ + return this; +} + diff --git a/basic/source/comp/token.cxx b/basic/source/comp/token.cxx new file mode 100644 index 000000000000..9fdfef0490b1 --- /dev/null +++ b/basic/source/comp/token.cxx @@ -0,0 +1,698 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_basic.hxx" + +#include <ctype.h> +#include "sbcomp.hxx" + +struct TokenTable { SbiToken t; const char *s; }; + +static short nToken; // Anzahl der Tokens + +static TokenTable* pTokTable; + +static TokenTable aTokTable_Basic [] = { // Token-Tabelle: + + { CAT, "&" }, + { MUL, "*" }, + { PLUS, "+" }, + { MINUS, "-" }, + { DIV, "/" }, + { EOS, ":" }, + { ASSIGN, ":=" }, + { LT, "<" }, + { LE, "<=" }, + { NE, "<>" }, + { EQ, "=" }, + { GT, ">" }, + { GE, ">=" }, + { ACCESS, "Access" }, + { ALIAS, "Alias" }, + { AND, "And" }, + { ANY, "Any" }, + { APPEND, "Append" }, + { AS, "As" }, + { BASE, "Base" }, + { BINARY, "Binary" }, + { TBOOLEAN, "Boolean" }, + { BYREF, "ByRef", }, + { TBYTE, "Byte", }, + { BYVAL, "ByVal", }, + { CALL, "Call" }, + { CASE, "Case" }, + { _CDECL_, "Cdecl" }, + { CLASSMODULE, "ClassModule" }, + { CLOSE, "Close" }, + { COMPARE, "Compare" }, + { COMPATIBLE,"Compatible" }, + { _CONST_, "Const" }, + { TCURRENCY,"Currency" }, + { TDATE, "Date" }, + { DECLARE, "Declare" }, + { DEFBOOL, "DefBool" }, + { DEFCUR, "DefCur" }, + { DEFDATE, "DefDate" }, + { DEFDBL, "DefDbl" }, + { DEFERR, "DefErr" }, + { DEFINT, "DefInt" }, + { DEFLNG, "DefLng" }, + { DEFOBJ, "DefObj" }, + { DEFSNG, "DefSng" }, + { DEFSTR, "DefStr" }, + { DEFVAR, "DefVar" }, + { DIM, "Dim" }, + { DO, "Do" }, + { TDOUBLE, "Double" }, + { EACH, "Each" }, + { ELSE, "Else" }, + { ELSEIF, "ElseIf" }, + { END, "End" }, + { ENDENUM, "End Enum" }, + { ENDFUNC, "End Function" }, + { ENDIF, "End If" }, + { ENDPROPERTY, "End Property" }, + { ENDSELECT,"End Select" }, + { ENDSUB, "End Sub" }, + { ENDTYPE, "End Type" }, + { ENDIF, "EndIf" }, + { ENUM, "Enum" }, + { EQV, "Eqv" }, + { ERASE, "Erase" }, + { _ERROR_, "Error" }, + { EXIT, "Exit" }, + { EXPLICIT, "Explicit" }, + { FOR, "For" }, + { FUNCTION, "Function" }, + { GET, "Get" }, + { GLOBAL, "Global" }, + { GOSUB, "GoSub" }, + { GOTO, "GoTo" }, + { IF, "If" }, + { IMP, "Imp" }, + { IMPLEMENTS, "Implements" }, + { _IN_, "In" }, + { INPUT, "Input" }, // auch INPUT # + { TINTEGER, "Integer" }, + { IS, "Is" }, + { LET, "Let" }, + { LIB, "Lib" }, + { LIKE, "Like" }, + { LINE, "Line" }, + { LINEINPUT,"Line Input" }, + { LOCAL, "Local" }, + { LOCK, "Lock" }, + { TLONG, "Long" }, + { LOOP, "Loop" }, + { LPRINT, "LPrint" }, + { LSET, "LSet" }, // JSM + { MOD, "Mod" }, + { NAME, "Name" }, + { NEW, "New" }, + { NEXT, "Next" }, + { NOT, "Not" }, + { TOBJECT, "Object" }, + { ON, "On" }, + { OPEN, "Open" }, + { OPTION, "Option" }, + { _OPTIONAL_, "Optional" }, + { OR, "Or" }, + { OUTPUT, "Output" }, + { PARAMARRAY, "ParamArray" }, + { PRESERVE, "Preserve" }, + { PRINT, "Print" }, + { PRIVATE, "Private" }, + { PROPERTY, "Property" }, + { PUBLIC, "Public" }, + { RANDOM, "Random" }, + { READ, "Read" }, + { REDIM, "ReDim" }, + { REM, "Rem" }, + { RESUME, "Resume" }, + { RETURN, "Return" }, + { RSET, "RSet" }, // JSM + { SELECT, "Select" }, + { SET, "Set" }, +#ifdef SHARED +#undef SHARED +#define tmpSHARED +#endif + { SHARED, "Shared" }, +#ifdef tmpSHARED +#define SHARED +#undef tmpSHARED +#endif + { TSINGLE, "Single" }, + { STATIC, "Static" }, + { STEP, "Step" }, + { STOP, "Stop" }, + { TSTRING, "String" }, + { SUB, "Sub" }, + { STOP, "System" }, + { TEXT, "Text" }, + { THEN, "Then" }, + { TO, "To", }, + { TYPE, "Type" }, + { TYPEOF, "TypeOf" }, + { UNTIL, "Until" }, + { TVARIANT, "Variant" }, + { VBASUPPORT, "VbaSupport" }, + { WEND, "Wend" }, + { WHILE, "While" }, + { WITH, "With" }, + { WRITE, "Write" }, // auch WRITE # + { XOR, "Xor" }, + { NIL, "" } +}; + +/* +TokenTable aTokTable_Java [] = { // Token-Tabelle: + + { JS_LOG_NOT, "!" }, + { JS_NE, "!=" }, + { JS_MOD, "%" }, + { JS_ASS_MOD, "%=" }, + { JS_BIT_AND, "&" }, + { JS_LOG_AND, "&&" }, + { JS_ASS_AND, "&=" }, + { JS_LPAREN, "(" }, + { JS_RPAREN, ")" }, + { JS_MUL, "*" }, + { JS_ASS_MUL, "*=" }, + { JS_PLUS, "+" }, + { JS_INC, "++" }, + { JS_ASS_PLUS, "+=" }, + { JS_COMMA, "," }, + { JS_MINUS, "-" }, + { JS_DEC, "--" }, + { JS_ASS_MINUS, "-=" }, + { JS_DIV, "/" }, + { JS_ASS_DIV, "/=" }, + { JS_COND_SEL, ":" }, + { JS_LT, "<" }, + { JS_LSHIFT, "<<" }, + { JS_ASS_LSHIFT,"<<=" }, + { JS_LE, "<=" }, + { JS_NE, "<>" }, + { JS_ASSIGNMENT,"=" }, + { JS_EQ, "==" }, + { JS_GT, ">" }, + { JS_RSHIFT, ">>" }, + { JS_ASS_RSHIFT,">>=" }, + { JS_RSHIFT_Z, ">>>" }, + { JS_ASS_RSHIFT_Z,">>>=" }, + { JS_GE, ">=" }, + { JS_COND_QUEST,"?" }, + { ACCESS, "Access" }, + { ALIAS, "Alias" }, + { AND, "And" }, + { ANY, "Any" }, + { APPEND, "Append" }, + { AS, "As" }, + { BASE, "Base" }, + { BINARY, "Binary" }, + { TBOOLEAN, "Boolean" }, + { BYVAL, "ByVal", }, + { CALL, "Call" }, + { CASE, "Case" }, + { _CDECL_, "Cdecl" }, + { CLOSE, "Close" }, + { COMPARE, "Compare" }, + { _CONST_, "Const" }, + { TCURRENCY,"Currency" }, + { TDATE, "Date" }, + { DECLARE, "Declare" }, + { DEFBOOL, "DefBool" }, + { DEFCUR, "DefCur" }, + { DEFDATE, "DefDate" }, + { DEFDBL, "DefDbl" }, + { DEFERR, "DefErr" }, + { DEFINT, "DefInt" }, + { DEFLNG, "DefLng" }, + { DEFOBJ, "DefObj" }, + { DEFSNG, "DefSng" }, + { DEFSTR, "DefStr" }, + { DEFVAR, "DefVar" }, + { DIM, "Dim" }, + { DO, "Do" }, + { TDOUBLE, "Double" }, + { EACH, "Each" }, + { ELSE, "Else" }, + { ELSEIF, "ElseIf" }, + { END, "End" }, + { ENDFUNC, "End Function" }, + { ENDIF, "End If" }, + { ENDSELECT,"End Select" }, + { ENDSUB, "End Sub" }, + { ENDTYPE, "End Type" }, + { ENDIF, "EndIf" }, + { EQV, "Eqv" }, + { ERASE, "Erase" }, + { _ERROR_, "Error" }, + { EXIT, "Exit" }, + { EXPLICIT, "Explicit" }, + { FOR, "For" }, + { FUNCTION, "Function" }, + { GLOBAL, "Global" }, + { GOSUB, "GoSub" }, + { GOTO, "GoTo" }, + { IF, "If" }, + { IMP, "Imp" }, + { _IN_, "In" }, + { INPUT, "Input" }, // auch INPUT # + { TINTEGER, "Integer" }, + { IS, "Is" }, + { LET, "Let" }, + { LIB, "Lib" }, + { LINE, "Line" }, + { LINEINPUT,"Line Input" }, + { LOCAL, "Local" }, + { LOCK, "Lock" }, + { TLONG, "Long" }, + { LOOP, "Loop" }, + { LPRINT, "LPrint" }, + { LSET, "LSet" }, // JSM + { MOD, "Mod" }, + { NAME, "Name" }, + { NEW, "New" }, + { NEXT, "Next" }, + { NOT, "Not" }, + { TOBJECT, "Object" }, + { ON, "On" }, + { OPEN, "Open" }, + { OPTION, "Option" }, + { _OPTIONAL_, "Optional" }, + { OR, "Or" }, + { OUTPUT, "Output" }, + { PRESERVE, "Preserve" }, + { PRINT, "Print" }, + { PRIVATE, "Private" }, + { PUBLIC, "Public" }, + { RANDOM, "Random" }, + { READ, "Read" }, + { REDIM, "ReDim" }, + { REM, "Rem" }, + { RESUME, "Resume" }, + { RETURN, "Return" }, + { RSET, "RSet" }, // JSM + { SELECT, "Select" }, + { SET, "Set" }, + { SHARED, "Shared" }, + { TSINGLE, "Single" }, + { STATIC, "Static" }, + { STEP, "Step" }, + { STOP, "Stop" }, + { TSTRING, "String" }, + { SUB, "Sub" }, + { STOP, "System" }, + { TEXT, "Text" }, + { THEN, "Then" }, + { TO, "To", }, + { TYPE, "Type" }, + { UNTIL, "Until" }, + { TVARIANT, "Variant" }, + { WEND, "Wend" }, + { WHILE, "While" }, + { WITH, "With" }, + { WRITE, "Write" }, // auch WRITE # + { XOR, "Xor" }, + { JS_LINDEX, "[" }, + { JS_RINDEX, "]" }, + { JS_BIT_XOR, "^" }, + { JS_ASS_XOR, "^=" }, + { JS_BIT_OR, "|" }, + { JS_ASS_OR, "|=" }, + { JS_LOG_OR, "||" }, + { JS_BIT_NOT, "~" }, + { NIL } +}; +*/ + +// Der Konstruktor ermittelt die Laenge der Token-Tabelle. + +SbiTokenizer::SbiTokenizer( const ::rtl::OUString& rSrc, StarBASIC* pb ) + : SbiScanner( rSrc, pb ) +{ + pTokTable = aTokTable_Basic; + //if( StarBASIC::GetGlobalLanguageMode() == SB_LANG_JAVASCRIPT ) + // pTokTable = aTokTable_Java; + TokenTable *tp; + bEof = bAs = FALSE; + eCurTok = NIL; + ePush = NIL; + bEos = bKeywords = bErrorIsSymbol = TRUE; + if( !nToken ) + for( nToken = 0, tp = pTokTable; tp->t; nToken++, tp++ ) {} +} + +SbiTokenizer::~SbiTokenizer() +{} + +// Wiederablage (Pushback) eines Tokens. (Bis zu 2 Tokens) + +void SbiTokenizer::Push( SbiToken t ) +{ + if( ePush != NIL ) + Error( SbERR_INTERNAL_ERROR, "PUSH" ); + else ePush = t; +} + +void SbiTokenizer::Error( SbError code, const char* pMsg ) +{ + aError = String::CreateFromAscii( pMsg ); + Error( code ); +} + +void SbiTokenizer::Error( SbError code, String aMsg ) +{ + aError = aMsg; + Error( code ); +} + +void SbiTokenizer::Error( SbError code, SbiToken tok ) +{ + aError = Symbol( tok ); + Error( code ); +} + +// Einlesen des naechsten Tokens, ohne dass das Token geschluckt wird + +SbiToken SbiTokenizer::Peek() +{ + if( ePush == NIL ) + { + USHORT nOldLine = nLine; + USHORT nOldCol1 = nCol1; + USHORT nOldCol2 = nCol2; + ePush = Next(); + nPLine = nLine; nLine = nOldLine; + nPCol1 = nCol1; nCol1 = nOldCol1; + nPCol2 = nCol2; nCol2 = nOldCol2; + } + return eCurTok = ePush; +} + +// Dies ist fuer die Decompilation. +// Zahlen und Symbole liefern einen Leerstring zurueck. + +const String& SbiTokenizer::Symbol( SbiToken t ) +{ + // Zeichen-Token? + if( t < FIRSTKWD ) + { + aSym = (char) t; + return aSym; + } + switch( t ) + { + case NEG : aSym = '-'; return aSym; + case EOS : aSym = String::CreateFromAscii( ":/CRLF" ); return aSym; + case EOLN : aSym = String::CreateFromAscii( "CRLF" ); return aSym; + default: break; + } + TokenTable* tp = pTokTable; + for( short i = 0; i < nToken; i++, tp++ ) + { + if( tp->t == t ) + { + aSym = String::CreateFromAscii( tp->s ); + return aSym; + } + } + const sal_Unicode *p = aSym.GetBuffer(); + if (*p <= ' ') aSym = String::CreateFromAscii( "???" ); + return aSym; +} + +// Einlesen des naechsten Tokens und Ablage desselben +// Tokens, die nicht in der Token-Tabelle vorkommen, werden +// direkt als Zeichen zurueckgeliefert. +// Einige Worte werden gesondert behandelt. + +SbiToken SbiTokenizer::Next() +{ + if (bEof) return EOLN; + // Schon eines eingelesen? + if( ePush != NIL ) + { + eCurTok = ePush; + ePush = NIL; + nLine = nPLine; + nCol1 = nPCol1; + nCol2 = nPCol2; + bEos = IsEoln( eCurTok ); + return eCurTok; + } + TokenTable *tp; + + // Sonst einlesen: + if( !NextSym() ) + { + bEof = bEos = TRUE; + return eCurTok = EOLN; + } + // Zeilenende? + if( aSym.GetBuffer()[0] == '\n' ) + { + bEos = TRUE; return eCurTok = EOLN; + } + bEos = FALSE; + + // Zahl? + if( bNumber ) + return eCurTok = NUMBER; + + // String? + else if( ( eScanType == SbxDATE || eScanType == SbxSTRING ) && !bSymbol ) + return eCurTok = FIXSTRING; + // Sonderfaelle von Zeichen, die zwischen "Z" und "a" liegen. ICompare() + // wertet die Position dieser Zeichen unterschiedlich aus. + else if( aSym.GetBuffer()[0] == '^' ) + return eCurTok = EXPON; + else if( aSym.GetBuffer()[0] == '\\' ) + return eCurTok = IDIV; + else + { + // Mit Typkennung oder ein Symbol und keine Keyword-Erkennung? + // Dann kein Token-Test + if( eScanType != SbxVARIANT + || ( !bKeywords && bSymbol ) ) + return eCurTok = SYMBOL; + // Gueltiges Token? + short lb = 0; + short ub = nToken-1; + short delta; + do + { + delta = (ub - lb) >> 1; + tp = &pTokTable[ lb + delta ]; + StringCompare res = aSym.CompareIgnoreCaseToAscii( tp->s ); + // Gefunden? + if( res == COMPARE_EQUAL ) goto special; + // Groesser? Dann untere Haelfte + if( res == COMPARE_LESS ) + { + if ((ub - lb) == 2) ub = lb; + else ub = ub - delta; + } + // Kleiner? Dann obere Haelfte + else + { + if ((ub -lb) == 2) lb = ub; + else lb = lb + delta; + } + } while( delta ); + // Symbol? Wenn nicht >= Token + sal_Unicode ch = aSym.GetBuffer()[0]; + if( !BasicSimpleCharClass::isAlpha( ch, bCompatible ) && !bSymbol ) + return eCurTok = (SbiToken) (ch & 0x00FF); + return eCurTok = SYMBOL; + } +special: + // LINE INPUT + if( tp->t == LINE ) + { + short nC1 = nCol1; + String aOldSym = aSym; + eCurTok = Peek(); + if( eCurTok == INPUT ) + { + Next(); + nCol1 = nC1; + return eCurTok = LINEINPUT; + } + else + { + aSym = aOldSym; + return eCurTok = LINE; + } + } + // END IF, CASE, SUB, DEF, FUNCTION, TYPE, CLASS, WITH + if( tp->t == END ) + { + // AB, 15.3.96, Spezialbehandlung fuer END, beim Peek() geht die + // aktuelle Zeile verloren, daher alles merken und danach restaurieren + USHORT nOldLine = nLine; + USHORT nOldCol = nCol; + USHORT nOldCol1 = nCol1; + USHORT nOldCol2 = nCol2; + String aOldSym = aSym; + SaveLine(); // pLine im Scanner sichern + + eCurTok = Peek(); + switch( eCurTok ) + { + case IF: Next(); eCurTok = ENDIF; break; + case SELECT: Next(); eCurTok = ENDSELECT; break; + case SUB: Next(); eCurTok = ENDSUB; break; + case FUNCTION: Next(); eCurTok = ENDFUNC; break; + case PROPERTY: Next(); eCurTok = ENDPROPERTY; break; + case TYPE: Next(); eCurTok = ENDTYPE; break; + case ENUM: Next(); eCurTok = ENDENUM; break; + case WITH: Next(); eCurTok = ENDWITH; break; + default : eCurTok = END; + } + nCol1 = nOldCol1; + if( eCurTok == END ) + { + // Alles zuruecksetzen, damit Token nach END ganz neu gelesen wird + ePush = NIL; + nLine = nOldLine; + nCol = nOldCol; + nCol2 = nOldCol2; + aSym = aOldSym; + RestoreLine(); // pLine im Scanner restaurieren + } + return eCurTok; + } + // Sind Datentypen Keywords? + // Nur nach AS, sonst sind es Symbole! + // Es gibt ja ERROR(), DATA(), STRING() etc. + eCurTok = tp->t; + // AS: Datentypen sind Keywords + if( tp->t == AS ) + bAs = TRUE; + else + { + if( bAs ) + bAs = FALSE; + else if( eCurTok >= DATATYPE1 && eCurTok <= DATATYPE2 && (bErrorIsSymbol || eCurTok != _ERROR_) ) + eCurTok = SYMBOL; + } + + // CLASSMODULE, PROPERTY, GET, ENUM token only visible in compatible mode + SbiToken eTok = tp->t; + if( bCompatible ) + { + // #129904 Suppress system + if( eTok == STOP && aSym.CompareIgnoreCaseToAscii( "system" ) == COMPARE_EQUAL ) + eCurTok = SYMBOL; + } + else + { + if( eTok == CLASSMODULE || + eTok == IMPLEMENTS || + eTok == PARAMARRAY || + eTok == ENUM || + eTok == PROPERTY || + eTok == GET || + eTok == TYPEOF ) + { + eCurTok = SYMBOL; + } + } + + bEos = IsEoln( eCurTok ); + return eCurTok; +} + +#ifdef _MSC_VER +#pragma optimize("",off) +#endif + +// Kann das aktuell eingelesene Token ein Label sein? + +BOOL SbiTokenizer::MayBeLabel( BOOL bNeedsColon ) +{ + if( eCurTok == SYMBOL ) + return bNeedsColon ? DoesColonFollow() : TRUE; + else + return BOOL( eCurTok == NUMBER + && eScanType == SbxINTEGER + && nVal >= 0 ); +} + +#ifdef _MSC_VER +#pragma optimize("",off) +#endif + + +void SbiTokenizer::Hilite( SbTextPortions& rList ) +{ + bErrors = FALSE; + bUsedForHilite = TRUE; + SbiToken eLastTok = NIL; + for( ;; ) + { + Next(); + if( IsEof() ) + break; + SbTextPortion aRes; + aRes.nLine = nLine; + aRes.nStart = nCol1; + aRes.nEnd = nCol2; + switch( eCurTok ) + { + case REM: + aRes.eType = SB_COMMENT; break; + case SYMBOL: + aRes.eType = SB_SYMBOL; break; + case FIXSTRING: + aRes.eType = SB_STRING; break; + case NUMBER: + aRes.eType = SB_NUMBER; break; + default: + if( ( eCurTok >= FIRSTKWD && eCurTok <= LASTKWD ) + || (eCurTok >= _CDECL_ ) ) + aRes.eType = SB_KEYWORD; + else + aRes.eType = SB_PUNCTUATION; + } + // Die Folge xxx.Keyword sollte nicht als Kwd geflagt werden + if( aRes.eType == SB_KEYWORD + && ( eLastTok == DOT|| eLastTok == EXCLAM ) ) + aRes.eType = SB_SYMBOL; + if( eCurTok != EOLN && aRes.nStart <= aRes.nEnd ) + rList.Insert( aRes, rList.Count() ); + if( aRes.eType == SB_COMMENT ) + break; + eLastTok = eCurTok; + } + bUsedForHilite = FALSE; +} + |