| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| #include "precompiled_formula.hxx" |
| #include "formula/FormulaCompiler.hxx" |
| #include "formula/errorcodes.hxx" |
| #include "formula/token.hxx" |
| #include "formula/tokenarray.hxx" |
| #include "core_resource.hxx" |
| #include "core_resource.hrc" |
| |
| #include <svl/zforlist.hxx> |
| #include <tools/rc.hxx> |
| #include <tools/rcid.h> |
| #include <com/sun/star/sheet/FormulaOpCodeMapEntry.hpp> |
| #include <com/sun/star/sheet/FormulaMapGroup.hpp> |
| #include <com/sun/star/sheet/FormulaMapGroupSpecialOffset.hpp> |
| #include <stdio.h> |
| |
| // ============================================================================= |
| namespace formula |
| { |
| // ============================================================================= |
| using namespace ::com::sun::star; |
| |
| static const sal_Char* pInternal[ 1 ] = { "TTT" }; |
| |
| // ============================================================================= |
| namespace |
| { |
| // ============================================================================= |
| class FormulaCompilerRecursionGuard |
| { |
| private: |
| short& rRecursion; |
| public: |
| FormulaCompilerRecursionGuard( short& rRec ) |
| : rRecursion( rRec ) { ++rRecursion; } |
| ~FormulaCompilerRecursionGuard() { --rRecursion; } |
| }; |
| |
| short lcl_GetRetFormat( OpCode eOpCode ) |
| { |
| switch (eOpCode) |
| { |
| case ocEqual: |
| case ocNotEqual: |
| case ocLess: |
| case ocGreater: |
| case ocLessEqual: |
| case ocGreaterEqual: |
| case ocAnd: |
| case ocOr: |
| case ocNot: |
| case ocTrue: |
| case ocFalse: |
| case ocIsEmpty: |
| case ocIsString: |
| case ocIsNonString: |
| case ocIsLogical: |
| case ocIsRef: |
| case ocIsValue: |
| case ocIsFormula: |
| case ocIsNA: |
| case ocIsErr: |
| case ocIsError: |
| case ocIsEven: |
| case ocIsOdd: |
| case ocExact: |
| return NUMBERFORMAT_LOGICAL; |
| case ocGetActDate: |
| case ocGetDate: |
| case ocEasterSunday : |
| return NUMBERFORMAT_DATE; |
| case ocGetActTime: |
| return NUMBERFORMAT_DATETIME; |
| case ocGetTime: |
| return NUMBERFORMAT_TIME; |
| case ocNPV: |
| case ocBW: |
| case ocDIA: |
| case ocGDA: |
| case ocGDA2: |
| case ocVBD: |
| case ocLIA: |
| case ocRMZ: |
| case ocZW: |
| case ocZinsZ: |
| case ocKapz: |
| case ocKumZinsZ: |
| case ocKumKapZ: |
| return NUMBERFORMAT_CURRENCY; |
| case ocZins: |
| case ocIRR: |
| case ocMIRR: |
| case ocZGZ: |
| case ocEffektiv: |
| case ocNominal: |
| case ocPercentSign: |
| return NUMBERFORMAT_PERCENT; |
| // case ocSum: |
| // case ocSumSQ: |
| // case ocProduct: |
| // case ocAverage: |
| // return -1; |
| default: |
| return NUMBERFORMAT_NUMBER; |
| } |
| return NUMBERFORMAT_NUMBER; |
| } |
| |
| inline void lclPushOpCodeMapEntry( ::std::vector< sheet::FormulaOpCodeMapEntry >& rVec, const String* pTable, sal_uInt16 nOpCode ) |
| { |
| sheet::FormulaOpCodeMapEntry aEntry; |
| aEntry.Token.OpCode = nOpCode; |
| aEntry.Name = pTable[nOpCode]; |
| rVec.push_back( aEntry); |
| } |
| |
| void lclPushOpCodeMapEntries( ::std::vector< sheet::FormulaOpCodeMapEntry >& rVec, const String* pTable, sal_uInt16 nOpCodeBeg, sal_uInt16 nOpCodeEnd ) |
| { |
| for (sal_uInt16 nOpCode = nOpCodeBeg; nOpCode < nOpCodeEnd; ++nOpCode) |
| lclPushOpCodeMapEntry( rVec, pTable, nOpCode ); |
| } |
| |
| void lclPushOpCodeMapEntries( ::std::vector< sheet::FormulaOpCodeMapEntry >& rVec, const String* pTable, const sal_uInt16* pnOpCodes, size_t nCount ) |
| { |
| for (const sal_uInt16* pnEnd = pnOpCodes + nCount; pnOpCodes < pnEnd; ++pnOpCodes) |
| lclPushOpCodeMapEntry( rVec, pTable, *pnOpCodes ); |
| } |
| |
| class OpCodeList : public Resource // temp object for resource |
| { |
| public: |
| |
| OpCodeList( sal_uInt16, FormulaCompiler::NonConstOpCodeMapPtr ); |
| |
| private: |
| bool getOpCodeString( String& rStr, sal_uInt16 nOp ); |
| void putDefaultOpCode( FormulaCompiler::NonConstOpCodeMapPtr xMap, sal_uInt16 nOp ); |
| |
| private: |
| enum SeparatorType |
| { |
| SEMICOLON_BASE, |
| COMMA_BASE |
| }; |
| SeparatorType meSepType; |
| }; |
| |
| OpCodeList::OpCodeList( sal_uInt16 nRID, FormulaCompiler::NonConstOpCodeMapPtr xMap ) : |
| Resource( ResId(nRID,*ResourceManager::getResManager()) ) |
| ,meSepType(SEMICOLON_BASE) |
| { |
| for (sal_uInt16 i = 0; i <= SC_OPCODE_LAST_OPCODE_ID; ++i) |
| { |
| String aOpStr; |
| if ( getOpCodeString(aOpStr, i) ) |
| xMap->putOpCode(aOpStr, OpCode(i)); |
| else |
| putDefaultOpCode(xMap, i); |
| } |
| |
| FreeResource(); |
| } |
| |
| bool OpCodeList::getOpCodeString( String& rStr, sal_uInt16 nOp ) |
| { |
| switch (nOp) |
| { |
| case SC_OPCODE_SEP: |
| { |
| if (meSepType == COMMA_BASE) |
| { |
| rStr = String::CreateFromAscii(","); |
| return true; |
| } |
| else if (meSepType == SEMICOLON_BASE) |
| { |
| rStr = String::CreateFromAscii(";"); |
| return true; |
| } |
| } |
| break; |
| case SC_OPCODE_ARRAY_COL_SEP: |
| { |
| if (meSepType == COMMA_BASE) |
| { |
| rStr = String::CreateFromAscii(","); |
| return true; |
| } |
| else if (meSepType == SEMICOLON_BASE) |
| { |
| rStr = String::CreateFromAscii(";"); |
| return true; |
| } |
| } |
| break; |
| case SC_OPCODE_ARRAY_ROW_SEP: |
| { |
| if (meSepType == COMMA_BASE) |
| { |
| rStr = String::CreateFromAscii(";"); |
| return true; |
| } |
| else if (meSepType == SEMICOLON_BASE) |
| { |
| rStr = String::CreateFromAscii("|"); |
| return true; |
| } |
| } |
| break; |
| } |
| |
| return false; |
| } |
| |
| void OpCodeList::putDefaultOpCode( FormulaCompiler::NonConstOpCodeMapPtr xMap, sal_uInt16 nOp ) |
| { |
| ResId aRes(nOp,*ResourceManager::getResManager()); |
| aRes.SetRT(RSC_STRING); |
| if (IsAvailableRes(aRes)) |
| xMap->putOpCode(aRes, OpCode(nOp)); |
| } |
| // ----------------------------------------------------------------------------- |
| // static |
| const sal_Unicode* lcl_UnicodeStrChr( const sal_Unicode* pStr,sal_Unicode c ) |
| { |
| if ( !pStr ) |
| return NULL; |
| while ( *pStr ) |
| { |
| if ( *pStr == c ) |
| return pStr; |
| pStr++; |
| } |
| return NULL; |
| } |
| // ============================================================================= |
| } // empty |
| // ============================================================================= |
| |
| void FormulaCompiler::OpCodeMap::putExternal( const String & rSymbol, const String & rAddIn ) |
| { |
| bool bOk = mpExternalHashMap->insert( ExternalHashMap::value_type( rSymbol, rAddIn)).second; |
| if (bOk) |
| bOk = mpReverseExternalHashMap->insert( ExternalHashMap::value_type( rAddIn, rSymbol)).second; |
| DBG_ASSERT( bOk, "OpCodeMap::putExternal: symbol not inserted"); |
| } |
| |
| void FormulaCompiler::OpCodeMap::putExternalSoftly( const String & rSymbol, const String & rAddIn ) |
| { |
| bool bOk = mpReverseExternalHashMap->insert( ExternalHashMap::value_type( rAddIn, rSymbol)).second; |
| if (bOk) |
| mpExternalHashMap->insert( ExternalHashMap::value_type( rSymbol, rAddIn)).second; |
| } |
| uno::Sequence< sheet::FormulaToken > FormulaCompiler::OpCodeMap::createSequenceOfFormulaTokens(const FormulaCompiler& _rCompiler,const uno::Sequence< ::rtl::OUString >& rNames ) const |
| { |
| const sal_Int32 nLen = rNames.getLength(); |
| uno::Sequence< sheet::FormulaToken > aTokens( nLen); |
| sheet::FormulaToken* pToken = aTokens.getArray(); |
| ::rtl::OUString const * pName = rNames.getConstArray(); |
| ::rtl::OUString const * const pStop = pName + nLen; |
| for ( ; pName < pStop; ++pName, ++pToken) |
| { |
| OpCodeHashMap::const_iterator iLook( mpHashMap->find( *pName)); |
| if (iLook != mpHashMap->end()) |
| pToken->OpCode = (*iLook).second; |
| else |
| { |
| ::rtl::OUString aIntName; |
| if (hasExternals()) |
| { |
| ExternalHashMap::const_iterator iExt( mpExternalHashMap->find( *pName)); |
| if (iExt != mpExternalHashMap->end()) |
| aIntName = (*iExt).second; |
| // Check for existence not needed here, only name-mapping is of |
| // interest. |
| } |
| if (!aIntName.getLength()) |
| aIntName = _rCompiler.FindAddInFunction(*pName, !isEnglish()); // bLocalFirst=sal_False for english |
| if (!aIntName.getLength()) |
| pToken->OpCode = getOpCodeUnknown(); |
| else |
| { |
| pToken->OpCode = ocExternal; |
| pToken->Data <<= aIntName; |
| } |
| } |
| } |
| return aTokens; |
| } |
| uno::Sequence< sheet::FormulaOpCodeMapEntry > FormulaCompiler::OpCodeMap::createSequenceOfAvailableMappings(const FormulaCompiler& _rCompiler,const sal_Int32 nGroups ) const |
| { |
| using namespace sheet; |
| |
| // Unfortunately uno::Sequence can't grow without cumbersome reallocs. As |
| // we don't know in advance how many elements it will have we use a |
| // temporary vector to add elements and then copy to Sequence :-( |
| ::std::vector< FormulaOpCodeMapEntry > aVec; |
| |
| if (nGroups == FormulaMapGroup::SPECIAL) |
| { |
| // Use specific order, keep in sync with |
| // offapi/com/sun/star/sheet/FormulaMapGroupSpecialOffset.idl |
| static const struct |
| { |
| sal_Int32 nOff; |
| OpCode eOp; |
| } aMap[] = { |
| { FormulaMapGroupSpecialOffset::PUSH , ocPush } , |
| { FormulaMapGroupSpecialOffset::CALL , ocCall } , |
| { FormulaMapGroupSpecialOffset::STOP , ocStop } , |
| { FormulaMapGroupSpecialOffset::EXTERNAL , ocExternal } , |
| { FormulaMapGroupSpecialOffset::NAME , ocName } , |
| { FormulaMapGroupSpecialOffset::NO_NAME , ocNoName } , |
| { FormulaMapGroupSpecialOffset::MISSING , ocMissing } , |
| { FormulaMapGroupSpecialOffset::BAD , ocBad } , |
| { FormulaMapGroupSpecialOffset::SPACES , ocSpaces } , |
| { FormulaMapGroupSpecialOffset::MAT_REF , ocMatRef } , |
| { FormulaMapGroupSpecialOffset::DB_AREA , ocDBArea } , |
| { FormulaMapGroupSpecialOffset::MACRO , ocMacro } , |
| { FormulaMapGroupSpecialOffset::COL_ROW_NAME , ocColRowName } |
| }; |
| const size_t nCount = sizeof(aMap)/sizeof(aMap[0]); |
| // Preallocate vector elements. |
| if (aVec.size() < nCount) |
| { |
| FormulaOpCodeMapEntry aEntry; |
| aEntry.Token.OpCode = getOpCodeUnknown(); |
| aVec.resize( nCount, aEntry); |
| } // if (aVec.size() < nCount) |
| |
| FormulaOpCodeMapEntry aEntry; |
| for (size_t i=0; i < nCount; ++i) |
| { |
| size_t nIndex = static_cast< size_t >( aMap[i].nOff ); |
| if (aVec.size() <= nIndex) |
| { |
| // The offsets really should be aligned with the size, so if |
| // the vector was preallocated above this code to resize it is |
| // just a measure in case the table isn't in sync with the API, |
| // usually it isn't executed. |
| aEntry.Token.OpCode = getOpCodeUnknown(); |
| aVec.resize( nIndex + 1, aEntry ); |
| } |
| aEntry.Token.OpCode = aMap[i].eOp; |
| aVec[nIndex] = aEntry; |
| } |
| } |
| else |
| { |
| /* FIXME: Once we support error constants in formulas we'll need a map |
| * group for that, e.g. FormulaMapGroup::ERROR_CONSTANTS, and fill |
| * SC_OPCODE_START_ERRORS to SC_OPCODE_STOP_ERRORS. */ |
| |
| // Anything else but SPECIAL. |
| if ((nGroups & FormulaMapGroup::SEPARATORS) != 0) |
| { |
| static const sal_uInt16 aOpCodes[] = { |
| SC_OPCODE_OPEN, |
| SC_OPCODE_CLOSE, |
| SC_OPCODE_SEP, |
| }; |
| lclPushOpCodeMapEntries( aVec, mpTable, aOpCodes, sizeof(aOpCodes)/sizeof(aOpCodes[0]) ); |
| } |
| if ((nGroups & FormulaMapGroup::ARRAY_SEPARATORS) != 0) |
| { |
| static const sal_uInt16 aOpCodes[] = { |
| SC_OPCODE_ARRAY_OPEN, |
| SC_OPCODE_ARRAY_CLOSE, |
| SC_OPCODE_ARRAY_ROW_SEP, |
| SC_OPCODE_ARRAY_COL_SEP |
| }; |
| lclPushOpCodeMapEntries( aVec, mpTable, aOpCodes, sizeof(aOpCodes)/sizeof(aOpCodes[0]) ); |
| } |
| if ((nGroups & FormulaMapGroup::UNARY_OPERATORS) != 0) |
| { |
| // Due to the nature of the percent operator following its operand |
| // it isn't sorted into unary operators for compiler interna. |
| lclPushOpCodeMapEntry( aVec, mpTable, ocPercentSign ); |
| // "+" can be used as unary operator too, push only if binary group is not set |
| if ((nGroups & FormulaMapGroup::BINARY_OPERATORS) == 0) |
| lclPushOpCodeMapEntry( aVec, mpTable, ocAdd ); |
| // regular unary operators |
| for (sal_uInt16 nOp = SC_OPCODE_START_UN_OP; nOp < SC_OPCODE_STOP_UN_OP && nOp < mnSymbols; ++nOp) |
| { |
| switch (nOp) |
| { |
| // NOT and NEG in fact are functions but for legacy reasons |
| // are sorted into unary operators for compiler interna. |
| case SC_OPCODE_NOT : |
| case SC_OPCODE_NEG : |
| break; // nothing, |
| default: |
| lclPushOpCodeMapEntry( aVec, mpTable, nOp ); |
| } |
| } |
| } |
| if ((nGroups & FormulaMapGroup::BINARY_OPERATORS) != 0) |
| { |
| for (sal_uInt16 nOp = SC_OPCODE_START_BIN_OP; nOp < SC_OPCODE_STOP_BIN_OP && nOp < mnSymbols; ++nOp) |
| { |
| switch (nOp) |
| { |
| // AND and OR in fact are functions but for legacy reasons |
| // are sorted into binary operators for compiler interna. |
| case SC_OPCODE_AND : |
| case SC_OPCODE_OR : |
| break; // nothing, |
| default: |
| lclPushOpCodeMapEntry( aVec, mpTable, nOp ); |
| } |
| } |
| } |
| if ((nGroups & FormulaMapGroup::FUNCTIONS) != 0) |
| { |
| // Function names are not consecutive, skip the gaps between |
| // functions with no parameter, functions with 1 parameter |
| lclPushOpCodeMapEntries( aVec, mpTable, SC_OPCODE_START_NO_PAR, ::std::min< sal_uInt16 >( SC_OPCODE_STOP_NO_PAR, mnSymbols ) ); |
| lclPushOpCodeMapEntries( aVec, mpTable, SC_OPCODE_START_1_PAR, ::std::min< sal_uInt16 >( SC_OPCODE_STOP_1_PAR, mnSymbols ) ); |
| // Additional functions not within range of functions. |
| static const sal_uInt16 aOpCodes[] = { |
| SC_OPCODE_IF, |
| SC_OPCODE_CHOSE, |
| SC_OPCODE_AND, |
| SC_OPCODE_OR, |
| SC_OPCODE_NOT, |
| SC_OPCODE_NEG |
| }; |
| lclPushOpCodeMapEntries( aVec, mpTable, aOpCodes, sizeof(aOpCodes)/sizeof(aOpCodes[0]) ); |
| // functions with 2 or more parameters. |
| for (sal_uInt16 nOp = SC_OPCODE_START_2_PAR; nOp < SC_OPCODE_STOP_2_PAR && nOp < mnSymbols; ++nOp) |
| { |
| switch (nOp) |
| { |
| // NO_NAME is in SPECIAL. |
| case SC_OPCODE_NO_NAME : |
| break; // nothing, |
| default: |
| lclPushOpCodeMapEntry( aVec, mpTable, nOp ); |
| } |
| } |
| // If AddIn functions are present in this mapping, use them, and only those. |
| if (hasExternals()) |
| { |
| for (ExternalHashMap::const_iterator it( mpExternalHashMap->begin());it != mpExternalHashMap->end(); ++it) |
| { |
| FormulaOpCodeMapEntry aEntry; |
| aEntry.Name = (*it).first; |
| aEntry.Token.Data <<= ::rtl::OUString( (*it).second); |
| aEntry.Token.OpCode = ocExternal; |
| aVec.push_back( aEntry); |
| } |
| } |
| else |
| { |
| //DBG_ASSERT( isCore(), "FormulaCompiler::OpCodeMap::createSequenceOfAvailableMappings: AddIn mapping from collection only implemented for core languages"); |
| _rCompiler.fillAddInToken(aVec,isEnglish()); |
| } |
| } |
| } |
| const FormulaOpCodeMapEntry* pRet = aVec.empty() ? 0 : &aVec[0]; |
| return uno::Sequence< FormulaOpCodeMapEntry >(pRet, aVec.size()); |
| } |
| //----------------------------------------------------------------------------- |
| |
| void FormulaCompiler::OpCodeMap::putOpCode( const String & rStr, const OpCode eOp ) |
| { |
| DBG_ASSERT( 0 < eOp && sal_uInt16(eOp) < mnSymbols, "OpCodeMap::putOpCode: OpCode out of range"); |
| if (0 < eOp && sal_uInt16(eOp) < mnSymbols) |
| { |
| DBG_ASSERT( (mpTable[eOp].Len() == 0) || (mpTable[eOp] == rStr) || (eOp == ocCurrency), |
| ByteString( "OpCodeMap::putOpCode: reusing OpCode "). |
| Append( ByteString::CreateFromInt32( sal_Int32( eOp))).Append( " ("). |
| Append( ByteString( rStr, RTL_TEXTENCODING_ASCII_US)).Append( ')').GetBuffer()); |
| mpTable[eOp] = rStr; |
| mpHashMap->insert( OpCodeHashMap::value_type( rStr, eOp)); |
| } |
| } |
| // ----------------------------------------------------------------------------- |
| // class FormulaCompiler |
| // ----------------------------------------------------------------------------- |
| DBG_NAME(FormulaCompiler) |
| FormulaCompiler::FormulaCompiler(FormulaTokenArray& _rArr) |
| : |
| pArr( &_rArr ), |
| pExternalRef(NULL), |
| pStack( NULL ), |
| nRecursion(0), |
| nNumFmt( NUMBERFORMAT_UNDEFINED ), |
| meGrammar( formula::FormulaGrammar::GRAM_UNSPECIFIED ), |
| bAutoCorrect( sal_False ), |
| bCorrected( sal_False ), |
| bCompileForFAP( sal_False ), |
| bIgnoreErrors( sal_False ) |
| |
| { |
| DBG_CTOR(FormulaCompiler,NULL); |
| } |
| FormulaCompiler::FormulaCompiler() |
| : |
| pArr( NULL ), |
| pExternalRef(NULL), |
| pStack( NULL ), |
| nRecursion(0), |
| nNumFmt( NUMBERFORMAT_UNDEFINED ), |
| meGrammar( formula::FormulaGrammar::GRAM_UNSPECIFIED ), |
| bAutoCorrect( sal_False ), |
| bCorrected( sal_False ), |
| bCompileForFAP( sal_False ), |
| bIgnoreErrors( sal_False ) |
| |
| { |
| DBG_CTOR(FormulaCompiler,NULL); |
| } |
| FormulaCompiler::~FormulaCompiler() |
| { |
| DBG_DTOR(FormulaCompiler,NULL); |
| } |
| |
| FormulaCompiler::OpCodeMapPtr FormulaCompiler::GetOpCodeMap( const sal_Int32 nLanguage ) const |
| { |
| FormulaCompiler::OpCodeMapPtr xMap; |
| using namespace sheet; |
| switch (nLanguage) |
| { |
| case FormulaLanguage::ODFF : |
| if (!mxSymbolsODFF) |
| InitSymbolsODFF(); |
| xMap = mxSymbolsODFF; |
| break; |
| case FormulaLanguage::ODF_11 : |
| if (!mxSymbolsPODF) |
| InitSymbolsPODF(); |
| xMap = mxSymbolsPODF; |
| break; |
| case FormulaLanguage::ENGLISH : |
| if (!mxSymbolsEnglish) |
| InitSymbolsEnglish(); |
| xMap = mxSymbolsEnglish; |
| break; |
| case FormulaLanguage::NATIVE : |
| if (!mxSymbolsNative) |
| InitSymbolsNative(); |
| xMap = mxSymbolsNative; |
| break; |
| default: |
| ; // nothing, NULL map returned |
| } |
| return xMap; |
| } |
| // ----------------------------------------------------------------------------- |
| |
| String FormulaCompiler::FindAddInFunction( const String& /*rUpperName*/, sal_Bool /*bLocalFirst*/ ) const |
| { |
| return String(); |
| } |
| // ----------------------------------------------------------------------------- |
| FormulaCompiler::OpCodeMapPtr FormulaCompiler::CreateOpCodeMap( |
| const uno::Sequence< |
| const sheet::FormulaOpCodeMapEntry > & rMapping, |
| bool bEnglish ) |
| { |
| using sheet::FormulaOpCodeMapEntry; |
| // Filter / API maps are never Core |
| NonConstOpCodeMapPtr xMap( new OpCodeMap( SC_OPCODE_LAST_OPCODE_ID + 1,false, FormulaGrammar::mergeToGrammar( FormulaGrammar::setEnglishBit(FormulaGrammar::GRAM_EXTERNAL, bEnglish),FormulaGrammar::CONV_UNSPECIFIED))); |
| FormulaOpCodeMapEntry const * pArr2 = rMapping.getConstArray(); |
| FormulaOpCodeMapEntry const * const pStop = pArr2 + rMapping.getLength(); |
| for ( ; pArr2 < pStop; ++pArr2) |
| { |
| OpCode eOp = OpCode(pArr2->Token.OpCode); |
| if (eOp != ocExternal) |
| xMap->putOpCode( pArr2->Name, eOp); |
| else |
| { |
| ::rtl::OUString aExternalName; |
| if (pArr2->Token.Data >>= aExternalName) |
| xMap->putExternal( pArr2->Name, aExternalName); |
| else |
| { |
| DBG_ERRORFILE( "FormulaCompiler::CreateOpCodeMap: no Token.Data external name"); |
| } |
| } |
| } |
| return xMap; |
| } |
| |
| // ----------------------------------------------------------------------------- |
| void lcl_fillNativeSymbols(FormulaCompiler::NonConstOpCodeMapPtr& _xMap,bool _destroy = false) |
| { |
| static FormulaCompiler::NonConstOpCodeMapPtr s_SymbolMap; |
| if ( _destroy ) |
| { |
| s_SymbolMap.reset(); |
| } // if ( _destroy ) |
| else if ( !s_SymbolMap.get() ) |
| { |
| // Core |
| s_SymbolMap.reset( new FormulaCompiler::OpCodeMap( SC_OPCODE_LAST_OPCODE_ID + 1, true, FormulaGrammar::GRAM_NATIVE_UI)); |
| OModuleClient aModuleClient; |
| OpCodeList aOpCodeListNative( RID_STRLIST_FUNCTION_NAMES, s_SymbolMap ); |
| // No AddInMap for native core mapping. |
| } // if ( !s_SymbolMap.get() ) |
| _xMap = s_SymbolMap; |
| } |
| // ----------------------------------------------------------------------------- |
| const String& FormulaCompiler::GetNativeSymbol( OpCode eOp ) |
| { |
| NonConstOpCodeMapPtr xSymbolsNative; |
| lcl_fillNativeSymbols(xSymbolsNative); |
| return xSymbolsNative->getSymbol( eOp ); |
| } |
| // ----------------------------------------------------------------------------- |
| void FormulaCompiler::InitSymbolsNative() const |
| { |
| if (mxSymbolsNative.get()) |
| return; |
| //! Experimental! |
| // Use English function names and separators instead of native in UI. |
| static const sal_Char aEnvVarName[] = "OOO_CALC_USE_ENGLISH_FORMULAS"; |
| const char* pEnv = getenv( aEnvVarName); |
| if (pEnv && (*pEnv == 'Y' || *pEnv == 'y' || *pEnv == '1') ) |
| { |
| fprintf( stderr, "%s=%s => UI uses English function names and separators in formulas.\n", |
| aEnvVarName, pEnv); |
| InitSymbolsEnglish(); |
| mxSymbolsNative = mxSymbolsEnglish; |
| return; |
| } |
| static NonConstOpCodeMapPtr s_sSymbol; |
| if ( !s_sSymbol.get() ) |
| lcl_fillNativeSymbols(s_sSymbol); |
| mxSymbolsNative = s_sSymbol; |
| } |
| // ----------------------------------------------------------------------------- |
| void FormulaCompiler::InitSymbolsEnglish() const |
| { |
| static NonConstOpCodeMapPtr s_sSymbol; |
| if ( !s_sSymbol.get() ) |
| loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH,FormulaGrammar::GRAM_ENGLISH,s_sSymbol); |
| mxSymbolsEnglish = s_sSymbol; |
| } |
| // ----------------------------------------------------------------------------- |
| void FormulaCompiler::InitSymbolsPODF() const |
| { |
| static NonConstOpCodeMapPtr s_sSymbol; |
| if ( !s_sSymbol.get() ) |
| loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH,FormulaGrammar::GRAM_PODF,s_sSymbol); |
| mxSymbolsPODF = s_sSymbol; |
| } |
| // ----------------------------------------------------------------------------- |
| void FormulaCompiler::InitSymbolsODFF() const |
| { |
| static NonConstOpCodeMapPtr s_sSymbol; |
| if ( !s_sSymbol.get() ) |
| loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH_ODFF,FormulaGrammar::GRAM_ODFF,s_sSymbol); |
| mxSymbolsODFF = s_sSymbol; |
| } |
| // ----------------------------------------------------------------------------- |
| void FormulaCompiler::loadSymbols(sal_uInt16 _nSymbols,FormulaGrammar::Grammar _eGrammar,NonConstOpCodeMapPtr& _xMap) const |
| { |
| if ( !_xMap.get() ) |
| { |
| // not Core |
| _xMap.reset( new OpCodeMap( SC_OPCODE_LAST_OPCODE_ID + 1, _eGrammar != FormulaGrammar::GRAM_ODFF, _eGrammar )); |
| OModuleClient aModuleClient; |
| OpCodeList aOpCodeList( _nSymbols, _xMap ); |
| |
| fillFromAddInMap( _xMap, _eGrammar); |
| // Fill from collection for AddIns not already present. |
| if ( FormulaGrammar::GRAM_ENGLISH != _eGrammar ) |
| fillFromAddInCollectionUpperName( _xMap); |
| else |
| fillFromAddInCollectionEnglishName( _xMap); |
| } |
| } |
| // ----------------------------------------------------------------------------- |
| void FormulaCompiler::fillFromAddInCollectionUpperName( NonConstOpCodeMapPtr /*xMap */) const |
| { |
| } |
| // ----------------------------------------------------------------------------- |
| void FormulaCompiler::fillFromAddInCollectionEnglishName( NonConstOpCodeMapPtr /*xMap */) const |
| { |
| } |
| // ----------------------------------------------------------------------------- |
| void FormulaCompiler::fillFromAddInMap( NonConstOpCodeMapPtr /*xMap*/, FormulaGrammar::Grammar /*_eGrammar */) const |
| { |
| } |
| // ----------------------------------------------------------------------------- |
| OpCode FormulaCompiler::GetEnglishOpCode( const String& rName ) const |
| { |
| FormulaCompiler::OpCodeMapPtr xMap = GetOpCodeMap(sheet::FormulaLanguage::ENGLISH); |
| |
| formula::OpCodeHashMap::const_iterator iLook( xMap->getHashMap()->find( rName ) ); |
| bool bFound = (iLook != xMap->getHashMap()->end()); |
| return bFound ? (*iLook).second : OpCode(ocNone); |
| } |
| |
| // Remove quotes, escaped quotes are unescaped. |
| sal_Bool FormulaCompiler::DeQuote( String& rStr ) |
| { |
| xub_StrLen nLen = rStr.Len(); |
| if ( nLen > 1 && rStr.GetChar(0) == '\'' && rStr.GetChar( nLen-1 ) == '\'' ) |
| { |
| rStr.Erase( nLen-1, 1 ); |
| rStr.Erase( 0, 1 ); |
| xub_StrLen nPos = 0; |
| while ( (nPos = rStr.SearchAscii( "\\\'", nPos)) != STRING_NOTFOUND ) |
| { |
| rStr.Erase( nPos, 1 ); |
| ++nPos; |
| } |
| return sal_True; |
| } |
| return sal_False; |
| } |
| // ----------------------------------------------------------------------------- |
| void FormulaCompiler::fillAddInToken(::std::vector< sheet::FormulaOpCodeMapEntry >& /*_rVec*/,bool /*_bIsEnglish*/) const |
| { |
| } |
| // ----------------------------------------------------------------------------- |
| sal_Bool FormulaCompiler::IsMatrixFunction(OpCode _eOpCode) |
| { |
| switch ( _eOpCode ) |
| { |
| case ocDde : |
| case ocGrowth : |
| case ocTrend : |
| case ocRKP : |
| case ocRGP : |
| case ocFrequency : |
| case ocMatTrans : |
| case ocMatMult : |
| case ocMatInv : |
| case ocMatrixUnit : |
| return sal_True; |
| default: |
| { |
| // added to avoid warnings |
| } |
| } |
| return sal_False; |
| } |
| |
| // ----------------------------------------------------------------------------- |
| FormulaCompiler::OpCodeMap::~OpCodeMap() |
| { |
| delete mpReverseExternalHashMap; |
| delete mpExternalHashMap; |
| delete [] mpTable; |
| delete mpHashMap; |
| } |
| // ----------------------------------------------------------------------------- |
| sal_Int32 FormulaCompiler::OpCodeMap::getOpCodeUnknown() |
| { |
| static const sal_Int32 kOpCodeUnknown = -1; |
| return kOpCodeUnknown; |
| } |
| // ----------------------------------------------------------------------------- |
| sal_Bool FormulaCompiler::GetToken() |
| { |
| static const short nRecursionMax = 42; |
| FormulaCompilerRecursionGuard aRecursionGuard( nRecursion ); |
| if ( nRecursion > nRecursionMax ) |
| { |
| SetError( errStackOverflow ); |
| pToken = new FormulaByteToken( ocStop ); |
| return sal_False; |
| } |
| if ( bAutoCorrect && !pStack ) |
| { // #61426# don't merge stacked subroutine code into entered formula |
| aCorrectedFormula += aCorrectedSymbol; |
| aCorrectedSymbol.Erase(); |
| } |
| sal_Bool bStop = sal_False; |
| if( pArr->GetCodeError() && !bIgnoreErrors ) |
| bStop = sal_True; |
| else |
| { |
| short nWasColRowName; |
| if ( pArr->nIndex |
| && pArr->pCode[ pArr->nIndex-1 ]->GetOpCode() == ocColRowName ) |
| nWasColRowName = 1; |
| else |
| nWasColRowName = 0; |
| pToken = pArr->Next(); |
| while( pToken && pToken->GetOpCode() == ocSpaces ) |
| { |
| if ( nWasColRowName ) |
| nWasColRowName++; |
| if ( bAutoCorrect && !pStack ) |
| CreateStringFromToken( aCorrectedFormula, pToken, sal_False ); |
| pToken = pArr->Next(); |
| } |
| if ( bAutoCorrect && !pStack && pToken ) |
| CreateStringFromToken( aCorrectedSymbol, pToken, sal_False ); |
| if( !pToken ) |
| { |
| if( pStack ) |
| { |
| PopTokenArray(); |
| return GetToken(); |
| } |
| else |
| bStop = sal_True; |
| } |
| else |
| { |
| if ( nWasColRowName >= 2 && pToken->GetOpCode() == ocColRowName ) |
| { // convert an ocSpaces to ocIntersect in RPN |
| pToken = new FormulaByteToken( ocIntersect ); |
| pArr->nIndex--; // we advanced to the second ocColRowName, step back |
| } |
| } |
| } |
| if( bStop ) |
| { |
| pToken = new FormulaByteToken( ocStop ); |
| return sal_False; |
| } |
| if( pToken->GetOpCode() == ocSubTotal ) |
| glSubTotal = sal_True; |
| else if ( pToken->GetOpCode() == ocExternalRef ) |
| { |
| return HandleExternalReference(*pToken); |
| } |
| else if( pToken->GetOpCode() == ocName ) |
| { |
| return HandleRange(); |
| } |
| else if( pToken->GetOpCode() == ocColRowName ) |
| { |
| return HandleSingleRef(); |
| } |
| else if( pToken->GetOpCode() == ocDBArea ) |
| { |
| return HandleDbData(); |
| } |
| else if( pToken->GetType() == svSingleRef ) |
| { |
| pArr->nRefs++; |
| } |
| else if( pToken->GetType() == svDoubleRef ) |
| { |
| pArr->nRefs++; |
| } |
| return sal_True; |
| } |
| //--------------------------------------------------------------------------- |
| // RPN creation by recursion |
| //--------------------------------------------------------------------------- |
| |
| void FormulaCompiler::Factor() |
| { |
| if ( pArr->GetCodeError() && !bIgnoreErrors ) |
| return; |
| |
| CurrentFactor pFacToken( this ); |
| |
| OpCode eOp = pToken->GetOpCode(); |
| if( eOp == ocPush || eOp == ocColRowNameAuto || eOp == ocMatRef || |
| eOp == ocDBArea |
| || (bCompileForFAP && ((eOp == ocName) || (eOp == ocDBArea) |
| || (eOp == ocColRowName) || (eOp == ocBad))) |
| ) |
| { |
| PutCode( pToken ); |
| eOp = NextToken(); |
| if( eOp == ocOpen ) |
| { |
| // PUSH( is an error that may be caused by an unknown function. |
| SetError( |
| ( pToken->GetType() == svString |
| || pToken->GetType() == svSingleRef ) |
| ? errNoName : errOperatorExpected ); |
| if ( bAutoCorrect && !pStack ) |
| { // assume multiplication |
| aCorrectedFormula += mxSymbols->getSymbol(ocMul); |
| bCorrected = sal_True; |
| NextToken(); |
| eOp = Expression(); |
| if( eOp != ocClose ) |
| SetError(errPairExpected); |
| else |
| eOp = NextToken(); |
| } |
| } |
| } |
| else if( eOp == ocOpen ) |
| { |
| NextToken(); |
| eOp = Expression(); |
| while ((eOp == ocSep) && (!pArr->GetCodeError() || bIgnoreErrors)) |
| { // range list (A1;A2) converted to (A1~A2) |
| pFacToken = pToken; |
| NextToken(); |
| eOp = Expression(); |
| // Do not ignore error here, regardless of bIgnoreErrors, otherwise |
| // errors like =(1;) would also result in display of =(1~) |
| if (!pArr->GetCodeError()) |
| { |
| pFacToken->NewOpCode( ocUnion,FormulaToken::PrivateAccess()); |
| PutCode( pFacToken); |
| } |
| } |
| if (eOp != ocClose) |
| SetError(errPairExpected); |
| else |
| eOp = NextToken(); |
| } |
| else |
| { |
| if( nNumFmt == NUMBERFORMAT_UNDEFINED ) |
| nNumFmt = lcl_GetRetFormat( eOp ); |
| // Functions that have to be always recalculated |
| switch( eOp ) |
| { |
| // no parameters: |
| case ocRandom: |
| case ocGetActDate: |
| case ocGetActTime: |
| // one parameter: |
| case ocFormula: |
| case ocInfo: |
| // more than one parameters: |
| // ocIndirect/ocIndirectXL otherwise would have to do |
| // StopListening and StartListening on a reference for every |
| // interpreted value. |
| case ocIndirect: |
| case ocIndirectXL: |
| // ocOffset results in indirect references. |
| case ocOffset: |
| pArr->SetRecalcModeAlways(); |
| break; |
| // Functions recalculated on every document load. |
| // Don't use SetRecalcModeOnLoad() which would override |
| // ModeAlways. |
| case ocConvert : |
| pArr->AddRecalcMode( RECALCMODE_ONLOAD ); |
| break; |
| // If the referred cell is moved the value changes. |
| case ocColumn : |
| case ocRow : |
| // ocCell needs recalc on move for some possible type values. |
| case ocCell : |
| pArr->SetRecalcModeOnRefMove(); |
| break; |
| case ocHyperLink : |
| pArr->SetHyperLink(sal_True); |
| break; |
| default: |
| ; // nothing |
| } |
| if (SC_OPCODE_START_NO_PAR <= eOp && eOp < SC_OPCODE_STOP_NO_PAR) |
| { |
| pFacToken = pToken; |
| eOp = NextToken(); |
| if (eOp != ocOpen) |
| { |
| SetError(errPairExpected); |
| PutCode( pFacToken ); |
| } |
| else |
| { |
| eOp = NextToken(); |
| if (eOp != ocClose) |
| SetError(errPairExpected); |
| PutCode(pFacToken); |
| eOp = NextToken(); |
| } |
| } |
| // special cases NOT() and NEG() |
| else if( eOp == ocNot || eOp == ocNeg |
| || (SC_OPCODE_START_1_PAR <= eOp && eOp < SC_OPCODE_STOP_1_PAR) ) |
| { |
| pFacToken = pToken; |
| eOp = NextToken(); |
| if( nNumFmt == NUMBERFORMAT_UNDEFINED && eOp == ocNot ) |
| nNumFmt = NUMBERFORMAT_LOGICAL; |
| if (eOp == ocOpen) |
| { |
| NextToken(); |
| eOp = Expression(); |
| } |
| else |
| SetError(errPairExpected); |
| if (eOp != ocClose) |
| SetError(errPairExpected); |
| else if ( !pArr->GetCodeError() ) |
| pFacToken->SetByte( 1 ); |
| PutCode( pFacToken ); |
| eOp = NextToken(); |
| } |
| else if ((SC_OPCODE_START_2_PAR <= eOp && eOp < SC_OPCODE_STOP_2_PAR) |
| || eOp == ocExternal |
| || eOp == ocMacro |
| || eOp == ocAnd |
| || eOp == ocOr |
| || eOp == ocBad |
| || ( eOp >= ocInternalBegin && eOp <= ocInternalEnd ) |
| || (bCompileForFAP && ((eOp == ocIf) || (eOp == ocChose))) |
| ) |
| { |
| pFacToken = pToken; |
| OpCode eMyLastOp = eOp; |
| eOp = NextToken(); |
| bool bNoParam = false; |
| bool bBadName = false; |
| if (eOp == ocOpen) |
| { |
| eOp = NextToken(); |
| if (eOp == ocClose) |
| bNoParam = true; |
| else |
| eOp = Expression(); |
| } |
| else if (eMyLastOp == ocBad) |
| { |
| // Just a bad name, not an unknown function, no parameters, no |
| // closing expected. |
| bBadName = true; |
| bNoParam = true; |
| } |
| else |
| SetError(errPairExpected); |
| sal_uInt8 nSepCount = 0; |
| if( !bNoParam ) |
| { |
| nSepCount++; |
| while ( (eOp == ocSep) && (!pArr->GetCodeError() || bIgnoreErrors) ) |
| { |
| nSepCount++; |
| NextToken(); |
| eOp = Expression(); |
| } |
| } |
| if (bBadName) |
| ; // nothing, keep current token for return |
| else if (eOp != ocClose) |
| SetError(errPairExpected); |
| else |
| eOp = NextToken(); |
| // Jumps are just normal functions for the FunctionAutoPilot tree view |
| if ( bCompileForFAP && pFacToken->GetType() == svJump ) |
| pFacToken = new FormulaFAPToken( pFacToken->GetOpCode(), nSepCount, pFacToken ); |
| else |
| pFacToken->SetByte( nSepCount ); |
| PutCode( pFacToken ); |
| } |
| else if (eOp == ocIf || eOp == ocChose) |
| { |
| // the PC counters are -1 |
| pFacToken = pToken; |
| if ( eOp == ocIf ) |
| pFacToken->GetJump()[ 0 ] = 3; // if, else, behind |
| else |
| pFacToken->GetJump()[ 0 ] = MAXJUMPCOUNT+1; |
| eOp = NextToken(); |
| if (eOp == ocOpen) |
| { |
| NextToken(); |
| eOp = Expression(); |
| } |
| else |
| SetError(errPairExpected); |
| short nJumpCount = 0; |
| PutCode( pFacToken ); |
| // #36253# during AutoCorrect (since pArr->GetCodeError() is |
| // ignored) an unlimited ocIf would crash because |
| // ScRawToken::Clone() allocates the JumpBuffer according to |
| // nJump[0]*2+2, which is 3*2+2 on ocIf. |
| const short nJumpMax = |
| (pFacToken->GetOpCode() == ocIf ? 3 : MAXJUMPCOUNT); |
| while ( (nJumpCount < (MAXJUMPCOUNT - 1)) && (eOp == ocSep) |
| && (!pArr->GetCodeError() || bIgnoreErrors) ) |
| { |
| if ( ++nJumpCount <= nJumpMax ) |
| pFacToken->GetJump()[nJumpCount] = pc-1; |
| NextToken(); |
| eOp = Expression(); |
| // ocSep or ocClose terminate the subexpression |
| PutCode( pToken ); |
| } |
| if (eOp != ocClose) |
| SetError(errPairExpected); |
| else |
| { |
| eOp = NextToken(); |
| // always limit to nJumpMax, no arbitrary overwrites |
| if ( ++nJumpCount <= nJumpMax ) |
| pFacToken->GetJump()[ nJumpCount ] = pc-1; |
| if ((pFacToken->GetOpCode() == ocIf && (nJumpCount > 3)) || |
| (nJumpCount >= MAXJUMPCOUNT)) |
| SetError(errIllegalParameter); |
| else |
| pFacToken->GetJump()[ 0 ] = nJumpCount; |
| } |
| } |
| else if ( eOp == ocMissing ) |
| { |
| PutCode( pToken ); |
| eOp = NextToken(); |
| } |
| else if ( eOp == ocClose ) |
| { |
| SetError( errParameterExpected ); |
| } |
| else if ( eOp == ocSep ) |
| { // Subsequent ocSep |
| SetError( errParameterExpected ); |
| if ( bAutoCorrect && !pStack ) |
| { |
| aCorrectedSymbol.Erase(); |
| bCorrected = sal_True; |
| } |
| } |
| else if ( eOp == ocExternalRef ) |
| { |
| PutCode(pToken); |
| eOp = NextToken(); |
| } |
| else |
| { |
| SetError( errUnknownToken ); |
| if ( bAutoCorrect && !pStack ) |
| { |
| if ( eOp == ocStop ) |
| { // trailing operator w/o operand |
| xub_StrLen nLen = aCorrectedFormula.Len(); |
| if ( nLen ) |
| aCorrectedFormula.Erase( nLen - 1 ); |
| aCorrectedSymbol.Erase(); |
| bCorrected = sal_True; |
| } |
| } |
| } |
| } |
| } |
| |
| //--------------------------------------------------------------------------- |
| |
| void FormulaCompiler::RangeLine() |
| { |
| Factor(); |
| while (pToken->GetOpCode() == ocRange) |
| { |
| FormulaToken** pCode1 = pCode - 1; |
| FormulaTokenRef p = pToken; |
| NextToken(); |
| Factor(); |
| FormulaToken** pCode2 = pCode - 1; |
| if (!MergeRangeReference( pCode1, pCode2)) |
| PutCode(p); |
| } |
| } |
| |
| //--------------------------------------------------------------------------- |
| |
| void FormulaCompiler::IntersectionLine() |
| { |
| RangeLine(); |
| while (pToken->GetOpCode() == ocIntersect) |
| { |
| FormulaTokenRef p = pToken; |
| NextToken(); |
| RangeLine(); |
| PutCode(p); |
| } |
| } |
| |
| //--------------------------------------------------------------------------- |
| |
| void FormulaCompiler::UnionLine() |
| { |
| IntersectionLine(); |
| while (pToken->GetOpCode() == ocUnion) |
| { |
| FormulaTokenRef p = pToken; |
| NextToken(); |
| IntersectionLine(); |
| PutCode(p); |
| } |
| } |
| |
| //--------------------------------------------------------------------------- |
| |
| void FormulaCompiler::UnaryLine() |
| { |
| if( pToken->GetOpCode() == ocAdd ) |
| GetToken(); |
| else if (SC_OPCODE_START_UN_OP <= pToken->GetOpCode() && |
| pToken->GetOpCode() < SC_OPCODE_STOP_UN_OP) |
| { |
| FormulaTokenRef p = pToken; |
| NextToken(); |
| UnaryLine(); |
| PutCode( p ); |
| } |
| else |
| UnionLine(); |
| } |
| |
| //--------------------------------------------------------------------------- |
| |
| void FormulaCompiler::PostOpLine() |
| { |
| UnaryLine(); |
| while ( pToken->GetOpCode() == ocPercentSign ) |
| { // this operator _follows_ its operand |
| PutCode( pToken ); |
| NextToken(); |
| } |
| } |
| |
| //--------------------------------------------------------------------------- |
| |
| void FormulaCompiler::PowLine() |
| { |
| PostOpLine(); |
| while (pToken->GetOpCode() == ocPow) |
| { |
| FormulaTokenRef p = pToken; |
| NextToken(); |
| PostOpLine(); |
| PutCode(p); |
| } |
| } |
| |
| //--------------------------------------------------------------------------- |
| |
| void FormulaCompiler::MulDivLine() |
| { |
| PowLine(); |
| while (pToken->GetOpCode() == ocMul || pToken->GetOpCode() == ocDiv) |
| { |
| FormulaTokenRef p = pToken; |
| NextToken(); |
| PowLine(); |
| PutCode(p); |
| } |
| } |
| |
| //--------------------------------------------------------------------------- |
| |
| void FormulaCompiler::AddSubLine() |
| { |
| MulDivLine(); |
| while (pToken->GetOpCode() == ocAdd || pToken->GetOpCode() == ocSub) |
| { |
| FormulaTokenRef p = pToken; |
| NextToken(); |
| MulDivLine(); |
| PutCode(p); |
| } |
| } |
| |
| //--------------------------------------------------------------------------- |
| |
| void FormulaCompiler::ConcatLine() |
| { |
| AddSubLine(); |
| while (pToken->GetOpCode() == ocAmpersand) |
| { |
| FormulaTokenRef p = pToken; |
| NextToken(); |
| AddSubLine(); |
| PutCode(p); |
| } |
| } |
| |
| //--------------------------------------------------------------------------- |
| |
| void FormulaCompiler::CompareLine() |
| { |
| ConcatLine(); |
| while (pToken->GetOpCode() >= ocEqual && pToken->GetOpCode() <= ocGreaterEqual) |
| { |
| FormulaTokenRef p = pToken; |
| NextToken(); |
| ConcatLine(); |
| PutCode(p); |
| } |
| } |
| |
| //--------------------------------------------------------------------------- |
| |
| void FormulaCompiler::NotLine() |
| { |
| CompareLine(); |
| while (pToken->GetOpCode() == ocNot) |
| { |
| FormulaTokenRef p = pToken; |
| NextToken(); |
| CompareLine(); |
| PutCode(p); |
| } |
| } |
| |
| //--------------------------------------------------------------------------- |
| |
| OpCode FormulaCompiler::Expression() |
| { |
| static const short nRecursionMax = 42; |
| FormulaCompilerRecursionGuard aRecursionGuard( nRecursion ); |
| if ( nRecursion > nRecursionMax ) |
| { |
| SetError( errStackOverflow ); |
| return ocStop; //! generate token instead? |
| } |
| NotLine(); |
| while (pToken->GetOpCode() == ocAnd || pToken->GetOpCode() == ocOr) |
| { |
| FormulaTokenRef p = pToken; |
| pToken->SetByte( 2 ); // 2 parameters! |
| NextToken(); |
| NotLine(); |
| PutCode(p); |
| } |
| return pToken->GetOpCode(); |
| } |
| // ----------------------------------------------------------------------------- |
| void FormulaCompiler::SetError(sal_uInt16 /*nError*/) |
| { |
| } |
| // ----------------------------------------------------------------------------- |
| FormulaTokenRef FormulaCompiler::ExtendRangeReference( FormulaToken & /*rTok1*/, FormulaToken & /*rTok2*/, bool /*bReuseDoubleRef*/ ) |
| { |
| return FormulaTokenRef(); |
| } |
| // ----------------------------------------------------------------------------- |
| bool FormulaCompiler::MergeRangeReference(FormulaToken * * const pCode1, FormulaToken * const * const pCode2 ) |
| { |
| FormulaToken *p1, *p2; |
| if (pc < 2 || !pCode1 || !pCode2 || |
| (pCode2 - pCode1 != 1) || (pCode - pCode2 != 1) || |
| ((p1 = *pCode1) == 0) || ((p2 = *pCode2) == 0) ) |
| return false; |
| |
| FormulaTokenRef p = ExtendRangeReference( *p1, *p2, true); |
| if (!p) |
| return false; |
| |
| p->IncRef(); |
| p1->DecRef(); |
| p2->DecRef(); |
| *pCode1 = p; |
| --pCode, --pc; |
| pArr->nRefs--; |
| |
| return true; |
| } |
| // ----------------------------------------------------------------------------- |
| sal_Bool FormulaCompiler::CompileTokenArray() |
| { |
| glSubTotal = sal_False; |
| bCorrected = sal_False; |
| if( !pArr->GetCodeError() || bIgnoreErrors ) |
| { |
| if ( bAutoCorrect ) |
| { |
| aCorrectedFormula.Erase(); |
| aCorrectedSymbol.Erase(); |
| } |
| pArr->nRefs = 0; // count from start |
| pArr->DelRPN(); |
| pStack = NULL; |
| FormulaToken* pData[ MAXCODE ]; |
| pCode = pData; |
| sal_Bool bWasForced = pArr->IsRecalcModeForced(); |
| if ( bWasForced ) |
| { |
| if ( bAutoCorrect ) |
| aCorrectedFormula = '='; |
| } |
| pArr->ClearRecalcMode(); |
| pArr->Reset(); |
| eLastOp = ocOpen; |
| pc = 0; |
| NextToken(); |
| OpCode eOp = Expression(); |
| // Some trailing garbage that doesn't form an expression? |
| if (eOp != ocStop) |
| SetError( errOperatorExpected); |
| |
| sal_uInt16 nErrorBeforePop = pArr->GetCodeError(); |
| |
| while( pStack ) |
| PopTokenArray(); |
| if( pc ) |
| { |
| pArr->pRPN = new FormulaToken*[ pc ]; |
| pArr->nRPN = pc; |
| memcpy( pArr->pRPN, pData, pc * sizeof( FormulaToken* ) ); |
| } |
| |
| // once an error, always an error |
| if( !pArr->GetCodeError() && nErrorBeforePop ) |
| pArr->SetCodeError( nErrorBeforePop); |
| |
| if( pArr->GetCodeError() && !bIgnoreErrors ) |
| { |
| pArr->DelRPN(); |
| pArr->SetHyperLink(sal_False); |
| } |
| |
| if ( bWasForced ) |
| pArr->SetRecalcModeForced(); |
| } |
| if( nNumFmt == NUMBERFORMAT_UNDEFINED ) |
| nNumFmt = NUMBERFORMAT_NUMBER; |
| return glSubTotal; |
| } |
| // ----------------------------------------------------------------------------- |
| void FormulaCompiler::PopTokenArray() |
| { |
| if( pStack ) |
| { |
| FormulaArrayStack* p = pStack; |
| pStack = p->pNext; |
| p->pArr->nRefs = sal::static_int_cast<short>( p->pArr->nRefs + pArr->nRefs ); |
| // obtain special RecalcMode from SharedFormula |
| if ( pArr->IsRecalcModeAlways() ) |
| p->pArr->SetRecalcModeAlways(); |
| else if ( !pArr->IsRecalcModeNormal() && p->pArr->IsRecalcModeNormal() ) |
| p->pArr->SetMaskedRecalcMode( pArr->GetRecalcMode() ); |
| p->pArr->SetCombinedBitsRecalcMode( pArr->GetRecalcMode() ); |
| if( p->bTemp ) |
| delete pArr; |
| pArr = p->pArr; |
| delete p; |
| } |
| } |
| // ----------------------------------------------------------------------------- |
| void FormulaCompiler::CreateStringFromTokenArray( String& rFormula ) |
| { |
| rtl::OUStringBuffer aBuffer( pArr->GetLen() * 5 ); |
| CreateStringFromTokenArray( aBuffer ); |
| rFormula = aBuffer; |
| } |
| |
| void FormulaCompiler::CreateStringFromTokenArray( rtl::OUStringBuffer& rBuffer ) |
| { |
| rBuffer.setLength(0); |
| if( !pArr->GetLen() ) |
| return; |
| |
| FormulaTokenArray* pSaveArr = pArr; |
| bool bODFF = FormulaGrammar::isODFF( meGrammar); |
| if (bODFF || FormulaGrammar::isPODF( meGrammar) ) |
| { |
| // Scan token array for missing args and re-write if present. |
| MissingConvention aConv( bODFF); |
| if (pArr->NeedsPofRewrite( aConv)) |
| pArr = pArr->RewriteMissingToPof( aConv); |
| } |
| |
| // At least one character per token, plus some are references, some are |
| // function names, some are numbers, ... |
| rBuffer.ensureCapacity( pArr->GetLen() * 5 ); |
| |
| if ( pArr->IsRecalcModeForced() ) |
| rBuffer.append(sal_Unicode('=')); |
| FormulaToken* t = pArr->First(); |
| while( t ) |
| t = CreateStringFromToken( rBuffer, t, sal_True ); |
| |
| if (pSaveArr != pArr) |
| { |
| delete pArr; |
| pArr = pSaveArr; |
| } |
| } |
| // ----------------------------------------------------------------------------- |
| FormulaToken* FormulaCompiler::CreateStringFromToken( String& rFormula, FormulaToken* pTokenP,sal_Bool bAllowArrAdvance ) |
| { |
| rtl::OUStringBuffer aBuffer; |
| FormulaToken* p = CreateStringFromToken( aBuffer, pTokenP, bAllowArrAdvance ); |
| rFormula += aBuffer; |
| return p; |
| } |
| |
| FormulaToken* FormulaCompiler::CreateStringFromToken( rtl::OUStringBuffer& rBuffer, FormulaToken* pTokenP,sal_Bool bAllowArrAdvance ) |
| { |
| sal_Bool bNext = sal_True; |
| sal_Bool bSpaces = sal_False; |
| FormulaToken* t = pTokenP; |
| OpCode eOp = t->GetOpCode(); |
| if( eOp >= ocAnd && eOp <= ocOr ) |
| { |
| // AND, OR infix? |
| if ( bAllowArrAdvance ) |
| t = pArr->Next(); |
| else |
| t = pArr->PeekNext(); |
| bNext = sal_False; |
| bSpaces = ( !t || t->GetOpCode() != ocOpen ); |
| } |
| if( bSpaces ) |
| rBuffer.append(sal_Unicode(' ')); |
| |
| if( eOp == ocSpaces ) |
| { |
| bool bIntersectionOp = mxSymbols->isODFF(); |
| if (bIntersectionOp) |
| { |
| const FormulaToken* p = pArr->PeekPrevNoSpaces(); |
| bIntersectionOp = (p && p->GetOpCode() == ocColRowName); |
| if (bIntersectionOp) |
| { |
| p = pArr->PeekNextNoSpaces(); |
| bIntersectionOp = (p && p->GetOpCode() == ocColRowName); |
| } |
| } |
| if (bIntersectionOp) |
| rBuffer.appendAscii( "!!"); |
| else |
| { |
| // most times it's just one blank |
| sal_uInt8 n = t->GetByte(); |
| for ( sal_uInt8 j=0; j<n; ++j ) |
| { |
| rBuffer.append(sal_Unicode(' ')); |
| } |
| } |
| } |
| else if( eOp >= ocInternalBegin && eOp <= ocInternalEnd ) |
| rBuffer.appendAscii( pInternal[ eOp - ocInternalBegin ] ); |
| else if( (sal_uInt16) eOp < mxSymbols->getSymbolCount()) // Keyword: |
| rBuffer.append(mxSymbols->getSymbol(eOp)); |
| else |
| { |
| DBG_ERRORFILE("unknown OpCode"); |
| rBuffer.append(GetNativeSymbol( ocErrName )); |
| } |
| if( bNext ) |
| { |
| if (eOp == ocExternalRef) |
| { |
| CreateStringFromExternal(rBuffer, pTokenP); |
| } |
| else |
| { |
| switch( t->GetType() ) |
| { |
| case svDouble: |
| AppendDouble( rBuffer, t->GetDouble() ); |
| break; |
| |
| case svString: |
| if( eOp == ocBad ) |
| rBuffer.append(t->GetString()); |
| else |
| AppendString( rBuffer, t->GetString() ); |
| break; |
| case svSingleRef: |
| CreateStringFromSingleRef(rBuffer,t); |
| break; |
| case svDoubleRef: |
| CreateStringFromDoubleRef(rBuffer,t); |
| break; |
| case svMatrix: |
| CreateStringFromMatrix( rBuffer, t ); |
| break; |
| |
| case svIndex: |
| CreateStringFromIndex( rBuffer, t ); |
| break; |
| case svExternal: |
| { |
| // mapped or translated name of AddIns |
| String aAddIn( t->GetExternal() ); |
| bool bMapped = mxSymbols->isPODF(); // ODF 1.1 directly uses programmatical name |
| if (!bMapped && mxSymbols->hasExternals()) |
| { |
| ExternalHashMap::const_iterator iLook = mxSymbols->getReverseExternalHashMap()->find( aAddIn); |
| if (iLook != mxSymbols->getReverseExternalHashMap()->end()) |
| { |
| aAddIn = (*iLook).second; |
| bMapped = true; |
| } |
| } |
| if (!bMapped && !mxSymbols->isEnglish()) |
| LocalizeString( aAddIn ); |
| rBuffer.append(aAddIn); |
| } |
| break; |
| case svByte: |
| case svJump: |
| case svFAP: |
| case svMissing: |
| case svSep: |
| break; // Opcodes |
| default: |
| DBG_ERROR("FormulaCompiler:: GetStringFromToken errUnknownVariable"); |
| } // of switch |
| } |
| } |
| if( bSpaces ) |
| rBuffer.append(sal_Unicode(' ')); |
| if ( bAllowArrAdvance ) |
| { |
| if( bNext ) |
| t = pArr->Next(); |
| return t; |
| } |
| return pTokenP; |
| } |
| // ----------------------------------------------------------------------------- |
| |
| void FormulaCompiler::AppendDouble( rtl::OUStringBuffer& rBuffer, double fVal ) |
| { |
| if ( mxSymbols->isEnglish() ) |
| { |
| ::rtl::math::doubleToUStringBuffer( rBuffer, fVal, |
| rtl_math_StringFormat_Automatic, |
| rtl_math_DecimalPlaces_Max, '.', sal_True ); |
| } |
| else |
| { |
| SvtSysLocale aSysLocale; |
| ::rtl::math::doubleToUStringBuffer( rBuffer, fVal, |
| rtl_math_StringFormat_Automatic, |
| rtl_math_DecimalPlaces_Max, |
| aSysLocale.GetLocaleDataPtr()->getNumDecimalSep().GetChar(0), |
| sal_True ); |
| } |
| } |
| // ----------------------------------------------------------------------------- |
| void FormulaCompiler::AppendBoolean( rtl::OUStringBuffer& rBuffer, bool bVal ) |
| { |
| rBuffer.append( mxSymbols->getSymbol(static_cast<OpCode>(bVal ? ocTrue : ocFalse)) ); |
| } |
| // ----------------------------------------------------------------------------- |
| sal_Bool FormulaCompiler::IsImportingXML() const |
| { |
| return sal_False; |
| } |
| // ----------------------------------------------------------------------------- |
| void FormulaCompiler::AppendString( rtl::OUStringBuffer& rBuffer, const String & rStr ) |
| { |
| if (IsImportingXML()) |
| rBuffer.append( rStr ); |
| else |
| { |
| rBuffer.append(sal_Unicode('"')); |
| if ( lcl_UnicodeStrChr( rStr.GetBuffer(), '"' ) == NULL ) |
| rBuffer.append( rStr ); |
| else |
| { |
| String aStr( rStr ); |
| aStr.SearchAndReplaceAll( '"', String( RTL_CONSTASCII_USTRINGPARAM( "\"\""))); |
| rBuffer.append(aStr); |
| } |
| rBuffer.append(sal_Unicode('"')); |
| } |
| } |
| // ----------------------------------------------------------------------------- |
| OpCode FormulaCompiler::NextToken() |
| { |
| if( !GetToken() ) |
| return ocStop; |
| OpCode eOp = pToken->GetOpCode(); |
| // There must be an operator before a push |
| if ( (eOp == ocPush || eOp == ocColRowNameAuto) && |
| !( (eLastOp == ocOpen) || (eLastOp == ocSep) || |
| (SC_OPCODE_START_BIN_OP <= eLastOp && eLastOp < SC_OPCODE_STOP_UN_OP)) ) |
| SetError(errOperatorExpected); |
| // Operator and Plus => operator |
| if (eOp == ocAdd && (eLastOp == ocOpen || eLastOp == ocSep || |
| (SC_OPCODE_START_BIN_OP <= eLastOp && eLastOp < SC_OPCODE_STOP_UN_OP))) |
| eOp = NextToken(); |
| else |
| { |
| // Before an operator there must not be another operator, with the |
| // exception of AND and OR. |
| if ( eOp != ocAnd && eOp != ocOr && |
| (SC_OPCODE_START_BIN_OP <= eOp && eOp < SC_OPCODE_STOP_BIN_OP ) |
| && (eLastOp == ocOpen || eLastOp == ocSep || |
| (SC_OPCODE_START_BIN_OP <= eLastOp && eLastOp < SC_OPCODE_STOP_UN_OP))) |
| { |
| SetError(errVariableExpected); |
| if ( bAutoCorrect && !pStack ) |
| { |
| if ( eOp == eLastOp || eLastOp == ocOpen ) |
| { // throw away duplicated operator |
| aCorrectedSymbol.Erase(); |
| bCorrected = sal_True; |
| } |
| else |
| { |
| xub_StrLen nPos = aCorrectedFormula.Len(); |
| if ( nPos ) |
| { |
| nPos--; |
| sal_Unicode c = aCorrectedFormula.GetChar( nPos ); |
| switch ( eOp ) |
| { // swap operators |
| case ocGreater: |
| if ( c == mxSymbols->getSymbol(ocEqual).GetChar(0) ) |
| { // >= instead of => |
| aCorrectedFormula.SetChar( nPos, |
| mxSymbols->getSymbol(ocGreater).GetChar(0) ); |
| aCorrectedSymbol = c; |
| bCorrected = sal_True; |
| } |
| break; |
| case ocLess: |
| if ( c == mxSymbols->getSymbol(ocEqual).GetChar(0) ) |
| { // <= instead of =< |
| aCorrectedFormula.SetChar( nPos, |
| mxSymbols->getSymbol(ocLess).GetChar(0) ); |
| aCorrectedSymbol = c; |
| bCorrected = sal_True; |
| } |
| else if ( c == mxSymbols->getSymbol(ocGreater).GetChar(0) ) |
| { // <> instead of >< |
| aCorrectedFormula.SetChar( nPos, |
| mxSymbols->getSymbol(ocLess).GetChar(0) ); |
| aCorrectedSymbol = c; |
| bCorrected = sal_True; |
| } |
| break; |
| case ocMul: |
| if ( c == mxSymbols->getSymbol(ocSub).GetChar(0) ) |
| { // *- instead of -* |
| aCorrectedFormula.SetChar( nPos, |
| mxSymbols->getSymbol(ocMul).GetChar(0) ); |
| aCorrectedSymbol = c; |
| bCorrected = sal_True; |
| } |
| break; |
| case ocDiv: |
| if ( c == mxSymbols->getSymbol(ocSub).GetChar(0) ) |
| { // /- instead of -/ |
| aCorrectedFormula.SetChar( nPos, |
| mxSymbols->getSymbol(ocDiv).GetChar(0) ); |
| aCorrectedSymbol = c; |
| bCorrected = sal_True; |
| } |
| break; |
| default: |
| ; // nothing |
| } |
| } |
| } |
| } |
| } |
| eLastOp = eOp; |
| } |
| return eOp; |
| } |
| void FormulaCompiler::PutCode( FormulaTokenRef& p ) |
| { |
| if( pc >= MAXCODE-1 ) |
| { |
| if ( pc == MAXCODE-1 ) |
| { |
| p = new FormulaByteToken( ocStop ); |
| p->IncRef(); |
| *pCode++ = p; |
| ++pc; |
| } |
| SetError(errCodeOverflow); |
| return; |
| } |
| if( pArr->GetCodeError() && !bCompileForFAP ) |
| return; |
| ForceArrayOperator( p, pCurrentFactorToken); |
| p->IncRef(); |
| *pCode++ = p; |
| pc++; |
| } |
| |
| // ----------------------------------------------------------------------------- |
| sal_Bool FormulaCompiler::HandleExternalReference(const FormulaToken& /*_aToken*/) |
| { |
| return sal_True; |
| } |
| // ----------------------------------------------------------------------------- |
| sal_Bool FormulaCompiler::HandleRange() |
| { |
| return sal_True; |
| } |
| // ----------------------------------------------------------------------------- |
| sal_Bool FormulaCompiler::HandleSingleRef() |
| { |
| return sal_True; |
| } |
| // ----------------------------------------------------------------------------- |
| sal_Bool FormulaCompiler::HandleDbData() |
| { |
| return sal_True; |
| } |
| // ----------------------------------------------------------------------------- |
| void FormulaCompiler::CreateStringFromSingleRef(rtl::OUStringBuffer& /*rBuffer*/,FormulaToken* /*pTokenP*/) |
| { |
| } |
| // ----------------------------------------------------------------------------- |
| void FormulaCompiler::CreateStringFromDoubleRef(rtl::OUStringBuffer& /*rBuffer*/,FormulaToken* /*pTokenP*/) |
| { |
| } |
| // ----------------------------------------------------------------------------- |
| void FormulaCompiler::CreateStringFromIndex(rtl::OUStringBuffer& /*rBuffer*/,FormulaToken* /*pTokenP*/) |
| { |
| } |
| // ----------------------------------------------------------------------------- |
| void FormulaCompiler::CreateStringFromMatrix(rtl::OUStringBuffer& /*rBuffer*/,FormulaToken* /*pTokenP*/) |
| { |
| } |
| // ----------------------------------------------------------------------------- |
| void FormulaCompiler::CreateStringFromExternal(rtl::OUStringBuffer& /*rBuffer*/,FormulaToken* /*pTokenP*/) |
| { |
| } |
| // ----------------------------------------------------------------------------- |
| void FormulaCompiler::LocalizeString( String& /*rName*/ ) |
| { |
| } |
| void FormulaCompiler::PushTokenArray( FormulaTokenArray* pa, sal_Bool bTemp ) |
| { |
| if ( bAutoCorrect && !pStack ) |
| { // #61426# don't merge stacked subroutine code into entered formula |
| aCorrectedFormula += aCorrectedSymbol; |
| aCorrectedSymbol.Erase(); |
| } |
| FormulaArrayStack* p = new FormulaArrayStack; |
| p->pNext = pStack; |
| p->pArr = pArr; |
| p->bTemp = bTemp; |
| pStack = p; |
| pArr = pa; |
| } |
| |
| // ============================================================================= |
| } // formula |
| // ============================================================================= |