/* -*- 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 .
*/
/** Store a matrix value in another matrix in the context of that other matrix is the result matrix of a jump matrix. All arguments must be valid and are
not checked. */ staticvoid lcl_storeJumpMatResult( const ScMatrix* pMat, ScJumpMatrix* pJumpMat, SCSIZE nC, SCSIZE nR )
{ if ( pMat->IsValue( nC, nR ) )
{ double fVal = pMat->GetDouble( nC, nR );
pJumpMat->PutResultDouble( fVal, nC, nR );
} elseif ( pMat->IsEmpty( nC, nR ) )
{
pJumpMat->PutResultEmpty( nC, nR );
} else
{
pJumpMat->PutResultString(pMat->GetString(nC, nR), nC, nR);
}
}
void ScInterpreter::ScIfError( bool bNAonly )
{ constshort* pJump = pCur->GetJump(); short nJumpCount = pJump[ 0 ]; if (!sp || nJumpCount != 2)
{ // Reset nGlobalError here to not propagate the old error, if any.
nGlobalError = (sp ? FormulaError::ParameterExpected : FormulaError::UnknownStackVariable);
PushError( nGlobalError);
aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] ); return;
}
formula::ParamClass eReturnType = ScParameterClassification::GetParameterType( pCur, SAL_MAX_UINT16); if (eReturnType == ParamClass::Reference)
{ /* TODO: What about error handling and do we actually
* need the result matrix above at all in this case? */
ScComplexRefData aRef;
aRef.Ref1 = aRef.Ref2 = *(xRef->GetSingleRef());
pJumpMatrix->GetRefList().push_back( aRef);
}
} break; case svDoubleRef:
{ // upper left plus offset within matrix
FormulaConstTokenRef xRef = pStack[sp-1]; double fVal;
ScRange aRange;
PopDoubleRef( aRange ); if ( nGlobalError != FormulaError::NONE )
{
fVal = CreateDoubleError( nGlobalError );
nGlobalError = FormulaError::NONE;
pJumpMatrix->PutResultDouble( fVal, nC, nR );
} else
{ // Do not modify the original range because we use it // to adjust the size of the result matrix if necessary.
ScAddress aAdr( aRange.aStart);
sal_uLong nCol = static_cast<sal_uLong>(aAdr.Col()) + nC;
sal_uLong nRow = static_cast<sal_uLong>(aAdr.Row()) + nR; if ((nCol > o3tl::make_unsigned(aRange.aEnd.Col()) &&
aRange.aEnd.Col() != aRange.aStart.Col())
|| (nRow > o3tl::make_unsigned(aRange.aEnd.Row()) &&
aRange.aEnd.Row() != aRange.aStart.Row()))
{
fVal = CreateDoubleError( FormulaError::NotAvailable );
pJumpMatrix->PutResultDouble( fVal, nC, nR );
} else
{ // Replicate column and/or row of a vector if it is // one. Note that this could be a range reference // that in fact consists of only one cell, e.g. A1:A1 if (aRange.aEnd.Col() == aRange.aStart.Col())
nCol = aRange.aStart.Col(); if (aRange.aEnd.Row() == aRange.aStart.Row())
nRow = aRange.aStart.Row();
aAdr.SetCol( static_cast<SCCOL>(nCol) );
aAdr.SetRow( static_cast<SCROW>(nRow) );
ScRefCellValue aCell(mrDoc, aAdr); if (aCell.hasEmptyValue())
pJumpMatrix->PutResultEmpty( nC, nR ); elseif (aCell.hasNumeric())
{ double fCellVal = GetCellValue(aAdr, aCell); if ( nGlobalError != FormulaError::NONE )
{
fCellVal = CreateDoubleError(
nGlobalError);
nGlobalError = FormulaError::NONE;
}
pJumpMatrix->PutResultDouble( fCellVal, nC, nR );
} else
{
svl::SharedString aStr;
GetCellString(aStr, aCell); if ( nGlobalError != FormulaError::NONE )
{
pJumpMatrix->PutResultDouble( CreateDoubleError(
nGlobalError), nC, nR);
nGlobalError = FormulaError::NONE;
} else
pJumpMatrix->PutResultString(aStr, nC, nR);
}
}
SCSIZE nParmCols = aRange.aEnd.Col() - aRange.aStart.Col() + 1;
SCSIZE nParmRows = aRange.aEnd.Row() - aRange.aStart.Row() + 1;
lcl_AdjustJumpMatrix( pJumpMatrix, nParmCols, nParmRows );
}
formula::ParamClass eReturnType = ScParameterClassification::GetParameterType( pCur, SAL_MAX_UINT16); if (eReturnType == ParamClass::Reference)
{ /* TODO: What about error handling and do we actually
* need the result matrix above at all in this case? */
pJumpMatrix->GetRefList().push_back( *(xRef->GetDoubleRef()));
}
} break; case svExternalSingleRef:
{
ScExternalRefCache::TokenRef pToken;
PopExternalSingleRef(pToken); if (nGlobalError != FormulaError::NONE)
{
pJumpMatrix->PutResultDouble( CreateDoubleError( nGlobalError), nC, nR );
nGlobalError = FormulaError::NONE;
} else
{ switch (pToken->GetType())
{ case svDouble:
pJumpMatrix->PutResultDouble( pToken->GetDouble(), nC, nR ); break; case svString:
pJumpMatrix->PutResultString( pToken->GetString(), nC, nR ); break; case svEmptyCell:
pJumpMatrix->PutResultEmpty( nC, nR ); break; default: // svError was already handled (set by // PopExternalSingleRef()) with nGlobalError // above.
assert(!"unhandled svExternalSingleRef case");
pJumpMatrix->PutResultDouble( CreateDoubleError(
FormulaError::UnknownStackVariable), nC, nR );
}
}
} break; case svExternalDoubleRef: case svMatrix:
{ // match matrix offsets double fVal;
ScMatrixRef pMat = GetMatrix(); if ( nGlobalError != FormulaError::NONE )
{
fVal = CreateDoubleError( nGlobalError );
nGlobalError = FormulaError::NONE;
pJumpMatrix->PutResultDouble( fVal, nC, nR );
} elseif ( !pMat )
{
fVal = CreateDoubleError( FormulaError::UnknownVariable );
pJumpMatrix->PutResultDouble( fVal, nC, nR );
} else
{
SCSIZE nCols, nRows;
pMat->GetDimensions( nCols, nRows ); if ((nCols <= nC && nCols != 1) ||
(nRows <= nR && nRows != 1))
{
fVal = CreateDoubleError( FormulaError::NotAvailable );
pJumpMatrix->PutResultDouble( fVal, nC, nR );
} else
{ // GetMatrix() does SetErrorInterpreter() at the // matrix, do not propagate an error from // matrix->GetValue() as global error.
pMat->SetErrorInterpreter(nullptr);
lcl_storeJumpMatResult(pMat.get(), pJumpMatrix, nC, nR);
}
lcl_AdjustJumpMatrix( pJumpMatrix, nCols, nRows );
}
} break; case svError:
{
PopError(); double fVal = CreateDoubleError( nGlobalError);
nGlobalError = FormulaError::NONE;
pJumpMatrix->PutResultDouble( fVal, nC, nR );
} break; default:
{
Pop(); double fVal = CreateDoubleError( FormulaError::IllegalArgument);
pJumpMatrix->PutResultDouble( fVal, nC, nR );
}
}
}
} bool bCont = pJumpMatrix->Next( nC, nR ); if ( bCont )
{ double fBool; short nStart, nNext, nStop;
pJumpMatrix->GetJump( nC, nR, fBool, nStart, nNext, nStop ); while ( bCont && nStart == nNext )
{ // push all results that have no jump path if ( bHasResMat && (GetDoubleErrorValue( fBool) != FormulaError::JumpMatHasResult) )
{ // a false without path results in an empty path value if ( fBool == 0.0 )
pJumpMatrix->PutResultEmptyPath( nC, nR ); else
pJumpMatrix->PutResultDouble( fBool, nC, nR );
}
bCont = pJumpMatrix->Next( nC, nR ); if ( bCont )
pJumpMatrix->GetJump( nC, nR, fBool, nStart, nNext, nStop );
} if ( bCont && nStart != nNext )
{ const ScTokenVec & rParams = pJumpMatrix->GetJumpParameters(); for ( autoconst & i : rParams )
{ // This is not the current state of the interpreter, so // push without error, and elements' errors are coded into // double.
PushWithoutError(*i);
}
aCode.Jump( nStart, nNext, nStop );
}
} if ( !bCont )
{ // We're done with it, throw away jump matrix, keep result. // For an intermediate result of Reference use the array of references // if there are more than one reference and the current ForceArray // context is ReferenceOrRefArray. // Else (also for a final result of Reference) use the matrix. // Treat the result of a jump command as final and use the matrix (see // tdf#115493 for why). if (pCur->GetInForceArray() == ParamClass::ReferenceOrRefArray &&
pJumpMatrix->GetRefList().size() > 1 &&
ScParameterClassification::GetParameterType( pCur, SAL_MAX_UINT16) == ParamClass::Reference &&
!FormulaCompiler::IsOpCodeJumpCommand( pJumpMatrix->GetOpCode()) &&
aCode.PeekNextOperator())
{
FormulaTokenRef xRef = new ScRefListToken(true);
*(xRef->GetRefList()) = pJumpMatrix->GetRefList();
pJumpMatrix = nullptr;
Pop();
PushTokenRef( xRef);
maTokenMatrixMap.erase( pCur); // There's no result matrix to remember in this case.
} else
{
ScMatrix* pResMat = pJumpMatrix->GetResultMatrix();
pJumpMatrix = nullptr;
Pop();
PushMatrix( pResMat ); // Remove jump matrix from map and remember result matrix in case it // could be reused in another path of the same condition.
maTokenMatrixMap.erase( pCur);
maTokenMatrixMap.emplace(pCur, pStack[sp-1]);
} returntrue;
} returnfalse;
}
double ScInterpreter::Compare( ScQueryOp eOp )
{
sc::Compare aComp;
aComp.meOp = eOp;
aComp.mbIgnoreCase = mrDoc.GetDocOptions().IsIgnoreCase(); for( short i = 1; i >= 0; i-- )
{
sc::Compare::Cell& rCell = aComp.maCells[i];
switch ( GetRawStackType() )
{ case svEmptyCell:
Pop();
rCell.mbEmpty = true; break; case svMissing: case svDouble:
rCell.mfValue = GetDouble();
rCell.mbValue = true; break; case svString:
rCell.maStr = GetString();
rCell.mbValue = false; break; case svDoubleRef : case svSingleRef :
{
ScAddress aAdr; if ( !PopDoubleRefOrSingleRef( aAdr ) ) break;
ScRefCellValue aCell(mrDoc, aAdr); if (aCell.hasEmptyValue())
rCell.mbEmpty = true; elseif (aCell.hasString())
{
svl::SharedString aStr;
GetCellString(aStr, aCell);
rCell.maStr = std::move(aStr);
rCell.mbValue = false;
} else
{
rCell.mfValue = GetCellValue(aAdr, aCell);
rCell.mbValue = true;
}
} break; case svExternalSingleRef:
{
ScMatrixRef pMat = GetMatrix(); if (!pMat)
{
SetError( FormulaError::IllegalParameter); break;
}
SCSIZE nC, nR;
pMat->GetDimensions(nC, nR); if (!nC || !nR)
{
SetError( FormulaError::IllegalParameter); break;
} if (pMat->IsEmpty(0, 0))
rCell.mbEmpty = true; elseif (pMat->IsStringOrEmpty(0, 0))
{
rCell.maStr = pMat->GetString(0, 0);
rCell.mbValue = false;
} else
{
rCell.mfValue = pMat->GetDouble(0, 0);
rCell.mbValue = true;
}
} break; case svExternalDoubleRef: // TODO: Find out how to handle this... // Xcl generates a position dependent intersection using // col/row, as it seems to do for all range references, not // only in compare context. We'd need a general implementation // for that behavior similar to svDoubleRef in scalar and array // mode. Which also means we'd have to change all places where // it currently is handled along with svMatrix. default:
PopError();
SetError( FormulaError::IllegalParameter); break;
}
} if( nGlobalError != FormulaError::NONE ) return 0;
nCurFmtType = nFuncFmtType = SvNumFormatType::LOGICAL; return sc::CompareFunc(aComp);
}
void ScInterpreter::ScRandomImpl( const std::function<double( double fFirst, double fLast )>& RandomFunc, double fFirst, double fLast )
{ if (bMatrixFormula)
{
SCCOL nCols = 0;
SCROW nRows = 0; // In JumpMatrix context use its dimensions for the return matrix; the // formula cell range selected may differ, for example if the result is // to be transposed. if (GetStackType(1) == svJumpMatrix)
{
SCSIZE nC, nR;
pStack[sp-1]->GetJumpMatrix()->GetDimensions( nC, nR);
nCols = std::max<SCCOL>(0, static_cast<SCCOL>(nC));
nRows = std::max<SCROW>(0, static_cast<SCROW>(nR));
} elseif (pMyFormulaCell)
pMyFormulaCell->GetMatColsRows( nCols, nRows);
if (nCols == 1 && nRows == 1)
{ // For compatibility with existing // com.sun.star.sheet.FunctionAccess.callFunction() calls that per // default are executed in array context unless // FA.setPropertyValue("IsArrayFunction",False) was set, return a // scalar double instead of a 1x1 matrix object. tdf#128218
PushDouble( RandomFunc( fFirst, fLast)); return;
}
// ScViewFunc::EnterMatrix() might be asking for // ScFormulaCell::GetResultDimensions(), which here are none so create // a 1x1 matrix at least which exactly is the case when EnterMatrix() // asks for a not selected range. if (nCols == 0)
nCols = 1; if (nRows == 0)
nRows = 1;
ScMatrixRef pResMat = GetNewMat( static_cast<SCSIZE>(nCols), static_cast<SCSIZE>(nRows), /*bEmpty*/true ); if (!pResMat)
PushError( FormulaError::MatrixSize); else
{ for (SCCOL i=0; i < nCols; ++i)
{ for (SCROW j=0; j < nRows; ++j)
{
pResMat->PutDouble( RandomFunc( fFirst, fLast), static_cast<SCSIZE>(i), static_cast<SCSIZE>(j));
}
}
PushMatrix( pResMat);
}
} else
{
PushDouble( RandomFunc( fFirst, fLast));
}
}
void ScInterpreter::ScRandArray()
{
sal_uInt8 nParamCount = GetByte(); // optional 5th para: // TRUE for a whole number // FALSE for a decimal number - default. bool bWholeNumber = false; if (nParamCount == 5)
bWholeNumber = GetBoolWithDefault(false);
// optional 4th para: The maximum value of the random numbers double fMax = 1.0; if (nParamCount >= 4)
fMax = GetDoubleWithDefault(1.0);
// optional 3rd para: The minimum value of the random numbers double fMin = 0.0; if (nParamCount >= 3)
fMin = GetDoubleWithDefault(0.0);
// optional 2nd para: The number of columns of the return array
SCCOL nCols = 1; if (nParamCount >= 2)
nCols = static_cast<SCCOL>(GetInt32WithDefault(1));
// optional 1st para: The number of rows of the return array
SCROW nRows = 1; if (nParamCount >= 1)
nRows = static_cast<SCROW>(GetInt32WithDefault(1));
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.