1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
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"
27 #include "osl/mutex.hxx"
29 #include <svl/zforlist.hxx>
30 #include <tools/rc.hxx>
31 #include <tools/rcid.h>
32 #include <com/sun/star/sheet/FormulaOpCodeMapEntry.hpp>
33 #include <com/sun/star/sheet/FormulaMapGroup.hpp>
34 #include <com/sun/star/sheet/FormulaMapGroupSpecialOffset.hpp>
35 #include <rtl/strbuf.hxx>
40 using namespace ::com::sun::star
;
42 static const sal_Char
* pInternal
[2] = { "TTT", "__DEBUG_VAR" };
46 class FormulaCompilerRecursionGuard
51 FormulaCompilerRecursionGuard( short& rRec
)
52 : rRecursion( rRec
) { ++rRecursion
; }
53 ~FormulaCompilerRecursionGuard() { --rRecursion
; }
56 short lcl_GetRetFormat( OpCode eOpCode
)
85 return css::util::NumberFormat::LOGICAL
;
89 return css::util::NumberFormat::DATE
;
91 return css::util::NumberFormat::DATETIME
;
93 return css::util::NumberFormat::TIME
;
107 return css::util::NumberFormat::CURRENCY
;
115 return css::util::NumberFormat::PERCENT
;
117 return css::util::NumberFormat::NUMBER
;
121 inline void lclPushOpCodeMapEntry( ::std::vector
< sheet::FormulaOpCodeMapEntry
>& rVec
,
122 const OUString
* pTable
, sal_uInt16 nOpCode
)
124 sheet::FormulaOpCodeMapEntry aEntry
;
125 aEntry
.Token
.OpCode
= nOpCode
;
126 aEntry
.Name
= pTable
[nOpCode
];
127 rVec
.push_back( aEntry
);
130 void lclPushOpCodeMapEntries( ::std::vector
< sheet::FormulaOpCodeMapEntry
>& rVec
,
131 const OUString
* pTable
, sal_uInt16 nOpCodeBeg
, sal_uInt16 nOpCodeEnd
)
133 for (sal_uInt16 nOpCode
= nOpCodeBeg
; nOpCode
< nOpCodeEnd
; ++nOpCode
)
134 lclPushOpCodeMapEntry( rVec
, pTable
, nOpCode
);
137 void lclPushOpCodeMapEntries( ::std::vector
< sheet::FormulaOpCodeMapEntry
>& rVec
,
138 const OUString
* pTable
, const sal_uInt16
* pnOpCodes
, size_t nCount
)
140 for (const sal_uInt16
* pnEnd
= pnOpCodes
+ nCount
; pnOpCodes
< pnEnd
; ++pnOpCodes
)
141 lclPushOpCodeMapEntry( rVec
, pTable
, *pnOpCodes
);
144 class OpCodeList
: public Resource
// temp object for resource
148 OpCodeList( sal_uInt16
, FormulaCompiler::NonConstOpCodeMapPtr
,
149 FormulaCompiler::SeparatorType
= FormulaCompiler::SEMICOLON_BASE
);
152 bool getOpCodeString( OUString
& rStr
, sal_uInt16 nOp
);
153 void putDefaultOpCode( FormulaCompiler::NonConstOpCodeMapPtr xMap
, sal_uInt16 nOp
, const CharClass
* pCharClass
);
156 FormulaCompiler::SeparatorType meSepType
;
159 OpCodeList::OpCodeList( sal_uInt16 nRID
, FormulaCompiler::NonConstOpCodeMapPtr xMap
,
160 FormulaCompiler::SeparatorType eSepType
) :
161 Resource( ResId( nRID
, *ResourceManager::getResManager()))
162 , meSepType( eSepType
)
164 SvtSysLocale aSysLocale
;
165 const CharClass
* pCharClass
= (xMap
->isEnglish() ? NULL
: aSysLocale
.GetCharClassPtr());
166 if (meSepType
== FormulaCompiler::RESOURCE_BASE
)
168 for (sal_uInt16 i
= 0; i
<= SC_OPCODE_LAST_OPCODE_ID
; ++i
)
170 putDefaultOpCode( xMap
, i
, pCharClass
);
175 for (sal_uInt16 i
= 0; i
<= SC_OPCODE_LAST_OPCODE_ID
; ++i
)
178 if ( getOpCodeString( aOpStr
, i
) )
179 xMap
->putOpCode( aOpStr
, OpCode(i
), pCharClass
);
181 putDefaultOpCode( xMap
, i
, pCharClass
);
188 bool OpCodeList::getOpCodeString( OUString
& rStr
, sal_uInt16 nOp
)
194 if (meSepType
== FormulaCompiler::COMMA_BASE
)
199 else if (meSepType
== FormulaCompiler::SEMICOLON_BASE
)
206 case SC_OPCODE_ARRAY_COL_SEP
:
208 if (meSepType
== FormulaCompiler::COMMA_BASE
)
213 else if (meSepType
== FormulaCompiler::SEMICOLON_BASE
)
220 case SC_OPCODE_ARRAY_ROW_SEP
:
222 if (meSepType
== FormulaCompiler::COMMA_BASE
)
227 else if (meSepType
== FormulaCompiler::SEMICOLON_BASE
)
239 void OpCodeList::putDefaultOpCode( FormulaCompiler::NonConstOpCodeMapPtr xMap
, sal_uInt16 nOp
,
240 const CharClass
* pCharClass
)
242 ResId
aRes( nOp
, *ResourceManager::getResManager());
243 aRes
.SetRT( RSC_STRING
);
244 if (IsAvailableRes( aRes
))
245 xMap
->putOpCode( aRes
.toString(), OpCode( nOp
), pCharClass
);
249 const sal_Unicode
* lcl_UnicodeStrChr( const sal_Unicode
* pStr
, sal_Unicode c
)
264 FormulaCompiler::NonConstOpCodeMapPtr mxSymbolMap
;
272 void FormulaCompiler::OpCodeMap::putExternal( const OUString
& rSymbol
, const OUString
& rAddIn
)
274 // Different symbols may map to the same AddIn, but the same AddIn may not
275 // map to different symbols, the first pair wins. Same symbol of course may
276 // not map to different AddIns, again the first pair wins and also the
277 // AddIn->symbol mapping is not inserted in other cases.
278 bool bOk
= mpExternalHashMap
->insert( ExternalHashMap::value_type( rSymbol
, rAddIn
)).second
;
279 SAL_WARN_IF( !bOk
, "formula.core", "OpCodeMap::putExternal: symbol not inserted, " << rSymbol
<< " -> " << rAddIn
);
282 bOk
= mpReverseExternalHashMap
->insert( ExternalHashMap::value_type( rAddIn
, rSymbol
)).second
;
283 // Failed insertion of the AddIn is ok for different symbols mapping to
284 // the same AddIn. Make this INFO only.
285 SAL_INFO_IF( !bOk
, "formula.core", "OpCodeMap::putExternal: AddIn not inserted, " << rAddIn
<< " -> " << rSymbol
);
289 void FormulaCompiler::OpCodeMap::putExternalSoftly( const OUString
& rSymbol
, const OUString
& rAddIn
)
291 bool bOk
= mpReverseExternalHashMap
->insert( ExternalHashMap::value_type( rAddIn
, rSymbol
)).second
;
293 mpExternalHashMap
->insert( ExternalHashMap::value_type( rSymbol
, rAddIn
));
296 uno::Sequence
< sheet::FormulaToken
> FormulaCompiler::OpCodeMap::createSequenceOfFormulaTokens(
297 const FormulaCompiler
& rCompiler
, const uno::Sequence
< OUString
>& rNames
) const
299 const sal_Int32 nLen
= rNames
.getLength();
300 uno::Sequence
< sheet::FormulaToken
> aTokens( nLen
);
301 sheet::FormulaToken
* pToken
= aTokens
.getArray();
302 OUString
const * pName
= rNames
.getConstArray();
303 OUString
const * const pStop
= pName
+ nLen
;
304 for ( ; pName
< pStop
; ++pName
, ++pToken
)
306 OpCodeHashMap::const_iterator
iLook( mpHashMap
->find( *pName
));
307 if (iLook
!= mpHashMap
->end())
308 pToken
->OpCode
= (*iLook
).second
;
314 ExternalHashMap::const_iterator
iExt( mpExternalHashMap
->find( *pName
));
315 if (iExt
!= mpExternalHashMap
->end())
316 aIntName
= (*iExt
).second
;
317 // Check for existence not needed here, only name-mapping is of
320 if (aIntName
.isEmpty())
321 aIntName
= rCompiler
.FindAddInFunction(*pName
, !isEnglish()); // bLocalFirst=false for english
322 if (aIntName
.isEmpty())
323 pToken
->OpCode
= getOpCodeUnknown();
326 pToken
->OpCode
= ocExternal
;
327 pToken
->Data
<<= aIntName
;
334 uno::Sequence
< sheet::FormulaOpCodeMapEntry
> FormulaCompiler::OpCodeMap::createSequenceOfAvailableMappings(
335 const FormulaCompiler
& rCompiler
, const sal_Int32 nGroups
) const
337 using namespace sheet
;
339 // Unfortunately uno::Sequence can't grow without cumbersome reallocs. As
340 // we don't know in advance how many elements it will have we use a
341 // temporary vector to add elements and then copy to Sequence :-(
342 ::std::vector
< FormulaOpCodeMapEntry
> aVec
;
344 if (nGroups
== FormulaMapGroup::SPECIAL
)
346 // Use specific order, keep in sync with
347 // offapi/com/sun/star/sheet/FormulaMapGroupSpecialOffset.idl
353 { FormulaMapGroupSpecialOffset::PUSH
, ocPush
} ,
354 { FormulaMapGroupSpecialOffset::CALL
, ocCall
} ,
355 { FormulaMapGroupSpecialOffset::STOP
, ocStop
} ,
356 { FormulaMapGroupSpecialOffset::EXTERNAL
, ocExternal
} ,
357 { FormulaMapGroupSpecialOffset::NAME
, ocName
} ,
358 { FormulaMapGroupSpecialOffset::NO_NAME
, ocNoName
} ,
359 { FormulaMapGroupSpecialOffset::MISSING
, ocMissing
} ,
360 { FormulaMapGroupSpecialOffset::BAD
, ocBad
} ,
361 { FormulaMapGroupSpecialOffset::SPACES
, ocSpaces
} ,
362 { FormulaMapGroupSpecialOffset::MAT_REF
, ocMatRef
} ,
363 { FormulaMapGroupSpecialOffset::DB_AREA
, ocDBArea
} ,
364 /* TODO: { FormulaMapGroupSpecialOffset::TABLE_REF , ocTableRef } , */
365 { FormulaMapGroupSpecialOffset::MACRO
, ocMacro
} ,
366 { FormulaMapGroupSpecialOffset::COL_ROW_NAME
, ocColRowName
}
368 const size_t nCount
= sizeof(aMap
)/sizeof(aMap
[0]);
369 // Preallocate vector elements.
370 if (aVec
.size() < nCount
)
372 FormulaOpCodeMapEntry aEntry
;
373 aEntry
.Token
.OpCode
= getOpCodeUnknown();
374 aVec
.resize( nCount
, aEntry
);
375 } // if (aVec.size() < nCount)
377 FormulaOpCodeMapEntry aEntry
;
378 for (size_t i
=0; i
< nCount
; ++i
)
380 size_t nIndex
= static_cast< size_t >( aMap
[i
].nOff
);
381 if (aVec
.size() <= nIndex
)
383 // The offsets really should be aligned with the size, so if
384 // the vector was preallocated above this code to resize it is
385 // just a measure in case the table isn't in sync with the API,
386 // usually it isn't executed.
387 aEntry
.Token
.OpCode
= getOpCodeUnknown();
388 aVec
.resize( nIndex
+ 1, aEntry
);
390 aEntry
.Token
.OpCode
= aMap
[i
].eOp
;
391 aVec
[nIndex
] = aEntry
;
396 /* FIXME: Once we support error constants in formulas we'll need a map
397 * group for that, e.g. FormulaMapGroup::ERROR_CONSTANTS, and fill
398 * SC_OPCODE_START_ERRORS to SC_OPCODE_STOP_ERRORS. */
400 // Anything else but SPECIAL.
401 if ((nGroups
& FormulaMapGroup::SEPARATORS
) != 0)
403 static const sal_uInt16 aOpCodes
[] = {
408 lclPushOpCodeMapEntries( aVec
, mpTable
, aOpCodes
, sizeof(aOpCodes
)/sizeof(aOpCodes
[0]) );
410 if ((nGroups
& FormulaMapGroup::ARRAY_SEPARATORS
) != 0)
412 static const sal_uInt16 aOpCodes
[] = {
413 SC_OPCODE_ARRAY_OPEN
,
414 SC_OPCODE_ARRAY_CLOSE
,
415 SC_OPCODE_ARRAY_ROW_SEP
,
416 SC_OPCODE_ARRAY_COL_SEP
418 lclPushOpCodeMapEntries( aVec
, mpTable
, aOpCodes
, sizeof(aOpCodes
)/sizeof(aOpCodes
[0]) );
420 if ((nGroups
& FormulaMapGroup::UNARY_OPERATORS
) != 0)
422 // Due to the nature of the percent operator following its operand
423 // it isn't sorted into unary operators for compiler interna.
424 lclPushOpCodeMapEntry( aVec
, mpTable
, ocPercentSign
);
425 // "+" can be used as unary operator too, push only if binary group is not set
426 if ((nGroups
& FormulaMapGroup::BINARY_OPERATORS
) == 0)
427 lclPushOpCodeMapEntry( aVec
, mpTable
, ocAdd
);
428 // regular unary operators
429 for (sal_uInt16 nOp
= SC_OPCODE_START_UN_OP
; nOp
< SC_OPCODE_STOP_UN_OP
&& nOp
< mnSymbols
; ++nOp
)
433 // NOT and NEG in fact are functions but for legacy reasons
434 // are sorted into unary operators for compiler interna.
439 lclPushOpCodeMapEntry( aVec
, mpTable
, nOp
);
443 if ((nGroups
& FormulaMapGroup::BINARY_OPERATORS
) != 0)
445 for (sal_uInt16 nOp
= SC_OPCODE_START_BIN_OP
; nOp
< SC_OPCODE_STOP_BIN_OP
&& nOp
< mnSymbols
; ++nOp
)
449 // AND and OR in fact are functions but for legacy reasons
450 // are sorted into binary operators for compiler interna.
455 lclPushOpCodeMapEntry( aVec
, mpTable
, nOp
);
459 if ((nGroups
& FormulaMapGroup::FUNCTIONS
) != 0)
461 // Function names are not consecutive, skip the gaps between
462 // functions with no parameter, functions with 1 parameter
463 lclPushOpCodeMapEntries( aVec
, mpTable
, SC_OPCODE_START_NO_PAR
,
464 ::std::min
< sal_uInt16
>( SC_OPCODE_STOP_NO_PAR
, mnSymbols
) );
465 lclPushOpCodeMapEntries( aVec
, mpTable
, SC_OPCODE_START_1_PAR
,
466 ::std::min
< sal_uInt16
>( SC_OPCODE_STOP_1_PAR
, mnSymbols
) );
467 // Additional functions not within range of functions.
468 static const sal_uInt16 aOpCodes
[] = {
478 lclPushOpCodeMapEntries( aVec
, mpTable
, aOpCodes
, sizeof(aOpCodes
)/sizeof(aOpCodes
[0]) );
479 // functions with 2 or more parameters.
480 for (sal_uInt16 nOp
= SC_OPCODE_START_2_PAR
; nOp
< SC_OPCODE_STOP_2_PAR
&& nOp
< mnSymbols
; ++nOp
)
484 // NO_NAME is in SPECIAL.
485 case SC_OPCODE_NO_NAME
:
488 lclPushOpCodeMapEntry( aVec
, mpTable
, nOp
);
491 // If AddIn functions are present in this mapping, use them, and only those.
494 for (ExternalHashMap::const_iterator
it( mpExternalHashMap
->begin());it
!= mpExternalHashMap
->end(); ++it
)
496 FormulaOpCodeMapEntry aEntry
;
497 aEntry
.Name
= (*it
).first
;
498 aEntry
.Token
.Data
<<= OUString( (*it
).second
);
499 aEntry
.Token
.OpCode
= ocExternal
;
500 aVec
.push_back( aEntry
);
505 rCompiler
.fillAddInToken( aVec
, isEnglish());
509 return uno::Sequence
< FormulaOpCodeMapEntry
>(aVec
.data(), aVec
.size());
513 void FormulaCompiler::OpCodeMap::putOpCode( const OUString
& rStr
, const OpCode eOp
, const CharClass
* pCharClass
)
515 DBG_ASSERT( 0 < eOp
&& sal_uInt16(eOp
) < mnSymbols
, "OpCodeMap::putOpCode: OpCode out of range");
516 if (0 < eOp
&& sal_uInt16(eOp
) < mnSymbols
)
518 SAL_WARN_IF( !(mpTable
[eOp
].isEmpty() || (mpTable
[eOp
] == rStr
) ||
519 (eOp
== ocCurrency
) || (eOp
== ocSep
) || (eOp
== ocArrayColSep
) ||
520 (eOp
== ocArrayRowSep
)), "formula.core",
521 "OpCodeMap::putOpCode: reusing OpCode " << static_cast<sal_uInt16
>(eOp
)
522 << ", replacing '" << mpTable
[eOp
] << "' with '" << rStr
<< "' in "
523 << (mbEnglish
? "" : "non-") << "English map 0x" << ::std::hex
<< meGrammar
);
524 // Case preserving opcode -> string, upper string -> opcode
526 OUString
aUpper( pCharClass
? pCharClass
->uppercase( rStr
) : rStr
.toAsciiUpperCase());
527 mpHashMap
->insert( OpCodeHashMap::value_type( aUpper
, eOp
));
531 // class FormulaCompiler
533 FormulaCompiler::FormulaCompiler( FormulaTokenArray
& rArr
)
535 nCurrentFactorParam(0),
541 nNumFmt( css::util::NumberFormat::UNDEFINED
),
543 meGrammar( formula::FormulaGrammar::GRAM_UNSPECIFIED
),
544 bAutoCorrect( false ),
547 mbJumpCommandReorder(true),
552 FormulaCompiler::FormulaCompiler()
554 nCurrentFactorParam(0),
560 nNumFmt( css::util::NumberFormat::UNDEFINED
),
562 meGrammar( formula::FormulaGrammar::GRAM_UNSPECIFIED
),
563 bAutoCorrect( false ),
566 mbJumpCommandReorder(true),
571 FormulaCompiler::~FormulaCompiler()
575 FormulaCompiler::OpCodeMapPtr
FormulaCompiler::GetOpCodeMap( const sal_Int32 nLanguage
) const
577 FormulaCompiler::OpCodeMapPtr xMap
;
578 using namespace sheet
;
581 case FormulaLanguage::ODFF
:
584 xMap
= mxSymbolsODFF
;
586 case FormulaLanguage::ODF_11
:
589 xMap
= mxSymbolsPODF
;
591 case FormulaLanguage::ENGLISH
:
592 if (!mxSymbolsEnglish
)
593 InitSymbolsEnglish();
594 xMap
= mxSymbolsEnglish
;
596 case FormulaLanguage::NATIVE
:
597 if (!mxSymbolsNative
)
599 xMap
= mxSymbolsNative
;
601 case FormulaLanguage::XL_ENGLISH
:
602 if (!mxSymbolsEnglishXL
)
603 InitSymbolsEnglishXL();
604 xMap
= mxSymbolsEnglishXL
;
606 case FormulaLanguage::OOXML
:
609 xMap
= mxSymbolsOOXML
;
612 ; // nothing, NULL map returned
617 OUString
FormulaCompiler::FindAddInFunction( const OUString
& /*rUpperName*/, bool /*bLocalFirst*/ ) const
622 FormulaCompiler::OpCodeMapPtr
FormulaCompiler::CreateOpCodeMap(
624 const sheet::FormulaOpCodeMapEntry
> & rMapping
,
627 using sheet::FormulaOpCodeMapEntry
;
628 // Filter / API maps are never Core
629 NonConstOpCodeMapPtr
xMap( new OpCodeMap( SC_OPCODE_LAST_OPCODE_ID
+ 1, false,
630 FormulaGrammar::mergeToGrammar( FormulaGrammar::setEnglishBit(
631 FormulaGrammar::GRAM_EXTERNAL
, bEnglish
), FormulaGrammar::CONV_UNSPECIFIED
)));
632 SvtSysLocale aSysLocale
;
633 const CharClass
* pCharClass
= (xMap
->isEnglish() ? NULL
: aSysLocale
.GetCharClassPtr());
634 FormulaOpCodeMapEntry
const * pArr2
= rMapping
.getConstArray();
635 FormulaOpCodeMapEntry
const * const pStop
= pArr2
+ rMapping
.getLength();
636 for ( ; pArr2
< pStop
; ++pArr2
)
638 OpCode eOp
= OpCode(pArr2
->Token
.OpCode
);
639 if (eOp
!= ocExternal
)
640 xMap
->putOpCode( pArr2
->Name
, eOp
, pCharClass
);
643 OUString aExternalName
;
644 if (pArr2
->Token
.Data
>>= aExternalName
)
645 xMap
->putExternal( pArr2
->Name
, aExternalName
);
648 SAL_WARN( "formula.core", "FormulaCompiler::CreateOpCodeMap: no Token.Data external name");
655 void lcl_fillNativeSymbols( FormulaCompiler::NonConstOpCodeMapPtr
& xMap
, bool bDestroy
= false )
657 static OpCodeMapData aSymbolMap
;
658 osl::MutexGuard
aGuard(&aSymbolMap
.maMtx
);
662 aSymbolMap
.mxSymbolMap
.reset();
664 else if (!aSymbolMap
.mxSymbolMap
)
667 aSymbolMap
.mxSymbolMap
.reset(
668 new FormulaCompiler::OpCodeMap(
669 SC_OPCODE_LAST_OPCODE_ID
+ 1, true, FormulaGrammar::GRAM_NATIVE_UI
));
670 OModuleClient aModuleClient
;
671 OpCodeList
aOpCodeListNative(RID_STRLIST_FUNCTION_NAMES
, aSymbolMap
.mxSymbolMap
);
672 // No AddInMap for native core mapping.
675 xMap
= aSymbolMap
.mxSymbolMap
;
678 const OUString
& FormulaCompiler::GetNativeSymbol( OpCode eOp
)
680 NonConstOpCodeMapPtr xSymbolsNative
;
681 lcl_fillNativeSymbols( xSymbolsNative
);
682 return xSymbolsNative
->getSymbol( eOp
);
685 sal_Unicode
FormulaCompiler::GetNativeSymbolChar( OpCode eOp
)
687 return GetNativeSymbol(eOp
)[0];
690 void FormulaCompiler::InitSymbolsNative() const
692 lcl_fillNativeSymbols( mxSymbolsNative
);
695 void FormulaCompiler::InitSymbolsEnglish() const
697 static OpCodeMapData aMap
;
698 osl::MutexGuard
aGuard(&aMap
.maMtx
);
699 if (!aMap
.mxSymbolMap
)
700 loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH
, FormulaGrammar::GRAM_ENGLISH
, aMap
.mxSymbolMap
);
701 mxSymbolsEnglish
= aMap
.mxSymbolMap
;
704 void FormulaCompiler::InitSymbolsPODF() const
706 static OpCodeMapData aMap
;
707 osl::MutexGuard
aGuard(&aMap
.maMtx
);
708 if (!aMap
.mxSymbolMap
)
709 loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH
, FormulaGrammar::GRAM_PODF
, aMap
.mxSymbolMap
, RESOURCE_BASE
);
710 mxSymbolsPODF
= aMap
.mxSymbolMap
;
713 void FormulaCompiler::InitSymbolsODFF() const
715 static OpCodeMapData aMap
;
716 osl::MutexGuard
aGuard(&aMap
.maMtx
);
717 if (!aMap
.mxSymbolMap
)
718 loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH_ODFF
, FormulaGrammar::GRAM_ODFF
, aMap
.mxSymbolMap
, RESOURCE_BASE
);
719 mxSymbolsODFF
= aMap
.mxSymbolMap
;
722 void FormulaCompiler::InitSymbolsEnglishXL() const
724 static OpCodeMapData aMap
;
725 osl::MutexGuard
aGuard(&aMap
.maMtx
);
726 if (!aMap
.mxSymbolMap
)
727 loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH
, FormulaGrammar::GRAM_ENGLISH
, aMap
.mxSymbolMap
);
728 mxSymbolsEnglishXL
= aMap
.mxSymbolMap
;
730 // TODO: For now, just replace the separators to the Excel English
731 // variants. Later, if we want to properly map Excel functions with Calc
732 // functions, we'll need to do a little more work here.
733 mxSymbolsEnglishXL
->putOpCode( OUString(','), ocSep
, NULL
);
734 mxSymbolsEnglishXL
->putOpCode( OUString(','), ocArrayColSep
, NULL
);
735 mxSymbolsEnglishXL
->putOpCode( OUString(';'), ocArrayRowSep
, NULL
);
738 void FormulaCompiler::InitSymbolsOOXML() const
740 static OpCodeMapData aMap
;
741 osl::MutexGuard
aGuard(&aMap
.maMtx
);
742 if (!aMap
.mxSymbolMap
)
743 loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH_OOXML
, FormulaGrammar::GRAM_OOXML
, aMap
.mxSymbolMap
, RESOURCE_BASE
);
744 mxSymbolsOOXML
= aMap
.mxSymbolMap
;
748 void FormulaCompiler::loadSymbols( sal_uInt16 nSymbols
, FormulaGrammar::Grammar eGrammar
,
749 NonConstOpCodeMapPtr
& rxMap
, SeparatorType eSepType
) const
754 rxMap
.reset( new OpCodeMap( SC_OPCODE_LAST_OPCODE_ID
+ 1, eGrammar
!= FormulaGrammar::GRAM_ODFF
, eGrammar
));
755 OModuleClient aModuleClient
;
756 OpCodeList
aOpCodeList( nSymbols
, rxMap
, eSepType
);
758 fillFromAddInMap( rxMap
, eGrammar
);
759 // Fill from collection for AddIns not already present.
760 if ( FormulaGrammar::GRAM_ENGLISH
!= eGrammar
)
761 fillFromAddInCollectionUpperName( rxMap
);
763 fillFromAddInCollectionEnglishName( rxMap
);
767 void FormulaCompiler::fillFromAddInCollectionUpperName( NonConstOpCodeMapPtr
/*xMap */) const
771 void FormulaCompiler::fillFromAddInCollectionEnglishName( NonConstOpCodeMapPtr
/*xMap */) const
775 void FormulaCompiler::fillFromAddInMap( NonConstOpCodeMapPtr
/*xMap*/, FormulaGrammar::Grammar
/*_eGrammar */) const
779 OpCode
FormulaCompiler::GetEnglishOpCode( const OUString
& rName
) const
781 FormulaCompiler::OpCodeMapPtr xMap
= GetOpCodeMap( sheet::FormulaLanguage::ENGLISH
);
783 formula::OpCodeHashMap::const_iterator
iLook( xMap
->getHashMap()->find( rName
) );
784 bool bFound
= (iLook
!= xMap
->getHashMap()->end());
785 return bFound
? (*iLook
).second
: OpCode(ocNone
);
788 bool FormulaCompiler::IsOpCodeVolatile( OpCode eOp
)
800 // more than one parameters:
801 // ocIndirect/ocIndirectXL otherwise would have to do
802 // StopListening and StartListening on a reference for every
803 // interpreted value.
806 // ocOffset results in indirect references.
808 // ocDebugVar shows internal value that may change as the internal state changes.
819 bool FormulaCompiler::IsOpCodeJumpCommand( OpCode eOp
)
834 // Remove quotes, escaped quotes are unescaped.
835 bool FormulaCompiler::DeQuote( OUString
& rStr
)
837 sal_Int32 nLen
= rStr
.getLength();
838 if ( nLen
> 1 && rStr
[0] == '\'' && rStr
[ nLen
-1 ] == '\'' )
840 rStr
= rStr
.copy( 1, nLen
-2 );
841 rStr
= rStr
.replaceAll( "\\\'", "\'" );
847 void FormulaCompiler::fillAddInToken(
848 ::std::vector
< sheet::FormulaOpCodeMapEntry
>& /*_rVec*/,
849 bool /*_bIsEnglish*/) const
853 bool FormulaCompiler::IsMatrixFunction( OpCode eOpCode
)
867 case ocModalValue_Multi
:
871 // added to avoid warnings
878 FormulaCompiler::OpCodeMap::~OpCodeMap()
880 delete mpReverseExternalHashMap
;
881 delete mpExternalHashMap
;
886 void FormulaCompiler::OpCodeMap::putCopyOpCode( const OUString
& rSymbol
, OpCode eOp
)
888 SAL_WARN_IF( !mpTable
[eOp
].isEmpty() && rSymbol
.isEmpty(), "formula.core",
889 "OpCodeMap::putCopyOpCode: NOT replacing OpCode " << static_cast<sal_uInt16
>(eOp
)
890 << " '" << mpTable
[eOp
] << "' with empty name!");
891 if (!mpTable
[eOp
].isEmpty() && rSymbol
.isEmpty())
892 mpHashMap
->insert( OpCodeHashMap::value_type( mpTable
[eOp
], eOp
));
895 mpTable
[eOp
] = rSymbol
;
896 mpHashMap
->insert( OpCodeHashMap::value_type( rSymbol
, eOp
));
900 void FormulaCompiler::OpCodeMap::copyFrom( const OpCodeMap
& r
, bool bOverrideKnownBad
)
903 mpHashMap
= new OpCodeHashMap( mnSymbols
);
905 sal_uInt16 n
= r
.getSymbolCount();
906 SAL_WARN_IF( n
!= mnSymbols
, "formula.core",
907 "OpCodeMap::copyFrom: unequal size, this: " << mnSymbols
<< " that: " << n
);
911 // OpCode 0 (ocPush) should never be in a map.
912 SAL_WARN_IF( !mpTable
[0].isEmpty() || !r
.mpTable
[0].isEmpty(), "formula.core",
913 "OpCodeMap::copyFrom: OpCode 0 assigned, this: '"
914 << mpTable
[0] << "' that: '" << r
.mpTable
[0] << "'");
916 // For bOverrideKnownBad when copying from the English core map (ODF 1.1
917 // and API) to the native map (UI "use English function names") replace the
918 // known bad legacy function names with correct ones.
919 if (bOverrideKnownBad
&& r
.mbCore
&&
920 FormulaGrammar::extractFormulaLanguage( meGrammar
) == sheet::FormulaLanguage::NATIVE
&&
921 FormulaGrammar::extractFormulaLanguage( r
.meGrammar
) == sheet::FormulaLanguage::ENGLISH
)
923 for (sal_uInt16 i
= 1; i
< n
; ++i
)
926 OpCode eOp
= OpCode(i
);
933 aSymbol
= "MULTIPLE.OPERATIONS";
936 aSymbol
= r
.mpTable
[i
];
938 putCopyOpCode( aSymbol
, eOp
);
943 for (sal_uInt16 i
= 1; i
< n
; ++i
)
945 OpCode eOp
= OpCode(i
);
946 const OUString
& rSymbol
= r
.mpTable
[i
];
947 putCopyOpCode( rSymbol
, eOp
);
951 // TODO: maybe copy the external maps too?
955 sal_uInt16
FormulaCompiler::GetErrorConstant( const OUString
& rName
) const
957 sal_uInt16 nError
= 0;
958 OpCodeHashMap::const_iterator
iLook( mxSymbols
->getHashMap()->find( rName
));
959 if (iLook
!= mxSymbols
->getHashMap()->end())
961 switch ((*iLook
).second
)
963 // Not all may make sense in a formula, but these we know as
969 nError
= errDivisionByZero
;
981 nError
= errIllegalFPOperation
;
984 nError
= NOTAVAILABLE
;
993 void FormulaCompiler::EnableJumpCommandReorder( bool bEnable
)
995 mbJumpCommandReorder
= bEnable
;
998 void FormulaCompiler::EnableStopOnError( bool bEnable
)
1000 mbStopOnError
= bEnable
;
1003 void FormulaCompiler::AppendErrorConstant( OUStringBuffer
& rBuffer
, sal_uInt16 nError
) const
1012 case errDivisionByZero
:
1024 case errIllegalFPOperation
:
1031 rBuffer
.append( mxSymbols
->getSymbol( eOp
));
1035 sal_Int32
FormulaCompiler::OpCodeMap::getOpCodeUnknown()
1037 static const sal_Int32 kOpCodeUnknown
= -1;
1038 return kOpCodeUnknown
;
1041 bool FormulaCompiler::GetToken()
1043 static const short nRecursionMax
= 42;
1044 FormulaCompilerRecursionGuard
aRecursionGuard( nRecursion
);
1045 if ( nRecursion
> nRecursionMax
)
1047 SetError( errStackOverflow
);
1048 mpToken
= new FormulaByteToken( ocStop
);
1051 if ( bAutoCorrect
&& !pStack
)
1052 { // don't merge stacked subroutine code into entered formula
1053 aCorrectedFormula
+= aCorrectedSymbol
;
1054 aCorrectedSymbol
.clear();
1057 if (pArr
->GetCodeError() && mbStopOnError
)
1061 short nWasColRowName
;
1063 && pArr
->pCode
[ pArr
->nIndex
-1 ]->GetOpCode() == ocColRowName
)
1067 mpToken
= pArr
->Next();
1068 while( mpToken
&& mpToken
->GetOpCode() == ocSpaces
)
1070 if ( nWasColRowName
)
1072 if ( bAutoCorrect
&& !pStack
)
1073 CreateStringFromToken( aCorrectedFormula
, mpToken
.get(), false );
1074 mpToken
= pArr
->Next();
1076 if ( bAutoCorrect
&& !pStack
&& mpToken
)
1077 CreateStringFromToken( aCorrectedSymbol
, mpToken
.get(), false );
1090 if ( nWasColRowName
>= 2 && mpToken
->GetOpCode() == ocColRowName
)
1091 { // convert an ocSpaces to ocIntersect in RPN
1092 mpToken
= new FormulaByteToken( ocIntersect
);
1093 pArr
->nIndex
--; // we advanced to the second ocColRowName, step back
1099 mpToken
= new FormulaByteToken( ocStop
);
1102 if ( mpToken
->GetOpCode() == ocSubTotal
||
1103 mpToken
->GetOpCode() == ocAggregate
)
1105 else if ( mpToken
->IsExternalRef() )
1107 return HandleExternalReference(*mpToken
);
1109 else if( mpToken
->GetOpCode() == ocName
)
1111 return HandleRange();
1113 else if( mpToken
->GetOpCode() == ocColRowName
)
1115 return HandleColRowName();
1117 else if( mpToken
->GetOpCode() == ocDBArea
)
1119 return HandleDbData();
1121 else if( mpToken
->GetOpCode() == ocTableRef
)
1123 return HandleTableRef();
1129 // RPN creation by recursion
1130 void FormulaCompiler::Factor()
1132 if (pArr
->GetCodeError() && mbStopOnError
)
1135 CurrentFactor
pFacToken( this );
1137 OpCode eOp
= mpToken
->GetOpCode();
1138 if( eOp
== ocPush
|| eOp
== ocColRowNameAuto
|| eOp
== ocMatRef
||
1139 eOp
== ocDBArea
|| eOp
== ocTableRef
1140 || (!mbJumpCommandReorder
&& ((eOp
== ocName
) || (eOp
== ocDBArea
)
1141 || (eOp
== ocTableRef
) || (eOp
== ocColRowName
) || (eOp
== ocBad
)))
1148 // PUSH( is an error that may be caused by an unknown function.
1150 ( mpToken
->GetType() == svString
1151 || mpToken
->GetType() == svSingleRef
)
1152 ? errNoName
: errOperatorExpected
);
1153 if ( bAutoCorrect
&& !pStack
)
1154 { // assume multiplication
1155 aCorrectedFormula
+= mxSymbols
->getSymbol( ocMul
);
1159 if( eOp
!= ocClose
)
1160 SetError( errPairExpected
);
1166 else if( eOp
== ocOpen
)
1170 while ((eOp
== ocSep
) && (!pArr
->GetCodeError() || !mbStopOnError
))
1171 { // range list (A1;A2) converted to (A1~A2)
1172 pFacToken
= mpToken
;
1174 CheckSetForceArrayParameter( mpToken
, 0);
1176 // Do not ignore error here, regardless of bIgnoreErrors, otherwise
1177 // errors like =(1;) would also result in display of =(1~)
1178 if (!pArr
->GetCodeError())
1180 pFacToken
->NewOpCode( ocUnion
, FormulaToken::PrivateAccess());
1181 PutCode( pFacToken
);
1185 SetError( errPairExpected
);
1191 if( nNumFmt
== css::util::NumberFormat::UNDEFINED
)
1192 nNumFmt
= lcl_GetRetFormat( eOp
);
1194 if ( IsOpCodeVolatile( eOp
) )
1195 pArr
->SetExclusiveRecalcModeAlways();
1200 // Functions recalculated on every document load.
1201 // Don't use SetExclusiveRecalcModeOnLoad() which would
1202 // override ModeAlways, use
1203 // AddRecalcMode(ScRecalcMode::ONLOAD) instead.
1208 pArr
->AddRecalcMode( ScRecalcMode::ONLOAD
);
1210 // If the referred cell is moved the value changes.
1213 pArr
->SetRecalcModeOnRefMove();
1215 // ocCell needs recalc on move for some possible type values.
1216 // and recalc mode on load, fdo#60646
1218 pArr
->SetRecalcModeOnRefMove();
1219 pArr
->AddRecalcMode( ScRecalcMode::ONLOAD
);
1222 // cell with hyperlink needs to be calculated on load to
1223 // get its matrix result generated.
1224 pArr
->AddRecalcMode( ScRecalcMode::ONLOAD
);
1225 pArr
->SetHyperLink( true);
1231 if (SC_OPCODE_START_NO_PAR
<= eOp
&& eOp
< SC_OPCODE_STOP_NO_PAR
)
1233 pFacToken
= mpToken
;
1237 SetError( errPairExpected
);
1238 PutCode( pFacToken
);
1244 SetError( errPairExpected
);
1245 PutCode( pFacToken
);
1249 // special cases NOT() and NEG()
1250 else if( eOp
== ocNot
|| eOp
== ocNeg
1251 || (SC_OPCODE_START_1_PAR
<= eOp
&& eOp
< SC_OPCODE_STOP_1_PAR
) )
1253 pFacToken
= mpToken
;
1255 if( nNumFmt
== css::util::NumberFormat::UNDEFINED
&& eOp
== ocNot
)
1256 nNumFmt
= css::util::NumberFormat::LOGICAL
;
1260 CheckSetForceArrayParameter( mpToken
, 0);
1264 SetError( errPairExpected
);
1266 SetError( errPairExpected
);
1267 else if ( !pArr
->GetCodeError() )
1268 pFacToken
->SetByte( 1 );
1269 PutCode( pFacToken
);
1272 else if ((SC_OPCODE_START_2_PAR
<= eOp
&& eOp
< SC_OPCODE_STOP_2_PAR
)
1273 || eOp
== ocExternal
1278 || ( eOp
>= ocInternalBegin
&& eOp
<= ocInternalEnd
)
1279 || (!mbJumpCommandReorder
&& IsOpCodeJumpCommand(eOp
)))
1281 pFacToken
= mpToken
;
1282 OpCode eMyLastOp
= eOp
;
1284 bool bNoParam
= false;
1285 bool bBadName
= false;
1293 CheckSetForceArrayParameter( mpToken
, 0);
1297 else if (eMyLastOp
== ocBad
)
1299 // Just a bad name, not an unknown function, no parameters, no
1300 // closing expected.
1305 SetError( errPairExpected
);
1306 sal_uInt8 nSepCount
= 0;
1310 while ((eOp
== ocSep
) && (!pArr
->GetCodeError() || !mbStopOnError
))
1313 CheckSetForceArrayParameter( mpToken
, nSepCount
);
1319 ; // nothing, keep current token for return
1320 else if (eOp
!= ocClose
)
1321 SetError( errPairExpected
);
1324 // Jumps are just normal functions for the FunctionAutoPilot tree view
1325 if (!mbJumpCommandReorder
&& pFacToken
->GetType() == svJump
)
1326 pFacToken
= new FormulaFAPToken( pFacToken
->GetOpCode(), nSepCount
, pFacToken
);
1328 pFacToken
->SetByte( nSepCount
);
1329 PutCode( pFacToken
);
1331 else if (IsOpCodeJumpCommand(eOp
))
1333 // the PC counters are -1
1334 pFacToken
= mpToken
;
1338 pFacToken
->GetJump()[ 0 ] = 3; // if, else, behind
1341 pFacToken
->GetJump()[ 0 ] = FORMULA_MAXJUMPCOUNT
+ 1;
1345 pFacToken
->GetJump()[ 0 ] = 2; // if, behind
1348 SAL_WARN( "formula.core", "FormulaCompiler::Factor: forgot to add a jump count case?");
1354 CheckSetForceArrayParameter( mpToken
, 0);
1358 SetError( errPairExpected
);
1359 PutCode( pFacToken
);
1360 // During AutoCorrect (since pArr->GetCodeError() is
1361 // ignored) an unlimited ocIf would crash because
1362 // ScRawToken::Clone() allocates the JumpBuffer according to
1363 // nJump[0]*2+2, which is 3*2+2 on ocIf and 2*2+2 ocIfError and ocIfNA.
1365 OpCode eFacOpCode
= pFacToken
->GetOpCode();
1372 nJumpMax
= FORMULA_MAXJUMPCOUNT
;
1380 SAL_WARN( "formula.core", "FormulaCompiler::Factor: forgot to add a jump max case?");
1382 short nJumpCount
= 0;
1383 while ( (nJumpCount
< (FORMULA_MAXJUMPCOUNT
- 1)) && (eOp
== ocSep
)
1384 && (!pArr
->GetCodeError() || !mbStopOnError
))
1386 if ( ++nJumpCount
<= nJumpMax
)
1387 pFacToken
->GetJump()[nJumpCount
] = pc
-1;
1389 CheckSetForceArrayParameter( mpToken
, nJumpCount
- 1);
1391 // ocSep or ocClose terminate the subexpression
1395 SetError( errPairExpected
);
1399 // always limit to nJumpMax, no arbitrary overwrites
1400 if ( ++nJumpCount
<= nJumpMax
)
1401 pFacToken
->GetJump()[ nJumpCount
] = pc
-1;
1402 eFacOpCode
= pFacToken
->GetOpCode();
1407 bLimitOk
= (nJumpCount
<= 3);
1410 bLimitOk
= (nJumpCount
< FORMULA_MAXJUMPCOUNT
); /* TODO: check, really <, not <=? */
1414 bLimitOk
= (nJumpCount
<= 2);
1418 SAL_WARN( "formula.core", "FormulaCompiler::Factor: forgot to add a jump limit case?");
1421 pFacToken
->GetJump()[ 0 ] = nJumpCount
;
1423 SetError( errIllegalParameter
);
1426 else if ( eOp
== ocMissing
)
1431 else if ( eOp
== ocClose
)
1433 SetError( errParameterExpected
);
1435 else if ( eOp
== ocSep
)
1436 { // Subsequent ocSep
1437 SetError( errParameterExpected
);
1438 if ( bAutoCorrect
&& !pStack
)
1440 aCorrectedSymbol
.clear();
1444 else if ( mpToken
->IsExternalRef() )
1451 SetError( errUnknownToken
);
1452 if ( bAutoCorrect
&& !pStack
)
1454 if ( eOp
== ocStop
)
1455 { // trailing operator w/o operand
1456 sal_Int32 nLen
= aCorrectedFormula
.getLength();
1458 aCorrectedFormula
= aCorrectedFormula
.copy( 0, nLen
- 1 );
1459 aCorrectedSymbol
.clear();
1467 void FormulaCompiler::RangeLine()
1470 while (mpToken
->GetOpCode() == ocRange
)
1472 FormulaToken
** pCode1
= pCode
- 1;
1473 FormulaTokenRef p
= mpToken
;
1476 FormulaToken
** pCode2
= pCode
- 1;
1477 if (!MergeRangeReference( pCode1
, pCode2
))
1482 void FormulaCompiler::IntersectionLine()
1485 while (mpToken
->GetOpCode() == ocIntersect
)
1487 FormulaTokenRef p
= mpToken
;
1494 void FormulaCompiler::UnionLine()
1497 while (mpToken
->GetOpCode() == ocUnion
)
1499 FormulaTokenRef p
= mpToken
;
1506 void FormulaCompiler::UnaryLine()
1508 if( mpToken
->GetOpCode() == ocAdd
)
1510 else if (SC_OPCODE_START_UN_OP
<= mpToken
->GetOpCode() &&
1511 mpToken
->GetOpCode() < SC_OPCODE_STOP_UN_OP
)
1513 FormulaTokenRef p
= mpToken
;
1522 void FormulaCompiler::PostOpLine()
1525 while ( mpToken
->GetOpCode() == ocPercentSign
)
1526 { // this operator _follows_ its operand
1532 void FormulaCompiler::PowLine()
1535 while (mpToken
->GetOpCode() == ocPow
)
1537 FormulaTokenRef p
= mpToken
;
1544 void FormulaCompiler::MulDivLine()
1547 while (mpToken
->GetOpCode() == ocMul
|| mpToken
->GetOpCode() == ocDiv
)
1549 FormulaTokenRef p
= mpToken
;
1556 void FormulaCompiler::AddSubLine()
1559 while (mpToken
->GetOpCode() == ocAdd
|| mpToken
->GetOpCode() == ocSub
)
1561 FormulaTokenRef p
= mpToken
;
1568 void FormulaCompiler::ConcatLine()
1571 while (mpToken
->GetOpCode() == ocAmpersand
)
1573 FormulaTokenRef p
= mpToken
;
1580 void FormulaCompiler::CompareLine()
1583 while (mpToken
->GetOpCode() >= ocEqual
&& mpToken
->GetOpCode() <= ocGreaterEqual
)
1585 FormulaTokenRef p
= mpToken
;
1592 void FormulaCompiler::NotLine()
1595 while (mpToken
->GetOpCode() == ocNot
)
1597 FormulaTokenRef p
= mpToken
;
1604 OpCode
FormulaCompiler::Expression()
1606 static const short nRecursionMax
= 42;
1607 FormulaCompilerRecursionGuard
aRecursionGuard( nRecursion
);
1608 if ( nRecursion
> nRecursionMax
)
1610 SetError( errStackOverflow
);
1611 return ocStop
; //! generate token instead?
1614 while (mpToken
->GetOpCode() == ocAnd
|| mpToken
->GetOpCode() == ocOr
)
1616 FormulaTokenRef p
= mpToken
;
1617 mpToken
->SetByte( 2 ); // 2 parameters!
1622 return mpToken
->GetOpCode();
1626 void FormulaCompiler::SetError( sal_uInt16
/*nError*/ )
1630 FormulaTokenRef
FormulaCompiler::ExtendRangeReference( FormulaToken
& /*rTok1*/, FormulaToken
& /*rTok2*/,
1631 bool /*bReuseDoubleRef*/ )
1633 return FormulaTokenRef();
1636 bool FormulaCompiler::MergeRangeReference( FormulaToken
* * const pCode1
, FormulaToken
* const * const pCode2
)
1638 FormulaToken
*p1
, *p2
;
1639 if (pc
< 2 || !pCode1
|| !pCode2
||
1640 (pCode2
- pCode1
!= 1) || (pCode
- pCode2
!= 1) ||
1641 ((p1
= *pCode1
) == 0) || ((p2
= *pCode2
) == 0) )
1644 FormulaTokenRef p
= ExtendRangeReference( *p1
, *p2
, true);
1657 bool FormulaCompiler::CompileTokenArray()
1661 if (!pArr
->GetCodeError() || !mbStopOnError
)
1665 aCorrectedFormula
.clear();
1666 aCorrectedSymbol
.clear();
1670 FormulaToken
* pData
[ FORMULA_MAXTOKENS
];
1672 bool bWasForced
= pArr
->IsRecalcModeForced();
1676 aCorrectedFormula
= "=";
1678 pArr
->ClearRecalcMode();
1683 OpCode eOp
= Expression();
1684 // Some trailing garbage that doesn't form an expression?
1686 SetError( errOperatorExpected
);
1688 sal_uInt16 nErrorBeforePop
= pArr
->GetCodeError();
1694 pArr
->pRPN
= new FormulaToken
*[ pc
];
1696 memcpy( pArr
->pRPN
, pData
, pc
* sizeof( FormulaToken
* ) );
1699 // once an error, always an error
1700 if( !pArr
->GetCodeError() && nErrorBeforePop
)
1701 pArr
->SetCodeError( nErrorBeforePop
);
1703 if (pArr
->GetCodeError() && mbStopOnError
)
1706 pArr
->SetHyperLink( false);
1710 pArr
->SetRecalcModeForced();
1712 if( nNumFmt
== css::util::NumberFormat::UNDEFINED
)
1713 nNumFmt
= css::util::NumberFormat::NUMBER
;
1717 void FormulaCompiler::PopTokenArray()
1721 FormulaArrayStack
* p
= pStack
;
1723 // obtain special RecalcMode from SharedFormula
1724 if ( pArr
->IsRecalcModeAlways() )
1725 p
->pArr
->SetExclusiveRecalcModeAlways();
1726 else if ( !pArr
->IsRecalcModeNormal() && p
->pArr
->IsRecalcModeNormal() )
1727 p
->pArr
->SetMaskedRecalcMode( pArr
->GetRecalcMode() );
1728 p
->pArr
->SetCombinedBitsRecalcMode( pArr
->GetRecalcMode() );
1729 if ( pArr
->IsHyperLink() ) // fdo 87534
1730 p
->pArr
->SetHyperLink( true );
1738 void FormulaCompiler::CreateStringFromTokenArray( OUString
& rFormula
)
1740 OUStringBuffer
aBuffer( pArr
->GetLen() * 5 );
1741 CreateStringFromTokenArray( aBuffer
);
1742 rFormula
= aBuffer
.makeStringAndClear();
1745 void FormulaCompiler::CreateStringFromTokenArray( OUStringBuffer
& rBuffer
)
1747 rBuffer
.setLength(0);
1748 if( !pArr
->GetLen() )
1751 FormulaTokenArray
* pSaveArr
= pArr
;
1752 bool bODFF
= FormulaGrammar::isODFF( meGrammar
);
1753 if (bODFF
|| FormulaGrammar::isPODF( meGrammar
) )
1755 // Scan token array for missing args and re-write if present.
1756 MissingConventionODF
aConv( bODFF
);
1757 if (pArr
->NeedsPodfRewrite( aConv
))
1758 pArr
= pArr
->RewriteMissing( aConv
);
1760 else if ( FormulaGrammar::isOOXML( meGrammar
) )
1762 // Scan token array for missing args and rewrite if present.
1763 MissingConventionOOXML aConv
;
1764 if (pArr
->NeedsOoxmlRewrite())
1765 pArr
= pArr
->RewriteMissing( aConv
);
1768 // At least one character per token, plus some are references, some are
1769 // function names, some are numbers, ...
1770 rBuffer
.ensureCapacity( pArr
->GetLen() * 5 );
1772 if ( pArr
->IsRecalcModeForced() )
1773 rBuffer
.append( '=');
1774 const FormulaToken
* t
= pArr
->First();
1776 t
= CreateStringFromToken( rBuffer
, t
, true );
1778 if (pSaveArr
!= pArr
)
1785 const FormulaToken
* FormulaCompiler::CreateStringFromToken( OUString
& rFormula
, const FormulaToken
* pTokenP
,
1786 bool bAllowArrAdvance
)
1788 OUStringBuffer aBuffer
;
1789 const FormulaToken
* p
= CreateStringFromToken( aBuffer
, pTokenP
, bAllowArrAdvance
);
1790 rFormula
+= aBuffer
.makeStringAndClear();
1794 const FormulaToken
* FormulaCompiler::CreateStringFromToken( OUStringBuffer
& rBuffer
, const FormulaToken
* pTokenP
,
1795 bool bAllowArrAdvance
)
1798 bool bSpaces
= false;
1799 const FormulaToken
* t
= pTokenP
;
1800 OpCode eOp
= t
->GetOpCode();
1801 if( eOp
>= ocAnd
&& eOp
<= ocOr
)
1804 if ( bAllowArrAdvance
)
1807 t
= pArr
->PeekNext();
1809 bSpaces
= ( !t
|| t
->GetOpCode() != ocOpen
);
1812 rBuffer
.append( ' ');
1814 if( eOp
== ocSpaces
)
1816 bool bIntersectionOp
= mxSymbols
->isODFF();
1817 if (bIntersectionOp
)
1819 const FormulaToken
* p
= pArr
->PeekPrevNoSpaces();
1820 bIntersectionOp
= (p
&& p
->GetOpCode() == ocColRowName
);
1821 if (bIntersectionOp
)
1823 p
= pArr
->PeekNextNoSpaces();
1824 bIntersectionOp
= (p
&& p
->GetOpCode() == ocColRowName
);
1827 if (bIntersectionOp
)
1828 rBuffer
.appendAscii( "!!");
1831 // most times it's just one blank
1832 sal_uInt8 n
= t
->GetByte();
1833 for ( sal_uInt8 j
=0; j
<n
; ++j
)
1835 rBuffer
.append( ' ');
1839 else if( eOp
>= ocInternalBegin
&& eOp
<= ocInternalEnd
)
1840 rBuffer
.appendAscii( pInternal
[ eOp
- ocInternalBegin
] );
1841 else if( (sal_uInt16
) eOp
< mxSymbols
->getSymbolCount()) // Keyword:
1842 rBuffer
.append( mxSymbols
->getSymbol( eOp
));
1845 SAL_WARN( "formula.core","unknown OpCode");
1846 rBuffer
.append( GetNativeSymbol( ocErrName
));
1850 if (t
->IsExternalRef())
1852 CreateStringFromExternal( rBuffer
, pTokenP
);
1856 switch( t
->GetType() )
1859 AppendDouble( rBuffer
, t
->GetDouble() );
1863 if( eOp
== ocBad
|| eOp
== ocStringXML
)
1864 rBuffer
.append( t
->GetString().getString());
1866 AppendString( rBuffer
, t
->GetString().getString() );
1869 CreateStringFromSingleRef( rBuffer
, t
);
1872 CreateStringFromDoubleRef( rBuffer
, t
);
1875 CreateStringFromMatrix( rBuffer
, t
);
1879 CreateStringFromIndex( rBuffer
, t
);
1880 if (t
->GetOpCode() == ocTableRef
&& bAllowArrAdvance
&& NeedsTableRefTransformation())
1882 // Suppress all TableRef related tokens, the resulting
1883 // range was written by CreateStringFromIndex().
1884 const FormulaToken
* const p
= pArr
->PeekNext();
1885 if (p
&& p
->GetOpCode() == ocTableRefOpen
)
1894 // Switch cases correspond with those in
1895 // ScCompiler::HandleTableRef()
1896 switch (t
->GetOpCode())
1898 case ocTableRefOpen
:
1901 case ocTableRefClose
:
1904 case ocTableRefItemAll
:
1905 case ocTableRefItemHeaders
:
1906 case ocTableRefItemData
:
1907 case ocTableRefItemTotals
:
1908 case ocTableRefItemThisRow
:
1924 // mapped or translated name of AddIns
1925 OUString
aAddIn( t
->GetExternal() );
1926 bool bMapped
= mxSymbols
->isPODF(); // ODF 1.1 directly uses programmatical name
1927 if (!bMapped
&& mxSymbols
->hasExternals())
1929 ExternalHashMap::const_iterator iLook
= mxSymbols
->getReverseExternalHashMap()->find( aAddIn
);
1930 if (iLook
!= mxSymbols
->getReverseExternalHashMap()->end())
1932 aAddIn
= (*iLook
).second
;
1936 if (!bMapped
&& !mxSymbols
->isEnglish())
1937 LocalizeString( aAddIn
);
1938 rBuffer
.append( aAddIn
);
1942 AppendErrorConstant( rBuffer
, t
->GetError());
1951 SAL_WARN("formula.core", "FormulaCompiler::GetStringFromToken: unknown token type " << t
->GetType());
1956 rBuffer
.append( ' ');
1957 if ( bAllowArrAdvance
)
1967 void FormulaCompiler::AppendDouble( OUStringBuffer
& rBuffer
, double fVal
) const
1969 if ( mxSymbols
->isEnglish() )
1971 ::rtl::math::doubleToUStringBuffer( rBuffer
, fVal
,
1972 rtl_math_StringFormat_Automatic
,
1973 rtl_math_DecimalPlaces_Max
, '.', true );
1977 SvtSysLocale aSysLocale
;
1978 ::rtl::math::doubleToUStringBuffer( rBuffer
, fVal
,
1979 rtl_math_StringFormat_Automatic
,
1980 rtl_math_DecimalPlaces_Max
,
1981 aSysLocale
.GetLocaleDataPtr()->getNumDecimalSep()[0],
1986 void FormulaCompiler::AppendBoolean( OUStringBuffer
& rBuffer
, bool bVal
) const
1988 rBuffer
.append( mxSymbols
->getSymbol( static_cast<OpCode
>(bVal
? ocTrue
: ocFalse
)) );
1991 void FormulaCompiler::AppendString( OUStringBuffer
& rBuffer
, const OUString
& rStr
)
1993 rBuffer
.append( '"');
1994 if ( lcl_UnicodeStrChr( rStr
.getStr(), '"' ) == NULL
)
1995 rBuffer
.append( rStr
);
1998 OUString aStr
= rStr
.replaceAll( "\"", "\"\"" );
1999 rBuffer
.append(aStr
);
2001 rBuffer
.append( '"');
2004 bool FormulaCompiler::NeedsTableRefTransformation() const
2006 /* TODO: currently only UI representations use Table structured
2007 * references. Not defined in ODFF, and not implemented yet for OOXML
2008 * export. Change this once OOXML export is implemented, until then write
2009 * A1 style references also for OOXML to not lose functionality. */
2010 // Unnecessary to explicitly check for ODFF grammar as the ocTableRefOpen
2011 // symbol is not defined there.
2012 return mxSymbols
->getSymbol( ocTableRefOpen
).isEmpty() || FormulaGrammar::isPODF( meGrammar
)
2013 || FormulaGrammar::isOOXML( meGrammar
);
2016 void FormulaCompiler::UpdateSeparatorsNative(
2017 const OUString
& rSep
, const OUString
& rArrayColSep
, const OUString
& rArrayRowSep
)
2019 NonConstOpCodeMapPtr xSymbolsNative
;
2020 lcl_fillNativeSymbols( xSymbolsNative
);
2021 xSymbolsNative
->putOpCode( rSep
, ocSep
, NULL
);
2022 xSymbolsNative
->putOpCode( rArrayColSep
, ocArrayColSep
, NULL
);
2023 xSymbolsNative
->putOpCode( rArrayRowSep
, ocArrayRowSep
, NULL
);
2026 void FormulaCompiler::ResetNativeSymbols()
2028 NonConstOpCodeMapPtr xSymbolsNative
;
2029 lcl_fillNativeSymbols( xSymbolsNative
, true);
2030 lcl_fillNativeSymbols( xSymbolsNative
);
2033 void FormulaCompiler::SetNativeSymbols( const OpCodeMapPtr
& xMap
)
2035 NonConstOpCodeMapPtr xSymbolsNative
;
2036 lcl_fillNativeSymbols( xSymbolsNative
);
2037 xSymbolsNative
->copyFrom( *xMap
, true);
2041 OpCode
FormulaCompiler::NextToken()
2045 OpCode eOp
= mpToken
->GetOpCode();
2046 // There must be an operator before a push
2047 if ( (eOp
== ocPush
|| eOp
== ocColRowNameAuto
) &&
2048 !( (eLastOp
== ocOpen
) || (eLastOp
== ocSep
) ||
2049 (SC_OPCODE_START_BIN_OP
<= eLastOp
&& eLastOp
< SC_OPCODE_STOP_UN_OP
)) )
2050 SetError( errOperatorExpected
);
2051 // Operator and Plus => operator
2052 if (eOp
== ocAdd
&& (eLastOp
== ocOpen
|| eLastOp
== ocSep
||
2053 (SC_OPCODE_START_BIN_OP
<= eLastOp
&& eLastOp
< SC_OPCODE_STOP_UN_OP
)))
2057 // Before an operator there must not be another operator, with the
2058 // exception of AND and OR.
2059 if ( eOp
!= ocAnd
&& eOp
!= ocOr
&&
2060 (SC_OPCODE_START_BIN_OP
<= eOp
&& eOp
< SC_OPCODE_STOP_BIN_OP
)
2061 && (eLastOp
== ocOpen
|| eLastOp
== ocSep
||
2062 (SC_OPCODE_START_BIN_OP
<= eLastOp
&& eLastOp
< SC_OPCODE_STOP_UN_OP
)))
2064 SetError( errVariableExpected
);
2065 if ( bAutoCorrect
&& !pStack
)
2067 if ( eOp
== eLastOp
|| eLastOp
== ocOpen
)
2068 { // throw away duplicated operator
2069 aCorrectedSymbol
.clear();
2074 sal_Int32 nPos
= aCorrectedFormula
.getLength();
2078 sal_Unicode c
= aCorrectedFormula
[ nPos
];
2082 if ( c
== mxSymbols
->getSymbolChar( ocEqual
) )
2083 { // >= instead of =>
2084 aCorrectedFormula
= aCorrectedFormula
.replaceAt( nPos
, 1,
2085 OUString( mxSymbols
->getSymbolChar(ocGreater
) ) );
2086 aCorrectedSymbol
= OUString(c
);
2091 if ( c
== mxSymbols
->getSymbolChar( ocEqual
) )
2092 { // <= instead of =<
2093 aCorrectedFormula
= aCorrectedFormula
.replaceAt( nPos
, 1,
2094 OUString( mxSymbols
->getSymbolChar(ocLess
) ) );
2095 aCorrectedSymbol
= OUString(c
);
2098 else if ( c
== mxSymbols
->getSymbolChar( ocGreater
) )
2099 { // <> instead of ><
2100 aCorrectedFormula
= aCorrectedFormula
.replaceAt( nPos
, 1,
2101 OUString( mxSymbols
->getSymbolChar(ocLess
) ) );
2102 aCorrectedSymbol
= OUString(c
);
2107 if ( c
== mxSymbols
->getSymbolChar( ocSub
) )
2108 { // *- instead of -*
2109 aCorrectedFormula
= aCorrectedFormula
.replaceAt( nPos
, 1,
2110 OUString( mxSymbols
->getSymbolChar(ocMul
) ) );
2111 aCorrectedSymbol
= OUString(c
);
2116 if ( c
== mxSymbols
->getSymbolChar( ocSub
) )
2117 { // /- instead of -/
2118 aCorrectedFormula
= aCorrectedFormula
.replaceAt( nPos
, 1,
2119 OUString( mxSymbols
->getSymbolChar(ocDiv
) ) );
2120 aCorrectedSymbol
= OUString(c
);
2135 void FormulaCompiler::PutCode( FormulaTokenRef
& p
)
2137 if( pc
>= FORMULA_MAXTOKENS
- 1 )
2139 if ( pc
== FORMULA_MAXTOKENS
- 1 )
2141 p
= new FormulaByteToken( ocStop
);
2146 SetError( errCodeOverflow
);
2149 if (pArr
->GetCodeError() && mbJumpCommandReorder
)
2151 ForceArrayOperator( p
);
2158 bool FormulaCompiler::HandleExternalReference( const FormulaToken
& /*_aToken*/)
2163 bool FormulaCompiler::HandleRange()
2168 bool FormulaCompiler::HandleColRowName()
2173 bool FormulaCompiler::HandleDbData()
2178 bool FormulaCompiler::HandleTableRef()
2183 void FormulaCompiler::CreateStringFromSingleRef( OUStringBuffer
& /*rBuffer*/, const FormulaToken
* /*pToken*/) const
2187 void FormulaCompiler::CreateStringFromDoubleRef( OUStringBuffer
& /*rBuffer*/, const FormulaToken
* /*pToken*/) const
2191 void FormulaCompiler::CreateStringFromIndex( OUStringBuffer
& /*rBuffer*/, const FormulaToken
* /*pToken*/) const
2195 void FormulaCompiler::CreateStringFromMatrix( OUStringBuffer
& /*rBuffer*/, const FormulaToken
* /*pToken*/) const
2199 void FormulaCompiler::CreateStringFromExternal( OUStringBuffer
& /*rBuffer*/, const FormulaToken
* /*pToken*/) const
2203 void FormulaCompiler::LocalizeString( OUString
& /*rName*/ ) const
2207 bool FormulaCompiler::IsForceArrayParameter( const FormulaToken
* /*pToken*/, sal_uInt16
/*nParam*/ ) const
2212 void FormulaCompiler::ForceArrayOperator( FormulaTokenRef
& rCurr
)
2214 if (!pCurrentFactorToken
|| (pCurrentFactorToken
.get() == rCurr
.get()))
2217 if (!(rCurr
->GetOpCode() != ocPush
&& (rCurr
->GetType() == svByte
|| rCurr
->GetType() == svJump
)))
2220 if (pCurrentFactorToken
->HasForceArray())
2222 rCurr
->SetForceArray( true);
2226 if (nCurrentFactorParam
&& IsForceArrayParameter( pCurrentFactorToken
.get(),
2227 static_cast<sal_uInt8
>(nCurrentFactorParam
- 1)))
2228 rCurr
->SetForceArray( true);
2231 void FormulaCompiler::CheckSetForceArrayParameter( FormulaTokenRef
& rCurr
, sal_uInt8 nParam
)
2233 if (!pCurrentFactorToken
)
2236 nCurrentFactorParam
= nParam
+ 1;
2238 ForceArrayOperator( rCurr
);
2241 void FormulaCompiler::PushTokenArray( FormulaTokenArray
* pa
, bool bTemp
)
2243 if ( bAutoCorrect
&& !pStack
)
2244 { // don't merge stacked subroutine code into entered formula
2245 aCorrectedFormula
+= aCorrectedSymbol
;
2246 aCorrectedSymbol
.clear();
2248 FormulaArrayStack
* p
= new FormulaArrayStack
;
2256 } // namespace formula
2258 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */