diff options
Diffstat (limited to 'basic/source/comp')
-rw-r--r-- | basic/source/comp/buffer.cxx | 252 | ||||
-rw-r--r-- | basic/source/comp/codegen.cxx | 537 | ||||
-rw-r--r-- | basic/source/comp/dim.cxx | 1224 | ||||
-rw-r--r-- | basic/source/comp/exprgen.cxx | 272 | ||||
-rw-r--r-- | basic/source/comp/exprnode.cxx | 488 | ||||
-rw-r--r-- | basic/source/comp/exprtree.cxx | 1243 | ||||
-rw-r--r-- | basic/source/comp/io.cxx | 334 | ||||
-rw-r--r-- | basic/source/comp/loops.cxx | 557 | ||||
-rwxr-xr-x | basic/source/comp/makefile.mk | 57 | ||||
-rw-r--r-- | basic/source/comp/parser.cxx | 872 | ||||
-rw-r--r-- | basic/source/comp/sbcomp.cxx | 303 | ||||
-rw-r--r-- | basic/source/comp/scanner.cxx | 612 | ||||
-rw-r--r-- | basic/source/comp/symtbl.cxx | 537 | ||||
-rw-r--r-- | basic/source/comp/token.cxx | 553 |
14 files changed, 7841 insertions, 0 deletions
diff --git a/basic/source/comp/buffer.cxx b/basic/source/comp/buffer.cxx new file mode 100644 index 000000000000..2e0fb542d3c2 --- /dev/null +++ b/basic/source/comp/buffer.cxx @@ -0,0 +1,252 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * 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 sal_uInt32 UP_LIMIT=0xFFFFFF00L; + +// The SbiBuffer will be expanded in increments of at least 16 Bytes. +// This is necessary, because many classes emanate from a buffer length +// of x*16 Bytes. + +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; +} + +// Reach out the buffer +// This lead to the deletion of the buffer! + +char* SbiBuffer::GetBuffer() +{ + char* p = pBuf; + pBuf = NULL; + pCur = NULL; + return p; +} + +// Test, if the buffer can contain n Bytes. +// In case of doubt it will be enlarged + +sal_Bool SbiBuffer::Check( sal_uInt16 n ) +{ + if( !n ) return sal_True; + if( ( static_cast<sal_uInt32>( nOff )+ n ) > static_cast<sal_uInt32>( nSize ) ) + { + if( nInc == 0 ) + return sal_False; + sal_uInt16 nn = 0; + while( nn < n ) nn = nn + nInc; + char* p; + if( ( static_cast<sal_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 sal_False; + } + else + { + if( nSize ) memcpy( p, pBuf, nSize ); + delete[] pBuf; + pBuf = p; + pCur = pBuf + nOff; + nSize = nSize + nn; + } + } + return sal_True; +} + +// Conditioning of the buffer onto the passed Byte limit + +void SbiBuffer::Align( sal_Int32 n ) +{ + if( nOff % n ) { + sal_uInt32 nn =( ( nOff + n ) / n ) * n; + if( nn <= UP_LIMIT ) + { + nn = nn - nOff; + if( Check( static_cast<sal_uInt16>(nn) ) ) + { + memset( pCur, 0, nn ); + pCur += nn; + nOff = nOff + nn; + } + } + } +} + +// Patch of a Location + +void SbiBuffer::Patch( sal_uInt32 off, sal_uInt32 val ) +{ + if( ( off + sizeof( sal_uInt32 ) ) < nOff ) + { + sal_uInt16 val1 = static_cast<sal_uInt16>( val & 0xFFFF ); + sal_uInt16 val2 = static_cast<sal_uInt16>( val >> 16 ); + sal_uInt8* p = (sal_uInt8*) pBuf + off; + *p++ = (char) ( val1 & 0xFF ); + *p++ = (char) ( val1 >> 8 ); + *p++ = (char) ( val2 & 0xFF ); + *p = (char) ( val2 >> 8 ); + } +} + +// Forward References upon label und procedures +// establish a linkage. The beginning of the linkage is at the passed parameter, +// the end of the linkage is 0. + +void SbiBuffer::Chain( sal_uInt32 off ) +{ + if( off && pBuf ) + { + sal_uInt8 *ip; + sal_uInt32 i = off; + sal_uInt32 val1 = (nOff & 0xFFFF); + sal_uInt32 val2 = (nOff >> 16); + do + { + ip = (sal_uInt8*) pBuf + i; + sal_uInt8* 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 ); + } +} + +sal_Bool SbiBuffer::operator +=( sal_Int8 n ) +{ + if( Check( 1 ) ) + { + *pCur++ = (char) n; nOff++; return sal_True; + } else return sal_False; +} + +sal_Bool SbiBuffer::operator +=( sal_uInt8 n ) +{ + if( Check( 1 ) ) + { + *pCur++ = (char) n; nOff++; return sal_True; + } else return sal_False; +} + +sal_Bool SbiBuffer::operator +=( sal_Int16 n ) +{ + if( Check( 2 ) ) + { + *pCur++ = (char) ( n & 0xFF ); + *pCur++ = (char) ( n >> 8 ); + nOff += 2; return sal_True; + } else return sal_False; +} + +sal_Bool SbiBuffer::operator +=( sal_uInt16 n ) +{ + if( Check( 2 ) ) + { + *pCur++ = (char) ( n & 0xFF ); + *pCur++ = (char) ( n >> 8 ); + nOff += 2; return sal_True; + } else return sal_False; +} + +sal_Bool SbiBuffer::operator +=( sal_uInt32 n ) +{ + if( Check( 4 ) ) + { + sal_uInt16 n1 = static_cast<sal_uInt16>( n & 0xFFFF ); + sal_uInt16 n2 = static_cast<sal_uInt16>( n >> 16 ); + if ( operator +=( n1 ) && operator +=( n2 ) ) + return sal_True; + return sal_True; + } + return sal_False; +} + +sal_Bool SbiBuffer::operator +=( sal_Int32 n ) +{ + return operator +=( (sal_uInt32) n ); +} + + +sal_Bool SbiBuffer::operator +=( const String& n ) +{ + sal_uInt16 l = n.Len() + 1; + if( Check( l ) ) + { + ByteString aByteStr( n, gsl_getSystemTextEncoding() ); + memcpy( pCur, aByteStr.GetBuffer(), l ); + pCur += l; + nOff = nOff + l; + return sal_True; + } + else return sal_False; +} + +sal_Bool SbiBuffer::Add( const void* p, sal_uInt16 len ) +{ + if( Check( len ) ) + { + memcpy( pCur, p, len ); + pCur += len; + nOff = nOff + len; + return sal_True; + } else return sal_False; +} + + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basic/source/comp/codegen.cxx b/basic/source/comp/codegen.cxx new file mode 100644 index 000000000000..249c37353c84 --- /dev/null +++ b/basic/source/comp/codegen.cxx @@ -0,0 +1,537 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * 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> +#include <algorithm> +#include <com/sun/star/script/ModuleType.hpp> + +// nInc is the increment size of the buffers + +SbiCodeGen::SbiCodeGen( SbModule& r, SbiParser* p, short nInc ) + : rMod( r ), aCode( p, nInc ) +{ + pParser = p; + bStmnt = sal_False; + nLine = 0; + nCol = 0; + nForLevel = 0; +} + +sal_uInt32 SbiCodeGen::GetPC() +{ + return aCode.GetSize(); +} + +// memorize the statement + +void SbiCodeGen::Statement() +{ + bStmnt = sal_True; + + nLine = pParser->GetLine(); + nCol = pParser->GetCol1(); + + // #29955 Store the information of the for-loop-layer + // in the uppper Byte of the column + nCol = (nCol & 0xff) + 0x100 * nForLevel; +} + +// Mark the beginning of a statement + +void SbiCodeGen::GenStmnt() +{ + if( bStmnt ) + { + bStmnt = sal_False; + Gen( _STMNT, nLine, nCol ); + } +} + +// The Gen-Routines return the offset of the 1. operand, +// so that jumps can sink their backchain there. + +sal_uInt32 SbiCodeGen::Gen( SbiOpcode eOpcode ) +{ +#ifdef DBG_UTIL + if( eOpcode < SbOP0_START || eOpcode > SbOP0_END ) + pParser->Error( SbERR_INTERNAL_ERROR, "OPCODE1" ); +#endif + GenStmnt(); + aCode += (sal_uInt8) eOpcode; + return GetPC(); +} + +sal_uInt32 SbiCodeGen::Gen( SbiOpcode eOpcode, sal_uInt32 nOpnd ) +{ +#ifdef DBG_UTIL + if( eOpcode < SbOP1_START || eOpcode > SbOP1_END ) + pParser->Error( SbERR_INTERNAL_ERROR, "OPCODE2" ); +#endif + GenStmnt(); + aCode += (sal_uInt8) eOpcode; + sal_uInt32 n = GetPC(); + aCode += nOpnd; + return n; +} + +sal_uInt32 SbiCodeGen::Gen( SbiOpcode eOpcode, sal_uInt32 nOpnd1, sal_uInt32 nOpnd2 ) +{ +#ifdef DBG_UTIL + if( eOpcode < SbOP2_START || eOpcode > SbOP2_END ) + pParser->Error( SbERR_INTERNAL_ERROR, "OPCODE3" ); +#endif + GenStmnt(); + aCode += (sal_uInt8) eOpcode; + sal_uInt32 n = GetPC(); + aCode += nOpnd1; + aCode += nOpnd2; + return n; +} + +// Storing of the created image in the module + +void SbiCodeGen::Save() +{ + SbiImage* p = new SbiImage; + rMod.StartDefinitions(); + // OPTION BASE-Value: + p->nDimBase = pParser->nBase; + // OPTION take over the EXPLICIT-Flag + if( pParser->bExplicit ) + p->SetFlag( SBIMG_EXPLICIT ); + + int nIfaceCount = 0; + if( rMod.mnType == com::sun::star::script::ModuleType::CLASS ) + { + OSL_TRACE("COdeGen::save() classmodule processing"); + rMod.bIsProxyModule = true; + p->SetFlag( SBIMG_CLASSMODULE ); + pCLASSFAC->AddClassModule( &rMod ); + + nIfaceCount = pParser->aIfaceVector.size(); + if( !rMod.pClassData ) + rMod.pClassData = new SbClassData; + if( nIfaceCount ) + { + 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() ); + } + } + + rMod.pClassData->maRequiredTypes = pParser->aRequiredTypes; + } + else + { + pCLASSFAC->RemoveClassModule( &rMod ); + // Only a ClassModule can revert to Normal + if ( rMod.mnType == com::sun::star::script::ModuleType::CLASS ) + rMod.mnType = com::sun::star::script::ModuleType::NORMAL; + rMod.bIsProxyModule = false; + } + + // 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; + sal_uInt16 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( sal_uInt16 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: + OSL_FAIL( "Illegal PropertyMode PROPERTY_MODE_NONE" ); + break; + } + String aPropName = pProc->GetPropName(); + if( nPass == 1 ) + aPropName = aPropName.Copy( aIfaceName.Len() + 1 ); + OSL_TRACE("*** getProcedureProperty for thing %s", + rtl::OUStringToOString( aPropName,RTL_TEXTENCODING_UTF8 ).getStr() ); + rMod.GetProcedureProperty( aPropName, ePropType ); + } + if( nPass == 1 ) + { + rMod.GetIfaceMapperMethod( aProcName, pMeth ); + } + else + { + pMeth = rMod.GetMethod( aProcName, pProc->GetType() ); + + if( !pProc->IsPublic() ) + pMeth->SetFlag( SBX_PRIVATE ); + + // Declare? -> Hidden + if( pProc->GetLib().Len() > 0 ) + pMeth->SetFlag( SBX_HIDDEN ); + + pMeth->nStart = pProc->GetAddr(); + pMeth->nLine1 = pProc->GetLine1(); + pMeth->nLine2 = pProc->GetLine2(); + // The parameter: + SbxInfo* pInfo = pMeth->GetInfo(); + String aHelpFile, aComment; + sal_uIntPtr nHelpId = 0; + if( pInfo ) + { + // Rescue the additional data + aHelpFile = pInfo->GetHelpFile(); + aComment = pInfo->GetComment(); + nHelpId = pInfo->GetHelpId(); + } + // And reestablish the parameter list + pInfo = new SbxInfo( aHelpFile, nHelpId ); + pInfo->SetComment( aComment ); + SbiSymPool* pPool = &pProc->GetParams(); + // The first element is always the value of the function! + for( sal_uInt16 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 hand-over an Optional-Info + sal_uInt16 nFlags = SBX_READ; + if( pPar->IsOptional() ) + nFlags |= SBX_OPTIONAL; + + pInfo->AddParam( pPar->GetName(), t, nFlags ); + + sal_uInt32 nUserData = 0; + sal_uInt16 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... + } + } + // The code + p->AddCode( aCode.GetBuffer(), aCode.GetSize() ); + + // The global StringPool. 0 is not occupied. + SbiStringPool* pPool = &pParser->aGblStrings; + sal_uInt16 nSize = pPool->GetSize(); + p->MakeStrings( nSize ); + sal_uInt16 i; + for( i = 1; i <= nSize; i++ ) + p->AddString( pPool->Find( i ) ); + + // Insert types + sal_uInt16 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( sal_uInt8* 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; + sal_uInt8* m_pCode; + T readParam( sal_uInt8*& pCode ) + { + short nBytes = sizeof( T ); + T nOp1=0; + for ( int i=0; i<nBytes; ++i ) + nOp1 |= *pCode++ << ( i * 8); + return nOp1; + } +public: + PCodeBufferWalker( sal_uInt8* pCode, T nBytes ): m_nBytes( nBytes ), m_pCode( pCode ) + { + } + void visitBuffer( PCodeVisitor< T >& visitor ) + { + sal_uInt8* pCode = m_pCode; + if ( !pCode ) + return; + sal_uInt8* 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( sal_uInt8* /*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 ); + return std::min(static_cast<T>(max), result); + } + virtual bool processParams(){ return false; } +}; + + + +template < class T, class S > + +class BufferTransformer : public PCodeVisitor< T > +{ + sal_uInt8* m_pStart; + SbiBuffer m_ConvertedBuf; +public: + BufferTransformer():m_pStart(NULL), m_ConvertedBuf( NULL, 1024 ) {} + virtual void start( sal_uInt8* pStart ){ m_pStart = pStart; } + virtual void processOpCode0( SbiOpcode eOp ) + { + m_ConvertedBuf += (sal_uInt8)eOp; + } + virtual void processOpCode1( SbiOpcode eOp, T nOp1 ) + { + m_ConvertedBuf += (sal_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 += (sal_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( sal_uInt8* pStart, T nOp1 ) + { + PCodeBufferWalker< T > aBuff( pStart, nOp1); + OffSetAccumulator< T, S > aVisitor; + aBuff.visitBuffer( aVisitor ); + return aVisitor.offset(); + } +}; + +sal_uInt32 +SbiCodeGen::calcNewOffSet( sal_uInt8* pCode, sal_uInt16 nOffset ) +{ + return BufferTransformer< sal_uInt16, sal_uInt32 >::convertBufferOffSet( pCode, nOffset ); +} + +sal_uInt16 +SbiCodeGen::calcLegacyOffSet( sal_uInt8* pCode, sal_uInt32 nOffset ) +{ + return BufferTransformer< sal_uInt32, sal_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 = (sal_uInt8*)aTrnsfrmer.buffer().GetBuffer(); + m_nCnvtdSize = static_cast<S>( aTrnsfrmer.buffer().GetSize() ); +} + +template class PCodeBuffConvertor< sal_uInt16, sal_uInt32 >; +template class PCodeBuffConvertor< sal_uInt32, sal_uInt16 >; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basic/source/comp/dim.cxx b/basic/source/comp/dim.cxx new file mode 100644 index 000000000000..ed166530f319 --- /dev/null +++ b/basic/source/comp/dim.cxx @@ -0,0 +1,1224 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * 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 "sbunoobj.hxx" + + +SbxObject* cloneTypeObjectImpl( const SbxObject& rTypeObj ); + +// Declaration of a variable +// If there are errors it will be parsed up to the comma or the newline. +// Return-value: a new instance, which were inserted and then deleted. +// Array-Indexex were returned as SbiDimList + +SbiSymDef* SbiParser::VarDecl( SbiDimList** ppDim, sal_Bool bStatic, sal_Bool bConst ) +{ + bool bWithEvents = false; + if( Peek() == WITHEVENTS ) + { + Next(); + bWithEvents = true; + } + if( !TestSymbol() ) return NULL; + SbxDataType t = eScanType; + SbiSymDef* pDef = bConst ? new SbiConstDef( aSym ) : new SbiSymDef( aSym ); + SbiDimList* pDim = NULL; + // Brackets? + if( Peek() == LPAREN ) + pDim = new SbiDimList( this ); + pDef->SetType( t ); + if( bStatic ) + pDef->SetStatic(); + if( bWithEvents ) + pDef->SetWithEvents(); + TypeDecl( *pDef ); + if( !ppDim && pDim ) + { + if(pDim->GetDims() ) + Error( SbERR_EXPECTED, "()" ); + delete pDim; + } + else if( ppDim ) + *ppDim = pDim; + return pDef; +} + +// Resolving of a AS-Type-Declaration +// The data type were inserted into the handed over variable + +void SbiParser::TypeDecl( SbiSymDef& rDef, sal_Bool bAsNewAlreadyParsed ) +{ + SbxDataType eType = rDef.GetType(); + if( bAsNewAlreadyParsed || Peek() == AS ) + { + short nSize = 0; + if( !bAsNewAlreadyParsed ) + Next(); + rDef.SetDefinedAs(); + String aType; + SbiToken eTok = Next(); + if( !bAsNewAlreadyParsed && eTok == NEW ) + { + rDef.SetNew(); + eTok = Next(); + } + switch( eTok ) + { + case ANY: + if( rDef.IsNew() ) + Error( SbERR_SYNTAX ); + eType = SbxVARIANT; break; + case TINTEGER: + case TLONG: + case TSINGLE: + case TDOUBLE: + case TCURRENCY: + case TDATE: + case TSTRING: + case TOBJECT: + case _ERROR_: + case TBOOLEAN: + case TVARIANT: + case TBYTE: + if( rDef.IsNew() ) + Error( SbERR_SYNTAX ); + eType = (eTok==TBYTE) ? SbxBYTE : SbxDataType( eTok - TINTEGER + SbxINTEGER ); + if( eType == SbxSTRING ) + { + // STRING*n ? + if( Peek() == MUL ) + { // fixed size! + Next(); + SbiConstExpression aSize( this ); + nSize = aSize.GetShortValue(); + if( nSize < 0 || (bVBASupportOn && nSize <= 0) ) + Error( SbERR_OUT_OF_RANGE ); + else + rDef.SetFixedStringLength( nSize ); + } + } + break; + case SYMBOL: // can only be a TYPE or a object class! + if( eScanType != SbxVARIANT ) + Error( SbERR_SYNTAX ); + else + { + String aCompleteName = aSym; + + // #52709 DIM AS NEW for Uno with full-qualified name + 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 ) || ( IsVBASupportOn() && VBAConstantHelper::instance().isVBAConstantType( aCompleteName ) ) ) + { + eType = SbxLONG; + break; + } + + // Take over in the String pool + rDef.SetTypeId( aGblStrings.Add( aCompleteName ) ); + + if( rDef.IsNew() && pProc == NULL ) + aRequiredTypes.push_back( aCompleteName ); + } + eType = SbxOBJECT; + break; + case FIXSTRING: // new syntax for complex UNO types + rDef.SetTypeId( aGblStrings.Add( aSym ) ); + eType = SbxOBJECT; + break; + default: + Error( SbERR_UNEXPECTED, eTok ); + Next(); + } + // The variable could have been declared with a suffix + 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 ); + } +} + +// Here variables, arrays and structures were definied. +// DIM/PRIVATE/PUBLIC/GLOBAL + +void SbiParser::Dim() +{ + DefVar( _DIM, ( pProc && bVBASupportOn ) ? pProc->IsStatic() : sal_False ); +} + +void SbiParser::DefVar( SbiOpcode eOp, sal_Bool bStatic ) +{ + SbiSymPool* pOldPool = pPool; + sal_Bool bSwitchPool = sal_False; + sal_Bool bPersistantGlobal = sal_False; + SbiToken eFirstTok = eCurTok; + if( pProc && ( eCurTok == GLOBAL || eCurTok == PUBLIC || eCurTok == PRIVATE ) ) + Error( SbERR_NOT_IN_SUBR, eCurTok ); + if( eCurTok == PUBLIC || eCurTok == GLOBAL ) + { + bSwitchPool = sal_True; // at the right moment switch to the global pool + if( eCurTok == GLOBAL ) + bPersistantGlobal = sal_True; + } + // behavior in VBA is that a module scope variable's lifetime is + // tied to the document. e.g. a module scope variable is global + if( GetBasic()->IsDocBasic() && bVBASupportOn && !pProc ) + bPersistantGlobal = sal_True; + // PRIVATE is a synonymous for DIM + // _CONST_? + sal_Bool bConst = sal_False; + if( eCurTok == _CONST_ ) + bConst = sal_True; + else if( Peek() == _CONST_ ) + Next(), bConst = sal_True; + + // #110004 It can also be a sub/function + if( !bConst && (eCurTok == SUB || eCurTok == FUNCTION || eCurTok == PROPERTY || + eCurTok == STATIC || eCurTok == ENUM || eCurTok == DECLARE || eCurTok == TYPE) ) + { + // Next token is read here, because !bConst + bool bPrivate = ( eFirstTok == PRIVATE ); + + if( eCurTok == STATIC ) + { + Next(); + DefStatic( bPrivate ); + } + else if( eCurTok == SUB || eCurTok == FUNCTION || eCurTok == PROPERTY ) + { + // End global chain if necessary (not done in + // SbiParser::Parse() under these conditions + if( bNewGblDefs && nGblChain == 0 ) + { + nGblChain = aGen.Gen( _JUMP, 0 ); + bNewGblDefs = sal_False; + } + Next(); + DefProc( sal_False, bPrivate ); + return; + } + else if( eCurTok == ENUM ) + { + Next(); + DefEnum( bPrivate ); + return; + } + else if( eCurTok == DECLARE ) + { + Next(); + DefDeclare( bPrivate ); + return; + } + // #i109049 + else if( eCurTok == TYPE ) + { + Next(); + DefType( bPrivate ); + return; + } + } + +#ifdef SHARED +#define tmpSHARED +#undef SHARED +#endif + // SHARED were ignored + if( Peek() == SHARED ) Next(); +#ifdef tmpSHARED +#define SHARED +#undef tmpSHARED +#endif + // PRESERVE only at REDIM + if( Peek() == PRESERVE ) + { + Next(); + if( eOp == _REDIM ) + eOp = _REDIMP; + else + Error( SbERR_UNEXPECTED, eCurTok ); + } + SbiSymDef* pDef; + SbiDimList* pDim; + + // #40689, Statics -> Modul-Initialising, skip in Sub + sal_uInt32 nEndOfStaticLbl = 0; + if( !bVBASupportOn && bStatic ) + { + nEndOfStaticLbl = aGen.Gen( _JUMP, 0 ); + aGen.Statement(); // catch up on static here + } + + sal_Bool bDefined = sal_False; + while( ( pDef = VarDecl( &pDim, bStatic, bConst ) ) != NULL ) + { + EnableErrors(); + // search variable: + if( bSwitchPool ) + pPool = &aGlobals; + SbiSymDef* pOld = pPool->Find( pDef->GetName() ); + // search also in the Runtime-Library + sal_Bool bRtlSym = sal_False; + if( !pOld ) + { + pOld = CheckRTLForSym( pDef->GetName(), SbxVARIANT ); + if( pOld ) + bRtlSym = sal_True; + } + if( pOld && !(eOp == _REDIM || eOp == _REDIMP) ) + { + if( pDef->GetScope() == SbLOCAL && pOld->GetScope() != SbLOCAL ) + pOld = NULL; + } + if( pOld ) + { + bDefined = sal_True; + // always an error at a RTL-S + if( !bRtlSym && (eOp == _REDIM || eOp == _REDIMP) ) + { + // compare the attributes at a REDIM + SbxDataType eDefType; + bool bError_ = false; + if( pOld->IsStatic() ) + { + bError_ = true; + } + else if( pOld->GetType() != ( eDefType = pDef->GetType() ) ) + { + if( !( eDefType == SbxVARIANT && !pDef->IsDefinedAs() ) ) + bError_ = true; + } + if( bError_ ) + Error( SbERR_VAR_DEFINED, pDef->GetName() ); + } + else + Error( SbERR_VAR_DEFINED, pDef->GetName() ); + delete pDef; pDef = pOld; + } + else + pPool->Add( pDef ); + + // #36374: Create the variable in front of the distinction IsNew() + // Otherwise error at Dim Identifier As New Type and option explicit + if( !bDefined && !(eOp == _REDIM || eOp == _REDIMP) + && ( !bConst || pDef->GetScope() == SbGLOBAL ) ) + { + // Declare variable or global constant + SbiOpcode eOp2; + switch ( pDef->GetScope() ) + { + case SbGLOBAL: eOp2 = bPersistantGlobal ? _GLOBAL_P : _GLOBAL; + goto global; + case SbPUBLIC: eOp2 = bPersistantGlobal ? _PUBLIC_P : _PUBLIC; + // #40689, no own Opcode anymore + if( bVBASupportOn && bStatic ) + { + eOp2 = _STATIC; + break; + } + global: aGen.BackChain( nGblChain ); + nGblChain = 0; + bGblDefs = bNewGblDefs = sal_True; + break; + default: eOp2 = _LOCAL; + } + sal_uInt32 nOpnd2 = sal::static_int_cast< sal_uInt16 >( pDef->GetType() ); + if( pDef->IsWithEvents() ) + nOpnd2 |= SBX_TYPE_WITH_EVENTS_FLAG; + + if( bCompatible && pDef->IsNew() ) + nOpnd2 |= SBX_TYPE_DIM_AS_NEW_FLAG; + + short nFixedStringLength = pDef->GetFixedStringLength(); + if( nFixedStringLength >= 0 ) + nOpnd2 |= (SBX_FIXED_LEN_STRING_FLAG + (sal_uInt32(nFixedStringLength) << 17)); // len = all bits above 0x10000 + + aGen.Gen( eOp2, pDef->GetId(), nOpnd2 ); + } + + // Initialising for self-defined daty types + // and per NEW created 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() ); + if ( bVBASupportOn ) + aGen.Gen( _VBASET ); + else + aGen.Gen( _SET ); + } + } + else + { + if( bConst ) + { + // Definition of the constants + if( pDim ) + { + Error( SbERR_SYNTAX ); + delete pDim; + } + SbiExpression aVar( this, *pDef ); + if( !TestToken( EQ ) ) + goto MyBreak; // (see below) + SbiConstExpression aExpr( this ); + if( !bDefined && aExpr.IsValid() ) + { + if( pDef->GetScope() == SbGLOBAL ) + { + // Create code only for the global constant! + aVar.Gen(); + aExpr.Gen(); + aGen.Gen( _PUTC ); + } + SbiConstDef* pConst = pDef->GetConstDef(); + if( aExpr.GetType() == SbxSTRING ) + pConst->Set( aExpr.GetString() ); + else + pConst->Set( aExpr.GetValue(), aExpr.GetType() ); + } + } + else if( pDim ) + { + // Dimension the variable + // Delete the var at REDIM beforehand + if( eOp == _REDIM ) + { + SbiExpression aExpr( this, *pDef, NULL ); + aExpr.Gen(); + if ( bVBASupportOn ) + // delete the array but + // clear the variable ( this + // allows the processing of + // the param to happen as normal without errors ( ordinary ERASE just clears the array ) + aGen.Gen( _ERASE_CLEAR ); + else + aGen.Gen( _ERASE ); + } + else if( eOp == _REDIMP ) + { + SbiExpression aExpr( this, *pDef, NULL ); + aExpr.Gen(); + aGen.Gen( _REDIMP_ERASE ); + } + pDef->SetDims( pDim->GetDims() ); + if( bPersistantGlobal ) + pDef->SetGlobal( sal_True ); + SbiExpression aExpr( this, *pDef, pDim ); + aExpr.Gen(); + pDef->SetGlobal( sal_False ); + aGen.Gen( (eOp == _STATIC) ? _DIM : eOp ); + } + } + if( !TestComma() ) + goto MyBreak; + + // Implementation of bSwitchPool (see above): pPool must not be set to &aGlobals + // at the VarDecl-Call. + // Apart from that the behavior should be absolutely identical, + // i.e., pPool had to be reset always at the end of the loop. + // also at a break + pPool = pOldPool; + continue; // Skip MyBreak + MyBreak: + pPool = pOldPool; + break; + } + + // #40689, finalize the jump over statics declarations + if( !bVBASupportOn && bStatic ) + { + // maintain the global chain + nGblChain = aGen.Gen( _JUMP, 0 ); + bGblDefs = bNewGblDefs = sal_True; + + // Register for Sub a jump to the end of statics + aGen.BackChain( nEndOfStaticLbl ); + } + +} + +// Here were Arrays redimensioned. + +void SbiParser::ReDim() +{ + DefVar( _REDIM, ( pProc && bVBASupportOn ) ? pProc->IsStatic() : sal_False ); +} + +// ERASE array, ... + +void SbiParser::Erase() +{ + while( !bAbort ) + { + SbiExpression aExpr( this, SbLVALUE ); + aExpr.Gen(); + aGen.Gen( _ERASE ); + if( !TestComma() ) break; + } +} + +// Declaration of a data type + +void SbiParser::Type() +{ + DefType( sal_False ); +} + +void SbiParser::DefType( sal_Bool bPrivate ) +{ + // TODO: Use bPrivate + (void)bPrivate; + + // Read the new Token lesen. It had to be a symbol + if (!TestSymbol()) + return; + + if (rTypeArray->Find(aSym,SbxCLASS_OBJECT)) + { + Error( SbERR_VAR_DEFINED, aSym ); + return; + } + + SbxObject *pType = new SbxObject(aSym); + + SbiSymDef* pElem; + SbiDimList* pDim = NULL; + sal_Bool bDone = sal_False; + + while( !bDone && !IsEof() ) + { + switch( Peek() ) + { + case ENDTYPE : + pElem = NULL; + bDone = sal_True; + Next(); + break; + + case EOLN : + case REM : + pElem = NULL; + Next(); + break; + + default: + pDim = NULL; + pElem = VarDecl(&pDim,sal_False,sal_False); + if( !pElem ) + bDone = sal_True; // Error occurred + } + if( pElem ) + { + SbxArray *pTypeMembers = pType->GetProperties(); + String aElemName = pElem->GetName(); + if( pTypeMembers->Find( aElemName, SbxCLASS_DONTCARE) ) + Error (SbERR_VAR_DEFINED); + else + { + SbxDataType eElemType = pElem->GetType(); + SbxProperty *pTypeElem = new SbxProperty( aElemName, eElemType ); + if( pDim ) + { + SbxDimArray* pArray = new SbxDimArray( pElem->GetType() ); + if ( pDim->GetSize() ) + { + // Dimension the target array + + for ( short i=0; i<pDim->GetSize();++i ) + { + sal_Int32 ub = -1; + sal_Int32 lb = nBase; + SbiExprNode* pNode = pDim->Get(i)->GetExprNode(); + ub = pNode->GetNumber(); + if ( !pDim->Get( i )->IsBased() ) // each dim is low/up + { + if ( ++i >= pDim->GetSize() ) // trouble + StarBASIC::FatalError( SbERR_INTERNAL_ERROR ); + pNode = pDim->Get(i)->GetExprNode(); + lb = ub; + ub = pNode->GetNumber(); + } + else if ( !bCompatible ) + ub += nBase; + pArray->AddDim32( lb, ub ); + } + pArray->setHasFixedSize( true ); + } + else + pArray->unoAddDim( 0, -1 ); // variant array + sal_uInt16 nSavFlags = pTypeElem->GetFlags(); + // need to reset the FIXED flag + // when calling PutObject ( because the type will not match Object ) + pTypeElem->ResetFlag( SBX_FIXED ); + pTypeElem->PutObject( pArray ); + pTypeElem->SetFlags( nSavFlags ); + } + // Nested user type? + if( eElemType == SbxOBJECT ) + { + sal_uInt16 nElemTypeId = pElem->GetTypeId(); + if( nElemTypeId != 0 ) + { + String aTypeName( aGblStrings.Find( nElemTypeId ) ); + SbxObject* pTypeObj = static_cast< SbxObject* >( rTypeArray->Find( aTypeName, SbxCLASS_OBJECT ) ); + if( pTypeObj != NULL ) + { + SbxObject* pCloneObj = cloneTypeObjectImpl( *pTypeObj ); + pTypeElem->PutObject( pCloneObj ); + } + } + } + delete pDim; + pTypeMembers->Insert( pTypeElem, pTypeMembers->Count() ); + } + delete pElem; + } + } + + pType->Remove( XubString( RTL_CONSTASCII_USTRINGPARAM("Name") ), SbxCLASS_DONTCARE ); + pType->Remove( XubString( RTL_CONSTASCII_USTRINGPARAM("Parent") ), SbxCLASS_DONTCARE ); + + rTypeArray->Insert (pType,rTypeArray->Count()); +} + + +// Declaration of Enum type + +void SbiParser::Enum() +{ + DefEnum( sal_False ); +} + +void SbiParser::DefEnum( sal_Bool bPrivate ) +{ + // Read a the new Token. It had to be a symbol + if (!TestSymbol()) + return; + + String aEnumName = aSym; + if( rEnumArray->Find(aEnumName,SbxCLASS_OBJECT) ) + { + Error( SbERR_VAR_DEFINED, aSym ); + return; + } + + SbxObject *pEnum = new SbxObject( aEnumName ); + if( bPrivate ) + pEnum->SetFlag( SBX_PRIVATE ); + + SbiSymDef* pElem; + SbiDimList* pDim; + sal_Bool bDone = sal_False; + + // Starting with -1 to make first default value 0 after ++ + sal_Int32 nCurrentEnumValue = -1; + while( !bDone && !IsEof() ) + { + switch( Peek() ) + { + case ENDENUM : + pElem = NULL; + bDone = sal_True; + Next(); + break; + + case EOLN : + case REM : + pElem = NULL; + Next(); + break; + + default: + { + // TODO: Check existing! + sal_Bool bDefined = sal_False; + + pDim = NULL; + pElem = VarDecl( &pDim, sal_False, sal_True ); + if( !pElem ) + { + bDone = sal_True; // Error occurred + break; + } + else if( pDim ) + { + delete pDim; + Error( SbERR_SYNTAX ); + bDone = sal_True; // Error occurred + break; + } + + SbiExpression aVar( this, *pElem ); + if( Peek() == EQ ) + { + Next(); + + SbiConstExpression aExpr( this ); + if( !bDefined && aExpr.IsValid() ) + { + SbxVariableRef xConvertVar = new SbxVariable(); + if( aExpr.GetType() == SbxSTRING ) + xConvertVar->PutString( aExpr.GetString() ); + else + xConvertVar->PutDouble( aExpr.GetValue() ); + + nCurrentEnumValue = xConvertVar->GetLong(); + } + } + else + nCurrentEnumValue++; + + SbiSymPool* pPoolToUse = bPrivate ? pPool : &aGlobals; + + SbiSymDef* pOld = pPoolToUse->Find( pElem->GetName() ); + if( pOld ) + { + Error( SbERR_VAR_DEFINED, pElem->GetName() ); + bDone = sal_True; // Error occurred + break; + } + + pPool->Add( pElem ); + + if( !bPrivate ) + { + SbiOpcode eOp = _GLOBAL; + aGen.BackChain( nGblChain ); + nGblChain = 0; + bGblDefs = bNewGblDefs = sal_True; + aGen.Gen( + eOp, pElem->GetId(), + sal::static_int_cast< sal_uInt16 >( pElem->GetType() ) ); + + aVar.Gen(); + sal_uInt16 nStringId = aGen.GetParser()->aGblStrings.Add( nCurrentEnumValue, SbxLONG ); + aGen.Gen( _NUMBER, nStringId ); + aGen.Gen( _PUTC ); + } + + SbiConstDef* pConst = pElem->GetConstDef(); + pConst->Set( nCurrentEnumValue, SbxLONG ); + } + } + if( pElem ) + { + SbxArray *pEnumMembers = pEnum->GetProperties(); + SbxProperty *pEnumElem = new SbxProperty( pElem->GetName(), SbxLONG ); + pEnumElem->PutLong( nCurrentEnumValue ); + pEnumElem->ResetFlag( SBX_WRITE ); + pEnumElem->SetFlag( SBX_CONST ); + pEnumMembers->Insert( pEnumElem, pEnumMembers->Count() ); + } + } + + pEnum->Remove( XubString( RTL_CONSTASCII_USTRINGPARAM("Name") ), SbxCLASS_DONTCARE ); + pEnum->Remove( XubString( RTL_CONSTASCII_USTRINGPARAM("Parent") ), SbxCLASS_DONTCARE ); + + rEnumArray->Insert( pEnum, rEnumArray->Count() ); +} + + +// Procedure-Declaration +// the first Token is already read in (SUB/FUNCTION) +// xxx Name [LIB "name"[ALIAS "name"]][(Parameter)][AS TYPE] + +SbiProcDef* SbiParser::ProcDecl( sal_Bool bDecl ) +{ + sal_Bool bFunc = sal_Bool( eCurTok == FUNCTION ); + sal_Bool bProp = sal_Bool( eCurTok == GET || eCurTok == SET || eCurTok == LET ); + if( !TestSymbol() ) return NULL; + String aName( aSym ); + SbxDataType eType = eScanType; + SbiProcDef* pDef = new SbiProcDef( this, aName, true ); + pDef->SetType( eType ); + if( Peek() == _CDECL_ ) + { + Next(); pDef->SetCdecl(); + } + if( Peek() == LIB ) + { + Next(); + if( Next() == FIXSTRING ) + pDef->GetLib() = aSym; + else + Error( SbERR_SYNTAX ); + } + if( Peek() == ALIAS ) + { + Next(); + if( Next() == FIXSTRING ) + pDef->GetAlias() = aSym; + else + Error( SbERR_SYNTAX ); + } + if( !bDecl ) + { + // CDECL, LIB and ALIAS are invalid + if( pDef->GetLib().Len() ) + Error( SbERR_UNEXPECTED, LIB ); + if( pDef->GetAlias().Len() ) + Error( SbERR_UNEXPECTED, ALIAS ); + if( pDef->IsCdecl() ) + Error( SbERR_UNEXPECTED, _CDECL_ ); + pDef->SetCdecl( sal_False ); + pDef->GetLib().Erase(); + pDef->GetAlias().Erase(); + } + else if( !pDef->GetLib().Len() ) + { + // ALIAS and CDECL only together with LIB + if( pDef->GetAlias().Len() ) + Error( SbERR_UNEXPECTED, ALIAS ); + if( pDef->IsCdecl() ) + Error( SbERR_UNEXPECTED, _CDECL_ ); + pDef->SetCdecl( sal_False ); + pDef->GetAlias().Erase(); + } + // Brackets? + if( Peek() == LPAREN ) + { + Next(); + if( Peek() == RPAREN ) + Next(); + else + for(;;) { + sal_Bool bByVal = sal_False; + sal_Bool bOptional = sal_False; + sal_Bool bParamArray = sal_False; + while( Peek() == BYVAL || Peek() == BYREF || Peek() == _OPTIONAL_ ) + { + if ( Peek() == BYVAL ) Next(), bByVal = sal_True; + else if ( Peek() == BYREF ) Next(), bByVal = sal_False; + else if ( Peek() == _OPTIONAL_ ) Next(), bOptional = sal_True; + } + if( bCompatible && Peek() == PARAMARRAY ) + { + if( bByVal || bOptional ) + Error( SbERR_UNEXPECTED, PARAMARRAY ); + Next(); + bParamArray = sal_True; + } + SbiSymDef* pPar = VarDecl( NULL, sal_False, sal_False ); + if( !pPar ) + break; + if( bByVal ) + pPar->SetByVal(); + if( bOptional ) + pPar->SetOptional(); + if( bParamArray ) + pPar->SetParamArray(); + pDef->GetParams().Add( pPar ); + SbiToken eTok = Next(); + if( eTok != COMMA && eTok != RPAREN ) + { + sal_Bool bError2 = sal_True; + if( bOptional && bCompatible && eTok == EQ ) + { + SbiConstExpression* pDefaultExpr = new SbiConstExpression( this ); + SbxDataType eType2 = pDefaultExpr->GetType(); + + sal_uInt16 nStringId; + if( eType2 == SbxSTRING ) + nStringId = aGblStrings.Add( pDefaultExpr->GetString() ); + else + nStringId = aGblStrings.Add( pDefaultExpr->GetValue(), eType2 ); + + pPar->SetDefaultId( nStringId ); + delete pDefaultExpr; + + eTok = Next(); + if( eTok == COMMA || eTok == RPAREN ) + bError2 = sal_False; + } + if( bError2 ) + { + Error( SbERR_EXPECTED, RPAREN ); + break; + } + } + if( eTok == RPAREN ) + break; + } + } + TypeDecl( *pDef ); + if( eType != SbxVARIANT && pDef->GetType() != eType ) + Error( SbERR_BAD_DECLARATION, aName ); + if( pDef->GetType() == SbxVARIANT && !( bFunc || bProp ) ) + pDef->SetType( SbxEMPTY ); + return pDef; +} + +// DECLARE + +void SbiParser::Declare() +{ + DefDeclare( sal_False ); +} + +void SbiParser::DefDeclare( sal_Bool bPrivate ) +{ + Next(); + if( eCurTok != SUB && eCurTok != FUNCTION ) + Error( SbERR_UNEXPECTED, eCurTok ); + else + { + bool bFunction = (eCurTok == FUNCTION); + + SbiProcDef* pDef = ProcDecl( sal_True ); + if( pDef ) + { + if( !pDef->GetLib().Len() ) + Error( SbERR_EXPECTED, LIB ); + // Is it already there? + SbiSymDef* pOld = aPublics.Find( pDef->GetName() ); + if( pOld ) + { + SbiProcDef* p = pOld->GetProcDef(); + if( !p ) + { + // Declared as a variable + Error( SbERR_BAD_DECLARATION, pDef->GetName() ); + delete pDef; + pDef = NULL; + } + else + pDef->Match( p ); + } + else + aPublics.Add( pDef ); + + if ( pDef ) + { + pDef->SetPublic( !bPrivate ); + + // New declare handling + if( pDef->GetLib().Len() > 0 ) + { + if( bNewGblDefs && nGblChain == 0 ) + { + nGblChain = aGen.Gen( _JUMP, 0 ); + bNewGblDefs = sal_False; + } + + sal_uInt16 nSavLine = nLine; + aGen.Statement(); + pDef->Define(); + pDef->SetLine1( nSavLine ); + pDef->SetLine2( nSavLine ); + + SbiSymPool& rPool = pDef->GetParams(); + sal_uInt16 nParCount = rPool.GetSize(); + + SbxDataType eType = pDef->GetType(); + if( bFunction ) + aGen.Gen( _PARAM, 0, sal::static_int_cast< sal_uInt16 >( eType ) ); + + if( nParCount > 1 ) + { + aGen.Gen( _ARGC ); + + for( sal_uInt16 i = 1 ; i < nParCount ; ++i ) + { + SbiSymDef* pParDef = rPool.Get( i ); + SbxDataType eParType = pParDef->GetType(); + + aGen.Gen( _PARAM, i, sal::static_int_cast< sal_uInt16 >( eParType ) ); + aGen.Gen( _ARGV ); + + sal_uInt16 nTyp = sal::static_int_cast< sal_uInt16 >( pParDef->GetType() ); + if( pParDef->IsByVal() ) + { + // Reset to avoid additional byval in call to wrapper function + pParDef->SetByVal( sal_False ); + nTyp |= 0x8000; + } + aGen.Gen( _ARGTYP, nTyp ); + } + } + + aGen.Gen( _LIB, aGblStrings.Add( pDef->GetLib() ) ); + + SbiOpcode eOp = pDef->IsCdecl() ? _CALLC : _CALL; + sal_uInt16 nId = pDef->GetId(); + if( pDef->GetAlias().Len() ) + nId = ( nId & 0x8000 ) | aGblStrings.Add( pDef->GetAlias() ); + if( nParCount > 1 ) + nId |= 0x8000; + aGen.Gen( eOp, nId, sal::static_int_cast< sal_uInt16 >( eType ) ); + + if( bFunction ) + aGen.Gen( _PUT ); + + aGen.Gen( _LEAVE ); + } + } + } + } +} + +void SbiParser::Attribute() +{ + // TODO: Need to implement the method as an attributed object. + while( Next() != EQ ) + { + if( Next() != DOT) + break; + } + + if( eCurTok != EQ ) + Error( SbERR_SYNTAX ); + else + SbiExpression aValue( this ); + + // Don't generate any code - just discard it. +} + +// Call of a SUB or a FUNCTION + +void SbiParser::Call() +{ + SbiExpression aVar( this, SbSYMBOL ); + aVar.Gen( FORCE_CALL ); + aGen.Gen( _GET ); +} + +// SUB/FUNCTION + +void SbiParser::SubFunc() +{ + DefProc( sal_False, sal_False ); +} + +// Read in of a procedure + +sal_Bool runsInSetup( void ); + +void SbiParser::DefProc( sal_Bool bStatic, sal_Bool bPrivate ) +{ + sal_uInt16 l1 = nLine, l2 = nLine; + sal_Bool bSub = sal_Bool( eCurTok == SUB ); + sal_Bool bProperty = sal_Bool( eCurTok == PROPERTY ); + PropertyMode ePropertyMode = PROPERTY_MODE_NONE; + if( bProperty ) + { + Next(); + if( eCurTok == GET ) + ePropertyMode = PROPERTY_MODE_GET; + else if( eCurTok == LET ) + ePropertyMode = PROPERTY_MODE_LET; + else if( eCurTok == SET ) + ePropertyMode = PROPERTY_MODE_SET; + else + Error( SbERR_EXPECTED, "Get or Let or Set" ); + } + + SbiToken eExit = eCurTok; + SbiProcDef* pDef = ProcDecl( sal_False ); + if( !pDef ) + return; + pDef->setPropertyMode( ePropertyMode ); + + // Is the Proc already declared? + SbiSymDef* pOld = aPublics.Find( pDef->GetName() ); + if( pOld ) + { + bool bError_ = false; + + pProc = pOld->GetProcDef(); + if( !pProc ) + { + // Declared as a variable + 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 ); + + // Now we set the search hierarchy for symbols as well as the + // current procedure. + aPublics.SetProcId( pProc->GetId() ); + pProc->GetParams().SetParent( &aPublics ); + if( bStatic ) + { + if ( bVBASupportOn ) + pProc->SetStatic( sal_True ); + else + Error( SbERR_NOT_IMPLEMENTED ); // STATIC SUB ... + } + else + { + pProc->SetStatic( sal_False ); + } + // Normal case: Local variable->parameter->global variable + pProc->GetLocals().SetParent( &pProc->GetParams() ); + pPool = &pProc->GetLocals(); + + pProc->Define(); + OpenBlock( eExit ); + StmntBlock( bSub ? ENDSUB : (bProperty ? ENDPROPERTY : ENDFUNC) ); + l2 = nLine; + pProc->SetLine1( l1 ); + pProc->SetLine2( l2 ); + pPool = &aPublics; + aPublics.SetProcId( 0 ); + // Open labels? + pProc->GetLabels().CheckRefs(); + CloseBlock(); + aGen.Gen( _LEAVE ); + pProc = NULL; +} + +// STATIC variable|procedure + +void SbiParser::Static() +{ + DefStatic( sal_False ); +} + +void SbiParser::DefStatic( sal_Bool bPrivate ) +{ + switch( Peek() ) + { + case SUB: + case FUNCTION: + case PROPERTY: + // End global chain if necessary (not done in + // SbiParser::Parse() under these conditions + if( bNewGblDefs && nGblChain == 0 ) + { + nGblChain = aGen.Gen( _JUMP, 0 ); + bNewGblDefs = sal_False; + } + Next(); + DefProc( sal_True, bPrivate ); + break; + default: { + if( !pProc ) + Error( SbERR_NOT_IN_SUBR ); + // Reset the Pool, so that STATIC-Declarations go into the + // global Pool + SbiSymPool* p = pPool; pPool = &aPublics; + DefVar( _STATIC, sal_True ); + pPool = p; + } break; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basic/source/comp/exprgen.cxx b/basic/source/comp/exprgen.cxx new file mode 100644 index 000000000000..214653f501d7 --- /dev/null +++ b/basic/source/comp/exprgen.cxx @@ -0,0 +1,272 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * 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" + +// Transform table for token operators and 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 }}; + +// Output of an element +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: + { + sal_uInt16 nStringId = pGen->GetParser()->aGblStrings.Add( aStrVal, sal_True ); + pGen->Gen( _SCONST, nStringId ); break; + } + default: + { + sal_uInt16 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; + } + } + // special treatment for WITH + else if( (pWithParent_ = GetWithParent()) != NULL ) + { + eOp = _ELEM; // .-Term in in WITH + } + 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 if( IsNew() ) + { + pGen->Gen( _CREATE, 0, 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; + } + } + } +} + +// Output of an operand element + +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; + // The ID is either the position or the String-ID + // If the bit Bit 0x8000 is set, the variable have + // a parameter list. + sal_uInt16 nId = ( eOp == _PARAM ) ? pDef->GetPos() : pDef->GetId(); + // Build a parameter list + if( aVar.pPar && aVar.pPar->GetSize() ) + { + nId |= 0x8000; + aVar.pPar->Gen(); + } + + pGen->Gen( eOp, nId, sal::static_int_cast< sal_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 ); + } + } +} + +// Create an Argv-Table +// The first element remain available for return value etc. +// See as well SbiProcDef::SbiProcDef() in symtbl.cxx + +void SbiExprList::Gen() +{ + if( pFirst ) + { + pParser->aGen.Gen( _ARGC ); + // Type adjustment at DECLARE + sal_uInt16 nCount = 1; + + for( SbiExpression* pExpr = pFirst; pExpr; pExpr = pExpr->pNext,nCount++ ) + { + pExpr->Gen(); + if( pExpr->GetName().Len() ) + { + // named arg + sal_uInt16 nSid = pParser->aGblStrings.Add( pExpr->GetName() ); + pParser->aGen.Gen( _ARGN, nSid ); + + /* TODO: Check after Declare concept change + // From 1996-01-10: Type adjustment at named -> search suitable parameter + if( pProc ) + { + // For the present: trigger an error + pParser->Error( SbERR_NO_NAMED_ARGS ); + + // Later, if Named Args at DECLARE is posible + //for( sal_uInt16 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 ); + } + } + } +} + +void SbiExpression::Gen( RecursiveMode eRecMode ) +{ + // special treatment for WITH + // If pExpr == .-term in With, approximately Gen for Basis-Object + pExpr->Gen( eRecMode ); + if( bByVal ) + pParser->aGen.Gen( _BYVAL ); + if( bBased ) + { + sal_uInt16 uBase = pParser->nBase; + if( pParser->IsCompatible() ) + uBase |= 0x8000; // #109275 Flag compatiblity + pParser->aGen.Gen( _BASED, uBase ); + pParser->aGen.Gen( _ARGV ); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basic/source/comp/exprnode.cxx b/basic/source/comp/exprnode.cxx new file mode 100644 index 000000000000..5c7bb0b51a88 --- /dev/null +++ b/basic/source/comp/exprnode.cxx @@ -0,0 +1,488 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * 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 are always Variant + eNodeType = SbxNODE; + bComposite= sal_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; + + // Results of functions are at no time fixed + bComposite= sal_Bool( aVar.pDef->GetProcDef() != NULL ); +} + +// #120061 TypeOf +SbiExprNode::SbiExprNode( SbiParser* p, SbiExprNode* l, sal_uInt16 nId ) +{ + BaseInit( p ); + + pLeft = l; + eType = SbxBOOL; + eNodeType = SbxTYPEOF; + nTypeStrId = nId; +} + +// new <type> +SbiExprNode::SbiExprNode( SbiParser* p, sal_uInt16 nId ) +{ + BaseInit( p ); + + eType = SbxOBJECT; + eNodeType = SbxNEW; + nTypeStrId = nId; +} + +// From 1995-12-17, auxiliary function for Ctor for the uniform initialisation +void SbiExprNode::BaseInit( SbiParser* p ) +{ + pGen = &p->aGen; + eTok = NIL; + pLeft = NULL; + pRight = NULL; + pWithParent = NULL; + bComposite = sal_False; + bError = sal_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; +} + +// From 1995-12-18 +SbiExprNode* SbiExprNode::GetRealNode() +{ + if( eNodeType == SbxVARVAL ) + { + SbiExprNode* p = this; + while( p->aVar.pNext ) + p = p->aVar.pNext; + return p; + } + else + return NULL; +} + +// This method transform the type, if it fits into the Integer range + +sal_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 sal_True; + } + } + } + return sal_False; +} + +sal_Bool SbiExprNode::IsNumber() +{ + return sal_Bool( eNodeType == SbxNUMVAL ); +} + +sal_Bool SbiExprNode::IsString() +{ + return sal_Bool( eNodeType == SbxSTRVAL ); +} + +sal_Bool SbiExprNode::IsVariable() +{ + return sal_Bool( eNodeType == SbxVARVAL ); +} + +sal_Bool SbiExprNode::IsLvalue() +{ + return IsVariable(); +} + +// Identify of the depth of a tree + +short SbiExprNode::GetDepth() +{ + if( IsOperand() ) return 0; + else + { + short d1 = pLeft->GetDepth(); + short d2 = pRight->GetDepth(); + return( (d1 < d2 ) ? d2 : d1 ) + 1; + } +} + + +// Adjustment of a tree: +// 1. Constant Folding +// 2. Type-Adjustment +// 3. Conversion of the operans into Strings +// 4. Lifting of the composite- and error-bits + +void SbiExprNode::Optimize() +{ + FoldConstants(); + CollectBits(); +} + +// Lifting of the composite- and error-bits + +void SbiExprNode::CollectBits() +{ + if( pLeft ) + { + pLeft->CollectBits(); + bError |= pLeft->bError; + bComposite |= pLeft->bComposite; + } + if( pRight ) + { + pRight->CollectBits(); + bError |= pRight->bError; + bComposite |= pRight->bComposite; + } +} + +// If a twig can be converted, True will be returned. In this case +// the result is in the left twig. + +void SbiExprNode::FoldConstants() +{ + if( IsOperand() || eTok == LIKE ) return; + if( pLeft ) + pLeft->FoldConstants(); + if( pRight ) + { + pRight->FoldConstants(); + if( pLeft->IsConstant() && pRight->IsConstant() + && pLeft->eNodeType == pRight->eNodeType ) + { + CollectBits(); + if( eTok == CAT ) + // CAT affiliate also two numbers! + eType = SbxSTRING; + if( pLeft->eType == SbxSTRING ) + // No Type Mismatch! + eType = SbxSTRING; + if( eType == SbxSTRING ) + { + String rl( pLeft->GetString() ); + String rr( pRight->GetString() ); + delete pLeft; pLeft = NULL; + delete pRight; pRight = NULL; + bComposite = sal_False; + if( eTok == PLUS || eTok == CAT ) + { + eTok = CAT; + // Linking: + 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 = sal_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 operations + sal_Bool err = sal_False; + if( nl > SbxMAXLNG ) err = sal_True, nl = SbxMAXLNG; + else + if( nl < SbxMINLNG ) err = sal_True, nl = SbxMINLNG; + if( nr > SbxMAXLNG ) err = sal_True, nr = SbxMAXLNG; + else + if( nr < SbxMINLNG ) err = sal_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 = sal_True; + } + } + sal_Bool bBothInt = sal_Bool( pLeft->eType < SbxSINGLE + && pRight->eType < SbxSINGLE ); + delete pLeft; pLeft = NULL; + delete pRight; pRight = NULL; + nVal = 0; + eType = SbxDOUBLE; + eNodeType = SbxNUMVAL; + bComposite = sal_False; + sal_Bool bCheckType = sal_False; + switch( eTok ) + { + case EXPON: + nVal = pow( nl, nr ); break; + case MUL: + bCheckType = sal_True; + nVal = nl * nr; break; + case DIV: + if( !nr ) + { + pGen->GetParser()->Error( SbERR_ZERODIV ); nVal = HUGE_VAL; + bError = sal_True; + } else nVal = nl / nr; + break; + case PLUS: + bCheckType = sal_True; + nVal = nl + nr; break; + case MINUS: + bCheckType = sal_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 = sal_True; + } else nVal = ll / lr; + eType = SbxLONG; break; + case MOD: + if( !lr ) + { + pGen->GetParser()->Error( SbERR_ZERODIV ); nVal = HUGE_VAL; + bError = sal_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 ); + + // Recover the data type to kill rounding error + if( bCheckType && bBothInt + && nVal >= SbxMINLNG && nVal <= SbxMAXLNG ) + { + // Decimal place away + long n = (long) nVal; + nVal = n; + eType = ( n >= SbxMININT && n <= SbxMAXINT ) + ? SbxINTEGER : SbxLONG; + } + } + } + } + else if( pLeft && pLeft->IsNumber() ) + { + nVal = pLeft->nVal; + delete pLeft; + pLeft = NULL; + eType = SbxDOUBLE; + eNodeType = SbxNUMVAL; + bComposite = sal_False; + switch( eTok ) + { + case NEG: + nVal = -nVal; break; + case NOT: { + // Integer operation! + sal_Bool err = sal_False; + if( nVal > SbxMAXLNG ) err = sal_True, nVal = SbxMAXLNG; + else + if( nVal < SbxMINLNG ) err = sal_True, nVal = SbxMINLNG; + if( err ) + { + pGen->GetParser()->Error( SbERR_MATH_OVERFLOW ); + bError = sal_True; + } + nVal = (double) ~((long) nVal); + eType = SbxLONG; + } break; + default: break; + } + } + if( eNodeType == SbxNUMVAL ) + { + // Potentially convolve in INTEGER (because of better 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; + } +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basic/source/comp/exprtree.cxx b/basic/source/comp/exprtree.cxx new file mode 100644 index 000000000000..8efd468ca05c --- /dev/null +++ b/basic/source/comp/exprtree.cxx @@ -0,0 +1,1243 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * 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, const KeywordSymbolInfo* pKeywordSymbolInfo ) +{ + pParser = p; + bError = bByVal = bBased = bBracket = sal_False; + nParenLevel = 0; + eCurExpr = t; + m_eMode = eMode; + pNext = NULL; + pExpr = (t != SbSTDEXPR ) ? Term( pKeywordSymbolInfo ) : 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 = sal_False; + pExpr = new SbiExprNode( pParser, n, t ); + pExpr->Optimize(); +} + +SbiExpression::SbiExpression( SbiParser* p, const String& r ) +{ + pParser = p; + pNext = NULL; + bError = bByVal = bBased = bBracket = sal_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 = sal_False; + eCurExpr = SbOPERAND; + pExpr = new SbiExprNode( pParser, r, SbxVARIANT, pPar ); +} + +SbiExpression::SbiExpression( SbiParser* p, SbiToken t ) +{ + pParser = p; + pNext = NULL; + bError = bByVal = bBased = bBracket = sal_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 sal_Bool DoParametersFollow( SbiParser* p, SbiExprType eCurExpr, SbiToken eTok ) +{ + if( eTok == LPAREN ) + return sal_True; + // Aber nur, wenn CALL-aehnlich! + if( !p->WhiteSpace() || eCurExpr != SbSYMBOL ) + return sal_False; + if ( eTok == NUMBER || eTok == MINUS || eTok == FIXSTRING + || eTok == SYMBOL || eTok == COMMA || eTok == DOT || eTok == NOT || eTok == BYVAL ) + { + return sal_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 sal_True; + } + return sal_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 + sal_Bool bHasType = sal_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 = sal_True; + + pDef = pProc; + pDef->SetType( bHasType ? eType : SbxEMPTY ); + if( pPar ) + { + // Dummy-Parameter generieren + sal_uInt16 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( const KeywordSymbolInfo* pKeywordSymbolInfo ) +{ + 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 = (pKeywordSymbolInfo == NULL) ? pParser->Next() : pKeywordSymbolInfo->m_eTok; + // Anfang des Parsings merken + pParser->LockColumn(); + String aSym( (pKeywordSymbolInfo == NULL) ? pParser->GetSym() : pKeywordSymbolInfo->m_aKeywordSymbol ); + SbxDataType eType = (pKeywordSymbolInfo == NULL) ? pParser->GetType() : pKeywordSymbolInfo->m_eSbxDataType; + 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 = sal_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! + sal_Bool bObj = sal_Bool( ( eTok == DOT || eTok == EXCLAM ) + && !pParser->WhiteSpace() ); + if( bObj ) + { + bBracket = sal_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 = sal_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 ); + + // #i109184: Check if symbol is or later will be defined inside module + SbModule& rMod = pParser->aGen.GetModule(); + SbxArray* pModMethods = rMod.GetMethods(); + if( pModMethods->Find( aSym, SbxCLASS_DONTCARE ) ) + pDef = NULL; + } + 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 = sal_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; + } + // 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 = sal_True; + } + } + } + SbiExprNode* pNd = new SbiExprNode( pParser, *pDef, eType ); + if( !pPar ) + pPar = new SbiParameters( pParser,sal_False,sal_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 ) + { + // defer error until runtime if in vba mode + if ( !pParser->IsVBASupportOn() ) + { + pParser->Error( SbERR_BAD_DECLARATION, aSym ); + bError = sal_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 = sal_True; + } + } + + if( bError ) + return NULL; + + String aSym( pParser->GetSym() ); + SbxDataType eType = pParser->GetType(); + SbiParameters* pPar = NULL; + SbiExprListVector* pvMoreParLcl = NULL; + eTok = pParser->Peek(); + // Parameter? + if( DoParametersFollow( pParser, eCurExpr, eTok ) ) + { + bool bStandaloneExpression = false; + pPar = new SbiParameters( pParser, bStandaloneExpression ); + bError |= !pPar->IsValid(); + eTok = pParser->Peek(); + + // i109624 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(); + } + + } + sal_Bool bObj = sal_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 = sal_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; + pNd->aVar.pvMorePar = pvMoreParLcl; + if( bObj ) + { + // Falls wir etwas mit Punkt einscannen, muss der + // Typ SbxOBJECT sein + + // 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 = sal_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( bool bUsedForTypeOf ) +{ + SbiExprNode *pRes; + SbiToken eTok; + + // Operand testen: + switch( eTok = pParser->Peek() ) + { + case SYMBOL: + pRes = Term(); + // process something like "IF Not r Is Nothing Then .." + if( !bUsedForTypeOf && 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 = sal_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; + pParser->Next(); + pNd = new SbiExprNode( pParser, Unary(), eTok, NULL ); + break; + case NOT: + if( pParser->IsVBASupportOn() ) + { + pNd = Operand(); + } + else + { + pParser->Next(); + pNd = new SbiExprNode( pParser, Unary(), eTok, NULL ); + } + break; + case PLUS: + pParser->Next(); + pNd = Unary(); + break; + case TYPEOF: + { + pParser->Next(); + bool bUsedForTypeOf = true; + SbiExprNode* pObjNode = Operand( bUsedForTypeOf ); + pParser->TestToken( IS ); + String aDummy; + SbiSymDef* pTypeDef = new SbiSymDef( aDummy ); + pParser->TypeDecl( *pTypeDef, sal_True ); + pNd = new SbiExprNode( pParser, pObjNode, pTypeDef->GetTypeId() ); + break; + } + case NEW: + { + pParser->Next(); + String aStr; + SbiSymDef* pTypeDef = new SbiSymDef( aStr ); + pParser->TypeDecl( *pTypeDef, sal_True ); + pNd = new SbiExprNode( pParser, 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::VBA_Not() +{ + SbiExprNode* pNd = NULL; + + SbiToken eTok = pParser->Peek(); + if( eTok == NOT ) + { + pParser->Next(); + pNd = new SbiExprNode( pParser, VBA_Not(), eTok, NULL ); + } + else + { + pNd = Comp(); + } + return pNd; +} + +SbiExprNode* SbiExpression::VBA_And() +{ + SbiExprNode* pNd = VBA_Not(); + if( m_eMode != EXPRMODE_EMPTY_PAREN ) + { + for( ;; ) + { + SbiToken eTok = pParser->Peek(); + if( eTok != AND ) + break; + eTok = pParser->Next(); + pNd = new SbiExprNode( pParser, pNd, eTok, VBA_Not() ); + } + } + return pNd; +} + +SbiExprNode* SbiExpression::VBA_Or() +{ + SbiExprNode* pNd = VBA_And(); + if( m_eMode != EXPRMODE_EMPTY_PAREN ) + { + for( ;; ) + { + SbiToken eTok = pParser->Peek(); + if( eTok != OR ) + break; + eTok = pParser->Next(); + pNd = new SbiExprNode( pParser, pNd, eTok, VBA_And() ); + } + } + return pNd; +} + +SbiExprNode* SbiExpression::VBA_Xor() +{ + SbiExprNode* pNd = VBA_Or(); + if( m_eMode != EXPRMODE_EMPTY_PAREN ) + { + for( ;; ) + { + SbiToken eTok = pParser->Peek(); + if( eTok != XOR ) + break; + eTok = pParser->Next(); + pNd = new SbiExprNode( pParser, pNd, eTok, VBA_Or() ); + } + } + return pNd; + +} + +SbiExprNode* SbiExpression::VBA_Eqv() +{ + SbiExprNode* pNd = VBA_Xor(); + if( m_eMode != EXPRMODE_EMPTY_PAREN ) + { + for( ;; ) + { + SbiToken eTok = pParser->Peek(); + if( eTok != EQV ) + break; + eTok = pParser->Next(); + pNd = new SbiExprNode( pParser, pNd, eTok, VBA_Xor() ); + } + } + return pNd; +} + +SbiExprNode* SbiExpression::VBA_Imp() +{ + SbiExprNode* pNd = VBA_Eqv(); + if( m_eMode != EXPRMODE_EMPTY_PAREN ) + { + for( ;; ) + { + SbiToken eTok = pParser->Peek(); + if( eTok != IMP ) + break; + eTok = pParser->Next(); + pNd = new SbiExprNode( pParser, pNd, eTok, VBA_Eqv() ); + } + } + return pNd; + +} + +SbiExprNode* SbiExpression::Like() +{ + SbiExprNode* pNd = pParser->IsVBASupportOn() ? VBA_Not() : 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->IsVBASupportOn() ) + { + pParser->Error( SbERR_SYNTAX ); + bError = sal_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 sal_Bool-Konstanten + sal_Bool bIsBool = sal_False; + if( pExpr->eNodeType == SbxVARVAL ) + { + SbiSymDef* pVarDef = pExpr->GetVar(); + + // Ist es eine sal_Bool-Konstante? + sal_Bool bBoolVal = sal_False; + if( pVarDef->GetName().EqualsIgnoreCaseAscii( "true" ) ) + { + bIsBool = sal_True; + bBoolVal = sal_True; + } + else if( pVarDef->GetName().EqualsIgnoreCaseAscii( "false" ) ) + //else if( pVarDef->GetName().ICompare( "false" ) == COMPARE_EQUAL ) + { + bIsBool = sal_True; + bBoolVal = sal_False; + } + + // Wenn es ein sal_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; + nExpr = + nDim = 0; + bError = + bBracket = sal_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, sal_Bool bConst, sal_Bool bPar) : +SbiParameters::SbiParameters( SbiParser* p, sal_Bool bStandaloneExpression, sal_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 = sal_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 ); + } + // Benannte Argumente: entweder .name= oder name:= + else + { + bool bByVal = false; + if( eTok == BYVAL ) + { + bByVal = true; + pParser->Next(); + eTok = pParser->Peek(); + } + + if( bAssumeExprLParenMode ) + { + pExpr = new SbiExpression( pParser, SbSTDEXPR, EXPRMODE_LPAREN_PENDING ); + bAssumeExprLParenMode = sal_False; + + SbiExprMode eModeAfter = pExpr->m_eMode; + if( eModeAfter == EXPRMODE_LPAREN_NOT_NEEDED ) + { + bBracket = sal_True; + } + else if( eModeAfter == EXPRMODE_ARRAY_OR_OBJECT ) + { + // Expression "looks" like an array assignment + // a(...)[(...)] = ? or a(...).b(...) + // RPAREN is already parsed + bBracket = sal_True; + bAssumeArrayMode = true; + eTok = NIL; + } + else if( eModeAfter == EXPRMODE_EMPTY_PAREN ) + { + bBracket = sal_True; + delete pExpr; + if( bByVal ) + pParser->Error( SbERR_LVALUE_EXPECTED ); + return; + } + } + else + pExpr = new SbiExpression( pParser ); + + if( bByVal && pExpr->IsLvalue() ) + pExpr->SetByVal(); + + 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 ); + } + 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 = sal_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 = sal_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 = sal_True; + + if( pParser->Next() != LPAREN ) + { + pParser->Error( SbERR_EXPECTED, LPAREN ); + bError = sal_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(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basic/source/comp/io.cxx b/basic/source/comp/io.cxx new file mode 100644 index 000000000000..bc458e54305f --- /dev/null +++ b/basic/source/comp/io.cxx @@ -0,0 +1,334 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * 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 + +sal_Bool SbiParser::Channel( sal_Bool bAlways ) +{ + sal_Bool bRes = sal_False; + Peek(); + if( IsHash() ) + { + SbiExpression aExpr( this ); + while( Peek() == COMMA || Peek() == SEMICOLON ) + Next(); + aExpr.Gen(); + aGen.Gen( _CHANNEL ); + bRes = sal_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() +{ + sal_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() +{ + sal_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 ); +} + + +// #i92642 Handle LINE keyword outside ::Next() +void SbiParser::Line() +{ + // #i92642: Special handling to allow name as symbol + if( Peek() == INPUT ) + { + Next(); + LineInput(); + } + else + { + aGen.Statement(); + + KeywordSymbolInfo aInfo; + aInfo.m_aKeywordSymbol = String( RTL_CONSTASCII_USTRINGPARAM( "line" ) ); + aInfo.m_eSbxDataType = GetType(); + aInfo.m_eTok = SYMBOL; + + Symbol( &aInfo ); + } +} + + +// LINE INPUT [prompt], var$ + +void SbiParser::LineInput() +{ + Channel( sal_True ); + SbiExpression* 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( sal_True ); + SbiExpression* pExpr = new SbiExpression( this, SbOPERAND ); + 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(); + // 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() +{ + // #i92642: Special handling to allow name as symbol + if( Peek() == EQ ) + { + aGen.Statement(); + + KeywordSymbolInfo aInfo; + aInfo.m_aKeywordSymbol = String( RTL_CONSTASCII_USTRINGPARAM( "name" ) ); + aInfo.m_eSbxDataType = GetType(); + aInfo.m_eTok = SYMBOL; + + Symbol( &aInfo ); + return; + } + 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; + } +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basic/source/comp/loops.cxx b/basic/source/comp/loops.cxx new file mode 100644 index 000000000000..c3e9159dac3b --- /dev/null +++ b/basic/source/comp/loops.cxx @@ -0,0 +1,557 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * 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() +{ + sal_uInt32 nEndLbl; + SbiToken eTok = NIL; + // Ende-Tokens ignorieren: + SbiExpression aCond( this ); + aCond.Gen(); + TestToken( THEN ); + if( IsEoln( Next() ) ) + { + // 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 + sal_uInt32 pnJmpToEndLbl[JMP_TABLE_SIZE]; // 100 ELSEIFs zulaessig + sal_uInt16 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 = sal_True; return; + } + } + while( eTok == ELSEIF ) + { + // Bei erfolgreichem IF/ELSEIF auf ENDIF springen + if( iJmp >= JMP_TABLE_SIZE ) + { + Error( SbERR_PROG_TOO_LARGE ); bAbort = sal_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 = sal_True; return; + } + } + } + if( eTok == ELSE ) + { + Next(); + sal_uInt32 nElseLbl = nEndLbl; + nEndLbl = aGen.Gen( _JUMP, 0 ); + aGen.BackChain( nElseLbl ); + + aGen.Statement(); + StmntBlock( ENDIF ); + } + else if( eTok == ENDIF ) + Next(); + + // Jmp-Tabelle abarbeiten + while( iJmp > 0 ) + { + iJmp--; + aGen.BackChain( pnJmpToEndLbl[iJmp] ); + } + } + else + { + // single line IF + bSingleLineIf = sal_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(); + sal_uInt32 nElseLbl = nEndLbl; + nEndLbl = aGen.Gen( _JUMP, 0 ); + aGen.BackChain( nElseLbl ); + while( !bAbort ) + { + if( !Parse() ) break; + eTok = Peek(); + if( eTok == EOLN ) + break; + } + } + bSingleLineIf = sal_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() +{ + sal_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(); + } + sal_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 ); + sal_uInt32 nStartLbl = aGen.GetPC(); + aCond.Gen(); + sal_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 ); + } + + sal_uInt32 nLoop = aGen.GetPC(); + // Test durchfuehren, evtl. Stack freigeben + sal_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(); + sal_uInt32 nLabelsTarget = aGen.Gen( _ONJUMP, 0 ); + SbiToken eTok = Next(); + if( eTok != GOTO && eTok != GOSUB ) + { + Error( SbERR_EXPECTED, "GoTo/GoSub" ); + eTok = GOTO; + } + // Label-Tabelle einlesen: + sal_uInt32 nLbl = 0; + do + { + Next(); // Label holen + if( MayBeLabel() ) + { + sal_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() ) + { + sal_uInt32 nOff = pProc->GetLabels().Reference( aSym ); + aGen.Gen( eOp, nOff ); + } + else Error( SbERR_LABEL_EXPECTED ); +} + +// RETURN [label] + +void SbiParser::Return() +{ + Next(); + if( MayBeLabel() ) + { + sal_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(); + sal_uInt32 nNextTarget = 0; + sal_uInt32 nDoneTarget = 0; + sal_Bool bElse = sal_False; + // Die Cases einlesen: + while( !bAbort ) + { + eTok = Next(); + if( eTok == CASE ) + { + if( nNextTarget ) + aGen.BackChain( nNextTarget ), nNextTarget = 0; + aGen.Statement(); + // Jeden Case einlesen + sal_Bool bDone = sal_False; + sal_uInt32 nTrueTarget = 0; + if( Peek() == ELSE ) + { + // CASE ELSE + Next(); + bElse = sal_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< sal_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 = sal_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")) + 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 + { + sal_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() +{ + sal_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 ); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basic/source/comp/makefile.mk b/basic/source/comp/makefile.mk new file mode 100755 index 000000000000..5fe64ceae091 --- /dev/null +++ b/basic/source/comp/makefile.mk @@ -0,0 +1,57 @@ +#************************************************************************* +# +# 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 + +SLOFILES= \ + $(EXCEPTIONSFILES) \ + $(SLO)$/buffer.obj \ + $(SLO)$/exprgen.obj \ + $(SLO)$/exprnode.obj \ + $(SLO)$/io.obj \ + $(SLO)$/loops.obj \ + $(SLO)$/sbcomp.obj \ + $(SLO)$/symtbl.obj \ + $(SLO)$/token.obj + +EXCEPTIONSFILES= \ + $(SLO)$/codegen.obj \ + $(SLO)$/dim.obj \ + $(SLO)$/exprtree.obj \ + $(SLO)$/parser.obj \ + $(SLO)$/scanner.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..2ea78357882f --- /dev/null +++ b/basic/source/comp/parser.cxx @@ -0,0 +1,872 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * 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 <com/sun/star/script/ModuleType.hpp> + +struct SbiParseStack { // "Stack" fuer Statement-Blocks + SbiParseStack* pNext; // Chain + SbiExprNode* pWithVar; // Variable fuer WITH + SbiToken eExitTok; // Exit-Token + sal_uInt32 nChain; // JUMP-Chain +}; + +struct SbiStatement { + SbiToken eTok; + void( SbiParser::*Func )(); // Verarbeitungsroutine + sal_Bool bMain; // sal_True: ausserhalb SUBs OK + sal_Bool bSubr; // sal_True: in SUBs OK +}; + +#define Y sal_True +#define N sal_False + +static SbiStatement StmntTable [] = { +{ ATTRIBUTE, &SbiParser::Attribute, Y, Y, }, // ATTRIBUTE +{ 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 +{ LINE, &SbiParser::Line, N, Y, }, // LINE, -> LINE INPUT (#i92642) +{ 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 = sal_False; + bClassModule = ( pm->GetModuleType() == com::sun::star::script::ModuleType::CLASS ); + OSL_TRACE("Parser - %s, bClassModule %d", rtl::OUStringToOString( pm->GetName(), RTL_TEXTENCODING_UTF8 ).getStr(), bClassModule ); + 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 + bVBASupportOn = pm->IsVBACompat(); + if ( bVBASupportOn ) + EnableCompatibility(); + +} + + +// 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 + +sal_Bool SbiParser::HasGlobalCode() +{ + if( bGblDefs && nGblChain ) + { + aGen.BackChain( nGblChain ); + aGen.Gen( _LEAVE ); + 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 ) + { + SbiToken eExitTok = p->eExitTok; + if( eTok == eExitTok || + (eTok == PROPERTY && (eExitTok == GET || eExitTok == LET) ) ) // #i109051 + { + p->nChain = aGen.Gen( _JUMP, p->nChain ); + return; + } + } + if( pStack ) + Error( SbERR_EXPECTED, pStack->eExitTok ); + else + Error( SbERR_BAD_EXIT ); +} + +sal_Bool SbiParser::TestSymbol( sal_Bool bKwdOk ) +{ + Peek(); + if( eCurTok == SYMBOL || ( bKwdOk && IsKwd( eCurTok ) ) ) + { + Next(); return sal_True; + } + Error( SbERR_SYMBOL_EXPECTED ); + return sal_False; +} + +// Testen auf ein bestimmtes Token + +sal_Bool SbiParser::TestToken( SbiToken t ) +{ + if( Peek() == t ) + { + Next(); return sal_True; + } + else + { + Error( SbERR_EXPECTED, t ); + return sal_False; + } +} + +// Testen auf Komma oder EOLN + +sal_Bool SbiParser::TestComma() +{ + SbiToken eTok = Peek(); + if( IsEoln( eTok ) ) + { + Next(); + return sal_False; + } + else if( eTok != COMMA ) + { + Error( SbERR_EXPECTED, COMMA ); + return sal_False; + } + Next(); + return sal_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 = sal_True; + } +} + +// Die Hauptroutine. Durch wiederholten Aufrufs dieser Routine wird +// die Quelle geparst. Returnwert sal_False bei Ende/Fehlern. + +sal_Bool SbiParser::Parse() +{ + if( bAbort ) return sal_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 sal_False; + } + + // Leerstatement? + if( IsEoln( eCurTok ) ) + { + Next(); return sal_True; + } + + if( !bSingleLineIf && MayBeLabel( sal_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 sal_True; + } + } + + // Ende des Parsings? + if( eCurTok == eEndTok || + ( bVBASupportOn && // #i109075 + (eCurTok == ENDFUNC || eCurTok == ENDPROPERTY || eCurTok == ENDSUB) && + (eEndTok == ENDFUNC || eEndTok == ENDPROPERTY || eEndTok == ENDSUB) ) ) + { + Next(); + if( eCurTok != NIL ) + aGen.Statement(); + return sal_False; + } + + // Kommentar? + if( eCurTok == REM ) + { + Next(); return sal_True; + } + + // In vba it's possible to do Error.foobar ( even if it results in + // a runtime error + if ( eCurTok == _ERROR_ && IsVBASupportOn() ) // we probably need to define a subset of keywords where this madness applies e.g. if ( IsVBASupportOn() && SymbolCanBeRedined( eCurTok ) ) + { + SbiTokenizer tokens( *(SbiTokenizer*)this ); + tokens.Next(); + if ( tokens.Peek() == DOT ) + { + eCurTok = SYMBOL; + ePush = eCurTok; + } + } + // 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 = sal_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 sal_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( const KeywordSymbolInfo* pKeywordSymbolInfo ) +{ + SbiExprMode eMode = bVBASupportOn ? EXPRMODE_STANDALONE : EXPRMODE_STANDARD; + SbiExpression aVar( this, SbSYMBOL, eMode, pKeywordSymbolInfo ); + + 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(); + 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; + 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(); + sal_uInt16 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, sal_True ); + + aLvalue.Gen(); + 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 ); + } + } +} + +// 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]; + 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; + } + + Peek(); + if( eCurTok != SYMBOL ) + { + Error( SbERR_SYMBOL_EXPECTED ); + return; + } + + String aImplementedIface = aSym; + Next(); + if( Peek() == DOT ) + { + String aDotStr( '.' ); + while( Peek() == DOT ) + { + aImplementedIface += aDotStr; + Next(); + SbiToken ePeekTok = Peek(); + if( ePeekTok == SYMBOL || IsKwd( ePeekTok ) ) + { + Next(); + aImplementedIface += aSym; + } + else + { + Next(); + Error( SbERR_SYMBOL_EXPECTED ); + break; + } + } + } + aIfaceVector.push_back( aImplementedIface ); +} + +void SbiParser::EnableCompatibility() +{ + if( !bCompatible ) + AddConstants(); + bCompatible = sal_True; +} + +// OPTION + +void SbiParser::Option() +{ + switch( Next() ) + { + case EXPLICIT: + bExplicit = sal_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: + { + SbiToken eTok = Next(); + if( eTok == BINARY ) + bText = sal_False; + else if( eTok == SYMBOL && GetSym().EqualsIgnoreCaseAscii("text") ) + bText = sal_True; + else + Error( SbERR_EXPECTED, "Text/Binary" ); + break; + } + case COMPATIBLE: + EnableCompatibility(); + break; + + case CLASSMODULE: + bClassModule = sal_True; + aGen.GetModule().SetModuleType( com::sun::star::script::ModuleType::CLASS ); + break; + case VBASUPPORT: // Option VBASupport used to override the module mode ( in fact this must reset the mode + if( Next() == NUMBER ) + { + if ( nVal == 1 || nVal == 0 ) + { + bVBASupportOn = ( nVal == 1 ); + if ( bVBASupportOn ) + EnableCompatibility(); + // if the module setting is different + // reset it to what the Option tells us + if ( bVBASupportOn != aGen.GetModule().IsVBACompat() ) + aGen.GetModule().SetVBACompat( bVBASupportOn ); + 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 ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basic/source/comp/sbcomp.cxx b/basic/source/comp/sbcomp.cxx new file mode 100644 index 000000000000..8f5aa58bfe97 --- /dev/null +++ b/basic/source/comp/sbcomp.cxx @@ -0,0 +1,303 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * 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 <basic/sbobjmod.hxx> + +// To activate tracing enable in sbtrace.hxx +#ifdef DBG_TRACE_BASIC + +// Trace Settings, used if no ini file / not found in ini file +static char GpTraceFileNameDefault[] = "d:\\zBasic.Asm\\BasicTrace.txt"; +static char* GpTraceFileName = GpTraceFileNameDefault; + +// GbTraceOn: +// true = tracing is active, false = tracing is disabled, default = true +// Set to false initially if you want to activate tracing on demand with +// TraceCommand( "TraceOn" ), see below +static bool GbTraceOn = true; + +// GbIncludePCodes: +// true = PCodes are written to trace, default = false, correspondents +// with TraceCommand( "PCodeOn" / "PCodeOff" ), see below +static bool GbIncludePCodes = false; + +static int GnIndentPerCallLevel = 4; +static int GnIndentForPCode = 2; + +/* + With trace enabled the runtime function TraceCommand + can be used to influence the trace functionality + from within the running Basic macro. + + Format: TraceCommand( command as String [, param as Variant] ) + + Supported commands (command is NOT case sensitive): + TraceCommand "TraceOn" sets GbTraceOn = true + TraceCommand "TraceOff" sets GbTraceOn = false + + TraceCommand "PCodeOn" sets GbIncludePCodes = true + TraceCommand "PCodeOff" sets GbIncludePCodes = false + + TraceCommand "Print", aVal writes aVal into the trace file as + long as it can be converted to string +*/ + +static void lcl_skipWhites( char*& rpc ) +{ + while( *rpc == ' ' || *rpc == '\t' ) + ++rpc; +} + +inline void lcl_findNextLine( char*& rpc, char* pe ) +{ + // Find line end + while( rpc < pe && *rpc != 13 && *rpc != 10 ) + ++rpc; + + // Read all + while( rpc < pe && (*rpc == 13 || *rpc == 10) ) + ++rpc; +} + +inline bool lcl_isAlpha( char c ) +{ + bool bRet = (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); + return bRet; +} + +static void lcl_ReadIniFile( const char* pIniFileName ) +{ + const int BUF_SIZE = 1000; + static sal_Char TraceFileNameBuffer[BUF_SIZE]; + sal_Char Buffer[BUF_SIZE]; + sal_Char VarNameBuffer[BUF_SIZE]; + sal_Char ValBuffer[BUF_SIZE]; + + FILE* pFile = fopen( pIniFileName ,"rb" ); + if( pFile == NULL ) + return; + + size_t nRead = fread( Buffer, 1, BUF_SIZE, pFile ); + + // Scan + char* pc = Buffer; + char* pe = Buffer + nRead; + while( pc < pe ) + { + lcl_skipWhites( pc ); if( pc == pe ) break; + + // Read variable + char* pVarStart = pc; + while( pc < pe && lcl_isAlpha( *pc ) ) + ++pc; + int nVarLen = pc - pVarStart; + if( nVarLen == 0 ) + { + lcl_findNextLine( pc, pe ); + continue; + } + strncpy( VarNameBuffer, pVarStart, nVarLen ); + VarNameBuffer[nVarLen] = '\0'; + + // Check = + lcl_skipWhites( pc ); if( pc == pe ) break; + if( *pc != '=' ) + continue; + ++pc; + lcl_skipWhites( pc ); if( pc == pe ) break; + + // Read value + char* pValStart = pc; + while( pc < pe && *pc != 13 && *pc != 10 ) + ++pc; + int nValLen = pc - pValStart; + if( nValLen == 0 ) + { + lcl_findNextLine( pc, pe ); + continue; + } + strncpy( ValBuffer, pValStart, nValLen ); + ValBuffer[nValLen] = '\0'; + + // Match variables + if( strcmp( VarNameBuffer, "GpTraceFileName") == 0 ) + { + strcpy( TraceFileNameBuffer, ValBuffer ); + GpTraceFileName = TraceFileNameBuffer; + } + else + if( strcmp( VarNameBuffer, "GbTraceOn") == 0 ) + GbTraceOn = (strcmp( ValBuffer, "true" ) == 0); + else + if( strcmp( VarNameBuffer, "GbIncludePCodes") == 0 ) + GbIncludePCodes = (strcmp( ValBuffer, "true" ) == 0); + else + if( strcmp( VarNameBuffer, "GnIndentPerCallLevel") == 0 ) + GnIndentPerCallLevel = strtol( ValBuffer, NULL, 10 ); + else + if( strcmp( VarNameBuffer, "GnIndentForPCode") == 0 ) + GnIndentForPCode = strtol( ValBuffer, NULL, 10 ); + } + fclose( pFile ); +} + if( eType & SbxARRAY ) + +void RTL_Impl_TraceCommand( StarBASIC* pBasic, SbxArray& rPar, sal_Bool bWrite ) +{ + (void)pBasic; + (void)bWrite; + + if ( rPar.Count() < 2 ) + { + StarBASIC::Error( SbERR_BAD_ARGUMENT ); + return; + } + + String aCommand = rPar.Get(1)->GetString(); + + if( aCommand.EqualsIgnoreCaseAscii( "TraceOn" ) ) + GbTraceOn = true; + else + if( aCommand.EqualsIgnoreCaseAscii( "TraceOff" ) ) + GbTraceOn = false; + else + if( aCommand.EqualsIgnoreCaseAscii( "PCodeOn" ) ) + GbIncludePCodes = true; + else + if( aCommand.EqualsIgnoreCaseAscii( "PCodeOff" ) ) + GbIncludePCodes = false; + else + if( aCommand.EqualsIgnoreCaseAscii( "Print" ) ) + { + if ( rPar.Count() < 3 ) + { + StarBASIC::Error( SbERR_BAD_ARGUMENT ); + return; + } + + SbxError eOld = SbxBase::GetError(); + if( eOld != SbxERR_OK ) + SbxBase::ResetError(); + + String aValStr = rPar.Get(2)->GetString(); + SbxError eErr = SbxBase::GetError(); + if( eErr != SbxERR_OK ) + { + aValStr = String( RTL_CONSTASCII_USTRINGPARAM( "<ERROR converting value to String>" ) ); + SbxBase::ResetError(); + } + + char Buffer[500]; + const char* pValStr = OUStringToOString( rtl::OUString( aValStr ), RTL_TEXTENCODING_ASCII_US ).getStr(); + + sprintf( Buffer, "### TRACE_PRINT: %s ###", pValStr ); + int nIndent = GnLastCallLvl * GnIndentPerCallLevel; + lcl_lineOut( GpTraceFileName, Buffer, lcl_getSpaces( nIndent ) ); + + if( eOld != SbxERR_OK ) + SbxBase::SetError( eOld ); + } +} + +#endif + +// Diese Routine ist hier definiert, damit der Compiler als eigenes Segment +// geladen werden kann. + +sal_Bool SbModule::Compile() +{ + if( pImage ) + return sal_True; + StarBASIC* pBasic = PTR_CAST(StarBASIC,GetParent()); + if( !pBasic ) + return sal_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 + sal_Bool bRet = IsCompiled(); + if( bRet ) + { + if( !this->ISA(SbObjModule) ) + pBasic->ClearAllModuleVars(); + RemoveVars(); // remove 'this' Modules variables + // clear all method statics + for( sal_uInt16 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(); + } + } + + return bRet; +} + +/************************************************************************** +* +* Syntax-Highlighting +* +**************************************************************************/ + +void StarBASIC::Highlight( const String& rSrc, SbTextPortions& rList ) +{ + SbiTokenizer aTok( rSrc ); + aTok.Hilite( rList ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basic/source/comp/scanner.cxx b/basic/source/comp/scanner.cxx new file mode 100644 index 000000000000..11c081d1b4b2 --- /dev/null +++ b/basic/source/comp/scanner.cxx @@ -0,0 +1,612 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * 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 = sal_False; + bHash = + bErrors = sal_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 = sal_True; + return; + } + if( !bError && bErrors ) + { + sal_Bool bRes = sal_True; + // Nur einen Fehler pro Statement reporten + bError = sal_True; + if( pBasic ) + { + // Falls EXPECTED oder UNEXPECTED kommen sollte, bezieht es sich + // immer auf das letzte Token, also die Col1 uebernehmen + sal_uInt16 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 sal_True zurueckgeliefert. +// Wird von SbiTokenizer::MayBeLabel() verwendet, um einen Label zu erkennen + +sal_Bool SbiScanner::DoesColonFollow() +{ + if( pLine && *pLine == ':' ) + { + pLine++; nCol++; return sal_True; + } + else return sal_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( (sal_uInt16) n + SbxINTEGER ); + } + return SbxVARIANT; +} + +// Einlesen des naechsten Symbols in die Variablen aSym, nVal und eType +// Returnwert ist sal_False bei EOF oder Fehlern +#define BUF_SIZE 80 + +namespace { + +/** Returns true, if the passed character is a white space character. */ +inline bool lclIsWhitespace( sal_Unicode cChar ) +{ + return (cChar == ' ') || (cChar == '\t') || (cChar == '\f'); +} + +} // namespace + +sal_Bool SbiScanner::NextSym() +{ + // Fuer den EOLN-Fall merken + sal_uInt16 nOldLine = nLine; + sal_uInt16 nOldCol1 = nCol1; + sal_uInt16 nOldCol2 = nCol2; + sal_Unicode buf[ BUF_SIZE ], *p = buf; + bHash = sal_False; + + eScanType = SbxVARIANT; + aSym.Erase(); + bSymbol = + bNumber = bSpaces = sal_False; + + // Zeile einlesen? + if( !pLine ) + { + sal_Int32 n = nBufPos; + sal_Int32 nLen = aBuf.getLength(); + if( nBufPos >= nLen ) + return sal_False; + const sal_Unicode* p2 = aBuf.getStr(); + p2 += n; + while( ( n < nLen ) && ( *p2 != '\n' ) && ( *p2 != '\r' ) ) + p2++, n++; + // #163944# ignore trailing whitespace + sal_Int32 nCopyEndPos = n; + while( (nBufPos < nCopyEndPos) && lclIsWhitespace( aBuf[ nCopyEndPos - 1 ] ) ) + --nCopyEndPos; + aLine = aBuf.copy( nBufPos, nCopyEndPos - 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( lclIsWhitespace( *pLine ) ) + pLine++, nCol++, bSpaces = sal_True; + + nCol1 = nCol; + + // nur Leerzeile? + if( !*pLine ) + goto eoln; + + if( bPrevLineExtentsComment ) + goto PrevLineCommentLbl; + + if( *pLine == '#' ) + { + pLine++; + nCol++; + bHash = sal_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 = sal_True; + short n = nCol; + for ( ; (BasicSimpleCharClass::isAlphaNumeric( *pLine, bCompatible ) || ( *pLine == '_' ) ); pLine++ ) + nCol++; + aSym = aLine.copy( n, nCol - n ); + + // Special handling for "go to" + if( bCompatible && *pLine && aSym.EqualsIgnoreCaseAscii( "go" ) ) + { + const sal_Unicode* pTestLine = pLine; + short nTestCol = nCol; + while( lclIsWhitespace( *pTestLine ) ) + { + pTestLine++; + nTestCol++; + } + + if( *pTestLine && *(pTestLine + 1) ) + { + String aTestSym = aLine.copy( nTestCol, 2 ); + if( aTestSym.EqualsIgnoreCaseAscii( "to" ) ) + { + aSym = String::CreateFromAscii( "goto" ); + pLine = pTestLine + 2; + nCol = nTestCol + 2; + } + } + } + + // 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; + sal_Bool bBufOverflow = sal_False; + while( strchr( "0123456789.DEde", *pLine ) && *pLine ) + { + // AB 4.1.1996: Buffer voll? -> leer weiter scannen + if( (p-buf) == (BUF_SIZE-1) ) + { + bBufOverflow = sal_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; + } + *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 = sal_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 ); + + 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 ); + + // 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; + 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; + case 'H': + break; + default : + // Wird als Operator angesehen + pLine--; nCol--; nCol1 = nCol-1; aSym = '&'; return SYMBOL; + } + bNumber = sal_True; + long l = 0; + int i; + sal_Bool bBufOverflow = sal_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 = sal_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 = sal_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; + sal_uInt16 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 = sal_False; + aSym = String::CreateFromAscii( "REM" ); + sal_uInt16 nLen = String( pLine ).Len(); + if( bCompatible && pLine[ nLen - 1 ] == '_' && pLine[ nLen - 2 ] == ' ' ) + bPrevLineExtentsComment = sal_True; + nCol2 = nCol2 + nLen; + pLine = NULL; + } + return sal_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 = sal_False; + } + return bRes; + } + else + { + pLine = NULL; + nLine = nOldLine; + nCol1 = nOldCol1; + nCol2 = nOldCol2; + aSym = '\n'; + nColLock = 0; + return sal_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; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basic/source/comp/symtbl.cxx b/basic/source/comp/symtbl.cxx new file mode 100644 index 000000000000..00c4b4cd6d87 --- /dev/null +++ b/basic/source/comp/symtbl.cxx @@ -0,0 +1,537 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * 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( sal_uInt16 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, sal_Bool bNoCase ) +{ + sal_uInt16 n = aData.Count(); + for( sal_uInt16 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 = (sal_uInt16) -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 +{ + sal_uInt16 nCount = aData.Count(); + for( sal_uInt16 i = 0; i < nCount; i++ ) + { + SbiSymDef* p = aData.GetObject( nCount - i - 1 ); + 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( sal_uInt16 n ) const +{ + for( sal_uInt16 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( sal_uInt16 n ) const +{ + if( n >= aData.Count() ) + return NULL; + else + return aData.GetObject( n ); +} + +sal_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(); +} + +sal_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( sal_uInt16 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 = + bWithEvents = + bByVal = + bChained = + bGlobal = sal_False; + pIn = + pPool = NULL; + nDefaultId = 0; + nFixedStringLength = -1; +} + +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. + +sal_uInt32 SbiSymDef::Reference() +{ + if( !bChained ) + { + sal_uInt32 n = nChain; + nChain = pIn->pParser->aGen.GetOffset(); + return n; + } + else return nChain; +} + +// Definition eines Symbols. +// Hier wird der Backchain aufgeloest, falls vorhanden + +sal_uInt32 SbiSymDef::Define() +{ + sal_uInt32 n = pIn->pParser->aGen.GetPC(); + pIn->pParser->aGen.GenStmnt(); + if( nChain ) pIn->pParser->aGen.BackChain( nChain ); + nChain = n; + bChained = sal_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, + sal_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 = sal_True; + bCdecl = sal_False; + bStatic = sal_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 + sal_uInt16 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: + OSL_FAIL( "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; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basic/source/comp/token.cxx b/basic/source/comp/token.cxx new file mode 100644 index 000000000000..7452c775b916 --- /dev/null +++ b/basic/source/comp/token.cxx @@ -0,0 +1,553 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * 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" }, + { ATTRIBUTE,"Attribute" }, + { 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" }, + { WITHEVENTS, "WithEvents" }, + { WRITE, "Write" }, // auch WRITE # + { XOR, "Xor" }, + { NIL, "" } +}; + + +// #i109076 +TokenLabelInfo::TokenLabelInfo( void ) +{ + m_pTokenCanBeLabelTab = new bool[VBASUPPORT+1]; + for( int i = 0 ; i <= VBASUPPORT ; ++i ) + m_pTokenCanBeLabelTab[i] = false; + + // Token accepted as label by VBA + SbiToken eLabelToken[] = { ACCESS, ALIAS, APPEND, BASE, BINARY, CLASSMODULE, + COMPARE, COMPATIBLE, DEFERR, _ERROR_, EXPLICIT, LIB, LINE, LPRINT, NAME, + TOBJECT, OUTPUT, PROPERTY, RANDOM, READ, STEP, STOP, TEXT, VBASUPPORT, NIL }; + SbiToken* pTok = eLabelToken; + SbiToken eTok; + for( pTok = eLabelToken ; (eTok = *pTok) != NIL ; ++pTok ) + m_pTokenCanBeLabelTab[eTok] = true; +} + +TokenLabelInfo::~TokenLabelInfo() +{ + delete[] m_pTokenCanBeLabelTab; +} + + +// Der Konstruktor ermittelt die Laenge der Token-Tabelle. + +SbiTokenizer::SbiTokenizer( const ::rtl::OUString& rSrc, StarBASIC* pb ) + : SbiScanner( rSrc, pb ) +{ + pTokTable = aTokTable_Basic; + TokenTable *tp; + bEof = bAs = sal_False; + eCurTok = NIL; + ePush = NIL; + bEos = bKeywords = bErrorIsSymbol = sal_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 ) + { + sal_uInt16 nOldLine = nLine; + sal_uInt16 nOldCol1 = nCol1; + sal_uInt16 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 = sal_True; + return eCurTok = EOLN; + } + // Zeilenende? + if( aSym.GetBuffer()[0] == '\n' ) + { + bEos = sal_True; return eCurTok = EOLN; + } + bEos = sal_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: + // #i92642 + if( eCurTok != NIL && eCurTok != REM && eCurTok != EOLN && (tp->t == NAME || tp->t == LINE) ) + return eCurTok = SYMBOL; + else if( tp->t == TEXT ) + return eCurTok = SYMBOL; + + // #i92642: Special LINE token handling -> SbiParser::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 + sal_uInt16 nOldLine = nLine; + sal_uInt16 nOldCol = nCol; + sal_uInt16 nOldCol1 = nCol1; + sal_uInt16 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 = sal_True; + else + { + if( bAs ) + bAs = sal_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? + +sal_Bool SbiTokenizer::MayBeLabel( sal_Bool bNeedsColon ) +{ + if( eCurTok == SYMBOL || m_aTokenLabelInfo.canTokenBeLabel( eCurTok ) ) + return bNeedsColon ? DoesColonFollow() : sal_True; + else + return sal_Bool( eCurTok == NUMBER + && eScanType == SbxINTEGER + && nVal >= 0 ); +} + +#ifdef _MSC_VER +#pragma optimize("",off) +#endif + + +void SbiTokenizer::Hilite( SbTextPortions& rList ) +{ + bErrors = sal_False; + bUsedForHilite = sal_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 = sal_False; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |