/* -*- 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 .
*/
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) 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;
}
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();
// set dirty again once more to be able to recalculate original for ( constauto& 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 ( constauto& 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: returntrue; default: returnfalse;
}
}
// 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 );
}
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 elseif (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;
}
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.113Bemerkung:
(vorverarbeitet am 2026-04-28)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.