diff options
Diffstat (limited to 'sc/source/filter/excel/xeformula.cxx')
-rw-r--r-- | sc/source/filter/excel/xeformula.cxx | 2646 |
1 files changed, 2646 insertions, 0 deletions
diff --git a/sc/source/filter/excel/xeformula.cxx b/sc/source/filter/excel/xeformula.cxx new file mode 100644 index 000000000000..34e48671a3bc --- /dev/null +++ b/sc/source/filter/excel/xeformula.cxx @@ -0,0 +1,2646 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sc.hxx" + +// XXX xelink.hxx MUST be included before xeformula.hxx because of the +// redifinition of the CREATE_OUSTRING() macro, which is in oox/helper.hxx +// (indirectly included via xelink.hxx) and ../inc/ftools.hxx (indirectly +// included via xeformula.hxx) that does an undef first. Ugly. +#include "xelink.hxx" +#include "xeformula.hxx" + +#include <list> +#include <map> +#include <memory> +#include "addincol.hxx" +#include "compiler.hxx" +#include "document.hxx" +#include "externalrefmgr.hxx" +#include "rangelst.hxx" +#include "token.hxx" +#include "tokenarray.hxx" +#include "xehelper.hxx" +#include "xename.hxx" +#include "xestream.hxx" + +using namespace ::formula; + +// External reference log ===================================================== + +XclExpRefLogEntry::XclExpRefLogEntry() : + mpUrl( 0 ), + mpFirstTab( 0 ), + mpLastTab( 0 ), + mnFirstXclTab( EXC_TAB_DELETED ), + mnLastXclTab( EXC_TAB_DELETED ) +{ +} + +// Formula compiler =========================================================== + +namespace { + +/** Wrapper structure for a processed Calc formula token with additional + settings (whitespaces). */ +struct XclExpScToken +{ + const FormulaToken* mpScToken; /// Currently processed Calc token. + sal_uInt8 mnSpaces; /// Number of spaces before the Calc token. + + inline explicit XclExpScToken() : mpScToken( 0 ), mnSpaces( 0 ) {} + inline bool Is() const { return mpScToken != 0; } + inline StackVar GetType() const { return mpScToken ? mpScToken->GetType() : static_cast< StackVar >( svUnknown ); } + inline OpCode GetOpCode() const { return mpScToken ? mpScToken->GetOpCode() : static_cast< OpCode >( ocNone ); } +}; + +// ---------------------------------------------------------------------------- + +/** Effective token class conversion types. */ +enum XclExpClassConv +{ + EXC_CLASSCONV_ORG, /// Keep original class of the token. + EXC_CLASSCONV_VAL, /// Convert ARR tokens to VAL class (REF remains uncahnged). + EXC_CLASSCONV_ARR /// Convert VAL tokens to ARR class (REF remains uncahnged). +}; + +// ---------------------------------------------------------------------------- + +/** Token class conversion and position of a token in the token array. */ +struct XclExpTokenConvInfo +{ + sal_uInt16 mnTokPos; /// Position of the token in the token array. + XclFuncParamConv meConv; /// Token class conversion type. + bool mbValType; /// Data type (false = REFTYPE, true = VALTYPE). +}; + +/** Vector of token position and conversion for all operands of an operator, + or for all parameters of a function. */ +struct XclExpOperandList : public ::std::vector< XclExpTokenConvInfo > +{ + inline explicit XclExpOperandList() { reserve( 2 ); } + void AppendOperand( sal_uInt16 nTokPos, XclFuncParamConv eConv, bool bValType ); +}; + +void XclExpOperandList::AppendOperand( sal_uInt16 nTokPos, XclFuncParamConv eConv, bool bValType ) +{ + resize( size() + 1 ); + XclExpTokenConvInfo& rConvInfo = back(); + rConvInfo.mnTokPos = nTokPos; + rConvInfo.meConv = eConv; + rConvInfo.mbValType = bValType; +} + +typedef ScfRef< XclExpOperandList > XclExpOperandListRef; +typedef ::std::vector< XclExpOperandListRef > XclExpOperandListVector; + +// ---------------------------------------------------------------------------- + +/** Encapsulates all data needed for a call to an external function (macro, add-in). */ +struct XclExpExtFuncData +{ + String maFuncName; /// Name of the function. + bool mbVBasic; /// True = Visual Basic macro call. + bool mbHidden; /// True = Create hidden defined name. + + inline explicit XclExpExtFuncData() : mbVBasic( false ), mbHidden( false ) {} + void Set( const String& rFuncName, bool bVBasic, bool bHidden ); +}; + +void XclExpExtFuncData::Set( const String& rFuncName, bool bVBasic, bool bHidden ) +{ + maFuncName = rFuncName; + mbVBasic = bVBasic; + mbHidden = bHidden; +} + +// ---------------------------------------------------------------------------- + +/** Encapsulates all data needed to process an entire function. */ +class XclExpFuncData +{ +public: + explicit XclExpFuncData( + const XclExpScToken& rTokData, + const XclFunctionInfo& rFuncInfo, + const XclExpExtFuncData& rExtFuncData ); + + inline const FormulaToken& GetScToken() const { return *mrTokData.mpScToken; } + inline OpCode GetOpCode() const { return mrFuncInfo.meOpCode; } + inline sal_uInt16 GetXclFuncIdx() const { return mrFuncInfo.mnXclFunc; } + inline bool IsVolatile() const { return mrFuncInfo.IsVolatile(); } + inline bool IsFixedParamCount() const { return mrFuncInfo.IsFixedParamCount(); } + inline bool IsMacroFunc() const { return mrFuncInfo.IsMacroFunc(); } + inline sal_uInt8 GetSpaces() const { return mrTokData.mnSpaces; } + inline const XclExpExtFuncData& GetExtFuncData() const { return maExtFuncData; } + inline sal_uInt8 GetReturnClass() const { return mrFuncInfo.mnRetClass; } + + const XclFuncParamInfo& GetParamInfo() const; + bool IsCalcOnlyParam() const; + bool IsExcelOnlyParam() const; + void IncParamInfoIdx(); + + inline sal_uInt8 GetMinParamCount() const { return mrFuncInfo.mnMinParamCount; } + inline sal_uInt8 GetMaxParamCount() const { return mrFuncInfo.mnMaxParamCount; } + inline sal_uInt8 GetParamCount() const { return static_cast< sal_uInt8 >( mxOperands->size() ); } + void FinishParam( sal_uInt16 nTokPos ); + inline XclExpOperandListRef GetOperandList() const { return mxOperands; } + + inline ScfUInt16Vec& GetAttrPosVec() { return maAttrPosVec; } + inline void AppendAttrPos( sal_uInt16 nPos ) { maAttrPosVec.push_back( nPos ); } + +private: + ScfUInt16Vec maAttrPosVec; /// Token array positions of tAttr tokens. + const XclExpScToken& mrTokData; /// Data about processed function name token. + const XclFunctionInfo& mrFuncInfo; /// Constant data about processed function. + XclExpExtFuncData maExtFuncData; /// Data for external functions (macro, add-in). + XclExpOperandListRef mxOperands; /// Class conversion and position of all parameters. + const XclFuncParamInfo* mpParamInfo; /// Information for current parameter. +}; + +XclExpFuncData::XclExpFuncData( const XclExpScToken& rTokData, + const XclFunctionInfo& rFuncInfo, const XclExpExtFuncData& rExtFuncData ) : + mrTokData( rTokData ), + mrFuncInfo( rFuncInfo ), + maExtFuncData( rExtFuncData ), + mxOperands( new XclExpOperandList ), + mpParamInfo( rFuncInfo.mpParamInfos ) +{ + DBG_ASSERT( mrTokData.mpScToken, "XclExpFuncData::XclExpFuncData - missing core token" ); + // set name of an add-in function + if( (maExtFuncData.maFuncName.Len() == 0) && dynamic_cast< const FormulaExternalToken* >( mrTokData.mpScToken ) ) + maExtFuncData.Set( GetScToken().GetExternal(), true, false ); +} + +const XclFuncParamInfo& XclExpFuncData::GetParamInfo() const +{ + static const XclFuncParamInfo saInvalidInfo = { EXC_PARAM_NONE, EXC_PARAMCONV_ORG, false }; + return mpParamInfo ? *mpParamInfo : saInvalidInfo; +} + +bool XclExpFuncData::IsCalcOnlyParam() const +{ + return mpParamInfo && (mpParamInfo->meValid == EXC_PARAM_CALCONLY); +} + +bool XclExpFuncData::IsExcelOnlyParam() const +{ + return mpParamInfo && (mpParamInfo->meValid == EXC_PARAM_EXCELONLY); +} + +void XclExpFuncData::IncParamInfoIdx() +{ + if( mpParamInfo ) + { + // move pointer to next entry, if something explicit follows + if( (static_cast<size_t>(mpParamInfo - mrFuncInfo.mpParamInfos + 1) < EXC_FUNCINFO_PARAMINFO_COUNT) && (mpParamInfo[ 1 ].meValid != EXC_PARAM_NONE) ) + ++mpParamInfo; + // if last parameter type is 'Excel-only' or 'Calc-only', do not repeat it + else if( IsExcelOnlyParam() || IsCalcOnlyParam() ) + mpParamInfo = 0; + // otherwise: repeat last parameter class + } +} + +void XclExpFuncData::FinishParam( sal_uInt16 nTokPos ) +{ + // write token class conversion info for this parameter + const XclFuncParamInfo& rParamInfo = GetParamInfo(); + mxOperands->AppendOperand( nTokPos, rParamInfo.meConv, rParamInfo.mbValType ); + // move to next parameter info structure + IncParamInfoIdx(); +} + +// compiler configuration ----------------------------------------------------- + +/** Type of token class handling. */ +enum XclExpFmlaClassType +{ + EXC_CLASSTYPE_CELL, /// Cell formula, shared formula. + EXC_CLASSTYPE_ARRAY, /// Array formula, conditional formatting, data validation. + EXC_CLASSTYPE_NAME /// Defined name, range list. +}; + +/** Configuration data of the formula compiler. */ +struct XclExpCompConfig +{ + XclFormulaType meType; /// Type of the formula to be created. + XclExpFmlaClassType meClassType; /// Token class handling type. + bool mbLocalLinkMgr; /// True = local (per-sheet) link manager, false = global. + bool mbFromCell; /// True = Any kind of cell formula (cell, array, shared). + bool mb3DRefOnly; /// True = Only 3D references allowed (e.g. names). + bool mbAllowArrays; /// True = Allow inline arrays. +}; + +/** The table containing configuration data for all formula types. */ +static const XclExpCompConfig spConfigTable[] = +{ + // formula type token class type lclLM inCell 3dOnly allowArray + { EXC_FMLATYPE_CELL, EXC_CLASSTYPE_CELL, true, true, false, true }, + { EXC_FMLATYPE_SHARED, EXC_CLASSTYPE_CELL, true, true, false, true }, + { EXC_FMLATYPE_MATRIX, EXC_CLASSTYPE_ARRAY, true, true, false, true }, + { EXC_FMLATYPE_CONDFMT, EXC_CLASSTYPE_ARRAY, true, false, false, false }, + { EXC_FMLATYPE_DATAVAL, EXC_CLASSTYPE_ARRAY, true, false, false, false }, + { EXC_FMLATYPE_NAME, EXC_CLASSTYPE_NAME, false, false, true, true }, + { EXC_FMLATYPE_CHART, EXC_CLASSTYPE_NAME, true, false, true, true }, + { EXC_FMLATYPE_CONTROL, EXC_CLASSTYPE_NAME, true, false, false, false }, + { EXC_FMLATYPE_WQUERY, EXC_CLASSTYPE_NAME, true, false, true, false }, + { EXC_FMLATYPE_LISTVAL, EXC_CLASSTYPE_NAME, true, false, false, false } +}; + +// ---------------------------------------------------------------------------- + +/** Working data of the formula compiler. Used to push onto a stack for recursive calls. */ +struct XclExpCompData +{ + typedef ScfRef< ScTokenArray > ScTokenArrayRef; + + const XclExpCompConfig& mrCfg; /// Configuration for current formula type. + ScTokenArrayRef mxOwnScTokArr; /// Own clone of a Calc token array. + XclTokenArrayIterator maTokArrIt; /// Iterator in Calc token array. + XclExpLinkManager* mpLinkMgr; /// Link manager for current context (local/global). + XclExpRefLog* mpRefLog; /// Log for external references. + const ScAddress* mpScBasePos; /// Current cell position of the formula. + + ScfUInt8Vec maTokVec; /// Byte vector containing token data. + ScfUInt8Vec maExtDataVec; /// Byte vector containing extended data (arrays, stacked NLRs). + XclExpOperandListVector maOpListVec; /// Formula structure, maps operators to their operands. + ScfUInt16Vec maOpPosStack; /// Stack with positions of operand tokens waiting for an operator. + bool mbStopAtSep; /// True = Stop subexpression creation at an ocSep token. + bool mbVolatile; /// True = Formula contains volatile function. + bool mbOk; /// Current state of the compiler. + + explicit XclExpCompData( const XclExpCompConfig* pCfg ); +}; + +XclExpCompData::XclExpCompData( const XclExpCompConfig* pCfg ) : + mrCfg( pCfg ? *pCfg : spConfigTable[ 0 ] ), + mpLinkMgr( 0 ), + mpRefLog( 0 ), + mpScBasePos( 0 ), + mbStopAtSep( false ), + mbVolatile( false ), + mbOk( pCfg != 0 ) +{ + DBG_ASSERT( pCfg, "XclExpFmlaCompImpl::Init - unknown formula type" ); +} + +} // namespace + +// ---------------------------------------------------------------------------- + +/** Implementation class of the export formula compiler. */ +class XclExpFmlaCompImpl : protected XclExpRoot, protected XclTokenArrayHelper +{ +public: + explicit XclExpFmlaCompImpl( const XclExpRoot& rRoot ); + + /** Creates an Excel token array from the passed Calc token array. */ + XclTokenArrayRef CreateFormula( + XclFormulaType eType, const ScTokenArray& rScTokArr, + const ScAddress* pScBasePos = 0, XclExpRefLog* pRefLog = 0 ); + /** Creates a single error token containing the passed error code. */ + XclTokenArrayRef CreateErrorFormula( sal_uInt8 nErrCode ); + /** Creates a single token for a special cell reference. */ + XclTokenArrayRef CreateSpecialRefFormula( sal_uInt8 nTokenId, const XclAddress& rXclPos ); + /** Creates a single tNameXR token for a reference to an external name. */ + XclTokenArrayRef CreateNameXFormula( sal_uInt16 nExtSheet, sal_uInt16 nExtName ); + + /** Returns true, if the passed formula type allows 3D references only. */ + bool Is3DRefOnly( XclFormulaType eType ) const; + + // ------------------------------------------------------------------------ +private: + const XclExpCompConfig* GetConfigForType( XclFormulaType eType ) const; + inline sal_uInt16 GetSize() const { return static_cast< sal_uInt16 >( mxData->maTokVec.size() ); } + + void Init( XclFormulaType eType ); + void Init( XclFormulaType eType, const ScTokenArray& rScTokArr, + const ScAddress* pScBasePos, XclExpRefLog* pRefLog ); + + void RecalcTokenClasses(); + void RecalcTokenClass( const XclExpTokenConvInfo& rConvInfo, XclFuncParamConv ePrevConv, XclExpClassConv ePrevClassConv, bool bWasRefClass ); + + void FinalizeFormula(); + XclTokenArrayRef CreateTokenArray(); + + // compiler --------------------------------------------------------------- + // XclExpScToken: pass-by-value and return-by-value is intended + + const FormulaToken* GetNextRawToken(); + const FormulaToken* PeekNextRawToken( bool bSkipSpaces ) const; + + bool GetNextToken( XclExpScToken& rTokData ); + XclExpScToken GetNextToken(); + + XclExpScToken Expression( XclExpScToken aTokData, bool bInParentheses, bool bStopAtSep ); + XclExpScToken SkipExpression( XclExpScToken aTokData, bool bStopAtSep ); + + XclExpScToken OrTerm( XclExpScToken aTokData, bool bInParentheses ); + XclExpScToken AndTerm( XclExpScToken aTokData, bool bInParentheses ); + XclExpScToken CompareTerm( XclExpScToken aTokData, bool bInParentheses ); + XclExpScToken ConcatTerm( XclExpScToken aTokData, bool bInParentheses ); + XclExpScToken AddSubTerm( XclExpScToken aTokData, bool bInParentheses ); + XclExpScToken MulDivTerm( XclExpScToken aTokData, bool bInParentheses ); + XclExpScToken PowTerm( XclExpScToken aTokData, bool bInParentheses ); + XclExpScToken UnaryPostTerm( XclExpScToken aTokData, bool bInParentheses ); + XclExpScToken UnaryPreTerm( XclExpScToken aTokData, bool bInParentheses ); + XclExpScToken ListTerm( XclExpScToken aTokData, bool bInParentheses ); + XclExpScToken IntersectTerm( XclExpScToken aTokData, bool& rbHasRefOp ); + XclExpScToken RangeTerm( XclExpScToken aTokData, bool& rbHasRefOp ); + XclExpScToken Factor( XclExpScToken aTokData ); + + // formula structure ------------------------------------------------------ + + void ProcessDouble( const XclExpScToken& rTokData ); + void ProcessString( const XclExpScToken& rTokData ); + void ProcessError( const XclExpScToken& rTokData ); + void ProcessMissing( const XclExpScToken& rTokData ); + void ProcessBad( const XclExpScToken& rTokData ); + void ProcessParentheses( const XclExpScToken& rTokData ); + void ProcessBoolean( const XclExpScToken& rTokData ); + void ProcessDdeLink( const XclExpScToken& rTokData ); + void ProcessExternal( const XclExpScToken& rTokData ); + void ProcessMatrix( const XclExpScToken& rTokData ); + + void ProcessFunction( const XclExpScToken& rTokData ); + void PrepareFunction( XclExpFuncData& rFuncData ); + void FinishFunction( XclExpFuncData& rFuncData, sal_uInt8 nCloseSpaces ); + void FinishIfFunction( XclExpFuncData& rFuncData ); + void FinishChooseFunction( XclExpFuncData& rFuncData ); + + XclExpScToken ProcessParam( XclExpScToken aTokData, XclExpFuncData& rFuncData ); + void PrepareParam( XclExpFuncData& rFuncData ); + void FinishParam( XclExpFuncData& rFuncData ); + void AppendDefaultParam( XclExpFuncData& rFuncData ); + void AppendTrailingParam( XclExpFuncData& rFuncData ); + + // reference handling ----------------------------------------------------- + + SCTAB GetScTab( const ScSingleRefData& rRefData ) const; + bool IsRef2D( const ScSingleRefData& rRefData ) const; + bool IsRef2D( const ScComplexRefData& rRefData ) const; + + void ConvertRefData( ScSingleRefData& rRefData, XclAddress& rXclPos, + bool bNatLangRef, bool bTruncMaxCol, bool bTruncMaxRow ) const; + void ConvertRefData( ScComplexRefData& rRefData, XclRange& rXclRange, + bool bNatLangRef ) const; + + XclExpRefLogEntry* GetNewRefLogEntry(); + void ProcessCellRef( const XclExpScToken& rTokData ); + void ProcessRangeRef( const XclExpScToken& rTokData ); + void ProcessExternalCellRef( const XclExpScToken& rTokData ); + void ProcessExternalRangeRef( const XclExpScToken& rTokData ); + void ProcessDefinedName( const XclExpScToken& rTokData ); + void ProcessExternalName( const XclExpScToken& rTokData ); + void ProcessDatabaseArea( const XclExpScToken& rTokData ); + + // token vector ----------------------------------------------------------- + + void PushOperandPos( sal_uInt16 nTokPos ); + void PushOperatorPos( sal_uInt16 nTokPos, const XclExpOperandListRef& rxOperands ); + sal_uInt16 PopOperandPos(); + + void Append( sal_uInt8 nData ); + void Append( sal_uInt8 nData, size_t nCount ); + void Append( sal_uInt16 nData ); + void Append( sal_uInt32 nData ); + void Append( double fData ); + void Append( const String& rString ); + + void AppendAddress( const XclAddress& rXclPos ); + void AppendRange( const XclRange& rXclRange ); + + void AppendSpaceToken( sal_uInt8 nType, sal_uInt8 nCount ); + + void AppendOperandTokenId( sal_uInt8 nTokenId, sal_uInt8 nSpaces = 0 ); + void AppendIntToken( sal_uInt16 nValue, sal_uInt8 nSpaces = 0 ); + void AppendNumToken( double fValue, sal_uInt8 nSpaces = 0 ); + void AppendBoolToken( bool bValue, sal_uInt8 nSpaces = 0 ); + void AppendErrorToken( sal_uInt8 nErrCode, sal_uInt8 nSpaces = 0 ); + void AppendMissingToken( sal_uInt8 nSpaces = 0 ); + void AppendNameToken( sal_uInt16 nNameIdx, sal_uInt8 nSpaces = 0 ); + void AppendMissingNameToken( const String& rName, sal_uInt8 nSpaces = 0 ); + void AppendNameXToken( sal_uInt16 nExtSheet, sal_uInt16 nExtName, sal_uInt8 nSpaces = 0 ); + void AppendMacroCallToken( const XclExpExtFuncData& rExtFuncData, sal_uInt8 nSpaces = 0 ); + void AppendAddInCallToken( const XclExpExtFuncData& rExtFuncData, sal_uInt8 nSpaces = 0 ); + void AppendEuroToolCallToken( const XclExpExtFuncData& rExtFuncData, sal_uInt8 nSpaces = 0 ); + + void AppendOperatorTokenId( sal_uInt8 nTokenId, const XclExpOperandListRef& rxOperands, sal_uInt8 nSpaces = 0 ); + void AppendUnaryOperatorToken( sal_uInt8 nTokenId, sal_uInt8 nSpaces = 0 ); + void AppendBinaryOperatorToken( sal_uInt8 nTokenId, bool bValType, sal_uInt8 nSpaces = 0 ); + void AppendLogicalOperatorToken( sal_uInt16 nXclFuncIdx, sal_uInt8 nOpCount ); + void AppendFuncToken( const XclExpFuncData& rFuncData ); + + void AppendParenToken( sal_uInt8 nOpenSpaces = 0, sal_uInt8 nCloseSpaces = 0 ); + void AppendJumpToken( XclExpFuncData& rFuncData, sal_uInt8 nAttrType ); + + void InsertZeros( sal_uInt16 nInsertPos, sal_uInt16 nInsertSize ); + void Overwrite( sal_uInt16 nWriteToPos, sal_uInt16 nOffset ); + + void UpdateAttrGoto( sal_uInt16 nAttrPos ); + + bool IsSpaceToken( sal_uInt16 nPos ) const; + void RemoveTrailingParen(); + + void AppendExt( sal_uInt8 nData ); + void AppendExt( sal_uInt8 nData, size_t nCount ); + void AppendExt( sal_uInt16 nData ); + void AppendExt( sal_uInt32 nData ); + void AppendExt( double fData ); + void AppendExt( const String& rString ); + + // ------------------------------------------------------------------------ +private: + typedef ::std::map< XclFormulaType, XclExpCompConfig > XclExpCompConfigMap; + typedef ScfRef< XclExpCompData > XclExpCompDataRef; + typedef ::std::vector< XclExpCompDataRef > XclExpCompDataVector; + + XclExpCompConfigMap maCfgMap; /// Compiler configuration map for all formula types. + XclFunctionProvider maFuncProv; /// Excel function data provider. + XclExpCompDataRef mxData; /// Working data for current formula. + XclExpCompDataVector maDataStack; /// Stack for working data, when compiler is called recursively. + const XclBiff meBiff; /// Cached BIFF version to save GetBiff() calls. + const SCsCOL mnMaxAbsCol; /// Maximum column index. + const SCsROW mnMaxAbsRow; /// Maximum row index. + const SCsCOL mnMaxScCol; /// Maximum column index in Calc itself. + const SCsROW mnMaxScRow; /// Maximum row index in Calc itself. + const sal_uInt16 mnMaxColMask; /// Mask to delete invalid bits in column fields. + const sal_uInt16 mnMaxRowMask; /// Mask to delete invalid bits in row fields. +}; + +// ---------------------------------------------------------------------------- + +XclExpFmlaCompImpl::XclExpFmlaCompImpl( const XclExpRoot& rRoot ) : + XclExpRoot( rRoot ), + maFuncProv( rRoot ), + meBiff( rRoot.GetBiff() ), + mnMaxAbsCol( static_cast< SCsCOL >( rRoot.GetXclMaxPos().Col() ) ), + mnMaxAbsRow( static_cast< SCsROW >( rRoot.GetXclMaxPos().Row() ) ), + mnMaxScCol( static_cast< SCsCOL >( rRoot.GetScMaxPos().Col() ) ), + mnMaxScRow( static_cast< SCsROW >( rRoot.GetScMaxPos().Row() ) ), + mnMaxColMask( static_cast< sal_uInt16 >( rRoot.GetXclMaxPos().Col() ) ), + mnMaxRowMask( static_cast< sal_uInt16 >( rRoot.GetXclMaxPos().Row() ) ) +{ + // build the configuration map + for( const XclExpCompConfig* pEntry = spConfigTable; pEntry != STATIC_TABLE_END( spConfigTable ); ++pEntry ) + maCfgMap[ pEntry->meType ] = *pEntry; +} + +XclTokenArrayRef XclExpFmlaCompImpl::CreateFormula( XclFormulaType eType, + const ScTokenArray& rScTokArr, const ScAddress* pScBasePos, XclExpRefLog* pRefLog ) +{ + // initialize the compiler + Init( eType, rScTokArr, pScBasePos, pRefLog ); + + // start compilation, if initialization didn't fail + if( mxData->mbOk ) + { + XclExpScToken aTokData( GetNextToken() ); + USHORT nScError = rScTokArr.GetCodeError(); + if( (nScError != 0) && (!aTokData.Is() || (aTokData.GetOpCode() == ocStop)) ) + { + // #i50253# convert simple ocStop token to error code formula (e.g. =#VALUE!) + AppendErrorToken( XclTools::GetXclErrorCode( nScError ), aTokData.mnSpaces ); + } + else if( aTokData.Is() ) + { + aTokData = Expression( aTokData, false, false ); + } + else + { + DBG_ERRORFILE( "XclExpFmlaCompImpl::CreateFormula - empty token array" ); + mxData->mbOk = false; + } + + if( mxData->mbOk ) + { + // #i44907# auto-generated SUBTOTAL formula cells have trailing ocStop token + mxData->mbOk = !aTokData.Is() || (aTokData.GetOpCode() == ocStop); + DBG_ASSERT( mxData->mbOk, "XclExpFmlaCompImpl::CreateFormula - unknown garbage behind formula" ); + } + } + + // finalize (add tAttrVolatile token, calculate all token classes) + RecalcTokenClasses(); + FinalizeFormula(); + + // leave recursive call, create and return the final token array + return CreateTokenArray(); +} + +XclTokenArrayRef XclExpFmlaCompImpl::CreateErrorFormula( sal_uInt8 nErrCode ) +{ + Init( EXC_FMLATYPE_NAME ); + AppendErrorToken( nErrCode ); + return CreateTokenArray(); +} + +XclTokenArrayRef XclExpFmlaCompImpl::CreateSpecialRefFormula( sal_uInt8 nTokenId, const XclAddress& rXclPos ) +{ + Init( EXC_FMLATYPE_NAME ); + AppendOperandTokenId( nTokenId ); + Append( rXclPos.mnRow ); + Append( rXclPos.mnCol ); // do not use AppendAddress(), we always need 16-bit column here + return CreateTokenArray(); +} + +XclTokenArrayRef XclExpFmlaCompImpl::CreateNameXFormula( sal_uInt16 nExtSheet, sal_uInt16 nExtName ) +{ + Init( EXC_FMLATYPE_NAME ); + AppendNameXToken( nExtSheet, nExtName ); + return CreateTokenArray(); +} + +bool XclExpFmlaCompImpl::Is3DRefOnly( XclFormulaType eType ) const +{ + const XclExpCompConfig* pCfg = GetConfigForType( eType ); + return pCfg && pCfg->mb3DRefOnly; +} + +// private -------------------------------------------------------------------- + +const XclExpCompConfig* XclExpFmlaCompImpl::GetConfigForType( XclFormulaType eType ) const +{ + XclExpCompConfigMap::const_iterator aIt = maCfgMap.find( eType ); + DBG_ASSERT( aIt != maCfgMap.end(), "XclExpFmlaCompImpl::GetConfigForType - unknown formula type" ); + return (aIt == maCfgMap.end()) ? 0 : &aIt->second; +} + +void XclExpFmlaCompImpl::Init( XclFormulaType eType ) +{ + // compiler invoked recursively? - store old working data + if( mxData.get() ) + maDataStack.push_back( mxData ); + // new compiler working data structure + mxData.reset( new XclExpCompData( GetConfigForType( eType ) ) ); +} + +void XclExpFmlaCompImpl::Init( XclFormulaType eType, const ScTokenArray& rScTokArr, + const ScAddress* pScBasePos, XclExpRefLog* pRefLog ) +{ + // common initialization + Init( eType ); + + // special initialization + if( mxData->mbOk ) switch( mxData->mrCfg.meType ) + { + case EXC_FMLATYPE_CELL: + case EXC_FMLATYPE_MATRIX: + case EXC_FMLATYPE_CHART: + mxData->mbOk = pScBasePos != 0; + DBG_ASSERT( mxData->mbOk, "XclExpFmlaCompImpl::Init - missing cell address" ); + mxData->mpScBasePos = pScBasePos; + break; + case EXC_FMLATYPE_SHARED: + mxData->mbOk = pScBasePos != 0; + DBG_ASSERT( mxData->mbOk, "XclExpFmlaCompImpl::Init - missing cell address" ); + // clone the passed token array, convert references relative to current cell position + mxData->mxOwnScTokArr.reset( rScTokArr.Clone() ); + ScCompiler::MoveRelWrap( *mxData->mxOwnScTokArr, GetDocPtr(), *pScBasePos, MAXCOL, MAXROW ); + // don't remember pScBasePos in mxData->mpScBasePos, shared formulas use real relative refs + break; + default:; + } + + if( mxData->mbOk ) + { + // link manager to be used + mxData->mpLinkMgr = mxData->mrCfg.mbLocalLinkMgr ? &GetLocalLinkManager() : &GetGlobalLinkManager(); + + // token array iterator (use cloned token array if present) + mxData->maTokArrIt.Init( mxData->mxOwnScTokArr.is() ? *mxData->mxOwnScTokArr : rScTokArr, false ); + mxData->mpRefLog = pRefLog; + } +} + +void XclExpFmlaCompImpl::RecalcTokenClasses() +{ + if( mxData->mbOk ) + { + mxData->mbOk = mxData->maOpPosStack.size() == 1; + DBG_ASSERT( mxData->mbOk, "XclExpFmlaCompImpl::RecalcTokenClasses - position of root token expected on stack" ); + if( mxData->mbOk ) + { + /* Cell and array formulas start with VAL conversion and VALTYPE + parameter type, defined names start with ARR conversion and + REFTYPE parameter type for the root token. */ + XclExpOperandList aOperands; + bool bNameFmla = mxData->mrCfg.meClassType == EXC_CLASSTYPE_NAME; + XclFuncParamConv eParamConv = bNameFmla ? EXC_PARAMCONV_ARR : EXC_PARAMCONV_VAL; + XclExpClassConv eClassConv = bNameFmla ? EXC_CLASSCONV_ARR : EXC_CLASSCONV_VAL; + XclExpTokenConvInfo aConvInfo = { PopOperandPos(), eParamConv, !bNameFmla }; + RecalcTokenClass( aConvInfo, eParamConv, eClassConv, bNameFmla ); + } + + // clear operand vectors (calls to the expensive InsertZeros() may follow) + mxData->maOpListVec.clear(); + mxData->maOpPosStack.clear(); + } +} + +void XclExpFmlaCompImpl::RecalcTokenClass( const XclExpTokenConvInfo& rConvInfo, + XclFuncParamConv ePrevConv, XclExpClassConv ePrevClassConv, bool bWasRefClass ) +{ + DBG_ASSERT( rConvInfo.mnTokPos < GetSize(), "XclExpFmlaCompImpl::RecalcTokenClass - invalid token position" ); + sal_uInt8& rnTokenId = mxData->maTokVec[ rConvInfo.mnTokPos ]; + sal_uInt8 nTokClass = GetTokenClass( rnTokenId ); + + // REF tokens in VALTYPE parameters behave like VAL tokens + if( rConvInfo.mbValType && (nTokClass == EXC_TOKCLASS_REF) ) + ChangeTokenClass( rnTokenId, nTokClass = EXC_TOKCLASS_VAL ); + + // replace RPO conversion of operator with parent conversion + XclFuncParamConv eConv = (rConvInfo.meConv == EXC_PARAMCONV_RPO) ? ePrevConv : rConvInfo.meConv; + + // find the effective token class conversion to be performed for this token + XclExpClassConv eClassConv = EXC_CLASSCONV_ORG; + switch( eConv ) + { + case EXC_PARAMCONV_ORG: + // conversion is forced independent of parent conversion + eClassConv = EXC_CLASSCONV_ORG; + break; + case EXC_PARAMCONV_VAL: + // conversion is forced independent of parent conversion + eClassConv = EXC_CLASSCONV_VAL; + break; + case EXC_PARAMCONV_ARR: + // conversion is forced independent of parent conversion + eClassConv = EXC_CLASSCONV_ARR; + break; + case EXC_PARAMCONV_RPT: + switch( ePrevConv ) + { + case EXC_PARAMCONV_ORG: + case EXC_PARAMCONV_VAL: + case EXC_PARAMCONV_ARR: + /* If parent token has REF class (REF token in REFTYPE + function parameter), then RPT does not repeat the + previous explicit ORG or ARR conversion, but always + falls back to VAL conversion. */ + eClassConv = bWasRefClass ? EXC_CLASSCONV_VAL : ePrevClassConv; + break; + case EXC_PARAMCONV_RPT: + // nested RPT repeats the previous effective conversion + eClassConv = ePrevClassConv; + break; + case EXC_PARAMCONV_RPX: + /* If parent token has REF class (REF token in REFTYPE + function parameter), then RPX repeats the previous + effective conversion (wich will be either ORG or ARR, + but never VAL), otherwise falls back to ORG conversion. */ + eClassConv = bWasRefClass ? ePrevClassConv : EXC_CLASSCONV_ORG; + break; + case EXC_PARAMCONV_RPO: // does not occur + break; + } + break; + case EXC_PARAMCONV_RPX: + /* If current token still has REF class, set previous effective + conversion as current conversion. This will not have an effect + on the REF token but is needed for RPT parameters of this + function that want to repeat this conversion type. If current + token is VAL or ARR class, the previous ARR conversion will be + repeated on the token, but VAL conversion will not. */ + eClassConv = ((nTokClass == EXC_TOKCLASS_REF) || (ePrevClassConv == EXC_CLASSCONV_ARR)) ? + ePrevClassConv : EXC_CLASSCONV_ORG; + break; + case EXC_PARAMCONV_RPO: // does not occur (see above) + break; + } + + // do the token class conversion + switch( eClassConv ) + { + case EXC_CLASSCONV_ORG: + /* Cell formulas: leave the current token class. Cell formulas + are the only type of formulas where all tokens can keep + their original token class. + Array and defined name formulas: convert VAL to ARR. */ + if( (mxData->mrCfg.meClassType != EXC_CLASSTYPE_CELL) && (nTokClass == EXC_TOKCLASS_VAL) ) + ChangeTokenClass( rnTokenId, nTokClass = EXC_TOKCLASS_ARR ); + break; + case EXC_CLASSCONV_VAL: + // convert ARR to VAL + if( nTokClass == EXC_TOKCLASS_ARR ) + ChangeTokenClass( rnTokenId, nTokClass = EXC_TOKCLASS_VAL ); + break; + case EXC_CLASSCONV_ARR: + // convert VAL to ARR + if( nTokClass == EXC_TOKCLASS_VAL ) + ChangeTokenClass( rnTokenId, nTokClass = EXC_TOKCLASS_ARR ); + break; + } + + // do conversion for nested operands, if token is an operator or function + if( rConvInfo.mnTokPos < mxData->maOpListVec.size() ) + if( const XclExpOperandList* pOperands = mxData->maOpListVec[ rConvInfo.mnTokPos ].get() ) + for( XclExpOperandList::const_iterator aIt = pOperands->begin(), aEnd = pOperands->end(); aIt != aEnd; ++aIt ) + RecalcTokenClass( *aIt, eConv, eClassConv, nTokClass == EXC_TOKCLASS_REF ); +} + +void XclExpFmlaCompImpl::FinalizeFormula() +{ + if( mxData->mbOk ) + { + // Volatile? Add a tAttrVolatile token at the beginning of the token array. + if( mxData->mbVolatile ) + { + // tAttrSpace token can be extended with volatile flag + if( !IsSpaceToken( 0 ) ) + { + InsertZeros( 0, 4 ); + mxData->maTokVec[ 0 ] = EXC_TOKID_ATTR; + } + mxData->maTokVec[ 1 ] |= EXC_TOK_ATTR_VOLATILE; + } + + // Token array too long? -> error + mxData->mbOk = mxData->maTokVec.size() <= EXC_TOKARR_MAXLEN; + } + + if( !mxData->mbOk ) + { + // Any unrecoverable error? -> Create a =#NA formula. + mxData->maTokVec.clear(); + mxData->maExtDataVec.clear(); + mxData->mbVolatile = false; + AppendErrorToken( EXC_ERR_NA ); + } +} + +XclTokenArrayRef XclExpFmlaCompImpl::CreateTokenArray() +{ + // create the Excel token array from working data before resetting mxData + DBG_ASSERT( mxData->mrCfg.mbAllowArrays || mxData->maExtDataVec.empty(), "XclExpFmlaCompImpl::CreateTokenArray - unexpected extended data" ); + if( !mxData->mrCfg.mbAllowArrays ) + mxData->maExtDataVec.clear(); + XclTokenArrayRef xTokArr( new XclTokenArray( mxData->maTokVec, mxData->maExtDataVec, mxData->mbVolatile ) ); + mxData.reset(); + + // compiler invoked recursively? - restore old working data + if( !maDataStack.empty() ) + { + mxData = maDataStack.back(); + maDataStack.pop_back(); + } + + return xTokArr; +} + +// compiler ------------------------------------------------------------------- + +const FormulaToken* XclExpFmlaCompImpl::GetNextRawToken() +{ + const FormulaToken* pScToken = mxData->maTokArrIt.Get(); + ++mxData->maTokArrIt; + return pScToken; +} + +const FormulaToken* XclExpFmlaCompImpl::PeekNextRawToken( bool bSkipSpaces ) const +{ + /* Returns pointer to next raw token in the token array. The token array + iterator already points to the next token (A call to GetNextToken() + always increases the iterator), so this function just returns the token + the iterator points to. To skip space tokens, a copy of the iterator is + created and set to the passed skip-spaces mode. If spaces have to be + skipped, and the iterator currently points to a space token, the + constructor will move it to the next non-space token. */ + XclTokenArrayIterator aTempIt( mxData->maTokArrIt, bSkipSpaces ); + return aTempIt.Get(); +} + +bool XclExpFmlaCompImpl::GetNextToken( XclExpScToken& rTokData ) +{ + rTokData.mpScToken = GetNextRawToken(); + rTokData.mnSpaces = (rTokData.GetOpCode() == ocSpaces) ? rTokData.mpScToken->GetByte() : 0; + while( rTokData.GetOpCode() == ocSpaces ) + rTokData.mpScToken = GetNextRawToken(); + return rTokData.Is(); +} + +XclExpScToken XclExpFmlaCompImpl::GetNextToken() +{ + XclExpScToken aTokData; + GetNextToken( aTokData ); + return aTokData; +} + +namespace { + +/** Returns the Excel token ID of a comparison operator or EXC_TOKID_NONE. */ +inline sal_uInt8 lclGetCompareTokenId( OpCode eOpCode ) +{ + switch( eOpCode ) + { + case ocLess: return EXC_TOKID_LT; + case ocLessEqual: return EXC_TOKID_LE; + case ocEqual: return EXC_TOKID_EQ; + case ocGreaterEqual: return EXC_TOKID_GE; + case ocGreater: return EXC_TOKID_GT; + case ocNotEqual: return EXC_TOKID_NE; + default:; + } + return EXC_TOKID_NONE; +} + +/** Returns the Excel token ID of a string concatenation operator or EXC_TOKID_NONE. */ +inline sal_uInt8 lclGetConcatTokenId( OpCode eOpCode ) +{ + return (eOpCode == ocAmpersand) ? EXC_TOKID_CONCAT : EXC_TOKID_NONE; +} + +/** Returns the Excel token ID of an addition/subtraction operator or EXC_TOKID_NONE. */ +inline sal_uInt8 lclGetAddSubTokenId( OpCode eOpCode ) +{ + switch( eOpCode ) + { + case ocAdd: return EXC_TOKID_ADD; + case ocSub: return EXC_TOKID_SUB; + default:; + } + return EXC_TOKID_NONE; +} + +/** Returns the Excel token ID of a multiplication/division operator or EXC_TOKID_NONE. */ +inline sal_uInt8 lclGetMulDivTokenId( OpCode eOpCode ) +{ + switch( eOpCode ) + { + case ocMul: return EXC_TOKID_MUL; + case ocDiv: return EXC_TOKID_DIV; + default:; + } + return EXC_TOKID_NONE; +} + +/** Returns the Excel token ID of a power operator or EXC_TOKID_NONE. */ +inline sal_uInt8 lclGetPowTokenId( OpCode eOpCode ) +{ + return (eOpCode == ocPow) ? EXC_TOKID_POWER : EXC_TOKID_NONE; +} + +/** Returns the Excel token ID of a trailing unary operator or EXC_TOKID_NONE. */ +inline sal_uInt8 lclGetUnaryPostTokenId( OpCode eOpCode ) +{ + return (eOpCode == ocPercentSign) ? EXC_TOKID_PERCENT : EXC_TOKID_NONE; +} + +/** Returns the Excel token ID of a leading unary operator or EXC_TOKID_NONE. */ +inline sal_uInt8 lclGetUnaryPreTokenId( OpCode eOpCode ) +{ + switch( eOpCode ) + { + case ocAdd: return EXC_TOKID_UPLUS; // +(1) + case ocNeg: return EXC_TOKID_UMINUS; // NEG(1) + case ocNegSub: return EXC_TOKID_UMINUS; // -(1) + default:; + } + return EXC_TOKID_NONE; +} + +/** Returns the Excel token ID of a reference list operator or EXC_TOKID_NONE. */ +inline sal_uInt8 lclGetListTokenId( OpCode eOpCode, bool bStopAtSep ) +{ + return ((eOpCode == ocUnion) || (!bStopAtSep && (eOpCode == ocSep))) ? EXC_TOKID_LIST : EXC_TOKID_NONE; +} + +/** Returns the Excel token ID of a reference intersection operator or EXC_TOKID_NONE. */ +inline sal_uInt8 lclGetIntersectTokenId( OpCode eOpCode ) +{ + return (eOpCode == ocIntersect) ? EXC_TOKID_ISECT : EXC_TOKID_NONE; +} + +/** Returns the Excel token ID of a reference range operator or EXC_TOKID_NONE. */ +inline sal_uInt8 lclGetRangeTokenId( OpCode eOpCode ) +{ + return (eOpCode == ocRange) ? EXC_TOKID_RANGE : EXC_TOKID_NONE; +} + +} // namespace + +XclExpScToken XclExpFmlaCompImpl::Expression( XclExpScToken aTokData, bool bInParentheses, bool bStopAtSep ) +{ + if( mxData->mbOk && aTokData.Is() ) + { + // remember old stop-at-ocSep mode, restored below + bool bOldStopAtSep = mxData->mbStopAtSep; + mxData->mbStopAtSep = bStopAtSep; + // start compilation of the subexpression + aTokData = OrTerm( aTokData, bInParentheses ); + // restore old stop-at-ocSep mode + mxData->mbStopAtSep = bOldStopAtSep; + } + return aTokData; +} + +XclExpScToken XclExpFmlaCompImpl::SkipExpression( XclExpScToken aTokData, bool bStopAtSep ) +{ + while( mxData->mbOk && aTokData.Is() && (aTokData.GetOpCode() != ocClose) && (!bStopAtSep || (aTokData.GetOpCode() != ocSep)) ) + { + if( aTokData.GetOpCode() == ocOpen ) + { + aTokData = SkipExpression( GetNextToken(), false ); + if( mxData->mbOk ) mxData->mbOk = aTokData.GetOpCode() == ocClose; + } + aTokData = GetNextToken(); + } + return aTokData; +} + +XclExpScToken XclExpFmlaCompImpl::OrTerm( XclExpScToken aTokData, bool bInParentheses ) +{ + aTokData = AndTerm( aTokData, bInParentheses ); + sal_uInt8 nParamCount = 1; + while( mxData->mbOk && (aTokData.GetOpCode() == ocOr) ) + { + RemoveTrailingParen(); + aTokData = AndTerm( GetNextToken(), bInParentheses ); + RemoveTrailingParen(); + ++nParamCount; + if( mxData->mbOk ) mxData->mbOk = nParamCount <= EXC_FUNC_MAXPARAM; + } + if( mxData->mbOk && (nParamCount > 1) ) + AppendLogicalOperatorToken( EXC_FUNCID_OR, nParamCount ); + return aTokData; +} + +XclExpScToken XclExpFmlaCompImpl::AndTerm( XclExpScToken aTokData, bool bInParentheses ) +{ + aTokData = CompareTerm( aTokData, bInParentheses ); + sal_uInt8 nParamCount = 1; + while( mxData->mbOk && (aTokData.GetOpCode() == ocAnd) ) + { + RemoveTrailingParen(); + aTokData = CompareTerm( GetNextToken(), bInParentheses ); + RemoveTrailingParen(); + ++nParamCount; + if( mxData->mbOk ) mxData->mbOk = nParamCount <= EXC_FUNC_MAXPARAM; + } + if( mxData->mbOk && (nParamCount > 1) ) + AppendLogicalOperatorToken( EXC_FUNCID_AND, nParamCount ); + return aTokData; +} + +XclExpScToken XclExpFmlaCompImpl::CompareTerm( XclExpScToken aTokData, bool bInParentheses ) +{ + aTokData = ConcatTerm( aTokData, bInParentheses ); + sal_uInt8 nOpTokenId = EXC_TOKID_NONE; + while( mxData->mbOk && ((nOpTokenId = lclGetCompareTokenId( aTokData.GetOpCode() )) != EXC_TOKID_NONE) ) + { + sal_uInt8 nSpaces = aTokData.mnSpaces; + aTokData = ConcatTerm( GetNextToken(), bInParentheses ); + AppendBinaryOperatorToken( nOpTokenId, true, nSpaces ); + } + return aTokData; +} + +XclExpScToken XclExpFmlaCompImpl::ConcatTerm( XclExpScToken aTokData, bool bInParentheses ) +{ + aTokData = AddSubTerm( aTokData, bInParentheses ); + sal_uInt8 nOpTokenId = EXC_TOKID_NONE; + while( mxData->mbOk && ((nOpTokenId = lclGetConcatTokenId( aTokData.GetOpCode() )) != EXC_TOKID_NONE) ) + { + sal_uInt8 nSpaces = aTokData.mnSpaces; + aTokData = AddSubTerm( GetNextToken(), bInParentheses ); + AppendBinaryOperatorToken( nOpTokenId, true, nSpaces ); + } + return aTokData; +} + +XclExpScToken XclExpFmlaCompImpl::AddSubTerm( XclExpScToken aTokData, bool bInParentheses ) +{ + aTokData = MulDivTerm( aTokData, bInParentheses ); + sal_uInt8 nOpTokenId = EXC_TOKID_NONE; + while( mxData->mbOk && ((nOpTokenId = lclGetAddSubTokenId( aTokData.GetOpCode() )) != EXC_TOKID_NONE) ) + { + sal_uInt8 nSpaces = aTokData.mnSpaces; + aTokData = MulDivTerm( GetNextToken(), bInParentheses ); + AppendBinaryOperatorToken( nOpTokenId, true, nSpaces ); + } + return aTokData; +} + +XclExpScToken XclExpFmlaCompImpl::MulDivTerm( XclExpScToken aTokData, bool bInParentheses ) +{ + aTokData = PowTerm( aTokData, bInParentheses ); + sal_uInt8 nOpTokenId = EXC_TOKID_NONE; + while( mxData->mbOk && ((nOpTokenId = lclGetMulDivTokenId( aTokData.GetOpCode() )) != EXC_TOKID_NONE) ) + { + sal_uInt8 nSpaces = aTokData.mnSpaces; + aTokData = PowTerm( GetNextToken(), bInParentheses ); + AppendBinaryOperatorToken( nOpTokenId, true, nSpaces ); + } + return aTokData; +} + +XclExpScToken XclExpFmlaCompImpl::PowTerm( XclExpScToken aTokData, bool bInParentheses ) +{ + aTokData = UnaryPostTerm( aTokData, bInParentheses ); + sal_uInt8 nOpTokenId = EXC_TOKID_NONE; + while( mxData->mbOk && ((nOpTokenId = lclGetPowTokenId( aTokData.GetOpCode() )) != EXC_TOKID_NONE) ) + { + sal_uInt8 nSpaces = aTokData.mnSpaces; + aTokData = UnaryPostTerm( GetNextToken(), bInParentheses ); + AppendBinaryOperatorToken( nOpTokenId, true, nSpaces ); + } + return aTokData; +} + +XclExpScToken XclExpFmlaCompImpl::UnaryPostTerm( XclExpScToken aTokData, bool bInParentheses ) +{ + aTokData = UnaryPreTerm( aTokData, bInParentheses ); + sal_uInt8 nOpTokenId = EXC_TOKID_NONE; + while( mxData->mbOk && ((nOpTokenId = lclGetUnaryPostTokenId( aTokData.GetOpCode() )) != EXC_TOKID_NONE) ) + { + AppendUnaryOperatorToken( nOpTokenId, aTokData.mnSpaces ); + GetNextToken( aTokData ); + } + return aTokData; +} + +XclExpScToken XclExpFmlaCompImpl::UnaryPreTerm( XclExpScToken aTokData, bool bInParentheses ) +{ + sal_uInt8 nOpTokenId = mxData->mbOk ? lclGetUnaryPreTokenId( aTokData.GetOpCode() ) : EXC_TOKID_NONE; + if( nOpTokenId != EXC_TOKID_NONE ) + { + sal_uInt8 nSpaces = aTokData.mnSpaces; + aTokData = UnaryPreTerm( GetNextToken(), bInParentheses ); + AppendUnaryOperatorToken( nOpTokenId, nSpaces ); + } + else + { + aTokData = ListTerm( aTokData, bInParentheses ); + } + return aTokData; +} + +XclExpScToken XclExpFmlaCompImpl::ListTerm( XclExpScToken aTokData, bool bInParentheses ) +{ + sal_uInt16 nSubExprPos = GetSize(); + bool bHasAnyRefOp = false; + bool bHasListOp = false; + aTokData = IntersectTerm( aTokData, bHasAnyRefOp ); + sal_uInt8 nOpTokenId = EXC_TOKID_NONE; + while( mxData->mbOk && ((nOpTokenId = lclGetListTokenId( aTokData.GetOpCode(), mxData->mbStopAtSep )) != EXC_TOKID_NONE) ) + { + sal_uInt8 nSpaces = aTokData.mnSpaces; + aTokData = IntersectTerm( GetNextToken(), bHasAnyRefOp ); + AppendBinaryOperatorToken( nOpTokenId, false, nSpaces ); + bHasAnyRefOp = bHasListOp = true; + } + if( bHasAnyRefOp ) + { + // add a tMemFunc token enclosing the entire reference subexpression + sal_uInt16 nSubExprSize = GetSize() - nSubExprPos; + InsertZeros( nSubExprPos, 3 ); + mxData->maTokVec[ nSubExprPos ] = GetTokenId( EXC_TOKID_MEMFUNC, EXC_TOKCLASS_REF ); + Overwrite( nSubExprPos + 1, nSubExprSize ); + // update the operand/operator stack (set the list expression as operand of the tMemFunc) + XclExpOperandListRef xOperands( new XclExpOperandList ); + xOperands->AppendOperand( PopOperandPos(), EXC_PARAMCONV_VAL, false ); + PushOperatorPos( nSubExprPos, xOperands ); + } + // #i86439# enclose list operator into parentheses, e.g. Calc's =AREAS(A1~A2) to Excel's =AREAS((A1;A2)) + if( bHasListOp && !bInParentheses ) + AppendParenToken(); + return aTokData; +} + +XclExpScToken XclExpFmlaCompImpl::IntersectTerm( XclExpScToken aTokData, bool& rbHasRefOp ) +{ + aTokData = RangeTerm( aTokData, rbHasRefOp ); + sal_uInt8 nOpTokenId = EXC_TOKID_NONE; + while( mxData->mbOk && ((nOpTokenId = lclGetIntersectTokenId( aTokData.GetOpCode() )) != EXC_TOKID_NONE) ) + { + sal_uInt8 nSpaces = aTokData.mnSpaces; + aTokData = RangeTerm( GetNextToken(), rbHasRefOp ); + AppendBinaryOperatorToken( nOpTokenId, false, nSpaces ); + rbHasRefOp = true; + } + return aTokData; +} + +XclExpScToken XclExpFmlaCompImpl::RangeTerm( XclExpScToken aTokData, bool& rbHasRefOp ) +{ + aTokData = Factor( aTokData ); + sal_uInt8 nOpTokenId = EXC_TOKID_NONE; + while( mxData->mbOk && ((nOpTokenId = lclGetRangeTokenId( aTokData.GetOpCode() )) != EXC_TOKID_NONE) ) + { + sal_uInt8 nSpaces = aTokData.mnSpaces; + aTokData = Factor( GetNextToken() ); + AppendBinaryOperatorToken( nOpTokenId, false, nSpaces ); + rbHasRefOp = true; + } + return aTokData; +} + +XclExpScToken XclExpFmlaCompImpl::Factor( XclExpScToken aTokData ) +{ + if( !mxData->mbOk || !aTokData.Is() ) return XclExpScToken(); + + switch( aTokData.GetType() ) + { + case svUnknown: mxData->mbOk = false; break; + case svDouble: ProcessDouble( aTokData ); break; + case svString: ProcessString( aTokData ); break; +#if 0 // erAck + case svError: ProcessError( aTokData ); break; +#endif + case svSingleRef: ProcessCellRef( aTokData ); break; + case svDoubleRef: ProcessRangeRef( aTokData ); break; + case svExternalSingleRef: ProcessExternalCellRef( aTokData ); break; + case svExternalDoubleRef: ProcessExternalRangeRef( aTokData ); break; + case svExternalName: ProcessExternalName( aTokData ); break; + case svMatrix: ProcessMatrix( aTokData ); break; + case svExternal: ProcessExternal( aTokData ); break; + + default: switch( aTokData.GetOpCode() ) + { + case ocNone: /* do nothing */ break; + case ocMissing: ProcessMissing( aTokData ); break; + case ocBad: ProcessBad( aTokData ); break; + case ocOpen: ProcessParentheses( aTokData ); break; + case ocName: ProcessDefinedName( aTokData ); break; + case ocDBArea: ProcessDatabaseArea( aTokData ); break; + case ocFalse: + case ocTrue: ProcessBoolean( aTokData ); break; + case ocDde: ProcessDdeLink( aTokData ); break; + default: ProcessFunction( aTokData ); + } + } + + return GetNextToken(); +} + +// formula structure ---------------------------------------------------------- + +void XclExpFmlaCompImpl::ProcessDouble( const XclExpScToken& rTokData ) +{ + double fValue = rTokData.mpScToken->GetDouble(); + double fInt; + double fFrac = modf( fValue, &fInt ); + if( (fFrac == 0.0) && (0.0 <= fInt) && (fInt <= 65535.0) ) + AppendIntToken( static_cast< sal_uInt16 >( fInt ), rTokData.mnSpaces ); + else + AppendNumToken( fValue, rTokData.mnSpaces ); +} + +void XclExpFmlaCompImpl::ProcessString( const XclExpScToken& rTokData ) +{ + AppendOperandTokenId( EXC_TOKID_STR, rTokData.mnSpaces ); + Append( rTokData.mpScToken->GetString() ); +} + +void XclExpFmlaCompImpl::ProcessError( const XclExpScToken& rTokData ) +{ +#if 0 // erAck + AppendErrorToken( XclTools::GetXclErrorCode( rTokData.mpScToken->GetError() ), rTokData.mnSpaces ); +#else + (void)rTokData; // compiler warning +#endif +} + +void XclExpFmlaCompImpl::ProcessMissing( const XclExpScToken& rTokData ) +{ + AppendMissingToken( rTokData.mnSpaces ); +} + +void XclExpFmlaCompImpl::ProcessBad( const XclExpScToken& rTokData ) +{ + AppendErrorToken( EXC_ERR_NA, rTokData.mnSpaces ); +} + +void XclExpFmlaCompImpl::ProcessParentheses( const XclExpScToken& rTokData ) +{ + XclExpScToken aTokData = Expression( GetNextToken(), true, false ); + mxData->mbOk = aTokData.GetOpCode() == ocClose; + AppendParenToken( rTokData.mnSpaces, aTokData.mnSpaces ); +} + +void XclExpFmlaCompImpl::ProcessBoolean( const XclExpScToken& rTokData ) +{ + mxData->mbOk = GetNextToken().GetOpCode() == ocOpen; + if( mxData->mbOk ) mxData->mbOk = GetNextToken().GetOpCode() == ocClose; + if( mxData->mbOk ) + AppendBoolToken( rTokData.GetOpCode() == ocTrue, rTokData.mnSpaces ); +} + +namespace { + +inline bool lclGetTokenString( String& rString, const XclExpScToken& rTokData ) +{ + bool bIsStr = (rTokData.GetType() == svString) && (rTokData.GetOpCode() == ocPush); + if( bIsStr ) + rString = rTokData.mpScToken->GetString(); + return bIsStr; +} + +} // namespace + +void XclExpFmlaCompImpl::ProcessDdeLink( const XclExpScToken& rTokData ) +{ + String aApplic, aTopic, aItem; + + mxData->mbOk = GetNextToken().GetOpCode() == ocOpen; + if( mxData->mbOk ) mxData->mbOk = lclGetTokenString( aApplic, GetNextToken() ); + if( mxData->mbOk ) mxData->mbOk = GetNextToken().GetOpCode() == ocSep; + if( mxData->mbOk ) mxData->mbOk = lclGetTokenString( aTopic, GetNextToken() ); + if( mxData->mbOk ) mxData->mbOk = GetNextToken().GetOpCode() == ocSep; + if( mxData->mbOk ) mxData->mbOk = lclGetTokenString( aItem, GetNextToken() ); + if( mxData->mbOk ) mxData->mbOk = GetNextToken().GetOpCode() == ocClose; + if( mxData->mbOk ) mxData->mbOk = aApplic.Len() && aTopic.Len() && aItem.Len(); + if( mxData->mbOk ) + { + sal_uInt16 nExtSheet, nExtName; + if( mxData->mpLinkMgr && mxData->mpLinkMgr->InsertDde( nExtSheet, nExtName, aApplic, aTopic, aItem ) ) + AppendNameXToken( nExtSheet, nExtName, rTokData.mnSpaces ); + else + AppendErrorToken( EXC_ERR_NA, rTokData.mnSpaces ); + } +} + +void XclExpFmlaCompImpl::ProcessExternal( const XclExpScToken& rTokData ) +{ + /* #i47228# Excel import generates svExternal/ocMacro tokens for invalid + names and for external/invalid function calls. This function looks for + the next token in the token array. If it is an opening parenthesis, the + token is processed as external function call, otherwise as undefined name. */ + const FormulaToken* pNextScToken = PeekNextRawToken( true ); + if( !pNextScToken || (pNextScToken->GetOpCode() != ocOpen) ) + AppendMissingNameToken( rTokData.mpScToken->GetExternal(), rTokData.mnSpaces ); + else + ProcessFunction( rTokData ); +} + +void XclExpFmlaCompImpl::ProcessMatrix( const XclExpScToken& rTokData ) +{ + const ScMatrix* pMatrix = static_cast< const ScToken* >( rTokData.mpScToken )->GetMatrix(); + if( pMatrix && mxData->mrCfg.mbAllowArrays ) + { + SCSIZE nScCols, nScRows; + pMatrix->GetDimensions( nScCols, nScRows ); + DBG_ASSERT( (nScCols > 0) && (nScRows > 0), "XclExpFmlaCompImpl::ProcessMatrix - invalid matrix size" ); + sal_uInt16 nCols = ::limit_cast< sal_uInt16 >( nScCols, 0, 256 ); + sal_uInt16 nRows = ::limit_cast< sal_uInt16 >( nScRows, 0, 1024 ); + + // create the tArray token + AppendOperandTokenId( GetTokenId( EXC_TOKID_ARRAY, EXC_TOKCLASS_ARR ), rTokData.mnSpaces ); + Append( static_cast< sal_uInt8 >( (meBiff == EXC_BIFF8) ? (nCols - 1) : nCols ) ); + Append( static_cast< sal_uInt16 >( (meBiff == EXC_BIFF8) ? (nRows - 1) : nRows ) ); + Append( static_cast< sal_uInt32 >( 0 ) ); + + // create the extended data containing the array values + AppendExt( static_cast< sal_uInt8 >( (meBiff == EXC_BIFF8) ? (nCols - 1) : nCols ) ); + AppendExt( static_cast< sal_uInt16 >( (meBiff == EXC_BIFF8) ? (nRows - 1) : nRows ) ); + for( SCSIZE nScRow = 0; nScRow < nScRows; ++nScRow ) + { + for( SCSIZE nScCol = 0; nScCol < nScCols; ++nScCol ) + { + ScMatValType nType; + const ScMatrixValue* pMatVal = pMatrix->Get( nScCol, nScRow, nType ); + DBG_ASSERT( pMatVal, "XclExpFmlaCompImpl::ProcessMatrix - missing matrix value" ); + if( ScMatrix::IsValueType( nType ) ) // value, boolean, or error + { + if( ScMatrix::IsBooleanType( nType ) ) + { + AppendExt( EXC_CACHEDVAL_BOOL ); + AppendExt( static_cast< sal_uInt8 >( pMatVal->GetBoolean() ? 1 : 0 ) ); + AppendExt( 0, 7 ); + } + else if( USHORT nErr = pMatVal->GetError() ) + { + AppendExt( EXC_CACHEDVAL_ERROR ); + AppendExt( XclTools::GetXclErrorCode( nErr ) ); + AppendExt( 0, 7 ); + } + else + { + AppendExt( EXC_CACHEDVAL_DOUBLE ); + AppendExt( pMatVal->fVal ); + } + } + else // string or empty + { + const String& rStr = pMatVal->GetString(); + if( rStr.Len() == 0 ) + { + AppendExt( EXC_CACHEDVAL_EMPTY ); + AppendExt( 0, 8 ); + } + else + { + AppendExt( EXC_CACHEDVAL_STRING ); + AppendExt( rStr ); + } + } + } + } + } + else + { + // array in places that do not allow it (cond fmts, data validation) + AppendErrorToken( EXC_ERR_NA, rTokData.mnSpaces ); + } +} + +void XclExpFmlaCompImpl::ProcessFunction( const XclExpScToken& rTokData ) +{ + OpCode eOpCode = rTokData.GetOpCode(); + const XclFunctionInfo* pFuncInfo = maFuncProv.GetFuncInfoFromOpCode( eOpCode ); + + XclExpExtFuncData aExtFuncData; + + // no exportable function found - try to create an external macro call + if( !pFuncInfo && (eOpCode >= SC_OPCODE_START_NO_PAR) ) + { + const String& rFuncName = ScCompiler::GetNativeSymbol( eOpCode ); + if( rFuncName.Len() ) + { + aExtFuncData.Set( rFuncName, true, false ); + pFuncInfo = maFuncProv.GetFuncInfoFromOpCode( ocMacro ); + } + } + + mxData->mbOk = pFuncInfo != 0; + if( !mxData->mbOk ) return; + + // functions simulated by a macro call in file format + if( pFuncInfo->IsMacroFunc() ) + aExtFuncData.Set( pFuncInfo->GetMacroFuncName(), false, true ); + + XclExpFuncData aFuncData( rTokData, *pFuncInfo, aExtFuncData ); + XclExpScToken aTokData; + + // preparations for special functions, before function processing starts + PrepareFunction( aFuncData ); + + enum { STATE_START, STATE_OPEN, STATE_PARAM, STATE_SEP, STATE_CLOSE, STATE_END } + eState = STATE_START; + while( eState != STATE_END ) switch( eState ) + { + case STATE_START: + mxData->mbOk = GetNextToken( aTokData ) && (aTokData.GetOpCode() == ocOpen); + eState = mxData->mbOk ? STATE_OPEN : STATE_END; + break; + case STATE_OPEN: + mxData->mbOk = GetNextToken( aTokData ); + eState = mxData->mbOk ? ((aTokData.GetOpCode() == ocClose) ? STATE_CLOSE : STATE_PARAM) : STATE_END; + break; + case STATE_PARAM: + aTokData = ProcessParam( aTokData, aFuncData ); + switch( aTokData.GetOpCode() ) + { + case ocSep: eState = STATE_SEP; break; + case ocClose: eState = STATE_CLOSE; break; + default: mxData->mbOk = false; + } + if( !mxData->mbOk ) eState = STATE_END; + break; + case STATE_SEP: + mxData->mbOk = (aFuncData.GetParamCount() < EXC_FUNC_MAXPARAM) && GetNextToken( aTokData ); + eState = mxData->mbOk ? STATE_PARAM : STATE_END; + break; + case STATE_CLOSE: + FinishFunction( aFuncData, aTokData.mnSpaces ); + eState = STATE_END; + break; + default:; + } +} + +void XclExpFmlaCompImpl::PrepareFunction( XclExpFuncData& rFuncData ) +{ + switch( rFuncData.GetOpCode() ) + { + case ocCot: // simulate COT(x) by (1/TAN(x)) + case ocCotHyp: // simulate COTH(x) by (1/TANH(x)) + AppendIntToken( 1 ); + break; + case ocArcCot: // simulate ACOT(x) by (PI/2-ATAN(x)) + AppendNumToken( F_PI2 ); + break; + default:; + } +} + +void XclExpFmlaCompImpl::FinishFunction( XclExpFuncData& rFuncData, sal_uInt8 nCloseSpaces ) +{ + // append missing parameters required in Excel, may modify param count + AppendTrailingParam( rFuncData ); + + // check if parameter count fits into the limits of the function + sal_uInt8 nParamCount = rFuncData.GetParamCount(); + if( (rFuncData.GetMinParamCount() <= nParamCount) && (nParamCount <= rFuncData.GetMaxParamCount()) ) + { + // first put the tAttrSpace tokens, they must not be included in tAttrGoto handling + AppendSpaceToken( EXC_TOK_ATTR_SPACE_SP_CLOSE, nCloseSpaces ); + AppendSpaceToken( EXC_TOK_ATTR_SPACE_SP, rFuncData.GetSpaces() ); + + // add tAttrGoto tokens for IF or CHOOSE functions + switch( rFuncData.GetOpCode() ) + { + case ocIf: + case ocChose: + AppendJumpToken( rFuncData, EXC_TOK_ATTR_GOTO ); + break; + default:; + } + + // put the tFunc or tFuncVar token (or another special token, e.g. tAttrSum) + AppendFuncToken( rFuncData ); + + // update volatile flag - is set if at least one used function is volatile + mxData->mbVolatile |= rFuncData.IsVolatile(); + + // update jump tokens for specific functions, add additional tokens + switch( rFuncData.GetOpCode() ) + { + case ocIf: + FinishIfFunction( rFuncData ); + break; + case ocChose: + FinishChooseFunction( rFuncData ); + break; + + case ocCot: // simulate COT(x) by (1/TAN(x)) + case ocCotHyp: // simulate COTH(x) by (1/TANH(x)) + AppendBinaryOperatorToken( EXC_TOKID_DIV, true ); + AppendParenToken(); + break; + case ocArcCot: // simulate ACOT(x) by (PI/2-ATAN(x)) + AppendBinaryOperatorToken( EXC_TOKID_SUB, true ); + AppendParenToken(); + break; + + default:; + } + } + else + mxData->mbOk = false; +} + +void XclExpFmlaCompImpl::FinishIfFunction( XclExpFuncData& rFuncData ) +{ + sal_uInt16 nParamCount = rFuncData.GetParamCount(); + DBG_ASSERT( (nParamCount == 2) || (nParamCount == 3), "XclExpFmlaCompImpl::FinishIfFunction - wrong parameter count" ); + const ScfUInt16Vec& rAttrPos = rFuncData.GetAttrPosVec(); + DBG_ASSERT( nParamCount == rAttrPos.size(), "XclExpFmlaCompImpl::FinishIfFunction - wrong number of tAttr tokens" ); + // update tAttrIf token following the condition parameter + Overwrite( rAttrPos[ 0 ] + 2, static_cast< sal_uInt16 >( rAttrPos[ 1 ] - rAttrPos[ 0 ] ) ); + // update the tAttrGoto tokens following true and false parameters + UpdateAttrGoto( rAttrPos[ 1 ] ); + if( nParamCount == 3 ) + UpdateAttrGoto( rAttrPos[ 2 ] ); +} + +void XclExpFmlaCompImpl::FinishChooseFunction( XclExpFuncData& rFuncData ) +{ + sal_uInt16 nParamCount = rFuncData.GetParamCount(); + ScfUInt16Vec& rAttrPos = rFuncData.GetAttrPosVec(); + DBG_ASSERT( nParamCount == rAttrPos.size(), "XclExpFmlaCompImpl::FinishChooseFunction - wrong number of tAttr tokens" ); + // number of choices is parameter count minus 1 + sal_uInt16 nChoices = nParamCount - 1; + // tAttrChoose token contains number of choices + Overwrite( rAttrPos[ 0 ] + 2, nChoices ); + // cache position of the jump table (follows number of choices in tAttrChoose token) + sal_uInt16 nJumpArrPos = rAttrPos[ 0 ] + 4; + // size of jump table: number of choices, plus 1 for error position + sal_uInt16 nJumpArrSize = 2 * (nChoices + 1); + // insert the jump table into the tAttrChoose token + InsertZeros( nJumpArrPos, nJumpArrSize ); + // update positions of tAttrGoto tokens after jump table insertion + sal_uInt16 nIdx; + for( nIdx = 1; nIdx < nParamCount; ++nIdx ) + rAttrPos[ nIdx ] = rAttrPos[ nIdx ] + nJumpArrSize; + // update the tAttrGoto tokens (they contain a value one-less to real distance) + for( nIdx = 1; nIdx < nParamCount; ++nIdx ) + UpdateAttrGoto( rAttrPos[ nIdx ] ); + // update the distances in the jump table + Overwrite( nJumpArrPos, nJumpArrSize ); + for( nIdx = 1; nIdx < nParamCount; ++nIdx ) + Overwrite( nJumpArrPos + 2 * nIdx, static_cast< sal_uInt16 >( rAttrPos[ nIdx ] + 4 - nJumpArrPos ) ); +} + +XclExpScToken XclExpFmlaCompImpl::ProcessParam( XclExpScToken aTokData, XclExpFuncData& rFuncData ) +{ + if( rFuncData.IsCalcOnlyParam() ) + { + // skip Calc-only parameter, stop at next ocClose or ocSep + aTokData = SkipExpression( aTokData, true ); + rFuncData.IncParamInfoIdx(); + } + else + { + // insert Excel-only parameters, modifies param count and class in rFuncData + while( rFuncData.IsExcelOnlyParam() ) + AppendDefaultParam( rFuncData ); + + // process the parameter, stop at next ocClose or ocSep + PrepareParam( rFuncData ); + /* #i37355# insert tMissArg token for missing parameters -- + Excel import filter adds ocMissing token (handled in Factor()), + but Calc itself does not do this if a new formula is entered. */ + switch( aTokData.GetOpCode() ) + { + case ocSep: + case ocClose: AppendMissingToken(); break; // empty parameter + default: aTokData = Expression( aTokData, false, true ); + } + // finalize the parameter and add special tokens, e.g. for IF or CHOOSE parameters + if( mxData->mbOk ) FinishParam( rFuncData ); + } + return aTokData; +} + +void XclExpFmlaCompImpl::PrepareParam( XclExpFuncData& rFuncData ) +{ + // index of this parameter is equal to number of already finished parameters + sal_uInt8 nParamIdx = rFuncData.GetParamCount(); + + switch( rFuncData.GetOpCode() ) + { + case ocIf: + switch( nParamIdx ) + { + // add a tAttrIf token before true-parameter (second parameter) + case 1: AppendJumpToken( rFuncData, EXC_TOK_ATTR_IF ); break; + // add a tAttrGoto token before false-parameter (third parameter) + case 2: AppendJumpToken( rFuncData, EXC_TOK_ATTR_GOTO ); break; + } + break; + + case ocChose: + switch( nParamIdx ) + { + // do nothing for first parameter + case 0: break; + // add a tAttrChoose token before first value parameter (second parameter) + case 1: AppendJumpToken( rFuncData, EXC_TOK_ATTR_CHOOSE ); break; + // add a tAttrGoto token before other value parameters + default: AppendJumpToken( rFuncData, EXC_TOK_ATTR_GOTO ); + } + break; + + case ocArcCotHyp: // simulate ACOTH(x) by ATANH(1/(x)) + if( nParamIdx == 0 ) + AppendIntToken( 1 ); + break; + default:; + } +} + +void XclExpFmlaCompImpl::FinishParam( XclExpFuncData& rFuncData ) +{ + // increase parameter count, update operand stack + rFuncData.FinishParam( PopOperandPos() ); + + // append more tokens for parameters of some special functions + sal_uInt8 nParamIdx = rFuncData.GetParamCount() - 1; + switch( rFuncData.GetOpCode() ) + { + case ocArcCotHyp: // simulate ACOTH(x) by ATANH(1/(x)) + if( nParamIdx == 0 ) + { + AppendParenToken(); + AppendBinaryOperatorToken( EXC_TOKID_DIV, true ); + } + break; + default:; + } +} + +void XclExpFmlaCompImpl::AppendDefaultParam( XclExpFuncData& rFuncData ) +{ + // prepare parameters of some special functions + PrepareParam( rFuncData ); + + switch( rFuncData.GetOpCode() ) + { + case ocExternal: + AppendAddInCallToken( rFuncData.GetExtFuncData() ); + break; + case ocEuroConvert: + AppendEuroToolCallToken( rFuncData.GetExtFuncData() ); + break; + case ocMacro: + AppendMacroCallToken( rFuncData.GetExtFuncData() ); + break; + default: + { + DBG_ASSERT( rFuncData.IsMacroFunc(), "XclExpFmlaCompImpl::AppendDefaultParam - unknown opcode" ); + if( rFuncData.IsMacroFunc() ) + AppendMacroCallToken( rFuncData.GetExtFuncData() ); + else + AppendMissingToken(); // to keep parameter count valid + } + } + + // update parameter count, add special parameter tokens + FinishParam( rFuncData ); +} + +void XclExpFmlaCompImpl::AppendTrailingParam( XclExpFuncData& rFuncData ) +{ + sal_uInt8 nParamCount = rFuncData.GetParamCount(); + switch( rFuncData.GetOpCode() ) + { + case ocIf: + if( nParamCount == 1 ) + { + // #112262# Excel needs at least two parameters in IF function + PrepareParam( rFuncData ); + AppendBoolToken( true ); + FinishParam( rFuncData ); + } + break; + + case ocRound: + case ocRoundUp: + case ocRoundDown: + if( nParamCount == 1 ) + { + // ROUND, ROUNDUP, ROUNDDOWN functions are fixed to 2 parameters in Excel + PrepareParam( rFuncData ); + AppendIntToken( 0 ); + FinishParam( rFuncData ); + } + break; + + case ocIndex: + if( nParamCount == 1 ) + { + // INDEX function needs at least 2 parameters in Excel + PrepareParam( rFuncData ); + AppendMissingToken(); + FinishParam( rFuncData ); + } + break; + + case ocExternal: + case ocMacro: + // external or macro call without parameters needs the external name reference + if( nParamCount == 0 ) + AppendDefaultParam( rFuncData ); + break; + + case ocGammaDist: + if( nParamCount == 3 ) + { + // GAMMADIST function needs 4 parameters in Excel + PrepareParam( rFuncData ); + AppendIntToken( 1 ); + FinishParam( rFuncData ); + } + break; + + case ocPoissonDist: + if( nParamCount == 2 ) + { + // POISSON function needs 3 parameters in Excel + PrepareParam( rFuncData ); + AppendIntToken( 1 ); + FinishParam( rFuncData ); + } + break; + + case ocNormDist: + if( nParamCount == 3 ) + { + // NORMDIST function needs 4 parameters in Excel + PrepareParam( rFuncData ); + AppendBoolToken( true ); + FinishParam( rFuncData ); + } + break; + + case ocLogNormDist: + switch( nParamCount ) + { + // LOGNORMDIST function needs 3 parameters in Excel + case 1: + PrepareParam( rFuncData ); + AppendIntToken( 0 ); + FinishParam( rFuncData ); + // do not break, add next default parameter + case 2: + PrepareParam( rFuncData ); + AppendIntToken( 1 ); + FinishParam( rFuncData ); + break; + default:; + } + + break; + + default:; + } +} + +// reference handling --------------------------------------------------------- + +namespace { + +inline bool lclIsRefRel2D( const ScSingleRefData& rRefData ) +{ + return rRefData.IsColRel() || rRefData.IsRowRel(); +} + +inline bool lclIsRefDel2D( const ScSingleRefData& rRefData ) +{ + return rRefData.IsColDeleted() || rRefData.IsRowDeleted(); +} + +inline bool lclIsRefRel2D( const ScComplexRefData& rRefData ) +{ + return lclIsRefRel2D( rRefData.Ref1 ) || lclIsRefRel2D( rRefData.Ref2 ); +} + +inline bool lclIsRefDel2D( const ScComplexRefData& rRefData ) +{ + return lclIsRefDel2D( rRefData.Ref1 ) || lclIsRefDel2D( rRefData.Ref2 ); +} + +} // namespace + +SCTAB XclExpFmlaCompImpl::GetScTab( const ScSingleRefData& rRefData ) const +{ + bool bInvTab = rRefData.IsTabDeleted() || (!mxData->mpScBasePos && IsInGlobals() && rRefData.IsTabRel()); + return bInvTab ? SCTAB_INVALID : static_cast< SCTAB >( rRefData.nTab ); +} + +bool XclExpFmlaCompImpl::IsRef2D( const ScSingleRefData& rRefData ) const +{ + /* rRefData.IsFlag3D() determines if sheet name is always visible, even on + the own sheet. If 3D references are allowed, the passed reference does + not count as 2D reference. */ + return (!mxData->mpLinkMgr || !rRefData.IsFlag3D()) && !rRefData.IsTabDeleted() && + (rRefData.IsTabRel() ? (rRefData.nRelTab == 0) : (static_cast< SCTAB >( rRefData.nTab ) == GetCurrScTab())); +} + +bool XclExpFmlaCompImpl::IsRef2D( const ScComplexRefData& rRefData ) const +{ + return IsRef2D( rRefData.Ref1 ) && IsRef2D( rRefData.Ref2 ); +} + +void XclExpFmlaCompImpl::ConvertRefData( + ScSingleRefData& rRefData, XclAddress& rXclPos, + bool bNatLangRef, bool bTruncMaxCol, bool bTruncMaxRow ) const +{ + if( mxData->mpScBasePos ) + { + // *** reference position exists (cell, matrix) - convert to absolute *** + rRefData.CalcAbsIfRel( *mxData->mpScBasePos ); + + // convert column index + SCsCOL& rnScCol = rRefData.nCol; + if( bTruncMaxCol && (rnScCol == mnMaxScCol) ) + rnScCol = mnMaxAbsCol; + else if( (rnScCol < 0) || (rnScCol > mnMaxAbsCol) ) + rRefData.SetColDeleted( TRUE ); + rXclPos.mnCol = static_cast< sal_uInt16 >( rnScCol ) & mnMaxColMask; + + // convert row index + SCsROW& rnScRow = rRefData.nRow; + if( bTruncMaxRow && (rnScRow == mnMaxScRow) ) + rnScRow = mnMaxAbsRow; + else if( (rnScRow < 0) || (rnScRow > mnMaxAbsRow) ) + rRefData.SetRowDeleted( TRUE ); + rXclPos.mnRow = static_cast< sal_uInt16 >( rnScRow ) & mnMaxRowMask; + } + else + { + // *** no reference position (shared, names, condfmt) - use relative values *** + + // convert column index (2-step-cast ScsCOL->sal_Int16->sal_uInt16 to get all bits correctly) + sal_Int16 nXclRelCol = static_cast< sal_Int16 >( rRefData.IsColRel() ? rRefData.nRelCol : rRefData.nCol ); + rXclPos.mnCol = static_cast< sal_uInt16 >( nXclRelCol ) & mnMaxColMask; + + // convert row index (2-step-cast ScsROW->sal_Int16->sal_uInt16 to get all bits correctly) + sal_Int16 nXclRelRow = static_cast< sal_Int16 >( rRefData.IsRowRel() ? rRefData.nRelRow : rRefData.nRow ); + rXclPos.mnRow = static_cast< sal_uInt16 >( nXclRelRow ) & mnMaxRowMask; + + // resolve relative tab index if possible + if( rRefData.IsTabRel() && !IsInGlobals() && (GetCurrScTab() < GetDoc().GetTableCount()) ) + rRefData.nTab = static_cast< SCsTAB >( GetCurrScTab() + rRefData.nRelTab ); + } + + // flags for relative column and row + if( bNatLangRef ) + { + DBG_ASSERT( meBiff == EXC_BIFF8, "XclExpFmlaCompImpl::ConvertRefData - NLRs only for BIFF8" ); + // Calc does not support absolute reference mode in natural language references + ::set_flag( rXclPos.mnCol, EXC_TOK_NLR_REL ); + } + else + { + sal_uInt16& rnRelField = (meBiff <= EXC_BIFF5) ? rXclPos.mnRow : rXclPos.mnCol; + ::set_flag( rnRelField, EXC_TOK_REF_COLREL, rRefData.IsColRel() ); + ::set_flag( rnRelField, EXC_TOK_REF_ROWREL, rRefData.IsRowRel() ); + } +} + +void XclExpFmlaCompImpl::ConvertRefData( + ScComplexRefData& rRefData, XclRange& rXclRange, bool bNatLangRef ) const +{ + // convert start and end of the range + ConvertRefData( rRefData.Ref1, rXclRange.maFirst, bNatLangRef, false, false ); + bool bTruncMaxCol = !rRefData.Ref1.IsColDeleted() && (rRefData.Ref1.nCol == 0); + bool bTruncMaxRow = !rRefData.Ref1.IsRowDeleted() && (rRefData.Ref1.nRow == 0); + ConvertRefData( rRefData.Ref2, rXclRange.maLast, bNatLangRef, bTruncMaxCol, bTruncMaxRow ); +} + +XclExpRefLogEntry* XclExpFmlaCompImpl::GetNewRefLogEntry() +{ + if( mxData->mpRefLog ) + { + mxData->mpRefLog->resize( mxData->mpRefLog->size() + 1 ); + return &mxData->mpRefLog->back(); + } + return 0; +} + +void XclExpFmlaCompImpl::ProcessCellRef( const XclExpScToken& rTokData ) +{ + // get the Excel address components, adjust internal data in aRefData + bool bNatLangRef = (meBiff == EXC_BIFF8) && mxData->mpScBasePos && (rTokData.GetOpCode() == ocColRowName); + ScSingleRefData aRefData = static_cast< const ScToken* >( rTokData.mpScToken )->GetSingleRef(); + XclAddress aXclPos( ScAddress::UNINITIALIZED ); + ConvertRefData( aRefData, aXclPos, bNatLangRef, false, false ); + + if( bNatLangRef ) + { + DBG_ASSERT( aRefData.IsColRel() != aRefData.IsRowRel(), + "XclExpFmlaCompImpl::ProcessCellRef - broken natural language reference" ); + // create tNlr token for natural language reference + sal_uInt8 nSubId = aRefData.IsColRel() ? EXC_TOK_NLR_COLV : EXC_TOK_NLR_ROWV; + AppendOperandTokenId( EXC_TOKID_NLR, rTokData.mnSpaces ); + Append( nSubId ); + AppendAddress( aXclPos ); + } + else + { + // store external cell contents in CRN records + if( mxData->mrCfg.mbFromCell && mxData->mpLinkMgr && mxData->mpScBasePos ) + mxData->mpLinkMgr->StoreCell( aRefData ); + + // create the tRef, tRefErr, tRefN, tRef3d, or tRefErr3d token + if( !mxData->mrCfg.mb3DRefOnly && IsRef2D( aRefData ) ) + { + // 2D reference (not in defined names, but allowed in range lists) + sal_uInt8 nBaseId = (!mxData->mpScBasePos && lclIsRefRel2D( aRefData )) ? EXC_TOKID_REFN : + (lclIsRefDel2D( aRefData ) ? EXC_TOKID_REFERR : EXC_TOKID_REF); + AppendOperandTokenId( GetTokenId( nBaseId, EXC_TOKCLASS_REF ), rTokData.mnSpaces ); + AppendAddress( aXclPos ); + } + else if( mxData->mpLinkMgr ) // 3D reference + { + // 1-based EXTERNSHEET index and 0-based Excel sheet index + sal_uInt16 nExtSheet, nXclTab; + mxData->mpLinkMgr->FindExtSheet( nExtSheet, nXclTab, GetScTab( aRefData ), GetNewRefLogEntry() ); + // write the token + sal_uInt8 nBaseId = lclIsRefDel2D( aRefData ) ? EXC_TOKID_REFERR3D : EXC_TOKID_REF3D; + AppendOperandTokenId( GetTokenId( nBaseId, EXC_TOKCLASS_REF ), rTokData.mnSpaces ); + Append( nExtSheet ); + if( meBiff <= EXC_BIFF5 ) + { + Append( 0, 8 ); + Append( nXclTab ); + Append( nXclTab ); + } + AppendAddress( aXclPos ); + } + else + { + // 3D ref in cond. format, or 2D ref in name + AppendErrorToken( EXC_ERR_REF, rTokData.mnSpaces ); + } + } +} + +void XclExpFmlaCompImpl::ProcessRangeRef( const XclExpScToken& rTokData ) +{ + // get the Excel address components, adjust internal data in aRefData + ScComplexRefData aRefData = static_cast< const ScToken* >( rTokData.mpScToken )->GetDoubleRef(); + XclRange aXclRange( ScAddress::UNINITIALIZED ); + ConvertRefData( aRefData, aXclRange, false ); + + // store external cell contents in CRN records + if( mxData->mrCfg.mbFromCell && mxData->mpLinkMgr && mxData->mpScBasePos ) + mxData->mpLinkMgr->StoreCellRange( aRefData ); + + // create the tArea, tAreaErr, tAreaN, tArea3d, or tAreaErr3d token + if( !mxData->mrCfg.mb3DRefOnly && IsRef2D( aRefData ) ) + { + // 2D reference (not in name formulas, but allowed in range lists) + sal_uInt8 nBaseId = (!mxData->mpScBasePos && lclIsRefRel2D( aRefData )) ? EXC_TOKID_AREAN : + (lclIsRefDel2D( aRefData ) ? EXC_TOKID_AREAERR : EXC_TOKID_AREA); + AppendOperandTokenId( GetTokenId( nBaseId, EXC_TOKCLASS_REF ), rTokData.mnSpaces ); + AppendRange( aXclRange ); + } + else if( mxData->mpLinkMgr ) // 3D reference + { + // 1-based EXTERNSHEET index and 0-based Excel sheet indexes + sal_uInt16 nExtSheet, nFirstXclTab, nLastXclTab; + mxData->mpLinkMgr->FindExtSheet( nExtSheet, nFirstXclTab, nLastXclTab, + GetScTab( aRefData.Ref1 ), GetScTab( aRefData.Ref2 ), GetNewRefLogEntry() ); + // write the token + sal_uInt8 nBaseId = lclIsRefDel2D( aRefData ) ? EXC_TOKID_AREAERR3D : EXC_TOKID_AREA3D; + AppendOperandTokenId( GetTokenId( nBaseId, EXC_TOKCLASS_REF ), rTokData.mnSpaces ); + Append( nExtSheet ); + if( meBiff <= EXC_BIFF5 ) + { + Append( 0, 8 ); + Append( nFirstXclTab ); + Append( nLastXclTab ); + } + AppendRange( aXclRange ); + } + else + { + // 3D ref in cond. format, or 2D ref in name + AppendErrorToken( EXC_ERR_REF, rTokData.mnSpaces ); + } +} + +void XclExpFmlaCompImpl::ProcessExternalCellRef( const XclExpScToken& rTokData ) +{ + if( mxData->mpLinkMgr ) + { + // get the Excel address components, adjust internal data in aRefData + ScSingleRefData aRefData = static_cast< const ScToken* >( rTokData.mpScToken )->GetSingleRef(); + XclAddress aXclPos( ScAddress::UNINITIALIZED ); + ConvertRefData( aRefData, aXclPos, false, false, false ); + + // store external cell contents in CRN records + USHORT nFileId = rTokData.mpScToken->GetIndex(); + const String& rTabName = rTokData.mpScToken->GetString(); + if( mxData->mrCfg.mbFromCell && mxData->mpScBasePos ) + mxData->mpLinkMgr->StoreCell( nFileId, rTabName, aRefData ); + + // 1-based EXTERNSHEET index and 0-based Excel sheet indexes + sal_uInt16 nExtSheet, nFirstSBTab, nLastSBTab; + mxData->mpLinkMgr->FindExtSheet( nFileId, rTabName, 1, nExtSheet, nFirstSBTab, nLastSBTab, GetNewRefLogEntry() ); + // write the token + sal_uInt8 nBaseId = lclIsRefDel2D( aRefData ) ? EXC_TOKID_REFERR3D : EXC_TOKID_REF3D; + AppendOperandTokenId( GetTokenId( nBaseId, EXC_TOKCLASS_REF ), rTokData.mnSpaces ); + Append( nExtSheet ); + if( meBiff <= EXC_BIFF5 ) + { + Append( 0, 8 ); + Append( nFirstSBTab ); + Append( nLastSBTab ); + } + AppendAddress( aXclPos ); + } + else + { + AppendErrorToken( EXC_ERR_REF, rTokData.mnSpaces ); + } +} + +void XclExpFmlaCompImpl::ProcessExternalRangeRef( const XclExpScToken& rTokData ) +{ + if( mxData->mpLinkMgr ) + { + // get the Excel address components, adjust internal data in aRefData + ScComplexRefData aRefData = static_cast< const ScToken* >( rTokData.mpScToken )->GetDoubleRef(); + XclRange aXclRange( ScAddress::UNINITIALIZED ); + ConvertRefData( aRefData, aXclRange, false ); + + // store external cell contents in CRN records + USHORT nFileId = rTokData.mpScToken->GetIndex(); + const String& rTabName = rTokData.mpScToken->GetString(); + if( mxData->mrCfg.mbFromCell && mxData->mpScBasePos ) + mxData->mpLinkMgr->StoreCellRange( nFileId, rTabName, aRefData ); + + // 1-based EXTERNSHEET index and 0-based Excel sheet indexes + sal_uInt16 nExtSheet, nFirstSBTab, nLastSBTab; + sal_uInt16 nTabSpan = static_cast< sal_uInt16 >( aRefData.Ref2.nTab - aRefData.Ref1.nTab + 1 ); + mxData->mpLinkMgr->FindExtSheet( nFileId, rTabName, nTabSpan, nExtSheet, nFirstSBTab, nLastSBTab, GetNewRefLogEntry() ); + // write the token + sal_uInt8 nBaseId = lclIsRefDel2D( aRefData ) ? EXC_TOKID_AREAERR3D : EXC_TOKID_AREA3D; + AppendOperandTokenId( GetTokenId( nBaseId, EXC_TOKCLASS_REF ), rTokData.mnSpaces ); + Append( nExtSheet ); + if( meBiff <= EXC_BIFF5 ) + { + Append( 0, 8 ); + Append( nFirstSBTab ); + Append( nLastSBTab ); + } + AppendRange( aXclRange ); + } + else + { + AppendErrorToken( EXC_ERR_REF, rTokData.mnSpaces ); + } +} + +void XclExpFmlaCompImpl::ProcessDefinedName( const XclExpScToken& rTokData ) +{ + XclExpNameManager& rNameMgr = GetNameManager(); + sal_uInt16 nNameIdx = rNameMgr.InsertName( rTokData.mpScToken->GetIndex() ); + if( nNameIdx != 0 ) + { + // global names always with tName token, local names dependent on config + SCTAB nScTab = rNameMgr.GetScTab( nNameIdx ); + if( (nScTab == SCTAB_GLOBAL) || (!mxData->mrCfg.mb3DRefOnly && (nScTab == GetCurrScTab())) ) + { + AppendNameToken( nNameIdx, rTokData.mnSpaces ); + } + else if( mxData->mpLinkMgr ) + { + // use the same special EXTERNNAME to refer to any local name + sal_uInt16 nExtSheet = mxData->mpLinkMgr->FindExtSheet( EXC_EXTSH_OWNDOC ); + AppendNameXToken( nExtSheet, nNameIdx, rTokData.mnSpaces ); + } + else + AppendErrorToken( EXC_ERR_NAME, rTokData.mnSpaces ); + // volatile names (containing volatile functions) + mxData->mbVolatile |= rNameMgr.IsVolatile( nNameIdx ); + } + else + AppendErrorToken( EXC_ERR_NAME, rTokData.mnSpaces ); +} + +void XclExpFmlaCompImpl::ProcessExternalName( const XclExpScToken& rTokData ) +{ + if( mxData->mpLinkMgr ) + { + ScExternalRefManager& rExtRefMgr = *GetDoc().GetExternalRefManager(); + USHORT nFileId = rTokData.mpScToken->GetIndex(); + const String& rName = rTokData.mpScToken->GetString(); + ScExternalRefCache::TokenArrayRef xArray = rExtRefMgr.getRangeNameTokens( nFileId, rName ); + if( xArray.get() ) + { + // store external cell contents in CRN records + if( mxData->mpScBasePos ) + { + for( FormulaToken* pScToken = xArray->First(); pScToken; pScToken = xArray->Next() ) + { + if( pScToken->GetOpCode() == ocExternalRef ) + { + switch( pScToken->GetType() ) + { + case svExternalSingleRef: + { + ScSingleRefData aRefData = static_cast< ScToken* >( pScToken )->GetSingleRef(); + aRefData.CalcAbsIfRel( *mxData->mpScBasePos ); + mxData->mpLinkMgr->StoreCell( nFileId, pScToken->GetString(), aRefData ); + } + break; + case svExternalDoubleRef: + { + ScComplexRefData aRefData = static_cast< ScToken* >( pScToken )->GetDoubleRef(); + aRefData.CalcAbsIfRel( *mxData->mpScBasePos ); + mxData->mpLinkMgr->StoreCellRange( nFileId, pScToken->GetString(), aRefData ); + } + default: + ; // nothing, avoid compiler warning + } + } + } + } + + // insert the new external name and create the tNameX token + sal_uInt16 nExtSheet, nExtName; + const String* pFile = rExtRefMgr.getExternalFileName( nFileId ); + if( pFile && mxData->mpLinkMgr->InsertExtName( nExtSheet, nExtName, *pFile, rName, xArray ) ) + { + AppendNameXToken( nExtSheet, nExtName, rTokData.mnSpaces ); + return; + } + } + } + + // on any error: create a #NAME? error + AppendErrorToken( EXC_ERR_NAME, rTokData.mnSpaces ); +} + +void XclExpFmlaCompImpl::ProcessDatabaseArea( const XclExpScToken& rTokData ) +{ + sal_uInt16 nNameIdx = GetNameManager().InsertDBRange( rTokData.mpScToken->GetIndex() ); + AppendNameToken( nNameIdx, rTokData.mnSpaces ); +} + +// token vector --------------------------------------------------------------- + +void XclExpFmlaCompImpl::PushOperandPos( sal_uInt16 nTokPos ) +{ + mxData->maOpPosStack.push_back( nTokPos ); +} + +void XclExpFmlaCompImpl::PushOperatorPos( sal_uInt16 nTokPos, const XclExpOperandListRef& rxOperands ) +{ + PushOperandPos( nTokPos ); + DBG_ASSERT( rxOperands.get(), "XclExpFmlaCompImpl::AppendOperatorTokenId - missing operand list" ); + if( mxData->maOpListVec.size() <= nTokPos ) + mxData->maOpListVec.resize( nTokPos + 1, XclExpOperandListRef() ); + mxData->maOpListVec[ nTokPos ] = rxOperands; +} + +sal_uInt16 XclExpFmlaCompImpl::PopOperandPos() +{ + DBG_ASSERT( !mxData->mbOk || !mxData->maOpPosStack.empty(), "XclExpFmlaCompImpl::PopOperandPos - token stack broken" ); + mxData->mbOk &= !mxData->maOpPosStack.empty(); + if( mxData->mbOk ) + { + sal_uInt16 nTokPos = mxData->maOpPosStack.back(); + mxData->maOpPosStack.pop_back(); + return nTokPos; + } + return 0; +} + +namespace { + +inline void lclAppend( ScfUInt8Vec& orVector, sal_uInt16 nData ) +{ + orVector.resize( orVector.size() + 2 ); + ShortToSVBT16( nData, &*(orVector.end() - 2) ); +} + +inline void lclAppend( ScfUInt8Vec& orVector, sal_uInt32 nData ) +{ + orVector.resize( orVector.size() + 4 ); + UInt32ToSVBT32( nData, &*(orVector.end() - 4) ); +} + +inline void lclAppend( ScfUInt8Vec& orVector, double fData ) +{ + orVector.resize( orVector.size() + 8 ); + DoubleToSVBT64( fData, &*(orVector.end() - 8) ); +} + +inline void lclAppend( ScfUInt8Vec& orVector, const XclExpRoot& rRoot, const String& rString, XclStrFlags nStrFlags ) +{ + XclExpStringRef xXclStr = XclExpStringHelper::CreateString( rRoot, rString, nStrFlags, EXC_TOK_STR_MAXLEN ); + size_t nSize = orVector.size(); + orVector.resize( nSize + xXclStr->GetSize() ); + xXclStr->WriteToMem( &orVector[ nSize ] ); +} + +} // namespace + +void XclExpFmlaCompImpl::Append( sal_uInt8 nData ) +{ + mxData->maTokVec.push_back( nData ); +} + +void XclExpFmlaCompImpl::Append( sal_uInt8 nData, size_t nCount ) +{ + mxData->maTokVec.resize( mxData->maTokVec.size() + nCount, nData ); +} + +void XclExpFmlaCompImpl::Append( sal_uInt16 nData ) +{ + lclAppend( mxData->maTokVec, nData ); +} + +void XclExpFmlaCompImpl::Append( sal_uInt32 nData ) +{ + lclAppend( mxData->maTokVec, nData ); +} + +void XclExpFmlaCompImpl::Append( double fData ) +{ + lclAppend( mxData->maTokVec, fData ); +} + +void XclExpFmlaCompImpl::Append( const String& rString ) +{ + lclAppend( mxData->maTokVec, GetRoot(), rString, EXC_STR_8BITLENGTH ); +} + +void XclExpFmlaCompImpl::AppendAddress( const XclAddress& rXclPos ) +{ + Append( rXclPos.mnRow ); + if( meBiff <= EXC_BIFF5 ) + Append( static_cast< sal_uInt8 >( rXclPos.mnCol ) ); + else + Append( rXclPos.mnCol ); +} + +void XclExpFmlaCompImpl::AppendRange( const XclRange& rXclRange ) +{ + Append( rXclRange.maFirst.mnRow ); + Append( rXclRange.maLast.mnRow ); + if( meBiff <= EXC_BIFF5 ) + { + Append( static_cast< sal_uInt8 >( rXclRange.maFirst.mnCol ) ); + Append( static_cast< sal_uInt8 >( rXclRange.maLast.mnCol ) ); + } + else + { + Append( rXclRange.maFirst.mnCol ); + Append( rXclRange.maLast.mnCol ); + } +} + +void XclExpFmlaCompImpl::AppendSpaceToken( sal_uInt8 nType, sal_uInt8 nCount ) +{ + if( nCount > 0 ) + { + Append( EXC_TOKID_ATTR ); + Append( EXC_TOK_ATTR_SPACE ); + Append( nType ); + Append( nCount ); + } +} + +void XclExpFmlaCompImpl::AppendOperandTokenId( sal_uInt8 nTokenId, sal_uInt8 nSpaces ) +{ + AppendSpaceToken( EXC_TOK_ATTR_SPACE_SP, nSpaces ); + PushOperandPos( GetSize() ); + Append( nTokenId ); +} + +void XclExpFmlaCompImpl::AppendIntToken( sal_uInt16 nValue, sal_uInt8 nSpaces ) +{ + AppendOperandTokenId( EXC_TOKID_INT, nSpaces ); + Append( nValue ); +} + +void XclExpFmlaCompImpl::AppendNumToken( double fValue, sal_uInt8 nSpaces ) +{ + AppendOperandTokenId( EXC_TOKID_NUM, nSpaces ); + Append( fValue ); +} + +void XclExpFmlaCompImpl::AppendBoolToken( bool bValue, sal_uInt8 nSpaces ) +{ + AppendOperandTokenId( EXC_TOKID_BOOL, nSpaces ); + Append( bValue ? EXC_TOK_BOOL_TRUE : EXC_TOK_BOOL_FALSE ); +} + +void XclExpFmlaCompImpl::AppendErrorToken( sal_uInt8 nErrCode, sal_uInt8 nSpaces ) +{ + AppendOperandTokenId( EXC_TOKID_ERR, nSpaces ); + Append( nErrCode ); +} + +void XclExpFmlaCompImpl::AppendMissingToken( sal_uInt8 nSpaces ) +{ + AppendOperandTokenId( EXC_TOKID_MISSARG, nSpaces ); +} + +void XclExpFmlaCompImpl::AppendNameToken( sal_uInt16 nNameIdx, sal_uInt8 nSpaces ) +{ + if( nNameIdx > 0 ) + { + AppendOperandTokenId( GetTokenId( EXC_TOKID_NAME, EXC_TOKCLASS_REF ), nSpaces ); + Append( nNameIdx ); + Append( 0, (meBiff <= EXC_BIFF5) ? 12 : 2 ); + } + else + AppendErrorToken( EXC_ERR_NAME ); +} + +void XclExpFmlaCompImpl::AppendMissingNameToken( const String& rName, sal_uInt8 nSpaces ) +{ + sal_uInt16 nNameIdx = GetNameManager().InsertRawName( rName ); + AppendNameToken( nNameIdx, nSpaces ); +} + +void XclExpFmlaCompImpl::AppendNameXToken( sal_uInt16 nExtSheet, sal_uInt16 nExtName, sal_uInt8 nSpaces ) +{ + AppendOperandTokenId( GetTokenId( EXC_TOKID_NAMEX, EXC_TOKCLASS_REF ), nSpaces ); + Append( nExtSheet ); + if( meBiff <= EXC_BIFF5 ) + Append( 0, 8 ); + Append( nExtName ); + Append( 0, (meBiff <= EXC_BIFF5) ? 12 : 2 ); +} + +void XclExpFmlaCompImpl::AppendMacroCallToken( const XclExpExtFuncData& rExtFuncData, sal_uInt8 nSpaces ) +{ + sal_uInt16 nNameIdx = GetNameManager().InsertMacroCall( rExtFuncData.maFuncName, rExtFuncData.mbVBasic, true, rExtFuncData.mbHidden ); + AppendNameToken( nNameIdx, nSpaces ); +} + +void XclExpFmlaCompImpl::AppendAddInCallToken( const XclExpExtFuncData& rExtFuncData, sal_uInt8 nSpaces ) +{ + String aXclFuncName; + if( mxData->mpLinkMgr && ScGlobal::GetAddInCollection()->GetExcelName( rExtFuncData.maFuncName, GetUILanguage(), aXclFuncName ) ) + { + sal_uInt16 nExtSheet, nExtName; + if( mxData->mpLinkMgr->InsertAddIn( nExtSheet, nExtName, aXclFuncName ) ) + { + AppendNameXToken( nExtSheet, nExtName, nSpaces ); + return; + } + } + AppendMacroCallToken( rExtFuncData, nSpaces ); +} + +void XclExpFmlaCompImpl::AppendEuroToolCallToken( const XclExpExtFuncData& rExtFuncData, sal_uInt8 nSpaces ) +{ + sal_uInt16 nExtSheet, nExtName; + if( mxData->mpLinkMgr && mxData->mpLinkMgr->InsertEuroTool( nExtSheet, nExtName, rExtFuncData.maFuncName ) ) + AppendNameXToken( nExtSheet, nExtName, nSpaces ); + else + AppendMacroCallToken( rExtFuncData, nSpaces ); +} + +void XclExpFmlaCompImpl::AppendOperatorTokenId( sal_uInt8 nTokenId, const XclExpOperandListRef& rxOperands, sal_uInt8 nSpaces ) +{ + AppendSpaceToken( EXC_TOK_ATTR_SPACE_SP, nSpaces ); + PushOperatorPos( GetSize(), rxOperands ); + Append( nTokenId ); +} + +void XclExpFmlaCompImpl::AppendUnaryOperatorToken( sal_uInt8 nTokenId, sal_uInt8 nSpaces ) +{ + XclExpOperandListRef xOperands( new XclExpOperandList ); + xOperands->AppendOperand( PopOperandPos(), EXC_PARAMCONV_RPO, true ); + AppendOperatorTokenId( nTokenId, xOperands, nSpaces ); +} + +void XclExpFmlaCompImpl::AppendBinaryOperatorToken( sal_uInt8 nTokenId, bool bValType, sal_uInt8 nSpaces ) +{ + XclExpOperandListRef xOperands( new XclExpOperandList ); + xOperands->AppendOperand( PopOperandPos(), EXC_PARAMCONV_RPO, bValType ); + xOperands->AppendOperand( PopOperandPos(), EXC_PARAMCONV_RPO, bValType ); + AppendOperatorTokenId( nTokenId, xOperands, nSpaces ); +} + +void XclExpFmlaCompImpl::AppendLogicalOperatorToken( sal_uInt16 nXclFuncIdx, sal_uInt8 nOpCount ) +{ + XclExpOperandListRef xOperands( new XclExpOperandList ); + for( sal_uInt8 nOpIdx = 0; nOpIdx < nOpCount; ++nOpIdx ) + xOperands->AppendOperand( PopOperandPos(), EXC_PARAMCONV_RPX, false ); + AppendOperatorTokenId( GetTokenId( EXC_TOKID_FUNCVAR, EXC_TOKCLASS_VAL ), xOperands ); + Append( nOpCount ); + Append( nXclFuncIdx ); +} + +void XclExpFmlaCompImpl::AppendFuncToken( const XclExpFuncData& rFuncData ) +{ + sal_uInt16 nXclFuncIdx = rFuncData.GetXclFuncIdx(); + sal_uInt8 nParamCount = rFuncData.GetParamCount(); + sal_uInt8 nRetClass = rFuncData.GetReturnClass(); + + if( (nXclFuncIdx == EXC_FUNCID_SUM) && (nParamCount == 1) ) + { + // SUM with only one parameter + AppendOperatorTokenId( EXC_TOKID_ATTR, rFuncData.GetOperandList() ); + Append( EXC_TOK_ATTR_SUM ); + Append( sal_uInt16( 0 ) ); + } + else if( rFuncData.IsFixedParamCount() ) + { + // fixed number of parameters + AppendOperatorTokenId( GetTokenId( EXC_TOKID_FUNC, nRetClass ), rFuncData.GetOperandList() ); + Append( nXclFuncIdx ); + } + else + { + // variable number of parameters + AppendOperatorTokenId( GetTokenId( EXC_TOKID_FUNCVAR, nRetClass ), rFuncData.GetOperandList() ); + Append( nParamCount ); + Append( nXclFuncIdx ); + } +} + +void XclExpFmlaCompImpl::AppendParenToken( sal_uInt8 nOpenSpaces, sal_uInt8 nCloseSpaces ) +{ + AppendSpaceToken( EXC_TOK_ATTR_SPACE_SP_OPEN, nOpenSpaces ); + AppendSpaceToken( EXC_TOK_ATTR_SPACE_SP_CLOSE, nCloseSpaces ); + Append( EXC_TOKID_PAREN ); +} + +void XclExpFmlaCompImpl::AppendJumpToken( XclExpFuncData& rFuncData, sal_uInt8 nAttrType ) +{ + // store the start position of the token + rFuncData.AppendAttrPos( GetSize() ); + // create the tAttr token + Append( EXC_TOKID_ATTR ); + Append( nAttrType ); + Append( sal_uInt16( 0 ) ); // placeholder that will be updated later +} + +void XclExpFmlaCompImpl::InsertZeros( sal_uInt16 nInsertPos, sal_uInt16 nInsertSize ) +{ + // insert zeros into the token array + DBG_ASSERT( nInsertPos < mxData->maTokVec.size(), "XclExpFmlaCompImpl::Insert - invalid position" ); + mxData->maTokVec.insert( mxData->maTokVec.begin() + nInsertPos, nInsertSize, 0 ); + + // update positions of operands waiting for an operator + for( ScfUInt16Vec::iterator aIt = mxData->maOpPosStack.begin(), aEnd = mxData->maOpPosStack.end(); aIt != aEnd; ++aIt ) + if( nInsertPos <= *aIt ) + *aIt = *aIt + nInsertSize; + + // update operand lists of all operator tokens + if( nInsertPos < mxData->maOpListVec.size() ) + mxData->maOpListVec.insert( mxData->maOpListVec.begin() + nInsertPos, nInsertSize, XclExpOperandListRef() ); + for( XclExpOperandListVector::iterator aIt = mxData->maOpListVec.begin(), aEnd = mxData->maOpListVec.end(); aIt != aEnd; ++aIt ) + if( aIt->get() ) + for( XclExpOperandList::iterator aIt2 = (*aIt)->begin(), aEnd2 = (*aIt)->end(); aIt2 != aEnd2; ++aIt2 ) + if( nInsertPos <= aIt2->mnTokPos ) + aIt2->mnTokPos = aIt2->mnTokPos + nInsertSize; +} + +void XclExpFmlaCompImpl::Overwrite( sal_uInt16 nWriteToPos, sal_uInt16 nOffset ) +{ + DBG_ASSERT( static_cast< size_t >( nWriteToPos + 1 ) < mxData->maTokVec.size(), "XclExpFmlaCompImpl::Overwrite - invalid position" ); + ShortToSVBT16( nOffset, &mxData->maTokVec[ nWriteToPos ] ); +} + +void XclExpFmlaCompImpl::UpdateAttrGoto( sal_uInt16 nAttrPos ) +{ + /* tAttrGoto contains distance from end of tAttr token to position behind + the function token (for IF or CHOOSE function), which is currently at + the end of the token array. Additionally this distance is decreased by + one, for whatever reason. So we have to subtract 4 and 1 from the + distance between the tAttr token start and the end of the token array. */ + Overwrite( nAttrPos + 2, static_cast< sal_uInt16 >( GetSize() - nAttrPos - 5 ) ); +} + +bool XclExpFmlaCompImpl::IsSpaceToken( sal_uInt16 nPos ) const +{ + return + (static_cast< size_t >( nPos + 4 ) <= mxData->maTokVec.size()) && + (mxData->maTokVec[ nPos ] == EXC_TOKID_ATTR) && + (mxData->maTokVec[ nPos + 1 ] == EXC_TOK_ATTR_SPACE); +} + +void XclExpFmlaCompImpl::RemoveTrailingParen() +{ + // remove trailing tParen token + if( !mxData->maTokVec.empty() && (mxData->maTokVec.back() == EXC_TOKID_PAREN) ) + mxData->maTokVec.pop_back(); + // remove remaining tAttrSpace tokens + while( (mxData->maTokVec.size() >= 4) && IsSpaceToken( GetSize() - 4 ) ) + mxData->maTokVec.erase( mxData->maTokVec.end() - 4, mxData->maTokVec.end() ); +} + +void XclExpFmlaCompImpl::AppendExt( sal_uInt8 nData ) +{ + mxData->maExtDataVec.push_back( nData ); +} + +void XclExpFmlaCompImpl::AppendExt( sal_uInt8 nData, size_t nCount ) +{ + mxData->maExtDataVec.resize( mxData->maExtDataVec.size() + nCount, nData ); +} + +void XclExpFmlaCompImpl::AppendExt( sal_uInt16 nData ) +{ + lclAppend( mxData->maExtDataVec, nData ); +} + +void XclExpFmlaCompImpl::AppendExt( sal_uInt32 nData ) +{ + lclAppend( mxData->maExtDataVec, nData ); +} + +void XclExpFmlaCompImpl::AppendExt( double fData ) +{ + lclAppend( mxData->maExtDataVec, fData ); +} + +void XclExpFmlaCompImpl::AppendExt( const String& rString ) +{ + lclAppend( mxData->maExtDataVec, GetRoot(), rString, (meBiff == EXC_BIFF8) ? EXC_STR_DEFAULT : EXC_STR_8BITLENGTH ); +} + +// ============================================================================ + +namespace { + +void lclInitOwnTab( ScSingleRefData& rRef, const ScAddress& rScPos, SCTAB nCurrScTab, bool b3DRefOnly ) +{ + if( b3DRefOnly ) + { + // no reduction to 2D reference, if global link manager is used + rRef.SetFlag3D( TRUE ); + } + else if( rScPos.Tab() == nCurrScTab ) + { + rRef.SetTabRel( TRUE ); + rRef.nRelTab = 0; + } +} + +void lclPutCellToTokenArray( ScTokenArray& rScTokArr, const ScAddress& rScPos, SCTAB nCurrScTab, bool b3DRefOnly ) +{ + ScSingleRefData aRef; + aRef.InitAddress( rScPos ); + lclInitOwnTab( aRef, rScPos, nCurrScTab, b3DRefOnly ); + rScTokArr.AddSingleReference( aRef ); +} + +void lclPutRangeToTokenArray( ScTokenArray& rScTokArr, const ScRange& rScRange, SCTAB nCurrScTab, bool b3DRefOnly ) +{ + if( rScRange.aStart == rScRange.aEnd ) + { + lclPutCellToTokenArray( rScTokArr, rScRange.aStart, nCurrScTab, b3DRefOnly ); + } + else + { + ScComplexRefData aRef; + aRef.InitRange( rScRange ); + lclInitOwnTab( aRef.Ref1, rScRange.aStart, nCurrScTab, b3DRefOnly ); + lclInitOwnTab( aRef.Ref2, rScRange.aEnd, nCurrScTab, b3DRefOnly ); + rScTokArr.AddDoubleReference( aRef ); + } +} + +} // namespace + +// ---------------------------------------------------------------------------- + +XclExpFormulaCompiler::XclExpFormulaCompiler( const XclExpRoot& rRoot ) : + XclExpRoot( rRoot ), + mxImpl( new XclExpFmlaCompImpl( rRoot ) ) +{ +} + +XclExpFormulaCompiler::~XclExpFormulaCompiler() +{ +} + +XclTokenArrayRef XclExpFormulaCompiler::CreateFormula( + XclFormulaType eType, const ScTokenArray& rScTokArr, + const ScAddress* pScBasePos, XclExpRefLog* pRefLog ) +{ + return mxImpl->CreateFormula( eType, rScTokArr, pScBasePos, pRefLog ); +} + +XclTokenArrayRef XclExpFormulaCompiler::CreateFormula( XclFormulaType eType, const ScAddress& rScPos ) +{ + ScTokenArray aScTokArr; + lclPutCellToTokenArray( aScTokArr, rScPos, GetCurrScTab(), mxImpl->Is3DRefOnly( eType ) ); + return mxImpl->CreateFormula( eType, aScTokArr ); +} + +XclTokenArrayRef XclExpFormulaCompiler::CreateFormula( XclFormulaType eType, const ScRange& rScRange ) +{ + ScTokenArray aScTokArr; + lclPutRangeToTokenArray( aScTokArr, rScRange, GetCurrScTab(), mxImpl->Is3DRefOnly( eType ) ); + return mxImpl->CreateFormula( eType, aScTokArr ); +} + +XclTokenArrayRef XclExpFormulaCompiler::CreateFormula( XclFormulaType eType, const ScRangeList& rScRanges ) +{ + ULONG nCount = rScRanges.Count(); + if( nCount == 0 ) + return XclTokenArrayRef(); + + ScTokenArray aScTokArr; + SCTAB nCurrScTab = GetCurrScTab(); + bool b3DRefOnly = mxImpl->Is3DRefOnly( eType ); + for( ULONG nIdx = 0; nIdx < nCount; ++nIdx ) + { + if( nIdx > 0 ) + aScTokArr.AddOpCode( ocUnion ); + lclPutRangeToTokenArray( aScTokArr, *rScRanges.GetObject( nIdx ), nCurrScTab, b3DRefOnly ); + } + return mxImpl->CreateFormula( eType, aScTokArr ); +} + +XclTokenArrayRef XclExpFormulaCompiler::CreateErrorFormula( sal_uInt8 nErrCode ) +{ + return mxImpl->CreateErrorFormula( nErrCode ); +} + +XclTokenArrayRef XclExpFormulaCompiler::CreateSpecialRefFormula( + sal_uInt8 nTokenId, const XclAddress& rXclPos ) +{ + return mxImpl->CreateSpecialRefFormula( nTokenId, rXclPos ); +} + +XclTokenArrayRef XclExpFormulaCompiler::CreateNameXFormula( + sal_uInt16 nExtSheet, sal_uInt16 nExtName ) +{ + return mxImpl->CreateNameXFormula( nExtSheet, nExtName ); +} + +// ============================================================================ + |