Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / formula / source / core / api / FormulaCompiler.cxx
blobdc9c73d7937d7aedc68dfdea19a808805e26540a
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/processfactory.hxx>
35 #include <com/sun/star/sheet/FormulaOpCodeMapEntry.hpp>
36 #include <com/sun/star/sheet/FormulaMapGroup.hpp>
37 #include <com/sun/star/sheet/FormulaMapGroupSpecialOffset.hpp>
38 #include <algorithm>
39 #include <mutex>
41 namespace formula
43 using namespace ::com::sun::star;
45 static const char* pInternal[2] = { "TTT", "__DEBUG_VAR" };
47 namespace {
49 class FormulaCompilerRecursionGuard
51 private:
52 short& rRecursion;
53 public:
54 explicit FormulaCompilerRecursionGuard( short& rRec )
55 : rRecursion( rRec ) { ++rRecursion; }
56 ~FormulaCompilerRecursionGuard() { --rRecursion; }
59 SvNumFormatType lcl_GetRetFormat( OpCode eOpCode )
61 switch (eOpCode)
63 case ocEqual:
64 case ocNotEqual:
65 case ocLess:
66 case ocGreater:
67 case ocLessEqual:
68 case ocGreaterEqual:
69 case ocAnd:
70 case ocOr:
71 case ocXor:
72 case ocNot:
73 case ocTrue:
74 case ocFalse:
75 case ocIsEmpty:
76 case ocIsString:
77 case ocIsNonString:
78 case ocIsLogical:
79 case ocIsRef:
80 case ocIsValue:
81 case ocIsFormula:
82 case ocIsNA:
83 case ocIsErr:
84 case ocIsError:
85 case ocIsEven:
86 case ocIsOdd:
87 case ocExact:
88 return SvNumFormatType::LOGICAL;
89 case ocGetActDate:
90 case ocGetDate:
91 case ocEasterSunday :
92 return SvNumFormatType::DATE;
93 case ocGetActTime:
94 return SvNumFormatType::DATETIME;
95 case ocGetTime:
96 return SvNumFormatType::TIME;
97 case ocNPV:
98 case ocPV:
99 case ocSYD:
100 case ocDDB:
101 case ocDB:
102 case ocVBD:
103 case ocSLN:
104 case ocPMT:
105 case ocFV:
106 case ocIpmt:
107 case ocPpmt:
108 case ocCumIpmt:
109 case ocCumPrinc:
110 return SvNumFormatType::CURRENCY;
111 case ocRate:
112 case ocIRR:
113 case ocMIRR:
114 case ocRRI:
115 case ocEffect:
116 case ocNominal:
117 case ocPercentSign:
118 return SvNumFormatType::PERCENT;
119 default:
120 return SvNumFormatType::NUMBER;
124 void lclPushOpCodeMapEntry( ::std::vector< sheet::FormulaOpCodeMapEntry >& rVec,
125 const OUString* pTable, sal_uInt16 nOpCode )
127 sheet::FormulaOpCodeMapEntry aEntry;
128 aEntry.Token.OpCode = nOpCode;
129 aEntry.Name = pTable[nOpCode];
130 rVec.push_back( aEntry);
133 void lclPushOpCodeMapEntries( ::std::vector< sheet::FormulaOpCodeMapEntry >& rVec,
134 const OUString* pTable, sal_uInt16 nOpCodeBeg, sal_uInt16 nOpCodeEnd )
136 for (sal_uInt16 nOpCode = nOpCodeBeg; nOpCode < nOpCodeEnd; ++nOpCode)
137 lclPushOpCodeMapEntry( rVec, pTable, nOpCode );
140 void lclPushOpCodeMapEntries( ::std::vector< sheet::FormulaOpCodeMapEntry >& rVec,
141 const OUString* pTable, const sal_uInt16* pnOpCodes, size_t nCount )
143 for (const sal_uInt16* pnEnd = pnOpCodes + nCount; pnOpCodes < pnEnd; ++pnOpCodes)
144 lclPushOpCodeMapEntry( rVec, pTable, *pnOpCodes );
147 CharClass* createCharClassIfNonEnglishUI()
149 const LanguageTag& rLanguageTag( Application::GetSettings().GetUILanguageTag());
150 if (rLanguageTag.getLanguage() == "en")
151 return nullptr;
152 return new CharClass( ::comphelper::getProcessComponentContext(), rLanguageTag);
155 class OpCodeList
157 public:
159 OpCodeList(const std::pair<const char*, int>* pSymbols, const FormulaCompiler::NonConstOpCodeMapPtr&,
160 FormulaCompiler::SeparatorType = FormulaCompiler::SeparatorType::SEMICOLON_BASE );
161 OpCodeList(const std::pair<TranslateId, int>* pSymbols, const FormulaCompiler::NonConstOpCodeMapPtr&,
162 FormulaCompiler::SeparatorType = FormulaCompiler::SeparatorType::SEMICOLON_BASE );
164 private:
165 bool getOpCodeString( OUString& rStr, sal_uInt16 nOp );
166 void putDefaultOpCode( const FormulaCompiler::NonConstOpCodeMapPtr& xMap, sal_uInt16 nOp, const CharClass* pCharClass );
168 private:
169 FormulaCompiler::SeparatorType meSepType;
170 const std::pair<const char*, int>* mpSymbols1;
171 const std::pair<TranslateId, int>* mpSymbols2;
174 OpCodeList::OpCodeList(const std::pair<const char*, int>* pSymbols, const FormulaCompiler::NonConstOpCodeMapPtr& xMap,
175 FormulaCompiler::SeparatorType eSepType)
176 : meSepType(eSepType)
177 , mpSymbols1(pSymbols)
178 , mpSymbols2(nullptr)
180 std::unique_ptr<CharClass> xCharClass( xMap->isEnglish() ? nullptr : createCharClassIfNonEnglishUI());
181 const CharClass* pCharClass = xCharClass.get();
182 if (meSepType == FormulaCompiler::SeparatorType::RESOURCE_BASE)
184 for (sal_uInt16 i = 0; i <= SC_OPCODE_LAST_OPCODE_ID; ++i)
186 putDefaultOpCode( xMap, i, pCharClass);
189 else
191 for (sal_uInt16 i = 0; i <= SC_OPCODE_LAST_OPCODE_ID; ++i)
193 OUString aOpStr;
194 if ( getOpCodeString( aOpStr, i) )
195 xMap->putOpCode( aOpStr, OpCode(i), pCharClass);
196 else
197 putDefaultOpCode( xMap, i, pCharClass);
202 OpCodeList::OpCodeList(const std::pair<TranslateId, int>* pSymbols, const FormulaCompiler::NonConstOpCodeMapPtr& xMap,
203 FormulaCompiler::SeparatorType eSepType)
204 : meSepType(eSepType)
205 , mpSymbols1(nullptr)
206 , mpSymbols2(pSymbols)
208 std::unique_ptr<CharClass> xCharClass( xMap->isEnglish() ? nullptr : createCharClassIfNonEnglishUI());
209 const CharClass* pCharClass = xCharClass.get();
210 if (meSepType == FormulaCompiler::SeparatorType::RESOURCE_BASE)
212 for (sal_uInt16 i = 0; i <= SC_OPCODE_LAST_OPCODE_ID; ++i)
214 putDefaultOpCode( xMap, i, pCharClass);
217 else
219 for (sal_uInt16 i = 0; i <= SC_OPCODE_LAST_OPCODE_ID; ++i)
221 OUString aOpStr;
222 if ( getOpCodeString( aOpStr, i) )
223 xMap->putOpCode( aOpStr, OpCode(i), pCharClass);
224 else
225 putDefaultOpCode( xMap, i, pCharClass);
230 bool OpCodeList::getOpCodeString( OUString& rStr, sal_uInt16 nOp )
232 switch (nOp)
234 case SC_OPCODE_SEP:
236 if (meSepType == FormulaCompiler::SeparatorType::SEMICOLON_BASE)
238 rStr = ";";
239 return true;
242 break;
243 case SC_OPCODE_ARRAY_COL_SEP:
245 if (meSepType == FormulaCompiler::SeparatorType::SEMICOLON_BASE)
247 rStr = ";";
248 return true;
251 break;
252 case SC_OPCODE_ARRAY_ROW_SEP:
254 if (meSepType == FormulaCompiler::SeparatorType::SEMICOLON_BASE)
256 rStr = "|";
257 return true;
260 break;
263 return false;
266 void OpCodeList::putDefaultOpCode( const FormulaCompiler::NonConstOpCodeMapPtr& xMap, sal_uInt16 nOp,
267 const CharClass* pCharClass )
269 OUString sKey;
270 if (mpSymbols1)
272 const char* pKey = nullptr;
273 for (const std::pair<const char*, int>* pSymbol = mpSymbols1; pSymbol->first; ++pSymbol)
275 if (nOp == pSymbol->second)
277 pKey = pSymbol->first;
278 break;
281 if (!pKey)
282 return;
283 sKey = OUString::createFromAscii(pKey);
285 else if (mpSymbols2)
287 TranslateId pKey;
288 for (const std::pair<TranslateId, int>* pSymbol = mpSymbols2; pSymbol->first; ++pSymbol)
290 if (nOp == pSymbol->second)
292 pKey = pSymbol->first;
293 break;
296 if (!pKey)
297 return;
298 sKey = ForResId(pKey);
300 xMap->putOpCode(sKey, OpCode(nOp), pCharClass);
303 // static
304 const sal_Unicode* lcl_UnicodeStrChr( const sal_Unicode* pStr, sal_Unicode c )
306 if ( !pStr )
307 return nullptr;
308 while ( *pStr )
310 if ( *pStr == c )
311 return pStr;
312 pStr++;
314 return nullptr;
317 struct OpCodeMapData
319 FormulaCompiler::NonConstOpCodeMapPtr mxSymbolMap;
320 std::mutex maMtx;
324 bool isPotentialRangeLeftOp( OpCode eOp )
326 switch (eOp)
328 case ocClose:
329 return true;
330 default:
331 return false;
335 bool isRangeResultFunction( OpCode eOp )
337 switch (eOp)
339 case ocIndirect:
340 case ocOffset:
341 return true;
342 default:
343 return false;
347 bool isRangeResultOpCode( OpCode eOp )
349 switch (eOp)
351 case ocRange:
352 case ocUnion:
353 case ocIntersect:
354 case ocIndirect:
355 case ocOffset:
356 return true;
357 default:
358 return false;
363 @param pToken
364 MUST be a valid token, caller has to ensure.
366 @param bRight
367 If bRPN==false, bRight==false means opcodes for left side are
368 checked, bRight==true means opcodes for right side. If bRPN==true
369 it doesn't matter except for the ocSep converted to ocUnion case.
371 bool isPotentialRangeType( FormulaToken const * pToken, bool bRPN, bool bRight )
373 switch (pToken->GetType())
375 case svByte: // could be range result, but only a few
376 if (bRPN)
377 return isRangeResultOpCode( pToken->GetOpCode());
378 else if (bRight)
379 return isRangeResultFunction( pToken->GetOpCode());
380 else
381 return isPotentialRangeLeftOp( pToken->GetOpCode());
382 case svSingleRef:
383 case svDoubleRef:
384 case svIndex: // could be range
385 //case svRefList: // um..what?
386 case svExternalSingleRef:
387 case svExternalDoubleRef:
388 case svExternalName: // could be range
389 return true;
390 case svSep:
391 // A special case if a previous ocSep was converted to ocUnion it
392 // stays svSep instead of svByte.
393 return bRPN && !bRight && pToken->GetOpCode() == ocUnion;
394 default:
395 // Separators are not part of RPN and right opcodes need to be
396 // other StackVar types or functions and thus svByte.
397 return !bRPN && !bRight && isPotentialRangeLeftOp( pToken->GetOpCode());
401 bool isIntersectable( FormulaToken** pCode1, FormulaToken** pCode2 )
403 FormulaToken* pToken1 = *pCode1;
404 FormulaToken* pToken2 = *pCode2;
405 if (pToken1 && pToken2)
406 return isPotentialRangeType( pToken1, true, false) && isPotentialRangeType( pToken2, true, true);
407 return false;
410 bool isAdjacentRpnEnd( sal_uInt16 nPC,
411 FormulaToken const * const * const pCode,
412 FormulaToken const * const * const pCode1,
413 FormulaToken const * const * const pCode2 )
415 return nPC >= 2 && pCode1 && pCode2 &&
416 (pCode2 - pCode1 == 1) && (pCode - pCode2 == 1) &&
417 (*pCode1 != nullptr) && (*pCode2 != nullptr);
420 bool isAdjacentOrGapRpnEnd( sal_uInt16 nPC,
421 FormulaToken const * const * const pCode,
422 FormulaToken const * const * const pCode1,
423 FormulaToken const * const * const pCode2 )
425 return nPC >= 2 && pCode1 && pCode2 &&
426 (pCode2 > pCode1) && (pCode - pCode2 == 1) &&
427 (*pCode1 != nullptr) && (*pCode2 != nullptr);
431 } // namespace
434 void FormulaCompiler::OpCodeMap::putExternal( const OUString & rSymbol, const OUString & rAddIn )
436 // Different symbols may map to the same AddIn, but the same AddIn may not
437 // map to different symbols, the first pair wins. Same symbol of course may
438 // not map to different AddIns, again the first pair wins and also the
439 // AddIn->symbol mapping is not inserted in other cases.
440 bool bOk = maExternalHashMap.emplace(rSymbol, rAddIn).second;
441 SAL_WARN_IF( !bOk, "formula.core", "OpCodeMap::putExternal: symbol not inserted, " << rSymbol << " -> " << rAddIn);
442 if (bOk)
444 bOk = maReverseExternalHashMap.emplace(rAddIn, rSymbol).second;
445 // Failed insertion of the AddIn is ok for different symbols mapping to
446 // the same AddIn. Make this INFO only.
447 SAL_INFO_IF( !bOk, "formula.core", "OpCodeMap::putExternal: AddIn not inserted, " << rAddIn << " -> " << rSymbol);
451 void FormulaCompiler::OpCodeMap::putExternalSoftly( const OUString & rSymbol, const OUString & rAddIn )
453 // Same as putExternal() but no warning, instead info whether inserted or not.
454 bool bOk = maExternalHashMap.emplace(rSymbol, rAddIn).second;
455 SAL_INFO( "formula.core", "OpCodeMap::putExternalSoftly: symbol " << (bOk ? "" : "not ") << "inserted, " << rSymbol << " -> " << rAddIn);
456 if (bOk)
458 bOk = maReverseExternalHashMap.emplace(rAddIn, rSymbol).second;
459 SAL_INFO_IF( !bOk, "formula.core", "OpCodeMap::putExternalSoftly: AddIn not inserted, " << rAddIn << " -> " << rSymbol);
463 uno::Sequence< sheet::FormulaToken > FormulaCompiler::OpCodeMap::createSequenceOfFormulaTokens(
464 const FormulaCompiler& rCompiler, const uno::Sequence< OUString >& rNames ) const
466 const sal_Int32 nLen = rNames.getLength();
467 uno::Sequence< sheet::FormulaToken > aTokens( nLen);
468 sheet::FormulaToken* pToken = aTokens.getArray();
469 OUString const * pName = rNames.getConstArray();
470 OUString const * const pStop = pName + nLen;
471 for ( ; pName < pStop; ++pName, ++pToken)
473 OpCodeHashMap::const_iterator iLook( maHashMap.find( *pName));
474 if (iLook != maHashMap.end())
475 pToken->OpCode = (*iLook).second;
476 else
478 OUString aIntName;
479 if (hasExternals())
481 ExternalHashMap::const_iterator iExt( maExternalHashMap.find( *pName));
482 if (iExt != maExternalHashMap.end())
483 aIntName = (*iExt).second;
484 // Check for existence not needed here, only name-mapping is of
485 // interest.
487 if (aIntName.isEmpty())
488 aIntName = rCompiler.FindAddInFunction(*pName, !isEnglish()); // bLocalFirst=false for english
489 if (aIntName.isEmpty())
490 pToken->OpCode = getOpCodeUnknown();
491 else
493 pToken->OpCode = ocExternal;
494 pToken->Data <<= aIntName;
498 return aTokens;
501 uno::Sequence< sheet::FormulaOpCodeMapEntry > FormulaCompiler::OpCodeMap::createSequenceOfAvailableMappings(
502 const FormulaCompiler& rCompiler, const sal_Int32 nGroups ) const
504 using namespace sheet;
506 // Unfortunately uno::Sequence can't grow without cumbersome reallocs. As
507 // we don't know in advance how many elements it will have we use a
508 // temporary vector to add elements and then copy to Sequence :-(
509 ::std::vector< FormulaOpCodeMapEntry > aVec;
511 if (nGroups == FormulaMapGroup::SPECIAL)
513 // Use specific order, keep in sync with
514 // offapi/com/sun/star/sheet/FormulaMapGroupSpecialOffset.idl
515 static const struct
517 sal_Int32 nOff;
518 OpCode eOp;
519 } aMap[] = {
520 { FormulaMapGroupSpecialOffset::PUSH , ocPush } ,
521 { FormulaMapGroupSpecialOffset::CALL , ocCall } ,
522 { FormulaMapGroupSpecialOffset::STOP , ocStop } ,
523 { FormulaMapGroupSpecialOffset::EXTERNAL , ocExternal } ,
524 { FormulaMapGroupSpecialOffset::NAME , ocName } ,
525 { FormulaMapGroupSpecialOffset::NO_NAME , ocNoName } ,
526 { FormulaMapGroupSpecialOffset::MISSING , ocMissing } ,
527 { FormulaMapGroupSpecialOffset::BAD , ocBad } ,
528 { FormulaMapGroupSpecialOffset::SPACES , ocSpaces } ,
529 { FormulaMapGroupSpecialOffset::MAT_REF , ocMatRef } ,
530 { FormulaMapGroupSpecialOffset::DB_AREA , ocDBArea } ,
531 /* TODO: { FormulaMapGroupSpecialOffset::TABLE_REF , ocTableRef } , */
532 { FormulaMapGroupSpecialOffset::MACRO , ocMacro } ,
533 { FormulaMapGroupSpecialOffset::COL_ROW_NAME , ocColRowName } ,
534 { FormulaMapGroupSpecialOffset::WHITESPACE , ocWhitespace }
536 const size_t nCount = SAL_N_ELEMENTS(aMap);
537 // Preallocate vector elements.
538 FormulaOpCodeMapEntry aEntry;
539 aEntry.Token.OpCode = getOpCodeUnknown();
540 aVec.resize(nCount, aEntry);
542 for (auto& i : aMap)
544 size_t nIndex = static_cast< size_t >( i.nOff );
545 if (aVec.size() <= nIndex)
547 // The offsets really should be aligned with the size, so if
548 // the vector was preallocated above this code to resize it is
549 // just a measure in case the table isn't in sync with the API,
550 // usually it isn't executed.
551 aEntry.Token.OpCode = getOpCodeUnknown();
552 aVec.resize( nIndex + 1, aEntry );
554 aEntry.Token.OpCode = i.eOp;
555 aVec[nIndex] = aEntry;
558 else
560 /* FIXME: Once we support error constants in formulas we'll need a map
561 * group for that, e.g. FormulaMapGroup::ERROR_CONSTANTS, and fill
562 * SC_OPCODE_START_ERRORS to SC_OPCODE_STOP_ERRORS. */
564 // Anything else but SPECIAL.
565 if ((nGroups & FormulaMapGroup::SEPARATORS) != 0)
567 static const sal_uInt16 aOpCodes[] = {
568 SC_OPCODE_OPEN,
569 SC_OPCODE_CLOSE,
570 SC_OPCODE_SEP,
572 lclPushOpCodeMapEntries( aVec, mpTable.get(), aOpCodes, SAL_N_ELEMENTS(aOpCodes) );
574 if ((nGroups & FormulaMapGroup::ARRAY_SEPARATORS) != 0)
576 static const sal_uInt16 aOpCodes[] = {
577 SC_OPCODE_ARRAY_OPEN,
578 SC_OPCODE_ARRAY_CLOSE,
579 SC_OPCODE_ARRAY_ROW_SEP,
580 SC_OPCODE_ARRAY_COL_SEP
582 lclPushOpCodeMapEntries( aVec, mpTable.get(), aOpCodes, SAL_N_ELEMENTS(aOpCodes) );
584 if ((nGroups & FormulaMapGroup::UNARY_OPERATORS) != 0)
586 // Due to the nature of the percent operator following its operand
587 // it isn't sorted into unary operators for compiler interna.
588 lclPushOpCodeMapEntry( aVec, mpTable.get(), ocPercentSign );
589 // "+" can be used as unary operator too, push only if binary group is not set
590 if ((nGroups & FormulaMapGroup::BINARY_OPERATORS) == 0)
591 lclPushOpCodeMapEntry( aVec, mpTable.get(), ocAdd );
592 // regular unary operators
593 for (sal_uInt16 nOp = SC_OPCODE_START_UN_OP; nOp < SC_OPCODE_STOP_UN_OP && nOp < mnSymbols; ++nOp)
595 lclPushOpCodeMapEntry( aVec, mpTable.get(), nOp );
598 if ((nGroups & FormulaMapGroup::BINARY_OPERATORS) != 0)
600 for (sal_uInt16 nOp = SC_OPCODE_START_BIN_OP; nOp < SC_OPCODE_STOP_BIN_OP && nOp < mnSymbols; ++nOp)
602 switch (nOp)
604 // AND and OR in fact are functions but for legacy reasons
605 // are sorted into binary operators for compiler interna.
606 case SC_OPCODE_AND :
607 case SC_OPCODE_OR :
608 break; // nothing,
609 default:
610 lclPushOpCodeMapEntry( aVec, mpTable.get(), nOp );
614 if ((nGroups & FormulaMapGroup::FUNCTIONS) != 0)
616 // Function names are not consecutive, skip the gaps between
617 // functions with no parameter, functions with 1 parameter
618 lclPushOpCodeMapEntries( aVec, mpTable.get(), SC_OPCODE_START_NO_PAR,
619 ::std::min< sal_uInt16 >( SC_OPCODE_STOP_NO_PAR, mnSymbols ) );
620 lclPushOpCodeMapEntries( aVec, mpTable.get(), SC_OPCODE_START_1_PAR,
621 ::std::min< sal_uInt16 >( SC_OPCODE_STOP_1_PAR, mnSymbols ) );
622 // Additional functions not within range of functions.
623 static const sal_uInt16 aOpCodes[] = {
624 SC_OPCODE_IF,
625 SC_OPCODE_IF_ERROR,
626 SC_OPCODE_IF_NA,
627 SC_OPCODE_CHOOSE,
628 SC_OPCODE_AND,
629 SC_OPCODE_OR
631 lclPushOpCodeMapEntries( aVec, mpTable.get(), aOpCodes, SAL_N_ELEMENTS(aOpCodes) );
632 // functions with 2 or more parameters.
633 for (sal_uInt16 nOp = SC_OPCODE_START_2_PAR; nOp < SC_OPCODE_STOP_2_PAR && nOp < mnSymbols; ++nOp)
635 switch (nOp)
637 // NO_NAME is in SPECIAL.
638 case SC_OPCODE_NO_NAME :
639 break; // nothing,
640 default:
641 lclPushOpCodeMapEntry( aVec, mpTable.get(), nOp );
644 // If AddIn functions are present in this mapping, use them, and only those.
645 if (hasExternals())
647 for (auto const& elem : maExternalHashMap)
649 FormulaOpCodeMapEntry aEntry;
650 aEntry.Name = elem.first;
651 aEntry.Token.Data <<= elem.second;
652 aEntry.Token.OpCode = ocExternal;
653 aVec.push_back( aEntry);
656 else
658 rCompiler.fillAddInToken( aVec, isEnglish());
662 return uno::Sequence< FormulaOpCodeMapEntry >(aVec.data(), aVec.size());
666 void FormulaCompiler::OpCodeMap::putOpCode( const OUString & rStr, const OpCode eOp, const CharClass* pCharClass )
668 if (0 < eOp && sal_uInt16(eOp) < mnSymbols)
670 bool bPutOp = mpTable[eOp].isEmpty();
671 bool bRemoveFromMap = false;
672 if (!bPutOp)
674 switch (eOp)
676 // These OpCodes are meant to overwrite and also remove an
677 // existing mapping.
678 case ocCurrency:
679 bPutOp = true;
680 bRemoveFromMap = true;
681 break;
682 // These separator OpCodes are meant to overwrite and also
683 // remove an existing mapping if it is not used for one of the
684 // other separators.
685 case ocArrayColSep:
686 bPutOp = true;
687 bRemoveFromMap = (mpTable[ocArrayRowSep] != mpTable[eOp] && mpTable[ocSep] != mpTable[eOp]);
688 break;
689 case ocArrayRowSep:
690 bPutOp = true;
691 bRemoveFromMap = (mpTable[ocArrayColSep] != mpTable[eOp] && mpTable[ocSep] != mpTable[eOp]);
692 break;
693 // For ocSep keep the ";" in map but remove any other if it is
694 // not used for ocArrayColSep or ocArrayRowSep.
695 case ocSep:
696 bPutOp = true;
697 bRemoveFromMap = (mpTable[eOp] != ";" &&
698 mpTable[ocArrayColSep] != mpTable[eOp] &&
699 mpTable[ocArrayRowSep] != mpTable[eOp]);
700 break;
701 // These OpCodes are known to be duplicates in the Excel
702 // external API mapping because of different parameter counts
703 // in different BIFF versions. Names are identical and entries
704 // are ignored.
705 case ocLinest:
706 case ocTrend:
707 case ocLogest:
708 case ocGrowth:
709 case ocTrunc:
710 case ocFixed:
711 case ocGetDayOfWeek:
712 case ocHLookup:
713 case ocVLookup:
714 case ocGetDiffDate360:
715 if (rStr == mpTable[eOp])
716 return;
717 [[fallthrough]];
718 // These OpCodes are known to be added to an existing mapping,
719 // but only for the OOXML external API mapping. This is *not*
720 // FormulaLanguage::OOXML. Keep the first
721 // (correct) definition for the OpCode, all following are
722 // additional alias entries in the map.
723 case ocErrorType:
724 case ocMultiArea:
725 case ocBackSolver:
726 case ocEasterSunday:
727 case ocCurrent:
728 case ocStyle:
729 if (mbEnglish &&
730 FormulaGrammar::extractFormulaLanguage( meGrammar) == FormulaGrammar::GRAM_EXTERNAL)
732 // Both bPutOp and bRemoveFromMap stay false.
733 break;
735 [[fallthrough]];
736 default:
737 SAL_WARN("formula.core",
738 "OpCodeMap::putOpCode: reusing OpCode " << static_cast<sal_uInt16>(eOp)
739 << ", replacing '" << mpTable[eOp] << "' with '" << rStr << "' in "
740 << (mbEnglish ? "" : "non-") << "English map 0x" << ::std::hex << meGrammar);
744 // Case preserving opcode -> string, upper string -> opcode
745 if (bRemoveFromMap)
747 OUString aUpper( pCharClass ? pCharClass->uppercase( mpTable[eOp]) : rStr.toAsciiUpperCase());
748 // Ensure we remove a mapping only for the requested OpCode.
749 OpCodeHashMap::const_iterator it( maHashMap.find( aUpper));
750 if (it != maHashMap.end() && (*it).second == eOp)
751 maHashMap.erase( it);
753 if (bPutOp)
754 mpTable[eOp] = rStr;
755 OUString aUpper( pCharClass ? pCharClass->uppercase( rStr) : rStr.toAsciiUpperCase());
756 maHashMap.emplace(aUpper, eOp);
758 else
760 SAL_WARN( "formula.core", "OpCodeMap::putOpCode: OpCode out of range");
765 FormulaCompiler::FormulaCompiler( FormulaTokenArray& rArr, bool bComputeII, bool bMatrixFlag )
767 nCurrentFactorParam(0),
768 pArr( &rArr ),
769 maArrIterator( rArr ),
770 pCode( nullptr ),
771 pStack( nullptr ),
772 eLastOp( ocPush ),
773 nRecursion( 0 ),
774 nNumFmt( SvNumFormatType::UNDEFINED ),
775 pc( 0 ),
776 meGrammar( formula::FormulaGrammar::GRAM_UNSPECIFIED ),
777 bAutoCorrect( false ),
778 bCorrected( false ),
779 glSubTotal( false ),
780 needsRPNTokenCheck( false ),
781 mbJumpCommandReorder(true),
782 mbStopOnError(true),
783 mbComputeII(bComputeII),
784 mbMatrixFlag(bMatrixFlag)
788 FormulaTokenArray FormulaCompiler::smDummyTokenArray;
790 FormulaCompiler::FormulaCompiler(bool bComputeII, bool bMatrixFlag)
792 nCurrentFactorParam(0),
793 pArr( nullptr ),
794 maArrIterator( smDummyTokenArray ),
795 pCode( nullptr ),
796 pStack( nullptr ),
797 eLastOp( ocPush ),
798 nRecursion(0),
799 nNumFmt( SvNumFormatType::UNDEFINED ),
800 pc( 0 ),
801 meGrammar( formula::FormulaGrammar::GRAM_UNSPECIFIED ),
802 bAutoCorrect( false ),
803 bCorrected( false ),
804 glSubTotal( false ),
805 needsRPNTokenCheck( false ),
806 mbJumpCommandReorder(true),
807 mbStopOnError(true),
808 mbComputeII(bComputeII),
809 mbMatrixFlag(bMatrixFlag)
813 FormulaCompiler::~FormulaCompiler()
817 FormulaCompiler::OpCodeMapPtr FormulaCompiler::GetOpCodeMap( const sal_Int32 nLanguage ) const
819 const bool bTemporary = !HasOpCodeMap(nLanguage);
820 OpCodeMapPtr xMap = GetFinalOpCodeMap(nLanguage);
821 if (bTemporary)
822 const_cast<FormulaCompiler*>(this)->DestroyOpCodeMap(nLanguage);
823 return xMap;
826 FormulaCompiler::OpCodeMapPtr FormulaCompiler::GetFinalOpCodeMap( const sal_Int32 nLanguage ) const
828 FormulaCompiler::OpCodeMapPtr xMap;
829 using namespace sheet;
830 switch (nLanguage)
832 case FormulaLanguage::ODFF :
833 if (!mxSymbolsODFF)
834 InitSymbolsODFF( InitSymbols::INIT);
835 xMap = mxSymbolsODFF;
836 break;
837 case FormulaLanguage::ODF_11 :
838 if (!mxSymbolsPODF)
839 InitSymbolsPODF( InitSymbols::INIT);
840 xMap = mxSymbolsPODF;
841 break;
842 case FormulaLanguage::ENGLISH :
843 if (!mxSymbolsEnglish)
844 InitSymbolsEnglish( InitSymbols::INIT);
845 xMap = mxSymbolsEnglish;
846 break;
847 case FormulaLanguage::NATIVE :
848 if (!mxSymbolsNative)
849 InitSymbolsNative( InitSymbols::INIT);
850 xMap = mxSymbolsNative;
851 break;
852 case FormulaLanguage::XL_ENGLISH:
853 if (!mxSymbolsEnglishXL)
854 InitSymbolsEnglishXL( InitSymbols::INIT);
855 xMap = mxSymbolsEnglishXL;
856 break;
857 case FormulaLanguage::OOXML:
858 if (!mxSymbolsOOXML)
859 InitSymbolsOOXML( InitSymbols::INIT);
860 xMap = mxSymbolsOOXML;
861 break;
862 case FormulaLanguage::API :
863 if (!mxSymbolsAPI)
864 InitSymbolsAPI( InitSymbols::INIT);
865 xMap = mxSymbolsAPI;
866 break;
867 default:
868 ; // nothing, NULL map returned
870 return xMap;
873 void FormulaCompiler::DestroyOpCodeMap( const sal_Int32 nLanguage )
875 using namespace sheet;
876 switch (nLanguage)
878 case FormulaLanguage::ODFF :
879 InitSymbolsODFF( InitSymbols::DESTROY);
880 break;
881 case FormulaLanguage::ODF_11 :
882 InitSymbolsPODF( InitSymbols::DESTROY);
883 break;
884 case FormulaLanguage::ENGLISH :
885 InitSymbolsEnglish( InitSymbols::DESTROY);
886 break;
887 case FormulaLanguage::NATIVE :
888 InitSymbolsNative( InitSymbols::DESTROY);
889 break;
890 case FormulaLanguage::XL_ENGLISH:
891 InitSymbolsEnglishXL( InitSymbols::DESTROY);
892 break;
893 case FormulaLanguage::OOXML:
894 InitSymbolsOOXML( InitSymbols::DESTROY);
895 break;
896 case FormulaLanguage::API :
897 InitSymbolsAPI( InitSymbols::DESTROY);
898 break;
899 default:
900 ; // nothing
904 bool FormulaCompiler::HasOpCodeMap( const sal_Int32 nLanguage ) const
906 using namespace sheet;
907 switch (nLanguage)
909 case FormulaLanguage::ODFF :
910 return InitSymbolsODFF( InitSymbols::ASK);
911 case FormulaLanguage::ODF_11 :
912 return InitSymbolsPODF( InitSymbols::ASK);
913 case FormulaLanguage::ENGLISH :
914 return InitSymbolsEnglish( InitSymbols::ASK);
915 case FormulaLanguage::NATIVE :
916 return InitSymbolsNative( InitSymbols::ASK);
917 case FormulaLanguage::XL_ENGLISH:
918 return InitSymbolsEnglishXL( InitSymbols::ASK);
919 case FormulaLanguage::OOXML:
920 return InitSymbolsOOXML( InitSymbols::ASK);
921 case FormulaLanguage::API :
922 return InitSymbolsAPI( InitSymbols::ASK);
923 default:
924 ; // nothing
926 return false;
929 OUString FormulaCompiler::FindAddInFunction( const OUString& /*rUpperName*/, bool /*bLocalFirst*/ ) const
931 return OUString();
934 FormulaCompiler::OpCodeMapPtr FormulaCompiler::CreateOpCodeMap(
935 const uno::Sequence<
936 const sheet::FormulaOpCodeMapEntry > & rMapping,
937 bool bEnglish )
939 using sheet::FormulaOpCodeMapEntry;
940 // Filter / API maps are never Core
941 NonConstOpCodeMapPtr xMap = std::make_shared<OpCodeMap>( SC_OPCODE_LAST_OPCODE_ID + 1, false,
942 FormulaGrammar::mergeToGrammar( FormulaGrammar::setEnglishBit(
943 FormulaGrammar::GRAM_EXTERNAL, bEnglish), FormulaGrammar::CONV_UNSPECIFIED));
944 std::unique_ptr<CharClass> xCharClass( xMap->isEnglish() ? nullptr : createCharClassIfNonEnglishUI());
945 const CharClass* pCharClass = xCharClass.get();
946 for (auto const& rMapEntry : rMapping)
948 OpCode eOp = OpCode(rMapEntry.Token.OpCode);
949 if (eOp != ocExternal)
950 xMap->putOpCode( rMapEntry.Name, eOp, pCharClass);
951 else
953 OUString aExternalName;
954 if (rMapEntry.Token.Data >>= aExternalName)
955 xMap->putExternal( rMapEntry.Name, aExternalName);
956 else
958 SAL_WARN( "formula.core", "FormulaCompiler::CreateOpCodeMap: no Token.Data external name");
962 return xMap;
965 static bool lcl_fillNativeSymbols( FormulaCompiler::NonConstOpCodeMapPtr& xMap, FormulaCompiler::InitSymbols eWhat = FormulaCompiler::InitSymbols::INIT )
967 static OpCodeMapData aSymbolMap;
968 std::unique_lock aGuard(aSymbolMap.maMtx);
970 if (eWhat == FormulaCompiler::InitSymbols::ASK)
972 return bool(aSymbolMap.mxSymbolMap);
974 else if (eWhat == FormulaCompiler::InitSymbols::DESTROY)
976 aSymbolMap.mxSymbolMap.reset();
978 else if (!aSymbolMap.mxSymbolMap)
980 // Core
981 aSymbolMap.mxSymbolMap =
982 std::make_shared<FormulaCompiler::OpCodeMap>(
983 SC_OPCODE_LAST_OPCODE_ID + 1, true, FormulaGrammar::GRAM_NATIVE_UI);
984 OpCodeList aOpCodeListSymbols(RID_STRLIST_FUNCTION_NAMES_SYMBOLS, aSymbolMap.mxSymbolMap);
985 OpCodeList aOpCodeListNative(RID_STRLIST_FUNCTION_NAMES, aSymbolMap.mxSymbolMap);
986 // No AddInMap for native core mapping.
989 xMap = aSymbolMap.mxSymbolMap;
991 return true;
994 const OUString& FormulaCompiler::GetNativeSymbol( OpCode eOp )
996 NonConstOpCodeMapPtr xSymbolsNative;
997 lcl_fillNativeSymbols( xSymbolsNative);
998 return xSymbolsNative->getSymbol( eOp );
1001 sal_Unicode FormulaCompiler::GetNativeSymbolChar( OpCode eOp )
1003 return GetNativeSymbol(eOp)[0];
1006 bool FormulaCompiler::InitSymbolsNative( FormulaCompiler::InitSymbols eWhat ) const
1008 return lcl_fillNativeSymbols( mxSymbolsNative, eWhat);
1011 bool FormulaCompiler::InitSymbolsEnglish( FormulaCompiler::InitSymbols eWhat ) const
1013 static OpCodeMapData aMap;
1014 std::unique_lock aGuard(aMap.maMtx);
1015 if (eWhat == InitSymbols::ASK)
1016 return bool(aMap.mxSymbolMap);
1017 else if (eWhat == InitSymbols::DESTROY)
1018 aMap.mxSymbolMap.reset();
1019 else if (!aMap.mxSymbolMap)
1020 loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH, FormulaGrammar::GRAM_ENGLISH, aMap.mxSymbolMap);
1021 mxSymbolsEnglish = aMap.mxSymbolMap;
1022 return true;
1025 bool FormulaCompiler::InitSymbolsPODF( FormulaCompiler::InitSymbols eWhat ) const
1027 static OpCodeMapData aMap;
1028 std::unique_lock aGuard(aMap.maMtx);
1029 if (eWhat == InitSymbols::ASK)
1030 return bool(aMap.mxSymbolMap);
1031 else if (eWhat == InitSymbols::DESTROY)
1032 aMap.mxSymbolMap.reset();
1033 else if (!aMap.mxSymbolMap)
1034 loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH_PODF, FormulaGrammar::GRAM_PODF, aMap.mxSymbolMap, SeparatorType::RESOURCE_BASE);
1035 mxSymbolsPODF = aMap.mxSymbolMap;
1036 return true;
1039 bool FormulaCompiler::InitSymbolsAPI( FormulaCompiler::InitSymbols eWhat ) const
1041 static OpCodeMapData aMap;
1042 std::unique_lock aGuard(aMap.maMtx);
1043 if (eWhat == InitSymbols::ASK)
1044 return bool(aMap.mxSymbolMap);
1045 else if (eWhat == InitSymbols::DESTROY)
1046 aMap.mxSymbolMap.reset();
1047 else if (!aMap.mxSymbolMap)
1048 loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH_API, FormulaGrammar::GRAM_API, aMap.mxSymbolMap, SeparatorType::RESOURCE_BASE);
1049 mxSymbolsAPI = aMap.mxSymbolMap;
1050 return true;
1053 bool FormulaCompiler::InitSymbolsODFF( FormulaCompiler::InitSymbols eWhat ) const
1055 static OpCodeMapData aMap;
1056 std::unique_lock aGuard(aMap.maMtx);
1057 if (eWhat == InitSymbols::ASK)
1058 return bool(aMap.mxSymbolMap);
1059 else if (eWhat == InitSymbols::DESTROY)
1060 aMap.mxSymbolMap.reset();
1061 else if (!aMap.mxSymbolMap)
1062 loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH_ODFF, FormulaGrammar::GRAM_ODFF, aMap.mxSymbolMap, SeparatorType::RESOURCE_BASE);
1063 mxSymbolsODFF = aMap.mxSymbolMap;
1064 return true;
1067 bool FormulaCompiler::InitSymbolsEnglishXL( FormulaCompiler::InitSymbols eWhat ) const
1069 static OpCodeMapData aMap;
1070 std::unique_lock aGuard(aMap.maMtx);
1071 if (eWhat == InitSymbols::ASK)
1072 return bool(aMap.mxSymbolMap);
1073 else if (eWhat == InitSymbols::DESTROY)
1074 aMap.mxSymbolMap.reset();
1075 else if (!aMap.mxSymbolMap)
1076 loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH, FormulaGrammar::GRAM_ENGLISH, aMap.mxSymbolMap);
1077 mxSymbolsEnglishXL = aMap.mxSymbolMap;
1078 if (eWhat != InitSymbols::INIT)
1079 return true;
1081 // TODO: For now, just replace the separators to the Excel English
1082 // variants. Later, if we want to properly map Excel functions with Calc
1083 // functions, we'll need to do a little more work here.
1084 mxSymbolsEnglishXL->putOpCode( OUString(','), ocSep, nullptr);
1085 mxSymbolsEnglishXL->putOpCode( OUString(','), ocArrayColSep, nullptr);
1086 mxSymbolsEnglishXL->putOpCode( OUString(';'), ocArrayRowSep, nullptr);
1088 return true;
1091 bool FormulaCompiler::InitSymbolsOOXML( FormulaCompiler::InitSymbols eWhat ) const
1093 static OpCodeMapData aMap;
1094 std::unique_lock aGuard(aMap.maMtx);
1095 if (eWhat == InitSymbols::ASK)
1096 return bool(aMap.mxSymbolMap);
1097 else if (eWhat == InitSymbols::DESTROY)
1098 aMap.mxSymbolMap.reset();
1099 else if (!aMap.mxSymbolMap)
1100 loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH_OOXML, FormulaGrammar::GRAM_OOXML, aMap.mxSymbolMap, SeparatorType::RESOURCE_BASE);
1101 mxSymbolsOOXML = aMap.mxSymbolMap;
1102 return true;
1106 void FormulaCompiler::loadSymbols(const std::pair<const char*, int>* pSymbols, FormulaGrammar::Grammar eGrammar,
1107 NonConstOpCodeMapPtr& rxMap, SeparatorType eSepType) const
1109 if ( rxMap )
1110 return;
1112 // not Core
1113 rxMap = std::make_shared<OpCodeMap>( SC_OPCODE_LAST_OPCODE_ID + 1, eGrammar != FormulaGrammar::GRAM_ODFF, eGrammar );
1114 OpCodeList aOpCodeList(pSymbols, rxMap, eSepType);
1116 fillFromAddInMap( rxMap, eGrammar);
1117 // Fill from collection for AddIns not already present.
1118 if (FormulaGrammar::GRAM_ENGLISH == eGrammar)
1119 fillFromAddInCollectionEnglishName( rxMap);
1120 else
1122 fillFromAddInCollectionUpperName( rxMap);
1123 if (FormulaGrammar::GRAM_API == eGrammar)
1125 // Add known but not in AddInMap English names, e.g. from the
1126 // PricingFunctions AddIn or any user supplied AddIn.
1127 fillFromAddInCollectionEnglishName( rxMap);
1132 void FormulaCompiler::fillFromAddInCollectionUpperName( const NonConstOpCodeMapPtr& /*xMap */) const
1136 void FormulaCompiler::fillFromAddInCollectionEnglishName( const NonConstOpCodeMapPtr& /*xMap */) const
1140 void FormulaCompiler::fillFromAddInMap( const NonConstOpCodeMapPtr& /*xMap*/, FormulaGrammar::Grammar /*_eGrammar */) const
1144 OpCode FormulaCompiler::GetEnglishOpCode( const OUString& rName ) const
1146 FormulaCompiler::OpCodeMapPtr xMap = GetOpCodeMap( sheet::FormulaLanguage::ENGLISH);
1148 formula::OpCodeHashMap::const_iterator iLook( xMap->getHashMap().find( rName ) );
1149 bool bFound = (iLook != xMap->getHashMap().end());
1150 return bFound ? (*iLook).second : ocNone;
1153 bool FormulaCompiler::IsOpCodeVolatile( OpCode eOp )
1155 bool bRet = false;
1156 switch (eOp)
1158 // no parameters:
1159 case ocRandom:
1160 case ocGetActDate:
1161 case ocGetActTime:
1162 // one parameter:
1163 case ocFormula:
1164 case ocInfo:
1165 // more than one parameters:
1166 // ocIndirect otherwise would have to do
1167 // StopListening and StartListening on a reference for every
1168 // interpreted value.
1169 case ocIndirect:
1170 // ocOffset results in indirect references.
1171 case ocOffset:
1172 // ocDebugVar shows internal value that may change as the internal state changes.
1173 case ocDebugVar:
1174 bRet = true;
1175 break;
1176 default:
1177 bRet = false;
1178 break;
1180 return bRet;
1183 bool FormulaCompiler::IsOpCodeJumpCommand( OpCode eOp )
1185 switch (eOp)
1187 case ocIf:
1188 case ocIfError:
1189 case ocIfNA:
1190 case ocChoose:
1191 return true;
1192 default:
1195 return false;
1198 // Remove quotes, escaped quotes are unescaped.
1199 bool FormulaCompiler::DeQuote( OUString& rStr )
1201 sal_Int32 nLen = rStr.getLength();
1202 if ( nLen > 1 && rStr[0] == '\'' && rStr[ nLen-1 ] == '\'' )
1204 rStr = rStr.copy( 1, nLen-2 );
1205 rStr = rStr.replaceAll( "''", "'" );
1206 return true;
1208 return false;
1211 void FormulaCompiler::fillAddInToken(
1212 ::std::vector< sheet::FormulaOpCodeMapEntry >& /*_rVec*/,
1213 bool /*_bIsEnglish*/) const
1217 bool FormulaCompiler::IsMatrixFunction( OpCode eOpCode )
1219 switch (eOpCode)
1221 case ocDde :
1222 case ocGrowth :
1223 case ocTrend :
1224 case ocLogest :
1225 case ocLinest :
1226 case ocFrequency :
1227 case ocMatTrans :
1228 case ocMatMult :
1229 case ocMatInv :
1230 case ocMatrixUnit :
1231 case ocModalValue_Multi :
1232 case ocFourier :
1233 return true;
1234 default:
1236 // added to avoid warnings
1239 return false;
1243 void FormulaCompiler::OpCodeMap::putCopyOpCode( const OUString& rSymbol, OpCode eOp )
1245 SAL_WARN_IF( !mpTable[eOp].isEmpty() && rSymbol.isEmpty(), "formula.core",
1246 "OpCodeMap::putCopyOpCode: NOT replacing OpCode " << static_cast<sal_uInt16>(eOp)
1247 << " '" << mpTable[eOp] << "' with empty name!");
1248 if (!mpTable[eOp].isEmpty() && rSymbol.isEmpty())
1249 maHashMap.emplace(mpTable[eOp], eOp);
1250 else
1252 mpTable[eOp] = rSymbol;
1253 maHashMap.emplace(rSymbol, eOp);
1257 void FormulaCompiler::OpCodeMap::copyFrom( const OpCodeMap& r )
1259 maHashMap = OpCodeHashMap( mnSymbols);
1261 sal_uInt16 n = r.getSymbolCount();
1262 SAL_WARN_IF( n != mnSymbols, "formula.core",
1263 "OpCodeMap::copyFrom: unequal size, this: " << mnSymbols << " that: " << n);
1264 if (n > mnSymbols)
1265 n = mnSymbols;
1267 // OpCode 0 (ocPush) should never be in a map.
1268 SAL_WARN_IF( !mpTable[0].isEmpty() || !r.mpTable[0].isEmpty(), "formula.core",
1269 "OpCodeMap::copyFrom: OpCode 0 assigned, this: '"
1270 << mpTable[0] << "' that: '" << r.mpTable[0] << "'");
1272 // For bOverrideKnownBad when copying from the English core map (ODF 1.1
1273 // and API) to the native map (UI "use English function names") replace the
1274 // known bad legacy function names with correct ones.
1275 if (r.mbCore &&
1276 FormulaGrammar::extractFormulaLanguage( meGrammar) == sheet::FormulaLanguage::NATIVE &&
1277 FormulaGrammar::extractFormulaLanguage( r.meGrammar) == sheet::FormulaLanguage::ENGLISH)
1279 for (sal_uInt16 i = 1; i < n; ++i)
1281 OUString aSymbol;
1282 OpCode eOp = OpCode(i);
1283 switch (eOp)
1285 case ocRRI:
1286 aSymbol = "RRI";
1287 break;
1288 case ocTableOp:
1289 aSymbol = "MULTIPLE.OPERATIONS";
1290 break;
1291 default:
1292 aSymbol = r.mpTable[i];
1294 putCopyOpCode( aSymbol, eOp);
1297 else
1299 for (sal_uInt16 i = 1; i < n; ++i)
1301 OpCode eOp = OpCode(i);
1302 const OUString& rSymbol = r.mpTable[i];
1303 putCopyOpCode( rSymbol, eOp);
1307 // This was meant to copy to native map that does not have AddIn symbols
1308 // but needs them from the source map. It is unclear what should happen if
1309 // the destination already had externals, so do it only if it doesn't.
1310 if (!hasExternals())
1312 maExternalHashMap = r.maExternalHashMap;
1313 maReverseExternalHashMap = r.maReverseExternalHashMap;
1314 mbCore = r.mbCore;
1315 if (mbEnglish != r.mbEnglish)
1317 // For now keep mbEnglishLocale setting, which is false for a
1318 // non-English native map we're copying to.
1319 /* TODO:
1320 if (!mbEnglish && r.mbEnglish)
1321 mbEnglishLocale = "getUseEnglishLocaleFromConfiguration()";
1322 or set from outside i.e. via ScCompiler.
1324 mbEnglish = r.mbEnglish;
1330 FormulaError FormulaCompiler::GetErrorConstant( const OUString& rName ) const
1332 FormulaError nError = FormulaError::NONE;
1333 OpCodeHashMap::const_iterator iLook( mxSymbols->getHashMap().find( rName));
1334 if (iLook != mxSymbols->getHashMap().end())
1336 switch ((*iLook).second)
1338 // Not all may make sense in a formula, but these we know as
1339 // opcodes.
1340 case ocErrNull:
1341 nError = FormulaError::NoCode;
1342 break;
1343 case ocErrDivZero:
1344 nError = FormulaError::DivisionByZero;
1345 break;
1346 case ocErrValue:
1347 nError = FormulaError::NoValue;
1348 break;
1349 case ocErrRef:
1350 nError = FormulaError::NoRef;
1351 break;
1352 case ocErrName:
1353 nError = FormulaError::NoName;
1354 break;
1355 case ocErrNum:
1356 nError = FormulaError::IllegalFPOperation;
1357 break;
1358 case ocErrNA:
1359 nError = FormulaError::NotAvailable;
1360 break;
1361 default:
1362 ; // nothing
1365 else
1367 // Per convention recognize detailed "#ERRxxx!" constants, always
1368 // untranslated. Error numbers are sal_uInt16 so at most 5 decimal
1369 // digits.
1370 if (rName.startsWithIgnoreAsciiCase("#ERR") && rName.getLength() <= 10 && rName[rName.getLength()-1] == '!')
1372 sal_uInt32 nErr = o3tl::toUInt32(rName.subView( 4, rName.getLength() - 5));
1373 if (0 < nErr && nErr <= SAL_MAX_UINT16 && isPublishedFormulaError(static_cast<FormulaError>(nErr)))
1374 nError = static_cast<FormulaError>(nErr);
1377 return nError;
1380 void FormulaCompiler::EnableJumpCommandReorder( bool bEnable )
1382 mbJumpCommandReorder = bEnable;
1385 void FormulaCompiler::EnableStopOnError( bool bEnable )
1387 mbStopOnError = bEnable;
1390 void FormulaCompiler::AppendErrorConstant( OUStringBuffer& rBuffer, FormulaError nError ) const
1392 OpCode eOp;
1393 switch (nError)
1395 case FormulaError::NoCode:
1396 eOp = ocErrNull;
1397 break;
1398 case FormulaError::DivisionByZero:
1399 eOp = ocErrDivZero;
1400 break;
1401 case FormulaError::NoValue:
1402 eOp = ocErrValue;
1403 break;
1404 case FormulaError::NoRef:
1405 eOp = ocErrRef;
1406 break;
1407 case FormulaError::NoName:
1408 eOp = ocErrName;
1409 break;
1410 case FormulaError::IllegalFPOperation:
1411 eOp = ocErrNum;
1412 break;
1413 case FormulaError::NotAvailable:
1414 eOp = ocErrNA;
1415 break;
1416 default:
1418 // Per convention create detailed "#ERRxxx!" constants, always
1419 // untranslated.
1420 rBuffer.append("#ERR");
1421 rBuffer.append(static_cast<sal_Int32>(nError));
1422 rBuffer.append('!');
1423 return;
1426 rBuffer.append( mxSymbols->getSymbol( eOp));
1429 constexpr short nRecursionMax = 100;
1431 bool FormulaCompiler::GetToken()
1433 FormulaCompilerRecursionGuard aRecursionGuard( nRecursion );
1434 if ( nRecursion > nRecursionMax )
1436 SetError( FormulaError::StackOverflow );
1437 mpLastToken = mpToken = new FormulaByteToken( ocStop );
1438 return false;
1440 if ( bAutoCorrect && !pStack )
1441 { // don't merge stacked subroutine code into entered formula
1442 aCorrectedFormula += aCorrectedSymbol;
1443 aCorrectedSymbol.clear();
1445 bool bStop = false;
1446 if (pArr->GetCodeError() != FormulaError::NONE && mbStopOnError)
1447 bStop = true;
1448 else
1450 FormulaTokenRef pSpacesToken;
1451 short nWasColRowName;
1452 if ( pArr->OpCodeBefore( maArrIterator.GetIndex() ) == ocColRowName )
1453 nWasColRowName = 1;
1454 else
1455 nWasColRowName = 0;
1456 OpCode eTmpOp;
1457 mpToken = maArrIterator.Next();
1458 while (mpToken && ((eTmpOp = mpToken->GetOpCode()) == ocSpaces || eTmpOp == ocWhitespace))
1460 if (eTmpOp == ocSpaces)
1462 // For significant whitespace remember last ocSpaces token.
1463 // Usually there's only one even for multiple spaces.
1464 pSpacesToken = mpToken;
1465 if ( nWasColRowName )
1466 nWasColRowName++;
1468 if ( bAutoCorrect && !pStack )
1469 CreateStringFromToken( aCorrectedFormula, mpToken.get() );
1470 mpToken = maArrIterator.Next();
1472 if ( bAutoCorrect && !pStack && mpToken )
1473 CreateStringFromToken( aCorrectedSymbol, mpToken.get() );
1474 if( !mpToken )
1476 if( pStack )
1478 PopTokenArray();
1479 // mpLastToken was popped as well and corresponds to the
1480 // then current last token during PushTokenArray(), e.g. for
1481 // HandleRange().
1482 return GetToken();
1484 else
1485 bStop = true;
1487 else
1489 if ( nWasColRowName >= 2 && mpToken->GetOpCode() == ocColRowName )
1490 { // convert an ocSpaces to ocIntersect in RPN
1491 mpLastToken = mpToken = new FormulaByteToken( ocIntersect );
1492 maArrIterator.StepBack(); // we advanced to the second ocColRowName, step back
1494 else if (pSpacesToken && FormulaGrammar::isExcelSyntax( meGrammar) &&
1495 mpLastToken && mpToken &&
1496 isPotentialRangeType( mpLastToken.get(), false, false) &&
1497 isPotentialRangeType( mpToken.get(), false, true))
1499 // Let IntersectionLine() <- Factor() decide how to treat this,
1500 // once the actual arguments are determined in RPN.
1501 mpLastToken = mpToken = pSpacesToken;
1502 maArrIterator.StepBack(); // step back from next non-spaces token
1503 return true;
1507 if( bStop )
1509 mpLastToken = mpToken = new FormulaByteToken( ocStop );
1510 return false;
1513 // Remember token for next round and any PushTokenArray() calls that may
1514 // occur in handlers.
1515 mpLastToken = mpToken;
1517 if ( mpToken->IsExternalRef() )
1519 return HandleExternalReference(*mpToken);
1521 else
1523 switch (mpToken->GetOpCode())
1525 case ocSubTotal:
1526 case ocAggregate:
1527 glSubTotal = true;
1528 break;
1529 case ocName:
1530 if( HandleRange())
1532 // Expanding ocName might have introduced tokens such as ocStyle that prevent formula threading,
1533 // but those wouldn't be present in the raw tokens array, so ensure RPN tokens will be checked too.
1534 needsRPNTokenCheck = true;
1535 return true;
1537 return false;
1538 case ocColRowName:
1539 return HandleColRowName();
1540 case ocDBArea:
1541 return HandleDbData();
1542 case ocTableRef:
1543 return HandleTableRef();
1544 case ocPush:
1545 if( mbComputeII )
1546 HandleIIOpCode(mpToken.get(), nullptr, 0);
1547 break;
1548 default:
1549 ; // nothing
1552 return true;
1556 // RPN creation by recursion
1557 void FormulaCompiler::Factor()
1559 if (pArr->GetCodeError() != FormulaError::NONE && mbStopOnError)
1560 return;
1562 CurrentFactor pFacToken( this );
1564 OpCode eOp = mpToken->GetOpCode();
1565 if (eOp == ocPush || eOp == ocColRowNameAuto || eOp == ocMatRef || eOp == ocDBArea
1566 || eOp == ocTableRef
1567 || (!mbJumpCommandReorder && ((eOp == ocName) || (eOp == ocColRowName) || (eOp == ocBad)))
1570 PutCode( mpToken );
1571 eOp = NextToken();
1572 if( eOp == ocOpen )
1574 // PUSH( is an error that may be caused by an unknown function.
1575 SetError(
1576 ( mpToken->GetType() == svString
1577 || mpToken->GetType() == svSingleRef )
1578 ? FormulaError::NoName : FormulaError::OperatorExpected );
1579 if ( bAutoCorrect && !pStack )
1580 { // assume multiplication
1581 aCorrectedFormula += mxSymbols->getSymbol( ocMul);
1582 bCorrected = true;
1583 NextToken();
1584 eOp = Expression();
1585 if( eOp != ocClose )
1586 SetError( FormulaError::PairExpected);
1587 else
1588 NextToken();
1592 else if( eOp == ocOpen )
1594 NextToken();
1595 eOp = Expression();
1596 while ((eOp == ocSep) && (pArr->GetCodeError() == FormulaError::NONE || !mbStopOnError))
1597 { // range list (A1;A2) converted to (A1~A2)
1598 pFacToken = mpToken;
1599 NextToken();
1600 CheckSetForceArrayParameter( mpToken, 0);
1601 eOp = Expression();
1602 // Do not ignore error here, regardless of mbStopOnError, to not
1603 // change the formula expression in case of an unexpected state.
1604 if (pArr->GetCodeError() == FormulaError::NONE && pc >= 2)
1606 // Left and right operands must be reference or function
1607 // returning reference to form a range list.
1608 const FormulaToken* p = pCode[-2];
1609 if (p && isPotentialRangeType( p, true, false))
1611 p = pCode[-1];
1612 if (p && isPotentialRangeType( p, true, true))
1614 pFacToken->NewOpCode( ocUnion, FormulaToken::PrivateAccess());
1615 // XXX NOTE: the token's eType is still svSep here!
1616 PutCode( pFacToken);
1621 if (eOp != ocClose)
1622 SetError( FormulaError::PairExpected);
1623 else
1624 NextToken();
1626 /* TODO: if no conversion to ocUnion is involved this could collect
1627 * such expression as a list or (matrix) vector to be passed as
1628 * argument for one parameter (which in fact the ocUnion svRefList is a
1629 * special case of), which would require a new StackVar type and needed
1630 * to be handled by the interpreter for functions that could support it
1631 * (i.e. already handle VAR_ARGS or svRefList parameters). This is also
1632 * not defined by ODF.
1633 * Does Excel handle =SUM((1;2))?
1634 * As is, the interpreter catches extraneous uncalculated
1635 * subexpressions like 1 of (1;2) as error. */
1637 else
1639 if( nNumFmt == SvNumFormatType::UNDEFINED )
1640 nNumFmt = lcl_GetRetFormat( eOp );
1642 if ( IsOpCodeVolatile( eOp) )
1643 pArr->SetExclusiveRecalcModeAlways();
1644 else
1646 switch( eOp )
1648 // Functions recalculated on every document load.
1649 // ONLOAD_LENIENT here to be able to distinguish and not
1650 // force a recalc (if not in an ALWAYS or ONLOAD_MUST
1651 // context) but keep an imported result from for example
1652 // OOXML a DDE call. Will be recalculated for ODFF.
1653 case ocConvertOOo :
1654 case ocDde:
1655 case ocMacro:
1656 case ocWebservice:
1657 pArr->AddRecalcMode( ScRecalcMode::ONLOAD_LENIENT );
1658 break;
1659 // RANDBETWEEN() is volatile like RAND(). Other Add-In
1660 // functions may have to be recalculated or not, we don't
1661 // know, classify as ONLOAD_LENIENT.
1662 case ocExternal:
1663 if (mpToken->GetExternal() == "com.sun.star.sheet.addin.Analysis.getRandbetween")
1664 pArr->SetExclusiveRecalcModeAlways();
1665 else
1666 pArr->AddRecalcMode( ScRecalcMode::ONLOAD_LENIENT );
1667 break;
1668 // If the referred cell is moved the value changes.
1669 case ocColumn :
1670 case ocRow :
1671 pArr->SetRecalcModeOnRefMove();
1672 break;
1673 // ocCell needs recalc on move for some possible type values.
1674 // And recalc mode on load, tdf#60645
1675 case ocCell :
1676 pArr->SetRecalcModeOnRefMove();
1677 pArr->AddRecalcMode( ScRecalcMode::ONLOAD_MUST );
1678 break;
1679 case ocHyperLink :
1680 // Cell with hyperlink needs to be calculated on load to
1681 // get its matrix result generated.
1682 pArr->AddRecalcMode( ScRecalcMode::ONLOAD_MUST );
1683 pArr->SetHyperLink( true);
1684 break;
1685 default:
1686 ; // nothing
1689 if (SC_OPCODE_START_NO_PAR <= eOp && eOp < SC_OPCODE_STOP_NO_PAR)
1691 pFacToken = mpToken;
1692 eOp = NextToken();
1693 if (eOp != ocOpen)
1695 SetError( FormulaError::PairExpected);
1696 PutCode( pFacToken );
1698 else
1700 eOp = NextToken();
1701 if (eOp != ocClose)
1702 SetError( FormulaError::PairExpected);
1703 PutCode( pFacToken);
1704 NextToken();
1707 else if (SC_OPCODE_START_1_PAR <= eOp && eOp < SC_OPCODE_STOP_1_PAR)
1709 if (eOp == ocIsoWeeknum && FormulaGrammar::isODFF( meGrammar ))
1711 // tdf#50950 ocIsoWeeknum can have 2 arguments when saved by older versions of Calc;
1712 // the opcode then has to be changed to ocWeek for backward compatibility
1713 pFacToken = mpToken;
1714 eOp = NextToken();
1715 bool bNoParam = false;
1716 if (eOp == ocOpen)
1718 eOp = NextToken();
1719 if (eOp == ocClose)
1720 bNoParam = true;
1721 else
1723 CheckSetForceArrayParameter( mpToken, 0);
1724 eOp = Expression();
1727 else
1728 SetError( FormulaError::PairExpected);
1729 sal_uInt32 nSepCount = 0;
1730 const sal_uInt16 nSepPos = maArrIterator.GetIndex() - 1; // separator position, if any
1731 if( !bNoParam )
1733 nSepCount++;
1734 while ((eOp == ocSep) && (pArr->GetCodeError() == FormulaError::NONE || !mbStopOnError))
1736 NextToken();
1737 CheckSetForceArrayParameter( mpToken, nSepCount);
1738 nSepCount++;
1739 if (nSepCount > FORMULA_MAXPARAMS)
1740 SetError( FormulaError::CodeOverflow);
1741 eOp = Expression();
1744 if (eOp != ocClose)
1745 SetError( FormulaError::PairExpected);
1746 else
1747 NextToken();
1748 pFacToken->SetByte( nSepCount );
1749 if (nSepCount == 2)
1751 // An old mode!=1 indicates ISO week, remove argument if
1752 // literal double value and keep function. Anything else
1753 // can not be resolved, there exists no "like ISO but week
1754 // starts on Sunday" mode in WEEKNUM and for an expression
1755 // we can't determine.
1756 // Current index is nSepPos+3 if expression stops, or
1757 // nSepPos+4 if expression continues after the call because
1758 // we just called NextToken() to move away from it.
1759 if (pc >= 2 && (maArrIterator.GetIndex() == nSepPos + 3 || maArrIterator.GetIndex() == nSepPos + 4) &&
1760 pArr->TokenAt(nSepPos+1)->GetType() == svDouble &&
1761 pArr->TokenAt(nSepPos+1)->GetDouble() != 1.0 &&
1762 pArr->TokenAt(nSepPos+2)->GetOpCode() == ocClose &&
1763 pArr->RemoveToken( nSepPos, 2) == 2)
1765 maArrIterator.AfterRemoveToken( nSepPos, 2);
1766 // Remove the ocPush/svDouble just removed also from
1767 // the compiler local RPN array.
1768 --pCode; --pc;
1769 (*pCode)->DecRef(); // may be dead now
1770 pFacToken->SetByte( nSepCount - 1 );
1772 else
1774 // For the remaining two arguments cases use the
1775 // compatibility function.
1776 pFacToken->NewOpCode( ocWeeknumOOo, FormulaToken::PrivateAccess());
1779 PutCode( pFacToken );
1781 else
1783 // standard handling of 1-parameter opcodes
1784 pFacToken = mpToken;
1785 eOp = NextToken();
1786 if( nNumFmt == SvNumFormatType::UNDEFINED && eOp == ocNot )
1787 nNumFmt = SvNumFormatType::LOGICAL;
1788 if (eOp == ocOpen)
1790 NextToken();
1791 CheckSetForceArrayParameter( mpToken, 0);
1792 eOp = Expression();
1794 else
1795 SetError( FormulaError::PairExpected);
1796 if (eOp != ocClose)
1797 SetError( FormulaError::PairExpected);
1798 else if ( pArr->GetCodeError() == FormulaError::NONE )
1800 pFacToken->SetByte( 1 );
1801 if (mbComputeII)
1803 FormulaToken** pArg = pCode - 1;
1804 HandleIIOpCode(pFacToken, &pArg, 1);
1807 PutCode( pFacToken );
1808 NextToken();
1811 else if ((SC_OPCODE_START_2_PAR <= eOp && eOp < SC_OPCODE_STOP_2_PAR)
1812 || eOp == ocExternal
1813 || eOp == ocMacro
1814 || eOp == ocAnd
1815 || eOp == ocOr
1816 || eOp == ocBad
1817 || ( eOp >= ocInternalBegin && eOp <= ocInternalEnd )
1818 || (!mbJumpCommandReorder && IsOpCodeJumpCommand(eOp)))
1820 pFacToken = mpToken;
1821 OpCode eMyLastOp = eOp;
1822 eOp = NextToken();
1823 bool bNoParam = false;
1824 bool bBadName = false;
1825 if (eOp == ocOpen)
1827 eOp = NextToken();
1828 if (eOp == ocClose)
1829 bNoParam = true;
1830 else
1832 CheckSetForceArrayParameter( mpToken, 0);
1833 eOp = Expression();
1836 else if (eMyLastOp == ocBad)
1838 // Just a bad name, not an unknown function, no parameters, no
1839 // closing expected.
1840 bBadName = true;
1841 bNoParam = true;
1843 else
1844 SetError( FormulaError::PairExpected);
1845 sal_uInt32 nSepCount = 0;
1846 if( !bNoParam )
1848 bool bDoIICompute = mbComputeII;
1849 // Array of FormulaToken double pointers to collect the parameters of II opcodes.
1850 FormulaToken*** pArgArray = nullptr;
1851 if (bDoIICompute)
1853 pArgArray = static_cast<FormulaToken***>(alloca(sizeof(FormulaToken**)*FORMULA_MAXPARAMSII));
1854 if (!pArgArray)
1855 bDoIICompute = false;
1858 nSepCount++;
1860 if (bDoIICompute)
1861 pArgArray[nSepCount-1] = pCode - 1; // Add first argument
1863 while ((eOp == ocSep) && (pArr->GetCodeError() == FormulaError::NONE || !mbStopOnError))
1865 NextToken();
1866 CheckSetForceArrayParameter( mpToken, nSepCount);
1867 nSepCount++;
1868 if (nSepCount > FORMULA_MAXPARAMS)
1869 SetError( FormulaError::CodeOverflow);
1870 eOp = Expression();
1871 if (bDoIICompute && nSepCount <= FORMULA_MAXPARAMSII)
1872 pArgArray[nSepCount - 1] = pCode - 1; // Add rest of the arguments
1874 if (bDoIICompute)
1875 HandleIIOpCode(pFacToken, pArgArray,
1876 std::min(nSepCount, static_cast<sal_uInt32>(FORMULA_MAXPARAMSII)));
1878 bool bDone = false;
1879 if (bBadName)
1880 ; // nothing, keep current token for return
1881 else if (eOp != ocClose)
1882 SetError( FormulaError::PairExpected);
1883 else
1885 NextToken();
1886 bDone = true;
1888 // Jumps are just normal functions for the FunctionAutoPilot tree view
1889 if (!mbJumpCommandReorder && pFacToken->GetType() == svJump)
1890 pFacToken = new FormulaFAPToken( pFacToken->GetOpCode(), nSepCount, pFacToken );
1891 else
1892 pFacToken->SetByte( nSepCount );
1893 PutCode( pFacToken );
1895 if (bDone)
1896 AnnotateOperands();
1898 else if (IsOpCodeJumpCommand(eOp))
1900 // the PC counters are -1
1901 pFacToken = mpToken;
1902 switch (eOp)
1904 case ocIf:
1905 pFacToken->GetJump()[ 0 ] = 3; // if, else, behind
1906 break;
1907 case ocChoose:
1908 pFacToken->GetJump()[ 0 ] = FORMULA_MAXJUMPCOUNT + 1;
1909 break;
1910 case ocIfError:
1911 case ocIfNA:
1912 pFacToken->GetJump()[ 0 ] = 2; // if, behind
1913 break;
1914 default:
1915 SAL_WARN("formula.core","Jump OpCode: " << +eOp);
1916 assert(!"FormulaCompiler::Factor: someone forgot to add a jump count case");
1918 eOp = NextToken();
1919 if (eOp == ocOpen)
1921 NextToken();
1922 CheckSetForceArrayParameter( mpToken, 0);
1923 eOp = Expression();
1925 else
1926 SetError( FormulaError::PairExpected);
1927 PutCode( pFacToken );
1928 // During AutoCorrect (since pArr->GetCodeError() is
1929 // ignored) an unlimited ocIf would crash because
1930 // ScRawToken::Clone() allocates the JumpBuffer according to
1931 // nJump[0]*2+2, which is 3*2+2 on ocIf and 2*2+2 ocIfError and ocIfNA.
1932 short nJumpMax;
1933 OpCode eFacOpCode = pFacToken->GetOpCode();
1934 switch (eFacOpCode)
1936 case ocIf:
1937 nJumpMax = 3;
1938 break;
1939 case ocChoose:
1940 nJumpMax = FORMULA_MAXJUMPCOUNT;
1941 break;
1942 case ocIfError:
1943 case ocIfNA:
1944 nJumpMax = 2;
1945 break;
1946 case ocStop:
1947 // May happen only if PutCode(pFacToken) ran into overflow.
1948 nJumpMax = 0;
1949 assert(pc == FORMULA_MAXTOKENS && pArr->GetCodeError() != FormulaError::NONE);
1950 break;
1951 default:
1952 nJumpMax = 0;
1953 SAL_WARN("formula.core","Jump OpCode: " << +eFacOpCode);
1954 assert(!"FormulaCompiler::Factor: someone forgot to add a jump max case");
1956 short nJumpCount = 0;
1957 while ( (nJumpCount < (FORMULA_MAXJUMPCOUNT - 1)) && (eOp == ocSep)
1958 && (pArr->GetCodeError() == FormulaError::NONE || !mbStopOnError))
1960 if ( ++nJumpCount <= nJumpMax )
1961 pFacToken->GetJump()[nJumpCount] = pc-1;
1962 NextToken();
1963 CheckSetForceArrayParameter( mpToken, nJumpCount - 1);
1964 eOp = Expression();
1965 // ocSep or ocClose terminate the subexpression
1966 PutCode( mpToken );
1968 if (eOp != ocClose)
1969 SetError( FormulaError::PairExpected);
1970 else
1972 NextToken();
1973 // always limit to nJumpMax, no arbitrary overwrites
1974 if ( ++nJumpCount <= nJumpMax )
1975 pFacToken->GetJump()[ nJumpCount ] = pc-1;
1976 eFacOpCode = pFacToken->GetOpCode();
1977 bool bLimitOk;
1978 switch (eFacOpCode)
1980 case ocIf:
1981 bLimitOk = (nJumpCount <= 3);
1982 break;
1983 case ocChoose:
1984 bLimitOk = (nJumpCount < FORMULA_MAXJUMPCOUNT);
1985 break;
1986 case ocIfError:
1987 case ocIfNA:
1988 bLimitOk = (nJumpCount <= 2);
1989 break;
1990 case ocStop:
1991 // May happen only if PutCode(pFacToken) ran into overflow.
1992 // This may had resulted from a stacked token array and
1993 // error wasn't propagated so assert only the program
1994 // counter.
1995 bLimitOk = false;
1996 assert(pc == FORMULA_MAXTOKENS);
1997 break;
1998 default:
1999 bLimitOk = false;
2000 SAL_WARN("formula.core","Jump OpCode: " << +eFacOpCode);
2001 assert(!"FormulaCompiler::Factor: someone forgot to add a jump limit case");
2003 if (bLimitOk)
2004 pFacToken->GetJump()[ 0 ] = nJumpCount;
2005 else
2006 SetError( FormulaError::IllegalParameter);
2009 else if ( eOp == ocMissing )
2011 PutCode( mpToken );
2012 NextToken();
2014 else if ( eOp == ocClose )
2016 SetError( FormulaError::ParameterExpected );
2018 else if ( eOp == ocSep )
2019 { // Subsequent ocSep
2020 SetError( FormulaError::ParameterExpected );
2021 if ( bAutoCorrect && !pStack )
2023 aCorrectedSymbol.clear();
2024 bCorrected = true;
2027 else if ( mpToken->IsExternalRef() )
2029 PutCode( mpToken);
2030 NextToken();
2032 else
2034 SetError( FormulaError::UnknownToken );
2035 if ( bAutoCorrect && !pStack )
2037 if ( eOp == ocStop )
2038 { // trailing operator w/o operand
2039 sal_Int32 nLen = aCorrectedFormula.getLength();
2040 if ( nLen )
2041 aCorrectedFormula = aCorrectedFormula.copy( 0, nLen - 1 );
2042 aCorrectedSymbol.clear();
2043 bCorrected = true;
2050 void FormulaCompiler::RangeLine()
2052 Factor();
2053 while (mpToken->GetOpCode() == ocRange)
2055 FormulaToken** pCode1 = pCode - 1;
2056 FormulaTokenRef p = mpToken;
2057 NextToken();
2058 Factor();
2059 FormulaToken** pCode2 = pCode - 1;
2060 if (!MergeRangeReference( pCode1, pCode2))
2061 PutCode(p);
2065 void FormulaCompiler::IntersectionLine()
2067 RangeLine();
2068 while (mpToken->GetOpCode() == ocIntersect || mpToken->GetOpCode() == ocSpaces)
2070 sal_uInt16 nCodeIndex = maArrIterator.GetIndex() - 1;
2071 FormulaToken** pCode1 = pCode - 1;
2072 FormulaTokenRef p = mpToken;
2073 NextToken();
2074 RangeLine();
2075 FormulaToken** pCode2 = pCode - 1;
2076 if (p->GetOpCode() == ocSpaces)
2078 // Convert to intersection if both left and right are references or
2079 // functions (potentially returning references, if not then a space
2080 // or no space would be a syntax error anyway), not other operators
2081 // or operands. Else discard.
2082 if (isAdjacentOrGapRpnEnd( pc, pCode, pCode1, pCode2) && isIntersectable( pCode1, pCode2))
2084 FormulaTokenRef pIntersect( new FormulaByteToken( ocIntersect));
2085 // Replace ocSpaces with ocIntersect so that when switching
2086 // formula syntax the correct operator string is created.
2087 // coverity[freed_arg : FALSE] - FormulaTokenRef has a ref so ReplaceToken won't delete pIntersect
2088 pArr->ReplaceToken( nCodeIndex, pIntersect.get(), FormulaTokenArray::ReplaceMode::CODE_ONLY);
2089 PutCode( pIntersect);
2092 else
2094 PutCode(p);
2099 void FormulaCompiler::UnionLine()
2101 IntersectionLine();
2102 while (mpToken->GetOpCode() == ocUnion)
2104 FormulaTokenRef p = mpToken;
2105 NextToken();
2106 IntersectionLine();
2107 PutCode(p);
2111 void FormulaCompiler::UnaryLine()
2113 if( mpToken->GetOpCode() == ocAdd )
2114 GetToken();
2115 else if (SC_OPCODE_START_UN_OP <= mpToken->GetOpCode() &&
2116 mpToken->GetOpCode() < SC_OPCODE_STOP_UN_OP)
2118 FormulaTokenRef p = mpToken;
2119 NextToken();
2120 UnaryLine();
2121 if (mbComputeII)
2123 FormulaToken** pArg = pCode - 1;
2124 HandleIIOpCode(p.get(), &pArg, 1);
2126 PutCode( p );
2128 else
2129 UnionLine();
2132 void FormulaCompiler::PostOpLine()
2134 UnaryLine();
2135 while ( mpToken->GetOpCode() == ocPercentSign )
2136 { // this operator _follows_ its operand
2137 if (mbComputeII)
2139 FormulaToken** pArg = pCode - 1;
2140 HandleIIOpCode(mpToken.get(), &pArg, 1);
2142 PutCode( mpToken );
2143 NextToken();
2147 void FormulaCompiler::PowLine()
2149 PostOpLine();
2150 while (mpToken->GetOpCode() == ocPow)
2152 FormulaTokenRef p = mpToken;
2153 FormulaToken** pArgArray[2];
2154 if (mbComputeII)
2155 pArgArray[0] = pCode - 1; // Add first argument
2156 NextToken();
2157 PostOpLine();
2158 if (mbComputeII)
2160 pArgArray[1] = pCode - 1; // Add second argument
2161 HandleIIOpCode(p.get(), pArgArray, 2);
2163 PutCode(p);
2167 void FormulaCompiler::MulDivLine()
2169 PowLine();
2170 while (mpToken->GetOpCode() == ocMul || mpToken->GetOpCode() == ocDiv)
2172 FormulaTokenRef p = mpToken;
2173 FormulaToken** pArgArray[2];
2174 if (mbComputeII)
2175 pArgArray[0] = pCode - 1; // Add first argument
2176 NextToken();
2177 PowLine();
2178 if (mbComputeII)
2180 pArgArray[1] = pCode - 1; // Add second argument
2181 HandleIIOpCode(p.get(), pArgArray, 2);
2183 PutCode(p);
2187 void FormulaCompiler::AddSubLine()
2189 MulDivLine();
2190 while (mpToken->GetOpCode() == ocAdd || mpToken->GetOpCode() == ocSub)
2192 FormulaTokenRef p = mpToken;
2193 FormulaToken** pArgArray[2];
2194 if (mbComputeII)
2195 pArgArray[0] = pCode - 1; // Add first argument
2196 NextToken();
2197 MulDivLine();
2198 if (mbComputeII)
2200 pArgArray[1] = pCode - 1; // Add second argument
2201 HandleIIOpCode(p.get(), pArgArray, 2);
2203 PutCode(p);
2207 void FormulaCompiler::ConcatLine()
2209 AddSubLine();
2210 while (mpToken->GetOpCode() == ocAmpersand)
2212 FormulaTokenRef p = mpToken;
2213 FormulaToken** pArgArray[2];
2214 if (mbComputeII)
2215 pArgArray[0] = pCode - 1; // Add first argument
2216 NextToken();
2217 AddSubLine();
2218 if (mbComputeII)
2220 pArgArray[1] = pCode - 1; // Add second argument
2221 HandleIIOpCode(p.get(), pArgArray, 2);
2223 PutCode(p);
2227 void FormulaCompiler::CompareLine()
2229 ConcatLine();
2230 while (mpToken->GetOpCode() >= ocEqual && mpToken->GetOpCode() <= ocGreaterEqual)
2232 FormulaTokenRef p = mpToken;
2233 FormulaToken** pArgArray[2];
2234 if (mbComputeII)
2235 pArgArray[0] = pCode - 1; // Add first argument
2236 NextToken();
2237 ConcatLine();
2238 if (mbComputeII)
2240 pArgArray[1] = pCode - 1; // Add second argument
2241 HandleIIOpCode(p.get(), pArgArray, 2);
2243 PutCode(p);
2247 OpCode FormulaCompiler::Expression()
2249 FormulaCompilerRecursionGuard aRecursionGuard( nRecursion );
2250 if ( nRecursion > nRecursionMax )
2252 SetError( FormulaError::StackOverflow );
2253 return ocStop; //! generate token instead?
2255 CompareLine();
2256 while (mpToken->GetOpCode() == ocAnd || mpToken->GetOpCode() == ocOr)
2258 FormulaTokenRef p = mpToken;
2259 mpToken->SetByte( 2 ); // 2 parameters!
2260 FormulaToken** pArgArray[2];
2261 if (mbComputeII)
2262 pArgArray[0] = pCode - 1; // Add first argument
2263 NextToken();
2264 CompareLine();
2265 if (mbComputeII)
2267 pArgArray[1] = pCode - 1; // Add second argument
2268 HandleIIOpCode(p.get(), pArgArray, 2);
2270 PutCode(p);
2272 return mpToken->GetOpCode();
2276 void FormulaCompiler::SetError( FormulaError /*nError*/ )
2280 FormulaTokenRef FormulaCompiler::ExtendRangeReference( FormulaToken & /*rTok1*/, FormulaToken & /*rTok2*/ )
2282 return FormulaTokenRef();
2285 bool FormulaCompiler::MergeRangeReference( FormulaToken * * const pCode1, FormulaToken * const * const pCode2 )
2287 if (!isAdjacentRpnEnd( pc, pCode, pCode1, pCode2))
2288 return false;
2290 FormulaToken *p1 = *pCode1, *p2 = *pCode2;
2291 FormulaTokenRef p = ExtendRangeReference( *p1, *p2);
2292 if (!p)
2293 return false;
2295 p->IncRef();
2296 p1->DecRef();
2297 p2->DecRef();
2298 *pCode1 = p.get();
2299 --pCode;
2300 --pc;
2302 return true;
2305 bool FormulaCompiler::CompileTokenArray()
2307 glSubTotal = false;
2308 bCorrected = false;
2309 needsRPNTokenCheck = false;
2310 if (pArr->GetCodeError() == FormulaError::NONE || !mbStopOnError)
2312 if ( bAutoCorrect )
2314 aCorrectedFormula.clear();
2315 aCorrectedSymbol.clear();
2317 pArr->DelRPN();
2318 maArrIterator.Reset();
2319 pStack = nullptr;
2320 FormulaToken* pDataArray[ FORMULA_MAXTOKENS + 1 ];
2321 // Code in some places refers to the last token as 'pCode - 1', which may
2322 // point before the first element if the expression is bad. So insert a dummy
2323 // node in that place which will make that token be nullptr.
2324 pDataArray[ 0 ] = nullptr;
2325 FormulaToken** pData = pDataArray + 1;
2326 pCode = pData;
2327 bool bWasForced = pArr->IsRecalcModeForced();
2328 if ( bWasForced && bAutoCorrect )
2329 aCorrectedFormula = "=";
2330 pArr->ClearRecalcMode();
2331 maArrIterator.Reset();
2332 eLastOp = ocOpen;
2333 pc = 0;
2334 NextToken();
2335 OpCode eOp = Expression();
2336 // Some trailing garbage that doesn't form an expression?
2337 if (eOp != ocStop)
2338 SetError( FormulaError::OperatorExpected);
2339 PostProcessCode();
2341 FormulaError nErrorBeforePop = pArr->GetCodeError();
2343 while( pStack )
2344 PopTokenArray();
2345 if( pc )
2347 pArr->CreateNewRPNArrayFromData( pData, pc );
2348 if( needsRPNTokenCheck )
2349 pArr->CheckAllRPNTokens();
2352 // once an error, always an error
2353 if( pArr->GetCodeError() == FormulaError::NONE && nErrorBeforePop != FormulaError::NONE )
2354 pArr->SetCodeError( nErrorBeforePop);
2356 if (pArr->GetCodeError() != FormulaError::NONE && mbStopOnError)
2358 pArr->DelRPN();
2359 maArrIterator.Reset();
2360 pArr->SetHyperLink( false);
2363 if ( bWasForced )
2364 pArr->SetRecalcModeForced();
2366 if( nNumFmt == SvNumFormatType::UNDEFINED )
2367 nNumFmt = SvNumFormatType::NUMBER;
2368 return glSubTotal;
2371 void FormulaCompiler::PopTokenArray()
2373 if( !pStack )
2374 return;
2376 FormulaArrayStack* p = pStack;
2377 pStack = p->pNext;
2378 // obtain special RecalcMode from SharedFormula
2379 if ( pArr->IsRecalcModeAlways() )
2380 p->pArr->SetExclusiveRecalcModeAlways();
2381 else if ( !pArr->IsRecalcModeNormal() && p->pArr->IsRecalcModeNormal() )
2382 p->pArr->SetMaskedRecalcMode( pArr->GetRecalcMode() );
2383 p->pArr->SetCombinedBitsRecalcMode( pArr->GetRecalcMode() );
2384 if ( pArr->IsHyperLink() ) // fdo 87534
2385 p->pArr->SetHyperLink( true );
2386 if( p->bTemp )
2387 delete pArr;
2388 pArr = p->pArr;
2389 maArrIterator = FormulaTokenArrayPlainIterator(*pArr);
2390 maArrIterator.Jump(p->nIndex);
2391 mpLastToken = p->mpLastToken;
2392 delete p;
2395 void FormulaCompiler::CreateStringFromTokenArray( OUString& rFormula )
2397 OUStringBuffer aBuffer( pArr->GetLen() * 5 );
2398 CreateStringFromTokenArray( aBuffer );
2399 rFormula = aBuffer.makeStringAndClear();
2402 void FormulaCompiler::CreateStringFromTokenArray( OUStringBuffer& rBuffer )
2404 rBuffer.setLength(0);
2405 if( !pArr->GetLen() )
2406 return;
2408 FormulaTokenArray* pSaveArr = pArr;
2409 int nSaveIndex = maArrIterator.GetIndex();
2410 bool bODFF = FormulaGrammar::isODFF( meGrammar);
2411 if (bODFF || FormulaGrammar::isPODF( meGrammar) )
2413 // Scan token array for missing args and re-write if present.
2414 MissingConventionODF aConv( bODFF);
2415 if (pArr->NeedsPodfRewrite( aConv))
2417 pArr = pArr->RewriteMissing( aConv );
2418 maArrIterator = FormulaTokenArrayPlainIterator( *pArr );
2421 else if ( FormulaGrammar::isOOXML( meGrammar ) )
2423 // Scan token array for missing args and rewrite if present.
2424 if (pArr->NeedsOoxmlRewrite())
2426 MissingConventionOOXML aConv;
2427 pArr = pArr->RewriteMissing( aConv );
2428 maArrIterator = FormulaTokenArrayPlainIterator( *pArr );
2432 // At least one character per token, plus some are references, some are
2433 // function names, some are numbers, ...
2434 rBuffer.ensureCapacity( pArr->GetLen() * 5 );
2436 if ( pArr->IsRecalcModeForced() )
2437 rBuffer.append( '=');
2438 const FormulaToken* t = maArrIterator.First();
2439 while( t )
2440 t = CreateStringFromToken( rBuffer, t, true );
2442 if (pSaveArr != pArr)
2444 delete pArr;
2445 pArr = pSaveArr;
2446 maArrIterator = FormulaTokenArrayPlainIterator( *pArr );
2447 maArrIterator.Jump(nSaveIndex);
2451 const FormulaToken* FormulaCompiler::CreateStringFromToken( OUString& rFormula, const FormulaToken* pTokenP )
2453 OUStringBuffer aBuffer;
2454 const FormulaToken* p = CreateStringFromToken( aBuffer, pTokenP );
2455 rFormula += aBuffer;
2456 return p;
2459 const FormulaToken* FormulaCompiler::CreateStringFromToken( OUStringBuffer& rBuffer, const FormulaToken* pTokenP,
2460 bool bAllowArrAdvance )
2462 bool bNext = true;
2463 bool bSpaces = false;
2464 const FormulaToken* t = pTokenP;
2465 OpCode eOp = t->GetOpCode();
2466 if( eOp >= ocAnd && eOp <= ocOr )
2468 // AND, OR infix?
2469 if ( bAllowArrAdvance )
2470 t = maArrIterator.Next();
2471 else
2472 t = maArrIterator.PeekNext();
2473 bNext = false;
2474 bSpaces = ( !t || t->GetOpCode() != ocOpen );
2476 if( bSpaces )
2477 rBuffer.append( ' ');
2479 if (eOp == ocSpaces || eOp == ocWhitespace)
2481 bool bWriteSpaces = true;
2482 if (eOp == ocSpaces && mxSymbols->isODFF())
2484 const FormulaToken* p = maArrIterator.PeekPrevNoSpaces();
2485 bool bIntersectionOp = (p && p->GetOpCode() == ocColRowName);
2486 if (bIntersectionOp)
2488 p = maArrIterator.PeekNextNoSpaces();
2489 bIntersectionOp = (p && p->GetOpCode() == ocColRowName);
2491 if (bIntersectionOp)
2493 rBuffer.append( "!!");
2494 bWriteSpaces = false;
2497 if (bWriteSpaces)
2499 // ODF v1.3 OpenFormula 5.14 Whitespace states "whitespace shall
2500 // not separate a function name from its initial opening
2501 // parenthesis".
2503 // ECMA-376-1:2016 18.17.2 Syntax states "that no space characters
2504 // shall separate a function-name from the left parenthesis (()
2505 // that follows it." and Excel even chokes on it.
2507 // Suppress/remove it in any case also in UI, it will not be
2508 // preserved.
2509 const FormulaToken* p = maArrIterator.PeekPrevNoSpaces();
2510 if (p && p->IsFunction())
2512 p = maArrIterator.PeekNextNoSpaces();
2513 if (p && p->GetOpCode() == ocOpen)
2514 bWriteSpaces = false;
2517 if (bWriteSpaces)
2519 // most times it's just one blank
2520 sal_uInt8 n = t->GetByte();
2521 for ( sal_uInt8 j=0; j<n; ++j )
2523 if (eOp == ocWhitespace)
2524 rBuffer.append( t->GetChar());
2525 else
2526 rBuffer.append( ' ');
2530 else if( eOp >= ocInternalBegin && eOp <= ocInternalEnd )
2531 rBuffer.appendAscii( pInternal[ eOp - ocInternalBegin ] );
2532 else if (eOp == ocIntersect)
2534 // Nasty, ugly, horrific, terrifying...
2535 if (FormulaGrammar::isExcelSyntax( meGrammar))
2536 rBuffer.append(' ');
2537 else
2538 rBuffer.append( mxSymbols->getSymbol( eOp));
2540 else if( static_cast<sal_uInt16>(eOp) < mxSymbols->getSymbolCount()) // Keyword:
2541 rBuffer.append( mxSymbols->getSymbol( eOp));
2542 else
2544 SAL_WARN( "formula.core","unknown OpCode");
2545 rBuffer.append( GetNativeSymbol( ocErrName ));
2547 if( bNext )
2549 if (t->IsExternalRef())
2551 CreateStringFromExternal( rBuffer, pTokenP);
2553 else
2555 switch( t->GetType() )
2557 case svDouble:
2558 AppendDouble( rBuffer, t->GetDouble() );
2559 break;
2561 case svString:
2562 if( eOp == ocBad || eOp == ocStringXML )
2563 rBuffer.append( t->GetString().getString());
2564 else
2565 AppendString( rBuffer, t->GetString().getString() );
2566 break;
2567 case svSingleRef:
2568 CreateStringFromSingleRef( rBuffer, t);
2569 break;
2570 case svDoubleRef:
2571 CreateStringFromDoubleRef( rBuffer, t);
2572 break;
2573 case svMatrix:
2574 case svMatrixCell:
2575 CreateStringFromMatrix( rBuffer, t );
2576 break;
2578 case svIndex:
2579 CreateStringFromIndex( rBuffer, t );
2580 if (t->GetOpCode() == ocTableRef && bAllowArrAdvance && NeedsTableRefTransformation())
2582 // Suppress all TableRef related tokens, the resulting
2583 // range was written by CreateStringFromIndex().
2584 const FormulaToken* const p = maArrIterator.PeekNext();
2585 if (p && p->GetOpCode() == ocTableRefOpen)
2587 int nLevel = 0;
2590 t = maArrIterator.Next();
2591 if (!t)
2592 break;
2594 // Switch cases correspond with those in
2595 // ScCompiler::HandleTableRef()
2596 switch (t->GetOpCode())
2598 case ocTableRefOpen:
2599 ++nLevel;
2600 break;
2601 case ocTableRefClose:
2602 --nLevel;
2603 break;
2604 case ocTableRefItemAll:
2605 case ocTableRefItemHeaders:
2606 case ocTableRefItemData:
2607 case ocTableRefItemTotals:
2608 case ocTableRefItemThisRow:
2609 case ocSep:
2610 case ocPush:
2611 case ocRange:
2612 case ocSpaces:
2613 case ocWhitespace:
2614 break;
2615 default:
2616 nLevel = 0;
2617 bNext = false;
2619 } while (nLevel);
2622 break;
2623 case svExternal:
2625 // mapped or translated name of AddIns
2626 OUString aAddIn( t->GetExternal() );
2627 bool bMapped = mxSymbols->isPODF(); // ODF 1.1 directly uses programmatical name
2628 if (!bMapped && mxSymbols->hasExternals())
2630 ExternalHashMap::const_iterator iLook = mxSymbols->getReverseExternalHashMap().find( aAddIn);
2631 if (iLook != mxSymbols->getReverseExternalHashMap().end())
2633 aAddIn = (*iLook).second;
2634 bMapped = true;
2637 if (!bMapped && !mxSymbols->isEnglish())
2638 LocalizeString( aAddIn );
2639 rBuffer.append( aAddIn);
2641 break;
2642 case svError:
2643 AppendErrorConstant( rBuffer, t->GetError());
2644 break;
2645 case svByte:
2646 case svJump:
2647 case svFAP:
2648 case svMissing:
2649 case svSep:
2650 break; // Opcodes
2651 default:
2652 SAL_WARN("formula.core", "FormulaCompiler::GetStringFromToken: unknown token type " << t->GetType());
2653 } // of switch
2656 if( bSpaces )
2657 rBuffer.append( ' ');
2658 if ( bAllowArrAdvance )
2660 if( bNext )
2661 t = maArrIterator.Next();
2662 return t;
2664 return pTokenP;
2668 void FormulaCompiler::AppendDouble( OUStringBuffer& rBuffer, double fVal ) const
2670 if ( mxSymbols->isEnglishLocale() )
2672 ::rtl::math::doubleToUStringBuffer( rBuffer, fVal,
2673 rtl_math_StringFormat_Automatic,
2674 rtl_math_DecimalPlaces_Max, '.', true );
2676 else
2678 SvtSysLocale aSysLocale;
2679 ::rtl::math::doubleToUStringBuffer( rBuffer, fVal,
2680 rtl_math_StringFormat_Automatic,
2681 rtl_math_DecimalPlaces_Max,
2682 aSysLocale.GetLocaleData().getNumDecimalSep()[0],
2683 true );
2687 void FormulaCompiler::AppendBoolean( OUStringBuffer& rBuffer, bool bVal ) const
2689 rBuffer.append( mxSymbols->getSymbol( bVal ? ocTrue : ocFalse ) );
2692 void FormulaCompiler::AppendString( OUStringBuffer& rBuffer, const OUString & rStr )
2694 rBuffer.append( '"');
2695 if ( lcl_UnicodeStrChr( rStr.getStr(), '"' ) == nullptr )
2696 rBuffer.append( rStr );
2697 else
2699 OUString aStr = rStr.replaceAll( "\"", "\"\"" );
2700 rBuffer.append(aStr);
2702 rBuffer.append( '"');
2705 bool FormulaCompiler::NeedsTableRefTransformation() const
2707 // Currently only UI representations and OOXML export use Table structured
2708 // references. Not defined in ODFF.
2709 // Unnecessary to explicitly check for ODFF grammar as the ocTableRefOpen
2710 // symbol is not defined there.
2711 return mxSymbols->getSymbol( ocTableRefOpen).isEmpty() || FormulaGrammar::isPODF( meGrammar);
2714 void FormulaCompiler::UpdateSeparatorsNative(
2715 const OUString& rSep, const OUString& rArrayColSep, const OUString& rArrayRowSep )
2717 NonConstOpCodeMapPtr xSymbolsNative;
2718 lcl_fillNativeSymbols( xSymbolsNative);
2719 xSymbolsNative->putOpCode( rSep, ocSep, nullptr);
2720 xSymbolsNative->putOpCode( rArrayColSep, ocArrayColSep, nullptr);
2721 xSymbolsNative->putOpCode( rArrayRowSep, ocArrayRowSep, nullptr);
2724 void FormulaCompiler::ResetNativeSymbols()
2726 NonConstOpCodeMapPtr xSymbolsNative;
2727 lcl_fillNativeSymbols( xSymbolsNative, InitSymbols::DESTROY);
2728 lcl_fillNativeSymbols( xSymbolsNative);
2731 void FormulaCompiler::SetNativeSymbols( const OpCodeMapPtr& xMap )
2733 NonConstOpCodeMapPtr xSymbolsNative;
2734 lcl_fillNativeSymbols( xSymbolsNative);
2735 xSymbolsNative->copyFrom( *xMap );
2739 OpCode FormulaCompiler::NextToken()
2741 if( !GetToken() )
2742 return ocStop;
2743 OpCode eOp = mpToken->GetOpCode();
2744 // There must be an operator before a push
2745 if ( (eOp == ocPush || eOp == ocColRowNameAuto) &&
2746 !( (eLastOp == ocOpen) || (eLastOp == ocSep) ||
2747 (SC_OPCODE_START_BIN_OP <= eLastOp && eLastOp < SC_OPCODE_STOP_UN_OP)) )
2748 SetError( FormulaError::OperatorExpected);
2749 // Operator and Plus => operator
2750 if (eOp == ocAdd && (eLastOp == ocOpen || eLastOp == ocSep ||
2751 (SC_OPCODE_START_BIN_OP <= eLastOp && eLastOp < SC_OPCODE_STOP_UN_OP)))
2753 FormulaCompilerRecursionGuard aRecursionGuard( nRecursion );
2754 eOp = NextToken();
2756 else
2758 // Before an operator there must not be another operator, with the
2759 // exception of AND and OR.
2760 if ( eOp != ocAnd && eOp != ocOr &&
2761 (SC_OPCODE_START_BIN_OP <= eOp && eOp < SC_OPCODE_STOP_BIN_OP )
2762 && (eLastOp == ocOpen || eLastOp == ocSep ||
2763 (SC_OPCODE_START_BIN_OP <= eLastOp && eLastOp < SC_OPCODE_STOP_UN_OP)))
2765 SetError( FormulaError::VariableExpected);
2766 if ( bAutoCorrect && !pStack )
2768 if ( eOp == eLastOp || eLastOp == ocOpen )
2769 { // throw away duplicated operator
2770 aCorrectedSymbol.clear();
2771 bCorrected = true;
2773 else
2775 sal_Int32 nPos = aCorrectedFormula.getLength();
2776 if ( nPos )
2778 nPos--;
2779 sal_Unicode c = aCorrectedFormula[ nPos ];
2780 switch ( eOp )
2781 { // swap operators
2782 case ocGreater:
2783 if ( c == mxSymbols->getSymbolChar( ocEqual) )
2784 { // >= instead of =>
2785 aCorrectedFormula = aCorrectedFormula.replaceAt( nPos, 1,
2786 rtl::OUStringChar( mxSymbols->getSymbolChar(ocGreater) ) );
2787 aCorrectedSymbol = OUString(c);
2788 bCorrected = true;
2790 break;
2791 case ocLess:
2792 if ( c == mxSymbols->getSymbolChar( ocEqual) )
2793 { // <= instead of =<
2794 aCorrectedFormula = aCorrectedFormula.replaceAt( nPos, 1,
2795 rtl::OUStringChar( mxSymbols->getSymbolChar(ocLess) ) );
2796 aCorrectedSymbol = OUString(c);
2797 bCorrected = true;
2799 else if ( c == mxSymbols->getSymbolChar( ocGreater) )
2800 { // <> instead of ><
2801 aCorrectedFormula = aCorrectedFormula.replaceAt( nPos, 1,
2802 rtl::OUStringChar( mxSymbols->getSymbolChar(ocLess) ) );
2803 aCorrectedSymbol = OUString(c);
2804 bCorrected = true;
2806 break;
2807 case ocMul:
2808 if ( c == mxSymbols->getSymbolChar( ocSub) )
2809 { // *- instead of -*
2810 aCorrectedFormula = aCorrectedFormula.replaceAt( nPos, 1,
2811 rtl::OUStringChar( mxSymbols->getSymbolChar(ocMul) ) );
2812 aCorrectedSymbol = OUString(c);
2813 bCorrected = true;
2815 break;
2816 case ocDiv:
2817 if ( c == mxSymbols->getSymbolChar( ocSub) )
2818 { // /- instead of -/
2819 aCorrectedFormula = aCorrectedFormula.replaceAt( nPos, 1,
2820 rtl::OUStringChar( mxSymbols->getSymbolChar(ocDiv) ) );
2821 aCorrectedSymbol = OUString(c);
2822 bCorrected = true;
2824 break;
2825 default:
2826 ; // nothing
2832 // Nasty, ugly, horrific, terrifying... significant whitespace...
2833 if (eOp == ocSpaces && FormulaGrammar::isExcelSyntax( meGrammar))
2835 // Fake an intersection op as last op for the next round, but at
2836 // least roughly check if it could make sense at all.
2837 FormulaToken* pPrev = maArrIterator.PeekPrevNoSpaces();
2838 if (pPrev && isPotentialRangeType( pPrev, false, false))
2840 FormulaToken* pNext = maArrIterator.PeekNextNoSpaces();
2841 if (pNext && isPotentialRangeType( pNext, false, true))
2842 eLastOp = ocIntersect;
2843 else
2844 eLastOp = eOp;
2846 else
2847 eLastOp = eOp;
2849 else
2850 eLastOp = eOp;
2852 return eOp;
2855 void FormulaCompiler::PutCode( FormulaTokenRef& p )
2857 if( pc >= FORMULA_MAXTOKENS - 1 )
2859 if ( pc == FORMULA_MAXTOKENS - 1 )
2861 SAL_WARN("formula.core", "FormulaCompiler::PutCode - CodeOverflow with OpCode " << +p->GetOpCode());
2862 p = new FormulaByteToken( ocStop );
2863 p->IncRef();
2864 *pCode++ = p.get();
2865 ++pc;
2867 SetError( FormulaError::CodeOverflow);
2868 return;
2870 if (pArr->GetCodeError() != FormulaError::NONE && mbJumpCommandReorder)
2871 return;
2872 ForceArrayOperator( p);
2873 p->IncRef();
2874 *pCode++ = p.get();
2875 pc++;
2879 bool FormulaCompiler::HandleExternalReference( const FormulaToken& /*_aToken*/)
2881 return true;
2884 bool FormulaCompiler::HandleRange()
2886 return true;
2889 bool FormulaCompiler::HandleColRowName()
2891 return true;
2894 bool FormulaCompiler::HandleDbData()
2896 return true;
2899 bool FormulaCompiler::HandleTableRef()
2901 return true;
2904 void FormulaCompiler::CreateStringFromSingleRef( OUStringBuffer& /*rBuffer*/, const FormulaToken* /*pToken*/) const
2908 void FormulaCompiler::CreateStringFromDoubleRef( OUStringBuffer& /*rBuffer*/, const FormulaToken* /*pToken*/) const
2912 void FormulaCompiler::CreateStringFromIndex( OUStringBuffer& /*rBuffer*/, const FormulaToken* /*pToken*/) const
2916 void FormulaCompiler::CreateStringFromMatrix( OUStringBuffer& /*rBuffer*/, const FormulaToken* /*pToken*/) const
2920 void FormulaCompiler::CreateStringFromExternal( OUStringBuffer& /*rBuffer*/, const FormulaToken* /*pToken*/) const
2924 void FormulaCompiler::LocalizeString( OUString& /*rName*/ ) const
2928 formula::ParamClass FormulaCompiler::GetForceArrayParameter( const FormulaToken* /*pToken*/, sal_uInt16 /*nParam*/ ) const
2930 return ParamClass::Unknown;
2933 void FormulaCompiler::ForceArrayOperator( FormulaTokenRef const & rCurr )
2935 if (pCurrentFactorToken.get() == rCurr.get())
2936 return;
2938 const OpCode eOp = rCurr->GetOpCode();
2939 const StackVar eType = rCurr->GetType();
2940 const bool bInlineArray = (eOp == ocPush && eType == svMatrix);
2942 if (!bInlineArray)
2944 if (rCurr->GetInForceArray() != ParamClass::Unknown)
2945 // Already set, unnecessary to evaluate again. This happens by calls to
2946 // CurrentFactor::operator=() while descending through Factor() and
2947 // then ascending back (and down and up, ...),
2948 // CheckSetForceArrayParameter() and later PutCode().
2949 return;
2951 if (!(eOp != ocPush && (eType == svByte || eType == svJump)))
2952 return;
2955 // Return class for inline arrays and functions returning array/matrix.
2956 // It's somewhat unclear what Excel actually does there and in
2957 // ECMA-376-1:2016 OOXML mentions "call to ... shall be an array formula"
2958 // only for FREQUENCY() and TRANSPOSE() but not for any other function
2959 // returning array/matrix or inline arrays, though for the latter has one
2960 // example in 18.17.2 Syntax:
2961 // "SUM(SQRT({1,2,3,4})) returns 6.14 when entered normally". However,
2962 // these need to be treated similar but not as ParamClass::ForceArray
2963 // (which would contradict the example in
2964 // https://bugs.documentfoundation.org/show_bug.cgi?id=122301#c19 and A6 of
2965 // https://bugs.documentfoundation.org/show_bug.cgi?id=133260#c10 ).
2966 // See also
2967 // commit d0ded163d8e93dc5b10d7a7c9bdab1d0a6a50bac
2968 // commit 5413c8871dec08eff19f514f5f391b946a45c86c
2969 constexpr ParamClass eArrayReturn = ParamClass::ForceArrayReturn;
2971 if (bInlineArray)
2973 // rCurr->SetInForceArray() can not be used with ocPush, but ocPush
2974 // with svMatrix has an implicit ParamClass::ForceArrayReturn.
2975 if (nCurrentFactorParam > 0 && pCurrentFactorToken
2976 && pCurrentFactorToken->GetInForceArray() == ParamClass::Unknown
2977 && GetForceArrayParameter( pCurrentFactorToken.get(), static_cast<sal_uInt16>(nCurrentFactorParam - 1))
2978 == ParamClass::Value)
2980 // Propagate to caller as if a function returning an array/matrix
2981 // was called (see also below).
2982 pCurrentFactorToken->SetInForceArray( eArrayReturn);
2984 return;
2987 if (!pCurrentFactorToken)
2989 if (mbMatrixFlag)
2991 // An array/matrix formula acts as ForceArray on all top level
2992 // operators and function calls, so that can be inherited properly
2993 // below.
2994 rCurr->SetInForceArray( ParamClass::ForceArray);
2996 else if (pc >= 2 && SC_OPCODE_START_BIN_OP <= eOp && eOp < SC_OPCODE_STOP_BIN_OP)
2998 // Binary operators are not functions followed by arguments
2999 // and need some peeking into RPN to inspect their operands.
3000 // Note that array context is not forced if only one
3001 // of the operands is an array like "={1;2}+A1:A2" returns #VALUE!
3002 // if entered in column A and not input in array mode, because it
3003 // involves a range reference with an implicit intersection. Check
3004 // both arguments are arrays, or the other is ocPush without ranges
3005 // for "={1;2}+3" or "={1;2}+A1".
3006 // Note this does not catch "={1;2}+ABS(A1)" that could be forced
3007 // to array, user still has to close in array mode.
3008 // The IsMatrixFunction() is only necessary because not all
3009 // functions returning matrix have ForceArrayReturn (yet?), see
3010 // OOXML comment above.
3012 const OpCode eOp1 = pCode[-1]->GetOpCode();
3013 const OpCode eOp2 = pCode[-2]->GetOpCode();
3014 const bool b1 = (pCode[-1]->GetInForceArray() != ParamClass::Unknown || IsMatrixFunction(eOp1));
3015 const bool b2 = (pCode[-2]->GetInForceArray() != ParamClass::Unknown || IsMatrixFunction(eOp2));
3016 if ((b1 && b2)
3017 || (b1 && eOp2 == ocPush && pCode[-2]->GetType() != svDoubleRef)
3018 || (b2 && eOp1 == ocPush && pCode[-1]->GetType() != svDoubleRef))
3020 rCurr->SetInForceArray( eArrayReturn);
3023 else if (pc >= 1 && SC_OPCODE_START_UN_OP <= eOp && eOp < SC_OPCODE_STOP_UN_OP)
3025 // Similar for unary operators.
3026 if (pCode[-1]->GetInForceArray() != ParamClass::Unknown || IsMatrixFunction(pCode[-1]->GetOpCode()))
3028 rCurr->SetInForceArray( eArrayReturn);
3031 return;
3034 // Inherited parameter class.
3035 const formula::ParamClass eForceType = pCurrentFactorToken->GetInForceArray();
3036 if (eForceType == ParamClass::ForceArray || eForceType == ParamClass::ReferenceOrRefArray)
3038 // ReferenceOrRefArray was set only if in ForceArray context already,
3039 // it is valid for the one function only to indicate the preferred
3040 // return type. Propagate as ForceArray if not another parameter
3041 // handling ReferenceOrRefArray.
3042 if (nCurrentFactorParam > 0
3043 && (GetForceArrayParameter( pCurrentFactorToken.get(), static_cast<sal_uInt16>(nCurrentFactorParam - 1))
3044 == ParamClass::ReferenceOrRefArray))
3045 rCurr->SetInForceArray( ParamClass::ReferenceOrRefArray);
3046 else
3047 rCurr->SetInForceArray( ParamClass::ForceArray);
3048 return;
3050 else if (eForceType == ParamClass::ReferenceOrForceArray)
3052 // Inherit further only if the return class of the nested function is
3053 // not Reference. Else flag as suppressed.
3054 if (GetForceArrayParameter( rCurr.get(), SAL_MAX_UINT16) != ParamClass::Reference)
3055 rCurr->SetInForceArray( eForceType);
3056 else
3057 rCurr->SetInForceArray( ParamClass::SuppressedReferenceOrForceArray);
3058 return;
3061 if (nCurrentFactorParam <= 0)
3062 return;
3064 // Actual current parameter's class.
3065 const formula::ParamClass eParamType = GetForceArrayParameter(
3066 pCurrentFactorToken.get(), static_cast<sal_uInt16>(nCurrentFactorParam - 1));
3067 if (eParamType == ParamClass::ForceArray)
3068 rCurr->SetInForceArray( eParamType);
3069 else if (eParamType == ParamClass::ReferenceOrForceArray)
3071 if (GetForceArrayParameter( rCurr.get(), SAL_MAX_UINT16) != ParamClass::Reference)
3072 rCurr->SetInForceArray( eParamType);
3073 else
3074 rCurr->SetInForceArray( formula::ParamClass::SuppressedReferenceOrForceArray);
3077 // Propagate a ForceArrayReturn to caller if the called function
3078 // returns one and the caller so far does not have a stronger array
3079 // mode set and expects a scalar value for this parameter.
3080 if (eParamType == ParamClass::Value && pCurrentFactorToken->GetInForceArray() == ParamClass::Unknown)
3082 if (IsMatrixFunction( eOp))
3083 pCurrentFactorToken->SetInForceArray( eArrayReturn);
3084 else if (GetForceArrayParameter( rCurr.get(), SAL_MAX_UINT16) == ParamClass::ForceArrayReturn)
3085 pCurrentFactorToken->SetInForceArray( ParamClass::ForceArrayReturn);
3089 void FormulaCompiler::CheckSetForceArrayParameter( FormulaTokenRef const & rCurr, sal_uInt8 nParam )
3091 if (!pCurrentFactorToken)
3092 return;
3094 nCurrentFactorParam = nParam + 1;
3096 ForceArrayOperator( rCurr);
3099 void FormulaCompiler::PushTokenArray( FormulaTokenArray* pa, bool bTemp )
3101 if ( bAutoCorrect && !pStack )
3102 { // don't merge stacked subroutine code into entered formula
3103 aCorrectedFormula += aCorrectedSymbol;
3104 aCorrectedSymbol.clear();
3106 FormulaArrayStack* p = new FormulaArrayStack;
3107 p->pNext = pStack;
3108 p->pArr = pArr;
3109 p->nIndex = maArrIterator.GetIndex();
3110 p->mpLastToken = mpLastToken;
3111 p->bTemp = bTemp;
3112 pStack = p;
3113 pArr = pa;
3114 maArrIterator = FormulaTokenArrayPlainIterator( *pArr );
3117 } // namespace formula
3119 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */