/* -*- 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 .
*/
sal_uInt8 FormulaToken::GetParamCount() const
{ if ( eOp < SC_OPCODE_STOP_DIV && eOp != ocExternal && eOp != ocMacro &&
!FormulaCompiler::IsOpCodeJumpCommand( eOp ) &&
eOp != ocPercentSign ) return 0; // parameters and specials // ocIf... jump commands not for FAP, have cByte then //2do: bool parameter whether FAP or not? elseif (GetByte()) return GetByte(); // all functions, also ocExternal and ocMacro elseif (SC_OPCODE_START_BIN_OP <= eOp && eOp < SC_OPCODE_STOP_BIN_OP && eOp != ocAnd && eOp != ocOr) return 2; // binary operators, compiler checked; OR and AND legacy but are functions elseif ((SC_OPCODE_START_UN_OP <= eOp && eOp < SC_OPCODE_STOP_UN_OP) || eOp == ocPercentSign) return 1; // unary operators, compiler checked elseif (SC_OPCODE_START_NO_PAR <= eOp && eOp < SC_OPCODE_STOP_NO_PAR) return 0; // no parameter elseif (FormulaCompiler::IsOpCodeJumpCommand( eOp )) return 1; // only the condition counts as parameter else return 0; // all the rest, no Parameter, or // if so then it should be in cByte
}
bool FormulaToken::IsExternalRef() const
{ bool bRet = false; switch (eType)
{ case svExternalSingleRef: case svExternalDoubleRef: case svExternalName:
bRet = true; break; default:
bRet = false; break;
} return bRet;
}
bool FormulaToken::IsRef() const
{ switch (eType)
{ case svSingleRef: case svDoubleRef: case svExternalSingleRef: case svExternalDoubleRef: returntrue; default: if (eOp == ocTableRef) returntrue;
}
ParamClass FormulaToken::GetInForceArray() const
{ // ok to be called for any derived class return (eOp == ocPush && eType == svMatrix) ? ParamClass::ForceArrayReturn : ParamClass::Unknown;
}
FormulaToken* FormulaTokenArray::Add( FormulaToken* t )
{
assert(!mbFinalized); if (mbFinalized)
{
t->DeleteIfZeroRef(); return nullptr;
}
// Allocating an array of size FORMULA_MAXTOKENS is simple, but that results in relatively large // allocations that malloc() implementations usually do not handle as efficiently as smaller // sizes (not only in terms of memory usage but also speed). Since most token arrays are going // to be small, start with a small array and resize only if needed. Eventually Finalize() will // reallocate the memory to size exactly matching the requirements. const size_t MAX_FAST_TOKENS = 32; if( !pCode )
pCode.reset(new FormulaToken*[ MAX_FAST_TOKENS ]); if( nLen == MAX_FAST_TOKENS )
{
FormulaToken** tmp = new FormulaToken*[ FORMULA_MAXTOKENS ];
std::copy(&pCode[0], &pCode[MAX_FAST_TOKENS], tmp);
pCode.reset(tmp);
} if( nLen < FORMULA_MAXTOKENS - 1 )
{
CheckToken(*t);
pCode[ nLen++ ] = t;
t->IncRef(); if( t->GetOpCode() == ocArrayClose ) return MergeArray(); return t;
} else
{
t->DeleteIfZeroRef(); if ( nLen == FORMULA_MAXTOKENS - 1 )
{
t = new FormulaByteToken( ocStop );
pCode[ nLen++ ] = t;
t->IncRef();
} return nullptr;
}
}
void FormulaTokenArray::AddRecalcMode( ScRecalcMode nBits )
{ constunsigned nExclusive = static_cast<sal_uInt8>(nBits & ScRecalcMode::EMask); if (nExclusive)
{ unsigned nExBit; if (nExclusive & (nExclusive - 1))
{ // More than one bit set, use highest priority. for (nExBit = 1; (nExBit & static_cast<sal_uInt8>(ScRecalcMode::EMask)) != 0; nExBit <<= 1)
{ if (nExclusive & nExBit) break;
}
} else
{ // Only one bit is set.
nExBit = nExclusive;
} // Set exclusive bit if priority is higher than existing. if (nExBit < static_cast<sal_uInt8>(nMode & ScRecalcMode::EMask))
SetMaskedRecalcMode( static_cast<ScRecalcMode>(nExBit));
}
SetCombinedBitsRecalcMode( nBits );
}
// RPN-Interpreter simulation. // Simply assumes a double as return value of each function.
std::unique_ptr<FormulaToken*[]> pStack(new FormulaToken* [nRPN]);
FormulaToken* pResult = new FormulaDoubleToken( 0.0 ); short sp = 0; for ( auto t: RPNTokens() )
{
OpCode eOp = t->GetOpCode();
sal_uInt8 nParams = t->GetParamCount(); switch ( eOp )
{ case ocAdd : case ocSub : case ocMul : case ocDiv : case ocPow : case ocPower : case ocAmpersand : case ocEqual : case ocNotEqual : case ocLess : case ocGreater : case ocLessEqual : case ocGreaterEqual :
{ for ( sal_uInt8 k = nParams; k; k-- )
{ if ( sp >= k && pStack[sp-k]->GetType() == svDoubleRef )
{
pResult->Delete(); returntrue;
}
}
} break; default:
{ // added to avoid warnings
}
} if ( eOp == ocPush || lcl_IsReference( eOp, t->GetType() ) )
pStack[sp++] = t; elseif (FormulaCompiler::IsOpCodeJumpCommand( eOp ))
{ // ignore Jumps, pop previous Result (Condition) if ( sp )
--sp;
} else
{ // pop parameters, push result
sp = sal::static_int_cast<short>( sp - nParams ); if ( sp < 0 )
{
SAL_WARN("formula.core", "FormulaTokenArray::HasMatrixDoubleRefOps: sp < 0" );
sp = 0;
}
pStack[sp++] = pResult;
}
}
pResult->Delete();
returnfalse;
}
// --- Formula rewrite of a token array
inlinebool MissingConventionODF::isRewriteNeeded( OpCode eOp ) const
{ switch (eOp)
{ case ocGammaDist: case ocPoissonDist: case ocAddress: case ocLogInv: case ocLogNormDist: case ocNormDist: returntrue; case ocMissing: case ocLog: return isPODF(); // rewrite only for PODF case ocDBCount: case ocDBCount2: return isODFF(); // rewrite only for ODFF default: returnfalse;
}
}
/* fdo 81596 To be implemented yet: ocExternal: ? ocMacro: ? ocIndex: INDEX() ?
*/ inlinebool MissingConventionOOXML::isRewriteNeeded( OpCode eOp )
{ switch (eOp)
{ case ocIf:
case ocExternal: case ocEuroConvert: case ocMacro:
case ocRound: case ocRoundUp: case ocRoundDown:
case ocIndex:
case ocCeil: case ocFloor:
case ocGammaDist: case ocFDist_LT: case ocPoissonDist: case ocNormDist: case ocLogInv: case ocLogNormDist: case ocHypGeomDist:
case ocDBCount: case ocDBCount2: returntrue;
default: returnfalse;
}
}
namespace {
class FormulaMissingContext
{ public: const FormulaToken* mpFunc; int mnCurArg;
/* TODO: with some effort we might be able to merge the two almost * identical function stacks into one and generalize things, otherwise
* adding yet another "omit argument" would be copypasta. */
int aOpCodeAddressStack[ nAlloc ]; // use of ADDRESS() function constint nOmitAddressArg = 3; // ADDRESS() 4th parameter A1/R1C1
int aOpCodeDcountStack[ nAlloc ]; // use of DCOUNT()/DCOUNTA() function constint nOmitDcountArg = 1; // DCOUNT() and DCOUNTA() 2nd parameter DatabaseField if 0
sal_uInt16 nTokens = GetLen() + 1;
FormulaMissingContext* pCtx = (nAlloc < nTokens ? new FormulaMissingContext[nTokens] : &aCtx[0]); int* pOcas = (nAlloc < nTokens ? newint[nTokens] : &aOpCodeAddressStack[0]); int* pOcds = (nAlloc < nTokens ? newint[nTokens] : &aOpCodeDcountStack[0]); // Never go below 0, never use 0, mpFunc always NULL.
pCtx[0].Clear(); int nFn = 0; int nOcas = 0; int nOcds = 0;
FormulaTokenArray *pNewArr = new FormulaTokenArray; // At least ScRecalcMode::ALWAYS needs to be set.
pNewArr->AddRecalcMode( GetRecalcMode());
FormulaTokenArrayPlainIterator aIter(*this); for ( FormulaToken *pCur = aIter.First(); pCur; pCur = aIter.Next() )
{ bool bAdd = true; // Don't write the expression of the new inserted ADDRESS() parameter. // Do NOT omit the new second parameter of INDIRECT() though. If that // was done for both, INDIRECT() actually could calculate different and // valid (but wrong) results with the then changed return value of // ADDRESS(). Better let it generate an error instead. for (int i = nOcas; i-- > 0 && bAdd; )
{ if (pCtx[ pOcas[ i ] ].mnCurArg == nOmitAddressArg)
{ // Omit everything except a trailing separator, the leading // separator is omitted below. The other way around would leave // an extraneous separator if no parameter followed. if (pOcas[ i ] != nFn || pCur->GetOpCode() != ocSep)
bAdd = false;
}
} // Strip the 2nd argument (leaving empty) of DCOUNT() and DCOUNTA() if // it is 0. for (int i = nOcds; i-- > 0 && bAdd; )
{ if (pCtx[ pOcds[ i ] ].mnCurArg == nOmitDcountArg)
{ // Omit only a literal 0 value, nothing else. if (pOcds[ i ] == nFn && pCur->GetOpCode() == ocPush && pCur->GetType() == svDouble &&
pCur->GetDouble() == 0.0)
{ // No other expression, between separators.
FormulaToken* p = aIter.PeekPrevNoSpaces(); if (p && p->GetOpCode() == ocSep)
{
p = aIter.PeekNextNoSpaces(); if (p && p->GetOpCode() == ocSep)
bAdd = false;
}
}
}
} switch ( pCur->GetOpCode() )
{ case ocOpen:
{
++nFn; // all following operations on _that_ function
pCtx[ nFn ].mpFunc = aIter.PeekPrevNoSpaces();
pCtx[ nFn ].mnCurArg = 0; if (rConv.isPODF() && pCtx[ nFn ].mpFunc && pCtx[ nFn ].mpFunc->GetOpCode() == ocAddress)
pOcas[ nOcas++ ] = nFn; // entering ADDRESS() if PODF elseif ((rConv.isODFF() || rConv.isOOXML()) && pCtx[ nFn ].mpFunc)
{
OpCode eOp = pCtx[ nFn ].mpFunc->GetOpCode(); if (eOp == ocDBCount || eOp == ocDBCount2)
pOcds[ nOcds++ ] = nFn; // entering DCOUNT() or DCOUNTA() if ODFF or OOXML
}
} break; case ocClose:
pCtx[ nFn ].AddMoreArgs( pNewArr, rConv );
SAL_WARN_IF(nFn <= 0, "formula.core", "FormulaTokenArray::RewriteMissing: underflow"); if (nOcas > 0 && pOcas[ nOcas-1 ] == nFn)
--nOcas; // leaving ADDRESS() elseif (nOcds > 0 && pOcds[ nOcds-1 ] == nFn)
--nOcds; // leaving DCOUNT() or DCOUNTA() if (nFn > 0)
--nFn; break; case ocSep:
pCtx[ nFn ].mnCurArg++; // Omit leading separator of ADDRESS() parameter. if (nOcas && pOcas[ nOcas-1 ] == nFn && pCtx[ nFn ].mnCurArg == nOmitAddressArg)
{
bAdd = false;
} break; case ocMissing: if ( bAdd )
bAdd = !pCtx[ nFn ].AddMissing( pNewArr, rConv ); break; default: break;
} if (bAdd)
{
OpCode eOp = pCur->GetOpCode(); if ( ( eOp == ocCeil || eOp == ocFloor ||
( eOp == ocLogNormDist && pCur->GetByte() == 4 ) ) &&
rConv.getConvention() == MissingConvention::FORMULA_MISSING_CONVENTION_OOXML )
{ switch ( eOp )
{ case ocCeil :
eOp = ocCeil_Math; break; case ocFloor :
eOp = ocFloor_Math; break; case ocLogNormDist :
eOp = ocLogNormDist_MS; break; default :
eOp = ocNone; break;
}
FormulaToken *pToken = new FormulaToken( svByte, eOp );
pNewArr->Add( pToken );
} elseif ( eOp == ocHypGeomDist &&
rConv.getConvention() == MissingConvention::FORMULA_MISSING_CONVENTION_OOXML )
{
FormulaToken *pToken = new FormulaToken( svByte, ocHypGeomDist_MS );
pNewArr->Add( pToken );
} else
pNewArr->AddToken( *pCur );
}
}
if (pOcds != &aOpCodeDcountStack[0]) delete [] pOcds; if (pOcas != &aOpCodeAddressStack[0]) delete [] pOcas; if (pCtx != &aCtx[0]) delete [] pCtx;
while ( mnIndex < mpFTA->GetLen() )
{
FormulaToken* t = mpFTA->GetArray()[ mnIndex++ ]; switch( t->GetType() )
{ case svSingleRef: case svDoubleRef: case svIndex: case svExternalSingleRef: case svExternalDoubleRef: case svExternalName: return t; default:
{ // added to avoid warnings
}
}
} return nullptr;
}
sal_Int16 FormulaDoubleToken::GetDoubleType() const
{ // This is a plain double value without type information, don't emit a // warning via FormulaToken::GetDoubleType(). return 0;
}
bool FormulaDoubleToken::operator==( const FormulaToken& r ) const
{ return FormulaToken::operator==( r ) && fDouble == r.GetDouble();
}
bool FormulaStringToken::operator==( const FormulaToken& r ) const
{ return FormulaToken::operator==( r ) && maString == r.GetString();
}
FormulaStringOpToken::FormulaStringOpToken( OpCode e, svl::SharedString r ) :
FormulaByteToken( e, 0, svString, ParamClass::Unknown ), maString(std::move( r )) {}
FormulaStringOpToken::FormulaStringOpToken( const FormulaStringOpToken& r ) :
FormulaByteToken( r ), maString( r.maString ) {}
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.