summaryrefslogtreecommitdiffstats
path: root/sc/source/core/tool/interpr4.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sc/source/core/tool/interpr4.cxx')
-rw-r--r--sc/source/core/tool/interpr4.cxx4764
1 files changed, 4764 insertions, 0 deletions
diff --git a/sc/source/core/tool/interpr4.cxx b/sc/source/core/tool/interpr4.cxx
new file mode 100644
index 000000000..347377ed0
--- /dev/null
+++ b/sc/source/core/tool/interpr4.cxx
@@ -0,0 +1,4764 @@
+/* -*- 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 <config_features.h>
+
+#include <interpre.hxx>
+
+#include <rangelst.hxx>
+#include <o3tl/safeint.hxx>
+#include <rtl/math.hxx>
+#include <sfx2/app.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/docfilt.hxx>
+#include <basic/sbmeth.hxx>
+#include <basic/sbmod.hxx>
+#include <basic/sbstar.hxx>
+#include <basic/sbx.hxx>
+#include <basic/sbxobj.hxx>
+#include <basic/sbuno.hxx>
+#include <svl/zforlist.hxx>
+#include <svl/sharedstringpool.hxx>
+#include <unotools/charclass.hxx>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+
+#include <com/sun/star/table/XCellRange.hpp>
+#include <com/sun/star/script/XInvocation.hpp>
+#include <com/sun/star/sheet/XSheetCellRange.hpp>
+
+#include <global.hxx>
+#include <dbdata.hxx>
+#include <formulacell.hxx>
+#include <callform.hxx>
+#include <addincol.hxx>
+#include <document.hxx>
+#include <dociter.hxx>
+#include <docoptio.hxx>
+#include <scmatrix.hxx>
+#include <adiasync.hxx>
+#include <sc.hrc>
+#include <cellsuno.hxx>
+#include <optuno.hxx>
+#include <rangeseq.hxx>
+#include <addinlis.hxx>
+#include <jumpmatrix.hxx>
+#include <parclass.hxx>
+#include <externalrefmgr.hxx>
+#include <formula/FormulaCompiler.hxx>
+#include <macromgr.hxx>
+#include <doubleref.hxx>
+#include <queryparam.hxx>
+#include <tokenarray.hxx>
+#include <compiler.hxx>
+
+#include <math.h>
+#include <float.h>
+#include <map>
+#include <algorithm>
+#include <basic/basmgr.hxx>
+#include <vbahelper/vbaaccesshelper.hxx>
+#include <memory>
+#include <stack>
+
+using namespace com::sun::star;
+using namespace formula;
+using ::std::unique_ptr;
+
+#define ADDIN_MAXSTRLEN 256
+
+thread_local std::unique_ptr<ScTokenStack> ScInterpreter::pGlobalStack;
+thread_local bool ScInterpreter::bGlobalStackInUse = false;
+
+// document access functions
+
+void ScInterpreter::ReplaceCell( ScAddress& rPos )
+{
+ size_t ListSize = pDok->m_TableOpList.size();
+ for ( size_t i = 0; i < ListSize; ++i )
+ {
+ ScInterpreterTableOpParams *const pTOp = pDok->m_TableOpList[ i ];
+ if ( rPos == pTOp->aOld1 )
+ {
+ rPos = pTOp->aNew1;
+ return ;
+ }
+ else if ( rPos == pTOp->aOld2 )
+ {
+ rPos = pTOp->aNew2;
+ return ;
+ }
+ }
+}
+
+bool ScInterpreter::IsTableOpInRange( const ScRange& rRange )
+{
+ if ( rRange.aStart == rRange.aEnd )
+ return false; // not considered to be a range in TableOp sense
+
+ // we can't replace a single cell in a range
+ size_t ListSize = pDok->m_TableOpList.size();
+ for ( size_t i = 0; i < ListSize; ++i )
+ {
+ ScInterpreterTableOpParams *const pTOp = pDok->m_TableOpList[ i ];
+ if ( rRange.In( pTOp->aOld1 ) )
+ return true;
+ if ( rRange.In( pTOp->aOld2 ) )
+ return true;
+ }
+ return false;
+}
+
+sal_uInt32 ScInterpreter::GetCellNumberFormat( const ScAddress& rPos, ScRefCellValue& rCell )
+{
+ sal_uInt32 nFormat;
+ FormulaError nErr;
+ if (rCell.isEmpty())
+ {
+ nFormat = pDok->GetNumberFormat( mrContext, rPos );
+ nErr = FormulaError::NONE;
+ }
+ else
+ {
+ if (rCell.meType == CELLTYPE_FORMULA)
+ nErr = rCell.mpFormula->GetErrCode();
+ else
+ nErr = FormulaError::NONE;
+ nFormat = pDok->GetNumberFormat( mrContext, rPos );
+ }
+
+ SetError(nErr);
+ return nFormat;
+}
+
+/// Only ValueCell, formula cells already store the result rounded.
+double ScInterpreter::GetValueCellValue( const ScAddress& rPos, double fOrig )
+{
+ if ( bCalcAsShown && fOrig != 0.0 )
+ {
+ sal_uInt32 nFormat = pDok->GetNumberFormat( mrContext, rPos );
+ fOrig = pDok->RoundValueAsShown( fOrig, nFormat, &mrContext );
+ }
+ return fOrig;
+}
+
+FormulaError ScInterpreter::GetCellErrCode( const ScRefCellValue& rCell )
+{
+ return rCell.meType == CELLTYPE_FORMULA ? rCell.mpFormula->GetErrCode() : FormulaError::NONE;
+}
+
+double ScInterpreter::ConvertStringToValue( const OUString& rStr )
+{
+ FormulaError nError = FormulaError::NONE;
+ double fValue = ScGlobal::ConvertStringToValue( rStr, maCalcConfig, nError, mnStringNoValueError,
+ pFormatter, nCurFmtType);
+ if (nError != FormulaError::NONE)
+ SetError(nError);
+ return fValue;
+}
+
+double ScInterpreter::ConvertStringToValue( const OUString& rStr, FormulaError& rError, SvNumFormatType& rCurFmtType )
+{
+ return ScGlobal::ConvertStringToValue( rStr, maCalcConfig, rError, mnStringNoValueError, pFormatter, rCurFmtType);
+}
+
+double ScInterpreter::GetCellValue( const ScAddress& rPos, ScRefCellValue& rCell )
+{
+ FormulaError nErr = nGlobalError;
+ nGlobalError = FormulaError::NONE;
+ double nVal = GetCellValueOrZero(rPos, rCell);
+ if ( nGlobalError == FormulaError::NONE || nGlobalError == FormulaError::CellNoValue )
+ nGlobalError = nErr;
+ return nVal;
+}
+
+double ScInterpreter::GetCellValueOrZero( const ScAddress& rPos, ScRefCellValue& rCell )
+{
+ double fValue = 0.0;
+
+ CellType eType = rCell.meType;
+ switch (eType)
+ {
+ case CELLTYPE_FORMULA:
+ {
+ ScFormulaCell* pFCell = rCell.mpFormula;
+ FormulaError nErr = pFCell->GetErrCode();
+ if( nErr == FormulaError::NONE )
+ {
+ if (pFCell->IsValue())
+ {
+ fValue = pFCell->GetValue();
+ pDok->GetNumberFormatInfo( mrContext, nCurFmtType, nCurFmtIndex,
+ rPos );
+ }
+ else
+ {
+ fValue = ConvertStringToValue(pFCell->GetString().getString());
+ }
+ }
+ else
+ {
+ fValue = 0.0;
+ SetError(nErr);
+ }
+ }
+ break;
+ case CELLTYPE_VALUE:
+ {
+ fValue = rCell.mfValue;
+ nCurFmtIndex = pDok->GetNumberFormat( mrContext, rPos );
+ nCurFmtType = mrContext.GetNumberFormatType( nCurFmtIndex );
+ if ( bCalcAsShown && fValue != 0.0 )
+ fValue = pDok->RoundValueAsShown( fValue, nCurFmtIndex, &mrContext );
+ }
+ break;
+ case CELLTYPE_STRING:
+ case CELLTYPE_EDIT:
+ {
+ // SUM(A1:A2) differs from A1+A2. No good. But people insist on
+ // it ... #i5658#
+ OUString aStr = rCell.getString(pDok);
+ fValue = ConvertStringToValue( aStr );
+ }
+ break;
+ case CELLTYPE_NONE:
+ fValue = 0.0; // empty or broadcaster cell
+ break;
+ }
+
+ return fValue;
+}
+
+void ScInterpreter::GetCellString( svl::SharedString& rStr, ScRefCellValue& rCell )
+{
+ FormulaError nErr = FormulaError::NONE;
+
+ switch (rCell.meType)
+ {
+ case CELLTYPE_STRING:
+ case CELLTYPE_EDIT:
+ rStr = mrStrPool.intern(rCell.getString(pDok));
+ break;
+ case CELLTYPE_FORMULA:
+ {
+ ScFormulaCell* pFCell = rCell.mpFormula;
+ nErr = pFCell->GetErrCode();
+ if (pFCell->IsValue())
+ {
+ rStr = GetStringFromDouble( pFCell->GetValue() );
+ }
+ else
+ rStr = pFCell->GetString();
+ }
+ break;
+ case CELLTYPE_VALUE:
+ {
+ rStr = GetStringFromDouble( rCell.mfValue );
+ }
+ break;
+ default:
+ rStr = svl::SharedString::getEmptyString();
+ break;
+ }
+
+ SetError(nErr);
+}
+
+bool ScInterpreter::CreateDoubleArr(SCCOL nCol1, SCROW nRow1, SCTAB nTab1,
+ SCCOL nCol2, SCROW nRow2, SCTAB nTab2, sal_uInt8* pCellArr)
+{
+
+ // Old Add-Ins are hard limited to sal_uInt16 values.
+ static_assert(MAXCOLCOUNT <= SAL_MAX_UINT16, "Add check for columns > SAL_MAX_UINT16!");
+ if (nRow1 > SAL_MAX_UINT16 || nRow2 > SAL_MAX_UINT16)
+ return false;
+
+ sal_uInt16 nCount = 0;
+ sal_uInt16* p = reinterpret_cast<sal_uInt16*>(pCellArr);
+ *p++ = static_cast<sal_uInt16>(nCol1);
+ *p++ = static_cast<sal_uInt16>(nRow1);
+ *p++ = static_cast<sal_uInt16>(nTab1);
+ *p++ = static_cast<sal_uInt16>(nCol2);
+ *p++ = static_cast<sal_uInt16>(nRow2);
+ *p++ = static_cast<sal_uInt16>(nTab2);
+ sal_uInt16* pCount = p;
+ *p++ = 0;
+ sal_uInt16 nPos = 14;
+ SCTAB nTab = nTab1;
+ ScAddress aAdr;
+ while (nTab <= nTab2)
+ {
+ aAdr.SetTab( nTab );
+ SCROW nRow = nRow1;
+ while (nRow <= nRow2)
+ {
+ aAdr.SetRow( nRow );
+ SCCOL nCol = nCol1;
+ while (nCol <= nCol2)
+ {
+ aAdr.SetCol( nCol );
+
+ ScRefCellValue aCell(*pDok, aAdr);
+ if (!aCell.isEmpty())
+ {
+ FormulaError nErr = FormulaError::NONE;
+ double nVal = 0.0;
+ bool bOk = true;
+ switch (aCell.meType)
+ {
+ case CELLTYPE_VALUE :
+ nVal = GetValueCellValue(aAdr, aCell.mfValue);
+ break;
+ case CELLTYPE_FORMULA :
+ if (aCell.mpFormula->IsValue())
+ {
+ nErr = aCell.mpFormula->GetErrCode();
+ nVal = aCell.mpFormula->GetValue();
+ }
+ else
+ bOk = false;
+ break;
+ default :
+ bOk = false;
+ break;
+ }
+ if (bOk)
+ {
+ if ((nPos + (4 * sizeof(sal_uInt16)) + sizeof(double)) > MAXARRSIZE)
+ return false;
+ *p++ = static_cast<sal_uInt16>(nCol);
+ *p++ = static_cast<sal_uInt16>(nRow);
+ *p++ = static_cast<sal_uInt16>(nTab);
+ *p++ = static_cast<sal_uInt16>(nErr);
+ memcpy( p, &nVal, sizeof(double));
+ nPos += 8 + sizeof(double);
+ p = reinterpret_cast<sal_uInt16*>( pCellArr + nPos );
+ nCount++;
+ }
+ }
+ nCol++;
+ }
+ nRow++;
+ }
+ nTab++;
+ }
+ *pCount = nCount;
+ return true;
+}
+
+bool ScInterpreter::CreateStringArr(SCCOL nCol1, SCROW nRow1, SCTAB nTab1,
+ SCCOL nCol2, SCROW nRow2, SCTAB nTab2,
+ sal_uInt8* pCellArr)
+{
+
+ // Old Add-Ins are hard limited to sal_uInt16 values.
+ static_assert(MAXCOLCOUNT <= SAL_MAX_UINT16, "Add check for columns > SAL_MAX_UINT16!");
+ if (nRow1 > SAL_MAX_UINT16 || nRow2 > SAL_MAX_UINT16)
+ return false;
+
+ sal_uInt16 nCount = 0;
+ sal_uInt16* p = reinterpret_cast<sal_uInt16*>(pCellArr);
+ *p++ = static_cast<sal_uInt16>(nCol1);
+ *p++ = static_cast<sal_uInt16>(nRow1);
+ *p++ = static_cast<sal_uInt16>(nTab1);
+ *p++ = static_cast<sal_uInt16>(nCol2);
+ *p++ = static_cast<sal_uInt16>(nRow2);
+ *p++ = static_cast<sal_uInt16>(nTab2);
+ sal_uInt16* pCount = p;
+ *p++ = 0;
+ sal_uInt16 nPos = 14;
+ SCTAB nTab = nTab1;
+ while (nTab <= nTab2)
+ {
+ SCROW nRow = nRow1;
+ while (nRow <= nRow2)
+ {
+ SCCOL nCol = nCol1;
+ while (nCol <= nCol2)
+ {
+ ScRefCellValue aCell(*pDok, ScAddress(nCol, nRow, nTab));
+ if (!aCell.isEmpty())
+ {
+ OUString aStr;
+ FormulaError nErr = FormulaError::NONE;
+ bool bOk = true;
+ switch (aCell.meType)
+ {
+ case CELLTYPE_STRING:
+ case CELLTYPE_EDIT:
+ aStr = aCell.getString(pDok);
+ break;
+ case CELLTYPE_FORMULA:
+ if (!aCell.mpFormula->IsValue())
+ {
+ nErr = aCell.mpFormula->GetErrCode();
+ aStr = aCell.mpFormula->GetString().getString();
+ }
+ else
+ bOk = false;
+ break;
+ default :
+ bOk = false;
+ break;
+ }
+ if (bOk)
+ {
+ OString aTmp(OUStringToOString(aStr,
+ osl_getThreadTextEncoding()));
+ // Old Add-Ins are limited to sal_uInt16 string
+ // lengths, and room for pad byte check.
+ if ( aTmp.getLength() > SAL_MAX_UINT16 - 2 )
+ return false;
+ // Append a 0-pad-byte if string length is odd
+ // MUST be sal_uInt16
+ sal_uInt16 nStrLen = static_cast<sal_uInt16>(aTmp.getLength());
+ sal_uInt16 nLen = ( nStrLen + 2 ) & ~1;
+
+ if ((static_cast<sal_uLong>(nPos) + (5 * sizeof(sal_uInt16)) + nLen) > MAXARRSIZE)
+ return false;
+ *p++ = static_cast<sal_uInt16>(nCol);
+ *p++ = static_cast<sal_uInt16>(nRow);
+ *p++ = static_cast<sal_uInt16>(nTab);
+ *p++ = static_cast<sal_uInt16>(nErr);
+ *p++ = nLen;
+ memcpy( p, aTmp.getStr(), nStrLen + 1);
+ nPos += 10 + nStrLen + 1;
+ sal_uInt8* q = pCellArr + nPos;
+ if( (nStrLen & 1) == 0 )
+ {
+ *q++ = 0;
+ nPos++;
+ }
+ p = reinterpret_cast<sal_uInt16*>( pCellArr + nPos );
+ nCount++;
+ }
+ }
+ nCol++;
+ }
+ nRow++;
+ }
+ nTab++;
+ }
+ *pCount = nCount;
+ return true;
+}
+
+bool ScInterpreter::CreateCellArr(SCCOL nCol1, SCROW nRow1, SCTAB nTab1,
+ SCCOL nCol2, SCROW nRow2, SCTAB nTab2,
+ sal_uInt8* pCellArr)
+{
+
+ // Old Add-Ins are hard limited to sal_uInt16 values.
+ static_assert(MAXCOLCOUNT <= SAL_MAX_UINT16, "Add check for columns > SAL_MAX_UINT16!");
+ if (nRow1 > SAL_MAX_UINT16 || nRow2 > SAL_MAX_UINT16)
+ return false;
+
+ sal_uInt16 nCount = 0;
+ sal_uInt16* p = reinterpret_cast<sal_uInt16*>(pCellArr);
+ *p++ = static_cast<sal_uInt16>(nCol1);
+ *p++ = static_cast<sal_uInt16>(nRow1);
+ *p++ = static_cast<sal_uInt16>(nTab1);
+ *p++ = static_cast<sal_uInt16>(nCol2);
+ *p++ = static_cast<sal_uInt16>(nRow2);
+ *p++ = static_cast<sal_uInt16>(nTab2);
+ sal_uInt16* pCount = p;
+ *p++ = 0;
+ sal_uInt16 nPos = 14;
+ SCTAB nTab = nTab1;
+ ScAddress aAdr;
+ while (nTab <= nTab2)
+ {
+ aAdr.SetTab( nTab );
+ SCROW nRow = nRow1;
+ while (nRow <= nRow2)
+ {
+ aAdr.SetRow( nRow );
+ SCCOL nCol = nCol1;
+ while (nCol <= nCol2)
+ {
+ aAdr.SetCol( nCol );
+ ScRefCellValue aCell(*pDok, aAdr);
+ if (!aCell.isEmpty())
+ {
+ FormulaError nErr = FormulaError::NONE;
+ sal_uInt16 nType = 0; // 0 = number; 1 = string
+ double nVal = 0.0;
+ OUString aStr;
+ bool bOk = true;
+ switch (aCell.meType)
+ {
+ case CELLTYPE_STRING :
+ case CELLTYPE_EDIT :
+ aStr = aCell.getString(pDok);
+ nType = 1;
+ break;
+ case CELLTYPE_VALUE :
+ nVal = GetValueCellValue(aAdr, aCell.mfValue);
+ break;
+ case CELLTYPE_FORMULA :
+ nErr = aCell.mpFormula->GetErrCode();
+ if (aCell.mpFormula->IsValue())
+ nVal = aCell.mpFormula->GetValue();
+ else
+ aStr = aCell.mpFormula->GetString().getString();
+ break;
+ default :
+ bOk = false;
+ break;
+ }
+ if (bOk)
+ {
+ if ((nPos + (5 * sizeof(sal_uInt16))) > MAXARRSIZE)
+ return false;
+ *p++ = static_cast<sal_uInt16>(nCol);
+ *p++ = static_cast<sal_uInt16>(nRow);
+ *p++ = static_cast<sal_uInt16>(nTab);
+ *p++ = static_cast<sal_uInt16>(nErr);
+ *p++ = nType;
+ nPos += 10;
+ if (nType == 0)
+ {
+ if ((nPos + sizeof(double)) > MAXARRSIZE)
+ return false;
+ memcpy( p, &nVal, sizeof(double));
+ nPos += sizeof(double);
+ }
+ else
+ {
+ OString aTmp(OUStringToOString(aStr,
+ osl_getThreadTextEncoding()));
+ // Old Add-Ins are limited to sal_uInt16 string
+ // lengths, and room for pad byte check.
+ if ( aTmp.getLength() > SAL_MAX_UINT16 - 2 )
+ return false;
+ // Append a 0-pad-byte if string length is odd
+ // MUST be sal_uInt16
+ sal_uInt16 nStrLen = static_cast<sal_uInt16>(aTmp.getLength());
+ sal_uInt16 nLen = ( nStrLen + 2 ) & ~1;
+ if ( (static_cast<sal_uLong>(nPos) + 2 + nLen) > MAXARRSIZE)
+ return false;
+ *p++ = nLen;
+ memcpy( p, aTmp.getStr(), nStrLen + 1);
+ nPos += 2 + nStrLen + 1;
+ sal_uInt8* q = pCellArr + nPos;
+ if( (nStrLen & 1) == 0 )
+ {
+ *q++ = 0;
+ nPos++;
+ }
+ }
+ nCount++;
+ p = reinterpret_cast<sal_uInt16*>( pCellArr + nPos );
+ }
+ }
+ nCol++;
+ }
+ nRow++;
+ }
+ nTab++;
+ }
+ *pCount = nCount;
+ return true;
+}
+
+// Stack operations
+
+// Also releases a TempToken if appropriate.
+
+void ScInterpreter::PushWithoutError( const FormulaToken& r )
+{
+ if ( sp >= MAXSTACK )
+ SetError( FormulaError::StackOverflow );
+ else
+ {
+ r.IncRef();
+ if( sp >= maxsp )
+ maxsp = sp + 1;
+ else
+ pStack[ sp ]->DecRef();
+ pStack[ sp ] = &r;
+ ++sp;
+ }
+}
+
+void ScInterpreter::Push( const FormulaToken& r )
+{
+ if ( sp >= MAXSTACK )
+ SetError( FormulaError::StackOverflow );
+ else
+ {
+ if (nGlobalError != FormulaError::NONE)
+ {
+ if (r.GetType() == svError)
+ PushWithoutError( r);
+ else
+ PushTempTokenWithoutError( new FormulaErrorToken( nGlobalError));
+ }
+ else
+ PushWithoutError( r);
+ }
+}
+
+void ScInterpreter::PushTempToken( FormulaToken* p )
+{
+ if ( sp >= MAXSTACK )
+ {
+ SetError( FormulaError::StackOverflow );
+ // p may be a dangling pointer hereafter!
+ p->DeleteIfZeroRef();
+ }
+ else
+ {
+ if (nGlobalError != FormulaError::NONE)
+ {
+ if (p->GetType() == svError)
+ {
+ p->SetError( nGlobalError);
+ PushTempTokenWithoutError( p);
+ }
+ else
+ {
+ // p may be a dangling pointer hereafter!
+ p->DeleteIfZeroRef();
+ PushTempTokenWithoutError( new FormulaErrorToken( nGlobalError));
+ }
+ }
+ else
+ PushTempTokenWithoutError( p);
+ }
+}
+
+void ScInterpreter::PushTempTokenWithoutError( const FormulaToken* p )
+{
+ p->IncRef();
+ if ( sp >= MAXSTACK )
+ {
+ SetError( FormulaError::StackOverflow );
+ // p may be a dangling pointer hereafter!
+ p->DecRef();
+ }
+ else
+ {
+ if( sp >= maxsp )
+ maxsp = sp + 1;
+ else
+ pStack[ sp ]->DecRef();
+ pStack[ sp ] = p;
+ ++sp;
+ }
+}
+
+void ScInterpreter::PushTokenRef( const formula::FormulaConstTokenRef& x )
+{
+ if ( sp >= MAXSTACK )
+ {
+ SetError( FormulaError::StackOverflow );
+ }
+ else
+ {
+ if (nGlobalError != FormulaError::NONE)
+ {
+ if (x->GetType() == svError && x->GetError() == nGlobalError)
+ PushTempTokenWithoutError( x.get());
+ else
+ PushTempTokenWithoutError( new FormulaErrorToken( nGlobalError));
+ }
+ else
+ PushTempTokenWithoutError( x.get());
+ }
+}
+
+void ScInterpreter::PushCellResultToken( bool bDisplayEmptyAsString,
+ const ScAddress & rAddress, SvNumFormatType * pRetTypeExpr, sal_uInt32 * pRetIndexExpr, bool bFinalResult )
+{
+ ScRefCellValue aCell(*pDok, rAddress);
+ if (aCell.hasEmptyValue())
+ {
+ bool bInherited = (aCell.meType == CELLTYPE_FORMULA);
+ if (pRetTypeExpr && pRetIndexExpr)
+ pDok->GetNumberFormatInfo(mrContext, *pRetTypeExpr, *pRetIndexExpr, rAddress);
+ PushTempToken( new ScEmptyCellToken( bInherited, bDisplayEmptyAsString));
+ return;
+ }
+
+ FormulaError nErr = FormulaError::NONE;
+ if (aCell.meType == CELLTYPE_FORMULA)
+ nErr = aCell.mpFormula->GetErrCode();
+
+ if (nErr != FormulaError::NONE)
+ {
+ PushError( nErr);
+ if (pRetTypeExpr)
+ *pRetTypeExpr = SvNumFormatType::UNDEFINED;
+ if (pRetIndexExpr)
+ *pRetIndexExpr = 0;
+ }
+ else if (aCell.hasString())
+ {
+ svl::SharedString aRes;
+ GetCellString( aRes, aCell);
+ PushString( aRes);
+ if (pRetTypeExpr)
+ *pRetTypeExpr = SvNumFormatType::TEXT;
+ if (pRetIndexExpr)
+ *pRetIndexExpr = 0;
+ }
+ else
+ {
+ double fVal = GetCellValue(rAddress, aCell);
+ if (bFinalResult)
+ {
+ TreatDoubleError( fVal);
+ if (!IfErrorPushError())
+ PushTempTokenWithoutError( CreateFormulaDoubleToken( fVal));
+ }
+ else
+ {
+ PushDouble( fVal);
+ }
+ if (pRetTypeExpr)
+ *pRetTypeExpr = nCurFmtType;
+ if (pRetIndexExpr)
+ *pRetIndexExpr = nCurFmtIndex;
+ }
+}
+
+// Simply throw away TOS.
+
+void ScInterpreter::Pop()
+{
+ if( sp )
+ sp--;
+ else
+ SetError(FormulaError::UnknownStackVariable);
+}
+
+// Simply throw away TOS and set error code, used with ocIsError et al.
+
+void ScInterpreter::PopError()
+{
+ if( sp )
+ {
+ sp--;
+ if (pStack[sp]->GetType() == svError)
+ nGlobalError = pStack[sp]->GetError();
+ }
+ else
+ SetError(FormulaError::UnknownStackVariable);
+}
+
+FormulaConstTokenRef ScInterpreter::PopToken()
+{
+ if (sp)
+ {
+ sp--;
+ const FormulaToken* p = pStack[ sp ];
+ if (p->GetType() == svError)
+ nGlobalError = p->GetError();
+ return p;
+ }
+ else
+ SetError(FormulaError::UnknownStackVariable);
+ return nullptr;
+}
+
+double ScInterpreter::PopDouble()
+{
+ nCurFmtType = SvNumFormatType::NUMBER;
+ nCurFmtIndex = 0;
+ if( sp )
+ {
+ --sp;
+ const FormulaToken* p = pStack[ sp ];
+ switch (p->GetType())
+ {
+ case svError:
+ nGlobalError = p->GetError();
+ break;
+ case svDouble:
+ {
+ SvNumFormatType nType = static_cast<SvNumFormatType>(p->GetDoubleType());
+ if (nType != SvNumFormatType::ALL && nType != SvNumFormatType::UNDEFINED)
+ nCurFmtType = nType;
+ return p->GetDouble();
+ }
+ case svEmptyCell:
+ case svMissing:
+ return 0.0;
+ default:
+ SetError( FormulaError::IllegalArgument);
+ }
+ }
+ else
+ SetError( FormulaError::UnknownStackVariable);
+ return 0.0;
+}
+
+svl::SharedString ScInterpreter::PopString()
+{
+ nCurFmtType = SvNumFormatType::TEXT;
+ nCurFmtIndex = 0;
+ if( sp )
+ {
+ --sp;
+ const FormulaToken* p = pStack[ sp ];
+ switch (p->GetType())
+ {
+ case svError:
+ nGlobalError = p->GetError();
+ break;
+ case svString:
+ return p->GetString();
+ case svEmptyCell:
+ case svMissing:
+ return svl::SharedString::getEmptyString();
+ default:
+ SetError( FormulaError::IllegalArgument);
+ }
+ }
+ else
+ SetError( FormulaError::UnknownStackVariable);
+
+ return svl::SharedString::getEmptyString();
+}
+
+void ScInterpreter::ValidateRef( const ScSingleRefData & rRef )
+{
+ SCCOL nCol;
+ SCROW nRow;
+ SCTAB nTab;
+ SingleRefToVars( rRef, nCol, nRow, nTab);
+}
+
+void ScInterpreter::ValidateRef( const ScComplexRefData & rRef )
+{
+ ValidateRef( rRef.Ref1);
+ ValidateRef( rRef.Ref2);
+}
+
+void ScInterpreter::ValidateRef( const ScRefList & rRefList )
+{
+ for (const auto& rRef : rRefList)
+ {
+ ValidateRef( rRef);
+ }
+}
+
+void ScInterpreter::SingleRefToVars( const ScSingleRefData & rRef,
+ SCCOL & rCol, SCROW & rRow, SCTAB & rTab )
+{
+ if ( rRef.IsColRel() )
+ rCol = aPos.Col() + rRef.Col();
+ else
+ rCol = rRef.Col();
+
+ if ( rRef.IsRowRel() )
+ rRow = aPos.Row() + rRef.Row();
+ else
+ rRow = rRef.Row();
+
+ if ( rRef.IsTabRel() )
+ rTab = aPos.Tab() + rRef.Tab();
+ else
+ rTab = rRef.Tab();
+
+ if( !pDok->ValidCol( rCol) || rRef.IsColDeleted() )
+ {
+ SetError( FormulaError::NoRef );
+ rCol = 0;
+ }
+ if( !pDok->ValidRow( rRow) || rRef.IsRowDeleted() )
+ {
+ SetError( FormulaError::NoRef );
+ rRow = 0;
+ }
+ if( !ValidTab( rTab, pDok->GetTableCount() - 1) || rRef.IsTabDeleted() )
+ {
+ SetError( FormulaError::NoRef );
+ rTab = 0;
+ }
+}
+
+void ScInterpreter::PopSingleRef(SCCOL& rCol, SCROW &rRow, SCTAB& rTab)
+{
+ ScAddress aAddr(rCol, rRow, rTab);
+ PopSingleRef(aAddr);
+ rCol = aAddr.Col();
+ rRow = aAddr.Row();
+ rTab = aAddr.Tab();
+}
+
+void ScInterpreter::PopSingleRef( ScAddress& rAdr )
+{
+ if( sp )
+ {
+ --sp;
+ const FormulaToken* p = pStack[ sp ];
+ switch (p->GetType())
+ {
+ case svError:
+ nGlobalError = p->GetError();
+ break;
+ case svSingleRef:
+ {
+ const ScSingleRefData* pRefData = p->GetSingleRef();
+ if (pRefData->IsDeleted())
+ {
+ SetError( FormulaError::NoRef);
+ break;
+ }
+
+ SCCOL nCol;
+ SCROW nRow;
+ SCTAB nTab;
+ SingleRefToVars( *pRefData, nCol, nRow, nTab);
+ rAdr.Set( nCol, nRow, nTab );
+ if (!pDok->m_TableOpList.empty())
+ ReplaceCell( rAdr );
+ }
+ break;
+ default:
+ SetError( FormulaError::IllegalParameter);
+ }
+ }
+ else
+ SetError( FormulaError::UnknownStackVariable);
+}
+
+void ScInterpreter::DoubleRefToVars( const formula::FormulaToken* p,
+ SCCOL& rCol1, SCROW &rRow1, SCTAB& rTab1,
+ SCCOL& rCol2, SCROW &rRow2, SCTAB& rTab2 )
+{
+ const ScComplexRefData& rCRef = *p->GetDoubleRef();
+ SingleRefToVars( rCRef.Ref1, rCol1, rRow1, rTab1);
+ SingleRefToVars( rCRef.Ref2, rCol2, rRow2, rTab2);
+ if (rCol2 < rCol1)
+ std::swap( rCol2, rCol1);
+ if (rRow2 < rRow1)
+ std::swap( rRow2, rRow1);
+ if (rTab2 < rTab1)
+ std::swap( rTab2, rTab1);
+ if (!pDok->m_TableOpList.empty())
+ {
+ ScRange aRange( rCol1, rRow1, rTab1, rCol2, rRow2, rTab2 );
+ if ( IsTableOpInRange( aRange ) )
+ SetError( FormulaError::IllegalParameter );
+ }
+}
+
+ScDBRangeBase* ScInterpreter::PopDBDoubleRef()
+{
+ StackVar eType = GetStackType();
+ switch (eType)
+ {
+ case svUnknown:
+ SetError(FormulaError::UnknownStackVariable);
+ break;
+ case svError:
+ PopError();
+ break;
+ case svDoubleRef:
+ {
+ SCCOL nCol1, nCol2;
+ SCROW nRow1, nRow2;
+ SCTAB nTab1, nTab2;
+ PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
+ if (nGlobalError != FormulaError::NONE)
+ break;
+ return new ScDBInternalRange(pDok,
+ ScRange(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2));
+ }
+ case svMatrix:
+ case svExternalDoubleRef:
+ {
+ ScMatrixRef pMat;
+ if (eType == svMatrix)
+ pMat = PopMatrix();
+ else
+ PopExternalDoubleRef(pMat);
+ if (nGlobalError != FormulaError::NONE)
+ break;
+ return new ScDBExternalRange(pDok, pMat);
+ }
+ default:
+ SetError( FormulaError::IllegalParameter);
+ }
+
+ return nullptr;
+}
+
+void ScInterpreter::PopDoubleRef(SCCOL& rCol1, SCROW &rRow1, SCTAB& rTab1,
+ SCCOL& rCol2, SCROW &rRow2, SCTAB& rTab2)
+{
+ if( sp )
+ {
+ --sp;
+ const FormulaToken* p = pStack[ sp ];
+ switch (p->GetType())
+ {
+ case svError:
+ nGlobalError = p->GetError();
+ break;
+ case svDoubleRef:
+ DoubleRefToVars( p, rCol1, rRow1, rTab1, rCol2, rRow2, rTab2);
+ break;
+ default:
+ SetError( FormulaError::IllegalParameter);
+ }
+ }
+ else
+ SetError( FormulaError::UnknownStackVariable);
+}
+
+void ScInterpreter::DoubleRefToRange( const ScComplexRefData & rCRef,
+ ScRange & rRange, bool bDontCheckForTableOp )
+{
+ SCCOL nCol;
+ SCROW nRow;
+ SCTAB nTab;
+ SingleRefToVars( rCRef.Ref1, nCol, nRow, nTab);
+ rRange.aStart.Set( nCol, nRow, nTab );
+ SingleRefToVars( rCRef.Ref2, nCol, nRow, nTab);
+ rRange.aEnd.Set( nCol, nRow, nTab );
+ rRange.PutInOrder();
+ if (!pDok->m_TableOpList.empty() && !bDontCheckForTableOp)
+ {
+ if ( IsTableOpInRange( rRange ) )
+ SetError( FormulaError::IllegalParameter );
+ }
+}
+
+void ScInterpreter::PopDoubleRef( ScRange & rRange, short & rParam, size_t & rRefInList )
+{
+ if (sp)
+ {
+ const formula::FormulaToken* pToken = pStack[ sp-1 ];
+ switch (pToken->GetType())
+ {
+ case svError:
+ nGlobalError = pToken->GetError();
+ break;
+ case svDoubleRef:
+ {
+ --sp;
+ const ScComplexRefData* pRefData = pToken->GetDoubleRef();
+ if (pRefData->IsDeleted())
+ {
+ SetError( FormulaError::NoRef);
+ break;
+ }
+ DoubleRefToRange( *pRefData, rRange);
+ break;
+ }
+ case svRefList:
+ {
+ const ScRefList* pList = pToken->GetRefList();
+ if (rRefInList < pList->size())
+ {
+ DoubleRefToRange( (*pList)[rRefInList], rRange);
+ if (++rRefInList < pList->size())
+ ++rParam;
+ else
+ {
+ --sp;
+ rRefInList = 0;
+ }
+ }
+ else
+ {
+ --sp;
+ rRefInList = 0;
+ SetError( FormulaError::IllegalParameter);
+ }
+ }
+ break;
+ default:
+ SetError( FormulaError::IllegalParameter);
+ }
+ }
+ else
+ SetError( FormulaError::UnknownStackVariable);
+}
+
+void ScInterpreter::PopDoubleRef( ScRange& rRange, bool bDontCheckForTableOp )
+{
+ if( sp )
+ {
+ --sp;
+ const FormulaToken* p = pStack[ sp ];
+ switch (p->GetType())
+ {
+ case svError:
+ nGlobalError = p->GetError();
+ break;
+ case svDoubleRef:
+ DoubleRefToRange( *p->GetDoubleRef(), rRange, bDontCheckForTableOp);
+ break;
+ default:
+ SetError( FormulaError::IllegalParameter);
+ }
+ }
+ else
+ SetError( FormulaError::UnknownStackVariable);
+}
+
+void ScInterpreter::PopExternalSingleRef(sal_uInt16& rFileId, OUString& rTabName, ScSingleRefData& rRef)
+{
+ if (!sp)
+ {
+ SetError(FormulaError::UnknownStackVariable);
+ return;
+ }
+
+ --sp;
+ const FormulaToken* p = pStack[sp];
+ StackVar eType = p->GetType();
+
+ if (eType == svError)
+ {
+ nGlobalError = p->GetError();
+ return;
+ }
+
+ if (eType != svExternalSingleRef)
+ {
+ SetError( FormulaError::IllegalParameter);
+ return;
+ }
+
+ rFileId = p->GetIndex();
+ rTabName = p->GetString().getString();
+ rRef = *p->GetSingleRef();
+}
+
+void ScInterpreter::PopExternalSingleRef(ScExternalRefCache::TokenRef& rToken, ScExternalRefCache::CellFormat* pFmt)
+{
+ sal_uInt16 nFileId;
+ OUString aTabName;
+ ScSingleRefData aData;
+ PopExternalSingleRef(nFileId, aTabName, aData, rToken, pFmt);
+}
+
+void ScInterpreter::PopExternalSingleRef(
+ sal_uInt16& rFileId, OUString& rTabName, ScSingleRefData& rRef,
+ ScExternalRefCache::TokenRef& rToken, ScExternalRefCache::CellFormat* pFmt)
+{
+ PopExternalSingleRef(rFileId, rTabName, rRef);
+ if (nGlobalError != FormulaError::NONE)
+ return;
+
+ ScExternalRefManager* pRefMgr = pDok->GetExternalRefManager();
+ const OUString* pFile = pRefMgr->getExternalFileName(rFileId);
+ if (!pFile)
+ {
+ SetError(FormulaError::NoName);
+ return;
+ }
+
+ if (rRef.IsTabRel())
+ {
+ OSL_FAIL("ScCompiler::GetToken: external single reference must have an absolute table reference!");
+ SetError(FormulaError::NoRef);
+ return;
+ }
+
+ ScAddress aAddr = rRef.toAbs(pDok, aPos);
+ ScExternalRefCache::CellFormat aFmt;
+ ScExternalRefCache::TokenRef xNew = pRefMgr->getSingleRefToken(
+ rFileId, rTabName, aAddr, &aPos, nullptr, &aFmt);
+
+ if (!xNew)
+ {
+ SetError(FormulaError::NoRef);
+ return;
+ }
+
+ if (xNew->GetType() == svError)
+ SetError( xNew->GetError());
+
+ rToken = xNew;
+ if (pFmt)
+ *pFmt = aFmt;
+}
+
+void ScInterpreter::PopExternalDoubleRef(sal_uInt16& rFileId, OUString& rTabName, ScComplexRefData& rRef)
+{
+ if (!sp)
+ {
+ SetError(FormulaError::UnknownStackVariable);
+ return;
+ }
+
+ --sp;
+ const FormulaToken* p = pStack[sp];
+ StackVar eType = p->GetType();
+
+ if (eType == svError)
+ {
+ nGlobalError = p->GetError();
+ return;
+ }
+
+ if (eType != svExternalDoubleRef)
+ {
+ SetError( FormulaError::IllegalParameter);
+ return;
+ }
+
+ rFileId = p->GetIndex();
+ rTabName = p->GetString().getString();
+ rRef = *p->GetDoubleRef();
+}
+
+void ScInterpreter::PopExternalDoubleRef(ScExternalRefCache::TokenArrayRef& rArray)
+{
+ sal_uInt16 nFileId;
+ OUString aTabName;
+ ScComplexRefData aData;
+ PopExternalDoubleRef(nFileId, aTabName, aData);
+ if (nGlobalError != FormulaError::NONE)
+ return;
+
+ GetExternalDoubleRef(nFileId, aTabName, aData, rArray);
+ if (nGlobalError != FormulaError::NONE)
+ return;
+}
+
+void ScInterpreter::PopExternalDoubleRef(ScMatrixRef& rMat)
+{
+ ScExternalRefCache::TokenArrayRef pArray;
+ PopExternalDoubleRef(pArray);
+ if (nGlobalError != FormulaError::NONE)
+ return;
+
+ // For now, we only support single range data for external
+ // references, which means the array should only contain a
+ // single matrix token.
+ formula::FormulaToken* p = pArray->FirstToken();
+ if (!p || p->GetType() != svMatrix)
+ SetError( FormulaError::IllegalParameter);
+ else
+ {
+ rMat = p->GetMatrix();
+ if (!rMat)
+ SetError( FormulaError::UnknownVariable);
+ }
+}
+
+void ScInterpreter::GetExternalDoubleRef(
+ sal_uInt16 nFileId, const OUString& rTabName, const ScComplexRefData& rData, ScExternalRefCache::TokenArrayRef& rArray)
+{
+ ScExternalRefManager* pRefMgr = pDok->GetExternalRefManager();
+ const OUString* pFile = pRefMgr->getExternalFileName(nFileId);
+ if (!pFile)
+ {
+ SetError(FormulaError::NoName);
+ return;
+ }
+ if (rData.Ref1.IsTabRel() || rData.Ref2.IsTabRel())
+ {
+ OSL_FAIL("ScCompiler::GetToken: external double reference must have an absolute table reference!");
+ SetError(FormulaError::NoRef);
+ return;
+ }
+
+ ScComplexRefData aData(rData);
+ ScRange aRange = aData.toAbs(pDok, aPos);
+ if (!pDok->ValidColRow(aRange.aStart.Col(), aRange.aStart.Row()) || !pDok->ValidColRow(aRange.aEnd.Col(), aRange.aEnd.Row()))
+ {
+ SetError(FormulaError::NoRef);
+ return;
+ }
+
+ ScExternalRefCache::TokenArrayRef pArray = pRefMgr->getDoubleRefTokens(
+ nFileId, rTabName, aRange, &aPos);
+
+ if (!pArray)
+ {
+ SetError(FormulaError::IllegalArgument);
+ return;
+ }
+
+ formula::FormulaTokenArrayPlainIterator aIter(*pArray);
+ formula::FormulaToken* pToken = aIter.First();
+ assert(pToken);
+ if (pToken->GetType() == svError)
+ {
+ SetError( pToken->GetError());
+ return;
+ }
+ if (pToken->GetType() != svMatrix)
+ {
+ SetError(FormulaError::IllegalArgument);
+ return;
+ }
+
+ if (aIter.Next())
+ {
+ // Can't handle more than one matrix per parameter.
+ SetError( FormulaError::IllegalArgument);
+ return;
+ }
+
+ rArray = pArray;
+}
+
+bool ScInterpreter::PopDoubleRefOrSingleRef( ScAddress& rAdr )
+{
+ switch ( GetStackType() )
+ {
+ case svDoubleRef :
+ {
+ ScRange aRange;
+ PopDoubleRef( aRange, true );
+ return DoubleRefToPosSingleRef( aRange, rAdr );
+ }
+ case svSingleRef :
+ {
+ PopSingleRef( rAdr );
+ return true;
+ }
+ default:
+ PopError();
+ SetError( FormulaError::NoRef );
+ }
+ return false;
+}
+
+void ScInterpreter::PopDoubleRefPushMatrix()
+{
+ if ( GetStackType() == svDoubleRef )
+ {
+ ScMatrixRef pMat = GetMatrix();
+ if ( pMat )
+ PushMatrix( pMat );
+ else
+ PushIllegalParameter();
+ }
+ else
+ SetError( FormulaError::NoRef );
+}
+
+void ScInterpreter::PopRefListPushMatrixOrRef()
+{
+ if ( GetStackType() == svRefList )
+ {
+ FormulaConstTokenRef xTok = pStack[sp-1];
+ const std::vector<ScComplexRefData>* pv = xTok->GetRefList();
+ if (pv)
+ {
+ const size_t nEntries = pv->size();
+ if (nEntries == 1)
+ {
+ --sp;
+ PushTempTokenWithoutError( new ScDoubleRefToken( pDok->GetSheetLimits(), (*pv)[0] ));
+ }
+ else if (bMatrixFormula)
+ {
+ // Only single cells can be stuffed into a column vector.
+ // XXX NOTE: Excel doesn't do this but returns #VALUE! instead.
+ // Though there's no compelling reason not to...
+ for (const auto & rRef : *pv)
+ {
+ if (rRef.Ref1 != rRef.Ref2)
+ return;
+ }
+ ScMatrixRef xMat = GetNewMat( 1, nEntries, true); // init empty
+ if (!xMat)
+ return;
+ for (size_t i=0; i < nEntries; ++i)
+ {
+ SCCOL nCol; SCROW nRow; SCTAB nTab;
+ SingleRefToVars( (*pv)[i].Ref1, nCol, nRow, nTab);
+ if (nGlobalError == FormulaError::NONE)
+ {
+ ScAddress aAdr( nCol, nRow, nTab);
+ ScRefCellValue aCell( *pDok, aAdr);
+ if (aCell.hasError())
+ xMat->PutError( aCell.mpFormula->GetErrCode(), 0, i);
+ else if (aCell.hasEmptyValue())
+ xMat->PutEmpty( 0, i);
+ else if (aCell.hasString())
+ xMat->PutString( mrStrPool.intern( aCell.getString( pDok)), 0, i);
+ else
+ xMat->PutDouble( aCell.getValue(), 0, i);
+ }
+ else
+ {
+ xMat->PutError( nGlobalError, 0, i);
+ nGlobalError = FormulaError::NONE;
+ }
+ }
+ --sp;
+ PushMatrix( xMat);
+ }
+ }
+ // else: keep token on stack, something will handle the error
+ }
+ else
+ SetError( FormulaError::NoRef );
+}
+
+void ScInterpreter::ConvertMatrixJumpConditionToMatrix()
+{
+ StackVar eStackType = GetStackType();
+ if (eStackType == svUnknown)
+ return; // can't do anything, some caller will catch that
+ if (eStackType == svMatrix)
+ return; // already matrix, nothing to do
+
+ if (eStackType != svDoubleRef && GetStackType(2) != svJumpMatrix)
+ return; // always convert svDoubleRef, others only in JumpMatrix context
+
+ GetTokenMatrixMap(); // make sure it exists, create if not.
+ ScMatrixRef pMat = GetMatrix();
+ if ( pMat )
+ PushMatrix( pMat );
+ else
+ PushIllegalParameter();
+}
+
+std::unique_ptr<ScTokenMatrixMap> ScInterpreter::CreateTokenMatrixMap()
+{
+ return std::make_unique<ScTokenMatrixMap>();
+}
+
+bool ScInterpreter::ConvertMatrixParameters()
+{
+ sal_uInt16 nParams = pCur->GetParamCount();
+ OSL_ENSURE( nParams <= sp, "ConvertMatrixParameters: stack/param count mismatch");
+ SCSIZE nJumpCols = 0, nJumpRows = 0;
+ for ( sal_uInt16 i=1; i <= nParams && i <= sp; ++i )
+ {
+ const FormulaToken* p = pStack[ sp - i ];
+ if ( p->GetOpCode() != ocPush && p->GetOpCode() != ocMissing)
+ {
+ OSL_FAIL( "ConvertMatrixParameters: not a push");
+ }
+ else
+ {
+ switch ( p->GetType() )
+ {
+ case svDouble:
+ case svString:
+ case svSingleRef:
+ case svExternalSingleRef:
+ case svMissing:
+ case svError:
+ case svEmptyCell:
+ // nothing to do
+ break;
+ case svMatrix:
+ {
+ if ( ScParameterClassification::GetParameterType( pCur, nParams - i)
+ == formula::ParamClass::Value )
+ { // only if single value expected
+ ScConstMatrixRef pMat = p->GetMatrix();
+ if ( !pMat )
+ SetError( FormulaError::UnknownVariable);
+ else
+ {
+ SCSIZE nCols, nRows;
+ pMat->GetDimensions( nCols, nRows);
+ if ( nJumpCols < nCols )
+ nJumpCols = nCols;
+ if ( nJumpRows < nRows )
+ nJumpRows = nRows;
+ }
+ }
+ }
+ break;
+ case svDoubleRef:
+ {
+ formula::ParamClass eType = ScParameterClassification::GetParameterType( pCur, nParams - i);
+ if ( eType != formula::ParamClass::Reference &&
+ eType != formula::ParamClass::ReferenceOrRefArray &&
+ eType != formula::ParamClass::ReferenceOrForceArray &&
+ // For scalar Value: convert to Array/JumpMatrix
+ // only if in array formula context, else (function
+ // has ForceArray or ReferenceOrForceArray
+ // parameter *somewhere else*) pick a normal
+ // position dependent implicit intersection later.
+ (eType != formula::ParamClass::Value || IsInArrayContext()))
+ {
+ SCCOL nCol1, nCol2;
+ SCROW nRow1, nRow2;
+ SCTAB nTab1, nTab2;
+ DoubleRefToVars( p, nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
+ // Make sure the map exists, created if not.
+ GetTokenMatrixMap();
+ ScMatrixRef pMat = CreateMatrixFromDoubleRef( p,
+ nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
+ if (pMat)
+ {
+ if ( eType == formula::ParamClass::Value )
+ { // only if single value expected
+ if ( nJumpCols < o3tl::make_unsigned(nCol2 - nCol1 + 1) )
+ nJumpCols = static_cast<SCSIZE>(nCol2 - nCol1 + 1);
+ if ( nJumpRows < o3tl::make_unsigned(nRow2 - nRow1 + 1) )
+ nJumpRows = static_cast<SCSIZE>(nRow2 - nRow1 + 1);
+ }
+ formula::FormulaToken* pNew = new ScMatrixToken( pMat);
+ pNew->IncRef();
+ pStack[ sp - i ] = pNew;
+ p->DecRef(); // p may be dead now!
+ }
+ }
+ }
+ break;
+ case svExternalDoubleRef:
+ {
+ formula::ParamClass eType = ScParameterClassification::GetParameterType( pCur, nParams - i);
+ if (eType == formula::ParamClass::Value || eType == formula::ParamClass::Array)
+ {
+ sal_uInt16 nFileId = p->GetIndex();
+ OUString aTabName = p->GetString().getString();
+ const ScComplexRefData& rRef = *p->GetDoubleRef();
+ ScExternalRefCache::TokenArrayRef pArray;
+ GetExternalDoubleRef(nFileId, aTabName, rRef, pArray);
+ if (nGlobalError != FormulaError::NONE || !pArray)
+ break;
+ formula::FormulaToken* pTemp = pArray->FirstToken();
+ if (!pTemp)
+ break;
+
+ ScMatrixRef pMat = pTemp->GetMatrix();
+ if (pMat)
+ {
+ if (eType == formula::ParamClass::Value)
+ { // only if single value expected
+ SCSIZE nC, nR;
+ pMat->GetDimensions( nC, nR);
+ if (nJumpCols < nC)
+ nJumpCols = nC;
+ if (nJumpRows < nR)
+ nJumpRows = nR;
+ }
+ formula::FormulaToken* pNew = new ScMatrixToken( pMat);
+ pNew->IncRef();
+ pStack[ sp - i ] = pNew;
+ p->DecRef(); // p may be dead now!
+ }
+ }
+ }
+ break;
+ case svRefList:
+ {
+ formula::ParamClass eType = ScParameterClassification::GetParameterType( pCur, nParams - i);
+ if ( eType != formula::ParamClass::Reference &&
+ eType != formula::ParamClass::ReferenceOrRefArray &&
+ eType != formula::ParamClass::ReferenceOrForceArray &&
+ eType != formula::ParamClass::ForceArray)
+ {
+ // can't convert to matrix
+ SetError( FormulaError::NoValue);
+ }
+ // else: the consuming function has to decide if and how to
+ // handle a reference list argument in array context.
+ }
+ break;
+ default:
+ OSL_FAIL( "ConvertMatrixParameters: unknown parameter type");
+ }
+ }
+ }
+ if( nJumpCols && nJumpRows )
+ {
+ short nPC = aCode.GetPC();
+ short nStart = nPC - 1; // restart on current code (-1)
+ short nNext = nPC; // next instruction after subroutine
+ short nStop = nPC + 1; // stop subroutine before reaching that
+ FormulaConstTokenRef xNew;
+ ScTokenMatrixMap::const_iterator aMapIter;
+ if (pTokenMatrixMap && ((aMapIter = pTokenMatrixMap->find( pCur)) != pTokenMatrixMap->end()))
+ xNew = (*aMapIter).second;
+ else
+ {
+ auto pJumpMat = std::make_shared<ScJumpMatrix>( pCur->GetOpCode(), nJumpCols, nJumpRows);
+ pJumpMat->SetAllJumps( 1.0, nStart, nNext, nStop);
+ // pop parameters and store in ScJumpMatrix, push in JumpMatrix()
+ ScTokenVec aParams(nParams);
+ for ( sal_uInt16 i=1; i <= nParams && sp > 0; ++i )
+ {
+ const FormulaToken* p = pStack[ --sp ];
+ p->IncRef();
+ // store in reverse order such that a push may simply iterate
+ aParams[ nParams - i ] = p;
+ }
+ pJumpMat->SetJumpParameters( std::move(aParams) );
+ xNew = new ScJumpMatrixToken( std::move(pJumpMat) );
+ GetTokenMatrixMap().emplace(pCur, xNew);
+ }
+ PushTempTokenWithoutError( xNew.get());
+ // set continuation point of path for main code line
+ aCode.Jump( nNext, nNext);
+ return true;
+ }
+ return false;
+}
+
+ScMatrixRef ScInterpreter::PopMatrix()
+{
+ if( sp )
+ {
+ --sp;
+ const FormulaToken* p = pStack[ sp ];
+ switch (p->GetType())
+ {
+ case svError:
+ nGlobalError = p->GetError();
+ break;
+ case svMatrix:
+ {
+ // ScMatrix itself maintains an im/mutable flag that should
+ // be obeyed where necessary... so we can return ScMatrixRef
+ // here instead of ScConstMatrixRef.
+ ScMatrix* pMat = const_cast<FormulaToken*>(p)->GetMatrix();
+ if ( pMat )
+ pMat->SetErrorInterpreter( this);
+ else
+ SetError( FormulaError::UnknownVariable);
+ return pMat;
+ }
+ default:
+ SetError( FormulaError::IllegalParameter);
+ }
+ }
+ else
+ SetError( FormulaError::UnknownStackVariable);
+ return nullptr;
+}
+
+sc::RangeMatrix ScInterpreter::PopRangeMatrix()
+{
+ sc::RangeMatrix aRet;
+ if (sp)
+ {
+ switch (pStack[sp-1]->GetType())
+ {
+ case svMatrix:
+ {
+ --sp;
+ const FormulaToken* p = pStack[sp];
+ aRet.mpMat = const_cast<FormulaToken*>(p)->GetMatrix();
+ if (aRet.mpMat)
+ {
+ aRet.mpMat->SetErrorInterpreter(this);
+ if (p->GetByte() == MATRIX_TOKEN_HAS_RANGE)
+ {
+ const ScComplexRefData& rRef = *p->GetDoubleRef();
+ if (!rRef.Ref1.IsColRel() && !rRef.Ref1.IsRowRel() && !rRef.Ref2.IsColRel() && !rRef.Ref2.IsRowRel())
+ {
+ aRet.mnCol1 = rRef.Ref1.Col();
+ aRet.mnRow1 = rRef.Ref1.Row();
+ aRet.mnTab1 = rRef.Ref1.Tab();
+ aRet.mnCol2 = rRef.Ref2.Col();
+ aRet.mnRow2 = rRef.Ref2.Row();
+ aRet.mnTab2 = rRef.Ref2.Tab();
+ }
+ }
+ }
+ else
+ SetError( FormulaError::UnknownVariable);
+ }
+ break;
+ default:
+ aRet.mpMat = PopMatrix();
+ }
+ }
+ return aRet;
+}
+
+void ScInterpreter::QueryMatrixType(const ScMatrixRef& xMat, SvNumFormatType& rRetTypeExpr, sal_uInt32& rRetIndexExpr)
+{
+ if (xMat)
+ {
+ SCSIZE nCols, nRows;
+ xMat->GetDimensions(nCols, nRows);
+ ScMatrixValue nMatVal = xMat->Get(0, 0);
+ ScMatValType nMatValType = nMatVal.nType;
+ if (ScMatrix::IsNonValueType( nMatValType))
+ {
+ if ( xMat->IsEmptyPath( 0, 0))
+ { // result of empty FALSE jump path
+ FormulaTokenRef xRes = CreateFormulaDoubleToken( 0.0);
+ PushTempToken( new ScMatrixFormulaCellToken(nCols, nRows, xMat, xRes.get()));
+ rRetTypeExpr = SvNumFormatType::LOGICAL;
+ }
+ else if ( xMat->IsEmptyResult( 0, 0))
+ { // empty formula result
+ FormulaTokenRef xRes = new ScEmptyCellToken( true, true); // inherited, display empty
+ PushTempToken( new ScMatrixFormulaCellToken(nCols, nRows, xMat, xRes.get()));
+ }
+ else if ( xMat->IsEmpty( 0, 0))
+ { // empty or empty cell
+ FormulaTokenRef xRes = new ScEmptyCellToken( false, true); // not inherited, display empty
+ PushTempToken( new ScMatrixFormulaCellToken(nCols, nRows, xMat, xRes.get()));
+ }
+ else
+ {
+ FormulaTokenRef xRes = new FormulaStringToken( nMatVal.GetString() );
+ PushTempToken( new ScMatrixFormulaCellToken(nCols, nRows, xMat, xRes.get()));
+ rRetTypeExpr = SvNumFormatType::TEXT;
+ }
+ }
+ else
+ {
+ FormulaError nErr = GetDoubleErrorValue( nMatVal.fVal);
+ FormulaTokenRef xRes;
+ if (nErr != FormulaError::NONE)
+ xRes = new FormulaErrorToken( nErr);
+ else
+ xRes = CreateFormulaDoubleToken( nMatVal.fVal);
+ PushTempToken( new ScMatrixFormulaCellToken(nCols, nRows, xMat, xRes.get()));
+ if ( rRetTypeExpr != SvNumFormatType::LOGICAL )
+ rRetTypeExpr = SvNumFormatType::NUMBER;
+ }
+ rRetIndexExpr = 0;
+ xMat->SetErrorInterpreter( nullptr);
+ }
+ else
+ SetError( FormulaError::UnknownStackVariable);
+}
+
+formula::FormulaToken* ScInterpreter::CreateFormulaDoubleToken( double fVal, SvNumFormatType nFmt )
+{
+ assert( mrContext.maTokens.size() == TOKEN_CACHE_SIZE );
+
+ // Find a spare token
+ for ( auto p : mrContext.maTokens )
+ {
+ if (p && p->GetRef() == 1)
+ {
+ p->GetDoubleAsReference() = fVal;
+ p->SetDoubleType( static_cast<sal_Int16>(nFmt) );
+ return p;
+ }
+ }
+
+ // Allocate a new token
+ auto p = new FormulaTypedDoubleToken( fVal, static_cast<sal_Int16>(nFmt) );
+ if ( mrContext.maTokens[mrContext.mnTokenCachePos] )
+ mrContext.maTokens[mrContext.mnTokenCachePos]->DecRef();
+ mrContext.maTokens[mrContext.mnTokenCachePos] = p;
+ p->IncRef();
+ mrContext.mnTokenCachePos = (mrContext.mnTokenCachePos + 1) % TOKEN_CACHE_SIZE;
+ return p;
+}
+
+formula::FormulaToken* ScInterpreter::CreateDoubleOrTypedToken( double fVal )
+{
+ // NumberFormat::NUMBER is the default untyped double.
+ if (nFuncFmtType != SvNumFormatType::ALL && nFuncFmtType != SvNumFormatType::NUMBER &&
+ nFuncFmtType != SvNumFormatType::UNDEFINED)
+ return CreateFormulaDoubleToken( fVal, nFuncFmtType);
+ else
+ return CreateFormulaDoubleToken( fVal);
+}
+
+void ScInterpreter::PushDouble(double nVal)
+{
+ TreatDoubleError( nVal );
+ if (!IfErrorPushError())
+ PushTempTokenWithoutError( CreateDoubleOrTypedToken( nVal));
+}
+
+void ScInterpreter::PushInt(int nVal)
+{
+ if (!IfErrorPushError())
+ PushTempTokenWithoutError( CreateDoubleOrTypedToken( nVal));
+}
+
+void ScInterpreter::PushStringBuffer( const sal_Unicode* pString )
+{
+ if ( pString )
+ {
+ svl::SharedString aSS = pDok->GetSharedStringPool().intern(OUString(pString));
+ PushString(aSS);
+ }
+ else
+ PushString(svl::SharedString::getEmptyString());
+}
+
+void ScInterpreter::PushString( const OUString& rStr )
+{
+ PushString(pDok->GetSharedStringPool().intern(rStr));
+}
+
+void ScInterpreter::PushString( const svl::SharedString& rString )
+{
+ if (!IfErrorPushError())
+ PushTempTokenWithoutError( new FormulaStringToken( rString ) );
+}
+
+void ScInterpreter::PushSingleRef(SCCOL nCol, SCROW nRow, SCTAB nTab)
+{
+ if (!IfErrorPushError())
+ {
+ ScSingleRefData aRef;
+ aRef.InitAddress(ScAddress(nCol,nRow,nTab));
+ PushTempTokenWithoutError( new ScSingleRefToken( pDok->GetSheetLimits(), aRef ) );
+ }
+}
+
+void ScInterpreter::PushDoubleRef(SCCOL nCol1, SCROW nRow1, SCTAB nTab1,
+ SCCOL nCol2, SCROW nRow2, SCTAB nTab2)
+{
+ if (!IfErrorPushError())
+ {
+ ScComplexRefData aRef;
+ aRef.InitRange(ScRange(nCol1,nRow1,nTab1,nCol2,nRow2,nTab2));
+ PushTempTokenWithoutError( new ScDoubleRefToken( pDok->GetSheetLimits(), aRef ) );
+ }
+}
+
+void ScInterpreter::PushExternalSingleRef(
+ sal_uInt16 nFileId, const OUString& rTabName, SCCOL nCol, SCROW nRow, SCTAB nTab)
+{
+ if (!IfErrorPushError())
+ {
+ ScSingleRefData aRef;
+ aRef.InitAddress(ScAddress(nCol,nRow,nTab));
+ PushTempTokenWithoutError( new ScExternalSingleRefToken(nFileId,
+ pDok->GetSharedStringPool().intern( rTabName), aRef)) ;
+ }
+}
+
+void ScInterpreter::PushExternalDoubleRef(
+ sal_uInt16 nFileId, const OUString& rTabName,
+ SCCOL nCol1, SCROW nRow1, SCTAB nTab1, SCCOL nCol2, SCROW nRow2, SCTAB nTab2)
+{
+ if (!IfErrorPushError())
+ {
+ ScComplexRefData aRef;
+ aRef.InitRange(ScRange(nCol1,nRow1,nTab1,nCol2,nRow2,nTab2));
+ PushTempTokenWithoutError( new ScExternalDoubleRefToken(nFileId,
+ pDok->GetSharedStringPool().intern( rTabName), aRef) );
+ }
+}
+
+void ScInterpreter::PushSingleRef( const ScRefAddress& rRef )
+{
+ if (!IfErrorPushError())
+ {
+ ScSingleRefData aRef;
+ aRef.InitFromRefAddress( pDok, rRef, aPos);
+ PushTempTokenWithoutError( new ScSingleRefToken( pDok->GetSheetLimits(), aRef ) );
+ }
+}
+
+void ScInterpreter::PushDoubleRef( const ScRefAddress& rRef1, const ScRefAddress& rRef2 )
+{
+ if (!IfErrorPushError())
+ {
+ ScComplexRefData aRef;
+ aRef.InitFromRefAddresses( pDok, rRef1, rRef2, aPos);
+ PushTempTokenWithoutError( new ScDoubleRefToken( pDok->GetSheetLimits(), aRef ) );
+ }
+}
+
+void ScInterpreter::PushMatrix( const sc::RangeMatrix& rMat )
+{
+ if (!rMat.isRangeValid())
+ {
+ // Just push the matrix part only.
+ PushMatrix(rMat.mpMat);
+ return;
+ }
+
+ rMat.mpMat->SetErrorInterpreter(nullptr);
+ nGlobalError = FormulaError::NONE;
+ PushTempTokenWithoutError(new ScMatrixRangeToken(rMat));
+}
+
+void ScInterpreter::PushMatrix(const ScMatrixRef& pMat)
+{
+ pMat->SetErrorInterpreter( nullptr);
+ // No if (!IfErrorPushError()) because ScMatrix stores errors itself,
+ // but with notifying ScInterpreter via nGlobalError, substituting it would
+ // mean to inherit the error on all array elements in all following
+ // operations.
+ nGlobalError = FormulaError::NONE;
+ PushTempTokenWithoutError( new ScMatrixToken( pMat ) );
+}
+
+void ScInterpreter::PushError( FormulaError nError )
+{
+ SetError( nError ); // only sets error if not already set
+ PushTempTokenWithoutError( new FormulaErrorToken( nGlobalError));
+}
+
+void ScInterpreter::PushParameterExpected()
+{
+ PushError( FormulaError::ParameterExpected);
+}
+
+void ScInterpreter::PushIllegalParameter()
+{
+ PushError( FormulaError::IllegalParameter);
+}
+
+void ScInterpreter::PushIllegalArgument()
+{
+ PushError( FormulaError::IllegalArgument);
+}
+
+void ScInterpreter::PushNA()
+{
+ PushError( FormulaError::NotAvailable);
+}
+
+void ScInterpreter::PushNoValue()
+{
+ PushError( FormulaError::NoValue);
+}
+
+bool ScInterpreter::IsMissing() const
+{
+ return sp && pStack[sp - 1]->GetType() == svMissing;
+}
+
+StackVar ScInterpreter::GetRawStackType()
+{
+ StackVar eRes;
+ if( sp )
+ {
+ eRes = pStack[sp - 1]->GetType();
+ }
+ else
+ {
+ SetError(FormulaError::UnknownStackVariable);
+ eRes = svUnknown;
+ }
+ return eRes;
+}
+
+StackVar ScInterpreter::GetStackType()
+{
+ StackVar eRes;
+ if( sp )
+ {
+ eRes = pStack[sp - 1]->GetType();
+ if( eRes == svMissing || eRes == svEmptyCell )
+ eRes = svDouble; // default!
+ }
+ else
+ {
+ SetError(FormulaError::UnknownStackVariable);
+ eRes = svUnknown;
+ }
+ return eRes;
+}
+
+StackVar ScInterpreter::GetStackType( sal_uInt8 nParam )
+{
+ StackVar eRes;
+ if( sp > nParam-1 )
+ {
+ eRes = pStack[sp - nParam]->GetType();
+ if( eRes == svMissing || eRes == svEmptyCell )
+ eRes = svDouble; // default!
+ }
+ else
+ eRes = svUnknown;
+ return eRes;
+}
+
+void ScInterpreter::ReverseStack( sal_uInt8 nParamCount )
+{
+ //reverse order of parameter stack
+ assert( sp >= nParamCount && " less stack elements than parameters");
+ sal_uInt16 nStackParams = std::min<sal_uInt16>( sp, nParamCount);
+ std::reverse( pStack+(sp-nStackParams), pStack+sp );
+}
+
+bool ScInterpreter::DoubleRefToPosSingleRef( const ScRange& rRange, ScAddress& rAdr )
+{
+ // Check for a singleton first - no implicit intersection for them.
+ if( rRange.aStart == rRange.aEnd )
+ {
+ rAdr = rRange.aStart;
+ return true;
+ }
+
+ bool bOk = false;
+
+ if ( pJumpMatrix )
+ {
+ bOk = rRange.aStart.Tab() == rRange.aEnd.Tab();
+ if ( !bOk )
+ SetError( FormulaError::IllegalArgument);
+ else
+ {
+ SCSIZE nC, nR;
+ pJumpMatrix->GetPos( nC, nR);
+ rAdr.SetCol( sal::static_int_cast<SCCOL>( rRange.aStart.Col() + nC ) );
+ rAdr.SetRow( sal::static_int_cast<SCROW>( rRange.aStart.Row() + nR ) );
+ rAdr.SetTab( rRange.aStart.Tab());
+ bOk = rRange.aStart.Col() <= rAdr.Col() && rAdr.Col() <=
+ rRange.aEnd.Col() && rRange.aStart.Row() <= rAdr.Row() &&
+ rAdr.Row() <= rRange.aEnd.Row();
+ if ( !bOk )
+ SetError( FormulaError::NoValue);
+ }
+ return bOk;
+ }
+
+ bOk = ScCompiler::DoubleRefToPosSingleRefScalarCase(rRange, rAdr, aPos);
+
+ if ( !bOk )
+ SetError( FormulaError::NoValue );
+ return bOk;
+}
+
+double ScInterpreter::GetDoubleFromMatrix(const ScMatrixRef& pMat)
+{
+ if (!pMat)
+ return 0.0;
+
+ if ( !pJumpMatrix )
+ {
+ double fVal = pMat->GetDoubleWithStringConversion( 0, 0);
+ FormulaError nErr = GetDoubleErrorValue( fVal);
+ if (nErr != FormulaError::NONE)
+ {
+ // Do not propagate the coded double error, but set nGlobalError in
+ // case the matrix did not have an error interpreter set.
+ SetError( nErr);
+ fVal = 0.0;
+ }
+ return fVal;
+ }
+
+ SCSIZE nCols, nRows, nC, nR;
+ pMat->GetDimensions( nCols, nRows);
+ pJumpMatrix->GetPos( nC, nR);
+ // Use vector replication for single row/column arrays.
+ if ( (nC < nCols || nCols == 1) && (nR < nRows || nRows == 1) )
+ {
+ double fVal = pMat->GetDoubleWithStringConversion( nC, nR);
+ FormulaError nErr = GetDoubleErrorValue( fVal);
+ if (nErr != FormulaError::NONE)
+ {
+ // Do not propagate the coded double error, but set nGlobalError in
+ // case the matrix did not have an error interpreter set.
+ SetError( nErr);
+ fVal = 0.0;
+ }
+ return fVal;
+ }
+
+ SetError( FormulaError::NoValue);
+ return 0.0;
+}
+
+double ScInterpreter::GetDouble()
+{
+ double nVal(0.0);
+ switch( GetRawStackType() )
+ {
+ case svDouble:
+ nVal = PopDouble();
+ break;
+ case svString:
+ nVal = ConvertStringToValue( PopString().getString());
+ break;
+ case svSingleRef:
+ {
+ ScAddress aAdr;
+ PopSingleRef( aAdr );
+ ScRefCellValue aCell(*pDok, aAdr);
+ nVal = GetCellValue(aAdr, aCell);
+ }
+ break;
+ case svDoubleRef:
+ { // generate position dependent SingleRef
+ ScRange aRange;
+ PopDoubleRef( aRange );
+ ScAddress aAdr;
+ if ( nGlobalError == FormulaError::NONE && DoubleRefToPosSingleRef( aRange, aAdr ) )
+ {
+ ScRefCellValue aCell(*pDok, aAdr);
+ nVal = GetCellValue(aAdr, aCell);
+ }
+ else
+ nVal = 0.0;
+ }
+ break;
+ case svExternalSingleRef:
+ {
+ ScExternalRefCache::TokenRef pToken;
+ PopExternalSingleRef(pToken);
+ if (nGlobalError == FormulaError::NONE)
+ {
+ if (pToken->GetType() == svDouble || pToken->GetType() == svEmptyCell)
+ nVal = pToken->GetDouble();
+ else
+ nVal = ConvertStringToValue( pToken->GetString().getString());
+ }
+ }
+ break;
+ case svExternalDoubleRef:
+ {
+ ScMatrixRef pMat;
+ PopExternalDoubleRef(pMat);
+ if (nGlobalError != FormulaError::NONE)
+ break;
+
+ nVal = GetDoubleFromMatrix(pMat);
+ }
+ break;
+ case svMatrix:
+ {
+ ScMatrixRef pMat = PopMatrix();
+ nVal = GetDoubleFromMatrix(pMat);
+ }
+ break;
+ case svError:
+ PopError();
+ nVal = 0.0;
+ break;
+ case svEmptyCell:
+ case svMissing:
+ Pop();
+ nVal = 0.0;
+ break;
+ default:
+ PopError();
+ SetError( FormulaError::IllegalParameter);
+ nVal = 0.0;
+ }
+ if ( nFuncFmtType == nCurFmtType )
+ nFuncFmtIndex = nCurFmtIndex;
+ return nVal;
+}
+
+double ScInterpreter::GetDoubleWithDefault(double nDefault)
+{
+ bool bMissing = IsMissing();
+ double nResultVal = GetDouble();
+ if ( bMissing )
+ nResultVal = nDefault;
+ return nResultVal;
+}
+
+sal_Int32 ScInterpreter::double_to_int32(double fVal)
+{
+ if (!std::isfinite(fVal))
+ {
+ SetError( GetDoubleErrorValue( fVal));
+ return SAL_MAX_INT32;
+ }
+ if (fVal > 0.0)
+ {
+ fVal = rtl::math::approxFloor( fVal);
+ if (fVal > SAL_MAX_INT32)
+ {
+ SetError( FormulaError::IllegalArgument);
+ return SAL_MAX_INT32;
+ }
+ }
+ else if (fVal < 0.0)
+ {
+ fVal = rtl::math::approxCeil( fVal);
+ if (fVal < SAL_MIN_INT32)
+ {
+ SetError( FormulaError::IllegalArgument);
+ return SAL_MAX_INT32;
+ }
+ }
+ return static_cast<sal_Int32>(fVal);
+}
+
+sal_Int32 ScInterpreter::GetInt32()
+{
+ return double_to_int32(GetDouble());
+}
+
+sal_Int32 ScInterpreter::GetInt32WithDefault( sal_Int32 nDefault )
+{
+ bool bMissing = IsMissing();
+ double fVal = GetDouble();
+ if ( bMissing )
+ return nDefault;
+ return double_to_int32(fVal);
+}
+
+sal_Int16 ScInterpreter::GetInt16()
+{
+ double fVal = GetDouble();
+ if (!std::isfinite(fVal))
+ {
+ SetError( GetDoubleErrorValue( fVal));
+ return SAL_MAX_INT16;
+ }
+ if (fVal > 0.0)
+ {
+ fVal = rtl::math::approxFloor( fVal);
+ if (fVal > SAL_MAX_INT16)
+ {
+ SetError( FormulaError::IllegalArgument);
+ return SAL_MAX_INT16;
+ }
+ }
+ else if (fVal < 0.0)
+ {
+ fVal = rtl::math::approxCeil( fVal);
+ if (fVal < SAL_MIN_INT16)
+ {
+ SetError( FormulaError::IllegalArgument);
+ return SAL_MAX_INT16;
+ }
+ }
+ return static_cast<sal_Int16>(fVal);
+}
+
+sal_uInt32 ScInterpreter::GetUInt32()
+{
+ double fVal = rtl::math::approxFloor( GetDouble());
+ if (!std::isfinite(fVal))
+ {
+ SetError( GetDoubleErrorValue( fVal));
+ return SAL_MAX_UINT32;
+ }
+ if (fVal < 0.0 || fVal > SAL_MAX_UINT32)
+ {
+ SetError( FormulaError::IllegalArgument);
+ return SAL_MAX_UINT32;
+ }
+ return static_cast<sal_uInt32>(fVal);
+}
+
+bool ScInterpreter::GetDoubleOrString( double& rDouble, svl::SharedString& rString )
+{
+ bool bDouble = true;
+ switch( GetRawStackType() )
+ {
+ case svDouble:
+ rDouble = PopDouble();
+ break;
+ case svString:
+ rString = PopString();
+ bDouble = false;
+ break;
+ case svDoubleRef :
+ case svSingleRef :
+ {
+ ScAddress aAdr;
+ if (!PopDoubleRefOrSingleRef( aAdr))
+ {
+ rDouble = 0.0;
+ return true; // caller needs to check nGlobalError
+ }
+ ScRefCellValue aCell( *pDok, aAdr);
+ if (aCell.hasNumeric())
+ {
+ rDouble = GetCellValue( aAdr, aCell);
+ }
+ else
+ {
+ GetCellString( rString, aCell);
+ bDouble = false;
+ }
+ }
+ break;
+ case svExternalSingleRef:
+ case svExternalDoubleRef:
+ case svMatrix:
+ {
+ ScMatValType nType = GetDoubleOrStringFromMatrix( rDouble, rString);
+ bDouble = ScMatrix::IsValueType( nType);
+ }
+ break;
+ case svError:
+ PopError();
+ rDouble = 0.0;
+ break;
+ case svEmptyCell:
+ case svMissing:
+ Pop();
+ rDouble = 0.0;
+ break;
+ default:
+ PopError();
+ SetError( FormulaError::IllegalParameter);
+ rDouble = 0.0;
+ }
+ if ( nFuncFmtType == nCurFmtType )
+ nFuncFmtIndex = nCurFmtIndex;
+ return bDouble;
+}
+
+svl::SharedString ScInterpreter::GetString()
+{
+ switch (GetRawStackType())
+ {
+ case svError:
+ PopError();
+ return svl::SharedString::getEmptyString();
+ case svMissing:
+ case svEmptyCell:
+ Pop();
+ return svl::SharedString::getEmptyString();
+ case svDouble:
+ {
+ return GetStringFromDouble( PopDouble() );
+ }
+ case svString:
+ return PopString();
+ case svSingleRef:
+ {
+ ScAddress aAdr;
+ PopSingleRef( aAdr );
+ if (nGlobalError == FormulaError::NONE)
+ {
+ ScRefCellValue aCell(*pDok, aAdr);
+ svl::SharedString aSS;
+ GetCellString(aSS, aCell);
+ return aSS;
+ }
+ else
+ return svl::SharedString::getEmptyString();
+ }
+ case svDoubleRef:
+ { // generate position dependent SingleRef
+ ScRange aRange;
+ PopDoubleRef( aRange );
+ ScAddress aAdr;
+ if ( nGlobalError == FormulaError::NONE && DoubleRefToPosSingleRef( aRange, aAdr ) )
+ {
+ ScRefCellValue aCell(*pDok, aAdr);
+ svl::SharedString aSS;
+ GetCellString(aSS, aCell);
+ return aSS;
+ }
+ else
+ return svl::SharedString::getEmptyString();
+ }
+ case svExternalSingleRef:
+ {
+ ScExternalRefCache::TokenRef pToken;
+ PopExternalSingleRef(pToken);
+ if (nGlobalError != FormulaError::NONE)
+ return svl::SharedString::getEmptyString();
+
+ if (pToken->GetType() == svDouble)
+ {
+ return GetStringFromDouble( pToken->GetDouble() );
+ }
+ else // svString or svEmpty
+ return pToken->GetString();
+ }
+ case svExternalDoubleRef:
+ {
+ ScMatrixRef pMat;
+ PopExternalDoubleRef(pMat);
+ return GetStringFromMatrix(pMat);
+ }
+ case svMatrix:
+ {
+ ScMatrixRef pMat = PopMatrix();
+ return GetStringFromMatrix(pMat);
+ }
+ break;
+ default:
+ PopError();
+ SetError( FormulaError::IllegalArgument);
+ }
+ return svl::SharedString::getEmptyString();
+}
+
+svl::SharedString ScInterpreter::GetStringFromMatrix(const ScMatrixRef& pMat)
+{
+ if ( !pMat )
+ ; // nothing
+ else if ( !pJumpMatrix )
+ {
+ return pMat->GetString( *pFormatter, 0, 0);
+ }
+ else
+ {
+ SCSIZE nCols, nRows, nC, nR;
+ pMat->GetDimensions( nCols, nRows);
+ pJumpMatrix->GetPos( nC, nR);
+ // Use vector replication for single row/column arrays.
+ if ( (nC < nCols || nCols == 1) && (nR < nRows || nRows == 1) )
+ return pMat->GetString( *pFormatter, nC, nR);
+
+ SetError( FormulaError::NoValue);
+ }
+ return svl::SharedString::getEmptyString();
+}
+
+ScMatValType ScInterpreter::GetDoubleOrStringFromMatrix(
+ double& rDouble, svl::SharedString& rString )
+{
+
+ rDouble = 0.0;
+ rString = svl::SharedString::getEmptyString();
+ ScMatValType nMatValType = ScMatValType::Empty;
+
+ ScMatrixRef pMat;
+ StackVar eType = GetStackType();
+ if (eType == svExternalDoubleRef || eType == svExternalSingleRef || eType == svMatrix)
+ {
+ pMat = GetMatrix();
+ }
+ else
+ {
+ PopError();
+ SetError( FormulaError::IllegalParameter);
+ return nMatValType;
+ }
+
+ ScMatrixValue nMatVal;
+ if (!pMat)
+ {
+ // nothing
+ }
+ else if (!pJumpMatrix)
+ {
+ nMatVal = pMat->Get(0, 0);
+ nMatValType = nMatVal.nType;
+ }
+ else
+ {
+ SCSIZE nCols, nRows, nC, nR;
+ pMat->GetDimensions( nCols, nRows);
+ pJumpMatrix->GetPos( nC, nR);
+ // Use vector replication for single row/column arrays.
+ if ( (nC < nCols || nCols == 1) && (nR < nRows || nRows == 1) )
+ {
+ nMatVal = pMat->Get( nC, nR);
+ nMatValType = nMatVal.nType;
+ }
+ else
+ SetError( FormulaError::NoValue);
+ }
+
+ if (ScMatrix::IsValueType( nMatValType))
+ {
+ rDouble = nMatVal.fVal;
+ FormulaError nError = nMatVal.GetError();
+ if (nError != FormulaError::NONE)
+ SetError( nError);
+ }
+ else
+ {
+ rString = nMatVal.GetString();
+ }
+
+ return nMatValType;
+}
+
+svl::SharedString ScInterpreter::GetStringFromDouble( double fVal )
+{
+ sal_uLong nIndex = pFormatter->GetStandardFormat(
+ SvNumFormatType::NUMBER,
+ ScGlobal::eLnge);
+ OUString aStr;
+ pFormatter->GetInputLineString(fVal, nIndex, aStr);
+ return mrStrPool.intern(aStr);
+}
+
+void ScInterpreter::ScDBGet()
+{
+ bool bMissingField = false;
+ unique_ptr<ScDBQueryParamBase> pQueryParam( GetDBParams(bMissingField) );
+ if (!pQueryParam)
+ {
+ // Failed to create query param.
+ PushIllegalParameter();
+ return;
+ }
+
+ pQueryParam->mbSkipString = false;
+ ScDBQueryDataIterator aValIter(pDok, mrContext, std::move(pQueryParam));
+ ScDBQueryDataIterator::Value aValue;
+ if (!aValIter.GetFirst(aValue) || aValue.mnError != FormulaError::NONE)
+ {
+ // No match found.
+ PushNoValue();
+ return;
+ }
+
+ ScDBQueryDataIterator::Value aValNext;
+ if (aValIter.GetNext(aValNext) && aValNext.mnError == FormulaError::NONE)
+ {
+ // There should be only one unique match.
+ PushIllegalArgument();
+ return;
+ }
+
+ if (aValue.mbIsNumber)
+ PushDouble(aValue.mfValue);
+ else
+ PushString(aValue.maString);
+}
+
+void ScInterpreter::ScExternal()
+{
+ sal_uInt8 nParamCount = GetByte();
+ OUString aUnoName;
+ OUString aFuncName( ScGlobal::getCharClassPtr()->uppercase( pCur->GetExternal() ) );
+ LegacyFuncData* pLegacyFuncData = ScGlobal::GetLegacyFuncCollection()->findByName(aFuncName);
+ if (pLegacyFuncData)
+ {
+ // Old binary non-UNO add-in function.
+ // NOTE: parameter count is 1-based with the 0th "parameter" being the
+ // return value, included in pLegacyFuncDatat->GetParamCount()
+ if (nParamCount < MAXFUNCPARAM && nParamCount == pLegacyFuncData->GetParamCount() - 1)
+ {
+ ParamType eParamType[MAXFUNCPARAM];
+ void* ppParam[MAXFUNCPARAM];
+ double nVal[MAXFUNCPARAM];
+ char* pStr[MAXFUNCPARAM];
+ sal_uInt8* pCellArr[MAXFUNCPARAM];
+ short i;
+
+ for (i = 0; i < MAXFUNCPARAM; i++)
+ {
+ eParamType[i] = pLegacyFuncData->GetParamType(i);
+ ppParam[i] = nullptr;
+ nVal[i] = 0.0;
+ pStr[i] = nullptr;
+ pCellArr[i] = nullptr;
+ }
+
+ for (i = nParamCount; (i > 0) && (nGlobalError == FormulaError::NONE); i--)
+ {
+ if (IsMissing())
+ {
+ // Old binary Add-In can't distinguish between missing
+ // omitted argument and 0 (or any other value). Force
+ // error.
+ SetError( FormulaError::ParameterExpected);
+ break; // for
+ }
+ switch (eParamType[i])
+ {
+ case ParamType::PTR_DOUBLE :
+ {
+ nVal[i-1] = GetDouble();
+ ppParam[i] = &nVal[i-1];
+ }
+ break;
+ case ParamType::PTR_STRING :
+ {
+ OString aStr(OUStringToOString(GetString().getString(),
+ osl_getThreadTextEncoding()));
+ if ( aStr.getLength() >= ADDIN_MAXSTRLEN )
+ SetError( FormulaError::StringOverflow );
+ else
+ {
+ pStr[i-1] = new char[ADDIN_MAXSTRLEN];
+ strncpy( pStr[i-1], aStr.getStr(), ADDIN_MAXSTRLEN );
+ pStr[i-1][ADDIN_MAXSTRLEN-1] = 0;
+ ppParam[i] = pStr[i-1];
+ }
+ }
+ break;
+ case ParamType::PTR_DOUBLE_ARR :
+ {
+ SCCOL nCol1;
+ SCROW nRow1;
+ SCTAB nTab1;
+ SCCOL nCol2;
+ SCROW nRow2;
+ SCTAB nTab2;
+ PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
+ pCellArr[i-1] = new sal_uInt8[MAXARRSIZE];
+ if (!CreateDoubleArr(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2, pCellArr[i-1]))
+ SetError(FormulaError::CodeOverflow);
+ else
+ ppParam[i] = pCellArr[i-1];
+ }
+ break;
+ case ParamType::PTR_STRING_ARR :
+ {
+ SCCOL nCol1;
+ SCROW nRow1;
+ SCTAB nTab1;
+ SCCOL nCol2;
+ SCROW nRow2;
+ SCTAB nTab2;
+ PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
+ pCellArr[i-1] = new sal_uInt8[MAXARRSIZE];
+ if (!CreateStringArr(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2, pCellArr[i-1]))
+ SetError(FormulaError::CodeOverflow);
+ else
+ ppParam[i] = pCellArr[i-1];
+ }
+ break;
+ case ParamType::PTR_CELL_ARR :
+ {
+ SCCOL nCol1;
+ SCROW nRow1;
+ SCTAB nTab1;
+ SCCOL nCol2;
+ SCROW nRow2;
+ SCTAB nTab2;
+ PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
+ pCellArr[i-1] = new sal_uInt8[MAXARRSIZE];
+ if (!CreateCellArr(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2, pCellArr[i-1]))
+ SetError(FormulaError::CodeOverflow);
+ else
+ ppParam[i] = pCellArr[i-1];
+ }
+ break;
+ default :
+ SetError(FormulaError::IllegalParameter);
+ break;
+ }
+ }
+ while ( i-- )
+ Pop(); // In case of error (otherwise i==0) pop all parameters
+
+ if (nGlobalError == FormulaError::NONE)
+ {
+ if ( pLegacyFuncData->GetAsyncType() == ParamType::NONE )
+ {
+ switch ( eParamType[0] )
+ {
+ case ParamType::PTR_DOUBLE :
+ {
+ double nErg = 0.0;
+ ppParam[0] = &nErg;
+ pLegacyFuncData->Call(ppParam);
+ PushDouble(nErg);
+ }
+ break;
+ case ParamType::PTR_STRING :
+ {
+ std::unique_ptr<char[]> pcErg(new char[ADDIN_MAXSTRLEN]);
+ ppParam[0] = pcErg.get();
+ pLegacyFuncData->Call(ppParam);
+ OUString aUni( pcErg.get(), strlen(pcErg.get()), osl_getThreadTextEncoding() );
+ PushString( aUni );
+ }
+ break;
+ default:
+ PushError( FormulaError::UnknownState );
+ }
+ }
+ else
+ {
+ // enable asyncs after loading
+ pArr->AddRecalcMode( ScRecalcMode::ONLOAD_LENIENT );
+ // assure identical handler with identical call?
+ double nErg = 0.0;
+ ppParam[0] = &nErg;
+ pLegacyFuncData->Call(ppParam);
+ sal_uLong nHandle = sal_uLong( nErg );
+ if ( nHandle >= 65536 )
+ {
+ ScAddInAsync* pAs = ScAddInAsync::Get( nHandle );
+ if ( !pAs )
+ {
+ pAs = new ScAddInAsync(nHandle, pLegacyFuncData, pDok);
+ pMyFormulaCell->StartListening( *pAs );
+ }
+ else
+ {
+ pMyFormulaCell->StartListening( *pAs );
+ if ( !pAs->HasDocument( pDok ) )
+ pAs->AddDocument( pDok );
+ }
+ if ( pAs->IsValid() )
+ {
+ switch ( pAs->GetType() )
+ {
+ case ParamType::PTR_DOUBLE :
+ PushDouble( pAs->GetValue() );
+ break;
+ case ParamType::PTR_STRING :
+ PushString( pAs->GetString() );
+ break;
+ default:
+ PushError( FormulaError::UnknownState );
+ }
+ }
+ else
+ PushNA();
+ }
+ else
+ PushNoValue();
+ }
+ }
+
+ for (i = 0; i < MAXFUNCPARAM; i++)
+ {
+ delete[] pStr[i];
+ delete[] pCellArr[i];
+ }
+ }
+ else
+ {
+ while( nParamCount-- > 0)
+ Pop();
+ PushIllegalParameter();
+ }
+ }
+ else if ( !( aUnoName = ScGlobal::GetAddInCollection()->FindFunction(aFuncName, false) ).isEmpty() )
+ {
+ // bLocalFirst=false in FindFunction, cFunc should be the stored
+ // internal name
+
+ ScUnoAddInCall aCall( *ScGlobal::GetAddInCollection(), aUnoName, nParamCount );
+
+ if ( !aCall.ValidParamCount() )
+ SetError( FormulaError::IllegalParameter );
+
+ if ( aCall.NeedsCaller() && GetError() == FormulaError::NONE )
+ {
+ SfxObjectShell* pShell = pDok->GetDocumentShell();
+ if (pShell)
+ aCall.SetCallerFromObjectShell( pShell );
+ else
+ {
+ // use temporary model object (without document) to supply options
+ aCall.SetCaller( static_cast<beans::XPropertySet*>(
+ new ScDocOptionsObj( pDok->GetDocOptions() ) ) );
+ }
+ }
+
+ short nPar = nParamCount;
+ while ( nPar > 0 && GetError() == FormulaError::NONE )
+ {
+ --nPar; // 0 .. (nParamCount-1)
+
+ uno::Any aParam;
+ if (IsMissing())
+ {
+ // Add-In has to explicitly handle an omitted empty missing
+ // argument, do not default to anything like GetDouble() would
+ // do (e.g. 0).
+ Pop();
+ aCall.SetParam( nPar, aParam );
+ continue; // while
+ }
+
+ StackVar nStackType = GetStackType();
+ ScAddInArgumentType eType = aCall.GetArgType( nPar );
+ switch (eType)
+ {
+ case SC_ADDINARG_INTEGER:
+ {
+ sal_Int32 nVal = GetInt32();
+ if (nGlobalError == FormulaError::NONE)
+ aParam <<= nVal;
+ }
+ break;
+
+ case SC_ADDINARG_DOUBLE:
+ aParam <<= GetDouble();
+ break;
+
+ case SC_ADDINARG_STRING:
+ aParam <<= GetString().getString();
+ break;
+
+ case SC_ADDINARG_INTEGER_ARRAY:
+ switch( nStackType )
+ {
+ case svDouble:
+ case svString:
+ case svSingleRef:
+ {
+ sal_Int32 nVal = GetInt32();
+ if (nGlobalError == FormulaError::NONE)
+ {
+ uno::Sequence<sal_Int32> aInner( &nVal, 1 );
+ uno::Sequence< uno::Sequence<sal_Int32> > aOuter( &aInner, 1 );
+ aParam <<= aOuter;
+ }
+ }
+ break;
+ case svDoubleRef:
+ {
+ ScRange aRange;
+ PopDoubleRef( aRange );
+ if (!ScRangeToSequence::FillLongArray( aParam, pDok, aRange ))
+ SetError(FormulaError::IllegalParameter);
+ }
+ break;
+ case svMatrix:
+ if (!ScRangeToSequence::FillLongArray( aParam, PopMatrix().get() ))
+ SetError(FormulaError::IllegalParameter);
+ break;
+ default:
+ PopError();
+ SetError(FormulaError::IllegalParameter);
+ }
+ break;
+
+ case SC_ADDINARG_DOUBLE_ARRAY:
+ switch( nStackType )
+ {
+ case svDouble:
+ case svString:
+ case svSingleRef:
+ {
+ double fVal = GetDouble();
+ uno::Sequence<double> aInner( &fVal, 1 );
+ uno::Sequence< uno::Sequence<double> > aOuter( &aInner, 1 );
+ aParam <<= aOuter;
+ }
+ break;
+ case svDoubleRef:
+ {
+ ScRange aRange;
+ PopDoubleRef( aRange );
+ if (!ScRangeToSequence::FillDoubleArray( aParam, pDok, aRange ))
+ SetError(FormulaError::IllegalParameter);
+ }
+ break;
+ case svMatrix:
+ if (!ScRangeToSequence::FillDoubleArray( aParam, PopMatrix().get() ))
+ SetError(FormulaError::IllegalParameter);
+ break;
+ default:
+ PopError();
+ SetError(FormulaError::IllegalParameter);
+ }
+ break;
+
+ case SC_ADDINARG_STRING_ARRAY:
+ switch( nStackType )
+ {
+ case svDouble:
+ case svString:
+ case svSingleRef:
+ {
+ OUString aString = GetString().getString();
+ uno::Sequence<OUString> aInner( &aString, 1 );
+ uno::Sequence< uno::Sequence<OUString> > aOuter( &aInner, 1 );
+ aParam <<= aOuter;
+ }
+ break;
+ case svDoubleRef:
+ {
+ ScRange aRange;
+ PopDoubleRef( aRange );
+ if (!ScRangeToSequence::FillStringArray( aParam, pDok, aRange ))
+ SetError(FormulaError::IllegalParameter);
+ }
+ break;
+ case svMatrix:
+ if (!ScRangeToSequence::FillStringArray( aParam, PopMatrix().get(), pFormatter ))
+ SetError(FormulaError::IllegalParameter);
+ break;
+ default:
+ PopError();
+ SetError(FormulaError::IllegalParameter);
+ }
+ break;
+
+ case SC_ADDINARG_MIXED_ARRAY:
+ switch( nStackType )
+ {
+ case svDouble:
+ case svString:
+ case svSingleRef:
+ {
+ uno::Any aElem;
+ if ( nStackType == svDouble )
+ aElem <<= GetDouble();
+ else if ( nStackType == svString )
+ aElem <<= GetString().getString();
+ else
+ {
+ ScAddress aAdr;
+ if ( PopDoubleRefOrSingleRef( aAdr ) )
+ {
+ ScRefCellValue aCell(*pDok, aAdr);
+ if (aCell.hasString())
+ {
+ svl::SharedString aStr;
+ GetCellString(aStr, aCell);
+ aElem <<= aStr.getString();
+ }
+ else
+ aElem <<= GetCellValue(aAdr, aCell);
+ }
+ }
+ uno::Sequence<uno::Any> aInner( &aElem, 1 );
+ uno::Sequence< uno::Sequence<uno::Any> > aOuter( &aInner, 1 );
+ aParam <<= aOuter;
+ }
+ break;
+ case svDoubleRef:
+ {
+ ScRange aRange;
+ PopDoubleRef( aRange );
+ if (!ScRangeToSequence::FillMixedArray( aParam, pDok, aRange ))
+ SetError(FormulaError::IllegalParameter);
+ }
+ break;
+ case svMatrix:
+ if (!ScRangeToSequence::FillMixedArray( aParam, PopMatrix().get() ))
+ SetError(FormulaError::IllegalParameter);
+ break;
+ default:
+ PopError();
+ SetError(FormulaError::IllegalParameter);
+ }
+ break;
+
+ case SC_ADDINARG_VALUE_OR_ARRAY:
+ switch( nStackType )
+ {
+ case svDouble:
+ aParam <<= GetDouble();
+ break;
+ case svString:
+ aParam <<= GetString().getString();
+ break;
+ case svSingleRef:
+ {
+ ScAddress aAdr;
+ if ( PopDoubleRefOrSingleRef( aAdr ) )
+ {
+ ScRefCellValue aCell(*pDok, aAdr);
+ if (aCell.hasString())
+ {
+ svl::SharedString aStr;
+ GetCellString(aStr, aCell);
+ aParam <<= aStr.getString();
+ }
+ else
+ aParam <<= GetCellValue(aAdr, aCell);
+ }
+ }
+ break;
+ case svDoubleRef:
+ {
+ ScRange aRange;
+ PopDoubleRef( aRange );
+ if (!ScRangeToSequence::FillMixedArray( aParam, pDok, aRange ))
+ SetError(FormulaError::IllegalParameter);
+ }
+ break;
+ case svMatrix:
+ if (!ScRangeToSequence::FillMixedArray( aParam, PopMatrix().get() ))
+ SetError(FormulaError::IllegalParameter);
+ break;
+ default:
+ PopError();
+ SetError(FormulaError::IllegalParameter);
+ }
+ break;
+
+ case SC_ADDINARG_CELLRANGE:
+ switch( nStackType )
+ {
+ case svSingleRef:
+ {
+ ScAddress aAdr;
+ PopSingleRef( aAdr );
+ ScRange aRange( aAdr );
+ uno::Reference<table::XCellRange> xObj =
+ ScCellRangeObj::CreateRangeFromDoc( pDok, aRange );
+ if (xObj.is())
+ aParam <<= xObj;
+ else
+ SetError(FormulaError::IllegalParameter);
+ }
+ break;
+ case svDoubleRef:
+ {
+ ScRange aRange;
+ PopDoubleRef( aRange );
+ uno::Reference<table::XCellRange> xObj =
+ ScCellRangeObj::CreateRangeFromDoc( pDok, aRange );
+ if (xObj.is())
+ {
+ aParam <<= xObj;
+ }
+ else
+ {
+ SetError(FormulaError::IllegalParameter);
+ }
+ }
+ break;
+ default:
+ PopError();
+ SetError(FormulaError::IllegalParameter);
+ }
+ break;
+
+ default:
+ PopError();
+ SetError(FormulaError::IllegalParameter);
+ }
+ aCall.SetParam( nPar, aParam );
+ }
+
+ while (nPar-- > 0)
+ {
+ Pop(); // in case of error, remove remaining args
+ }
+ if ( GetError() == FormulaError::NONE )
+ {
+ aCall.ExecuteCall();
+
+ if ( aCall.HasVarRes() ) // handle async functions
+ {
+ pArr->AddRecalcMode( ScRecalcMode::ONLOAD_LENIENT );
+ uno::Reference<sheet::XVolatileResult> xRes = aCall.GetVarRes();
+ ScAddInListener* pLis = ScAddInListener::Get( xRes );
+ // In case there is no pMyFormulaCell, i.e. while interpreting
+ // temporarily from within the Function Wizard, try to obtain a
+ // valid result from an existing listener for that volatile, or
+ // create a new and hope for an immediate result. If none
+ // available that should lead to a void result and thus #N/A.
+ bool bTemporaryListener = false;
+ if ( !pLis )
+ {
+ pLis = ScAddInListener::CreateListener( xRes, pDok );
+ if (pMyFormulaCell)
+ pMyFormulaCell->StartListening( *pLis );
+ else
+ bTemporaryListener = true;
+ }
+ else if (pMyFormulaCell)
+ {
+ pMyFormulaCell->StartListening( *pLis );
+ if ( !pLis->HasDocument( pDok ) )
+ {
+ pLis->AddDocument( pDok );
+ }
+ }
+
+ aCall.SetResult( pLis->GetResult() ); // use result from async
+
+ if (bTemporaryListener)
+ {
+ try
+ {
+ // EventObject can be any, not evaluated by
+ // ScAddInListener::disposing()
+ css::lang::EventObject aEvent;
+ pLis->disposing(aEvent); // pLis is dead hereafter
+ }
+ catch (const uno::Exception&)
+ {
+ }
+ }
+ }
+
+ if ( aCall.GetErrCode() != FormulaError::NONE )
+ {
+ PushError( aCall.GetErrCode() );
+ }
+ else if ( aCall.HasMatrix() )
+ {
+ PushMatrix( aCall.GetMatrix() );
+ }
+ else if ( aCall.HasString() )
+ {
+ PushString( aCall.GetString() );
+ }
+ else
+ {
+ PushDouble( aCall.GetValue() );
+ }
+ }
+ else // error...
+ PushError( GetError());
+ }
+ else
+ {
+ while( nParamCount-- > 0)
+ {
+ Pop();
+ }
+ PushError( FormulaError::NoAddin );
+ }
+}
+
+void ScInterpreter::ScMissing()
+{
+ if ( aCode.IsEndOfPath() )
+ PushTempToken( new ScEmptyCellToken( false, false ) );
+ else
+ PushTempToken( new FormulaMissingToken );
+}
+
+#if HAVE_FEATURE_SCRIPTING
+
+static uno::Any lcl_getSheetModule( const uno::Reference<table::XCellRange>& xCellRange, const ScDocument* pDok )
+{
+ uno::Reference< sheet::XSheetCellRange > xSheetRange( xCellRange, uno::UNO_QUERY_THROW );
+ uno::Reference< beans::XPropertySet > xProps( xSheetRange->getSpreadsheet(), uno::UNO_QUERY_THROW );
+ OUString sCodeName;
+ xProps->getPropertyValue("CodeName") >>= sCodeName;
+ // #TODO #FIXME ideally we should 'throw' here if we don't get a valid parent, but... it is possible
+ // to create a module ( and use 'Option VBASupport 1' ) for a calc document, in this scenario there
+ // are *NO* special document module objects ( of course being able to switch between vba/non vba mode at
+ // the document in the future could fix this, especially IF the switching of the vba mode takes care to
+ // create the special document module objects if they don't exist.
+ BasicManager* pBasMgr = pDok->GetDocumentShell()->GetBasicManager();
+
+ uno::Reference< uno::XInterface > xIf;
+ if ( pBasMgr && !pBasMgr->GetName().isEmpty() )
+ {
+ OUString sProj( "Standard" );
+ if ( !pDok->GetDocumentShell()->GetBasicManager()->GetName().isEmpty() )
+ {
+ sProj = pDok->GetDocumentShell()->GetBasicManager()->GetName();
+ }
+ StarBASIC* pBasic = pDok->GetDocumentShell()->GetBasicManager()->GetLib( sProj );
+ if ( pBasic )
+ {
+ SbModule* pMod = pBasic->FindModule( sCodeName );
+ if ( pMod )
+ {
+ xIf = pMod->GetUnoModule();
+ }
+ }
+ }
+ return uno::makeAny( xIf );
+}
+
+static bool lcl_setVBARange( const ScRange& aRange, const ScDocument* pDok, SbxVariable* pPar )
+{
+ bool bOk = false;
+ try
+ {
+ uno::Reference< uno::XInterface > xVBARange;
+ uno::Reference<table::XCellRange> xCellRange = ScCellRangeObj::CreateRangeFromDoc( pDok, aRange );
+ uno::Sequence< uno::Any > aArgs(2);
+ aArgs[0] = lcl_getSheetModule( xCellRange, pDok );
+ aArgs[1] <<= xCellRange;
+ xVBARange = ooo::vba::createVBAUnoAPIServiceWithArgs( pDok->GetDocumentShell(), "ooo.vba.excel.Range", aArgs );
+ if ( xVBARange.is() )
+ {
+ SbxObjectRef aObj = GetSbUnoObject( "A-Range", uno::Any( xVBARange ) );
+ SetSbUnoObjectDfltPropName( aObj.get() );
+ bOk = pPar->PutObject( aObj.get() );
+ }
+ }
+ catch( uno::Exception& )
+ {
+ }
+ return bOk;
+}
+
+static bool lcl_isNumericResult( double& fVal, const SbxVariable* pVar )
+{
+ switch (pVar->GetType())
+ {
+ case SbxINTEGER:
+ case SbxLONG:
+ case SbxSINGLE:
+ case SbxDOUBLE:
+ case SbxCURRENCY:
+ case SbxDATE:
+ case SbxUSHORT:
+ case SbxULONG:
+ case SbxINT:
+ case SbxUINT:
+ case SbxSALINT64:
+ case SbxSALUINT64:
+ case SbxDECIMAL:
+ fVal = pVar->GetDouble();
+ return true;
+ case SbxBOOL:
+ fVal = (pVar->GetBool() ? 1.0 : 0.0);
+ return true;
+ default:
+ ; // nothing
+ }
+ return false;
+}
+
+#endif
+
+void ScInterpreter::ScMacro()
+{
+
+#if !HAVE_FEATURE_SCRIPTING
+ PushNoValue(); // without DocShell no CallBasic
+ return;
+#else
+ SbxBase::ResetError();
+
+ sal_uInt8 nParamCount = GetByte();
+ OUString aMacro( pCur->GetExternal() );
+
+ SfxObjectShell* pDocSh = pDok->GetDocumentShell();
+ if ( !pDocSh )
+ {
+ PushNoValue(); // without DocShell no CallBasic
+ return;
+ }
+
+ // no security queue beforehand (just CheckMacroWarn), moved to CallBasic
+
+ // If the Dok was loaded during a Basic-Calls,
+ // is the Sbx-object created(?)
+// pDocSh->GetSbxObject();
+
+ // search function with the name,
+ // then assemble SfxObjectShell::CallBasic from aBasicStr, aMacroStr
+
+ StarBASIC* pRoot;
+
+ try
+ {
+ pRoot = pDocSh->GetBasic();
+ }
+ catch (...)
+ {
+ pRoot = nullptr;
+ }
+
+ SbxVariable* pVar = pRoot ? pRoot->Find(aMacro, SbxClassType::Method) : nullptr;
+ if( !pVar || pVar->GetType() == SbxVOID || dynamic_cast<const SbMethod*>( pVar) == nullptr )
+ {
+ PushError( FormulaError::NoMacro );
+ return;
+ }
+
+ bool bVolatileMacro = false;
+ SbMethod* pMethod = static_cast<SbMethod*>(pVar);
+
+ SbModule* pModule = pMethod->GetModule();
+ bool bUseVBAObjects = pModule->IsVBACompat();
+ SbxObject* pObject = pModule->GetParent();
+ OSL_ENSURE(dynamic_cast<const StarBASIC *>(pObject) != nullptr, "No Basic found!");
+ OUString aMacroStr = pObject->GetName() + "." + pModule->GetName() + "." + pMethod->GetName();
+ OUString aBasicStr;
+ if (pRoot && bUseVBAObjects)
+ {
+ // just here to make sure the VBA objects when we run the macro during ODF import
+ pRoot->getVBAGlobals();
+ }
+ if (pObject->GetParent())
+ {
+ aBasicStr = pObject->GetParent()->GetName(); // document BASIC
+ }
+ else
+ {
+ aBasicStr = SfxGetpApp()->GetName(); // application BASIC
+ }
+ // assemble a parameter array
+
+ SbxArrayRef refPar = new SbxArray;
+ bool bOk = true;
+ for( sal_uInt32 i = nParamCount; i && bOk ; i-- )
+ {
+ SbxVariable* pPar = refPar->Get32(i);
+ switch( GetStackType() )
+ {
+ case svDouble:
+ pPar->PutDouble( GetDouble() );
+ break;
+ case svString:
+ pPar->PutString( GetString().getString() );
+ break;
+ case svExternalSingleRef:
+ {
+ ScExternalRefCache::TokenRef pToken;
+ PopExternalSingleRef(pToken);
+ if (nGlobalError != FormulaError::NONE)
+ bOk = false;
+ else
+ {
+ if ( pToken->GetType() == svString )
+ pPar->PutString( pToken->GetString().getString() );
+ else if ( pToken->GetType() == svDouble )
+ pPar->PutDouble( pToken->GetDouble() );
+ else
+ {
+ SetError( FormulaError::IllegalArgument );
+ bOk = false;
+ }
+ }
+ }
+ break;
+ case svSingleRef:
+ {
+ ScAddress aAdr;
+ PopSingleRef( aAdr );
+ if ( bUseVBAObjects )
+ {
+ ScRange aRange( aAdr );
+ bOk = lcl_setVBARange( aRange, pDok, pPar );
+ }
+ else
+ {
+ bOk = SetSbxVariable( pPar, aAdr );
+ }
+ }
+ break;
+ case svDoubleRef:
+ {
+ SCCOL nCol1;
+ SCROW nRow1;
+ SCTAB nTab1;
+ SCCOL nCol2;
+ SCROW nRow2;
+ SCTAB nTab2;
+ PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
+ if( nTab1 != nTab2 )
+ {
+ SetError( FormulaError::IllegalParameter );
+ bOk = false;
+ }
+ else
+ {
+ if ( bUseVBAObjects )
+ {
+ ScRange aRange( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
+ bOk = lcl_setVBARange( aRange, pDok, pPar );
+ }
+ else
+ {
+ SbxDimArrayRef refArray = new SbxDimArray;
+ refArray->AddDim32( 1, nRow2 - nRow1 + 1 );
+ refArray->AddDim32( 1, nCol2 - nCol1 + 1 );
+ ScAddress aAdr( nCol1, nRow1, nTab1 );
+ for( SCROW nRow = nRow1; bOk && nRow <= nRow2; nRow++ )
+ {
+ aAdr.SetRow( nRow );
+ sal_Int32 nIdx[ 2 ];
+ nIdx[ 0 ] = nRow-nRow1+1;
+ for( SCCOL nCol = nCol1; bOk && nCol <= nCol2; nCol++ )
+ {
+ aAdr.SetCol( nCol );
+ nIdx[ 1 ] = nCol-nCol1+1;
+ SbxVariable* p = refArray->Get32( nIdx );
+ bOk = SetSbxVariable( p, aAdr );
+ }
+ }
+ pPar->PutObject( refArray.get() );
+ }
+ }
+ }
+ break;
+ case svExternalDoubleRef:
+ case svMatrix:
+ {
+ ScMatrixRef pMat = GetMatrix();
+ SCSIZE nC, nR;
+ if (pMat && nGlobalError == FormulaError::NONE)
+ {
+ pMat->GetDimensions(nC, nR);
+ SbxDimArrayRef refArray = new SbxDimArray;
+ refArray->AddDim32( 1, static_cast<sal_Int32>(nR) );
+ refArray->AddDim32( 1, static_cast<sal_Int32>(nC) );
+ for( SCSIZE nMatRow = 0; nMatRow < nR; nMatRow++ )
+ {
+ sal_Int32 nIdx[ 2 ];
+ nIdx[ 0 ] = static_cast<sal_Int32>(nMatRow+1);
+ for( SCSIZE nMatCol = 0; nMatCol < nC; nMatCol++ )
+ {
+ nIdx[ 1 ] = static_cast<sal_Int32>(nMatCol+1);
+ SbxVariable* p = refArray->Get32( nIdx );
+ if (pMat->IsStringOrEmpty(nMatCol, nMatRow))
+ {
+ p->PutString( pMat->GetString(nMatCol, nMatRow).getString() );
+ }
+ else
+ {
+ p->PutDouble( pMat->GetDouble(nMatCol, nMatRow));
+ }
+ }
+ }
+ pPar->PutObject( refArray.get() );
+ }
+ else
+ {
+ SetError( FormulaError::IllegalParameter );
+ }
+ }
+ break;
+ default:
+ SetError( FormulaError::IllegalParameter );
+ bOk = false;
+ }
+ }
+ if( bOk )
+ {
+ pDok->LockTable( aPos.Tab() );
+ SbxVariableRef refRes = new SbxVariable;
+ pDok->IncMacroInterpretLevel();
+ ErrCode eRet = pDocSh->CallBasic( aMacroStr, aBasicStr, refPar.get(), refRes.get() );
+ pDok->DecMacroInterpretLevel();
+ pDok->UnlockTable( aPos.Tab() );
+
+ ScMacroManager* pMacroMgr = pDok->GetMacroManager();
+ if (pMacroMgr)
+ {
+ bVolatileMacro = pMacroMgr->GetUserFuncVolatile( pMethod->GetName() );
+ pMacroMgr->AddDependentCell(pModule->GetName(), pMyFormulaCell);
+ }
+
+ double fVal;
+ SbxDataType eResType = refRes->GetType();
+ if( SbxBase::GetError() )
+ {
+ SetError( FormulaError::NoValue);
+ }
+ if ( eRet != ERRCODE_NONE )
+ {
+ PushNoValue();
+ }
+ else if (lcl_isNumericResult( fVal, refRes.get()))
+ {
+ switch (eResType)
+ {
+ case SbxDATE:
+ nFuncFmtType = SvNumFormatType::DATE;
+ break;
+ case SbxBOOL:
+ nFuncFmtType = SvNumFormatType::LOGICAL;
+ break;
+ // Do not add SbxCURRENCY, we don't know which currency.
+ default:
+ ; // nothing
+ }
+ PushDouble( fVal );
+ }
+ else if ( eResType & SbxARRAY )
+ {
+ SbxBase* pElemObj = refRes->GetObject();
+ SbxDimArray* pDimArray = dynamic_cast<SbxDimArray*>(pElemObj);
+ sal_Int32 nDim = pDimArray ? pDimArray->GetDims32() : 0;
+ if ( 1 <= nDim && nDim <= 2 )
+ {
+ sal_Int32 nCs, nCe, nRs, nRe;
+ SCSIZE nC, nR;
+ SCCOL nColIdx;
+ SCROW nRowIdx;
+ if ( nDim == 1 )
+ { // array( cols ) one line, several columns
+ pDimArray->GetDim32( 1, nCs, nCe );
+ nC = static_cast<SCSIZE>(nCe - nCs + 1);
+ nRs = 0;
+ nR = 1;
+ nColIdx = 0;
+ nRowIdx = 1;
+ }
+ else
+ { // array( rows, cols )
+ pDimArray->GetDim32( 1, nRs, nRe );
+ nR = static_cast<SCSIZE>(nRe - nRs + 1);
+ pDimArray->GetDim32( 2, nCs, nCe );
+ nC = static_cast<SCSIZE>(nCe - nCs + 1);
+ nColIdx = 1;
+ nRowIdx = 0;
+ }
+ ScMatrixRef pMat = GetNewMat( nC, nR);
+ if ( pMat )
+ {
+ SbxVariable* pV;
+ for ( SCSIZE j=0; j < nR; j++ )
+ {
+ sal_Int32 nIdx[ 2 ];
+ // in one-dimensional array( cols ) nIdx[1]
+ // from SbxDimArray::Get is ignored
+ nIdx[ nRowIdx ] = nRs + static_cast<sal_Int32>(j);
+ for ( SCSIZE i=0; i < nC; i++ )
+ {
+ nIdx[ nColIdx ] = nCs + static_cast<sal_Int32>(i);
+ pV = pDimArray->Get32( nIdx );
+ if ( lcl_isNumericResult( fVal, pV) )
+ {
+ pMat->PutDouble( fVal, i, j );
+ }
+ else
+ {
+ pMat->PutString(mrStrPool.intern(pV->GetOUString()), i, j);
+ }
+ }
+ }
+ PushMatrix( pMat );
+ }
+ else
+ {
+ PushIllegalArgument();
+ }
+ }
+ else
+ {
+ PushNoValue();
+ }
+ }
+ else
+ {
+ PushString( refRes->GetOUString() );
+ }
+ }
+
+ if (bVolatileMacro && meVolatileType == NOT_VOLATILE)
+ meVolatileType = VOLATILE_MACRO;
+#endif
+}
+
+#if HAVE_FEATURE_SCRIPTING
+
+bool ScInterpreter::SetSbxVariable( SbxVariable* pVar, const ScAddress& rPos )
+{
+ bool bOk = true;
+ ScRefCellValue aCell(*pDok, rPos);
+ if (!aCell.isEmpty())
+ {
+ FormulaError nErr;
+ double nVal;
+ switch (aCell.meType)
+ {
+ case CELLTYPE_VALUE :
+ nVal = GetValueCellValue(rPos, aCell.mfValue);
+ pVar->PutDouble( nVal );
+ break;
+ case CELLTYPE_STRING :
+ case CELLTYPE_EDIT :
+ pVar->PutString(aCell.getString(pDok));
+ break;
+ case CELLTYPE_FORMULA :
+ nErr = aCell.mpFormula->GetErrCode();
+ if( nErr == FormulaError::NONE )
+ {
+ if (aCell.mpFormula->IsValue())
+ {
+ nVal = aCell.mpFormula->GetValue();
+ pVar->PutDouble( nVal );
+ }
+ else
+ pVar->PutString(aCell.mpFormula->GetString().getString());
+ }
+ else
+ {
+ SetError( nErr );
+ bOk = false;
+ }
+ break;
+ default :
+ pVar->PutEmpty();
+ }
+ }
+ else
+ pVar->PutEmpty();
+
+ return bOk;
+}
+
+#endif
+
+void ScInterpreter::ScTableOp()
+{
+ sal_uInt8 nParamCount = GetByte();
+ if (nParamCount != 3 && nParamCount != 5)
+ {
+ PushIllegalParameter();
+ return;
+ }
+ std::unique_ptr<ScInterpreterTableOpParams> pTableOp(new ScInterpreterTableOpParams);
+ if (nParamCount == 5)
+ {
+ PopSingleRef( pTableOp->aNew2 );
+ PopSingleRef( pTableOp->aOld2 );
+ }
+ PopSingleRef( pTableOp->aNew1 );
+ PopSingleRef( pTableOp->aOld1 );
+ PopSingleRef( pTableOp->aFormulaPos );
+
+ pTableOp->bValid = true;
+ pDok->m_TableOpList.push_back(pTableOp.get());
+ pDok->IncInterpreterTableOpLevel();
+
+ bool bReuseLastParams = (pDok->aLastTableOpParams == *pTableOp);
+ if ( bReuseLastParams )
+ {
+ pTableOp->aNotifiedFormulaPos = pDok->aLastTableOpParams.aNotifiedFormulaPos;
+ pTableOp->bRefresh = true;
+ for ( const auto& rPos : pTableOp->aNotifiedFormulaPos )
+ { // emulate broadcast and indirectly collect cell pointers
+ ScRefCellValue aCell(*pDok, rPos);
+ if (aCell.meType == CELLTYPE_FORMULA)
+ aCell.mpFormula->SetTableOpDirty();
+ }
+ }
+ else
+ { // broadcast and indirectly collect cell pointers and positions
+ pDok->SetTableOpDirty( pTableOp->aOld1 );
+ if ( nParamCount == 5 )
+ pDok->SetTableOpDirty( pTableOp->aOld2 );
+ }
+ pTableOp->bCollectNotifications = false;
+
+ ScRefCellValue aCell(*pDok, pTableOp->aFormulaPos);
+ if (aCell.meType == CELLTYPE_FORMULA)
+ aCell.mpFormula->SetDirtyVar();
+ if (aCell.hasNumeric())
+ {
+ PushDouble(GetCellValue(pTableOp->aFormulaPos, aCell));
+ }
+ else
+ {
+ svl::SharedString aCellString;
+ GetCellString(aCellString, aCell);
+ PushString( aCellString );
+ }
+
+ auto const itr =
+ ::std::find(pDok->m_TableOpList.begin(), pDok->m_TableOpList.end(), pTableOp.get());
+ if (itr != pDok->m_TableOpList.end())
+ {
+ pDok->m_TableOpList.erase(itr);
+ }
+
+ // set dirty again once more to be able to recalculate original
+ for ( const auto& pCell : pTableOp->aNotifiedFormulaCells )
+ {
+ pCell->SetTableOpDirty();
+ }
+
+ // save these params for next incarnation
+ if ( !bReuseLastParams )
+ pDok->aLastTableOpParams = *pTableOp;
+
+ if (aCell.meType == CELLTYPE_FORMULA)
+ {
+ aCell.mpFormula->SetDirtyVar();
+ aCell.mpFormula->GetErrCode(); // recalculate original
+ }
+
+ // Reset all dirty flags so next incarnation does really collect all cell
+ // pointers during notifications and not just non-dirty ones, which may
+ // happen if a formula cell is used by more than one TableOp block.
+ for ( const auto& pCell : pTableOp->aNotifiedFormulaCells )
+ {
+ pCell->ResetTableOpDirtyVar();
+ }
+ pTableOp.reset();
+
+ pDok->DecInterpreterTableOpLevel();
+}
+
+void ScInterpreter::ScDBArea()
+{
+ ScDBData* pDBData = pDok->GetDBCollection()->getNamedDBs().findByIndex(pCur->GetIndex());
+ if (pDBData)
+ {
+ ScComplexRefData aRefData;
+ aRefData.InitFlags();
+ ScRange aRange;
+ pDBData->GetArea(aRange);
+ aRange.aEnd.SetTab(aRange.aStart.Tab());
+ aRefData.SetRange(pDok->GetSheetLimits(), aRange, aPos);
+ PushTempToken( new ScDoubleRefToken( pDok->GetSheetLimits(), aRefData ) );
+ }
+ else
+ PushError( FormulaError::NoName);
+}
+
+void ScInterpreter::ScColRowNameAuto()
+{
+ ScComplexRefData aRefData( *pCur->GetDoubleRef() );
+ ScRange aAbs = aRefData.toAbs(pDok, aPos);
+ if (!pDok->ValidRange(aAbs))
+ {
+ PushError( FormulaError::NoRef );
+ return;
+ }
+
+ SCCOL nStartCol;
+ SCROW nStartRow;
+
+ // maybe remember limit by using defined ColRowNameRange
+ SCCOL nCol2 = aAbs.aEnd.Col();
+ SCROW nRow2 = aAbs.aEnd.Row();
+ // DataArea of the first cell
+ nStartCol = aAbs.aStart.Col();
+ nStartRow = aAbs.aStart.Row();
+ aAbs.aEnd = aAbs.aStart; // Shrink to the top-left cell.
+
+ {
+ // Expand to the data area. Only modify the end position.
+ SCCOL nDACol1 = aAbs.aStart.Col(), nDACol2 = aAbs.aEnd.Col();
+ SCROW nDARow1 = aAbs.aStart.Row(), nDARow2 = aAbs.aEnd.Row();
+ pDok->GetDataArea(aAbs.aStart.Tab(), nDACol1, nDARow1, nDACol2, nDARow2, true, false);
+ aAbs.aEnd.SetCol(nDACol2);
+ aAbs.aEnd.SetRow(nDARow2);
+ }
+
+ // corresponds with ScCompiler::GetToken
+ if ( aRefData.Ref1.IsColRel() )
+ { // ColName
+ aAbs.aEnd.SetCol(nStartCol);
+ // maybe get previous limit by using defined ColRowNameRange
+ if (aAbs.aEnd.Row() > nRow2)
+ aAbs.aEnd.SetRow(nRow2);
+ SCROW nMyRow;
+ if ( aPos.Col() == nStartCol )
+ {
+ nMyRow = aPos.Row();
+ if ( nStartRow <= nMyRow && nMyRow <= aAbs.aEnd.Row())
+ { //Formula in the same column and within the range
+ if ( nMyRow == nStartRow )
+ { // take the rest under the name
+ nStartRow++;
+ if ( nStartRow > pDok->MaxRow() )
+ nStartRow = pDok->MaxRow();
+ aAbs.aStart.SetRow(nStartRow);
+ }
+ else
+ { // below the name to the formula cell
+ aAbs.aEnd.SetRow(nMyRow - 1);
+ }
+ }
+ }
+ }
+ else
+ { // RowName
+ aAbs.aEnd.SetRow(nStartRow);
+ // maybe get previous limit by using defined ColRowNameRange
+ if (aAbs.aEnd.Col() > nCol2)
+ aAbs.aEnd.SetCol(nCol2);
+ SCCOL nMyCol;
+ if ( aPos.Row() == nStartRow )
+ {
+ nMyCol = aPos.Col();
+ if (nStartCol <= nMyCol && nMyCol <= aAbs.aEnd.Col())
+ { //Formula in the same column and within the range
+ if ( nMyCol == nStartCol )
+ { // take the rest under the name
+ nStartCol++;
+ if ( nStartCol > pDok->MaxCol() )
+ nStartCol = pDok->MaxCol();
+ aAbs.aStart.SetCol(nStartCol);
+ }
+ else
+ { // below the name to the formula cell
+ aAbs.aEnd.SetCol(nMyCol - 1);
+ }
+ }
+ }
+ }
+ aRefData.SetRange(pDok->GetSheetLimits(), aAbs, aPos);
+ PushTempToken( new ScDoubleRefToken( pDok->GetSheetLimits(), aRefData ) );
+}
+
+// --- internals ------------------------------------------------------------
+
+void ScInterpreter::ScTTT()
+{ // temporary test, testing functions etc.
+ sal_uInt8 nParamCount = GetByte();
+ // do something, count down nParamCount with Pops!
+
+ // clean up Stack
+ while ( nParamCount-- > 0)
+ Pop();
+ PushError(FormulaError::NoValue);
+}
+
+ScInterpreter::ScInterpreter( ScFormulaCell* pCell, ScDocument* pDoc, ScInterpreterContext& rContext,
+ const ScAddress& rPos, ScTokenArray& r, bool bForGroupThreading )
+ : aCode(r)
+ , aPos(rPos)
+ , pArr(&r)
+ , mrContext(rContext)
+ , pDok(pDoc)
+ , mpLinkManager(pDok->GetLinkManager())
+ , mrStrPool(pDoc->GetSharedStringPool())
+ , pJumpMatrix(nullptr)
+ , pMyFormulaCell(pCell)
+ , pFormatter(rContext.GetFormatTable())
+ , pCur(nullptr)
+ , nGlobalError(FormulaError::NONE)
+ , sp(0)
+ , maxsp(0)
+ , nFuncFmtIndex(0)
+ , nCurFmtIndex(0)
+ , nRetFmtIndex(0)
+ , nFuncFmtType(SvNumFormatType::ALL)
+ , nCurFmtType(SvNumFormatType::ALL)
+ , nRetFmtType(SvNumFormatType::ALL)
+ , mnStringNoValueError(FormulaError::NoValue)
+ , mnSubTotalFlags(SubtotalFlags::NONE)
+ , cPar(0)
+ , bCalcAsShown(pDoc->GetDocOptions().IsCalcAsShown())
+ , meVolatileType(r.IsRecalcModeAlways() ? VOLATILE : NOT_VOLATILE)
+{
+ MergeCalcConfig();
+
+ if(pMyFormulaCell)
+ {
+ ScMatrixMode cMatFlag = pMyFormulaCell->GetMatrixFlag();
+ bMatrixFormula = ( cMatFlag == ScMatrixMode::Formula );
+ }
+ else
+ bMatrixFormula = false;
+
+ // Lets not use the global stack while formula-group-threading.
+ // as it complicates its life-cycle mgmt since for threading formula-groups,
+ // ScInterpreter is preallocated (in main thread) for each worker thread.
+ if (!bGlobalStackInUse && !bForGroupThreading)
+ {
+ bGlobalStackInUse = true;
+ if (!pGlobalStack)
+ pGlobalStack.reset(new ScTokenStack);
+ pStackObj = pGlobalStack.get();
+ }
+ else
+ {
+ pStackObj = new ScTokenStack;
+ }
+ pStack = pStackObj->pPointer;
+}
+
+ScInterpreter::~ScInterpreter()
+{
+ if ( pStackObj == pGlobalStack.get() )
+ bGlobalStackInUse = false;
+ else
+ delete pStackObj;
+}
+
+void ScInterpreter::Init( ScFormulaCell* pCell, const ScAddress& rPos, ScTokenArray& rTokArray )
+{
+ aCode.ReInit(rTokArray);
+ aPos = rPos;
+ pArr = &rTokArray;
+ xResult = nullptr;
+ pJumpMatrix = nullptr;
+ pTokenMatrixMap.reset();
+ pMyFormulaCell = pCell;
+ pCur = nullptr;
+ nGlobalError = FormulaError::NONE;
+ sp = 0;
+ maxsp = 0;
+ nFuncFmtIndex = 0;
+ nCurFmtIndex = 0;
+ nRetFmtIndex = 0;
+ nFuncFmtType = SvNumFormatType::ALL;
+ nCurFmtType = SvNumFormatType::ALL;
+ nRetFmtType = SvNumFormatType::ALL;
+ mnStringNoValueError = FormulaError::NoValue;
+ mnSubTotalFlags = SubtotalFlags::NONE;
+ cPar = 0;
+}
+
+ScCalcConfig& ScInterpreter::GetOrCreateGlobalConfig()
+{
+ if (!mpGlobalConfig)
+ mpGlobalConfig = new ScCalcConfig();
+ return *mpGlobalConfig;
+}
+
+void ScInterpreter::SetGlobalConfig(const ScCalcConfig& rConfig)
+{
+ GetOrCreateGlobalConfig() = rConfig;
+}
+
+const ScCalcConfig& ScInterpreter::GetGlobalConfig()
+{
+ return GetOrCreateGlobalConfig();
+}
+
+void ScInterpreter::MergeCalcConfig()
+{
+ maCalcConfig = GetOrCreateGlobalConfig();
+ maCalcConfig.MergeDocumentSpecific( pDok->GetCalcConfig());
+}
+
+void ScInterpreter::GlobalExit()
+{
+ OSL_ENSURE(!bGlobalStackInUse, "who is still using the TokenStack?");
+ pGlobalStack.reset();
+}
+
+namespace {
+
+double applyImplicitIntersection(const sc::RangeMatrix& rMat, const ScAddress& rPos)
+{
+ if (rMat.mnRow1 <= rPos.Row() && rPos.Row() <= rMat.mnRow2 && rMat.mnCol1 == rMat.mnCol2)
+ {
+ SCROW nOffset = rPos.Row() - rMat.mnRow1;
+ return rMat.mpMat->GetDouble(0, nOffset);
+ }
+
+ if (rMat.mnCol1 <= rPos.Col() && rPos.Col() <= rMat.mnCol2 && rMat.mnRow1 == rMat.mnRow2)
+ {
+ SCROW nOffset = rPos.Col() - rMat.mnCol1;
+ return rMat.mpMat->GetDouble(nOffset, 0);
+ }
+
+ double fVal;
+ rtl::math::setNan(&fVal);
+ return fVal;
+}
+
+// Test for Functions that evaluate an error code and directly set nGlobalError to 0
+bool IsErrFunc(OpCode oc)
+{
+ switch (oc)
+ {
+ case ocCount :
+ case ocCount2 :
+ case ocErrorType :
+ case ocIsEmpty :
+ case ocIsErr :
+ case ocIsError :
+ case ocIsFormula :
+ case ocIsLogical :
+ case ocIsNA :
+ case ocIsNonString :
+ case ocIsRef :
+ case ocIsString :
+ case ocIsValue :
+ case ocN :
+ case ocType :
+ case ocIfError :
+ case ocIfNA :
+ case ocErrorType_ODF :
+ case ocAggregate: // may ignore errors depending on option
+ case ocIfs_MS:
+ case ocSwitch_MS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+} //namespace
+
+StackVar ScInterpreter::Interpret()
+{
+ SvNumFormatType nRetTypeExpr = SvNumFormatType::UNDEFINED;
+ sal_uInt32 nRetIndexExpr = 0;
+ sal_uInt16 nErrorFunction = 0;
+ sal_uInt16 nErrorFunctionCount = 0;
+ std::vector<sal_uInt16> aErrorFunctionStack;
+ sal_uInt16 nStackBase;
+
+ nGlobalError = FormulaError::NONE;
+ nStackBase = sp = maxsp = 0;
+ nRetFmtType = SvNumFormatType::UNDEFINED;
+ nFuncFmtType = SvNumFormatType::UNDEFINED;
+ nFuncFmtIndex = nCurFmtIndex = nRetFmtIndex = 0;
+ xResult = nullptr;
+ pJumpMatrix = nullptr;
+ mnSubTotalFlags = SubtotalFlags::NONE;
+ ScTokenMatrixMap::const_iterator aTokenMatrixMapIter;
+
+ // Once upon a time we used to have FP exceptions on, and there was a
+ // Windows printer driver that kept switching off exceptions, so we had to
+ // switch them back on again every time. Who knows if there isn't a driver
+ // that keeps switching exceptions on, now that we run with exceptions off,
+ // so reassure exceptions are really off.
+ SAL_MATH_FPEXCEPTIONS_OFF();
+
+ OpCode eOp = ocNone;
+ aCode.Reset();
+ for (;;)
+ {
+ pCur = aCode.Next();
+ if (!pCur || (nGlobalError != FormulaError::NONE && nErrorFunction > nErrorFunctionCount) )
+ break;
+ eOp = pCur->GetOpCode();
+ cPar = pCur->GetByte();
+ if ( eOp == ocPush )
+ {
+ // RPN code push without error
+ PushWithoutError( *pCur );
+ nCurFmtType = SvNumFormatType::UNDEFINED;
+ }
+ else if (pTokenMatrixMap &&
+ !FormulaCompiler::IsOpCodeJumpCommand( eOp ) &&
+ ((aTokenMatrixMapIter = pTokenMatrixMap->find( pCur)) !=
+ pTokenMatrixMap->end()) &&
+ (*aTokenMatrixMapIter).second->GetType() != svJumpMatrix)
+ {
+ // Path already calculated, reuse result.
+ nStackBase = sp - pCur->GetParamCount();
+ if ( nStackBase > sp )
+ nStackBase = sp; // underflow?!?
+ sp = nStackBase;
+ PushTokenRef( (*aTokenMatrixMapIter).second);
+ }
+ else
+ {
+ // previous expression determines the current number format
+ nCurFmtType = nRetTypeExpr;
+ nCurFmtIndex = nRetIndexExpr;
+ // default function's format, others are set if needed
+ nFuncFmtType = SvNumFormatType::NUMBER;
+ nFuncFmtIndex = 0;
+
+ if (FormulaCompiler::IsOpCodeJumpCommand( eOp ))
+ nStackBase = sp; // don't mess around with the jumps
+ else
+ {
+ // Convert parameters to matrix if in array/matrix formula and
+ // parameters of function indicate doing so. Create JumpMatrix
+ // if necessary.
+ if ( MatrixParameterConversion() )
+ {
+ eOp = ocNone; // JumpMatrix created
+ nStackBase = sp;
+ }
+ else if (sp >= pCur->GetParamCount())
+ nStackBase = sp - pCur->GetParamCount();
+ else
+ nStackBase = sp; // underflow?!?
+ }
+
+ switch( eOp )
+ {
+ case ocSep:
+ case ocClose: // pushed by the compiler
+ case ocMissing : ScMissing(); break;
+ case ocMacro : ScMacro(); break;
+ case ocDBArea : ScDBArea(); break;
+ case ocColRowNameAuto : ScColRowNameAuto(); break;
+ case ocIf : ScIfJump(); break;
+ case ocIfError : ScIfError( false ); break;
+ case ocIfNA : ScIfError( true ); break;
+ case ocChoose : ScChooseJump(); break;
+ case ocAdd : ScAdd(); break;
+ case ocSub : ScSub(); break;
+ case ocMul : ScMul(); break;
+ case ocDiv : ScDiv(); break;
+ case ocAmpersand : ScAmpersand(); break;
+ case ocPow : ScPow(); break;
+ case ocEqual : ScEqual(); break;
+ case ocNotEqual : ScNotEqual(); break;
+ case ocLess : ScLess(); break;
+ case ocGreater : ScGreater(); break;
+ case ocLessEqual : ScLessEqual(); break;
+ case ocGreaterEqual : ScGreaterEqual(); break;
+ case ocAnd : ScAnd(); break;
+ case ocOr : ScOr(); break;
+ case ocXor : ScXor(); break;
+ case ocIntersect : ScIntersect(); break;
+ case ocRange : ScRangeFunc(); break;
+ case ocUnion : ScUnionFunc(); break;
+ case ocNot : ScNot(); break;
+ case ocNegSub :
+ case ocNeg : ScNeg(); break;
+ case ocPercentSign : ScPercentSign(); break;
+ case ocPi : ScPi(); break;
+ case ocRandom : ScRandom(); break;
+ case ocRandomNV : ScRandom(); break;
+ case ocRandbetweenNV : ScRandbetween(); break;
+ case ocTrue : ScTrue(); break;
+ case ocFalse : ScFalse(); break;
+ case ocGetActDate : ScGetActDate(); break;
+ case ocGetActTime : ScGetActTime(); break;
+ case ocNotAvail : PushError( FormulaError::NotAvailable); break;
+ case ocDeg : ScDeg(); break;
+ case ocRad : ScRad(); break;
+ case ocSin : ScSin(); break;
+ case ocCos : ScCos(); break;
+ case ocTan : ScTan(); break;
+ case ocCot : ScCot(); break;
+ case ocArcSin : ScArcSin(); break;
+ case ocArcCos : ScArcCos(); break;
+ case ocArcTan : ScArcTan(); break;
+ case ocArcCot : ScArcCot(); break;
+ case ocSinHyp : ScSinHyp(); break;
+ case ocCosHyp : ScCosHyp(); break;
+ case ocTanHyp : ScTanHyp(); break;
+ case ocCotHyp : ScCotHyp(); break;
+ case ocArcSinHyp : ScArcSinHyp(); break;
+ case ocArcCosHyp : ScArcCosHyp(); break;
+ case ocArcTanHyp : ScArcTanHyp(); break;
+ case ocArcCotHyp : ScArcCotHyp(); break;
+ case ocCosecant : ScCosecant(); break;
+ case ocSecant : ScSecant(); break;
+ case ocCosecantHyp : ScCosecantHyp(); break;
+ case ocSecantHyp : ScSecantHyp(); break;
+ case ocExp : ScExp(); break;
+ case ocLn : ScLn(); break;
+ case ocLog10 : ScLog10(); break;
+ case ocSqrt : ScSqrt(); break;
+ case ocFact : ScFact(); break;
+ case ocGetYear : ScGetYear(); break;
+ case ocGetMonth : ScGetMonth(); break;
+ case ocGetDay : ScGetDay(); break;
+ case ocGetDayOfWeek : ScGetDayOfWeek(); break;
+ case ocWeek : ScGetWeekOfYear(); break;
+ case ocIsoWeeknum : ScGetIsoWeekOfYear(); break;
+ case ocWeeknumOOo : ScWeeknumOOo(); break;
+ case ocEasterSunday : ScEasterSunday(); break;
+ case ocNetWorkdays : ScNetWorkdays( false); break;
+ case ocNetWorkdays_MS : ScNetWorkdays( true ); break;
+ case ocWorkday_MS : ScWorkday_MS(); break;
+ case ocGetHour : ScGetHour(); break;
+ case ocGetMin : ScGetMin(); break;
+ case ocGetSec : ScGetSec(); break;
+ case ocPlusMinus : ScPlusMinus(); break;
+ case ocAbs : ScAbs(); break;
+ case ocInt : ScInt(); break;
+ case ocEven : ScEven(); break;
+ case ocOdd : ScOdd(); break;
+ case ocPhi : ScPhi(); break;
+ case ocGauss : ScGauss(); break;
+ case ocStdNormDist : ScStdNormDist(); break;
+ case ocStdNormDist_MS : ScStdNormDist_MS(); break;
+ case ocFisher : ScFisher(); break;
+ case ocFisherInv : ScFisherInv(); break;
+ case ocIsEmpty : ScIsEmpty(); break;
+ case ocIsString : ScIsString(); break;
+ case ocIsNonString : ScIsNonString(); break;
+ case ocIsLogical : ScIsLogical(); break;
+ case ocType : ScType(); break;
+ case ocCell : ScCell(); break;
+ case ocIsRef : ScIsRef(); break;
+ case ocIsValue : ScIsValue(); break;
+ case ocIsFormula : ScIsFormula(); break;
+ case ocFormula : ScFormula(); break;
+ case ocIsNA : ScIsNV(); break;
+ case ocIsErr : ScIsErr(); break;
+ case ocIsError : ScIsError(); break;
+ case ocIsEven : ScIsEven(); break;
+ case ocIsOdd : ScIsOdd(); break;
+ case ocN : ScN(); break;
+ case ocGetDateValue : ScGetDateValue(); break;
+ case ocGetTimeValue : ScGetTimeValue(); break;
+ case ocCode : ScCode(); break;
+ case ocTrim : ScTrim(); break;
+ case ocUpper : ScUpper(); break;
+ case ocProper : ScProper(); break;
+ case ocLower : ScLower(); break;
+ case ocLen : ScLen(); break;
+ case ocT : ScT(); break;
+ case ocClean : ScClean(); break;
+ case ocValue : ScValue(); break;
+ case ocNumberValue : ScNumberValue(); break;
+ case ocChar : ScChar(); break;
+ case ocArcTan2 : ScArcTan2(); break;
+ case ocMod : ScMod(); break;
+ case ocPower : ScPower(); break;
+ case ocRound : ScRound(); break;
+ case ocRoundSig : ScRoundSignificant(); break;
+ case ocRoundUp : ScRoundUp(); break;
+ case ocTrunc :
+ case ocRoundDown : ScRoundDown(); break;
+ case ocCeil : ScCeil( true ); break;
+ case ocCeil_MS : ScCeil_MS(); break;
+ case ocCeil_Precise :
+ case ocCeil_ISO : ScCeil_Precise(); break;
+ case ocCeil_Math : ScCeil( false ); break;
+ case ocFloor : ScFloor( true ); break;
+ case ocFloor_MS : ScFloor_MS(); break;
+ case ocFloor_Precise : ScFloor_Precise(); break;
+ case ocFloor_Math : ScFloor( false ); break;
+ case ocSumProduct : ScSumProduct(); break;
+ case ocSumSQ : ScSumSQ(); break;
+ case ocSumX2MY2 : ScSumX2MY2(); break;
+ case ocSumX2DY2 : ScSumX2DY2(); break;
+ case ocSumXMY2 : ScSumXMY2(); break;
+ case ocRawSubtract : ScRawSubtract(); break;
+ case ocLog : ScLog(); break;
+ case ocGCD : ScGCD(); break;
+ case ocLCM : ScLCM(); break;
+ case ocGetDate : ScGetDate(); break;
+ case ocGetTime : ScGetTime(); break;
+ case ocGetDiffDate : ScGetDiffDate(); break;
+ case ocGetDiffDate360 : ScGetDiffDate360(); break;
+ case ocGetDateDif : ScGetDateDif(); break;
+ case ocMin : ScMin() ; break;
+ case ocMinA : ScMin( true ); break;
+ case ocMax : ScMax(); break;
+ case ocMaxA : ScMax( true ); break;
+ case ocSum : ScSum(); break;
+ case ocProduct : ScProduct(); break;
+ case ocNPV : ScNPV(); break;
+ case ocIRR : ScIRR(); break;
+ case ocMIRR : ScMIRR(); break;
+ case ocISPMT : ScISPMT(); break;
+ case ocAverage : ScAverage() ; break;
+ case ocAverageA : ScAverage( true ); break;
+ case ocCount : ScCount(); break;
+ case ocCount2 : ScCount2(); break;
+ case ocVar :
+ case ocVarS : ScVar(); break;
+ case ocVarA : ScVar( true ); break;
+ case ocVarP :
+ case ocVarP_MS : ScVarP(); break;
+ case ocVarPA : ScVarP( true ); break;
+ case ocStDev :
+ case ocStDevS : ScStDev(); break;
+ case ocStDevA : ScStDev( true ); break;
+ case ocStDevP :
+ case ocStDevP_MS : ScStDevP(); break;
+ case ocStDevPA : ScStDevP( true ); break;
+ case ocPV : ScPV(); break;
+ case ocSYD : ScSYD(); break;
+ case ocDDB : ScDDB(); break;
+ case ocDB : ScDB(); break;
+ case ocVBD : ScVDB(); break;
+ case ocPDuration : ScPDuration(); break;
+ case ocSLN : ScSLN(); break;
+ case ocPMT : ScPMT(); break;
+ case ocColumns : ScColumns(); break;
+ case ocRows : ScRows(); break;
+ case ocSheets : ScSheets(); break;
+ case ocColumn : ScColumn(); break;
+ case ocRow : ScRow(); break;
+ case ocSheet : ScSheet(); break;
+ case ocRRI : ScRRI(); break;
+ case ocFV : ScFV(); break;
+ case ocNper : ScNper(); break;
+ case ocRate : ScRate(); break;
+ case ocFilterXML : ScFilterXML(); break;
+ case ocWebservice : ScWebservice(); break;
+ case ocEncodeURL : ScEncodeURL(); break;
+ case ocColor : ScColor(); break;
+ case ocErf_MS : ScErf(); break;
+ case ocErfc_MS : ScErfc(); break;
+ case ocIpmt : ScIpmt(); break;
+ case ocPpmt : ScPpmt(); break;
+ case ocCumIpmt : ScCumIpmt(); break;
+ case ocCumPrinc : ScCumPrinc(); break;
+ case ocEffect : ScEffect(); break;
+ case ocNominal : ScNominal(); break;
+ case ocSubTotal : ScSubTotal(); break;
+ case ocAggregate : ScAggregate(); break;
+ case ocDBSum : ScDBSum(); break;
+ case ocDBCount : ScDBCount(); break;
+ case ocDBCount2 : ScDBCount2(); break;
+ case ocDBAverage : ScDBAverage(); break;
+ case ocDBGet : ScDBGet(); break;
+ case ocDBMax : ScDBMax(); break;
+ case ocDBMin : ScDBMin(); break;
+ case ocDBProduct : ScDBProduct(); break;
+ case ocDBStdDev : ScDBStdDev(); break;
+ case ocDBStdDevP : ScDBStdDevP(); break;
+ case ocDBVar : ScDBVar(); break;
+ case ocDBVarP : ScDBVarP(); break;
+ case ocIndirect : ScIndirect(); break;
+ case ocAddress : ScAddressFunc(); break;
+ case ocMatch : ScMatch(); break;
+ case ocCountEmptyCells : ScCountEmptyCells(); break;
+ case ocCountIf : ScCountIf(); break;
+ case ocSumIf : ScSumIf(); break;
+ case ocAverageIf : ScAverageIf(); break;
+ case ocSumIfs : ScSumIfs(); break;
+ case ocAverageIfs : ScAverageIfs(); break;
+ case ocCountIfs : ScCountIfs(); break;
+ case ocLookup : ScLookup(); break;
+ case ocVLookup : ScVLookup(); break;
+ case ocHLookup : ScHLookup(); break;
+ case ocIndex : ScIndex(); break;
+ case ocMultiArea : ScMultiArea(); break;
+ case ocOffset : ScOffset(); break;
+ case ocAreas : ScAreas(); break;
+ case ocCurrency : ScCurrency(); break;
+ case ocReplace : ScReplace(); break;
+ case ocFixed : ScFixed(); break;
+ case ocFind : ScFind(); break;
+ case ocExact : ScExact(); break;
+ case ocLeft : ScLeft(); break;
+ case ocRight : ScRight(); break;
+ case ocSearch : ScSearch(); break;
+ case ocMid : ScMid(); break;
+ case ocText : ScText(); break;
+ case ocSubstitute : ScSubstitute(); break;
+ case ocRegex : ScRegex(); break;
+ case ocRept : ScRept(); break;
+ case ocConcat : ScConcat(); break;
+ case ocConcat_MS : ScConcat_MS(); break;
+ case ocTextJoin_MS : ScTextJoin_MS(); break;
+ case ocIfs_MS : ScIfs_MS(); break;
+ case ocSwitch_MS : ScSwitch_MS(); break;
+ case ocMinIfs_MS : ScMinIfs_MS(); break;
+ case ocMaxIfs_MS : ScMaxIfs_MS(); break;
+ case ocMatValue : ScMatValue(); break;
+ case ocMatrixUnit : ScEMat(); break;
+ case ocMatDet : ScMatDet(); break;
+ case ocMatInv : ScMatInv(); break;
+ case ocMatMult : ScMatMult(); break;
+ case ocMatTrans : ScMatTrans(); break;
+ case ocMatRef : ScMatRef(); break;
+ case ocB : ScB(); break;
+ case ocNormDist : ScNormDist( 3 ); break;
+ case ocNormDist_MS : ScNormDist( 4 ); break;
+ case ocExpDist :
+ case ocExpDist_MS : ScExpDist(); break;
+ case ocBinomDist :
+ case ocBinomDist_MS : ScBinomDist(); break;
+ case ocPoissonDist : ScPoissonDist( true ); break;
+ case ocPoissonDist_MS : ScPoissonDist( false ); break;
+ case ocCombin : ScCombin(); break;
+ case ocCombinA : ScCombinA(); break;
+ case ocPermut : ScPermut(); break;
+ case ocPermutationA : ScPermutationA(); break;
+ case ocHypGeomDist : ScHypGeomDist( 4 ); break;
+ case ocHypGeomDist_MS : ScHypGeomDist( 5 ); break;
+ case ocLogNormDist : ScLogNormDist( 1 ); break;
+ case ocLogNormDist_MS : ScLogNormDist( 4 ); break;
+ case ocTDist : ScTDist(); break;
+ case ocTDist_MS : ScTDist_MS(); break;
+ case ocTDist_RT : ScTDist_T( 1 ); break;
+ case ocTDist_2T : ScTDist_T( 2 ); break;
+ case ocFDist :
+ case ocFDist_RT : ScFDist(); break;
+ case ocFDist_LT : ScFDist_LT(); break;
+ case ocChiDist : ScChiDist( true ); break;
+ case ocChiDist_MS : ScChiDist( false ); break;
+ case ocChiSqDist : ScChiSqDist(); break;
+ case ocChiSqDist_MS : ScChiSqDist_MS(); break;
+ case ocStandard : ScStandard(); break;
+ case ocAveDev : ScAveDev(); break;
+ case ocDevSq : ScDevSq(); break;
+ case ocKurt : ScKurt(); break;
+ case ocSkew : ScSkew(); break;
+ case ocSkewp : ScSkewp(); break;
+ case ocModalValue : ScModalValue(); break;
+ case ocModalValue_MS : ScModalValue_MS( true ); break;
+ case ocModalValue_Multi : ScModalValue_MS( false ); break;
+ case ocMedian : ScMedian(); break;
+ case ocGeoMean : ScGeoMean(); break;
+ case ocHarMean : ScHarMean(); break;
+ case ocWeibull :
+ case ocWeibull_MS : ScWeibull(); break;
+ case ocBinomInv :
+ case ocCritBinom : ScCritBinom(); break;
+ case ocNegBinomVert : ScNegBinomDist(); break;
+ case ocNegBinomDist_MS : ScNegBinomDist_MS(); break;
+ case ocNoName : ScNoName(); break;
+ case ocBad : ScBadName(); break;
+ case ocZTest :
+ case ocZTest_MS : ScZTest(); break;
+ case ocTTest :
+ case ocTTest_MS : ScTTest(); break;
+ case ocFTest :
+ case ocFTest_MS : ScFTest(); break;
+ case ocRank :
+ case ocRank_Eq : ScRank( false ); break;
+ case ocRank_Avg : ScRank( true ); break;
+ case ocPercentile :
+ case ocPercentile_Inc : ScPercentile( true ); break;
+ case ocPercentile_Exc : ScPercentile( false ); break;
+ case ocPercentrank :
+ case ocPercentrank_Inc : ScPercentrank( true ); break;
+ case ocPercentrank_Exc : ScPercentrank( false ); break;
+ case ocLarge : ScLarge(); break;
+ case ocSmall : ScSmall(); break;
+ case ocFrequency : ScFrequency(); break;
+ case ocQuartile :
+ case ocQuartile_Inc : ScQuartile( true ); break;
+ case ocQuartile_Exc : ScQuartile( false ); break;
+ case ocNormInv :
+ case ocNormInv_MS : ScNormInv(); break;
+ case ocSNormInv :
+ case ocSNormInv_MS : ScSNormInv(); break;
+ case ocConfidence :
+ case ocConfidence_N : ScConfidence(); break;
+ case ocConfidence_T : ScConfidenceT(); break;
+ case ocTrimMean : ScTrimMean(); break;
+ case ocProb : ScProbability(); break;
+ case ocCorrel : ScCorrel(); break;
+ case ocCovar :
+ case ocCovarianceP : ScCovarianceP(); break;
+ case ocCovarianceS : ScCovarianceS(); break;
+ case ocPearson : ScPearson(); break;
+ case ocRSQ : ScRSQ(); break;
+ case ocSTEYX : ScSTEYX(); break;
+ case ocSlope : ScSlope(); break;
+ case ocIntercept : ScIntercept(); break;
+ case ocTrend : ScTrend(); break;
+ case ocGrowth : ScGrowth(); break;
+ case ocLinest : ScLinest(); break;
+ case ocLogest : ScLogest(); break;
+ case ocForecast_LIN :
+ case ocForecast : ScForecast(); break;
+ case ocForecast_ETS_ADD : ScForecast_Ets( etsAdd ); break;
+ case ocForecast_ETS_SEA : ScForecast_Ets( etsSeason ); break;
+ case ocForecast_ETS_MUL : ScForecast_Ets( etsMult ); break;
+ case ocForecast_ETS_PIA : ScForecast_Ets( etsPIAdd ); break;
+ case ocForecast_ETS_PIM : ScForecast_Ets( etsPIMult ); break;
+ case ocForecast_ETS_STA : ScForecast_Ets( etsStatAdd ); break;
+ case ocForecast_ETS_STM : ScForecast_Ets( etsStatMult ); break;
+ case ocGammaLn :
+ case ocGammaLn_MS : ScLogGamma(); break;
+ case ocGamma : ScGamma(); break;
+ case ocGammaDist : ScGammaDist( true ); break;
+ case ocGammaDist_MS : ScGammaDist( false ); break;
+ case ocGammaInv :
+ case ocGammaInv_MS : ScGammaInv(); break;
+ case ocChiTest :
+ case ocChiTest_MS : ScChiTest(); break;
+ case ocChiInv :
+ case ocChiInv_MS : ScChiInv(); break;
+ case ocChiSqInv :
+ case ocChiSqInv_MS : ScChiSqInv(); break;
+ case ocTInv :
+ case ocTInv_2T : ScTInv( 2 ); break;
+ case ocTInv_MS : ScTInv( 4 ); break;
+ case ocFInv :
+ case ocFInv_RT : ScFInv(); break;
+ case ocFInv_LT : ScFInv_LT(); break;
+ case ocLogInv :
+ case ocLogInv_MS : ScLogNormInv(); break;
+ case ocBetaDist : ScBetaDist(); break;
+ case ocBetaDist_MS : ScBetaDist_MS(); break;
+ case ocBetaInv :
+ case ocBetaInv_MS : ScBetaInv(); break;
+ case ocFourier : ScFourier(); break;
+ case ocExternal : ScExternal(); break;
+ case ocTableOp : ScTableOp(); break;
+ case ocStop : break;
+ case ocErrorType : ScErrorType(); break;
+ case ocErrorType_ODF : ScErrorType_ODF(); break;
+ case ocCurrent : ScCurrent(); break;
+ case ocStyle : ScStyle(); break;
+ case ocDde : ScDde(); break;
+ case ocBase : ScBase(); break;
+ case ocDecimal : ScDecimal(); break;
+ case ocConvertOOo : ScConvertOOo(); break;
+ case ocEuroConvert : ScEuroConvert(); break;
+ case ocRoman : ScRoman(); break;
+ case ocArabic : ScArabic(); break;
+ case ocInfo : ScInfo(); break;
+ case ocHyperLink : ScHyperLink(); break;
+ case ocBahtText : ScBahtText(); break;
+ case ocGetPivotData : ScGetPivotData(); break;
+ case ocJis : ScJis(); break;
+ case ocAsc : ScAsc(); break;
+ case ocLenB : ScLenB(); break;
+ case ocRightB : ScRightB(); break;
+ case ocLeftB : ScLeftB(); break;
+ case ocMidB : ScMidB(); break;
+ case ocReplaceB : ScReplaceB(); break;
+ case ocFindB : ScFindB(); break;
+ case ocSearchB : ScSearchB(); break;
+ case ocUnicode : ScUnicode(); break;
+ case ocUnichar : ScUnichar(); break;
+ case ocBitAnd : ScBitAnd(); break;
+ case ocBitOr : ScBitOr(); break;
+ case ocBitXor : ScBitXor(); break;
+ case ocBitRshift : ScBitRshift(); break;
+ case ocBitLshift : ScBitLshift(); break;
+ case ocTTT : ScTTT(); break;
+ case ocDebugVar : ScDebugVar(); break;
+ case ocNone : nFuncFmtType = SvNumFormatType::UNDEFINED; break;
+ default : PushError( FormulaError::UnknownOpCode); break;
+ }
+
+ // If the function pushed a subroutine as result, continue with
+ // execution of the subroutine.
+ if (sp > nStackBase && pStack[sp-1]->GetOpCode() == ocCall)
+ {
+ Pop(); continue;
+ }
+
+ if (FormulaCompiler::IsOpCodeVolatile(eOp))
+ meVolatileType = VOLATILE;
+
+ // Remember result matrix in case it could be reused.
+ if (pTokenMatrixMap && sp && GetStackType() == svMatrix)
+ pTokenMatrixMap->emplace(pCur, pStack[sp-1]);
+
+ // outer function determines format of an expression
+ if ( nFuncFmtType != SvNumFormatType::UNDEFINED )
+ {
+ nRetTypeExpr = nFuncFmtType;
+ // Inherit the format index for currency, date or time formats.
+ switch (nFuncFmtType)
+ {
+ case SvNumFormatType::CURRENCY:
+ case SvNumFormatType::DATE:
+ case SvNumFormatType::TIME:
+ case SvNumFormatType::DATETIME:
+ case SvNumFormatType::DURATION:
+ nRetIndexExpr = nFuncFmtIndex;
+ break;
+ default:
+ nRetIndexExpr = 0;
+ }
+ }
+ }
+
+ // Need a clean stack environment for the JumpMatrix to work.
+ if (nGlobalError != FormulaError::NONE && eOp != ocPush && sp > nStackBase + 1)
+ {
+ // Not all functions pop all parameters in case an error is
+ // generated. Clean up stack. Assumes that every function pushes a
+ // result, may be arbitrary in case of error.
+ FormulaConstTokenRef xLocalResult = pStack[ sp - 1 ];
+ while (sp > nStackBase)
+ Pop();
+ PushTokenRef( xLocalResult );
+ }
+
+ bool bGotResult;
+ do
+ {
+ bGotResult = false;
+ sal_uInt8 nLevel = 0;
+ if ( GetStackType( ++nLevel ) == svJumpMatrix )
+ ; // nothing
+ else if ( GetStackType( ++nLevel ) == svJumpMatrix )
+ ; // nothing
+ else
+ nLevel = 0;
+ if ( nLevel == 1 || (nLevel == 2 && aCode.IsEndOfPath()) )
+ {
+ if (nLevel == 1)
+ aErrorFunctionStack.push_back( nErrorFunction);
+ bGotResult = JumpMatrix( nLevel );
+ if (aErrorFunctionStack.empty())
+ assert(!"ScInterpreter::Interpret - aErrorFunctionStack empty in JumpMatrix context");
+ else
+ {
+ nErrorFunction = aErrorFunctionStack.back();
+ if (bGotResult)
+ aErrorFunctionStack.pop_back();
+ }
+ }
+ else
+ pJumpMatrix = nullptr;
+ } while ( bGotResult );
+
+ if( IsErrFunc(eOp) )
+ ++nErrorFunction;
+
+ if ( nGlobalError != FormulaError::NONE )
+ {
+ if ( !nErrorFunctionCount )
+ { // count of errorcode functions in formula
+ FormulaTokenArrayPlainIterator aIter(*pArr);
+ for ( FormulaToken* t = aIter.FirstRPN(); t; t = aIter.NextRPN() )
+ {
+ if ( IsErrFunc(t->GetOpCode()) )
+ ++nErrorFunctionCount;
+ }
+ }
+ if ( nErrorFunction >= nErrorFunctionCount )
+ ++nErrorFunction; // that's it, error => terminate
+ }
+ }
+
+ // End: obtain result
+
+ bool bForcedResultType;
+ switch (eOp)
+ {
+ case ocGetDateValue:
+ case ocGetTimeValue:
+ // Force final result of DATEVALUE and TIMEVALUE to number type,
+ // which so far was date or time for calculations.
+ nRetTypeExpr = nFuncFmtType = SvNumFormatType::NUMBER;
+ nRetIndexExpr = nFuncFmtIndex = 0;
+ bForcedResultType = true;
+ break;
+ default:
+ bForcedResultType = false;
+ }
+
+ if (sp == 1)
+ {
+ pCur = pStack[ sp-1 ];
+ if( pCur->GetOpCode() == ocPush )
+ {
+ // An svRefList can be resolved if it a) contains just one
+ // reference, or b) in array context contains an array of single
+ // cell references.
+ if (pCur->GetType() == svRefList)
+ {
+ PopRefListPushMatrixOrRef();
+ pCur = pStack[ sp-1 ];
+ }
+ switch( pCur->GetType() )
+ {
+ case svEmptyCell:
+ ; // nothing
+ break;
+ case svError:
+ nGlobalError = pCur->GetError();
+ break;
+ case svDouble :
+ {
+ // If typed, pop token to obtain type information and
+ // push a plain untyped double so the result token to
+ // be transferred to the formula cell result does not
+ // unnecessarily duplicate the information.
+ if (pCur->GetDoubleType() != 0)
+ {
+ double fVal = PopDouble();
+ if (!bForcedResultType)
+ {
+ if (nCurFmtType != nFuncFmtType)
+ nRetIndexExpr = 0; // carry format index only for matching type
+ nRetTypeExpr = nFuncFmtType = nCurFmtType;
+ }
+ if (nRetTypeExpr == SvNumFormatType::DURATION)
+ {
+ // Round the duration in case a wall clock time
+ // display format is used instead of a duration
+ // format. To micro seconds which then catches
+ // the converted hh:mm:ss.9999997 cases.
+ if (fVal != 0.0)
+ {
+ fVal *= 86400.0;
+ fVal = rtl::math::round( fVal, 6);
+ fVal /= 86400.0;
+ }
+ }
+ PushTempToken( CreateFormulaDoubleToken( fVal));
+ }
+ if ( nFuncFmtType == SvNumFormatType::UNDEFINED )
+ {
+ nRetTypeExpr = SvNumFormatType::NUMBER;
+ nRetIndexExpr = 0;
+ }
+ }
+ break;
+ case svString :
+ nRetTypeExpr = SvNumFormatType::TEXT;
+ nRetIndexExpr = 0;
+ break;
+ case svSingleRef :
+ {
+ ScAddress aAdr;
+ PopSingleRef( aAdr );
+ if( nGlobalError == FormulaError::NONE)
+ PushCellResultToken( false, aAdr, &nRetTypeExpr, &nRetIndexExpr, true);
+ }
+ break;
+ case svRefList :
+ PopError(); // maybe #REF! takes precedence over #VALUE!
+ PushError( FormulaError::NoValue);
+ break;
+ case svDoubleRef :
+ {
+ if ( bMatrixFormula )
+ { // create matrix for {=A1:A5}
+ PopDoubleRefPushMatrix();
+ ScMatrixRef xMat = PopMatrix();
+ QueryMatrixType(xMat, nRetTypeExpr, nRetIndexExpr);
+ }
+ else
+ {
+ ScRange aRange;
+ PopDoubleRef( aRange );
+ ScAddress aAdr;
+ if ( nGlobalError == FormulaError::NONE && DoubleRefToPosSingleRef( aRange, aAdr))
+ PushCellResultToken( false, aAdr, &nRetTypeExpr, &nRetIndexExpr, true);
+ }
+ }
+ break;
+ case svExternalDoubleRef:
+ {
+ ScMatrixRef xMat;
+ PopExternalDoubleRef(xMat);
+ QueryMatrixType(xMat, nRetTypeExpr, nRetIndexExpr);
+ }
+ break;
+ case svMatrix :
+ {
+ sc::RangeMatrix aMat = PopRangeMatrix();
+ if (aMat.isRangeValid())
+ {
+ // This matrix represents a range reference. Apply implicit intersection.
+ double fVal = applyImplicitIntersection(aMat, aPos);
+ if (std::isnan(fVal))
+ PushNoValue();
+ else
+ PushInt(fVal);
+ }
+ else
+ // This is a normal matrix.
+ QueryMatrixType(aMat.mpMat, nRetTypeExpr, nRetIndexExpr);
+ }
+ break;
+ case svExternalSingleRef:
+ {
+ FormulaTokenRef xToken;
+ ScExternalRefCache::CellFormat aFmt;
+ PopExternalSingleRef(xToken, &aFmt);
+ if (nGlobalError != FormulaError::NONE)
+ break;
+
+ PushTokenRef(xToken);
+
+ if (aFmt.mbIsSet)
+ {
+ nFuncFmtType = aFmt.mnType;
+ nFuncFmtIndex = aFmt.mnIndex;
+ }
+ }
+ break;
+ default :
+ SetError( FormulaError::UnknownStackVariable);
+ }
+ }
+ else
+ SetError( FormulaError::UnknownStackVariable);
+ }
+ else if (sp > 1)
+ SetError( FormulaError::OperatorExpected);
+ else
+ SetError( FormulaError::NoCode);
+
+ if (bForcedResultType || nRetTypeExpr != SvNumFormatType::UNDEFINED)
+ {
+ nRetFmtType = nRetTypeExpr;
+ nRetFmtIndex = nRetIndexExpr;
+ }
+ else if( nFuncFmtType != SvNumFormatType::UNDEFINED )
+ {
+ nRetFmtType = nFuncFmtType;
+ nRetFmtIndex = nFuncFmtIndex;
+ }
+ else
+ nRetFmtType = SvNumFormatType::NUMBER;
+
+ if (nGlobalError != FormulaError::NONE && GetStackType() != svError )
+ PushError( nGlobalError);
+
+ // THE final result.
+ xResult = PopToken();
+ if (!xResult)
+ xResult = new FormulaErrorToken( FormulaError::UnknownStackVariable);
+
+ // release tokens in expression stack
+ const FormulaToken** p = pStack;
+ while( maxsp-- )
+ (*p++)->DecRef();
+
+ StackVar eType = xResult->GetType();
+ if (eType == svMatrix)
+ // Results are immutable in case they would be reused as input for new
+ // interpreters.
+ xResult->GetMatrix()->SetImmutable();
+ return eType;
+}
+
+void ScInterpreter::AssertFormulaMatrix()
+{
+ bMatrixFormula = true;
+}
+
+svl::SharedString ScInterpreter::GetStringResult() const
+{
+ return xResult->GetString();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */