bool ScInterpreter::IsTableOpInRange( const ScRange& rRange )
{ if ( rRange.aStart == rRange.aEnd ) returnfalse; // not considered to be a range in TableOp sense
// we can't replace a single cell in a range
size_t ListSize = mrDoc.m_TableOpList.size(); for ( size_t i = 0; i < ListSize; ++i )
{
ScInterpreterTableOpParams *const pTOp = mrDoc.m_TableOpList[ i ]; if ( rRange.Contains( pTOp->aOld1 ) ) returntrue; if ( rRange.Contains( pTOp->aOld2 ) ) returntrue;
} returnfalse;
}
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());
}
}
if (rRef.IsTabRel())
{
OSL_FAIL("ScCompiler::GetToken: external single reference must have an absolute table reference!");
SetError(FormulaError::NoRef); 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 = mrDoc.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;
}
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( mrDoc.GetSheetLimits(), (*pv)[0] ));
} elseif (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 (constauto & 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(mrDoc, aAdr); if (aCell.hasError())
xMat->PutError( aCell.getFormula()->GetErrCode(), 0, i); elseif (aCell.hasEmptyValue())
xMat->PutEmpty( 0, i); elseif (aCell.hasString())
xMat->PutString( mrStrPool.intern( aCell.getString(mrDoc)), 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();
}
bool ScInterpreter::ConvertMatrixParameters()
{
sal_uInt16 nParams = pCur->GetParamCount();
SAL_WARN_IF( nParams > sp, "sc.core", "ConvertMatrixParameters: stack/param count mismatch: eOp: "
<< static_cast<int>(pCur->GetOpCode()) << " sp: " << sp << " nParams: " << nParams);
assert(nParams <= sp);
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)
{
assert(!"ConvertMatrixParameters: not a push");
} else
{ switch ( p->GetType() )
{ case svDouble: case svString: case svStringName: 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( std::move(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( std::move(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::NoRef);
} // else: the consuming function has to decide if and how to // handle a reference list argument in array context.
} break; default:
assert(!"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 ((aMapIter = maTokenMatrixMap.find( pCur)) != maTokenMatrixMap.end())
xNew = (*aMapIter).second; else
{
std::shared_ptr<ScJumpMatrix> pJumpMat; try
{
pJumpMat = std::make_shared<ScJumpMatrix>( pCur->GetOpCode(), nJumpCols, nJumpRows);
} catch (const std::bad_alloc&)
{
SAL_WARN("sc.core", "std::bad_alloc in ScJumpMatrix ctor with " << nJumpCols << " columns and " << nJumpRows << " rows"); returnfalse;
}
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); returntrue;
} returnfalse;
}
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;
}
void ScInterpreter::PushMatrix( const sc::RangeMatrix& rMat )
{ if (!rMat.isRangeValid())
{ // Just push the matrix part only.
PushMatrix(rMat.mpMat); return;
}
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));
}
bOk = ScCompiler::DoubleRefToPosSingleRefScalarCase(rRange, rAdr, aPos);
if ( !bOk )
SetError( FormulaError::NoValue ); return bOk;
}
double ScInterpreter::GetDoubleFromMatrix(const ScMatrixRef& pMat)
{ if (!pMat) return0.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;
}
pQueryParam->mbSkipString = false;
ScDBQueryDataIterator aValIter(mrDoc, 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( pCur->GetExternal().toAsciiUpperCase()); // programmatic name
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] = newchar[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 ( !aCall.ValidParamCount() )
SetError( FormulaError::IllegalParameter );
if ( aCall.NeedsCaller() && GetError() == FormulaError::NONE )
{
ScDocShell* pShell = mrDoc.GetDocumentShell(); if (pShell)
aCall.SetCallerFromObjectShell( pShell ); else
{ // use temporary model object (without document) to supply options
aCall.SetCaller( static_cast<beans::XPropertySet*>( new ScDocOptionsObj( mrDoc.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
}
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, &mrDoc ); if (pMyFormulaCell)
pMyFormulaCell->StartListening( *pLis ); else
bTemporaryListener = true;
} elseif (pMyFormulaCell)
{
pMyFormulaCell->StartListening( *pLis ); if ( !pLis->HasDocument( &mrDoc ) )
{
pLis->AddDocument( &mrDoc );
}
}
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&)
{
}
}
}
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(u"CodeName"_ustr) >>= 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();
staticbool 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(); returntrue; case SbxBOOL:
fVal = (pVar->GetBool() ? 1.0 : 0.0); returntrue; default:
; // nothing
} returnfalse;
}
#endif
void ScInterpreter::ScMacro()
{
#if !HAVE_FEATURE_SCRIPTING
PushNoValue(); // without DocShell no CallBasic return; #else
SbxBase::ResetError();
auto const itr =
::std::find(mrDoc.m_TableOpList.begin(), mrDoc.m_TableOpList.end(), &aTableOp);
if (itr != mrDoc.m_TableOpList.end())
{
mrDoc.m_TableOpList.erase(itr);
}
// set dirty again once more to be able to recalculate original
for ( const auto& pCell : aTableOp.aNotifiedFormulaCells )
{
pCell->SetTableOpDirty();
}
// save these params for next incarnation
if ( !bReuseLastParams )
mrDoc.aLastTableOpParams = aTableOp;
if (aCell.getType() == CELLTYPE_FORMULA)
{
aCell.getFormula()->SetDirtyVar();
aCell.getFormula()->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 : aTableOp.aNotifiedFormulaCells )
{
pCell->ResetTableOpDirtyVar();
}
// 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();
mrDoc.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);
if ( aPos.Col() == nStartCol )
{
SCROW 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 > mrDoc.MaxRow() )
nStartRow = mrDoc.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);
if ( aPos.Row() == nStartRow )
{
SCCOL 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 > mrDoc.MaxCol() )
nStartCol = mrDoc.MaxCol();
aAbs.aStart.SetCol(nStartCol);
}
else
{ // below the name to the formula cell
aAbs.aEnd.SetCol(nMyCol - 1);
}
}
}
}
aRefData.SetRange(mrDoc.GetSheetLimits(), aAbs, aPos);
PushTempToken( new ScDoubleRefToken( mrDoc.GetSheetLimits(), aRefData ) );
}
// Let's 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;
}
// 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:
case ocXLookup:
return true;
default:
return false;
}
}
// 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();
if (bIsOpCodeJumpCommand)
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
{
const sal_uInt8 nParamCount = pCur->GetParamCount();
if (sp >= nParamCount)
nStackBase = sp - nParamCount;
else
{
SAL_WARN("sc.core", "Stack anomaly at " << aPos.Tab() << "," << aPos.Col() << "," << aPos.Row()
<< " " << aPos.Format(
ScRefFlags::VALID | ScRefFlags::FORCE_DOC | ScRefFlags::TAB_3D, &mrDoc)
<< " eOp: " << static_cast<int>(eOp)
<< " params: " << static_cast<int>(nParamCount)
<< " nStackBase: " << nStackBase << " sp: " << sp);
nStackBase = sp;
assert(!"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 ocChooseCols : ScChooseCols(); break;
case ocChooseRows : ScChooseRows(); 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 ocRandArray : ScRandArray(); break;
case ocRandomNV : ScRandom(); break;
case ocRandbetweenNV : ScRandbetween(); break;
case ocFilter : ScFilter(); break;
case ocSort : ScSort(); break;
case ocSortBy : ScSortBy(); break;
case ocDrop : ScDrop(); break;
case ocExpand : ScExpand(); break;
case ocHStack : ScHStack(); break;
case ocVStack : ScVStack(); break;
case ocTake : ScTake(); break;
case ocTextAfter : ScTextAfter(); break;
case ocTextBefore : ScTextBefore(); break;
case ocTextSplit : ScTextSplit(); break;
case ocToCol : ScToCol(); break;
case ocToRow : ScToRow(); break;
case ocUnique : ScUnique(); break;
case ocLet : ScLet(); break;
case ocWrapCols : ScWrapCols(); break;
case ocWrapRows : ScWrapRows(); 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 ocXMatch : ScXMatch(); 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 ocXLookup : ScXLookup(); 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 ocMatSequence : ScMatSequence(); 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 (sp && GetStackType() == svMatrix)
maTokenMatrixMap.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
else if (nErrorFunctionCount && sp && GetStackType() == svError)
{
// Clear global error if we have an individual error result, so
// an error evaluating function can receive multiple arguments
// and not all evaluated arguments inheriting the error.
// This is important for at least IFS() and SWITCH() as long as
// they are classified as error evaluating functions and not
// implemented as short-cutting jump code paths, but also for
// more than one evaluated argument to AGGREGATE() or COUNT()
// that may ignore errors.
nGlobalError = FormulaError::NONE;
}
}
}
// 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;
// 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;
}