summaryrefslogtreecommitdiffstats
path: root/basic/source/comp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
commit267c6f2ac71f92999e969232431ba04678e7437e (patch)
tree358c9467650e1d0a1d7227a21dac2e3d08b622b2 /basic/source/comp
parentInitial commit. (diff)
downloadlibreoffice-267c6f2ac71f92999e969232431ba04678e7437e.tar.xz
libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.zip
Adding upstream version 4:24.2.0.upstream/4%24.2.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'basic/source/comp')
-rw-r--r--basic/source/comp/basiccharclass.cxx58
-rw-r--r--basic/source/comp/buffer.cxx94
-rw-r--r--basic/source/comp/codegen.cxx585
-rw-r--r--basic/source/comp/dim.cxx1363
-rw-r--r--basic/source/comp/exprgen.cxx281
-rw-r--r--basic/source/comp/exprnode.cxx480
-rw-r--r--basic/source/comp/exprtree.cxx1123
-rw-r--r--basic/source/comp/io.cxx309
-rw-r--r--basic/source/comp/loops.cxx572
-rw-r--r--basic/source/comp/parser.cxx898
-rw-r--r--basic/source/comp/sbcomp.cxx85
-rw-r--r--basic/source/comp/scanner.cxx717
-rw-r--r--basic/source/comp/symtbl.cxx534
-rw-r--r--basic/source/comp/token.cxx572
14 files changed, 7671 insertions, 0 deletions
diff --git a/basic/source/comp/basiccharclass.cxx b/basic/source/comp/basiccharclass.cxx
new file mode 100644
index 0000000000..c06bd8bb60
--- /dev/null
+++ b/basic/source/comp/basiccharclass.cxx
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <basiccharclass.hxx>
+
+#include <unotools/charclass.hxx>
+#include <rtl/character.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+
+bool BasicCharClass::isLetter( sal_Unicode c )
+{
+ //All characters from C0 to FF are letters except D7 and F7
+ return (c < 0xFF ?
+ ( (c >= 0xC0) && (c != 0xD7) && (c != 0xF7) ) :
+ BasicCharClass::isLetterUnicode( c ));
+}
+
+bool BasicCharClass::isLetterUnicode( sal_Unicode c )
+{
+ static CharClass* pCharClass = new CharClass( Application::GetSettings().GetLanguageTag() );
+ // can we get pCharClass to accept a sal_Unicode instead of this waste?
+ return pCharClass->isLetter( OUString(c), 0 );
+}
+
+bool BasicCharClass::isAlpha( sal_Unicode c, bool bCompatible )
+{
+ return rtl::isAsciiAlpha(c)
+ || (bCompatible && BasicCharClass::isLetter( c ));
+}
+
+bool BasicCharClass::isAlphaNumeric( sal_Unicode c, bool bCompatible )
+{
+ return rtl::isAsciiDigit( c ) || BasicCharClass::isAlpha( c, bCompatible );
+}
+
+bool BasicCharClass::isWhitespace( sal_Unicode c )
+{
+ return (c == ' ') || (c == '\t') || (c == '\f');
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/basic/source/comp/buffer.cxx b/basic/source/comp/buffer.cxx
new file mode 100644
index 0000000000..06dafe7a21
--- /dev/null
+++ b/basic/source/comp/buffer.cxx
@@ -0,0 +1,94 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <buffer.hxx>
+#include <basic/sberrors.hxx>
+
+namespace
+{
+const sal_uInt32 UP_LIMIT=0xFFFFFF00;
+
+template <class I, typename T> void write(I it, T n)
+{
+ *it = static_cast<sal_uInt8>(n & 0xFF);
+ // coverity[stray_semicolon : FALSE] - coverity parse error
+ if constexpr (sizeof(n) > 1)
+ {
+ for (std::size_t i = 1; i < sizeof(n); ++i)
+ {
+ n >>= 8;
+ *++it = static_cast<sal_uInt8>(n & 0xFF);
+ }
+ }
+}
+}
+
+template <typename T> void SbiBuffer::append(T n)
+{
+ if (m_aErrCode)
+ return;
+ if ((m_aBuf.size() + sizeof(n)) > UP_LIMIT)
+ {
+ m_aErrCode = ERRCODE_BASIC_PROG_TOO_LARGE;
+ m_aBuf.clear();
+ return;
+ }
+ write(std::back_inserter(m_aBuf), n);
+}
+
+void SbiBuffer::operator+=(sal_Int8 n) { append(n); }
+void SbiBuffer::operator+=(sal_Int16 n) { append(n); }
+void SbiBuffer::operator+=(sal_uInt8 n) { append(n); }
+void SbiBuffer::operator+=(sal_uInt16 n) { append(n); }
+void SbiBuffer::operator+=(sal_uInt32 n) { append(n); }
+void SbiBuffer::operator+=(sal_Int32 n) { append(n); }
+
+// Patch of a Location
+
+void SbiBuffer::Patch( sal_uInt32 off, sal_uInt32 val )
+{
+ if (m_aErrCode)
+ return;
+ if ((off + sizeof(sal_uInt32)) <= GetSize())
+ write(m_aBuf.begin() + off, val);
+}
+
+// Forward References upon label and 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 (m_aErrCode)
+ return;
+ for (sal_uInt32 i = off; i;)
+ {
+ if ((i + sizeof(sal_uInt32)) > GetSize())
+ {
+ m_aErrCode = ERRCODE_BASIC_INTERNAL_ERROR;
+ m_sErrMsg = "BACKCHAIN";
+ break;
+ }
+ auto ip = m_aBuf.begin() + i;
+ i = ip[0] | (ip[1] << 8) | (ip[2] << 16) | (ip[3] << 24);
+ write(ip, GetSize());
+ }
+}
+
+/* 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 0000000000..9f2f4960bf
--- /dev/null
+++ b/basic/source/comp/codegen.cxx
@@ -0,0 +1,585 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <basic/sberrors.hxx>
+#include <basic/sbx.hxx>
+#include <basic/sbmeth.hxx>
+#include <basic/sbmod.hxx>
+#include <image.hxx>
+#include <codegen.hxx>
+#include <parser.hxx>
+#include <sbintern.hxx>
+#include <cstddef>
+#include <limits>
+#include <algorithm>
+#include <osl/diagnose.h>
+#include <rtl/ustrbuf.hxx>
+#include <o3tl/string_view.hxx>
+#include <com/sun/star/script/ModuleType.hpp>
+
+// nInc is the increment size of the buffers
+
+SbiCodeGen::SbiCodeGen(SbModule& r, SbiParser* p)
+ : pParser(p)
+ , rMod(r)
+ , nLine(0)
+ , nCol(0)
+ , nForLevel(0)
+ , bStmnt(false)
+{
+}
+
+sal_uInt32 SbiCodeGen::GetPC() const
+{
+ return aCode.GetSize();
+}
+
+// memorize the statement
+
+void SbiCodeGen::Statement()
+{
+ if( pParser->IsCodeCompleting() )
+ return;
+
+ bStmnt = true;
+
+ nLine = pParser->GetLine();
+ nCol = pParser->GetCol1();
+
+ // #29955 Store the information of the for-loop-layer
+ // in the upper Byte of the column
+ nCol = (nCol & 0xff) + 0x100 * nForLevel;
+}
+
+// Mark the beginning of a statement
+
+void SbiCodeGen::GenStmnt()
+{
+ if( pParser->IsCodeCompleting() )
+ return;
+
+ if( bStmnt )
+ {
+ bStmnt = false;
+ Gen( SbiOpcode::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 )
+{
+ if( pParser->IsCodeCompleting() )
+ return 0;
+
+#ifdef DBG_UTIL
+ if( eOpcode < SbiOpcode::SbOP0_START || eOpcode > SbiOpcode::SbOP0_END )
+ pParser->Error( ERRCODE_BASIC_INTERNAL_ERROR, "OPCODE1" );
+#endif
+ GenStmnt();
+ aCode += static_cast<sal_uInt8>(eOpcode);
+ return GetPC();
+}
+
+sal_uInt32 SbiCodeGen::Gen( SbiOpcode eOpcode, sal_uInt32 nOpnd )
+{
+ if( pParser->IsCodeCompleting() )
+ return 0;
+
+#ifdef DBG_UTIL
+ if( eOpcode < SbiOpcode::SbOP1_START || eOpcode > SbiOpcode::SbOP1_END )
+ pParser->Error( ERRCODE_BASIC_INTERNAL_ERROR, "OPCODE2" );
+#endif
+ GenStmnt();
+ aCode += static_cast<sal_uInt8>(eOpcode);
+ sal_uInt32 n = GetPC();
+ aCode += nOpnd;
+ return n;
+}
+
+sal_uInt32 SbiCodeGen::Gen( SbiOpcode eOpcode, sal_uInt32 nOpnd1, sal_uInt32 nOpnd2 )
+{
+ if( pParser->IsCodeCompleting() )
+ return 0;
+
+#ifdef DBG_UTIL
+ if( eOpcode < SbiOpcode::SbOP2_START || eOpcode > SbiOpcode::SbOP2_END )
+ pParser->Error( ERRCODE_BASIC_INTERNAL_ERROR, "OPCODE3" );
+#endif
+ GenStmnt();
+ aCode += static_cast<sal_uInt8>(eOpcode);
+ sal_uInt32 n = GetPC();
+ aCode += nOpnd1;
+ aCode += nOpnd2;
+ return n;
+}
+
+// Storing of the created image in the module
+
+void SbiCodeGen::Save()
+{
+ if( pParser->IsCodeCompleting() )
+ return;
+
+ std::unique_ptr<SbiImage> p(new SbiImage);
+ rMod.StartDefinitions();
+ // OPTION BASE-Value:
+ p->nDimBase = pParser->nBase;
+ // OPTION take over the EXPLICIT-Flag
+ if( pParser->bExplicit )
+ p->SetFlag( SbiImageFlags::EXPLICIT );
+
+ int nIfaceCount = 0;
+ if( rMod.mnType == css::script::ModuleType::CLASS )
+ {
+ rMod.bIsProxyModule = true;
+ p->SetFlag( SbiImageFlags::CLASSMODULE );
+ GetSbData()->pClassFac->AddClassModule( &rMod );
+
+ nIfaceCount = pParser->aIfaceVector.size();
+ if( !rMod.pClassData )
+ rMod.pClassData.reset(new SbClassData);
+ if( nIfaceCount )
+ {
+ for( int i = 0 ; i < nIfaceCount ; i++ )
+ {
+ const OUString& rIfaceName = pParser->aIfaceVector[i];
+ SbxVariable* pIfaceVar = new SbxVariable( SbxVARIANT );
+ pIfaceVar->SetName( rIfaceName );
+ SbxArray* pIfaces = rMod.pClassData->mxIfaces.get();
+ pIfaces->Insert( pIfaceVar, pIfaces->Count() );
+ }
+ }
+
+ rMod.pClassData->maRequiredTypes = pParser->aRequiredTypes;
+ }
+ else
+ {
+ GetSbData()->pClassFac->RemoveClassModule( &rMod );
+ // Only a ClassModule can revert to Normal
+ if ( rMod.mnType == css::script::ModuleType::CLASS )
+ {
+ rMod.mnType = css::script::ModuleType::NORMAL;
+ }
+ rMod.bIsProxyModule = false;
+ }
+
+ // GlobalCode-Flag
+ if( pParser->HasGlobalCode() )
+ {
+ p->SetFlag( SbiImageFlags::INITCODE );
+ }
+ // The entry points:
+ for( SbiSymDef* pDef = pParser->aPublics.First(); pDef;
+ pDef = pParser->aPublics.Next() )
+ {
+ SbiProcDef* pProc = pDef->GetProcDef();
+ if( pProc && pProc->IsDefined() )
+ {
+ OUString aProcName = pProc->GetName();
+ OUStringBuffer aIfaceProcName;
+ OUString aIfaceName;
+ sal_uInt16 nPassCount = 1;
+ if( nIfaceCount )
+ {
+ int nPropPrefixFound = aProcName.indexOf("Property ");
+ std::u16string_view aPureProcName = aProcName;
+ std::u16string_view aPropPrefix;
+ if( nPropPrefixFound == 0 )
+ {
+ aPropPrefix = aProcName.subView( 0, 13 ); // 13 == Len( "Property ?et " )
+ aPureProcName = aProcName.subView( 13 );
+ }
+ for( int i = 0 ; i < nIfaceCount ; i++ )
+ {
+ const OUString& rIfaceName = pParser->aIfaceVector[i];
+ bool bFound = o3tl::starts_with(aPureProcName, rIfaceName );
+ if( bFound && aPureProcName[rIfaceName.getLength()] == '_' )
+ {
+ if( nPropPrefixFound == 0 )
+ {
+ aIfaceProcName.append(aPropPrefix);
+ }
+ aIfaceProcName.append(aPureProcName.substr(rIfaceName.getLength() + 1) );
+ aIfaceName = rIfaceName;
+ nPassCount = 2;
+ break;
+ }
+ }
+ }
+ SbMethod* pMeth = nullptr;
+ for( sal_uInt16 nPass = 0 ; nPass < nPassCount ; nPass++ )
+ {
+ if( nPass == 1 )
+ {
+ aProcName = aIfaceProcName.toString();
+ }
+ PropertyMode ePropMode = pProc->getPropertyMode();
+ if( ePropMode != PropertyMode::NONE )
+ {
+ SbxDataType ePropType = SbxEMPTY;
+ switch( ePropMode )
+ {
+ case PropertyMode::Get:
+ ePropType = pProc->GetType();
+ break;
+ case PropertyMode::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 PropertyMode::Set:
+ ePropType = SbxOBJECT;
+ break;
+ default:
+ OSL_FAIL("Illegal PropertyMode");
+ break;
+ }
+ OUString aPropName = pProc->GetPropName();
+ if( nPass == 1 )
+ {
+ aPropName = aPropName.copy( aIfaceName.getLength() + 1 );
+ }
+ rMod.GetProcedureProperty( aPropName, ePropType );
+ }
+ if( nPass == 1 )
+ {
+ rMod.GetIfaceMapperMethod( aProcName, pMeth );
+ }
+ else
+ {
+ pMeth = rMod.GetMethod( aProcName, pProc->GetType() );
+
+ if( !pProc->IsPublic() )
+ {
+ pMeth->SetFlag( SbxFlagBits::Private );
+ }
+ // Declare? -> Hidden
+ if( !pProc->GetLib().isEmpty())
+ {
+ pMeth->SetFlag( SbxFlagBits::Hidden );
+ }
+ pMeth->nStart = pProc->GetAddr();
+ pMeth->nLine1 = pProc->GetLine1();
+ pMeth->nLine2 = pProc->GetLine2();
+ // The parameter:
+ SbxInfo* pInfo = pMeth->GetInfo();
+ OUString aHelpFile, aComment;
+ sal_uInt32 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 = static_cast<SbxDataType>( t | SbxBYREF );
+ }
+ if( pPar->GetDims() )
+ {
+ t = static_cast<SbxDataType>( t | SbxARRAY );
+ }
+ // #33677 hand-over an Optional-Info
+ SbxFlagBits nFlags = SbxFlagBits::Read;
+ if( pPar->IsOptional() )
+ {
+ nFlags |= SbxFlagBits::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( pPar->IsWithBrackets() )
+ {
+ nUserData |= PARAM_INFO_WITHBRACKETS;
+ }
+ SbxParamInfo* pParam = nullptr;
+ if( nUserData )
+ {
+ pParam = const_cast<SbxParamInfo*>(pInfo->GetParam( i ));
+ }
+ if( pParam )
+ {
+ pParam->nUserData = nUserData;
+ }
+ }
+ pMeth->SetInfo( pInfo );
+ }
+ } // for( iPass...
+ }
+ }
+ if (aCode.GetErrCode())
+ {
+ pParser->Error(aCode.GetErrCode(), aCode.GetErrMessage());
+ }
+ // The code
+ p->AddCode(aCode.GetBuffer());
+
+ // The global StringPool. 0 is not occupied.
+ SbiStringPool* pPool = &pParser->aGblStrings;
+ sal_uInt16 nSize = pPool->GetSize();
+ p->MakeStrings( nSize );
+ sal_uInt32 i;
+ for( i = 1; i <= nSize; i++ )
+ {
+ p->AddString( pPool->Find( i ) );
+ }
+ // Insert types
+ sal_uInt32 nCount = pParser->rTypeArray->Count();
+ for (i = 0; i < nCount; i++)
+ {
+ p->AddType(static_cast<SbxObject *>(pParser->rTypeArray->Get(i)));
+ }
+ // Insert enum objects
+ nCount = pParser->rEnumArray->Count();
+ for (i = 0; i < nCount; i++)
+ {
+ p->AddEnum(static_cast<SbxObject *>(pParser->rEnumArray->Get(i)));
+ }
+ if( !p->IsError() )
+ {
+ rMod.pImage = std::move(p);
+ }
+ rMod.EndDefinitions();
+}
+
+namespace {
+
+template < class T >
+class PCodeVisitor
+{
+public:
+ virtual ~PCodeVisitor();
+
+ virtual void start( const 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;
+};
+
+}
+
+template <class T> PCodeVisitor< T >::~PCodeVisitor()
+{}
+
+namespace {
+
+template <class T>
+class PCodeBufferWalker
+{
+private:
+ T m_nBytes;
+ const sal_uInt8* m_pCode;
+ static T readParam( sal_uInt8 const *& pCode )
+ {
+ T nOp1=0;
+ for ( std::size_t i=0; i<sizeof( T ); ++i )
+ nOp1 |= *pCode++ << ( i * 8);
+ return nOp1;
+ }
+public:
+ PCodeBufferWalker( const sal_uInt8* pCode, T nBytes ): m_nBytes( nBytes ), m_pCode( pCode )
+ {
+ }
+ void visitBuffer( PCodeVisitor< T >& visitor )
+ {
+ const sal_uInt8* pCode = m_pCode;
+ if ( !pCode )
+ return;
+ const sal_uInt8* pEnd = pCode + m_nBytes;
+ visitor.start( m_pCode );
+ T nOp1 = 0, nOp2 = 0;
+ for( ; pCode < pEnd; )
+ {
+ SbiOpcode eOp = static_cast<SbiOpcode>(*pCode++);
+
+ if ( eOp <= SbiOpcode::SbOP0_END )
+ visitor.processOpCode0( eOp );
+ else if( eOp >= SbiOpcode::SbOP1_START && eOp <= SbiOpcode::SbOP1_END )
+ {
+ if ( visitor.processParams() )
+ nOp1 = readParam( pCode );
+ else
+ pCode += sizeof( T );
+ visitor.processOpCode1( eOp, nOp1 );
+ }
+ else if( eOp >= SbiOpcode::SbOP2_START && eOp <= SbiOpcode::SbOP2_END )
+ {
+ if ( visitor.processParams() )
+ {
+ nOp1 = readParam( pCode );
+ nOp2 = readParam( pCode );
+ }
+ else
+ pCode += ( sizeof( T ) * 2 );
+ visitor.processOpCode2( eOp, nOp1, nOp2 );
+ }
+ }
+ }
+};
+
+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( const sal_uInt8* /*pStart*/ ) override {}
+ virtual void processOpCode0( SbiOpcode /*eOp*/ ) override { ++m_nNumOp0; }
+ virtual void processOpCode1( SbiOpcode /*eOp*/, T /*nOp1*/ ) override { ++m_nNumSingleParams; }
+ virtual void processOpCode2( SbiOpcode /*eOp*/, T /*nOp1*/, T /*nOp2*/ ) override { ++m_nNumDoubleParams; }
+ S offset()
+ {
+ typedef decltype(T(1) + S(1)) larger_t; // type capable to hold both value ranges of T and S
+ 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<larger_t>(max, result);
+ }
+ virtual bool processParams() override { return false; }
+};
+
+
+template < class T, class S >
+class BufferTransformer : public PCodeVisitor< T >
+{
+ const sal_uInt8* m_pStart;
+ SbiBuffer m_ConvertedBuf;
+public:
+ BufferTransformer():m_pStart(nullptr) {}
+ virtual void start( const sal_uInt8* pStart ) override { m_pStart = pStart; }
+ virtual void processOpCode0( SbiOpcode eOp ) override
+ {
+ m_ConvertedBuf += static_cast<sal_uInt8>(eOp);
+ }
+ virtual void processOpCode1( SbiOpcode eOp, T nOp1 ) override
+ {
+ m_ConvertedBuf += static_cast<sal_uInt8>(eOp);
+ switch( eOp )
+ {
+ case SbiOpcode::JUMP_:
+ case SbiOpcode::JUMPT_:
+ case SbiOpcode::JUMPF_:
+ case SbiOpcode::GOSUB_:
+ case SbiOpcode::CASEIS_:
+ case SbiOpcode::RETURN_:
+ case SbiOpcode::ERRHDL_:
+ case SbiOpcode::TESTFOR_:
+ nOp1 = static_cast<T>( convertBufferOffSet(m_pStart, nOp1) );
+ break;
+ case SbiOpcode::RESUME_:
+ if ( nOp1 > 1 )
+ nOp1 = static_cast<T>( convertBufferOffSet(m_pStart, nOp1) );
+ break;
+ default:
+ break;
+
+ }
+ m_ConvertedBuf += static_cast<S>(nOp1);
+ }
+ virtual void processOpCode2( SbiOpcode eOp, T nOp1, T nOp2 ) override
+ {
+ m_ConvertedBuf += static_cast<sal_uInt8>(eOp);
+ if ( eOp == SbiOpcode::CASEIS_ && nOp1 )
+ nOp1 = static_cast<T>( convertBufferOffSet(m_pStart, nOp1) );
+ m_ConvertedBuf += static_cast<S>(nOp1);
+ m_ConvertedBuf += static_cast<S>(nOp2);
+
+ }
+ virtual bool processParams() override { return true; }
+ // 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( const 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 const * pCode, sal_uInt16 nOffset )
+{
+ return BufferTransformer< sal_uInt16, sal_uInt32 >::convertBufferOffSet( pCode, nOffset );
+}
+
+sal_uInt16
+SbiCodeGen::calcLegacyOffSet( sal_uInt8 const * 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 );
+ // TODO: handle buffer errors
+ m_aCnvtdBuf = aTrnsfrmer.buffer().GetBuffer();
+}
+
+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 0000000000..cbc25b0152
--- /dev/null
+++ b/basic/source/comp/dim.cxx
@@ -0,0 +1,1363 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <basic/sberrors.hxx>
+#include <basic/sbstar.hxx>
+#include <basic/sbx.hxx>
+#include <sbunoobj.hxx>
+#include <parser.hxx>
+#include <sb.hxx>
+#include <osl/diagnose.h>
+#include <com/sun/star/reflection/theCoreReflection.hpp>
+#include <comphelper/processfactory.hxx>
+#include <com/sun/star/uno/Exception.hpp>
+#include <basic/codecompletecache.hxx>
+#include <memory>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+// Declaration of a variable
+// If there are errors it will be parsed up to the comma or the newline.
+// Return-value: a new instance, which were inserted and then deleted.
+// Array-Index were returned as SbiExprList
+
+SbiSymDef* SbiParser::VarDecl( SbiExprListPtr* ppDim, bool bStatic, bool bConst )
+{
+ bool bWithEvents = false;
+ if( Peek() == WITHEVENTS )
+ {
+ Next();
+ bWithEvents = true;
+ }
+ if( !TestSymbol() ) return nullptr;
+ SbxDataType t = eScanType;
+ SbiSymDef* pDef = bConst ? new SbiConstDef( aSym ) : new SbiSymDef( aSym );
+ SbiExprListPtr pDim;
+ // Brackets?
+ if( Peek() == LPAREN )
+ {
+ pDim = SbiExprList::ParseDimList( this );
+ if( !pDim->GetDims() )
+ pDef->SetWithBrackets();
+ }
+ pDef->SetType( t );
+ if( bStatic )
+ pDef->SetStatic();
+ if( bWithEvents )
+ pDef->SetWithEvents();
+ TypeDecl( *pDef );
+ if( !ppDim && pDim )
+ {
+ if(pDim->GetDims() )
+ Error( ERRCODE_BASIC_EXPECTED, "()" );
+ }
+ else if( ppDim )
+ *ppDim = std::move(pDim);
+ return pDef;
+}
+
+// Resolving of an AS-Type-Declaration
+// The data type were inserted into the handed over variable
+
+void SbiParser::TypeDecl( SbiSymDef& rDef, bool bAsNewAlreadyParsed )
+{
+ SbxDataType eType = rDef.GetType();
+ if( !(bAsNewAlreadyParsed || Peek() == AS) )
+ return;
+
+ short nSize = 0;
+ if( !bAsNewAlreadyParsed )
+ Next();
+ rDef.SetDefinedAs();
+ SbiToken eTok = Next();
+ if( !bAsNewAlreadyParsed && eTok == NEW )
+ {
+ rDef.SetNew();
+ eTok = Next();
+ }
+ switch( eTok )
+ {
+ case ANY:
+ if( rDef.IsNew() )
+ Error( ERRCODE_BASIC_SYNTAX );
+ eType = SbxVARIANT; break;
+ case TINTEGER:
+ case TLONG:
+ case TSINGLE:
+ case TDOUBLE:
+ case TCURRENCY:
+ case TDATE:
+ case TSTRING:
+ case TOBJECT:
+ case ERROR_:
+ case TBOOLEAN:
+ case TVARIANT:
+ case TBYTE:
+ if( rDef.IsNew() )
+ Error( ERRCODE_BASIC_SYNTAX );
+ eType = (eTok==TBYTE) ? SbxBYTE : SbxDataType( eTok - TINTEGER + SbxINTEGER );
+ if( eType == SbxSTRING )
+ {
+ // STRING*n ?
+ if( Peek() == MUL )
+ { // fixed size!
+ Next();
+ SbiConstExpression aSize( this );
+ nSize = aSize.GetShortValue();
+ if( nSize < 0 || (bVBASupportOn && nSize <= 0) )
+ Error( ERRCODE_BASIC_OUT_OF_RANGE );
+ else
+ rDef.SetFixedStringLength( nSize );
+ }
+ }
+ break;
+ case SYMBOL: // can only be a TYPE or an object class!
+ if( eScanType != SbxVARIANT )
+ Error( ERRCODE_BASIC_SYNTAX );
+ else
+ {
+ OUString aCompleteName = aSym;
+
+ // #52709 DIM AS NEW for Uno with full-qualified name
+ if( Peek() == DOT )
+ {
+ OUString aDotStr( '.' );
+ while( Peek() == DOT )
+ {
+ aCompleteName += aDotStr;
+ Next();
+ SbiToken ePeekTok = Peek();
+ if( ePeekTok == SYMBOL || IsKwd( ePeekTok ) )
+ {
+ Next();
+ aCompleteName += aSym;
+ }
+ else
+ {
+ Next();
+ Error( ERRCODE_BASIC_UNEXPECTED, SYMBOL );
+ break;
+ }
+ }
+ }
+ else if( rEnumArray->Find( aCompleteName, SbxClassType::Object ) || ( IsVBASupportOn() && VBAConstantHelper::instance().isVBAConstantType( aCompleteName ) ) )
+ {
+ eType = SbxLONG;
+ break;
+ }
+
+ // Take over in the string pool
+ rDef.SetTypeId( aGblStrings.Add( aCompleteName ) );
+
+ if( rDef.IsNew() && pProc == nullptr )
+ aRequiredTypes.push_back( aCompleteName );
+ }
+ eType = SbxOBJECT;
+ break;
+ case FIXSTRING: // new syntax for complex UNO types
+ rDef.SetTypeId( aGblStrings.Add( aSym ) );
+ eType = SbxOBJECT;
+ break;
+ default:
+ Error( ERRCODE_BASIC_UNEXPECTED, eTok );
+ Next();
+ }
+ // The variable could have been declared with a suffix
+ if( rDef.GetType() != SbxVARIANT )
+ {
+ if( rDef.GetType() != eType )
+ Error( ERRCODE_BASIC_VAR_DEFINED, rDef.GetName() );
+ else if( eType == SbxSTRING && rDef.GetLen() != nSize )
+ Error( ERRCODE_BASIC_VAR_DEFINED, rDef.GetName() );
+ }
+ rDef.SetType( eType );
+ rDef.SetLen( nSize );
+}
+
+// Here variables, arrays and structures were defined.
+// DIM/PRIVATE/PUBLIC/GLOBAL
+
+void SbiParser::Dim()
+{
+ DefVar( SbiOpcode::DIM_, pProc && bVBASupportOn && pProc->IsStatic() );
+}
+
+void SbiParser::DefVar( SbiOpcode eOp, bool bStatic )
+{
+ SbiSymPool* pOldPool = pPool;
+ bool bSwitchPool = false;
+ bool bPersistentGlobal = false;
+ SbiToken eFirstTok = eCurTok;
+
+ if( pProc && ( eCurTok == GLOBAL || eCurTok == PUBLIC || eCurTok == PRIVATE ) )
+ Error( ERRCODE_BASIC_NOT_IN_SUBR, eCurTok );
+ if( eCurTok == PUBLIC || eCurTok == GLOBAL )
+ {
+ bSwitchPool = true; // at the right moment switch to the global pool
+ if( eCurTok == GLOBAL )
+ bPersistentGlobal = true;
+ }
+ // behavior in VBA is that a module scope variable's lifetime is
+ // tied to the document. e.g. a module scope variable is global
+ if( GetBasic()->IsDocBasic() && bVBASupportOn && !pProc )
+ bPersistentGlobal = true;
+ // PRIVATE is a synonymous for DIM
+ // _CONST_?
+ bool bConst = false;
+ if( eCurTok == CONST_ )
+ bConst = true;
+ else if( Peek() == CONST_ )
+ {
+ Next();
+ bConst = true;
+ }
+
+ // #110004 It can also be a sub/function
+ if( !bConst && (eCurTok == SUB || eCurTok == FUNCTION || eCurTok == PROPERTY ||
+ eCurTok == STATIC || eCurTok == ENUM || eCurTok == DECLARE || eCurTok == TYPE) )
+ {
+ // Next token is read here, because !bConst
+ bool bPrivate = ( eFirstTok == PRIVATE );
+
+ if( eCurTok == STATIC )
+ {
+ Next();
+ DefStatic( bPrivate );
+ }
+ else if( eCurTok == SUB || eCurTok == FUNCTION || eCurTok == PROPERTY )
+ {
+ // End global chain if necessary (not done in
+ // SbiParser::Parse() under these conditions
+ if( bNewGblDefs && nGblChain == 0 )
+ {
+ nGblChain = aGen.Gen( SbiOpcode::JUMP_, 0 );
+ bNewGblDefs = false;
+ }
+ Next();
+ DefProc( false, bPrivate );
+ return;
+ }
+ else if( eCurTok == ENUM )
+ {
+ Next();
+ DefEnum( bPrivate );
+ return;
+ }
+ else if( eCurTok == DECLARE )
+ {
+ Next();
+ DefDeclare( bPrivate );
+ return;
+ }
+ // #i109049
+ else if( eCurTok == TYPE )
+ {
+ Next();
+ DefType(); // TODO: Use bPrivate in DefType()
+ return;
+ }
+ }
+
+ // SHARED were ignored
+ if( Peek() == SHARED ) Next();
+
+ // PRESERVE only at REDIM
+ if( Peek() == PRESERVE )
+ {
+ Next();
+ if( eOp == SbiOpcode::REDIM_ )
+ eOp = SbiOpcode::REDIMP_;
+ else
+ Error( ERRCODE_BASIC_UNEXPECTED, eCurTok );
+ }
+ SbiSymDef* pDef;
+ SbiExprListPtr pDim;
+
+ // #40689, Statics -> Module-Initialising, skip in Sub
+ sal_uInt32 nEndOfStaticLbl = 0;
+ if( !bVBASupportOn && bStatic )
+ {
+ nEndOfStaticLbl = aGen.Gen( SbiOpcode::JUMP_, 0 );
+ aGen.Statement(); // catch up on static here
+ }
+
+ bool bDefined = false;
+ while( ( pDef = VarDecl( &pDim, bStatic, bConst ) ) != nullptr )
+ {
+ /*fprintf(stderr, "Actual sub: \n");
+ fprintf(stderr, "Symbol name: %s\n",OUStringToOString(pDef->GetName(),RTL_TEXTENCODING_UTF8).getStr());*/
+ EnableErrors();
+ // search variable:
+ if( bSwitchPool )
+ pPool = &aGlobals;
+ SbiSymDef* pOld = pPool->Find( pDef->GetName() );
+ // search also in the Runtime-Library
+ bool bRtlSym = false;
+ if( !pOld )
+ {
+ pOld = CheckRTLForSym( pDef->GetName(), SbxVARIANT );
+ if( pOld )
+ bRtlSym = true;
+ }
+ if( pOld && eOp != SbiOpcode::REDIM_ && eOp != SbiOpcode::REDIMP_ )
+ {
+ if( pDef->GetScope() == SbLOCAL )
+ if (auto eOldScope = pOld->GetScope(); eOldScope != SbLOCAL && eOldScope != SbPARAM)
+ pOld = nullptr;
+ }
+ if( pOld )
+ {
+ bDefined = true;
+ // always an error at a RTL-S
+ if( !bRtlSym && (eOp == SbiOpcode::REDIM_ || eOp == SbiOpcode::REDIMP_) )
+ {
+ // compare the attributes at a REDIM
+ SbxDataType eDefType;
+ bool bError_ = false;
+ if( pOld->IsStatic() )
+ {
+ bError_ = true;
+ }
+ else if( pOld->GetType() != ( eDefType = pDef->GetType() ) )
+ {
+ if( eDefType != SbxVARIANT || pDef->IsDefinedAs() )
+ bError_ = true;
+ }
+ if( bError_ )
+ Error( ERRCODE_BASIC_VAR_DEFINED, pDef->GetName() );
+ }
+ else
+ Error( ERRCODE_BASIC_VAR_DEFINED, pDef->GetName() );
+ delete pDef; pDef = pOld;
+ }
+ else
+ pPool->Add( pDef );
+
+ // #36374: Create the variable in front of the distinction IsNew()
+ // Otherwise error at Dim Identifier As New Type and option explicit
+ if( !bDefined && eOp != SbiOpcode::REDIM_ && eOp != SbiOpcode::REDIMP_
+ && ( !bConst || pDef->GetScope() == SbGLOBAL ) )
+ {
+ // Declare variable or global constant
+ SbiOpcode eOp2;
+ switch ( pDef->GetScope() )
+ {
+ case SbGLOBAL: eOp2 = bPersistentGlobal ? SbiOpcode::GLOBAL_P_ : SbiOpcode::GLOBAL_;
+ goto global;
+ case SbPUBLIC: eOp2 = bPersistentGlobal ? SbiOpcode::PUBLIC_P_ : SbiOpcode::PUBLIC_;
+ // #40689, no own Opcode anymore
+ if( bVBASupportOn && bStatic )
+ {
+ eOp2 = SbiOpcode::STATIC_;
+ break;
+ }
+ global: aGen.BackChain( nGblChain );
+ nGblChain = 0;
+ bGblDefs = bNewGblDefs = true;
+ break;
+ default: eOp2 = SbiOpcode::LOCAL_;
+ }
+ sal_uInt32 nOpnd2 = sal::static_int_cast< sal_uInt16 >( pDef->GetType() );
+ if( pDef->IsWithEvents() )
+ nOpnd2 |= SBX_TYPE_WITH_EVENTS_FLAG;
+
+ if( bCompatible && pDef->IsNew() )
+ nOpnd2 |= SBX_TYPE_DIM_AS_NEW_FLAG;
+
+ short nFixedStringLength = pDef->GetFixedStringLength();
+ if( nFixedStringLength >= 0 )
+ nOpnd2 |= (SBX_FIXED_LEN_STRING_FLAG + (sal_uInt32(nFixedStringLength) << 17)); // len = all bits above 0x10000
+
+ if( pDim != nullptr && pDim->GetDims() > 0 )
+ nOpnd2 |= SBX_TYPE_VAR_TO_DIM_FLAG;
+
+ aGen.Gen( eOp2, pDef->GetId(), nOpnd2 );
+ }
+
+ // Initialising for self-defined data types
+ // and per NEW created variable
+ if( pDef->GetType() == SbxOBJECT
+ && pDef->GetTypeId() )
+ {
+ if( !bCompatible && !pDef->IsNew() )
+ {
+ OUString aTypeName( aGblStrings.Find( pDef->GetTypeId() ) );
+ if( rTypeArray->Find( aTypeName, SbxClassType::Object ) == nullptr )
+ {
+ if( CodeCompleteOptions::IsExtendedTypeDeclaration() )
+ {
+ if(!IsUnoInterface(aTypeName))
+ Error( ERRCODE_BASIC_UNDEF_TYPE, aTypeName );
+ }
+ else
+ Error( ERRCODE_BASIC_UNDEF_TYPE, aTypeName );
+ }
+ }
+
+ if( bConst )
+ {
+ Error( ERRCODE_BASIC_SYNTAX );
+ }
+
+ if( pDim )
+ {
+ if( eOp == SbiOpcode::REDIMP_ )
+ {
+ SbiExpression aExpr( this, *pDef, nullptr );
+ aExpr.Gen();
+ aGen.Gen( SbiOpcode::REDIMP_ERASE_ );
+
+ pDef->SetDims( pDim->GetDims() );
+ SbiExpression aExpr2( this, *pDef, std::move(pDim) );
+ aExpr2.Gen();
+ aGen.Gen( SbiOpcode::DCREATE_REDIMP_, pDef->GetId(), pDef->GetTypeId() );
+ }
+ else
+ {
+ // tdf#145371, tdf#136755 - only delete the variable beforehand REDIM
+ if (eOp == SbiOpcode::REDIM_)
+ {
+ SbiExpression aExpr(this, *pDef, nullptr);
+ aExpr.Gen();
+ aGen.Gen(bVBASupportOn ? SbiOpcode::ERASE_CLEAR_ : SbiOpcode::ERASE_);
+ }
+
+ pDef->SetDims( pDim->GetDims() );
+ SbiExpression aExpr2( this, *pDef, std::move(pDim) );
+ aExpr2.Gen();
+ aGen.Gen( SbiOpcode::DCREATE_, pDef->GetId(), pDef->GetTypeId() );
+ }
+ }
+ else
+ {
+ SbiExpression aExpr( this, *pDef );
+ aExpr.Gen();
+
+ /* tdf#88442
+ * Don't initialize a
+ * Global X as New SomeObjectType
+ * if it has already been initialized.
+ * This approach relies on JUMPT evaluating Object->NULL as being 'false'
+ * But the effect of this code is similar to inserting
+ * If IsNull(YourGlobal)
+ * Set YourGlobal = ' new obj
+ * End If ' If IsNull(YourGlobal)
+ * Only for globals. For locals that check is skipped as it's unnecessary
+ */
+ sal_uInt32 come_from = 0;
+ if ( pDef->GetScope() == SbGLOBAL )
+ {
+ come_from = aGen.Gen( SbiOpcode::JUMPT_, 0 );
+ aGen.Gen( SbiOpcode::FIND_, pDef->GetId(), pDef->GetTypeId() );
+ }
+
+ SbiOpcode eOp_ = pDef->IsNew() ? SbiOpcode::CREATE_ : SbiOpcode::TCREATE_;
+ aGen.Gen( eOp_, pDef->GetId(), pDef->GetTypeId() );
+ if ( bVBASupportOn )
+ aGen.Gen( SbiOpcode::VBASET_ );
+ else
+ aGen.Gen( SbiOpcode::SET_ );
+
+ if ( come_from )
+ {
+ // See other tdf#88442 comment above where come_from is
+ // initialized. This is effectively 'inserting' the
+ // End If ' If IsNull(YourGlobal)
+ aGen.BackChain( come_from );
+ }
+ }
+ }
+ else
+ {
+ if( bConst )
+ {
+ // Definition of the constants
+ if( pDim )
+ {
+ Error( ERRCODE_BASIC_SYNTAX );
+ }
+ SbiExpression aVar( this, *pDef );
+ if( !TestToken( EQ ) )
+ goto MyBreak; // (see below)
+ SbiConstExpression aExpr( this );
+ if( !bDefined && aExpr.IsValid() )
+ {
+ if( pDef->GetScope() == SbGLOBAL )
+ {
+ // Create code only for the global constant!
+ aVar.Gen();
+ aExpr.Gen();
+ aGen.Gen( SbiOpcode::PUTC_ );
+ }
+ SbiConstDef* pConst = pDef->GetConstDef();
+ if( aExpr.GetType() == SbxSTRING )
+ pConst->Set( aExpr.GetString() );
+ else
+ pConst->Set( aExpr.GetValue(), aExpr.GetType() );
+ }
+ }
+ else if( pDim )
+ {
+ // Dimension the variable
+ // Delete the var at REDIM beforehand
+ if( eOp == SbiOpcode::REDIM_ )
+ {
+ SbiExpression aExpr( this, *pDef, nullptr );
+ aExpr.Gen();
+ if ( bVBASupportOn )
+ // delete the array but
+ // clear the variable ( this
+ // allows the processing of
+ // the param to happen as normal without errors ( ordinary ERASE just clears the array )
+ aGen.Gen( SbiOpcode::ERASE_CLEAR_ );
+ else
+ aGen.Gen( SbiOpcode::ERASE_ );
+ }
+ else if( eOp == SbiOpcode::REDIMP_ )
+ {
+ SbiExpression aExpr( this, *pDef, nullptr );
+ aExpr.Gen();
+ aGen.Gen( SbiOpcode::REDIMP_ERASE_ );
+ }
+ pDef->SetDims( pDim->GetDims() );
+ if( bPersistentGlobal )
+ pDef->SetGlobal( true );
+ SbiExpression aExpr( this, *pDef, std::move(pDim) );
+ aExpr.Gen();
+ pDef->SetGlobal( false );
+ aGen.Gen( (eOp == SbiOpcode::STATIC_) ? SbiOpcode::DIM_ : eOp );
+ }
+ }
+ if( !TestComma() )
+ goto MyBreak;
+
+ // Implementation of bSwitchPool (see above): pPool must not be set to &aGlobals
+ // at the VarDecl-Call.
+ // Apart from that the behavior should be absolutely identical,
+ // i.e., pPool had to be reset always at the end of the loop.
+ // also at a break
+ pPool = pOldPool;
+ continue; // Skip MyBreak
+ MyBreak:
+ pPool = pOldPool;
+ break;
+ }
+
+ // #40689, finalize the jump over statics declarations
+ if( !bVBASupportOn && bStatic )
+ {
+ // maintain the global chain
+ nGblChain = aGen.Gen( SbiOpcode::JUMP_, 0 );
+ bGblDefs = bNewGblDefs = true;
+
+ // Register for Sub a jump to the end of statics
+ aGen.BackChain( nEndOfStaticLbl );
+ }
+
+}
+
+// Here were Arrays redimensioned.
+
+void SbiParser::ReDim()
+{
+ DefVar( SbiOpcode::REDIM_, pProc && bVBASupportOn && pProc->IsStatic() );
+}
+
+// ERASE array, ...
+
+void SbiParser::Erase()
+{
+ while( !bAbort )
+ {
+ SbiExpression aExpr( this, SbLVALUE );
+ aExpr.Gen();
+ aGen.Gen( SbiOpcode::ERASE_ );
+ if( !TestComma() ) break;
+ }
+}
+
+// Declaration of a data type
+
+void SbiParser::Type()
+{
+ DefType();
+}
+
+void SbiParser::DefType()
+{
+ // Read the new Token lesen. It had to be a symbol
+ if (!TestSymbol())
+ return;
+
+ if (rTypeArray->Find(aSym,SbxClassType::Object))
+ {
+ Error( ERRCODE_BASIC_VAR_DEFINED, aSym );
+ return;
+ }
+
+ SbxObject *pType = new SbxObject(aSym);
+
+ bool bDone = false;
+
+ while( !bDone && !IsEof() )
+ {
+ std::unique_ptr<SbiSymDef> pElem;
+ SbiExprListPtr pDim;
+ switch( Peek() )
+ {
+ case ENDTYPE :
+ bDone = true;
+ Next();
+ break;
+
+ case EOLN :
+ case REM :
+ Next();
+ break;
+
+ default:
+ pElem.reset(VarDecl(&pDim, false, false));
+ if( !pElem )
+ bDone = true; // Error occurred
+ }
+ if( pElem )
+ {
+ SbxArray *pTypeMembers = pType->GetProperties();
+ OUString aElemName = pElem->GetName();
+ if( pTypeMembers->Find( aElemName, SbxClassType::DontCare) )
+ {
+ Error (ERRCODE_BASIC_VAR_DEFINED);
+ }
+ else
+ {
+ SbxDataType eElemType = pElem->GetType();
+ SbxProperty *pTypeElem = new SbxProperty( aElemName, eElemType );
+ if( pDim )
+ {
+ SbxDimArray* pArray = new SbxDimArray( pElem->GetType() );
+ if ( pDim->GetSize() )
+ {
+ // Dimension the target array
+
+ for ( short i=0; i<pDim->GetSize();++i )
+ {
+ sal_Int32 lb = nBase;
+ SbiExprNode* pNode = pDim->Get(i)->GetExprNode();
+ sal_Int32 ub = pNode->GetNumber();
+ if ( !pDim->Get( i )->IsBased() ) // each dim is low/up
+ {
+ if ( ++i >= pDim->GetSize() ) // trouble
+ StarBASIC::FatalError( ERRCODE_BASIC_INTERNAL_ERROR );
+ pNode = pDim->Get(i)->GetExprNode();
+ lb = ub;
+ ub = pNode->GetNumber();
+ }
+ else if ( !bCompatible )
+ ub += nBase;
+ pArray->AddDim(lb, ub);
+ }
+ pArray->setHasFixedSize( true );
+ }
+ else
+ pArray->unoAddDim(0, -1); // variant array
+ SbxFlagBits nSavFlags = pTypeElem->GetFlags();
+ // need to reset the FIXED flag
+ // when calling PutObject ( because the type will not match Object )
+ pTypeElem->ResetFlag( SbxFlagBits::Fixed );
+ pTypeElem->PutObject( pArray );
+ pTypeElem->SetFlags( nSavFlags );
+ }
+ // Nested user type?
+ if( eElemType == SbxOBJECT )
+ {
+ sal_uInt16 nElemTypeId = pElem->GetTypeId();
+ if( nElemTypeId != 0 )
+ {
+ OUString aTypeName( aGblStrings.Find( nElemTypeId ) );
+ SbxObject* pTypeObj = static_cast< SbxObject* >( rTypeArray->Find( aTypeName, SbxClassType::Object ) );
+ if( pTypeObj != nullptr )
+ {
+ SbxObjectRef pCloneObj = cloneTypeObjectImpl( *pTypeObj );
+ pTypeElem->PutObject( pCloneObj.get() );
+ }
+ }
+ }
+ pTypeMembers->Insert(pTypeElem, pTypeMembers->Count());
+ }
+ }
+ }
+
+ pType->Remove( "Name", SbxClassType::DontCare );
+ pType->Remove( "Parent", SbxClassType::DontCare );
+
+ rTypeArray->Insert(pType, rTypeArray->Count());
+}
+
+
+// Declaration of Enum type
+
+void SbiParser::Enum()
+{
+ DefEnum( false );
+}
+
+void SbiParser::DefEnum( bool bPrivate )
+{
+ // Read the new Token. It had to be a symbol
+ if (!TestSymbol())
+ return;
+
+ OUString aEnumName = aSym;
+ if( rEnumArray->Find(aEnumName,SbxClassType::Object) )
+ {
+ Error( ERRCODE_BASIC_VAR_DEFINED, aSym );
+ return;
+ }
+
+ SbxObject *pEnum = new SbxObject( aEnumName );
+ if( bPrivate )
+ {
+ pEnum->SetFlag( SbxFlagBits::Private );
+ }
+ SbiSymDef* pElem;
+ bool bDone = false;
+
+ // Starting with -1 to make first default value 0 after ++
+ sal_Int32 nCurrentEnumValue = -1;
+ while( !bDone && !IsEof() )
+ {
+ switch( Peek() )
+ {
+ case ENDENUM :
+ pElem = nullptr;
+ bDone = true;
+ Next();
+ break;
+
+ case EOLN :
+ case REM :
+ pElem = nullptr;
+ Next();
+ break;
+
+ default:
+ {
+ SbiExprListPtr pDim;
+ pElem = VarDecl( &pDim, false, true );
+ if( !pElem )
+ {
+ bDone = true; // Error occurred
+ break;
+ }
+ else if( pDim )
+ {
+ Error( ERRCODE_BASIC_SYNTAX );
+ bDone = true; // Error occurred
+ break;
+ }
+
+ SbiExpression aVar( this, *pElem );
+ if( Peek() == EQ )
+ {
+ Next();
+
+ SbiConstExpression aExpr( this );
+ if( aExpr.IsValid() )
+ {
+ SbxVariableRef xConvertVar = new SbxVariable();
+ if( aExpr.GetType() == SbxSTRING )
+ xConvertVar->PutString( aExpr.GetString() );
+ else
+ xConvertVar->PutDouble( aExpr.GetValue() );
+
+ nCurrentEnumValue = xConvertVar->GetLong();
+ }
+ }
+ else
+ nCurrentEnumValue++;
+
+ SbiSymPool* pPoolToUse = bPrivate ? pPool : &aGlobals;
+
+ SbiSymDef* pOld = pPoolToUse->Find( pElem->GetName() );
+ if( pOld )
+ {
+ Error( ERRCODE_BASIC_VAR_DEFINED, pElem->GetName() );
+ bDone = true; // Error occurred
+ break;
+ }
+
+ pPool->Add( pElem );
+
+ if( !bPrivate )
+ {
+ aGen.BackChain( nGblChain );
+ nGblChain = 0;
+ bGblDefs = bNewGblDefs = true;
+ aGen.Gen(
+ SbiOpcode::GLOBAL_, pElem->GetId(),
+ sal::static_int_cast< sal_uInt16 >( pElem->GetType() ) );
+
+ aVar.Gen();
+ sal_uInt16 nStringId = aGen.GetParser()->aGblStrings.Add( nCurrentEnumValue, SbxLONG );
+ aGen.Gen( SbiOpcode::NUMBER_, nStringId );
+ aGen.Gen( SbiOpcode::PUTC_ );
+ }
+
+ SbiConstDef* pConst = pElem->GetConstDef();
+ pConst->Set( nCurrentEnumValue, SbxLONG );
+ }
+ }
+ if( pElem )
+ {
+ SbxArray *pEnumMembers = pEnum->GetProperties();
+ SbxProperty *pEnumElem = new SbxProperty( pElem->GetName(), SbxLONG );
+ pEnumElem->PutLong( nCurrentEnumValue );
+ pEnumElem->ResetFlag( SbxFlagBits::Write );
+ pEnumElem->SetFlag( SbxFlagBits::Const );
+ pEnumMembers->Insert(pEnumElem, pEnumMembers->Count());
+ }
+ }
+
+ pEnum->Remove( "Name", SbxClassType::DontCare );
+ pEnum->Remove( "Parent", SbxClassType::DontCare );
+
+ rEnumArray->Insert(pEnum, rEnumArray->Count());
+}
+
+
+// Procedure-Declaration
+// the first Token is already read in (SUB/FUNCTION)
+// xxx Name [LIB "name"[ALIAS "name"]][(Parameter)][AS TYPE]
+
+SbiProcDef* SbiParser::ProcDecl( bool bDecl )
+{
+ bool bFunc = ( eCurTok == FUNCTION );
+ bool bProp = ( eCurTok == GET || eCurTok == SET || eCurTok == LET );
+ if( !TestSymbol() ) return nullptr;
+ OUString aName( aSym );
+ SbxDataType eType = eScanType;
+ SbiProcDef* pDef = new SbiProcDef( this, aName, true );
+ pDef->SetType( eType );
+ if( Peek() == CDECL_ )
+ {
+ Next(); pDef->SetCdecl(true);
+ }
+ if( Peek() == LIB )
+ {
+ Next();
+ if( Next() == FIXSTRING )
+ {
+ pDef->GetLib() = aSym;
+ }
+ else
+ {
+ Error( ERRCODE_BASIC_SYNTAX );
+ }
+ }
+ if( Peek() == ALIAS )
+ {
+ Next();
+ if( Next() == FIXSTRING )
+ {
+ pDef->GetAlias() = aSym;
+ }
+ else
+ {
+ Error( ERRCODE_BASIC_SYNTAX );
+ }
+ }
+ if( !bDecl )
+ {
+ // CDECL, LIB and ALIAS are invalid
+ if( !pDef->GetLib().isEmpty() )
+ {
+ Error( ERRCODE_BASIC_UNEXPECTED, LIB );
+ }
+ if( !pDef->GetAlias().isEmpty() )
+ {
+ Error( ERRCODE_BASIC_UNEXPECTED, ALIAS );
+ }
+ if( pDef->IsCdecl() )
+ {
+ Error( ERRCODE_BASIC_UNEXPECTED, CDECL_ );
+ }
+ pDef->SetCdecl( false );
+ pDef->GetLib().clear();
+ pDef->GetAlias().clear();
+ }
+ else if( pDef->GetLib().isEmpty() )
+ {
+ // ALIAS and CDECL only together with LIB
+ if( !pDef->GetAlias().isEmpty() )
+ {
+ Error( ERRCODE_BASIC_UNEXPECTED, ALIAS );
+ }
+ if( pDef->IsCdecl() )
+ {
+ Error( ERRCODE_BASIC_UNEXPECTED, CDECL_ );
+ }
+ pDef->SetCdecl( false );
+ pDef->GetAlias().clear();
+ }
+ // Brackets?
+ if( Peek() == LPAREN )
+ {
+ Next();
+ if( Peek() == RPAREN )
+ {
+ Next();
+ }
+ else
+ {
+ for(;;)
+ {
+ bool bByVal = false;
+ bool bOptional = false;
+ bool bParamArray = false;
+ while( Peek() == BYVAL || Peek() == BYREF || Peek() == OPTIONAL_ )
+ {
+ if( Peek() == BYVAL )
+ {
+ bByVal = true;
+ }
+ else if ( Peek() == BYREF )
+ {
+ bByVal = false;
+ }
+ else if ( Peek() == OPTIONAL_ )
+ {
+ bOptional = true;
+ }
+ Next();
+ }
+ if( bCompatible && Peek() == PARAMARRAY )
+ {
+ if( bByVal || bOptional )
+ {
+ Error( ERRCODE_BASIC_UNEXPECTED, PARAMARRAY );
+ }
+ Next();
+ bParamArray = true;
+ }
+ SbiSymDef* pPar = VarDecl( nullptr, false, false );
+ if( !pPar )
+ {
+ break;
+ }
+ if( bByVal )
+ {
+ pPar->SetByVal(true);
+ }
+ if( bOptional )
+ {
+ pPar->SetOptional();
+ }
+ if( bParamArray )
+ {
+ pPar->SetParamArray();
+ }
+ if (SbiSymDef* pOldDef = pDef->GetParams().Find(pPar->GetName(), false))
+ {
+ Error(ERRCODE_BASIC_VAR_DEFINED, pPar->GetName());
+ delete pPar;
+ pPar = pOldDef;
+ }
+ else
+ pDef->GetParams().Add( pPar );
+ SbiToken eTok = Next();
+ if( eTok != COMMA && eTok != RPAREN )
+ {
+ bool bError2 = true;
+ if( bOptional && bCompatible && eTok == EQ )
+ {
+ auto pDefaultExpr = std::make_unique<SbiConstExpression>(this);
+ SbxDataType eType2 = pDefaultExpr->GetType();
+
+ sal_uInt16 nStringId;
+ if( eType2 == SbxSTRING )
+ {
+ nStringId = aGblStrings.Add( pDefaultExpr->GetString() );
+ }
+ else
+ {
+ nStringId = aGblStrings.Add( pDefaultExpr->GetValue(), eType2 );
+ }
+ pPar->SetDefaultId( nStringId );
+ pDefaultExpr.reset();
+
+ eTok = Next();
+ if( eTok == COMMA || eTok == RPAREN )
+ {
+ bError2 = false;
+ }
+ }
+ if( bError2 )
+ {
+ Error( ERRCODE_BASIC_EXPECTED, RPAREN );
+ break;
+ }
+ }
+ if( eTok == RPAREN )
+ {
+ break;
+ }
+ }
+ }
+ }
+ TypeDecl( *pDef );
+ if( eType != SbxVARIANT && pDef->GetType() != eType )
+ {
+ Error( ERRCODE_BASIC_BAD_DECLARATION, aName );
+ }
+ if( pDef->GetType() == SbxVARIANT && !( bFunc || bProp ) )
+ {
+ pDef->SetType( SbxEMPTY );
+ }
+ return pDef;
+}
+
+// DECLARE
+
+void SbiParser::Declare()
+{
+ DefDeclare( false );
+}
+
+void SbiParser::DefDeclare( bool bPrivate )
+{
+ Next();
+ if( eCurTok == PTRSAFE )
+ Next();
+
+ if( eCurTok != SUB && eCurTok != FUNCTION )
+ {
+ Error( ERRCODE_BASIC_UNEXPECTED, eCurTok );
+ }
+ else
+ {
+ bool bFunction = (eCurTok == FUNCTION);
+
+ SbiProcDef* pDef = ProcDecl( true );
+ if( pDef )
+ {
+ if( pDef->GetLib().isEmpty() )
+ {
+ Error( ERRCODE_BASIC_EXPECTED, LIB );
+ }
+ // Is it already there?
+ SbiSymDef* pOld = aPublics.Find( pDef->GetName() );
+ if( pOld )
+ {
+ SbiProcDef* p = pOld->GetProcDef();
+ if( !p )
+ {
+ // Declared as a variable
+ Error( ERRCODE_BASIC_BAD_DECLARATION, pDef->GetName() );
+ delete pDef;
+ pDef = nullptr;
+ }
+ else
+ {
+ pDef->Match( p );
+ }
+ }
+ else
+ {
+ aPublics.Add( pDef );
+ }
+ if ( pDef )
+ {
+ pDef->SetPublic( !bPrivate );
+
+ // New declare handling
+ if( !pDef->GetLib().isEmpty())
+ {
+ if( bNewGblDefs && nGblChain == 0 )
+ {
+ nGblChain = aGen.Gen( SbiOpcode::JUMP_, 0 );
+ bNewGblDefs = false;
+ }
+
+ sal_uInt16 nSavLine = nLine;
+ aGen.Statement();
+ pDef->Define();
+ pDef->SetLine1( nSavLine );
+ pDef->SetLine2( nSavLine );
+
+ SbiSymPool& rPool = pDef->GetParams();
+ sal_uInt16 nParCount = rPool.GetSize();
+
+ SbxDataType eType = pDef->GetType();
+ if( bFunction )
+ {
+ aGen.Gen( SbiOpcode::PARAM_, 0, sal::static_int_cast< sal_uInt16 >( eType ) );
+ }
+ if( nParCount > 1 )
+ {
+ aGen.Gen( SbiOpcode::ARGC_ );
+
+ for( sal_uInt16 i = 1 ; i < nParCount ; ++i )
+ {
+ SbiSymDef* pParDef = rPool.Get( i );
+ SbxDataType eParType = pParDef->GetType();
+
+ aGen.Gen( SbiOpcode::PARAM_, i, sal::static_int_cast< sal_uInt16 >( eParType ) );
+ aGen.Gen( SbiOpcode::ARGV_ );
+
+ sal_uInt16 nTyp = sal::static_int_cast< sal_uInt16 >( pParDef->GetType() );
+ if( pParDef->IsByVal() )
+ {
+ // Reset to avoid additional byval in call to wrapper function
+ pParDef->SetByVal( false );
+ nTyp |= 0x8000;
+ }
+ aGen.Gen( SbiOpcode::ARGTYP_, nTyp );
+ }
+ }
+
+ aGen.Gen( SbiOpcode::LIB_, aGblStrings.Add( pDef->GetLib() ) );
+
+ SbiOpcode eOp = pDef->IsCdecl() ? SbiOpcode::CALLC_ : SbiOpcode::CALL_;
+ sal_uInt16 nId = pDef->GetId();
+ if( !pDef->GetAlias().isEmpty() )
+ {
+ nId = ( nId & 0x8000 ) | aGblStrings.Add( pDef->GetAlias() );
+ }
+ if( nParCount > 1 )
+ {
+ nId |= 0x8000;
+ }
+ aGen.Gen( eOp, nId, sal::static_int_cast< sal_uInt16 >( eType ) );
+
+ if( bFunction )
+ {
+ aGen.Gen( SbiOpcode::PUT_ );
+ }
+ aGen.Gen( SbiOpcode::LEAVE_ );
+ }
+ }
+ }
+ }
+}
+
+void SbiParser::Attribute()
+{
+ // TODO: Need to implement the method as an attributed object.
+ while( Next() != EQ )
+ {
+ if( Next() != DOT)
+ {
+ break;
+ }
+ }
+
+ if( eCurTok != EQ )
+ {
+ Error( ERRCODE_BASIC_SYNTAX );
+ }
+ else
+ {
+ SbiExpression aValue( this );
+ }
+ // Don't generate any code - just discard it.
+}
+
+// Call of a SUB or a FUNCTION
+
+void SbiParser::Call()
+{
+ SbiExpression aVar( this, SbSYMBOL );
+ aVar.Gen( FORCE_CALL );
+ aGen.Gen( SbiOpcode::GET_ );
+}
+
+// SUB/FUNCTION
+
+void SbiParser::SubFunc()
+{
+ DefProc( false, false );
+}
+
+// Read in of a procedure
+
+void SbiParser::DefProc( bool bStatic, bool bPrivate )
+{
+ sal_uInt16 l1 = nLine;
+ bool bSub = ( eCurTok == SUB );
+ bool bProperty = ( eCurTok == PROPERTY );
+ PropertyMode ePropertyMode = PropertyMode::NONE;
+ if( bProperty )
+ {
+ Next();
+ if( eCurTok == GET )
+ {
+ ePropertyMode = PropertyMode::Get;
+ }
+ else if( eCurTok == LET )
+ {
+ ePropertyMode = PropertyMode::Let;
+ }
+ else if( eCurTok == SET )
+ {
+ ePropertyMode = PropertyMode::Set;
+ }
+ else
+ {
+ Error( ERRCODE_BASIC_EXPECTED, "Get or Let or Set" );
+ }
+ }
+
+ SbiToken eExit = eCurTok;
+ SbiProcDef* pDef = ProcDecl( false );
+ if( !pDef )
+ {
+ return;
+ }
+ pDef->setPropertyMode( ePropertyMode );
+
+ // Is the Proc already declared?
+ SbiSymDef* pOld = aPublics.Find( pDef->GetName() );
+ if( pOld )
+ {
+ pProc = pOld->GetProcDef();
+ if( !pProc )
+ {
+ // Declared as a variable
+ Error( ERRCODE_BASIC_BAD_DECLARATION, pDef->GetName() );
+ delete pDef;
+ return;
+ }
+ // #100027: Multiple declaration -> Error
+ // #112787: Not for setup, REMOVE for 8
+ else if( pProc->IsUsedForProcDecl() )
+ {
+ PropertyMode ePropMode = pDef->getPropertyMode();
+ if( ePropMode == PropertyMode::NONE || ePropMode == pProc->getPropertyMode() )
+ {
+ Error( ERRCODE_BASIC_PROC_DEFINED, pDef->GetName() );
+ delete pDef;
+ return;
+ }
+ }
+
+ pDef->Match( pProc );
+ }
+ else
+ {
+ aPublics.Add( pDef );
+ }
+ assert(pDef);
+ pProc = pDef;
+ pProc->SetPublic( !bPrivate );
+
+ // Now we set the search hierarchy for symbols as well as the
+ // current procedure.
+ aPublics.SetProcId( pProc->GetId() );
+ pProc->GetParams().SetParent( &aPublics );
+ if( bStatic )
+ {
+ if ( bVBASupportOn )
+ {
+ pProc->SetStatic();
+ }
+ else
+ {
+ Error( ERRCODE_BASIC_NOT_IMPLEMENTED ); // STATIC SUB ...
+ }
+ }
+ else
+ {
+ pProc->SetStatic( false );
+ }
+ // Normal case: Local variable->parameter->global variable
+ pProc->GetLocals().SetParent( &pProc->GetParams() );
+ pPool = &pProc->GetLocals();
+
+ pProc->Define();
+ OpenBlock( eExit );
+ StmntBlock( bSub ? ENDSUB : (bProperty ? ENDPROPERTY : ENDFUNC) );
+ sal_uInt16 l2 = nLine;
+ pProc->SetLine1( l1 );
+ pProc->SetLine2( l2 );
+ pPool = &aPublics;
+ aPublics.SetProcId( 0 );
+ // Open labels?
+ pProc->GetLabels().CheckRefs();
+ CloseBlock();
+ aGen.Gen( SbiOpcode::LEAVE_ );
+ pProc = nullptr;
+}
+
+// STATIC variable|procedure
+
+void SbiParser::Static()
+{
+ DefStatic( false );
+}
+
+void SbiParser::DefStatic( bool bPrivate )
+{
+ SbiSymPool* p;
+
+ switch( Peek() )
+ {
+ case SUB:
+ case FUNCTION:
+ case PROPERTY:
+ // End global chain if necessary (not done in
+ // SbiParser::Parse() under these conditions
+ if( bNewGblDefs && nGblChain == 0 )
+ {
+ nGblChain = aGen.Gen( SbiOpcode::JUMP_, 0 );
+ bNewGblDefs = false;
+ }
+ Next();
+ DefProc( true, bPrivate );
+ break;
+ default:
+ if( !pProc )
+ {
+ Error( ERRCODE_BASIC_NOT_IN_SUBR );
+ }
+ // Reset the Pool, so that STATIC-Declarations go into the
+ // global Pool
+ p = pPool;
+ pPool = &aPublics;
+ DefVar( SbiOpcode::STATIC_, true );
+ pPool = p;
+ break;
+ }
+}
+
+bool SbiParser::IsUnoInterface(const OUString& sTypeName)
+{
+ try
+ {
+ return css::reflection::theCoreReflection::get(
+ comphelper::getProcessComponentContext())->forName(sTypeName).is();
+ }
+ catch(const Exception&)
+ {
+ OSL_FAIL("Could not create reflection.CoreReflection.");
+ }
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/basic/source/comp/exprgen.cxx b/basic/source/comp/exprgen.cxx
new file mode 100644
index 0000000000..76f1ab776a
--- /dev/null
+++ b/basic/source/comp/exprgen.cxx
@@ -0,0 +1,281 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <basic/sberrors.hxx>
+
+#include <codegen.hxx>
+#include <expr.hxx>
+#include <parser.hxx>
+
+// Transform table for token operators and opcodes
+
+namespace {
+
+struct OpTable {
+ SbiToken eTok; // Token
+ SbiOpcode eOp; // Opcode
+};
+
+}
+
+const OpTable aOpTable [] = {
+ { EXPON,SbiOpcode::EXP_ },
+ { MUL, SbiOpcode::MUL_ },
+ { DIV, SbiOpcode::DIV_ },
+ { IDIV, SbiOpcode::IDIV_ },
+ { MOD, SbiOpcode::MOD_ },
+ { PLUS, SbiOpcode::PLUS_ },
+ { MINUS,SbiOpcode::MINUS_ },
+ { EQ, SbiOpcode::EQ_ },
+ { NE, SbiOpcode::NE_ },
+ { LE, SbiOpcode::LE_ },
+ { GE, SbiOpcode::GE_ },
+ { LT, SbiOpcode::LT_ },
+ { GT, SbiOpcode::GT_ },
+ { AND, SbiOpcode::AND_ },
+ { OR, SbiOpcode::OR_ },
+ { XOR, SbiOpcode::XOR_ },
+ { EQV, SbiOpcode::EQV_ },
+ { IMP, SbiOpcode::IMP_ },
+ { NOT, SbiOpcode::NOT_ },
+ { NEG, SbiOpcode::NEG_ },
+ { CAT, SbiOpcode::CAT_ },
+ { LIKE, SbiOpcode::LIKE_ },
+ { IS, SbiOpcode::IS_ },
+ { NIL, SbiOpcode::NOP_ }};
+
+// Output of an element
+void SbiExprNode::Gen( SbiCodeGen& rGen, RecursiveMode eRecMode )
+{
+ sal_uInt16 nStringId;
+
+ if( IsConstant() )
+ {
+ switch( GetType() )
+ {
+ case SbxEMPTY:
+ rGen.Gen( SbiOpcode::EMPTY_ );
+ break;
+ case SbxSTRING:
+ nStringId = rGen.GetParser()->aGblStrings.Add( aStrVal );
+ rGen.Gen( SbiOpcode::SCONST_, nStringId );
+ break;
+ default:
+ // tdf#131296 - generate SbiOpcode::NUMBER_ instead of SbiOpcode::CONST_
+ // for SbxINTEGER and SbxLONG including their numeric value and its data type,
+ // which will be restored in SbiRuntime::StepLOADNC.
+ nStringId = rGen.GetParser()->aGblStrings.Add( nVal, eType );
+ rGen.Gen( SbiOpcode::NUMBER_, nStringId );
+ break;
+ }
+ }
+ else if( IsOperand() )
+ {
+ SbiExprNode* pWithParent_ = nullptr;
+ SbiOpcode eOp;
+ if( aVar.pDef->GetScope() == SbPARAM )
+ {
+ eOp = SbiOpcode::PARAM_;
+ if( aVar.pDef->GetPos() == 0 )
+ {
+ 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() ? SbiOpcode::FIND_G_ : SbiOpcode::FIND_;
+ }
+ }
+ }
+ // special treatment for WITH
+ else if( (pWithParent_ = pWithParent) != nullptr )
+ {
+ eOp = SbiOpcode::ELEM_; // .-Term in WITH
+ }
+ else
+ {
+ eOp = ( aVar.pDef->GetScope() == SbRTL ) ? SbiOpcode::RTL_ :
+ (aVar.pDef->IsGlobal() ? SbiOpcode::FIND_G_ : SbiOpcode::FIND_);
+ }
+
+ if( eOp == SbiOpcode::FIND_ )
+ {
+
+ SbiProcDef* pProc = aVar.pDef->GetProcDef();
+ if ( rGen.GetParser()->bClassModule )
+ {
+ eOp = SbiOpcode::FIND_CM_;
+ }
+ else if ( aVar.pDef->IsStatic() || (pProc && pProc->IsStatic()) )
+ {
+ eOp = SbiOpcode::FIND_STATIC_;
+ }
+ }
+ for( SbiExprNode* p = this; p; p = p->aVar.pNext )
+ {
+ if( p == this && pWithParent_ != nullptr )
+ {
+ pWithParent_->Gen(rGen);
+ }
+ p->GenElement( rGen, eOp );
+ eOp = SbiOpcode::ELEM_;
+ }
+ }
+ else if( eNodeType == SbxTYPEOF )
+ {
+ pLeft->Gen(rGen);
+ rGen.Gen( SbiOpcode::TESTCLASS_, nTypeStrId );
+ }
+ else if( eNodeType == SbxNEW )
+ {
+ rGen.Gen( SbiOpcode::CREATE_, 0, nTypeStrId );
+ }
+ else
+ {
+ pLeft->Gen(rGen);
+ if( pRight )
+ {
+ pRight->Gen(rGen);
+ }
+ for( const OpTable* p = aOpTable; p->eTok != NIL; p++ )
+ {
+ if( p->eTok == eTok )
+ {
+ rGen.Gen( p->eOp ); break;
+ }
+ }
+ }
+}
+
+// Output of an operand element
+
+void SbiExprNode::GenElement( SbiCodeGen& rGen, SbiOpcode eOp )
+{
+#ifdef DBG_UTIL
+ if ((eOp < SbiOpcode::RTL_ || eOp > SbiOpcode::CALLC_) && eOp != SbiOpcode::FIND_G_ && eOp != SbiOpcode::FIND_CM_ && eOp != SbiOpcode::FIND_STATIC_)
+ rGen.GetParser()->Error( ERRCODE_BASIC_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 == SbiOpcode::PARAM_ ) ? pDef->GetPos() : pDef->GetId();
+ // Build a parameter list
+ if( aVar.pPar && aVar.pPar->GetSize() )
+ {
+ nId |= 0x8000;
+ aVar.pPar->Gen(rGen);
+ }
+
+ rGen.Gen( eOp, nId, sal::static_int_cast< sal_uInt16 >( GetType() ) );
+
+ if( aVar.pvMorePar )
+ {
+ for( auto& pExprList: *aVar.pvMorePar )
+ {
+ pExprList->Gen(rGen);
+ rGen.Gen( SbiOpcode::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(SbiCodeGen& rGen)
+{
+ if( aData.empty() )
+ return;
+
+ rGen.Gen( SbiOpcode::ARGC_ );
+ // Type adjustment at DECLARE
+
+ for( auto& pExpr: aData )
+ {
+ pExpr->Gen();
+ if( !pExpr->GetName().isEmpty() )
+ {
+ // named arg
+ sal_uInt16 nSid = rGen.GetParser()->aGblStrings.Add( pExpr->GetName() );
+ rGen.Gen( SbiOpcode::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( ERRCODE_BASIC_NO_NAMED_ARGS );
+
+ // Later, if Named Args at DECLARE is possible
+ //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
+ {
+ rGen.Gen( SbiOpcode::ARGV_ );
+ }
+ }
+}
+
+void SbiExpression::Gen( RecursiveMode eRecMode )
+{
+ // special treatment for WITH
+ // If pExpr == .-term in With, approximately Gen for Basis-Object
+ pExpr->Gen( pParser->aGen, eRecMode );
+ if( bByVal )
+ {
+ pParser->aGen.Gen( SbiOpcode::BYVAL_ );
+ }
+ if( bBased )
+ {
+ sal_uInt16 uBase = pParser->nBase;
+ if( pParser->IsCompatible() )
+ {
+ uBase |= 0x8000; // #109275 Flag compatibility
+ }
+ pParser->aGen.Gen( SbiOpcode::BASED_, uBase );
+ pParser->aGen.Gen( SbiOpcode::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 0000000000..ade1d5832b
--- /dev/null
+++ b/basic/source/comp/exprnode.cxx
@@ -0,0 +1,480 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <cmath>
+
+#include <o3tl/temporary.hxx>
+#include <parser.hxx>
+#include <expr.hxx>
+#include <tools/long.hxx>
+
+#include <basic/sberrors.hxx>
+
+#include <rtl/math.hxx>
+#include <utility>
+
+SbiExprNode::SbiExprNode( std::unique_ptr<SbiExprNode> l, SbiToken t, std::unique_ptr<SbiExprNode> r ) :
+ pLeft(std::move(l)),
+ pRight(std::move(r)),
+ pWithParent(nullptr),
+ eNodeType(SbxNODE),
+ eType(SbxVARIANT), // Nodes are always Variant
+ eTok(t),
+ bError(false)
+{
+}
+
+SbiExprNode::SbiExprNode( double n, SbxDataType t ):
+ nVal(n),
+ pWithParent(nullptr),
+ eNodeType(SbxNUMVAL),
+ eType(t),
+ eTok(NIL),
+ bError(false)
+{
+}
+
+SbiExprNode::SbiExprNode( OUString aVal ):
+ aStrVal(std::move(aVal)),
+ pWithParent(nullptr),
+ eNodeType(SbxSTRVAL),
+ eType(SbxSTRING),
+ eTok(NIL),
+ bError(false)
+{
+}
+
+SbiExprNode::SbiExprNode( const SbiSymDef& r, SbxDataType t, SbiExprListPtr l ) :
+ pWithParent(nullptr),
+ eNodeType(SbxVARVAL),
+ eTok(NIL),
+ bError(false)
+{
+ eType = ( t == SbxVARIANT ) ? r.GetType() : t;
+ aVar.pDef = const_cast<SbiSymDef*>(&r);
+ aVar.pPar = l.release();
+ aVar.pvMorePar = nullptr;
+ aVar.pNext= nullptr;
+}
+
+// #120061 TypeOf
+SbiExprNode::SbiExprNode( std::unique_ptr<SbiExprNode> l, sal_uInt16 nId ) :
+ nTypeStrId(nId),
+ pLeft(std::move(l)),
+ pWithParent(nullptr),
+ eNodeType(SbxTYPEOF),
+ eType(SbxBOOL),
+ eTok(NIL),
+ bError(false)
+{
+}
+
+// new <type>
+SbiExprNode::SbiExprNode( sal_uInt16 nId ) :
+ nTypeStrId(nId),
+ pWithParent(nullptr),
+ eNodeType(SbxNEW),
+ eType(SbxOBJECT),
+ eTok(NIL),
+ bError(false)
+{
+}
+
+SbiExprNode::SbiExprNode() :
+ pWithParent(nullptr),
+ eNodeType(SbxDUMMY),
+ eType(SbxVARIANT),
+ eTok(NIL),
+ bError(false)
+{
+}
+
+SbiExprNode::~SbiExprNode()
+{
+ if( IsVariable() )
+ {
+ delete aVar.pPar;
+ delete aVar.pNext;
+ delete aVar.pvMorePar;
+ }
+}
+
+SbiSymDef* SbiExprNode::GetVar()
+{
+ if( eNodeType == SbxVARVAL )
+ return aVar.pDef;
+ else
+ return nullptr;
+}
+
+SbiSymDef* SbiExprNode::GetRealVar()
+{
+ SbiExprNode* p = GetRealNode();
+ if( p )
+ return p->GetVar();
+ else
+ return nullptr;
+}
+
+// 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 nullptr;
+}
+
+// This method transform the type, if it fits into the Integer range
+
+void SbiExprNode::ConvertToIntConstIfPossible()
+{
+ if( eNodeType == SbxNUMVAL )
+ {
+ if( eType >= SbxINTEGER && eType <= SbxDOUBLE )
+ {
+ if( nVal >= SbxMININT && nVal <= SbxMAXINT && modf( nVal, &o3tl::temporary(double()) ) == 0 )
+ {
+ eType = SbxINTEGER;
+ }
+ }
+ }
+}
+
+bool SbiExprNode::IsNumber() const
+{
+ return eNodeType == SbxNUMVAL;
+}
+
+bool SbiExprNode::IsVariable() const
+{
+ return eNodeType == SbxVARVAL;
+}
+
+bool SbiExprNode::IsLvalue() const
+{
+ return IsVariable();
+}
+
+// Adjustment of a tree:
+// 1. Constant Folding
+// 2. Type-Adjustment
+// 3. Conversion of the operands into Strings
+// 4. Lifting of the composite- and error-bits
+
+void SbiExprNode::Optimize(SbiParser* pParser)
+{
+ FoldConstants(pParser);
+ CollectBits();
+}
+
+// Lifting of the error-bits
+
+void SbiExprNode::CollectBits()
+{
+ if( pLeft )
+ {
+ pLeft->CollectBits();
+ bError = bError || pLeft->bError;
+ }
+ if( pRight )
+ {
+ pRight->CollectBits();
+ bError = bError || pRight->bError;
+ }
+}
+
+// If a twig can be converted, True will be returned. In this case
+// the result is in the left twig.
+void SbiExprNode::FoldConstants(SbiParser* pParser)
+{
+ if( IsOperand() || eTok == LIKE ) return;
+
+ if (pLeft && !pRight)
+ FoldConstantsUnaryNode(pParser);
+ else if (pLeft && pRight)
+ FoldConstantsBinaryNode(pParser);
+
+ if( eNodeType == SbxNUMVAL )
+ {
+ // Potentially convolve in INTEGER (because of better opcode)?
+ if( eType == SbxSINGLE || eType == SbxDOUBLE )
+ {
+ if( nVal >= SbxMINLNG && nVal <= SbxMAXLNG
+ && !modf( nVal, &o3tl::temporary(double()) ) )
+ eType = SbxLONG;
+ }
+ if( eType == SbxLONG && nVal >= SbxMININT && nVal <= SbxMAXINT )
+ eType = SbxINTEGER;
+ }
+}
+
+void SbiExprNode::FoldConstantsBinaryNode(SbiParser* pParser)
+{
+ pLeft->FoldConstants(pParser);
+ pRight->FoldConstants(pParser);
+ if( !(pLeft->IsConstant() && pRight->IsConstant()
+ && pLeft->eNodeType == pRight->eNodeType) )
+ return;
+
+ CollectBits();
+ if( eTok == CAT )
+ // CAT affiliate also two numbers!
+ eType = SbxSTRING;
+ if( pLeft->eType == SbxSTRING )
+ // No Type Mismatch!
+ eType = SbxSTRING;
+ if( eType == SbxSTRING )
+ {
+ OUString rl( pLeft->GetString() );
+ OUString rr( pRight->GetString() );
+ pLeft.reset();
+ pRight.reset();
+ if( eTok == PLUS || eTok == CAT )
+ {
+ eTok = CAT;
+ // Linking:
+ aStrVal = rl;
+ aStrVal += rr;
+ eType = SbxSTRING;
+ eNodeType = SbxSTRVAL;
+ }
+ else
+ {
+ eType = SbxBOOL;
+ eNodeType = SbxNUMVAL;
+ int eRes = rr.compareTo( rl );
+ switch( eTok )
+ {
+ case EQ:
+ nVal = ( eRes == 0 ) ? SbxTRUE : SbxFALSE;
+ break;
+ case NE:
+ nVal = ( eRes != 0 ) ? SbxTRUE : SbxFALSE;
+ break;
+ case LT:
+ nVal = ( eRes > 0 ) ? SbxTRUE : SbxFALSE;
+ break;
+ case GT:
+ nVal = ( eRes < 0 ) ? SbxTRUE : SbxFALSE;
+ break;
+ case LE:
+ nVal = ( eRes >= 0 ) ? SbxTRUE : SbxFALSE;
+ break;
+ case GE:
+ nVal = ( eRes <= 0 ) ? SbxTRUE : SbxFALSE;
+ break;
+ default:
+ pParser->Error( ERRCODE_BASIC_CONVERSION );
+ bError = true;
+ break;
+ }
+ }
+ }
+ else
+ {
+ double nl = pLeft->nVal;
+ double nr = pRight->nVal;
+ // tdf#141201, tdf#147089 - round MOD/IDIV literals to Integer values
+ if (eTok == MOD || eTok == IDIV)
+ {
+ nl = rtl::math::round(nl);
+ nr = rtl::math::round(nr);
+ }
+ tools::Long ll = 0, lr = 0;
+ if( ( eTok >= AND && eTok <= IMP )
+ || eTok == IDIV || eTok == MOD )
+ {
+ // Integer operations
+ bool bErr = false;
+ if( nl > SbxMAXLNG )
+ {
+ bErr = true;
+ nl = SbxMAXLNG;
+ }
+ else if( nl < SbxMINLNG )
+ {
+ bErr = true;
+ nl = SbxMINLNG;
+ }
+ if( nr > SbxMAXLNG )
+ {
+ bErr = true;
+ nr = SbxMAXLNG;
+ }
+ else if( nr < SbxMINLNG )
+ {
+ bErr = true;
+ nr = SbxMINLNG;
+ }
+ ll = static_cast<tools::Long>(nl); lr = static_cast<tools::Long>(nr);
+ if( bErr )
+ {
+ pParser->Error( ERRCODE_BASIC_MATH_OVERFLOW );
+ bError = true;
+ }
+ }
+ bool bBothInt = ( pLeft->eType < SbxSINGLE
+ && pRight->eType < SbxSINGLE );
+ pLeft.reset();
+ pRight.reset();
+ nVal = 0;
+ eType = SbxDOUBLE;
+ eNodeType = SbxNUMVAL;
+ bool bCheckType = false;
+ switch( eTok )
+ {
+ case EXPON:
+ nVal = pow( nl, nr ); break;
+ case MUL:
+ bCheckType = true;
+ nVal = nl * nr; break;
+ case DIV:
+ if( !nr )
+ {
+ pParser->Error( ERRCODE_BASIC_ZERODIV ); nVal = HUGE_VAL;
+ bError = true;
+ } else nVal = nl / nr;
+ break;
+ case PLUS:
+ bCheckType = true;
+ nVal = nl + nr; break;
+ case MINUS:
+ bCheckType = true;
+ nVal = nl - nr; break;
+ case EQ:
+ nVal = ( nl == nr ) ? SbxTRUE : SbxFALSE;
+ eType = SbxBOOL; break;
+ case NE:
+ nVal = ( nl != nr ) ? SbxTRUE : SbxFALSE;
+ eType = SbxBOOL; break;
+ case LT:
+ nVal = ( nl < nr ) ? SbxTRUE : SbxFALSE;
+ eType = SbxBOOL; break;
+ case GT:
+ nVal = ( nl > nr ) ? SbxTRUE : SbxFALSE;
+ eType = SbxBOOL; break;
+ case LE:
+ nVal = ( nl <= nr ) ? SbxTRUE : SbxFALSE;
+ eType = SbxBOOL; break;
+ case GE:
+ nVal = ( nl >= nr ) ? SbxTRUE : SbxFALSE;
+ eType = SbxBOOL; break;
+ case IDIV:
+ if( !lr )
+ {
+ pParser->Error( ERRCODE_BASIC_ZERODIV ); nVal = HUGE_VAL;
+ bError = true;
+ } else nVal = ll / lr;
+ eType = SbxLONG; break;
+ case MOD:
+ if( !lr )
+ {
+ pParser->Error( ERRCODE_BASIC_ZERODIV ); nVal = HUGE_VAL;
+ bError = true;
+ } else nVal = ll - lr * (ll/lr);
+ eType = SbxLONG; break;
+ case AND:
+ nVal = static_cast<double>( ll & lr ); eType = SbxLONG; break;
+ case OR:
+ nVal = static_cast<double>( ll | lr ); eType = SbxLONG; break;
+ case XOR:
+ nVal = static_cast<double>( ll ^ lr ); eType = SbxLONG; break;
+ case EQV:
+ nVal = static_cast<double>( ~ll ^ lr ); eType = SbxLONG; break;
+ case IMP:
+ nVal = static_cast<double>( ~ll | lr ); eType = SbxLONG; break;
+ default: break;
+ }
+
+ if( !std::isfinite( nVal ) )
+ pParser->Error( ERRCODE_BASIC_MATH_OVERFLOW );
+
+ // Recover the data type to kill rounding error
+ if( bCheckType && bBothInt
+ && nVal >= SbxMINLNG && nVal <= SbxMAXLNG )
+ {
+ // Decimal place away
+ tools::Long n = static_cast<tools::Long>(nVal);
+ nVal = n;
+ eType = ( n >= SbxMININT && n <= SbxMAXINT )
+ ? SbxINTEGER : SbxLONG;
+ }
+ }
+
+}
+void SbiExprNode::FoldConstantsUnaryNode(SbiParser* pParser)
+{
+ pLeft->FoldConstants(pParser);
+ if (pLeft->IsNumber())
+ {
+ nVal = pLeft->nVal;
+ pLeft.reset();
+ eType = SbxDOUBLE;
+ eNodeType = SbxNUMVAL;
+ switch( eTok )
+ {
+ case NEG:
+ nVal = -nVal; break;
+ case NOT: {
+ // Integer operation!
+ bool bErr = false;
+ if( nVal > SbxMAXLNG )
+ {
+ bErr = true;
+ nVal = SbxMAXLNG;
+ }
+ else if( nVal < SbxMINLNG )
+ {
+ bErr = true;
+ nVal = SbxMINLNG;
+ }
+ if( bErr )
+ {
+ pParser->Error( ERRCODE_BASIC_MATH_OVERFLOW );
+ bError = true;
+ }
+ nVal = static_cast<double>(~static_cast<tools::Long>(nVal));
+ eType = SbxLONG;
+ } break;
+ default: break;
+ }
+ }
+ if( eNodeType == SbxNUMVAL )
+ {
+ // Potentially convolve in INTEGER (because of better opcode)?
+ if( eType == SbxSINGLE || eType == SbxDOUBLE )
+ {
+ if( nVal >= SbxMINLNG && nVal <= SbxMAXLNG
+ && !modf( nVal, &o3tl::temporary(double()) ) )
+ 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 0000000000..989f1c6330
--- /dev/null
+++ b/basic/source/comp/exprtree.cxx
@@ -0,0 +1,1123 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <memory>
+#include <parser.hxx>
+#include <basic/sberrors.hxx>
+#include <basic/sbmod.hxx>
+#include <comphelper/SetFlagContextHelper.hxx>
+#include <expr.hxx>
+
+SbiExpression::SbiExpression( SbiParser* p, SbiExprType t,
+ SbiExprMode eMode, const KeywordSymbolInfo* pKeywordSymbolInfo ) :
+ pParser(p),
+ eCurExpr(t),
+ m_eMode(eMode)
+{
+ pExpr = (t != SbSTDEXPR ) ? Term( pKeywordSymbolInfo ) : Boolean();
+ if( t != SbSYMBOL )
+ {
+ pExpr->Optimize(pParser);
+ }
+ if( t == SbLVALUE && !pExpr->IsLvalue() )
+ {
+ p->Error( ERRCODE_BASIC_LVALUE_EXPECTED );
+ }
+ if( t == SbOPERAND && !IsVariable() )
+ {
+ p->Error( ERRCODE_BASIC_VAR_EXPECTED );
+ }
+}
+
+SbiExpression::SbiExpression( SbiParser* p, double n, SbxDataType t ) :
+ pParser(p),
+ eCurExpr(SbOPERAND),
+ m_eMode(EXPRMODE_STANDARD)
+{
+ pExpr = std::make_unique<SbiExprNode>( n, t );
+ pExpr->Optimize(pParser);
+}
+
+SbiExpression::SbiExpression( SbiParser* p, const SbiSymDef& r, SbiExprListPtr pPar ) :
+ pParser(p),
+ eCurExpr(SbOPERAND),
+ m_eMode(EXPRMODE_STANDARD)
+{
+ pExpr = std::make_unique<SbiExprNode>( r, SbxVARIANT, std::move(pPar) );
+}
+
+SbiExpression::~SbiExpression() { }
+
+// reading in a complete identifier
+// an identifier has the following form:
+// name[(Parameter)][.Name[(parameter)]]...
+// structure elements are coupled via the element pNext,
+// so that they're not in the tree.
+
+// Are there parameters without brackets following? This may be a number,
+// a string, a symbol or also a comma (if the 1st parameter is missing)
+
+static bool DoParametersFollow( const SbiParser* p, SbiExprType eCurExpr, SbiToken eTok )
+{
+ if( eTok == LPAREN )
+ {
+ return true;
+ }
+ // but only if similar to CALL!
+ if( !p->WhiteSpace() || eCurExpr != SbSYMBOL )
+ {
+ return false;
+ }
+ if ( eTok == NUMBER || eTok == MINUS || eTok == FIXSTRING ||
+ eTok == SYMBOL || eTok == COMMA || eTok == DOT || eTok == NOT || eTok == BYVAL )
+ {
+ return true;
+ }
+ else // check for default params with reserved names ( e.g. names of tokens )
+ {
+ SbiTokenizer tokens( *static_cast<const SbiTokenizer*>(p) );
+ // Urk the Next() / Peek() semantics are... weird
+ tokens.Next();
+ if ( tokens.Peek() == ASSIGN )
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+// definition of a new symbol
+
+static SbiSymDef* AddSym ( SbiToken eTok, SbiSymPool& rPool, SbiExprType eCurExpr,
+ const OUString& rName, SbxDataType eType, const SbiExprList* pPar )
+{
+ SbiSymDef* pDef;
+ // A= is not a procedure
+ bool bHasType = ( eTok == EQ || eTok == DOT );
+ if( ( !bHasType && eCurExpr == SbSYMBOL ) || pPar )
+ {
+ // so this is a procedure
+ // the correct pool should be found out, as
+ // procs must always get into a public pool
+ SbiSymPool* pPool = &rPool;
+ if( pPool->GetScope() != SbPUBLIC )
+ {
+ pPool = &rPool.GetParser()->aPublics;
+ }
+ SbiProcDef* pProc = pPool->AddProc( rName );
+
+ // special treatment for Colls like Documents(1)
+ if( eCurExpr == SbSTDEXPR )
+ {
+ bHasType = true;
+ }
+ pDef = pProc;
+ pDef->SetType( bHasType ? eType : SbxEMPTY );
+ if( pPar )
+ {
+ // generate dummy parameters
+ for( sal_Int32 n = 1; n <= pPar->GetSize(); n++ )
+ {
+ OUString aPar = "PAR" + OUString::number( n );
+ pProc->GetParams().AddSym( aPar );
+ }
+ }
+ }
+ else
+ {
+ // or a normal symbol
+ pDef = rPool.AddSym( rName );
+ pDef->SetType( eType );
+ }
+ return pDef;
+}
+
+// currently even keywords are allowed (because of Dflt properties of the same name)
+
+std::unique_ptr<SbiExprNode> SbiExpression::Term( const KeywordSymbolInfo* pKeywordSymbolInfo )
+{
+ if( pParser->Peek() == DOT )
+ {
+ SbiExprNode* pWithVar = pParser->GetWithVar();
+ // #26608: get to the node-chain's end to pass the correct object
+ SbiSymDef* pDef = pWithVar ? pWithVar->GetRealVar() : nullptr;
+ std::unique_ptr<SbiExprNode> pNd;
+ if( !pDef )
+ {
+ pParser->Next();
+ }
+ else
+ {
+ pNd = ObjTerm( *pDef );
+ if( pNd )
+ {
+ pNd->SetWithParent( pWithVar );
+ }
+ }
+ if( !pNd )
+ {
+ pParser->Error( ERRCODE_BASIC_UNEXPECTED, DOT );
+ pNd = std::make_unique<SbiExprNode>( 1.0, SbxDOUBLE );
+ }
+ return pNd;
+ }
+
+ SbiToken eTok = (pKeywordSymbolInfo == nullptr) ? pParser->Next() : SYMBOL;
+ // memorize the parsing's begin
+ pParser->LockColumn();
+ OUString aSym( (pKeywordSymbolInfo == nullptr) ? pParser->GetSym() : pKeywordSymbolInfo->m_aKeywordSymbol );
+ SbxDataType eType = (pKeywordSymbolInfo == nullptr) ? pParser->GetType() : pKeywordSymbolInfo->m_eSbxDataType;
+ SbiExprListPtr pPar;
+ std::unique_ptr<SbiExprListVector> pvMoreParLcl;
+ // are there parameters following?
+ SbiToken eNextTok = pParser->Peek();
+ // is it a known parameter?
+ // create a string constant then, which will be recognized
+ // in the SbiParameters-ctor and is continued to be handled
+ if( eNextTok == ASSIGN )
+ {
+ pParser->UnlockColumn();
+ return std::make_unique<SbiExprNode>( aSym );
+ }
+ // no keywords allowed from here on!
+ if( SbiTokenizer::IsKwd( eTok )
+ && (!pParser->IsCompatible() || eTok != INPUT) )
+ {
+ pParser->Error( ERRCODE_BASIC_SYNTAX );
+ bError = true;
+ }
+
+ eTok = eNextTok;
+ if( DoParametersFollow( pParser, eCurExpr, eTok ) )
+ {
+ bool bStandaloneExpression = (m_eMode == EXPRMODE_STANDALONE);
+ pPar = SbiExprList::ParseParameters( pParser, bStandaloneExpression );
+ bError = bError || !pPar->IsValid();
+ if( !bError )
+ bBracket = pPar->IsBracket();
+ eTok = pParser->Peek();
+
+ // i75443 check for additional sets of parameters
+ while( eTok == LPAREN )
+ {
+ if( pvMoreParLcl == nullptr )
+ {
+ pvMoreParLcl.reset(new SbiExprListVector);
+ }
+ SbiExprListPtr pAddPar = SbiExprList::ParseParameters( pParser );
+ bError = bError || !pAddPar->IsValid();
+ pvMoreParLcl->push_back( std::move(pAddPar) );
+ eTok = pParser->Peek();
+ }
+ }
+ // It might be an object part, if . or ! is following.
+ // In case of . the variable must already be defined;
+ // it's an object, if pDef is NULL after the search.
+ bool bObj = ( ( eTok == DOT || eTok == EXCLAM )
+ && !pParser->WhiteSpace() );
+ if( bObj )
+ {
+ bBracket = false; // Now the bracket for the first term is obsolete
+ if( eType == SbxVARIANT )
+ {
+ eType = SbxOBJECT;
+ }
+ else
+ {
+ // Name%. really does not work!
+ pParser->Error( ERRCODE_BASIC_BAD_DECLARATION, aSym );
+ bError = true;
+ }
+ }
+ // Search:
+ SbiSymDef* pDef = pParser->pPool->Find( aSym );
+ if( !pDef )
+ {
+ // Part of the Runtime-Library?
+ // from 31.3.1996: swapped out to parser-method
+ // (is also needed in SbiParser::DefVar() in DIM.CXX)
+ pDef = pParser->CheckRTLForSym( aSym, eType );
+
+ // #i109184: Check if symbol is or later will be defined inside module
+ SbModule& rMod = pParser->aGen.GetModule();
+ if( rMod.FindMethod( aSym, SbxClassType::DontCare ) )
+ {
+ pDef = nullptr;
+ }
+ }
+ if( !pDef )
+ {
+ if( bObj )
+ {
+ eType = SbxOBJECT;
+ }
+ pDef = AddSym( eTok, *pParser->pPool, eCurExpr, aSym, eType, pPar.get() );
+ // 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
+ {
+
+ SbiConstDef* pConst = pDef->GetConstDef();
+ if( pConst )
+ {
+ pPar = nullptr;
+ pvMoreParLcl.reset();
+ if( pConst->GetType() == SbxSTRING )
+ {
+ return std::make_unique<SbiExprNode>( pConst->GetString() );
+ }
+ else
+ {
+ return std::make_unique<SbiExprNode>( pConst->GetValue(), pConst->GetType() );
+ }
+ }
+
+ // 0 parameters come up to ()
+ if( pDef->GetDims() )
+ {
+ if( pPar && pPar->GetSize() && pPar->GetSize() != pDef->GetDims() )
+ {
+ pParser->Error( ERRCODE_BASIC_WRONG_DIMS );
+ }
+ }
+ if( pDef->IsDefinedAs() )
+ {
+ SbxDataType eDefType = pDef->GetType();
+ // #119187 Only error if types conflict
+ if( eType >= SbxINTEGER && eType <= SbxSTRING && eType != eDefType )
+ {
+ // How? Define with AS first and take a Suffix then?
+ pParser->Error( ERRCODE_BASIC_BAD_DECLARATION, aSym );
+ bError = true;
+ }
+ else if ( eType == SbxVARIANT )
+ {
+ // if there's nothing named, take the type of the entry,
+ // but only if the var hasn't been defined with AS XXX
+ // so that we catch n% = 5 : print n
+ eType = eDefType;
+ }
+ }
+ // checking type of variables:
+ // is there named anything different in the scanner?
+ // That's OK for methods!
+ if( eType != SbxVARIANT && // Variant takes everything
+ eType != pDef->GetType() &&
+ !pDef->GetProcDef() )
+ {
+ // maybe pDef describes an object that so far has only been
+ // recognized as SbxVARIANT - then change type of pDef
+ // from 16.12.95 (similar cases possible perhaps?!?)
+ if( eType == SbxOBJECT && pDef->GetType() == SbxVARIANT )
+ {
+ pDef->SetType( SbxOBJECT );
+ }
+ else
+ {
+ pParser->Error( ERRCODE_BASIC_BAD_DECLARATION, aSym );
+ bError = true;
+ }
+ }
+ }
+ auto pNd = std::make_unique<SbiExprNode>( *pDef, eType );
+ if( !pPar )
+ {
+ pPar = SbiExprList::ParseParameters( pParser,false,false );
+ }
+ pNd->aVar.pPar = pPar.release();
+ pNd->aVar.pvMorePar = pvMoreParLcl.release();
+ if( bObj )
+ {
+ // from 8.1.95: Object may also be of the type SbxVARIANT
+ if( pDef->GetType() == SbxVARIANT )
+ pDef->SetType( SbxOBJECT );
+ // if we scan something with point,
+ // the type must be SbxOBJECT
+ if( pDef->GetType() != SbxOBJECT && pDef->GetType() != SbxVARIANT )
+ {
+ // defer error until runtime if in vba mode
+ if ( !pParser->IsVBASupportOn() )
+ {
+ pParser->Error( ERRCODE_BASIC_BAD_DECLARATION, aSym );
+ bError = true;
+ }
+ }
+ if( !bError )
+ {
+ pNd->aVar.pNext = ObjTerm( *pDef ).release();
+ }
+ }
+
+ pParser->UnlockColumn();
+ return pNd;
+}
+
+// construction of an object term. A term of this kind is part
+// of an expression that begins with an object variable.
+
+std::unique_ptr<SbiExprNode> SbiExpression::ObjTerm( SbiSymDef& rObj )
+{
+ pParser->Next();
+ SbiToken eTok = pParser->Next();
+ if( eTok != SYMBOL && !SbiTokenizer::IsKwd( eTok ) && !SbiTokenizer::IsExtra( eTok ) )
+ {
+ // #66745 Some operators can also be allowed
+ // as identifiers, important for StarOne
+ if( eTok != MOD && eTok != NOT && eTok != AND && eTok != OR &&
+ eTok != XOR && eTok != EQV && eTok != IMP && eTok != IS )
+ {
+ pParser->Error( ERRCODE_BASIC_VAR_EXPECTED );
+ bError = true;
+ }
+ }
+
+ if( bError )
+ {
+ return nullptr;
+ }
+ OUString aSym( pParser->GetSym() );
+ SbxDataType eType = pParser->GetType();
+ SbiExprListPtr pPar;
+ SbiExprListVector* pvMoreParLcl = nullptr;
+ eTok = pParser->Peek();
+
+ if( DoParametersFollow( pParser, eCurExpr, eTok ) )
+ {
+ pPar = SbiExprList::ParseParameters( pParser, false/*bStandaloneExpression*/ );
+ bError = bError || !pPar->IsValid();
+ eTok = pParser->Peek();
+
+ // i109624 check for additional sets of parameters
+ while( eTok == LPAREN )
+ {
+ if( pvMoreParLcl == nullptr )
+ {
+ pvMoreParLcl = new SbiExprListVector;
+ }
+ SbiExprListPtr pAddPar = SbiExprList::ParseParameters( pParser );
+ bError = bError || !pPar->IsValid();
+ pvMoreParLcl->push_back( std::move(pAddPar) );
+ eTok = pParser->Peek();
+ }
+ }
+ bool bObj = ( ( eTok == DOT || eTok == EXCLAM ) && !pParser->WhiteSpace() );
+ if( bObj )
+ {
+ if( eType == SbxVARIANT )
+ {
+ eType = SbxOBJECT;
+ }
+ else
+ {
+ // Name%. does really not work!
+ pParser->Error( ERRCODE_BASIC_BAD_DECLARATION, aSym );
+ bError = true;
+ }
+ }
+
+ // an object's symbol pool is always PUBLIC
+ SbiSymPool& rPool = rObj.GetPool();
+ rPool.SetScope( SbPUBLIC );
+ SbiSymDef* pDef = rPool.Find( aSym );
+ if( !pDef )
+ {
+ pDef = AddSym( eTok, rPool, eCurExpr, aSym, eType, pPar.get() );
+ pDef->SetType( eType );
+ }
+
+ auto pNd = std::make_unique<SbiExprNode>( *pDef, eType );
+ pNd->aVar.pPar = pPar.release();
+ pNd->aVar.pvMorePar = pvMoreParLcl;
+ if( bObj )
+ {
+ if( pDef->GetType() == SbxVARIANT )
+ {
+ pDef->SetType( SbxOBJECT );
+ }
+ if( pDef->GetType() != SbxOBJECT )
+ {
+ pParser->Error( ERRCODE_BASIC_BAD_DECLARATION, aSym );
+ bError = true;
+ }
+ if( !bError )
+ {
+ pNd->aVar.pNext = ObjTerm( *pDef ).release();
+ pNd->eType = eType;
+ }
+ }
+ return pNd;
+}
+
+// an operand can be:
+// constant
+// scalar variable
+// structure elements
+// array elements
+// functions
+// bracketed expressions
+
+std::unique_ptr<SbiExprNode> SbiExpression::Operand( bool bUsedForTypeOf )
+{
+ std::unique_ptr<SbiExprNode> pRes;
+
+ // test operand:
+ switch( SbiToken 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 = std::make_unique<SbiExprNode>( std::move(pRes), eTok, Like() );
+ }
+ break;
+ case DOT: // .with
+ pRes = Term(); break;
+ case NOT:
+ pRes = VBA_Not();
+ break;
+ case NUMBER:
+ pParser->Next();
+ pRes = std::make_unique<SbiExprNode>( pParser->GetDbl(), pParser->GetType() );
+ break;
+ case FIXSTRING:
+ pParser->Next();
+ pRes = std::make_unique<SbiExprNode>( pParser->GetSym() ); break;
+ case LPAREN:
+ pParser->Next();
+ if( nParenLevel == 0 && m_eMode == EXPRMODE_LPAREN_PENDING && pParser->Peek() == RPAREN )
+ {
+ m_eMode = EXPRMODE_EMPTY_PAREN;
+ pRes = std::make_unique<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( ERRCODE_BASIC_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--;
+ break;
+ default:
+ // keywords here are OK at the moment!
+ if( SbiTokenizer::IsKwd( eTok ) )
+ {
+ pRes = Term();
+ }
+ else
+ {
+ pParser->Next();
+ pRes = std::make_unique<SbiExprNode>( 1.0, SbxDOUBLE );
+ pParser->Error( ERRCODE_BASIC_UNEXPECTED, eTok );
+ }
+ break;
+ }
+ return pRes;
+}
+
+std::unique_ptr<SbiExprNode> SbiExpression::Unary()
+{
+ std::unique_ptr<SbiExprNode> pNd;
+ SbiToken eTok = pParser->Peek();
+ switch( eTok )
+ {
+ case MINUS:
+ eTok = NEG;
+ pParser->Next();
+ pNd = std::make_unique<SbiExprNode>( Unary(), eTok, nullptr );
+ break;
+ case NOT:
+ if( pParser->IsVBASupportOn() )
+ {
+ pNd = Operand();
+ }
+ else
+ {
+ pParser->Next();
+ pNd = std::make_unique<SbiExprNode>( Unary(), eTok, nullptr );
+ }
+ break;
+ case PLUS:
+ pParser->Next();
+ pNd = Unary();
+ break;
+ case TYPEOF:
+ {
+ pParser->Next();
+ std::unique_ptr<SbiExprNode> pObjNode = Operand( true/*bUsedForTypeOf*/ );
+ pParser->TestToken( IS );
+ SbiSymDef* pTypeDef = new SbiSymDef( OUString() );
+ pParser->TypeDecl( *pTypeDef, true );
+ pNd = std::make_unique<SbiExprNode>( std::move(pObjNode), pTypeDef->GetTypeId() );
+ break;
+ }
+ case NEW:
+ {
+ pParser->Next();
+ SbiSymDef* pTypeDef = new SbiSymDef( OUString() );
+ pParser->TypeDecl( *pTypeDef, true );
+ pNd = std::make_unique<SbiExprNode>( pTypeDef->GetTypeId() );
+ break;
+ }
+ default:
+ pNd = Operand();
+ }
+ return pNd;
+}
+
+std::unique_ptr<SbiExprNode> SbiExpression::Exp()
+{
+ std::unique_ptr<SbiExprNode> pNd = Unary();
+ if( m_eMode != EXPRMODE_EMPTY_PAREN )
+ {
+ while( pParser->Peek() == EXPON )
+ {
+ SbiToken eTok = pParser->Next();
+ pNd = std::make_unique<SbiExprNode>( std::move(pNd), eTok, Unary() );
+ }
+ }
+ return pNd;
+}
+
+std::unique_ptr<SbiExprNode> SbiExpression::MulDiv()
+{
+ std::unique_ptr<SbiExprNode> pNd = Exp();
+ if( m_eMode != EXPRMODE_EMPTY_PAREN )
+ {
+ for( ;; )
+ {
+ SbiToken eTok = pParser->Peek();
+ if( eTok != MUL && eTok != DIV )
+ {
+ break;
+ }
+ eTok = pParser->Next();
+ pNd = std::make_unique<SbiExprNode>( std::move(pNd), eTok, Exp() );
+ }
+ }
+ return pNd;
+}
+
+std::unique_ptr<SbiExprNode> SbiExpression::IntDiv()
+{
+ std::unique_ptr<SbiExprNode> pNd = MulDiv();
+ if( m_eMode != EXPRMODE_EMPTY_PAREN )
+ {
+ while( pParser->Peek() == IDIV )
+ {
+ SbiToken eTok = pParser->Next();
+ pNd = std::make_unique<SbiExprNode>( std::move(pNd), eTok, MulDiv() );
+ }
+ }
+ return pNd;
+}
+
+std::unique_ptr<SbiExprNode> SbiExpression::Mod()
+{
+ std::unique_ptr<SbiExprNode> pNd = IntDiv();
+ if( m_eMode != EXPRMODE_EMPTY_PAREN )
+ {
+ while( pParser->Peek() == MOD )
+ {
+ SbiToken eTok = pParser->Next();
+ pNd = std::make_unique<SbiExprNode>( std::move(pNd), eTok, IntDiv() );
+ }
+ }
+ return pNd;
+}
+
+std::unique_ptr<SbiExprNode> SbiExpression::AddSub()
+{
+ std::unique_ptr<SbiExprNode> pNd = Mod();
+ if( m_eMode != EXPRMODE_EMPTY_PAREN )
+ {
+ for( ;; )
+ {
+ SbiToken eTok = pParser->Peek();
+ if( eTok != PLUS && eTok != MINUS )
+ {
+ break;
+ }
+ eTok = pParser->Next();
+ pNd = std::make_unique<SbiExprNode>( std::move(pNd), eTok, Mod() );
+ }
+ }
+ return pNd;
+}
+
+std::unique_ptr<SbiExprNode> SbiExpression::Cat()
+{
+ std::unique_ptr<SbiExprNode> pNd = AddSub();
+ if( m_eMode != EXPRMODE_EMPTY_PAREN )
+ {
+ for( ;; )
+ {
+ SbiToken eTok = pParser->Peek();
+ if( eTok != CAT )
+ {
+ break;
+ }
+ eTok = pParser->Next();
+ pNd = std::make_unique<SbiExprNode>( std::move(pNd), eTok, AddSub() );
+ }
+ }
+ return pNd;
+}
+
+std::unique_ptr<SbiExprNode> SbiExpression::Comp()
+{
+ std::unique_ptr<SbiExprNode> pNd = Cat();
+ if( m_eMode != EXPRMODE_EMPTY_PAREN )
+ {
+ 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 = std::make_unique<SbiExprNode>( std::move(pNd), eTok, Cat() );
+ }
+ }
+ return pNd;
+}
+
+
+std::unique_ptr<SbiExprNode> SbiExpression::VBA_Not()
+{
+ std::unique_ptr<SbiExprNode> pNd;
+
+ SbiToken eTok = pParser->Peek();
+ if( eTok == NOT )
+ {
+ pParser->Next();
+ pNd = std::make_unique<SbiExprNode>( VBA_Not(), eTok, nullptr );
+ }
+ else
+ {
+ pNd = Comp();
+ }
+ return pNd;
+}
+
+std::unique_ptr<SbiExprNode> SbiExpression::Like()
+{
+ std::unique_ptr<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 = std::make_unique<SbiExprNode>( std::move(pNd), eTok, Comp() );
+ nCount++;
+ }
+ // multiple operands in a row does not work
+ if( nCount > 1 && !pParser->IsVBASupportOn() )
+ {
+ pParser->Error( ERRCODE_BASIC_SYNTAX );
+ bError = true;
+ }
+ }
+ return pNd;
+}
+
+std::unique_ptr<SbiExprNode> SbiExpression::Boolean()
+{
+ std::unique_ptr<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 = std::make_unique<SbiExprNode>( std::move(pNd), eTok, Like() );
+ }
+ }
+ return pNd;
+}
+
+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 special treatment for sal_Bool-constants
+ bool bIsBool = false;
+ if( pExpr->eNodeType == SbxVARVAL )
+ {
+ SbiSymDef* pVarDef = pExpr->GetVar();
+
+ bool bBoolVal = false;
+ if( pVarDef->GetName().equalsIgnoreAsciiCase( "true" ) )
+ {
+ bIsBool = true;
+ bBoolVal = true;
+ }
+ else if( pVarDef->GetName().equalsIgnoreAsciiCase( "false" ) )
+ //else if( pVarDef->GetName().ICompare( "false" ) == COMPARE_EQUAL )
+ {
+ bIsBool = true;
+ bBoolVal = false;
+ }
+
+ if( bIsBool )
+ {
+ pExpr = std::make_unique<SbiExprNode>( (bBoolVal ? SbxTRUE : SbxFALSE), SbxINTEGER );
+ eType = pExpr->GetType();
+ nVal = pExpr->nVal;
+ }
+ }
+
+ if( !bIsBool )
+ {
+ pParser->Error( ERRCODE_BASIC_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( ERRCODE_BASIC_OUT_OF_RANGE );
+ }
+ else if( n < SbxMININT )
+ {
+ n = SbxMININT;
+ pParser->Error( ERRCODE_BASIC_OUT_OF_RANGE );
+ }
+
+ return static_cast<short>(n);
+ }
+}
+
+
+SbiExprList::SbiExprList( )
+{
+ nDim = 0;
+ bError = false;
+ bBracket = false;
+}
+
+SbiExprList::~SbiExprList() {}
+
+SbiExpression* SbiExprList::Get( size_t n )
+{
+ return aData[n].get();
+}
+
+void SbiExprList::addExpression( std::unique_ptr<SbiExpression>&& pExpr )
+{
+ aData.push_back(std::move(pExpr));
+}
+
+// the parameter list is completely parsed
+// "procedurename()" is OK
+// it's a function without parameters then
+// i. e. you give an array as procedure parameter
+
+// #i79918/#i80532: bConst has never been set to true
+// -> reused as bStandaloneExpression
+//SbiParameters::SbiParameters( SbiParser* p, sal_Bool bConst, sal_Bool bPar) :
+SbiExprListPtr SbiExprList::ParseParameters( SbiParser* pParser, bool bStandaloneExpression, bool bPar)
+{
+ auto pExprList = std::make_unique<SbiExprList>();
+ if( !bPar )
+ {
+ return pExprList;
+ }
+
+ SbiToken eTok = pParser->Peek();
+
+ bool bAssumeExprLParenMode = false;
+ bool bAssumeArrayMode = false;
+ if( eTok == LPAREN )
+ {
+ if( bStandaloneExpression )
+ {
+ bAssumeExprLParenMode = true;
+ }
+ else
+ {
+ pExprList->bBracket = true;
+ pParser->Next();
+ eTok = pParser->Peek();
+ }
+ }
+
+
+ if( ( pExprList->bBracket && eTok == RPAREN ) || SbiTokenizer::IsEoln( eTok ) )
+ {
+ if( eTok == RPAREN )
+ {
+ pParser->Next();
+ }
+ return pExprList;
+ }
+ // read in parameter table and lay down in correct order!
+ while( !pExprList->bError )
+ {
+ std::unique_ptr<SbiExpression> pExpr;
+ // missing argument
+ if( eTok == COMMA )
+ {
+ pExpr = std::make_unique<SbiExpression>( pParser, 0, SbxEMPTY );
+ }
+ // named arguments: either .name= or name:=
+ else
+ {
+ bool bByVal = false;
+ if( eTok == BYVAL )
+ {
+ bByVal = true;
+ pParser->Next();
+ eTok = pParser->Peek();
+ }
+
+ if( bAssumeExprLParenMode )
+ {
+ pExpr = std::make_unique<SbiExpression>( pParser, SbSTDEXPR, EXPRMODE_LPAREN_PENDING );
+ bAssumeExprLParenMode = false;
+
+ SbiExprMode eModeAfter = pExpr->m_eMode;
+ if( eModeAfter == EXPRMODE_LPAREN_NOT_NEEDED )
+ {
+ pExprList->bBracket = true;
+ }
+ else if( eModeAfter == EXPRMODE_ARRAY_OR_OBJECT )
+ {
+ // Expression "looks" like an array assignment
+ // a(...)[(...)] = ? or a(...).b(...)
+ // RPAREN is already parsed
+ pExprList->bBracket = true;
+ bAssumeArrayMode = true;
+ eTok = NIL;
+ }
+ else if( eModeAfter == EXPRMODE_EMPTY_PAREN )
+ {
+ pExprList->bBracket = true;
+ return pExprList;
+ }
+ }
+ else
+ {
+ pExpr = std::make_unique<SbiExpression>( pParser );
+ }
+ if( bByVal && pExpr->IsLvalue() )
+ {
+ pExpr->SetByVal();
+ }
+ if( !bAssumeArrayMode )
+ {
+ OUString aName;
+ if( pParser->Peek() == ASSIGN )
+ {
+ // VBA mode: name:=
+ // SbiExpression::Term() has made as string out of it
+ aName = pExpr->GetString();
+ pParser->Next();
+ pExpr = std::make_unique<SbiExpression>( pParser );
+ }
+ pExpr->GetName() = aName;
+ }
+ }
+ pExprList->bError = pExprList->bError || !pExpr->IsValid();
+ pExprList->aData.push_back(std::move(pExpr));
+ if( bAssumeArrayMode )
+ {
+ break;
+ }
+ // next element?
+ eTok = pParser->Peek();
+ if( eTok != COMMA )
+ {
+ if( ( pExprList->bBracket && eTok == RPAREN ) || SbiTokenizer::IsEoln( eTok ) )
+ {
+ // tdf#80731
+ if (SbiTokenizer::IsEoln(eTok) && pExprList->bBracket)
+ {
+ // tdf#106529: only fail here in strict mode (i.e. when compiled from IDE), and
+ // allow legacy code with missing closing parenthesis when started e.g. from
+ // extensions and event handlers
+ if (comphelper::IsContextFlagActive("BasicStrict"))
+ {
+ pParser->Error(ERRCODE_BASIC_EXPECTED, RPAREN);
+ pExprList->bError = true;
+ }
+ }
+ break;
+ }
+ pParser->Error( pExprList->bBracket ? ERRCODE_BASIC_BAD_BRACKETS : ERRCODE_BASIC_EXPECTED, COMMA );
+ pExprList->bError = true;
+ }
+ else
+ {
+ pParser->Next();
+ eTok = pParser->Peek();
+ if( ( pExprList->bBracket && eTok == RPAREN ) || SbiTokenizer::IsEoln( eTok ) )
+ {
+ break;
+ }
+ }
+ }
+ // closing bracket
+ if( eTok == RPAREN )
+ {
+ pParser->Next();
+ pParser->Peek();
+ if( !pExprList->bBracket )
+ {
+ pParser->Error( ERRCODE_BASIC_BAD_BRACKETS );
+ pExprList->bError = true;
+ }
+ }
+ pExprList->nDim = pExprList->GetSize();
+ return pExprList;
+}
+
+// A list of array dimensions is parsed.
+
+SbiExprListPtr SbiExprList::ParseDimList( SbiParser* pParser )
+{
+ auto pExprList = std::make_unique<SbiExprList>();
+
+ if( pParser->Next() != LPAREN )
+ {
+ pParser->Error( ERRCODE_BASIC_EXPECTED, LPAREN );
+ pExprList->bError = true; return pExprList;
+ }
+
+ if( pParser->Peek() != RPAREN )
+ {
+ SbiToken eTok;
+ for( ;; )
+ {
+ auto pExpr1 = std::make_unique<SbiExpression>( pParser );
+ eTok = pParser->Next();
+ if( eTok == TO )
+ {
+ auto pExpr2 = std::make_unique<SbiExpression>( pParser );
+ pExpr1->ConvertToIntConstIfPossible();
+ pExpr2->ConvertToIntConstIfPossible();
+ eTok = pParser->Next();
+ pExprList->bError = pExprList->bError || !pExpr1->IsValid() || !pExpr2->IsValid();
+ pExprList->aData.push_back(std::move(pExpr1));
+ pExprList->aData.push_back(std::move(pExpr2));
+ }
+ else
+ {
+ pExpr1->SetBased();
+ pExpr1->ConvertToIntConstIfPossible();
+ pExprList->bError = pExprList->bError || !pExpr1->IsValid();
+ pExprList->aData.push_back(std::move(pExpr1));
+ }
+ pExprList->nDim++;
+ if( eTok == RPAREN ) break;
+ if( eTok != COMMA )
+ {
+ pParser->Error( ERRCODE_BASIC_BAD_BRACKETS );
+ pParser->Next();
+ break;
+ }
+ }
+ }
+ else pParser->Next();
+ return pExprList;
+}
+
+/* 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 0000000000..9e91413fd9
--- /dev/null
+++ b/basic/source/comp/io.cxx
@@ -0,0 +1,309 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <basic/sberrors.hxx>
+#include <parser.hxx>
+#include <iosys.hxx>
+#include <memory>
+
+// test if there's an I/O channel
+
+bool SbiParser::Channel( bool bAlways )
+{
+ bool bRes = false;
+ Peek();
+ if( IsHash() )
+ {
+ SbiExpression aExpr( this );
+ while( Peek() == COMMA || Peek() == SEMICOLON )
+ Next();
+ aExpr.Gen();
+ aGen.Gen( SbiOpcode::CHANNEL_ );
+ bRes = true;
+ }
+ else if( bAlways )
+ Error( ERRCODE_BASIC_EXPECTED, "#" );
+ return bRes;
+}
+
+// it's tried that at object variables the Default-
+// Property is addressed for PRINT and WRITE
+
+void SbiParser::Print()
+{
+ bool bChan = Channel();
+
+ while( !bAbort )
+ {
+ if( !IsEoln( Peek() ) )
+ {
+ auto pExpr = std::make_unique<SbiExpression>(this);
+ pExpr->Gen();
+ pExpr.reset();
+ Peek();
+ aGen.Gen( eCurTok == COMMA ? SbiOpcode::PRINTF_ : SbiOpcode::BPRINT_ );
+ }
+ if( eCurTok == COMMA || eCurTok == SEMICOLON )
+ {
+ Next();
+ if( IsEoln( Peek() ) ) break;
+ }
+ else
+ {
+ aGen.Gen( SbiOpcode::PRCHAR_, '\n' );
+ break;
+ }
+ }
+ if( bChan )
+ aGen.Gen( SbiOpcode::CHAN0_ );
+}
+
+// WRITE #chan, expr, ...
+
+void SbiParser::Write()
+{
+ bool bChan = Channel();
+
+ while( !bAbort )
+ {
+ auto pExpr = std::make_unique<SbiExpression>(this);
+ pExpr->Gen();
+ pExpr.reset();
+ aGen.Gen( SbiOpcode::BWRITE_ );
+ if( Peek() == COMMA )
+ {
+ aGen.Gen( SbiOpcode::PRCHAR_, ',' );
+ Next();
+ if( IsEoln( Peek() ) ) break;
+ }
+ else
+ {
+ aGen.Gen( SbiOpcode::PRCHAR_, '\n' );
+ break;
+ }
+ }
+ if( bChan )
+ aGen.Gen( SbiOpcode::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 = "line";
+ aInfo.m_eSbxDataType = GetType();
+
+ Symbol( &aInfo );
+ }
+}
+
+
+// LINE INPUT [prompt], var$
+
+void SbiParser::LineInput()
+{
+ Channel( true );
+ auto pExpr = std::make_unique<SbiExpression>( this, SbOPERAND );
+ if( !pExpr->IsVariable() )
+ Error( ERRCODE_BASIC_VAR_EXPECTED );
+ if( pExpr->GetType() != SbxVARIANT && pExpr->GetType() != SbxSTRING )
+ Error( ERRCODE_BASIC_CONVERSION );
+ pExpr->Gen();
+ aGen.Gen( SbiOpcode::LINPUT_ );
+ pExpr.reset();
+ aGen.Gen( SbiOpcode::CHAN0_ ); // ResetChannel() not in StepLINPUT() anymore
+}
+
+// INPUT
+
+void SbiParser::Input()
+{
+ aGen.Gen( SbiOpcode::RESTART_ );
+ Channel( true );
+ auto pExpr = std::make_unique<SbiExpression>( this, SbOPERAND );
+ while( !bAbort )
+ {
+ if( !pExpr->IsVariable() )
+ Error( ERRCODE_BASIC_VAR_EXPECTED );
+ pExpr->Gen();
+ aGen.Gen( SbiOpcode::INPUT_ );
+ if( Peek() == COMMA )
+ {
+ Next();
+ pExpr.reset(new SbiExpression( this, SbOPERAND ));
+ }
+ else break;
+ }
+ pExpr.reset();
+ aGen.Gen( SbiOpcode::CHAN0_ );
+}
+
+// OPEN stringexpr FOR mode ACCESS access mode AS Channel [Len=n]
+
+void SbiParser::Open()
+{
+ bInStatement = true;
+ SbiExpression aFileName( this );
+ SbiToken eTok;
+ TestToken( FOR );
+ StreamMode nMode = StreamMode::NONE;
+ SbiStreamFlags nFlags = SbiStreamFlags::NONE;
+ switch( Next() )
+ {
+ case INPUT:
+ nMode = StreamMode::READ; nFlags |= SbiStreamFlags::Input; break;
+ case OUTPUT:
+ nMode = StreamMode::WRITE | StreamMode::TRUNC; nFlags |= SbiStreamFlags::Output; break;
+ case APPEND:
+ nMode = StreamMode::WRITE; nFlags |= SbiStreamFlags::Append; break;
+ case RANDOM:
+ nMode = StreamMode::READ | StreamMode::WRITE; nFlags |= SbiStreamFlags::Random; break;
+ case BINARY:
+ nMode = StreamMode::READ | StreamMode::WRITE; nFlags |= SbiStreamFlags::Binary; break;
+ default:
+ Error( ERRCODE_BASIC_SYNTAX );
+ }
+ if( Peek() == ACCESS )
+ {
+ Next();
+ eTok = Next();
+ // influence only READ,WRITE-Flags in nMode
+ nMode &= ~StreamMode(StreamMode::READ | StreamMode::WRITE); // delete
+ if( eTok == READ )
+ {
+ if( Peek() == WRITE )
+ {
+ Next();
+ nMode |= StreamMode::READ | StreamMode::WRITE;
+ }
+ else
+ nMode |= StreamMode::READ;
+ }
+ else if( eTok == WRITE )
+ nMode |= StreamMode::WRITE;
+ else
+ Error( ERRCODE_BASIC_SYNTAX );
+ }
+ switch( Peek() )
+ {
+ case SHARED:
+ Next(); nMode |= StreamMode::SHARE_DENYNONE; break;
+ case LOCK:
+ Next();
+ eTok = Next();
+ if( eTok == READ )
+ {
+ if( Peek() == WRITE )
+ {
+ Next();
+ nMode |= StreamMode::SHARE_DENYALL;
+ }
+ else nMode |= StreamMode::SHARE_DENYREAD;
+ }
+ else if( eTok == WRITE )
+ nMode |= StreamMode::SHARE_DENYWRITE;
+ else
+ Error( ERRCODE_BASIC_SYNTAX );
+ break;
+ default: break;
+ }
+ TestToken( AS );
+ // channel number
+ auto pChan = std::make_unique<SbiExpression>( this );
+ std::unique_ptr<SbiExpression> pLen;
+ if( Peek() == SYMBOL )
+ {
+ Next();
+ if( aSym.equalsIgnoreAsciiCase("LEN") )
+ {
+ TestToken( EQ );
+ pLen.reset(new SbiExpression( this ));
+ }
+ }
+ if( !pLen ) pLen.reset(new SbiExpression( this, 128, SbxINTEGER ));
+ // the stack for the OPEN command looks as follows:
+ // block length
+ // channel number
+ // file name
+ pLen->Gen();
+ pChan->Gen();
+ aFileName.Gen();
+ aGen.Gen( SbiOpcode::OPEN_, static_cast<sal_uInt32>(nMode), static_cast<sal_uInt32>(nFlags) );
+ bInStatement = false;
+}
+
+// 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 = "name";
+ aInfo.m_eSbxDataType = GetType();
+
+ Symbol( &aInfo );
+ return;
+ }
+ SbiExpression aExpr1( this );
+ TestToken( AS );
+ SbiExpression aExpr2( this );
+ aExpr1.Gen();
+ aExpr2.Gen();
+ aGen.Gen( SbiOpcode::RENAME_ );
+}
+
+// CLOSE [n,...]
+
+void SbiParser::Close()
+{
+ Peek();
+ if( IsEoln( eCurTok ) )
+ aGen.Gen( SbiOpcode::CLOSE_, 0 );
+ else
+ for( ;; )
+ {
+ SbiExpression aExpr( this );
+ while( Peek() == COMMA || Peek() == SEMICOLON )
+ Next();
+ aExpr.Gen();
+ aGen.Gen( SbiOpcode::CHANNEL_ );
+ aGen.Gen( SbiOpcode::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 0000000000..07aac44943
--- /dev/null
+++ b/basic/source/comp/loops.cxx
@@ -0,0 +1,572 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <parser.hxx>
+#include <memory>
+
+#include <basic/sberrors.hxx>
+
+// Single-line IF and Multiline IF
+
+void SbiParser::If()
+{
+ sal_uInt32 nEndLbl;
+ SbiToken eTok = NIL;
+ // ignore end-tokens
+ SbiExpression aCond( this );
+ aCond.Gen();
+ TestToken( THEN );
+ if( IsEoln( Next() ) )
+ {
+ // At the end of each block a jump to ENDIF must be inserted,
+ // so that the condition is not evaluated again at ELSEIF.
+ // The table collects all jump points.
+#define JMP_TABLE_SIZE 100
+ sal_uInt32 pnJmpToEndLbl[JMP_TABLE_SIZE]; // 100 ELSEIFs allowed
+ sal_uInt16 iJmp = 0; // current table index
+
+ // multiline IF
+ nEndLbl = aGen.Gen( SbiOpcode::JUMPF_, 0 );
+ eTok = Peek();
+ while( !( eTok == ELSEIF || eTok == ELSE || eTok == ENDIF ) &&
+ !bAbort && Parse() )
+ {
+ eTok = Peek();
+ if( IsEof() )
+ {
+ Error( ERRCODE_BASIC_BAD_BLOCK, IF ); bAbort = true; return;
+ }
+ }
+ while( eTok == ELSEIF )
+ {
+ // jump to ENDIF in case of a successful IF/ELSEIF
+ if( iJmp >= JMP_TABLE_SIZE )
+ {
+ Error( ERRCODE_BASIC_PROG_TOO_LARGE ); bAbort = true; return;
+ }
+ pnJmpToEndLbl[iJmp++] = aGen.Gen( SbiOpcode::JUMP_, 0 );
+
+ Next();
+ aGen.BackChain( nEndLbl );
+
+ aGen.Statement();
+ auto pCond = std::make_unique<SbiExpression>( this );
+ pCond->Gen();
+ nEndLbl = aGen.Gen( SbiOpcode::JUMPF_, 0 );
+ pCond.reset();
+ TestToken( THEN );
+ eTok = Peek();
+ while( !( eTok == ELSEIF || eTok == ELSE || eTok == ENDIF ) &&
+ !bAbort && Parse() )
+ {
+ eTok = Peek();
+ if( IsEof() )
+ {
+ Error( ERRCODE_BASIC_BAD_BLOCK, ELSEIF ); bAbort = true; return;
+ }
+ }
+ }
+ if( eTok == ELSE )
+ {
+ Next();
+ sal_uInt32 nElseLbl = nEndLbl;
+ nEndLbl = aGen.Gen( SbiOpcode::JUMP_, 0 );
+ aGen.BackChain( nElseLbl );
+
+ aGen.Statement();
+ StmntBlock( ENDIF );
+ }
+ else if( eTok == ENDIF )
+ Next();
+
+
+ while( iJmp > 0 )
+ {
+ iJmp--;
+ aGen.BackChain( pnJmpToEndLbl[iJmp] );
+ }
+ }
+ else
+ {
+ // single line IF
+ bSingleLineIf = true;
+ nEndLbl = aGen.Gen( SbiOpcode::JUMPF_, 0 );
+ Push( eCurTok );
+ // tdf#128263: update push positions to correctly restore in Next()
+ nPLine = nLine;
+ nPCol1 = nCol1;
+ nPCol2 = nCol2;
+
+ 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( SbiOpcode::JUMP_, 0 );
+ aGen.BackChain( nElseLbl );
+ while( !bAbort )
+ {
+ if( !Parse() ) break;
+ eTok = Peek();
+ if( eTok == EOLN || eTok == REM )
+ break;
+ }
+ }
+ bSingleLineIf = false;
+ }
+ aGen.BackChain( nEndLbl );
+}
+
+// ELSE/ELSEIF/ENDIF without IF
+
+void SbiParser::NoIf()
+{
+ Error( ERRCODE_BASIC_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 ? SbiOpcode::JUMPF_ : SbiOpcode::JUMPT_, nStartLbl );
+ } else
+ if (eTok == EOLN || eTok == REM)
+ aGen.Gen (SbiOpcode::JUMP_, nStartLbl);
+ else
+ Error( ERRCODE_BASIC_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 ? SbiOpcode::JUMPT_ : SbiOpcode::JUMPF_, 0 );
+ StmntBlock( LOOP );
+ TestEoln();
+ aGen.Gen( SbiOpcode::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( SbiOpcode::JUMPF_, 0 );
+ StmntBlock( WEND );
+ aGen.Gen( SbiOpcode::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 on the Stack
+
+ if( bForEach )
+ {
+ TestToken( IN_ );
+ SbiExpression aCollExpr( this, SbOPERAND );
+ aCollExpr.Gen(); // Collection var to for stack
+ TestEoln();
+ aGen.Gen( SbiOpcode::INITFOREACH_ );
+ }
+ else
+ {
+ TestToken( EQ );
+ SbiExpression aStartExpr( this );
+ aStartExpr.Gen();
+ TestToken( TO );
+ SbiExpression aStopExpr( this );
+ aStopExpr.Gen();
+ if( Peek() == STEP )
+ {
+ Next();
+ SbiExpression aStepExpr( this );
+ aStepExpr.Gen();
+ }
+ else
+ {
+ SbiExpression aOne( this, 1, SbxINTEGER );
+ aOne.Gen();
+ }
+ TestEoln();
+ // The stack has all 4 elements now: variable, start, end, increment
+ // bind start value
+ aGen.Gen( SbiOpcode::INITFOR_ );
+ }
+
+ sal_uInt32 nLoop = aGen.GetPC();
+ // do tests, maybe free the stack
+ sal_uInt32 nEndTarget = aGen.Gen( SbiOpcode::TESTFOR_, 0 );
+ OpenBlock( FOR );
+ StmntBlock( NEXT );
+ aGen.Gen( SbiOpcode::NEXT_ );
+ aGen.Gen( SbiOpcode::JUMP_, nLoop );
+ // are there variables after NEXT?
+ if( Peek() == SYMBOL )
+ {
+ SbiExpression aVar( this, SbOPERAND );
+ if( aVar.GetRealVar() != aLvalue.GetRealVar() )
+ Error( ERRCODE_BASIC_EXPECTED, aLvalue.GetRealVar()->GetName() );
+ }
+ aGen.BackChain( nEndTarget );
+ CloseBlock();
+}
+
+// WITH .. END WITH
+
+void SbiParser::With()
+{
+ SbiExpression aVar( this, SbOPERAND );
+
+ SbiExprNode *pNode = aVar.GetExprNode()->GetRealNode();
+ if (!pNode)
+ return;
+ SbiSymDef* pDef = pNode->GetVar();
+ // Variant, from 27.6.1997, #41090: empty -> must be Object
+ if( pDef->GetType() == SbxVARIANT || pDef->GetType() == SbxEMPTY )
+ pDef->SetType( SbxOBJECT );
+ else if( pDef->GetType() != SbxOBJECT )
+ Error( ERRCODE_BASIC_NEEDS_OBJECT );
+
+
+ pNode->SetType( SbxOBJECT );
+
+ OpenBlock( NIL, aVar.GetExprNode() );
+ StmntBlock( ENDWITH );
+ CloseBlock();
+}
+
+// LOOP/NEXT/WEND without construct
+
+void SbiParser::BadBlock()
+{
+ if( eEndTok )
+ Error( ERRCODE_BASIC_BAD_BLOCK, eEndTok );
+ else
+ Error( ERRCODE_BASIC_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( SbiOpcode::ONJUMP_, 0 );
+ SbiToken eTok = Next();
+ if( eTok != GOTO && eTok != GOSUB )
+ {
+ Error( ERRCODE_BASIC_EXPECTED, "GoTo/GoSub" );
+ eTok = GOTO;
+ }
+
+ sal_uInt32 nLbl = 0;
+ do
+ {
+ Next(); // get label
+ if( MayBeLabel() )
+ {
+ sal_uInt32 nOff = pProc->GetLabels().Reference( aSym );
+ aGen.Gen( SbiOpcode::JUMP_, nOff );
+ nLbl++;
+ }
+ else Error( ERRCODE_BASIC_LABEL_EXPECTED );
+ }
+ while( !bAbort && TestComma() );
+ if( eTok == GOSUB )
+ nLbl |= 0x8000;
+ aGen.Patch( nLabelsTarget, nLbl );
+}
+
+// GOTO/GOSUB
+
+void SbiParser::Goto()
+{
+ SbiOpcode eOp = eCurTok == GOTO ? SbiOpcode::JUMP_ : SbiOpcode::GOSUB_;
+ Next();
+ if( MayBeLabel() )
+ {
+ sal_uInt32 nOff = pProc->GetLabels().Reference( aSym );
+ aGen.Gen( eOp, nOff );
+ }
+ else Error( ERRCODE_BASIC_LABEL_EXPECTED );
+}
+
+// RETURN [label]
+
+void SbiParser::Return()
+{
+ Next();
+ if( MayBeLabel() )
+ {
+ sal_uInt32 nOff = pProc->GetLabels().Reference( aSym );
+ aGen.Gen( SbiOpcode::RETURN_, nOff );
+ }
+ else aGen.Gen( SbiOpcode::RETURN_, 0 );
+}
+
+// SELECT CASE
+
+void SbiParser::Select()
+{
+ TestToken( CASE );
+ SbiExpression aCase( this );
+ SbiToken eTok = NIL;
+ aCase.Gen();
+ aGen.Gen( SbiOpcode::CASE_ );
+ TestEoln();
+ sal_uInt32 nNextTarget = 0;
+ sal_uInt32 nDoneTarget = 0;
+ bool bElse = false;
+
+ while( !bAbort )
+ {
+ eTok = Next();
+ if( eTok == CASE )
+ {
+ if( nNextTarget )
+ {
+ aGen.BackChain( nNextTarget );
+ nNextTarget = 0;
+ }
+ aGen.Statement();
+
+ bool bDone = false;
+ sal_uInt32 nTrueTarget = 0;
+ if( Peek() == ELSE )
+ {
+ // CASE ELSE
+ Next();
+ bElse = true;
+ }
+ else while( !bDone )
+ {
+ if( bElse )
+ Error( ERRCODE_BASIC_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( ERRCODE_BASIC_SYNTAX );
+ else Next();
+ SbiExpression aCompare( this );
+ aCompare.Gen();
+ nTrueTarget = aGen.Gen(
+ SbiOpcode::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( SbiOpcode::CASETO_, nTrueTarget );
+ }
+ else
+ // CASE a
+ nTrueTarget = aGen.Gen( SbiOpcode::CASEIS_, nTrueTarget, SbxEQ );
+
+ }
+ if( Peek() == COMMA ) Next();
+ else
+ {
+ TestEoln();
+ bDone = true;
+ }
+ }
+
+ if( !bElse )
+ {
+ nNextTarget = aGen.Gen( SbiOpcode::JUMP_, nNextTarget );
+ aGen.BackChain( nTrueTarget );
+ }
+ // build the statement body
+ 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( SbiOpcode::JUMP_, nDoneTarget );
+ }
+ else if( !IsEoln( eTok ) )
+ break;
+ }
+done:
+ if( eTok != ENDSELECT )
+ Error( ERRCODE_BASIC_EXPECTED, ENDSELECT );
+ if( nNextTarget )
+ aGen.BackChain( nNextTarget );
+ aGen.BackChain( nDoneTarget );
+ aGen.Gen( SbiOpcode::ENDCASE_ );
+}
+
+// ON Error/Variable
+
+void SbiParser::On()
+{
+ SbiToken eTok = Peek();
+ OUString aString = SbiTokenizer::Symbol(eTok);
+ if (aString.equalsIgnoreAsciiCase("ERROR"))
+ {
+ eTok = ERROR_; // Error comes as SYMBOL
+ }
+ if( eTok != ERROR_ && eTok != LOCAL )
+ {
+ OnGoto();
+ }
+ else
+ {
+ if( eTok == LOCAL )
+ {
+ Next();
+ }
+ Next (); // no more TestToken, as there'd be an error otherwise
+
+ Next(); // get token after error
+ if( eCurTok == GOTO )
+ {
+ // ON ERROR GOTO label|0
+ Next();
+ bool bError_ = false;
+ if( MayBeLabel() )
+ {
+ if( eCurTok == NUMBER && !nVal )
+ {
+ aGen.Gen( SbiOpcode::STDERROR_ );
+ }
+ else
+ {
+ sal_uInt32 nOff = pProc->GetLabels().Reference( aSym );
+ aGen.Gen( SbiOpcode::ERRHDL_, nOff );
+ }
+ }
+ else if( eCurTok == MINUS )
+ {
+ Next();
+ if( eCurTok == NUMBER && nVal == 1 )
+ {
+ aGen.Gen( SbiOpcode::STDERROR_ );
+ }
+ else
+ {
+ bError_ = true;
+ }
+ }
+ if( bError_ )
+ {
+ Error( ERRCODE_BASIC_LABEL_EXPECTED );
+ }
+ }
+ else if( eCurTok == RESUME )
+ {
+ TestToken( NEXT );
+ aGen.Gen( SbiOpcode::NOERROR_ );
+ }
+ else Error( ERRCODE_BASIC_EXPECTED, "GoTo/Resume" );
+ }
+}
+
+// RESUME [0]|NEXT|label
+
+void SbiParser::Resume()
+{
+ sal_uInt32 nLbl;
+
+ switch( Next() )
+ {
+ case EOS:
+ case EOLN:
+ aGen.Gen( SbiOpcode::RESUME_, 0 );
+ break;
+ case NEXT:
+ aGen.Gen( SbiOpcode::RESUME_, 1 );
+ Next();
+ break;
+ case NUMBER:
+ if( !nVal )
+ {
+ aGen.Gen( SbiOpcode::RESUME_, 0 );
+ break;
+ }
+ [[fallthrough]];
+ case SYMBOL:
+ if( MayBeLabel() )
+ {
+ nLbl = pProc->GetLabels().Reference( aSym );
+ aGen.Gen( SbiOpcode::RESUME_, nLbl );
+ Next();
+ break;
+ }
+ [[fallthrough]];
+ default:
+ Error( ERRCODE_BASIC_LABEL_EXPECTED );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/basic/source/comp/parser.cxx b/basic/source/comp/parser.cxx
new file mode 100644
index 0000000000..97bd27675f
--- /dev/null
+++ b/basic/source/comp/parser.cxx
@@ -0,0 +1,898 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <basic/sberrors.hxx>
+#include <basic/sbxmeth.hxx>
+#include <basic/sbmod.hxx>
+#include <basic/sbstar.hxx>
+#include <basic/sbx.hxx>
+#include <parser.hxx>
+#include <com/sun/star/script/ModuleType.hpp>
+#include <rtl/character.hxx>
+
+struct SbiParseStack { // "Stack" for statement-blocks
+ SbiParseStack* pNext; // Chain
+ SbiExprNode* pWithVar;
+ SbiToken eExitTok;
+ sal_uInt32 nChain; // JUMP-Chain
+};
+
+namespace {
+
+struct SbiStatement {
+ SbiToken eTok;
+ void( SbiParser::*Func )();
+ bool bMain; // true: OK outside the SUB
+ bool bSubr; // true: OK inside the SUB
+};
+
+}
+
+#define Y true
+#define N false
+
+const 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, nullptr, N, N }
+};
+
+SbiParser::SbiParser( StarBASIC* pb, SbModule* pm )
+ : SbiTokenizer( pm->GetSource32(), pb ),
+ pStack(nullptr),
+ pProc(nullptr),
+ pWithVar(nullptr),
+ eEndTok(NIL),
+ bGblDefs(false),
+ bNewGblDefs(false),
+ bSingleLineIf(false),
+ bCodeCompleting(false),
+ aGlobals( aGblStrings, SbGLOBAL, this ),
+ aPublics( aGblStrings, SbPUBLIC, this ),
+ aRtlSyms( aGblStrings, SbRTL, this ),
+ aGen( *pm, this ),
+ nBase(0),
+ bExplicit(false)
+{
+ bClassModule = ( pm->GetModuleType() == css::script::ModuleType::CLASS );
+ pPool = &aPublics;
+ for(SbxDataType & eDefType : eDefTypes)
+ eDefType = SbxVARIANT; // no explicit default type
+
+ aPublics.SetParent( &aGlobals );
+ aGlobals.SetParent( &aRtlSyms );
+
+
+ nGblChain = aGen.Gen( SbiOpcode::JUMP_, 0 );
+
+ rTypeArray = new SbxArray; // array for user defined types
+ rEnumArray = new SbxArray; // array for Enum types
+ bVBASupportOn = pm->IsVBASupport();
+ if ( bVBASupportOn )
+ EnableCompatibility();
+
+}
+
+SbiParser::~SbiParser() { }
+
+// part of the runtime-library?
+SbiSymDef* SbiParser::CheckRTLForSym(const OUString& rSym, SbxDataType eType)
+{
+ SbxVariable* pVar = GetBasic()->GetRtl()->Find(rSym, SbxClassType::DontCare);
+ if (!pVar)
+ return nullptr;
+
+ if (SbxMethod* pMethod = dynamic_cast<SbxMethod*>(pVar))
+ {
+ SbiProcDef* pProc_ = aRtlSyms.AddProc( rSym );
+ if (pMethod->IsRuntimeFunction())
+ {
+ pProc_->SetType( pMethod->GetRuntimeFunctionReturnType() );
+ }
+ else
+ {
+ pProc_->SetType( pVar->GetType() );
+ }
+ return pProc_;
+ }
+
+
+ SbiSymDef* pDef = aRtlSyms.AddSym(rSym);
+ pDef->SetType(eType);
+ return pDef;
+}
+
+// close global chain
+
+bool SbiParser::HasGlobalCode()
+{
+ if( bGblDefs && nGblChain )
+ {
+ aGen.BackChain( nGblChain );
+ aGen.Gen( SbiOpcode::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 service the for-loop level
+ if( eTok == FOR )
+ aGen.IncForLevel();
+}
+
+void SbiParser::CloseBlock()
+{
+ if( !pStack )
+ return;
+
+ SbiParseStack* p = pStack;
+
+ // #29955 service the for-loop level
+ 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( SbiOpcode::JUMP_, p->nChain );
+ return;
+ }
+ }
+ if( pStack )
+ Error( ERRCODE_BASIC_EXPECTED, pStack->eExitTok );
+ else
+ Error( ERRCODE_BASIC_BAD_EXIT );
+}
+
+bool SbiParser::TestSymbol()
+{
+ Peek();
+ if( eCurTok == SYMBOL )
+ {
+ Next(); return true;
+ }
+ Error( ERRCODE_BASIC_SYMBOL_EXPECTED );
+ return false;
+}
+
+
+bool SbiParser::TestToken( SbiToken t )
+{
+ if( Peek() == t )
+ {
+ Next(); return true;
+ }
+ else
+ {
+ Error( ERRCODE_BASIC_EXPECTED, t );
+ return false;
+ }
+}
+
+
+bool SbiParser::TestComma()
+{
+ SbiToken eTok = Peek();
+ if( IsEoln( eTok ) )
+ {
+ Next();
+ return false;
+ }
+ else if( eTok != COMMA )
+ {
+ Error( ERRCODE_BASIC_EXPECTED, COMMA );
+ return false;
+ }
+ Next();
+ return true;
+}
+
+
+void SbiParser::TestEoln()
+{
+ if( !IsEoln( Next() ) )
+ {
+ Error( ERRCODE_BASIC_EXPECTED, EOLN );
+ while( !IsEoln( Next() ) ) {}
+ }
+}
+
+
+void SbiParser::StmntBlock( SbiToken eEnd )
+{
+ SbiToken xe = eEndTok;
+ eEndTok = eEnd;
+ while( !bAbort && Parse() ) {}
+ eEndTok = xe;
+ if( IsEof() )
+ {
+ Error( ERRCODE_BASIC_BAD_BLOCK, eEnd );
+ bAbort = true;
+ }
+}
+
+void SbiParser::SetCodeCompleting( bool b )
+{
+ bCodeCompleting = b;
+}
+
+
+bool SbiParser::Parse()
+{
+ if( bAbort ) return false;
+
+ EnableErrors();
+
+ bErrorIsSymbol = false;
+ Peek();
+ bErrorIsSymbol = true;
+
+ if( IsEof() )
+ {
+ // AB #33133: If no sub has been created before,
+ // the global chain must be closed here!
+ // AB #40689: Due to the new static-handling there
+ // can be another nGblChain, so ask for it before.
+ if( bNewGblDefs && nGblChain == 0 )
+ nGblChain = aGen.Gen( SbiOpcode::JUMP_, 0 );
+ return false;
+ }
+
+
+ if( IsEoln( eCurTok ) )
+ {
+ Next(); return true;
+ }
+
+ if( !bSingleLineIf && MayBeLabel( true ) )
+ {
+ // is a label
+ if( !pProc )
+ Error( ERRCODE_BASIC_NOT_IN_MAIN, aSym );
+ else
+ pProc->GetLabels().Define( aSym );
+ Next(); Peek();
+
+ if( IsEoln( eCurTok ) )
+ {
+ Next(); return true;
+ }
+ }
+
+ // end of parsing?
+ if( eCurTok == eEndTok ||
+ ( bVBASupportOn && // #i109075
+ (eCurTok == ENDFUNC || eCurTok == ENDPROPERTY || eCurTok == ENDSUB) &&
+ (eEndTok == ENDFUNC || eEndTok == ENDPROPERTY || eEndTok == ENDSUB) ) )
+ {
+ Next();
+ if( eCurTok != NIL )
+ aGen.Statement();
+ return false;
+ }
+
+ // comment?
+ if( eCurTok == REM )
+ {
+ Next(); return 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( *this );
+ tokens.Next();
+ if ( tokens.Peek() == DOT )
+ {
+ eCurTok = SYMBOL;
+ ePush = eCurTok;
+ }
+ }
+ // if there's a symbol, it's either a variable (LET)
+ // or a SUB-procedure (CALL without brackets)
+ // DOT for assignments in the WITH-block: .A=5
+ if( eCurTok == SYMBOL || eCurTok == DOT )
+ {
+ if( !pProc )
+ Error( ERRCODE_BASIC_EXPECTED, SUB );
+ else
+ {
+ // for correct line and column...
+ Next();
+ Push( eCurTok );
+ aGen.Statement();
+ Symbol(nullptr);
+ }
+ }
+ else
+ {
+ Next();
+
+ // statement parsers
+
+ const SbiStatement* p;
+ for( p = StmntTable; p->eTok != NIL; p++ )
+ if( p->eTok == eCurTok )
+ break;
+ if( p->eTok != NIL )
+ {
+ if( !pProc && !p->bMain )
+ Error( ERRCODE_BASIC_NOT_IN_MAIN, eCurTok );
+ else if( pProc && !p->bSubr )
+ Error( ERRCODE_BASIC_NOT_IN_SUBR, eCurTok );
+ else
+ {
+ // AB #41606/#40689: Due to the new static-handling there
+ // can be another nGblChain, so ask for it before.
+ if( bNewGblDefs && nGblChain == 0 &&
+ ( eCurTok == SUB || eCurTok == FUNCTION || eCurTok == PROPERTY ) )
+ {
+ nGblChain = aGen.Gen( SbiOpcode::JUMP_, 0 );
+ bNewGblDefs = false;
+ }
+ // statement-opcode at the beginning of a sub, too, please
+ if( ( p->bSubr && (eCurTok != STATIC || Peek() == SUB || Peek() == FUNCTION ) ) ||
+ eCurTok == SUB || eCurTok == FUNCTION )
+ aGen.Statement();
+ (this->*( p->Func ) )();
+ ErrCode nSbxErr = SbxBase::GetError();
+ if( nSbxErr )
+ {
+ SbxBase::ResetError();
+ Error( nSbxErr );
+ }
+ }
+ }
+ else
+ Error( ERRCODE_BASIC_UNEXPECTED, eCurTok );
+ }
+
+ // test for the statement's end -
+ // might also be an ELSE, as there must not necessary be a : before the ELSE!
+
+ if( !IsEos() )
+ {
+ Peek();
+ if( !IsEos() && eCurTok != ELSE )
+ {
+ // if the parsing has been aborted, jump over to the ":"
+ Error( ERRCODE_BASIC_UNEXPECTED, eCurTok );
+ while( !IsEos() ) Next();
+ }
+ }
+ // The parser aborts at the end, the
+ // next token has not been fetched yet!
+ return true;
+}
+
+
+SbiExprNode* SbiParser::GetWithVar()
+{
+ if( pWithVar )
+ return pWithVar;
+
+ SbiParseStack* p = pStack;
+ while( p )
+ {
+ // LoopVar can at the moment only be for with
+ if( p->pWithVar )
+ return p->pWithVar;
+ p = p->pNext;
+ }
+ return nullptr;
+}
+
+
+// assignment or 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( ERRCODE_BASIC_EXPECTED, "=" );
+
+ RecursiveMode eRecMode = ( bEQ ? PREVENT_CALL : FORCE_CALL );
+ bool bSpecialMidHandling = false;
+ SbiSymDef* pDef = aVar.GetRealVar();
+ if( bEQ && pDef && pDef->GetScope() == SbRTL )
+ {
+ OUString aRtlName = pDef->GetName();
+ if( aRtlName.equalsIgnoreAsciiCase("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( std::make_unique<SbiExpression>( this, -1, SbxLONG ) );
+
+ TestToken( EQ );
+ pPar->addExpression( std::make_unique<SbiExpression>( this ) );
+
+ bSpecialMidHandling = true;
+ }
+ }
+ }
+ }
+ aVar.Gen( eRecMode );
+ if( bSpecialMidHandling )
+ return;
+
+ if( !bEQ )
+ {
+ aGen.Gen( SbiOpcode::GET_ );
+ }
+ else
+ {
+ // so it must be an assignment!
+ if( !aVar.IsLvalue() )
+ Error( ERRCODE_BASIC_LVALUE_EXPECTED );
+ TestToken( EQ );
+ SbiExpression aExpr( this );
+ aExpr.Gen();
+ SbiOpcode eOp = SbiOpcode::PUT_;
+ if( pDef )
+ {
+ if( pDef->GetConstDef() )
+ Error( ERRCODE_BASIC_DUPLICATE_DEF, pDef->GetName() );
+ if( pDef->GetType() == SbxOBJECT )
+ {
+ eOp = SbiOpcode::SET_;
+ if( pDef->GetTypeId() )
+ {
+ aGen.Gen( SbiOpcode::SETCLASS_, pDef->GetTypeId() );
+ return;
+ }
+ }
+ }
+ aGen.Gen( eOp );
+ }
+}
+
+
+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( ERRCODE_BASIC_DUPLICATE_DEF, pDef->GetName() );
+ nLen = aLvalue.GetRealVar()->GetLen();
+ }
+ if( nLen )
+ aGen.Gen( SbiOpcode::PAD_, nLen );
+ aGen.Gen( SbiOpcode::PUT_ );
+}
+
+// assignments of an object-variable
+
+void SbiParser::Set()
+{
+ SbiExpression aLvalue( this, SbLVALUE );
+ SbxDataType eType = aLvalue.GetType();
+ if( eType != SbxOBJECT && eType != SbxEMPTY && eType != SbxVARIANT )
+ Error( ERRCODE_BASIC_INVALID_OBJECT );
+ TestToken( EQ );
+ SbiSymDef* pDef = aLvalue.GetRealVar();
+ if( pDef->GetConstDef() )
+ Error( ERRCODE_BASIC_DUPLICATE_DEF, pDef->GetName() );
+
+ SbiToken eTok = Peek();
+ if( eTok == NEW )
+ {
+ Next();
+ auto pTypeDef = std::make_unique<SbiSymDef>( OUString() );
+ TypeDecl( *pTypeDef, true );
+
+ aLvalue.Gen();
+ aGen.Gen( SbiOpcode::CREATE_, pDef->GetId(), pTypeDef->GetTypeId() );
+ aGen.Gen( SbiOpcode::SETCLASS_, pDef->GetTypeId() );
+ }
+ else
+ {
+ SbiExpression aExpr( this );
+ aLvalue.Gen();
+ aExpr.Gen();
+ // It's a good idea to distinguish between
+ // set something = another &
+ // something = another
+ // ( it's necessary for vba objects where set is object
+ // specific and also doesn't involve processing default params )
+ if( pDef->GetTypeId() )
+ {
+ if ( bVBASupportOn )
+ aGen.Gen( SbiOpcode::VBASETCLASS_, pDef->GetTypeId() );
+ else
+ aGen.Gen( SbiOpcode::SETCLASS_, pDef->GetTypeId() );
+ }
+ else
+ {
+ if ( bVBASupportOn )
+ aGen.Gen( SbiOpcode::VBASET_ );
+ else
+ aGen.Gen( SbiOpcode::SET_ );
+ }
+ }
+}
+
+// JSM 07.10.95
+void SbiParser::LSet()
+{
+ SbiExpression aLvalue( this, SbLVALUE );
+ if( aLvalue.GetType() != SbxSTRING )
+ {
+ Error( ERRCODE_BASIC_INVALID_OBJECT );
+ }
+ TestToken( EQ );
+ SbiSymDef* pDef = aLvalue.GetRealVar();
+ if( pDef && pDef->GetConstDef() )
+ {
+ Error( ERRCODE_BASIC_DUPLICATE_DEF, pDef->GetName() );
+ }
+ SbiExpression aExpr( this );
+ aLvalue.Gen();
+ aExpr.Gen();
+ aGen.Gen( SbiOpcode::LSET_ );
+}
+
+// JSM 07.10.95
+void SbiParser::RSet()
+{
+ SbiExpression aLvalue( this, SbLVALUE );
+ if( aLvalue.GetType() != SbxSTRING )
+ {
+ Error( ERRCODE_BASIC_INVALID_OBJECT );
+ }
+ TestToken( EQ );
+ SbiSymDef* pDef = aLvalue.GetRealVar();
+ if( pDef && pDef->GetConstDef() )
+ Error( ERRCODE_BASIC_DUPLICATE_DEF, pDef->GetName() );
+ SbiExpression aExpr( this );
+ aLvalue.Gen();
+ aExpr.Gen();
+ aGen.Gen( SbiOpcode::RSET_ );
+}
+
+// DEFINT, DEFLNG, DEFSNG, DEFDBL, DEFSTR and so on
+
+void SbiParser::DefXXX()
+{
+ sal_Unicode ch1, ch2;
+ SbxDataType t = SbxDataType( eCurTok - DEFINT + SbxINTEGER );
+
+ while( !bAbort )
+ {
+ if( Next() != SYMBOL ) break;
+ ch1 = rtl::toAsciiUpperCase(aSym[0]);
+ ch2 = 0;
+ if( Peek() == MINUS )
+ {
+ Next();
+ if( Next() != SYMBOL ) Error( ERRCODE_BASIC_SYMBOL_EXPECTED );
+ else
+ {
+ ch2 = rtl::toAsciiUpperCase(aSym[0]);
+ if( ch2 < ch1 )
+ {
+ Error( ERRCODE_BASIC_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( SbiOpcode::STOP_ );
+ Peek(); // #35694: only Peek(), so that EOL is recognized in Single-Line-If
+}
+
+// IMPLEMENTS
+
+void SbiParser::Implements()
+{
+ if( !bClassModule )
+ {
+ Error( ERRCODE_BASIC_UNEXPECTED, IMPLEMENTS );
+ return;
+ }
+
+ Peek();
+ if( eCurTok != SYMBOL )
+ {
+ Error( ERRCODE_BASIC_SYMBOL_EXPECTED );
+ return;
+ }
+
+ OUString aImplementedIface = aSym;
+ Next();
+ if( Peek() == DOT )
+ {
+ OUString aDotStr( '.' );
+ while( Peek() == DOT )
+ {
+ aImplementedIface += aDotStr;
+ Next();
+ SbiToken ePeekTok = Peek();
+ if( ePeekTok == SYMBOL || IsKwd( ePeekTok ) )
+ {
+ Next();
+ aImplementedIface += aSym;
+ }
+ else
+ {
+ Next();
+ Error( ERRCODE_BASIC_SYMBOL_EXPECTED );
+ break;
+ }
+ }
+ }
+ aIfaceVector.push_back( aImplementedIface );
+}
+
+void SbiParser::EnableCompatibility()
+{
+ if( !bCompatible )
+ AddConstants();
+ bCompatible = true;
+}
+
+// OPTION
+
+void SbiParser::Option()
+{
+ switch( Next() )
+ {
+ case BASIC_EXPLICIT:
+ bExplicit = true; break;
+ case BASE:
+ if( Next() == NUMBER && ( nVal == 0 || nVal == 1 ) )
+ {
+ nBase = static_cast<short>(nVal);
+ break;
+ }
+ Error( ERRCODE_BASIC_EXPECTED, "0/1" );
+ break;
+ case PRIVATE:
+ {
+ OUString aString = SbiTokenizer::Symbol(Next());
+ if( !aString.equalsIgnoreAsciiCase("Module") )
+ {
+ Error( ERRCODE_BASIC_EXPECTED, "Module" );
+ }
+ break;
+ }
+ case COMPARE:
+ {
+ SbiToken eTok = Next();
+ if( eTok == BINARY )
+ {
+ }
+ else if( eTok == SYMBOL && GetSym().equalsIgnoreAsciiCase("text") )
+ {
+ }
+ else
+ {
+ Error( ERRCODE_BASIC_EXPECTED, "Text/Binary" );
+ }
+ break;
+ }
+ case COMPATIBLE:
+ EnableCompatibility();
+ break;
+
+ case CLASSMODULE:
+ bClassModule = true;
+ aGen.GetModule().SetModuleType( css::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().IsVBASupport() )
+ {
+ aGen.GetModule().SetVBASupport( bVBASupportOn );
+ }
+ break;
+ }
+ }
+ Error( ERRCODE_BASIC_EXPECTED, "0/1" );
+ break;
+ default:
+ Error( ERRCODE_BASIC_BAD_OPTION, eCurTok );
+ }
+}
+
+static void addStringConst( SbiSymPool& rPool, const OUString& pSym, const OUString& rStr )
+{
+ SbiConstDef* pConst = new SbiConstDef( pSym );
+ pConst->SetType( SbxSTRING );
+ pConst->Set( rStr );
+ rPool.Add( pConst );
+}
+
+static void addNumericConst(SbiSymPool& rPool, const OUString& pSym, double nVal)
+{
+ SbiConstDef* pConst = new SbiConstDef(pSym);
+ pConst->Set(nVal, SbxDOUBLE);
+ rPool.Add(pConst);
+}
+
+void SbiParser::AddConstants()
+{
+ // tdf#153543 - shell constants
+ // See https://learn.microsoft.com/en-us/office/vba/language/reference/user-interface-help/shell-constants
+ addNumericConst(aPublics, "vbHide", 0);
+ addNumericConst(aPublics, "vbNormalFocus", 1);
+ addNumericConst(aPublics, "vbMinimizedFocus", 2);
+ addNumericConst(aPublics, "vbMaximizedFocus", 3);
+ addNumericConst(aPublics, "vbNormalNoFocus", 4);
+ addNumericConst(aPublics, "vbMinimizedNoFocus", 6);
+
+ // tdf#131563 - add vba color constants
+ // See https://docs.microsoft.com/en-us/office/vba/language/reference/user-interface-help/color-constants
+ addNumericConst(aPublics, "vbBlack", 0x0);
+ addNumericConst(aPublics, "vbRed", 0xFF);
+ addNumericConst(aPublics, "vbGreen", 0xFF00);
+ addNumericConst(aPublics, "vbYellow", 0xFFFF);
+ addNumericConst(aPublics, "vbBlue", 0xFF0000);
+ addNumericConst(aPublics, "vbMagenta", 0xFF00FF);
+ addNumericConst(aPublics, "vbCyan", 0xFFFF00);
+ addNumericConst(aPublics, "vbWhite", 0xFFFFFF);
+
+ // #113063 Create constant RTL symbols
+ addStringConst( aPublics, "vbCr", "\x0D" );
+ addStringConst( aPublics, "vbCrLf", "\x0D\x0A" );
+ addStringConst( aPublics, "vbFormFeed", "\x0C" );
+ addStringConst( aPublics, "vbLf", "\x0A" );
+#ifdef _WIN32
+ addStringConst( aPublics, "vbNewLine", "\x0D\x0A" );
+#else
+ addStringConst( aPublics, "vbNewLine", "\x0A" );
+#endif
+ addStringConst( aPublics, "vbNullString", "" );
+ addStringConst( aPublics, "vbTab", "\x09" );
+ addStringConst( aPublics, "vbVerticalTab", "\x0B" );
+
+ addStringConst( aPublics, "vbNullChar", OUString(u'\0') );
+}
+
+// ERROR n
+
+void SbiParser::ErrorStmnt()
+{
+ SbiExpression aPar( this );
+ aPar.Gen();
+ aGen.Gen( SbiOpcode::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 0000000000..1a6a52b228
--- /dev/null
+++ b/basic/source/comp/sbcomp.cxx
@@ -0,0 +1,85 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <basic/sbmeth.hxx>
+#include <basic/sbstar.hxx>
+#include <basic/sbx.hxx>
+#include <parser.hxx>
+#include <image.hxx>
+#include <sbintern.hxx>
+#include <sbobjmod.hxx>
+#include <memory>
+
+// This routine is defined here, so that the
+// compiler can be loaded as a discrete segment.
+
+bool SbModule::Compile()
+{
+ if( pImage )
+ return true;
+ StarBASIC* pBasic = dynamic_cast<StarBASIC*>( GetParent() );
+ if( !pBasic )
+ return false;
+ SbxBase::ResetError();
+
+ SbModule* pOld = GetSbData()->pCompMod;
+ GetSbData()->pCompMod = this;
+
+ auto pParser = std::make_unique<SbiParser>( pBasic, this );
+ while( pParser->Parse() ) {}
+ if( !pParser->GetErrors() )
+ pParser->aGen.Save();
+ pParser.reset();
+ // for the disassembler
+ if( pImage )
+ pImage->aOUSource = aOUSource;
+
+ GetSbData()->pCompMod = pOld;
+
+ // compiling a module, the module-global
+ // variables of all modules become invalid
+ bool bRet = IsCompiled();
+ if( bRet )
+ {
+ if( dynamic_cast<const SbObjModule*>( this) == nullptr )
+ pBasic->ClearAllModuleVars();
+ RemoveVars(); // remove 'this' Modules variables
+ // clear all method statics
+ for (sal_uInt32 i = 0; i < pMethods->Count(); i++)
+ {
+ SbMethod* p = dynamic_cast<SbMethod*>(pMethods->Get(i));
+ if( p )
+ p->ClearStatics();
+ }
+
+ // #i31510 Init other libs only if Basic isn't running
+ if( GetSbData()->pInst == nullptr )
+ {
+ SbxObject* pParent_ = pBasic->GetParent();
+ if( pParent_ )
+ pBasic = dynamic_cast<StarBASIC*>( pParent_ );
+ if( pBasic )
+ pBasic->ClearAllModuleVars();
+ }
+ }
+
+ return bRet;
+}
+/* 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 0000000000..45b65a29b1
--- /dev/null
+++ b/basic/source/comp/scanner.cxx
@@ -0,0 +1,717 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <basiccharclass.hxx>
+#include <scanner.hxx>
+#include <sbintern.hxx>
+#include <runtime.hxx>
+
+#include <basic/sberrors.hxx>
+#include <i18nlangtag/lang.h>
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <rtl/character.hxx>
+#include <o3tl/string_view.hxx>
+#include <utility>
+
+SbiScanner::SbiScanner(OUString _aBuf, StarBASIC* p)
+ : aBuf(std::move(_aBuf))
+ , nLineIdx(-1)
+ , nSaveLineIdx(-1)
+ , pBasic(p)
+ , eScanType(SbxVARIANT)
+ , nVal(0)
+ , nSavedCol1(0)
+ , nCol(0)
+ , nErrors(0)
+ , nColLock(0)
+ , nBufPos(0)
+ , nLine(0)
+ , nCol1(0)
+ , nCol2(0)
+ , bSymbol(false)
+ , bNumber(false)
+ , bSpaces(false)
+ , bAbort(false)
+ , bHash(true)
+ , bError(false)
+ , bCompatible(false)
+ , bVBASupportOn(false)
+ , bPrevLineExtentsComment(false)
+ , bClosingUnderscore(false)
+ , bLineEndsWithWhitespace(false)
+ , bInStatement(false)
+{
+}
+
+void SbiScanner::LockColumn()
+{
+ if( !nColLock++ )
+ nSavedCol1 = nCol1;
+}
+
+void SbiScanner::UnlockColumn()
+{
+ if( nColLock )
+ nColLock--;
+}
+
+void SbiScanner::GenError( ErrCode code )
+{
+ if( GetSbData()->bBlockCompilerError )
+ {
+ bAbort = true;
+ return;
+ }
+ if( !bError )
+ {
+ bool bRes = true;
+ // report only one error per statement
+ bError = true;
+ if( pBasic )
+ {
+ // in case of EXPECTED or UNEXPECTED it always refers
+ // to the last token, so take the Col1 over
+ sal_Int32 nc = nColLock ? nSavedCol1 : nCol1;
+ if ( code.anyOf(
+ ERRCODE_BASIC_EXPECTED,
+ ERRCODE_BASIC_UNEXPECTED,
+ ERRCODE_BASIC_SYMBOL_EXPECTED,
+ ERRCODE_BASIC_LABEL_EXPECTED) )
+ {
+ nc = nCol1;
+ if( nc > nCol2 ) nCol2 = nc;
+ }
+ bRes = pBasic->CError( code, aError, nLine, nc, nCol2 );
+ }
+ bAbort = bAbort || !bRes || ( code == ERRCODE_BASIC_NO_MEMORY || code == ERRCODE_BASIC_PROG_TOO_LARGE );
+ }
+ nErrors++;
+}
+
+
+// used by SbiTokenizer::MayBeLabel() to detect a label
+bool SbiScanner::DoesColonFollow()
+{
+ if(nCol < aLine.getLength() && aLine[nCol] == ':')
+ {
+ ++nLineIdx; ++nCol;
+ return true;
+ }
+ else
+ return false;
+}
+
+// test for legal suffix
+static SbxDataType GetSuffixType( sal_Unicode c )
+{
+ switch (c)
+ {
+ case '%':
+ return SbxINTEGER;
+ case '&':
+ return SbxLONG;
+ case '!':
+ return SbxSINGLE;
+ case '#':
+ return SbxDOUBLE;
+ case '@':
+ return SbxCURRENCY;
+ case '$':
+ return SbxSTRING;
+ default:
+ return SbxVARIANT;
+ }
+}
+
+// reading the next symbol into the variables aSym, nVal and eType
+// return value is sal_False at EOF or errors
+#define BUF_SIZE 80
+
+void SbiScanner::scanAlphanumeric()
+{
+ sal_Int32 n = nCol;
+ while(nCol < aLine.getLength() && (BasicCharClass::isAlphaNumeric(aLine[nCol], bCompatible) || aLine[nCol] == '_'))
+ {
+ ++nLineIdx;
+ ++nCol;
+ }
+ aSym = aLine.copy(n, nCol - n);
+}
+
+void SbiScanner::scanGoto()
+{
+ sal_Int32 n = nCol;
+ while(n < aLine.getLength() && BasicCharClass::isWhitespace(aLine[n]))
+ ++n;
+
+ if(n + 1 < aLine.getLength())
+ {
+ std::u16string_view aTemp = aLine.subView(n, 2);
+ if(o3tl::equalsIgnoreAsciiCase(aTemp, u"to"))
+ {
+ aSym = "goto";
+ nLineIdx += n + 2 - nCol;
+ nCol = n + 2;
+ }
+ }
+}
+
+bool SbiScanner::readLine()
+{
+ if(nBufPos >= aBuf.getLength())
+ return false;
+
+ sal_Int32 n = nBufPos;
+ sal_Int32 nLen = aBuf.getLength();
+
+ while(n < nLen && aBuf[n] != '\r' && aBuf[n] != '\n')
+ ++n;
+
+ // Trim trailing whitespace
+ sal_Int32 nEnd = n;
+ while(nBufPos < nEnd && BasicCharClass::isWhitespace(aBuf[nEnd - 1]))
+ --nEnd;
+
+ // tdf#149402 - check if line ends with a whitespace
+ bLineEndsWithWhitespace = (n > nEnd);
+ aLine = aBuf.copy(nBufPos, nEnd - nBufPos);
+
+ // Fast-forward past the line ending
+ if(n + 1 < nLen && aBuf[n] == '\r' && aBuf[n + 1] == '\n')
+ n += 2;
+ else if(n < nLen)
+ ++n;
+
+ nBufPos = n;
+ nLineIdx = 0;
+
+ ++nLine;
+ nCol = nCol1 = nCol2 = 0;
+ nColLock = 0;
+
+ return true;
+}
+
+bool SbiScanner::NextSym()
+{
+ // memorize for the EOLN-case
+ sal_Int32 nOldLine = nLine;
+ sal_Int32 nOldCol1 = nCol1;
+ sal_Int32 nOldCol2 = nCol2;
+ sal_Unicode buf[ BUF_SIZE ], *p = buf;
+
+ eScanType = SbxVARIANT;
+ aSym.clear();
+ bHash = bSymbol = bNumber = bSpaces = false;
+ bool bCompilerDirective = false;
+
+ // read in line?
+ if (nLineIdx == -1)
+ {
+ if(!readLine())
+ return false;
+
+ nOldLine = nLine;
+ nOldCol1 = nOldCol2 = 0;
+ }
+
+ const sal_Int32 nLineIdxScanStart = nLineIdx;
+
+ if(nCol < aLine.getLength() && BasicCharClass::isWhitespace(aLine[nCol]))
+ {
+ bSpaces = true;
+ while(nCol < aLine.getLength() && BasicCharClass::isWhitespace(aLine[nCol]))
+ {
+ ++nLineIdx;
+ ++nCol;
+ }
+ }
+
+ nCol1 = nCol;
+
+ // only blank line?
+ if(nCol >= aLine.getLength())
+ goto eoln;
+
+ if( bPrevLineExtentsComment )
+ goto PrevLineCommentLbl;
+
+ if(nCol < aLine.getLength() && aLine[nCol] == '#')
+ {
+ sal_Int32 nLineTempIdx = nLineIdx;
+ do
+ {
+ nLineTempIdx++;
+ } while (nLineTempIdx < aLine.getLength() && !BasicCharClass::isWhitespace(aLine[nLineTempIdx])
+ && aLine[nLineTempIdx] != '#' && aLine[nLineTempIdx] != ',');
+ // leave it if it is a date literal - it will be handled later
+ if (nLineTempIdx >= aLine.getLength() || aLine[nLineTempIdx] != '#')
+ {
+ ++nLineIdx;
+ ++nCol;
+ //ignore compiler directives (# is first non-space character)
+ if (nOldCol2 == 0)
+ bCompilerDirective = true;
+ else
+ bHash = true;
+ }
+ }
+
+ // copy character if symbol
+ if(nCol < aLine.getLength() && (BasicCharClass::isAlpha(aLine[nCol], bCompatible) || aLine[nCol] == '_'))
+ {
+ // if there's nothing behind '_' , it's the end of a line!
+ if(nCol + 1 == aLine.getLength() && aLine[nCol] == '_')
+ {
+ // Note that nCol is not incremented here...
+ ++nLineIdx;
+ goto eoln;
+ }
+
+ bSymbol = true;
+
+ scanAlphanumeric();
+
+ // Special handling for "go to"
+ if(nCol < aLine.getLength() && bCompatible && aSym.equalsIgnoreAsciiCase("go"))
+ scanGoto();
+
+ // tdf#125637 - check for closing underscore
+ if (nCol == aLine.getLength() && aLine[nCol - 1] == '_')
+ {
+ bClosingUnderscore = true;
+ }
+ // type recognition?
+ // don't test the exclamation mark
+ // if there's a symbol behind it
+ else if((nCol >= aLine.getLength() || aLine[nCol] != '!') ||
+ (nCol + 1 >= aLine.getLength() || !BasicCharClass::isAlpha(aLine[nCol + 1], bCompatible)))
+ {
+ if(nCol < aLine.getLength())
+ {
+ SbxDataType t(GetSuffixType(aLine[nCol]));
+ if( t != SbxVARIANT )
+ {
+ eScanType = t;
+ ++nLineIdx;
+ ++nCol;
+ }
+ }
+ }
+ }
+
+ // read in and convert if number
+ else if((nCol < aLine.getLength() && rtl::isAsciiDigit(aLine[nCol])) ||
+ (nCol + 1 < aLine.getLength() && aLine[nCol] == '.' && rtl::isAsciiDigit(aLine[nCol + 1])))
+ {
+ short exp = 0;
+ short dec = 0;
+ eScanType = SbxDOUBLE;
+ bool bScanError = false;
+ bool bBufOverflow = false;
+ // All this because of 'D' or 'd' floating point type, sigh...
+ while(!bScanError && nCol < aLine.getLength() && strchr("0123456789.DEde", aLine[nCol]))
+ {
+ // from 4.1.1996: buffer full? -> go on scanning empty
+ if( (p-buf) == (BUF_SIZE-1) )
+ {
+ bBufOverflow = true;
+ ++nLineIdx;
+ ++nCol;
+ continue;
+ }
+ // point or exponent?
+ if(aLine[nCol] == '.')
+ {
+ if( ++dec > 1 )
+ bScanError = true;
+ else
+ *p++ = '.';
+ }
+ else if(strchr("DdEe", aLine[nCol]))
+ {
+ if (++exp > 1)
+ bScanError = true;
+ else
+ {
+ *p++ = 'E';
+ if (nCol + 1 < aLine.getLength() && (aLine[nCol+1] == '+' || aLine[nCol+1] == '-'))
+ {
+ ++nLineIdx;
+ ++nCol;
+ if( (p-buf) == (BUF_SIZE-1) )
+ {
+ bBufOverflow = true;
+ continue;
+ }
+ *p++ = aLine[nCol];
+ }
+ }
+ }
+ else
+ {
+ *p++ = aLine[nCol];
+ }
+ ++nLineIdx;
+ ++nCol;
+ }
+ *p = 0;
+ aSym = p; bNumber = true;
+
+ // For bad characters, scan and parse errors generate only one error.
+ ErrCode nError = ERRCODE_NONE;
+ if (bScanError)
+ {
+ --nLineIdx;
+ --nCol;
+ aError = OUString( aLine[nCol]);
+ nError = ERRCODE_BASIC_BAD_CHAR_IN_NUMBER;
+ }
+
+ rtl_math_ConversionStatus eStatus = rtl_math_ConversionStatus_Ok;
+ const sal_Unicode* pParseEnd = buf;
+ nVal = rtl_math_uStringToDouble( buf, buf+(p-buf), '.', ',', &eStatus, &pParseEnd );
+ if (pParseEnd != buf+(p-buf))
+ {
+ // e.g. "12e" or "12e+", or with bScanError "12d"+"E".
+ sal_Int32 nChars = buf+(p-buf) - pParseEnd;
+ nLineIdx -= nChars;
+ nCol -= nChars;
+ // For bScanError, nLineIdx and nCol were already decremented, just
+ // add that character to the parse end.
+ if (bScanError)
+ ++nChars;
+ // Copy error position from original string, not the buffer
+ // replacement where "12dE" => "12EE".
+ aError = aLine.copy( nCol, nChars);
+ nError = ERRCODE_BASIC_BAD_CHAR_IN_NUMBER;
+ }
+ else if (eStatus != rtl_math_ConversionStatus_Ok)
+ {
+ // Keep the scan error and character at position, if any.
+ if (!nError)
+ nError = ERRCODE_BASIC_MATH_OVERFLOW;
+ }
+
+ if (nError)
+ GenError( nError );
+
+ if( !dec && !exp )
+ {
+ if( nVal >= SbxMININT && nVal <= SbxMAXINT )
+ eScanType = SbxINTEGER;
+ else if( nVal >= SbxMINLNG && nVal <= SbxMAXLNG )
+ eScanType = SbxLONG;
+ }
+
+ if( bBufOverflow )
+ GenError( ERRCODE_BASIC_MATH_OVERFLOW );
+
+ // type recognition?
+ if( nCol < aLine.getLength() )
+ {
+ SbxDataType t(GetSuffixType(aLine[nCol]));
+ if( t != SbxVARIANT )
+ {
+ eScanType = t;
+ ++nLineIdx;
+ ++nCol;
+ }
+ // tdf#130476 - don't allow String trailing data type character with numbers
+ if ( t == SbxSTRING )
+ {
+ GenError( ERRCODE_BASIC_SYNTAX );
+ }
+ }
+ }
+
+ // Hex/octal number? Read in and convert:
+ else if(aLine.getLength() - nCol > 1 && aLine[nCol] == '&')
+ {
+ ++nLineIdx; ++nCol;
+ sal_Unicode base = 16;
+ sal_Unicode xch = aLine[nCol];
+ ++nLineIdx; ++nCol;
+ switch( rtl::toAsciiUpperCase( xch ) )
+ {
+ case 'O':
+ base = 8;
+ break;
+ case 'H':
+ break;
+ default :
+ // treated as an operator
+ --nLineIdx; --nCol; nCol1 = nCol-1;
+ aSym = "&";
+ return true;
+ }
+ bNumber = true;
+ // Hex literals are signed Integers ( as defined by basic
+ // e.g. -2,147,483,648 through 2,147,483,647 (signed)
+ sal_uInt64 lu = 0;
+ bool bOverflow = false;
+ while(nCol < aLine.getLength() && BasicCharClass::isAlphaNumeric(aLine[nCol], false))
+ {
+ sal_Unicode ch = rtl::toAsciiUpperCase(aLine[nCol]);
+ ++nLineIdx; ++nCol;
+ if( ((base == 16 ) && rtl::isAsciiHexDigit( ch ) ) ||
+ ((base == 8) && rtl::isAsciiOctalDigit( ch )))
+ {
+ int i = ch - '0';
+ if( i > 9 ) i -= 7;
+ lu = ( lu * base ) + i;
+ if( lu > SAL_MAX_UINT32 )
+ {
+ bOverflow = true;
+ }
+ }
+ else
+ {
+ aError = OUString(ch);
+ GenError( ERRCODE_BASIC_BAD_CHAR_IN_NUMBER );
+ }
+ }
+
+ // tdf#130476 - take into account trailing data type characters
+ if( nCol < aLine.getLength() )
+ {
+ SbxDataType t(GetSuffixType(aLine[nCol]));
+ if( t != SbxVARIANT )
+ {
+ eScanType = t;
+ ++nLineIdx;
+ ++nCol;
+ }
+ // tdf#130476 - don't allow String trailing data type character with numbers
+ if ( t == SbxSTRING )
+ {
+ GenError( ERRCODE_BASIC_SYNTAX );
+ }
+ }
+
+ // tdf#130476 - take into account trailing data type characters
+ switch ( eScanType )
+ {
+ case SbxINTEGER:
+ nVal = static_cast<double>( static_cast<sal_Int16>(lu) );
+ if ( lu > SbxMAXUINT )
+ {
+ bOverflow = true;
+ }
+ break;
+ case SbxLONG: nVal = static_cast<double>( static_cast<sal_Int32>(lu) ); break;
+ case SbxVARIANT:
+ {
+ // tdf#62326 - If the value of the hex string without explicit type character lies within
+ // the range of 0x8000 (SbxMAXINT + 1) and 0xFFFF (SbxMAXUINT) inclusive, cast the value
+ // to 16 bit in order to get signed integers, e.g., SbxMININT through SbxMAXINT
+ sal_Int32 ls = (lu > SbxMAXINT && lu <= SbxMAXUINT) ? static_cast<sal_Int16>(lu) : static_cast<sal_Int32>(lu);
+ eScanType = ( ls >= SbxMININT && ls <= SbxMAXINT ) ? SbxINTEGER : SbxLONG;
+ nVal = static_cast<double>(ls);
+ break;
+ }
+ default:
+ nVal = static_cast<double>(lu);
+ break;
+ }
+ if( bOverflow )
+ GenError( ERRCODE_BASIC_MATH_OVERFLOW );
+ }
+
+ // Strings:
+ else if (nLineIdx < aLine.getLength() && (aLine[nLineIdx] == '"' || aLine[nLineIdx] == '['))
+ {
+ sal_Unicode cSep = aLine[nLineIdx];
+ if( cSep == '[' )
+ {
+ bSymbol = true;
+ cSep = ']';
+ }
+ sal_Int32 n = nCol + 1;
+ while (nLineIdx < aLine.getLength())
+ {
+ do
+ {
+ nLineIdx++;
+ nCol++;
+ }
+ while (nLineIdx < aLine.getLength() && (aLine[nLineIdx] != cSep));
+ if (nLineIdx < aLine.getLength() && aLine[nLineIdx] == cSep)
+ {
+ nLineIdx++; nCol++;
+ if (nLineIdx >= aLine.getLength() || aLine[nLineIdx] != cSep || cSep == ']')
+ {
+ // If VBA Interop then doesn't eat the [] chars
+ if ( cSep == ']' && bVBASupportOn )
+ aSym = aLine.copy( n - 1, nCol - n + 1);
+ else
+ aSym = aLine.copy( n, nCol - n - 1 );
+ // get out duplicate string delimiters
+ OUStringBuffer aSymBuf(aSym.getLength());
+ for ( sal_Int32 i = 0, len = aSym.getLength(); i < len; ++i )
+ {
+ aSymBuf.append( aSym[i] );
+ if ( aSym[i] == cSep && ( i+1 < len ) && aSym[i+1] == cSep )
+ ++i;
+ }
+ aSym = aSymBuf.makeStringAndClear();
+ if( cSep != ']' )
+ eScanType = SbxSTRING;
+ break;
+ }
+ }
+ else
+ {
+ aError = OUString(cSep);
+ GenError( ERRCODE_BASIC_EXPECTED );
+ }
+ }
+ }
+
+ // Date:
+ else if (nLineIdx < aLine.getLength() && aLine[nLineIdx] == '#')
+ {
+ sal_Int32 n = nCol + 1;
+ do
+ {
+ nLineIdx++;
+ nCol++;
+ }
+ while (nLineIdx < aLine.getLength() && (aLine[nLineIdx] != '#'));
+ if (nLineIdx < aLine.getLength() && aLine[nLineIdx] == '#')
+ {
+ nLineIdx++; nCol++;
+ aSym = aLine.copy( n, nCol - n - 1 );
+
+ // parse date literal
+ std::shared_ptr<SvNumberFormatter> pFormatter;
+ if (GetSbData()->pInst)
+ {
+ pFormatter = GetSbData()->pInst->GetNumberFormatter();
+ }
+ else
+ {
+ sal_uInt32 nDummy;
+ pFormatter = SbiInstance::PrepareNumberFormatter( nDummy, nDummy, nDummy );
+ }
+ sal_uInt32 nIndex = pFormatter->GetStandardIndex( LANGUAGE_ENGLISH_US);
+ bool bSuccess = pFormatter->IsNumberFormat(aSym, nIndex, nVal);
+ if( bSuccess )
+ {
+ SvNumFormatType nType_ = pFormatter->GetType(nIndex);
+ if( !(nType_ & SvNumFormatType::DATE) )
+ bSuccess = false;
+ }
+
+ if (!bSuccess)
+ GenError( ERRCODE_BASIC_CONVERSION );
+
+ bNumber = true;
+ eScanType = SbxDOUBLE;
+ }
+ else
+ {
+ aError = OUString('#');
+ GenError( ERRCODE_BASIC_EXPECTED );
+ }
+ }
+ // invalid characters:
+ else if (nLineIdx < aLine.getLength() && aLine[nLineIdx] >= 0x7F)
+ {
+ GenError( ERRCODE_BASIC_SYNTAX ); nLineIdx++; nCol++;
+ }
+ // other groups:
+ else
+ {
+ sal_Int32 n = 1;
+ auto nChar = nLineIdx < aLine.getLength() ? aLine[nLineIdx] : 0;
+ ++nLineIdx;
+ if (nLineIdx < aLine.getLength())
+ {
+ switch (nChar)
+ {
+ case '<': if( aLine[nLineIdx] == '>' || aLine[nLineIdx] == '=' ) n = 2; break;
+ case '>': if( aLine[nLineIdx] == '=' ) n = 2; break;
+ case ':': if( aLine[nLineIdx] == '=' ) n = 2; break;
+ }
+ }
+ aSym = aLine.copy(nCol, std::min(n, aLine.getLength() - nCol));
+ nLineIdx += n-1; nCol = nCol + n;
+ }
+
+ nCol2 = nCol-1;
+
+PrevLineCommentLbl:
+
+ if( bPrevLineExtentsComment || (eScanType != SbxSTRING &&
+ ( bCompilerDirective ||
+ aSym.startsWith("'") ||
+ aSym.equalsIgnoreAsciiCase( "REM" ) ) ) )
+ {
+ bPrevLineExtentsComment = false;
+ aSym = "REM";
+ sal_Int32 nLen = aLine.getLength() - nLineIdx;
+ // tdf#149402 - don't extend comment if line ends in a whitespace (BasicCharClass::isWhitespace)
+ if (bCompatible && !bLineEndsWithWhitespace && aLine[nLineIdx + nLen - 1] == '_'
+ && aLine[nLineIdx + nLen - 2] == ' ')
+ bPrevLineExtentsComment = true;
+ nCol2 = nCol2 + nLen;
+ nLineIdx = -1;
+ }
+
+ if (nLineIdx == nLineIdxScanStart)
+ {
+ GenError( ERRCODE_BASIC_SYMBOL_EXPECTED );
+ return false;
+ }
+
+ return true;
+
+
+eoln:
+ if (nCol && aLine[--nLineIdx] == '_' && !bClosingUnderscore)
+ {
+ nLineIdx = -1;
+ bool bRes = NextSym();
+ if( aSym.startsWith(".") )
+ {
+ // object _
+ // .Method
+ // ^^^ <- spaces is legal in MSO VBA
+ bSpaces = false;
+ }
+ return bRes;
+ }
+ else
+ {
+ nLineIdx = -1;
+ nLine = nOldLine;
+ nCol1 = nOldCol1;
+ nCol2 = nOldCol2;
+ aSym = "\n";
+ nColLock = 0;
+ bClosingUnderscore = false;
+ // tdf#149157 - break multiline continuation in a comment after a new line
+ bPrevLineExtentsComment = false;
+ return true;
+ }
+}
+
+/* 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 0000000000..6caa3b2ed3
--- /dev/null
+++ b/basic/source/comp/symtbl.cxx
@@ -0,0 +1,534 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <memory>
+#include <parser.hxx>
+
+#include <osl/diagnose.h>
+
+#include <stdio.h>
+#include <rtl/character.hxx>
+#include <basic/sberrors.hxx>
+#include <utility>
+
+// All symbol names are laid down int the symbol-pool's stringpool, so that
+// all symbols are handled in the same case. On saving the code-image, the
+// global stringpool with the respective symbols is also saved.
+// The local stringpool holds all the symbols that don't move to the image
+// (labels, constant names etc.).
+
+SbiStringPool::SbiStringPool( )
+{}
+
+SbiStringPool::~SbiStringPool()
+{}
+
+OUString SbiStringPool::Find( sal_uInt32 n ) const
+{
+ if( n == 0 || n > aData.size() )
+ return OUString();
+ else
+ return aData[n - 1];
+}
+
+short SbiStringPool::Add( const OUString& rVal )
+{
+ sal_uInt32 n = aData.size();
+ for( sal_uInt32 i = 0; i < n; ++i )
+ {
+ OUString& p = aData[i];
+ if( p == rVal )
+ return i+1;
+ }
+
+ aData.push_back(rVal);
+ return static_cast<short>(++n);
+}
+
+short SbiStringPool::Add(double n, SbxDataType t)
+{
+ size_t size = 0;
+ const size_t aBufLength = 40;
+ char buf[aBufLength]{};
+
+ // tdf#143707 - add the type character after the null termination of the string in order to
+ // keep compatibility. After the type character has been added, the buffer contains the value
+ // of the double n, the string termination symbol, and the type character.
+ switch( t )
+ {
+ // tdf#142460 - properly handle boolean values in string pool
+ case SbxBOOL:
+ size = snprintf(buf, sizeof(buf), "%d", static_cast<short>(n)) + 1;
+ buf[size++] = 'b';
+ break;
+ // tdf#131296 - store numeric value including its type character
+ // See GetSuffixType in basic/source/comp/scanner.cxx for type characters
+ case SbxINTEGER:
+ size = snprintf(buf, sizeof(buf), "%d", static_cast<short>(n)) + 1;
+ buf[size++] = '%';
+ break;
+ case SbxLONG:
+ size = snprintf(buf, sizeof(buf), "%" SAL_PRIdINT32, static_cast<sal_Int32>(n)) + 1;
+ buf[size++] = '&';
+ break;
+ case SbxSINGLE:
+ size = snprintf(buf, sizeof(buf), "%.6g", static_cast<float>(n)) + 1;
+ buf[size++] = '!';
+ break;
+ case SbxDOUBLE:
+ size = snprintf(buf, sizeof(buf), "%.16g", n) + 1;
+ buf[size++] = '#';
+ break;
+ case SbxCURRENCY:
+ size = snprintf(buf, sizeof(buf), "%.16g", n) + 1;
+ buf[size++] = '@';
+ break;
+ default: assert(false); break; // should not happen
+ }
+
+ // tdf#143707 - add the content of the buffer to the string pool including its calculated length
+ return Add(OUString::fromUtf8(std::string_view(buf, size)));
+}
+
+SbiSymPool::SbiSymPool( SbiStringPool& r, SbiSymScope s, SbiParser* pP ) :
+ rStrings(r),
+ pParent(nullptr),
+ pParser(pP),
+ eScope(s),
+ nProcId(0),
+ nCur(0)
+{
+}
+
+SbiSymPool::~SbiSymPool()
+{}
+
+
+SbiSymDef* SbiSymPool::First()
+{
+ nCur = sal_uInt16(-1);
+ return Next();
+}
+
+SbiSymDef* SbiSymPool::Next()
+{
+ if (m_Data.size() <= ++nCur)
+ return nullptr;
+ else
+ return m_Data[ nCur ].get();
+}
+
+
+SbiSymDef* SbiSymPool::AddSym( const OUString& rName )
+{
+ SbiSymDef* p = new SbiSymDef( rName );
+ p->nPos = m_Data.size();
+ p->nId = rStrings.Add( rName );
+ p->nProcId = nProcId;
+ p->pIn = this;
+ m_Data.insert( m_Data.begin() + p->nPos, std::unique_ptr<SbiSymDef>(p) );
+ return p;
+}
+
+SbiProcDef* SbiSymPool::AddProc( const OUString& rName )
+{
+ SbiProcDef* p = new SbiProcDef( pParser, rName );
+ p->nPos = m_Data.size();
+ p->nId = rStrings.Add( rName );
+ // procs are always local
+ p->nProcId = 0;
+ p->pIn = this;
+ m_Data.insert( m_Data.begin() + p->nPos, std::unique_ptr<SbiProcDef>(p) );
+ return p;
+}
+
+// adding an externally constructed symbol definition
+
+void SbiSymPool::Add( SbiSymDef* pDef )
+{
+ if( !(pDef && pDef->pIn != this) )
+ return;
+
+ if( pDef->pIn )
+ {
+#ifdef DBG_UTIL
+
+ pParser->Error( ERRCODE_BASIC_INTERNAL_ERROR, "Dbl Pool" );
+#endif
+ return;
+ }
+
+ pDef->nPos = m_Data.size();
+ if( !pDef->nId )
+ {
+ // A unique name must be created in the string pool
+ // for static variables (Form ProcName:VarName)
+ OUString aName( pDef->aName );
+ if( pDef->IsStatic() )
+ {
+ aName = pParser->aGblStrings.Find( nProcId )
+ + ":"
+ + pDef->aName;
+ }
+ pDef->nId = rStrings.Add( aName );
+ }
+
+ if( !pDef->GetProcDef() )
+ {
+ pDef->nProcId = nProcId;
+ }
+ pDef->pIn = this;
+ m_Data.insert( m_Data.begin() + pDef->nPos, std::unique_ptr<SbiSymDef>(pDef) );
+}
+
+
+SbiSymDef* SbiSymPool::Find( const OUString& rName, bool bSearchInParents )
+{
+ sal_uInt16 nCount = m_Data.size();
+ for( sal_uInt16 i = 0; i < nCount; i++ )
+ {
+ SbiSymDef &r = *m_Data[ nCount - i - 1 ];
+ if( ( !r.nProcId || ( r.nProcId == nProcId)) &&
+ ( r.aName.equalsIgnoreAsciiCase(rName)))
+ {
+ return &r;
+ }
+ }
+ if( bSearchInParents && pParent )
+ {
+ return pParent->Find( rName );
+ }
+ else
+ {
+ return nullptr;
+ }
+}
+
+
+// find via position (from 0)
+
+SbiSymDef* SbiSymPool::Get( sal_uInt16 n )
+{
+ if (m_Data.size() <= n)
+ {
+ return nullptr;
+ }
+ else
+ {
+ return m_Data[ n ].get();
+ }
+}
+
+sal_uInt32 SbiSymPool::Define( const OUString& rName )
+{
+ SbiSymDef* p = Find( rName );
+ if( p )
+ {
+ if( p->IsDefined() )
+ {
+ pParser->Error( ERRCODE_BASIC_LABEL_DEFINED, rName );
+ }
+ }
+ else
+ {
+ p = AddSym( rName );
+ }
+ return p->Define();
+}
+
+sal_uInt32 SbiSymPool::Reference( const OUString& rName )
+{
+ SbiSymDef* p = Find( rName );
+ if( !p )
+ {
+ p = AddSym( rName );
+ }
+ // to be sure
+ pParser->aGen.GenStmnt();
+ return p->Reference();
+}
+
+
+void SbiSymPool::CheckRefs()
+{
+ for (std::unique_ptr<SbiSymDef> & r : m_Data)
+ {
+ if( !r->IsDefined() )
+ {
+ pParser->Error( ERRCODE_BASIC_UNDEF_LABEL, r->GetName() );
+ }
+ }
+}
+
+SbiSymDef::SbiSymDef( OUString _aName ) :
+ aName(std::move(_aName)),
+ eType(SbxEMPTY),
+ pIn(nullptr),
+ nLen(0),
+ nDims(0),
+ nId(0),
+ nTypeId(0),
+ nProcId(0),
+ nPos(0),
+ nChain(0),
+ bNew(false),
+ bChained(false),
+ bByVal(false),
+ bOpt(false),
+ bStatic(false),
+ bAs(false),
+ bGlobal(false),
+ bParamArray(false),
+ bWithEvents(false),
+ bWithBrackets(false),
+ nDefaultId(0),
+ nFixedStringLength(-1)
+{
+}
+
+SbiSymDef::~SbiSymDef()
+{
+}
+
+SbiProcDef* SbiSymDef::GetProcDef()
+{
+ return nullptr;
+}
+
+SbiConstDef* SbiSymDef::GetConstDef()
+{
+ return nullptr;
+}
+
+
+const OUString& SbiSymDef::GetName()
+{
+ if( pIn )
+ {
+ aName = pIn->rStrings.Find( nId );
+ }
+ return aName;
+}
+
+
+void SbiSymDef::SetType( SbxDataType t )
+{
+ if( t == SbxVARIANT && pIn )
+ {
+ //See if there have been any deftype statements to set the default type
+ //of a variable based on its starting letter
+ sal_Unicode cu = aName[0];
+ if( cu < 256 )
+ {
+ unsigned char ch = static_cast<unsigned char>(cu);
+ if( ch == '_' )
+ {
+ ch = 'Z';
+ }
+ int ch2 = rtl::toAsciiUpperCase( ch );
+ int nIndex = ch2 - 'A';
+ if (nIndex >= 0 && nIndex < N_DEF_TYPES)
+ t = pIn->pParser->eDefTypes[nIndex];
+ }
+ }
+ eType = t;
+}
+
+// construct a backchain, if not yet defined
+// the value that shall be stored as an operand is returned
+
+sal_uInt32 SbiSymDef::Reference()
+{
+ if( !bChained )
+ {
+ sal_uInt32 n = nChain;
+ nChain = pIn->pParser->aGen.GetOffset();
+ return n;
+ }
+ else return nChain;
+}
+
+
+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 = true;
+ return nChain;
+}
+
+// A symbol definition may have its own pool. This is the case
+// for objects and procedures (local variable)
+
+SbiSymPool& SbiSymDef::GetPool()
+{
+ if( !pPool )
+ {
+ pPool = std::make_unique<SbiSymPool>( pIn->pParser->aGblStrings, SbLOCAL, pIn->pParser );// is dumped
+ }
+ return *pPool;
+}
+
+SbiSymScope SbiSymDef::GetScope() const
+{
+ return pIn ? pIn->GetScope() : SbLOCAL;
+}
+
+
+// The procedure definition has three pools:
+// 1) aParams: is filled by the definition. Contains the
+// parameters' names, like they're used inside the body.
+// The first element is the return value.
+// 2) pPool: all local variables
+// 3) aLabels: labels
+
+SbiProcDef::SbiProcDef( SbiParser* pParser, const OUString& rName,
+ bool bProcDecl )
+ : SbiSymDef( rName )
+ , aParams( pParser->aGblStrings, SbPARAM, pParser ) // is dumped
+ , aLabels( pParser->aLclStrings, SbLOCAL, pParser ) // is not dumped
+ , mbProcDecl( bProcDecl )
+{
+ aParams.SetParent( &pParser->aPublics );
+ pPool = std::make_unique<SbiSymPool>( pParser->aGblStrings, SbLOCAL, pParser );
+ pPool->SetParent( &aParams );
+ nLine1 =
+ nLine2 = 0;
+ mePropMode = PropertyMode::NONE;
+ bPublic = true;
+ bCdecl = false;
+ bStatic = false;
+ // For return values the first element of the parameter
+ // list is always defined with name and type of the proc
+ aParams.AddSym( aName );
+}
+
+SbiProcDef::~SbiProcDef()
+{}
+
+SbiProcDef* SbiProcDef::GetProcDef()
+{
+ return this;
+}
+
+void SbiProcDef::SetType( SbxDataType t )
+{
+ SbiSymDef::SetType( t );
+ aParams.Get( 0 )->SetType( eType );
+}
+
+// match with a forward-declaration
+// if the match is OK, pOld is replaced by this in the pool
+// pOld is deleted in any case!
+
+void SbiProcDef::Match( SbiProcDef* pOld )
+{
+ SbiSymDef *pn=nullptr;
+ // parameter 0 is the function name
+ sal_uInt16 i;
+ for( i = 1; i < aParams.GetSize(); i++ )
+ {
+ SbiSymDef* po = pOld->aParams.Get( i );
+ pn = aParams.Get( i );
+ // no type matching - that is done during running
+ // but is it maybe called with too little parameters?
+ if( !po && !pn->IsOptional() && !pn->IsParamArray() )
+ {
+ break;
+ }
+ pOld->aParams.Next();
+ }
+
+ if( pn && i < aParams.GetSize() && pOld->pIn )
+ {
+ // mark the whole line
+ pOld->pIn->GetParser()->SetCol1( 0 );
+ pOld->pIn->GetParser()->Error( ERRCODE_BASIC_BAD_DECLARATION, aName );
+ }
+
+ if( !pIn && pOld->pIn )
+ {
+ // Replace old entry with the new one
+ nPos = pOld->nPos;
+ nId = pOld->nId;
+ pIn = pOld->pIn;
+
+ // don't delete pOld twice, if it's stored in m_Data
+ if (pOld == pIn->m_Data[nPos].get())
+ pOld = nullptr;
+ pIn->m_Data[nPos].reset(this);
+ }
+ delete pOld;
+}
+
+void SbiProcDef::setPropertyMode( PropertyMode ePropMode )
+{
+ mePropMode = ePropMode;
+ if( mePropMode == PropertyMode::NONE )
+ return;
+
+ // Prop name = original scanned procedure name
+ maPropName = aName;
+
+ // CompleteProcName includes "Property xxx "
+ // to avoid conflicts with other symbols
+ OUString aCompleteProcName = "Property ";
+ switch( mePropMode )
+ {
+ case PropertyMode::Get: aCompleteProcName += "Get "; break;
+ case PropertyMode::Let: aCompleteProcName += "Let "; break;
+ case PropertyMode::Set: aCompleteProcName += "Set "; break;
+ case PropertyMode::NONE: OSL_FAIL( "Illegal PropertyMode PropertyMode::NONE" ); break;
+ }
+ aCompleteProcName += aName;
+ aName = aCompleteProcName;
+}
+
+
+SbiConstDef::SbiConstDef( const OUString& rName )
+ : SbiSymDef( rName )
+{
+ nVal = 0; eType = SbxINTEGER;
+}
+
+void SbiConstDef::Set( double n, SbxDataType t )
+{
+ aVal.clear(); nVal = n; eType = t;
+}
+
+void SbiConstDef::Set( const OUString& 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 0000000000..814d5488f8
--- /dev/null
+++ b/basic/source/comp/token.cxx
@@ -0,0 +1,572 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <array>
+
+#include <basic/sberrors.hxx>
+#include <sal/macros.h>
+#include <o3tl/string_view.hxx>
+#include <basiccharclass.hxx>
+#include <token.hxx>
+
+namespace {
+
+struct TokenTable { SbiToken t; const char *s; };
+
+}
+
+const TokenTable aTokTable_Basic [] = {
+ { 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" },
+ { BASIC_EXPLICIT, "Explicit" },
+ { FOR, "For" },
+ { FUNCTION, "Function" },
+ { GET, "Get" },
+ { GLOBAL, "Global" },
+ { GOSUB, "GoSub" },
+ { GOTO, "GoTo" },
+ { IF, "If" },
+ { IMP, "Imp" },
+ { IMPLEMENTS, "Implements" },
+ { IN_, "In" },
+ { INPUT, "Input" }, // also 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" },
+ { PTRSAFE, "PtrSafe" },
+ { PUBLIC, "Public" },
+ { RANDOM, "Random" },
+ { READ, "Read" },
+ { REDIM, "ReDim" },
+ { REM, "Rem" },
+ { RESUME, "Resume" },
+ { RETURN, "Return" },
+ { RSET, "RSet" }, // JSM
+ { SELECT, "Select" },
+ { SET, "Set" },
+ { SHARED, "Shared" },
+ { TSINGLE, "Single" },
+ { STATIC, "Static" },
+ { STEP, "Step" },
+ { STOP, "Stop" },
+ { TSTRING, "String" },
+ { SUB, "Sub" },
+ { STOP, "System" },
+ { TEXT, "Text" },
+ { THEN, "Then" },
+ { TO, "To", },
+ { TYPE, "Type" },
+ { TYPEOF, "TypeOf" },
+ { UNTIL, "Until" },
+ { TVARIANT, "Variant" },
+ { VBASUPPORT, "VbaSupport" },
+ { WEND, "Wend" },
+ { WHILE, "While" },
+ { WITH, "With" },
+ { WITHEVENTS, "WithEvents" },
+ { WRITE, "Write" }, // also WRITE #
+ { XOR, "Xor" },
+};
+
+namespace {
+
+// #i109076
+class TokenLabelInfo
+{
+ std::array<bool,VBASUPPORT+1> m_pTokenCanBeLabelTab;
+
+public:
+ TokenLabelInfo();
+
+ bool canTokenBeLabel( SbiToken eTok )
+ { return m_pTokenCanBeLabelTab[eTok]; }
+};
+
+}
+
+// #i109076
+TokenLabelInfo::TokenLabelInfo()
+{
+ m_pTokenCanBeLabelTab.fill(false);
+
+ // Token accepted as label by VBA
+ static const SbiToken eLabelToken[] = { ACCESS, ALIAS, APPEND, BASE, BINARY, CLASSMODULE,
+ COMPARE, COMPATIBLE, DEFERR, ERROR_, BASIC_EXPLICIT, LIB, LINE, LPRINT, NAME,
+ TOBJECT, OUTPUT, PROPERTY, RANDOM, READ, STEP, STOP, TEXT, VBASUPPORT };
+ for( SbiToken eTok : eLabelToken )
+ {
+ m_pTokenCanBeLabelTab[eTok] = true;
+ }
+}
+
+
+SbiTokenizer::SbiTokenizer( const OUString& rSrc, StarBASIC* pb )
+ : SbiScanner(rSrc, pb)
+ , eCurTok(NIL)
+ , ePush(NIL)
+ , nPLine(0)
+ , nPCol1(0)
+ , nPCol2(0)
+ , bEof(false)
+ , bEos(true)
+ , bAs(false)
+ , bErrorIsSymbol(true)
+{
+}
+
+void SbiTokenizer::Push( SbiToken t )
+{
+ if( ePush != NIL )
+ Error( ERRCODE_BASIC_INTERNAL_ERROR, "PUSH" );
+ else ePush = t;
+}
+
+void SbiTokenizer::Error( ErrCode code, const OUString &aMsg )
+{
+ aError = aMsg;
+ Error( code );
+}
+
+void SbiTokenizer::Error( ErrCode code, SbiToken tok )
+{
+ aError = Symbol( tok );
+ Error( code );
+}
+
+// reading in the next token without absorbing it
+
+SbiToken SbiTokenizer::Peek()
+{
+ if( ePush == NIL )
+ {
+ sal_Int32 nOldLine = nLine;
+ sal_Int32 nOldCol1 = nCol1;
+ sal_Int32 nOldCol2 = nCol2;
+ ePush = Next();
+ nPLine = nLine; nLine = nOldLine;
+ nPCol1 = nCol1; nCol1 = nOldCol1;
+ nPCol2 = nCol2; nCol2 = nOldCol2;
+ }
+ eCurTok = ePush;
+ return eCurTok;
+}
+
+// For decompilation. Numbers and symbols return an empty string.
+
+const OUString& SbiTokenizer::Symbol( SbiToken t )
+{
+ // character token?
+ if( t < FIRSTKWD )
+ {
+ aSym = OUString(sal::static_int_cast<sal_Unicode>(t));
+ return aSym;
+ }
+ switch( t )
+ {
+ case NEG :
+ aSym = "-";
+ return aSym;
+ case EOS :
+ aSym = ":/CRLF";
+ return aSym;
+ case EOLN :
+ aSym = "CRLF";
+ return aSym;
+ default:
+ break;
+ }
+ for( auto& rTok : aTokTable_Basic )
+ {
+ if( rTok.t == t )
+ {
+ aSym = OStringToOUString(rTok.s, RTL_TEXTENCODING_ASCII_US);
+ return aSym;
+ }
+ }
+ const sal_Unicode *p = aSym.getStr();
+ if (*p <= ' ')
+ {
+ aSym = "???";
+ }
+ return aSym;
+}
+
+// Reading in the next token and put it down.
+// Tokens that don't appear in the token table
+// are directly returned as a character.
+// Some words are treated in a special way.
+
+SbiToken SbiTokenizer::Next()
+{
+ if (bEof)
+ {
+ return EOLN;
+ }
+ // have read in one already?
+ if( ePush != NIL )
+ {
+ eCurTok = ePush;
+ ePush = NIL;
+ nLine = nPLine;
+ nCol1 = nPCol1;
+ nCol2 = nPCol2;
+ bEos = IsEoln( eCurTok );
+ return eCurTok;
+ }
+ const TokenTable *tp;
+
+ if( !NextSym() )
+ {
+ bEof = bEos = true;
+ eCurTok = EOLN;
+ return eCurTok;
+ }
+
+ if( aSym.startsWith("\n") )
+ {
+ bEos = true;
+ eCurTok = EOLN;
+ return eCurTok;
+ }
+ bEos = false;
+
+ if( bNumber )
+ {
+ eCurTok = NUMBER;
+ return eCurTok;
+ }
+ else if( ( eScanType == SbxDATE || eScanType == SbxSTRING ) && !bSymbol )
+ {
+ eCurTok = FIXSTRING;
+ return eCurTok;
+ }
+ else if( aSym.isEmpty() )
+ {
+ //something went wrong
+ bEof = bEos = true;
+ eCurTok = EOLN;
+ return eCurTok;
+ }
+ // Special cases of characters that are between "Z" and "a". ICompare()
+ // evaluates the position of these characters in different ways.
+ else if( aSym[0] == '^' )
+ {
+ eCurTok = EXPON;
+ return eCurTok;
+ }
+ else if( aSym[0] == '\\' )
+ {
+ eCurTok = IDIV;
+ return eCurTok;
+ }
+ else
+ {
+ if( eScanType != SbxVARIANT )
+ {
+ eCurTok = SYMBOL;
+ return eCurTok;
+ }
+ // valid token?
+ short lb = 0;
+ short ub = std::size(aTokTable_Basic)-1;
+ short delta;
+ do
+ {
+ delta = (ub - lb) >> 1;
+ tp = &aTokTable_Basic[ lb + delta ];
+ sal_Int32 res = aSym.compareToIgnoreAsciiCaseAscii( tp->s );
+
+ if( res == 0 )
+ {
+ goto special;
+ }
+ if( res < 0 )
+ {
+ if ((ub - lb) == 2)
+ {
+ ub = lb;
+ }
+ else
+ {
+ ub = ub - delta;
+ }
+ }
+ else
+ {
+ if ((ub -lb) == 2)
+ {
+ lb = ub;
+ }
+ else
+ {
+ lb = lb + delta;
+ }
+ }
+ }
+ while( delta );
+ // Symbol? if not >= token
+ sal_Unicode ch = aSym[0];
+ if( !BasicCharClass::isAlpha( ch, bCompatible ) && !bSymbol )
+ {
+ eCurTok = static_cast<SbiToken>(ch & 0x00FF);
+ return eCurTok;
+ }
+ eCurTok = SYMBOL;
+ return eCurTok;
+ }
+special:
+ // #i92642
+ bool bStartOfLine = (eCurTok == NIL || eCurTok == REM || eCurTok == EOLN ||
+ eCurTok == THEN || eCurTok == ELSE); // single line If
+ if( !bStartOfLine && (tp->t == NAME || tp->t == LINE) )
+ {
+ eCurTok = SYMBOL;
+ return eCurTok;
+ }
+ else if( tp->t == TEXT )
+ {
+ eCurTok = SYMBOL;
+ return eCurTok;
+ }
+ // maybe we can expand this for other statements that have parameters
+ // that are keywords ( and those keywords are only used within such
+ // statements )
+ // what's happening here is that if we come across 'append' ( and we are
+ // not in the middle of parsing a special statement ( like 'Open')
+ // we just treat keyword 'append' as a normal 'SYMBOL'.
+ // Also we accept Dim APPEND
+ else if ( ( !bInStatement || eCurTok == DIM ) && tp->t == APPEND )
+ {
+ eCurTok = SYMBOL;
+ return eCurTok;
+ }
+ // #i92642: Special LINE token handling -> SbiParser::Line()
+
+ // END IF, CASE, SUB, DEF, FUNCTION, TYPE, CLASS, WITH
+ if( tp->t == END )
+ {
+ // from 15.3.96, special treatment for END, at Peek() the current
+ // time is lost, so memorize everything and restore after
+ sal_Int32 nOldLine = nLine;
+ sal_Int32 nOldCol = nCol;
+ sal_Int32 nOldCol1 = nCol1;
+ sal_Int32 nOldCol2 = nCol2;
+ OUString aOldSym = aSym;
+ SaveLine(); // save pLine in the scanner
+
+ 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; break;
+ }
+ nCol1 = nOldCol1;
+ if( eCurTok == END )
+ {
+ // reset everything so that token is read completely newly after END
+ ePush = NIL;
+ nLine = nOldLine;
+ nCol = nOldCol;
+ nCol2 = nOldCol2;
+ aSym = aOldSym;
+ RestoreLine();
+ }
+ return eCurTok;
+ }
+ // are data types keywords?
+ // there is ERROR(), DATA(), STRING() etc.
+ eCurTok = tp->t;
+ // AS: data types are keywords
+ if( tp->t == AS )
+ {
+ bAs = true;
+ }
+ else
+ {
+ if( bAs )
+ {
+ bAs = false;
+ }
+ else if( eCurTok >= DATATYPE1 && eCurTok <= DATATYPE2 && (bErrorIsSymbol || eCurTok != ERROR_) )
+ {
+ eCurTok = SYMBOL;
+ }
+ }
+
+ // CLASSMODULE, PROPERTY, GET, ENUM token only visible in compatible mode
+ SbiToken eTok = tp->t;
+ if( bCompatible )
+ {
+ // #129904 Suppress system
+ if( eTok == STOP && aSym.equalsIgnoreAsciiCase("system") )
+ {
+ eCurTok = SYMBOL;
+ }
+ if( eTok == GET && bStartOfLine )
+ {
+ 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;
+}
+
+bool SbiTokenizer::MayBeLabel( bool bNeedsColon )
+{
+ static TokenLabelInfo gaStaticTokenLabelInfo;
+
+ if( eCurTok == SYMBOL || gaStaticTokenLabelInfo.canTokenBeLabel( eCurTok ) )
+ {
+ return !bNeedsColon || DoesColonFollow();
+ }
+ else
+ {
+ return ( eCurTok == NUMBER
+ && eScanType == SbxINTEGER
+ && nVal >= 0 );
+ }
+}
+
+
+OUString SbiTokenizer::GetKeywordCase( std::u16string_view sKeyword )
+{
+ for( auto& rTok : aTokTable_Basic )
+ {
+ if( o3tl::equalsIgnoreAsciiCase(sKeyword, rTok.s) )
+ return OStringToOUString(rTok.s, RTL_TEXTENCODING_ASCII_US);
+ }
+ return OUString();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */