Avoid potential negative array index access to cached text.
[LibreOffice.git] / formula / source / core / api / FormulaCompiler.cxx
blob3656c65def1ea983d84ae4b0959b0b9929a2c27d
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 /* TODO: { FormulaMapGroupSpecialOffset::TABLE_REF , ocTableRef } , */
533 { FormulaMapGroupSpecialOffset::MACRO , ocMacro } ,
534 { FormulaMapGroupSpecialOffset::COL_ROW_NAME , ocColRowName } ,
535 { FormulaMapGroupSpecialOffset::WHITESPACE , ocWhitespace }
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_AND,
630 SC_OPCODE_OR
632 lclPushOpCodeMapEntries( aVec, mpTable.get(), aOpCodes, SAL_N_ELEMENTS(aOpCodes) );
633 // functions with 2 or more parameters.
634 for (sal_uInt16 nOp = SC_OPCODE_START_2_PAR; nOp < SC_OPCODE_STOP_2_PAR && nOp < mnSymbols; ++nOp)
636 switch (nOp)
638 // NO_NAME is in SPECIAL.
639 case SC_OPCODE_NO_NAME :
640 break; // nothing,
641 default:
642 lclPushOpCodeMapEntry( aVec, mpTable.get(), nOp );
645 // If AddIn functions are present in this mapping, use them, and only those.
646 if (hasExternals())
648 for (auto const& elem : maExternalHashMap)
650 FormulaOpCodeMapEntry aEntry;
651 aEntry.Name = elem.first;
652 aEntry.Token.Data <<= elem.second;
653 aEntry.Token.OpCode = ocExternal;
654 aVec.push_back( aEntry);
657 else
659 rCompiler.fillAddInToken( aVec, isEnglish());
663 return uno::Sequence< FormulaOpCodeMapEntry >(aVec.data(), aVec.size());
667 void FormulaCompiler::OpCodeMap::putOpCode( const OUString & rStr, const OpCode eOp, const CharClass* pCharClass )
669 if (0 < eOp && sal_uInt16(eOp) < mnSymbols)
671 bool bPutOp = mpTable[eOp].isEmpty();
672 bool bRemoveFromMap = false;
673 if (!bPutOp)
675 switch (eOp)
677 // These OpCodes are meant to overwrite and also remove an
678 // existing mapping.
679 case ocCurrency:
680 bPutOp = true;
681 bRemoveFromMap = true;
682 break;
683 // These separator OpCodes are meant to overwrite and also
684 // remove an existing mapping if it is not used for one of the
685 // other separators.
686 case ocArrayColSep:
687 bPutOp = true;
688 bRemoveFromMap = (mpTable[ocArrayRowSep] != mpTable[eOp] && mpTable[ocSep] != mpTable[eOp]);
689 break;
690 case ocArrayRowSep:
691 bPutOp = true;
692 bRemoveFromMap = (mpTable[ocArrayColSep] != mpTable[eOp] && mpTable[ocSep] != mpTable[eOp]);
693 break;
694 // For ocSep keep the ";" in map but remove any other if it is
695 // not used for ocArrayColSep or ocArrayRowSep.
696 case ocSep:
697 bPutOp = true;
698 bRemoveFromMap = (mpTable[eOp] != ";" &&
699 mpTable[ocArrayColSep] != mpTable[eOp] &&
700 mpTable[ocArrayRowSep] != mpTable[eOp]);
701 break;
702 // These OpCodes are known to be duplicates in the Excel
703 // external API mapping because of different parameter counts
704 // in different BIFF versions. Names are identical and entries
705 // are ignored.
706 case ocLinest:
707 case ocTrend:
708 case ocLogest:
709 case ocGrowth:
710 case ocTrunc:
711 case ocFixed:
712 case ocGetDayOfWeek:
713 case ocHLookup:
714 case ocVLookup:
715 case ocGetDiffDate360:
716 if (rStr == mpTable[eOp])
717 return;
718 [[fallthrough]];
719 // These OpCodes are known to be added to an existing mapping,
720 // but only for the OOXML external API mapping. This is *not*
721 // FormulaLanguage::OOXML. Keep the first
722 // (correct) definition for the OpCode, all following are
723 // additional alias entries in the map.
724 case ocErrorType:
725 case ocMultiArea:
726 case ocBackSolver:
727 case ocEasterSunday:
728 case ocCurrent:
729 case ocStyle:
730 if (mbEnglish &&
731 FormulaGrammar::extractFormulaLanguage( meGrammar) == FormulaGrammar::GRAM_EXTERNAL)
733 // Both bPutOp and bRemoveFromMap stay false.
734 break;
736 [[fallthrough]];
737 default:
738 SAL_WARN("formula.core",
739 "OpCodeMap::putOpCode: reusing OpCode " << static_cast<sal_uInt16>(eOp)
740 << ", replacing '" << mpTable[eOp] << "' with '" << rStr << "' in "
741 << (mbEnglish ? "" : "non-") << "English map 0x" << ::std::hex << meGrammar);
745 // Case preserving opcode -> string, upper string -> opcode
746 if (bRemoveFromMap)
748 OUString aUpper( pCharClass ? pCharClass->uppercase( mpTable[eOp]) : rStr.toAsciiUpperCase());
749 // Ensure we remove a mapping only for the requested OpCode.
750 OpCodeHashMap::const_iterator it( maHashMap.find( aUpper));
751 if (it != maHashMap.end() && (*it).second == eOp)
752 maHashMap.erase( it);
754 if (bPutOp)
755 mpTable[eOp] = rStr;
756 OUString aUpper( pCharClass ? pCharClass->uppercase( rStr) : rStr.toAsciiUpperCase());
757 maHashMap.emplace(aUpper, eOp);
759 else
761 SAL_WARN( "formula.core", "OpCodeMap::putOpCode: OpCode out of range");
766 FormulaCompiler::FormulaCompiler( FormulaTokenArray& rArr, bool bComputeII, bool bMatrixFlag )
768 nCurrentFactorParam(0),
769 pArr( &rArr ),
770 maArrIterator( rArr ),
771 pCode( nullptr ),
772 pStack( nullptr ),
773 eLastOp( ocPush ),
774 nRecursion( 0 ),
775 nNumFmt( SvNumFormatType::UNDEFINED ),
776 pc( 0 ),
777 meGrammar( formula::FormulaGrammar::GRAM_UNSPECIFIED ),
778 bAutoCorrect( false ),
779 bCorrected( false ),
780 glSubTotal( false ),
781 needsRPNTokenCheck( false ),
782 mbJumpCommandReorder(true),
783 mbStopOnError(true),
784 mbComputeII(bComputeII),
785 mbMatrixFlag(bMatrixFlag)
789 FormulaTokenArray FormulaCompiler::smDummyTokenArray;
791 FormulaCompiler::FormulaCompiler(bool bComputeII, bool bMatrixFlag)
793 nCurrentFactorParam(0),
794 pArr( nullptr ),
795 maArrIterator( smDummyTokenArray ),
796 pCode( nullptr ),
797 pStack( nullptr ),
798 eLastOp( ocPush ),
799 nRecursion(0),
800 nNumFmt( SvNumFormatType::UNDEFINED ),
801 pc( 0 ),
802 meGrammar( formula::FormulaGrammar::GRAM_UNSPECIFIED ),
803 bAutoCorrect( false ),
804 bCorrected( false ),
805 glSubTotal( false ),
806 needsRPNTokenCheck( false ),
807 mbJumpCommandReorder(true),
808 mbStopOnError(true),
809 mbComputeII(bComputeII),
810 mbMatrixFlag(bMatrixFlag)
814 FormulaCompiler::~FormulaCompiler()
818 FormulaCompiler::OpCodeMapPtr FormulaCompiler::GetOpCodeMap( const sal_Int32 nLanguage ) const
820 const bool bTemporary = !HasOpCodeMap(nLanguage);
821 OpCodeMapPtr xMap = GetFinalOpCodeMap(nLanguage);
822 if (bTemporary)
823 const_cast<FormulaCompiler*>(this)->DestroyOpCodeMap(nLanguage);
824 return xMap;
827 FormulaCompiler::OpCodeMapPtr FormulaCompiler::GetFinalOpCodeMap( const sal_Int32 nLanguage ) const
829 FormulaCompiler::OpCodeMapPtr xMap;
830 using namespace sheet;
831 switch (nLanguage)
833 case FormulaLanguage::ODFF :
834 if (!mxSymbolsODFF)
835 InitSymbolsODFF( InitSymbols::INIT);
836 xMap = mxSymbolsODFF;
837 break;
838 case FormulaLanguage::ODF_11 :
839 if (!mxSymbolsPODF)
840 InitSymbolsPODF( InitSymbols::INIT);
841 xMap = mxSymbolsPODF;
842 break;
843 case FormulaLanguage::ENGLISH :
844 if (!mxSymbolsEnglish)
845 InitSymbolsEnglish( InitSymbols::INIT);
846 xMap = mxSymbolsEnglish;
847 break;
848 case FormulaLanguage::NATIVE :
849 if (!mxSymbolsNative)
850 InitSymbolsNative( InitSymbols::INIT);
851 xMap = mxSymbolsNative;
852 break;
853 case FormulaLanguage::XL_ENGLISH:
854 if (!mxSymbolsEnglishXL)
855 InitSymbolsEnglishXL( InitSymbols::INIT);
856 xMap = mxSymbolsEnglishXL;
857 break;
858 case FormulaLanguage::OOXML:
859 if (!mxSymbolsOOXML)
860 InitSymbolsOOXML( InitSymbols::INIT);
861 xMap = mxSymbolsOOXML;
862 break;
863 case FormulaLanguage::API :
864 if (!mxSymbolsAPI)
865 InitSymbolsAPI( InitSymbols::INIT);
866 xMap = mxSymbolsAPI;
867 break;
868 default:
869 ; // nothing, NULL map returned
871 return xMap;
874 void FormulaCompiler::DestroyOpCodeMap( const sal_Int32 nLanguage )
876 using namespace sheet;
877 switch (nLanguage)
879 case FormulaLanguage::ODFF :
880 InitSymbolsODFF( InitSymbols::DESTROY);
881 break;
882 case FormulaLanguage::ODF_11 :
883 InitSymbolsPODF( InitSymbols::DESTROY);
884 break;
885 case FormulaLanguage::ENGLISH :
886 InitSymbolsEnglish( InitSymbols::DESTROY);
887 break;
888 case FormulaLanguage::NATIVE :
889 InitSymbolsNative( InitSymbols::DESTROY);
890 break;
891 case FormulaLanguage::XL_ENGLISH:
892 InitSymbolsEnglishXL( InitSymbols::DESTROY);
893 break;
894 case FormulaLanguage::OOXML:
895 InitSymbolsOOXML( InitSymbols::DESTROY);
896 break;
897 case FormulaLanguage::API :
898 InitSymbolsAPI( InitSymbols::DESTROY);
899 break;
900 default:
901 ; // nothing
905 bool FormulaCompiler::HasOpCodeMap( const sal_Int32 nLanguage ) const
907 using namespace sheet;
908 switch (nLanguage)
910 case FormulaLanguage::ODFF :
911 return InitSymbolsODFF( InitSymbols::ASK);
912 case FormulaLanguage::ODF_11 :
913 return InitSymbolsPODF( InitSymbols::ASK);
914 case FormulaLanguage::ENGLISH :
915 return InitSymbolsEnglish( InitSymbols::ASK);
916 case FormulaLanguage::NATIVE :
917 return InitSymbolsNative( InitSymbols::ASK);
918 case FormulaLanguage::XL_ENGLISH:
919 return InitSymbolsEnglishXL( InitSymbols::ASK);
920 case FormulaLanguage::OOXML:
921 return InitSymbolsOOXML( InitSymbols::ASK);
922 case FormulaLanguage::API :
923 return InitSymbolsAPI( InitSymbols::ASK);
924 default:
925 ; // nothing
927 return false;
930 OUString FormulaCompiler::FindAddInFunction( const OUString& /*rUpperName*/, bool /*bLocalFirst*/ ) const
932 return OUString();
935 FormulaCompiler::OpCodeMapPtr FormulaCompiler::CreateOpCodeMap(
936 const uno::Sequence<
937 const sheet::FormulaOpCodeMapEntry > & rMapping,
938 bool bEnglish )
940 using sheet::FormulaOpCodeMapEntry;
941 // Filter / API maps are never Core
942 NonConstOpCodeMapPtr xMap = std::make_shared<OpCodeMap>( SC_OPCODE_LAST_OPCODE_ID + 1, false,
943 FormulaGrammar::mergeToGrammar( FormulaGrammar::setEnglishBit(
944 FormulaGrammar::GRAM_EXTERNAL, bEnglish), FormulaGrammar::CONV_UNSPECIFIED));
945 std::unique_ptr<CharClass> xCharClass( xMap->isEnglish() ? nullptr : createCharClassIfNonEnglishUI());
946 const CharClass* pCharClass = xCharClass.get();
947 for (auto const& rMapEntry : rMapping)
949 OpCode eOp = OpCode(rMapEntry.Token.OpCode);
950 if (eOp != ocExternal)
951 xMap->putOpCode( rMapEntry.Name, eOp, pCharClass);
952 else
954 OUString aExternalName;
955 if (rMapEntry.Token.Data >>= aExternalName)
956 xMap->putExternal( rMapEntry.Name, aExternalName);
957 else
959 SAL_WARN( "formula.core", "FormulaCompiler::CreateOpCodeMap: no Token.Data external name");
963 return xMap;
966 static bool lcl_fillNativeSymbols( FormulaCompiler::NonConstOpCodeMapPtr& xMap, FormulaCompiler::InitSymbols eWhat = FormulaCompiler::InitSymbols::INIT )
968 static OpCodeMapData aSymbolMap;
969 static std::map<OUString, OpCodeMapData> aLocaleSymbolMap;
970 std::unique_lock aGuard(aSymbolMap.maMtx);
972 if (comphelper::LibreOfficeKit::isActive())
974 OUString langauge = comphelper::LibreOfficeKit::getLanguageTag().getLanguage();
975 if (eWhat == FormulaCompiler::InitSymbols::ASK)
977 return aLocaleSymbolMap.contains(langauge)
978 && bool(aLocaleSymbolMap[langauge].mxSymbolMap);
980 else if (eWhat == FormulaCompiler::InitSymbols::DESTROY)
982 aLocaleSymbolMap[langauge].mxSymbolMap.reset();
984 else if (!aLocaleSymbolMap[langauge].mxSymbolMap)
986 // Core
987 aLocaleSymbolMap[langauge].mxSymbolMap = std::make_shared<FormulaCompiler::OpCodeMap>(
988 SC_OPCODE_LAST_OPCODE_ID + 1, true, FormulaGrammar::GRAM_NATIVE_UI);
989 OpCodeList aOpCodeListSymbols(RID_STRLIST_FUNCTION_NAMES_SYMBOLS,
990 aLocaleSymbolMap[langauge].mxSymbolMap);
991 OpCodeList aOpCodeListNative(RID_STRLIST_FUNCTION_NAMES,
992 aLocaleSymbolMap[langauge].mxSymbolMap);
993 // No AddInMap for native core mapping.
996 xMap = aLocaleSymbolMap[langauge].mxSymbolMap;
998 else
1000 if (eWhat == FormulaCompiler::InitSymbols::ASK)
1002 return bool(aSymbolMap.mxSymbolMap);
1004 else if (eWhat == FormulaCompiler::InitSymbols::DESTROY)
1006 aSymbolMap.mxSymbolMap.reset();
1008 else if (!aSymbolMap.mxSymbolMap)
1010 // Core
1011 aSymbolMap.mxSymbolMap = std::make_shared<FormulaCompiler::OpCodeMap>(
1012 SC_OPCODE_LAST_OPCODE_ID + 1, true, FormulaGrammar::GRAM_NATIVE_UI);
1013 OpCodeList aOpCodeListSymbols(RID_STRLIST_FUNCTION_NAMES_SYMBOLS,
1014 aSymbolMap.mxSymbolMap);
1015 OpCodeList aOpCodeListNative(RID_STRLIST_FUNCTION_NAMES, aSymbolMap.mxSymbolMap);
1016 // No AddInMap for native core mapping.
1019 xMap = aSymbolMap.mxSymbolMap;
1022 return true;
1025 const OUString& FormulaCompiler::GetNativeSymbol( OpCode eOp )
1027 NonConstOpCodeMapPtr xSymbolsNative;
1028 lcl_fillNativeSymbols( xSymbolsNative);
1029 return xSymbolsNative->getSymbol( eOp );
1032 sal_Unicode FormulaCompiler::GetNativeSymbolChar( OpCode eOp )
1034 return GetNativeSymbol(eOp)[0];
1037 bool FormulaCompiler::InitSymbolsNative( FormulaCompiler::InitSymbols eWhat ) const
1039 return lcl_fillNativeSymbols( mxSymbolsNative, eWhat);
1042 bool FormulaCompiler::InitSymbolsEnglish( FormulaCompiler::InitSymbols eWhat ) const
1044 static OpCodeMapData aMap;
1045 std::unique_lock aGuard(aMap.maMtx);
1046 if (eWhat == InitSymbols::ASK)
1047 return bool(aMap.mxSymbolMap);
1048 else if (eWhat == InitSymbols::DESTROY)
1049 aMap.mxSymbolMap.reset();
1050 else if (!aMap.mxSymbolMap)
1051 loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH, FormulaGrammar::GRAM_ENGLISH, aMap.mxSymbolMap);
1052 mxSymbolsEnglish = aMap.mxSymbolMap;
1053 return true;
1056 bool FormulaCompiler::InitSymbolsPODF( FormulaCompiler::InitSymbols eWhat ) const
1058 static OpCodeMapData aMap;
1059 std::unique_lock aGuard(aMap.maMtx);
1060 if (eWhat == InitSymbols::ASK)
1061 return bool(aMap.mxSymbolMap);
1062 else if (eWhat == InitSymbols::DESTROY)
1063 aMap.mxSymbolMap.reset();
1064 else if (!aMap.mxSymbolMap)
1065 loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH_PODF, FormulaGrammar::GRAM_PODF, aMap.mxSymbolMap, SeparatorType::RESOURCE_BASE);
1066 mxSymbolsPODF = aMap.mxSymbolMap;
1067 return true;
1070 bool FormulaCompiler::InitSymbolsAPI( FormulaCompiler::InitSymbols eWhat ) const
1072 static OpCodeMapData aMap;
1073 std::unique_lock aGuard(aMap.maMtx);
1074 if (eWhat == InitSymbols::ASK)
1075 return bool(aMap.mxSymbolMap);
1076 else if (eWhat == InitSymbols::DESTROY)
1077 aMap.mxSymbolMap.reset();
1078 else if (!aMap.mxSymbolMap)
1079 loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH_API, FormulaGrammar::GRAM_API, aMap.mxSymbolMap, SeparatorType::RESOURCE_BASE);
1080 mxSymbolsAPI = aMap.mxSymbolMap;
1081 return true;
1084 bool FormulaCompiler::InitSymbolsODFF( FormulaCompiler::InitSymbols eWhat ) const
1086 static OpCodeMapData aMap;
1087 std::unique_lock aGuard(aMap.maMtx);
1088 if (eWhat == InitSymbols::ASK)
1089 return bool(aMap.mxSymbolMap);
1090 else if (eWhat == InitSymbols::DESTROY)
1091 aMap.mxSymbolMap.reset();
1092 else if (!aMap.mxSymbolMap)
1093 loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH_ODFF, FormulaGrammar::GRAM_ODFF, aMap.mxSymbolMap, SeparatorType::RESOURCE_BASE);
1094 mxSymbolsODFF = aMap.mxSymbolMap;
1095 return true;
1098 bool FormulaCompiler::InitSymbolsEnglishXL( FormulaCompiler::InitSymbols eWhat ) const
1100 static OpCodeMapData aMap;
1101 std::unique_lock aGuard(aMap.maMtx);
1102 if (eWhat == InitSymbols::ASK)
1103 return bool(aMap.mxSymbolMap);
1104 else if (eWhat == InitSymbols::DESTROY)
1105 aMap.mxSymbolMap.reset();
1106 else if (!aMap.mxSymbolMap)
1107 loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH, FormulaGrammar::GRAM_ENGLISH, aMap.mxSymbolMap);
1108 mxSymbolsEnglishXL = aMap.mxSymbolMap;
1109 if (eWhat != InitSymbols::INIT)
1110 return true;
1112 // TODO: For now, just replace the separators to the Excel English
1113 // variants. Later, if we want to properly map Excel functions with Calc
1114 // functions, we'll need to do a little more work here.
1115 mxSymbolsEnglishXL->putOpCode( OUString(','), ocSep, nullptr);
1116 mxSymbolsEnglishXL->putOpCode( OUString(','), ocArrayColSep, nullptr);
1117 mxSymbolsEnglishXL->putOpCode( OUString(';'), ocArrayRowSep, nullptr);
1119 return true;
1122 bool FormulaCompiler::InitSymbolsOOXML( FormulaCompiler::InitSymbols eWhat ) const
1124 static OpCodeMapData aMap;
1125 std::unique_lock aGuard(aMap.maMtx);
1126 if (eWhat == InitSymbols::ASK)
1127 return bool(aMap.mxSymbolMap);
1128 else if (eWhat == InitSymbols::DESTROY)
1129 aMap.mxSymbolMap.reset();
1130 else if (!aMap.mxSymbolMap)
1131 loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH_OOXML, FormulaGrammar::GRAM_OOXML, aMap.mxSymbolMap, SeparatorType::RESOURCE_BASE);
1132 mxSymbolsOOXML = aMap.mxSymbolMap;
1133 return true;
1137 void FormulaCompiler::loadSymbols(const std::pair<const char*, int>* pSymbols, FormulaGrammar::Grammar eGrammar,
1138 NonConstOpCodeMapPtr& rxMap, SeparatorType eSepType) const
1140 if ( rxMap )
1141 return;
1143 // not Core
1144 rxMap = std::make_shared<OpCodeMap>( SC_OPCODE_LAST_OPCODE_ID + 1, eGrammar != FormulaGrammar::GRAM_ODFF, eGrammar );
1145 OpCodeList aOpCodeList(pSymbols, rxMap, eSepType);
1147 fillFromAddInMap( rxMap, eGrammar);
1148 // Fill from collection for AddIns not already present.
1149 if (FormulaGrammar::GRAM_ENGLISH == eGrammar)
1150 fillFromAddInCollectionEnglishName( rxMap);
1151 else
1153 fillFromAddInCollectionUpperName( rxMap);
1154 if (FormulaGrammar::GRAM_API == eGrammar)
1156 // Add known but not in AddInMap English names, e.g. from the
1157 // PricingFunctions AddIn or any user supplied AddIn.
1158 fillFromAddInCollectionEnglishName( rxMap);
1163 void FormulaCompiler::fillFromAddInCollectionUpperName( const NonConstOpCodeMapPtr& /*xMap */) const
1167 void FormulaCompiler::fillFromAddInCollectionEnglishName( const NonConstOpCodeMapPtr& /*xMap */) const
1171 void FormulaCompiler::fillFromAddInMap( const NonConstOpCodeMapPtr& /*xMap*/, FormulaGrammar::Grammar /*_eGrammar */) const
1175 OpCode FormulaCompiler::GetEnglishOpCode( const OUString& rName ) const
1177 FormulaCompiler::OpCodeMapPtr xMap = GetOpCodeMap( sheet::FormulaLanguage::ENGLISH);
1179 formula::OpCodeHashMap::const_iterator iLook( xMap->getHashMap().find( rName ) );
1180 bool bFound = (iLook != xMap->getHashMap().end());
1181 return bFound ? (*iLook).second : ocNone;
1184 bool FormulaCompiler::IsOpCodeVolatile( OpCode eOp )
1186 bool bRet = false;
1187 switch (eOp)
1189 // no parameters:
1190 case ocRandom:
1191 case ocGetActDate:
1192 case ocGetActTime:
1193 // one parameter:
1194 case ocFormula:
1195 case ocInfo:
1196 // more than one parameters:
1197 // ocIndirect otherwise would have to do
1198 // StopListening and StartListening on a reference for every
1199 // interpreted value.
1200 case ocIndirect:
1201 // ocOffset results in indirect references.
1202 case ocOffset:
1203 // ocDebugVar shows internal value that may change as the internal state changes.
1204 case ocDebugVar:
1205 bRet = true;
1206 break;
1207 default:
1208 bRet = false;
1209 break;
1211 return bRet;
1214 bool FormulaCompiler::IsOpCodeJumpCommand( OpCode eOp )
1216 switch (eOp)
1218 case ocIf:
1219 case ocIfError:
1220 case ocIfNA:
1221 case ocChoose:
1222 return true;
1223 default:
1226 return false;
1229 // Remove quotes, escaped quotes are unescaped.
1230 bool FormulaCompiler::DeQuote( OUString& rStr )
1232 sal_Int32 nLen = rStr.getLength();
1233 if ( nLen > 1 && rStr[0] == '\'' && rStr[ nLen-1 ] == '\'' )
1235 rStr = rStr.copy( 1, nLen-2 );
1236 rStr = rStr.replaceAll( "''", "'" );
1237 return true;
1239 return false;
1242 void FormulaCompiler::fillAddInToken(
1243 ::std::vector< sheet::FormulaOpCodeMapEntry >& /*_rVec*/,
1244 bool /*_bIsEnglish*/) const
1248 bool FormulaCompiler::IsMatrixFunction( OpCode eOpCode )
1250 switch (eOpCode)
1252 case ocDde :
1253 case ocGrowth :
1254 case ocTrend :
1255 case ocLogest :
1256 case ocLinest :
1257 case ocFrequency :
1258 case ocMatTrans :
1259 case ocMatMult :
1260 case ocMatInv :
1261 case ocMatrixUnit :
1262 case ocModalValue_Multi :
1263 case ocFourier :
1264 case ocFilter :
1265 case ocSort :
1266 case ocSortBy :
1267 return true;
1268 default:
1270 // added to avoid warnings
1273 return false;
1277 void FormulaCompiler::OpCodeMap::putCopyOpCode( const OUString& rSymbol, OpCode eOp )
1279 SAL_WARN_IF( !mpTable[eOp].isEmpty() && rSymbol.isEmpty(), "formula.core",
1280 "OpCodeMap::putCopyOpCode: NOT replacing OpCode " << static_cast<sal_uInt16>(eOp)
1281 << " '" << mpTable[eOp] << "' with empty name!");
1282 if (!mpTable[eOp].isEmpty() && rSymbol.isEmpty())
1283 maHashMap.emplace(mpTable[eOp], eOp);
1284 else
1286 mpTable[eOp] = rSymbol;
1287 maHashMap.emplace(rSymbol, eOp);
1291 void FormulaCompiler::OpCodeMap::copyFrom( const OpCodeMap& r )
1293 maHashMap = OpCodeHashMap( mnSymbols);
1295 sal_uInt16 n = r.getSymbolCount();
1296 SAL_WARN_IF( n != mnSymbols, "formula.core",
1297 "OpCodeMap::copyFrom: unequal size, this: " << mnSymbols << " that: " << n);
1298 if (n > mnSymbols)
1299 n = mnSymbols;
1301 // OpCode 0 (ocPush) should never be in a map.
1302 SAL_WARN_IF( !mpTable[0].isEmpty() || !r.mpTable[0].isEmpty(), "formula.core",
1303 "OpCodeMap::copyFrom: OpCode 0 assigned, this: '"
1304 << mpTable[0] << "' that: '" << r.mpTable[0] << "'");
1306 // For bOverrideKnownBad when copying from the English core map (ODF 1.1
1307 // and API) to the native map (UI "use English function names") replace the
1308 // known bad legacy function names with correct ones.
1309 if (r.mbCore &&
1310 FormulaGrammar::extractFormulaLanguage( meGrammar) == sheet::FormulaLanguage::NATIVE &&
1311 FormulaGrammar::extractFormulaLanguage( r.meGrammar) == sheet::FormulaLanguage::ENGLISH)
1313 for (sal_uInt16 i = 1; i < n; ++i)
1315 OUString aSymbol;
1316 OpCode eOp = OpCode(i);
1317 switch (eOp)
1319 case ocRRI:
1320 aSymbol = "RRI";
1321 break;
1322 case ocTableOp:
1323 aSymbol = "MULTIPLE.OPERATIONS";
1324 break;
1325 default:
1326 aSymbol = r.mpTable[i];
1328 putCopyOpCode( aSymbol, eOp);
1331 else
1333 for (sal_uInt16 i = 1; i < n; ++i)
1335 OpCode eOp = OpCode(i);
1336 const OUString& rSymbol = r.mpTable[i];
1337 putCopyOpCode( rSymbol, eOp);
1341 // This was meant to copy to native map that does not have AddIn symbols
1342 // but needs them from the source map. It is unclear what should happen if
1343 // the destination already had externals, so do it only if it doesn't.
1344 if (!hasExternals())
1346 maExternalHashMap = r.maExternalHashMap;
1347 maReverseExternalHashMap = r.maReverseExternalHashMap;
1348 mbCore = r.mbCore;
1349 if (mbEnglish != r.mbEnglish)
1351 // For now keep mbEnglishLocale setting, which is false for a
1352 // non-English native map we're copying to.
1353 /* TODO:
1354 if (!mbEnglish && r.mbEnglish)
1355 mbEnglishLocale = "getUseEnglishLocaleFromConfiguration()";
1356 or set from outside i.e. via ScCompiler.
1358 mbEnglish = r.mbEnglish;
1364 FormulaError FormulaCompiler::GetErrorConstant( const OUString& rName ) const
1366 FormulaError nError = FormulaError::NONE;
1367 OpCodeHashMap::const_iterator iLook( mxSymbols->getHashMap().find( rName));
1368 if (iLook != mxSymbols->getHashMap().end())
1370 switch ((*iLook).second)
1372 // Not all may make sense in a formula, but these we know as
1373 // opcodes.
1374 case ocErrNull:
1375 nError = FormulaError::NoCode;
1376 break;
1377 case ocErrDivZero:
1378 nError = FormulaError::DivisionByZero;
1379 break;
1380 case ocErrValue:
1381 nError = FormulaError::NoValue;
1382 break;
1383 case ocErrRef:
1384 nError = FormulaError::NoRef;
1385 break;
1386 case ocErrName:
1387 nError = FormulaError::NoName;
1388 break;
1389 case ocErrNum:
1390 nError = FormulaError::IllegalFPOperation;
1391 break;
1392 case ocErrNA:
1393 nError = FormulaError::NotAvailable;
1394 break;
1395 default:
1396 ; // nothing
1399 else
1401 // Per convention recognize detailed "#ERRxxx!" constants, always
1402 // untranslated. Error numbers are sal_uInt16 so at most 5 decimal
1403 // digits.
1404 if (rName.startsWithIgnoreAsciiCase("#ERR") && rName.getLength() <= 10 && rName[rName.getLength()-1] == '!')
1406 sal_uInt32 nErr = o3tl::toUInt32(rName.subView( 4, rName.getLength() - 5));
1407 if (0 < nErr && nErr <= SAL_MAX_UINT16 && isPublishedFormulaError(static_cast<FormulaError>(nErr)))
1408 nError = static_cast<FormulaError>(nErr);
1411 return nError;
1414 void FormulaCompiler::EnableJumpCommandReorder( bool bEnable )
1416 mbJumpCommandReorder = bEnable;
1419 void FormulaCompiler::EnableStopOnError( bool bEnable )
1421 mbStopOnError = bEnable;
1424 void FormulaCompiler::AppendErrorConstant( OUStringBuffer& rBuffer, FormulaError nError ) const
1426 OpCode eOp;
1427 switch (nError)
1429 case FormulaError::NoCode:
1430 eOp = ocErrNull;
1431 break;
1432 case FormulaError::DivisionByZero:
1433 eOp = ocErrDivZero;
1434 break;
1435 case FormulaError::NoValue:
1436 eOp = ocErrValue;
1437 break;
1438 case FormulaError::NoRef:
1439 eOp = ocErrRef;
1440 break;
1441 case FormulaError::NoName:
1442 eOp = ocErrName;
1443 break;
1444 case FormulaError::IllegalFPOperation:
1445 eOp = ocErrNum;
1446 break;
1447 case FormulaError::NotAvailable:
1448 eOp = ocErrNA;
1449 break;
1450 default:
1452 // Per convention create detailed "#ERRxxx!" constants, always
1453 // untranslated.
1454 rBuffer.append("#ERR");
1455 rBuffer.append(static_cast<sal_Int32>(nError));
1456 rBuffer.append('!');
1457 return;
1460 rBuffer.append( mxSymbols->getSymbol( eOp));
1463 constexpr short nRecursionMax = 100;
1465 bool FormulaCompiler::GetToken()
1467 FormulaCompilerRecursionGuard aRecursionGuard( nRecursion );
1468 if ( nRecursion > nRecursionMax )
1470 SetError( FormulaError::StackOverflow );
1471 mpLastToken = mpToken = new FormulaByteToken( ocStop );
1472 return false;
1474 if ( bAutoCorrect && !pStack )
1475 { // don't merge stacked subroutine code into entered formula
1476 aCorrectedFormula += aCorrectedSymbol;
1477 aCorrectedSymbol.clear();
1479 bool bStop = false;
1480 if (pArr->GetCodeError() != FormulaError::NONE && mbStopOnError)
1481 bStop = true;
1482 else
1484 FormulaTokenRef pSpacesToken;
1485 short nWasColRowName;
1486 if ( pArr->OpCodeBefore( maArrIterator.GetIndex() ) == ocColRowName )
1487 nWasColRowName = 1;
1488 else
1489 nWasColRowName = 0;
1490 OpCode eTmpOp;
1491 mpToken = maArrIterator.Next();
1492 while (mpToken && ((eTmpOp = mpToken->GetOpCode()) == ocSpaces || eTmpOp == ocWhitespace))
1494 if (eTmpOp == ocSpaces)
1496 // For significant whitespace remember last ocSpaces token.
1497 // Usually there's only one even for multiple spaces.
1498 pSpacesToken = mpToken;
1499 if ( nWasColRowName )
1500 nWasColRowName++;
1502 if ( bAutoCorrect && !pStack )
1503 CreateStringFromToken( aCorrectedFormula, mpToken.get() );
1504 mpToken = maArrIterator.Next();
1506 if ( bAutoCorrect && !pStack && mpToken )
1507 CreateStringFromToken( aCorrectedSymbol, mpToken.get() );
1508 if( !mpToken )
1510 if( pStack )
1512 PopTokenArray();
1513 // mpLastToken was popped as well and corresponds to the
1514 // then current last token during PushTokenArray(), e.g. for
1515 // HandleRange().
1516 return GetToken();
1518 else
1519 bStop = true;
1521 else
1523 if ( nWasColRowName >= 2 && mpToken->GetOpCode() == ocColRowName )
1524 { // convert an ocSpaces to ocIntersect in RPN
1525 mpLastToken = mpToken = new FormulaByteToken( ocIntersect );
1526 maArrIterator.StepBack(); // we advanced to the second ocColRowName, step back
1528 else if (pSpacesToken && FormulaGrammar::isExcelSyntax( meGrammar) &&
1529 mpLastToken && mpToken &&
1530 isPotentialRangeType( mpLastToken.get(), false, false) &&
1531 isPotentialRangeType( mpToken.get(), false, true))
1533 // Let IntersectionLine() <- Factor() decide how to treat this,
1534 // once the actual arguments are determined in RPN.
1535 mpLastToken = mpToken = pSpacesToken;
1536 maArrIterator.StepBack(); // step back from next non-spaces token
1537 return true;
1541 if( bStop )
1543 mpLastToken = mpToken = new FormulaByteToken( ocStop );
1544 return false;
1547 // Remember token for next round and any PushTokenArray() calls that may
1548 // occur in handlers.
1549 mpLastToken = mpToken;
1551 if ( mpToken->IsExternalRef() )
1553 return HandleExternalReference(*mpToken);
1555 else
1557 switch (mpToken->GetOpCode())
1559 case ocSubTotal:
1560 case ocAggregate:
1561 glSubTotal = true;
1562 break;
1563 case ocName:
1564 if( HandleRange())
1566 // Expanding ocName might have introduced tokens such as ocStyle that prevent formula threading,
1567 // but those wouldn't be present in the raw tokens array, so ensure RPN tokens will be checked too.
1568 needsRPNTokenCheck = true;
1569 return true;
1571 return false;
1572 case ocColRowName:
1573 return HandleColRowName();
1574 case ocDBArea:
1575 return HandleDbData();
1576 case ocTableRef:
1577 return HandleTableRef();
1578 case ocPush:
1579 if( mbComputeII )
1580 HandleIIOpCode(mpToken.get(), nullptr, 0);
1581 break;
1582 default:
1583 ; // nothing
1586 return true;
1590 // RPN creation by recursion
1591 void FormulaCompiler::Factor()
1593 if (pArr->GetCodeError() != FormulaError::NONE && mbStopOnError)
1594 return;
1596 CurrentFactor pFacToken( this );
1598 OpCode eOp = mpToken->GetOpCode();
1599 if (eOp == ocPush || eOp == ocColRowNameAuto || eOp == ocMatRef || eOp == ocDBArea
1600 || eOp == ocTableRef
1601 || (!mbJumpCommandReorder && ((eOp == ocName) || (eOp == ocColRowName) || (eOp == ocBad)))
1604 PutCode( mpToken );
1605 eOp = NextToken();
1606 if( eOp == ocOpen )
1608 // PUSH( is an error that may be caused by an unknown function.
1609 SetError(
1610 ( mpToken->GetType() == svString
1611 || mpToken->GetType() == svSingleRef )
1612 ? FormulaError::NoName : FormulaError::OperatorExpected );
1613 if ( bAutoCorrect && !pStack )
1614 { // assume multiplication
1615 aCorrectedFormula += mxSymbols->getSymbol( ocMul);
1616 bCorrected = true;
1617 NextToken();
1618 eOp = Expression();
1619 if( eOp != ocClose )
1620 SetError( FormulaError::PairExpected);
1621 else
1622 NextToken();
1626 else if( eOp == ocOpen )
1628 NextToken();
1629 eOp = Expression();
1630 while ((eOp == ocSep) && (pArr->GetCodeError() == FormulaError::NONE || !mbStopOnError))
1631 { // range list (A1;A2) converted to (A1~A2)
1632 pFacToken = mpToken;
1633 NextToken();
1634 CheckSetForceArrayParameter( mpToken, 0);
1635 eOp = Expression();
1636 // Do not ignore error here, regardless of mbStopOnError, to not
1637 // change the formula expression in case of an unexpected state.
1638 if (pArr->GetCodeError() == FormulaError::NONE && pc >= 2)
1640 // Left and right operands must be reference or function
1641 // returning reference to form a range list.
1642 const FormulaToken* p = pCode[-2];
1643 if (p && isPotentialRangeType( p, true, false))
1645 p = pCode[-1];
1646 if (p && isPotentialRangeType( p, true, true))
1648 pFacToken->NewOpCode( ocUnion, FormulaToken::PrivateAccess());
1649 // XXX NOTE: the token's eType is still svSep here!
1650 PutCode( pFacToken);
1655 if (eOp != ocClose)
1656 SetError( FormulaError::PairExpected);
1657 else
1658 NextToken();
1660 /* TODO: if no conversion to ocUnion is involved this could collect
1661 * such expression as a list or (matrix) vector to be passed as
1662 * argument for one parameter (which in fact the ocUnion svRefList is a
1663 * special case of), which would require a new StackVar type and needed
1664 * to be handled by the interpreter for functions that could support it
1665 * (i.e. already handle VAR_ARGS or svRefList parameters). This is also
1666 * not defined by ODF.
1667 * Does Excel handle =SUM((1;2))?
1668 * As is, the interpreter catches extraneous uncalculated
1669 * subexpressions like 1 of (1;2) as error. */
1671 else
1673 if( nNumFmt == SvNumFormatType::UNDEFINED )
1674 nNumFmt = lcl_GetRetFormat( eOp );
1676 if ( IsOpCodeVolatile( eOp) )
1677 pArr->SetExclusiveRecalcModeAlways();
1678 else
1680 switch( eOp )
1682 // Functions recalculated on every document load.
1683 // ONLOAD_LENIENT here to be able to distinguish and not
1684 // force a recalc (if not in an ALWAYS or ONLOAD_MUST
1685 // context) but keep an imported result from for example
1686 // OOXML a DDE call. Will be recalculated for ODFF.
1687 case ocConvertOOo :
1688 case ocDde:
1689 case ocMacro:
1690 case ocWebservice:
1691 pArr->AddRecalcMode( ScRecalcMode::ONLOAD_LENIENT );
1692 break;
1693 // RANDBETWEEN() is volatile like RAND(). Other Add-In
1694 // functions may have to be recalculated or not, we don't
1695 // know, classify as ONLOAD_LENIENT.
1696 case ocExternal:
1697 if (mpToken->GetExternal() == "com.sun.star.sheet.addin.Analysis.getRandbetween")
1698 pArr->SetExclusiveRecalcModeAlways();
1699 else
1700 pArr->AddRecalcMode( ScRecalcMode::ONLOAD_LENIENT );
1701 break;
1702 // If the referred cell is moved the value changes.
1703 case ocColumn :
1704 case ocRow :
1705 pArr->SetRecalcModeOnRefMove();
1706 break;
1707 // ocCell needs recalc on move for some possible type values.
1708 // And recalc mode on load, tdf#60645
1709 case ocCell :
1710 pArr->SetRecalcModeOnRefMove();
1711 pArr->AddRecalcMode( ScRecalcMode::ONLOAD_MUST );
1712 break;
1713 case ocHyperLink :
1714 // Cell with hyperlink needs to be calculated on load to
1715 // get its matrix result generated.
1716 pArr->AddRecalcMode( ScRecalcMode::ONLOAD_MUST );
1717 pArr->SetHyperLink( true);
1718 break;
1719 default:
1720 ; // nothing
1723 if (SC_OPCODE_START_NO_PAR <= eOp && eOp < SC_OPCODE_STOP_NO_PAR)
1725 pFacToken = mpToken;
1726 eOp = NextToken();
1727 if (eOp != ocOpen)
1729 SetError( FormulaError::PairExpected);
1730 PutCode( pFacToken );
1732 else
1734 eOp = NextToken();
1735 if (eOp != ocClose)
1736 SetError( FormulaError::PairExpected);
1737 PutCode( pFacToken);
1738 NextToken();
1741 else if (SC_OPCODE_START_1_PAR <= eOp && eOp < SC_OPCODE_STOP_1_PAR)
1743 if (eOp == ocIsoWeeknum && FormulaGrammar::isODFF( meGrammar ))
1745 // tdf#50950 ocIsoWeeknum can have 2 arguments when saved by older versions of Calc;
1746 // the opcode then has to be changed to ocWeek for backward compatibility
1747 pFacToken = mpToken;
1748 eOp = NextToken();
1749 bool bNoParam = false;
1750 if (eOp == ocOpen)
1752 eOp = NextToken();
1753 if (eOp == ocClose)
1754 bNoParam = true;
1755 else
1757 CheckSetForceArrayParameter( mpToken, 0);
1758 eOp = Expression();
1761 else
1762 SetError( FormulaError::PairExpected);
1763 sal_uInt32 nSepCount = 0;
1764 const sal_uInt16 nSepPos = maArrIterator.GetIndex() - 1; // separator position, if any
1765 if( !bNoParam )
1767 nSepCount++;
1768 while ((eOp == ocSep) && (pArr->GetCodeError() == FormulaError::NONE || !mbStopOnError))
1770 NextToken();
1771 CheckSetForceArrayParameter( mpToken, nSepCount);
1772 nSepCount++;
1773 if (nSepCount > FORMULA_MAXPARAMS)
1774 SetError( FormulaError::CodeOverflow);
1775 eOp = Expression();
1778 if (eOp != ocClose)
1779 SetError( FormulaError::PairExpected);
1780 else
1781 NextToken();
1782 pFacToken->SetByte( nSepCount );
1783 if (nSepCount == 2)
1785 // An old mode!=1 indicates ISO week, remove argument if
1786 // literal double value and keep function. Anything else
1787 // can not be resolved, there exists no "like ISO but week
1788 // starts on Sunday" mode in WEEKNUM and for an expression
1789 // we can't determine.
1790 // Current index is nSepPos+3 if expression stops, or
1791 // nSepPos+4 if expression continues after the call because
1792 // we just called NextToken() to move away from it.
1793 if (pc >= 2 && (maArrIterator.GetIndex() == nSepPos + 3 || maArrIterator.GetIndex() == nSepPos + 4) &&
1794 pArr->TokenAt(nSepPos+1)->GetType() == svDouble &&
1795 pArr->TokenAt(nSepPos+1)->GetDouble() != 1.0 &&
1796 pArr->TokenAt(nSepPos+2)->GetOpCode() == ocClose &&
1797 pArr->RemoveToken( nSepPos, 2) == 2)
1799 maArrIterator.AfterRemoveToken( nSepPos, 2);
1800 // Remove the ocPush/svDouble just removed also from
1801 // the compiler local RPN array.
1802 --pCode; --pc;
1803 (*pCode)->DecRef(); // may be dead now
1804 pFacToken->SetByte( nSepCount - 1 );
1806 else
1808 // For the remaining two arguments cases use the
1809 // compatibility function.
1810 pFacToken->NewOpCode( ocWeeknumOOo, FormulaToken::PrivateAccess());
1813 PutCode( pFacToken );
1815 else
1817 // standard handling of 1-parameter opcodes
1818 pFacToken = mpToken;
1819 eOp = NextToken();
1820 if( nNumFmt == SvNumFormatType::UNDEFINED && eOp == ocNot )
1821 nNumFmt = SvNumFormatType::LOGICAL;
1822 if (eOp == ocOpen)
1824 NextToken();
1825 CheckSetForceArrayParameter( mpToken, 0);
1826 eOp = Expression();
1828 else
1829 SetError( FormulaError::PairExpected);
1830 if (eOp != ocClose)
1831 SetError( FormulaError::PairExpected);
1832 else if ( pArr->GetCodeError() == FormulaError::NONE )
1834 pFacToken->SetByte( 1 );
1835 if (mbComputeII)
1837 FormulaToken** pArg = pCode - 1;
1838 HandleIIOpCode(pFacToken, &pArg, 1);
1841 PutCode( pFacToken );
1842 NextToken();
1845 else if ((SC_OPCODE_START_2_PAR <= eOp && eOp < SC_OPCODE_STOP_2_PAR)
1846 || eOp == ocExternal
1847 || eOp == ocMacro
1848 || eOp == ocAnd
1849 || eOp == ocOr
1850 || eOp == ocBad
1851 || ( eOp >= ocInternalBegin && eOp <= ocInternalEnd )
1852 || (!mbJumpCommandReorder && IsOpCodeJumpCommand(eOp)))
1854 pFacToken = mpToken;
1855 OpCode eMyLastOp = eOp;
1856 eOp = NextToken();
1857 bool bNoParam = false;
1858 bool bBadName = false;
1859 if (eOp == ocOpen)
1861 eOp = NextToken();
1862 if (eOp == ocClose)
1863 bNoParam = true;
1864 else
1866 CheckSetForceArrayParameter( mpToken, 0);
1867 eOp = Expression();
1870 else if (eMyLastOp == ocBad)
1872 // Just a bad name, not an unknown function, no parameters, no
1873 // closing expected.
1874 bBadName = true;
1875 bNoParam = true;
1877 else
1878 SetError( FormulaError::PairExpected);
1879 sal_uInt32 nSepCount = 0;
1880 if( !bNoParam )
1882 bool bDoIICompute = mbComputeII;
1883 // Array of FormulaToken double pointers to collect the parameters of II opcodes.
1884 FormulaToken*** pArgArray = nullptr;
1885 if (bDoIICompute)
1887 pArgArray = static_cast<FormulaToken***>(alloca(sizeof(FormulaToken**)*FORMULA_MAXPARAMSII));
1888 if (!pArgArray)
1889 bDoIICompute = false;
1892 nSepCount++;
1894 if (bDoIICompute)
1895 pArgArray[nSepCount-1] = pCode - 1; // Add first argument
1897 while ((eOp == ocSep) && (pArr->GetCodeError() == FormulaError::NONE || !mbStopOnError))
1899 NextToken();
1900 CheckSetForceArrayParameter( mpToken, nSepCount);
1901 nSepCount++;
1902 if (nSepCount > FORMULA_MAXPARAMS)
1903 SetError( FormulaError::CodeOverflow);
1904 eOp = Expression();
1905 if (bDoIICompute && nSepCount <= FORMULA_MAXPARAMSII)
1906 pArgArray[nSepCount - 1] = pCode - 1; // Add rest of the arguments
1908 if (bDoIICompute)
1909 HandleIIOpCode(pFacToken, pArgArray,
1910 std::min(nSepCount, static_cast<sal_uInt32>(FORMULA_MAXPARAMSII)));
1912 bool bDone = false;
1913 if (bBadName)
1914 ; // nothing, keep current token for return
1915 else if (eOp != ocClose)
1916 SetError( FormulaError::PairExpected);
1917 else
1919 NextToken();
1920 bDone = true;
1922 // Jumps are just normal functions for the FunctionAutoPilot tree view
1923 if (!mbJumpCommandReorder && pFacToken->GetType() == svJump)
1924 pFacToken = new FormulaFAPToken( pFacToken->GetOpCode(), nSepCount, pFacToken );
1925 else
1926 pFacToken->SetByte( nSepCount );
1927 PutCode( pFacToken );
1929 if (bDone)
1930 AnnotateOperands();
1932 else if (IsOpCodeJumpCommand(eOp))
1934 // the PC counters are -1
1935 pFacToken = mpToken;
1936 switch (eOp)
1938 case ocIf:
1939 pFacToken->GetJump()[ 0 ] = 3; // if, else, behind
1940 break;
1941 case ocChoose:
1942 pFacToken->GetJump()[ 0 ] = FORMULA_MAXJUMPCOUNT + 1;
1943 break;
1944 case ocIfError:
1945 case ocIfNA:
1946 pFacToken->GetJump()[ 0 ] = 2; // if, behind
1947 break;
1948 default:
1949 SAL_WARN("formula.core","Jump OpCode: " << +eOp);
1950 assert(!"FormulaCompiler::Factor: someone forgot to add a jump count case");
1952 eOp = NextToken();
1953 if (eOp == ocOpen)
1955 NextToken();
1956 CheckSetForceArrayParameter( mpToken, 0);
1957 eOp = Expression();
1959 else
1960 SetError( FormulaError::PairExpected);
1961 PutCode( pFacToken );
1962 // During AutoCorrect (since pArr->GetCodeError() is
1963 // ignored) an unlimited ocIf would crash because
1964 // ScRawToken::Clone() allocates the JumpBuffer according to
1965 // nJump[0]*2+2, which is 3*2+2 on ocIf and 2*2+2 ocIfError and ocIfNA.
1966 short nJumpMax;
1967 OpCode eFacOpCode = pFacToken->GetOpCode();
1968 switch (eFacOpCode)
1970 case ocIf:
1971 nJumpMax = 3;
1972 break;
1973 case ocChoose:
1974 nJumpMax = FORMULA_MAXJUMPCOUNT;
1975 break;
1976 case ocIfError:
1977 case ocIfNA:
1978 nJumpMax = 2;
1979 break;
1980 case ocStop:
1981 // May happen only if PutCode(pFacToken) ran into overflow.
1982 nJumpMax = 0;
1983 assert(pc == FORMULA_MAXTOKENS && pArr->GetCodeError() != FormulaError::NONE);
1984 break;
1985 default:
1986 nJumpMax = 0;
1987 SAL_WARN("formula.core","Jump OpCode: " << +eFacOpCode);
1988 assert(!"FormulaCompiler::Factor: someone forgot to add a jump max case");
1990 short nJumpCount = 0;
1991 while ( (nJumpCount < (FORMULA_MAXJUMPCOUNT - 1)) && (eOp == ocSep)
1992 && (pArr->GetCodeError() == FormulaError::NONE || !mbStopOnError))
1994 if ( ++nJumpCount <= nJumpMax )
1995 pFacToken->GetJump()[nJumpCount] = pc-1;
1996 NextToken();
1997 CheckSetForceArrayParameter( mpToken, nJumpCount - 1);
1998 eOp = Expression();
1999 // ocSep or ocClose terminate the subexpression
2000 PutCode( mpToken );
2002 if (eOp != ocClose)
2003 SetError( FormulaError::PairExpected);
2004 else
2006 NextToken();
2007 // always limit to nJumpMax, no arbitrary overwrites
2008 if ( ++nJumpCount <= nJumpMax )
2009 pFacToken->GetJump()[ nJumpCount ] = pc-1;
2010 eFacOpCode = pFacToken->GetOpCode();
2011 bool bLimitOk;
2012 switch (eFacOpCode)
2014 case ocIf:
2015 bLimitOk = (nJumpCount <= 3);
2016 break;
2017 case ocChoose:
2018 bLimitOk = (nJumpCount < FORMULA_MAXJUMPCOUNT);
2019 break;
2020 case ocIfError:
2021 case ocIfNA:
2022 bLimitOk = (nJumpCount <= 2);
2023 break;
2024 case ocStop:
2025 // May happen only if PutCode(pFacToken) ran into overflow.
2026 // This may had resulted from a stacked token array and
2027 // error wasn't propagated so assert only the program
2028 // counter.
2029 bLimitOk = false;
2030 assert(pc == FORMULA_MAXTOKENS);
2031 break;
2032 default:
2033 bLimitOk = false;
2034 SAL_WARN("formula.core","Jump OpCode: " << +eFacOpCode);
2035 assert(!"FormulaCompiler::Factor: someone forgot to add a jump limit case");
2037 if (bLimitOk)
2038 pFacToken->GetJump()[ 0 ] = nJumpCount;
2039 else
2040 SetError( FormulaError::IllegalParameter);
2043 else if ( eOp == ocMissing )
2045 PutCode( mpToken );
2046 NextToken();
2048 else if ( eOp == ocClose )
2050 SetError( FormulaError::ParameterExpected );
2052 else if ( eOp == ocSep )
2053 { // Subsequent ocSep
2054 SetError( FormulaError::ParameterExpected );
2055 if ( bAutoCorrect && !pStack )
2057 aCorrectedSymbol.clear();
2058 bCorrected = true;
2061 else if ( mpToken->IsExternalRef() )
2063 PutCode( mpToken);
2064 NextToken();
2066 else
2068 SetError( FormulaError::UnknownToken );
2069 if ( bAutoCorrect && !pStack )
2071 if ( eOp == ocStop )
2072 { // trailing operator w/o operand
2073 sal_Int32 nLen = aCorrectedFormula.getLength();
2074 if ( nLen )
2075 aCorrectedFormula = aCorrectedFormula.copy( 0, nLen - 1 );
2076 aCorrectedSymbol.clear();
2077 bCorrected = true;
2084 void FormulaCompiler::RangeLine()
2086 Factor();
2087 while (mpToken->GetOpCode() == ocRange)
2089 FormulaToken** pCode1 = pCode - 1;
2090 FormulaTokenRef p = mpToken;
2091 NextToken();
2092 Factor();
2093 FormulaToken** pCode2 = pCode - 1;
2094 if (!MergeRangeReference( pCode1, pCode2))
2095 PutCode(p);
2099 void FormulaCompiler::IntersectionLine()
2101 RangeLine();
2102 while (mpToken->GetOpCode() == ocIntersect || mpToken->GetOpCode() == ocSpaces)
2104 sal_uInt16 nCodeIndex = maArrIterator.GetIndex() - 1;
2105 FormulaToken** pCode1 = pCode - 1;
2106 FormulaTokenRef p = mpToken;
2107 NextToken();
2108 RangeLine();
2109 FormulaToken** pCode2 = pCode - 1;
2110 if (p->GetOpCode() == ocSpaces)
2112 // Convert to intersection if both left and right are references or
2113 // functions (potentially returning references, if not then a space
2114 // or no space would be a syntax error anyway), not other operators
2115 // or operands. Else discard.
2116 if (isAdjacentOrGapRpnEnd( pc, pCode, pCode1, pCode2) && isIntersectable( pCode1, pCode2))
2118 FormulaTokenRef pIntersect( new FormulaByteToken( ocIntersect));
2119 // Replace ocSpaces with ocIntersect so that when switching
2120 // formula syntax the correct operator string is created.
2121 // coverity[freed_arg : FALSE] - FormulaTokenRef has a ref so ReplaceToken won't delete pIntersect
2122 pArr->ReplaceToken( nCodeIndex, pIntersect.get(), FormulaTokenArray::ReplaceMode::CODE_ONLY);
2123 PutCode( pIntersect);
2126 else
2128 PutCode(p);
2133 void FormulaCompiler::UnionLine()
2135 IntersectionLine();
2136 while (mpToken->GetOpCode() == ocUnion)
2138 FormulaTokenRef p = mpToken;
2139 NextToken();
2140 IntersectionLine();
2141 PutCode(p);
2145 void FormulaCompiler::UnaryLine()
2147 if( mpToken->GetOpCode() == ocAdd )
2148 GetToken();
2149 else if (SC_OPCODE_START_UN_OP <= mpToken->GetOpCode() &&
2150 mpToken->GetOpCode() < SC_OPCODE_STOP_UN_OP)
2152 FormulaTokenRef p = mpToken;
2153 NextToken();
2154 UnaryLine();
2155 if (mbComputeII)
2157 FormulaToken** pArg = pCode - 1;
2158 HandleIIOpCode(p.get(), &pArg, 1);
2160 PutCode( p );
2162 else
2163 UnionLine();
2166 void FormulaCompiler::PostOpLine()
2168 UnaryLine();
2169 while ( mpToken->GetOpCode() == ocPercentSign )
2170 { // this operator _follows_ its operand
2171 if (mbComputeII)
2173 FormulaToken** pArg = pCode - 1;
2174 HandleIIOpCode(mpToken.get(), &pArg, 1);
2176 PutCode( mpToken );
2177 NextToken();
2181 void FormulaCompiler::PowLine()
2183 PostOpLine();
2184 while (mpToken->GetOpCode() == ocPow)
2186 FormulaTokenRef p = mpToken;
2187 FormulaToken** pArgArray[2];
2188 if (mbComputeII)
2189 pArgArray[0] = pCode - 1; // Add first argument
2190 NextToken();
2191 PostOpLine();
2192 if (mbComputeII)
2194 pArgArray[1] = pCode - 1; // Add second argument
2195 HandleIIOpCode(p.get(), pArgArray, 2);
2197 PutCode(p);
2201 void FormulaCompiler::MulDivLine()
2203 PowLine();
2204 while (mpToken->GetOpCode() == ocMul || mpToken->GetOpCode() == ocDiv)
2206 FormulaTokenRef p = mpToken;
2207 FormulaToken** pArgArray[2];
2208 if (mbComputeII)
2209 pArgArray[0] = pCode - 1; // Add first argument
2210 NextToken();
2211 PowLine();
2212 if (mbComputeII)
2214 pArgArray[1] = pCode - 1; // Add second argument
2215 HandleIIOpCode(p.get(), pArgArray, 2);
2217 PutCode(p);
2221 void FormulaCompiler::AddSubLine()
2223 MulDivLine();
2224 while (mpToken->GetOpCode() == ocAdd || mpToken->GetOpCode() == ocSub)
2226 FormulaTokenRef p = mpToken;
2227 FormulaToken** pArgArray[2];
2228 if (mbComputeII)
2229 pArgArray[0] = pCode - 1; // Add first argument
2230 NextToken();
2231 MulDivLine();
2232 if (mbComputeII)
2234 pArgArray[1] = pCode - 1; // Add second argument
2235 HandleIIOpCode(p.get(), pArgArray, 2);
2237 PutCode(p);
2241 void FormulaCompiler::ConcatLine()
2243 AddSubLine();
2244 while (mpToken->GetOpCode() == ocAmpersand)
2246 FormulaTokenRef p = mpToken;
2247 FormulaToken** pArgArray[2];
2248 if (mbComputeII)
2249 pArgArray[0] = pCode - 1; // Add first argument
2250 NextToken();
2251 AddSubLine();
2252 if (mbComputeII)
2254 pArgArray[1] = pCode - 1; // Add second argument
2255 HandleIIOpCode(p.get(), pArgArray, 2);
2257 PutCode(p);
2261 void FormulaCompiler::CompareLine()
2263 ConcatLine();
2264 while (mpToken->GetOpCode() >= ocEqual && mpToken->GetOpCode() <= ocGreaterEqual)
2266 FormulaTokenRef p = mpToken;
2267 FormulaToken** pArgArray[2];
2268 if (mbComputeII)
2269 pArgArray[0] = pCode - 1; // Add first argument
2270 NextToken();
2271 ConcatLine();
2272 if (mbComputeII)
2274 pArgArray[1] = pCode - 1; // Add second argument
2275 HandleIIOpCode(p.get(), pArgArray, 2);
2277 PutCode(p);
2281 OpCode FormulaCompiler::Expression()
2283 FormulaCompilerRecursionGuard aRecursionGuard( nRecursion );
2284 if ( nRecursion > nRecursionMax )
2286 SetError( FormulaError::StackOverflow );
2287 return ocStop; //! generate token instead?
2289 CompareLine();
2290 while (mpToken->GetOpCode() == ocAnd || mpToken->GetOpCode() == ocOr)
2292 FormulaTokenRef p = mpToken;
2293 mpToken->SetByte( 2 ); // 2 parameters!
2294 FormulaToken** pArgArray[2];
2295 if (mbComputeII)
2296 pArgArray[0] = pCode - 1; // Add first argument
2297 NextToken();
2298 CompareLine();
2299 if (mbComputeII)
2301 pArgArray[1] = pCode - 1; // Add second argument
2302 HandleIIOpCode(p.get(), pArgArray, 2);
2304 PutCode(p);
2306 return mpToken->GetOpCode();
2310 void FormulaCompiler::SetError( FormulaError /*nError*/ )
2314 FormulaTokenRef FormulaCompiler::ExtendRangeReference( FormulaToken & /*rTok1*/, FormulaToken & /*rTok2*/ )
2316 return FormulaTokenRef();
2319 bool FormulaCompiler::MergeRangeReference( FormulaToken * * const pCode1, FormulaToken * const * const pCode2 )
2321 if (!isAdjacentRpnEnd( pc, pCode, pCode1, pCode2))
2322 return false;
2324 FormulaToken *p1 = *pCode1, *p2 = *pCode2;
2325 FormulaTokenRef p = ExtendRangeReference( *p1, *p2);
2326 if (!p)
2327 return false;
2329 p->IncRef();
2330 p1->DecRef();
2331 p2->DecRef();
2332 *pCode1 = p.get();
2333 --pCode;
2334 --pc;
2336 return true;
2339 bool FormulaCompiler::CompileTokenArray()
2341 glSubTotal = false;
2342 bCorrected = false;
2343 needsRPNTokenCheck = false;
2344 if (pArr->GetCodeError() == FormulaError::NONE || !mbStopOnError)
2346 if ( bAutoCorrect )
2348 aCorrectedFormula.clear();
2349 aCorrectedSymbol.clear();
2351 pArr->DelRPN();
2352 maArrIterator.Reset();
2353 pStack = nullptr;
2354 FormulaToken* pDataArray[ FORMULA_MAXTOKENS + 1 ];
2355 // Code in some places refers to the last token as 'pCode - 1', which may
2356 // point before the first element if the expression is bad. So insert a dummy
2357 // node in that place which will make that token be nullptr.
2358 pDataArray[ 0 ] = nullptr;
2359 FormulaToken** pData = pDataArray + 1;
2360 pCode = pData;
2361 bool bWasForced = pArr->IsRecalcModeForced();
2362 if ( bWasForced && bAutoCorrect )
2363 aCorrectedFormula = "=";
2364 pArr->ClearRecalcMode();
2365 maArrIterator.Reset();
2366 eLastOp = ocOpen;
2367 pc = 0;
2368 NextToken();
2369 OpCode eOp = Expression();
2370 // Some trailing garbage that doesn't form an expression?
2371 if (eOp != ocStop)
2372 SetError( FormulaError::OperatorExpected);
2373 PostProcessCode();
2375 FormulaError nErrorBeforePop = pArr->GetCodeError();
2377 while( pStack )
2378 PopTokenArray();
2379 if( pc )
2381 pArr->CreateNewRPNArrayFromData( pData, pc );
2382 if( needsRPNTokenCheck )
2383 pArr->CheckAllRPNTokens();
2386 // once an error, always an error
2387 if( pArr->GetCodeError() == FormulaError::NONE && nErrorBeforePop != FormulaError::NONE )
2388 pArr->SetCodeError( nErrorBeforePop);
2390 if (pArr->GetCodeError() != FormulaError::NONE && mbStopOnError)
2392 pArr->DelRPN();
2393 maArrIterator.Reset();
2394 pArr->SetHyperLink( false);
2397 if ( bWasForced )
2398 pArr->SetRecalcModeForced();
2400 if( nNumFmt == SvNumFormatType::UNDEFINED )
2401 nNumFmt = SvNumFormatType::NUMBER;
2402 return glSubTotal;
2405 void FormulaCompiler::PopTokenArray()
2407 if( !pStack )
2408 return;
2410 FormulaArrayStack* p = pStack;
2411 pStack = p->pNext;
2412 // obtain special RecalcMode from SharedFormula
2413 if ( pArr->IsRecalcModeAlways() )
2414 p->pArr->SetExclusiveRecalcModeAlways();
2415 else if ( !pArr->IsRecalcModeNormal() && p->pArr->IsRecalcModeNormal() )
2416 p->pArr->SetMaskedRecalcMode( pArr->GetRecalcMode() );
2417 p->pArr->SetCombinedBitsRecalcMode( pArr->GetRecalcMode() );
2418 if ( pArr->IsHyperLink() ) // fdo 87534
2419 p->pArr->SetHyperLink( true );
2420 if( p->bTemp )
2421 delete pArr;
2422 pArr = p->pArr;
2423 maArrIterator = FormulaTokenArrayPlainIterator(*pArr);
2424 maArrIterator.Jump(p->nIndex);
2425 mpLastToken = p->mpLastToken;
2426 delete p;
2429 void FormulaCompiler::CreateStringFromTokenArray( OUString& rFormula )
2431 OUStringBuffer aBuffer( pArr->GetLen() * 5 );
2432 CreateStringFromTokenArray( aBuffer );
2433 rFormula = aBuffer.makeStringAndClear();
2436 void FormulaCompiler::CreateStringFromTokenArray( OUStringBuffer& rBuffer )
2438 rBuffer.setLength(0);
2439 if( !pArr->GetLen() )
2440 return;
2442 FormulaTokenArray* pSaveArr = pArr;
2443 int nSaveIndex = maArrIterator.GetIndex();
2444 bool bODFF = FormulaGrammar::isODFF( meGrammar);
2445 if (bODFF || FormulaGrammar::isPODF( meGrammar) )
2447 // Scan token array for missing args and re-write if present.
2448 MissingConventionODF aConv( bODFF);
2449 if (pArr->NeedsPodfRewrite( aConv))
2451 pArr = pArr->RewriteMissing( aConv );
2452 maArrIterator = FormulaTokenArrayPlainIterator( *pArr );
2455 else if ( FormulaGrammar::isOOXML( meGrammar ) )
2457 // Scan token array for missing args and rewrite if present.
2458 if (pArr->NeedsOoxmlRewrite())
2460 MissingConventionOOXML aConv;
2461 pArr = pArr->RewriteMissing( aConv );
2462 maArrIterator = FormulaTokenArrayPlainIterator( *pArr );
2466 // At least one character per token, plus some are references, some are
2467 // function names, some are numbers, ...
2468 rBuffer.ensureCapacity( pArr->GetLen() * 5 );
2470 if ( pArr->IsRecalcModeForced() )
2471 rBuffer.append( '=');
2472 const FormulaToken* t = maArrIterator.First();
2473 while( t )
2474 t = CreateStringFromToken( rBuffer, t, true );
2476 if (pSaveArr != pArr)
2478 delete pArr;
2479 pArr = pSaveArr;
2480 maArrIterator = FormulaTokenArrayPlainIterator( *pArr );
2481 maArrIterator.Jump(nSaveIndex);
2485 const FormulaToken* FormulaCompiler::CreateStringFromToken( OUString& rFormula, const FormulaToken* pTokenP )
2487 OUStringBuffer aBuffer;
2488 const FormulaToken* p = CreateStringFromToken( aBuffer, pTokenP );
2489 rFormula += aBuffer;
2490 return p;
2493 const FormulaToken* FormulaCompiler::CreateStringFromToken( OUStringBuffer& rBuffer, const FormulaToken* pTokenP,
2494 bool bAllowArrAdvance )
2496 bool bNext = true;
2497 bool bSpaces = false;
2498 const FormulaToken* t = pTokenP;
2499 OpCode eOp = t->GetOpCode();
2500 if( eOp >= ocAnd && eOp <= ocOr )
2502 // AND, OR infix?
2503 if ( bAllowArrAdvance )
2504 t = maArrIterator.Next();
2505 else
2506 t = maArrIterator.PeekNext();
2507 bNext = false;
2508 bSpaces = ( !t || t->GetOpCode() != ocOpen );
2510 if( bSpaces )
2511 rBuffer.append( ' ');
2513 if (eOp == ocSpaces || eOp == ocWhitespace)
2515 bool bWriteSpaces = true;
2516 if (eOp == ocSpaces && mxSymbols->isODFF())
2518 const FormulaToken* p = maArrIterator.PeekPrevNoSpaces();
2519 bool bIntersectionOp = (p && p->GetOpCode() == ocColRowName);
2520 if (bIntersectionOp)
2522 p = maArrIterator.PeekNextNoSpaces();
2523 bIntersectionOp = (p && p->GetOpCode() == ocColRowName);
2525 if (bIntersectionOp)
2527 rBuffer.append( "!!");
2528 bWriteSpaces = false;
2531 if (bWriteSpaces)
2533 // ODF v1.3 OpenFormula 5.14 Whitespace states "whitespace shall
2534 // not separate a function name from its initial opening
2535 // parenthesis".
2537 // ECMA-376-1:2016 18.17.2 Syntax states "that no space characters
2538 // shall separate a function-name from the left parenthesis (()
2539 // that follows it." and Excel even chokes on it.
2541 // Suppress/remove it in any case also in UI, it will not be
2542 // preserved.
2543 const FormulaToken* p = maArrIterator.PeekPrevNoSpaces();
2544 if (p && p->IsFunction())
2546 p = maArrIterator.PeekNextNoSpaces();
2547 if (p && p->GetOpCode() == ocOpen)
2548 bWriteSpaces = false;
2551 if (bWriteSpaces)
2553 // most times it's just one blank
2554 sal_uInt8 n = t->GetByte();
2555 for ( sal_uInt8 j=0; j<n; ++j )
2557 if (eOp == ocWhitespace)
2558 rBuffer.append( t->GetChar());
2559 else
2560 rBuffer.append( ' ');
2564 else if( eOp >= ocInternalBegin && eOp <= ocInternalEnd )
2565 rBuffer.appendAscii( pInternal[ eOp - ocInternalBegin ] );
2566 else if (eOp == ocIntersect)
2568 // Nasty, ugly, horrific, terrifying...
2569 if (FormulaGrammar::isExcelSyntax( meGrammar))
2570 rBuffer.append(' ');
2571 else
2572 rBuffer.append( mxSymbols->getSymbol( eOp));
2574 else if( static_cast<sal_uInt16>(eOp) < mxSymbols->getSymbolCount()) // Keyword:
2575 rBuffer.append( mxSymbols->getSymbol( eOp));
2576 else
2578 SAL_WARN( "formula.core","unknown OpCode");
2579 rBuffer.append( GetNativeSymbol( ocErrName ));
2581 if( bNext )
2583 if (t->IsExternalRef())
2585 CreateStringFromExternal( rBuffer, pTokenP);
2587 else
2589 switch( t->GetType() )
2591 case svDouble:
2592 AppendDouble( rBuffer, t->GetDouble() );
2593 break;
2595 case svString:
2596 if( eOp == ocBad || eOp == ocStringXML )
2597 rBuffer.append( t->GetString().getString());
2598 else
2599 AppendString( rBuffer, t->GetString().getString() );
2600 break;
2601 case svSingleRef:
2602 CreateStringFromSingleRef( rBuffer, t);
2603 break;
2604 case svDoubleRef:
2605 CreateStringFromDoubleRef( rBuffer, t);
2606 break;
2607 case svMatrix:
2608 case svMatrixCell:
2609 CreateStringFromMatrix( rBuffer, t );
2610 break;
2612 case svIndex:
2613 CreateStringFromIndex( rBuffer, t );
2614 if (t->GetOpCode() == ocTableRef && bAllowArrAdvance && NeedsTableRefTransformation())
2616 // Suppress all TableRef related tokens, the resulting
2617 // range was written by CreateStringFromIndex().
2618 const FormulaToken* const p = maArrIterator.PeekNext();
2619 if (p && p->GetOpCode() == ocTableRefOpen)
2621 int nLevel = 0;
2624 t = maArrIterator.Next();
2625 if (!t)
2626 break;
2628 // Switch cases correspond with those in
2629 // ScCompiler::HandleTableRef()
2630 switch (t->GetOpCode())
2632 case ocTableRefOpen:
2633 ++nLevel;
2634 break;
2635 case ocTableRefClose:
2636 --nLevel;
2637 break;
2638 case ocTableRefItemAll:
2639 case ocTableRefItemHeaders:
2640 case ocTableRefItemData:
2641 case ocTableRefItemTotals:
2642 case ocTableRefItemThisRow:
2643 case ocSep:
2644 case ocPush:
2645 case ocRange:
2646 case ocSpaces:
2647 case ocWhitespace:
2648 break;
2649 default:
2650 nLevel = 0;
2651 bNext = false;
2653 } while (nLevel);
2656 break;
2657 case svExternal:
2659 // mapped or translated name of AddIns
2660 OUString aAddIn( t->GetExternal() );
2661 bool bMapped = mxSymbols->isPODF(); // ODF 1.1 directly uses programmatical name
2662 if (!bMapped && mxSymbols->hasExternals())
2664 ExternalHashMap::const_iterator iLook = mxSymbols->getReverseExternalHashMap().find( aAddIn);
2665 if (iLook != mxSymbols->getReverseExternalHashMap().end())
2667 aAddIn = (*iLook).second;
2668 bMapped = true;
2671 if (!bMapped && !mxSymbols->isEnglish())
2672 LocalizeString( aAddIn );
2673 rBuffer.append( aAddIn);
2675 break;
2676 case svError:
2677 AppendErrorConstant( rBuffer, t->GetError());
2678 break;
2679 case svByte:
2680 case svJump:
2681 case svFAP:
2682 case svMissing:
2683 case svSep:
2684 break; // Opcodes
2685 default:
2686 SAL_WARN("formula.core", "FormulaCompiler::GetStringFromToken: unknown token type " << t->GetType());
2687 } // of switch
2690 if( bSpaces )
2691 rBuffer.append( ' ');
2692 if ( bAllowArrAdvance )
2694 if( bNext )
2695 t = maArrIterator.Next();
2696 return t;
2698 return pTokenP;
2702 void FormulaCompiler::AppendDouble( OUStringBuffer& rBuffer, double fVal ) const
2704 if ( mxSymbols->isEnglishLocale() )
2706 ::rtl::math::doubleToUStringBuffer( rBuffer, fVal,
2707 rtl_math_StringFormat_Automatic,
2708 rtl_math_DecimalPlaces_Max, '.', true );
2710 else
2712 SvtSysLocale aSysLocale;
2713 ::rtl::math::doubleToUStringBuffer( rBuffer, fVal,
2714 rtl_math_StringFormat_Automatic,
2715 rtl_math_DecimalPlaces_Max,
2716 aSysLocale.GetLocaleData().getNumDecimalSep()[0],
2717 true );
2721 void FormulaCompiler::AppendBoolean( OUStringBuffer& rBuffer, bool bVal ) const
2723 rBuffer.append( mxSymbols->getSymbol( bVal ? ocTrue : ocFalse ) );
2726 void FormulaCompiler::AppendString( OUStringBuffer& rBuffer, const OUString & rStr )
2728 rBuffer.append( '"');
2729 if ( lcl_UnicodeStrChr( rStr.getStr(), '"' ) == nullptr )
2730 rBuffer.append( rStr );
2731 else
2733 OUString aStr = rStr.replaceAll( "\"", "\"\"" );
2734 rBuffer.append(aStr);
2736 rBuffer.append( '"');
2739 bool FormulaCompiler::NeedsTableRefTransformation() const
2741 // Currently only UI representations and OOXML export use Table structured
2742 // references. Not defined in ODFF.
2743 // Unnecessary to explicitly check for ODFF grammar as the ocTableRefOpen
2744 // symbol is not defined there.
2745 return mxSymbols->getSymbol( ocTableRefOpen).isEmpty() || FormulaGrammar::isPODF( meGrammar);
2748 void FormulaCompiler::UpdateSeparatorsNative(
2749 const OUString& rSep, const OUString& rArrayColSep, const OUString& rArrayRowSep )
2751 NonConstOpCodeMapPtr xSymbolsNative;
2752 lcl_fillNativeSymbols( xSymbolsNative);
2753 xSymbolsNative->putOpCode( rSep, ocSep, nullptr);
2754 xSymbolsNative->putOpCode( rArrayColSep, ocArrayColSep, nullptr);
2755 xSymbolsNative->putOpCode( rArrayRowSep, ocArrayRowSep, nullptr);
2758 void FormulaCompiler::ResetNativeSymbols()
2760 NonConstOpCodeMapPtr xSymbolsNative;
2761 lcl_fillNativeSymbols( xSymbolsNative, InitSymbols::DESTROY);
2762 lcl_fillNativeSymbols( xSymbolsNative);
2765 void FormulaCompiler::SetNativeSymbols( const OpCodeMapPtr& xMap )
2767 NonConstOpCodeMapPtr xSymbolsNative;
2768 lcl_fillNativeSymbols( xSymbolsNative);
2769 xSymbolsNative->copyFrom( *xMap );
2773 OpCode FormulaCompiler::NextToken()
2775 if( !GetToken() )
2776 return ocStop;
2777 OpCode eOp = mpToken->GetOpCode();
2778 // There must be an operator before a push
2779 if ( (eOp == ocPush || eOp == ocColRowNameAuto) &&
2780 !( (eLastOp == ocOpen) || (eLastOp == ocSep) ||
2781 (SC_OPCODE_START_BIN_OP <= eLastOp && eLastOp < SC_OPCODE_STOP_UN_OP)) )
2782 SetError( FormulaError::OperatorExpected);
2783 // Operator and Plus => operator
2784 if (eOp == ocAdd && (eLastOp == ocOpen || eLastOp == ocSep ||
2785 (SC_OPCODE_START_BIN_OP <= eLastOp && eLastOp < SC_OPCODE_STOP_UN_OP)))
2787 FormulaCompilerRecursionGuard aRecursionGuard( nRecursion );
2788 eOp = NextToken();
2790 else
2792 // Before an operator there must not be another operator, with the
2793 // exception of AND and OR.
2794 if ( eOp != ocAnd && eOp != ocOr &&
2795 (SC_OPCODE_START_BIN_OP <= eOp && eOp < SC_OPCODE_STOP_BIN_OP )
2796 && (eLastOp == ocOpen || eLastOp == ocSep ||
2797 (SC_OPCODE_START_BIN_OP <= eLastOp && eLastOp < SC_OPCODE_STOP_UN_OP)))
2799 SetError( FormulaError::VariableExpected);
2800 if ( bAutoCorrect && !pStack )
2802 if ( eOp == eLastOp || eLastOp == ocOpen )
2803 { // throw away duplicated operator
2804 aCorrectedSymbol.clear();
2805 bCorrected = true;
2807 else
2809 sal_Int32 nPos = aCorrectedFormula.getLength();
2810 if ( nPos )
2812 nPos--;
2813 sal_Unicode c = aCorrectedFormula[ nPos ];
2814 switch ( eOp )
2815 { // swap operators
2816 case ocGreater:
2817 if ( c == mxSymbols->getSymbolChar( ocEqual) )
2818 { // >= instead of =>
2819 aCorrectedFormula = aCorrectedFormula.replaceAt( nPos, 1,
2820 rtl::OUStringChar( mxSymbols->getSymbolChar(ocGreater) ) );
2821 aCorrectedSymbol = OUString(c);
2822 bCorrected = true;
2824 break;
2825 case ocLess:
2826 if ( c == mxSymbols->getSymbolChar( ocEqual) )
2827 { // <= instead of =<
2828 aCorrectedFormula = aCorrectedFormula.replaceAt( nPos, 1,
2829 rtl::OUStringChar( mxSymbols->getSymbolChar(ocLess) ) );
2830 aCorrectedSymbol = OUString(c);
2831 bCorrected = true;
2833 else if ( c == mxSymbols->getSymbolChar( ocGreater) )
2834 { // <> instead of ><
2835 aCorrectedFormula = aCorrectedFormula.replaceAt( nPos, 1,
2836 rtl::OUStringChar( mxSymbols->getSymbolChar(ocLess) ) );
2837 aCorrectedSymbol = OUString(c);
2838 bCorrected = true;
2840 break;
2841 case ocMul:
2842 if ( c == mxSymbols->getSymbolChar( ocSub) )
2843 { // *- instead of -*
2844 aCorrectedFormula = aCorrectedFormula.replaceAt( nPos, 1,
2845 rtl::OUStringChar( mxSymbols->getSymbolChar(ocMul) ) );
2846 aCorrectedSymbol = OUString(c);
2847 bCorrected = true;
2849 break;
2850 case ocDiv:
2851 if ( c == mxSymbols->getSymbolChar( ocSub) )
2852 { // /- instead of -/
2853 aCorrectedFormula = aCorrectedFormula.replaceAt( nPos, 1,
2854 rtl::OUStringChar( mxSymbols->getSymbolChar(ocDiv) ) );
2855 aCorrectedSymbol = OUString(c);
2856 bCorrected = true;
2858 break;
2859 default:
2860 ; // nothing
2866 // Nasty, ugly, horrific, terrifying... significant whitespace...
2867 if (eOp == ocSpaces && FormulaGrammar::isExcelSyntax( meGrammar))
2869 // Fake an intersection op as last op for the next round, but at
2870 // least roughly check if it could make sense at all.
2871 FormulaToken* pPrev = maArrIterator.PeekPrevNoSpaces();
2872 if (pPrev && isPotentialRangeType( pPrev, false, false))
2874 FormulaToken* pNext = maArrIterator.PeekNextNoSpaces();
2875 if (pNext && isPotentialRangeType( pNext, false, true))
2876 eLastOp = ocIntersect;
2877 else
2878 eLastOp = eOp;
2880 else
2881 eLastOp = eOp;
2883 else
2884 eLastOp = eOp;
2886 return eOp;
2889 void FormulaCompiler::PutCode( FormulaTokenRef& p )
2891 if( pc >= FORMULA_MAXTOKENS - 1 )
2893 if ( pc == FORMULA_MAXTOKENS - 1 )
2895 SAL_WARN("formula.core", "FormulaCompiler::PutCode - CodeOverflow with OpCode " << +p->GetOpCode());
2896 p = new FormulaByteToken( ocStop );
2897 p->IncRef();
2898 *pCode++ = p.get();
2899 ++pc;
2901 SetError( FormulaError::CodeOverflow);
2902 return;
2904 if (pArr->GetCodeError() != FormulaError::NONE && mbJumpCommandReorder)
2905 return;
2906 ForceArrayOperator( p);
2907 p->IncRef();
2908 *pCode++ = p.get();
2909 pc++;
2913 bool FormulaCompiler::HandleExternalReference( const FormulaToken& /*_aToken*/)
2915 return true;
2918 bool FormulaCompiler::HandleRange()
2920 return true;
2923 bool FormulaCompiler::HandleColRowName()
2925 return true;
2928 bool FormulaCompiler::HandleDbData()
2930 return true;
2933 bool FormulaCompiler::HandleTableRef()
2935 return true;
2938 void FormulaCompiler::CreateStringFromSingleRef( OUStringBuffer& /*rBuffer*/, const FormulaToken* /*pToken*/) const
2942 void FormulaCompiler::CreateStringFromDoubleRef( OUStringBuffer& /*rBuffer*/, const FormulaToken* /*pToken*/) const
2946 void FormulaCompiler::CreateStringFromIndex( OUStringBuffer& /*rBuffer*/, const FormulaToken* /*pToken*/) const
2950 void FormulaCompiler::CreateStringFromMatrix( OUStringBuffer& /*rBuffer*/, const FormulaToken* /*pToken*/) const
2954 void FormulaCompiler::CreateStringFromExternal( OUStringBuffer& /*rBuffer*/, const FormulaToken* /*pToken*/) const
2958 void FormulaCompiler::LocalizeString( OUString& /*rName*/ ) const
2962 formula::ParamClass FormulaCompiler::GetForceArrayParameter( const FormulaToken* /*pToken*/, sal_uInt16 /*nParam*/ ) const
2964 return ParamClass::Unknown;
2967 void FormulaCompiler::ForceArrayOperator( FormulaTokenRef const & rCurr )
2969 if (pCurrentFactorToken.get() == rCurr.get())
2970 return;
2972 const OpCode eOp = rCurr->GetOpCode();
2973 const StackVar eType = rCurr->GetType();
2974 const bool bInlineArray = (eOp == ocPush && eType == svMatrix);
2976 if (!bInlineArray)
2978 if (rCurr->GetInForceArray() != ParamClass::Unknown)
2979 // Already set, unnecessary to evaluate again. This happens by calls to
2980 // CurrentFactor::operator=() while descending through Factor() and
2981 // then ascending back (and down and up, ...),
2982 // CheckSetForceArrayParameter() and later PutCode().
2983 return;
2985 if (!(eOp != ocPush && (eType == svByte || eType == svJump)))
2986 return;
2989 // Return class for inline arrays and functions returning array/matrix.
2990 // It's somewhat unclear what Excel actually does there and in
2991 // ECMA-376-1:2016 OOXML mentions "call to ... shall be an array formula"
2992 // only for FREQUENCY() and TRANSPOSE() but not for any other function
2993 // returning array/matrix or inline arrays, though for the latter has one
2994 // example in 18.17.2 Syntax:
2995 // "SUM(SQRT({1,2,3,4})) returns 6.14 when entered normally". However,
2996 // these need to be treated similar but not as ParamClass::ForceArray
2997 // (which would contradict the example in
2998 // https://bugs.documentfoundation.org/show_bug.cgi?id=122301#c19 and A6 of
2999 // https://bugs.documentfoundation.org/show_bug.cgi?id=133260#c10 ).
3000 // See also
3001 // commit d0ded163d8e93dc5b10d7a7c9bdab1d0a6a50bac
3002 // commit 5413c8871dec08eff19f514f5f391b946a45c86c
3003 constexpr ParamClass eArrayReturn = ParamClass::ForceArrayReturn;
3005 if (bInlineArray)
3007 // rCurr->SetInForceArray() can not be used with ocPush, but ocPush
3008 // with svMatrix has an implicit ParamClass::ForceArrayReturn.
3009 if (nCurrentFactorParam > 0 && pCurrentFactorToken
3010 && pCurrentFactorToken->GetInForceArray() == ParamClass::Unknown
3011 && GetForceArrayParameter( pCurrentFactorToken.get(), static_cast<sal_uInt16>(nCurrentFactorParam - 1))
3012 == ParamClass::Value)
3014 // Propagate to caller as if a function returning an array/matrix
3015 // was called (see also below).
3016 pCurrentFactorToken->SetInForceArray( eArrayReturn);
3018 return;
3021 if (!pCurrentFactorToken)
3023 if (mbMatrixFlag)
3025 // An array/matrix formula acts as ForceArray on all top level
3026 // operators and function calls, so that can be inherited properly
3027 // below.
3028 rCurr->SetInForceArray( ParamClass::ForceArray);
3030 else if (pc >= 2 && SC_OPCODE_START_BIN_OP <= eOp && eOp < SC_OPCODE_STOP_BIN_OP)
3032 // Binary operators are not functions followed by arguments
3033 // and need some peeking into RPN to inspect their operands.
3034 // Note that array context is not forced if only one
3035 // of the operands is an array like "={1;2}+A1:A2" returns #VALUE!
3036 // if entered in column A and not input in array mode, because it
3037 // involves a range reference with an implicit intersection. Check
3038 // both arguments are arrays, or the other is ocPush without ranges
3039 // for "={1;2}+3" or "={1;2}+A1".
3040 // Note this does not catch "={1;2}+ABS(A1)" that could be forced
3041 // to array, user still has to close in array mode.
3042 // The IsMatrixFunction() is only necessary because not all
3043 // functions returning matrix have ForceArrayReturn (yet?), see
3044 // OOXML comment above.
3046 const OpCode eOp1 = pCode[-1]->GetOpCode();
3047 const OpCode eOp2 = pCode[-2]->GetOpCode();
3048 const bool b1 = (pCode[-1]->GetInForceArray() != ParamClass::Unknown || IsMatrixFunction(eOp1));
3049 const bool b2 = (pCode[-2]->GetInForceArray() != ParamClass::Unknown || IsMatrixFunction(eOp2));
3050 if ((b1 && b2)
3051 || (b1 && eOp2 == ocPush && pCode[-2]->GetType() != svDoubleRef)
3052 || (b2 && eOp1 == ocPush && pCode[-1]->GetType() != svDoubleRef))
3054 rCurr->SetInForceArray( eArrayReturn);
3057 else if (pc >= 1 && SC_OPCODE_START_UN_OP <= eOp && eOp < SC_OPCODE_STOP_UN_OP)
3059 // Similar for unary operators.
3060 if (pCode[-1]->GetInForceArray() != ParamClass::Unknown || IsMatrixFunction(pCode[-1]->GetOpCode()))
3062 rCurr->SetInForceArray( eArrayReturn);
3065 return;
3068 // Inherited parameter class.
3069 const formula::ParamClass eForceType = pCurrentFactorToken->GetInForceArray();
3070 if (eForceType == ParamClass::ForceArray || eForceType == ParamClass::ReferenceOrRefArray)
3072 // ReferenceOrRefArray was set only if in ForceArray context already,
3073 // it is valid for the one function only to indicate the preferred
3074 // return type. Propagate as ForceArray if not another parameter
3075 // handling ReferenceOrRefArray.
3076 if (nCurrentFactorParam > 0
3077 && (GetForceArrayParameter( pCurrentFactorToken.get(), static_cast<sal_uInt16>(nCurrentFactorParam - 1))
3078 == ParamClass::ReferenceOrRefArray))
3079 rCurr->SetInForceArray( ParamClass::ReferenceOrRefArray);
3080 else
3081 rCurr->SetInForceArray( ParamClass::ForceArray);
3082 return;
3084 else if (eForceType == ParamClass::ReferenceOrForceArray)
3086 // Inherit further only if the return class of the nested function is
3087 // not Reference. Else flag as suppressed.
3088 if (GetForceArrayParameter( rCurr.get(), SAL_MAX_UINT16) != ParamClass::Reference)
3089 rCurr->SetInForceArray( eForceType);
3090 else
3091 rCurr->SetInForceArray( ParamClass::SuppressedReferenceOrForceArray);
3092 return;
3095 if (nCurrentFactorParam <= 0)
3096 return;
3098 // Actual current parameter's class.
3099 const formula::ParamClass eParamType = GetForceArrayParameter(
3100 pCurrentFactorToken.get(), static_cast<sal_uInt16>(nCurrentFactorParam - 1));
3101 if (eParamType == ParamClass::ForceArray)
3102 rCurr->SetInForceArray( eParamType);
3103 else if (eParamType == ParamClass::ReferenceOrForceArray)
3105 if (GetForceArrayParameter( rCurr.get(), SAL_MAX_UINT16) != ParamClass::Reference)
3106 rCurr->SetInForceArray( eParamType);
3107 else
3108 rCurr->SetInForceArray( formula::ParamClass::SuppressedReferenceOrForceArray);
3111 // Propagate a ForceArrayReturn to caller if the called function
3112 // returns one and the caller so far does not have a stronger array
3113 // mode set and expects a scalar value for this parameter.
3114 if (eParamType == ParamClass::Value && pCurrentFactorToken->GetInForceArray() == ParamClass::Unknown)
3116 if (IsMatrixFunction( eOp))
3117 pCurrentFactorToken->SetInForceArray( eArrayReturn);
3118 else if (GetForceArrayParameter( rCurr.get(), SAL_MAX_UINT16) == ParamClass::ForceArrayReturn)
3119 pCurrentFactorToken->SetInForceArray( ParamClass::ForceArrayReturn);
3123 void FormulaCompiler::CheckSetForceArrayParameter( FormulaTokenRef const & rCurr, sal_uInt8 nParam )
3125 if (!pCurrentFactorToken)
3126 return;
3128 nCurrentFactorParam = nParam + 1;
3130 ForceArrayOperator( rCurr);
3133 void FormulaCompiler::PushTokenArray( FormulaTokenArray* pa, bool bTemp )
3135 if ( bAutoCorrect && !pStack )
3136 { // don't merge stacked subroutine code into entered formula
3137 aCorrectedFormula += aCorrectedSymbol;
3138 aCorrectedSymbol.clear();
3140 FormulaArrayStack* p = new FormulaArrayStack;
3141 p->pNext = pStack;
3142 p->pArr = pArr;
3143 p->nIndex = maArrIterator.GetIndex();
3144 p->mpLastToken = mpLastToken;
3145 p->bTemp = bTemp;
3146 pStack = p;
3147 pArr = pa;
3148 maArrIterator = FormulaTokenArrayPlainIterator( *pArr );
3151 } // namespace formula
3153 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */