bump product version to 7.2.5.1
[LibreOffice.git] / formula / source / core / api / FormulaCompiler.cxx
blobbe5ce09d132f9b1463dca7682dbe3d058a24d916
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 <formula/FormulaCompiler.hxx>
22 #include <formula/errorcodes.hxx>
23 #include <formula/token.hxx>
24 #include <formula/tokenarray.hxx>
25 #include <core_resource.hxx>
26 #include <core_resource.hrc>
28 #include <osl/mutex.hxx>
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>
40 namespace formula
42 using namespace ::com::sun::star;
44 static const char* pInternal[2] = { "TTT", "__DEBUG_VAR" };
46 namespace {
48 class FormulaCompilerRecursionGuard
50 private:
51 short& rRecursion;
52 public:
53 explicit FormulaCompilerRecursionGuard( short& rRec )
54 : rRecursion( rRec ) { ++rRecursion; }
55 ~FormulaCompilerRecursionGuard() { --rRecursion; }
58 SvNumFormatType lcl_GetRetFormat( OpCode eOpCode )
60 switch (eOpCode)
62 case ocEqual:
63 case ocNotEqual:
64 case ocLess:
65 case ocGreater:
66 case ocLessEqual:
67 case ocGreaterEqual:
68 case ocAnd:
69 case ocOr:
70 case ocXor:
71 case ocNot:
72 case ocTrue:
73 case ocFalse:
74 case ocIsEmpty:
75 case ocIsString:
76 case ocIsNonString:
77 case ocIsLogical:
78 case ocIsRef:
79 case ocIsValue:
80 case ocIsFormula:
81 case ocIsNA:
82 case ocIsErr:
83 case ocIsError:
84 case ocIsEven:
85 case ocIsOdd:
86 case ocExact:
87 return SvNumFormatType::LOGICAL;
88 case ocGetActDate:
89 case ocGetDate:
90 case ocEasterSunday :
91 return SvNumFormatType::DATE;
92 case ocGetActTime:
93 return SvNumFormatType::DATETIME;
94 case ocGetTime:
95 return SvNumFormatType::TIME;
96 case ocNPV:
97 case ocPV:
98 case ocSYD:
99 case ocDDB:
100 case ocDB:
101 case ocVBD:
102 case ocSLN:
103 case ocPMT:
104 case ocFV:
105 case ocIpmt:
106 case ocPpmt:
107 case ocCumIpmt:
108 case ocCumPrinc:
109 return SvNumFormatType::CURRENCY;
110 case ocRate:
111 case ocIRR:
112 case ocMIRR:
113 case ocRRI:
114 case ocEffect:
115 case ocNominal:
116 case ocPercentSign:
117 return SvNumFormatType::PERCENT;
118 default:
119 return SvNumFormatType::NUMBER;
123 void lclPushOpCodeMapEntry( ::std::vector< sheet::FormulaOpCodeMapEntry >& rVec,
124 const OUString* pTable, sal_uInt16 nOpCode )
126 sheet::FormulaOpCodeMapEntry aEntry;
127 aEntry.Token.OpCode = nOpCode;
128 aEntry.Name = pTable[nOpCode];
129 rVec.push_back( aEntry);
132 void lclPushOpCodeMapEntries( ::std::vector< sheet::FormulaOpCodeMapEntry >& rVec,
133 const OUString* pTable, sal_uInt16 nOpCodeBeg, sal_uInt16 nOpCodeEnd )
135 for (sal_uInt16 nOpCode = nOpCodeBeg; nOpCode < nOpCodeEnd; ++nOpCode)
136 lclPushOpCodeMapEntry( rVec, pTable, nOpCode );
139 void lclPushOpCodeMapEntries( ::std::vector< sheet::FormulaOpCodeMapEntry >& rVec,
140 const OUString* pTable, const sal_uInt16* pnOpCodes, size_t nCount )
142 for (const sal_uInt16* pnEnd = pnOpCodes + nCount; pnOpCodes < pnEnd; ++pnOpCodes)
143 lclPushOpCodeMapEntry( rVec, pTable, *pnOpCodes );
146 CharClass* createCharClassIfNonEnglishUI()
148 const LanguageTag& rLanguageTag( Application::GetSettings().GetUILanguageTag());
149 if (rLanguageTag.getLanguage() == "en")
150 return nullptr;
151 return new CharClass( ::comphelper::getProcessComponentContext(), rLanguageTag);
154 class OpCodeList
156 public:
158 OpCodeList(bool bLocalized, const std::pair<const char*, int>* pSymbols, const FormulaCompiler::NonConstOpCodeMapPtr&,
159 FormulaCompiler::SeparatorType = FormulaCompiler::SeparatorType::SEMICOLON_BASE );
161 private:
162 bool getOpCodeString( OUString& rStr, sal_uInt16 nOp );
163 void putDefaultOpCode( const FormulaCompiler::NonConstOpCodeMapPtr& xMap, sal_uInt16 nOp, const CharClass* pCharClass );
165 private:
166 FormulaCompiler::SeparatorType meSepType;
167 const std::pair<const char*, int>* mpSymbols;
168 bool mbLocalized;
171 OpCodeList::OpCodeList(bool bLocalized, const std::pair<const char*, int>* pSymbols, const FormulaCompiler::NonConstOpCodeMapPtr& xMap,
172 FormulaCompiler::SeparatorType eSepType)
173 : meSepType(eSepType)
174 , mpSymbols(pSymbols)
175 , mbLocalized(bLocalized)
177 std::unique_ptr<CharClass> xCharClass( xMap->isEnglish() ? nullptr : createCharClassIfNonEnglishUI());
178 const CharClass* pCharClass = xCharClass.get();
179 if (meSepType == FormulaCompiler::SeparatorType::RESOURCE_BASE)
181 for (sal_uInt16 i = 0; i <= SC_OPCODE_LAST_OPCODE_ID; ++i)
183 putDefaultOpCode( xMap, i, pCharClass);
186 else
188 for (sal_uInt16 i = 0; i <= SC_OPCODE_LAST_OPCODE_ID; ++i)
190 OUString aOpStr;
191 if ( getOpCodeString( aOpStr, i) )
192 xMap->putOpCode( aOpStr, OpCode(i), pCharClass);
193 else
194 putDefaultOpCode( xMap, i, pCharClass);
199 bool OpCodeList::getOpCodeString( OUString& rStr, sal_uInt16 nOp )
201 switch (nOp)
203 case SC_OPCODE_SEP:
205 if (meSepType == FormulaCompiler::SeparatorType::SEMICOLON_BASE)
207 rStr = ";";
208 return true;
211 break;
212 case SC_OPCODE_ARRAY_COL_SEP:
214 if (meSepType == FormulaCompiler::SeparatorType::SEMICOLON_BASE)
216 rStr = ";";
217 return true;
220 break;
221 case SC_OPCODE_ARRAY_ROW_SEP:
223 if (meSepType == FormulaCompiler::SeparatorType::SEMICOLON_BASE)
225 rStr = "|";
226 return true;
229 break;
232 return false;
235 void OpCodeList::putDefaultOpCode( const FormulaCompiler::NonConstOpCodeMapPtr& xMap, sal_uInt16 nOp,
236 const CharClass* pCharClass )
238 const char* pKey = nullptr;
239 for (const std::pair<const char*, int>* pSymbol = mpSymbols; pSymbol->first; ++pSymbol)
241 if (nOp == pSymbol->second)
243 pKey = pSymbol->first;
244 break;
247 if (!pKey)
248 return;
249 OUString sKey = !mbLocalized ? OUString::createFromAscii(pKey) : ForResId(pKey);
250 xMap->putOpCode(sKey, OpCode(nOp), pCharClass);
253 // static
254 const sal_Unicode* lcl_UnicodeStrChr( const sal_Unicode* pStr, sal_Unicode c )
256 if ( !pStr )
257 return nullptr;
258 while ( *pStr )
260 if ( *pStr == c )
261 return pStr;
262 pStr++;
264 return nullptr;
267 struct OpCodeMapData
269 FormulaCompiler::NonConstOpCodeMapPtr mxSymbolMap;
270 osl::Mutex maMtx;
274 bool isPotentialRangeLeftOp( OpCode eOp )
276 switch (eOp)
278 case ocClose:
279 return true;
280 default:
281 return false;
285 bool isRangeResultFunction( OpCode eOp )
287 switch (eOp)
289 case ocIndirect:
290 case ocOffset:
291 return true;
292 default:
293 return false;
297 bool isRangeResultOpCode( OpCode eOp )
299 switch (eOp)
301 case ocRange:
302 case ocUnion:
303 case ocIntersect:
304 case ocIndirect:
305 case ocOffset:
306 return true;
307 default:
308 return false;
313 @param pToken
314 MUST be a valid token, caller has to ensure.
316 @param bRight
317 If bRPN==false, bRight==false means opcodes for left side are
318 checked, bRight==true means opcodes for right side. If bRPN==true
319 it doesn't matter except for the ocSep converted to ocUnion case.
321 bool isPotentialRangeType( FormulaToken const * pToken, bool bRPN, bool bRight )
323 switch (pToken->GetType())
325 case svByte: // could be range result, but only a few
326 if (bRPN)
327 return isRangeResultOpCode( pToken->GetOpCode());
328 else if (bRight)
329 return isRangeResultFunction( pToken->GetOpCode());
330 else
331 return isPotentialRangeLeftOp( pToken->GetOpCode());
332 case svSingleRef:
333 case svDoubleRef:
334 case svIndex: // could be range
335 //case svRefList: // um..what?
336 case svExternalSingleRef:
337 case svExternalDoubleRef:
338 case svExternalName: // could be range
339 return true;
340 case svSep:
341 // A special case if a previous ocSep was converted to ocUnion it
342 // stays svSep instead of svByte.
343 return bRPN && !bRight && pToken->GetOpCode() == ocUnion;
344 default:
345 // Separators are not part of RPN and right opcodes need to be
346 // other StackVar types or functions and thus svByte.
347 return !bRPN && !bRight && isPotentialRangeLeftOp( pToken->GetOpCode());
351 bool isIntersectable( FormulaToken** pCode1, FormulaToken** pCode2 )
353 FormulaToken* pToken1 = *pCode1;
354 FormulaToken* pToken2 = *pCode2;
355 if (pToken1 && pToken2)
356 return isPotentialRangeType( pToken1, true, false) && isPotentialRangeType( pToken2, true, true);
357 return false;
360 bool isAdjacentRpnEnd( sal_uInt16 nPC,
361 FormulaToken const * const * const pCode,
362 FormulaToken const * const * const pCode1,
363 FormulaToken const * const * const pCode2 )
365 return nPC >= 2 && pCode1 && pCode2 &&
366 (pCode2 - pCode1 == 1) && (pCode - pCode2 == 1) &&
367 (*pCode1 != nullptr) && (*pCode2 != nullptr);
370 bool isAdjacentOrGapRpnEnd( sal_uInt16 nPC,
371 FormulaToken const * const * const pCode,
372 FormulaToken const * const * const pCode1,
373 FormulaToken const * const * const pCode2 )
375 return nPC >= 2 && pCode1 && pCode2 &&
376 (pCode2 > pCode1) && (pCode - pCode2 == 1) &&
377 (*pCode1 != nullptr) && (*pCode2 != nullptr);
381 } // namespace
384 void FormulaCompiler::OpCodeMap::putExternal( const OUString & rSymbol, const OUString & rAddIn )
386 // Different symbols may map to the same AddIn, but the same AddIn may not
387 // map to different symbols, the first pair wins. Same symbol of course may
388 // not map to different AddIns, again the first pair wins and also the
389 // AddIn->symbol mapping is not inserted in other cases.
390 bool bOk = maExternalHashMap.emplace(rSymbol, rAddIn).second;
391 SAL_WARN_IF( !bOk, "formula.core", "OpCodeMap::putExternal: symbol not inserted, " << rSymbol << " -> " << rAddIn);
392 if (bOk)
394 bOk = maReverseExternalHashMap.emplace(rAddIn, rSymbol).second;
395 // Failed insertion of the AddIn is ok for different symbols mapping to
396 // the same AddIn. Make this INFO only.
397 SAL_INFO_IF( !bOk, "formula.core", "OpCodeMap::putExternal: AddIn not inserted, " << rAddIn << " -> " << rSymbol);
401 void FormulaCompiler::OpCodeMap::putExternalSoftly( const OUString & rSymbol, const OUString & rAddIn )
403 bool bOk = maReverseExternalHashMap.emplace(rAddIn, rSymbol).second;
404 if (bOk)
405 maExternalHashMap.emplace(rSymbol, rAddIn);
408 uno::Sequence< sheet::FormulaToken > FormulaCompiler::OpCodeMap::createSequenceOfFormulaTokens(
409 const FormulaCompiler& rCompiler, const uno::Sequence< OUString >& rNames ) const
411 const sal_Int32 nLen = rNames.getLength();
412 uno::Sequence< sheet::FormulaToken > aTokens( nLen);
413 sheet::FormulaToken* pToken = aTokens.getArray();
414 OUString const * pName = rNames.getConstArray();
415 OUString const * const pStop = pName + nLen;
416 for ( ; pName < pStop; ++pName, ++pToken)
418 OpCodeHashMap::const_iterator iLook( maHashMap.find( *pName));
419 if (iLook != maHashMap.end())
420 pToken->OpCode = (*iLook).second;
421 else
423 OUString aIntName;
424 if (hasExternals())
426 ExternalHashMap::const_iterator iExt( maExternalHashMap.find( *pName));
427 if (iExt != maExternalHashMap.end())
428 aIntName = (*iExt).second;
429 // Check for existence not needed here, only name-mapping is of
430 // interest.
432 if (aIntName.isEmpty())
433 aIntName = rCompiler.FindAddInFunction(*pName, !isEnglish()); // bLocalFirst=false for english
434 if (aIntName.isEmpty())
435 pToken->OpCode = getOpCodeUnknown();
436 else
438 pToken->OpCode = ocExternal;
439 pToken->Data <<= aIntName;
443 return aTokens;
446 uno::Sequence< sheet::FormulaOpCodeMapEntry > FormulaCompiler::OpCodeMap::createSequenceOfAvailableMappings(
447 const FormulaCompiler& rCompiler, const sal_Int32 nGroups ) const
449 using namespace sheet;
451 // Unfortunately uno::Sequence can't grow without cumbersome reallocs. As
452 // we don't know in advance how many elements it will have we use a
453 // temporary vector to add elements and then copy to Sequence :-(
454 ::std::vector< FormulaOpCodeMapEntry > aVec;
456 if (nGroups == FormulaMapGroup::SPECIAL)
458 // Use specific order, keep in sync with
459 // offapi/com/sun/star/sheet/FormulaMapGroupSpecialOffset.idl
460 static const struct
462 sal_Int32 nOff;
463 OpCode eOp;
464 } aMap[] = {
465 { FormulaMapGroupSpecialOffset::PUSH , ocPush } ,
466 { FormulaMapGroupSpecialOffset::CALL , ocCall } ,
467 { FormulaMapGroupSpecialOffset::STOP , ocStop } ,
468 { FormulaMapGroupSpecialOffset::EXTERNAL , ocExternal } ,
469 { FormulaMapGroupSpecialOffset::NAME , ocName } ,
470 { FormulaMapGroupSpecialOffset::NO_NAME , ocNoName } ,
471 { FormulaMapGroupSpecialOffset::MISSING , ocMissing } ,
472 { FormulaMapGroupSpecialOffset::BAD , ocBad } ,
473 { FormulaMapGroupSpecialOffset::SPACES , ocSpaces } ,
474 { FormulaMapGroupSpecialOffset::MAT_REF , ocMatRef } ,
475 { FormulaMapGroupSpecialOffset::DB_AREA , ocDBArea } ,
476 /* TODO: { FormulaMapGroupSpecialOffset::TABLE_REF , ocTableRef } , */
477 { FormulaMapGroupSpecialOffset::MACRO , ocMacro } ,
478 { FormulaMapGroupSpecialOffset::COL_ROW_NAME , ocColRowName }
480 const size_t nCount = SAL_N_ELEMENTS(aMap);
481 // Preallocate vector elements.
482 FormulaOpCodeMapEntry aEntry;
483 aEntry.Token.OpCode = getOpCodeUnknown();
484 aVec.resize(nCount, aEntry);
486 for (auto& i : aMap)
488 size_t nIndex = static_cast< size_t >( i.nOff );
489 if (aVec.size() <= nIndex)
491 // The offsets really should be aligned with the size, so if
492 // the vector was preallocated above this code to resize it is
493 // just a measure in case the table isn't in sync with the API,
494 // usually it isn't executed.
495 aEntry.Token.OpCode = getOpCodeUnknown();
496 aVec.resize( nIndex + 1, aEntry );
498 aEntry.Token.OpCode = i.eOp;
499 aVec[nIndex] = aEntry;
502 else
504 /* FIXME: Once we support error constants in formulas we'll need a map
505 * group for that, e.g. FormulaMapGroup::ERROR_CONSTANTS, and fill
506 * SC_OPCODE_START_ERRORS to SC_OPCODE_STOP_ERRORS. */
508 // Anything else but SPECIAL.
509 if ((nGroups & FormulaMapGroup::SEPARATORS) != 0)
511 static const sal_uInt16 aOpCodes[] = {
512 SC_OPCODE_OPEN,
513 SC_OPCODE_CLOSE,
514 SC_OPCODE_SEP,
516 lclPushOpCodeMapEntries( aVec, mpTable.get(), aOpCodes, SAL_N_ELEMENTS(aOpCodes) );
518 if ((nGroups & FormulaMapGroup::ARRAY_SEPARATORS) != 0)
520 static const sal_uInt16 aOpCodes[] = {
521 SC_OPCODE_ARRAY_OPEN,
522 SC_OPCODE_ARRAY_CLOSE,
523 SC_OPCODE_ARRAY_ROW_SEP,
524 SC_OPCODE_ARRAY_COL_SEP
526 lclPushOpCodeMapEntries( aVec, mpTable.get(), aOpCodes, SAL_N_ELEMENTS(aOpCodes) );
528 if ((nGroups & FormulaMapGroup::UNARY_OPERATORS) != 0)
530 // Due to the nature of the percent operator following its operand
531 // it isn't sorted into unary operators for compiler interna.
532 lclPushOpCodeMapEntry( aVec, mpTable.get(), ocPercentSign );
533 // "+" can be used as unary operator too, push only if binary group is not set
534 if ((nGroups & FormulaMapGroup::BINARY_OPERATORS) == 0)
535 lclPushOpCodeMapEntry( aVec, mpTable.get(), ocAdd );
536 // regular unary operators
537 for (sal_uInt16 nOp = SC_OPCODE_START_UN_OP; nOp < SC_OPCODE_STOP_UN_OP && nOp < mnSymbols; ++nOp)
539 lclPushOpCodeMapEntry( aVec, mpTable.get(), nOp );
542 if ((nGroups & FormulaMapGroup::BINARY_OPERATORS) != 0)
544 for (sal_uInt16 nOp = SC_OPCODE_START_BIN_OP; nOp < SC_OPCODE_STOP_BIN_OP && nOp < mnSymbols; ++nOp)
546 switch (nOp)
548 // AND and OR in fact are functions but for legacy reasons
549 // are sorted into binary operators for compiler interna.
550 case SC_OPCODE_AND :
551 case SC_OPCODE_OR :
552 break; // nothing,
553 default:
554 lclPushOpCodeMapEntry( aVec, mpTable.get(), nOp );
558 if ((nGroups & FormulaMapGroup::FUNCTIONS) != 0)
560 // Function names are not consecutive, skip the gaps between
561 // functions with no parameter, functions with 1 parameter
562 lclPushOpCodeMapEntries( aVec, mpTable.get(), SC_OPCODE_START_NO_PAR,
563 ::std::min< sal_uInt16 >( SC_OPCODE_STOP_NO_PAR, mnSymbols ) );
564 lclPushOpCodeMapEntries( aVec, mpTable.get(), SC_OPCODE_START_1_PAR,
565 ::std::min< sal_uInt16 >( SC_OPCODE_STOP_1_PAR, mnSymbols ) );
566 // Additional functions not within range of functions.
567 static const sal_uInt16 aOpCodes[] = {
568 SC_OPCODE_IF,
569 SC_OPCODE_IF_ERROR,
570 SC_OPCODE_IF_NA,
571 SC_OPCODE_CHOOSE,
572 SC_OPCODE_AND,
573 SC_OPCODE_OR
575 lclPushOpCodeMapEntries( aVec, mpTable.get(), aOpCodes, SAL_N_ELEMENTS(aOpCodes) );
576 // functions with 2 or more parameters.
577 for (sal_uInt16 nOp = SC_OPCODE_START_2_PAR; nOp < SC_OPCODE_STOP_2_PAR && nOp < mnSymbols; ++nOp)
579 switch (nOp)
581 // NO_NAME is in SPECIAL.
582 case SC_OPCODE_NO_NAME :
583 break; // nothing,
584 default:
585 lclPushOpCodeMapEntry( aVec, mpTable.get(), nOp );
588 // If AddIn functions are present in this mapping, use them, and only those.
589 if (hasExternals())
591 for (auto const& elem : maExternalHashMap)
593 FormulaOpCodeMapEntry aEntry;
594 aEntry.Name = elem.first;
595 aEntry.Token.Data <<= elem.second;
596 aEntry.Token.OpCode = ocExternal;
597 aVec.push_back( aEntry);
600 else
602 rCompiler.fillAddInToken( aVec, isEnglish());
606 return uno::Sequence< FormulaOpCodeMapEntry >(aVec.data(), aVec.size());
610 void FormulaCompiler::OpCodeMap::putOpCode( const OUString & rStr, const OpCode eOp, const CharClass* pCharClass )
612 if (0 < eOp && sal_uInt16(eOp) < mnSymbols)
614 bool bPutOp = mpTable[eOp].isEmpty();
615 bool bRemoveFromMap = false;
616 if (!bPutOp)
618 switch (eOp)
620 // These OpCodes are meant to overwrite and also remove an
621 // existing mapping.
622 case ocCurrency:
623 bPutOp = true;
624 bRemoveFromMap = true;
625 break;
626 // These separator OpCodes are meant to overwrite and also
627 // remove an existing mapping if it is not used for one of the
628 // other separators.
629 case ocArrayColSep:
630 bPutOp = true;
631 bRemoveFromMap = (mpTable[ocArrayRowSep] != mpTable[eOp] && mpTable[ocSep] != mpTable[eOp]);
632 break;
633 case ocArrayRowSep:
634 bPutOp = true;
635 bRemoveFromMap = (mpTable[ocArrayColSep] != mpTable[eOp] && mpTable[ocSep] != mpTable[eOp]);
636 break;
637 // For ocSep keep the ";" in map but remove any other if it is
638 // not used for ocArrayColSep or ocArrayRowSep.
639 case ocSep:
640 bPutOp = true;
641 bRemoveFromMap = (mpTable[eOp] != ";" &&
642 mpTable[ocArrayColSep] != mpTable[eOp] &&
643 mpTable[ocArrayRowSep] != mpTable[eOp]);
644 break;
645 // These OpCodes are known to be duplicates in the Excel
646 // external API mapping because of different parameter counts
647 // in different BIFF versions. Names are identical and entries
648 // are ignored.
649 case ocLinest:
650 case ocTrend:
651 case ocLogest:
652 case ocGrowth:
653 case ocTrunc:
654 case ocFixed:
655 case ocGetDayOfWeek:
656 case ocHLookup:
657 case ocVLookup:
658 case ocGetDiffDate360:
659 if (rStr == mpTable[eOp])
660 return;
661 [[fallthrough]];
662 // These OpCodes are known to be added to an existing mapping,
663 // but only for the OOXML external API mapping. This is *not*
664 // FormulaLanguage::OOXML. Keep the first
665 // (correct) definition for the OpCode, all following are
666 // additional alias entries in the map.
667 case ocErrorType:
668 case ocMultiArea:
669 case ocBackSolver:
670 case ocEasterSunday:
671 case ocCurrent:
672 case ocStyle:
673 if (mbEnglish &&
674 FormulaGrammar::extractFormulaLanguage( meGrammar) == FormulaGrammar::GRAM_EXTERNAL)
676 // Both bPutOp and bRemoveFromMap stay false.
677 break;
679 [[fallthrough]];
680 default:
681 SAL_WARN("formula.core",
682 "OpCodeMap::putOpCode: reusing OpCode " << static_cast<sal_uInt16>(eOp)
683 << ", replacing '" << mpTable[eOp] << "' with '" << rStr << "' in "
684 << (mbEnglish ? "" : "non-") << "English map 0x" << ::std::hex << meGrammar);
688 // Case preserving opcode -> string, upper string -> opcode
689 if (bRemoveFromMap)
691 OUString aUpper( pCharClass ? pCharClass->uppercase( mpTable[eOp]) : rStr.toAsciiUpperCase());
692 // Ensure we remove a mapping only for the requested OpCode.
693 OpCodeHashMap::const_iterator it( maHashMap.find( aUpper));
694 if (it != maHashMap.end() && (*it).second == eOp)
695 maHashMap.erase( it);
697 if (bPutOp)
698 mpTable[eOp] = rStr;
699 OUString aUpper( pCharClass ? pCharClass->uppercase( rStr) : rStr.toAsciiUpperCase());
700 maHashMap.emplace(aUpper, eOp);
702 else
704 SAL_WARN( "formula.core", "OpCodeMap::putOpCode: OpCode out of range");
709 FormulaCompiler::FormulaCompiler( FormulaTokenArray& rArr, bool bComputeII, bool bMatrixFlag )
711 nCurrentFactorParam(0),
712 pArr( &rArr ),
713 maArrIterator( rArr ),
714 pCode( nullptr ),
715 pStack( nullptr ),
716 eLastOp( ocPush ),
717 nRecursion( 0 ),
718 nNumFmt( SvNumFormatType::UNDEFINED ),
719 pc( 0 ),
720 meGrammar( formula::FormulaGrammar::GRAM_UNSPECIFIED ),
721 bAutoCorrect( false ),
722 bCorrected( false ),
723 glSubTotal( false ),
724 needsRPNTokenCheck( false ),
725 mbJumpCommandReorder(true),
726 mbStopOnError(true),
727 mbComputeII(bComputeII),
728 mbMatrixFlag(bMatrixFlag)
732 FormulaTokenArray FormulaCompiler::smDummyTokenArray;
734 FormulaCompiler::FormulaCompiler(bool bComputeII, bool bMatrixFlag)
736 nCurrentFactorParam(0),
737 pArr( nullptr ),
738 maArrIterator( smDummyTokenArray ),
739 pCode( nullptr ),
740 pStack( nullptr ),
741 eLastOp( ocPush ),
742 nRecursion(0),
743 nNumFmt( SvNumFormatType::UNDEFINED ),
744 pc( 0 ),
745 meGrammar( formula::FormulaGrammar::GRAM_UNSPECIFIED ),
746 bAutoCorrect( false ),
747 bCorrected( false ),
748 glSubTotal( false ),
749 needsRPNTokenCheck( false ),
750 mbJumpCommandReorder(true),
751 mbStopOnError(true),
752 mbComputeII(bComputeII),
753 mbMatrixFlag(bMatrixFlag)
757 FormulaCompiler::~FormulaCompiler()
761 FormulaCompiler::OpCodeMapPtr FormulaCompiler::GetOpCodeMap( const sal_Int32 nLanguage ) const
763 FormulaCompiler::OpCodeMapPtr xMap;
764 using namespace sheet;
765 switch (nLanguage)
767 case FormulaLanguage::ODFF :
768 if (!mxSymbolsODFF)
769 InitSymbolsODFF();
770 xMap = mxSymbolsODFF;
771 break;
772 case FormulaLanguage::ODF_11 :
773 if (!mxSymbolsPODF)
774 InitSymbolsPODF();
775 xMap = mxSymbolsPODF;
776 break;
777 case FormulaLanguage::ENGLISH :
778 if (!mxSymbolsEnglish)
779 InitSymbolsEnglish();
780 xMap = mxSymbolsEnglish;
781 break;
782 case FormulaLanguage::NATIVE :
783 if (!mxSymbolsNative)
784 InitSymbolsNative();
785 xMap = mxSymbolsNative;
786 break;
787 case FormulaLanguage::XL_ENGLISH:
788 if (!mxSymbolsEnglishXL)
789 InitSymbolsEnglishXL();
790 xMap = mxSymbolsEnglishXL;
791 break;
792 case FormulaLanguage::OOXML:
793 if (!mxSymbolsOOXML)
794 InitSymbolsOOXML();
795 xMap = mxSymbolsOOXML;
796 break;
797 case FormulaLanguage::API :
798 if (!mxSymbolsAPI)
799 InitSymbolsAPI();
800 xMap = mxSymbolsAPI;
801 break;
802 default:
803 ; // nothing, NULL map returned
805 return xMap;
808 OUString FormulaCompiler::FindAddInFunction( const OUString& /*rUpperName*/, bool /*bLocalFirst*/ ) const
810 return OUString();
813 FormulaCompiler::OpCodeMapPtr FormulaCompiler::CreateOpCodeMap(
814 const uno::Sequence<
815 const sheet::FormulaOpCodeMapEntry > & rMapping,
816 bool bEnglish )
818 using sheet::FormulaOpCodeMapEntry;
819 // Filter / API maps are never Core
820 NonConstOpCodeMapPtr xMap = std::make_shared<OpCodeMap>( SC_OPCODE_LAST_OPCODE_ID + 1, false,
821 FormulaGrammar::mergeToGrammar( FormulaGrammar::setEnglishBit(
822 FormulaGrammar::GRAM_EXTERNAL, bEnglish), FormulaGrammar::CONV_UNSPECIFIED));
823 std::unique_ptr<CharClass> xCharClass( xMap->isEnglish() ? nullptr : createCharClassIfNonEnglishUI());
824 const CharClass* pCharClass = xCharClass.get();
825 for (auto const& rMapEntry : rMapping)
827 OpCode eOp = OpCode(rMapEntry.Token.OpCode);
828 if (eOp != ocExternal)
829 xMap->putOpCode( rMapEntry.Name, eOp, pCharClass);
830 else
832 OUString aExternalName;
833 if (rMapEntry.Token.Data >>= aExternalName)
834 xMap->putExternal( rMapEntry.Name, aExternalName);
835 else
837 SAL_WARN( "formula.core", "FormulaCompiler::CreateOpCodeMap: no Token.Data external name");
841 return xMap;
844 static void lcl_fillNativeSymbols( FormulaCompiler::NonConstOpCodeMapPtr& xMap, bool bDestroy = false )
846 static OpCodeMapData aSymbolMap;
847 osl::MutexGuard aGuard(&aSymbolMap.maMtx);
849 if ( bDestroy )
851 aSymbolMap.mxSymbolMap.reset();
853 else if (!aSymbolMap.mxSymbolMap)
855 // Core
856 aSymbolMap.mxSymbolMap =
857 std::make_shared<FormulaCompiler::OpCodeMap>(
858 SC_OPCODE_LAST_OPCODE_ID + 1, true, FormulaGrammar::GRAM_NATIVE_UI);
859 OpCodeList aOpCodeListSymbols(false, RID_STRLIST_FUNCTION_NAMES_SYMBOLS, aSymbolMap.mxSymbolMap);
860 OpCodeList aOpCodeListNative(true, RID_STRLIST_FUNCTION_NAMES, aSymbolMap.mxSymbolMap);
861 // No AddInMap for native core mapping.
864 xMap = aSymbolMap.mxSymbolMap;
867 const OUString& FormulaCompiler::GetNativeSymbol( OpCode eOp )
869 NonConstOpCodeMapPtr xSymbolsNative;
870 lcl_fillNativeSymbols( xSymbolsNative);
871 return xSymbolsNative->getSymbol( eOp );
874 sal_Unicode FormulaCompiler::GetNativeSymbolChar( OpCode eOp )
876 return GetNativeSymbol(eOp)[0];
879 void FormulaCompiler::InitSymbolsNative() const
881 lcl_fillNativeSymbols( mxSymbolsNative);
884 void FormulaCompiler::InitSymbolsEnglish() const
886 static OpCodeMapData aMap;
887 osl::MutexGuard aGuard(&aMap.maMtx);
888 if (!aMap.mxSymbolMap)
889 loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH, FormulaGrammar::GRAM_ENGLISH, aMap.mxSymbolMap);
890 mxSymbolsEnglish = aMap.mxSymbolMap;
893 void FormulaCompiler::InitSymbolsPODF() const
895 static OpCodeMapData aMap;
896 osl::MutexGuard aGuard(&aMap.maMtx);
897 if (!aMap.mxSymbolMap)
898 loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH_PODF, FormulaGrammar::GRAM_PODF, aMap.mxSymbolMap, SeparatorType::RESOURCE_BASE);
899 mxSymbolsPODF = aMap.mxSymbolMap;
902 void FormulaCompiler::InitSymbolsAPI() const
904 static OpCodeMapData aMap;
905 osl::MutexGuard aGuard(&aMap.maMtx);
906 if (!aMap.mxSymbolMap)
907 // XFunctionAccess API always used PODF grammar, keep it.
908 loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH_API, FormulaGrammar::GRAM_PODF, aMap.mxSymbolMap, SeparatorType::RESOURCE_BASE);
909 mxSymbolsAPI = aMap.mxSymbolMap;
912 void FormulaCompiler::InitSymbolsODFF() const
914 static OpCodeMapData aMap;
915 osl::MutexGuard aGuard(&aMap.maMtx);
916 if (!aMap.mxSymbolMap)
917 loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH_ODFF, FormulaGrammar::GRAM_ODFF, aMap.mxSymbolMap, SeparatorType::RESOURCE_BASE);
918 mxSymbolsODFF = aMap.mxSymbolMap;
921 void FormulaCompiler::InitSymbolsEnglishXL() const
923 static OpCodeMapData aMap;
924 osl::MutexGuard aGuard(&aMap.maMtx);
925 if (!aMap.mxSymbolMap)
926 loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH, FormulaGrammar::GRAM_ENGLISH, aMap.mxSymbolMap);
927 mxSymbolsEnglishXL = aMap.mxSymbolMap;
929 // TODO: For now, just replace the separators to the Excel English
930 // variants. Later, if we want to properly map Excel functions with Calc
931 // functions, we'll need to do a little more work here.
932 mxSymbolsEnglishXL->putOpCode( OUString(','), ocSep, nullptr);
933 mxSymbolsEnglishXL->putOpCode( OUString(','), ocArrayColSep, nullptr);
934 mxSymbolsEnglishXL->putOpCode( OUString(';'), ocArrayRowSep, nullptr);
937 void FormulaCompiler::InitSymbolsOOXML() const
939 static OpCodeMapData aMap;
940 osl::MutexGuard aGuard(&aMap.maMtx);
941 if (!aMap.mxSymbolMap)
942 loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH_OOXML, FormulaGrammar::GRAM_OOXML, aMap.mxSymbolMap, SeparatorType::RESOURCE_BASE);
943 mxSymbolsOOXML = aMap.mxSymbolMap;
947 void FormulaCompiler::loadSymbols(const std::pair<const char*, int>* pSymbols, FormulaGrammar::Grammar eGrammar,
948 NonConstOpCodeMapPtr& rxMap, SeparatorType eSepType) const
950 if ( rxMap )
951 return;
953 // not Core
954 rxMap = std::make_shared<OpCodeMap>( SC_OPCODE_LAST_OPCODE_ID + 1, eGrammar != FormulaGrammar::GRAM_ODFF, eGrammar );
955 OpCodeList aOpCodeList(false, pSymbols, rxMap, eSepType);
957 fillFromAddInMap( rxMap, eGrammar);
958 // Fill from collection for AddIns not already present.
959 if ( FormulaGrammar::GRAM_ENGLISH != eGrammar )
960 fillFromAddInCollectionUpperName( rxMap);
961 else
962 fillFromAddInCollectionEnglishName( rxMap);
965 void FormulaCompiler::fillFromAddInCollectionUpperName( const NonConstOpCodeMapPtr& /*xMap */) const
969 void FormulaCompiler::fillFromAddInCollectionEnglishName( const NonConstOpCodeMapPtr& /*xMap */) const
973 void FormulaCompiler::fillFromAddInMap( const NonConstOpCodeMapPtr& /*xMap*/, FormulaGrammar::Grammar /*_eGrammar */) const
977 OpCode FormulaCompiler::GetEnglishOpCode( const OUString& rName ) const
979 FormulaCompiler::OpCodeMapPtr xMap = GetOpCodeMap( sheet::FormulaLanguage::ENGLISH);
981 formula::OpCodeHashMap::const_iterator iLook( xMap->getHashMap().find( rName ) );
982 bool bFound = (iLook != xMap->getHashMap().end());
983 return bFound ? (*iLook).second : ocNone;
986 bool FormulaCompiler::IsOpCodeVolatile( OpCode eOp )
988 bool bRet = false;
989 switch (eOp)
991 // no parameters:
992 case ocRandom:
993 case ocGetActDate:
994 case ocGetActTime:
995 // one parameter:
996 case ocFormula:
997 case ocInfo:
998 // more than one parameters:
999 // ocIndirect otherwise would have to do
1000 // StopListening and StartListening on a reference for every
1001 // interpreted value.
1002 case ocIndirect:
1003 // ocOffset results in indirect references.
1004 case ocOffset:
1005 // ocDebugVar shows internal value that may change as the internal state changes.
1006 case ocDebugVar:
1007 bRet = true;
1008 break;
1009 default:
1010 bRet = false;
1011 break;
1013 return bRet;
1016 bool FormulaCompiler::IsOpCodeJumpCommand( OpCode eOp )
1018 switch (eOp)
1020 case ocIf:
1021 case ocIfError:
1022 case ocIfNA:
1023 case ocChoose:
1024 return true;
1025 default:
1028 return false;
1031 // Remove quotes, escaped quotes are unescaped.
1032 bool FormulaCompiler::DeQuote( OUString& rStr )
1034 sal_Int32 nLen = rStr.getLength();
1035 if ( nLen > 1 && rStr[0] == '\'' && rStr[ nLen-1 ] == '\'' )
1037 rStr = rStr.copy( 1, nLen-2 );
1038 rStr = rStr.replaceAll( "\\\'", "\'" );
1039 return true;
1041 return false;
1044 void FormulaCompiler::fillAddInToken(
1045 ::std::vector< sheet::FormulaOpCodeMapEntry >& /*_rVec*/,
1046 bool /*_bIsEnglish*/) const
1050 bool FormulaCompiler::IsMatrixFunction( OpCode eOpCode )
1052 switch (eOpCode)
1054 case ocDde :
1055 case ocGrowth :
1056 case ocTrend :
1057 case ocLogest :
1058 case ocLinest :
1059 case ocFrequency :
1060 case ocMatTrans :
1061 case ocMatMult :
1062 case ocMatInv :
1063 case ocMatrixUnit :
1064 case ocModalValue_Multi :
1065 case ocFourier :
1066 return true;
1067 default:
1069 // added to avoid warnings
1072 return false;
1076 void FormulaCompiler::OpCodeMap::putCopyOpCode( const OUString& rSymbol, OpCode eOp )
1078 SAL_WARN_IF( !mpTable[eOp].isEmpty() && rSymbol.isEmpty(), "formula.core",
1079 "OpCodeMap::putCopyOpCode: NOT replacing OpCode " << static_cast<sal_uInt16>(eOp)
1080 << " '" << mpTable[eOp] << "' with empty name!");
1081 if (!mpTable[eOp].isEmpty() && rSymbol.isEmpty())
1082 maHashMap.emplace(mpTable[eOp], eOp);
1083 else
1085 mpTable[eOp] = rSymbol;
1086 maHashMap.emplace(rSymbol, eOp);
1090 void FormulaCompiler::OpCodeMap::copyFrom( const OpCodeMap& r )
1092 maHashMap = OpCodeHashMap( mnSymbols);
1094 sal_uInt16 n = r.getSymbolCount();
1095 SAL_WARN_IF( n != mnSymbols, "formula.core",
1096 "OpCodeMap::copyFrom: unequal size, this: " << mnSymbols << " that: " << n);
1097 if (n > mnSymbols)
1098 n = mnSymbols;
1100 // OpCode 0 (ocPush) should never be in a map.
1101 SAL_WARN_IF( !mpTable[0].isEmpty() || !r.mpTable[0].isEmpty(), "formula.core",
1102 "OpCodeMap::copyFrom: OpCode 0 assigned, this: '"
1103 << mpTable[0] << "' that: '" << r.mpTable[0] << "'");
1105 // For bOverrideKnownBad when copying from the English core map (ODF 1.1
1106 // and API) to the native map (UI "use English function names") replace the
1107 // known bad legacy function names with correct ones.
1108 if (r.mbCore &&
1109 FormulaGrammar::extractFormulaLanguage( meGrammar) == sheet::FormulaLanguage::NATIVE &&
1110 FormulaGrammar::extractFormulaLanguage( r.meGrammar) == sheet::FormulaLanguage::ENGLISH)
1112 for (sal_uInt16 i = 1; i < n; ++i)
1114 OUString aSymbol;
1115 OpCode eOp = OpCode(i);
1116 switch (eOp)
1118 case ocRRI:
1119 aSymbol = "RRI";
1120 break;
1121 case ocTableOp:
1122 aSymbol = "MULTIPLE.OPERATIONS";
1123 break;
1124 default:
1125 aSymbol = r.mpTable[i];
1127 putCopyOpCode( aSymbol, eOp);
1130 else
1132 for (sal_uInt16 i = 1; i < n; ++i)
1134 OpCode eOp = OpCode(i);
1135 const OUString& rSymbol = r.mpTable[i];
1136 putCopyOpCode( rSymbol, eOp);
1140 // TODO: maybe copy the external maps too?
1144 FormulaError FormulaCompiler::GetErrorConstant( const OUString& rName ) const
1146 FormulaError nError = FormulaError::NONE;
1147 OpCodeHashMap::const_iterator iLook( mxSymbols->getHashMap().find( rName));
1148 if (iLook != mxSymbols->getHashMap().end())
1150 switch ((*iLook).second)
1152 // Not all may make sense in a formula, but these we know as
1153 // opcodes.
1154 case ocErrNull:
1155 nError = FormulaError::NoCode;
1156 break;
1157 case ocErrDivZero:
1158 nError = FormulaError::DivisionByZero;
1159 break;
1160 case ocErrValue:
1161 nError = FormulaError::NoValue;
1162 break;
1163 case ocErrRef:
1164 nError = FormulaError::NoRef;
1165 break;
1166 case ocErrName:
1167 nError = FormulaError::NoName;
1168 break;
1169 case ocErrNum:
1170 nError = FormulaError::IllegalFPOperation;
1171 break;
1172 case ocErrNA:
1173 nError = FormulaError::NotAvailable;
1174 break;
1175 default:
1176 ; // nothing
1179 else
1181 // Per convention recognize detailed "#ERRxxx!" constants, always
1182 // untranslated. Error numbers are sal_uInt16 so at most 5 decimal
1183 // digits.
1184 if (rName.startsWithIgnoreAsciiCase("#ERR") && rName.getLength() <= 10 && rName[rName.getLength()-1] == '!')
1186 sal_uInt32 nErr = rName.copy( 4, rName.getLength() - 5).toUInt32();
1187 if (0 < nErr && nErr <= SAL_MAX_UINT16 && isPublishedFormulaError(static_cast<FormulaError>(nErr)))
1188 nError = static_cast<FormulaError>(nErr);
1191 return nError;
1194 void FormulaCompiler::EnableJumpCommandReorder( bool bEnable )
1196 mbJumpCommandReorder = bEnable;
1199 void FormulaCompiler::EnableStopOnError( bool bEnable )
1201 mbStopOnError = bEnable;
1204 void FormulaCompiler::AppendErrorConstant( OUStringBuffer& rBuffer, FormulaError nError ) const
1206 OpCode eOp;
1207 switch (nError)
1209 case FormulaError::NoCode:
1210 eOp = ocErrNull;
1211 break;
1212 case FormulaError::DivisionByZero:
1213 eOp = ocErrDivZero;
1214 break;
1215 case FormulaError::NoValue:
1216 eOp = ocErrValue;
1217 break;
1218 case FormulaError::NoRef:
1219 eOp = ocErrRef;
1220 break;
1221 case FormulaError::NoName:
1222 eOp = ocErrName;
1223 break;
1224 case FormulaError::IllegalFPOperation:
1225 eOp = ocErrNum;
1226 break;
1227 case FormulaError::NotAvailable:
1228 eOp = ocErrNA;
1229 break;
1230 default:
1232 // Per convention create detailed "#ERRxxx!" constants, always
1233 // untranslated.
1234 rBuffer.append("#ERR");
1235 rBuffer.append(static_cast<sal_Int32>(nError));
1236 rBuffer.append('!');
1237 return;
1240 rBuffer.append( mxSymbols->getSymbol( eOp));
1243 constexpr short nRecursionMax = 100;
1245 bool FormulaCompiler::GetToken()
1247 FormulaCompilerRecursionGuard aRecursionGuard( nRecursion );
1248 if ( nRecursion > nRecursionMax )
1250 SetError( FormulaError::StackOverflow );
1251 mpLastToken = mpToken = new FormulaByteToken( ocStop );
1252 return false;
1254 if ( bAutoCorrect && !pStack )
1255 { // don't merge stacked subroutine code into entered formula
1256 aCorrectedFormula += aCorrectedSymbol;
1257 aCorrectedSymbol.clear();
1259 bool bStop = false;
1260 if (pArr->GetCodeError() != FormulaError::NONE && mbStopOnError)
1261 bStop = true;
1262 else
1264 FormulaTokenRef pSpacesToken;
1265 short nWasColRowName;
1266 if ( pArr->OpCodeBefore( maArrIterator.GetIndex() ) == ocColRowName )
1267 nWasColRowName = 1;
1268 else
1269 nWasColRowName = 0;
1270 mpToken = maArrIterator.Next();
1271 while( mpToken && mpToken->GetOpCode() == ocSpaces )
1273 // For significant whitespace remember last ocSpaces token. Usually
1274 // there's only one even for multiple spaces.
1275 pSpacesToken = mpToken;
1276 if ( nWasColRowName )
1277 nWasColRowName++;
1278 if ( bAutoCorrect && !pStack )
1279 CreateStringFromToken( aCorrectedFormula, mpToken.get() );
1280 mpToken = maArrIterator.Next();
1282 if ( bAutoCorrect && !pStack && mpToken )
1283 CreateStringFromToken( aCorrectedSymbol, mpToken.get() );
1284 if( !mpToken )
1286 if( pStack )
1288 PopTokenArray();
1289 // mpLastToken was popped as well and corresponds to the
1290 // then current last token during PushTokenArray(), e.g. for
1291 // HandleRange().
1292 return GetToken();
1294 else
1295 bStop = true;
1297 else
1299 if ( nWasColRowName >= 2 && mpToken->GetOpCode() == ocColRowName )
1300 { // convert an ocSpaces to ocIntersect in RPN
1301 mpLastToken = mpToken = new FormulaByteToken( ocIntersect );
1302 maArrIterator.StepBack(); // we advanced to the second ocColRowName, step back
1304 else if (pSpacesToken && FormulaGrammar::isExcelSyntax( meGrammar) &&
1305 mpLastToken && mpToken &&
1306 isPotentialRangeType( mpLastToken.get(), false, false) &&
1307 isPotentialRangeType( mpToken.get(), false, true))
1309 // Let IntersectionLine() <- Factor() decide how to treat this,
1310 // once the actual arguments are determined in RPN.
1311 mpLastToken = mpToken = pSpacesToken;
1312 maArrIterator.StepBack(); // step back from next non-spaces token
1313 return true;
1317 if( bStop )
1319 mpLastToken = mpToken = new FormulaByteToken( ocStop );
1320 return false;
1323 // Remember token for next round and any PushTokenArray() calls that may
1324 // occur in handlers.
1325 mpLastToken = mpToken;
1327 if ( mpToken->IsExternalRef() )
1329 return HandleExternalReference(*mpToken);
1331 else
1333 switch (mpToken->GetOpCode())
1335 case ocSubTotal:
1336 case ocAggregate:
1337 glSubTotal = true;
1338 break;
1339 case ocName:
1340 if( HandleRange())
1342 // Expanding ocName might have introduced tokens such as ocStyle that prevent formula threading,
1343 // but those wouldn't be present in the raw tokens array, so ensure RPN tokens will be checked too.
1344 needsRPNTokenCheck = true;
1345 return true;
1347 return false;
1348 case ocColRowName:
1349 return HandleColRowName();
1350 case ocDBArea:
1351 return HandleDbData();
1352 case ocTableRef:
1353 return HandleTableRef();
1354 case ocPush:
1355 if( mbComputeII )
1356 HandleIIOpCode(mpToken.get(), nullptr, 0);
1357 break;
1358 default:
1359 ; // nothing
1362 return true;
1366 // RPN creation by recursion
1367 void FormulaCompiler::Factor()
1369 if (pArr->GetCodeError() != FormulaError::NONE && mbStopOnError)
1370 return;
1372 CurrentFactor pFacToken( this );
1374 OpCode eOp = mpToken->GetOpCode();
1375 if (eOp == ocPush || eOp == ocColRowNameAuto || eOp == ocMatRef || eOp == ocDBArea
1376 || eOp == ocTableRef
1377 || (!mbJumpCommandReorder && ((eOp == ocName) || (eOp == ocColRowName) || (eOp == ocBad)))
1380 PutCode( mpToken );
1381 eOp = NextToken();
1382 if( eOp == ocOpen )
1384 // PUSH( is an error that may be caused by an unknown function.
1385 SetError(
1386 ( mpToken->GetType() == svString
1387 || mpToken->GetType() == svSingleRef )
1388 ? FormulaError::NoName : FormulaError::OperatorExpected );
1389 if ( bAutoCorrect && !pStack )
1390 { // assume multiplication
1391 aCorrectedFormula += mxSymbols->getSymbol( ocMul);
1392 bCorrected = true;
1393 NextToken();
1394 eOp = Expression();
1395 if( eOp != ocClose )
1396 SetError( FormulaError::PairExpected);
1397 else
1398 NextToken();
1402 else if( eOp == ocOpen )
1404 NextToken();
1405 eOp = Expression();
1406 while ((eOp == ocSep) && (pArr->GetCodeError() == FormulaError::NONE || !mbStopOnError))
1407 { // range list (A1;A2) converted to (A1~A2)
1408 pFacToken = mpToken;
1409 NextToken();
1410 CheckSetForceArrayParameter( mpToken, 0);
1411 eOp = Expression();
1412 // Do not ignore error here, regardless of mbStopOnError, to not
1413 // change the formula expression in case of an unexpected state.
1414 if (pArr->GetCodeError() == FormulaError::NONE && pc >= 2)
1416 // Left and right operands must be reference or function
1417 // returning reference to form a range list.
1418 const FormulaToken* p = pCode[-2];
1419 if (p && isPotentialRangeType( p, true, false))
1421 p = pCode[-1];
1422 if (p && isPotentialRangeType( p, true, true))
1424 pFacToken->NewOpCode( ocUnion, FormulaToken::PrivateAccess());
1425 // XXX NOTE: the token's eType is still svSep here!
1426 PutCode( pFacToken);
1431 if (eOp != ocClose)
1432 SetError( FormulaError::PairExpected);
1433 else
1434 NextToken();
1436 /* TODO: if no conversion to ocUnion is involved this could collect
1437 * such expression as a list or (matrix) vector to be passed as
1438 * argument for one parameter (which in fact the ocUnion svRefList is a
1439 * special case of), which would require a new StackVar type and needed
1440 * to be handled by the interpreter for functions that could support it
1441 * (i.e. already handle VAR_ARGS or svRefList parameters). This is also
1442 * not defined by ODF.
1443 * Does Excel handle =SUM((1;2))?
1444 * As is, the interpreter catches extraneous uncalculated
1445 * subexpressions like 1 of (1;2) as error. */
1447 else
1449 if( nNumFmt == SvNumFormatType::UNDEFINED )
1450 nNumFmt = lcl_GetRetFormat( eOp );
1452 if ( IsOpCodeVolatile( eOp) )
1453 pArr->SetExclusiveRecalcModeAlways();
1454 else
1456 switch( eOp )
1458 // Functions recalculated on every document load.
1459 // ONLOAD_LENIENT here to be able to distinguish and not
1460 // force a recalc (if not in an ALWAYS or ONLOAD_MUST
1461 // context) but keep an imported result from for example
1462 // OOXML a DDE call. Will be recalculated for ODFF.
1463 case ocConvertOOo :
1464 case ocDde:
1465 case ocMacro:
1466 case ocWebservice:
1467 pArr->AddRecalcMode( ScRecalcMode::ONLOAD_LENIENT );
1468 break;
1469 // RANDBETWEEN() is volatile like RAND(). Other Add-In
1470 // functions may have to be recalculated or not, we don't
1471 // know, classify as ONLOAD_LENIENT.
1472 case ocExternal:
1473 if (mpToken->GetExternal() == "com.sun.star.sheet.addin.Analysis.getRandbetween")
1474 pArr->SetExclusiveRecalcModeAlways();
1475 else
1476 pArr->AddRecalcMode( ScRecalcMode::ONLOAD_LENIENT );
1477 break;
1478 // If the referred cell is moved the value changes.
1479 case ocColumn :
1480 case ocRow :
1481 pArr->SetRecalcModeOnRefMove();
1482 break;
1483 // ocCell needs recalc on move for some possible type values.
1484 // And recalc mode on load, tdf#60645
1485 case ocCell :
1486 pArr->SetRecalcModeOnRefMove();
1487 pArr->AddRecalcMode( ScRecalcMode::ONLOAD_MUST );
1488 break;
1489 case ocHyperLink :
1490 // Cell with hyperlink needs to be calculated on load to
1491 // get its matrix result generated.
1492 pArr->AddRecalcMode( ScRecalcMode::ONLOAD_MUST );
1493 pArr->SetHyperLink( true);
1494 break;
1495 default:
1496 ; // nothing
1499 if (SC_OPCODE_START_NO_PAR <= eOp && eOp < SC_OPCODE_STOP_NO_PAR)
1501 pFacToken = mpToken;
1502 eOp = NextToken();
1503 if (eOp != ocOpen)
1505 SetError( FormulaError::PairExpected);
1506 PutCode( pFacToken );
1508 else
1510 eOp = NextToken();
1511 if (eOp != ocClose)
1512 SetError( FormulaError::PairExpected);
1513 PutCode( pFacToken);
1514 NextToken();
1517 else if (SC_OPCODE_START_1_PAR <= eOp && eOp < SC_OPCODE_STOP_1_PAR)
1519 if (eOp == ocIsoWeeknum && FormulaGrammar::isODFF( meGrammar ))
1521 // tdf#50950 ocIsoWeeknum can have 2 arguments when saved by older versions of Calc;
1522 // the opcode then has to be changed to ocWeek for backward compatibility
1523 pFacToken = mpToken;
1524 eOp = NextToken();
1525 bool bNoParam = false;
1526 if (eOp == ocOpen)
1528 eOp = NextToken();
1529 if (eOp == ocClose)
1530 bNoParam = true;
1531 else
1533 CheckSetForceArrayParameter( mpToken, 0);
1534 eOp = Expression();
1537 else
1538 SetError( FormulaError::PairExpected);
1539 sal_uInt32 nSepCount = 0;
1540 const sal_uInt16 nSepPos = maArrIterator.GetIndex() - 1; // separator position, if any
1541 if( !bNoParam )
1543 nSepCount++;
1544 while ((eOp == ocSep) && (pArr->GetCodeError() == FormulaError::NONE || !mbStopOnError))
1546 NextToken();
1547 CheckSetForceArrayParameter( mpToken, nSepCount);
1548 nSepCount++;
1549 if (nSepCount > FORMULA_MAXPARAMS)
1550 SetError( FormulaError::CodeOverflow);
1551 eOp = Expression();
1554 if (eOp != ocClose)
1555 SetError( FormulaError::PairExpected);
1556 else
1557 NextToken();
1558 pFacToken->SetByte( nSepCount );
1559 if (nSepCount == 2)
1561 // An old mode!=1 indicates ISO week, remove argument if
1562 // literal double value and keep function. Anything else
1563 // can not be resolved, there exists no "like ISO but week
1564 // starts on Sunday" mode in WEEKNUM and for an expression
1565 // we can't determine.
1566 // Current index is nSepPos+3 if expression stops, or
1567 // nSepPos+4 if expression continues after the call because
1568 // we just called NextToken() to move away from it.
1569 if (pc >= 2 && (maArrIterator.GetIndex() == nSepPos + 3 || maArrIterator.GetIndex() == nSepPos + 4) &&
1570 pArr->TokenAt(nSepPos+1)->GetType() == svDouble &&
1571 pArr->TokenAt(nSepPos+1)->GetDouble() != 1.0 &&
1572 pArr->TokenAt(nSepPos+2)->GetOpCode() == ocClose &&
1573 pArr->RemoveToken( nSepPos, 2) == 2)
1575 maArrIterator.AfterRemoveToken( nSepPos, 2);
1576 // Remove the ocPush/svDouble just removed also from
1577 // the compiler local RPN array.
1578 --pCode; --pc;
1579 (*pCode)->DecRef(); // may be dead now
1580 pFacToken->SetByte( nSepCount - 1 );
1582 else
1584 // For the remaining two arguments cases use the
1585 // compatibility function.
1586 pFacToken->NewOpCode( ocWeeknumOOo, FormulaToken::PrivateAccess());
1589 PutCode( pFacToken );
1591 else
1593 // standard handling of 1-parameter opcodes
1594 pFacToken = mpToken;
1595 eOp = NextToken();
1596 if( nNumFmt == SvNumFormatType::UNDEFINED && eOp == ocNot )
1597 nNumFmt = SvNumFormatType::LOGICAL;
1598 if (eOp == ocOpen)
1600 NextToken();
1601 CheckSetForceArrayParameter( mpToken, 0);
1602 eOp = Expression();
1604 else
1605 SetError( FormulaError::PairExpected);
1606 if (eOp != ocClose)
1607 SetError( FormulaError::PairExpected);
1608 else if ( pArr->GetCodeError() == FormulaError::NONE )
1610 pFacToken->SetByte( 1 );
1611 if (mbComputeII)
1613 FormulaToken** pArg = pCode - 1;
1614 HandleIIOpCode(pFacToken, &pArg, 1);
1617 PutCode( pFacToken );
1618 NextToken();
1621 else if ((SC_OPCODE_START_2_PAR <= eOp && eOp < SC_OPCODE_STOP_2_PAR)
1622 || eOp == ocExternal
1623 || eOp == ocMacro
1624 || eOp == ocAnd
1625 || eOp == ocOr
1626 || eOp == ocBad
1627 || ( eOp >= ocInternalBegin && eOp <= ocInternalEnd )
1628 || (!mbJumpCommandReorder && IsOpCodeJumpCommand(eOp)))
1630 pFacToken = mpToken;
1631 OpCode eMyLastOp = eOp;
1632 eOp = NextToken();
1633 bool bNoParam = false;
1634 bool bBadName = false;
1635 if (eOp == ocOpen)
1637 eOp = NextToken();
1638 if (eOp == ocClose)
1639 bNoParam = true;
1640 else
1642 CheckSetForceArrayParameter( mpToken, 0);
1643 eOp = Expression();
1646 else if (eMyLastOp == ocBad)
1648 // Just a bad name, not an unknown function, no parameters, no
1649 // closing expected.
1650 bBadName = true;
1651 bNoParam = true;
1653 else
1654 SetError( FormulaError::PairExpected);
1655 sal_uInt32 nSepCount = 0;
1656 if( !bNoParam )
1658 bool bDoIICompute = mbComputeII;
1659 // Array of FormulaToken double pointers to collect the parameters of II opcodes.
1660 FormulaToken*** pArgArray = nullptr;
1661 if (bDoIICompute)
1663 pArgArray = static_cast<FormulaToken***>(alloca(sizeof(FormulaToken**)*FORMULA_MAXPARAMSII));
1664 if (!pArgArray)
1665 bDoIICompute = false;
1668 nSepCount++;
1670 if (bDoIICompute)
1671 pArgArray[nSepCount-1] = pCode - 1; // Add first argument
1673 while ((eOp == ocSep) && (pArr->GetCodeError() == FormulaError::NONE || !mbStopOnError))
1675 NextToken();
1676 CheckSetForceArrayParameter( mpToken, nSepCount);
1677 nSepCount++;
1678 if (nSepCount > FORMULA_MAXPARAMS)
1679 SetError( FormulaError::CodeOverflow);
1680 eOp = Expression();
1681 if (bDoIICompute && nSepCount <= FORMULA_MAXPARAMSII)
1682 pArgArray[nSepCount - 1] = pCode - 1; // Add rest of the arguments
1684 if (bDoIICompute)
1685 HandleIIOpCode(pFacToken, pArgArray,
1686 std::min(nSepCount, static_cast<sal_uInt32>(FORMULA_MAXPARAMSII)));
1688 bool bDone = false;
1689 if (bBadName)
1690 ; // nothing, keep current token for return
1691 else if (eOp != ocClose)
1692 SetError( FormulaError::PairExpected);
1693 else
1695 NextToken();
1696 bDone = true;
1698 // Jumps are just normal functions for the FunctionAutoPilot tree view
1699 if (!mbJumpCommandReorder && pFacToken->GetType() == svJump)
1700 pFacToken = new FormulaFAPToken( pFacToken->GetOpCode(), nSepCount, pFacToken );
1701 else
1702 pFacToken->SetByte( nSepCount );
1703 PutCode( pFacToken );
1705 if (bDone)
1706 AnnotateOperands();
1708 else if (IsOpCodeJumpCommand(eOp))
1710 // the PC counters are -1
1711 pFacToken = mpToken;
1712 switch (eOp)
1714 case ocIf:
1715 pFacToken->GetJump()[ 0 ] = 3; // if, else, behind
1716 break;
1717 case ocChoose:
1718 pFacToken->GetJump()[ 0 ] = FORMULA_MAXJUMPCOUNT + 1;
1719 break;
1720 case ocIfError:
1721 case ocIfNA:
1722 pFacToken->GetJump()[ 0 ] = 2; // if, behind
1723 break;
1724 default:
1725 SAL_WARN("formula.core","Jump OpCode: " << +eOp);
1726 assert(!"FormulaCompiler::Factor: someone forgot to add a jump count case");
1728 eOp = NextToken();
1729 if (eOp == ocOpen)
1731 NextToken();
1732 CheckSetForceArrayParameter( mpToken, 0);
1733 eOp = Expression();
1735 else
1736 SetError( FormulaError::PairExpected);
1737 PutCode( pFacToken );
1738 // During AutoCorrect (since pArr->GetCodeError() is
1739 // ignored) an unlimited ocIf would crash because
1740 // ScRawToken::Clone() allocates the JumpBuffer according to
1741 // nJump[0]*2+2, which is 3*2+2 on ocIf and 2*2+2 ocIfError and ocIfNA.
1742 short nJumpMax;
1743 OpCode eFacOpCode = pFacToken->GetOpCode();
1744 switch (eFacOpCode)
1746 case ocIf:
1747 nJumpMax = 3;
1748 break;
1749 case ocChoose:
1750 nJumpMax = FORMULA_MAXJUMPCOUNT;
1751 break;
1752 case ocIfError:
1753 case ocIfNA:
1754 nJumpMax = 2;
1755 break;
1756 default:
1757 nJumpMax = 0;
1758 SAL_WARN("formula.core","Jump OpCode: " << +eFacOpCode);
1759 assert(!"FormulaCompiler::Factor: someone forgot to add a jump max case");
1761 short nJumpCount = 0;
1762 while ( (nJumpCount < (FORMULA_MAXJUMPCOUNT - 1)) && (eOp == ocSep)
1763 && (pArr->GetCodeError() == FormulaError::NONE || !mbStopOnError))
1765 if ( ++nJumpCount <= nJumpMax )
1766 pFacToken->GetJump()[nJumpCount] = pc-1;
1767 NextToken();
1768 CheckSetForceArrayParameter( mpToken, nJumpCount - 1);
1769 eOp = Expression();
1770 // ocSep or ocClose terminate the subexpression
1771 PutCode( mpToken );
1773 if (eOp != ocClose)
1774 SetError( FormulaError::PairExpected);
1775 else
1777 NextToken();
1778 // always limit to nJumpMax, no arbitrary overwrites
1779 if ( ++nJumpCount <= nJumpMax )
1780 pFacToken->GetJump()[ nJumpCount ] = pc-1;
1781 eFacOpCode = pFacToken->GetOpCode();
1782 bool bLimitOk;
1783 switch (eFacOpCode)
1785 case ocIf:
1786 bLimitOk = (nJumpCount <= 3);
1787 break;
1788 case ocChoose:
1789 bLimitOk = (nJumpCount < FORMULA_MAXJUMPCOUNT);
1790 break;
1791 case ocIfError:
1792 case ocIfNA:
1793 bLimitOk = (nJumpCount <= 2);
1794 break;
1795 default:
1796 bLimitOk = false;
1797 SAL_WARN("formula.core","Jump OpCode: " << +eFacOpCode);
1798 assert(!"FormulaCompiler::Factor: someone forgot to add a jump limit case");
1800 if (bLimitOk)
1801 pFacToken->GetJump()[ 0 ] = nJumpCount;
1802 else
1803 SetError( FormulaError::IllegalParameter);
1806 else if ( eOp == ocMissing )
1808 PutCode( mpToken );
1809 NextToken();
1811 else if ( eOp == ocClose )
1813 SetError( FormulaError::ParameterExpected );
1815 else if ( eOp == ocSep )
1816 { // Subsequent ocSep
1817 SetError( FormulaError::ParameterExpected );
1818 if ( bAutoCorrect && !pStack )
1820 aCorrectedSymbol.clear();
1821 bCorrected = true;
1824 else if ( mpToken->IsExternalRef() )
1826 PutCode( mpToken);
1827 NextToken();
1829 else
1831 SetError( FormulaError::UnknownToken );
1832 if ( bAutoCorrect && !pStack )
1834 if ( eOp == ocStop )
1835 { // trailing operator w/o operand
1836 sal_Int32 nLen = aCorrectedFormula.getLength();
1837 if ( nLen )
1838 aCorrectedFormula = aCorrectedFormula.copy( 0, nLen - 1 );
1839 aCorrectedSymbol.clear();
1840 bCorrected = true;
1847 void FormulaCompiler::RangeLine()
1849 Factor();
1850 while (mpToken->GetOpCode() == ocRange)
1852 FormulaToken** pCode1 = pCode - 1;
1853 FormulaTokenRef p = mpToken;
1854 NextToken();
1855 Factor();
1856 FormulaToken** pCode2 = pCode - 1;
1857 if (!MergeRangeReference( pCode1, pCode2))
1858 PutCode(p);
1862 void FormulaCompiler::IntersectionLine()
1864 RangeLine();
1865 while (mpToken->GetOpCode() == ocIntersect || mpToken->GetOpCode() == ocSpaces)
1867 sal_uInt16 nCodeIndex = maArrIterator.GetIndex() - 1;
1868 FormulaToken** pCode1 = pCode - 1;
1869 FormulaTokenRef p = mpToken;
1870 NextToken();
1871 RangeLine();
1872 FormulaToken** pCode2 = pCode - 1;
1873 if (p->GetOpCode() == ocSpaces)
1875 // Convert to intersection if both left and right are references or
1876 // functions (potentially returning references, if not then a space
1877 // or no space would be a syntax error anyway), not other operators
1878 // or operands. Else discard.
1879 if (isAdjacentOrGapRpnEnd( pc, pCode, pCode1, pCode2) && isIntersectable( pCode1, pCode2))
1881 FormulaTokenRef pIntersect( new FormulaByteToken( ocIntersect));
1882 // Replace ocSpaces with ocIntersect so that when switching
1883 // formula syntax the correct operator string is created.
1884 pArr->ReplaceToken( nCodeIndex, pIntersect.get(), FormulaTokenArray::ReplaceMode::CODE_ONLY);
1885 PutCode( pIntersect);
1888 else
1890 PutCode(p);
1895 void FormulaCompiler::UnionLine()
1897 IntersectionLine();
1898 while (mpToken->GetOpCode() == ocUnion)
1900 FormulaTokenRef p = mpToken;
1901 NextToken();
1902 IntersectionLine();
1903 PutCode(p);
1907 void FormulaCompiler::UnaryLine()
1909 if( mpToken->GetOpCode() == ocAdd )
1910 GetToken();
1911 else if (SC_OPCODE_START_UN_OP <= mpToken->GetOpCode() &&
1912 mpToken->GetOpCode() < SC_OPCODE_STOP_UN_OP)
1914 FormulaTokenRef p = mpToken;
1915 NextToken();
1916 UnaryLine();
1917 if (mbComputeII)
1919 FormulaToken** pArg = pCode - 1;
1920 HandleIIOpCode(p.get(), &pArg, 1);
1922 PutCode( p );
1924 else
1925 UnionLine();
1928 void FormulaCompiler::PostOpLine()
1930 UnaryLine();
1931 while ( mpToken->GetOpCode() == ocPercentSign )
1932 { // this operator _follows_ its operand
1933 if (mbComputeII)
1935 FormulaToken** pArg = pCode - 1;
1936 HandleIIOpCode(mpToken.get(), &pArg, 1);
1938 PutCode( mpToken );
1939 NextToken();
1943 void FormulaCompiler::PowLine()
1945 PostOpLine();
1946 while (mpToken->GetOpCode() == ocPow)
1948 FormulaTokenRef p = mpToken;
1949 FormulaToken** pArgArray[2];
1950 if (mbComputeII)
1951 pArgArray[0] = pCode - 1; // Add first argument
1952 NextToken();
1953 PostOpLine();
1954 if (mbComputeII)
1956 pArgArray[1] = pCode - 1; // Add second argument
1957 HandleIIOpCode(p.get(), pArgArray, 2);
1959 PutCode(p);
1963 void FormulaCompiler::MulDivLine()
1965 PowLine();
1966 while (mpToken->GetOpCode() == ocMul || mpToken->GetOpCode() == ocDiv)
1968 FormulaTokenRef p = mpToken;
1969 FormulaToken** pArgArray[2];
1970 if (mbComputeII)
1971 pArgArray[0] = pCode - 1; // Add first argument
1972 NextToken();
1973 PowLine();
1974 if (mbComputeII)
1976 pArgArray[1] = pCode - 1; // Add second argument
1977 HandleIIOpCode(p.get(), pArgArray, 2);
1979 PutCode(p);
1983 void FormulaCompiler::AddSubLine()
1985 MulDivLine();
1986 while (mpToken->GetOpCode() == ocAdd || mpToken->GetOpCode() == ocSub)
1988 FormulaTokenRef p = mpToken;
1989 FormulaToken** pArgArray[2];
1990 if (mbComputeII)
1991 pArgArray[0] = pCode - 1; // Add first argument
1992 NextToken();
1993 MulDivLine();
1994 if (mbComputeII)
1996 pArgArray[1] = pCode - 1; // Add second argument
1997 HandleIIOpCode(p.get(), pArgArray, 2);
1999 PutCode(p);
2003 void FormulaCompiler::ConcatLine()
2005 AddSubLine();
2006 while (mpToken->GetOpCode() == ocAmpersand)
2008 FormulaTokenRef p = mpToken;
2009 FormulaToken** pArgArray[2];
2010 if (mbComputeII)
2011 pArgArray[0] = pCode - 1; // Add first argument
2012 NextToken();
2013 AddSubLine();
2014 if (mbComputeII)
2016 pArgArray[1] = pCode - 1; // Add second argument
2017 HandleIIOpCode(p.get(), pArgArray, 2);
2019 PutCode(p);
2023 void FormulaCompiler::CompareLine()
2025 ConcatLine();
2026 while (mpToken->GetOpCode() >= ocEqual && mpToken->GetOpCode() <= ocGreaterEqual)
2028 FormulaTokenRef p = mpToken;
2029 FormulaToken** pArgArray[2];
2030 if (mbComputeII)
2031 pArgArray[0] = pCode - 1; // Add first argument
2032 NextToken();
2033 ConcatLine();
2034 if (mbComputeII)
2036 pArgArray[1] = pCode - 1; // Add second argument
2037 HandleIIOpCode(p.get(), pArgArray, 2);
2039 PutCode(p);
2043 OpCode FormulaCompiler::Expression()
2045 FormulaCompilerRecursionGuard aRecursionGuard( nRecursion );
2046 if ( nRecursion > nRecursionMax )
2048 SetError( FormulaError::StackOverflow );
2049 return ocStop; //! generate token instead?
2051 CompareLine();
2052 while (mpToken->GetOpCode() == ocAnd || mpToken->GetOpCode() == ocOr)
2054 FormulaTokenRef p = mpToken;
2055 mpToken->SetByte( 2 ); // 2 parameters!
2056 FormulaToken** pArgArray[2];
2057 if (mbComputeII)
2058 pArgArray[0] = pCode - 1; // Add first argument
2059 NextToken();
2060 CompareLine();
2061 if (mbComputeII)
2063 pArgArray[1] = pCode - 1; // Add second argument
2064 HandleIIOpCode(p.get(), pArgArray, 2);
2066 PutCode(p);
2068 return mpToken->GetOpCode();
2072 void FormulaCompiler::SetError( FormulaError /*nError*/ )
2076 FormulaTokenRef FormulaCompiler::ExtendRangeReference( FormulaToken & /*rTok1*/, FormulaToken & /*rTok2*/ )
2078 return FormulaTokenRef();
2081 bool FormulaCompiler::MergeRangeReference( FormulaToken * * const pCode1, FormulaToken * const * const pCode2 )
2083 if (!isAdjacentRpnEnd( pc, pCode, pCode1, pCode2))
2084 return false;
2086 FormulaToken *p1 = *pCode1, *p2 = *pCode2;
2087 FormulaTokenRef p = ExtendRangeReference( *p1, *p2);
2088 if (!p)
2089 return false;
2091 p->IncRef();
2092 p1->DecRef();
2093 p2->DecRef();
2094 *pCode1 = p.get();
2095 --pCode;
2096 --pc;
2098 return true;
2101 bool FormulaCompiler::CompileTokenArray()
2103 glSubTotal = false;
2104 bCorrected = false;
2105 needsRPNTokenCheck = false;
2106 if (pArr->GetCodeError() == FormulaError::NONE || !mbStopOnError)
2108 if ( bAutoCorrect )
2110 aCorrectedFormula.clear();
2111 aCorrectedSymbol.clear();
2113 pArr->DelRPN();
2114 maArrIterator.Reset();
2115 pStack = nullptr;
2116 FormulaToken* pDataArray[ FORMULA_MAXTOKENS + 1 ];
2117 // Code in some places refers to the last token as 'pCode - 1', which may
2118 // point before the first element if the expression is bad. So insert a dummy
2119 // node in that place which will make that token be nullptr.
2120 pDataArray[ 0 ] = nullptr;
2121 FormulaToken** pData = pDataArray + 1;
2122 pCode = pData;
2123 bool bWasForced = pArr->IsRecalcModeForced();
2124 if ( bWasForced && bAutoCorrect )
2125 aCorrectedFormula = "=";
2126 pArr->ClearRecalcMode();
2127 maArrIterator.Reset();
2128 eLastOp = ocOpen;
2129 pc = 0;
2130 NextToken();
2131 OpCode eOp = Expression();
2132 // Some trailing garbage that doesn't form an expression?
2133 if (eOp != ocStop)
2134 SetError( FormulaError::OperatorExpected);
2135 PostProcessCode();
2137 FormulaError nErrorBeforePop = pArr->GetCodeError();
2139 while( pStack )
2140 PopTokenArray();
2141 if( pc )
2143 pArr->CreateNewRPNArrayFromData( pData, pc );
2144 if( needsRPNTokenCheck )
2145 pArr->CheckAllRPNTokens();
2148 // once an error, always an error
2149 if( pArr->GetCodeError() == FormulaError::NONE && nErrorBeforePop != FormulaError::NONE )
2150 pArr->SetCodeError( nErrorBeforePop);
2152 if (pArr->GetCodeError() != FormulaError::NONE && mbStopOnError)
2154 pArr->DelRPN();
2155 maArrIterator.Reset();
2156 pArr->SetHyperLink( false);
2159 if ( bWasForced )
2160 pArr->SetRecalcModeForced();
2162 if( nNumFmt == SvNumFormatType::UNDEFINED )
2163 nNumFmt = SvNumFormatType::NUMBER;
2164 return glSubTotal;
2167 void FormulaCompiler::PopTokenArray()
2169 if( !pStack )
2170 return;
2172 FormulaArrayStack* p = pStack;
2173 pStack = p->pNext;
2174 // obtain special RecalcMode from SharedFormula
2175 if ( pArr->IsRecalcModeAlways() )
2176 p->pArr->SetExclusiveRecalcModeAlways();
2177 else if ( !pArr->IsRecalcModeNormal() && p->pArr->IsRecalcModeNormal() )
2178 p->pArr->SetMaskedRecalcMode( pArr->GetRecalcMode() );
2179 p->pArr->SetCombinedBitsRecalcMode( pArr->GetRecalcMode() );
2180 if ( pArr->IsHyperLink() ) // fdo 87534
2181 p->pArr->SetHyperLink( true );
2182 if( p->bTemp )
2183 delete pArr;
2184 pArr = p->pArr;
2185 maArrIterator = FormulaTokenArrayPlainIterator(*pArr);
2186 maArrIterator.Jump(p->nIndex);
2187 mpLastToken = p->mpLastToken;
2188 delete p;
2191 void FormulaCompiler::CreateStringFromTokenArray( OUString& rFormula )
2193 OUStringBuffer aBuffer( pArr->GetLen() * 5 );
2194 CreateStringFromTokenArray( aBuffer );
2195 rFormula = aBuffer.makeStringAndClear();
2198 void FormulaCompiler::CreateStringFromTokenArray( OUStringBuffer& rBuffer )
2200 rBuffer.setLength(0);
2201 if( !pArr->GetLen() )
2202 return;
2204 FormulaTokenArray* pSaveArr = pArr;
2205 int nSaveIndex = maArrIterator.GetIndex();
2206 bool bODFF = FormulaGrammar::isODFF( meGrammar);
2207 if (bODFF || FormulaGrammar::isPODF( meGrammar) )
2209 // Scan token array for missing args and re-write if present.
2210 MissingConventionODF aConv( bODFF);
2211 if (pArr->NeedsPodfRewrite( aConv))
2213 pArr = pArr->RewriteMissing( aConv );
2214 maArrIterator = FormulaTokenArrayPlainIterator( *pArr );
2217 else if ( FormulaGrammar::isOOXML( meGrammar ) )
2219 // Scan token array for missing args and rewrite if present.
2220 if (pArr->NeedsOoxmlRewrite())
2222 MissingConventionOOXML aConv;
2223 pArr = pArr->RewriteMissing( aConv );
2224 maArrIterator = FormulaTokenArrayPlainIterator( *pArr );
2228 // At least one character per token, plus some are references, some are
2229 // function names, some are numbers, ...
2230 rBuffer.ensureCapacity( pArr->GetLen() * 5 );
2232 if ( pArr->IsRecalcModeForced() )
2233 rBuffer.append( '=');
2234 const FormulaToken* t = maArrIterator.First();
2235 while( t )
2236 t = CreateStringFromToken( rBuffer, t, true );
2238 if (pSaveArr != pArr)
2240 delete pArr;
2241 pArr = pSaveArr;
2242 maArrIterator = FormulaTokenArrayPlainIterator( *pArr );
2243 maArrIterator.Jump(nSaveIndex);
2247 const FormulaToken* FormulaCompiler::CreateStringFromToken( OUString& rFormula, const FormulaToken* pTokenP )
2249 OUStringBuffer aBuffer;
2250 const FormulaToken* p = CreateStringFromToken( aBuffer, pTokenP );
2251 rFormula += aBuffer;
2252 return p;
2255 const FormulaToken* FormulaCompiler::CreateStringFromToken( OUStringBuffer& rBuffer, const FormulaToken* pTokenP,
2256 bool bAllowArrAdvance )
2258 bool bNext = true;
2259 bool bSpaces = false;
2260 const FormulaToken* t = pTokenP;
2261 OpCode eOp = t->GetOpCode();
2262 if( eOp >= ocAnd && eOp <= ocOr )
2264 // AND, OR infix?
2265 if ( bAllowArrAdvance )
2266 t = maArrIterator.Next();
2267 else
2268 t = maArrIterator.PeekNext();
2269 bNext = false;
2270 bSpaces = ( !t || t->GetOpCode() != ocOpen );
2272 if( bSpaces )
2273 rBuffer.append( ' ');
2275 if( eOp == ocSpaces )
2277 bool bWriteSpaces = true;
2278 if (mxSymbols->isODFF())
2280 const FormulaToken* p = maArrIterator.PeekPrevNoSpaces();
2281 bool bIntersectionOp = (p && p->GetOpCode() == ocColRowName);
2282 if (bIntersectionOp)
2284 p = maArrIterator.PeekNextNoSpaces();
2285 bIntersectionOp = (p && p->GetOpCode() == ocColRowName);
2287 if (bIntersectionOp)
2289 rBuffer.append( "!!");
2290 bWriteSpaces = false;
2293 if (bWriteSpaces)
2295 // ODF v1.3 OpenFormula 5.14 Whitespace states "whitespace shall
2296 // not separate a function name from its initial opening
2297 // parenthesis".
2299 // ECMA-376-1:2016 18.17.2 Syntax states "that no space characters
2300 // shall separate a function-name from the left parenthesis (()
2301 // that follows it." and Excel even chokes on it.
2303 // Suppress/remove it in any case also in UI, it will not be
2304 // preserved.
2305 const FormulaToken* p = maArrIterator.PeekPrevNoSpaces();
2306 if (p && p->IsFunction())
2308 p = maArrIterator.PeekNextNoSpaces();
2309 if (p && p->GetOpCode() == ocOpen)
2310 bWriteSpaces = false;
2313 if (bWriteSpaces)
2315 // most times it's just one blank
2316 sal_uInt8 n = t->GetByte();
2317 for ( sal_uInt8 j=0; j<n; ++j )
2319 rBuffer.append( ' ');
2323 else if( eOp >= ocInternalBegin && eOp <= ocInternalEnd )
2324 rBuffer.appendAscii( pInternal[ eOp - ocInternalBegin ] );
2325 else if (eOp == ocIntersect)
2327 // Nasty, ugly, horrific, terrifying...
2328 if (FormulaGrammar::isExcelSyntax( meGrammar))
2329 rBuffer.append(' ');
2330 else
2331 rBuffer.append( mxSymbols->getSymbol( eOp));
2333 else if( static_cast<sal_uInt16>(eOp) < mxSymbols->getSymbolCount()) // Keyword:
2334 rBuffer.append( mxSymbols->getSymbol( eOp));
2335 else
2337 SAL_WARN( "formula.core","unknown OpCode");
2338 rBuffer.append( GetNativeSymbol( ocErrName ));
2340 if( bNext )
2342 if (t->IsExternalRef())
2344 CreateStringFromExternal( rBuffer, pTokenP);
2346 else
2348 switch( t->GetType() )
2350 case svDouble:
2351 AppendDouble( rBuffer, t->GetDouble() );
2352 break;
2354 case svString:
2355 if( eOp == ocBad || eOp == ocStringXML )
2356 rBuffer.append( t->GetString().getString());
2357 else
2358 AppendString( rBuffer, t->GetString().getString() );
2359 break;
2360 case svSingleRef:
2361 CreateStringFromSingleRef( rBuffer, t);
2362 break;
2363 case svDoubleRef:
2364 CreateStringFromDoubleRef( rBuffer, t);
2365 break;
2366 case svMatrix:
2367 case svMatrixCell:
2368 CreateStringFromMatrix( rBuffer, t );
2369 break;
2371 case svIndex:
2372 CreateStringFromIndex( rBuffer, t );
2373 if (t->GetOpCode() == ocTableRef && bAllowArrAdvance && NeedsTableRefTransformation())
2375 // Suppress all TableRef related tokens, the resulting
2376 // range was written by CreateStringFromIndex().
2377 const FormulaToken* const p = maArrIterator.PeekNext();
2378 if (p && p->GetOpCode() == ocTableRefOpen)
2380 int nLevel = 0;
2383 t = maArrIterator.Next();
2384 if (!t)
2385 break;
2387 // Switch cases correspond with those in
2388 // ScCompiler::HandleTableRef()
2389 switch (t->GetOpCode())
2391 case ocTableRefOpen:
2392 ++nLevel;
2393 break;
2394 case ocTableRefClose:
2395 --nLevel;
2396 break;
2397 case ocTableRefItemAll:
2398 case ocTableRefItemHeaders:
2399 case ocTableRefItemData:
2400 case ocTableRefItemTotals:
2401 case ocTableRefItemThisRow:
2402 case ocSep:
2403 case ocPush:
2404 case ocRange:
2405 case ocSpaces:
2406 break;
2407 default:
2408 nLevel = 0;
2409 bNext = false;
2411 } while (nLevel);
2414 break;
2415 case svExternal:
2417 // mapped or translated name of AddIns
2418 OUString aAddIn( t->GetExternal() );
2419 bool bMapped = mxSymbols->isPODF(); // ODF 1.1 directly uses programmatical name
2420 if (!bMapped && mxSymbols->hasExternals())
2422 ExternalHashMap::const_iterator iLook = mxSymbols->getReverseExternalHashMap().find( aAddIn);
2423 if (iLook != mxSymbols->getReverseExternalHashMap().end())
2425 aAddIn = (*iLook).second;
2426 bMapped = true;
2429 if (!bMapped && !mxSymbols->isEnglish())
2430 LocalizeString( aAddIn );
2431 rBuffer.append( aAddIn);
2433 break;
2434 case svError:
2435 AppendErrorConstant( rBuffer, t->GetError());
2436 break;
2437 case svByte:
2438 case svJump:
2439 case svFAP:
2440 case svMissing:
2441 case svSep:
2442 break; // Opcodes
2443 default:
2444 SAL_WARN("formula.core", "FormulaCompiler::GetStringFromToken: unknown token type " << t->GetType());
2445 } // of switch
2448 if( bSpaces )
2449 rBuffer.append( ' ');
2450 if ( bAllowArrAdvance )
2452 if( bNext )
2453 t = maArrIterator.Next();
2454 return t;
2456 return pTokenP;
2460 void FormulaCompiler::AppendDouble( OUStringBuffer& rBuffer, double fVal ) const
2462 if ( mxSymbols->isEnglish() )
2464 ::rtl::math::doubleToUStringBuffer( rBuffer, fVal,
2465 rtl_math_StringFormat_Automatic,
2466 rtl_math_DecimalPlaces_Max, '.', true );
2468 else
2470 SvtSysLocale aSysLocale;
2471 ::rtl::math::doubleToUStringBuffer( rBuffer, fVal,
2472 rtl_math_StringFormat_Automatic,
2473 rtl_math_DecimalPlaces_Max,
2474 aSysLocale.GetLocaleData().getNumDecimalSep()[0],
2475 true );
2479 void FormulaCompiler::AppendBoolean( OUStringBuffer& rBuffer, bool bVal ) const
2481 rBuffer.append( mxSymbols->getSymbol( bVal ? ocTrue : ocFalse ) );
2484 void FormulaCompiler::AppendString( OUStringBuffer& rBuffer, const OUString & rStr )
2486 rBuffer.append( '"');
2487 if ( lcl_UnicodeStrChr( rStr.getStr(), '"' ) == nullptr )
2488 rBuffer.append( rStr );
2489 else
2491 OUString aStr = rStr.replaceAll( "\"", "\"\"" );
2492 rBuffer.append(aStr);
2494 rBuffer.append( '"');
2497 bool FormulaCompiler::NeedsTableRefTransformation() const
2499 // Currently only UI representations and OOXML export use Table structured
2500 // references. Not defined in ODFF.
2501 // Unnecessary to explicitly check for ODFF grammar as the ocTableRefOpen
2502 // symbol is not defined there.
2503 return mxSymbols->getSymbol( ocTableRefOpen).isEmpty() || FormulaGrammar::isPODF( meGrammar);
2506 void FormulaCompiler::UpdateSeparatorsNative(
2507 const OUString& rSep, const OUString& rArrayColSep, const OUString& rArrayRowSep )
2509 NonConstOpCodeMapPtr xSymbolsNative;
2510 lcl_fillNativeSymbols( xSymbolsNative);
2511 xSymbolsNative->putOpCode( rSep, ocSep, nullptr);
2512 xSymbolsNative->putOpCode( rArrayColSep, ocArrayColSep, nullptr);
2513 xSymbolsNative->putOpCode( rArrayRowSep, ocArrayRowSep, nullptr);
2516 void FormulaCompiler::ResetNativeSymbols()
2518 NonConstOpCodeMapPtr xSymbolsNative;
2519 lcl_fillNativeSymbols( xSymbolsNative, true);
2520 lcl_fillNativeSymbols( xSymbolsNative);
2523 void FormulaCompiler::SetNativeSymbols( const OpCodeMapPtr& xMap )
2525 NonConstOpCodeMapPtr xSymbolsNative;
2526 lcl_fillNativeSymbols( xSymbolsNative);
2527 xSymbolsNative->copyFrom( *xMap );
2531 OpCode FormulaCompiler::NextToken()
2533 if( !GetToken() )
2534 return ocStop;
2535 OpCode eOp = mpToken->GetOpCode();
2536 // There must be an operator before a push
2537 if ( (eOp == ocPush || eOp == ocColRowNameAuto) &&
2538 !( (eLastOp == ocOpen) || (eLastOp == ocSep) ||
2539 (SC_OPCODE_START_BIN_OP <= eLastOp && eLastOp < SC_OPCODE_STOP_UN_OP)) )
2540 SetError( FormulaError::OperatorExpected);
2541 // Operator and Plus => operator
2542 if (eOp == ocAdd && (eLastOp == ocOpen || eLastOp == ocSep ||
2543 (SC_OPCODE_START_BIN_OP <= eLastOp && eLastOp < SC_OPCODE_STOP_UN_OP)))
2545 FormulaCompilerRecursionGuard aRecursionGuard( nRecursion );
2546 eOp = NextToken();
2548 else
2550 // Before an operator there must not be another operator, with the
2551 // exception of AND and OR.
2552 if ( eOp != ocAnd && eOp != ocOr &&
2553 (SC_OPCODE_START_BIN_OP <= eOp && eOp < SC_OPCODE_STOP_BIN_OP )
2554 && (eLastOp == ocOpen || eLastOp == ocSep ||
2555 (SC_OPCODE_START_BIN_OP <= eLastOp && eLastOp < SC_OPCODE_STOP_UN_OP)))
2557 SetError( FormulaError::VariableExpected);
2558 if ( bAutoCorrect && !pStack )
2560 if ( eOp == eLastOp || eLastOp == ocOpen )
2561 { // throw away duplicated operator
2562 aCorrectedSymbol.clear();
2563 bCorrected = true;
2565 else
2567 sal_Int32 nPos = aCorrectedFormula.getLength();
2568 if ( nPos )
2570 nPos--;
2571 sal_Unicode c = aCorrectedFormula[ nPos ];
2572 switch ( eOp )
2573 { // swap operators
2574 case ocGreater:
2575 if ( c == mxSymbols->getSymbolChar( ocEqual) )
2576 { // >= instead of =>
2577 aCorrectedFormula = aCorrectedFormula.replaceAt( nPos, 1,
2578 OUString( mxSymbols->getSymbolChar(ocGreater) ) );
2579 aCorrectedSymbol = OUString(c);
2580 bCorrected = true;
2582 break;
2583 case ocLess:
2584 if ( c == mxSymbols->getSymbolChar( ocEqual) )
2585 { // <= instead of =<
2586 aCorrectedFormula = aCorrectedFormula.replaceAt( nPos, 1,
2587 OUString( mxSymbols->getSymbolChar(ocLess) ) );
2588 aCorrectedSymbol = OUString(c);
2589 bCorrected = true;
2591 else if ( c == mxSymbols->getSymbolChar( ocGreater) )
2592 { // <> instead of ><
2593 aCorrectedFormula = aCorrectedFormula.replaceAt( nPos, 1,
2594 OUString( mxSymbols->getSymbolChar(ocLess) ) );
2595 aCorrectedSymbol = OUString(c);
2596 bCorrected = true;
2598 break;
2599 case ocMul:
2600 if ( c == mxSymbols->getSymbolChar( ocSub) )
2601 { // *- instead of -*
2602 aCorrectedFormula = aCorrectedFormula.replaceAt( nPos, 1,
2603 OUString( mxSymbols->getSymbolChar(ocMul) ) );
2604 aCorrectedSymbol = OUString(c);
2605 bCorrected = true;
2607 break;
2608 case ocDiv:
2609 if ( c == mxSymbols->getSymbolChar( ocSub) )
2610 { // /- instead of -/
2611 aCorrectedFormula = aCorrectedFormula.replaceAt( nPos, 1,
2612 OUString( mxSymbols->getSymbolChar(ocDiv) ) );
2613 aCorrectedSymbol = OUString(c);
2614 bCorrected = true;
2616 break;
2617 default:
2618 ; // nothing
2624 // Nasty, ugly, horrific, terrifying... significant whitespace...
2625 if (eOp == ocSpaces && FormulaGrammar::isExcelSyntax( meGrammar))
2627 // Fake an intersection op as last op for the next round, but at
2628 // least roughly check if it could make sense at all.
2629 FormulaToken* pPrev = maArrIterator.PeekPrevNoSpaces();
2630 if (pPrev && isPotentialRangeType( pPrev, false, false))
2632 FormulaToken* pNext = maArrIterator.PeekNextNoSpaces();
2633 if (pNext && isPotentialRangeType( pNext, false, true))
2634 eLastOp = ocIntersect;
2635 else
2636 eLastOp = eOp;
2638 else
2639 eLastOp = eOp;
2641 else
2642 eLastOp = eOp;
2644 return eOp;
2647 void FormulaCompiler::PutCode( FormulaTokenRef& p )
2649 if( pc >= FORMULA_MAXTOKENS - 1 )
2651 if ( pc == FORMULA_MAXTOKENS - 1 )
2653 p = new FormulaByteToken( ocStop );
2654 p->IncRef();
2655 *pCode++ = p.get();
2656 ++pc;
2658 SetError( FormulaError::CodeOverflow);
2659 return;
2661 if (pArr->GetCodeError() != FormulaError::NONE && mbJumpCommandReorder)
2662 return;
2663 ForceArrayOperator( p);
2664 p->IncRef();
2665 *pCode++ = p.get();
2666 pc++;
2670 bool FormulaCompiler::HandleExternalReference( const FormulaToken& /*_aToken*/)
2672 return true;
2675 bool FormulaCompiler::HandleRange()
2677 return true;
2680 bool FormulaCompiler::HandleColRowName()
2682 return true;
2685 bool FormulaCompiler::HandleDbData()
2687 return true;
2690 bool FormulaCompiler::HandleTableRef()
2692 return true;
2695 void FormulaCompiler::CreateStringFromSingleRef( OUStringBuffer& /*rBuffer*/, const FormulaToken* /*pToken*/) const
2699 void FormulaCompiler::CreateStringFromDoubleRef( OUStringBuffer& /*rBuffer*/, const FormulaToken* /*pToken*/) const
2703 void FormulaCompiler::CreateStringFromIndex( OUStringBuffer& /*rBuffer*/, const FormulaToken* /*pToken*/) const
2707 void FormulaCompiler::CreateStringFromMatrix( OUStringBuffer& /*rBuffer*/, const FormulaToken* /*pToken*/) const
2711 void FormulaCompiler::CreateStringFromExternal( OUStringBuffer& /*rBuffer*/, const FormulaToken* /*pToken*/) const
2715 void FormulaCompiler::LocalizeString( OUString& /*rName*/ ) const
2719 formula::ParamClass FormulaCompiler::GetForceArrayParameter( const FormulaToken* /*pToken*/, sal_uInt16 /*nParam*/ ) const
2721 return ParamClass::Unknown;
2724 void FormulaCompiler::ForceArrayOperator( FormulaTokenRef const & rCurr )
2726 if (rCurr->GetInForceArray() != ParamClass::Unknown)
2727 // Already set, unnecessary to evaluate again. This happens by calls to
2728 // CurrentFactor::operator=() while descending through Factor() and
2729 // then ascending back (and down and up, ...),
2730 // CheckSetForceArrayParameter() and later PutCode().
2731 return;
2733 const OpCode eOp = rCurr->GetOpCode();
2734 const StackVar eType = rCurr->GetType();
2735 bool bInlineArray = false;
2736 if (!(eOp != ocPush && (eType == svByte || eType == svJump))
2737 && !(bInlineArray = (eOp == ocPush && eType == svMatrix)))
2738 return;
2740 // Return class for inline arrays and functions returning array/matrix.
2741 // It's somewhat unclear what Excel actually does there and in
2742 // ECMA-376-1:2016 OOXML mentions "call to ... shall be an array formula"
2743 // only for FREQUENCY() and TRANSPOSE() but not for any other function
2744 // returning array/matrix or inline arrays, though for the latter has one
2745 // example in 18.17.2 Syntax:
2746 // "SUM(SQRT({1,2,3,4})) returns 6.14 when entered normally". However,
2747 // these need to be treated similar but not as ParamClass::ForceArray
2748 // (which would contradict the example in
2749 // https://bugs.documentfoundation.org/show_bug.cgi?id=122301#c19 and A6 of
2750 // https://bugs.documentfoundation.org/show_bug.cgi?id=133260#c10 ).
2751 // See also
2752 // commit d0ded163d8e93dc5b10d7a7c9bdab1d0a6a50bac
2753 // commit 5413c8871dec08eff19f514f5f391b946a45c86c
2754 constexpr ParamClass eArrayReturn = ParamClass::ForceArrayReturn;
2756 if (bInlineArray)
2758 // rCurr->SetInForceArray() can not be used with ocPush.
2759 if (pCurrentFactorToken && pCurrentFactorToken->GetInForceArray() == ParamClass::Unknown)
2761 // Propagate to caller as if a function returning an array/matrix
2762 // was called (see also below).
2763 pCurrentFactorToken->SetInForceArray( eArrayReturn);
2765 return;
2768 if (!pCurrentFactorToken || (pCurrentFactorToken.get() == rCurr.get()))
2770 if (!pCurrentFactorToken && mbMatrixFlag)
2772 // An array/matrix formula acts as ForceArray on all top level
2773 // operators and function calls, so that can be inherited properly
2774 // below.
2775 rCurr->SetInForceArray( ParamClass::ForceArray);
2777 return;
2780 // Inherited parameter class.
2781 const formula::ParamClass eForceType = pCurrentFactorToken->GetInForceArray();
2782 if (eForceType == ParamClass::ForceArray || eForceType == ParamClass::ReferenceOrRefArray)
2784 // ReferenceOrRefArray was set only if in ForceArray context already,
2785 // it is valid for the one function only to indicate the preferred
2786 // return type. Propagate as ForceArray if not another parameter
2787 // handling ReferenceOrRefArray.
2788 if (nCurrentFactorParam > 0
2789 && (GetForceArrayParameter( pCurrentFactorToken.get(), static_cast<sal_uInt16>(nCurrentFactorParam - 1))
2790 == ParamClass::ReferenceOrRefArray))
2791 rCurr->SetInForceArray( ParamClass::ReferenceOrRefArray);
2792 else
2793 rCurr->SetInForceArray( ParamClass::ForceArray);
2794 return;
2796 else if (eForceType == ParamClass::ReferenceOrForceArray)
2798 // Inherit further only if the return class of the nested function is
2799 // not Reference. Else flag as suppressed.
2800 if (GetForceArrayParameter( rCurr.get(), SAL_MAX_UINT16) != ParamClass::Reference)
2801 rCurr->SetInForceArray( eForceType);
2802 else
2803 rCurr->SetInForceArray( ParamClass::SuppressedReferenceOrForceArray);
2804 return;
2807 if (nCurrentFactorParam <= 0)
2808 return;
2810 // Actual current parameter's class.
2811 const formula::ParamClass eParamType = GetForceArrayParameter(
2812 pCurrentFactorToken.get(), static_cast<sal_uInt16>(nCurrentFactorParam - 1));
2813 if (eParamType == ParamClass::ForceArray)
2814 rCurr->SetInForceArray( eParamType);
2815 else if (eParamType == ParamClass::ReferenceOrForceArray)
2817 if (GetForceArrayParameter( rCurr.get(), SAL_MAX_UINT16) != ParamClass::Reference)
2818 rCurr->SetInForceArray( eParamType);
2819 else
2820 rCurr->SetInForceArray( formula::ParamClass::SuppressedReferenceOrForceArray);
2823 // Propagate a ForceArrayReturn to caller if the called function
2824 // returns one and the caller so far does not have a stronger array
2825 // mode set.
2826 if (pCurrentFactorToken->GetInForceArray() == ParamClass::Unknown)
2828 if (IsMatrixFunction( eOp))
2829 pCurrentFactorToken->SetInForceArray( eArrayReturn);
2830 else if (GetForceArrayParameter( rCurr.get(), SAL_MAX_UINT16) == ParamClass::ForceArrayReturn)
2831 pCurrentFactorToken->SetInForceArray( ParamClass::ForceArrayReturn);
2835 void FormulaCompiler::CheckSetForceArrayParameter( FormulaTokenRef const & rCurr, sal_uInt8 nParam )
2837 if (!pCurrentFactorToken)
2838 return;
2840 nCurrentFactorParam = nParam + 1;
2842 ForceArrayOperator( rCurr);
2845 void FormulaCompiler::PushTokenArray( FormulaTokenArray* pa, bool bTemp )
2847 if ( bAutoCorrect && !pStack )
2848 { // don't merge stacked subroutine code into entered formula
2849 aCorrectedFormula += aCorrectedSymbol;
2850 aCorrectedSymbol.clear();
2852 FormulaArrayStack* p = new FormulaArrayStack;
2853 p->pNext = pStack;
2854 p->pArr = pArr;
2855 p->nIndex = maArrIterator.GetIndex();
2856 p->mpLastToken = mpLastToken;
2857 p->bTemp = bTemp;
2858 pStack = p;
2859 pArr = pa;
2860 maArrIterator = FormulaTokenArrayPlainIterator( *pArr );
2863 } // namespace formula
2865 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */