blob: eb9fdcdfafa44092567b5609e89dad975af3bad6 [file] [log] [blame]
/**************************************************************
*
* 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
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_formula.hxx"
// INCLUDE ---------------------------------------------------------------
#include <cstddef>
#include <cstdio>
#include <string.h>
#include <limits.h>
#include <tools/debug.hxx>
#include "formula/token.hxx"
#include "formula/tokenarray.hxx"
#include "formula/FormulaCompiler.hxx"
#include <formula/compiler.hrc>
//#include "rechead.hxx"
//#include "parclass.hxx"
//#include "jumpmatrix.hxx"
#define MAXJUMPCOUNT 32 /* maximum number of jumps (ocChose) */
namespace formula
{
using namespace com::sun::star;
// ImpTokenIterator wird je Interpreter angelegt, mehrfache auch durch
// SubCode via FormulaTokenIterator Push/Pop moeglich
IMPL_FIXEDMEMPOOL_NEWDEL( ImpTokenIterator, 32, 16 )
// Align MemPools on 4k boundaries - 64 bytes (4k is a MUST for OS/2)
// Need a lot of FormulaDoubleToken
const sal_uInt16 nMemPoolDoubleToken = (0x3000 - 64) / sizeof(FormulaDoubleToken);
IMPL_FIXEDMEMPOOL_NEWDEL_DLL( FormulaDoubleToken, nMemPoolDoubleToken, nMemPoolDoubleToken )
// Need a lot of FormulaByteToken
const sal_uInt16 nMemPoolByteToken = (0x3000 - 64) / sizeof(FormulaByteToken);
IMPL_FIXEDMEMPOOL_NEWDEL_DLL( FormulaByteToken, nMemPoolByteToken, nMemPoolByteToken )
// Need several FormulaStringToken
const sal_uInt16 nMemPoolStringToken = (0x1000 - 64) / sizeof(FormulaStringToken);
IMPL_FIXEDMEMPOOL_NEWDEL_DLL( FormulaStringToken, nMemPoolStringToken, nMemPoolStringToken )
// --- helpers --------------------------------------------------------------
inline sal_Bool lcl_IsReference( OpCode eOp, StackVar eType )
{
return
(eOp == ocPush && (eType == svSingleRef || eType == svDoubleRef))
|| (eOp == ocColRowNameAuto && eType == svDoubleRef)
|| (eOp == ocColRowName && eType == svSingleRef)
|| (eOp == ocMatRef && eType == svSingleRef)
;
}
// --- class FormulaToken --------------------------------------------------------
FormulaToken::~FormulaToken()
{
}
sal_Bool FormulaToken::Is3DRef() const
{
return sal_False;
}
sal_Bool FormulaToken::IsFunction() const
{
// OpCode eOp = GetOpCode();
return (eOp != ocPush && eOp != ocBad && eOp != ocColRowName &&
eOp != ocColRowNameAuto && eOp != ocName && eOp != ocDBArea &&
(GetByte() != 0 // x parameters
|| (SC_OPCODE_START_NO_PAR <= eOp && eOp < SC_OPCODE_STOP_NO_PAR) // no parameter
|| (ocIf == eOp || ocChose == eOp ) // @ jump commands
|| (SC_OPCODE_START_1_PAR <= eOp && eOp < SC_OPCODE_STOP_1_PAR) // one parameter
|| (SC_OPCODE_START_2_PAR <= eOp && eOp < SC_OPCODE_STOP_2_PAR) // x parameters (cByte==0 in
// FuncAutoPilot)
|| eOp == ocMacro || eOp == ocExternal // macros, AddIns
|| eOp == ocAnd || eOp == ocOr // former binary, now x parameters
|| eOp == ocNot || eOp == ocNeg // unary but function
|| (eOp >= ocInternalBegin && eOp <= ocInternalEnd) // internal
));
}
sal_uInt8 FormulaToken::GetParamCount() const
{
// OpCode eOp = GetOpCode();
if ( eOp < SC_OPCODE_STOP_DIV && eOp != ocExternal && eOp != ocMacro &&
eOp != ocIf && eOp != ocChose && eOp != ocPercentSign )
return 0; // parameters and specials
// ocIf and ocChose not for FAP, have cByte then
//2do: sal_Bool parameter whether FAP or not?
else if ( GetByte() )
return GetByte(); // all functions, also ocExternal and ocMacro
else if (SC_OPCODE_START_BIN_OP <= eOp && eOp < SC_OPCODE_STOP_BIN_OP)
return 2; // binary
else if ((SC_OPCODE_START_UN_OP <= eOp && eOp < SC_OPCODE_STOP_UN_OP)
|| eOp == ocPercentSign)
return 1; // unary
else if (SC_OPCODE_START_NO_PAR <= eOp && eOp < SC_OPCODE_STOP_NO_PAR)
return 0; // no parameter
else if (SC_OPCODE_START_1_PAR <= eOp && eOp < SC_OPCODE_STOP_1_PAR)
return 1; // one parameter
else if ( eOp == ocIf || eOp == ocChose )
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
}
sal_Bool FormulaToken::IsMatrixFunction() const
{
return formula::FormulaCompiler::IsMatrixFunction(GetOpCode());
}
sal_Bool FormulaToken::operator==( const FormulaToken& rToken ) const
{
// don't compare reference count!
return eType == rToken.eType && GetOpCode() == rToken.GetOpCode();
}
// --- virtual dummy methods -------------------------------------------------
sal_uInt8 FormulaToken::GetByte() const
{
// ok to be called for any derived class
return 0;
}
void FormulaToken::SetByte( sal_uInt8 )
{
DBG_ERRORFILE( "FormulaToken::SetByte: virtual dummy called" );
}
bool FormulaToken::HasForceArray() const
{
// ok to be called for any derived class
return false;
}
void FormulaToken::SetForceArray( bool )
{
DBG_ERRORFILE( "FormulaToken::SetForceArray: virtual dummy called" );
}
double FormulaToken::GetDouble() const
{
DBG_ERRORFILE( "FormulaToken::GetDouble: virtual dummy called" );
return 0.0;
}
double & FormulaToken::GetDoubleAsReference()
{
DBG_ERRORFILE( "FormulaToken::GetDouble: virtual dummy called" );
static double fVal = 0.0;
return fVal;
}
const String& FormulaToken::GetString() const
{
DBG_ERRORFILE( "FormulaToken::GetString: virtual dummy called" );
static String aDummyString;
return aDummyString;
}
sal_uInt16 FormulaToken::GetIndex() const
{
DBG_ERRORFILE( "FormulaToken::GetIndex: virtual dummy called" );
return 0;
}
void FormulaToken::SetIndex( sal_uInt16 )
{
DBG_ERRORFILE( "FormulaToken::SetIndex: virtual dummy called" );
}
short* FormulaToken::GetJump() const
{
DBG_ERRORFILE( "FormulaToken::GetJump: virtual dummy called" );
return NULL;
}
const String& FormulaToken::GetExternal() const
{
DBG_ERRORFILE( "FormulaToken::GetExternal: virtual dummy called" );
static String aDummyString;
return aDummyString;
}
FormulaToken* FormulaToken::GetFAPOrigToken() const
{
DBG_ERRORFILE( "FormulaToken::GetFAPOrigToken: virtual dummy called" );
return NULL;
}
sal_uInt16 FormulaToken::GetError() const
{
DBG_ERRORFILE( "FormulaToken::GetError: virtual dummy called" );
return 0;
}
void FormulaToken::SetError( sal_uInt16 )
{
DBG_ERRORFILE( "FormulaToken::SetError: virtual dummy called" );
}
sal_Bool FormulaToken::TextEqual( const FormulaToken& rToken ) const
{
return *this == rToken;
}
// ==========================================================================
// real implementations of virtual functions
// --------------------------------------------------------------------------
sal_uInt8 FormulaByteToken::GetByte() const { return nByte; }
void FormulaByteToken::SetByte( sal_uInt8 n ) { nByte = n; }
bool FormulaByteToken::HasForceArray() const { return bHasForceArray; }
void FormulaByteToken::SetForceArray( bool b ) { bHasForceArray = b; }
sal_Bool FormulaByteToken::operator==( const FormulaToken& r ) const
{
return FormulaToken::operator==( r ) && nByte == r.GetByte() &&
bHasForceArray == r.HasForceArray();
}
FormulaToken* FormulaFAPToken::GetFAPOrigToken() const { return pOrigToken; }
sal_Bool FormulaFAPToken::operator==( const FormulaToken& r ) const
{
return FormulaByteToken::operator==( r ) && pOrigToken == r.GetFAPOrigToken();
}
short* FormulaJumpToken::GetJump() const { return pJump; }
sal_Bool FormulaJumpToken::operator==( const FormulaToken& r ) const
{
return FormulaToken::operator==( r ) && pJump[0] == r.GetJump()[0] &&
memcmp( pJump+1, r.GetJump()+1, pJump[0] * sizeof(short) ) == 0;
}
FormulaJumpToken::~FormulaJumpToken()
{
delete [] pJump;
}
bool FormulaTokenArray::AddFormulaToken(const sheet::FormulaToken& _aToken,ExternalReferenceHelper* /*_pRef*/)
{
bool bError = false;
const OpCode eOpCode = static_cast<OpCode>(_aToken.OpCode); //! assuming equal values for the moment
const uno::TypeClass eClass = _aToken.Data.getValueTypeClass();
switch ( eClass )
{
case uno::TypeClass_VOID:
// empty data -> use AddOpCode (does some special cases)
AddOpCode( eOpCode );
break;
case uno::TypeClass_DOUBLE:
// double is only used for "push"
if ( eOpCode == ocPush )
AddDouble( _aToken.Data.get<double>() );
else
bError = true;
break;
case uno::TypeClass_LONG:
{
// long is svIndex, used for name / database area, or "byte" for spaces
sal_Int32 nValue = _aToken.Data.get<sal_Int32>();
if ( eOpCode == ocName || eOpCode == ocDBArea )
AddToken( formula::FormulaIndexToken( eOpCode, static_cast<sal_uInt16>(nValue) ) );
else if ( eOpCode == ocSpaces )
AddToken( formula::FormulaByteToken( ocSpaces, static_cast<sal_uInt8>(nValue) ) );
else
bError = true;
}
break;
case uno::TypeClass_STRING:
{
String aStrVal( _aToken.Data.get<rtl::OUString>() );
if ( eOpCode == ocPush )
AddString( aStrVal );
else if ( eOpCode == ocBad )
AddBad( aStrVal );
else if ( eOpCode == ocExternal || eOpCode == ocMacro )
AddToken( formula::FormulaExternalToken( eOpCode, aStrVal ) );
else
bError = true; // unexpected string: don't know what to do with it
}
break;
default:
bError = true;
} // switch ( eClass )
return bError;
}
bool FormulaTokenArray::Fill(const uno::Sequence< sheet::FormulaToken >& _aSequence,ExternalReferenceHelper* _pRef)
{
bool bError = false;
const sal_Int32 nCount = _aSequence.getLength();
for (sal_Int32 nPos=0; nPos<nCount; nPos++)
{
bError |= AddFormulaToken( _aSequence[nPos] ,_pRef);
}
return bError;
}
//////////////////////////////////////////////////////////////////////////
FormulaToken* FormulaTokenArray::GetNextReference()
{
while( nIndex < nLen )
{
FormulaToken* t = pCode[ nIndex++ ];
switch( t->GetType() )
{
case svSingleRef:
case svDoubleRef:
case svExternalSingleRef:
case svExternalDoubleRef:
return t;
default:
{
// added to avoid warnings
}
}
}
return NULL;
}
FormulaToken* FormulaTokenArray::GetNextColRowName()
{
while( nIndex < nLen )
{
FormulaToken* t = pCode[ nIndex++ ];
if ( t->GetOpCode() == ocColRowName )
return t;
}
return NULL;
}
FormulaToken* FormulaTokenArray::GetNextReferenceRPN()
{
while( nIndex < nRPN )
{
FormulaToken* t = pRPN[ nIndex++ ];
switch( t->GetType() )
{
case svSingleRef:
case svDoubleRef:
case svExternalSingleRef:
case svExternalDoubleRef:
return t;
default:
{
// added to avoid warnings
}
}
}
return NULL;
}
FormulaToken* FormulaTokenArray::GetNextReferenceOrName()
{
if( pCode )
{
while ( nIndex < nLen )
{
FormulaToken* t = pCode[ nIndex++ ];
switch( t->GetType() )
{
case svSingleRef:
case svDoubleRef:
case svIndex:
case svExternalSingleRef:
case svExternalDoubleRef:
case svExternalName:
return t;
default:
{
// added to avoid warnings
}
}
}
}
return NULL;
}
FormulaToken* FormulaTokenArray::GetNextName()
{
if( pCode )
{
while ( nIndex < nLen )
{
FormulaToken* t = pCode[ nIndex++ ];
if( t->GetType() == svIndex )
return t;
}
} // if( pCode )
return NULL;
}
FormulaToken* FormulaTokenArray::GetNextDBArea()
{
if( pCode )
{
while ( nIndex < nLen )
{
FormulaToken* t = pCode[ nIndex++ ];
if( t->GetOpCode() == ocDBArea )
return t;
} // while ( nIndex < nLen )+
}
return NULL;
}
FormulaToken* FormulaTokenArray::GetNextOpCodeRPN( OpCode eOp )
{
while( nIndex < nRPN )
{
FormulaToken* t = pRPN[ nIndex++ ];
if ( t->GetOpCode() == eOp )
return t;
}
return NULL;
}
FormulaToken* FormulaTokenArray::Next()
{
if( pCode && nIndex < nLen )
return pCode[ nIndex++ ];
else
return NULL;
}
FormulaToken* FormulaTokenArray::NextNoSpaces()
{
if( pCode )
{
while( (nIndex < nLen) && (pCode[ nIndex ]->GetOpCode() == ocSpaces) )
++nIndex;
if( nIndex < nLen )
return pCode[ nIndex++ ];
}
return NULL;
}
FormulaToken* FormulaTokenArray::NextRPN()
{
if( pRPN && nIndex < nRPN )
return pRPN[ nIndex++ ];
else
return NULL;
}
FormulaToken* FormulaTokenArray::PrevRPN()
{
if( pRPN && nIndex )
return pRPN[ --nIndex ];
else
return NULL;
}
void FormulaTokenArray::DelRPN()
{
if( nRPN )
{
FormulaToken** p = pRPN;
for( sal_uInt16 i = 0; i < nRPN; i++ )
{
(*p++)->DecRef();
}
delete [] pRPN;
}
pRPN = NULL;
nRPN = nIndex = 0;
}
FormulaToken* FormulaTokenArray::PeekPrev( sal_uInt16 & nIdx )
{
if (0 < nIdx && nIdx <= nLen)
return pCode[--nIdx];
return NULL;
}
FormulaToken* FormulaTokenArray::PeekNext()
{
if( pCode && nIndex < nLen )
return pCode[ nIndex ];
else
return NULL;
}
FormulaToken* FormulaTokenArray::PeekNextNoSpaces()
{
if( pCode && nIndex < nLen )
{
sal_uInt16 j = nIndex;
while ( pCode[j]->GetOpCode() == ocSpaces && j < nLen )
j++;
if ( j < nLen )
return pCode[ j ];
else
return NULL;
}
else
return NULL;
}
FormulaToken* FormulaTokenArray::PeekPrevNoSpaces()
{
if( pCode && nIndex > 1 )
{
sal_uInt16 j = nIndex - 2;
while ( pCode[j]->GetOpCode() == ocSpaces && j > 0 )
j--;
if ( j > 0 || pCode[j]->GetOpCode() != ocSpaces )
return pCode[ j ];
else
return NULL;
}
else
return NULL;
}
sal_Bool FormulaTokenArray::HasOpCode( OpCode eOp ) const
{
for ( sal_uInt16 j=0; j < nLen; j++ )
{
if ( pCode[j]->GetOpCode() == eOp )
return sal_True;
}
return sal_False;
}
sal_Bool FormulaTokenArray::HasOpCodeRPN( OpCode eOp ) const
{
for ( sal_uInt16 j=0; j < nRPN; j++ )
{
if ( pRPN[j]->GetOpCode() == eOp )
return sal_True;
}
return sal_False;
}
sal_Bool FormulaTokenArray::HasNameOrColRowName() const
{
for ( sal_uInt16 j=0; j < nLen; j++ )
{
if( pCode[j]->GetType() == svIndex || pCode[j]->GetOpCode() == ocColRowName )
return sal_True;
}
return sal_False;
}
////////////////////////////////////////////////////////////////////////////
FormulaTokenArray::FormulaTokenArray()
{
pCode = NULL; pRPN = NULL;
nError = nLen = nIndex = nRPN = nRefs = 0;
bHyperLink = sal_False;
ClearRecalcMode();
}
FormulaTokenArray::FormulaTokenArray( const FormulaTokenArray& rArr )
{
Assign( rArr );
}
FormulaTokenArray::~FormulaTokenArray()
{
Clear();
}
void FormulaTokenArray::Assign( const FormulaTokenArray& r )
{
nLen = r.nLen;
nRPN = r.nRPN;
nIndex = r.nIndex;
nError = r.nError;
nRefs = r.nRefs;
nMode = r.nMode;
bHyperLink = r.bHyperLink;
pCode = NULL;
pRPN = NULL;
FormulaToken** pp;
if( nLen )
{
pp = pCode = new FormulaToken*[ nLen ];
memcpy( pp, r.pCode, nLen * sizeof( FormulaToken* ) );
for( sal_uInt16 i = 0; i < nLen; i++ )
(*pp++)->IncRef();
}
if( nRPN )
{
pp = pRPN = new FormulaToken*[ nRPN ];
memcpy( pp, r.pRPN, nRPN * sizeof( FormulaToken* ) );
for( sal_uInt16 i = 0; i < nRPN; i++ )
(*pp++)->IncRef();
}
}
FormulaTokenArray& FormulaTokenArray::operator=( const FormulaTokenArray& rArr )
{
Clear();
Assign( rArr );
return *this;
}
FormulaTokenArray* FormulaTokenArray::Clone() const
{
FormulaTokenArray* p = new FormulaTokenArray;
p->nLen = nLen;
p->nRPN = nRPN;
p->nRefs = nRefs;
p->nMode = nMode;
p->nError = nError;
p->bHyperLink = bHyperLink;
FormulaToken** pp;
if( nLen )
{
pp = p->pCode = new FormulaToken*[ nLen ];
memcpy( pp, pCode, nLen * sizeof( FormulaToken* ) );
for( sal_uInt16 i = 0; i < nLen; i++, pp++ )
{
*pp = (*pp)->Clone();
(*pp)->IncRef();
}
}
if( nRPN )
{
pp = p->pRPN = new FormulaToken*[ nRPN ];
memcpy( pp, pRPN, nRPN * sizeof( FormulaToken* ) );
for( sal_uInt16 i = 0; i < nRPN; i++, pp++ )
{
FormulaToken* t = *pp;
if( t->GetRef() > 1 )
{
FormulaToken** p2 = pCode;
sal_uInt16 nIdx = 0xFFFF;
for( sal_uInt16 j = 0; j < nLen; j++, p2++ )
{
if( *p2 == t )
{
nIdx = j; break;
}
}
if( nIdx == 0xFFFF )
*pp = t->Clone();
else
*pp = p->pCode[ nIdx ];
}
else
*pp = t->Clone();
(*pp)->IncRef();
}
}
return p;
}
void FormulaTokenArray::Clear()
{
if( nRPN ) DelRPN();
if( pCode )
{
FormulaToken** p = pCode;
for( sal_uInt16 i = 0; i < nLen; i++ )
{
(*p++)->DecRef();
}
delete [] pCode;
}
pCode = NULL; pRPN = NULL;
nError = nLen = nIndex = nRPN = nRefs = 0;
bHyperLink = sal_False;
ClearRecalcMode();
}
FormulaToken* FormulaTokenArray::AddToken( const FormulaToken& r )
{
return Add( r.Clone() );
}
FormulaToken* FormulaTokenArray::MergeArray( )
{
return NULL;
}
FormulaToken* FormulaTokenArray::Add( FormulaToken* t )
{
if( !pCode )
pCode = new FormulaToken*[ MAXCODE ];
if( nLen < MAXCODE-1 )
{
// fprintf (stderr, "Add : %d\n", t->GetOpCode());
pCode[ nLen++ ] = t;
if( t->GetOpCode() == ocPush
&& ( t->GetType() == svSingleRef || t->GetType() == svDoubleRef ) )
nRefs++;
t->IncRef();
if( t->GetOpCode() == ocArrayClose )
return MergeArray();
return t;
}
else
{
t->Delete();
if ( nLen == MAXCODE-1 )
{
t = new FormulaByteToken( ocStop );
pCode[ nLen++ ] = t;
t->IncRef();
}
return NULL;
}
}
FormulaToken* FormulaTokenArray::AddString( const sal_Unicode* pStr )
{
return AddString( String( pStr ) );
}
FormulaToken* FormulaTokenArray::AddString( const String& rStr )
{
return Add( new FormulaStringToken( rStr ) );
}
FormulaToken* FormulaTokenArray::AddDouble( double fVal )
{
return Add( new FormulaDoubleToken( fVal ) );
}
FormulaToken* FormulaTokenArray::AddName( sal_uInt16 n )
{
return Add( new FormulaIndexToken( ocName, n ) );
}
FormulaToken* FormulaTokenArray::AddExternal( const sal_Unicode* pStr )
{
return AddExternal( String( pStr ) );
}
FormulaToken* FormulaTokenArray::AddExternal( const String& rStr,
OpCode eOp /* = ocExternal */ )
{
return Add( new FormulaExternalToken( eOp, rStr ) );
}
FormulaToken* FormulaTokenArray::AddBad( const sal_Unicode* pStr )
{
return AddBad( String( pStr ) );
}
FormulaToken* FormulaTokenArray::AddBad( const String& rStr )
{
return Add( new FormulaStringOpToken( ocBad, rStr ) );
}
void FormulaTokenArray::AddRecalcMode( ScRecalcMode nBits )
{
//! Reihenfolge ist wichtig
if ( nBits & RECALCMODE_ALWAYS )
SetRecalcModeAlways();
else if ( !IsRecalcModeAlways() )
{
if ( nBits & RECALCMODE_ONLOAD )
SetRecalcModeOnLoad();
else if ( nBits & RECALCMODE_ONLOAD_ONCE && !IsRecalcModeOnLoad() )
SetRecalcModeOnLoadOnce();
}
SetCombinedBitsRecalcMode( nBits );
}
sal_Bool FormulaTokenArray::HasMatrixDoubleRefOps()
{
if ( pRPN && nRPN )
{
// RPN-Interpreter Simulation
// als Ergebnis jeder Funktion wird einfach ein Double angenommen
FormulaToken** pStack = new FormulaToken* [nRPN];
FormulaToken* pResult = new FormulaDoubleToken( 0.0 );
short sp = 0;
for ( sal_uInt16 j = 0; j < nRPN; j++ )
{
FormulaToken* t = pRPN[j];
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();
delete [] pStack;
return sal_True;
}
}
}
break;
default:
{
// added to avoid warnings
}
}
if ( eOp == ocPush || lcl_IsReference( eOp, t->GetType() ) )
pStack[sp++] = t;
else if ( eOp == ocIf || eOp == ocChose )
{ // Jumps ignorieren, vorheriges Result (Condition) poppen
if ( sp )
--sp;
}
else
{ // pop parameters, push result
sp = sal::static_int_cast<short>( sp - nParams );
if ( sp < 0 )
{
DBG_ERROR( "FormulaTokenArray::HasMatrixDoubleRefOps: sp < 0" );
sp = 0;
}
pStack[sp++] = pResult;
}
}
pResult->Delete();
delete [] pStack;
}
return sal_False;
}
// --- POF (plain old formula) rewrite of a token array ---------------------
#if 0
// static function can't be compiled if not used (warning)
//#if OSL_DEBUG_LEVEL > 0
static void DumpTokArr( FormulaTokenArray *pCode )
{
fprintf (stderr, "TokenArr: ");
for ( FormulaToken *pCur = pCode->First(); pCur; pCur = pCode->Next() )
fprintf( stderr, "t%d,o%d ",
pCur->GetType(), pCur->GetOpCode() );
fprintf (stderr, "\n");
}
#endif
inline bool MissingConvention::isRewriteNeeded( OpCode eOp ) const
{
switch (eOp)
{
case ocGammaDist:
case ocPoissonDist:
case ocAddress:
case ocLogNormDist:
case ocNormDist:
return true;
case ocMissing:
case ocLog:
return !isODFF(); // rewrite only for PODF
default:
return false;
}
}
class FormulaMissingContext
{
public:
const FormulaToken* mpFunc;
int mnCurArg;
void Clear() { mpFunc = NULL; mnCurArg = 0; }
inline bool AddDefaultArg( FormulaTokenArray* pNewArr, int nArg, double f ) const;
bool AddMissingExternal( FormulaTokenArray* pNewArr ) const;
bool AddMissing( FormulaTokenArray *pNewArr, const MissingConvention & rConv ) const;
void AddMoreArgs( FormulaTokenArray *pNewArr, const MissingConvention & rConv ) const;
};
void FormulaMissingContext::AddMoreArgs( FormulaTokenArray *pNewArr, const MissingConvention & rConv ) const
{
if ( !mpFunc )
return;
switch (mpFunc->GetOpCode())
{
case ocGammaDist:
if (mnCurArg == 2)
{
pNewArr->AddOpCode( ocSep );
pNewArr->AddDouble( 1.0 ); // 4th, Cumulative=sal_True()
}
break;
case ocPoissonDist:
if (mnCurArg == 1)
{
pNewArr->AddOpCode( ocSep );
pNewArr->AddDouble( 1.0 ); // 3rd, Cumulative=sal_True()
}
break;
case ocNormDist:
if ( mnCurArg == 2 )
{
pNewArr->AddOpCode( ocSep );
pNewArr->AddDouble( 1.0 ); // 4th, Cumulative=sal_True()
}
break;
case ocLogNormDist:
if ( mnCurArg == 0 )
{
pNewArr->AddOpCode( ocSep );
pNewArr->AddDouble( 0.0 ); // 2nd, mean = 0.0
}
if ( mnCurArg <= 1 )
{
pNewArr->AddOpCode( ocSep );
pNewArr->AddDouble( 1.0 ); // 3rd, standard deviation = 1.0
}
break;
case ocLog:
if ( !rConv.isODFF() && mnCurArg == 0 )
{
pNewArr->AddOpCode( ocSep );
pNewArr->AddDouble( 10.0 ); // 2nd, basis 10
}
break;
default:
break;
}
}
inline bool FormulaMissingContext::AddDefaultArg( FormulaTokenArray* pNewArr, int nArg, double f ) const
{
if (mnCurArg == nArg)
{
pNewArr->AddDouble( f );
return true;
}
return false;
}
bool FormulaMissingContext::AddMissingExternal( FormulaTokenArray *pNewArr ) const
{
// Only called for PODF, not ODFF. No need to distinguish.
const String &rName = mpFunc->GetExternal();
// initial (fast) check:
sal_Unicode nLastChar = rName.GetChar( rName.Len() - 1);
if ( nLastChar != 't' && nLastChar != 'm' )
return false;
if (rName.EqualsIgnoreCaseAscii(
"com.sun.star.sheet.addin.Analysis.getAccrint" ))
{
return AddDefaultArg( pNewArr, 4, 1000.0 );
}
if (rName.EqualsIgnoreCaseAscii(
"com.sun.star.sheet.addin.Analysis.getAccrintm" ))
{
return AddDefaultArg( pNewArr, 3, 1000.0 );
}
return false;
}
bool FormulaMissingContext::AddMissing( FormulaTokenArray *pNewArr, const MissingConvention & rConv ) const
{
if ( !mpFunc )
return false;
bool bRet = false;
const OpCode eOp = mpFunc->GetOpCode();
// Add for both, PODF and ODFF
switch (eOp)
{
case ocAddress:
return AddDefaultArg( pNewArr, 2, 1.0 ); // abs
default:
break;
}
if (rConv.isODFF())
{
// Add for ODFF
}
else
{
// Add for PODF
switch (eOp)
{
case ocFixed:
return AddDefaultArg( pNewArr, 1, 2.0 );
case ocBetaDist:
case ocBetaInv:
case ocRMZ: // PMT
return AddDefaultArg( pNewArr, 3, 0.0 );
case ocZinsZ: // IPMT
case ocKapz: // PPMT
return AddDefaultArg( pNewArr, 4, 0.0 );
case ocBW: // PV
case ocZW: // FV
bRet |= AddDefaultArg( pNewArr, 2, 0.0 ); // pmt
bRet |= AddDefaultArg( pNewArr, 3, 0.0 ); // [fp]v
break;
case ocZins: // RATE
bRet |= AddDefaultArg( pNewArr, 1, 0.0 ); // pmt
bRet |= AddDefaultArg( pNewArr, 3, 0.0 ); // fv
bRet |= AddDefaultArg( pNewArr, 4, 0.0 ); // type
break;
case ocExternal:
return AddMissingExternal( pNewArr );
// --- more complex cases ---
case ocOffset:
// FIXME: rather tough.
// if arg 3 (height) ommitted, export arg1 (rows)
break;
default:
break;
}
}
return bRet;
}
bool FormulaTokenArray::NeedsPofRewrite( const MissingConvention & rConv )
{
for ( FormulaToken *pCur = First(); pCur; pCur = Next() )
{
if ( rConv.isRewriteNeeded( pCur->GetOpCode()))
return true;
}
return false;
}
FormulaTokenArray * FormulaTokenArray::RewriteMissingToPof( const MissingConvention & rConv )
{
const size_t nAlloc = 256;
FormulaMissingContext aCtx[ nAlloc ];
int aOpCodeAddressStack[ nAlloc ]; // use of ADDRESS() function
const int nOmitAddressArg = 3; // ADDRESS() 4th parameter A1/R1C1
sal_uInt16 nTokens = GetLen() + 1;
FormulaMissingContext* pCtx = (nAlloc < nTokens ? new FormulaMissingContext[nTokens] : &aCtx[0]);
int* pOcas = (nAlloc < nTokens ? new int[nTokens] : &aOpCodeAddressStack[0]);
// Never go below 0, never use 0, mpFunc always NULL.
pCtx[0].Clear();
int nFn = 0;
int nOcas = 0;
FormulaTokenArray *pNewArr = new FormulaTokenArray;
// At least RECALCMODE_ALWAYS needs to be set.
pNewArr->AddRecalcMode( GetRecalcMode());
for ( FormulaToken *pCur = First(); pCur; pCur = 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 erverything 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;
}
//fprintf( stderr, "ocAddress %d arg %d%s\n", (int)i, (int)pCtx[ pOcas[ i ] ].mnCurArg, (bAdd ? "" : " omitted"));
}
switch ( pCur->GetOpCode() )
{
case ocOpen:
++nFn; // all following operations on _that_ function
pCtx[ nFn ].mpFunc = PeekPrevNoSpaces();
pCtx[ nFn ].mnCurArg = 0;
if (pCtx[ nFn ].mpFunc && pCtx[ nFn ].mpFunc->GetOpCode() == ocAddress && !rConv.isODFF())
pOcas[ nOcas++ ] = nFn; // entering ADDRESS() if PODF
break;
case ocClose:
pCtx[ nFn ].AddMoreArgs( pNewArr, rConv );
DBG_ASSERT( nFn > 0, "FormulaTokenArray::RewriteMissingToPof: underflow");
if (nOcas > 0 && pOcas[ nOcas-1 ] == nFn)
--nOcas; // leaving ADDRESS()
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;
//fprintf( stderr, "ocAddress %d sep %d omitted\n", (int)nOcas-1, nOmitAddressArg);
}
break;
case ocMissing:
if (bAdd)
bAdd = !pCtx[ nFn ].AddMissing( pNewArr, rConv );
break;
default:
break;
}
if (bAdd)
pNewArr->AddToken( *pCur );
}
if (pOcas != &aOpCodeAddressStack[0])
delete [] pOcas;
if (pCtx != &aCtx[0])
delete [] pCtx;
return pNewArr;
}
bool FormulaTokenArray::MayReferenceFollow()
{
if ( pCode && nLen > 0 )
{
// ignore trailing spaces
sal_uInt16 i = nLen - 1;
while ( i > 0 && pCode[i]->GetOpCode() == SC_OPCODE_SPACES )
{
--i;
}
if ( i > 0 || pCode[i]->GetOpCode() != SC_OPCODE_SPACES )
{
OpCode eOp = pCode[i]->GetOpCode();
if ( (SC_OPCODE_START_BIN_OP <= eOp && eOp < SC_OPCODE_STOP_BIN_OP ) ||
(SC_OPCODE_START_UN_OP <= eOp && eOp < SC_OPCODE_STOP_UN_OP ) ||
eOp == SC_OPCODE_OPEN || eOp == SC_OPCODE_SEP )
{
return true;
}
}
}
return false;
}
FormulaToken* FormulaTokenArray::AddOpCode( OpCode eOp )
{
FormulaToken* pRet = NULL;
switch ( eOp )
{
case ocOpen:
case ocClose:
case ocSep:
case ocArrayOpen:
case ocArrayClose:
case ocArrayRowSep:
case ocArrayColSep:
pRet = new FormulaToken( svSep,eOp );
break;
case ocIf:
case ocChose:
{
short nJump[MAXJUMPCOUNT + 1];
nJump[ 0 ] = ocIf == eOp ? 3 : MAXJUMPCOUNT+1;
pRet = new FormulaJumpToken( eOp, (short*)nJump );
}
break;
default:
pRet = new FormulaByteToken( eOp, 0, sal_False );
break;
}
return AddToken( *pRet );
}
/*----------------------------------------------------------------------*/
FormulaTokenIterator::FormulaTokenIterator( const FormulaTokenArray& rArr )
{
pCur = NULL;
Push( &rArr );
}
FormulaTokenIterator::~FormulaTokenIterator()
{
while( pCur )
Pop();
}
void FormulaTokenIterator::Push( const FormulaTokenArray* pArr )
{
ImpTokenIterator* p = new ImpTokenIterator;
p->pArr = pArr;
p->nPC = -1;
p->nStop = SHRT_MAX;
p->pNext = pCur;
pCur = p;
}
void FormulaTokenIterator::Pop()
{
ImpTokenIterator* p = pCur;
if( p )
{
pCur = p->pNext;
delete p;
}
}
void FormulaTokenIterator::Reset()
{
while( pCur->pNext )
Pop();
pCur->nPC = -1;
}
const FormulaToken* FormulaTokenIterator::First()
{
Reset();
return Next();
}
const FormulaToken* FormulaTokenIterator::Next()
{
const FormulaToken* t = GetNonEndOfPathToken( ++pCur->nPC );
if( !t && pCur->pNext )
{
Pop();
t = Next();
}
return t;
}
const FormulaToken* FormulaTokenIterator::PeekNextOperator()
{
const FormulaToken* t = NULL;
short nIdx = pCur->nPC;
while (!t && ((t = GetNonEndOfPathToken( ++nIdx)) != NULL))
{
if (t->GetOpCode() == ocPush)
t = NULL; // ignore operands
}
if (!t && pCur->pNext)
{
ImpTokenIterator* pHere = pCur;
pCur = pCur->pNext;
t = PeekNextOperator();
pCur = pHere;
}
return t;
}
//! The nPC counts after a Push() are -1
void FormulaTokenIterator::Jump( short nStart, short nNext, short nStop )
{
pCur->nPC = nNext;
if( nStart != nNext )
{
Push( pCur->pArr );
pCur->nPC = nStart;
pCur->nStop = nStop;
}
}
const FormulaToken* FormulaTokenIterator::GetNonEndOfPathToken( short nIdx ) const
{
if (nIdx < pCur->pArr->nRPN && nIdx < pCur->nStop)
{
const FormulaToken* t = pCur->pArr->pRPN[ nIdx ];
// such an OpCode ends an IF() or CHOOSE() path
return (t->GetOpCode() == ocSep || t->GetOpCode() == ocClose) ? NULL : t;
}
return NULL;
}
bool FormulaTokenIterator::IsEndOfPath() const
{
return GetNonEndOfPathToken( pCur->nPC + 1) == NULL;
}
// -----------------------------------------------------------------------------
// ==========================================================================
// real implementations of virtual functions
// --------------------------------------------------------------------------
double FormulaDoubleToken::GetDouble() const { return fDouble; }
double & FormulaDoubleToken::GetDoubleAsReference() { return fDouble; }
sal_Bool FormulaDoubleToken::operator==( const FormulaToken& r ) const
{
return FormulaToken::operator==( r ) && fDouble == r.GetDouble();
}
const String& FormulaStringToken::GetString() const { return aString; }
sal_Bool FormulaStringToken::operator==( const FormulaToken& r ) const
{
return FormulaToken::operator==( r ) && aString == r.GetString();
}
const String& FormulaStringOpToken::GetString() const { return aString; }
sal_Bool FormulaStringOpToken::operator==( const FormulaToken& r ) const
{
return FormulaByteToken::operator==( r ) && aString == r.GetString();
}
sal_uInt16 FormulaIndexToken::GetIndex() const { return nIndex; }
void FormulaIndexToken::SetIndex( sal_uInt16 n ) { nIndex = n; }
sal_Bool FormulaIndexToken::operator==( const FormulaToken& r ) const
{
return FormulaToken::operator==( r ) && nIndex == r.GetIndex();
}
const String& FormulaExternalToken::GetExternal() const { return aExternal; }
sal_uInt8 FormulaExternalToken::GetByte() const { return nByte; }
void FormulaExternalToken::SetByte( sal_uInt8 n ) { nByte = n; }
sal_Bool FormulaExternalToken::operator==( const FormulaToken& r ) const
{
return FormulaToken::operator==( r ) && nByte == r.GetByte() &&
aExternal == r.GetExternal();
}
sal_uInt16 FormulaErrorToken::GetError() const { return nError; }
void FormulaErrorToken::SetError( sal_uInt16 nErr ) { nError = nErr; }
sal_Bool FormulaErrorToken::operator==( const FormulaToken& r ) const
{
return FormulaToken::operator==( r ) &&
nError == static_cast< const FormulaErrorToken & >(r).GetError();
}
double FormulaMissingToken::GetDouble() const { return 0.0; }
const String& FormulaMissingToken::GetString() const
{
static String aDummyString;
return aDummyString;
}
sal_Bool FormulaMissingToken::operator==( const FormulaToken& r ) const
{
return FormulaToken::operator==( r );
}
FormulaSubroutineToken::FormulaSubroutineToken( const FormulaSubroutineToken& r ) :
FormulaToken( r ),
mpArray( r.mpArray->Clone())
{
}
FormulaSubroutineToken::~FormulaSubroutineToken()
{
delete mpArray;
}
const FormulaTokenArray* FormulaSubroutineToken::GetTokenArray() const
{
return mpArray;
}
sal_Bool FormulaSubroutineToken::operator==( const FormulaToken& r ) const
{
// Arrays don't equal..
return FormulaToken::operator==( r ) &&
(mpArray == static_cast<const FormulaSubroutineToken&>(r).mpArray);
}
sal_Bool FormulaUnknownToken::operator==( const FormulaToken& r ) const
{
return FormulaToken::operator==( r );
}
// -----------------------------------------------------------------------------
} // formula
// -----------------------------------------------------------------------------