LCOV - code coverage report
Current view: top level - usr/local/src/libreoffice/formula/source/core/api - FormulaCompiler.cxx (source / functions) Hit Total Coverage
Test: libreoffice_filtered.info Lines: 677 1018 66.5 %
Date: 2013-07-09 Functions: 61 87 70.1 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2             : /*
       3             :  * This file is part of the LibreOffice project.
       4             :  *
       5             :  * This Source Code Form is subject to the terms of the Mozilla Public
       6             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       7             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
       8             :  *
       9             :  * This file incorporates work covered by the following license notice:
      10             :  *
      11             :  *   Licensed to the Apache Software Foundation (ASF) under one or more
      12             :  *   contributor license agreements. See the NOTICE file distributed
      13             :  *   with this work for additional information regarding copyright
      14             :  *   ownership. The ASF licenses this file to you under the Apache
      15             :  *   License, Version 2.0 (the "License"); you may not use this file
      16             :  *   except in compliance with the License. You may obtain a copy of
      17             :  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
      18             :  */
      19             : #include <sal/macros.h>
      20             : #include "formula/FormulaCompiler.hxx"
      21             : #include "formula/errorcodes.hxx"
      22             : #include "formula/token.hxx"
      23             : #include "formula/tokenarray.hxx"
      24             : #include "core_resource.hxx"
      25             : #include "core_resource.hrc"
      26             : 
      27             : #include <svl/zforlist.hxx>
      28             : #include <tools/rc.hxx>
      29             : #include <tools/rcid.h>
      30             : #include <com/sun/star/sheet/FormulaOpCodeMapEntry.hpp>
      31             : #include <com/sun/star/sheet/FormulaMapGroup.hpp>
      32             : #include <com/sun/star/sheet/FormulaMapGroupSpecialOffset.hpp>
      33             : #include <rtl/strbuf.hxx>
      34             : #include <stdio.h>
      35             : 
      36             : // =============================================================================
      37             : namespace formula
      38             : {
      39             : // =============================================================================
      40             :     using namespace ::com::sun::star;
      41             : 
      42             :     static const sal_Char* pInternal[ 1 ] = { "TTT" };
      43             : 
      44             : // =============================================================================
      45             : namespace
      46             : {
      47             : // =============================================================================
      48             : class FormulaCompilerRecursionGuard
      49             : {
      50             : private:
      51             :             short&              rRecursion;
      52             : public:
      53       54159 :                                 FormulaCompilerRecursionGuard( short& rRec )
      54       54159 :                                     : rRecursion( rRec ) { ++rRecursion; }
      55       54159 :                                 ~FormulaCompilerRecursionGuard() { --rRecursion; }
      56             : };
      57             : 
      58        1965 : short lcl_GetRetFormat( OpCode eOpCode )
      59             : {
      60        1965 :     switch (eOpCode)
      61             :     {
      62             :         case ocEqual:
      63             :         case ocNotEqual:
      64             :         case ocLess:
      65             :         case ocGreater:
      66             :         case ocLessEqual:
      67             :         case ocGreaterEqual:
      68             :         case ocAnd:
      69             :         case ocOr:
      70             :         case ocXor:
      71             :         case ocNot:
      72             :         case ocTrue:
      73             :         case ocFalse:
      74             :         case ocIsEmpty:
      75             :         case ocIsString:
      76             :         case ocIsNonString:
      77             :         case ocIsLogical:
      78             :         case ocIsRef:
      79             :         case ocIsValue:
      80             :         case ocIsFormula:
      81             :         case ocIsNA:
      82             :         case ocIsErr:
      83             :         case ocIsError:
      84             :         case ocIsEven:
      85             :         case ocIsOdd:
      86             :         case ocExact:
      87         135 :             return NUMBERFORMAT_LOGICAL;
      88             :         case ocGetActDate:
      89             :         case ocGetDate:
      90             :         case ocEasterSunday :
      91           0 :             return NUMBERFORMAT_DATE;
      92             :         case ocGetActTime:
      93           3 :             return NUMBERFORMAT_DATETIME;
      94             :         case ocGetTime:
      95           0 :             return NUMBERFORMAT_TIME;
      96             :         case ocNPV:
      97             :         case ocBW:
      98             :         case ocDIA:
      99             :         case ocGDA:
     100             :         case ocGDA2:
     101             :         case ocVBD:
     102             :         case ocLIA:
     103             :         case ocRMZ:
     104             :         case ocZW:
     105             :         case ocZinsZ:
     106             :         case ocKapz:
     107             :         case ocKumZinsZ:
     108             :         case ocKumKapZ:
     109           0 :             return NUMBERFORMAT_CURRENCY;
     110             :         case ocZins:
     111             :         case ocIRR:
     112             :         case ocMIRR:
     113             :         case ocZGZ:
     114             :         case ocEffektiv:
     115             :         case ocNominal:
     116             :         case ocPercentSign:
     117           0 :             return NUMBERFORMAT_PERCENT;
     118             :         default:
     119        1827 :             return NUMBERFORMAT_NUMBER;
     120             :     }
     121             : }
     122             : 
     123        8856 : inline void lclPushOpCodeMapEntry( ::std::vector< sheet::FormulaOpCodeMapEntry >& rVec, const String* pTable, sal_uInt16 nOpCode )
     124             : {
     125        8856 :     sheet::FormulaOpCodeMapEntry aEntry;
     126        8856 :     aEntry.Token.OpCode = nOpCode;
     127        8856 :     aEntry.Name = pTable[nOpCode];
     128        8856 :     rVec.push_back( aEntry);
     129        8856 : }
     130             : 
     131          54 : void lclPushOpCodeMapEntries( ::std::vector< sheet::FormulaOpCodeMapEntry >& rVec, const String* pTable, sal_uInt16 nOpCodeBeg, sal_uInt16 nOpCodeEnd )
     132             : {
     133        2484 :     for (sal_uInt16 nOpCode = nOpCodeBeg; nOpCode < nOpCodeEnd; ++nOpCode)
     134        2430 :         lclPushOpCodeMapEntry( rVec, pTable, nOpCode );
     135          54 : }
     136             : 
     137          81 : void lclPushOpCodeMapEntries( ::std::vector< sheet::FormulaOpCodeMapEntry >& rVec, const String* pTable, const sal_uInt16* pnOpCodes, size_t nCount )
     138             : {
     139         486 :     for (const sal_uInt16* pnEnd = pnOpCodes + nCount; pnOpCodes < pnEnd; ++pnOpCodes)
     140         405 :         lclPushOpCodeMapEntry( rVec, pTable, *pnOpCodes );
     141          81 : }
     142             : 
     143         347 : class OpCodeList : public Resource        // temp object for resource
     144             : {
     145             : public:
     146             : 
     147             :     OpCodeList( sal_uInt16, FormulaCompiler::NonConstOpCodeMapPtr );
     148             : 
     149             : private:
     150             :     bool getOpCodeString( String& rStr, sal_uInt16 nOp );
     151             :     void putDefaultOpCode( FormulaCompiler::NonConstOpCodeMapPtr xMap, sal_uInt16 nOp );
     152             : 
     153             : private:
     154             :     enum SeparatorType
     155             :     {
     156             :         SEMICOLON_BASE,
     157             :         COMMA_BASE
     158             :     };
     159             :     SeparatorType meSepType;
     160             : };
     161             : 
     162         347 : OpCodeList::OpCodeList( sal_uInt16 nRID, FormulaCompiler::NonConstOpCodeMapPtr xMap ) :
     163         347 :     Resource( ResId(nRID,*ResourceManager::getResManager()) )
     164         347 :     ,meSepType(SEMICOLON_BASE)
     165             : {
     166      144699 :      for (sal_uInt16 i = 0; i <= SC_OPCODE_LAST_OPCODE_ID; ++i)
     167             :     {
     168      144352 :         String aOpStr;
     169      144352 :         if ( getOpCodeString(aOpStr, i) )
     170        1041 :             xMap->putOpCode(aOpStr, OpCode(i));
     171             :         else
     172      143311 :             putDefaultOpCode(xMap, i);
     173      144352 :     }
     174             : 
     175         347 :     FreeResource();
     176         347 : }
     177             : 
     178      144352 : bool OpCodeList::getOpCodeString( String& rStr, sal_uInt16 nOp )
     179             : {
     180      144352 :     switch (nOp)
     181             :     {
     182             :         case SC_OPCODE_SEP:
     183             :         {
     184         347 :             if (meSepType == COMMA_BASE)
     185             :             {
     186           0 :                 rStr = OUString(",");
     187           0 :                 return true;
     188             :             }
     189         347 :             else if (meSepType == SEMICOLON_BASE)
     190             :             {
     191         347 :                 rStr = OUString(";");
     192         347 :                 return true;
     193             :             }
     194             :         }
     195           0 :         break;
     196             :         case SC_OPCODE_ARRAY_COL_SEP:
     197             :         {
     198         347 :             if (meSepType == COMMA_BASE)
     199             :             {
     200           0 :                 rStr = OUString(",");
     201           0 :                 return true;
     202             :             }
     203         347 :             else if (meSepType == SEMICOLON_BASE)
     204             :             {
     205         347 :                 rStr = OUString(";");
     206         347 :                 return true;
     207             :             }
     208             :         }
     209           0 :         break;
     210             :         case SC_OPCODE_ARRAY_ROW_SEP:
     211             :         {
     212         347 :             if (meSepType == COMMA_BASE)
     213             :             {
     214           0 :                 rStr = OUString(";");
     215           0 :                 return true;
     216             :             }
     217         347 :             else if (meSepType == SEMICOLON_BASE)
     218             :             {
     219         347 :                 rStr = OUString("|");
     220         347 :                 return true;
     221             :             }
     222             :         }
     223           0 :         break;
     224             :     }
     225             : 
     226      143311 :     return false;
     227             : }
     228             : 
     229      143311 : void OpCodeList::putDefaultOpCode( FormulaCompiler::NonConstOpCodeMapPtr xMap, sal_uInt16 nOp )
     230             : {
     231      143311 :     ResId aRes(nOp,*ResourceManager::getResManager());
     232      143311 :     aRes.SetRT(RSC_STRING);
     233      143311 :     if (IsAvailableRes(aRes))
     234      116566 :         xMap->putOpCode(aRes, OpCode(nOp));
     235      143311 : }
     236             : // -----------------------------------------------------------------------------
     237             : // static
     238          42 : const sal_Unicode* lcl_UnicodeStrChr( const sal_Unicode* pStr,sal_Unicode c )
     239             : {
     240          42 :     if ( !pStr )
     241           0 :         return NULL;
     242         150 :     while ( *pStr )
     243             :     {
     244          66 :         if ( *pStr == c )
     245           0 :             return pStr;
     246          66 :         pStr++;
     247             :     }
     248          42 :     return NULL;
     249             : }
     250             : // =============================================================================
     251             : } // empty
     252             : // =============================================================================
     253             : 
     254        6324 : void FormulaCompiler::OpCodeMap::putExternal( const String & rSymbol, const String & rAddIn )
     255             : {
     256        6324 :     bool bOk = mpExternalHashMap->insert( ExternalHashMap::value_type( rSymbol, rAddIn)).second;
     257        6324 :     if (bOk)
     258        6324 :         bOk = mpReverseExternalHashMap->insert( ExternalHashMap::value_type( rAddIn, rSymbol)).second;
     259             :     DBG_ASSERT( bOk, "OpCodeMap::putExternal: symbol not inserted");
     260        6324 : }
     261             : 
     262        1550 : void FormulaCompiler::OpCodeMap::putExternalSoftly( const String & rSymbol, const String & rAddIn )
     263             : {
     264        1550 :     bool bOk = mpReverseExternalHashMap->insert( ExternalHashMap::value_type( rAddIn, rSymbol)).second;
     265        1550 :     if (bOk)
     266          24 :         mpExternalHashMap->insert( ExternalHashMap::value_type( rSymbol, rAddIn));
     267        1550 : }
     268           0 : uno::Sequence< sheet::FormulaToken > FormulaCompiler::OpCodeMap::createSequenceOfFormulaTokens(const FormulaCompiler& _rCompiler,const uno::Sequence< OUString >& rNames ) const
     269             : {
     270           0 :     const sal_Int32 nLen = rNames.getLength();
     271           0 :     uno::Sequence< sheet::FormulaToken > aTokens( nLen);
     272           0 :     sheet::FormulaToken* pToken = aTokens.getArray();
     273           0 :     OUString const * pName = rNames.getConstArray();
     274           0 :     OUString const * const pStop = pName + nLen;
     275           0 :     for ( ; pName < pStop; ++pName, ++pToken)
     276             :     {
     277           0 :         OpCodeHashMap::const_iterator iLook( mpHashMap->find( *pName));
     278           0 :         if (iLook != mpHashMap->end())
     279           0 :             pToken->OpCode = (*iLook).second;
     280             :         else
     281             :         {
     282           0 :             OUString aIntName;
     283           0 :             if (hasExternals())
     284             :             {
     285           0 :                 ExternalHashMap::const_iterator iExt( mpExternalHashMap->find( *pName));
     286           0 :                 if (iExt != mpExternalHashMap->end())
     287           0 :                     aIntName = (*iExt).second;
     288             :                 // Check for existence not needed here, only name-mapping is of
     289             :                 // interest.
     290             :             }
     291           0 :             if (aIntName.isEmpty())
     292           0 :                 aIntName = _rCompiler.FindAddInFunction(*pName, !isEnglish());    // bLocalFirst=false for english
     293           0 :             if (aIntName.isEmpty())
     294           0 :                 pToken->OpCode = getOpCodeUnknown();
     295             :             else
     296             :             {
     297           0 :                 pToken->OpCode = ocExternal;
     298           0 :                 pToken->Data <<= aIntName;
     299           0 :             }
     300             :         }
     301             :     }
     302           0 :     return aTokens;
     303             : }
     304         162 : uno::Sequence< sheet::FormulaOpCodeMapEntry > FormulaCompiler::OpCodeMap::createSequenceOfAvailableMappings(const FormulaCompiler& _rCompiler,const sal_Int32 nGroups ) const
     305             : {
     306             :     using namespace sheet;
     307             : 
     308             :     // Unfortunately uno::Sequence can't grow without cumbersome reallocs. As
     309             :     // we don't know in advance how many elements it will have we use a
     310             :     // temporary vector to add elements and then copy to Sequence :-(
     311         162 :     ::std::vector< FormulaOpCodeMapEntry > aVec;
     312             : 
     313         162 :     if (nGroups == FormulaMapGroup::SPECIAL)
     314             :     {
     315             :         // Use specific order, keep in sync with
     316             :         // offapi/com/sun/star/sheet/FormulaMapGroupSpecialOffset.idl
     317             :         static const struct
     318             :         {
     319             :             sal_Int32 nOff;
     320             :             OpCode    eOp;
     321             :         } aMap[] = {
     322             :             { FormulaMapGroupSpecialOffset::PUSH              , ocPush }           ,
     323             :             { FormulaMapGroupSpecialOffset::CALL              , ocCall }           ,
     324             :             { FormulaMapGroupSpecialOffset::STOP              , ocStop }           ,
     325             :             { FormulaMapGroupSpecialOffset::EXTERNAL          , ocExternal }       ,
     326             :             { FormulaMapGroupSpecialOffset::NAME              , ocName }           ,
     327             :             { FormulaMapGroupSpecialOffset::NO_NAME           , ocNoName }         ,
     328             :             { FormulaMapGroupSpecialOffset::MISSING           , ocMissing }        ,
     329             :             { FormulaMapGroupSpecialOffset::BAD               , ocBad }            ,
     330             :             { FormulaMapGroupSpecialOffset::SPACES            , ocSpaces }         ,
     331             :             { FormulaMapGroupSpecialOffset::MAT_REF           , ocMatRef }         ,
     332             :             { FormulaMapGroupSpecialOffset::DB_AREA           , ocDBArea }         ,
     333             :             { FormulaMapGroupSpecialOffset::MACRO             , ocMacro }          ,
     334             :             { FormulaMapGroupSpecialOffset::COL_ROW_NAME      , ocColRowName }
     335             :         };
     336          27 :         const size_t nCount = sizeof(aMap)/sizeof(aMap[0]);
     337             :         // Preallocate vector elements.
     338          27 :         if (aVec.size() < nCount)
     339             :         {
     340          27 :             FormulaOpCodeMapEntry aEntry;
     341          27 :             aEntry.Token.OpCode = getOpCodeUnknown();
     342          27 :             aVec.resize( nCount, aEntry);
     343             :         } // if (aVec.size() < nCount)
     344             : 
     345          27 :         FormulaOpCodeMapEntry aEntry;
     346         378 :         for (size_t i=0; i < nCount; ++i)
     347             :         {
     348         351 :             size_t nIndex = static_cast< size_t >( aMap[i].nOff );
     349         351 :             if (aVec.size() <= nIndex)
     350             :             {
     351             :                 // The offsets really should be aligned with the size, so if
     352             :                 // the vector was preallocated above this code to resize it is
     353             :                 // just a measure in case the table isn't in sync with the API,
     354             :                 // usually it isn't executed.
     355           0 :                 aEntry.Token.OpCode = getOpCodeUnknown();
     356           0 :                 aVec.resize( nIndex + 1, aEntry );
     357             :             }
     358         351 :             aEntry.Token.OpCode = aMap[i].eOp;
     359         351 :             aVec[nIndex] = aEntry;
     360          27 :         }
     361             :     }
     362             :     else
     363             :     {
     364             :         /* FIXME: Once we support error constants in formulas we'll need a map
     365             :          * group for that, e.g. FormulaMapGroup::ERROR_CONSTANTS, and fill
     366             :          * SC_OPCODE_START_ERRORS to SC_OPCODE_STOP_ERRORS. */
     367             : 
     368             :         // Anything else but SPECIAL.
     369         135 :         if ((nGroups & FormulaMapGroup::SEPARATORS) != 0)
     370             :         {
     371             :             static const sal_uInt16 aOpCodes[] = {
     372             :                 SC_OPCODE_OPEN,
     373             :                 SC_OPCODE_CLOSE,
     374             :                 SC_OPCODE_SEP,
     375             :             };
     376          27 :             lclPushOpCodeMapEntries( aVec, mpTable, aOpCodes, sizeof(aOpCodes)/sizeof(aOpCodes[0]) );
     377             :         }
     378         135 :         if ((nGroups & FormulaMapGroup::ARRAY_SEPARATORS) != 0)
     379             :         {
     380             :             static const sal_uInt16 aOpCodes[] = {
     381             :                 SC_OPCODE_ARRAY_OPEN,
     382             :                 SC_OPCODE_ARRAY_CLOSE,
     383             :                 SC_OPCODE_ARRAY_ROW_SEP,
     384             :                 SC_OPCODE_ARRAY_COL_SEP
     385             :             };
     386          27 :             lclPushOpCodeMapEntries( aVec, mpTable, aOpCodes, sizeof(aOpCodes)/sizeof(aOpCodes[0]) );
     387             :         }
     388         135 :         if ((nGroups & FormulaMapGroup::UNARY_OPERATORS) != 0)
     389             :         {
     390             :             // Due to the nature of the percent operator following its operand
     391             :             // it isn't sorted into unary operators for compiler interna.
     392          27 :             lclPushOpCodeMapEntry( aVec, mpTable, ocPercentSign );
     393             :             // "+" can be used as unary operator too, push only if binary group is not set
     394          27 :             if ((nGroups & FormulaMapGroup::BINARY_OPERATORS) == 0)
     395          27 :                 lclPushOpCodeMapEntry( aVec, mpTable, ocAdd );
     396             :             // regular unary operators
     397         108 :             for (sal_uInt16 nOp = SC_OPCODE_START_UN_OP; nOp < SC_OPCODE_STOP_UN_OP && nOp < mnSymbols; ++nOp)
     398             :             {
     399          81 :                 switch (nOp)
     400             :                 {
     401             :                     // NOT and NEG in fact are functions but for legacy reasons
     402             :                     // are sorted into unary operators for compiler interna.
     403             :                     case SC_OPCODE_NOT :
     404             :                     case SC_OPCODE_NEG :
     405          54 :                         break;   // nothing,
     406             :                     default:
     407          27 :                         lclPushOpCodeMapEntry( aVec, mpTable, nOp );
     408             :                 }
     409             :             }
     410             :         }
     411         135 :         if ((nGroups & FormulaMapGroup::BINARY_OPERATORS) != 0)
     412             :         {
     413         486 :             for (sal_uInt16 nOp = SC_OPCODE_START_BIN_OP; nOp < SC_OPCODE_STOP_BIN_OP && nOp < mnSymbols; ++nOp)
     414             :             {
     415         459 :                 switch (nOp)
     416             :                 {
     417             :                     // AND and OR in fact are functions but for legacy reasons
     418             :                     // are sorted into binary operators for compiler interna.
     419             :                     case SC_OPCODE_AND :
     420             :                     case SC_OPCODE_OR :
     421          54 :                         break;   // nothing,
     422             :                     default:
     423         405 :                         lclPushOpCodeMapEntry( aVec, mpTable, nOp );
     424             :                 }
     425             :             }
     426             :         }
     427         135 :         if ((nGroups & FormulaMapGroup::FUNCTIONS) != 0)
     428             :         {
     429             :             // Function names are not consecutive, skip the gaps between
     430             :             // functions with no parameter, functions with 1 parameter
     431          27 :             lclPushOpCodeMapEntries( aVec, mpTable, SC_OPCODE_START_NO_PAR, ::std::min< sal_uInt16 >( SC_OPCODE_STOP_NO_PAR, mnSymbols ) );
     432          27 :             lclPushOpCodeMapEntries( aVec, mpTable, SC_OPCODE_START_1_PAR, ::std::min< sal_uInt16 >( SC_OPCODE_STOP_1_PAR, mnSymbols ) );
     433             :             // Additional functions not within range of functions.
     434             :             static const sal_uInt16 aOpCodes[] = {
     435             :                 SC_OPCODE_IF,
     436             :                 SC_OPCODE_IF_ERROR,
     437             :                 SC_OPCODE_IF_NA,
     438             :                 SC_OPCODE_CHOSE,
     439             :                 SC_OPCODE_AND,
     440             :                 SC_OPCODE_OR,
     441             :                 SC_OPCODE_NOT,
     442             :                 SC_OPCODE_NEG
     443             :             };
     444          27 :             lclPushOpCodeMapEntries( aVec, mpTable, aOpCodes, sizeof(aOpCodes)/sizeof(aOpCodes[0]) );
     445             :             // functions with 2 or more parameters.
     446        5589 :             for (sal_uInt16 nOp = SC_OPCODE_START_2_PAR; nOp < SC_OPCODE_STOP_2_PAR && nOp < mnSymbols; ++nOp)
     447             :             {
     448        5562 :                 switch (nOp)
     449             :                 {
     450             :                     // NO_NAME is in SPECIAL.
     451             :                     case SC_OPCODE_NO_NAME :
     452          27 :                         break;   // nothing,
     453             :                     default:
     454        5535 :                         lclPushOpCodeMapEntry( aVec, mpTable, nOp );
     455             :                 }
     456             :             }
     457             :             // If AddIn functions are present in this mapping, use them, and only those.
     458          27 :             if (hasExternals())
     459             :             {
     460        2970 :                 for (ExternalHashMap::const_iterator it( mpExternalHashMap->begin());it != mpExternalHashMap->end(); ++it)
     461             :                 {
     462        2943 :                     FormulaOpCodeMapEntry aEntry;
     463        2943 :                     aEntry.Name = (*it).first;
     464        2943 :                     aEntry.Token.Data <<= OUString( (*it).second);
     465        2943 :                     aEntry.Token.OpCode = ocExternal;
     466        2943 :                     aVec.push_back( aEntry);
     467        2943 :                 }
     468             :             }
     469             :             else
     470             :             {
     471           0 :                 _rCompiler.fillAddInToken(aVec,isEnglish());
     472             :             }
     473             :         }
     474             :     }
     475         162 :     const FormulaOpCodeMapEntry* pRet = aVec.empty() ? 0 : &aVec[0];
     476         162 :     return uno::Sequence< FormulaOpCodeMapEntry >(pRet, aVec.size());
     477             : }
     478             : //-----------------------------------------------------------------------------
     479             : 
     480      127279 : void FormulaCompiler::OpCodeMap::putOpCode( const String & rStr, const OpCode eOp )
     481             : {
     482             :     DBG_ASSERT( 0 < eOp && sal_uInt16(eOp) < mnSymbols, "OpCodeMap::putOpCode: OpCode out of range");
     483      127279 :     if (0 < eOp && sal_uInt16(eOp) < mnSymbols)
     484             :     {
     485             :         DBG_ASSERT( (mpTable[eOp].Len() == 0) || (mpTable[eOp] == rStr) ||
     486             :             (eOp == ocCurrency) || (eOp == ocSep) || (eOp == ocArrayColSep) ||
     487             :             (eOp == ocArrayRowSep),
     488             :             OStringBuffer(
     489             :                 RTL_CONSTASCII_STRINGPARAM("OpCodeMap::putOpCode: reusing OpCode ")).
     490             :             append(sal_Int32(eOp)).append(RTL_CONSTASCII_STRINGPARAM(" (")).
     491             :             append(OUStringToOString(rStr, RTL_TEXTENCODING_ASCII_US)).
     492             :             append(')').getStr());
     493      127279 :         mpTable[eOp] = rStr;
     494      127279 :         mpHashMap->insert( OpCodeHashMap::value_type( rStr, eOp));
     495             :     }
     496      127279 : }
     497             : // -----------------------------------------------------------------------------
     498             : // class FormulaCompiler
     499             : // -----------------------------------------------------------------------------
     500             : DBG_NAME(FormulaCompiler)
     501        5461 : FormulaCompiler::FormulaCompiler(FormulaTokenArray& _rArr)
     502             :         :
     503             :         pArr( &_rArr ),
     504             :         pExternalRef(NULL),
     505             :         pStack( NULL ),
     506             :         nRecursion(0),
     507             :         nNumFmt( NUMBERFORMAT_UNDEFINED ),
     508             :         meGrammar( formula::FormulaGrammar::GRAM_UNSPECIFIED ),
     509             :         bAutoCorrect( false ),
     510             :         bCorrected( false ),
     511             :         bCompileForFAP( false ),
     512        5461 :         bIgnoreErrors( false )
     513             : 
     514             : {
     515             :     DBG_CTOR(FormulaCompiler,NULL);
     516        5461 : }
     517       27279 : FormulaCompiler::FormulaCompiler()
     518             :         :
     519             :         pArr( NULL ),
     520             :         pExternalRef(NULL),
     521             :         pStack( NULL ),
     522             :         nRecursion(0),
     523             :         nNumFmt( NUMBERFORMAT_UNDEFINED ),
     524             :         meGrammar( formula::FormulaGrammar::GRAM_UNSPECIFIED ),
     525             :         bAutoCorrect( false ),
     526             :         bCorrected( false ),
     527             :         bCompileForFAP( false ),
     528       27279 :         bIgnoreErrors( false )
     529             : 
     530             : {
     531             :     DBG_CTOR(FormulaCompiler,NULL);
     532       27279 : }
     533       32740 : FormulaCompiler::~FormulaCompiler()
     534             : {
     535             :     DBG_DTOR(FormulaCompiler,NULL);
     536       32740 : }
     537             : 
     538       33011 : FormulaCompiler::OpCodeMapPtr FormulaCompiler::GetOpCodeMap( const sal_Int32 nLanguage ) const
     539             : {
     540       33011 :     FormulaCompiler::OpCodeMapPtr xMap;
     541             :     using namespace sheet;
     542       33011 :     switch (nLanguage)
     543             :     {
     544             :         case FormulaLanguage::ODFF :
     545        1453 :             if (!mxSymbolsODFF)
     546        1318 :                 InitSymbolsODFF();
     547        1453 :             xMap = mxSymbolsODFF;
     548        1453 :             break;
     549             :         case FormulaLanguage::ODF_11 :
     550         361 :             if (!mxSymbolsPODF)
     551         361 :                 InitSymbolsPODF();
     552         361 :             xMap = mxSymbolsPODF;
     553         361 :             break;
     554             :         case FormulaLanguage::ENGLISH :
     555          65 :             if (!mxSymbolsEnglish)
     556          65 :                 InitSymbolsEnglish();
     557          65 :             xMap = mxSymbolsEnglish;
     558          65 :             break;
     559             :         case FormulaLanguage::NATIVE :
     560       31034 :             if (!mxSymbolsNative)
     561       30997 :                 InitSymbolsNative();
     562       31034 :             xMap = mxSymbolsNative;
     563       31034 :             break;
     564             :         case FormulaLanguage::XL_ENGLISH:
     565          98 :             if (!mxSymbolsEnglishXL)
     566          98 :                 InitSymbolsEnglishXL();
     567          98 :             xMap = mxSymbolsEnglishXL;
     568          98 :             break;
     569             :         default:
     570             :             ;   // nothing, NULL map returned
     571             :     }
     572       33011 :     return xMap;
     573             : }
     574             : // -----------------------------------------------------------------------------
     575             : 
     576           0 : String FormulaCompiler::FindAddInFunction( const String& /*rUpperName*/, bool /*bLocalFirst*/ ) const
     577             : {
     578           0 :     return String();
     579             : }
     580             : // -----------------------------------------------------------------------------
     581          27 : FormulaCompiler::OpCodeMapPtr FormulaCompiler::CreateOpCodeMap(
     582             :         const uno::Sequence<
     583             :         const sheet::FormulaOpCodeMapEntry > & rMapping,
     584             :         bool bEnglish )
     585             : {
     586             :     using sheet::FormulaOpCodeMapEntry;
     587             :     // Filter / API maps are never Core
     588          27 :     NonConstOpCodeMapPtr xMap( new OpCodeMap( SC_OPCODE_LAST_OPCODE_ID + 1,false, FormulaGrammar::mergeToGrammar( FormulaGrammar::setEnglishBit(FormulaGrammar::GRAM_EXTERNAL, bEnglish),FormulaGrammar::CONV_UNSPECIFIED)));
     589          27 :     FormulaOpCodeMapEntry const * pArr2 = rMapping.getConstArray();
     590          27 :     FormulaOpCodeMapEntry const * const pStop = pArr2 + rMapping.getLength();
     591       11193 :     for ( ; pArr2 < pStop; ++pArr2)
     592             :     {
     593       11166 :         OpCode eOp = OpCode(pArr2->Token.OpCode);
     594       11166 :         if (eOp != ocExternal)
     595        8439 :             xMap->putOpCode( pArr2->Name, eOp);
     596             :         else
     597             :         {
     598        2727 :             OUString aExternalName;
     599        2727 :             if (pArr2->Token.Data >>= aExternalName)
     600        2727 :                 xMap->putExternal( pArr2->Name, aExternalName);
     601             :             else
     602             :             {
     603             :                 SAL_WARN( "formula.core", "FormulaCompiler::CreateOpCodeMap: no Token.Data external name");
     604        2727 :             }
     605             :         }
     606             :     }
     607          27 :     return xMap;
     608             : }
     609             : 
     610             : // -----------------------------------------------------------------------------
     611       62296 : void lcl_fillNativeSymbols(FormulaCompiler::NonConstOpCodeMapPtr& _xMap,bool _destroy = false)
     612             : {
     613       62296 :     static FormulaCompiler::NonConstOpCodeMapPtr s_SymbolMap;
     614       62296 :     if ( _destroy )
     615             :     {
     616         313 :         s_SymbolMap.reset();
     617             :     } // if ( _destroy )
     618       61983 :     else if ( !s_SymbolMap.get() )
     619             :     {
     620             :         // Core
     621         314 :         s_SymbolMap.reset( new FormulaCompiler::OpCodeMap( SC_OPCODE_LAST_OPCODE_ID + 1, true, FormulaGrammar::GRAM_NATIVE_UI));
     622         314 :         OModuleClient aModuleClient;
     623         314 :         OpCodeList aOpCodeListNative( RID_STRLIST_FUNCTION_NAMES, s_SymbolMap );
     624             :         // No AddInMap for native core mapping.
     625             :     } // if ( !s_SymbolMap.get() )
     626       62296 :     _xMap = s_SymbolMap;
     627       62296 : }
     628             : // -----------------------------------------------------------------------------
     629       30360 : const String& FormulaCompiler::GetNativeSymbol( OpCode eOp )
     630             : {
     631       30360 :     NonConstOpCodeMapPtr xSymbolsNative;
     632       30360 :     lcl_fillNativeSymbols(xSymbolsNative);
     633       30360 :     return xSymbolsNative->getSymbol( eOp );
     634             : }
     635             : // -----------------------------------------------------------------------------
     636       30997 : void FormulaCompiler::InitSymbolsNative() const
     637             : {
     638       30997 :     lcl_fillNativeSymbols(mxSymbolsNative);
     639       30997 : }
     640             : // -----------------------------------------------------------------------------
     641          65 : void FormulaCompiler::InitSymbolsEnglish() const
     642             : {
     643          65 :     static NonConstOpCodeMapPtr s_sSymbol;
     644          65 :     if ( !s_sSymbol.get() )
     645           3 :         loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH,FormulaGrammar::GRAM_ENGLISH,s_sSymbol);
     646          65 :     mxSymbolsEnglish = s_sSymbol;
     647          65 : }
     648             : // -----------------------------------------------------------------------------
     649         361 : void FormulaCompiler::InitSymbolsPODF() const
     650             : {
     651         361 :     static NonConstOpCodeMapPtr s_sSymbol;
     652         361 :     if ( !s_sSymbol.get() )
     653           7 :         loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH,FormulaGrammar::GRAM_PODF,s_sSymbol);
     654         361 :     mxSymbolsPODF = s_sSymbol;
     655         361 : }
     656             : // -----------------------------------------------------------------------------
     657        1318 : void FormulaCompiler::InitSymbolsODFF() const
     658             : {
     659        1318 :     static NonConstOpCodeMapPtr s_sSymbol;
     660        1318 :     if ( !s_sSymbol.get() )
     661          20 :         loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH_ODFF,FormulaGrammar::GRAM_ODFF,s_sSymbol);
     662        1318 :     mxSymbolsODFF = s_sSymbol;
     663        1318 : }
     664             : // -----------------------------------------------------------------------------
     665          98 : void FormulaCompiler::InitSymbolsEnglishXL() const
     666             : {
     667          98 :     static NonConstOpCodeMapPtr s_sSymbol;
     668          98 :     if ( !s_sSymbol.get() )
     669           3 :         loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH,FormulaGrammar::GRAM_ENGLISH,s_sSymbol);
     670          98 :     mxSymbolsEnglishXL = s_sSymbol;
     671             : 
     672             :     // TODO: For now, just replace the separators to the Excel English
     673             :     // variants. Later, if we want to properly map Excel functions with Calc
     674             :     // functions, we'll need to do a little more work here.
     675          98 :     mxSymbolsEnglishXL->putOpCode(OUString(','), ocSep);
     676          98 :     mxSymbolsEnglishXL->putOpCode(OUString(','), ocArrayColSep);
     677          98 :     mxSymbolsEnglishXL->putOpCode(OUString(';'), ocArrayRowSep);
     678          98 : }
     679             : 
     680             : // -----------------------------------------------------------------------------
     681          33 : void FormulaCompiler::loadSymbols(sal_uInt16 _nSymbols,FormulaGrammar::Grammar _eGrammar,NonConstOpCodeMapPtr& _xMap) const
     682             : {
     683          33 :     if ( !_xMap.get() )
     684             :     {
     685             :         // not Core
     686          33 :         _xMap.reset( new OpCodeMap( SC_OPCODE_LAST_OPCODE_ID + 1, _eGrammar != FormulaGrammar::GRAM_ODFF, _eGrammar ));
     687          33 :         OModuleClient aModuleClient;
     688          66 :         OpCodeList aOpCodeList( _nSymbols, _xMap );
     689             : 
     690          33 :         fillFromAddInMap( _xMap, _eGrammar);
     691             :         // Fill from collection for AddIns not already present.
     692          33 :         if ( FormulaGrammar::GRAM_ENGLISH != _eGrammar )
     693          27 :             fillFromAddInCollectionUpperName( _xMap);
     694             :         else
     695          39 :             fillFromAddInCollectionEnglishName( _xMap);
     696             :     }
     697          33 : }
     698             : // -----------------------------------------------------------------------------
     699           0 : void FormulaCompiler::fillFromAddInCollectionUpperName( NonConstOpCodeMapPtr /*xMap */) const
     700             : {
     701           0 : }
     702             : // -----------------------------------------------------------------------------
     703           0 : void FormulaCompiler::fillFromAddInCollectionEnglishName( NonConstOpCodeMapPtr /*xMap */) const
     704             : {
     705           0 : }
     706             : // -----------------------------------------------------------------------------
     707           0 : void FormulaCompiler::fillFromAddInMap( NonConstOpCodeMapPtr /*xMap*/, FormulaGrammar::Grammar /*_eGrammar */) const
     708             : {
     709           0 : }
     710             : // -----------------------------------------------------------------------------
     711           0 : OpCode FormulaCompiler::GetEnglishOpCode( const String& rName ) const
     712             : {
     713           0 :     FormulaCompiler::OpCodeMapPtr xMap = GetOpCodeMap(sheet::FormulaLanguage::ENGLISH);
     714             : 
     715           0 :     formula::OpCodeHashMap::const_iterator iLook( xMap->getHashMap()->find( rName ) );
     716           0 :     bool bFound = (iLook != xMap->getHashMap()->end());
     717           0 :     return bFound ? (*iLook).second : OpCode(ocNone);
     718             : }
     719             : 
     720       10796 : bool FormulaCompiler::IsOpCodeVolatile( OpCode eOp )
     721             : {
     722       10796 :     bool bRet = false;
     723       10796 :     switch (eOp)
     724             :     {
     725             :         // no parameters:
     726             :         case ocRandom:
     727             :         case ocGetActDate:
     728             :         case ocGetActTime:
     729             :         // one parameter:
     730             :         case ocFormula:
     731             :         case ocInfo:
     732             :         // more than one parameters:
     733             :             // ocIndirect/ocIndirectXL otherwise would have to do
     734             :             // StopListening and StartListening on a reference for every
     735             :             // interpreted value.
     736             :         case ocIndirect:
     737             :         case ocIndirectXL:
     738             :             // ocOffset results in indirect references.
     739             :         case ocOffset:
     740          59 :             bRet = true;
     741          59 :             break;
     742             :         default:
     743       10737 :             bRet = false;
     744       10737 :             break;
     745             :     }
     746       10796 :     return bRet;
     747             : }
     748             : 
     749             : // Remove quotes, escaped quotes are unescaped.
     750          16 : bool FormulaCompiler::DeQuote( String& rStr )
     751             : {
     752          16 :     xub_StrLen nLen = rStr.Len();
     753          16 :     if ( nLen > 1 && rStr.GetChar(0) == '\'' && rStr.GetChar( nLen-1 ) == '\'' )
     754             :     {
     755           0 :         rStr.Erase( nLen-1, 1 );
     756           0 :         rStr.Erase( 0, 1 );
     757           0 :         xub_StrLen nPos = 0;
     758           0 :         while ( (nPos = rStr.SearchAscii( "\\\'", nPos)) != STRING_NOTFOUND )
     759             :         {
     760           0 :             rStr.Erase( nPos, 1 );
     761           0 :             ++nPos;
     762             :         }
     763           0 :         return true;
     764             :     }
     765          16 :     return false;
     766             : }
     767             : // -----------------------------------------------------------------------------
     768           0 : void FormulaCompiler::fillAddInToken(::std::vector< sheet::FormulaOpCodeMapEntry >& /*_rVec*/,bool /*_bIsEnglish*/) const
     769             : {
     770           0 : }
     771             : // -----------------------------------------------------------------------------
     772           0 : bool FormulaCompiler::IsMatrixFunction(OpCode _eOpCode)
     773             : {
     774           0 :     switch ( _eOpCode )
     775             :     {
     776             :         case ocDde :
     777             :         case ocGrowth :
     778             :         case ocTrend :
     779             :         case ocRKP :
     780             :         case ocRGP :
     781             :         case ocFrequency :
     782             :         case ocMatTrans :
     783             :         case ocMatMult :
     784             :         case ocMatInv :
     785             :         case ocMatrixUnit :
     786           0 :             return true;
     787             :         default:
     788             :         {
     789             :             // added to avoid warnings
     790             :         }
     791             :     }
     792           0 :     return false;
     793             : }
     794             : 
     795             : // -----------------------------------------------------------------------------
     796         748 : FormulaCompiler::OpCodeMap::~OpCodeMap()
     797             : {
     798         374 :     delete mpReverseExternalHashMap;
     799         374 :     delete mpExternalHashMap;
     800         374 :     delete [] mpTable;
     801         374 :     delete mpHashMap;
     802         748 : }
     803             : // -----------------------------------------------------------------------------
     804           0 : void FormulaCompiler::OpCodeMap::copyFrom( const OpCodeMap& r )
     805             : {
     806           0 :     delete mpHashMap;
     807           0 :     mpHashMap = new OpCodeHashMap(mnSymbols);
     808             : 
     809           0 :     sal_uInt16 n = r.getSymbolCount();
     810           0 :     for (sal_uInt16 i = 0; i < n; ++i)
     811             :     {
     812           0 :         OpCode eOp = OpCode(i);
     813           0 :         const String& rSymbol = r.getSymbol(eOp);
     814           0 :         putOpCode(rSymbol, eOp);
     815             :     }
     816             : 
     817             :     // TODO: maybe copy the external maps too?
     818           0 : }
     819             : // -----------------------------------------------------------------------------
     820             : 
     821          18 : sal_uInt16 FormulaCompiler::GetErrorConstant( const String& rName ) const
     822             : {
     823          18 :     sal_uInt16 nError = 0;
     824          18 :     OpCodeHashMap::const_iterator iLook( mxSymbols->getHashMap()->find( rName));
     825          18 :     if (iLook != mxSymbols->getHashMap()->end())
     826             :     {
     827          18 :         switch ((*iLook).second)
     828             :         {
     829             :             // Not all may make sense in a formula, but these we know as
     830             :             // opcodes.
     831             :             case ocErrNull:
     832           0 :                 nError = errNoCode;
     833           0 :                 break;
     834             :             case ocErrDivZero:
     835           4 :                 nError = errDivisionByZero;
     836           4 :                 break;
     837             :             case ocErrValue:
     838           0 :                 nError = errNoValue;
     839           0 :                 break;
     840             :             case ocErrRef:
     841           0 :                 nError = errNoRef;
     842           0 :                 break;
     843             :             case ocErrName:
     844           0 :                 nError = errNoName;
     845           0 :                 break;
     846             :             case ocErrNum:
     847           0 :                 nError = errIllegalFPOperation;
     848           0 :                 break;
     849             :             case ocErrNA:
     850          14 :                 nError = NOTAVAILABLE;
     851          14 :                 break;
     852             :             default:
     853             :                 ;   // nothing
     854             :         }
     855             :     }
     856          18 :     return nError;
     857             : }
     858             : 
     859             : 
     860           0 : void FormulaCompiler::AppendErrorConstant( OUStringBuffer& rBuffer, sal_uInt16 nError )
     861             : {
     862             :     OpCode eOp;
     863           0 :     switch (nError)
     864             :     {
     865             :         default:
     866             :         case errNoCode:
     867           0 :             eOp = ocErrNull;
     868           0 :             break;
     869             :         case errDivisionByZero:
     870           0 :             eOp = ocErrDivZero;
     871           0 :             break;
     872             :         case errNoValue:
     873           0 :             eOp = ocErrValue;
     874           0 :             break;
     875             :         case errNoRef:
     876           0 :             eOp = ocErrRef;
     877           0 :             break;
     878             :         case errNoName:
     879           0 :             eOp = ocErrName;
     880           0 :             break;
     881             :         case errIllegalFPOperation:
     882           0 :             eOp = ocErrNum;
     883           0 :             break;
     884             :         case NOTAVAILABLE:
     885           0 :             eOp = ocErrNA;
     886           0 :             break;
     887             :     }
     888           0 :     rBuffer.append( mxSymbols->getSymbol( eOp));
     889           0 : }
     890             : 
     891             : // -----------------------------------------------------------------------------
     892          54 : sal_Int32 FormulaCompiler::OpCodeMap::getOpCodeUnknown()
     893             : {
     894             :     static const sal_Int32 kOpCodeUnknown = -1;
     895          54 :     return kOpCodeUnknown;
     896             : }
     897             : // -----------------------------------------------------------------------------
     898       41089 : bool FormulaCompiler::GetToken()
     899             : {
     900             :     static const short nRecursionMax = 42;
     901       41089 :     FormulaCompilerRecursionGuard aRecursionGuard( nRecursion );
     902       41089 :     if ( nRecursion > nRecursionMax )
     903             :     {
     904           0 :         SetError( errStackOverflow );
     905           0 :         mpToken = new FormulaByteToken( ocStop );
     906           0 :         return false;
     907             :     }
     908       41089 :     if ( bAutoCorrect && !pStack )
     909             :     {   // don't merge stacked subroutine code into entered formula
     910           0 :         aCorrectedFormula += aCorrectedSymbol;
     911           0 :         aCorrectedSymbol.Erase();
     912             :     }
     913       41089 :     bool bStop = false;
     914       41089 :     if( pArr->GetCodeError() && !bIgnoreErrors )
     915           9 :         bStop = true;
     916             :     else
     917             :     {
     918             :         short nWasColRowName;
     919       41080 :         if ( pArr->nIndex
     920       41080 :           && pArr->pCode[ pArr->nIndex-1 ]->GetOpCode() == ocColRowName )
     921           0 :              nWasColRowName = 1;
     922             :         else
     923       41080 :              nWasColRowName = 0;
     924       41080 :         mpToken = pArr->Next();
     925       82234 :         while( mpToken && mpToken->GetOpCode() == ocSpaces )
     926             :         {
     927          74 :             if ( nWasColRowName )
     928           0 :                 nWasColRowName++;
     929          74 :             if ( bAutoCorrect && !pStack )
     930           0 :                 CreateStringFromToken( aCorrectedFormula, mpToken.get(), false );
     931          74 :             mpToken = pArr->Next();
     932             :         }
     933       41080 :         if ( bAutoCorrect && !pStack && mpToken )
     934           0 :             CreateStringFromToken( aCorrectedSymbol, mpToken.get(), false );
     935       41080 :         if( !mpToken )
     936             :         {
     937        4837 :             if( pStack )
     938             :             {
     939         204 :                 PopTokenArray();
     940         204 :                 return GetToken();
     941             :             }
     942             :             else
     943        4633 :                 bStop = true;
     944             :         }
     945             :         else
     946             :         {
     947       36243 :             if ( nWasColRowName >= 2 && mpToken->GetOpCode() == ocColRowName )
     948             :             {   // convert an ocSpaces to ocIntersect in RPN
     949           0 :                 mpToken = new FormulaByteToken( ocIntersect );
     950           0 :                 pArr->nIndex--;     // we advanced to the second ocColRowName, step back
     951             :             }
     952             :         }
     953             :     }
     954       40885 :     if( bStop )
     955             :     {
     956        4642 :         mpToken = new FormulaByteToken( ocStop );
     957        4642 :         return false;
     958             :     }
     959       36243 :     if( mpToken->GetOpCode() == ocSubTotal )
     960           6 :         glSubTotal = true;
     961       36237 :     else if ( mpToken->IsExternalRef() )
     962             :     {
     963          34 :         return HandleExternalReference(*mpToken);
     964             :     }
     965       36203 :     else if( mpToken->GetOpCode() == ocName )
     966             :     {
     967         182 :         return HandleRange();
     968             :     }
     969       36021 :     else if( mpToken->GetOpCode() == ocColRowName )
     970             :     {
     971           0 :         return HandleSingleRef();
     972             :     }
     973       36021 :     else if( mpToken->GetOpCode() == ocDBArea )
     974             :     {
     975           2 :         return HandleDbData();
     976             :     }
     977       36019 :     else if( mpToken->GetType() == svSingleRef )
     978             :     {
     979        6252 :         pArr->nRefs++;
     980             :     }
     981       29767 :     else if( mpToken->GetType() == svDoubleRef )
     982             :     {
     983        2307 :         pArr->nRefs++;
     984             :     }
     985       36025 :     return true;
     986             : }
     987             : //---------------------------------------------------------------------------
     988             : // RPN creation by recursion
     989             : //---------------------------------------------------------------------------
     990             : 
     991       18592 : void FormulaCompiler::Factor()
     992             : {
     993       18592 :     if ( pArr->GetCodeError() && !bIgnoreErrors )
     994       18610 :         return;
     995             : 
     996       18574 :     CurrentFactor pFacToken( this );
     997             : 
     998       18574 :     OpCode eOp = mpToken->GetOpCode();
     999       18574 :     if( eOp == ocPush || eOp == ocColRowNameAuto || eOp == ocMatRef ||
    1000             :             eOp == ocDBArea
    1001        5209 :             || (bCompileForFAP && ((eOp == ocName) || (eOp == ocDBArea)
    1002           0 :             || (eOp == ocColRowName) || (eOp == ocBad)))
    1003             :         )
    1004             :     {
    1005       13365 :         PutCode( mpToken );
    1006       13365 :         eOp = NextToken();
    1007       26730 :         if( eOp == ocOpen )
    1008             :         {
    1009             :             // PUSH( is an error that may be caused by an unknown function.
    1010             :             SetError(
    1011           0 :                 ( mpToken->GetType() == svString
    1012           0 :                || mpToken->GetType() == svSingleRef )
    1013           0 :                ? errNoName : errOperatorExpected );
    1014           0 :             if ( bAutoCorrect && !pStack )
    1015             :             {   // assume multiplication
    1016           0 :                 aCorrectedFormula += mxSymbols->getSymbol(ocMul);
    1017           0 :                 bCorrected = true;
    1018           0 :                 NextToken();
    1019           0 :                 eOp = Expression();
    1020           0 :                 if( eOp != ocClose )
    1021           0 :                     SetError(errPairExpected);
    1022             :                 else
    1023           0 :                     eOp = NextToken();
    1024             :             }
    1025             :         }
    1026             :     }
    1027        5209 :     else if( eOp == ocOpen )
    1028             :     {
    1029         998 :         NextToken();
    1030         998 :         eOp = Expression();
    1031        1996 :         while ((eOp == ocSep) && (!pArr->GetCodeError() || bIgnoreErrors))
    1032             :         {   // range list  (A1;A2)  converted to  (A1~A2)
    1033           0 :             pFacToken = mpToken;
    1034           0 :             NextToken();
    1035           0 :             eOp = Expression();
    1036             :             // Do not ignore error here, regardless of bIgnoreErrors, otherwise
    1037             :             // errors like =(1;) would also result in display of =(1~)
    1038           0 :             if (!pArr->GetCodeError())
    1039             :             {
    1040           0 :                 pFacToken->NewOpCode( ocUnion,FormulaToken::PrivateAccess());
    1041           0 :                 PutCode( pFacToken);
    1042             :             }
    1043             :         }
    1044         998 :         if (eOp != ocClose)
    1045           0 :             SetError(errPairExpected);
    1046             :         else
    1047         998 :             eOp = NextToken();
    1048             :     }
    1049             :     else
    1050             :     {
    1051        4211 :         if( nNumFmt == NUMBERFORMAT_UNDEFINED )
    1052        1965 :             nNumFmt = lcl_GetRetFormat( eOp );
    1053             : 
    1054        4211 :         if ( IsOpCodeVolatile(eOp) )
    1055          24 :             pArr->SetExclusiveRecalcModeAlways();
    1056             :         else
    1057             :         {
    1058        4187 :             switch( eOp )
    1059             :             {
    1060             :                     // Functions recalculated on every document load.
    1061             :                     // Don't use SetExclusiveRecalcModeOnLoad() which would
    1062             :                     // override ModeAlways, use
    1063             :                     // AddRecalcMode(RECALCMODE_ONLOAD) instead.
    1064             :                 case ocConvert :
    1065             :                 case ocDde:
    1066             :                 case ocMacro:
    1067             :                 case ocExternal:
    1068          17 :                     pArr->AddRecalcMode( RECALCMODE_ONLOAD );
    1069          17 :                 break;
    1070             :                     // If the referred cell is moved the value changes.
    1071             :                 case ocColumn :
    1072             :                 case ocRow :
    1073          29 :                     pArr->SetRecalcModeOnRefMove();
    1074          29 :                 break;
    1075             :                     // ocCell needs recalc on move for some possible type values.
    1076             :                     // and recalc mode on load, fdo#60646
    1077             :                 case ocCell :
    1078          28 :                     pArr->SetRecalcModeOnRefMove();
    1079          28 :                     pArr->AddRecalcMode( RECALCMODE_ONLOAD );
    1080          28 :                 break;
    1081             :                 case ocHyperLink :
    1082             :                     // cell with hyperlink needs to be calculated on load to
    1083             :                     // get its matrix result generated.
    1084           0 :                     pArr->AddRecalcMode( RECALCMODE_ONLOAD );
    1085           0 :                     pArr->SetHyperLink(true);
    1086           0 :                 break;
    1087             :                 default:
    1088             :                     ;   // nothing
    1089             :             }
    1090             :         }
    1091        4211 :         if (SC_OPCODE_START_NO_PAR <= eOp && eOp < SC_OPCODE_STOP_NO_PAR)
    1092             :         {
    1093          53 :             pFacToken = mpToken;
    1094          53 :             eOp = NextToken();
    1095         106 :             if (eOp != ocOpen)
    1096             :             {
    1097           0 :                 SetError(errPairExpected);
    1098           0 :                 PutCode( pFacToken );
    1099             :             }
    1100             :             else
    1101             :             {
    1102          53 :                 eOp = NextToken();
    1103          53 :                 if (eOp != ocClose)
    1104           0 :                     SetError(errPairExpected);
    1105          53 :                 PutCode(pFacToken);
    1106          53 :                 eOp = NextToken();
    1107             :             }
    1108             :         }
    1109             :         // special cases NOT() and NEG()
    1110        4158 :         else if( eOp == ocNot || eOp == ocNeg
    1111        4158 :               || (SC_OPCODE_START_1_PAR <= eOp && eOp < SC_OPCODE_STOP_1_PAR) )
    1112             :         {
    1113         244 :             pFacToken = mpToken;
    1114         244 :             eOp = NextToken();
    1115         244 :             if( nNumFmt == NUMBERFORMAT_UNDEFINED && eOp == ocNot )
    1116           0 :                 nNumFmt = NUMBERFORMAT_LOGICAL;
    1117         244 :             if (eOp == ocOpen)
    1118             :             {
    1119         244 :                 NextToken();
    1120         244 :                 eOp = Expression();
    1121             :             }
    1122             :             else
    1123           0 :                 SetError(errPairExpected);
    1124         244 :             if (eOp != ocClose)
    1125           0 :                 SetError(errPairExpected);
    1126         244 :             else if ( !pArr->GetCodeError() )
    1127         244 :                 pFacToken->SetByte( 1 );
    1128         244 :             PutCode( pFacToken );
    1129         244 :             eOp = NextToken();
    1130             :         }
    1131        3914 :         else if ((SC_OPCODE_START_2_PAR <= eOp && eOp < SC_OPCODE_STOP_2_PAR)
    1132         490 :                 || eOp == ocExternal
    1133         490 :                 || eOp == ocMacro
    1134         489 :                 || eOp == ocAnd
    1135         477 :                 || eOp == ocOr
    1136         457 :                 || eOp == ocBad
    1137         442 :                 || ( eOp >= ocInternalBegin && eOp <= ocInternalEnd )
    1138         442 :                 || ( bCompileForFAP
    1139           0 :                      && ( eOp == ocIf || eOp == ocIfError || eOp == ocIfNA || eOp == ocChose ) )
    1140             :             )
    1141             :         {
    1142        3472 :             pFacToken = mpToken;
    1143        3472 :             OpCode eMyLastOp = eOp;
    1144        3472 :             eOp = NextToken();
    1145        3472 :             bool bNoParam = false;
    1146        3472 :             bool bBadName = false;
    1147        3472 :             if (eOp == ocOpen)
    1148             :             {
    1149        3457 :                 eOp = NextToken();
    1150        3457 :                 if (eOp == ocClose)
    1151           8 :                     bNoParam = true;
    1152             :                 else
    1153        3449 :                     eOp = Expression();
    1154             :             }
    1155          15 :             else if (eMyLastOp == ocBad)
    1156             :             {
    1157             :                 // Just a bad name, not an unknown function, no parameters, no
    1158             :                 // closing expected.
    1159          15 :                 bBadName = true;
    1160          15 :                 bNoParam = true;
    1161             :             }
    1162             :             else
    1163           0 :                 SetError(errPairExpected);
    1164        3472 :             sal_uInt8 nSepCount = 0;
    1165        3472 :             if( !bNoParam )
    1166             :             {
    1167        3449 :                 nSepCount++;
    1168       10144 :                 while ( (eOp == ocSep) && (!pArr->GetCodeError() || bIgnoreErrors) )
    1169             :                 {
    1170        3246 :                     nSepCount++;
    1171        3246 :                     NextToken();
    1172        3246 :                     eOp = Expression();
    1173             :                 }
    1174             :             }
    1175        3472 :             if (bBadName)
    1176             :                 ;   // nothing, keep current token for return
    1177        3457 :             else if (eOp != ocClose)
    1178           0 :                 SetError(errPairExpected);
    1179             :             else
    1180        3457 :                 eOp = NextToken();
    1181             :             // Jumps are just normal functions for the FunctionAutoPilot tree view
    1182        3472 :             if ( bCompileForFAP && pFacToken->GetType() == svJump )
    1183           0 :                 pFacToken = new FormulaFAPToken( pFacToken->GetOpCode(), nSepCount, pFacToken );
    1184             :             else
    1185        3472 :                 pFacToken->SetByte( nSepCount );
    1186        3472 :             PutCode( pFacToken );
    1187             :         }
    1188         442 :         else if (eOp == ocIf || eOp == ocIfError || eOp == ocIfNA || eOp == ocChose)
    1189             :         {
    1190             :             // the PC counters are -1
    1191          39 :             pFacToken = mpToken;
    1192          39 :             switch (eOp)
    1193             :             {
    1194             :                 case ocIf:
    1195          27 :                     pFacToken->GetJump()[ 0 ] = 3;  // if, else, behind
    1196          27 :                     break;
    1197             :                 case ocChose:
    1198           0 :                     pFacToken->GetJump()[ 0 ] = FORMULA_MAXJUMPCOUNT + 1;
    1199           0 :                     break;
    1200             :                 case ocIfError:
    1201             :                 case ocIfNA:
    1202          12 :                     pFacToken->GetJump()[ 0 ] = 2;  // if, behind
    1203          12 :                     break;
    1204             :                 default:
    1205             :                     SAL_WARN( "formula.core", "FormulaCompiler::Factor: forgot to add a jump count case?");
    1206             :             }
    1207          39 :             eOp = NextToken();
    1208          39 :             if (eOp == ocOpen)
    1209             :             {
    1210          39 :                 NextToken();
    1211          39 :                 eOp = Expression();
    1212             :             }
    1213             :             else
    1214           0 :                 SetError(errPairExpected);
    1215          39 :             PutCode( pFacToken );
    1216             :             // During AutoCorrect (since pArr->GetCodeError() is
    1217             :             // ignored) an unlimited ocIf would crash because
    1218             :             // ScRawToken::Clone() allocates the JumpBuffer according to
    1219             :             // nJump[0]*2+2, which is 3*2+2 on ocIf and 2*2+2 ocIfError and ocIfNA.
    1220             :             short nJumpMax;
    1221          39 :             OpCode eFacOpCode = pFacToken->GetOpCode();
    1222          39 :             switch (eFacOpCode)
    1223             :             {
    1224             :                 case ocIf:
    1225          27 :                     nJumpMax = 3;
    1226          27 :                     break;
    1227             :                 case ocChose:
    1228           0 :                     nJumpMax = FORMULA_MAXJUMPCOUNT;
    1229           0 :                     break;
    1230             :                 case ocIfError:
    1231             :                 case ocIfNA:
    1232          12 :                     nJumpMax = 2;
    1233          12 :                     break;
    1234             :                 default:
    1235           0 :                     nJumpMax = 0;
    1236             :                     SAL_WARN( "formula.core", "FormulaCompiler::Factor: forgot to add a jump max case?");
    1237             :             }
    1238          39 :             short nJumpCount = 0;
    1239         213 :             while ( (nJumpCount < (FORMULA_MAXJUMPCOUNT - 1)) && (eOp == ocSep)
    1240         135 :                     && (!pArr->GetCodeError() || bIgnoreErrors) )
    1241             :             {
    1242          48 :                 if ( ++nJumpCount <= nJumpMax )
    1243          48 :                     pFacToken->GetJump()[nJumpCount] = pc-1;
    1244          48 :                 NextToken();
    1245          48 :                 eOp = Expression();
    1246             :                 // ocSep or ocClose terminate the subexpression
    1247          48 :                 PutCode( mpToken );
    1248             :             }
    1249          39 :             if (eOp != ocClose)
    1250           0 :                 SetError(errPairExpected);
    1251             :             else
    1252             :             {
    1253          39 :                 eOp = NextToken();
    1254             :                 // always limit to nJumpMax, no arbitrary overwrites
    1255          39 :                 if ( ++nJumpCount <= nJumpMax )
    1256          39 :                     pFacToken->GetJump()[ nJumpCount ] = pc-1;
    1257          39 :                 eFacOpCode = pFacToken->GetOpCode();
    1258             :                 bool bLimitOk;
    1259          39 :                 switch (eFacOpCode)
    1260             :                 {
    1261             :                     case ocIf:
    1262          27 :                         bLimitOk = (nJumpCount <= 3);
    1263          27 :                         break;
    1264             :                     case ocChose:
    1265           0 :                         bLimitOk = (nJumpCount < FORMULA_MAXJUMPCOUNT); /* TODO: check, really <, not <=? */
    1266           0 :                         break;
    1267             :                     case ocIfError:
    1268             :                     case ocIfNA:
    1269          12 :                         bLimitOk = (nJumpCount <= 2);
    1270          12 :                         break;
    1271             :                     default:
    1272           0 :                         bLimitOk = false;
    1273             :                         SAL_WARN( "formula.core", "FormulaCompiler::Factor: forgot to add a jump limit case?");
    1274             :                 }
    1275          39 :                 if (bLimitOk)
    1276          39 :                     pFacToken->GetJump()[ 0 ] = nJumpCount;
    1277             :                 else
    1278           0 :                     SetError(errIllegalParameter);
    1279          39 :             }
    1280             :         }
    1281         403 :         else if ( eOp == ocMissing )
    1282             :         {
    1283           8 :             PutCode( mpToken );
    1284           8 :             eOp = NextToken();
    1285             :         }
    1286         395 :         else if ( eOp == ocClose )
    1287             :         {
    1288           0 :             SetError( errParameterExpected );
    1289             :         }
    1290         395 :         else if ( eOp == ocSep )
    1291             :         {   // Subsequent ocSep
    1292           0 :             SetError( errParameterExpected );
    1293           0 :             if ( bAutoCorrect && !pStack )
    1294             :             {
    1295           0 :                 aCorrectedSymbol.Erase();
    1296           0 :                 bCorrected = true;
    1297             :             }
    1298             :         }
    1299         395 :         else if ( mpToken->IsExternalRef() )
    1300             :         {
    1301           0 :             PutCode(mpToken);
    1302           0 :             eOp = NextToken();
    1303             :         }
    1304             :         else
    1305             :         {
    1306         395 :             SetError( errUnknownToken );
    1307         395 :             if ( bAutoCorrect && !pStack )
    1308             :             {
    1309           0 :                 if ( eOp == ocStop )
    1310             :                 {   // trailing operator w/o operand
    1311           0 :                     xub_StrLen nLen = aCorrectedFormula.Len();
    1312           0 :                     if ( nLen )
    1313           0 :                         aCorrectedFormula.Erase( nLen - 1 );
    1314           0 :                     aCorrectedSymbol.Erase();
    1315           0 :                     bCorrected = true;
    1316             :                 }
    1317             :             }
    1318             :         }
    1319       18574 :     }
    1320             : }
    1321             : 
    1322             : //---------------------------------------------------------------------------
    1323             : 
    1324       18592 : void FormulaCompiler::RangeLine()
    1325             : {
    1326       18592 :     Factor();
    1327       37184 :     while (mpToken->GetOpCode() == ocRange)
    1328             :     {
    1329           0 :         FormulaToken** pCode1 = pCode - 1;
    1330           0 :         FormulaTokenRef p = mpToken;
    1331           0 :         NextToken();
    1332           0 :         Factor();
    1333           0 :         FormulaToken** pCode2 = pCode - 1;
    1334           0 :         if (!MergeRangeReference( pCode1, pCode2))
    1335           0 :             PutCode(p);
    1336           0 :     }
    1337       18592 : }
    1338             : 
    1339             : //---------------------------------------------------------------------------
    1340             : 
    1341       18592 : void FormulaCompiler::IntersectionLine()
    1342             : {
    1343       18592 :     RangeLine();
    1344       37184 :     while (mpToken->GetOpCode() == ocIntersect)
    1345             :     {
    1346           0 :         FormulaTokenRef p = mpToken;
    1347           0 :         NextToken();
    1348           0 :         RangeLine();
    1349           0 :         PutCode(p);
    1350           0 :     }
    1351       18592 : }
    1352             : 
    1353             : //---------------------------------------------------------------------------
    1354             : 
    1355       18592 : void FormulaCompiler::UnionLine()
    1356             : {
    1357       18592 :     IntersectionLine();
    1358       37184 :     while (mpToken->GetOpCode() == ocUnion)
    1359             :     {
    1360           0 :         FormulaTokenRef p = mpToken;
    1361           0 :         NextToken();
    1362           0 :         IntersectionLine();
    1363           0 :         PutCode(p);
    1364           0 :     }
    1365       18592 : }
    1366             : 
    1367             : //---------------------------------------------------------------------------
    1368             : 
    1369       18668 : void FormulaCompiler::UnaryLine()
    1370             : {
    1371       18668 :     if( mpToken->GetOpCode() == ocAdd )
    1372           0 :         GetToken();
    1373       22465 :     else if (SC_OPCODE_START_UN_OP <= mpToken->GetOpCode() &&
    1374        3797 :             mpToken->GetOpCode() < SC_OPCODE_STOP_UN_OP)
    1375             :     {
    1376          76 :         FormulaTokenRef p = mpToken;
    1377          76 :         NextToken();
    1378          76 :         UnaryLine();
    1379          76 :         PutCode( p );
    1380             :     }
    1381             :     else
    1382       18592 :         UnionLine();
    1383       18668 : }
    1384             : 
    1385             : //---------------------------------------------------------------------------
    1386             : 
    1387       18592 : void FormulaCompiler::PostOpLine()
    1388             : {
    1389       18592 :     UnaryLine();
    1390       37184 :     while ( mpToken->GetOpCode() == ocPercentSign )
    1391             :     {   // this operator _follows_ its operand
    1392           0 :         PutCode( mpToken );
    1393           0 :         NextToken();
    1394             :     }
    1395       18592 : }
    1396             : 
    1397             : //---------------------------------------------------------------------------
    1398             : 
    1399       18590 : void FormulaCompiler::PowLine()
    1400             : {
    1401       18590 :     PostOpLine();
    1402       37182 :     while (mpToken->GetOpCode() == ocPow)
    1403             :     {
    1404           2 :         FormulaTokenRef p = mpToken;
    1405           2 :         NextToken();
    1406           2 :         PostOpLine();
    1407           2 :         PutCode(p);
    1408           2 :     }
    1409       18590 : }
    1410             : 
    1411             : //---------------------------------------------------------------------------
    1412             : 
    1413       16674 : void FormulaCompiler::MulDivLine()
    1414             : {
    1415       16674 :     PowLine();
    1416       35264 :     while (mpToken->GetOpCode() == ocMul || mpToken->GetOpCode() == ocDiv)
    1417             :     {
    1418        1916 :         FormulaTokenRef p = mpToken;
    1419        1916 :         NextToken();
    1420        1916 :         PowLine();
    1421        1916 :         PutCode(p);
    1422        1916 :     }
    1423       16674 : }
    1424             : 
    1425             : //---------------------------------------------------------------------------
    1426             : 
    1427       13112 : void FormulaCompiler::AddSubLine()
    1428             : {
    1429       13112 :     MulDivLine();
    1430       29786 :     while (mpToken->GetOpCode() == ocAdd || mpToken->GetOpCode() == ocSub)
    1431             :     {
    1432        3562 :         FormulaTokenRef p = mpToken;
    1433        3562 :         NextToken();
    1434        3562 :         MulDivLine();
    1435        3562 :         PutCode(p);
    1436        3562 :     }
    1437       13112 : }
    1438             : 
    1439             : //---------------------------------------------------------------------------
    1440             : 
    1441       13112 : void FormulaCompiler::ConcatLine()
    1442             : {
    1443       13112 :     AddSubLine();
    1444       26224 :     while (mpToken->GetOpCode() == ocAmpersand)
    1445             :     {
    1446           0 :         FormulaTokenRef p = mpToken;
    1447           0 :         NextToken();
    1448           0 :         AddSubLine();
    1449           0 :         PutCode(p);
    1450           0 :     }
    1451       13112 : }
    1452             : 
    1453             : //---------------------------------------------------------------------------
    1454             : 
    1455       13070 : void FormulaCompiler::CompareLine()
    1456             : {
    1457       13070 :     ConcatLine();
    1458       26182 :     while (mpToken->GetOpCode() >= ocEqual && mpToken->GetOpCode() <= ocGreaterEqual)
    1459             :     {
    1460          42 :         FormulaTokenRef p = mpToken;
    1461          42 :         NextToken();
    1462          42 :         ConcatLine();
    1463          42 :         PutCode(p);
    1464          42 :     }
    1465       13070 : }
    1466             : 
    1467             : //---------------------------------------------------------------------------
    1468             : 
    1469       13070 : void FormulaCompiler::NotLine()
    1470             : {
    1471       13070 :     CompareLine();
    1472       26140 :     while (mpToken->GetOpCode() == ocNot)
    1473             :     {
    1474           0 :         FormulaTokenRef p = mpToken;
    1475           0 :         NextToken();
    1476           0 :         CompareLine();
    1477           0 :         PutCode(p);
    1478           0 :     }
    1479       13070 : }
    1480             : 
    1481             : //---------------------------------------------------------------------------
    1482             : 
    1483       13070 : OpCode FormulaCompiler::Expression()
    1484             : {
    1485             :     static const short nRecursionMax = 42;
    1486       13070 :     FormulaCompilerRecursionGuard aRecursionGuard( nRecursion );
    1487       13070 :     if ( nRecursion > nRecursionMax )
    1488             :     {
    1489           0 :         SetError( errStackOverflow );
    1490           0 :         return ocStop;      //! generate token instead?
    1491             :     }
    1492       13070 :     NotLine();
    1493       26140 :     while (mpToken->GetOpCode() == ocAnd || mpToken->GetOpCode() == ocOr)
    1494             :     {
    1495           0 :         FormulaTokenRef p = mpToken;
    1496           0 :         mpToken->SetByte( 2 );       // 2 parameters!
    1497           0 :         NextToken();
    1498           0 :         NotLine();
    1499           0 :         PutCode(p);
    1500           0 :     }
    1501       13070 :     return mpToken->GetOpCode();
    1502             : }
    1503             : // -----------------------------------------------------------------------------
    1504           0 : void FormulaCompiler::SetError(sal_uInt16 /*nError*/)
    1505             : {
    1506           0 : }
    1507             : // -----------------------------------------------------------------------------
    1508           0 : FormulaTokenRef FormulaCompiler::ExtendRangeReference( FormulaToken & /*rTok1*/, FormulaToken & /*rTok2*/, bool /*bReuseDoubleRef*/ )
    1509             : {
    1510           0 :     return FormulaTokenRef();
    1511             : }
    1512             : // -----------------------------------------------------------------------------
    1513           0 : bool FormulaCompiler::MergeRangeReference(FormulaToken * * const pCode1, FormulaToken * const * const pCode2 )
    1514             : {
    1515             :     FormulaToken *p1, *p2;
    1516           0 :     if (pc < 2 || !pCode1 || !pCode2 ||
    1517           0 :             (pCode2 - pCode1 != 1) || (pCode - pCode2 != 1) ||
    1518           0 :             ((p1 = *pCode1) == 0) || ((p2 = *pCode2) == 0) )
    1519           0 :         return false;
    1520             : 
    1521           0 :     FormulaTokenRef p = ExtendRangeReference( *p1, *p2, true);
    1522           0 :     if (!p)
    1523           0 :         return false;
    1524             : 
    1525           0 :     p->IncRef();
    1526           0 :     p1->DecRef();
    1527           0 :     p2->DecRef();
    1528           0 :     *pCode1 = p.get();
    1529           0 :     --pCode, --pc;
    1530           0 :     pArr->nRefs--;
    1531             : 
    1532           0 :     return true;
    1533             : }
    1534             : // -----------------------------------------------------------------------------
    1535        5046 : bool FormulaCompiler::CompileTokenArray()
    1536             : {
    1537        5046 :     glSubTotal = false;
    1538        5046 :     bCorrected = false;
    1539        5046 :     if( !pArr->GetCodeError() || bIgnoreErrors )
    1540             :     {
    1541        5046 :         if ( bAutoCorrect )
    1542             :         {
    1543           0 :             aCorrectedFormula.Erase();
    1544           0 :             aCorrectedSymbol.Erase();
    1545             :         }
    1546        5046 :         pArr->nRefs = 0;    // count from start
    1547        5046 :         pArr->DelRPN();
    1548        5046 :         pStack = NULL;
    1549             :         FormulaToken* pData[ FORMULA_MAXTOKENS ];
    1550        5046 :         pCode = pData;
    1551        5046 :         bool bWasForced = pArr->IsRecalcModeForced();
    1552        5046 :         if ( bWasForced )
    1553             :         {
    1554           9 :             if ( bAutoCorrect )
    1555           0 :                 aCorrectedFormula = '=';
    1556             :         }
    1557        5046 :         pArr->ClearRecalcMode();
    1558        5046 :         pArr->Reset();
    1559        5046 :         eLastOp = ocOpen;
    1560        5046 :         pc = 0;
    1561        5046 :         NextToken();
    1562        5046 :         OpCode eOp = Expression();
    1563             :         // Some trailing garbage that doesn't form an expression?
    1564        5046 :         if (eOp != ocStop)
    1565         396 :             SetError( errOperatorExpected);
    1566             : 
    1567        5046 :         sal_uInt16 nErrorBeforePop = pArr->GetCodeError();
    1568             : 
    1569       10092 :         while( pStack )
    1570           0 :             PopTokenArray();
    1571        5046 :         if( pc )
    1572             :         {
    1573        4642 :             pArr->pRPN = new FormulaToken*[ pc ];
    1574        4642 :             pArr->nRPN = pc;
    1575        4642 :             memcpy( pArr->pRPN, pData, pc * sizeof( FormulaToken* ) );
    1576             :         }
    1577             : 
    1578             :         // once an error, always an error
    1579        5046 :         if( !pArr->GetCodeError() && nErrorBeforePop )
    1580           0 :             pArr->SetCodeError( nErrorBeforePop);
    1581             : 
    1582        5046 :         if( pArr->GetCodeError() && !bIgnoreErrors )
    1583             :         {
    1584         405 :             pArr->DelRPN();
    1585         405 :             pArr->SetHyperLink(false);
    1586             :         }
    1587             : 
    1588        5046 :         if ( bWasForced )
    1589           9 :             pArr->SetRecalcModeForced();
    1590             :     }
    1591        5046 :     if( nNumFmt == NUMBERFORMAT_UNDEFINED )
    1592        3081 :         nNumFmt = NUMBERFORMAT_NUMBER;
    1593        5046 :     return glSubTotal;
    1594             : }
    1595             : // -----------------------------------------------------------------------------
    1596         204 : void FormulaCompiler::PopTokenArray()
    1597             : {
    1598         204 :     if( pStack )
    1599             :     {
    1600         204 :         FormulaArrayStack* p = pStack;
    1601         204 :         pStack = p->pNext;
    1602         204 :         p->pArr->nRefs = sal::static_int_cast<short>( p->pArr->nRefs + pArr->nRefs );
    1603             :         // obtain special RecalcMode from SharedFormula
    1604         204 :         if ( pArr->IsRecalcModeAlways() )
    1605           0 :             p->pArr->SetExclusiveRecalcModeAlways();
    1606         204 :         else if ( !pArr->IsRecalcModeNormal() && p->pArr->IsRecalcModeNormal() )
    1607           0 :             p->pArr->SetMaskedRecalcMode( pArr->GetRecalcMode() );
    1608         204 :         p->pArr->SetCombinedBitsRecalcMode( pArr->GetRecalcMode() );
    1609         204 :         if( p->bTemp )
    1610         204 :             delete pArr;
    1611         204 :         pArr = p->pArr;
    1612         204 :         delete p;
    1613             :     }
    1614         204 : }
    1615             : // -----------------------------------------------------------------------------
    1616         149 : void FormulaCompiler::CreateStringFromTokenArray( String& rFormula )
    1617             : {
    1618         149 :     OUStringBuffer aBuffer( pArr->GetLen() * 5 );
    1619         149 :     CreateStringFromTokenArray( aBuffer );
    1620         149 :     rFormula = aBuffer.makeStringAndClear();
    1621         149 : }
    1622             : 
    1623         416 : void FormulaCompiler::CreateStringFromTokenArray( OUStringBuffer& rBuffer )
    1624             : {
    1625         416 :     rBuffer.setLength(0);
    1626         416 :     if( !pArr->GetLen() )
    1627         416 :         return;
    1628             : 
    1629         416 :     FormulaTokenArray* pSaveArr = pArr;
    1630         416 :     bool bODFF = FormulaGrammar::isODFF( meGrammar);
    1631         416 :     if (bODFF || FormulaGrammar::isPODF( meGrammar) )
    1632             :     {
    1633             :         // Scan token array for missing args and re-write if present.
    1634         209 :         MissingConvention aConv( bODFF);
    1635         209 :         if (pArr->NeedsPofRewrite( aConv))
    1636           0 :             pArr = pArr->RewriteMissingToPof( aConv);
    1637             :     }
    1638             : 
    1639             :     // At least one character per token, plus some are references, some are
    1640             :     // function names, some are numbers, ...
    1641         416 :     rBuffer.ensureCapacity( pArr->GetLen() * 5 );
    1642             : 
    1643         416 :     if ( pArr->IsRecalcModeForced() )
    1644           0 :         rBuffer.append(sal_Unicode('='));
    1645         416 :     FormulaToken* t = pArr->First();
    1646        1992 :     while( t )
    1647        1160 :         t = CreateStringFromToken( rBuffer, t, true );
    1648             : 
    1649         416 :     if (pSaveArr != pArr)
    1650             :     {
    1651           0 :         delete pArr;
    1652           0 :         pArr = pSaveArr;
    1653             :     }
    1654             : }
    1655             : // -----------------------------------------------------------------------------
    1656       24008 : FormulaToken* FormulaCompiler::CreateStringFromToken( String& rFormula, FormulaToken* pTokenP,bool bAllowArrAdvance )
    1657             : {
    1658       24008 :     OUStringBuffer aBuffer;
    1659       24008 :     FormulaToken* p = CreateStringFromToken( aBuffer, pTokenP, bAllowArrAdvance );
    1660       24008 :     rFormula += aBuffer.makeStringAndClear();
    1661       24008 :     return p;
    1662             : }
    1663             : 
    1664       25168 : FormulaToken* FormulaCompiler::CreateStringFromToken( OUStringBuffer& rBuffer, FormulaToken* pTokenP,bool bAllowArrAdvance )
    1665             : {
    1666       25168 :     bool bNext = true;
    1667       25168 :     bool bSpaces = false;
    1668       25168 :     FormulaToken* t = pTokenP;
    1669       25168 :     OpCode eOp = t->GetOpCode();
    1670       25168 :     if( eOp >= ocAnd && eOp <= ocOr )
    1671             :     {
    1672             :         // AND, OR infix?
    1673          20 :         if ( bAllowArrAdvance )
    1674          20 :             t = pArr->Next();
    1675             :         else
    1676           0 :             t = pArr->PeekNext();
    1677          20 :         bNext = false;
    1678          20 :         bSpaces = ( !t || t->GetOpCode() != ocOpen );
    1679             :     }
    1680       25168 :     if( bSpaces )
    1681           0 :         rBuffer.append(sal_Unicode(' '));
    1682             : 
    1683       25168 :     if( eOp == ocSpaces )
    1684             :     {
    1685           4 :         bool bIntersectionOp = mxSymbols->isODFF();
    1686           4 :         if (bIntersectionOp)
    1687             :         {
    1688           0 :             const FormulaToken* p = pArr->PeekPrevNoSpaces();
    1689           0 :             bIntersectionOp = (p && p->GetOpCode() == ocColRowName);
    1690           0 :             if (bIntersectionOp)
    1691             :             {
    1692           0 :                 p = pArr->PeekNextNoSpaces();
    1693           0 :                 bIntersectionOp = (p && p->GetOpCode() == ocColRowName);
    1694             :             }
    1695             :         }
    1696           4 :         if (bIntersectionOp)
    1697           0 :             rBuffer.appendAscii( "!!");
    1698             :         else
    1699             :         {
    1700             :             // most times it's just one blank
    1701           4 :             sal_uInt8 n = t->GetByte();
    1702           8 :             for ( sal_uInt8 j=0; j<n; ++j )
    1703             :             {
    1704           4 :                 rBuffer.append(sal_Unicode(' '));
    1705             :             }
    1706             :         }
    1707             :     }
    1708       25164 :     else if( eOp >= ocInternalBegin && eOp <= ocInternalEnd )
    1709           0 :         rBuffer.appendAscii( pInternal[ eOp - ocInternalBegin ] );
    1710       25164 :     else if( (sal_uInt16) eOp < mxSymbols->getSymbolCount())        // Keyword:
    1711       25164 :         rBuffer.append(mxSymbols->getSymbol(eOp));
    1712             :     else
    1713             :     {
    1714             :         SAL_WARN( "formula.core","unknown OpCode");
    1715           0 :         rBuffer.append(GetNativeSymbol( ocErrName ));
    1716             :     }
    1717       25168 :     if( bNext )
    1718             :     {
    1719       25148 :         if (t->IsExternalRef())
    1720             :         {
    1721           4 :             CreateStringFromExternal(rBuffer, pTokenP);
    1722             :         }
    1723             :         else
    1724             :         {
    1725       25144 :             switch( t->GetType() )
    1726             :             {
    1727             :             case svDouble:
    1728         203 :                 AppendDouble( rBuffer, t->GetDouble() );
    1729         203 :             break;
    1730             : 
    1731             :             case svString:
    1732          74 :                 if( eOp == ocBad || eOp == ocStringXML )
    1733          32 :                     rBuffer.append(t->GetString());
    1734             :                 else
    1735          42 :                     AppendString( rBuffer, t->GetString() );
    1736          74 :                 break;
    1737             :             case svSingleRef:
    1738       12398 :                 CreateStringFromSingleRef(rBuffer,t);
    1739       12398 :                 break;
    1740             :             case svDoubleRef:
    1741       11947 :                 CreateStringFromDoubleRef(rBuffer,t);
    1742       11947 :                 break;
    1743             :             case svMatrix:
    1744           0 :                 CreateStringFromMatrix( rBuffer, t );
    1745           0 :                 break;
    1746             : 
    1747             :             case svIndex:
    1748           6 :                 CreateStringFromIndex( rBuffer, t );
    1749           6 :                 break;
    1750             :             case svExternal:
    1751             :             {
    1752             :                 // mapped or translated name of AddIns
    1753           0 :                 String aAddIn( t->GetExternal() );
    1754           0 :                 bool bMapped = mxSymbols->isPODF();     // ODF 1.1 directly uses programmatical name
    1755           0 :                 if (!bMapped && mxSymbols->hasExternals())
    1756             :                 {
    1757           0 :                     ExternalHashMap::const_iterator iLook = mxSymbols->getReverseExternalHashMap()->find( aAddIn);
    1758           0 :                     if (iLook != mxSymbols->getReverseExternalHashMap()->end())
    1759             :                     {
    1760           0 :                         aAddIn = (*iLook).second;
    1761           0 :                         bMapped = true;
    1762             :                     }
    1763             :                 }
    1764           0 :                 if (!bMapped && !mxSymbols->isEnglish())
    1765           0 :                         LocalizeString( aAddIn );
    1766           0 :                     rBuffer.append(aAddIn);
    1767             :                 }
    1768           0 :             break;
    1769             :             case svError:
    1770           0 :                 AppendErrorConstant( rBuffer, t->GetError());
    1771           0 :             break;
    1772             :             case svByte:
    1773             :             case svJump:
    1774             :             case svFAP:
    1775             :             case svMissing:
    1776             :             case svSep:
    1777         516 :                 break;      // Opcodes
    1778             :             default:
    1779             :                 OSL_FAIL("FormulaCompiler:: GetStringFromToken errUnknownVariable");
    1780             :             } // of switch
    1781             :         }
    1782             :     }
    1783       25168 :     if( bSpaces )
    1784           0 :         rBuffer.append(sal_Unicode(' '));
    1785       25168 :     if ( bAllowArrAdvance )
    1786             :     {
    1787        1160 :         if( bNext )
    1788        1140 :             t = pArr->Next();
    1789        1160 :         return t;
    1790             :     }
    1791       24008 :     return pTokenP;
    1792             : }
    1793             : // -----------------------------------------------------------------------------
    1794             : 
    1795         203 : void FormulaCompiler::AppendDouble( OUStringBuffer& rBuffer, double fVal )
    1796             : {
    1797         203 :     if ( mxSymbols->isEnglish() )
    1798             :     {
    1799             :         ::rtl::math::doubleToUStringBuffer( rBuffer, fVal,
    1800             :                 rtl_math_StringFormat_Automatic,
    1801         182 :                 rtl_math_DecimalPlaces_Max, '.', true );
    1802             :     }
    1803             :     else
    1804             :     {
    1805          21 :         SvtSysLocale aSysLocale;
    1806             :         ::rtl::math::doubleToUStringBuffer( rBuffer, fVal,
    1807             :                 rtl_math_StringFormat_Automatic,
    1808             :                 rtl_math_DecimalPlaces_Max,
    1809          21 :                 aSysLocale.GetLocaleDataPtr()->getNumDecimalSep()[0],
    1810          21 :                 true );
    1811             :     }
    1812         203 : }
    1813             : // -----------------------------------------------------------------------------
    1814           0 : void FormulaCompiler::AppendBoolean( OUStringBuffer& rBuffer, bool bVal )
    1815             : {
    1816           0 :     rBuffer.append( mxSymbols->getSymbol(static_cast<OpCode>(bVal ? ocTrue : ocFalse)) );
    1817           0 : }
    1818             : // -----------------------------------------------------------------------------
    1819          42 : void FormulaCompiler::AppendString( OUStringBuffer& rBuffer, const String & rStr )
    1820             : {
    1821          42 :     rBuffer.append(sal_Unicode('"'));
    1822          42 :     if ( lcl_UnicodeStrChr( rStr.GetBuffer(), '"' ) == NULL )
    1823          42 :         rBuffer.append( rStr );
    1824             :     else
    1825             :     {
    1826           0 :         String aStr( rStr );
    1827           0 :         aStr.SearchAndReplaceAll( OUString('"'), OUString("\"\"") );
    1828           0 :         rBuffer.append(aStr);
    1829             :     }
    1830          42 :     rBuffer.append(sal_Unicode('"'));
    1831          42 : }
    1832             : 
    1833         313 : void FormulaCompiler::UpdateSeparatorsNative(
    1834             :     const OUString& rSep, const OUString& rArrayColSep, const OUString& rArrayRowSep )
    1835             : {
    1836         313 :     NonConstOpCodeMapPtr xSymbolsNative;
    1837         313 :     lcl_fillNativeSymbols(xSymbolsNative);
    1838         313 :     xSymbolsNative->putOpCode(rSep, ocSep);
    1839         313 :     xSymbolsNative->putOpCode(rArrayColSep, ocArrayColSep);
    1840         313 :     xSymbolsNative->putOpCode(rArrayRowSep, ocArrayRowSep);
    1841         313 : }
    1842             : 
    1843         313 : void FormulaCompiler::ResetNativeSymbols()
    1844             : {
    1845         313 :     NonConstOpCodeMapPtr xSymbolsNative;
    1846         313 :     lcl_fillNativeSymbols(xSymbolsNative, true);
    1847         313 :     lcl_fillNativeSymbols(xSymbolsNative);
    1848         313 : }
    1849             : 
    1850           0 : void FormulaCompiler::SetNativeSymbols( const OpCodeMapPtr& xMap )
    1851             : {
    1852           0 :     NonConstOpCodeMapPtr xSymbolsNative;
    1853           0 :     lcl_fillNativeSymbols(xSymbolsNative);
    1854           0 :     xSymbolsNative->copyFrom(*xMap);
    1855           0 : }
    1856             : 
    1857             : // -----------------------------------------------------------------------------
    1858       40701 : OpCode FormulaCompiler::NextToken()
    1859             : {
    1860       40701 :     if( !GetToken() )
    1861        4642 :         return ocStop;
    1862       36059 :     OpCode eOp = mpToken->GetOpCode();
    1863             :     // There must be an operator before a push
    1864       49334 :     if ( (eOp == ocPush || eOp == ocColRowNameAuto) &&
    1865       19392 :             !( (eLastOp == ocOpen) || (eLastOp == ocSep) ||
    1866        2866 :                 (SC_OPCODE_START_BIN_OP <= eLastOp && eLastOp < SC_OPCODE_STOP_UN_OP)) )
    1867           0 :         SetError(errOperatorExpected);
    1868             :     // Operator and Plus => operator
    1869       38814 :     if (eOp == ocAdd && (eLastOp == ocOpen || eLastOp == ocSep ||
    1870        2755 :                 (SC_OPCODE_START_BIN_OP <= eLastOp && eLastOp < SC_OPCODE_STOP_UN_OP)))
    1871           0 :         eOp = NextToken();
    1872             :     else
    1873             :     {
    1874             :         // Before an operator there must not be another operator, with the
    1875             :         // exception of AND and OR.
    1876       36059 :         if ( eOp != ocAnd && eOp != ocOr &&
    1877        9319 :                 (SC_OPCODE_START_BIN_OP <= eOp && eOp < SC_OPCODE_STOP_BIN_OP )
    1878       11035 :                 && (eLastOp == ocOpen || eLastOp == ocSep ||
    1879        5513 :                     (SC_OPCODE_START_BIN_OP <= eLastOp && eLastOp < SC_OPCODE_STOP_UN_OP)))
    1880             :         {
    1881           9 :             SetError(errVariableExpected);
    1882           9 :             if ( bAutoCorrect && !pStack )
    1883             :             {
    1884           0 :                 if ( eOp == eLastOp || eLastOp == ocOpen )
    1885             :                 {   // throw away duplicated operator
    1886           0 :                     aCorrectedSymbol.Erase();
    1887           0 :                     bCorrected = true;
    1888             :                 }
    1889             :                 else
    1890             :                 {
    1891           0 :                     xub_StrLen nPos = aCorrectedFormula.Len();
    1892           0 :                     if ( nPos )
    1893             :                     {
    1894           0 :                         nPos--;
    1895           0 :                         sal_Unicode c = aCorrectedFormula.GetChar( nPos );
    1896           0 :                         switch ( eOp )
    1897             :                         {   // swap operators
    1898             :                             case ocGreater:
    1899           0 :                                 if ( c == mxSymbols->getSymbol(ocEqual).GetChar(0) )
    1900             :                                 {   // >= instead of =>
    1901             :                                     aCorrectedFormula.SetChar( nPos,
    1902           0 :                                         mxSymbols->getSymbol(ocGreater).GetChar(0) );
    1903           0 :                                     aCorrectedSymbol = c;
    1904           0 :                                     bCorrected = true;
    1905             :                                 }
    1906           0 :                             break;
    1907             :                             case ocLess:
    1908           0 :                                 if ( c == mxSymbols->getSymbol(ocEqual).GetChar(0) )
    1909             :                                 {   // <= instead of =<
    1910             :                                     aCorrectedFormula.SetChar( nPos,
    1911           0 :                                         mxSymbols->getSymbol(ocLess).GetChar(0) );
    1912           0 :                                     aCorrectedSymbol = c;
    1913           0 :                                     bCorrected = true;
    1914             :                                 }
    1915           0 :                                 else if ( c == mxSymbols->getSymbol(ocGreater).GetChar(0) )
    1916             :                                 {   // <> instead of ><
    1917             :                                     aCorrectedFormula.SetChar( nPos,
    1918           0 :                                         mxSymbols->getSymbol(ocLess).GetChar(0) );
    1919           0 :                                     aCorrectedSymbol = c;
    1920           0 :                                     bCorrected = true;
    1921             :                                 }
    1922           0 :                             break;
    1923             :                             case ocMul:
    1924           0 :                                 if ( c == mxSymbols->getSymbol(ocSub).GetChar(0) )
    1925             :                                 {   // *- instead of -*
    1926             :                                     aCorrectedFormula.SetChar( nPos,
    1927           0 :                                         mxSymbols->getSymbol(ocMul).GetChar(0) );
    1928           0 :                                     aCorrectedSymbol = c;
    1929           0 :                                     bCorrected = true;
    1930             :                                 }
    1931           0 :                             break;
    1932             :                             case ocDiv:
    1933           0 :                                 if ( c == mxSymbols->getSymbol(ocSub).GetChar(0) )
    1934             :                                 {   // /- instead of -/
    1935             :                                     aCorrectedFormula.SetChar( nPos,
    1936           0 :                                         mxSymbols->getSymbol(ocDiv).GetChar(0) );
    1937           0 :                                     aCorrectedSymbol = c;
    1938           0 :                                     bCorrected = true;
    1939             :                                 }
    1940           0 :                             break;
    1941             :                             default:
    1942             :                                 ;   // nothing
    1943             :                         }
    1944             :                     }
    1945             :                 }
    1946             :             }
    1947             :         }
    1948       36059 :         eLastOp = eOp;
    1949             :     }
    1950       36059 :     return eOp;
    1951             : }
    1952       22827 : void FormulaCompiler::PutCode( FormulaTokenRef& p )
    1953             : {
    1954       22827 :     if( pc >= FORMULA_MAXTOKENS - 1 )
    1955             :     {
    1956           0 :         if ( pc == FORMULA_MAXTOKENS - 1 )
    1957             :         {
    1958           0 :             p = new FormulaByteToken( ocStop );
    1959           0 :             p->IncRef();
    1960           0 :             *pCode++ = p.get();
    1961           0 :             ++pc;
    1962             :         }
    1963           0 :         SetError(errCodeOverflow);
    1964           0 :         return;
    1965             :     }
    1966       22827 :     if( pArr->GetCodeError() && !bCompileForFAP )
    1967           9 :         return;
    1968       22818 :     ForceArrayOperator( p, pCurrentFactorToken);
    1969       22818 :     p->IncRef();
    1970       22818 :     *pCode++ = p.get();
    1971       22818 :     pc++;
    1972             : }
    1973             : 
    1974             : // -----------------------------------------------------------------------------
    1975           0 : bool FormulaCompiler::HandleExternalReference(const FormulaToken& /*_aToken*/)
    1976             : {
    1977           0 :     return true;
    1978             : }
    1979             : // -----------------------------------------------------------------------------
    1980           0 : bool FormulaCompiler::HandleRange()
    1981             : {
    1982           0 :     return true;
    1983             : }
    1984             : // -----------------------------------------------------------------------------
    1985           0 : bool FormulaCompiler::HandleSingleRef()
    1986             : {
    1987           0 :     return true;
    1988             : }
    1989             : // -----------------------------------------------------------------------------
    1990           0 : bool FormulaCompiler::HandleDbData()
    1991             : {
    1992           0 :     return true;
    1993             : }
    1994             : // -----------------------------------------------------------------------------
    1995           0 : void FormulaCompiler::CreateStringFromSingleRef(OUStringBuffer& /*rBuffer*/,FormulaToken* /*pTokenP*/)
    1996             : {
    1997           0 : }
    1998             : // -----------------------------------------------------------------------------
    1999           0 : void FormulaCompiler::CreateStringFromDoubleRef(OUStringBuffer& /*rBuffer*/,FormulaToken* /*pTokenP*/)
    2000             : {
    2001           0 : }
    2002             : // -----------------------------------------------------------------------------
    2003           0 : void FormulaCompiler::CreateStringFromIndex(OUStringBuffer& /*rBuffer*/,FormulaToken* /*pTokenP*/)
    2004             : {
    2005           0 : }
    2006             : // -----------------------------------------------------------------------------
    2007           0 : void FormulaCompiler::CreateStringFromMatrix(OUStringBuffer& /*rBuffer*/,FormulaToken* /*pTokenP*/)
    2008             : {
    2009           0 : }
    2010             : // -----------------------------------------------------------------------------
    2011           0 : void FormulaCompiler::CreateStringFromExternal(OUStringBuffer& /*rBuffer*/,FormulaToken* /*pTokenP*/)
    2012             : {
    2013           0 : }
    2014             : // -----------------------------------------------------------------------------
    2015           0 : void FormulaCompiler::LocalizeString( String& /*rName*/ )
    2016             : {
    2017           0 : }
    2018         204 : void FormulaCompiler::PushTokenArray( FormulaTokenArray* pa, bool bTemp )
    2019             : {
    2020         204 :     if ( bAutoCorrect && !pStack )
    2021             :     {   // don't merge stacked subroutine code into entered formula
    2022           0 :         aCorrectedFormula += aCorrectedSymbol;
    2023           0 :         aCorrectedSymbol.Erase();
    2024             :     }
    2025         204 :     FormulaArrayStack* p = new FormulaArrayStack;
    2026         204 :     p->pNext      = pStack;
    2027         204 :     p->pArr       = pArr;
    2028         204 :     p->bTemp      = bTemp;
    2029         204 :     pStack        = p;
    2030         204 :     pArr          = pa;
    2031         204 : }
    2032             : 
    2033             : // =============================================================================
    2034             : } // formula
    2035             : // =============================================================================
    2036             : 
    2037             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.10