LibreOffice Module formula (master) 1
FormulaCompiler.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19#include <sal/macros.h>
20#include <sal/log.hxx>
21#include <rtl/math.hxx>
24#include <formula/token.hxx>
26#include <o3tl/string_view.hxx>
27#include <core_resource.hxx>
28#include <core_resource.hrc>
29
30#include <svl/zforlist.hxx>
32#include <vcl/svapp.hxx>
33#include <vcl/settings.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>
40
41namespace formula
42{
43 using namespace ::com::sun::star;
44
45 static const char* pInternal[2] = { "TTT", "__DEBUG_VAR" };
46
47namespace {
48
49class FormulaCompilerRecursionGuard
50{
51 private:
52 short& rRecursion;
53 public:
54 explicit FormulaCompilerRecursionGuard( short& rRec )
55 : rRecursion( rRec ) { ++rRecursion; }
56 ~FormulaCompilerRecursionGuard() { --rRecursion; }
57};
58
59SvNumFormatType lcl_GetRetFormat( OpCode eOpCode )
60{
61 switch (eOpCode)
62 {
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;
121 }
122}
123
124void lclPushOpCodeMapEntry( ::std::vector< sheet::FormulaOpCodeMapEntry >& rVec,
125 const OUString* pTable, sal_uInt16 nOpCode )
126{
127 sheet::FormulaOpCodeMapEntry aEntry;
128 aEntry.Token.OpCode = nOpCode;
129 aEntry.Name = pTable[nOpCode];
130 rVec.push_back( aEntry);
131}
132
133void lclPushOpCodeMapEntries( ::std::vector< sheet::FormulaOpCodeMapEntry >& rVec,
134 const OUString* pTable, sal_uInt16 nOpCodeBeg, sal_uInt16 nOpCodeEnd )
135{
136 for (sal_uInt16 nOpCode = nOpCodeBeg; nOpCode < nOpCodeEnd; ++nOpCode)
137 lclPushOpCodeMapEntry( rVec, pTable, nOpCode );
138}
139
140void lclPushOpCodeMapEntries( ::std::vector< sheet::FormulaOpCodeMapEntry >& rVec,
141 const OUString* pTable, const sal_uInt16* pnOpCodes, size_t nCount )
142{
143 for (const sal_uInt16* pnEnd = pnOpCodes + nCount; pnOpCodes < pnEnd; ++pnOpCodes)
144 lclPushOpCodeMapEntry( rVec, pTable, *pnOpCodes );
145}
146
147CharClass* createCharClassIfNonEnglishUI()
148{
149 const LanguageTag& rLanguageTag( Application::GetSettings().GetUILanguageTag());
150 if (rLanguageTag.getLanguage() == "en")
151 return nullptr;
152 return new CharClass( ::comphelper::getProcessComponentContext(), rLanguageTag);
153}
154
155class OpCodeList
156{
157public:
158
159 OpCodeList(const std::pair<const char*, int>* pSymbols, const FormulaCompiler::NonConstOpCodeMapPtr&,
161 OpCodeList(const std::pair<TranslateId, int>* pSymbols, const FormulaCompiler::NonConstOpCodeMapPtr&,
163
164private:
165 bool getOpCodeString( OUString& rStr, sal_uInt16 nOp );
166 void putDefaultOpCode( const FormulaCompiler::NonConstOpCodeMapPtr& xMap, sal_uInt16 nOp, const CharClass* pCharClass );
167
168private:
170 const std::pair<const char*, int>* mpSymbols1;
171 const std::pair<TranslateId, int>* mpSymbols2;
172};
173
174OpCodeList::OpCodeList(const std::pair<const char*, int>* pSymbols, const FormulaCompiler::NonConstOpCodeMapPtr& xMap,
176 : meSepType(eSepType)
177 , mpSymbols1(pSymbols)
178 , mpSymbols2(nullptr)
179{
180 std::unique_ptr<CharClass> xCharClass( xMap->isEnglish() ? nullptr : createCharClassIfNonEnglishUI());
181 const CharClass* pCharClass = xCharClass.get();
182 if (meSepType == FormulaCompiler::SeparatorType::RESOURCE_BASE)
183 {
184 for (sal_uInt16 i = 0; i <= SC_OPCODE_LAST_OPCODE_ID; ++i)
185 {
186 putDefaultOpCode( xMap, i, pCharClass);
187 }
188 }
189 else
190 {
191 for (sal_uInt16 i = 0; i <= SC_OPCODE_LAST_OPCODE_ID; ++i)
192 {
193 OUString aOpStr;
194 if ( getOpCodeString( aOpStr, i) )
195 xMap->putOpCode( aOpStr, OpCode(i), pCharClass);
196 else
197 putDefaultOpCode( xMap, i, pCharClass);
198 }
199 }
200}
201
202OpCodeList::OpCodeList(const std::pair<TranslateId, int>* pSymbols, const FormulaCompiler::NonConstOpCodeMapPtr& xMap,
203 FormulaCompiler::SeparatorType eSepType)
204 : meSepType(eSepType)
205 , mpSymbols1(nullptr)
206 , mpSymbols2(pSymbols)
207{
208 std::unique_ptr<CharClass> xCharClass( xMap->isEnglish() ? nullptr : createCharClassIfNonEnglishUI());
209 const CharClass* pCharClass = xCharClass.get();
210 if (meSepType == FormulaCompiler::SeparatorType::RESOURCE_BASE)
211 {
212 for (sal_uInt16 i = 0; i <= SC_OPCODE_LAST_OPCODE_ID; ++i)
213 {
214 putDefaultOpCode( xMap, i, pCharClass);
215 }
216 }
217 else
218 {
219 for (sal_uInt16 i = 0; i <= SC_OPCODE_LAST_OPCODE_ID; ++i)
220 {
221 OUString aOpStr;
222 if ( getOpCodeString( aOpStr, i) )
223 xMap->putOpCode( aOpStr, OpCode(i), pCharClass);
224 else
225 putDefaultOpCode( xMap, i, pCharClass);
226 }
227 }
228}
229
230bool OpCodeList::getOpCodeString( OUString& rStr, sal_uInt16 nOp )
231{
232 switch (nOp)
233 {
234 case SC_OPCODE_SEP:
235 {
236 if (meSepType == FormulaCompiler::SeparatorType::SEMICOLON_BASE)
237 {
238 rStr = ";";
239 return true;
240 }
241 }
242 break;
244 {
245 if (meSepType == FormulaCompiler::SeparatorType::SEMICOLON_BASE)
246 {
247 rStr = ";";
248 return true;
249 }
250 }
251 break;
253 {
254 if (meSepType == FormulaCompiler::SeparatorType::SEMICOLON_BASE)
255 {
256 rStr = "|";
257 return true;
258 }
259 }
260 break;
261 }
262
263 return false;
264}
265
266void OpCodeList::putDefaultOpCode( const FormulaCompiler::NonConstOpCodeMapPtr& xMap, sal_uInt16 nOp,
267 const CharClass* pCharClass )
268{
269 OUString sKey;
270 if (mpSymbols1)
271 {
272 const char* pKey = nullptr;
273 for (const std::pair<const char*, int>* pSymbol = mpSymbols1; pSymbol->first; ++pSymbol)
274 {
275 if (nOp == pSymbol->second)
276 {
277 pKey = pSymbol->first;
278 break;
279 }
280 }
281 if (!pKey)
282 return;
283 sKey = OUString::createFromAscii(pKey);
284 }
285 else if (mpSymbols2)
286 {
287 TranslateId pKey;
288 for (const std::pair<TranslateId, int>* pSymbol = mpSymbols2; pSymbol->first; ++pSymbol)
289 {
290 if (nOp == pSymbol->second)
291 {
292 pKey = pSymbol->first;
293 break;
294 }
295 }
296 if (!pKey)
297 return;
298 sKey = ForResId(pKey);
299 }
300 xMap->putOpCode(sKey, OpCode(nOp), pCharClass);
301}
302
303// static
304const sal_Unicode* lcl_UnicodeStrChr( const sal_Unicode* pStr, sal_Unicode c )
305{
306 if ( !pStr )
307 return nullptr;
308 while ( *pStr )
309 {
310 if ( *pStr == c )
311 return pStr;
312 pStr++;
313 }
314 return nullptr;
315}
316
317struct OpCodeMapData
318{
319 FormulaCompiler::NonConstOpCodeMapPtr mxSymbolMap;
320 std::mutex maMtx;
321};
322
323
324bool isPotentialRangeLeftOp( OpCode eOp )
325{
326 switch (eOp)
327 {
328 case ocClose:
329 return true;
330 default:
331 return false;
332 }
333}
334
335bool isRangeResultFunction( OpCode eOp )
336{
337 switch (eOp)
338 {
339 case ocIndirect:
340 case ocOffset:
341 return true;
342 default:
343 return false;
344 }
345}
346
347bool isRangeResultOpCode( OpCode eOp )
348{
349 switch (eOp)
350 {
351 case ocRange:
352 case ocUnion:
353 case ocIntersect:
354 case ocIndirect:
355 case ocOffset:
356 return true;
357 default:
358 return false;
359 }
360}
361
371bool isPotentialRangeType( FormulaToken const * pToken, bool bRPN, bool bRight )
372{
373 switch (pToken->GetType())
374 {
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?
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());
398 }
399}
400
401bool isIntersectable( FormulaToken** pCode1, FormulaToken** pCode2 )
402{
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;
408}
409
410bool isAdjacentRpnEnd( sal_uInt16 nPC,
411 FormulaToken const * const * const pCode,
412 FormulaToken const * const * const pCode1,
413 FormulaToken const * const * const pCode2 )
414{
415 return nPC >= 2 && pCode1 && pCode2 &&
416 (pCode2 - pCode1 == 1) && (pCode - pCode2 == 1) &&
417 (*pCode1 != nullptr) && (*pCode2 != nullptr);
418}
419
420bool isAdjacentOrGapRpnEnd( sal_uInt16 nPC,
421 FormulaToken const * const * const pCode,
422 FormulaToken const * const * const pCode1,
423 FormulaToken const * const * const pCode2 )
424{
425 return nPC >= 2 && pCode1 && pCode2 &&
426 (pCode2 > pCode1) && (pCode - pCode2 == 1) &&
427 (*pCode1 != nullptr) && (*pCode2 != nullptr);
428}
429
430
431} // namespace
432
433
434void FormulaCompiler::OpCodeMap::putExternal( const OUString & rSymbol, const OUString & rAddIn )
435{
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)
443 {
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);
448 }
449}
450
451void FormulaCompiler::OpCodeMap::putExternalSoftly( const OUString & rSymbol, const OUString & rAddIn )
452{
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)
457 {
458 bOk = maReverseExternalHashMap.emplace(rAddIn, rSymbol).second;
459 SAL_INFO_IF( !bOk, "formula.core", "OpCodeMap::putExternalSoftly: AddIn not inserted, " << rAddIn << " -> " << rSymbol);
460 }
461}
462
463uno::Sequence< sheet::FormulaToken > FormulaCompiler::OpCodeMap::createSequenceOfFormulaTokens(
464 const FormulaCompiler& rCompiler, const uno::Sequence< OUString >& rNames ) const
465{
466 const sal_Int32 nLen = rNames.getLength();
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)
472 {
473 OpCodeHashMap::const_iterator iLook( maHashMap.find( *pName));
474 if (iLook != maHashMap.end())
475 pToken->OpCode = (*iLook).second;
476 else
477 {
478 OUString aIntName;
479 if (hasExternals())
480 {
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.
486 }
487 if (aIntName.isEmpty())
488 aIntName = rCompiler.FindAddInFunction(*pName, !isEnglish()); // bLocalFirst=false for english
489 if (aIntName.isEmpty())
490 pToken->OpCode = getOpCodeUnknown();
491 else
492 {
493 pToken->OpCode = ocExternal;
494 pToken->Data <<= aIntName;
495 }
496 }
497 }
498 return aTokens;
499}
500
501uno::Sequence< sheet::FormulaOpCodeMapEntry > FormulaCompiler::OpCodeMap::createSequenceOfAvailableMappings(
502 const FormulaCompiler& rCompiler, const sal_Int32 nGroups ) const
503{
504 using namespace sheet;
505
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;
510
511 if (nGroups == FormulaMapGroup::SPECIAL)
512 {
513 // Use specific order, keep in sync with
514 // offapi/com/sun/star/sheet/FormulaMapGroupSpecialOffset.idl
515 static const struct
516 {
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 }
535 };
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);
541
542 for (auto& i : aMap)
543 {
544 size_t nIndex = static_cast< size_t >( i.nOff );
545 if (aVec.size() <= nIndex)
546 {
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 );
553 }
554 aEntry.Token.OpCode = i.eOp;
555 aVec[nIndex] = aEntry;
556 }
557 }
558 else
559 {
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. */
563
564 // Anything else but SPECIAL.
565 if ((nGroups & FormulaMapGroup::SEPARATORS) != 0)
566 {
567 static const sal_uInt16 aOpCodes[] = {
571 };
572 lclPushOpCodeMapEntries( aVec, mpTable.get(), aOpCodes, SAL_N_ELEMENTS(aOpCodes) );
573 }
574 if ((nGroups & FormulaMapGroup::ARRAY_SEPARATORS) != 0)
575 {
576 static const sal_uInt16 aOpCodes[] = {
581 };
582 lclPushOpCodeMapEntries( aVec, mpTable.get(), aOpCodes, SAL_N_ELEMENTS(aOpCodes) );
583 }
584 if ((nGroups & FormulaMapGroup::UNARY_OPERATORS) != 0)
585 {
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)
594 {
595 lclPushOpCodeMapEntry( aVec, mpTable.get(), nOp );
596 }
597 }
598 if ((nGroups & FormulaMapGroup::BINARY_OPERATORS) != 0)
599 {
600 for (sal_uInt16 nOp = SC_OPCODE_START_BIN_OP; nOp < SC_OPCODE_STOP_BIN_OP && nOp < mnSymbols; ++nOp)
601 {
602 switch (nOp)
603 {
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 );
611 }
612 }
613 }
614 if ((nGroups & FormulaMapGroup::FUNCTIONS) != 0)
615 {
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[] = {
630 };
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)
634 {
635 switch (nOp)
636 {
637 // NO_NAME is in SPECIAL.
638 case SC_OPCODE_NO_NAME :
639 break; // nothing,
640 default:
641 lclPushOpCodeMapEntry( aVec, mpTable.get(), nOp );
642 }
643 }
644 // If AddIn functions are present in this mapping, use them, and only those.
645 if (hasExternals())
646 {
647 for (auto const& elem : maExternalHashMap)
648 {
649 FormulaOpCodeMapEntry aEntry;
650 aEntry.Name = elem.first;
651 aEntry.Token.Data <<= elem.second;
652 aEntry.Token.OpCode = ocExternal;
653 aVec.push_back( aEntry);
654 }
655 }
656 else
657 {
658 rCompiler.fillAddInToken( aVec, isEnglish());
659 }
660 }
661 }
662 return uno::Sequence< FormulaOpCodeMapEntry >(aVec.data(), aVec.size());
663}
664
665
666void FormulaCompiler::OpCodeMap::putOpCode( const OUString & rStr, const OpCode eOp, const CharClass* pCharClass )
667{
668 if (0 < eOp && sal_uInt16(eOp) < mnSymbols)
669 {
670 bool bPutOp = mpTable[eOp].isEmpty();
671 bool bRemoveFromMap = false;
672 if (!bPutOp)
673 {
674 switch (eOp)
675 {
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)
731 {
732 // Both bPutOp and bRemoveFromMap stay false.
733 break;
734 }
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);
741 }
742 }
743
744 // Case preserving opcode -> string, upper string -> opcode
745 if (bRemoveFromMap)
746 {
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);
752 }
753 if (bPutOp)
754 mpTable[eOp] = rStr;
755 OUString aUpper( pCharClass ? pCharClass->uppercase( rStr) : rStr.toAsciiUpperCase());
756 maHashMap.emplace(aUpper, eOp);
757 }
758 else
759 {
760 SAL_WARN( "formula.core", "OpCodeMap::putOpCode: OpCode out of range");
761 }
762}
763
764
765FormulaCompiler::FormulaCompiler( FormulaTokenArray& rArr, bool bComputeII, bool bMatrixFlag )
766 :
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)
785{
786}
787
789
790FormulaCompiler::FormulaCompiler(bool bComputeII, bool bMatrixFlag)
791 :
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)
810{
811}
812
814{
815}
816
818{
819 const bool bTemporary = !HasOpCodeMap(nLanguage);
820 OpCodeMapPtr xMap = GetFinalOpCodeMap(nLanguage);
821 if (bTemporary)
822 const_cast<FormulaCompiler*>(this)->DestroyOpCodeMap(nLanguage);
823 return xMap;
824}
825
827{
829 using namespace sheet;
830 switch (nLanguage)
831 {
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:
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
869 }
870 return xMap;
871}
872
873void FormulaCompiler::DestroyOpCodeMap( const sal_Int32 nLanguage )
874{
875 using namespace sheet;
876 switch (nLanguage)
877 {
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
901 }
902}
903
904bool FormulaCompiler::HasOpCodeMap( const sal_Int32 nLanguage ) const
905{
906 using namespace sheet;
907 switch (nLanguage)
908 {
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
925 }
926 return false;
927}
928
929OUString FormulaCompiler::FindAddInFunction( const OUString& /*rUpperName*/, bool /*bLocalFirst*/ ) const
930{
931 return OUString();
932}
933
935 const uno::Sequence<
936 const sheet::FormulaOpCodeMapEntry > & rMapping,
937 bool bEnglish )
938{
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,
944 std::unique_ptr<CharClass> xCharClass( xMap->isEnglish() ? nullptr : createCharClassIfNonEnglishUI());
945 const CharClass* pCharClass = xCharClass.get();
946 for (auto const& rMapEntry : rMapping)
947 {
948 OpCode eOp = OpCode(rMapEntry.Token.OpCode);
949 if (eOp != ocExternal)
950 xMap->putOpCode( rMapEntry.Name, eOp, pCharClass);
951 else
952 {
953 OUString aExternalName;
954 if (rMapEntry.Token.Data >>= aExternalName)
955 xMap->putExternal( rMapEntry.Name, aExternalName);
956 else
957 {
958 SAL_WARN( "formula.core", "FormulaCompiler::CreateOpCodeMap: no Token.Data external name");
959 }
960 }
961 }
962 return xMap;
963}
964
965static bool lcl_fillNativeSymbols( FormulaCompiler::NonConstOpCodeMapPtr& xMap, FormulaCompiler::InitSymbols eWhat = FormulaCompiler::InitSymbols::INIT )
966{
967 static OpCodeMapData aSymbolMap;
968 std::unique_lock aGuard(aSymbolMap.maMtx);
969
970 if (eWhat == FormulaCompiler::InitSymbols::ASK)
971 {
972 return bool(aSymbolMap.mxSymbolMap);
973 }
974 else if (eWhat == FormulaCompiler::InitSymbols::DESTROY)
975 {
976 aSymbolMap.mxSymbolMap.reset();
977 }
978 else if (!aSymbolMap.mxSymbolMap)
979 {
980 // Core
981 aSymbolMap.mxSymbolMap =
982 std::make_shared<FormulaCompiler::OpCodeMap>(
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.
987 }
988
989 xMap = aSymbolMap.mxSymbolMap;
990
991 return true;
992}
993
995{
996 NonConstOpCodeMapPtr xSymbolsNative;
997 lcl_fillNativeSymbols( xSymbolsNative);
998 return xSymbolsNative->getSymbol( eOp );
999}
1000
1002{
1003 return GetNativeSymbol(eOp)[0];
1004}
1005
1007{
1008 return lcl_fillNativeSymbols( mxSymbolsNative, eWhat);
1009}
1010
1012{
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;
1023}
1024
1026{
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;
1037}
1038
1040{
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;
1051}
1052
1054{
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;
1065}
1066
1068{
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;
1080
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);
1087
1088 return true;
1089}
1090
1092{
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;
1103}
1104
1105
1106void FormulaCompiler::loadSymbols(const std::pair<const char*, int>* pSymbols, FormulaGrammar::Grammar eGrammar,
1107 NonConstOpCodeMapPtr& rxMap, SeparatorType eSepType) const
1108{
1109 if ( rxMap )
1110 return;
1111
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);
1115
1116 fillFromAddInMap( rxMap, eGrammar);
1117 // Fill from collection for AddIns not already present.
1118 if (FormulaGrammar::GRAM_ENGLISH == eGrammar)
1120 else
1121 {
1123 if (FormulaGrammar::GRAM_API == eGrammar)
1124 {
1125 // Add known but not in AddInMap English names, e.g. from the
1126 // PricingFunctions AddIn or any user supplied AddIn.
1128 }
1129 }
1130}
1131
1133{
1134}
1135
1137{
1138}
1139
1141{
1142}
1143
1144OpCode FormulaCompiler::GetEnglishOpCode( const OUString& rName ) const
1145{
1146 FormulaCompiler::OpCodeMapPtr xMap = GetOpCodeMap( sheet::FormulaLanguage::ENGLISH);
1147
1148 formula::OpCodeHashMap::const_iterator iLook( xMap->getHashMap().find( rName ) );
1149 bool bFound = (iLook != xMap->getHashMap().end());
1150 return bFound ? (*iLook).second : ocNone;
1151}
1152
1154{
1155 bool bRet = false;
1156 switch (eOp)
1157 {
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;
1179 }
1180 return bRet;
1181}
1182
1184{
1185 switch (eOp)
1186 {
1187 case ocIf:
1188 case ocIfError:
1189 case ocIfNA:
1190 case ocChoose:
1191 return true;
1192 default:
1193 ;
1194 }
1195 return false;
1196}
1197
1198// Remove quotes, escaped quotes are unescaped.
1199bool FormulaCompiler::DeQuote( OUString& rStr )
1200{
1201 sal_Int32 nLen = rStr.getLength();
1202 if ( nLen > 1 && rStr[0] == '\'' && rStr[ nLen-1 ] == '\'' )
1203 {
1204 rStr = rStr.copy( 1, nLen-2 );
1205 rStr = rStr.replaceAll( "''", "'" );
1206 return true;
1207 }
1208 return false;
1209}
1210
1212 ::std::vector< sheet::FormulaOpCodeMapEntry >& /*_rVec*/,
1213 bool /*_bIsEnglish*/) const
1214{
1215}
1216
1218{
1219 switch (eOpCode)
1220 {
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:
1235 {
1236 // added to avoid warnings
1237 }
1238 }
1239 return false;
1240}
1241
1242
1243void FormulaCompiler::OpCodeMap::putCopyOpCode( const OUString& rSymbol, OpCode eOp )
1244{
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
1251 {
1252 mpTable[eOp] = rSymbol;
1253 maHashMap.emplace(rSymbol, eOp);
1254 }
1255}
1256
1258{
1259 maHashMap = OpCodeHashMap( mnSymbols);
1260
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;
1266
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] << "'");
1271
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)
1278 {
1279 for (sal_uInt16 i = 1; i < n; ++i)
1280 {
1281 OUString aSymbol;
1282 OpCode eOp = OpCode(i);
1283 switch (eOp)
1284 {
1285 case ocRRI:
1286 aSymbol = "RRI";
1287 break;
1288 case ocTableOp:
1289 aSymbol = "MULTIPLE.OPERATIONS";
1290 break;
1291 default:
1292 aSymbol = r.mpTable[i];
1293 }
1294 putCopyOpCode( aSymbol, eOp);
1295 }
1296 }
1297 else
1298 {
1299 for (sal_uInt16 i = 1; i < n; ++i)
1300 {
1301 OpCode eOp = OpCode(i);
1302 const OUString& rSymbol = r.mpTable[i];
1303 putCopyOpCode( rSymbol, eOp);
1304 }
1305 }
1306
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())
1311 {
1312 maExternalHashMap = r.maExternalHashMap;
1313 maReverseExternalHashMap = r.maReverseExternalHashMap;
1314 mbCore = r.mbCore;
1315 if (mbEnglish != r.mbEnglish)
1316 {
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.
1323 */
1324 mbEnglish = r.mbEnglish;
1325 }
1326 }
1327}
1328
1329
1331{
1333 OpCodeHashMap::const_iterator iLook( mxSymbols->getHashMap().find( rName));
1334 if (iLook != mxSymbols->getHashMap().end())
1335 {
1336 switch ((*iLook).second)
1337 {
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:
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:
1357 break;
1358 case ocErrNA:
1360 break;
1361 default:
1362 ; // nothing
1363 }
1364 }
1365 else
1366 {
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] == '!')
1371 {
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);
1375 }
1376 }
1377 return nError;
1378}
1379
1381{
1382 mbJumpCommandReorder = bEnable;
1383}
1384
1386{
1387 mbStopOnError = bEnable;
1388}
1389
1390void FormulaCompiler::AppendErrorConstant( OUStringBuffer& rBuffer, FormulaError nError ) const
1391{
1392 OpCode eOp;
1393 switch (nError)
1394 {
1396 eOp = ocErrNull;
1397 break;
1399 eOp = ocErrDivZero;
1400 break;
1402 eOp = ocErrValue;
1403 break;
1405 eOp = ocErrRef;
1406 break;
1408 eOp = ocErrName;
1409 break;
1411 eOp = ocErrNum;
1412 break;
1414 eOp = ocErrNA;
1415 break;
1416 default:
1417 {
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;
1424 }
1425 }
1426 rBuffer.append( mxSymbols->getSymbol( eOp));
1427}
1428
1429constexpr short nRecursionMax = 100;
1430
1432{
1433 FormulaCompilerRecursionGuard aRecursionGuard( nRecursion );
1434 if ( nRecursion > nRecursionMax )
1435 {
1438 return false;
1439 }
1440 if ( bAutoCorrect && !pStack )
1441 { // don't merge stacked subroutine code into entered formula
1443 aCorrectedSymbol.clear();
1444 }
1445 bool bStop = false;
1447 bStop = true;
1448 else
1449 {
1450 FormulaTokenRef pSpacesToken;
1451 short nWasColRowName;
1453 nWasColRowName = 1;
1454 else
1455 nWasColRowName = 0;
1456 OpCode eTmpOp;
1458 while (mpToken && ((eTmpOp = mpToken->GetOpCode()) == ocSpaces || eTmpOp == ocWhitespace))
1459 {
1460 if (eTmpOp == ocSpaces)
1461 {
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++;
1467 }
1468 if ( bAutoCorrect && !pStack )
1471 }
1472 if ( bAutoCorrect && !pStack && mpToken )
1474 if( !mpToken )
1475 {
1476 if( pStack )
1477 {
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();
1483 }
1484 else
1485 bStop = true;
1486 }
1487 else
1488 {
1489 if ( nWasColRowName >= 2 && mpToken->GetOpCode() == ocColRowName )
1490 { // convert an ocSpaces to ocIntersect in RPN
1492 maArrIterator.StepBack(); // we advanced to the second ocColRowName, step back
1493 }
1494 else if (pSpacesToken && FormulaGrammar::isExcelSyntax( meGrammar) &&
1495 mpLastToken && mpToken &&
1496 isPotentialRangeType( mpLastToken.get(), false, false) &&
1497 isPotentialRangeType( mpToken.get(), false, true))
1498 {
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;
1504 }
1505 }
1506 }
1507 if( bStop )
1508 {
1510 return false;
1511 }
1512
1513 // Remember token for next round and any PushTokenArray() calls that may
1514 // occur in handlers.
1516
1517 if ( mpToken->IsExternalRef() )
1518 {
1520 }
1521 else
1522 {
1523 switch (mpToken->GetOpCode())
1524 {
1525 case ocSubTotal:
1526 case ocAggregate:
1527 glSubTotal = true;
1528 break;
1529 case ocName:
1530 if( HandleRange())
1531 {
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;
1536 }
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
1550 }
1551 }
1552 return true;
1553}
1554
1555
1556// RPN creation by recursion
1558{
1560 return;
1561
1562 CurrentFactor pFacToken( this );
1563
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)))
1568 )
1569 {
1570 PutCode( mpToken );
1571 eOp = NextToken();
1572 if( eOp == ocOpen )
1573 {
1574 // PUSH( is an error that may be caused by an unknown function.
1575 SetError(
1576 ( mpToken->GetType() == svString
1577 || mpToken->GetType() == svSingleRef )
1579 if ( bAutoCorrect && !pStack )
1580 { // assume multiplication
1581 aCorrectedFormula += mxSymbols->getSymbol( ocMul);
1582 bCorrected = true;
1583 NextToken();
1584 eOp = Expression();
1585 if( eOp != ocClose )
1587 else
1588 NextToken();
1589 }
1590 }
1591 }
1592 else if( eOp == ocOpen )
1593 {
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();
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)
1605 {
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))
1610 {
1611 p = pCode[-1];
1612 if (p && isPotentialRangeType( p, true, true))
1613 {
1615 // XXX NOTE: the token's eType is still svSep here!
1616 PutCode( pFacToken);
1617 }
1618 }
1619 }
1620 }
1621 if (eOp != ocClose)
1623 else
1624 NextToken();
1625
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. */
1636 }
1637 else
1638 {
1639 if( nNumFmt == SvNumFormatType::UNDEFINED )
1640 nNumFmt = lcl_GetRetFormat( eOp );
1641
1642 if ( IsOpCodeVolatile( eOp) )
1644 else
1645 {
1646 switch( eOp )
1647 {
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:
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")
1665 else
1667 break;
1668 // If the referred cell is moved the value changes.
1669 case ocColumn :
1670 case ocRow :
1672 break;
1673 // ocCell needs recalc on move for some possible type values.
1674 // And recalc mode on load, tdf#60645
1675 case ocCell :
1678 break;
1679 case ocHyperLink :
1680 // Cell with hyperlink needs to be calculated on load to
1681 // get its matrix result generated.
1683 pArr->SetHyperLink( true);
1684 break;
1685 default:
1686 ; // nothing
1687 }
1688 }
1690 {
1691 pFacToken = mpToken;
1692 eOp = NextToken();
1693 if (eOp != ocOpen)
1694 {
1696 PutCode( pFacToken );
1697 }
1698 else
1699 {
1700 eOp = NextToken();
1701 if (eOp != ocClose)
1703 PutCode( pFacToken);
1704 NextToken();
1705 }
1706 }
1707 else if (SC_OPCODE_START_1_PAR <= eOp && eOp < SC_OPCODE_STOP_1_PAR)
1708 {
1710 {
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)
1717 {
1718 eOp = NextToken();
1719 if (eOp == ocClose)
1720 bNoParam = true;
1721 else
1722 {
1724 eOp = Expression();
1725 }
1726 }
1727 else
1729 sal_uInt32 nSepCount = 0;
1730 const sal_uInt16 nSepPos = maArrIterator.GetIndex() - 1; // separator position, if any
1731 if( !bNoParam )
1732 {
1733 nSepCount++;
1734 while ((eOp == ocSep) && (pArr->GetCodeError() == FormulaError::NONE || !mbStopOnError))
1735 {
1736 NextToken();
1738 nSepCount++;
1739 if (nSepCount > FORMULA_MAXPARAMS)
1741 eOp = Expression();
1742 }
1743 }
1744 if (eOp != ocClose)
1746 else
1747 NextToken();
1748 pFacToken->SetByte( nSepCount );
1749 if (nSepCount == 2)
1750 {
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)
1764 {
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 );
1771 }
1772 else
1773 {
1774 // For the remaining two arguments cases use the
1775 // compatibility function.
1777 }
1778 }
1779 PutCode( pFacToken );
1780 }
1781 else
1782 {
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)
1789 {
1790 NextToken();
1792 eOp = Expression();
1793 }
1794 else
1796 if (eOp != ocClose)
1798 else if ( pArr->GetCodeError() == FormulaError::NONE )
1799 {
1800 pFacToken->SetByte( 1 );
1801 if (mbComputeII)
1802 {
1803 FormulaToken** pArg = pCode - 1;
1804 HandleIIOpCode(pFacToken, &pArg, 1);
1805 }
1806 }
1807 PutCode( pFacToken );
1808 NextToken();
1809 }
1810 }
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 )
1819 {
1820 pFacToken = mpToken;
1821 OpCode eMyLastOp = eOp;
1822 eOp = NextToken();
1823 bool bNoParam = false;
1824 bool bBadName = false;
1825 if (eOp == ocOpen)
1826 {
1827 eOp = NextToken();
1828 if (eOp == ocClose)
1829 bNoParam = true;
1830 else
1831 {
1833 eOp = Expression();
1834 }
1835 }
1836 else if (eMyLastOp == ocBad)
1837 {
1838 // Just a bad name, not an unknown function, no parameters, no
1839 // closing expected.
1840 bBadName = true;
1841 bNoParam = true;
1842 }
1843 else
1845 sal_uInt32 nSepCount = 0;
1846 if( !bNoParam )
1847 {
1848 bool bDoIICompute = mbComputeII;
1849 // Array of FormulaToken double pointers to collect the parameters of II opcodes.
1850 FormulaToken*** pArgArray = nullptr;
1851 if (bDoIICompute)
1852 {
1853 pArgArray = static_cast<FormulaToken***>(alloca(sizeof(FormulaToken**)*FORMULA_MAXPARAMSII));
1854 if (!pArgArray)
1855 bDoIICompute = false;
1856 }
1857
1858 nSepCount++;
1859
1860 if (bDoIICompute)
1861 pArgArray[nSepCount-1] = pCode - 1; // Add first argument
1862
1863 while ((eOp == ocSep) && (pArr->GetCodeError() == FormulaError::NONE || !mbStopOnError))
1864 {
1865 NextToken();
1867 nSepCount++;
1868 if (nSepCount > FORMULA_MAXPARAMS)
1870 eOp = Expression();
1871 if (bDoIICompute && nSepCount <= FORMULA_MAXPARAMSII)
1872 pArgArray[nSepCount - 1] = pCode - 1; // Add rest of the arguments
1873 }
1874 if (bDoIICompute)
1875 HandleIIOpCode(pFacToken, pArgArray,
1876 std::min(nSepCount, static_cast<sal_uInt32>(FORMULA_MAXPARAMSII)));
1877 }
1878 bool bDone = false;
1879 if (bBadName)
1880 ; // nothing, keep current token for return
1881 else if (eOp != ocClose)
1883 else
1884 {
1885 NextToken();
1886 bDone = true;
1887 }
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 );
1894
1895 if (bDone)
1897 }
1898 else if (IsOpCodeJumpCommand(eOp))
1899 {
1900 // the PC counters are -1
1901 pFacToken = mpToken;
1902 switch (eOp)
1903 {
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");
1917 }
1918 eOp = NextToken();
1919 if (eOp == ocOpen)
1920 {
1921 NextToken();
1923 eOp = Expression();
1924 }
1925 else
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)
1935 {
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;
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");
1955 }
1956 short nJumpCount = 0;
1957 while ( (nJumpCount < (FORMULA_MAXJUMPCOUNT - 1)) && (eOp == ocSep)
1959 {
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 );
1967 }
1968 if (eOp != ocClose)
1970 else
1971 {
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)
1979 {
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");
2002 }
2003 if (bLimitOk)
2004 pFacToken->GetJump()[ 0 ] = nJumpCount;
2005 else
2007 }
2008 }
2009 else if ( eOp == ocMissing )
2010 {
2011 PutCode( mpToken );
2012 NextToken();
2013 }
2014 else if ( eOp == ocClose )
2015 {
2017 }
2018 else if ( eOp == ocSep )
2019 { // Subsequent ocSep
2021 if ( bAutoCorrect && !pStack )
2022 {
2023 aCorrectedSymbol.clear();
2024 bCorrected = true;
2025 }
2026 }
2027 else if ( mpToken->IsExternalRef() )
2028 {
2029 PutCode( mpToken);
2030 NextToken();
2031 }
2032 else
2033 {
2035 if ( bAutoCorrect && !pStack )
2036 {
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;
2044 }
2045 }
2046 }
2047 }
2048}
2049
2051{
2052 Factor();
2053 while (mpToken->GetOpCode() == ocRange)
2054 {
2055 FormulaToken** pCode1 = pCode - 1;
2057 NextToken();
2058 Factor();
2059 FormulaToken** pCode2 = pCode - 1;
2060 if (!MergeRangeReference( pCode1, pCode2))
2061 PutCode(p);
2062 }
2063}
2064
2066{
2067 RangeLine();
2068 while (mpToken->GetOpCode() == ocIntersect || mpToken->GetOpCode() == ocSpaces)
2069 {
2070 sal_uInt16 nCodeIndex = maArrIterator.GetIndex() - 1;
2071 FormulaToken** pCode1 = pCode - 1;
2073 NextToken();
2074 RangeLine();
2075 FormulaToken** pCode2 = pCode - 1;
2076 if (p->GetOpCode() == ocSpaces)
2077 {
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))
2083 {
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);
2090 }
2091 }
2092 else
2093 {
2094 PutCode(p);
2095 }
2096 }
2097}
2098
2100{
2102 while (mpToken->GetOpCode() == ocUnion)
2103 {
2105 NextToken();
2107 PutCode(p);
2108 }
2109}
2110
2112{
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)
2117 {
2119 NextToken();
2120 UnaryLine();
2121 if (mbComputeII)
2122 {
2123 FormulaToken** pArg = pCode - 1;
2124 HandleIIOpCode(p.get(), &pArg, 1);
2125 }
2126 PutCode( p );
2127 }
2128 else
2129 UnionLine();
2130}
2131
2133{
2134 UnaryLine();
2135 while ( mpToken->GetOpCode() == ocPercentSign )
2136 { // this operator _follows_ its operand
2137 if (mbComputeII)
2138 {
2139 FormulaToken** pArg = pCode - 1;
2140 HandleIIOpCode(mpToken.get(), &pArg, 1);
2141 }
2142 PutCode( mpToken );
2143 NextToken();
2144 }
2145}
2146
2148{
2149 PostOpLine();
2150 while (mpToken->GetOpCode() == ocPow)
2151 {
2153 FormulaToken** pArgArray[2];
2154 if (mbComputeII)
2155 pArgArray[0] = pCode - 1; // Add first argument
2156 NextToken();
2157 PostOpLine();
2158 if (mbComputeII)
2159 {
2160 pArgArray[1] = pCode - 1; // Add second argument
2161 HandleIIOpCode(p.get(), pArgArray, 2);
2162 }
2163 PutCode(p);
2164 }
2165}
2166
2168{
2169 PowLine();
2170 while (mpToken->GetOpCode() == ocMul || mpToken->GetOpCode() == ocDiv)
2171 {
2173 FormulaToken** pArgArray[2];
2174 if (mbComputeII)
2175 pArgArray[0] = pCode - 1; // Add first argument
2176 NextToken();
2177 PowLine();
2178 if (mbComputeII)
2179 {
2180 pArgArray[1] = pCode - 1; // Add second argument
2181 HandleIIOpCode(p.get(), pArgArray, 2);
2182 }
2183 PutCode(p);
2184 }
2185}
2186
2188{
2189 MulDivLine();
2190 while (mpToken->GetOpCode() == ocAdd || mpToken->GetOpCode() == ocSub)
2191 {
2193 FormulaToken** pArgArray[2];
2194 if (mbComputeII)
2195 pArgArray[0] = pCode - 1; // Add first argument
2196 NextToken();
2197 MulDivLine();
2198 if (mbComputeII)
2199 {
2200 pArgArray[1] = pCode - 1; // Add second argument
2201 HandleIIOpCode(p.get(), pArgArray, 2);
2202 }
2203 PutCode(p);
2204 }
2205}
2206
2208{
2209 AddSubLine();
2210 while (mpToken->GetOpCode() == ocAmpersand)
2211 {
2213 FormulaToken** pArgArray[2];
2214 if (mbComputeII)
2215 pArgArray[0] = pCode - 1; // Add first argument
2216 NextToken();
2217 AddSubLine();
2218 if (mbComputeII)
2219 {
2220 pArgArray[1] = pCode - 1; // Add second argument
2221 HandleIIOpCode(p.get(), pArgArray, 2);
2222 }
2223 PutCode(p);
2224 }
2225}
2226
2228{
2229 ConcatLine();
2230 while (mpToken->GetOpCode() >= ocEqual && mpToken->GetOpCode() <= ocGreaterEqual)
2231 {
2233 FormulaToken** pArgArray[2];
2234 if (mbComputeII)
2235 pArgArray[0] = pCode - 1; // Add first argument
2236 NextToken();
2237 ConcatLine();
2238 if (mbComputeII)
2239 {
2240 pArgArray[1] = pCode - 1; // Add second argument
2241 HandleIIOpCode(p.get(), pArgArray, 2);
2242 }
2243 PutCode(p);
2244 }
2245}
2246
2248{
2249 FormulaCompilerRecursionGuard aRecursionGuard( nRecursion );
2250 if ( nRecursion > nRecursionMax )
2251 {
2253 return ocStop;
2254 }
2255 CompareLine();
2256 while (mpToken->GetOpCode() == ocAnd || mpToken->GetOpCode() == ocOr)
2257 {
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)
2266 {
2267 pArgArray[1] = pCode - 1; // Add second argument
2268 HandleIIOpCode(p.get(), pArgArray, 2);
2269 }
2270 PutCode(p);
2271 }
2272 return mpToken->GetOpCode();
2273}
2274
2275
2277{
2278}
2279
2281{
2282 return FormulaTokenRef();
2283}
2284
2285bool FormulaCompiler::MergeRangeReference( FormulaToken * * const pCode1, FormulaToken * const * const pCode2 )
2286{
2287 if (!isAdjacentRpnEnd( pc, pCode, pCode1, pCode2))
2288 return false;
2289
2290 FormulaToken *p1 = *pCode1, *p2 = *pCode2;
2292 if (!p)
2293 return false;
2294
2295 p->IncRef();
2296 p1->DecRef();
2297 p2->DecRef();
2298 *pCode1 = p.get();
2299 --pCode;
2300 --pc;
2301
2302 return true;
2303}
2304
2306{
2307 glSubTotal = false;
2308 bCorrected = false;
2309 needsRPNTokenCheck = false;
2311 {
2312 if ( bAutoCorrect )
2313 {
2314 aCorrectedFormula.clear();
2315 aCorrectedSymbol.clear();
2316 }
2317 pArr->DelRPN();
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 = "=";
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)
2340
2341 FormulaError nErrorBeforePop = pArr->GetCodeError();
2342
2343 while( pStack )
2344 PopTokenArray();
2345 if( pc )
2346 {
2348 if( needsRPNTokenCheck )
2350 }
2351
2352 // once an error, always an error
2353 if( pArr->GetCodeError() == FormulaError::NONE && nErrorBeforePop != FormulaError::NONE )
2354 pArr->SetCodeError( nErrorBeforePop);
2355
2357 {
2358 pArr->DelRPN();
2360 pArr->SetHyperLink( false);
2361 }
2362
2363 if ( bWasForced )
2365 }
2366 if( nNumFmt == SvNumFormatType::UNDEFINED )
2367 nNumFmt = SvNumFormatType::NUMBER;
2368 return glSubTotal;
2369}
2370
2372{
2373 if( !pStack )
2374 return;
2375
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;
2390 maArrIterator.Jump(p->nIndex);
2391 mpLastToken = p->mpLastToken;
2392 delete p;
2393}
2394
2396{
2397 OUStringBuffer aBuffer( pArr->GetLen() * 5 );
2399 rFormula = aBuffer.makeStringAndClear();
2400}
2401
2402void FormulaCompiler::CreateStringFromTokenArray( OUStringBuffer& rBuffer )
2403{
2404 rBuffer.setLength(0);
2405 if( !pArr->GetLen() )
2406 return;
2407
2408 FormulaTokenArray* pSaveArr = pArr;
2409 int nSaveIndex = maArrIterator.GetIndex();
2410 bool bODFF = FormulaGrammar::isODFF( meGrammar);
2411 if (bODFF || FormulaGrammar::isPODF( meGrammar) )
2412 {
2413 // Scan token array for missing args and re-write if present.
2414 MissingConventionODF aConv( bODFF);
2415 if (pArr->NeedsPodfRewrite( aConv))
2416 {
2417 pArr = pArr->RewriteMissing( aConv );
2419 }
2420 }
2421 else if ( FormulaGrammar::isOOXML( meGrammar ) )
2422 {
2423 // Scan token array for missing args and rewrite if present.
2424 if (pArr->NeedsOoxmlRewrite())
2425 {
2427 pArr = pArr->RewriteMissing( aConv );
2429 }
2430 }
2431
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 );
2435
2436 if ( pArr->IsRecalcModeForced() )
2437 rBuffer.append( '=');
2438 const FormulaToken* t = maArrIterator.First();
2439 while( t )
2440 t = CreateStringFromToken( rBuffer, t, true );
2441
2442 if (pSaveArr != pArr)
2443 {
2444 delete pArr;
2445 pArr = pSaveArr;
2447 maArrIterator.Jump(nSaveIndex);
2448 }
2449}
2450
2451const FormulaToken* FormulaCompiler::CreateStringFromToken( OUString& rFormula, const FormulaToken* pTokenP )
2452{
2453 OUStringBuffer aBuffer;
2454 const FormulaToken* p = CreateStringFromToken( aBuffer, pTokenP );
2455 rFormula += aBuffer;
2456 return p;
2457}
2458
2459const FormulaToken* FormulaCompiler::CreateStringFromToken( OUStringBuffer& rBuffer, const FormulaToken* pTokenP,
2460 bool bAllowArrAdvance )
2461{
2462 bool bNext = true;
2463 bool bSpaces = false;
2464 const FormulaToken* t = pTokenP;
2465 OpCode eOp = t->GetOpCode();
2466 if( eOp >= ocAnd && eOp <= ocOr )
2467 {
2468 // AND, OR infix?
2469 if ( bAllowArrAdvance )
2470 t = maArrIterator.Next();
2471 else
2473 bNext = false;
2474 bSpaces = ( !t || t->GetOpCode() != ocOpen );
2475 }
2476 if( bSpaces )
2477 rBuffer.append( ' ');
2478
2479 if (eOp == ocSpaces || eOp == ocWhitespace)
2480 {
2481 bool bWriteSpaces = true;
2482 if (eOp == ocSpaces && mxSymbols->isODFF())
2483 {
2485 bool bIntersectionOp = (p && p->GetOpCode() == ocColRowName);
2486 if (bIntersectionOp)
2487 {
2489 bIntersectionOp = (p && p->GetOpCode() == ocColRowName);
2490 }
2491 if (bIntersectionOp)
2492 {
2493 rBuffer.append( "!!");
2494 bWriteSpaces = false;
2495 }
2496 }
2497 if (bWriteSpaces)
2498 {
2499 // ODF v1.3 OpenFormula 5.14 Whitespace states "whitespace shall
2500 // not separate a function name from its initial opening
2501 // parenthesis".
2502 //
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.
2506 //
2507 // Suppress/remove it in any case also in UI, it will not be
2508 // preserved.
2510 if (p && p->IsFunction())
2511 {
2513 if (p && p->GetOpCode() == ocOpen)
2514 bWriteSpaces = false;
2515 }
2516 }
2517 if (bWriteSpaces)
2518 {
2519 // most times it's just one blank
2520 sal_uInt8 n = t->GetByte();
2521 for ( sal_uInt8 j=0; j<n; ++j )
2522 {
2523 if (eOp == ocWhitespace)
2524 rBuffer.append( t->GetChar());
2525 else
2526 rBuffer.append( ' ');
2527 }
2528 }
2529 }
2530 else if( eOp >= ocInternalBegin && eOp <= ocInternalEnd )
2531 rBuffer.appendAscii( pInternal[ eOp - ocInternalBegin ] );
2532 else if (eOp == ocIntersect)
2533 {
2534 // Nasty, ugly, horrific, terrifying...
2536 rBuffer.append(' ');
2537 else
2538 rBuffer.append( mxSymbols->getSymbol( eOp));
2539 }
2540 else if( static_cast<sal_uInt16>(eOp) < mxSymbols->getSymbolCount()) // Keyword:
2541 rBuffer.append( mxSymbols->getSymbol( eOp));
2542 else
2543 {
2544 SAL_WARN( "formula.core","unknown OpCode");
2545 rBuffer.append( GetNativeSymbol( ocErrName ));
2546 }
2547 if( bNext )
2548 {
2549 if (t->IsExternalRef())
2550 {
2551 CreateStringFromExternal( rBuffer, pTokenP);
2552 }
2553 else
2554 {
2555 switch( t->GetType() )
2556 {
2557 case svDouble:
2558 AppendDouble( rBuffer, t->GetDouble() );
2559 break;
2560
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;
2577
2578 case svIndex:
2579 CreateStringFromIndex( rBuffer, t );
2580 if (t->GetOpCode() == ocTableRef && bAllowArrAdvance && NeedsTableRefTransformation())
2581 {
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)
2586 {
2587 int nLevel = 0;
2588 do
2589 {
2590 t = maArrIterator.Next();
2591 if (!t)
2592 break;
2593
2594 // Switch cases correspond with those in
2595 // ScCompiler::HandleTableRef()
2596 switch (t->GetOpCode())
2597 {
2598 case ocTableRefOpen:
2599 ++nLevel;
2600 break;
2601 case ocTableRefClose:
2602 --nLevel;
2603 break;
2604 case ocTableRefItemAll:
2606 case ocTableRefItemData:
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;
2618 }
2619 } while (nLevel);
2620 }
2621 }
2622 break;
2623 case svExternal:
2624 {
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())
2629 {
2630 ExternalHashMap::const_iterator iLook = mxSymbols->getReverseExternalHashMap().find( aAddIn);
2631 if (iLook != mxSymbols->getReverseExternalHashMap().end())
2632 {
2633 aAddIn = (*iLook).second;
2634 bMapped = true;
2635 }
2636 }
2637 if (!bMapped && !mxSymbols->isEnglish())
2638 LocalizeString( aAddIn );
2639 rBuffer.append( aAddIn);
2640 }
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
2654 }
2655 }
2656 if( bSpaces )
2657 rBuffer.append( ' ');
2658 if ( bAllowArrAdvance )
2659 {
2660 if( bNext )
2661 t = maArrIterator.Next();
2662 return t;
2663 }
2664 return pTokenP;
2665}
2666
2667
2668void FormulaCompiler::AppendDouble( OUStringBuffer& rBuffer, double fVal ) const
2669{
2670 if ( mxSymbols->isEnglishLocale() )
2671 {
2672 ::rtl::math::doubleToUStringBuffer( rBuffer, fVal,
2673 rtl_math_StringFormat_Automatic,
2674 rtl_math_DecimalPlaces_Max, '.', true );
2675 }
2676 else
2677 {
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 );
2684 }
2685}
2686
2687void FormulaCompiler::AppendBoolean( OUStringBuffer& rBuffer, bool bVal ) const
2688{
2689 rBuffer.append( mxSymbols->getSymbol( bVal ? ocTrue : ocFalse ) );
2690}
2691
2692void FormulaCompiler::AppendString( OUStringBuffer& rBuffer, const OUString & rStr )
2693{
2694 rBuffer.append( '"');
2695 if ( lcl_UnicodeStrChr( rStr.getStr(), '"' ) == nullptr )
2696 rBuffer.append( rStr );
2697 else
2698 {
2699 OUString aStr = rStr.replaceAll( "\"", "\"\"" );
2700 rBuffer.append(aStr);
2701 }
2702 rBuffer.append( '"');
2703}
2704
2706{
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);
2712}
2713
2715 const OUString& rSep, const OUString& rArrayColSep, const OUString& rArrayRowSep )
2716{
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);
2722}
2723
2725{
2726 NonConstOpCodeMapPtr xSymbolsNative;
2727 lcl_fillNativeSymbols( xSymbolsNative, InitSymbols::DESTROY);
2728 lcl_fillNativeSymbols( xSymbolsNative);
2729}
2730
2732{
2733 NonConstOpCodeMapPtr xSymbolsNative;
2734 lcl_fillNativeSymbols( xSymbolsNative);
2735 xSymbolsNative->copyFrom( *xMap );
2736}
2737
2738
2740{
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) ||
2749 // Operator and Plus => operator
2750 if (eOp == ocAdd && (eLastOp == ocOpen || eLastOp == ocSep ||
2752 {
2753 FormulaCompilerRecursionGuard aRecursionGuard( nRecursion );
2754 eOp = NextToken();
2755 }
2756 else
2757 {
2758 // Before an operator there must not be another operator, with the
2759 // exception of AND and OR.
2760 if ( eOp != ocAnd && eOp != ocOr &&
2762 && (eLastOp == ocOpen || eLastOp == ocSep ||
2764 {
2766 if ( bAutoCorrect && !pStack )
2767 {
2768 if ( eOp == eLastOp || eLastOp == ocOpen )
2769 { // throw away duplicated operator
2770 aCorrectedSymbol.clear();
2771 bCorrected = true;
2772 }
2773 else
2774 {
2775 sal_Int32 nPos = aCorrectedFormula.getLength();
2776 if ( nPos )
2777 {
2778 nPos--;
2780 switch ( eOp )
2781 { // swap operators
2782 case ocGreater:
2783 if ( c == mxSymbols->getSymbolChar( ocEqual) )
2784 { // >= instead of =>
2786 rtl::OUStringChar( mxSymbols->getSymbolChar(ocGreater) ) );
2787 aCorrectedSymbol = OUString(c);
2788 bCorrected = true;
2789 }
2790 break;
2791 case ocLess:
2792 if ( c == mxSymbols->getSymbolChar( ocEqual) )
2793 { // <= instead of =<
2795 rtl::OUStringChar( mxSymbols->getSymbolChar(ocLess) ) );
2796 aCorrectedSymbol = OUString(c);
2797 bCorrected = true;
2798 }
2799 else if ( c == mxSymbols->getSymbolChar( ocGreater) )
2800 { // <> instead of ><
2802 rtl::OUStringChar( mxSymbols->getSymbolChar(ocLess) ) );
2803 aCorrectedSymbol = OUString(c);
2804 bCorrected = true;
2805 }
2806 break;
2807 case ocMul:
2808 if ( c == mxSymbols->getSymbolChar( ocSub) )
2809 { // *- instead of -*
2811 rtl::OUStringChar( mxSymbols->getSymbolChar(ocMul) ) );
2812 aCorrectedSymbol = OUString(c);
2813 bCorrected = true;
2814 }
2815 break;
2816 case ocDiv:
2817 if ( c == mxSymbols->getSymbolChar( ocSub) )
2818 { // /- instead of -/
2820 rtl::OUStringChar( mxSymbols->getSymbolChar(ocDiv) ) );
2821 aCorrectedSymbol = OUString(c);
2822 bCorrected = true;
2823 }
2824 break;
2825 default:
2826 ; // nothing
2827 }
2828 }
2829 }
2830 }
2831 }
2832 // Nasty, ugly, horrific, terrifying... significant whitespace...
2834 {
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.
2838 if (pPrev && isPotentialRangeType( pPrev, false, false))
2839 {
2841 if (pNext && isPotentialRangeType( pNext, false, true))
2843 else
2844 eLastOp = eOp;
2845 }
2846 else
2847 eLastOp = eOp;
2848 }
2849 else
2850 eLastOp = eOp;
2851 }
2852 return eOp;
2853}
2854
2856{
2857 if( pc >= FORMULA_MAXTOKENS - 1 )
2858 {
2859 if ( pc == FORMULA_MAXTOKENS - 1 )
2860 {
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;
2866 }
2868 return;
2869 }
2871 return;
2873 p->IncRef();
2874 *pCode++ = p.get();
2875 pc++;
2876}
2877
2878
2880{
2881 return true;
2882}
2883
2885{
2886 return true;
2887}
2888
2890{
2891 return true;
2892}
2893
2895{
2896 return true;
2897}
2898
2900{
2901 return true;
2902}
2903
2904void FormulaCompiler::CreateStringFromSingleRef( OUStringBuffer& /*rBuffer*/, const FormulaToken* /*pToken*/) const
2905{
2906}
2907
2908void FormulaCompiler::CreateStringFromDoubleRef( OUStringBuffer& /*rBuffer*/, const FormulaToken* /*pToken*/) const
2909{
2910}
2911
2912void FormulaCompiler::CreateStringFromIndex( OUStringBuffer& /*rBuffer*/, const FormulaToken* /*pToken*/) const
2913{
2914}
2915
2916void FormulaCompiler::CreateStringFromMatrix( OUStringBuffer& /*rBuffer*/, const FormulaToken* /*pToken*/) const
2917{
2918}
2919
2920void FormulaCompiler::CreateStringFromExternal( OUStringBuffer& /*rBuffer*/, const FormulaToken* /*pToken*/) const
2921{
2922}
2923
2924void FormulaCompiler::LocalizeString( OUString& /*rName*/ ) const
2925{
2926}
2927
2928formula::ParamClass FormulaCompiler::GetForceArrayParameter( const FormulaToken* /*pToken*/, sal_uInt16 /*nParam*/ ) const
2929{
2930 return ParamClass::Unknown;
2931}
2932
2934{
2935 if (pCurrentFactorToken.get() == rCurr.get())
2936 return;
2937
2938 const OpCode eOp = rCurr->GetOpCode();
2939 const StackVar eType = rCurr->GetType();
2940 const bool bInlineArray = (eOp == ocPush && eType == svMatrix);
2941
2942 if (!bInlineArray)
2943 {
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;
2950
2951 if (!(eOp != ocPush && (eType == svByte || eType == svJump)))
2952 return;
2953 }
2954
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;
2970
2971 if (bInlineArray)
2972 {
2973 // rCurr->SetInForceArray() can not be used with ocPush, but ocPush
2974 // with svMatrix has an implicit ParamClass::ForceArrayReturn.
2976 && pCurrentFactorToken->GetInForceArray() == ParamClass::Unknown
2977 && GetForceArrayParameter( pCurrentFactorToken.get(), static_cast<sal_uInt16>(nCurrentFactorParam - 1))
2979 {
2980 // Propagate to caller as if a function returning an array/matrix
2981 // was called (see also below).
2982 pCurrentFactorToken->SetInForceArray( eArrayReturn);
2983 }
2984 return;
2985 }
2986
2988 {
2989 if (mbMatrixFlag)
2990 {
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);
2995 }
2996 else if (pc >= 2 && SC_OPCODE_START_BIN_OP <= eOp && eOp < SC_OPCODE_STOP_BIN_OP)
2997 {
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.
3011
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))
3019 {
3020 rCurr->SetInForceArray( eArrayReturn);
3021 }
3022 }
3023 else if (pc >= 1 && SC_OPCODE_START_UN_OP <= eOp && eOp < SC_OPCODE_STOP_UN_OP)
3024 {
3025 // Similar for unary operators.
3026 if (pCode[-1]->GetInForceArray() != ParamClass::Unknown || IsMatrixFunction(pCode[-1]->GetOpCode()))
3027 {
3028 rCurr->SetInForceArray( eArrayReturn);
3029 }
3030 }
3031 return;
3032 }
3033
3034 // Inherited parameter class.
3035 const formula::ParamClass eForceType = pCurrentFactorToken->GetInForceArray();
3036 if (eForceType == ParamClass::ForceArray || eForceType == ParamClass::ReferenceOrRefArray)
3037 {
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))
3045 rCurr->SetInForceArray( ParamClass::ReferenceOrRefArray);
3046 else
3047 rCurr->SetInForceArray( ParamClass::ForceArray);
3048 return;
3049 }
3050 else if (eForceType == ParamClass::ReferenceOrForceArray)
3051 {
3052 // Inherit further only if the return class of the nested function is
3053 // not Reference. Else flag as suppressed.
3055 rCurr->SetInForceArray( eForceType);
3056 else
3057 rCurr->SetInForceArray( ParamClass::SuppressedReferenceOrForceArray);
3058 return;
3059 }
3060
3061 if (nCurrentFactorParam <= 0)
3062 return;
3063
3064 // Actual current parameter's class.
3066 pCurrentFactorToken.get(), static_cast<sal_uInt16>(nCurrentFactorParam - 1));
3067 if (eParamType == ParamClass::ForceArray)
3068 rCurr->SetInForceArray( eParamType);
3069 else if (eParamType == ParamClass::ReferenceOrForceArray)
3070 {
3072 rCurr->SetInForceArray( eParamType);
3073 else
3075 }
3076
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)
3081 {
3082 if (IsMatrixFunction( eOp))
3083 pCurrentFactorToken->SetInForceArray( eArrayReturn);
3086 }
3087}
3088
3090{
3092 return;
3093
3094 nCurrentFactorParam = nParam + 1;
3095
3096 ForceArrayOperator( rCurr);
3097}
3098
3100{
3101 if ( bAutoCorrect && !pStack )
3102 { // don't merge stacked subroutine code into entered formula
3104 aCorrectedSymbol.clear();
3105 }
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;
3115}
3116
3117} // namespace formula
3118
3119/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
short & rRecursion
const std::pair< TranslateId, int > * mpSymbols2
FormulaCompiler::NonConstOpCodeMapPtr mxSymbolMap
const std::pair< const char *, int > * mpSymbols1
FormulaCompiler::SeparatorType meSepType
std::mutex maMtx
#define FORMULA_MAXPARAMS
#define FORMULA_MAXPARAMSII
#define FORMULA_MAXJUMPCOUNT
#define FORMULA_MAXTOKENS
XPropertyListType t
const char * pName
static const AllSettings & GetSettings()
OUString uppercase(const OUString &rStr, sal_Int32 nPos, sal_Int32 nCount) const
const OUString & getNumDecimalSep() const
const LocaleDataWrapper & GetLocaleData() const
Mappings from strings to OpCodes and vice versa.
FormulaGrammar::Grammar meGrammar
Hash map of ocExternal, AddIn String -> Filter String.
sal_uInt16 getSymbolCount() const
Get the symbol count.
std::unique_ptr< OUString[]> mpTable
Hash map of symbols, OUString -> OpCode.
void putCopyOpCode(const OUString &rSymbol, OpCode eOp)
Conditionally put a mapping in copyFrom() context.
void copyFrom(const OpCodeMap &r)
Copy mappings from r into this map, effectively replacing this map.
ExternalHashMap maExternalHashMap
Array of symbols, OpCode -> OUString, offset==OpCode.
bool mbCore
Count of OpCode symbols.
ExternalHashMap maReverseExternalHashMap
Hash map of ocExternal, Filter String -> AddIn String.
bool mbEnglish
If mapping was setup by core, not filters.
FormulaTokenRef pCurrentFactorToken
bool InitSymbolsEnglishXL(InitSymbols) const
only SymbolsODFF, on demand
virtual formula::ParamClass GetForceArrayParameter(const FormulaToken *pToken, sal_uInt16 nParam) const
If a parameter nParam (0-based) is to be forced to array for OpCode eOp, i.e.
void EnableStopOnError(bool bEnable)
NonConstOpCodeMapPtr mxSymbolsOOXML
NonConstOpCodeMapPtr mxSymbolsPODF
virtual void CreateStringFromMatrix(OUStringBuffer &rBuffer, const FormulaToken *pToken) const
OpCodeMapPtr GetFinalOpCodeMap(const sal_Int32 nLanguage) const
Get finalized OpCodeMap for formula language.
OpCode GetEnglishOpCode(const OUString &rName) const
Get OpCode for English symbol.
FormulaTokenArray * pArr
virtual void fillAddInToken(::std::vector< css::sheet::FormulaOpCodeMapEntry > &_rVec, bool _bIsEnglish) const
FormulaError GetErrorConstant(const OUString &rName) const
virtual void SetError(FormulaError nError)
bool NeedsTableRefTransformation() const
Whether current symbol set and grammar need transformation of Table structured references to A1 style...
void EnableJumpCommandReorder(bool bEnable)
virtual FormulaTokenRef ExtendRangeReference(FormulaToken &rTok1, FormulaToken &rTok2)
bool MergeRangeReference(FormulaToken **const pCode1, FormulaToken *const *const pCode2)
NonConstOpCodeMapPtr mxSymbolsEnglishXL
static bool IsMatrixFunction(OpCode _eOpCode)
void PushTokenArray(FormulaTokenArray *, bool)
FormulaArrayStack * pStack
FormulaCompiler(const FormulaCompiler &)=delete
void loadSymbols(const std::pair< const char *, int > *pSymbols, FormulaGrammar::Grammar eGrammar, NonConstOpCodeMapPtr &rxMap, SeparatorType eSepType=SeparatorType::SEMICOLON_BASE) const
only SymbolsOOXML, on demand
static FormulaTokenArray smDummyTokenArray
static bool DeQuote(OUString &rStr)
static void AppendString(OUStringBuffer &rBuffer, const OUString &rStr)
static bool IsOpCodeVolatile(OpCode eOp)
void PutCode(FormulaTokenRef &)
bool InitSymbolsODFF(InitSymbols) const
only SymbolsAPI, on demand
bool InitSymbolsOOXML(InitSymbols) const
only SymbolsEnglishXL, on demand
void AppendErrorConstant(OUStringBuffer &rBuffer, FormulaError nError) const
static bool IsOpCodeJumpCommand(OpCode eOp)
NonConstOpCodeMapPtr mxSymbolsAPI
virtual void fillFromAddInMap(const NonConstOpCodeMapPtr &xMap, FormulaGrammar::Grammar _eGrammar) const
virtual bool HandleExternalReference(const FormulaToken &_aToken)
void CheckSetForceArrayParameter(FormulaTokenRef const &rCurr, sal_uInt8 nParam)
Check pCurrentFactorToken for nParam's (0-based) ForceArray types and set ForceArray at rCurr if so.
bool HasOpCodeMap(const sal_Int32 nLanguage) const
Whether the singleton OpCodeMap for formula language exists already.
void AppendBoolean(OUStringBuffer &rBuffer, bool bVal) const
bool mbComputeII
Whether to stop compilation on first encountered error.
bool InitSymbolsPODF(InitSymbols) const
only SymbolsEnglish, maybe later
void ForceArrayOperator(FormulaTokenRef const &rCurr)
bool InitSymbolsAPI(InitSymbols) const
only SymbolsPODF, on demand
static const OUString & GetNativeSymbol(OpCode eOp)
static void SetNativeSymbols(const OpCodeMapPtr &xMap)
static sal_Unicode GetNativeSymbolChar(OpCode eOp)
virtual void CreateStringFromExternal(OUStringBuffer &rBuffer, const FormulaToken *pToken) const
virtual void fillFromAddInCollectionUpperName(const NonConstOpCodeMapPtr &xMap) const
virtual void CreateStringFromSingleRef(OUStringBuffer &rBuffer, const FormulaToken *pToken) const
bool mbStopOnError
Whether or not to reorder RPN for jump commands.
SeparatorType
Separators mapped when loading opcodes from the resource, values other than RESOURCE_BASE may overrid...
static OpCodeMapPtr CreateOpCodeMap(const css::uno::Sequence< const css::sheet::FormulaOpCodeMapEntry > &rMapping, bool bEnglish)
Create an internal symbol map from API mapping.
void AppendDouble(OUStringBuffer &rBuffer, double fVal) const
FormulaTokenArrayPlainIterator maArrIterator
virtual void LocalizeString(OUString &rName) const
void DestroyOpCodeMap(const sal_Int32 nLanguage)
Destroy the singleton OpCodeMap for formula language.
std::shared_ptr< OpCodeMap > NonConstOpCodeMapPtr
virtual void fillFromAddInCollectionEnglishName(const NonConstOpCodeMapPtr &xMap) const
static void UpdateSeparatorsNative(const OUString &rSep, const OUString &rArrayColSep, const OUString &rArrayRowSep)
virtual void CreateStringFromDoubleRef(OUStringBuffer &rBuffer, const FormulaToken *pToken) const
void CreateStringFromTokenArray(OUString &rFormula)
FormulaGrammar::Grammar meGrammar
bool InitSymbolsNative(InitSymbols) const
NonConstOpCodeMapPtr mxSymbolsEnglish
bool InitSymbolsEnglish(InitSymbols) const
only SymbolsNative, on first document creation
std::shared_ptr< const OpCodeMap > OpCodeMapPtr
const FormulaToken * CreateStringFromToken(OUString &rFormula, const FormulaToken *pToken)
virtual void HandleIIOpCode(FormulaToken *, FormulaToken ***, sal_uInt8)
OpCodeMapPtr GetOpCodeMap(const sal_Int32 nLanguage) const
Get OpCodeMap for formula language.
virtual OUString FindAddInFunction(const OUString &rUpperName, bool bLocalFirst) const
NonConstOpCodeMapPtr mxSymbolsNative
NonConstOpCodeMapPtr mxSymbolsODFF
virtual void CreateStringFromIndex(OUStringBuffer &rBuffer, const FormulaToken *pToken) const
Grammars digested by ScCompiler.
Definition: grammar.hxx:33
static bool isODFF(const Grammar eGrammar)
If grammar is of ODFF.
Definition: grammar.hxx:199
static sal_Int32 extractFormulaLanguage(const Grammar eGrammar)
Definition: grammar.hxx:175
static Grammar mergeToGrammar(const Grammar eGrammar, const AddressConvention eConv)
Definition: grammar.cxx:74
static bool isPODF(const Grammar eGrammar)
If grammar is of ODF 1.1.
Definition: grammar.hxx:192
static bool isExcelSyntax(const Grammar eGrammar)
If grammar has an Excel syntax, determined by address convention.
Definition: grammar.hxx:226
Grammar
Values encoding the formula language plus address reference convention plus English parsing/formattin...
Definition: grammar.hxx:68
@ GRAM_API
API English with A1 reference style, unbracketed.
Definition: grammar.hxx:147
@ GRAM_ENGLISH
English with default A1 reference style.
Definition: grammar.hxx:82
@ GRAM_NATIVE_UI
Native with reference style as set in UI, may be A1 or R1C1.
Definition: grammar.hxx:111
@ GRAM_ODFF
ODFF with default ODF A1 bracketed references.
Definition: grammar.hxx:72
@ GRAM_PODF
ODF 1.1 with default ODF A1 bracketed references.
Definition: grammar.hxx:77
@ GRAM_EXTERNAL
OpCodeMap set by external filter and merged with reference convention plus English bit on top.
Definition: grammar.hxx:160
@ GRAM_OOXML
Excel OOXML with Excel OOXML reference style.
Definition: grammar.hxx:142
static Grammar setEnglishBit(const Grammar eGrammar, const bool bEnglish)
Definition: grammar.cxx:66
static bool isOOXML(const Grammar eGrammar)
If grammar is of OOXML.
Definition: grammar.hxx:206
FormulaToken * PeekPrevNoSpaces() const
Definition: token.cxx:1872
FormulaToken * PeekNextNoSpaces() const
Only after Reset/First/Next/Last/Prev!
Definition: token.cxx:1856
void AfterRemoveToken(sal_uInt16 nOffset, sal_uInt16 nCount)
Definition: token.cxx:1888
FormulaToken * TokenAt(sal_uInt16 nIdx) const
Return pCode[nIdx], or nullptr if nIdx is out of bounds.
Definition: tokenarray.hxx:320
void SetHyperLink(bool bVal)
Definition: tokenarray.hxx:391
bool IsRecalcModeNormal() const
Definition: tokenarray.hxx:421
bool NeedsOoxmlRewrite()
Determines if this formula needs any changes to convert it to OOXML.
Definition: token.cxx:1367
sal_uInt16 GetLen() const
Definition: tokenarray.hxx:387
void CheckAllRPNTokens()
Call CheckToken() for all RPN tokens.
Definition: token.cxx:739
void AddRecalcMode(ScRecalcMode nBits)
Bits aren't set directly but validated and handled according to priority if more than one exclusive b...
Definition: token.cxx:920
bool IsRecalcModeAlways() const
Definition: tokenarray.hxx:423
FormulaTokenArray * RewriteMissing(const MissingConvention &rConv)
Rewrites to Plain Old Formula or OOXML, substituting missing parameters.
Definition: token.cxx:1378
void CreateNewRPNArrayFromData(FormulaToken **pData, sal_uInt16 nSize)
Assign pRPN to point to a newly created array filled with the data from pData.
Definition: tokenarray.hxx:361
bool IsRecalcModeForced() const
Definition: tokenarray.hxx:425
OpCode OpCodeBefore(sal_uInt16 nIdx) const
Return the opcode at pCode[nIdx-1], ocNone if nIdx-1 is out of bounds.
Definition: tokenarray.hxx:331
bool NeedsPodfRewrite(const MissingConventionODF &rConv)
Determines if this formula needs any changes to convert it to something previous versions of OOo coul...
Definition: token.cxx:1357
FormulaToken * ReplaceToken(sal_uInt16 nOffset, FormulaToken *, ReplaceMode eMode)
Also used by the compiler.
Definition: token.cxx:761
void SetCodeError(FormulaError n)
Definition: tokenarray.hxx:390
FormulaError GetCodeError() const
Definition: tokenarray.hxx:389
sal_uInt16 RemoveToken(sal_uInt16 nOffset, sal_uInt16 nCount)
Remove a sequence of tokens from pCode array, and pRPN array if the tokens are referenced there.
Definition: token.cxx:794
ScRecalcMode GetRecalcMode() const
Definition: tokenarray.hxx:394
void NewOpCode(OpCode e, const PrivateAccess &)
Definition: token.hxx:217
void DecRef() const
Definition: token.hxx:151
virtual ParamClass GetInForceArray() const
Definition: token.cxx:173
OpCode GetOpCode() const
Definition: token.hxx:158
StackVar GetType() const
Definition: token.hxx:138
virtual void SetByte(sal_uInt8 n)
Definition: token.cxx:168
virtual short * GetJump() const
Definition: token.cxx:252
virtual double GetDouble() const
Definition: token.cxx:184
#define SC_OPCODE_STOP_BIN_OP
Definition: compiler.hxx:95
#define SC_OPCODE_IF
Definition: compiler.hxx:32
#define SC_OPCODE_SEP
Definition: compiler.hxx:38
#define SC_OPCODE_ARRAY_COL_SEP
Definition: compiler.hxx:54
#define SC_OPCODE_CHOOSE
Definition: compiler.hxx:35
#define SC_OPCODE_STOP_1_PAR
Definition: compiler.hxx:210
#define SC_OPCODE_START_2_PAR
Definition: compiler.hxx:213
#define SC_OPCODE_IF_NA
Definition: compiler.hxx:34
#define SC_OPCODE_ARRAY_OPEN
Definition: compiler.hxx:51
#define SC_OPCODE_STOP_UN_OP
Definition: compiler.hxx:102
#define SC_OPCODE_ARRAY_CLOSE
Definition: compiler.hxx:52
#define SC_OPCODE_CLOSE
Definition: compiler.hxx:37
#define SC_OPCODE_START_UN_OP
Definition: compiler.hxx:100
#define SC_OPCODE_STOP_2_PAR
Definition: compiler.hxx:513
#define SC_OPCODE_STOP_NO_PAR
Definition: compiler.hxx:117
#define SC_OPCODE_NO_NAME
Definition: compiler.hxx:380
#define SC_OPCODE_ARRAY_ROW_SEP
Definition: compiler.hxx:53
#define SC_OPCODE_OPEN
Definition: compiler.hxx:36
#define SC_OPCODE_OR
Definition: compiler.hxx:91
#define SC_OPCODE_START_1_PAR
Definition: compiler.hxx:120
#define SC_OPCODE_START_NO_PAR
Definition: compiler.hxx:107
#define SC_OPCODE_START_BIN_OP
Definition: compiler.hxx:77
#define SC_OPCODE_AND
Definition: compiler.hxx:90
#define SC_OPCODE_LAST_OPCODE_ID
Definition: compiler.hxx:516
#define SC_OPCODE_IF_ERROR
Definition: compiler.hxx:33
OUString ForResId(TranslateId aId)
int nCount
UNDEFINED
bool isPublishedFormulaError(FormulaError nErr)
Error values that are accepted as detailed "#ERRxxx!" constants.
Definition: errorcodes.hxx:128
FormulaError
Definition: errorcodes.hxx:32
DocumentType eType
sal_Int32 nIndex
void * p
sal_Int64 n
sal_uInt16 nPos
#define SAL_INFO_IF(condition, area, stream)
#define SAL_WARN_IF(condition, area, stream)
#define SAL_WARN(area, stream)
#define SAL_INFO(area, stream)
#define SAL_N_ELEMENTS(arr)
aStr
std::unique_ptr< sal_Int32[]> pData
static const char * pInternal[2]
@ Value
In array formula: single value to be passed.
Definition: paramclass.hxx:32
@ SuppressedReferenceOrForceArray
Same as ReferenceOrForceArray but suppressed / not inherited in the compiler's ForceArray context to ...
Definition: paramclass.hxx:70
@ ReferenceOrRefArray
Like Reference but the function accepts also a list of references (ocUnion svRefList) as one argument...
Definition: paramclass.hxx:45
@ ForceArray
Area reference must be converted to array in any case, and must also be propagated to subsequent oper...
Definition: paramclass.hxx:56
@ ReferenceOrForceArray
Area reference is not converted to array, but ForceArray must be propagated to subsequent operators a...
Definition: paramclass.hxx:63
@ ForceArrayReturn
A function return forces the caller into array mode for this one call, making it behave like it had F...
Definition: paramclass.hxx:75
@ Reference
In array formula: area reference must stay reference.
Definition: paramclass.hxx:37
::boost::intrusive_ptr< FormulaToken > FormulaTokenRef
Definition: types.hxx:27
constexpr short nRecursionMax
StackVar
Definition: token.hxx:49
@ svMissing
Definition: token.hxx:79
@ svIndex
Definition: token.hxx:56
@ svExternalDoubleRef
Definition: token.hxx:74
@ svExternalName
Definition: token.hxx:75
@ svDouble
Definition: token.hxx:51
@ svSep
Definition: token.hxx:80
@ svError
Definition: token.hxx:78
@ svDoubleRef
Definition: token.hxx:54
@ svExternal
Definition: token.hxx:58
@ svExternalSingleRef
Definition: token.hxx:73
@ svMatrixCell
Definition: token.hxx:64
@ svString
Definition: token.hxx:52
@ svJump
Definition: token.hxx:57
@ svMatrix
Definition: token.hxx:55
@ svFAP
Definition: token.hxx:59
@ svByte
Definition: token.hxx:50
@ svSingleRef
Definition: token.hxx:53
static bool lcl_fillNativeSymbols(FormulaCompiler::NonConstOpCodeMapPtr &xMap, FormulaCompiler::InitSymbols eWhat=FormulaCompiler::InitSymbols::INIT)
std::unordered_map< OUString, OpCode > OpCodeHashMap
int i
sal_uInt32 toUInt32(std::u16string_view str, sal_Int16 radix=10)
HashMap_OWString_Interface aMap
OpCode
Definition: opcode.hxx:29
@ ocCurrent
Definition: opcode.hxx:109
@ ocNot
Definition: opcode.hxx:112
@ ocEasterSunday
Definition: opcode.hxx:492
@ ocDebugVar
Definition: opcode.hxx:510
@ ocTableRefItemThisRow
Definition: opcode.hxx:62
@ ocInternalBegin
Definition: opcode.hxx:508
@ ocIsEven
Definition: opcode.hxx:163
@ ocMatrixUnit
Definition: opcode.hxx:355
@ ocBackSolver
Definition: opcode.hxx:357
@ ocDiv
Definition: opcode.hxx:84
@ ocAggregate
Definition: opcode.hxx:395
@ ocTableRef
Definition: opcode.hxx:66
@ ocIRR
Definition: opcode.hxx:239
@ ocTableRefItemHeaders
Definition: opcode.hxx:59
@ ocCall
Definition: opcode.hxx:32
@ ocFalse
Definition: opcode.hxx:105
@ ocStyle
Definition: opcode.hxx:480
@ ocInfo
Definition: opcode.hxx:195
@ ocNPV
Definition: opcode.hxx:238
@ ocAmpersand
Definition: opcode.hxx:85
@ ocStringXML
Definition: opcode.hxx:54
@ ocIsEmpty
Definition: opcode.hxx:151
@ ocDDB
Definition: opcode.hxx:266
@ ocWebservice
Definition: opcode.hxx:500
@ ocIsRef
Definition: opcode.hxx:157
@ ocDde
Definition: opcode.hxx:481
@ ocTrunc
Definition: opcode.hxx:215
@ ocGrowth
Definition: opcode.hxx:432
@ ocCell
Definition: opcode.hxx:156
@ ocMultiArea
Definition: opcode.hxx:320
@ ocLessEqual
Definition: opcode.hxx:91
@ ocConvertOOo
Definition: opcode.hxx:494
@ ocNone
Definition: opcode.hxx:515
@ ocOr
Definition: opcode.hxx:94
@ ocNotEqual
Definition: opcode.hxx:88
@ ocClose
Definition: opcode.hxx:43
@ ocPMT
Definition: opcode.hxx:271
@ ocColRowName
Definition: opcode.hxx:68
@ ocIsNA
Definition: opcode.hxx:160
@ ocCumIpmt
Definition: opcode.hxx:282
@ ocMatInv
Definition: opcode.hxx:352
@ ocIndirect
Definition: opcode.hxx:307
@ ocHLookup
Definition: opcode.hxx:319
@ ocSubTotal
Definition: opcode.hxx:286
@ ocFV
Definition: opcode.hxx:277
@ ocArrayRowSep
Definition: opcode.hxx:49
@ ocPpmt
Definition: opcode.hxx:281
@ ocRow
Definition: opcode.hxx:275
@ ocGetActTime
Definition: opcode.hxx:107
@ ocMacro
Definition: opcode.hxx:67
@ ocNominal
Definition: opcode.hxx:285
@ ocSYD
Definition: opcode.hxx:265
@ ocIsNonString
Definition: opcode.hxx:153
@ ocSLN
Definition: opcode.hxx:270
@ ocOffset
Definition: opcode.hxx:321
@ ocFixed
Definition: opcode.hxx:327
@ ocIfNA
Definition: opcode.hxx:39
@ ocXor
Definition: opcode.hxx:95
@ ocMatTrans
Definition: opcode.hxx:354
@ ocIsString
Definition: opcode.hxx:152
@ ocPow
Definition: opcode.hxx:86
@ ocGetDate
Definition: opcode.hxx:226
@ ocAdd
Definition: opcode.hxx:81
@ ocStop
Definition: opcode.hxx:33
@ ocTableRefItemTotals
Definition: opcode.hxx:61
@ ocRange
Definition: opcode.hxx:98
@ ocUnion
Definition: opcode.hxx:97
@ ocHyperLink
Definition: opcode.hxx:496
@ ocErrName
Definition: opcode.hxx:77
@ ocEqual
Definition: opcode.hxx:87
@ ocLogest
Definition: opcode.hxx:434
@ ocFrequency
Definition: opcode.hxx:411
@ ocModalValue_Multi
Definition: opcode.hxx:392
@ ocErrorType
Definition: opcode.hxx:191
@ ocCumPrinc
Definition: opcode.hxx:283
@ ocMatMult
Definition: opcode.hxx:353
@ ocIpmt
Definition: opcode.hxx:280
@ ocRRI
Definition: opcode.hxx:276
@ ocGetActDate
Definition: opcode.hxx:106
@ ocErrValue
Definition: opcode.hxx:75
@ ocOpen
Definition: opcode.hxx:42
@ ocSub
Definition: opcode.hxx:82
@ ocMIRR
Definition: opcode.hxx:240
@ ocTrend
Definition: opcode.hxx:431
@ ocInternalEnd
Definition: opcode.hxx:511
@ ocWeeknumOOo
Definition: opcode.hxx:474
@ ocGreater
Definition: opcode.hxx:90
@ ocRandom
Definition: opcode.hxx:103
@ ocColRowNameAuto
Definition: opcode.hxx:69
@ ocTrue
Definition: opcode.hxx:104
@ ocLinest
Definition: opcode.hxx:433
@ ocTableRefItemData
Definition: opcode.hxx:60
@ ocExternal
Definition: opcode.hxx:34
@ ocSpaces
Definition: opcode.hxx:55
@ ocAnd
Definition: opcode.hxx:93
@ ocPercentSign
Definition: opcode.hxx:71
@ ocErrNull
Definition: opcode.hxx:73
@ ocSep
Definition: opcode.hxx:46
@ ocMissing
Definition: opcode.hxx:52
@ ocTableRefItemAll
Definition: opcode.hxx:58
@ ocVBD
Definition: opcode.hxx:268
@ ocIntersect
Definition: opcode.hxx:96
@ ocPush
Definition: opcode.hxx:31
@ ocGreaterEqual
Definition: opcode.hxx:92
@ ocBad
Definition: opcode.hxx:53
@ ocIsError
Definition: opcode.hxx:162
@ ocErrNA
Definition: opcode.hxx:79
@ ocDBArea
Definition: opcode.hxx:65
@ ocRate
Definition: opcode.hxx:279
@ ocIfError
Definition: opcode.hxx:38
@ ocErrNum
Definition: opcode.hxx:78
@ ocWhitespace
Definition: opcode.hxx:56
@ ocTableRefClose
Definition: opcode.hxx:45
@ ocMatRef
Definition: opcode.hxx:57
@ ocLess
Definition: opcode.hxx:89
@ ocCurrency
Definition: opcode.hxx:325
@ ocFourier
Definition: opcode.hxx:505
@ ocErrDivZero
Definition: opcode.hxx:74
@ ocNoName
Definition: opcode.hxx:479
@ ocDB
Definition: opcode.hxx:267
@ ocIsFormula
Definition: opcode.hxx:159
@ ocErrRef
Definition: opcode.hxx:76
@ ocArrayColSep
Definition: opcode.hxx:50
@ ocMul
Definition: opcode.hxx:83
@ ocPV
Definition: opcode.hxx:264
@ ocVLookup
Definition: opcode.hxx:318
@ ocGetTime
Definition: opcode.hxx:227
@ ocTableOp
Definition: opcode.hxx:460
@ ocChoose
Definition: opcode.hxx:40
@ ocColumn
Definition: opcode.hxx:274
@ ocGetDayOfWeek
Definition: opcode.hxx:475
@ ocExact
Definition: opcode.hxx:329
@ ocIf
Definition: opcode.hxx:37
@ ocIsValue
Definition: opcode.hxx:158
@ ocGetDiffDate360
Definition: opcode.hxx:229
@ ocFormula
Definition: opcode.hxx:193
@ ocIsOdd
Definition: opcode.hxx:164
@ ocIsErr
Definition: opcode.hxx:161
@ ocIsoWeeknum
Definition: opcode.hxx:473
@ ocIsLogical
Definition: opcode.hxx:154
@ ocEffect
Definition: opcode.hxx:284
@ ocTableRefOpen
Definition: opcode.hxx:44
@ ocName
Definition: opcode.hxx:35
This is dirty and only the compiler should use it!
Definition: token.hxx:216
unsigned char sal_uInt8
#define SAL_MAX_UINT16
sal_uInt16 sal_Unicode
std::unique_ptr< char[]> aBuffer
SvNumFormatType