tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / formula / source / core / api / FormulaCompiler.cxx
blobe67911f5e573620e76638fc33d631aeafae9af04
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 <sal/log.hxx>
21 #include <rtl/math.hxx>
22 #include <formula/FormulaCompiler.hxx>
23 #include <formula/errorcodes.hxx>
24 #include <formula/token.hxx>
25 #include <formula/tokenarray.hxx>
26 #include <o3tl/string_view.hxx>
27 #include <core_resource.hxx>
28 #include <core_resource.hrc>
30 #include <svl/zforlist.hxx>
31 #include <unotools/charclass.hxx>
32 #include <vcl/svapp.hxx>
33 #include <vcl/settings.hxx>
34 #include <comphelper/lok.hxx>
35 #include <comphelper/processfactory.hxx>
36 #include <com/sun/star/sheet/FormulaOpCodeMapEntry.hpp>
37 #include <com/sun/star/sheet/FormulaMapGroup.hpp>
38 #include <com/sun/star/sheet/FormulaMapGroupSpecialOffset.hpp>
39 #include <algorithm>
40 #include <mutex>
42 namespace formula
44 using namespace ::com::sun::star;
46 static const char* pInternal[2] = { "TTT", "__DEBUG_VAR" };
48 namespace {
50 class FormulaCompilerRecursionGuard
52 private:
53 short& rRecursion;
54 public:
55 explicit FormulaCompilerRecursionGuard( short& rRec )
56 : rRecursion( rRec ) { ++rRecursion; }
57 ~FormulaCompilerRecursionGuard() { --rRecursion; }
60 SvNumFormatType lcl_GetRetFormat( OpCode eOpCode )
62 switch (eOpCode)
64 case ocEqual:
65 case ocNotEqual:
66 case ocLess:
67 case ocGreater:
68 case ocLessEqual:
69 case ocGreaterEqual:
70 case ocAnd:
71 case ocOr:
72 case ocXor:
73 case ocNot:
74 case ocTrue:
75 case ocFalse:
76 case ocIsEmpty:
77 case ocIsString:
78 case ocIsNonString:
79 case ocIsLogical:
80 case ocIsRef:
81 case ocIsValue:
82 case ocIsFormula:
83 case ocIsNA:
84 case ocIsErr:
85 case ocIsError:
86 case ocIsEven:
87 case ocIsOdd:
88 case ocExact:
89 return SvNumFormatType::LOGICAL;
90 case ocGetActDate:
91 case ocGetDate:
92 case ocEasterSunday :
93 return SvNumFormatType::DATE;
94 case ocGetActTime:
95 return SvNumFormatType::DATETIME;
96 case ocGetTime:
97 return SvNumFormatType::TIME;
98 case ocNPV:
99 case ocPV:
100 case ocSYD:
101 case ocDDB:
102 case ocDB:
103 case ocVBD:
104 case ocSLN:
105 case ocPMT:
106 case ocFV:
107 case ocIpmt:
108 case ocPpmt:
109 case ocCumIpmt:
110 case ocCumPrinc:
111 return SvNumFormatType::CURRENCY;
112 case ocRate:
113 case ocIRR:
114 case ocMIRR:
115 case ocRRI:
116 case ocEffect:
117 case ocNominal:
118 case ocPercentSign:
119 return SvNumFormatType::PERCENT;
120 default:
121 return SvNumFormatType::NUMBER;
125 void lclPushOpCodeMapEntry( ::std::vector< sheet::FormulaOpCodeMapEntry >& rVec,
126 const OUString* pTable, sal_uInt16 nOpCode )
128 sheet::FormulaOpCodeMapEntry aEntry;
129 aEntry.Token.OpCode = nOpCode;
130 aEntry.Name = pTable[nOpCode];
131 rVec.push_back( aEntry);
134 void lclPushOpCodeMapEntries( ::std::vector< sheet::FormulaOpCodeMapEntry >& rVec,
135 const OUString* pTable, sal_uInt16 nOpCodeBeg, sal_uInt16 nOpCodeEnd )
137 for (sal_uInt16 nOpCode = nOpCodeBeg; nOpCode < nOpCodeEnd; ++nOpCode)
138 lclPushOpCodeMapEntry( rVec, pTable, nOpCode );
141 void lclPushOpCodeMapEntries( ::std::vector< sheet::FormulaOpCodeMapEntry >& rVec,
142 const OUString* pTable, const sal_uInt16* pnOpCodes, size_t nCount )
144 for (const sal_uInt16* pnEnd = pnOpCodes + nCount; pnOpCodes < pnEnd; ++pnOpCodes)
145 lclPushOpCodeMapEntry( rVec, pTable, *pnOpCodes );
148 CharClass* createCharClassIfNonEnglishUI()
150 const LanguageTag& rLanguageTag( Application::GetSettings().GetUILanguageTag());
151 if (rLanguageTag.getLanguage() == "en")
152 return nullptr;
153 return new CharClass( ::comphelper::getProcessComponentContext(), rLanguageTag);
156 class OpCodeList
158 public:
160 OpCodeList(const std::pair<const char*, int>* pSymbols, const FormulaCompiler::NonConstOpCodeMapPtr&,
161 FormulaCompiler::SeparatorType = FormulaCompiler::SeparatorType::SEMICOLON_BASE );
162 OpCodeList(const std::pair<TranslateId, int>* pSymbols, const FormulaCompiler::NonConstOpCodeMapPtr&,
163 FormulaCompiler::SeparatorType = FormulaCompiler::SeparatorType::SEMICOLON_BASE );
165 private:
166 bool getOpCodeString( OUString& rStr, sal_uInt16 nOp );
167 void putDefaultOpCode( const FormulaCompiler::NonConstOpCodeMapPtr& xMap, sal_uInt16 nOp, const CharClass* pCharClass );
169 private:
170 FormulaCompiler::SeparatorType meSepType;
171 const std::pair<const char*, int>* mpSymbols1;
172 const std::pair<TranslateId, int>* mpSymbols2;
175 OpCodeList::OpCodeList(const std::pair<const char*, int>* pSymbols, const FormulaCompiler::NonConstOpCodeMapPtr& xMap,
176 FormulaCompiler::SeparatorType eSepType)
177 : meSepType(eSepType)
178 , mpSymbols1(pSymbols)
179 , mpSymbols2(nullptr)
181 std::unique_ptr<CharClass> xCharClass( xMap->isEnglish() ? nullptr : createCharClassIfNonEnglishUI());
182 const CharClass* pCharClass = xCharClass.get();
183 if (meSepType == FormulaCompiler::SeparatorType::RESOURCE_BASE)
185 for (sal_uInt16 i = 0; i <= SC_OPCODE_LAST_OPCODE_ID; ++i)
187 putDefaultOpCode( xMap, i, pCharClass);
190 else
192 for (sal_uInt16 i = 0; i <= SC_OPCODE_LAST_OPCODE_ID; ++i)
194 OUString aOpStr;
195 if ( getOpCodeString( aOpStr, i) )
196 xMap->putOpCode( aOpStr, OpCode(i), pCharClass);
197 else
198 putDefaultOpCode( xMap, i, pCharClass);
203 OpCodeList::OpCodeList(const std::pair<TranslateId, int>* pSymbols, const FormulaCompiler::NonConstOpCodeMapPtr& xMap,
204 FormulaCompiler::SeparatorType eSepType)
205 : meSepType(eSepType)
206 , mpSymbols1(nullptr)
207 , mpSymbols2(pSymbols)
209 std::unique_ptr<CharClass> xCharClass( xMap->isEnglish() ? nullptr : createCharClassIfNonEnglishUI());
210 const CharClass* pCharClass = xCharClass.get();
211 if (meSepType == FormulaCompiler::SeparatorType::RESOURCE_BASE)
213 for (sal_uInt16 i = 0; i <= SC_OPCODE_LAST_OPCODE_ID; ++i)
215 putDefaultOpCode( xMap, i, pCharClass);
218 else
220 for (sal_uInt16 i = 0; i <= SC_OPCODE_LAST_OPCODE_ID; ++i)
222 OUString aOpStr;
223 if ( getOpCodeString( aOpStr, i) )
224 xMap->putOpCode( aOpStr, OpCode(i), pCharClass);
225 else
226 putDefaultOpCode( xMap, i, pCharClass);
231 bool OpCodeList::getOpCodeString( OUString& rStr, sal_uInt16 nOp )
233 switch (nOp)
235 case SC_OPCODE_SEP:
237 if (meSepType == FormulaCompiler::SeparatorType::SEMICOLON_BASE)
239 rStr = ";";
240 return true;
243 break;
244 case SC_OPCODE_ARRAY_COL_SEP:
246 if (meSepType == FormulaCompiler::SeparatorType::SEMICOLON_BASE)
248 rStr = ";";
249 return true;
252 break;
253 case SC_OPCODE_ARRAY_ROW_SEP:
255 if (meSepType == FormulaCompiler::SeparatorType::SEMICOLON_BASE)
257 rStr = "|";
258 return true;
261 break;
264 return false;
267 void OpCodeList::putDefaultOpCode( const FormulaCompiler::NonConstOpCodeMapPtr& xMap, sal_uInt16 nOp,
268 const CharClass* pCharClass )
270 OUString sKey;
271 if (mpSymbols1)
273 const char* pKey = nullptr;
274 for (const std::pair<const char*, int>* pSymbol = mpSymbols1; pSymbol->first; ++pSymbol)
276 if (nOp == pSymbol->second)
278 pKey = pSymbol->first;
279 break;
282 if (!pKey)
283 return;
284 sKey = OUString::createFromAscii(pKey);
286 else if (mpSymbols2)
288 TranslateId pKey;
289 for (const std::pair<TranslateId, int>* pSymbol = mpSymbols2; pSymbol->first; ++pSymbol)
291 if (nOp == pSymbol->second)
293 pKey = pSymbol->first;
294 break;
297 if (!pKey)
298 return;
299 sKey = ForResId(pKey);
301 xMap->putOpCode(sKey, OpCode(nOp), pCharClass);
304 // static
305 const sal_Unicode* lcl_UnicodeStrChr( const sal_Unicode* pStr, sal_Unicode c )
307 if ( !pStr )
308 return nullptr;
309 while ( *pStr )
311 if ( *pStr == c )
312 return pStr;
313 pStr++;
315 return nullptr;
318 struct OpCodeMapData
320 FormulaCompiler::NonConstOpCodeMapPtr mxSymbolMap;
321 std::mutex maMtx;
325 bool isPotentialRangeLeftOp( OpCode eOp )
327 switch (eOp)
329 case ocClose:
330 return true;
331 default:
332 return false;
336 bool isRangeResultFunction( OpCode eOp )
338 switch (eOp)
340 case ocIndirect:
341 case ocOffset:
342 return true;
343 default:
344 return false;
348 bool isRangeResultOpCode( OpCode eOp )
350 switch (eOp)
352 case ocRange:
353 case ocUnion:
354 case ocIntersect:
355 case ocIndirect:
356 case ocOffset:
357 return true;
358 default:
359 return false;
364 @param pToken
365 MUST be a valid token, caller has to ensure.
367 @param bRight
368 If bRPN==false, bRight==false means opcodes for left side are
369 checked, bRight==true means opcodes for right side. If bRPN==true
370 it doesn't matter except for the ocSep converted to ocUnion case.
372 bool isPotentialRangeType( FormulaToken const * pToken, bool bRPN, bool bRight )
374 switch (pToken->GetType())
376 case svByte: // could be range result, but only a few
377 if (bRPN)
378 return isRangeResultOpCode( pToken->GetOpCode());
379 else if (bRight)
380 return isRangeResultFunction( pToken->GetOpCode());
381 else
382 return isPotentialRangeLeftOp( pToken->GetOpCode());
383 case svSingleRef:
384 case svDoubleRef:
385 case svIndex: // could be range
386 //case svRefList: // um..what?
387 case svExternalSingleRef:
388 case svExternalDoubleRef:
389 case svExternalName: // could be range
390 return true;
391 case svSep:
392 // A special case if a previous ocSep was converted to ocUnion it
393 // stays svSep instead of svByte.
394 return bRPN && !bRight && pToken->GetOpCode() == ocUnion;
395 default:
396 // Separators are not part of RPN and right opcodes need to be
397 // other StackVar types or functions and thus svByte.
398 return !bRPN && !bRight && isPotentialRangeLeftOp( pToken->GetOpCode());
402 bool isIntersectable( FormulaToken** pCode1, FormulaToken** pCode2 )
404 FormulaToken* pToken1 = *pCode1;
405 FormulaToken* pToken2 = *pCode2;
406 if (pToken1 && pToken2)
407 return isPotentialRangeType( pToken1, true, false) && isPotentialRangeType( pToken2, true, true);
408 return false;
411 bool isAdjacentRpnEnd( sal_uInt16 nPC,
412 FormulaToken const * const * const pCode,
413 FormulaToken const * const * const pCode1,
414 FormulaToken const * const * const pCode2 )
416 return nPC >= 2 && pCode1 && pCode2 &&
417 (pCode2 - pCode1 == 1) && (pCode - pCode2 == 1) &&
418 (*pCode1 != nullptr) && (*pCode2 != nullptr);
421 bool isAdjacentOrGapRpnEnd( sal_uInt16 nPC,
422 FormulaToken const * const * const pCode,
423 FormulaToken const * const * const pCode1,
424 FormulaToken const * const * const pCode2 )
426 return nPC >= 2 && pCode1 && pCode2 &&
427 (pCode2 > pCode1) && (pCode - pCode2 == 1) &&
428 (*pCode1 != nullptr) && (*pCode2 != nullptr);
432 } // namespace
435 void FormulaCompiler::OpCodeMap::putExternal( const OUString & rSymbol, const OUString & rAddIn )
437 // Different symbols may map to the same AddIn, but the same AddIn may not
438 // map to different symbols, the first pair wins. Same symbol of course may
439 // not map to different AddIns, again the first pair wins and also the
440 // AddIn->symbol mapping is not inserted in other cases.
441 bool bOk = maExternalHashMap.emplace(rSymbol, rAddIn).second;
442 SAL_WARN_IF( !bOk, "formula.core", "OpCodeMap::putExternal: symbol not inserted, " << rSymbol << " -> " << rAddIn);
443 if (bOk)
445 bOk = maReverseExternalHashMap.emplace(rAddIn, rSymbol).second;
446 // Failed insertion of the AddIn is ok for different symbols mapping to
447 // the same AddIn. Make this INFO only.
448 SAL_INFO_IF( !bOk, "formula.core", "OpCodeMap::putExternal: AddIn not inserted, " << rAddIn << " -> " << rSymbol);
452 void FormulaCompiler::OpCodeMap::putExternalSoftly( const OUString & rSymbol, const OUString & rAddIn )
454 // Same as putExternal() but no warning, instead info whether inserted or not.
455 bool bOk = maExternalHashMap.emplace(rSymbol, rAddIn).second;
456 SAL_INFO( "formula.core", "OpCodeMap::putExternalSoftly: symbol " << (bOk ? "" : "not ") << "inserted, " << rSymbol << " -> " << rAddIn);
457 if (bOk)
459 bOk = maReverseExternalHashMap.emplace(rAddIn, rSymbol).second;
460 SAL_INFO_IF( !bOk, "formula.core", "OpCodeMap::putExternalSoftly: AddIn not inserted, " << rAddIn << " -> " << rSymbol);
464 uno::Sequence< sheet::FormulaToken > FormulaCompiler::OpCodeMap::createSequenceOfFormulaTokens(
465 const FormulaCompiler& rCompiler, const uno::Sequence< OUString >& rNames ) const
467 const sal_Int32 nLen = rNames.getLength();
468 uno::Sequence< sheet::FormulaToken > aTokens( nLen);
469 sheet::FormulaToken* pToken = aTokens.getArray();
470 OUString const * pName = rNames.getConstArray();
471 OUString const * const pStop = pName + nLen;
472 for ( ; pName < pStop; ++pName, ++pToken)
474 OpCodeHashMap::const_iterator iLook( maHashMap.find( *pName));
475 if (iLook != maHashMap.end())
476 pToken->OpCode = (*iLook).second;
477 else
479 OUString aIntName;
480 if (hasExternals())
482 ExternalHashMap::const_iterator iExt( maExternalHashMap.find( *pName));
483 if (iExt != maExternalHashMap.end())
484 aIntName = (*iExt).second;
485 // Check for existence not needed here, only name-mapping is of
486 // interest.
488 if (aIntName.isEmpty())
489 aIntName = rCompiler.FindAddInFunction(*pName, !isEnglish()); // bLocalFirst=false for english
490 if (aIntName.isEmpty())
491 pToken->OpCode = getOpCodeUnknown();
492 else
494 pToken->OpCode = ocExternal;
495 pToken->Data <<= aIntName;
499 return aTokens;
502 uno::Sequence< sheet::FormulaOpCodeMapEntry > FormulaCompiler::OpCodeMap::createSequenceOfAvailableMappings(
503 const FormulaCompiler& rCompiler, const sal_Int32 nGroups ) const
505 using namespace sheet;
507 // Unfortunately uno::Sequence can't grow without cumbersome reallocs. As
508 // we don't know in advance how many elements it will have we use a
509 // temporary vector to add elements and then copy to Sequence :-(
510 ::std::vector< FormulaOpCodeMapEntry > aVec;
512 if (nGroups == FormulaMapGroup::SPECIAL)
514 // Use specific order, keep in sync with
515 // offapi/com/sun/star/sheet/FormulaMapGroupSpecialOffset.idl
516 static const struct
518 sal_Int32 nOff;
519 OpCode eOp;
520 } aMap[] = {
521 { FormulaMapGroupSpecialOffset::PUSH , ocPush } ,
522 { FormulaMapGroupSpecialOffset::CALL , ocCall } ,
523 { FormulaMapGroupSpecialOffset::STOP , ocStop } ,
524 { FormulaMapGroupSpecialOffset::EXTERNAL , ocExternal } ,
525 { FormulaMapGroupSpecialOffset::NAME , ocName } ,
526 { FormulaMapGroupSpecialOffset::NO_NAME , ocNoName } ,
527 { FormulaMapGroupSpecialOffset::MISSING , ocMissing } ,
528 { FormulaMapGroupSpecialOffset::BAD , ocBad } ,
529 { FormulaMapGroupSpecialOffset::SPACES , ocSpaces } ,
530 { FormulaMapGroupSpecialOffset::MAT_REF , ocMatRef } ,
531 { FormulaMapGroupSpecialOffset::DB_AREA , ocDBArea } ,
532 { FormulaMapGroupSpecialOffset::MACRO , ocMacro } ,
533 { FormulaMapGroupSpecialOffset::COL_ROW_NAME , ocColRowName } ,
534 { FormulaMapGroupSpecialOffset::WHITESPACE , ocWhitespace } ,
535 { FormulaMapGroupSpecialOffset::TABLE_REF , ocTableRef }
537 const size_t nCount = SAL_N_ELEMENTS(aMap);
538 // Preallocate vector elements.
539 FormulaOpCodeMapEntry aEntry;
540 aEntry.Token.OpCode = getOpCodeUnknown();
541 aVec.resize(nCount, aEntry);
543 for (auto& i : aMap)
545 size_t nIndex = static_cast< size_t >( i.nOff );
546 if (aVec.size() <= nIndex)
548 // The offsets really should be aligned with the size, so if
549 // the vector was preallocated above this code to resize it is
550 // just a measure in case the table isn't in sync with the API,
551 // usually it isn't executed.
552 aEntry.Token.OpCode = getOpCodeUnknown();
553 aVec.resize( nIndex + 1, aEntry );
555 aEntry.Token.OpCode = i.eOp;
556 aVec[nIndex] = aEntry;
559 else
561 /* FIXME: Once we support error constants in formulas we'll need a map
562 * group for that, e.g. FormulaMapGroup::ERROR_CONSTANTS, and fill
563 * SC_OPCODE_START_ERRORS to SC_OPCODE_STOP_ERRORS. */
565 // Anything else but SPECIAL.
566 if ((nGroups & FormulaMapGroup::SEPARATORS) != 0)
568 static const sal_uInt16 aOpCodes[] = {
569 SC_OPCODE_OPEN,
570 SC_OPCODE_CLOSE,
571 SC_OPCODE_SEP,
573 lclPushOpCodeMapEntries( aVec, mpTable.get(), aOpCodes, SAL_N_ELEMENTS(aOpCodes) );
575 if ((nGroups & FormulaMapGroup::ARRAY_SEPARATORS) != 0)
577 static const sal_uInt16 aOpCodes[] = {
578 SC_OPCODE_ARRAY_OPEN,
579 SC_OPCODE_ARRAY_CLOSE,
580 SC_OPCODE_ARRAY_ROW_SEP,
581 SC_OPCODE_ARRAY_COL_SEP
583 lclPushOpCodeMapEntries( aVec, mpTable.get(), aOpCodes, SAL_N_ELEMENTS(aOpCodes) );
585 if ((nGroups & FormulaMapGroup::UNARY_OPERATORS) != 0)
587 // Due to the nature of the percent operator following its operand
588 // it isn't sorted into unary operators for compiler interna.
589 lclPushOpCodeMapEntry( aVec, mpTable.get(), ocPercentSign );
590 // "+" can be used as unary operator too, push only if binary group is not set
591 if ((nGroups & FormulaMapGroup::BINARY_OPERATORS) == 0)
592 lclPushOpCodeMapEntry( aVec, mpTable.get(), ocAdd );
593 // regular unary operators
594 for (sal_uInt16 nOp = SC_OPCODE_START_UN_OP; nOp < SC_OPCODE_STOP_UN_OP && nOp < mnSymbols; ++nOp)
596 lclPushOpCodeMapEntry( aVec, mpTable.get(), nOp );
599 if ((nGroups & FormulaMapGroup::BINARY_OPERATORS) != 0)
601 for (sal_uInt16 nOp = SC_OPCODE_START_BIN_OP; nOp < SC_OPCODE_STOP_BIN_OP && nOp < mnSymbols; ++nOp)
603 switch (nOp)
605 // AND and OR in fact are functions but for legacy reasons
606 // are sorted into binary operators for compiler interna.
607 case SC_OPCODE_AND :
608 case SC_OPCODE_OR :
609 break; // nothing,
610 default:
611 lclPushOpCodeMapEntry( aVec, mpTable.get(), nOp );
615 if ((nGroups & FormulaMapGroup::FUNCTIONS) != 0)
617 // Function names are not consecutive, skip the gaps between
618 // functions with no parameter, functions with 1 parameter
619 lclPushOpCodeMapEntries( aVec, mpTable.get(), SC_OPCODE_START_NO_PAR,
620 ::std::min< sal_uInt16 >( SC_OPCODE_STOP_NO_PAR, mnSymbols ) );
621 lclPushOpCodeMapEntries( aVec, mpTable.get(), SC_OPCODE_START_1_PAR,
622 ::std::min< sal_uInt16 >( SC_OPCODE_STOP_1_PAR, mnSymbols ) );
623 // Additional functions not within range of functions.
624 static const sal_uInt16 aOpCodes[] = {
625 SC_OPCODE_IF,
626 SC_OPCODE_IF_ERROR,
627 SC_OPCODE_IF_NA,
628 SC_OPCODE_CHOOSE,
629 SC_OPCODE_LET,
630 SC_OPCODE_AND,
631 SC_OPCODE_OR
633 lclPushOpCodeMapEntries( aVec, mpTable.get(), aOpCodes, SAL_N_ELEMENTS(aOpCodes) );
634 // functions with 2 or more parameters.
635 for (sal_uInt16 nOp = SC_OPCODE_START_2_PAR; nOp < SC_OPCODE_STOP_2_PAR && nOp < mnSymbols; ++nOp)
637 switch (nOp)
639 // NO_NAME is in SPECIAL.
640 case SC_OPCODE_NO_NAME :
641 break; // nothing,
642 default:
643 lclPushOpCodeMapEntry( aVec, mpTable.get(), nOp );
646 // If AddIn functions are present in this mapping, use them, and only those.
647 if (hasExternals())
649 for (auto const& elem : maExternalHashMap)
651 FormulaOpCodeMapEntry aEntry;
652 aEntry.Name = elem.first;
653 aEntry.Token.Data <<= elem.second;
654 aEntry.Token.OpCode = ocExternal;
655 aVec.push_back( aEntry);
658 else
660 rCompiler.fillAddInToken( aVec, isEnglish());
664 return uno::Sequence< FormulaOpCodeMapEntry >(aVec.data(), aVec.size());
668 void FormulaCompiler::OpCodeMap::putOpCode( const OUString & rStr, const OpCode eOp, const CharClass* pCharClass )
670 if (0 < eOp && sal_uInt16(eOp) < mnSymbols)
672 bool bPutOp = mpTable[eOp].isEmpty();
673 bool bRemoveFromMap = false;
674 if (!bPutOp)
676 switch (eOp)
678 // These OpCodes are meant to overwrite and also remove an
679 // existing mapping.
680 case ocCurrency:
681 bPutOp = true;
682 bRemoveFromMap = true;
683 break;
684 // These separator OpCodes are meant to overwrite and also
685 // remove an existing mapping if it is not used for one of the
686 // other separators.
687 case ocArrayColSep:
688 bPutOp = true;
689 bRemoveFromMap = (mpTable[ocArrayRowSep] != mpTable[eOp] && mpTable[ocSep] != mpTable[eOp]);
690 break;
691 case ocArrayRowSep:
692 bPutOp = true;
693 bRemoveFromMap = (mpTable[ocArrayColSep] != mpTable[eOp] && mpTable[ocSep] != mpTable[eOp]);
694 break;
695 // For ocSep keep the ";" in map but remove any other if it is
696 // not used for ocArrayColSep or ocArrayRowSep.
697 case ocSep:
698 bPutOp = true;
699 bRemoveFromMap = (mpTable[eOp] != ";" &&
700 mpTable[ocArrayColSep] != mpTable[eOp] &&
701 mpTable[ocArrayRowSep] != mpTable[eOp]);
702 break;
703 // These OpCodes are known to be duplicates in the Excel
704 // external API mapping because of different parameter counts
705 // in different BIFF versions. Names are identical and entries
706 // are ignored.
707 case ocLinest:
708 case ocTrend:
709 case ocLogest:
710 case ocGrowth:
711 case ocTrunc:
712 case ocFixed:
713 case ocGetDayOfWeek:
714 case ocHLookup:
715 case ocVLookup:
716 case ocGetDiffDate360:
717 if (rStr == mpTable[eOp])
718 return;
719 [[fallthrough]];
720 // These OpCodes are known to be added to an existing mapping,
721 // but only for the OOXML external API mapping. This is *not*
722 // FormulaLanguage::OOXML. Keep the first
723 // (correct) definition for the OpCode, all following are
724 // additional alias entries in the map.
725 case ocErrorType:
726 case ocMultiArea:
727 case ocBackSolver:
728 case ocEasterSunday:
729 case ocCurrent:
730 case ocStyle:
731 if (mbEnglish &&
732 FormulaGrammar::extractFormulaLanguage( meGrammar) == FormulaGrammar::GRAM_EXTERNAL)
734 // Both bPutOp and bRemoveFromMap stay false.
735 break;
737 [[fallthrough]];
738 default:
739 SAL_WARN("formula.core",
740 "OpCodeMap::putOpCode: reusing OpCode " << static_cast<sal_uInt16>(eOp)
741 << ", replacing '" << mpTable[eOp] << "' with '" << rStr << "' in "
742 << (mbEnglish ? "" : "non-") << "English map 0x" << ::std::hex << meGrammar);
746 // Case preserving opcode -> string, upper string -> opcode
747 if (bRemoveFromMap)
749 OUString aUpper( pCharClass ? pCharClass->uppercase( mpTable[eOp]) : rStr.toAsciiUpperCase());
750 // Ensure we remove a mapping only for the requested OpCode.
751 OpCodeHashMap::const_iterator it( maHashMap.find( aUpper));
752 if (it != maHashMap.end() && (*it).second == eOp)
753 maHashMap.erase( it);
755 if (bPutOp)
756 mpTable[eOp] = rStr;
757 OUString aUpper( pCharClass ? pCharClass->uppercase( rStr) : rStr.toAsciiUpperCase());
758 maHashMap.emplace(aUpper, eOp);
760 else
762 SAL_WARN( "formula.core", "OpCodeMap::putOpCode: OpCode out of range");
767 FormulaCompiler::FormulaCompiler( FormulaTokenArray& rArr, bool bComputeII, bool bMatrixFlag )
769 nCurrentFactorParam(0),
770 pArr( &rArr ),
771 maArrIterator( rArr ),
772 pCode( nullptr ),
773 pStack( nullptr ),
774 eLastOp( ocPush ),
775 nRecursion( 0 ),
776 nNumFmt( SvNumFormatType::UNDEFINED ),
777 pc( 0 ),
778 meGrammar( formula::FormulaGrammar::GRAM_UNSPECIFIED ),
779 bAutoCorrect( false ),
780 bCorrected( false ),
781 glSubTotal( false ),
782 needsRPNTokenCheck( false ),
783 mbJumpCommandReorder(true),
784 mbStopOnError(true),
785 mbComputeII(bComputeII),
786 mbMatrixFlag(bMatrixFlag)
790 FormulaTokenArray FormulaCompiler::smDummyTokenArray;
792 FormulaCompiler::FormulaCompiler(bool bComputeII, bool bMatrixFlag)
794 nCurrentFactorParam(0),
795 pArr( nullptr ),
796 maArrIterator( smDummyTokenArray ),
797 pCode( nullptr ),
798 pStack( nullptr ),
799 eLastOp( ocPush ),
800 nRecursion(0),
801 nNumFmt( SvNumFormatType::UNDEFINED ),
802 pc( 0 ),
803 meGrammar( formula::FormulaGrammar::GRAM_UNSPECIFIED ),
804 bAutoCorrect( false ),
805 bCorrected( false ),
806 glSubTotal( false ),
807 needsRPNTokenCheck( false ),
808 mbJumpCommandReorder(true),
809 mbStopOnError(true),
810 mbComputeII(bComputeII),
811 mbMatrixFlag(bMatrixFlag)
815 FormulaCompiler::~FormulaCompiler()
819 FormulaCompiler::OpCodeMapPtr FormulaCompiler::GetOpCodeMap( const sal_Int32 nLanguage ) const
821 const bool bTemporary = !HasOpCodeMap(nLanguage);
822 OpCodeMapPtr xMap = GetFinalOpCodeMap(nLanguage);
823 if (bTemporary)
824 const_cast<FormulaCompiler*>(this)->DestroyOpCodeMap(nLanguage);
825 return xMap;
828 FormulaCompiler::OpCodeMapPtr FormulaCompiler::GetFinalOpCodeMap( const sal_Int32 nLanguage ) const
830 FormulaCompiler::OpCodeMapPtr xMap;
831 using namespace sheet;
832 switch (nLanguage)
834 case FormulaLanguage::ODFF :
835 if (!mxSymbolsODFF)
836 InitSymbolsODFF( InitSymbols::INIT);
837 xMap = mxSymbolsODFF;
838 break;
839 case FormulaLanguage::ODF_11 :
840 if (!mxSymbolsPODF)
841 InitSymbolsPODF( InitSymbols::INIT);
842 xMap = mxSymbolsPODF;
843 break;
844 case FormulaLanguage::ENGLISH :
845 if (!mxSymbolsEnglish)
846 InitSymbolsEnglish( InitSymbols::INIT);
847 xMap = mxSymbolsEnglish;
848 break;
849 case FormulaLanguage::NATIVE :
850 if (!mxSymbolsNative)
851 InitSymbolsNative( InitSymbols::INIT);
852 xMap = mxSymbolsNative;
853 break;
854 case FormulaLanguage::XL_ENGLISH:
855 if (!mxSymbolsEnglishXL)
856 InitSymbolsEnglishXL( InitSymbols::INIT);
857 xMap = mxSymbolsEnglishXL;
858 break;
859 case FormulaLanguage::OOXML:
860 if (!mxSymbolsOOXML)
861 InitSymbolsOOXML( InitSymbols::INIT);
862 xMap = mxSymbolsOOXML;
863 break;
864 case FormulaLanguage::API :
865 if (!mxSymbolsAPI)
866 InitSymbolsAPI( InitSymbols::INIT);
867 xMap = mxSymbolsAPI;
868 break;
869 default:
870 ; // nothing, NULL map returned
872 return xMap;
875 void FormulaCompiler::DestroyOpCodeMap( const sal_Int32 nLanguage )
877 using namespace sheet;
878 switch (nLanguage)
880 case FormulaLanguage::ODFF :
881 InitSymbolsODFF( InitSymbols::DESTROY);
882 break;
883 case FormulaLanguage::ODF_11 :
884 InitSymbolsPODF( InitSymbols::DESTROY);
885 break;
886 case FormulaLanguage::ENGLISH :
887 InitSymbolsEnglish( InitSymbols::DESTROY);
888 break;
889 case FormulaLanguage::NATIVE :
890 InitSymbolsNative( InitSymbols::DESTROY);
891 break;
892 case FormulaLanguage::XL_ENGLISH:
893 InitSymbolsEnglishXL( InitSymbols::DESTROY);
894 break;
895 case FormulaLanguage::OOXML:
896 InitSymbolsOOXML( InitSymbols::DESTROY);
897 break;
898 case FormulaLanguage::API :
899 InitSymbolsAPI( InitSymbols::DESTROY);
900 break;
901 default:
902 ; // nothing
906 bool FormulaCompiler::HasOpCodeMap( const sal_Int32 nLanguage ) const
908 using namespace sheet;
909 switch (nLanguage)
911 case FormulaLanguage::ODFF :
912 return InitSymbolsODFF( InitSymbols::ASK);
913 case FormulaLanguage::ODF_11 :
914 return InitSymbolsPODF( InitSymbols::ASK);
915 case FormulaLanguage::ENGLISH :
916 return InitSymbolsEnglish( InitSymbols::ASK);
917 case FormulaLanguage::NATIVE :
918 return InitSymbolsNative( InitSymbols::ASK);
919 case FormulaLanguage::XL_ENGLISH:
920 return InitSymbolsEnglishXL( InitSymbols::ASK);
921 case FormulaLanguage::OOXML:
922 return InitSymbolsOOXML( InitSymbols::ASK);
923 case FormulaLanguage::API :
924 return InitSymbolsAPI( InitSymbols::ASK);
925 default:
926 ; // nothing
928 return false;
931 OUString FormulaCompiler::FindAddInFunction( const OUString& /*rUpperName*/, bool /*bLocalFirst*/ ) const
933 return OUString();
936 FormulaCompiler::OpCodeMapPtr FormulaCompiler::CreateOpCodeMap(
937 const uno::Sequence<
938 const sheet::FormulaOpCodeMapEntry > & rMapping,
939 bool bEnglish )
941 using sheet::FormulaOpCodeMapEntry;
942 // Filter / API maps are never Core
943 NonConstOpCodeMapPtr xMap = std::make_shared<OpCodeMap>( SC_OPCODE_LAST_OPCODE_ID + 1, false,
944 FormulaGrammar::mergeToGrammar( FormulaGrammar::setEnglishBit(
945 FormulaGrammar::GRAM_EXTERNAL, bEnglish), FormulaGrammar::CONV_UNSPECIFIED));
946 std::unique_ptr<CharClass> xCharClass( xMap->isEnglish() ? nullptr : createCharClassIfNonEnglishUI());
947 const CharClass* pCharClass = xCharClass.get();
948 for (auto const& rMapEntry : rMapping)
950 OpCode eOp = OpCode(rMapEntry.Token.OpCode);
951 if (eOp != ocExternal)
952 xMap->putOpCode( rMapEntry.Name, eOp, pCharClass);
953 else
955 OUString aExternalName;
956 if (rMapEntry.Token.Data >>= aExternalName)
957 xMap->putExternal( rMapEntry.Name, aExternalName);
958 else
960 SAL_WARN( "formula.core", "FormulaCompiler::CreateOpCodeMap: no Token.Data external name");
964 return xMap;
967 static bool lcl_fillNativeSymbols( FormulaCompiler::NonConstOpCodeMapPtr& xMap, FormulaCompiler::InitSymbols eWhat = FormulaCompiler::InitSymbols::INIT )
969 static OpCodeMapData aSymbolMap;
970 static std::map<OUString, OpCodeMapData> aLocaleSymbolMap;
971 std::unique_lock aGuard(aSymbolMap.maMtx);
973 if (comphelper::LibreOfficeKit::isActive())
975 OUString language = comphelper::LibreOfficeKit::getLanguageTag().getLanguage();
976 if (eWhat == FormulaCompiler::InitSymbols::ASK)
978 return aLocaleSymbolMap.contains(language)
979 && bool(aLocaleSymbolMap[language].mxSymbolMap);
981 else if (eWhat == FormulaCompiler::InitSymbols::DESTROY)
983 aLocaleSymbolMap[language].mxSymbolMap.reset();
985 else if (!aLocaleSymbolMap[language].mxSymbolMap)
987 // Core
988 aLocaleSymbolMap[language].mxSymbolMap = std::make_shared<FormulaCompiler::OpCodeMap>(
989 SC_OPCODE_LAST_OPCODE_ID + 1, true, FormulaGrammar::GRAM_NATIVE_UI);
990 OpCodeList aOpCodeListSymbols(RID_STRLIST_FUNCTION_NAMES_SYMBOLS,
991 aLocaleSymbolMap[language].mxSymbolMap);
992 OpCodeList aOpCodeListNative(RID_STRLIST_FUNCTION_NAMES,
993 aLocaleSymbolMap[language].mxSymbolMap);
994 // No AddInMap for native core mapping.
997 xMap = aLocaleSymbolMap[language].mxSymbolMap;
999 else
1001 if (eWhat == FormulaCompiler::InitSymbols::ASK)
1003 return bool(aSymbolMap.mxSymbolMap);
1005 else if (eWhat == FormulaCompiler::InitSymbols::DESTROY)
1007 aSymbolMap.mxSymbolMap.reset();
1009 else if (!aSymbolMap.mxSymbolMap)
1011 // Core
1012 aSymbolMap.mxSymbolMap = std::make_shared<FormulaCompiler::OpCodeMap>(
1013 SC_OPCODE_LAST_OPCODE_ID + 1, true, FormulaGrammar::GRAM_NATIVE_UI);
1014 OpCodeList aOpCodeListSymbols(RID_STRLIST_FUNCTION_NAMES_SYMBOLS,
1015 aSymbolMap.mxSymbolMap);
1016 OpCodeList aOpCodeListNative(RID_STRLIST_FUNCTION_NAMES, aSymbolMap.mxSymbolMap);
1017 // No AddInMap for native core mapping.
1020 xMap = aSymbolMap.mxSymbolMap;
1023 return true;
1026 const OUString& FormulaCompiler::GetNativeSymbol( OpCode eOp )
1028 NonConstOpCodeMapPtr xSymbolsNative;
1029 lcl_fillNativeSymbols( xSymbolsNative);
1030 return xSymbolsNative->getSymbol( eOp );
1033 sal_Unicode FormulaCompiler::GetNativeSymbolChar( OpCode eOp )
1035 return GetNativeSymbol(eOp)[0];
1038 bool FormulaCompiler::InitSymbolsNative( FormulaCompiler::InitSymbols eWhat ) const
1040 return lcl_fillNativeSymbols( mxSymbolsNative, eWhat);
1043 bool FormulaCompiler::InitSymbolsEnglish( FormulaCompiler::InitSymbols eWhat ) const
1045 static OpCodeMapData aMap;
1046 std::unique_lock aGuard(aMap.maMtx);
1047 if (eWhat == InitSymbols::ASK)
1048 return bool(aMap.mxSymbolMap);
1049 else if (eWhat == InitSymbols::DESTROY)
1050 aMap.mxSymbolMap.reset();
1051 else if (!aMap.mxSymbolMap)
1052 loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH, FormulaGrammar::GRAM_ENGLISH, aMap.mxSymbolMap);
1053 mxSymbolsEnglish = aMap.mxSymbolMap;
1054 return true;
1057 bool FormulaCompiler::InitSymbolsPODF( FormulaCompiler::InitSymbols eWhat ) const
1059 static OpCodeMapData aMap;
1060 std::unique_lock aGuard(aMap.maMtx);
1061 if (eWhat == InitSymbols::ASK)
1062 return bool(aMap.mxSymbolMap);
1063 else if (eWhat == InitSymbols::DESTROY)
1064 aMap.mxSymbolMap.reset();
1065 else if (!aMap.mxSymbolMap)
1066 loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH_PODF, FormulaGrammar::GRAM_PODF, aMap.mxSymbolMap, SeparatorType::RESOURCE_BASE);
1067 mxSymbolsPODF = aMap.mxSymbolMap;
1068 return true;
1071 bool FormulaCompiler::InitSymbolsAPI( FormulaCompiler::InitSymbols eWhat ) const
1073 static OpCodeMapData aMap;
1074 std::unique_lock aGuard(aMap.maMtx);
1075 if (eWhat == InitSymbols::ASK)
1076 return bool(aMap.mxSymbolMap);
1077 else if (eWhat == InitSymbols::DESTROY)
1078 aMap.mxSymbolMap.reset();
1079 else if (!aMap.mxSymbolMap)
1080 loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH_API, FormulaGrammar::GRAM_API, aMap.mxSymbolMap, SeparatorType::RESOURCE_BASE);
1081 mxSymbolsAPI = aMap.mxSymbolMap;
1082 return true;
1085 bool FormulaCompiler::InitSymbolsODFF( FormulaCompiler::InitSymbols eWhat ) const
1087 static OpCodeMapData aMap;
1088 std::unique_lock aGuard(aMap.maMtx);
1089 if (eWhat == InitSymbols::ASK)
1090 return bool(aMap.mxSymbolMap);
1091 else if (eWhat == InitSymbols::DESTROY)
1092 aMap.mxSymbolMap.reset();
1093 else if (!aMap.mxSymbolMap)
1094 loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH_ODFF, FormulaGrammar::GRAM_ODFF, aMap.mxSymbolMap, SeparatorType::RESOURCE_BASE);
1095 mxSymbolsODFF = aMap.mxSymbolMap;
1096 return true;
1099 bool FormulaCompiler::InitSymbolsEnglishXL( FormulaCompiler::InitSymbols eWhat ) const
1101 static OpCodeMapData aMap;
1102 std::unique_lock aGuard(aMap.maMtx);
1103 if (eWhat == InitSymbols::ASK)
1104 return bool(aMap.mxSymbolMap);
1105 else if (eWhat == InitSymbols::DESTROY)
1106 aMap.mxSymbolMap.reset();
1107 else if (!aMap.mxSymbolMap)
1108 loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH, FormulaGrammar::GRAM_ENGLISH, aMap.mxSymbolMap);
1109 mxSymbolsEnglishXL = aMap.mxSymbolMap;
1110 if (eWhat != InitSymbols::INIT)
1111 return true;
1113 // TODO: For now, just replace the separators to the Excel English
1114 // variants. Later, if we want to properly map Excel functions with Calc
1115 // functions, we'll need to do a little more work here.
1116 mxSymbolsEnglishXL->putOpCode( OUString(','), ocSep, nullptr);
1117 mxSymbolsEnglishXL->putOpCode( OUString(','), ocArrayColSep, nullptr);
1118 mxSymbolsEnglishXL->putOpCode( OUString(';'), ocArrayRowSep, nullptr);
1120 return true;
1123 bool FormulaCompiler::InitSymbolsOOXML( FormulaCompiler::InitSymbols eWhat ) const
1125 static OpCodeMapData aMap;
1126 std::unique_lock aGuard(aMap.maMtx);
1127 if (eWhat == InitSymbols::ASK)
1128 return bool(aMap.mxSymbolMap);
1129 else if (eWhat == InitSymbols::DESTROY)
1130 aMap.mxSymbolMap.reset();
1131 else if (!aMap.mxSymbolMap)
1132 loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH_OOXML, FormulaGrammar::GRAM_OOXML, aMap.mxSymbolMap, SeparatorType::RESOURCE_BASE);
1133 mxSymbolsOOXML = aMap.mxSymbolMap;
1134 return true;
1138 void FormulaCompiler::loadSymbols(const std::pair<const char*, int>* pSymbols, FormulaGrammar::Grammar eGrammar,
1139 NonConstOpCodeMapPtr& rxMap, SeparatorType eSepType) const
1141 if ( rxMap )
1142 return;
1144 // not Core
1145 rxMap = std::make_shared<OpCodeMap>( SC_OPCODE_LAST_OPCODE_ID + 1, eGrammar != FormulaGrammar::GRAM_ODFF, eGrammar );
1146 OpCodeList aOpCodeList(pSymbols, rxMap, eSepType);
1148 fillFromAddInMap( rxMap, eGrammar);
1149 // Fill from collection for AddIns not already present.
1150 if (FormulaGrammar::GRAM_ENGLISH == eGrammar)
1151 fillFromAddInCollectionEnglishName( rxMap);
1152 else
1154 fillFromAddInCollectionUpperName( rxMap);
1155 if (FormulaGrammar::GRAM_API == eGrammar)
1157 // Add known but not in AddInMap English names, e.g. from the
1158 // PricingFunctions AddIn or any user supplied AddIn.
1159 fillFromAddInCollectionEnglishName( rxMap);
1161 else if (FormulaGrammar::GRAM_OOXML == eGrammar)
1163 // Add specified Add-In compatibility name.
1164 fillFromAddInCollectionExcelName( rxMap);
1169 void FormulaCompiler::fillFromAddInCollectionUpperName( const NonConstOpCodeMapPtr& /*xMap */) const
1173 void FormulaCompiler::fillFromAddInCollectionEnglishName( const NonConstOpCodeMapPtr& /*xMap */) const
1177 void FormulaCompiler::fillFromAddInCollectionExcelName( const NonConstOpCodeMapPtr& /*xMap */) const
1181 void FormulaCompiler::fillFromAddInMap( const NonConstOpCodeMapPtr& /*xMap*/, FormulaGrammar::Grammar /*_eGrammar */) const
1185 OpCode FormulaCompiler::GetEnglishOpCode( const OUString& rName ) const
1187 FormulaCompiler::OpCodeMapPtr xMap = GetOpCodeMap( sheet::FormulaLanguage::ENGLISH);
1189 formula::OpCodeHashMap::const_iterator iLook( xMap->getHashMap().find( rName ) );
1190 bool bFound = (iLook != xMap->getHashMap().end());
1191 return bFound ? (*iLook).second : ocNone;
1194 bool FormulaCompiler::IsOpCodeVolatile( OpCode eOp )
1196 bool bRet = false;
1197 switch (eOp)
1199 // no parameters:
1200 case ocRandom:
1201 case ocGetActDate:
1202 case ocGetActTime:
1203 // one parameter:
1204 case ocFormula:
1205 case ocInfo:
1206 // more than one parameters:
1207 // ocIndirect otherwise would have to do
1208 // StopListening and StartListening on a reference for every
1209 // interpreted value.
1210 case ocIndirect:
1211 // ocOffset results in indirect references.
1212 case ocOffset:
1213 // ocDebugVar shows internal value that may change as the internal state changes.
1214 case ocDebugVar:
1215 // ocRandArray is a volatile function.
1216 case ocRandArray:
1217 bRet = true;
1218 break;
1219 default:
1220 bRet = false;
1221 break;
1223 return bRet;
1226 bool FormulaCompiler::IsOpCodeJumpCommand( OpCode eOp )
1228 switch (eOp)
1230 case ocIf:
1231 case ocIfError:
1232 case ocIfNA:
1233 case ocChoose:
1234 case ocLet:
1235 return true;
1236 default:
1239 return false;
1242 // Remove quotes, escaped quotes are unescaped.
1243 bool FormulaCompiler::DeQuote( OUString& rStr )
1245 sal_Int32 nLen = rStr.getLength();
1246 if ( nLen > 1 && rStr[0] == '\'' && rStr[ nLen-1 ] == '\'' )
1248 rStr = rStr.copy( 1, nLen-2 );
1249 rStr = rStr.replaceAll( "''", "'" );
1250 return true;
1252 return false;
1255 void FormulaCompiler::fillAddInToken(
1256 ::std::vector< sheet::FormulaOpCodeMapEntry >& /*_rVec*/,
1257 bool /*_bIsEnglish*/) const
1261 bool FormulaCompiler::IsMatrixFunction( OpCode eOpCode )
1263 switch (eOpCode)
1265 case ocDde :
1266 case ocGrowth :
1267 case ocTrend :
1268 case ocLogest :
1269 case ocLinest :
1270 case ocFrequency :
1271 case ocMatSequence :
1272 case ocMatTrans :
1273 case ocMatMult :
1274 case ocMatInv :
1275 case ocMatrixUnit :
1276 case ocModalValue_Multi :
1277 case ocFourier :
1278 case ocFilter :
1279 case ocSort :
1280 case ocSortBy :
1281 case ocRandArray :
1282 case ocUnique :
1283 case ocLet :
1284 return true;
1285 default:
1287 // added to avoid warnings
1290 return false;
1294 void FormulaCompiler::OpCodeMap::putCopyOpCode( const OUString& rSymbol, OpCode eOp, const CharClass* pCharClass )
1296 SAL_WARN_IF( !mpTable[eOp].isEmpty() && rSymbol.isEmpty(), "formula.core",
1297 "OpCodeMap::putCopyOpCode: NOT replacing OpCode " << static_cast<sal_uInt16>(eOp)
1298 << " '" << mpTable[eOp] << "' with empty name!");
1299 if (!mpTable[eOp].isEmpty() && rSymbol.isEmpty())
1301 OUString aUpper( pCharClass ? pCharClass->uppercase( mpTable[eOp]) : mpTable[eOp].toAsciiUpperCase());
1302 maHashMap.emplace(aUpper, eOp);
1304 else
1306 OUString aUpper( pCharClass ? pCharClass->uppercase( rSymbol) : rSymbol.toAsciiUpperCase());
1307 mpTable[eOp] = rSymbol;
1308 maHashMap.emplace(aUpper, eOp);
1312 void FormulaCompiler::OpCodeMap::copyFrom( const OpCodeMap& r )
1314 maHashMap = OpCodeHashMap( mnSymbols);
1316 sal_uInt16 n = r.getSymbolCount();
1317 SAL_WARN_IF( n != mnSymbols, "formula.core",
1318 "OpCodeMap::copyFrom: unequal size, this: " << mnSymbols << " that: " << n);
1319 if (n > mnSymbols)
1320 n = mnSymbols;
1322 // OpCode 0 (ocPush) should never be in a map.
1323 SAL_WARN_IF( !mpTable[0].isEmpty() || !r.mpTable[0].isEmpty(), "formula.core",
1324 "OpCodeMap::copyFrom: OpCode 0 assigned, this: '"
1325 << mpTable[0] << "' that: '" << r.mpTable[0] << "'");
1327 std::unique_ptr<CharClass> xCharClass( r.mbEnglish ? nullptr : createCharClassIfNonEnglishUI());
1328 const CharClass* pCharClass = xCharClass.get();
1330 // For bOverrideKnownBad when copying from the English core map (ODF 1.1
1331 // and API) to the native map (UI "use English function names") replace the
1332 // known bad legacy function names with correct ones.
1333 if (r.mbCore &&
1334 FormulaGrammar::extractFormulaLanguage( meGrammar) == sheet::FormulaLanguage::NATIVE &&
1335 FormulaGrammar::extractFormulaLanguage( r.meGrammar) == sheet::FormulaLanguage::ENGLISH)
1337 for (sal_uInt16 i = 1; i < n; ++i)
1339 OUString aSymbol;
1340 OpCode eOp = OpCode(i);
1341 switch (eOp)
1343 case ocRRI:
1344 aSymbol = "RRI";
1345 break;
1346 case ocTableOp:
1347 aSymbol = "MULTIPLE.OPERATIONS";
1348 break;
1349 default:
1350 aSymbol = r.mpTable[i];
1352 putCopyOpCode( aSymbol, eOp, pCharClass);
1355 else
1357 for (sal_uInt16 i = 1; i < n; ++i)
1359 OpCode eOp = OpCode(i);
1360 const OUString& rSymbol = r.mpTable[i];
1361 putCopyOpCode( rSymbol, eOp, pCharClass);
1365 // This was meant to copy to native map that does not have AddIn symbols
1366 // but needs them from the source map. It is unclear what should happen if
1367 // the destination already had externals, so do it only if it doesn't.
1368 if (!hasExternals())
1370 maExternalHashMap = r.maExternalHashMap;
1371 maReverseExternalHashMap = r.maReverseExternalHashMap;
1372 mbCore = r.mbCore;
1373 if (mbEnglish != r.mbEnglish)
1375 // For now keep mbEnglishLocale setting, which is false for a
1376 // non-English native map we're copying to.
1377 /* TODO:
1378 if (!mbEnglish && r.mbEnglish)
1379 mbEnglishLocale = "getUseEnglishLocaleFromConfiguration()";
1380 or set from outside i.e. via ScCompiler.
1382 mbEnglish = r.mbEnglish;
1388 FormulaError FormulaCompiler::GetErrorConstant( const OUString& rName ) const
1390 FormulaError nError = FormulaError::NONE;
1391 OpCodeHashMap::const_iterator iLook( mxSymbols->getHashMap().find( rName));
1392 if (iLook != mxSymbols->getHashMap().end())
1394 switch ((*iLook).second)
1396 // Not all may make sense in a formula, but these we know as
1397 // opcodes.
1398 case ocErrNull:
1399 nError = FormulaError::NoCode;
1400 break;
1401 case ocErrDivZero:
1402 nError = FormulaError::DivisionByZero;
1403 break;
1404 case ocErrValue:
1405 nError = FormulaError::NoValue;
1406 break;
1407 case ocErrRef:
1408 nError = FormulaError::NoRef;
1409 break;
1410 case ocErrName:
1411 nError = FormulaError::NoName;
1412 break;
1413 case ocErrNum:
1414 nError = FormulaError::IllegalFPOperation;
1415 break;
1416 case ocErrNA:
1417 nError = FormulaError::NotAvailable;
1418 break;
1419 default:
1420 ; // nothing
1423 else
1425 // Per convention recognize detailed "#ERRxxx!" constants, always
1426 // untranslated. Error numbers are sal_uInt16 so at most 5 decimal
1427 // digits.
1428 if (rName.startsWithIgnoreAsciiCase("#ERR") && rName.getLength() <= 10 && rName[rName.getLength()-1] == '!')
1430 sal_uInt32 nErr = o3tl::toUInt32(rName.subView( 4, rName.getLength() - 5));
1431 if (0 < nErr && nErr <= SAL_MAX_UINT16 && isPublishedFormulaError(static_cast<FormulaError>(nErr)))
1432 nError = static_cast<FormulaError>(nErr);
1435 return nError;
1438 void FormulaCompiler::EnableJumpCommandReorder( bool bEnable )
1440 mbJumpCommandReorder = bEnable;
1443 void FormulaCompiler::EnableStopOnError( bool bEnable )
1445 mbStopOnError = bEnable;
1448 void FormulaCompiler::AppendErrorConstant( OUStringBuffer& rBuffer, FormulaError nError ) const
1450 OpCode eOp;
1451 switch (nError)
1453 case FormulaError::NoCode:
1454 eOp = ocErrNull;
1455 break;
1456 case FormulaError::DivisionByZero:
1457 eOp = ocErrDivZero;
1458 break;
1459 case FormulaError::NoValue:
1460 eOp = ocErrValue;
1461 break;
1462 case FormulaError::NoRef:
1463 eOp = ocErrRef;
1464 break;
1465 case FormulaError::NoName:
1466 eOp = ocErrName;
1467 break;
1468 case FormulaError::IllegalFPOperation:
1469 eOp = ocErrNum;
1470 break;
1471 case FormulaError::NotAvailable:
1472 eOp = ocErrNA;
1473 break;
1474 default:
1476 // Per convention create detailed "#ERRxxx!" constants, always
1477 // untranslated.
1478 rBuffer.append("#ERR");
1479 rBuffer.append(static_cast<sal_Int32>(nError));
1480 rBuffer.append('!');
1481 return;
1484 rBuffer.append( mxSymbols->getSymbol( eOp));
1487 constexpr short nRecursionMax = 100;
1489 bool FormulaCompiler::GetToken()
1491 FormulaCompilerRecursionGuard aRecursionGuard( nRecursion );
1492 if ( nRecursion > nRecursionMax )
1494 SetError( FormulaError::StackOverflow );
1495 mpLastToken = mpToken = new FormulaByteToken( ocStop );
1496 return false;
1498 if ( bAutoCorrect && !pStack )
1499 { // don't merge stacked subroutine code into entered formula
1500 aCorrectedFormula += aCorrectedSymbol;
1501 aCorrectedSymbol.clear();
1503 bool bStop = false;
1504 if (pArr->GetCodeError() != FormulaError::NONE && mbStopOnError)
1505 bStop = true;
1506 else
1508 FormulaTokenRef pSpacesToken;
1509 short nWasColRowName;
1510 if ( pArr->OpCodeBefore( maArrIterator.GetIndex() ) == ocColRowName )
1511 nWasColRowName = 1;
1512 else
1513 nWasColRowName = 0;
1514 OpCode eTmpOp;
1515 mpToken = maArrIterator.Next();
1516 while (mpToken && ((eTmpOp = mpToken->GetOpCode()) == ocSpaces || eTmpOp == ocWhitespace))
1518 if (eTmpOp == ocSpaces)
1520 // For significant whitespace remember last ocSpaces token.
1521 // Usually there's only one even for multiple spaces.
1522 pSpacesToken = mpToken;
1523 if ( nWasColRowName )
1524 nWasColRowName++;
1526 if ( bAutoCorrect && !pStack )
1527 CreateStringFromToken( aCorrectedFormula, mpToken.get() );
1528 mpToken = maArrIterator.Next();
1530 if ( bAutoCorrect && !pStack && mpToken )
1531 CreateStringFromToken( aCorrectedSymbol, mpToken.get() );
1532 if( !mpToken )
1534 if( pStack )
1536 PopTokenArray();
1537 // mpLastToken was popped as well and corresponds to the
1538 // then current last token during PushTokenArray(), e.g. for
1539 // HandleRange().
1540 return GetToken();
1542 else
1543 bStop = true;
1545 else
1547 if ( nWasColRowName >= 2 && mpToken->GetOpCode() == ocColRowName )
1548 { // convert an ocSpaces to ocIntersect in RPN
1549 mpLastToken = mpToken = new FormulaByteToken( ocIntersect );
1550 maArrIterator.StepBack(); // we advanced to the second ocColRowName, step back
1552 else if (pSpacesToken && FormulaGrammar::isExcelSyntax( meGrammar) &&
1553 mpLastToken && mpToken &&
1554 isPotentialRangeType( mpToken.get(), false, true) &&
1555 (mpLastToken->GetOpCode() == ocClose || isPotentialRangeType( mpLastToken.get(), false, false)))
1557 // Let IntersectionLine() <- Factor() decide how to treat this,
1558 // once the actual arguments are determined in RPN.
1559 mpLastToken = mpToken = std::move(pSpacesToken);
1560 maArrIterator.StepBack(); // step back from next non-spaces token
1561 return true;
1565 if( bStop )
1567 mpLastToken = mpToken = new FormulaByteToken( ocStop );
1568 return false;
1571 // Remember token for next round and any PushTokenArray() calls that may
1572 // occur in handlers.
1573 mpLastToken = mpToken;
1575 if ( mpToken->IsExternalRef() )
1577 return HandleExternalReference(*mpToken);
1579 else
1581 switch (mpToken->GetOpCode())
1583 case ocSubTotal:
1584 case ocAggregate:
1585 glSubTotal = true;
1586 break;
1587 case ocStringName:
1588 if( HandleStringName())
1589 return true;
1590 else
1591 return false;
1592 case ocName:
1593 if( HandleRange())
1595 // Expanding ocName might have introduced tokens such as ocStyle that prevent formula threading,
1596 // but those wouldn't be present in the raw tokens array, so ensure RPN tokens will be checked too.
1597 needsRPNTokenCheck = true;
1598 return true;
1600 return false;
1601 case ocColRowName:
1602 return HandleColRowName();
1603 case ocDBArea:
1604 return HandleDbData();
1605 case ocTableRef:
1606 return HandleTableRef();
1607 case ocPush:
1608 if( mbComputeII )
1609 HandleIIOpCode(mpToken.get(), nullptr, 0);
1610 break;
1611 default:
1612 ; // nothing
1615 return true;
1619 // RPN creation by recursion
1620 void FormulaCompiler::Factor()
1622 if (pArr->GetCodeError() != FormulaError::NONE && mbStopOnError)
1623 return;
1625 CurrentFactor pFacToken( this );
1627 OpCode eOp = mpToken->GetOpCode();
1628 if (eOp == ocPush || eOp == ocColRowNameAuto || eOp == ocMatRef || eOp == ocDBArea
1629 || eOp == ocTableRef
1630 || (!mbJumpCommandReorder && ((eOp == ocName) || (eOp == ocColRowName) || (eOp == ocBad)))
1633 PutCode( mpToken );
1634 eOp = NextToken();
1635 if( eOp == ocOpen )
1637 // PUSH( is an error that may be caused by an unknown function.
1638 SetError(
1639 ( mpToken->GetType() == svString
1640 || mpToken->GetType() == svSingleRef )
1641 ? FormulaError::NoName : FormulaError::OperatorExpected );
1642 if ( bAutoCorrect && !pStack )
1643 { // assume multiplication
1644 aCorrectedFormula += mxSymbols->getSymbol( ocMul);
1645 bCorrected = true;
1646 NextToken();
1647 eOp = Expression();
1648 if( eOp != ocClose )
1649 SetError( FormulaError::PairExpected);
1650 else
1651 NextToken();
1655 else if( eOp == ocOpen )
1657 NextToken();
1658 eOp = Expression();
1659 while ((eOp == ocSep) && (pArr->GetCodeError() == FormulaError::NONE || !mbStopOnError))
1660 { // range list (A1;A2) converted to (A1~A2)
1661 pFacToken = mpToken;
1662 NextToken();
1663 CheckSetForceArrayParameter( mpToken, 0);
1664 eOp = Expression();
1665 // Do not ignore error here, regardless of mbStopOnError, to not
1666 // change the formula expression in case of an unexpected state.
1667 if (pArr->GetCodeError() == FormulaError::NONE && pc >= 2)
1669 // Left and right operands must be reference or function
1670 // returning reference to form a range list.
1671 const FormulaToken* p = pCode[-2];
1672 if (p && isPotentialRangeType( p, true, false))
1674 p = pCode[-1];
1675 if (p && isPotentialRangeType( p, true, true))
1677 pFacToken->NewOpCode( ocUnion, FormulaToken::PrivateAccess());
1678 // XXX NOTE: the token's eType is still svSep here!
1679 PutCode( pFacToken);
1684 if (eOp != ocClose)
1685 SetError( FormulaError::PairExpected);
1686 else
1687 NextToken();
1689 /* TODO: if no conversion to ocUnion is involved this could collect
1690 * such expression as a list or (matrix) vector to be passed as
1691 * argument for one parameter (which in fact the ocUnion svRefList is a
1692 * special case of), which would require a new StackVar type and needed
1693 * to be handled by the interpreter for functions that could support it
1694 * (i.e. already handle VAR_ARGS or svRefList parameters). This is also
1695 * not defined by ODF.
1696 * Does Excel handle =SUM((1;2))?
1697 * As is, the interpreter catches extraneous uncalculated
1698 * subexpressions like 1 of (1;2) as error. */
1700 else
1702 if( nNumFmt == SvNumFormatType::UNDEFINED )
1703 nNumFmt = lcl_GetRetFormat( eOp );
1705 if ( IsOpCodeVolatile( eOp) )
1706 pArr->SetExclusiveRecalcModeAlways();
1707 else
1709 switch( eOp )
1711 // Functions recalculated on every document load.
1712 // ONLOAD_LENIENT here to be able to distinguish and not
1713 // force a recalc (if not in an ALWAYS or ONLOAD_MUST
1714 // context) but keep an imported result from for example
1715 // OOXML a DDE call. Will be recalculated for ODFF.
1716 case ocConvertOOo :
1717 case ocDde:
1718 case ocMacro:
1719 case ocWebservice:
1720 pArr->AddRecalcMode( ScRecalcMode::ONLOAD_LENIENT );
1721 break;
1722 // RANDBETWEEN() is volatile like RAND(). Other Add-In
1723 // functions may have to be recalculated or not, we don't
1724 // know, classify as ONLOAD_LENIENT.
1725 case ocExternal:
1726 if (mpToken->GetExternal() == "com.sun.star.sheet.addin.Analysis.getRandbetween")
1727 pArr->SetExclusiveRecalcModeAlways();
1728 else
1729 pArr->AddRecalcMode( ScRecalcMode::ONLOAD_LENIENT );
1730 break;
1731 // If the referred cell is moved the value changes.
1732 case ocColumn :
1733 case ocRow :
1734 pArr->SetRecalcModeOnRefMove();
1735 break;
1736 // ocCell needs recalc on move for some possible type values.
1737 // And recalc mode on load, tdf#60645
1738 case ocCell :
1739 pArr->SetRecalcModeOnRefMove();
1740 pArr->AddRecalcMode( ScRecalcMode::ONLOAD_MUST );
1741 break;
1742 case ocHyperLink :
1743 // Cell with hyperlink needs to be calculated on load to
1744 // get its matrix result generated.
1745 pArr->AddRecalcMode( ScRecalcMode::ONLOAD_MUST );
1746 pArr->SetHyperLink( true);
1747 break;
1748 default:
1749 ; // nothing
1752 if (SC_OPCODE_START_NO_PAR <= eOp && eOp < SC_OPCODE_STOP_NO_PAR)
1754 pFacToken = mpToken;
1755 eOp = NextToken();
1756 if (eOp != ocOpen)
1758 SetError( FormulaError::PairExpected);
1759 PutCode( pFacToken );
1761 else
1763 eOp = NextToken();
1764 if (eOp != ocClose)
1765 SetError( FormulaError::PairExpected);
1766 PutCode( pFacToken);
1767 NextToken();
1770 else if (SC_OPCODE_START_1_PAR <= eOp && eOp < SC_OPCODE_STOP_1_PAR)
1772 if (eOp == ocIsoWeeknum && FormulaGrammar::isODFF( meGrammar ))
1774 // tdf#50950 ocIsoWeeknum can have 2 arguments when saved by older versions of Calc;
1775 // the opcode then has to be changed to ocWeek for backward compatibility
1776 pFacToken = mpToken;
1777 eOp = NextToken();
1778 bool bNoParam = false;
1779 if (eOp == ocOpen)
1781 eOp = NextToken();
1782 if (eOp == ocClose)
1783 bNoParam = true;
1784 else
1786 CheckSetForceArrayParameter( mpToken, 0);
1787 eOp = Expression();
1790 else
1791 SetError( FormulaError::PairExpected);
1792 sal_uInt32 nSepCount = 0;
1793 const sal_uInt16 nSepPos = maArrIterator.GetIndex() - 1; // separator position, if any
1794 if( !bNoParam )
1796 nSepCount++;
1797 while ((eOp == ocSep) && (pArr->GetCodeError() == FormulaError::NONE || !mbStopOnError))
1799 NextToken();
1800 CheckSetForceArrayParameter( mpToken, nSepCount);
1801 nSepCount++;
1802 if (nSepCount > FORMULA_MAXPARAMS)
1803 SetError( FormulaError::CodeOverflow);
1804 eOp = Expression();
1807 if (eOp != ocClose)
1808 SetError( FormulaError::PairExpected);
1809 else
1810 NextToken();
1811 pFacToken->SetByte( nSepCount );
1812 if (nSepCount == 2)
1814 // An old mode!=1 indicates ISO week, remove argument if
1815 // literal double value and keep function. Anything else
1816 // can not be resolved, there exists no "like ISO but week
1817 // starts on Sunday" mode in WEEKNUM and for an expression
1818 // we can't determine.
1819 // Current index is nSepPos+3 if expression stops, or
1820 // nSepPos+4 if expression continues after the call because
1821 // we just called NextToken() to move away from it.
1822 if (pc >= 2 && (maArrIterator.GetIndex() == nSepPos + 3 || maArrIterator.GetIndex() == nSepPos + 4) &&
1823 pArr->TokenAt(nSepPos+1)->GetType() == svDouble &&
1824 pArr->TokenAt(nSepPos+1)->GetDouble() != 1.0 &&
1825 pArr->TokenAt(nSepPos+2)->GetOpCode() == ocClose &&
1826 pArr->RemoveToken( nSepPos, 2) == 2)
1828 maArrIterator.AfterRemoveToken( nSepPos, 2);
1829 // Remove the ocPush/svDouble just removed also from
1830 // the compiler local RPN array.
1831 --pCode; --pc;
1832 (*pCode)->DecRef(); // may be dead now
1833 pFacToken->SetByte( nSepCount - 1 );
1835 else
1837 // For the remaining two arguments cases use the
1838 // compatibility function.
1839 pFacToken->NewOpCode( ocWeeknumOOo, FormulaToken::PrivateAccess());
1842 PutCode( pFacToken );
1844 else
1846 // standard handling of 1-parameter opcodes
1847 pFacToken = mpToken;
1848 eOp = NextToken();
1849 if( nNumFmt == SvNumFormatType::UNDEFINED && eOp == ocNot )
1850 nNumFmt = SvNumFormatType::LOGICAL;
1851 if (eOp == ocOpen)
1853 NextToken();
1854 CheckSetForceArrayParameter( mpToken, 0);
1855 eOp = Expression();
1857 else
1858 SetError( FormulaError::PairExpected);
1859 if (eOp != ocClose)
1860 SetError( FormulaError::PairExpected);
1861 else if ( pArr->GetCodeError() == FormulaError::NONE )
1863 pFacToken->SetByte( 1 );
1864 if (mbComputeII)
1866 FormulaToken** pArg = pCode - 1;
1867 HandleIIOpCode(pFacToken, &pArg, 1);
1870 PutCode( pFacToken );
1871 NextToken();
1874 else if ((SC_OPCODE_START_2_PAR <= eOp && eOp < SC_OPCODE_STOP_2_PAR)
1875 || eOp == ocExternal
1876 || eOp == ocMacro
1877 || eOp == ocAnd
1878 || eOp == ocOr
1879 || eOp == ocBad
1880 || ( eOp >= ocInternalBegin && eOp <= ocInternalEnd )
1881 || (!mbJumpCommandReorder && IsOpCodeJumpCommand(eOp)))
1883 pFacToken = mpToken;
1884 OpCode eMyLastOp = eOp;
1885 eOp = NextToken();
1886 bool bNoParam = false;
1887 bool bBadName = false;
1888 if (eOp == ocOpen)
1890 eOp = NextToken();
1891 if (eOp == ocClose)
1892 bNoParam = true;
1893 else
1895 CheckSetForceArrayParameter( mpToken, 0);
1896 eOp = Expression();
1899 else if (eMyLastOp == ocBad)
1901 // Just a bad name, not an unknown function, no parameters, no
1902 // closing expected.
1903 bBadName = true;
1904 bNoParam = true;
1906 else
1907 SetError( FormulaError::PairExpected);
1908 sal_uInt32 nSepCount = 0;
1909 if( !bNoParam )
1911 bool bDoIICompute = mbComputeII;
1912 // Array of FormulaToken double pointers to collect the parameters of II opcodes.
1913 FormulaToken*** pArgArray = nullptr;
1914 if (bDoIICompute)
1916 pArgArray = static_cast<FormulaToken***>(alloca(sizeof(FormulaToken**)*FORMULA_MAXPARAMSII));
1917 if (!pArgArray)
1918 bDoIICompute = false;
1921 nSepCount++;
1923 if (bDoIICompute)
1924 pArgArray[nSepCount-1] = pCode - 1; // Add first argument
1926 while ((eOp == ocSep) && (pArr->GetCodeError() == FormulaError::NONE || !mbStopOnError))
1928 NextToken();
1929 CheckSetForceArrayParameter( mpToken, nSepCount);
1930 nSepCount++;
1931 if (nSepCount > FORMULA_MAXPARAMS)
1932 SetError( FormulaError::CodeOverflow);
1933 eOp = Expression();
1934 if (bDoIICompute && nSepCount <= FORMULA_MAXPARAMSII)
1935 pArgArray[nSepCount - 1] = pCode - 1; // Add rest of the arguments
1937 if (bDoIICompute)
1938 HandleIIOpCode(pFacToken, pArgArray,
1939 std::min(nSepCount, static_cast<sal_uInt32>(FORMULA_MAXPARAMSII)));
1941 bool bDone = false;
1942 if (bBadName)
1943 ; // nothing, keep current token for return
1944 else if (eOp != ocClose)
1945 SetError( FormulaError::PairExpected);
1946 else
1948 NextToken();
1949 bDone = true;
1951 // Jumps are just normal functions for the FunctionAutoPilot tree view
1952 if (!mbJumpCommandReorder && pFacToken->GetType() == svJump)
1953 pFacToken = new FormulaFAPToken( pFacToken->GetOpCode(), nSepCount, pFacToken );
1954 else
1955 pFacToken->SetByte( nSepCount );
1956 PutCode( pFacToken );
1958 if (bDone)
1959 AnnotateOperands();
1961 else if (IsOpCodeJumpCommand(eOp))
1963 // the PC counters are -1
1964 pFacToken = mpToken;
1965 switch (eOp)
1967 case ocIf:
1968 pFacToken->GetJump()[ 0 ] = 3; // if, else, behind
1969 break;
1970 case ocChoose:
1971 pFacToken->GetJump()[ 0 ] = FORMULA_MAXJUMPCOUNT + 1;
1972 break;
1973 case ocLet:
1974 pFacToken->GetJump()[ 0 ] = FORMULA_MAXPARAMS + 1;
1975 break;
1976 case ocIfError:
1977 case ocIfNA:
1978 pFacToken->GetJump()[ 0 ] = 2; // if, behind
1979 break;
1980 default:
1981 SAL_WARN("formula.core","Jump OpCode: " << +eOp);
1982 assert(!"FormulaCompiler::Factor: someone forgot to add a jump count case");
1984 eOp = NextToken();
1985 if (eOp == ocOpen)
1987 NextToken();
1988 CheckSetForceArrayParameter( mpToken, 0);
1989 eOp = Expression();
1991 else
1992 SetError( FormulaError::PairExpected);
1993 PutCode( pFacToken );
1994 // During AutoCorrect (since pArr->GetCodeError() is
1995 // ignored) an unlimited ocIf would crash because
1996 // ScRawToken::Clone() allocates the JumpBuffer according to
1997 // nJump[0]*2+2, which is 3*2+2 on ocIf and 2*2+2 ocIfError and ocIfNA.
1998 short nJumpMax;
1999 OpCode eFacOpCode = pFacToken->GetOpCode();
2000 switch (eFacOpCode)
2002 case ocIf:
2003 nJumpMax = 3;
2004 break;
2005 case ocChoose:
2006 nJumpMax = FORMULA_MAXJUMPCOUNT;
2007 break;
2008 case ocLet:
2009 nJumpMax = FORMULA_MAXPARAMS;
2010 break;
2011 case ocIfError:
2012 case ocIfNA:
2013 nJumpMax = 2;
2014 break;
2015 case ocStop:
2016 // May happen only if PutCode(pFacToken) ran into overflow.
2017 nJumpMax = 0;
2018 assert(pc == FORMULA_MAXTOKENS && pArr->GetCodeError() != FormulaError::NONE);
2019 break;
2020 default:
2021 nJumpMax = 0;
2022 SAL_WARN("formula.core","Jump OpCode: " << +eFacOpCode);
2023 assert(!"FormulaCompiler::Factor: someone forgot to add a jump max case");
2025 short nJumpCount = 0;
2026 while ( (nJumpCount < (FORMULA_MAXPARAMS - 1)) && (eOp == ocSep)
2027 && (pArr->GetCodeError() == FormulaError::NONE || !mbStopOnError))
2029 if ( ++nJumpCount <= nJumpMax )
2030 pFacToken->GetJump()[nJumpCount] = pc-1;
2031 NextToken();
2032 CheckSetForceArrayParameter( mpToken, nJumpCount - 1);
2033 eOp = Expression();
2034 // ocSep or ocClose terminate the subexpression
2035 PutCode( mpToken );
2037 if (eOp != ocClose)
2038 SetError( FormulaError::PairExpected);
2039 else
2041 NextToken();
2042 // always limit to nJumpMax, no arbitrary overwrites
2043 if ( ++nJumpCount <= nJumpMax )
2044 pFacToken->GetJump()[ nJumpCount ] = pc-1;
2045 eFacOpCode = pFacToken->GetOpCode();
2046 bool bLimitOk;
2047 switch (eFacOpCode)
2049 case ocIf:
2050 bLimitOk = (nJumpCount <= 3);
2051 break;
2052 case ocChoose:
2053 bLimitOk = (nJumpCount < FORMULA_MAXJUMPCOUNT);
2054 break;
2055 case ocLet:
2056 bLimitOk = (nJumpCount < FORMULA_MAXPARAMS);
2057 break;
2058 case ocIfError:
2059 case ocIfNA:
2060 bLimitOk = (nJumpCount <= 2);
2061 break;
2062 case ocStop:
2063 // May happen only if PutCode(pFacToken) ran into overflow.
2064 // This may had resulted from a stacked token array and
2065 // error wasn't propagated so assert only the program
2066 // counter.
2067 bLimitOk = false;
2068 assert(pc == FORMULA_MAXTOKENS);
2069 break;
2070 default:
2071 bLimitOk = false;
2072 SAL_WARN("formula.core","Jump OpCode: " << +eFacOpCode);
2073 assert(!"FormulaCompiler::Factor: someone forgot to add a jump limit case");
2075 if (bLimitOk)
2076 pFacToken->GetJump()[ 0 ] = nJumpCount;
2077 else
2078 SetError( FormulaError::IllegalParameter);
2081 else if ( eOp == ocMissing )
2083 PutCode( mpToken );
2084 NextToken();
2086 else if ( eOp == ocClose )
2088 SetError( FormulaError::ParameterExpected );
2090 else if ( eOp == ocSep )
2091 { // Subsequent ocSep
2092 SetError( FormulaError::ParameterExpected );
2093 if ( bAutoCorrect && !pStack )
2095 aCorrectedSymbol.clear();
2096 bCorrected = true;
2099 else if ( mpToken->IsExternalRef() )
2101 PutCode( mpToken);
2102 NextToken();
2104 else
2106 SetError( FormulaError::UnknownToken );
2107 if ( bAutoCorrect && !pStack )
2109 if ( eOp == ocStop )
2110 { // trailing operator w/o operand
2111 sal_Int32 nLen = aCorrectedFormula.getLength();
2112 if ( nLen )
2113 aCorrectedFormula = aCorrectedFormula.copy( 0, nLen - 1 );
2114 aCorrectedSymbol.clear();
2115 bCorrected = true;
2122 void FormulaCompiler::RangeLine()
2124 Factor();
2125 while (mpToken->GetOpCode() == ocRange)
2127 FormulaToken** pCode1 = pCode - 1;
2128 FormulaTokenRef p = mpToken;
2129 NextToken();
2130 Factor();
2131 FormulaToken** pCode2 = pCode - 1;
2132 if (!MergeRangeReference( pCode1, pCode2))
2133 PutCode(p);
2137 void FormulaCompiler::IntersectionLine()
2139 RangeLine();
2140 while (mpToken->GetOpCode() == ocIntersect || mpToken->GetOpCode() == ocSpaces)
2142 sal_uInt16 nCodeIndex = maArrIterator.GetIndex() - 1;
2143 FormulaToken** pCode1 = pCode - 1;
2144 FormulaTokenRef p = mpToken;
2145 NextToken();
2146 RangeLine();
2147 FormulaToken** pCode2 = pCode - 1;
2148 if (p->GetOpCode() == ocSpaces)
2150 // Convert to intersection if both left and right are references or
2151 // functions (potentially returning references, if not then a space
2152 // or no space would be a syntax error anyway), not other operators
2153 // or operands. Else discard.
2154 if (isAdjacentOrGapRpnEnd( pc, pCode, pCode1, pCode2) && isIntersectable( pCode1, pCode2))
2156 FormulaTokenRef pIntersect( new FormulaByteToken( ocIntersect));
2157 // Replace ocSpaces with ocIntersect so that when switching
2158 // formula syntax the correct operator string is created.
2159 // coverity[freed_arg : FALSE] - FormulaTokenRef has a ref so ReplaceToken won't delete pIntersect
2160 pArr->ReplaceToken( nCodeIndex, pIntersect.get(), FormulaTokenArray::ReplaceMode::CODE_ONLY);
2161 PutCode( pIntersect);
2164 else
2166 PutCode(p);
2171 void FormulaCompiler::UnionLine()
2173 IntersectionLine();
2174 while (mpToken->GetOpCode() == ocUnion)
2176 FormulaTokenRef p = mpToken;
2177 NextToken();
2178 IntersectionLine();
2179 PutCode(p);
2183 void FormulaCompiler::UnaryLine()
2185 if( mpToken->GetOpCode() == ocAdd )
2186 GetToken();
2187 else if (SC_OPCODE_START_UN_OP <= mpToken->GetOpCode() &&
2188 mpToken->GetOpCode() < SC_OPCODE_STOP_UN_OP)
2190 FormulaTokenRef p = mpToken;
2191 NextToken();
2192 UnaryLine();
2193 if (mbComputeII)
2195 FormulaToken** pArg = pCode - 1;
2196 HandleIIOpCode(p.get(), &pArg, 1);
2198 PutCode( p );
2200 else
2201 UnionLine();
2204 void FormulaCompiler::PostOpLine()
2206 UnaryLine();
2207 while ( mpToken->GetOpCode() == ocPercentSign )
2208 { // this operator _follows_ its operand
2209 if (mbComputeII)
2211 FormulaToken** pArg = pCode - 1;
2212 HandleIIOpCode(mpToken.get(), &pArg, 1);
2214 PutCode( mpToken );
2215 NextToken();
2219 void FormulaCompiler::PowLine()
2221 PostOpLine();
2222 while (mpToken->GetOpCode() == ocPow)
2224 FormulaTokenRef p = mpToken;
2225 FormulaToken** pArgArray[2];
2226 if (mbComputeII)
2227 pArgArray[0] = pCode - 1; // Add first argument
2228 NextToken();
2229 PostOpLine();
2230 if (mbComputeII)
2232 pArgArray[1] = pCode - 1; // Add second argument
2233 HandleIIOpCode(p.get(), pArgArray, 2);
2235 PutCode(p);
2239 void FormulaCompiler::MulDivLine()
2241 PowLine();
2242 while (mpToken->GetOpCode() == ocMul || mpToken->GetOpCode() == ocDiv)
2244 FormulaTokenRef p = mpToken;
2245 FormulaToken** pArgArray[2];
2246 if (mbComputeII)
2247 pArgArray[0] = pCode - 1; // Add first argument
2248 NextToken();
2249 PowLine();
2250 if (mbComputeII)
2252 pArgArray[1] = pCode - 1; // Add second argument
2253 HandleIIOpCode(p.get(), pArgArray, 2);
2255 PutCode(p);
2259 void FormulaCompiler::AddSubLine()
2261 MulDivLine();
2262 while (mpToken->GetOpCode() == ocAdd || mpToken->GetOpCode() == ocSub)
2264 FormulaTokenRef p = mpToken;
2265 FormulaToken** pArgArray[2];
2266 if (mbComputeII)
2267 pArgArray[0] = pCode - 1; // Add first argument
2268 NextToken();
2269 MulDivLine();
2270 if (mbComputeII)
2272 pArgArray[1] = pCode - 1; // Add second argument
2273 HandleIIOpCode(p.get(), pArgArray, 2);
2275 PutCode(p);
2279 void FormulaCompiler::ConcatLine()
2281 AddSubLine();
2282 while (mpToken->GetOpCode() == ocAmpersand)
2284 FormulaTokenRef p = mpToken;
2285 FormulaToken** pArgArray[2];
2286 if (mbComputeII)
2287 pArgArray[0] = pCode - 1; // Add first argument
2288 NextToken();
2289 AddSubLine();
2290 if (mbComputeII)
2292 pArgArray[1] = pCode - 1; // Add second argument
2293 HandleIIOpCode(p.get(), pArgArray, 2);
2295 PutCode(p);
2299 void FormulaCompiler::CompareLine()
2301 ConcatLine();
2302 while (mpToken->GetOpCode() >= ocEqual && mpToken->GetOpCode() <= ocGreaterEqual)
2304 FormulaTokenRef p = mpToken;
2305 FormulaToken** pArgArray[2];
2306 if (mbComputeII)
2307 pArgArray[0] = pCode - 1; // Add first argument
2308 NextToken();
2309 ConcatLine();
2310 if (mbComputeII)
2312 pArgArray[1] = pCode - 1; // Add second argument
2313 HandleIIOpCode(p.get(), pArgArray, 2);
2315 PutCode(p);
2319 OpCode FormulaCompiler::Expression()
2321 FormulaCompilerRecursionGuard aRecursionGuard( nRecursion );
2322 if ( nRecursion > nRecursionMax )
2324 SetError( FormulaError::StackOverflow );
2325 return ocStop; //! generate token instead?
2327 CompareLine();
2328 while (mpToken->GetOpCode() == ocAnd || mpToken->GetOpCode() == ocOr)
2330 FormulaTokenRef p = mpToken;
2331 mpToken->SetByte( 2 ); // 2 parameters!
2332 FormulaToken** pArgArray[2];
2333 if (mbComputeII)
2334 pArgArray[0] = pCode - 1; // Add first argument
2335 NextToken();
2336 CompareLine();
2337 if (mbComputeII)
2339 pArgArray[1] = pCode - 1; // Add second argument
2340 HandleIIOpCode(p.get(), pArgArray, 2);
2342 PutCode(p);
2344 return mpToken->GetOpCode();
2348 void FormulaCompiler::SetError( FormulaError /*nError*/ )
2352 FormulaTokenRef FormulaCompiler::ExtendRangeReference( FormulaToken & /*rTok1*/, FormulaToken & /*rTok2*/ )
2354 return FormulaTokenRef();
2357 bool FormulaCompiler::MergeRangeReference( FormulaToken * * const pCode1, FormulaToken * const * const pCode2 )
2359 if (!isAdjacentRpnEnd( pc, pCode, pCode1, pCode2))
2360 return false;
2362 FormulaToken *p1 = *pCode1, *p2 = *pCode2;
2363 FormulaTokenRef p = ExtendRangeReference( *p1, *p2);
2364 if (!p)
2365 return false;
2367 p->IncRef();
2368 p1->DecRef();
2369 p2->DecRef();
2370 *pCode1 = p.get();
2371 --pCode;
2372 --pc;
2374 return true;
2377 bool FormulaCompiler::CompileTokenArray()
2379 glSubTotal = false;
2380 bCorrected = false;
2381 needsRPNTokenCheck = false;
2382 if (pArr->GetCodeError() == FormulaError::NONE || !mbStopOnError)
2384 if ( bAutoCorrect )
2386 aCorrectedFormula.clear();
2387 aCorrectedSymbol.clear();
2389 pArr->DelRPN();
2390 maArrIterator.Reset();
2391 pStack = nullptr;
2392 FormulaToken* pDataArray[ FORMULA_MAXTOKENS + 1 ];
2393 // Code in some places refers to the last token as 'pCode - 1', which may
2394 // point before the first element if the expression is bad. So insert a dummy
2395 // node in that place which will make that token be nullptr.
2396 pDataArray[ 0 ] = nullptr;
2397 FormulaToken** pData = pDataArray + 1;
2398 pCode = pData;
2399 bool bWasForced = pArr->IsRecalcModeForced();
2400 if ( bWasForced && bAutoCorrect )
2401 aCorrectedFormula = "=";
2402 pArr->ClearRecalcMode();
2403 maArrIterator.Reset();
2404 eLastOp = ocOpen;
2405 pc = 0;
2406 NextToken();
2407 OpCode eOp = Expression();
2408 // Some trailing garbage that doesn't form an expression?
2409 if (eOp != ocStop)
2410 SetError( FormulaError::OperatorExpected);
2411 PostProcessCode();
2413 FormulaError nErrorBeforePop = pArr->GetCodeError();
2415 while( pStack )
2416 PopTokenArray();
2417 if( pc )
2419 pArr->CreateNewRPNArrayFromData( pData, pc );
2420 if( needsRPNTokenCheck )
2421 pArr->CheckAllRPNTokens();
2424 // once an error, always an error
2425 if( pArr->GetCodeError() == FormulaError::NONE && nErrorBeforePop != FormulaError::NONE )
2426 pArr->SetCodeError( nErrorBeforePop);
2428 if (pArr->GetCodeError() != FormulaError::NONE && mbStopOnError)
2430 pArr->DelRPN();
2431 maArrIterator.Reset();
2432 pArr->SetHyperLink( false);
2435 if ( bWasForced )
2436 pArr->SetRecalcModeForced();
2438 if( nNumFmt == SvNumFormatType::UNDEFINED )
2439 nNumFmt = SvNumFormatType::NUMBER;
2440 return glSubTotal;
2443 void FormulaCompiler::PopTokenArray()
2445 if( !pStack )
2446 return;
2448 FormulaArrayStack* p = pStack;
2449 pStack = p->pNext;
2450 // obtain special RecalcMode from SharedFormula
2451 if ( pArr->IsRecalcModeAlways() )
2452 p->pArr->SetExclusiveRecalcModeAlways();
2453 else if ( !pArr->IsRecalcModeNormal() && p->pArr->IsRecalcModeNormal() )
2454 p->pArr->SetMaskedRecalcMode( pArr->GetRecalcMode() );
2455 p->pArr->SetCombinedBitsRecalcMode( pArr->GetRecalcMode() );
2456 if ( pArr->IsHyperLink() ) // fdo 87534
2457 p->pArr->SetHyperLink( true );
2458 if( p->bTemp )
2459 delete pArr;
2460 pArr = p->pArr;
2461 maArrIterator = FormulaTokenArrayPlainIterator(*pArr);
2462 maArrIterator.Jump(p->nIndex);
2463 mpLastToken = p->mpLastToken;
2464 delete p;
2467 void FormulaCompiler::CreateStringFromTokenArray( OUString& rFormula )
2469 OUStringBuffer aBuffer( pArr->GetLen() * 5 );
2470 CreateStringFromTokenArray( aBuffer );
2471 rFormula = aBuffer.makeStringAndClear();
2474 void FormulaCompiler::CreateStringFromTokenArray( OUStringBuffer& rBuffer )
2476 rBuffer.setLength(0);
2477 if( !pArr->GetLen() )
2478 return;
2480 FormulaTokenArray* pSaveArr = pArr;
2481 int nSaveIndex = maArrIterator.GetIndex();
2482 bool bODFF = FormulaGrammar::isODFF( meGrammar);
2483 if (bODFF || FormulaGrammar::isPODF( meGrammar) )
2485 // Scan token array for missing args and re-write if present.
2486 MissingConventionODF aConv( bODFF);
2487 if (pArr->NeedsPodfRewrite( aConv))
2489 pArr = pArr->RewriteMissing( aConv );
2490 maArrIterator = FormulaTokenArrayPlainIterator( *pArr );
2493 else if ( FormulaGrammar::isOOXML( meGrammar ) )
2495 // Scan token array for missing args and rewrite if present.
2496 if (pArr->NeedsOoxmlRewrite())
2498 MissingConventionOOXML aConv;
2499 pArr = pArr->RewriteMissing( aConv );
2500 maArrIterator = FormulaTokenArrayPlainIterator( *pArr );
2504 // At least one character per token, plus some are references, some are
2505 // function names, some are numbers, ...
2506 rBuffer.ensureCapacity( pArr->GetLen() * 5 );
2508 if ( pArr->IsRecalcModeForced() )
2509 rBuffer.append( '=');
2510 const FormulaToken* t = maArrIterator.First();
2511 while( t )
2512 t = CreateStringFromToken( rBuffer, t, true );
2514 if (pSaveArr != pArr)
2516 delete pArr;
2517 pArr = pSaveArr;
2518 maArrIterator = FormulaTokenArrayPlainIterator( *pArr );
2519 maArrIterator.Jump(nSaveIndex);
2523 const FormulaToken* FormulaCompiler::CreateStringFromToken( OUString& rFormula, const FormulaToken* pTokenP )
2525 OUStringBuffer aBuffer;
2526 const FormulaToken* p = CreateStringFromToken( aBuffer, pTokenP );
2527 rFormula += aBuffer;
2528 return p;
2531 const FormulaToken* FormulaCompiler::CreateStringFromToken( OUStringBuffer& rBuffer, const FormulaToken* pTokenP,
2532 bool bAllowArrAdvance )
2534 bool bNext = true;
2535 bool bSpaces = false;
2536 const FormulaToken* t = pTokenP;
2537 OpCode eOp = t->GetOpCode();
2538 if( eOp >= ocAnd && eOp <= ocOr )
2540 // AND, OR infix?
2541 if ( bAllowArrAdvance )
2542 t = maArrIterator.Next();
2543 else
2544 t = maArrIterator.PeekNext();
2545 bNext = false;
2546 bSpaces = ( !t || t->GetOpCode() != ocOpen );
2548 if( bSpaces )
2549 rBuffer.append( ' ');
2551 if (eOp == ocSpaces || eOp == ocWhitespace)
2553 bool bWriteSpaces = true;
2554 if (eOp == ocSpaces && mxSymbols->isODFF())
2556 const FormulaToken* p = maArrIterator.PeekPrevNoSpaces();
2557 bool bIntersectionOp = (p && p->GetOpCode() == ocColRowName);
2558 if (bIntersectionOp)
2560 p = maArrIterator.PeekNextNoSpaces();
2561 bIntersectionOp = (p && p->GetOpCode() == ocColRowName);
2563 if (bIntersectionOp)
2565 rBuffer.append( "!!");
2566 bWriteSpaces = false;
2569 if (bWriteSpaces)
2571 // ODF v1.3 OpenFormula 5.14 Whitespace states "whitespace shall
2572 // not separate a function name from its initial opening
2573 // parenthesis".
2575 // ECMA-376-1:2016 18.17.2 Syntax states "that no space characters
2576 // shall separate a function-name from the left parenthesis (()
2577 // that follows it." and Excel even chokes on it.
2579 // Suppress/remove it in any case also in UI, it will not be
2580 // preserved.
2581 const FormulaToken* p = maArrIterator.PeekPrevNoSpaces();
2582 if (p && p->IsFunction())
2584 p = maArrIterator.PeekNextNoSpaces();
2585 if (p && p->GetOpCode() == ocOpen)
2586 bWriteSpaces = false;
2589 if (bWriteSpaces)
2591 // most times it's just one blank
2592 sal_uInt8 n = t->GetByte();
2593 for ( sal_uInt8 j=0; j<n; ++j )
2595 if (eOp == ocWhitespace)
2596 rBuffer.append( t->GetChar());
2597 else
2598 rBuffer.append( ' ');
2602 else if( eOp >= ocInternalBegin && eOp <= ocInternalEnd )
2603 rBuffer.appendAscii( pInternal[ eOp - ocInternalBegin ] );
2604 else if (eOp == ocIntersect)
2606 // Nasty, ugly, horrific, terrifying...
2607 if (FormulaGrammar::isExcelSyntax( meGrammar))
2608 rBuffer.append(' ');
2609 else
2610 rBuffer.append( mxSymbols->getSymbol( eOp));
2612 else if ( eOp == ocEasterSunday)
2614 // EASTERSUNDAY belongs to ODFF since ODF 1.4
2615 if (m_oODFSavingVersion.has_value()
2616 && m_oODFSavingVersion.value() >= SvtSaveOptions::ODFSVER_012
2617 && m_oODFSavingVersion.value() < SvtSaveOptions::ODFSVER_014)
2618 rBuffer.append(u"ORG.OPENOFFICE." + mxSymbols->getSymbol(eOp));
2619 else
2620 rBuffer.append(mxSymbols->getSymbol(eOp));
2622 else if( static_cast<sal_uInt16>(eOp) < mxSymbols->getSymbolCount()) // Keyword:
2623 rBuffer.append( mxSymbols->getSymbol( eOp));
2624 else
2626 SAL_WARN( "formula.core","unknown OpCode");
2627 rBuffer.append( GetNativeSymbol( ocErrName ));
2629 if( bNext )
2631 if (t->IsExternalRef())
2633 CreateStringFromExternal( rBuffer, pTokenP);
2635 else
2637 switch( t->GetType() )
2639 case svDouble:
2640 AppendDouble( rBuffer, t->GetDouble() );
2641 break;
2643 case svString:
2644 if( eOp == ocBad || eOp == ocStringXML || eOp == ocStringName )
2645 rBuffer.append( t->GetString().getString());
2646 else
2647 AppendString( rBuffer, t->GetString().getString() );
2648 break;
2649 case svSingleRef:
2650 CreateStringFromSingleRef( rBuffer, t);
2651 break;
2652 case svDoubleRef:
2653 CreateStringFromDoubleRef( rBuffer, t);
2654 break;
2655 case svMatrix:
2656 case svMatrixCell:
2657 CreateStringFromMatrix( rBuffer, t );
2658 break;
2660 case svIndex:
2661 CreateStringFromIndex( rBuffer, t );
2662 if (t->GetOpCode() == ocTableRef && bAllowArrAdvance && NeedsTableRefTransformation())
2664 // Suppress all TableRef related tokens, the resulting
2665 // range was written by CreateStringFromIndex().
2666 const FormulaToken* const p = maArrIterator.PeekNext();
2667 if (p && p->GetOpCode() == ocTableRefOpen)
2669 int nLevel = 0;
2672 t = maArrIterator.Next();
2673 if (!t)
2674 break;
2676 // Switch cases correspond with those in
2677 // ScCompiler::HandleTableRef()
2678 switch (t->GetOpCode())
2680 case ocTableRefOpen:
2681 ++nLevel;
2682 break;
2683 case ocTableRefClose:
2684 --nLevel;
2685 break;
2686 case ocTableRefItemAll:
2687 case ocTableRefItemHeaders:
2688 case ocTableRefItemData:
2689 case ocTableRefItemTotals:
2690 case ocTableRefItemThisRow:
2691 case ocSep:
2692 case ocPush:
2693 case ocRange:
2694 case ocSpaces:
2695 case ocWhitespace:
2696 break;
2697 default:
2698 nLevel = 0;
2699 bNext = false;
2701 } while (nLevel);
2704 break;
2705 case svExternal:
2707 // mapped or translated name of AddIns
2708 OUString aAddIn( t->GetExternal() );
2709 bool bMapped = mxSymbols->isPODF(); // ODF 1.1 directly uses programmatical name
2710 if (!bMapped && mxSymbols->hasExternals())
2712 if (mxSymbols->isOOXML())
2714 // Write compatibility name, if any.
2715 if (GetExcelName( aAddIn))
2716 bMapped = true;
2718 if (!bMapped)
2720 ExternalHashMap::const_iterator iLook = mxSymbols->getReverseExternalHashMap().find( aAddIn);
2721 if (iLook != mxSymbols->getReverseExternalHashMap().end())
2723 aAddIn = (*iLook).second;
2724 bMapped = true;
2728 if (!bMapped && !mxSymbols->isEnglish())
2729 LocalizeString( aAddIn );
2730 rBuffer.append( aAddIn);
2732 break;
2733 case svError:
2734 AppendErrorConstant( rBuffer, t->GetError());
2735 break;
2736 case svByte:
2737 case svJump:
2738 case svFAP:
2739 case svMissing:
2740 case svSep:
2741 break; // Opcodes
2742 default:
2743 SAL_WARN("formula.core", "FormulaCompiler::GetStringFromToken: unknown token type " << t->GetType());
2744 } // of switch
2747 if( bSpaces )
2748 rBuffer.append( ' ');
2749 if ( bAllowArrAdvance )
2751 if( bNext )
2752 t = maArrIterator.Next();
2753 return t;
2755 return pTokenP;
2759 void FormulaCompiler::AppendDouble( OUStringBuffer& rBuffer, double fVal ) const
2761 if ( mxSymbols->isEnglishLocale() )
2763 ::rtl::math::doubleToUStringBuffer( rBuffer, fVal,
2764 rtl_math_StringFormat_Automatic,
2765 rtl_math_DecimalPlaces_Max, '.', true );
2767 else
2769 SvtSysLocale aSysLocale;
2770 ::rtl::math::doubleToUStringBuffer( rBuffer, fVal,
2771 rtl_math_StringFormat_Automatic,
2772 rtl_math_DecimalPlaces_Max,
2773 aSysLocale.GetLocaleData().getNumDecimalSep()[0],
2774 true );
2778 void FormulaCompiler::AppendBoolean( OUStringBuffer& rBuffer, bool bVal ) const
2780 rBuffer.append( mxSymbols->getSymbol( bVal ? ocTrue : ocFalse ) );
2783 void FormulaCompiler::AppendString( OUStringBuffer& rBuffer, const OUString & rStr )
2785 rBuffer.append( '"');
2786 if ( lcl_UnicodeStrChr( rStr.getStr(), '"' ) == nullptr )
2787 rBuffer.append( rStr );
2788 else
2790 OUString aStr = rStr.replaceAll( "\"", "\"\"" );
2791 rBuffer.append(aStr);
2793 rBuffer.append( '"');
2796 bool FormulaCompiler::NeedsTableRefTransformation() const
2798 // Currently only UI representations and OOXML export use Table structured
2799 // references. Not defined in ODFF.
2800 // Unnecessary to explicitly check for ODFF grammar as the ocTableRefOpen
2801 // symbol is not defined there.
2802 return mxSymbols->getSymbol( ocTableRefOpen).isEmpty() || FormulaGrammar::isPODF( meGrammar);
2805 void FormulaCompiler::UpdateSeparatorsNative(
2806 const OUString& rSep, const OUString& rArrayColSep, const OUString& rArrayRowSep )
2808 NonConstOpCodeMapPtr xSymbolsNative;
2809 lcl_fillNativeSymbols( xSymbolsNative);
2810 xSymbolsNative->putOpCode( rSep, ocSep, nullptr);
2811 xSymbolsNative->putOpCode( rArrayColSep, ocArrayColSep, nullptr);
2812 xSymbolsNative->putOpCode( rArrayRowSep, ocArrayRowSep, nullptr);
2815 void FormulaCompiler::ResetNativeSymbols()
2817 NonConstOpCodeMapPtr xSymbolsNative;
2818 lcl_fillNativeSymbols( xSymbolsNative, InitSymbols::DESTROY);
2819 lcl_fillNativeSymbols( xSymbolsNative);
2822 void FormulaCompiler::SetNativeSymbols( const OpCodeMapPtr& xMap )
2824 NonConstOpCodeMapPtr xSymbolsNative;
2825 lcl_fillNativeSymbols( xSymbolsNative);
2826 xSymbolsNative->copyFrom( *xMap );
2830 OpCode FormulaCompiler::NextToken()
2832 if( !GetToken() )
2833 return ocStop;
2834 OpCode eOp = mpToken->GetOpCode();
2835 // There must be an operator before a push
2836 if ( (eOp == ocPush || eOp == ocColRowNameAuto) &&
2837 !( (eLastOp == ocOpen) || (eLastOp == ocSep) ||
2838 (SC_OPCODE_START_BIN_OP <= eLastOp && eLastOp < SC_OPCODE_STOP_UN_OP)) )
2839 SetError( FormulaError::OperatorExpected);
2840 // Operator and Plus => operator
2841 if (eOp == ocAdd && (eLastOp == ocOpen || eLastOp == ocSep ||
2842 (SC_OPCODE_START_BIN_OP <= eLastOp && eLastOp < SC_OPCODE_STOP_UN_OP)))
2844 FormulaCompilerRecursionGuard aRecursionGuard( nRecursion );
2845 eOp = NextToken();
2847 else
2849 // Before an operator there must not be another operator, with the
2850 // exception of AND and OR.
2851 if ( eOp != ocAnd && eOp != ocOr &&
2852 (SC_OPCODE_START_BIN_OP <= eOp && eOp < SC_OPCODE_STOP_BIN_OP )
2853 && (eLastOp == ocOpen || eLastOp == ocSep ||
2854 (SC_OPCODE_START_BIN_OP <= eLastOp && eLastOp < SC_OPCODE_STOP_UN_OP)))
2856 SetError( FormulaError::VariableExpected);
2857 if ( bAutoCorrect && !pStack )
2859 if ( eOp == eLastOp || eLastOp == ocOpen )
2860 { // throw away duplicated operator
2861 aCorrectedSymbol.clear();
2862 bCorrected = true;
2864 else
2866 sal_Int32 nPos = aCorrectedFormula.getLength();
2867 if ( nPos )
2869 nPos--;
2870 sal_Unicode c = aCorrectedFormula[ nPos ];
2871 switch ( eOp )
2872 { // swap operators
2873 case ocGreater:
2874 if ( c == mxSymbols->getSymbolChar( ocEqual) )
2875 { // >= instead of =>
2876 aCorrectedFormula = aCorrectedFormula.replaceAt( nPos, 1,
2877 rtl::OUStringChar( mxSymbols->getSymbolChar(ocGreater) ) );
2878 aCorrectedSymbol = OUString(c);
2879 bCorrected = true;
2881 break;
2882 case ocLess:
2883 if ( c == mxSymbols->getSymbolChar( ocEqual) )
2884 { // <= instead of =<
2885 aCorrectedFormula = aCorrectedFormula.replaceAt( nPos, 1,
2886 rtl::OUStringChar( mxSymbols->getSymbolChar(ocLess) ) );
2887 aCorrectedSymbol = OUString(c);
2888 bCorrected = true;
2890 else if ( c == mxSymbols->getSymbolChar( ocGreater) )
2891 { // <> instead of ><
2892 aCorrectedFormula = aCorrectedFormula.replaceAt( nPos, 1,
2893 rtl::OUStringChar( mxSymbols->getSymbolChar(ocLess) ) );
2894 aCorrectedSymbol = OUString(c);
2895 bCorrected = true;
2897 break;
2898 case ocMul:
2899 if ( c == mxSymbols->getSymbolChar( ocSub) )
2900 { // *- instead of -*
2901 aCorrectedFormula = aCorrectedFormula.replaceAt( nPos, 1,
2902 rtl::OUStringChar( mxSymbols->getSymbolChar(ocMul) ) );
2903 aCorrectedSymbol = OUString(c);
2904 bCorrected = true;
2906 break;
2907 case ocDiv:
2908 if ( c == mxSymbols->getSymbolChar( ocSub) )
2909 { // /- instead of -/
2910 aCorrectedFormula = aCorrectedFormula.replaceAt( nPos, 1,
2911 rtl::OUStringChar( mxSymbols->getSymbolChar(ocDiv) ) );
2912 aCorrectedSymbol = OUString(c);
2913 bCorrected = true;
2915 break;
2916 default:
2917 ; // nothing
2923 // Nasty, ugly, horrific, terrifying... significant whitespace...
2924 if (eOp == ocSpaces && FormulaGrammar::isExcelSyntax( meGrammar))
2926 // Fake an intersection op as last op for the next round, but at
2927 // least roughly check if it could make sense at all.
2928 FormulaToken* pPrev = maArrIterator.PeekPrevNoSpaces();
2929 if (pPrev && isPotentialRangeType( pPrev, false, false))
2931 FormulaToken* pNext = maArrIterator.PeekNextNoSpaces();
2932 if (pNext && isPotentialRangeType( pNext, false, true))
2933 eLastOp = ocIntersect;
2934 else
2935 eLastOp = eOp;
2937 else
2938 eLastOp = eOp;
2940 else
2941 eLastOp = eOp;
2943 return eOp;
2946 void FormulaCompiler::PutCode( FormulaTokenRef& p )
2948 if( pc >= FORMULA_MAXTOKENS - 1 )
2950 if ( pc == FORMULA_MAXTOKENS - 1 )
2952 SAL_WARN("formula.core", "FormulaCompiler::PutCode - CodeOverflow with OpCode " << +p->GetOpCode());
2953 p = new FormulaByteToken( ocStop );
2954 p->IncRef();
2955 *pCode++ = p.get();
2956 ++pc;
2958 SetError( FormulaError::CodeOverflow);
2959 return;
2961 if (pArr->GetCodeError() != FormulaError::NONE && mbJumpCommandReorder)
2962 return;
2963 ForceArrayOperator( p);
2964 p->IncRef();
2965 *pCode++ = p.get();
2966 pc++;
2970 bool FormulaCompiler::HandleExternalReference( const FormulaToken& /*_aToken*/)
2972 return true;
2975 bool FormulaCompiler::HandleStringName()
2977 return true;
2980 bool FormulaCompiler::HandleRange()
2982 return true;
2985 bool FormulaCompiler::HandleColRowName()
2987 return true;
2990 bool FormulaCompiler::HandleDbData()
2992 return true;
2995 bool FormulaCompiler::HandleTableRef()
2997 return true;
3000 void FormulaCompiler::CreateStringFromSingleRef( OUStringBuffer& /*rBuffer*/, const FormulaToken* /*pToken*/) const
3004 void FormulaCompiler::CreateStringFromDoubleRef( OUStringBuffer& /*rBuffer*/, const FormulaToken* /*pToken*/) const
3008 void FormulaCompiler::CreateStringFromIndex( OUStringBuffer& /*rBuffer*/, const FormulaToken* /*pToken*/) const
3012 void FormulaCompiler::CreateStringFromMatrix( OUStringBuffer& /*rBuffer*/, const FormulaToken* /*pToken*/) const
3016 void FormulaCompiler::CreateStringFromExternal( OUStringBuffer& /*rBuffer*/, const FormulaToken* /*pToken*/) const
3020 void FormulaCompiler::LocalizeString( OUString& /*rName*/ ) const
3024 bool FormulaCompiler::GetExcelName( OUString& /*rName*/ ) const
3026 return false;
3029 formula::ParamClass FormulaCompiler::GetForceArrayParameter( const FormulaToken* /*pToken*/, sal_uInt16 /*nParam*/ ) const
3031 return ParamClass::Unknown;
3034 void FormulaCompiler::ForceArrayOperator( FormulaTokenRef const & rCurr )
3036 if (pCurrentFactorToken.get() == rCurr.get())
3037 return;
3039 const OpCode eOp = rCurr->GetOpCode();
3040 const StackVar eType = rCurr->GetType();
3041 const bool bInlineArray = (eOp == ocPush && eType == svMatrix);
3043 if (!bInlineArray)
3045 if (rCurr->GetInForceArray() != ParamClass::Unknown)
3046 // Already set, unnecessary to evaluate again. This happens by calls to
3047 // CurrentFactor::operator=() while descending through Factor() and
3048 // then ascending back (and down and up, ...),
3049 // CheckSetForceArrayParameter() and later PutCode().
3050 return;
3052 if (!(eOp != ocPush && (eType == svByte || eType == svJump)))
3053 return;
3056 // Return class for inline arrays and functions returning array/matrix.
3057 // It's somewhat unclear what Excel actually does there and in
3058 // ECMA-376-1:2016 OOXML mentions "call to ... shall be an array formula"
3059 // only for FREQUENCY() and TRANSPOSE() but not for any other function
3060 // returning array/matrix or inline arrays, though for the latter has one
3061 // example in 18.17.2 Syntax:
3062 // "SUM(SQRT({1,2,3,4})) returns 6.14 when entered normally". However,
3063 // these need to be treated similar but not as ParamClass::ForceArray
3064 // (which would contradict the example in
3065 // https://bugs.documentfoundation.org/show_bug.cgi?id=122301#c19 and A6 of
3066 // https://bugs.documentfoundation.org/show_bug.cgi?id=133260#c10 ).
3067 // See also
3068 // commit d0ded163d8e93dc5b10d7a7c9bdab1d0a6a50bac
3069 // commit 5413c8871dec08eff19f514f5f391b946a45c86c
3070 constexpr ParamClass eArrayReturn = ParamClass::ForceArrayReturn;
3072 if (bInlineArray)
3074 // rCurr->SetInForceArray() can not be used with ocPush, but ocPush
3075 // with svMatrix has an implicit ParamClass::ForceArrayReturn.
3076 if (nCurrentFactorParam > 0 && pCurrentFactorToken
3077 && pCurrentFactorToken->GetInForceArray() == ParamClass::Unknown
3078 && GetForceArrayParameter( pCurrentFactorToken.get(), static_cast<sal_uInt16>(nCurrentFactorParam - 1))
3079 == ParamClass::Value)
3081 // Propagate to caller as if a function returning an array/matrix
3082 // was called (see also below).
3083 pCurrentFactorToken->SetInForceArray( eArrayReturn);
3085 return;
3088 if (!pCurrentFactorToken)
3090 if (mbMatrixFlag)
3092 // An array/matrix formula acts as ForceArray on all top level
3093 // operators and function calls, so that can be inherited properly
3094 // below.
3095 rCurr->SetInForceArray( ParamClass::ForceArray);
3097 else if (pc >= 2 && SC_OPCODE_START_BIN_OP <= eOp && eOp < SC_OPCODE_STOP_BIN_OP)
3099 // Binary operators are not functions followed by arguments
3100 // and need some peeking into RPN to inspect their operands.
3101 // Note that array context is not forced if only one
3102 // of the operands is an array like "={1;2}+A1:A2" returns #VALUE!
3103 // if entered in column A and not input in array mode, because it
3104 // involves a range reference with an implicit intersection. Check
3105 // both arguments are arrays, or the other is ocPush without ranges
3106 // for "={1;2}+3" or "={1;2}+A1".
3107 // Note this does not catch "={1;2}+ABS(A1)" that could be forced
3108 // to array, user still has to close in array mode.
3109 // The IsMatrixFunction() is only necessary because not all
3110 // functions returning matrix have ForceArrayReturn (yet?), see
3111 // OOXML comment above.
3113 const OpCode eOp1 = pCode[-1]->GetOpCode();
3114 const OpCode eOp2 = pCode[-2]->GetOpCode();
3115 const bool b1 = (pCode[-1]->GetInForceArray() != ParamClass::Unknown || IsMatrixFunction(eOp1));
3116 const bool b2 = (pCode[-2]->GetInForceArray() != ParamClass::Unknown || IsMatrixFunction(eOp2));
3117 if ((b1 && b2)
3118 || (b1 && eOp2 == ocPush && pCode[-2]->GetType() != svDoubleRef)
3119 || (b2 && eOp1 == ocPush && pCode[-1]->GetType() != svDoubleRef))
3121 rCurr->SetInForceArray( eArrayReturn);
3124 else if (pc >= 1 && SC_OPCODE_START_UN_OP <= eOp && eOp < SC_OPCODE_STOP_UN_OP)
3126 // Similar for unary operators.
3127 if (pCode[-1]->GetInForceArray() != ParamClass::Unknown || IsMatrixFunction(pCode[-1]->GetOpCode()))
3129 rCurr->SetInForceArray( eArrayReturn);
3132 return;
3135 // Inherited parameter class.
3136 const formula::ParamClass eForceType = pCurrentFactorToken->GetInForceArray();
3137 if (eForceType == ParamClass::ForceArray || eForceType == ParamClass::ReferenceOrRefArray)
3139 // ReferenceOrRefArray was set only if in ForceArray context already,
3140 // it is valid for the one function only to indicate the preferred
3141 // return type. Propagate as ForceArray if not another parameter
3142 // handling ReferenceOrRefArray.
3143 if (nCurrentFactorParam > 0
3144 && (GetForceArrayParameter( pCurrentFactorToken.get(), static_cast<sal_uInt16>(nCurrentFactorParam - 1))
3145 == ParamClass::ReferenceOrRefArray))
3146 rCurr->SetInForceArray( ParamClass::ReferenceOrRefArray);
3147 else
3148 rCurr->SetInForceArray( ParamClass::ForceArray);
3149 return;
3151 else if (eForceType == ParamClass::ReferenceOrForceArray)
3153 // Inherit further only if the return class of the nested function is
3154 // not Reference. Else flag as suppressed.
3155 if (GetForceArrayParameter( rCurr.get(), SAL_MAX_UINT16) != ParamClass::Reference)
3156 rCurr->SetInForceArray( eForceType);
3157 else
3158 rCurr->SetInForceArray( ParamClass::SuppressedReferenceOrForceArray);
3159 return;
3162 if (nCurrentFactorParam <= 0)
3163 return;
3165 // Actual current parameter's class.
3166 const formula::ParamClass eParamType = GetForceArrayParameter(
3167 pCurrentFactorToken.get(), static_cast<sal_uInt16>(nCurrentFactorParam - 1));
3168 if (eParamType == ParamClass::ForceArray)
3169 rCurr->SetInForceArray( eParamType);
3170 else if (eParamType == ParamClass::ReferenceOrForceArray)
3172 if (GetForceArrayParameter( rCurr.get(), SAL_MAX_UINT16) != ParamClass::Reference)
3173 rCurr->SetInForceArray( eParamType);
3174 else
3175 rCurr->SetInForceArray( formula::ParamClass::SuppressedReferenceOrForceArray);
3178 // Propagate a ForceArrayReturn to caller if the called function
3179 // returns one and the caller so far does not have a stronger array
3180 // mode set and expects a scalar value for this parameter.
3181 if (eParamType == ParamClass::Value && pCurrentFactorToken->GetInForceArray() == ParamClass::Unknown)
3183 if (IsMatrixFunction( eOp))
3184 pCurrentFactorToken->SetInForceArray( eArrayReturn);
3185 else if (GetForceArrayParameter( rCurr.get(), SAL_MAX_UINT16) == ParamClass::ForceArrayReturn)
3186 pCurrentFactorToken->SetInForceArray( ParamClass::ForceArrayReturn);
3190 void FormulaCompiler::CheckSetForceArrayParameter( FormulaTokenRef const & rCurr, sal_uInt8 nParam )
3192 if (!pCurrentFactorToken)
3193 return;
3195 nCurrentFactorParam = nParam + 1;
3197 ForceArrayOperator( rCurr);
3200 void FormulaCompiler::PushTokenArray( FormulaTokenArray* pa, bool bTemp )
3202 if ( bAutoCorrect && !pStack )
3203 { // don't merge stacked subroutine code into entered formula
3204 aCorrectedFormula += aCorrectedSymbol;
3205 aCorrectedSymbol.clear();
3207 FormulaArrayStack* p = new FormulaArrayStack;
3208 p->pNext = pStack;
3209 p->pArr = pArr;
3210 p->nIndex = maArrIterator.GetIndex();
3211 p->mpLastToken = mpLastToken;
3212 p->bTemp = bTemp;
3213 pStack = p;
3214 pArr = pa;
3215 maArrIterator = FormulaTokenArrayPlainIterator( *pArr );
3218 } // namespace formula
3220 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */