1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
19 #include <sal/macros.h>
20 #include <sal/log.hxx>
21 #include <rtl/math.hxx>
22 #include <formula/FormulaCompiler.hxx>
23 #include <formula/errorcodes.hxx>
24 #include <formula/token.hxx>
25 #include <formula/tokenarray.hxx>
26 #include <o3tl/string_view.hxx>
27 #include <core_resource.hxx>
28 #include <core_resource.hrc>
30 #include <svl/zforlist.hxx>
31 #include <unotools/charclass.hxx>
32 #include <vcl/svapp.hxx>
33 #include <vcl/settings.hxx>
34 #include <comphelper/lok.hxx>
35 #include <comphelper/processfactory.hxx>
36 #include <com/sun/star/sheet/FormulaOpCodeMapEntry.hpp>
37 #include <com/sun/star/sheet/FormulaMapGroup.hpp>
38 #include <com/sun/star/sheet/FormulaMapGroupSpecialOffset.hpp>
44 using namespace ::com::sun::star
;
46 static const char* pInternal
[2] = { "TTT", "__DEBUG_VAR" };
50 class FormulaCompilerRecursionGuard
55 explicit FormulaCompilerRecursionGuard( short& rRec
)
56 : rRecursion( rRec
) { ++rRecursion
; }
57 ~FormulaCompilerRecursionGuard() { --rRecursion
; }
60 SvNumFormatType
lcl_GetRetFormat( OpCode eOpCode
)
89 return SvNumFormatType::LOGICAL
;
93 return SvNumFormatType::DATE
;
95 return SvNumFormatType::DATETIME
;
97 return SvNumFormatType::TIME
;
111 return SvNumFormatType::CURRENCY
;
119 return SvNumFormatType::PERCENT
;
121 return SvNumFormatType::NUMBER
;
125 void lclPushOpCodeMapEntry( ::std::vector
< sheet::FormulaOpCodeMapEntry
>& rVec
,
126 const OUString
* pTable
, sal_uInt16 nOpCode
)
128 sheet::FormulaOpCodeMapEntry aEntry
;
129 aEntry
.Token
.OpCode
= nOpCode
;
130 aEntry
.Name
= pTable
[nOpCode
];
131 rVec
.push_back( aEntry
);
134 void lclPushOpCodeMapEntries( ::std::vector
< sheet::FormulaOpCodeMapEntry
>& rVec
,
135 const OUString
* pTable
, sal_uInt16 nOpCodeBeg
, sal_uInt16 nOpCodeEnd
)
137 for (sal_uInt16 nOpCode
= nOpCodeBeg
; nOpCode
< nOpCodeEnd
; ++nOpCode
)
138 lclPushOpCodeMapEntry( rVec
, pTable
, nOpCode
);
141 void lclPushOpCodeMapEntries( ::std::vector
< sheet::FormulaOpCodeMapEntry
>& rVec
,
142 const OUString
* pTable
, const sal_uInt16
* pnOpCodes
, size_t nCount
)
144 for (const sal_uInt16
* pnEnd
= pnOpCodes
+ nCount
; pnOpCodes
< pnEnd
; ++pnOpCodes
)
145 lclPushOpCodeMapEntry( rVec
, pTable
, *pnOpCodes
);
148 CharClass
* createCharClassIfNonEnglishUI()
150 const LanguageTag
& rLanguageTag( Application::GetSettings().GetUILanguageTag());
151 if (rLanguageTag
.getLanguage() == "en")
153 return new CharClass( ::comphelper::getProcessComponentContext(), rLanguageTag
);
160 OpCodeList(const std::pair
<const char*, int>* pSymbols
, const FormulaCompiler::NonConstOpCodeMapPtr
&,
161 FormulaCompiler::SeparatorType
= FormulaCompiler::SeparatorType::SEMICOLON_BASE
);
162 OpCodeList(const std::pair
<TranslateId
, int>* pSymbols
, const FormulaCompiler::NonConstOpCodeMapPtr
&,
163 FormulaCompiler::SeparatorType
= FormulaCompiler::SeparatorType::SEMICOLON_BASE
);
166 bool getOpCodeString( OUString
& rStr
, sal_uInt16 nOp
);
167 void putDefaultOpCode( const FormulaCompiler::NonConstOpCodeMapPtr
& xMap
, sal_uInt16 nOp
, const CharClass
* pCharClass
);
170 FormulaCompiler::SeparatorType meSepType
;
171 const std::pair
<const char*, int>* mpSymbols1
;
172 const std::pair
<TranslateId
, int>* mpSymbols2
;
175 OpCodeList::OpCodeList(const std::pair
<const char*, int>* pSymbols
, const FormulaCompiler::NonConstOpCodeMapPtr
& xMap
,
176 FormulaCompiler::SeparatorType eSepType
)
177 : meSepType(eSepType
)
178 , mpSymbols1(pSymbols
)
179 , mpSymbols2(nullptr)
181 std::unique_ptr
<CharClass
> xCharClass( xMap
->isEnglish() ? nullptr : createCharClassIfNonEnglishUI());
182 const CharClass
* pCharClass
= xCharClass
.get();
183 if (meSepType
== FormulaCompiler::SeparatorType::RESOURCE_BASE
)
185 for (sal_uInt16 i
= 0; i
<= SC_OPCODE_LAST_OPCODE_ID
; ++i
)
187 putDefaultOpCode( xMap
, i
, pCharClass
);
192 for (sal_uInt16 i
= 0; i
<= SC_OPCODE_LAST_OPCODE_ID
; ++i
)
195 if ( getOpCodeString( aOpStr
, i
) )
196 xMap
->putOpCode( aOpStr
, OpCode(i
), pCharClass
);
198 putDefaultOpCode( xMap
, i
, pCharClass
);
203 OpCodeList::OpCodeList(const std::pair
<TranslateId
, int>* pSymbols
, const FormulaCompiler::NonConstOpCodeMapPtr
& xMap
,
204 FormulaCompiler::SeparatorType eSepType
)
205 : meSepType(eSepType
)
206 , mpSymbols1(nullptr)
207 , mpSymbols2(pSymbols
)
209 std::unique_ptr
<CharClass
> xCharClass( xMap
->isEnglish() ? nullptr : createCharClassIfNonEnglishUI());
210 const CharClass
* pCharClass
= xCharClass
.get();
211 if (meSepType
== FormulaCompiler::SeparatorType::RESOURCE_BASE
)
213 for (sal_uInt16 i
= 0; i
<= SC_OPCODE_LAST_OPCODE_ID
; ++i
)
215 putDefaultOpCode( xMap
, i
, pCharClass
);
220 for (sal_uInt16 i
= 0; i
<= SC_OPCODE_LAST_OPCODE_ID
; ++i
)
223 if ( getOpCodeString( aOpStr
, i
) )
224 xMap
->putOpCode( aOpStr
, OpCode(i
), pCharClass
);
226 putDefaultOpCode( xMap
, i
, pCharClass
);
231 bool OpCodeList::getOpCodeString( OUString
& rStr
, sal_uInt16 nOp
)
237 if (meSepType
== FormulaCompiler::SeparatorType::SEMICOLON_BASE
)
244 case SC_OPCODE_ARRAY_COL_SEP
:
246 if (meSepType
== FormulaCompiler::SeparatorType::SEMICOLON_BASE
)
253 case SC_OPCODE_ARRAY_ROW_SEP
:
255 if (meSepType
== FormulaCompiler::SeparatorType::SEMICOLON_BASE
)
267 void OpCodeList::putDefaultOpCode( const FormulaCompiler::NonConstOpCodeMapPtr
& xMap
, sal_uInt16 nOp
,
268 const CharClass
* pCharClass
)
273 const char* pKey
= nullptr;
274 for (const std::pair
<const char*, int>* pSymbol
= mpSymbols1
; pSymbol
->first
; ++pSymbol
)
276 if (nOp
== pSymbol
->second
)
278 pKey
= pSymbol
->first
;
284 sKey
= OUString::createFromAscii(pKey
);
289 for (const std::pair
<TranslateId
, int>* pSymbol
= mpSymbols2
; pSymbol
->first
; ++pSymbol
)
291 if (nOp
== pSymbol
->second
)
293 pKey
= pSymbol
->first
;
299 sKey
= ForResId(pKey
);
301 xMap
->putOpCode(sKey
, OpCode(nOp
), pCharClass
);
305 const sal_Unicode
* lcl_UnicodeStrChr( const sal_Unicode
* pStr
, sal_Unicode c
)
320 FormulaCompiler::NonConstOpCodeMapPtr mxSymbolMap
;
325 bool isPotentialRangeLeftOp( OpCode eOp
)
336 bool isRangeResultFunction( OpCode eOp
)
348 bool isRangeResultOpCode( OpCode eOp
)
365 MUST be a valid token, caller has to ensure.
368 If bRPN==false, bRight==false means opcodes for left side are
369 checked, bRight==true means opcodes for right side. If bRPN==true
370 it doesn't matter except for the ocSep converted to ocUnion case.
372 bool isPotentialRangeType( FormulaToken
const * pToken
, bool bRPN
, bool bRight
)
374 switch (pToken
->GetType())
376 case svByte
: // could be range result, but only a few
378 return isRangeResultOpCode( pToken
->GetOpCode());
380 return isRangeResultFunction( pToken
->GetOpCode());
382 return isPotentialRangeLeftOp( pToken
->GetOpCode());
385 case svIndex
: // could be range
386 //case svRefList: // um..what?
387 case svExternalSingleRef
:
388 case svExternalDoubleRef
:
389 case svExternalName
: // could be range
392 // A special case if a previous ocSep was converted to ocUnion it
393 // stays svSep instead of svByte.
394 return bRPN
&& !bRight
&& pToken
->GetOpCode() == ocUnion
;
396 // Separators are not part of RPN and right opcodes need to be
397 // other StackVar types or functions and thus svByte.
398 return !bRPN
&& !bRight
&& isPotentialRangeLeftOp( pToken
->GetOpCode());
402 bool isIntersectable( FormulaToken
** pCode1
, FormulaToken
** pCode2
)
404 FormulaToken
* pToken1
= *pCode1
;
405 FormulaToken
* pToken2
= *pCode2
;
406 if (pToken1
&& pToken2
)
407 return isPotentialRangeType( pToken1
, true, false) && isPotentialRangeType( pToken2
, true, true);
411 bool isAdjacentRpnEnd( sal_uInt16 nPC
,
412 FormulaToken
const * const * const pCode
,
413 FormulaToken
const * const * const pCode1
,
414 FormulaToken
const * const * const pCode2
)
416 return nPC
>= 2 && pCode1
&& pCode2
&&
417 (pCode2
- pCode1
== 1) && (pCode
- pCode2
== 1) &&
418 (*pCode1
!= nullptr) && (*pCode2
!= nullptr);
421 bool isAdjacentOrGapRpnEnd( sal_uInt16 nPC
,
422 FormulaToken
const * const * const pCode
,
423 FormulaToken
const * const * const pCode1
,
424 FormulaToken
const * const * const pCode2
)
426 return nPC
>= 2 && pCode1
&& pCode2
&&
427 (pCode2
> pCode1
) && (pCode
- pCode2
== 1) &&
428 (*pCode1
!= nullptr) && (*pCode2
!= nullptr);
435 void FormulaCompiler::OpCodeMap::putExternal( const OUString
& rSymbol
, const OUString
& rAddIn
)
437 // Different symbols may map to the same AddIn, but the same AddIn may not
438 // map to different symbols, the first pair wins. Same symbol of course may
439 // not map to different AddIns, again the first pair wins and also the
440 // AddIn->symbol mapping is not inserted in other cases.
441 bool bOk
= maExternalHashMap
.emplace(rSymbol
, rAddIn
).second
;
442 SAL_WARN_IF( !bOk
, "formula.core", "OpCodeMap::putExternal: symbol not inserted, " << rSymbol
<< " -> " << rAddIn
);
445 bOk
= maReverseExternalHashMap
.emplace(rAddIn
, rSymbol
).second
;
446 // Failed insertion of the AddIn is ok for different symbols mapping to
447 // the same AddIn. Make this INFO only.
448 SAL_INFO_IF( !bOk
, "formula.core", "OpCodeMap::putExternal: AddIn not inserted, " << rAddIn
<< " -> " << rSymbol
);
452 void FormulaCompiler::OpCodeMap::putExternalSoftly( const OUString
& rSymbol
, const OUString
& rAddIn
)
454 // Same as putExternal() but no warning, instead info whether inserted or not.
455 bool bOk
= maExternalHashMap
.emplace(rSymbol
, rAddIn
).second
;
456 SAL_INFO( "formula.core", "OpCodeMap::putExternalSoftly: symbol " << (bOk
? "" : "not ") << "inserted, " << rSymbol
<< " -> " << rAddIn
);
459 bOk
= maReverseExternalHashMap
.emplace(rAddIn
, rSymbol
).second
;
460 SAL_INFO_IF( !bOk
, "formula.core", "OpCodeMap::putExternalSoftly: AddIn not inserted, " << rAddIn
<< " -> " << rSymbol
);
464 uno::Sequence
< sheet::FormulaToken
> FormulaCompiler::OpCodeMap::createSequenceOfFormulaTokens(
465 const FormulaCompiler
& rCompiler
, const uno::Sequence
< OUString
>& rNames
) const
467 const sal_Int32 nLen
= rNames
.getLength();
468 uno::Sequence
< sheet::FormulaToken
> aTokens( nLen
);
469 sheet::FormulaToken
* pToken
= aTokens
.getArray();
470 OUString
const * pName
= rNames
.getConstArray();
471 OUString
const * const pStop
= pName
+ nLen
;
472 for ( ; pName
< pStop
; ++pName
, ++pToken
)
474 OpCodeHashMap::const_iterator
iLook( maHashMap
.find( *pName
));
475 if (iLook
!= maHashMap
.end())
476 pToken
->OpCode
= (*iLook
).second
;
482 ExternalHashMap::const_iterator
iExt( maExternalHashMap
.find( *pName
));
483 if (iExt
!= maExternalHashMap
.end())
484 aIntName
= (*iExt
).second
;
485 // Check for existence not needed here, only name-mapping is of
488 if (aIntName
.isEmpty())
489 aIntName
= rCompiler
.FindAddInFunction(*pName
, !isEnglish()); // bLocalFirst=false for english
490 if (aIntName
.isEmpty())
491 pToken
->OpCode
= getOpCodeUnknown();
494 pToken
->OpCode
= ocExternal
;
495 pToken
->Data
<<= aIntName
;
502 uno::Sequence
< sheet::FormulaOpCodeMapEntry
> FormulaCompiler::OpCodeMap::createSequenceOfAvailableMappings(
503 const FormulaCompiler
& rCompiler
, const sal_Int32 nGroups
) const
505 using namespace sheet
;
507 // Unfortunately uno::Sequence can't grow without cumbersome reallocs. As
508 // we don't know in advance how many elements it will have we use a
509 // temporary vector to add elements and then copy to Sequence :-(
510 ::std::vector
< FormulaOpCodeMapEntry
> aVec
;
512 if (nGroups
== FormulaMapGroup::SPECIAL
)
514 // Use specific order, keep in sync with
515 // offapi/com/sun/star/sheet/FormulaMapGroupSpecialOffset.idl
521 { FormulaMapGroupSpecialOffset::PUSH
, ocPush
} ,
522 { FormulaMapGroupSpecialOffset::CALL
, ocCall
} ,
523 { FormulaMapGroupSpecialOffset::STOP
, ocStop
} ,
524 { FormulaMapGroupSpecialOffset::EXTERNAL
, ocExternal
} ,
525 { FormulaMapGroupSpecialOffset::NAME
, ocName
} ,
526 { FormulaMapGroupSpecialOffset::NO_NAME
, ocNoName
} ,
527 { FormulaMapGroupSpecialOffset::MISSING
, ocMissing
} ,
528 { FormulaMapGroupSpecialOffset::BAD
, ocBad
} ,
529 { FormulaMapGroupSpecialOffset::SPACES
, ocSpaces
} ,
530 { FormulaMapGroupSpecialOffset::MAT_REF
, ocMatRef
} ,
531 { FormulaMapGroupSpecialOffset::DB_AREA
, ocDBArea
} ,
532 { FormulaMapGroupSpecialOffset::MACRO
, ocMacro
} ,
533 { FormulaMapGroupSpecialOffset::COL_ROW_NAME
, ocColRowName
} ,
534 { FormulaMapGroupSpecialOffset::WHITESPACE
, ocWhitespace
} ,
535 { FormulaMapGroupSpecialOffset::TABLE_REF
, ocTableRef
}
537 const size_t nCount
= SAL_N_ELEMENTS(aMap
);
538 // Preallocate vector elements.
539 FormulaOpCodeMapEntry aEntry
;
540 aEntry
.Token
.OpCode
= getOpCodeUnknown();
541 aVec
.resize(nCount
, aEntry
);
545 size_t nIndex
= static_cast< size_t >( i
.nOff
);
546 if (aVec
.size() <= nIndex
)
548 // The offsets really should be aligned with the size, so if
549 // the vector was preallocated above this code to resize it is
550 // just a measure in case the table isn't in sync with the API,
551 // usually it isn't executed.
552 aEntry
.Token
.OpCode
= getOpCodeUnknown();
553 aVec
.resize( nIndex
+ 1, aEntry
);
555 aEntry
.Token
.OpCode
= i
.eOp
;
556 aVec
[nIndex
] = aEntry
;
561 /* FIXME: Once we support error constants in formulas we'll need a map
562 * group for that, e.g. FormulaMapGroup::ERROR_CONSTANTS, and fill
563 * SC_OPCODE_START_ERRORS to SC_OPCODE_STOP_ERRORS. */
565 // Anything else but SPECIAL.
566 if ((nGroups
& FormulaMapGroup::SEPARATORS
) != 0)
568 static const sal_uInt16 aOpCodes
[] = {
573 lclPushOpCodeMapEntries( aVec
, mpTable
.get(), aOpCodes
, SAL_N_ELEMENTS(aOpCodes
) );
575 if ((nGroups
& FormulaMapGroup::ARRAY_SEPARATORS
) != 0)
577 static const sal_uInt16 aOpCodes
[] = {
578 SC_OPCODE_ARRAY_OPEN
,
579 SC_OPCODE_ARRAY_CLOSE
,
580 SC_OPCODE_ARRAY_ROW_SEP
,
581 SC_OPCODE_ARRAY_COL_SEP
583 lclPushOpCodeMapEntries( aVec
, mpTable
.get(), aOpCodes
, SAL_N_ELEMENTS(aOpCodes
) );
585 if ((nGroups
& FormulaMapGroup::UNARY_OPERATORS
) != 0)
587 // Due to the nature of the percent operator following its operand
588 // it isn't sorted into unary operators for compiler interna.
589 lclPushOpCodeMapEntry( aVec
, mpTable
.get(), ocPercentSign
);
590 // "+" can be used as unary operator too, push only if binary group is not set
591 if ((nGroups
& FormulaMapGroup::BINARY_OPERATORS
) == 0)
592 lclPushOpCodeMapEntry( aVec
, mpTable
.get(), ocAdd
);
593 // regular unary operators
594 for (sal_uInt16 nOp
= SC_OPCODE_START_UN_OP
; nOp
< SC_OPCODE_STOP_UN_OP
&& nOp
< mnSymbols
; ++nOp
)
596 lclPushOpCodeMapEntry( aVec
, mpTable
.get(), nOp
);
599 if ((nGroups
& FormulaMapGroup::BINARY_OPERATORS
) != 0)
601 for (sal_uInt16 nOp
= SC_OPCODE_START_BIN_OP
; nOp
< SC_OPCODE_STOP_BIN_OP
&& nOp
< mnSymbols
; ++nOp
)
605 // AND and OR in fact are functions but for legacy reasons
606 // are sorted into binary operators for compiler interna.
611 lclPushOpCodeMapEntry( aVec
, mpTable
.get(), nOp
);
615 if ((nGroups
& FormulaMapGroup::FUNCTIONS
) != 0)
617 // Function names are not consecutive, skip the gaps between
618 // functions with no parameter, functions with 1 parameter
619 lclPushOpCodeMapEntries( aVec
, mpTable
.get(), SC_OPCODE_START_NO_PAR
,
620 ::std::min
< sal_uInt16
>( SC_OPCODE_STOP_NO_PAR
, mnSymbols
) );
621 lclPushOpCodeMapEntries( aVec
, mpTable
.get(), SC_OPCODE_START_1_PAR
,
622 ::std::min
< sal_uInt16
>( SC_OPCODE_STOP_1_PAR
, mnSymbols
) );
623 // Additional functions not within range of functions.
624 static const sal_uInt16 aOpCodes
[] = {
633 lclPushOpCodeMapEntries( aVec
, mpTable
.get(), aOpCodes
, SAL_N_ELEMENTS(aOpCodes
) );
634 // functions with 2 or more parameters.
635 for (sal_uInt16 nOp
= SC_OPCODE_START_2_PAR
; nOp
< SC_OPCODE_STOP_2_PAR
&& nOp
< mnSymbols
; ++nOp
)
639 // NO_NAME is in SPECIAL.
640 case SC_OPCODE_NO_NAME
:
643 lclPushOpCodeMapEntry( aVec
, mpTable
.get(), nOp
);
646 // If AddIn functions are present in this mapping, use them, and only those.
649 for (auto const& elem
: maExternalHashMap
)
651 FormulaOpCodeMapEntry aEntry
;
652 aEntry
.Name
= elem
.first
;
653 aEntry
.Token
.Data
<<= elem
.second
;
654 aEntry
.Token
.OpCode
= ocExternal
;
655 aVec
.push_back( aEntry
);
660 rCompiler
.fillAddInToken( aVec
, isEnglish());
664 return uno::Sequence
< FormulaOpCodeMapEntry
>(aVec
.data(), aVec
.size());
668 void FormulaCompiler::OpCodeMap::putOpCode( const OUString
& rStr
, const OpCode eOp
, const CharClass
* pCharClass
)
670 if (0 < eOp
&& sal_uInt16(eOp
) < mnSymbols
)
672 bool bPutOp
= mpTable
[eOp
].isEmpty();
673 bool bRemoveFromMap
= false;
678 // These OpCodes are meant to overwrite and also remove an
682 bRemoveFromMap
= true;
684 // These separator OpCodes are meant to overwrite and also
685 // remove an existing mapping if it is not used for one of the
689 bRemoveFromMap
= (mpTable
[ocArrayRowSep
] != mpTable
[eOp
] && mpTable
[ocSep
] != mpTable
[eOp
]);
693 bRemoveFromMap
= (mpTable
[ocArrayColSep
] != mpTable
[eOp
] && mpTable
[ocSep
] != mpTable
[eOp
]);
695 // For ocSep keep the ";" in map but remove any other if it is
696 // not used for ocArrayColSep or ocArrayRowSep.
699 bRemoveFromMap
= (mpTable
[eOp
] != ";" &&
700 mpTable
[ocArrayColSep
] != mpTable
[eOp
] &&
701 mpTable
[ocArrayRowSep
] != mpTable
[eOp
]);
703 // These OpCodes are known to be duplicates in the Excel
704 // external API mapping because of different parameter counts
705 // in different BIFF versions. Names are identical and entries
716 case ocGetDiffDate360
:
717 if (rStr
== mpTable
[eOp
])
720 // These OpCodes are known to be added to an existing mapping,
721 // but only for the OOXML external API mapping. This is *not*
722 // FormulaLanguage::OOXML. Keep the first
723 // (correct) definition for the OpCode, all following are
724 // additional alias entries in the map.
732 FormulaGrammar::extractFormulaLanguage( meGrammar
) == FormulaGrammar::GRAM_EXTERNAL
)
734 // Both bPutOp and bRemoveFromMap stay false.
739 SAL_WARN("formula.core",
740 "OpCodeMap::putOpCode: reusing OpCode " << static_cast<sal_uInt16
>(eOp
)
741 << ", replacing '" << mpTable
[eOp
] << "' with '" << rStr
<< "' in "
742 << (mbEnglish
? "" : "non-") << "English map 0x" << ::std::hex
<< meGrammar
);
746 // Case preserving opcode -> string, upper string -> opcode
749 OUString
aUpper( pCharClass
? pCharClass
->uppercase( mpTable
[eOp
]) : rStr
.toAsciiUpperCase());
750 // Ensure we remove a mapping only for the requested OpCode.
751 OpCodeHashMap::const_iterator
it( maHashMap
.find( aUpper
));
752 if (it
!= maHashMap
.end() && (*it
).second
== eOp
)
753 maHashMap
.erase( it
);
757 OUString
aUpper( pCharClass
? pCharClass
->uppercase( rStr
) : rStr
.toAsciiUpperCase());
758 maHashMap
.emplace(aUpper
, eOp
);
762 SAL_WARN( "formula.core", "OpCodeMap::putOpCode: OpCode out of range");
767 FormulaCompiler::FormulaCompiler( FormulaTokenArray
& rArr
, bool bComputeII
, bool bMatrixFlag
)
769 nCurrentFactorParam(0),
771 maArrIterator( rArr
),
776 nNumFmt( SvNumFormatType::UNDEFINED
),
778 meGrammar( formula::FormulaGrammar::GRAM_UNSPECIFIED
),
779 bAutoCorrect( false ),
782 needsRPNTokenCheck( false ),
783 mbJumpCommandReorder(true),
785 mbComputeII(bComputeII
),
786 mbMatrixFlag(bMatrixFlag
)
790 FormulaTokenArray
FormulaCompiler::smDummyTokenArray
;
792 FormulaCompiler::FormulaCompiler(bool bComputeII
, bool bMatrixFlag
)
794 nCurrentFactorParam(0),
796 maArrIterator( smDummyTokenArray
),
801 nNumFmt( SvNumFormatType::UNDEFINED
),
803 meGrammar( formula::FormulaGrammar::GRAM_UNSPECIFIED
),
804 bAutoCorrect( false ),
807 needsRPNTokenCheck( false ),
808 mbJumpCommandReorder(true),
810 mbComputeII(bComputeII
),
811 mbMatrixFlag(bMatrixFlag
)
815 FormulaCompiler::~FormulaCompiler()
819 FormulaCompiler::OpCodeMapPtr
FormulaCompiler::GetOpCodeMap( const sal_Int32 nLanguage
) const
821 const bool bTemporary
= !HasOpCodeMap(nLanguage
);
822 OpCodeMapPtr xMap
= GetFinalOpCodeMap(nLanguage
);
824 const_cast<FormulaCompiler
*>(this)->DestroyOpCodeMap(nLanguage
);
828 FormulaCompiler::OpCodeMapPtr
FormulaCompiler::GetFinalOpCodeMap( const sal_Int32 nLanguage
) const
830 FormulaCompiler::OpCodeMapPtr xMap
;
831 using namespace sheet
;
834 case FormulaLanguage::ODFF
:
836 InitSymbolsODFF( InitSymbols::INIT
);
837 xMap
= mxSymbolsODFF
;
839 case FormulaLanguage::ODF_11
:
841 InitSymbolsPODF( InitSymbols::INIT
);
842 xMap
= mxSymbolsPODF
;
844 case FormulaLanguage::ENGLISH
:
845 if (!mxSymbolsEnglish
)
846 InitSymbolsEnglish( InitSymbols::INIT
);
847 xMap
= mxSymbolsEnglish
;
849 case FormulaLanguage::NATIVE
:
850 if (!mxSymbolsNative
)
851 InitSymbolsNative( InitSymbols::INIT
);
852 xMap
= mxSymbolsNative
;
854 case FormulaLanguage::XL_ENGLISH
:
855 if (!mxSymbolsEnglishXL
)
856 InitSymbolsEnglishXL( InitSymbols::INIT
);
857 xMap
= mxSymbolsEnglishXL
;
859 case FormulaLanguage::OOXML
:
861 InitSymbolsOOXML( InitSymbols::INIT
);
862 xMap
= mxSymbolsOOXML
;
864 case FormulaLanguage::API
:
866 InitSymbolsAPI( InitSymbols::INIT
);
870 ; // nothing, NULL map returned
875 void FormulaCompiler::DestroyOpCodeMap( const sal_Int32 nLanguage
)
877 using namespace sheet
;
880 case FormulaLanguage::ODFF
:
881 InitSymbolsODFF( InitSymbols::DESTROY
);
883 case FormulaLanguage::ODF_11
:
884 InitSymbolsPODF( InitSymbols::DESTROY
);
886 case FormulaLanguage::ENGLISH
:
887 InitSymbolsEnglish( InitSymbols::DESTROY
);
889 case FormulaLanguage::NATIVE
:
890 InitSymbolsNative( InitSymbols::DESTROY
);
892 case FormulaLanguage::XL_ENGLISH
:
893 InitSymbolsEnglishXL( InitSymbols::DESTROY
);
895 case FormulaLanguage::OOXML
:
896 InitSymbolsOOXML( InitSymbols::DESTROY
);
898 case FormulaLanguage::API
:
899 InitSymbolsAPI( InitSymbols::DESTROY
);
906 bool FormulaCompiler::HasOpCodeMap( const sal_Int32 nLanguage
) const
908 using namespace sheet
;
911 case FormulaLanguage::ODFF
:
912 return InitSymbolsODFF( InitSymbols::ASK
);
913 case FormulaLanguage::ODF_11
:
914 return InitSymbolsPODF( InitSymbols::ASK
);
915 case FormulaLanguage::ENGLISH
:
916 return InitSymbolsEnglish( InitSymbols::ASK
);
917 case FormulaLanguage::NATIVE
:
918 return InitSymbolsNative( InitSymbols::ASK
);
919 case FormulaLanguage::XL_ENGLISH
:
920 return InitSymbolsEnglishXL( InitSymbols::ASK
);
921 case FormulaLanguage::OOXML
:
922 return InitSymbolsOOXML( InitSymbols::ASK
);
923 case FormulaLanguage::API
:
924 return InitSymbolsAPI( InitSymbols::ASK
);
931 OUString
FormulaCompiler::FindAddInFunction( const OUString
& /*rUpperName*/, bool /*bLocalFirst*/ ) const
936 FormulaCompiler::OpCodeMapPtr
FormulaCompiler::CreateOpCodeMap(
938 const sheet::FormulaOpCodeMapEntry
> & rMapping
,
941 using sheet::FormulaOpCodeMapEntry
;
942 // Filter / API maps are never Core
943 NonConstOpCodeMapPtr xMap
= std::make_shared
<OpCodeMap
>( SC_OPCODE_LAST_OPCODE_ID
+ 1, false,
944 FormulaGrammar::mergeToGrammar( FormulaGrammar::setEnglishBit(
945 FormulaGrammar::GRAM_EXTERNAL
, bEnglish
), FormulaGrammar::CONV_UNSPECIFIED
));
946 std::unique_ptr
<CharClass
> xCharClass( xMap
->isEnglish() ? nullptr : createCharClassIfNonEnglishUI());
947 const CharClass
* pCharClass
= xCharClass
.get();
948 for (auto const& rMapEntry
: rMapping
)
950 OpCode eOp
= OpCode(rMapEntry
.Token
.OpCode
);
951 if (eOp
!= ocExternal
)
952 xMap
->putOpCode( rMapEntry
.Name
, eOp
, pCharClass
);
955 OUString aExternalName
;
956 if (rMapEntry
.Token
.Data
>>= aExternalName
)
957 xMap
->putExternal( rMapEntry
.Name
, aExternalName
);
960 SAL_WARN( "formula.core", "FormulaCompiler::CreateOpCodeMap: no Token.Data external name");
967 static bool lcl_fillNativeSymbols( FormulaCompiler::NonConstOpCodeMapPtr
& xMap
, FormulaCompiler::InitSymbols eWhat
= FormulaCompiler::InitSymbols::INIT
)
969 static OpCodeMapData aSymbolMap
;
970 static std::map
<OUString
, OpCodeMapData
> aLocaleSymbolMap
;
971 std::unique_lock
aGuard(aSymbolMap
.maMtx
);
973 if (comphelper::LibreOfficeKit::isActive())
975 OUString language
= comphelper::LibreOfficeKit::getLanguageTag().getLanguage();
976 if (eWhat
== FormulaCompiler::InitSymbols::ASK
)
978 return aLocaleSymbolMap
.contains(language
)
979 && bool(aLocaleSymbolMap
[language
].mxSymbolMap
);
981 else if (eWhat
== FormulaCompiler::InitSymbols::DESTROY
)
983 aLocaleSymbolMap
[language
].mxSymbolMap
.reset();
985 else if (!aLocaleSymbolMap
[language
].mxSymbolMap
)
988 aLocaleSymbolMap
[language
].mxSymbolMap
= std::make_shared
<FormulaCompiler::OpCodeMap
>(
989 SC_OPCODE_LAST_OPCODE_ID
+ 1, true, FormulaGrammar::GRAM_NATIVE_UI
);
990 OpCodeList
aOpCodeListSymbols(RID_STRLIST_FUNCTION_NAMES_SYMBOLS
,
991 aLocaleSymbolMap
[language
].mxSymbolMap
);
992 OpCodeList
aOpCodeListNative(RID_STRLIST_FUNCTION_NAMES
,
993 aLocaleSymbolMap
[language
].mxSymbolMap
);
994 // No AddInMap for native core mapping.
997 xMap
= aLocaleSymbolMap
[language
].mxSymbolMap
;
1001 if (eWhat
== FormulaCompiler::InitSymbols::ASK
)
1003 return bool(aSymbolMap
.mxSymbolMap
);
1005 else if (eWhat
== FormulaCompiler::InitSymbols::DESTROY
)
1007 aSymbolMap
.mxSymbolMap
.reset();
1009 else if (!aSymbolMap
.mxSymbolMap
)
1012 aSymbolMap
.mxSymbolMap
= std::make_shared
<FormulaCompiler::OpCodeMap
>(
1013 SC_OPCODE_LAST_OPCODE_ID
+ 1, true, FormulaGrammar::GRAM_NATIVE_UI
);
1014 OpCodeList
aOpCodeListSymbols(RID_STRLIST_FUNCTION_NAMES_SYMBOLS
,
1015 aSymbolMap
.mxSymbolMap
);
1016 OpCodeList
aOpCodeListNative(RID_STRLIST_FUNCTION_NAMES
, aSymbolMap
.mxSymbolMap
);
1017 // No AddInMap for native core mapping.
1020 xMap
= aSymbolMap
.mxSymbolMap
;
1026 const OUString
& FormulaCompiler::GetNativeSymbol( OpCode eOp
)
1028 NonConstOpCodeMapPtr xSymbolsNative
;
1029 lcl_fillNativeSymbols( xSymbolsNative
);
1030 return xSymbolsNative
->getSymbol( eOp
);
1033 sal_Unicode
FormulaCompiler::GetNativeSymbolChar( OpCode eOp
)
1035 return GetNativeSymbol(eOp
)[0];
1038 bool FormulaCompiler::InitSymbolsNative( FormulaCompiler::InitSymbols eWhat
) const
1040 return lcl_fillNativeSymbols( mxSymbolsNative
, eWhat
);
1043 bool FormulaCompiler::InitSymbolsEnglish( FormulaCompiler::InitSymbols eWhat
) const
1045 static OpCodeMapData aMap
;
1046 std::unique_lock
aGuard(aMap
.maMtx
);
1047 if (eWhat
== InitSymbols::ASK
)
1048 return bool(aMap
.mxSymbolMap
);
1049 else if (eWhat
== InitSymbols::DESTROY
)
1050 aMap
.mxSymbolMap
.reset();
1051 else if (!aMap
.mxSymbolMap
)
1052 loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH
, FormulaGrammar::GRAM_ENGLISH
, aMap
.mxSymbolMap
);
1053 mxSymbolsEnglish
= aMap
.mxSymbolMap
;
1057 bool FormulaCompiler::InitSymbolsPODF( FormulaCompiler::InitSymbols eWhat
) const
1059 static OpCodeMapData aMap
;
1060 std::unique_lock
aGuard(aMap
.maMtx
);
1061 if (eWhat
== InitSymbols::ASK
)
1062 return bool(aMap
.mxSymbolMap
);
1063 else if (eWhat
== InitSymbols::DESTROY
)
1064 aMap
.mxSymbolMap
.reset();
1065 else if (!aMap
.mxSymbolMap
)
1066 loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH_PODF
, FormulaGrammar::GRAM_PODF
, aMap
.mxSymbolMap
, SeparatorType::RESOURCE_BASE
);
1067 mxSymbolsPODF
= aMap
.mxSymbolMap
;
1071 bool FormulaCompiler::InitSymbolsAPI( FormulaCompiler::InitSymbols eWhat
) const
1073 static OpCodeMapData aMap
;
1074 std::unique_lock
aGuard(aMap
.maMtx
);
1075 if (eWhat
== InitSymbols::ASK
)
1076 return bool(aMap
.mxSymbolMap
);
1077 else if (eWhat
== InitSymbols::DESTROY
)
1078 aMap
.mxSymbolMap
.reset();
1079 else if (!aMap
.mxSymbolMap
)
1080 loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH_API
, FormulaGrammar::GRAM_API
, aMap
.mxSymbolMap
, SeparatorType::RESOURCE_BASE
);
1081 mxSymbolsAPI
= aMap
.mxSymbolMap
;
1085 bool FormulaCompiler::InitSymbolsODFF( FormulaCompiler::InitSymbols eWhat
) const
1087 static OpCodeMapData aMap
;
1088 std::unique_lock
aGuard(aMap
.maMtx
);
1089 if (eWhat
== InitSymbols::ASK
)
1090 return bool(aMap
.mxSymbolMap
);
1091 else if (eWhat
== InitSymbols::DESTROY
)
1092 aMap
.mxSymbolMap
.reset();
1093 else if (!aMap
.mxSymbolMap
)
1094 loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH_ODFF
, FormulaGrammar::GRAM_ODFF
, aMap
.mxSymbolMap
, SeparatorType::RESOURCE_BASE
);
1095 mxSymbolsODFF
= aMap
.mxSymbolMap
;
1099 bool FormulaCompiler::InitSymbolsEnglishXL( FormulaCompiler::InitSymbols eWhat
) const
1101 static OpCodeMapData aMap
;
1102 std::unique_lock
aGuard(aMap
.maMtx
);
1103 if (eWhat
== InitSymbols::ASK
)
1104 return bool(aMap
.mxSymbolMap
);
1105 else if (eWhat
== InitSymbols::DESTROY
)
1106 aMap
.mxSymbolMap
.reset();
1107 else if (!aMap
.mxSymbolMap
)
1108 loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH
, FormulaGrammar::GRAM_ENGLISH
, aMap
.mxSymbolMap
);
1109 mxSymbolsEnglishXL
= aMap
.mxSymbolMap
;
1110 if (eWhat
!= InitSymbols::INIT
)
1113 // TODO: For now, just replace the separators to the Excel English
1114 // variants. Later, if we want to properly map Excel functions with Calc
1115 // functions, we'll need to do a little more work here.
1116 mxSymbolsEnglishXL
->putOpCode( OUString(','), ocSep
, nullptr);
1117 mxSymbolsEnglishXL
->putOpCode( OUString(','), ocArrayColSep
, nullptr);
1118 mxSymbolsEnglishXL
->putOpCode( OUString(';'), ocArrayRowSep
, nullptr);
1123 bool FormulaCompiler::InitSymbolsOOXML( FormulaCompiler::InitSymbols eWhat
) const
1125 static OpCodeMapData aMap
;
1126 std::unique_lock
aGuard(aMap
.maMtx
);
1127 if (eWhat
== InitSymbols::ASK
)
1128 return bool(aMap
.mxSymbolMap
);
1129 else if (eWhat
== InitSymbols::DESTROY
)
1130 aMap
.mxSymbolMap
.reset();
1131 else if (!aMap
.mxSymbolMap
)
1132 loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH_OOXML
, FormulaGrammar::GRAM_OOXML
, aMap
.mxSymbolMap
, SeparatorType::RESOURCE_BASE
);
1133 mxSymbolsOOXML
= aMap
.mxSymbolMap
;
1138 void FormulaCompiler::loadSymbols(const std::pair
<const char*, int>* pSymbols
, FormulaGrammar::Grammar eGrammar
,
1139 NonConstOpCodeMapPtr
& rxMap
, SeparatorType eSepType
) const
1145 rxMap
= std::make_shared
<OpCodeMap
>( SC_OPCODE_LAST_OPCODE_ID
+ 1, eGrammar
!= FormulaGrammar::GRAM_ODFF
, eGrammar
);
1146 OpCodeList
aOpCodeList(pSymbols
, rxMap
, eSepType
);
1148 fillFromAddInMap( rxMap
, eGrammar
);
1149 // Fill from collection for AddIns not already present.
1150 if (FormulaGrammar::GRAM_ENGLISH
== eGrammar
)
1151 fillFromAddInCollectionEnglishName( rxMap
);
1154 fillFromAddInCollectionUpperName( rxMap
);
1155 if (FormulaGrammar::GRAM_API
== eGrammar
)
1157 // Add known but not in AddInMap English names, e.g. from the
1158 // PricingFunctions AddIn or any user supplied AddIn.
1159 fillFromAddInCollectionEnglishName( rxMap
);
1161 else if (FormulaGrammar::GRAM_OOXML
== eGrammar
)
1163 // Add specified Add-In compatibility name.
1164 fillFromAddInCollectionExcelName( rxMap
);
1169 void FormulaCompiler::fillFromAddInCollectionUpperName( const NonConstOpCodeMapPtr
& /*xMap */) const
1173 void FormulaCompiler::fillFromAddInCollectionEnglishName( const NonConstOpCodeMapPtr
& /*xMap */) const
1177 void FormulaCompiler::fillFromAddInCollectionExcelName( const NonConstOpCodeMapPtr
& /*xMap */) const
1181 void FormulaCompiler::fillFromAddInMap( const NonConstOpCodeMapPtr
& /*xMap*/, FormulaGrammar::Grammar
/*_eGrammar */) const
1185 OpCode
FormulaCompiler::GetEnglishOpCode( const OUString
& rName
) const
1187 FormulaCompiler::OpCodeMapPtr xMap
= GetOpCodeMap( sheet::FormulaLanguage::ENGLISH
);
1189 formula::OpCodeHashMap::const_iterator
iLook( xMap
->getHashMap().find( rName
) );
1190 bool bFound
= (iLook
!= xMap
->getHashMap().end());
1191 return bFound
? (*iLook
).second
: ocNone
;
1194 bool FormulaCompiler::IsOpCodeVolatile( OpCode eOp
)
1206 // more than one parameters:
1207 // ocIndirect otherwise would have to do
1208 // StopListening and StartListening on a reference for every
1209 // interpreted value.
1211 // ocOffset results in indirect references.
1213 // ocDebugVar shows internal value that may change as the internal state changes.
1215 // ocRandArray is a volatile function.
1226 bool FormulaCompiler::IsOpCodeJumpCommand( OpCode eOp
)
1242 // Remove quotes, escaped quotes are unescaped.
1243 bool FormulaCompiler::DeQuote( OUString
& rStr
)
1245 sal_Int32 nLen
= rStr
.getLength();
1246 if ( nLen
> 1 && rStr
[0] == '\'' && rStr
[ nLen
-1 ] == '\'' )
1248 rStr
= rStr
.copy( 1, nLen
-2 );
1249 rStr
= rStr
.replaceAll( "''", "'" );
1255 void FormulaCompiler::fillAddInToken(
1256 ::std::vector
< sheet::FormulaOpCodeMapEntry
>& /*_rVec*/,
1257 bool /*_bIsEnglish*/) const
1261 bool FormulaCompiler::IsMatrixFunction( OpCode eOpCode
)
1271 case ocMatSequence
:
1276 case ocModalValue_Multi
:
1287 // added to avoid warnings
1294 void FormulaCompiler::OpCodeMap::putCopyOpCode( const OUString
& rSymbol
, OpCode eOp
, const CharClass
* pCharClass
)
1296 SAL_WARN_IF( !mpTable
[eOp
].isEmpty() && rSymbol
.isEmpty(), "formula.core",
1297 "OpCodeMap::putCopyOpCode: NOT replacing OpCode " << static_cast<sal_uInt16
>(eOp
)
1298 << " '" << mpTable
[eOp
] << "' with empty name!");
1299 if (!mpTable
[eOp
].isEmpty() && rSymbol
.isEmpty())
1301 OUString
aUpper( pCharClass
? pCharClass
->uppercase( mpTable
[eOp
]) : mpTable
[eOp
].toAsciiUpperCase());
1302 maHashMap
.emplace(aUpper
, eOp
);
1306 OUString
aUpper( pCharClass
? pCharClass
->uppercase( rSymbol
) : rSymbol
.toAsciiUpperCase());
1307 mpTable
[eOp
] = rSymbol
;
1308 maHashMap
.emplace(aUpper
, eOp
);
1312 void FormulaCompiler::OpCodeMap::copyFrom( const OpCodeMap
& r
)
1314 maHashMap
= OpCodeHashMap( mnSymbols
);
1316 sal_uInt16 n
= r
.getSymbolCount();
1317 SAL_WARN_IF( n
!= mnSymbols
, "formula.core",
1318 "OpCodeMap::copyFrom: unequal size, this: " << mnSymbols
<< " that: " << n
);
1322 // OpCode 0 (ocPush) should never be in a map.
1323 SAL_WARN_IF( !mpTable
[0].isEmpty() || !r
.mpTable
[0].isEmpty(), "formula.core",
1324 "OpCodeMap::copyFrom: OpCode 0 assigned, this: '"
1325 << mpTable
[0] << "' that: '" << r
.mpTable
[0] << "'");
1327 std::unique_ptr
<CharClass
> xCharClass( r
.mbEnglish
? nullptr : createCharClassIfNonEnglishUI());
1328 const CharClass
* pCharClass
= xCharClass
.get();
1330 // For bOverrideKnownBad when copying from the English core map (ODF 1.1
1331 // and API) to the native map (UI "use English function names") replace the
1332 // known bad legacy function names with correct ones.
1334 FormulaGrammar::extractFormulaLanguage( meGrammar
) == sheet::FormulaLanguage::NATIVE
&&
1335 FormulaGrammar::extractFormulaLanguage( r
.meGrammar
) == sheet::FormulaLanguage::ENGLISH
)
1337 for (sal_uInt16 i
= 1; i
< n
; ++i
)
1340 OpCode eOp
= OpCode(i
);
1347 aSymbol
= "MULTIPLE.OPERATIONS";
1350 aSymbol
= r
.mpTable
[i
];
1352 putCopyOpCode( aSymbol
, eOp
, pCharClass
);
1357 for (sal_uInt16 i
= 1; i
< n
; ++i
)
1359 OpCode eOp
= OpCode(i
);
1360 const OUString
& rSymbol
= r
.mpTable
[i
];
1361 putCopyOpCode( rSymbol
, eOp
, pCharClass
);
1365 // This was meant to copy to native map that does not have AddIn symbols
1366 // but needs them from the source map. It is unclear what should happen if
1367 // the destination already had externals, so do it only if it doesn't.
1368 if (!hasExternals())
1370 maExternalHashMap
= r
.maExternalHashMap
;
1371 maReverseExternalHashMap
= r
.maReverseExternalHashMap
;
1373 if (mbEnglish
!= r
.mbEnglish
)
1375 // For now keep mbEnglishLocale setting, which is false for a
1376 // non-English native map we're copying to.
1378 if (!mbEnglish && r.mbEnglish)
1379 mbEnglishLocale = "getUseEnglishLocaleFromConfiguration()";
1380 or set from outside i.e. via ScCompiler.
1382 mbEnglish
= r
.mbEnglish
;
1388 FormulaError
FormulaCompiler::GetErrorConstant( const OUString
& rName
) const
1390 FormulaError nError
= FormulaError::NONE
;
1391 OpCodeHashMap::const_iterator
iLook( mxSymbols
->getHashMap().find( rName
));
1392 if (iLook
!= mxSymbols
->getHashMap().end())
1394 switch ((*iLook
).second
)
1396 // Not all may make sense in a formula, but these we know as
1399 nError
= FormulaError::NoCode
;
1402 nError
= FormulaError::DivisionByZero
;
1405 nError
= FormulaError::NoValue
;
1408 nError
= FormulaError::NoRef
;
1411 nError
= FormulaError::NoName
;
1414 nError
= FormulaError::IllegalFPOperation
;
1417 nError
= FormulaError::NotAvailable
;
1425 // Per convention recognize detailed "#ERRxxx!" constants, always
1426 // untranslated. Error numbers are sal_uInt16 so at most 5 decimal
1428 if (rName
.startsWithIgnoreAsciiCase("#ERR") && rName
.getLength() <= 10 && rName
[rName
.getLength()-1] == '!')
1430 sal_uInt32 nErr
= o3tl::toUInt32(rName
.subView( 4, rName
.getLength() - 5));
1431 if (0 < nErr
&& nErr
<= SAL_MAX_UINT16
&& isPublishedFormulaError(static_cast<FormulaError
>(nErr
)))
1432 nError
= static_cast<FormulaError
>(nErr
);
1438 void FormulaCompiler::EnableJumpCommandReorder( bool bEnable
)
1440 mbJumpCommandReorder
= bEnable
;
1443 void FormulaCompiler::EnableStopOnError( bool bEnable
)
1445 mbStopOnError
= bEnable
;
1448 void FormulaCompiler::AppendErrorConstant( OUStringBuffer
& rBuffer
, FormulaError nError
) const
1453 case FormulaError::NoCode
:
1456 case FormulaError::DivisionByZero
:
1459 case FormulaError::NoValue
:
1462 case FormulaError::NoRef
:
1465 case FormulaError::NoName
:
1468 case FormulaError::IllegalFPOperation
:
1471 case FormulaError::NotAvailable
:
1476 // Per convention create detailed "#ERRxxx!" constants, always
1478 rBuffer
.append("#ERR");
1479 rBuffer
.append(static_cast<sal_Int32
>(nError
));
1480 rBuffer
.append('!');
1484 rBuffer
.append( mxSymbols
->getSymbol( eOp
));
1487 constexpr short nRecursionMax
= 100;
1489 bool FormulaCompiler::GetToken()
1491 FormulaCompilerRecursionGuard
aRecursionGuard( nRecursion
);
1492 if ( nRecursion
> nRecursionMax
)
1494 SetError( FormulaError::StackOverflow
);
1495 mpLastToken
= mpToken
= new FormulaByteToken( ocStop
);
1498 if ( bAutoCorrect
&& !pStack
)
1499 { // don't merge stacked subroutine code into entered formula
1500 aCorrectedFormula
+= aCorrectedSymbol
;
1501 aCorrectedSymbol
.clear();
1504 if (pArr
->GetCodeError() != FormulaError::NONE
&& mbStopOnError
)
1508 FormulaTokenRef pSpacesToken
;
1509 short nWasColRowName
;
1510 if ( pArr
->OpCodeBefore( maArrIterator
.GetIndex() ) == ocColRowName
)
1515 mpToken
= maArrIterator
.Next();
1516 while (mpToken
&& ((eTmpOp
= mpToken
->GetOpCode()) == ocSpaces
|| eTmpOp
== ocWhitespace
))
1518 if (eTmpOp
== ocSpaces
)
1520 // For significant whitespace remember last ocSpaces token.
1521 // Usually there's only one even for multiple spaces.
1522 pSpacesToken
= mpToken
;
1523 if ( nWasColRowName
)
1526 if ( bAutoCorrect
&& !pStack
)
1527 CreateStringFromToken( aCorrectedFormula
, mpToken
.get() );
1528 mpToken
= maArrIterator
.Next();
1530 if ( bAutoCorrect
&& !pStack
&& mpToken
)
1531 CreateStringFromToken( aCorrectedSymbol
, mpToken
.get() );
1537 // mpLastToken was popped as well and corresponds to the
1538 // then current last token during PushTokenArray(), e.g. for
1547 if ( nWasColRowName
>= 2 && mpToken
->GetOpCode() == ocColRowName
)
1548 { // convert an ocSpaces to ocIntersect in RPN
1549 mpLastToken
= mpToken
= new FormulaByteToken( ocIntersect
);
1550 maArrIterator
.StepBack(); // we advanced to the second ocColRowName, step back
1552 else if (pSpacesToken
&& FormulaGrammar::isExcelSyntax( meGrammar
) &&
1553 mpLastToken
&& mpToken
&&
1554 isPotentialRangeType( mpToken
.get(), false, true) &&
1555 (mpLastToken
->GetOpCode() == ocClose
|| isPotentialRangeType( mpLastToken
.get(), false, false)))
1557 // Let IntersectionLine() <- Factor() decide how to treat this,
1558 // once the actual arguments are determined in RPN.
1559 mpLastToken
= mpToken
= std::move(pSpacesToken
);
1560 maArrIterator
.StepBack(); // step back from next non-spaces token
1567 mpLastToken
= mpToken
= new FormulaByteToken( ocStop
);
1571 // Remember token for next round and any PushTokenArray() calls that may
1572 // occur in handlers.
1573 mpLastToken
= mpToken
;
1575 if ( mpToken
->IsExternalRef() )
1577 return HandleExternalReference(*mpToken
);
1581 switch (mpToken
->GetOpCode())
1588 if( HandleStringName())
1595 // Expanding ocName might have introduced tokens such as ocStyle that prevent formula threading,
1596 // but those wouldn't be present in the raw tokens array, so ensure RPN tokens will be checked too.
1597 needsRPNTokenCheck
= true;
1602 return HandleColRowName();
1604 return HandleDbData();
1606 return HandleTableRef();
1609 HandleIIOpCode(mpToken
.get(), nullptr, 0);
1619 // RPN creation by recursion
1620 void FormulaCompiler::Factor()
1622 if (pArr
->GetCodeError() != FormulaError::NONE
&& mbStopOnError
)
1625 CurrentFactor
pFacToken( this );
1627 OpCode eOp
= mpToken
->GetOpCode();
1628 if (eOp
== ocPush
|| eOp
== ocColRowNameAuto
|| eOp
== ocMatRef
|| eOp
== ocDBArea
1629 || eOp
== ocTableRef
1630 || (!mbJumpCommandReorder
&& ((eOp
== ocName
) || (eOp
== ocColRowName
) || (eOp
== ocBad
)))
1637 // PUSH( is an error that may be caused by an unknown function.
1639 ( mpToken
->GetType() == svString
1640 || mpToken
->GetType() == svSingleRef
)
1641 ? FormulaError::NoName
: FormulaError::OperatorExpected
);
1642 if ( bAutoCorrect
&& !pStack
)
1643 { // assume multiplication
1644 aCorrectedFormula
+= mxSymbols
->getSymbol( ocMul
);
1648 if( eOp
!= ocClose
)
1649 SetError( FormulaError::PairExpected
);
1655 else if( eOp
== ocOpen
)
1659 while ((eOp
== ocSep
) && (pArr
->GetCodeError() == FormulaError::NONE
|| !mbStopOnError
))
1660 { // range list (A1;A2) converted to (A1~A2)
1661 pFacToken
= mpToken
;
1663 CheckSetForceArrayParameter( mpToken
, 0);
1665 // Do not ignore error here, regardless of mbStopOnError, to not
1666 // change the formula expression in case of an unexpected state.
1667 if (pArr
->GetCodeError() == FormulaError::NONE
&& pc
>= 2)
1669 // Left and right operands must be reference or function
1670 // returning reference to form a range list.
1671 const FormulaToken
* p
= pCode
[-2];
1672 if (p
&& isPotentialRangeType( p
, true, false))
1675 if (p
&& isPotentialRangeType( p
, true, true))
1677 pFacToken
->NewOpCode( ocUnion
, FormulaToken::PrivateAccess());
1678 // XXX NOTE: the token's eType is still svSep here!
1679 PutCode( pFacToken
);
1685 SetError( FormulaError::PairExpected
);
1689 /* TODO: if no conversion to ocUnion is involved this could collect
1690 * such expression as a list or (matrix) vector to be passed as
1691 * argument for one parameter (which in fact the ocUnion svRefList is a
1692 * special case of), which would require a new StackVar type and needed
1693 * to be handled by the interpreter for functions that could support it
1694 * (i.e. already handle VAR_ARGS or svRefList parameters). This is also
1695 * not defined by ODF.
1696 * Does Excel handle =SUM((1;2))?
1697 * As is, the interpreter catches extraneous uncalculated
1698 * subexpressions like 1 of (1;2) as error. */
1702 if( nNumFmt
== SvNumFormatType::UNDEFINED
)
1703 nNumFmt
= lcl_GetRetFormat( eOp
);
1705 if ( IsOpCodeVolatile( eOp
) )
1706 pArr
->SetExclusiveRecalcModeAlways();
1711 // Functions recalculated on every document load.
1712 // ONLOAD_LENIENT here to be able to distinguish and not
1713 // force a recalc (if not in an ALWAYS or ONLOAD_MUST
1714 // context) but keep an imported result from for example
1715 // OOXML a DDE call. Will be recalculated for ODFF.
1720 pArr
->AddRecalcMode( ScRecalcMode::ONLOAD_LENIENT
);
1722 // RANDBETWEEN() is volatile like RAND(). Other Add-In
1723 // functions may have to be recalculated or not, we don't
1724 // know, classify as ONLOAD_LENIENT.
1726 if (mpToken
->GetExternal() == "com.sun.star.sheet.addin.Analysis.getRandbetween")
1727 pArr
->SetExclusiveRecalcModeAlways();
1729 pArr
->AddRecalcMode( ScRecalcMode::ONLOAD_LENIENT
);
1731 // If the referred cell is moved the value changes.
1734 pArr
->SetRecalcModeOnRefMove();
1736 // ocCell needs recalc on move for some possible type values.
1737 // And recalc mode on load, tdf#60645
1739 pArr
->SetRecalcModeOnRefMove();
1740 pArr
->AddRecalcMode( ScRecalcMode::ONLOAD_MUST
);
1743 // Cell with hyperlink needs to be calculated on load to
1744 // get its matrix result generated.
1745 pArr
->AddRecalcMode( ScRecalcMode::ONLOAD_MUST
);
1746 pArr
->SetHyperLink( true);
1752 if (SC_OPCODE_START_NO_PAR
<= eOp
&& eOp
< SC_OPCODE_STOP_NO_PAR
)
1754 pFacToken
= mpToken
;
1758 SetError( FormulaError::PairExpected
);
1759 PutCode( pFacToken
);
1765 SetError( FormulaError::PairExpected
);
1766 PutCode( pFacToken
);
1770 else if (SC_OPCODE_START_1_PAR
<= eOp
&& eOp
< SC_OPCODE_STOP_1_PAR
)
1772 if (eOp
== ocIsoWeeknum
&& FormulaGrammar::isODFF( meGrammar
))
1774 // tdf#50950 ocIsoWeeknum can have 2 arguments when saved by older versions of Calc;
1775 // the opcode then has to be changed to ocWeek for backward compatibility
1776 pFacToken
= mpToken
;
1778 bool bNoParam
= false;
1786 CheckSetForceArrayParameter( mpToken
, 0);
1791 SetError( FormulaError::PairExpected
);
1792 sal_uInt32 nSepCount
= 0;
1793 const sal_uInt16 nSepPos
= maArrIterator
.GetIndex() - 1; // separator position, if any
1797 while ((eOp
== ocSep
) && (pArr
->GetCodeError() == FormulaError::NONE
|| !mbStopOnError
))
1800 CheckSetForceArrayParameter( mpToken
, nSepCount
);
1802 if (nSepCount
> FORMULA_MAXPARAMS
)
1803 SetError( FormulaError::CodeOverflow
);
1808 SetError( FormulaError::PairExpected
);
1811 pFacToken
->SetByte( nSepCount
);
1814 // An old mode!=1 indicates ISO week, remove argument if
1815 // literal double value and keep function. Anything else
1816 // can not be resolved, there exists no "like ISO but week
1817 // starts on Sunday" mode in WEEKNUM and for an expression
1818 // we can't determine.
1819 // Current index is nSepPos+3 if expression stops, or
1820 // nSepPos+4 if expression continues after the call because
1821 // we just called NextToken() to move away from it.
1822 if (pc
>= 2 && (maArrIterator
.GetIndex() == nSepPos
+ 3 || maArrIterator
.GetIndex() == nSepPos
+ 4) &&
1823 pArr
->TokenAt(nSepPos
+1)->GetType() == svDouble
&&
1824 pArr
->TokenAt(nSepPos
+1)->GetDouble() != 1.0 &&
1825 pArr
->TokenAt(nSepPos
+2)->GetOpCode() == ocClose
&&
1826 pArr
->RemoveToken( nSepPos
, 2) == 2)
1828 maArrIterator
.AfterRemoveToken( nSepPos
, 2);
1829 // Remove the ocPush/svDouble just removed also from
1830 // the compiler local RPN array.
1832 (*pCode
)->DecRef(); // may be dead now
1833 pFacToken
->SetByte( nSepCount
- 1 );
1837 // For the remaining two arguments cases use the
1838 // compatibility function.
1839 pFacToken
->NewOpCode( ocWeeknumOOo
, FormulaToken::PrivateAccess());
1842 PutCode( pFacToken
);
1846 // standard handling of 1-parameter opcodes
1847 pFacToken
= mpToken
;
1849 if( nNumFmt
== SvNumFormatType::UNDEFINED
&& eOp
== ocNot
)
1850 nNumFmt
= SvNumFormatType::LOGICAL
;
1854 CheckSetForceArrayParameter( mpToken
, 0);
1858 SetError( FormulaError::PairExpected
);
1860 SetError( FormulaError::PairExpected
);
1861 else if ( pArr
->GetCodeError() == FormulaError::NONE
)
1863 pFacToken
->SetByte( 1 );
1866 FormulaToken
** pArg
= pCode
- 1;
1867 HandleIIOpCode(pFacToken
, &pArg
, 1);
1870 PutCode( pFacToken
);
1874 else if ((SC_OPCODE_START_2_PAR
<= eOp
&& eOp
< SC_OPCODE_STOP_2_PAR
)
1875 || eOp
== ocExternal
1880 || ( eOp
>= ocInternalBegin
&& eOp
<= ocInternalEnd
)
1881 || (!mbJumpCommandReorder
&& IsOpCodeJumpCommand(eOp
)))
1883 pFacToken
= mpToken
;
1884 OpCode eMyLastOp
= eOp
;
1886 bool bNoParam
= false;
1887 bool bBadName
= false;
1895 CheckSetForceArrayParameter( mpToken
, 0);
1899 else if (eMyLastOp
== ocBad
)
1901 // Just a bad name, not an unknown function, no parameters, no
1902 // closing expected.
1907 SetError( FormulaError::PairExpected
);
1908 sal_uInt32 nSepCount
= 0;
1911 bool bDoIICompute
= mbComputeII
;
1912 // Array of FormulaToken double pointers to collect the parameters of II opcodes.
1913 FormulaToken
*** pArgArray
= nullptr;
1916 pArgArray
= static_cast<FormulaToken
***>(alloca(sizeof(FormulaToken
**)*FORMULA_MAXPARAMSII
));
1918 bDoIICompute
= false;
1924 pArgArray
[nSepCount
-1] = pCode
- 1; // Add first argument
1926 while ((eOp
== ocSep
) && (pArr
->GetCodeError() == FormulaError::NONE
|| !mbStopOnError
))
1929 CheckSetForceArrayParameter( mpToken
, nSepCount
);
1931 if (nSepCount
> FORMULA_MAXPARAMS
)
1932 SetError( FormulaError::CodeOverflow
);
1934 if (bDoIICompute
&& nSepCount
<= FORMULA_MAXPARAMSII
)
1935 pArgArray
[nSepCount
- 1] = pCode
- 1; // Add rest of the arguments
1938 HandleIIOpCode(pFacToken
, pArgArray
,
1939 std::min(nSepCount
, static_cast<sal_uInt32
>(FORMULA_MAXPARAMSII
)));
1943 ; // nothing, keep current token for return
1944 else if (eOp
!= ocClose
)
1945 SetError( FormulaError::PairExpected
);
1951 // Jumps are just normal functions for the FunctionAutoPilot tree view
1952 if (!mbJumpCommandReorder
&& pFacToken
->GetType() == svJump
)
1953 pFacToken
= new FormulaFAPToken( pFacToken
->GetOpCode(), nSepCount
, pFacToken
);
1955 pFacToken
->SetByte( nSepCount
);
1956 PutCode( pFacToken
);
1961 else if (IsOpCodeJumpCommand(eOp
))
1963 // the PC counters are -1
1964 pFacToken
= mpToken
;
1968 pFacToken
->GetJump()[ 0 ] = 3; // if, else, behind
1971 pFacToken
->GetJump()[ 0 ] = FORMULA_MAXJUMPCOUNT
+ 1;
1974 pFacToken
->GetJump()[ 0 ] = FORMULA_MAXPARAMS
+ 1;
1978 pFacToken
->GetJump()[ 0 ] = 2; // if, behind
1981 SAL_WARN("formula.core","Jump OpCode: " << +eOp
);
1982 assert(!"FormulaCompiler::Factor: someone forgot to add a jump count case");
1988 CheckSetForceArrayParameter( mpToken
, 0);
1992 SetError( FormulaError::PairExpected
);
1993 PutCode( pFacToken
);
1994 // During AutoCorrect (since pArr->GetCodeError() is
1995 // ignored) an unlimited ocIf would crash because
1996 // ScRawToken::Clone() allocates the JumpBuffer according to
1997 // nJump[0]*2+2, which is 3*2+2 on ocIf and 2*2+2 ocIfError and ocIfNA.
1999 OpCode eFacOpCode
= pFacToken
->GetOpCode();
2006 nJumpMax
= FORMULA_MAXJUMPCOUNT
;
2009 nJumpMax
= FORMULA_MAXPARAMS
;
2016 // May happen only if PutCode(pFacToken) ran into overflow.
2018 assert(pc
== FORMULA_MAXTOKENS
&& pArr
->GetCodeError() != FormulaError::NONE
);
2022 SAL_WARN("formula.core","Jump OpCode: " << +eFacOpCode
);
2023 assert(!"FormulaCompiler::Factor: someone forgot to add a jump max case");
2025 short nJumpCount
= 0;
2026 while ( (nJumpCount
< (FORMULA_MAXPARAMS
- 1)) && (eOp
== ocSep
)
2027 && (pArr
->GetCodeError() == FormulaError::NONE
|| !mbStopOnError
))
2029 if ( ++nJumpCount
<= nJumpMax
)
2030 pFacToken
->GetJump()[nJumpCount
] = pc
-1;
2032 CheckSetForceArrayParameter( mpToken
, nJumpCount
- 1);
2034 // ocSep or ocClose terminate the subexpression
2038 SetError( FormulaError::PairExpected
);
2042 // always limit to nJumpMax, no arbitrary overwrites
2043 if ( ++nJumpCount
<= nJumpMax
)
2044 pFacToken
->GetJump()[ nJumpCount
] = pc
-1;
2045 eFacOpCode
= pFacToken
->GetOpCode();
2050 bLimitOk
= (nJumpCount
<= 3);
2053 bLimitOk
= (nJumpCount
< FORMULA_MAXJUMPCOUNT
);
2056 bLimitOk
= (nJumpCount
< FORMULA_MAXPARAMS
);
2060 bLimitOk
= (nJumpCount
<= 2);
2063 // May happen only if PutCode(pFacToken) ran into overflow.
2064 // This may had resulted from a stacked token array and
2065 // error wasn't propagated so assert only the program
2068 assert(pc
== FORMULA_MAXTOKENS
);
2072 SAL_WARN("formula.core","Jump OpCode: " << +eFacOpCode
);
2073 assert(!"FormulaCompiler::Factor: someone forgot to add a jump limit case");
2076 pFacToken
->GetJump()[ 0 ] = nJumpCount
;
2078 SetError( FormulaError::IllegalParameter
);
2081 else if ( eOp
== ocMissing
)
2086 else if ( eOp
== ocClose
)
2088 SetError( FormulaError::ParameterExpected
);
2090 else if ( eOp
== ocSep
)
2091 { // Subsequent ocSep
2092 SetError( FormulaError::ParameterExpected
);
2093 if ( bAutoCorrect
&& !pStack
)
2095 aCorrectedSymbol
.clear();
2099 else if ( mpToken
->IsExternalRef() )
2106 SetError( FormulaError::UnknownToken
);
2107 if ( bAutoCorrect
&& !pStack
)
2109 if ( eOp
== ocStop
)
2110 { // trailing operator w/o operand
2111 sal_Int32 nLen
= aCorrectedFormula
.getLength();
2113 aCorrectedFormula
= aCorrectedFormula
.copy( 0, nLen
- 1 );
2114 aCorrectedSymbol
.clear();
2122 void FormulaCompiler::RangeLine()
2125 while (mpToken
->GetOpCode() == ocRange
)
2127 FormulaToken
** pCode1
= pCode
- 1;
2128 FormulaTokenRef p
= mpToken
;
2131 FormulaToken
** pCode2
= pCode
- 1;
2132 if (!MergeRangeReference( pCode1
, pCode2
))
2137 void FormulaCompiler::IntersectionLine()
2140 while (mpToken
->GetOpCode() == ocIntersect
|| mpToken
->GetOpCode() == ocSpaces
)
2142 sal_uInt16 nCodeIndex
= maArrIterator
.GetIndex() - 1;
2143 FormulaToken
** pCode1
= pCode
- 1;
2144 FormulaTokenRef p
= mpToken
;
2147 FormulaToken
** pCode2
= pCode
- 1;
2148 if (p
->GetOpCode() == ocSpaces
)
2150 // Convert to intersection if both left and right are references or
2151 // functions (potentially returning references, if not then a space
2152 // or no space would be a syntax error anyway), not other operators
2153 // or operands. Else discard.
2154 if (isAdjacentOrGapRpnEnd( pc
, pCode
, pCode1
, pCode2
) && isIntersectable( pCode1
, pCode2
))
2156 FormulaTokenRef
pIntersect( new FormulaByteToken( ocIntersect
));
2157 // Replace ocSpaces with ocIntersect so that when switching
2158 // formula syntax the correct operator string is created.
2159 // coverity[freed_arg : FALSE] - FormulaTokenRef has a ref so ReplaceToken won't delete pIntersect
2160 pArr
->ReplaceToken( nCodeIndex
, pIntersect
.get(), FormulaTokenArray::ReplaceMode::CODE_ONLY
);
2161 PutCode( pIntersect
);
2171 void FormulaCompiler::UnionLine()
2174 while (mpToken
->GetOpCode() == ocUnion
)
2176 FormulaTokenRef p
= mpToken
;
2183 void FormulaCompiler::UnaryLine()
2185 if( mpToken
->GetOpCode() == ocAdd
)
2187 else if (SC_OPCODE_START_UN_OP
<= mpToken
->GetOpCode() &&
2188 mpToken
->GetOpCode() < SC_OPCODE_STOP_UN_OP
)
2190 FormulaTokenRef p
= mpToken
;
2195 FormulaToken
** pArg
= pCode
- 1;
2196 HandleIIOpCode(p
.get(), &pArg
, 1);
2204 void FormulaCompiler::PostOpLine()
2207 while ( mpToken
->GetOpCode() == ocPercentSign
)
2208 { // this operator _follows_ its operand
2211 FormulaToken
** pArg
= pCode
- 1;
2212 HandleIIOpCode(mpToken
.get(), &pArg
, 1);
2219 void FormulaCompiler::PowLine()
2222 while (mpToken
->GetOpCode() == ocPow
)
2224 FormulaTokenRef p
= mpToken
;
2225 FormulaToken
** pArgArray
[2];
2227 pArgArray
[0] = pCode
- 1; // Add first argument
2232 pArgArray
[1] = pCode
- 1; // Add second argument
2233 HandleIIOpCode(p
.get(), pArgArray
, 2);
2239 void FormulaCompiler::MulDivLine()
2242 while (mpToken
->GetOpCode() == ocMul
|| mpToken
->GetOpCode() == ocDiv
)
2244 FormulaTokenRef p
= mpToken
;
2245 FormulaToken
** pArgArray
[2];
2247 pArgArray
[0] = pCode
- 1; // Add first argument
2252 pArgArray
[1] = pCode
- 1; // Add second argument
2253 HandleIIOpCode(p
.get(), pArgArray
, 2);
2259 void FormulaCompiler::AddSubLine()
2262 while (mpToken
->GetOpCode() == ocAdd
|| mpToken
->GetOpCode() == ocSub
)
2264 FormulaTokenRef p
= mpToken
;
2265 FormulaToken
** pArgArray
[2];
2267 pArgArray
[0] = pCode
- 1; // Add first argument
2272 pArgArray
[1] = pCode
- 1; // Add second argument
2273 HandleIIOpCode(p
.get(), pArgArray
, 2);
2279 void FormulaCompiler::ConcatLine()
2282 while (mpToken
->GetOpCode() == ocAmpersand
)
2284 FormulaTokenRef p
= mpToken
;
2285 FormulaToken
** pArgArray
[2];
2287 pArgArray
[0] = pCode
- 1; // Add first argument
2292 pArgArray
[1] = pCode
- 1; // Add second argument
2293 HandleIIOpCode(p
.get(), pArgArray
, 2);
2299 void FormulaCompiler::CompareLine()
2302 while (mpToken
->GetOpCode() >= ocEqual
&& mpToken
->GetOpCode() <= ocGreaterEqual
)
2304 FormulaTokenRef p
= mpToken
;
2305 FormulaToken
** pArgArray
[2];
2307 pArgArray
[0] = pCode
- 1; // Add first argument
2312 pArgArray
[1] = pCode
- 1; // Add second argument
2313 HandleIIOpCode(p
.get(), pArgArray
, 2);
2319 OpCode
FormulaCompiler::Expression()
2321 FormulaCompilerRecursionGuard
aRecursionGuard( nRecursion
);
2322 if ( nRecursion
> nRecursionMax
)
2324 SetError( FormulaError::StackOverflow
);
2325 return ocStop
; //! generate token instead?
2328 while (mpToken
->GetOpCode() == ocAnd
|| mpToken
->GetOpCode() == ocOr
)
2330 FormulaTokenRef p
= mpToken
;
2331 mpToken
->SetByte( 2 ); // 2 parameters!
2332 FormulaToken
** pArgArray
[2];
2334 pArgArray
[0] = pCode
- 1; // Add first argument
2339 pArgArray
[1] = pCode
- 1; // Add second argument
2340 HandleIIOpCode(p
.get(), pArgArray
, 2);
2344 return mpToken
->GetOpCode();
2348 void FormulaCompiler::SetError( FormulaError
/*nError*/ )
2352 FormulaTokenRef
FormulaCompiler::ExtendRangeReference( FormulaToken
& /*rTok1*/, FormulaToken
& /*rTok2*/ )
2354 return FormulaTokenRef();
2357 bool FormulaCompiler::MergeRangeReference( FormulaToken
* * const pCode1
, FormulaToken
* const * const pCode2
)
2359 if (!isAdjacentRpnEnd( pc
, pCode
, pCode1
, pCode2
))
2362 FormulaToken
*p1
= *pCode1
, *p2
= *pCode2
;
2363 FormulaTokenRef p
= ExtendRangeReference( *p1
, *p2
);
2377 bool FormulaCompiler::CompileTokenArray()
2381 needsRPNTokenCheck
= false;
2382 if (pArr
->GetCodeError() == FormulaError::NONE
|| !mbStopOnError
)
2386 aCorrectedFormula
.clear();
2387 aCorrectedSymbol
.clear();
2390 maArrIterator
.Reset();
2392 FormulaToken
* pDataArray
[ FORMULA_MAXTOKENS
+ 1 ];
2393 // Code in some places refers to the last token as 'pCode - 1', which may
2394 // point before the first element if the expression is bad. So insert a dummy
2395 // node in that place which will make that token be nullptr.
2396 pDataArray
[ 0 ] = nullptr;
2397 FormulaToken
** pData
= pDataArray
+ 1;
2399 bool bWasForced
= pArr
->IsRecalcModeForced();
2400 if ( bWasForced
&& bAutoCorrect
)
2401 aCorrectedFormula
= "=";
2402 pArr
->ClearRecalcMode();
2403 maArrIterator
.Reset();
2407 OpCode eOp
= Expression();
2408 // Some trailing garbage that doesn't form an expression?
2410 SetError( FormulaError::OperatorExpected
);
2413 FormulaError nErrorBeforePop
= pArr
->GetCodeError();
2419 pArr
->CreateNewRPNArrayFromData( pData
, pc
);
2420 if( needsRPNTokenCheck
)
2421 pArr
->CheckAllRPNTokens();
2424 // once an error, always an error
2425 if( pArr
->GetCodeError() == FormulaError::NONE
&& nErrorBeforePop
!= FormulaError::NONE
)
2426 pArr
->SetCodeError( nErrorBeforePop
);
2428 if (pArr
->GetCodeError() != FormulaError::NONE
&& mbStopOnError
)
2431 maArrIterator
.Reset();
2432 pArr
->SetHyperLink( false);
2436 pArr
->SetRecalcModeForced();
2438 if( nNumFmt
== SvNumFormatType::UNDEFINED
)
2439 nNumFmt
= SvNumFormatType::NUMBER
;
2443 void FormulaCompiler::PopTokenArray()
2448 FormulaArrayStack
* p
= pStack
;
2450 // obtain special RecalcMode from SharedFormula
2451 if ( pArr
->IsRecalcModeAlways() )
2452 p
->pArr
->SetExclusiveRecalcModeAlways();
2453 else if ( !pArr
->IsRecalcModeNormal() && p
->pArr
->IsRecalcModeNormal() )
2454 p
->pArr
->SetMaskedRecalcMode( pArr
->GetRecalcMode() );
2455 p
->pArr
->SetCombinedBitsRecalcMode( pArr
->GetRecalcMode() );
2456 if ( pArr
->IsHyperLink() ) // fdo 87534
2457 p
->pArr
->SetHyperLink( true );
2461 maArrIterator
= FormulaTokenArrayPlainIterator(*pArr
);
2462 maArrIterator
.Jump(p
->nIndex
);
2463 mpLastToken
= p
->mpLastToken
;
2467 void FormulaCompiler::CreateStringFromTokenArray( OUString
& rFormula
)
2469 OUStringBuffer
aBuffer( pArr
->GetLen() * 5 );
2470 CreateStringFromTokenArray( aBuffer
);
2471 rFormula
= aBuffer
.makeStringAndClear();
2474 void FormulaCompiler::CreateStringFromTokenArray( OUStringBuffer
& rBuffer
)
2476 rBuffer
.setLength(0);
2477 if( !pArr
->GetLen() )
2480 FormulaTokenArray
* pSaveArr
= pArr
;
2481 int nSaveIndex
= maArrIterator
.GetIndex();
2482 bool bODFF
= FormulaGrammar::isODFF( meGrammar
);
2483 if (bODFF
|| FormulaGrammar::isPODF( meGrammar
) )
2485 // Scan token array for missing args and re-write if present.
2486 MissingConventionODF
aConv( bODFF
);
2487 if (pArr
->NeedsPodfRewrite( aConv
))
2489 pArr
= pArr
->RewriteMissing( aConv
);
2490 maArrIterator
= FormulaTokenArrayPlainIterator( *pArr
);
2493 else if ( FormulaGrammar::isOOXML( meGrammar
) )
2495 // Scan token array for missing args and rewrite if present.
2496 if (pArr
->NeedsOoxmlRewrite())
2498 MissingConventionOOXML aConv
;
2499 pArr
= pArr
->RewriteMissing( aConv
);
2500 maArrIterator
= FormulaTokenArrayPlainIterator( *pArr
);
2504 // At least one character per token, plus some are references, some are
2505 // function names, some are numbers, ...
2506 rBuffer
.ensureCapacity( pArr
->GetLen() * 5 );
2508 if ( pArr
->IsRecalcModeForced() )
2509 rBuffer
.append( '=');
2510 const FormulaToken
* t
= maArrIterator
.First();
2512 t
= CreateStringFromToken( rBuffer
, t
, true );
2514 if (pSaveArr
!= pArr
)
2518 maArrIterator
= FormulaTokenArrayPlainIterator( *pArr
);
2519 maArrIterator
.Jump(nSaveIndex
);
2523 const FormulaToken
* FormulaCompiler::CreateStringFromToken( OUString
& rFormula
, const FormulaToken
* pTokenP
)
2525 OUStringBuffer aBuffer
;
2526 const FormulaToken
* p
= CreateStringFromToken( aBuffer
, pTokenP
);
2527 rFormula
+= aBuffer
;
2531 const FormulaToken
* FormulaCompiler::CreateStringFromToken( OUStringBuffer
& rBuffer
, const FormulaToken
* pTokenP
,
2532 bool bAllowArrAdvance
)
2535 bool bSpaces
= false;
2536 const FormulaToken
* t
= pTokenP
;
2537 OpCode eOp
= t
->GetOpCode();
2538 if( eOp
>= ocAnd
&& eOp
<= ocOr
)
2541 if ( bAllowArrAdvance
)
2542 t
= maArrIterator
.Next();
2544 t
= maArrIterator
.PeekNext();
2546 bSpaces
= ( !t
|| t
->GetOpCode() != ocOpen
);
2549 rBuffer
.append( ' ');
2551 if (eOp
== ocSpaces
|| eOp
== ocWhitespace
)
2553 bool bWriteSpaces
= true;
2554 if (eOp
== ocSpaces
&& mxSymbols
->isODFF())
2556 const FormulaToken
* p
= maArrIterator
.PeekPrevNoSpaces();
2557 bool bIntersectionOp
= (p
&& p
->GetOpCode() == ocColRowName
);
2558 if (bIntersectionOp
)
2560 p
= maArrIterator
.PeekNextNoSpaces();
2561 bIntersectionOp
= (p
&& p
->GetOpCode() == ocColRowName
);
2563 if (bIntersectionOp
)
2565 rBuffer
.append( "!!");
2566 bWriteSpaces
= false;
2571 // ODF v1.3 OpenFormula 5.14 Whitespace states "whitespace shall
2572 // not separate a function name from its initial opening
2575 // ECMA-376-1:2016 18.17.2 Syntax states "that no space characters
2576 // shall separate a function-name from the left parenthesis (()
2577 // that follows it." and Excel even chokes on it.
2579 // Suppress/remove it in any case also in UI, it will not be
2581 const FormulaToken
* p
= maArrIterator
.PeekPrevNoSpaces();
2582 if (p
&& p
->IsFunction())
2584 p
= maArrIterator
.PeekNextNoSpaces();
2585 if (p
&& p
->GetOpCode() == ocOpen
)
2586 bWriteSpaces
= false;
2591 // most times it's just one blank
2592 sal_uInt8 n
= t
->GetByte();
2593 for ( sal_uInt8 j
=0; j
<n
; ++j
)
2595 if (eOp
== ocWhitespace
)
2596 rBuffer
.append( t
->GetChar());
2598 rBuffer
.append( ' ');
2602 else if( eOp
>= ocInternalBegin
&& eOp
<= ocInternalEnd
)
2603 rBuffer
.appendAscii( pInternal
[ eOp
- ocInternalBegin
] );
2604 else if (eOp
== ocIntersect
)
2606 // Nasty, ugly, horrific, terrifying...
2607 if (FormulaGrammar::isExcelSyntax( meGrammar
))
2608 rBuffer
.append(' ');
2610 rBuffer
.append( mxSymbols
->getSymbol( eOp
));
2612 else if ( eOp
== ocEasterSunday
)
2614 // EASTERSUNDAY belongs to ODFF since ODF 1.4
2615 if (m_oODFSavingVersion
.has_value()
2616 && m_oODFSavingVersion
.value() >= SvtSaveOptions::ODFSVER_012
2617 && m_oODFSavingVersion
.value() < SvtSaveOptions::ODFSVER_014
)
2618 rBuffer
.append(u
"ORG.OPENOFFICE." + mxSymbols
->getSymbol(eOp
));
2620 rBuffer
.append(mxSymbols
->getSymbol(eOp
));
2622 else if( static_cast<sal_uInt16
>(eOp
) < mxSymbols
->getSymbolCount()) // Keyword:
2623 rBuffer
.append( mxSymbols
->getSymbol( eOp
));
2626 SAL_WARN( "formula.core","unknown OpCode");
2627 rBuffer
.append( GetNativeSymbol( ocErrName
));
2631 if (t
->IsExternalRef())
2633 CreateStringFromExternal( rBuffer
, pTokenP
);
2637 switch( t
->GetType() )
2640 AppendDouble( rBuffer
, t
->GetDouble() );
2644 if( eOp
== ocBad
|| eOp
== ocStringXML
|| eOp
== ocStringName
)
2645 rBuffer
.append( t
->GetString().getString());
2647 AppendString( rBuffer
, t
->GetString().getString() );
2650 CreateStringFromSingleRef( rBuffer
, t
);
2653 CreateStringFromDoubleRef( rBuffer
, t
);
2657 CreateStringFromMatrix( rBuffer
, t
);
2661 CreateStringFromIndex( rBuffer
, t
);
2662 if (t
->GetOpCode() == ocTableRef
&& bAllowArrAdvance
&& NeedsTableRefTransformation())
2664 // Suppress all TableRef related tokens, the resulting
2665 // range was written by CreateStringFromIndex().
2666 const FormulaToken
* const p
= maArrIterator
.PeekNext();
2667 if (p
&& p
->GetOpCode() == ocTableRefOpen
)
2672 t
= maArrIterator
.Next();
2676 // Switch cases correspond with those in
2677 // ScCompiler::HandleTableRef()
2678 switch (t
->GetOpCode())
2680 case ocTableRefOpen
:
2683 case ocTableRefClose
:
2686 case ocTableRefItemAll
:
2687 case ocTableRefItemHeaders
:
2688 case ocTableRefItemData
:
2689 case ocTableRefItemTotals
:
2690 case ocTableRefItemThisRow
:
2707 // mapped or translated name of AddIns
2708 OUString
aAddIn( t
->GetExternal() );
2709 bool bMapped
= mxSymbols
->isPODF(); // ODF 1.1 directly uses programmatical name
2710 if (!bMapped
&& mxSymbols
->hasExternals())
2712 if (mxSymbols
->isOOXML())
2714 // Write compatibility name, if any.
2715 if (GetExcelName( aAddIn
))
2720 ExternalHashMap::const_iterator iLook
= mxSymbols
->getReverseExternalHashMap().find( aAddIn
);
2721 if (iLook
!= mxSymbols
->getReverseExternalHashMap().end())
2723 aAddIn
= (*iLook
).second
;
2728 if (!bMapped
&& !mxSymbols
->isEnglish())
2729 LocalizeString( aAddIn
);
2730 rBuffer
.append( aAddIn
);
2734 AppendErrorConstant( rBuffer
, t
->GetError());
2743 SAL_WARN("formula.core", "FormulaCompiler::GetStringFromToken: unknown token type " << t
->GetType());
2748 rBuffer
.append( ' ');
2749 if ( bAllowArrAdvance
)
2752 t
= maArrIterator
.Next();
2759 void FormulaCompiler::AppendDouble( OUStringBuffer
& rBuffer
, double fVal
) const
2761 if ( mxSymbols
->isEnglishLocale() )
2763 ::rtl::math::doubleToUStringBuffer( rBuffer
, fVal
,
2764 rtl_math_StringFormat_Automatic
,
2765 rtl_math_DecimalPlaces_Max
, '.', true );
2769 SvtSysLocale aSysLocale
;
2770 ::rtl::math::doubleToUStringBuffer( rBuffer
, fVal
,
2771 rtl_math_StringFormat_Automatic
,
2772 rtl_math_DecimalPlaces_Max
,
2773 aSysLocale
.GetLocaleData().getNumDecimalSep()[0],
2778 void FormulaCompiler::AppendBoolean( OUStringBuffer
& rBuffer
, bool bVal
) const
2780 rBuffer
.append( mxSymbols
->getSymbol( bVal
? ocTrue
: ocFalse
) );
2783 void FormulaCompiler::AppendString( OUStringBuffer
& rBuffer
, const OUString
& rStr
)
2785 rBuffer
.append( '"');
2786 if ( lcl_UnicodeStrChr( rStr
.getStr(), '"' ) == nullptr )
2787 rBuffer
.append( rStr
);
2790 OUString aStr
= rStr
.replaceAll( "\"", "\"\"" );
2791 rBuffer
.append(aStr
);
2793 rBuffer
.append( '"');
2796 bool FormulaCompiler::NeedsTableRefTransformation() const
2798 // Currently only UI representations and OOXML export use Table structured
2799 // references. Not defined in ODFF.
2800 // Unnecessary to explicitly check for ODFF grammar as the ocTableRefOpen
2801 // symbol is not defined there.
2802 return mxSymbols
->getSymbol( ocTableRefOpen
).isEmpty() || FormulaGrammar::isPODF( meGrammar
);
2805 void FormulaCompiler::UpdateSeparatorsNative(
2806 const OUString
& rSep
, const OUString
& rArrayColSep
, const OUString
& rArrayRowSep
)
2808 NonConstOpCodeMapPtr xSymbolsNative
;
2809 lcl_fillNativeSymbols( xSymbolsNative
);
2810 xSymbolsNative
->putOpCode( rSep
, ocSep
, nullptr);
2811 xSymbolsNative
->putOpCode( rArrayColSep
, ocArrayColSep
, nullptr);
2812 xSymbolsNative
->putOpCode( rArrayRowSep
, ocArrayRowSep
, nullptr);
2815 void FormulaCompiler::ResetNativeSymbols()
2817 NonConstOpCodeMapPtr xSymbolsNative
;
2818 lcl_fillNativeSymbols( xSymbolsNative
, InitSymbols::DESTROY
);
2819 lcl_fillNativeSymbols( xSymbolsNative
);
2822 void FormulaCompiler::SetNativeSymbols( const OpCodeMapPtr
& xMap
)
2824 NonConstOpCodeMapPtr xSymbolsNative
;
2825 lcl_fillNativeSymbols( xSymbolsNative
);
2826 xSymbolsNative
->copyFrom( *xMap
);
2830 OpCode
FormulaCompiler::NextToken()
2834 OpCode eOp
= mpToken
->GetOpCode();
2835 // There must be an operator before a push
2836 if ( (eOp
== ocPush
|| eOp
== ocColRowNameAuto
) &&
2837 !( (eLastOp
== ocOpen
) || (eLastOp
== ocSep
) ||
2838 (SC_OPCODE_START_BIN_OP
<= eLastOp
&& eLastOp
< SC_OPCODE_STOP_UN_OP
)) )
2839 SetError( FormulaError::OperatorExpected
);
2840 // Operator and Plus => operator
2841 if (eOp
== ocAdd
&& (eLastOp
== ocOpen
|| eLastOp
== ocSep
||
2842 (SC_OPCODE_START_BIN_OP
<= eLastOp
&& eLastOp
< SC_OPCODE_STOP_UN_OP
)))
2844 FormulaCompilerRecursionGuard
aRecursionGuard( nRecursion
);
2849 // Before an operator there must not be another operator, with the
2850 // exception of AND and OR.
2851 if ( eOp
!= ocAnd
&& eOp
!= ocOr
&&
2852 (SC_OPCODE_START_BIN_OP
<= eOp
&& eOp
< SC_OPCODE_STOP_BIN_OP
)
2853 && (eLastOp
== ocOpen
|| eLastOp
== ocSep
||
2854 (SC_OPCODE_START_BIN_OP
<= eLastOp
&& eLastOp
< SC_OPCODE_STOP_UN_OP
)))
2856 SetError( FormulaError::VariableExpected
);
2857 if ( bAutoCorrect
&& !pStack
)
2859 if ( eOp
== eLastOp
|| eLastOp
== ocOpen
)
2860 { // throw away duplicated operator
2861 aCorrectedSymbol
.clear();
2866 sal_Int32 nPos
= aCorrectedFormula
.getLength();
2870 sal_Unicode c
= aCorrectedFormula
[ nPos
];
2874 if ( c
== mxSymbols
->getSymbolChar( ocEqual
) )
2875 { // >= instead of =>
2876 aCorrectedFormula
= aCorrectedFormula
.replaceAt( nPos
, 1,
2877 rtl::OUStringChar( mxSymbols
->getSymbolChar(ocGreater
) ) );
2878 aCorrectedSymbol
= OUString(c
);
2883 if ( c
== mxSymbols
->getSymbolChar( ocEqual
) )
2884 { // <= instead of =<
2885 aCorrectedFormula
= aCorrectedFormula
.replaceAt( nPos
, 1,
2886 rtl::OUStringChar( mxSymbols
->getSymbolChar(ocLess
) ) );
2887 aCorrectedSymbol
= OUString(c
);
2890 else if ( c
== mxSymbols
->getSymbolChar( ocGreater
) )
2891 { // <> instead of ><
2892 aCorrectedFormula
= aCorrectedFormula
.replaceAt( nPos
, 1,
2893 rtl::OUStringChar( mxSymbols
->getSymbolChar(ocLess
) ) );
2894 aCorrectedSymbol
= OUString(c
);
2899 if ( c
== mxSymbols
->getSymbolChar( ocSub
) )
2900 { // *- instead of -*
2901 aCorrectedFormula
= aCorrectedFormula
.replaceAt( nPos
, 1,
2902 rtl::OUStringChar( mxSymbols
->getSymbolChar(ocMul
) ) );
2903 aCorrectedSymbol
= OUString(c
);
2908 if ( c
== mxSymbols
->getSymbolChar( ocSub
) )
2909 { // /- instead of -/
2910 aCorrectedFormula
= aCorrectedFormula
.replaceAt( nPos
, 1,
2911 rtl::OUStringChar( mxSymbols
->getSymbolChar(ocDiv
) ) );
2912 aCorrectedSymbol
= OUString(c
);
2923 // Nasty, ugly, horrific, terrifying... significant whitespace...
2924 if (eOp
== ocSpaces
&& FormulaGrammar::isExcelSyntax( meGrammar
))
2926 // Fake an intersection op as last op for the next round, but at
2927 // least roughly check if it could make sense at all.
2928 FormulaToken
* pPrev
= maArrIterator
.PeekPrevNoSpaces();
2929 if (pPrev
&& isPotentialRangeType( pPrev
, false, false))
2931 FormulaToken
* pNext
= maArrIterator
.PeekNextNoSpaces();
2932 if (pNext
&& isPotentialRangeType( pNext
, false, true))
2933 eLastOp
= ocIntersect
;
2946 void FormulaCompiler::PutCode( FormulaTokenRef
& p
)
2948 if( pc
>= FORMULA_MAXTOKENS
- 1 )
2950 if ( pc
== FORMULA_MAXTOKENS
- 1 )
2952 SAL_WARN("formula.core", "FormulaCompiler::PutCode - CodeOverflow with OpCode " << +p
->GetOpCode());
2953 p
= new FormulaByteToken( ocStop
);
2958 SetError( FormulaError::CodeOverflow
);
2961 if (pArr
->GetCodeError() != FormulaError::NONE
&& mbJumpCommandReorder
)
2963 ForceArrayOperator( p
);
2970 bool FormulaCompiler::HandleExternalReference( const FormulaToken
& /*_aToken*/)
2975 bool FormulaCompiler::HandleStringName()
2980 bool FormulaCompiler::HandleRange()
2985 bool FormulaCompiler::HandleColRowName()
2990 bool FormulaCompiler::HandleDbData()
2995 bool FormulaCompiler::HandleTableRef()
3000 void FormulaCompiler::CreateStringFromSingleRef( OUStringBuffer
& /*rBuffer*/, const FormulaToken
* /*pToken*/) const
3004 void FormulaCompiler::CreateStringFromDoubleRef( OUStringBuffer
& /*rBuffer*/, const FormulaToken
* /*pToken*/) const
3008 void FormulaCompiler::CreateStringFromIndex( OUStringBuffer
& /*rBuffer*/, const FormulaToken
* /*pToken*/) const
3012 void FormulaCompiler::CreateStringFromMatrix( OUStringBuffer
& /*rBuffer*/, const FormulaToken
* /*pToken*/) const
3016 void FormulaCompiler::CreateStringFromExternal( OUStringBuffer
& /*rBuffer*/, const FormulaToken
* /*pToken*/) const
3020 void FormulaCompiler::LocalizeString( OUString
& /*rName*/ ) const
3024 bool FormulaCompiler::GetExcelName( OUString
& /*rName*/ ) const
3029 formula::ParamClass
FormulaCompiler::GetForceArrayParameter( const FormulaToken
* /*pToken*/, sal_uInt16
/*nParam*/ ) const
3031 return ParamClass::Unknown
;
3034 void FormulaCompiler::ForceArrayOperator( FormulaTokenRef
const & rCurr
)
3036 if (pCurrentFactorToken
.get() == rCurr
.get())
3039 const OpCode eOp
= rCurr
->GetOpCode();
3040 const StackVar eType
= rCurr
->GetType();
3041 const bool bInlineArray
= (eOp
== ocPush
&& eType
== svMatrix
);
3045 if (rCurr
->GetInForceArray() != ParamClass::Unknown
)
3046 // Already set, unnecessary to evaluate again. This happens by calls to
3047 // CurrentFactor::operator=() while descending through Factor() and
3048 // then ascending back (and down and up, ...),
3049 // CheckSetForceArrayParameter() and later PutCode().
3052 if (!(eOp
!= ocPush
&& (eType
== svByte
|| eType
== svJump
)))
3056 // Return class for inline arrays and functions returning array/matrix.
3057 // It's somewhat unclear what Excel actually does there and in
3058 // ECMA-376-1:2016 OOXML mentions "call to ... shall be an array formula"
3059 // only for FREQUENCY() and TRANSPOSE() but not for any other function
3060 // returning array/matrix or inline arrays, though for the latter has one
3061 // example in 18.17.2 Syntax:
3062 // "SUM(SQRT({1,2,3,4})) returns 6.14 when entered normally". However,
3063 // these need to be treated similar but not as ParamClass::ForceArray
3064 // (which would contradict the example in
3065 // https://bugs.documentfoundation.org/show_bug.cgi?id=122301#c19 and A6 of
3066 // https://bugs.documentfoundation.org/show_bug.cgi?id=133260#c10 ).
3068 // commit d0ded163d8e93dc5b10d7a7c9bdab1d0a6a50bac
3069 // commit 5413c8871dec08eff19f514f5f391b946a45c86c
3070 constexpr ParamClass eArrayReturn
= ParamClass::ForceArrayReturn
;
3074 // rCurr->SetInForceArray() can not be used with ocPush, but ocPush
3075 // with svMatrix has an implicit ParamClass::ForceArrayReturn.
3076 if (nCurrentFactorParam
> 0 && pCurrentFactorToken
3077 && pCurrentFactorToken
->GetInForceArray() == ParamClass::Unknown
3078 && GetForceArrayParameter( pCurrentFactorToken
.get(), static_cast<sal_uInt16
>(nCurrentFactorParam
- 1))
3079 == ParamClass::Value
)
3081 // Propagate to caller as if a function returning an array/matrix
3082 // was called (see also below).
3083 pCurrentFactorToken
->SetInForceArray( eArrayReturn
);
3088 if (!pCurrentFactorToken
)
3092 // An array/matrix formula acts as ForceArray on all top level
3093 // operators and function calls, so that can be inherited properly
3095 rCurr
->SetInForceArray( ParamClass::ForceArray
);
3097 else if (pc
>= 2 && SC_OPCODE_START_BIN_OP
<= eOp
&& eOp
< SC_OPCODE_STOP_BIN_OP
)
3099 // Binary operators are not functions followed by arguments
3100 // and need some peeking into RPN to inspect their operands.
3101 // Note that array context is not forced if only one
3102 // of the operands is an array like "={1;2}+A1:A2" returns #VALUE!
3103 // if entered in column A and not input in array mode, because it
3104 // involves a range reference with an implicit intersection. Check
3105 // both arguments are arrays, or the other is ocPush without ranges
3106 // for "={1;2}+3" or "={1;2}+A1".
3107 // Note this does not catch "={1;2}+ABS(A1)" that could be forced
3108 // to array, user still has to close in array mode.
3109 // The IsMatrixFunction() is only necessary because not all
3110 // functions returning matrix have ForceArrayReturn (yet?), see
3111 // OOXML comment above.
3113 const OpCode eOp1
= pCode
[-1]->GetOpCode();
3114 const OpCode eOp2
= pCode
[-2]->GetOpCode();
3115 const bool b1
= (pCode
[-1]->GetInForceArray() != ParamClass::Unknown
|| IsMatrixFunction(eOp1
));
3116 const bool b2
= (pCode
[-2]->GetInForceArray() != ParamClass::Unknown
|| IsMatrixFunction(eOp2
));
3118 || (b1
&& eOp2
== ocPush
&& pCode
[-2]->GetType() != svDoubleRef
)
3119 || (b2
&& eOp1
== ocPush
&& pCode
[-1]->GetType() != svDoubleRef
))
3121 rCurr
->SetInForceArray( eArrayReturn
);
3124 else if (pc
>= 1 && SC_OPCODE_START_UN_OP
<= eOp
&& eOp
< SC_OPCODE_STOP_UN_OP
)
3126 // Similar for unary operators.
3127 if (pCode
[-1]->GetInForceArray() != ParamClass::Unknown
|| IsMatrixFunction(pCode
[-1]->GetOpCode()))
3129 rCurr
->SetInForceArray( eArrayReturn
);
3135 // Inherited parameter class.
3136 const formula::ParamClass eForceType
= pCurrentFactorToken
->GetInForceArray();
3137 if (eForceType
== ParamClass::ForceArray
|| eForceType
== ParamClass::ReferenceOrRefArray
)
3139 // ReferenceOrRefArray was set only if in ForceArray context already,
3140 // it is valid for the one function only to indicate the preferred
3141 // return type. Propagate as ForceArray if not another parameter
3142 // handling ReferenceOrRefArray.
3143 if (nCurrentFactorParam
> 0
3144 && (GetForceArrayParameter( pCurrentFactorToken
.get(), static_cast<sal_uInt16
>(nCurrentFactorParam
- 1))
3145 == ParamClass::ReferenceOrRefArray
))
3146 rCurr
->SetInForceArray( ParamClass::ReferenceOrRefArray
);
3148 rCurr
->SetInForceArray( ParamClass::ForceArray
);
3151 else if (eForceType
== ParamClass::ReferenceOrForceArray
)
3153 // Inherit further only if the return class of the nested function is
3154 // not Reference. Else flag as suppressed.
3155 if (GetForceArrayParameter( rCurr
.get(), SAL_MAX_UINT16
) != ParamClass::Reference
)
3156 rCurr
->SetInForceArray( eForceType
);
3158 rCurr
->SetInForceArray( ParamClass::SuppressedReferenceOrForceArray
);
3162 if (nCurrentFactorParam
<= 0)
3165 // Actual current parameter's class.
3166 const formula::ParamClass eParamType
= GetForceArrayParameter(
3167 pCurrentFactorToken
.get(), static_cast<sal_uInt16
>(nCurrentFactorParam
- 1));
3168 if (eParamType
== ParamClass::ForceArray
)
3169 rCurr
->SetInForceArray( eParamType
);
3170 else if (eParamType
== ParamClass::ReferenceOrForceArray
)
3172 if (GetForceArrayParameter( rCurr
.get(), SAL_MAX_UINT16
) != ParamClass::Reference
)
3173 rCurr
->SetInForceArray( eParamType
);
3175 rCurr
->SetInForceArray( formula::ParamClass::SuppressedReferenceOrForceArray
);
3178 // Propagate a ForceArrayReturn to caller if the called function
3179 // returns one and the caller so far does not have a stronger array
3180 // mode set and expects a scalar value for this parameter.
3181 if (eParamType
== ParamClass::Value
&& pCurrentFactorToken
->GetInForceArray() == ParamClass::Unknown
)
3183 if (IsMatrixFunction( eOp
))
3184 pCurrentFactorToken
->SetInForceArray( eArrayReturn
);
3185 else if (GetForceArrayParameter( rCurr
.get(), SAL_MAX_UINT16
) == ParamClass::ForceArrayReturn
)
3186 pCurrentFactorToken
->SetInForceArray( ParamClass::ForceArrayReturn
);
3190 void FormulaCompiler::CheckSetForceArrayParameter( FormulaTokenRef
const & rCurr
, sal_uInt8 nParam
)
3192 if (!pCurrentFactorToken
)
3195 nCurrentFactorParam
= nParam
+ 1;
3197 ForceArrayOperator( rCurr
);
3200 void FormulaCompiler::PushTokenArray( FormulaTokenArray
* pa
, bool bTemp
)
3202 if ( bAutoCorrect
&& !pStack
)
3203 { // don't merge stacked subroutine code into entered formula
3204 aCorrectedFormula
+= aCorrectedSymbol
;
3205 aCorrectedSymbol
.clear();
3207 FormulaArrayStack
* p
= new FormulaArrayStack
;
3210 p
->nIndex
= maArrIterator
.GetIndex();
3211 p
->mpLastToken
= mpLastToken
;
3215 maArrIterator
= FormulaTokenArrayPlainIterator( *pArr
);
3218 } // namespace formula
3220 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */