/* -*- 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));
}
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.